libbb: rework NOMMU helper API so that it makes more sense
[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         // Be nice to nommu machines.
30         failed = 0;
31         pid = vfork();
32         if (pid < 0) /* error */
33                 return pid;
34         if (!pid) { /* child */
35                 /* Don't use BB_EXECVP tricks here! */
36                 execvp(argv[0], argv);
37
38                 /* We are (maybe) sharing a stack with blocked parent,
39                  * let parent know we failed and then exit to unblock parent
40                  * (but don't run atexit() stuff, which would screw up parent.)
41                  */
42                 failed = errno;
43                 _exit(0);
44         }
45         /* parent */
46         /* Unfortunately, this is not reliable: vfork()
47          * can be equivalent to fork() according to standards */
48         if (failed) {
49                 errno = failed;
50                 return -1;
51         }
52         return pid;
53 }
54
55 /* Die with an error message if we can't spawn a child process. */
56 pid_t xspawn(char **argv)
57 {
58         pid_t pid = spawn(argv);
59         if (pid < 0) bb_perror_msg_and_die("%s", *argv);
60         return pid;
61 }
62
63
64
65 #if 0 //ndef BB_NOMMU
66 // Die with an error message if we can't daemonize.
67 void xdaemon(int nochdir, int noclose)
68 {
69         if (daemon(nochdir, noclose))
70                 bb_perror_msg_and_die("daemon");
71 }
72 #endif
73
74 #if 0 // def BB_NOMMU
75 void vfork_daemon_rexec(int nochdir, int noclose, char **argv)
76 {
77         int fd;
78
79         /* Maybe we are already re-execed and come here again? */
80         if (re_execed)
81                 return;
82
83         setsid();
84
85         if (!nochdir)
86                 xchdir("/");
87
88         if (!noclose) {
89                 /* if "/dev/null" doesn't exist, bail out! */
90                 fd = xopen(bb_dev_null, O_RDWR);
91                 dup2(fd, STDIN_FILENO);
92                 dup2(fd, STDOUT_FILENO);
93                 dup2(fd, STDERR_FILENO);
94                 while (fd > 2)
95                         close(fd--);
96         }
97
98         switch (vfork()) {
99         case 0: /* child */
100                 /* Make certain we are not a session leader, or else we
101                  * might reacquire a controlling terminal */
102                 if (vfork())
103                         _exit(0);
104                 /* High-order bit of first char in argv[0] is a hidden
105                  * "we have (alrealy) re-execed, don't do it again" flag */
106                 argv[0][0] |= 0x80;
107                 execv(CONFIG_BUSYBOX_EXEC_PATH, argv);
108                 bb_perror_msg_and_die("exec %s", CONFIG_BUSYBOX_EXEC_PATH);
109         case -1: /* error */
110                 bb_perror_msg_and_die("vfork");
111         default: /* parent */
112                 exit(0);
113         }
114 }
115 #endif /* BB_NOMMU */
116
117 #ifdef BB_NOMMU
118 static void daemon_or_rexec(char **argv)
119 {
120         pid_t pid;
121         /* Maybe we are already re-execed and come here again? */
122         if (re_execed)
123                 return;
124
125         pid = vfork();
126         if (pid < 0) /* wtf? */
127                 bb_perror_msg_and_die("vfork");
128         if (pid) /* parent */
129                 exit(0);
130         /* child - re-exec ourself */
131         /* high-order bit of first char in argv[0] is a hidden
132          * "we have (alrealy) re-execed, don't do it again" flag */
133         argv[0][0] |= 0x80;
134         execv(CONFIG_BUSYBOX_EXEC_PATH, argv);
135         bb_perror_msg_and_die("exec %s", CONFIG_BUSYBOX_EXEC_PATH);
136 }
137 #else
138 static void daemon_or_rexec(void)
139 {
140         pid_t pid;
141         pid = fork();
142         if (pid < 0) /* wtf? */
143                 bb_perror_msg_and_die("fork");
144         if (pid) /* parent */
145                 exit(0);
146         /* child */
147 }
148 #define daemon_or_rexec(argv) daemon_or_rexec()
149 #endif
150
151
152 /* Due to a #define in libbb.h on MMU systems we actually have 1 argument -
153  * char **argv "vanishes" */
154 void bb_daemonize_or_rexec(int flags, char **argv)
155 {
156         int fd;
157
158         fd = xopen(bb_dev_null, O_RDWR);
159
160         if (flags & DAEMON_CHDIR_ROOT)
161                 xchdir("/");
162
163         if (flags & DAEMON_DEVNULL_STDIO) {
164                 close(0);
165                 close(1);
166                 close(2);
167         }
168
169         while ((unsigned)fd < 2)
170                 fd = dup(fd); /* have 0,1,2 open at least to /dev/null */
171
172         if (!(flags & DAEMON_ONLY_SANITIZE)) {
173                 daemon_or_rexec(argv);
174                 /* if daemonizing, make sure we detach from stdio */
175                 setsid();
176                 dup2(fd, 0);
177                 dup2(fd, 1);
178                 dup2(fd, 2);
179         }
180         if (fd > 2)
181                 close(fd--);
182         if (flags & DAEMON_CLOSE_EXTRA_FDS)
183                 while (fd > 2)
184                         close(fd--); /* close everything after fd#2 */
185 }
186
187 void bb_sanitize_stdio(void)
188 {
189         bb_daemonize_or_rexec(DAEMON_ONLY_SANITIZE, NULL);
190 }