From 169d474a18e81662af59ff614557c426523e520d Mon Sep 17 00:00:00 2001 From: th0truth Date: Sat, 8 Aug 2026 15:45:59 +0300 Subject: [PATCH 1/5] refactor(hwmonitor): move code that generates JSON to hardware_to_json --- include/util.h | 2 ++ src/hwmonitor.c | 58 ++++---------------------------- src/util.c | 89 +++++++++++++++++++++++++++++-------------------- 3 files changed, 61 insertions(+), 88 deletions(-) diff --git a/include/util.h b/include/util.h index 5f8bf53..e66e934 100644 --- a/include/util.h +++ b/include/util.h @@ -79,6 +79,8 @@ void free_hardware(SystemHardware *hw); */ void format_size(char *target_format, uint64_t bytes, char *buffer, size_t buf_size); +cJSON *hardware_to_json(const SystemHardware *hw); + /** * \brief Generates and prints (or saves) the JSON output. * \param[in] config Pointer to the Config structure. diff --git a/src/hwmonitor.c b/src/hwmonitor.c index e5ca375..faa791c 100644 --- a/src/hwmonitor.c +++ b/src/hwmonitor.c @@ -44,61 +44,15 @@ main(int argc, char **argv) display_clear(); printf("hwmonitor - Live View (Press Ctrl+C to exit)\n"); } - if (config.use_ai && config.ai_prompt != NULL) { - /* Create context for AI analysis */ - cJSON *ctx = cJSON_CreateObject(); - - /* Show hardware components */ - if (hw.battery != NULL) { - cJSON_AddItemToObject(ctx, "battery", battery_to_json_obj(hw.battery)); + cJSON *ctx = hardware_to_json(&hw); + if (ctx != NULL) { + char *ctx_str = cJSON_PrintUnformatted(ctx); + groq_analyze_hardware(ctx_str, config.ai_prompt); + free(ctx_str); + cJSON_Delete(ctx); } - if (hw.cpu != NULL) { - cJSON_AddItemToObject(ctx, "cpu", cpu_to_json_obj(hw.cpu)); - } - - if (hw.mainboard != NULL) { - cJSON_AddItemToObject(ctx, "mainboard", mainboard_to_json_obj(hw.mainboard)); - } - - if (hw.os != NULL) { - cJSON_AddItemToObject(ctx, "os", os_to_json_obj(hw.os)); - } - - if (hw.ram != NULL) { - cJSON_AddItemToObject(ctx, "ram", ram_to_json_obj(hw.ram)); - } - - if (hw.gpus != NULL && hw.gpu_count > 0) { - cJSON *list = cJSON_CreateArray(); - for (int i = 0; i < hw.gpu_count; ++i) { - cJSON_AddItemToArray(list, gpu_to_json_obj(hw.gpus[i])); - } - cJSON_AddItemToObject(ctx, "gpus", list); - } - - if (hw.networks != NULL && hw.network_count > 0) { - cJSON *list = cJSON_CreateArray(); - for (int i = 0; i < hw.network_count; ++i) { - cJSON_AddItemToArray(list, network_to_json_obj(hw.networks[i])); - } - cJSON_AddItemToObject(ctx, "networks", list); - } - - if (hw.storages != NULL && hw.storage_count > 0) { - cJSON *list = cJSON_CreateArray(); - for (int i = 0; i < hw.storage_count; ++i) { - cJSON_AddItemToArray(list, storage_to_json_obj(hw.storages[i])); - } - cJSON_AddItemToObject(ctx, "storages", list); - } - - char *ctx_str = cJSON_PrintUnformatted(ctx); - groq_analyze_hardware(ctx_str, config.ai_prompt); - free(ctx_str); - cJSON_Delete(ctx); - if (!config.watch_mode) { free(config.ai_prompt); config.ai_prompt = NULL; diff --git a/src/util.c b/src/util.c index d35ae7d..d8202a7 100644 --- a/src/util.c +++ b/src/util.c @@ -123,70 +123,87 @@ free_hardware(SystemHardware *hw) } } -void -output_json(const Config *config, const SystemHardware *hw) +cJSON * +hardware_to_json(const SystemHardware *hw) { - cJSON *json = cJSON_CreateObject(); - cJSON_AddNumberToObject(json, "schema_version", 1); - cJSON_AddStringToObject(json, "tool", "hwmonitor"); + cJSON *json_obj = cJSON_CreateObject(); + + if (hw == NULL || json_obj == NULL) { + return NULL; + } if (hw->battery != NULL) { - cJSON_AddItemToObject(json, "battery", battery_to_json_obj(hw->battery)); + cJSON_AddItemToObject(json_obj, "battery", battery_to_json_obj(hw->battery)); } if (hw->cpu != NULL) { - cJSON_AddItemToObject(json, "cpu", cpu_to_json_obj(hw->cpu)); + cJSON_AddItemToObject(json_obj, "cpu", cpu_to_json_obj(hw->cpu)); } - if (hw->gpus != NULL && hw->gpu_count > 0) { - cJSON *gpu_list = cJSON_CreateArray(); - for (int i = 0; i < hw->gpu_count; ++i) { - cJSON_AddItemToArray(gpu_list, gpu_to_json_obj(hw->gpus[i])); - } - cJSON_AddItemToObject(json, "gpus", gpu_list); + if (hw->mainboard != NULL) { + cJSON_AddItemToObject(json_obj, "mainboard", mainboard_to_json_obj(hw->mainboard)); } - if (hw->mainboard != NULL) { - cJSON_AddItemToObject(json, "mainboard", mainboard_to_json_obj(hw->mainboard)); + if (hw->os != NULL) { + cJSON_AddItemToObject(json_obj, "os", os_to_json_obj(hw->os)); } - if (hw->networks != NULL && hw->network_count > 0) { - cJSON *network_list = cJSON_CreateArray(); - for (int i = 0; i < hw->network_count; ++i) { - cJSON_AddItemToArray(network_list, network_to_json_obj(hw->networks[i])); - } - cJSON_AddItemToObject(json, "networks", network_list); + if (hw->ram != NULL) { + cJSON_AddItemToObject(json_obj, "ram", ram_to_json_obj(hw->ram)); } - if (hw->os != NULL) { - cJSON_AddItemToObject(json, "os", os_to_json_obj(hw->os)); + if (hw->gpus != NULL && hw->gpu_count > 0) { + cJSON *list = cJSON_CreateArray(); + for (int i = 0; i < hw->gpu_count; ++i) { + cJSON_AddItemToArray(list, gpu_to_json_obj(hw->gpus[i])); + } + cJSON_AddItemToObject(json_obj, "gpus", list); } - if (hw->ram != NULL) { - cJSON_AddItemToObject(json, "ram", ram_to_json_obj(hw->ram)); + if (hw->networks != NULL && hw->network_count > 0) { + cJSON *list = cJSON_CreateArray(); + for (int i = 0; i < hw->network_count; ++i) { + cJSON_AddItemToArray(list, network_to_json_obj(hw->networks[i])); + } + cJSON_AddItemToObject(json_obj, "networks", list); } if (hw->storages != NULL && hw->storage_count > 0) { - cJSON *storage_list = cJSON_CreateArray(); + cJSON *list = cJSON_CreateArray(); for (int i = 0; i < hw->storage_count; ++i) { - cJSON_AddItemToArray(storage_list, storage_to_json_obj(hw->storages[i])); + cJSON_AddItemToArray(list, storage_to_json_obj(hw->storages[i])); } - cJSON_AddItemToObject(json, "storages", storage_list); + cJSON_AddItemToObject(json_obj, "storages", list); } + + return json_obj; +} - char *json_str = cJSON_Print(json); - if (config->output_file != NULL) { - if (file_write_string(config->output_file, json_str)) { - printf("Success: Hardware report saved to '%s'\n", config->output_file); +void +output_json(const Config *config, const SystemHardware *hw) +{ + cJSON *json_obj = hardware_to_json(hw); + if (json_obj == NULL) { + return; + } + + char *json_str = cJSON_Print(json_obj); + if (json_str != NULL) { + if (config->output_file != NULL) { + if (file_write_string(config->output_file, json_str)) { + printf("Success: Hardware report saved to '%s'\n", config->output_file); + } + } else { + printf("%s\n", json_str); } - } else { - printf("%s\n", json_str); + free(json_str); } - free(json_str); - cJSON_Delete(json); + + cJSON_Delete(json_obj); } + void output_plaintext(const SystemHardware *hw) { From 980e4c3cc237dee093c28b2adcaae5923bbb5d82 Mon Sep 17 00:00:00 2001 From: th0truth Date: Sat, 8 Aug 2026 15:53:29 +0300 Subject: [PATCH 2/5] refactor(.h): remove docstrings --- include/api/groq.h | 9 -------- include/api/http.h | 27 ++--------------------- include/base.h | 5 ----- include/battery.h | 24 --------------------- include/cpu.h | 9 -------- include/display.h | 51 -------------------------------------------- include/file.h | 19 ----------------- include/gpu.h | 28 ------------------------ include/io.h | 20 ----------------- include/mainboard.h | 24 --------------------- include/network.h | 9 -------- include/os.h | 24 --------------------- include/ram.h | 9 -------- include/storage.h | 32 ---------------------------- include/theme.h | 13 ------------ include/util.h | 52 --------------------------------------------- 16 files changed, 2 insertions(+), 353 deletions(-) diff --git a/include/api/groq.h b/include/api/groq.h index 912f883..e4928fd 100644 --- a/include/api/groq.h +++ b/include/api/groq.h @@ -6,17 +6,8 @@ extern "C" { #include "base.h" -/** - * \brief Default model optimized for fast CLI usage. - */ #define GROQ_DEFAULT_MODEL "llama-3.1-8b-instant" -/** - * \brief Queries Groq to analyze the provided hardware telemetry. - * \param[in] hardware_json The JSON representation of the system/hardware. - * \param[in] user_prompt The user's specific question or command. - * \return true on successful API request and parsing, false otherwise. - */ bool groq_analyze_hardware(const char *hardware_json, const char *user_prompt); #ifdef __cplusplus diff --git a/include/api/http.h b/include/api/http.h index a9ab00c..2661c8e 100644 --- a/include/api/http.h +++ b/include/api/http.h @@ -1,8 +1,3 @@ -/** - * \file http.h - * \brief Generic HTTP networking subsystem using libcurl. - */ - #pragma once #ifdef __cplusplus @@ -13,30 +8,12 @@ extern "C" { #include #include -/** - * \struct HttpResponse - * \brief Buffer to store the dynamically allocated raw string response from an HTTP request. - */ typedef struct { - char *data; /**< Null-terminated string containing the HTTP response body */ - size_t size; /**< Size of the response data in bytes (excluding null terminator) */ + char *data; + size_t size; } HttpResponse; -/** - * \brief Performs a blocking HTTP POST request. - * \param[in] url The target endpoint URL. - * \param[in] headers A linked list of HTTP headers (e.g., Authorization, Content-Type). - * \param[in] payload The raw string payload to send in the body of the request. - * \param[out] out_response Pointer to an HttpResponse struct to hold the result. - * The caller MUST call http_free_response() on success to avoid leaks. - * \return true if the network request succeeded (CURLE_OK), false otherwise. - */ bool http_post(const char *url, struct curl_slist *headers, const char *payload, HttpResponse *out_response); - -/** - * \brief Safely frees the dynamically allocated memory inside an HttpResponse. - * \param[in,out] response Pointer to the HttpResponse to clean up. - */ void http_free_response(HttpResponse *response); #ifdef __cplusplus diff --git a/include/base.h b/include/base.h index 9a6b504..326fbd1 100644 --- a/include/base.h +++ b/include/base.h @@ -1,8 +1,3 @@ -/** - * \file base.h - * \brief Header file for base module. - */ - #pragma once #ifdef __cplusplus diff --git a/include/battery.h b/include/battery.h index 6214ae8..2860e83 100644 --- a/include/battery.h +++ b/include/battery.h @@ -1,8 +1,3 @@ -/** - * \file battery.h - * \brief Header file for battery module. - */ - #pragma once #ifdef __cplusplus @@ -12,10 +7,6 @@ extern "C" { #include #include -/** - * \struct BATTERY - * \brief Structure to hold battery metrics and vendor information. - */ typedef struct { uint16_t capacity; float voltage_min_design; @@ -33,23 +24,8 @@ typedef struct { char *serial; } BATTERY; -/** - * \brief Discovers and parses battery information from /sys/class/power_supply. - * \return Pointer to a newly allocated BATTERY struct, or NULL on failure. - */ BATTERY *battery_get_info(void); - -/** - * \brief Deep-frees a BATTERY structure and its internal strings. - * \param[in] battery Pointer to the structure to free. - */ void free_battery(BATTERY *battery); - -/** - * \brief Converts a BATTERY structure to a cJSON object. - * \param[in] battery Pointer to the BATTERY structure. - * \return Pointer to a cJSON object (caller must delete). - */ cJSON *battery_to_json_obj(const BATTERY *battery); #ifdef __cplusplus diff --git a/include/cpu.h b/include/cpu.h index 082fc0d..e1fb60e 100644 --- a/include/cpu.h +++ b/include/cpu.h @@ -1,8 +1,3 @@ -/** - * \file cpu.h - * \brief Header file for cpu module. - */ - #pragma once #ifdef __cplusplus @@ -12,10 +7,6 @@ extern "C" { #include #include -/** - * \struct CPU - * \brief Structure to hold CPU metrics and vendor information. - */ typedef struct { char *vendor_id; char *model_name; diff --git a/include/display.h b/include/display.h index 595b903..0616813 100644 --- a/include/display.h +++ b/include/display.h @@ -1,8 +1,3 @@ -/** - * \file display.h - * \brief Header file for display module. - */ - #pragma once #ifdef __cplusplus @@ -18,60 +13,14 @@ extern "C" { #include "storage.h" #include "network.h" -/** - * \brief Displays OS information to the console. - * \param[in] os Pointer to the OS structure. - */ void display_os(const OS *os); - -/** - * \brief Displays CPU information to the console. - * \param[in] cpu Pointer to the CPU structure. - */ void display_cpu(const CPU *cpu); - -/** - * \brief Displays RAM information to the console. - * \param[in] ram Pointer to the RAM structure. - */ void display_ram(const RAM *ram); - -/** - * \brief Displays information for all GPUs to the console. - * \param[in] gpus Array of pointers to GPU structures. - * \param[in] count Number of GPUs in the array. - */ void display_gpus(GPU **gpus, int count); - -/** - * \brief Displays Battery information to the console. - * \param[in] battery Pointer to the BATTERY structure. - */ void display_battery(const BATTERY *battery); - -/** - * \brief Displays Mainboard information to the console. - * \param[in] mainboard Pointer to the MAINBOARD structure. - */ void display_mainboard(const MAINBOARD *mainboard); - -/** - * \brief Displays information for all Storage devices to the console. - * \param[in] storages Array of pointers to STORAGE structures. - * \param[in] count Number of storages in the array. - */ void display_storages(STORAGE **storages, int count); - -/** - * \brief Displays information for all Network interfaces to the console. - * \param[in] networks Array of pointers to Network structures. - * \param[in] count Number of networks in the array. - */ void display_networks(Network **networks, int count); - -/** - * \brief Clears the terminal screen and resets cursor position using ANSI codes. - */ void display_clear(void); #ifdef __cplusplus diff --git a/include/file.h b/include/file.h index c97fcf7..7ac7469 100644 --- a/include/file.h +++ b/include/file.h @@ -1,8 +1,3 @@ -/** - * \file file.h - * \brief Header file for file module. - */ - #pragma once #ifdef __cplusplus @@ -11,21 +6,7 @@ extern "C" { #include -/** - * \brief Reads a file into a dynamically allocated string, stripping specified characters. - * \param[in] filename Path to the file to read. - * \param[in] exclude Characters to strip out from the file content. - * \param[in] verbose If true, prints warning messages on failure. - * \return A null-terminated dynamically allocated string, or NULL on error. - */ char *file_read_stripped(const char *filename, const char *exclude, bool verbose); - -/** - * \brief Writes a string to a file. - * \param[in] filename Path to the file to write. - * \param[in] data String data to write. - * \return true on success, false on failure. - */ bool file_write_string(const char *filename, const char *data); #ifdef __cplusplus diff --git a/include/gpu.h b/include/gpu.h index 2813066..037f492 100644 --- a/include/gpu.h +++ b/include/gpu.h @@ -1,8 +1,3 @@ -/** - * \file gpu.h - * \brief Header file for gpu module. - */ - #pragma once #ifdef __cplusplus @@ -14,7 +9,6 @@ extern "C" { #define MAX_GPUS 16 -/* GPU Vendor IDs (PCI hex codes) */ #define PCI_VENDOR_NVIDIA "0x10de" #define PCI_VENDOR_INTEL "0x8086" #define PCI_VENDOR_AMD "0x1002" @@ -37,31 +31,9 @@ typedef struct { char *pci_slot_name; } GPU; -/** - * \brief Discovers all GPUs in the system via sysfs. - * \param[out] count Pointer to an integer where the number of GPUs will be stored. - * \return A dynamically allocated array of GPU pointers, or NULL on failure. - */ GPU **gpu_get_all(int *count); - -/** - * \brief Frees a single GPU structure and all its internal strings. - * \param[in] gpu Pointer to the GPU structure to free. - */ void free_gpu(GPU *gpu); - -/** - * \brief Frees an array of GPU pointers and the array itself. - * \param[in] gpus Array of GPU pointers to free. - * \param[in] count Number of GPUs in the array. - */ void free_gpus(GPU **gpus, int count); - -/** - * \brief Converts a GPU structure to a cJSON object. - * \param[in] gpu Pointer to the GPU structure. - * \return Pointer to a cJSON object (caller must delete). - */ cJSON *gpu_to_json_obj(const GPU *gpu); #ifdef __cplusplus diff --git a/include/io.h b/include/io.h index 090a5ab..ea605d3 100644 --- a/include/io.h +++ b/include/io.h @@ -1,8 +1,3 @@ -/** - * \file io.h - * \brief Header file for string parsing utilities. - */ - #pragma once #ifdef __cplusplus @@ -11,22 +6,7 @@ extern "C" { #include "base.h" -/** - * \brief Searches a source string for a key and returns the associated value. - * \param[in] source The source string to search. - * \param[in] search The key string to find. - * \param[in] delim Delimiter characters to find the end of the value. - * \return Dynamically allocated string (caller must free), or NULL if not found. - */ char *str_find_value(const char *source, const char *search, const char *delim); - -/** - * \brief Searches a source string for a key and returns the value as a double. - * \param[in] source The source string to search. - * \param[in] search The key string to find. - * \param[in] delim Delimiter characters to find the end of the value. - * \return Value as a double, or 0.0 if not found. - */ double str_parse_value(const char *source, const char *search, const char *delim); #ifdef __cplusplus diff --git a/include/mainboard.h b/include/mainboard.h index 552faec..a4c7714 100644 --- a/include/mainboard.h +++ b/include/mainboard.h @@ -1,8 +1,3 @@ -/** - * \file mainboard.h - * \brief Header file for mainboard/system DMI discovery. - */ - #pragma once #ifdef __cplusplus @@ -11,10 +6,6 @@ extern "C" { #include -/** - * \struct MAINBOARD - * \brief Structure to hold system and motherboard DMI information. - */ typedef struct { char *sys_vendor; char *product_name; @@ -25,23 +16,8 @@ typedef struct { char *serial; } MAINBOARD; -/** - * \brief Discovers and parses Mainboard/System information from sysfs DMI data. - * \return Pointer to a newly allocated MAINBOARD struct, or NULL on failure. - */ MAINBOARD *mainboard_get_info(void); - -/** - * \brief Deep-frees a MAINBOARD structure and its internal strings. - * \param[in] mainboard Pointer to the MAINBOARD structure to free. - */ void free_mainboard(MAINBOARD *mainboard); - -/** - * \brief Converts a MAINBOARD structure to a cJSON object. - * \param[in] mainboard Pointer to the MAINBOARD structure. - * \return Pointer to a cJSON object (caller must delete). - */ cJSON *mainboard_to_json_obj(const MAINBOARD *mainboard); #ifdef __cplusplus diff --git a/include/network.h b/include/network.h index 6b5cbb3..e4b0ddb 100644 --- a/include/network.h +++ b/include/network.h @@ -1,8 +1,3 @@ -/** - * \file network.h - * \brief Header file for network module. - */ - #pragma once #ifdef __cplusplus @@ -14,10 +9,6 @@ extern "C" { #define MAX_NETWORKS 32 -/** - * \struct Network - * \brief Structure to hold network interface information. - */ typedef struct { char *interface; char *driver; diff --git a/include/os.h b/include/os.h index 40695b5..e3631bb 100644 --- a/include/os.h +++ b/include/os.h @@ -1,8 +1,3 @@ -/** - * \file os.h - * \brief Header file for os module. - */ - #pragma once #ifdef __cplusplus @@ -11,10 +6,6 @@ extern "C" { #include -/** - * \struct OS - * \brief Structure to hold operating system release information. - */ typedef struct { char *name; char *version_id; @@ -25,23 +16,8 @@ typedef struct { char *de_id; } OS; -/** - * \brief Discovers and parses OS information (typically from /etc/os-release). - * \return Pointer to a newly allocated OS struct, or NULL on failure. - */ OS *os_get_info(void); - -/** - * \brief Deep-frees an OS structure and its internal strings. - * \param[in] os Pointer to the OS structure to free. - */ void free_os(OS *os); - -/** - * \brief Converts an OS structure to a cJSON object. - * \param[in] os Pointer to the OS structure. - * \return Pointer to a cJSON object (caller must delete). - */ cJSON *os_to_json_obj(const OS *os); #ifdef __cplusplus diff --git a/include/ram.h b/include/ram.h index 0e0e5ec..c8c37de 100644 --- a/include/ram.h +++ b/include/ram.h @@ -1,8 +1,3 @@ -/** - * \file ram.h - * \brief Header file for ram module. - */ - #pragma once #ifdef __cplusplus @@ -12,10 +7,6 @@ extern "C" { #include #include -/** - * \struct RAM - * \brief Structure to hold system memory metrics. - */ typedef struct { uint64_t total; uint64_t free; diff --git a/include/storage.h b/include/storage.h index 6bbcc1d..e494fa4 100644 --- a/include/storage.h +++ b/include/storage.h @@ -1,8 +1,3 @@ -/** - * \file storage.h - * \brief Header file for system storage and block device discovery. - */ - #pragma once #ifdef __cplusplus @@ -13,10 +8,6 @@ extern "C" { #include #include -/** - * \struct STORAGE - * \brief Structure to hold storage drive details. - */ typedef struct { char *device; bool removable; @@ -27,32 +18,9 @@ typedef struct { uint64_t size_bytes; } STORAGE; -/** - * \brief Discovers all block devices in the system via sysfs. - * Returns a dynamically allocated array of STORAGE pointers. - * \param[out] count Pointer to an integer where the number of drives will be stored. - * \return Array of pointers to STORAGE structures. - */ STORAGE **storage_get_all(int *count); - -/** - * \brief Frees a single STORAGE structure and its internal strings. - * \param[in] storage Pointer to the STORAGE structure to free. - */ void free_storage(STORAGE *storage); - -/** - * \brief Frees an array of STORAGE pointers and the array itself. - * \param[in] storages Array of pointers. - * \param[in] count Number of elements. - */ void free_storages(STORAGE **storages, int count); - -/** - * \brief Converts a STORAGE structure to a cJSON object. - * \param[in] storage Pointer to the STORAGE structure. - * \return Pointer to a cJSON object (caller must delete). - */ cJSON *storage_to_json_obj(const STORAGE *storage); #ifdef __cplusplus diff --git a/include/theme.h b/include/theme.h index 5a49f54..36f43e9 100644 --- a/include/theme.h +++ b/include/theme.h @@ -1,8 +1,3 @@ -/** - * \file theme.h - * \brief Header file for theme module. - */ - #pragma once #ifdef __cplusplus @@ -11,10 +6,6 @@ extern "C" { #include "base.h" -/** - * \enum ThemeColor - * \brief Available theme color categories. - */ typedef enum { COLOR_PRIMARY, COLOR_LABEL, @@ -25,10 +16,6 @@ typedef enum { COLOR_RESET } ThemeColor; -/** - * \struct Theme - * \brief Holds ANSI escape codes for styling console output. - */ typedef struct { const char *primary; const char *label; diff --git a/include/util.h b/include/util.h index e66e934..64167d8 100644 --- a/include/util.h +++ b/include/util.h @@ -1,8 +1,3 @@ -/** - * \file util.h - * \brief Header file for utility orchestration, formatting, and CLI parsing. - */ - #pragma once #ifdef __cplusplus @@ -19,10 +14,6 @@ extern "C" { #include "storage.h" #include "network.h" -/** - * \struct Config - * \brief Config structure to hold user preferences from flags. - */ typedef struct { char *ai_prompt; char *output_file; @@ -39,10 +30,6 @@ typedef struct { bool watch_mode; } Config; -/** - * \struct SystemHardware - * \brief Centralized struct to hold all fetched hardware data. - */ typedef struct { BATTERY *battery; CPU *cpu; @@ -57,55 +44,16 @@ typedef struct { int storage_count; } SystemHardware; -/** - * \brief Fetches required hardware data based on configuration. - * \param[in] config Pointer to the Config structure. - * \param[out] hw Pointer to the SystemHardware structure to populate. - */ void fetch_hardware(const Config *config, SystemHardware *hw); - -/** - * \brief Frees all allocated memory within the SystemHardware struct. - * \param[in,out] hw Pointer to the SystemHardware structure. - */ void free_hardware(SystemHardware *hw); -/** - * \brief Formats a byte size into a human-readable string. - * \param[in] target_format Format unit (e.g. "GiB", "MiB"). - * \param[in] bytes Number of bytes. - * \param[out] buffer Output character buffer. - * \param[in] buf_size Size of the output buffer. - */ void format_size(char *target_format, uint64_t bytes, char *buffer, size_t buf_size); cJSON *hardware_to_json(const SystemHardware *hw); - -/** - * \brief Generates and prints (or saves) the JSON output. - * \param[in] config Pointer to the Config structure. - * \param[in] hw Pointer to the SystemHardware structure. - */ void output_json(const Config *config, const SystemHardware *hw); - -/** - * \brief Renders the hardware data in a formatted plain-text view. - * \param[in] hw Pointer to the SystemHardware structure. - */ void output_plaintext(const SystemHardware *hw); -/** - * \brief Parses command-line arguments and populates the Config struct. - * \param[in] argc Argument count. - * \param[in] argv Argument array. - * \param[out] config Pointer to the Config structure. - */ void parse_arguments(int argc, char **argv, Config *config); - -/** - * \brief Prints usage and help documentation for the tool. - * \param[in] prog_name Name of the program binary. - */ void print_usage(const char *prog_name); #ifdef __cplusplus From ba2ad79492e1f534a06c077072f7de3282c2a7d3 Mon Sep 17 00:00:00 2001 From: th0truth Date: Sat, 8 Aug 2026 16:39:12 +0300 Subject: [PATCH 3/5] refactor(file): add sysfs directory enumerate --- include/file.h | 3 ++ src/linux/file.c | 44 ++++++++++++++++++++++++++++++ src/linux/hardware/gpu.c | 35 +++++++----------------- src/linux/hardware/network.c | 28 +------------------ src/linux/hardware/storage.c | 53 ++++++++++-------------------------- 5 files changed, 72 insertions(+), 91 deletions(-) diff --git a/include/file.h b/include/file.h index 7ac7469..30faff7 100644 --- a/include/file.h +++ b/include/file.h @@ -9,6 +9,9 @@ extern "C" { char *file_read_stripped(const char *filename, const char *exclude, bool verbose); bool file_write_string(const char *filename, const char *data); +typedef void *(*sysfs_parse_fn)(const char *entry_name); +void **sysfs_enumerate(const char *dir_path, sysfs_parse_fn parse_fn, int max_items, int *out_count); + #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/src/linux/file.c b/src/linux/file.c index 6e47ebf..8209d1c 100644 --- a/src/linux/file.c +++ b/src/linux/file.c @@ -1,5 +1,8 @@ +#include "file.h" #include "base.h" +#include "cJSON.h" #include +#include #define FILE_READ_BUFFER 4096 @@ -72,3 +75,44 @@ file_write_string(const char *filename, const char *data) fclose(fp); return true; } + +void ** +sysfs_enumerate(const char *dir_path, sysfs_parse_fn parse_fn, int max_items, int *out_count) +{ + if (out_count == NULL) { + return NULL; + } + *out_count = 0; + + if (dir_path == NULL || parse_fn == NULL || max_items <= 0) { + return NULL; + } + + DIR *dir = opendir(dir_path); + if (dir == NULL) { + return NULL; + } + + void **list = calloc(max_items, sizeof(void *)); + if (list == NULL) { + closedir(dir); + return NULL; + } + + struct dirent *entry; + while ((entry = readdir(dir)) != NULL && *out_count < max_items) { + if (entry->d_name[0] == '.') { + continue; + } + + void *item = parse_fn(entry->d_name); + if (item != NULL) { + list[(*out_count)++] = item; + } + } + + closedir(dir); + + return list; +} + diff --git a/src/linux/hardware/gpu.c b/src/linux/hardware/gpu.c index e3f0f57..c2b0245 100644 --- a/src/linux/hardware/gpu.c +++ b/src/linux/hardware/gpu.c @@ -2,7 +2,6 @@ #include "file.h" #include "io.h" #include "gpu.h" -#include static void gpu_handle_nvidia(GPU *gpu) @@ -78,34 +77,20 @@ gpu_parse_sysfs(const char *card_name) return gpu; } -GPU ** -gpu_get_all(int *count) +static void * +gpu_parse_entry(const char *name) { - *count = 0; - DIR *dir = opendir("/sys/class/drm"); - if (dir == NULL) { - return NULL; - } - - GPU **list = calloc(MAX_GPUS, sizeof(GPU *)); - if (list == NULL) { - closedir(dir); - return NULL; + if (strncmp(name, "card", 4) == 0 && isdigit((unsigned char)name[4])) { + return (void *)gpu_parse_sysfs(name); } - struct dirent *entry; - while ((entry = readdir(dir)) != NULL && *count < MAX_GPUS) { - /* Only process directories named "cardX" where X is a digit (ignore renderD nodes) */ - if (strncmp(entry->d_name, "card", 4) == 0 && isdigit((unsigned char)entry->d_name[4])) { - GPU *gpu = gpu_parse_sysfs(entry->d_name); - if (gpu != NULL) { - list[(*count)++] = gpu; - } - } - } + return NULL; +} - closedir(dir); - return list; +GPU ** +gpu_get_all(int *count) +{ + return (GPU **)sysfs_enumerate("/sys/class/drm", gpu_parse_entry, MAX_GPUS, count); } void diff --git a/src/linux/hardware/network.c b/src/linux/hardware/network.c index 33fb882..d2f9972 100644 --- a/src/linux/hardware/network.c +++ b/src/linux/hardware/network.c @@ -2,7 +2,6 @@ #include "file.h" #include "io.h" #include "network.h" -#include Network * network_get_info(const char *interface) @@ -39,32 +38,7 @@ network_get_info(const char *interface) Network ** network_get_all(int *count) { - *count = 0; - DIR *dir = opendir("/sys/class/net"); - if (dir == NULL) { - return NULL; - } - - Network **list = calloc(MAX_NETWORKS, sizeof(Network *)); - if (list == NULL) { - closedir(dir); - return NULL; - } - - struct dirent *entry; - while ((entry = readdir(dir)) != NULL && *count < MAX_NETWORKS) { - if (entry->d_name[0] == '.') { - continue; - } - - Network *net = network_get_info(entry->d_name); - if (net != NULL) { - list[(*count)++] = net; - } - } - - closedir(dir); - return list; + return (Network **)sysfs_enumerate("/sys/class/net", (sysfs_parse_fn)network_get_info, MAX_NETWORKS, count); } void diff --git a/src/linux/hardware/storage.c b/src/linux/hardware/storage.c index 6151f53..9883594 100644 --- a/src/linux/hardware/storage.c +++ b/src/linux/hardware/storage.c @@ -2,8 +2,6 @@ #include "file.h" #include "io.h" #include "storage.h" -#include -#include #define MAX_STORAGES 64 @@ -24,7 +22,7 @@ trim_trailing_spaces(char *str) static STORAGE * storage_parse_sysfs(const char *block_name) { - char buffer[PATH_MAX]; + char buffer[BUFFER_SIZE]; STORAGE *storage = calloc(1, sizeof(*storage)); if (storage == NULL) { return NULL; @@ -68,46 +66,23 @@ storage_parse_sysfs(const char *block_name) return storage; } -STORAGE ** -storage_get_all(int *count) +static void * +storage_parse_entry(const char *name) { - *count = 0; - DIR *dir = opendir("/sys/class/block"); - if (dir == NULL) { - return NULL; - } - - STORAGE **list = calloc(MAX_STORAGES, sizeof(STORAGE *)); - if (list == NULL) { - closedir(dir); + /* Filter virtual devices */ + if (strncmp(name, "loop", 4) == 0 || + strncmp(name, "ram", 3) == 0 || + strncmp(name, "zram", 4) == 0) { return NULL; } + + return (void *)storage_parse_sysfs(name); +} - struct dirent *entry; - while ((entry = readdir(dir)) != NULL && *count < MAX_STORAGES) { - if (entry->d_name[0] == '.') { - continue; - } - - /* Ignore loop, ram, and zram virtual block devices */ - if (strncmp(entry->d_name, "loop", 4) == 0) { - continue; - } - if (strncmp(entry->d_name, "ram", 3) == 0) { - continue; - } - if (strncmp(entry->d_name, "zram", 4) == 0) { - continue; - } - - STORAGE *s = storage_parse_sysfs(entry->d_name); - if (s != NULL) { - list[(*count)++] = s; - } - } - - closedir(dir); - return list; +STORAGE ** +storage_get_all(int *count) +{ + return (STORAGE **)sysfs_enumerate("/sys/class/block", storage_parse_entry, MAX_STORAGES, count); } void From f6ec185293251f072eb0b796630ad3318574bb99 Mon Sep 17 00:00:00 2001 From: th0truth Date: Sat, 8 Aug 2026 16:59:46 +0300 Subject: [PATCH 4/5] refactor(file): add sysfs_read_attr_fmt --- include/file.h | 1 + src/linux/file.c | 15 +++++++++++++++ src/linux/hardware/battery.c | 4 +--- src/linux/hardware/cpu.c | 6 +----- src/linux/hardware/gpu.c | 24 ++++++------------------ src/linux/hardware/network.c | 5 +---- src/linux/hardware/storage.c | 24 +++++++----------------- 7 files changed, 32 insertions(+), 47 deletions(-) diff --git a/include/file.h b/include/file.h index 30faff7..9ba574a 100644 --- a/include/file.h +++ b/include/file.h @@ -11,6 +11,7 @@ bool file_write_string(const char *filename, const char *data); typedef void *(*sysfs_parse_fn)(const char *entry_name); void **sysfs_enumerate(const char *dir_path, sysfs_parse_fn parse_fn, int max_items, int *out_count); +char *sysfs_read_attr_fmt(const char *exclude, const char *fmt, ...); #ifdef __cplusplus } diff --git a/src/linux/file.c b/src/linux/file.c index 8209d1c..9c21165 100644 --- a/src/linux/file.c +++ b/src/linux/file.c @@ -3,6 +3,8 @@ #include "cJSON.h" #include #include +#include +#include #define FILE_READ_BUFFER 4096 @@ -116,3 +118,16 @@ sysfs_enumerate(const char *dir_path, sysfs_parse_fn parse_fn, int max_items, in return list; } +char * +sysfs_read_attr_fmt(const char *exclude, const char *fmt, ...) +{ + char buffer[BUFFER_SIZE]; + va_list args; + + va_start(args, fmt); + vsnprintf(buffer, sizeof(buffer), fmt, args); + va_end(args); + + return file_read_stripped(buffer, exclude != NULL ? exclude : "\n", false); +} + diff --git a/src/linux/hardware/battery.c b/src/linux/hardware/battery.c index 98e211b..b05d525 100644 --- a/src/linux/hardware/battery.c +++ b/src/linux/hardware/battery.c @@ -9,14 +9,12 @@ BATTERY * battery_get_info(void) { - char buffer[BUFFER_SIZE]; char *uevent = NULL; bool found = false; /* Search for an active battery interface (typically BAT0 to BAT5) */ for (int i = 0; i < 5; ++i) { - snprintf(buffer, sizeof(buffer), "/sys/class/power_supply/BAT%d/uevent", i); - uevent = file_read_stripped(buffer, "=", false); + uevent = sysfs_read_attr_fmt("=", "/sys/class/power_supply/BAT%d/uevent", i); if (uevent != NULL) { found = true; diff --git a/src/linux/hardware/cpu.c b/src/linux/hardware/cpu.c index 06b22f4..2aed6f3 100644 --- a/src/linux/hardware/cpu.c +++ b/src/linux/hardware/cpu.c @@ -37,16 +37,12 @@ cpu_get_total_cores(void) static float cpu_get_total_freq_mhz(uint16_t core_id, const char *filename) { - char buffer[BUFFER_SIZE]; - snprintf(buffer, sizeof(buffer), "/sys/devices/system/cpu/cpu%u/cpufreq/%s", core_id, filename); - - char *cpu_freq = file_read_stripped(buffer, "\n", true); + char *cpu_freq = sysfs_read_attr_fmt("\n", "/sys/devices/system/cpu/cpu%u/cpufreq/%s", core_id, filename); if (cpu_freq == NULL) { return -1; } int32_t khz = atoi(cpu_freq); - free(cpu_freq); if (khz <= 0) { diff --git a/src/linux/hardware/gpu.c b/src/linux/hardware/gpu.c index c2b0245..bb8170f 100644 --- a/src/linux/hardware/gpu.c +++ b/src/linux/hardware/gpu.c @@ -6,11 +6,8 @@ static void gpu_handle_nvidia(GPU *gpu) { - char buffer[BUFFER_SIZE]; /* NVIDIA driver exposes detailed information via procfs */ - snprintf(buffer, sizeof(buffer), "/proc/driver/nvidia/gpus/%s/information", gpu->pci_slot_name); - - char *n_info = file_read_stripped(buffer, "\t", false); + char *n_info = sysfs_read_attr_fmt("\t", "/proc/driver/nvidia/gpus/%s/information", gpu->pci_slot_name); if (n_info != NULL) { gpu->model = str_find_value(n_info, "Model: ", "\n"); gpu->irq = str_find_value(n_info, "IRQ: ", "\n"); @@ -32,11 +29,8 @@ gpu_handle_nvidia(GPU *gpu) static GPU * gpu_parse_sysfs(const char *card_name) { - char buffer[BUFFER_SIZE]; /* Read PCI Vendor ID to verify hardware presence */ - snprintf(buffer, sizeof(buffer), "/sys/class/drm/%s/device/vendor", card_name); - - char *vendor = file_read_stripped(buffer, "\n", false); + char *vendor = sysfs_read_attr_fmt("\n", "/sys/class/drm/%s/device/vendor", card_name); if (vendor == NULL) { return NULL; } @@ -49,18 +43,12 @@ gpu_parse_sysfs(const char *card_name) gpu->vendor = vendor; /* Basic PCI identifiers */ - snprintf(buffer, sizeof(buffer), "/sys/class/drm/%s/device/device", card_name); - gpu->device_id = file_read_stripped(buffer, "\n", false); - - snprintf(buffer, sizeof(buffer), "/sys/class/drm/%s/device/subsystem_device", card_name); - gpu->subsys_device = file_read_stripped(buffer, "\n", false); - - snprintf(buffer, sizeof(buffer), "/sys/class/drm/%s/device/subsystem_vendor", card_name); - gpu->subsys_vendor = file_read_stripped(buffer, "\n", false); + gpu->device_id = sysfs_read_attr_fmt("\n", "/sys/class/drm/%s/device/device", card_name); + gpu->subsys_device = sysfs_read_attr_fmt("\n", "/sys/class/drm/%s/device/subsystem_device", card_name); + gpu->subsys_vendor = sysfs_read_attr_fmt("\n", "/sys/class/drm/%s/device/subsystem_vendor", card_name); /* Parse uevent file for driver and slot information */ - snprintf(buffer, sizeof(buffer), "/sys/class/drm/%s/device/uevent", card_name); - char *uevent = file_read_stripped(buffer, "=", false); + char *uevent = sysfs_read_attr_fmt("=", "/sys/class/drm/%s/device/uevent", card_name); if (uevent != NULL) { gpu->driver = str_find_value(uevent, "DRIVER", "\n"); gpu->pci_id = str_find_value(uevent, "PCI_ID", "\n"); diff --git a/src/linux/hardware/network.c b/src/linux/hardware/network.c index d2f9972..0a23193 100644 --- a/src/linux/hardware/network.c +++ b/src/linux/hardware/network.c @@ -17,10 +17,7 @@ network_get_info(const char *interface) net->interface = strdup(interface); - char path[BUFFER_SIZE]; - snprintf(path, sizeof(path), "/sys/class/net/%s/device/uevent", interface); - - char *uevent = file_read_stripped(path, "=", false); + char *uevent = sysfs_read_attr_fmt("=", "/sys/class/net/%s/device/uevent", interface); if (uevent == NULL) { /* Some interfaces might not have a 'device' link (e.g., loopback, virtual) */ return net; diff --git a/src/linux/hardware/storage.c b/src/linux/hardware/storage.c index 9883594..241d1f5 100644 --- a/src/linux/hardware/storage.c +++ b/src/linux/hardware/storage.c @@ -22,7 +22,6 @@ trim_trailing_spaces(char *str) static STORAGE * storage_parse_sysfs(const char *block_name) { - char buffer[BUFFER_SIZE]; STORAGE *storage = calloc(1, sizeof(*storage)); if (storage == NULL) { return NULL; @@ -31,37 +30,28 @@ storage_parse_sysfs(const char *block_name) storage->device = strdup(block_name); /* Size in sysfs is reported in 512-byte sectors */ - snprintf(buffer, sizeof(buffer), "/sys/class/block/%s/size", block_name); - char *size_str = file_read_stripped(buffer, "\n", false); + char *size_str = sysfs_read_attr_fmt("\n", "/sys/class/block/%s/size", block_name); if (size_str != NULL) { storage->size_bytes = (uint64_t)strtoull(size_str, NULL, 10) * 512ULL; free(size_str); } - snprintf(buffer, sizeof(buffer), "/sys/class/block/%s/removable", block_name); - char *removable_str = file_read_stripped(buffer, "\n", false); + char *removable_str = sysfs_read_attr_fmt("\n", "/sys/class/block/%s/removable", block_name); if (removable_str != NULL) { storage->removable = (atoi(removable_str) == 1); free(removable_str); } - snprintf(buffer, sizeof(buffer), "/sys/class/block/%s/device/model", block_name); - storage->model = file_read_stripped(buffer, "\n", false); + storage->model = sysfs_read_attr_fmt("\n", "/sys/class/block/%s/device/model", block_name); trim_trailing_spaces(storage->model); - snprintf(buffer, sizeof(buffer), "/sys/class/block/%s/device/serial", block_name); - storage->serial = file_read_stripped(buffer, "\n", false); + storage->serial = sysfs_read_attr_fmt("\n", "/sys/class/block/%s/device/serial", block_name); trim_trailing_spaces(storage->serial); /* Some drivers put 'address', some just don't have it */ - snprintf(buffer, sizeof(buffer), "/sys/class/block/%s/device/address", block_name); - storage->pci_slot_name = file_read_stripped(buffer, "\n", false); - - /* uuid (note: uuid is usually not exposed under /sys/block directly on modern kernels, - * but often wwid or by-uuid via udev. However, following the snippet's exact structure:) - */ - snprintf(buffer, sizeof(buffer), "/sys/class/block/%s/uuid", block_name); - storage->uuid = file_read_stripped(buffer, "\n", false); + storage->pci_slot_name = sysfs_read_attr_fmt("\n", "/sys/class/block/%s/device/address", block_name); + + storage->uuid = sysfs_read_attr_fmt("\n", "/sys/class/block/%s/uuid", block_name); return storage; } From 90956afb4a6b9e7431cdcbe2c62727a4989c8241 Mon Sep 17 00:00:00 2001 From: th0truth Date: Sat, 8 Aug 2026 17:13:14 +0300 Subject: [PATCH 5/5] refactor(base): add a macro DEFINE_FREE_ARRAY --- include/base.h | 16 ++++++++++++++++ src/linux/hardware/gpu.c | 12 +----------- src/linux/hardware/network.c | 12 +----------- src/linux/hardware/storage.c | 12 +----------- 4 files changed, 19 insertions(+), 33 deletions(-) diff --git a/include/base.h b/include/base.h index 326fbd1..94856a4 100644 --- a/include/base.h +++ b/include/base.h @@ -18,6 +18,22 @@ extern "C" { #define STR_OR_UNK(s) ((s) ? (s) : "") +#define DEFINE_FREE_ARRAY(func_name, item_type, free_item_fn) \ + void func_name(item_type **items, int count) \ + { \ + if (items == NULL) { \ + return; \ + } \ + \ + for (int i = 0; i < count; i++) { \ + if (items[i] != NULL) { \ + free_item_fn(items[i]); \ + } \ + } \ + \ + free(items); \ + } \ + #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/src/linux/hardware/gpu.c b/src/linux/hardware/gpu.c index bb8170f..0ffe116 100644 --- a/src/linux/hardware/gpu.c +++ b/src/linux/hardware/gpu.c @@ -105,17 +105,7 @@ free_gpu(GPU *gpu) free(gpu); } -void -free_gpus(GPU **gpus, int count) -{ - if (gpus == NULL) { - return; - } - for (int i = 0; i < count; ++i) { - free_gpu(gpus[i]); - } - free(gpus); -} +DEFINE_FREE_ARRAY(free_gpus, GPU, free_gpu) cJSON * gpu_to_json_obj(const GPU *gpu) diff --git a/src/linux/hardware/network.c b/src/linux/hardware/network.c index 0a23193..a3ebe52 100644 --- a/src/linux/hardware/network.c +++ b/src/linux/hardware/network.c @@ -52,17 +52,7 @@ free_network(Network *net) free(net); } -void -free_networks(Network **networks, int count) -{ - if (networks == NULL) { - return; - } - for (int i = 0; i < count; ++i) { - free_network(networks[i]); - } - free(networks); -} +DEFINE_FREE_ARRAY(free_networks, Network, free_network) cJSON * network_to_json_obj(const Network *net) diff --git a/src/linux/hardware/storage.c b/src/linux/hardware/storage.c index 241d1f5..c64394e 100644 --- a/src/linux/hardware/storage.c +++ b/src/linux/hardware/storage.c @@ -89,17 +89,7 @@ free_storage(STORAGE *storage) free(storage); } -void -free_storages(STORAGE **storages, int count) -{ - if (storages == NULL) { - return; - } - for (int i = 0; i < count; ++i) { - free_storage(storages[i]); - } - free(storages); -} +DEFINE_FREE_ARRAY(free_storages, STORAGE, free_storage) cJSON * storage_to_json_obj(const STORAGE *storage)