cforkpipestdiosus

Can I assume stdio streams point to the correct file descriptor after a dup2?


I have code where I want to encapsulate a particular piece of code into a forked child so that if the code causes issues, it will not affect the parent and the parent will be able to report.

I am not execing after the fork call. Just plain fork and then the code runs that would have normally run in the parent.

The code in the child outputs stuff to stdout and stderr. Due to issues with interleaving output and stdio buffering, I was now looking into TLPI for a better solution.

The idea I came up with was roughly:

Now, I presume there is one step missing between the dup2 and the setbuf inside the child. What is it?

setbuf takes a FILE* whereas of course dup2 acts on int.

freopen came to mind, but it requires a path. If I merely want to assign a new fileno to the streams, how can I do that?


Solution

  • The extern variables stdin, stdout and stderr are FILE * pointers. You can pass these to setbuf / setvbuf. However, note that the child has a separate address space so whilst it will inherit the state of the buffers at the time of the fork(), both can continue to use the buffers safely, and if you fflush STDOUT and STDERR, the output buffers will be empty anyway.

    This is the way I would do it in the child (untested, and please do add some error handling):

    void
    child ( ... )
    {
      const char *devnull = "/dev/null";
    
      /* turn off buffering */
    
      setvbuf (stdin, NULL, _IONBF);
      setvbuf (stdout, NULL, _IONBF);
      setvbuf (stderr, NULL, _IONBF);
    
      for (i = getdtablesize () - 1; i >= 0; i--)
        {
          if ((i != write_end_of_pipe1) && (i != write_end_of_pipe2))
            close (i);
        }
    
      i = open (devnull, O_RDWR);
      if (i == -1)
        {
          fprintf (stderr, "Unable to open /dev/null\n");
          return;
        }
    
      i = open (devnull, O_RDONLY);
      if (i != 0)
        {
          dup2 (i, 0);
          close (i);
        }
    
      dup2 (write_end_of_pipe1, 1);
      close (write_end_of_pipe1);
    
      dup2 (write_end_of_pipe2, 2);
      close (write_end_of_pipe2);
    
      return;
    }