Adds a new output which can be used to display common memory-statistics, like top would do it.
The current implementation supports only linux and reads /proc/meminfo. Signed-of-by: Benjamin Block <[email protected]> --- i3status.c | 14 +++ include/i3status.h | 1 + man/i3status.man | 27 ++++++ src/print_mem_usage.c | 210 +++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 252 insertions(+), 0 deletions(-) create mode 100644 src/print_mem_usage.c diff --git a/i3status.c b/i3status.c index dd7d1e6..f86d0e6 100644 --- a/i3status.c +++ b/i3status.c @@ -234,6 +234,13 @@ int main(int argc, char *argv[]) { CFG_END() }; + cfg_opt_t mem_usage_opts[] = { + CFG_STR("format", "%usage", CFGF_NONE), + CFG_INT("include_buffers", 0, CFGF_NONE), + CFG_INT("include_caches", 0, CFGF_NONE), + CFG_END() + }; + cfg_opt_t temp_opts[] = { CFG_STR("format", "%degrees C", CFGF_NONE), CFG_STR("path", NULL, CFGF_NONE), @@ -268,6 +275,7 @@ int main(int argc, char *argv[]) { CFG_SEC("ddate", ddate_opts, CFGF_NONE), CFG_SEC("load", load_opts, CFGF_NONE), CFG_SEC("cpu_usage", usage_opts, CFGF_NONE), + CFG_SEC("mem_usage", mem_usage_opts, CFGF_TITLE | CFGF_MULTI), CFG_END() }; @@ -410,6 +418,12 @@ int main(int argc, char *argv[]) { CASE_SEC("cpu_usage") print_cpu_usage(cfg_getstr(sec, "format")); + + CASE_SEC_TITLE("mem_usage") + print_mem_usage(title, + cfg_getstr(sec, "format"), + cfg_getint(sec, "include_buffers"), + cfg_getint(sec, "include_caches")); } if (output_format == O_I3BAR) printf("],"); diff --git a/include/i3status.h b/include/i3status.h index e40003c..6afccdd 100644 --- a/include/i3status.h +++ b/include/i3status.h @@ -73,6 +73,7 @@ void print_wireless_info(const char *interface, const char *format_up, const cha void print_run_watch(const char *title, const char *pidfile, const char *format); void print_cpu_temperature_info(int zone, const char *path, const char *format); void print_cpu_usage(const char *format); +void print_mem_usage(const char *title, const char *format, int include_buffers, int include_caches); void print_eth_info(const char *interface, const char *format_up, const char *format_down); void print_load(); void print_volume(const char *fmt, const char *device, const char *mixer, int mixer_idx); diff --git a/man/i3status.man b/man/i3status.man index fb1bbf4..b14eaa6 100644 --- a/man/i3status.man +++ b/man/i3status.man @@ -136,6 +136,8 @@ managers like dwm, wmii and xmonad though it will work with any windowmanger xmobar:: xmobar is a minimalistic, text based, status bar. It was designed to work with the xmonad Window Manager. +i3bar:: +the native status bar of i3. none:: Does not use any color codes. Separates values by the pipe symbol. This should be used with i3bar and can be used for custom scripts. @@ -220,6 +222,31 @@ Gets the percentual CPU usage from +/proc/stat+. *Example format*: +%usage+ +=== Memory Usage + +Gets several memory-parameters from +/proc/meminfo+. + +*Example order*: +memory_usage phy_free+ + +*Example config*: +------------------------------------------------------------- +memory_usage top { + format = "Memory: %phytotal total, %phyused used, %phyfree + free; Swap: %swaptotal total, %swapused used, + %swapfree free" + include_buffers = 0 + include_caches = 0 +} +------------------------------------------------------------- + +------------------------------------------------------------- +memory_usage phy_free { + format = "M: %phyfree" + include_buffers = 0 + include_caches = 0 +} +------------------------------------------------------------- + === Load Gets the system load (number of processes waiting for CPU time in the last diff --git a/src/print_mem_usage.c b/src/print_mem_usage.c new file mode 100644 index 0000000..fed9f67 --- /dev/null +++ b/src/print_mem_usage.c @@ -0,0 +1,210 @@ +// vim:sw=8:sts=8:ts=8:expandtab +#include <stdlib.h> +#include <limits.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> + +#ifdef __FreeBSD__ +/* don't have any freebsd-systems .. */ +#endif + +#include "i3status.h" + +enum mem_usage_idx { + MEM_USAGE_MIN = 0, + MEM_USAGE_TOTAL = 0, + MEM_USAGE_FREE = 1, + MEM_USAGE_USED = 2, + MEM_USAGE_MAX = 3 +}; + +/* + * shall provide a common way to express the amount of memory used and available + * + * the values are expected to be kByte-numbers + */ +struct mem_usage { + unsigned long phy[3]; + unsigned long swap[3]; +}; + +/* os-specific implementations provided at the bottom */ +int get_mem_usage(struct mem_usage * usage, int include_buffers, + int include_caches); + +void +print_mem_usage(const char *title, const char *format, int include_buffers, + int include_caches) +{ + const char *walk; + struct mem_usage usage; + + if (output_format == O_I3BAR) + printf("{\"name\":\"mem_usage\", \"full_text\":\""); + + if(get_mem_usage(&usage, include_buffers, include_caches)) + goto err_out; + +/* matches the str against (walk + 1) */ +#define matchField(str) (strncmp(walk + 1, (str), sizeof((str)) - 1) == 0) + + for (walk = format; *walk != '\0'; walk++) { + if (*walk != '%') { + putchar(*walk); + continue; + } + + /* dispaly in MegaBytes */ + if (matchField("phytotal")) { + printf("%lumB", usage.phy[MEM_USAGE_TOTAL] >> 10); + walk += sizeof("phytotal") - 1; + } else if (matchField("phyfree")) { + printf("%lumB", usage.phy[MEM_USAGE_FREE] >> 10); + walk += sizeof("phyfree") - 1; + } else if (matchField("phyused")) { + printf("%lumB", usage.phy[MEM_USAGE_USED] >> 10); + walk += sizeof("phyused") - 1; + } else if (matchField("swaptotal")) { + printf("%lumB", usage.swap[MEM_USAGE_TOTAL] >> 10); + walk += sizeof("swaptotal") - 1; + } else if (matchField("swapfree")) { + printf("%lumB", usage.swap[MEM_USAGE_FREE] >> 10); + walk += sizeof("swapfree") - 1; + } else if (matchField("swapused")) { + printf("%lumB", usage.swap[MEM_USAGE_USED] >> 10); + walk += sizeof("swapused") - 1; + } + } + + if (output_format == O_I3BAR) + printf("\"}"); + +#undef matchField + + return; +err_out: + (void) fputs("Cannot read mem-usage\n", stderr); +} + +#if defined(LINUX) +int +to_ulong(char *c, unsigned long *ul) +{ + *ul = strtoul(c, NULL, 10); + if (errno == ERANGE || errno == EINVAL) + return 1; + return 0; +} + +int +get_mem_usage(struct mem_usage * usage, int include_buffers, + int include_caches) +{ + /* + * Documentation: $LINUX_HOME/Documentation/filesystems/proc.txt + * Implementation: $LINUX_HOME/fs/proc/meminfo.c + */ + char * file = "/proc/meminfo"; + /* + * we actually need 6 lines: + * - MemTotal + * - MemFree + * - Buffers + * - Cached + * - SwapTotal + * - SwapFree + * + * SwapFree is the last of the necessary ones. So, if you + * look into the implementation, there are currently around + * 20 lines, each with 28 Bytes. Therefore we need to read + * at least 560 Bytes. + */ + char buffer[1024]; /* should be plenty */ + int read = 0; /* how many lines were found */ + + char *fn, *fv, *fl; /* the 3 line-tokens */ + unsigned long tmp, caches, buffers; + + if (!slurp(file, buffer, sizeof(buffer))) + goto err_out; + + memset(usage, 0, sizeof(*usage)); + + /* working with strings is so much fun in C */ + +/* matches the str against fn */ +#define matchField(str) (strncmp(fn, (str), sizeof((str)) - 1) == 0) +/* try to get a value out of fv */ +#define getValue(val) (to_ulong(fv, &(val)) == 0) + + /* tokenize one line */ + fn = strtok(buffer, " "); /* pointer to the field-name (and ':') */ + fv = strtok(NULL, " "); /* pointer to the field-value */ + fl = strtok(NULL, "\n"); /* should contain 'kB' */ + while(fn && fv && fl) { + /* + * try to find a important line and if one is found + * try to convert the field-value-string into a usable + */ + if (matchField("MemTotal") && getValue(tmp)) { + usage->phy[MEM_USAGE_TOTAL] = tmp; + read++; + } else if (matchField("MemFree") && getValue(tmp)) { + usage->phy[MEM_USAGE_FREE] = tmp; + read++; + } else if (matchField("Buffers") && getValue(tmp)) { + buffers = tmp; + read++; + } else if (matchField("Cached") && getValue(tmp)) { + caches = tmp; + read++; + } else if (matchField("SwapTotal") && getValue(tmp)) { + usage->swap[MEM_USAGE_TOTAL] = tmp; + read++; + } else if (matchField("SwapFree") && getValue(tmp)) { + usage->swap[MEM_USAGE_FREE] = tmp; + read++; + } + + if(read >= 6) + break; + + /* try to tokenize an other line */ + fn = strtok(NULL, " "); + fv = strtok(NULL, " "); + fl = strtok(NULL, "\n"); + } + +#undef getValue +#undef matchField + + if(read < 6) + goto err_out; + + usage->phy[MEM_USAGE_USED] = usage->phy[MEM_USAGE_TOTAL] - + usage->phy[MEM_USAGE_FREE]; + usage->swap[MEM_USAGE_USED] = usage->swap[MEM_USAGE_TOTAL] - + usage->swap[MEM_USAGE_FREE]; + + if (include_buffers == 0) { + usage->phy[MEM_USAGE_FREE] += buffers; + usage->phy[MEM_USAGE_USED] -= buffers; + } + + if (include_caches == 0) { + usage->phy[MEM_USAGE_FREE] += caches; + usage->phy[MEM_USAGE_USED] -= caches; + } + + return 0; +err_out: + return 1; +} +#else +int +get_mem_usage(struct mem_usage * usage) +{ + return 1; +} +#endif -- 1.7.3.4
