What Is a Good Linux Exit Error Code Strategy

What is a good Linux exit error code strategy?

The only convention is that you return 0 for success, and something other than zero for an error. Most well-known unix programs document the various return codes that they can return, and so should you. It doesn't make a lot of sense to try to make a common list for all possible error codes that any arbitrary program could return, or else you end up with tens of thousands of them like some other OS's, and even then, it doesn't always cover the specific type of error you want to return.

So just be consistent, and be sure to document whatever scheme you decide to use.

Are there any standard exit status codes in Linux?

8 bits of the return code and 8 bits of the number of the killing signal are mixed into a single value on the return from wait(2) & co..

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <signal.h>

int main() {
int status;

pid_t child = fork();
if (child <= 0)
exit(42);
waitpid(child, &status, 0);
if (WIFEXITED(status))
printf("first child exited with %u\n", WEXITSTATUS(status));
/* prints: "first child exited with 42" */

child = fork();
if (child <= 0)
kill(getpid(), SIGSEGV);
waitpid(child, &status, 0);
if (WIFSIGNALED(status))
printf("second child died with %u\n", WTERMSIG(status));
/* prints: "second child died with 11" */
}

How are you determining the exit status? Traditionally, the shell only stores an 8-bit return code, but sets the high bit if the process was abnormally terminated.


$ sh -c 'exit 42'; echo $?
42
$ sh -c 'kill -SEGV $$'; echo $?
Segmentation fault
139
$ expr 139 - 128
11

If you're seeing anything other than this, then the program probably has a SIGSEGV signal handler which then calls exit normally, so it isn't actually getting killed by the signal. (Programs can chose to handle any signals aside from SIGKILL and SIGSTOP.)

What are the error exit values for diff?

It depends on your diff command. Mine (GNU diffutils 3.0) says:

An exit status of 0 means no differences were found, 1 means some
differences were found, and 2 means trouble. Normally, differing
binary files count as trouble, but this can be altered by using the
-a or --text option, or the -q or --brief option.

Different BASH exit status codes

You can return any integral exit code you like. The BASH scripting guide page you reference just says it could be confusing when debugging something that returns a well-known code for some other reason.

That page also mentions /usr/include/sysexits.h as an attempt to systematize exit codes for C programmers.

General solution for a program's exit status with set -e

The reason || true works is that conditionals are safe under set -e. This can be extended easily to your scenario.

command && rv=0 || rv=$?

As an aside, you should avoid uppercase for your private variables.

How to return exit code 0 from a failed command

Simply append return 0 to the function to force a function to always exit successful.

function a() {
ls aaaaa 2>&1
return 0
}

a
echo $? # prints 0

If you wish to do it inline for any reason you can append || true to the command:

ls aaaaa 2>&1 || true
echo $? # prints 0

If you wish to invert the exit status simple prepend the command with !

! ls aaaaa 2>&1
echo $? # prints 0

! ls /etc/resolv.conf 2>&1
echo $? # prints 1

Also if you state what you are trying to achieve overall we might be able to guide you to better answers.

Capturing exit status code of child process

You can get the exit status of the child via the first argument of wait(), or the second argument of waitpid(), and then using the macros WIFEXITED and WEXITSTATUS with it.

For instance:

pid_t ret = c2b_popen4("myprog", pin, pout, perr, 0);

if ( ret > 0 ) {
int status;

if ( waitpid(ret, &status, 0) == -1 ) {
perror("waitpid() failed");
exit(EXIT_FAILURE);
}

if ( WIFEXITED(status) ) {
int es = WEXITSTATUS(status);
printf("Exit status was %d\n", es);
}
}

A simplified working example:

failprog.c:

int main(void) {
return 53;
}

shellex.c:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

int main(void)
{
pid_t p = fork();
if ( p == -1 ) {
perror("fork failed");
return EXIT_FAILURE;
}
else if ( p == 0 ) {
execl("/bin/sh", "bin/sh", "-c", "./failprog", "NULL");
return EXIT_FAILURE;
}

int status;
if ( waitpid(p, &status, 0) == -1 ) {
perror("waitpid failed");
return EXIT_FAILURE;
}

if ( WIFEXITED(status) ) {
const int es = WEXITSTATUS(status);
printf("exit status was %d\n", es);
}

return EXIT_SUCCESS;
}

Output:

paul@thoth:~/src/sandbox$ ./shellex
exit status was 53
paul@thoth:~/src/sandbox$

waitpid() will block until the process with the supplied process ID exits. Since you're calling your function with a popen() name and passing pipes to it, presumably your child process doesn't terminate quickly, so that probably wouldn't be the right place to check it, if the call succeeded. You can pass WNOHANG as the third parameter to waitpid() to check if the process has terminated, and to return 0 if the child has not yet exited, but you have to be careful about when you do this, since you get no guarantees about which process will run when. If you call waitpid() with WNOHANG immediately after returning from c2b_popen4(), it may return 0 before your child process has had a chance to execute and terminate with an error code, and make it look as if the execution was successful when it's just about to not be successful.

If the process does die immediately, you'll have problems reading from and writing to your pipes, so one option would be to check waitpid() if you get an error from the first attempt to do that, to check if the read() or write() is failing because your child process died. If that turns out to be true, you can retrieve the exit status and exit your overall program then.

There are other possible strategies, including catching the SIGCHLD signal, since that'll be raised whenever one of your child processes dies. It would be OK, for instance, to call _exit() right from your signal handler, after waiting for the child process (calling waitpid() in a signal handler is also safe) and getting its exit status.



Related Topics



Leave a reply



Submit