C 函数从另一个函数接收文件描述符,但在输入文件描述符的dup2上失败

C 函数从另一个函数接收文件描述符,但在输入文件描述符的dup2上失败,c,fork,file-descriptor,execvp,C,Fork,File Descriptor,Execvp,我正在创建一个自定义shell,并尝试让多个管道工作,以及在管道之前进行IO重定向。例如:prog

我正在创建一个自定义shell,并尝试让多个管道工作,以及在管道之前进行IO重定向。例如:
prog
。我的方法是执行管道左侧的命令(
prog
),并将管道的写入端传递给另一个派生子函数的函数,在写入端文件描述符上使用
dup2
,并适当地使用
execvp
。它将对管道的右侧执行相同的操作,但它将以管道的读取端作为输入。请记住,我用来执行VP的函数能够自己处理io重定向

我已经调试并验证了我为管道创建的文件描述符列表是正确的,并且也适当地传递给了子级。但是,当被调用函数的分叉子函数尝试
dup2(inputFdForStdin,STDIN\u FILENO)
STDOUT
的相应
dup2
函数时,我得到一个错误,即文件描述符不好。如何正确地将管道读写端的文件描述符传递给另一个派生子函数的函数

runPipedCommands是一个父函数,它将管道的左右命令“块”分隔开来,并使用适当的文件描述符将每一侧传递给
runSimple
。我不认为我用于
execvp
的命令参数结构是相关的,因此我不会共享该代码

