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
Show less
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>
+
...
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", ¤t_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