0

I'm trying to create a simple program which simulates the "ls -l | tail -n 2" call in terminal. I'm using "fork" and "execvp" for that purpose. Well, here is the code:

int main(int argc, char *argv[])
{
    int pipefd[2];
    pid_t child1;
    pid_t child2;
    char* com1[] = {"ls", "-l",NULL};
    char* com2[] = {"tail", "-n","2",NULL};

    if (!(child1 = fork())) 
    { 
       close(STDOUT);
       dup(pipefd[1]); 
       close(pipefd[1]); 
       execvp (com1[0], com1);
       _exit(EXIT_SUCCESS);
    }
    else
    {
        close(pipefd[1]);
        if (!(child2 = fork())) 
        { 
            close(STDIN);
            dup(pipefd[0]); /* dupWR now holds the lowest fd available, meaning STDOUT's */
            perror("dup 2");
            close(pipefd[0]); /* reader will see EOF */
            execvp (com2[0], com2);
            _exit(EXIT_SUCCESS);
        }
        else
        {
            close(pipefd[0]);
            waitpid(child2,0,0);
        }
        waitpid(child1,0,0);
    }


    return 0;
}

I get these errors:

dup 2: Bad file descriptor
tail: cannot fstat `standard input': Bad file descriptor
tail: -: Bad file descriptor

It seems to me that there is a problem in synchronization. In fact, if I declare: com2[] = {"ls", "-l",NULL}; It works fine (I mean as in normal shell). Moreover, I found that the second "dup" in the second "fork" returns error. Why is that? I don't know where is the problem with this code. Please help!

Edit: I added this code (forgot to create pipes):

if (pipe(pipefd) == -1) {
    perror("pipe");
    exit(EXIT_FAILURE);
}

Thanks, Useless!

Aladin
  • 492
  • 1
  • 8
  • 21
  • 1
    I just want to point out that you're not "simulating" `ls -l | tail -n 2` at all, you're actually running it. You're just typing the command in a very wordy way. :-) – Lee Daniel Crocker May 19 '13 at 22:43
  • did you define STDIN and STDOUT somewhere? – LtWorf May 20 '13 at 14:25
  • Anyone interested in further reading on this topic should really checkout chapter 44 of http://man7.org/tlpi/. Specifically, "Using Pipes to Connect Filters" outlines the whole process and shows how the solution below isn't entirely correct. The solution below assumes that STDOUT is open, which isn't guaranteed. Also, it assumes that pipefd[1] != STDOUT_FILENO, which isn't guaranteed either. – Homer6 Oct 24 '13 at 22:01

1 Answers1

1
close(STDOUT);
dup(pipefd[1]); 
close(pipefd[1]); 

Since dup returns the new file descriptor, and you don't use the return value, you're discarding it.

Did you want to replace stdout instead, like so?

dup2(pipefd[1], STDOUT_FILENO);

If so, pipefd[] should really be initialized first. Did you mean to call pipe somewhere?

Useless
  • 64,155
  • 6
  • 88
  • 132
  • It doesn't matter that he discards the FD, since it's a known value of STDOUT or STDIN, right? But obviously the pipes need to be created first. And while dup2 is better than dup, it should still have worked the way it was (after actually calling pipe) – xaxxon May 20 '13 at 00:35
  • No, `dup` doesn't _know_ you want to replace stdout, does it? It returns a new file descriptor which might have value 1, but probably isn't required to. – Useless May 20 '13 at 09:53