Make other process handle SIGTERM properly - c

I have some program, that I would like to run under supervision system. When program it to be stopped, it is sent SIGTERM signal, and, after some timeout, SIGKILL signal. My issue is that program exits ungracefully on SIGTERM -- it uses default handler.
Neither changing supervision system nor the program is option for me. I consider writing some kind of wrapper, that would fork the program, catch SIGTERM and request graceful exit in it, something in lines of this pseudo-C:
// wrapper.c
setsid();
signal(SIGTERM, &make_child_exit_gracefully);
pid = fork() ? waitpid(pid) : exec("/sbin/naughty-program");
Problem with that approach, that if naughty-program some-why reject request for graceful exit, SIGKILL from supervision system would kill wrapper, but program will just receive SIGHUP, which it can ignore. This way, I can end with wild naughty-program, re-parented to PID1 instead of being killed.
Is there more reliable solution? Is it possible to somehow inject non-trivial code to child process to be used as signal handler? Is it possible to map naughty-program into address space of wrapper and transmit control to it?
Ideally, I am looking for POSIX-compliant solution, although Linux-specific is fine too.

Related

Does the kernel propagates SIGKILL to the process or kills it immediately?

I'm currently learning how signals really works in POSIX-systems. Signals such as SIGTERM can be caught and handled with custom handlers, this means that the kernel propagates the signal to the process itself. On the other hand SIGKILL is not catchable and cannot be handled firstly because it is its main purpose - kill the process. But I'm wondering does this means that the SIGKILL is not even propagated to the process ? I mean if the users will send kill -9 to some process using terminal the kernel will immediately purge that process without even forwarding the signal to it, or the signal will still be forwarded to the process but with the exception that we cannot add a custom handler to it? Sorry, if you consider this question dummy! I'm just starting with POSIX systems
some signals can catch and we name those catchable signals and some are uncatchable. The signals SIGKILL and SIGSTOP cannot be caught, blocked, or ignored.
You can see a complete list of signals and appropriate action here.

c/linux infinite loop application: deallocate memory if kill -9 command is called

I developed a C application in linux that contains an infinite loop while(1).
There are some pointers that are dynamically allocated and are useful under the infinite loop, so the only time to deallocate memory is after interrupting the while(1) by ctrl-z, ctrl-c, kill -9 apppid, killall appname.
So the idea is that I associate new handler that deallocates memory to the interruption events signals.
void deallocatehandler(int signal){ printf("Memory Deallocation\n"); exit(0);}
int main(){
signal(SIGINT, &deallocatehandler);
signal(SIGTSTP, &deallocatehandler);
signal(SIGKILL, &deallocatehandler);
while(1){
/**
Some code here
**/
}
}
If I press ctrl-c or ctrl-z the handler is called but the problem is with SIGKILL. The commands kill -9 and killall doesn't launch the handler.
Has someone an idea why? and is there suggestions to correct it?
The whole point of SIGKILL is to kill the process no matter what. That's why you're not allowed to handle it.
In most cases, you start with a SIGTERM to give the process a chance to exit nicely. SIGKILL is usually used as a last resort when SIGTERM does not work. After all, the expected behavior from SIGTERM is that the process exits. If it doesn't you know that something is wrong.
From the manual
The SIGTERM signal is a generic signal used to cause program termination. Unlike SIGKILL, this signal can be blocked, handled, and ignored. It is the normal way to politely ask a program to terminate.
...
The SIGKILL signal is used to cause immediate program termination. It cannot be handled or ignored, and is therefore always fatal. It is also not possible to block this signal.
In the same document, you can also read this interesting thing
In fact, if SIGKILL fails to terminate a process, that by itself constitutes an operating system bug which you should report.
You can't catch SIGKILL and SIGSTOP signals. So your signal handler wouldn't do anything.
There's nothing you can do when your process receives SIGKILL, let alone any memory cleanup.
On Linux, memory will be cleaned up on program exit, so this is probably not an issue.
Usually such cleanup-on-exit is done for SIGTERM.
The correct answer is don't send SIGKILL (kill -9 should only be used if kill itself doesn't work).
That's not the way to request to a process to terminate itself. Send SIGTERM first and if it doesn't work, then send SIGKILL.
man 7 signal
When you SIGKILL a process, you don't ask it to terminate nicely. You ask the kernel to stop any further execution for that process.
Thus, the process can't be aware it has received a SIGKILL.
But it shouldn't matter for you, since a SIGKILL shall only be issued when SIGTERM has shown no success.
So the idea is that I associate new handler that deallocates memory to the interruption events signals.
Not needed! After the process terminates, whatever the reason was, all memory is deallocated from the kernel. So you have no need to do that manually.
With IPC resources and semaphores you will have this problem which can't be handled properly at all.
Your question is ill-posed for several reasons.
First, malloced memory is freed when your process terminates. Most machines would be completely unusable if modern architectures wouldn't do this automatically.
Second, some signals and some ways of process termination aren't catchable. So for these there is no hope of doing repairwork anyhow. Among the methods that terminate an execution without much cleanup are some signals, abort, quick_exit, _Exit.
Third, using signalhandlers for cleanup jobs is complete overkill. The C library has atexit and at_quick_exit (since C11) handlers that are designed for that purpose. So if you have to do something special when an execution terminates (such as writing some final message to a socket, cleaning up files or shared memory) use the tools that were invented for this.
You can't catch SIGKILL (kill -9) by definition. It is meant as a "last resort" way to kill a process, so for this reason the process must not be able to catch it. For a friendly termination request, check for SIGTERM (kill -15 or kill without specific value).
But you should in general not want to catch such events, unless you need to do very specific clean-up actions. The memory will be deallocated but the Operating System; no need for your program to catch the signals only to free the memory.

