LM: detects LM from processes list if systemd is not available

2. removes type field
3. small optimizations
This commit is contained in:
Carter Li
2026-08-26 10:50:16 +08:00
parent 3b76ade662
commit 9670b585fb
4 changed files with 260 additions and 33 deletions
+2 -2
View File
@@ -978,7 +978,7 @@ elseif(OpenBSD)
src/detection/gpu/gpu_bsddrm.c
src/detection/gtk_qt/gtk.c
src/detection/host/host_obsd.c
src/detection/lm/lm_nosupport.c
src/detection/lm/lm_linux.c
src/detection/icons/icons_linux.c
src/detection/initsystem/initsystem_linux.c
src/detection/keyboard/keyboard_nosupport.c
@@ -1227,7 +1227,7 @@ elseif(SunOS)
src/detection/initsystem/initsystem_linux.c
src/detection/keyboard/keyboard_nosupport.c
src/detection/libc/libc_nosupport.c
src/detection/lm/lm_nosupport.c
src/detection/lm/lm_linux.c
src/detection/loadavg/loadavg_sunos.c
src/detection/locale/locale_linux.c
src/detection/localip/localip_linux.c
-1
View File
@@ -5,7 +5,6 @@
typedef struct FFLMResult {
FFstrbuf service;
FFstrbuf type;
FFstrbuf version;
} FFLMResult;
+258 -20
View File
@@ -1,18 +1,35 @@
#include "lm.h"
#include "common/io.h"
#include "common/mallocHelper.h"
#include "common/properties.h"
#include "common/dbus.h"
#include "common/processing.h"
#include "common/strutil.h"
#include "detection/displayserver/displayserver.h"
#include <unistd.h>
#if __FreeBSD__
#include <sys/sysctl.h>
#include <sys/types.h>
#include <sys/user.h>
#elif __OpenBSD__
#include <sys/param.h>
#include <sys/sysctl.h>
#include <kvm.h>
#elif __sun
#include <procfs.h>
#elif __NetBSD__
#include <sys/types.h>
#include <sys/sysctl.h>
#endif
#define FF_SYSTEMD_SESSIONS_PATH "/run/systemd/sessions/"
#define FF_SYSTEMD_USERS_PATH "/run/systemd/users/"
static const char* getGdmVersion(FFstrbuf* version) {
const char* error = ffProcessAppendStdOut(version, (char* const[]) { "gdm", "--version", nullptr });
const char* error = ffProcessAppendStdOut(version, (char* const[]) { "gdm3", "--version", nullptr });
if (error || version->length == 0) {
error = ffProcessAppendStdOut(version, (char* const[]) { "gdm3", "--version", nullptr });
error = ffProcessAppendStdOut(version, (char* const[]) { "gdm", "--version", nullptr });
if (error || version->length == 0) {
return "Failed to get GDM version";
}
@@ -112,34 +129,264 @@ static const char* getLightdmVersion(FFstrbuf* version) {
return nullptr;
}
const char* ffDetectLM(FFLMResult* result) {
#if __linux__ && !__ANDROID__
static const char* detectBySystemd(FFLMResult* result) {
FF_STRBUF_AUTO_DESTROY path = ffStrbufCreate();
FF_STRBUF_AUTO_DESTROY sessionId = ffStrbufCreateS(getenv("XDG_SESSION_ID"));
if (sessionId.length == 0) {
// On some incorrectly configured systems, $XDG_SESSION_ID is not set. Try finding it ourself
// WARNING: This is private data. Do not parse
ffStrbufSetF(&path, FF_SYSTEMD_USERS_PATH "%d", instance.state.platform.uid);
ffStrbufAppendS(&path, FF_SYSTEMD_USERS_PATH);
ffStrbufAppendUInt(&path, instance.state.platform.uid);
// This is actually buggy, and assumes current user is using DE
// `sd_pid_get_session` can be a better option, but we need to find a pid to use
if (!ffParsePropFileValues(path.chars, 1, (FFpropquery[]) {
if (!ffParsePropFileValues(path.chars, 1, (FFpropquery[]){
{ "DISPLAY=", &sessionId },
})) {
return "Failed to get $XDG_SESSION_ID";
}
}
ffStrbufSetS(&path, FF_SYSTEMD_SESSIONS_PATH);
ffStrbufClear(&path);
ffStrbufAppendS(&path, FF_SYSTEMD_SESSIONS_PATH);
ffStrbufAppend(&path, &sessionId);
// WARNING: This is private data. Do not parse
if (!ffParsePropFileValues(path.chars, 2, (FFpropquery[]) {
{ "SERVICE=", &result->service },
{ "TYPE=", &result->type },
})) {
if (!ffParsePropFile(path.chars, "SERVICE=", &result->service)) {
return "Failed to parse " FF_SYSTEMD_SESSIONS_PATH "$XDG_SESSION_ID";
}
return nullptr;
}
#endif
static const char* testLms(const char* psName) {
static const char* A[] = {
"atrium",
};
static const char* C[] = {
"cdm",
};
static const char* E[] = {
"entrance",
};
static const char* G[] = {
"gdm",
"gdm3",
"greetd",
};
static const char* L[] = {
"lemurs",
"lightdm",
"lxdm",
"ly",
};
static const char* P[] = {
"plasmalogin",
};
static const char* S[] = {
"sddm",
"slim",
};
static const char* T[] = {
"tbsm",
};
static const char* X[] = {
"xdm",
};
switch (psName[0]) {
#define PS_CASE(letter, array) \
case letter: \
for (uint32_t i = 0; i < ARRAY_SIZE(array); ++i) { \
if (ffStrEqualsIgnCase(psName, array[i])) { \
return array[i]; \
} \
} \
break;
PS_CASE('a', A)
PS_CASE('c', C)
PS_CASE('e', E)
PS_CASE('g', G)
PS_CASE('l', L)
PS_CASE('p', P)
PS_CASE('s', S)
PS_CASE('t', T)
PS_CASE('x', X)
}
return nullptr;
}
const char* detectByProcesses(FFLMResult* result) {
#if __FreeBSD__
#ifdef __DragonFly__
#define ki_comm kp_comm
#endif
int request[] = { CTL_KERN, KERN_PROC, KERN_PROC_UID, 0 };
size_t length = 0;
if (sysctl(request, ARRAY_SIZE(request), nullptr, &length, nullptr, 0) != 0) {
return "sysctl({CTL_KERN, KERN_PROC, KERN_PROC_UID}, nullptr) failed";
}
FF_AUTO_FREE struct kinfo_proc* procs = (struct kinfo_proc*) malloc(length);
if (sysctl(request, ARRAY_SIZE(request), procs, &length, nullptr, 0) != 0) {
return "sysctl({CTL_KERN, KERN_PROC, KERN_PROC_UID}, procs) failed";
}
length /= sizeof(*procs);
for (struct kinfo_proc* proc = procs; proc < procs + length; ++proc) {
const char* lm = testLms(proc->ki_comm);
if (lm) {
ffStrbufSetStatic(&result->service, lm);
break;
}
}
#elif __OpenBSD__
kvm_t* kd = kvm_open(nullptr, nullptr, nullptr, KVM_NO_FILES, nullptr);
int count = 0;
const struct kinfo_proc* proc = kvm_getprocs(kd, KERN_PROC_UID, 0, sizeof(*proc), &count);
if (proc) {
for (int i = 0; i < count; ++i) {
const char* lm = testLms(proc[i].p_comm);
if (lm) {
ffStrbufSetStatic(&result->service, lm);
break;
}
}
}
kvm_close(kd);
#elif __sun
FF_AUTO_CLOSE_DIR DIR* procdir = opendir("/proc");
if (procdir == nullptr) {
return "opendir(\"/proc\") failed";
}
FF_STRBUF_AUTO_DESTROY procPath = ffStrbufCreateA(64);
ffStrbufAppendS(&procPath, "/proc/");
uint32_t procPathLength = procPath.length;
struct dirent* dirent;
while ((dirent = readdir(procdir)) != nullptr) {
if (!ffCharIsDigit(dirent->d_name[0])) {
continue;
}
ffStrbufAppendS(&procPath, dirent->d_name);
ffStrbufAppendS(&procPath, "/psinfo");
psinfo_t proc;
if (ffReadFileData(procPath.chars, sizeof(proc), &proc) == sizeof(proc)) {
ffStrbufSubstrBefore(&procPath, procPathLength);
if (proc.pr_uid != 0) {
continue;
}
const char* lm = testLms(proc.pr_fname);
if (lm) {
ffStrbufSetS(&result->service, lm);
break;
}
}
}
#elif __linux__ || __GNU__
FF_AUTO_CLOSE_DIR DIR* procdir = opendir("/proc");
if (procdir == nullptr) {
return "opendir(\"/proc\") failed";
}
FF_STRBUF_AUTO_DESTROY procPath = ffStrbufCreateA(64);
int procfd = dirfd(procdir);
struct dirent* dirent;
while ((dirent = readdir(procdir)) != nullptr) {
// Match only folders starting with a number (the pid folders)
if (dirent->d_type != DT_DIR || !ffCharIsDigit(dirent->d_name[0])) {
continue;
}
ffStrbufSetS(&procPath, dirent->d_name);
uint32_t procFolderPathLength = procPath.length;
// Don't check for processes not owned by root (login managers run as root).
char loginuid[32];
ffStrbufAppendS(&procPath, "/loginuid");
ssize_t bytesRead = ffReadFileDataRelative(procfd, procPath.chars, sizeof(loginuid) - 1, loginuid);
if (bytesRead <= 0) {
continue;
}
loginuid[bytesRead] = '\0';
if (strtol(loginuid, nullptr, 10) != (long) (uint32_t) -1 /* no loginuid */) {
continue;
}
ffStrbufSubstrBefore(&procPath, procFolderPathLength);
ffStrbufAppendS(&procPath, "/comm");
char comm[256];
bytesRead = ffReadFileDataRelative(procfd, procPath.chars, sizeof(comm) - 1, comm);
if (bytesRead <= 0) {
continue;
}
if (comm[bytesRead - 1] == '\n') {
--bytesRead;
}
comm[bytesRead] = '\0';
const char* lm = testLms(comm);
if (lm) {
ffStrbufSetS(&result->service, lm);
break;
}
}
#elif __NetBSD__
int request[] = { CTL_KERN, KERN_PROC2, KERN_PROC_UID, 0, sizeof(struct kinfo_proc2), INT_MAX };
size_t size = 0;
if (sysctl(request, ARRAY_SIZE(request), nullptr, &size, nullptr, 0) != 0) {
return "sysctl(KERN_PROC_UID, nullptr) failed";
}
FF_AUTO_FREE struct kinfo_proc2* procs = malloc(size);
if (sysctl(request, ARRAY_SIZE(request), procs, &size, nullptr, 0) != 0) {
return "sysctl(KERN_PROC_UID, procs) failed";
}
for (struct kinfo_proc2* proc = procs; proc < procs + (size / sizeof(struct kinfo_proc2)); proc++) {
const char* lm = testLms(proc->p_comm);
if (lm) {
ffStrbufSetStatic(&result->service, lm);
break;
}
}
#endif
if (result->service.length == 0) {
return "Failed to detect login manager by processes";
}
return nullptr;
}
const char* ffDetectLM(FFLMResult* result) {
const char* error = "";
#if __linux__ && !__ANDROID__
error = detectBySystemd(result);
#endif
if (error != nullptr) {
error = detectByProcesses(result);
}
if (error != nullptr) {
return error;
}
if (instance.config.general.detectVersion) {
if (ffStrbufStartsWithS(&result->service, "gdm")) {
@@ -155,14 +402,5 @@ const char* ffDetectLM(FFLMResult* result) {
}
}
// Correct char cases
if (ffStrbufIgnCaseEqualS(&result->type, FF_WM_PROTOCOL_WAYLAND)) {
ffStrbufSetS(&result->type, FF_WM_PROTOCOL_WAYLAND);
} else if (ffStrbufIgnCaseEqualS(&result->type, FF_WM_PROTOCOL_X11)) {
ffStrbufSetS(&result->type, FF_WM_PROTOCOL_X11);
} else if (ffStrbufIgnCaseEqualS(&result->type, FF_WM_PROTOCOL_TTY)) {
ffStrbufSetS(&result->type, FF_WM_PROTOCOL_TTY);
}
return nullptr;
}
-10
View File
@@ -8,7 +8,6 @@ bool ffPrintLM(FFLMOptions* options) {
bool success = false;
FFLMResult result;
ffStrbufInit(&result.service);
ffStrbufInit(&result.type);
ffStrbufInit(&result.version);
const char* error = ffDetectLM(&result);
@@ -28,14 +27,10 @@ bool ffPrintLM(FFLMOptions* options) {
if (result.version.length) {
printf(" %s", result.version.chars);
}
if (result.type.length) {
printf(" (%s)", result.type.chars);
}
putchar('\n');
} else {
FF_PRINT_FORMAT_CHECKED(FF_MODULE_GET_DISPLAY_NAME(LM), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) {
FF_ARG(result.service, "service"),
FF_ARG(result.type, "type"),
FF_ARG(result.version, "version"),
}));
}
@@ -43,7 +38,6 @@ bool ffPrintLM(FFLMOptions* options) {
exit:
ffStrbufDestroy(&result.service);
ffStrbufDestroy(&result.type);
ffStrbufDestroy(&result.version);
return success;
@@ -69,7 +63,6 @@ bool ffGenerateLMJsonResult([[maybe_unused]] FFLMOptions* options, yyjson_mut_do
bool success = false;
FFLMResult result;
ffStrbufInit(&result.service);
ffStrbufInit(&result.type);
ffStrbufInit(&result.version);
const char* error = ffDetectLM(&result);
@@ -85,13 +78,11 @@ bool ffGenerateLMJsonResult([[maybe_unused]] FFLMOptions* options, yyjson_mut_do
yyjson_mut_val* obj = yyjson_mut_obj_add_obj(doc, module, "result");
yyjson_mut_obj_add_strbuf(doc, obj, "service", &result.service);
yyjson_mut_obj_add_strbuf(doc, obj, "type", &result.type);
yyjson_mut_obj_add_strbuf(doc, obj, "version", &result.version);
success = true;
exit:
ffStrbufDestroy(&result.service);
ffStrbufDestroy(&result.type);
ffStrbufDestroy(&result.version);
return success;
@@ -138,7 +129,6 @@ FFModuleBaseInfo ffLMModuleInfo = {
.generateJsonConfig = (void*) ffGenerateLMJsonConfig,
.formatArgs = FF_FORMAT_ARG_LIST(((FFModuleFormatArg[]) {
{ "LM service", "service" },
{ "LM type", "type" },
{ "LM version", "version" },
})),
.defaultOrder = 20,