blob: c457bfd93d4090d14ccf8aaad3e2d2655aa61aac [file] [log] [blame]
Bernhard Reutner-Fischer9d7010c2005-09-21 18:25:05 +00001/* vi: set sw=4 ts=4: */
2/* nohup -- run a command immune to hangups, with output to a non-tty
3 Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc.
4
5 Licensed under the GPL v2, see the file LICENSE in this tarball.
6
7 */
8
9/* Written by Jim Meyering */
10/* initial busybox port by Bernhard Fischer */
11
12#include <stdio_ext.h> /* __fpending */
13#include <stdio.h>
14#include <unistd.h>
15#include <fcntl.h>
16#include <sys/types.h>
17#include <signal.h>
18#include <errno.h>
19
20#include "busybox.h"
21#define EXIT_CANNOT_INVOKE (126)
22#define NOHUP_FAILURE (127)
23#define EXIT_ENOENT NOHUP_FAILURE
24
25
26
27#if defined F_GETFD && defined F_SETFD
28static inline int set_cloexec_flag (int desc)
29{
30 int flags = fcntl (desc, F_GETFD, 0);
31 if (0 <= flags) {
32 if (flags == (flags |= FD_CLOEXEC) ||
33 fcntl (desc, F_SETFD, flags) != -1) {
34 return 0;
35 }
36 }
37 return -1;
38}
39#else
40#define set_cloexec_flag(desc) (0)
41#endif
42
"Vladimir N. Oleynik"1f0ac232005-09-22 13:26:23 +000043static int fd_reopen (int desired_fd, char const *file, int flags)
Bernhard Reutner-Fischer9d7010c2005-09-21 18:25:05 +000044{
45 int fd;
46
47 close (desired_fd);
"Vladimir N. Oleynik"1f0ac232005-09-22 13:26:23 +000048 fd = open (file, flags | O_WRONLY, S_IRUSR | S_IWUSR);
Bernhard Reutner-Fischer9d7010c2005-09-21 18:25:05 +000049 if (fd == desired_fd || fd < 0)
50 return fd;
51 else {
52 int fd2 = fcntl (fd, F_DUPFD, desired_fd);
53 int saved_errno = errno;
54 close (fd);
55 errno = saved_errno;
56 return fd2;
57 }
58}
59
60
61/* Close standard output, exiting with status 'exit_failure' on failure.
62 If a program writes *anything* to stdout, that program should close
63 stdout and make sure that it succeeds before exiting. Otherwise,
64 suppose that you go to the extreme of checking the return status
65 of every function that does an explicit write to stdout. The last
66 printf can succeed in writing to the internal stream buffer, and yet
67 the fclose(stdout) could still fail (due e.g., to a disk full error)
68 when it tries to write out that buffered data. Thus, you would be
69 left with an incomplete output file and the offending program would
70 exit successfully. Even calling fflush is not always sufficient,
71 since some file systems (NFS and CODA) buffer written/flushed data
72 until an actual close call.
73
74 Besides, it's wasteful to check the return value from every call
75 that writes to stdout -- just let the internal stream state record
76 the failure. That's what the ferror test is checking below.
77
78 It's important to detect such failures and exit nonzero because many
79 tools (most notably `make' and other build-management systems) depend
80 on being able to detect failure in other tools via their exit status. */
81
82static void close_stdout (void)
83{
84 int prev_fail = ferror (stdout);
85 int none_pending = (0 == __fpending (stdout));
86 int fclose_fail = fclose (stdout);
87
88 if (prev_fail || fclose_fail) {
89 /* If ferror returned zero, no data remains to be flushed, and we'd
90 otherwise fail with EBADF due to a failed fclose, then assume that
91 it's ok to ignore the fclose failure. That can happen when a
92 program like cp is invoked like this `cp a b >&-' (i.e., with
93 stdout closed) and doesn't generate any output (hence no previous
94 error and nothing to be flushed). */
95 if ((fclose_fail ? errno : 0) == EBADF && !prev_fail && none_pending)
96 return;
97
98 bb_perror_msg_and_die(bb_msg_write_error);
99 }
100}
101
102
103int nohup_main (int argc, char **argv)
104{
105 int saved_stderr_fd;
106
107 if (argc < 2)
108 bb_show_usage();
109
110 bb_default_error_retval = NOHUP_FAILURE;
111
112 atexit (close_stdout);
113
114 /* If standard input is a tty, replace it with /dev/null.
115 Note that it is deliberately opened for *writing*,
116 to ensure any read evokes an error. */
117 if (isatty (STDIN_FILENO))
"Vladimir N. Oleynik"1f0ac232005-09-22 13:26:23 +0000118 fd_reopen (STDIN_FILENO, "/dev/null", 0);
Bernhard Reutner-Fischer9d7010c2005-09-21 18:25:05 +0000119
120 /* If standard output is a tty, redirect it (appending) to a file.
121 First try nohup.out, then $HOME/nohup.out. */
122 if (isatty (STDOUT_FILENO)) {
123 char *in_home = NULL;
124 char const *file = "nohup.out";
"Vladimir N. Oleynik"1f0ac232005-09-22 13:26:23 +0000125 int fd = fd_reopen (STDOUT_FILENO, file, O_CREAT | O_APPEND);
Bernhard Reutner-Fischer9d7010c2005-09-21 18:25:05 +0000126
127 if (fd < 0) {
128 if ((in_home = getenv ("HOME")) != NULL) {
129 in_home = concat_path_file(in_home, file);
"Vladimir N. Oleynik"1f0ac232005-09-22 13:26:23 +0000130 fd = fd_reopen (STDOUT_FILENO, in_home, O_CREAT | O_APPEND);
Bernhard Reutner-Fischer9d7010c2005-09-21 18:25:05 +0000131 }
132 if (fd < 0) {
133 bb_perror_msg("failed to open '%s'", file);
134 if (in_home)
135 bb_perror_msg("failed to open '%s'",in_home);
"Vladimir N. Oleynik"1f0ac232005-09-22 13:26:23 +0000136 return (NOHUP_FAILURE);
Bernhard Reutner-Fischer9d7010c2005-09-21 18:25:05 +0000137 }
138 file = in_home;
139 }
140
141 umask (~(S_IRUSR | S_IWUSR));
142 bb_error_msg("appending output to '%s'", file);
143 if (ENABLE_FEATURE_CLEAN_UP)
144 free (in_home);
145 }
146
147 /* If standard error is a tty, redirect it to stdout. */
148 if (isatty (STDERR_FILENO)) {
149 /* Save a copy of stderr before redirecting, so we can use the original
150 if execve fails. It's no big deal if this dup fails. It might
151 not change anything, and at worst, it'll lead to suppression of
152 the post-failed-execve diagnostic. */
153 saved_stderr_fd = dup (STDERR_FILENO);
154
155 if (0 <= saved_stderr_fd && set_cloexec_flag (saved_stderr_fd) == -1)
156 bb_perror_msg_and_die("failed to set the copy"
157 "of stderr to close on exec");
158
159 if (dup2 (STDOUT_FILENO, STDERR_FILENO) < 0) {
160 if (errno != EBADF)
161 bb_perror_msg_and_die("failed to redirect standard error");
162 close (STDERR_FILENO);
163 }
164 } else
165 saved_stderr_fd = STDERR_FILENO;
166
167 signal (SIGHUP, SIG_IGN);
168
169 {
170 char **cmd = argv + 1;
171
172 execvp (*cmd, cmd);
173
174 /* The execve failed. Output a diagnostic to stderr only if:
175 - stderr was initially redirected to a non-tty, or
176 - stderr was initially directed to a tty, and we
177 can dup2 it to point back to that same tty.
178 In other words, output the diagnostic if possible, but only if
179 it will go to the original stderr. */
180 if (dup2 (saved_stderr_fd, STDERR_FILENO) == STDERR_FILENO)
181 bb_perror_msg("cannot run command '%s'",*cmd);
182
183 return (errno == ENOENT ? EXIT_ENOENT : EXIT_CANNOT_INVOKE);
184 }
185}