summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--i3status.c20
-rw-r--r--i3status.conf7
-rw-r--r--include/i3status.h1
-rw-r--r--man/i3status.man47
-rw-r--r--src/print_mem.c225
5 files changed, 300 insertions, 0 deletions
diff --git a/i3status.c b/i3status.c
index 5088c96..6461d6b 100644
--- a/i3status.c
+++ b/i3status.c
@@ -421,6 +421,19 @@ 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("format_degraded", NULL, CFGF_NONE),
+ CFG_STR("threshold_degraded", NULL, CFGF_NONE),
+ CFG_STR("threshold_critical", NULL, CFGF_NONE),
+ CFG_STR("memory_used_method", "classical", 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 +503,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 +738,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, "format_degraded"), cfg_getstr(sec, "threshold_degraded"), cfg_getstr(sec, "threshold_critical"), cfg_getstr(sec, "memory_used_method"));
+ 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/i3status.conf b/i3status.conf
index 7f37964..07ffb74 100644
--- a/i3status.conf
+++ b/i3status.conf
@@ -17,6 +17,7 @@ order += "wireless _first_"
order += "ethernet _first_"
order += "battery all"
order += "load"
+order += "memory"
order += "tztime local"
wireless _first_ {
@@ -42,6 +43,12 @@ load {
format = "%1min"
}
+memory {
+ format = "%used | %available"
+ threshold_degraded = "1G"
+ format_degraded = "MEMORY < %available"
+}
+
disk "/" {
format = "%avail"
}
diff --git a/include/i3status.h b/include/i3status.h
index 9ac471d..fe9206e 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 *format_degraded, const char *threshold_degraded, const char *threshold_critical, const char *memory_used_method);
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 4ed54ac..1d679a3 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,12 @@ cpu_temperature 0 {
path = "/sys/devices/platform/coretemp.0/temp1_input"
}
+memory {
+ format = "%used"
+ threshold_degraded = "10%"
+ format_degraded = "MEMORY: %free"
+}
+
disk "/" {
format = "%free"
}
@@ -192,6 +199,7 @@ double-quote (""") characters need to be replaced with "`&amp;`", "`&lt;`",
for generated content (e.g. wireless ESSID, time).
*Example configuration*:
+
-------------------------------------------------------------
general {
output_format = "xmobar"
@@ -428,6 +436,45 @@ 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 a Linux system from +/proc/meminfo+. Other
+systems are currently not supported.
+
+As format placeholders, +total+, +used+, +free+, +available+ and +shared+ are
+available. These will print human readable values. It's also possible to prefix
+the placeholders with +percentage_+ to get a value in percent.
+
+It's possible to define a +threshold_degraded+ and a +threshold_critical+ to
+color the status bar output in yellow or red, if the available memory falls
+below the given threshold. Possible values of the threshold can be any integer,
+suffixed with an iec symbol (+T+, +G+, +M+, +K+). Alternatively, the integer
+can be suffixed by a percent sign, which then rets evaluated relatively to
+total memory.
+
+If the +format_degraded+ parameter is given and either the critical or the
+degraded threshold applies, +format_degraded+ will get used as format string.
+It acts equivalently to +format+.
+
+As Linux' meminfo doesn't expose the overall memory in use, there are multiple
+methods to distinguish the actually used memory.
+
+*Example used_memory_method*: +memavailable+ ("total memory" - "MemAvailable", matches gnome system monitor)
+
+*Example used_memory_method*: +classical+ ("total memory" - "free" - "buffers" - "cache", matches gnome system monitor)
+
+*Example order*: +memory+
+
+*Example format*: +%free %available (%used) / %total+
+
+*Example format*: +%percentage_used used, %percentage_free free, %percentage_shared shared+
+
+*Example threshold_degraded*: +10%+
+
+*Example threshold_critical*: +5%+
+
+*Example format_degraded*: +Memory LOW: %free+
+
=== 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..46523d6
--- /dev/null
+++ b/src/print_mem.c
@@ -0,0 +1,225 @@
+// 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) {
+ long mem_absolute = -1;
+ char *endptr = NULL;
+
+ 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
+}