Pipes, dup2 and exec()

Aris Kantas picture Aris Kantas · Nov 24, 2015 · Viewed 54.2k times · Source

I have to write a shell that can run pipes. For example commands like ls -l | wc -l". I have successfully parsed the command given by the user as below:

"ls" = firstcmd

"-l" = frsarg

"wc" = scmd

"-l" = secarg

Now I have to use two forks since the commands are two and a pipe. The code block that I wrote to exec the command is the following:

pid_t pid;
int fd[2];

pipe(fd);
pid = fork();

if(pid==0)
{        
    dup2(fd[WRITE_END], STDOUT_FILENO);
    close(fd[READ_END]);
    execlp(firstcmd, firstcmd, frsarg, (char*) NULL);
}
else
{ 
    pid=fork();

    if(pid==0)
    {
        dup2(fd[READ_END], STDIN_FILENO);
        close(fd[WRITE_END]);
        execlp(scmd, scmd, secarg, (char*) NULL);
    }
}

So when I run my shell and I enter the command ls -l | wc -l (for example) the result from the execs doesn't show up but the shell keeps running normally.

The strange thing is that the result of the command shows only when I terminate my shell with "exit" or "^C".

What is wrong with that output? Why doesn't it show up right after I enter the command?

Answer

user1969104 picture user1969104 · Nov 24, 2015

You need to close all the pipe descriptors in both the parent process and the child process (after duplication in the child process). In your code the main issue is that, the wc process does not exit because there are still writers present (since the parent process has not closed the write end). Changes shown below. I have also added the waitpid in the parent process to wait for the wc process.

pid_t pid;
int fd[2];

pipe(fd);
pid = fork();

if(pid==0)
{
    dup2(fd[WRITE_END], STDOUT_FILENO);
    close(fd[READ_END]);
    close(fd[WRITE_END]);
    execlp(firstcmd, firstcmd, frsarg, (char*) NULL);
    fprintf(stderr, "Failed to execute '%s'\n", firstcmd);
    exit(1);
}
else
{ 
    pid=fork();

    if(pid==0)
    {
        dup2(fd[READ_END], STDIN_FILENO);
        close(fd[WRITE_END]);
        close(fd[READ_END]);
        execlp(scmd, scmd, secarg,(char*) NULL);
        fprintf(stderr, "Failed to execute '%s'\n", scmd);
        exit(1);
    }
    else
    {
        int status;
        close(fd[READ_END]);
        close(fd[WRITE_END]);
        waitpid(pid, &status, 0);
    }
}