Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

failed system() doesn't set $? #10186

Open
p5pRT opened this issue Feb 18, 2010 · 10 comments
Open

failed system() doesn't set $? #10186

p5pRT opened this issue Feb 18, 2010 · 10 comments

Comments

@p5pRT
Copy link

p5pRT commented Feb 18, 2010

Migrated from rt.perl.org#72930 (status was 'open')

Searchable as RT72930$

@p5pRT
Copy link
Author

p5pRT commented Feb 18, 2010

From davem@pigeon

Created by davem@pigeon.(none)

When the fork of a system() fails, the return value of system() is
(correctly) -1, but the value of $? is (arguably incorrectly) still 0.
Either $? needs setting too, or the perlfunc.pod "check all
possible failure modes" example needs updating. Personally I'd suggest the
former.

For example, on a system where there isn't enough virtual memory left,
this​:

  my $ret = system("date");
  warn "ret=$ret \$?=$? \$!=$!\n";

gives this​:

  ret=-1 $?=0 $!=Cannot allocate memory

See also http​://www.perlmonks.org/?node_id=823579 .

Perl Info

Flags:
    category=core
    severity=medium

Site configuration information for perl 5.11.4:

Configured by davem at Wed Feb 17 14:01:30 GMT 2010.

Summary of my perl5 (revision 5 version 11 subversion 4) configuration:
  Commit id: 03c4920e421b61a21ff9ca04a3990ac8c98ad7b7
  Platform:
    osname=linux, osvers=2.6.30.10-105.2.16.fc11.i686.pae, archname=i686-linux-thread-multi
    uname='linux pigeon 2.6.30.10-105.2.16.fc11.i686.pae #1 smp thu feb 4 15:55:59 utc 2010 i686 i686 i386 gnulinux '
    config_args='-des -Dusedevel -Dprefix=/home/davem/perl5/git/bleed.out -Uinstallusrbinperl -Duseithreads -Doptimize=-g -Accflags=-ggdb'
    hint=recommended, useposix=true, d_sigaction=define
    useithreads=define, usemultiplicity=define
    useperlio=define, d_sfio=undef, uselargefiles=define, usesocks=undef
    use64bitint=undef, use64bitall=undef, uselongdouble=undef
    usemymalloc=n, bincompat5005=undef
  Compiler:
    cc='cc', ccflags ='-D_REENTRANT -D_GNU_SOURCE -ggdb -DDEBUGGING -fno-strict-aliasing -pipe -fstack-protector -I/usr/local/include -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64',
    optimize='-g',
    cppflags='-D_REENTRANT -D_GNU_SOURCE -ggdb -DDEBUGGING -fno-strict-aliasing -pipe -fstack-protector -I/usr/local/include'
    ccversion='', gccversion='4.4.1 20090725 (Red Hat 4.4.1-2)', gccosandvers=''
    intsize=4, longsize=4, ptrsize=4, doublesize=8, byteorder=1234
    d_longlong=define, longlongsize=8, d_longdbl=define, longdblsize=12
    ivtype='long', ivsize=4, nvtype='double', nvsize=8, Off_t='off_t', lseeksize=8
    alignbytes=4, prototype=define
  Linker and Libraries:
    ld='cc', ldflags =' -fstack-protector -L/usr/local/lib'
    libpth=/usr/local/lib /lib /usr/lib
    libs=-lnsl -lgdbm -ldb -ldl -lm -lcrypt -lutil -lpthread -lc
    perllibs=-lnsl -ldl -lm -lcrypt -lutil -lpthread -lc
    libc=/lib/libc-2.10.2.so, so=so, useshrplib=false, libperl=libperl.a
    gnulibc_version='2.10.2'
  Dynamic Linking:
    dlsrc=dl_dlopen.xs, dlext=so, d_dlsymun=undef, ccdlflags='-Wl,-E'
    cccdlflags='-fPIC', lddlflags='-shared -g -L/usr/local/lib -fstack-protector'

Locally applied patches:
    


@INC for perl 5.11.4:
    lib
    /home/davem/perl5/git/bleed.out/lib/site_perl/5.11.4/i686-linux-thread-multi
    /home/davem/perl5/git/bleed.out/lib/site_perl/5.11.4
    /home/davem/perl5/git/bleed.out/lib/5.11.4/i686-linux-thread-multi
    /home/davem/perl5/git/bleed.out/lib/5.11.4
    /home/davem/perl5/git/bleed.out/lib/site_perl
    .


