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