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