No output in the parent process without fflush(stdout)

185 views Asked by At

I'm trying to understand what is behind this behaviour in my parent process.

Basically, I create a child process and connect its stdout to my pipe. The parent process continuously reads from the pipe and does some stuff.

I noticed that when inserting the while loop in the parent the stdout seems to be lost, nothing appears on the terminal etc I thought that the output of stdout would somehow go to the pipe (maybe an issue with dup2) but that doesn't seem to be the issue. If I don't continuously fflush(stdout) in the parent process, whatever I'm trying to get to the terminal just won't show. Without a while loop in the parent it works fine, but I'm really not sure why it's happening or if the rest of my implementation is problematic somehow.

Nothing past the read system call seems to be going to the stdout in the parent process. Assuming the output of inotifywait in the pipe is small enough ( 30 > bytes ), what exactly is wrong with this program?

What I expect to happen is the stdout of inotifywait to go to the pipe, then for the parent to read the message, run strtok and print the file name (which only appears in stdout when I fflush)

Running the program with inotify installed and creating any file in the current directory of the program should be enough. Removing the while loop does print the created file's name (as expected).

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <errno.h>
int main(void) {
  char b[100];
  int pipefd;
  if (mkfifo("fifo", 0666) == -1) {
    if (errno != EEXIST) {
      perror("mkfifo");
      exit(EXIT_FAILURE);
    }
  }

  pid_t pid = fork();
  if (pid < 0) {
    perror("fork");
    exit(1);
  }

  if ((pipefd = open("fifo", O_RDWR)) < 0) {
    perror("open pipe");
    exit(EXIT_FAILURE);
  }

  if (pid == 0) {
    dup2(pipefd, 1);
    const char* dir = ".";
    const char* args[] = {"inotifywait", dir,  "-m",       "-e",
                          "create",      "-e", "moved_to", NULL};
    execvp("inotifywait", (char**)args);
    perror("inotifywait");
  } else {
    while (1) {
      fflush(stdout);  // the output only appears in stdout with this here
      if (read(pipefd, b, 30) < 0) {
        perror("problem @ read");
        exit(1);
      }
      char filename[30];
      printf("anything");
      sscanf(b, "./ CREATE %s", filename);
      printf("%s", filename);
    }
  }
}
1

There are 1 answers

0
Andreas Wenzel On

The streams used by the C standard library are designed in such a way that they are normally buffered (except for the standard error stream stderr).

The standard output stream is normally line buffered, unless the output device is not an interactive device, in which case it is normally fully buffered. Therefore, in your case, it is probably line buffered.

This means that the buffer will only be flushed

  1. when it is full,
  2. when an \n character is encountered,
  3. when the stream is closed (e.g. during normal program termination),
  4. when reading input from an unbuffered or line-buffered stream (in certain situations), or
  5. when you explicitly call fflush.

This explains why you are not seeing the output, because none of the above are happening in your infinite loop (when you don't call fflush). Although you are reading input, you are not doing this from a C standard library FILE * stream. Instead, you are bypassing the C runtime library (e.g. glibc) by using the read system call directly (i.e. you are using a file descriptor instead of a stream).

The simplest solution to your problem would probably be to replace the line

printf("%s", filename);

with:

printf("%s\n", filename);

If stdout is line-buffered (which should be the case if it is connected to a terminal), then the input should automatically be flushed after every line and an explicit call to fflush should no longer be necessary.