Environment for perl 5.11.4:
    HOME=/home/davem
    LANG=en_US.UTF-8
    LANGUAGE (unset)
    LD_LIBRARY_PATH (unset)
    LOGDIR (unset)
    PATH=/usr/lib/qt-3.3/bin:/usr/kerberos/sbin:/usr/kerberos/bin:/usr/lib/ccache:/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin:/home/davem/bin
    PERL_BADLANG (unset)
    SHELL=/bin/bash

@p5pRT
Copy link
Author

p5pRT commented Feb 18, 2010

From @rgarcia

On 18 February 2010 17​:43, none via RT <perlbug-followup@​perl.org> wrote​:

When the fork of a system() fails, the return value of system() is
(correctly) -1, but the value of $? is (arguably incorrectly) still 0.

$? is supposed to return the status from the child process, which in
that case couldn't be created. So $? can't be trusted, and I'd suggest
this is a doc bug.

@p5pRT
Copy link
Author

p5pRT commented Feb 18, 2010

The RT System itself - Status changed from 'new' to 'open'

@p5pRT
Copy link
Author

p5pRT commented Feb 18, 2010

From @ikegami

On Thu, Feb 18, 2010 at 4​:57 PM, Rafael Garcia-Suarez <rgs@​consttype.org>wrote​:

On 18 February 2010 17​:43, none via RT <perlbug-followup@​perl.org> wrote​:

When the fork of a system() fails, the return value of system() is
(correctly) -1, but the value of $? is (arguably incorrectly) still 0.

$? is supposed to return the status from the child process, which in
that case couldn't be created. So $? can't be trusted, and I'd suggest
this is a doc bug.

Except in the reported case, $? *is* set to -1 on error, and people rely on
that.

$ perl -wle'print $?; system("nonexistant"); print $?'
0
Can't exec "nonexistant"​: No such file or directory at -e line 1.
-1

Changing that behaviour will needlessly break backwards compatibility. I
believe this is a code bug and not a documentation bug.

@p5pRT
Copy link
Author

p5pRT commented Feb 19, 2010

From @Abigail

On Thu, Feb 18, 2010 at 06​:55​:56PM -0500, Eric Brine wrote​:

On Thu, Feb 18, 2010 at 4​:57 PM, Rafael Garcia-Suarez <rgs@​consttype.org>wrote​:

On 18 February 2010 17​:43, none via RT <perlbug-followup@​perl.org> wrote​:

When the fork of a system() fails, the return value of system() is
(correctly) -1, but the value of $? is (arguably incorrectly) still 0.

$? is supposed to return the status from the child process, which in
that case couldn't be created. So $? can't be trusted, and I'd suggest
this is a doc bug.

Except in the reported case, $? *is* set to -1 on error, and people rely on
that.

$ perl -wle'print $?; system("nonexistant"); print $?'
0
Can't exec "nonexistant"​: No such file or directory at -e line 1.
-1

But here the fork *succeeds*, it's the exec that's failing. There is still
a child process. Which is different from the fork itself failing.

Abigail

@p5pRT
Copy link
Author

p5pRT commented Feb 19, 2010

From @iabyn

On Thu, Feb 18, 2010 at 10​:57​:57PM +0100, Rafael Garcia-Suarez wrote​:

On 18 February 2010 17​:43, none via RT <perlbug-followup@​perl.org> wrote​:

When the fork of a system() fails, the return value of system() is
(correctly) -1, but the value of $? is (arguably incorrectly) still 0.

$? is supposed to return the status from the child process, which in
that case couldn't be created. So $? can't be trusted, and I'd suggest
this is a doc bug.

The system() docs since at least 5.000 have been deeply ambiguous about
the relationship between $? and the return value of system(). Since
5.005_03 we've been promising "You can check all the failure possibilities
by inspecting C<$?>", so I suspect there's a fair chunk of code out there
that does
  system(...)​:
  various_tests_for_failure($?);
and that all miss the rare but possible case that the fork itself fails.

Note that we already fake up the value of $? under some circumstances​:
if the fork succeeds but the exec fails, we pass the errno of the exec in
a pipe back to the parent, then set $? to - 1 and $! to that errno.

I think system should set $? to -1 on fork failure because​:

1) it makes things easier; it is no longer the case that $? and system()
always have the same value *except* for a few rare circumstances

