blob: ec8b9b1d72d784c3f249e71c6347e55fe598ef52 [file] [log] [blame]
Bernhard Reutner-Fischer0a8812b2006-05-19 13:12:21 +00001/* vi: set sw=4 ts=4: */
Eric Andersen35e643b2003-07-28 07:40:39 +00002/*
Eric Andersenaff114c2004-04-14 17:51:38 +00003 * Rexec program for system have fork() as vfork() with foreground option
Russ Dilla1fece22003-12-15 21:57:44 +00004 *
5 * Copyright (C) Vladimir N. Oleynik <dzo@simtreas.ru>
6 * Copyright (C) 2003 Russ Dill <Russ.Dill@asu.edu>
7 *
Eric Andersenaff114c2004-04-14 17:51:38 +00008 * daemon() portion taken from uClibc:
Russ Dilla1fece22003-12-15 21:57:44 +00009 *
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 *
Bernhard Reutner-Fischer0a8812b2006-05-19 13:12:21 +000015 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
Eric Andersen35e643b2003-07-28 07:40:39 +000016 */
17
Russ Dilla1fece22003-12-15 21:57:44 +000018#include <paths.h>
Eric Andersen35e643b2003-07-28 07:40:39 +000019#include "libbb.h"
20
Denis Vlasenkobb7fcb42007-03-26 13:20:04 +000021/* 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. */
23pid_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;
Denis Vlasenkoafa70232007-03-26 17:25:33 +000043 _exit(111);
Denis Vlasenkobb7fcb42007-03-26 13:20:04 +000044 }
45 /* parent */
Denis Vlasenkoafa70232007-03-26 17:25:33 +000046 /* Unfortunately, this is not reliable: according to standards
47 * vfork() can be equivalent to fork() and we won't see value
48 * of 'failed'.
49 * Interested party can wait on pid and learn exit code.
50 * If 111 - then it (most probably) failed to exec */
Denis Vlasenkobb7fcb42007-03-26 13:20:04 +000051 if (failed) {
52 errno = failed;
53 return -1;
54 }
55 return pid;
56}
57
58/* Die with an error message if we can't spawn a child process. */
59pid_t xspawn(char **argv)
60{
61 pid_t pid = spawn(argv);
62 if (pid < 0) bb_perror_msg_and_die("%s", *argv);
63 return pid;
64}
65
66
67
68#if 0 //ndef BB_NOMMU
69// Die with an error message if we can't daemonize.
70void xdaemon(int nochdir, int noclose)
71{
72 if (daemon(nochdir, noclose))
73 bb_perror_msg_and_die("daemon");
74}
75#endif
76
77#if 0 // def BB_NOMMU
Denis Vlasenko9067f132007-03-24 12:11:17 +000078void vfork_daemon_rexec(int nochdir, int noclose, char **argv)
Eric Andersen35e643b2003-07-28 07:40:39 +000079{
Russ Dilla1fece22003-12-15 21:57:44 +000080 int fd;
Eric Andersenc7bda1c2004-03-15 08:29:22 +000081
Denis Vlasenkobb7fcb42007-03-26 13:20:04 +000082 /* Maybe we are already re-execed and come here again? */
83 if (re_execed)
84 return;
85
Russ Dilla1fece22003-12-15 21:57:44 +000086 setsid();
Eric Andersenc7bda1c2004-03-15 08:29:22 +000087
Russ Dilla1fece22003-12-15 21:57:44 +000088 if (!nochdir)
Bernhard Reutner-Fischerd591a362006-08-20 17:35:13 +000089 xchdir("/");
Russ Dilla1fece22003-12-15 21:57:44 +000090
Denis Vlasenko9067f132007-03-24 12:11:17 +000091 if (!noclose) {
92 /* if "/dev/null" doesn't exist, bail out! */
93 fd = xopen(bb_dev_null, O_RDWR);
Russ Dilla1fece22003-12-15 21:57:44 +000094 dup2(fd, STDIN_FILENO);
95 dup2(fd, STDOUT_FILENO);
96 dup2(fd, STDERR_FILENO);
Denis Vlasenko9af7c9d2007-01-19 21:19:35 +000097 while (fd > 2)
98 close(fd--);
Rob Landleyd9a761d2006-06-16 16:35:53 +000099 }
Eric Andersen35e643b2003-07-28 07:40:39 +0000100
Russ Dilla1fece22003-12-15 21:57:44 +0000101 switch (vfork()) {
102 case 0: /* child */
103 /* Make certain we are not a session leader, or else we
104 * might reacquire a controlling terminal */
105 if (vfork())
106 _exit(0);
Denis Vlasenko9067f132007-03-24 12:11:17 +0000107 /* High-order bit of first char in argv[0] is a hidden
108 * "we have (alrealy) re-execed, don't do it again" flag */
109 argv[0][0] |= 0x80;
110 execv(CONFIG_BUSYBOX_EXEC_PATH, argv);
111 bb_perror_msg_and_die("exec %s", CONFIG_BUSYBOX_EXEC_PATH);
Russ Dilla1fece22003-12-15 21:57:44 +0000112 case -1: /* error */
113 bb_perror_msg_and_die("vfork");
114 default: /* parent */
115 exit(0);
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000116 }
Eric Andersen35e643b2003-07-28 07:40:39 +0000117}
Bernhard Reutner-Fischerc418d482006-05-31 10:19:51 +0000118#endif /* BB_NOMMU */
Denis Vlasenkobb7fcb42007-03-26 13:20:04 +0000119
120#ifdef BB_NOMMU
Denis Vlasenko53091ec2007-03-26 13:35:09 +0000121void forkexit_or_rexec(char **argv)
Denis Vlasenkobb7fcb42007-03-26 13:20:04 +0000122{
123 pid_t pid;
124 /* Maybe we are already re-execed and come here again? */
125 if (re_execed)
126 return;
127
128 pid = vfork();
129 if (pid < 0) /* wtf? */
130 bb_perror_msg_and_die("vfork");
131 if (pid) /* parent */
132 exit(0);
133 /* child - re-exec ourself */
134 /* high-order bit of first char in argv[0] is a hidden
135 * "we have (alrealy) re-execed, don't do it again" flag */
136 argv[0][0] |= 0x80;
137 execv(CONFIG_BUSYBOX_EXEC_PATH, argv);
138 bb_perror_msg_and_die("exec %s", CONFIG_BUSYBOX_EXEC_PATH);
139}
140#else
Denis Vlasenko53091ec2007-03-26 13:35:09 +0000141/* Dance around (void)...*/
142#undef forkexit_or_rexec
143void forkexit_or_rexec(void)
Denis Vlasenkobb7fcb42007-03-26 13:20:04 +0000144{
145 pid_t pid;
146 pid = fork();
147 if (pid < 0) /* wtf? */
148 bb_perror_msg_and_die("fork");
149 if (pid) /* parent */
150 exit(0);
151 /* child */
152}
Denis Vlasenko53091ec2007-03-26 13:35:09 +0000153#define forkexit_or_rexec(argv) forkexit_or_rexec()
Denis Vlasenkobb7fcb42007-03-26 13:20:04 +0000154#endif
155
156
157/* Due to a #define in libbb.h on MMU systems we actually have 1 argument -
158 * char **argv "vanishes" */
159void bb_daemonize_or_rexec(int flags, char **argv)
160{
161 int fd;
162
163 fd = xopen(bb_dev_null, O_RDWR);
164
165 if (flags & DAEMON_CHDIR_ROOT)
166 xchdir("/");
167
168 if (flags & DAEMON_DEVNULL_STDIO) {
169 close(0);
170 close(1);
171 close(2);
172 }
173
174 while ((unsigned)fd < 2)
175 fd = dup(fd); /* have 0,1,2 open at least to /dev/null */
176
177 if (!(flags & DAEMON_ONLY_SANITIZE)) {
Denis Vlasenko53091ec2007-03-26 13:35:09 +0000178 forkexit_or_rexec(argv);
Denis Vlasenkobb7fcb42007-03-26 13:20:04 +0000179 /* if daemonizing, make sure we detach from stdio */
180 setsid();
181 dup2(fd, 0);
182 dup2(fd, 1);
183 dup2(fd, 2);
184 }
185 if (fd > 2)
186 close(fd--);
187 if (flags & DAEMON_CLOSE_EXTRA_FDS)
188 while (fd > 2)
189 close(fd--); /* close everything after fd#2 */
190}
191
192void bb_sanitize_stdio(void)
193{
194 bb_daemonize_or_rexec(DAEMON_ONLY_SANITIZE, NULL);
195}