/* beastie - minimal system info */ #define VERSION "dev" #include #include #include #include #include #include #include #include #include #include /* 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; }