fork () и execvp () неожиданный результат при использовании с sudo - PullRequest
0 голосов
/ 30 апреля 2020

Так что, когда я вызываю эту программу без sudo. Это работает нормально.

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

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

Но с sudo (когда мне нужно ввести свой пароль) это дает странный вывод:

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

Затем на любом входе терминал завершается. Далее это происходит только на недавно появившемся терминале. И когда родитель ждет ребенка, проблема с sudo исчезает.

Может кто-нибудь объяснить, почему?

Ответы [ 2 ]

1 голос
/ 01 мая 2020

почему это происходит

Вы fork обрабатываете свой процесс, поэтому сейчас есть два процесса.

Один процесс является родительским, процесс выполняется ваша оболочка, как shell -> fork() -> exec(sudo) -> exec(./a.out). Родитель завершается, потому что fork возвращается с ненулевым значением, а затем main() достигает закрытия }. main по умолчанию возвращает 0. Таким образом, shell видит, что ваша программа завершена с состоянием выхода 0. И ваша оболочка приветствует вас новой строкой приглашения pasha@skynet:~$ после завершения вашей программы.

Другой процесс - дочерний, запускаемый из вашей программы, где fork вернул ноль, как shell -> fork() -> exec(sudo) -> exec(./a.out) -> fork() -> exec(bash). Дочерний процесс - bash, он печатает root@skynet:~# (он был запущен после sudo) и ожидает ввода.

Эти два процесса выполняются одновременно - ie. ваша оболочка (из которой вы выполнили sudo ./a.out) и недавно bash запускаются из вашей программы. Обе эти программы пытаются читать и записывать на один и тот же вход и выход одновременно.

Дочерний процесс, ie. bash, требуется эксклюзивный контроль над входом в терминал. Таким образом, дочерний процесс bash выполняет tcsetpgrp . Но ваша оболочка - это та, которая контролирует ваш терминал, а не дочерний процесс. Таким образом, дочерний процесс либо получает сигнал SIGTTOU , либо, возможно, SIGTTIN при попытке чтения с входа. Затем дочерний элемент bash выполнил обработчик сигналов по умолчанию - он завершается.

Запуск sudo bash & из вашей оболочки вызовет проблему, аналогичную той, которую вызывает ваша программа.

0 голосов
/ 01 мая 2020

Ваша программа верна; попробуйте это с "ls" вместо "bash",

$ ./a.out ls -al /tmp

Причина, по которой он не работает с bash, заключается в том, что bash ожидает, что процесс будет лидером группы из группы процессов переднего плана терминала , которой это не является.

Тем не менее, несмотря на то, что программа корректна, ее серьезное отсутствие обработки ошибок обидно: - ). Например, при вызове программы, которая не существует, execvp() возвращается с ошибкой (а не с возвратом вообще), которая игнорируется. С эффектом, который ... ну ... вы можете только догадываться, сработало ли это.

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

Вот мое воплощение этого. Longer. Обработка ошибок. Видя, как все прошло после прекращения ребенка.

#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;
}
...