Does bidirectional popen () work on Mac OS X in C?

My question is Where can I find a working C code example that sets up and uses a bi-directional pipe with popen () on MacOS?

I am trying to use popen () to open a bi-directional channel between my C program and an external program on Mac OS X.

My C program has several times:

  • read input line from native stdin
  • reformat this as input for an external program
  • calling an external program, passing formatted input to an external program stdin
  • read the results from the side of the standard program
  • process these results and output some diagnostic data to its own stdout

I can do this easily if I use an actual file to store the I / O and then use system () or similar, but this is too slow since I have billions of inputs. So I would like to do this entirely in memory.

The related man page and internet discussion in this group and others show that I can open a pipe in an external program and then write to it with fprintf in the same way as writing to a file.

pout = popen(external_program, "w")
fprintf(pout,input_to_external_program);

      

This works great as a unidirectional pipe feeding INTO formatted inputs to an external program.

Apparently on Mac OS it is possible to open a bidirectional pipe by specifying "r +" instead of "r" or "w" as the appropriate argument to popen and thereby read and write to the program using the same pipe.

pout = popen(external_program, "r+")
fprintf(pout,"%s", input_to_external_program);
fscanf(fpout,"%s", &output_from_external_program);

      

I don't need to alternate between reading, writing, reading, writing, etc., since all that needs to happen is that my program will finish writing input to the external program before it starts reading the output from the external program.

An external program should simply read one self-contained input sequence (ie, end with a special line indicating "end of input") and then run until completion, writing output as it finishes, and then ending.

But when I try to use this function, it just doesn't work because something is blocking - an external program runs, but doesn't even complete one input.

Even when I just forget about reading, but just replace "w" with "r +", then the functionality that previously worked stops working.

I've searched most of this morning for a working popen () example that I can modify, but everything I've found uses a unidirectional channel only.

So we're done with

My question . Where can I find a working C code example that sets up and uses a bi-directional pipe with popen () on MacOS? **

If it makes it easier, I can replace the C part with Perl.

UPDATE

Thanks for the answers, but I still can't get it to work properly, although I can make some small-scale versions. For example, I wrote a little program that would simply add it to whatever numbers were fed to it, and would exit if the input code was -99 / I called this "copy".

#include <stdio.h>
#include <unistd.h>

int 
main(int argc, char **argv) {

  int k;

  setbuf(stdout,NULL);

  while (scanf("%d",&k) == 1) {
    if (k == -99) break;
    printf("%d\n",k+1);
  }

}

      

Then I wrote a parent program that will create a pipe, write some stuff, and then wait for the exit (without doing NOHANG, because I want the parent to keep sucking the output of the child until the child is finished).

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>

int main(void) {

    FILE *p;
    int  k;

    p = popen("copy", "r+");
    if (!p) {
        puts("Can't connect to copy");
        return 1;
    }

    fprintf(p,"%d\n",100);
    fprintf(p,"%d\n",200);
    fprintf(p,"%d\n",300);
    fprintf(p,"%d\n",-99);

    fflush(p);

    while (fscanf(p,"%d",&k) == 1)  {
      printf("%d\n", k);
    }

    pclose(p);

    return 0;
 }

      

This works great, but as soon as I replace the dummy "copy" program with my actual subprocess, it just hangs.

Everything should be the same - the parent process creates an entire input sequence for the child that reads it, does some work, prints an arbitrary number of lines of output, and then exits.

But something else - either the child process doesn't receive all of its input, or it buffers its output, or something else. Mysterious.

+3


source to share


2 answers


Here's a program that interacts with bc

to perform integer calculations. In the simplest case, all you have to do is use fgets()

both fputs()

to read and write to / from the pipe.

However, if you enter something that does not output, such as setting a variable (for example x=1

) or runtime errors (for example 1/0

), then it fgets()

will continue to wait indefinitely for input. To avoid this situation, you need to make the pipe non-blocking and retry it to exit the pipe process.



#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>

int main(void) {
    FILE *p;
    char buf[1025];
    int i, fd, delay;

    p = popen("bc", "r+");
    if (!p) {
        puts("Can't connect to bc");
        return 1;
    }
    /* Make the pipe non-blocking so we don't freeze if bc produces no output */
    fd = fileno(p);
    fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK);

    puts("Give me a sum to do (e.g., 2+2):");
    if (fgets(buf, 1024, stdin)) {
        fputs(buf, p);
        fflush(p);
        for (delay=1,i=0; i<20; delay*=2,i++) {
            usleep(delay);
            if (fgets(buf, 1024, p)) {
                printf("The answer is %s", buf);
                break;
            }
        }
    }
    pclose(p);

    return 0;
}

      

Also, don't forget that pipes tend to be buffered. This is not a problem because it fgets()

includes a newline at the end of the input.
Bugfix: channels are block buffered, so call fflush()

after sending data to the channel.

+3


source


Make sure you are fflush(pout)

in the parent process and that the child process executes fflush(stdout)

after each exit.



+1


source







All Articles