/* nohup -- run a command immune to hangups, with output to a non-tty           This is the nohup utility
   Copyright (C) 2003-2018 Free Software Foundation, Inc.                       
                                                                                
   This program is free software: you can redistribute it and/or modify         
   it under the terms of the GNU General Public License as published by         
   the Free Software Foundation, either version 3 of the License, or            
   (at your option) any later version.                                          
                                                                                
   This program is distributed in the hope that it will be useful,              
   but WITHOUT ANY WARRANTY; without even the implied warranty of               
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                
   GNU General Public License for more details.                                 
                                                                                
   You should have received a copy of the GNU General Public License            
   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */   The GNUv3 license
                                                                                
/* Written by Jim Meyering  */                                                  
                                                                                
#include <config.h>                                                             Provides system specific information
#include <getopt.h>                                                             ...!includes auto-comment...
#include <stdio.h>                                                              Provides standard I/O capability
#include <sys/types.h>                                                          Provides system data types
#include <signal.h>                                                             ...!includes auto-comment...
                                                                                
#include "system.h"                                                             ...!includes auto-comment...
                                                                                
#include "error.h"                                                              ...!includes auto-comment...
#include "filenamecat.h"                                                        ...!includes auto-comment...
#include "fd-reopen.h"                                                          ...!includes auto-comment...
#include "long-options.h"                                                       ...!includes auto-comment...
#include "unistd--.h"                                                           ...!includes auto-comment...
                                                                                
#define PROGRAM_NAME "nohup"                                                    Line 33
                                                                                
#define AUTHORS proper_name ("Jim Meyering")                                    Line 35
                                                                                
static struct option const long_options[] =                                     Line 37
{                                                                               
  {NULL, 0, NULL, 0}                                                            Line 39
};                                                                              Block 1
                                                                                
/* Exit statuses.  */                                                           
enum                                                                            Line 43
  {                                                                             
    /* 'nohup' itself failed.  */                                               
    POSIX_NOHUP_FAILURE = 127                                                   Line 46
  };                                                                            
                                                                                
