Search code examples
clinuxforkpipeline

How do I process a n commands in pipelines in C?


So I have an assignment where I have to process n commands that are in pipe. These commands are Linux based. If my understanding is correct, I know I have to create a for loop that continuously forks() the child of the main process, and execute those children, which are then connected by pipes. Here's my code so far.

void  main(void)
{
    char **cmds[3];
    char *c1[] = { "ls", "-l", "/etc", 0 };
    char *c2[] = { "head", "-n", "10", 0 };
    char *c3[] = { "tail", "-n", "5", 0 };
    cmds[0] = (char **)c1;
    cmds[1] = (char **)c2;
    cmds[2] = (char **)c3; 
    
    int pid, status;
    pid = fork();
    
    if(pid == 0){//child proccess
        int fd[2];
        int infd;
        int i;
        for(i = 0; i < 2; i++){
            pipe(fd);
            int ppid;
            ppid = fork();
            
            if(ppid > 0){
                dup2(fd[1], 1);
                close(fd[0]);
                //executes the nth command
                execvp(*(cmds+i)[0], *(cmds+i));
            }else if(ppid == 0){
                dup2(fd[0], 0);
                close(fd[1]);
                //executes the n+1th command
                execvp(*(cmds+i+1)[0], *(cmds+i+1));
            }
        }
    }else if (pid > 0){//parents proccess
        while((pid = wait(&status)) != -1);
        
    }
}

As the program stands right now, I'm able to only pipe the first and second commands, but for some reason the 3rd command goes completely undetected. How would I fix this?


Solution

  • The following are the three processes you want to create:

    stdin  ->  ls    ->  pipe1
    pipe1  ->  head  ->  pipe2
    pipe2  ->  tail  ->  stdout
    

    You have at most one pipe created at time, but head needs to communicate with two.

    Base your loop around the creation of processes, not around the creation of pipes. That means you will need to carry pipe information from one pass to the other.

    pid_t pids[ num_cmds ];
    
    int next_stdin_fd = 0;
    
    for (int i = 0; i < num_cmds; ++i ) {
       int stdin_fd = next_stdin_fd;
       int stdout_fd;
    
       if ( i == num_programs - 1 ) {
          next_stdin_fd = -1;
          stdout_fd     = 1;
       } else {
          int pipe_fds[2];
          pipe( pipe_fds );
          next_stdin_fd = pipe_fds[ 0 ];
          stdout_fd     = pipe_fds[ 1 ];
       }
    
       pids[ i ] = fork();
       if ( pid == 0 ) {
          if ( stdin_fd != 0 ) {
             dup2( stdin_fd, 0 );
             close( stdin_fd );
          }
    
          if ( stdout_fd != 1 ) {
             dup2( stdout_fd, 1 );
             close( stdout_fd );
          }
    
          if ( next_stdin_fd != -1 ) {
             close( next_stdin_fd );
          }
    
          execvp( *cmds[i], cmds[i]+1 );
       }
    
       if ( stdout_fd != 1 ) {
          close( stdout_fd );
       }
    }
    
    for (int i = 0; i < num_cmds; ++i ) {
       int status;
       waitpid( pids[ i ], &status, 0 );
    }
    

    Error checking omitted for brevity.