M
Moritz Karbach
Hi,
since my last question "How to kill a forked child process..." [1], I have
learned a little bit more.
I have adjusted the run() function of my Command class following all the
hints you gave me and it works.
But now I have the problem, that in the dmesg appears something like:
But I never call Perl's wait() in my code! Nor do I call system's wait
explicitly.
Btw, I haven't found out yet what this is good for:
$SIG{TTIN} = "IGNORE";
$SIG{TTOU} = "IGNORE";
setpgrp(0,0);
Btw2, if I'm redirecting STDERR of the forked child to STDOUT so that the
parent receives it as well, I have the a problem with executing shell
skripts that do something like
#!/bin/bash
somebinary &
The problem is, that the reading of the pipe
my @output = <$fh_child>;
close($fh_child);
blocks until somebinary has finished, although both the shell skript and the
sh -c interpreter called by Perl are done a lot earlier.
Nevertheless, here is the run function:
<code>
sub runFork # ()
{
my $this = shift;
my $fh_child; # pipe to the child process running the command
$SIG{CHLD} = "IGNORE";
#
# fork the program branch
#
my $pid;
$pid = open($fh_child,"-|");
die "cannot fork: $!\n" unless defined($pid);
if ( $pid == 0 )
{
#
# child (command branch)
#
my @temp = ($EUID, $EGID);
my $orig_uid = $UID;
my $orig_gid = $GID;
$EUID = $UID;
$EGID = $GID;
# Drop privileges
$UID = $orig_uid;
$GID = $orig_gid;
# Make sure privs are really gone
($EUID, $EGID) = @temp;
die "Can't drop privileges"
unless $UID == $EUID && $GID eq $EGID;
$SIG{TTIN} = "IGNORE";
$SIG{TTOU} = "IGNORE";
#open(STDERR, ">&STDOUT") or die "Can't dup STDOUT: $!";
setpgrp(0,0);
exec($this->{command}) or die ("cannot run program: $!");
exit;
}
#
# parent
#
# fork once more for the watchdog for the timeout
#
my $wdog_id;
if ( $this->{timeout} > 0 )
{
$wdog_id = fork;
die "cannot fork: $!\n" unless defined($wdog_id);
if ( $wdog_id == 0 )
{
#
# child - watchdog branch
#
$SIG{TTIN} = "IGNORE";
$SIG{TTOU} = "IGNORE";
setpgrp(0,0);
sleep($this->{timeout});
#
# killing negative process id's also sends the signal to all
# other processes in the process-group of $pid
#
# In our case this would kill both the shell interpreter and
# the executable launched by the interpreter.
#
kill(9, -$pid);
exit 0;
}
}
#
# read output of the command
#
# this blocks until the command is finished!
#
my @output = <$fh_child>;
close($fh_child);
$this->{output} = \@output;
#
# return value of the command
#
$this->{ret} = $?;
my $sig = $this->{ret} & 127;
#
# if command succeeded, we don't need the watchdog any more: kill!
#
if ( defined($wdog_id) && $sig != 9 )
{
kill(9, $wdog_id);
}
return $this->{ret};
}
</code>
Cheers,
- Moritz
[1] <[email protected]>
since my last question "How to kill a forked child process..." [1], I have
learned a little bit more.
I have adjusted the run() function of my Command class following all the
hints you gave me and it works.
But now I have the problem, that in the dmesg appears something like:
application bug: h1mcJobwrapper.(25668) has SIGCHLD set to SIG_IGN but
calls wait().
(see the NOTES section of 'man 2 wait'). Workaround activated.
But I never call Perl's wait() in my code! Nor do I call system's wait
explicitly.
Btw, I haven't found out yet what this is good for:
$SIG{TTIN} = "IGNORE";
$SIG{TTOU} = "IGNORE";
setpgrp(0,0);
Btw2, if I'm redirecting STDERR of the forked child to STDOUT so that the
parent receives it as well, I have the a problem with executing shell
skripts that do something like
#!/bin/bash
somebinary &
The problem is, that the reading of the pipe
my @output = <$fh_child>;
close($fh_child);
blocks until somebinary has finished, although both the shell skript and the
sh -c interpreter called by Perl are done a lot earlier.
Nevertheless, here is the run function:
<code>
sub runFork # ()
{
my $this = shift;
my $fh_child; # pipe to the child process running the command
$SIG{CHLD} = "IGNORE";
#
# fork the program branch
#
my $pid;
$pid = open($fh_child,"-|");
die "cannot fork: $!\n" unless defined($pid);
if ( $pid == 0 )
{
#
# child (command branch)
#
my @temp = ($EUID, $EGID);
my $orig_uid = $UID;
my $orig_gid = $GID;
$EUID = $UID;
$EGID = $GID;
# Drop privileges
$UID = $orig_uid;
$GID = $orig_gid;
# Make sure privs are really gone
($EUID, $EGID) = @temp;
die "Can't drop privileges"
unless $UID == $EUID && $GID eq $EGID;
$SIG{TTIN} = "IGNORE";
$SIG{TTOU} = "IGNORE";
#open(STDERR, ">&STDOUT") or die "Can't dup STDOUT: $!";
setpgrp(0,0);
exec($this->{command}) or die ("cannot run program: $!");
exit;
}
#
# parent
#
# fork once more for the watchdog for the timeout
#
my $wdog_id;
if ( $this->{timeout} > 0 )
{
$wdog_id = fork;
die "cannot fork: $!\n" unless defined($wdog_id);
if ( $wdog_id == 0 )
{
#
# child - watchdog branch
#
$SIG{TTIN} = "IGNORE";
$SIG{TTOU} = "IGNORE";
setpgrp(0,0);
sleep($this->{timeout});
#
# killing negative process id's also sends the signal to all
# other processes in the process-group of $pid
#
# In our case this would kill both the shell interpreter and
# the executable launched by the interpreter.
#
kill(9, -$pid);
exit 0;
}
}
#
# read output of the command
#
# this blocks until the command is finished!
#
my @output = <$fh_child>;
close($fh_child);
$this->{output} = \@output;
#
# return value of the command
#
$this->{ret} = $?;
my $sig = $this->{ret} & 127;
#
# if command succeeded, we don't need the watchdog any more: kill!
#
if ( defined($wdog_id) && $sig != 9 )
{
kill(9, $wdog_id);
}
return $this->{ret};
}
</code>
Cheers,
- Moritz
[1] <[email protected]>