How can I modify my C code to kill a child process in OpenSUSE 42.3 using sudo?

90 views Asked by At

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.

1

There are 1 answers

2
Barmar On

The sleeper is a child process of sudo. You're only killing the sudo process, 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 sudo doesn't create a new process group for the children, which doesn't seem likely.

    sprintf(cmd, "/usr/bin/sudo kill -%d %d", SIG, -pid);

Also, if fork() returns -1 you should exit after printing the error message. The usual structure looks like this:

if ((pid = vfork()) == 0) {
    // child code
} else if (pid == -1) {
    // report error
} else {
    // parent code
}