summaryrefslogtreecommitdiffstats
path: root/src/detection/users
diff options
context:
space:
mode:
Diffstat (limited to 'src/detection/users')
-rw-r--r--src/detection/users/users.h14
-rw-r--r--src/detection/users/users_linux.c204
-rw-r--r--src/detection/users/users_nosupport.c6
-rw-r--r--src/detection/users/users_obsd.c39
-rw-r--r--src/detection/users/users_windows.c61
5 files changed, 324 insertions, 0 deletions
diff --git a/src/detection/users/users.h b/src/detection/users/users.h
new file mode 100644
index 0000000..8f24cc0
--- /dev/null
+++ b/src/detection/users/users.h
@@ -0,0 +1,14 @@
+#pragma once
+
+#include "fastfetch.h"
+#include "modules/users/option.h"
+
+typedef struct FFUserResult {
+ FFstrbuf name;
+ FFstrbuf hostName;
+ FFstrbuf clientIp;
+ FFstrbuf sessionName;
+ uint64_t loginTime; // ms
+} FFUserResult;
+
+const char* ffDetectUsers(FFUsersOptions* options, FFlist* users);
diff --git a/src/detection/users/users_linux.c b/src/detection/users/users_linux.c
new file mode 100644
index 0000000..bc84eed
--- /dev/null
+++ b/src/detection/users/users_linux.c
@@ -0,0 +1,204 @@
+#include "common/io.h"
+#include "common/properties.h"
+#include "fastfetch.h"
+#include "users.h"
+
+#include <unistd.h>
+
+#if FF_HAVE_UTMPX
+ #include <utmpx.h>
+#else
+ // for Android compatibility
+ #include <utmp.h>
+ #define utmpx utmp
+ #define setutxent setutent
+ #define getutxent getutent
+#endif
+#if __linux__ || __GNU__
+ #include <netinet/in.h>
+ #include <arpa/inet.h>
+#endif
+
+#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, instance.state.platform.uid);
+ 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
+
+#if __linux__ || __GNU__
+static void fillUtmpIpAddr(FFUserResult* user, struct utmpx* n) {
+ bool isIpv6 = false;
+ for (int i = 1; i < 4; ++i) {
+ if (n->ut_addr_v6[i] != 0) {
+ isIpv6 = true;
+ break;
+ }
+ }
+
+ if (isIpv6) {
+ char ipv6_str[INET6_ADDRSTRLEN];
+ if (inet_ntop(AF_INET6, n->ut_addr_v6, ipv6_str, INET6_ADDRSTRLEN) != NULL) {
+ ffStrbufSetS(&user->clientIp, ipv6_str);
+ }
+ } else if (n->ut_addr_v6[0] != 0) {
+ char ipv4_str[INET_ADDRSTRLEN];
+ if (inet_ntop(AF_INET, n->ut_addr_v6, ipv4_str, INET_ADDRSTRLEN) != NULL) {
+ ffStrbufSetS(&user->clientIp, ipv4_str);
+ }
+ }
+}
+#else
+static void fillUtmpIpAddr(FF_A_UNUSED FFUserResult* user, FF_A_UNUSED struct utmpx* n) {
+}
+#endif
+
+const char* detectByUtmp(FFUsersOptions* options, FFlist* users) {
+ struct utmpx* n = NULL;
+ setutxent();
+
+next:
+ while ((n = getutxent())) {
+ if (n->ut_type != USER_PROCESS) {
+ continue;
+ }
+
+ if (options->myselfOnly && !ffStrbufEqualS(&instance.state.platform.userName, n->ut_user)) {
+ continue;
+ }
+
+ FF_LIST_FOR_EACH (FFUserResult, user, *users) {
+ if (ffStrbufEqualS(&user->name, n->ut_user)) {
+ uint64_t newLoginTime = (uint64_t) n->ut_tv.tv_sec * 1000 + (uint64_t) n->ut_tv.tv_usec / 1000;
+ if (newLoginTime > user->loginTime) {
+ ffStrbufSetS(&user->hostName, n->ut_host);
+ ffStrbufSetS(&user->sessionName, n->ut_line);
+ fillUtmpIpAddr(user, n);
+ user->loginTime = newLoginTime;
+ }
+ goto next;
+ }
+ }
+
+ 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);
+ ffStrbufInit(&user->clientIp);
+ fillUtmpIpAddr(user, n);
+ user->loginTime = (uint64_t) n->ut_tv.tv_sec * 1000 + (uint64_t) n->ut_tv.tv_usec / 1000;
+ }
+
+ endutxent();
+
+ 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;
+}
diff --git a/src/detection/users/users_nosupport.c b/src/detection/users/users_nosupport.c
new file mode 100644
index 0000000..0a7e30a
--- /dev/null
+++ b/src/detection/users/users_nosupport.c
@@ -0,0 +1,6 @@
+#include "fastfetch.h"
+#include "users.h"
+
+const char* ffDetectUsers(FFUsersOptions* options, FFlist* users) {
+ return "Not supported on this platform";
+}
diff --git a/src/detection/users/users_obsd.c b/src/detection/users/users_obsd.c
new file mode 100644
index 0000000..97a22f0
--- /dev/null
+++ b/src/detection/users/users_obsd.c
@@ -0,0 +1,39 @@
+#include "fastfetch.h"
+#include "users.h"
+#include "common/io.h"
+
+#include <utmp.h>
+
+const char* ffDetectUsers(FF_A_UNUSED FFUsersOptions* options, FFlist* users) {
+ FF_AUTO_CLOSE_FILE FILE* fp = fopen(_PATH_UTMP, "r");
+ if (!fp) {
+ return "fopen(_PATH_UTMP, r) failed";
+ }
+
+ struct utmp n;
+next:
+ while (fread(&n, sizeof(n), 1, fp) == 1) {
+ if (!n.ut_name[0]) {
+ continue;
+ }
+
+ if (options->myselfOnly && !ffStrbufEqualS(&instance.state.platform.userName, n.ut_name)) {
+ continue;
+ }
+
+ FF_LIST_FOR_EACH (FFUserResult, user, *users) {
+ if (ffStrbufEqualS(&user->name, n.ut_name)) {
+ goto next;
+ }
+ }
+
+ FFUserResult* user = FF_LIST_ADD(FFUserResult, *users);
+ ffStrbufInitS(&user->name, n.ut_name);
+ ffStrbufInitS(&user->hostName, n.ut_host);
+ ffStrbufInitS(&user->sessionName, n.ut_line);
+ ffStrbufInit(&user->clientIp);
+ user->loginTime = (uint64_t) n.ut_time * 1000;
+ }
+
+ return NULL;
+}
diff --git a/src/detection/users/users_windows.c b/src/detection/users/users_windows.c
new file mode 100644
index 0000000..4cde44e
--- /dev/null
+++ b/src/detection/users/users_windows.c
@@ -0,0 +1,61 @@
+#include "users.h"
+#include "common/windows/unicode.h"
+#include "common/time.h"
+
+#include <windows.h>
+#include <wtsapi32.h>
+#include <ws2tcpip.h>
+
+const char* ffDetectUsers(FFUsersOptions* options, FFlist* users) {
+ WTS_SESSION_INFO_1W* sessionInfo;
+ DWORD sessionCount;
+ DWORD level = 1;
+
+ if (!WTSEnumerateSessionsExW(WTS_CURRENT_SERVER_HANDLE, &level, 0, &sessionInfo, &sessionCount)) {
+ return "WTSEnumerateSessionsW(WTS_CURRENT_SERVER_HANDLE) failed";
+ }
+
+ for (DWORD i = 0; i < sessionCount; i++) {
+ WTS_SESSION_INFO_1W* session = &sessionInfo[i];
+ if (session->State != WTSActive) {
+ continue;
+ }
+
+ FF_STRBUF_AUTO_DESTROY userName = ffStrbufCreateWS(session->pUserName);
+
+ if (options->myselfOnly && !ffStrbufEqual(&instance.state.platform.userName, &userName)) {
+ continue;
+ }
+
+ FFUserResult* user = FF_LIST_ADD(FFUserResult, *users);
+ ffStrbufInitMove(&user->name, &userName);
+ ffStrbufInitWS(&user->hostName, session->pHostName);
+ ffStrbufInitWS(&user->sessionName, session->pSessionName);
+ ffStrbufInit(&user->clientIp);
+ user->loginTime = 0;
+
+ DWORD bytes = 0;
+ PWTS_CLIENT_ADDRESS address = NULL;
+ if (WTSQuerySessionInformationW(WTS_CURRENT_SERVER_HANDLE, session->SessionId, WTSClientAddress, (LPWSTR*) &address, &bytes)) {
+ if (address->AddressFamily == AF_INET) {
+ ffStrbufSetF(&user->clientIp, "%u.%u.%u.%u", address->Address[2], address->Address[3], address->Address[4], address->Address[5]);
+ } else if (address->AddressFamily == AF_INET6) {
+ char ipStr[INET6_ADDRSTRLEN];
+ const char* end = RtlIpv6AddressToStringA((const IN6_ADDR*) address->Address, ipStr);
+ ffStrbufSetNS(&user->clientIp, (uint32_t) (end - ipStr), ipStr);
+ }
+ WTSFreeMemory(address);
+ }
+
+ bytes = 0;
+ PWTSINFOW wtsInfo = NULL;
+ if (WTSQuerySessionInformationW(WTS_CURRENT_SERVER_HANDLE, session->SessionId, WTSSessionInfo, (LPWSTR*) &wtsInfo, &bytes)) {
+ user->loginTime = ffFileTimeToUnixMs((uint64_t) wtsInfo->LogonTime.QuadPart);
+ WTSFreeMemory(wtsInfo);
+ }
+ }
+
+ WTSFreeMemoryExW(WTSTypeSessionInfoLevel1, sessionInfo, 1);
+
+ return NULL;
+}