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