aboutsummaryrefslogtreecommitdiff
path: root/libbb/vfork_daemon_rexec.c
diff options
context:
space:
mode:
authorRon Yorston <rmy@pobox.com>2017-08-22 14:56:12 +0100
committerRon Yorston <rmy@pobox.com>2017-08-22 14:56:12 +0100
commitce9af1cc5ea23f754587448cf35b5120c77bfeef (patch)
tree69e5eaba5e75ab909ed92d5045393471b8ff3c13 /libbb/vfork_daemon_rexec.c
parentc170026700eabb10147dd848c45c06995b43a32e (diff)
parente837a0dbbebf4229306df98fe9ee3b9bb30630c4 (diff)
downloadbusybox-w32-ce9af1cc5ea23f754587448cf35b5120c77bfeef.tar.gz
busybox-w32-ce9af1cc5ea23f754587448cf35b5120c77bfeef.tar.bz2
busybox-w32-ce9af1cc5ea23f754587448cf35b5120c77bfeef.zip
Merge branch 'busybox' into merge
Diffstat (limited to 'libbb/vfork_daemon_rexec.c')
-rw-r--r--libbb/vfork_daemon_rexec.c191
1 files changed, 118 insertions, 73 deletions
diff --git a/libbb/vfork_daemon_rexec.c b/libbb/vfork_daemon_rexec.c
index 4b3ed5a3b..eca2fabf5 100644
--- a/libbb/vfork_daemon_rexec.c
+++ b/libbb/vfork_daemon_rexec.c
@@ -14,78 +14,46 @@
14 * 14 *
15 * Licensed under GPLv2 or later, see file LICENSE in this source tree. 15 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
16 */ 16 */
17#include <sys/prctl.h>
18#ifndef PR_SET_NAME
19#define PR_SET_NAME 15
20#endif
21#ifndef PR_GET_NAME
22#define PR_GET_NAME 16
23#endif
17 24
18#include "busybox.h" /* uses applet tables */ 25#include "busybox.h" /* uses applet tables */
19#include "NUM_APPLETS.h" 26#include "NUM_APPLETS.h"
20 27
21#if !ENABLE_PLATFORM_MINGW32 28#define NOFORK_SUPPORT ((NUM_APPLETS > 1) && (ENABLE_FEATURE_PREFER_APPLETS || ENABLE_FEATURE_SH_NOFORK))
22/* This does a fork/exec in one call, using vfork(). Returns PID of new child, 29#define NOEXEC_SUPPORT ((NUM_APPLETS > 1) && (ENABLE_FEATURE_PREFER_APPLETS || ENABLE_FEATURE_SH_STANDALONE))
23 * -1 for failure. Runs argv[0], searching path if that has no / in it. */
24pid_t FAST_FUNC spawn(char **argv)
25{
26 /* Compiler should not optimize stores here */
27 volatile int failed;
28 pid_t pid;
29
30 fflush_all();
31
32 /* Be nice to nommu machines. */
33 failed = 0;
34 pid = vfork();
35 if (pid < 0) /* error */
36 return pid;
37 if (!pid) { /* child */
38 /* This macro is ok - it doesn't do NOEXEC/NOFORK tricks */
39 BB_EXECVP(argv[0], argv);
40
41 /* We are (maybe) sharing a stack with blocked parent,
42 * let parent know we failed and then exit to unblock parent
43 * (but don't run atexit() stuff, which would screw up parent.)
44 */
45 failed = errno;
46 /* mount, for example, does not want the message */
47 /*bb_perror_msg("can't execute '%s'", argv[0]);*/
48 _exit(111);
49 }
50 /* parent */
51 /* Unfortunately, this is not reliable: according to standards
52 * vfork() can be equivalent to fork() and we won't see value
53 * of 'failed'.
54 * Interested party can wait on pid and learn exit code.
55 * If 111 - then it (most probably) failed to exec */
56 if (failed) {
57 safe_waitpid(pid, NULL, 0); /* prevent zombie */
58 errno = failed;
59 return -1;
60 }
61 return pid;
62}
63#endif
64 30
65/* Die with an error message if we can't spawn a child process. */ 31#if defined(__linux__) && (NUM_APPLETS > 1)
66pid_t FAST_FUNC xspawn(char **argv) 32void FAST_FUNC set_task_comm(const char *comm)
67{ 33{
68 pid_t pid = spawn(argv); 34 /* okay if too long (truncates) */
69 if (pid < 0) 35 prctl(PR_SET_NAME, (long)comm, 0, 0, 0);
70 bb_simple_perror_msg_and_die(*argv);
71 return pid;
72} 36}
37#endif
73 38
74#if ENABLE_FEATURE_PREFER_APPLETS \ 39/*
75 || ENABLE_FEATURE_SH_NOFORK 40 * NOFORK/NOEXEC support
41 */
42#if NOFORK_SUPPORT
76static jmp_buf die_jmp; 43static jmp_buf die_jmp;
77static void jump(void) 44static void jump(void)
78{ 45{
79 /* Special case. We arrive here if NOFORK applet 46 /* Special case. We arrive here if NOFORK applet
80 * calls xfunc, which then decides to die. 47 * calls xfunc, which then decides to die.
81 * We don't die, but jump instead back to caller. 48 * We don't die, but instead jump back to caller.
82 * NOFORK applets still cannot carelessly call xfuncs: 49 * NOFORK applets still cannot carelessly call xfuncs:
83 * p = xmalloc(10); 50 * p = xmalloc(10);
84 * q = xmalloc(10); // BUG! if this dies, we leak p! 51 * q = xmalloc(10); // BUG! if this dies, we leak p!
85 */ 52 */
86 /* | 0x100 allows to pass zero exitcode (longjmp can't pass 0). 53 /* | 0x100 allows to pass zero exitcode (longjmp can't pass 0).
87 * This works because exitcodes are bytes, 54 * This works because exitcodes are bytes,
88 * run_nofork_applet() ensures that by "& 0xff" */ 55 * run_nofork_applet() ensures that by "& 0xff"
56 */
89 longjmp(die_jmp, xfunc_error_retval | 0x100); 57 longjmp(die_jmp, xfunc_error_retval | 0x100);
90} 58}
91 59
@@ -94,6 +62,7 @@ struct nofork_save_area {
94 void (*die_func)(void); 62 void (*die_func)(void);
95 const char *applet_name; 63 const char *applet_name;
96 uint32_t option_mask32; 64 uint32_t option_mask32;
65 smallint logmode;
97 uint8_t xfunc_error_retval; 66 uint8_t xfunc_error_retval;
98}; 67};
99static void save_nofork_data(struct nofork_save_area *save) 68static void save_nofork_data(struct nofork_save_area *save)
@@ -102,6 +71,7 @@ static void save_nofork_data(struct nofork_save_area *save)
102 save->die_func = die_func; 71 save->die_func = die_func;
103 save->applet_name = applet_name; 72 save->applet_name = applet_name;
104 save->option_mask32 = option_mask32; 73 save->option_mask32 = option_mask32;
74 save->logmode = logmode;
105 save->xfunc_error_retval = xfunc_error_retval; 75 save->xfunc_error_retval = xfunc_error_retval;
106} 76}
107static void restore_nofork_data(struct nofork_save_area *save) 77static void restore_nofork_data(struct nofork_save_area *save)
@@ -110,6 +80,7 @@ static void restore_nofork_data(struct nofork_save_area *save)
110 die_func = save->die_func; 80 die_func = save->die_func;
111 applet_name = save->applet_name; 81 applet_name = save->applet_name;
112 option_mask32 = save->option_mask32; 82 option_mask32 = save->option_mask32;
83 logmode = save->logmode;
113 xfunc_error_retval = save->xfunc_error_retval; 84 xfunc_error_retval = save->xfunc_error_retval;
114} 85}
115 86
@@ -120,16 +91,15 @@ int FAST_FUNC run_nofork_applet(int applet_no, char **argv)
120 91
121 save_nofork_data(&old); 92 save_nofork_data(&old);
122 93
94 logmode = LOGMODE_STDIO;
123 xfunc_error_retval = EXIT_FAILURE; 95 xfunc_error_retval = EXIT_FAILURE;
124 96 /* In case getopt() was already called:
125 /* In case getopt() or getopt32() was already called:
126 * reset the libc getopt() function, which keeps internal state. 97 * reset the libc getopt() function, which keeps internal state.
98 * (getopt32() does it itself, but getopt() doesn't (and can't))
127 */ 99 */
128 GETOPT_RESET(); 100 GETOPT_RESET();
129 101
130 argc = 1; 102 argc = string_array_len(argv);
131 while (argv[argc])
132 argc++;
133 103
134 /* If xfunc "dies" in NOFORK applet, die_func longjmp's here instead */ 104 /* If xfunc "dies" in NOFORK applet, die_func longjmp's here instead */
135 die_func = jump; 105 die_func = jump;
@@ -142,19 +112,97 @@ int FAST_FUNC run_nofork_applet(int applet_no, char **argv)
142 applet_name = tmp_argv[0]; 112 applet_name = tmp_argv[0];
143 /* Finally we can call NOFORK applet's main() */ 113 /* Finally we can call NOFORK applet's main() */
144 rc = applet_main[applet_no](argc, tmp_argv); 114 rc = applet_main[applet_no](argc, tmp_argv);
115 /* Important for shells: `which CMD` was failing */
116 fflush_all();
145 } else { 117 } else {
146 /* xfunc died in NOFORK applet */ 118 /* xfunc died in NOFORK applet */
147 } 119 }
148 120
149 /* Restoring some globals */ 121 /* Restoring some globals */
150 restore_nofork_data(&old); 122 restore_nofork_data(&old);
151
152 /* Other globals can be simply reset to defaults */ 123 /* Other globals can be simply reset to defaults */
153 GETOPT_RESET(); 124 GETOPT_RESET();
154 125
155 return rc & 0xff; /* don't confuse people with "exitcodes" >255 */ 126 return rc & 0xff; /* don't confuse people with "exitcodes" >255 */
156} 127}
157#endif /* FEATURE_PREFER_APPLETS || FEATURE_SH_NOFORK */ 128#endif
129
130#if NOEXEC_SUPPORT
131void FAST_FUNC run_noexec_applet_and_exit(int a, const char *name, char **argv)
132{
133 /* reset some state and run without execing */
134 /* msg_eol = "\n"; - no caller needs this reinited yet */
135 logmode = LOGMODE_STDIO;
136 xfunc_error_retval = EXIT_FAILURE;
137 die_func = NULL;
138 GETOPT_RESET();
139
140//TODO: think pidof, pgrep, pkill!
141//set_task_comm() makes our pidof find NOEXECs (e.g. "yes >/dev/null"),
142//but one from procps-ng-3.3.10 needs more!
143//Rewrite /proc/PID/cmdline? (need to save argv0 and length at init for this to work!)
144 set_task_comm(name);
145 /* applet_name is set by this function: */
146 run_applet_no_and_exit(a, name, argv);
147}
148#endif
149
150/*
151 * Higher-level code, hiding optional NOFORK/NOEXEC trickery.
152 */
153
154#if !ENABLE_PLATFORM_MINGW32
155/* This does a fork/exec in one call, using vfork(). Returns PID of new child,
156 * -1 for failure. Runs argv[0], searching path if that has no / in it. */
157pid_t FAST_FUNC spawn(char **argv)
158{
159 /* Compiler should not optimize stores here */
160 volatile int failed;
161 pid_t pid;
162
163 fflush_all();
164
165 /* Be nice to nommu machines. */
166 failed = 0;
167 pid = vfork();
168 if (pid < 0) /* error */
169 return pid;
170 if (!pid) { /* child */
171 /* This macro is ok - it doesn't do NOEXEC/NOFORK tricks */
172 BB_EXECVP(argv[0], argv);
173
174 /* We are (maybe) sharing a stack with blocked parent,
175 * let parent know we failed and then exit to unblock parent
176 * (but don't run atexit() stuff, which would screw up parent.)
177 */
178 failed = errno;
179 /* mount, for example, does not want the message */
180 /*bb_perror_msg("can't execute '%s'", argv[0]);*/
181 _exit(111);
182 }
183 /* parent */
184 /* Unfortunately, this is not reliable: according to standards
185 * vfork() can be equivalent to fork() and we won't see value
186 * of 'failed'.
187 * Interested party can wait on pid and learn exit code.
188 * If 111 - then it (most probably) failed to exec */
189 if (failed) {
190 safe_waitpid(pid, NULL, 0); /* prevent zombie */
191 errno = failed;
192 return -1;
193 }
194 return pid;
195}
196#endif
197
198/* Die with an error message if we can't spawn a child process. */
199pid_t FAST_FUNC xspawn(char **argv)
200{
201 pid_t pid = spawn(argv);
202 if (pid < 0)
203 bb_simple_perror_msg_and_die(*argv);
204 return pid;
205}
158 206
159int FAST_FUNC spawn_and_wait(char **argv) 207int FAST_FUNC spawn_and_wait(char **argv)
160{ 208{
@@ -174,21 +222,12 @@ int FAST_FUNC spawn_and_wait(char **argv)
174 return wait4pid(rc); 222 return wait4pid(rc);
175 223
176 /* child */ 224 /* child */
177 /* reset some state and run without execing */ 225 run_noexec_applet_and_exit(a, argv[0], argv);
178
179 /* msg_eol = "\n"; - no caller needs this reinited yet */
180 logmode = LOGMODE_STDIO;
181 /* die_func = NULL; - needed if the caller is a shell,
182 * init, or a NOFORK applet. But none of those call us
183 * as of yet (and that should probably always stay true).
184 */
185 /* xfunc_error_retval and applet_name are init by: */
186 run_applet_no_and_exit(a, argv[0], argv);
187 } 226 }
188# endif 227# endif
189# endif 228# endif
190 } 229 }
191#endif /* FEATURE_PREFER_APPLETS */ 230#endif
192 rc = spawn(argv); 231 rc = spawn(argv);
193 return wait4pid(rc); 232 return wait4pid(rc);
194} 233}
@@ -209,6 +248,9 @@ pid_t FAST_FUNC fork_or_rexec(char **argv)
209 /* Maybe we are already re-execed and come here again? */ 248 /* Maybe we are already re-execed and come here again? */
210 if (re_execed) 249 if (re_execed)
211 return 0; 250 return 0;
251
252 /* fflush_all(); ? - so far all callers had no buffered output to flush */
253
212 pid = xvfork(); 254 pid = xvfork();
213 if (pid) /* parent */ 255 if (pid) /* parent */
214 return pid; 256 return pid;
@@ -245,8 +287,11 @@ void FAST_FUNC bb_daemonize_or_rexec(int flags, char **argv)
245 fd = dup(fd); /* have 0,1,2 open at least to /dev/null */ 287 fd = dup(fd); /* have 0,1,2 open at least to /dev/null */
246 288
247 if (!(flags & DAEMON_ONLY_SANITIZE)) { 289 if (!(flags & DAEMON_ONLY_SANITIZE)) {
290
291 /* fflush_all(); - add it in fork_or_rexec() if necessary */
292
248 if (fork_or_rexec(argv)) 293 if (fork_or_rexec(argv))
249 exit(EXIT_SUCCESS); /* parent */ 294 _exit(EXIT_SUCCESS); /* parent */
250 /* if daemonizing, detach from stdio & ctty */ 295 /* if daemonizing, detach from stdio & ctty */
251 setsid(); 296 setsid();
252 dup2(fd, 0); 297 dup2(fd, 0);
@@ -258,7 +303,7 @@ void FAST_FUNC bb_daemonize_or_rexec(int flags, char **argv)
258 * Prevent this: stop being a session leader. 303 * Prevent this: stop being a session leader.
259 */ 304 */
260 if (fork_or_rexec(argv)) 305 if (fork_or_rexec(argv))
261 exit(EXIT_SUCCESS); /* parent */ 306 _exit(EXIT_SUCCESS); /* parent */
262 } 307 }
263 } 308 }
264 while (fd > 2) { 309 while (fd > 2) {