source: MondoRescue/branches/3.3/mindi-busybox/libbb/vfork_daemon_rexec.c@ 3901

Last change on this file since 3901 was 3621, checked in by Bruno Cornec, 10 years ago

New 3?3 banch for incorporation of latest busybox 1.25. Changing minor version to handle potential incompatibilities.

File size: 7.7 KB
RevLine 
[821]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 *
[2725]15 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
[821]16 */
17
[2725]18#include "busybox.h" /* uses applet tables */
[821]19
[1765]20/* This does a fork/exec in one call, using vfork(). Returns PID of new child,
21 * -1 for failure. Runs argv[0], searching path if that has no / in it. */
[2725]22pid_t FAST_FUNC spawn(char **argv)
[1765]23{
24 /* Compiler should not optimize stores here */
25 volatile int failed;
26 pid_t pid;
[821]27
[2725]28 fflush_all();
[1765]29
30 /* Be nice to nommu machines. */
31 failed = 0;
32 pid = vfork();
33 if (pid < 0) /* error */
34 return pid;
35 if (!pid) { /* child */
36 /* This macro is ok - it doesn't do NOEXEC/NOFORK tricks */
37 BB_EXECVP(argv[0], argv);
38
39 /* We are (maybe) sharing a stack with blocked parent,
40 * let parent know we failed and then exit to unblock parent
41 * (but don't run atexit() stuff, which would screw up parent.)
42 */
43 failed = errno;
[2725]44 /* mount, for example, does not want the message */
45 /*bb_perror_msg("can't execute '%s'", argv[0]);*/
[1765]46 _exit(111);
47 }
48 /* parent */
49 /* Unfortunately, this is not reliable: according to standards
50 * vfork() can be equivalent to fork() and we won't see value
51 * of 'failed'.
52 * Interested party can wait on pid and learn exit code.
53 * If 111 - then it (most probably) failed to exec */
54 if (failed) {
[2725]55 safe_waitpid(pid, NULL, 0); /* prevent zombie */
[1765]56 errno = failed;
57 return -1;
58 }
59 return pid;
60}
61
62/* Die with an error message if we can't spawn a child process. */
[2725]63pid_t FAST_FUNC xspawn(char **argv)
[821]64{
[1765]65 pid_t pid = spawn(argv);
66 if (pid < 0)
[2725]67 bb_simple_perror_msg_and_die(*argv);
[1765]68 return pid;
69}
[821]70
[1765]71#if ENABLE_FEATURE_PREFER_APPLETS
[3621]72static jmp_buf die_jmp;
73static void jump(void)
74{
75 /* Special case. We arrive here if NOFORK applet
76 * calls xfunc, which then decides to die.
77 * We don't die, but jump instead back to caller.
78 * NOFORK applets still cannot carelessly call xfuncs:
79 * p = xmalloc(10);
80 * q = xmalloc(10); // BUG! if this dies, we leak p!
81 */
82 /* | 0x100 allows to pass zero exitcode (longjmp can't pass 0).
83 * This works because exitcodes are bytes,
84 * run_nofork_applet() ensures that by "& 0xff" */
85 longjmp(die_jmp, xfunc_error_retval | 0x100);
86}
87
[3232]88struct nofork_save_area {
89 jmp_buf die_jmp;
[3621]90 void (*die_func)(void);
[3232]91 const char *applet_name;
92 uint32_t option_mask32;
93 uint8_t xfunc_error_retval;
94};
95static void save_nofork_data(struct nofork_save_area *save)
[1765]96{
97 memcpy(&save->die_jmp, &die_jmp, sizeof(die_jmp));
[3621]98 save->die_func = die_func;
[2725]99 save->applet_name = applet_name;
[3621]100 save->option_mask32 = option_mask32;
[1765]101 save->xfunc_error_retval = xfunc_error_retval;
102}
[3232]103static void restore_nofork_data(struct nofork_save_area *save)
[1765]104{
105 memcpy(&die_jmp, &save->die_jmp, sizeof(die_jmp));
[3621]106 die_func = save->die_func;
[2725]107 applet_name = save->applet_name;
[3621]108 option_mask32 = save->option_mask32;
[1765]109 xfunc_error_retval = save->xfunc_error_retval;
110}
111
[3232]112int FAST_FUNC run_nofork_applet(int applet_no, char **argv)
[1765]113{
114 int rc, argc;
[3232]115 struct nofork_save_area old;
[1765]116
[3232]117 save_nofork_data(&old);
118
[1765]119 xfunc_error_retval = EXIT_FAILURE;
[2725]120
121 /* In case getopt() or getopt32() was already called:
122 * reset the libc getopt() function, which keeps internal state.
123 *
124 * BSD-derived getopt() functions require that optind be set to 1 in
125 * order to reset getopt() state. This used to be generally accepted
126 * way of resetting getopt(). However, glibc's getopt()
127 * has additional getopt() state beyond optind, and requires that
128 * optind be set to zero to reset its state. So the unfortunate state of
129 * affairs is that BSD-derived versions of getopt() misbehave if
130 * optind is set to 0 in order to reset getopt(), and glibc's getopt()
131 * will core dump if optind is set 1 in order to reset getopt().
132 *
133 * More modern versions of BSD require that optreset be set to 1 in
134 * order to reset getopt(). Sigh. Standards, anyone?
135 */
136#ifdef __GLIBC__
137 optind = 0;
138#else /* BSD style */
139 optind = 1;
140 /* optreset = 1; */
141#endif
142 /* optarg = NULL; opterr = 1; optopt = 63; - do we need this too? */
143 /* (values above are what they initialized to in glibc and uclibc) */
144 /* option_mask32 = 0; - not needed, no applet depends on it being 0 */
145
[1765]146 argc = 1;
147 while (argv[argc])
148 argc++;
149
[3621]150 /* If xfunc "dies" in NOFORK applet, die_func longjmp's here instead */
151 die_func = jump;
[1765]152 rc = setjmp(die_jmp);
153 if (!rc) {
154 /* Some callers (xargs)
155 * need argv untouched because they free argv[i]! */
156 char *tmp_argv[argc+1];
157 memcpy(tmp_argv, argv, (argc+1) * sizeof(tmp_argv[0]));
[3621]158 applet_name = tmp_argv[0];
[1765]159 /* Finally we can call NOFORK applet's main() */
[2725]160 rc = applet_main[applet_no](argc, tmp_argv);
[3621]161 } else {
162 /* xfunc died in NOFORK applet */
[821]163 }
164
[2725]165 /* Restoring some globals */
[3232]166 restore_nofork_data(&old);
[2725]167
168 /* Other globals can be simply reset to defaults */
169#ifdef __GLIBC__
170 optind = 0;
171#else /* BSD style */
172 optind = 1;
173#endif
174
175 return rc & 0xff; /* don't confuse people with "exitcodes" >255 */
[1765]176}
177#endif /* FEATURE_PREFER_APPLETS */
178
[2725]179int FAST_FUNC spawn_and_wait(char **argv)
[1765]180{
181 int rc;
182#if ENABLE_FEATURE_PREFER_APPLETS
[2725]183 int a = find_applet_by_name(argv[0]);
[1765]184
[2725]185 if (a >= 0 && (APPLET_IS_NOFORK(a)
[3232]186# if BB_MMU
[2725]187 || APPLET_IS_NOEXEC(a) /* NOEXEC trick needs fork() */
[3232]188# endif
[1765]189 )) {
[3232]190# if BB_MMU
[2725]191 if (APPLET_IS_NOFORK(a))
[3232]192# endif
[1765]193 {
194 return run_nofork_applet(a, argv);
195 }
[3232]196# if BB_MMU
[1765]197 /* MMU only */
198 /* a->noexec is true */
199 rc = fork();
200 if (rc) /* parent or error */
201 return wait4pid(rc);
202 /* child */
203 xfunc_error_retval = EXIT_FAILURE;
[2725]204 run_applet_no_and_exit(a, argv);
[3232]205# endif
[821]206 }
[1765]207#endif /* FEATURE_PREFER_APPLETS */
208 rc = spawn(argv);
209 return wait4pid(rc);
210}
211
212#if !BB_MMU
[2725]213void FAST_FUNC re_exec(char **argv)
[1765]214{
215 /* high-order bit of first char in argv[0] is a hidden
216 * "we have (already) re-execed, don't do it again" flag */
217 argv[0][0] |= 0x80;
218 execv(bb_busybox_exec_path, argv);
[2725]219 bb_perror_msg_and_die("can't execute '%s'", bb_busybox_exec_path);
[1765]220}
221
[2725]222pid_t FAST_FUNC fork_or_rexec(char **argv)
[1765]223{
224 pid_t pid;
225 /* Maybe we are already re-execed and come here again? */
226 if (re_execed)
[2725]227 return 0;
228 pid = xvfork();
[1765]229 if (pid) /* parent */
[2725]230 return pid;
[1765]231 /* child - re-exec ourself */
232 re_exec(argv);
233}
234#endif
235
236/* Due to a #define in libbb.h on MMU systems we actually have 1 argument -
237 * char **argv "vanishes" */
[2725]238void FAST_FUNC bb_daemonize_or_rexec(int flags, char **argv)
[1765]239{
240 int fd;
241
242 if (flags & DAEMON_CHDIR_ROOT)
243 xchdir("/");
244
245 if (flags & DAEMON_DEVNULL_STDIO) {
246 close(0);
247 close(1);
248 close(2);
[821]249 }
[1765]250
[2725]251 fd = open(bb_dev_null, O_RDWR);
252 if (fd < 0) {
253 /* NB: we can be called as bb_sanitize_stdio() from init
254 * or mdev, and there /dev/null may legitimately not (yet) exist!
255 * Do not use xopen above, but obtain _ANY_ open descriptor,
256 * even bogus one as below. */
257 fd = xopen("/", O_RDONLY); /* don't believe this can fail */
258 }
[1765]259
260 while ((unsigned)fd < 2)
261 fd = dup(fd); /* have 0,1,2 open at least to /dev/null */
262
263 if (!(flags & DAEMON_ONLY_SANITIZE)) {
[2725]264 if (fork_or_rexec(argv))
265 exit(EXIT_SUCCESS); /* parent */
[3232]266 /* if daemonizing, detach from stdio & ctty */
[1765]267 setsid();
268 dup2(fd, 0);
269 dup2(fd, 1);
270 dup2(fd, 2);
[3232]271 if (flags & DAEMON_DOUBLE_FORK) {
272 /* On Linux, session leader can acquire ctty
273 * unknowingly, by opening a tty.
274 * Prevent this: stop being a session leader.
275 */
276 if (fork_or_rexec(argv))
277 exit(EXIT_SUCCESS); /* parent */
278 }
[1765]279 }
280 while (fd > 2) {
281 close(fd--);
282 if (!(flags & DAEMON_CLOSE_EXTRA_FDS))
283 return;
284 /* else close everything after fd#2 */
285 }
[821]286}
[1765]287
[2725]288void FAST_FUNC bb_sanitize_stdio(void)
[1765]289{
290 bb_daemonize_or_rexec(DAEMON_ONLY_SANITIZE, NULL);
291}
Note: See TracBrowser for help on using the repository browser.