Does a KILL signal exit a process immediately? - c

I'm working on a server code that uses fork() and exec to create child processes. The PID of the child is registered when fork() succeeds and cleaned up when the CHILD signal has been caught.
If the server needs to stop, all programs are killed, eventually with a KILL signal. Now, this works by means of iterating through all registered PIDs and waiting for the CHILD signal handler to remove the PIDs. This will fail if child program did not exit properly. Therefore I want to use kill in combination with waitpid to ensure that PID list is cleaned up and log and do some other stuff otherwise.
Consider the next code sample:
kill(pid, SIGKILL);
waitpid(pid, NULL, WNOHANG);
Excerpt from waitpid(2):
waitpid(): on success, returns the process ID of the child whose state has changed; if WNOHANG was specified and one or more child(ren)
specified by pid exist, but have not yet changed state, then 0 is returned. On error, -1 is returned.
Is the process given by pid always gone before the next function kicks in? Will waitpid always return -1 in the above case?

Is the process given by pid always gone before the next function kicks in?
There is no guarantee for that. On a multiprocessor your process might be on CPU 0 while the cleanup in the kernel for the killed process takes place on CPU 1. That's a classical race-condition. Even on singlecore processors there is no guarantee for that.
Will waitpid always return -1 in the above case?
Since it is a race condition - in most cases it perhaps will. But there is no guarantee.
Since you are not interested in the status, this semicode might be more appropriate in your case:
// kill all childs
foreach(pid from pidlist)
kill(pid, SIGKILL);
// gather results - remove zombies
while( not_empty(pidlist) )
pid = waitpid(-1, NULL, WNOHANG);
if( pid > 0 )
remove_list_item(pidlist, pid);
else if( pid == 0 )
sleep(1);
else
break;

The KILL signal handler will run during the killed processes CPU time. This is potentially much later than your waitpid call, especially on a loaded system, so waitpid can very well return 0.

JFYI there is also the option of pidfd_send_signal(). From the manual:
The pidfd_send_signal() system call allows the avoidance of race
conditions that occur when using traditional interfaces (such as
kill(2)) to signal a process. The problem is that the
traditional interfaces specify the target process via a process
ID (PID), with the result that the sender may accidentally send a
signal to the wrong process if the originally intended target
process has terminated and its PID has been recycled for another
process. By contrast, a PID file descriptor is a stable
reference to a specific process; if that process terminates,
pidfd_send_signal() fails with the error ESRCH.
You can see an example on how to use it here.

Related

Systemcall multiprocess child not resuming in parent (fork) Linux

