Kyle R. Burton on 31 Oct 2003 22:43:02 -0500


[Date Prev] [Date Next] [Thread Prev] [Thread Next] [Date Index] [Thread Index]

Re: [PLUG] fun with perl: eval{} and exceptions


> > eval { $cmd_pid=open3(\*IN, \*OUT, \*ERR, @cmd); };
> > if($@ =~ /exec/){
> > 	print "\@ is $@\n";
> > 	$cmd_errstr = $@;
> > 	$cmd_retval = "-1";
> > 	die;
> > };
> > ...
> 
> oh, i forgot:  the duplicates happen if you take out the call to die(),
> but not if you leave it in, which is why i'm thinking this is some 
> wierdness involved with a fork().

Well, I just hacked togeather some test code and it looks like open3
first creates pipes appropriately, then forks, and then attempts to
exec the command.

Since the fork has already happened, a failure of exec(), now in the
child, can only be reported to it's std{out,err}.

I don't get mixed output, I get synchronous output.  The only trick is
to exit in the child (if $pid is zero/undef) _after_ the implied exec
(so if the exec fails in the child, the child still exits).


HTH
Kyle







use strict;
use warnings;
use IPC::Open3;
$|++;

#my @cmd = qw(ls -ld /tmp);     # works
#my @cmd = qw(ls -l /adsftmp);  # command errors after successful fork+exec
my @cmd = qw(addfadf -l /tmp); # fails exec after fork
my $pid = '';

my $sigpipe = 0;
$SIG{PIPE} = sub {
  print STDERR "got SIGPIPE\n";
  $sigpipe++;
};

my($in,$out,$err) = ('','','');
eval {
  $pid = open3($in,$out,$err,@cmd);
  unless ( $pid ) {
    print "\$\$=$$ pid=$pid Whoa, the exec failed?: $!\n";
  }
  print "\$\$=$$ pid=$pid open3 returned\n";
};
print "\$\$=$$ pid=$pid back outside the eval...\n";

if ($@) {
  die "\$\$=$$ pid=$pid whoops: $@";
}

print "\$\$=$$ pid=$pid no exception, everthing seems to have worked, proceding to read \n";

sleep 1;
readfh_nonblock("out",$out);
sleep 1;
readfh_nonblock("err",$err);

foreach my $fh ($in,$out,$err) {
  close $fh if $fh;
}

print "finished, closing file handles\n";
print "  pid=$pid\n";
print "  sigpipe=$sigpipe\n";

# non-blocking read loop
sub readfh_nonblock {
  my($name,$fh) = @_;

  return unless $fh;

  print "$name:\n";
  print "  pid=$pid\n";
  print "  sigpipe=$sigpipe\n";


  print "$name===>\n";
  while (1) {
    my $buff;
    my $nr = sysread($fh,$buff,1024);
    unless (defined $nr) {
      print "Error reading from handle $name: $!\n";
      last;
    }
    last unless $nr;
    print $buff;
  }

  print "<===$name\n";
}


-- 

------------------------------------------------------------------------------
Wisdom and Compassion are inseparable.
        -- Christmas Humphreys
mortis@voicenet.com                            http://www.voicenet.com/~mortis
------------------------------------------------------------------------------
___________________________________________________________________________
Philadelphia Linux Users Group         --        http://www.phillylinux.org
Announcements - http://lists.phillylinux.org/mailman/listinfo/plug-announce
General Discussion  --   http://lists.phillylinux.org/mailman/listinfo/plug