beastie/beastie.c
2026-03-16 02:41:26 -05:00

336 lines
9.3 KiB
C

/* beastie - minimal system info */
#define VERSION "dev"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <pwd.h>
#include <sys/utsname.h>
#include <sys/types.h>
#include <sys/sysctl.h>
#include <sys/statvfs.h>
/* module IDs - reference these in config.h modules[] */
enum module {
MOD_SEP, /* blank separator line */
MOD_USERATHOST, /* user@hostname */
MOD_OS, /* os name + release */
MOD_KERNEL, /* kern.ident */
MOD_CPU, /* hw.model */
MOD_UPTIME, /* uptime */
MOD_MEM, /* used / total memory */
MOD_SHELL, /* $SHELL basename */
MOD_TERM, /* $TERM */
MOD_WM, /* wm (via xdpyinfo) */
MOD_DISK, /* used / total on / */
MOD_PKGS, /* pkg count */
MOD_RES, /* screen resolution */
MOD_COLORS, /* 16-color palette */
};
#include "config.h"
#define ART_H (int)(sizeof(ART)/sizeof(ART[0]))
#define NMOD (int)(sizeof(modules)/sizeof(modules[0]))
#define MAX_INFO 32
/* format uptime as Xd Xh Xm from boot time */
static void
uptime_str(char *buf, size_t len)
{
long up = 0;
struct timeval bt;
size_t sz = sizeof(bt);
if (sysctlbyname("kern.boottime", &bt, &sz, NULL, 0) == 0)
up = (long)(time(NULL) - bt.tv_sec);
if (!up) { snprintf(buf, len, "unknown"); return; }
long d = up / 86400, h = (up % 86400) / 3600, m = (up % 3600) / 60;
if (d) snprintf(buf, len, "%ldd %ldh %ldm", d, h, m);
else if (h) snprintf(buf, len, "%ldh %ldm", h, m);
else snprintf(buf, len, "%ldm", m);
}
/* format memory as used / total in MB */
static void
mem_str(char *buf, size_t len)
{
unsigned long total_mb = 0, used_mb = 0;
unsigned long physmem;
unsigned int free_c = 0, inactive_c = 0;
int pgsz = 0;
size_t sz;
sz = sizeof(physmem);
if (sysctlbyname("hw.physmem", &physmem, &sz, NULL, 0) == -1) {
snprintf(buf, len, "unknown"); return;
}
/* available = free + inactive pages */
sz = sizeof(free_c); sysctlbyname("vm.stats.vm.v_free_count", &free_c, &sz, NULL, 0);
sz = sizeof(inactive_c); sysctlbyname("vm.stats.vm.v_inactive_count", &inactive_c, &sz, NULL, 0);
sz = sizeof(pgsz); sysctlbyname("hw.pagesize", &pgsz, &sz, NULL, 0);
unsigned long avail = (unsigned long)(free_c + inactive_c) * pgsz;
total_mb = physmem >> 20;
used_mb = (physmem - avail) >> 20;
if (!total_mb) { snprintf(buf, len, "unknown"); return; }
snprintf(buf, len, "%luM / %luM", used_mb, total_mb);
}
/* read cpu model string */
static void
cpu_str(char *buf, size_t len)
{
size_t sz = len;
if (sysctlbyname("hw.model", buf, &sz, NULL, 0) == 0) return;
snprintf(buf, len, "unknown");
}
/* disk usage on / in GB used / total */
static void
disk_str(char *buf, size_t len)
{
struct statvfs s;
if (statvfs("/", &s) == -1) { snprintf(buf, len, "unknown"); return; }
unsigned long long total = (unsigned long long)s.f_blocks * s.f_frsize;
unsigned long long avail = (unsigned long long)s.f_bavail * s.f_frsize;
unsigned long long used = total - avail;
snprintf(buf, len, "%.1fG / %.1fG",
(double)used / (1024*1024*1024),
(double)total / (1024*1024*1024));
}
/* installed pkg count via pkg info */
static void
pkgs_str(char *buf, size_t len)
{
FILE *f = popen("pkg info 2>/dev/null | wc -l", "r");
if (f) {
int n = 0;
fscanf(f, "%d", &n);
pclose(f);
if (n > 0) { snprintf(buf, len, "%d (pkg)", n); return; }
}
snprintf(buf, len, "unknown");
}
/* screen resolution via xdpyinfo */
static void
res_str(char *buf, size_t len)
{
FILE *f = popen("xdpyinfo 2>/dev/null | grep dimensions | awk '{print $2}'", "r");
if (f) {
char res[32] = {0};
fgets(res, sizeof(res), f);
pclose(f);
size_t rl = strlen(res);
if (rl && res[rl-1] == '\n') res[rl-1] = '\0';
if (res[0]) { snprintf(buf, len, "%s", res); return; }
}
snprintf(buf, len, "unknown");
}
/* read custom kernel name via kern.ident sysctl */
static void
kernel_ident(char *buf, size_t len)
{
size_t sz = len;
if (sysctlbyname("kern.ident", buf, &sz, NULL, 0) == 0) return;
snprintf(buf, len, "unknown");
}
int
main(int argc, char *argv[])
{
if (argc == 2 && strcmp(argv[1], "--version") == 0) {
printf("Beastie version " VERSION "\n");
printf("Beastie is a minimal fetch tool for BSD systems.\n");
printf("Compiled with "
#if defined(__clang__)
"clang " __clang_version__
#elif defined(__GNUC__)
"gcc " __VERSION__
#else
"unknown compiler"
#endif
"\n");
return 0;
}
struct utsname uts;
char cpu[256] = {0};
char kid[64] = {0};
char upt[64] = {0};
char mem[64] = {0};
char dsk[64] = {0};
char pkg[64] = {0};
char res[64] = {0};
uname(&uts);
/* prefer $USER env, fall back to passwd db */
const char *user = getenv("USER");
if (!user) {
struct passwd *pw = getpwuid(getuid());
user = pw ? pw->pw_name : "?";
}
cpu_str(cpu, sizeof(cpu));
kernel_ident(kid, sizeof(kid));
uptime_str(upt, sizeof(upt));
mem_str(mem, sizeof(mem));
disk_str(dsk, sizeof(dsk));
pkgs_str(pkg, sizeof(pkg));
res_str(res, sizeof(res));
/* strip path prefix from $SHELL */
const char *shell = getenv("SHELL");
if (shell) { const char *s = strrchr(shell, '/'); if (s) shell = s + 1; }
else shell = "unknown";
/* normalize $TERM - strip -256color/-color suffix, map xterm generically */
char termbuf[64] = "unknown";
const char *termenv = getenv("TERM");
if (termenv) {
if (strncmp(termenv, "xterm", 5) == 0)
snprintf(termbuf, sizeof(termbuf), "generic terminal emulator");
else {
char *p;
snprintf(termbuf, sizeof(termbuf), "%s", termenv);
if ((p = strstr(termbuf, "-256color"))) *p = '\0';
else if ((p = strstr(termbuf, "-color"))) *p = '\0';
}
}
const char *term = termbuf;
/* detect x server vendor via xdpyinfo to avoid linking against libX11 */
char wmbuf[64] = "none";
if (getenv("DISPLAY")) {
FILE *xdpy = popen("xdpyinfo 2>/dev/null | grep 'vendor string' | awk '{print $3}'", "r");
if (xdpy) {
char vendor[64] = {0};
fgets(vendor, sizeof(vendor), xdpy);
pclose(xdpy);
size_t vl = strlen(vendor);
if (vl && vendor[vl-1] == '\n') vendor[vl-1] = '\0';
if (vendor[0])
snprintf(wmbuf, sizeof(wmbuf), "dwm (on %s)", vendor);
else
snprintf(wmbuf, sizeof(wmbuf), "dwm");
} else {
snprintf(wmbuf, sizeof(wmbuf), "dwm");
}
}
const char *wm = wmbuf;
/* build info lines from modules[] */
char ibufs[MAX_INFO][512];
const char *ilines[MAX_INFO];
int ni = 0;
for (int j = 0; j < NMOD && ni < MAX_INFO; j++) {
switch (modules[j]) {
case MOD_SEP:
ilines[ni++] = "";
break;
case MOD_USERATHOST:
snprintf(ibufs[ni], sizeof(ibufs[0]),
COLOR_BOLD COLOR_USER "%s" COLOR_RESET
"@"
COLOR_BOLD COLOR_USER "%s" COLOR_RESET,
user, uts.nodename);
ilines[ni] = ibufs[ni]; ni++;
break;
case MOD_OS:
snprintf(ibufs[ni], sizeof(ibufs[0]),
COLOR_LABEL "os " COLOR_RESET " %s %s",
uts.sysname, uts.release);
ilines[ni] = ibufs[ni]; ni++;
break;
case MOD_KERNEL:
snprintf(ibufs[ni], sizeof(ibufs[0]),
COLOR_LABEL "kernel" COLOR_RESET " %s", kid);
ilines[ni] = ibufs[ni]; ni++;
break;
case MOD_CPU:
snprintf(ibufs[ni], sizeof(ibufs[0]),
COLOR_LABEL "cpu " COLOR_RESET " %s", cpu);
ilines[ni] = ibufs[ni]; ni++;
break;
case MOD_UPTIME:
snprintf(ibufs[ni], sizeof(ibufs[0]),
COLOR_LABEL "uptime" COLOR_RESET " %s", upt);
ilines[ni] = ibufs[ni]; ni++;
break;
case MOD_MEM:
snprintf(ibufs[ni], sizeof(ibufs[0]),
COLOR_LABEL "mem " COLOR_RESET " %s", mem);
ilines[ni] = ibufs[ni]; ni++;
break;
case MOD_SHELL:
snprintf(ibufs[ni], sizeof(ibufs[0]),
COLOR_LABEL "shell " COLOR_RESET " %s", shell);
ilines[ni] = ibufs[ni]; ni++;
break;
case MOD_TERM:
snprintf(ibufs[ni], sizeof(ibufs[0]),
COLOR_LABEL "term " COLOR_RESET " %s", term);
ilines[ni] = ibufs[ni]; ni++;
break;
case MOD_WM:
snprintf(ibufs[ni], sizeof(ibufs[0]),
COLOR_LABEL "wm " COLOR_RESET " %s", wm);
ilines[ni] = ibufs[ni]; ni++;
break;
case MOD_DISK:
snprintf(ibufs[ni], sizeof(ibufs[0]),
COLOR_LABEL "disk " COLOR_RESET " %s", dsk);
ilines[ni] = ibufs[ni]; ni++;
break;
case MOD_PKGS:
snprintf(ibufs[ni], sizeof(ibufs[0]),
COLOR_LABEL "pkgs " COLOR_RESET " %s", pkg);
ilines[ni] = ibufs[ni]; ni++;
break;
case MOD_RES:
snprintf(ibufs[ni], sizeof(ibufs[0]),
COLOR_LABEL "res " COLOR_RESET " %s", res);
ilines[ni] = ibufs[ni]; ni++;
break;
case MOD_COLORS: {
/* render two rows of 8 color swatches each */
char *p = ibufs[ni];
size_t rem = sizeof(ibufs[0]);
int n;
for (int k = 0; k < 8; k++) {
n = snprintf(p, rem, "\033[4%dm ", k);
p += n; rem -= n;
}
n = snprintf(p, rem, COLOR_RESET " ");
p += n; rem -= n;
for (int k = 8; k < 16; k++) {
n = snprintf(p, rem, "\033[48;5;%dm ", k);
p += n; rem -= n;
}
snprintf(p, rem, COLOR_RESET);
ilines[ni] = ibufs[ni]; ni++;
break;
}
}
}
/* print art + info side by side, padding whichever is shorter */
int total = ni > ART_H ? ni : ART_H;
printf("\n");
for (int i = 0; i < total; i++) {
printf(" ");
if (i < ART_H)
printf(COLOR_ART "%s" COLOR_RESET, ART[i]);
else
printf("%*s", ART_W, "");
printf(" ");
if (i < ni)
printf("%s", ilines[i]);
printf("\n");
}
return 0;
}