diff --git a/CMakeLists.txt b/CMakeLists.txt index 7f4605509..33e72853b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 diff --git a/src/detection/lm/lm.h b/src/detection/lm/lm.h index d30229a79..8bedf8e5c 100644 --- a/src/detection/lm/lm.h +++ b/src/detection/lm/lm.h @@ -5,7 +5,6 @@ typedef struct FFLMResult { FFstrbuf service; - FFstrbuf type; FFstrbuf version; } FFLMResult; diff --git a/src/detection/lm/lm_linux.c b/src/detection/lm/lm_linux.c index 1623c7277..d208a2641 100644 --- a/src/detection/lm/lm_linux.c +++ b/src/detection/lm/lm_linux.c @@ -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 +#if __FreeBSD__ + #include + #include + #include +#elif __OpenBSD__ + #include + #include + #include +#elif __sun + #include +#elif __NetBSD__ + #include + #include +#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; } diff --git a/src/modules/lm/lm.c b/src/modules/lm/lm.c index ca9debe11..2ce541eed 100644 --- a/src/modules/lm/lm.c +++ b/src/modules/lm/lm.c @@ -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,