0

I am having trouble using sigtimedwait() to catch SIGCHLD signals on FreeBSD. The following source works well on Debian GNU/Linux 7 but gives me a Resource temporarily unavailable on FreeBSD 9.1:

#include <stdio.h>
#include <signal.h>
#include <errno.h>
#include <stdlib.h>
#include <time.h>

int main() {
        sigset_t set;
        pid_t pid;

        printf("SIGCHLD is %i\n", SIGCHLD);

        sigemptyset(&set);
        sigaddset(&set, SIGCHLD);
        sigprocmask(SIG_BLOCK, &set, NULL);

        pid = fork();

        if(pid == -1) {
                printf("fork failed: %s\n", strerror(errno));
                exit(1);
        } else if(pid) {
                sigset_t set2;
                siginfo_t siginfo;
                struct timespec timeout = {3, 0};
                int signal;

                sigemptyset(&set2);
                sigaddset(&set2, SIGCHLD);

                signal = sigtimedwait(&set2, &siginfo, &timeout);

                if(signal == -1) {
                        printf("sigtimedwait failed: %s\n", strerror(errno));
                        exit(2);
                } else {
                        printf("received signal %i from %i with status %i\n", signal, siginfo.si_pid, siginfo.si_status);
                }
        } else {
                sleep(1);
                exit(123);
        }

        return 0;
}

Output on Linux:

SIGCHLD is 17
received signal 17 from 27600 with status 123

Output on FreeBSD:

SIGCHLD is 20
sigtimedwait failed: Resource temporarily unavailable

Using signal() works fine on BSD but this is not quite what I want. What am I missing?

pp3345
  • 31
  • 3

1 Answers1

0

I think this is a kernel/library bug in FreeBSD. It looks like sigtimedwait is not reporting the signal because it's ignored by default. So you could do 2 things

  1. Install a signal handler for SIGCHLD event. Even it's never getting called since you're blocking the signal, it will workaround the bug.

  2. Use kqueue with EVFILT_SIGNAL which definitely works in this case but is not portable (so you'd need an ifdef)

For 2, here is the equivalent code

     int kq = kqueue();
     struct kevent ke;
     EV_SET(&ke, SIGCHLD, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL);
     kevent(kq, &ke, 1, NULL, 0, NULL);
     if (kevent(kq, NULL, 0, &ke, 1, &timeout) == 1) {
          signal = ke.ident;
     }
     else {
         // Catches errors in the add, timeout, and kevent wait
         signal = -1;
     }
     close(kq);
     // note that siginfo is not populated, there is no way to populate it using kqueue.
Guillaume
  • 2,044
  • 12
  • 11