MrRhabdos
MrRhabdos

Reputation: 23

Executing the ls | wc linux command in c program using 2 processes communicating trough a pipe

I'm currently having problems with the following exercise:

I want to "mimic" the pipe command line ls | wc in linux bash with the following program. What I do is:

When I do ls | wc in linux terminal I get the following result:

8      8     101

But if I execute my program I get the following result:

0      0      0

Here is my program:

#include <stdlib.h> 
#include <errno.h> 
#include <stdio.h>
#include <unistd.h> 
#include <sys/types.h> 
#include <sys/stat.h> 
#include <fcntl.h> 
#include <string.h> 
#include <sys/wait.h>
#include <libgen.h>
#include <signal.h>
#include <errno.h>

int main(void){
        int mypipe[2];
        pid_t pid1, pid2;

        if (pipe(mypipe)<0)
                perror ("pipe error"), exit(1);
        if ((pid1=fork())<0)
                perror ("fork error"), exit(1);

        else if (pid1==0) {
                //reader child
                close (mypipe[1]);
                if (dup2(mypipe[0], STDIN_FILENO)!=STDIN_FILENO)
                        perror ("dup2 error"), exit(1);
                close (mypipe[0]); 
                if (execlp("wc", "wc", NULL)<0)
                        perror("execlp1 error"), exit(1);
                else {  //pid >0, parent
                        if ((pid2=fork())<0)
                                perror ("fork error"), exit(2);
                        else if (pid2==0) {     
                                //writer child
                                close(mypipe[0]);
                                if (dup2(mypipe[1], STDOUT_FILENO) != STDOUT_FILENO)
                                        perror("dup2 error"), exit(1);
                                close (mypipe[1]);
                                if (execlp("ls", "ls", NULL)<0)
                                        perror ("execlp error"), exit(1);
                        }
                        else {  //parent
                                close(mypipe[0]);
                                close(mypipe[1]);

                                waitpid(pid1, NULL, 0);
                                waitpid(pid2, NULL, 0);
                                exit(0);
                        }
                }
        }
return 0;
}

What am I doing wrong? Thanks in advance for the answers!

Upvotes: 2

Views: 1197

Answers (1)

Jonathan Leffler
Jonathan Leffler

Reputation: 754530

Your code is confused. You have many irrelevant headers, and repeat #include <errno.h>. In the main() function, you have:

int main(void)
{
    int mypipe[2];
    pid_t pid1, pid2;

    if (pipe(mypipe) < 0)
        perror("pipe error"), exit(1);
    if ((pid1 = fork()) < 0)
        perror("fork error"), exit(1);
    else if (pid1 == 0)
    {   
        // reader child
        close(mypipe[1]);
        if (dup2(mypipe[0], STDIN_FILENO) != STDIN_FILENO)
            perror("dup2 error"), exit(1);
        close(mypipe[0]);
        if (execlp("wc", "wc", NULL) < 0)
            perror("execlp1 error"), exit(1);
        else            // pid >0, parent
        {   
            …
        }
    }
    return 0;
}

The else if (pid1 == 0) clause is executed by the child. It closes the write end of the pipe, duplicates the read end to standard input and closes the read end of the pipe. It then does execlp() on wc. Only if the code fails to execute wc will the else clause be executed, and then there is only the read end of the pipe left open. Meanwhile, the original process simply exits. That closes the pipe, so the wc command gets no input, and reports 0 0 0 as a result.

You need to rewrite the code. The parent process should wait until both its children execute. Especially while debugging, you should not ignore the exit status of children, and you should report it.

Here's some code that works. Note that it avoids bushy decision trees — it is a linear sequence of if / else if / … / else code. This is easier to understand, in general, than a bushy, multi-level set of conditions.

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>

int main(void)
{
    int fd[2];
    pid_t pid1, pid2;

    if (pipe(fd) < 0)
        perror("pipe error"), exit(1);
    else if ((pid1 = fork()) < 0)
        perror("fork error"), exit(1);
    else if (pid1 == 0)
    {
        /* ls */
        dup2(fd[1], STDOUT_FILENO);
        close(fd[0]);
        close(fd[1]);
        execlp("ls", "ls", (char *)0);
        perror("exec ls");
        exit(1);
    }
    else if ((pid2 = fork()) < 0)
        perror("fork error"), exit(1);
    else if (pid2 == 0)
    {
        /* wc */
        dup2(fd[0], STDIN_FILENO);
        close(fd[0]);
        close(fd[1]);
        execlp("wc", "wc", (char *)0);
        perror("exec wc");
        exit(1);
    }
    else
    {
        close(fd[0]);
        close(fd[1]);
        int status1;
        int status2;
        int corpse1 = waitpid(pid1, &status1, 0);
        int corpse2 = waitpid(pid2, &status2, 0);
        printf("ls: pid = %d, corpse = %d, exit status = 0x%.4X\n", pid1, corpse1, status1);
        printf("ls: pid = %d, corpse = %d, exit status = 0x%.4X\n", pid2, corpse2, status2);
    }
    return 0;
}

A sample run of the program pipe41 on my machine produced:

$ pipe41
     175     175    1954
ls: pid = 44770, corpse = 44770, exit status = 0x0000
ls: pid = 44771, corpse = 44771, exit status = 0x0000
$ ls | wc
     175     175    1954
$

Upvotes: 2

Related Questions