Pithikos
Pithikos

Reputation: 20300

Read from two pipes indefinetely and then print on stdout in C

I have two pipes that both get different data at random points. What I want is to print the content from both pipes to stdout.

              __________________
   pipe1     |                  |
]==============>               -----------.
             |                  |          \
             |     process1     |           -----> stdout
   pipe2     |                  |          /
]==============>               -----------´
             |__________________|

My code looks about this in process1:

while (1) {
  read(pipe1[0], &buff, sizeof(char));  
  write(1, &buff, sizeof(char));
  read(pipe2[0], &buff2, sizeof(char)); 
  write(1, &buff2, sizeof(char));
}

However that doesn't work as one read() can be blocked(if no data is coming) while data comes from the other pipe.

How can I print simultaneously from both pipes without being blocked in one pipe? Or any other suggestion on how to solve this is welcome.

Upvotes: 1

Views: 591

Answers (2)

outis
outis

Reputation: 77400

Use select to wait on both sockets. When data is ready, it will tell you which pipes have data available.

void setnonblocking(int fd) {
    int opts;

    opts = fcntl(fd,F_GETFL);
    if (opts < 0) {
        perror("Couldn't get file descriptor flags");
        exit(EXIT_FAILURE);
    }
    opts = (opts | O_NONBLOCK);
    if (fcntl(fd,F_SETFL,opts) < 0) {
        perror("Couldn't set file descriptor to non-blocking");
        exit(EXIT_FAILURE);
    }
    return;
}

#ifndef BUFSIZE
#  define BUFSIZE 1024
#endif
void cat(fd_set* waiting, int fd) {
   static char buf[BUFSIZE];

   int readCnt;
   if (FD_ISSET(fd, waiting)) {
       while ((readCnt = read(fd, buf, BUFSIZE)) > 0) {
           write(stdout, buf, readCnt);
       }
       if (readCnt < 0) {
           perror("Error reading from pipe");
       }
   }
}

...
{
    fd_set pipes, readable;

    setnonblocking(pipes1[0]);
    setnonblocking(pipes2[0]);

    FD_ZERO(&pipes);
    FD_SET(pipe1[0],&pipes);
    FD_SET(pipe2[0],&pipes);

    int ready;
    while (1) {
        if ((ready = select(2, &pipes, NULL, NULL, NULL)) > 0) {
            cat(&pipes, pipe1[0]);
            cat(&pipes, pipe2[0]);
        } else {
            // no time limit, so there was an error
            perror("Error waiting for input");
            exit(EXIT_FAILURE);
        }
        FD_SET(pipe1[0],&pipes);
        FD_SET(pipe2[0],&pipes);
    }
}

Note the above runs forever unless there's an error. You will likely want your program to stop at some point.

Upvotes: 3

You need to multiplex the inputs. The relevant system call is poll or select (or ppollor pselect). The select tutorial is useful to read.

Upvotes: 2

Related Questions