zombie process created in code, and killed in another part

I want to write a 'zombie creator' and 'zombie terminator'. Main point is that I want to create zombies in one part and terminate them in other part of code. I'm using C.
Example:
create_zombie(); //let's say it's a spawn, using fork etc.
/* a houndred lines below */
kill_zombie(PID); // PID is determinated by user, I want to leave him the choice
I know how to do this using fork(), if .. else, but that's not the point. I'm looking for some kind of remote control. Is that possible? Sleeping him for a long time could be a solution?
I'm assuming Linux, but the process should be similar on other operating systems. You want to look into the kill() function declared typically declared in the signal.h header file. This will allow you to send a signal to a specific PID from your zombie killer. The easiest approach would be to send your zombie process a kill signal (SIGKILL). SIGKILL cannot be caught or ignored, and immediately kill a process dead.
If you need to do some cleanup in your zombie process, you can create a signal handler with the signal() function. This will allow you to specify a function to call when a process receives a signal. This function would implement your cleanup code and then exit().
On linux, your shell should have a kill command that mimics the functionality of kill(). The syntax is typically kill -s 9 PID. This will send a SIGKILL (signal number 9) to the process PID.
I hope this answer nudges you in the proper direction.
When you fork a process, fork returns 0 in the child process and the child's process id in the parent. You can save them in an array, write them to a file, or write them to a pipe and don't "uncap" the other end until you need it.

Which "fatal" signals should a user-level program catch?

First of all, I do know that there was a similar question here in the past.
But that question wasn't answered properly. Instead, it diverted into suggestion what to do to catch signals.
So just to clarify: I've done whatever needs to be done to handle signals.
I have an application that forks a daemon that monitors the main process through pipe.
If a main process crashes (e.g. segmentation fault), it has a signal handler that writes all the required info to pipe and aborts.
The goal is to have as much info as possible when something bad happens to the application, w/o messing with "normal" operation, such as SIGHUP, SIGUSR1, etc.
So my question is: which signals should I catch?
I mean signals that w/o me catching them would cause application to abort anyway.
So far I've come up with the following list:
SIGINT (^C, user-initiated, but still good to know)
SIGTERM (kill <pid> from shell or, AFAIK, can be result of OutOfMemory)
SIGSEGV
SIGILL
SIGFPE
SIGBUS
SIGQUIT
Does anybody know if I miss something? kill -l has lots of them... :)
I'm looking at my copy of advanced programming the unix environment (Stevens). According to the table in the section on signals, the following POSIX signals will by default terminate the process, if you don't catch them. It wouldn't be unreasonable to monitor all of these that you don't use:
SIGABRT
SIGALRM
SIGFPE
SIGHUP
SIGILL
SIGINT
SIGKILL
SIGPIPE
SIGQUIT
SIGSEGV
SIGTERM
SIGUSR1
SIGUSR2
You can catch all of these except SIGKILL, but hopefully SIGKILL won't come up very often for you.
Note that your signal man page (man 7 signal) should give you the proper list for your system - this is the POSIX list, and may differ depending on your architecture.
You should not catch the signal and write the code to a pipe. This is both unnecessary and not failsafe.
Let me quote an answer from the question you linked to, to point out why it's not failsafe: "What makes you think that a SEGV hasn't already corrupted your program memory"
Now you may be wondering how to do it better, and why I've said it is "unnecessary".
The return code of the waitpid syscall can be checked using WIFSIGNALED() to determine whether the process was terminated normally or via a signal, and WTERMSIG() will return the signal number.
This is failsafe and it does not require a handler or a pipe. Plus, you don't need to worry what to catch, because it will report every signal that terminates your process.
It depends: whatever signal you like if that's useful to inform the user something bad just happened. However SIGKILL and SIGSTOP cannot be caught.

How to kill the parent process and its children on ctrl+C or ctrl+Z

I have the main process in my program that fork() some children processes and then goes into endless loop (Also, the children processes are endless). Now, I want to kill all processes, close a socket, de-attach shared memory, and clean all similar stuff on terminating the program with Ctrl+C or Ctrl+Z. I search the internet and I found that I could do that by sending some signals like SIGSTOP and SIGINT, but I don't know how to do it.So, how can I accomplish this in my program?
From outside the program, you can send any process a signal using the kill command.
By default, kill will send the SIGTERM signal, which will terminate a process, and free its allocated resources. You can use the ps command to find the process ids of your program's processes. Using CTRL-C will only terminate the parent process. It will not kill the child processes. If you just forked, and didn't exec a new program, then all of your child processes will have the same name as the parent, which means you can use the killall command to terminate them all in one go. If you are logged in remotely, then logging out will cause a SIGHUP signal to be sent to all of the processes you spawned during the session, which will terminate them by default.
From inside the program, there is a kill() function that operates similar to the command. You will need the process ids still, so it's important that your parent code remembers the child process id returned by fork.
When your process exits brutally, all resources are certainly freed.
However, if you want to control the behaviour (what order, etc, I don't know what) then you should install a signal handler. See sigaction(2).

Resources