r16321: Add suite_start/suite_finish hooks, support --format=quiet
[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 2 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, write to the Free Software
19    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21
22 #include "includes.h"
23 #include "lib/cmdline/popt_common.h"
24 #include "system/time.h"
25 #include "system/wait.h"
26 #include "system/filesys.h"
27 #include "libcli/libcli.h"
28 #include "lib/ldb/include/ldb.h"
29 #include "lib/events/events.h"
30
31 #include "torture/torture.h"
32 #include "torture/ui.h"
33 #include "build.h"
34 #include "dlinklist.h"
35 #include "librpc/rpc/dcerpc.h"
36
37 #define MAX_COLS 80 /* FIXME: Determine this at run-time */
38
39 /****************************************************************************
40 run a specified test or "ALL"
41 ****************************************************************************/
42 static BOOL run_test(struct torture_context *torture, const char *name)
43 {
44         BOOL ret = True;
45         struct torture_op *o;
46         BOOL matched = False;
47
48         if (strequal(name,"ALL")) {
49                 for (o = torture_ops; o; o = o->next) {
50                         if (!run_test(torture, o->name)) {
51                                 ret = False;
52                         }
53                 }
54                 return ret;
55         }
56
57         for (o = torture_ops; o; o = o->next) {
58                 if (gen_fnmatch(name, o->name) == 0) {
59                         double t;
60                         matched = True;
61                         init_iconv();
62                         printf("Running %s\n", o->name);
63                         if (o->multi_fn) {
64                                 BOOL result = False;
65                                 t = torture_create_procs(o->multi_fn, 
66                                                          &result);
67                                 if (!result) { 
68                                         ret = False;
69                                         printf("TEST %s FAILED!\n", o->name);
70                                 }
71                                          
72                         } else {
73                                 struct timeval tv = timeval_current();
74                                 if (!o->fn(torture)) {
75                                         ret = False;
76                                         printf("TEST %s FAILED!\n", o->name);
77                                 }
78                                 t = timeval_elapsed(&tv);
79                         }
80                         printf("%s took %g secs\n\n", o->name, t);
81                 }
82         }
83
84         if (!matched) {
85                 printf("Unknown torture operation '%s'\n", name);
86                 ret = False;
87         }
88
89         return ret;
90 }
91
92 static void parse_dns(const char *dns)
93 {
94         char *userdn, *basedn, *secret;
95         char *p, *d;
96
97         /* retrievieng the userdn */
98         p = strchr_m(dns, '#');
99         if (!p) {
100                 lp_set_cmdline("torture:ldap_userdn", "");
101                 lp_set_cmdline("torture:ldap_basedn", "");
102                 lp_set_cmdline("torture:ldap_secret", "");
103                 return;
104         }
105         userdn = strndup(dns, p - dns);
106         lp_set_cmdline("torture:ldap_userdn", userdn);
107
108         /* retrieve the basedn */
109         d = p + 1;
110         p = strchr_m(d, '#');
111         if (!p) {
112                 lp_set_cmdline("torture:ldap_basedn", "");
113                 lp_set_cmdline("torture:ldap_secret", "");
114                 return;
115         }
116         basedn = strndup(d, p - d);
117         lp_set_cmdline("torture:ldap_basedn", basedn);
118
119         /* retrieve the secret */
120         p = p + 1;
121         if (!p) {
122                 lp_set_cmdline("torture:ldap_secret", "");
123                 return;
124         }
125         secret = strdup(p);
126         lp_set_cmdline("torture:ldap_secret", secret);
127
128         printf ("%s - %s - %s\n", userdn, basedn, secret);
129
130 }
131
132 static void usage(poptContext pc)
133 {
134         struct torture_op *o;
135         char last_prefix[64];
136         int i;
137
138         poptPrintUsage(pc, stdout, 0);
139         printf("\n");
140
141         printf("The binding format is:\n\n");
142
143         printf("  TRANSPORT:host[flags]\n\n");
144
145         printf("  where TRANSPORT is either ncacn_np for SMB, ncacn_ip_tcp for RPC/TCP\n");
146         printf("  or ncalrpc for local connections.\n\n");
147
148         printf("  'host' is an IP or hostname or netbios name. If the binding string\n");
149         printf("  identifies the server side of an endpoint, 'host' may be an empty\n");
150         printf("  string.\n\n");
151
152         printf("  'flags' can include a SMB pipe name if using the ncacn_np transport or\n");
153         printf("  a TCP port number if using the ncacn_ip_tcp transport, otherwise they\n");
154         printf("  will be auto-determined.\n\n");
155
156         printf("  other recognised flags are:\n\n");
157
158         printf("    sign : enable ntlmssp signing\n");
159         printf("    seal : enable ntlmssp sealing\n");
160         printf("    connect : enable rpc connect level auth (auth, but no sign or seal)\n");
161         printf("    validate: enable the NDR validator\n");
162         printf("    print: enable debugging of the packets\n");
163         printf("    bigendian: use bigendian RPC\n");
164         printf("    padcheck: check reply data for non-zero pad bytes\n\n");
165
166         printf("  For example, these all connect to the samr pipe:\n\n");
167
168         printf("    ncacn_np:myserver\n");
169         printf("    ncacn_np:myserver[samr]\n");
170         printf("    ncacn_np:myserver[\\pipe\\samr]\n");
171         printf("    ncacn_np:myserver[/pipe/samr]\n");
172         printf("    ncacn_np:myserver[samr,sign,print]\n");
173         printf("    ncacn_np:myserver[\\pipe\\samr,sign,seal,bigendian]\n");
174         printf("    ncacn_np:myserver[/pipe/samr,seal,validate]\n");
175         printf("    ncacn_np:\n");
176         printf("    ncacn_np:[/pipe/samr]\n\n");
177
178         printf("    ncacn_ip_tcp:myserver\n");
179         printf("    ncacn_ip_tcp:myserver[1024]\n");
180         printf("    ncacn_ip_tcp:myserver[1024,sign,seal]\n\n");
181
182         printf("    ncalrpc:\n\n");
183
184         printf("The UNC format is:\n\n");
185
186         printf("  //server/share\n\n");
187
188         printf("Tests are:");
189
190         i = 0;
191         last_prefix[0] = '\0';
192         for (o = torture_ops; o; o = o->next) {
193                 const char * sep;
194
195                 if ((sep = strchr(o->name, '-'))) {
196                         if (strncmp(o->name, last_prefix, sep - o->name) != 0) {
197                                 strncpy(last_prefix, o->name,
198                                         MIN(sizeof(last_prefix),
199                                             sep - o->name));
200                                 printf("\n\n  ");
201                                 i = 0;
202                         }
203                 }
204
205                 if (i + strlen(o->name) >= (MAX_COLS - 2)) {
206                         printf("\n  ");
207                         i = 0;
208                 }
209                 i+=printf("%s ", o->name);
210         }
211         printf("\n\n");
212
213         printf("The default test is ALL.\n");
214
215         exit(1);
216 }
217
218 static BOOL is_binding_string(const char *binding_string)
219 {
220         TALLOC_CTX *mem_ctx = talloc_init("is_binding_string");
221         struct dcerpc_binding *binding_struct;
222         NTSTATUS status;
223         
224         status = dcerpc_parse_binding(mem_ctx, binding_string, &binding_struct);
225
226         talloc_free(mem_ctx);
227         return NT_STATUS_IS_OK(status);
228 }
229
230 static void max_runtime_handler(int sig)
231 {
232         DEBUG(0,("maximum runtime exceeded for smbtorture - terminating\n"));
233         exit(1);
234 }
235
236 static void simple_tcase_start (struct torture_context *ctx, 
237                                                            struct torture_tcase *tcase)
238 {
239         printf("Testing %s\n", tcase->name);
240 }
241
242 static void simple_test_start (struct torture_context *ctx, 
243                                                            struct torture_tcase *tcase,
244                                                            struct torture_test *test)
245 {
246         printf("Testing %s/%s\n", tcase->name, test->name);
247 }
248
249 static void simple_test_result (struct torture_context *context, 
250                                                                 enum torture_result res, const char *reason)
251 {
252         switch (res) {
253         case TORTURE_OK:
254                 if (reason)
255                         printf("OK: %s\n", reason);
256                 break;
257         case TORTURE_FAIL:
258                 printf("ERROR: %s - %s\n", context->active_test->name, reason);
259                 break;
260         case TORTURE_TODO:
261                 printf("TODO: %s - %s\n", context->active_test->name, reason);
262                 break;
263         case TORTURE_SKIP:
264                 printf("SKIP: %s - %s\n", context->active_test->name, reason);
265                 break;
266
267         }
268 }
269
270 static void simple_comment (struct torture_context *test, const char *comment)
271 {
272         printf("# %s\n", comment);
273 }
274
275 const static struct torture_ui_ops std_ui_ops = {
276         .comment = simple_comment,
277         .test_start = simple_test_start,
278         .tcase_start = simple_tcase_start,
279         .test_result = simple_test_result
280 };
281
282
283 static void subunit_test_start (struct torture_context *ctx, 
284                                                             struct torture_tcase *tcase,
285                                                                 struct torture_test *test)
286 {
287         printf("test: %s\n", test->name);
288 }
289
290 static void subunit_test_result (struct torture_context *context, 
291                                                                  enum torture_result res, const char *reason)
292 {
293         switch (res) {
294         case TORTURE_OK:
295                 printf("success: %s\n", context->active_test->name);
296                 break;
297         case TORTURE_FAIL:
298                 printf("failure: %s [ %s ]\n", context->active_test->name, reason);
299                 break;
300         case TORTURE_TODO:
301                 printf("todo: %s\n", context->active_test->name);
302                 break;
303         case TORTURE_SKIP:
304                 printf("skip: %s\n", context->active_test->name);
305                 break;
306         }
307 }
308
309 static void subunit_comment (struct torture_context *test, const char *comment)
310 {
311         printf("# %s\n", comment);
312 }
313
314 const static struct torture_ui_ops subunit_ui_ops = {
315         .comment = subunit_comment,
316         .test_start = subunit_test_start,
317         .test_result = subunit_test_result
318 };
319
320 static void harness_test_start (struct torture_context *ctx, 
321                                                             struct torture_tcase *tcase,
322                                                                 struct torture_test *test)
323 {
324 }
325
326 static void harness_test_result (struct torture_context *context, 
327                                                                  enum torture_result res, const char *reason)
328 {
329         switch (res) {
330         case TORTURE_OK:
331                 printf("ok %s - %s\n", context->active_test->name, reason);
332                 break;
333         case TORTURE_FAIL:
334                 printf("not ok %s - %s\n", context->active_test->name, reason);
335                 break;
336         case TORTURE_TODO:
337                 printf("todo %s - %s\n", context->active_test->name, reason);
338                 break;
339         case TORTURE_SKIP:
340                 printf("skip %s - %s\n", context->active_test->name, reason);
341                 break;
342         }
343 }
344
345 static void harness_comment (struct torture_context *test, const char *comment)
346 {
347         printf("# %s\n", comment);
348 }
349
350 const static struct torture_ui_ops harness_ui_ops = {
351         .comment = harness_comment,
352         .test_start = harness_test_start,
353         .test_result = harness_test_result
354 };
355
356 static void quiet_test_start (struct torture_context *ctx, 
357                                                             struct torture_tcase *tcase,
358                                                                 struct torture_test *test)
359 {
360         putchar('.');
361 }
362
363 const static struct torture_ui_ops quiet_ui_ops = {
364         .test_start = quiet_test_start,
365 };
366
367
368 /****************************************************************************
369   main program
370 ****************************************************************************/
371  int main(int argc,char *argv[])
372 {
373         int opt, i;
374         char *p;
375         BOOL correct = True;
376         int max_runtime=0;
377         int argc_new;
378         struct torture_context *torture;
379         char **argv_new;
380         poptContext pc;
381         static const char *ui_ops_name = "simple";
382         enum {OPT_LOADFILE=1000,OPT_UNCLIST,OPT_TIMELIMIT,OPT_DNS,
383               OPT_DANGEROUS,OPT_SMB_PORTS,OPT_ASYNC};
384         
385         struct poptOption long_options[] = {
386                 POPT_AUTOHELP
387                 {"format", 0, POPT_ARG_STRING, &ui_ops_name, 0, "Output format (one of: simple, subunit, harness)", NULL },
388                 {"smb-ports",   'p', POPT_ARG_STRING, NULL,     OPT_SMB_PORTS,  "SMB ports",    NULL},
389                 {"seed",          0, POPT_ARG_INT,  &torture_seed,      0,      "seed",         NULL},
390                 {"num-progs",     0, POPT_ARG_INT,  &torture_nprocs,    0,      "num progs",    NULL},
391                 {"num-ops",       0, POPT_ARG_INT,  &torture_numops,    0,      "num ops",      NULL},
392                 {"entries",       0, POPT_ARG_INT,  &torture_entries,   0,      "entries",      NULL},
393                 {"use-oplocks", 'L', POPT_ARG_NONE, &use_oplocks,       0,      "use oplocks",  NULL},
394                 {"show-all",      0, POPT_ARG_NONE, &torture_showall,   0,      "show all",     NULL},
395                 {"loadfile",      0, POPT_ARG_STRING,   NULL,   OPT_LOADFILE,   "loadfile",     NULL},
396                 {"unclist",       0, POPT_ARG_STRING,   NULL,   OPT_UNCLIST,    "unclist",      NULL},
397                 {"timelimit",   't', POPT_ARG_STRING,   NULL,   OPT_TIMELIMIT,  "timelimit",    NULL},
398                 {"failures",    'f', POPT_ARG_INT,  &torture_failures,  0,      "failures",     NULL},
399                 {"parse-dns",   'D', POPT_ARG_STRING,   NULL,   OPT_DNS,        "parse-dns",    NULL},
400                 {"dangerous",   'X', POPT_ARG_NONE,     NULL,   OPT_DANGEROUS,
401                  "run dangerous tests (eg. wiping out password database)", NULL},
402                 {"async",       'a', POPT_ARG_NONE,     NULL,   OPT_ASYNC,
403                  "run async tests", NULL},
404                 {"num-async",    0, POPT_ARG_INT,  &torture_numasync,  0,
405                  "number of simultaneous async requests", NULL},
406                 {"maximum-runtime", 0, POPT_ARG_INT, &max_runtime, 0, 
407                  "set maximum time for smbtorture to live", "seconds"},
408                 POPT_COMMON_SAMBA
409                 POPT_COMMON_CONNECTION
410                 POPT_COMMON_CREDENTIALS
411                 POPT_COMMON_VERSION
412                 POPT_TABLEEND
413         };
414
415 #ifdef HAVE_SETBUFFER
416         setbuffer(stdout, NULL, 0);
417 #endif
418
419         /* we are never interested in SIGPIPE */
420         BlockSignals(True,SIGPIPE);
421
422         pc = poptGetContext("smbtorture", argc, (const char **) argv, long_options, 
423                             POPT_CONTEXT_KEEP_FIRST);
424
425         poptSetOtherOptionHelp(pc, "<binding>|<unc> TEST1 TEST2 ...");
426
427         while((opt = poptGetNextOpt(pc)) != -1) {
428                 switch (opt) {
429                 case OPT_LOADFILE:
430                         lp_set_cmdline("torture:loadfile", poptGetOptArg(pc));
431                         break;
432                 case OPT_UNCLIST:
433                         lp_set_cmdline("torture:unclist", poptGetOptArg(pc));
434                         break;
435                 case OPT_TIMELIMIT:
436                         lp_set_cmdline("torture:timelimit", poptGetOptArg(pc));
437                         break;
438                 case OPT_DNS:
439                         parse_dns(poptGetOptArg(pc));
440                         break;
441                 case OPT_DANGEROUS:
442                         lp_set_cmdline("torture:dangerous", "Yes");
443                         break;
444                 case OPT_ASYNC:
445                         lp_set_cmdline("torture:async", "Yes");
446                         break;
447                 case OPT_SMB_PORTS:
448                         lp_set_cmdline("smb ports", poptGetOptArg(pc));
449                         break;
450                 default:
451                         d_printf("Invalid option %s: %s\n", 
452                                  poptBadOption(pc, 0), poptStrerror(opt));
453                         torture_init();
454                         usage(pc);
455                         exit(1);
456                 }
457         }
458
459         if (max_runtime) {
460                 /* this will only work if nobody else uses alarm(),
461                    which means it won't work for some tests, but we
462                    can't use the event context method we use for smbd
463                    as so many tests create their own event
464                    context. This will at least catch most cases. */
465                 signal(SIGALRM, max_runtime_handler);
466                 alarm(max_runtime);
467         }
468
469         torture_init();
470         ldb_global_init();
471
472         if (torture_seed == 0) {
473                 torture_seed = time(NULL);
474         } 
475         printf("Using seed %d\n", torture_seed);
476         srandom(torture_seed);
477
478         argv_new = discard_const_p(char *, poptGetArgs(pc));
479
480         argc_new = argc;
481         for (i=0; i<argc; i++) {
482                 if (argv_new[i] == NULL) {
483                         argc_new = i;
484                         break;
485                 }
486         }
487
488         if (argc_new < 3) {
489                 usage(pc);
490                 exit(1);
491         }
492
493         for(p = argv_new[1]; *p; p++) {
494                 if(*p == '\\')
495                         *p = '/';
496         }
497
498         /* see if its a RPC transport specifier */
499         if (is_binding_string(argv_new[1])) {
500                 lp_set_cmdline("torture:binding", argv_new[1]);
501         } else {
502                 char *binding = NULL;
503                 char *host = NULL, *share = NULL;
504
505                 if (!smbcli_parse_unc(argv_new[1], NULL, &host, &share)) {
506                         d_printf("Invalid option: %s is not a valid torture target (share or binding string)\n\n", argv_new[1]);
507                         usage(pc);
508                 }
509
510                 lp_set_cmdline("torture:host", host);
511                 lp_set_cmdline("torture:share", share);
512                 asprintf(&binding, "ncacn_np:%s", host);
513                 lp_set_cmdline("torture:binding", binding);
514         }
515
516         torture = talloc_zero(NULL, struct torture_context);
517         if (!strcmp(ui_ops_name, "simple")) {
518                 torture->ui_ops = &std_ui_ops;
519         } else if (!strcmp(ui_ops_name, "subunit")) {
520                 torture->ui_ops = &subunit_ui_ops;
521         } else if (!strcmp(ui_ops_name, "harness")) {
522                 torture->ui_ops = &harness_ui_ops;
523         } else if (!strcmp(ui_ops_name, "quiet")) {
524                 torture->ui_ops = &quiet_ui_ops;
525         } else {
526                 printf("Unknown output format '%s'\n", ui_ops_name);
527                 exit(1);
528         }
529
530         if (argc_new == 0) {
531                 printf("You must specify a test to run, or 'ALL'\n");
532         } else {
533                 for (i=2;i<argc_new;i++) {
534                         if (!run_test(torture, argv_new[i])) {
535                                 correct = False;
536                         }
537                 }
538         }
539
540         talloc_free(torture);
541
542         if (correct) {
543                 return(0);
544         } else {
545                 return(1);
546         }
547 }