执行不正常

时间:2018-08-10 01:53:15

标签: c fork exec execve

我正在用c编写一个基本的shell,它将允许我在服务器(本地主机)中执行简单的命令(不要求我检查可选参数),例如“ ls”。该程序必须能够处理多个客户端。

我已经用execve()完成了执行命令之前的所有工作(我必须使用此功能)。我发现execve()在失败时返回-1,并且在成功时不返回任何内容,因此我正在使用fork()在该进程中执行命令。

现在,解决问题。我如何知道execve()是否成功执行?我似乎找不到问题,我的代码总是向客户端返回“ OK”。 “ 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 个答案:

答案 0 :(得分:2)

  

我怎么知道execve()是否成功执行?

首先,正如您已经看到的那样,服务器知道execve()调用本身即使返回也失败了。

第二,子进程终止后,wait()waitpid()可以告诉您其退出状态是什么。您似乎也已经解决了。

所以当你继续说

  

我似乎找不到问题,我的代码总是向客户端返回“ OK”。

,这使我相信您的实际问题更多是“如何获得命令的输出?”。答:通过孩子的标准输出和(可能)标准错误流。还有什么?

默认情况下,子级从其父级继承其标准输入,标准输出和标准错误,但是您可以通过在execve()之前的子进程中调用dup2()来为其提供不同的输入。通过将孩子的流连接到父母或其他进程可以读取的pipe()上,您可以执行各种相当复杂的操作,但是最简单的方法是使用dup2()将孩子的输出和错误流定向到套接字:

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

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

然后,命令的输出(如果有的话)将直接通过电线发送到客户端。

答案 1 :(得分:1)

此行:

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

应更改为:

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

然后,如果对execve()的调用失败,并且失败,则代码不会继续执行。

注意:exit()EXIT_FAILURE都来自头文件:stdlib.h

答案 2 :(得分:1)

正如问题下方的Stargateur中的@ comments所说,我对execPID的比较做错了。基本上,pid是父进程时为正,子进程为零时,因此将if ((execPID = fork()) > pid)更改为if ((execPID = fork()) == 0)实际上可以解决此问题。