When testing compatibility for an update of a legacy program in OpenSUSE, I traced the problem to a strange version difference. The following C program creates a child process in sudo, and then kills this process in sudo:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main (int argc, char *argv[])
{
char execName[80] = "/usr/bin/sudo";
char cmd[128];
int pid = 0;
int SIG = 15;
if (argc > 1) SIG = atoi(argv[1]);
printf("Sudo sleeping for 30 seconds.\n");
printf("Sudo killing with signal %d.\n", SIG);
printf("Use `ps aux | grep sleep` within 30 seconds to verify it was killed. Only this grep should show.\n");
if ((pid = vfork()) == 0) {
execl (execName, execName, "/bin/sleep", "30", (char *)NULL);
perror(execName);
exit (-1);
} else {
if (pid == -1) perror("Bad PID for sleeper");
fprintf( stderr, "sleeper process PID = %d\n", pid );
}
sprintf(cmd, "/usr/bin/sudo kill -%d %d", SIG, pid);
system(cmd);
}
In order to execute this code, users need to be given access to sudo, and sudoers needs to be given access to kill.
This accurately represents how the legacy program works, except the sleeper program is a process that runs continuously, and there are more steps in between. Unfortunately, it is not realistic to abandon this program entirely.
In OpenSUSE 13.2, this program successfully kills the sleeper. However, in OpenSUSE 42.3, the sleeper remains. Why? How can I rewrite this so that I can give my own sudo children in OpenSUSE 42.3 a kill signal?
As an experiment, I changed the final system call to /usr/bin/sudo pkill -15 -P %d, where %d is the process ID. For some reason, this worked across versions, although not reliably. None of the theories I had for why these differ could adequately explain why this works.
The sleeper is a child process of
sudo. You're only killing thesudoprocess, not its children.You can kill an entire process group by negating the PID of the process group leading. This will work as long as
sudodoesn't create a new process group for the children, which doesn't seem likely.Also, if
fork()returns-1you should exit after printing the error message. The usual structure looks like this: