diff options
Diffstat (limited to 'src/print_mem.c')
-rw-r--r-- | src/print_mem.c | 224 |
1 files changed, 224 insertions, 0 deletions
diff --git a/src/print_mem.c b/src/print_mem.c new file mode 100644 index 0000000..a37fa29 --- /dev/null +++ b/src/print_mem.c @@ -0,0 +1,224 @@ +// vim:ts=4:sw=4:expandtab +#include <ctype.h> +#include <stdio.h> +#include <stdlib.h> +#include <yajl/yajl_gen.h> +#include <yajl/yajl_version.h> +#include "i3status.h" + +#define BINARY_BASE UINT64_C(1024) + +#define MAX_EXPONENT 4 +static const char *const iec_symbols[MAX_EXPONENT + 1] = {"", "Ki", "Mi", "Gi", "Ti"}; + +static const char memoryfile_linux[] = "/proc/meminfo"; + +/* + * Prints the given amount of bytes in a human readable manner. + * + */ +static int print_bytes_human(char *outwalk, uint64_t bytes) { + double size = bytes; + int exponent = 0; + int bin_base = BINARY_BASE; + while (size >= bin_base && exponent < MAX_EXPONENT) { + size /= bin_base; + exponent += 1; + } + return sprintf(outwalk, "%.1f %sB", size, iec_symbols[exponent]); +} + +/* + * Convert a string to its absolute representation based on the total + * memory of `mem_total`. + * + * The string can contain any percentage values, which then return a + * the value of `size` in relation to `mem_total`. + * Alternatively an absolute value can be given, suffixed with an iec + * symbol. + * + */ +static long memory_absolute(const long mem_total, const char *size) { + char *endptr = NULL; + + long mem_absolute = strtol(size, &endptr, 10); + + if (endptr) { + while (endptr[0] != '\0' && isspace(endptr[0])) + endptr++; + + switch (endptr[0]) { + case 'T': + case 't': + mem_absolute *= BINARY_BASE; + case 'G': + case 'g': + mem_absolute *= BINARY_BASE; + case 'M': + case 'm': + mem_absolute *= BINARY_BASE; + case 'K': + case 'k': + mem_absolute *= BINARY_BASE; + break; + case '%': + mem_absolute = mem_total * mem_absolute / 100; + break; + default: + break; + } + } + + return mem_absolute; +} + +void print_memory(yajl_gen json_gen, char *buffer, const char *format, const char *format_degraded, const char *threshold_degraded, const char *threshold_critical, const char *memory_used_method) { + char *outwalk = buffer; + +#if defined(linux) + const char *selected_format = format; + const char *walk; + const char *output_color = NULL; + + long ram_total = -1; + long ram_free = -1; + long ram_available = -1; + long ram_used = -1; + long ram_shared = -1; + long ram_cached = -1; + long ram_buffers = -1; + + FILE *file = fopen(memoryfile_linux, "r"); + if (!file) { + goto error; + } + char line[128]; + while (fgets(line, sizeof line, file)) { + if (BEGINS_WITH(line, "MemTotal:")) { + ram_total = strtol(line + strlen("MemTotal:"), NULL, 10); + } + if (BEGINS_WITH(line, "MemFree:")) { + ram_free = strtol(line + strlen("MemFree:"), NULL, 10); + } + if (BEGINS_WITH(line, "MemAvailable:")) { + ram_available = strtol(line + strlen("MemAvailable:"), NULL, 10); + } + if (BEGINS_WITH(line, "Buffers:")) { + ram_buffers = strtol(line + strlen("Buffers:"), NULL, 10); + } + if (BEGINS_WITH(line, "Cached:")) { + ram_cached = strtol(line + strlen("Cached:"), NULL, 10); + } + if (BEGINS_WITH(line, "Shmem:")) { + ram_shared = strtol(line + strlen("Shmem:"), NULL, 10); + } + if (ram_total != -1 && ram_free != -1 && ram_available != -1 && ram_buffers != -1 && ram_cached != -1 && ram_shared != -1) { + break; + } + } + fclose(file); + + if (ram_total == -1 || ram_free == -1 || ram_available == -1 || ram_buffers == -1 || ram_cached == -1 || ram_shared == -1) { + goto error; + } + + ram_total = ram_total * BINARY_BASE; + ram_free = ram_free * BINARY_BASE; + ram_available = ram_available * BINARY_BASE; + ram_buffers = ram_buffers * BINARY_BASE; + ram_cached = ram_cached * BINARY_BASE; + ram_shared = ram_shared * BINARY_BASE; + + if (BEGINS_WITH(memory_used_method, "memavailable")) { + ram_used = ram_total - ram_available; + } else if (BEGINS_WITH(memory_used_method, "classical")) { + ram_used = ram_total - ram_free - ram_buffers - ram_cached; + } + + if (threshold_degraded) { + long abs = memory_absolute(ram_total, threshold_degraded); + if (ram_available < abs) { + output_color = "color_degraded"; + } + } + + if (threshold_critical) { + long abs = memory_absolute(ram_total, threshold_critical); + if (ram_available < abs) { + output_color = "color_bad"; + } + } + + if (output_color) { + START_COLOR(output_color); + + if (format_degraded) + selected_format = format_degraded; + } + + for (walk = selected_format; *walk != '\0'; walk++) { + if (*walk != '%') { + *(outwalk++) = *walk; + continue; + } + if (BEGINS_WITH(walk + 1, "total")) { + outwalk += print_bytes_human(outwalk, ram_total); + walk += strlen("total"); + } + + if (BEGINS_WITH(walk + 1, "used")) { + outwalk += print_bytes_human(outwalk, ram_used); + walk += strlen("used"); + } + + if (BEGINS_WITH(walk + 1, "free")) { + outwalk += print_bytes_human(outwalk, ram_free); + walk += strlen("free"); + } + + if (BEGINS_WITH(walk + 1, "available")) { + outwalk += print_bytes_human(outwalk, ram_available); + walk += strlen("available"); + } + + if (BEGINS_WITH(walk + 1, "shared")) { + outwalk += print_bytes_human(outwalk, ram_shared); + walk += strlen("shared"); + } + + if (BEGINS_WITH(walk + 1, "percentage_free")) { + outwalk += sprintf(outwalk, "%.01f%s", 100.0 * ram_free / ram_total, pct_mark); + walk += strlen("percentage_free"); + } + + if (BEGINS_WITH(walk + 1, "percentage_available")) { + outwalk += sprintf(outwalk, "%.01f%s", 100.0 * ram_available / ram_total, pct_mark); + walk += strlen("percentage_available"); + } + + if (BEGINS_WITH(walk + 1, "percentage_used")) { + outwalk += sprintf(outwalk, "%.01f%s", 100.0 * ram_used / ram_total, pct_mark); + walk += strlen("percentage_used"); + } + + if (BEGINS_WITH(walk + 1, "percentage_shared")) { + outwalk += sprintf(outwalk, "%.01f%s", 100.0 * ram_shared / ram_total, pct_mark); + walk += strlen("percentage_shared"); + } + } + + if (output_color) + END_COLOR; + + *outwalk = '\0'; + OUTPUT_FULL_TEXT(buffer); + + return; +error: + OUTPUT_FULL_TEXT("can't read memory"); + fputs("i3status: Cannot read system memory using /proc/meminfo\n", stderr); +#else + OUTPUT_FULL_TEXT(""); + fputs("i3status: Memory status information is not supported on this system\n", stderr); +#endif +} |