master ohmu/fastfetch-openbsd / src / modules / users / users.c
ohmu  ·  2026-10-15
  1#include "common/printing.h"
  2#include "common/jsonconfig.h"
  3#include "common/time.h"
  4#include "common/strutil.h"
  5#include "detection/users/users.h"
  6#include "modules/users/users.h"
  7
  8#pragma GCC diagnostic ignored "-Wformat" // warning: unknown conversion type character 'F' in format
  9
 10bool ffPrintUsers(FFUsersOptions* options) {
 11    FF_LIST_AUTO_DESTROY users = ffListCreate();
 12
 13    const char* error = ffDetectUsers(options, &users);
 14
 15    if (error) {
 16        ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Users), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error);
 17        return false;
 18    }
 19
 20    if (users.length == 0) {
 21        ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Users), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", "Unable to detect any users");
 22        return false;
 23    }
 24
 25    if (options->moduleArgs.outputFormat.length == 0) {
 26        if (options->compact) {
 27            ffPrintLogoAndKey(FF_MODULE_GET_DISPLAY_NAME(Users), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT);
 28
 29            FF_STRBUF_AUTO_DESTROY result = ffStrbufCreate();
 30            for (uint32_t i = 0; i < users.length; ++i) {
 31                if (i > 0) {
 32                    ffStrbufAppendS(&result, ", ");
 33                }
 34                FFUserResult* user = FF_LIST_GET(FFUserResult, users, i);
 35                ffStrbufAppend(&result, &user->name);
 36            }
 37            ffStrbufPutTo(&result, stdout);
 38        } else {
 39            for (uint32_t i = 0; i < users.length; ++i) {
 40                FFUserResult* user = FF_LIST_GET(FFUserResult, users, i);
 41
 42                ffPrintLogoAndKey(FF_MODULE_GET_DISPLAY_NAME(Users), users.length == 1 ? 0 : (uint8_t) (i + 1), &options->moduleArgs, FF_PRINT_TYPE_DEFAULT);
 43
 44                FF_STRBUF_AUTO_DESTROY result = ffStrbufCreateCopy(&user->name);
 45                if (user->hostName.length) {
 46                    ffStrbufAppendF(&result, "@%s", user->hostName.chars);
 47                }
 48
 49                if (user->loginTime) {
 50                    ffStrbufAppendF(&result, " - login time %s", ffTimeToShortStr(user->loginTime));
 51                }
 52
 53                ffStrbufPutTo(&result, stdout);
 54            }
 55        }
 56    } else {
 57        uint64_t now = ffTimeGetNow();
 58        for (uint32_t i = 0; i < users.length; ++i) {
 59            FFUserResult* user = FF_LIST_GET(FFUserResult, users, i);
 60
 61            uint64_t duration = now - user->loginTime;
 62            uint32_t milliseconds = (uint32_t) (duration % 1000);
 63            duration /= 1000;
 64            uint32_t seconds = (uint32_t) (duration % 60);
 65            duration /= 60;
 66            uint32_t minutes = (uint32_t) (duration % 60);
 67            duration /= 60;
 68            uint32_t hours = (uint32_t) (duration % 24);
 69            duration /= 24;
 70            uint32_t days = (uint32_t) duration;
 71
 72            FFTimeGetAgeResult age = ffTimeGetAge(user->loginTime, ffTimeGetNow());
 73
 74            FF_PRINT_FORMAT_CHECKED(FF_MODULE_GET_DISPLAY_NAME(Users), users.length == 1 ? 0 : (uint8_t) (i + 1), &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) {
 75                                                                                                                                                      FF_ARG(user->name, "name"),
 76                                                                                                                                                      FF_ARG(user->hostName, "host-name"),
 77                                                                                                                                                      FF_ARG(user->sessionName, "session-name"),
 78                                                                                                                                                      FF_ARG(user->clientIp, "client-ip"),
 79                                                                                                                                                      { FF_ARG_TYPE_STRING, ffTimeToShortStr(user->loginTime), "login-time" },
 80                                                                                                                                                      FF_ARG(days, "days"),
 81                                                                                                                                                      FF_ARG(hours, "hours"),
 82                                                                                                                                                      FF_ARG(minutes, "minutes"),
 83                                                                                                                                                      FF_ARG(seconds, "seconds"),
 84                                                                                                                                                      FF_ARG(milliseconds, "milliseconds"),
 85                                                                                                                                                      FF_ARG(age.years, "years"),
 86                                                                                                                                                      FF_ARG(age.daysOfYear, "days-of-year"),
 87                                                                                                                                                      FF_ARG(age.yearsFraction, "years-fraction"),
 88                                                                                                                                                  }));
 89        }
 90    }
 91
 92    FF_LIST_FOR_EACH (FFUserResult, user, users) {
 93        ffStrbufDestroy(&user->clientIp);
 94        ffStrbufDestroy(&user->hostName);
 95        ffStrbufDestroy(&user->sessionName);
 96        ffStrbufDestroy(&user->name);
 97    }
 98
 99    return true;
