267 lines
6.4 KiB
C
267 lines
6.4 KiB
C
#define _POSIX_C_SOURCE 200809L
|
|
|
|
/*
|
|
* qfetch.c — dynamic & portable system information fetcher
|
|
*
|
|
* Goals:
|
|
* - Pure C (C99)
|
|
* - Heap-allocated strings (caller owns memory)
|
|
* - Portable across Linux, macOS, and BSD
|
|
* - Graceful fallbacks when platform-specific features are unavailable
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <pwd.h>
|
|
#include <sys/utsname.h>
|
|
#include <errno.h>
|
|
|
|
/* Platform-specific headers for macOS / BSD */
|
|
#if defined(__APPLE__) || defined(__FreeBSD__)
|
|
#include <sys/types.h>
|
|
#include <sys/sysctl.h>
|
|
#include <sys/time.h>
|
|
#include <time.h>
|
|
#endif
|
|
|
|
/*
|
|
* Utility: duplicate a string or return "unknown" if NULL/empty.
|
|
* Always returns heap-allocated memory.
|
|
*/
|
|
static char *dup_or_unknown(const char *s) {
|
|
if (!s || !*s)
|
|
return strdup("unknown");
|
|
return strdup(s);
|
|
}
|
|
|
|
/*
|
|
* Fetch system hostname using POSIX gethostname().
|
|
* Buffer size is determined dynamically via sysconf().
|
|
*/
|
|
char *get_hostname(void) {
|
|
long max = sysconf(_SC_HOST_NAME_MAX);
|
|
if (max < 0)
|
|
max = 256; /* fallback if limit is unavailable */
|
|
|
|
char *buf = calloc(max + 1, 1);
|
|
if (!buf)
|
|
return dup_or_unknown(NULL);
|
|
|
|
if (gethostname(buf, max) != 0) {
|
|
free(buf);
|
|
return dup_or_unknown(NULL);
|
|
}
|
|
return buf;
|
|
}
|
|
|
|
/*
|
|
* Fetch current username via getpwuid().
|
|
*/
|
|
char *get_user(void) {
|
|
struct passwd *pw = getpwuid(getuid());
|
|
return pw ? dup_or_unknown(pw->pw_name) : dup_or_unknown(NULL);
|
|
}
|
|
|
|
/*
|
|
* Fetch login shell name (basename only, e.g. "bash").
|
|
*/
|
|
char *get_shell(void) {
|
|
struct passwd *pw = getpwuid(getuid());
|
|
if (!pw || !pw->pw_shell)
|
|
return dup_or_unknown(NULL);
|
|
|
|
/* Extract basename from full path */
|
|
const char *s = strrchr(pw->pw_shell, '/');
|
|
return dup_or_unknown(s ? s + 1 : pw->pw_shell);
|
|
}
|
|
|
|
/*
|
|
* Fetch kernel name and version using uname().
|
|
*/
|
|
char *get_kernel(void) {
|
|
struct utsname u;
|
|
if (uname(&u) != 0)
|
|
return dup_or_unknown(NULL);
|
|
|
|
size_t len = strlen(u.sysname) + strlen(u.release) + 2;
|
|
char *buf = malloc(len);
|
|
if (!buf)
|
|
return dup_or_unknown(NULL);
|
|
|
|
snprintf(buf, len, "%s %s", u.sysname, u.release);
|
|
return buf;
|
|
}
|
|
|
|
/*
|
|
* Fetch operating system / distribution name.
|
|
* Linux: /etc/os-release
|
|
* macOS/BSD: static identifier
|
|
*/
|
|
char *get_os(void) {
|
|
#if defined(__linux__)
|
|
FILE *f = fopen("/etc/os-release", "r");
|
|
if (!f)
|
|
return dup_or_unknown("Linux");
|
|
|
|
char line[512];
|
|
while (fgets(line, sizeof line, f)) {
|
|
if (strncmp(line, "PRETTY_NAME=", 12) == 0) {
|
|
char *v = line + 12;
|
|
v[strcspn(v, "\n")] = 0; /* strip newline */
|
|
if (*v == '"') v++;
|
|
char *e = strrchr(v, '"');
|
|
if (e) *e = 0;
|
|
fclose(f);
|
|
return strdup(v);
|
|
}
|
|
}
|
|
fclose(f);
|
|
return dup_or_unknown("Linux");
|
|
#elif defined(__APPLE__)
|
|
return dup_or_unknown("macOS");
|
|
#elif defined(__FreeBSD__)
|
|
return dup_or_unknown("FreeBSD");
|
|
#else
|
|
return dup_or_unknown("Unix");
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* Fetch system uptime.
|
|
* Linux: /proc/uptime
|
|
* macOS/BSD: sysctl(KERN_BOOTTIME)
|
|
*/
|
|
char *get_uptime(void) {
|
|
#if defined(__linux__)
|
|
FILE *f = fopen("/proc/uptime", "r");
|
|
if (!f)
|
|
return dup_or_unknown(NULL);
|
|
|
|
double s;
|
|
if (fscanf(f, "%lf", &s) != 1) {
|
|
fclose(f);
|
|
return dup_or_unknown(NULL);
|
|
}
|
|
fclose(f);
|
|
#elif defined(__APPLE__) || defined(__FreeBSD__)
|
|
struct timeval boottime;
|
|
size_t len = sizeof(boottime);
|
|
int mib[2] = { CTL_KERN, KERN_BOOTTIME };
|
|
|
|
if (sysctl(mib, 2, &boottime, &len, NULL, 0) != 0)
|
|
return dup_or_unknown(NULL);
|
|
|
|
time_t now = time(NULL);
|
|
double s = difftime(now, boottime.tv_sec);
|
|
#else
|
|
return dup_or_unknown("n/a");
|
|
#endif
|
|
|
|
/* Convert seconds to human-readable format */
|
|
int d = s / 86400;
|
|
int h = ((int)s % 86400) / 3600;
|
|
int m = ((int)s % 3600) / 60;
|
|
|
|
char buf[64];
|
|
if (d)
|
|
snprintf(buf, sizeof buf, "%dd %dh %dm", d, h, m);
|
|
else if (h)
|
|
snprintf(buf, sizeof buf, "%dh %dm", h, m);
|
|
else
|
|
snprintf(buf, sizeof buf, "%dm", m);
|
|
|
|
return strdup(buf);
|
|
}
|
|
|
|
/*
|
|
* Fetch CPU model and core count.
|
|
* Linux: /proc/cpuinfo
|
|
* macOS/BSD: sysctl hw.model + hw.ncpu
|
|
*/
|
|
char *get_cpu(void) {
|
|
#if defined(__linux__)
|
|
FILE *f = fopen("/proc/cpuinfo", "r");
|
|
if (!f)
|
|
return dup_or_unknown(NULL);
|
|
|
|
char line[512];
|
|
char model[256] = "";
|
|
int cores = 0;
|
|
|
|
while (fgets(line, sizeof line, f)) {
|
|
if (strncmp(line, "model name", 10) == 0) {
|
|
if (!*model) {
|
|
char *p = strchr(line, ':');
|
|
if (p) {
|
|
p += 2;
|
|
p[strcspn(p, "\n")] = 0;
|
|
strncpy(model, p, sizeof model - 1);
|
|
}
|
|
}
|
|
cores++;
|
|
}
|
|
}
|
|
fclose(f);
|
|
|
|
if (!*model)
|
|
return dup_or_unknown(NULL);
|
|
|
|
char buf[512];
|
|
snprintf(buf, sizeof buf, "%s (%d cores)", model, cores);
|
|
return strdup(buf);
|
|
#elif defined(__APPLE__) || defined(__FreeBSD__)
|
|
char model[256];
|
|
size_t len = sizeof(model);
|
|
|
|
if (sysctlbyname("hw.model", model, &len, NULL, 0) != 0)
|
|
strncpy(model, "unknown", sizeof model);
|
|
|
|
int cores = 0;
|
|
len = sizeof(cores);
|
|
sysctlbyname("hw.ncpu", &cores, &len, NULL, 0);
|
|
|
|
char buf[512];
|
|
snprintf(buf, sizeof buf, "%s (%d cores)", model, cores);
|
|
return strdup(buf);
|
|
#else
|
|
return dup_or_unknown("unknown cpu");
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* Entry point.
|
|
* Fetches all information, prints it, then frees memory.
|
|
*/
|
|
int main(void) {
|
|
char *user = get_user();
|
|
char *host = get_hostname();
|
|
char *shell = get_shell();
|
|
char *os = get_os();
|
|
char *kernel = get_kernel();
|
|
char *uptime = get_uptime();
|
|
char *cpu = get_cpu();
|
|
|
|
printf("\n"
|
|
"\033[1m\033[37mwelcome, %s\033[1;31m ♥\033[0m\n\n"
|
|
" \033[1;37m• \033[1;37mhost\033[0m %s\n"
|
|
" \033[1;33m• \033[1;37mdistro\033[0m %s\n"
|
|
" \033[1;32m• \033[1;37mshell\033[0m %s\n"
|
|
" \033[1;36m• \033[1;37mcpu\033[0m %s\n"
|
|
" \033[1;34m• \033[1;37mkernel\033[0m %s\n"
|
|
" \033[1;35m• \033[1;37muptime\033[0m %s\n\n",
|
|
user, host, os, shell, cpu, kernel, uptime);
|
|
|
|
/* Free all heap-allocated strings */
|
|
free(user);
|
|
free(host);
|
|
free(shell);
|
|
free(os);
|
|
free(kernel);
|
|
free(uptime);
|
|
free(cpu);
|
|
|
|
return 0;
|
|
}
|
|
|