ohmu
·
2026-10-15
1#include "common/printing.h"
2#include "common/jsonconfig.h"
3#include "common/percent.h"
4#include "common/strutil.h"
5#include "detection/loadavg/loadavg.h"
6#include "detection/cpu/cpu.h"
7#include "modules/loadavg/loadavg.h"
8
9bool ffPrintLoadavg(FFLoadavgOptions* options) {
10 double result[3] = { 0.0 / 0.0, 0.0 / 0.0, 0.0 / 0.0 };
11
12 const char* error = ffDetectLoadavg(result);
13 if (error) {
14 ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Loadavg), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error);
15 return false;
16 }
17
18 if (options->moduleArgs.outputFormat.length == 0) {
19 if (options->compact) {
20 ffPrintLogoAndKey(FF_MODULE_GET_DISPLAY_NAME(Loadavg), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT);
21 printf("%.*f, %.*f, %.*f\n", options->ndigits, result[0], options->ndigits, result[1], options->ndigits, result[2]);
22 } else {
23 FFCPUResult cpu = {
24 .temperature = FF_CPU_TEMP_UNSET,
25 .frequencyMax = 0,
26 .frequencyBase = 0,
27 .name = ffStrbufCreate(),
28 .vendor = ffStrbufCreate(),
29 };
30 ffDetectCPU(&(FFCPUOptions) {}, &cpu);
31
32 FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreate();
33 for (uint32_t index = 0; index < 3; index++) {
34 uint32_t duration = index == 0 ? 1 : index == 1 ? 5
35 : 15;
36 if (options->moduleArgs.key.length == 0) {
37 ffStrbufSetF(&buffer, "%s (%d min)", FF_MODULE_GET_DISPLAY_NAME(Loadavg), duration);
38 } else {
39 ffStrbufClear(&buffer);
40 FF_PARSE_FORMAT_STRING_CHECKED(&buffer, &options->moduleArgs.key, ((FFformatarg[]) {
41 FF_ARG(index, "index"),
42 FF_ARG(duration, "duration"),
43 FF_ARG(options->moduleArgs.keyIcon, "icon"),
44 FF_ARG(FF_MODULE_GET_DISPLAY_NAME(Loadavg), "module-name"),
45 }));
46 }
47
48 ffPrintLogoAndKey(buffer.chars, 0, &options->moduleArgs, FF_PRINT_TYPE_NO_CUSTOM_KEY);
49
50 ffStrbufClear(&buffer);
51 double percent = result[index] * 100 / cpu.coresOnline;
52 FFPercentageTypeFlags percentType = options->percent.type == 0 ? instance.config.display.percentType : options->percent.type;
53
54 if (percentType & FF_PERCENTAGE_TYPE_BAR_BIT) {
55 ffPercentAppendBar(&buffer, percent, options->percent, &options->moduleArgs);
56 }
57
58 if (!(percentType & FF_PERCENTAGE_TYPE_HIDE_OTHERS_BIT)) {
59 if (buffer.length > 0) {
60 ffStrbufAppendC(&buffer, ' ');
61 }
62 ffStrbufAppendF(&buffer, "%.*f", options->ndigits, result[index]);
63 }
64
65 if (percentType & FF_PERCENTAGE_TYPE_NUM_BIT) {
66 if (buffer.length > 0) {
67 ffStrbufAppendC(&buffer, ' ');
68 }
69 ffPercentAppendNum(&buffer, percent, options->percent, buffer.length > 0, &options->moduleArgs);
70 }
71
72 ffStrbufPutTo(&buffer, stdout);
73 }
74 }
75 } else {
76 FF_PRINT_FORMAT_CHECKED(FF_MODULE_GET_DISPLAY_NAME(Loadavg), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) {
77 FF_ARG(result[0], "loadavg1"),
78 FF_ARG(result[1], "loadavg2"),
79 FF_ARG(result[2], "loadavg3"),
80 }));
81 }
82
83 return true;
84}
85
86void ffParseLoadavgJsonObject(FFLoadavgOptions* options, yyjson_val* module) {
87 yyjson_val *key, *val;
88 size_t idx, max;
89 yyjson_obj_foreach (module, idx, max, key, val) {
90 if (ffJsonConfigParseModuleArgs(key, val, &options->moduleArgs)) {
91 continue;
92 }
93
94 if (unsafe_yyjson_equals_str(key, "ndigits")) {
95 // The schema documents 0..9 and the value ends up as the precision of `printf("%.*f")`,
96 // so a wider value is not truncated to fit `uint8_t` silently any more.
97 uint32_t ndigits = 0;
98 if (!ffJsonConfigParseUInt32(val, &ndigits, 9)) {
99 ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Loadavg), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Property 'ndigits' must be an integer between 0 and 9");
100 continue;
101 }
102 options->ndigits = (uint8_t) ndigits;
103 continue;
104 }
105
106 if (unsafe_yyjson_equals_str(key, "compact")) {
107 options->compact = yyjson_get_bool(val);
108 continue;
109 }
110
111 if (ffPercentParseJsonObject(key, val, &options->percent)) {
112 continue;
113 }
114
115 ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Loadavg), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key));
116 }
117}
118
119[[gnu::cold]]
120void ffGenerateLoadavgJsonConfig(FFLoadavgOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) {
121 ffJsonConfigGenerateModuleArgsConfig(doc, module, &options->moduleArgs);
122
123 yyjson_mut_obj_add_uint(doc, module, "ndigits", options->ndigits);
124
125 yyjson_mut_obj_add_bool(doc, module, "compact", options->compact);
126
127 ffPercentGenerateJsonConfig(doc, module, options->percent);
128}
129
130bool ffGenerateLoadavgJsonResult([[maybe_unused]] FFLoadavgOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) {
131 double result[3] = { 0.0 / 0.0, 0.0 / 0.0, 0.0 / 0.0 };
132
133 const char* error = ffDetectLoadavg(result);
134 if (error) {
135 yyjson_mut_obj_add_str(doc, module, "error", error);
136 return false;
137 }
138
139 yyjson_mut_val* arr = yyjson_mut_obj_add_arr(doc, module, "result");
140 for (size_t i = 0; i < 3; i++) {
141 yyjson_mut_arr_add_real(doc, arr, result[i]);
142 }
143
144 return true;
145}
146
147void ffInitLoadavgOptions(FFLoadavgOptions* options) {
148 ffOptionInitModuleArg(&options->moduleArgs, "");
149
150 options->percent = (FFPercentageModuleConfig) { 50, 80, 0 };
151 options->ndigits = 2;
152 options->compact = true;
153}
154
155void ffDestroyLoadavgOptions(FFLoadavgOptions* options) {
156 ffOptionDestroyModuleArg(&options->moduleArgs);
157}
158
159FFModuleBaseInfo ffLoadavgModuleInfo = {
160 .name = "Loadavg",
161 .description = "Print system load averages",
162 .displayName = {
163 .en = "Load Average",
164 .ar = "متوسط الحمل",
165 .cs = "Průměrná zátěž",
166 .de = "Systemlast",
167 .es = "Promedio de carga",
168 .fi = "Kuormituksen Keskiarvo",
169 .fr = "Charge moyenne",
170 .gl = "Carga media",
171 .he = "עומס ממוצע",
172 .id = "Beban Rata-rata",
173 .it = "Carico medio",
174 .ja = "ロードアベレージ",
175 .ko = "시스템 부하 평균",
176 .pl = "Średnie obciążenie",
177 .pt = "Média de carga",
178 .ru = "Средняя нагрузка",
179 .tr = "Sistem Yükü",
180 .uk = "Середнє навантаження",
181 .vi = "Tải trung bình",
182 .zh_CN = "系统负载",
183 .zh_TW = "系統負載",
184 },
185 .initOptions = (void*) ffInitLoadavgOptions,
186 .destroyOptions = (void*) ffDestroyLoadavgOptions,
187 .parseJsonObject = (void*) ffParseLoadavgJsonObject,
188 .printModule = (void*) ffPrintLoadavg,
189 .generateJsonResult = (void*) ffGenerateLoadavgJsonResult,
190 .generateJsonConfig = (void*) ffGenerateLoadavgJsonConfig,
191 .formatArgs = FF_FORMAT_ARG_LIST(((FFModuleFormatArg[]) {
192 { "Load average over 1min", "loadavg1" },
193 { "Load average over 5min", "loadavg2" },
194 { "Load average over 15min", "loadavg3" },
195 })),
196 .defaultOrder = 12,
197};