xref: /openbsd-src/regress/usr.sbin/httpd/tests/Proc.pm (revision 441954ed84cef7a219e47c09a09c58b516bc2103)
1*441954edSanton#	$OpenBSD: Proc.pm,v 1.3 2021/10/05 17:40:08 anton Exp $
253d930aeSreyk
353d930aeSreyk# Copyright (c) 2010-2014 Alexander Bluhm <bluhm@openbsd.org>
453d930aeSreyk#
553d930aeSreyk# Permission to use, copy, modify, and distribute this software for any
653d930aeSreyk# purpose with or without fee is hereby granted, provided that the above
753d930aeSreyk# copyright notice and this permission notice appear in all copies.
853d930aeSreyk#
953d930aeSreyk# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1053d930aeSreyk# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1153d930aeSreyk# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1253d930aeSreyk# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1353d930aeSreyk# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1453d930aeSreyk# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1553d930aeSreyk# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1653d930aeSreyk
1753d930aeSreykuse strict;
1853d930aeSreykuse warnings;
1953d930aeSreyk
2053d930aeSreykpackage Proc;
2153d930aeSreykuse Carp;
2253d930aeSreykuse Errno;
2353d930aeSreykuse File::Basename;
2453d930aeSreykuse IO::File;
2553d930aeSreykuse POSIX;
2653d930aeSreykuse Time::HiRes qw(time alarm sleep);
2753d930aeSreyk
2853d930aeSreykmy %CHILDREN;
2953d930aeSreyk
3053d930aeSreyksub kill_children {
3153d930aeSreyk	my @pids = @_ ? @_ : keys %CHILDREN
3253d930aeSreyk	    or return;
3353d930aeSreyk	my @perms;
3453d930aeSreyk	foreach my $pid (@pids) {
3553d930aeSreyk		if (kill(TERM => $pid) != 1 and $!{EPERM}) {
3653d930aeSreyk			push @perms, $pid;
3753d930aeSreyk		}
3853d930aeSreyk	}
39*441954edSanton	if (my @sudo = split(' ', $ENV{SUDO}) and @perms) {
4053d930aeSreyk		local $?;  # do not modify during END block
41*441954edSanton		my @cmd = (@sudo, '/bin/kill', '-TERM', @perms);
4253d930aeSreyk		system(@cmd);
4353d930aeSreyk	}
4453d930aeSreyk	delete @CHILDREN{@pids};
4553d930aeSreyk}
4653d930aeSreyk
4753d930aeSreykBEGIN {
4853d930aeSreyk	$SIG{TERM} = $SIG{INT} = sub {
4953d930aeSreyk		my $sig = shift;
5053d930aeSreyk		kill_children();
5153d930aeSreyk		$SIG{TERM} = $SIG{INT} = 'DEFAULT';
5253d930aeSreyk		POSIX::raise($sig);
5353d930aeSreyk	};
5453d930aeSreyk}
5553d930aeSreyk
5653d930aeSreykEND {
5753d930aeSreyk	kill_children();
5853d930aeSreyk	$SIG{TERM} = $SIG{INT} = 'DEFAULT';
5953d930aeSreyk}
6053d930aeSreyk
6153d930aeSreyksub new {
6253d930aeSreyk	my $class = shift;
6353d930aeSreyk	my $self = { @_ };
6453d930aeSreyk	$self->{down} ||= "Shutdown";
6553d930aeSreyk	$self->{func} && ref($self->{func}) eq 'CODE'
6653d930aeSreyk	    or croak "$class func not given";
6753d930aeSreyk	$self->{logfile}
6853d930aeSreyk	    or croak "$class log file not given";
6953d930aeSreyk	open(my $fh, '>', $self->{logfile})
7053d930aeSreyk	    or die "$class log file $self->{logfile} create failed: $!";
7153d930aeSreyk	$fh->autoflush;
7253d930aeSreyk	$self->{log} = $fh;
7353d930aeSreyk	return bless $self, $class;
7453d930aeSreyk}
7553d930aeSreyk
7653d930aeSreyksub run {
7753d930aeSreyk	my $self = shift;
7853d930aeSreyk
7953d930aeSreyk	pipe(my $reader, my $writer)
8053d930aeSreyk	    or die ref($self), " pipe to child failed: $!";
8153d930aeSreyk	defined(my $pid = fork())
8253d930aeSreyk	    or die ref($self), " fork child failed: $!";
8353d930aeSreyk	if ($pid) {
8453d930aeSreyk		$CHILDREN{$pid} = 1;
8553d930aeSreyk		$self->{pid} = $pid;
8653d930aeSreyk		close($reader);
8753d930aeSreyk		$self->{pipe} = $writer;
8853d930aeSreyk		return $self;
8953d930aeSreyk	}
9053d930aeSreyk	%CHILDREN = ();
9153d930aeSreyk	$SIG{TERM} = $SIG{INT} = 'DEFAULT';
9253d930aeSreyk	$SIG{__DIE__} = sub {
9353d930aeSreyk		die @_ if $^S;
9453d930aeSreyk		warn @_;
9553d930aeSreyk		IO::Handle::flush(\*STDERR);
9653d930aeSreyk		POSIX::_exit(255);
9753d930aeSreyk	};
9853d930aeSreyk	open(STDERR, '>&', $self->{log})
9953d930aeSreyk	    or die ref($self), " dup STDERR failed: $!";
10053d930aeSreyk	close($writer);
10153d930aeSreyk	open(STDIN, '<&', $reader)
10253d930aeSreyk	    or die ref($self), " dup STDIN failed: $!";
10353d930aeSreyk	close($reader);
10453d930aeSreyk
10553d930aeSreyk	do {
10653d930aeSreyk		$self->child();
10753d930aeSreyk		print STDERR $self->{up}, "\n";
10853d930aeSreyk		$self->{begin} = time();
10953d930aeSreyk		$self->{func}->($self);
11053d930aeSreyk	} while ($self->{redo});
11153d930aeSreyk	$self->{end} = time();
11253d930aeSreyk	print STDERR "Shutdown", "\n";
11353d930aeSreyk	if ($self->{timefile}) {
11453d930aeSreyk		open(my $fh, '>>', $self->{timefile})
11553d930aeSreyk		    or die ref($self), " open $self->{timefile} failed: $!";
11653d930aeSreyk		printf $fh "time='%s' duration='%.10g' ".
11753d930aeSreyk		    "test='%s'\n",
11853d930aeSreyk		    scalar(localtime(time())), $self->{end} - $self->{begin},
11953d930aeSreyk		    basename($self->{testfile});
12053d930aeSreyk	}
12153d930aeSreyk
12253d930aeSreyk	IO::Handle::flush(\*STDOUT);
12353d930aeSreyk	IO::Handle::flush(\*STDERR);
12453d930aeSreyk	POSIX::_exit(0);
12553d930aeSreyk}
12653d930aeSreyk
12753d930aeSreyksub wait {
12853d930aeSreyk	my $self = shift;
12953d930aeSreyk	my $flags = shift;
13053d930aeSreyk
13153d930aeSreyk	my $pid = $self->{pid}
13253d930aeSreyk	    or croak ref($self), " no child pid";
13353d930aeSreyk	my $kid = waitpid($pid, $flags);
13453d930aeSreyk	if ($kid > 0) {
13553d930aeSreyk		my $status = $?;
13653d930aeSreyk		my $code;
13753d930aeSreyk		$code = "exit: ".   WEXITSTATUS($?) if WIFEXITED($?);
13853d930aeSreyk		$code = "signal: ". WTERMSIG($?)    if WIFSIGNALED($?);
13953d930aeSreyk		$code = "stop: ".   WSTOPSIG($?)    if WIFSTOPPED($?);
14053d930aeSreyk		delete $CHILDREN{$pid} if WIFEXITED($?) || WIFSIGNALED($?);
14153d930aeSreyk		return wantarray ? ($kid, $status, $code) : $kid;
14253d930aeSreyk	}
14353d930aeSreyk	return $kid;
14453d930aeSreyk}
14553d930aeSreyk
14653d930aeSreyksub loggrep {
14753d930aeSreyk	my $self = shift;
14853d930aeSreyk	my($regex, $timeout) = @_;
14953d930aeSreyk
150f8f8530cSbluhm	my $end;
151f8f8530cSbluhm	$end = time() + $timeout if $timeout;
15253d930aeSreyk
15353d930aeSreyk	do {
15453d930aeSreyk		my($kid, $status, $code) = $self->wait(WNOHANG);
15553d930aeSreyk		if ($kid > 0 && $status != 0 && !$self->{dryrun}) {
15653d930aeSreyk			# child terminated with failure
15753d930aeSreyk			die ref($self), " child status: $status $code";
15853d930aeSreyk		}
15953d930aeSreyk		open(my $fh, '<', $self->{logfile})
16053d930aeSreyk		    or die ref($self), " log file open failed: $!";
16153d930aeSreyk		my @match = grep { /$regex/ } <$fh>;
16253d930aeSreyk		return wantarray ? @match : $match[0] if @match;
16353d930aeSreyk		close($fh);
16453d930aeSreyk		# pattern not found
16553d930aeSreyk		if ($kid == 0) {
16653d930aeSreyk			# child still running, wait for log data
16753d930aeSreyk			sleep .1;
16853d930aeSreyk		} else {
16953d930aeSreyk			# child terminated, no new log data possible
17053d930aeSreyk			return;
17153d930aeSreyk		}
17253d930aeSreyk	} while ($timeout and time() < $end);
17353d930aeSreyk
17453d930aeSreyk	return;
17553d930aeSreyk}
17653d930aeSreyk
17753d930aeSreyksub up {
17853d930aeSreyk	my $self = shift;
17953d930aeSreyk	my $timeout = shift || 10;
18053d930aeSreyk	$self->loggrep(qr/$self->{up}/, $timeout)
18153d930aeSreyk	    or croak ref($self), " no '$self->{up}' in $self->{logfile} ".
18253d930aeSreyk		"after $timeout seconds";
18353d930aeSreyk	return $self;
18453d930aeSreyk}
18553d930aeSreyk
18653d930aeSreyksub down {
18753d930aeSreyk	my $self = shift;
18853d930aeSreyk	my $timeout = shift || 300;
18953d930aeSreyk	$self->loggrep(qr/$self->{down}/, $timeout)
19053d930aeSreyk	    or croak ref($self), " no '$self->{down}' in $self->{logfile} ".
19153d930aeSreyk		"after $timeout seconds";
19253d930aeSreyk	return $self;
19353d930aeSreyk}
19453d930aeSreyk
19553d930aeSreyksub kill_child {
19653d930aeSreyk	my $self = shift;
19753d930aeSreyk	kill_children($self->{pid});
19853d930aeSreyk	return $self;
19953d930aeSreyk}
20053d930aeSreyk
20153d930aeSreyk1;
202