1#!/usr/bin/perl
2# Bootstrap Samba and run a number of tests against it.
3# Copyright (C) 2005-2007 Jelmer Vernooij <jelmer@samba.org>
4# Published under the GNU GPL, v3 or later.
5
6package Samba3;
7
8use strict;
9use Cwd qw(abs_path);
10use FindBin qw($RealBin);
11use POSIX;
12
13sub binpath($$)
14{
15	my ($self, $binary) = @_;
16
17	if (defined($self->{bindir})) {
18		my $path = "$self->{bindir}/$binary";
19		-f $path or die("File $path doesn't exist");
20		return $path;
21	}
22
23	return $binary;
24}
25
26sub new($$) {
27	my ($classname, $bindir) = @_;
28	my $self = { bindir => $bindir };
29	bless $self;
30	return $self;
31}
32
33sub teardown_env($$)
34{
35	my ($self, $envvars) = @_;
36
37	my $smbdpid = read_pid($envvars, "smbd");
38	my $nmbdpid = read_pid($envvars, "nmbd");
39	my $winbinddpid = read_pid($envvars, "winbindd");
40
41	$self->stop_sig_term($smbdpid);
42	$self->stop_sig_term($nmbdpid);
43	$self->stop_sig_term($winbinddpid);
44
45	sleep(2);
46
47	$self->stop_sig_kill($smbdpid);
48	$self->stop_sig_kill($nmbdpid);
49	$self->stop_sig_kill($winbinddpid);
50
51	return 0;
52}
53
54sub getlog_env_app($$$)
55{
56	my ($self, $envvars, $name) = @_;
57
58	my $title = "$name LOG of: $envvars->{NETBIOSNAME}\n";
59	my $out = $title;
60
61	open(LOG, "<".$envvars->{$name."_TEST_LOG"});
62
63	seek(LOG, $envvars->{$name."_TEST_LOG_POS"}, SEEK_SET);
64	while (<LOG>) {
65		$out .= $_;
66	}
67	$envvars->{$name."_TEST_LOG_POS"} = tell(LOG);
68	close(LOG);
69
70	return "" if $out eq $title;
71
72	return $out;
73}
74
75sub getlog_env($$)
76{
77	my ($self, $envvars) = @_;
78	my $ret = "";
79
80	$ret .= $self->getlog_env_app($envvars, "SMBD");
81	$ret .= $self->getlog_env_app($envvars, "NMBD");
82	$ret .= $self->getlog_env_app($envvars, "WINBINDD");
83
84	return $ret;
85}
86
87sub check_env($$)
88{
89	my ($self, $envvars) = @_;
90
91	# TODO ...
92	return 1;
93}
94
95sub setup_env($$$)
96{
97	my ($self, $envname, $path) = @_;
98
99	if ($envname eq "dc") {
100		return $self->setup_dc("$path/dc");
101	} elsif ($envname eq "member") {
102		if (not defined($self->{vars}->{dc})) {
103			$self->setup_dc("$path/dc");
104		}
105		return $self->setup_member("$path/member", $self->{vars}->{dc});
106	} else {
107		return undef;
108	}
109}
110
111sub setup_dc($$)
112{
113	my ($self, $path) = @_;
114
115	print "PROVISIONING DC...";
116
117	my $dc_options = "
118	domain master = yes
119	domain logons = yes
120	lanman auth = yes
121";
122
123	my $vars = $self->provision($path,
124				    "LOCALDC2",
125				    2,
126				    "localdc2pass",
127				    $dc_options);
128
129	$self->check_or_start($vars,
130			      ($ENV{SMBD_MAXTIME} or 2700),
131			       "yes", "yes", "yes");
132
133	$self->wait_for_start($vars);
134
135	$self->{vars}->{dc} = $vars;
136
137	return $vars;
138}
139
140sub setup_member($$$)
141{
142	my ($self, $prefix, $dcvars) = @_;
143
144	print "PROVISIONING MEMBER...";
145
146	my $member_options = "
147	security = domain
148	server signing = on
149";
150	my $ret = $self->provision($prefix,
151				   "LOCALMEMBER3",
152				   3,
153				   "localmember3pass",
154				   $member_options);
155
156	$ret or die("Unable to provision");
157
158	my $net = $self->binpath("net");
159	my $cmd = "";
160	$cmd .= "SOCKET_WRAPPER_DEFAULT_IFACE=\"$ret->{SOCKET_WRAPPER_DEFAULT_IFACE}\" ";
161	$cmd .= "$net join $ret->{CONFIGURATION} $dcvars->{DOMAIN} member";
162	$cmd .= " -U$dcvars->{USERNAME}\%$dcvars->{PASSWORD}";
163
164	system($cmd) == 0 or die("Join failed\n$cmd");
165
166	$self->check_or_start($ret,
167			      ($ENV{SMBD_MAXTIME} or 2700),
168			       "yes", "yes", "yes");
169
170	$self->wait_for_start($ret);
171
172	$ret->{DC_SERVER} = $dcvars->{SERVER};
173	$ret->{DC_SERVER_IP} = $dcvars->{SERVER_IP};
174	$ret->{DC_NETBIOSNAME} = $dcvars->{NETBIOSNAME};
175	$ret->{DC_USERNAME} = $dcvars->{USERNAME};
176	$ret->{DC_PASSWORD} = $dcvars->{PASSWORD};
177
178	return $ret;
179}
180
181sub stop($)
182{
183	my ($self) = @_;
184}
185
186sub stop_sig_term($$) {
187	my ($self, $pid) = @_;
188	kill("USR1", $pid) or kill("ALRM", $pid) or warn("Unable to kill $pid: $!");
189}
190
191sub stop_sig_kill($$) {
192	my ($self, $pid) = @_;
193	kill("ALRM", $pid) or warn("Unable to kill $pid: $!");
194}
195
196sub write_pid($$$)
197{
198	my ($env_vars, $app, $pid) = @_;
199
200	open(PID, ">$env_vars->{PIDDIR}/timelimit.$app.pid");
201	print PID $pid;
202	close(PID);
203}
204
205sub read_pid($$)
206{
207	my ($env_vars, $app) = @_;
208
209	open(PID, "<$env_vars->{PIDDIR}/timelimit.$app.pid");
210	my $pid = <PID>;
211	close(PID);
212	return $pid;
213}
214
215sub check_or_start($$$$$) {
216	my ($self, $env_vars, $maxtime, $nmbd, $winbindd, $smbd) = @_;
217
218	unlink($env_vars->{NMBD_TEST_LOG});
219	print "STARTING NMBD...";
220	my $pid = fork();
221	if ($pid == 0) {
222		open STDOUT, ">$env_vars->{NMBD_TEST_LOG}";
223		open STDERR, '>&STDOUT';
224
225		SocketWrapper::set_default_iface($env_vars->{SOCKET_WRAPPER_DEFAULT_IFACE});
226
227		$ENV{WINBINDD_SOCKET_DIR} = $env_vars->{WINBINDD_SOCKET_DIR};
228
229		$ENV{NSS_WRAPPER_PASSWD} = $env_vars->{NSS_WRAPPER_PASSWD};
230		$ENV{NSS_WRAPPER_GROUP} = $env_vars->{NSS_WRAPPER_GROUP};
231		$ENV{NSS_WRAPPER_WINBIND_SO_PATH} = $env_vars->{NSS_WRAPPER_WINBIND_SO_PATH};
232
233		if ($nmbd ne "yes") {
234			$SIG{USR1} = $SIG{ALRM} = $SIG{INT} = $SIG{QUIT} = $SIG{TERM} = sub {
235				my $signame = shift;
236				print("Skip nmbd received signal $signame");
237				exit 0;
238			};
239			sleep($maxtime);
240			exit 0;
241		}
242
243		my @optargs = ("-d0");
244		if (defined($ENV{NMBD_OPTIONS})) {
245			@optargs = split(/ /, $ENV{NMBD_OPTIONS});
246		}
247
248		$ENV{MAKE_TEST_BINARY} = $self->binpath("nmbd");
249
250		my @preargs = ($self->binpath("timelimit"), $maxtime);
251		if(defined($ENV{NMBD_VALGRIND})) {
252			@preargs = split(/ /, $ENV{NMBD_VALGRIND});
253		}
254
255		exec(@preargs, $self->binpath("nmbd"), "-F", "-S", "--no-process-group", "-s", $env_vars->{SERVERCONFFILE}, @optargs) or die("Unable to start nmbd: $!");
256	}
257	write_pid($env_vars, "nmbd", $pid);
258	print "DONE\n";
259
260	unlink($env_vars->{WINBINDD_TEST_LOG});
261	print "STARTING WINBINDD...";
262	$pid = fork();
263	if ($pid == 0) {
264		open STDOUT, ">$env_vars->{WINBINDD_TEST_LOG}";
265		open STDERR, '>&STDOUT';
266
267		SocketWrapper::set_default_iface($env_vars->{SOCKET_WRAPPER_DEFAULT_IFACE});
268
269		$ENV{WINBINDD_SOCKET_DIR} = $env_vars->{WINBINDD_SOCKET_DIR};
270
271		$ENV{NSS_WRAPPER_PASSWD} = $env_vars->{NSS_WRAPPER_PASSWD};
272		$ENV{NSS_WRAPPER_GROUP} = $env_vars->{NSS_WRAPPER_GROUP};
273		$ENV{NSS_WRAPPER_WINBIND_SO_PATH} = $env_vars->{NSS_WRAPPER_WINBIND_SO_PATH};
274
275		if ($winbindd ne "yes") {
276			$SIG{USR1} = $SIG{ALRM} = $SIG{INT} = $SIG{QUIT} = $SIG{TERM} = sub {
277				my $signame = shift;
278				print("Skip winbindd received signal $signame");
279				exit 0;
280			};
281			sleep($maxtime);
282			exit 0;
283		}
284
285		my @optargs = ("-d0");
286		if (defined($ENV{WINBINDD_OPTIONS})) {
287			@optargs = split(/ /, $ENV{WINBINDD_OPTIONS});
288		}
289
290		$ENV{MAKE_TEST_BINARY} = $self->binpath("winbindd");
291
292		my @preargs = ($self->binpath("timelimit"), $maxtime);
293		if(defined($ENV{WINBINDD_VALGRIND})) {
294			@preargs = split(/ /, $ENV{WINBINDD_VALGRIND});
295		}
296
297		exec(@preargs, $self->binpath("winbindd"), "-F", "-S", "--no-process-group", "-s", $env_vars->{SERVERCONFFILE}, @optargs) or die("Unable to start winbindd: $!");
298	}
299	write_pid($env_vars, "winbindd", $pid);
300	print "DONE\n";
301
302	unlink($env_vars->{SMBD_TEST_LOG});
303	print "STARTING SMBD...";
304	$pid = fork();
305	if ($pid == 0) {
306		open STDOUT, ">$env_vars->{SMBD_TEST_LOG}";
307		open STDERR, '>&STDOUT';
308
309		SocketWrapper::set_default_iface($env_vars->{SOCKET_WRAPPER_DEFAULT_IFACE});
310
311		$ENV{WINBINDD_SOCKET_DIR} = $env_vars->{WINBINDD_SOCKET_DIR};
312
313		$ENV{NSS_WRAPPER_PASSWD} = $env_vars->{NSS_WRAPPER_PASSWD};
314		$ENV{NSS_WRAPPER_GROUP} = $env_vars->{NSS_WRAPPER_GROUP};
315		$ENV{NSS_WRAPPER_WINBIND_SO_PATH} = $env_vars->{NSS_WRAPPER_WINBIND_SO_PATH};
316
317		if ($smbd ne "yes") {
318			$SIG{USR1} = $SIG{ALRM} = $SIG{INT} = $SIG{QUIT} = $SIG{TERM} = sub {
319				my $signame = shift;
320				print("Skip smbd received signal $signame");
321				exit 0;
322			};
323			sleep($maxtime);
324			exit 0;
325		}
326
327		$ENV{MAKE_TEST_BINARY} = $self->binpath("smbd");
328		my @optargs = ("-d0");
329		if (defined($ENV{SMBD_OPTIONS})) {
330			@optargs = split(/ /, $ENV{SMBD_OPTIONS});
331		}
332		my @preargs = ($self->binpath("timelimit"), $maxtime);
333		if(defined($ENV{SMBD_VALGRIND})) {
334			@preargs = split(/ /,$ENV{SMBD_VALGRIND});
335		}
336		exec(@preargs, $self->binpath("smbd"), "-F", "-S", "--no-process-group", "-s", $env_vars->{SERVERCONFFILE}, @optargs) or die("Unable to start smbd: $!");
337	}
338	write_pid($env_vars, "smbd", $pid);
339	print "DONE\n";
340
341	return 0;
342}
343
344sub create_clientconf($$$)
345{
346	my ($self, $prefix, $domain) = @_;
347
348	my $lockdir = "$prefix/locks";
349	my $logdir = "$prefix/logs";
350	my $piddir = "$prefix/pid";
351	my $privatedir = "$prefix/private";
352	my $conffile = "$prefix/smb.conf";
353
354	my $torture_interfaces='127.0.0.6/8,127.0.0.7/8,127.0.0.8/8,127.0.0.9/8,127.0.0.10/8,127.0.0.11/8';
355	open(CONF, ">$conffile");
356	print CONF "
357[global]
358	workgroup = $domain
359
360	private dir = $privatedir
361	pid directory = $piddir
362	lock directory = $lockdir
363	log file = $logdir/log.\%m
364	log level = 0
365
366	name resolve order = bcast
367
368	netbios name = TORTURE_6
369	interfaces = $torture_interfaces
370	panic action = $RealBin/gdb_backtrace \%d %\$(MAKE_TEST_BINARY)
371
372	passdb backend = tdbsam
373	";
374	close(CONF);
375}
376
377sub provision($$$$$$)
378{
379	my ($self, $prefix, $server, $swiface, $password, $extra_options) = @_;
380
381	##
382	## setup the various environment variables we need
383	##
384
385	my %ret = ();
386	my $server_ip = "127.0.0.$swiface";
387	my $domain = "SAMBA-TEST";
388
389	my $unix_name = ($ENV{USER} or $ENV{LOGNAME} or `PATH=/usr/ucb:$ENV{PATH} whoami`);
390	chomp $unix_name;
391	my $unix_uid = $>;
392	my $unix_gids_str = $);
393	my @unix_gids = split(" ", $unix_gids_str);
394
395	my $prefix_abs = abs_path($prefix);
396	my $bindir_abs = abs_path($self->{bindir});
397
398	my @dirs = ();
399
400	my $shrdir="$prefix_abs/share";
401	push(@dirs,$shrdir);
402
403	my $libdir="$prefix_abs/lib";
404	push(@dirs,$libdir);
405
406	my $piddir="$prefix_abs/pid";
407	push(@dirs,$piddir);
408
409	my $privatedir="$prefix_abs/private";
410	push(@dirs,$privatedir);
411
412	my $lockdir="$prefix_abs/lockdir";
413	push(@dirs,$lockdir);
414
415	my $logdir="$prefix_abs/logs";
416	push(@dirs,$logdir);
417
418	# this gets autocreated by winbindd
419	my $wbsockdir="$prefix_abs/winbindd";
420	my $wbsockprivdir="$lockdir/winbindd_privileged";
421
422	##
423	## create the test directory layout
424	##
425	die ("prefix_abs = ''") if $prefix_abs eq "";
426	die ("prefix_abs = '/'") if $prefix_abs eq "/";
427
428	mkdir($prefix_abs, 0777);
429	print "CREATE TEST ENVIRONMENT IN '$prefix'...";
430	system("rm -rf $prefix_abs/*");
431	mkdir($_, 0777) foreach(@dirs);
432
433	my $conffile="$libdir/server.conf";
434
435	my $nss_wrapper_pl = "$ENV{PERL} $RealBin/../lib/nss_wrapper/nss_wrapper.pl";
436	my $nss_wrapper_passwd = "$privatedir/passwd";
437	my $nss_wrapper_group = "$privatedir/group";
438
439	open(CONF, ">$conffile") or die("Unable to open $conffile");
440	print CONF "
441[global]
442	netbios name = $server
443	interfaces = $server_ip/8
444	bind interfaces only = yes
445	panic action = $RealBin/gdb_backtrace %d %\$(MAKE_TEST_BINARY)
446
447	workgroup = $domain
448
449	private dir = $privatedir
450	pid directory = $piddir
451	lock directory = $lockdir
452	log file = $logdir/log.\%m
453	log level = 0
454
455	name resolve order = bcast
456
457	state directory = $lockdir
458	cache directory = $lockdir
459
460	passdb backend = tdbsam
461
462	time server = yes
463
464	add user script =		$nss_wrapper_pl --passwd_path $nss_wrapper_passwd --type passwd --action add --name %u
465	add group script =		$nss_wrapper_pl --group_path  $nss_wrapper_group  --type group  --action add --name %g
466	add machine script =		$nss_wrapper_pl --passwd_path $nss_wrapper_passwd --type passwd --action add --name %u
467	add user to group script =	$nss_wrapper_pl --passwd_path $nss_wrapper_passwd --type member --action add --member %u --name %g --group_path $nss_wrapper_group
468	delete user script =		$nss_wrapper_pl --passwd_path $nss_wrapper_passwd --type passwd --action delete --name %u
469	delete group script =		$nss_wrapper_pl --group_path  $nss_wrapper_group  --type group  --action delete --name %g
470	delete user from group script = $nss_wrapper_pl --passwd_path $nss_wrapper_passwd --type member --action delete --member %u --name %g --group_path $nss_wrapper_group
471
472	kernel oplocks = no
473	kernel change notify = no
474
475	syslog = no
476	printing = bsd
477	printcap name = /dev/null
478
479	winbindd:socket dir = $wbsockdir
480	idmap uid = 100000-200000
481	idmap gid = 100000-200000
482
483#	min receivefile size = 4000
484
485	read only = no
486	smbd:sharedelay = 100000
487	smbd:writetimeupdatedelay = 500000
488	map hidden = yes
489	map system = yes
490	create mask = 755
491	vfs objects = $bindir_abs/xattr_tdb.so $bindir_abs/streams_depot.so
492
493	# Begin extra options
494	$extra_options
495	# End extra options
496
497	#Include user defined custom parameters if set
498";
499
500	if (defined($ENV{INCLUDE_CUSTOM_CONF})) {
501		print CONF "\t$ENV{INCLUDE_CUSTOM_CONF}\n";
502	}
503
504	print CONF "
505[tmp]
506	path = $shrdir
507[hideunread]
508	copy = tmp
509	hide unreadable = yes
510[hideunwrite]
511	copy = tmp
512	hide unwriteable files = yes
513[print1]
514	copy = tmp
515	printable = yes
516	printing = vlp
517	print command = $bindir_abs/vlp tdbfile=$lockdir/vlp.tdb print %p %s
518	lpq command = $bindir_abs/vlp tdbfile=$lockdir/vlp.tdb lpq %p
519	lp rm command = $bindir_abs/vlp tdbfile=$lockdir/vlp.tdb lprm %p %j
520	lp pause command = $bindir_abs/vlp tdbfile=$lockdir/vlp.tdb lppause %p %j
521	lp resume command = $bindir_abs/vlp tdbfile=$lockdir/vlp.tdb lpresume %p %j
522	queue pause command = $bindir_abs/vlp tdbfile=$lockdir/vlp.tdb queuepause %p
523	queue resume command = $bindir_abs/vlp tdbfile=$lockdir/vlp.tdb queueresume %p
524
525[print2]
526	copy = print1
527[print3]
528	copy = print1
529[print4]
530	copy = print1
531	";
532	close(CONF);
533
534	##
535	## create a test account
536	##
537
538	open(PASSWD, ">$nss_wrapper_passwd") or die("Unable to open $nss_wrapper_passwd");
539	print PASSWD "nobody:x:65534:65533:nobody gecos:$prefix_abs:/bin/false
540root:x:65533:65532:root gecos:$prefix_abs:/bin/false
541$unix_name:x:$unix_uid:$unix_gids[0]:$unix_name gecos:$prefix_abs:/bin/false
542";
543	close(PASSWD);
544
545	open(GROUP, ">$nss_wrapper_group") or die("Unable to open $nss_wrapper_group");
546	print GROUP "nobody:x:65533:
547nogroup:x:65534:nobody
548root:x:65532:
549$unix_name-group:x:$unix_gids[0]:
550";
551	close(GROUP);
552
553	$ENV{NSS_WRAPPER_PASSWD} = $nss_wrapper_passwd;
554	$ENV{NSS_WRAPPER_GROUP} = $nss_wrapper_group;
555
556	open(PWD, "|".$self->binpath("smbpasswd")." -c $conffile -L -s -a $unix_name >/dev/null");
557	print PWD "$password\n$password\n";
558	close(PWD) or die("Unable to set password for test account");
559
560	delete $ENV{NSS_WRAPPER_PASSWD};
561	delete $ENV{NSS_WRAPPER_GROUP};
562
563	print "DONE\n";
564
565	$ret{SERVER_IP} = $server_ip;
566	$ret{NMBD_TEST_LOG} = "$prefix/nmbd_test.log";
567	$ret{NMBD_TEST_LOG_POS} = 0;
568	$ret{WINBINDD_TEST_LOG} = "$prefix/winbindd_test.log";
569	$ret{WINBINDD_TEST_LOG_POS} = 0;
570	$ret{SMBD_TEST_LOG} = "$prefix/smbd_test.log";
571	$ret{SMBD_TEST_LOG_POS} = 0;
572	$ret{SERVERCONFFILE} = $conffile;
573	$ret{CONFIGURATION} ="-s $conffile";
574	$ret{SERVER} = $server;
575	$ret{USERNAME} = $unix_name;
576	$ret{DOMAIN} = $domain;
577	$ret{NETBIOSNAME} = $server;
578	$ret{PASSWORD} = $password;
579	$ret{PIDDIR} = $piddir;
580	$ret{WINBINDD_SOCKET_DIR} = $wbsockdir;
581	$ret{WINBINDD_PRIV_PIPE_DIR} = $wbsockprivdir;
582	$ret{SOCKET_WRAPPER_DEFAULT_IFACE} = $swiface;
583	$ret{NSS_WRAPPER_PASSWD} = $nss_wrapper_passwd;
584	$ret{NSS_WRAPPER_GROUP} = $nss_wrapper_group;
585	$ret{NSS_WRAPPER_WINBIND_SO_PATH} = $ENV{NSS_WRAPPER_WINBIND_SO_PATH};
586
587	return \%ret;
588}
589
590sub wait_for_start($$)
591{
592	my ($self, $envvars) = @_;
593
594	# give time for nbt server to register its names
595	print "delaying for nbt name registration\n";
596	sleep(10);
597	# This will return quickly when things are up, but be slow if we need to wait for (eg) SSL init
598	system($self->binpath("nmblookup") ." $envvars->{CONFIGURATION} -U $envvars->{SERVER_IP} __SAMBA__");
599	system($self->binpath("nmblookup") ." $envvars->{CONFIGURATION} __SAMBA__");
600	system($self->binpath("nmblookup") ." $envvars->{CONFIGURATION} -U 127.255.255.255 __SAMBA__");
601	system($self->binpath("nmblookup") ." $envvars->{CONFIGURATION} -U $envvars->{SERVER_IP} $envvars->{SERVER}");
602	system($self->binpath("nmblookup") ." $envvars->{CONFIGURATION} $envvars->{SERVER}");
603	# make sure smbd is also up set
604	print "wait for smbd\n";
605	system($self->binpath("smbclient") ." $envvars->{CONFIGURATION} -L $envvars->{SERVER_IP} -U% -p 139 | head -2");
606	system($self->binpath("smbclient") ." $envvars->{CONFIGURATION} -L $envvars->{SERVER_IP} -U% -p 139 | head -2");
607
608	print $self->getlog_env($envvars);
609}
610
6111;
612