r16304: Improve testing UI API. This now allows registering the full
[gd/samba-autobuild/.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                                                            struct torture_test *test)
239 {
240         printf("Testing %s...\n", tcase->name);
241 }
242
243 static void simple_test_start (struct torture_context *ctx, 
244                                                            struct torture_tcase *tcase,
245                                                            struct torture_test *test)
246 {
247         printf("Testing %s/%s...\n", tcase->name, test->name);
248 }
249
250 static void simple_test_result (struct torture_context *context, 
251                                                                 enum torture_result res, const char *reason)
252 {
253         switch (res) {
254         case TORTURE_OK:
255                 if (reason)
256                         printf("OK: %s\n", reason);
257                 break;
258         case TORTURE_FAIL:
259                 printf("ERROR: %s - %s\n", context->active_test->name, reason);
260                 break;
261         case TORTURE_TODO:
262                 printf("TODO: %s - %s\n", context->active_test->name, reason);
263                 break;
264         case TORTURE_SKIP:
265                 printf("SKIP: %s - %s\n", context->active_test->name, reason);
266                 break;
267
268         }
269 }
270
271 static void simple_comment (struct torture_context *test, const char *comment)
272 {
273         printf("# %s\n", comment);
274 }
275
276 const static struct torture_ui_ops std_ui_ops = {
277         .comment = simple_comment,
278         .test_start = simple_test_start,
279         .tcase_start = simple_tcase_start,
280         .test_result = simple_test_result
281 };
282
283
284 static void subunit_test_start (struct torture_context *ctx, 
285                                                             struct torture_tcase *tcase,
286                                                                 struct torture_test *test)
287 {
288         printf("test: %s\n", test->name);
289 }
290
291 static void subunit_test_result (struct torture_context *context, 
292                                                                  enum torture_result res, const char *reason)
293 {
294         switch (res) {
295         case TORTURE_OK:
296                 printf("success: %s\n", context->active_test->name);
297                 break;
298         case TORTURE_FAIL:
299                 printf("failure: %s [ %s ]\n", context->active_test->name, reason);
300                 break;
301         case TORTURE_TODO:
302                 printf("todo: %s\n", context->active_test->name);
303                 break;
304         case TORTURE_SKIP:
305                 printf("skip: %s\n", context->active_test->name);
306                 break;
307         }
308 }
309
310 static void subunit_comment (struct torture_context *test, const char *comment)
311 {
312         printf("# %s\n", comment);
313 }
314
315 const static struct torture_ui_ops subunit_ui_ops = {
316         .comment = subunit_comment,
317         .test_start = subunit_test_start,
318         .test_result = subunit_test_result
319 };
320
321 static void harness_test_start (struct torture_context *ctx, 
322                                                             struct torture_tcase *tcase,
323                                                                 struct torture_test *test)
324 {
325 }
326
327 static void harness_test_result (struct torture_context *context, 
328                                                                  enum torture_result res, const char *reason)
329 {
330         switch (res) {
331         case TORTURE_OK:
332                 printf("ok %s - %s\n", context->active_test->name, reason);
333                 break;
334         case TORTURE_FAIL:
335                 printf("not ok %s - %s\n", context->active_test->name, reason);
336                 break;
337         case TORTURE_TODO:
338                 printf("todo %s - %s\n", context->active_test->name, reason);
339                 break;
340         case TORTURE_SKIP:
341                 printf("skip %s - %s\n", context->active_test->name, reason);
342                 break;
343         }
344 }
345
346 static void harness_comment (struct torture_context *test, const char *comment)
347 {
348         printf("# %s\n", comment);
349 }
350
351 const static struct torture_ui_ops harness_ui_ops = {
352         .comment = harness_comment,
353         .test_start = harness_test_start,
354         .test_result = harness_test_result
355 };
356
357 /****************************************************************************
358   main program
359 ****************************************************************************/
360  int main(int argc,char *argv[])
361 {
362         int opt, i;
363         char *p;
364         BOOL correct = True;
365         int max_runtime=0;
366         int argc_new;
367         struct torture_context *torture;
368         char **argv_new;
369         poptContext pc;
370         static const char *ui_ops_name = "simple";
371         enum {OPT_LOADFILE=1000,OPT_UNCLIST,OPT_TIMELIMIT,OPT_DNS,
372               OPT_DANGEROUS,OPT_SMB_PORTS,OPT_ASYNC};
373         
374         struct poptOption long_options[] = {
375                 POPT_AUTOHELP
376                 {"format", 0, POPT_ARG_STRING, &ui_ops_name, 0, "Output format (one of: simple, subunit, harness)", NULL },
377                 {"smb-ports",   'p', POPT_ARG_STRING, NULL,     OPT_SMB_PORTS,  "SMB ports",    NULL},
378                 {"seed",          0, POPT_ARG_INT,  &torture_seed,      0,      "seed",         NULL},
379                 {"num-progs",     0, POPT_ARG_INT,  &torture_nprocs,    0,      "num progs",    NULL},
380                 {"num-ops",       0, POPT_ARG_INT,  &torture_numops,    0,      "num ops",      NULL},
381                 {"entries",       0, POPT_ARG_INT,  &torture_entries,   0,      "entries",      NULL},
382                 {"use-oplocks", 'L', POPT_ARG_NONE, &use_oplocks,       0,      "use oplocks",  NULL},
383                 {"show-all",      0, POPT_ARG_NONE, &torture_showall,   0,      "show all",     NULL},
384                 {"loadfile",      0, POPT_ARG_STRING,   NULL,   OPT_LOADFILE,   "loadfile",     NULL},
385                 {"unclist",       0, POPT_ARG_STRING,   NULL,   OPT_UNCLIST,    "unclist",      NULL},
386                 {"timelimit",   't', POPT_ARG_STRING,   NULL,   OPT_TIMELIMIT,  "timelimit",    NULL},
387                 {"failures",    'f', POPT_ARG_INT,  &torture_failures,  0,      "failures",     NULL},
388                 {"parse-dns",   'D', POPT_ARG_STRING,   NULL,   OPT_DNS,        "parse-dns",    NULL},
389                 {"dangerous",   'X', POPT_ARG_NONE,     NULL,   OPT_DANGEROUS,
390                  "run dangerous tests (eg. wiping out password database)", NULL},
391                 {"async",       'a', POPT_ARG_NONE,     NULL,   OPT_ASYNC,
392                  "run async tests", NULL},
393                 {"num-async",    0, POPT_ARG_INT,  &torture_numasync,  0,
394                  "number of simultaneous async requests", NULL},
395                 {"maximum-runtime", 0, POPT_ARG_INT, &max_runtime, 0, 
396                  "set maximum time for smbtorture to live", "seconds"},
397                 POPT_COMMON_SAMBA
398                 POPT_COMMON_CONNECTION
399                 POPT_COMMON_CREDENTIALS
400                 POPT_COMMON_VERSION
401                 POPT_TABLEEND
402         };
403
404 #ifdef HAVE_SETBUFFER
405         setbuffer(stdout, NULL, 0);
406 #endif
407
408         /* we are never interested in SIGPIPE */
409         BlockSignals(True,SIGPIPE);
410
411         pc = poptGetContext("smbtorture", argc, (const char **) argv, long_options, 
412                             POPT_CONTEXT_KEEP_FIRST);
413
414         poptSetOtherOptionHelp(pc, "<binding>|<unc> TEST1 TEST2 ...");
415
416         while((opt = poptGetNextOpt(pc)) != -1) {
417                 switch (opt) {
418                 case OPT_LOADFILE:
419                         lp_set_cmdline("torture:loadfile", poptGetOptArg(pc));
420                         break;
421                 case OPT_UNCLIST:
422                         lp_set_cmdline("torture:unclist", poptGetOptArg(pc));
423                         break;
424                 case OPT_TIMELIMIT:
425                         lp_set_cmdline("torture:timelimit", poptGetOptArg(pc));
426                         break;
427                 case OPT_DNS:
428                         parse_dns(poptGetOptArg(pc));
429                         break;
430                 case OPT_DANGEROUS:
431                         lp_set_cmdline("torture:dangerous", "Yes");
432                         break;
433                 case OPT_ASYNC:
434                         lp_set_cmdline("torture:async", "Yes");
435                         break;
436                 case OPT_SMB_PORTS:
437                         lp_set_cmdline("smb ports", poptGetOptArg(pc));
438                         break;
439                 default:
440                         d_printf("Invalid option %s: %s\n", 
441                                  poptBadOption(pc, 0), poptStrerror(opt));
442                         torture_init();
443                         usage(pc);
444                         exit(1);
445                 }
446         }
447
448         if (max_runtime) {
449                 /* this will only work if nobody else uses alarm(),
450                    which means it won't work for some tests, but we
451                    can't use the event context method we use for smbd
452                    as so many tests create their own event
453                    context. This will at least catch most cases. */
454                 signal(SIGALRM, max_runtime_handler);
455                 alarm(max_runtime);
456         }
457
458         torture_init();
459         ldb_global_init();
460
461         if (torture_seed == 0) {
462                 torture_seed = time(NULL);
463         } 
464         printf("Using seed %d\n", torture_seed);
465         srandom(torture_seed);
466
467         argv_new = discard_const_p(char *, poptGetArgs(pc));
468
469         argc_new = argc;
470         for (i=0; i<argc; i++) {
471                 if (argv_new[i] == NULL) {
472                         argc_new = i;
473                         break;
474                 }
475         }
476
477         if (argc_new < 3) {
478                 usage(pc);
479                 exit(1);
480         }
481
482         for(p = argv_new[1]; *p; p++) {
483                 if(*p == '\\')
484                         *p = '/';
485         }
486
487         /* see if its a RPC transport specifier */
488         if (is_binding_string(argv_new[1])) {
489                 lp_set_cmdline("torture:binding", argv_new[1]);
490         } else {
491                 char *binding = NULL;
492                 char *host = NULL, *share = NULL;
493
494                 if (!smbcli_parse_unc(argv_new[1], NULL, &host, &share)) {
495                         d_printf("Invalid option: %s is not a valid torture target (share or binding string)\n\n", argv_new[1]);
496                         usage(pc);
497                 }
498
499                 lp_set_cmdline("torture:host", host);
500                 lp_set_cmdline("torture:share", share);
501                 asprintf(&binding, "ncacn_np:%s", host);
502                 lp_set_cmdline("torture:binding", binding);
503         }
504
505         torture = talloc_zero(NULL, struct torture_context);
506         if (!strcmp(ui_ops_name, "simple")) {
507                 torture->ui_ops = &std_ui_ops;
508         } else if (!strcmp(ui_ops_name, "subunit")) {
509                 torture->ui_ops = &subunit_ui_ops;
510         } else if (!strcmp(ui_ops_name, "harness")) {
511                 torture->ui_ops = &harness_ui_ops;
512         } else {
513                 printf("Unknown output format '%s'\n", ui_ops_name);
514                 exit(1);
515         }
516
517         if (argc_new == 0) {
518                 printf("You must specify a test to run, or 'ALL'\n");
519         } else {
520                 for (i=2;i<argc_new;i++) {
521                         if (!run_test(torture, argv_new[i])) {
522                                 correct = False;
523                         }
524                 }
525         }
526
527         talloc_free(torture);
528
529         if (correct) {
530                 return(0);
531         } else {
532                 return(1);
533         }
534 }