aboutsummaryrefslogtreecommitdiff
path: root/win32/process.c
diff options
context:
space:
mode:
Diffstat (limited to 'win32/process.c')
-rw-r--r--win32/process.c999
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
7pid_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
15pid_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
56int FAST_FUNC
57parse_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 */
109char * FAST_FUNC
110quote_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
142char * FAST_FUNC
143find_first_executable(const char *name)
144{
145 const char *path = getenv("PATH");
146 return find_executable(name, &path);
147}
148
149static intptr_t
150spawnveq(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
218static intptr_t
219mingw_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 */
228char ** FAST_FUNC
229grow_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
241static int
242create_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
290static intptr_t
291mingw_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
342static intptr_t
343mingw_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
374pid_t FAST_FUNC
375mingw_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
384intptr_t FAST_FUNC
385mingw_spawn_detach(char **argv)
386{
387 return mingw_spawnvp(P_DETACH, argv[0], argv);
388}
389
390intptr_t FAST_FUNC
391mingw_spawn_proc(const char **argv)
392{
393 return mingw_spawnvp(P_NOWAIT, argv[0], (char *const *)argv);
394}
395
396BOOL 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
434static 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
483static 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
504static intptr_t
505shell_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
536int
537mingw_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
545int
546mingw_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
564int
565mingw_execv(const char *cmd, char *const *argv)
566{
567 return mingw_execve(cmd, argv, NULL);
568}
569
570#if ENABLE_FEATURE_HTTPD_CGI
571int 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
581static 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 */
592static 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
675pid_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 */
691procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags
692#if !ENABLE_FEATURE_PS_TIME && !ENABLE_FEATURE_PS_LONG
693UNUSED_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
798void 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 */
817static 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(), &current_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 */
844static 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
864static 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 */
905int 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
982int FAST_FUNC is_valid_signal(int number)
983{
984 return isalpha(*get_signame(number));
985}
986
987int exit_code_to_wait_status(DWORD exit_code)
988{
989 return exit_code_to_wait_status_cmd(exit_code, NULL);
990}
991
992int 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}