[comp.unix.wizards] Confusing documentation about system

roy@phri.UUCP (Roy Smith) (02/26/88)

	I was recently surprised to discover that if you run the following
program fragment:

	printf ("%d\n", system ("exit 0"));
	printf ("%d\n", system ("exit 1"));
	printf ("%d\n", system ("exit 2"));
	printf ("%d\n", system ("exit 3"));

you get:

	0
	256
	512
	768

	Clearly what is going on is that system() is returning the exit
status as described in wait(2), i.e. with the argument to exit() shifted up
one byte, and the low byte containing the termination status.  But, as I
read the man pages, the above fragment should have printed 0, 1, 2, and 3.

	System(3) says, "...returns the exit status of the shell."  Sh(1)
says, under "Special commands ... exit [n]", "the exit status is that of
the last command executed." and, under "Commands", "The value of a
simple-command is its exit status...", and under "Parameter substition",
describing the "?" parameter, "the value returned by the last executed
command in decimal."

	I would (and in fact, did) put all those together, and assume that
the following two code fragments (the first in a C program, the latter in a
shell script) would produce the same output.

	printf ("%d\n", system ("exit 1"));

	#!/bin/sh
	sh -c "exit 1"
	echo $?

but they don't; the first prints "256", the latter "1".

	Is this a case of my being thick (wouldn't be the first time) or is
the documentation indeed misleading?
-- 
Roy Smith, {allegra,cmcl2,philabs}!phri!roy
System Administrator, Public Health Research Institute
455 First Avenue, New York, NY 10016

ok@quintus.UUCP (Richard A. O'Keefe) (02/26/88)

In article <3161@phri.UUCP>, roy@phri.UUCP (Roy Smith) writes:
> 
> 	I was recently surprised to discover that if you run the following
> program fragment:
> 	printf("%d\n", system("exit 0"));
...
> 	printf("%d\n", system("exit 3"));
> you get:
> 	0
...
> 	768
> 
> 	Clearly what is going on is that system() is returning the exit
> status as described in wait(2), i.e. with the argument to exit() shifted up
> one byte, and the low byte containing the termination status.  But, as I
> read the man pages, the above fragment should have printed 0, 1, 2, and 3.

The documentation has always been confusing.  It's not just 4.3BSD.
However, don't BSD systems come with a /usr/include/sys/wait.h which
makes it rather clearer?

The picture is (on a 32-bit machine)
	+----------------+--------+-+-------+
	|xxxxxxxxxxxxxxxx|exit-val|c|termval|
	+----------------+--------+-+-------+
	31 (MSB)	 15	 8 7 6	   0 (LSB)

exit-val is the least significant byte of the value passed to exit().
	 For a stopped process, it is the number of the signal which
	 caused the child to stop.
c	 is 1 if the child dumped core.
termval  is the "termination status" of the child.  It is 0 for
	 successful exit, 0177 for a stopped process, otherwise the
	 number of the signal which killed the child.

You aren't told about stopped processes unless you ask or they are
being traced; wait(2) is supposed to treat them as if they were still running.

system(3) adds its own complication: the *exit status* (NOT return
value!) 127 means "couldn't execute the shell".  That is, if it
couldn't fork a shell, you get ((system(...) >> 8) & 255) == 127.
Note that this is ambiguous!  Consider system("exit 127"), for example.

Summary:
	x = system(command_string);

#ifdef BSD
	if ((x & 0xFF) == 0x7F) {
	    cause_of_death = NOT_DEAD_BUT_SLEEPING;
	    childs_death_signal = (x >> 8) & 0xFF;
	} else
#endif
	if ((x & 0xFF) != 0) {
	    cause_of_death = KILLED_BY_SIGNAL;
	    childs_death_signal = x & 0x7F;
	    child_dumped_core = (x >> 7) & 1;
	} else
	if ((x & 0xFFFF) == 0x7F00) {
	    cause_of_death = COULD_NOT_EXECUTE_SHELL;
	} else {
	    cause_of_death = NATURAL_CAUSES;
	    childs_exit_status = (x >> 8) & 0xFF;
	}

This is what I have culled from manuals, and seems to be right.
Let's have it from someone who *knows*.

dave@galaxia.zone1.com (David H. Brierley) (02/27/88)

In article <3161@phri.UUCP> roy@phri.UUCP (Roy Smith) writes:
>
>	printf ("%d\n", system ("exit 1"));
>you get:
>	256
>
>	Clearly what is going on is that system() is returning the exit
>status as described in wait(2), i.e. with the argument to exit() shifted up
>one byte, and the low byte containing the termination status.  But, as I
>read the man pages, the above fragment should have printed 0, 1, 2, and 3.

This subject has come up several times recently so I thought I'd put my two
cents worth in.  As you have guessed, system() is indeed returning the exit
status as defined by wait().  The initial reason for this is obvious if you
think about it.  The way the system() routine works is to fork off another
process, wait for it to complete, and return the status.  Since system() must
use wait() to wait for the process, the reason for returning the status that
was returned by wait() is initially simply laziness.  Before anyone gets all
up in arms about my calling any of the Supreme UNIX Gods lazy, let me add that
returning the status as defined by wait() is also a good thing.  It's entirely
possible that whoever wrote system() returned the wait() status on purpose
and if not they probably looked back and said "this is a good thing, leave
it alone" when they realized what they had done.  The exit status has other
information in it other than just the value that was passed to exit() and
that information could be of use to someones program.  For example, I might
want to take one action if the program exited with a status of 1 and a
different action if the program died with a segmentation fault.

I think whats really needed is for someone to rewrite the manual page for
system() to clearly indicate that the exit status is defined by wait().
-- 
David H. Brierley
Home: dave@galaxia.zone1.com   ...!rayssd!galaxia!dave
Work: dhb@rayssd.ray.com       {sun,decuac,cbosgd,gatech,necntc,ukma}!rayssd!dhb

ron@topaz.rutgers.edu (Ron Natalie) (02/27/88)

You're confusing shell exit status (to itself) and the process
exit status available to C programs (via wait).  Read the wait(2)
manual page.  The high byte during normal operation contains
the arghument from the exit sys-call in the child.  The low byte
contains the process termination status.  This usually means
whether the process died of some signal (like Illegal Instruction,
Memory Fault, Bus Error, etc...) and whether or not it dumped core.

-Ron

roy@phri.UUCP (Roy Smith) (02/28/88)

ron@topaz.rutgers.edu (Ron Natalie) writes:
> You're confusing shell exit status (to itself) and the process
> exit status available to C programs (via wait).

	The point is, if you read the man pages (including wait(2)), you can't
help but get confused.  Why?  Because there are two different things being
discussed (shell-type exit codes where the argument to exit(2) is available
directly and wait-type exit codes where the argument to exit(2) is up-shifted
a byte) but the same terms are used haphazardly to refer to either or both.
-- 
Roy Smith, {allegra,cmcl2,philabs}!phri!roy
System Administrator, Public Health Research Institute
455 First Avenue, New York, NY 10016

rbj@icst-cmr.arpa (Root Boy Jim) (03/01/88)

   From: Ron Natalie <ron@topaz.rutgers.EDU>

   You're confusing shell exit status (to itself) and the process
   exit status available to C programs (via wait).  Read the wait(2)
   manual page.  The high byte during normal operation contains
   the arghument from the exit sys-call in the child.  The low byte
   contains the process termination status.  This usually means
   whether the process died of some signal (like Illegal Instruction,
   Memory Fault, Bus Error, etc...) and whether or not it dumped core.

   -Ron

Yes, he is. But aren't these two bytes mutually exclusive? Why not
just return the exit status byte, or the negative of the signal number
that killed it? Core dumping adds 256 to the signal number, and being
stopped adds 128 to the signal. Why is it so complex?

	(Root Boy) Jim Cottrell	<rbj@icst-cmr.arpa>
	National Bureau of Standards
	Flamer's Hotline: (301) 975-5688
Tex SEX!  The HOME of WHEELS!  The dripping of COFFEE!!  Take me
 to Minnesota but don't EMBARRASS me!!

ron@topaz.rutgers.edu (Ron Natalie) (03/06/88)

Your idea is supposed to be easier?

-Ron

rbj@icst-cmr.arpa (Root Boy Jim) (03/09/88)

   Your idea is supposed to be easier?

   -Ron

In the default case (normal exit), yes. Of course for some people,
perhaps death by signal is the default case :-)

	(Root Boy) Jim Cottrell	<rbj@icst-cmr.arpa>
	National Bureau of Standards
	Flamer's Hotline: (301) 975-5688
	Please come home with me...  I have Tylenol!!

P.S. And I didn't put any cyanide in it either!