- add applet nohup(1)
[platform/upstream/busybox.git] / coreutils / nohup.c
1 /* 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
28 static 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
43 static int fd_reopen (int desired_fd, char const *file, int flags, mode_t mode)
44 {
45         int fd;
46
47         close (desired_fd);
48         fd = open (file, flags, mode);
49         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
82 static 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
103 int 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))
118                 fd_reopen (STDIN_FILENO, "/dev/null", O_WRONLY, 0);
119
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";
125                 int fd = fd_reopen (STDOUT_FILENO, file,
126                                 O_CREAT | O_WRONLY | O_APPEND, S_IRUSR | S_IWUSR);
127
128                 if (fd < 0) {
129                         if ((in_home = getenv ("HOME")) != NULL) {
130                                 in_home = concat_path_file(in_home, file);
131                                 fd = fd_reopen (STDOUT_FILENO, in_home,
132                                                 O_CREAT | O_WRONLY | O_APPEND, S_IRUSR | S_IWUSR);
133                         }
134                         if (fd < 0) {
135                                 bb_perror_msg("failed to open '%s'", file);
136                                 if (in_home)
137                                         bb_perror_msg("failed to open '%s'",in_home);
138                                 exit (NOHUP_FAILURE);
139                         }
140                         file = in_home;
141                 }
142
143                 umask (~(S_IRUSR | S_IWUSR));
144                 bb_error_msg("appending output to '%s'", file);
145                 if (ENABLE_FEATURE_CLEAN_UP)
146                         free (in_home);
147         }
148
149         /* If standard error is a tty, redirect it to stdout.  */
150         if (isatty (STDERR_FILENO)) {
151         /* Save a copy of stderr before redirecting, so we can use the original
152          if execve fails.  It's no big deal if this dup fails.  It might
153          not change anything, and at worst, it'll lead to suppression of
154          the post-failed-execve diagnostic.  */
155                 saved_stderr_fd = dup (STDERR_FILENO);
156
157                 if (0 <= saved_stderr_fd && set_cloexec_flag (saved_stderr_fd) == -1)
158                         bb_perror_msg_and_die("failed to set the copy"
159                                         "of stderr to close on exec");
160
161                 if (dup2 (STDOUT_FILENO, STDERR_FILENO) < 0) {
162                         if (errno != EBADF)
163                                 bb_perror_msg_and_die("failed to redirect standard error");
164                         close (STDERR_FILENO);
165                 }
166         } else
167                 saved_stderr_fd = STDERR_FILENO;
168
169         signal (SIGHUP, SIG_IGN);
170
171         {
172         char **cmd = argv + 1;
173
174         execvp (*cmd, cmd);
175
176         /* The execve failed.  Output a diagnostic to stderr only if:
177            - stderr was initially redirected to a non-tty, or
178            - stderr was initially directed to a tty, and we
179            can dup2 it to point back to that same tty.
180            In other words, output the diagnostic if possible, but only if
181            it will go to the original stderr.  */
182         if (dup2 (saved_stderr_fd, STDERR_FILENO) == STDERR_FILENO)
183                 bb_perror_msg("cannot run command '%s'",*cmd);
184
185         return (errno == ENOENT ? EXIT_ENOENT : EXIT_CANNOT_INVOKE);
186         }
187 }
188