From 72fba9114b48b9567ac5d22c9739fab6eb83c988 Mon Sep 17 00:00:00 2001 From: Emmanuel Kasper Date: Thu, 29 Jun 2017 14:48:12 +0200 Subject: [PATCH] Add run_fork_with_timeout utility This runs subroutine in a forked process and kills it after a timeout --- src/PVE/Tools.pm | 66 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/src/PVE/Tools.pm b/src/PVE/Tools.pm index f9ca118..bd025e2 100644 --- a/src/PVE/Tools.pm +++ b/src/PVE/Tools.pm @@ -839,6 +839,72 @@ sub next_spice_port { return next_unused_port(61000, 61099, $family, $address); } +# sigkill after $timeout a $sub running in a fork if it can't write a pipe +# the $sub has to return a single scalar +sub run_fork_with_timeout { + my ($timeout, $sub) = @_; + + my $res; + my $error; + my $pipe_out = IO::Pipe->new(); + my $pipe_err = IO::Pipe->new(); + + # disable pending alarms, save their remaining time + my $prev_alarm = alarm 0; + + # trap before forking to avoid leaving a zombie if the parent get killed + my $sig_received; + local $SIG{INT} = $SIG{TERM} = $SIG{QUIT} = $SIG{HUP} = $SIG{PIPE} = sub { + $sig_received++; + }; + + my $child = fork(); + if (!defined($child)) { + die "fork failed: $!\n"; + return $res; + } + + if (!$child) { + $pipe_out->writer(); + $pipe_err->writer(); + + eval { + $res = $sub->(); + print {$pipe_out} "$res"; + $pipe_out->flush(); + }; + if (my $err = $@) { + print {$pipe_err} "$err"; + $pipe_err->flush(); + POSIX::_exit(1); + } + POSIX::_exit(0); + } + + $pipe_out->reader(); + $pipe_err->reader(); + + my $readvalues = sub { + local $/ = undef; + $res = <$pipe_out>; + $error = <$pipe_err>; + }; + eval { + run_with_timeout($timeout, $readvalues); + }; + warn $@ if $@; + $pipe_out->close(); + $pipe_err->close(); + kill('KILL', $child); + waitpid($child, 0); + + alarm $prev_alarm; + die "interrupted by unexpected signal\n" if $sig_received; + + die $error if $error; + return $res; +} + # NOTE: NFS syscall can't be interrupted, so alarm does # not work to provide timeouts. # from 'man nfs': "Only SIGKILL can interrupt a pending NFS operation" -- 2.39.2