How to close stdout and stderr in C?

37,698

Solution 1

What have you tried? Doesn't fclose work?

Solution 2

If you want to prevent your application from writing to the console, then:

#include <stdio.h>

int main()
{    
    fprintf(stdout, "stdout: msg1\n");
    fprintf(stderr, "stderr: msg1\n");
    fclose(stdout);

    fprintf(stdout, "stdout: msg2\n");  // Please read the note setion below
    fprintf(stderr, "stderr: msg2\n");
    fclose(stderr);

    fprintf(stdout, "stdout: msg3\n");
    fprintf(stderr, "stderr: msg3\n");
}

Outputs:

stdout: msg1
stderr: msg1
stderr: msg2

Note: any attempt to use a FILE pointer after the file is closed is erroneous. I'm doing it in this case just to illustrate what closing these file descriptors might do to your application.

Solution 3

Warning: I am not experienced in C at all, but recently read a slide that answers this question directly by Jim Meyering, a RedHat employee and GNUlib maintainer: https://www.gnu.org/ghm/2011/paris/slides/jim-meyering-goodbye-world.pdf. I merely summarize.

TL;DR

Get closeout.c and its dependencies from GNUlib into your source and call

atexit(close_stdout);

as your first line in main.

Summary

First, some heads up warning, quoting POSIX:

Since after the call to fclose() any use of stream results in undefined behavior, fclose() should not be used on stdin, stdout, or stderr except immediately before process termination, ...... If there are any atexit() handlers registered by the application, such a call to fclose() should not occur until the last handler is finishing. Once fclose() has been used to close stdin, stdout, or stderr, there is no standard way to reopen any of these streams.

Usage of close() on file descriptors STDIN_FILENO, STDOUT_FILENO or STDERR_FILENO should immediately be followed by an operation to reopen these file descriptors. ...... Furthermore, a close() followed by a reopen operation (e.g. open(), dup() etc) is not atomic; dup2() should be used to change standard file descriptors.

Closing stream without handling its errors is not robust, and it is the same for stdout and stderr. Here is a list of errors you need to handle:

  • fclose(stdout)
  • ferror(stdout) a.k.a. previous error
  • __fpending(stdout) a.k.a. stuff not flushed

Handling these errors, as GNUlib implements in close-stream.c, is quoted below.

int
close_stream (FILE *stream)
{
  const bool some_pending = (__fpending (stream) != 0);
  const bool prev_fail = (ferror (stream) != 0);
  const bool fclose_fail = (fclose (stream) != 0);

  /* Return an error indication if there was a previous failure or if
     fclose failed, with one exception: ignore an fclose failure if
     there was no previous error, no data remains to be flushed, and
     fclose failed with EBADF.  That can happen when a program like cp
     is invoked like this 'cp a b >&-' (i.e., with standard output
     closed) and doesn't generate any output (hence no previous error
     and nothing to be flushed).  */

  if (prev_fail || (fclose_fail && (some_pending || errno != EBADF)))
    {
      if (! fclose_fail)
        errno = 0;
      return EOF;
    }

  return 0;
}

Notice: __fpending is special to glibc and may not be portable. OTOH, it is on the way to be standardized as fpending.

P.S.:

I just wanted to direct the stdout and stderr output to a log file instead of console.

That is not a good reason to close stdout and stderr if you are writing a daemon according to http://cloud9.hedgee.com./scribbles/daemon#logging. You should let a daemon manager (such as daemon tools, runit, s6, nosh, OpenRC and systemd) handle the redirection.

However, you still should close any stream that the program has ever written to in the end to check for errors. Quote from close-stream.c:

If a program writes anything to STREAM, that program should close STREAM and make sure that it succeeds before exiting. Otherwise, suppose that you go to the extreme of checking the return status of every function that does an explicit write to STREAM. The last printf can succeed in writing to the internal stream buffer, and yet the fclose(STREAM) could still fail (due e.g., to a disk full error) when it tries to write out that buffered data. Thus, you would be left with an incomplete output file and the offending program would exit successfully. Even calling fflush is not always sufficient, since some file systems (NFS and CODA) buffer written/flushed data until an actual close call.

Besides, it's wasteful to check the return value from every call that writes to STREAM -- just let the internal stream state record the failure. That's what the ferror test is checking below.

Solution 4

Actually you can also use the close function:

#include<unistd.h> // close
#include<stdio.h>  // STDOUT_FILENO,STDERR_FILENO
...
close(STDOUT_FILENO);
close(STDERR_FILENO);
...
Share:
37,698

Related videos on Youtube

Usman
Author by

Usman

Updated on April 23, 2020

Comments

  • Usman
    Usman about 4 years

    I need to close stdout and stderr for one of my C program. How is it possible without exiting the program in execution?

    • Joseph Stine
      Joseph Stine about 13 years
      Why do you want to close them? What do you hope to accomplish?
    • karlphillip
      karlphillip about 13 years
      What do you expect to happen when you close them?
    • Peyman
      Peyman about 13 years
      @Joseph He might want to put the process in background. In this case you're always better to close all standard file descriptors so that you won't end up having them invalidated.
    • Usman
      Usman about 13 years
      I just wanted to direct the stdout and stderr output to a log file instead of console. Sorry for answering late. I have got it working now.
  • R.. GitHub STOP HELPING ICE
    R.. GitHub STOP HELPING ICE about 13 years
    This code has undefined behavior (using a FILE * after the file was closed). It could just as well crash or worse.
  • karlphillip
    karlphillip about 13 years
    @R That part of the code you mentioned is just to illustrate what the application does since the questioner didn't answered our comments about what he expected to happen after closing stdout/stderr. Since I added a warning in the answer about this, I'm not sure I still deserve the -1, but thanks anyway.
  • R.. GitHub STOP HELPING ICE
    R.. GitHub STOP HELPING ICE about 13 years
    I removed the -1, but "will do" is still erroneous. Saying this is what it "might do" would be better, but either way you should note that the example code has undefined behavior and is an incorrect program.
  • onmyway133
    onmyway133 over 10 years
    @lhf so how to open it again ?
  • lhf
    lhf over 10 years
    @entropy, there is no way to open it again.
  • Jens Munk
    Jens Munk almost 10 years
    you can reopen it using freopen("/dev/tty","w",stdout); This is often used if a call to execl fails.
  • Ungeheuer
    Ungeheuer about 7 years
    Is close any of the 3 default streams dangerous, with potentially persistent ramifications for the actual machine you ran the executable on?

Related