Does anyone know how to return errors to the parent process after you've fork()ed, when your exec() fails?
I'm doing this in the clamd tests:
my $pid = fork(); die "Fork failed" unless defined $pid; if (!$pid) { exec($clamd); } ...But if clamd fails to start for some reason, I don't know how to find out reliably. exec() of course will return an error, but I don't know how to propogate that to the parent.
What do you mean by "exec() fails"? From perldoc -f exec
It fails and returns false only if the command does not exist *and* it is executed directly instead of via your system's command shell (see below).
Under what conditions do you expect the call to fail? Unless the docs are wrong, if the program you are trying to execute exists, you will never get a false value from exec. Perhaps system() would be a better choice, and you can handle terminating the child process manually?
Re:Exec failure
Matts on 2002-11-04T20:48:53
Sorry I guess I wasn't clear. I mean it fails in exactly the manner described that you quoted;-)
i.e. how can I communicate back to the parent this error condition?Re:Exec failure
rjray on 2002-11-04T21:12:47
Non-zero exit code from the child. Your wait call's return value can be used to make sure that you get the expected child's death. You can use a time-out, since "success" probably means that the child never effectively exits (or if it does, the time-out is probably not needed). When wait returns the PID of the deceased child, $? will have the status/exit code.
Would this work?
Ovid on 2002-11-04T22:40:38
Maybe you can use some variant of the following? We use Windows boxes at work (damn it!), so I rarely get a chance to do much nifty stuff with forking. As a result, I could totally be smoking crack with this.
#!/usr/bin/perl -w
use strict;
#!/usr/bin/perl -w
use strict;
use IO::Handle;
pipe( READER, WRITER );
WRITER->autoflush(1);
# Change the following line to a non-existent program to test
my $program = '/usr/bin/echo';
my $pid = fork;
die unless defined $pid;
unless ($pid) {
close READER;
local $^W;
{exec {$program} 'something'};
print WRITER "Child ($$) failed to exec ($program)\n";
close WRITER;
exit;
}
else {
close WRITER;
my $line = <READER>;
if ( $line ) {
chomp $line;
print "Parent ($$) read ($line)\n";
}
else {
print "Nothing to report, sir!\n";
}
close READER;
}Re:Would this work?
Matts on 2002-11-05T08:44:34
I don't think so, because the line:would block, so if the exec() worked fine, it would never return. I'll try it though - you never know.my $line = <READER>;
Plus if you set the pipe to non-blocking reads, then there's timing issues. How long do you wait for something to come out of the pipe?
pipe()
to sync to the child, fork()
ing, and marking it as close on exit with a fcntl()
. Here are some snippets glued together from pieces of IPC::Run:( $sync_reader_fd, $self->{SYNC_WRITER_FD} ) = _pipe;
$kid->{PID} = fork();
croak "$! during fork" unless defined $kid->{PID};
unless ( $kid->{PID} ) {
eval {
open $s1, ">&=$self->{SYNC_WRITER_FD}"
or croak "$! setting filehandle to fd SYNC_WRITER_FD";
fcntl $s1, F_SETFD, 1;
die "exec failed: simulating exec() failure"
if $self->{_simulate_exec_failure};
_exec $kid->{PATH}, @{$kid->{VAL}}[1..$#{$kid->{VAL}}];
croak "exec failed: $!";
};
if ( $@ ) {
_write $self->{SYNC_WRITER_FD}, $@;
## Avoid DESTROY.
POSIX::exit 1;
}
}
_debug "fork() = ", $kid->{PID} if _debugging_details;
## Wait for kid to get to it's exec() and see if it fails.
_close $self->{SYNC_WRITER_FD};
my $sync_pulse = _read $sync_reader_fd;
_close $sync_reader_fd;
if ( ! defined $sync_pulse || length $sync_pulse ) {
if ( waitpid( $kid->{PID}, 0 ) >= 0 ) {
$kid->{RESULT} = $?;
}
else {
$kid->{RESULT} = -1;
}
$sync_pulse =
"error reading synchronization pipe for $kid->{NUM}, pid $kid->{PID}"
unless length $sync_pulse;
croak $sync_pulse;
}
return $kid->{PID};
- Barrie
Re:How IPC::Run does it
barries on 2002-11-05T14:39:05
make that "close on exec()", not "close on exit".- Barrie