To answer this, let's refer to the C99 standard. If you read on 7.20.4.3 The exit function, you'll find the following statement:
5 Finally, control is returned to the host environment. If the value of status
is zero or EXIT_SUCCESS
, an implementation-defined form of the status successful termination is returned. If the value of status
is EXIT_FAILURE
, an implementation-defined form of the status unsuccessful termination is returned. Otherwise the status returned is implementation-defined.
What should be emphasized here is that the C standard doesn't specify any specific codes for exit status. While in the snippet above you can see zero mentioned, it's purely part of the exit()
API. Rephrasing the remainder of the sentence, that zero can be mapped onto any other implementation-defined successful termination. Similarly, there's no guarantee that the definitions of EXIT_SUCCESS
and EXIT_FAILURE
won't be mapped onto other codes. In particular, it may not even end up as an integer.
Furthermore, if you look at 7.20.4.6 The system function, you'll notice:
Returns
3 If the argument is a null pointer, the system function returns nonzero only if a command processor is available. If the argument is not a null pointer, and the system function does return, it returns an implementation-defined value.
Which confirms that exit codes are purely implementation-defined, and you can't rely on any particular behavior.
Now, POSIX extends this a bit. If you read on the stdlib.h
header, you'd find:
EXIT_FAILURE
Unsuccessful termination for exit(); evaluates to a non-zero value.
EXIT_SUCCESS
Successful termination for exit(); evaluates to 0.
This already restricts the implementations to use 0
for a successful exit. However, it doesn't really define which of the non-zero values is used for failure.
If you read on exit()
function, you'd find:
The value of status may be 0, EXIT_SUCCESS, EXIT_FAILURE, or any other value, though only the least significant 8 bits (that is, status & 0377) shall be available to a waiting parent process.
Which also extends the C definition by guaranteeing that the exit status in range 0…255 will be made available to other processes.
So, POSIX defines zero as successful exit, EXIT_FAILURE
(some non-zero value) as unsuccessful exit and leaves the remaining values for program use.
However, if you are parsing termination codes from other processes, you should take a look at wait()
function as well. It defines additional macros used to process the return status for non-normal termination statuses, such as termination by signal.
If we go even further, we can find that FreeBSD introduces sysexits.h header to define exit codes even further.
The successful exit is always indicated by a status of 0, or EX_OK. Error numbers begin at EX__BASE to reduce the possibility of clashing with oth er exit statuses that random programs may already return. The meaning of the codes is approximately as follows:
EX_USAGE (64) The command was used incorrectly, e.g., with the wrong number of arguments, a bad flag, a bad syntax in a parameter, or whatever.
[…]
Which means that applications written for FreeBSD may use well-defined exit codes 64+.
To summarize shortly:
- The C standard defines only the API for returning successful and unsuccessful exit. It doesn't define how to use, pass or obtain that result.
- POSIX strictens that to using zero for success, and guarantees that in case of normal process termination, the 8 least significant bits of exit code will be made available to other processes (e.g. via
wait()
).
- FreeBSD reserves exit codes 64+ to create well-defined error condition exit codes.
That's how far the standards go. Now, in the reality most programs use zero to indicate plain success, and non-zero codes to indicate various other statuses. Programs using exit codes to pass specific information often describe them in manpages.
For example, if you look at GNU cmp
:
An exit status of 0 means no differences were found, 1 means some
differences were found, and 2 means trouble.