Tracing exec process from the parent to count the number of forks. In the parent I resume the execve and verify it stopped. I resume it (with ptrace(cont..)or SIGCONT). However its not resuming.
I read I should set PTRACE_O_TRACEEXEC option failing to do so results in SIGTRAP being sent to tracee upon a call to execve. Ive added into the child & parent, it doesnt seem to make a difference.
Im using Linux MX, it shouldnt be a problem as this should work in all recent Linux versions.
argument: /bin/bash -c "echo 'first test' | wc -c"
int main(int argc, char *argv[]) {
int status;
int counter = 0;
pid_t pid = fork();
if (pid < 0)
exit(1);
else if (pid == 0) {
ptrace(PTRACE_TRACEME, pid, NULL, NULL);
ptrace(PTRACE_SETOPTIONS, pid, 0, PTRACE_O_TRACEEXEC);
raise(SIGSTOP);
execve(argv[1], &argv[1], NULL);
} else {
wait(&status);
if (WIFSTOPPED(status))
printf("child is stopped");
if (status == 0)
printf("The child process terminated normally.");
if (status == 1)
printf("The child process terminated with an error!.");
ptrace(PTRACE_CONT, pid, NULL, NULL); //<< Child should restart here, not sure if pid = childs or parents.
raise(SIGCONT); // if ptrace(cont) doesnt work then this should make the child start.
if (WIFSTOPPED(status))
printf("child is stopped"); // << this shouldnt print bc Ive continued the child process.
while (1) {
ptrace(PTRACE_SETOPTIONS, pid, 0, PTRACE_O_EXITKILL | PTRACE_O_TRACEFORK);
if (status >> 8 == (SIGTRAP | (PTRACE_O_TRACEFORK << 8)) {
printf("it works");
break;
}
}
}
return 0;
}
The question and some of the code comments indicate some uncertainty about the behavior of ptrace, that in turn suggests unfamiliarity with its documentation. Manual pages are not always easy to understand, but you should definitely start there.
There are several problems with the program presented. Among the more significant are:
PTRACE_TRACEME is the only ptrace command recognized from the tracee. All other ptrace commands must be executed by the tracer, which is the parent in this case. In particular, any PTRACE_SETOPTIONS commands must be executed by the tracer.
The tracer (parent) uses raise() to attempt to send a SIGCONT to the child. This is wrong for two reasons:
raise() sends the specified signal to the calling process, which is not the one that is intended to receive the signal. The kill() function should be used instead to send a signal (of your choice) to another process. But,
A traced process is stopped whenever it receives a signal, but this ptrace-stop is different from "stopped by a signal". The effect of SIGCONT to resume execution applies only to the latter kind of stoppage. Therefore, sending a ptrace-stopped process a SIGCONT is counterproductive if the objective is to make the tracee resume execution. Not only will it not resume the process from ptrace-stop, but when it ultimately is delivered, it will cause the process to reenter ptrace-stop.
The program seems to assume that the second WIFSTOPPED(status) might evaluate to a different value than the first, and in particular, to a value that reflects the child's status at the time of the evaluation of WIFSTOPPED. That is a faulty assumption. WIFSTOPPED and its brethren simply interpret the status integer provided by wait(), which is a static representation of the status of the waited-for process as of the time the wait returned. It will not reflect any status changes since that time.
The program pervasively assumes that all its function calls succeed normally. Generally speaking, this is not a safe assumption. The program should check the return values of its function calls to verify successful completion, and take appropriate action (error message, possibly program termination) in the event of unexpected failure.
Overall, the program does not reflect a good understanding about the usage paradigm of ptrace(), and especially about the role of signals. For example, I take this comment:
I read I should set PTRACE_O_TRACEEXEC option failing to do so results
in SIGTRAP being sent to tracee upon a call to execve.
Yes, if the PTRACE_O_TRACEEXEC is not enabled, then the traced process will be sent a SIGTRAP when it calls execve, but the whole point of that is so that the tracer can catch that and have the option to trace the tracee at that point. As with any signal (other than SIGKILL) delivered to the tracee, the tracer has complete control over whether the signal is actually acted upon by the tracee. Furthermore, if PTRACE_O_TRACEEXEC is in effect then the tracee still stops for tracing when it performs an execve -- the difference is that the status returned to the tracer can distinguish between this case and the one where a SIGTRAP is delivered to the tracee for some other reason (read the docs for details).
So here's a general outline:
The original process forks.
The child executes a PTRACE_TRACEME command to attach itself as tracee to its parent process. (It is not useful for your purposes for the child to execute any other ptrace commands.)
The child proceeds normally to whichever exec-family function is appropriate (all will eventually result in a call to execve). It's not particularly useful for the child to send itself a signal prior to the exec if the only point is to get the attention of the tracer.
The parent loops, repeatedly waiting for the child. It doesn't have to use the wait() function specifically; other functions from that family, such as waitpid(), may also be used.
Upon each successful return from a wait, the parent handles a ptrace or other event. The first of these is likely to be the SIGTRAP generated by the child's initial execve (though it cannot be ruled out that another event, for a different signal, is received first). The status code filled in by wait conveys information about the nature of the event, and in particular, which signal or traced event triggered it.
The parent sets whatever ptrace options it wants when the first ptrace event is received. This can include PTRACE_O_TRACEEXEC if you like, though again, exec events are traced regardless. If you want to count not only the forks of the initial child, but also any forks of that child's descendants, then you probably want to set PTRACE_O_TRACEFORK.
After every event handled, the parent performs a PTRACE_CONT command targeting the tracee. If you are tracing all the forked descendants, then the applicable tracee is not necessarily the initial child.
The parent exits the loop after it has handled a termination event for every traced descendant.

Kill child process spawned with execl without making it zombie

I need to spawn a long-running child process and then kill it from the parent code. At the moment I do like this:
int PID = fork();
if (PID == 0) {
execl("myexec", "myexec", nullptr);
perror("ERROR");
exit(1);
}
// do something in between and then:
kill(PID, SIGKILL);
This does the job in the sense that the child process is stopped, but then it remains as a zombie. I tried to completely remove it by adding:
kill(getpid(), SIGCHLD);
to no avail. I must be doing something wrong but I can't figure out what, so any help would be greatly appreciated. Thanks.
signal(SIGCHLD, SIG_IGN);
kill(getpid(), SIGCHLD);
Presto. No zombie.
By ignoring SIGCHLD we tell the kernel we don't care about exit codes so the zombies just go away immediately.
You have been answered with:
signal(SIGCHLD, SIG_IGN);
to ignore the signal sent to the parent when a child dies. This is an old mechanism to avoid zombies, but zombies are your friends, as my answer will explain.
The zombies are not a bug, but a feature of the system. They are there to complete the fork(2), wait(2), exit(2), kill(2) group of system calls.
When you wait(2) for a child to die, the kernel tests if there's a child running with the characteristics you state in the wait(2). If it exists, the wait(2) will block, because the wait(2) system call is the one used in unix to give the parent the exit status of the waited child. If you use wait() and you have done no fork() a new child previously, wait() should give you an error, because you are calling wait with no fork (i'll stop boldening the system calls in this discussion from here on) but what happens if the parent did a fork but the child died before the parent was capable of making a wait. Should this be taken as an error? No. The system maintains the process table entry for the child proces, until one of two things happen: The parent dies (then all children processess get orphaned, being adopted by process id 1 ---init/systemd--- which is continously blocked in wait calls; or the parent does a wait, in which case the status of one (or the one requested) of the children is reported.
So in a proper usage of the system, it is possible (or necessary) to make a wait call for each fork you make. if you do more waits than forks, you get errors... if you make more forks than waits, you get zombies. In order to compensate this, your code should be changed to make the appropiate wait call.
kill(PID, SIGINT); /* don't use SIGKILL in the first time, give time to your process to arrange its last will before dying */
res = waitpid(PID, &status, 0);
And this will allow the child to die normally. The child is going to die, because you killed it (except if the child has decided to ignore the signal you send to it)
The reason for no race condition here (the child could die before is is wait()ed for) is the zombie process. Zombie processes are not proper processes, they don't accept signals, it is impossible to kill them, because they are dead already (no pun here ;)). They only occupy the process table slot, but no resource is allocated to them. When a parent does a wait call, if there's a zombie, it will be freed and the accounting info will be transferred to the parent (this is how the accounting is done), including the exit status, and if there isn't (because it died prematurely and you had invoked the above behaviour) you will get an error from wait, and the accounting info will be tranferred to init/systemd, which will cope for this. If you decide to ignore the SIGCHLD signal, you are cancelling the production of zombies, but the accounting is being feed in the wron way to init/systemd, and not accounted in the parent. (no such process can be waited for) you cannot distinguish if the wait fails because the child process died or because you didn't spawn it correctly. More is to come.
Let's say that the child cannot exec a new program and it dies (calling exit()). When you kill it, nothing happens, as there's no target process (well, you should receive an error from kill call, but I assume you are not interested in the kill result, as it is irrelevant for this case, you are interested in the status of the child, or how did the child died. This means you need to wait for it. if you get a normal exit, with a status of 1, (as you did an exit in case exec() fails) you will know that the child was not able to exec (you still need to distinguish if the 1 exit code was produced by the child or by the program later run by the child). If you successfully killed the child, you should get a status telling you that the child was killed with signal (the one you sent) and you will know that your code is behaving properly.
In case you don't want to block your parent process in the wait system call (well, your child program could have decided to ignore signals and the kill had no effect), then you can substitute the above by this:
kill(PID, SIGINT);
res = waitpid(PID, &status, WNOHANG);
that will not block the parent, in the case the child program has decided to ignore the signal you send to it. In this case, if wait returns -1 and errno value EINTR, then you know that your child has decided to ignore the signal you sent to it, and you need help from the operator (or be more drastic, e.g. killing it with SIGKILL).
A good approach should be
void alarm_handler()
{
}
...
kill(PID, SIGINT); /* kill it softly (it's your child, man!!) */
void *saved = signal(SIGALRM, alarm_handler);
alarm(3); /* put an awakener, you will be interrupted in 3s. */
res = waitpid(PID, &status, 0);
signal(SIGALRM, saaved); /* restore the previous signal handler */
if (res == -1 && errno == EINTR) {
/* we where interrupted by the alarm, and child didn't die. */
kill(PID, SIGKILL); /* be more rude */
}

SIGKILL to a subprocess tree on parent termination

I have a daemon application that starts several 3rd party executables (all closed-sources and non modifiable).
I would like to have all the child processes to automatically terminate when the parent exits for any reason (including crashes).
Currently, I am using prctl to achieve this (see also this question):
int ret = fork();
if (ret == 0) {
//Setup other stuff
prctl (PR_SET_PDEATHSIG, SIGKILL);
if (execve( "childexecutable" ) < 0) { /*signal error*/}
}
However, if "childexecutable" also forks and spawns "grandchildren", then "grandchildren" is not killed when my process exits.
Maybe I could create an intermediate process that serves as subreaper, that would then kill "someexecutable" when my process dies, but then wait for SIGCHLD and continue to kill child processes until none is left, but it seems very brittle.
Are there better solutions?
Creating a subreaper is not useful in this case, your grandchildren would be reparented to and reaped by init anyway.
What you could do however is:
Start a parent process and fork a child immediately.
The parent will simply wait for the child.
The child will carry out all the work of your actual program, including spawning any other children via fork + execve.
Upon exit of the child for any reason (including deathly signals e.g. a crash) the parent can issue kill(0, SIGKILL) or killpg(getpgid(0), SIGKILL) to kill all the processes in its process group. Issuing a SIGINT/SIGTERM before SIGKILL would probably be a better idea depending on what child processes you want to run, as they could handle such signals and do a graceful cleanup of used resources (including children) before exiting.
Assuming that none of the children or grandchildren changes their process group while running, this will kill the entire tree of processes upon exit of your program. You could also keep the PR_SET_PDEATHSIG before any execve to make this more robust. Again depending on the processes you want to run a PR_SET_PDEATHSIG with SIGINT/SIGTERM could make more sense than SIGKILL.
You can issue setpgid(getpid(), 0) before doing any of the above to create a new process group for your program and avoid killing any parents when issuing kill(0, SIGKILL).
The logic of the "parent" process should be really simple, just a fork + wait in a loop + kill upon the right condition returned by wait. Of course, if this process crashes too then all bets are off, so take care in writing simple and reliable code.

How do I get the PID of a set process in the handler of its own SIGCHLD?

I'm writting a toy shell for university and I have to update the status of a background process when it ends. So I came up with the idea of making the handler of SIGCHLD do that, since that signal is sent when the process ends. The problem is that in order to implement the command jobs, I have to update the status from "running" to "terminated" and first I have to find that specific process in the array I have dedicated to it, and one way to do it is by searching by pid, since the array stores the information that is display in jobs. Each entry stores the process pid, the status (which is a string) and the command itself.
Now the question is:
Is there a way to get the pid of the process that called the signal when it ended?
Right now this is what my handler function looks like:
void handler(int sig){
int child_pid;
child_pid = wait(NULL);
//finds the process with a pid identical
//to child_pid in the list and updates its status
...
}
Since wait(NULL) returns the pid of the first process that ends since it's called, the status is only updated when another background process ends and therefore the wrong process status is updated.
We haven't been tought many things from the wait() and waitpid()functions apart from that they waits for a process to end, so any insight may be helpful.
While it is not a good idea to use wait in a signal handler, you can do the following to accomplish what you are trying to do.
void handler(int sig){
int child_pid;
int status;
child_pid = waitpid(-1, &status, WUNTRACED | WNOHANG);
if(child_pid > 0)
{
// your code
// make sure to deal with the cases of child_pid < 0 and child_pid == 0
}
}
What you are doing is not technically wrong, however, it would be better to use waitpid. When you use waitpid(-1,...) it works similarly to if you used wait(...) and will not only wait for a specified process but any process that terminates. The main difference is that you can specify WUNTRACED which will suspend execution until a process in the wait set becomes either terminated or stopped. The WNOHANG will tell waitpid to not suspend the execution of the process. You don't want your handler to be suspended.
If multiple signals are sent to the same process i.e. due to multiple children terminating at the same time, then it will seem like only one signal was sent because the signal handler will not be executed again. This is due to how signals are sent; when a signal is created it is put into an exception table for the process to then "receive" it; signals do not use queues. To account for this, you will need to iterate over the waitpid(-1,...) call until it returns 0 to make sure you are reaping all of the terminated children.
Additionally, do watch out for where else you are reaping the child (note that if the child has already been reaped then waitpid will return 0 if you use the WNOHANG flag). I would assume that this is what is causing the behavior you are seeing of the status only updating when another background process ends. For example, because you are making a toy shell I assume that you are waiting for the foreground processes somewhere, and if you use the wait function there as well as in your handler you could get 1 of two things to happen. The child gets reaped in the 'wait for foreground process' method and then when the handler is executed there is nothing for it to reap. And 2, the child gets reaped in the handler method, and then the 'wait for foreground process' never exits.

Killing and reaping process

I have a child process specified by pid. This process could be:
Running
Defunct/Zombie (unreaped)
Already reaped (and thus no longer exists)
I would like to kill this process and ensure no zombie remains. Currently my code is
kill(pid, SIGKILL);
int temp;
waitpid(pid, &temp, 0);
Would this work?
EDIT: The process specified by pid is a child of my program.
This looks fine so far, but I wonder why you would let case 3 happen. You should perform some bookkeeping, which of your child processes have terminated and are waiting to be reaped.
One way would be to install a handler for SIGCHLD, setting a flag that a waitpid is in order. That way you guarantee that all pids are actually those of your child processes.
Should work fine, but be sure to check the returnvalue of waitpid.
The call may have returned due to a signal.

Resources