maint: use new emit_try_help in place of equivalent fprintf
[coreutils/ericb.git] / src / nohup.c
blobe19d2940e471cb30baf91c6d9f0ffad8ac314640
1 /* nohup -- run a command immune to hangups, with output to a non-tty
2 Copyright (C) 2003-2005, 2007-2012 Free Software Foundation, Inc.
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.
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.
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/>. */
17 /* Written by Jim Meyering */
19 #include <config.h>
20 #include <getopt.h>
21 #include <stdio.h>
22 #include <sys/types.h>
23 #include <signal.h>
25 #include "system.h"
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"
35 #define PROGRAM_NAME "nohup"
37 #define AUTHORS proper_name ("Jim Meyering")
39 /* Exit statuses. */
40 enum
42 /* `nohup' itself failed. */
43 POSIX_NOHUP_FAILURE = 127
46 void
47 usage (int status)
49 if (status != EXIT_SUCCESS)
50 emit_try_help ();
51 else
53 printf (_("\
54 Usage: %s COMMAND [ARG]...\n\
55 or: %s OPTION\n\
56 "),
57 program_name, program_name);
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 ();
75 exit (status);
78 int
79 main (int argc, char **argv)
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;
89 initialize_main (&argc, &argv);
90 set_program_name (argv[0]);
91 setlocale (LC_ALL, "");
92 bindtextdomain (PACKAGE, LOCALEDIR);
93 textdomain (PACKAGE);
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);
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);
109 if (argc <= optind)
111 error (0, 0, _("missing operand"));
112 usage (exit_internal_failure);
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);
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)
125 if (fd_reopen (STDIN_FILENO, "/dev/null", O_WRONLY, 0) < 0)
127 error (0, errno, _("failed to render standard input unusable"));
128 exit (exit_internal_failure);
130 if (!redirecting_stdout && !redirecting_stderr)
131 error (0, 0, _("ignoring input"));
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))
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));
149 if (out_fd < 0)
151 int saved_errno = errno;
152 char const *home = getenv ("HOME");
153 if (home)
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));
160 if (out_fd < 0)
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);
169 file = in_home;
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);
181 /* If standard error is a tty, redirect it. */
182 if (redirecting_stderr)
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);
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"));
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")));
201 if (dup2 (out_fd, STDERR_FILENO) < 0)
202 error (exit_internal_failure, errno,
203 _("failed to redirect standard error"));
205 if (stdout_is_closed)
206 close (out_fd);
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);
218 signal (SIGHUP, SIG_IGN);
221 int exit_status;
222 int saved_errno;
223 char **cmd = argv + optind;
225 execvp (*cmd, cmd);
226 exit_status = (errno == ENOENT ? EXIT_ENOENT : EXIT_CANNOT_INVOKE);
227 saved_errno = errno;
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));
238 exit (exit_status);