Line data Source code
1 : /* nohup -- run a command immune to hangups, with output to a non-tty
2 : Copyright (C) 2003, 2004, 2005, 2007 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 "Jim Meyering"
38 :
39 : /* Exit statuses. */
40 : enum
41 : {
42 : /* `nohup' itself failed. */
43 : NOHUP_FAILURE = 127
44 : };
45 :
46 : char *program_name;
47 :
48 : void
49 10 : usage (int status)
50 : {
51 10 : if (status != EXIT_SUCCESS)
52 8 : fprintf (stderr, _("Try `%s --help' for more information.\n"),
53 : program_name);
54 : else
55 : {
56 2 : printf (_("\
57 : Usage: %s COMMAND [ARG]...\n\
58 : or: %s OPTION\n\
59 : "),
60 : program_name, program_name);
61 :
62 2 : fputs (_("\
63 : Run COMMAND, ignoring hangup signals.\n\
64 : \n\
65 : "), stdout);
66 2 : fputs (HELP_OPTION_DESCRIPTION, stdout);
67 2 : fputs (VERSION_OPTION_DESCRIPTION, stdout);
68 2 : printf (_("\n\
69 : If standard input is a terminal, redirect it from /dev/null.\n\
70 : If standard output is a terminal, append output to `nohup.out' if possible,\n\
71 : `$HOME/nohup.out' otherwise.\n\
72 : If standard error is a terminal, redirect it to standard output.\n\
73 : To save output to FILE, use `%s COMMAND > FILE'.\n"),
74 : program_name);
75 2 : printf (USAGE_BUILTIN_WARNING, PROGRAM_NAME);
76 2 : emit_bug_reporting_address ();
77 : }
78 10 : exit (status);
79 : }
80 :
81 : int
82 19 : main (int argc, char **argv)
83 : {
84 19 : int out_fd = STDOUT_FILENO;
85 19 : int saved_stderr_fd = STDERR_FILENO;
86 : bool ignoring_input;
87 : bool redirecting_stdout;
88 : bool stdout_is_closed;
89 : bool redirecting_stderr;
90 :
91 : initialize_main (&argc, &argv);
92 19 : program_name = argv[0];
93 19 : setlocale (LC_ALL, "");
94 : bindtextdomain (PACKAGE, LOCALEDIR);
95 : textdomain (PACKAGE);
96 :
97 19 : initialize_exit_failure (NOHUP_FAILURE);
98 19 : atexit (close_stdout);
99 :
100 19 : parse_long_options (argc, argv, PROGRAM_NAME, PACKAGE_NAME, VERSION,
101 : usage, AUTHORS, (char const *) NULL);
102 16 : if (getopt_long (argc, argv, "+", NULL, NULL) != -1)
103 6 : usage (NOHUP_FAILURE);
104 :
105 10 : if (argc <= optind)
106 : {
107 2 : error (0, 0, _("missing operand"));
108 2 : usage (NOHUP_FAILURE);
109 : }
110 :
111 8 : ignoring_input = isatty (STDIN_FILENO);
112 8 : redirecting_stdout = isatty (STDOUT_FILENO);
113 8 : stdout_is_closed = (!redirecting_stdout && errno == EBADF);
114 8 : redirecting_stderr = isatty (STDERR_FILENO);
115 :
116 : /* If standard input is a tty, replace it with /dev/null if possible.
117 : Note that it is deliberately opened for *writing*,
118 : to ensure any read evokes an error. */
119 8 : if (ignoring_input)
120 : {
121 6 : fd_reopen (STDIN_FILENO, "/dev/null", O_WRONLY, 0);
122 6 : if (!redirecting_stdout && !redirecting_stderr)
123 0 : error (0, 0, _("ignoring input"));
124 : }
125 :
126 : /* If standard output is a tty, redirect it (appending) to a file.
127 : First try nohup.out, then $HOME/nohup.out. If standard error is
128 : a tty and standard output is closed, open nohup.out or
129 : $HOME/nohup.out without redirecting anything. */
130 8 : if (redirecting_stdout || (redirecting_stderr && stdout_is_closed))
131 : {
132 7 : char *in_home = NULL;
133 7 : char const *file = "nohup.out";
134 7 : int flags = O_CREAT | O_WRONLY | O_APPEND;
135 7 : mode_t mode = S_IRUSR | S_IWUSR;
136 7 : mode_t umask_value = umask (~mode);
137 7 : out_fd = (redirecting_stdout
138 : ? fd_reopen (STDOUT_FILENO, file, flags, mode)
139 7 : : open (file, flags, mode));
140 :
141 7 : if (out_fd < 0)
142 : {
143 0 : int saved_errno = errno;
144 0 : char const *home = getenv ("HOME");
145 0 : if (home)
146 : {
147 0 : in_home = file_name_concat (home, file, NULL);
148 0 : out_fd = (redirecting_stdout
149 : ? fd_reopen (STDOUT_FILENO, in_home, flags, mode)
150 0 : : open (in_home, flags, mode));
151 : }
152 0 : if (out_fd < 0)
153 : {
154 0 : int saved_errno2 = errno;
155 0 : error (0, saved_errno, _("failed to open %s"), quote (file));
156 0 : if (in_home)
157 0 : error (0, saved_errno2, _("failed to open %s"),
158 : quote (in_home));
159 0 : exit (NOHUP_FAILURE);
160 : }
161 0 : file = in_home;
162 : }
163 :
164 7 : umask (umask_value);
165 7 : error (0, 0,
166 : _(ignoring_input
167 : ? "ignoring input and appending output to %s"
168 : : "appending output to %s"),
169 : quote (file));
170 7 : free (in_home);
171 : }
172 :
173 : /* If standard error is a tty, redirect it. */
174 8 : if (redirecting_stderr)
175 : {
176 : /* Save a copy of stderr before redirecting, so we can use the original
177 : if execve fails. It's no big deal if this dup fails. It might
178 : not change anything, and at worst, it'll lead to suppression of
179 : the post-failed-execve diagnostic. */
180 8 : saved_stderr_fd = dup (STDERR_FILENO);
181 :
182 8 : if (0 <= saved_stderr_fd
183 8 : && set_cloexec_flag (saved_stderr_fd, true) != 0)
184 0 : error (NOHUP_FAILURE, errno,
185 : _("failed to set the copy of stderr to close on exec"));
186 :
187 8 : if (!redirecting_stdout)
188 1 : error (0, 0,
189 : _(ignoring_input
190 : ? "ignoring input and redirecting stderr to stdout"
191 : : "redirecting stderr to stdout"));
192 :
193 8 : if (dup2 (out_fd, STDERR_FILENO) < 0)
194 0 : error (NOHUP_FAILURE, errno, _("failed to redirect standard error"));
195 :
196 8 : if (stdout_is_closed)
197 0 : close (out_fd);
198 : }
199 :
200 8 : signal (SIGHUP, SIG_IGN);
201 :
202 : {
203 : int exit_status;
204 : int saved_errno;
205 8 : char **cmd = argv + optind;
206 :
207 8 : execvp (*cmd, cmd);
208 8 : exit_status = (errno == ENOENT ? EXIT_ENOENT : EXIT_CANNOT_INVOKE);
209 8 : saved_errno = errno;
210 :
211 : /* The execve failed. Output a diagnostic to stderr only if:
212 : - stderr was initially redirected to a non-tty, or
213 : - stderr was initially directed to a tty, and we
214 : can dup2 it to point back to that same tty.
215 : In other words, output the diagnostic if possible, but only if
216 : it will go to the original stderr. */
217 8 : if (dup2 (saved_stderr_fd, STDERR_FILENO) == STDERR_FILENO)
218 8 : error (0, saved_errno, _("cannot run command %s"), quote (*cmd));
219 :
220 8 : exit (exit_status);
221 : }
222 : }
|