Browse Source

LibCore: Add Core::System::current_executable_path()

This is based on Jakt::File::current_executable_path() and all the other
sources I looked at to figure out the per-platform way to do this. My
goodness, every platform has its own bespoke way.
Andrew Kaster 2 years ago
parent
commit
bb831a27dd
2 changed files with 45 additions and 0 deletions
  1. 43 0
      Userland/Libraries/LibCore/System.cpp
  2. 2 0
      Userland/Libraries/LibCore/System.h

+ 43 - 0
Userland/Libraries/LibCore/System.cpp

@@ -44,11 +44,16 @@ static int memfd_create(char const* name, unsigned int flags)
 
 #if defined(AK_OS_MACOS)
 #    include <crt_externs.h>
+#    include <mach-o/dyld.h>
 #    include <sys/mman.h>
 #else
 extern char** environ;
 #endif
 
+#if defined(AK_OS_BSD_GENERIC)
+#    include <sys/sysctl.h>
+#endif
+
 #define HANDLE_SYSCALL_RETURN_VALUE(syscall_name, rc, success_value) \
     if ((rc) < 0) {                                                  \
         return Error::from_syscall(syscall_name##sv, rc);            \
@@ -1726,4 +1731,42 @@ char** environment()
 #endif
 }
 
+ErrorOr<String> current_executable_path()
+{
+    char path[4096] = {};
+#if defined(AK_OS_LINUX) || defined(AK_OS_ANDROID) || defined(AK_OS_SERENITY)
+    auto ret = ::readlink("/proc/self/exe", path, sizeof(path) - 1);
+    // Ignore error if it wasn't a symlink
+    if (ret == -1 && errno != EINVAL)
+        return Error::from_syscall("readlink"sv, -errno);
+#elif defined(AK_OS_DRAGONFLY)
+    return String::from_deprecated_string(TRY(readlink("/proc/curproc/file"sv)));
+#elif defined(AK_OS_SOLARIS)
+    return String::from_deprecated_string(TRY(readlink("/proc/self/path/a.out"sv)));
+#elif defined(AK_OS_FREEBSD)
+    int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 };
+    size_t len = sizeof(path);
+    if (sysctl(mib, 4, path, &len, nullptr, 0) < 0)
+        return Errno::from_syscall("sysctl"sv, -errno);
+#elif defined(AK_OS_NETBSD)
+    int mib[4] = { CTL_KERN, KERN_PROC_ARGS, -1, KERN_PROC_PATHNAME };
+    size_t len = sizeof(path);
+    if (sysctl(mib, 4, path, &len, nullptr, 0) < 0)
+        return Errno::from_syscall("sysctl"sv, -errno);
+#elif defined(AK_OS_MACOS)
+    u32 size = sizeof(path);
+    auto ret = _NSGetExecutablePath(path, &size);
+    if (ret != 0)
+        return Error::from_errno(ENAMETOOLONG);
+#elif defined(AK_OS_EMSCRIPTEN)
+    return Error::from_string_view("current_executable_path() unknown on this platform"sv);
+#else
+#    warning "Not sure how to get current_executable_path on this platform!"
+    // GetModuleFileName on Windows, unsure about OpenBSD.
+    return Error::from_string_view("current_executable_path unknown"sv);
+#endif
+    path[sizeof(path) - 1] = '\0';
+    return String::from_utf8({ path, strlen(path) });
+}
+
 }

+ 2 - 0
Userland/Libraries/LibCore/System.h

@@ -273,4 +273,6 @@ ErrorOr<String> resolve_executable_from_environment(StringView filename, int fla
 
 char** environment();
 
+ErrorOr<String> current_executable_path();
+
 }