ohmu
·
2026-10-15
1#pragma once
2
3#include "common/FFstrbuf.h"
4#include "common/io.h" // FFNativeFD, ffGetNullFD
5
6#ifndef _WIN32
7 #include <sys/types.h> // pid_t
8#endif
9
10typedef struct FFProcessHandle {
11#if _WIN32
12 void* pid; // HANDLE
13 void* pipeRead; // HANDLE
14#else
15 pid_t pid;
16 int pipeRead;
17#endif
18} FFProcessHandle;
19
20// Which of the child's two output streams are wired to the pipe that `ffProcessReadOutput()` drains.
21// The stream that is not selected is pointed at the null device, so it can neither interleave with
22// the captured text nor fill the pipe and block the child. This is a bitfield rather than a value
23// because `BOTH` is a combination: each backend sets up the child's two stdio slots independently,
24// testing one bit each.
25typedef enum FFProcessOutputType: uint8_t {
26 FF_PROCESS_OUTPUT_STDOUT_BIT = 1 << 0,
27 FF_PROCESS_OUTPUT_STDERR_BIT = 1 << 1,
28 FF_PROCESS_OUTPUT_BOTH = FF_PROCESS_OUTPUT_STDOUT_BIT | FF_PROCESS_OUTPUT_STDERR_BIT,
29} FFProcessOutputType;
30
31// `stdinFd` is the fd the child process gets as its stdin. Pass `ffGetNullFD()` to detach it from
32// our stdin, or `FF_PROCESS_INHERIT_STDIN` to inherit ours, which is what the helpers below do.
33// Detaching matters on Android, where `/system/bin/cmd` forwards its stdin over binder to the
34// service: the kernel rejects the whole transaction when that fd is a terminal, and the tool then
35// only reports `Failure calling service <name>: Failed transaction`.
36#define FF_PROCESS_INHERIT_STDIN ((FFNativeFD) -1)
37const char* ffProcessSpawn(char* const argv[], FFProcessOutputType useOutput, FFNativeFD stdinFd, FFProcessHandle* outHandle);
38const char* ffProcessReadOutput(FFProcessHandle* handle, FFstrbuf* buffer); // Destroys handle internally
39
40static inline const char* ffProcessAppendStdOut(FFstrbuf* buffer, char* const argv[]) {
41 FFProcessHandle handle;
42 const char* error = ffProcessSpawn(argv, FF_PROCESS_OUTPUT_STDOUT_BIT, FF_PROCESS_INHERIT_STDIN, &handle);
43 if (error) {
44 return error;
45 }
46
47 error = ffProcessReadOutput(&handle, buffer);
48 if (!error) {
49 ffStrbufTrimRightSpace(buffer);
50 }
51 return error;
52}
53
54static inline const char* ffProcessAppendStdErr(FFstrbuf* buffer, char* const argv[]) {
55 FFProcessHandle handle;
56 const char* error = ffProcessSpawn(argv, FF_PROCESS_OUTPUT_STDERR_BIT, FF_PROCESS_INHERIT_STDIN, &handle);
57 if (error) {
58 return error;
59 }
60
61 error = ffProcessReadOutput(&handle, buffer);
62 if (!error) {
63 ffStrbufTrimRightSpace(buffer);
64 }
65 return error;
66}
67
68#ifdef _WIN32
69bool ffProcessGetInfoWindows(uint32_t pid, uint32_t* ppid, FFstrbuf* pname, FFstrbuf* exe, const char** exeName, FFstrbuf* exePath, bool* gui);
70#else
71void ffProcessGetInfoLinux(pid_t pid, FFstrbuf* processName, FFstrbuf* exe, const char** exeName, FFstrbuf* exePath);
72const char* ffProcessGetBasicInfoLinux(pid_t pid, FFstrbuf* name, pid_t* ppid, int32_t* tty);
73#endif