The Handler Ate Errno

A daemon reaps children from a SIGCHLD handler. The author knew about signal safety and deliberately used write instead of printf.

static void reap(int sig) {
    static const char msg[] = "child exited\n";
    write(STDERR_FILENO, msg, sizeof msg - 1);
    while (waitpid(-1, NULL, WNOHANG) > 0)
        ;
}

Meanwhile the main loop does:

ssize_t n = recv(sock, buf, sizeof buf, 0);
if (n < 0) {
    if (errno == EINTR) continue;
    log_fatal("recv failed: %s", strerror(errno));
    exit(1);
}

Occasionally the daemon dies with recv failed: No child processes. What is the defect?

A

recv should have been retried unconditionally, because a signal handler can make any syscall return a short count rather than an error

B

reap does not save and restore errno, so waitpid overwrites it with ECHILD before the main loop reads it

C

write is not async-signal-safe, so calling it from a handler corrupts the stdio state that strerror later reads

D

waitpid is not async-signal-safe and must not be called from a signal handler