r26249: Remove a couple more uses of global_loadparm.
[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 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, struct smbcli_transport *transport)
287 {
288         bool server_unicode;
289         int len;
290         if (!str || !str->s) return true;
291         len = strlen(str->s);
292         if (flags & STR_TERMINATE) len++;
293
294         server_unicode = (transport->negotiate.capabilities&CAP_UNICODE)?true:false;
295         if (getenv("CLI_FORCE_ASCII") || !lp_unicode(global_loadparm)) {
296                 server_unicode = false;
297         }
298
299         if ((flags & STR_UNICODE) || server_unicode) {
300                 len *= 2;
301         } else if (flags & STR_TERMINATE_ASCII) {
302                 len++;
303         }
304         if (str->private_length != len) {
305                 printf("Expected wire_length %d but got %d for '%s'\n", 
306                        len, str->private_length, str->s);
307                 return true;
308         }
309         return false;
310 }
311
312 /*
313   dump a all_info QFILEINFO structure
314 */
315 void dump_all_info(TALLOC_CTX *mem_ctx, union smb_fileinfo *finfo)
316 {
317         d_printf("\tcreate_time:    %s\n", nt_time_string(mem_ctx, finfo->all_info.out.create_time));
318         d_printf("\taccess_time:    %s\n", nt_time_string(mem_ctx, finfo->all_info.out.access_time));
319         d_printf("\twrite_time:     %s\n", nt_time_string(mem_ctx, finfo->all_info.out.write_time));
320         d_printf("\tchange_time:    %s\n", nt_time_string(mem_ctx, finfo->all_info.out.change_time));
321         d_printf("\tattrib:         0x%x\n", finfo->all_info.out.attrib);
322         d_printf("\talloc_size:     %llu\n", (long long)finfo->all_info.out.alloc_size);
323         d_printf("\tsize:           %llu\n", (long long)finfo->all_info.out.size);
324         d_printf("\tnlink:          %u\n", finfo->all_info.out.nlink);
325         d_printf("\tdelete_pending: %u\n", finfo->all_info.out.delete_pending);
326         d_printf("\tdirectory:      %u\n", finfo->all_info.out.directory);
327         d_printf("\tea_size:        %u\n", finfo->all_info.out.ea_size);
328         d_printf("\tfname:          '%s'\n", finfo->all_info.out.fname.s);
329 }
330
331 /*
332   dump file infor by name
333 */
334 void torture_all_info(struct smbcli_tree *tree, const char *fname)
335 {
336         TALLOC_CTX *mem_ctx = talloc_named(tree, 0, "%s", fname);
337         union smb_fileinfo finfo;
338         NTSTATUS status;
339
340         finfo.generic.level = RAW_FILEINFO_ALL_INFO;
341         finfo.generic.in.file.path = fname;
342         status = smb_raw_pathinfo(tree, mem_ctx, &finfo);
343         if (!NT_STATUS_IS_OK(status)) {
344                 d_printf("%s - %s\n", fname, nt_errstr(status));
345                 return;
346         }
347
348         d_printf("%s:\n", fname);
349         dump_all_info(mem_ctx, &finfo);
350         talloc_free(mem_ctx);
351 }
352
353
354 /*
355   set a attribute on a file
356 */
357 bool torture_set_file_attribute(struct smbcli_tree *tree, const char *fname, uint16_t attrib)
358 {
359         union smb_setfileinfo sfinfo;
360         NTSTATUS status;
361
362         ZERO_STRUCT(sfinfo.basic_info.in);
363         sfinfo.basic_info.level = RAW_SFILEINFO_BASIC_INFORMATION;
364         sfinfo.basic_info.in.file.path = fname;
365         sfinfo.basic_info.in.attrib = attrib;
366         status = smb_raw_setpathinfo(tree, &sfinfo);
367         return NT_STATUS_IS_OK(status);
368 }
369
370
371 /*
372   set a file descriptor as sparse
373 */
374 NTSTATUS torture_set_sparse(struct smbcli_tree *tree, int fnum)
375 {
376         union smb_ioctl nt;
377         NTSTATUS status;
378         TALLOC_CTX *mem_ctx;
379
380         mem_ctx = talloc_named_const(tree, 0, "torture_set_sparse");
381         if (!mem_ctx) {
382                 return NT_STATUS_NO_MEMORY;
383         }
384
385         nt.ntioctl.level = RAW_IOCTL_NTIOCTL;
386         nt.ntioctl.in.function = FSCTL_SET_SPARSE;
387         nt.ntioctl.in.file.fnum = fnum;
388         nt.ntioctl.in.fsctl = true;
389         nt.ntioctl.in.filter = 0;
390         nt.ntioctl.in.max_data = 0;
391         nt.ntioctl.in.blob = data_blob(NULL, 0);
392
393         status = smb_raw_ioctl(tree, mem_ctx, &nt);
394
395         talloc_free(mem_ctx);
396
397         return status;
398 }
399
400 /*
401   check that an EA has the right value 
402 */
403 NTSTATUS torture_check_ea(struct smbcli_state *cli, 
404                           const char *fname, const char *eaname, const char *value)
405 {
406         union smb_fileinfo info;
407         NTSTATUS status;
408         struct ea_name ea;
409         TALLOC_CTX *mem_ctx = talloc_new(cli);
410
411         info.ea_list.level = RAW_FILEINFO_EA_LIST;
412         info.ea_list.in.file.path = fname;
413         info.ea_list.in.num_names = 1;
414         info.ea_list.in.ea_names = &ea;
415
416         ea.name.s = eaname;
417
418         status = smb_raw_pathinfo(cli->tree, mem_ctx, &info);
419         if (!NT_STATUS_IS_OK(status)) {
420                 talloc_free(mem_ctx);
421                 return status;
422         }
423
424         if (info.ea_list.out.num_eas != 1) {
425                 printf("Expected 1 ea in ea_list\n");
426                 talloc_free(mem_ctx);
427                 return NT_STATUS_EA_CORRUPT_ERROR;
428         }
429
430         if (strcasecmp_m(eaname, info.ea_list.out.eas[0].name.s) != 0) {
431                 printf("Expected ea '%s' not '%s' in ea_list\n",
432                        eaname, info.ea_list.out.eas[0].name.s);
433                 talloc_free(mem_ctx);
434                 return NT_STATUS_EA_CORRUPT_ERROR;
435         }
436
437         if (value == NULL) {
438                 if (info.ea_list.out.eas[0].value.length != 0) {
439                         printf("Expected zero length ea for %s\n", eaname);
440                         talloc_free(mem_ctx);
441                         return NT_STATUS_EA_CORRUPT_ERROR;
442                 }
443                 talloc_free(mem_ctx);
444                 return NT_STATUS_OK;
445         }
446
447         if (strlen(value) == info.ea_list.out.eas[0].value.length &&
448             memcmp(value, info.ea_list.out.eas[0].value.data,
449                    info.ea_list.out.eas[0].value.length) == 0) {
450                 talloc_free(mem_ctx);
451                 return NT_STATUS_OK;
452         }
453
454         printf("Expected value '%s' not '%*.*s' for ea %s\n",
455                value, 
456                (int)info.ea_list.out.eas[0].value.length,
457                (int)info.ea_list.out.eas[0].value.length,
458                info.ea_list.out.eas[0].value.data,
459                eaname);
460
461         talloc_free(mem_ctx);
462
463         return NT_STATUS_EA_CORRUPT_ERROR;
464 }
465
466 _PUBLIC_ bool torture_open_connection_share(TALLOC_CTX *mem_ctx,
467                                    struct smbcli_state **c, 
468                                    struct torture_context *tctx,
469                                    const char *hostname, 
470                                    const char *sharename,
471                                    struct event_context *ev)
472 {
473         NTSTATUS status;
474
475         status = smbcli_full_connection(mem_ctx, c, hostname, 
476                                         sharename, NULL,
477                                         cmdline_credentials, ev);
478         if (!NT_STATUS_IS_OK(status)) {
479                 printf("Failed to open connection - %s\n", nt_errstr(status));
480                 return false;
481         }
482
483         (*c)->transport->options.use_oplocks = torture_setting_bool(tctx, "use_oplocks", false);
484         (*c)->transport->options.use_level2_oplocks = torture_setting_bool(tctx, "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                                      struct torture_context *tctx,
492                                      char **host, char **share)
493 {
494         char **unc_list = NULL;
495         int num_unc_names = 0;
496         const char *p;
497
498         (*host) = talloc_strdup(mem_ctx, torture_setting_string(tctx, "host", NULL));
499         (*share) = talloc_strdup(mem_ctx, torture_setting_string(tctx, "share", NULL));
500         
501         p = torture_setting_string(tctx, "unclist", NULL);
502         if (!p) {
503                 return true;
504         }
505
506         unc_list = file_lines_load(p, &num_unc_names, NULL);
507         if (!unc_list || num_unc_names <= 0) {
508                 DEBUG(0,("Failed to load unc names list from '%s'\n", p));
509                 return false;
510         }
511
512         if (!smbcli_parse_unc(unc_list[conn_index % num_unc_names],
513                               mem_ctx, host, share)) {
514                 DEBUG(0, ("Failed to parse UNC name %s\n",
515                           unc_list[conn_index % num_unc_names]));
516                 return false;
517         }
518
519         talloc_free(unc_list);
520         return true;
521 }
522
523
524
525 _PUBLIC_ bool torture_open_connection_ev(struct smbcli_state **c,
526                                          int conn_index,
527                                          struct torture_context *tctx,
528                                          struct event_context *ev)
529 {
530         char *host, *share;
531         bool ret;
532
533         if (!torture_get_conn_index(conn_index, ev, tctx, &host, &share)) {
534                 return false;
535         }
536
537         ret = torture_open_connection_share(NULL, c, tctx, host, share, ev);
538         talloc_free(host);
539         talloc_free(share);
540
541         return ret;
542 }
543
544 _PUBLIC_ bool torture_open_connection(struct smbcli_state **c, struct torture_context *tctx, int conn_index)
545 {
546         return torture_open_connection_ev(c, conn_index, tctx,
547                                           cli_credentials_get_event_context(cmdline_credentials));
548 }
549
550
551
552 _PUBLIC_ bool torture_close_connection(struct smbcli_state *c)
553 {
554         bool ret = true;
555         if (!c) return true;
556         if (NT_STATUS_IS_ERR(smbcli_tdis(c))) {
557                 printf("tdis failed (%s)\n", smbcli_errstr(c->tree));
558                 ret = false;
559         }
560         talloc_free(c);
561         return ret;
562 }
563
564
565 /* check if the server produced the expected error code */
566 _PUBLIC_ bool check_error(const char *location, struct smbcli_state *c, 
567                  uint8_t eclass, uint32_t ecode, NTSTATUS nterr)
568 {
569         NTSTATUS status;
570         
571         status = smbcli_nt_error(c->tree);
572         if (NT_STATUS_IS_DOS(status)) {
573                 int class, num;
574                 class = NT_STATUS_DOS_CLASS(status);
575                 num = NT_STATUS_DOS_CODE(status);
576                 if (eclass != class || ecode != num) {
577                         printf("unexpected error code %s\n", nt_errstr(status));
578                         printf(" expected %s or %s (at %s)\n", 
579                                nt_errstr(NT_STATUS_DOS(eclass, ecode)), 
580                                nt_errstr(nterr), location);
581                         return false;
582                 }
583         } else {
584                 if (!NT_STATUS_EQUAL(nterr, status)) {
585                         printf("unexpected error code %s\n", nt_errstr(status));
586                         printf(" expected %s (at %s)\n", nt_errstr(nterr), location);
587                         return false;
588                 }
589         }
590
591         return true;
592 }
593
594 static struct smbcli_state *current_cli;
595 static int procnum; /* records process count number when forking */
596
597 static void sigcont(int sig)
598 {
599 }
600
601 double torture_create_procs(struct torture_context *tctx, 
602                                                         bool (*fn)(struct torture_context *, struct smbcli_state *, int), bool *result)
603 {
604         int i, status;
605         volatile pid_t *child_status;
606         volatile bool *child_status_out;
607         int synccount;
608         int tries = 8;
609         int torture_nprocs = torture_setting_int(tctx, "nprocs", 4);
610         double start_time_limit = 10 + (torture_nprocs * 1.5);
611         struct timeval tv;
612
613         *result = true;
614
615         synccount = 0;
616
617         signal(SIGCONT, sigcont);
618
619         child_status = (volatile pid_t *)shm_setup(sizeof(pid_t)*torture_nprocs);
620         if (!child_status) {
621                 printf("Failed to setup shared memory\n");
622                 return -1;
623         }
624
625         child_status_out = (volatile bool *)shm_setup(sizeof(bool)*torture_nprocs);
626         if (!child_status_out) {
627                 printf("Failed to setup result status shared memory\n");
628                 return -1;
629         }
630
631         for (i = 0; i < torture_nprocs; i++) {
632                 child_status[i] = 0;
633                 child_status_out[i] = true;
634         }
635
636         tv = timeval_current();
637
638         for (i=0;i<torture_nprocs;i++) {
639                 procnum = i;
640                 if (fork() == 0) {
641                         char *myname;
642
643                         pid_t mypid = getpid();
644                         srandom(((int)mypid) ^ ((int)time(NULL)));
645
646                         asprintf(&myname, "CLIENT%d", i);
647                         lp_set_cmdline(tctx->lp_ctx, "netbios name", myname);
648                         free(myname);
649
650
651                         while (1) {
652                                 if (torture_open_connection(&current_cli, tctx, i)) {
653                                         break;
654                                 }
655                                 if (tries-- == 0) {
656                                         printf("pid %d failed to start\n", (int)getpid());
657                                         _exit(1);
658                                 }
659                                 msleep(100);    
660                         }
661
662                         child_status[i] = getpid();
663
664                         pause();
665
666                         if (child_status[i]) {
667                                 printf("Child %d failed to start!\n", i);
668                                 child_status_out[i] = 1;
669                                 _exit(1);
670                         }
671
672                         child_status_out[i] = fn(tctx, current_cli, i);
673                         _exit(0);
674                 }
675         }
676
677         do {
678                 synccount = 0;
679                 for (i=0;i<torture_nprocs;i++) {
680                         if (child_status[i]) synccount++;
681                 }
682                 if (synccount == torture_nprocs) break;
683                 msleep(100);
684         } while (timeval_elapsed(&tv) < start_time_limit);
685
686         if (synccount != torture_nprocs) {
687                 printf("FAILED TO START %d CLIENTS (started %d)\n", torture_nprocs, synccount);
688                 *result = false;
689                 return timeval_elapsed(&tv);
690         }
691
692         printf("Starting %d clients\n", torture_nprocs);
693
694         /* start the client load */
695         tv = timeval_current();
696         for (i=0;i<torture_nprocs;i++) {
697                 child_status[i] = 0;
698         }
699
700         printf("%d clients started\n", torture_nprocs);
701
702         kill(0, SIGCONT);
703
704         for (i=0;i<torture_nprocs;i++) {
705                 int ret;
706                 while ((ret=waitpid(0, &status, 0)) == -1 && errno == EINTR) /* noop */ ;
707                 if (ret == -1 || WEXITSTATUS(status) != 0) {
708                         *result = false;
709                 }
710         }
711
712         printf("\n");
713         
714         for (i=0;i<torture_nprocs;i++) {
715                 if (!child_status_out[i]) {
716                         *result = false;
717                 }
718         }
719         return timeval_elapsed(&tv);
720 }
721
722 static bool wrap_smb_multi_test(struct torture_context *torture,
723                                                                 struct torture_tcase *tcase,
724                                                                 struct torture_test *test)
725 {
726         bool (*fn)(struct torture_context *, struct smbcli_state *, int ) = test->fn;
727         bool result;
728
729         torture_create_procs(torture, fn, &result);
730
731         return result;
732 }
733
734 _PUBLIC_ struct torture_test *torture_suite_add_smb_multi_test(
735                                                                         struct torture_suite *suite,
736                                                                         const char *name,
737                                                                         bool (*run) (struct torture_context *,
738                                                                                                  struct smbcli_state *,
739                                                                                                 int i))
740 {
741         struct torture_test *test; 
742         struct torture_tcase *tcase;
743         
744         tcase = torture_suite_add_tcase(suite, name);
745
746         test = talloc(tcase, struct torture_test);
747
748         test->name = talloc_strdup(test, name);
749         test->description = NULL;
750         test->run = wrap_smb_multi_test;
751         test->fn = run;
752         test->dangerous = false;
753
754         DLIST_ADD_END(tcase->tests, test, struct torture_test *);
755
756         return test;
757
758 }
759
760 static bool wrap_simple_2smb_test(struct torture_context *torture_ctx,
761                                                                         struct torture_tcase *tcase,
762                                                                         struct torture_test *test)
763 {
764         bool (*fn) (struct torture_context *, struct smbcli_state *,
765                                 struct smbcli_state *);
766         bool ret;
767
768         struct smbcli_state *cli1, *cli2;
769
770         if (!torture_open_connection(&cli1, torture_ctx, 0) || 
771                 !torture_open_connection(&cli2, torture_ctx, 1))
772                 return false;
773
774         fn = test->fn;
775
776         ret = fn(torture_ctx, cli1, cli2);
777
778         talloc_free(cli1);
779         talloc_free(cli2);
780
781         return ret;
782 }
783
784
785
786 _PUBLIC_ struct torture_test *torture_suite_add_2smb_test(
787                                                                         struct torture_suite *suite,
788                                                                         const char *name,
789                                                                         bool (*run) (struct torture_context *,
790                                                                                                 struct smbcli_state *,
791                                                                                                 struct smbcli_state *))
792 {
793         struct torture_test *test; 
794         struct torture_tcase *tcase;
795         
796         tcase = torture_suite_add_tcase(suite, name);
797
798         test = talloc(tcase, struct torture_test);
799
800         test->name = talloc_strdup(test, name);
801         test->description = NULL;
802         test->run = wrap_simple_2smb_test;
803         test->fn = run;
804         test->dangerous = false;
805
806         DLIST_ADD_END(tcase->tests, test, struct torture_test *);
807
808         return test;
809
810 }
811
812 static bool wrap_simple_1smb_test(struct torture_context *torture_ctx,
813                                                                         struct torture_tcase *tcase,
814                                                                         struct torture_test *test)
815 {
816         bool (*fn) (struct torture_context *, struct smbcli_state *);
817         bool ret;
818
819         struct smbcli_state *cli1;
820
821         if (!torture_open_connection(&cli1, torture_ctx, 0))
822                 return false;
823
824         fn = test->fn;
825
826         ret = fn(torture_ctx, cli1);
827
828         talloc_free(cli1);
829
830         return ret;
831 }
832
833 _PUBLIC_ struct torture_test *torture_suite_add_1smb_test(
834                                 struct torture_suite *suite,
835                                 const char *name,
836                                 bool (*run) (struct torture_context *, struct smbcli_state *))
837 {
838         struct torture_test *test; 
839         struct torture_tcase *tcase;
840         
841         tcase = torture_suite_add_tcase(suite, name);
842
843         test = talloc(tcase, struct torture_test);
844
845         test->name = talloc_strdup(test, name);
846         test->description = NULL;
847         test->run = wrap_simple_1smb_test;
848         test->fn = run;
849         test->dangerous = false;
850
851         DLIST_ADD_END(tcase->tests, test, struct torture_test *);
852
853         return test;
854 }
855
856