#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; #define RAM_SHIFT 12 static const format_pair_t block_ram_units[] = { { "-si", UNIT_SI << RAM_SHIFT }, { "-b", UNIT_B << RAM_SHIFT }, { "-kib", UNIT_KB << RAM_SHIFT }, { "-mib", UNIT_MB << RAM_SHIFT }, { "-gib", UNIT_GB << RAM_SHIFT }, { "-tib", UNIT_TB << RAM_SHIFT }, { "-kb", (UNIT_KB | UNIT_SI) << RAM_SHIFT }, { "-mb", (UNIT_MB | UNIT_SI) << RAM_SHIFT }, { "-gb", (UNIT_GB | UNIT_SI) << RAM_SHIFT }, { "-tb", (UNIT_TB | UNIT_SI) << RAM_SHIFT }, { NULL }, }; static const format_option_t block_ram_options[] = { { { "total", RAM_TOTAL }, .suffixes = block_ram_units }, { { "free", RAM_FREE }, .suffixes = block_ram_units }, { { "used", RAM_USED }, .suffixes = block_ram_units }, { { "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; unit_t mask = UNIT_MASK << RAM_SHIFT; unit_t unit = (ram->format.parts[i].mark & mask) >> RAM_SHIFT; switch (ram->format.parts[i].mark & ~mask) { case RAM_STRING: start += snprintf(buffer + start, rest, "%s", ram->format.parts[i].string); break; case RAM_TOTAL: start += snprintf_units(buffer + start, rest, total * 1024, unit); break; case RAM_FREE: start += snprintf_units(buffer + start, rest, available * 1024, unit); break; case RAM_USED: start += snprintf_units(buffer + start, rest, (total - available) * 1024, unit); 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 void block_ram_init(block_t *block) { extern const block_scheme_t block_text_scheme; block_text_scheme.init_fn(block); block->update_interval = block_ram_interval; block->update_fn = block_ram_update; } 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 int block_ram_validate(block_t *block) { block_ram_t *ram = (block_ram_t *)block; int errors = 0; if (ram->block.text == NULL) { log_error("Block '%s' requires key '%s'", block->label, "text"); errors++; } if (!format_init(&ram->format, ram->block.text, '%')) { log_error("Block '%s' has an invalid format", block->label); errors++; } else { int marked = format_remark(&ram->format, block->label, block_ram_options); if (marked < 0) errors += -marked; 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 errors; } const block_scheme_t block_ram_scheme = { .name = "ram", .entries = NULL, .size = sizeof(block_ram_t), .init_fn = block_ram_init, .clean_fn = block_ram_clean, .validate_fn = block_ram_validate, };