diff options
Diffstat (limited to 'source4/selftest')
38 files changed, 6115 insertions, 0 deletions
diff --git a/source4/selftest/README b/source4/selftest/README new file mode 100644 index 0000000000..f8be20a569 --- /dev/null +++ b/source4/selftest/README @@ -0,0 +1,119 @@ +# vim: ft=rst + +This directory contains test scripts that are useful for running a +bunch of tests all at once. + +Available testsuites +==================== +The available testsuites are obtained from a script, usually +selftest/samba{3,4}_tests.sh. This script should for each testsuite output +the name of the test, the command to run and the environment that should be +provided. Use the included "plantest" function to generate the required output. + +Testsuite behaviour +================================ + +Exit code +------------ +The testsuites should exit with a non-zero exit code if at least one +test failed. Skipped tests should not influence the exit code. + +Output format +------------- +Testsuites can simply use the exit code to indicate whether all of their +tests have succeeded or one or more have failed. It is also possible to +provide more granular information using the Subunit protocol. + +This protocol works by writing simple messages to standard output. Any +messages that can not be interpreted by this protocol are considered comments +for the last announced test. + +Accepted commands are: + +test +~~~~~~~~~~~~ +test: <NAME> + +Announce that a new test with the specified name is starting + +success +~~~~~~~~~~~~~~~ +success: <NAME> + +Announce that the test with the specified name is done and ran successfully. + +failure +~~~~~~~~~~~~~~~ +failure: <NAME> +failure: <NAME> [ REASON ] + +Announce that the test with the specified name failed. Optionally, it is +possible to specify a reason it failed. + +skip +~~~~~~~~~~~~ +skip: <NAME> +skip: <NAME> [ REASON ] + +Announce that the test with the specified name was skipped. Optionally a +reason can be specified. + +knownfail +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +knownfail: <NAME> +knownfail: <NAME> [ REASON ] + +Announce that the test with the specified name was run and failed as expected. +Alternatively it is also possible to simply return "failure:" here but +specify in the samba4-knownfailures file that it is failing. + +Environments +============ +Tests often need to run against a server with particular things set up, +a "environment". This environment is provided by the test "target": Samba 3, +Samba 4 or Windows. + +The following environments are currently available: + + - none: No server set up, no variables set. + - dc: Domain controller set up. The following environment variables will + be set: + + * USERNAME: Administrator user name + * PASSWORD: Administrator password + * DOMAIN: Domain name + * REALM: Realm name + * SERVER: DC host name + * SERVER_IP: DC IPv4 address + * NETBIOSNAME: DC NetBIOS name + * NETIOSALIAS: DC NetBIOS alias + + - member: Domain controller and member server that is joined to it set up. The + following environment variables will be set: + + * USERNAME: Domain administrator user name + * PASSWORD: Domain administrator password + * DOMAIN: Domain name + * REALM: Realm name + * SERVER: Name of the member server + + +Running tests +============= + +To run all the tests use:: + + make test + +To run a quick subset (aiming for about 1 minute of testing) run:: + + make quicktest + +To run a specific test, use this syntax:: + + make test TESTS=testname + +for example:: + + make test TESTS=samba4.BASE-DELETE + diff --git a/source4/selftest/SocketWrapper.pm b/source4/selftest/SocketWrapper.pm new file mode 100644 index 0000000000..e63605b8df --- /dev/null +++ b/source4/selftest/SocketWrapper.pm @@ -0,0 +1,66 @@ +#!/usr/bin/perl +# Bootstrap Samba and run a number of tests against it. +# Copyright (C) 2005-2007 Jelmer Vernooij <jelmer@samba.org> +# Published under the GNU GPL, v3 or later. + +package SocketWrapper; + +use Exporter; +@ISA = qw(Exporter); +@EXPORT_OK = qw(setup_dir setup_pcap set_default_iface); + +use strict; +use FindBin qw($RealBin); + +sub setup_dir($$) +{ + my ($dir, $pcap) = @_; + my $pcap_dir = undef; + + if (defined($dir)) { + if ( -d $dir ) { + unlink <$dir/*>; + } else { + mkdir($dir, 0777); + } + + if ($pcap) { + $pcap_dir = $dir."/pcap"; + + if ( -d $pcap_dir ) { + unlink <$pcap_dir/*>; + } else { + mkdir($pcap_dir, 0777); + } + } + } + + if (defined($pcap_dir)) { + $ENV{SOCKET_WRAPPER_PCAP_DIR} = $pcap_dir; + } else { + delete $ENV{SOCKET_WRAPPER_PCAP_DIR}; + } + + if (defined($dir)) { + $ENV{SOCKET_WRAPPER_DIR} = $dir; + } else { + delete $ENV{SOCKET_WRAPPER_DIR}; + } + + return $dir; +} + +sub setup_pcap($) +{ + my ($pcap_file) = @_; + + $ENV{SOCKET_WRAPPER_PCAP_FILE} = $pcap_file; +} + +sub set_default_iface($) +{ + my ($i) = @_; + $ENV{SOCKET_WRAPPER_DEFAULT_IFACE} = $i; +} + +1; diff --git a/source4/selftest/Subunit.pm b/source4/selftest/Subunit.pm new file mode 100644 index 0000000000..05e51da541 --- /dev/null +++ b/source4/selftest/Subunit.pm @@ -0,0 +1,97 @@ +package Subunit; + +require Exporter; +@ISA = qw(Exporter); +@EXPORT_OK = qw(parse_results); + +use strict; + +sub parse_results($$$$$) +{ + my ($msg_ops, $statistics, $fh, $expecting_failure, $open_tests) = @_; + my $unexpected_ok = 0; + my $expected_fail = 0; + my $unexpected_fail = 0; + my $unexpected_err = 0; + my $orig_open_len = $#$open_tests; + + while(<$fh>) { + if (/^test: (.+)\n/) { + $msg_ops->control_msg($_); + $msg_ops->start_test($open_tests, $1); + push (@$open_tests, $1); + } elsif (/^(success|successful|failure|skip|knownfail|error): (.*?)( \[)?([ \t]*)\n/) { + $msg_ops->control_msg($_); + my $reason = undef; + if ($3) { + $reason = ""; + # reason may be specified in next lines + my $terminated = 0; + while(<$fh>) { + $msg_ops->control_msg($_); + if ($_ eq "]\n") { $terminated = 1; last; } else { $reason .= $_; } + } + + unless ($terminated) { + $statistics->{TESTS_ERROR}++; + $msg_ops->end_test($open_tests, $2, $1, 1, "reason interrupted"); + return 1; + } + } + my $result = $1; + if ($1 eq "success" or $1 eq "successful") { + pop(@$open_tests); #FIXME: Check that popped value == $2 + if ($expecting_failure->(join(".", @$open_tests) . ".$2")) { + $statistics->{TESTS_UNEXPECTED_OK}++; + $msg_ops->end_test($open_tests, $2, $1, 1, $reason); + $unexpected_ok++; + } else { + $statistics->{TESTS_EXPECTED_OK}++; + $msg_ops->end_test($open_tests, $2, $1, 0, $reason); + } + } elsif ($1 eq "failure") { + pop(@$open_tests); #FIXME: Check that popped value == $2 + if ($expecting_failure->(join(".", @$open_tests) . ".$2")) { + $statistics->{TESTS_EXPECTED_FAIL}++; + $msg_ops->end_test($open_tests, $2, $1, 0, $reason); + $expected_fail++; + } else { + $statistics->{TESTS_UNEXPECTED_FAIL}++; + $msg_ops->end_test($open_tests, $2, $1, 1, $reason); + $unexpected_fail++; + } + } elsif ($1 eq "knownfail") { + pop(@$open_tests); #FIXME: Check that popped value == $2 + $statistics->{TESTS_EXPECTED_FAIL}++; + $msg_ops->end_test($open_tests, $2, $1, 0, $reason); + } elsif ($1 eq "skip") { + $statistics->{TESTS_SKIP}++; + pop(@$open_tests); #FIXME: Check that popped value == $2 + $msg_ops->end_test($open_tests, $2, $1, 0, $reason); + } elsif ($1 eq "error") { + $statistics->{TESTS_ERROR}++; + pop(@$open_tests); #FIXME: Check that popped value == $2 + $msg_ops->end_test($open_tests, $2, $1, 1, $reason); + $unexpected_err++; + } + } else { + $msg_ops->output_msg($_); + } + } + + while ($#$open_tests > $orig_open_len) { + $msg_ops->end_test($open_tests, pop(@$open_tests), "error", 1, + "was started but never finished!"); + $statistics->{TESTS_ERROR}++; + $unexpected_err++; + } + + return 1 if $unexpected_err > 0; + return 1 if $unexpected_fail > 0; + return 1 if $unexpected_ok > 0 and $expected_fail > 0; + return 0 if $unexpected_ok > 0 and $expected_fail == 0; + return 0 if $expected_fail > 0; + return 1; +} + +1; diff --git a/source4/selftest/TODO b/source4/selftest/TODO new file mode 100644 index 0000000000..67776ffc76 --- /dev/null +++ b/source4/selftest/TODO @@ -0,0 +1,2 @@ +- warn about unexpected successes +- better way to detect that smbd has finished initialization diff --git a/source4/selftest/config.mk b/source4/selftest/config.mk new file mode 100644 index 0000000000..5503a42cdd --- /dev/null +++ b/source4/selftest/config.mk @@ -0,0 +1,81 @@ +TEST_FORMAT = plain + +SELFTEST = $(LD_LIBPATH_OVERRIDE) $(PERL) $(selftestdir)/selftest.pl --prefix=${selftest_prefix} \ + --builddir=$(builddir) --srcdir=$(srcdir) \ + --expected-failures=$(srcdir)/samba4-knownfail \ + --format=$(TEST_FORMAT) \ + --exclude=$(srcdir)/samba4-skip --testlist="./selftest/samba4_tests.sh|" \ + $(TEST_OPTIONS) + +SELFTEST_NOSLOW_OPTS = --exclude=$(srcdir)/samba4-slow +SELFTEST_QUICK_OPTS = $(SELFTEST_NOSLOW_OPTS) --quick + +slowtest:: everything + $(SELFTEST) $(DEFAULT_TEST_OPTIONS) --immediate $(TESTS) + +test:: everything + $(SELFTEST) $(SELFTEST_NOSLOW_OPTS) $(DEFAULT_TEST_OPTIONS) --immediate \ + $(TESTS) + +kvmtest:: everything + $(SELFTEST) $(SELFTEST_NOSLOW_OPTS) $(DEFAULT_TEST_OPTIONS) --immediate \ + --target=kvm --image=$(KVM_IMAGE) + +kvmquicktest:: everything + $(SELFTEST) $(DEFAULT_TEST_OPTIONS) --immediate \ + $(SELFTEST_QUICK_OPTS) --target=kvm --image=$(KVM_IMAGE) + +testone:: everything + $(SELFTEST) $(SELFTEST_NOSLOW_OPTS) $(DEFAULT_TEST_OPTIONS) --one $(TESTS) + +test-swrap:: everything + $(SELFTEST) $(SELFTEST_NOSLOW_OPTS) --socket-wrapper --immediate $(TESTS) + +test-swrap-pcap:: everything + $(SELFTEST) $(SELFTEST_NOSLOW_OPTS) --socket-wrapper-pcap --immediate $(TESTS) + +test-swrap-keep-pcap:: everything + $(SELFTEST) $(SELFTEST_NOSLOW_OPTS) --socket-wrapper-keep-pcap --immediate $(TESTS) + +test-noswrap:: everything + $(SELFTEST) $(SELFTEST_NOSLOW_OPTS) --immediate $(TESTS) + +quicktest:: all + $(SELFTEST) $(SELFTEST_QUICK_OPTS) --socket-wrapper --immediate $(TESTS) + +quicktestone:: all + $(SELFTEST) $(SELFTEST_QUICK_OPTS) --socket-wrapper --one $(TESTS) + +testenv:: everything + $(SELFTEST) $(SELFTEST_NOSLOW_OPTS) --socket-wrapper --testenv + +valgrindtest:: valgrindtest-all + +valgrindtest-quick:: all + SMBD_VALGRIND="xterm -n smbd -e $(srcdir)/script/valgrind_run $(LD_LIBPATH_OVERRIDE)" \ + VALGRIND="valgrind -q --num-callers=30 --log-file=${selftest_prefix}/valgrind.log" \ + $(SELFTEST) $(SELFTEST_QUICK_OPTS) --immediate --socket-wrapper $(TESTS) + +valgrindtest-all:: everything + SMBD_VALGRIND="xterm -n smbd -e $(srcdir)/script/valgrind_run $(LD_LIBPATH_OVERRIDE)" \ + VALGRIND="valgrind -q --num-callers=30 --log-file=${selftest_prefix}/valgrind.log" \ + $(SELFTEST) $(SELFTEST_NOSLOW_OPTS) --immediate --socket-wrapper $(TESTS) + +valgrindtest-env:: everything + SMBD_VALGRIND="xterm -n smbd -e $(srcdir)/script/valgrind_run $(LD_LIBPATH_OVERRIDE)" \ + VALGRIND="valgrind -q --num-callers=30 --log-file=${selftest_prefix}/valgrind.log" \ + $(SELFTEST) $(SELFTEST_NOSLOW_OPTS) --socket-wrapper --testenv + +gdbtest:: gdbtest-all + +gdbtest-quick:: all + SMBD_VALGRIND="xterm -n smbd -e $(srcdir)/script/gdb_run $(LD_LIBPATH_OVERRIDE)" \ + $(SELFTEST) $(SELFTEST_QUICK_OPTS) --immediate --socket-wrapper $(TESTS) + +gdbtest-all:: everything + SMBD_VALGRIND="xterm -n smbd -e $(srcdir)/script/gdb_run $(LD_LIBPATH_OVERRIDE)" \ + $(SELFTEST) $(SELFTEST_NOSLOW_OPTS) --immediate --socket-wrapper $(TESTS) + +gdbtest-env:: everything + SMBD_VALGRIND="xterm -n smbd -e $(srcdir)/script/gdb_run $(LD_LIBPATH_OVERRIDE)" \ + $(SELFTEST) $(SELFTEST_NOSLOW_OPTS) --socket-wrapper --testenv diff --git a/source4/selftest/output/buildfarm.pm b/source4/selftest/output/buildfarm.pm new file mode 100644 index 0000000000..cee6c1e63a --- /dev/null +++ b/source4/selftest/output/buildfarm.pm @@ -0,0 +1,120 @@ +#!/usr/bin/perl + +package output::buildfarm; + +use Exporter; +@ISA = qw(Exporter); + +use FindBin qw($RealBin); +use lib "$RealBin/.."; + +use Subunit qw(parse_results); + +use strict; + +sub new($$$) { + my ($class) = @_; + my $self = { + test_output => {}, + start_time => time() + }; + bless($self, $class); +} + +sub start_testsuite($$) +{ + my ($self, $name) = @_; + my $out = ""; + + $self->{NAME} = $name; + $self->{START_TIME} = time(); + + my $duration = $self->{START_TIME} - $self->{start_time}; + $out .= "--==--==--==--==--==--==--==--==--==--==--\n"; + $out .= "Running test $name (level 0 stdout)\n"; + $out .= "--==--==--==--==--==--==--==--==--==--==--\n"; + $out .= scalar(localtime())."\n"; + $out .= "SELFTEST RUNTIME: " . $duration . "s\n"; + $out .= "NAME: $name\n"; + + $self->{test_output}->{$name} = ""; + + print $out; +} + +sub output_msg($$) +{ + my ($self, $output) = @_; + + $self->{test_output}->{$self->{NAME}} .= $output; +} + +sub control_msg($$) +{ + my ($self, $output) = @_; + + $self->{test_output}->{$self->{NAME}} .= $output; +} + +sub end_testsuite($$$$$$) +{ + my ($self, $name, $result, $unexpected, $reason) = @_; + my $out = ""; + + $out .= "TEST RUNTIME: " . (time() - $self->{START_TIME}) . "s\n"; + + if (not $unexpected) { + $out .= "ALL OK\n"; + } else { + $out .= "ERROR: $reason\n"; + $out .= $self->{test_output}->{$name}; + } + + $out .= "==========================================\n"; + if (not $unexpected) { + $out .= "TEST PASSED: $name\n"; + } else { + $out .= "TEST FAILED: $name (status $reason)\n"; + } + $out .= "==========================================\n"; + + print $out; +} + +sub start_test($$$) +{ + my ($self, $parents, $testname) = @_; + + if ($#$parents == -1) { + $self->start_testsuite($testname); + } +} + +sub end_test($$$$$) +{ + my ($self, $parents, $testname, $result, $unexpected, $reason) = @_; + + if ($unexpected) { + $self->{test_output}->{$self->{NAME}} .= "UNEXPECTED($result): $testname\n"; + } + + if ($#$parents == -1) { + $self->end_testsuite($testname, $result, $unexpected, $reason); + } +} + +sub summary($) +{ + my ($self) = @_; + + print "DURATION: " . (time() - $self->{start_time}) . " seconds\n"; +} + +sub skip_testsuite($$$$) +{ + my ($self, $name, $reason) = @_; + + print "SKIPPED: $name\n"; +} + +1; diff --git a/source4/selftest/output/html.pm b/source4/selftest/output/html.pm new file mode 100644 index 0000000000..1049527129 --- /dev/null +++ b/source4/selftest/output/html.pm @@ -0,0 +1,354 @@ +#!/usr/bin/perl + +package output::html; +use Exporter; +@ISA = qw(Exporter); + +use strict; +use warnings; + +use FindBin qw($RealBin); +use lib "$RealBin/.."; + +use Subunit qw(parse_results); + +sub new($$$) { + my ($class, $dirname, $statistics) = @_; + my $self = { + dirname => $dirname, + active_test => undef, + local_statistics => {}, + statistics => $statistics, + msg => "", + error_summary => { + skip => [], + expected_success => [], + unexpected_success => [], + expected_failure => [], + unexpected_failure => [], + skip_testsuites => [], + error => [] + } + }; + + link("$RealBin/output/testresults.css", "$dirname/testresults.css"); + + open(INDEX, ">$dirname/index.html"); + + bless($self, $class); + + $self->print_html_header("Samba Testsuite Run", *INDEX); + + print INDEX " <center>"; + print INDEX " <table>\n"; + print INDEX " <tr>\n"; + print INDEX " <td class=\"tableHead\">Test</td>\n"; + print INDEX " <td class=\"tableHead\">Result</td>\n"; + print INDEX " </tr>\n"; + + return $self; +} + +sub print_html_header($$$) +{ + my ($self, $title, $fh) = @_; + + print $fh "<html lang=\"en\">\n"; + print $fh "<head>\n"; + print $fh " <title>$title</title>\n"; + print $fh " <link rel=\"stylesheet\" type=\"text/css\" href=\"testresults.css\"/>\n"; + print $fh "</head>\n"; + print $fh "<body>\n"; + print $fh "<table width=\"100%\" border=\"0\" cellspacing=\"0\">\n"; + print $fh " <tr><td class=\"title\">$title</td></tr>\n"; + print $fh " <tr><td>\n"; +} + +sub print_html_footer($$) +{ + my ($self, $fh) = @_; + + print $fh "</td></tr>\n"; + print $fh "</table>\n"; + print $fh "</body>\n"; + print $fh "</html>\n"; +} + +sub output_msg($$); + +sub start_testsuite($$) +{ + my ($self, $name) = @_; + + $self->{local_statistics} = { + success => 0, + skip => 0, + error => 0, + failure => 0 + }; + + $self->{NAME} = $name; + $self->{HTMLFILE} = "$name.html"; + $self->{HTMLFILE} =~ s/[:\t\n \/]/_/g; + + open(TEST, ">$self->{dirname}/$self->{HTMLFILE}") or die("Unable to open $self->{HTMLFILE} for writing"); + + $self->print_html_header("Test Results for $name", *TEST); + + print TEST "<h2>Tests</h2>\n"; + + print TEST " <table>\n"; +} + +sub control_msg($$) +{ + my ($self, $output) = @_; + + $self->{msg} .= "<span class=\"control\">$output<br/></span>\n"; +} + +sub output_msg($$) +{ + my ($self, $output) = @_; + + unless (defined($self->{active_test})) { + print TEST "$output<br/>"; + } else { + $self->{msg} .= "$output<br/>"; + } +} + +sub end_testsuite($$$$) +{ + my ($self, $name, $result, $unexpected, $reason) = @_; + + print TEST "</table>\n"; + + print TEST "<div class=\"duration\">Duration: " . (time() - $self->{START_TIME}) . "s</div>\n"; + + $self->print_html_footer(*TEST); + + close(TEST); + + print INDEX "<tr>\n"; + print INDEX " <td class=\"testSuite\"><a href=\"$self->{HTMLFILE}\">$name</a></td>\n"; + my $st = $self->{local_statistics}; + + if (not $unexpected) { + if ($result eq "failure") { + print INDEX " <td class=\"resultExpectedFailure\">"; + } else { + print INDEX " <td class=\"resultOk\">"; + } + } else { + print INDEX " <td class=\"resultFailure\">"; + } + + my $l = 0; + if ($st->{success} > 0) { + print INDEX "$st->{success} ok"; + $l++; + } + if ($st->{skip} > 0) { + print INDEX ", " if ($l); + print INDEX "$st->{skip} skipped"; + $l++; + } + if ($st->{failure} > 0) { + print INDEX ", " if ($l); + print INDEX "$st->{failure} failures"; + $l++; + } + if ($st->{error} > 0) { + print INDEX ", " if ($l); + print INDEX "$st->{error} errors"; + $l++; + } + + if ($l == 0) { + if (not $unexpected) { + print INDEX "OK"; + } else { + print INDEX "FAIL"; + } + } + + print INDEX "</td>"; + + print INDEX "</tr>\n"; +} + +sub start_test($$) +{ + my ($self, $parents, $testname) = @_; + + if ($#$parents == -1) { + $self->{START_TIME} = time(); + $self->start_testsuite($testname); + return; + } + + $self->{active_test} = $testname; + $self->{msg} = ""; +} + +sub end_test($$$$$$) +{ + my ($self, $parents, $testname, $result, $unexpected, $reason) = @_; + + if ($#$parents == -1) { + $self->end_testsuite($testname, $result, $unexpected, $reason); + return; + } + + print TEST "<tr>"; + + $self->{local_statistics}->{$result}++; + + my $track_class; + + if ($result eq "skip") { + print TEST "<td class=\"outputSkipped\">\n"; + $track_class = "skip"; + } elsif ($unexpected) { + print TEST "<td class=\"outputFailure\">\n"; + if ($result eq "error") { + $track_class = "error"; + } else { + $track_class = "unexpected_$result"; + } + } else { + if ($result eq "failure") { + print TEST "<td class=\"outputExpectedFailure\">\n"; + } else { + print TEST "<td class=\"outputOk\">\n"; + } + $track_class = "expected_$result"; + } + + push(@{$self->{error_summary}->{$track_class}}, , + [$self->{HTMLFILE}, $testname, $self->{NAME}, + $reason]); + + print TEST "<a name=\"$testname\"><h3>$testname</h3></a>\n"; + + print TEST $self->{msg}; + + if (defined($reason)) { + print TEST "<div class=\"reason\">$reason</div>\n"; + } + + print TEST "</td></tr>\n"; + + $self->{active_test} = undef; +} + +sub summary($) +{ + my ($self) = @_; + + my $st = $self->{statistics}; + print INDEX "<tr>\n"; + print INDEX " <td class=\"testSuiteTotal\">Total</td>\n"; + + if ($st->{TESTS_UNEXPECTED_OK} == 0 and + $st->{TESTS_UNEXPECTED_FAIL} == 0 and + $st->{TESTS_ERROR} == 0) { + print INDEX " <td class=\"resultOk\">"; + } else { + print INDEX " <td class=\"resultFailure\">"; + } + print INDEX ($st->{TESTS_EXPECTED_OK} + $st->{TESTS_UNEXPECTED_OK}) . " ok"; + if ($st->{TESTS_UNEXPECTED_OK} > 0) { + print INDEX " ($st->{TESTS_UNEXPECTED_OK} unexpected)"; + } + if ($st->{TESTS_SKIP} > 0) { + print INDEX ", $st->{TESTS_SKIP} skipped"; + } + if (($st->{TESTS_UNEXPECTED_FAIL} + $st->{TESTS_EXPECTED_FAIL}) > 0) { + print INDEX ", " . ($st->{TESTS_UNEXPECTED_FAIL} + $st->{TESTS_EXPECTED_FAIL}) . " failures"; + if ($st->{TESTS_UNEXPECTED_FAIL} > 0) { + print INDEX " ($st->{TESTS_EXPECTED_FAIL} expected)"; + } + } + if ($st->{TESTS_ERROR} > 0) { + print INDEX ", $st->{TESTS_ERROR} errors"; + } + + print INDEX "</td>"; + + print INDEX "</tr>\n"; + + print INDEX "</table>\n"; + print INDEX "<a href=\"summary.html\">Summary</a>\n"; + print INDEX "</center>\n"; + $self->print_html_footer(*INDEX); + close(INDEX); + + my $summ = $self->{error_summary}; + open(SUMMARY, ">$self->{dirname}/summary.html"); + $self->print_html_header("Summary", *SUMMARY); + sub print_table($$) { + my ($title, $list) = @_; + return if ($#$list == -1); + print SUMMARY "<h3>$title</h3>\n"; + print SUMMARY "<table>\n"; + print SUMMARY "<tr>\n"; + print SUMMARY " <td class=\"tableHead\">Testsuite</td>\n"; + print SUMMARY " <td class=\"tableHead\">Test</td>\n"; + print SUMMARY " <td class=\"tableHead\">Reason</td>\n"; + print SUMMARY "</tr>\n"; + + foreach (@$list) { + print SUMMARY "<tr>\n"; + print SUMMARY " <td><a href=\"" . $$_[0] . "\">$$_[2]</a></td>\n"; + print SUMMARY " <td><a href=\"" . $$_[0] . "#$$_[1]\">$$_[1]</a></td>\n"; + if (defined($$_[3])) { + print SUMMARY " <td>$$_[3]</td>\n"; + } else { + print SUMMARY " <td></td>\n"; + } + print SUMMARY "</tr>\n"; + } + + print SUMMARY "</table>"; + } + print_table("Errors", $summ->{error}); + print_table("Unexpected successes", $summ->{unexpected_success}); + print_table("Unexpected failures", $summ->{unexpected_failure}); + print_table("Skipped tests", $summ->{skip}); + print_table("Expected failures", $summ->{expected_failure}); + + print SUMMARY "<h3>Skipped testsuites</h3>\n"; + print SUMMARY "<table>\n"; + print SUMMARY "<tr>\n"; + print SUMMARY " <td class=\"tableHead\">Testsuite</td>\n"; + print SUMMARY " <td class=\"tableHead\">Reason</td>\n"; + print SUMMARY "</tr>\n"; + + foreach (@{$summ->{skip_testsuites}}) { + print SUMMARY "<tr>\n"; + print SUMMARY " <td>$$_[0]</td>\n"; + if (defined($$_[1])) { + print SUMMARY " <td>$$_[1]</td>\n"; + } else { + print SUMMARY " <td></td>\n"; + } + print SUMMARY "</tr>\n"; + } + + print SUMMARY "</table>"; + + $self->print_html_footer(*SUMMARY); + close(SUMMARY); +} + +sub skip_testsuite($$$$) +{ + my ($self, $name, $reason) = @_; + + push (@{$self->{error_summary}->{skip_testsuites}}, + [$name, $reason]); +} + +1; diff --git a/source4/selftest/output/plain.pm b/source4/selftest/output/plain.pm new file mode 100644 index 0000000000..4bec4e0fdc --- /dev/null +++ b/source4/selftest/output/plain.pm @@ -0,0 +1,195 @@ +#!/usr/bin/perl + +package output::plain; +use Exporter; +@ISA = qw(Exporter); + +use FindBin qw($RealBin); +use lib "$RealBin/.."; + +use strict; + +sub new($$$$$$$) { + my ($class, $summaryfile, $verbose, $immediate, $statistics, $totaltests) = @_; + my $self = { + verbose => $verbose, + immediate => $immediate, + statistics => $statistics, + start_time => time(), + test_output => {}, + suitesfailed => [], + suites_ok => 0, + skips => {}, + summaryfile => $summaryfile, + index => 0, + totalsuites => $totaltests, + }; + bless($self, $class); +} + +sub output_msg($$); + +sub start_testsuite($$) +{ + my ($self, $name) = @_; + + $self->{index}++; + $self->{NAME} = $name; + $self->{START_TIME} = time(); + + my $duration = $self->{START_TIME} - $self->{start_time}; + + $self->{test_output}->{$name} = "" unless($self->{verbose}); + + my $out = ""; + $out .= "[$self->{index}/$self->{totalsuites} in ".$duration."s"; + $out .= sprintf(", %d errors", ($#{$self->{suitesfailed}}+1)) if ($#{$self->{suitesfailed}} > -1); + $out .= "] $name"; + if ($self->{immediate}) { + print "$out\n"; + } else { + require Term::ReadKey; + my ($wchar, $hchar, $wpixels, $hpixels) = Term::ReadKey::GetTerminalSize(); + foreach (1..$wchar) { $out.= " "; } + print "\r".substr($out, 0, $wchar); + } +} + +sub output_msg($$) +{ + my ($self, $output) = @_; + + if ($self->{verbose}) { + require FileHandle; + print $output; + STDOUT->flush(); + } else { + $self->{test_output}->{$self->{NAME}} .= $output; + } +} + +sub control_msg($$) +{ + my ($self, $output) = @_; + + $self->output_msg($output); +} + +sub end_testsuite($$$$$) +{ + my ($self, $name, $result, $unexpected, $reason) = @_; + my $out = ""; + + if ($unexpected) { + if ($result eq "success" and not defined($reason)) { + $reason = "Expected negative exit code, got positive exit code"; + } + $self->output_msg("ERROR: $reason\n"); + push (@{$self->{suitesfailed}}, $name); + } else { + $self->{suites_ok}++; + } + + if ($unexpected and $self->{immediate} and not $self->{verbose}) { + $out .= $self->{test_output}->{$name}; + } + + + print $out; +} + +sub start_test($$$) +{ + my ($self, $parents, $testname) = @_; + + if ($#$parents == -1) { + $self->start_testsuite($testname); + } +} + +sub end_test($$$$$) +{ + my ($self, $parents, $testname, $result, $unexpected, $reason) = @_; + + if ($#$parents == -1) { + $self->end_testsuite($testname, $result, $unexpected, $reason); + return; + } + + my $append = ""; + + unless ($unexpected) { + $self->{test_output}->{$self->{NAME}} = ""; + return; + } + + my $fullname = join(".", @$parents).".$testname"; + + $append = "UNEXPECTED($result): $testname ($fullname)\n"; + + $self->{test_output}->{$self->{NAME}} .= $append; + + if ($self->{immediate} and not $self->{verbose}) { + print $self->{test_output}->{$self->{NAME}}; + $self->{test_output}->{$self->{NAME}} = ""; + } +} + +sub summary($) +{ + my ($self) = @_; + + open(SUMMARY, ">$self->{summaryfile}"); + + if ($#{$self->{suitesfailed}} > -1) { + print SUMMARY "= Failed tests =\n"; + + foreach (@{$self->{suitesfailed}}) { + print SUMMARY "== $_ ==\n"; + print SUMMARY $self->{test_output}->{$_}."\n\n"; + } + + print SUMMARY "\n"; + } + + if (not $self->{immediate} and not $self->{verbose}) { + foreach (@{$self->{suitesfailed}}) { + print "===============================================================================\n"; + print "FAIL: $_\n"; + print $self->{test_output}->{$_}; + print "\n"; + } + } + + print SUMMARY "= Skipped tests =\n"; + foreach my $reason (keys %{$self->{skips}}) { + print SUMMARY "$reason\n"; + foreach my $name (@{$self->{skips}->{$reason}}) { + print SUMMARY "\t$name\n"; + } + print SUMMARY "\n"; + } + close(SUMMARY); + + print "\nA summary with detailed informations can be found in:\n $self->{summaryfile}\n"; + + if ($#{$self->{suitesfailed}} == -1) { + my $ok = $self->{statistics}->{TESTS_EXPECTED_OK} + + $self->{statistics}->{TESTS_EXPECTED_FAIL}; + print "\nALL OK ($ok tests in $self->{suites_ok} testsuites)\n"; + } else { + print "\nFAILED ($self->{statistics}->{TESTS_UNEXPECTED_FAIL} failures and $self->{statistics}->{TESTS_ERROR} errors in ". ($#{$self->{suitesfailed}}+1) ." testsuites)\n"; + } + +} + +sub skip_testsuite($$) +{ + my ($self, $name, $reason) = @_; + + push (@{$self->{skips}->{$reason}}, $name); + + $self->{totalsuites}--; +} + +1; diff --git a/source4/selftest/output/testresults.css b/source4/selftest/output/testresults.css new file mode 100644 index 0000000000..66d1d6b2ad --- /dev/null +++ b/source4/selftest/output/testresults.css @@ -0,0 +1,129 @@ +/* Stylesheet for Samba test results. + * + * Partially based on the CSS file from lcov. + */ + +/* All views: main title format */ +td.title +{ + text-align: center; + padding-bottom: 10px; + font-family: sans-serif; + font-size: 20pt; + font-style: italic; + font-weight: bold; +} + +/* Index table headers */ +td.tableHead +{ + text-align: center; + color: #FFFFFF; + background-color: #6688D4; + font-family: sans-serif; + font-size: 120%; + font-weight: bold; +} + +/* Testsuite names */ +td.testSuite +{ + text-align: left; + padding-left: 10px; + padding-right: 20px; + color: #284FA8; + background-color: #DAE7FE; + font-family: monospace; +} + +/* Successful */ +td.resultOk +{ + text-align: right; + padding-left: 10px; + padding-right: 10px; + background-color: #A7FC9D; + font-weight: bold; +} + +/* Failure */ +td.resultFailure +{ + text-align: right; + padding-left: 10px; + padding-right: 10px; + background-color: #FF0000; + font-weight: bold; +} + +/* Expected failure */ +td.resultExpectedFailure +{ + text-align: right; + padding-left: 10px; + padding-right: 10px; + background-color: #FFA500; + font-weight: bold; +} + +/* Skipped */ +td.resultSkipped +{ + text-align: center; + padding-left: 10px; + padding-right: 10px; + background-color: #FFEA20; + font-weight: bold; +} + +td.duration +{ + text-align: right; +} + +td.durationSkipped +{ + text-align: right; +} + +td.outputSkipped +{ + background-color: #FFEA20; +} + +td.outputOk +{ + background-color: #A7FC9D; +} + +td.outputFailure +{ + background-color: #FF0000; +} + +td.outputExpectedFailure +{ + background-color: #FFA500; +} + +div.reason +{ + text-align: center; + font-weight: bold; +} + +span.control +{ + display: none; +} + +div.duration +{ + text-align: right; + font-weight: bold; +} + +div.command +{ + background-color: gray; +} diff --git a/source4/selftest/samba4_tests.sh b/source4/selftest/samba4_tests.sh new file mode 100755 index 0000000000..6f6ffbba60 --- /dev/null +++ b/source4/selftest/samba4_tests.sh @@ -0,0 +1,362 @@ +#!/bin/sh +# This script generates a list of testsuites that should be run as part of +# the Samba 4 test suite. + +# The output of this script is parsed by selftest.pl, which then decides +# which of the tests to actually run. It will, for example, skip all tests +# listed in samba4-skip or only run a subset during "make quicktest". + +# The idea is that this script outputs all of the tests of Samba 4, not +# just those that are known to pass, and list those that should be skipped +# or are known to file in samba4-skip/samba4-knownfail. This makes it +# very easy to see what functionality is still missing in Samba 4 and makes +# it possible to run the testsuite against other servers, such as Samba 3 or +# Windows that have a different set of features. + +# The syntax for a testsuite is "-- TEST --" on a single line, followed +# by the name of the test, the environment it needs and the command to run, all +# three separated by newlines. All other lines in the output are considered +# comments. + +if [ ! -n "$PERL" ] +then + PERL=perl +fi + +incdir=`dirname $0` + +plantest() { + name=$1 + env=$2 + shift 2 + cmdline="$*" + echo "-- TEST --" + if [ "$env" = "none" ]; then + echo "samba4.$name" + else + echo "samba4.$name ($env)" + fi + echo $env + echo $cmdline +} + +normalize_testname() { + name=$1 + shift 1 + echo $name | tr "A-Z-" "a-z." +} + +plansmbtorturetest() { + name=$1 + env=$2 + shift 2 + other_args="$*" + modname=`normalize_testname $name` + cmdline="$VALGRIND $smb4torture $other_args $name" + plantest "$modname" "$env" $cmdline +} + +$incdir/../bin/smbtorture -V + +samba4srcdir=$incdir/.. +samba4bindir=$samba4srcdir/bin +SCRIPTDIR=$samba4srcdir/../testprogs/ejs +smb4torture="$samba4bindir/smbtorture $TORTURE_OPTIONS" + +plantest "js.base" dc "$SCRIPTDIR/base.js" $CONFIGURATION +plantest "js.ldb" none "$SCRIPTDIR/ldb.js" `pwd` $CONFIGURATION -d 10 + +# Simple tests for LDAP and CLDAP + +for options in "" "--option=socket:testnonblock=true" "-U\$USERNAME%\$PASSWORD --option=socket:testnonblock=true" "-U\$USERNAME%\$PASSWORD"; do + plantest "ldb.ldap with options $options" dc $samba4srcdir/../testprogs/blackbox/test_ldb.sh ldap \$SERVER_IP $options +done +# see if we support ldaps +if grep ENABLE_GNUTLS.1 include/config.h > /dev/null; then + for options in "" "-U\$USERNAME%\$PASSWORD"; do + plantest "ldb.ldaps with options $options" dc $samba4srcdir/../testprogs/blackbox/test_ldb.sh ldaps \$SERVER_IP $options + done +fi +plantest "ldb.ldapi with options $options" dc $samba4srcdir/../testprogs/blackbox/test_ldb.sh ldapi \$PREFIX_ABS/dc/private/ldapi $options +for t in LDAP-CLDAP LDAP-BASIC LDAP-SCHEMA LDAP-UPTODATEVECTOR +do + plansmbtorturetest "$t" dc "-U\$USERNAME%\$PASSWORD" //\$SERVER_IP/_none_ +done + +# only do the ldb tests when not in quick mode - they are quite slow, and ldb +# is now pretty well tested by the rest of the quick tests anyway +LDBDIR=$samba4srcdir/lib/ldb +export LDBDIR +plantest "ldb" none TEST_DATA_PREFIX=\$PREFIX $LDBDIR/tests/test-tdb.sh + +# Tests for RPC + +# add tests to this list as they start passing, so we test +# that they stay passing +ncacn_np_tests="RPC-SCHANNEL RPC-JOIN RPC-LSA RPC-DSSETUP RPC-ALTERCONTEXT RPC-MULTIBIND RPC-NETLOGON RPC-HANDLES RPC-SAMSYNC RPC-SAMBA3SESSIONKEY RPC-SAMBA3-GETUSERNAME RPC-SAMBA3-LSA RPC-BINDSAMBA3 RPC-NETLOGSAMBA3 RPC-ASYNCBIND RPC-LSALOOKUP RPC-LSA-GETUSER RPC-SCHANNEL2 RPC-AUTHCONTEXT" +ncalrpc_tests="RPC-SCHANNEL RPC-JOIN RPC-LSA RPC-DSSETUP RPC-ALTERCONTEXT RPC-MULTIBIND RPC-NETLOGON RPC-DRSUAPI RPC-ASYNCBIND RPC-LSALOOKUP RPC-LSA-GETUSER RPC-SCHANNEL2 RPC-AUTHCONTEXT" +ncacn_ip_tcp_tests="RPC-SCHANNEL RPC-JOIN RPC-LSA RPC-DSSETUP RPC-ALTERCONTEXT RPC-MULTIBIND RPC-NETLOGON RPC-HANDLES RPC-DSSYNC RPC-ASYNCBIND RPC-LSALOOKUP RPC-LSA-GETUSER RPC-SCHANNEL2 RPC-AUTHCONTEXT" +slow_ncacn_np_tests="RPC-SAMLOGON RPC-SAMR RPC-SAMR-USERS RPC-SAMR-PASSWORDS" +slow_ncalrpc_tests="RPC-SAMR RPC-SAMR-PASSWORDS" +slow_ncacn_ip_tcp_tests="RPC-SAMR RPC-SAMR-PASSWORDS RPC-CRACKNAMES" + +all_tests="$ncalrpc_tests $ncacn_np_tests $ncacn_ip_tcp_tests $slow_ncalrpc_tests $slow_ncacn_np_tests $slow_ncacn_ip_tcp_tests RPC-SECRETS RPC-SAMBA3-SHARESEC" + +# Make sure all tests get run +for t in `$smb4torture --list | grep "^RPC-"` +do + echo $all_tests | grep $t > /dev/null + if [ $? -ne 0 ] + then + auto_rpc_tests="$auto_rpc_tests $t" + fi +done + +for bindoptions in seal,padcheck $VALIDATE bigendian; do + for transport in ncalrpc ncacn_np ncacn_ip_tcp; do + case $transport in + ncalrpc) tests=$ncalrpc_tests ;; + ncacn_np) tests=$ncacn_np_tests ;; + ncacn_ip_tcp) tests=$ncacn_ip_tcp_tests ;; + esac + for t in $tests; do + plantest "`normalize_testname $t` on $transport with $bindoptions" dc $VALGRIND $smb4torture $transport:"\$SERVER[$bindoptions]" -U"\$USERNAME"%"\$PASSWORD" -W \$DOMAIN $t "$*" + done + plantest "rpc.samba3.sharesec on $transport with $bindoptions" dc $VALGRIND $smb4torture $transport:"\$SERVER[$bindoptions]" -U"\$USERNAME"%"\$PASSWORD" -W \$DOMAIN --option=torture:share=tmp RPC-SAMBA3-SHARESEC "$*" + done +done + +for bindoptions in "" $VALIDATE bigendian; do + for t in $auto_rpc_tests; do + plantest "`normalize_testname $t` with $bindoptions" dc $VALGRIND $smb4torture "\$SERVER[$bindoptions]" -U"\$USERNAME"%"\$PASSWORD" -W \$DOMAIN $t "$*" + done +done + +for bindoptions in connect $VALIDATE ; do + for transport in ncalrpc ncacn_np ncacn_ip_tcp; do + case $transport in + ncalrpc) tests=$slow_ncalrpc_tests ;; + ncacn_np) tests=$slow_ncacn_np_tests ;; + ncacn_ip_tcp) tests=$slow_ncacn_ip_tcp_tests ;; + esac + for t in $tests; do + plantest "`normalize_testname $t` on $transport with $bindoptions" dc $VALGRIND $smb4torture $transport:"\$SERVER[$bindoptions]" -U"\$USERNAME"%"\$PASSWORD" -W \$DOMAIN $t "$*" + done + done +done + + +# Tests for the NET API + +net=`$smb4torture --list | grep ^NET-` + +for t in $net; do + plansmbtorturetest "$t" dc "\$SERVER[$VALIDATE]" -U"\$USERNAME"%"\$PASSWORD" -W "\$DOMAIN" "$*" +done + +# Tests for session keys + +bindoptions="" +transport="ncacn_np" +for ntlmoptions in \ + "-k no --option=usespnego=yes" \ + "-k no --option=usespnego=yes --option=ntlmssp_client:128bit=no" \ + "-k no --option=usespnego=yes --option=ntlmssp_client:56bit=yes" \ + "-k no --option=usespnego=yes --option=ntlmssp_client:56bit=no" \ + "-k no --option=usespnego=yes --option=ntlmssp_client:128bit=no --option=ntlmssp_client:56bit=yes" \ + "-k no --option=usespnego=yes --option=ntlmssp_client:128bit=no --option=ntlmssp_client:56bit=no" \ + "-k no --option=usespnego=yes --option=clientntlmv2auth=yes" \ + "-k no --option=usespnego=yes --option=clientntlmv2auth=yes --option=ntlmssp_client:128bit=no" \ + "-k no --option=usespnego=yes --option=clientntlmv2auth=yes --option=ntlmssp_client:128bit=no --option=ntlmssp_client:56bit=yes" \ + "-k no --option=usespnego=no --option=clientntlmv2auth=yes" \ + "-k no --option=gensec:spnego=no --option=clientntlmv2auth=yes" \ + "-k no --option=usespnego=no"; do + name="rpc.secrets on $transport with $bindoptions with $ntlmoptions" + plantest "$name" dc $smb4torture $transport:"\$SERVER[$bindoptions]" $ntlmoptions -U"\$USERNAME"%"\$PASSWORD" -W \$DOMAIN --option=gensec:target_hostname=\$NETBIOSNAME RPC-SECRETS "$*" +done +plantest "rpc.secrets on $transport with $bindoptions with Kerberos" dc $smb4torture $transport:"\$SERVER[$bindoptions]" -k yes -U"\$USERNAME"%"\$PASSWORD" -W \$DOMAIN "--option=gensec:target_hostname=\$NETBIOSNAME" RPC-SECRETS "$*" +plantest "rpc.secrets on $transport with $bindoptions with Kerberos - use target principal" dc $smb4torture $transport:"\$SERVER[$bindoptions]" -k yes -U"\$USERNAME"%"\$PASSWORD" -W \$DOMAIN "--option=clientusespnegoprincipal=yes" "--option=gensec:target_hostname=\$NETBIOSNAME" RPC-SECRETS "$*" +plantest "rpc.secrets on $transport with Kerberos - use Samba3 style login" dc $smb4torture $transport:"\$SERVER" -k yes -U"\$USERNAME"%"\$PASSWORD" -W "\$DOMAIN" "--option=gensec:fake_gssapi_krb5=yes" "--option=gensec:gssapi_krb5=no" "--option=gensec:target_hostname=\$NETBIOSNAME" "RPC-SECRETS-none*" "$*" +plantest "rpc.secrets on $transport with Kerberos - use Samba3 style login, use target principal" dc $smb4torture $transport:"\$SERVER" -k yes -U"\$USERNAME"%"\$PASSWORD" -W "\$DOMAIN" "--option=clientusespnegoprincipal=yes" "--option=gensec:fake_gssapi_krb5=yes" "--option=gensec:gssapi_krb5=no" "--option=gensec:target_hostname=\$NETBIOSNAME" "RPC-SECRETS-none*" "$*" + +# Echo tests +transports="ncacn_np ncacn_ip_tcp ncalrpc" + +for transport in $transports; do + for bindoptions in connect spnego spnego,sign spnego,seal $VALIDATE padcheck bigendian bigendian,seal; do + for ntlmoptions in \ + "--option=socket:testnonblock=True --option=torture:quick=yes"; do + plantest "rpc.echo on $transport with $bindoptions and $ntlmoptions" dc $smb4torture $transport:"\$SERVER[$bindoptions]" $ntlmoptions -U"\$USERNAME"%"\$PASSWORD" -W "\$DOMAIN" RPC-ECHO "$*" + done + done +done + +for transport in $transports; do + for bindoptions in sign seal; do + for ntlmoptions in \ + "--option=ntlmssp_client:ntlm2=yes --option=torture:quick=yes" \ + "--option=ntlmssp_client:ntlm2=no --option=torture:quick=yes" \ + "--option=ntlmssp_client:ntlm2=yes --option=ntlmssp_client:128bit=no --option=torture:quick=yes" \ + "--option=ntlmssp_client:ntlm2=no --option=ntlmssp_client:128bit=no --option=torture:quick=yes" \ + "--option=ntlmssp_client:ntlm2=yes --option=ntlmssp_client:keyexchange=no --option=torture:quick=yes" \ + "--option=ntlmssp_client:ntlm2=no --option=ntlmssp_client:keyexchange=no --option=torture:quick=yes" \ + "--option=clientntlmv2auth=yes --option=ntlmssp_client:keyexchange=no --option=torture:quick=yes" \ + "--option=clientntlmv2auth=yes --option=ntlmssp_client:128bit=no --option=ntlmssp_client:keyexchange=yes --option=torture:quick=yes" \ + "--option=clientntlmv2auth=yes --option=ntlmssp_client:128bit=no --option=ntlmssp_client:keyexchange=no --option=torture:quick=yes" \ + ; do + plantest "rpc.echo on $transport with $bindoptions and $ntlmoptions" dc $smb4torture $transport:"\$SERVER[$bindoptions]" $ntlmoptions -U"\$USERNAME"%"\$PASSWORD" -W \$DOMAIN RPC-ECHO "$*" + done + done +done + +plantest "rpc.echo on ncacn_np over smb2" dc $smb4torture ncacn_np:"\$SERVER[smb2]" -U"\$USERNAME"%"\$PASSWORD" -W \$DOMAIN RPC-ECHO "$*" + +# Tests against the NTVFS POSIX backend +NTVFSARGS="" +NTVFSARGS="${NTVFSARGS} --option=torture:sharedelay=100000" +NTVFSARGS="${NTVFSARGS} --option=torture:oplocktimeout=3" +NTVFSARGS="${NTVFSARGS} --option=torture:writetimeupdatedelay=500000" + +smb2=`$smb4torture --list | grep "^SMB2-" | xargs` +#The QFILEINFO-IPC test needs to be on ipc$ +raw=`$smb4torture --list | grep "^RAW-" | grep -v "RAW-QFILEINFO-IPC"| xargs` +base=`$smb4torture --list | grep "^BASE-" | xargs` + +for t in $base $raw $smb2; do + plansmbtorturetest "$t" dc $ADDARGS //\$SERVER/tmp -U"\$USERNAME"%"\$PASSWORD" $NTVFSARGS +done + +plansmbtorturetest "RAW-QFILEINFO-IPC" dc $ADDARGS //\$SERVER/ipc$ -U"\$USERNAME"%"\$PASSWORD" + +rap=`$smb4torture --list | grep "^RAP-" | xargs` +for t in $rap; do + plansmbtorturetest "$t" dc $ADDARGS //\$SERVER/IPC\\\$ -U"\$USERNAME"%"\$PASSWORD" +done + +# Tests against the NTVFS CIFS backend +for t in $base $raw; do + plantest "ntvfs.cifs.`normalize_testname $t`" dc $VALGRIND $smb4torture //\$NETBIOSNAME/cifs -U"\$USERNAME"%"\$PASSWORD" $NTVFSARGS $t +done + +# Local tests + +for t in `$smb4torture --list | grep "^LOCAL-" | xargs`; do + plansmbtorturetest "$t" none ncalrpc: "$*" +done + +if test -f $samba4bindir/tdbtorture +then + plantest "tdb.stress" none $VALGRIND $samba4bindir/tdbtorture +fi + +# Pidl tests + +if test x"${PIDL_TESTS_SKIP}" = x"yes"; then + echo "Skipping pidl tests - PIDL_TESTS_SKIP=yes" +elif $PERL -e 'eval require Test::More;' > /dev/null 2>&1; then + for f in $samba4srcdir/pidl/tests/*.pl; do + plantest "pidl.`basename $f .pl`" none $PERL $f "|" $samba4srcdir/script/harness2subunit.pl + done +else + echo "Skipping pidl tests - Test::More not installed" +fi + +# Blackbox Tests: +# tests that interact directly with the command-line tools rather than using +# the API + +bbdir=$incdir/../../testprogs/blackbox + +plantest "blackbox.ndrdump" dc $bbdir/test_ndrdump.sh +plantest "blackbox.net" dc $samba4srcdir/utils/tests/test_net.sh "\$SERVER" "\$USERNAME" "\$PASSWORD" "\$DOMAIN" +plantest "blackbox.kinit" dc $bbdir/test_kinit.sh "\$SERVER" "\$USERNAME" "\$PASSWORD" "\$REALM" "\$DOMAIN" "$PREFIX" $CONFIGURATION +plantest "blackbox.cifsdd" dc $samba4srcdir/client/tests/test_cifsdd.sh "\$SERVER" "\$USERNAME" "\$PASSWORD" "\$DOMAIN" +plantest "blackbox.nmblookup" dc $samba4srcdir/utils/tests/test_nmblookup.sh "\$NETBIOSNAME" "\$NETBIOSALIAS" "\$SERVER" "\$SERVER_IP" +plantest "blackbox.nmblookup" member $samba4srcdir/utils/tests/test_nmblookup.sh "\$NETBIOSNAME" "\$NETBIOSALIAS" "\$SERVER" "\$SERVER_IP" +plantest "blackbox.locktest" dc $bbdir/test_locktest.sh "\$SERVER" "\$USERNAME" "\$PASSWORD" "\$DOMAIN" "$PREFIX" +plantest "blackbox.masktest" dc $bbdir/test_masktest.sh "\$SERVER" "\$USERNAME" "\$PASSWORD" "\$DOMAIN" "$PREFIX" +plantest "blackbox.gentest" dc $bbdir/test_gentest.sh "\$SERVER" "\$USERNAME" "\$PASSWORD" "\$DOMAIN" "$PREFIX" +plantest "blackbox.wbinfo" dc $samba4srcdir/nsswitch/tests/test_wbinfo.sh "\$DOMAIN" "\$USERNAME" "\$PASSWORD" "dc" +plantest "blackbox.wbinfo" member $samba4srcdir/nsswitch/tests/test_wbinfo.sh "\$DOMAIN" "\$DC_USERNAME" "\$DC_PASSWORD" "member" +plantest "blackbox.wintest" none $bbdir/test_wintest.sh "$TORTURE_OPTIONS" + +# Tests using the "Simple" NTVFS backend + +for t in "BASE-RW1"; do + plantest "ntvfs.simple.`normalize_testname $t`" dc $VALGRIND $smb4torture $ADDARGS //\$SERVER/simple -U"\$USERNAME"%"\$PASSWORD" $t +done + +DATADIR=$samba4srcdir/../testdata + +plantest "js.samba3sam" none $samba4bindir/smbscript $SCRIPTDIR/samba3sam.js $CONFIGURATION `pwd` $DATADIR/samba3/ + +# Domain Member Tests + +plantest "rpc.echo against member server with local creds" member $VALGRIND $smb4torture ncacn_np:"\$NETBIOSNAME" -U"\$NETBIOSNAME/\$USERNAME"%"\$PASSWORD" RPC-ECHO "$*" +plantest "rpc.echo against member server with domain creds" member $VALGRIND $smb4torture ncacn_np:"\$NETBIOSNAME" -U"\$DOMAIN/\$DC_USERNAME"%"\$DC_PASSWORD" RPC-ECHO "$*" +plantest "rpc.samr against member server with local creds" member $VALGRIND $smb4torture ncacn_np:"\$NETBIOSNAME" -U"\$NETBIOSNAME/\$USERNAME"%"\$PASSWORD" "RPC-SAMR" "$*" +plantest "rpc.samr.users against member server with local creds" member $VALGRIND $smb4torture ncacn_np:"\$NETBIOSNAME" -U"\$NETBIOSNAME/\$USERNAME"%"\$PASSWORD" "RPC-SAMR-USERS" "$*" +plantest "rpc.samr.passwords against member server with local creds" member $VALGRIND $smb4torture ncacn_np:"\$NETBIOSNAME" -U"\$NETBIOSNAME/\$USERNAME"%"\$PASSWORD" "RPC-SAMR-PASSWORDS" "$*" +plantest "blackbox.smbclient against member server with local creds" member $samba4srcdir/client/tests/test_smbclient.sh "\$NETBIOSNAME" "\$USERNAME" "\$PASSWORD" "\$NETBIOSNAME" "$PREFIX" + +NBT_TESTS=`$smb4torture --list | grep "^NBT-" | xargs` + +for t in $NBT_TESTS; do + plansmbtorturetest "$t" dc //\$SERVER/_none_ -U\$USERNAME%\$PASSWORD +done + +WB_OPTS="--option=\"torture:strict mode=no\"" +WB_OPTS="${WB_OPTS} --option=\"torture:timelimit=1\"" +WB_OPTS="${WB_OPTS} --option=\"torture:winbindd separator=/\"" +WB_OPTS="${WB_OPTS} --option=\"torture:winbindd netbios name=\$SERVER\"" +WB_OPTS="${WB_OPTS} --option=\"torture:winbindd netbios domain=\$DOMAIN\"" + +WINBIND_STRUCT_TESTS=`$smb4torture --list | grep "^WINBIND-STRUCT" | xargs` +WINBIND_NDR_TESTS=`$smb4torture --list | grep "^WINBIND-NDR" | xargs` +for env in dc member; do + for t in $WINBIND_STRUCT_TESTS; do + plansmbtorturetest $t $env $WB_OPTS //_none_/_none_ + done + + for t in $WINBIND_NDR_TESTS; do + plansmbtorturetest $t $env $WB_OPTS //_none_/_none_ + done +done + +if test -f $samba4bindir/nsstest +then + plantest "nss.test using winbind" member $VALGRIND $samba4bindir/nsstest $samba4bindir/shared/libnss_winbind.so +fi + +PYTHON=/usr/bin/python +SUBUNITRUN="$PYTHON ./scripting/bin/subunitrun" +plantest "ldb.python" none PYTHONPATH="$PYTHONPATH:lib/ldb/tests/python/" $SUBUNITRUN api +plantest "credentials.python" none PYTHONPATH="$PYTHONPATH:auth/credentials/tests" $SUBUNITRUN bindings +plantest "registry.python" none PYTHONPATH="$PYTHONPATH:lib/registry/tests/" $SUBUNITRUN bindings +plantest "tdb.python" none PYTHONPATH="$PYTHONPATH:lib/tdb/python/tests" $SUBUNITRUN simple +plantest "auth.python" none PYTHONPATH="$PYTHONPATH:auth/tests/" $SUBUNITRUN bindings +plantest "security.python" none PYTHONPATH="$PYTHONPATH:libcli/security/tests" $SUBUNITRUN bindings +plantest "param.python" none PYTHONPATH="$PYTHONPATH:param/tests" $SUBUNITRUN bindings +plantest "upgrade.python" none $SUBUNITRUN samba.tests.upgrade +plantest "samba.python" none $SUBUNITRUN samba.tests +plantest "provision.python" none $SUBUNITRUN samba.tests.provision +plantest "samba3.python" none $SUBUNITRUN samba.tests.samba3 +plantest "samr.python" dc $SUBUNITRUN samba.tests.dcerpc.sam +plantest "dcerpc.bare.python" dc $SUBUNITRUN samba.tests.dcerpc.bare +plantest "unixinfo.python" dc $SUBUNITRUN samba.tests.dcerpc.unix +plantest "samdb.python" none $SUBUNITRUN samba.tests.samdb +plantest "events.python" none PYTHONPATH="$PYTHONPATH:lib/events" $SUBUNITRUN tests +plantest "messaging.python" none PYTHONPATH="$PYTHONPATH:lib/messaging/tests" $SUBUNITRUN bindings +plantest "samba3sam.python" none PYTHONPATH="$PYTHONPATH:dsdb/samdb/ldb_modules/tests" $SUBUNITRUN samba3sam +plantest "rpcecho.python" dc $SUBUNITRUN samba.tests.dcerpc.rpcecho +plantest "winreg.python" dc $SUBUNITRUN -U\$USERNAME%\$PASSWORD samba.tests.dcerpc.registry +plantest "ldap.python" dc $PYTHON $samba4srcdir/lib/ldb/tests/python/ldap.py $CONFIGURATION \$SERVER -U\$USERNAME%\$PASSWORD -W \$DOMAIN +plantest "blackbox.samba3dump" none $PYTHON scripting/bin/samba3dump $samba4srcdir/../testdata/samba3 +rm -rf $PREFIX/upgrade +plantest "blackbox.upgrade" none $PYTHON setup/upgrade $CONFIGURATION --targetdir=$PREFIX/upgrade ../testdata/samba3 ../testdata/samba3/smb.conf +rm -rf $PREFIX/provision +mkdir $PREFIX/provision +plantest "blackbox.provision.py" none PYTHON="$PYTHON" $samba4srcdir/setup/tests/blackbox_provision.sh "$PREFIX/provision" +plantest "blackbox.provision-backend.py" none PYTHON="$PYTHON" $samba4srcdir/setup/tests/blackbox_provision-backend.sh "$PREFIX/provision" +plantest "blackbox.setpassword.py" none PYTHON="$PYTHON" $samba4srcdir/setup/tests/blackbox_setpassword.sh "$PREFIX/provision" +plantest "blackbox.newuser.py" none PYTHON="$PYTHON" $samba4srcdir/setup/tests/blackbox_newuser.sh "$PREFIX/provision" diff --git a/source4/selftest/selftest.pl b/source4/selftest/selftest.pl new file mode 100755 index 0000000000..e86ccbe468 --- /dev/null +++ b/source4/selftest/selftest.pl @@ -0,0 +1,874 @@ +#!/usr/bin/perl +# Bootstrap Samba and run a number of tests against it. +# Copyright (C) 2005-2008 Jelmer Vernooij <jelmer@samba.org> +# Published under the GNU GPL, v3 or later. + +=pod + +=head1 NAME + +selftest - Samba test runner + +=head1 SYNOPSIS + +selftest --help + +selftest [--srcdir=DIR] [--builddir=DIR] [--target=samba4|samba3|win|kvm] [--socket-wrapper] [--quick] [--exclude=FILE] [--include=FILE] [--one] [--prefix=prefix] [--immediate] [--testlist=FILE] [TESTS] + +=head1 DESCRIPTION + +A simple test runner. TESTS is a regular expression with tests to run. + +=head1 OPTIONS + +=over 4 + +=item I<--help> + +Show list of available options. + +=item I<--srcdir=DIR> + +Source directory. + +=item I<--builddir=DIR> + +Build directory. + +=item I<--prefix=DIR> + +Change directory to run tests in. Default is 'st'. + +=item I<--immediate> + +Show errors as soon as they happen rather than at the end of the test run. + +=item I<--target samba4|samba3|win|kvm> + +Specify test target against which to run. Default is 'samba4'. + +=item I<--quick> + +Run only a limited number of tests. Intended to run in about 30 seconds on +moderately recent systems. + +=item I<--socket-wrapper> + +Use socket wrapper library for communication with server. Only works +when the server is running locally. + +Will prevent TCP and UDP ports being opened on the local host but +(transparently) redirects these calls to use unix domain sockets. + +=item I<--expected-failures> + +Specify a file containing a list of tests that are expected to fail. Failures for +these tests will be counted as successes, successes will be counted as failures. + +The format for the file is, one entry per line: + +TESTSUITE-NAME.TEST-NAME + +The reason for a test can also be specified, by adding a hash sign (#) and the reason +after the test name. + +=item I<--exclude> + +Specify a file containing a list of tests that should be skipped. Possible +candidates are tests that segfault the server, flip or don't end. The format of this file is the same as +for the --expected-failures flag. + +=item I<--include> + +Specify a file containing a list of tests that should be run. Same format +as the --exclude flag. + +Not includes specified means all tests will be run. + +=item I<--one> + +Abort as soon as one test fails. + +=item I<--testlist> + +Load a list of tests from the specified location. + +=back + +=head1 ENVIRONMENT + +=over 4 + +=item I<SMBD_VALGRIND> + +=item I<TORTURE_MAXTIME> + +=item I<VALGRIND> + +=item I<TLS_ENABLED> + +=item I<srcdir> + +=back + +=head1 LICENSE + +selftest is licensed under the GNU General Public License L<http://www.gnu.org/licenses/gpl.html>. + +=head1 AUTHOR + +Jelmer Vernooij + +=cut + +use strict; + +use FindBin qw($RealBin $Script); +use File::Spec; +use Getopt::Long; +use POSIX; +use Cwd qw(abs_path); +use lib "$RealBin"; +use Subunit qw(parse_results); +use SocketWrapper; + +my $opt_help = 0; +my $opt_target = "samba4"; +my $opt_quick = 0; +my $opt_socket_wrapper = 0; +my $opt_socket_wrapper_pcap = undef; +my $opt_socket_wrapper_keep_pcap = undef; +my $opt_one = 0; +my $opt_immediate = 0; +my $opt_expected_failures = undef; +my @opt_exclude = (); +my @opt_include = (); +my $opt_verbose = 0; +my $opt_image = undef; +my $opt_testenv = 0; +my $ldap = undef; +my $opt_analyse_cmd = undef; +my $opt_resetup_env = undef; +my $opt_bindir = undef; +my $opt_no_lazy_setup = undef; +my $opt_format = "plain"; +my @testlists = (); + +my $srcdir = "."; +my $builddir = "."; +my $prefix = "./st"; + +my @expected_failures = (); +my @includes = (); +my @excludes = (); + +my $statistics = { + SUITES_FAIL => 0, + + TESTS_UNEXPECTED_OK => 0, + TESTS_EXPECTED_OK => 0, + TESTS_UNEXPECTED_FAIL => 0, + TESTS_EXPECTED_FAIL => 0, + TESTS_ERROR => 0, + TESTS_SKIP => 0, +}; + +sub find_in_list($$) +{ + my ($list, $fullname) = @_; + + foreach (@$list) { + if ($fullname =~ /$$_[0]/) { + return ($$_[1]) if ($$_[1]); + return "NO REASON SPECIFIED"; + } + } + + return undef; +} + +sub expecting_failure($) +{ + my ($name) = @_; + return find_in_list(\@expected_failures, $name); +} + +sub skip($) +{ + my ($name) = @_; + + return find_in_list(\@excludes, $name); +} + +sub getlog_env($); + +sub setup_pcap($) +{ + my ($name) = @_; + + return unless ($opt_socket_wrapper_pcap); + return unless defined($ENV{SOCKET_WRAPPER_PCAP_DIR}); + + my $fname = $name; + $fname =~ s%[^abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\-]%_%g; + + my $pcap_file = "$ENV{SOCKET_WRAPPER_PCAP_DIR}/$fname.pcap"; + + SocketWrapper::setup_pcap($pcap_file); + + return $pcap_file; +} + +sub cleanup_pcap($$$) +{ + my ($pcap_file, $expected_ret, $ret) = @_; + + return unless ($opt_socket_wrapper_pcap); + return if ($opt_socket_wrapper_keep_pcap); + return unless ($expected_ret == $ret); + return unless defined($pcap_file); + + unlink($pcap_file); +} + +sub run_testsuite($$$$$$) +{ + my ($envname, $name, $cmd, $i, $totalsuites, $msg_ops) = @_; + my $pcap_file = setup_pcap($name); + + $msg_ops->start_test([], $name); + + unless (open(RESULT, "$cmd 2>&1|")) { + $statistics->{TESTS_ERROR}++; + $msg_ops->end_test([], $name, "error", 1, "Unable to run $cmd: $!"); + $statistics->{SUITES_FAIL}++; + return 0; + } + + my $expected_ret = parse_results( + $msg_ops, $statistics, *RESULT, \&expecting_failure, [$name]); + + my $envlog = getlog_env($envname); + $msg_ops->output_msg("ENVLOG: $envlog\n") if ($envlog ne ""); + + $msg_ops->output_msg("CMD: $cmd\n"); + + my $ret = close(RESULT); + $ret = 0 unless $ret == 1; + + my $exitcode = $? >> 8; + + if ($ret == 1) { + $msg_ops->end_test([], $name, "success", $expected_ret != $ret, undef); + } else { + $msg_ops->end_test([], $name, "failure", $expected_ret != $ret, "Exit code was $exitcode"); + } + + cleanup_pcap($pcap_file, $expected_ret, $ret); + + if (not $opt_socket_wrapper_keep_pcap and defined($pcap_file)) { + $msg_ops->output_msg("PCAP FILE: $pcap_file\n"); + } + + if ($ret != $expected_ret) { + $statistics->{SUITES_FAIL}++; + exit(1) if ($opt_one); + } + + return ($ret == $expected_ret); +} + +sub ShowHelp() +{ + print "Samba test runner +Copyright (C) Jelmer Vernooij <jelmer\@samba.org> + +Usage: $Script [OPTIONS] PREFIX + +Generic options: + --help this help page + --target=samba[34]|win|kvm Samba version to target + --testlist=FILE file to read available tests from + +Paths: + --prefix=DIR prefix to run tests in [st] + --srcdir=DIR source directory [.] + --builddir=DIR output directory [.] + +Target Specific: + --socket-wrapper-pcap save traffic to pcap directories + --socket-wrapper-keep-pcap keep all pcap files, not just those for tests that + failed + --socket-wrapper enable socket wrapper + --expected-failures=FILE specify list of tests that is guaranteed to fail + +Samba4 Specific: + --ldap=openldap|fedora-ds back smbd onto specified ldap server + +Samba3 Specific: + --bindir=PATH path to binaries + +Kvm Specific: + --image=PATH path to KVM image + +Behaviour: + --quick run quick overall test + --one abort when the first test fails + --immediate print test output for failed tests during run + --verbose be verbose + --analyse-cmd CMD command to run after each test +"; + exit(0); +} + +my $result = GetOptions ( + 'help|h|?' => \$opt_help, + 'target=s' => \$opt_target, + 'prefix=s' => \$prefix, + 'socket-wrapper' => \$opt_socket_wrapper, + 'socket-wrapper-pcap' => \$opt_socket_wrapper_pcap, + 'socket-wrapper-keep-pcap' => \$opt_socket_wrapper_keep_pcap, + 'quick' => \$opt_quick, + 'one' => \$opt_one, + 'immediate' => \$opt_immediate, + 'expected-failures=s' => \$opt_expected_failures, + 'exclude=s' => \@opt_exclude, + 'include=s' => \@opt_include, + 'srcdir=s' => \$srcdir, + 'builddir=s' => \$builddir, + 'verbose' => \$opt_verbose, + 'testenv' => \$opt_testenv, + 'ldap:s' => \$ldap, + 'analyse-cmd=s' => \$opt_analyse_cmd, + 'no-lazy-setup' => \$opt_no_lazy_setup, + 'resetup-environment' => \$opt_resetup_env, + 'bindir:s' => \$opt_bindir, + 'format=s' => \$opt_format, + 'image=s' => \$opt_image, + 'testlist=s' => \@testlists + ); + +exit(1) if (not $result); + +ShowHelp() if ($opt_help); + +my $tests = shift; + +# quick hack to disable rpc validation when using valgrind - its way too slow +unless (defined($ENV{VALGRIND})) { + $ENV{VALIDATE} = "validate"; + $ENV{MALLOC_CHECK_} = 2; +} + +my $old_pwd = "$RealBin/.."; + +# Backwards compatibility: +if (defined($ENV{TEST_LDAP}) and $ENV{TEST_LDAP} eq "yes") { + if (defined($ENV{FEDORA_DS_ROOT})) { + $ldap = "fedora-ds"; + } else { + $ldap = "openldap"; + } +} + +my $torture_maxtime = ($ENV{TORTURE_MAXTIME} or 1200); +if ($ldap) { + # LDAP is slow + $torture_maxtime *= 2; +} + +$prefix =~ s+//+/+; +$prefix =~ s+/./+/+; +$prefix =~ s+/$++; + +die("using an empty prefix isn't allowed") unless $prefix ne ""; + +#Ensure we have the test prefix around +mkdir($prefix, 0777) unless -d $prefix; + +my $prefix_abs = abs_path($prefix); +my $srcdir_abs = abs_path($srcdir); + +die("using an empty absolute prefix isn't allowed") unless $prefix_abs ne ""; +die("using '/' as absolute prefix isn't allowed") unless $prefix_abs ne "/"; + +$ENV{PREFIX} = $prefix; +$ENV{KRB5CCNAME} = "$prefix/krb5ticket"; +$ENV{PREFIX_ABS} = $prefix_abs; +$ENV{SRCDIR} = $srcdir; +$ENV{SRCDIR_ABS} = $srcdir_abs; + +if (defined($ENV{RUN_FROM_BUILD_FARM}) and + ($ENV{RUN_FROM_BUILD_FARM} eq "yes")) { + $opt_format = "buildfarm"; +} + +my $tls_enabled = not $opt_quick; +$ENV{TLS_ENABLED} = ($tls_enabled?"yes":"no"); +$ENV{LDB_MODULES_PATH} = "$old_pwd/bin/modules/ldb"; +$ENV{LD_SAMBA_MODULE_PATH} = "$old_pwd/bin/modules"; +sub prefix_pathvar($$) +{ + my ($name, $newpath) = @_; + if (defined($ENV{$name})) { + $ENV{$name} = "$newpath:$ENV{$name}"; + } else { + $ENV{$name} = $newpath; + } +} +prefix_pathvar("PKG_CONFIG_PATH", "$old_pwd/bin/pkgconfig"); +# Required for smbscript: +prefix_pathvar("PATH", "$old_pwd/bin"); +prefix_pathvar("PYTHONPATH", "$old_pwd/bin/python"); + +if ($opt_socket_wrapper_keep_pcap) { + # Socket wrapper keep pcap implies socket wrapper pcap + $opt_socket_wrapper_pcap = 1; +} + +if ($opt_socket_wrapper_pcap) { + # Socket wrapper pcap implies socket wrapper + $opt_socket_wrapper = 1; +} + +my $socket_wrapper_dir; +if ($opt_socket_wrapper) { + $socket_wrapper_dir = SocketWrapper::setup_dir("$prefix/w", $opt_socket_wrapper_pcap); + print "SOCKET_WRAPPER_DIR=$socket_wrapper_dir\n"; +} else { + warn("Not using socket wrapper, but also not running as root. Will not be able to listen on proper ports") unless $< == 0; +} + +my $target; +my $testenv_default = "none"; + +if ($opt_target eq "samba4") { + $testenv_default = "member"; + require target::Samba4; + $target = new Samba4($opt_bindir or "$srcdir/bin", $ldap, "$srcdir/setup"); +} elsif ($opt_target eq "samba3") { + if ($opt_socket_wrapper and `$opt_bindir/smbd -b | grep SOCKET_WRAPPER` eq "") { + die("You must include --enable-socket-wrapper when compiling Samba in order to execute 'make test'. Exiting...."); + } + $testenv_default = "dc"; + require target::Samba3; + $target = new Samba3($opt_bindir); +} elsif ($opt_target eq "win") { + die("Windows tests will not run with socket wrapper enabled.") + if ($opt_socket_wrapper); + $testenv_default = "dc"; + require target::Windows; + $target = new Windows(); +} elsif ($opt_target eq "kvm") { + die("Kvm tests will not run with socket wrapper enabled.") + if ($opt_socket_wrapper); + require target::Kvm; + die("No image specified") unless ($opt_image); + $target = new Kvm($opt_image, undef); +} + +# +# Start a Virtual Distributed Ethernet Switch +# Returns the pid of the switch. +# +sub start_vde_switch($) +{ + my ($path) = @_; + + system("vde_switch --pidfile $path/vde.pid --sock $path/vde.sock --daemon"); + + open(PID, "$path/vde.pid"); + <PID> =~ /([0-9]+)/; + my $pid = $1; + close(PID); + + return $pid; +} + +# Stop a Virtual Distributed Ethernet Switch +sub stop_vde_switch($) +{ + my ($pid) = @_; + kill 9, $pid; +} + +sub read_test_regexes($) +{ + my ($name) = @_; + my @ret = (); + open(LF, "<$name") or die("unable to read $name: $!"); + while (<LF>) { + chomp; + next if (/^#/); + if (/^(.*?)([ \t]+)\#([\t ]*)(.*?)$/) { + push (@ret, [$1, $4]); + } else { + s/^(.*?)([ \t]+)\#([\t ]*)(.*?)$//; + push (@ret, [$_, undef]); + } + } + close(LF); + return @ret; +} + +if (defined($opt_expected_failures)) { + @expected_failures = read_test_regexes($opt_expected_failures); +} + +foreach (@opt_exclude) { + push (@excludes, read_test_regexes($_)); +} + +if ($opt_quick) { + push (@includes, read_test_regexes("samba4-quick")); +} + +foreach (@opt_include) { + push (@includes, read_test_regexes($_)); +} + +my $interfaces = join(',', ("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")); + +my $conffile = "$prefix_abs/client/client.conf"; + +sub write_clientconf($$) +{ + my ($conffile, $vars) = @_; + + mkdir("$prefix/client", 0777) unless -d "$prefix/client"; + + if ( -d "$prefix/client/private" ) { + unlink <$prefix/client/private/*>; + } else { + mkdir("$prefix/client/private", 0777); + } + + open(CF, ">$conffile"); + print CF "[global]\n"; + if (defined($ENV{VALGRIND})) { + print CF "\ticonv:native = true\n"; + } else { + print CF "\ticonv:native = false\n"; + } + print CF "\tnetbios name = client\n"; + if (defined($vars->{DOMAIN})) { + print CF "\tworkgroup = $vars->{DOMAIN}\n"; + } + if (defined($vars->{REALM})) { + print CF "\trealm = $vars->{REALM}\n"; + } + if (defined($vars->{NCALRPCDIR})) { + print CF "\tncalrpc dir = $vars->{NCALRPCDIR}\n"; + } + if (defined($vars->{PIDDIR})) { + print CF "\tpid directory = $vars->{PIDDIR}\n"; + } + if (defined($vars->{WINBINDD_SOCKET_DIR})) { + print CF "\twinbindd socket directory = $vars->{WINBINDD_SOCKET_DIR}\n"; + } + if ($opt_socket_wrapper) { + print CF "\tinterfaces = $interfaces\n"; + } + print CF " + private dir = $prefix_abs/client/private + js include = $srcdir_abs/scripting/libjs + name resolve order = bcast + panic action = $srcdir_abs/script/gdb_backtrace \%PID\% \%PROG\% + max xmit = 32K + notify:inotify = false + ldb:nosync = true + system:anonymous = true + torture:basedir = $prefix_abs/client +#We don't want to pass our self-tests if the PAC code is wrong + gensec:require_pac = true + modules dir = $ENV{LD_SAMBA_MODULE_PATH} +"; + close(CF); +} + +my @torture_options = (); +push (@torture_options, "--configfile=$conffile"); +# ensure any one smbtorture call doesn't run too long +push (@torture_options, "--maximum-runtime=$torture_maxtime"); +push (@torture_options, "--target=$opt_target"); +push (@torture_options, "--basedir=$prefix_abs"); +push (@torture_options, "--option=torture:progress=no") unless ($opt_verbose); +push (@torture_options, "--format=subunit"); +push (@torture_options, "--option=torture:quick=yes") if ($opt_quick); + +$ENV{TORTURE_OPTIONS} = join(' ', @torture_options); +print "OPTIONS $ENV{TORTURE_OPTIONS}\n"; + +my @todo = (); + +my $testsdir = "$srcdir/selftest"; +$ENV{SMB_CONF_PATH} = "$conffile"; +$ENV{CONFIGURATION} = "--configfile=$conffile"; + +my %required_envs = (); + +sub read_testlist($) +{ + my ($filename) = @_; + + my @ret = (); + open(IN, $filename) or die("Unable to open $filename: $!"); + + while (<IN>) { + if ($_ eq "-- TEST --\n") { + my $name = <IN>; + $name =~ s/\n//g; + my $env = <IN>; + $env =~ s/\n//g; + my $cmdline = <IN>; + $cmdline =~ s/\n//g; + if (not defined($tests) or $name =~ /$tests/) { + $required_envs{$env} = 1; + push (@ret, [$name, $env, $cmdline]); + } + } else { + print; + } + } + close(IN) or die("Error creating recipe"); + return @ret; +} + +if ($#testlists == -1) { + die("No testlists specified"); +} + +my @available = (); +foreach my $fn (@testlists) { + foreach (read_testlist($fn)) { + my $name = $$_[0]; + next if (@includes and not find_in_list(\@includes, $name)); + push (@available, $_); + } +} + +my $msg_ops; +if ($opt_format eq "buildfarm") { + require output::buildfarm; + $msg_ops = new output::buildfarm($statistics); +} elsif ($opt_format eq "plain") { + require output::plain; + $msg_ops = new output::plain("$prefix/summary", $opt_verbose, $opt_immediate, $statistics, $#available+1); +} elsif ($opt_format eq "html") { + require output::html; + mkdir("test-results", 0777); + $msg_ops = new output::html("test-results", $statistics); +} else { + die("Invalid output format '$opt_format'"); +} + + +foreach (@available) { + my $name = $$_[0]; + my $skipreason = skip($name); + if ($skipreason) { + $msg_ops->skip_testsuite($name, $skipreason); + } else { + push(@todo, $_); + } +} + +if ($#todo == -1) { + print STDERR "No tests to run\n"; + exit(1); + } + +my $suitestotal = $#todo + 1; +my $i = 0; +$| = 1; + +my %running_envs = (); + +my @exported_envvars = ( + # domain stuff + "DOMAIN", + "REALM", + + # domain controller stuff + "DC_SERVER", + "DC_SERVER_IP", + "DC_NETBIOSNAME", + "DC_NETBIOSALIAS", + + # server stuff + "SERVER", + "SERVER_IP", + "NETBIOSNAME", + "NETBIOSALIAS", + + # user stuff + "USERNAME", + "PASSWORD", + "DC_USERNAME", + "DC_PASSWORD", + + # misc stuff + "KRB5_CONFIG", + "WINBINDD_SOCKET_DIR", + "WINBINDD_PRIV_PIPE_DIR" +); + +$SIG{INT} = $SIG{QUIT} = $SIG{TERM} = sub { + my $signame = shift; + teardown_env($_) foreach(keys %running_envs); + die("Received signal $signame"); +}; + +sub setup_env($) +{ + my ($envname) = @_; + + my $testenv_vars; + if ($envname eq "none") { + $testenv_vars = {}; + } elsif (defined($running_envs{$envname})) { + $testenv_vars = $running_envs{$envname}; + if (not $target->check_env($testenv_vars)) { + $testenv_vars = undef; + } + } else { + $testenv_vars = $target->setup_env($envname, $prefix); + } + + return undef unless defined($testenv_vars); + + $running_envs{$envname} = $testenv_vars; + + SocketWrapper::set_default_iface(6); + write_clientconf($conffile, $testenv_vars); + + foreach (@exported_envvars) { + if (defined($testenv_vars->{$_})) { + $ENV{$_} = $testenv_vars->{$_}; + } else { + delete $ENV{$_}; + } + } + + return $testenv_vars; +} + +sub exported_envvars_str($) +{ + my ($testenv_vars) = @_; + my $out = ""; + + foreach (@exported_envvars) { + next unless defined($testenv_vars->{$_}); + $out .= $_."=".$testenv_vars->{$_}."\n"; + } + + return $out; +} + +sub getlog_env($) +{ + my ($envname) = @_; + return "" if ($envname eq "none"); + return $target->getlog_env($running_envs{$envname}); +} + +sub check_env($) +{ + my ($envname) = @_; + return 1 if ($envname eq "none"); + return $target->check_env($running_envs{$envname}); +} + +sub teardown_env($) +{ + my ($envname) = @_; + return if ($envname eq "none"); + $target->teardown_env($running_envs{$envname}); + delete $running_envs{$envname}; +} + +if ($opt_no_lazy_setup) { + setup_env($_) foreach (keys %required_envs); +} + +if ($opt_testenv) { + my $testenv_name = $ENV{SELFTEST_TESTENV}; + $testenv_name = $testenv_default unless defined($testenv_name); + + my $testenv_vars = setup_env($testenv_name); + + $ENV{PIDDIR} = $testenv_vars->{PIDDIR}; + + my $envvarstr = exported_envvars_str($testenv_vars); + + my $term = ($ENV{TERM} or "xterm"); + system("$term -e 'echo -e \" +Welcome to the Samba4 Test environment '$testenv_name' + +This matches the client environment used in make test +smbd is pid `cat \$PIDDIR/smbd.pid` + +Some useful environment variables: +TORTURE_OPTIONS=\$TORTURE_OPTIONS +CONFIGURATION=\$CONFIGURATION + +$envvarstr +\" && LD_LIBRARY_PATH=$ENV{LD_LIBRARY_PATH} bash'"); + teardown_env($testenv_name); +} else { + foreach (@todo) { + $i++; + my $cmd = $$_[2]; + $cmd =~ s/([\(\)])/\\$1/g; + my $name = $$_[0]; + my $envname = $$_[1]; + + my $envvars = setup_env($envname); + if (not defined($envvars)) { + $msg_ops->skip_testsuite($name, "unable to set up environment $envname"); + next; + } + + run_testsuite($envname, $name, $cmd, $i, $suitestotal, + $msg_ops); + + if (defined($opt_analyse_cmd)) { + system("$opt_analyse_cmd \"$name\""); + } + + teardown_env($envname) if ($opt_resetup_env); + } +} + +print "\n"; + +teardown_env($_) foreach (keys %running_envs); + +$target->stop(); + +$msg_ops->summary(); + +my $failed = 0; + +# if there were any valgrind failures, show them +foreach (<$prefix/valgrind.log*>) { + next unless (-s $_); + system("grep DWARF2.CFI.reader $_ > /dev/null"); + if ($? >> 8 == 0) { + print "VALGRIND FAILURE\n"; + $failed++; + system("cat $_"); + } +} + +if ($opt_format eq "buildfarm") { + print "TEST STATUS: $statistics->{SUITES_FAIL}\n"; +} + +exit $statistics->{SUITES_FAIL}; diff --git a/source4/selftest/target/Kvm.pm b/source4/selftest/target/Kvm.pm new file mode 100644 index 0000000000..3b17a2909c --- /dev/null +++ b/source4/selftest/target/Kvm.pm @@ -0,0 +1,167 @@ +#!/usr/bin/perl +# Start a KVM machine and run a number of tests against it. +# Copyright (C) 2005-2008 Jelmer Vernooij <jelmer@samba.org> +# Published under the GNU GPL, v3 or later. + +package Kvm; + +use strict; +use Cwd qw(abs_path); +use FindBin qw($RealBin); +use POSIX; + +sub new($$$$) { + my ($classname, $dc_image, $vdesocket) = @_; + my $self = { + dc_image => $dc_image, + vdesocket => $vdesocket, + }; + bless $self; + return $self; +} + +sub write_kvm_ifup($$$) +{ + my ($self, $path, $ip_prefix) = @_; + open(SCRIPT, ">$path/kvm-ifup"); + + print SCRIPT <<__EOF__; +#!/bin/sh + +PREFIX=$ip_prefix + +/sbin/ifconfig \$1 \$PREFIX.1 up + +cat <<EOF>$path/udhcpd.conf +interface \$1 +start \$PREFIX.20 +end \$PREFIX.20 +max_leases 1 +lease_file $path/udhcpd.leases +pidfile $path/udhcpd.pid +EOF + +touch $path/udhcpd.leases + +/usr/sbin/udhcpd $path/udhcpd.conf + +exit 0 +__EOF__ + close(SCRIPT); + chmod(0755, "$path/kvm-ifup"); + + return ("$path/kvm-ifup", "$path/udhcpd.pid", "$ip_prefix.20"); +} + +sub teardown_env($$) +{ + my ($self, $envvars) = @_; + + print "Killing kvm instance $envvars->{KVM_PID}\n"; + + kill 9, $envvars->{KVM_PID}; + + if (defined($envvars->{DHCPD_PID})) { + print "Killing dhcpd instance $envvars->{DHCPD_PID}\n"; + kill 9, $envvars->{DHCPD_PID}; + } + + return 0; +} + +sub getlog_env($$) +{ + my ($self, $envvars) = @_; + + return ""; +} + +sub check_env($$) +{ + my ($self, $envvars) = @_; + + # FIXME: Check whether $self->{pid} is still running + + return 1; +} + +sub read_pidfile($) +{ + my ($path) = @_; + + open(PID, $path); + <PID> =~ /([0-9]+)/; + my ($pid) = $1; + close(PID); + return $pid; +} + +sub start($$$) +{ + my ($self, $path, $image) = @_; + + my $pidfile = "$path/kvm.pid"; + + my $opts = (defined($ENV{KVM_OPTIONS})?$ENV{KVM_OPTIONS}:"-nographic"); + + if (defined($ENV{KVM_SNAPSHOT})) { + $opts .= " -loadvm $ENV{KVM_SNAPSHOT}"; + } + + my $netopts; + my $dhcp_pid; + my $ip_address; + + if ($self->{vdesocket}) { + $netopts = "vde,socket=$self->{vdesocket}"; + } else { + my $ifup_script, $dhcpd_pidfile; + ($ifup_script, $dhcpd_pidfile, $ip_address) = $self->write_kvm_ifup($path, "192.168.9"); + $netopts = "tap,script=$ifup_script"; + $dhcp_pid = read_pidfile($dhcpd_pidfile); + } + + system("kvm -name \"Samba 4 Test Subject\" $opts -monitor unix:$path/kvm.monitor,server,nowait -daemonize -pidfile $pidfile -snapshot $image -net nic -net $netopts"); + + return (read_pidfile($pidfile), $dhcp_pid, $ip_address); +} + +sub setup_env($$$) +{ + my ($self, $envname, $path) = @_; + + if ($envname eq "dc") { + ($self->{dc_pid}, $self->{dc_dhcpd_pid}, $self->{dc_ip}) = $self->start($path, $self->{dc_image}); + + sub choose_var($$) { + my ($name, $default) = @_; + return defined($ENV{"KVM_DC_$name"})?$ENV{"KVM_DC_$name"}:$default; + } + + if ($envname eq "dc") { + return { + KVM_PID => $self->{dc_pid}, + DHCPD_PID => $self->{dc_dhcpd_pid}, + USERNAME => choose_var("USERNAME", "Administrator"), + PASSWORD => choose_var("PASSWORD", "penguin"), + DOMAIN => choose_var("DOMAIN", "SAMBA"), + REALM => choose_var("REALM", "SAMBA"), + SERVER => choose_var("SERVER", "DC"), + SERVER_IP => $self->{dc_ip}, + NETBIOSNAME => choose_var("NETBIOSNAME", "DC"), + NETBIOSALIAS => choose_var("NETBIOSALIAS", "DC"), + }; + } else { + return undef; + } + } else { + return undef; + } +} + +sub stop($) +{ + my ($self) = @_; +} + +1; diff --git a/source4/selftest/target/Samba3.pm b/source4/selftest/target/Samba3.pm new file mode 100644 index 0000000000..b0c4eb22bd --- /dev/null +++ b/source4/selftest/target/Samba3.pm @@ -0,0 +1,434 @@ +#!/usr/bin/perl +# Bootstrap Samba and run a number of tests against it. +# Copyright (C) 2005-2007 Jelmer Vernooij <jelmer@samba.org> +# Published under the GNU GPL, v3 or later. + +package Samba3; + +use strict; +use Cwd qw(abs_path); +use FindBin qw($RealBin); +use POSIX; + +sub binpath($$) +{ + my ($self, $binary) = @_; + + if (defined($self->{bindir})) { + my $path = "$self->{bindir}/$binary"; + -f $path or die("File $path doesn't exist"); + return $path; + } + + return $binary; +} + +sub new($$) { + my ($classname, $bindir) = @_; + my $self = { bindir => $bindir }; + bless $self; + return $self; +} + +sub teardown_env($$) +{ + my ($self, $envvars) = @_; + + my $smbdpid = read_pid($envvars, "smbd"); + my $nmbdpid = read_pid($envvars, "nmbd"); +# my $winbinddpid = read_pid($envvars, "winbindd"); + + $self->stop_sig_term($smbdpid); + $self->stop_sig_term($nmbdpid); +# $self->stop_sig_term($winbinddpid); + $self->stop_sig_kill($smbdpid); + $self->stop_sig_kill($nmbdpid); +# $self->stop_sig_kill($winbinddpid); + + return 0; +} + +sub getlog_env_app($$$) +{ + my ($self, $envvars, $name) = @_; + + my $title = "$name LOG of: $envvars->{NETBIOSNAME}\n"; + my $out = $title; + + open(LOG, "<".$envvars->{$name."_TEST_LOG"}); + + seek(LOG, $envvars->{$name."_TEST_LOG_POS"}, SEEK_SET); + while (<LOG>) { + $out .= $_; + } + $envvars->{$name."_TEST_LOG_POS"} = tell(LOG); + close(LOG); + + return "" if $out eq $title; + + return $out; +} + +sub getlog_env($$) +{ + my ($self, $envvars) = @_; + my $ret = ""; + + $ret .= $self->getlog_env_app($envvars, "SMBD"); + $ret .= $self->getlog_env_app($envvars, "NMBD"); +# $ret .= $self->getlog_env_app($envvars, "WINBINDD"); + + return $ret; +} + +sub check_env($$) +{ + my ($self, $envvars) = @_; + + # TODO ... + return 1; +} + +sub setup_env($$$) +{ + my ($self, $envname, $path) = @_; + + if ($envname eq "dc") { + return $self->setup_dc("$path/dc"); + } else { + return undef; + } +} + +sub setup_dc($$) +{ + my ($self, $path) = @_; + + my $vars = $self->provision($path, "dc"); + + $self->check_or_start($vars, + ($ENV{NMBD_MAXTIME} or 2700), + ($ENV{WINBINDD_MAXTIME} or 2700), + ($ENV{SMBD_MAXTIME} or 2700)); + + $self->wait_for_start($vars); + + return $vars; +} + +sub stop($) +{ + my ($self) = @_; +} + +sub stop_sig_term($$) { + my ($self, $pid) = @_; + kill("USR1", $pid) or kill("ALRM", $pid) or warn("Unable to kill $pid: $!"); +} + +sub stop_sig_kill($$) { + my ($self, $pid) = @_; + kill("ALRM", $pid) or warn("Unable to kill $pid: $!"); +} + +sub write_pid($$$) +{ + my ($env_vars, $app, $pid) = @_; + + open(PID, ">$env_vars->{PIDDIR}/timelimit.$app.pid"); + print PID $pid; + close(PID); +} + +sub read_pid($$) +{ + my ($env_vars, $app) = @_; + + open(PID, "<$env_vars->{PIDDIR}/timelimit.$app.pid"); + my $pid = <PID>; + close(PID); + return $pid; +} + +sub check_or_start($$$$) { + my ($self, $env_vars, $nmbd_maxtime, $winbindd_maxtime, $smbd_maxtime) = @_; + + unlink($env_vars->{NMBD_TEST_LOG}); + print "STARTING NMBD..."; + my $pid = fork(); + if ($pid == 0) { + open STDOUT, ">$env_vars->{NMBD_TEST_LOG}"; + open STDERR, '>&STDOUT'; + + $ENV{WINBINDD_SOCKET_DIR} = $env_vars->{WINBINDD_SOCKET_DIR}; + + my @optargs = ("-d0"); + if (defined($ENV{NMBD_OPTIONS})) { + @optargs = split(/ /, $ENV{NMBD_OPTIONS}); + } + + $ENV{MAKE_TEST_BINARY} = $self->binpath("nmbd"); + + my @preargs = ($self->binpath("timelimit"), $nmbd_maxtime); + if(defined($ENV{NMBD_VALGRIND})) { + @preargs = split(/ /, $ENV{NMBD_VALGRIND}); + } + + exec(@preargs, $self->binpath("nmbd"), "-F", "-S", "--no-process-group", "-s", $env_vars->{SERVERCONFFILE}, @optargs) or die("Unable to start nmbd: $!"); + } + write_pid($env_vars, "nmbd", $pid); + print "DONE\n"; + +# disable winbindd until the build-farm faked_users work with it +# unlink($env_vars->{WINBINDD_TEST_LOG}); +# print "STARTING WINBINDD..."; +# $pid = fork(); +# if ($pid == 0) { +# open STDOUT, ">$env_vars->{WINBINDD_TEST_LOG}"; +# open STDERR, '>&STDOUT'; +# +# $ENV{WINBINDD_SOCKET_DIR} = $env_vars->{WINBINDD_SOCKET_DIR}; +# +# my @optargs = ("-d0"); +# if (defined($ENV{WINBINDD_OPTIONS})) { +# @optargs = split(/ /, $ENV{WINBINDD_OPTIONS}); +# } +# +# $ENV{MAKE_TEST_BINARY} = $self->binpath("winbindd"); +# exec($self->binpath("timelimit"), $winbindd_maxtime, $ENV{WINBINDD_VALGRIND}, $self->binpath("winbindd"), "-F", "-S", "--no-process-group", "-s", $env_vars->{SERVERCONFFILE}, @optargs) or die("Unable to start winbindd: $!"); +# } +# write_pid($env_vars, "winbindd", $pid); +# print "DONE\n"; + + unlink($env_vars->{SMBD_TEST_LOG}); + print "STARTING SMBD..."; + $pid = fork(); + if ($pid == 0) { + open STDOUT, ">$env_vars->{SMBD_TEST_LOG}"; + open STDERR, '>&STDOUT'; + + $ENV{WINBINDD_SOCKET_DIR} = $env_vars->{WINBINDD_SOCKET_DIR}; + + $ENV{MAKE_TEST_BINARY} = $self->binpath("smbd"); + my @optargs = ("-d0"); + if (defined($ENV{SMBD_OPTIONS})) { + @optargs = split(/ /, $ENV{SMBD_OPTIONS}); + } + my @preargs = ($self->binpath("timelimit"), $smbd_maxtime); + if(defined($ENV{SMBD_VALGRIND})) { + @preargs = split(/ /,$ENV{SMBD_VALGRIND}); + } + exec(@preargs, $self->binpath("smbd"), "-F", "-S", "--no-process-group", "-s", $env_vars->{SERVERCONFFILE}, @optargs) or die("Unable to start smbd: $!"); + } + write_pid($env_vars, "smbd", $pid); + print "DONE\n"; + + return 0; +} + +sub create_clientconf($$$) +{ + my ($self, $prefix, $domain) = @_; + + my $lockdir = "$prefix/locks"; + my $logdir = "$prefix/logs"; + my $piddir = "$prefix/pid"; + my $privatedir = "$prefix/private"; + my $scriptdir = "$RealBin/.."; + my $conffile = "$prefix/smb.conf"; + + 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'; + open(CONF, ">$conffile"); + print CONF " +[global] + workgroup = $domain + + private dir = $privatedir + pid directory = $piddir + lock directory = $lockdir + log file = $logdir/log.\%m + log level = 0 + + name resolve order = bcast + + netbios name = TORTURE_6 + interfaces = $torture_interfaces + panic action = $scriptdir/gdb_backtrace \%d %\$(MAKE_TEST_BINARY) + + passdb backend = tdbsam + "; + close(CONF); +} + +sub provision($$$) +{ + my ($self, $prefix, $role) = @_; + + ## + ## setup the various environment variables we need + ## + + my %ret = (); + my $server = "LOCALHOST2"; + my $server_ip = "127.0.0.2"; + my $domain = "SAMBA-TEST"; + + my $username = `PATH=/usr/ucb:$ENV{PATH} whoami`; + chomp $username; + my $password = "test"; + + my $srcdir="$RealBin/.."; + my $scriptdir="$srcdir/selftest"; + my $prefix_abs = abs_path($prefix); + + my @dirs = (); + + my $shrdir="$prefix_abs/share"; + push(@dirs,$shrdir); + + my $libdir="$prefix_abs/lib"; + push(@dirs,$libdir); + + my $piddir="$prefix_abs/pid"; + push(@dirs,$piddir); + + my $privatedir="$prefix_abs/private"; + push(@dirs,$privatedir); + + my $lockdir="$prefix_abs/lockdir"; + push(@dirs,$lockdir); + + my $logdir="$prefix_abs/logs"; + push(@dirs,$logdir); + + # this gets autocreated by winbindd + my $wbsockdir="$prefix_abs/winbindd"; + my $wbsockprivdir="$lockdir/winbindd_privileged"; + + ## + ## create the test directory layout + ## + mkdir($prefix_abs, 0777); + print "CREATE TEST ENVIRONMENT IN '$prefix'..."; + system("rm -rf $prefix_abs/*"); + mkdir($_, 0777) foreach(@dirs); + + my $conffile="$libdir/server.conf"; + + open(CONF, ">$conffile") or die("Unable to open $conffile"); + print CONF " +[global] + workgroup = $domain + + private dir = $privatedir + pid directory = $piddir + lock directory = $lockdir + log file = $logdir/log.\%m + log level = 0 + + name resolve order = bcast + + netbios name = $server + interfaces = $server_ip/8 + bind interfaces only = yes + panic action = $scriptdir/gdb_backtrace %d %\$(MAKE_TEST_BINARY) + + passdb backend = tdbsam + + ; Necessary to add the build farm hacks + add user script = /bin/false + add machine script = /bin/false + + kernel oplocks = no + kernel change notify = no + + syslog = no + printing = bsd + printcap name = /dev/null + +"; + + if ($role eq "dc") { + print CONF "\tdomain logons = yes\n"; + print CONF "\tdomain master = yes\n"; + } + +print CONF " + + winbindd:socket dir = $wbsockdir + +[tmp] + path = $shrdir + read only = no + smbd:sharedelay = 100000 + map hidden = yes + map system = yes + create mask = 755 +[hideunread] + copy = tmp + hide unreadable = yes +[hideunwrite] + copy = tmp + hide unwriteable files = yes +[print1] + copy = tmp + printable = yes + printing = test +[print2] + copy = print1 +[print3] + copy = print1 +[print4] + copy = print1 + "; + close(CONF); + + ## + ## create a test account + ## + + open(PWD, "|".$self->binpath("smbpasswd")." -c $conffile -L -s -a $username"); + print PWD "$password\n$password\n"; + close(PWD) or die("Unable to set password for test account"); + + print "DONE\n"; + + $ret{SERVER_IP} = $server_ip; + $ret{NMBD_TEST_LOG} = "$prefix/nmbd_test.log"; + $ret{WINBINDD_TEST_LOG} = "$prefix/winbindd_test.log"; + $ret{SMBD_TEST_LOG} = "$prefix/smbd_test.log"; + $ret{SERVERCONFFILE} = $conffile; + $ret{CONFIGURATION} ="-s $conffile"; + $ret{SERVER} = $server; + $ret{USERNAME} = $username; + $ret{DOMAIN} = $domain; + $ret{NETBIOSNAME} = $server; + $ret{PASSWORD} = $password; + $ret{PIDDIR} = $piddir; + $ret{WINBINDD_SOCKET_DIR} = $wbsockdir; + $ret{WINBINDD_PRIV_PIPE_DIR} = $wbsockprivdir; + return \%ret; +} + +sub wait_for_start($$) +{ + my ($self, $envvars) = @_; + + # give time for nbt server to register its names + print "delaying for nbt name registration\n"; + sleep(10); + # This will return quickly when things are up, but be slow if we need to wait for (eg) SSL init + system($self->binpath("nmblookup") ." $envvars->{CONFIGURATION} -U $envvars->{SERVER_IP} __SAMBA__"); + system($self->binpath("nmblookup") ." $envvars->{CONFIGURATION} __SAMBA__"); + system($self->binpath("nmblookup") ." $envvars->{CONFIGURATION} -U 127.255.255.255 __SAMBA__"); + system($self->binpath("nmblookup") ." $envvars->{CONFIGURATION} -U $envvars->{SERVER_IP} $envvars->{SERVER}"); + system($self->binpath("nmblookup") ." $envvars->{CONFIGURATION} $envvars->{SERVER}"); + # make sure smbd is also up set + print "wait for smbd\n"; + system($self->binpath("smbclient") ." $envvars->{CONFIGURATION} -L $envvars->{SERVER_IP} -U% -p 139 | head -2"); + system($self->binpath("smbclient") ." $envvars->{CONFIGURATION} -L $envvars->{SERVER_IP} -U% -p 139 | head -2"); + + print $self->getlog_env($envvars); +} + +1; diff --git a/source4/selftest/target/Samba4.pm b/source4/selftest/target/Samba4.pm new file mode 100644 index 0000000000..8835f69c6c --- /dev/null +++ b/source4/selftest/target/Samba4.pm @@ -0,0 +1,957 @@ +#!/usr/bin/perl +# Bootstrap Samba and run a number of tests against it. +# Copyright (C) 2005-2007 Jelmer Vernooij <jelmer@samba.org> +# Published under the GNU GPL, v3 or later. + +package Samba4; + +use strict; +use Cwd qw(abs_path); +use FindBin qw($RealBin); +use POSIX; + +sub new($$$$) { + my ($classname, $bindir, $ldap, $setupdir) = @_; + my $self = { + vars => {}, + ldap => $ldap, + bindir => $bindir, + setupdir => $setupdir + }; + bless $self; + return $self; +} + +sub openldap_start($$$) { + my ($slapd_conf, $uri, $logs) = @_; + my $oldpath = $ENV{PATH}; + my $olroot = ""; + my $olpath = ""; + if (defined $ENV{OPENLDAP_ROOT}) { + $olroot = "$ENV{OPENLDAP_ROOT}"; + $olpath = "$olroot/libexec:$olroot/sbin:"; + } + $ENV{PATH} = "$olpath/usr/local/sbin:/usr/sbin:/sbin:$ENV{PATH}"; + system("slapd -d63 -f $slapd_conf -h $uri > $logs 2>&1 &"); + $ENV{PATH} = $oldpath; +} + +sub slapd_start($$) +{ + my $count = 0; + my ($self, $env_vars) = @_; + + my $uri = $env_vars->{LDAP_URI}; + + # running slapd in the background means it stays in the same process group, so it can be + # killed by timelimit + if ($self->{ldap} eq "fedora-ds") { + system("$ENV{FEDORA_DS_ROOT}/sbin/ns-slapd -D $env_vars->{FEDORA_DS_DIR} -d0 -i $env_vars->{FEDORA_DS_PIDFILE}> $env_vars->{LDAPDIR}/logs 2>&1 &"); + } elsif ($self->{ldap} eq "openldap") { + openldap_start($env_vars->{SLAPD_CONF}, $uri, "$env_vars->{LDAPDIR}/logs"); + } + while (system("$self->{bindir}/ldbsearch -H $uri -s base -b \"\" supportedLDAPVersion > /dev/null") != 0) { + $count++; + if ($count > 40) { + $self->slapd_stop($env_vars); + return 0; + } + sleep(1); + } + return 1; +} + +sub slapd_stop($$) +{ + my ($self, $envvars) = @_; + if ($self->{ldap} eq "fedora-ds") { + system("$envvars->{LDAPDIR}/slapd-samba4/stop-slapd"); + } elsif ($self->{ldap} eq "openldap") { + open(IN, "<$envvars->{OPENLDAP_PIDFILE}") or + die("unable to open slapd pid file: $envvars->{OPENLDAP_PIDFILE}"); + kill 9, <IN>; + close(IN); + } + return 1; +} + +sub check_or_start($$$) +{ + my ($self, $env_vars, $max_time) = @_; + return 0 if ( -p $env_vars->{SMBD_TEST_FIFO}); + + unlink($env_vars->{SMBD_TEST_FIFO}); + POSIX::mkfifo($env_vars->{SMBD_TEST_FIFO}, 0700); + unlink($env_vars->{SMBD_TEST_LOG}); + + print "STARTING SMBD... "; + my $pid = fork(); + if ($pid == 0) { + open STDIN, $env_vars->{SMBD_TEST_FIFO}; + open STDOUT, ">$env_vars->{SMBD_TEST_LOG}"; + open STDERR, '>&STDOUT'; + + SocketWrapper::set_default_iface($env_vars->{SOCKET_WRAPPER_DEFAULT_IFACE}); + + my $valgrind = ""; + if (defined($ENV{SMBD_VALGRIND})) { + $valgrind = $ENV{SMBD_VALGRIND}; + } + + $ENV{KRB5_CONFIG} = $env_vars->{KRB5_CONFIG}; + + $ENV{NSS_WRAPPER_PASSWD} = $env_vars->{NSS_WRAPPER_PASSWD}; + $ENV{NSS_WRAPPER_GROUP} = $env_vars->{NSS_WRAPPER_GROUP}; + + # Start slapd before smbd, but with the fifo on stdin + if (defined($self->{ldap})) { + $self->slapd_start($env_vars) or + die("couldn't start slapd (2nd time)"); + } + + my $optarg = ""; + if (defined($max_time)) { + $optarg = "--maximum-runtime=$max_time "; + } + if (defined($ENV{SMBD_OPTIONS})) { + $optarg.= " $ENV{SMBD_OPTIONS}"; + } + my $ret = system("$valgrind $self->{bindir}/smbd $optarg $env_vars->{CONFIGURATION} -M single -i --leak-report-full"); + if ($? == -1) { + print "Unable to start smbd: $ret: $!\n"; + exit 1; + } + unlink($env_vars->{SMBD_TEST_FIFO}); + my $exit = $? >> 8; + if ( $ret == 0 ) { + print "smbd exits with status $exit\n"; + } elsif ( $ret & 127 ) { + print "smbd got signal ".($ret & 127)." and exits with $exit!\n"; + } else { + $ret = $? >> 8; + print "smbd failed with status $exit!\n"; + } + exit $exit; + } + print "DONE\n"; + + open(DATA, ">$env_vars->{SMBD_TEST_FIFO}"); + + return $pid; +} + +sub wait_for_start($$) +{ + my ($self, $testenv_vars) = @_; + # give time for nbt server to register its names + print "delaying for nbt name registration\n"; + sleep 2; + + # This will return quickly when things are up, but be slow if we + # need to wait for (eg) SSL init + system("bin/nmblookup $testenv_vars->{CONFIGURATION} $testenv_vars->{SERVER}"); + system("bin/nmblookup $testenv_vars->{CONFIGURATION} -U $testenv_vars->{SERVER_IP} $testenv_vars->{SERVER}"); + system("bin/nmblookup $testenv_vars->{CONFIGURATION} $testenv_vars->{NETBIOSNAME}"); + system("bin/nmblookup $testenv_vars->{CONFIGURATION} -U $testenv_vars->{SERVER_IP} $testenv_vars->{NETBIOSNAME}"); + system("bin/nmblookup $testenv_vars->{CONFIGURATION} $testenv_vars->{NETBIOSALIAS}"); + system("bin/nmblookup $testenv_vars->{CONFIGURATION} -U $testenv_vars->{SERVER_IP} $testenv_vars->{NETBIOSALIAS}"); + system("bin/nmblookup $testenv_vars->{CONFIGURATION} $testenv_vars->{SERVER}"); + system("bin/nmblookup $testenv_vars->{CONFIGURATION} -U $testenv_vars->{SERVER_IP} $testenv_vars->{SERVER}"); + system("bin/nmblookup $testenv_vars->{CONFIGURATION} $testenv_vars->{NETBIOSNAME}"); + system("bin/nmblookup $testenv_vars->{CONFIGURATION} -U $testenv_vars->{SERVER_IP} $testenv_vars->{NETBIOSNAME}"); + system("bin/nmblookup $testenv_vars->{CONFIGURATION} $testenv_vars->{NETBIOSALIAS}"); + system("bin/nmblookup $testenv_vars->{CONFIGURATION} -U $testenv_vars->{SERVER_IP} $testenv_vars->{NETBIOSALIAS}"); + + print $self->getlog_env($testenv_vars); +} + +sub write_ldb_file($$$) +{ + my ($self, $file, $ldif) = @_; + + open(LDIF, "|$self->{bindir}/ldbadd -H $file >/dev/null"); + print LDIF $ldif; + return close(LDIF); +} + +sub add_wins_config($$) +{ + my ($self, $privatedir) = @_; + + return $self->write_ldb_file("$privatedir/wins_config.ldb", " +dn: name=TORTURE_6,CN=PARTNERS +objectClass: wreplPartner +name: TORTURE_6 +address: 127.0.0.6 +pullInterval: 0 +pushChangeCount: 0 +type: 0x3 +"); +} + +sub mk_fedora_ds($$$) +{ + my ($self, $ldapdir, $configuration) = @_; + + my $fedora_ds_inf = "$ldapdir/fedorads.inf"; + my $fedora_ds_extra_ldif = "$ldapdir/fedorads-partitions.ldif"; + + #Make the subdirectory be as fedora DS would expect + my $fedora_ds_dir = "$ldapdir/slapd-samba4"; + + my $pidfile = "$fedora_ds_dir/logs/slapd-samba4.pid"; + +my $dir = getcwd(); +chdir "$ENV{FEDORA_DS_ROOT}/bin" || die; + if (system("perl $ENV{FEDORA_DS_ROOT}/sbin/setup-ds.pl --silent --file=$fedora_ds_inf >&2") != 0) { + chdir $dir; + die("perl $ENV{FEDORA_DS_ROOT}/sbin/setup-ds.pl --silent --file=$fedora_ds_inf FAILED: $?"); + } + chdir $dir || die; + + return ($fedora_ds_dir, $pidfile); +} + +sub mk_openldap($$$) +{ + my ($self, $ldapdir, $configuration) = @_; + + my $slapd_conf = "$ldapdir/slapd.conf"; + my $pidfile = "$ldapdir/slapd.pid"; + my $modconf = "$ldapdir/modules.conf"; + + my $oldpath = $ENV{PATH}; + my $olpath = ""; + my $olroot = ""; + if (defined $ENV{OPENLDAP_ROOT}) { + $olroot = "$ENV{OPENLDAP_ROOT}"; + $olpath = "$olroot/libexec:$olroot/sbin:"; + } + $ENV{PATH} = "$olpath/usr/local/sbin:/usr/sbin:/sbin:$ENV{PATH}"; + + unlink($modconf); + open(CONF, ">$modconf"); close(CONF); + + if (system("slaptest -u -f $slapd_conf >&2") != 0) { + open(CONF, ">$modconf"); + # enable slapd modules + print CONF " +modulepath $olroot/libexec/openldap +moduleload syncprov +moduleload memberof +moduleload refint +"; + close(CONF); + } + if (system("slaptest -u -f $slapd_conf >&2") != 0) { + open(CONF, ">$modconf"); + # enable slapd modules + print CONF " +modulepath $olroot/libexec/openldap +moduleload back_hdb +moduleload syncprov +moduleload memberof +moduleload refint +"; + close(CONF); + } + + if (system("slaptest -u -f $slapd_conf >&2") != 0) { + open(CONF, ">$modconf"); + # enable slapd modules + print CONF " +moduleload back_hdb +moduleload syncprov +moduleload memberof +moduleload refint +"; + close(CONF); + } + + if (system("slaptest -u -f $slapd_conf >&2") != 0) { + open(CONF, ">$modconf"); + # enable slapd modules + print CONF " +modulepath /usr/lib/ldap +moduleload back_hdb +moduleload syncprov +moduleload memberof +moduleload refint +"; + close(CONF); + } + + if (system("slaptest -u -f $slapd_conf >&2") != 0) { + open(CONF, ">$modconf"); + # enable slapd modules (Fedora layout) + print CONF " +modulepath /usr/lib/openldap +moduleload syncprov +moduleload memberof +moduleload refint +"; + close(CONF); + } + + if (system("slaptest -u -f $slapd_conf >&2") != 0) { + open(CONF, ">$modconf"); + # enable slapd modules (Fedora x86_64 layout) + print CONF " +modulepath /usr/lib64/openldap +moduleload syncprov +moduleload memberof +moduleload refint +"; + close(CONF); + } + + system("slaptest -u -f $slapd_conf") == 0 or die("slaptest still fails after adding modules"); + + + $ENV{PATH} = $oldpath; + + return ($slapd_conf, $pidfile); +} + +sub mk_keyblobs($$) +{ + my ($self, $tlsdir) = @_; + + #TLS and PKINIT crypto blobs + my $dhfile = "$tlsdir/dhparms.pem"; + my $cafile = "$tlsdir/ca.pem"; + my $certfile = "$tlsdir/cert.pem"; + my $reqkdc = "$tlsdir/req-kdc.der"; + my $kdccertfile = "$tlsdir/kdc.pem"; + my $keyfile = "$tlsdir/key.pem"; + my $adminkeyfile = "$tlsdir/adminkey.pem"; + my $reqadmin = "$tlsdir/req-admin.der"; + my $admincertfile = "$tlsdir/admincert.pem"; + + mkdir($tlsdir, 0777); + + #This is specified here to avoid draining entropy on every run + open(DHFILE, ">$dhfile"); + print DHFILE <<EOF; +-----BEGIN DH PARAMETERS----- +MGYCYQC/eWD2xkb7uELmqLi+ygPMKyVcpHUo2yCluwnbPutEueuxrG/Cys8j8wLO +svCN/jYNyR2NszOmg7ZWcOC/4z/4pWDVPUZr8qrkhj5MRKJc52MncfaDglvEdJrv +YX70obsCAQI= +-----END DH PARAMETERS----- +EOF + close(DHFILE); + + #Likewise, we pregenerate the key material. This allows the + #other certificates to be pre-generated + open(KEYFILE, ">$keyfile"); + print KEYFILE <<EOF; +-----BEGIN RSA PRIVATE KEY----- +MIICXQIBAAKBgQDKg6pAwCHUMA1DfHDmWhZfd+F0C+9Jxcqvpw9ii9En3E1uflpc +ol3+S9/6I/uaTmJHZre+DF3dTzb/UOZo0Zem8N+IzzkgoGkFafjXuT3BL5UPY2/H +6H+pPqVIRLOmrWImai359YyoKhFyo37Y6HPeU8QcZ+u2rS9geapIWfeuowIDAQAB +AoGAAqDLzFRR/BF1kpsiUfL4WFvTarCe9duhwj7ORc6fs785qAXuwUYAJ0Uvzmy6 +HqoGv3t3RfmeHDmjcpPHsbOKnsOQn2MgmthidQlPBMWtQMff5zdoYNUFiPS0XQBq +szNW4PRjaA9KkLQVTwnzdXGkBSkn/nGxkaVu7OR3vJOBoo0CQQDO4upypesnbe6p +9/xqfZ2uim8IwV1fLlFClV7WlCaER8tsQF4lEi0XSzRdXGUD/dilpY88Nb+xok/X +8Z8OvgAXAkEA+pcLsx1gN7kxnARxv54jdzQjC31uesJgMKQXjJ0h75aUZwTNHmZQ +vPxi6u62YiObrN5oivkixwFNncT9MxTxVQJBAMaWUm2SjlLe10UX4Zdm1MEB6OsC +kVoX37CGKO7YbtBzCfTzJGt5Mwc1DSLA2cYnGJqIfSFShptALlwedot0HikCQAJu +jNKEKnbf+TdGY8Q0SKvTebOW2Aeg80YFkaTvsXCdyXrmdQcifw4WdO9KucJiDhSz +Y9hVapz7ykEJtFtWjLECQQDIlfc63I5ZpXfg4/nN4IJXUW6AmPVOYIA5215itgki +cSlMYli1H9MEXH0pQMGv5Qyd0OYIx2DDg96mZ+aFvqSG +-----END RSA PRIVATE KEY----- +EOF + close(KEYFILE); + + open(ADMINKEYFILE, ">$adminkeyfile"); + + print ADMINKEYFILE <<EOF; +-----BEGIN RSA PRIVATE KEY----- +MIICXQIBAAKBgQD0+OL7TQBj0RejbIH1+g5GeRaWaM9xF43uE5y7jUHEsi5owhZF +5iIoHZeeL6cpDF5y1BZRs0JlA1VqMry1jjKlzFYVEMMFxB6esnXhl0Jpip1JkUMM +XLOP1m/0dqayuHBWozj9f/cdyCJr0wJIX1Z8Pr+EjYRGPn/MF0xdl3JRlwIDAQAB +AoGAP8mjCP628Ebc2eACQzOWjgEvwYCPK4qPmYOf1zJkArzG2t5XAGJ5WGrENRuB +cm3XFh1lpmaADl982UdW3gul4gXUy6w4XjKK4vVfhyHj0kZ/LgaXUK9BAGhroJ2L +osIOUsaC6jdx9EwSRctwdlF3wWJ8NK0g28AkvIk+FlolW4ECQQD7w5ouCDnf58CN +u4nARx4xv5XJXekBvOomkCQAmuOsdOb6b9wn3mm2E3au9fueITjb3soMR31AF6O4 +eAY126rXAkEA+RgHzybzZEP8jCuznMqoN2fq/Vrs6+W3M8/G9mzGEMgLLpaf2Jiz +I9tLZ0+OFk9tkRaoCHPfUOCrVWJZ7Y53QQJBAMhoA6rw0WDyUcyApD5yXg6rusf4 +ASpo/tqDkqUIpoL464Qe1tjFqtBM3gSXuhs9xsz+o0bzATirmJ+WqxrkKTECQHt2 +OLCpKqwAspU7N+w32kaUADoRLisCEdrhWklbwpQgwsIVsCaoEOpt0CLloJRYTANE +yoZeAErTALjyZYZEPcECQQDlUi0N8DFxQ/lOwWyR3Hailft+mPqoPCa8QHlQZnlG ++cfgNl57YHMTZFwgUVFRdJNpjH/WdZ5QxDcIVli0q+Ko +-----END RSA PRIVATE KEY----- +EOF + + #generated with + # hxtool issue-certificate --self-signed --issue-ca \ + # --ca-private-key="FILE:$KEYFILE" \ + # --subject="CN=CA,DC=samba,DC=example,DC=com" \ + # --certificate="FILE:$CAFILE" --lifetime="25 years" + + open(CAFILE, ">$cafile"); + print CAFILE <<EOF; +-----BEGIN CERTIFICATE----- +MIICcTCCAdqgAwIBAgIUaBPmjnPVqyFqR5foICmLmikJTzgwCwYJKoZIhvcNAQEFMFIxEzAR +BgoJkiaJk/IsZAEZDANjb20xFzAVBgoJkiaJk/IsZAEZDAdleGFtcGxlMRUwEwYKCZImiZPy +LGQBGQwFc2FtYmExCzAJBgNVBAMMAkNBMCIYDzIwMDgwMzAxMTIyMzEyWhgPMjAzMzAyMjQx +MjIzMTJaMFIxEzARBgoJkiaJk/IsZAEZDANjb20xFzAVBgoJkiaJk/IsZAEZDAdleGFtcGxl +MRUwEwYKCZImiZPyLGQBGQwFc2FtYmExCzAJBgNVBAMMAkNBMIGfMA0GCSqGSIb3DQEBAQUA +A4GNADCBiQKBgQDKg6pAwCHUMA1DfHDmWhZfd+F0C+9Jxcqvpw9ii9En3E1uflpcol3+S9/6 +I/uaTmJHZre+DF3dTzb/UOZo0Zem8N+IzzkgoGkFafjXuT3BL5UPY2/H6H+pPqVIRLOmrWIm +ai359YyoKhFyo37Y6HPeU8QcZ+u2rS9geapIWfeuowIDAQABo0IwQDAOBgNVHQ8BAf8EBAMC +AaYwHQYDVR0OBBYEFMLZufegDKLZs0VOyFXYK1L6M8oyMA8GA1UdEwEB/wQFMAMBAf8wDQYJ +KoZIhvcNAQEFBQADgYEAAZJbCAAkaqgFJ0xgNovn8Ydd0KswQPjicwiODPgw9ZPoD2HiOUVO +yYDRg/dhFF9y656OpcHk4N7qZ2sl3RlHkzDu+dseETW+CnKvQIoXNyeARRJSsSlwrwcoD4JR +HTLk2sGigsWwrJ2N99sG/cqSJLJ1MFwLrs6koweBnYU0f/g= +-----END CERTIFICATE----- +EOF + + #generated with GNUTLS internally in Samba. + + open(CERTFILE, ">$certfile"); + print CERTFILE <<EOF; +-----BEGIN CERTIFICATE----- +MIICYTCCAcygAwIBAgIE5M7SRDALBgkqhkiG9w0BAQUwZTEdMBsGA1UEChMUU2Ft +YmEgQWRtaW5pc3RyYXRpb24xNDAyBgNVBAsTK1NhbWJhIC0gdGVtcG9yYXJ5IGF1 +dG9nZW5lcmF0ZWQgY2VydGlmaWNhdGUxDjAMBgNVBAMTBVNhbWJhMB4XDTA2MDgw +NDA0MzY1MloXDTA4MDcwNDA0MzY1MlowZTEdMBsGA1UEChMUU2FtYmEgQWRtaW5p +c3RyYXRpb24xNDAyBgNVBAsTK1NhbWJhIC0gdGVtcG9yYXJ5IGF1dG9nZW5lcmF0 +ZWQgY2VydGlmaWNhdGUxDjAMBgNVBAMTBVNhbWJhMIGcMAsGCSqGSIb3DQEBAQOB +jAAwgYgCgYDKg6pAwCHUMA1DfHDmWhZfd+F0C+9Jxcqvpw9ii9En3E1uflpcol3+ +S9/6I/uaTmJHZre+DF3dTzb/UOZo0Zem8N+IzzkgoGkFafjXuT3BL5UPY2/H6H+p +PqVIRLOmrWImai359YyoKhFyo37Y6HPeU8QcZ+u2rS9geapIWfeuowIDAQABoyUw +IzAMBgNVHRMBAf8EAjAAMBMGA1UdJQQMMAoGCCsGAQUFBwMBMAsGCSqGSIb3DQEB +BQOBgQAmkN6XxvDnoMkGcWLCTwzxGfNNSVcYr7TtL2aJh285Xw9zaxcm/SAZBFyG +LYOChvh6hPU7joMdDwGfbiLrBnMag+BtGlmPLWwp/Kt1wNmrRhduyTQFhN3PP6fz +nBr9vVny2FewB2gHmelaPS//tXdxivSXKz3NFqqXLDJjq7P8wA== +-----END CERTIFICATE----- +EOF + close(CERTFILE); + + #KDC certificate + # hxtool request-create \ + # --subject="CN=krbtgt,CN=users,DC=samba,DC=example,DC=com" \ + # --key="FILE:$KEYFILE" $KDCREQ + + # hxtool issue-certificate --ca-certificate=FILE:$CAFILE,$KEYFILE \ + # --type="pkinit-kdc" \ + # --pk-init-principal="krbtgt/SAMBA.EXAMPLE.COM@SAMBA.EXAMPLE.COM" \ + # --req="PKCS10:$KDCREQ" --certificate="FILE:$KDCCERTFILE" \ + # --lifetime="25 years" + + open(KDCCERTFILE, ">$kdccertfile"); + print KDCCERTFILE <<EOF; +-----BEGIN CERTIFICATE----- +MIIDDDCCAnWgAwIBAgIUI2Tzj+JnMzMcdeabcNo30rovzFAwCwYJKoZIhvcNAQEFMFIxEzAR +BgoJkiaJk/IsZAEZDANjb20xFzAVBgoJkiaJk/IsZAEZDAdleGFtcGxlMRUwEwYKCZImiZPy +LGQBGQwFc2FtYmExCzAJBgNVBAMMAkNBMCIYDzIwMDgwMzAxMTMxOTIzWhgPMjAzMzAyMjQx +MzE5MjNaMGYxEzARBgoJkiaJk/IsZAEZDANjb20xFzAVBgoJkiaJk/IsZAEZDAdleGFtcGxl +MRUwEwYKCZImiZPyLGQBGQwFc2FtYmExDjAMBgNVBAMMBXVzZXJzMQ8wDQYDVQQDDAZrcmJ0 +Z3QwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMqDqkDAIdQwDUN8cOZaFl934XQL70nF +yq+nD2KL0SfcTW5+WlyiXf5L3/oj+5pOYkdmt74MXd1PNv9Q5mjRl6bw34jPOSCgaQVp+Ne5 +PcEvlQ9jb8fof6k+pUhEs6atYiZqLfn1jKgqEXKjftjoc95TxBxn67atL2B5qkhZ966jAgMB +AAGjgcgwgcUwDgYDVR0PAQH/BAQDAgWgMBIGA1UdJQQLMAkGBysGAQUCAwUwVAYDVR0RBE0w +S6BJBgYrBgEFAgKgPzA9oBMbEVNBTUJBLkVYQU1QTEUuQ09NoSYwJKADAgEBoR0wGxsGa3Ji +dGd0GxFTQU1CQS5FWEFNUExFLkNPTTAfBgNVHSMEGDAWgBTC2bn3oAyi2bNFTshV2CtS+jPK +MjAdBgNVHQ4EFgQUwtm596AMotmzRU7IVdgrUvozyjIwCQYDVR0TBAIwADANBgkqhkiG9w0B +AQUFAAOBgQBmrVD5MCmZjfHp1nEnHqTIh8r7lSmVtDx4s9MMjxm9oNrzbKXynvdhwQYFVarc +ge4yRRDXtSebErOl71zVJI9CVeQQpwcH+tA85oGA7oeFtO/S7ls581RUU6tGgyxV4veD+lJv +KPH5LevUtgD+q9H4LU4Sq5N3iFwBaeryB0g2wg== +-----END CERTIFICATE----- +EOF + + # hxtool request-create \ + # --subject="CN=Administrator,CN=users,DC=samba,DC=example,DC=com" \ + # --key="FILE:$ADMINKEYFILE" $ADMINREQFILE + + # hxtool issue-certificate --ca-certificate=FILE:$CAFILE,$KEYFILE \ + # --type="pkinit-client" \ + # --pk-init-principal="administrator@SAMBA.EXAMPLE.COM" \ + # --req="PKCS10:$ADMINREQFILE" --certificate="FILE:$ADMINCERTFILE" \ + # --lifetime="25 years" + + open(ADMINCERTFILE, ">$admincertfile"); + print ADMINCERTFILE <<EOF; +-----BEGIN CERTIFICATE----- +MIIDHTCCAoagAwIBAgIUC0W5dW/N9kE+NgD0mKK34YgyqQ0wCwYJKoZIhvcNAQEFMFIxEzAR +BgoJkiaJk/IsZAEZDANjb20xFzAVBgoJkiaJk/IsZAEZDAdleGFtcGxlMRUwEwYKCZImiZPy +LGQBGQwFc2FtYmExCzAJBgNVBAMMAkNBMCIYDzIwMDgwMzAxMTMyMzAwWhgPMjAzMzAyMjQx +MzIzMDBaMG0xEzARBgoJkiaJk/IsZAEZDANjb20xFzAVBgoJkiaJk/IsZAEZDAdleGFtcGxl +MRUwEwYKCZImiZPyLGQBGQwFc2FtYmExDjAMBgNVBAMMBXVzZXJzMRYwFAYDVQQDDA1BZG1p +bmlzdHJhdG9yMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQD0+OL7TQBj0RejbIH1+g5G +eRaWaM9xF43uE5y7jUHEsi5owhZF5iIoHZeeL6cpDF5y1BZRs0JlA1VqMry1jjKlzFYVEMMF +xB6esnXhl0Jpip1JkUMMXLOP1m/0dqayuHBWozj9f/cdyCJr0wJIX1Z8Pr+EjYRGPn/MF0xd +l3JRlwIDAQABo4HSMIHPMA4GA1UdDwEB/wQEAwIFoDAoBgNVHSUEITAfBgcrBgEFAgMEBggr +BgEFBQcDAgYKKwYBBAGCNxQCAjBIBgNVHREEQTA/oD0GBisGAQUCAqAzMDGgExsRU0FNQkEu +RVhBTVBMRS5DT02hGjAYoAMCAQGhETAPGw1hZG1pbmlzdHJhdG9yMB8GA1UdIwQYMBaAFMLZ +ufegDKLZs0VOyFXYK1L6M8oyMB0GA1UdDgQWBBQg81bLyfCA88C2B/BDjXlGuaFaxjAJBgNV +HRMEAjAAMA0GCSqGSIb3DQEBBQUAA4GBAHsqSqul0hZCXn4t8Kfp3v/JLMiUMJihR1XOgzoa +ufLOQ1HNzFUHKuo1JEQ1+i5gHT/arLu/ZBF4BfQol7vW27gKIEt0fkRV8EvoPxXvSokHq0Ku +HCuPOhYNEP3wYiwB3g93NMCinWVlz0mh5aijEU7y/XrjlZxBKFFrTE+BJi1o +-----END CERTIFICATE----- +EOF + close(ADMINCERTFILE); +} + +sub provision($$$$$$) +{ + my ($self, $prefix, $server_role, $netbiosname, $netbiosalias, $swiface, $password) = @_; + + my $smbd_loglevel = 1; + my $username = "administrator"; + my $domain = "SAMBADOMAIN"; + my $realm = "SAMBA.EXAMPLE.COM"; + my $dnsname = "samba.example.com"; + my $basedn = "dc=samba,dc=example,dc=com"; + my $unix_name = ($ENV{USER} or $ENV{LOGNAME} or `whoami`); + chomp $unix_name; + my $unix_uid = $>; + my $unix_gids_str = $); + my @unix_gids = split(" ", $unix_gids_str); + my $srcdir="$RealBin/.."; + -d $prefix or mkdir($prefix, 0777) or die("Unable to create $prefix"); + my $prefix_abs = abs_path($prefix); + my $tmpdir = "$prefix_abs/tmp"; + my $etcdir = "$prefix_abs/etc"; + my $piddir = "$prefix_abs/pid"; + my $conffile = "$etcdir/smb.conf"; + my $krb5_config = "$etcdir/krb5.conf"; + my $privatedir = "$prefix_abs/private"; + my $ncalrpcdir = "$prefix_abs/ncalrpc"; + my $lockdir = "$prefix_abs/lockdir"; + my $winbindd_socket_dir = "$prefix_abs/winbindd_socket"; + my $winbindd_privileged_socket_dir = "$prefix_abs/winbindd_privileged_socket"; + my $ntp_signd_socket_dir = "$prefix_abs/ntp_signd_socket"; + my $nsswrap_passwd = "$etcdir/passwd"; + my $nsswrap_group = "$etcdir/group"; + + my $configuration = "--configfile=$conffile"; + my $ldapdir = "$privatedir/ldap"; + + my $tlsdir = "$privatedir/tls"; + + my $ifaceipv4 = "127.0.0.$swiface"; + my $interfaces = "$ifaceipv4/8"; + + (system("rm -rf $prefix/*") == 0) or die("Unable to clean up"); + mkdir($_, 0777) foreach ($privatedir, $etcdir, $piddir, $ncalrpcdir, $lockdir, + $tmpdir, "$tmpdir/test1", "$tmpdir/test2"); + + + my $localbasedn = $basedn; + $localbasedn = "CN=$netbiosname" if $server_role eq "member server"; + + open(CONFFILE, ">$conffile"); + print CONFFILE " +[global] + netbios name = $netbiosname + netbios aliases = $netbiosalias + workgroup = $domain + realm = $realm + private dir = $privatedir + pid directory = $piddir + ncalrpc dir = $ncalrpcdir + lock dir = $lockdir + setup directory = $self->{setupdir} + modules dir = $self->{bindir}/modules + js include = $srcdir/scripting/libjs + winbindd socket directory = $winbindd_socket_dir + winbindd privileged socket directory = $winbindd_privileged_socket_dir + ntp signd socket directory = $ntp_signd_socket_dir + winbind separator = / + name resolve order = bcast + interfaces = $interfaces + tls dh params file = $tlsdir/dhparms.pem + panic action = $srcdir/script/gdb_backtrace \%PID% \%PROG% + wins support = yes + server role = $server_role + max xmit = 32K + server max protocol = SMB2 + notify:inotify = false + ldb:nosync = true +#We don't want to pass our self-tests if the PAC code is wrong + gensec:require_pac = true + log level = $smbd_loglevel + +[tmp] + path = $tmpdir + read only = no + ntvfs handler = posix + posix:sharedelay = 100000 + posix:eadb = $lockdir/eadb.tdb + posix:oplocktimeout = 3 + posix:writetimeupdatedelay = 500000 + +[test1] + path = $tmpdir/test1 + read only = no + ntvfs handler = posix + posix:sharedelay = 100000 + posix:eadb = $lockdir/eadb.tdb + posix:oplocktimeout = 3 + posix:writetimeupdatedelay = 500000 + +[test2] + path = $tmpdir/test2 + read only = no + ntvfs handler = posix + posix:sharedelay = 100000 + posix:eadb = $lockdir/eadb.tdb + posix:oplocktimeout = 3 + posix:writetimeupdatedelay = 500000 + +[cifs] + read only = no + ntvfs handler = cifs + cifs:server = $netbiosname + cifs:share = tmp +#There is no username specified here, instead the client is expected +#to log in with kerberos, and smbd will used delegated credentials. + +[simple] + path = $tmpdir + read only = no + ntvfs handler = simple + +[sysvol] + path = $lockdir/sysvol + read only = yes + +[netlogon] + path = $lockdir/sysvol/$dnsname/scripts + read only = no + +[cifsposix] + copy = simple + ntvfs handler = cifsposix +"; + close(CONFFILE); + + $self->mk_keyblobs($tlsdir); + + open(KRB5CONF, ">$krb5_config"); + print KRB5CONF " +#Generated krb5.conf for $realm + +[libdefaults] + default_realm = $realm + dns_lookup_realm = false + dns_lookup_kdc = false + ticket_lifetime = 24h + forwardable = yes + +[realms] + $realm = { + kdc = 127.0.0.1:88 + admin_server = 127.0.0.1:88 + default_domain = $dnsname + } + $dnsname = { + kdc = 127.0.0.1:88 + admin_server = 127.0.0.1:88 + default_domain = $dnsname + } + $domain = { + kdc = 127.0.0.1:88 + admin_server = 127.0.0.1:88 + default_domain = $dnsname + } + +[appdefaults] + pkinit_anchors = FILE:$tlsdir/ca.pem + +[kdc] + enable-pkinit = true + pkinit_identity = FILE:$tlsdir/kdc.pem,$tlsdir/key.pem + pkinit_anchors = FILE:$tlsdir/ca.pem + +[domain_realm] + .$dnsname = $realm +"; + close(KRB5CONF); + + open(PWD, ">$nsswrap_passwd"); + print PWD " +root:x:0:0:root gecos:$prefix_abs:/bin/false +$unix_name:x:$unix_uid:$unix_gids[0]:$unix_name gecos:$prefix_abs:/bin/false +nobody:x:65534:65533:nobody gecos:$prefix_abs:/bin/false +"; + close(PWD); + + open(GRP, ">$nsswrap_group"); + print GRP " +root:x:0: +wheel:x:10: +users:x:100: +nobody:x:65533: +nogroup:x:65534:nobody +"; + close(GRP); + +#Ensure the config file is valid before we start + if (system("$self->{bindir}/testparm $configuration -v --suppress-prompt >/dev/null 2>&1") != 0) { + system("$self->{bindir}/testparm -v --suppress-prompt $configuration >&2"); + die("Failed to create a valid smb.conf configuration!"); + } + + (system("($self->{bindir}/testparm $configuration -v --suppress-prompt --parameter-name=\"netbios name\" --section-name=global 2> /dev/null | grep -i \"^$netbiosname\" ) >/dev/null 2>&1") == 0) or die("Failed to create a valid smb.conf configuration! $self->{bindir}/testparm $configuration -v --suppress-prompt --parameter-name=\"netbios name\" --section-name=global"); + + my @provision_options = (); + push (@provision_options, "NSS_WRAPPER_PASSWD=\"$nsswrap_passwd\""); + push (@provision_options, "NSS_WRAPPER_GROUP=\"$nsswrap_group\""); + if (defined($ENV{GDB_PROVISION})) { + push (@provision_options, "gdb --args python"); + } + if (defined($ENV{VALGRIND_PROVISION})) { + push (@provision_options, "valgrind"); + } + push (@provision_options, "$self->{setupdir}/provision"); + push (@provision_options, split(' ', $configuration)); + push (@provision_options, "--host-name=$netbiosname"); + push (@provision_options, "--host-ip=$ifaceipv4"); + push (@provision_options, "--quiet"); + push (@provision_options, "--domain=$domain"); + push (@provision_options, "--realm=$realm"); + push (@provision_options, "--adminpass=$password"); + push (@provision_options, "--krbtgtpass=krbtgt$password"); + push (@provision_options, "--machinepass=machine$password"); + push (@provision_options, "--root=$unix_name"); + + push (@provision_options, "--server-role=\"$server_role\""); + + my $ldap_uri= "$ldapdir/ldapi"; + $ldap_uri =~ s|/|%2F|g; + $ldap_uri = "ldapi://$ldap_uri"; + + my $ret = { + KRB5_CONFIG => $krb5_config, + PIDDIR => $piddir, + SERVER => $netbiosname, + SERVER_IP => $ifaceipv4, + NETBIOSNAME => $netbiosname, + NETBIOSALIAS => $netbiosalias, + LDAP_URI => $ldap_uri, + DOMAIN => $domain, + USERNAME => $username, + REALM => $realm, + PASSWORD => $password, + LDAPDIR => $ldapdir, + WINBINDD_SOCKET_DIR => $winbindd_socket_dir, + NCALRPCDIR => $ncalrpcdir, + LOCKDIR => $lockdir, + CONFIGURATION => $configuration, + SOCKET_WRAPPER_DEFAULT_IFACE => $swiface, + NSS_WRAPPER_PASSWD => $nsswrap_passwd, + NSS_WRAPPER_GROUP => $nsswrap_group, + }; + + if (defined($self->{ldap})) { + + push (@provision_options, "--ldap-backend=$ldap_uri"); + system("$self->{setupdir}/provision-backend $configuration --ldap-admin-pass=$password --root=$unix_name --realm=$realm --domain=$domain --host-name=$netbiosname --ldap-backend-type=$self->{ldap}>&2") == 0 or die("backend provision failed"); + + push (@provision_options, "--password=$password"); + + if ($self->{ldap} eq "openldap") { + push (@provision_options, "--username=samba-admin"); + ($ret->{SLAPD_CONF}, $ret->{OPENLDAP_PIDFILE}) = $self->mk_openldap($ldapdir, $configuration) or die("Unable to create openldap directories"); + push (@provision_options, "--ldap-backend-type=openldap"); + } elsif ($self->{ldap} eq "fedora-ds") { + push (@provision_options, "--simple-bind-dn=cn=Manager,$localbasedn"); + ($ret->{FEDORA_DS_DIR}, $ret->{FEDORA_DS_PIDFILE}) = $self->mk_fedora_ds($ldapdir, $configuration) or die("Unable to create fedora ds directories"); + push (@provision_options, "--ldap-backend-type=fedora-ds"); + } + + $self->slapd_start($ret) or + die("couldn't start slapd"); + } + + my $provision_cmd = join(" ", @provision_options); + (system($provision_cmd) == 0) or die("Unable to provision: \n$provision_cmd\n"); + + if (defined($self->{ldap})) { + $self->slapd_stop($ret) or + die("couldn't stop slapd"); + } + + return $ret; +} + +sub provision_member($$$) +{ + my ($self, $prefix, $dcvars) = @_; + print "PROVISIONING MEMBER..."; + + my $ret = $self->provision($prefix, + "member server", + "localmember3", + "localmember", + 3, + "localmemberpass"); + + $ret or die("Unable to provision"); + + my $cmd = ""; + $cmd .= "SOCKET_WRAPPER_DEFAULT_IFACE=\"$ret->{SOCKET_WRAPPER_DEFAULT_IFACE}\" "; + $cmd .= "KRB5_CONFIG=\"$ret->{KRB5_CONFIG}\" "; + $cmd .= "$self->{bindir}/net join $ret->{CONFIGURATION} $dcvars->{DOMAIN} member"; + $cmd .= " -U$dcvars->{USERNAME}\%$dcvars->{PASSWORD}"; + + system($cmd) == 0 or die("Join failed\n$cmd"); + + $ret->{SMBD_TEST_FIFO} = "$prefix/smbd_test.fifo"; + $ret->{SMBD_TEST_LOG} = "$prefix/smbd_test.log"; + $ret->{SMBD_TEST_LOG_POS} = 0; + + $ret->{DC_SERVER} = $dcvars->{SERVER}; + $ret->{DC_SERVER_IP} = $dcvars->{SERVER_IP}; + $ret->{DC_NETBIOSNAME} = $dcvars->{NETBIOSNAME}; + $ret->{DC_NETBIOSALIAS} = $dcvars->{NETBIOSALIAS}; + $ret->{DC_USERNAME} = $dcvars->{USERNAME}; + $ret->{DC_PASSWORD} = $dcvars->{PASSWORD}; + + return $ret; +} + +sub provision_dc($$) +{ + my ($self, $prefix) = @_; + + print "PROVISIONING DC..."; + my $ret = $self->provision($prefix, + "domain controller", + "localdc1", + "localdc", + 1, + "localdcpass"); + + $self->add_wins_config("$prefix/private") or + die("Unable to add wins configuration"); + + $ret->{SMBD_TEST_FIFO} = "$prefix/smbd_test.fifo"; + $ret->{SMBD_TEST_LOG} = "$prefix/smbd_test.log"; + $ret->{SMBD_TEST_LOG_POS} = 0; + return $ret; +} + +sub teardown_env($$) +{ + my ($self, $envvars) = @_; + my $pid; + + close(DATA); + + if (-f "$envvars->{PIDDIR}/smbd.pid" ) { + open(IN, "<$envvars->{PIDDIR}/smbd.pid") or die("unable to open smbd pid file"); + $pid = <IN>; + close(IN); + + # Give the process 20 seconds to exit. gcov needs + # this time to write out the covarge data + my $count = 0; + until (kill(0, $pid) == 0) { + # if no process sucessfully signalled, then we are done + sleep(1); + $count++; + last if $count > 20; + } + + # If it is still around, kill it + if ($count > 20) { + print "smbd process $pid took more than $count seconds to exit, killing\n"; + kill 9, $pid; + } + } + + my $failed = $? >> 8; + + $self->slapd_stop($envvars) if ($self->{ldap}); + + print $self->getlog_env($envvars); + + return $failed; +} + +sub getlog_env($$) +{ + my ($self, $envvars) = @_; + my $title = "SMBD LOG of: $envvars->{NETBIOSNAME}\n"; + my $out = $title; + + open(LOG, "<$envvars->{SMBD_TEST_LOG}"); + + seek(LOG, $envvars->{SMBD_TEST_LOG_POS}, SEEK_SET); + while (<LOG>) { + $out .= $_; + } + $envvars->{SMBD_TEST_LOG_POS} = tell(LOG); + close(LOG); + + return "" if $out eq $title; + + return $out; +} + +sub check_env($$) +{ + my ($self, $envvars) = @_; + + return 1 if (-p $envvars->{SMBD_TEST_FIFO}); + + print $self->getlog_env($envvars); + + return 0; +} + +sub setup_env($$$) +{ + my ($self, $envname, $path) = @_; + + if ($envname eq "dc") { + return $self->setup_dc("$path/dc"); + } elsif ($envname eq "member") { + if (not defined($self->{vars}->{dc})) { + $self->setup_dc("$path/dc"); + } + return $self->setup_member("$path/member", $self->{vars}->{dc}); + } else { + die("Samba4 can't provide environment '$envname'"); + } +} + +sub setup_member($$$$) +{ + my ($self, $path, $dc_vars) = @_; + + my $env = $self->provision_member($path, $dc_vars); + + $self->check_or_start($env, ($ENV{SMBD_MAXTIME} or 7500)); + + $self->wait_for_start($env); + + return $env; +} + +sub setup_dc($$) +{ + my ($self, $path) = @_; + + my $env = $self->provision_dc($path); + + $self->check_or_start($env, + ($ENV{SMBD_MAXTIME} or 7500)); + + $self->wait_for_start($env); + + $self->{vars}->{dc} = $env; + + return $env; +} + +sub stop($) +{ + my ($self) = @_; +} + +1; diff --git a/source4/selftest/target/Windows.pm b/source4/selftest/target/Windows.pm new file mode 100644 index 0000000000..d0c90d7f7b --- /dev/null +++ b/source4/selftest/target/Windows.pm @@ -0,0 +1,40 @@ +#!/usr/bin/perl +# Bootstrap Samba and run a number of tests against it. +# Copyright (C) 2005-2007 Jelmer Vernooij <jelmer@samba.org> +# Published under the GNU GPL, v3 or later. + +package Windows; + +use strict; +use FindBin qw($RealBin); +use POSIX; + +sub new($) +{ + my ($classname) = @_; + my $self = { }; + bless $self; + return $self; +} + +sub provision($$$) +{ + my ($self, $environment, $prefix) = @_; + + die ("Windows tests will not run without root privileges.") + if (`whoami` ne "root"); + + die("Environment variable WINTESTCONF has not been defined.\n". + "Windows tests will not run unconfigured.") if (not defined($ENV{WINTESTCONF})); + + die ("$ENV{WINTESTCONF} could not be read.") if (! -r $ENV{WINTESTCONF}); + + $ENV{WINTEST_DIR}="$ENV{SRCDIR}/selftest/win"; +} + +sub setup_env($$) +{ + my ($self, $name) = @_; +} + +1; diff --git a/source4/selftest/test_samba4.pl b/source4/selftest/test_samba4.pl new file mode 100755 index 0000000000..f2935be66b --- /dev/null +++ b/source4/selftest/test_samba4.pl @@ -0,0 +1,20 @@ +#!/usr/bin/perl + +use Test::More tests => 3; +use FindBin qw($RealBin); +use lib $RealBin; +use Samba4; + +my $s = new Samba4($RealBin."/../bin", undef, $RealBin."/../setup"); + +ok($s); + +is($RealBin."/../bin", $s->{bindir}); + +ok($s->write_ldb_file("tmpldb", " +dn: a=b +a: b +c: d +")); + +unlink("tmpldb"); diff --git a/source4/selftest/test_subunit.pl b/source4/selftest/test_subunit.pl new file mode 100755 index 0000000000..28522ed49f --- /dev/null +++ b/source4/selftest/test_subunit.pl @@ -0,0 +1,7 @@ +#!/usr/bin/perl + +use Test::More tests => 0; +use FindBin qw($RealBin); +use lib $RealBin; +use Subunit qw(parse_results); + diff --git a/source4/selftest/test_w2k3.sh b/source4/selftest/test_w2k3.sh new file mode 100755 index 0000000000..3cd034d000 --- /dev/null +++ b/source4/selftest/test_w2k3.sh @@ -0,0 +1,48 @@ +#!/bin/sh + +# tests that should pass against a w2k3 DC, as administrator + +# add tests to this list as they start passing, so we test +# that they stay passing +ncacn_np_tests="RPC-SCHANNEL RPC-DSSETUP RPC-EPMAPPER RPC-SAMR RPC-WKSSVC RPC-SRVSVC RPC-EVENTLOG RPC-NETLOGON RPC-LSA RPC-SAMLOGON RPC-SAMSYNC RPC-MULTIBIND RPC-WINREG RPC-SPOOLSS RPC-SPOOLSS-WIN" +ncacn_ip_tcp_tests="RPC-SCHANNEL RPC-EPMAPPER RPC-SAMR RPC-NETLOGON RPC-LSA RPC-SAMLOGON RPC-SAMSYNC RPC-MULTIBIND" + +if [ $# -lt 4 ]; then +cat <<EOF +Usage: test_w2k3.sh SERVER USERNAME PASSWORD DOMAIN REALM +EOF +exit 1; +fi + +server="$1" +username="$2" +password="$3" +domain="$4" +realm="$5" +shift 5 + +incdir=`dirname $0` +. $incdir/test_functions.sh + +OPTIONS="-U$username%$password -W $domain --option realm=$realm" + +name="RPC-SPOOLSS on ncacn_np" +testit "$name" rpc bin/smbtorture $TORTURE_OPTIONS ncacn_np:"$server" $OPTIONS RPC-SPOOLSS "$*" + +for bindoptions in padcheck connect sign seal ntlm,sign ntlm,seal $VALIDATE bigendian; do + for transport in ncacn_ip_tcp ncacn_np; do + case $transport in + ncacn_np) tests=$ncacn_np_tests ;; + ncacn_ip_tcp) tests=$ncacn_ip_tcp_tests ;; + esac + for t in $tests; do + name="$t on $transport with $bindoptions" + testit "$name" rpc bin/smbtorture $TORTURE_OPTIONS $transport:"$server[$bindoptions]" $OPTIONS $t "$*" + done + done +done + +name="RPC-DRSUAPI on ncacn_ip_tcp with seal" +testit "$name" rpc bin/smbtorture $TORTURE_OPTIONS ncacn_ip_tcp:"$server[seal]" $OPTIONS RPC-DRSUAPI "$*" +name="RPC-DRSUAPI on ncacn_ip_tcp with seal,bigendian" +testit "$name" rpc bin/smbtorture $TORTURE_OPTIONS ncacn_ip_tcp:"$server[seal,bigendian]" $OPTIONS RPC-DRSUAPI "$*" diff --git a/source4/selftest/test_w2k3_file.sh b/source4/selftest/test_w2k3_file.sh new file mode 100755 index 0000000000..d59d2b49ef --- /dev/null +++ b/source4/selftest/test_w2k3_file.sh @@ -0,0 +1,44 @@ +#!/bin/sh + +# this runs the file serving tests that are expected to pass with win2003 + +if [ $# -lt 3 ]; then +cat <<EOF +Usage: test_w2k3_file.sh UNC USERNAME PASSWORD <first> <smbtorture args> +EOF +exit 1; +fi + +unc="$1" +username="$2" +password="$3" +start="$4" +shift 4 +ADDARGS="$*" + +incdir=`dirname $0` +. $incdir/test_functions.sh + +tests="BASE-FDPASS BASE-LOCK " +tests="$tests BASE-UNLINK BASE-ATTR" +tests="$tests BASE-DIR1 BASE-DIR2 BASE-VUID" +tests="$tests BASE-TCON BASE-TCONDEV BASE-RW1" +tests="$tests BASE-DENY3 BASE-XCOPY BASE-OPEN BASE-DENYDOS" +tests="$tests BASE-DELETE BASE-PROPERTIES BASE-MANGLE" +tests="$tests BASE-CHKPATH BASE-SECLEAK BASE-TRANS2" +tests="$tests BASE-NTDENY1 BASE-NTDENY2 BASE-RENAME BASE-OPENATTR" +tests="$tests RAW-QFILEINFO RAW-SFILEINFO-BUG RAW-SFILEINFO" +tests="$tests RAW-LOCK RAW-MKDIR RAW-SEEK RAW-CONTEXT RAW-MUX RAW-OPEN RAW-WRITE" +tests="$tests RAW-UNLINK RAW-READ RAW-CLOSE RAW-IOCTL RAW-CHKPATH RAW-RENAME" +tests="$tests RAW-EAS RAW-STREAMS RAW-OPLOCK RAW-NOTIFY BASE-DELAYWRITE" +# slowest tests last +tests="$tests BASE-DENY1 BASE-DENY2" + +# these tests are known to fail against windows +fail="RAW-SEARCH RAW-ACLS RAW-QFSINFO" + +echo "Skipping tests expected to fail: $fail" + +for t in $tests; do + testit "$t" smb $VALGRIND bin/smbtorture $TORTURE_OPTIONS $ADDARGS $unc -U"$username"%"$password" $t +done diff --git a/source4/selftest/test_win.sh b/source4/selftest/test_win.sh new file mode 100755 index 0000000000..bc40620af5 --- /dev/null +++ b/source4/selftest/test_win.sh @@ -0,0 +1,42 @@ +#!/bin/sh + +# A shell script to connect to a windows host over telnet, +# setup for a smbtorture test, +# run the test, +# and remove the previously configured directory and share. +# Copyright Brad Henry <brad@samba.org> 2006 +# Released under the GNU GPL version 3 or later. + +. selftest/test_functions.sh + +export SMBTORTURE_REMOTE_HOST=`perl -I$WINTEST_DIR $WINTEST_DIR/vm_get_ip.pl VM_CFG_PATH` +if [ -z $SMBTORTURE_REMOTE_HOST ]; then + # Restore snapshot to ensure VM is in a known state, then exit. + restore_snapshot "Test failed to get the IP address of the windows host." "$VM_CFG_PATH" + exit 1 +fi + +name="BASE against Windows 2003" +testit "$name" smb $WINTEST_DIR/wintest_base.sh $SMBTORTURE_REMOTE_HOST \ + $SMBTORTURE_USERNAME $SMBTORTURE_PASSWORD $SMBTORTURE_WORKGROUP + +name="RAW against Windows 2003" +testit "$name" smb $WINTEST_DIR/wintest_raw.sh $SMBTORTURE_REMOTE_HOST \ + $SMBTORTURE_USERNAME $SMBTORTURE_PASSWORD $SMBTORTURE_WORKGROUP + +name="RPC against Windows 2003" +testit "$name" smb $WINTEST_DIR/wintest_rpc.sh $SMBTORTURE_REMOTE_HOST \ + $SMBTORTURE_USERNAME $SMBTORTURE_PASSWORD $SMBTORTURE_WORKGROUP + +name="NET against Windows 2003" +testit "$name" smb $WINTEST_DIR/wintest_net.sh $SMBTORTURE_REMOTE_HOST \ + $SMBTORTURE_USERNAME $SMBTORTURE_PASSWORD $SMBTORTURE_WORKGROUP + +name="Windows 2003 against smbd" +testit "$name" smb $WINTEST_DIR/wintest_client.sh $SMBTORTURE_REMOTE_HOST + +dc_tests="RPC-DRSUAPI ncacn_np ncacn_ip_tcp" +for name in $dc_tests; do + testit "$name against Windows 2003 DC" rpc $WINTEST_DIR/wintest_2k3_dc.sh \ + "$name" +done diff --git a/source4/selftest/tests_win.sh b/source4/selftest/tests_win.sh new file mode 100755 index 0000000000..19460eee0e --- /dev/null +++ b/source4/selftest/tests_win.sh @@ -0,0 +1,30 @@ +#!/bin/sh + + if [ `whoami` != "root" ]; then + echo "Windows tests will not run without root privileges." + exit 1 + fi + + if [ "$DO_SOCKET_WRAPPER" = SOCKET_WRAPPER ]; then + echo "Windows tests will not run with socket wrapper enabled." + exit 1 + fi + + if [ ! $WINTESTCONF ]; then + echo "Environment variable WINTESTCONF has not been defined." + echo "Windows tests will not run unconfigured." + exit 1 + fi + + if [ ! -r $WINTESTCONF ]; then + echo "$WINTESTCONF could not be read." + exit 1 + fi + + export WINTEST_DIR=$SRCDIR/selftest/win + export TMPDIR=$TMPDIR + export NETBIOSNAME=$NETBIOSNAME + + . $WINTESTCONF + + $SRCDIR/selftest/test_win.sh diff --git a/source4/selftest/tests_win2k3_dc.sh b/source4/selftest/tests_win2k3_dc.sh new file mode 100755 index 0000000000..290a4ef666 --- /dev/null +++ b/source4/selftest/tests_win2k3_dc.sh @@ -0,0 +1,22 @@ +#!/bin/sh + +if [ ! $WINTESTCONF ]; then + echo "Environment variable WINTESTCONF has not been defined." + echo "Windows tests will not run unconfigured." + exit 1 +fi + +if [ ! -r $WINTESTCONF ]; then + echo "$WINTESTCONF could not be read." + exit 1 +fi + +. selftest/test_functions.sh + +export SRCDIR=$SRCDIR + +tests="RPC-DRSUAPI RPC-SPOOLSS ncacn_np ncacn_ip_tcp" + +for name in $tests; do + testit $name rpc $SRCDIR/selftest/win/wintest_2k3_dc.sh $name +done diff --git a/source4/selftest/win/README b/source4/selftest/win/README new file mode 100644 index 0000000000..fc934385c4 --- /dev/null +++ b/source4/selftest/win/README @@ -0,0 +1,121 @@ +This framework uses a VMware Server hosted Windows guest VM to test the +behaviour of Windows -> Samba and Samba -> Windows interactions. To setup a +Windows host for testing, vm_setup.tar.gz contain some scripts which create +an administrative user account, and enable and start the installed telnet +service on the Windows host. Optionally, the hostname and workgroup name can +also be set. vm_setup.tar.gz is currently located in the SOC/bnh branch of +Samba's SVN repository. + +PREREQUISITES + +To use these scripts, VMware Server needs to be running with a Windows guest +VM installed, IP addressed, and VMware tools needs to be installed and running +on the guest VM. The Windows OS I used to test with was Windows Server 2003, +but I think this should work with any version of Windows that has the +Microsoft telnet service installed. The VMware Server versions I used for +testing was 1.0.0 build-27828, and 1.0.0 build-28343. + +PLEASE NOTE: Due to problems with my original revert_snapshot() code, the initial +setup now requires that the VM configuration setting 'When Powering Off' is +manually set to 'Revert to snapshot' (snapshot.action="autoRevert" in the +guest's .vmx file). This should not be a permanent change, but the original +revert_snapshot() code I wrote no longer works and i'm not sure why. + +On the machine that these scripts are running on (this need not be the same +machine as the VMware host), the VMware perl scripting api needs to be +installed, as well as the vix-perl api. These come with the VMware Server +console package. + +After unzipping this file, the libraries are installed by extracting the +VMware-vix-e.x.p-<revision number>.tar.gz and +VMware-VmPerlAPI-e.x.p-<revision number>.tar.gz archives, and running the +vmware-install.pl scripts inside their respective directories. + +On Slackware 10.2, I encountered a problem in that when I tried to use the vix +api libraries, I would get the following error: + +SSLLoadSharedLibrary: Failed to load library /<client program directory>/libcrypto.so.0.9.7:/<client program directory>/libcrypto.so.0.9.7: cannot open a shared object file: No such file or directory. + +The fix found on the VMware knowledge base (search http://kb.vmware.com for +Doc ID: 1837104) states that it's a known problem with the scripting libraries, +and can be resolved by installing VMware Server on the host, which properly +sets up the SSL module loader. This is what I would suggest if you encounter +this, as it solved the problem for me (I don't have VMware Server actually +running on that host though). + +INSTALLATION + +To use these scripts, modify initial_setup.conf to match your environment. The +GUEST_HOSTNAME, GUEST_WORKGROUP, HOST_SERVER_NAME, HOST_SERVER_PORT, +HOST_USERNAME, and HOST_PASSWORD variables are optional, and are commented out +in this release. + +Running initial_setup.sh will: +* Get the IP address of the Windows guest VM. +* Take a snapshot of the pristine Windows guest. +* Copy the windows scripts from the windows-scripts directory on the unix host + to the directory on the Windows guest specified by the + GUEST_SCRIPT_PATH option. This path will be created on the guest if + it does not already exist. +* Execute win_setup.wsf on the Windows guest in order to create the + administrator account specified by GUEST_USERNAME and GUEST_PASSWORD, + enable and start the telnet service, and set the GUEST_HOSTNAME and + GUEST_WORKGROUP if configured. +* If these operations are successful so far, another snapshot is taken at this + point. This is the snapshot which is restored if the tests encounter + problems they are unable to recover from. + +These operations leave the Windows guest in a state such that it can be +remotely administered with telnet. Specifically, this will allow us to use +'make wintest' in Samba 4 to perform smbtorture tests against a Windows host, +and perform tests from a Windows client to a Samba server. + +INTEGRATING WITH THE BUILD FARM + +Follow the standard steps to add a host to the build farm. The major +difference is that we will need to run these tests as root. To run the +Windows tests in the build farm, a .fns file will need to be created for +your new host that exports a WINTESTCONF environment variable pointing to a +config file used by 'make wintest'. An example of this config file can be +found at source/selftest/win/test_win.conf in the Samba 4 source tree. + +I've also included the bnhtest.fns file that I'm using for my build farm host +below, as an example. It was modified from generic.fns. + +action_test_windows() { + do_make wintest + w_status=$? + echo "WINTEST STATUS: $w_status" + return $w_status; +} + +per_run_hook + +system=`uname` + +export WINTESTCONF="/home/build/win/test_win.conf" + +for compiler in gcc cc icc; do + + # arrgh, "which" gives no err code on solaris + path=`which $compiler` + if [ -x "$path" ]; then + + if $compiler -v 2>&1 | grep gcc.version > /dev/null; then + isgcc=1 + CFLAGS="-Wall" + export CFLAGS + else + CFLAGS="" + export CFLAGS + isgcc=0 + fi + if [ $compiler = gcc -o $isgcc = 0 ]; then + + # only attempt samba4 if we have perl + if which perl > /dev/null; then + test_tree samba4 source $compiler configure build install test_windows test + fi + fi + fi +done diff --git a/source4/selftest/win/VMHost.pm b/source4/selftest/win/VMHost.pm new file mode 100644 index 0000000000..1f02f21c28 --- /dev/null +++ b/source4/selftest/win/VMHost.pm @@ -0,0 +1,359 @@ +#!/usr/bin/perl -w + +# A perl object to provide a simple, unified method of handling some +# VMware Server VM management functions using the perl and VIX API's. +# Copyright Brad Henry <brad@samba.org> 2006 +# Released under the GNU GPL version 3 or later. + +# VMware Perl API +use VMware::VmPerl; +use VMware::VmPerl::VM; +use VMware::VmPerl::ConnectParams; + +# VMware C bindings +use VMware::Vix::Simple; +use VMware::Vix::API::Constants; + +# Create a class to abstract from the Vix and VMPerl APIs. +{ package VMHost; + my $perl_vm = VMware::VmPerl::VM::new(); + my $perl_vm_credentials; + my $vix_vm; + my $vix_vm_host; + + my $err_code = 0; + my $err_str = ""; + + my $hostname; + my $port; + my $username; + my $password; + my $vm_cfg_path; + my $guest_admin_username; + my $guest_admin_password; + + sub error { + my $old_err_code = $err_code; + my $old_err_str = $err_str; + $err_code = 0; + $err_str = ""; + return ($old_err_code, $old_err_str); + } + + # Power on the guest if it isn't already running. + # Returns 0 when the guest is already running, and + # if not, it waits until it is started. + sub start_guest { + my $vm_power_state = $perl_vm->get_execution_state(); + if (!defined($vm_power_state)) { + ($err_code, $err_str) = $perl_vm->get_last_error(); + return ($err_code); + } + if ($vm_power_state == VMware::VmPerl::VM_EXECUTION_STATE_OFF + || $vm_power_state == + VMware::VmPerl::VM_EXECUTION_STATE_SUSPENDED) + { + if (!$perl_vm->start()) { + ($err_code, $err_str) = + $perl_vm->get_last_error(); + return ($err_code); + } + while ($perl_vm->get_tools_last_active() == 0) { + sleep(60); + } + } + return ($err_code); + } + + sub host_connect { + # When called as a method, the first parameter passed is the + # name of the method. Called locally, this function will lose + # the first parameter. + shift @_; + ($hostname, $port, $username, $password, $vm_cfg_path, + $guest_admin_username, $guest_admin_password) = @_; + + # Connect to host using vmperl api. + $perl_vm_credentials = + VMware::VmPerl::ConnectParams::new($hostname, $port, + $username, $password); + if (!$perl_vm->connect($perl_vm_credentials, $vm_cfg_path)) { + ($err_code, $err_str) = $perl_vm->get_last_error(); + undef $perl_vm; + return ($err_code); + } + + # Connect to host using vix api. + ($err_code, $vix_vm_host) = + VMware::Vix::Simple::HostConnect( + VMware::Vix::Simple::VIX_API_VERSION, + VMware::Vix::Simple::VIX_SERVICEPROVIDER_VMWARE_SERVER, + $hostname, $port, $username, $password, + 0, VMware::Vix::Simple::VIX_INVALID_HANDLE); + if ($err_code != VMware::Vix::Simple::VIX_OK) { + $err_str = + VMware::Vix::Simple::GetErrorText($err_code); + undef $perl_vm; + undef $vix_vm; + undef $vix_vm_host; + return ($err_code); + } + + # Power on our guest os if it isn't already running. + $err_code = start_guest(); + if ($err_code != 0) { + my $old_err_str = $err_str; + $err_str = "Starting guest power after connect " . + "failed: " . $old_err_str; + undef $perl_vm; + undef $vix_vm; + undef $vix_vm_host; + return ($err_code); + } + + # Open VM. + ($err_code, $vix_vm) = + VMware::Vix::Simple::VMOpen($vix_vm_host, $vm_cfg_path); + if ($err_code != VMware::Vix::Simple::VIX_OK) { + $err_str = + VMware::Vix::Simple::GetErrorText($err_code); + undef $perl_vm; + undef $vix_vm; + undef $vix_vm_host; + return ($err_code); + } + + # Login to $vix_vm guest OS. + $err_code = VMware::Vix::Simple::VMLoginInGuest($vix_vm, + $guest_admin_username, $guest_admin_password, + 0); + if ($err_code != VMware::Vix::Simple::VIX_OK) { + $err_str = + VMware::Vix::Simple::GetErrorText($err_code); + undef $perl_vm; + undef $vix_vm; + undef $vix_vm_host; + return ($err_code); + } + return ($err_code); + } + + sub host_disconnect { + undef $perl_vm; + + $perl_vm = VMware::VmPerl::VM::new(); + if (!$perl_vm) { + $err_code = 1; + $err_str = "Error creating new VmPerl object"; + } + + undef $vix_vm; + VMware::Vix::Simple::HostDisconnect($vix_vm_host); + VMware::Vix::Simple::ReleaseHandle($vix_vm_host); + return ($err_code); + } + + sub host_reconnect { + $err_code = host_disconnect(); + if ($err_code != 0) { + my $old_err_str = $err_str; + $err_str = "Disconnecting from host failed: " . + $old_err_str; + return ($err_code); + } + + $err_code = host_connect(NULL, $hostname, $port, $username, + $password, $vm_cfg_path, $guest_admin_username, + $guest_admin_password); + if ($err_code != 0) { + my $old_err_str = $err_str; + $err_str = "Re-connecting to host failed: " . + $old_err_str; + return ($err_code); + } + return ($err_code); + } + + sub create_snapshot { + my $snapshot; + + ($err_code, $snapshot) = + VMware::Vix::Simple::VMCreateSnapshot($vix_vm, + "Snapshot", "Created by vm_setup.pl", 0, + VMware::Vix::Simple::VIX_INVALID_HANDLE); + + VMware::Vix::Simple::ReleaseHandle($snapshot); + + if ($err_code != VMware::Vix::Simple::VIX_OK) { + $err_str = + VMware::Vix::Simple::GetErrorText($err_code); + return $err_code; + } + + $err_code = host_reconnect(); + if ($err_code != 0) { + my $old_err_str = $err_str; + $err_str = "Reconnecting to host after creating " . + "snapshot: " . $old_err_str; + return ($err_code); + } + return ($err_code); + } + + sub revert_snapshot { + # Because of problems with VMRevertToSnapshot(), we have to + # rely on the guest having set 'Revert to Snapshot' following + # a power-off event. + $err_code = VMware::Vix::Simple::VMPowerOff($vix_vm, 0); + if ($err_code != VMware::Vix::Simple::VIX_OK) { + $err_str = + VMware::Vix::Simple::GetErrorText($err_code); + return $err_code; + } + + # host_reconnect() will power-on a guest in a non-running state. + $err_code = host_reconnect(); + if ($err_code != 0) { + my $old_err_str = $err_str; + $err_str = "Reconnecting to host after reverting " . + "snapshot: " . $old_err_str; + return ($err_code); + } + return ($err_code); + } + + # $dest_path must exist. It doesn't get created. + sub copy_files_to_guest { + shift @_; + my (%files) = @_; + + my $src_file; + my $dest_file; + + foreach $src_file (keys(%files)) { + $dest_file = $files{$src_file}; + $err_code = + VMware::Vix::Simple::VMCopyFileFromHostToGuest( + $vix_vm, $src_file, $dest_file, 0, + VMware::Vix::Simple::VIX_INVALID_HANDLE); + if ($err_code != VMware::Vix::Simple::VIX_OK) { + $err_str = "Copying $src_file: " . + VMware::Vix::Simple::GetErrorText( + $err_code); + return $err_code; + } + } + return $err_code; + } + + sub copy_to_guest { + # Read parameters $src_path, $dest_path. + shift @_; + my ($src_path, $dest_dir) = @_; + + my $len = length($dest_dir); + my $idx = rindex($dest_dir, '\\'); + if ($idx != ($len - 1)) { + $err_code = -1; + $err_str = "Destination $dest_dir must be a " . + "directory path"; + return ($err_code); + } + + # Create the directory $dest_path on the guest VM filesystem. + my $cmd = "cmd.exe "; + my $cmd_args = "/C MKDIR " . $dest_dir; + $err_code = run_on_guest(NULL, $cmd, $cmd_args); + if ( $err_code != 0) { + my $old_err_str = $err_str; + $err_str = "Creating directory $dest_dir on host: " . + $old_err_str; + return ($err_code); + } + + # If $src_filepath specifies a file, create it in $dest_path + # and keep the same name. + # If $src_path is a directory, create the files it contains in + # $dest_path, keeping the same names. + $len = length($src_path); + my %files; + $idx = rindex($src_path, '/'); + if ($idx == ($len - 1)) { + # $src_path is a directory. + if (!opendir (DIR_HANDLE, $src_path)) { + $err_code = -1; + $err_str = "Error opening directory $src_path"; + return $err_code; + } + + foreach $file (readdir DIR_HANDLE) { + my $src_file = $src_path . $file; + + if (!opendir(DIR_HANDLE2, $src_file)) { + # We aren't interested in subdirs. + my $dest_path = $dest_dir . $file; + $files{$src_file} = $dest_path; + } else { + closedir(DIR_HANDLE2); + } + } + } else { + # Strip if preceeding path from $src_path. + my $src_file = substr($src_path, ($idx + 1), $len); + my $dest_path = $dest_dir . $src_file; + + # Add $src_path => $dest_path to %files. + $files{$src_path} = $dest_path; + } + + $err_code = copy_files_to_guest(NULL, %files); + if ($err_code != 0) { + my $old_err_str = $err_str; + $err_str = "Copying files to host after " . + "populating %files: " . $old_err_str; + return ($err_code); + } + return ($err_code); + } + + sub run_on_guest { + # Read parameters $cmd, $cmd_args. + shift @_; + my ($cmd, $cmd_args) = @_; + + $err_code = VMware::Vix::Simple::VMRunProgramInGuest($vix_vm, + $cmd, $cmd_args, 0, + VMware::Vix::Simple::VIX_INVALID_HANDLE); + if ($err_code != VMware::Vix::Simple::VIX_OK) { + $err_str = VMware::Vix::Simple::GetErrorText( + $err_code); + return ($err_code); + } + + return ($err_code); + } + + sub get_guest_ip { + my $guest_ip = $perl_vm->get_guest_info('ip'); + + if (!defined($guest_ip)) { + ($err_code, $err_str) = $perl_vm->get_last_error(); + return NULL; + } + + if (!($guest_ip)) { + $err_code = 1; + $err_str = "Guest did not set the 'ip' variable"; + return NULL; + } + return $guest_ip; + } + + sub DESTROY { + host_disconnect(); + undef $perl_vm; + undef $vix_vm_host; + } +} + +return TRUE; diff --git a/source4/selftest/win/common.exp b/source4/selftest/win/common.exp new file mode 100644 index 0000000000..28a3d7a6a1 --- /dev/null +++ b/source4/selftest/win/common.exp @@ -0,0 +1,521 @@ +# A library of commonly used functions written in expect. +# Copyright Brad Henry <brad@samba.org> 2006 +# Released under the GNU GPL version 3 or later. + +# This function maps a drive letter to a share point. +proc map_share { remote_prompt share_drive sharepoint username domain password } { + set default_err_str "Unknown error in function map_share" + set err_str $default_err_str + + set cmd "net use $share_drive $sharepoint $password /USER:$username@$domain\r\n" + send $cmd + + expect { + "The command completed successfully." { + expect_prompt $remote_prompt + set err_str "OK" + } \ + "The local device name is already in use." { + expect_prompt $remote_prompt + set err_str "The device name $share_drive is already in use" + } \ + "The network name cannot be found." { + expect_prompt $remote_prompt + set err_str "Sharepoint $sharepoint could not be found" + } \ + timeout { + set err_str "Function map_share timed out while mapping $share_drive to $sharepoint" + } + } + return $err_str +} + +# This function unmaps a drive letter from a share point. +proc unmap_share { remote_prompt share_drive } { + set default_err_str "Unknown error in function unmap_share" + set err_str $default_err_str + + set cmd "net use $share_drive /DELETE\r\n" + send $cmd + + expect { + "was deleted successfully." { + expect_prompt $remote_prompt + set err_str "OK" + } \ + "NET HELPMSG 2250" { + expect_prompt $remote_prompt + set err_str "The network connection could not be found while unmapping $share_drive" + } \ + timeout { + set err_str "Function unmap_share timed out while unmapping $share_drive" + } + } + return $err_str +} + +# This function uses xcopy to copy a text file from one location on the +# remote windows host to another. +proc xcopy_file { remote_prompt in_filename out_filename xcopy_options } { + set default_err_str "Unknown error in function xcopy_file" + set err_str $default_err_str + + set cmd "xcopy $in_filename $out_filename $xcopy_options\r\n" + send $cmd + + expect { + "(F = file, D = directory)? " { + set cmd "F\r\n" + send $cmd + expect { + "1 File(s) copied\r\n\r\n" { + expect_prompt $remote_prompt + set err_str "OK" + } \ + "0 File(s) copied\r\n\r\n" { + expect_prompt $remote_prompt + set err_str $default_err_str + } \ + timeout { + set err_str "Function xcopy_file has timed out while copying $in_filename" + } + } + } \ + "1 File(s) copied\r\n\r\n" { + expect_prompt $remote_prompt + set err_str "OK" + } \ + "0 File(s) copied\r\n\r\n" { + expect_prompt $remote_prompt + set err_str $default_err_str + } \ + timeout { + set err_str "Function xcopy_file timed out while copying $in_filename" + } + } + return $err_str +} + +# This function creates a temporary file on the remote windows host. +# The file contents are populated by a recursive directory listing of +# the windows %HOMEDRIVE%. +proc create_tmp_file { remote_prompt filename } { + set default_err_str "Unknown error in function create_tmp_file" + set err_str $default_err_str + + set cmd "dir %HOMEDRIVE%\\ /S > $filename\r\n" + send $cmd + expect { + $remote_prompt { + set err_str "OK" + } \ + timeout { + set err_str "Function create_tmp_file timed out while creating $filename" + } + } + return $err_str +} + +# This function compares two files on the remote windows host. +proc compare_files { remote_prompt file1 file2 } { + set default_err_str "Unknown error in function compare_files" + set err_str $default_err_str + + set cmd "fc $file1 $file2\r\n" + send $cmd + expect { + "FC: no differences encountered\r\n\r\n\r\n" { + expect_prompt $remote_prompt + set err_str "OK" + } \ + "\*\*\*\*\* $file1" { + expect_prompt $remote_prompt + set err_str "Files $file1 and $file2 differ" + } \ + "\*\*\*\*\* $file2" { + expect_prompt $remote_prompt + set err_str "Files $file1 and $file2 differ" + } \ + timeout { + set err_str "Function compare_files timed out while comparing files $file1 and $file2" + } + } + return $err_str +} + +# This function deletes a file on the remote windows host. +proc delete_file { remote_prompt filename } { + set default_err_str "Unknown error in function delete_file" + set err_str $default_err_str + + set cmd "del $filename\r\n" + send $cmd + expect { + "Could Not" { + expect_prompt $remote_prompt + set err_str $default_err_str + } \ + $remote_prompt { + set err_str "OK" + } \ + timeout { + set err_str "Function delete_file timed oout while deleting $filename" + } + } + return $err_str +} + +# This function copies a text file over telnet from the local unix host +# to the remote windows host. +proc copy_file { remote_prompt in_filename out_filename } { + set default_err_str "Unknown error in function copy_file" + set err_str $default_err_str + + # The octal ASCII code for Control-Z is 032. + set CTRLZ \032 + + # Open local file and read contents. + set in_file [open $in_filename r] + set in_data [read $in_file] + + # Initiate copy on remote host. + set cmd "copy con $out_filename\r\n" + send $cmd + + # Separate $in_data into lines and send to remote host. + set out_data [split $in_data "\n"] + foreach out_line $out_data { + send $out_line + # We might as well do a unix -> windows line conversion. + send "\r\n" + # Are we overwriting an existing file? + # If so, exit so we can handle it. + expect { + "(Yes/No/All)" { + send "NO\r\n" + expect_prompt $remote_prompt + set err_str "File exists" + } \ + $out_line { + set err_str "OK" + } \ + timeout { + set err_str "Function copy_file timed out while copying $in_filename" + } + } + if { $err_str != "OK" } { + return $err_str + } else { + set err_str $default_err_str + } + } + + # ^Z\r to complete the transfer. + send $CTRLZ + send "\r" + expect { + "file(s) copied." { + set err_str [expect_prompt $remote_prompt] + } \ + $remote_prompt { + set err_str $default_err_str + } \ + timeout { + expect_prompt $remote_prompt + set err_str "Function copy_file timed out while finishing copy of $in_filename" + } + } + return $err_str +} + +# This function waits for the command prompt and reports an error on +# timeout. +proc expect_prompt { remote_prompt } { + set default_err_str "Unknown error occurred while waiting for the command prompt" + set err_str $default_err_str + + expect { + $remote_prompt { + set err_str "OK" + } \ + timeout { + set err_str "Timeout occurred while waiting for the command prompt" + } + } + return $err_str +} + +# This function will create a telnet login shell to $remote_host as $username. +# If expected dialogue is not recieved, return with a specific error if one +# is recognized. Otherwise return a generic error indicating the function +# name. +proc telnet_login { remote_prompt remote_host username password } { + + set default_err_str "Unknown error in function telnet_login" + set err_str $default_err_str + + set cmd "telnet $remote_host\r" + send $cmd + expect { + "login: " { + set err_str "OK" + } \ + "Connection refused" { + set err_str "Connection refused" + } \ + "No route to host" { + set err_str "No route to host" + } \ + timeout { + set err_str "Function telnet_login timed out while waiting for the login prompt" + } + } + if { $err_str != "OK" } { + # Return because something unexpected happened. + return $err_str + } else { + # Reset err_str + set err_str $default_err_str + } + + set cmd "$username\r" + send $cmd + expect { + "password: " { + set err_str "OK" + } \ + timeout { + set err_str "Function telnet_login timed out while waiting for the password prompt" + } + } + if { $err_str != "OK" } { + return $err_str + } else { + set err_str $default_err_str + } + + set cmd "$password\r" + send $cmd + expect { + $remote_prompt { + set err_str "OK" + } \ + "Login Failed" { + set err_str "Telnet login failed" + } \ + timeout { + set err_str "Function telnet_login timed out while waiting for the command prompt" + } + } + return $err_str +} + +proc create_directory { remote_prompt sharepath } { + + set default_err_str "Unknown error in function create_directory" + set err_str $default_err_str + + set cmd "mkdir $sharepath\r\n" + send $cmd + expect { + "already exists" { + expect_prompt $remote_prompt + set err_str "Directory already exists" + } \ + $remote_prompt { + set err_str "OK" + } \ + timeout { + expect_prompt $remote_prompt + set err_str "Timeout reached starting create_directory." + } + } + return $err_str +} + +proc delete_directory { remote_prompt sharepath } { + + set default_err_str "Unknown error in function delete_directory" + set err_str $default_err_str + + set cmd "rmdir /S /Q $sharepath\r\n" + send $cmd + expect { + "Access is denied." { + expect_prompt $remote_prompt + set err_str "Directory access is denied" + } \ + $remote_prompt { + set err_str "OK" + } \ + timeout { + expect_prompt $remote_prompt + set err_str "Timeout reached in delete_directory" + } + } + return $err_str +} + +proc create_share { remote_prompt username sharepath sharename } { + + set default_err_str "Unknown error in function create_share" + set err_str $default_err_str + + set cmd "net share $sharename=$sharepath /GRANT:$username,FULL\r\n" + send $cmd + expect { + "was shared successfully." { + set err_str [expect_prompt $remote_prompt] + } \ + "NET HELPMSG 2118." { + expect_prompt $remote_prompt + set err_str "The name has already been shared" + } \ + $remote_prompt { + set err_str $default_err_str + } \ + timeout { + expect_prompt $remote_prompt + set err_str "Timeout reached in create_share" + } + } + return $err_str +} + +proc delete_share { remote_prompt sharename } { + + set default_err_str "Unknown error in function delete_share" + set err_str $default_err_str + + set cmd "net share $sharename /DELETE\r\n" + send $cmd + expect { + "was deleted successfully." { + set err_str [expect_prompt $remote_prompt] + } \ + "does not exist." { + expect_prompt $remote_prompt + set err_str "The share does not exist" + } \ + $remote_prompt { + set err_str $default_err_str + } \ + timeout { + expect_prompt $remote_prompt + set err_str "Timeout reached in delete_share" + } + } + return $err_str +} + +proc delete_hosts_entry { remote_prompt hosts_file_path backup_hosts_filename } { + + set default_err_str "Unknown error in function delete_hosts_entry" + set err_str $default_err_str + + set cmd "cd $hosts_file_path\r\n" + send $cmd + expect { + "." { + expect_prompt $remote_prompt + set err_str $default_err_str + } \ + $remote_prompt { + set err_str "OK" + } \ + timeout { + expect_prompt $remote_prompt + set err_str "Timeout reached in delete_hosts_entry" + } + } + if { $err_str != "OK" } { + return $err_str + } else { + set err_str $default_err_str + } + + set cmd "move /Y $backup_hosts_filename hosts\r\n" + send $cmd + expect { + "1 file(s) moved." { + set err_str [expect_prompt $remote_prompt] + } \ + "cannot find the file specified." { + expect_prompt $remote_prompt + set err_str "File not found" + } \ + $remote_prompt { + set err_str $default_err_str + } \ + timeout { + expect_prompt $remote_prompt + set err_str "Function delete_hosts_entry timed out while renaming $backup_hosts_filename" + } + } + return $err_str +} + +proc create_hosts_entry { remote_prompt hosts_file_path hostname ip \ + backup_hosts_filename } { + + set default_err_str "Unknown error in function create_hosts_entry" + set err_str $default_err_str + + set cmd "cd $hosts_file_path\r\n" + send $cmd + expect { + "." { + expect_prompt $remote_prompt + set err_str $default_err_str + } \ + $remote_prompt { + set err_str "OK" + } \ + timeout { + expect_prompt $remote_prompt + set err_str "Timeout reached in create_hosts_entry" + } + } + if { $err_str != "OK" } { + return $err_str + } else { + set err_str $default_err_str + } + + set cmd "copy /Y hosts $backup_hosts_filename\r\n" + send $cmd + expect { + "1 file(s) copied." { + set err_str [expect_prompt $remote_prompt] + } \ + "cannot find the file specified." { + expect_prompt $remote_prompt + set err_str "File not found." + } \ + $remote_prompt { + set err_str $default_err_str + } \ + timeout { + expect_prompt $remote_prompt + set err_str "Function create_hosts_entry timed out while copying hosts file" + } + } + if { $err_str != "OK" } { + return $err_str + } else { + set err_str $default_err_str + } + + set cmd "echo $ip $hostname #smbtorture host. >> hosts\r\n" + send $cmd + expect { + $remote_prompt { + set err_str "OK" + } \ + timeout { + expect_prompt $remote_prompt + set err_str "Function create_hosts timed out while updating hosts file" + } + } + return $err_str +} diff --git a/source4/selftest/win/test_win.conf b/source4/selftest/win/test_win.conf new file mode 100644 index 0000000000..ed52be999e --- /dev/null +++ b/source4/selftest/win/test_win.conf @@ -0,0 +1,83 @@ + +# perl needs to know to look in $WINTEST_DIR for VMHost.pm. +export PERLLIB=$WINTEST_DIR + +# Command prompt that we are expecting on the windows host. +export SMBTORTURE_REMOTE_PROMPT=">" + +## +## The variables in this section apply to the 'make wintest_dc' set of tests. +## + +# A username and password with admin rights to the DC we're testing against. +export WIN2K3_DC_USERNAME="tortureuser" +export WIN2K3_DC_PASSWORD="torturepass" + +# The domain and realm that the DC is configured for. +export WIN2K3_DC_DOMAIN="WINTESTDC" +export WIN2K3_DC_REALM="wintest.dc" + +# The path to the DC vmware image config file, local to the vmware server. +export WIN2K3_DC_VM_CFG_PATH="/var/lib/vmware/Virtual Machines/Windows Server 2003 DC BuildFarm/Windows 2003 DC BuildFarm.vmx" + +## +## The parameters in this section apply to the 'make wintest' set of tests. +## + +# The username and password we will be testing with. +# This user will need admin rights on the remote windows host. +export SMBTORTURE_USERNAME="tortureuser" +export SMBTORTURE_PASSWORD="torturepass" + +# The name of the workgroup we will be using on the remote windows host. +export SMBTORTURE_WORKGROUP="SMBTEST" + +# The name of and path to the windows share we'll be testing against. +export SMBTORTURE_REMOTE_SHARE_NAME="smbtorture_share" +export SMBTORTURE_REMOTE_SHARE_PATH="%HOMEDRIVE%\smbtorture_shared_dir" + +# Default timeout for the expect scripts to wait for a response from the remote. +export SMBTORTURE_EXPECT_TIMEOUT=30 + +# Path to the local smbtorture binary. +export SMBTORTURE_BIN_PATH="bin/smbtorture" + +# Local system hostname and ip address we'll be adding to the remote's +# hosts file. +export SMBTORTURE_LOCAL_HOSTNAME=$NETBIOSNAME +export SMBTORTURE_LOCAL_IP="192.168.100.12" + +# Filename of the windows hosts' unedited hosts file. +export REMOTE_BACKUP_HOSTS_FILENAME="hosts.smbtorture" +export REMOTE_HOSTS_FILE_PATH="%SYSTEMROOT%\\system32\\drivers\\etc" + +# These coincide with the parameters mktestsetup.sh uses to setup smbd. +export SMBTORTURE_LOCAL_USERNAME="administrator" +export SMBTORTURE_LOCAL_PASSWORD="penguin" +export SMBTORTURE_LOCAL_DOMAIN="SAMBADOMAIN" + +# This is the name of the samba share the windows vm will connect to. +export SMBTORTURE_LOCAL_SHARE_NAME="TMP" + +# This is the drive letter which will be used to mount a share on the windows vm. +export SMBTORTURE_REMOTE_DRIVE_LETTER="X:" + +# This is the name of the file which will be created on the windows vm +# and used for samba server tests. +export SMBTORTURE_TMP_FILENAME="smbtorture.tmp" + +# The path to the vmware image config file local to the vmware server. +export VM_CFG_PATH="/var/lib/vmware/Virtual Machines/Win2k3-BuildFarm/Win2k3-BuildFarm.vmx" + +# In order to copy files and execute programs on the guest vm, +# we need administrator-level credentials to log in with. +export GUEST_ADMIN_USERNAME="administrator" +export GUEST_ADMIN_PASSWORD="adminpass" + +# These parameters are optional. If not specified, the script tries to access +# a local vmware server as the executing user. +# logged-in user running the script are used. +export HOST_SERVER_NAME="vmhost" +export HOST_SERVER_PORT=902 +export HOST_USERNAME="vmuser" +export HOST_PASSWORD="vmpass" diff --git a/source4/selftest/win/vm_get_ip.pl b/source4/selftest/win/vm_get_ip.pl new file mode 100644 index 0000000000..9657a34790 --- /dev/null +++ b/source4/selftest/win/vm_get_ip.pl @@ -0,0 +1,48 @@ +#!/usr/bin/perl -w + +# A perl script to connect to a VMware server and get the IP address of a VM. +# Copyright Brad Henry <brad@samba.org> 2006 +# Released under the GNU GPL version 3 or later. + +use VMHost; + +sub check_error { + my $vm = VMHost; + my $custom_err_str = ""; + ($vm, $custom_err_str) = @_; + + my ($err_code, $err_str) = $vm->error; + if ($err_code != 0) { + undef $vm; + die $custom_err_str . "Returned $err_code: $err_str.\n"; + } +} + +# Read in parameters from environment. +my $vm_cfg_path = $ENV{"$ARGV[0]"}; +my $host_server_name = $ENV{'HOST_SERVER_NAME'}; +my $host_server_port = $ENV{'HOST_SERVER_PORT'}; +if (!defined($host_server_port)) { + $host_server_port = 902; +} + +my $host_username = $ENV{'HOST_USERNAME'}; +my $host_password = $ENV{'HOST_PASSWORD'}; +my $guest_admin_username = $ENV{'GUEST_ADMIN_USERNAME'}; +my $guest_admin_password = $ENV{'GUEST_ADMIN_PASSWORD'}; + +my $vm = VMHost; + +$vm->host_connect($host_server_name, $host_server_port, $host_username, + $host_password, $vm_cfg_path, $guest_admin_username, + $guest_admin_password); +check_error($vm, "Error in \$vm->host_connect().\n"); + +my $guest_ip = $vm->get_guest_ip(); +check_error($vm, "Error in \$vm->get_guest_ip().\n"); + +print $guest_ip; + +undef $vm; + +exit 0; diff --git a/source4/selftest/win/vm_load_snapshot.pl b/source4/selftest/win/vm_load_snapshot.pl new file mode 100644 index 0000000000..35e80badce --- /dev/null +++ b/source4/selftest/win/vm_load_snapshot.pl @@ -0,0 +1,46 @@ +#!/usr/bin/perl -w + +# A perl script to connect to a VMware server and revert a VM snapshot. +# Copyright Brad Henry <brad@samba.org> 2006 +# Released under the GNU GPL version 3 or later. + +use VMHost; + +sub check_error { +my $vm = VMHost; + my $custom_err_str = ""; + ($vm, $custom_err_str) = @_; + + my ($err_code, $err_str) = $vm->error; + if ($err_code != 0) { + undef $vm; + die $custom_err_str . "Returned $err_code: $err_str.\n"; + } +} + +# Read in parameters from environment. +my $vm_cfg_path = $ENV{'VM_CFG_PATH'}; +my $host_server_name = $ENV{'HOST_SERVER_NAME'}; +my $host_server_port = $ENV{'HOST_SERVER_PORT'}; +if (!defined($host_server_port)) { + $host_server_port = 902; +} + +my $host_username = $ENV{'HOST_USERNAME'}; +my $host_password = $ENV{'HOST_PASSWORD'}; +my $guest_admin_username = $ENV{'GUEST_ADMIN_USERNAME'}; +my $guest_admin_password = $ENV{'GUEST_ADMIN_PASSWORD'}; + +my $vm = VMHost; + +$vm->host_connect($host_server_name, $host_server_port, $host_username, + $host_password, $vm_cfg_path, $guest_admin_username, + $guest_admin_password); +check_error($vm, "Error in \$vm->host_connect().\n"); + +$vm->revert_snapshot(); +check_error($vm, "Error in \$vm->revert_snapshot().\n"); + +undef $vm; + +exit 0; diff --git a/source4/selftest/win/wintest_2k3_dc.sh b/source4/selftest/win/wintest_2k3_dc.sh new file mode 100755 index 0000000000..ed964a274a --- /dev/null +++ b/source4/selftest/win/wintest_2k3_dc.sh @@ -0,0 +1,114 @@ +#!/bin/sh + +if [ $# -lt 1 ]; then +cat <<EOF +Usage: wintest_2k3_dc.sh TESTGROUP +EOF +exit 1; +fi + +TESTGROUP=$1 + +if [ -z $WINTEST_DIR ]; then + echo "Environment variable WINTEST_DIR not found." + exit 1; +fi + +# This variable is defined in the per-hosts .fns file for build-farm hosts that run windows tests. +if [ -z $WINTESTCONF ]; then + echo "Please point environment variable WINTESTCONF to your test_win.conf file." + exit 1; +fi + +. $WINTESTCONF +. $WINTEST_DIR/wintest_functions.sh + +export WIN2K3_DC_REMOTE_HOST=`perl -I$WINTEST_DIR $WINTEST_DIR/vm_get_ip.pl WIN2K3_DC_VM_CFG_PATH` + +if [ -z $WIN2K3_DC_REMOTE_HOST ]; then + # Restore snapshot to ensure VM is in a known state, then exit. + restore_snapshot "Test failed to get the IP address of the windows 2003 DC." "$WIN2K3_DC_VM_CFG_PATH" + exit 1; +fi + +server=$WIN2K3_DC_REMOTE_HOST +username=$WIN2K3_DC_USERNAME +password=$WIN2K3_DC_PASSWORD +domain=$WIN2K3_DC_DOMAIN +realm=$WIN2K3_DC_REALM + +OPTIONS="-U$username%$password -W $domain --option realm=$realm" + +all_errs=0 + +on_error() { + name=$1 + + all_errs=`expr $all_errs + 1` + restore_snapshot "$name test failed." "$WIN2K3_DC_VM_CFG_PATH" +} + +drsuapi_tests() { + + name="RPC-DRSUAPI on ncacn_ip_tcp with seal" + bin/smbtorture \ + ncacn_ip_tcp:$server[seal] $OPTIONS \ + RPC-DRSUAPI || on_error "$name" + + name="RPC-DRSUAPI on ncacn_ip_tcp with seal,bigendian" + bin/smbtorture \ + ncacn_ip_tcp:$server[seal,bigendian] $OPTIONS \ + RPC-DRSUAPI || on_error "$name" +} + +spoolss_tests() { + + name="RPC-SPOOLSS on ncacn_np" + bin/smbtorture \ + ncacn_np:$server $OPTIONS \ + RPC-SPOOLSS || on_error "$name" +} + +ncacn_ip_tcp_tests() { + bindopt=$1 + transport="ncacn_ip_tcp" + tests="RPC-SCHANNEL RPC-EPMAPPER RPC-SAMR RPC-NETLOGON RPC-LSA RPC-SAMLOGON RPC-SAMSYNC RPC-MULTIBIND" + + for bindoptions in $bindopt; do + for t in $tests; do + name="$t on $transport with $bindoptions" + bin/smbtorture $TORTURE_OPTIONS \ + $transport:$server[$bindoptions] \ + $OPTIONS $t || on_error "$name" + done + done +} + +ncacn_np_tests() { + bindopt=$1 + transport="ncacn_np" + tests="RPC-SCHANNEL RPC-DSSETUP RPC-EPMAPPER RPC-SAMR RPC-WKSSVC RPC-SRVSVC RPC-EVENTLOG RPC-NETLOGON RPC-LSA RPC-SAMLOGON RPC-SAMSYNC RPC-MULTIBIND RPC-WINREG" + + for bindoptions in $bindopt; do + for t in $tests; do + name="$t on $transport with $bindoptions" + bin/smbtorture $TORTURE_OPTIONS \ + $transport:$server[$bindoptions] \ + $OPTIONS $t || on_error "$name" + done + done +} + +bindoptions="padcheck connect sign seal ntlm,sign ntml,seal $VALIDATE bigendian" + +case $TESTGROUP in + RPC-DRSUAPI) drsuapi_tests ;; + RPC-SPOOLSS) spoolss_tests ;; + ncacn_ip_tcp) ncacn_ip_tcp_tests $bindoptions ;; + ncacn_np) ncacn_np_tests $bindoptions ;; + *) echo "$TESTGROUP is not a known set of tests." + exit 1; + ;; +esac + +exit $all_errs diff --git a/source4/selftest/win/wintest_base.sh b/source4/selftest/win/wintest_base.sh new file mode 100755 index 0000000000..b78da4f7c9 --- /dev/null +++ b/source4/selftest/win/wintest_base.sh @@ -0,0 +1,67 @@ +#!/bin/sh + +. selftest/test_functions.sh + +. selftest/win/wintest_functions.sh + +# This variable is defined in the per-hosts .fns file. +. $WINTESTCONF + +if [ $# -lt 4 ]; then +cat <<EOF +Usage: test_net.sh SERVER USERNAME PASSWORD DOMAIN +EOF +exit 1; +fi + +server="$1" +username="$2" +password="$3" +domain="$4" +shift 4 + +export SMBTORTURE_REMOTE_HOST=$server + +base_tests="BASE-UNLINK BASE-ATTR BASE-DELETE BASE-TCON BASE-OPEN BASE-CHKPATH" + +all_errs=0 +err=0 + +on_error() { + errstr=$1 + + all_errs=`expr $all_errs + 1` + restore_snapshot $errstr "$VM_CFG_PATH" +} + +for t in $base_tests; do + test_name="$t / WINDOWS SERVER" + echo -e "\n$test_name SETUP PHASE" + + setup_share_test + + if [ $err_rtn -ne 0 ]; then + # If test setup fails, load VM snapshot and skip test. + on_error "\n$test_name setup failed, skipping test." + else + echo -e "\n$test_name setup completed successfully." + + $SMBTORTURE_BIN_PATH -U $username%$password \ + -W $domain //$server/$SMBTORTURE_REMOTE_SHARE_NAME \ + $t || err=1 + if [ $err -ne 0 ]; then + on_error "\n$test_name failed." + else + echo -e "\n$test_name CLEANUP PHASE" + remove_share_test + if [ $err_rtn -ne 0 ]; then + # If cleanup fails, restore VM snapshot. + on_error "\n$test_name removal failed." + else + echo -e "\n$test_name removal completed successfully." + fi + fi + fi +done + +exit $all_errs diff --git a/source4/selftest/win/wintest_client.exp b/source4/selftest/win/wintest_client.exp new file mode 100644 index 0000000000..ccf5d06ea9 --- /dev/null +++ b/source4/selftest/win/wintest_client.exp @@ -0,0 +1,95 @@ +# An expect script to create a temporary file, map a share, copy the file to the share, +# and compare the contents of the two files. +# Copyright Brad Henry <brad@samba.org> 2006 +# Released under the GNU GPL version 3 or later. + +proc run_test { remote_prompt tmp_filename share_drive host_drive buildhost_ip buildhost_share username domain password } { + + # Create the temp file on the windows host and connect to the samba share. + set host_tmpfile "$host_drive\\$tmp_filename" + set err_str [create_tmp_file $remote_prompt $host_tmpfile] + if { $err_str != "OK" } { + return $err_str + } + + set buildhost_sharepoint "\\\\$buildhost_ip\\$buildhost_share" + set err_str [map_share $remote_prompt $share_drive $buildhost_sharepoint $username $domain $password] + if { $err_str != "OK" } { + return $err_str + } + + # Copy the temp file to the share and compare its contents with the original. + set share_tmpfile "$share_drive\\$tmp_filename" + set xcopy_options "" + set err_str [xcopy_file $remote_prompt $host_tmpfile $share_tmpfile $xcopy_options] + if { $err_str != "OK" } { + return $err_str + } + + set err_str [compare_files $remote_prompt $host_tmpfile $share_tmpfile] + if { $err_str != "OK" } { + return $err_str + } + + # Remove files and unmap share. + set err_str [delete_file $remote_prompt $share_tmpfile] + if { $err_str != "OK" } { + return $err_str + } + set err_str [delete_file $remote_prompt $host_tmpfile] + if { $err_str != "OK" } { + return $err_str + } + + set err_str [unmap_share $remote_prompt $share_drive] + if {$err_str != "OK" } { + return $err_str + } + + return $err_str +} + +# Read parameters. +set remote_prompt $env(SMBTORTURE_REMOTE_PROMPT) +set remote_host $env(SMBTORTURE_REMOTE_HOST) +set username $env(SMBTORTURE_USERNAME) +set password $env(SMBTORTURE_PASSWORD) +set timeout $env(SMBTORTURE_EXPECT_TIMEOUT) + +set tmp_filename $env(SMBTORTURE_TMP_FILENAME) + +set share_drive $env(SMBTORTURE_REMOTE_DRIVE_LETTER) +set host_drive "%HOMEDRIVE%" + +set buildhost_ip $env(SMBTORTURE_LOCAL_IP) +set buildhost_share $env(SMBTORTURE_LOCAL_SHARE_NAME) +set buildhost_username $env(SMBTORTURE_LOCAL_USERNAME) +set buildhost_domain $env(SMBTORTURE_LOCAL_DOMAIN) +set buildhost_password $env(SMBTORTURE_LOCAL_PASSWORD) + +set err_val [spawn $env(SHELL)] +if {$err_val == 0} { + puts stderr "Expect failed while spawning a shell process." + exit $err_val +} + +set err_str [telnet_login $remote_prompt $remote_host $username $password] +if {$err_str != "OK"} { + puts stderr "\nFunction telnet_login failed during Samba server testing." + puts stderr "Error was: $err_str." + exit 1 +} + +set err_str [run_test $remote_prompt $tmp_filename $share_drive $host_drive $buildhost_ip $buildhost_share $buildhost_username $buildhost_domain $buildhost_password] +if {$err_str != "OK"} { + puts stderr "\nFunction run_test failed during Samba server testing." + puts stderr "Error was: $err_str." + + # Log off from the telnet server. + send "exit\r\n" + exit 1 +} + +# Log off from the telnet server. +send "exit\r\n" +exit 0 diff --git a/source4/selftest/win/wintest_client.sh b/source4/selftest/win/wintest_client.sh new file mode 100755 index 0000000000..6b76ae36f7 --- /dev/null +++ b/source4/selftest/win/wintest_client.sh @@ -0,0 +1,26 @@ +#!/bin/sh + +. selftest/test_functions.sh + +. selftest/win/wintest_functions.sh + +# This variable is defined in the per-hosts .fns file. +. $WINTESTCONF + +export SMBTORTURE_REMOTE_HOST=$1 + +test_name="WINDOWS CLIENT / SAMBA SERVER SHARE" + +cat $WINTEST_DIR/common.exp > $TMPDIR/client_test.exp +cat $WINTEST_DIR/wintest_client.exp >> $TMPDIR/client_test.exp + +expect $TMPDIR/client_test.exp || all_errs=`expr $all_errs + 1` + +if [ $all_errs > 0 ]; then + # Restore snapshot to ensure VM is in a known state. + restore_snapshot "\n$test_name failed." "$VM_CFG_PATH" +fi + +rm -f $TMPDIR/client_test.exp + +exit $all_errs diff --git a/source4/selftest/win/wintest_functions.sh b/source4/selftest/win/wintest_functions.sh new file mode 100755 index 0000000000..3c0a1dccac --- /dev/null +++ b/source4/selftest/win/wintest_functions.sh @@ -0,0 +1,54 @@ +#!/bin/sh + +# Setup the windows environment. +# This was the best way I could figure out including library files +# for the moment. +# I was finding that "cat common.exp wintest_setup.exp | expect -f -" +# fails to run, but exits with 0 status something like 1% of the time. + +setup_share_test() +{ + echo -e "\nSetting up windows environment." + cat $WINTEST_DIR/common.exp > $TMPDIR/setup.exp + cat $WINTEST_DIR/wintest_setup.exp >> $TMPDIR/setup.exp + expect $TMPDIR/setup.exp + err_rtn=$? + rm -f $TMPDIR/setup.exp +} + +# Clean up the windows environment after the test has run or failed. +remove_share_test() +{ + echo -e "\nCleaning up windows environment." + cat $WINTEST_DIR/common.exp > $TMPDIR/remove.exp + cat $WINTEST_DIR/wintest_remove.exp >> $TMPDIR/remove.exp + expect $TMPDIR/remove.exp + err_rtn=$? + rm -f $TMPDIR/remove.exp +} + +restore_snapshot() +{ + err_str=$1 + VMX_PATH=$2 + + # Display the error that caused us to restore the snapshot. + echo -e $err_str + + if [ -z $HOST_SERVER_NAME ]; then + # The vmware server is running locally. + vmrun revertToSnapshot "$VMX_PATH" + err_rtn=$? + else + vmrun -h $HOST_SERVER_NAME -P $HOST_SERVER_PORT \ + -u $HOST_USERNAME -p $HOST_PASSWORD \ + revertToSnapshot "$VMX_PATH" + err_rtn=$? + fi + + if [ $err_rtn -eq 0 ]; then + echo "Snapshot restored." + else + echo "Error $err_rtn restoring snapshot!" + fi +} diff --git a/source4/selftest/win/wintest_net.sh b/source4/selftest/win/wintest_net.sh new file mode 100755 index 0000000000..88cec1f3a7 --- /dev/null +++ b/source4/selftest/win/wintest_net.sh @@ -0,0 +1,62 @@ +#!/bin/sh + +. selftest/test_functions.sh + +. selftest/win/wintest_functions.sh + +# This variable is defined in the per-hosts .fns file. +. $WINTESTCONF + +if [ $# -lt 4 ]; then +cat <<EOF +Usage: test_net.sh SERVER USERNAME PASSWORD DOMAIN +EOF +exit 1; +fi + +server="$1" +username="$2" +password="$3" +domain="$4" +shift 4 + +ncacn_np_tests="NET-API-LOOKUP NET-API-LOOKUPHOST NET-API-RPCCONN-BIND NET-API-RPCCONN-SRV NET-API-RPCCONN-DC NET-API-RPCCONN-DCINFO NET-API-LISTSHARES" +#These tests fail on ncacn_np: NET-API-LOOKUPPDC NET-API-CREATEUSER NET-API-DELETEUSER + +ncalrpc_tests="NET-API-RPCCONN-SRV NET-API-RPCCONN-DC NET-API-RPCCONN-DCINFO NET-API-LISTSHARES" +#These tests fail on ncalrpc: NET-API-CREATEUSER NET-API-DELETEUSER + +ncacn_ip_tcp_tests="NET-API-LOOKUP NET-API-LOOKUPHOST NET-API-RPCCONN-SRV NET-API-RPCCONN-DC NET-API-RPCCONN-DCINFO NET-API-LISTSHARES" +#These tests fail on ncacn_ip_tcp: NET-API-LOOKUPPDC NET-API-CREATEUSER NET-API-DELETEUSER + +bind_options="seal,padcheck bigendian" + +test_type="ncalrpc ncacn_np ncacn_ip_tcp" + +all_errs=0 + +on_error() { + errstr=$1 + + all_errs=`expr $all_errs + 1` + restore_snapshot "$errstr" "$VM_CFG_PATH" +} + +for o in $bind_options; do + for transport in $test_type; do + case $transport in + ncalrpc) net_test=$ncalrpc_tests ;; + ncacn_np) net_test=$ncacn_np_tests ;; + ncacn_ip_tcp) net_test=$ncacn_ip_tcp_tests ;; + esac + + for t in $net_test; do + test_name="$t on $transport with $o" + $SMBTORTURE_BIN_PATH -U $username%$password \ + -W $domain $transport:$server[$o] \ + $t || on_error "\n$test_name failed." + done + done +done + +exit $all_errs diff --git a/source4/selftest/win/wintest_raw.sh b/source4/selftest/win/wintest_raw.sh new file mode 100755 index 0000000000..541e19829a --- /dev/null +++ b/source4/selftest/win/wintest_raw.sh @@ -0,0 +1,68 @@ +#!/bin/sh + +. selftest/test_functions.sh + +. selftest/win/wintest_functions.sh + +# This variable is defined in the per-hosts .fns file. +. $WINTESTCONF + +if [ $# -lt 4 ]; then +cat <<EOF +Usage: test_net.sh SERVER USERNAME PASSWORD DOMAIN +EOF +exit 1; +fi + +server="$1" +username="$2" +password="$3" +domain="$4" +shift 4 + +export SMBTORTURE_REMOTE_HOST=$server + +raw_tests="RAW-QFILEINFO RAW-SFILEINFO RAW-MKDIR RAW-SEEK RAW-OPEN RAW-WRITE RAW-UNLINK RAW-READ RAW-CLOSE RAW-IOCTL RAW-RENAME RAW-EAS RAW-STREAMS" +# This test fails: RAW-QFSINFO + +all_errs=0 +err=0 + +on_error() { + errstr=$1 + all_errs=`expr $all_errs + 1` + + restore_snapshot "$errstr" "$VM_CFG_PATH" +} + +for t in $raw_tests; do + test_name="$t / WINDOWS SERVER" + echo -e "\n$test_name SETUP PHASE" + + setup_share_test + + if [ $err_rtn -ne 0 ]; then + # If test setup fails, load VM snapshot and skip test. + on_error "\n$test_name setup failed, skipping test." + else + echo -e "\n$test_name setup completed successfully." + + $SMBTORTURE_BIN_PATH -U $username%$password -W $domain \ + //$server/$SMBTORTURE_REMOTE_SHARE_NAME \ + $t || err=1 + if [ $err -ne 0 ]; then + on_error "\n$test_name failed." + else + echo -e "\n$test_name CLEANUP PHASE" + remove_share_test + if [ $err_rtn -ne 0 ]; then + # If cleanup fails, restore VM snapshot. + on_error "\n$test_name removal failed." + else + echo -e "\n$test_name removal completed successfully." + fi + fi + fi +done + +exit $all_errs diff --git a/source4/selftest/win/wintest_remove.exp b/source4/selftest/win/wintest_remove.exp new file mode 100644 index 0000000000..36dc4a7d24 --- /dev/null +++ b/source4/selftest/win/wintest_remove.exp @@ -0,0 +1,71 @@ +# An expect script to remove a directory and share which was +# previously setup for an smbtorture test. +# Copyright Brad Henry <brad@samba.org> 2006 +# Released under the GNU GPL version 3 or later. + +proc remove_test { remote_prompt sharepath sharename hosts_file_path \ + backup_hosts_filename } { + + set err_str [delete_share $remote_prompt $sharename] + if { $err_str != "OK" } { + puts stderr "Error in function delete_share: $err_str." + puts stderr "Function remove_test will continue." + } + + set err_str [delete_directory $remote_prompt $sharepath] + if { $err_str != "OK" } { + puts stderr "Error in function delete_directory: $err_str." + puts stderr "Function remove_test will continue." + } + + # Overwrite the current hosts file with the backup we made during setup. + set err_str [delete_hosts_entry $remote_prompt $hosts_file_path \ + $backup_hosts_filename] + if { $err_str != "OK" } { + puts stderr "Error in function delete_hosts_entry: $err_str." + puts stderr "Function remove_test will continue." + } + return $err_str +} + +# read parameters +set remote_host $env(SMBTORTURE_REMOTE_HOST) +set remote_prompt $env(SMBTORTURE_REMOTE_PROMPT) + +set username $env(SMBTORTURE_USERNAME) +set password $env(SMBTORTURE_PASSWORD) + +set timeout $env(SMBTORTURE_EXPECT_TIMEOUT) + +set sharepath $env(SMBTORTURE_REMOTE_SHARE_PATH) +set sharename $env(SMBTORTURE_REMOTE_SHARE_NAME) + +set backup_hosts_filename $env(REMOTE_BACKUP_HOSTS_FILENAME) +set hosts_file_path $env(REMOTE_HOSTS_FILE_PATH) + +set err_val [spawn $env(SHELL)] +if {$err_val == 0} { + puts stderr "Expect failed while spawning a shell process." + exit $err_val +} + +set err_str [telnet_login $remote_prompt $remote_host $username $password] +if {$err_str != "OK"} { + puts stderr "\nFunction telnet_login failed during cleanup." + puts stderr "Error was: $err_str." + exit 1 +} + +set err_str [remove_test $remote_prompt $sharepath $sharename \ + $hosts_file_path $backup_hosts_filename] +if {$err_str != "OK"} { + puts stderr "\nFunction remove_test failed." + puts stderr "Error was: $err_str." + # Log off from the telnet server. + send "exit\r\n" + exit 1 +} + +# Log off from the telnet server. +send "exit\r\n" +exit 0 diff --git a/source4/selftest/win/wintest_rpc.sh b/source4/selftest/win/wintest_rpc.sh new file mode 100755 index 0000000000..d0a0783c2e --- /dev/null +++ b/source4/selftest/win/wintest_rpc.sh @@ -0,0 +1,66 @@ +#!/bin/sh + +. selftest/test_functions.sh + +. selftest/win/wintest_functions.sh + +# This variable is defined in the per-hosts .fns file. +. $WINTESTCONF + +if [ $# -lt 4 ]; then +cat <<EOF +Usage: test_rpc.sh SERVER USERNAME PASSWORD DOMAIN +EOF +exit 1; +fi + +server="$1" +username="$2" +password="$3" +domain="$4" +shift 4 + +ncacn_np_tests="RPC-SRVSVC RPC-UNIXINFO RPC-ECHO RPC-DSSETUP RPC-ALTERCONTEXT RPC-MULTIBIND" +# These tests fail on ncacn_np: RPC-SPOOLSS RPC-SCHANNEL RPC-JOIN RPC-LSA +# RPC-NETLOGON + +ncalrpc_tests="RPC-UNIXINFO RPC-ECHO" +# These tests fail on ncalrpc: RPC-SCHANNEL RPC-JOIN RPC-LSA RPC-DSSETUP +# RPC-ALTERCONTEXT RPC-MULTIBIND RPC-NETLOGON + +ncacn_ip_tcp_tests="RPC-UNIXINFO RPC-ECHO" +# These tests fail on ncacn_ip_tcp: RPC-SCHANNEL RPC-JOIN RPC-LSA RPC-DSSETUP +# RPC-ALTERCONTEXT RPC-MULTIBIND RPC-NETLOGON + +bind_options="seal,padcheck bigendian" + +test_type="ncalrpc ncacn_np ncacn_ip_tcp" + +all_errs=0 + +on_error() { + errstr=$1 + all_errs=`expr $all_errs + 1` + + restore_snapshot "$errstr" "$VM_CFG_PATH" +} + +for o in $bind_options; do + for transport in $test_type; do + case $transport in + ncalrpc) rpc_test=$ncalrpc_tests ;; + ncacn_np) rpc_test=$ncacn_np_tests ;; + ncacn_ip_tcp) rpc_test=$ncacn_ip_tcp_tests ;; + esac + + for t in $rpc_test; do + test_name="$t on $transport with $o" + + $SMBTORTURE_BIN_PATH -U $username%$password \ + -W $domain $transport:$server[$o] \ + $t || on_error "\n$test_name failed." + done + done +done + +exit $all_errs diff --git a/source4/selftest/win/wintest_setup.exp b/source4/selftest/win/wintest_setup.exp new file mode 100644 index 0000000000..f55732ac00 --- /dev/null +++ b/source4/selftest/win/wintest_setup.exp @@ -0,0 +1,104 @@ +# An expect script to setup a directory and share for an smbtorture test. +# Copyright Brad Henry <brad@samba.org> 2006 +# Released under the GNU GPL version 3 or later. + +proc setup_test { remote_prompt sharepath sharename username local_hostname \ + local_ip hosts_file_path backup_hosts_filename } { + + # If creating the directory fails, remove, then + # re-create the directory. + set err_str [create_directory $remote_prompt $sharepath] + if { $err_str != "OK" } { + if { $err_str != "Directory already exists" } { + puts stderr "\nUnexpected error occured in setup_test.\n" + puts stderr "Function create_directory returned $err_str." + } else { + puts stdout "\nDirectory $sharepath exists." + } + puts stdout "Re-creating directory $sharepath." + + set err_str [delete_directory $remote_prompt $sharepath] + if { $err_str != "OK" } { + return $err_str + } + set err_str [create_directory $remote_prompt $sharepath] + if { $err_str != "OK" } { + return $err_str + } + } + + # If creating the share fails, remove, then + # re-create the share. + set err_str [create_share $remote_prompt $username $sharepath \ + $sharename] + if { $err_str != "OK" } { + if { $err_str != "The name has already been shared" } { + puts stderr "\nUnexpected error occured in setup_test." + puts stderr "Function create_share returned $err_str." + } else { + puts stdout "\nShare $sharename exists." + } + puts stdout "Re-creating share $sharename." + + set err_str [delete_share $remote_prompt $sharename] + if { $err_str != "OK" } { + return $err_str + } + set err_str [create_share $remote_prompt $username $sharepath \ + $sharename] + if { $err_str != "OK" } { + return $err_str + } + } + + # Add a hosts file entry on the windows machine for the smbtorture host. + set err_str [create_hosts_entry $remote_prompt $hosts_file_path \ + $local_hostname $local_ip $backup_hosts_filename] + return $err_str +} + +# Read parameters. +set remote_host $env(SMBTORTURE_REMOTE_HOST) +set remote_prompt $env(SMBTORTURE_REMOTE_PROMPT) + +set username $env(SMBTORTURE_USERNAME) +set password $env(SMBTORTURE_PASSWORD) + +set timeout $env(SMBTORTURE_EXPECT_TIMEOUT) + +set sharepath $env(SMBTORTURE_REMOTE_SHARE_PATH) +set sharename $env(SMBTORTURE_REMOTE_SHARE_NAME) + +set local_hostname $env(SMBTORTURE_LOCAL_HOSTNAME) +set local_ip $env(SMBTORTURE_LOCAL_IP) + +set backup_hosts_filename $env(REMOTE_BACKUP_HOSTS_FILENAME) +set hosts_file_path $env(REMOTE_HOSTS_FILE_PATH) + +set err_val [spawn $env(SHELL)] +if {$err_val == 0} { + puts stderr "Expect failed while spawning a shell process." + exit $err_val +} + +set err_str [telnet_login $remote_prompt $remote_host $username $password] +if {$err_str != "OK"} { + puts stderr "\nFunction telnet_login failed during setup." + puts stderr "Error was: $err_str." + exit 1 +} + +set err_str [setup_test $remote_prompt $sharepath $sharename $username \ + $local_hostname $local_ip $hosts_file_path \ + $backup_hosts_filename] +if {$err_str != "OK"} { + puts stderr "\nFunction setup_test failed during setup." + puts stderr "Error was: $err_str." + # Log off from the telnet server. + send "exit\r\n" + exit 1 +} + +# Log off from the telnet server. +send "exit\r\n" +exit 0 |