// this function only runs iff there is a pipe within a command string
// input: a list of command structs, each of which contain a list of
//        strings of each command and its arguments (for execvp)
int runPipedCommands(char *args)
{
    // this code just counts the number of pipes in commands
    int numPipes = 0;
    int j = 0;
    int status;
    int i = 0;
    pid_t pid;
    char *cmdToken;
    char *cmdSavePter;
    int childStdin = STDIN_FILENO;
    int childStdout = STDOUT_FILENO;
    int cmdsRun = 0;
    struct command *commands;

    while (args[i])
    {
        if (args[i++] == '|')
            numPipes++;
    }

    printf("number of pipes: %d\n\n", numPipes);

    int pipefds[2 * numPipes];
    // create the pipes
    for (i = 0; i < (numPipes); i++)
    {
        if (pipe(pipefds + i * 2) < 0)
        {
            perror("couldn't pipe");
            exit(EXIT_FAILURE);
        }
    }
    pid = fork();
    if (pid == 0)
    {
        cmdToken = strtok_r(args, "|", &cmdSavePter);
        while (cmdToken)
        {
            fprintf(stderr, "\ncmdToken: %s\n", cmdToken);
            fprintf(stderr, "pipefds: %d, %d\n\n", pipefds[0], pipefds[1]);
            commands = parseW(cmdToken);
            cmdsRun += 1;
            //if not last command
            if (cmdsRun <= numPipes)
            {
                childStdout = pipefds[j + 1];
            }

            //if not first command and not max pipes reached
            if (j != 0)
            {
                childStdin = pipefds[j - 2];
            }

            // close dup2ed fds
            for (i = 0; i < 2 * numPipes; i++)
            {
                close(pipefds[i]);
            }

            runSimple(commands, childStdin, childStdout);
            cmdToken = strtok_r(NULL, "|", &cmdSavePter);
            j += 2;
        }
    }
    else if (pid < 0)
    {
        perror("error");
        exit(EXIT_FAILURE);
    }
    /**Parent closes the pipes and wait for child*/

    for (i = 0; i < 2 * numPipes; i++)
    {
        close(pipefds[i]);
    }

    for (i = 0; i < numPipes + 1; i++)
        wait(&status);
}
void runSimple(struct command *args, int fdin, int fdout)
{
    fprintf(stderr, "in rumSimple\n");
    pid_t pid;
    int i = 0;
    int fd; // store file descriptors

    pid = fork(); // store child pid
    if (pid < 0)  // error
    {
        printf("An error occured while forking child for runSimple.\n");
        exit(EXIT_FAILURE);
    }
    else if (pid == 0) // child
    {
        fprintf(stderr, "runSimple forked child: fdin: %d, fdout: %d\n", fdin, fdout);
        // setting fds below are for the scenario
        // where there is redirection before a pipe
        // set fd to input
        if (dup2(fdin, STDIN_FILENO) < 0)
        {
            perror("runSimple child dup2 stdin:");
            exit(EXIT_FAILURE);
        }

        // set fd to output
        if (dup2(fdout, STDOUT_FILENO) < 0)
        {
            perror("runSimple child dup2 stdout");
            exit(EXIT_FAILURE);
        }

        i = 0;
        // loop and deal with special operators
        while (args[i].cmd)
        {
            // printf("command: %s\n", args[i].cmd[0]);
            // deal with &>
            if (args[i].cmd && !strcmp(args[i].cmd[0], "&>"))
            {
                fd = creat(args[i + 1].cmd[0], 0644);
                if (fd < 0)
                {
                    perror("open");
                    exit(EXIT_FAILURE);
                }

                if (dup2(fd, STDOUT_FILENO) < 0)
                {
                    perror("runSimple: failed dup2 for stdout on &>");
                    exit(EXIT_FAILURE);
                }
                if (dup2(STDOUT_FILENO, STDERR_FILENO) < 0)
                {
                    perror("runSimple: failed dup2 for stderr on &>");
                    exit(EXIT_FAILURE);
                }
                close(fd);
                // args[i] = NULL;
            }

            // deal with 2>
            if (args[i].cmd && !strcmp(args[i].cmd[0], "2>"))
            {

                fd = creat(args[i + 1].cmd[0], 0644);
                if (fd < 0)
                {
                    perror("runSimple: error opening file for 2>");
                    exit(EXIT_FAILURE);
                }
                if (dup2(fd, STDERR_FILENO) < 0)
                {
                    perror("runSimple: failed dup2 on stderr redirecting on 2>");
                    exit(EXIT_FAILURE);
                }
                close(fd);
                // args[i] = NULL;
            }

            // deal with > and 1>
            if (args[i].cmd && (!strcmp(args[i].cmd[0], ">") || !strcmp(args[i].cmd[0], "1>")))
            {
                fd = creat(args[i + 1].cmd[0], 0644);
                if (fd < 0)
                {
                    perror("runSimple: error opening argument for >");
                    exit(EXIT_FAILURE);
                }

                // make child's stdout into file
                if (dup2(fd, STDOUT_FILENO) < 0)
                {
                    perror("runSimple: failure dup2 on redirecting STDOUT to fd on > or 1>");
                    exit(EXIT_FAILURE);
                }
                close(fd);
                // args[i] = NULL;
            }

            // deal with <, making sure to check if args[i] exists first
            if (args[i].cmd && !strcmp(args[i].cmd[0], "<"))
            {
                fd = open(args[i + 1].cmd[0], O_RDONLY);
                if (fd < 0)
                {
                    perror("runSimple: error opening argument for <");
                    exit(EXIT_FAILURE);
                }
                if (dup2(fd, STDIN_FILENO) < 0)
                {
                    perror("runSimple: error dup2 on redirecting stdin to fd on <");
                    exit(EXIT_FAILURE);
                }
                close(fd);
                // args[i] = NULL;
            }

            i++;
        }

        // exec input with appropriate fd
        execvp(args[0].cmd[0], args[0].cmd);
        perror("runSimple:");
        exit(EXIT_FAILURE);
    }
    int status;

    // parent should wait for child to finish if & not specified
    waitpid(pid, &status, 0);
}
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <setjmp.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <fcntl.h>

// store jump mark
sigjmp_buf mark;

// command struct to store
// individual cmds and their
// arguments
struct command
{
    char **cmd;  // list of arguments
    int numArgs; // number of total arguments in line
};

