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*/
55NTSTATUS 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*/
172int 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   */
255void *shm_setup(int size)
256{
257	int shmid;
258	void *ret;
259
260#ifdef __QNXNTO__
261	shmid = shm_open("private", O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
262	if (shmid == -1) {
263		printf("can't get shared memory\n");
264		exit(1);
265	}
266	shm_unlink("private");
267	if (ftruncate(shmid, size) == -1) {
268		printf("can't set shared memory size\n");
269		exit(1);
270	}
271	ret = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, shmid, 0);
272	if (ret == MAP_FAILED) {
273		printf("can't map shared memory\n");
274		exit(1);
275	}
276#else
277	shmid = shmget(IPC_PRIVATE, size, SHM_R | SHM_W);
278	if (shmid == -1) {
279		printf("can't get shared memory\n");
280		exit(1);
281	}
282	ret = (void *)shmat(shmid, 0, 0);
283	if (!ret || ret == (void *)-1) {
284		printf("can't attach to shared memory\n");
285		return NULL;
286	}
287	/* the following releases the ipc, but note that this process
288	   and all its children will still have access to the memory, its
289	   just that the shmid is no longer valid for other shm calls. This
290	   means we don't leave behind lots of shm segments after we exit
291
292	   See Stevens "advanced programming in unix env" for details
293	   */
294	shmctl(shmid, IPC_RMID, 0);
295#endif
296
297	return ret;
298}
299
300
301/**
302  check that a wire string matches the flags specified
303  not 100% accurate, but close enough for testing
304*/
305bool wire_bad_flags(struct smb_wire_string *str, int flags,
306		    struct smbcli_transport *transport)
307{
308	bool server_unicode;
309	int len;
310	if (!str || !str->s) return true;
311	len = strlen(str->s);
312	if (flags & STR_TERMINATE) len++;
313
314	server_unicode = (transport->negotiate.capabilities&CAP_UNICODE)?true:false;
315	if (getenv("CLI_FORCE_ASCII") || !transport->options.unicode) {
316		server_unicode = false;
317	}
318
319	if ((flags & STR_UNICODE) || server_unicode) {
320		len *= 2;
321	} else if (flags & STR_TERMINATE_ASCII) {
322		len++;
323	}
324	if (str->private_length != len) {
325		printf("Expected wire_length %d but got %d for '%s'\n",
326		       len, str->private_length, str->s);
327		return true;
328	}
329	return false;
330}
331
332/*
333  dump a all_info QFILEINFO structure
334*/
335void dump_all_info(TALLOC_CTX *mem_ctx, union smb_fileinfo *finfo)
336{
337	d_printf("\tcreate_time:    %s\n", nt_time_string(mem_ctx, finfo->all_info.out.create_time));
338	d_printf("\taccess_time:    %s\n", nt_time_string(mem_ctx, finfo->all_info.out.access_time));
339	d_printf("\twrite_time:     %s\n", nt_time_string(mem_ctx, finfo->all_info.out.write_time));
340	d_printf("\tchange_time:    %s\n", nt_time_string(mem_ctx, finfo->all_info.out.change_time));
341	d_printf("\tattrib:         0x%x\n", finfo->all_info.out.attrib);
342	d_printf("\talloc_size:     %llu\n", (long long)finfo->all_info.out.alloc_size);
343	d_printf("\tsize:           %llu\n", (long long)finfo->all_info.out.size);
344	d_printf("\tnlink:          %u\n", finfo->all_info.out.nlink);
345	d_printf("\tdelete_pending: %u\n", finfo->all_info.out.delete_pending);
346	d_printf("\tdirectory:      %u\n", finfo->all_info.out.directory);
347	d_printf("\tea_size:        %u\n", finfo->all_info.out.ea_size);
348	d_printf("\tfname:          '%s'\n", finfo->all_info.out.fname.s);
349}
350
351/*
352  dump file infor by name
353*/
354void torture_all_info(struct smbcli_tree *tree, const char *fname)
355{
356	TALLOC_CTX *mem_ctx = talloc_named(tree, 0, "%s", fname);
357	union smb_fileinfo finfo;
358	NTSTATUS status;
359
360	finfo.generic.level = RAW_FILEINFO_ALL_INFO;
361	finfo.generic.in.file.path = fname;
362	status = smb_raw_pathinfo(tree, mem_ctx, &finfo);
363	if (!NT_STATUS_IS_OK(status)) {
364		d_printf("%s - %s\n", fname, nt_errstr(status));
365		return;
366	}
367
368	d_printf("%s:\n", fname);
369	dump_all_info(mem_ctx, &finfo);
370	talloc_free(mem_ctx);
371}
372
373
374/*
375  set a attribute on a file
376*/
377bool torture_set_file_attribute(struct smbcli_tree *tree, const char *fname, uint16_t attrib)
378{
379	union smb_setfileinfo sfinfo;
380	NTSTATUS status;
381
382	ZERO_STRUCT(sfinfo.basic_info.in);
383	sfinfo.basic_info.level = RAW_SFILEINFO_BASIC_INFORMATION;
384	sfinfo.basic_info.in.file.path = fname;
385	sfinfo.basic_info.in.attrib = attrib;
386	status = smb_raw_setpathinfo(tree, &sfinfo);
387	return NT_STATUS_IS_OK(status);
388}
389
390
391/*
392  set a file descriptor as sparse
393*/
394NTSTATUS torture_set_sparse(struct smbcli_tree *tree, int fnum)
395{
396	union smb_ioctl nt;
397	NTSTATUS status;
398	TALLOC_CTX *mem_ctx;
399
400	mem_ctx = talloc_named_const(tree, 0, "torture_set_sparse");
401	if (!mem_ctx) {
402		return NT_STATUS_NO_MEMORY;
403	}
404
405	nt.ntioctl.level = RAW_IOCTL_NTIOCTL;
406	nt.ntioctl.in.function = FSCTL_SET_SPARSE;
407	nt.ntioctl.in.file.fnum = fnum;
408	nt.ntioctl.in.fsctl = true;
409	nt.ntioctl.in.filter = 0;
410	nt.ntioctl.in.max_data = 0;
411	nt.ntioctl.in.blob = data_blob(NULL, 0);
412
413	status = smb_raw_ioctl(tree, mem_ctx, &nt);
414
415	talloc_free(mem_ctx);
416
417	return status;
418}
419
420/*
421  check that an EA has the right value
422*/
423NTSTATUS torture_check_ea(struct smbcli_state *cli,
424			  const char *fname, const char *eaname, const char *value)
425{
426	union smb_fileinfo info;
427	NTSTATUS status;
428	struct ea_name ea;
429	TALLOC_CTX *mem_ctx = talloc_new(cli);
430
431	info.ea_list.level = RAW_FILEINFO_EA_LIST;
432	info.ea_list.in.file.path = fname;
433	info.ea_list.in.num_names = 1;
434	info.ea_list.in.ea_names = &ea;
435
436	ea.name.s = eaname;
437
438	status = smb_raw_pathinfo(cli->tree, mem_ctx, &info);
439	if (!NT_STATUS_IS_OK(status)) {
440		talloc_free(mem_ctx);
441		return status;
442	}
443
444	if (info.ea_list.out.num_eas != 1) {
445		printf("Expected 1 ea in ea_list\n");
446		talloc_free(mem_ctx);
447		return NT_STATUS_EA_CORRUPT_ERROR;
448	}
449
450	if (strcasecmp_m(eaname, info.ea_list.out.eas[0].name.s) != 0) {
451		printf("Expected ea '%s' not '%s' in ea_list\n",
452		       eaname, info.ea_list.out.eas[0].name.s);
453		talloc_free(mem_ctx);
454		return NT_STATUS_EA_CORRUPT_ERROR;
455	}
456
457	if (value == NULL) {
458		if (info.ea_list.out.eas[0].value.length != 0) {
459			printf("Expected zero length ea for %s\n", eaname);
460			talloc_free(mem_ctx);
461			return NT_STATUS_EA_CORRUPT_ERROR;
462		}
463		talloc_free(mem_ctx);
464		return NT_STATUS_OK;
465	}
466
467	if (strlen(value) == info.ea_list.out.eas[0].value.length &&
468	    memcmp(value, info.ea_list.out.eas[0].value.data,
469		   info.ea_list.out.eas[0].value.length) == 0) {
470		talloc_free(mem_ctx);
471		return NT_STATUS_OK;
472	}
473
474	printf("Expected value '%s' not '%*.*s' for ea %s\n",
475	       value,
476	       (int)info.ea_list.out.eas[0].value.length,
477	       (int)info.ea_list.out.eas[0].value.length,
478	       info.ea_list.out.eas[0].value.data,
479	       eaname);
480
481	talloc_free(mem_ctx);
482
483	return NT_STATUS_EA_CORRUPT_ERROR;
484}
485
486_PUBLIC_ bool torture_open_connection_share(TALLOC_CTX *mem_ctx,
487				   struct smbcli_state **c,
488				   struct torture_context *tctx,
489				   const char *hostname,
490				   const char *sharename,
491				   struct tevent_context *ev)
492{
493	NTSTATUS status;
494
495	struct smbcli_options options;
496	struct smbcli_session_options session_options;
497
498	lp_smbcli_options(tctx->lp_ctx, &options);
499	lp_smbcli_session_options(tctx->lp_ctx, &session_options);
500
501	options.use_oplocks = torture_setting_bool(tctx, "use_oplocks", true);
502	options.use_level2_oplocks = torture_setting_bool(tctx, "use_level2_oplocks", true);
503
504	status = smbcli_full_connection(mem_ctx, c, hostname,
505					lp_smb_ports(tctx->lp_ctx),
506					sharename, NULL,
507					lp_socket_options(tctx->lp_ctx),
508					cmdline_credentials,
509					lp_resolve_context(tctx->lp_ctx),
510					ev, &options, &session_options,
511					lp_iconv_convenience(tctx->lp_ctx),
512					lp_gensec_settings(tctx, tctx->lp_ctx));
513	if (!NT_STATUS_IS_OK(status)) {
514		printf("Failed to open connection - %s\n", nt_errstr(status));
515		return false;
516	}
517
518	return true;
519}
520
521_PUBLIC_ bool torture_get_conn_index(int conn_index,
522				     TALLOC_CTX *mem_ctx,
523				     struct torture_context *tctx,
524				     char **host, char **share)
525{
526	char **unc_list = NULL;
527	int num_unc_names = 0;
528	const char *p;
529
530	(*host) = talloc_strdup(mem_ctx, torture_setting_string(tctx, "host", NULL));
531	(*share) = talloc_strdup(mem_ctx, torture_setting_string(tctx, "share", NULL));
532
533	p = torture_setting_string(tctx, "unclist", NULL);
534	if (!p) {
535		return true;
536	}
537
538	unc_list = file_lines_load(p, &num_unc_names, 0, NULL);
539	if (!unc_list || num_unc_names <= 0) {
540		DEBUG(0,("Failed to load unc names list from '%s'\n", p));
541		return false;
542	}
543
544	if (!smbcli_parse_unc(unc_list[conn_index % num_unc_names],
545			      mem_ctx, host, share)) {
546		DEBUG(0, ("Failed to parse UNC name %s\n",
547			  unc_list[conn_index % num_unc_names]));
548		return false;
549	}
550
551	talloc_free(unc_list);
552	return true;
553}
554
555
556
557_PUBLIC_ bool torture_open_connection_ev(struct smbcli_state **c,
558					 int conn_index,
559					 struct torture_context *tctx,
560					 struct tevent_context *ev)
561{
562	char *host, *share;
563	bool ret;
564
565	if (!torture_get_conn_index(conn_index, ev, tctx, &host, &share)) {
566		return false;
567	}
568
569	ret = torture_open_connection_share(NULL, c, tctx, host, share, ev);
570	talloc_free(host);
571	talloc_free(share);
572
573	return ret;
574}
575
576_PUBLIC_ bool torture_open_connection(struct smbcli_state **c, struct torture_context *tctx, int conn_index)
577{
578	return torture_open_connection_ev(c, conn_index, tctx, tctx->ev);
579}
580
581
582
583_PUBLIC_ bool torture_close_connection(struct smbcli_state *c)
584{
585	bool ret = true;
586	if (!c) return true;
587	if (NT_STATUS_IS_ERR(smbcli_tdis(c))) {
588		printf("tdis failed (%s)\n", smbcli_errstr(c->tree));
589		ret = false;
590	}
591	talloc_free(c);
592	return ret;
593}
594
595
596/* check if the server produced the expected error code */
597_PUBLIC_ bool check_error(const char *location, struct smbcli_state *c,
598		 uint8_t eclass, uint32_t ecode, NTSTATUS nterr)
599{
600	NTSTATUS status;
601
602	status = smbcli_nt_error(c->tree);
603	if (NT_STATUS_IS_DOS(status)) {
604		int classnum, num;
605		classnum = NT_STATUS_DOS_CLASS(status);
606		num = NT_STATUS_DOS_CODE(status);
607                if (eclass != classnum || ecode != num) {
608                        printf("unexpected error code %s\n", nt_errstr(status));
609                        printf(" expected %s or %s (at %s)\n",
610			       nt_errstr(NT_STATUS_DOS(eclass, ecode)),
611                               nt_errstr(nterr), location);
612                        return false;
613                }
614        } else {
615                if (!NT_STATUS_EQUAL(nterr, status)) {
616                        printf("unexpected error code %s\n", nt_errstr(status));
617                        printf(" expected %s (at %s)\n", nt_errstr(nterr), location);
618                        return false;
619                }
620        }
621
622	return true;
623}
624
625static struct smbcli_state *current_cli;
626static int procnum; /* records process count number when forking */
627
628static void sigcont(int sig)
629{
630}
631
632double torture_create_procs(struct torture_context *tctx,
633							bool (*fn)(struct torture_context *, struct smbcli_state *, int), bool *result)
634{
635	int i, status;
636	volatile pid_t *child_status;
637	volatile bool *child_status_out;
638	int synccount;
639	int tries = 8;
640	int torture_nprocs = torture_setting_int(tctx, "nprocs", 4);
641	double start_time_limit = 10 + (torture_nprocs * 1.5);
642	struct timeval tv;
643
644	*result = true;
645
646	synccount = 0;
647
648	signal(SIGCONT, sigcont);
649
650	child_status = (volatile pid_t *)shm_setup(sizeof(pid_t)*torture_nprocs);
651	if (!child_status) {
652		printf("Failed to setup shared memory\n");
653		return -1;
654	}
655
656	child_status_out = (volatile bool *)shm_setup(sizeof(bool)*torture_nprocs);
657	if (!child_status_out) {
658		printf("Failed to setup result status shared memory\n");
659		return -1;
660	}
661
662	for (i = 0; i < torture_nprocs; i++) {
663		child_status[i] = 0;
664		child_status_out[i] = true;
665	}
666
667	tv = timeval_current();
668
669	for (i=0;i<torture_nprocs;i++) {
670		procnum = i;
671		if (fork() == 0) {
672			char *myname;
673
674			pid_t mypid = getpid();
675			srandom(((int)mypid) ^ ((int)time(NULL)));
676
677			if (asprintf(&myname, "CLIENT%d", i) == -1) {
678				printf("asprintf failed\n");
679				return -1;
680			}
681			lp_set_cmdline(tctx->lp_ctx, "netbios name", myname);
682			free(myname);
683
684
685			while (1) {
686				if (torture_open_connection(&current_cli, tctx, i)) {
687					break;
688				}
689				if (tries-- == 0) {
690					printf("pid %d failed to start\n", (int)getpid());
691					_exit(1);
692				}
693				msleep(100);
694			}
695
696			child_status[i] = getpid();
697
698			pause();
699
700			if (child_status[i]) {
701				printf("Child %d failed to start!\n", i);
702				child_status_out[i] = 1;
703				_exit(1);
704			}
705
706			child_status_out[i] = fn(tctx, current_cli, i);
707			_exit(0);
708		}
709	}
710
711	do {
712		synccount = 0;
713		for (i=0;i<torture_nprocs;i++) {
714			if (child_status[i]) synccount++;
715		}
716		if (synccount == torture_nprocs) break;
717		msleep(100);
718	} while (timeval_elapsed(&tv) < start_time_limit);
719
720	if (synccount != torture_nprocs) {
721		printf("FAILED TO START %d CLIENTS (started %d)\n", torture_nprocs, synccount);
722		*result = false;
723		return timeval_elapsed(&tv);
724	}
725
726	printf("Starting %d clients\n", torture_nprocs);
727
728	/* start the client load */
729	tv = timeval_current();
730	for (i=0;i<torture_nprocs;i++) {
731		child_status[i] = 0;
732	}
733
734	printf("%d clients started\n", torture_nprocs);
735
736	kill(0, SIGCONT);
737
738	for (i=0;i<torture_nprocs;i++) {
739		int ret;
740		while ((ret=waitpid(0, &status, 0)) == -1 && errno == EINTR) /* noop */ ;
741		if (ret == -1 || WEXITSTATUS(status) != 0) {
742			*result = false;
743		}
744	}
745
746	printf("\n");
747
748	for (i=0;i<torture_nprocs;i++) {
749		if (!child_status_out[i]) {
750			*result = false;
751		}
752	}
753	return timeval_elapsed(&tv);
754}
755
756static bool wrap_smb_multi_test(struct torture_context *torture,
757								struct torture_tcase *tcase,
758								struct torture_test *test)
759{
760	bool (*fn)(struct torture_context *, struct smbcli_state *, int ) = test->fn;
761	bool result;
762
763	torture_create_procs(torture, fn, &result);
764
765	return result;
766}
767
768_PUBLIC_ struct torture_test *torture_suite_add_smb_multi_test(
769									struct torture_suite *suite,
770									const char *name,
771									bool (*run) (struct torture_context *,
772												 struct smbcli_state *,
773												int i))
774{
775	struct torture_test *test;
776	struct torture_tcase *tcase;
777
778	tcase = torture_suite_add_tcase(suite, name);
779
780	test = talloc(tcase, struct torture_test);
781
782	test->name = talloc_strdup(test, name);
783	test->description = NULL;
784	test->run = wrap_smb_multi_test;
785	test->fn = run;
786	test->dangerous = false;
787
788	DLIST_ADD_END(tcase->tests, test, struct torture_test *);
789
790	return test;
791
792}
793
794static bool wrap_simple_2smb_test(struct torture_context *torture_ctx,
795									struct torture_tcase *tcase,
796									struct torture_test *test)
797{
798	bool (*fn) (struct torture_context *, struct smbcli_state *,
799				struct smbcli_state *);
800	bool ret;
801
802	struct smbcli_state *cli1, *cli2;
803
804	if (!torture_open_connection(&cli1, torture_ctx, 0) ||
805		!torture_open_connection(&cli2, torture_ctx, 1))
806		return false;
807
808	fn = test->fn;
809
810	ret = fn(torture_ctx, cli1, cli2);
811
812	talloc_free(cli1);
813	talloc_free(cli2);
814
815	return ret;
816}
817
818
819
820_PUBLIC_ struct torture_test *torture_suite_add_2smb_test(
821									struct torture_suite *suite,
822									const char *name,
823									bool (*run) (struct torture_context *,
824												struct smbcli_state *,
825												struct smbcli_state *))
826{
827	struct torture_test *test;
828	struct torture_tcase *tcase;
829
830	tcase = torture_suite_add_tcase(suite, name);
831
832	test = talloc(tcase, struct torture_test);
833
834	test->name = talloc_strdup(test, name);
835	test->description = NULL;
836	test->run = wrap_simple_2smb_test;
837	test->fn = run;
838	test->dangerous = false;
839
840	DLIST_ADD_END(tcase->tests, test, struct torture_test *);
841
842	return test;
843
844}
845
846static bool wrap_simple_1smb_test(struct torture_context *torture_ctx,
847									struct torture_tcase *tcase,
848									struct torture_test *test)
849{
850	bool (*fn) (struct torture_context *, struct smbcli_state *);
851	bool ret;
852
853	struct smbcli_state *cli1;
854
855	if (!torture_open_connection(&cli1, torture_ctx, 0))
856		return false;
857
858	fn = test->fn;
859
860	ret = fn(torture_ctx, cli1);
861
862	talloc_free(cli1);
863
864	return ret;
865}
866
867_PUBLIC_ struct torture_test *torture_suite_add_1smb_test(
868				struct torture_suite *suite,
869				const char *name,
870				bool (*run) (struct torture_context *, struct smbcli_state *))
871{
872	struct torture_test *test;
873	struct torture_tcase *tcase;
874
875	tcase = torture_suite_add_tcase(suite, name);
876
877	test = talloc(tcase, struct torture_test);
878
879	test->name = talloc_strdup(test, name);
880	test->description = NULL;
881	test->run = wrap_simple_1smb_test;
882	test->fn = run;
883	test->dangerous = false;
884
885	DLIST_ADD_END(tcase->tests, test, struct torture_test *);
886
887	return test;
888}
889
890
891NTSTATUS torture_second_tcon(TALLOC_CTX *mem_ctx,
892			     struct smbcli_session *session,
893			     const char *sharename,
894			     struct smbcli_tree **res)
895{
896	union smb_tcon tcon;
897	struct smbcli_tree *result;
898	TALLOC_CTX *tmp_ctx;
899	NTSTATUS status;
900
901	if ((tmp_ctx = talloc_new(mem_ctx)) == NULL) {
902		return NT_STATUS_NO_MEMORY;
903	}
904
905	result = smbcli_tree_init(session, tmp_ctx, false);
906	if (result == NULL) {
907		talloc_free(tmp_ctx);
908		return NT_STATUS_NO_MEMORY;
909	}
910
911	tcon.generic.level = RAW_TCON_TCONX;
912	tcon.tconx.in.flags = 0;
913
914	/* Ignore share mode security here */
915	tcon.tconx.in.password = data_blob(NULL, 0);
916	tcon.tconx.in.path = sharename;
917	tcon.tconx.in.device = "?????";
918
919	status = smb_raw_tcon(result, tmp_ctx, &tcon);
920	if (!NT_STATUS_IS_OK(status)) {
921		talloc_free(tmp_ctx);
922		return status;
923	}
924
925	result->tid = tcon.tconx.out.tid;
926	*res = talloc_steal(mem_ctx, result);
927	talloc_free(tmp_ctx);
928	return NT_STATUS_OK;
929}
930