19f1d1ae35414e5bad476ee0e972f106119f190d
[ira/wip.git] / source4 / torture / smbtorture.c
1 /* 
2    Unix SMB/CIFS implementation.
3    SMB torture tester
4    Copyright (C) Andrew Tridgell 1997-2003
5    Copyright (C) Jelmer Vernooij 2006-2008
6    
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 3 of the License, or
10    (at your option) any later version.
11    
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16    
17    You should have received a copy of the GNU General Public License
18    along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include "includes.h"
22 #include "lib/cmdline/popt_common.h"
23 #include "system/time.h"
24 #include "system/wait.h"
25 #include "system/filesys.h"
26 #include "system/readline.h"
27 #include "lib/smbreadline/smbreadline.h"
28 #include "libcli/libcli.h"
29 #include "lib/ldb/include/ldb.h"
30 #include "lib/events/events.h"
31 #include "dynconfig/dynconfig.h"
32
33 #include "torture/smbtorture.h"
34 #include "../lib/util/dlinklist.h"
35 #include "librpc/rpc/dcerpc.h"
36 #include "param/param.h"
37
38 #include "auth/credentials/credentials.h"
39
40 static bool run_matching(struct torture_context *torture,
41                                                  const char *prefix, 
42                                                  const char *expr,
43                                                  struct torture_suite *suite,
44                                                  bool *matched)
45 {
46         bool ret = true;
47
48         if (suite == NULL) {
49                 struct torture_suite *o;
50
51                 for (o = torture_root->children; o; o = o->next) {
52                         if (gen_fnmatch(expr, o->name) == 0) {
53                                 *matched = true;
54                                 reload_charcnv(torture->lp_ctx);
55                                 ret &= torture_run_suite(torture, o);
56                                 continue;
57                         }
58
59                         ret &= run_matching(torture, o->name, expr, o, matched);
60                 }
61         } else {
62                 char *name;
63                 struct torture_suite *c;
64                 struct torture_tcase *t;
65
66                 for (c = suite->children; c; c = c->next) {
67                         asprintf(&name, "%s-%s", prefix, c->name);
68
69                         if (gen_fnmatch(expr, name) == 0) {
70                                 *matched = true;
71                                 reload_charcnv(torture->lp_ctx);
72                                 torture->active_testname = talloc_strdup(torture, prefix);
73                                 ret &= torture_run_suite(torture, c);
74                                 free(name);
75                                 continue;
76                         }
77                         
78                         ret &= run_matching(torture, name, expr, c, matched);
79
80                         free(name);
81                 }
82
83                 for (t = suite->testcases; t; t = t->next) {
84                         asprintf(&name, "%s-%s", prefix, t->name);
85                         if (gen_fnmatch(expr, name) == 0) {
86                                 *matched = true;
87                                 reload_charcnv(torture->lp_ctx);
88                                 torture->active_testname = talloc_strdup(torture, prefix);
89                                 ret &= torture_run_tcase(torture, t);
90                                 talloc_free(torture->active_testname);
91                         }
92                         free(name);
93                 }
94         }
95
96         return ret;
97 }
98
99 #define MAX_COLS 80 /* FIXME: Determine this at run-time */
100
101 /****************************************************************************
102 run a specified test or "ALL"
103 ****************************************************************************/
104 static bool run_test(struct torture_context *torture, const char *name)
105 {
106         bool ret = true;
107         bool matched = false;
108         struct torture_suite *o;
109
110         if (strequal(name, "ALL")) {
111                 for (o = torture_root->children; o; o = o->next) {
112                         ret &= torture_run_suite(torture, o);
113                 }
114                 return ret;
115         }
116
117         ret = run_matching(torture, NULL, name, NULL, &matched);
118
119         if (!matched) {
120                 printf("Unknown torture operation '%s'\n", name);
121                 return false;
122         }
123
124         return ret;
125 }
126
127 static bool parse_target(struct loadparm_context *lp_ctx, const char *target)
128 {
129         char *host = NULL, *share = NULL;
130         struct dcerpc_binding *binding_struct;
131         NTSTATUS status;
132
133         /* see if its a RPC transport specifier */
134         if (!smbcli_parse_unc(target, NULL, &host, &share)) {
135                 status = dcerpc_parse_binding(talloc_autofree_context(), target, &binding_struct);
136                 if (NT_STATUS_IS_ERR(status)) {
137                         d_printf("Invalid option: %s is not a valid torture target (share or binding string)\n\n", target);
138                         return false;
139                 }
140                 lp_set_cmdline(lp_ctx, "torture:host", binding_struct->host);
141                 if (lp_parm_string(lp_ctx, NULL, "torture", "share") == NULL)
142                         lp_set_cmdline(lp_ctx, "torture:share", "IPC$");
143                 lp_set_cmdline(lp_ctx, "torture:binding", target);
144         } else {
145                 lp_set_cmdline(lp_ctx, "torture:host", host);
146                 lp_set_cmdline(lp_ctx, "torture:share", share);
147                 lp_set_cmdline(lp_ctx, "torture:binding", host);
148         }
149
150         return true;
151 }
152
153 static void parse_dns(struct loadparm_context *lp_ctx, const char *dns)
154 {
155         char *userdn, *basedn, *secret;
156         char *p, *d;
157
158         /* retrievieng the userdn */
159         p = strchr_m(dns, '#');
160         if (!p) {
161                 lp_set_cmdline(lp_ctx, "torture:ldap_userdn", "");
162                 lp_set_cmdline(lp_ctx, "torture:ldap_basedn", "");
163                 lp_set_cmdline(lp_ctx, "torture:ldap_secret", "");
164                 return;
165         }
166         userdn = strndup(dns, p - dns);
167         lp_set_cmdline(lp_ctx, "torture:ldap_userdn", userdn);
168
169         /* retrieve the basedn */
170         d = p + 1;
171         p = strchr_m(d, '#');
172         if (!p) {
173                 lp_set_cmdline(lp_ctx, "torture:ldap_basedn", "");
174                 lp_set_cmdline(lp_ctx, "torture:ldap_secret", "");
175                 return;
176         }
177         basedn = strndup(d, p - d);
178         lp_set_cmdline(lp_ctx, "torture:ldap_basedn", basedn);
179
180         /* retrieve the secret */
181         p = p + 1;
182         if (!p) {
183                 lp_set_cmdline(lp_ctx, "torture:ldap_secret", "");
184                 return;
185         }
186         secret = strdup(p);
187         lp_set_cmdline(lp_ctx, "torture:ldap_secret", secret);
188
189         printf ("%s - %s - %s\n", userdn, basedn, secret);
190
191 }
192
193 static void print_test_list(void)
194 {
195         struct torture_suite *o;
196         struct torture_suite *s;
197         struct torture_tcase *t;
198
199         if (torture_root == NULL)
200                 return;
201
202         for (o = torture_root->children; o; o = o->next) {
203                 for (s = o->children; s; s = s->next) {
204                         printf("%s-%s\n", o->name, s->name);
205                 }
206
207                 for (t = o->testcases; t; t = t->next) {
208                         printf("%s-%s\n", o->name, t->name);
209                 }
210         }
211 }
212
213 _NORETURN_ static void usage(poptContext pc)
214 {
215         struct torture_suite *o;
216         struct torture_suite *s;
217         struct torture_tcase *t;
218         int i;
219
220         poptPrintUsage(pc, stdout, 0);
221         printf("\n");
222
223         printf("The binding format is:\n\n");
224
225         printf("  TRANSPORT:host[flags]\n\n");
226
227         printf("  where TRANSPORT is either ncacn_np for SMB, ncacn_ip_tcp for RPC/TCP\n");
228         printf("  or ncalrpc for local connections.\n\n");
229
230         printf("  'host' is an IP or hostname or netbios name. If the binding string\n");
231         printf("  identifies the server side of an endpoint, 'host' may be an empty\n");
232         printf("  string.\n\n");
233
234         printf("  'flags' can include a SMB pipe name if using the ncacn_np transport or\n");
235         printf("  a TCP port number if using the ncacn_ip_tcp transport, otherwise they\n");
236         printf("  will be auto-determined.\n\n");
237
238         printf("  other recognised flags are:\n\n");
239
240         printf("    sign : enable ntlmssp signing\n");
241         printf("    seal : enable ntlmssp sealing\n");
242         printf("    connect : enable rpc connect level auth (auth, but no sign or seal)\n");
243         printf("    validate: enable the NDR validator\n");
244         printf("    print: enable debugging of the packets\n");
245         printf("    bigendian: use bigendian RPC\n");
246         printf("    padcheck: check reply data for non-zero pad bytes\n\n");
247
248         printf("  For example, these all connect to the samr pipe:\n\n");
249
250         printf("    ncacn_np:myserver\n");
251         printf("    ncacn_np:myserver[samr]\n");
252         printf("    ncacn_np:myserver[\\pipe\\samr]\n");
253         printf("    ncacn_np:myserver[/pipe/samr]\n");
254         printf("    ncacn_np:myserver[samr,sign,print]\n");
255         printf("    ncacn_np:myserver[\\pipe\\samr,sign,seal,bigendian]\n");
256         printf("    ncacn_np:myserver[/pipe/samr,seal,validate]\n");
257         printf("    ncacn_np:\n");
258         printf("    ncacn_np:[/pipe/samr]\n\n");
259
260         printf("    ncacn_ip_tcp:myserver\n");
261         printf("    ncacn_ip_tcp:myserver[1024]\n");
262         printf("    ncacn_ip_tcp:myserver[1024,sign,seal]\n\n");
263
264         printf("    ncalrpc:\n\n");
265
266         printf("The UNC format is:\n\n");
267
268         printf("  //server/share\n\n");
269
270         printf("Tests are:");
271
272         if (torture_root == NULL) {
273             printf("NO TESTS LOADED\n");
274             exit(1);
275         }
276
277         for (o = torture_root->children; o; o = o->next) {
278                 printf("\n%s (%s):\n  ", o->description, o->name);
279
280                 i = 0;
281                 for (s = o->children; s; s = s->next) {
282                         if (i + strlen(o->name) + strlen(s->name) >= (MAX_COLS - 3)) {
283                                 printf("\n  ");
284                                 i = 0;
285                         }
286                         i+=printf("%s-%s ", o->name, s->name);
287                 }
288
289                 for (t = o->testcases; t; t = t->next) {
290                         if (i + strlen(o->name) + strlen(t->name) >= (MAX_COLS - 3)) {
291                                 printf("\n  ");
292                                 i = 0;
293                         }
294                         i+=printf("%s-%s ", o->name, t->name);
295                 }
296
297                 if (i) printf("\n");
298         }
299
300         printf("\nThe default test is ALL.\n");
301
302         exit(1);
303 }
304
305 _NORETURN_ static void max_runtime_handler(int sig)
306 {
307         DEBUG(0,("maximum runtime exceeded for smbtorture - terminating\n"));
308         exit(1);
309 }
310
311 struct timeval last_suite_started;
312
313 static void simple_suite_start(struct torture_context *ctx,
314                                struct torture_suite *suite)
315 {
316         last_suite_started = timeval_current();
317         printf("Running %s\n", suite->name);
318 }
319
320 static void simple_suite_finish(struct torture_context *ctx,
321                                 struct torture_suite *suite)
322 {
323
324         printf("%s took %g secs\n\n", suite->name, 
325                    timeval_elapsed(&last_suite_started));
326 }
327
328 static void simple_test_result(struct torture_context *context, 
329                                enum torture_result res, const char *reason)
330 {
331         switch (res) {
332         case TORTURE_OK:
333                 if (reason)
334                         printf("OK: %s\n", reason);
335                 break;
336         case TORTURE_FAIL:
337                 printf("TEST %s FAILED! - %s\n", context->active_test->name, reason);
338                 break;
339         case TORTURE_ERROR:
340                 printf("ERROR IN TEST %s! - %s\n", context->active_test->name, reason); 
341                 break;
342         case TORTURE_SKIP:
343                 printf("SKIP: %s - %s\n", context->active_test->name, reason);
344                 break;
345         }
346 }
347
348 static void simple_comment(struct torture_context *test, 
349                            const char *comment)
350 {
351         printf("%s", comment);
352 }
353
354 static void simple_warning(struct torture_context *test, 
355                            const char *comment)
356 {
357         fprintf(stderr, "WARNING: %s\n", comment);
358 }
359
360 const static struct torture_ui_ops std_ui_ops = {
361         .comment = simple_comment,
362         .warning = simple_warning,
363         .suite_start = simple_suite_start,
364         .suite_finish = simple_suite_finish,
365         .test_result = simple_test_result
366 };
367
368
369 static void quiet_suite_start(struct torture_context *ctx,
370                               struct torture_suite *suite)
371 {
372         int i;
373         ctx->quiet = true;
374         for (i = 1; i < ctx->level; i++) putchar('\t');
375         printf("%s: ", suite->name);
376         fflush(stdout);
377 }
378
379 static void quiet_suite_finish(struct torture_context *ctx,
380                                struct torture_suite *suite)
381 {
382         putchar('\n');
383 }
384
385 static void quiet_test_result(struct torture_context *context, 
386                               enum torture_result res, const char *reason)
387 {
388         fflush(stdout);
389         switch (res) {
390         case TORTURE_OK: putchar('.'); break;
391         case TORTURE_FAIL: putchar('F'); break;
392         case TORTURE_ERROR: putchar('E'); break;
393         case TORTURE_SKIP: putchar('I'); break;
394         }
395 }
396
397 const static struct torture_ui_ops quiet_ui_ops = {
398         .suite_start = quiet_suite_start,
399         .suite_finish = quiet_suite_finish,
400         .test_result = quiet_test_result
401 };
402
403 static void run_shell(struct torture_context *tctx)
404 {
405         char *cline;
406         int argc;
407         const char **argv;
408         int ret;
409
410         while (1) {
411                 cline = smb_readline("torture> ", NULL, NULL);
412
413                 if (cline == NULL)
414                         return;
415         
416                 ret = poptParseArgvString(cline, &argc, &argv);
417                 if (ret != 0) {
418                         fprintf(stderr, "Error parsing line\n");
419                         continue;
420                 }
421
422                 if (!strcmp(argv[0], "quit")) {
423                         return;
424                 } else if (!strcmp(argv[0], "set")) {
425                         if (argc < 3) {
426                                 fprintf(stderr, "Usage: set <variable> <value>\n");
427                         } else {
428                                 char *name = talloc_asprintf(NULL, "torture:%s", argv[1]);
429                                 lp_set_cmdline(tctx->lp_ctx, name, argv[2]);
430                                 talloc_free(name);
431                         }
432                 } else if (!strcmp(argv[0], "help")) {
433                         fprintf(stderr, "Available commands:\n"
434                                                         " help - This help command\n"
435                                                         " run - Run test\n"
436                                                         " set - Change variables\n"
437                                                         "\n");
438                 } else if (!strcmp(argv[0], "run")) {
439                         if (argc < 2) {
440                                 fprintf(stderr, "Usage: run TEST-NAME [OPTIONS...]\n");
441                         } else {
442                                 run_test(tctx, argv[1]);
443                         }
444                 }
445                 free(cline);
446         }
447 }
448
449 /****************************************************************************
450   main program
451 ****************************************************************************/
452 int main(int argc,char *argv[])
453 {
454         int opt, i;
455         bool correct = true;
456         int max_runtime=0;
457         int argc_new;
458         struct torture_context *torture;
459         const struct torture_ui_ops *ui_ops;
460         char **argv_new;
461         poptContext pc;
462         static const char *target = "other";
463         NTSTATUS status;
464         int shell = false;
465         static const char *ui_ops_name = "simple";
466         const char *basedir = NULL;
467         const char *extra_module = NULL;
468         static int list_tests = 0;
469         int num_extra_users = 0;
470         enum {OPT_LOADFILE=1000,OPT_UNCLIST,OPT_TIMELIMIT,OPT_DNS, OPT_LIST,
471               OPT_DANGEROUS,OPT_SMB_PORTS,OPT_ASYNC,OPT_NUMPROGS,OPT_EXTRA_USER};
472         
473         struct poptOption long_options[] = {
474                 POPT_AUTOHELP
475                 {"format", 0, POPT_ARG_STRING, &ui_ops_name, 0, "Output format (one of: simple, subunit)", NULL },
476                 {"smb-ports",   'p', POPT_ARG_STRING, NULL,     OPT_SMB_PORTS,  "SMB ports",    NULL},
477                 {"basedir",       0, POPT_ARG_STRING, &basedir, 0, "base directory", "BASEDIR" },
478                 {"seed",          0, POPT_ARG_INT,  &torture_seed,      0,      "Seed to use for randomizer",   NULL},
479                 {"num-progs",     0, POPT_ARG_INT,  NULL,       OPT_NUMPROGS,   "num progs",    NULL},
480                 {"num-ops",       0, POPT_ARG_INT,  &torture_numops,    0,      "num ops",      NULL},
481                 {"entries",       0, POPT_ARG_INT,  &torture_entries,   0,      "entries",      NULL},
482                 {"loadfile",      0, POPT_ARG_STRING,   NULL,   OPT_LOADFILE,   "NBench load file to use",      NULL},
483                 {"list",          0, POPT_ARG_NONE, &list_tests, 0, "List available tests and exit", NULL },
484                 {"unclist",       0, POPT_ARG_STRING,   NULL,   OPT_UNCLIST,    "unclist",      NULL},
485                 {"timelimit",   't', POPT_ARG_INT,      NULL,   OPT_TIMELIMIT,  "Set time limit (in seconds)",  NULL},
486                 {"failures",    'f', POPT_ARG_INT,  &torture_failures,  0,      "failures",     NULL},
487                 {"parse-dns",   'D', POPT_ARG_STRING,   NULL,   OPT_DNS,        "parse-dns",    NULL},
488                 {"dangerous",   'X', POPT_ARG_NONE,     NULL,   OPT_DANGEROUS,
489                  "run dangerous tests (eg. wiping out password database)", NULL},
490                 {"load-module",  0,  POPT_ARG_STRING, &extra_module,     0, "load tests from DSO file",    "SOFILE"},
491                 {"shell",               0, POPT_ARG_NONE, &shell, true, "Run shell", NULL},
492                 {"target",              'T', POPT_ARG_STRING, &target, 0, "samba3|samba4|other", NULL},
493                 {"async",       'a', POPT_ARG_NONE,     NULL,   OPT_ASYNC,
494                  "run async tests", NULL},
495                 {"num-async",    0, POPT_ARG_INT,  &torture_numasync,  0,
496                  "number of simultaneous async requests", NULL},
497                 {"maximum-runtime", 0, POPT_ARG_INT, &max_runtime, 0, 
498                  "set maximum time for smbtorture to live", "seconds"},
499                 {"extra-user",   0, POPT_ARG_STRING, NULL, OPT_EXTRA_USER,
500                  "extra user credentials", NULL},
501                 POPT_COMMON_SAMBA
502                 POPT_COMMON_CONNECTION
503                 POPT_COMMON_CREDENTIALS
504                 POPT_COMMON_VERSION
505                 { NULL }
506         };
507
508         setlinebuf(stdout);
509
510         /* we are never interested in SIGPIPE */
511         BlockSignals(true, SIGPIPE);
512
513         pc = poptGetContext("smbtorture", argc, (const char **) argv, long_options, 
514                             POPT_CONTEXT_KEEP_FIRST);
515
516         poptSetOtherOptionHelp(pc, "<binding>|<unc> TEST1 TEST2 ...");
517
518         while((opt = poptGetNextOpt(pc)) != -1) {
519                 switch (opt) {
520                 case OPT_LOADFILE:
521                         lp_set_cmdline(cmdline_lp_ctx, "torture:loadfile", poptGetOptArg(pc));
522                         break;
523                 case OPT_UNCLIST:
524                         lp_set_cmdline(cmdline_lp_ctx, "torture:unclist", poptGetOptArg(pc));
525                         break;
526                 case OPT_TIMELIMIT:
527                         lp_set_cmdline(cmdline_lp_ctx, "torture:timelimit", poptGetOptArg(pc));
528                         break;
529                 case OPT_NUMPROGS:
530                         lp_set_cmdline(cmdline_lp_ctx, "torture:nprocs", poptGetOptArg(pc));
531                         break;
532                 case OPT_DNS:
533                         parse_dns(cmdline_lp_ctx, poptGetOptArg(pc));
534                         break;
535                 case OPT_DANGEROUS:
536                         lp_set_cmdline(cmdline_lp_ctx, "torture:dangerous", "Yes");
537                         break;
538                 case OPT_ASYNC:
539                         lp_set_cmdline(cmdline_lp_ctx, "torture:async", "Yes");
540                         break;
541                 case OPT_SMB_PORTS:
542                         lp_set_cmdline(cmdline_lp_ctx, "smb ports", poptGetOptArg(pc));
543                         break;
544                 case OPT_EXTRA_USER:
545                         {
546                                 char *option = talloc_asprintf(NULL, "torture:extra_user%u",
547                                                                ++num_extra_users);
548                                 char *value = poptGetOptArg(pc);
549                                 lp_set_cmdline(cmdline_lp_ctx, option, value);
550                                 talloc_free(option);
551                         }
552                         break;
553                 }
554         }
555
556         if (strcmp(target, "samba3") == 0) {
557                 lp_set_cmdline(cmdline_lp_ctx, "torture:samba3", "true");
558         } else if (strcmp(target, "samba4") == 0) {
559                 lp_set_cmdline(cmdline_lp_ctx, "torture:samba4", "true");
560         }
561
562         if (max_runtime) {
563                 /* this will only work if nobody else uses alarm(),
564                    which means it won't work for some tests, but we
565                    can't use the event context method we use for smbd
566                    as so many tests create their own event
567                    context. This will at least catch most cases. */
568                 signal(SIGALRM, max_runtime_handler);
569                 alarm(max_runtime);
570         }
571
572         if (extra_module != NULL) {
573             init_module_fn fn = load_module(talloc_autofree_context(), poptGetOptArg(pc));
574
575             if (fn == NULL) 
576                 d_printf("Unable to load module from %s\n", poptGetOptArg(pc));
577             else {
578                 status = fn();
579                 if (NT_STATUS_IS_ERR(status)) {
580                     d_printf("Error initializing module %s: %s\n", 
581                              poptGetOptArg(pc), nt_errstr(status));
582                 }
583             }
584         } else { 
585                 torture_init();
586         }
587
588         if (list_tests) {
589                 print_test_list();
590                 return 0;
591         }
592
593         if (torture_seed == 0) {
594                 torture_seed = time(NULL);
595         } 
596         printf("Using seed %d\n", torture_seed);
597         srandom(torture_seed);
598
599         argv_new = discard_const_p(char *, poptGetArgs(pc));
600
601         argc_new = argc;
602         for (i=0; i<argc; i++) {
603                 if (argv_new[i] == NULL) {
604                         argc_new = i;
605                         break;
606                 }
607         }
608
609         if (!(argc_new >= 3 || (shell && argc_new >= 2))) {
610                 usage(pc);
611                 exit(1);
612         }
613
614         if (!parse_target(cmdline_lp_ctx, argv_new[1])) {
615                 usage(pc);
616                 exit(1);
617         }
618
619         if (!strcmp(ui_ops_name, "simple")) {
620                 ui_ops = &std_ui_ops;
621         } else if (!strcmp(ui_ops_name, "subunit")) {
622                 ui_ops = &torture_subunit_ui_ops;
623         } else if (!strcmp(ui_ops_name, "quiet")) {
624                 ui_ops = &quiet_ui_ops;
625         } else {
626                 printf("Unknown output format '%s'\n", ui_ops_name);
627                 exit(1);
628         }
629
630         torture = torture_context_init(s4_event_context_init(NULL), ui_ops);
631         if (basedir != NULL) {
632                 if (basedir[0] != '/') {
633                         fprintf(stderr, "Please specify an absolute path to --basedir\n");
634                         return 1;
635                 }
636                 torture->outputdir = basedir;
637         } else {
638                 char *pwd = talloc_size(torture, PATH_MAX);
639                 if (!getcwd(pwd, PATH_MAX)) {
640                         fprintf(stderr, "Unable to determine current working directory\n");
641                         return 1;
642                 }
643                 torture->outputdir = pwd;
644         }
645
646         torture->lp_ctx = cmdline_lp_ctx;
647
648         if (argc_new == 0) {
649                 printf("You must specify a test to run, or 'ALL'\n");
650         } else if (shell) {
651                 run_shell(torture);
652         } else {
653                 for (i=2;i<argc_new;i++) {
654                         if (!run_test(torture, argv_new[i])) {
655                                 correct = false;
656                         }
657                 }
658         }
659
660         if (torture->returncode && correct) {
661                 return(0);
662         } else {
663                 return(1);
664         }
665 }