// on sigint kill stdin buffer and show prompt
void sigintHandler(int sig_num)
{
    // Reset handler to catch SIGINT next time.
    signal(SIGINT, sigintHandler);
    // jump back to main to avoid fgets
    siglongjmp(mark, 1);
}

// wait for and read from stdin
char *get_args()
{
    char *args = calloc(sizeof(char), 2048);
    char *result = fgets(args, 2047, stdin);
    // terminate on end of file (ctrl-d)
    if (!result)
    {
        printf("exit\n");
        exit(EXIT_FAILURE);
    }
    char *pter = strrchr(args, '\n');
    if (pter)
        *pter = 0;
    return args;
}

// parse whiteSpace in command, return array of command structs
struct command *parseW(char *cmd)
{
    char *tempCmd = strndup(cmd, strlen(cmd)); // duplicate input command to keep it unchanged
    char *savePter;                            // savePter for strtok_r
    int i = 0, size = 0, numCmds = 0;          // i fo

    char *token = strtok_r(tempCmd, " \t", &savePter); // get the first whitespace token from cmd
    while (token)                                      // loop through and count number of actual args
    {
        // if not operator, add strlen
        if (strcmp(token, "|") && strcmp(token, ">") && strcmp(token, "<") &&
            strcmp(token, "1>") && strcmp(token, "2>") && strcmp(token, "&>"))
        {
        }
        else // is special operator
        {
            numCmds += 2;
        }
        i++;
        token = strtok_r(NULL, " \t", &savePter);
    }
    numCmds++; // increment bc of initial cmd

    // initializing commands list
    struct command *commands = calloc(numCmds + 1, sizeof(struct command));
    strcpy(tempCmd, cmd);
    savePter = NULL;
    i = 0;

    token = strtok_r(tempCmd, " \t", &savePter);
    // loop through and allocate space for each cmd and its arguments
    while (token)
    {
        // error check invalid pipes
        if (!strcmp(token, "|") && i == 0 && size == 0)
        {
            printf("parseW: invalid pipe parsed\n");
            return NULL;
        }

        // if not operator, add strlen
        if (strcmp(token, "|") && strcmp(token, ">") && strcmp(token, "<") &&
            strcmp(token, "1>") && strcmp(token, "2>") && strcmp(token, "&>"))
        {
            size += strlen(token);
        }
        else // is special operator
        {
            if (size == 0)
            {
                printf("parseW: invalid syntax, unexpected operator or token '%s'\n", token);
                return NULL;
            }
            else
            {

                // add all prev commands size
                commands[i].numArgs = numCmds;
                commands[i++].cmd = calloc(size + 1, sizeof(char *));

                // add special operator
                commands[i].numArgs = numCmds;
                commands[i++].cmd = calloc(strlen(token) + 1, sizeof(char *));
                size = 0;
            }
        }
        token = strtok_r(NULL, " \t", &savePter);
    }
    if (!size) // if last token parsed was a pipe, it's invalid
    {
        printf("parseW: invalid pipe parsed\n");
        return NULL;
    }
    commands[i].numArgs = numCmds;
    commands[i].cmd = calloc(size + 1, sizeof(char *)); // add final command allocation

    strcpy(tempCmd, cmd);
    savePter = NULL;
    i = 0;     // cmd index
    int z = 0; // cmd argument index
    token = strtok_r(tempCmd, " \t", &savePter);

    // loop through and set each cmd and their args
    while (token)
    {
        // if not operator, add token
        if (strcmp(token, "|") && strcmp(token, ">") && strcmp(token, "<") &&
            strcmp(token, "1>") && strcmp(token, "2>") && strcmp(token, "&>"))
        {
            commands[i].cmd[z++] = token;
        }
        else // is special operator, reset arg index, inc cmd index
        {
            z = 0;
            commands[++i].cmd[0] = token;
            i++;
        }
        token = strtok_r(NULL, " \t", &savePter);
    }

    return commands;
}

