|
|
@@ -0,0 +1,478 @@
|
|
|
+#define _POSIX_C_SOURCE 200809L
|
|
|
+
|
|
|
+#include <pwd.h>
|
|
|
+#include <stdio.h>
|
|
|
+#include <stdlib.h>
|
|
|
+#include <string.h>
|
|
|
+#include <strings.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>
|
|
|
+#include <uvm/uvm_extern.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 MAX(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__)
|
|
|
+ struct uvmexp u;
|
|
|
+ size_t len = sizeof(u);
|
|
|
+ int mib[2] = { CTL_VM, VM_UVMEXP };
|
|
|
+ unsigned long long total, freep, used;
|
|
|
+
|
|
|
+ if (sysctl(mib, 2, &u, &len, NULL, 0) < 0 || !u.pagesize || !u.npages) {
|
|
|
+ UNKNOWN(i->memory);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ total = (unsigned long long)u.npages * (unsigned long long)u.pagesize;
|
|
|
+ freep = (unsigned long long)u.free * (unsigned long long)u.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 = MAX(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;
|
|
|
+}
|