Pi66

Home | Repos | About | Blog | Gaza

sta
minimal & simple status monitoring app.
git clone https://git.pi66.xyz/sta

About | Files | Commits | Refs | License


add: components dir for modular status functions

Author: pi66
Date: 2026-03-24 20:31
commit: 0b564174

Stat

A components/battery.c 257 +257 -0
A components/cpu.c 162 +162 -0
A components/datetime.c 20 +20 -0
A components/disk.c 62 +62 -0
A components/ram.c 220 +220 -0
A components/run_command.c 29 +29 -0
A components/temperature.c 75 +75 -0
A components/util.c 146 +146 -0
A components/wifi.c 275 +275 -0

Diff

diff --git a/components/battery.c b/components/battery.c
new file mode 100644
index 0000000..734bc1d
--- /dev/null
+++ b/components/battery.c
@@ -0,0 +1,257 @@
+/* See LICENSE file for copyright and license details. */
+#include <stdio.h>
+#include <string.h>
+
+#include "../util.h"
+
+#if defined(__linux__)
+/*
+ * https://www.kernel.org/doc/html/latest/power/power_supply_class.html
+ */
+	#include <limits.h>
+	#include <stdint.h>
+	#include <unistd.h>
+

...
Show all
diff --git a/components/battery.c b/components/battery.c
new file mode 100644
index 0000000..734bc1d
--- /dev/null
+++ b/components/battery.c
@@ -0,0 +1,257 @@
+/* See LICENSE file for copyright and license details. */
+#include <stdio.h>
+#include <string.h>
+
+#include "../util.h"
+
+#if defined(__linux__)
+/*
+ * https://www.kernel.org/doc/html/latest/power/power_supply_class.html
+ */
+	#include <limits.h>
+	#include <stdint.h>
+	#include <unistd.h>
+
+	#define POWER_SUPPLY_CAPACITY "/sys/class/power_supply/%s/capacity"
+	#define POWER_SUPPLY_STATUS   "/sys/class/power_supply/%s/status"
+	#define POWER_SUPPLY_CHARGE   "/sys/class/power_supply/%s/charge_now"
+	#define POWER_SUPPLY_ENERGY   "/sys/class/power_supply/%s/energy_now"
+	#define POWER_SUPPLY_CURRENT  "/sys/class/power_supply/%s/current_now"
+	#define POWER_SUPPLY_POWER    "/sys/class/power_supply/%s/power_now"
+
+	static const char *
+	pick(const char *bat, const char *f1, const char *f2, char *path,
+	     size_t length)
+	{
+	static _Thread_local char buf[1024];
+		if (esnprintf(buf, path, length, f1, bat) > 0 &&
+		    access(path, R_OK) == 0)
+			return f1;
+
+		if (esnprintf(buf, path, length, f2, bat) > 0 &&
+		    access(path, R_OK) == 0)
+			return f2;
+
+		return NULL;
+	}
+
+	const char *
+	battery_perc(const char *bat)
+	{
+	static _Thread_local char buf[1024];
+		int cap_perc;
+		char path[PATH_MAX];
+
+		if (esnprintf(buf, path, sizeof(path), POWER_SUPPLY_CAPACITY, bat) < 0)
+			return NULL;
+		if (pscanf(buf, path, "%d", &cap_perc) != 1)
+			return NULL;
+
+		return bprintf(buf, "%d", cap_perc);
+	}
+
+	const char *
+	battery_state(const char *bat)
+	{
+	static _Thread_local char buf[1024];
+		static struct {
+			char *state;
+			char *symbol;
+		} map[] = {
+			{ "Charging",    "󰚥" },
+			{ "Discharging", "󰚦" },
+			{ "Full",        "󰚥" },
+			{ "Not charging", "󰚥"},
+		};
+		size_t i;
+		char path[PATH_MAX], state[12];
+
+		if (esnprintf(buf, path, sizeof(path), POWER_SUPPLY_STATUS, bat) < 0)
+			return NULL;
+		if (pscanf(buf, path, "%12[a-zA-Z ]", state) != 1)
+			return NULL;
+
+		for (i = 0; i < LEN(map); i++)
+			if (!strcmp(map[i].state, state))
+				break;
+
+		return (i == LEN(map)) ? "?" : map[i].symbol;
+	}
+
+	const char *
+	battery_remaining(const char *bat)
+	{
+	static _Thread_local char buf[1024];
+		uintmax_t charge_now, current_now, m, h;
+		double timeleft;
+		char path[PATH_MAX], state[12];
+
+		if (esnprintf(buf, path, sizeof(path), POWER_SUPPLY_STATUS, bat) < 0)
+			return NULL;
+		if (pscanf(buf, path, "%12[a-zA-Z ]", state) != 1)
+			return NULL;
+
+		if (!pick(bat, POWER_SUPPLY_CHARGE, POWER_SUPPLY_ENERGY, path,
+		          sizeof(path)) ||
+		    pscanf(buf, path, "%ju", &charge_now) < 0)
+			return NULL;
+
+		if (!strcmp(state, "Discharging")) {
+			if (!pick(bat, POWER_SUPPLY_CURRENT, POWER_SUPPLY_POWER, path,
+			          sizeof(path)) ||
+			    pscanf(buf, path, "%ju", &current_now) < 0)
+				return NULL;
+
+			if (current_now == 0)
+				return NULL;
+
+			timeleft = (double)charge_now / (double)current_now;
+			h = timeleft;
+			m = (timeleft - (double)h) * 60;
+
+			return bprintf(buf, "%juh %jum", h, m);
+		}
+
+		return "";
+	}
+#elif defined(__OpenBSD__)
+	#include <fcntl.h>
+	#include <machine/apmvar.h>
+	#include <sys/ioctl.h>
+	#include <unistd.h>
+
+	static int
+	load_apm_power_info(struct apm_power_info *apm_info)
+	{
+	static _Thread_local char buf[1024];
+		int fd;
+
+		fd = open("/dev/apm", O_RDONLY);
+		if (fd < 0) {
+			warn("open '/dev/apm':");
+			return 0;
+		}
+
+		memset(apm_info, 0, sizeof(struct apm_power_info));
+		if (ioctl(fd, APM_IOC_GETPOWER, apm_info) < 0) {
+			warn("ioctl 'APM_IOC_GETPOWER':");
+			close(fd);
+			return 0;
+		}
+		return close(fd), 1;
+	}
+
+	const char *
+	battery_perc(const char *unused)
+	{
+	static _Thread_local char buf[1024];
+		struct apm_power_info apm_info;
+
+		if (load_apm_power_info(&apm_info))
+			return bprintf(buf, "%d", apm_info.battery_life);
+
+		return NULL;
+	}
+
+	const char *
+	battery_state(const char *unused)
+	{
+	static _Thread_local char buf[1024];
+		struct {
+			unsigned int state;
+			char *symbol;
+		} map[] = {
+			{ APM_AC_ON,      "+" },
+			{ APM_AC_OFF,     "-" },
+		};
+		struct apm_power_info apm_info;
+		size_t i;
+
+		if (load_apm_power_info(&apm_info)) {
+			for (i = 0; i < LEN(map); i++)
+				if (map[i].state == apm_info.ac_state)
+					break;
+
+			return (i == LEN(map)) ? "?" : map[i].symbol;
+		}
+
+		return NULL;
+	}
+
+	const char *
+	battery_remaining(const char *unused)
+	{
+	static _Thread_local char buf[1024];
+		struct apm_power_info apm_info;
+		unsigned int h, m;
+
+		if (load_apm_power_info(&apm_info)) {
+			if (apm_info.ac_state != APM_AC_ON) {
+				h = apm_info.minutes_left / 60;
+				m = apm_info.minutes_left % 60;
+				return bprintf(buf, "%uh %02um", h, m);
+			} else {
+				return "";
+			}
+		}
+
+		return NULL;
+	}
+#elif defined(__FreeBSD__)
+	#include <sys/sysctl.h>
+
+	#define BATTERY_LIFE  "hw.acpi.battery.life"
+	#define BATTERY_STATE "hw.acpi.battery.state"
+	#define BATTERY_TIME  "hw.acpi.battery.time"
+
+	const char *
+	battery_perc(const char *unused)
+	{
+	static _Thread_local char buf[1024];
+		int cap_perc;
+		size_t len;
+
+		len = sizeof(cap_perc);
+		if (sysctlbyname(BATTERY_LIFE, &cap_perc, &len, NULL, 0) < 0 || !len)
+			return NULL;
+
+		return bprintf(buf, "%d", cap_perc);
+	}
+
+	const char *
+	battery_state(const char *unused)
+	{
+	static _Thread_local char buf[1024];
+		int state;
+		size_t len;
+
+		len = sizeof(state);
+		if (sysctlbyname(BATTERY_STATE, &state, &len, NULL, 0) < 0 || !len)
+			return NULL;
+
+		switch (state) {
+		case 0: /* FALLTHROUGH */
+		case 2:
+			return "+";
+		case 1:
+			return "-";
+		default:
+			return "?";
+		}
+	}
+
+	const char *
+	battery_remaining(const char *unused)
+	{
+	static _Thread_local char buf[1024];
+		int rem;
+		size_t len;
+
+		len = sizeof(rem);
+		if (sysctlbyname(BATTERY_TIME, &rem, &len, NULL, 0) < 0 || !len
+		    || rem < 0)
+			return NULL;
+
+		return bprintf(buf, "%uh %02um", rem / 60, rem % 60);
+	}
+#endif
diff --git a/components/cpu.c b/components/cpu.c
new file mode 100644
index 0000000..f95759a
--- /dev/null
+++ b/components/cpu.c
@@ -0,0 +1,162 @@
+/* See LICENSE file for copyright and license details. */
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "../util.h"
+
+#if defined(__linux__)
+	#define CPU_FREQ "/sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq"
+
+	const char *
+	cpu_freq(const char *unused)
+	{
+	static _Thread_local char buf[1024];
+		uintmax_t freq;
+
+		/* in kHz */
+		if (pscanf(buf, CPU_FREQ, "%ju", &freq) != 1)
+			return NULL;
+
+		return fmt_human(buf, freq * 1000, 1000);
+	}
+
+	const char *
+	cpu_perc(const char *unused)
+	{
+	static _Thread_local char buf[1024];
+		static long double a[7];
+		long double b[7], sum;
+
+		memcpy(b, a, sizeof(b));
+		/* cpu user nice system idle iowait irq softirq */
+		if (pscanf(buf, "/proc/stat", "%*s %Lf %Lf %Lf %Lf %Lf %Lf %Lf",
+		           &a[0], &a[1], &a[2], &a[3], &a[4], &a[5], &a[6])
+		    != 7)
+			return NULL;
+
+		if (b[0] == 0)
+			return NULL;
+
+		sum = (b[0] + b[1] + b[2] + b[3] + b[4] + b[5] + b[6]) -
+		      (a[0] + a[1] + a[2] + a[3] + a[4] + a[5] + a[6]);
+
+		if (sum == 0)
+			return NULL;
+
+		return bprintf(buf, "%d", (int)(100 *
+		               ((b[0] + b[1] + b[2] + b[5] + b[6]) -
+		                (a[0] + a[1] + a[2] + a[5] + a[6])) / sum));
+	}
+#elif defined(__OpenBSD__)
+	#include <sys/param.h>
+	#include <sys/sched.h>
+	#include <sys/sysctl.h>
+
+	const char *
+	cpu_freq(const char *unused)
+	{
+	static _Thread_local char buf[1024];
+		int freq, mib[2];
+		size_t size;
+
+		mib[0] = CTL_HW;
+		mib[1] = HW_CPUSPEED;
+
+		size = sizeof(freq);
+
+		/* in MHz */
+		if (sysctl(mib, 2, &freq, &size, NULL, 0) < 0) {
+			warn("sysctl 'HW_CPUSPEED':");
+			return NULL;
+		}
+
+		return fmt_human(buf, freq * 1E6, 1000);
+	}
+
+	const char *
+	cpu_perc(const char *unused)
+	{
+	static _Thread_local char buf[1024];
+		int mib[2];
+		static uintmax_t a[CPUSTATES];
+		uintmax_t b[CPUSTATES], sum;
+		size_t size;
+
+		mib[0] = CTL_KERN;
+		mib[1] = KERN_CPTIME;
+
+		size = sizeof(a);
+
+		memcpy(b, a, sizeof(b));
+		if (sysctl(mib, 2, &a, &size, NULL, 0) < 0) {
+			warn("sysctl 'KERN_CPTIME':");
+			return NULL;
+		}
+		if (b[0] == 0)
+			return NULL;
+
+		sum = (a[CP_USER] + a[CP_NICE] + a[CP_SYS] + a[CP_INTR] + a[CP_IDLE]) -
+		      (b[CP_USER] + b[CP_NICE] + b[CP_SYS] + b[CP_INTR] + b[CP_IDLE]);
+
+		if (sum == 0)
+			return NULL;
+
+		return bprintf(buf, "%d", 100 *
+		               ((a[CP_USER] + a[CP_NICE] + a[CP_SYS] +
+		                 a[CP_INTR]) -
+		                (b[CP_USER] + b[CP_NICE] + b[CP_SYS] +
+		                 b[CP_INTR])) / sum);
+	}
+#elif defined(__FreeBSD__)
+	#include <devstat.h>
+	#include <sys/param.h>
+	#include <sys/sysctl.h>
+
+	const char *
+	cpu_freq(const char *unused)
+	{
+	static _Thread_local char buf[1024];
+		int freq;
+		size_t size;
+
+		size = sizeof(freq);
+		/* in MHz */
+		if (sysctlbyname("hw.clockrate", &freq, &size, NULL, 0) < 0 || !size) {
+			warn("sysctlbyname 'hw.clockrate':");
+			return NULL;
+		}
+
+		return fmt_human(buf, freq * 1E6, 1000);
+	}
+
+	const char *
+	cpu_perc(const char *unused)
+	{
+	static _Thread_local char buf[1024];
+		size_t size;
+		static long a[CPUSTATES];
+		long b[CPUSTATES], sum;
+
+		size = sizeof(a);
+		memcpy(b, a, sizeof(b));
+		if (sysctlbyname("kern.cp_time", &a, &size, NULL, 0) < 0 || !size) {
+			warn("sysctlbyname 'kern.cp_time':");
+			return NULL;
+		}
+		if (b[0] == 0)
+			return NULL;
+
+		sum = (a[CP_USER] + a[CP_NICE] + a[CP_SYS] + a[CP_INTR] + a[CP_IDLE]) -
+		      (b[CP_USER] + b[CP_NICE] + b[CP_SYS] + b[CP_INTR] + b[CP_IDLE]);
+
+		if (sum == 0)
+			return NULL;
+
+		return bprintf(buf, "%d", 100 *
+		               ((a[CP_USER] + a[CP_NICE] + a[CP_SYS] +
+		                 a[CP_INTR]) -
+		                (b[CP_USER] + b[CP_NICE] + b[CP_SYS] +
+		                 b[CP_INTR])) / sum);
+	}
+#endif
diff --git a/components/datetime.c b/components/datetime.c
new file mode 100644
index 0000000..b06ece2
--- /dev/null
+++ b/components/datetime.c
@@ -0,0 +1,20 @@
+/* See LICENSE file for copyright and license details. */
+#include <stdio.h>
+#include <time.h>
+
+#include "../util.h"
+
+const char *
+datetime(const char *fmt)
+{
+	static _Thread_local char buf[1024];
+	time_t t;
+
+	t = time(NULL);
+	if (!strftime(buf, sizeof(buf), fmt, localtime(&t))) {
+		warn("strftime: Result string exceeds buffer size");
+		return NULL;
+	}
+
+	return buf;
+}
diff --git a/components/disk.c b/components/disk.c
new file mode 100644
index 0000000..3d2028a
--- /dev/null
+++ b/components/disk.c
@@ -0,0 +1,62 @@
+/* See LICENSE file for copyright and license details. */
+#include <stdio.h>
+#include <sys/statvfs.h>
+
+#include "../util.h"
+
+const char *
+disk_free(const char *path)
+{
+	static _Thread_local char buf[1024];
+	struct statvfs fs;
+
+	if (statvfs(path, &fs) < 0) {
+		warn("statvfs '%s':", path);
+		return NULL;
+	}
+
+	return fmt_human(buf, fs.f_frsize * fs.f_bavail, 1024);
+}
+
+const char *
+disk_perc(const char *path)
+{
+	static _Thread_local char buf[1024];
+	struct statvfs fs;
+
+	if (statvfs(path, &fs) < 0) {
+		warn("statvfs '%s':", path);
+		return NULL;
+	}
+
+	return bprintf(buf, "%d", (int)(100 *
+	               (1 - ((double)fs.f_bavail / (double)fs.f_blocks))));
+}
+
+const char *
+disk_total(const char *path)
+{
+	static _Thread_local char buf[1024];
+	struct statvfs fs;
+
+	if (statvfs(path, &fs) < 0) {
+		warn("statvfs '%s':", path);
+		return NULL;
+	}
+
+	return fmt_human(buf, fs.f_frsize * fs.f_blocks, 1024);
+}
+
+const char *
+disk_used(const char *path)
+{
+	static _Thread_local char buf[1024];
+	struct statvfs fs;
+
+	if (statvfs(path, &fs) < 0) {
+		warn("statvfs '%s':", path);
+		return NULL;
+	}
+
+	return fmt_human(buf, fs.f_frsize * (fs.f_blocks - fs.f_bfree), 1024);
+}
diff --git a/components/ram.c b/components/ram.c
new file mode 100644
index 0000000..4fe2752
--- /dev/null
+++ b/components/ram.c
@@ -0,0 +1,220 @@
+/* See LICENSE file for copyright and license details. */
+#include <stdio.h>
+
+#include "../util.h"
+
+#if defined(__linux__)
+	#include <stdint.h>
+
+	const char *
+	ram_free(const char *unused)
+	{
+	static _Thread_local char buf[1024];
+		uintmax_t free;
+
+		if (pscanf(buf, "/proc/meminfo",
+		           "MemTotal: %ju kB\n"
+		           "MemFree: %ju kB\n"
+		           "MemAvailable: %ju kB\n",
+		           &free, &free, &free) != 3)
+			return NULL;
+
+		return fmt_human(buf, free * 1024, 1024);
+	}
+
+	const char *
+	ram_perc(const char *unused)
+	{
+	static _Thread_local char buf[1024];
+		uintmax_t total, free, buffers, cached;
+		int percent;
+
+		if (pscanf(buf, "/proc/meminfo",
+		           "MemTotal: %ju kB\n"
+		           "MemFree: %ju kB\n"
+		           "MemAvailable: %ju kB\n"
+		           "Buffers: %ju kB\n"
+		           "Cached: %ju kB\n",
+		           &total, &free, &buffers, &buffers, &cached) != 5)
+			return NULL;
+
+		if (total == 0)
+			return NULL;
+
+		percent = 100 * ((total - free) - (buffers + cached)) / total;
+		return bprintf(buf, "%d", percent);
+	}
+
+	const char *
+	ram_total(const char *unused)
+	{
+	static _Thread_local char buf[1024];
+		uintmax_t total;
+
+		if (pscanf(buf, "/proc/meminfo", "MemTotal: %ju kB\n", &total)
+		    != 1)
+			return NULL;
+
+		return fmt_human(buf, total * 1024, 1024);
+	}
+
+	const char *
+	ram_used(const char *unused)
+	{
+	static _Thread_local char buf[1024];
+		uintmax_t total, free, buffers, cached, used;
+
+		if (pscanf(buf, "/proc/meminfo",
+		           "MemTotal: %ju kB\n"
+		           "MemFree: %ju kB\n"
+		           "MemAvailable: %ju kB\n"
+		           "Buffers: %ju kB\n"
+		           "Cached: %ju kB\n",
+		           &total, &free, &buffers, &buffers, &cached) != 5)
+			return NULL;
+
+		used = (total - free - buffers - cached);
+		return fmt_human(buf, used * 1024, 1024);
+	}
+#elif defined(__OpenBSD__)
+	#include <stdlib.h>
+	#include <sys/sysctl.h>
+	#include <sys/types.h>
+	#include <unistd.h>
+
+	#define LOG1024 10
+	#define pagetok(size, pageshift) (size_t)(size << (pageshift - LOG1024))
+
+	inline int
+	load_uvmexp(struct uvmexp *uvmexp)
+	{
+	static _Thread_local char buf[1024];
+		int uvmexp_mib[] = {CTL_VM, VM_UVMEXP};
+		size_t size;
+
+		size = sizeof(*uvmexp);
+
+		if (sysctl(uvmexp_mib, 2, uvmexp, &size, NULL, 0) >= 0)
+			return 1;
+
+		return 0;
+	}
+
+	const char *
+	ram_free(const char *unused)
+	{
+	static _Thread_local char buf[1024];
+		struct uvmexp uvmexp;
+		int free_pages;
+
+		if (!load_uvmexp(&uvmexp))
+			return NULL;
+
+		free_pages = uvmexp.npages - uvmexp.active;
+		return fmt_human(buf, pagetok(free_pages, uvmexp.pageshift) *
+				 1024, 1024);
+	}
+
+	const char *
+	ram_perc(const char *unused)
+	{
+	static _Thread_local char buf[1024];
+		struct uvmexp uvmexp;
+		int percent;
+
+		if (!load_uvmexp(&uvmexp))
+			return NULL;
+
+		percent = uvmexp.active * 100 / uvmexp.npages;
+		return bprintf(buf, "%d", percent);
+	}
+
+	const char *
+	ram_total(const char *unused)
+	{
+	static _Thread_local char buf[1024];
+		struct uvmexp uvmexp;
+
+		if (!load_uvmexp(&uvmexp))
+			return NULL;
+
+		return fmt_human(buf, pagetok(uvmexp.npages,
+					 uvmexp.pageshift) * 1024, 1024);
+	}
+
+	const char *
+	ram_used(const char *unused)
+	{
+	static _Thread_local char buf[1024];
+		struct uvmexp uvmexp;
+
+		if (!load_uvmexp(&uvmexp))
+			return NULL;
+
+		return fmt_human(buf, pagetok(uvmexp.active,
+					 uvmexp.pageshift) * 1024, 1024);
+	}
+#elif defined(__FreeBSD__)
+	#include <sys/sysctl.h>
+	#include <sys/vmmeter.h>
+	#include <unistd.h>
+	#include <vm/vm_param.h>
+
+	const char *
+	ram_free(const char *unused) {
+		struct vmtotal vm_stats;
+		int mib[] = {CTL_VM, VM_TOTAL};
+		size_t len;
+
+		len = sizeof(struct vmtotal);
+		if (sysctl(mib, 2, &vm_stats, &len, NULL, 0) < 0
+		    || !len)
+			return NULL;
+
+		return fmt_human(buf, vm_stats.t_free * getpagesize(), 1024);
+	}
+
+	const char *
+	ram_total(const char *unused) {
+		unsigned int npages;
+		size_t len;
+
+		len = sizeof(npages);
+		if (sysctlbyname("vm.stats.vm.v_page_count",
+		                 &npages, &len, NULL, 0) < 0 || !len)
+			return NULL;
+
+		return fmt_human(buf, npages * getpagesize(), 1024);
+	}
+
+	const char *
+	ram_perc(const char *unused) {
+		unsigned int npages;
+		unsigned int active;
+		size_t len;
+
+		len = sizeof(npages);
+		if (sysctlbyname("vm.stats.vm.v_page_count",
+		                 &npages, &len, NULL, 0) < 0 || !len)
+			return NULL;
+
+		if (sysctlbyname("vm.stats.vm.v_active_count",
+		                 &active, &len, NULL, 0) < 0 || !len)
+			return NULL;
+
+		return bprintf(buf, "%d", active * 100 / npages);
+	}
+
+	const char *
+	ram_used(const char *unused) {
+		unsigned int active;
+		size_t len;
+
+		len = sizeof(active);
+		if (sysctlbyname("vm.stats.vm.v_active_count",
+		                 &active, &len, NULL, 0) < 0 || !len)
+			return NULL;
+
+		return fmt_human(buf, active * getpagesize(), 1024);
+	}
+#endif
diff --git a/components/run_command.c b/components/run_command.c
new file mode 100644
index 0000000..b9fc3a2
--- /dev/null
+++ b/components/run_command.c
@@ -0,0 +1,29 @@
+/* See LICENSE file for copyright and license details. */
+#include <stdio.h>
+#include <string.h>
+
+const char *
+run_command(const char *cmd)
+{
+	static _Thread_local char buf[1024];
+	char *p;
+	FILE *fp;
+
+	if (!(fp = popen(cmd, "r"))) {
+		fprintf(stderr,"error '%s':", cmd);
+		return "";
+	}
+
+	p = fgets(buf, sizeof(buf) - 1, fp);
+	if (pclose(fp) < 0) {
+		fprintf(stderr,"pclose '%s':", cmd);
+		return "";
+	}
+	if (!p)
+		return "";
+
+	if ((p = strrchr(buf, '\n')))
+		p[0] = '\0';
+
+	return buf[0] ? buf : "";
+}
diff --git a/components/temperature.c b/components/temperature.c
new file mode 100644
index 0000000..9d9e45d
--- /dev/null
+++ b/components/temperature.c
@@ -0,0 +1,75 @@
+/* See LICENSE file for copyright and license details. */
+#include <stddef.h>
+
+#include "../util.h"
+
+
+#if defined(__linux__)
+	#include <stdint.h>
+
+	const char *
+	temp(const char *file)
+	{
+	static _Thread_local char buf[1024];
+		uintmax_t temp;
+
+		if (pscanf(buf, file, "%ju", &temp) != 1)
+			return NULL;
+
+		return bprintf(buf, "%ju", temp / 1000);
+	}
+#elif defined(__OpenBSD__)
+	#include <stdio.h>
+	#include <sys/time.h> /* before <sys/sensors.h> for struct timeval */
+	#include <sys/sensors.h>
+	#include <sys/sysctl.h>
+
+	const char *
+	temp(const char *unused)
+	{
+	static _Thread_local char buf[1024];
+		int mib[5];
+		size_t size;
+		struct sensor temp;
+
+		mib[0] = CTL_HW;
+		mib[1] = HW_SENSORS;
+		mib[2] = 0; /* cpu0 */
+		mib[3] = SENSOR_TEMP;
+		mib[4] = 0; /* temp0 */
+
+		size = sizeof(temp);
+
+		if (sysctl(mib, 5, &temp, &size, NULL, 0) < 0) {
+			warn("sysctl 'SENSOR_TEMP':");
+			return NULL;
+		}
+
+		/* kelvin to celsius */
+		return bprintf(buf, "%d", (int)((float)(temp.value-273150000) / 1E6));
+	}
+#elif defined(__FreeBSD__)
+	#include <stdio.h>
+	#include <stdlib.h>
+	#include <sys/sysctl.h>
+
+	#define ACPI_TEMP "hw.acpi.thermal.%s.temperature"
+
+	const char *
+	temp(const char *zone)
+	{
+	static _Thread_local char buf[1024];
+		char buf[256];
+		int temp;
+		size_t len;
+
+		len = sizeof(temp);
+		snprintf(buf, sizeof(buf), ACPI_TEMP, zone);
+		if (sysctlbyname(buf, &temp, &len, NULL, 0) < 0
+				|| !len)
+			return NULL;
+
+		/* kelvin to decimal celcius */
+		return bprintf(buf, "%d.%d", (temp - 2731) / 10, abs((temp - 2731) % 10));
+	}
+#endif
diff --git a/components/util.c b/components/util.c
new file mode 100644
index 0000000..9524d85
--- /dev/null
+++ b/components/util.c
@@ -0,0 +1,146 @@
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../util.h"
+
+char *argv0;
+
+static void
+verr(const char *fmt, va_list ap)
+{
+	vfprintf(stderr, fmt, ap);
+
+	if (fmt[0] && fmt[strlen(fmt) - 1] == ':') {
+		fputc(' ', stderr);
+		perror(NULL);
+	} else {
+		fputc('\n', stderr);
+	}
+}
+
+void
+warn(const char *fmt, ...)
+{
+	
+	va_list ap;
+
+	va_start(ap, fmt);
+	verr(fmt, ap);
+	va_end(ap);
+}
+
+void
+die(const char *fmt, ...)
+{
+	
+	va_list ap;
+
+	va_start(ap, fmt);
+	verr(fmt, ap);
+	va_end(ap);
+
+	exit(1);
+}
+
+static int
+evsnprintf(char *str, size_t size, const char *fmt, va_list ap)
+{
+	
+	int ret;
+
+	ret = vsnprintf(str, size, fmt, ap);
+
+	if (ret < 0) {
+		warn("vsnprintf:");
+		return -1;
+	} else if ((size_t)ret >= size) {
+		warn("vsnprintf: Output truncated");
+		return -1;
+	}
+
+	return ret;
+}
+
+int 
+esnprintf(const char *buf, char *str, size_t size, const char *fmt, ...)
+{
+	
+	va_list ap;
+	int ret;
+
+	va_start(ap, fmt);
+	ret = evsnprintf(str, size, fmt, ap);
+	va_end(ap);
+
+	return ret;
+}
+
+char *
+bprintf(const char *buf, const char *fmt, ...)
+{
+    va_list ap;
+    int ret;
+
+    va_start(ap, fmt);
+    ret = evsnprintf(buf, sizeof(buf), fmt, ap);
+    va_end(ap);
+
+    return (ret < 0) ? NULL : buf;
+}
+
+const char *
+fmt_human(const char *buf, uintmax_t num, int base)
+{
+	
+	double scaled;
+	size_t i, prefixlen;
+	const char **prefix;
+	const char *prefix_1000[] = { "", "k", "M", "G", "T", "P", "E", "Z",
+	                              "Y" };
+	const char *prefix_1024[] = { "", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei",
+	                              "Zi", "Yi" };
+
+	switch (base) {
+	case 1000:
+		prefix = prefix_1000;
+		prefixlen = LEN(prefix_1000);
+		break;
+	case 1024:
+		prefix = prefix_1024;
+		prefixlen = LEN(prefix_1024);
+		break;
+	default:
+		warn("fmt_human: Invalid base");
+		return NULL;
+	}
+
+	scaled = num;
+	for (i = 0; i < prefixlen && scaled >= base; i++)
+		scaled /= base;
+
+	return bprintf(buf, "%.1f %s", scaled, prefix[i]);
+}
+
+int
+pscanf(const char *buf, const char *path, const char *fmt, ...)
+{
+	
+	FILE *fp;
+	va_list ap;
+	int n;
+
+	if (!(fp = fopen(path, "r"))) {
+		warn("fopen '%s':", path);
+		return -1;
+	}
+	va_start(ap, fmt);
+	n = vfscanf(fp, fmt, ap);
+	va_end(ap);
+	fclose(fp);
+
+	return (n == EOF) ? -1 : n;
+}
+
diff --git a/components/wifi.c b/components/wifi.c
new file mode 100644
index 0000000..5808175
--- /dev/null
+++ b/components/wifi.c
@@ -0,0 +1,275 @@
+/* See LICENSE file for copyright and license details. */
+#include <ifaddrs.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+#include "../util.h"
+
+#define RSSI_TO_PERC(rssi) \
+			rssi >= -50 ? 100 : \
+			(rssi <= -100 ? 0 : \
+			(2 * (rssi + 100)))
+
+#if defined(__linux__)
+	#include <limits.h>
+	#include <linux/wireless.h>
+
+	#define NET_OPERSTATE "/sys/class/net/%s/operstate"
+
+	const char *
+	wifi_perc(const char *interface)
+	{
+	static _Thread_local char buf[1024];
+		int cur;
+		size_t i;
+		char *p, *datastart;
+		char path[PATH_MAX];
+		char status[5];
+		FILE *fp;
+
+		if (esnprintf(buf, path, sizeof(path), NET_OPERSTATE, interface) < 0)
+			return NULL;
+		if (!(fp = fopen(path, "r"))) {
+			warn("fopen '%s':", path);
+			return NULL;
+		}
+		p = fgets(status, 5, fp);
+		fclose(fp);
+		if (!p || strcmp(status, "up\n") != 0)
+			return NULL;
+
+		if (!(fp = fopen("/proc/net/wireless", "r"))) {
+			warn("fopen '/proc/net/wireless':");
+			return NULL;
+		}
+
+		for (i = 0; i < 3; i++)
+			if (!(p = fgets(buf, sizeof(buf) - 1, fp)))
+				break;
+
+		fclose(fp);
+		if (i < 2 || !p)
+			return NULL;
+
+		if (!(datastart = strstr(buf, interface)))
+			return NULL;
+
+		datastart = (datastart+(strlen(interface)+1));
+		sscanf(datastart + 1, " %*d   %d  %*d  %*d\t\t  %*d\t   "
+		       "%*d\t\t%*d\t\t %*d\t  %*d\t\t %*d", &cur);
+
+		/* 70 is the max of /proc/net/wireless */
+		return bprintf(buf, "%d", (int)((float)cur / 70 * 100));
+	}
+
+	const char *
+	wifi_essid(const char *interface)
+	{
+	static _Thread_local char buf[1024];
+		static char id[IW_ESSID_MAX_SIZE+1];
+		int sockfd;
+		struct iwreq wreq;
+
+		memset(&wreq, 0, sizeof(struct iwreq));
+		wreq.u.essid.length = IW_ESSID_MAX_SIZE+1;
+		if (esnprintf(buf, wreq.ifr_name, sizeof(wreq.ifr_name), "%s",
+		              interface) < 0)
+			return NULL;
+
+		if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+			warn("socket 'AF_INET':");
+			return NULL;
+		}
+		wreq.u.essid.pointer = id;
+		if (ioctl(sockfd,SIOCGIWESSID, &wreq) < 0) {
+			warn("ioctl 'SIOCGIWESSID':");
+			close(sockfd);
+			return NULL;
+		}
+
+		close(sockfd);
+
+		if (!strcmp(id, ""))
+			return NULL;
+
+		return id;
+	}
+#elif defined(__OpenBSD__)
+	#include <net/if.h>
+	#include <net/if_media.h>
+	#include <net80211/ieee80211.h>
+	#include <sys/select.h> /* before <sys/ieee80211_ioctl.h> for NBBY */
+	#include <net80211/ieee80211_ioctl.h>
+	#include <stdlib.h>
+	#include <sys/types.h>
+
+	static int
+	load_ieee80211_nodereq(const char *interface, struct ieee80211_nodereq *nr)
+	{
+	static _Thread_local char buf[1024];
+		struct ieee80211_bssid bssid;
+		int sockfd;
+		uint8_t zero_bssid[IEEE80211_ADDR_LEN];
+
+		memset(&bssid, 0, sizeof(bssid));
+		memset(nr, 0, sizeof(struct ieee80211_nodereq));
+		if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+			warn("socket 'AF_INET':");
+			return 0;
+		}
+		strlcpy(bssid.i_name, interface, sizeof(bssid.i_name));
+		if ((ioctl(sockfd, SIOCG80211BSSID, &bssid)) < 0) {
+			warn("ioctl 'SIOCG80211BSSID':");
+			close(sockfd);
+			return 0;
+		}
+		memset(&zero_bssid, 0, sizeof(zero_bssid));
+		if (memcmp(bssid.i_bssid, zero_bssid,
+		    IEEE80211_ADDR_LEN) == 0) {
+			close(sockfd);
+			return 0;
+		}
+		strlcpy(nr->nr_ifname, interface, sizeof(nr->nr_ifname));
+		memcpy(&nr->nr_macaddr, bssid.i_bssid, sizeof(nr->nr_macaddr));
+		if ((ioctl(sockfd, SIOCG80211NODE, nr)) < 0 && nr->nr_rssi) {
+			warn("ioctl 'SIOCG80211NODE':");
+			close(sockfd);
+			return 0;
+		}
+
+		return close(sockfd), 1;
+	}
+
+	const char *
+	wifi_perc(const char *interface)
+	{
+	static _Thread_local char buf[1024];
+		struct ieee80211_nodereq nr;
+		int q;
+
+		if (load_ieee80211_nodereq(interface, &nr)) {
+			if (nr.nr_max_rssi)
+				q = IEEE80211_NODEREQ_RSSI(&nr);
+			else
+				q = RSSI_TO_PERC(nr.nr_rssi);
+
+			return bprintf(buf, "%d", q);
+		}
+
+		return NULL;
+	}
+
+	const char *
+	wifi_essid(const char *interface)
+	{
+	static _Thread_local char buf[1024];
+		struct ieee80211_nodereq nr;
+
+		if (load_ieee80211_nodereq(interface, &nr))
+			return bprintf(buf, "%s", nr.nr_nwid);
+
+		return NULL;
+	}
+#elif defined(__FreeBSD__)
+	#include <net/if.h>
+	#include <net80211/ieee80211_ioctl.h>
+
+	int
+	load_ieee80211req(int sock, const char *interface, void *data, int type, size_t *len)
+	{
+	static _Thread_local char buf[1024];
+		char warn_buf[256];
+		struct ieee80211req ireq;
+		memset(&ireq, 0, sizeof(ireq));
+		ireq.i_type = type;
+		ireq.i_data = (caddr_t) data;
+		ireq.i_len = *len;
+
+		strlcpy(ireq.i_name, interface, sizeof(ireq.i_name));
+		if (ioctl(sock, SIOCG80211, &ireq) < 0) {
+			snprintf(warn_buf,  sizeof(warn_buf),
+					"ioctl: 'SIOCG80211': %d", type);
+			warn(warn_buf);
+			return 0;
+		}
+
+		*len = ireq.i_len;
+		return 1;
+	}
+
+	const char *
+	wifi_perc(const char *interface)
+	{
+	static _Thread_local char buf[1024];
+		union {
+			struct ieee80211req_sta_req sta;
+			uint8_t buf[24 * 1024];
+		} info;
+		uint8_t bssid[IEEE80211_ADDR_LEN];
+		int rssi_dbm;
+		int sockfd;
+		size_t len;
+		const char *fmt;
+
+		if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+			warn("socket 'AF_INET':");
+			return NULL;
+		}
+
+		/* Retreive MAC address of interface */
+		len = IEEE80211_ADDR_LEN;
+		fmt = NULL;
+		if (load_ieee80211req(sockfd, interface, &bssid, IEEE80211_IOC_BSSID, &len))
+		{
+	static _Thread_local char buf[1024];
+			/* Retrieve info on station with above BSSID */
+			memset(&info, 0, sizeof(info));
+			memcpy(info.sta.is_u.macaddr, bssid, sizeof(bssid));
+
+			len = sizeof(info);
+			if (load_ieee80211req(sockfd, interface, &info, IEEE80211_IOC_STA_INFO, &len)) {
+				rssi_dbm = info.sta.info[0].isi_noise +
+ 					         info.sta.info[0].isi_rssi / 2;
+
+				fmt = bprintf(buf, "%d", RSSI_TO_PERC(rssi_dbm));
+			}
+		}
+
+		close(sockfd);
+		return fmt;
+	}
+
+	const char *
+	wifi_essid(const char *interface)
+	{
+	static _Thread_local char buf[1024];
+		char ssid[IEEE80211_NWID_LEN + 1];
+		size_t len;
+		int sockfd;
+		const char *fmt;
+
+		if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+			warn("socket 'AF_INET':");
+			return NULL;
+		}
+
+		fmt = NULL;
+		len = sizeof(ssid);
+		memset(&ssid, 0, len);
+		if (load_ieee80211req(sockfd, interface, &ssid, IEEE80211_IOC_SSID, &len)) {
+			if (len < sizeof(ssid))
+				len += 1;
+			else
+				len = sizeof(ssid);
+
+			ssid[len - 1] = '\0';
+			fmt = bprintf(buf, "%s", ssid);
+		}
+
+		close(sockfd);
+		return fmt;
+	}
+#endif

// Created by Pi66

// Copyright ©Pi66 2026