Files
fastfetch/src/common/impl/FFPlatform_unix.c
T

321 lines
11 KiB
C
Raw Normal View History

2023-01-15 15:57:26 +01:00
#include "FFPlatform_private.h"
2026-01-06 09:48:38 +08:00
#include "common/FFstrbuf.h"
#include "common/arrayUtils.h"
2026-01-06 09:48:38 +08:00
#include "common/stringUtils.h"
#include "common/io.h"
2023-01-15 15:57:26 +01:00
#include "fastfetch_config.h"
#include <unistd.h>
#include <pwd.h>
2023-08-24 10:49:59 +08:00
#include <limits.h>
2023-01-15 15:57:26 +01:00
#include <sys/utsname.h>
#include <paths.h>
2023-01-15 15:57:26 +01:00
2023-08-24 10:49:59 +08:00
#ifdef __APPLE__
2026-03-29 09:28:49 +08:00
# include <mach-o/dyld.h>
# include <sys/sysctl.h>
#elif defined(__FreeBSD__) || defined(__NetBSD__)
2026-03-29 09:28:49 +08:00
# include <sys/sysctl.h>
#elif defined(__OpenBSD__)
2026-03-29 09:28:49 +08:00
# include <sys/sysctl.h>
# include <kvm.h>
# include "common/path.h"
2025-02-05 02:08:28 +01:00
#elif defined(__HAIKU__)
2026-03-29 09:28:49 +08:00
# include <image.h>
# include <OS.h>
2023-08-24 10:49:59 +08:00
#endif
2026-03-29 09:28:49 +08:00
static void getExePath(FFPlatform* platform) {
2025-12-12 09:03:18 +08:00
char exePath[PATH_MAX];
2026-03-29 09:28:49 +08:00
#if defined(__linux__) || defined(__GNU__)
ssize_t exePathLen = readlink("/proc/self/exe", exePath, sizeof(exePath) - 1);
if (exePathLen >= 0) {
exePath[exePathLen] = '\0';
}
#elif defined(__APPLE__)
uint32_t exePathLen = sizeof(exePath);
if (_NSGetExecutablePath(exePath, &exePathLen) == 0) {
exePathLen = (uint32_t) strlen(exePath);
} else {
exePathLen = 0;
}
#elif defined(__FreeBSD__) || defined(__NetBSD__)
size_t exePathLen = sizeof(exePath);
if (sysctl(
2026-03-30 10:27:23 +08:00
(int[]) { CTL_KERN,
2026-03-29 09:28:49 +08:00
# ifdef __FreeBSD__
KERN_PROC,
KERN_PROC_PATHNAME,
(pid_t) platform->pid
# else
KERN_PROC_ARGS,
2026-04-08 14:30:14 +08:00
(pid_t) platform->pid,
2026-03-29 09:28:49 +08:00
KERN_PROC_PATHNAME
# endif
},
4,
exePath,
&exePathLen,
NULL,
0) < 0)
exePathLen = 0;
else {
exePathLen--; // remove terminating NUL
}
#elif defined(__OpenBSD__)
// OpenBSD doesn't have a reliable way to get the executable path.
// Current implementation uses argv[0], which can be easily spoofed.
// See #2195
size_t exePathLen = 0;
kvm_t* kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, NULL);
if (kd) {
int kpCount;
struct kinfo_proc* kp = kvm_getprocs(kd, KERN_PROC_PID, (pid_t) platform->pid, sizeof(*kp), &kpCount);
if (kp && kpCount == 1) {
char** argv = kvm_getargv(kd, kp, 0);
if (argv && argv[0]) {
char* arg0 = argv[0];
if (arg0[0]) {
if (strchr(arg0, '/') != NULL) // likely a path (absolute or relative)
{
2026-03-29 09:28:49 +08:00
exePathLen = strlen(arg0);
if (exePathLen < ARRAY_SIZE(exePath)) {
memcpy(exePath, arg0, exePathLen);
exePath[exePathLen] = '\0';
} else {
exePathLen = 0;
}
2026-03-29 09:28:49 +08:00
} else {
FF_STRBUF_AUTO_DESTROY tmpPath = ffStrbufCreate();
if (ffFindExecutableInPath(arg0, &tmpPath) == NULL && tmpPath.length < ARRAY_SIZE(exePath)) {
memcpy(exePath, tmpPath.chars, tmpPath.length + 1);
exePathLen = tmpPath.length;
}
2026-03-29 09:28:49 +08:00
}
2026-03-29 09:28:49 +08:00
if (exePathLen > 0) {
struct stat st;
if (stat(exePath, &st) == 0 && S_ISREG(st.st_mode)) {
int cntp;
struct kinfo_file* kf = kvm_getfiles(kd, KERN_FILE_BYPID, platform->pid, sizeof(*kf), &cntp);
if (kf) {
int i;
for (i = 0; i < cntp; i++) {
if (kf[i].fd_fd == KERN_FILE_TEXT) {
// KERN_FILE_TEXT is the executable file, not a shared library, and should be unique in the list.
if (st.st_dev != (dev_t) kf[i].va_fsid || st.st_ino != (ino_t) kf[i].va_fileid) {
i = -1;
}
2026-03-29 09:28:49 +08:00
break;
}
}
2026-03-29 09:28:49 +08:00
if (i < 0) {
exePathLen = 0;
}
2026-03-29 09:28:49 +08:00
} else {
// If we can't get the list of open files, we can't verify that the file is actually the executable
// Assume it is
}
2026-03-29 09:28:49 +08:00
} else {
exePathLen = 0;
}
}
}
}
}
2026-03-29 09:28:49 +08:00
kvm_close(kd);
}
#elif defined(__sun)
ssize_t exePathLen = readlink("/proc/self/path/a.out", exePath, sizeof(exePath) - 1);
if (exePathLen >= 0) {
exePath[exePathLen] = '\0';
}
#elif defined(__HAIKU__)
size_t exePathLen = 0;
image_info info;
int32 cookie = 0;
while (get_next_image_info(B_CURRENT_TEAM, &cookie, &info) == B_OK) {
if (info.type == B_APP_IMAGE) {
exePathLen = strlcpy(exePath, info.name, sizeof(exePath));
break;
2025-02-05 02:08:28 +01:00
}
2026-03-29 09:28:49 +08:00
}
#endif
if (exePathLen > 0) {
2024-01-28 09:56:36 +08:00
ffStrbufEnsureFree(&platform->exePath, PATH_MAX);
2026-03-29 09:28:49 +08:00
if (realpath(exePath, platform->exePath.chars)) {
2024-01-28 09:56:36 +08:00
ffStrbufRecalculateLength(&platform->exePath);
2026-03-29 09:28:49 +08:00
} else {
2024-01-28 09:56:36 +08:00
ffStrbufSetNS(&platform->exePath, (uint32_t) exePathLen, exePath);
2026-03-29 09:28:49 +08:00
}
2024-01-28 09:56:36 +08:00
}
2023-08-24 14:20:48 +08:00
}
2026-03-29 09:28:49 +08:00
static void platformPathAddEnv(FFlist* dirs, const char* env) {
const char* envValue = getenv(env);
2026-03-29 09:28:49 +08:00
if (!ffStrSet(envValue)) {
return;
2026-03-29 09:28:49 +08:00
}
FF_STRBUF_AUTO_DESTROY value = ffStrbufCreateA(64);
ffStrbufAppendS(&value, envValue);
uint32_t startIndex = 0;
2026-03-29 09:28:49 +08:00
while (startIndex < value.length) {
uint32_t colonIndex = ffStrbufNextIndexC(&value, startIndex, ':');
value.chars[colonIndex] = '\0';
2026-03-29 09:28:49 +08:00
if (!ffStrSet(value.chars + startIndex)) {
startIndex = colonIndex + 1;
continue;
}
ffPlatformPathAddAbsolute(dirs, value.chars + startIndex);
startIndex = colonIndex + 1;
}
}
2026-03-29 09:28:49 +08:00
static void getHomeDir(FFPlatform* platform, const struct passwd* pwd) {
2023-02-21 10:02:04 +08:00
const char* home = pwd ? pwd->pw_dir : getenv("HOME");
2023-01-15 15:57:26 +01:00
ffStrbufAppendS(&platform->homeDir, home);
ffStrbufEnsureEndsWithC(&platform->homeDir, '/');
}
2026-03-29 09:28:49 +08:00
static void getCacheDir(FFPlatform* platform) {
2023-01-15 15:57:26 +01:00
const char* cache = getenv("XDG_CACHE_HOME");
2026-03-29 09:28:49 +08:00
if (ffStrSet(cache)) {
2023-01-15 15:57:26 +01:00
ffStrbufAppendS(&platform->cacheDir, cache);
ffStrbufEnsureEndsWithC(&platform->cacheDir, '/');
2026-03-29 09:28:49 +08:00
} else {
2023-01-15 15:57:26 +01:00
ffStrbufAppend(&platform->cacheDir, &platform->homeDir);
ffStrbufAppendS(&platform->cacheDir, ".cache/");
}
}
2026-03-29 09:28:49 +08:00
static void getConfigDirs(FFPlatform* platform) {
2025-01-08 09:44:45 +08:00
// Always make sure `${XDG_CONFIG_HOME:-$HOME/.config}` is the first entry
platformPathAddEnv(&platform->configDirs, "XDG_CONFIG_HOME");
2023-01-15 15:57:26 +01:00
ffPlatformPathAddHome(&platform->configDirs, platform, ".config/");
2026-03-29 09:28:49 +08:00
#if defined(__APPLE__)
ffPlatformPathAddHome(&platform->configDirs, platform, "Library/Preferences/");
ffPlatformPathAddHome(&platform->configDirs, platform, "Library/Application Support/");
#endif
#if defined(__HAIKU__)
ffPlatformPathAddHome(&platform->configDirs, platform, "config/settings/");
#endif
2023-01-15 15:57:26 +01:00
ffPlatformPathAddHome(&platform->configDirs, platform, "");
platformPathAddEnv(&platform->configDirs, "XDG_CONFIG_DIRS");
2023-01-15 15:57:26 +01:00
2026-03-29 09:28:49 +08:00
#if !defined(__APPLE__)
ffPlatformPathAddAbsolute(&platform->configDirs, FASTFETCH_TARGET_DIR_ETC "/xdg/");
#endif
2023-01-15 15:57:26 +01:00
2023-02-21 10:02:04 +08:00
ffPlatformPathAddAbsolute(&platform->configDirs, FASTFETCH_TARGET_DIR_ETC "/");
ffPlatformPathAddAbsolute(&platform->configDirs, FASTFETCH_TARGET_DIR_INSTALL_SYSCONF "/");
2023-01-15 15:57:26 +01:00
}
2026-03-29 09:28:49 +08:00
static void getDataDirs(FFPlatform* platform) {
platformPathAddEnv(&platform->dataDirs, "XDG_DATA_HOME");
2023-01-15 15:57:26 +01:00
ffPlatformPathAddHome(&platform->dataDirs, platform, ".local/share/");
2023-01-15 16:26:08 +01:00
2023-08-24 10:49:59 +08:00
// Add ${currentExePath}/../share
2026-03-29 09:28:49 +08:00
if (platform->exePath.length > 0) {
2023-08-24 14:20:48 +08:00
FF_STRBUF_AUTO_DESTROY path = ffStrbufCreateCopy(&platform->exePath);
ffStrbufSubstrBeforeLastC(&path, '/');
ffStrbufSubstrBeforeLastC(&path, '/');
ffStrbufAppendS(&path, "/share");
ffPlatformPathAddAbsolute(&platform->dataDirs, path.chars);
2023-08-24 10:49:59 +08:00
}
2026-03-29 09:28:49 +08:00
#ifdef __APPLE__
ffPlatformPathAddHome(&platform->dataDirs, platform, "Library/Application Support/");
#endif
2023-01-15 16:26:08 +01:00
2023-01-15 15:57:26 +01:00
ffPlatformPathAddHome(&platform->dataDirs, platform, "");
platformPathAddEnv(&platform->dataDirs, "XDG_DATA_DIRS");
#ifdef _PATH_LOCALBASE
ffPlatformPathAddAbsolute(&platform->dataDirs, _PATH_LOCALBASE "/share/");
#endif
2023-02-21 10:02:04 +08:00
ffPlatformPathAddAbsolute(&platform->dataDirs, FASTFETCH_TARGET_DIR_USR "/local/share/");
ffPlatformPathAddAbsolute(&platform->dataDirs, FASTFETCH_TARGET_DIR_USR "/share/");
2023-01-15 15:57:26 +01:00
}
2026-03-29 09:28:49 +08:00
static void getUserName(FFPlatform* platform, const struct passwd* pwd) {
if (pwd) {
ffStrbufSetS(&platform->userName, pwd->pw_name);
ffStrbufSetS(&platform->fullUserName, pwd->pw_gecos);
ffStrbufTrimSpace(&platform->fullUserName);
2026-03-29 09:28:49 +08:00
} else {
ffStrbufSetS(&platform->userName, getenv("USER"));
}
2023-01-15 15:57:26 +01:00
}
2026-03-29 09:28:49 +08:00
static void getHostName(FFPlatform* platform, const struct utsname* uts) {
ffStrbufAppendS(&platform->hostName, uts->nodename);
2023-01-15 15:57:26 +01:00
}
2026-03-29 09:28:49 +08:00
static void getUserShell(FFPlatform* platform, const struct passwd* pwd) {
2023-01-15 15:57:26 +01:00
const char* shell = getenv("SHELL");
2026-03-29 09:28:49 +08:00
if (!ffStrSet(shell) && pwd) {
2023-01-15 15:57:26 +01:00
shell = pwd->pw_shell;
2026-03-29 09:28:49 +08:00
}
2023-01-15 15:57:26 +01:00
ffStrbufAppendS(&platform->userShell, shell);
}
2026-03-29 09:28:49 +08:00
static void getSysinfo(FFPlatformSysinfo* info, const struct utsname* uts) {
2024-07-02 10:12:26 +08:00
ffStrbufAppendS(&info->name, uts->sysname);
ffStrbufAppendS(&info->release, uts->release);
ffStrbufAppendS(&info->version, uts->version);
2026-03-29 09:28:49 +08:00
#ifdef __HAIKU__
2025-02-05 02:08:28 +01:00
/* historical reason */
2026-03-29 09:28:49 +08:00
if (ffStrEquals(uts->machine, "BePC")) {
2025-02-05 10:15:41 +08:00
ffStrbufSetStatic(&info->architecture, "i386");
2026-03-29 09:28:49 +08:00
} else
#endif
ffStrbufAppendS(&info->architecture, uts->machine);
2024-07-02 10:12:26 +08:00
2026-03-29 09:28:49 +08:00
#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__OpenBSD__) || defined(__NetBSD__)
2024-07-02 10:12:26 +08:00
size_t length = sizeof(info->pageSize);
2026-03-30 10:27:23 +08:00
sysctl((int[]) { CTL_HW, HW_PAGESIZE }, 2, &info->pageSize, &length, NULL, 0);
2026-03-29 09:28:49 +08:00
#else
2024-07-02 10:12:26 +08:00
info->pageSize = (uint32_t) sysconf(_SC_PAGESIZE);
2026-03-29 09:28:49 +08:00
#endif
2023-11-08 00:02:00 +08:00
}
2026-03-29 09:28:49 +08:00
static void getCwd(FFPlatform* platform) {
char cwd[PATH_MAX];
2026-03-29 09:28:49 +08:00
if (getcwd(cwd, sizeof(cwd)) != NULL) {
ffStrbufSetS(&platform->cwd, cwd);
ffStrbufEnsureEndsWithC(&platform->cwd, '/');
}
}
2026-03-29 09:28:49 +08:00
void ffPlatformInitImpl(FFPlatform* platform) {
2026-02-12 13:56:55 +08:00
platform->pid = (uint32_t) getpid();
2026-02-10 20:25:50 +08:00
platform->uid = getuid();
struct passwd* pwd = getpwuid(platform->uid);
2023-01-15 15:57:26 +01:00
struct utsname uts;
2026-03-29 09:28:49 +08:00
if (uname(&uts) < 0) {
2023-01-15 15:57:26 +01:00
memset(&uts, 0, sizeof(uts));
2026-03-29 09:28:49 +08:00
}
2023-01-15 15:57:26 +01:00
2023-08-24 14:20:48 +08:00
getExePath(platform);
getCwd(platform);
2023-01-15 15:57:26 +01:00
getHomeDir(platform, pwd);
getCacheDir(platform);
getConfigDirs(platform);
getDataDirs(platform);
getUserName(platform, pwd);
getHostName(platform, &uts);
getUserShell(platform, pwd);
2024-07-02 10:12:26 +08:00
getSysinfo(&platform->sysinfo, &uts);
2023-01-15 15:57:26 +01:00
}