d077b3b515691b7a51748c93c9c590fb32288501
[amitay/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 3 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, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include "includes.h"
22 #include "lib/cmdline/popt_common.h"
23 #include "libcli/raw/libcliraw.h"
24 #include "libcli/raw/ioctl.h"
25 #include "libcli/libcli.h"
26 #include "system/filesys.h"
27 #include "system/shmem.h"
28 #include "system/wait.h"
29 #include "system/time.h"
30 #include "torture/ui.h"
31 #include "torture/torture.h"
32 #include "util/dlinklist.h"
33 #include "auth/credentials/credentials.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_transport *transport)
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 = (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, 
545                                           cli_credentials_get_event_context(cmdline_credentials));
546 }
547
548
549
550 _PUBLIC_ bool torture_close_connection(struct smbcli_state *c)
551 {
552         bool ret = True;
553         if (!c) return True;
554         if (NT_STATUS_IS_ERR(smbcli_tdis(c))) {
555                 printf("tdis failed (%s)\n", smbcli_errstr(c->tree));
556                 ret = False;
557         }
558         talloc_free(c);
559         return ret;
560 }
561
562
563 /* check if the server produced the expected error code */
564 _PUBLIC_ bool check_error(const char *location, struct smbcli_state *c, 
565                  uint8_t eclass, uint32_t ecode, NTSTATUS nterr)
566 {
567         NTSTATUS status;
568         
569         status = smbcli_nt_error(c->tree);
570         if (NT_STATUS_IS_DOS(status)) {
571                 int class, num;
572                 class = NT_STATUS_DOS_CLASS(status);
573                 num = NT_STATUS_DOS_CODE(status);
574                 if (eclass != class || ecode != num) {
575                         printf("unexpected error code %s\n", nt_errstr(status));
576                         printf(" expected %s or %s (at %s)\n", 
577                                nt_errstr(NT_STATUS_DOS(eclass, ecode)), 
578                                nt_errstr(nterr), location);
579                         return False;
580                 }
581         } else {
582                 if (!NT_STATUS_EQUAL(nterr, status)) {
583                         printf("unexpected error code %s\n", nt_errstr(status));
584                         printf(" expected %s (at %s)\n", nt_errstr(nterr), location);
585                         return False;
586                 }
587         }
588
589         return True;
590 }
591
592 static struct smbcli_state *current_cli;
593 static int procnum; /* records process count number when forking */
594
595 static void sigcont(int sig)
596 {
597 }
598
599 double torture_create_procs(struct torture_context *tctx, 
600                                                         bool (*fn)(struct torture_context *, struct smbcli_state *, int), bool *result)
601 {
602         int i, status;
603         volatile pid_t *child_status;
604         volatile bool *child_status_out;
605         int synccount;
606         int tries = 8;
607         int torture_nprocs = torture_setting_int(tctx, "nprocs", 4);
608         double start_time_limit = 10 + (torture_nprocs * 1.5);
609         struct timeval tv;
610
611         *result = True;
612
613         synccount = 0;
614
615         signal(SIGCONT, sigcont);
616
617         child_status = (volatile pid_t *)shm_setup(sizeof(pid_t)*torture_nprocs);
618         if (!child_status) {
619                 printf("Failed to setup shared memory\n");
620                 return -1;
621         }
622
623         child_status_out = (volatile bool *)shm_setup(sizeof(bool)*torture_nprocs);
624         if (!child_status_out) {
625                 printf("Failed to setup result status shared memory\n");
626                 return -1;
627         }
628
629         for (i = 0; i < torture_nprocs; i++) {
630                 child_status[i] = 0;
631                 child_status_out[i] = True;
632         }
633
634         tv = timeval_current();
635
636         for (i=0;i<torture_nprocs;i++) {
637                 procnum = i;
638                 if (fork() == 0) {
639                         char *myname;
640
641                         pid_t mypid = getpid();
642                         srandom(((int)mypid) ^ ((int)time(NULL)));
643
644                         asprintf(&myname, "CLIENT%d", i);
645                         lp_set_cmdline("netbios name", myname);
646                         free(myname);
647
648
649                         while (1) {
650                                 if (torture_open_connection(&current_cli, i)) {
651                                         break;
652                                 }
653                                 if (tries-- == 0) {
654                                         printf("pid %d failed to start\n", (int)getpid());
655                                         _exit(1);
656                                 }
657                                 msleep(100);    
658                         }
659
660                         child_status[i] = getpid();
661
662                         pause();
663
664                         if (child_status[i]) {
665                                 printf("Child %d failed to start!\n", i);
666                                 child_status_out[i] = 1;
667                                 _exit(1);
668                         }
669
670                         child_status_out[i] = fn(tctx, current_cli, i);
671                         _exit(0);
672                 }
673         }
674
675         do {
676                 synccount = 0;
677                 for (i=0;i<torture_nprocs;i++) {
678                         if (child_status[i]) synccount++;
679                 }
680                 if (synccount == torture_nprocs) break;
681                 msleep(100);
682         } while (timeval_elapsed(&tv) < start_time_limit);
683
684         if (synccount != torture_nprocs) {
685                 printf("FAILED TO START %d CLIENTS (started %d)\n", torture_nprocs, synccount);
686                 *result = False;
687                 return timeval_elapsed(&tv);
688         }
689
690         printf("Starting %d clients\n", torture_nprocs);
691
692         /* start the client load */
693         tv = timeval_current();
694         for (i=0;i<torture_nprocs;i++) {
695                 child_status[i] = 0;
696         }
697
698         printf("%d clients started\n", torture_nprocs);
699
700         kill(0, SIGCONT);
701
702         for (i=0;i<torture_nprocs;i++) {
703                 int ret;
704                 while ((ret=waitpid(0, &status, 0)) == -1 && errno == EINTR) /* noop */ ;
705                 if (ret == -1 || WEXITSTATUS(status) != 0) {
706                         *result = False;
707                 }
708         }
709
710         printf("\n");
711         
712         for (i=0;i<torture_nprocs;i++) {
713                 if (!child_status_out[i]) {
714                         *result = False;
715                 }
716         }
717         return timeval_elapsed(&tv);
718 }
719
720 static bool wrap_smb_multi_test(struct torture_context *torture,
721                                                                 struct torture_tcase *tcase,
722                                                                 struct torture_test *test)
723 {
724         bool (*fn)(struct torture_context *, struct smbcli_state *, int ) = test->fn;
725         bool result;
726
727         torture_create_procs(torture, fn, &result);
728
729         return result;
730 }
731
732 _PUBLIC_ struct torture_test *torture_suite_add_smb_multi_test(
733                                                                         struct torture_suite *suite,
734                                                                         const char *name,
735                                                                         bool (*run) (struct torture_context *,
736                                                                                                  struct smbcli_state *,
737                                                                                                 int i))
738 {
739         struct torture_test *test; 
740         struct torture_tcase *tcase;
741         
742         tcase = torture_suite_add_tcase(suite, name);
743
744         test = talloc(tcase, struct torture_test);
745
746         test->name = talloc_strdup(test, name);
747         test->description = NULL;
748         test->run = wrap_smb_multi_test;
749         test->fn = run;
750         test->dangerous = false;
751
752         DLIST_ADD_END(tcase->tests, test, struct torture_test *);
753
754         return test;
755
756 }
757
758 static bool wrap_simple_2smb_test(struct torture_context *torture_ctx,
759                                                                         struct torture_tcase *tcase,
760                                                                         struct torture_test *test)
761 {
762         bool (*fn) (struct torture_context *, struct smbcli_state *,
763                                 struct smbcli_state *);
764         bool ret;
765
766         struct smbcli_state *cli1, *cli2;
767
768         if (!torture_open_connection(&cli1, 0) || 
769                 !torture_open_connection(&cli2, 1))
770                 return false;
771
772         fn = test->fn;
773
774         ret = fn(torture_ctx, cli1, cli2);
775
776         talloc_free(cli1);
777         talloc_free(cli2);
778
779         return ret;
780 }
781
782
783
784 _PUBLIC_ struct torture_test *torture_suite_add_2smb_test(
785                                                                         struct torture_suite *suite,
786                                                                         const char *name,
787                                                                         bool (*run) (struct torture_context *,
788                                                                                                 struct smbcli_state *,
789                                                                                                 struct smbcli_state *))
790 {
791         struct torture_test *test; 
792         struct torture_tcase *tcase;
793         
794         tcase = torture_suite_add_tcase(suite, name);
795
796         test = talloc(tcase, struct torture_test);
797
798         test->name = talloc_strdup(test, name);
799         test->description = NULL;
800         test->run = wrap_simple_2smb_test;
801         test->fn = run;
802         test->dangerous = false;
803
804         DLIST_ADD_END(tcase->tests, test, struct torture_test *);
805
806         return test;
807
808 }
809
810 static bool wrap_simple_1smb_test(struct torture_context *torture_ctx,
811                                                                         struct torture_tcase *tcase,
812                                                                         struct torture_test *test)
813 {
814         bool (*fn) (struct torture_context *, struct smbcli_state *);
815         bool ret;
816
817         struct smbcli_state *cli1;
818
819         if (!torture_open_connection(&cli1, 0))
820                 return false;
821
822         fn = test->fn;
823
824         ret = fn(torture_ctx, cli1);
825
826         talloc_free(cli1);
827
828         return ret;
829 }
830
831 _PUBLIC_ struct torture_test *torture_suite_add_1smb_test(
832                                                                         struct torture_suite *suite,
833                                                                         const char *name,
834                                                                         bool (*run) (struct torture_context *,
835                                                                                                 struct smbcli_state *))
836 {
837         struct torture_test *test; 
838         struct torture_tcase *tcase;
839         
840         tcase = torture_suite_add_tcase(suite, name);
841
842         test = talloc(tcase, struct torture_test);
843
844         test->name = talloc_strdup(test, name);
845         test->description = NULL;
846         test->run = wrap_simple_1smb_test;
847         test->fn = run;
848         test->dangerous = false;
849
850         DLIST_ADD_END(tcase->tests, test, struct torture_test *);
851
852         return test;
853 }
854
855