Implement first instance of NOFORK applet - echo
[oweals/busybox.git] / libbb / vfork_daemon_rexec.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * Rexec program for system have fork() as vfork() with foreground option
4  *
5  * Copyright (C) Vladimir N. Oleynik <dzo@simtreas.ru>
6  * Copyright (C) 2003 Russ Dill <Russ.Dill@asu.edu>
7  *
8  * daemon() portion taken from uClibc:
9  *
10  * Copyright (c) 1991, 1993
11  *      The Regents of the University of California.  All rights reserved.
12  *
13  * Modified for uClibc by Erik Andersen <andersee@debian.org>
14  *
15  * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
16  */
17
18 #include <paths.h>
19 #include "libbb.h"
20
21 /* This does a fork/exec in one call, using vfork().  Returns PID of new child,
22  * -1 for failure.  Runs argv[0], searching path if that has no / in it. */
23 pid_t spawn(char **argv)
24 {
25         /* Compiler should not optimize stores here */
26         volatile int failed;
27         pid_t pid;
28
29 // Ain't it a good place to fflush(NULL)?
30
31         /* Be nice to nommu machines. */
32         failed = 0;
33         pid = vfork();
34         if (pid < 0) /* error */
35                 return pid;
36         if (!pid) { /* child */
37                 /* This macro is ok - it doesn't do NOEXEC/NOFORK tricks */
38                 BB_EXECVP(argv[0], argv);
39
40                 /* We are (maybe) sharing a stack with blocked parent,
41                  * let parent know we failed and then exit to unblock parent
42                  * (but don't run atexit() stuff, which would screw up parent.)
43                  */
44                 failed = errno;
45                 _exit(111);
46         }
47         /* parent */
48         /* Unfortunately, this is not reliable: according to standards
49          * vfork() can be equivalent to fork() and we won't see value
50          * of 'failed'.
51          * Interested party can wait on pid and learn exit code.
52          * If 111 - then it (most probably) failed to exec */
53         if (failed) {
54                 errno = failed;
55                 return -1;
56         }
57         return pid;
58 }
59
60 /* Die with an error message if we can't spawn a child process. */
61 pid_t xspawn(char **argv)
62 {
63         pid_t pid = spawn(argv);
64         if (pid < 0) bb_perror_msg_and_die("%s", *argv);
65         return pid;
66 }
67
68
69
70 #if 0 //ndef BB_NOMMU
71 // Die with an error message if we can't daemonize.
72 void xdaemon(int nochdir, int noclose)
73 {
74         if (daemon(nochdir, noclose))
75                 bb_perror_msg_and_die("daemon");
76 }
77 #endif
78
79 #if 0 // def BB_NOMMU
80 void vfork_daemon_rexec(int nochdir, int noclose, char **argv)
81 {
82         int fd;
83
84         /* Maybe we are already re-execed and come here again? */
85         if (re_execed)
86                 return;
87
88         setsid();
89
90         if (!nochdir)
91                 xchdir("/");
92
93         if (!noclose) {
94                 /* if "/dev/null" doesn't exist, bail out! */
95                 fd = xopen(bb_dev_null, O_RDWR);
96                 dup2(fd, STDIN_FILENO);
97                 dup2(fd, STDOUT_FILENO);
98                 dup2(fd, STDERR_FILENO);
99                 while (fd > 2)
100                         close(fd--);
101         }
102
103         switch (vfork()) {
104         case 0: /* child */
105                 /* Make certain we are not a session leader, or else we
106                  * might reacquire a controlling terminal */
107                 if (vfork())
108                         _exit(0);
109                 /* High-order bit of first char in argv[0] is a hidden
110                  * "we have (alrealy) re-execed, don't do it again" flag */
111                 argv[0][0] |= 0x80;
112                 execv(CONFIG_BUSYBOX_EXEC_PATH, argv);
113                 bb_perror_msg_and_die("exec %s", CONFIG_BUSYBOX_EXEC_PATH);
114         case -1: /* error */
115                 bb_perror_msg_and_die("vfork");
116         default: /* parent */
117                 exit(0);
118         }
119 }
120 #endif /* BB_NOMMU */
121
122 #ifdef BB_NOMMU
123 void forkexit_or_rexec(char **argv)
124 {
125         pid_t pid;
126         /* Maybe we are already re-execed and come here again? */
127         if (re_execed)
128                 return;
129
130         pid = vfork();
131         if (pid < 0) /* wtf? */
132                 bb_perror_msg_and_die("vfork");
133         if (pid) /* parent */
134                 exit(0);
135         /* child - re-exec ourself */
136         /* high-order bit of first char in argv[0] is a hidden
137          * "we have (alrealy) re-execed, don't do it again" flag */
138         argv[0][0] |= 0x80;
139         execv(CONFIG_BUSYBOX_EXEC_PATH, argv);
140         bb_perror_msg_and_die("exec %s", CONFIG_BUSYBOX_EXEC_PATH);
141 }
142 #else
143 /* Dance around (void)...*/
144 #undef forkexit_or_rexec
145 void forkexit_or_rexec(void)
146 {
147         pid_t pid;
148         pid = fork();
149         if (pid < 0) /* wtf? */
150                 bb_perror_msg_and_die("fork");
151         if (pid) /* parent */
152                 exit(0);
153         /* child */
154 }
155 #define forkexit_or_rexec(argv) forkexit_or_rexec()
156 #endif
157
158
159 /* Due to a #define in libbb.h on MMU systems we actually have 1 argument -
160  * char **argv "vanishes" */
161 void bb_daemonize_or_rexec(int flags, char **argv)
162 {
163         int fd;
164
165         fd = xopen(bb_dev_null, O_RDWR);
166
167         if (flags & DAEMON_CHDIR_ROOT)
168                 xchdir("/");
169
170         if (flags & DAEMON_DEVNULL_STDIO) {
171                 close(0);
172                 close(1);
173                 close(2);
174         }
175
176         while ((unsigned)fd < 2)
177                 fd = dup(fd); /* have 0,1,2 open at least to /dev/null */
178
179         if (!(flags & DAEMON_ONLY_SANITIZE)) {
180                 forkexit_or_rexec(argv);
181                 /* if daemonizing, make sure we detach from stdio */
182                 setsid();
183                 dup2(fd, 0);
184                 dup2(fd, 1);
185                 dup2(fd, 2);
186         }
187         if (fd > 2)
188                 close(fd--);
189         if (flags & DAEMON_CLOSE_EXTRA_FDS)
190                 while (fd > 2)
191                         close(fd--); /* close everything after fd#2 */
192 }
193
194 void bb_sanitize_stdio(void)
195 {
196         bb_daemonize_or_rexec(DAEMON_ONLY_SANITIZE, NULL);
197 }