#include #include #include #include #include "../block.h" #include "../format.h" #include "../any_log.h" typedef struct { block_text_t block; format_t format; } block_ram_t; typedef enum { RAM_STRING = 0, RAM_TOTAL, RAM_FREE, RAM_USED, RAM_FREE_PERC, RAM_USED_PERC, } block_ram_mark_t; static const format_option_t block_ram_options[] = { { { "total", RAM_TOTAL } }, { { "free", RAM_FREE } }, { { "used", RAM_USED } }, { { "free-percentage", RAM_FREE_PERC } }, { { "used-percentage", RAM_USED_PERC } }, { { NULL } }, }; static const struct timespec block_ram_interval = { .tv_sec = 1, .tv_nsec = 0, }; static void block_ram_update(block_t *block) { const char *path = "/proc/meminfo"; FILE *meminfo = fopen(path, "rb"); if (meminfo == NULL) { log_value_debug("Failed to open meminfo", "s:label", block->label, "s:path", path, "i:errno", errno); return; } uintmax_t total, unused, available, buffers, cached; int n = fscanf(meminfo, "MemTotal: %ju kB\n" "MemFree: %ju kB\n" "MemAvailable: %ju kB\n" "Buffers: %ju kB\n" "Cached: %ju kB\n", &total, &unused, &available, &buffers, &cached); fclose(meminfo); if (n != 5) { log_value_debug("Failed to read meminfo", "s:label", block->label, "i:errno", errno); return; } block_ram_t *ram = (block_ram_t *)block; char buffer[256] = { 0 }; size_t start = 0, size = sizeof(buffer); for (size_t i = 0; i < ram->format.length; i++) { size_t rest = start >= size ? 0 : size - start; if (rest == 0) break; switch (ram->format.parts[i].mark) { case RAM_STRING: start += snprintf(buffer + start, rest, "%s", ram->format.parts[i].string); break; case RAM_TOTAL: start += snprintf(buffer + start, rest, "%ld", total); break; case RAM_FREE: start += snprintf(buffer + start, rest, "%ld", available); break; case RAM_USED: start += snprintf(buffer + start, rest, "%ld", total - available); break; case RAM_FREE_PERC: start += snprintf(buffer + start, rest, "%ld", 100 * available / total); break; case RAM_USED_PERC: start += snprintf(buffer + start, rest, "%ld", 100 * (total - available) / total); break; default: unreachable(); } } free(ram->block.text); ram->block.text = strcopy(buffer); assert(ram->block.text != NULL); } static block_t *block_ram_alloc(const block_scheme_t *scheme) { block_t *block = calloc(1, sizeof(block_ram_t)); block->type = BLOCK_TEXT; block->update_interval = block_ram_interval; block->update_fn = block_ram_update; return block; } static void block_ram_clean(block_t *block) { block_ram_t *ram = (block_ram_t *)block; format_free(&ram->format); free(ram->block.text); } static bool block_ram_validate(block_t *block, const block_scheme_t *scheme) { block_ram_t *ram = (block_ram_t *)block; if (ram->block.text == NULL) { log_error("Block '%s' requires key '%s'", block->label, "text"); return false; } if (!format_init(&ram->format, ram->block.text, '%')) { log_error("Block '%s' has an invalid format", block->label); return false; } int marked = format_remark(&ram->format, block->label, block_ram_options); if (marked < 0) return false; if (marked == 0) { log_warn("Block '%s' does not use any ram option", block->label); block->update_fn = NULL; log_debug("Disabled updates for block '%s'", block->label); return true; } return true; } const block_scheme_t block_ram_scheme = { .name = "ram", .entries = NULL, .alloc_fn = block_ram_alloc, .clean_fn = block_ram_clean, .validate_fn = block_ram_validate, .resolve_fn = NULL, };