xref: /freebsd/tests/sys/acl/run (revision 8adbc73788329dea45afb9354935e4195395fd4f)
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# $FreeBSD$
31b3af24b4SEnji Cooper#
32b3af24b4SEnji Cooper
33b3af24b4SEnji Cooper#
34b3af24b4SEnji Cooper# Possible improvements:
35b3af24b4SEnji Cooper#
36b3af24b4SEnji Cooper# - distinguish stdout and stderr output
37b3af24b4SEnji Cooper# - add environment variable like assignments
38b3af24b4SEnji Cooper# - run up to a specific line
39b3af24b4SEnji Cooper# - resume at a specific line
40b3af24b4SEnji Cooper#
41b3af24b4SEnji Cooper
42b3af24b4SEnji Cooperuse strict;
43b3af24b4SEnji Cooperuse FileHandle;
44b3af24b4SEnji Cooperuse Getopt::Std;
45b3af24b4SEnji Cooperuse POSIX qw(isatty setuid getcwd);
46b3af24b4SEnji Cooperuse vars qw($opt_l $opt_v);
47b3af24b4SEnji Cooper
48b3af24b4SEnji Cooperno warnings qw(taint);
49b3af24b4SEnji Cooper
50b3af24b4SEnji Cooper$opt_l = ~0;  # a really huge number
51b3af24b4SEnji Coopergetopts('l:v');
52b3af24b4SEnji Cooper
53b3af24b4SEnji Coopermy ($OK, $FAILED) = ("ok", "failed");
54b3af24b4SEnji Cooperif (isatty(fileno(STDOUT))) {
55b3af24b4SEnji Cooper	$OK = "\033[32m" . $OK . "\033[m";
56b3af24b4SEnji Cooper	$FAILED = "\033[31m\033[1m" . $FAILED . "\033[m";
57b3af24b4SEnji Cooper}
58b3af24b4SEnji Cooper
59b3af24b4SEnji Coopersub exec_test($$);
60b3af24b4SEnji Coopersub process_test($$$$);
61b3af24b4SEnji Cooper
62b3af24b4SEnji Coopermy ($prog, $in, $out) = ([], [], []);
63b3af24b4SEnji Coopermy $prog_line = 0;
64b3af24b4SEnji Coopermy ($tests, $failed) = (0,0);
65b3af24b4SEnji Coopermy $lineno;
66b3af24b4SEnji Coopermy $width = ($ENV{COLUMNS} || 80) >> 1;
67b3af24b4SEnji Cooper
68b3af24b4SEnji Cooperfor (;;) {
69b3af24b4SEnji Cooper  my $line = <>; $lineno++;
70b3af24b4SEnji Cooper  if (defined $line) {
71b3af24b4SEnji Cooper    # Substitute %VAR and %{VAR} with environment variables.
72b3af24b4SEnji Cooper    $line =~ s[%(\w+)][$ENV{$1}]eg;
73*8adbc737SEitan Adler    $line =~ s[%\{(\w+)\}][$ENV{$1}]eg;
74b3af24b4SEnji Cooper  }
75b3af24b4SEnji Cooper  if (defined $line) {
76b3af24b4SEnji Cooper    if ($line =~ s/^\s*< ?//) {
77b3af24b4SEnji Cooper      push @$in, $line;
78b3af24b4SEnji Cooper    } elsif ($line =~ s/^\s*> ?//) {
79b3af24b4SEnji Cooper      push @$out, $line;
80b3af24b4SEnji Cooper    } else {
81b3af24b4SEnji Cooper      process_test($prog, $prog_line, $in, $out);
82b3af24b4SEnji Cooper      last if $prog_line >= $opt_l;
83b3af24b4SEnji Cooper
84b3af24b4SEnji Cooper      $prog = [];
85b3af24b4SEnji Cooper      $prog_line = 0;
86b3af24b4SEnji Cooper    }
87b3af24b4SEnji Cooper    if ($line =~ s/^\s*\$ ?//) {
88b3af24b4SEnji Cooper      $prog = [ map { s/\\(.)/$1/g; $_ } split /(?<!\\)\s+/, $line ];
89b3af24b4SEnji Cooper      $prog_line = $lineno;
90b3af24b4SEnji Cooper      $in = [];
91b3af24b4SEnji Cooper      $out = [];
92b3af24b4SEnji Cooper    }
93b3af24b4SEnji Cooper  } else {
94b3af24b4SEnji Cooper    process_test($prog, $prog_line, $in, $out);
95b3af24b4SEnji Cooper    last;
96b3af24b4SEnji Cooper  }
97b3af24b4SEnji Cooper}
98b3af24b4SEnji Cooper
99b3af24b4SEnji Coopermy $status = sprintf("%d commands (%d passed, %d failed)",
100b3af24b4SEnji Cooper	$tests, $tests-$failed, $failed);
101b3af24b4SEnji Cooperif (isatty(fileno(STDOUT))) {
102b3af24b4SEnji Cooper	if ($failed) {
103b3af24b4SEnji Cooper		$status = "\033[31m\033[1m" . $status . "\033[m";
104b3af24b4SEnji Cooper	} else {
105b3af24b4SEnji Cooper		$status = "\033[32m" . $status . "\033[m";
106b3af24b4SEnji Cooper	}
107b3af24b4SEnji Cooper}
108b3af24b4SEnji Cooperprint $status, "\n";
109b3af24b4SEnji Cooperexit $failed ? 1 : 0;
110b3af24b4SEnji Cooper
111b3af24b4SEnji Cooper
112b3af24b4SEnji Coopersub process_test($$$$) {
113b3af24b4SEnji Cooper  my ($prog, $prog_line, $in, $out) = @_;
114b3af24b4SEnji Cooper
115b3af24b4SEnji Cooper  return unless @$prog;
116b3af24b4SEnji Cooper
117b3af24b4SEnji Cooper       my $p = [ @$prog ];
118b3af24b4SEnji Cooper       print "[$prog_line] \$ ", join(' ',
119b3af24b4SEnji Cooper             map { s/\s/\\$&/g; $_ } @$p), " -- ";
120b3af24b4SEnji Cooper       my $result = exec_test($prog, $in);
121b3af24b4SEnji Cooper       my @good = ();
122b3af24b4SEnji Cooper       my $nmax = (@$out > @$result) ? @$out : @$result;
123b3af24b4SEnji Cooper       for (my $n=0; $n < $nmax; $n++) {
124b3af24b4SEnji Cooper	   my $use_re;
125b3af24b4SEnji Cooper	   if (defined $out->[$n] && $out->[$n] =~ /^~ /) {
126b3af24b4SEnji Cooper		$use_re = 1;
127b3af24b4SEnji Cooper		$out->[$n] =~ s/^~ //g;
128b3af24b4SEnji Cooper	   }
129b3af24b4SEnji Cooper
130b3af24b4SEnji Cooper           if (!defined($out->[$n]) || !defined($result->[$n]) ||
131b3af24b4SEnji Cooper               (!$use_re && $result->[$n] ne $out->[$n]) ||
132b3af24b4SEnji Cooper               ( $use_re && $result->[$n] !~ /^$out->[$n]/)) {
133b3af24b4SEnji Cooper               push @good, ($use_re ? '!~' : '!=');
134b3af24b4SEnji Cooper	   }
135b3af24b4SEnji Cooper	   else {
136b3af24b4SEnji Cooper               push @good, ($use_re ? '=~' : '==');
137b3af24b4SEnji Cooper           }
138b3af24b4SEnji Cooper       }
139b3af24b4SEnji Cooper       my $good = !(grep /!/, @good);
140b3af24b4SEnji Cooper       $tests++;
141b3af24b4SEnji Cooper       $failed++ unless $good;
142b3af24b4SEnji Cooper       print $good ? $OK : $FAILED, "\n";
143b3af24b4SEnji Cooper       if (!$good || $opt_v) {
144b3af24b4SEnji Cooper         for (my $n=0; $n < $nmax; $n++) {
145b3af24b4SEnji Cooper	   my $l = defined($out->[$n]) ? $out->[$n] : "~";
146b3af24b4SEnji Cooper	   chomp $l;
147b3af24b4SEnji Cooper	   my $r = defined($result->[$n]) ? $result->[$n] : "~";
148b3af24b4SEnji Cooper	   chomp $r;
149b3af24b4SEnji Cooper	   print sprintf("%-" . ($width-3) . "s %s %s\n",
150b3af24b4SEnji Cooper			 $r, $good[$n], $l);
151b3af24b4SEnji Cooper         }
152b3af24b4SEnji Cooper       }
153b3af24b4SEnji Cooper}
154b3af24b4SEnji Cooper
155b3af24b4SEnji Cooper
156b3af24b4SEnji Coopersub su($) {
157b3af24b4SEnji Cooper  my ($user) = @_;
158b3af24b4SEnji Cooper
159b3af24b4SEnji Cooper  $user ||= "root";
160b3af24b4SEnji Cooper
161b3af24b4SEnji Cooper  my ($login, $pass, $uid, $gid) = getpwnam($user)
162b3af24b4SEnji Cooper    or return [ "su: user $user does not exist\n" ];
163b3af24b4SEnji Cooper  my @groups = ();
164b3af24b4SEnji Cooper  my $fh = new FileHandle("/etc/group")
165b3af24b4SEnji Cooper    or return [ "opening /etc/group: $!\n" ];
166b3af24b4SEnji Cooper  while (<$fh>) {
167b3af24b4SEnji Cooper    chomp;
168b3af24b4SEnji Cooper    my ($group, $passwd, $gid, $users) = split /:/;
169b3af24b4SEnji Cooper    foreach my $u (split /,/, $users) {
170b3af24b4SEnji Cooper      push @groups, $gid
171b3af24b4SEnji Cooper	if ($user eq $u);
172b3af24b4SEnji Cooper    }
173b3af24b4SEnji Cooper  }
174b3af24b4SEnji Cooper  $fh->close;
175b3af24b4SEnji Cooper
176b3af24b4SEnji Cooper  my $groups = join(" ", ($gid, $gid, @groups));
177b3af24b4SEnji Cooper  #print STDERR "[[$groups]]\n";
178b3af24b4SEnji Cooper  $! = 0;  # reset errno
179b3af24b4SEnji Cooper  $> = 0;
180b3af24b4SEnji Cooper  $( = $gid;
181b3af24b4SEnji Cooper  $) = $groups;
182b3af24b4SEnji Cooper  if ($!) {
183b3af24b4SEnji Cooper    return [ "su: $!\n" ];
184b3af24b4SEnji Cooper  }
185b3af24b4SEnji Cooper  if ($uid != 0) {
186b3af24b4SEnji Cooper    $> = $uid;
187b3af24b4SEnji Cooper    #$< = $uid;
188b3af24b4SEnji Cooper    if ($!) {
189b3af24b4SEnji Cooper      return [ "su: $prog->[1]: $!\n" ];
190b3af24b4SEnji Cooper    }
191b3af24b4SEnji Cooper  }
192b3af24b4SEnji Cooper  #print STDERR "[($>,$<)($(,$))]";
193b3af24b4SEnji Cooper  return [];
194b3af24b4SEnji Cooper}
195b3af24b4SEnji Cooper
196b3af24b4SEnji Cooper
197b3af24b4SEnji Coopersub sg($) {
198b3af24b4SEnji Cooper  my ($group) = @_;
199b3af24b4SEnji Cooper
200b3af24b4SEnji Cooper  my $gid = getgrnam($group)
201b3af24b4SEnji Cooper    or return [ "sg: group $group does not exist\n" ];
202b3af24b4SEnji Cooper  my %groups = map { $_ eq $gid ? () : ($_ => 1) } (split /\s/, $));
203b3af24b4SEnji Cooper
204b3af24b4SEnji Cooper  #print STDERR "<<", join("/", keys %groups), ">>\n";
205b3af24b4SEnji Cooper  my $groups = join(" ", ($gid, $gid, keys %groups));
206b3af24b4SEnji Cooper  #print STDERR "[[$groups]]\n";
207b3af24b4SEnji Cooper  $! = 0;  # reset errno
208b3af24b4SEnji Cooper  if ($> != 0) {
209b3af24b4SEnji Cooper	  my $uid = $>;
210b3af24b4SEnji Cooper	  $> = 0;
211b3af24b4SEnji Cooper	  $( = $gid;
212b3af24b4SEnji Cooper	  $) = $groups;
213b3af24b4SEnji Cooper	  $> = $uid;
214b3af24b4SEnji Cooper  } else {
215b3af24b4SEnji Cooper	  $( = $gid;
216b3af24b4SEnji Cooper	  $) = $groups;
217b3af24b4SEnji Cooper  }
218b3af24b4SEnji Cooper  if ($!) {
219b3af24b4SEnji Cooper    return [ "sg: $!\n" ];
220b3af24b4SEnji Cooper  }
221b3af24b4SEnji Cooper  print STDERR "[($>,$<)($(,$))]";
222b3af24b4SEnji Cooper  return [];
223b3af24b4SEnji Cooper}
224b3af24b4SEnji Cooper
225b3af24b4SEnji Cooper
226b3af24b4SEnji Coopersub exec_test($$) {
227b3af24b4SEnji Cooper  my ($prog, $in) = @_;
228b3af24b4SEnji Cooper  local (*IN, *IN_DUP, *IN2, *OUT_DUP, *OUT, *OUT2);
229b3af24b4SEnji Cooper  my $needs_shell = (join('', @$prog) =~ /[][|<>"'`\$\*\?]/);
230b3af24b4SEnji Cooper
231b3af24b4SEnji Cooper  if ($prog->[0] eq "umask") {
232b3af24b4SEnji Cooper    umask oct $prog->[1];
233b3af24b4SEnji Cooper    return [];
234b3af24b4SEnji Cooper  } elsif ($prog->[0] eq "cd") {
235b3af24b4SEnji Cooper    if (!chdir $prog->[1]) {
236b3af24b4SEnji Cooper      return [ "chdir: $prog->[1]: $!\n" ];
237b3af24b4SEnji Cooper    }
238b3af24b4SEnji Cooper    $ENV{PWD} = getcwd;
239b3af24b4SEnji Cooper    return [];
240b3af24b4SEnji Cooper  } elsif ($prog->[0] eq "su") {
241b3af24b4SEnji Cooper    return su($prog->[1]);
242b3af24b4SEnji Cooper  } elsif ($prog->[0] eq "sg") {
243b3af24b4SEnji Cooper    return sg($prog->[1]);
244b3af24b4SEnji Cooper  } elsif ($prog->[0] eq "export") {
245b3af24b4SEnji Cooper    my ($name, $value) = split /=/, $prog->[1];
246b3af24b4SEnji Cooper    # FIXME: need to evaluate $value, so that things like this will work:
247b3af24b4SEnji Cooper    # export dir=$PWD/dir
248b3af24b4SEnji Cooper    $ENV{$name} = $value;
249b3af24b4SEnji Cooper    return [];
250b3af24b4SEnji Cooper  } elsif ($prog->[0] eq "unset") {
251b3af24b4SEnji Cooper    delete $ENV{$prog->[1]};
252b3af24b4SEnji Cooper    return [];
253b3af24b4SEnji Cooper  }
254b3af24b4SEnji Cooper
255b3af24b4SEnji Cooper  pipe *IN2, *OUT
256b3af24b4SEnji Cooper    or die "Can't create pipe for reading: $!";
257b3af24b4SEnji Cooper  open *IN_DUP, "<&STDIN"
258b3af24b4SEnji Cooper    or *IN_DUP = undef;
259b3af24b4SEnji Cooper  open *STDIN, "<&IN2"
260b3af24b4SEnji Cooper    or die "Can't duplicate pipe for reading: $!";
261b3af24b4SEnji Cooper  close *IN2;
262b3af24b4SEnji Cooper
263b3af24b4SEnji Cooper  open *OUT_DUP, ">&STDOUT"
264b3af24b4SEnji Cooper    or die "Can't duplicate STDOUT: $!";
265b3af24b4SEnji Cooper  pipe *IN, *OUT2
266b3af24b4SEnji Cooper    or die "Can't create pipe for writing: $!";
267b3af24b4SEnji Cooper  open *STDOUT, ">&OUT2"
268b3af24b4SEnji Cooper    or die "Can't duplicate pipe for writing: $!";
269b3af24b4SEnji Cooper  close *OUT2;
270b3af24b4SEnji Cooper
271b3af24b4SEnji Cooper  *STDOUT->autoflush();
272b3af24b4SEnji Cooper  *OUT->autoflush();
273b3af24b4SEnji Cooper
274b3af24b4SEnji Cooper  $SIG{CHLD} = 'IGNORE';
275b3af24b4SEnji Cooper
276b3af24b4SEnji Cooper  if (fork()) {
277b3af24b4SEnji Cooper    # Server
278b3af24b4SEnji Cooper    if (*IN_DUP) {
279b3af24b4SEnji Cooper      open *STDIN, "<&IN_DUP"
280b3af24b4SEnji Cooper        or die "Can't duplicate STDIN: $!";
281b3af24b4SEnji Cooper      close *IN_DUP
282b3af24b4SEnji Cooper        or die "Can't close STDIN duplicate: $!";
283b3af24b4SEnji Cooper    }
284b3af24b4SEnji Cooper    open *STDOUT, ">&OUT_DUP"
285b3af24b4SEnji Cooper      or die "Can't duplicate STDOUT: $!";
286b3af24b4SEnji Cooper    close *OUT_DUP
287b3af24b4SEnji Cooper      or die "Can't close STDOUT duplicate: $!";
288b3af24b4SEnji Cooper
289b3af24b4SEnji Cooper    foreach my $line (@$in) {
290b3af24b4SEnji Cooper      #print "> $line";
291b3af24b4SEnji Cooper      print OUT $line;
292b3af24b4SEnji Cooper    }
293b3af24b4SEnji Cooper    close *OUT
294b3af24b4SEnji Cooper      or die "Can't close pipe for writing: $!";
295b3af24b4SEnji Cooper
296b3af24b4SEnji Cooper    my $result = [];
297b3af24b4SEnji Cooper    while (<IN>) {
298b3af24b4SEnji Cooper      #print "< $_";
299b3af24b4SEnji Cooper      if ($needs_shell) {
300b3af24b4SEnji Cooper	s#^/bin/sh: line \d+: ##;
301b3af24b4SEnji Cooper      }
302b3af24b4SEnji Cooper      push @$result, $_;
303b3af24b4SEnji Cooper    }
304b3af24b4SEnji Cooper    return $result;
305b3af24b4SEnji Cooper  } else {
306b3af24b4SEnji Cooper    # Client
307b3af24b4SEnji Cooper    $< = $>;
308b3af24b4SEnji Cooper    close IN
309b3af24b4SEnji Cooper      or die "Can't close read end for input pipe: $!";
310b3af24b4SEnji Cooper    close OUT
311b3af24b4SEnji Cooper      or die "Can't close write end for output pipe: $!";
312b3af24b4SEnji Cooper    close OUT_DUP
313b3af24b4SEnji Cooper      or die "Can't close STDOUT duplicate: $!";
314b3af24b4SEnji Cooper    local *ERR_DUP;
315b3af24b4SEnji Cooper    open ERR_DUP, ">&STDERR"
316b3af24b4SEnji Cooper      or die "Can't duplicate STDERR: $!";
317b3af24b4SEnji Cooper    open STDERR, ">&STDOUT"
318b3af24b4SEnji Cooper      or die "Can't join STDOUT and STDERR: $!";
319b3af24b4SEnji Cooper
320b3af24b4SEnji Cooper    if ($needs_shell) {
321b3af24b4SEnji Cooper      exec ('/bin/sh', '-c', join(" ", @$prog));
322b3af24b4SEnji Cooper    } else {
323b3af24b4SEnji Cooper      exec @$prog;
324b3af24b4SEnji Cooper    }
325b3af24b4SEnji Cooper    print STDERR $prog->[0], ": $!\n";
326b3af24b4SEnji Cooper    exit;
327b3af24b4SEnji Cooper  }
328b3af24b4SEnji Cooper}
329b3af24b4SEnji Cooper
330