Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Perl forward SIGINT to parent process from `system` command

Tags:

perl

If I have a long-running system command like apt-cache search <some query>, is there a way to forward a SIGINT sent via ^C on the command line to the parent Perl process in such a way that all child processes are reaped.

This example does not have the desired behavior. The signal is sent to the child process.

#!/usr/bin/env perl
use strict;
use warnings FATAL => 'all';
use autodie;

# long running command on ubuntu, produces a ton of output.
# replace with your favorite long running command
system("apt-cache search hi");

print("Perl did not catch SIGINT even with autodie\n");

I tried searching around for ways of capturing the pid of the child that would be created by system("apt-cache search hi &"), but couldn't find any, so I tried forking and execing the process and writing a signal handler. This doesn't work because apt-cache itself launches a few processes via the clone system call. Hand-rolling some logic to walk part of the process tree and clean up the

#!/usr/bin/env perl
use strict;
use warnings FATAL => 'all';
use autodie;

my $cpid;

$SIG{INT} = sub {
    kill 'KILL', $cpid;
    exit;
};

# long running command on ubuntu, produces a ton of output.
# replace with your favorite long running command
$cpid = fork;
if ($cpid == 0) {
    exec 'apt-cache', 'search', 'hi';
}

print "Perl did not catch SIGINT even with autodie\n";

I guess essentially what I want is either a way of determining whether a child process launched by system exited due to a signal like SIGINT so I can have the Perl script clean up after itself or a way of walking the child processes and reaping them in such a way that strange edge cases of process management are handled cleanly and portably.

like image 603
Gregory Nisbet Avatar asked Dec 24 '15 21:12

Gregory Nisbet


1 Answers

Make the child the head of a process group, then send the signal to the whole process group.

#!/usr/bin/perl

use strict;
use warnings;
use autodie;

use POSIX qw( setpgid );

my $child_pid;

$SIG{INT} = sub {
    kill INT => -$child_pid if $child_pid;
    exit(0x80 | 2);  # 2 = SIGINT
};

my $child_pid = fork();
if (!$child_pid) {
    setpgid($$);
    exec 'apt-cache', 'search', 'hi';
}

WAIT: {
   no autodie;
   waitpid($child_pid, 0);
   redo WAIT if $? == -1 and $!{EINTR};
   die $! if $? == -1;
}

exit( ( $? >> 8 ) | ( 0x80 | ( $? & 0x7F ) ) );
like image 91
ikegami Avatar answered Oct 05 '22 07:10

ikegami