void                                                                            Line 49
usage (int status)                                                              Line 50
{                                                                               
  if (status != EXIT_SUCCESS)                                                   Line 52
    emit_try_help ();                                                           ...!common auto-comment...
  else                                                                          Line 54
    {                                                                           
      printf (_("\                                                              Line 56
Usage: %s COMMAND [ARG]...\n\                                                   Line 57
  or:  %s OPTION\n\                                                             Line 58
"),                                                                             Line 59
              program_name, program_name);                                      Line 60
                                                                                
      fputs (_("\                                                               Line 62
Run COMMAND, ignoring hangup signals.\n\                                        Line 63
\n\                                                                             
"), stdout);                                                                    Line 65
      fputs (HELP_OPTION_DESCRIPTION, stdout);                                  Line 66
      fputs (VERSION_OPTION_DESCRIPTION, stdout);                               Line 67
      printf (_("\n\                                                            Line 68
If standard input is a terminal, redirect it from an unreadable file.\n\        Line 69
If standard output is a terminal, append output to 'nohup.out' if possible,\n\  Line 70
'$HOME/nohup.out' otherwise.\n\                                                 Line 71
If standard error is a terminal, redirect it to standard output.\n\             Line 72
To save output to FILE, use '%s COMMAND > FILE'.\n"),                           Line 73
              program_name);                                                    Line 74
      printf (USAGE_BUILTIN_WARNING, PROGRAM_NAME);                             Line 75
      emit_ancillary_info (PROGRAM_NAME);                                       Line 76
    }                                                                           
  exit (status);                                                                Line 78
}                                                                               Block 3
                                                                                
int                                                                             
main (int argc, char **argv)                                                    Line 82
{                                                                               
  int out_fd = STDOUT_FILENO;                                                   Line 84
  int saved_stderr_fd = STDERR_FILENO;                                          Line 85
  bool ignoring_input;                                                          Line 86
  bool redirecting_stdout;                                                      Line 87
  bool stdout_is_closed;                                                        Line 88
  bool redirecting_stderr;                                                      Line 89
  int exit_internal_failure;                                                    Line 90
                                                                                
  initialize_main (&argc, &argv);                                               VMS-specific entry point handling wildcard expansion
  set_program_name (argv[0]);                                                   Retains program name and discards path
  setlocale (LC_ALL, "");                                                       Sets up internationalization (i18n)
  bindtextdomain (PACKAGE, LOCALEDIR);                                          Assigns i18n directorySets text domain for _() [gettext()] function
  textdomain (PACKAGE);                                                         Sets text domain for _() [gettext()] function
                                                                                
  /* POSIX 2008 requires that internal failure give status 127; unlike          
     for env, exec, nice, time, and xargs where it requires internal            
     failure give something in the range 1-125.  For consistency with           
     other tools, fail with EXIT_CANCELED unless POSIXLY_CORRECT.  */           
  exit_internal_failure = (getenv ("POSIXLY_CORRECT")                           Line 102
                           ? POSIX_NOHUP_FAILURE : EXIT_CANCELED);              Line 103
  initialize_exit_failure (exit_internal_failure);                              Line 104
  atexit (close_stdout);                                                        Close stdout on exit (see gnulib)
                                                                                
  parse_long_options (argc, argv, PROGRAM_NAME, PACKAGE_NAME, Version,          ...!common auto-comment...
                      usage, AUTHORS, (char const *) NULL);                     Line 108
  if (getopt_long (argc, argv, "+", long_options, NULL) != -1)                  Line 109
    usage (exit_internal_failure);                                              Line 110
                                                                                
  if (argc <= optind)                                                           Line 112
    {                                                                           
      error (0, 0, _("missing operand"));                                       Line 114
      usage (exit_internal_failure);                                            Line 115
    }                                                                           
                                                                                
  ignoring_input = isatty (STDIN_FILENO);                                       Line 118
  redirecting_stdout = isatty (STDOUT_FILENO);                                  Line 119
  stdout_is_closed = (!redirecting_stdout && errno == EBADF);                   Line 120
  redirecting_stderr = isatty (STDERR_FILENO);                                  Line 121
                                                                                
  /* If standard input is a tty, replace it with /dev/null if possible.         
     Note that it is deliberately opened for *writing*,                         
     to ensure any read evokes an error.  */                                    
  if (ignoring_input)                                                           Line 126
    {                                                                           
      if (fd_reopen (STDIN_FILENO, "/dev/null", O_WRONLY, 0) < 0)               Line 128...!syscalls auto-comment...
        error (exit_internal_failure, errno,                                    Line 129
               _("failed to render standard input unusable"));                  Line 130
      if (!redirecting_stdout && !redirecting_stderr)                           Line 131
        error (0, 0, _("ignoring input"));                                      Line 132
    }                                                                           
                                                                                
  /* If standard output is a tty, redirect it (appending) to a file.            
     First try nohup.out, then $HOME/nohup.out.  If standard error is           
     a tty and standard output is closed, open nohup.out or                     
     $HOME/nohup.out without redirecting anything.  */                          
  if (redirecting_stdout || (redirecting_stderr && stdout_is_closed))           Line 139
    {                                                                           
      char *in_home = NULL;                                                     Line 141
      char const *file = "nohup.out";                                           Line 142
      int flags = O_CREAT | O_WRONLY | O_APPEND;                                Line 143
      mode_t mode = S_IRUSR | S_IWUSR;                                          Line 144
      mode_t umask_value = umask (~mode);                                       Line 145
      out_fd = (redirecting_stdout                                              Line 146
                ? fd_reopen (STDOUT_FILENO, file, flags, mode)                  Line 147...!syscalls auto-comment...
                : open (file, flags, mode));                                    Line 148...!syscalls auto-comment...
                                                                                
      if (out_fd < 0)                                                           Line 150
        {                                                                       
          int saved_errno = errno;                                              Line 152
          char const *home = getenv ("HOME");                                   Line 153
          if (home)                                                             Line 154
            {                                                                   
              in_home = file_name_concat (home, file, NULL);                    Line 156
              out_fd = (redirecting_stdout                                      Line 157
                        ? fd_reopen (STDOUT_FILENO, in_home, flags, mode)       Line 158...!syscalls auto-comment...
                        : open (in_home, flags, mode));                         Line 159...!syscalls auto-comment...
            }                                                                   
          if (out_fd < 0)                                                       Line 161
            {                                                                   
              int saved_errno2 = errno;                                         Line 163
              error (0, saved_errno, _("failed to open %s"), quoteaf (file));   Line 164
              if (in_home)                                                      Line 165
                error (0, saved_errno2, _("failed to open %s"),                 Line 166
                       quoteaf (in_home));                                      Line 167
              return exit_internal_failure;                                     Line 168
            }                                                                   
          file = in_home;                                                       Line 170
        }                                                                       
                                                                                
      umask (umask_value);                                                      Line 173
      error (0, 0,                                                              Line 174
             _(ignoring_input                                                   Line 175
               ? N_("ignoring input and appending output to %s")                Line 176
               : N_("appending output to %s")),                                 Line 177
             quoteaf (file));                                                   Line 178
      free (in_home);                                                           Line 179
    }                                                                           
                                                                                
  /* If standard error is a tty, redirect it.  */                               
  if (redirecting_stderr)                                                       Line 183
    {                                                                           
      /* Save a copy of stderr before redirecting, so we can use the original   
         if execve fails.  It's no big deal if this dup fails.  It might        
         not change anything, and at worst, it'll lead to suppression of        
         the post-failed-execve diagnostic.  */                                 
      saved_stderr_fd = fcntl (STDERR_FILENO, F_DUPFD_CLOEXEC,                  Line 189...!syscalls auto-comment...
                               STDERR_FILENO + 1);                              Line 190
                                                                                
      if (!redirecting_stdout)                                                  Line 192
        error (0, 0,                                                            Line 193
               _(ignoring_input                                                 Line 194
                 ? N_("ignoring input and redirecting stderr to stdout")        Line 195
                 : N_("redirecting stderr to stdout")));                        Line 196
                                                                                
      if (dup2 (out_fd, STDERR_FILENO) < 0)                                     Line 198
        error (exit_internal_failure, errno,                                    Line 199
               _("failed to redirect standard error"));                         Line 200
                                                                                
      if (stdout_is_closed)                                                     Line 202
        close (out_fd);                                                         Line 203...!syscalls auto-comment...
    }                                                                           
                                                                                
  /* error() flushes stderr, but does not check for write failure.              
     Normally, we would catch this via our atexit() hook of                     
     close_stdout, but execvp() gets in the way.  If stderr                     
     encountered a write failure, there is no need to try calling               
     error() again, particularly since we may have just changed the             
     underlying fd out from under stderr.  */                                   
  if (ferror (stderr))                                                          Line 212
    return exit_internal_failure;                                               Line 213
                                                                                
  signal (SIGHUP, SIG_IGN);                                                     Line 215
                                                                                
  char **cmd = argv + optind;                                                   Line 217
  execvp (*cmd, cmd);                                                           Line 218
  int exit_status = errno == ENOENT ? EXIT_ENOENT : EXIT_CANNOT_INVOKE;         Line 219
  int saved_errno = errno;                                                      Line 220
                                                                                
  /* The execve failed.  Output a diagnostic to stderr only if:                 
     - stderr was initially redirected to a non-tty, or                         
     - stderr was initially directed to a tty, and we                           
     can dup2 it to point back to that same tty.                                
     In other words, output the diagnostic if possible, but only if             
     it will go to the original stderr.  */                                     
  if (dup2 (saved_stderr_fd, STDERR_FILENO) == STDERR_FILENO)                   Line 228
    error (0, saved_errno, _("failed to run command %s"), quoteaf (*cmd));      Line 229
                                                                                
  return exit_status;                                                           Line 231
}                                                                               Block 4