Search code examples
cposixpopen

If child process created by popen() is killed, parent process will quit, even with signal_handler() installed


The overall design of my program is:

  1. I install a signal handler, which set a flag if some signals are sent;
  2. I start an event loop
  3. The event loop quits if flag is turned on, with some finalizing steps.

The code is roughly like this (and just in case someone would like to check the complete version, it can be found here and a Makefile is also prepared):

volatile sig_atomic_t ev_flag = 0;

static void signal_handler(int signum) {
    ev_flag = 1;
    char msg[] = "Signal caught\n";
    write(STDOUT_FILENO, msg, strlen(msg));
}

void install_signal_handler() {
    struct sigaction act;
    act.sa_handler = signal_handler;
    act.sa_flags = SA_RESETHAND;
    if (sigaction(SIGINT, &act, 0)  + sigaction(SIGABRT, &act, 0) +
        sigaction(SIGQUIT, &act, 0) + sigaction(SIGTERM, &act, 0) +
        sigaction(SIGPIPE, &act, 0) + sigaction(SIGCHLD, &act, 0) +
        sigaction(SIGSEGV, &act, 0) + sigaction(SIGTRAP, &act, 0) < 0) {
        perror("sigaction()");
        abort();
    }
}

int main(void) {
    install_signal_handler();
    FILE *child_proc;
    child_proc = popen(child_proc_cmd, "w");
    while (ev_flag == 0) {
        if (fwrite(data, 1, data_size, child_proc) != data_size) {
            perror("fwrite()");
            break;
        }
    }
    printf("\nThe evloop quitted, gracefully\n");
    pclose(child_proc);
    return 0;
}

This design works well for most cases, except one: If the child process is somehow killed by kill <pid> or kill -KILL <pid>, my parent process won't reach

printf("\nThe evloop quitted, gracefully\n");

as expected, even if my signal_handler() is invoked. Any thoughts? Or any bugs in my current design?


Solution

    1. You never call install_signal_handler(), so your signal handler is never executed.

    2. In signal handler you would write from null pointer: write(STDOUT_FILENO, 0, strlen(msg));

    #include <stdio.h>
    #include <stdlib.h>
    #include <signal.h>
    #include <string.h>
    #include <unistd.h>
    
    volatile sig_atomic_t ev_flag = 0;
    
    static void signal_handler(int signum) {
        ev_flag = 1;
        char msg[] = "Signal caught :";
        char *sig = strsignal(signum);
        write(STDOUT_FILENO, msg, strlen(msg));   // Write from buffer
        write(STDOUT_FILENO, sig, strlen(sig));   // Write signal string
        write(STDOUT_FILENO, "\n", 1);
    }
    
    void install_signal_handler()
    {
        struct sigaction act;
        act.sa_handler = signal_handler;
        act.sa_flags = SA_RESETHAND;
        if (sigaction(SIGINT, &act, 0)  + sigaction(SIGABRT, &act, 0) +
            sigaction(SIGQUIT, &act, 0) + sigaction(SIGTERM, &act, 0) +
            sigaction(SIGPIPE, &act, 0) + sigaction(SIGCHLD, &act, 0) +
            sigaction(SIGSEGV, &act, 0) + sigaction(SIGTRAP, &act, 0) < 0) {
            perror("sigaction()");
            abort();
        }
    }
    
    int main(void)
    {
        const char data[] = "Lorem ipsum dolor sit amet\n";
        size_t data_size = strlen(data);
        const char child_proc_cmd[] = "wc -l";
    
        install_signal_handler();    // Install signal handlers
    
        FILE *child_proc;
        child_proc = popen(child_proc_cmd, "w");
        while (ev_flag == 0) {
            if (fwrite(data, 1, data_size, child_proc) != data_size) {
                perror("fwrite()");
                break;
            }
        }
        printf("\nThe evloop quitted, gracefully\n");
        pclose(child_proc);
        return 0;
    }
    

    Output:

    Signal caught :Child exited
    Signal caught :Broken pipe
    fwrite(): Broken pipe
    
    The evloop quitted, gracefully