summaryrefslogtreecommitdiffstats
path: root/src/detection/lm/lm_linux.c
diff options
context:
space:
mode:
authorsumuel <samuel@yakubos.org>2026-08-17 20:44:55 +0000
committersumuel <samuel@yakubos.org>2026-08-17 20:44:55 +0000
commit76424950e373d3b04ac3dd13019151bfba3e8423 (patch)
tree4c3cfdbda039e592b9186be3e28f8d7cfd439e8a /src/detection/lm/lm_linux.c
Add the files
Diffstat (limited to 'src/detection/lm/lm_linux.c')
-rw-r--r--src/detection/lm/lm_linux.c168
1 files changed, 168 insertions, 0 deletions
diff --git a/src/detection/lm/lm_linux.c b/src/detection/lm/lm_linux.c
new file mode 100644
index 0000000..140205d
--- /dev/null
+++ b/src/detection/lm/lm_linux.c
@@ -0,0 +1,168 @@
+#include "lm.h"
+#include "common/properties.h"
+#include "common/dbus.h"
+#include "common/processing.h"
+#include "detection/displayserver/displayserver.h"
+
+#include <unistd.h>
+
+#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", NULL });
+ if (error || version->length == 0) {
+ error = ffProcessAppendStdOut(version, (char* const[]) { "gdm3", "--version", NULL });
+ if (error || version->length == 0) {
+ return "Failed to get GDM version";
+ }
+ }
+
+ // GDM 44.1
+ ffStrbufSubstrAfterFirstC(version, ' ');
+ return NULL;
+}
+
+static const char* getSshdVersion(FFstrbuf* version) {
+ const char* error = ffProcessAppendStdErr(version, (char* const[]) { "sshd", "-V", NULL });
+ if (error) {
+ return error;
+ }
+
+ // OpenSSH_9.0p1, OpenSSL 3.0.9 30 May 2023...
+ ffStrbufSubstrBeforeFirstC(version, ',');
+ ffStrbufSubstrAfterFirstC(version, '_');
+ return NULL;
+}
+
+#ifdef FF_HAVE_ZLIB
+ #include "common/library.h"
+ #include <stdlib.h>
+ #include <zlib.h>
+
+static const char* getSddmVersion(FFstrbuf* version) {
+ FF_LIBRARY_LOAD_MESSAGE(zlib, "libz" FF_LIBRARY_EXTENSION, 2)
+ FF_LIBRARY_LOAD_SYMBOL_MESSAGE(zlib, gzopen)
+ FF_LIBRARY_LOAD_SYMBOL_MESSAGE(zlib, gzread)
+ FF_LIBRARY_LOAD_SYMBOL_MESSAGE(zlib, gzerror)
+ FF_LIBRARY_LOAD_SYMBOL_MESSAGE(zlib, gztell)
+ FF_LIBRARY_LOAD_SYMBOL_MESSAGE(zlib, gzrewind)
+ FF_LIBRARY_LOAD_SYMBOL_MESSAGE(zlib, gzclose)
+
+ gzFile file = ffgzopen(FASTFETCH_TARGET_DIR_USR "/share/man/man1/sddm.1.gz", "rb");
+ if (file == Z_NULL) {
+ return "ffgzopen(\"/usr/share/man/man1/sddm.1.gz\", \"rb\") failed";
+ }
+
+ ffStrbufEnsureFree(version, 2047);
+ memset(version->chars, 0, version->allocated);
+ int size = ffgzread(file, version->chars, version->allocated - 1);
+ ffgzclose(file);
+
+ if (size <= 0) {
+ return "ffgzread(file, version->chars, version->length) failed";
+ }
+
+ version->length = (uint32_t) size;
+ uint32_t index = ffStrbufFirstIndexS(version, ".TH ");
+ if (index == version->length) {
+ ffStrbufClear(version);
+ return ".TH is not found";
+ }
+
+ ffStrbufSubstrBefore(version, ffStrbufNextIndexC(version, index, '\n'));
+ ffStrbufSubstrAfter(version, index + (uint32_t) strlen(".TH "));
+
+ // "SDDM" 1 "May 2014" "sddm 0.20.0" "sddm"
+ ffStrbufSubstrBeforeLastC(version, ' ');
+ ffStrbufTrimRight(version, '"');
+ ffStrbufSubstrAfterLastC(version, ' ');
+
+ return NULL;
+}
+#else
+static const char* getSddmVersion(FF_A_UNUSED FFstrbuf* version) {
+ return "Fastfetch is built without libz support";
+}
+#endif
+
+static const char* getXfwmVersion(FFstrbuf* version) {
+ const char* error = ffProcessAppendStdOut(version, (char* const[]) { "xfwm4", "--version", NULL });
+ if (error) {
+ return error;
+ }
+
+ // This is xfwm4 version 4.18.0 (revision 7e7473c5b) for Xfce 4.18...
+ ffStrbufSubstrAfterFirstS(version, "version ");
+ ffStrbufSubstrBeforeFirstC(version, ' ');
+
+ return NULL;
+}
+
+static const char* getLightdmVersion(FFstrbuf* version) {
+ const char* error = ffProcessAppendStdErr(version, (char* const[]) { "lightdm", "--version", NULL });
+ if (error) {
+ return error;
+ }
+
+ // lightdm 1.30.0
+ ffStrbufSubstrAfterFirstC(version, ' ');
+ ffStrbufTrimRight(version, '\n');
+
+ return NULL;
+}
+
+const char* ffDetectLM(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);
+
+ // 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[]) {
+ { "DISPLAY=", &sessionId },
+ })) {
+ return "Failed to get $XDG_SESSION_ID";
+ }
+ }
+
+ ffStrbufSetS(&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 },
+ })) {
+ return "Failed to parse " FF_SYSTEMD_SESSIONS_PATH "$XDG_SESSION_ID";
+ }
+
+ if (instance.config.general.detectVersion) {
+ if (ffStrbufStartsWithS(&result->service, "gdm")) {
+ getGdmVersion(&result->version);
+ } else if (ffStrbufStartsWithS(&result->service, "sddm")) {
+ getSddmVersion(&result->version);
+ } else if (ffStrbufStartsWithS(&result->service, "xfwm")) {
+ getXfwmVersion(&result->version);
+ } else if (ffStrbufStartsWithS(&result->service, "lightdm")) {
+ getLightdmVersion(&result->version);
+ } else if (ffStrbufStartsWithS(&result->service, "sshd")) {
+ getSshdVersion(&result->version);
+ }
+ }
+
+ // 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 NULL;
+}