I have a program in C. I wish for it to always exit cleanly with exit code of 0 when it gets a SIGTERM. What is the earliest place I can register the signal handler? I added it at the top of my main(), but I worry it might get a sigterm just before the signal registers.
Is it possible to register a signal handler even earlier?
Yes you can. Using platform specific initializers such as gcc's __attribute((constructor)). But that's hardly a robust solution.
If you wish to "to always exit cleanly with exit code of 0 when it gets a SIGTERM", then instruct the process-spawning code to start with SIGTERM blocked.
Your main can then register a signal handler and unblock SIGTERM (with sigprocmask or pthread_sigmask, at which point the signal handler will run immediately if it had been received at any point in between process creation up to the signal-unblocking call.
Essentially, it will defer the delivery of the signal up to a point where you're ready too handle it.
(Note that if you start the process with the signal ignored rather than blocked, then any instance of the signal received up to unignoring the signal will have been lost, as if they never happened. That would seem to go against your stated requirement.)
If you can switch to C++: between start of the program and main global variables are initialized. So in theory you could have code like the following that would be run before main is called.
int f() {
signal(...);
return 0;
}
int x = f();
But you don't have a guarantee in which order global objects are initialized, so x might not be initialized first, but last.
But coming back to your original request: the time between starting the program and main is so short, why do you want to prepare against someone sending a SIGTERM in that short time? Isn't that too unlikely to happen?
If it is possible you could change the parent to ignore SIGTERM and then fork and execve. signal man page says
A child created via fork(2) inherits a copy of its parent's
signal dispositions. During an execve(2), the dispositions of
handled signals are reset to the default; the dispositions of
ignored signals are left unchanged.
So you could start your process ignoring SIGTERM until it sets a handler for SIGTERM.
Related
I wrote a signal handler for a process, and fork() after that, the signal handler will be applied to both parent and child processes. If I replace the child process with "exec", the signal handler is no more.
I know this happens because "exec" call will overwrite the child process address space with it's own.
I just want to know if there is a way to make signal handler work even after "exec" call ?
No. From the man pages:
execve() does not return on success, and the text, data, bss, and stack of the calling process are overwritten by that of the program loaded. The program invoked inherits the calling process's PID, and any open file descriptors that are not set to close on exec. Signals pending on the calling process are cleared. Any signals set to be caught by the calling process are reset to their default behaviour. The SIGCHLD signal (when set to SIG_IGN) may or may not be reset to SIG_DFL.
In fact, if the signal handler were still active after the code had been replaced with some very different code, you could expect all sorts of mayhem when the signal occurred. The signal handler is, after all, just an address to call when something happens (discounting SIG_IGN and SIG_DFL for now). Who knows what piece of code would be at that address when you replace the entire text segment?
If we assign different handler to say SIGABRT, etc.. We might not be able to kill the process, conspicuously, it's not a good idea unless we can. So, the manual says SIGSTOP and SIGKILL cannot be blocked or ignored, does it mean that it must kill or stop the process no matter what handler you assign?
The SIGSTOP and SIGKILL signals cannot be caught or ignored. It is possible to set handlers for them using signal() or sigaction(), but those handlers will never be called, so there is no point in doing so.
i am making a small project which will be incorporated into larger project. basically what it does is keeps track of threads that are created by way of adding them to a main struct which keeps track of what the thread does (its main function) and its pthread_t id. the other struct keeps track of the data to be passed to the function and the element number of where the pthread_t id is stored inside threads[]. its a bit micky mouse and it jumps around a bit but it all works besides when it is time to kill the thread. i get no segfaults and no errors and the program finishes fine, but the thread does not get killed when pthread_kill() is called (the function returns 0 meaning no error and it worked) although the thread continues to run until the main application returns.
pthread_kill() will not kill a thread. The only difference with kill() is that the signal is handled by the designated thread and not handled while that thread has the signal masked (see pthread_sigmask()). A signal like SIGTERM will by default still terminate the entire process.
If you are considering to call pthread_exit() from a signal handler, you should probably use pthread_cancel() instead.
Cancellation is safe if all code that may be cancelled cooperates (or the code that calls it disables cancellation for the time). Most libraries do not care about this, though.
A safer method is to ask the thread to exit without any force, such as by sending a special message to it (if the thread normally processes messages).
Alternatively, don't bother to kill any threads and just call _exit(), _Exit() or quick_exit().
From http://pubs.opengroup.org/onlinepubs/7908799/xsh/pthread_kill.html
As in kill(), if sig is zero, error checking is performed but no signal is actually sent.
so the following
pthread_kill(threads[i].tID, 0);
Wont actually kill the thread. You need to use an actual signal to kill a thread. A list of signals can be found here:
http://pubs.opengroup.org/onlinepubs/7908799/xsh/signal.h.html
I'm trying to write a signal handler to catch any number of consecutive SIGINT signals and prevent the program from exiting. The program is a simple file server. The handler sets a global flag which causes the while loop accepting new connections to end, a call to pthread_exit() ensures that main lets current connections finish before exiting. It all goes like clockwork when I hit ctrl-C once but a second time exits the program immediately.
I tried first with signal():
signal(SIGINT, catch_sigint);
...
static void catch_sigint(int signo)
{
...
signal(SIGINT, catch_sigint);
}
I also tried it using sigaction:
struct sigaction sigint_handler;
sigint_handler.sa_handler = catch_sigint;
sigemptyset(&sigint_handler.sa_mask);
sigint_handler.sa_flags = 0;
sigaction(SIGINT, &sigint_handler, NULL);
Unsure how to "reinstall" this one I just duplicated this code in the handler similar to the handler using the signal() method.
Neither one of these works as I expected.
Additional info:
The program is a simple file server. It receives a request from the client which is simply a string consisting of the requested file name. It utilizes pthreads so that transfers can occur simultaneously. Upon receiving SIGINT I wish for the server to exit the while loop and wait for all current transfers to complete then close. As is, no matter how I code the signal handler a second SIGINT terminates the program immediately.
int serverStop = 0;
...
int main()
{
/* set up the server -- socket(), bind() etc. */
struct sigaction sigint_hadler;
sigint_handler.sa_handler = catch_sigint;
sigint_handler.sa_flags = 0;
sigemptyset(&sigint_handler.sa_mask);
sigaction(SIGINT, &sigint_handler, NULL);
/* signal(SIGINT, catch_sigint); */
while(serverStop == 0)
{
/* accept new connections and pthread_create() for each */
}
pthread_exit(NULL);
}
...
static void catch_sigint(int signo)
{
serverStop = 1;
/* signal(SIGINT, catch_sigint) */
}
I don't think any other code could be pertinent but feel free to ask for elaboration
On Linux, you should not have to reinstall the signal handler, using either signal (which implements BSD semantics by default) or sigaction.
when I hit ctrl-C once but a second time exits the program immediately.
That's not because your handler got reset, but likely because your signal handler is doing something it shouldn't.
Here is how I would debug this issue: run the program under GDB and
(gdb) catch syscall exit
(gdb) catch syscall exit_group
(gdb) run
Now wait a bit for the program to start working, and hit Control-C. That will give you (gdb) prompt. Now continue the program as if it has received SIGINT: signal SIGINT (this will invoke your handler). Repeat the 'Control-C/signal SIGINT' sequence again. If you get stopped in either exit or exit_group system call, see where that is coming from (using GDB where command).
Update:
Given the new code you posted, it's not clear exactly where you call pthread_exit to "ensures that main lets current connections finish before exiting". As written, your main thread will exit the loop on first Control-C, and proceed to call exit which would not wait for other threads to finish.
Either you didn't show your actual code, or the "second Control-C" is a red herring and your first Control-C takes you out already (without finishing work in other threads).
NOTE: this is largely guesswork.
I'm pretty sure that calling pthread_exit in the main thread is a bad idea. If the main thread has quit, then the OS may try to send subsequent signals to some other thread.
I recommend that instead of using pthread_exit in the main thread, you just pthread_join() all the other threads, then exit normally.
But it's also important to ensure that the other threads do not get the signals. Normally this is done with sigprocmask (or maybe more correctly pthread_sigmask, which is the same under Linux) to mask the signal out in the worker threads. This ensures that the signal is never delivered to them.
Note that to avoid race conditions, you should use pthread_sigmask in the main thread just before creating a child thread, then set the signal mask back again in the main thread afterwards. This ensures that there is no window, however small, during which a child thread can possibly get unwanted signals.
I'm not sure to understand. A signal handler should usually not re-install any signal handler (including itself), because the signal handler stays in function till another is installed. See also SA_NODEFER flag to sigaction to be able to catch the signal during its handling.
A signal handler should be short. See my answer to this question. It usually mostly sets a volatile sig_atomic_t variable.
What is not working? Don't do complex or long-lasting processing inside signal handlers.
Please show your code...
I've got a situation where a signal handler needs to, under certain conditions, return without unmasking itself, i.e. after returning the signal should remain blocked in the thread's signal mask. Jumping out of the signal handler with longjmp will not work because I need to return to the exact point that was interrupted like a normal signal handler return. Is there any conformant way to do this, short of using the ucontext_t which was removed from the standards? I cannot change signal handlers or dispositions; all effects must be local to the thread.
The purpose of this code has to do with some atomic operations and potential for a race condition or deadlock. Basically the potentially interrupted code looks like:
atomic_write(&thread_local_flag, 1);
atomic_dec(&global_counter);
If the flag has been set and the counter decremented, all is well and the signal handler has nothing to do, but the signal could possibly arrive between the two instructions. In this case, the signal handler wants to immediately return and let the decrement proceed, but the process is being bombarded with signals (intended to be received by all threads for an arcane synchronization purpose) and there's a possibility it could loop forever (or at least for unbounded time) processing signals while other threads never receive their signals.
If I could leave the signal blocked when the signal handler returns, there would be no problem.
Hmm, the best I've found so far.. This seems to work and does not depend on any ucontext functions, just the structure which was not removed. In the signal handler:
if (thread_local_flag) {
sigaddset(&((ucontext_t *)ctx)->uc_sigmask, sig);
return;
}
Here sig and ctx are the first and third argument to the SA_SIGINFO-type signal handler, respectively.
Any thoughts on whether this is correct usage or a horrible hack (or both)?