-1

On Ubuntu 16 I am trying to write a program exercising pipes, forking, and execing:

  • the program will accept a file name via a command-line argument;
  • a child process will open the named file and exec cat to transfer the content to a second child process; and
  • the second child will exec grep to select the lines that contain numbers for forwarding to a third child process
  • the third child process prints the received lines.

Here's my code:

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

#define BLOCK_SIZE 4096

int main(int argc, char** argv)
{
    int PID;
    int pipe1[2];
    int pipe2[2];
    int pipe3[2];
    char fileName[256];
    int lengthfileName = strlen(argv[1]);
    char content[BLOCK_SIZE];
    char modifiedContent[BLOCK_SIZE];
    int file;
    if(argc < 2)
    {
        printf("Usage prog file\n");
        exit(1);
    }
    if(pipe(pipe1) < 0)
    {
        printf("Error at pipe\n");
        exit(1);
    }
    if(pipe(pipe2) < 0)
    {
        printf("Error at pipe\n");
        exit(1);
    }
    if(pipe(pipe3) < 0)
    {
        printf("Error at pipe\n");
        exit(1);
    }
    if((PID = fork()) < 0)
    {
        printf("Error at process\n");
        exit(1);
    }


    if(PID == 0) //first child
    {
        close(pipe1[1]);
        read(pipe1[0],fileName,lengthfileName); 
        close(pipe1[0]);

        close(pipe2[0]); 
        dup2(pipe2[1],1);
        close(pipe2[1]); 
        execlp("/bin/cat","cat",fileName,NULL);


        exit(0);
    }
    else // parent
    {
        close(pipe1[0]); 
        write(pipe1[1],argv[1],lengthfileName);
        close(pipe1[1]);

        int status;

        if((PID = fork()) < 0)
        {
        printf("Error at process\n");
        exit(1);
        }
        if(PID == 0) // child 2
        {
            close(pipe2[1]);
            //read(pipe2[0],content,BLOCK_SIZE);
            //dup2(pipe2[0],0);// ***********************MARKED LINE HERE *****************************************
            close(pipe2[0]);    

            close(pipe3[0]);
            dup2(pipe3[1],1);
            close(pipe3[1]);    

            execlp("grep","grep","[0-9]",NULL);

            exit(0);

        }

        if((PID = fork()) < 0)
        {
            printf("Error at process\n");
            exit(1);
        }
        if(PID == 0) //cod fiu 2
        {
            close(pipe3[1]);
            read(pipe3[0],modifiedContent,BLOCK_SIZE);
            close(pipe3[0]);
            printf("GOT FROM PIPE:%s",modifiedContent);
            exit(0);
        }
        waitpid(PID, &status, 0);
    }

    return 0;
}

My problem is inside the child process 2 code, where I try to use the pipeline as input for grep. As presented the input is taken from the terminal; if I uncomment the marked lines then the program hangs, and I have to manually kill it to make it stop.

What's wrong with how I'm using pipe2 to feed data to grep in child process 2? Or is the problem somewhere else?

John Bollinger
  • 160,171
  • 8
  • 81
  • 157
R. Barbus
  • 73
  • 1
  • 7
  • One major problem in the first child is that `numeFisier` doesn't contain a *null terminated* string. – Some programmer dude Nov 30 '16 at 14:08
  • It would be helpful if the code is in english :) @Someprogrammerdude Joachim you are awesome waiting for your answer to this – Vinay Shukla Nov 30 '16 at 14:27
  • @Someprogrammerdude i don't understand why that is a problem, I use it only in 2 functions in child one and the file opening is ok, I mean child one send perfectly the content of the file to child 2 – R. Barbus Nov 30 '16 at 14:47
  • @VinayShukla Sorry for that, i translated the code :) – R. Barbus Nov 30 '16 at 14:48
  • If a string is not terminated, you will have *undefined behavior*. That it *seems* to work is just a fluke. It might stop working when you least expect it. – Some programmer dude Nov 30 '16 at 18:12

1 Answers1

0

It's a bit silly that you transfer the file name to the first child via a pipe, but rely on that child inheriting its length from its parent. If you're going to inherit the name's length, then you might as well inherit the whole file name, dispensing with the first pipe.

You could conceivably send the (fixed-size) length value over the pipe first to avoid inheriting it, but such a scheme is pointless -- not only do forked child processes inherit data from their parents, you cannot avoid relying on that in your program. In particular, the children must inherit the open pipe ends and the arrays of pipe file descriptors from the parent for the single-parent approach to work at all.

Note also that you are (maybe) lucking into null termination of the file name received over the pipe. The first child neither reads it from the pipe nor sets it explicitly.

But the main problem appears to be that you have stray open pipe ends. You create all three pipes in the parent, before forking any children. At each fork, the child will therefore inherit the open file descriptors for all pipe ends that the parent has not yet closed. The child processes should close all of the open pipe ends they do not use, but they only close some of them. Programs such as grep (and cat) don't exit until they see the end of the file, and they won't see that on a pipe while any process holds the write end open.

Specifically, the parent process never closes the write end of pipe2, and in fact the third child inherits that open descriptor and also does not close it. The first child closes its copy of that FD when it exits, but with two other handles on the pipe end open, that end remains open. Therefore, when the second child is taking its input from that pipe, it never sees end-of-file, and never exits. Making the parent close both ends of pipe2 between forking the second child and forking the third child should solve that problem.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157