Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ExitCodes bigger than 255, possible?

If yes, on which operating system, shell or whatever?

Consider the following java program (I'm using java just as an example, any language would be good for this question, which is more about operation systems):

public class ExitCode {     public static void main(String args[]) {         System.exit(Integer.parseInt(args[0]));     } } 

Running it on Linux and bash, it returns always values less equal 255, e.g. (echo $? prints the exit code of the previous executed command)

> java ExitCode 2; echo $? 2  > java ExitCode 128; echo $? 128  > java ExitCode 255; echo $? 255  > java ExitCode 256; echo $? 0  > java ExitCode 65536; echo $? 0 

EDITED: the (only, so far) answer below fully explain what happens on UNIXes. I'm still wondering about other OSes.

like image 731
Davide Avatar asked Oct 07 '08 17:10

Davide


People also ask

What does 255 exit code mean?

Jobs fail on Windows executions hosts with " exit code 255 " when the submission users do not have read and execute permission to the command processor ( cmd.exe ). Grant submission users read and execute permission on cmd.exe to allow job execution without failure.

What is exit status 255 Linux?

While trying to use SSH, you may get a response indicating permission is denied. Or if you get an error with a code of 255, it means there's a problem with your SSH connection.

What does exit status 1 mean?

The "Exit Code 1" is simply a "Generic Exit Code" which means the job failed and this can be for any reason.

What is the range of exit codes for an unsuccessful command execution?

Every command returns an exit status (sometimes referred to as a return status or exit code). A successful command returns a 0, while an unsuccessful one returns a non-zero value that usually can be interpreted as an error code.


2 Answers

Using wait() or waitpid()

It is not possible on Unix and derivatives using POSIX functions like wait() and waitpid(). The exit status information returned consists of two 8-bit fields, one containing the exit status, and the other containing information about the cause of death (0 implying orderly exit under program control, other values indicating that a signal killed it, and indicating whether a core was dumped).

Using sigaction() with SA_SIGINFO

If you work hard, and read the POSIX specification of sigaction() and <signal.h> and Signal Actions, you will find that you can get hold of the 32-bit value passed to exit() by a child process. However, it is not completely straight-forward.

#include <errno.h> #include <signal.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/wait.h> #include <time.h> #include <unistd.h>  static siginfo_t sig_info = { 0 }; static volatile sig_atomic_t sig_num = 0; static void *sig_ctxt = 0;  static void catcher(int signum, siginfo_t *info, void *vp) {     sig_num = signum;     sig_info = *info;     sig_ctxt = vp; }  static void set_handler(int signum) {     struct sigaction sa;     sa.sa_flags = SA_SIGINFO;     sa.sa_sigaction = catcher;     sigemptyset(&sa.sa_mask);      if (sigaction(signum, &sa, 0) != 0)     {         int errnum = errno;         fprintf(stderr, "Failed to set signal handler (%d: %s)\n", errnum, strerror(errnum));         exit(1);     } }  static void prt_interrupt(FILE *fp) {     if (sig_num != 0)     {         fprintf(fp, "Signal %d from PID %d (status 0x%.8X; UID %d)\n",                 sig_info.si_signo, (int)sig_info.si_pid, sig_info.si_status,                 (int)sig_info.si_uid);         sig_num = 0;     } }  static void five_kids(void) {     const int base = 0xCC00FF40;     for (int i = 0; i < 5; i++)     {         pid_t pid = fork();         if (pid < 0)             break;         else if (pid == 0)         {             printf("PID %d - exiting with status %d (0x%.8X)\n",                    (int)getpid(), base + i, base + i);             exit(base + i);         }         else         {             int status = 0;             pid_t corpse = wait(&status);             if (corpse != -1)                 printf("Child: %d; Corpse: %d; Status = 0x%.4X - waited\n", pid, corpse, (status & 0xFFFF));             struct timespec nap = { .tv_sec = 0, .tv_nsec = 1000000 }; // 1 millisecond             nanosleep(&nap, 0);             prt_interrupt(stdout);             fflush(0);         }     } }  int main(void) {     set_handler(SIGCHLD);     five_kids(); } 

When run (program sigexit73 compiled from sigexit73.c), this produces output like:

$ sigexit73 PID 26599 - exiting with status -872349888 (0xCC00FF40) Signal 20 from PID 26599 (status 0xCC00FF40; UID 501) Child: 26600; Corpse: 26599; Status = 0x4000 - waited PID 26600 - exiting with status -872349887 (0xCC00FF41) Signal 20 from PID 26600 (status 0xCC00FF41; UID 501) Child: 26601; Corpse: 26600; Status = 0x4100 - waited PID 26601 - exiting with status -872349886 (0xCC00FF42) Signal 20 from PID 26601 (status 0xCC00FF42; UID 501) Child: 26602; Corpse: 26601; Status = 0x4200 - waited PID 26602 - exiting with status -872349885 (0xCC00FF43) Signal 20 from PID 26602 (status 0xCC00FF43; UID 501) Child: 26603; Corpse: 26602; Status = 0x4300 - waited PID 26603 - exiting with status -872349884 (0xCC00FF44) Signal 20 from PID 26603 (status 0xCC00FF44; UID 501) $ 

With the one millisecond call to nanosleep() removed, the output is apt to look like:

$ sigexit73 sigexit23 PID 26621 - exiting with status -872349888 (0xCC00FF40) Signal 20 from PID 26621 (status 0xCC00FF40; UID 501) Child: 26622; Corpse: 26621; Status = 0x4000 - waited PID 26622 - exiting with status -872349887 (0xCC00FF41) PID 26623 - exiting with status -872349886 (0xCC00FF42) Signal 20 from PID 26622 (status 0xCC00FF41; UID 501) Child: 26624; Corpse: 26623; Status = 0x4200 - waited Signal 20 from PID 26623 (status 0xCC00FF42; UID 501) Child: 26625; Corpse: 26622; Status = 0x4100 - waited PID 26624 - exiting with status -872349885 (0xCC00FF43) PID 26625 - exiting with status -872349884 (0xCC00FF44) $ 

Note that there are only three lines starting Signal here, and also only three lines ending waited; some of the signals and exit statuses are lost. This is likely to be because of timing issues between the SIGCHLD signals being set to the parent process.

However, the key point is that 4 bytes of data can be transmitted in the exit() status when the code uses sigaction(), SIGCHLD, SA_SIGINFO to track the status.

Just for the record, the testing was performed on a MacBook Pro running macOS Mojave 10.14.6, using GCC 9.2.0 and XCode 11.3.1. The code is also available in my SOQ (Stack Overflow Questions) repository on GitHub as file sigexit73.c in the src/so-1843-7779 sub-directory.

like image 59
Jonathan Leffler Avatar answered Nov 11 '22 12:11

Jonathan Leffler


On modern Windows, the OS itself, and the default console shell (CMD.EXE), accept and show exit codes throughout at least the whole range of 32-bit signed integers. Running your example above in CMD.EXE gives the exit codes you asked for :

> java ExitCode 2 > echo %errorlevel% 2  > java ExitCode 128 > echo %errorlevel% 128  > java ExitCode 255 > echo %errorlevel% 255  > java ExitCode 256 > echo %errorlevel% 256  > java ExitCode 65536 > echo %errorlevel% 65536 

Windows doesn't really have the concept of Unix signals, nor does it try to hijack the exit code to add extra information, so as long as your shell (or whatever program ends up reading the exit code) doesn't do that either, you should get back the exit codes you returned. Fortunately, programs that use Microsoft's C runtime (including all programs compiled with MS Visual C++) preserve the exit code as is from exiting processes.

like image 45
Michael Ratanapintha Avatar answered Nov 11 '22 14:11

Michael Ratanapintha