summaryrefslogtreecommitdiffstats
path: root/src/common/impl/FFPlatform_unix.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/common/impl/FFPlatform_unix.c')
-rw-r--r--src/common/impl/FFPlatform_unix.c321
1 files changed, 321 insertions, 0 deletions
diff --git a/src/common/impl/FFPlatform_unix.c b/src/common/impl/FFPlatform_unix.c
new file mode 100644
index 0000000..de69f6b
--- /dev/null
+++ b/src/common/impl/FFPlatform_unix.c
@@ -0,0 +1,321 @@
+#include "FFPlatform_private.h"
+#include "common/FFstrbuf.h"
+#include "common/arrutil.h"
+#include "common/strutil.h"
+#include "common/io.h"
+#include "fastfetch_config.h"
+
+#include <unistd.h>
+#include <pwd.h>
+#include <limits.h>
+#include <sys/utsname.h>
+#include <paths.h>
+
+#ifdef __APPLE__
+ #include <mach-o/dyld.h>
+ #include <sys/sysctl.h>
+#elif defined(__FreeBSD__) || defined(__NetBSD__)
+ #include <sys/sysctl.h>
+#elif defined(__OpenBSD__)
+ #include <sys/sysctl.h>
+ #include <sys/stat.h>
+ #include <kvm.h>
+ #include "common/path.h"
+#elif defined(__HAIKU__)
+ #include <image.h>
+ #include <OS.h>
+#endif
+
+static void getExePath(FFPlatform* platform) {
+ char exePath[PATH_MAX];
+#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(
+ (int[]) { CTL_KERN,
+ #ifdef __FreeBSD__
+ KERN_PROC,
+ KERN_PROC_PATHNAME,
+ (pid_t) platform->pid
+ #else
+ KERN_PROC_ARGS,
+ (pid_t) platform->pid,
+ 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)
+ {
+ exePathLen = strlen(arg0);
+ if (exePathLen < ARRAY_SIZE(exePath)) {
+ memcpy(exePath, arg0, exePathLen);
+ exePath[exePathLen] = '\0';
+ } else {
+ exePathLen = 0;
+ }
+ } 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;
+ }
+ }
+
+ 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, (pid_t) 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;
+ }
+ break;
+ }
+ }
+ if (i < 0) {
+ exePathLen = 0;
+ }
+ } 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
+ }
+ } else {
+ exePathLen = 0;
+ }
+ }
+ }
+ }
+ }
+ 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;
+ }
+ }
+#endif
+ if (exePathLen > 0) {
+ ffStrbufEnsureFree(&platform->exePath, PATH_MAX);
+ if (realpath(exePath, platform->exePath.chars)) {
+ ffStrbufRecalculateLength(&platform->exePath);
+ } else {
+ ffStrbufSetNS(&platform->exePath, (uint32_t) exePathLen, exePath);
+ }
+ }
+}
+
+static void platformPathAddEnv(FFlist* dirs, const char* env) {
+ const char* envValue = getenv(env);
+ if (!ffStrSet(envValue)) {
+ return;
+ }
+
+ FF_STRBUF_AUTO_DESTROY value = ffStrbufCreateA(64);
+ ffStrbufAppendS(&value, envValue);
+
+ uint32_t startIndex = 0;
+ while (startIndex < value.length) {
+ uint32_t colonIndex = ffStrbufNextIndexC(&value, startIndex, ':');
+ value.chars[colonIndex] = '\0';
+
+ if (!ffStrSet(value.chars + startIndex)) {
+ startIndex = colonIndex + 1;
+ continue;
+ }
+
+ ffPlatformPathAddAbsolute(dirs, value.chars + startIndex);
+
+ startIndex = colonIndex + 1;
+ }
+}
+
+static void getHomeDir(FFPlatform* platform, const struct passwd* pwd) {
+ const char* home = pwd ? pwd->pw_dir : getenv("HOME");
+ ffStrbufAppendS(&platform->homeDir, home);
+ ffStrbufEnsureEndsWithC(&platform->homeDir, '/');
+}
+
+static void getCacheDir(FFPlatform* platform) {
+ const char* cache = getenv("XDG_CACHE_HOME");
+ if (ffStrSet(cache)) {
+ ffStrbufAppendS(&platform->cacheDir, cache);
+ ffStrbufEnsureEndsWithC(&platform->cacheDir, '/');
+ } else {
+ ffStrbufAppend(&platform->cacheDir, &platform->homeDir);
+ ffStrbufAppendS(&platform->cacheDir, ".cache/");
+ }
+}
+
+static void getConfigDirs(FFPlatform* platform) {
+ // Always make sure `${XDG_CONFIG_HOME:-$HOME/.config}` is the first entry
+ platformPathAddEnv(&platform->configDirs, "XDG_CONFIG_HOME");
+ ffPlatformPathAddHome(&platform->configDirs, platform, ".config/");
+
+#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
+
+ ffPlatformPathAddHome(&platform->configDirs, platform, "");
+ platformPathAddEnv(&platform->configDirs, "XDG_CONFIG_DIRS");
+
+#if !defined(__APPLE__)
+ ffPlatformPathAddAbsolute(&platform->configDirs, FASTFETCH_TARGET_DIR_ETC "/xdg/");
+#endif
+
+ ffPlatformPathAddAbsolute(&platform->configDirs, FASTFETCH_TARGET_DIR_ETC "/");
+ ffPlatformPathAddAbsolute(&platform->configDirs, FASTFETCH_TARGET_DIR_INSTALL_SYSCONF "/");
+}
+
+static void getDataDirs(FFPlatform* platform) {
+ platformPathAddEnv(&platform->dataDirs, "XDG_DATA_HOME");
+ ffPlatformPathAddHome(&platform->dataDirs, platform, ".local/share/");
+
+ // Add ${currentExePath}/../share
+ if (platform->exePath.length > 0) {
+ FF_STRBUF_AUTO_DESTROY path = ffStrbufCreateCopy(&platform->exePath);
+ ffStrbufSubstrBeforeLastC(&path, '/');
+ ffStrbufSubstrBeforeLastC(&path, '/');
+ ffStrbufAppendS(&path, "/share");
+ ffPlatformPathAddAbsolute(&platform->dataDirs, path.chars);
+ }
+
+#ifdef __APPLE__
+ ffPlatformPathAddHome(&platform->dataDirs, platform, "Library/Application Support/");
+#endif
+
+ ffPlatformPathAddHome(&platform->dataDirs, platform, "");
+ platformPathAddEnv(&platform->dataDirs, "XDG_DATA_DIRS");
+#ifdef _PATH_LOCALBASE
+ ffPlatformPathAddAbsolute(&platform->dataDirs, _PATH_LOCALBASE "/share/");
+#endif
+ ffPlatformPathAddAbsolute(&platform->dataDirs, FASTFETCH_TARGET_DIR_USR "/local/share/");
+ ffPlatformPathAddAbsolute(&platform->dataDirs, FASTFETCH_TARGET_DIR_USR "/share/");
+}
+
+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);
+ } else {
+ ffStrbufSetS(&platform->userName, getenv("USER"));
+ }
+}
+
+static void getHostName(FFPlatform* platform, const struct utsname* uts) {
+ ffStrbufAppendS(&platform->hostName, uts->nodename);
+}
+
+static void getUserShell(FFPlatform* platform, const struct passwd* pwd) {
+ const char* shell = getenv("SHELL");
+ if (!ffStrSet(shell) && pwd) {
+ shell = pwd->pw_shell;
+ }
+
+ ffStrbufAppendS(&platform->userShell, shell);
+}
+
+static void getSysinfo(FFPlatformSysinfo* info, const struct utsname* uts) {
+ ffStrbufAppendS(&info->name, uts->sysname);
+ ffStrbufAppendS(&info->release, uts->release);
+ ffStrbufAppendS(&info->version, uts->version);
+#ifdef __HAIKU__
+ /* historical reason */
+ if (ffStrEquals(uts->machine, "BePC")) {
+ ffStrbufSetStatic(&info->architecture, "i386");
+ } else
+#endif
+ ffStrbufAppendS(&info->architecture, uts->machine);
+
+#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__OpenBSD__) || defined(__NetBSD__)
+ size_t length = sizeof(info->pageSize);
+ sysctl((int[]) { CTL_HW, HW_PAGESIZE }, 2, &info->pageSize, &length, NULL, 0);
+#else
+ info->pageSize = (uint32_t) sysconf(_SC_PAGESIZE);
+#endif
+}
+
+static void getCwd(FFPlatform* platform) {
+ char cwd[PATH_MAX];
+ if (getcwd(cwd, sizeof(cwd)) != NULL) {
+ ffStrbufSetS(&platform->cwd, cwd);
+ ffStrbufEnsureEndsWithC(&platform->cwd, '/');
+ }
+}
+
+void ffPlatformInitImpl(FFPlatform* platform) {
+ platform->pid = (uint32_t) getpid();
+ platform->uid = getuid();
+ struct passwd* pwd = getpwuid(platform->uid);
+
+ struct utsname uts;
+ if (uname(&uts) < 0) {
+ memset(&uts, 0, sizeof(uts));
+ }
+
+ getExePath(platform);
+ getCwd(platform);
+ getHomeDir(platform, pwd);
+ getCacheDir(platform);
+ getConfigDirs(platform);
+ getDataDirs(platform);
+
+ getUserName(platform, pwd);
+ getHostName(platform, &uts);
+ getUserShell(platform, pwd);
+
+ getSysinfo(&platform->sysinfo, &uts);
+}