diff --git a/src/common/format.c b/src/common/format.c index 4ed096dc1..8ccf09fa3 100644 --- a/src/common/format.c +++ b/src/common/format.c @@ -12,6 +12,8 @@ void ffFormatAppendFormatArg(FFstrbuf* buffer, const FFformatarg* formatarg) ffStrbufAppendF(buffer, "%i", *(int*)formatarg->value); else if(formatarg->type == FF_FORMAT_ARG_TYPE_UINT) ffStrbufAppendF(buffer, "%" PRIu32, *(uint32_t*)formatarg->value); + else if(formatarg->type == FF_FORMAT_ARG_TYPE_UINT64) + ffStrbufAppendF(buffer, "%" PRIu64, *(uint64_t*)formatarg->value); else if(formatarg->type == FF_FORMAT_ARG_TYPE_UINT16) ffStrbufAppendF(buffer, "%" PRIu16, *(uint16_t*)formatarg->value); else if(formatarg->type == FF_FORMAT_ARG_TYPE_UINT8) diff --git a/src/common/format.h b/src/common/format.h index 57c8c680f..3e7c751f2 100644 --- a/src/common/format.h +++ b/src/common/format.h @@ -7,6 +7,7 @@ typedef enum FFformatargtype { FF_FORMAT_ARG_TYPE_NULL = 0, FF_FORMAT_ARG_TYPE_UINT, + FF_FORMAT_ARG_TYPE_UINT64, FF_FORMAT_ARG_TYPE_UINT16, FF_FORMAT_ARG_TYPE_UINT8, FF_FORMAT_ARG_TYPE_INT, diff --git a/src/detection/users/users.h b/src/detection/users/users.h index 8842e16a2..7248d8322 100644 --- a/src/detection/users/users.h +++ b/src/detection/users/users.h @@ -5,6 +5,15 @@ #include "fastfetch.h" -void ffDetectUsers(FFlist* users /* List of FFstrbuf */, FFstrbuf* error); +typedef struct FFUserResult +{ + FFstrbuf name; + FFstrbuf hostname; + FFstrbuf clientIp; + FFstrbuf tty; + uint64_t loginTime; // ms +} FFUserResult; + +const char* ffDetectUsers(FFlist* users); #endif diff --git a/src/detection/users/users_linux.c b/src/detection/users/users_linux.c index c17bf139c..dbad1255d 100644 --- a/src/detection/users/users_linux.c +++ b/src/detection/users/users_linux.c @@ -11,7 +11,7 @@ #define getutxent getutent #endif -void ffDetectUsers(FFlist* users, FFstrbuf* error) +const char* ffDetectUsers(FFlist* users) { struct utmpx* n = NULL; setutxent(); @@ -22,15 +22,25 @@ next: if(n->ut_type != USER_PROCESS) continue; - FF_LIST_FOR_EACH(FFstrbuf, user, *users) + FF_LIST_FOR_EACH(FFUserResult, user, *users) { - if(ffStrbufEqualS(user, n->ut_user)) + if(ffStrbufEqualS(&user->name, n->ut_user)) goto next; } - ffStrbufInitS((FFstrbuf*)ffListAdd(users), n->ut_user); + FFUserResult* user = (FFUserResult*) ffListAdd(users); + ffStrbufInitS(&user->name, n->ut_user); + ffStrbufInitS(&user->hostname, n->ut_host); + ffStrbufInitS(&user->tty, n->ut_line); + #ifdef __linux__ + if(n->ut_addr_v6[0] || n->ut_addr_v6[1] || n->ut_addr_v6[2] || n->ut_addr_v6[3]) + ffStrbufInitF(&user->clientIp, "%u.%u.%u.%u", n->ut_addr_v6[0], n->ut_addr_v6[1], n->ut_addr_v6[2], n->ut_addr_v6[3]); + else + #else + ffStrbufInit(&user->clientIp); + #endif + user->loginTime = (uint64_t) n->ut_tv.tv_sec * 1000 + (uint64_t) n->ut_tv.tv_usec / 1000; } - if(users->length == 0) - ffStrbufAppendS(error, "Unable to detect users"); + return NULL; } diff --git a/src/detection/users/users_windows.c b/src/detection/users/users_windows.c index 01fab40a7..6ef912670 100644 --- a/src/detection/users/users_windows.c +++ b/src/detection/users/users_windows.c @@ -4,17 +4,20 @@ #include #include -void ffDetectUsers(FFlist* users, FFstrbuf* error) +static uint64_t to_ms(uint64_t ret) +{ + ret -= 116444736000000000ull; + return ret / 10000ull; +} + +const char* ffDetectUsers(FFlist* users) { WTS_SESSION_INFO_1W* sessionInfo; DWORD sessionCount; DWORD level = 1; if(!WTSEnumerateSessionsExW(WTS_CURRENT_SERVER_HANDLE, &level, 0, &sessionInfo, &sessionCount)) - { - ffStrbufAppendS(error, "WTSEnumerateSessionsW(WTS_CURRENT_SERVER_HANDLE) failed"); - return; - } + return "WTSEnumerateSessionsW(WTS_CURRENT_SERVER_HANDLE) failed"; for (DWORD i = 0; i < sessionCount; i++) { @@ -22,14 +25,33 @@ void ffDetectUsers(FFlist* users, FFstrbuf* error) if(session->State != WTSActive) continue; - FF_STRBUF_AUTO_DESTROY domainName = ffStrbufCreateWS(session->pDomainName); - FF_STRBUF_AUTO_DESTROY userName = ffStrbufCreateWS(session->pUserName); + FFUserResult* user = (FFUserResult*) ffListAdd(users); + ffStrbufInitWS(&user->name, session->pUserName); + ffStrbufInitWS(&user->hostname, session->pHostName); - ffStrbufInitF((FFstrbuf*)ffListAdd(users), "%s\\%s", domainName.chars, userName.chars); + DWORD bytes = 0; + PWTS_CLIENT_ADDRESS address = NULL; + if (WTSQuerySessionInformationW(WTS_CURRENT_SERVER_HANDLE, session->SessionId, WTSClientAddress, (LPWSTR *) &address, &bytes)) + { + if (address->AddressFamily == 2 /*AF_INET*/) + ffStrbufInitF(&user->clientIp, "%u.%u.%u.%u", address->Address[2], address->Address[3], address->Address[4], address->Address[5]); + WTSFreeMemory(address); + } + else + ffStrbufInitS(&user->clientIp, "0.0.0.0"); + + bytes = 0; + PWTSINFOW wtsInfo = NULL; + if (WTSQuerySessionInformationW(WTS_CURRENT_SERVER_HANDLE, session->SessionId, WTSSessionInfo, (LPWSTR *) &wtsInfo, &bytes)) + { + user->loginTime = to_ms(*(uint64_t*) &wtsInfo->LogonTime); + WTSFreeMemory(wtsInfo); + } + else + user->loginTime = 0; } WTSFreeMemoryExW(WTSTypeSessionInfoLevel1, sessionInfo, 1); - if(users->length == 0) - ffStrbufAppendS(error, "Unable to detect users"); + return NULL; } diff --git a/src/modules/users/option.h b/src/modules/users/option.h index 80bf87698..6883e8dd0 100644 --- a/src/modules/users/option.h +++ b/src/modules/users/option.h @@ -8,4 +8,6 @@ typedef struct FFUsersOptions { FFModuleBaseInfo moduleInfo; FFModuleArgs moduleArgs; + + bool compact; } FFUsersOptions; diff --git a/src/modules/users/users.c b/src/modules/users/users.c index 6c6387082..764e73811 100644 --- a/src/modules/users/users.c +++ b/src/modules/users/users.c @@ -4,42 +4,90 @@ #include "modules/users/users.h" #include "util/stringUtils.h" +#include + #define FF_USERS_NUM_FORMAT_ARGS 1 void ffPrintUsers(FFUsersOptions* options) { - FF_LIST_AUTO_DESTROY users = ffListCreate(sizeof(FFstrbuf)); + FF_LIST_AUTO_DESTROY users = ffListCreate(sizeof(FFUserResult)); - FF_STRBUF_AUTO_DESTROY error = ffStrbufCreate(); + const char* error = ffDetectUsers(&users); - ffDetectUsers(&users, &error); - - if(error.length > 0) + if(error) { - ffPrintError(FF_USERS_MODULE_NAME, 0, &options->moduleArgs, "%*s", error.length, error.chars); + ffPrintError(FF_USERS_MODULE_NAME, 0, &options->moduleArgs, "%s", error); return; } - FF_STRBUF_AUTO_DESTROY result = ffStrbufCreate(); - for(uint32_t i = 0; i < users.length; ++i) + if(users.length == 0) { - if(i > 0) - ffStrbufAppendS(&result, ", "); - FFstrbuf* user = (FFstrbuf*)ffListGet(&users, i); - ffStrbufAppend(&result, user); - ffStrbufDestroy(user); + ffPrintError(FF_USERS_MODULE_NAME, 0, &options->moduleArgs, "%s", "Unable to detect any users"); + return; } if(options->moduleArgs.outputFormat.length == 0) { - ffPrintLogoAndKey(FF_USERS_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); - puts(result.chars); + if(options->compact) + { + ffPrintLogoAndKey(FF_USERS_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); + + FF_STRBUF_AUTO_DESTROY result = ffStrbufCreate(); + for(uint32_t i = 0; i < users.length; ++i) + { + if(i > 0) + ffStrbufAppendS(&result, ", "); + FFUserResult* user = (FFUserResult*)ffListGet(&users, i); + ffStrbufAppend(&result, &user->name); + } + ffStrbufPutTo(&result, stdout); + } + else + { + for(uint32_t i = 0; i < users.length; ++i) + { + FFUserResult* user = (FFUserResult*)ffListGet(&users, i); + + ffPrintLogoAndKey(FF_USERS_MODULE_NAME, users.length == 1 ? 0 : (uint8_t) (i + 1), &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); + + FF_STRBUF_AUTO_DESTROY result = ffStrbufCreateCopy(&user->name); + if(user->hostname.length) + ffStrbufAppendF(&result, "@%s", user->hostname.chars); + + if(user->loginTime) + { + char buf[64]; + time_t time = (time_t) (user->loginTime / 1000); + strftime(buf, sizeof(buf), "%FT%T%z", localtime(&time)); + ffStrbufAppendF(&result, " - login time %s", buf); + } + + ffStrbufPutTo(&result, stdout); + } + } } else { - ffPrintFormat(FF_USERS_MODULE_NAME, 0, &options->moduleArgs, FF_USERS_NUM_FORMAT_ARGS, (FFformatarg[]){ - {FF_FORMAT_ARG_TYPE_STRBUF, &result}, - }); + for(uint32_t i = 0; i < users.length; ++i) + { + FFUserResult* user = (FFUserResult*)ffListGet(&users, i); + + ffPrintFormat(FF_USERS_MODULE_NAME, users.length == 1 ? 0 : (uint8_t) (i + 1), &options->moduleArgs, FF_USERS_NUM_FORMAT_ARGS, (FFformatarg[]){ + {FF_FORMAT_ARG_TYPE_STRBUF, &user->name}, + {FF_FORMAT_ARG_TYPE_STRBUF, &user->hostname}, + {FF_FORMAT_ARG_TYPE_STRBUF, &user->tty}, + {FF_FORMAT_ARG_TYPE_STRBUF, &user->clientIp}, + {FF_FORMAT_ARG_TYPE_UINT64, &user->loginTime}, + }); + } + } + + FF_LIST_FOR_EACH(FFUserResult, user, users) + { + ffStrbufDestroy(&user->clientIp); + ffStrbufDestroy(&user->hostname); + ffStrbufDestroy(&user->tty); + ffStrbufDestroy(&user->name); } } @@ -47,6 +95,8 @@ void ffInitUsersOptions(FFUsersOptions* options) { ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_USERS_MODULE_NAME, ffParseUsersCommandOptions, ffParseUsersJsonObject, ffPrintUsers, NULL); ffOptionInitModuleArg(&options->moduleArgs); + + options->compact = true; } bool ffParseUsersCommandOptions(FFUsersOptions* options, const char* key, const char* value) @@ -56,6 +106,12 @@ bool ffParseUsersCommandOptions(FFUsersOptions* options, const char* key, const if (ffOptionParseModuleArgs(key, subKey, value, &options->moduleArgs)) return true; + if(ffStrEqualsIgnCase(subKey, "compact")) + { + options->compact = ffOptionParseBoolean(value); + return true; + } + return false; } @@ -77,6 +133,12 @@ void ffParseUsersJsonObject(FFUsersOptions* options, yyjson_val* module) if (ffJsonConfigParseModuleArgs(key, val, &options->moduleArgs)) continue; + if (ffStrEqualsIgnCase(key, "compact")) + { + options->compact = yyjson_get_bool(val); + continue; + } + ffPrintError(FF_USERS_MODULE_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); } } diff --git a/src/util/windows/unicode.h b/src/util/windows/unicode.h index 6892134dc..a25d4a7b4 100644 --- a/src/util/windows/unicode.h +++ b/src/util/windows/unicode.h @@ -10,6 +10,7 @@ void ffStrbufSetNWS(FFstrbuf* result, uint32_t length, const wchar_t* source); static inline void ffStrbufSetWS(FFstrbuf* result, const wchar_t* source) { + if (!source) return ffStrbufClear(result); return ffStrbufSetNWS(result, (uint32_t)wcslen(source), source); } @@ -17,6 +18,7 @@ void ffStrbufInitNWS(FFstrbuf* result, uint32_t length, const wchar_t* source); static inline void ffStrbufInitWS(FFstrbuf* result, const wchar_t* source) { + if (!source) return ffStrbufInit(result); return ffStrbufInitNWS(result, (uint32_t)wcslen(source), source); } @@ -29,6 +31,7 @@ static inline FFstrbuf ffStrbufCreateNWS(uint32_t length, const wchar_t* source) static inline FFstrbuf ffStrbufCreateWS(const wchar_t* source) { + if (!source) return ffStrbufCreate(); return ffStrbufCreateNWS((uint32_t)wcslen(source), source); }