/* nohup -- run a command immune to hangups, with output to a non-tty This is the nohup utility
Copyright (C) 2003-2018 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. */ The GNUv3 license
/* Written by Jim Meyering */
#include <config.h> Provides system specific information
#include <getopt.h> ...!includes auto-comment...
#include <stdio.h> Provides standard I/O capability
#include <sys/types.h> Provides system data types
#include <signal.h> ...!includes auto-comment...
#include "system.h" ...!includes auto-comment...
#include "error.h" ...!includes auto-comment...
#include "filenamecat.h" ...!includes auto-comment...
#include "fd-reopen.h" ...!includes auto-comment...
#include "long-options.h" ...!includes auto-comment...
#include "unistd--.h" ...!includes auto-comment...
#define PROGRAM_NAME "nohup" Line 33
#define AUTHORS proper_name ("Jim Meyering") Line 35
static struct option const long_options[] = Line 37
{
{NULL, 0, NULL, 0} Line 39
}; Block 1
/* Exit statuses. */
enum Line 43
{
/* 'nohup' itself failed. */
POSIX_NOHUP_FAILURE = 127 Line 46
};
void Line 49
usage (int status) Line 50
{
if (status != EXIT_SUCCESS) Line 52
emit_try_help (); ...!common auto-comment...
else Line 54
{
printf (_("\ Line 56
Usage: %s COMMAND [ARG]...\n\ Line 57
or: %s OPTION\n\ Line 58
"), Line 59
program_name, program_name); Line 60
fputs (_("\ Line 62
Run COMMAND, ignoring hangup signals.\n\ Line 63
\n\
"), stdout); Line 65
fputs (HELP_OPTION_DESCRIPTION, stdout); Line 66
fputs (VERSION_OPTION_DESCRIPTION, stdout); Line 67
printf (_("\n\ Line 68
If standard input is a terminal, redirect it from an unreadable file.\n\ Line 69
If standard output is a terminal, append output to 'nohup.out' if possible,\n\ Line 70
'$HOME/nohup.out' otherwise.\n\ Line 71
If standard error is a terminal, redirect it to standard output.\n\ Line 72
To save output to FILE, use '%s COMMAND > FILE'.\n"), Line 73
program_name); Line 74
printf (USAGE_BUILTIN_WARNING, PROGRAM_NAME); Line 75
emit_ancillary_info (PROGRAM_NAME); Line 76
}
exit (status); Line 78
} Block 3
int
main (int argc, char **argv) Line 82
{
int out_fd = STDOUT_FILENO; Line 84
int saved_stderr_fd = STDERR_FILENO; Line 85
bool ignoring_input; Line 86
bool redirecting_stdout; Line 87
bool stdout_is_closed; Line 88
bool redirecting_stderr; Line 89
int exit_internal_failure; Line 90
initialize_main (&argc, &argv); VMS-specific entry point handling wildcard expansion
set_program_name (argv[0]); Retains program name and discards path
setlocale (LC_ALL, ""); Sets up internationalization (i18n)
bindtextdomain (PACKAGE, LOCALEDIR); Assigns i18n directorySets text domain for _() [gettext()] function
textdomain (PACKAGE); Sets text domain for _() [gettext()] function
/* POSIX 2008 requires that internal failure give status 127; unlike
for env, exec, nice, time, and xargs where it requires internal
failure give something in the range 1-125. For consistency with
other tools, fail with EXIT_CANCELED unless POSIXLY_CORRECT. */
exit_internal_failure = (getenv ("POSIXLY_CORRECT") Line 102
? POSIX_NOHUP_FAILURE : EXIT_CANCELED); Line 103
initialize_exit_failure (exit_internal_failure); Line 104
atexit (close_stdout); Close stdout on exit (see gnulib)
parse_long_options (argc, argv, PROGRAM_NAME, PACKAGE_NAME, Version, ...!common auto-comment...
usage, AUTHORS, (char const *) NULL); Line 108
if (getopt_long (argc, argv, "+", long_options, NULL) != -1) Line 109
usage (exit_internal_failure); Line 110
if (argc <= optind) Line 112
{
error (0, 0, _("missing operand")); Line 114
usage (exit_internal_failure); Line 115
}
ignoring_input = isatty (STDIN_FILENO); Line 118
redirecting_stdout = isatty (STDOUT_FILENO); Line 119
stdout_is_closed = (!redirecting_stdout && errno == EBADF); Line 120
redirecting_stderr = isatty (STDERR_FILENO); Line 121
/* If standard input is a tty, replace it with /dev/null if possible.
Note that it is deliberately opened for *writing*,
to ensure any read evokes an error. */
if (ignoring_input) Line 126
{
if (fd_reopen (STDIN_FILENO, "/dev/null", O_WRONLY, 0) < 0) Line 128...!syscalls auto-comment...
error (exit_internal_failure, errno, Line 129
_("failed to render standard input unusable")); Line 130
if (!redirecting_stdout && !redirecting_stderr) Line 131
error (0, 0, _("ignoring input")); Line 132
}
/* If standard output is a tty, redirect it (appending) to a file.
First try nohup.out, then $HOME/nohup.out. If standard error is
a tty and standard output is closed, open nohup.out or
$HOME/nohup.out without redirecting anything. */
if (redirecting_stdout || (redirecting_stderr && stdout_is_closed)) Line 139
{
char *in_home = NULL; Line 141
char const *file = "nohup.out"; Line 142
int flags = O_CREAT | O_WRONLY | O_APPEND; Line 143
mode_t mode = S_IRUSR | S_IWUSR; Line 144
mode_t umask_value = umask (~mode); Line 145
out_fd = (redirecting_stdout Line 146
? fd_reopen (STDOUT_FILENO, file, flags, mode) Line 147...!syscalls auto-comment...
: open (file, flags, mode)); Line 148...!syscalls auto-comment...
if (out_fd < 0) Line 150
{
int saved_errno = errno; Line 152
char const *home = getenv ("HOME"); Line 153
if (home) Line 154
{
in_home = file_name_concat (home, file, NULL); Line 156
out_fd = (redirecting_stdout Line 157
? fd_reopen (STDOUT_FILENO, in_home, flags, mode) Line 158...!syscalls auto-comment...
: open (in_home, flags, mode)); Line 159...!syscalls auto-comment...
}
if (out_fd < 0) Line 161
{
int saved_errno2 = errno; Line 163
error (0, saved_errno, _("failed to open %s"), quoteaf (file)); Line 164
if (in_home) Line 165
error (0, saved_errno2, _("failed to open %s"), Line 166
quoteaf (in_home)); Line 167
return exit_internal_failure; Line 168
}
file = in_home; Line 170
}
umask (umask_value); Line 173
error (0, 0, Line 174
_(ignoring_input Line 175
? N_("ignoring input and appending output to %s") Line 176
: N_("appending output to %s")), Line 177
quoteaf (file)); Line 178
free (in_home); Line 179
}
/* If standard error is a tty, redirect it. */
if (redirecting_stderr) Line 183
{
/* Save a copy of stderr before redirecting, so we can use the original
if execve fails. It's no big deal if this dup fails. It might
not change anything, and at worst, it'll lead to suppression of
the post-failed-execve diagnostic. */
saved_stderr_fd = fcntl (STDERR_FILENO, F_DUPFD_CLOEXEC, Line 189...!syscalls auto-comment...
STDERR_FILENO + 1); Line 190
if (!redirecting_stdout) Line 192
error (0, 0, Line 193
_(ignoring_input Line 194
? N_("ignoring input and redirecting stderr to stdout") Line 195
: N_("redirecting stderr to stdout"))); Line 196
if (dup2 (out_fd, STDERR_FILENO) < 0) Line 198
error (exit_internal_failure, errno, Line 199
_("failed to redirect standard error")); Line 200
if (stdout_is_closed) Line 202
close (out_fd); Line 203...!syscalls auto-comment...
}
/* error() flushes stderr, but does not check for write failure.
Normally, we would catch this via our atexit() hook of
close_stdout, but execvp() gets in the way. If stderr
encountered a write failure, there is no need to try calling
error() again, particularly since we may have just changed the
underlying fd out from under stderr. */
if (ferror (stderr)) Line 212
return exit_internal_failure; Line 213
signal (SIGHUP, SIG_IGN); Line 215
char **cmd = argv + optind; Line 217
execvp (*cmd, cmd); Line 218
int exit_status = errno == ENOENT ? EXIT_ENOENT : EXIT_CANNOT_INVOKE; Line 219
int saved_errno = errno; Line 220
/* The execve failed. Output a diagnostic to stderr only if:
- stderr was initially redirected to a non-tty, or
- stderr was initially directed to a tty, and we
can dup2 it to point back to that same tty.
In other words, output the diagnostic if possible, but only if
it will go to the original stderr. */
if (dup2 (saved_stderr_fd, STDERR_FILENO) == STDERR_FILENO) Line 228
error (0, saved_errno, _("failed to run command %s"), quoteaf (*cmd)); Line 229
return exit_status; Line 231
} Block 4