Tizen 2.0 Release
[external/tizen-coreutils.git] / src / nohup.c
1 /* nohup -- run a command immune to hangups, with output to a non-tty
2    Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc.
3
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.
8
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.
13
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.  */
17
18 /* Written by Jim Meyering  */
19
20 #include <config.h>
21 #include <getopt.h>
22 #include <stdio.h>
23 #include <sys/types.h>
24 #include <signal.h>
25
26 #include "system.h"
27
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"
35
36 #define PROGRAM_NAME "nohup"
37
38 #define AUTHORS "Jim Meyering"
39
40 /* Exit statuses.  */
41 enum
42   {
43     /* `nohup' itself failed.  */
44     NOHUP_FAILURE = 127
45   };
46
47 char *program_name;
48
49 void
50 usage (int status)
51 {
52   if (status != EXIT_SUCCESS)
53     fprintf (stderr, _("Try `%s --help' for more information.\n"),
54              program_name);
55   else
56     {
57       printf (_("\
58 Usage: %s COMMAND [ARG]...\n\
59   or:  %s OPTION\n\
60 "),
61               program_name, program_name);
62
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);
71     }
72   exit (status);
73 }
74
75 int
76 main (int argc, char **argv)
77 {
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;
84
85   initialize_main (&argc, &argv);
86   program_name = argv[0];
87   setlocale (LC_ALL, "");
88   bindtextdomain (PACKAGE, LOCALEDIR);
89   textdomain (PACKAGE);
90
91   initialize_exit_failure (NOHUP_FAILURE);
92   atexit (close_stdout);
93
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);
98
99   if (argc <= optind)
100     {
101       error (0, 0, _("missing operand"));
102       usage (NOHUP_FAILURE);
103     }
104
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);
109
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)
114     {
115       fd_reopen (STDIN_FILENO, "/dev/null", O_WRONLY, 0);
116       if (!redirecting_stdout && !redirecting_stderr)
117         error (0, 0, _("ignoring input"));
118     }
119
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))
125     {
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));
134
135       if (out_fd < 0)
136         {
137           int saved_errno = errno;
138           char const *home = getenv ("HOME");
139           if (home)
140             {
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));
145             }
146           if (out_fd < 0)
147             {
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);
154             }
155           file = in_home;
156         }
157
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);
165     }
166
167   /* If standard error is a tty, redirect it.  */
168   if (redirecting_stderr)
169     {
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);
175
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"));
180
181       if (!redirecting_stdout)
182         error (0, 0,
183                _(ignoring_input
184                  ? "ignoring input and redirecting stderr to stdout"
185                  : "redirecting stderr to stdout"));
186
187       if (dup2 (out_fd, STDERR_FILENO) < 0)
188         error (NOHUP_FAILURE, errno, _("failed to redirect standard error"));
189
190       if (stdout_is_closed)
191         close (out_fd);
192     }
193
194   signal (SIGHUP, SIG_IGN);
195
196   {
197     int exit_status;
198     int saved_errno;
199     char **cmd = argv + optind;
200
201     execvp (*cmd, cmd);
202     exit_status = (errno == ENOENT ? EXIT_ENOENT : EXIT_CANNOT_INVOKE);
203     saved_errno = errno;
204
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));
213
214     exit (exit_status);
215   }
216 }