Search code examples
cshellposix

Using `kill` to trigger a parent's `waitpid`


I'm using sigaction(SIGTSTP, &ctrlz_signal, NULL); to ensure I can catch Ctrl-Z.

void ctrlz_signal(int sigNo) {
    printf("Caught Ctrl-Z  |  curr_chld=%d\n", CURR_CHILD);
    if(CURR_CHILD != 0) kill(CURR_CHILD, SIGTSTP);
}

CURR_CHILD is set through forking:

    sigaction(SIGTSTP, &ctrlz_signal, NULL);

    int         status;
    CURR_CHILD = fork();


    if (CURR_CHILD < 0) {
        // ...

    } else if (CURR_CHILD == 0) {  // child
        // exec(...)

    } else {  // back in parent
        waitpid(CURR_CHILD, &status, 0);  // wait for child to finish
        // ...
    }

It seems like it successfully stops the child process, but then it doesn't trigger the parent's waitpid. I would've thought there would be a signal sent to the parent when I'd use this kill function, but it seems like it isn't since my "shell" gets stuck on that waitpid line.

How do I make it so that, once Ctrl-Z is pressed, the process running an exec command (which is a child, at that point) should be put in the background (stopped), and then the parent process should gain back its control over the flow of the program ?


Solution

  • printf is not an async-signal-safe function (as it is not re-entrant) and therefore must not be used in a signal handler. Using such functions will lead to undefined behavior.

    If you want a child process that is not traced with ptrace to return, you have to specify the WUNTRACED option when you use waitpid.

    Alternatively, you can use ptrace with the PTRACE_TRACEME option, so that it will provide status for the child process even if the WUNTRACED option is not specified in the waitpid call.