ohmu
·
2026-10-15
1#include "fastfetch.h"
2#include "common/debug.h"
3#include "common/processing.h"
4#include "common/io.h"
5#include "common/strutil.h"
6#include "common/mallocHelper.h"
7
8#include <stdlib.h>
9#include <unistd.h>
10#include <signal.h>
11#include <poll.h>
12#include <fcntl.h>
13#include <errno.h>
14#include <sys/wait.h>
15
16#if !(__ANDROID__ || __OpenBSD__)
17 #include <spawn.h>
18#endif
19
20#if defined(__FreeBSD__) || defined(__APPLE__)
21 #include <sys/types.h>
22 #include <sys/user.h>
23 #include <sys/sysctl.h>
24#endif
25#if defined(__APPLE__)
26 #include <libproc.h>
27#elif defined(__sun)
28 #include <procfs.h>
29#elif defined(__OpenBSD__)
30 #include <sys/param.h>
31 #include <sys/sysctl.h>
32#elif defined(__NetBSD__)
33 #include <sys/types.h>
34 #include <sys/sysctl.h>
35#elif defined(__HAIKU__)
36 #include <OS.h>
37 #include <image.h>
38#endif
39
40#ifndef environ
41extern char** environ;
42#endif
43
44enum { FF_PIPE_BUFSIZ = 8192 };
45
46static inline int ffPipe2(int* readEnd, int* writeEnd, int flags) {
47 int fds[2];
48#ifndef FF_HAVE_PIPE2
49 if (pipe(fds) == -1) {
50 return -1;
51 }
52 fcntl(fds[0], F_SETFL, fcntl(fds[0], F_GETFL) | flags);
53 fcntl(fds[1], F_SETFL, fcntl(fds[1], F_GETFL) | flags);
54#else
55 if (pipe2(fds, flags) == -1) {
56 return -1;
57 }
58#endif
59 *readEnd = fds[0];
60 *writeEnd = fds[1];
61 return 0;
62}
63
64// Not thread-safe
65const char* ffProcessSpawn(char* const argv[], FFProcessOutputType useOutput, FFNativeFD stdinFd, FFProcessHandle* outHandle) {
66 FF_AUTO_CLOSE_FD int pipeRead = -1;
67 FF_AUTO_CLOSE_FD int pipeWrite = -1;
68 if (ffPipe2(&pipeRead, &pipeWrite, O_CLOEXEC) == -1) {
69 FF_DEBUG("pipe() failed: %s", strerror(errno));
70 return "pipe() failed";
71 }
72
73 pid_t childPid = -1;
74 int nullFile = ffGetNullFD();
75 // Each stream is wired to the pipe when its bit is set and to the null device otherwise, so an
76 // unselected stream can neither mix into the captured text nor fill the pipe and block the child.
77 int stdoutFd = (useOutput & FF_PROCESS_OUTPUT_STDOUT_BIT) ? pipeWrite : nullFile;
78 int stderrFd = (useOutput & FF_PROCESS_OUTPUT_STDERR_BIT) ? pipeWrite : nullFile;
79
80#if !(__ANDROID__ || __OpenBSD__)
81
82 // NetBSD / Darwin: native syscall
83 // Linux (glibc): clone3-execve
84 // FreeBSD: vfork-execve
85 // illumos: vforkx-execve
86 // OpenBSD / Android (bionic): fork-execve
87
88 posix_spawn_file_actions_t file_actions;
89 posix_spawn_file_actions_init(&file_actions);
90 posix_spawn_file_actions_adddup2(&file_actions, stdoutFd, STDOUT_FILENO);
91 posix_spawn_file_actions_adddup2(&file_actions, stderrFd, STDERR_FILENO);
92 if (ffIsValidNativeFD(stdinFd)) {
93 posix_spawn_file_actions_adddup2(&file_actions, stdinFd, STDIN_FILENO);
94 }
95
96 static char* oldLang = nullptr;
97 static int langIndex = -1;
98
99 if (langIndex >= 0) {
100 // Found before
101 if (oldLang) // oldLang was set only if it needed to be changed
102 {
103 if (environ[langIndex] != oldLang) {
104 // environ is changed outside of this function
105 langIndex = -1;
106 } else {
107 environ[langIndex] = (char*) "LANG=C.UTF-8";
108 }
109 }
110 }
111 if (langIndex < 0) {
112 for (int i = 0; environ[i] != nullptr; i++) {
113 if (ffStrStartsWith(environ[i], "LANG=")) {
114 langIndex = i;
115 const char* langValue = environ[i] + 5; // Skip "LANG="
116 if (ffStrEqualsIgnCase(langValue, "C") ||
117 ffStrStartsWithIgnCase(environ[i], "C.") ||
118 ffStrEqualsIgnCase(langValue, "en_US") ||
119 ffStrStartsWithIgnCase(langValue, "en_US.")) {
120 break; // No need to change LANG
121 }
122 oldLang = environ[i];
123 environ[i] = (char*) "LANG=C.UTF-8"; // Set LANG to C.UTF-8 for consistent output
124 break;
125 }
126 }
127 }
128
129 int ret = posix_spawnp(&childPid, argv[0], &file_actions, nullptr, argv, environ);
130
131 if (oldLang) {
132 environ[langIndex] = oldLang;
133 }
134
135 posix_spawn_file_actions_destroy(&file_actions);
136
137 if (ret != 0) {
138 FF_DEBUG("posix_spawnp() failed: %s", strerror(ret));
139 if (ret == ENOENT) {
140 return "command not found";
141 }
142 return "posix_spawnp() failed";
143 }
144
145#else
146
147 // https://github.com/termux/termux-packages/issues/25369
148 childPid = fork();
149 if (childPid == -1) {
150 FF_DEBUG("fork() failed: %s", strerror(errno));
151 return "fork() failed";
152 }
153
154 if (childPid == 0) {
155 // Child process
156 dup2(stdoutFd, STDOUT_FILENO);
157 dup2(stderrFd, STDERR_FILENO);
158 if (ffIsValidNativeFD(stdinFd)) {
159 dup2(stdinFd, STDIN_FILENO);
160 }
161 putenv("LANG=C.UTF-8");
162 execvp(argv[0], argv);
163 _exit(127);
164 }
165
166#endif
167
168 outHandle->pid = childPid;
169 outHandle->pipeRead = pipeRead;
170 pipeRead = -1; // ownership moved into outHandle, which ffProcessReadOutput() lets go
171 return nullptr;
172}
173
174const char* ffProcessReadOutput(FFProcessHandle* handle, FFstrbuf* buffer) {
175 assert(handle->pipeRead != -1);
176 assert(handle->pid != -1);
177
178 const int32_t timeout = instance.config.general.processingTimeout;
179 FF_AUTO_CLOSE_FD int childPipeFd = handle->pipeRead;
180 pid_t childPid = handle->pid;
181 handle->pipeRead = -1;
182 handle->pid = -1;
183 char str[FF_PIPE_BUFSIZ];
184
185 for (;;) {
186 if (timeout >= 0) {
187 struct pollfd pollfd = { childPipeFd, POLLIN, 0 };
188 int pollret = poll(&pollfd, 1, timeout);
189 if (pollret == 0) {
190 FF_DEBUG("poll(&pollfd, 1, timeout) timeout (try increasing --processing-timeout)");
191 kill(childPid, SIGTERM);
192 waitpid(childPid, nullptr, 0);
193 return "poll(&pollfd, 1, timeout) timeout (try increasing --processing-timeout)";
194 } else if (pollret < 0 || (pollfd.revents & POLLERR)) {
195 kill(childPid, SIGTERM);
196 waitpid(childPid, nullptr, 0);
197 return pollret < 0
198 ? "poll(&pollfd, 1, timeout) error: pollret < 0"
199 : "poll(&pollfd, 1, timeout) error: pollfd.revents & POLLERR";
200 }
201 }
202
203 ssize_t nRead = read(childPipeFd, str, FF_PIPE_BUFSIZ);
204 if (nRead > 0) {
205 ffStrbufAppendNS(buffer, (uint32_t) nRead, str);
206 } else if (nRead == 0) {
207 int stat_loc = 0;
208 if (childPid > 0 && waitpid(childPid, &stat_loc, 0) == childPid) {
209 if (!WIFEXITED(stat_loc)) {
210 return "child process exited abnormally";
211 }
212 if (WEXITSTATUS(stat_loc) == 127) {
213 FF_DEBUG("command not found");
214 return "command not found";
215 }
216 // We only handle 127 as an error. See `getTerminalVersionUrxvt` in `terminalshell.c`
217 return nullptr;
218 }
219 return nullptr;
220 } else if (nRead < 0) {
221 break;
222 }
223 }
224
225 FF_DEBUG("read(childPipeFd, str, FF_PIPE_BUFSIZ) failed: %s", strerror(errno));
226 return "read(childPipeFd, str, FF_PIPE_BUFSIZ) failed";
227}
228
229void ffProcessGetInfoLinux(pid_t pid, FFstrbuf* processName, FFstrbuf* exe, const char** exeName, FFstrbuf* exePath) {
230 assert(processName->length > 0);
231 ffStrbufClear(exe);
232 if (exePath) {
233 ffStrbufClear(exePath);
234 }
235
236#if defined(__linux__) || defined(__GNU__)
237
238 char filePath[64];
239 snprintf(filePath, sizeof(filePath), "/proc/%d/cmdline", (int) pid);
240
241 if (ffReadFileBuffer(filePath, exe)) {
242 const char* p = exe->chars;
243 uint32_t len = (uint32_t) strlen(p);
244
245 if (len + 1 < exe->length) {
246 const char* name = memrchr(p, '/', len);
247 if (name) {
248 name++;
249 } else {
250 name = p;
251 }
252
253 // For interpreters, try to find the real script path in the arguments
254 if (ffStrStartsWith(name, "python")
255 #ifndef __ANDROID__
256 || ffStrEquals(name, "guile") // for shepherd
257 #endif
258 ) {
259 // `cmdline` always ends with a trailing '\0', and ffReadFileBuffer appends another \0
260 // So `exe->chars` is always double '\0' terminated
261 for (p = p + len + 1; *p && *p == '-'; p += strlen(p) + 1) { // Skip arguments
262 assert(p - exe->chars < exe->allocated);
263 }
264 if (*p) {
265 len = (uint32_t) strlen(p);
266 memmove(exe->chars, p, len + 1);
267 }
268 }
269 }
270
271 assert(len < exe->allocated);
272 exe->length = len;
273 ffStrbufTrimLeft(exe, '-'); // Login shells start with a dash
274 }
275
276 if (exePath) {
277 snprintf(filePath, sizeof(filePath), "/proc/%d/exe", (int) pid);
278 char buf[PATH_MAX];
279 ssize_t length = readlink(filePath, buf, PATH_MAX - 1);
280 if (length > 0) // doesn't contain trailing NUL
281 {
282 buf[length] = '\0';
283 // When the process is a deleted executable, the resolved path is like `/usr/bin/app (deleted)`
284 // But we can still access the binary via `/proc/pid/exe`. See #2136
285 if (ffPathExists(buf, FF_PATHTYPE_ANY)) {
286 ffStrbufSetNS(exePath, (uint32_t) length, buf);
287 }
288 }
289
290 if (exePath->length == 0 && access(filePath, X_OK) == 0) {
291 ffStrbufSetS(exePath, filePath);
292 }
293 }
294
295#elif defined(__APPLE__)
296
297 size_t len = 0;
298 int mibs[] = { CTL_KERN, KERN_PROCARGS2, pid };
299 if (sysctl(mibs, ARRAY_SIZE(mibs), nullptr, &len, nullptr, 0) == 0) { // try get arg0
300 // don't know why if don't let len longer, proArgs2 and len will change during the following sysctl() in old MacOS version.
301 len++;
302 FF_AUTO_FREE char* const procArgs2 = malloc(len);
303 if (sysctl(mibs, ARRAY_SIZE(mibs), procArgs2, &len, nullptr, 0) == 0) {
304 // https://gist.github.com/nonowarn/770696#file-getargv-c-L46
305 uint32_t argc = *(uint32_t*) procArgs2;
306 const char* realExePath = procArgs2 + sizeof(argc);
307
308 const char* arg0 = memchr(realExePath, '\0', len - (size_t) (realExePath - procArgs2));
309 if (exePath) {
310 ffStrbufSetNS(exePath, (uint32_t) (arg0 - realExePath), realExePath);
311 }
312
313 do {
314 arg0++;
315 } while (*arg0 == '\0');
316 assert(arg0 < procArgs2 + len);
317
318 if (argc > 1) {
319 // #977
320 const char* p = strrchr(arg0, '/');
321 if (p) {
322 p++;
323 } else {
324 p = arg0;
325 }
326 if (ffStrStartsWithIgnCase(p, "python")) { // /opt/homebrew/Cellar/python@3.12/3.12.3/Frameworks/Python.framework/Versions/3.12/Resources/Python.app/Contents/MacOS/Python /Users/carter/.local/bin/xonsh
327 arg0 = p + strlen(p) + 1;
328 }
329 }
330
331 if (*arg0 == '-') {
332 arg0++; // Login shells
333 }
334
335 ffStrbufSetS(exe, arg0);
336 }
337 }
338
339 if (exePath || exe->length == 0) {
340 char buf[PROC_PIDPATHINFO_MAXSIZE];
341 int length = proc_pidpath(pid, buf, ARRAY_SIZE(buf));
342 if (length > 0) {
343 if (exe->length == 0) {
344 ffStrbufSetNS(exe, (uint32_t) length, buf);
345 }
346 if (exePath) {
347 // We don't use exec_path above as exePath because it's a relative path and can be different
348 // from the actual executable being run (for example, when the original file is moved)
349 ffStrbufSetNS(exePath, (uint32_t) length, buf);
350 }
351 }
352 }
353
354#elif defined(__FreeBSD__) || defined(__NetBSD__)
355
356 size_t size = ARG_MAX;
357 FF_AUTO_FREE char* args = malloc(size);
358
359 static_assert(ARG_MAX > PATH_MAX, "");
360
361 if (exePath && sysctl((int[]) { CTL_KERN,
362 #if __FreeBSD__
363 KERN_PROC,
364 KERN_PROC_PATHNAME,
365 pid
366 #else
367 KERN_PROC_ARGS,
368 pid,
369 KERN_PROC_PATHNAME
370 #endif
371 },
372 4,
373 args,
374 &size,
375 nullptr,
376 0) == 0)
377 ffStrbufSetNS(exePath, (uint32_t) (size - 1), args);
378
379 size = ARG_MAX;
380 if (sysctl(
381 (int[]) { CTL_KERN,
382 #if __FreeBSD__
383 KERN_PROC,
384 KERN_PROC_ARGS,
385 pid
386 #else
387 KERN_PROC_ARGS,
388 pid,
389 KERN_PROC_ARGV,
390 #endif
391 },
392 4,
393 args,
394 &size,
395 nullptr,
396 0) == 0) {
397 char* arg0 = args;
398 size_t arg0Len = strlen(args);
399 if (size > arg0Len + 1) {
400 char* p = (char*) memrchr(args, '/', arg0Len);
401 if (p) {
402 p++;
403 } else {
404 p = arg0;
405 }
406 if (ffStrStartsWith(p, "python")) // /usr/local/bin/python3.9 /home/carter/.local/bin/xonsh
407 {
408 arg0 += arg0Len + 1;
409 }
410 }
411 if (arg0[0] == '-') {
412 arg0++;
413 }
414 ffStrbufSetS(exe, arg0);
415 }
416
417#elif defined(__sun)
418
419 char filePath[128];
420 snprintf(filePath, sizeof(filePath), "/proc/%d/psinfo", (int) pid);
421 psinfo_t proc;
422 if (ffReadFileData(filePath, sizeof(proc), &proc) == sizeof(proc)) {
423 const char* args = proc.pr_psargs;
424 if (args[0] == '-') {
425 ++args;
426 }
427 const char* end = strchr(args, ' ');
428 ffStrbufSetNS(exe, end ? (uint32_t) (end - args) : (uint32_t) strlen(args), args);
429 }
430
431 if (exePath) {
432 snprintf(filePath, sizeof(filePath), "/proc/%d/path/a.out", (int) pid);
433 char buf[PATH_MAX];
434 ssize_t length = readlink(filePath, buf, PATH_MAX - 1);
435 if (length > 0) // doesn't contain trailing NUL
436 {
437 buf[length] = '\0';
438 ffStrbufSetNS(exePath, (uint32_t) length, buf);
439 }
440 }
441
442#elif defined(__OpenBSD__)
443
444 char argvBuf[ARG_MAX];
445 size_t argvSize = sizeof(argvBuf);
446 int argvMib[] = { CTL_KERN, KERN_PROC_ARGS, pid, KERN_PROC_ARGV };
447 if (sysctl(argvMib, ARRAY_SIZE(argvMib), argvBuf, &argvSize, nullptr, 0) == 0) {
448 // The buffer is filled with an array of char pointers followed by the strings themselves
449 char** argv = (char**) argvBuf;
450 if (argv[0] && (char*) argv[0] >= argvBuf && (char*) argv[0] < argvBuf + argvSize) {
451 const char* arg0 = argv[0];
452 if (arg0[0] == '-') {
453 arg0++;
454 }
455 ffStrbufSetS(exe, arg0);
456 }
457 }
458
459#elif defined(__HAIKU__)
460
461 image_info info;
462 int32 cookie = 0;
463
464 while (get_next_image_info(pid, &cookie, &info) == B_OK) {
465 if (info.type != B_APP_IMAGE) {
466 continue;
467 }
468 ffStrbufSetS(exe, info.name);
469
470 if (exePath) {
471 ffStrbufSet(exePath, exe);
472 }
473 break;
474 }
475
476#endif
477
478 if (exe->length == 0) {
479 ffStrbufSet(exe, processName);
480 }
481
482 assert(exe->length > 0);
483 uint32_t lastSlashIndex = ffStrbufLastIndexC(exe, '/');
484 if (lastSlashIndex < exe->length) {
485 *exeName = exe->chars + lastSlashIndex + 1;
486 }
487}
488
489const char* ffProcessGetBasicInfoLinux(pid_t pid, FFstrbuf* name, pid_t* ppid, int32_t* tty) {
490 if (pid <= 0) {
491 FF_DEBUG("Invalid pid");
492 return "Invalid pid";
493 }
494
495#if defined(__linux__) || defined(__GNU__)
496
497 char procFilePath[64];
498 #if __linux__
499 if (ppid || tty)
500 #endif
501 {
502 snprintf(procFilePath, sizeof(procFilePath), "/proc/%d/stat", (int) pid);
503 char buf[PROC_FILE_BUFFSIZ];
504 ssize_t nRead = ffReadFileData(procFilePath, sizeof(buf) - 1, buf);
505 if (nRead <= 8) {
506 FF_DEBUG("ffReadFileData(/proc/pid/stat, PROC_FILE_BUFFSIZ-1, buf) failed");
507 return "ffReadFileData(/proc/pid/stat, PROC_FILE_BUFFSIZ-1, buf) failed";
508 }
509 buf[nRead] = '\0'; // pid (comm) state ppid pgrp session tty
510
511 const char* pState = nullptr;
512
513 {
514 // comm in `/proc/pid/stat` is not encoded, and may contain ' ', ')' or even `\n`
515 const char* start = memchr(buf, '(', (size_t) nRead);
516 if (!start) {
517 FF_DEBUG("memchr(stat, '(') failed");
518 return "memchr(stat, '(') failed";
519 }
520 start++;
521 const char* end = memrchr(start, ')', (size_t) nRead - (size_t) (start - buf));
522 if (!end) {
523 FF_DEBUG("memrchr(stat, ')') failed");
524 return "memrchr(stat, ')') failed";
525 }
526 ffStrbufSetNS(name, (uint32_t) (end - start), start);
527 ffStrbufTrimRightSpace(name);
528 if (name->chars[0] == '\0') {
529 FF_DEBUG("process name is empty");
530 return "process name is empty";
531 }
532 pState = end + 2; // skip ") "
533 }
534
535 #if !__linux__
536 if (ppid || tty)
537 #endif
538 {
539 int ppid_, tty_;
540 if (sscanf(pState + 2, "%d %*d %*d %d", &ppid_, &tty_) < 2) {
541 FF_DEBUG("sscanf(stat) failed");
542 return "sscanf(stat) failed";
543 }
544
545 if (ppid) {
546 *ppid = (pid_t) ppid_;
547 }
548 if (tty) {
549 *tty = tty_ & 0xFF;
550 }
551 }
552 }
553 #if __linux__
554 else {
555 snprintf(procFilePath, sizeof(procFilePath), "/proc/%d/comm", (int) pid);
556 ssize_t nRead = ffReadFileBuffer(procFilePath, name);
557 if (nRead <= 0) {
558 FF_DEBUG("ffReadFileBuffer(/proc/pid/comm, name) failed");
559 return "ffReadFileBuffer(/proc/pid/comm, name) failed";
560 }
561 ffStrbufTrimRightSpace(name);
562 }
563 #endif
564
565#elif defined(__APPLE__)
566
567 struct kinfo_proc proc;
568 size_t size = sizeof(proc);
569 if (sysctl(
570 (int[]) { CTL_KERN, KERN_PROC, KERN_PROC_PID, pid }, 4, &proc, &size, nullptr, 0)) {
571 FF_DEBUG("sysctl(KERN_PROC_PID) failed: %s", strerror(errno));
572 return "sysctl(KERN_PROC_PID) failed";
573 }
574
575 ffStrbufSetS(name, proc.kp_proc.p_comm); // truncated to 16 chars
576 if (ppid) {
577 *ppid = (pid_t) proc.kp_eproc.e_ppid;
578 }
579 if (tty) {
580 *tty = ((proc.kp_eproc.e_tdev >> 24) & 0xFF) == 0x10
581 ? proc.kp_eproc.e_tdev & 0xFFFFFF
582 : -1;
583 }
584
585#elif defined(__FreeBSD__)
586
587 #ifdef __DragonFly__
588 #define ki_comm kp_comm
589 #define ki_ppid kp_ppid
590 #define ki_tdev kp_tdev
591 #define ki_flag kp_flags
592 #endif
593
594 struct kinfo_proc proc;
595 size_t size = sizeof(proc);
596 if (sysctl(
597 (int[]) { CTL_KERN, KERN_PROC, KERN_PROC_PID, pid }, 4, &proc, &size, nullptr, 0)) {
598 FF_DEBUG("sysctl(KERN_PROC_PID) failed: %s", strerror(errno));
599 return "sysctl(KERN_PROC_PID) failed";
600 }
601
602 ffStrbufSetS(name, proc.ki_comm);
603 if (ppid) {
604 *ppid = (pid_t) proc.ki_ppid;
605 }
606 if (tty) {
607 if (proc.ki_tdev != NODEV && proc.ki_flag & P_CONTROLT) {
608 const char* ttyName = devname(proc.ki_tdev, S_IFCHR);
609 if (ffStrStartsWith(ttyName, "pts/")) {
610 *tty = (int32_t) strtol(ttyName + strlen("pts/"), nullptr, 10);
611 } else {
612 *tty = -1;
613 }
614 } else {
615 *tty = -1;
616 }
617 }
618
619#elif defined(__NetBSD__)
620
621 struct kinfo_proc2 proc;
622 size_t size = sizeof(proc);
623 if (sysctl(
624 (int[]) { CTL_KERN, KERN_PROC2, KERN_PROC_PID, pid, sizeof(proc), 1 }, 6, &proc, &size, nullptr, 0) != 0) {
625 FF_DEBUG("sysctl(KERN_PROC_PID) failed: %s", strerror(errno));
626 return "sysctl(KERN_PROC_PID) failed";
627 }
628
629 ffStrbufSetS(name, proc.p_comm);
630 if (ppid) {
631 *ppid = (pid_t) proc.p_ppid;
632 }
633 if (tty) {
634 if (proc.p_flag & P_CONTROLT) {
635 const char* ttyName = devname(proc.p_tdev, S_IFCHR);
636 if (ffStrStartsWith(ttyName, "pts/")) {
637 *tty = (int32_t) strtol(ttyName + strlen("pts/"), nullptr, 10);
638 } else {
639 *tty = -1;
640 }
641 } else {
642 *tty = -1;
643 }
644 }
645
646#elif defined(__sun)
647 char path[128];
648 snprintf(path, sizeof(path), "/proc/%d/psinfo", (int) pid);
649 psinfo_t proc;
650 if (ffReadFileData(path, sizeof(proc), &proc) != sizeof(proc)) {
651 FF_DEBUG("ffReadFileData(psinfo) failed");
652 return "ffReadFileData(psinfo) failed";
653 }
654
655 ffStrbufSetS(name, proc.pr_fname);
656 if (ppid) {
657 *ppid = proc.pr_ppid;
658 }
659 if (tty) {
660 *tty = (int) proc.pr_ttydev;
661 }
662
663#elif defined(__OpenBSD__)
664
665 struct kinfo_proc proc;
666 size_t size = sizeof(proc);
667 int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, pid, (int) sizeof(struct kinfo_proc), 1 };
668 if (sysctl(mib, ARRAY_SIZE(mib), &proc, &size, nullptr, 0) == 0) {
669 ffStrbufSetS(name, proc.p_comm);
670 if (ppid) {
671 *ppid = proc.p_ppid;
672 }
673 if (tty) {
674 *tty = (int) proc.p_tdev;
675 }
676 } else {
677 FF_DEBUG("sysctl(KERN_PROC_PID) failed: %s", strerror(errno));
678 return "sysctl(KERN_PROC_PID) failed";
679 }
680
681#elif defined(__HAIKU__)
682
683 team_info info;
684 if (get_team_info(pid, &info) == B_OK) {
685 ffStrbufSetS(name, info.name);
686 if (ppid) {
687 *ppid = info.parent;
688 }
689 }
690
691 FF_UNUSED(tty);
692
693#else
694
695 FF_DEBUG("Unsupported platform");
696 return "Unsupported platform";
697
698#endif
699
700 return nullptr;
701}