1/* nohup -- run a command immune to hangups, with output to a non-tty
2   Copyright (C) 2003-2005, 2007-2010 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 <http://www.gnu.org/licenses/>.  */
16
17/* Written by Jim Meyering  */
18
19#include <config.h>
20#include <getopt.h>
21#include <stdio.h>
22#include <sys/types.h>
23#include <signal.h>
24
25#include "system.h"
26
27#include "cloexec.h"
28#include "error.h"
29#include "filenamecat.h"
30#include "fd-reopen.h"
31#include "long-options.h"
32#include "quote.h"
33#include "unistd--.h"
34
35#define PROGRAM_NAME "nohup"
36
37#define AUTHORS proper_name ("Jim Meyering")
38
39/* Exit statuses.  */
40enum
41  {
42    /* `nohup' itself failed.  */
43    POSIX_NOHUP_FAILURE = 127
44  };
45
46void
47usage (int status)
48{
49  if (status != EXIT_SUCCESS)
50    fprintf (stderr, _("Try `%s --help' for more information.\n"),
51             program_name);
52  else
53    {
54      printf (_("\
55Usage: %s COMMAND [ARG]...\n\
56  or:  %s OPTION\n\
57"),
58              program_name, program_name);
59
60      fputs (_("\
61Run COMMAND, ignoring hangup signals.\n\
62\n\
63"), stdout);
64      fputs (HELP_OPTION_DESCRIPTION, stdout);
65      fputs (VERSION_OPTION_DESCRIPTION, stdout);
66      printf (_("\n\
67If standard input is a terminal, redirect it from /dev/null.\n\
68If standard output is a terminal, append output to `nohup.out' if possible,\n\
69`$HOME/nohup.out' otherwise.\n\
70If standard error is a terminal, redirect it to standard output.\n\
71To save output to FILE, use `%s COMMAND > FILE'.\n"),
72              program_name);
73      printf (USAGE_BUILTIN_WARNING, PROGRAM_NAME);
74      emit_ancillary_info ();
75    }
76  exit (status);
77}
78
79int
80main (int argc, char **argv)
81{
82  int out_fd = STDOUT_FILENO;
83  int saved_stderr_fd = STDERR_FILENO;
84  bool ignoring_input;
85  bool redirecting_stdout;
86  bool stdout_is_closed;
87  bool redirecting_stderr;
88  int exit_internal_failure;
89
90  initialize_main (&argc, &argv);
91  set_program_name (argv[0]);
92  setlocale (LC_ALL, "");
93  bindtextdomain (PACKAGE, LOCALEDIR);
94  textdomain (PACKAGE);
95
96  /* POSIX 2008 requires that internal failure give status 127; unlike
97     for env, exec, nice, time, and xargs where it requires internal
98     failure give something in the range 1-125.  For consistency with
99     other tools, fail with EXIT_CANCELED unless POSIXLY_CORRECT.  */
100  exit_internal_failure = (getenv ("POSIXLY_CORRECT")
101                           ? POSIX_NOHUP_FAILURE : EXIT_CANCELED);
102  initialize_exit_failure (exit_internal_failure);
103  atexit (close_stdout);
104
105  parse_long_options (argc, argv, PROGRAM_NAME, PACKAGE_NAME, Version,
106                      usage, AUTHORS, (char const *) NULL);
107  if (getopt_long (argc, argv, "+", NULL, NULL) != -1)
108    usage (exit_internal_failure);
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        {
128          error (0, errno, _("failed to render standard input unusable"));
129          exit (exit_internal_failure);
130        }
131      if (!redirecting_stdout && !redirecting_stderr)
132        error (0, 0, _("ignoring input"));
133    }
134
135  /* If standard output is a tty, redirect it (appending) to a file.
136     First try nohup.out, then $HOME/nohup.out.  If standard error is
137     a tty and standard output is closed, open nohup.out or
138     $HOME/nohup.out without redirecting anything.  */
139  if (redirecting_stdout || (redirecting_stderr && stdout_is_closed))
140    {
141      char *in_home = NULL;
142      char const *file = "nohup.out";
143      int flags = O_CREAT | O_WRONLY | O_APPEND;
144      mode_t mode = S_IRUSR | S_IWUSR;
145      mode_t umask_value = umask (~mode);
146      out_fd = (redirecting_stdout
147                ? fd_reopen (STDOUT_FILENO, file, flags, mode)
148                : open (file, flags, mode));
149
150      if (out_fd < 0)
151        {
152          int saved_errno = errno;
153          char const *home = getenv ("HOME");
154          if (home)
155            {
156              in_home = file_name_concat (home, file, NULL);
157              out_fd = (redirecting_stdout
158                        ? fd_reopen (STDOUT_FILENO, in_home, flags, mode)
159                        : open (in_home, flags, mode));
160            }
161          if (out_fd < 0)
162            {
163              int saved_errno2 = errno;
164              error (0, saved_errno, _("failed to open %s"), quote (file));
165              if (in_home)
166                error (0, saved_errno2, _("failed to open %s"),
167                       quote (in_home));
168              exit (exit_internal_failure);
169            }
170          file = in_home;
171        }
172
173      umask (umask_value);
174      error (0, 0,
175             _(ignoring_input
176               ? N_("ignoring input and appending output to %s")
177               : N_("appending output to %s")),
178             quote (file));
179      free (in_home);
180    }
181
182  /* If standard error is a tty, redirect it.  */
183  if (redirecting_stderr)
184    {
185      /* Save a copy of stderr before redirecting, so we can use the original
186         if execve fails.  It's no big deal if this dup fails.  It might
187         not change anything, and at worst, it'll lead to suppression of
188         the post-failed-execve diagnostic.  */
189      saved_stderr_fd = dup (STDERR_FILENO);
190
191      if (0 <= saved_stderr_fd
192          && set_cloexec_flag (saved_stderr_fd, true) != 0)
193        error (exit_internal_failure, errno,
194               _("failed to set the copy of stderr to close on exec"));
195
196      if (!redirecting_stdout)
197        error (0, 0,
198               _(ignoring_input
199                 ? N_("ignoring input and redirecting stderr to stdout")
200                 : N_("redirecting stderr to stdout")));
201
202      if (dup2 (out_fd, STDERR_FILENO) < 0)
203        error (exit_internal_failure, errno,
204               _("failed to redirect standard error"));
205
206      if (stdout_is_closed)
207        close (out_fd);
208    }
209
210  /* error() flushes stderr, but does not check for write failure.
211     Normally, we would catch this via our atexit() hook of
212     close_stdout, but execvp() gets in the way.  If stderr
213     encountered a write failure, there is no need to try calling
214     error() again, particularly since we may have just changed the
215     underlying fd out from under stderr.  */
216  if (ferror (stderr))
217    exit (exit_internal_failure);
218
219  signal (SIGHUP, SIG_IGN);
220
221  {
222    int exit_status;
223    int saved_errno;
224    char **cmd = argv + optind;
225
226    execvp (*cmd, cmd);
227    exit_status = (errno == ENOENT ? EXIT_ENOENT : EXIT_CANNOT_INVOKE);
228    saved_errno = errno;
229
230    /* The execve failed.  Output a diagnostic to stderr only if:
231       - stderr was initially redirected to a non-tty, or
232       - stderr was initially directed to a tty, and we
233         can dup2 it to point back to that same tty.
234       In other words, output the diagnostic if possible, but only if
235       it will go to the original stderr.  */
236    if (dup2 (saved_stderr_fd, STDERR_FILENO) == STDERR_FILENO)
237      error (0, saved_errno, _("failed to run command %s"), quote (*cmd));
238
239    exit (exit_status);
240  }
241}
242