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