r22834: fixed a memory leak in the torture_open_connection() code, and removed
[gd/samba-autobuild/.git] / source4 / torture / util_smb.c
1 /* 
2    Unix SMB/CIFS implementation.
3    SMB torture tester utility functions
4    Copyright (C) Andrew Tridgell 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 "libcli/raw/libcliraw.h"
25 #include "libcli/raw/ioctl.h"
26 #include "libcli/libcli.h"
27 #include "system/filesys.h"
28 #include "system/shmem.h"
29 #include "system/wait.h"
30 #include "system/time.h"
31 #include "torture/ui.h"
32 #include "torture/torture.h"
33 #include "util/dlinklist.h"
34
35
36 /**
37   setup a directory ready for a test
38 */
39 _PUBLIC_ bool torture_setup_dir(struct smbcli_state *cli, const char *dname)
40 {
41         smb_raw_exit(cli->session);
42         if (smbcli_deltree(cli->tree, dname) == -1 ||
43             NT_STATUS_IS_ERR(smbcli_mkdir(cli->tree, dname))) {
44                 printf("Unable to setup %s - %s\n", dname, smbcli_errstr(cli->tree));
45                 return False;
46         }
47         return True;
48 }
49
50 /*
51   create a directory, returning a handle to it
52 */
53 NTSTATUS create_directory_handle(struct smbcli_tree *tree, const char *dname, int *fnum)
54 {
55         NTSTATUS status;
56         union smb_open io;
57         TALLOC_CTX *mem_ctx;
58
59         mem_ctx = talloc_named_const(tree, 0, "create_directory_handle");
60
61         io.generic.level = RAW_OPEN_NTCREATEX;
62         io.ntcreatex.in.root_fid = 0;
63         io.ntcreatex.in.flags = 0;
64         io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
65         io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
66         io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
67         io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE;
68         io.ntcreatex.in.alloc_size = 0;
69         io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF;
70         io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
71         io.ntcreatex.in.security_flags = 0;
72         io.ntcreatex.in.fname = dname;
73
74         status = smb_raw_open(tree, mem_ctx, &io);
75         talloc_free(mem_ctx);
76
77         if (NT_STATUS_IS_OK(status)) {
78                 *fnum = io.ntcreatex.out.file.fnum;
79         }
80
81         return status;
82 }
83
84
85 /**
86   sometimes we need a fairly complex file to work with, so we can test
87   all possible attributes. 
88 */
89 _PUBLIC_ int create_complex_file(struct smbcli_state *cli, TALLOC_CTX *mem_ctx, const char *fname)
90 {
91         int fnum;
92         char buf[7] = "abc";
93         union smb_setfileinfo setfile;
94         union smb_fileinfo fileinfo;
95         time_t t = (time(NULL) & ~1);
96         NTSTATUS status;
97
98         smbcli_unlink(cli->tree, fname);
99         fnum = smbcli_nt_create_full(cli->tree, fname, 0, 
100                                      SEC_RIGHTS_FILE_ALL,
101                                      FILE_ATTRIBUTE_NORMAL,
102                                      NTCREATEX_SHARE_ACCESS_DELETE|
103                                      NTCREATEX_SHARE_ACCESS_READ|
104                                      NTCREATEX_SHARE_ACCESS_WRITE, 
105                                      NTCREATEX_DISP_OVERWRITE_IF,
106                                      0, 0);
107         if (fnum == -1) return -1;
108
109         smbcli_write(cli->tree, fnum, 0, buf, 0, sizeof(buf));
110
111         if (strchr(fname, ':') == NULL) {
112                 /* setup some EAs */
113                 setfile.generic.level = RAW_SFILEINFO_EA_SET;
114                 setfile.generic.in.file.fnum = fnum;
115                 setfile.ea_set.in.num_eas = 2;  
116                 setfile.ea_set.in.eas = talloc_array(mem_ctx, struct ea_struct, 2);
117                 setfile.ea_set.in.eas[0].flags = 0;
118                 setfile.ea_set.in.eas[0].name.s = "EAONE";
119                 setfile.ea_set.in.eas[0].value = data_blob_talloc(mem_ctx, "VALUE1", 6);
120                 setfile.ea_set.in.eas[1].flags = 0;
121                 setfile.ea_set.in.eas[1].name.s = "SECONDEA";
122                 setfile.ea_set.in.eas[1].value = data_blob_talloc(mem_ctx, "ValueTwo", 8);
123                 status = smb_raw_setfileinfo(cli->tree, &setfile);
124                 if (!NT_STATUS_IS_OK(status)) {
125                         printf("Failed to setup EAs\n");
126                 }
127         }
128
129         /* make sure all the timestamps aren't the same, and are also 
130            in different DST zones*/
131         setfile.generic.level = RAW_SFILEINFO_SETATTRE;
132         setfile.generic.in.file.fnum = fnum;
133
134         setfile.setattre.in.create_time = t + 9*30*24*60*60;
135         setfile.setattre.in.access_time = t + 6*30*24*60*60;
136         setfile.setattre.in.write_time  = t + 3*30*24*60*60;
137
138         status = smb_raw_setfileinfo(cli->tree, &setfile);
139         if (!NT_STATUS_IS_OK(status)) {
140                 printf("Failed to setup file times - %s\n", nt_errstr(status));
141         }
142
143         /* make sure all the timestamps aren't the same */
144         fileinfo.generic.level = RAW_FILEINFO_GETATTRE;
145         fileinfo.generic.in.file.fnum = fnum;
146
147         status = smb_raw_fileinfo(cli->tree, mem_ctx, &fileinfo);
148         if (!NT_STATUS_IS_OK(status)) {
149                 printf("Failed to query file times - %s\n", nt_errstr(status));
150         }
151
152         if (setfile.setattre.in.create_time != fileinfo.getattre.out.create_time) {
153                 printf("create_time not setup correctly\n");
154         }
155         if (setfile.setattre.in.access_time != fileinfo.getattre.out.access_time) {
156                 printf("access_time not setup correctly\n");
157         }
158         if (setfile.setattre.in.write_time != fileinfo.getattre.out.write_time) {
159                 printf("write_time not setup correctly\n");
160         }
161
162         return fnum;
163 }
164
165
166 /*
167   sometimes we need a fairly complex directory to work with, so we can test
168   all possible attributes. 
169 */
170 int create_complex_dir(struct smbcli_state *cli, TALLOC_CTX *mem_ctx, const char *dname)
171 {
172         int fnum;
173         union smb_setfileinfo setfile;
174         union smb_fileinfo fileinfo;
175         time_t t = (time(NULL) & ~1);
176         NTSTATUS status;
177
178         smbcli_deltree(cli->tree, dname);
179         fnum = smbcli_nt_create_full(cli->tree, dname, 0, 
180                                      SEC_RIGHTS_DIR_ALL,
181                                      FILE_ATTRIBUTE_DIRECTORY,
182                                      NTCREATEX_SHARE_ACCESS_READ|
183                                      NTCREATEX_SHARE_ACCESS_WRITE, 
184                                      NTCREATEX_DISP_OPEN_IF,
185                                      NTCREATEX_OPTIONS_DIRECTORY, 0);
186         if (fnum == -1) return -1;
187
188         if (strchr(dname, ':') == NULL) {
189                 /* setup some EAs */
190                 setfile.generic.level = RAW_SFILEINFO_EA_SET;
191                 setfile.generic.in.file.fnum = fnum;
192                 setfile.ea_set.in.num_eas = 2;  
193                 setfile.ea_set.in.eas = talloc_array(mem_ctx, struct ea_struct, 2);
194                 setfile.ea_set.in.eas[0].flags = 0;
195                 setfile.ea_set.in.eas[0].name.s = "EAONE";
196                 setfile.ea_set.in.eas[0].value = data_blob_talloc(mem_ctx, "VALUE1", 6);
197                 setfile.ea_set.in.eas[1].flags = 0;
198                 setfile.ea_set.in.eas[1].name.s = "SECONDEA";
199                 setfile.ea_set.in.eas[1].value = data_blob_talloc(mem_ctx, "ValueTwo", 8);
200                 status = smb_raw_setfileinfo(cli->tree, &setfile);
201                 if (!NT_STATUS_IS_OK(status)) {
202                         printf("Failed to setup EAs\n");
203                 }
204         }
205
206         /* make sure all the timestamps aren't the same, and are also 
207            in different DST zones*/
208         setfile.generic.level = RAW_SFILEINFO_SETATTRE;
209         setfile.generic.in.file.fnum = fnum;
210
211         setfile.setattre.in.create_time = t + 9*30*24*60*60;
212         setfile.setattre.in.access_time = t + 6*30*24*60*60;
213         setfile.setattre.in.write_time  = t + 3*30*24*60*60;
214
215         status = smb_raw_setfileinfo(cli->tree, &setfile);
216         if (!NT_STATUS_IS_OK(status)) {
217                 printf("Failed to setup file times - %s\n", nt_errstr(status));
218         }
219
220         /* make sure all the timestamps aren't the same */
221         fileinfo.generic.level = RAW_FILEINFO_GETATTRE;
222         fileinfo.generic.in.file.fnum = fnum;
223
224         status = smb_raw_fileinfo(cli->tree, mem_ctx, &fileinfo);
225         if (!NT_STATUS_IS_OK(status)) {
226                 printf("Failed to query file times - %s\n", nt_errstr(status));
227         }
228
229         if (setfile.setattre.in.create_time != fileinfo.getattre.out.create_time) {
230                 printf("create_time not setup correctly\n");
231         }
232         if (setfile.setattre.in.access_time != fileinfo.getattre.out.access_time) {
233                 printf("access_time not setup correctly\n");
234         }
235         if (setfile.setattre.in.write_time != fileinfo.getattre.out.write_time) {
236                 printf("write_time not setup correctly\n");
237         }
238
239         return fnum;
240 }
241
242
243
244 /* return a pointer to a anonymous shared memory segment of size "size"
245    which will persist across fork() but will disappear when all processes
246    exit 
247
248    The memory is not zeroed 
249
250    This function uses system5 shared memory. It takes advantage of a property
251    that the memory is not destroyed if it is attached when the id is removed
252    */
253 void *shm_setup(int size)
254 {
255         int shmid;
256         void *ret;
257
258         shmid = shmget(IPC_PRIVATE, size, SHM_R | SHM_W);
259         if (shmid == -1) {
260                 printf("can't get shared memory\n");
261                 exit(1);
262         }
263         ret = (void *)shmat(shmid, 0, 0);
264         if (!ret || ret == (void *)-1) {
265                 printf("can't attach to shared memory\n");
266                 return NULL;
267         }
268         /* the following releases the ipc, but note that this process
269            and all its children will still have access to the memory, its
270            just that the shmid is no longer valid for other shm calls. This
271            means we don't leave behind lots of shm segments after we exit 
272
273            See Stevens "advanced programming in unix env" for details
274            */
275         shmctl(shmid, IPC_RMID, 0);
276         
277         return ret;
278 }
279
280
281 /*
282   check that a wire string matches the flags specified 
283   not 100% accurate, but close enough for testing
284 */
285 bool wire_bad_flags(struct smb_wire_string *str, int flags, struct smbcli_state *cli)
286 {
287         bool server_unicode;
288         int len;
289         if (!str || !str->s) return True;
290         len = strlen(str->s);
291         if (flags & STR_TERMINATE) len++;
292
293         server_unicode = (cli->transport->negotiate.capabilities&CAP_UNICODE)?True:False;
294         if (getenv("CLI_FORCE_ASCII") || !lp_unicode()) {
295                 server_unicode = False;
296         }
297
298         if ((flags & STR_UNICODE) || server_unicode) {
299                 len *= 2;
300         } else if (flags & STR_TERMINATE_ASCII) {
301                 len++;
302         }
303         if (str->private_length != len) {
304                 printf("Expected wire_length %d but got %d for '%s'\n", 
305                        len, str->private_length, str->s);
306                 return True;
307         }
308         return False;
309 }
310
311 /*
312   dump a all_info QFILEINFO structure
313 */
314 void dump_all_info(TALLOC_CTX *mem_ctx, union smb_fileinfo *finfo)
315 {
316         d_printf("\tcreate_time:    %s\n", nt_time_string(mem_ctx, finfo->all_info.out.create_time));
317         d_printf("\taccess_time:    %s\n", nt_time_string(mem_ctx, finfo->all_info.out.access_time));
318         d_printf("\twrite_time:     %s\n", nt_time_string(mem_ctx, finfo->all_info.out.write_time));
319         d_printf("\tchange_time:    %s\n", nt_time_string(mem_ctx, finfo->all_info.out.change_time));
320         d_printf("\tattrib:         0x%x\n", finfo->all_info.out.attrib);
321         d_printf("\talloc_size:     %llu\n", (long long)finfo->all_info.out.alloc_size);
322         d_printf("\tsize:           %llu\n", (long long)finfo->all_info.out.size);
323         d_printf("\tnlink:          %u\n", finfo->all_info.out.nlink);
324         d_printf("\tdelete_pending: %u\n", finfo->all_info.out.delete_pending);
325         d_printf("\tdirectory:      %u\n", finfo->all_info.out.directory);
326         d_printf("\tea_size:        %u\n", finfo->all_info.out.ea_size);
327         d_printf("\tfname:          '%s'\n", finfo->all_info.out.fname.s);
328 }
329
330 /*
331   dump file infor by name
332 */
333 void torture_all_info(struct smbcli_tree *tree, const char *fname)
334 {
335         TALLOC_CTX *mem_ctx = talloc_named(tree, 0, "%s", fname);
336         union smb_fileinfo finfo;
337         NTSTATUS status;
338
339         finfo.generic.level = RAW_FILEINFO_ALL_INFO;
340         finfo.generic.in.file.path = fname;
341         status = smb_raw_pathinfo(tree, mem_ctx, &finfo);
342         if (!NT_STATUS_IS_OK(status)) {
343                 d_printf("%s - %s\n", fname, nt_errstr(status));
344                 return;
345         }
346
347         d_printf("%s:\n", fname);
348         dump_all_info(mem_ctx, &finfo);
349         talloc_free(mem_ctx);
350 }
351
352
353 /*
354   set a attribute on a file
355 */
356 bool torture_set_file_attribute(struct smbcli_tree *tree, const char *fname, uint16_t attrib)
357 {
358         union smb_setfileinfo sfinfo;
359         NTSTATUS status;
360
361         ZERO_STRUCT(sfinfo.basic_info.in);
362         sfinfo.basic_info.level = RAW_SFILEINFO_BASIC_INFORMATION;
363         sfinfo.basic_info.in.file.path = fname;
364         sfinfo.basic_info.in.attrib = attrib;
365         status = smb_raw_setpathinfo(tree, &sfinfo);
366         return NT_STATUS_IS_OK(status);
367 }
368
369
370 /*
371   set a file descriptor as sparse
372 */
373 NTSTATUS torture_set_sparse(struct smbcli_tree *tree, int fnum)
374 {
375         union smb_ioctl nt;
376         NTSTATUS status;
377         TALLOC_CTX *mem_ctx;
378
379         mem_ctx = talloc_named_const(tree, 0, "torture_set_sparse");
380         if (!mem_ctx) {
381                 return NT_STATUS_NO_MEMORY;
382         }
383
384         nt.ntioctl.level = RAW_IOCTL_NTIOCTL;
385         nt.ntioctl.in.function = FSCTL_SET_SPARSE;
386         nt.ntioctl.in.file.fnum = fnum;
387         nt.ntioctl.in.fsctl = True;
388         nt.ntioctl.in.filter = 0;
389         nt.ntioctl.in.max_data = 0;
390         nt.ntioctl.in.blob = data_blob(NULL, 0);
391
392         status = smb_raw_ioctl(tree, mem_ctx, &nt);
393
394         talloc_free(mem_ctx);
395
396         return status;
397 }
398
399 /*
400   check that an EA has the right value 
401 */
402 NTSTATUS torture_check_ea(struct smbcli_state *cli, 
403                           const char *fname, const char *eaname, const char *value)
404 {
405         union smb_fileinfo info;
406         NTSTATUS status;
407         struct ea_name ea;
408         TALLOC_CTX *mem_ctx = talloc_new(cli);
409
410         info.ea_list.level = RAW_FILEINFO_EA_LIST;
411         info.ea_list.in.file.path = fname;
412         info.ea_list.in.num_names = 1;
413         info.ea_list.in.ea_names = &ea;
414
415         ea.name.s = eaname;
416
417         status = smb_raw_pathinfo(cli->tree, mem_ctx, &info);
418         if (!NT_STATUS_IS_OK(status)) {
419                 talloc_free(mem_ctx);
420                 return status;
421         }
422
423         if (info.ea_list.out.num_eas != 1) {
424                 printf("Expected 1 ea in ea_list\n");
425                 talloc_free(mem_ctx);
426                 return NT_STATUS_EA_CORRUPT_ERROR;
427         }
428
429         if (strcasecmp_m(eaname, info.ea_list.out.eas[0].name.s) != 0) {
430                 printf("Expected ea '%s' not '%s' in ea_list\n",
431                        eaname, info.ea_list.out.eas[0].name.s);
432                 talloc_free(mem_ctx);
433                 return NT_STATUS_EA_CORRUPT_ERROR;
434         }
435
436         if (value == NULL) {
437                 if (info.ea_list.out.eas[0].value.length != 0) {
438                         printf("Expected zero length ea for %s\n", eaname);
439                         talloc_free(mem_ctx);
440                         return NT_STATUS_EA_CORRUPT_ERROR;
441                 }
442                 talloc_free(mem_ctx);
443                 return NT_STATUS_OK;
444         }
445
446         if (strlen(value) == info.ea_list.out.eas[0].value.length &&
447             memcmp(value, info.ea_list.out.eas[0].value.data,
448                    info.ea_list.out.eas[0].value.length) == 0) {
449                 talloc_free(mem_ctx);
450                 return NT_STATUS_OK;
451         }
452
453         printf("Expected value '%s' not '%*.*s' for ea %s\n",
454                value, 
455                (int)info.ea_list.out.eas[0].value.length,
456                (int)info.ea_list.out.eas[0].value.length,
457                info.ea_list.out.eas[0].value.data,
458                eaname);
459
460         talloc_free(mem_ctx);
461
462         return NT_STATUS_EA_CORRUPT_ERROR;
463 }
464
465 _PUBLIC_ bool torture_open_connection_share(TALLOC_CTX *mem_ctx,
466                                    struct smbcli_state **c, 
467                                    const char *hostname, 
468                                    const char *sharename,
469                                    struct event_context *ev)
470 {
471         NTSTATUS status;
472
473         status = smbcli_full_connection(mem_ctx, c, hostname, 
474                                         sharename, NULL,
475                                         cmdline_credentials, ev);
476         if (!NT_STATUS_IS_OK(status)) {
477                 printf("Failed to open connection - %s\n", nt_errstr(status));
478                 return False;
479         }
480
481         (*c)->transport->options.use_oplocks = lp_parm_bool(-1, "torture", 
482                                                                                                                 "use_oplocks", False);
483         (*c)->transport->options.use_level2_oplocks = lp_parm_bool(-1, "torture", 
484                                                                                                 "use_level2_oplocks", False);
485
486         return True;
487 }
488
489 _PUBLIC_ bool torture_get_conn_index(int conn_index,
490                                      TALLOC_CTX *mem_ctx,
491                                      char **host, char **share)
492 {
493         char **unc_list = NULL;
494         int num_unc_names = 0;
495         const char *p;
496
497         (*host) = talloc_strdup(mem_ctx, lp_parm_string(-1, "torture", "host"));
498         (*share) = talloc_strdup(mem_ctx, lp_parm_string(-1, "torture", "share"));
499         
500         p = lp_parm_string(-1, "torture", "unclist");
501         if (!p) {
502                 return True;
503         }
504
505         unc_list = file_lines_load(p, &num_unc_names, NULL);
506         if (!unc_list || num_unc_names <= 0) {
507                 DEBUG(0,("Failed to load unc names list from '%s'\n", p));
508                 return False;
509         }
510
511         if (!smbcli_parse_unc(unc_list[conn_index % num_unc_names],
512                               mem_ctx, host, share)) {
513                 DEBUG(0, ("Failed to parse UNC name %s\n",
514                           unc_list[conn_index % num_unc_names]));
515                 return False;
516         }
517
518         talloc_free(unc_list);
519         return True;
520 }
521
522
523
524 _PUBLIC_ bool torture_open_connection_ev(struct smbcli_state **c,
525                                          int conn_index,
526                                          struct event_context *ev)
527 {
528         char *host, *share;
529         bool ret;
530
531         if (!torture_get_conn_index(conn_index, ev, &host, &share)) {
532                 return False;
533         }
534
535         ret = torture_open_connection_share(NULL, c, host, share, ev);
536         talloc_free(host);
537         talloc_free(share);
538
539         return ret;
540 }
541
542 _PUBLIC_ bool torture_open_connection(struct smbcli_state **c, int conn_index)
543 {
544         return torture_open_connection_ev(c, conn_index, NULL);
545 }
546
547
548
549 _PUBLIC_ bool torture_close_connection(struct smbcli_state *c)
550 {
551         bool ret = True;
552         if (!c) return True;
553         if (NT_STATUS_IS_ERR(smbcli_tdis(c))) {
554                 printf("tdis failed (%s)\n", smbcli_errstr(c->tree));
555                 ret = False;
556         }
557         talloc_free(c);
558         return ret;
559 }
560
561
562 /* check if the server produced the expected error code */
563 _PUBLIC_ bool check_error(const char *location, struct smbcli_state *c, 
564                  uint8_t eclass, uint32_t ecode, NTSTATUS nterr)
565 {
566         NTSTATUS status;
567         
568         status = smbcli_nt_error(c->tree);
569         if (NT_STATUS_IS_DOS(status)) {
570                 int class, num;
571                 class = NT_STATUS_DOS_CLASS(status);
572                 num = NT_STATUS_DOS_CODE(status);
573                 if (eclass != class || ecode != num) {
574                         printf("unexpected error code %s\n", nt_errstr(status));
575                         printf(" expected %s or %s (at %s)\n", 
576                                nt_errstr(NT_STATUS_DOS(eclass, ecode)), 
577                                nt_errstr(nterr), location);
578                         return False;
579                 }
580         } else {
581                 if (!NT_STATUS_EQUAL(nterr, status)) {
582                         printf("unexpected error code %s\n", nt_errstr(status));
583                         printf(" expected %s (at %s)\n", nt_errstr(nterr), location);
584                         return False;
585                 }
586         }
587
588         return True;
589 }
590
591 static struct smbcli_state *current_cli;
592 static int procnum; /* records process count number when forking */
593
594 static void sigcont(int sig)
595 {
596 }
597
598 double torture_create_procs(struct torture_context *tctx, 
599                                                         bool (*fn)(struct torture_context *, struct smbcli_state *, int), bool *result)
600 {
601         int i, status;
602         volatile pid_t *child_status;
603         volatile bool *child_status_out;
604         int synccount;
605         int tries = 8;
606         int torture_nprocs = lp_parm_int(-1, "torture", "nprocs", 4);
607         double start_time_limit = 10 + (torture_nprocs * 1.5);
608         struct timeval tv;
609
610         *result = True;
611
612         synccount = 0;
613
614         signal(SIGCONT, sigcont);
615
616         child_status = (volatile pid_t *)shm_setup(sizeof(pid_t)*torture_nprocs);
617         if (!child_status) {
618                 printf("Failed to setup shared memory\n");
619                 return -1;
620         }
621
622         child_status_out = (volatile bool *)shm_setup(sizeof(bool)*torture_nprocs);
623         if (!child_status_out) {
624                 printf("Failed to setup result status shared memory\n");
625                 return -1;
626         }
627
628         for (i = 0; i < torture_nprocs; i++) {
629                 child_status[i] = 0;
630                 child_status_out[i] = True;
631         }
632
633         tv = timeval_current();
634
635         for (i=0;i<torture_nprocs;i++) {
636                 procnum = i;
637                 if (fork() == 0) {
638                         char *myname;
639
640                         pid_t mypid = getpid();
641                         srandom(((int)mypid) ^ ((int)time(NULL)));
642
643                         asprintf(&myname, "CLIENT%d", i);
644                         lp_set_cmdline("netbios name", myname);
645                         free(myname);
646
647
648                         while (1) {
649                                 if (torture_open_connection(&current_cli, i)) {
650                                         break;
651                                 }
652                                 if (tries-- == 0) {
653                                         printf("pid %d failed to start\n", (int)getpid());
654                                         _exit(1);
655                                 }
656                                 msleep(100);    
657                         }
658
659                         child_status[i] = getpid();
660
661                         pause();
662
663                         if (child_status[i]) {
664                                 printf("Child %d failed to start!\n", i);
665                                 child_status_out[i] = 1;
666                                 _exit(1);
667                         }
668
669                         child_status_out[i] = fn(tctx, current_cli, i);
670                         _exit(0);
671                 }
672         }
673
674         do {
675                 synccount = 0;
676                 for (i=0;i<torture_nprocs;i++) {
677                         if (child_status[i]) synccount++;
678                 }
679                 if (synccount == torture_nprocs) break;
680                 msleep(100);
681         } while (timeval_elapsed(&tv) < start_time_limit);
682
683         if (synccount != torture_nprocs) {
684                 printf("FAILED TO START %d CLIENTS (started %d)\n", torture_nprocs, synccount);
685                 *result = False;
686                 return timeval_elapsed(&tv);
687         }
688
689         printf("Starting %d clients\n", torture_nprocs);
690
691         /* start the client load */
692         tv = timeval_current();
693         for (i=0;i<torture_nprocs;i++) {
694                 child_status[i] = 0;
695         }
696
697         printf("%d clients started\n", torture_nprocs);
698
699         kill(0, SIGCONT);
700
701         for (i=0;i<torture_nprocs;i++) {
702                 int ret;
703                 while ((ret=waitpid(0, &status, 0)) == -1 && errno == EINTR) /* noop */ ;
704                 if (ret == -1 || WEXITSTATUS(status) != 0) {
705                         *result = False;
706                 }
707         }
708
709         printf("\n");
710         
711         for (i=0;i<torture_nprocs;i++) {
712                 if (!child_status_out[i]) {
713                         *result = False;
714                 }
715         }
716         return timeval_elapsed(&tv);
717 }
718
719 static bool wrap_smb_multi_test(struct torture_context *torture,
720                                                                 struct torture_tcase *tcase,
721                                                                 struct torture_test *test)
722 {
723         bool (*fn)(struct torture_context *, struct smbcli_state *, int ) = test->fn;
724         bool result;
725
726         torture_create_procs(torture, fn, &result);
727
728         return result;
729 }
730
731 _PUBLIC_ struct torture_test *torture_suite_add_smb_multi_test(
732                                                                         struct torture_suite *suite,
733                                                                         const char *name,
734                                                                         bool (*run) (struct torture_context *,
735                                                                                                  struct smbcli_state *,
736                                                                                                 int i))
737 {
738         struct torture_test *test; 
739         struct torture_tcase *tcase;
740         
741         tcase = torture_suite_add_tcase(suite, name);
742
743         test = talloc(tcase, struct torture_test);
744
745         test->name = talloc_strdup(test, name);
746         test->description = NULL;
747         test->run = wrap_smb_multi_test;
748         test->fn = run;
749         test->dangerous = false;
750
751         DLIST_ADD_END(tcase->tests, test, struct torture_test *);
752
753         return test;
754
755 }
756
757 static bool wrap_simple_2smb_test(struct torture_context *torture_ctx,
758                                                                         struct torture_tcase *tcase,
759                                                                         struct torture_test *test)
760 {
761         bool (*fn) (struct torture_context *, struct smbcli_state *,
762                                 struct smbcli_state *);
763         bool ret;
764
765         struct smbcli_state *cli1, *cli2;
766
767         if (!torture_open_connection(&cli1, 0) || 
768                 !torture_open_connection(&cli2, 1))
769                 return false;
770
771         fn = test->fn;
772
773         ret = fn(torture_ctx, cli1, cli2);
774
775         talloc_free(cli1);
776         talloc_free(cli2);
777
778         return ret;
779 }
780
781
782
783 _PUBLIC_ struct torture_test *torture_suite_add_2smb_test(
784                                                                         struct torture_suite *suite,
785                                                                         const char *name,
786                                                                         bool (*run) (struct torture_context *,
787                                                                                                 struct smbcli_state *,
788                                                                                                 struct smbcli_state *))
789 {
790         struct torture_test *test; 
791         struct torture_tcase *tcase;
792         
793         tcase = torture_suite_add_tcase(suite, name);
794
795         test = talloc(tcase, struct torture_test);
796
797         test->name = talloc_strdup(test, name);
798         test->description = NULL;
799         test->run = wrap_simple_2smb_test;
800         test->fn = run;
801         test->dangerous = false;
802
803         DLIST_ADD_END(tcase->tests, test, struct torture_test *);
804
805         return test;
806
807 }
808
809 static bool wrap_simple_1smb_test(struct torture_context *torture_ctx,
810                                                                         struct torture_tcase *tcase,
811                                                                         struct torture_test *test)
812 {
813         bool (*fn) (struct torture_context *, struct smbcli_state *);
814         bool ret;
815
816         struct smbcli_state *cli1;
817
818         if (!torture_open_connection(&cli1, 0))
819                 return false;
820
821         fn = test->fn;
822
823         ret = fn(torture_ctx, cli1);
824
825         talloc_free(cli1);
826
827         return ret;
828 }
829
830 _PUBLIC_ struct torture_test *torture_suite_add_1smb_test(
831                                                                         struct torture_suite *suite,
832                                                                         const char *name,
833                                                                         bool (*run) (struct torture_context *,
834                                                                                                 struct smbcli_state *))
835 {
836         struct torture_test *test; 
837         struct torture_tcase *tcase;
838         
839         tcase = torture_suite_add_tcase(suite, name);
840
841         test = talloc(tcase, struct torture_test);
842
843         test->name = talloc_strdup(test, name);
844         test->description = NULL;
845         test->run = wrap_simple_1smb_test;
846         test->fn = run;
847         test->dangerous = false;
848
849         DLIST_ADD_END(tcase->tests, test, struct torture_test *);
850
851         return test;
852 }
853
854