(root)/
coreutils-9.4/
src/
nohup.c
       1  /* nohup -- run a command immune to hangups, with output to a non-tty
       2     Copyright (C) 2003-2023 Free Software Foundation, Inc.
       3  
       4     This program is free software: you can redistribute it and/or modify
       5     it under the terms of the GNU General Public License as published by
       6     the Free Software Foundation, either version 3 of the License, or
       7     (at your option) any later version.
       8  
       9     This program is distributed in the hope that it will be useful,
      10     but WITHOUT ANY WARRANTY; without even the implied warranty of
      11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      12     GNU General Public License for more details.
      13  
      14     You should have received a copy of the GNU General Public License
      15     along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
      16  
      17  /* Written by Jim Meyering  */
      18  
      19  #include <config.h>
      20  #include <stdio.h>
      21  #include <sys/types.h>
      22  #include <signal.h>
      23  
      24  #include "system.h"
      25  
      26  #include "filenamecat.h"
      27  #include "fd-reopen.h"
      28  #include "long-options.h"
      29  #include "unistd--.h"
      30  
      31  #define PROGRAM_NAME "nohup"
      32  
      33  #define AUTHORS proper_name ("Jim Meyering")
      34  
      35  /* Exit statuses.  */
      36  enum
      37    {
      38      /* 'nohup' itself failed.  */
      39      POSIX_NOHUP_FAILURE = 127
      40    };
      41  
      42  void
      43  usage (int status)
      44  {
      45    if (status != EXIT_SUCCESS)
      46      emit_try_help ();
      47    else
      48      {
      49        printf (_("\
      50  Usage: %s COMMAND [ARG]...\n\
      51    or:  %s OPTION\n\
      52  "),
      53                program_name, program_name);
      54  
      55        fputs (_("\
      56  Run COMMAND, ignoring hangup signals.\n\
      57  \n\
      58  "), stdout);
      59        fputs (HELP_OPTION_DESCRIPTION, stdout);
      60        fputs (VERSION_OPTION_DESCRIPTION, stdout);
      61        printf (_("\n\
      62  If standard input is a terminal, redirect it from an unreadable file.\n\
      63  If standard output is a terminal, append output to 'nohup.out' if possible,\n\
      64  '$HOME/nohup.out' otherwise.\n\
      65  If standard error is a terminal, redirect it to standard output.\n\
      66  To save output to FILE, use '%s COMMAND > FILE'.\n"),
      67                program_name);
      68        printf (USAGE_BUILTIN_WARNING, PROGRAM_NAME);
      69        emit_exec_status (PROGRAM_NAME);
      70        emit_ancillary_info (PROGRAM_NAME);
      71      }
      72    exit (status);
      73  }
      74  
      75  /* GCC 13 gets confused by the dup2 calls.  */
      76  #if 13 <= __GNUC__
      77  # pragma GCC diagnostic ignored "-Wanalyzer-fd-leak"
      78  #endif
      79  
      80  int
      81  main (int argc, char **argv)
      82  {
      83    int out_fd = STDOUT_FILENO;
      84    int saved_stderr_fd = STDERR_FILENO;
      85    bool ignoring_input;
      86    bool redirecting_stdout;
      87    bool stdout_is_closed;
      88    bool redirecting_stderr;
      89    int exit_internal_failure;
      90  
      91    initialize_main (&argc, &argv);
      92    set_program_name (argv[0]);
      93    setlocale (LC_ALL, "");
      94    bindtextdomain (PACKAGE, LOCALEDIR);
      95    textdomain (PACKAGE);
      96  
      97    /* POSIX 2008 requires that internal failure give status 127; unlike
      98       for env, exec, nice, time, and xargs where it requires internal
      99       failure give something in the range 1-125.  For consistency with
     100       other tools, fail with EXIT_CANCELED unless POSIXLY_CORRECT.  */
     101    exit_internal_failure = (getenv ("POSIXLY_CORRECT")
     102                             ? POSIX_NOHUP_FAILURE : EXIT_CANCELED);
     103    initialize_exit_failure (exit_internal_failure);
     104    atexit (close_stdout);
     105  
     106    parse_gnu_standard_options_only (argc, argv, PROGRAM_NAME, PACKAGE_NAME,
     107                                     Version, false, usage, AUTHORS,
     108                                     (char const *) nullptr);
     109  
     110    if (argc <= optind)
     111      {
     112        error (0, 0, _("missing operand"));
     113        usage (exit_internal_failure);
     114      }
     115  
     116    ignoring_input = isatty (STDIN_FILENO);
     117    redirecting_stdout = isatty (STDOUT_FILENO);
     118    stdout_is_closed = (!redirecting_stdout && errno == EBADF);
     119    redirecting_stderr = isatty (STDERR_FILENO);
     120  
     121    /* If standard input is a tty, replace it with /dev/null if possible.
     122       Note that it is deliberately opened for *writing*,
     123       to ensure any read evokes an error.  */
     124    if (ignoring_input)
     125      {
     126        if (fd_reopen (STDIN_FILENO, "/dev/null", O_WRONLY, 0) < 0)
     127          error (exit_internal_failure, errno,
     128                 _("failed to render standard input unusable"));
     129        if (!redirecting_stdout && !redirecting_stderr)
     130          error (0, 0, _("ignoring input"));
     131      }
     132  
     133    /* If standard output is a tty, redirect it (appending) to a file.
     134       First try nohup.out, then $HOME/nohup.out.  If standard error is
     135       a tty and standard output is closed, open nohup.out or
     136       $HOME/nohup.out without redirecting anything.  */
     137    if (redirecting_stdout || (redirecting_stderr && stdout_is_closed))
     138      {
     139        char *in_home = nullptr;
     140        char const *file = "nohup.out";
     141        int flags = O_CREAT | O_WRONLY | O_APPEND;
     142        mode_t mode = S_IRUSR | S_IWUSR;
     143        mode_t umask_value = umask (~mode);
     144        out_fd = (redirecting_stdout
     145                  ? fd_reopen (STDOUT_FILENO, file, flags, mode)
     146                  : open (file, flags, mode));
     147  
     148        if (out_fd < 0)
     149          {
     150            int saved_errno = errno;
     151            char const *home = getenv ("HOME");
     152            if (home)
     153              {
     154                in_home = file_name_concat (home, file, nullptr);
     155                out_fd = (redirecting_stdout
     156                          ? fd_reopen (STDOUT_FILENO, in_home, flags, mode)
     157                          : open (in_home, flags, mode));
     158              }
     159            if (out_fd < 0)
     160              {
     161                int saved_errno2 = errno;
     162                error (0, saved_errno, _("failed to open %s"), quoteaf (file));
     163                if (in_home)
     164                  error (0, saved_errno2, _("failed to open %s"),
     165                         quoteaf (in_home));
     166                return exit_internal_failure;
     167              }
     168            file = in_home;
     169          }
     170  
     171        umask (umask_value);
     172        error (0, 0,
     173               _(ignoring_input
     174                 ? N_("ignoring input and appending output to %s")
     175                 : N_("appending output to %s")),
     176               quoteaf (file));
     177        free (in_home);
     178      }
     179  
     180    /* If standard error is a tty, redirect it.  */
     181    if (redirecting_stderr)
     182      {
     183        /* Save a copy of stderr before redirecting, so we can use the original
     184           if execve fails.  It's no big deal if this dup fails.  It might
     185           not change anything, and at worst, it'll lead to suppression of
     186           the post-failed-execve diagnostic.  */
     187        saved_stderr_fd = fcntl (STDERR_FILENO, F_DUPFD_CLOEXEC,
     188                                 STDERR_FILENO + 1);
     189  
     190        if (!redirecting_stdout)
     191          error (0, 0,
     192                 _(ignoring_input
     193                   ? N_("ignoring input and redirecting stderr to stdout")
     194                   : N_("redirecting stderr to stdout")));
     195  
     196        if (dup2 (out_fd, STDERR_FILENO) < 0)
     197          error (exit_internal_failure, errno,
     198                 _("failed to redirect standard error"));
     199  
     200        if (stdout_is_closed)
     201          close (out_fd);
     202      }
     203  
     204    /* error() flushes stderr, but does not check for write failure.
     205       Normally, we would catch this via our atexit() hook of
     206       close_stdout, but execvp() gets in the way.  If stderr
     207       encountered a write failure, there is no need to try calling
     208       error() again, particularly since we may have just changed the
     209       underlying fd out from under stderr.  */
     210    if (ferror (stderr))
     211      return exit_internal_failure;
     212  
     213    signal (SIGHUP, SIG_IGN);
     214  
     215    char **cmd = argv + optind;
     216    execvp (*cmd, cmd);
     217    int exit_status = errno == ENOENT ? EXIT_ENOENT : EXIT_CANNOT_INVOKE;
     218    int saved_errno = errno;
     219  
     220    /* The execve failed.  Output a diagnostic to stderr only if:
     221       - stderr was initially redirected to a non-tty, or
     222       - stderr was initially directed to a tty, and we
     223       can dup2 it to point back to that same tty.
     224       In other words, output the diagnostic if possible, but only if
     225       it will go to the original stderr.  */
     226    if (dup2 (saved_stderr_fd, STDERR_FILENO) == STDERR_FILENO)
     227      error (0, saved_errno, _("failed to run command %s"), quoteaf (*cmd));
     228  
     229    return exit_status;
     230  }