Most .c files (AUTHORS): Revert the WRITTEN_BY/AUTHORS change
[platform/upstream/coreutils.git] / src / nohup.c
1 /* nohup -- run a command immume to hangups, with output to a non-tty
2    Copyright (C) 2003 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
17
18 /* Written by Jim Meyering  */
19
20 #include <config.h>
21 #include <stdio.h>
22 #include <sys/types.h>
23 #include <signal.h>
24
25 #include "system.h"
26
27 #include "error.h"
28 #include "long-options.h"
29 #include "path-concat.h"
30 #include "quote.h"
31
32 #define PROGRAM_NAME "nohup"
33
34 #define AUTHORS "Jim Meyering"
35
36 /* Exit statuses.  */
37 enum
38   {
39     /* `nohup' found the specified command but failed to invoke it.  */
40     NOHUP_FOUND_BUT_CANNOT_INVOKE = 126,
41
42     /* `nohup' itself failed, or did not find the specified command.  */
43     NOHUP_FAILURE = 127
44   };
45
46 char *program_name;
47
48 void
49 usage (int status)
50 {
51   if (status != 0)
52     fprintf (stderr, _("Try `%s --help' for more information.\n"),
53              program_name);
54   else
55     {
56       printf (_("\
57 Usage: %s COMMAND [ARG]...\n\
58   or:  %s OPTION\n\
59 "),
60               program_name, program_name);
61
62       fputs (_("\
63 Run COMMAND, ignoring hangup signals.\n\
64 \n\
65 "), stdout);
66       fputs (HELP_OPTION_DESCRIPTION, stdout);
67       fputs (VERSION_OPTION_DESCRIPTION, stdout);
68       printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
69     }
70   exit (status);
71 }
72
73 int
74 main (int argc, char **argv)
75 {
76   int fd;
77   int saved_stderr_fd = -1;
78   int stderr_isatty;
79
80   initialize_main (&argc, &argv);
81   program_name = argv[0];
82   setlocale (LC_ALL, "");
83   bindtextdomain (PACKAGE, LOCALEDIR);
84   textdomain (PACKAGE);
85
86   atexit (close_stdout);
87
88   parse_long_options (argc, argv, PROGRAM_NAME, GNU_PACKAGE, VERSION,
89                       usage, AUTHORS, NULL);
90
91   /* The above handles --help and --version.
92      Now, handle `--'.  */
93   if (argc > 1 && STREQ (argv[1], "--"))
94     {
95       --argc;
96       ++argv;
97     }
98
99   if (argc <= 1)
100     {
101       error (0, 0, _("too few arguments"));
102       usage (NOHUP_FAILURE);
103     }
104
105   /* If standard output is a tty, redirect it (appending) to a file.
106      First try nohup.out, then $HOME/nohup.out.  */
107   if (isatty (STDOUT_FILENO))
108     {
109       char *in_home = NULL;
110       char const *file = "nohup.out";
111       int flags = O_CREAT | O_WRONLY | O_APPEND;
112       mode_t mode = S_IRUSR | S_IWUSR;
113       int saved_errno;
114
115       fd = open (file, flags, mode);
116       if (fd == -1)
117         {
118           saved_errno = errno;
119           in_home = path_concat (getenv ("HOME"), file, NULL);
120           fd = open (in_home, flags, mode);
121           if (fd == -1)
122             {
123               int saved_errno2 = errno;
124               error (0, saved_errno, _("failed to open %s"), quote (file));
125               error (0, saved_errno2, _("failed to open %s"), quote (in_home));
126               exit (NOHUP_FAILURE);
127             }
128           file = in_home;
129         }
130
131       /* Redirect standard output to the file.  */
132       if (dup2 (fd, STDOUT_FILENO) == -1)
133         error (NOHUP_FAILURE, errno, _("failed to redirect standard output"));
134
135       error (0, 0, _("appending output to %s"), quote (file));
136       if (in_home)
137         free (in_home);
138     }
139   else
140     {
141       fd = STDOUT_FILENO;
142     }
143
144   /* If stderr is on a tty, redirect it to stdout.  */
145   if ((stderr_isatty = isatty (STDERR_FILENO)))
146     {
147       /* Save a copy of stderr before redirecting, so we can use the original
148          if execve fails.  It's no big deal if this dup fails.  It might
149          not change anything, and at worst, it'll lead to suppression of
150          the post-failed-execve diagnostic.  */
151       saved_stderr_fd = dup (STDERR_FILENO);
152
153       if (dup2 (fd, STDERR_FILENO) == -1)
154         error (NOHUP_FAILURE, errno, _("failed to redirect standard error"));
155     }
156
157   /* Ignore hang-up signals.  */
158   {
159 #ifdef _POSIX_SOURCE
160     struct sigaction sigact;
161     sigact.sa_handler = SIG_IGN;
162     sigemptyset (&sigact.sa_mask);
163     sigact.sa_flags = 0;
164     sigaction (SIGHUP, &sigact, NULL);
165 #else
166     signal (SIGHUP, SIG_IGN);
167 #endif
168   }
169
170   {
171     int exit_status;
172     int saved_errno;
173     char **cmd = argv + 1;
174
175     execvp (*cmd, cmd);
176     exit_status = (errno == ENOENT
177                    ? NOHUP_FAILURE
178                    : NOHUP_FOUND_BUT_CANNOT_INVOKE);
179     saved_errno = errno;
180
181     /* The execve failed.  Output a diagnostic to stderr only if:
182        - stderr was initially redirected to a non-tty, or
183        - stderr was initially directed to a tty, and we've
184          just dup2'd it to point back to that same tty.
185        In other words, output the diagnostic if possible, but not if
186        it'd go to nohup.out.  */
187     if ( ! stderr_isatty
188         || (saved_stderr_fd != -1
189             && dup2 (saved_stderr_fd, STDERR_FILENO) != -1))
190       error (0, saved_errno, _("cannot run command %s"), quote (*cmd));
191
192     exit (exit_status);
193   }
194 }