简体   繁体   中英

fork() and execvp() unexpected outcome when used with sudo

So when i invoke this program without sudo. It works fine.

#include <unistd.h>
#include <sys/wait.h>

int main(int argc, char** argv)
{
    if(fork() == 0) execvp(argv[1], &argv[1]);
    // else wait(NULL);
}

But with sudo (when i need to input my password) it gives an odd output:

pasha@skynet:~$ sudo ./a.out bash
[sudo] password for pasha:         
pasha@skynet:~$ root@skynet:~# 

Then on any input the terminal terminates. Further it only happens on a newly spawned terminal. And when the parent waits for the child the problem with sudo disappears.

Can someone explain why?

why this happens

You are fork ing your process, so there are two processes now.

One process is the parent, the process run by your shell, like shell -> fork() -> exec(sudo) -> exec(./a.out) . The parent terminates, because fork returns with nonzero and then main() reaches closing } . main by default returns 0 . So shell sees that your program terminated with exit status 0. And your shell greets you with a new pasha@skynet:~$ prompt line after your program is done.

The other process is the child, run from your program where fork returned zero, like shell -> fork() -> exec(sudo) -> exec(./a.out) -> fork() -> exec(bash) . The child process is bash , it prints root@skynet:~# (it was run after sudo ) and waits for input.

Those two processes are running at the same time - ie. your shell (from which you executed sudo./a.out ) and the newly bash run from your program. Both those programs try to read and write to the same input and output at the same time.

The child process, ie. bash , needs to have exclusive control over the input in the terminal. So the child process bash executes tcsetpgrp . But your shell is that one that is controlling your terminal, not the child process. So the child process either receives signal SIGTTOU or maybe SIGTTIN upon trying to read from the input. Then the child bash executed the default handler for the signals - it terminates.

Running sudo bash & from your shell would cause a similar problem to the one that your program causes.

Your program is correct; try it out with "ls" instead of "bash",

$ ./a.out ls -al /tmp

The reason why it does not seem to work with bash is that bash expect the process to be the group leader of the terminal foreground process group , which it isn't.

That said, while the program is correct, it's severe lack of error handling is offending:-). For example, when calling a program that does not exist, execvp() returns with an error (as opposed to not returning at all) which is ignored. With the effect that... well... you can only guess if it worked.

$ ./a.out frobozzzzz
$ # (hm)

Here's my incarnation of it. Longer. Handling errors. Seeing how it went after child terminated.

#include <assert.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/wait.h>
#include <sys/types.h>


int main(int argc, char** argv)
{
    int status;
    pid_t pid, terminated;

    pid = fork();
    if (pid == -1 /*unlikely*/) {
        perror("fork()");
        exit(EXIT_FAILURE);
    }
    if (pid == 0 /*child*/) {
        if (execvp(argv[1], &argv[1]) != 0) { // when argv[1] is no
                                              // progrm in path
            perror("execvp()");
            exit(EXIT_FAILURE);
        }
        else
            assert(!"not getting here because successful exec() never returns");
    }

    // optional: wait for child to terminate, and print diagnostics
    terminated = waitpid(pid, &status, 0);
    if (terminated == -1) {
        perror("waitpid()");
        exit(EXIT_FAILURE);
    }

    if (terminated == pid) { // how come those not be equal?
        if (WIFEXITED(status))
            fprintf(stderr, "child terminated with exit status %d\n", WEXITSTATUS(status));
        else if (WIFSIGNALED(status))
            fprintf(stderr, "child terminated by %d\n", WTERMSIG(status));
        else
            fprintf(stderr, "see \"man waidpid\" for what that could be\n");
    }

    return 0;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM