Search code examples
cpopen

C: popen reading from stdout blocks


The manpage for popen says "reading from a "popened" stream reads the command's standard output".

However, I can't seem to get the subprocess output in the trivial program below. The "reader" parent process blocks on the read (whether using fgets or fread)

What am I missing?

Attaching to the pinger program with gdb shows it is looping and calling printf to output text. Just nothing detected by fgets on the parent's side...

PINGER.C

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

int
main(int argc, char **argv)
{
    int i = 0;
    while (1)
    {
        printf("stdout %d\n", i++);
        sleep(1);
    }
}

POPENTEST.C

#include <stdio.h>
#include <errno.h>
#include <stdlib.h>


int
main(int argc, char **argv)
{
    char *cmd = "./pinger";
    printf("Running '%s'\n", cmd);

    FILE *fp = popen(cmd, "r");
    if (!fp)
    {
        perror("popen failed:");
        exit(1);
    }

    printf("fp open\n");

    char inLine[1024];
    while (fgets(inLine, sizeof(inLine), fp) != NULL)
    {
        printf("Received: '%s'\n", inLine);
    }

    printf("feof=%d ferror=%d: %s\n", feof(fp), ferror(fp), strerror(errno));
    pclose(fp);
}

OUTPUT

$ ./popenTest
fp open

Solution

  • By default, C buffers writes to the stdout when stdout is not connected to a tty. This means that from the OS' perspective, the program has not written anything to stdout until either the buffer is full or you manually flushed the output:

    #include <string.h>
    #include <stdio.h>
    #include <unistd.h> 
    
    int
    main(int argc, char **argv)
    {
        int i = 0;
        while (1)
        {
            printf("stdout %d\n", i++);
            fflush(stdout);
            sleep(1);
        }
    }
    

    When connected to a tty, the stdout is automatically flushed on every newline. But this automatic flushing does not happen when the stdout is connected to a pipe.