What does WEXITSTATUS(status) return?

Learn what does wexitstatus(status) return? with practical examples, diagrams, and best practices. Covers c, unix, signals development techniques with visual explanations.

Understanding WEXITSTATUS(status): Decoding Child Process Exit Codes

Diagram illustrating a parent process waiting for a child process to exit and then interpreting its status.

Explore the WEXITSTATUS macro in C/Unix, its purpose in extracting the exit status of a child process, and how to correctly interpret its return value for robust application development.

In Unix-like operating systems, when a parent process spawns a child process using fork(), it often needs to know how that child process terminated. The wait() or waitpid() system calls are used for this purpose, returning an integer status value. This status value, however, is not a simple exit code; it's a bitmask containing various pieces of information about the child's termination. To extract the actual exit code, developers rely on a set of macros, among which WEXITSTATUS is crucial. This article delves into what WEXITSTATUS(status) returns and how to use it effectively.

The status Argument: More Than Just an Exit Code

When a child process terminates, the kernel encodes its termination reason into a single integer status value. This value can indicate several scenarios:

  • Normal Termination: The child process called exit(), _exit(), or returned from main().
  • Abnormal Termination (Signal): The child process was terminated by an unhandled signal (e.g., SIGSEGV for a segmentation fault, SIGKILL).
  • Stopped/Continued: The child process was stopped or continued by a signal (relevant for job control).

Because status is a bitmask, direct comparison or arithmetic operations on it are unreliable for determining the exit code. Instead, POSIX provides a set of macros to safely extract specific information.

flowchart TD
    A[Child Process Terminates] --> B{Kernel Encodes Status}
    B --> C[Parent Calls wait()/waitpid()]
    C --> D{Receives 'status' Integer}
    D --> E{Is WIFEXITED(status) true?}
    E -- Yes --> F[WEXITSTATUS(status) returns exit code]
    E -- No --> G{Is WIFSIGNALED(status) true?}
    G -- Yes --> H[WTERMSIG(status) returns signal number]
    G -- No --> I[Other termination reasons (e.g., stopped)]
    F --> J[Parent interprets exit code]
    H --> J

Flowchart illustrating how a parent process interprets the status value from a child.

What WEXITSTATUS(status) Returns

The WEXITSTATUS(status) macro is specifically designed to extract the low-order 8 bits of the child's exit status. This means it returns an integer value between 0 and 255, inclusive. This 8-bit value is the actual exit code that the child process passed to exit() or returned from main().

Crucially, WEXITSTATUS(status) should only be used if WIFEXITED(status) evaluates to true. WIFEXITED(status) is another macro that checks if the child process terminated normally (i.e., not by a signal). If WIFEXITED is false, then the value returned by WEXITSTATUS is undefined and meaningless.

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

int main() {
    pid_t pid;
    int status;

    pid = fork();

    if (pid == -1) {
        perror("fork");
        exit(EXIT_FAILURE);
    } else if (pid == 0) {
        // Child process
        printf("Child process running with PID %d\n", getpid());
        // Child exits with status 42
        exit(42);
    } else {
        // Parent process
        printf("Parent process waiting for child PID %d\n", pid);
        if (waitpid(pid, &status, 0) == -1) {
            perror("waitpid");
            exit(EXIT_FAILURE);
        }

        if (WIFEXITED(status)) {
            printf("Child exited normally with status: %d\n", WEXITSTATUS(status));
        } else if (WIFSIGNALED(status)) {
            printf("Child terminated by signal: %d\n", WTERMSIG(status));
        } else if (WIFSTOPPED(status)) {
            printf("Child stopped by signal: %d\n", WSTOPSIG(status));
        } else {
            printf("Child terminated abnormally for unknown reason.\n");
        }
    }

    return 0;
}

C program demonstrating the use of WIFEXITED and WEXITSTATUS to retrieve a child's exit code.

Why Only 8 Bits? The POSIX Standard

The POSIX standard dictates that the exit status of a process is an integer value, but only the lower 8 bits are guaranteed to be portable and meaningful as an exit code. This means that if a child process calls exit(256), WEXITSTATUS will return 0 (256 modulo 256). Similarly, exit(257) would result in WEXITSTATUS returning 1. This 8-bit limitation is a historical artifact and a design choice to allow other bits in the status integer to convey information about signals, core dumps, and job control.

For most applications, an exit code between 0 and 255 is sufficient to indicate success (typically 0) or various error conditions (non-zero values). If more detailed error information is needed, processes typically write to stderr or use other inter-process communication mechanisms.