ohmu
·
2026-10-15
1#include "common/printing.h"
2#include "common/jsonconfig.h"
3#include "common/size.h"
4#include "common/strutil.h"
5#include "detection/netio/netio.h"
6#include "modules/netio/netio.h"
7
8static int sortInfs(const FFNetIOResult* left, const FFNetIOResult* right) {
9 return ffStrbufComp(&left->name, &right->name);
10}
11
12static void formatKey(const FFNetIOOptions* options, FFNetIOResult* inf, uint32_t index, FFstrbuf* key) {
13 if (options->moduleArgs.key.length == 0) {
14 if (!inf->name.length) {
15 ffStrbufSetF(&inf->name, "unknown %u", (unsigned) index);
16 }
17
18 ffStrbufSetF(key, "%s (%s)", FF_MODULE_GET_DISPLAY_NAME(NetIO), inf->name.chars);
19 } else {
20 ffStrbufClear(key);
21 FF_PARSE_FORMAT_STRING_CHECKED(key, &options->moduleArgs.key, ((FFformatarg[]) {
22 FF_ARG(index, "index"),
23 FF_ARG(inf->name, "ifname"),
24 FF_ARG(options->moduleArgs.keyIcon, "icon"),
25 FF_ARG(FF_MODULE_GET_DISPLAY_NAME(NetIO), "module-name"),
26 }));
27 }
28}
29
30bool ffPrintNetIO(FFNetIOOptions* options) {
31 FF_LIST_AUTO_DESTROY result = ffListCreate();
32 const char* error = ffDetectNetIO(&result, options);
33
34 if (error) {
35 ffPrintError(FF_MODULE_GET_DISPLAY_NAME(NetIO), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error);
36 return false;
37 }
38
39 ffListSort(&result, sizeof(FFNetIOResult), (const void*) sortInfs);
40
41 uint32_t index = 0;
42 FF_STRBUF_AUTO_DESTROY key = ffStrbufCreate();
43 FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreate();
44 FF_STRBUF_AUTO_DESTROY buffer2 = ffStrbufCreate();
45
46 FF_LIST_FOR_EACH (FFNetIOResult, inf, result) {
47 formatKey(options, inf, result.length == 1 ? 0 : index + 1, &key);
48 ffStrbufClear(&buffer);
49
50 if (options->moduleArgs.outputFormat.length == 0) {
51 ffPrintLogoAndKey(key.chars, 0, &options->moduleArgs, FF_PRINT_TYPE_NO_CUSTOM_KEY);
52
53 ffSizeAppendNum(inf->rxBytes, &buffer);
54 if (!options->detectTotal) {
55 ffStrbufAppendS(&buffer, "/s");
56 }
57 ffStrbufAppendS(&buffer, " (IN) - ");
58
59 ffSizeAppendNum(inf->txBytes, &buffer);
60 if (!options->detectTotal) {
61 ffStrbufAppendS(&buffer, "/s");
62 }
63 ffStrbufAppendS(&buffer, " (OUT)");
64
65 if (inf->defaultRoute && !options->defaultRouteOnly) {
66 ffStrbufAppendS(&buffer, " *");
67 }
68 ffStrbufPutTo(&buffer, stdout);
69 } else {
70 ffStrbufClear(&buffer2);
71 ffSizeAppendNum(inf->rxBytes, &buffer);
72 if (!options->detectTotal) {
73 ffStrbufAppendS(&buffer, "/s");
74 }
75 ffSizeAppendNum(inf->txBytes, &buffer2);
76 if (!options->detectTotal) {
77 ffStrbufAppendS(&buffer2, "/s");
78 }
79
80 FF_PRINT_FORMAT_CHECKED(key.chars, 0, &options->moduleArgs, FF_PRINT_TYPE_NO_CUSTOM_KEY, ((FFformatarg[]) {
81 FF_ARG(buffer, "rx-size"),
82 FF_ARG(buffer2, "tx-size"),
83 FF_ARG(inf->name, "ifname"),
84 FF_ARG(inf->defaultRoute, "is-default-route"),
85 FF_ARG(inf->txBytes, "tx-bytes"),
86 FF_ARG(inf->rxBytes, "rx-bytes"),
87 FF_ARG(inf->txPackets, "tx-packets"),
88 FF_ARG(inf->rxPackets, "rx-packets"),
89 FF_ARG(inf->rxErrors, "rx-errors"),
90 FF_ARG(inf->txErrors, "tx-errors"),
91 FF_ARG(inf->rxDrops, "rx-drops"),
92 FF_ARG(inf->txDrops, "tx-drops"),
93 }));
94 }
95 ++index;
96 }
97
98 FF_LIST_FOR_EACH (FFNetIOResult, inf, result) {
99 ffStrbufDestroy(&inf->name);
100 }
101
102 return true;
103}
104
105void ffParseNetIOJsonObject(FFNetIOOptions* options, yyjson_val* module) {
106 yyjson_val *key, *val;
107 size_t idx, max;
108 yyjson_obj_foreach (module, idx, max, key, val) {
109 if (ffJsonConfigParseModuleArgs(key, val, &options->moduleArgs)) {
110 continue;
111 }
112
113 if (unsafe_yyjson_equals_str(key, "namePrefix")) {
114 ffStrbufSetJsonVal(&options->namePrefix, val);
115 continue;
116 }
117
118 if (unsafe_yyjson_equals_str(key, "defaultRouteOnly")) {
119 options->defaultRouteOnly = yyjson_get_bool(val);
120 continue;
121 }
122
123 if (unsafe_yyjson_equals_str(key, "detectTotal")) {
124 options->detectTotal = yyjson_get_bool(val);
125 continue;
126 }
127
128 if (unsafe_yyjson_equals_str(key, "waitTime")) {
129 if (!ffJsonConfigParseUInt32(val, &options->waitTime, UINT32_MAX)) {
130 ffPrintError(FF_MODULE_GET_DISPLAY_NAME(NetIO), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Property 'waitTime' must be a non-negative integer no greater than 4294967295");
131 continue;
132 }
133 if (options->waitTime == 0) {
134 options->waitTime = 1;
135 }
136 continue;
137 }
138
139 ffPrintError(FF_MODULE_GET_DISPLAY_NAME(NetIO), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key));
140 }
141}
142
143[[gnu::cold]]
144void ffGenerateNetIOJsonConfig(FFNetIOOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) {
145 ffJsonConfigGenerateModuleArgsConfig(doc, module, &options->moduleArgs);
146
147 yyjson_mut_obj_add_strbuf(doc, module, "namePrefix", &options->namePrefix);
148
149 yyjson_mut_obj_add_bool(doc, module, "defaultRouteOnly", options->defaultRouteOnly);
150
151 yyjson_mut_obj_add_bool(doc, module, "detectTotal", options->detectTotal);
152
153 yyjson_mut_obj_add_uint(doc, module, "waitTime", options->waitTime);
154}
155
156bool ffGenerateNetIOJsonResult(FFNetIOOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) {
157 FF_LIST_AUTO_DESTROY result = ffListCreate();
158 const char* error = ffDetectNetIO(&result, options);
159
160 if (error) {
161 yyjson_mut_obj_add_str(doc, module, "error", error);
162 return false;
163 }
164
165 yyjson_mut_val* arr = yyjson_mut_obj_add_arr(doc, module, "result");
166 FF_LIST_FOR_EACH (FFNetIOResult, counter, result) {
167 yyjson_mut_val* obj = yyjson_mut_arr_add_obj(doc, arr);
168 yyjson_mut_obj_add_strbuf(doc, obj, "name", &counter->name);
169 yyjson_mut_obj_add_bool(doc, obj, "defaultRoute", counter->defaultRoute);
170 yyjson_mut_obj_add_uint(doc, obj, "txBytes", counter->txBytes);
171 yyjson_mut_obj_add_uint(doc, obj, "rxBytes", counter->rxBytes);
172 yyjson_mut_obj_add_uint(doc, obj, "txPackets", counter->txPackets);
173 yyjson_mut_obj_add_uint(doc, obj, "rxPackets", counter->rxPackets);
174 yyjson_mut_obj_add_uint(doc, obj, "rxErrors", counter->rxErrors);
175 yyjson_mut_obj_add_uint(doc, obj, "txErrors", counter->txErrors);
176 yyjson_mut_obj_add_uint(doc, obj, "rxDrops", counter->rxDrops);
177 yyjson_mut_obj_add_uint(doc, obj, "txDrops", counter->txDrops);
178 }
179
180 FF_LIST_FOR_EACH (FFNetIOResult, inf, result) {
181 ffStrbufDestroy(&inf->name);
182 }
183
184 return true;
185}
186
187void ffInitNetIOOptions(FFNetIOOptions* options) {
188 ffOptionInitModuleArg(&options->moduleArgs, "");
189
190 ffStrbufInit(&options->namePrefix);
191 options->defaultRouteOnly =
192#if __ANDROID__
193 false
194#else
195 true
196#endif
197 ;
198 options->detectTotal = false;
199 options->waitTime = 250;
200}
201
202void ffDestroyNetIOOptions(FFNetIOOptions* options) {
203 ffOptionDestroyModuleArg(&options->moduleArgs);
204 ffStrbufDestroy(&options->namePrefix);
205}
206
207FFModuleBaseInfo ffNetIOModuleInfo = {
208 .name = "NetIO",
209 .description = "Print network I/O throughput",
210 .displayName = {
211 .en = "Network I/O",
212 .ar = "إدخال/إخراج الشبكة",
213 .cs = "Síťové I/O",
214 .de = "Netzwerk I/O",
215 .es = "E/S de la red",
216 .fi = "Verkon I/O",
217 .fr = "E/S réseau",
218 .gl = "E/S da rede",
219 .he = "קלט/פלט רשת",
220 .id = "I/O Jaringan",
221 .it = "I/O di rete",
222 .ja = "ネットワークI/O",
223 .ko = "네트워크 I/O",
224 .pl = "I/O sieciowe",
225 .pt = "E/S de rede",
226 .ru = "Сетевой I/O",
227 .tr = "Ağ G/Ç",
228 .uk = "Мережа В/В",
229 .vi = "Mạng I/O",
230 .zh_CN = "网络 I/O",
231 .zh_TW = "網路 I/O",
232 },
233 .initOptions = (void*) ffInitNetIOOptions,
234 .destroyOptions = (void*) ffDestroyNetIOOptions,
235 .parseJsonObject = (void*) ffParseNetIOJsonObject,
236 .printModule = (void*) ffPrintNetIO,
237 .generateJsonResult = (void*) ffGenerateNetIOJsonResult,
238 .generateJsonConfig = (void*) ffGenerateNetIOJsonConfig,
239 .formatArgs = FF_FORMAT_ARG_LIST(((FFModuleFormatArg[]) {
240 { "Size of data received [per second] (formatted)", "rx-size" },
241 { "Size of data sent [per second] (formatted)", "tx-size" },
242 { "Interface name *", "ifname" },
243 { "Is default route", "is-default-route" },
244 { "Size of data received [per second] (in bytes)", "rx-bytes" },
245 { "Size of data sent [per second] (in bytes)", "tx-bytes" },
246 { "Number of packets received [per second]", "rx-packets" },
247 { "Number of packets sent [per second]", "tx-packets" },
248 { "Number of errors received [per second]", "rx-errors" },
249 { "Number of errors sent [per second]", "tx-errors" },
250 { "Number of packets dropped when receiving [per second]", "rx-drops" },
251 { "Number of packets dropped when sending [per second]", "tx-drops" },
252 })),
253 .defaultOrder = 67,
254};