diff options
author | Ron Yorston <rmy@pobox.com> | 2017-08-22 14:56:12 +0100 |
---|---|---|
committer | Ron Yorston <rmy@pobox.com> | 2017-08-22 14:56:12 +0100 |
commit | ce9af1cc5ea23f754587448cf35b5120c77bfeef (patch) | |
tree | 69e5eaba5e75ab909ed92d5045393471b8ff3c13 /libbb/vfork_daemon_rexec.c | |
parent | c170026700eabb10147dd848c45c06995b43a32e (diff) | |
parent | e837a0dbbebf4229306df98fe9ee3b9bb30630c4 (diff) | |
download | busybox-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.c | 191 |
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. */ | ||
24 | pid_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) |
66 | pid_t FAST_FUNC xspawn(char **argv) | 32 | void 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 | ||
76 | static jmp_buf die_jmp; | 43 | static jmp_buf die_jmp; |
77 | static void jump(void) | 44 | static 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 | }; |
99 | static void save_nofork_data(struct nofork_save_area *save) | 68 | static 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 | } |
107 | static void restore_nofork_data(struct nofork_save_area *save) | 77 | static 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 | ||
131 | void 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. */ | ||
157 | pid_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. */ | ||
199 | pid_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 | ||
159 | int FAST_FUNC spawn_and_wait(char **argv) | 207 | int 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) { |