ohmu
·
2026-10-15
1#include "common/printing.h"
2#include "common/jsonconfig.h"
3#include "common/temps.h"
4#include "common/size.h"
5#include "detection/physicaldisk/physicaldisk.h"
6#include "modules/physicaldisk/physicaldisk.h"
7
8static int sortDevices(const FFPhysicalDiskResult* left, const FFPhysicalDiskResult* right) {
9 return ffStrbufComp(&left->name, &right->name);
10}
11
12static void formatKey(const FFPhysicalDiskOptions* options, FFPhysicalDiskResult* dev, uint32_t index, FFstrbuf* key) {
13 if (options->moduleArgs.key.length == 0) {
14 ffStrbufSetF(key, "%s (%s)", FF_MODULE_GET_DISPLAY_NAME(PhysicalDisk), dev->name.length ? dev->name.chars : dev->devPath.chars);
15 } else {
16 ffStrbufClear(key);
17 FF_PARSE_FORMAT_STRING_CHECKED(key, &options->moduleArgs.key, ((FFformatarg[]) {
18 FF_ARG(index, "index"),
19 FF_ARG(dev->name, "name"),
20 FF_ARG(dev->devPath, "dev-path"),
21 FF_ARG(options->moduleArgs.keyIcon, "icon"),
22 FF_ARG(FF_MODULE_GET_DISPLAY_NAME(PhysicalDisk), "module-name"),
23 }));
24 }
25}
26
27bool ffPrintPhysicalDisk(FFPhysicalDiskOptions* options) {
28 FF_LIST_AUTO_DESTROY result = ffListCreate();
29 const char* error = ffDetectPhysicalDisk(&result, options);
30
31 if (error) {
32 ffPrintError(FF_MODULE_GET_DISPLAY_NAME(PhysicalDisk), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error);
33 return false;
34 }
35
36 ffListSort(&result, sizeof(FFPhysicalDiskResult), (const void*) sortDevices);
37
38 uint32_t index = 0;
39 FF_STRBUF_AUTO_DESTROY key = ffStrbufCreate();
40 FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreate();
41
42 FF_LIST_FOR_EACH (FFPhysicalDiskResult, dev, result) {
43 formatKey(options, dev, result.length == 1 ? 0 : index + 1, &key);
44 ffStrbufClear(&buffer);
45 ffSizeAppendNum(dev->size, &buffer);
46
47 const char* physicalType = dev->type & FF_PHYSICALDISK_TYPE_VIRTUAL
48 ? "Virtual"
49 : dev->type & FF_PHYSICALDISK_TYPE_HDD
50 ? "HDD"
51 : dev->type & FF_PHYSICALDISK_TYPE_SSD
52 ? "SSD"
53 : "";
54 const char* removableType = dev->type & FF_PHYSICALDISK_TYPE_REMOVABLE
55 ? "Removable"
56 : dev->type & FF_PHYSICALDISK_TYPE_FIXED
57 ? "Fixed"
58 : "";
59 const char* readOnlyType = dev->type & FF_PHYSICALDISK_TYPE_READONLY
60 ? "Read-only"
61 : "";
62
63 if (options->moduleArgs.outputFormat.length == 0) {
64 ffPrintLogoAndKey(key.chars, 0, &options->moduleArgs, FF_PRINT_TYPE_NO_CUSTOM_KEY);
65
66 if (physicalType[0] || removableType[0] || readOnlyType[0]) {
67 ffStrbufAppendS(&buffer, " [");
68 if (physicalType[0]) {
69 ffStrbufAppendS(&buffer, physicalType);
70 }
71 if (removableType[0]) {
72 if (buffer.chars[buffer.length - 1] != '[') {
73 ffStrbufAppendS(&buffer, ", ");
74 }
75 ffStrbufAppendS(&buffer, removableType);
76 }
77 if (readOnlyType[0]) {
78 if (buffer.chars[buffer.length - 1] != '[') {
79 ffStrbufAppendS(&buffer, ", ");
80 }
81 ffStrbufAppendS(&buffer, readOnlyType);
82 }
83 ffStrbufAppendC(&buffer, ']');
84 }
85
86 if (dev->temperature != FF_PHYSICALDISK_TEMP_UNSET) {
87 if (buffer.length > 0) {
88 ffStrbufAppendS(&buffer, " - ");
89 }
90
91 ffTempsAppendNum(dev->temperature, &buffer, options->tempConfig, &options->moduleArgs);
92 }
93 ffStrbufPutTo(&buffer, stdout);
94 } else {
95 FF_STRBUF_AUTO_DESTROY tempStr = ffStrbufCreate();
96 ffTempsAppendNum(dev->temperature, &tempStr, options->tempConfig, &options->moduleArgs);
97 if (dev->type & FF_PHYSICALDISK_TYPE_READWRITE) {
98 readOnlyType = "Read-write";
99 }
100 FF_PRINT_FORMAT_CHECKED(key.chars, 0, &options->moduleArgs, FF_PRINT_TYPE_NO_CUSTOM_KEY, ((FFformatarg[]) {
101 FF_ARG(buffer, "size"),
102 FF_ARG(dev->name, "name"),
103 FF_ARG(dev->interconnect, "interconnect"),
104 FF_ARG(dev->devPath, "dev-path"),
105 FF_ARG(dev->serial, "serial"),
106 FF_ARG(physicalType, "physical-type"),
107 FF_ARG(removableType, "removable-type"),
108 FF_ARG(readOnlyType, "readonly-type"),
109 FF_ARG(dev->revision, "revision"),
110 FF_ARG(tempStr, "temperature"),
111 }));
112 }
113 ++index;
114 }
115
116 FF_LIST_FOR_EACH (FFPhysicalDiskResult, dev, result) {
117 ffStrbufDestroy(&dev->name);
118 ffStrbufDestroy(&dev->interconnect);
119 ffStrbufDestroy(&dev->devPath);
120 ffStrbufDestroy(&dev->serial);
121 ffStrbufDestroy(&dev->revision);
122 }
123
124 return true;
125}
126
127void ffParsePhysicalDiskJsonObject(FFPhysicalDiskOptions* options, yyjson_val* module) {
128 yyjson_val *key, *val;
129 size_t idx, max;
130 yyjson_obj_foreach (module, idx, max, key, val) {
131 if (ffJsonConfigParseModuleArgs(key, val, &options->moduleArgs)) {
132 continue;
133 }
134
135 if (unsafe_yyjson_equals_str(key, "namePrefix")) {
136 ffStrbufSetJsonVal(&options->namePrefix, val);
137 continue;
138 }
139
140 if (unsafe_yyjson_equals_str(key, "hideVirtual")) {
141 if (!yyjson_is_bool(val)) {
142 ffPrintError(FF_MODULE_GET_DISPLAY_NAME(PhysicalDisk), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "hideVirtual must be a boolean");
143 } else {
144 if (unsafe_yyjson_is_true(val)) {
145 options->hideType |= FF_PHYSICALDISK_TYPE_VIRTUAL;
146 } else {
147 options->hideType &= ~FF_PHYSICALDISK_TYPE_VIRTUAL;
148 }
149 }
150 continue;
151 }
152
153 if (unsafe_yyjson_equals_str(key, "hideUnused")) {
154 if (!yyjson_is_bool(val)) {
155 ffPrintError(FF_MODULE_GET_DISPLAY_NAME(PhysicalDisk), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "hideUnused must be a boolean");
156 } else {
157 if (unsafe_yyjson_is_true(val)) {
158 options->hideType |= FF_PHYSICALDISK_TYPE_UNUSED;
159 } else {
160 options->hideType &= ~FF_PHYSICALDISK_TYPE_UNUSED;
161 }
162 }
163 continue;
164 }
165
166 if (ffTempsParseJsonObject(key, val, &options->temp, &options->tempConfig)) {
167 continue;
168 }
169
170 ffPrintError(FF_MODULE_GET_DISPLAY_NAME(PhysicalDisk), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key));
171 }
172}
173
174[[gnu::cold]]
175void ffGeneratePhysicalDiskJsonConfig(FFPhysicalDiskOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) {
176 ffJsonConfigGenerateModuleArgsConfig(doc, module, &options->moduleArgs);
177
178 yyjson_mut_obj_add_strbuf(doc, module, "namePrefix", &options->namePrefix);
179
180 ffTempsGenerateJsonConfig(doc, module, options->temp, options->tempConfig);
181
182 yyjson_mut_obj_add_bool(doc, module, "hideVirtual", !!(options->hideType & FF_PHYSICALDISK_TYPE_VIRTUAL));
183 yyjson_mut_obj_add_bool(doc, module, "hideUnused", !!(options->hideType & FF_PHYSICALDISK_TYPE_UNUSED));
184}
185
186bool ffGeneratePhysicalDiskJsonResult(FFPhysicalDiskOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) {
187 FF_LIST_AUTO_DESTROY result = ffListCreate();
188 const char* error = ffDetectPhysicalDisk(&result, options);
189
190 if (error) {
191 yyjson_mut_obj_add_str(doc, module, "error", error);
192 return false;
193 }
194
195 yyjson_mut_val* arr = yyjson_mut_obj_add_arr(doc, module, "result");
196 FF_LIST_FOR_EACH (FFPhysicalDiskResult, dev, result) {
197 yyjson_mut_val* obj = yyjson_mut_arr_add_obj(doc, arr);
198 yyjson_mut_obj_add_strbuf(doc, obj, "name", &dev->name);
199 yyjson_mut_obj_add_strbuf(doc, obj, "devPath", &dev->devPath);
200 yyjson_mut_obj_add_strbuf(doc, obj, "interconnect", &dev->interconnect);
201
202 if (dev->type & FF_PHYSICALDISK_TYPE_VIRTUAL) {
203 yyjson_mut_obj_add_str(doc, obj, "kind", "Virtual");
204 } else if (dev->type & FF_PHYSICALDISK_TYPE_HDD) {
205 yyjson_mut_obj_add_str(doc, obj, "kind", "HDD");
206 } else if (dev->type & FF_PHYSICALDISK_TYPE_SSD) {
207 yyjson_mut_obj_add_str(doc, obj, "kind", "SSD");
208 } else {
209 yyjson_mut_obj_add_null(doc, obj, "kind");
210 }
211
212 yyjson_mut_obj_add_uint(doc, obj, "size", dev->size);
213 yyjson_mut_obj_add_strbuf(doc, obj, "serial", &dev->serial);
214
215 if (dev->type & FF_PHYSICALDISK_TYPE_REMOVABLE) {
216 yyjson_mut_obj_add_bool(doc, obj, "removable", true);
217 } else if (dev->type & FF_PHYSICALDISK_TYPE_FIXED) {
218 yyjson_mut_obj_add_bool(doc, obj, "removable", false);
219 } else {
220 yyjson_mut_obj_add_null(doc, obj, "removable");
221 }
222
223 if (dev->type & FF_PHYSICALDISK_TYPE_READONLY) {
224 yyjson_mut_obj_add_bool(doc, obj, "readOnly", true);
225 } else if (dev->type & FF_PHYSICALDISK_TYPE_READWRITE) {
226 yyjson_mut_obj_add_bool(doc, obj, "readOnly", false);
227 } else {
228 yyjson_mut_obj_add_null(doc, obj, "readOnly");
229 }
230
231 yyjson_mut_obj_add_bool(doc, obj, "unknown", !!(dev->type & FF_PHYSICALDISK_TYPE_UNUSED));
232
233 yyjson_mut_obj_add_strbuf(doc, obj, "revision", &dev->revision);
234
235 if (dev->temperature != FF_PHYSICALDISK_TEMP_UNSET) {
236 yyjson_mut_obj_add_real(doc, obj, "temperature", dev->temperature);
237 } else {
238 yyjson_mut_obj_add_null(doc, obj, "temperature");
239 }
240 }
241
242 FF_LIST_FOR_EACH (FFPhysicalDiskResult, dev, result) {
243 ffStrbufDestroy(&dev->name);
244 ffStrbufDestroy(&dev->interconnect);
245 ffStrbufDestroy(&dev->devPath);
246 ffStrbufDestroy(&dev->serial);
247 ffStrbufDestroy(&dev->revision);
248 }
249
250 return true;
251}
252
253void ffInitPhysicalDiskOptions(FFPhysicalDiskOptions* options) {
254 ffOptionInitModuleArg(&options->moduleArgs, "");
255
256 ffStrbufInit(&options->namePrefix);
257 options->temp = false;
258 options->tempConfig = (FFColorRangeConfig) { 50, 70 };
259 options->hideType = FF_PHYSICALDISK_TYPE_UNUSED;
260}
261
262void ffDestroyPhysicalDiskOptions(FFPhysicalDiskOptions* options) {
263 ffOptionDestroyModuleArg(&options->moduleArgs);
264 ffStrbufDestroy(&options->namePrefix);
265}
266
267FFModuleBaseInfo ffPhysicalDiskModuleInfo = {
268 .name = "PhysicalDisk",
269 .description = "Print physical disk information",
270 .displayName = {
271 .en = "Physical Disk",
272 .ar = "القرص الفعلي",
273 .cs = "Fyzický disk",
274 .de = "Physische Festplatte",
275 .es = "Disco físico",
276 .fi = "Fyysinen Levy",
277 .fr = "Disque physique",
278 .gl = "Disco físico",
279 .he = "דיסק פיזי",
280 .id = "Disk Fisik",
281 .it = "Disco fisico",
282 .ja = "物理ディスク",
283 .ko = "물리 디스크",
284 .pl = "Dysk fizyczny",
285 .pt = "Disco físico",
286 .ru = "Физический диск",
287 .tr = "Fiziksel Disk",
288 .uk = "Фізичний диск",
289 .vi = "Ổ đĩa vật lý",
290 .zh_CN = "物理磁盘",
291 .zh_TW = "物理磁碟",
292 },
293 .initOptions = (void*) ffInitPhysicalDiskOptions,
294 .destroyOptions = (void*) ffDestroyPhysicalDiskOptions,
295 .parseJsonObject = (void*) ffParsePhysicalDiskJsonObject,
296 .printModule = (void*) ffPrintPhysicalDisk,
297 .generateJsonResult = (void*) ffGeneratePhysicalDiskJsonResult,
298 .generateJsonConfig = (void*) ffGeneratePhysicalDiskJsonConfig,
299 .formatArgs = FF_FORMAT_ARG_LIST(((FFModuleFormatArg[]) {
300 { "Device size (formatted)", "size" },
301 { "Device name *", "name" },
302 { "Device interconnect type", "interconnect" },
303 { "Device raw file path *", "dev-path" },
304 { "Serial number", "serial" },
305 { "Device kind (SSD or HDD)", "physical-type" },
306 { "Device kind (Removable or Fixed)", "removable-type" },
307 { "Device kind (Read-only or Read-write)", "readonly-type" },
308 { "Product revision", "revision" },
309 { "Device temperature (formatted)", "temperature" },
310 })),
311 .defaultOrder = 69,
312};