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