// runs non pipe/redirection commands
void runSimple(struct command *args, int fdin, int fdout)
{
    fprintf(stderr, "in rumSimple\n");
    pid_t pid;
    int i = 0;
    int fd; // store file descriptors

    pid = fork(); // store child pid
    if (pid < 0)  // error
    {
        printf("An error occured while forking child for runSimple.\n");
        exit(EXIT_FAILURE);
    }
    else if (pid == 0) // child
    {
        fprintf(stderr, "runSimple forked child: fdin: %d, fdout: %d\n", fdin, fdout);
        // setting fds below are for the scenario
        // where there is redirection before a pipe
        // set fd to input
        if (dup2(fdin, STDIN_FILENO) < 0)
        {
            perror("runSimple child dup2 stdin:");
            exit(EXIT_FAILURE);
        }

        // set fd to output
        if (dup2(fdout, STDOUT_FILENO) < 0)
        {
            perror("runSimple child dup2 stdout");
            exit(EXIT_FAILURE);
        }

        i = 0;
        // loop and deal with special operators
        while (args[i].cmd)
        {
            // printf("command: %s\n", args[i].cmd[0]);
            // deal with &>
            if (args[i].cmd && !strcmp(args[i].cmd[0], "&>"))
            {
                fd = creat(args[i + 1].cmd[0], 0644);
                if (fd < 0)
                {
                    perror("open");
                    exit(EXIT_FAILURE);
                }

                if (dup2(fd, STDOUT_FILENO) < 0)
                {
                    perror("runSimple: failed dup2 for stdout on &>");
                    exit(EXIT_FAILURE);
                }
                if (dup2(STDOUT_FILENO, STDERR_FILENO) < 0)
                {
                    perror("runSimple: failed dup2 for stderr on &>");
                    exit(EXIT_FAILURE);
                }
                close(fd);
                // args[i] = NULL;
            }

            // deal with 2>
            if (args[i].cmd && !strcmp(args[i].cmd[0], "2>"))
            {

                fd = creat(args[i + 1].cmd[0], 0644);
                if (fd < 0)
                {
                    perror("runSimple: error opening file for 2>");
                    exit(EXIT_FAILURE);
                }
                if (dup2(fd, STDERR_FILENO) < 0)
                {
                    perror("runSimple: failed dup2 on stderr redirecting on 2>");
                    exit(EXIT_FAILURE);
                }
                close(fd);
                // args[i] = NULL;
            }

            // deal with > and 1>
            if (args[i].cmd && (!strcmp(args[i].cmd[0], ">") || !strcmp(args[i].cmd[0], "1>")))
            {
                fd = creat(args[i + 1].cmd[0], 0644);
                if (fd < 0)
                {
                    perror("runSimple: error opening argument for >");
                    exit(EXIT_FAILURE);
                }

                // make child's stdout into file
                if (dup2(fd, STDOUT_FILENO) < 0)
                {
                    perror("runSimple: failure dup2 on redirecting STDOUT to fd on > or 1>");
                    exit(EXIT_FAILURE);
                }
                close(fd);
                // args[i] = NULL;
            }

            // deal with <, making sure to check if args[i] exists first
            if (args[i].cmd && !strcmp(args[i].cmd[0], "<"))
            {
                fd = open(args[i + 1].cmd[0], O_RDONLY);
                if (fd < 0)
                {
                    perror("runSimple: error opening argument for <");
                    exit(EXIT_FAILURE);
                }
                if (dup2(fd, STDIN_FILENO) < 0)
                {
                    perror("runSimple: error dup2 on redirecting stdin to fd on <");
                    exit(EXIT_FAILURE);
                }
                close(fd);
                // args[i] = NULL;
            }

            i++;
        }

        // exec input with appropriate fd
        execvp(args[0].cmd[0], args[0].cmd);
        perror("runSimple:");
        exit(EXIT_FAILURE);
    }
    int status;

    // parent should wait for child to finish if & not specified
    waitpid(pid, &status, 0);
}

