You need to be quite careful with the plumbing:
- Call pipe() twice, one for pipe-to-child, one for pipe-from-child, yielding 4 file descriptors.
- Call fork().
- In child:
- Call close() on standard input (file descriptor 0).
- Call dup() - or dup2() - to make read end of pipe-to-child into standard input.
- Call close() on read end of pipe-to-child.
- Call close() on write end of pipe-to-child.
- Call close() on standard output (file descriptor 1).
- Call dup() - or dup2() - to make write end of pipe-from-child into standard output
- Call close() on write end of pipe-from-child.
- Call close() on read end of pipe-from-child.
- Execute the required program.
- In parent:
- Call close on read end of pipe-to-child.
- Call close on write end of pipe-from-child.
- Loop sending data to child on write end of pipe-to child and reading data from child on read end of pipe-from-child
- When no more to send to child, close write end of pipe-to-child.
- When all data received, close read end of pipe-from-child.
Note how many closes there are, especially in the child. If you use dup2(), you don't have to close standard input and output explicitly; however, dup() works correctly if you do the explicit closes. Also note that neither dup() nor dup2() closes the file descriptor that is duplicated. If you omit closing the pipes, then neither program can detect EOF correctly; the fact that the current process can still write to a pipe means that there is no EOF on the pipe, and the program will hang indefinitely.
Note that this solution does not alter standard error for the child; it goes to the same place as standard error for the parent. Often, this is correct. If you have a specific requirement that error messages from the child are handled differently, then take appropriate action on the child's standard error too.