maint: rename octhexdigits macros
[coreutils.git] / src / nohup.c
blobc853638b44a2f78d8f2f9df8470015ac91cb2851
1 /* nohup -- run a command immune to hangups, with output to a non-tty
2 Copyright (C) 2003-2024 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 <https://www.gnu.org/licenses/>. */
17 /* Written by Jim Meyering */
19 #include <config.h>
20 #include <stdio.h>
21 #include <sys/types.h>
22 #include <signal.h>
24 #include "system.h"
26 #include "filenamecat.h"
27 #include "fd-reopen.h"
28 #include "long-options.h"
29 #include "unistd--.h"
31 #define PROGRAM_NAME "nohup"
33 #define AUTHORS proper_name ("Jim Meyering")
35 /* Exit statuses. */
36 enum
38 /* 'nohup' itself failed. */
39 POSIX_NOHUP_FAILURE = 127
42 void
43 usage (int status)
45 if (status != EXIT_SUCCESS)
46 emit_try_help ();
47 else
49 printf (_("\
50 Usage: %s COMMAND [ARG]...\n\
51 or: %s OPTION\n\
52 "),
53 program_name, program_name);
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);
72 exit (status);
75 /* GCC 13 gets confused by the dup2 calls
76 <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=109839>. */
77 #if 13 <= __GNUC__
78 # pragma GCC diagnostic ignored "-Wanalyzer-fd-leak"
79 #endif
81 int
82 main (int argc, char **argv)
84 int out_fd = STDOUT_FILENO;
85 int saved_stderr_fd = STDERR_FILENO;
86 bool ignoring_input;
87 bool redirecting_stdout;
88 bool stdout_is_closed;
89 bool redirecting_stderr;
90 int exit_internal_failure;
92 initialize_main (&argc, &argv);
93 set_program_name (argv[0]);
94 setlocale (LC_ALL, "");
95 bindtextdomain (PACKAGE, LOCALEDIR);
96 textdomain (PACKAGE);
98 /* POSIX 2008 requires that internal failure give status 127; unlike
99 for env, exec, nice, time, and xargs where it requires internal
100 failure give something in the range 1-125. For consistency with
101 other tools, fail with EXIT_CANCELED unless POSIXLY_CORRECT. */
102 exit_internal_failure = (getenv ("POSIXLY_CORRECT")
103 ? POSIX_NOHUP_FAILURE : EXIT_CANCELED);
104 initialize_exit_failure (exit_internal_failure);
105 atexit (close_stdout);
107 parse_gnu_standard_options_only (argc, argv, PROGRAM_NAME, PACKAGE_NAME,
108 Version, false, usage, AUTHORS,
109 (char const *) nullptr);
111 if (argc <= optind)
113 error (0, 0, _("missing operand"));
114 usage (exit_internal_failure);
117 ignoring_input = isatty (STDIN_FILENO);
118 redirecting_stdout = isatty (STDOUT_FILENO);
119 stdout_is_closed = (!redirecting_stdout && errno == EBADF);
120 redirecting_stderr = isatty (STDERR_FILENO);
122 /* If standard input is a tty, replace it with /dev/null if possible.
123 Note that it is deliberately opened for *writing*,
124 to ensure any read evokes an error. */
125 if (ignoring_input)
127 if (fd_reopen (STDIN_FILENO, "/dev/null", O_WRONLY, 0) < 0)
128 error (exit_internal_failure, errno,
129 _("failed to render standard input unusable"));
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 = nullptr;
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, nullptr);
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"), quoteaf (file));
164 if (in_home)
165 error (0, saved_errno2, _("failed to open %s"),
166 quoteaf (in_home));
167 return 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 quoteaf (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 = fcntl (STDERR_FILENO, F_DUPFD_CLOEXEC,
189 STDERR_FILENO + 1);
191 if (!redirecting_stdout)
192 error (0, 0,
193 _(ignoring_input
194 ? N_("ignoring input and redirecting stderr to stdout")
195 : N_("redirecting stderr to stdout")));
197 if (dup2 (out_fd, STDERR_FILENO) < 0)
198 error (exit_internal_failure, errno,
199 _("failed to redirect standard error"));
201 if (stdout_is_closed)
202 close (out_fd);
205 /* error() flushes stderr, but does not check for write failure.
206 Normally, we would catch this via our atexit() hook of
207 close_stdout, but execvp() gets in the way. If stderr
208 encountered a write failure, there is no need to try calling
209 error() again, particularly since we may have just changed the
210 underlying fd out from under stderr. */
211 if (ferror (stderr))
212 return exit_internal_failure;
214 signal (SIGHUP, SIG_IGN);
216 char **cmd = argv + optind;
217 execvp (*cmd, cmd);
218 int exit_status = errno == ENOENT ? EXIT_ENOENT : EXIT_CANNOT_INVOKE;
219 int saved_errno = errno;
221 /* The execve failed. Output a diagnostic to stderr only if:
222 - stderr was initially redirected to a non-tty, or
223 - stderr was initially directed to a tty, and we
224 can dup2 it to point back to that same tty.
225 In other words, output the diagnostic if possible, but only if
226 it will go to the original stderr. */
227 if (dup2 (saved_stderr_fd, STDERR_FILENO) == STDERR_FILENO)
228 error (0, saved_errno, _("failed to run command %s"), quoteaf (*cmd));
230 return exit_status;