diff --git a/README.md b/README.md index eb65e10f..3c1b0878 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,11 @@ On debian-based systems, the following line will install all requirements: apt-get install autoconf libconfuse-dev libyajl-dev libasound2-dev libiw-dev asciidoc libpulse-dev libnl-genl-3-dev meson ``` +On OpenBSD, the following line will install the requirements: +```bash +pkg_add autoconf libconfuse libyajl asciidoc pluseaudio meson +``` + ## Upstream i3status is developed at https://github.com/i3/i3status diff --git a/man/i3status.man b/man/i3status.man index 36e1a6e7..df04c87e 100644 --- a/man/i3status.man +++ b/man/i3status.man @@ -467,8 +467,8 @@ starting from %cpu0. This feature is currently not supported in FreeBSD. === Memory -Gets the memory usage from system on a Linux system from +/proc/meminfo+. Other -systems are currently not supported. +Gets the memory usage from system on Linux from +/proc/meminfo+, on OpenBSD +from sysctl. 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 @@ -495,7 +495,8 @@ decimals can be configured via the +decimals+ option. If no such option is given the converted format placeholder will have one decimal. As Linux' meminfo doesn't expose the overall memory in use, there are multiple -methods to distinguish the actually used memory. +methods to distinguish the actually used memory. The methods are availabe on +OpenBSD as well. *Example memory_used_method*: +memavailable+ ("total memory" - "MemAvailable", matches +free+ command) diff --git a/src/print_cpu_usage.c b/src/print_cpu_usage.c index a7ee2e46..968f044f 100644 --- a/src/print_cpu_usage.c +++ b/src/print_cpu_usage.c @@ -45,14 +45,12 @@ struct cpu_usage { long system; long idle; long total; + int spin; #else int user; int nice; int system; int idle; -#if defined(__OpenBSD__) - int spin; -#endif int total; #endif }; diff --git a/src/print_mem.c b/src/print_mem.c index 76a70b90..ddf5f67c 100644 --- a/src/print_mem.c +++ b/src/print_mem.c @@ -5,6 +5,10 @@ #include #include #include +#include +#include +#include +#include #include "i3status.h" #define MAX_DECIMALS 4 @@ -12,12 +16,20 @@ #define BINARY_BASE 1024UL -#if defined(__linux__) static const char *const iec_symbols[] = {"B", "KiB", "MiB", "GiB", "TiB"}; #define MAX_EXPONENT ((sizeof iec_symbols / sizeof *iec_symbols) - 1) -#endif -#if defined(__linux__) +struct print_mem_info { + unsigned long ram_total; + unsigned long ram_free; + unsigned long ram_used; + unsigned long ram_available; + unsigned long ram_buffers; + unsigned long ram_cached; + unsigned long ram_shared; + char *output_color; +}; + /* * Prints the given amount of bytes in a human readable manner. * @@ -40,9 +52,11 @@ static int print_bytes_human(char *outwalk, unsigned long bytes, const char *uni static int print_percentage(char *outwalk, float percent) { return snprintf(outwalk, STRING_SIZE, "%.1f%s", percent, pct_mark); } -#endif -#if defined(__linux__) +static unsigned long page2byte (unsigned pageCnt) { + return pageCnt * getpagesize (); +} + /* * Convert a string to its absolute representation based on the total * memory of `mem_total`. @@ -84,22 +98,39 @@ static unsigned long memory_absolute(const char *mem_amount, const unsigned long return amount; } -#endif -void print_memory(memory_ctx_t *ctx) { - char *outwalk = ctx->buf; +static void handle_used_method(struct print_mem_info *minfo, memory_ctx_t *ctx) { + if (BEGINS_WITH(ctx->memory_used_method, "memavailable")) { + minfo->ram_used = minfo->ram_total - minfo->ram_available; + } else if (BEGINS_WITH(ctx->memory_used_method, "classical")) { + minfo->ram_used = minfo->ram_total - minfo->ram_free - minfo->ram_buffers - minfo->ram_cached; + } else { + die("Unexpected value: memory_used_method = %s", ctx->memory_used_method); + } +} -#if defined(__linux__) - const char *selected_format = ctx->format; - const char *output_color = NULL; +static void handle_threshold(struct print_mem_info *minfo, memory_ctx_t *ctx) { + if (ctx->threshold_degraded) { + const unsigned long threshold = memory_absolute(ctx->threshold_degraded, minfo->ram_total); + if (minfo->ram_available < threshold) { + minfo->output_color = "color_degraded"; + } + } + if (ctx->threshold_critical) { + const unsigned long threshold = memory_absolute(ctx->threshold_critical, minfo->ram_total); + if (minfo->ram_available < threshold) { + minfo->output_color = "color_bad"; + } + } +} + +static void get_memory_info(struct print_mem_info *minfo, memory_ctx_t *ctx) { + char *outwalk = ctx->buf; +#if defined(__linux__) int unread_fields = 6; - unsigned long ram_total; - unsigned long ram_free; - unsigned long ram_available; - unsigned long ram_buffers; - unsigned long ram_cached; - unsigned long ram_shared; + + minfo->output_color = NULL FILE *file = fopen("/proc/meminfo", "r"); if (!file) { @@ -107,17 +138,17 @@ void print_memory(memory_ctx_t *ctx) { } for (char line[128]; fgets(line, sizeof line, file);) { if (BEGINS_WITH(line, "MemTotal:")) { - ram_total = strtoul(line + strlen("MemTotal:"), NULL, 10); + minfo->ram_total = strtoul(line + strlen("MemTotal:"), NULL, 10); } else if (BEGINS_WITH(line, "MemFree:")) { - ram_free = strtoul(line + strlen("MemFree:"), NULL, 10); + minfo->ram_free = strtoul(line + strlen("MemFree:"), NULL, 10); } else if (BEGINS_WITH(line, "MemAvailable:")) { - ram_available = strtoul(line + strlen("MemAvailable:"), NULL, 10); + minfo->ram_available = strtoul(line + strlen("MemAvailable:"), NULL, 10); } else if (BEGINS_WITH(line, "Buffers:")) { - ram_buffers = strtoul(line + strlen("Buffers:"), NULL, 10); + minfo->ram_buffers = strtoul(line + strlen("Buffers:"), NULL, 10); } else if (BEGINS_WITH(line, "Cached:")) { - ram_cached = strtoul(line + strlen("Cached:"), NULL, 10); + minfo->ram_cached = strtoul(line + strlen("Cached:"), NULL, 10); } else if (BEGINS_WITH(line, "Shmem:")) { - ram_shared = strtoul(line + strlen("Shmem:"), NULL, 10); + minfo->ram_shared = strtoul(line + strlen("Shmem:"), NULL, 10); } else { continue; } @@ -132,43 +163,69 @@ void print_memory(memory_ctx_t *ctx) { } // Values are in kB, convert them to B. - ram_total *= 1024UL; - ram_free *= 1024UL; - ram_available *= 1024UL; - ram_buffers *= 1024UL; - ram_cached *= 1024UL; - ram_shared *= 1024UL; + minfo->ram_total *= 1024UL; + minfo->ram_free *= 1024UL; + minfo->ram_available *= 1024UL; + minfo->ram_buffers *= 1024UL; + minfo->ram_cached *= 1024UL; + minfo->ram_shared *= 1024UL; - unsigned long ram_used; - if (BEGINS_WITH(ctx->memory_used_method, "memavailable")) { - ram_used = ram_total - ram_available; - } else if (BEGINS_WITH(ctx->memory_used_method, "classical")) { - ram_used = ram_total - ram_free - ram_buffers - ram_cached; - } else { - die("Unexpected value: memory_used_method = %s", ctx->memory_used_method); - } + handle_used_method(minfo, ctx); + handle_threshold(minfo, ctx); +error: + OUTPUT_FULL_TEXT("can't read memory"); + fputs("i3status: Cannot read system memory using /proc/meminfo\n", stderr); - if (ctx->threshold_degraded) { - const unsigned long threshold = memory_absolute(ctx->threshold_degraded, ram_total); - if (ram_available < threshold) { - output_color = "color_degraded"; - } - } +#elif defined(__OpenBSD__) + int uvm_mib [] = { CTL_VM, VM_UVMEXP }; + int bcstats_mib[] = {CTL_VFS, VFS_GENERIC, VFS_BCACHESTAT}; + int malloc_mib[] = {CTL_KERN, KERN_MALLOCSTATS, KERN_MALLOC_KMEMSTATS, M_SHM}; + struct uvmexp our_uvmexp; + struct bcachestats bcstats; + struct kmemstats kmemstats; + size_t size = sizeof (our_uvmexp); - if (ctx->threshold_critical) { - const unsigned long threshold = memory_absolute(ctx->threshold_critical, ram_total); - if (ram_available < threshold) { - output_color = "color_bad"; - } + if (sysctl(uvm_mib, 2, &our_uvmexp, &size, NULL, 0) < 0) { + OUTPUT_FULL_TEXT(""); + fputs("i3status: cannot get memory info (uvm)!\n", stderr); + return; } - if (output_color) { - START_COLOR(output_color); + size = sizeof(bcstats); + if (sysctl(bcstats_mib, 3, &bcstats, &size, NULL, 0) == -1) { + OUTPUT_FULL_TEXT(""); + fputs("i3status: cannot get memory info (bcachestats)!\n", stderr); + return; + } - if (ctx->format_degraded) - selected_format = ctx->format_degraded; - } + size = sizeof(kmemstats); + if (sysctl(malloc_mib, 4, &kmemstats, &size, NULL, 0) == -1) { + OUTPUT_FULL_TEXT(""); + fputs("i3status: cannot get memory info (malloc)!\n", stderr); + return; + } + + minfo->ram_total = page2byte(our_uvmexp.npages); + minfo->ram_free = page2byte(our_uvmexp.free + our_uvmexp.inactive); + minfo->ram_used = page2byte(our_uvmexp.npages - our_uvmexp.free - our_uvmexp.inactive); + minfo->ram_available = minfo->ram_free; + minfo->ram_buffers = page2byte(bcstats.numbufpages); + minfo->ram_cached = 0; + minfo->ram_shared = kmemstats.ks_memuse; + minfo->output_color = NULL; + + handle_used_method(minfo, ctx); + handle_threshold(minfo, ctx); +#else + // For FreeBSD implementation see: https://bal0n.es/git/slstatus/file/components/ram.c.html + OUTPUT_FULL_TEXT(""); + fputs("i3status: Memory status information is not supported on this system\n", stderr); +#endif +} +static void print_formatted_memory(struct print_mem_info *minfo, memory_ctx_t *ctx) { + char *outwalk = ctx->buf; + const char *selected_format = ctx->format; char string_ram_total[STRING_SIZE]; char string_ram_used[STRING_SIZE]; char string_ram_free[STRING_SIZE]; @@ -179,15 +236,22 @@ void print_memory(memory_ctx_t *ctx) { char string_percentage_used[STRING_SIZE]; char string_percentage_shared[STRING_SIZE]; - print_bytes_human(string_ram_total, ram_total, ctx->unit, ctx->decimals); - print_bytes_human(string_ram_used, ram_used, ctx->unit, ctx->decimals); - print_bytes_human(string_ram_free, ram_free, ctx->unit, ctx->decimals); - print_bytes_human(string_ram_available, ram_available, ctx->unit, ctx->decimals); - print_bytes_human(string_ram_shared, ram_shared, ctx->unit, ctx->decimals); - print_percentage(string_percentage_free, 100.0 * ram_free / ram_total); - print_percentage(string_percentage_available, 100.0 * ram_available / ram_total); - print_percentage(string_percentage_used, 100.0 * ram_used / ram_total); - print_percentage(string_percentage_shared, 100.0 * ram_shared / ram_total); + if (minfo->output_color) { + START_COLOR(minfo->output_color); + + if (ctx->format_degraded) + selected_format = ctx->format_degraded; + } + + print_bytes_human(string_ram_total, minfo->ram_total, ctx->unit, ctx->decimals); + print_bytes_human(string_ram_used, minfo->ram_used, ctx->unit, ctx->decimals); + print_bytes_human(string_ram_free, minfo->ram_free, ctx->unit, ctx->decimals); + print_bytes_human(string_ram_available, minfo->ram_available, ctx->unit, ctx->decimals); + print_bytes_human(string_ram_shared, minfo->ram_shared, ctx->unit, ctx->decimals); + print_percentage(string_percentage_free, 100.0 * minfo->ram_free / minfo->ram_total); + print_percentage(string_percentage_available, 100.0 * minfo->ram_available / minfo->ram_total); + print_percentage(string_percentage_used, 100.0 * minfo->ram_used / minfo->ram_total); + print_percentage(string_percentage_shared, 100.0 * minfo->ram_shared / minfo->ram_total); placeholder_t placeholders[] = { {.name = "%total", .value = string_ram_total}, @@ -205,17 +269,15 @@ void print_memory(memory_ctx_t *ctx) { OUTPUT_FORMATTED; free(formatted); - if (output_color) + if (minfo->output_color) END_COLOR; OUTPUT_FULL_TEXT(ctx->buf); +} - 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 +void print_memory(memory_ctx_t *ctx) { + struct print_mem_info minfo; + + get_memory_info(&minfo, ctx); + print_formatted_memory(&minfo, ctx); }