#1
  1. No Profile Picture
    Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Mar 2005
    Posts
    415
    Rep Power
    20

    Shell pipes implementation


    Hi,
    I am working on a simple shell with support for pipes. Currently I am trying to get it to work with just one pipe, however I am not really sure on how this is done. What I tried is:
    - I take some user input with one pipe in it ( for example: cat test.txt | sort ).
    - I split it in a left part and a right part.
    - I create a pipe using: pipe(fd)
    - I fork the main process.
    - Set the new child process's standard output to fd[1]
    - Let the new child process execute the left side of the command
    - After the child process has finished, i fork the main process again.
    - Set the new child process's standard input to fd[0]
    - Let the new child process execute the left side of the command.

    The code below is what I am using. It seems like there is something missing to let the second child use the input from the pipe for executing its commands, but I dont know how this is done.
    c Code:
    int handlePipedRequest( char* request){
    	int fd[2];
    	int status;
    	pid_t pid;
    	char* requestLeftFromPipe;
    	char* requestRightFromPipe;
     
    	if( pipe( fd ) < 0 ){
    		perror("Failed to setup pipe.");
    		exit(1);
    	}
     
    	requestLeftFromPipe = strtok( request, "|" );
    	requestRightFromPipe = strtok( NULL, "|" );
     
    	// Fork and let it execute everything on the left side of the pipe
    	pid = fork();
    	if( pid ==  0 ){
    		//close( fd[0] );
    		dup2( fd[1], 1 );
    		executeRequest( requestLeftFromPipe );
    	} else if( pid == -1 ){
    		perror("Failed to fork!\n");
    		exit(1);
    	} else{
    		waitpid( pid, &status, 0 );
    	}
     
    	// fork and let it execute everything on the right side of the pipe
    	pid = fork();
    	if( pid ==  0 ){
    		//close( fd[1] );
    		dup2( fd[0], 0 );
    		executeRequest( requestRightFromPipe );
    	} else if( pid == -1 ){
    		perror("Failed to fork!\n");
    		exit(1);
    	} else{
    		waitpid( pid, &status, 0 );
    		close( fd[0] );
    		close( fd[1] );
    	}
    	return 0;
    }
     
     
    // The executeRequest function just takes a program name with parameters and executes it.
    int executeRequest( char* request ){
    	char* parameters[MAX_NUMBER_OF_PARAMETERS];
    	extractParameters( parameters, request, MAX_NUMBER_OF_PARAMETERS );
    	if ( execvp( parameters[0], parameters ) == -1 ){
    		perror("Cannot execute the requested program.");
    		exit(1);
    	}
    	return 0;
    }

    Can anyone give me a hint on this?
    Thanks in advance.
  2. #2
  3. No Profile Picture
    Contributing User
    Devshed Intermediate (1500 - 1999 posts)

    Join Date
    Feb 2004
    Location
    San Francisco Bay
    Posts
    1,939
    Rep Power
    1313
    Originally Posted by Stefan1
    - I split it in a left part and a right part.
    - I create a pipe using: pipe(fd)
    - I fork the main process.
    - Set the new child process's standard output to fd[1]
    - Let the new child process execute the left side of the command
    - After the child process has finished, i fork the main process again.
    - Set the new child process's standard input to fd[0]
    - Let the new child process execute the left side of the command.
    This is not correct. The constituents of a pipe execute simultaneously. I replied to a similar post in the past: http://forums.devshed.com/showpost.p...40&postcount=5
  4. #3
  5. No Profile Picture
    Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Mar 2005
    Posts
    415
    Rep Power
    20
    So you mean I should create a pipe between the parent and child1 and a pipe between the parent and child2. Then I attach the output part of child1's pipe to the input of child2's pipe?

    What I don't understand is that a command like:
    cat help.txt | sort
    needs the output of 'cat help.txt' before it can start with 'sort'. So how can i run them concurrently, what if child2 tries to read the input before child1 has executed its command and sees there is no input, so it executes its command without input?

    And is it correct that if I change the stdin pipe of child2 that it will use that is input to execute its commands? So in my example it would get the information that needs to be sorted from the stdin right?

    Thanks for the reply!

    Grtz Stefan
  6. #4
  7. No Profile Picture
    Contributing User
    Devshed Intermediate (1500 - 1999 posts)

    Join Date
    Feb 2004
    Location
    San Francisco Bay
    Posts
    1,939
    Rep Power
    1313
    Originally Posted by Stefan1
    So you mean I should create a pipe between the parent and child1 and a pipe between the parent and child2. Then I attach the output part of child1's pipe to the input of child2's pipe?
    Still incorrect. (How do you propose to "attach" the two parts of the two pipes?)

    Parent:
    1. Parent creates one pipe my_pipe[2].
    2. Parent forks child 1.
    3. Parent forks child 2.
    4. Parent closes both sides of my_pipe.
    5. Parent waits for children to exit.


    Child 1:
    1. Child 1 closes my_pipe[0] (the read end).
    2. Child 1 does dup2(my_pipe[1], 1) (1 being STDOUT_FILENO).
    3. Child 1 executes "cat help.txt" using execvp or something similar.


    Child 2:
    (You can figure this out. It has to close my_pipe[1], make its stdin a copy of my_pipe[0], and execute its process.)
    Originally Posted by Stefan1
    What I don't understand is that a command like:
    cat help.txt | sort
    needs the output of 'cat help.txt' before it can start with 'sort'. So how can i run them concurrently, what if child2 tries to read the input before child1 has executed its command and sees there is no input, so it executes its command without input?
    Okay, you're really confused. :) Have you ever written a program that accepted typed input from a user? That program spent 99.999% of its time doing nothing but waiting for you to type something and press enter. What happens if your program has scanf("%d", &k) and input is from the terminal? scanf blocks until the input is available. Almost all input works this way, whether it's from a terminal, a file, or a pipe, and the 'sort' program is no exception. The exceptions either cannot have their stdin piped or detect when their input isn't from a terminal and act accordingly (if engineered properly).
    Originally Posted by Stefan1
    And is it correct that if I change the stdin pipe of child2 that it will use that is input to execute its commands? So in my example it would get the information that needs to be sorted from the stdin right?
    stdin is whatever stream file descriptor 0 happens to point to. It could be an open file, a terminal, or the read end of a pipe (among other things). If your program takes input from stdin (such as your use of the 'sort' command) and you want the input to be from a particular thing, you need to make file descriptor 0 point to that thing before you execute the command. The same thing goes for output to stdout, except in this case it's file descriptor 1 that controls what stdout is. I'm not sure that answers your question, but your question wasn't very clear.

    Comments on this post

    • Stefan1 agrees : thanks
  8. #5
  9. No Profile Picture
    Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Mar 2005
    Posts
    415
    Rep Power
    20
    That explained alot!

    I got everything working now, although its not really pretty/flexible, I understand enough to play with for a while.

    Thanks for the help!

IMN logo majestic logo threadwatch logo seochat tools logo