Cerdicipe
Cerdicipe

Reputation: 347

execl() does not seem to read from stdin

I'm trying to reproduce this command in c language:

ls | wc > output.txt

So, to do that, I wrote the following program:

#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <stdio.h>
#include <errno.h>

int main()
{
    pid_t lsFork, wcFork;
    int tube[2];
    pipe(tube);

    lsFork = fork();
    if(lsFork == 0) // ls command
    {
        close(tube[0]);
        dup2(tube[1], STDOUT_FILENO);
        close(tube[1]);
        if(execl("/usr/bin/ls", "ls", NULL) == -1)
            perror("Cannot execute ls");
    }
    else
    {
        wcFork = fork();
        if(wcFork == 0)  // wc command
        {
            sleep(1);
            int file = open("output.txt", O_WRONLY | O_CREAT);
            if(file == -1)
                perror("Cannot open output.txt");

            close(tube[1]);
            dup2(tube[0], STDIN_FILENO);
            close(tube[0]);
            dup2(file, STDOUT_FILENO);
            close(file);

            /*char buffer[BUFSIZ];
            read(STDIN_FILENO, buffer, BUFSIZ);
            write(STDOUT_FILENO, buffer, BUFSIZ);*/

            if(execl("/usr/bin/wc", "wc", NULL) == -1)
                perror("Cannot execute wc");

            close(STDOUT_FILENO);
        }
        else // parent
        {
            int status;
            waitpid(lsFork, &status, 0);
            waitpid(wcFork, &status, 0);
        }
    }

    return EXIT_SUCCESS;
}

But, the program does not exit. According to htop, the wc command is blocking the program. To understand this behaviour, I wrote a piece of code (the lines commented before execl()) and I don't understand what this works and not execl(). Am I forgetting something when calling this function?

Upvotes: 2

Views: 2984

Answers (3)

Mayukh Sarkar
Mayukh Sarkar

Reputation: 2615

Don't complicate things when you can do it easily.. Try the simpler code below & see if you can understand anything or not.

int main(){
    int tube[2];
    int fID;
    pipe(tube);
    if (fork() == 0){
      // this is the child process
      close(tube[0]); // reading end of the pipe
      dup2(tube[1], 1); // stdout ---> pipe writing end
      execlp("ls", "ls", NULL);
    }else{
      if (fork() == 0){
        //umask(0022);
        fID = open("sample.txt", O_WRONLY | O_CREAT, 0644);
        close(tube[1]); // writing end of the pipe
        dup2(tube[0], 0);  // stdin ----> pipe reading end
        dup2(fID, 1);
        execlp("wc", "wc", NULL);
      }
    }
  return 0;
}

Note If the purpose of the code is to solely implement the above mentioned piping, then you don't need to implement any waiting mechanisms. The OS will auto-kill all the zombie child, if any. Moreover execlp("wc", "wc", NULL); will auto block the program to end. Hence it will not exit early

Upvotes: 1

The parent process still has the pipe open, so wc is waiting around in case the parent decides to write stuff (which wc would need to count).

Close both ends of the pipe in the parent too:

    else // parent
    {
        int status;
        close(tube[0]); // <---
        close(tube[1]); // <---
        waitpid(lsFork, &status, 0);
        waitpid(wcFork, &status, 0);
    }

Upvotes: 1

user5671122
user5671122

Reputation: 29

You'll need to close the write end of the pipe in the parent too.

Upvotes: 0

Related Questions