*** empty log message ***
[coreutils.git] / src / nohup.c
blob1f8e62bc4f0c69bff0df03d63f9262393df68077
1 /* nohup -- run a command immune to hangups, with output to a non-tty
2 Copyright (C) 2003, 2004, 2005 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 2, or (at your option)
7 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, write to the Free Software Foundation,
16 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
18 /* Written by Jim Meyering */
20 #include <config.h>
21 #include <getopt.h>
22 #include <stdio.h>
23 #include <sys/types.h>
24 #include <signal.h>
26 #include "system.h"
28 #include "cloexec.h"
29 #include "error.h"
30 #include "filenamecat.h"
31 #include "fd-reopen.h"
32 #include "long-options.h"
33 #include "quote.h"
34 #include "unistd--.h"
36 #define PROGRAM_NAME "nohup"
38 #define AUTHORS "Jim Meyering"
40 /* Exit statuses. */
41 enum
43 /* `nohup' itself failed. */
44 NOHUP_FAILURE = 127
47 char *program_name;
49 void
50 usage (int status)
52 if (status != EXIT_SUCCESS)
53 fprintf (stderr, _("Try `%s --help' for more information.\n"),
54 program_name);
55 else
57 printf (_("\
58 Usage: %s COMMAND [ARG]...\n\
59 or: %s OPTION\n\
60 "),
61 program_name, program_name);
63 fputs (_("\
64 Run COMMAND, ignoring hangup signals.\n\
65 \n\
66 "), stdout);
67 fputs (HELP_OPTION_DESCRIPTION, stdout);
68 fputs (VERSION_OPTION_DESCRIPTION, stdout);
69 printf (USAGE_BUILTIN_WARNING, PROGRAM_NAME);
70 printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
72 exit (status);
75 int
76 main (int argc, char **argv)
78 int out_fd = STDOUT_FILENO;
79 int saved_stderr_fd = STDERR_FILENO;
80 bool ignoring_input;
81 bool redirecting_stdout;
82 bool stdout_is_closed;
83 bool redirecting_stderr;
85 initialize_main (&argc, &argv);
86 program_name = argv[0];
87 setlocale (LC_ALL, "");
88 bindtextdomain (PACKAGE, LOCALEDIR);
89 textdomain (PACKAGE);
91 initialize_exit_failure (NOHUP_FAILURE);
92 atexit (close_stdout);
94 parse_long_options (argc, argv, PROGRAM_NAME, GNU_PACKAGE, VERSION,
95 usage, AUTHORS, (char const *) NULL);
96 if (getopt_long (argc, argv, "+", NULL, NULL) != -1)
97 usage (NOHUP_FAILURE);
99 if (argc <= optind)
101 error (0, 0, _("missing operand"));
102 usage (NOHUP_FAILURE);
105 ignoring_input = isatty (STDIN_FILENO);
106 redirecting_stdout = isatty (STDOUT_FILENO);
107 stdout_is_closed = (!redirecting_stdout && errno == EBADF);
108 redirecting_stderr = isatty (STDERR_FILENO);
110 /* If standard input is a tty, replace it with /dev/null if possible.
111 Note that it is deliberately opened for *writing*,
112 to ensure any read evokes an error. */
113 if (ignoring_input)
115 fd_reopen (STDIN_FILENO, "/dev/null", O_WRONLY, 0);
116 if (!redirecting_stdout && !redirecting_stderr)
117 error (0, 0, _("ignoring input"));
120 /* If standard output is a tty, redirect it (appending) to a file.
121 First try nohup.out, then $HOME/nohup.out. If standard error is
122 a tty and standard output is closed, open nohup.out or
123 $HOME/nohup.out without redirecting anything. */
124 if (redirecting_stdout || (redirecting_stderr && stdout_is_closed))
126 char *in_home = NULL;
127 char const *file = "nohup.out";
128 int flags = O_CREAT | O_WRONLY | O_APPEND;
129 mode_t mode = S_IRUSR | S_IWUSR;
130 mode_t umask_value = umask (~mode);
131 out_fd = (redirecting_stdout
132 ? fd_reopen (STDOUT_FILENO, file, flags, mode)
133 : open (file, flags, mode));
135 if (out_fd < 0)
137 int saved_errno = errno;
138 char const *home = getenv ("HOME");
139 if (home)
141 in_home = file_name_concat (home, file, NULL);
142 out_fd = (redirecting_stdout
143 ? fd_reopen (STDOUT_FILENO, in_home, flags, mode)
144 : open (in_home, flags, mode));
146 if (out_fd < 0)
148 int saved_errno2 = errno;
149 error (0, saved_errno, _("failed to open %s"), quote (file));
150 if (in_home)
151 error (0, saved_errno2, _("failed to open %s"),
152 quote (in_home));
153 exit (NOHUP_FAILURE);
155 file = in_home;
158 umask (umask_value);
159 error (0, 0,
160 _(ignoring_input
161 ? "ignoring input and appending output to %s"
162 : "appending output to %s"),
163 quote (file));
164 free (in_home);
167 /* If standard error is a tty, redirect it. */
168 if (redirecting_stderr)
170 /* Save a copy of stderr before redirecting, so we can use the original
171 if execve fails. It's no big deal if this dup fails. It might
172 not change anything, and at worst, it'll lead to suppression of
173 the post-failed-execve diagnostic. */
174 saved_stderr_fd = dup (STDERR_FILENO);
176 if (0 <= saved_stderr_fd
177 && set_cloexec_flag (saved_stderr_fd, true) != 0)
178 error (NOHUP_FAILURE, errno,
179 _("failed to set the copy of stderr to close on exec"));
181 if (!redirecting_stdout)
182 error (0, 0,
183 _(ignoring_input
184 ? "ignoring input and redirecting stderr to stdout"
185 : "redirecting stderr to stdout"));
187 if (dup2 (out_fd, STDERR_FILENO) < 0)
188 error (NOHUP_FAILURE, errno, _("failed to redirect standard error"));
190 if (stdout_is_closed)
191 close (out_fd);
194 signal (SIGHUP, SIG_IGN);
197 int exit_status;
198 int saved_errno;
199 char **cmd = argv + optind;
201 execvp (*cmd, cmd);
202 exit_status = (errno == ENOENT ? EXIT_ENOENT : EXIT_CANNOT_INVOKE);
203 saved_errno = errno;
205 /* The execve failed. Output a diagnostic to stderr only if:
206 - stderr was initially redirected to a non-tty, or
207 - stderr was initially directed to a tty, and we
208 can dup2 it to point back to that same tty.
209 In other words, output the diagnostic if possible, but only if
210 it will go to the original stderr. */
211 if (dup2 (saved_stderr_fd, STDERR_FILENO) == STDERR_FILENO)
212 error (0, saved_errno, _("cannot run command %s"), quote (*cmd));
214 exit (exit_status);