I've got a couple signal handlers I'm using to exit my program cleanly, and I'd like to play nicely with whatever else has hooked them by chaining the signal handlers calls. I'm using sigaction per the man page for signal saying it's not preferred anymore.
Looking at the sigaction struct, there's signal masks and such that are specified, along with several flags. What's the "right" way to call the currently installed handler so that all those options are taken into account?
The answer is "it depends": on what the signal handlers do:
The first reaction from many will be that a signal handler will be used to handle a SIGINT, etc., and terminate a program cleanly.
On the other hand, there are (more or less) benign signals such as SIGWINCH (which you would like to not stop your program).
In "terminate a program cleanly", there may not be room for other programs to do something useful. For instance, the proposed chained-handler may close files that you rely upon. So you have to start with a good knowledge of what the other handlers do.
If the signal handler is in the latter class, simply updating a variable which the application can test, then the signal handler function is just another function. When you call signal to associate a signal with a function, that returns the previous handler (which may be one of the magic values, e.g.,. SIG_DFL, SIG_IGN). If you save that, it's possible to check if it is none of those, and (presumably) a genuine function. Calling that function would continue execution as if it were part of your current signal handler.
It is not a new idea (I used it in the late 1990s), and is discussed occasionally:
signal() overwriting other signal handlers
sigaction - how to find and invoke previous signal handler (sa_handler)
Use reentrant functions for safer signal handling
And of course:
signal - signal management
signal.h - signals
Well, the proper answer IMO is "Don't do that". I think you should reconsider if you plan to chain signal handlers.
Basically, if you have something so critical it has to be cleaned up even if a fatal signal arrives, then you should do that part quickly and reset the signal handlers before letting any other code run.
Signals such as SIGTERM and SIGQUIT should be handled by events that terminate your program in the normal fashion. Typically your signal handler writes on a pipe to message the normal event loop in the application, or sets a global variable if you don't have an event loop.
Perhaps you know this, but please also make sure to read the list of functions that are safe to call from a signal handler. It should be in the man page. Anything except for that list is not safe.
Related
The socket documentation for linux (man 7 socket) says that you can set your socket to be O_ASYNC and then receive a signal when the socket is ready for read/write.
However, it seems most people use epoll instead. What is the reason for using epoll rather than this asynchronous signaling system?
If you have a central loop where you catch all kind of events makes it very easy to write a single threaded application and you don't have to take care about all the synchronization problems which may occur if you are running with different execution contexts.
If you use a signal handler you must take care that you never call a non-reentrant function from the signal handler context. There is a list of Async-signal-safe functions you are allowed to call. And as you can see, it is a short list! As a result your signal handler can not do much, maybe only set a flag or send a message and the real work must be done "somewhere". In fact, signal handlers are very limited.
And using signal handlers in multi threaded applications is also not so easy as it looks in the first place, as the handler is per task and not per thread. Read more: signal handler function in multithreaded environment
There are ways to do some work with linux signal handlers.
We can either register system handlers for every signals (if we have sourcecode) or
Run the process under strace to view them.
Stratergy 1:
But if we dont have source code, how can we catch a signals to an application to do something with it and return back? (not a one time debugging but permanent feature). [may be hack a system call?]
Stratergy 2:
And in case we do have source code, is writing to a file safe in case of multiple signals ? or is it more safe to execute signal handler in a fork() process and discard SIGCHLD? what happens if another signals comes in when handling previous signal?
For your Stratergy 2, depends on how your log files are written and how the signals are triggered (asynchronously or not). Normally stdio library functions are not async-signal-safe.
See details in http://man7.org/linux/man-pages/man7/signal-safety.7.html
To avoid problems with unsafe functions, there are two possible
choices:
1. Ensure that (a) the signal handler calls only async-signal-safe
functions, and (b) the signal handler itself is reentrant with
respect to global variables in the main program.
2. Block signal delivery in the main program when calling functions
that are unsafe or operating on global data that is also accessed
by the signal handler.
Stratergy 1: But if we dont have source code, how can we catch a signals to an application to do something with it and return back? (not a one time debugging but permanent feature). [may be hack a system call?]
To intercept a signal delivered to a process there are at least 2 ways:
ptrace(2) (which is what strace uses) see this answer for an example.
LD_PRELOAD: (I'd not advise this approach) you can use it to set handlers for every signal and replace signal and sigaction with two wrapper functions to prevent the program from overriding your signal handlers (please note the recommendations in this other answer).
Can a signal handler be interrupted by another signal (except of SIGKILL, SIGSTOP)?
Therefore, do I need to check for EINTR in my signal handler when calling interruptable syscalls?
(Linux and other Unixes)
Yes, the execution of a signal handler may itself be interrupted by the delivery of another signal.
There are a few nuances, however.
By default, user-defined signal handlers temporarily block the very signal which invoked them. This is the default behavior of sigaction unless the SA_NODEFER flag is set. (This is also the behavior of the older, discouraged signal function, at least on most implementations.)
Additionally, sigaction can explicitly block signals during the handler's execution by setting the sa_mask member of the const struct sigaction. Most code you see will explicitly empty this member during struct initialization, though it is often more robust to sigfillset this member and not worry about interruptions.
So, again, yes. Handle EINTR, errno, &c. as appropriate, or, better yet, design the handler to do no more than set a sig_atomic_t flag and avoid many worries.
Reading about interrupts in linux, I understand that their handlers will run till completion (lets not consider the bottom halves here). So, assume that my code has SIGINT handler registered (using the signal()/sigaction() call) with a while(1)-loop in it (i.e the handler never returns).
If I quit my program abruptly while running, then shouldn't this scenario freeze my machine entirely? Won't my machine with only one CPU core go into an infinite loop?
What I mean is; since my interrupt handler is not returning, won't the CPU be stuck in executing the while(1) code only? (i.e no other process will get the chance of running, because there won't be any context-switch/preemption inside the handler or can the interrupt handler get preempted in between running the while(1) loop?)
You definitely mix signal handlers and interrupt handlers, despite they have similar handling. Unlike you are writing kernel code you won't meet interrupt handlers directly.
But, game rules for signal handlers are very similar. You should either exit from a signal handler or finish the program (and, the latter is analog for stopping the whole system, for the kernel land). This includes exotic ways for exiting signal handlers as longjmp().
From kernel POV, a process in forever loop in an interrupt handler doesn't differ from a process with the same loop in a usual code piece like main(). Entering a signal handler modifies signal mask but doesn't change things radically. Such process can be stopped, traced, killed in the same manner as outside of signal.
(All this doesn't concern some special process classes with advanced credentials. E.g. X Window server can be special because it disables some kernel activity during its video adapter handling. But you likely should know the needed safety rules when writing such software.)
This seems like a silly question, but I can't find the answer to it anywhere I look. I know that in UNIX, signals are handled asynchronously. If I write a function that handles a signal, where is that function run? Is a new thread spawned? Is an existing thread interrupted somehow? Or is this handled in a system thread like asynchronous I/O is?
A signal function is executed as if a thread in the process has been interrupted. That is, the signal handler is called using the signaled thread and the stack is rearranged so that when the signal handler returns the thread continues execution. No new threads are introduced.
An existing process thread is interrupted until the function returns. There are serious restrictions on what it can safely do to ensure it doesn't corrupt state of function calls the thread was in the middle of - specifically, any functions it calls that the thread may have already been calling must be async reentrant. See the man pages e.g. signal, sigaction for further details or ask more specific questions as you like.
It's not a separate thread, but your code is hastily suspended. That's why only a limited subset of the POSIX calls is available.
From the signal man page:
The routine handler must be very careful, since processing elsewhere was interrupted at some arbitrary point. POSIX has the concept of "safe function". If a signal interrupts an unsafe function, and handler calls an unsafe function, then the behavior is undefined. Safe functions are listed explicitly in the various standards.