initial commit
This commit is contained in:
commit
240504f287
6 changed files with 472 additions and 0 deletions
16
Makefile
Normal file
16
Makefile
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
# beastie - minimal system info
|
||||
include config.mk
|
||||
|
||||
beastie: beastie.c config.h
|
||||
${CC} ${CFLAGS} -o $@ beastie.c ${LDFLAGS}
|
||||
|
||||
install: beastie
|
||||
install -m 755 beastie ${DESTDIR}${PREFIX}/bin/beastie
|
||||
|
||||
uninstall:
|
||||
rm -f ${DESTDIR}${PREFIX}/bin/beastie
|
||||
|
||||
clean:
|
||||
rm -f beastie
|
||||
|
||||
.PHONY: install uninstall clean
|
||||
59
README
Normal file
59
README
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
beastie - minimal system fetch
|
||||
================================
|
||||
|
||||
Minimal system info tool for BSD. Displays info side-by-side with
|
||||
ASCII art. Named after the BSD daemon mascot.
|
||||
|
||||
Reads hardware/OS info via sysctlbyname(3).
|
||||
WM detection uses xdpyinfo(1) at runtime if MOD_WM is enabled.
|
||||
|
||||
|
||||
EXAMPLE OUTPUT
|
||||
--------------
|
||||
|
||||
%@ realdaemon@daemon-box
|
||||
%# @##
|
||||
%#@ @%%%%%%@%%##@ os FreeBSD 15.0-RELEASE-p4
|
||||
@%%##@%%%%%@######%## kernel MinKern
|
||||
%%%###%%%%# @@ @# cpu Intel(R) Core(TM) i7-4910MQ CPU @ 2.90GHz
|
||||
%%%%%%%%## @ @#@ uptime 3h 45m
|
||||
@%%%%%## @@#@@# mem 2689M / 16237M
|
||||
%%%%#### @@##### shell sh
|
||||
%%%%%%#####%##@## *: term st
|
||||
@%%%#@######%#@ : :@ wm dwm (on XLibre)
|
||||
%%%%%%###@ @# @:@ @:
|
||||
%%###### %#@
|
||||
@%%@##@###@%%##@
|
||||
%%%##%####@%@@
|
||||
@%%%%%%%%%@
|
||||
@%@::%@@%##@
|
||||
@%:@%%%####@
|
||||
@%%%@@%@:@%:#
|
||||
@%%%%%@ @+ :-:@@:::@@@
|
||||
%##@@ %::::::# . @
|
||||
%%@ %@ @ @
|
||||
@@%####
|
||||
|
||||
|
||||
CONFIGURATION
|
||||
-------------
|
||||
Edit config.h and recompile. Configurable:
|
||||
|
||||
COLOR_ART ANSI color for the ASCII art
|
||||
COLOR_USER ANSI color for the user@host header
|
||||
COLOR_LABEL ANSI color for info labels
|
||||
ART_W Visual width of the widest art line (must be accurate)
|
||||
ART[] The ASCII art, one line per string
|
||||
|
||||
|
||||
BUILD AND INSTALL
|
||||
-----------------
|
||||
make
|
||||
doas make install
|
||||
|
||||
Installs to /usr/local/bin/beastie. Override PREFIX to change.
|
||||
|
||||
|
||||
REQUIREMENTS
|
||||
------------
|
||||
BSD: base system, optionally xdpyinfo(1) for wm vendor detection
|
||||
BIN
beastie
Executable file
BIN
beastie
Executable file
Binary file not shown.
336
beastie.c
Normal file
336
beastie.c
Normal file
|
|
@ -0,0 +1,336 @@
|
|||
/* 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;
|
||||
}
|
||||
54
config.h
Normal file
54
config.h
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
/* beastie - configuration, edit and recompile */
|
||||
|
||||
/* colors */
|
||||
#define COLOR_RESET "\033[0m"
|
||||
#define COLOR_BOLD "\033[1m"
|
||||
#define COLOR_ART "\033[1;31m" /* bold red - art */
|
||||
#define COLOR_USER "\033[1;34m" /* bold blue - user@host */
|
||||
#define COLOR_LABEL "\033[1;35m" /* bold magenta - info labels */
|
||||
|
||||
/* modules to display, in order - comment out or reorder to taste */
|
||||
static const enum module modules[] = {
|
||||
MOD_USERATHOST,
|
||||
MOD_SEP,
|
||||
MOD_OS,
|
||||
MOD_KERNEL,
|
||||
MOD_CPU,
|
||||
MOD_UPTIME,
|
||||
MOD_MEM,
|
||||
MOD_SHELL,
|
||||
MOD_TERM,
|
||||
MOD_WM,
|
||||
MOD_DISK,
|
||||
MOD_PKGS,
|
||||
MOD_RES,
|
||||
MOD_SEP,
|
||||
/* MOD_COLORS, */
|
||||
};
|
||||
|
||||
/* ascii art - ART_W must equal the visual width of the longest line */
|
||||
#define ART_W 38
|
||||
static const char *ART[] = {
|
||||
" %@ ",
|
||||
" %# @## ",
|
||||
" %#@ @%%%%%%@%%##@ ",
|
||||
" @%%##@%%%%%@######%## ",
|
||||
" %%%###%%%%# @@ @# ",
|
||||
" %%%%%%%%## @ @#@ ",
|
||||
" @%%%%%## @@#@@# ",
|
||||
" %%%%#### @@##### ",
|
||||
" %%%%%%#####%##@## *: ",
|
||||
" @%%%#@######%#@ : :@ ",
|
||||
" %%%%%%###@ @# @:@ @: ",
|
||||
" %%###### %#@ ",
|
||||
" @%%@##@###@%%##@ ",
|
||||
" %%%##%####@%@@ ",
|
||||
" @%%%%%%%%%@ ",
|
||||
" @%@::%@@%##@ ",
|
||||
" @%:@%%%####@ ",
|
||||
" @%%%@@%@:@%:# ",
|
||||
" @%%%%%@ @+ :-:@@:::@@@ ",
|
||||
" %##@@ %::::::# . @ ",
|
||||
"%%@ %@ @ @ ",
|
||||
" @@%#### ",
|
||||
};
|
||||
7
config.mk
Normal file
7
config.mk
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
# beastie - build configuration, edit and recompile
|
||||
|
||||
PREFIX = /usr/local
|
||||
|
||||
CC = cc
|
||||
CFLAGS = -O2 -Wall -Wextra -std=c99 -pedantic
|
||||
LDFLAGS =
|
||||
Loading…
Reference in a new issue