100}
101
102void ffParseUsersJsonObject(FFUsersOptions* options, yyjson_val* module) {
103    yyjson_val *key, *val;
104    size_t idx, max;
105    yyjson_obj_foreach (module, idx, max, key, val) {
106        if (unsafe_yyjson_equals_str(key, "type") || unsafe_yyjson_equals_str(key, "condition")) {
107            continue;
108        }
109
110        if (ffJsonConfigParseModuleArgs(key, val, &options->moduleArgs)) {
111            continue;
112        }
113
114        if (unsafe_yyjson_equals_str(key, "compact")) {
115            options->compact = yyjson_get_bool(val);
116            continue;
117        }
118
119        if (unsafe_yyjson_equals_str(key, "myselfOnly")) {
120            options->myselfOnly = yyjson_get_bool(val);
121            continue;
122        }
123
124        ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Users), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key));
125    }
126}
127
128[[gnu::cold]]
129void ffGenerateUsersJsonConfig(FFUsersOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) {
130    ffJsonConfigGenerateModuleArgsConfig(doc, module, &options->moduleArgs);
131
132    yyjson_mut_obj_add_bool(doc, module, "compact", options->compact);
133
134    yyjson_mut_obj_add_bool(doc, module, "myselfOnly", options->myselfOnly);
135}
136
137bool ffGenerateUsersJsonResult(FFUsersOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) {
138    FF_LIST_AUTO_DESTROY results = ffListCreate();
139
140    const char* error = ffDetectUsers(options, &results);
141
142    if (error) {
143        yyjson_mut_obj_add_str(doc, module, "error", error);
144        return false;
145    }
146
147    yyjson_mut_val* arr = yyjson_mut_obj_add_arr(doc, module, "result");
148    FF_LIST_FOR_EACH (FFUserResult, user, results) {
149        yyjson_mut_val* obj = yyjson_mut_arr_add_obj(doc, arr);
150        yyjson_mut_obj_add_strbuf(doc, obj, "name", &user->name);
151        yyjson_mut_obj_add_strbuf(doc, obj, "hostName", &user->hostName);
152        yyjson_mut_obj_add_strbuf(doc, obj, "sessionName", &user->sessionName);
153        yyjson_mut_obj_add_strbuf(doc, obj, "clientIp", &user->clientIp);
154        const char* pstr = ffTimeToFullStr(user->loginTime);
155        if (*pstr) {
156            yyjson_mut_obj_add_strcpy(doc, obj, "loginTime", pstr);
157        } else {
158            yyjson_mut_obj_add_null(doc, obj, "loginTime");
159        }
160    }
161
162    FF_LIST_FOR_EACH (FFUserResult, user, results) {
163        ffStrbufDestroy(&user->clientIp);
164        ffStrbufDestroy(&user->hostName);
165        ffStrbufDestroy(&user->sessionName);
166        ffStrbufDestroy(&user->name);
167    }
168
169    return true;
170}
171
172void ffInitUsersOptions(FFUsersOptions* options) {
173    ffOptionInitModuleArg(&options->moduleArgs, "");
174
175    options->compact = false;
176    options->myselfOnly = false;
177}
178
179void ffDestroyUsersOptions(FFUsersOptions* options) {
180    ffOptionDestroyModuleArg(&options->moduleArgs);
181}
182
183FFModuleBaseInfo ffUsersModuleInfo = {
184    .name = "Users",
185    .description = "Print users who are currently logged in",
186    .displayName = {
187        .en = "Users",
188        .ar = "المستخدمون",
189        .cs = "Uživatelé",
190        .de = "Benutzer",
191        .es = "Usuarios",
192        .fi = "Käyttäjät",
193        .fr = "Utilisateurs",
194        .gl = "Usuarios",
195        .he = "משתמשים",
196        .id = "Pengguna",
197        .it = "Utenti",
198        .ja = "ユーザー",
199        .ko = "사용자",
200        .pl = "Użytkownicy",
201        .pt = "Usuários",
202        .ru = "Пользователи",
203        .tr = "Kullanıcılar",
204        .uk = "Користувачі",
205        .vi = "Người dùng",
206        .zh_CN = "用户",
207        .zh_TW = "使用者",
208    },
209    .initOptions = (void*) ffInitUsersOptions,
210    .destroyOptions = (void*) ffDestroyUsersOptions,
211    .parseJsonObject = (void*) ffParseUsersJsonObject,
212    .printModule = (void*) ffPrintUsers,
213    .generateJsonResult = (void*) ffGenerateUsersJsonResult,
214    .generateJsonConfig = (void*) ffGenerateUsersJsonConfig,
215    .formatArgs = FF_FORMAT_ARG_LIST(((FFModuleFormatArg[]) {
216        { "User name", "name" },
217        { "Host name", "host-name" },
218        { "Session name", "session-name" },
219        { "Client IP", "client-ip" },
220        { "Login Time in local timezone", "login-time" },
221        { "Days after login", "days" },
222        { "Hours after login", "hours" },
223        { "Minutes after login", "minutes" },
224        { "Seconds after login", "seconds" },
225        { "Milliseconds after login", "milliseconds" },
226        { "Years integer after login", "years" },
227        { "Days of year after login", "days-of-year" },
228        { "Years fraction after login", "years-fraction" },
229    })),
230    .defaultOrder = 58,
231};