2

I've a parent and a child processes. In the parent I established a signal handler for a SIGCHLD. I send SIGTSTP signal to the child, that trigers SIGCHLD and in SIGCHLD siganl handler in parent I call wait function to get a status of the stopped child. But instead of returning immediately it blocks. Then I send a SIGCONT signal to the child and wait returns with errno set to Interuppted system call. I can't understand what I'm missing.

pid_t pid;


static void sig_chld(int signo);


int main() {

    struct sigaction act, savechld;
    sigemptyset(&act.sa_mask);
    act.sa_flags = 0;


    act.sa_handler = sig_chld;
    if (sigaction(SIGCHLD, &act, &savechld) < 0){
        return errno;
    }

    pid = fork();
    switch (pid){
        case -1:{
            perror("fork failed");
            return errno;
        }
        case 0:{    //child
            if (sigaction(SIGCHLD, &savechld, NULL) < 0)
                return errno;

            execlp(path, name_of_executable, (char*)NULL);
            return errno;
        }
        default:{
            for (;;)
                pause();
        }
    }
    return 0;
}



void sig_chld(int signo) {
    int statol;
    if (wait(&statol) < 0){
        perror("waitt error");
        exit(errno);
    }

    if (WIFSTOPPED(statol)){
        puts("Child is stopped");
    } else if (WIFEXITED(statol)){
        puts("Child exited");
    } else if (WIFCONTINUED(statol)){
        puts("Child continued");
    } else if (WIFSIGNALED(statol)){
        puts("Child is signaled");
        int sig = WTERMSIG(statol);
        psignal(sig, NULL);
    }
}
  • 1
    Not sure what you are confused about. `wait` blocks the parent until child terminates. Stopped child is not a terminated child. – SergeyA Mar 13 '19 at 17:19
  • 1
    From the man page, it waits for process to change state. – Игорь Корпенко Mar 13 '19 at 17:26
  • You can't safely call anything but async-signal-safe functions in a signal handler. On Linux, the list can be found on [the `signal-safety` man page](http://man7.org/linux/man-pages/man7/signal-safety.7.html). None of the functions you are calling - `wait()`, `perror()`, `exit()`, `puts()`, or `psignal()` are async-signal-safe. [Footnote 188 of the C 11 standard](https://port70.net/~nsz/c/c11/n1570.html#note188) even states: "Thus, a signal handler cannot, in general, call standard library functions." – Andrew Henle Mar 13 '19 at 17:41
  • 1
    @AndrewHenle: Both `wait()` and `waitpid()` are listed as async-signal safe in the POSIX description of [Signal Concepts](http://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html#tag_15_04). The Linux page you link to also lists both `wait()` and `waitpid()` as safe. The C standard is almost impossibly restrictive; it is the bare minimum subset and, for all practical purposes, real systems are never as restrictive as the C standard. – Jonathan Leffler Mar 13 '19 at 17:45
  • @JonathanLeffler I missed the `wait()` call. *The C standard is almost impossibly restrictive; it is the bare minimum subset and, for all practical purposes, real systems are never as restrictive as the C standard.* The point was that without platform-specific documentation that states a library function call can safely be called from within a signal handler, it's unsafe to do so and likely invokes undefined behavior. – Andrew Henle Mar 13 '19 at 23:21
  • @AndrewHenle: The [tag:unix] tag hints (rather strongly) that the platform is not as impossibly restrictive as the C standard but that it probably hews fairly close to what POSIX allows. – Jonathan Leffler Mar 13 '19 at 23:22
  • @JonathanLeffler I assumed Linux, and Linux only loosely follows POSIX. "GNU's not Unix" isn't really a good thing. Read the notes on `fork()` on the [`signal-safety` man page](http://man7.org/linux/man-pages/man7/signal-safety.7.html) I seriously doubt after 16+ years POSIX is going to remove `fork()` from the list of functions required to be async-signal-safe just because Linux can't seem to make it happen. AFAIK AIX, HP-UX have no problems, and I **know** Solaris doesn't. – Andrew Henle Mar 13 '19 at 23:26
  • @AndrewHenle — Linux on `fork()` is interesting. I would've thought it was obvious that if a signal handler directly or indirectly calls a function that isn't async-signal safe, then the result is unreliable. It is less obvious that `pthread_atfork()` handlers will fire if you're not familiar with the function. There are suggestions around that using `fork()` in threaded programs is really not a good idea, and this would seem to be part of the reason why; other reasons are based on the fact that there's only one thread in the child process, so all sorts of resources may be locked afterwards. – Jonathan Leffler Mar 13 '19 at 23:33

1 Answers1

5

You have to use waitpid() instead of wait(), and you need to specify the option WUNTRACED to also have stopped children reported with waitpid(), like this:

if (waitpid(-1, &statol, WUNTRACED) < 0) {

Now waitpid() should return immediately and your macro WIFSTOPPED(&statol) should be true.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Ctx
  • 18,090
  • 24
  • 36
  • 51
  • 2
    You could probably use `WNOHANG` too — though it probably doesn't matter too much if you don't loop. However, a process can have multiple children (even children that the current program didn't start, but that the program which executed the current one started and didn't wait for), and you might need to collect more than one corpse. – Jonathan Leffler Mar 13 '19 at 17:49