5

I'm having to work on a logging module that can be called from various places in a large project. The problem I have is that sometimes the module may be called from code executed inside a signal handler. Normally, the logging module includes time data using localtime() and strftime(), but of course these calls are not async-signal safe, and can cause deadlocks if called from within a signal handler. Is there any way (on a GNU/Linux system) to tell whether or not my code is currently executing in a signal handler context, apart from e.g., having every signal handler set a flag while processing? I think it would be better to simplify our signal handlers, but in this case I don't have a choice as to where the logging module might be called. It would be nice if I could test and just omit the timestamp information if the module is called during signal handling.

skaffman
  • 398,947
  • 96
  • 818
  • 769
Jay Walker
  • 399
  • 1
  • 10
  • It's often a bad idea to have system calls from signal handlers...are you sure you're protected here? – JXG Sep 07 '11 at 12:27
  • Yeah, that's sort of the problem. I want to avoid unsafe system calls *if* I'm in a signal handler, but have them available if the code is called when I'm not in a signal handler. – Jay Walker Oct 11 '11 at 14:56

3 Answers3

4

First of all, your question ("Am I in a signal handler?") does not have a well-defined answer. Consider the following code:

#include <setjmp.h>
#include <signal.h>

jmp_buf jb;
int foo(int s)
{
    longjmp(jb,1);
}

int main()
{
    if (setjmp(jb)) {
        puts("Am I in a signal handler now, or not?");
        return 0;
    }
    signal(SIGINT, foo);
    raise(SIGINT);
}

With that said, there is a technique you could use to answer this question in a meaningful way for many programs. Choose a signal you don't intend to use, and add it to the sa_mask for all the signals you handle, installing the signal handlers using sigaction. Then you can use sigprocmask to check the current signal mask, and if your designated signal is in the signal mask, that means a signal handler has been invoked and has not yet returned (returning would restore the original signal mask).

R.. GitHub STOP HELPING ICE
  • 208,859
  • 35
  • 376
  • 711
  • That's an interesting point about the longjmp (or siglongjmp); I'll have to think about that and what it actually means to be in a signal handler. My main concern is knowing whether or not I could be in some other part of the code that could be holding a lock, and since that sort of thing wouldn't be setjmp/longjmp safe anyway I don't really use the jmp functions anywhere they could cause that problem. I like the sigaction/sa_mask suggestion though, that sounds like basically exactly what I'm looking for. – Jay Walker Oct 11 '11 at 15:09
4

The easiest way is to log via a (named) pipe (writes upto PIPE_MAX are atomic) , or via an UDP socket (idem). The message origin can be set by the function generating the message. Of course you'll need a process that actually reads and processes the messages, but it can be kept outside the context of a signal handler.


BTW: you don't need a separate process to recieve the messages, you can send the messages to your own process and add (the reading end of) the pipe to the fd_set (given your program sits in a select or poll loop), or poll it periodically.

wildplasser
  • 43,142
  • 8
  • 66
  • 109
  • Also, what you try to do is perverse. You don't longjump from a signal handler(see sigjump et.al). Also, the stdio functions are not considerd signal-safe (they maight call malloc(), etc.) – wildplasser Sep 03 '11 at 14:38
  • Oops, I commented on the program by R. which was supposed to be perverse. Sorry! – wildplasser Sep 03 '11 at 15:06
  • I like this answer in that it gives a sound way to do things, but I don't really want to add a separate process and send all log messages through a pipe if I don't have to. I still wish there were an easy way to detect whether I was in a signal handler; it seems like it shouldn't be difficult for the OS to know. – Jay Walker Oct 11 '11 at 15:02
  • 1
    You can send the messages to your own process and add the pipe to the fd_set (given you are in a select or poll loop) – wildplasser Oct 11 '11 at 16:37
0

Does your system have sigpending? I don't know what the behavior of that function is during a signal handler. If it returns a set flag, though, you could be pessimistic and skip async-unsafe calls if any signals are pending.

mtrw
  • 34,200
  • 7
  • 63
  • 71
  • 1
    I think that sigpending is set only before the signal is delivered. Once the signal handler is entered, the signal isn't set as "pending". – Jay Walker Mar 05 '10 at 22:53