diff options
Diffstat (limited to 'test/run')
-rw-r--r-- | test/run | 193 |
1 files changed, 153 insertions, 40 deletions
@@ -1,11 +1,23 @@ -#!/usr/bin/perl +#!/usr/bin/perl -w -U + +# +# Possible improvements: +# +# - distinguish stdout and stderr output +# - add environment variable like assignments +# - run up to a specific line +# - resume at a specific line +# use strict; use FileHandle; -use POSIX qw(geteuid getegid isatty); +use Getopt::Std; +use POSIX qw(isatty setuid); +use vars qw($opt_v); + +no warnings qw(taint); -my $owner = getpwuid(geteuid()); -my $group = getgrgid(getegid()); +getopts('v'); my ($OK, $FAILED) = ("ok", "failed"); if (isatty(fileno(STDOUT))) { @@ -13,19 +25,61 @@ if (isatty(fileno(STDOUT))) { $FAILED = "\033[31m\033[1m" . $FAILED . "\033[m"; } +sub exec_test($$); + my ($prog, $in, $out) = ([], [], []); -my $line = 0; +my $line_number = 0; my $prog_line; -my ($tests, $failed); +my ($tests, $failed) = (0,0); for (;;) { - my $script = <>; $line++; - $script =~ s/\@OWNER\@/$owner/g; - $script =~ s/\@GROUP\@/$group/g; - next if (defined($script) && $script =~ /^!/); - if (!defined($script) || $script =~ s/^\$ ?//) { - if (@$prog) { - #print "[$prog_line] \$ ", join(' ', @$prog), " -- "; + my $line = <>; $line_number++; + if (defined $line) { + # Substitute %VAR and %{VAR} with environment variables. + $line =~ s[%(?:(\w+)|\{(\w+)\})][$ENV{"$1$2"}]eg; + } + if (defined $line) { + if ($line =~ s/^\s*< ?//) { + push @$in, $line; + } elsif ($line =~ s/^\s*> ?//) { + push @$out, $line; + } else { + process_test($prog, $prog_line, $in, $out); + + $prog = []; + $prog_line = 0; + } + if ($line =~ s/^\s*\$ ?//) { + $line =~ s/\s+#.*//; # remove comments here... + $prog = [ map { s/\\(.)/$1/g; $_ } split /(?<!\\)\s+/, $line ]; + $prog_line = $line_number; + $in = []; + $out = []; + } + } else { + process_test($prog, $prog_line, $in, $out); + last; + } +} + +my $status = sprintf("%d commands (%d passed, %d failed)", + $tests, $tests-$failed, $failed); +if (isatty(fileno(STDOUT))) { + if ($failed) { + $status = "\033[31m\033[1m" . $status . "\033[m"; + } else { + $status = "\033[32m" . $status . "\033[m"; + } +} +print $status, "\n"; +exit $failed ? 1 : 0; + + +sub process_test($$$$) { + my ($prog, $prog_line, $in, $out) = @_; + + return unless @$prog; + my $p = [ @$prog ]; print "[$prog_line] \$ ", join(' ', map { s/\s/\\$&/g; $_ } @$p), " -- "; @@ -36,9 +90,6 @@ for (;;) { if (!defined($out->[$n]) || !defined($result->[$n]) || $out->[$n] ne $result->[$n]) { $good = 0; - #chomp $out->[$n]; - #chomp $result->[$n]; - #print "$out->[$n] != $result->[$n]"; } } $tests++; @@ -50,37 +101,88 @@ for (;;) { chomp $l; my $r = defined($result->[$n]) ? $result->[$n] : "~"; chomp $r; - print sprintf("%-37s | %-39s\n", $l, $r); + print sprintf("%-37s %s %-39s\n", $l, $l eq $r ? "|" : "?", $r); } + } elsif ($opt_v) { + print join('', @$result); } +} + + +sub su($) { + my ($user) = @_; + + $user ||= "root"; + + my ($login, $pass, $uid, $gid) = getpwnam($user) + or return [ "su: user $user does not exist\n" ]; + my @groups = (); + my $fh = new FileHandle("/etc/group") + or return [ "opening /etc/group: $!\n" ]; + while (<$fh>) { + chomp; + my ($group, $passwd, $gid, $users) = split /:/; + foreach my $u (split /,/, $users) { + push @groups, $gid + if ($user eq $u); } - #$prog = [ split /\s+/, $script ] if $script; - $prog = [ map { s/\\(.)/$1/g; $_ } split /(?<!\\)\s+/, $script ] if $script; - $prog_line = $line; - $in = []; - $out = []; - } elsif ($script =~ s/^> ?//) { - push @$in, $script; - } else { - push @$out, $script; } - last unless defined($script); + $fh->close; + + my $groups = join(" ", ($gid, $gid, @groups)); + #print STDERR "[[$groups]]\n"; + $! = 0; # reset errno + $> = 0; + $( = $gid; + $) = $groups; + if ($!) { + return [ "su: $!\n" ]; + } + if ($uid != 0) { + $> = $uid; + #$< = $uid; + if ($!) { + return [ "su: $prog->[1]: $!\n" ]; + } + } + #print STDERR "[($>,$<)($(,$))]"; + return []; } -my $status = sprintf("%d commands (%d passed, %d failed)", - $tests, $tests-$failed, $failed); -if (isatty(fileno(STDOUT))) { - if ($failed) { - $status = "\033[31m\033[1m" . $status . "\033[m"; - } else { - $status = "\033[32m" . $status . "\033[m"; - } + + +sub sg($) { + my ($group) = @_; + + my $gid = getgrnam($group) + or return [ "sg: group $group does not exist\n" ]; + my %groups = map { $_ eq $gid ? () : ($_ => 1) } (split /\s/, $)); + + #print STDERR "<<", join("/", keys %groups), ">>\n"; + my $groups = join(" ", ($gid, $gid, keys %groups)); + #print STDERR "[[$groups]]\n"; + $! = 0; # reset errno + if ($> != 0) { + my $uid = $>; + $> = 0; + $( = $gid; + $) = $groups; + $> = $uid; + } else { + $( = $gid; + $) = $groups; + } + if ($!) { + return [ "sg: $!\n" ]; + } + print STDERR "[($>,$<)($(,$))]"; + return []; } -print $status, "\n"; -exit $failed ? 1 : 0; + sub exec_test($$) { my ($prog, $in) = @_; local (*IN, *IN_DUP, *IN2, *OUT_DUP, *OUT, *OUT2); + my $needs_shell = (join('', @$prog) =~ /[][|<>"'`\$\*\?]/); if ($prog->[0] eq "umask") { umask oct $prog->[1]; @@ -90,6 +192,10 @@ sub exec_test($$) { return [ "chdir: $prog->[1]: $!\n" ]; } return []; + } elsif ($prog->[0] eq "su") { + return su($prog->[1]); + } elsif ($prog->[0] eq "sg") { + return sg($prog->[1]); } pipe *IN2, *OUT @@ -134,11 +240,15 @@ sub exec_test($$) { my $result = []; while (<IN>) { #print "< $_"; + if ($needs_shell) { + s#^/bin/sh: line \d+: ##; + } push @$result, $_; } return $result; } else { # Client + $< = $>; close IN or die "Can't close read end for input pipe: $!"; close OUT @@ -151,9 +261,12 @@ sub exec_test($$) { open STDERR, ">&STDOUT" or die "Can't join STDOUT and STDERR: $!"; - #print ERR_DUP "<", join(' ', @$prog), ">\n"; - exec @$prog; - print ERR_DUP $prog->[0], ": $!\n"; + if ($needs_shell) { + exec ('/bin/sh', '-c', join(" ", @$prog)); + } else { + exec @$prog; + } + print STDERR $prog->[0], ": $!\n"; exit; } } |