tags:

views:

39

answers:

3

I've blocked, and then waited for a signal via the following code:

sigset_t set;
sigfillset(&set); // all signals
sigprocmask(SIG_SETMASK, &set, NULL); // block all signals
siginfo_t info;
int signum = sigwaitinfo(&set, &info); // wait for next signal
struct sigaction act;
sigaction(signum, NULL, &act); // get the current handler for the signal
act.sa_handler(signum); // invoke it

The last line generates a segmentation fault, as the handler is set to SIG_DFL (defined as 0). How can I manually invoke the default handler if it's set to SIG_DFL or SIG_IGN? Also note that SIG_IGN is defined as 1.

+1  A: 

I see 2 mistakes in your code :
1) You should reverse the last two lines like this :

act.sa_handler(signum);
sigaction(signum, NULL, &act);

2) You must pass a function handler to the fiedl sa_handler instead of a int. The prototype of the function shoudl look like this :

   /**
    *some where in you code
     */
     void handler (int signal){ /*your code*/}
   /**
    *
    */      
    act.sa_handler = handler;

If you want the default handler to be invoked, you should set the field sa_handler to SIG_DFL and it should work.

Dimitri
I'm afraid you're answering another question :)
Matt Joiner
No, this is the 2 things i saw in your code. But the answer of your question is in the last sentence
Dimitri
+1  A: 

I'm not aware of the way to do it.

Only suggestion I have is to look into the man 7 signal and perform manually the action according the table you see there. Ign is nothing. Core is call to abort(). Term is _exit().

Of course you can also set signal handler back to SIG_DFL and then kill(getpid(),THE_SIG) (or its equivalent raise(THE_SIG)). (I personally do not like raise because on some systems it might produce some messages on stderr.)

Dummy00001
I'm not sure this would work, since I've applied a `sigprocmask()` for all signals to the current signal already.
Matt Joiner
+1 You'd have to unblock just that signal for SIG_DFL, but would have to be careful about SIGSTOP. See my answer.
pilcrow
+1  A: 

As you discovered you cannot invoke SIG_DFL and SIG_IGN per se.

Briefly, imitating normal signal disposition would be quite easy for user-defined sa_handlers, easy enough for SIG_IGN with the caveat that you'd need to waitpid() in the case of CHLD, and more-or-less straightforward but unpleasant for SIG_DFL, re-raising to let the kernel do its magic.

Does this do what you want?

#include <signal.h>
#include <stdlib.h>

/* Manually dispose of a signal, mimicking the behavior of current
 * signal dispositions as best we can.  We won't cause EINTR, for
 * instance.
 *
 * FIXME:  save and restore errno around the SIG_DFL logic and
 *         SIG_IGN/CHLD logic.
 */
void dispatch_signal(const int signo) {
    int stop = 0;
    sigset_t oset;
    struct sigaction curact;

    sigaction(signo, NULL, &curact);

    /* SIG_IGN => noop or soak up child term/stop signals (for CHLD) */
    if (SIG_IGN == curact.sa_handler) {
        if (SIGCHLD == signo) {
          int status;
          while (waitpid(-1, &status, WNOHANG|WUNTRACED) > 0) {;}
        } 
        return;
    }

    /* user defined => invoke it */
    if (SIG_DFL != curact.sa_handler) {
        curact.sa_handler(signo);
        return;
    }

    /* SIG_DFL => let kernel handle it (mostly).
     *
     *  We handle noop signals ourselves -- "Ign" and "Cont", which we
     *  can never intercept while stopped.
     */
    if (SIGURG == signo || SIGWINCH == signo || SIGCONT == signo) return;

    /*  Unblock CONT if this is a "Stop" signal, so that we may be
     *  woken up.
     */
    stop = (SIGTSTP == signo || SIGTTIN == signo || SIGTTOU == signo);
    if (stop) {
        sigset_t sig_cont;

        sigemptyset(&sig_cont);
        sigaddset(&sig_cont, SIGCONT);
        sigprocmask(SIG_UNBLOCK, &sig_cont, &oset);
    }

    /*  Re-raise, letting the kernel do the work:
     *     - Set exit codes and corefiles for "Term" and "Core"
     *     - Halt us and signal WUNTRACED'ing parents for "Stop"
     *     - Do the right thing if we forgot to handle any special
     *       signals or signals yet to be introduced
     */
    kill(getpid(), signo);

    /* Re-block CONT, if needed */
    if (stop) sigprocmask(SIG_SETMASK, &oset, NULL);
}

UPDATE (in response to OP's excellent questions)

1: does this slot in after the sigwaitinfo?

Yes. Something like:

... block signals ...
signo = sigwaitinfo(&set, &info);
dispatch_signal(signo);

*2: Why not raise those signals handled by SIG_IGN, they'll be ignored anyway*

It's slightly more efficient to noop in userspace than by three syscalls (re-raise, unmask, re-mask). Moreover, CHLD has special semantics when SIG_IGNored.

3: Why treat SIGCHLD specially?

Originally (check answer edits) I didn't -- re-raised it in the SIG_IGN case, because IGNored CHLD signals tell the kernel to automatically reap children.

However, I changed it because "natural" CHLD signals carry information about the terminated process (at least PID, status, and real UID). User-generated CHLD signals don't carry the same semantics, and, in my testing, IGNoring them doesn't cause 2.6 to autoreap queued zombies whose SIGCHLD was "missed." So, I do it myself.

4: Why are "stop" related signals unblocking CONT. Will not invoking the default handler for CONT unstop the process?

If we're stopped (not executing) and CONT is blocked, we will never receive the signal to wake us up!

5: Why not call raise instead of the kill line you've given?

Personal preference; raise() would work, too.

pilcrow
I like your attempt, a few things are concerning/confusing; 1: does this slot in after the sigwaitinfo? 2: Why not raise those signals handled by SIG_IGN, they'll be ignored anyway? 3: Why treat SIGCHLD specially? If the default does not wait for children, neither should this. 4: Why are "stop" related signals unblocking CONT. Will not invoking the default handler for CONT unstop the process? 5: Why not call `raise` instead of the `kill` line you've given?
Matt Joiner
@Matt, I'll attempt to answer your questions in the answer...
pilcrow
Great answers, thanks. I'll point out that you don't make use of the special SIGCHLD info anyway. A good point regarding SIGCONT being blocked, as we can't process it through `sigwaitinfo` while stopped?
Matt Joiner
Matt, I access that info through waitpid()s interface, FWIW.
pilcrow