ssf.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582
  1. #ifdef __OpenBSD__
  2. #define _BSD_SOURCE
  3. #else
  4. #define _POSIX_C_SOURCE 200809L
  5. #endif
  6. #include <pwd.h>
  7. #include <stdio.h>
  8. #include <stdlib.h>
  9. #include <string.h>
  10. #include <strings.h>
  11. #include <sys/types.h>
  12. #include <sys/utsname.h>
  13. #include <time.h>
  14. #include <unistd.h>
  15. #ifdef __OpenBSD__
  16. #include <getopt.h>
  17. #endif
  18. #ifdef __linux__
  19. #include <sys/sysinfo.h>
  20. #endif
  21. #ifdef __OpenBSD__
  22. #include <sys/param.h>
  23. #include <sys/sysctl.h>
  24. #include <sys/time.h>
  25. #include <uvm/uvm_extern.h>
  26. #endif
  27. #include "ascii"
  28. /* sizes */
  29. #define MAXLINE 256
  30. #define BUFSIZE 512
  31. #define CLR_RESET "\033[0m"
  32. #define CLR_BLUE "\033[1;34m"
  33. #define CLR_CYAN "\033[1;36m"
  34. #define CLR_GREEN "\033[1;32m"
  35. #define CLR_MAGENTA "\033[1;35m"
  36. #define CLR_RED "\033[1;31m"
  37. #define CLR_WHITE "\033[1;37m"
  38. #define CLR_YELLOW "\033[1;33m"
  39. #define LEN(x) (sizeof(x) / sizeof(*(x)))
  40. #define MAXIMUM(a,b) ((a) > (b) ? (a) : (b))
  41. #define UNKNOWN(buf) sset((buf), sizeof(buf), "unknown")
  42. #define APPEND(buf, fmt, ...) \
  43. do { \
  44. size_t __off = strlen(buf); \
  45. if (__off < sizeof(buf)) \
  46. snprintf((buf) + __off, sizeof(buf) - __off, fmt, __VA_ARGS__); \
  47. } while (0)
  48. #ifdef __OpenBSD__
  49. static int sysctl_hw (const char *, void *, size_t *);
  50. #endif
  51. struct Info
  52. {
  53. char hostname[MAXLINE], kernel[MAXLINE], uptime[MAXLINE];
  54. char cpu[MAXLINE], memory[MAXLINE], load[MAXLINE];
  55. char distro[MAXLINE], shell[MAXLINE], terminal[MAXLINE], user[MAXLINE];
  56. };
  57. struct AsciiEntry
  58. {
  59. const char *needle;
  60. const char **art;
  61. const char *color;
  62. };
  63. static void sset (char *, size_t, const char *);
  64. static void usage (void);
  65. static void hostname (struct Info *);
  66. static void kernel (struct Info *);
  67. static void uptime (struct Info *);
  68. static void cpu (struct Info *);
  69. static void memory (struct Info *);
  70. static void load (struct Info *);
  71. static void distro (struct Info *);
  72. static void shell (struct Info *);
  73. static void terminal (struct Info *);
  74. static void user (struct Info *);
  75. static void collect (struct Info *);
  76. static void display (struct Info *, int, int, char *);
  77. static const char **asciisel (const char *, const char **);
  78. static int asciimatch (const char *, const char *);
  79. static char *argv0;
  80. static const struct AsciiEntry ascii_entries[] = {
  81. {"alpine", ascii_alpine, CLR_CYAN},
  82. {"android", ascii_android, CLR_GREEN},
  83. {"arch", ascii_arch, CLR_CYAN},
  84. {"arco", ascii_arco, CLR_BLUE},
  85. {"artix", ascii_artix, CLR_CYAN},
  86. {"centos", ascii_centos, CLR_MAGENTA},
  87. {"debian", ascii_debian, CLR_RED},
  88. {"endeavour", ascii_endeavour, CLR_MAGENTA},
  89. {"fedora", ascii_fedora, CLR_BLUE},
  90. {"freebsd", ascii_freebsd, CLR_RED},
  91. {"gentoo", ascii_gentoo, CLR_MAGENTA},
  92. {"linux mint", ascii_linux_mint, CLR_GREEN},
  93. {"macos", ascii_macos, CLR_WHITE},
  94. {"mac os", ascii_macos, CLR_WHITE},
  95. {"manjaro", ascii_manjaro, CLR_GREEN},
  96. {"nixos", ascii_nixos, CLR_BLUE},
  97. {"openbsd", ascii_openbsd, CLR_YELLOW},
  98. {"opensuse", ascii_opensuse, CLR_GREEN},
  99. {"pop!_os", ascii_pop_os, CLR_CYAN},
  100. {"pop os", ascii_pop_os, CLR_CYAN},
  101. {"slackware", ascii_slackware, CLR_BLUE},
  102. {"solus", ascii_solus, CLR_BLUE},
  103. {"ubuntu", ascii_ubuntu, CLR_MAGENTA},
  104. {"void", ascii_void, CLR_GREEN},
  105. {"linux", ascii_linux, CLR_WHITE}
  106. };
  107. static void
  108. sset (char *dst, size_t dstsz, const char *src)
  109. {
  110. if (!dstsz)
  111. return;
  112. #ifdef __OpenBSD__
  113. strlcpy (dst, src, dstsz);
  114. #else
  115. snprintf (dst, dstsz, "%s", src);
  116. #endif
  117. }
  118. #ifdef __OpenBSD__
  119. static int
  120. sysctl_hw (const char *name, void *out, size_t *lenp)
  121. {
  122. int mib[2];
  123. int miblen = 2;
  124. if (!strcmp (name, "hw.model")) {
  125. mib[0] = CTL_HW;
  126. mib[1] = HW_MODEL;
  127. }
  128. else if (!strcmp (name, "hw.ncpu")) {
  129. mib[0] = CTL_HW;
  130. mib[1] = HW_NCPU;
  131. }
  132. else if (!strcmp (name, "hw.physmem64")) {
  133. mib[0] = CTL_HW;
  134. mib[1] = HW_PHYSMEM64;
  135. }
  136. else {
  137. return -1;
  138. }
  139. return sysctl (mib, (u_int) miblen, out, lenp, NULL, 0);
  140. }
  141. #endif
  142. static void
  143. usage (void)
  144. {
  145. fprintf (stderr, "usage: %s [-m] [-s] [-k key] [-h] [-v]\n", argv0);
  146. exit (1);
  147. }
  148. static void
  149. hostname (struct Info *i)
  150. {
  151. if (gethostname (i->hostname, sizeof (i->hostname)) < 0)
  152. UNKNOWN (i->hostname);
  153. }
  154. static void
  155. kernel (struct Info *i)
  156. {
  157. struct utsname u;
  158. if (uname (&u) < 0 || !*u.sysname)
  159. UNKNOWN (i->kernel);
  160. else
  161. snprintf (i->kernel, sizeof (i->kernel), "%s %s", u.sysname, u.release);
  162. }
  163. static void
  164. uptime (struct Info *i)
  165. {
  166. #if defined(__linux__)
  167. struct sysinfo s;
  168. int d, h, m;
  169. if (sysinfo (&s) < 0) {
  170. UNKNOWN (i->uptime);
  171. return;
  172. }
  173. d = s.uptime / 86400;
  174. h = (s.uptime % 86400) / 3600;
  175. m = (s.uptime % 3600) / 60;
  176. #elif defined(__OpenBSD__)
  177. struct timeval boottime;
  178. time_t now, up;
  179. int mib[2] = { CTL_KERN, KERN_BOOTTIME };
  180. size_t size = sizeof (boottime);
  181. int d, h, m;
  182. if (sysctl (mib, 2, &boottime, &size, NULL, 0) < 0 || !boottime.tv_sec) {
  183. UNKNOWN (i->uptime);
  184. return;
  185. }
  186. if ((now = time (NULL)) == (time_t) - 1) {
  187. UNKNOWN (i->uptime);
  188. return;
  189. }
  190. up = now - boottime.tv_sec;
  191. if (up < 0)
  192. up = 0;
  193. d = up / 86400;
  194. h = (up % 86400) / 3600;
  195. m = (up % 3600) / 60;
  196. #else
  197. UNKNOWN (i->uptime);
  198. return;
  199. #endif
  200. if (d)
  201. snprintf (i->uptime, sizeof (i->uptime), "%dd %dh %dm", d, h, m);
  202. else if (h)
  203. snprintf (i->uptime, sizeof (i->uptime), "%dh %dm", h, m);
  204. else
  205. snprintf (i->uptime, sizeof (i->uptime), "%dm", m);
  206. }
  207. static void
  208. cpu (struct Info *i)
  209. {
  210. #if defined(__linux__)
  211. FILE *f;
  212. char buf[BUFSIZE], model[MAXLINE] = "unknown";
  213. int cores = 0;
  214. if (!(f = fopen ("/proc/cpuinfo", "r"))) {
  215. UNKNOWN (i->cpu);
  216. return;
  217. }
  218. while (fgets (buf, sizeof (buf), f))
  219. if (!strncmp (buf, "model name", 10)) {
  220. if ((buf[strcspn (buf, "\n")] = 0, strchr (buf, ':')))
  221. sset (model, sizeof (model), strchr (buf, ':') + 2);
  222. }
  223. else if (!strncmp (buf, "processor", 9))
  224. cores++;
  225. fclose (f);
  226. sset (i->cpu, sizeof (i->cpu), model);
  227. if (cores)
  228. APPEND (i->cpu, " (%d cores)", cores);
  229. #elif defined(__OpenBSD__)
  230. char model[MAXLINE] = "unknown";
  231. int cores = 0;
  232. size_t len;
  233. len = sizeof (model);
  234. if (sysctl_hw ("hw.model", model, &len) == -1 || !*model) {
  235. UNKNOWN (i->cpu);
  236. return;
  237. }
  238. len = sizeof (cores);
  239. if (sysctl_hw ("hw.ncpu", &cores, &len) == -1)
  240. cores = 0;
  241. sset (i->cpu, sizeof (i->cpu), model);
  242. if (cores > 0)
  243. APPEND (i->cpu, " (%d cores)", cores);
  244. #else
  245. UNKNOWN (i->cpu);
  246. #endif
  247. }
  248. static void
  249. memory (struct Info *i)
  250. {
  251. #if defined(__linux__)
  252. FILE *f;
  253. char buf[BUFSIZE];
  254. long total = 0, avail = 0;
  255. if (!(f = fopen ("/proc/meminfo", "r"))) {
  256. UNKNOWN (i->memory);
  257. return;
  258. }
  259. while (fgets (buf, sizeof (buf), f) && (!total || !avail))
  260. if (!strncmp (buf, "MemTotal:", 9))
  261. sscanf (buf, "MemTotal: %ld", &total);
  262. else if (!strncmp (buf, "MemAvailable:", 13))
  263. sscanf (buf, "MemAvailable: %ld", &avail);
  264. fclose (f);
  265. if (total && avail) {
  266. long used = (total - avail) / 1024;
  267. long tmb = total / 1024;
  268. snprintf (i->memory, sizeof (i->memory), "%ld/%ld MB", used, tmb);
  269. }
  270. else {
  271. UNKNOWN (i->memory);
  272. }
  273. #elif defined(__OpenBSD__)
  274. int mib[2];
  275. struct uvmexp uvmexp;
  276. unsigned long long total, used;
  277. size_t len;
  278. long pagesize;
  279. mib[0] = CTL_HW;
  280. mib[1] = HW_PHYSMEM64;
  281. len = sizeof (total);
  282. if (sysctl (mib, 2, &total, &len, NULL, 0) == -1) {
  283. UNKNOWN (i->memory);
  284. return;
  285. }
  286. mib[0] = CTL_VM;
  287. mib[1] = VM_UVMEXP;
  288. len = sizeof (uvmexp);
  289. if (sysctl (mib, 2, &uvmexp, &len, NULL, 0) == -1) {
  290. UNKNOWN (i->memory);
  291. return;
  292. }
  293. if ((pagesize = sysconf (_SC_PAGESIZE)) <= 0) {
  294. UNKNOWN (i->memory);
  295. return;
  296. }
  297. used =
  298. (unsigned long long) (uvmexp.active +
  299. uvmexp.wired) * (unsigned long long) pagesize;
  300. snprintf (i->memory, sizeof (i->memory), "%llu/%llu MiB",
  301. used / (1024ULL * 1024ULL), total / (1024ULL * 1024ULL));
  302. #else
  303. UNKNOWN (i->memory);
  304. #endif
  305. }
  306. static void
  307. load (struct Info *i)
  308. {
  309. #if defined(__linux__)
  310. struct sysinfo s;
  311. if (sysinfo (&s) < 0) {
  312. UNKNOWN (i->load);
  313. return;
  314. }
  315. snprintf (i->load, sizeof (i->load), "%.2f %.2f %.2f",
  316. s.loads[0] / 65536.0, s.loads[1] / 65536.0, s.loads[2] / 65536.0);
  317. #elif defined(__OpenBSD__)
  318. double loadavg[3];
  319. if (getloadavg (loadavg, 3) == -1) {
  320. UNKNOWN (i->load);
  321. return;
  322. }
  323. snprintf (i->load, sizeof (i->load), "%.2f %.2f %.2f",
  324. loadavg[0], loadavg[1], loadavg[2]);
  325. #else
  326. UNKNOWN (i->load);
  327. #endif
  328. }
  329. static void
  330. distro (struct Info *i)
  331. {
  332. FILE *f;
  333. char buf[BUFSIZE], *p, *q;
  334. *i->distro = 0;
  335. if ((f = fopen ("/etc/os-release", "r"))) {
  336. while (fgets (buf, sizeof (buf), f))
  337. if (!strncmp (buf, "PRETTY_NAME=", 12) && (p = strchr (buf, '"'))) {
  338. if ((q = strchr (++p, '"'))) {
  339. *q = 0;
  340. sset (i->distro, sizeof (i->distro), p);
  341. break;
  342. }
  343. }
  344. fclose (f);
  345. }
  346. if (!*i->distro && (f = fopen ("/etc/issue", "r"))) {
  347. if (fgets (buf, sizeof (buf), f)) {
  348. buf[strcspn (buf, "\n\\\\")] = 0;
  349. sset (i->distro, sizeof (i->distro), buf);
  350. }
  351. fclose (f);
  352. }
  353. if (!*i->distro) {
  354. struct utsname u;
  355. if (uname (&u) == 0 && *u.sysname)
  356. sset (i->distro, sizeof (i->distro), u.sysname);
  357. else
  358. UNKNOWN (i->distro);
  359. }
  360. }
  361. static void
  362. shell (struct Info *i)
  363. {
  364. char *s, *b;
  365. if (!(s = getenv ("SHELL"))) {
  366. UNKNOWN (i->shell);
  367. return;
  368. }
  369. sset (i->shell, sizeof (i->shell), (b = strrchr (s, '/')) ? b + 1 : s);
  370. }
  371. static void
  372. terminal (struct Info *i)
  373. {
  374. char *t = getenv ("TERM");
  375. if (!t) {
  376. UNKNOWN (i->terminal);
  377. return;
  378. }
  379. sset (i->terminal, sizeof (i->terminal), t);
  380. }
  381. static void
  382. user (struct Info *i)
  383. {
  384. struct passwd *p = getpwuid (getuid ());
  385. if (!p || !p->pw_name) {
  386. UNKNOWN (i->user);
  387. return;
  388. }
  389. sset (i->user, sizeof (i->user), p->pw_name);
  390. }
  391. static void
  392. collect (struct Info *i)
  393. {
  394. hostname (i);
  395. kernel (i);
  396. uptime (i);
  397. cpu (i);
  398. memory (i);
  399. load (i);
  400. distro (i);
  401. shell (i);
  402. terminal (i);
  403. user (i);
  404. }
  405. static void
  406. display (struct Info *i, int m, int s, char *k)
  407. {
  408. char *sep;
  409. struct
  410. {
  411. char *name, *val;
  412. } fields[] = {
  413. {"user", i->user}, {"hostname", i->hostname}, {"distro", i->distro},
  414. {"kernel", i->kernel}, {"uptime", i->uptime}, {"shell", i->shell},
  415. {"terminal", i->terminal}, {"cpu", i->cpu}, {"memory", i->memory},
  416. {"load", i->load}
  417. }, short_fields[] = {
  418. {"hostname", i->hostname}, {"kernel", i->kernel}, {"uptime", i->uptime},
  419. {"memory", i->memory}
  420. };
  421. const char **ascii;
  422. const char *color, *line;
  423. int n, j, ascii_lines, ascii_width, max_lines, len, pad;
  424. int total, short_total;
  425. sep = m ? "|" : ": ";
  426. total = LEN (fields);
  427. short_total = LEN (short_fields);
  428. if (k) {
  429. for (j = 0; j < total; j++)
  430. if (!strcmp (k, fields[j].name)) {
  431. printf ("%s\n", fields[j].val);
  432. return;
  433. }
  434. return;
  435. }
  436. ascii = asciisel (i->distro, &color);
  437. ascii_lines = 0;
  438. ascii_width = 0;
  439. if (ascii) {
  440. for (j = 0; ascii[j]; j++) {
  441. len = (int) strlen (ascii[j]);
  442. if (len > ascii_width)
  443. ascii_width = len;
  444. }
  445. ascii_lines = j;
  446. }
  447. n = s ? short_total : total;
  448. max_lines = MAXIMUM (ascii_lines, n);
  449. for (j = 0; j < max_lines; j++) {
  450. len = 0;
  451. pad = ascii_width;
  452. if (j < ascii_lines) {
  453. line = ascii[j];
  454. len = (int) strlen (line);
  455. pad = ascii_width - len;
  456. if (pad < 0)
  457. pad = 0;
  458. printf ("%s%s%s", color, line, CLR_RESET);
  459. if (pad)
  460. printf ("%*s", pad, "");
  461. }
  462. else if (ascii_width) {
  463. printf ("%*s", ascii_width, "");
  464. }
  465. printf (" ");
  466. if (j < n)
  467. printf ("%s%s%s", (s ? short_fields : fields)[j].name, sep,
  468. (s ? short_fields : fields)[j].val);
  469. putchar ('\n');
  470. }
  471. }
  472. static const char **
  473. asciisel (const char *distro, const char **color)
  474. {
  475. int idx, count;
  476. count = (int) LEN (ascii_entries);
  477. for (idx = 0; idx < count; idx++)
  478. if (asciimatch (distro, ascii_entries[idx].needle)) {
  479. *color = ascii_entries[idx].color;
  480. return ascii_entries[idx].art;
  481. }
  482. *color = CLR_WHITE;
  483. return ascii_linux;
  484. }
  485. static int
  486. asciimatch (const char *distro, const char *needle)
  487. {
  488. size_t len;
  489. const char *p;
  490. if (!distro || !needle)
  491. return 0;
  492. len = strlen (needle);
  493. if (!len)
  494. return 0;
  495. for (p = distro; *p; p++)
  496. if (!strncasecmp (p, needle, len))
  497. return 1;
  498. return 0;
  499. }
  500. int
  501. main (int argc, char **argv)
  502. {
  503. struct Info info = { 0 };
  504. int m = 0, s = 0, c;
  505. char *k = NULL;
  506. for (argv0 = *argv; (c = getopt (argc, argv, "msk:hv")) != -1;)
  507. switch (c) {
  508. case 'm':
  509. m = 1;
  510. break;
  511. case 's':
  512. s = 1;
  513. break;
  514. case 'k':
  515. k = optarg;
  516. break;
  517. case 'h':
  518. usage ();
  519. break;
  520. case 'v':
  521. puts ("ssf 1.0");
  522. exit (0);
  523. default:
  524. usage ();
  525. }
  526. collect (&info);
  527. display (&info, m, s, k);
  528. return 0;
  529. }