From f64fd298b6b366b78b078b8112faae8437122b94 Mon Sep 17 00:00:00 2001 From: Kristaps Dz Date: Tue, 4 Nov 2025 22:43:29 -0800 Subject: [PATCH] Add memory usage for OpenBSD Draw from sysctl(2) to populate memory fields for OpenBSD. Unlike on Linux, there's no shared or buffer memory, so the available memory is simply free and cached. This makes the memory calculation devices return the same amount. Update the manpage to reflect this new information, and also remove a line specific to Linux (regarding returned amounts being in KiB) that isn't relevant. --- man/i3status.man | 13 +++++------ src/print_mem.c | 60 ++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 61 insertions(+), 12 deletions(-) diff --git a/man/i3status.man b/man/i3status.man index 06a120c0..d3675c96 100644 --- a/man/i3status.man +++ b/man/i3status.man @@ -469,8 +469,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 +/proc/meminfo+ on a Linux system and +sysctl+ on +OpenBSD. 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 @@ -487,17 +487,16 @@ 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+. -It's also possible to define the unit for the various format placeholders. As -+/proc/meminfo+ returns the memory in kB they will be converted to the given -unit. If no unit is given or the +auto+ option is used, the conversion will -select the maximum possible unit. +It's also possible to define the unit for the various format placeholders. +If no unit is given or the +auto+ option is used, the conversion will select the +maximum possible unit. As the converted format placeholder will be a decimal number, the number of 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. On OpenBSD, these are the same. *Example memory_used_method*: +memavailable+ ("total memory" - "MemAvailable", matches +free+ command) diff --git a/src/print_mem.c b/src/print_mem.c index 76a70b90..c480b783 100644 --- a/src/print_mem.c +++ b/src/print_mem.c @@ -5,6 +5,11 @@ #include #include #include +#ifdef __OpenBSD__ +# include +# include +# include +#endif #include "i3status.h" #define MAX_DECIMALS 4 @@ -12,12 +17,12 @@ #define BINARY_BASE 1024UL -#if defined(__linux__) +#if defined(__linux__) || defined(__OpenBSD__) 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__) +#if defined(__linux__) || defined(__OpenBSD__) /* * Prints the given amount of bytes in a human readable manner. * @@ -42,7 +47,7 @@ static int print_percentage(char *outwalk, float percent) { } #endif -#if defined(__linux__) +#if defined(__linux__) || defined(__OpenBSD__) /* * Convert a string to its absolute representation based on the total * memory of `mem_total`. @@ -89,11 +94,10 @@ static unsigned long memory_absolute(const char *mem_amount, const unsigned long void print_memory(memory_ctx_t *ctx) { char *outwalk = ctx->buf; -#if defined(__linux__) +#if defined(__linux__) || defined(__OpenBSD__) const char *selected_format = ctx->format; const char *output_color = NULL; - int unread_fields = 6; unsigned long ram_total; unsigned long ram_free; unsigned long ram_available; @@ -101,6 +105,51 @@ void print_memory(memory_ctx_t *ctx) { unsigned long ram_cached; unsigned long ram_shared; +#if defined(__OpenBSD__) + int64_t tmp; + size_t sz; + + /* Total memory set to the physical memory less kernel. */ + + int usermem_mib[] = {CTL_HW, HW_USERMEM64}; + sz = sizeof(tmp); + if (sysctl(usermem_mib, 2, &tmp, &sz, NULL, 0) != 0) { + goto error; + } + ram_total = tmp; + + int uvmexp_mib[] = {CTL_VM, VM_UVMEXP}; + struct uvmexp uvmexp; + sz = sizeof(uvmexp); + if (sysctl(uvmexp_mib, 2, &uvmexp, &sz, NULL, 0) == -1) { + goto error; + } + const long pagesize = sysconf(_SC_PAGESIZE); + ram_free = uvmexp.free * pagesize; + + int shmall_mib[] = {CTL_KERN, KERN_SHMINFO, KERN_SHMINFO_SHMALL}; + sz = sizeof(tmp); + if (sysctl(shmall_mib, 3, &tmp, &sz, NULL, 0) == -1) { + goto error; + } + + int bcstats_mib[] = {CTL_VFS, VFS_GENERIC, VFS_BCACHESTAT}; + struct bcachestats bcstats; + sz = sizeof(bcstats); + if (sysctl(bcstats_mib, 3, &bcstats, &sz, NULL, 0) == -1) { + goto error; + } + ram_cached = bcstats.numbufpages * pagesize; + + /* Available is free and cached memory. */ + + ram_available = ram_free + ram_cached; + + /* This information doesn't seem to exist. */ + + ram_shared = ram_buffers = 0; +#elif defined(__linux__) + int unread_fields = 6; FILE *file = fopen("/proc/meminfo", "r"); if (!file) { goto error; @@ -138,6 +187,7 @@ void print_memory(memory_ctx_t *ctx) { ram_buffers *= 1024UL; ram_cached *= 1024UL; ram_shared *= 1024UL; +#endif unsigned long ram_used; if (BEGINS_WITH(ctx->memory_used_method, "memavailable")) {