Blame lib/closeout.c

Packit 709fb3
/* Close standard output and standard error, exiting with a diagnostic on error.
Packit 709fb3
Packit 709fb3
   Copyright (C) 1998-2002, 2004, 2006, 2008-2017 Free Software Foundation,
Packit 709fb3
   Inc.
Packit 709fb3
Packit 709fb3
   This program is free software: you can redistribute it and/or modify
Packit 709fb3
   it under the terms of the GNU General Public License as published by
Packit 709fb3
   the Free Software Foundation; either version 3 of the License, or
Packit 709fb3
   (at your option) any later version.
Packit 709fb3
Packit 709fb3
   This program is distributed in the hope that it will be useful,
Packit 709fb3
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 709fb3
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 709fb3
   GNU General Public License for more details.
Packit 709fb3
Packit 709fb3
   You should have received a copy of the GNU General Public License
Packit 709fb3
   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
Packit 709fb3
Packit 709fb3
#include <config.h>
Packit 709fb3
Packit 709fb3
#include "closeout.h"
Packit 709fb3
Packit 709fb3
#include <errno.h>
Packit 709fb3
#include <stdbool.h>
Packit 709fb3
#include <stdio.h>
Packit 709fb3
#include <unistd.h>
Packit 709fb3
Packit 709fb3
#include "gettext.h"
Packit 709fb3
#define _(msgid) gettext (msgid)
Packit 709fb3
Packit 709fb3
#include "close-stream.h"
Packit 709fb3
#include "error.h"
Packit 709fb3
#include "exitfail.h"
Packit 709fb3
#include "quotearg.h"
Packit 709fb3
Packit 709fb3
#ifndef __has_feature
Packit 709fb3
# define __has_feature(a) false
Packit 709fb3
#endif
Packit 709fb3
Packit 709fb3
#if defined __SANITIZE_ADDRESS__ || __has_feature (address_sanitizer)
Packit 709fb3
enum { SANITIZE_ADDRESS = true };
Packit 709fb3
#else
Packit 709fb3
enum { SANITIZE_ADDRESS = false };
Packit 709fb3
#endif
Packit 709fb3
Packit 709fb3
static const char *file_name;
Packit 709fb3
Packit 709fb3
/* Set the file name to be reported in the event an error is detected
Packit 709fb3
   by close_stdout.  */
Packit 709fb3
void
Packit 709fb3
close_stdout_set_file_name (const char *file)
Packit 709fb3
{
Packit 709fb3
  file_name = file;
Packit 709fb3
}
Packit 709fb3
Packit 709fb3
static bool ignore_EPIPE /* = false */;
Packit 709fb3
Packit 709fb3
/* Specify the reaction to an EPIPE error during the closing of stdout:
Packit 709fb3
     - If ignore = true, it shall be ignored.
Packit 709fb3
     - If ignore = false, it shall evoke a diagnostic, along with a nonzero
Packit 709fb3
       exit status.
Packit 709fb3
   The default is ignore = false.
Packit 709fb3
Packit 709fb3
   This setting matters only if the SIGPIPE signal is ignored (i.e. its
Packit 709fb3
   handler set to SIG_IGN) or blocked.  Only particular programs need to
Packit 709fb3
   temporarily ignore SIGPIPE.  If SIGPIPE is ignored or blocked because
Packit 709fb3
   it was ignored or blocked in the parent process when it created the
Packit 709fb3
   child process, it usually is a bug in the parent process: It is bad
Packit 709fb3
   practice to have SIGPIPE ignored or blocked while creating a child
Packit 709fb3
   process.
Packit 709fb3
Packit 709fb3
   EPIPE occurs when writing to a pipe or socket that has no readers now,
Packit 709fb3
   when SIGPIPE is ignored or blocked.
Packit 709fb3
Packit 709fb3
   The ignore = false setting is suitable for a scenario where it is normally
Packit 709fb3
   guaranteed that the pipe writer terminates before the pipe reader.  In
Packit 709fb3
   this case, an EPIPE is an indication of a premature termination of the
Packit 709fb3
   pipe reader and should lead to a diagnostic and a nonzero exit status.
Packit 709fb3
Packit 709fb3
   The ignore = true setting is suitable for a scenario where you don't know
Packit 709fb3
   ahead of time whether the pipe writer or the pipe reader will terminate
Packit 709fb3
   first.  In this case, an EPIPE is an indication that the pipe writer can
Packit 709fb3
   stop doing useless write() calls; this is what close_stdout does anyway.
Packit 709fb3
   EPIPE is part of the normal pipe/socket shutdown protocol in this case,
Packit 709fb3
   and should not lead to a diagnostic message.  */
Packit 709fb3
Packit 709fb3
void
Packit 709fb3
close_stdout_set_ignore_EPIPE (bool ignore)
Packit 709fb3
{
Packit 709fb3
  ignore_EPIPE = ignore;
Packit 709fb3
}
Packit 709fb3
Packit 709fb3
/* Close standard output.  On error, issue a diagnostic and _exit
Packit 709fb3
   with status 'exit_failure'.
Packit 709fb3
Packit 709fb3
   Also close standard error.  On error, _exit with status 'exit_failure'.
Packit 709fb3
Packit 709fb3
   Since close_stdout is commonly registered via 'atexit', POSIX
Packit 709fb3
   and the C standard both say that it should not call 'exit',
Packit 709fb3
   because the behavior is undefined if 'exit' is called more than
Packit 709fb3
   once.  So it calls '_exit' instead of 'exit'.  If close_stdout
Packit 709fb3
   is registered via atexit before other functions are registered,
Packit 709fb3
   the other functions can act before this _exit is invoked.
Packit 709fb3
Packit 709fb3
   Applications that use close_stdout should flush any streams
Packit 709fb3
   other than stdout and stderr before exiting, since the call to
Packit 709fb3
   _exit will bypass other buffer flushing.  Applications should
Packit 709fb3
   be flushing and closing other streams anyway, to check for I/O
Packit 709fb3
   errors.  Also, applications should not use tmpfile, since _exit
Packit 709fb3
   can bypass the removal of these files.
Packit 709fb3
Packit 709fb3
   It's important to detect such failures and exit nonzero because many
Packit 709fb3
   tools (most notably 'make' and other build-management systems) depend
Packit 709fb3
   on being able to detect failure in other tools via their exit status.  */
Packit 709fb3
Packit 709fb3
void
Packit 709fb3
close_stdout (void)
Packit 709fb3
{
Packit 709fb3
  if (close_stream (stdout) != 0
Packit 709fb3
      && !(ignore_EPIPE && errno == EPIPE))
Packit 709fb3
    {
Packit 709fb3
      char const *write_error = _("write error");
Packit 709fb3
      if (file_name)
Packit 709fb3
        error (0, errno, "%s: %s", quotearg_colon (file_name),
Packit 709fb3
               write_error);
Packit 709fb3
      else
Packit 709fb3
        error (0, errno, "%s", write_error);
Packit 709fb3
Packit 709fb3
      _exit (exit_failure);
Packit 709fb3
    }
Packit 709fb3
Packit 709fb3
  /* Close stderr only if not sanitizing, as sanitizers may report to
Packit 709fb3
     stderr after this function returns.  */
Packit 709fb3
  if (!SANITIZE_ADDRESS && close_stream (stderr) != 0)
Packit 709fb3
    _exit (exit_failure);
Packit 709fb3
}