Thread: Piping problem!

  1. #1
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654

    Piping problem!

    Long story short, I can't get piping to work under *nix. Not being the most experienced developer with *nix, I figured I'd ask you guys to help me out a little.

    I am trying a very simple thing. Basically I want to capture the output of the "printenv" command and print it to the console.
    The problem is that the parent process blocks waiting for input from the child which has long since terminated. I don't know how to make it work.

    This is the code I am using:
    Code:
    #include <unistd.h>
    #include <sys/types.h>
    #include <stdio.h>
    
    typedef struct pipe
    {
    	int read, write;
    } pipe_t;
    
    const int _stdin = 0;
    const int _stdout = 1;
    const int _stderr = 2;
    
    void create_pipe(pipe_t* pipe_)
    {
    	int tmp[2];
    	pipe(tmp);
    	pipe_->read = tmp[0];
    	pipe_->write = tmp[1];
    }
    
    void close_pipe(pipe_t* pipe)
    {
    	close(pipe->read);
    	close(pipe->write);
    }
    
    int main()
    {
    	pipe_t printenv, sort;
    
    	create_pipe(&printenv);
    	if (fork() == 0) // printenv
    	{
    		sleep(2);
    		puts("I'm the child program!");
    		dup2(printenv.write, _stdout);
    		close_pipe(&printenv);
    		execlp("printenv", NULL);
    	}
    	else
    	{
    		puts("I'm the parent process! Now reading from child output!");
    		char buf[2048];
    		ssize_t size = 0;
    		while (size = read(printenv.read, buf, sizeof(buf)))
    		{
    			printf("OK! Got output from child process! Size = %u\n", size);
    		}
    		puts("Parent process is now terminating!");
    	}
    	close_pipe(&printenv);
    
    	return 0;
    }
    The output I am getting is:
    OK! Got output from child process! Size = 2048
    OK! Got output from child process! Size = 144

    Then it hangs.
    Speaking of which, I don't know the type of ssize_t nor what type specifier would be the right for printf in that case. Hence why I've just put %u.
    I don't know if there are any constants in some header for the file descriptors for stdin, stdout and stderr, either. Everywhere I look, they just use hardcoded constants, which is stupid.

    Any information would be highly appreciated.
    (I might probably be doing one or more things wrong.)
    Thanks.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  2. #2
    Registered User
    Join Date
    Oct 2011
    Location
    Denmark
    Posts
    80
    I think you're problem comes from the fact that you're never exiting you're "while" loop.

    It's been a long time since I worked with pipes so I'm not sure, but probably when you close the pipe, the read function will return an error code, so you can try :

    Code:
      while( ( size = read(printenv.read, buf, sizeof(buff)) > 0 )
    Hope that helps!

  3. #3
    Registered User
    Join Date
    Oct 2006
    Posts
    3,445
    a ssize_t is the signed version of size_t, so basically it's a signed long.

    also, I see that you're calling dup2() until AFTER you print the string. is this your intention? because it seems like printing the string would just get sent to standard output instead of the pipe, unless you call dup2() before printing the string.
    Last edited by Elkvis; 11-15-2011 at 11:35 AM.

  4. #4
    Registered User
    Join Date
    Oct 2006
    Posts
    3,445
    I think you also have to close stdout before you can dup2() it

  5. #5
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    Quote Originally Posted by Tibo-88 View Post
    I think you're problem comes from the fact that you're never exiting you're "while" loop.

    It's been a long time since I worked with pipes so I'm not sure, but probably when you close the pipe, the read function will return an error code, so you can try :

    Code:
      while( ( size = read(printenv.read, buf, sizeof(buff)) > 0 )
    Hope that helps!
    Well, I've tried
    Code:
    		while ((size = read(printenv.read, buf, sizeof(buf))) > 0)
    		{
    			printf("OK! Got output from child process! Status = %d\n", (int)size);
    		}
    But the output is the same.
    read is blocking.

    Quote Originally Posted by Elkvis View Post
    also, I see that you're not calling dup2() until AFTER you print the string. is this your intention? because it seems like printing the string would just get sent to standard output instead of the pipe, unless you call dup2() before printing the string.
    What string? The path for the child is intentional (allowing me to trace the flow of program without a debugger).
    The parent process is obviously not supposed to print to the pipe.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  6. #6
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    Quote Originally Posted by Elkvis View Post
    I think you also have to close stdout before you can dup2() it
    Closing stdout before duping seems to have no effect.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  7. #7
    Registered User
    Join Date
    Oct 2006
    Posts
    3,445
    I think the answer here is to use poll() or select() to determine if there is any data available to read from the parent process. poll() tends to be a little easier to use, and its functionality is nearly identical to select(), so I would definitely recommend to use poll(), although your needs may dictate otherwise.

  8. #8
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    I need to pipe this to another program's stdin later, which will most likely use read, so that isn't really an option.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  9. #9
    Registered User
    Join Date
    Oct 2006
    Posts
    3,445
    have you considered using popen()?

  10. #10
    Registered User
    Join Date
    Oct 2011
    Location
    Denmark
    Posts
    80
    Here is a nice tuto on pipe, looks a lot like what you want to do I think : 6.2.2 Creating Pipes in C

    o
    ooops I didn't see all the reply above, please ignore!
    Last edited by Tibo-88; 11-15-2011 at 12:24 PM.

  11. #11
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    I was hoping for an easy, quick solution. Clearly there should just be some minor error since I have seen that this is possible.
    If nothing else, I'll have to ask tomorrow and get this sorted out.

    I wouldn't know how to pipe the return from popen to another program later.
    I know for sure that the stdout is indeed redirected to the pipe, but I just can't seem to properly read it.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  12. #12
    Registered User
    Join Date
    Nov 2010
    Location
    Long Beach, CA
    Posts
    5,909
    I'm assuming you're writing this program in part for educational purposes. If not, look into using popen/pclose.


    Quote Originally Posted by Elysia
    The output I am getting is:
    OK! Got output from child process! Size = 2048
    OK! Got output from child process! Size = 144


    Then it hangs.
    Quote Originally Posted by man 7 pipe
    If all file descriptors referring to the write end of a pipe have been closed, then an attempt to read(2) from the pipe will see end-of-file (read(2) will return 0).
    You need to call close(printenv.write) in the parent process so that when the child terminates, all descriptors to that file are closed and read can see the EOF.


    Quote Originally Posted by Elysia
    Speaking of which, I don't know the type of ssize_t nor what type specifier would be the right for printf in that case. Hence why I've just put %u.
    Quote Originally Posted by man 3 printf
    z A following integer conversion corresponds to a size_t or ssize_t argument. (Linux libc5 has Z with this meaning. Don’t use it.)
    I think you want "%zu".


    Quote Originally Posted by Elysia
    I don't know if there are any constants in some header for the file descriptors for stdin, stdout and stderr, either. Everywhere I look, they just use hardcoded constants, which is stupid
    Since you're on a *nix system, use the STDOUT_FILENO macro in unistd.h. In C, identifiers with a leading _ are reserved for the implementation.


    I also get the following error:
    Code:
    $ gcc -Wall -g -std=c99  pipe.c   -o pipe
    pipe.c: In function ‘main’:
    pipe.c:39: warning: null argument where non-null required (argument 2)
    You need two non-null params for execlp. The first is what is actually executed. The second will be argv[0] for the exec'ed process. Use execlp("printenv", "printenv", NULL);

  13. #13
    Registered User
    Join Date
    Oct 2006
    Posts
    3,445
    popen() returns a FILE* and using c-style i/o, you should be able to detect the EOF condition.

  14. #14
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    Quote Originally Posted by anduril462 View Post
    I'm assuming you're writing this program in part for educational purposes. If not, look into using popen/pclose.
    Your assumption is correct.

    You need to call close(printenv.write) in the parent process so that when the child terminates, all descriptors to that file are closed and read can see the EOF.
    Ah, that solved it. Thanks!
    I guess the problem is that the pipe is buffered and since parent holds a handle to the writing end of the pipe, the contents is never flushed, and hence read blocks?

    I think you want "%zu".

    Since you're on a *nix system, use the STDOUT_FILENO macro in unistd.h. In C, identifiers with a leading _ are reserved for the implementation.
    Thanks for that.
    Yeah, I know _ is reserved for implementation, but ah well. I couldn't figure out a better name.

    I also get the following error:
    Code:
    $ gcc -Wall -g -std=c99  pipe.c   -o pipe
    pipe.c: In function ‘main’:
    pipe.c:39: warning: null argument where non-null required (argument 2)
    You need two non-null params for execlp. The first is what is actually executed. The second will be argv[0] for the exec'ed process. Use execlp("printenv", "printenv", NULL);
    I don't get it. Weird.
    I typically compile with
    gcc -std=c99 -Wextra -Wall -pedantic main.c -o main.exe
    (Yeah, I know ".exe" is not needed, but I like it.)
    The compiler's version is 4.1.2.
    Meh. Advice taken and code updated.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  15. #15
    'Allo, 'Allo, Allo
    Join Date
    Apr 2008
    Posts
    639
    It's blocking because the parent still has the write end of the pipe open. You need to close that before the read loop.

    I think you also have to close stdout before you can dup2() it
    dup2 should be closing the second argument itself

    Code:
    const int _stdin = 0;
    const int _stdout = 1;
    const int _stderr = 2;
    There are standard posix defines for these, in the shape of STD(IN|OUT|ERR)_FILENO. Not that they're anything else

    EDIT: So heavily, heavily ninja'd

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Implementation of Shell -- Problem in multiple piping
    By ani_pwl in forum C Programming
    Replies: 4
    Last Post: 10-22-2011, 10:00 AM
  2. Problem with piping in my simple UNIX shell
    By Integral Birth in forum C Programming
    Replies: 0
    Last Post: 11-28-2010, 07:37 PM
  3. programming unix shell piping problem
    By Kyro in forum Linux Programming
    Replies: 2
    Last Post: 08-28-2003, 07:52 AM
  4. piping....
    By coolDUDE in forum C Programming
    Replies: 1
    Last Post: 11-18-2002, 05:42 AM