2) We make a lot of code already out there that relied on our ambiguous
documentation actual trap some error conditions that they currently
silently miss.

3) I for one was very confused by the perlmonks thread and look me along
while to think of checking for system() return as well as $?

Anyway for info, the history of the system() documentation in perlfunc, ad
regards return value and $?, is roughly as follows​:

5.000 has this basic entry​:

  The return value is the exit status of the program as
  returned by the wait() call. To get the actual exit value divide by
  256.

5.005_03​: added usage examples​:

  @​args = ("command", "arg1", "arg2");
  system(@​args) == 0
  or die "system @​args failed​: $?"

  You can check all the failure possibilities by inspecting
  C<$?> like this​:

  $exit_value = $? >> 8;
  $signal_num = $? & 127;
  $dumped_core = $? & 128;

5.6.0 added​:

  Return value of -1 indicates a failure to start the program (inspect $!
  for the reason).

5.10.0 amended return value description and updated the $? processing
example​:

  Return value of -1 indicates a failure to start the program or an
  error of the wait(2) system call (inspect $! for the reason).

  if ($? == -1) {
  print "failed to execute​: $!\n";
  }
  elsif ($? & 127) {
  printf "child died with signal %d, %s coredump\n",
  ($? &amp; 127), ($? & 128) ? 'with' : 'without';
  }
  else {
  printf "child exited with value %d\n", $? >> 8;
  }

--
Any [programming] language that doesn't occasionally surprise the
novice will pay for it by continually surprising the expert.
  -- Larry Wall

@p5pRT
Copy link
Author

p5pRT commented Feb 19, 2010

From @ikegami

On Fri, Feb 19, 2010 at 3​:58 AM, Abigail <abigail@​abigail.be> wrote​:

On Thu, Feb 18, 2010 at 06​:55​:56PM -0500, Eric Brine wrote​:

Except in the reported case, $? *is* set to -1 on error, and people rely
on
that.

$ perl -wle'print $?; system("nonexistant"); print $?'
0
Can't exec "nonexistant"​: No such file or directory at -e line 1.
-1

But here the fork *succeeds*, it's the exec that's failing. There is still
a child process. Which is different from the fork itself failing.

That's only true if exec() failing and fork() are different classes of
failures, and I don't buy that. They are both instances of system() failing
to launch the desired program.

To the user, the relevant modes are​:
- The program couldn't be launched.
- The program was launched and it was killed.
- The program was launched and it returned an error.
- The program was launched and it succeeded.

system() goes to great lengths to provide that interface. exec() errors are
NOT reported as child errors. Instead, system() creates a pipe to transmit
exec() failures back to the parent process so that they can be properly
reported.

(IPC​::Open3 is broken in this regard. Fix pending 5.12's release)

@p5pRT
Copy link
Author

p5pRT commented Jul 9, 2012

From @doy

What kind of system would be able to replicate this problem? Linux seems
to just kill off the process when it runs out of available memory. This
ticket seems like it would be easy to fix, but I don't want to fiddle
around with this sort of thing without being able to tell if it is
actually fixed.

-doy

@p5pRT
Copy link
Author

p5pRT commented Jul 11, 2012

From @iabyn

On Mon, Jul 09, 2012 at 10​:51​:26AM -0700, Jesse Luehrs via RT wrote​:

What kind of system would be able to replicate this problem? Linux seems
to just kill off the process when it runs out of available memory. This
ticket seems like it would be easy to fix, but I don't want to fiddle
around with this sort of thing without being able to tell if it is
actually fixed.

Can't you use 'ulimit -v' or '-u' or similar?

--
"You may not work around any technical limitations in the software"
  -- Windows Vista license

@p5pRT
Copy link
Author

p5pRT commented Jul 11, 2012

From @doy

On Wed, Jul 11, 2012 at 05​:49​:26PM +0100, Dave Mitchell wrote​:

On Mon, Jul 09, 2012 at 10​:51​:26AM -0700, Jesse Luehrs via RT wrote​:

What kind of system would be able to replicate this problem? Linux seems
to just kill off the process when it runs out of available memory. This
ticket seems like it would be easy to fix, but I don't want to fiddle
around with this sort of thing without being able to tell if it is
actually fixed.

Can't you use 'ulimit -v' or '-u' or similar?

I tried that, the subprocess just ended up either receiving SIGKILL or
SIGSEGV, depending on whether it used too much heap or stack space. I
couldn't get the fork() call itself to fail.

-doy

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants