Execve не работает должным образом

Я пишу базовую оболочку на c, которая позволит мне выполнять простые команды (меня не просят проверять необязательные аргументы), такие как «ls» на сервере (localhost). Программа должна быть способна работать с несколькими клиентами.

Я сделал все, вплоть до выполнения команды с помощью execve() (Я ДОЛЖЕН ИСПОЛЬЗОВАТЬ ЭТУ ФУНКЦИЮ). Я обнаружил, что execve() возвращает -1 в случае ошибки и ничего не возвращает в случае успеха, поэтому я делаю fork() для выполнения команды в этом процессе.

Теперь к проблеме. Как узнать, успешно ли выполнено execve()? Кажется, я не могу найти проблему, мой код всегда возвращает клиенту «ОК». «csapp.h» — это просто исходный файл, содержащий оболочки для некоторых функций.

#include "csapp.h"

void echo(int connfd, pid_t pid);

int main(int argc, char **argv)
{
    int listenfd, connfd;
    unsigned int clientlen;
    struct sockaddr_in clientaddr;
    struct hostent *hp;
    char *haddrp, *port;
    pid_t pid;

    if (argc != 2) {
        fprintf(stderr, "usage: %s <port>\n", argv[0]);
        exit(0);
    }
    port = argv[1];

    listenfd = Open_listenfd(port);
    while (1) {
        clientlen = sizeof(clientaddr);
        while(1){
            connfd = Accept(listenfd, (SA *)&clientaddr, &clientlen);
            if((pid=Fork())==-1){
                Close(connfd);
            }
            if(pid > 0){
                break;
            }
        }

        /* Determine the domain name and IP address of the client */
        hp = Gethostbyaddr((const char *)&clientaddr.sin_addr.s_addr,
                    sizeof(clientaddr.sin_addr.s_addr), AF_INET);
        haddrp = inet_ntoa(clientaddr.sin_addr);
        printf("server connected to %s (%s)\n", hp->h_name, haddrp);

        echo(connfd, pid);
        Close(connfd);
    }
    exit(0);
}

void trim(char *string){
    string[strlen(string)-1]=0;

}

char* concat(const char *s1, const char *s2)
{
    char *result = malloc(strlen(s1) + strlen(s2) + 1); // +1 for the null-terminator
    // in real code you would check for errors in malloc here
    strcpy(result, s1);
    strcat(result, s2);
    return result;
}

void echo(int connfd, pid_t pid)
{
    size_t n;
    char buf[MAXLINE];
    rio_t rio;

    char *args[2];

    args[1] = NULL;

    Rio_readinitb(&rio, connfd);
    while((n = Rio_readlineb(&rio, buf, MAXLINE)) != 0) {
        trim(buf);
        args[0] = concat("/bin/", buf);
        printf("server received %lu bytes\n", n);
        printf("Command: %s\n",buf);
        pid_t execPID;
        int status;
        if((execPID = fork()) > pid){
            execve(args[0],args,NULL);
        }else{

            wait(&status);
            if(WIFEXITED(status)){
                if (WEXITSTATUS(status) == 0){
                    printf("status: %d\n", status);
                    printf("WIFEXITED: %d\n", WIFEXITED(status));
                    printf("WEXITSTATUS: %d\n", WEXITSTATUS(status));
                        Rio_writen(connfd, "OK\n", 3); 
                }
                else
                        Rio_writen(connfd, "ERROR\n", 6);

            }
        }
        /*if(status == -1){
                Rio_writen(connfd, "ERROR\n", 6);

        }
            else{
                Rio_writen(connfd, "OK\n", 3);
                printf("%d\n", status);
            }*/


    }

}

Вывод для «m» и «ls», отправленных клиентом:

server received 2 bytes
Command: m
status: 0
WIFEXITED: 1
WEXITSTATUS: 0
server received 3 bytes
Command: ls
status: 0
WIFEXITED: 1
WEXITSTATUS: 0
Makefile   client    csapp.c  csapp.o  server.c
README.md  client.c  csapp.h  server

Я был бы очень признателен за помощь, я застрял в этом последние 14 часов.