// this function only runs iff there is a pipe within a command string
// input: a list of command structs, each of which contain a list of
//        strings of each command and its arguments (for execvp)
int runPipedCommands(char *args)
{
    // this code just counds the number of pipes in commands
    int numPipes = 0;
    int j = 0;
    int status;
    int i = 0;
    pid_t pid;
    char *cmdToken;
    char *cmdSavePter;
    int childStdin = STDIN_FILENO;
    int childStdout = STDOUT_FILENO;
    int cmdsRun = 0;
    struct command *commands;

    while (args[i])
    {
        if (args[i++] == '|')
            numPipes++;
    }

    printf("number of pipes: %d\n\n", numPipes);

    int pipefds[2 * numPipes];
    // create the pipes
    for (i = 0; i < (numPipes); i++)
    {
        if (pipe(pipefds + i * 2) < 0)
        {
            perror("couldn't pipe");
            exit(EXIT_FAILURE);
        }
    }
    pid = fork();
    if (pid == 0)
    {
        cmdToken = strtok_r(args, "|", &cmdSavePter);
        while (cmdToken)
        {
            fprintf(stderr, "\ncmdToken: %s\n", cmdToken);
            fprintf(stderr, "pipefds: %d, %d\n\n", pipefds[0], pipefds[1]);
            commands = parseW(cmdToken);
            cmdsRun += 1;
            //if not last command
            if (cmdsRun <= numPipes)
            {
                childStdout = pipefds[j + 1];
            }

            //if not first command and not max pipes reached
            if (j != 0)
            {
                childStdin = pipefds[j - 2];
            }

            // close dup2ed fds
            for (i = 0; i < 2 * numPipes; i++)
            {
                close(pipefds[i]);
            }

            runSimple(commands, childStdin, childStdout);
            cmdToken = strtok_r(NULL, "|", &cmdSavePter);
            j += 2;
        }
    }
    else if (pid < 0)
    {
        perror("error");
        exit(EXIT_FAILURE);
    }
    /**Parent closes the pipes and wait for children*/

    for (i = 0; i < 2 * numPipes; i++)
    {
        close(pipefds[i]);
    }

    for (i = 0; i < numPipes + 1; i++)
        wait(&status);
}

/*
Main method has an infinite while loop that does:
    1) check if input is from terminal, if so go to 2
    2) print myshell prompt
    3) wait for user stdin by using get_args() function
    4) while loop to loop through individual semicolon separated commands
    5) for each individual command, get each argument token and parse
       whitespace by calling parseW, which returns array of strings
       of every single command and their arguments
    6) hands control off to runSimple(), which runs the list of commands 
       appropriately, and handles command operators (>, <, 1>, etc)

Main method also has a signal handler for SIGINT (ctrl+c), in which event
another signal handler for SIGINT is setup, and a jump is made to the before
the while loop so that the shell can print prompt and listen for commands again
*/
int main(int argc, char *argv[])
{
    int i = 0, size = 0;
    char *semi;
    char *semiSavePter;
    signal(SIGINT, sigintHandler);
    siginterrupt(SIGINT, 1);
    sigsetjmp(mark, 1);

    while (1)
    {
        if (isatty(STDIN_FILENO))
        {
            // print prompt
            printf("\n\033[1;32mmyshell\033[0m>");
            char *line = get_args();

            // parse semis
            semi = strtok_r(line, ";", &semiSavePter);
            // for every semicolon, parse each cmd
            while (semi)
            {
                // get array of args
                struct command *args = parseW(semi);
                // if error in parsing, break and reprint prompt
                if (!args)
                    break;

                // if pipe runPipedCommands
                if (strchr(semi, '|'))
                {
                    int result = runPipedCommands(semi);
                }
                else
                {
                    runSimple(args, STDIN_FILENO, STDOUT_FILENO);
                }

                semi = strtok_r(NULL, ";", &semiSavePter);
            }
        }
    }
    return 0;
};