r19339: Merge my 4.0-unittest branch. This adds an API for more fine-grained
[kai/samba.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 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_open_connection(struct smbcli_state **c, int conn_index)
490 {
491         const char *host = lp_parm_string(-1, "torture", "host");
492         const char *share = lp_parm_string(-1, "torture", "share");
493         char **unc_list = NULL;
494         int num_unc_names = 0;
495         const char *p;
496         
497         p = lp_parm_string(-1, "torture", "unclist");
498         if (p) {
499                 char *h, *s;
500                 unc_list = file_lines_load(p, &num_unc_names, NULL);
501                 if (!unc_list || num_unc_names <= 0) {
502                         printf("Failed to load unc names list from '%s'\n", p);
503                         exit(1);
504                 }
505
506                 if (!smbcli_parse_unc(unc_list[conn_index % num_unc_names],
507                                       NULL, &h, &s)) {
508                         printf("Failed to parse UNC name %s\n",
509                                unc_list[conn_index % num_unc_names]);
510                         exit(1);
511                 }
512                 host = h;
513                 share = s;
514         }
515
516         return torture_open_connection_share(NULL, c, host, share, NULL);
517 }
518
519 _PUBLIC_ bool torture_open_connection_ev(struct smbcli_state **c,
520                                          int conn_index,
521                                          struct event_context *ev)
522 {
523         const char *host = lp_parm_string(-1, "torture", "host");
524         const char *share = lp_parm_string(-1, "torture", "share");
525         char **unc_list = NULL;
526         int num_unc_names = 0;
527         const char *p;
528         
529         p = lp_parm_string(-1, "torture", "unclist");
530         if (p) {
531                 char *h, *s;
532                 unc_list = file_lines_load(p, &num_unc_names, NULL);
533                 if (!unc_list || num_unc_names <= 0) {
534                         printf("Failed to load unc names list from '%s'\n", p);
535                         exit(1);
536                 }
537
538                 if (!smbcli_parse_unc(unc_list[conn_index % num_unc_names],
539                                       NULL, &h, &s)) {
540                         printf("Failed to parse UNC name %s\n",
541                                unc_list[conn_index % num_unc_names]);
542                         exit(1);
543                 }
544                 host = h;
545                 share = s;
546         }
547
548
549         return torture_open_connection_share(NULL, c, host, share, ev);
550 }
551
552
553
554 _PUBLIC_ bool torture_close_connection(struct smbcli_state *c)
555 {
556         bool ret = True;
557         if (!c) return True;
558         if (NT_STATUS_IS_ERR(smbcli_tdis(c))) {
559                 printf("tdis failed (%s)\n", smbcli_errstr(c->tree));
560                 ret = False;
561         }
562         talloc_free(c);
563         return ret;
564 }
565
566
567 /* check if the server produced the expected error code */
568 _PUBLIC_ bool check_error(const char *location, struct smbcli_state *c, 
569                  uint8_t eclass, uint32_t ecode, NTSTATUS nterr)
570 {
571         NTSTATUS status;
572         
573         status = smbcli_nt_error(c->tree);
574         if (NT_STATUS_IS_DOS(status)) {
575                 int class, num;
576                 class = NT_STATUS_DOS_CLASS(status);
577                 num = NT_STATUS_DOS_CODE(status);
578                 if (eclass != class || ecode != num) {
579                         printf("unexpected error code %s\n", nt_errstr(status));
580                         printf(" expected %s or %s (at %s)\n", 
581                                nt_errstr(NT_STATUS_DOS(eclass, ecode)), 
582                                nt_errstr(nterr), location);
583                         return False;
584                 }
585         } else {
586                 if (!NT_STATUS_EQUAL(nterr, status)) {
587                         printf("unexpected error code %s\n", nt_errstr(status));
588                         printf(" expected %s (at %s)\n", nt_errstr(nterr), location);
589                         return False;
590                 }
591         }
592
593         return True;
594 }
595
596 static struct smbcli_state *current_cli;
597 static int procnum; /* records process count number when forking */
598
599 static void sigcont(int sig)
600 {
601 }
602
603 double torture_create_procs(struct torture_context *tctx, 
604                                                         bool (*fn)(struct torture_context *, struct smbcli_state *, int), bool *result)
605 {
606         int i, status;
607         volatile pid_t *child_status;
608         volatile bool *child_status_out;
609         int synccount;
610         int tries = 8;
611         double start_time_limit = 10 + (torture_nprocs * 1.5);
612         struct timeval tv;
613
614         *result = True;
615
616         synccount = 0;
617
618         signal(SIGCONT, sigcont);
619
620         child_status = (volatile pid_t *)shm_setup(sizeof(pid_t)*torture_nprocs);
621         if (!child_status) {
622                 printf("Failed to setup shared memory\n");
623                 return -1;
624         }
625
626         child_status_out = (volatile bool *)shm_setup(sizeof(bool)*torture_nprocs);
627         if (!child_status_out) {
628                 printf("Failed to setup result status shared memory\n");
629                 return -1;
630         }
631
632         for (i = 0; i < torture_nprocs; i++) {
633                 child_status[i] = 0;
634                 child_status_out[i] = True;
635         }
636
637         tv = timeval_current();
638
639         for (i=0;i<torture_nprocs;i++) {
640                 procnum = i;
641                 if (fork() == 0) {
642                         char *myname;
643
644                         pid_t mypid = getpid();
645                         srandom(((int)mypid) ^ ((int)time(NULL)));
646
647                         asprintf(&myname, "CLIENT%d", i);
648                         lp_set_cmdline("netbios name", myname);
649                         free(myname);
650
651
652                         while (1) {
653                                 if (torture_open_connection(&current_cli, i)) {
654                                         break;
655                                 }
656                                 if (tries-- == 0) {
657                                         printf("pid %d failed to start\n", (int)getpid());
658                                         _exit(1);
659                                 }
660                                 msleep(100);    
661                         }
662
663                         child_status[i] = getpid();
664
665                         pause();
666
667                         if (child_status[i]) {
668                                 printf("Child %d failed to start!\n", i);
669                                 child_status_out[i] = 1;
670                                 _exit(1);
671                         }
672
673                         child_status_out[i] = fn(tctx, current_cli, i);
674                         _exit(0);
675                 }
676         }
677
678         do {
679                 synccount = 0;
680                 for (i=0;i<torture_nprocs;i++) {
681                         if (child_status[i]) synccount++;
682                 }
683                 if (synccount == torture_nprocs) break;
684                 msleep(100);
685         } while (timeval_elapsed(&tv) < start_time_limit);
686
687         if (synccount != torture_nprocs) {
688                 printf("FAILED TO START %d CLIENTS (started %d)\n", torture_nprocs, synccount);
689                 *result = False;
690                 return timeval_elapsed(&tv);
691         }
692
693         printf("Starting %d clients\n", torture_nprocs);
694
695         /* start the client load */
696         tv = timeval_current();
697         for (i=0;i<torture_nprocs;i++) {
698                 child_status[i] = 0;
699         }
700
701         printf("%d clients started\n", torture_nprocs);
702
703         kill(0, SIGCONT);
704
705         for (i=0;i<torture_nprocs;i++) {
706                 int ret;
707                 while ((ret=waitpid(0, &status, 0)) == -1 && errno == EINTR) /* noop */ ;
708                 if (ret == -1 || WEXITSTATUS(status) != 0) {
709                         *result = False;
710                 }
711         }
712
713         printf("\n");
714         
715         for (i=0;i<torture_nprocs;i++) {
716                 if (!child_status_out[i]) {
717                         *result = False;
718                 }
719         }
720         return timeval_elapsed(&tv);
721 }
722
723 static bool wrap_smb_multi_test(struct torture_context *torture,
724                                                                 struct torture_tcase *tcase,
725                                                                 struct torture_test *test)
726 {
727         bool (*fn)(struct torture_context *, struct smbcli_state *, int ) = test->fn;
728         bool result;
729
730         torture_create_procs(torture, fn, &result);
731
732         return result;
733 }
734
735 _PUBLIC_ struct torture_test *torture_suite_add_smb_multi_test(
736                                                                         struct torture_suite *suite,
737                                                                         const char *name,
738                                                                         bool (*run) (struct torture_context *,
739                                                                                                  struct smbcli_state *,
740                                                                                                 int i))
741 {
742         struct torture_test *test; 
743         struct torture_tcase *tcase;
744         
745         tcase = torture_suite_add_tcase(suite, name);
746
747         test = talloc(tcase, struct torture_test);
748
749         test->name = talloc_strdup(test, name);
750         test->description = NULL;
751         test->run = wrap_smb_multi_test;
752         test->fn = run;
753         test->dangerous = false;
754
755         DLIST_ADD_END(tcase->tests, test, struct torture_test *);
756
757         return test;
758
759 }
760
761 static bool wrap_simple_2smb_test(struct torture_context *torture_ctx,
762                                                                         struct torture_tcase *tcase,
763                                                                         struct torture_test *test)
764 {
765         bool (*fn) (struct torture_context *, struct smbcli_state *,
766                                 struct smbcli_state *);
767         bool ret;
768
769         struct smbcli_state *cli1, *cli2;
770
771         if (!torture_open_connection(&cli1, 0) || 
772                 !torture_open_connection(&cli2, 1))
773                 return false;
774
775         fn = test->fn;
776
777         ret = fn(torture_ctx, cli1, cli2);
778
779         talloc_free(cli1);
780         talloc_free(cli2);
781
782         return ret;
783 }
784
785
786
787 _PUBLIC_ struct torture_test *torture_suite_add_2smb_test(
788                                                                         struct torture_suite *suite,
789                                                                         const char *name,
790                                                                         bool (*run) (struct torture_context *,
791                                                                                                 struct smbcli_state *,
792                                                                                                 struct smbcli_state *))
793 {
794         struct torture_test *test; 
795         struct torture_tcase *tcase;
796         
797         tcase = torture_suite_add_tcase(suite, name);
798
799         test = talloc(tcase, struct torture_test);
800
801         test->name = talloc_strdup(test, name);
802         test->description = NULL;
803         test->run = wrap_simple_2smb_test;
804         test->fn = run;
805         test->dangerous = false;
806
807         DLIST_ADD_END(tcase->tests, test, struct torture_test *);
808
809         return test;
810
811 }
812
813 static bool wrap_simple_1smb_test(struct torture_context *torture_ctx,
814                                                                         struct torture_tcase *tcase,
815                                                                         struct torture_test *test)
816 {
817         bool (*fn) (struct torture_context *, struct smbcli_state *);
818         bool ret;
819
820         struct smbcli_state *cli1;
821
822         if (!torture_open_connection(&cli1, 0))
823                 return false;
824
825         fn = test->fn;
826
827         ret = fn(torture_ctx, cli1);
828
829         talloc_free(cli1);
830
831         return ret;
832 }
833
834 _PUBLIC_ struct torture_test *torture_suite_add_1smb_test(
835                                                                         struct torture_suite *suite,
836                                                                         const char *name,
837                                                                         bool (*run) (struct torture_context *,
838                                                                                                 struct smbcli_state *))
839 {
840         struct torture_test *test; 
841         struct torture_tcase *tcase;
842         
843         tcase = torture_suite_add_tcase(suite, name);
844
845         test = talloc(tcase, struct torture_test);
846
847         test->name = talloc_strdup(test, name);
848         test->description = NULL;
849         test->run = wrap_simple_1smb_test;
850         test->fn = run;
851         test->dangerous = false;
852
853         DLIST_ADD_END(tcase->tests, test, struct torture_test *);
854
855         return test;
856 }
857
858