person Johnny Beltran    schedule 10.08.2018    source источник
comment
Итак, вы начинаете с C и хотите сделать shell-сервер... это очень много. Ваш вопрос неясен, слишком широк и не имеет минимально воспроизводимого примера. Прочитайте Как спросить   -  person Stargateur    schedule 10.08.2018
comment
wait(&status); вы не посмотрели статус ошибки wait(), плюс не используйте wait(), он устарел, используйте waitpid(), чтобы обязательно дождаться хорошего ребенка.   -  person Stargateur    schedule 10.08.2018
comment
if((execPID = fork()) > pid) не имеет никакого смысла, вы должны убедиться, что fork() является успешным, а затем просто сравнить его с 0 сейчас, если это ребенок, как вы сделали в своем первом fork()   -  person Stargateur    schedule 10.08.2018
comment
@Stargateur wait(&status) и waitpid(-1,&status,0) всегда возвращают -1, независимо от того, успешно ли выполнилась команда или нет   -  person Johnny Beltran    schedule 10.08.2018
comment
@Stargateur Вывод: сервер получил 3 байта Команда: ls Проверка: -1 статус: 0 WIFEXITED: 1 WEXITSTATUS: 0 Makefile client csapp.c csapp.o server.c README.md client.c csapp.h сервер сервер получил 7 байт Команда : asdasd execve не удалось: нет такого файла или каталога Проверка: -1 статус: 0 WIFEXITED: 1 WEXITSTATUS: 0   -  person Johnny Beltran    schedule 10.08.2018
comment
Посмотрите на ошибку, значит посмотрите errno, используйте perror("wait():"); для печати.   -  person Stargateur    schedule 10.08.2018
comment
@Stargateur Я знаю, что ожидание возвращает идентификатор завершенного дочернего процесса, тогда это означает, что ожидание и ожидание не могут получить идентификатор дочернего процесса, почему?   -  person Johnny Beltran    schedule 10.08.2018
comment
@Stargateur Хорошо, я добавил perror(wait::) после строки wait(&status) и вывод: wait():: Нет дочерних процессов, так что я думаю, вилка не работает?   -  person Johnny Beltran    schedule 10.08.2018
comment
Я думаю, это потому, что это не родитель, который запускает wait(), делает правильный fork(), как я сказал в своем третьем комментарии.   -  person Stargateur    schedule 10.08.2018
comment
@Stargateur Хорошо, я запутался, я поставил это вместо предыдущего оператора if и все равно не работает: if((execPID = fork()) == -1){ close(connfd); } if(execPID › 0){ execve(args[0],args,NULL); ошибка (сбой выполнения); выход(EXIT_FAILURE);   -  person Johnny Beltran    schedule 10.08.2018
comment
Вы делаете это неправильно, это ребенок должен выполнить команду, поэтому, когда execPID это 0, а не когда > 0   -  person Stargateur    schedule 10.08.2018
comment
@Stargateur ЧУВАК, Я ДОЛЖЕН ТЕБЕ СВОЕЙ ЖИЗНЬЮ, я мог бы поклясться, что pid = 0, когда это родительский процесс, СПАСИБО! Я хочу отметить это как правильный ответ   -  person Johnny Beltran    schedule 10.08.2018


Ответы (3)


Как узнать, успешно ли выполнено execve()?

Во-первых, как вы уже заметили, сервер знает, что сам вызов execve() потерпел неудачу, если он вообще возвращается.

Во-вторых, после завершения дочернего процесса wait() или waitpid() могут сообщить вам, каков был его статус завершения. У тебя, похоже, тоже получилось.

Поэтому, когда вы продолжаете говорить,

Кажется, я не могу найти проблему, мой код всегда возвращает клиенту «ОК».

, что наводит меня на мысль, что ваш фактический вопрос больше похож на «как мне получить вывод команды?» Ответ: через стандартный вывод дочернего процесса и (возможно) стандартные потоки ошибок. Как еще?

По умолчанию дочерний элемент наследует свой стандартный ввод, стандартный вывод и стандартную ошибку от своего родителя, но вы можете задать ему другие, вызвав dup2() в дочернем процессе до execve(). Вы можете делать с этим множество довольно сложных вещей, подключая дочерние потоки к pipe(), которые может прочитать родительский или какой-либо другой процесс, но проще всего использовать dup2(), чтобы просто направить потоки вывода и ошибок дочернего процесса в разъем:

// standard output:
if (dup2(connfd, 1) == -1) { /* handle error */ }

// standard error:
if (dup2(connfd, 2) == -1) { /* handle error */ }

Затем выходные данные команды, если таковые имеются, будут отправлены клиенту напрямую по сети.

person John Bollinger    schedule 10.08.2018

эта строка:

{
    execve(args[0],args,NULL);
}

следует изменить на:

{
    execve( args[0],args,NULL );
    perror( "execve failed" );
    exit( EXIT_FAILURE );
}

Затем код не продолжает выполняться, если вызов execve() терпит неудачу, И если он терпит неудачу, сообщает вам, почему.

Примечание: и exit(), и EXIT_FAILURE взяты из заголовочного файла: stdlib.h

person user3629249    schedule 10.08.2018
comment
Я добавил эти строки, и теперь сервер выводит ошибку execve: Нет такого файла или каталога, когда я набираю несуществующую команду, но он продолжает отправлять клиенту OK. wait(&status) и waitpid(-1,&status,0) всегда возвращают -1 независимо от того, успешно ли выполнилась команда или нет. Почему это происходит? Разве они не должны возвращать -1 только в случае сбоя, потому что именно тогда подпроцесс фактически отправляет статус выхода? - person Johnny Beltran; 10.08.2018
comment
@JohnnyBeltran: Если команда ls, вы выполните ее правильно, только если процесс находится в каталоге /bin (при условии, что ls найден как /bin/ls). Если вы напишете /bin/ls в качестве команды, вы сможете запустить ее в другом месте. Есть функция execvp(), которая ищет каталоги, перечисленные в $PATH (переменная среды); когда вы не используете это (или execvpe(), если это доступно), поиск исполняемого файла на основе пути невозможен. - person Jonathan Leffler; 10.08.2018

Как @Stargateur сказал в комментарии под вопросом, я неправильно сравнивал execPID. По сути, pid положителен, когда это родительский процесс, и равен нулю, когда это дочерний процесс, поэтому изменение if ((execPID = fork()) > pid) на if ((execPID = fork()) == 0) фактически решает проблему.

person Johnny Beltran    schedule 10.08.2018