20

I'd like to open a pipe using popen() and have non-blocking 'read' access to it.

How can I achieve this?

(The examples I found were all blocking/synchronous)

Matt Joiner
  • 112,946
  • 110
  • 377
  • 526
jldupont
  • 93,734
  • 56
  • 203
  • 318

3 Answers3

33

Setup like this:

FILE *f = popen("./output", "r");
int d = fileno(f);
fcntl(d, F_SETFL, O_NONBLOCK);

Now you can read:

ssize_t r = read(d, buf, count);
if (r == -1 && errno == EAGAIN)
    no data yet
else if (r > 0)
    received data
else
    pipe closed

When you're done, cleanup:

pclose(f);
Matt Joiner
  • 112,946
  • 110
  • 377
  • 526
  • 1
    the pipe, being a FILE pointer, is inherently buffered, is there any assurance that by using the file descriptor directly you're not going to miss something that was pulled into the file buffer, or can this be guaranteed as long as you don't call fget/fread/etc first? – stu Jun 03 '16 at 17:10
7

popen() internally calls pipe(), fork(), dup2() (to point the child process's fds 0/1/2 to the pipes) and execve(). Have you considered using these instead? In that case, you can set the pipe you read to non-blocking using fcntl().

update: Here's an example, just for illustrative purposes:

int read_pipe_for_command(const char **argv)
{
   int p[2];

   /* Create the pipe. */
   if (pipe(p))
   {
      return -1;
   }

   /* Set non-blocking on the readable end. */
   if (fcntl(p[0], F_SETFL, O_NONBLOCK))
   {
      close(p[0]);
      close(p[1]);
      return -1;
   }

   /* Create child process. */
   switch (fork())
   {
      case -1:
          close(p[0]);
          close(p[1]);
          return -1;
      case 0:
          /* We're the child process, close read end of pipe */
          close(p[0]);
          /* Make stdout into writable end */
          dup2(p[1], 1);
          /* Run program */
          execvp(*argv, argv);
          /* If we got this far there was an error... */
          perror(*argv);
          exit(-1);
      default:
          /* We're the parent process, close write end of pipe */
          close(p[1]);
          return p[0];
   }
}
Luca Ceresoli
  • 1,591
  • 8
  • 19
asveikau
  • 39,039
  • 2
  • 53
  • 68
2

Never tried it but I don't see why you couldn't grab the file descriptors with fileno(), use fcntl() to set to non-blocking, and use read()/write(). Worth a try.

Duck
  • 26,924
  • 5
  • 64
  • 92