xref: /freebsd/tests/sys/acl/run (revision d0b2dbfa0ecf2bbc9709efc5e20baf8e4b44bbbf)
1b3af24b4SEnji Cooper#!/usr/bin/perl -w -U
2b3af24b4SEnji Cooper
3b3af24b4SEnji Cooper# Copyright (c) 2007, 2008 Andreas Gruenbacher.
4b3af24b4SEnji Cooper# All rights reserved.
5b3af24b4SEnji Cooper#
6b3af24b4SEnji Cooper# Redistribution and use in source and binary forms, with or without
7b3af24b4SEnji Cooper# modification, are permitted provided that the following conditions
8b3af24b4SEnji Cooper# are met:
9b3af24b4SEnji Cooper# 1. Redistributions of source code must retain the above copyright
10b3af24b4SEnji Cooper#    notice, this list of conditions, and the following disclaimer,
11b3af24b4SEnji Cooper#    without modification, immediately at the beginning of the file.
12b3af24b4SEnji Cooper# 2. The name of the author may not be used to endorse or promote products
13b3af24b4SEnji Cooper#    derived from this software without specific prior written permission.
14b3af24b4SEnji Cooper#
15b3af24b4SEnji Cooper# Alternatively, this software may be distributed under the terms of the
16b3af24b4SEnji Cooper# GNU Public License ("GPL").
17b3af24b4SEnji Cooper#
18b3af24b4SEnji Cooper# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19b3af24b4SEnji Cooper# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20b3af24b4SEnji Cooper# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21b3af24b4SEnji Cooper# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
22b3af24b4SEnji Cooper# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23b3af24b4SEnji Cooper# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24b3af24b4SEnji Cooper# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25b3af24b4SEnji Cooper# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26b3af24b4SEnji Cooper# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27b3af24b4SEnji Cooper# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28b3af24b4SEnji Cooper# SUCH DAMAGE.
29b3af24b4SEnji Cooper#
30b3af24b4SEnji Cooper#
31b3af24b4SEnji Cooper
32b3af24b4SEnji Cooper#
33b3af24b4SEnji Cooper# Possible improvements:
34b3af24b4SEnji Cooper#
35b3af24b4SEnji Cooper# - distinguish stdout and stderr output
36b3af24b4SEnji Cooper# - add environment variable like assignments
37b3af24b4SEnji Cooper# - run up to a specific line
38b3af24b4SEnji Cooper# - resume at a specific line
39b3af24b4SEnji Cooper#
40b3af24b4SEnji Cooper
41b3af24b4SEnji Cooperuse strict;
42b3af24b4SEnji Cooperuse FileHandle;
43b3af24b4SEnji Cooperuse Getopt::Std;
44b3af24b4SEnji Cooperuse POSIX qw(isatty setuid getcwd);
45b3af24b4SEnji Cooperuse vars qw($opt_l $opt_v);
46b3af24b4SEnji Cooper
47b3af24b4SEnji Cooperno warnings qw(taint);
48b3af24b4SEnji Cooper
49b3af24b4SEnji Cooper$opt_l = ~0;  # a really huge number
50b3af24b4SEnji Coopergetopts('l:v');
51b3af24b4SEnji Cooper
52b3af24b4SEnji Coopermy ($OK, $FAILED) = ("ok", "failed");
53b3af24b4SEnji Cooperif (isatty(fileno(STDOUT))) {
54b3af24b4SEnji Cooper	$OK = "\033[32m" . $OK . "\033[m";
55b3af24b4SEnji Cooper	$FAILED = "\033[31m\033[1m" . $FAILED . "\033[m";
56b3af24b4SEnji Cooper}
57b3af24b4SEnji Cooper
58b3af24b4SEnji Coopersub exec_test($$);
59b3af24b4SEnji Coopersub process_test($$$$);
60b3af24b4SEnji Cooper
61b3af24b4SEnji Coopermy ($prog, $in, $out) = ([], [], []);
62b3af24b4SEnji Coopermy $prog_line = 0;
63b3af24b4SEnji Coopermy ($tests, $failed) = (0,0);
64b3af24b4SEnji Coopermy $lineno;
65b3af24b4SEnji Coopermy $width = ($ENV{COLUMNS} || 80) >> 1;
66b3af24b4SEnji Cooper
67b3af24b4SEnji Cooperfor (;;) {
68b3af24b4SEnji Cooper  my $line = <>; $lineno++;
69b3af24b4SEnji Cooper  if (defined $line) {
70b3af24b4SEnji Cooper    # Substitute %VAR and %{VAR} with environment variables.
71b3af24b4SEnji Cooper    $line =~ s[%(\w+)][$ENV{$1}]eg;
72*8adbc737SEitan Adler    $line =~ s[%\{(\w+)\}][$ENV{$1}]eg;
73b3af24b4SEnji Cooper  }
74b3af24b4SEnji Cooper  if (defined $line) {
75b3af24b4SEnji Cooper    if ($line =~ s/^\s*< ?//) {
76b3af24b4SEnji Cooper      push @$in, $line;
77b3af24b4SEnji Cooper    } elsif ($line =~ s/^\s*> ?//) {
78b3af24b4SEnji Cooper      push @$out, $line;
79b3af24b4SEnji Cooper    } else {
80b3af24b4SEnji Cooper      process_test($prog, $prog_line, $in, $out);
81b3af24b4SEnji Cooper      last if $prog_line >= $opt_l;
82b3af24b4SEnji Cooper
83b3af24b4SEnji Cooper      $prog = [];
84b3af24b4SEnji Cooper      $prog_line = 0;
85b3af24b4SEnji Cooper    }
86b3af24b4SEnji Cooper    if ($line =~ s/^\s*\$ ?//) {
87b3af24b4SEnji Cooper      $prog = [ map { s/\\(.)/$1/g; $_ } split /(?<!\\)\s+/, $line ];
88b3af24b4SEnji Cooper      $prog_line = $lineno;
89b3af24b4SEnji Cooper      $in = [];
90b3af24b4SEnji Cooper      $out = [];
91b3af24b4SEnji Cooper    }
92b3af24b4SEnji Cooper  } else {
93b3af24b4SEnji Cooper    process_test($prog, $prog_line, $in, $out);
94b3af24b4SEnji Cooper    last;
95b3af24b4SEnji Cooper  }
96b3af24b4SEnji Cooper}
97b3af24b4SEnji Cooper
98b3af24b4SEnji Coopermy $status = sprintf("%d commands (%d passed, %d failed)",
99b3af24b4SEnji Cooper	$tests, $tests-$failed, $failed);
100b3af24b4SEnji Cooperif (isatty(fileno(STDOUT))) {
101b3af24b4SEnji Cooper	if ($failed) {
102b3af24b4SEnji Cooper		$status = "\033[31m\033[1m" . $status . "\033[m";
103b3af24b4SEnji Cooper	} else {
104b3af24b4SEnji Cooper		$status = "\033[32m" . $status . "\033[m";
105b3af24b4SEnji Cooper	}
106b3af24b4SEnji Cooper}
107b3af24b4SEnji Cooperprint $status, "\n";
108b3af24b4SEnji Cooperexit $failed ? 1 : 0;
109b3af24b4SEnji Cooper
110b3af24b4SEnji Cooper
111b3af24b4SEnji Coopersub process_test($$$$) {
112b3af24b4SEnji Cooper  my ($prog, $prog_line, $in, $out) = @_;
113b3af24b4SEnji Cooper
114b3af24b4SEnji Cooper  return unless @$prog;
115b3af24b4SEnji Cooper
116b3af24b4SEnji Cooper       my $p = [ @$prog ];
117b3af24b4SEnji Cooper       print "[$prog_line] \$ ", join(' ',
118b3af24b4SEnji Cooper             map { s/\s/\\$&/g; $_ } @$p), " -- ";
119b3af24b4SEnji Cooper       my $result = exec_test($prog, $in);
120b3af24b4SEnji Cooper       my @good = ();
121b3af24b4SEnji Cooper       my $nmax = (@$out > @$result) ? @$out : @$result;
122b3af24b4SEnji Cooper       for (my $n=0; $n < $nmax; $n++) {
123b3af24b4SEnji Cooper	   my $use_re;
124b3af24b4SEnji Cooper	   if (defined $out->[$n] && $out->[$n] =~ /^~ /) {
125b3af24b4SEnji Cooper		$use_re = 1;
126b3af24b4SEnji Cooper		$out->[$n] =~ s/^~ //g;
127b3af24b4SEnji Cooper	   }
128b3af24b4SEnji Cooper
129b3af24b4SEnji Cooper           if (!defined($out->[$n]) || !defined($result->[$n]) ||
130b3af24b4SEnji Cooper               (!$use_re && $result->[$n] ne $out->[$n]) ||
131b3af24b4SEnji Cooper               ( $use_re && $result->[$n] !~ /^$out->[$n]/)) {
132b3af24b4SEnji Cooper               push @good, ($use_re ? '!~' : '!=');
133b3af24b4SEnji Cooper	   }
134b3af24b4SEnji Cooper	   else {
135b3af24b4SEnji Cooper               push @good, ($use_re ? '=~' : '==');
136b3af24b4SEnji Cooper           }
137b3af24b4SEnji Cooper       }
138b3af24b4SEnji Cooper       my $good = !(grep /!/, @good);
139b3af24b4SEnji Cooper       $tests++;
140b3af24b4SEnji Cooper       $failed++ unless $good;
141b3af24b4SEnji Cooper       print $good ? $OK : $FAILED, "\n";
142b3af24b4SEnji Cooper       if (!$good || $opt_v) {
143b3af24b4SEnji Cooper         for (my $n=0; $n < $nmax; $n++) {
144b3af24b4SEnji Cooper	   my $l = defined($out->[$n]) ? $out->[$n] : "~";
145b3af24b4SEnji Cooper	   chomp $l;
146b3af24b4SEnji Cooper	   my $r = defined($result->[$n]) ? $result->[$n] : "~";
147b3af24b4SEnji Cooper	   chomp $r;
148b3af24b4SEnji Cooper	   print sprintf("%-" . ($width-3) . "s %s %s\n",
149b3af24b4SEnji Cooper			 $r, $good[$n], $l);
150b3af24b4SEnji Cooper         }
151b3af24b4SEnji Cooper       }
152b3af24b4SEnji Cooper}
153b3af24b4SEnji Cooper
154b3af24b4SEnji Cooper
155b3af24b4SEnji Coopersub su($) {
156b3af24b4SEnji Cooper  my ($user) = @_;
157b3af24b4SEnji Cooper
158b3af24b4SEnji Cooper  $user ||= "root";
159b3af24b4SEnji Cooper
160b3af24b4SEnji Cooper  my ($login, $pass, $uid, $gid) = getpwnam($user)
161b3af24b4SEnji Cooper    or return [ "su: user $user does not exist\n" ];
162b3af24b4SEnji Cooper  my @groups = ();
163b3af24b4SEnji Cooper  my $fh = new FileHandle("/etc/group")
164b3af24b4SEnji Cooper    or return [ "opening /etc/group: $!\n" ];
165b3af24b4SEnji Cooper  while (<$fh>) {
166b3af24b4SEnji Cooper    chomp;
167b3af24b4SEnji Cooper    my ($group, $passwd, $gid, $users) = split /:/;
168b3af24b4SEnji Cooper    foreach my $u (split /,/, $users) {
169b3af24b4SEnji Cooper      push @groups, $gid
170b3af24b4SEnji Cooper	if ($user eq $u);
171b3af24b4SEnji Cooper    }
172b3af24b4SEnji Cooper  }
173b3af24b4SEnji Cooper  $fh->close;
174b3af24b4SEnji Cooper
175b3af24b4SEnji Cooper  my $groups = join(" ", ($gid, $gid, @groups));
176b3af24b4SEnji Cooper  #print STDERR "[[$groups]]\n";
177b3af24b4SEnji Cooper  $! = 0;  # reset errno
178b3af24b4SEnji Cooper  $> = 0;
179b3af24b4SEnji Cooper  $( = $gid;
180b3af24b4SEnji Cooper  $) = $groups;
181b3af24b4SEnji Cooper  if ($!) {
182b3af24b4SEnji Cooper    return [ "su: $!\n" ];
183b3af24b4SEnji Cooper  }
184b3af24b4SEnji Cooper  if ($uid != 0) {
185b3af24b4SEnji Cooper    $> = $uid;
186b3af24b4SEnji Cooper    #$< = $uid;
187b3af24b4SEnji Cooper    if ($!) {
188b3af24b4SEnji Cooper      return [ "su: $prog->[1]: $!\n" ];
189b3af24b4SEnji Cooper    }
190b3af24b4SEnji Cooper  }
191b3af24b4SEnji Cooper  #print STDERR "[($>,$<)($(,$))]";
192b3af24b4SEnji Cooper  return [];
193b3af24b4SEnji Cooper}
194b3af24b4SEnji Cooper
195b3af24b4SEnji Cooper
196b3af24b4SEnji Coopersub sg($) {
197b3af24b4SEnji Cooper  my ($group) = @_;
198b3af24b4SEnji Cooper
199b3af24b4SEnji Cooper  my $gid = getgrnam($group)
200b3af24b4SEnji Cooper    or return [ "sg: group $group does not exist\n" ];
201b3af24b4SEnji Cooper  my %groups = map { $_ eq $gid ? () : ($_ => 1) } (split /\s/, $));
202b3af24b4SEnji Cooper
203b3af24b4SEnji Cooper  #print STDERR "<<", join("/", keys %groups), ">>\n";
204b3af24b4SEnji Cooper  my $groups = join(" ", ($gid, $gid, keys %groups));
205b3af24b4SEnji Cooper  #print STDERR "[[$groups]]\n";
206b3af24b4SEnji Cooper  $! = 0;  # reset errno
207b3af24b4SEnji Cooper  if ($> != 0) {
208b3af24b4SEnji Cooper	  my $uid = $>;
209b3af24b4SEnji Cooper	  $> = 0;
210b3af24b4SEnji Cooper	  $( = $gid;
211b3af24b4SEnji Cooper	  $) = $groups;
212b3af24b4SEnji Cooper	  $> = $uid;
213b3af24b4SEnji Cooper  } else {
214b3af24b4SEnji Cooper	  $( = $gid;
215b3af24b4SEnji Cooper	  $) = $groups;
216b3af24b4SEnji Cooper  }
217b3af24b4SEnji Cooper  if ($!) {
218b3af24b4SEnji Cooper    return [ "sg: $!\n" ];
219b3af24b4SEnji Cooper  }
220b3af24b4SEnji Cooper  print STDERR "[($>,$<)($(,$))]";
221b3af24b4SEnji Cooper  return [];
222b3af24b4SEnji Cooper}
223b3af24b4SEnji Cooper
224b3af24b4SEnji Cooper
225b3af24b4SEnji Coopersub exec_test($$) {
226b3af24b4SEnji Cooper  my ($prog, $in) = @_;
227b3af24b4SEnji Cooper  local (*IN, *IN_DUP, *IN2, *OUT_DUP, *OUT, *OUT2);
228b3af24b4SEnji Cooper  my $needs_shell = (join('', @$prog) =~ /[][|<>"'`\$\*\?]/);
229b3af24b4SEnji Cooper
230b3af24b4SEnji Cooper  if ($prog->[0] eq "umask") {
231b3af24b4SEnji Cooper    umask oct $prog->[1];
232b3af24b4SEnji Cooper    return [];
233b3af24b4SEnji Cooper  } elsif ($prog->[0] eq "cd") {
234b3af24b4SEnji Cooper    if (!chdir $prog->[1]) {
235b3af24b4SEnji Cooper      return [ "chdir: $prog->[1]: $!\n" ];
236b3af24b4SEnji Cooper    }
237b3af24b4SEnji Cooper    $ENV{PWD} = getcwd;
238b3af24b4SEnji Cooper    return [];
239b3af24b4SEnji Cooper  } elsif ($prog->[0] eq "su") {
240b3af24b4SEnji Cooper    return su($prog->[1]);
241b3af24b4SEnji Cooper  } elsif ($prog->[0] eq "sg") {
242b3af24b4SEnji Cooper    return sg($prog->[1]);
243b3af24b4SEnji Cooper  } elsif ($prog->[0] eq "export") {
244b3af24b4SEnji Cooper    my ($name, $value) = split /=/, $prog->[1];
245b3af24b4SEnji Cooper    # FIXME: need to evaluate $value, so that things like this will work:
246b3af24b4SEnji Cooper    # export dir=$PWD/dir
247b3af24b4SEnji Cooper    $ENV{$name} = $value;
248b3af24b4SEnji Cooper    return [];
249b3af24b4SEnji Cooper  } elsif ($prog->[0] eq "unset") {
250b3af24b4SEnji Cooper    delete $ENV{$prog->[1]};
251b3af24b4SEnji Cooper    return [];
252b3af24b4SEnji Cooper  }
253b3af24b4SEnji Cooper
254b3af24b4SEnji Cooper  pipe *IN2, *OUT
255b3af24b4SEnji Cooper    or die "Can't create pipe for reading: $!";
256b3af24b4SEnji Cooper  open *IN_DUP, "<&STDIN"
257b3af24b4SEnji Cooper    or *IN_DUP = undef;
258b3af24b4SEnji Cooper  open *STDIN, "<&IN2"
259b3af24b4SEnji Cooper    or die "Can't duplicate pipe for reading: $!";
260b3af24b4SEnji Cooper  close *IN2;
261b3af24b4SEnji Cooper
262b3af24b4SEnji Cooper  open *OUT_DUP, ">&STDOUT"
263b3af24b4SEnji Cooper    or die "Can't duplicate STDOUT: $!";
264b3af24b4SEnji Cooper  pipe *IN, *OUT2
265b3af24b4SEnji Cooper    or die "Can't create pipe for writing: $!";
266b3af24b4SEnji Cooper  open *STDOUT, ">&OUT2"
267b3af24b4SEnji Cooper    or die "Can't duplicate pipe for writing: $!";
268b3af24b4SEnji Cooper  close *OUT2;
269b3af24b4SEnji Cooper
270b3af24b4SEnji Cooper  *STDOUT->autoflush();
271b3af24b4SEnji Cooper  *OUT->autoflush();
272b3af24b4SEnji Cooper
273b3af24b4SEnji Cooper  $SIG{CHLD} = 'IGNORE';
274b3af24b4SEnji Cooper
275b3af24b4SEnji Cooper  if (fork()) {
276b3af24b4SEnji Cooper    # Server
277b3af24b4SEnji Cooper    if (*IN_DUP) {
278b3af24b4SEnji Cooper      open *STDIN, "<&IN_DUP"
279b3af24b4SEnji Cooper        or die "Can't duplicate STDIN: $!";
280b3af24b4SEnji Cooper      close *IN_DUP
281b3af24b4SEnji Cooper        or die "Can't close STDIN duplicate: $!";
282b3af24b4SEnji Cooper    }
283b3af24b4SEnji Cooper    open *STDOUT, ">&OUT_DUP"
284b3af24b4SEnji Cooper      or die "Can't duplicate STDOUT: $!";
285b3af24b4SEnji Cooper    close *OUT_DUP
286b3af24b4SEnji Cooper      or die "Can't close STDOUT duplicate: $!";
287b3af24b4SEnji Cooper
288b3af24b4SEnji Cooper    foreach my $line (@$in) {
289b3af24b4SEnji Cooper      #print "> $line";
290b3af24b4SEnji Cooper      print OUT $line;
291b3af24b4SEnji Cooper    }
292b3af24b4SEnji Cooper    close *OUT
293b3af24b4SEnji Cooper      or die "Can't close pipe for writing: $!";
294b3af24b4SEnji Cooper
295b3af24b4SEnji Cooper    my $result = [];
296b3af24b4SEnji Cooper    while (<IN>) {
297b3af24b4SEnji Cooper      #print "< $_";
298b3af24b4SEnji Cooper      if ($needs_shell) {
299b3af24b4SEnji Cooper	s#^/bin/sh: line \d+: ##;
300b3af24b4SEnji Cooper      }
301b3af24b4SEnji Cooper      push @$result, $_;
302b3af24b4SEnji Cooper    }
303b3af24b4SEnji Cooper    return $result;
304b3af24b4SEnji Cooper  } else {
305b3af24b4SEnji Cooper    # Client
306b3af24b4SEnji Cooper    $< = $>;
307b3af24b4SEnji Cooper    close IN
308b3af24b4SEnji Cooper      or die "Can't close read end for input pipe: $!";
309b3af24b4SEnji Cooper    close OUT
310b3af24b4SEnji Cooper      or die "Can't close write end for output pipe: $!";
311b3af24b4SEnji Cooper    close OUT_DUP
312b3af24b4SEnji Cooper      or die "Can't close STDOUT duplicate: $!";
313b3af24b4SEnji Cooper    local *ERR_DUP;
314b3af24b4SEnji Cooper    open ERR_DUP, ">&STDERR"
315b3af24b4SEnji Cooper      or die "Can't duplicate STDERR: $!";
316b3af24b4SEnji Cooper    open STDERR, ">&STDOUT"
317b3af24b4SEnji Cooper      or die "Can't join STDOUT and STDERR: $!";
318b3af24b4SEnji Cooper
319b3af24b4SEnji Cooper    if ($needs_shell) {
320b3af24b4SEnji Cooper      exec ('/bin/sh', '-c', join(" ", @$prog));
321b3af24b4SEnji Cooper    } else {
322b3af24b4SEnji Cooper      exec @$prog;
323b3af24b4SEnji Cooper    }
324b3af24b4SEnji Cooper    print STDERR $prog->[0], ": $!\n";
325b3af24b4SEnji Cooper    exit;
326b3af24b4SEnji Cooper  }
327b3af24b4SEnji Cooper}
328b3af24b4SEnji Cooper
329