Home > Enterprise >  how to read entirety of pipe
how to read entirety of pipe

Time:03-27

I am working on a program where the main program forks itself and the child process calls exec. I have set it up so that the parent process has 2 pipes StdOutPipe and StdInPipe, and the child process calls dup so that stdout writes to the StdOutPipe and stdin reads from StdInPipe. Then the parent process calls wait, after which i would like to read the entirety of the StdOutPipe into a buffer. I know you can do so by reading one character at a time, but is there a faster way to do so?

CodePudding user response:

For performance reasons, one typically reads a chunk at a time, not a character at a time.

  1. Loop,
    1. Attempt to enlarge the buffer so it can fit CHUNK_SIZE more bytes.
    2. If an error occurred,
      1. Fail.
    3. Attempt to read CHUNK_SIZE bytes from the pipe into the unused part of the buffer.
    4. If an error occurred,
      1. Fail.
    5. If EOF was reached,
      1. Break.
    6. Increased the total number of bytes read by the number of bytes read.

CodePudding user response:

A pipe is basically a byte stream which means:

  1. There's no concept of messages or message boundaries with pipes
  2. The process reading from a pipe can read blocks of data of any size, regardless of the size of blocks written by the writing process

A read from a pipe is usually blocked until atleast a byte is written to the pipe.

That said, here's how i would implement your issue.

  1. Create two pipes, stdinpipe and stdoutpipe
  2. Do a fork
  3. Parent process should close the write end of the pipes and sit in a loop, waiting until data is written to pipe
  4. Child process should close the read end of the pipes and duplicate STDOUT to stdoutpipe and STDIN to stdinpipe
  5. Child process can then do an exec.

Sample code:

#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#define STDPIPE_BUFFER_SIZE 4096
#define ARGV_SIZE 3

int main()
{

    // Stdoutpipe and stdint pipe
    int stdoutpipe[2], stdinpipe[2], stdin_char_count, stdout_char_count, stdout_read, stdin_read;
    pid_t pid;
    char stdinbuffer[STDPIPE_BUFFER_SIZE], stdoutbuffer[STDPIPE_BUFFER_SIZE];
    char *argv[ARGV_SIZE]; // arguments to exec

    if (pipe(stdinpipe) == -1 || pipe(stdoutpipe) == -1)
        exit(1); // error occurred

    // Fork and exec

    switch (pid = fork())
    {
    case -1:
        exit(1); // error
    case 0:

        // child close the read end of both pipes

        if (close(stdinpipe[0]) == -1 || close(stdoutpipe[0]) == -1)
            exit(1);

        // have the pipes as the new STDIN and STDOUT
        if (dup2(stdinpipe[1], STDIN_FILENO) == -1 || dup2(stdoutpipe[1], STDOUT_FILENO) == -1)
            exit(1);

        argv[0] = "/usr/bin/ssh"; // replace with your own program [ssh -V  in my case]
        argv[1] = "-V";
        argv[2] = NULL;

        execve(argv[0], argv, NULL);
        exit(1); // if we get here something horribly bad happened

    default:
        // parent process
        stdin_char_count = 0;
        stdout_char_count = 0;
        // parent close write end of both pipes
        if (close(stdinpipe[1]) == -1 || close(stdoutpipe[1]) == -1)
            exit(1);

        for (;;)
        {
            stdin_read = read(stdinpipe[0], stdinbuffer, STDPIPE_BUFFER_SIZE);
            stdout_read = read(stdinpipe[0], stdinbuffer, STDPIPE_BUFFER_SIZE);

            if (stdin_read == 0 && stdout_read == 0)
            {
                stdinbuffer[stdin_char_count] = '\0';
                stdoutbuffer[stdout_char_count] = '\0';
                break;
            }

            if (stdin_read == -1 && stdout_read == -1)
                exit(1); // we cant recover from this
                
            stdin_char_count  = stdin_read;
            stdout_char_count  = stdout_read;
        }
        printf("%s\n", stdoutbuffer);
        wait(NULL);
    }
}

source: https://man7.org/linux/man-pages/man2/pipe.2.html

  • Related