ssf
Owner: IIIlllIIIllI URL: git@git.0x00nyx.xyz:seb/ssf.git
ssf.c
/* (c) 2025 Sebastian Michalk <sebastian.michalk@pm.me> */
#ifndef __OpenBSD__
#define _POSIX_C_SOURCE 200809L
#endif
#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/utsname.h>
#include <time.h>
#include <unistd.h>
#ifdef __linux__
#include <sys/sysinfo.h>
#endif
#ifdef __OpenBSD__
#include <sys/param.h>
#include <sys/sysctl.h>
#include <sys/time.h>
#endif
#include "ascii"
/* sizes */
#define MAXLINE 256
#define BUFSIZE 512
#define CLR_RESET "\033[0m"
#define CLR_BLUE "\033[1;34m"
#define CLR_CYAN "\033[1;36m"
#define CLR_GREEN "\033[1;32m"
#define CLR_MAGENTA "\033[1;35m"
#define CLR_RED "\033[1;31m"
#define CLR_WHITE "\033[1;37m"
#define CLR_YELLOW "\033[1;33m"
#define LEN(x) (sizeof(x) / sizeof(*(x)))
#define MAXIMUM(a,b) ((a) > (b) ? (a) : (b))
#define UNKNOWN(buf) strcpy((buf), "unknown")
#define APPEND(buf, fmt, ...) \
do { \
size_t __off = strlen(buf); \
if (__off < sizeof(buf)) \
snprintf((buf) + __off, sizeof(buf) - __off, fmt, __VA_ARGS__); \
} while (0)
struct Info {
char hostname[MAXLINE], kernel[MAXLINE], uptime[MAXLINE];
char cpu[MAXLINE], memory[MAXLINE], load[MAXLINE];
char distro[MAXLINE], shell[MAXLINE], terminal[MAXLINE], user[MAXLINE];
};
struct AsciiEntry {
const char *needle;
const char **art;
const char *color;
};
static void usage(void);
static void hostname(struct Info *);
static void kernel(struct Info *);
static void uptime(struct Info *);
static void cpu(struct Info *);
static void memory(struct Info *);
static void load(struct Info *);
static void distro(struct Info *);
static void shell(struct Info *);
static void terminal(struct Info *);
static void user(struct Info *);
static void collect(struct Info *);
static void display(struct Info *, int, int, char *);
static const char **asciisel(const char *, const char **);
static int asciimatch(const char *, const char *);
static char *argv0;
static const struct AsciiEntry ascii_entries[] = {
{"alpine", ascii_alpine, CLR_CYAN},
{"android", ascii_android, CLR_GREEN},
{"arch", ascii_arch, CLR_CYAN},
{"arco", ascii_arco, CLR_BLUE},
{"artix", ascii_artix, CLR_CYAN},
{"centos", ascii_centos, CLR_MAGENTA},
{"debian", ascii_debian, CLR_RED},
{"endeavour", ascii_endeavour, CLR_MAGENTA},
{"fedora", ascii_fedora, CLR_BLUE},
{"freebsd", ascii_freebsd, CLR_RED},
{"gentoo", ascii_gentoo, CLR_MAGENTA},
{"linux mint", ascii_linux_mint, CLR_GREEN},
{"macos", ascii_macos, CLR_WHITE},
{"mac os", ascii_macos, CLR_WHITE},
{"manjaro", ascii_manjaro, CLR_GREEN},
{"nixos", ascii_nixos, CLR_BLUE},
{"openbsd", ascii_openbsd, CLR_YELLOW},
{"opensuse", ascii_opensuse, CLR_GREEN},
{"pop!_os", ascii_pop_os, CLR_CYAN},
{"pop os", ascii_pop_os, CLR_CYAN},
{"slackware", ascii_slackware, CLR_BLUE},
{"solus", ascii_solus, CLR_BLUE},
{"ubuntu", ascii_ubuntu, CLR_MAGENTA},
{"void", ascii_void, CLR_GREEN},
{"linux", ascii_linux, CLR_WHITE}
};
static void
usage(void)
{
fprintf(stderr, "usage: %s [-m] [-s] [-k key] [-h] [-v]\n", argv0);
exit(1);
}
static void
hostname(struct Info *i)
{
if (gethostname(i->hostname, sizeof(i->hostname)) < 0)
UNKNOWN(i->hostname);
}
static void
kernel(struct Info *i)
{
struct utsname u;
if (uname(&u) < 0 || !*u.sysname)
UNKNOWN(i->kernel);
else
snprintf(i->kernel, sizeof(i->kernel), "%s %s", u.sysname, u.release);
}
static void
uptime(struct Info *i)
{
#if defined(__linux__)
struct sysinfo s;
int d, h, m;
if (sysinfo(&s) < 0) {
UNKNOWN(i->uptime);
return;
}
d = s.uptime / 86400;
h = (s.uptime % 86400) / 3600;
m = (s.uptime % 3600) / 60;
#elif defined(__OpenBSD__)
struct timeval boottime;
time_t now, up;
int mib[2] = { CTL_KERN, KERN_BOOTTIME };
size_t size = sizeof(boottime);
int d, h, m;
if (sysctl(mib, 2, &boottime, &size, NULL, 0) < 0 || !boottime.tv_sec) {
UNKNOWN(i->uptime);
return;
}
if ((now = time(NULL)) == (time_t)-1) {
UNKNOWN(i->uptime);
return;
}
up = now - boottime.tv_sec;
if (up < 0)
up = 0;
d = up / 86400;
h = (up % 86400) / 3600;
m = (up % 3600) / 60;
#else
UNKNOWN(i->uptime);
return;
#endif
if (d)
snprintf(i->uptime, sizeof(i->uptime), "%dd %dh %dm", d, h, m);
else if (h)
snprintf(i->uptime, sizeof(i->uptime), "%dh %dm", h, m);
else
snprintf(i->uptime, sizeof(i->uptime), "%dm", m);
}
static void
cpu(struct Info *i)
{
#if defined(__linux__)
FILE *f;
char buf[BUFSIZE], model[MAXLINE] = "unknown";
int cores = 0;
if (!(f = fopen("/proc/cpuinfo", "r"))) {
UNKNOWN(i->cpu);
return;
}
while (fgets(buf, sizeof(buf), f))
if (!strncmp(buf, "model name", 10)) {
if ((buf[strcspn(buf, "\n")] = 0, strchr(buf, ':')))
strcpy(model, strchr(buf, ':') + 2);
} else if (!strncmp(buf, "processor", 9))
cores++;
fclose(f);
strcpy(i->cpu, model);
if (cores)
APPEND(i->cpu, " (%d cores)", cores);
#elif defined(__OpenBSD__)
char model[MAXLINE] = "unknown";
int cores = 0;
size_t len;
len = sizeof(model);
if (sysctlbyname("hw.model", model, &len, NULL, 0) == -1 || !*model) {
UNKNOWN(i->cpu);
return;
}
len = sizeof(cores);
if (sysctlbyname("hw.ncpu", &cores, &len, NULL, 0) == -1)
cores = 0;
strcpy(i->cpu, model);
if (cores > 0)
APPEND(i->cpu, " (%d cores)", cores);
#else
UNKNOWN(i->cpu);
#endif
}
static void
memory(struct Info *i)
{
#if defined(__linux__)
FILE *f;
char buf[BUFSIZE];
long total = 0, avail = 0;
if (!(f = fopen("/proc/meminfo", "r"))) {
UNKNOWN(i->memory);
return;
}
while (fgets(buf, sizeof(buf), f) && (!total || !avail))
if (!strncmp(buf, "MemTotal:", 9))
sscanf(buf, "MemTotal: %ld", &total);
else if (!strncmp(buf, "MemAvailable:", 13))
sscanf(buf, "MemAvailable: %ld", &avail);
fclose(f);
if (total && avail) {
long used = (total - avail) / 1024;
long tmb = total / 1024;
snprintf(i->memory, sizeof(i->memory), "%ld/%ld MB", used, tmb);
} else {
UNKNOWN(i->memory);
}
#elif defined(__OpenBSD__)
long pagesize = 0;
unsigned int pagecount = 0, freecount = 0;
unsigned long long total, freep, used;
size_t len;
len = sizeof(pagesize);
if (sysctlbyname("hw.pagesize", &pagesize, &len, NULL, 0) == -1 || pagesize <= 0) {
UNKNOWN(i->memory);
return;
}
len = sizeof(pagecount);
if (sysctlbyname("vm.stats.vm.v_page_count", &pagecount, &len, NULL, 0) == -1 || !pagecount) {
UNKNOWN(i->memory);
return;
}
len = sizeof(freecount);
if (sysctlbyname("vm.stats.vm.v_free_count", &freecount, &len, NULL, 0) == -1)
freecount = 0;
total = (unsigned long long)pagecount * (unsigned long long)pagesize;
freep = (unsigned long long)freecount * (unsigned long long)pagesize;
used = total > freep ? total - freep : 0;
snprintf(i->memory, sizeof(i->memory), "%llu/%llu MB",
used / (1024ULL * 1024ULL), total / (1024ULL * 1024ULL));
#else
UNKNOWN(i->memory);
#endif
}
static void
load(struct Info *i)
{
#if defined(__linux__)
struct sysinfo s;
if (sysinfo(&s) < 0) {
UNKNOWN(i->load);
return;
}
snprintf(i->load, sizeof(i->load), "%.2f %.2f %.2f",
s.loads[0] / 65536.0, s.loads[1] / 65536.0, s.loads[2] / 65536.0);
#elif defined(__OpenBSD__)
double loadavg[3];
if (getloadavg(loadavg, 3) == -1) {
UNKNOWN(i->load);
return;
}
snprintf(i->load, sizeof(i->load), "%.2f %.2f %.2f",
loadavg[0], loadavg[1], loadavg[2]);
#else
UNKNOWN(i->load);
#endif
}
static void
distro(struct Info *i)
{
FILE *f;
char buf[BUFSIZE], *p, *q;
*i->distro = 0;
if ((f = fopen("/etc/os-release", "r"))) {
while (fgets(buf, sizeof(buf), f))
if (!strncmp(buf, "PRETTY_NAME=", 12) && (p = strchr(buf, '"'))) {
if ((q = strchr(++p, '"'))) {
*q = 0;
strcpy(i->distro, p);
break;
}
}
fclose(f);
}
if (!*i->distro && (f = fopen("/etc/issue", "r"))) {
if (fgets(buf, sizeof(buf), f)) {
buf[strcspn(buf, "\n\\\\")] = 0;
strcpy(i->distro, buf);
}
fclose(f);
}
if (!*i->distro) UNKNOWN(i->distro);
}
static void
shell(struct Info *i)
{
char *s, *b;
if (!(s = getenv("SHELL"))) {
UNKNOWN(i->shell);
return;
}
strcpy(i->shell, (b = strrchr(s, '/')) ? b + 1 : s);
}
static void
terminal(struct Info *i)
{
char *t = getenv("TERM");
if (!t) {
UNKNOWN(i->terminal);
return;
}
strcpy(i->terminal, t);
}
static void
user(struct Info *i)
{
struct passwd *p = getpwuid(getuid());
if (!p || !p->pw_name) {
UNKNOWN(i->user);
return;
}
strcpy(i->user, p->pw_name);
}
static void
collect(struct Info *i)
{
hostname(i); kernel(i); uptime(i); cpu(i); memory(i);
load(i); distro(i); shell(i); terminal(i); user(i);
}
static void
display(struct Info *i, int m, int s, char *k)
{
char *sep;
struct { char *name, *val; } fields[] = {
{"user", i->user}, {"hostname", i->hostname}, {"distro", i->distro},
{"kernel", i->kernel}, {"uptime", i->uptime}, {"shell", i->shell},
{"terminal", i->terminal}, {"cpu", i->cpu}, {"memory", i->memory}, {"load", i->load}
}, short_fields[] = {
{"hostname", i->hostname}, {"kernel", i->kernel}, {"uptime", i->uptime}, {"memory", i->memory}
};
const char **ascii;
const char *color, *line;
int n, j, ascii_lines, ascii_width, max_lines, len, pad;
int total, short_total;
sep = m ? "|" : ": ";
total = LEN(fields);
short_total = LEN(short_fields);
if (k) {
for (j = 0; j < total; j++)
if (!strcmp(k, fields[j].name)) {
printf("%s\n", fields[j].val);
return;
}
return;
}
ascii = asciisel(i->distro, &color);
ascii_lines = 0;
ascii_width = 0;
if (ascii) {
for (j = 0; ascii[j]; j++) {
len = (int)strlen(ascii[j]);
if (len > ascii_width)
ascii_width = len;
}
ascii_lines = j;
}
n = s ? short_total : total;
max_lines = MAXIMUM(ascii_lines, n);
for (j = 0; j < max_lines; j++) {
len = 0;
pad = ascii_width;
if (j < ascii_lines) {
line = ascii[j];
len = (int)strlen(line);
pad = ascii_width - len;
if (pad < 0)
pad = 0;
printf("%s%s%s", color, line, CLR_RESET);
if (pad)
printf("%*s", pad, "");
} else if (ascii_width) {
printf("%*s", ascii_width, "");
}
printf(" ");
if (j < n)
printf("%s%s%s", (s ? short_fields : fields)[j].name, sep,
(s ? short_fields : fields)[j].val);
putchar('\n');
}
}
static const char **
asciisel(const char *distro, const char **color)
{
int idx, count;
count = (int)LEN(ascii_entries);
for (idx = 0; idx < count; idx++)
if (asciimatch(distro, ascii_entries[idx].needle)) {
*color = ascii_entries[idx].color;
return ascii_entries[idx].art;
}
*color = CLR_WHITE;
return ascii_linux;
}
static int
asciimatch(const char *distro, const char *needle)
{
size_t len;
const char *p;
if (!distro || !needle)
return 0;
len = strlen(needle);
if (!len)
return 0;
for (p = distro; *p; p++)
if (!strncasecmp(p, needle, len))
return 1;
return 0;
}
int
main(int argc, char **argv)
{
struct Info info = {0};
int m = 0, s = 0, c;
char *k = NULL;
for (argv0 = *argv; (c = getopt(argc, argv, "msk:hv")) != -1;)
switch (c) {
case 'm': m = 1; break;
case 's': s = 1; break;
case 'k': k = optarg; break;
case 'h': usage(); break;
case 'v': puts("ssf 1.0"); exit(0);
default: usage();
}
collect(&info);
display(&info, m, s, k);
return 0;
}