Search code examples
cfork

What is the output that the program trying to achieve


I know about the fork(), dup2 calls but I cannot infer the output of the program.

#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <unistd.h>

int main (int argc, char *argv[]){
    pid_t pid1, pid2;
    int fds[2];

    char *argv1[] = { "ls", "-l","usr/bin", NULL};
    char *argv2[] = {"more", NULL};

    pipe(fds);

    pid1=fork();
    if(!pid1){
        close(fds[0]);
        dup2(fds[1], STDOUT_FILENO);
        close(fds[1]);
        execvp(argv1[0],argv1);
    }

    pid2=fork();
    if(!pid2){
        close(fds[1]);
        dup2(fds[0], STDOUT_FILENO);
        close(fds[0]);
        execvp(argv1[0],argv2);
    }

    close(fds[0]);
    close(fds[1]);
    waitpid(pid2,NULL,0);

    return EXIT_SUCCESS;

}

Also what would happen if i experiment and mistakenly press pid1 instead of pid2 at the waitpid call in the end.


Solution

  • Nominally, the program is running the shell pipeline

    ls -l usr/bin | more
    

    In practice, the connections to more are mishandled (using copy-and-paste is dangerous if you don't make all the necessary changes). It connects the read end of the pipe to the standard output of more, which is simply broken. It also runs ls a second time (passing argv1[0] to execvp() instead of argv2[0]) but tells ls that its name is more. It also doesn't believe execvp() can fail — but it can.

    With those minimal fixes in place (the program doesn't include <stdio.h> so there is no error reporting), you get something like:

    #include <stdlib.h>
    #include <sys/wait.h>
    #include <unistd.h>
    
    int main(void)
    {
        pid_t pid1, pid2;
        int fds[2];
    
        char *argv1[] = { "ls", "-l", "usr/bin", NULL };
        char *argv2[] = { "more", NULL };
    
        pipe(fds);
    
        pid1 = fork();
        if (pid1 == 0)
        {
            close(fds[0]);
            dup2(fds[1], STDOUT_FILENO);
            close(fds[1]);
            execvp(argv1[0], argv1);
            exit(EXIT_FAILURE);
        }
    
        pid2 = fork();
        if (pid2 == 0)
        {
            close(fds[1]);
            dup2(fds[0], STDIN_FILENO);
            close(fds[0]);
            execvp(argv2[0], argv2);
            exit(EXIT_FAILURE);
        }
    
        close(fds[0]);
        close(fds[1]);
        waitpid(pid2, NULL, 0);
    
        return EXIT_SUCCESS;
    }
    

    I called the program pipe31 (created from pipe31.c) and got the sample output:

    $ pipe31
    ls: usr/bin: No such file or directory
    $ mkdir -p usr/bin
    $ random -n 15 1000 9999 > usr/bin/polyglot
    $ pipe31
    total 8
    -rw-r--r--  1 jonathanleffler  staff  75 Dec  8 22:07 polyglot
    $
    

    When invoked as shown, the local random program creates 15 random numbers between 1000 and 9999, each on its own line — corresponding to 75 bytes in the file. The outputs were piped via more, but it is hard to spot that in a web browser.

    As to your proposed experimentation — experiment away — you won't cause harm. What you see will depend on the size of the directory you're working in, and the size of your terminal window. However, if the output from ls -l is big enough (but not too big), then you'd get a shell prompt in the middle of your ls listing, and you'd have more showing data and waiting to read a newline. There could potentially be a competition between the shell and more for the following inputs which could get interesting. That assumes you fix the problems identified in the rest of the answer before experimenting.