diff options
| -rw-r--r-- | i3status.c | 23 | ||||
| -rw-r--r-- | include/i3status.h | 1 | ||||
| -rw-r--r-- | man/i3status.man | 48 | ||||
| -rw-r--r-- | src/print_mem.c | 208 | 
4 files changed, 280 insertions, 0 deletions
| @@ -421,6 +421,22 @@ int main(int argc, char *argv[]) {          CFG_CUSTOM_SEP_BLOCK_WIDTH_OPT,          CFG_END()}; +    cfg_opt_t memory_opts[] = { +        CFG_STR("format", "%used %free %available", CFGF_NONE), +        CFG_STR("degraded_format_below_threshold", NULL, CFGF_NONE), +        CFG_STR("degraded_threshold_type", "percentage_avail", CFGF_NONE), +        CFG_FLOAT("degraded_low_threshold", 0, CFGF_NONE), +        CFG_STR("critical_format_below_threshold", NULL, CFGF_NONE), +        CFG_STR("critical_threshold_type", "percentage_avail", CFGF_NONE), +        CFG_FLOAT("critical_low_threshold", 0, CFGF_NONE), +        CFG_BOOL("use_available_memory", true, CFGF_NONE), +        CFG_CUSTOM_ALIGN_OPT, +        CFG_CUSTOM_COLOR_OPTS, +        CFG_CUSTOM_MIN_WIDTH_OPT, +        CFG_CUSTOM_SEPARATOR_OPT, +        CFG_CUSTOM_SEP_BLOCK_WIDTH_OPT, +        CFG_END()}; +      cfg_opt_t usage_opts[] = {          CFG_STR("format", "%usage", CFGF_NONE),          CFG_STR("format_above_threshold", NULL, CFGF_NONE), @@ -490,6 +506,7 @@ int main(int argc, char *argv[]) {          CFG_SEC("tztime", tztime_opts, CFGF_TITLE | CFGF_MULTI),          CFG_SEC("ddate", ddate_opts, CFGF_NONE),          CFG_SEC("load", load_opts, CFGF_NONE), +        CFG_SEC("memory", memory_opts, CFGF_NONE),          CFG_SEC("cpu_usage", usage_opts, CFGF_NONE),          CFG_END()}; @@ -724,6 +741,12 @@ int main(int argc, char *argv[]) {                  SEC_CLOSE_MAP;              } +            CASE_SEC("memory") { +                SEC_OPEN_MAP("memory"); +                print_memory(json_gen, buffer, cfg_getstr(sec, "format"), cfg_getstr(sec, "degraded_format_below_threshold"), cfg_getstr(sec, "degraded_threshold_type"), cfg_getfloat(sec, "degraded_low_threshold"), cfg_getstr(sec, "critical_format_below_threshold"), cfg_getstr(sec, "critical_threshold_type"), cfg_getfloat(sec, "critical_low_threshold"), cfg_getbool(sec, "use_available_memory")); +                SEC_CLOSE_MAP; +            } +              CASE_SEC("time") {                  SEC_OPEN_MAP("time");                  print_time(json_gen, buffer, NULL, cfg_getstr(sec, "format"), NULL, NULL, NULL, tv.tv_sec); diff --git a/include/i3status.h b/include/i3status.h index 9ac471d..2aa4317 100644 --- a/include/i3status.h +++ b/include/i3status.h @@ -223,6 +223,7 @@ void print_cpu_temperature_info(yajl_gen json_gen, char *buffer, int zone, const  void print_cpu_usage(yajl_gen json_gen, char *buffer, const char *format, const char *format_above_threshold, const char *format_above_degraded_threshold, const char *path, const float max_threshold, const float degraded_threshold);  void print_eth_info(yajl_gen json_gen, char *buffer, const char *interface, const char *format_up, const char *format_down);  void print_load(yajl_gen json_gen, char *buffer, const char *format, const char *format_above_threshold, const float max_threshold); +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);  void print_volume(yajl_gen json_gen, char *buffer, const char *fmt, const char *fmt_muted, const char *device, const char *mixer, int mixer_idx);  bool process_runs(const char *path);  int volume_pulseaudio(uint32_t sink_idx, const char *sink_name); diff --git a/man/i3status.man b/man/i3status.man index 7d81203..ec02ce1 100644 --- a/man/i3status.man +++ b/man/i3status.man @@ -55,6 +55,7 @@ order += "wireless wlan0"  order += "ethernet eth0"  order += "battery 0"  order += "cpu_temperature 0" +order += "memory"  order += "load"  order += "tztime local"  order += "tztime berlin" @@ -113,6 +114,10 @@ cpu_temperature 0 {  	path = "/sys/devices/platform/coretemp.0/temp1_input"  } +memory { +        format = "%used" +} +  disk "/" {  	format = "%free"  } @@ -424,6 +429,49 @@ starting from %cpu0. This feature is currently not supported in FreeBSD.  *Example format_above_degraded_threshold*: +Warning above degraded threshold: %usage+ +=== Memory + +Gets the memory usage from system. +On Linux, the information is taken from +/proc/meminfo+. + +These values can also be expressed in percentages with the +percentage_used+, ++percentage_free+ and +percentage_shared+ formats. + +If a +critical_low_threshold+ is defined, the output will be colored using ++color_bad+. The unit of this threshold is defined by the unit given in ++critical_threshold_type+, which can be one of "bytes_free", "bytes_avail", +"percentage_free" and "percentage_avail". Additionally, the former two can be +prefixed with one of "k", "m", "g" or "t" to change the exact unit. +For example, setting critical_low_threshold to 2 and threshold_type to "gbytes_avail" +causes available memory below 2 GiB to be colored with +color_bad+. The +defaults are "percentage_avail" with a threshold of 0. +Furthermore, the format used when the threshold is reached can be customized +using the option +critical_format_below_threshold+. +Same applies to +degraded_low_threshold+, +degraded_threshold_type+ and ++degraded_format_below_threshold+ using +color_degraded+. + +The most common one is: +"used memory" = "total memory" - "free memory" - "buffers" - "cache" +This is the default in i3status. Some other programs use +"used memory" = "total memory" - "available memory" +You can disable this behavior using +use_available_memory+. + +*Example order*: +memory+ + +*Example format*: +%free %available (%used)/ %total+ + +*Example format*: +used %percentage_used , free %percentage_free, shared %percentage_shared+ + +*Example degraded_low_threshold*: +10+ + +*Example degraded_threshold_type*: +percentage_free+ + +*Example critical_low_threshold*: +5+ + +*Example critical_format_below_threshold*: +Warning: %percentage_free+ + +*Example use_available_memory: +false+ +  === Load  Gets the system load (number of processes waiting for CPU time in the last 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 <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 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 +} | 
