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 часов.

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 */ }

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

Эта строка:

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

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

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

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

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

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

Другие вопросы по тегам