tags:

views:

794

answers:

4

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)

A: 

Have you looked at the "see also" section of the man page for popen()?

Quick google search revealed this page: http://beej.us/guide/bgnet/output/html/singlepage/bgnet.html#blocking Talks about blocking and non-blocking access to file descriptors.

just_wes
+3  A: 

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
I'll do that! Thanks!
jldupont
I can confirm that this procedure works.
jldupont
+1  A: 

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 parent process, close the writable part of the pipe */
          close(p[1]);
          return p[0];
      default:
          /* Close readable 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);
   }
}
asveikau
+6  A: 

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
Works beautifully... thanks!
jldupont