diff options
Diffstat (limited to 'win32/process.c')
-rw-r--r-- | win32/process.c | 999 |
1 files changed, 999 insertions, 0 deletions
diff --git a/win32/process.c b/win32/process.c new file mode 100644 index 000000000..33f45ee42 --- /dev/null +++ b/win32/process.c | |||
@@ -0,0 +1,999 @@ | |||
1 | #include "libbb.h" | ||
2 | #include <tlhelp32.h> | ||
3 | #include <psapi.h> | ||
4 | #include "lazyload.h" | ||
5 | #include "NUM_APPLETS.h" | ||
6 | |||
7 | pid_t waitpid(pid_t pid, int *status, int options) | ||
8 | #if ENABLE_TIME | ||
9 | { | ||
10 | return mingw_wait3(pid, status, options, NULL); | ||
11 | } | ||
12 | #endif | ||
13 | |||
14 | #if ENABLE_TIME | ||
15 | pid_t mingw_wait3(pid_t pid, int *status, int options, struct rusage *rusage) | ||
16 | #endif | ||
17 | { | ||
18 | HANDLE proc; | ||
19 | DWORD code; | ||
20 | |||
21 | /* Windows does not understand parent-child */ | ||
22 | if (pid > 0 && options == 0) { | ||
23 | if ( (proc=OpenProcess(SYNCHRONIZE|PROCESS_QUERY_INFORMATION, | ||
24 | FALSE, pid)) != NULL ) { | ||
25 | WaitForSingleObject(proc, INFINITE); | ||
26 | GetExitCodeProcess(proc, &code); | ||
27 | #if ENABLE_TIME | ||
28 | if (rusage != NULL) { | ||
29 | FILETIME crTime, exTime, keTime, usTime; | ||
30 | |||
31 | memset(rusage, 0, sizeof(*rusage)); | ||
32 | if (GetProcessTimes(proc, &crTime, &exTime, &keTime, &usTime)) { | ||
33 | uint64_t kernel_usec = | ||
34 | (((uint64_t)keTime.dwHighDateTime << 32) | ||
35 | | (uint64_t)keTime.dwLowDateTime)/10; | ||
36 | uint64_t user_usec = | ||
37 | (((uint64_t)usTime.dwHighDateTime << 32) | ||
38 | | (uint64_t)usTime.dwLowDateTime)/10; | ||
39 | |||
40 | rusage->ru_utime.tv_sec = user_usec / 1000000U; | ||
41 | rusage->ru_utime.tv_usec = user_usec % 1000000U; | ||
42 | rusage->ru_stime.tv_sec = kernel_usec / 1000000U; | ||
43 | rusage->ru_stime.tv_usec = kernel_usec % 1000000U; | ||
44 | } | ||
45 | } | ||
46 | #endif | ||
47 | CloseHandle(proc); | ||
48 | *status = exit_code_to_wait_status(code); | ||
49 | return pid; | ||
50 | } | ||
51 | } | ||
52 | errno = pid < 0 ? ENOSYS : EINVAL; | ||
53 | return -1; | ||
54 | } | ||
55 | |||
56 | int FAST_FUNC | ||
57 | parse_interpreter(const char *cmd, interp_t *interp) | ||
58 | { | ||
59 | char *path, *t; | ||
60 | int n; | ||
61 | |||
62 | while (TRUE) { | ||
63 | n = open_read_close(cmd, interp->buf, sizeof(interp->buf)-1); | ||
64 | if (n < 4) /* at least '#!/x' and not error */ | ||
65 | break; | ||
66 | |||
67 | /* | ||
68 | * See http://www.in-ulm.de/~mascheck/various/shebang/ for trivia | ||
69 | * relating to '#!'. See also https://lwn.net/Articles/630727/ | ||
70 | * for Linux-specific details. | ||
71 | */ | ||
72 | if (interp->buf[0] != '#' || interp->buf[1] != '!') | ||
73 | break; | ||
74 | interp->buf[n] = '\0'; | ||
75 | if ((t=strchr(interp->buf, '\n')) == NULL) | ||
76 | break; | ||
77 | t[1] = '\0'; | ||
78 | |||
79 | if ((path=strtok(interp->buf+2, " \t\r\n")) == NULL) | ||
80 | break; | ||
81 | |||
82 | t = (char *)bb_basename(path); | ||
83 | if (*t == '\0') | ||
84 | break; | ||
85 | |||
86 | interp->path = path; | ||
87 | interp->name = t; | ||
88 | interp->opts = strtok(NULL, "\r\n"); | ||
89 | /* Trim leading and trailing whitespace from the options. | ||
90 | * If the resulting string is empty return a NULL pointer. */ | ||
91 | if (interp->opts && trim(interp->opts) == interp->opts) | ||
92 | interp->opts = NULL; | ||
93 | return 1; | ||
94 | } | ||
95 | |||
96 | if (n >= 0 && is_suffixed_with_case(cmd, ".sh")) { | ||
97 | interp->path = (char *)DEFAULT_SHELL; | ||
98 | interp->name = (char *)DEFAULT_SHELL_SHORT_NAME; | ||
99 | interp->opts = NULL; | ||
100 | return 1; | ||
101 | } | ||
102 | return 0; | ||
103 | } | ||
104 | |||
105 | /* | ||
106 | * See https://docs.microsoft.com/en-us/cpp/cpp/main-function-command-line-args?view=vs-2019#parsing-c-command-line-arguments | ||
107 | * (Parsing C++ Command-Line Arguments) | ||
108 | */ | ||
109 | char * FAST_FUNC | ||
110 | quote_arg(const char *arg) | ||
111 | { | ||
112 | char *d, *r = xmalloc(2 * strlen(arg) + 3); // max-esc, quotes, \0 | ||
113 | size_t nbs = 0; // consecutive backslashes before current char | ||
114 | int quoted = !*arg; | ||
115 | |||
116 | for (d = r; *arg; *d++ = *arg++) { | ||
117 | if (*arg == ' ' || *arg == '\t') | ||
118 | quoted = 1; | ||
119 | |||
120 | if (*arg == '\\' || *arg == '"') | ||
121 | *d++ = '\\'; | ||
122 | else | ||
123 | d -= nbs; // undo nbs escapes, if any (not followed by DQ) | ||
124 | |||
125 | if (*arg == '\\') | ||
126 | ++nbs; | ||
127 | else | ||
128 | nbs = 0; | ||
129 | } | ||
130 | |||
131 | if (quoted) { | ||
132 | memmove(r + 1, r, d++ - r); | ||
133 | *r = *d++ = '"'; | ||
134 | } else { | ||
135 | d -= nbs; | ||
136 | } | ||
137 | |||
138 | *d = 0; | ||
139 | return r; | ||
140 | } | ||
141 | |||
142 | char * FAST_FUNC | ||
143 | find_first_executable(const char *name) | ||
144 | { | ||
145 | const char *path = getenv("PATH"); | ||
146 | return find_executable(name, &path); | ||
147 | } | ||
148 | |||
149 | static intptr_t | ||
150 | spawnveq(int mode, const char *path, char *const *argv, char *const *env) | ||
151 | { | ||
152 | char **new_argv; | ||
153 | char *new_path = NULL; | ||
154 | int i, argc; | ||
155 | intptr_t ret; | ||
156 | struct stat st; | ||
157 | size_t len = 0; | ||
158 | |||
159 | /* | ||
160 | * Require that the file exists, is a regular file and is executable. | ||
161 | * It may still contain garbage but we let spawnve deal with that. | ||
162 | */ | ||
163 | if (stat(path, &st) == 0) { | ||
164 | if (!S_ISREG(st.st_mode) || !(st.st_mode&S_IXUSR)) { | ||
165 | errno = EACCES; | ||
166 | return -1; | ||
167 | } | ||
168 | } | ||
169 | else { | ||
170 | return -1; | ||
171 | } | ||
172 | |||
173 | argc = string_array_len((char **)argv); | ||
174 | new_argv = xzalloc(sizeof(*argv)*(argc+1)); | ||
175 | for (i = 0; i < argc; i++) { | ||
176 | new_argv[i] = quote_arg(argv[i]); | ||
177 | len += strlen(new_argv[i]) + 1; | ||
178 | } | ||
179 | |||
180 | /* Special case: spawnve won't execute a batch file if the first | ||
181 | * argument is a relative path containing forward slashes. Absolute | ||
182 | * paths are fine but there's no harm in converting them too. */ | ||
183 | if (has_bat_suffix(path)) { | ||
184 | slash_to_bs(new_argv[0]); | ||
185 | |||
186 | /* Another special case: spawnve returns ENOEXEC when passed an | ||
187 | * empty batch file. Pretend it worked. */ | ||
188 | if (st.st_size == 0) { | ||
189 | ret = 0; | ||
190 | goto done; | ||
191 | } | ||
192 | } | ||
193 | |||
194 | /* | ||
195 | * Another special case: if a file doesn't have an extension add | ||
196 | * a '.' at the end. This forces spawnve to use precisely the | ||
197 | * file specified without trying to add an extension. | ||
198 | */ | ||
199 | if (!strchr(bb_basename(path), '.')) { | ||
200 | new_path = xasprintf("%s.", path); | ||
201 | } | ||
202 | |||
203 | errno = 0; | ||
204 | ret = spawnve(mode, new_path ? new_path : path, new_argv, env); | ||
205 | if (errno == EINVAL && len > bb_arg_max()) | ||
206 | errno = E2BIG; | ||
207 | |||
208 | done: | ||
209 | for (i = 0;i < argc;i++) | ||
210 | free(new_argv[i]); | ||
211 | free(new_argv); | ||
212 | free(new_path); | ||
213 | |||
214 | return ret; | ||
215 | } | ||
216 | |||
217 | #if ENABLE_FEATURE_PREFER_APPLETS && NUM_APPLETS > 1 | ||
218 | static intptr_t | ||
219 | mingw_spawn_applet(int mode, | ||
220 | char *const *argv, | ||
221 | char *const *envp) | ||
222 | { | ||
223 | return spawnveq(mode, bb_busybox_exec_path, argv, envp); | ||
224 | } | ||
225 | #endif | ||
226 | |||
227 | /* Make a copy of an argv array with n extra slots at the start */ | ||
228 | char ** FAST_FUNC | ||
229 | grow_argv(char **argv, int n) | ||
230 | { | ||
231 | char **new_argv; | ||
232 | int argc; | ||
233 | |||
234 | argc = string_array_len(argv) + 1; | ||
235 | new_argv = xmalloc(sizeof(*argv) * (argc + n)); | ||
236 | memcpy(new_argv + n, argv, sizeof(*argv) * argc); | ||
237 | return new_argv; | ||
238 | } | ||
239 | |||
240 | #if ENABLE_FEATURE_HTTPD_CGI | ||
241 | static int | ||
242 | create_detached_process(const char *prog, char *const *argv) | ||
243 | { | ||
244 | int argc, i; | ||
245 | char *command = NULL; | ||
246 | STARTUPINFO siStartInfo; | ||
247 | PROCESS_INFORMATION piProcInfo; | ||
248 | int success; | ||
249 | |||
250 | argc = string_array_len((char **)argv); | ||
251 | for (i = 0; i < argc; i++) { | ||
252 | char *qarg = quote_arg(argv[i]); | ||
253 | command = xappendword(command, qarg); | ||
254 | if (ENABLE_FEATURE_CLEAN_UP) | ||
255 | free(qarg); | ||
256 | } | ||
257 | |||
258 | ZeroMemory(&siStartInfo, sizeof(STARTUPINFO)); | ||
259 | siStartInfo.cb = sizeof(STARTUPINFO); | ||
260 | siStartInfo.hStdInput = (HANDLE)_get_osfhandle(STDIN_FILENO); | ||
261 | siStartInfo.hStdOutput = (HANDLE)_get_osfhandle(STDOUT_FILENO); | ||
262 | siStartInfo.dwFlags = STARTF_USESTDHANDLES; | ||
263 | |||
264 | success = CreateProcess((LPCSTR)prog, | ||
265 | (LPSTR)command, /* command line */ | ||
266 | NULL, /* process security attributes */ | ||
267 | NULL, /* primary thread security attributes */ | ||
268 | TRUE, /* handles are inherited */ | ||
269 | CREATE_NO_WINDOW, /* creation flags */ | ||
270 | NULL, /* use parent's environment */ | ||
271 | NULL, /* use parent's current directory */ | ||
272 | &siStartInfo, /* STARTUPINFO pointer */ | ||
273 | &piProcInfo); /* receives PROCESS_INFORMATION */ | ||
274 | |||
275 | if (ENABLE_FEATURE_CLEAN_UP) | ||
276 | free(command); | ||
277 | |||
278 | if (!success) | ||
279 | return -1; | ||
280 | exit(0); | ||
281 | } | ||
282 | |||
283 | # define SPAWNVEQ(m, p, a, e) \ | ||
284 | ((m != HTTPD_DETACH) ? spawnveq(m, p, a, e) : \ | ||
285 | create_detached_process(p, a)) | ||
286 | #else | ||
287 | # define SPAWNVEQ(m, p, a, e) spawnveq(m, p, a, e) | ||
288 | #endif | ||
289 | |||
290 | static intptr_t | ||
291 | mingw_spawn_interpreter(int mode, const char *prog, char *const *argv, | ||
292 | char *const *envp, int level) | ||
293 | { | ||
294 | intptr_t ret = -1; | ||
295 | int nopts; | ||
296 | interp_t interp; | ||
297 | char **new_argv; | ||
298 | char *path = NULL; | ||
299 | int is_unix_path; | ||
300 | |||
301 | if (!parse_interpreter(prog, &interp)) | ||
302 | return SPAWNVEQ(mode, prog, argv, envp); | ||
303 | |||
304 | if (++level > 4) { | ||
305 | errno = ELOOP; | ||
306 | return -1; | ||
307 | } | ||
308 | |||
309 | nopts = interp.opts != NULL; | ||
310 | new_argv = grow_argv((char **)(argv + 1), nopts + 2); | ||
311 | new_argv[1] = interp.opts; | ||
312 | new_argv[nopts+1] = (char *)prog; /* pass absolute path */ | ||
313 | |||
314 | is_unix_path = unix_path(interp.path); | ||
315 | #if ENABLE_FEATURE_PREFER_APPLETS && NUM_APPLETS > 1 | ||
316 | if (is_unix_path && find_applet_by_name(interp.name) >= 0) { | ||
317 | /* the fake path indicates the index of the script */ | ||
318 | new_argv[0] = path = xasprintf("%d:/%s", nopts+1, interp.name); | ||
319 | ret = SPAWNVEQ(mode, bb_busybox_exec_path, new_argv, envp); | ||
320 | goto done; | ||
321 | } | ||
322 | #endif | ||
323 | |||
324 | path = file_is_win32_exe(interp.path); | ||
325 | if (!path && is_unix_path) | ||
326 | path = find_first_executable(interp.name); | ||
327 | |||
328 | if (path) { | ||
329 | new_argv[0] = path; | ||
330 | ret = mingw_spawn_interpreter(mode, path, new_argv, envp, level); | ||
331 | } else { | ||
332 | errno = ENOENT; | ||
333 | } | ||
334 | #if ENABLE_FEATURE_PREFER_APPLETS && NUM_APPLETS > 1 | ||
335 | done: | ||
336 | #endif | ||
337 | free(path); | ||
338 | free(new_argv); | ||
339 | return ret; | ||
340 | } | ||
341 | |||
342 | static intptr_t | ||
343 | mingw_spawnvp(int mode, const char *cmd, char *const *argv) | ||
344 | { | ||
345 | char *path; | ||
346 | intptr_t ret; | ||
347 | |||
348 | #if ENABLE_FEATURE_PREFER_APPLETS && NUM_APPLETS > 1 | ||
349 | if ((!has_path(cmd) || unix_path(cmd)) && | ||
350 | find_applet_by_name(bb_basename(cmd)) >= 0) | ||
351 | return mingw_spawn_applet(mode, argv, NULL); | ||
352 | #endif | ||
353 | if (has_path(cmd)) { | ||
354 | path = file_is_win32_exe(cmd); | ||
355 | if (path) { | ||
356 | ret = mingw_spawn_interpreter(mode, path, argv, NULL, 0); | ||
357 | free(path); | ||
358 | return ret; | ||
359 | } | ||
360 | if (unix_path(cmd)) | ||
361 | cmd = bb_basename(cmd); | ||
362 | } | ||
363 | |||
364 | if (!has_path(cmd) && (path = find_first_executable(cmd)) != NULL) { | ||
365 | ret = mingw_spawn_interpreter(mode, path, argv, NULL, 0); | ||
366 | free(path); | ||
367 | return ret; | ||
368 | } | ||
369 | |||
370 | errno = ENOENT; | ||
371 | return -1; | ||
372 | } | ||
373 | |||
374 | pid_t FAST_FUNC | ||
375 | mingw_spawn(char **argv) | ||
376 | { | ||
377 | intptr_t ret; | ||
378 | |||
379 | ret = mingw_spawnvp(P_NOWAIT, argv[0], (char *const *)argv); | ||
380 | |||
381 | return ret == -1 ? (pid_t)-1 : (pid_t)GetProcessId((HANDLE)ret); | ||
382 | } | ||
383 | |||
384 | intptr_t FAST_FUNC | ||
385 | mingw_spawn_detach(char **argv) | ||
386 | { | ||
387 | return mingw_spawnvp(P_DETACH, argv[0], argv); | ||
388 | } | ||
389 | |||
390 | intptr_t FAST_FUNC | ||
391 | mingw_spawn_proc(const char **argv) | ||
392 | { | ||
393 | return mingw_spawnvp(P_NOWAIT, argv[0], (char *const *)argv); | ||
394 | } | ||
395 | |||
396 | BOOL WINAPI kill_child_ctrl_handler(DWORD dwCtrlType) | ||
397 | { | ||
398 | static pid_t child_pid = 0; | ||
399 | DWORD dummy, *procs, count, rcount, i; | ||
400 | DECLARE_PROC_ADDR(DWORD, GetConsoleProcessList, LPDWORD, DWORD); | ||
401 | |||
402 | if (child_pid == 0) { | ||
403 | // First call sets child pid | ||
404 | child_pid = dwCtrlType; | ||
405 | return FALSE; | ||
406 | } | ||
407 | |||
408 | if (dwCtrlType == CTRL_C_EVENT || dwCtrlType == CTRL_BREAK_EVENT) { | ||
409 | if (!INIT_PROC_ADDR(kernel32.dll, GetConsoleProcessList)) | ||
410 | return TRUE; | ||
411 | |||
412 | count = GetConsoleProcessList(&dummy, 1) + 16; | ||
413 | procs = malloc(sizeof(DWORD) * count); | ||
414 | rcount = GetConsoleProcessList(procs, count); | ||
415 | if (rcount != 0 && rcount <= count) { | ||
416 | for (i = 0; i < rcount; i++) { | ||
417 | if (procs[i] == child_pid) { | ||
418 | // Child is attached to our console | ||
419 | break; | ||
420 | } | ||
421 | } | ||
422 | if (i == rcount) { | ||
423 | // Kill non-console child; console children can | ||
424 | // handle Ctrl-C as they see fit. | ||
425 | kill(-child_pid, SIGINT); | ||
426 | } | ||
427 | } | ||
428 | free(procs); | ||
429 | return TRUE; | ||
430 | } | ||
431 | return FALSE; | ||
432 | } | ||
433 | |||
434 | static int exit_code_to_wait_status_cmd(DWORD exit_code, const char *cmd) | ||
435 | { | ||
436 | int sig, status; | ||
437 | DECLARE_PROC_ADDR(ULONG, RtlNtStatusToDosError, NTSTATUS); | ||
438 | DWORD flags, code; | ||
439 | char *msg = NULL; | ||
440 | const char *sep = ": "; | ||
441 | |||
442 | if (exit_code == 0xc0000005) | ||
443 | return SIGSEGV; | ||
444 | else if (exit_code == 0xc000013a) | ||
445 | return SIGINT; | ||
446 | |||
447 | // When a process is terminated as if by a signal the Windows | ||
448 | // exit code is zero apart from the signal in its topmost byte. | ||
449 | // This is a busybox-w32 convention. | ||
450 | sig = exit_code >> 24; | ||
451 | if (sig != 0 && exit_code == sig << 24 && is_valid_signal(sig)) | ||
452 | return sig; | ||
453 | |||
454 | // The exit code may be an NTSTATUS code. Try to obtain a | ||
455 | // descriptive message for it. | ||
456 | if (exit_code > 0xff) { | ||
457 | flags = FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM; | ||
458 | if (INIT_PROC_ADDR(ntdll.dll, RtlNtStatusToDosError)) { | ||
459 | code = RtlNtStatusToDosError(exit_code); | ||
460 | if (FormatMessage(flags, NULL, code, 0, (char *)&msg, 0, NULL)) { | ||
461 | char *cr = strrchr(msg, '\r'); | ||
462 | if (cr) { // Replace CRLF with a space | ||
463 | cr[0] = ' '; | ||
464 | cr[1] = '\0'; | ||
465 | } | ||
466 | } | ||
467 | } | ||
468 | |||
469 | if (!cmd) | ||
470 | cmd = sep = ""; | ||
471 | bb_error_msg("%s%s%sError 0x%lx", cmd, sep, msg ?: "", exit_code); | ||
472 | LocalFree(msg); | ||
473 | } | ||
474 | |||
475 | // Use least significant byte as exit code, but not if it's zero | ||
476 | // and the Windows exit code as a whole is non-zero. | ||
477 | status = exit_code & 0xff; | ||
478 | if (exit_code != 0 && status == 0) | ||
479 | status = 255; | ||
480 | return status << 8; | ||
481 | } | ||
482 | |||
483 | static NORETURN void wait_for_child(HANDLE child, const char *cmd) | ||
484 | { | ||
485 | DWORD code; | ||
486 | int status; | ||
487 | |||
488 | if (getppid() == 1) | ||
489 | exit(0); | ||
490 | |||
491 | kill_child_ctrl_handler(GetProcessId(child)); | ||
492 | SetConsoleCtrlHandler(kill_child_ctrl_handler, TRUE); | ||
493 | WaitForSingleObject(child, INFINITE); | ||
494 | GetExitCodeProcess(child, &code); | ||
495 | // We don't need the wait status, but get it anyway so the error | ||
496 | // message can include the command. In such cases we pass the | ||
497 | // exit status to exit() so our caller won't repeat the message. | ||
498 | status = exit_code_to_wait_status_cmd(code, cmd); | ||
499 | if (!WIFSIGNALED(status) && code > 0xff) | ||
500 | code = WEXITSTATUS(status); | ||
501 | exit((int)code); | ||
502 | } | ||
503 | |||
504 | static intptr_t | ||
505 | shell_execute(const char *path, char *const *argv) | ||
506 | { | ||
507 | SHELLEXECUTEINFO info; | ||
508 | char *args; | ||
509 | |||
510 | memset(&info, 0, sizeof(SHELLEXECUTEINFO)); | ||
511 | info.cbSize = sizeof(SHELLEXECUTEINFO); | ||
512 | info.fMask = SEE_MASK_NOCLOSEPROCESS; | ||
513 | /* info.hwnd = NULL; */ | ||
514 | info.lpVerb = "runas"; | ||
515 | info.lpFile = path; | ||
516 | |||
517 | args = NULL; | ||
518 | if (*argv++) { | ||
519 | while (*argv) { | ||
520 | char *q = quote_arg(*argv++); | ||
521 | args = xappendword(args, q); | ||
522 | free(q); | ||
523 | } | ||
524 | } | ||
525 | |||
526 | info.lpParameters = args; | ||
527 | /* info.lpDirectory = NULL; */ | ||
528 | info.nShow = SW_SHOWNORMAL; | ||
529 | |||
530 | mingw_shell_execute(&info); | ||
531 | |||
532 | free(args); | ||
533 | return info.hProcess ? (intptr_t)info.hProcess : -1; | ||
534 | } | ||
535 | |||
536 | int | ||
537 | mingw_execvp(const char *cmd, char *const *argv) | ||
538 | { | ||
539 | intptr_t ret = mingw_spawnvp(P_NOWAIT, cmd, argv); | ||
540 | if (ret != -1) | ||
541 | wait_for_child((HANDLE)ret, cmd); | ||
542 | return ret; | ||
543 | } | ||
544 | |||
545 | int | ||
546 | mingw_execve(const char *cmd, char *const *argv, char *const *envp) | ||
547 | { | ||
548 | intptr_t ret = mingw_spawn_interpreter(P_NOWAIT, cmd, argv, envp, 0); | ||
549 | |||
550 | if (ret == -1 && GetLastError() == ERROR_ELEVATION_REQUIRED) { | ||
551 | // Command exists but failed because it wants elevated privileges. | ||
552 | // Try again using ShellExecuteEx(). | ||
553 | SetLastError(0); | ||
554 | ret = shell_execute(cmd, argv); | ||
555 | if (GetLastError()) | ||
556 | exit(1); | ||
557 | } | ||
558 | |||
559 | if (ret != -1) | ||
560 | wait_for_child((HANDLE)ret, cmd); | ||
561 | return ret; | ||
562 | } | ||
563 | |||
564 | int | ||
565 | mingw_execv(const char *cmd, char *const *argv) | ||
566 | { | ||
567 | return mingw_execve(cmd, argv, NULL); | ||
568 | } | ||
569 | |||
570 | #if ENABLE_FEATURE_HTTPD_CGI | ||
571 | int httpd_execv_detach(const char *script, char *const *argv) | ||
572 | { | ||
573 | intptr_t ret = mingw_spawn_interpreter(HTTPD_DETACH, script, | ||
574 | (char *const *)argv, NULL, 0); | ||
575 | if (ret != -1) | ||
576 | exit(0); | ||
577 | return ret; | ||
578 | } | ||
579 | #endif | ||
580 | |||
581 | static inline long long filetime_to_ticks(const FILETIME *ft) | ||
582 | { | ||
583 | return (((long long)ft->dwHighDateTime << 32) + ft->dwLowDateTime)/ | ||
584 | HNSEC_PER_TICK; | ||
585 | } | ||
586 | |||
587 | /* | ||
588 | * Attempt to get a string from another instance of busybox.exe. | ||
589 | * This will only work if the other process is using the same binary | ||
590 | * as the current process. If anything goes wrong just give up. | ||
591 | */ | ||
592 | static char *get_bb_string(DWORD pid, const char *exe, char *string) | ||
593 | { | ||
594 | HANDLE proc; | ||
595 | HMODULE mlist[32]; | ||
596 | DWORD needed; | ||
597 | void *address; | ||
598 | char *my_base; | ||
599 | char buffer[128]; | ||
600 | char exepath[PATH_MAX]; | ||
601 | char *name = NULL; | ||
602 | int i; | ||
603 | DECLARE_PROC_ADDR(DWORD, GetProcessImageFileNameA, HANDLE, | ||
604 | LPSTR, DWORD); | ||
605 | DECLARE_PROC_ADDR(BOOL, EnumProcessModules, HANDLE, HMODULE *, | ||
606 | DWORD, LPDWORD); | ||
607 | DECLARE_PROC_ADDR(DWORD, GetModuleFileNameExA, HANDLE, HMODULE, | ||
608 | LPSTR, DWORD); | ||
609 | |||
610 | if (!INIT_PROC_ADDR(psapi.dll, GetProcessImageFileNameA) || | ||
611 | !INIT_PROC_ADDR(psapi.dll, EnumProcessModules) || | ||
612 | !INIT_PROC_ADDR(psapi.dll, GetModuleFileNameExA)) | ||
613 | return NULL; | ||
614 | |||
615 | if (!(proc=OpenProcess(PROCESS_QUERY_INFORMATION|PROCESS_VM_READ, | ||
616 | FALSE, pid))) { | ||
617 | return NULL; | ||
618 | } | ||
619 | |||
620 | if (exe == NULL) { | ||
621 | if (GetProcessImageFileNameA(proc, exepath, PATH_MAX) != 0) { | ||
622 | exe = bb_basename(exepath); | ||
623 | } | ||
624 | } | ||
625 | |||
626 | /* | ||
627 | * Search for the module that matches the name of the executable. | ||
628 | * The values returned in mlist are actually the base address of | ||
629 | * the module in the other process (as noted in the documentation | ||
630 | * for the MODULEINFO structure). | ||
631 | */ | ||
632 | if (!EnumProcessModules(proc, mlist, sizeof(mlist), &needed)) { | ||
633 | goto finish; | ||
634 | } | ||
635 | |||
636 | for (i=0; exe != NULL && i<needed/sizeof(HMODULE); ++i) { | ||
637 | char modname[MAX_PATH]; | ||
638 | if (GetModuleFileNameExA(proc, mlist[i], modname, sizeof(modname))) { | ||
639 | if (strcasecmp(bb_basename(modname), exe) == 0) { | ||
640 | break; | ||
641 | } | ||
642 | } | ||
643 | } | ||
644 | |||
645 | if (i == needed/sizeof(HMODULE)) { | ||
646 | goto finish; | ||
647 | } | ||
648 | |||
649 | /* attempt to read the BusyBox version string */ | ||
650 | my_base = (char *)GetModuleHandle(NULL); | ||
651 | address = (char *)mlist[i] + ((char *)bb_banner - my_base); | ||
652 | if (!ReadProcessMemory(proc, address, buffer, 128, NULL)) { | ||
653 | goto finish; | ||
654 | } | ||
655 | |||
656 | if (memcmp(buffer, bb_banner, strlen(bb_banner)) != 0) { | ||
657 | /* version mismatch (or not BusyBox at all) */ | ||
658 | goto finish; | ||
659 | } | ||
660 | |||
661 | /* attempt to read the required string */ | ||
662 | address = (char *)mlist[i] + ((char *)string - my_base); | ||
663 | if (!ReadProcessMemory(proc, address, buffer, 128, NULL)) { | ||
664 | goto finish; | ||
665 | } | ||
666 | |||
667 | buffer[127] = '\0'; | ||
668 | name = auto_string(xstrdup(buffer)); | ||
669 | |||
670 | finish: | ||
671 | CloseHandle(proc); | ||
672 | return name; | ||
673 | } | ||
674 | |||
675 | pid_t getppid(void) | ||
676 | { | ||
677 | procps_status_t *sp = NULL; | ||
678 | int my_pid = getpid(); | ||
679 | |||
680 | while ((sp = procps_scan(sp, 0)) != NULL) { | ||
681 | if (sp->pid == my_pid) { | ||
682 | return sp->ppid; | ||
683 | } | ||
684 | } | ||
685 | return 1; | ||
686 | } | ||
687 | |||
688 | #define NPIDS 128 | ||
689 | |||
690 | /* POSIX version in libbb/procps.c */ | ||
691 | procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags | ||
692 | #if !ENABLE_FEATURE_PS_TIME && !ENABLE_FEATURE_PS_LONG | ||
693 | UNUSED_PARAM | ||
694 | #endif | ||
695 | ) | ||
696 | { | ||
697 | PROCESSENTRY32 pe; | ||
698 | HANDLE proc; | ||
699 | const char *comm, *name; | ||
700 | BOOL ret; | ||
701 | |||
702 | pe.dwSize = sizeof(pe); | ||
703 | if (!sp) { | ||
704 | sp = xzalloc(sizeof(struct procps_status_t)); | ||
705 | sp->snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); | ||
706 | if (sp->snapshot == INVALID_HANDLE_VALUE) { | ||
707 | free(sp); | ||
708 | return NULL; | ||
709 | } | ||
710 | if (Process32First(sp->snapshot, &pe)) { | ||
711 | int maxpids = 0; | ||
712 | do { | ||
713 | if (sp->npids == maxpids) { | ||
714 | maxpids += NPIDS; | ||
715 | sp->pids = xrealloc(sp->pids, sizeof(DWORD) * maxpids); | ||
716 | } | ||
717 | sp->pids[sp->npids++] = pe.th32ProcessID; | ||
718 | } while (Process32Next(sp->snapshot, &pe)); | ||
719 | } | ||
720 | ret = Process32First(sp->snapshot, &pe); | ||
721 | } | ||
722 | else { | ||
723 | ret = Process32Next(sp->snapshot, &pe); | ||
724 | } | ||
725 | |||
726 | if (!ret) { | ||
727 | CloseHandle(sp->snapshot); | ||
728 | free(sp->pids); | ||
729 | free(sp); | ||
730 | return NULL; | ||
731 | } | ||
732 | |||
733 | memset(&sp->vsz, 0, sizeof(*sp) - offsetof(procps_status_t, vsz)); | ||
734 | #if !ENABLE_DESKTOP | ||
735 | strcpy(sp->state, " "); | ||
736 | #endif | ||
737 | |||
738 | #if ENABLE_FEATURE_PS_TIME || ENABLE_FEATURE_PS_LONG | ||
739 | if (flags & (PSSCAN_STIME|PSSCAN_UTIME|PSSCAN_START_TIME)) { | ||
740 | FILETIME crTime, exTime, keTime, usTime; | ||
741 | |||
742 | if ((proc=OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, | ||
743 | FALSE, pe.th32ProcessID))) { | ||
744 | if (GetProcessTimes(proc, &crTime, &exTime, &keTime, &usTime)) { | ||
745 | long long ticks_since_boot, boot_time, create_time; | ||
746 | FILETIME now; | ||
747 | |||
748 | ticks_since_boot = GetTickCount64()/MS_PER_TICK; | ||
749 | GetSystemTimeAsFileTime(&now); | ||
750 | boot_time = filetime_to_ticks(&now) - ticks_since_boot; | ||
751 | create_time = filetime_to_ticks(&crTime); | ||
752 | |||
753 | sp->start_time = (unsigned long)(create_time - boot_time); | ||
754 | sp->stime = (unsigned long)filetime_to_ticks(&keTime); | ||
755 | sp->utime = (unsigned long)filetime_to_ticks(&usTime); | ||
756 | } | ||
757 | CloseHandle(proc); | ||
758 | } | ||
759 | } | ||
760 | #endif | ||
761 | |||
762 | if (flags & PSSCAN_UIDGID) { | ||
763 | /* if we can open the process it belongs to us */ | ||
764 | if ((proc=OpenProcess(PROCESS_ALL_ACCESS, FALSE, pe.th32ProcessID))) { | ||
765 | sp->uid = DEFAULT_UID; | ||
766 | sp->gid = DEFAULT_GID; | ||
767 | CloseHandle(proc); | ||
768 | } | ||
769 | } | ||
770 | |||
771 | /* The parent of PID 0 is 0. If the parent is a PID we haven't | ||
772 | * seen set PPID to 1. */ | ||
773 | sp->ppid = pe.th32ProcessID != 0; | ||
774 | for (int i = 0; i < sp->npids; ++i) { | ||
775 | if (sp->pids[i] == pe.th32ParentProcessID) { | ||
776 | sp->ppid = pe.th32ParentProcessID; | ||
777 | break; | ||
778 | } | ||
779 | } | ||
780 | sp->pid = pe.th32ProcessID; | ||
781 | |||
782 | if (flags & PSSCAN_COMM) { | ||
783 | if (sp->pid == getpid()) { | ||
784 | comm = applet_name; | ||
785 | } | ||
786 | else if ((name=get_bb_string(sp->pid, pe.szExeFile, bb_comm)) != NULL) { | ||
787 | comm = name; | ||
788 | } | ||
789 | else { | ||
790 | comm = pe.szExeFile; | ||
791 | } | ||
792 | safe_strncpy(sp->comm, comm, COMM_LEN); | ||
793 | } | ||
794 | |||
795 | return sp; | ||
796 | } | ||
797 | |||
798 | void FAST_FUNC read_cmdline(char *buf, int col, unsigned pid, const char *comm) | ||
799 | { | ||
800 | const char *str, *cmdline; | ||
801 | |||
802 | *buf = '\0'; | ||
803 | if (pid == getpid()) | ||
804 | cmdline = bb_command_line; | ||
805 | else if ((str=get_bb_string(pid, NULL, bb_command_line)) != NULL) | ||
806 | cmdline = str; | ||
807 | else | ||
808 | cmdline = comm; | ||
809 | safe_strncpy(buf, cmdline, col); | ||
810 | } | ||
811 | |||
812 | /** | ||
813 | * Determine whether a process runs in the same architecture as the current | ||
814 | * one. That test is required before we assume that GetProcAddress() returns | ||
815 | * a valid address *for the target process*. | ||
816 | */ | ||
817 | static inline int process_architecture_matches_current(HANDLE process) | ||
818 | { | ||
819 | static BOOL current_is_wow = -1; | ||
820 | BOOL is_wow; | ||
821 | |||
822 | if (current_is_wow == -1 && | ||
823 | !IsWow64Process (GetCurrentProcess(), ¤t_is_wow)) | ||
824 | current_is_wow = -2; | ||
825 | if (current_is_wow == -2) | ||
826 | return 0; /* could not determine current process' WoW-ness */ | ||
827 | if (!IsWow64Process (process, &is_wow)) | ||
828 | return 0; /* cannot determine */ | ||
829 | return is_wow == current_is_wow; | ||
830 | } | ||
831 | |||
832 | /** | ||
833 | * This function tries to terminate a Win32 process, as gently as possible, | ||
834 | * by injecting a thread that calls ExitProcess(). | ||
835 | * | ||
836 | * Note: as kernel32.dll is loaded before any process, the other process and | ||
837 | * this process will have ExitProcess() at the same address. | ||
838 | * | ||
839 | * The idea comes from the Dr Dobb's article "A Safer Alternative to | ||
840 | * TerminateProcess()" by Andrew Tucker (July 1, 1999), | ||
841 | * http://www.drdobbs.com/a-safer-alternative-to-terminateprocess/184416547 | ||
842 | * | ||
843 | */ | ||
844 | static int kill_signal_by_handle(HANDLE process, int sig) | ||
845 | { | ||
846 | DECLARE_PROC_ADDR(DWORD, ExitProcess, LPVOID); | ||
847 | PVOID arg = (PVOID)(intptr_t)(sig << 24); | ||
848 | DWORD thread_id; | ||
849 | HANDLE thread; | ||
850 | |||
851 | if (!INIT_PROC_ADDR(kernel32, ExitProcess) || | ||
852 | !process_architecture_matches_current(process)) { | ||
853 | SetLastError(ERROR_ACCESS_DENIED); | ||
854 | return -1; | ||
855 | } | ||
856 | |||
857 | if (sig != 0 && (thread = CreateRemoteThread(process, NULL, 0, | ||
858 | ExitProcess, arg, 0, &thread_id))) { | ||
859 | CloseHandle(thread); | ||
860 | } | ||
861 | return 0; | ||
862 | } | ||
863 | |||
864 | static int kill_signal(pid_t pid, int sig) | ||
865 | { | ||
866 | HANDLE process; | ||
867 | int ret = 0; | ||
868 | DWORD code, flags; | ||
869 | |||
870 | if (sig == SIGKILL) | ||
871 | flags = PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION; | ||
872 | else | ||
873 | flags = SYNCHRONIZE | PROCESS_CREATE_THREAD | | ||
874 | PROCESS_QUERY_INFORMATION | | ||
875 | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | | ||
876 | PROCESS_VM_READ; | ||
877 | process = OpenProcess(flags, FALSE, pid); | ||
878 | |||
879 | if (!process) | ||
880 | return -1; | ||
881 | |||
882 | if (!GetExitCodeProcess(process, &code) || code != STILL_ACTIVE) { | ||
883 | SetLastError(ERROR_INVALID_PARAMETER); | ||
884 | ret = -1; | ||
885 | } else if (sig == SIGKILL) { | ||
886 | /* This way of terminating processes is not gentle: they get no | ||
887 | * chance to clean up after themselves (closing file handles, | ||
888 | * removing .lock files, terminating spawned processes (if any), | ||
889 | * etc). */ | ||
890 | ret = !TerminateProcess(process, SIGKILL << 24); | ||
891 | } else { | ||
892 | ret = kill_signal_by_handle(process, sig); | ||
893 | } | ||
894 | CloseHandle(process); | ||
895 | |||
896 | return ret; | ||
897 | } | ||
898 | |||
899 | /** | ||
900 | * If the process ID is positive signal that process only. If negative | ||
901 | * or zero signal all descendants of the indicated process. Zero | ||
902 | * indicates the current process; negative indicates the process with | ||
903 | * process ID -pid. | ||
904 | */ | ||
905 | int kill(pid_t pid, int sig) | ||
906 | { | ||
907 | DWORD *pids; | ||
908 | int max_len, i, len, ret = 0; | ||
909 | |||
910 | if (!is_valid_signal(sig)) { | ||
911 | errno = EINVAL; | ||
912 | return -1; | ||
913 | } | ||
914 | |||
915 | max_len = NPIDS; | ||
916 | pids = xmalloc(sizeof(*pids) * max_len); | ||
917 | |||
918 | if(pid > 0) | ||
919 | pids[0] = (DWORD)pid; | ||
920 | else if (pid == 0) | ||
921 | pids[0] = (DWORD)getpid(); | ||
922 | else | ||
923 | pids[0] = (DWORD)-pid; | ||
924 | len = 1; | ||
925 | |||
926 | /* | ||
927 | * Even if Process32First()/Process32Next() seem to traverse the | ||
928 | * processes in topological order (i.e. parent processes before | ||
929 | * child processes), there is nothing in the Win32 API documentation | ||
930 | * suggesting that this is guaranteed. | ||
931 | * | ||
932 | * Therefore, run through them at least twice and stop when no more | ||
933 | * process IDs were added to the list. | ||
934 | */ | ||
935 | if (pid <= 0) { | ||
936 | HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); | ||
937 | PROCESSENTRY32 entry; | ||
938 | int pid_added; | ||
939 | |||
940 | if (snapshot == INVALID_HANDLE_VALUE) { | ||
941 | errno = err_win_to_posix(); | ||
942 | free(pids); | ||
943 | return -1; | ||
944 | } | ||
945 | |||
946 | entry.dwSize = sizeof(entry); | ||
947 | pid_added = TRUE; | ||
948 | while (pid_added && Process32First(snapshot, &entry)) { | ||
949 | pid_added = FALSE; | ||
950 | |||
951 | do { | ||
952 | for (i = len - 1; i >= 0; i--) { | ||
953 | if (pids[i] == entry.th32ProcessID) | ||
954 | break; | ||
955 | if (pids[i] == entry.th32ParentProcessID) { | ||
956 | if (len == max_len) { | ||
957 | max_len += NPIDS; | ||
958 | pids = xrealloc(pids, sizeof(*pids) * max_len); | ||
959 | } | ||
960 | pids[len++] = entry.th32ProcessID; | ||
961 | pid_added = TRUE; | ||
962 | } | ||
963 | } | ||
964 | } while (Process32Next(snapshot, &entry)); | ||
965 | } | ||
966 | |||
967 | CloseHandle(snapshot); | ||
968 | } | ||
969 | |||
970 | for (i = len - 1; i >= 0; i--) { | ||
971 | SetLastError(0); | ||
972 | if (kill_signal(pids[i], sig)) { | ||
973 | errno = err_win_to_posix(); | ||
974 | ret = -1; | ||
975 | } | ||
976 | } | ||
977 | free(pids); | ||
978 | |||
979 | return ret; | ||
980 | } | ||
981 | |||
982 | int FAST_FUNC is_valid_signal(int number) | ||
983 | { | ||
984 | return isalpha(*get_signame(number)); | ||
985 | } | ||
986 | |||
987 | int exit_code_to_wait_status(DWORD exit_code) | ||
988 | { | ||
989 | return exit_code_to_wait_status_cmd(exit_code, NULL); | ||
990 | } | ||
991 | |||
992 | int exit_code_to_posix(DWORD exit_code) | ||
993 | { | ||
994 | int status = exit_code_to_wait_status(exit_code); | ||
995 | |||
996 | if (WIFSIGNALED(status)) | ||
997 | return 128 + WTERMSIG(status); | ||
998 | return WEXITSTATUS(status); | ||
999 | } | ||