ohmu
·
2026-10-15
1#include "initsystem.h"
2#include "common/processing.h"
3#include "common/binary.h"
4#include "common/strutil.h"
5
6#include <libgen.h>
7#include <unistd.h>
8
9[[maybe_unused]] static bool extractSystemdVersion(const char* str, uint32_t len, void* userdata) {
10 if (!ffStrStartsWith(str, "systemd ")) {
11 return true;
12 }
13 const char* pstart = str + strlen("systemd ");
14 const char* pend = memmem(pstart, len - strlen("systemd "), " running in ", strlen(" running in "));
15 if (!pend) {
16 return true;
17 }
18 ffStrbufSetNS((FFstrbuf*) userdata, (uint32_t) (pend - pstart), pstart);
19 return false;
20}
21
22const char* ffDetectInitSystem(FFInitSystemResult* result) {
23 const char* error = ffProcessGetBasicInfoLinux((int) result->pid, &result->name, nullptr, nullptr);
24 if (error) {
25#ifdef __ANDROID__
26 if (access("/system/bin/init", F_OK) == 0) {
27 ffStrbufSetStatic(&result->exe, "/system/bin/init");
28 ffStrbufSetStatic(&result->name, "init");
29 return nullptr;
30 }
31#endif
32 return error;
33 }
34
35 const char* _;
36 // In linux /proc/1/exe is not readable
37 ffProcessGetInfoLinux((int) result->pid, &result->name, &result->exe, &_, nullptr);
38 if (result->exe.chars[0] == '/') {
39 // In some old system, /sbin/init is a symlink
40 char buf[PATH_MAX];
41 if (realpath(result->exe.chars, buf)) {
42 ffStrbufSetS(&result->exe, buf);
43 ffStrbufSetS(&result->name, basename(result->exe.chars));
44 }
45 }
46
47 if (instance.config.general.detectVersion) {
48#if (defined(__linux__) && !defined(__ANDROID__)) || defined(__GNU__)
49 if (ffStrbufEqualS(&result->name, "systemd")) {
50 ffBinaryExtractStrings(result->exe.chars, extractSystemdVersion, &result->version, (uint32_t) strlen("systemd 0.0 running in x"));
51 if (result->version.length == 0) {
52 if (ffProcessAppendStdOut(&result->version, (char* const[]) {
53 ffStrbufEndsWithS(&result->exe, "/systemd") ? result->exe.chars : "systemctl", // use exe path in case users have another systemd installed
54 "--version",
55 nullptr,
56 }) == nullptr &&
57 result->version.length) {
58 uint32_t iStart = ffStrbufFirstIndexC(&result->version, '(');
59 if (iStart < result->version.length) {
60 uint32_t iEnd = ffStrbufNextIndexC(&result->version, iStart + 1, ')');
61 ffStrbufSubstrBefore(&result->version, iEnd);
62 ffStrbufSubstrAfter(&result->version, iStart);
63 }
64 }
65 }
66 } else if (ffStrbufEqualS(&result->name, "dinit")) {
67 if (ffProcessAppendStdOut(&result->version, (char* const[]) {
68 ffStrbufEndsWithS(&result->exe, "/dinit") ? result->exe.chars : "dinit",
69 "--version",
70 nullptr,
71 }) == nullptr &&
72 result->version.length) {
73 // Dinit version 0.18.0.
74 ffStrbufSubstrBeforeFirstC(&result->version, '\n');
75 ffStrbufTrimRight(&result->version, '.');
76 ffStrbufSubstrAfterLastC(&result->version, ' ');
77 }
78 } else if (ffStrbufEqualS(&result->name, "shepherd")) {
79 if (ffProcessAppendStdOut(&result->version, (char* const[]) {
80 ffStrbufEndsWithS(&result->exe, "/shepherd") ? result->exe.chars : "shepherd",
81 "--version",
82 nullptr,
83 }) == nullptr &&
84 result->version.length) {
85 // shepherd (GNU Shepherd) 1.0.6
86 // The first line in the output might not contain the version
87 if (!ffStrbufStartsWithS(&result->version, "shepherd")) {
88 ffStrbufSubstrAfterFirstC(&result->version, '\n');
89 }
90
91 ffStrbufSubstrBeforeFirstC(&result->version, '\n');
92 ffStrbufSubstrAfterLastC(&result->version, ' ');
93 }
94 }
95#elif __APPLE__
96 if (ffStrbufEqualS(&result->name, "launchd")) {
97 if (ffProcessAppendStdOut(&result->version, (char* const[]) {
98 "/bin/launchctl",
99 "version",
100 nullptr,
101 }) == nullptr &&
102 result->version.length) {
103 uint32_t iStart = ffStrbufFirstIndexS(&result->version, "Version ");
104 if (iStart < result->version.length) {
105 iStart += (uint32_t) strlen("Version");
106 uint32_t iEnd = ffStrbufNextIndexC(&result->version, iStart + 1, ':');
107 ffStrbufSubstrBefore(&result->version, iEnd);
108 ffStrbufSubstrAfter(&result->version, iStart);
109 }
110 }
111 }
112#endif
113 }
114
115 return nullptr;
116}