r15131: Tidy help output. When printing the list of tests, group them by prefix
[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
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 or ncacn_ip_tcp for RPC/TCP\n\n");
146
147         printf("  'host' is an IP or hostname or netbios name. If the binding string\n");
148         printf("  identifies the server side of an endpoint, 'host' may be an empty\n");
149         printf("  string.\n\n");
150
151         printf("  'flags' can include a SMB pipe name if using the ncacn_np transport or\n");
152         printf("  a TCP port number if using the ncacn_ip_tcp transport, otherwise they\n");
153         printf("  will be auto-determined.\n\n");
154
155         printf("  other recognised flags are:\n\n");
156
157         printf("    sign : enable ntlmssp signing\n");
158         printf("    seal : enable ntlmssp sealing\n");
159         printf("    connect : enable rpc connect level auth (auth, but no sign or seal)\n");
160         printf("    validate: enable the NDR validator\n");
161         printf("    print: enable debugging of the packets\n");
162         printf("    bigendian: use bigendian RPC\n");
163         printf("    padcheck: check reply data for non-zero pad bytes\n\n");
164
165         printf("  For example, these all connect to the samr pipe:\n\n");
166
167         printf("    ncacn_np:myserver\n");
168         printf("    ncacn_np:myserver[samr]\n");
169         printf("    ncacn_np:myserver[\\pipe\\samr]\n");
170         printf("    ncacn_np:myserver[/pipe/samr]\n");
171         printf("    ncacn_np:myserver[samr,sign,print]\n");
172         printf("    ncacn_np:myserver[\\pipe\\samr,sign,seal,bigendian]\n");
173         printf("    ncacn_np:myserver[/pipe/samr,seal,validate]\n");
174         printf("    ncacn_np:\n");
175         printf("    ncacn_np:[/pipe/samr]\n\n");
176
177         printf("    ncacn_ip_tcp:myserver\n");
178         printf("    ncacn_ip_tcp:myserver[1024]\n");
179         printf("    ncacn_ip_tcp:myserver[1024,sign,seal]\n\n");
180
181         printf("The UNC format is:\n\n");
182
183         printf("  //server/share\n\n");
184
185         printf("Tests are:");
186
187         i = 0;
188         last_prefix[0] = '\0';
189         for (o = torture_ops; o; o = o->next) {
190                 const char * sep;
191
192                 if ((sep = strchr(o->name, '-'))) {
193                         if (strncmp(o->name, last_prefix, sep - o->name) != 0) {
194                                 strncpy(last_prefix, o->name,
195                                         MIN(sizeof(last_prefix),
196                                             sep - o->name));
197                                 printf("\n\n  ");
198                                 i = 0;
199                         }
200                 }
201
202                 if (i + strlen(o->name) >= (MAX_COLS - 2)) {
203                         printf("\n  ");
204                         i = 0;
205                 }
206                 i+=printf("%s ", o->name);
207         }
208         printf("\n\n");
209
210         printf("The default test is ALL.\n");
211
212         exit(1);
213 }
214
215 static BOOL is_binding_string(const char *binding_string)
216 {
217         TALLOC_CTX *mem_ctx = talloc_init("is_binding_string");
218         struct dcerpc_binding *binding_struct;
219         NTSTATUS status;
220         
221         status = dcerpc_parse_binding(mem_ctx, binding_string, &binding_struct);
222
223         talloc_free(mem_ctx);
224         return NT_STATUS_IS_OK(status);
225 }
226
227 static void max_runtime_handler(int sig)
228 {
229         DEBUG(0,("maximum runtime exceeded for smbtorture - terminating\n"));
230         exit(1);
231 }
232
233 static void simple_test_start (struct torture_test *test)
234 {
235         printf("Testing %s...\n", test->name);
236 }
237
238 static void simple_test_result (struct torture_test *test, enum torture_result res)
239 {
240         printf("\t %d\n",res);
241 }
242
243 static void simple_comment (struct torture_test *test, const char *comment)
244 {
245         printf("# %s\n", comment);
246 }
247
248 const static struct torture_ui_ops std_ui_ops = {
249         .comment = simple_comment,
250         .test_start = simple_test_start,
251         .test_result = simple_test_result
252 };
253
254 /****************************************************************************
255   main program
256 ****************************************************************************/
257  int main(int argc,char *argv[])
258 {
259         int opt, i;
260         char *p;
261         BOOL correct = True;
262         int max_runtime=0;
263         int argc_new;
264         struct torture_context *torture;
265         char **argv_new;
266         poptContext pc;
267         enum {OPT_LOADFILE=1000,OPT_UNCLIST,OPT_TIMELIMIT,OPT_DNS,
268             OPT_DANGEROUS,OPT_SMB_PORTS};
269         
270         struct poptOption long_options[] = {
271                 POPT_AUTOHELP
272                 {"smb-ports",   'p', POPT_ARG_STRING, NULL,     OPT_SMB_PORTS,  "SMB ports",    NULL},
273                 {"seed",          0, POPT_ARG_INT,  &torture_seed,      0,      "seed",         NULL},
274                 {"num-progs",     0, POPT_ARG_INT,  &torture_nprocs,    0,      "num progs",    NULL},
275                 {"num-ops",       0, POPT_ARG_INT,  &torture_numops,    0,      "num ops",      NULL},
276                 {"entries",       0, POPT_ARG_INT,  &torture_entries,   0,      "entries",      NULL},
277                 {"use-oplocks", 'L', POPT_ARG_NONE, &use_oplocks,       0,      "use oplocks",  NULL},
278                 {"show-all",      0, POPT_ARG_NONE, &torture_showall,   0,      "show all",     NULL},
279                 {"loadfile",      0, POPT_ARG_STRING,   NULL,   OPT_LOADFILE,   "loadfile",     NULL},
280                 {"unclist",       0, POPT_ARG_STRING,   NULL,   OPT_UNCLIST,    "unclist",      NULL},
281                 {"timelimit",   't', POPT_ARG_STRING,   NULL,   OPT_TIMELIMIT,  "timelimit",    NULL},
282                 {"failures",    'f', POPT_ARG_INT,  &torture_failures,  0,      "failures",     NULL},
283                 {"parse-dns",   'D', POPT_ARG_STRING,   NULL,   OPT_DNS,        "parse-dns",    NULL},
284                 {"dangerous",   'X', POPT_ARG_NONE,     NULL,   OPT_DANGEROUS,  "dangerous",    NULL},
285                 {"maximum-runtime", 0, POPT_ARG_INT, &max_runtime, 0, 
286                  "set maximum time for smbtorture to live", "seconds"},
287                 POPT_COMMON_SAMBA
288                 POPT_COMMON_CONNECTION
289                 POPT_COMMON_CREDENTIALS
290                 POPT_COMMON_VERSION
291                 POPT_TABLEEND
292         };
293
294 #ifdef HAVE_SETBUFFER
295         setbuffer(stdout, NULL, 0);
296 #endif
297
298         /* we are never interested in SIGPIPE */
299         BlockSignals(True,SIGPIPE);
300
301         pc = poptGetContext("smbtorture", argc, (const char **) argv, long_options, 
302                             POPT_CONTEXT_KEEP_FIRST);
303
304         poptSetOtherOptionHelp(pc, "<binding>|<unc> TEST1 TEST2 ...");
305
306         while((opt = poptGetNextOpt(pc)) != -1) {
307                 switch (opt) {
308                 case OPT_LOADFILE:
309                         lp_set_cmdline("torture:loadfile", poptGetOptArg(pc));
310                         break;
311                 case OPT_UNCLIST:
312                         lp_set_cmdline("torture:unclist", poptGetOptArg(pc));
313                         break;
314                 case OPT_TIMELIMIT:
315                         lp_set_cmdline("torture:timelimit", poptGetOptArg(pc));
316                         break;
317                 case OPT_DNS:
318                         parse_dns(poptGetOptArg(pc));
319                         break;
320                 case OPT_DANGEROUS:
321                         lp_set_cmdline("torture:dangerous", "Yes");
322                         break;
323                 case OPT_SMB_PORTS:
324                         lp_set_cmdline("smb ports", poptGetOptArg(pc));
325                         break;
326                 default:
327                         d_printf("Invalid option %s: %s\n", 
328                                  poptBadOption(pc, 0), poptStrerror(opt));
329                         torture_init();
330                         usage(pc);
331                         exit(1);
332                 }
333         }
334
335         if (max_runtime) {
336                 /* this will only work if nobody else uses alarm(),
337                    which means it won't work for some tests, but we
338                    can't use the event context method we use for smbd
339                    as so many tests create their own event
340                    context. This will at least catch most cases. */
341                 signal(SIGALRM, max_runtime_handler);
342                 alarm(max_runtime);
343         }
344
345         torture_init();
346         ldb_global_init();
347
348         if (torture_seed == 0) {
349                 torture_seed = time(NULL);
350         } 
351         printf("Using seed %d\n", torture_seed);
352         srandom(torture_seed);
353
354         argv_new = discard_const_p(char *, poptGetArgs(pc));
355
356         argc_new = argc;
357         for (i=0; i<argc; i++) {
358                 if (argv_new[i] == NULL) {
359                         argc_new = i;
360                         break;
361                 }
362         }
363
364         if (argc_new < 3) {
365                 usage(pc);
366                 exit(1);
367         }
368
369         for(p = argv_new[1]; *p; p++) {
370                 if(*p == '\\')
371                         *p = '/';
372         }
373
374         /* see if its a RPC transport specifier */
375         if (is_binding_string(argv_new[1])) {
376                 lp_set_cmdline("torture:binding", argv_new[1]);
377         } else {
378                 char *binding = NULL;
379                 char *host = NULL, *share = NULL;
380
381                 if (!smbcli_parse_unc(argv_new[1], NULL, &host, &share)) {
382                         d_printf("Invalid option: %s is not a valid torture target (share or binding string)\n\n", argv_new[1]);
383                         usage(pc);
384                 }
385
386                 lp_set_cmdline("torture:host", host);
387                 lp_set_cmdline("torture:share", share);
388                 asprintf(&binding, "ncacn_np:%s", host);
389                 lp_set_cmdline("torture:binding", binding);
390         }
391
392         torture = talloc_zero(NULL, struct torture_context);
393         torture->ui_ops = &std_ui_ops;
394
395         if (argc_new == 0) {
396                 printf("You must specify a test to run, or 'ALL'\n");
397         } else {
398                 for (i=2;i<argc_new;i++) {
399                         if (!run_test(torture, argv_new[i])) {
400                                 correct = False;
401                         }
402                 }
403         }
404
405         talloc_free(torture);
406
407         if (correct) {
408                 return(0);
409         } else {
410                 return(1);
411         }
412 }