From 3a04ae254279640ab97e8f10d9b2c06685cf7e74 Mon Sep 17 00:00:00 2001 From: Carter Li Date: Tue, 25 Nov 2025 16:40:52 +0800 Subject: [PATCH] Users (Linux): adds systemd fallback for user detection on Linux Fixes #2064 --- src/detection/users/users_linux.c | 124 +++++++++++++++++++++++++++++- 1 file changed, 122 insertions(+), 2 deletions(-) diff --git a/src/detection/users/users_linux.c b/src/detection/users/users_linux.c index 8b828d2d4..b7f3a4018 100644 --- a/src/detection/users/users_linux.c +++ b/src/detection/users/users_linux.c @@ -1,6 +1,10 @@ +#include "common/io/io.h" +#include "common/properties.h" #include "fastfetch.h" #include "users.h" +#include + #if FF_HAVE_UTMPX #include #else @@ -15,7 +19,111 @@ #include #endif -const char* ffDetectUsers(FFUsersOptions* options, FFlist* users) +#if __linux__ +bool detectUserBySystemd(const FFstrbuf* pathUsers, FFlist* users) +{ + FF_STRBUF_AUTO_DESTROY state = ffStrbufCreate(); + FF_STRBUF_AUTO_DESTROY userName = ffStrbufCreate(); + FF_STRBUF_AUTO_DESTROY loginTime = ffStrbufCreate(); + FF_STRBUF_AUTO_DESTROY sessions = ffStrbufCreate(); + + // WARNING: This is private data. Do not parse + if (!ffParsePropFileValues(pathUsers->chars, 4, (FFpropquery[]) { + {"NAME=", &userName}, + {"STATE=", &state}, + {"REALTIME=", &loginTime}, + {"ONLINE_SESSIONS=", &sessions}, + }) || !ffStrbufEqualS(&state, "active")) + return false; + + FFUserResult* user = FF_LIST_ADD(FFUserResult, *users); + ffStrbufInitMove(&user->name, &userName); + ffStrbufInit(&user->hostName); + ffStrbufInit(&user->sessionName); + ffStrbufInit(&user->clientIp); + ffStrbufSubstrBefore(&loginTime, loginTime.length - 3); // converts us to ms + user->loginTime = ffStrbufToUInt(&loginTime, 0); + + FF_STRBUF_AUTO_DESTROY pathSessions = ffStrbufCreateS("/run/systemd/sessions/"); + const uint32_t pathSessionsBaseLen = pathSessions.length; + + FF_STRBUF_AUTO_DESTROY tty = ffStrbufCreate(); + FF_STRBUF_AUTO_DESTROY remoteHost = ffStrbufCreate(); + FF_STRBUF_AUTO_DESTROY service = ffStrbufCreate(); + + char* token = NULL; + size_t n = 0; + while (ffStrbufGetdelim(&token, &n, ' ', &sessions)) + { + ffStrbufSubstrBefore(&pathSessions, pathSessionsBaseLen); + ffStrbufAppendS(&pathSessions, token); + + ffStrbufClear(&remoteHost); + ffStrbufClear(&service); + ffStrbufClear(&tty); + ffStrbufClear(&loginTime); + + // WARNING: This is private data. Do not parse + if (ffParsePropFileValues(pathSessions.chars, 4, (FFpropquery[]) { + {"REMOTE_HOST=", &remoteHost}, + {"TTY=", &tty}, + {"SERVICE=", &service}, + {"REALTIME=", &loginTime}, + }) && !ffStrbufEqualS(&service, "systemd-user")) + { + if (remoteHost.length) + { + ffStrbufTrimRight(&remoteHost, ']'); + ffStrbufTrimLeft(&remoteHost, '['); + ffStrbufInitMove(&user->hostName, &remoteHost); + } + else + ffStrbufSetStatic(&user->hostName, "localhost"); + ffStrbufInitMove(&user->sessionName, tty.length ? &tty : &service); + if (loginTime.length) + { + ffStrbufSubstrBefore(&loginTime, loginTime.length - 3); // converts us to ms + user->loginTime = ffStrbufToUInt(&loginTime, 0); + } + break; + } + } + + return true; +} + +const char* detectBySystemd(FFUsersOptions* options, FFlist* users) +{ + // For some reason, debian/ubuntu no longer updates `/var/run/utmp` (#2064) + // Query systemd instead + FF_STRBUF_AUTO_DESTROY pathUsers = ffStrbufCreateS("/run/systemd/users/"); + + if (options->myselfOnly) + { + ffStrbufAppendUInt(&pathUsers, getuid()); + detectUserBySystemd(&pathUsers, users); + } + else + { + const uint32_t pathUsersBaseLen = pathUsers.length; + FF_AUTO_CLOSE_DIR DIR* dirp = opendir(pathUsers.chars); + if (!dirp) return "opendir(\"/run/systemd/users/\") failed"; + + struct dirent* entry; + while ((entry = readdir(dirp))) + { + if (entry->d_type != DT_REG) continue; + + ffStrbufAppendS(&pathUsers, entry->d_name); + detectUserBySystemd(&pathUsers, users); + ffStrbufSubstrBefore(&pathUsers, pathUsersBaseLen); + } + } + return NULL; +} +#endif + +const char* detectByUtmp(FFUsersOptions* options, FFlist* users) { struct utmpx* n = NULL; setutxent(); @@ -35,7 +143,7 @@ next: goto next; } - FFUserResult* user = (FFUserResult*) ffListAdd(users); + FFUserResult* user = FF_LIST_ADD(FFUserResult, *users); ffStrbufInitS(&user->name, n->ut_user); ffStrbufInitS(&user->hostName, n->ut_host); ffStrbufInitS(&user->sessionName, n->ut_line); @@ -68,3 +176,15 @@ next: return NULL; } + +const char* ffDetectUsers(FFUsersOptions* options, FFlist* users) +{ + const char* err = detectByUtmp(options, users); + if (err) return err; + + #if __linux__ + if (users->length == 0) detectBySystemd(options, users); + #endif + + return NULL; +}