1/* Copyright (C) 2006 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
2
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; version 2 of the License.
6
7 This program is distributed in the hope that it will be useful,
8 but WITHOUT ANY WARRANTY; without even the implied warranty of
9 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 GNU General Public License for more details.
11
12 You should have received a copy of the GNU General Public License
13 along with this program; if not, write to the Free Software
14 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */
15
16/* Unit test of the control file module of the Aria engine WL#3234 */
17
18/*
19 Note that it is not possible to test the durability of the write (can't
20 pull the plug programmatically :)
21*/
22
23#include <my_global.h>
24#include <my_sys.h>
25#include <tap.h>
26#ifdef _WIN32
27#include <direct.h> /* rmdir */
28#endif
29#ifndef WITH_ARIA_STORAGE_ENGINE
30/*
31 If Aria is not compiled in, normally we don't come to building this test.
32*/
33#error "Aria engine is not compiled in, test cannot be built"
34#endif
35
36#include "maria.h"
37#include "../../../storage/maria/maria_def.h"
38#include <my_getopt.h>
39
40#define EXTRACT_DEFINITIONS
41#include "../ma_control_file.c"
42#undef EXTRACT_DEFINITIONS
43
44char file_name[FN_REFLEN];
45
46/* The values we'll set and expect the control file module to return */
47LSN expect_checkpoint_lsn;
48uint32 expect_logno;
49TrID expect_max_trid;
50uint8 expect_recovery_failures;
51
52static int delete_file(myf my_flags);
53/*
54 Those are test-specific wrappers around the module's API functions: after
55 calling the module's API functions they perform checks on the result.
56*/
57static int close_file(void); /* wraps ma_control_file_end */
58/* wraps ma_control_file_open_or_create */
59static int open_file(void);
60/* wraps ma_control_file_write_and_force */
61static int write_file(LSN checkpoint_lsn, uint32 logno, TrID trid,
62 uint8 rec_failures);
63
64/* Tests */
65static int test_one_log_and_recovery_failures(void);
66static int test_five_logs_and_max_trid(void);
67static int test_3_checkpoints_and_2_logs(void);
68static int test_binary_content(void);
69static int test_start_stop(void);
70static int test_2_open_and_2_close(void);
71static int test_bad_magic_string(void);
72static int test_bad_checksum(void);
73static int test_bad_hchecksum(void);
74static int test_future_size(void);
75static int test_bad_blocksize(void);
76static int test_bad_size(void);
77
78/* Utility */
79static int verify_module_values_match_expected(void);
80static int verify_module_values_are_impossible(void);
81static void usage(void);
82static void get_options(int argc, char *argv[]);
83
84/*
85 If "expr" is FALSE, this macro will make the function print a diagnostic
86 message and immediately return 1.
87 This is inspired from assert() but does not crash the binary (sometimes we
88 may want to see how other tests go even if one fails).
89 RET_ERR means "return error".
90*/
91
92#define RET_ERR_UNLESS(expr) \
93 {if (!(expr)) {diag("line %d: failure: '%s'", __LINE__, #expr); assert(0);return 1;}}
94
95
96/* Used to ignore error messages from ma_control_file_open() */
97
98static void my_ignore_message(uint error __attribute__((unused)),
99 const char *str __attribute__((unused)),
100 myf MyFlags __attribute__((unused)))
101{
102 DBUG_ENTER("my_message_no_curses");
103 DBUG_PRINT("enter",("message: %s",str));
104 DBUG_VOID_RETURN;
105}
106
107void (*default_error_handler_hook)(uint my_err, const char *str,
108 myf MyFlags) = 0;
109
110
111/* like ma_control_file_open(), but without error messages */
112
113static CONTROL_FILE_ERROR local_ma_control_file_open(void)
114{
115 CONTROL_FILE_ERROR error;
116 error_handler_hook= my_ignore_message;
117 error= ma_control_file_open(TRUE, TRUE);
118 error_handler_hook= default_error_handler_hook;
119 return error;
120}
121
122static char *create_tmpdir(const char *progname)
123{
124 static char test_dirname[FN_REFLEN];
125 char tmp_name[FN_REFLEN];
126 size_t length;
127
128 /* Create a temporary directory of name TMP-'executable', but without the -t extension */
129 fn_format(tmp_name, progname, "", "", MY_REPLACE_DIR | MY_REPLACE_EXT);
130 length= strlen(tmp_name);
131 if (length > 2 && tmp_name[length-2] == '-' && tmp_name[length-1] == 't')
132 tmp_name[length-2]= 0;
133 strxmov(test_dirname, "TMP-", tmp_name, NullS);
134
135 /*
136 Don't give an error if we can't create dir, as it may already exist from a previously aborted
137 run
138 */
139 (void) my_mkdir(test_dirname, 0777, MYF(0));
140 return test_dirname;
141}
142
143
144int main(int argc,char *argv[])
145{
146 MY_INIT(argv[0]);
147 my_init();
148
149 default_error_handler_hook= error_handler_hook;
150
151 plan(12);
152
153 maria_data_root= create_tmpdir(argv[0]);
154
155 diag("Unit tests for control file");
156
157 get_options(argc,argv);
158
159 diag("Deleting control file at startup, if there is an old one");
160 RET_ERR_UNLESS(0 == delete_file(0)); /* if fails, can't continue */
161
162 diag("Tests of normal conditions");
163 ok(0 == test_one_log_and_recovery_failures(),
164 "test of creating one log and recording recovery failures");
165 ok(0 == test_five_logs_and_max_trid(),
166 "test of creating five logs and many transactions");
167 ok(0 == test_3_checkpoints_and_2_logs(),
168 "test of creating three checkpoints and two logs");
169 ok(0 == test_binary_content(), "test of the binary content of the file");
170 ok(0 == test_start_stop(), "test of multiple starts and stops");
171 diag("Tests of abnormal conditions");
172 ok(0 == test_2_open_and_2_close(),
173 "test of two open and two close (strange call sequence)");
174 ok(0 == test_bad_magic_string(), "test of bad magic string");
175 ok(0 == test_bad_checksum(), "test of bad checksum");
176 ok(0 == test_bad_hchecksum(), "test of bad hchecksum");
177 ok(0 == test_future_size(), "test of ability to handlr future versions");
178 ok(0 == test_bad_blocksize(), "test of bad blocksize");
179 ok(0 == test_bad_size(), "test of too small/big file");
180
181 delete_file(0);
182 rmdir(maria_data_root);
183
184 my_uuid_end();
185 my_end(0);
186 return exit_status();
187}
188
189
190static int delete_file(myf my_flags)
191{
192 RET_ERR_UNLESS(fn_format(file_name, CONTROL_FILE_BASE_NAME,
193 maria_data_root, "", MYF(MY_WME)) != NullS);
194 /*
195 Maybe file does not exist, ignore error.
196 The error will however be printed on stderr.
197 */
198 my_delete(file_name, my_flags);
199 expect_checkpoint_lsn= LSN_IMPOSSIBLE;
200 expect_logno= FILENO_IMPOSSIBLE;
201 expect_max_trid= expect_recovery_failures= 0;
202
203 return 0;
204}
205
206/*
207 Verifies that global values last_checkpoint_lsn, last_logno,
208 max_trid_in_control_file (belonging to the module) match what we expect.
209*/
210static int verify_module_values_match_expected(void)
211{
212 RET_ERR_UNLESS(last_logno == expect_logno);
213 RET_ERR_UNLESS(last_checkpoint_lsn == expect_checkpoint_lsn);
214 RET_ERR_UNLESS(max_trid_in_control_file == expect_max_trid);
215 RET_ERR_UNLESS(recovery_failures == expect_recovery_failures);
216 return 0;
217}
218
219
220/*
221 Verifies that global values last_checkpoint_lsn and last_logno (belonging
222 to the module) are impossible (this is used when the file has been closed).
223*/
224static int verify_module_values_are_impossible(void)
225{
226 RET_ERR_UNLESS(last_logno == FILENO_IMPOSSIBLE);
227 RET_ERR_UNLESS(last_checkpoint_lsn == LSN_IMPOSSIBLE);
228 RET_ERR_UNLESS(max_trid_in_control_file == 0);
229 return 0;
230}
231
232
233static int close_file(void)
234{
235 /* Simulate shutdown */
236 ma_control_file_end();
237 /* Verify amnesia */
238 RET_ERR_UNLESS(verify_module_values_are_impossible() == 0);
239 return 0;
240}
241
242static int open_file(void)
243{
244 RET_ERR_UNLESS(local_ma_control_file_open() == CONTROL_FILE_OK);
245 /* Check that the module reports expected information */
246 RET_ERR_UNLESS(verify_module_values_match_expected() == 0);
247 return 0;
248}
249
250static int write_file(LSN checkpoint_lsn, uint32 logno, TrID trid,
251 uint8 rec_failures)
252{
253 RET_ERR_UNLESS(ma_control_file_write_and_force(checkpoint_lsn, logno, trid,
254 rec_failures)
255 == 0);
256 /* Check that the module reports expected information */
257 RET_ERR_UNLESS(verify_module_values_match_expected() == 0);
258 return 0;
259}
260
261static int test_one_log_and_recovery_failures(void)
262{
263 RET_ERR_UNLESS(open_file() == CONTROL_FILE_OK);
264 expect_logno= 123;
265 RET_ERR_UNLESS(write_file(last_checkpoint_lsn, expect_logno,
266 max_trid_in_control_file,
267 recovery_failures) == 0);
268 expect_recovery_failures= 158;
269 RET_ERR_UNLESS(write_file(last_checkpoint_lsn, expect_logno,
270 max_trid_in_control_file,
271 expect_recovery_failures) == 0);
272 RET_ERR_UNLESS(close_file() == 0);
273 return 0;
274}
275
276static int test_five_logs_and_max_trid(void)
277{
278 uint i;
279
280 RET_ERR_UNLESS(open_file() == CONTROL_FILE_OK);
281 expect_logno= 100;
282 expect_max_trid= 14111978111ULL;
283 for (i= 0; i<5; i++)
284 {
285 expect_logno*= 3;
286 RET_ERR_UNLESS(write_file(last_checkpoint_lsn, expect_logno,
287 expect_max_trid,
288 recovery_failures) == 0);
289 }
290 RET_ERR_UNLESS(close_file() == 0);
291 return 0;
292}
293
294static int test_3_checkpoints_and_2_logs(void)
295{
296 /*
297 Simulate one checkpoint, one log creation, two checkpoints, one
298 log creation.
299 */
300 RET_ERR_UNLESS(open_file() == CONTROL_FILE_OK);
301 expect_checkpoint_lsn= MAKE_LSN(5, 10000);
302 RET_ERR_UNLESS(write_file(expect_checkpoint_lsn, expect_logno,
303 max_trid_in_control_file,
304 recovery_failures) == 0);
305
306 expect_logno= 17;
307 RET_ERR_UNLESS(write_file(expect_checkpoint_lsn, expect_logno,
308 max_trid_in_control_file,
309 recovery_failures) == 0);
310
311 expect_checkpoint_lsn= MAKE_LSN(17, 20000);
312 RET_ERR_UNLESS(write_file(expect_checkpoint_lsn, expect_logno,
313 max_trid_in_control_file,
314 recovery_failures) == 0);
315
316 expect_checkpoint_lsn= MAKE_LSN(17, 45000);
317 RET_ERR_UNLESS(write_file(expect_checkpoint_lsn, expect_logno,
318 max_trid_in_control_file,
319 recovery_failures) == 0);
320
321 expect_logno= 19;
322 RET_ERR_UNLESS(write_file(expect_checkpoint_lsn, expect_logno,
323 max_trid_in_control_file,
324 recovery_failures) == 0);
325 RET_ERR_UNLESS(close_file() == 0);
326 return 0;
327}
328
329static int test_binary_content(void)
330{
331 uint i;
332 int fd;
333
334 /*
335 TEST4: actually check by ourselves the content of the file.
336 Note that constants (offsets) are hard-coded here, precisely to prevent
337 someone from changing them in the control file module and breaking
338 backward-compatibility.
339 TODO: when we reach the format-freeze state, we may even just do a
340 comparison with a raw binary string, to not depend on any uint4korr
341 future change/breakage.
342 */
343
344 uchar buffer[45];
345 RET_ERR_UNLESS((fd= my_open(file_name,
346 O_BINARY | O_RDWR,
347 MYF(MY_WME))) >= 0);
348 RET_ERR_UNLESS(my_read(fd, buffer, 45, MYF(MY_FNABP | MY_WME)) == 0);
349 RET_ERR_UNLESS(my_close(fd, MYF(MY_WME)) == 0);
350 RET_ERR_UNLESS(open_file() == CONTROL_FILE_OK);
351 i= uint3korr(buffer + 34 );
352 RET_ERR_UNLESS(i == LSN_FILE_NO(last_checkpoint_lsn));
353 i= uint4korr(buffer + 37);
354 RET_ERR_UNLESS(i == LSN_OFFSET(last_checkpoint_lsn));
355 i= uint4korr(buffer + 41);
356 RET_ERR_UNLESS(i == last_logno);
357 RET_ERR_UNLESS(close_file() == 0);
358 return 0;
359}
360
361static int test_start_stop(void)
362{
363 /* TEST5: Simulate start/nothing/stop/start/nothing/stop/start */
364
365 RET_ERR_UNLESS(open_file() == CONTROL_FILE_OK);
366 RET_ERR_UNLESS(close_file() == 0);
367 RET_ERR_UNLESS(open_file() == CONTROL_FILE_OK);
368 RET_ERR_UNLESS(close_file() == 0);
369 RET_ERR_UNLESS(open_file() == CONTROL_FILE_OK);
370 RET_ERR_UNLESS(close_file() == 0);
371 return 0;
372}
373
374static int test_2_open_and_2_close(void)
375{
376 RET_ERR_UNLESS(open_file() == CONTROL_FILE_OK);
377 RET_ERR_UNLESS(open_file() == CONTROL_FILE_OK);
378 RET_ERR_UNLESS(close_file() == 0);
379 RET_ERR_UNLESS(close_file() == 0);
380 return 0;
381}
382
383
384static int test_bad_magic_string(void)
385{
386 uchar buffer[4];
387 int fd;
388
389 RET_ERR_UNLESS(open_file() == CONTROL_FILE_OK);
390 RET_ERR_UNLESS(close_file() == 0);
391
392 /* Corrupt magic string */
393 RET_ERR_UNLESS((fd= my_open(file_name,
394 O_BINARY | O_RDWR,
395 MYF(MY_WME))) >= 0);
396 RET_ERR_UNLESS(my_pread(fd, buffer, 4, 0, MYF(MY_FNABP | MY_WME)) == 0);
397 RET_ERR_UNLESS(my_pwrite(fd, (const uchar *)"papa", 4, 0,
398 MYF(MY_FNABP | MY_WME)) == 0);
399
400 /* Check that control file module sees the problem */
401 RET_ERR_UNLESS(local_ma_control_file_open() ==
402 CONTROL_FILE_BAD_MAGIC_STRING);
403 /* Restore magic string */
404 RET_ERR_UNLESS(my_pwrite(fd, buffer, 4, 0, MYF(MY_FNABP | MY_WME)) == 0);
405 RET_ERR_UNLESS(my_close(fd, MYF(MY_WME)) == 0);
406 RET_ERR_UNLESS(open_file() == CONTROL_FILE_OK);
407 RET_ERR_UNLESS(close_file() == 0);
408 return 0;
409}
410
411static int test_bad_checksum(void)
412{
413 uchar buffer[4];
414 int fd;
415
416 RET_ERR_UNLESS(open_file() == CONTROL_FILE_OK);
417 RET_ERR_UNLESS(close_file() == 0);
418
419 /* Corrupt checksum */
420 RET_ERR_UNLESS((fd= my_open(file_name,
421 O_BINARY | O_RDWR,
422 MYF(MY_WME))) >= 0);
423 RET_ERR_UNLESS(my_pread(fd, buffer, 1, 30, MYF(MY_FNABP | MY_WME)) == 0);
424 buffer[0]+= 3; /* mangle checksum */
425 RET_ERR_UNLESS(my_pwrite(fd, buffer, 1, 30, MYF(MY_FNABP | MY_WME)) == 0);
426 /* Check that control file module sees the problem */
427 RET_ERR_UNLESS(local_ma_control_file_open() ==
428 CONTROL_FILE_BAD_CHECKSUM);
429 /* Restore checksum */
430 buffer[0]-= 3;
431 RET_ERR_UNLESS(my_pwrite(fd, buffer, 1, 30, MYF(MY_FNABP | MY_WME)) == 0);
432 RET_ERR_UNLESS(my_close(fd, MYF(MY_WME)) == 0);
433
434 return 0;
435}
436
437
438static int test_bad_blocksize(void)
439{
440 maria_block_size<<= 1;
441 /* Check that control file module sees the problem */
442 RET_ERR_UNLESS(local_ma_control_file_open() ==
443 CONTROL_FILE_WRONG_BLOCKSIZE);
444 /* Restore blocksize */
445 maria_block_size>>= 1;
446
447 RET_ERR_UNLESS(open_file() == CONTROL_FILE_OK);
448 RET_ERR_UNLESS(close_file() == 0);
449 return 0;
450}
451
452
453static int test_future_size(void)
454{
455 /*
456 Here we check ability to add fields only so we can use
457 defined constants
458 */
459 uint32 sum;
460 int fd;
461 uchar buffer[CF_CREATE_TIME_TOTAL_SIZE + CF_CHANGEABLE_TOTAL_SIZE + 2];
462 RET_ERR_UNLESS((fd= my_open(file_name,
463 O_BINARY | O_RDWR,
464 MYF(MY_WME))) >= 0);
465 RET_ERR_UNLESS(my_read(fd, buffer,
466 CF_CREATE_TIME_TOTAL_SIZE + CF_CHANGEABLE_TOTAL_SIZE,
467 MYF(MY_FNABP | MY_WME)) == 0);
468 RET_ERR_UNLESS(my_close(fd, MYF(MY_WME)) == 0);
469 /* "add" new field of 1 byte (value 1) to header and variable part */
470 memmove(buffer + CF_CREATE_TIME_TOTAL_SIZE + 1,
471 buffer + CF_CREATE_TIME_TOTAL_SIZE,
472 CF_CHANGEABLE_TOTAL_SIZE);
473 buffer[CF_CREATE_TIME_TOTAL_SIZE - CF_CHECKSUM_SIZE]= '\1';
474 buffer[CF_CREATE_TIME_TOTAL_SIZE + CF_CHANGEABLE_TOTAL_SIZE + 1]= '\1';
475 /* fix lengths */
476 int2store(buffer + CF_CREATE_TIME_SIZE_OFFSET, CF_CREATE_TIME_TOTAL_SIZE + 1);
477 int2store(buffer + CF_CHANGEABLE_SIZE_OFFSET, CF_CHANGEABLE_TOTAL_SIZE + 1);
478 /* recalculete checksums */
479 sum= (uint32) my_checksum(0, buffer, CF_CREATE_TIME_TOTAL_SIZE -
480 CF_CHECKSUM_SIZE + 1);
481 int4store(buffer + CF_CREATE_TIME_TOTAL_SIZE - CF_CHECKSUM_SIZE + 1, sum);
482 sum= (uint32) my_checksum(0, buffer + CF_CREATE_TIME_TOTAL_SIZE + 1 +
483 CF_CHECKSUM_SIZE,
484 CF_CHANGEABLE_TOTAL_SIZE - CF_CHECKSUM_SIZE + 1);
485 int4store(buffer + CF_CREATE_TIME_TOTAL_SIZE + 1, sum);
486 /* write new file and check it */
487 RET_ERR_UNLESS((fd= my_open(file_name,
488 O_BINARY | O_RDWR,
489 MYF(MY_WME))) >= 0);
490 RET_ERR_UNLESS(my_pwrite(fd, buffer,
491 CF_CREATE_TIME_TOTAL_SIZE +
492 CF_CHANGEABLE_TOTAL_SIZE + 2,
493 0, MYF(MY_FNABP | MY_WME)) == 0);
494 RET_ERR_UNLESS(my_close(fd, MYF(MY_WME)) == 0);
495 RET_ERR_UNLESS(open_file() == CONTROL_FILE_OK);
496 RET_ERR_UNLESS(close_file() == 0);
497
498 return(0);
499}
500
501static int test_bad_hchecksum(void)
502{
503 uchar buffer[4];
504 int fd;
505
506 RET_ERR_UNLESS(open_file() == CONTROL_FILE_OK);
507 RET_ERR_UNLESS(close_file() == 0);
508
509 /* Corrupt checksum */
510 RET_ERR_UNLESS((fd= my_open(file_name,
511 O_BINARY | O_RDWR,
512 MYF(MY_WME))) >= 0);
513 RET_ERR_UNLESS(my_pread(fd, buffer, 1, 26, MYF(MY_FNABP | MY_WME)) == 0);
514 buffer[0]+= 3; /* mangle checksum */
515 RET_ERR_UNLESS(my_pwrite(fd, buffer, 1, 26, MYF(MY_FNABP | MY_WME)) == 0);
516 /* Check that control file module sees the problem */
517 RET_ERR_UNLESS(local_ma_control_file_open() ==
518 CONTROL_FILE_BAD_HEAD_CHECKSUM);
519 /* Restore checksum */
520 buffer[0]-= 3;
521 RET_ERR_UNLESS(my_pwrite(fd, buffer, 1, 26, MYF(MY_FNABP | MY_WME)) == 0);
522 RET_ERR_UNLESS(my_close(fd, MYF(MY_WME)) == 0);
523
524 return 0;
525}
526
527
528static int test_bad_size(void)
529{
530 uchar buffer[]=
531 "123456789012345678901234567890123456789012345678901234567890123456";
532 int fd, i;
533
534 /* A too short file */
535 RET_ERR_UNLESS(delete_file(MYF(MY_WME)) == 0);
536 RET_ERR_UNLESS((fd= my_open(file_name,
537 O_BINARY | O_RDWR | O_CREAT,
538 MYF(MY_WME))) >= 0);
539 RET_ERR_UNLESS(my_write(fd, buffer, 10, MYF(MY_FNABP | MY_WME)) == 0);
540 /* Check that control file module sees the problem */
541 RET_ERR_UNLESS(local_ma_control_file_open() ==
542 CONTROL_FILE_TOO_SMALL);
543 for (i= 0; i < 8; i++)
544 {
545 RET_ERR_UNLESS(my_write(fd, buffer, 66, MYF(MY_FNABP | MY_WME)) == 0);
546 }
547 /* Check that control file module sees the problem */
548 RET_ERR_UNLESS(local_ma_control_file_open() ==
549 CONTROL_FILE_TOO_BIG);
550 RET_ERR_UNLESS(my_close(fd, MYF(MY_WME)) == 0);
551
552 /* Leave a correct control file */
553 RET_ERR_UNLESS(delete_file(MYF(MY_WME)) == 0);
554 RET_ERR_UNLESS(open_file() == CONTROL_FILE_OK);
555 RET_ERR_UNLESS(close_file() == 0);
556
557 return 0;
558}
559
560
561static struct my_option my_long_options[] =
562{
563#ifndef DBUG_OFF
564 {"debug", '#', "Debug log.",
565 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
566#endif
567 {"help", '?', "Display help and exit",
568 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
569 {"version", 'V', "Print version number and exit",
570 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
571 { 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
572};
573
574
575static void version(void)
576{
577 printf("ma_control_file_test: unit test for the control file "
578 "module of the Aria storage engine. Ver 1.0 \n");
579}
580
581static my_bool
582get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
583 char *argument __attribute__((unused)))
584{
585 switch(optid) {
586 case 'V':
587 version();
588 exit(0);
589 case '#':
590 DBUG_PUSH (argument);
591 break;
592 case '?':
593 version();
594 usage();
595 exit(0);
596 }
597 return 0;
598}
599
600
601/* Read options */
602
603static void get_options(int argc, char *argv[])
604{
605 int ho_error;
606
607 if ((ho_error=handle_options(&argc, &argv, my_long_options,
608 get_one_option)))
609 exit(ho_error);
610
611 return;
612} /* get options */
613
614
615static void usage(void)
616{
617 printf("Usage: %s [options]\n\n", my_progname);
618 my_print_help(my_long_options);
619 my_print_variables(my_long_options);
620}
621
622#include "../ma_check_standalone.h"
623