Search code examples
cstdinexecvp

stdin into execvp() while using fork() and pipe()


So I am trying to read from standard input and then get the input ready so that later on it can be used inside execvp().

What I am implementing here is basically a pipe for some terminal commands.

Here is how an example of my code goes.

input:

ls -s1

sort -n

output:

commands[0]="ls"

commands[1]="-s1"

commands2[0]="��""

commands2[1]="��""

sort: cannot read: t: No such file or directory

Here is my code

# include <stdlib.h>
# include <stdio.h>
# include <unistd.h>
# include <string.h>
# include <sys/wait.h>
# define BUF_SIZE 256
    

int main()
{
    char buffer[BUF_SIZE];
    char *commands[5];
    char *commands2[5];
    int argc = 0;
    int argc2 = 0;

    fgets(buffer, BUF_SIZE, stdin);
        
        for ( commands[argc] = strtok(buffer, " \t\n"); 
              commands[argc] != NULL; 
              commands[++argc] = strtok(NULL, " \t\n") ) {
            printf("commands[%d]=\"%s\"\n", argc, commands[argc]);
        }
        commands[argc] = NULL;

    fgets(buffer, BUF_SIZE, stdin);
        
        for ( commands2[argc2] = strtok(buffer, " \t\n"); 
              commands2[argc2] != NULL; 
              commands2[++argc2] = strtok(NULL, " \t\n") ) {
            printf("commands2[%d]=\"%s\"\n", argc2, commands2[argc]);
        }
        commands2[argc2] = NULL;

    int my_pipe[2];

    if (pipe(my_pipe) == -1)
    {
        perror("cannot create pipe\n");
    }

    pid_t my_pid;

    my_pid = fork();

    if (my_pid < 0)
    {
        perror("Failed fork\n");
    }

    if (my_pid > 0)
    {
        close(my_pipe[1]);
        dup2(my_pipe[0], 0);
        close(my_pipe[0]);

        wait(NULL);
        execvp(commands2[0],commands2); 
    }
    else
    {
        close(my_pipe[0]);   
        dup2(my_pipe[1], 1);
        close(my_pipe[1]);

        execvp(commands[0],commands);
    }
}

Solution

  • One major problem is that you read the second line over the first in buffer, and the commands[] array contains pointers into buffer too. That's not a recipe for happiness. The simplest fix is to define char buffer2[BUF_SIZE]; and use that in the second fgets() call and for loop.

    Using argc in printf("commands2[%d]=\"%s\"\n", argc2, commands2[argc]); is a copy'n'paste bug — it should reference argc2 twice. This helped hide the previous problem.

    Note that perror() does not exit; your code blunders on if pipe() fails, or if fork() fails.

    The wait() in if (my_pid > 0) is bad; remove it.

    If execvp() fails, you should report an error and exit with a non-zero status.

    Putting those changes together yields code such as:

    #include <stdlib.h>
    #include <stdio.h>
    #include <unistd.h>
    #include <string.h>
    #include <sys/wait.h>
    
    #define BUF_SIZE 256
    
    int main(void)
    {
        char buffer[BUF_SIZE];
        char buffer2[BUF_SIZE];
        char *commands[5];
        char *commands2[5];
        int argc = 0;
        int argc2 = 0;
    
        fgets(buffer, BUF_SIZE, stdin);
    
        for (commands[argc] = strtok(buffer, " \t\n");
             commands[argc] != NULL;
             commands[++argc] = strtok(NULL, " \t\n"))
        {
            printf("commands[%d]=\"%s\"\n", argc, commands[argc]);
        }
        commands[argc] = NULL;
    
        fgets(buffer2, BUF_SIZE, stdin);
    
        for (commands2[argc2] = strtok(buffer2, " \t\n");
             commands2[argc2] != NULL;
             commands2[++argc2] = strtok(NULL, " \t\n"))
        {
            printf("commands2[%d]=\"%s\"\n", argc2, commands2[argc2]);
        }
        commands2[argc2] = NULL;
    
        int my_pipe[2];
    
        if (pipe(my_pipe) == -1)
        {
            perror("cannot create pipe\n");
            exit(EXIT_FAILURE);
        }
    
        pid_t my_pid = fork();
    
        if (my_pid < 0)
        {
            perror("Failed fork\n");
            exit(EXIT_FAILURE);
        }
    
        if (my_pid > 0)
        {
            close(my_pipe[1]);
            dup2(my_pipe[0], 0);
            close(my_pipe[0]);
    
            execvp(commands2[0], commands2);
            perror(commands2[0]);
            exit(EXIT_FAILURE);
        }
        else
        {
            close(my_pipe[0]);
            dup2(my_pipe[1], 1);
            close(my_pipe[1]);
    
            execvp(commands[0], commands);
            perror(commands[0]);
            exit(EXIT_FAILURE);
        }
        return EXIT_FAILURE;
    }
    

    When I run the program, it produces the appropriate output. Note that the return at the end of main() is actually never reached.