From c55754542ec8f17246415fa19baf06ef7dc6ab87 Mon Sep 17 00:00:00 2001 From: Felix Buehler Date: Tue, 20 Mar 2018 15:35:28 +0100 Subject: Implement support for memory-usage on Linux --- src/print_mem.c | 208 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 208 insertions(+) create mode 100644 src/print_mem.c (limited to 'src/print_mem.c') diff --git a/src/print_mem.c b/src/print_mem.c new file mode 100644 index 0000000..c2f48f4 --- /dev/null +++ b/src/print_mem.c @@ -0,0 +1,208 @@ +// vim:ts=4:sw=4:expandtab +#include +#include +#include +#include +#include +#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 filename[] = "/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]); +} + +/* + * Determines whether remaining bytes are below given threshold. + * + */ +static bool below_threshold(const long totalRam, const long usedRam, const char *threshold_type, const long low_threshold) { + // empty is available or free, based on "use_available_memory" + long empty = totalRam - usedRam; + if (BEGINS_WITH(threshold_type, "percentage_")) { + return 100.0 * empty / totalRam < low_threshold; + } else if (strcasecmp(threshold_type, "bytes_free") == 0) { + return empty < low_threshold; + } else if (threshold_type[0] != '\0' && strncasecmp(threshold_type + 1, "bytes_", strlen("bytes_")) == 0) { + uint64_t base = BINARY_BASE; + long factor = 1; + + switch (threshold_type[0]) { + case 'T': + case 't': + factor *= base; + case 'G': + case 'g': + factor *= base; + case 'M': + case 'm': + factor *= base; + case 'K': + case 'k': + factor *= base; + break; + default: + return false; + } + return empty < low_threshold * factor; + } + return false; +} + +void print_memory(yajl_gen json_gen, char *buffer, const char *format, const char *degraded_format_below_threshold, const char *degraded_threshold_type, const float degraded_low_threshold, const char *critical_format_below_threshold, const char *critical_threshold_type, const float critical_low_threshold, const bool use_available_memory) { + char *outwalk = buffer; + +#if defined(linux) + const char *selected_format = format; + const char *walk; + bool colorful_output = false; + + long totalRam = -1; + long freeRam = -1; + long availableRam = -1; + long usedRam = -1; + long sharedRam = -1; + long cached = -1; + long buffers = -1; + + FILE *file = fopen(filename, "r"); + if (!file) { + goto error; + } + char line[128]; + while (fgets(line, sizeof line, file)) { + if (BEGINS_WITH(line, "MemTotal:")) { + totalRam = strtol(line + strlen("MemTotal:"), NULL, 10); + } + if (BEGINS_WITH(line, "MemFree:")) { + freeRam = strtol(line + strlen("MemFree:"), NULL, 10); + } + if (BEGINS_WITH(line, "MemAvailable:")) { + availableRam = strtol(line + strlen("MemAvailable:"), NULL, 10); + } + if (BEGINS_WITH(line, "Buffers:")) { + buffers = strtol(line + strlen("Buffers:"), NULL, 10); + } + if (BEGINS_WITH(line, "Cached:")) { + cached = strtol(line + strlen("Cached:"), NULL, 10); + } + if (BEGINS_WITH(line, "Shmem:")) { + sharedRam = strtol(line + strlen("Shmem:"), NULL, 10); + } + if (totalRam != -1 && freeRam != -1 && availableRam != -1 && buffers != -1 && cached != -1 && sharedRam != -1) { + break; + } + } + fclose(file); + + if (totalRam == -1 || freeRam == -1 || availableRam == -1 || buffers == -1 || cached == -1 || sharedRam == -1) { + goto error; + } + + totalRam = totalRam * BINARY_BASE; + freeRam = freeRam * BINARY_BASE; + availableRam = availableRam * BINARY_BASE; + buffers = buffers * BINARY_BASE; + cached = cached * BINARY_BASE; + sharedRam = sharedRam * BINARY_BASE; + if (use_available_memory) { + usedRam = totalRam - availableRam; + } else { + usedRam = totalRam - freeRam - buffers - cached; + } + + if (degraded_low_threshold > 0 && below_threshold(totalRam, usedRam, degraded_threshold_type, degraded_low_threshold)) { + if (critical_low_threshold > 0 && below_threshold(totalRam, usedRam, critical_threshold_type, critical_low_threshold)) { + START_COLOR("color_bad"); + colorful_output = true; + if (critical_format_below_threshold != NULL) + selected_format = critical_format_below_threshold; + } else { + START_COLOR("color_degraded"); + colorful_output = true; + if (degraded_format_below_threshold != NULL) + selected_format = degraded_format_below_threshold; + } + } + + for (walk = selected_format; *walk != '\0'; walk++) { + if (*walk != '%') { + *(outwalk++) = *walk; + continue; + } + if (BEGINS_WITH(walk + 1, "total")) { + outwalk += print_bytes_human(outwalk, totalRam); + walk += strlen("total"); + } + + if (BEGINS_WITH(walk + 1, "used")) { + outwalk += print_bytes_human(outwalk, usedRam); + walk += strlen("used"); + } + + if (BEGINS_WITH(walk + 1, "free")) { + outwalk += print_bytes_human(outwalk, freeRam); + walk += strlen("free"); + } + + if (BEGINS_WITH(walk + 1, "available")) { + outwalk += print_bytes_human(outwalk, availableRam); + walk += strlen("available"); + } + + if (BEGINS_WITH(walk + 1, "shared")) { + outwalk += print_bytes_human(outwalk, sharedRam); + walk += strlen("shared"); + } + + if (BEGINS_WITH(walk + 1, "percentage_free")) { + outwalk += sprintf(outwalk, "%.01f%s", 100.0 * freeRam / totalRam, pct_mark); + walk += strlen("percentage_free"); + } + + if (BEGINS_WITH(walk + 1, "percentage_available")) { + outwalk += sprintf(outwalk, "%.01f%s", 100.0 * availableRam / totalRam, pct_mark); + walk += strlen("percentage_available"); + } + + if (BEGINS_WITH(walk + 1, "percentage_used")) { + outwalk += sprintf(outwalk, "%.01f%s", 100.0 * usedRam / totalRam, pct_mark); + walk += strlen("percentage_used"); + } + + if (BEGINS_WITH(walk + 1, "percentage_shared")) { + outwalk += sprintf(outwalk, "%.01f%s", 100.0 * sharedRam / totalRam, pct_mark); + walk += strlen("percentage_shared"); + } + } + + if (colorful_output) + 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 + fputs("i3status: Memory status information is not supported on this system\n", stderr); +#endif +} -- cgit v1.2.3