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