mirror of
https://github.com/fastfetch-cli/fastfetch.git
synced 2026-10-29 16:26:39 +01:00
205 lines
6.8 KiB
C
205 lines
6.8 KiB
C
#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;
|
|
}
|