/*** Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. File: AuPaths.cpp Date: 2021-7-14 Author: Reece ***/ #include #include "AuPaths.hpp" #if defined(AURORA_IS_POSIX_DERIVED) #include #include #define _USE_GET_CWD #endif #include namespace Aurora::Process { AUKN_SYM bool GetWorkingDirectory(AuString &path) { if (!AuTryResize(path, 0x4000)) { return false; } #if defined(AURORA_IS_MODERNNT_DERIVED) std::wstring eh; eh.resize(path.size()); auto length = ::GetCurrentDirectoryW(eh.size(), eh.data()); if (length == 0) { return false; } path = Locale::ConvertFromWChar(eh.data(), length); if (!path.ends_with(AuFS::kPathSplitter)) { path += AuFS::kPathSplitter; } return true; #elif defined(AURORA_PLATFORM_ANDROID) // TODO: consider pulling internal storage package directory return false; #elif defined(_USE_GET_CWD) if (getcwd(path.data(), path.size()) == nullptr) { return false; } path.resize(strlen(path.data())); if (!path.ends_with(AuFS::kPathSplitter)) { path += AuFS::kPathSplitter; } return true; #endif return false; } static bool GetModulePathSlow(AuString &path, char &splitter) { path.clear(); splitter = 0; // TODO: BSD alternative sysctl CTL_KERN KERN_PROC KERN_PROC_PATHNAME -1 #if defined(AURORA_IS_MODERNNT_DERIVED) wchar_t chungus[0x4000]; auto len = GetModuleFileNameW( NULL, chungus, AuArraySize(chungus) ); if (!len) { return false; } path = Aurora::Locale::ConvertFromWChar(chungus, len); splitter = '\\'; if (AuEndsWith(path, ".dll")) { path.clear(); return false; } return true; #elif defined(AURORA_IS_XNU_DERIVED) // TODO: _NSGetExecutablePath() or CFBundleCopyExecutableURL? // i think it would make sense to use NS over CoreFoundation // i think we're also going to have to use CF's libevent abstraction - runloop source // to pass through kqueue structures with the mach window message loop, like how // user32 has MsgWaitMultipleObjects. // t. windows and loonix dev // // TODO: if macos >= 10.12 || quarantine xattr // /Applications/ CFURLRef bundleURL = CFBundleCopyExecutableURL(CFBundleGetMainBundle()); CFStringRef pathRef = CFURLCopyFileSystemPath(bundleURL, kCFURLPOSIXPathStyle); auto utf16length = CFStringGetLength(pathRef); auto maxUtf8len = CFStringGetMaximumSizeForEncoding(utf16length, kCFStringEncodingUTF8); path.resize(maxUtf8len); if (!CFStringGetCString(pathRef, path.data(), maxUtf8len, kCFStringEncodingUTF8)) { path.clear(); return false; } path.resize(std::strlen(path.c_str())); splitter = '/'; #elif defined(AURORA_PLATFORM_ANDROID) // SELinux gets in the way in some instances // There is no reason we should be searching for the jre binary // I'm also not convined pulling a cached apk path is the best thing to do here // Why should we even bother pulling /system/bin/app_process? return false; #elif defined(AURORA_IS_POSIX_DERIVED) static const AuList procFsPaths = { "/proc/self/exe", "/proc/self/file", "/proc/curproc/exe", "/proc/curproc/file" }; if (!AuTryResize(path, 8192)) { return false; } for (const auto &name : procFsPaths) { ssize_t count; if ((count = readlink(name.c_str(), &path[0], path.size())) > 0) { if (!AuTryResize(path, count)) { return false; } splitter = '/'; return true; } } return false; #endif } static bool GetModulePath(AuString &module, AuString &partialPath, AuString &fullPath) { static AuString cachedModule, cachedPartialPath, cachedFullPath; static bool init = false; static AuThreadPrimitives::SpinLock spinlock; char spitter; AU_TRY_LOCK_GUARD_RET_DEF(spinlock); if (AuExchange(init, true)) { if (!cachedModule.size()) { return false; } goto returnCached; } if (!GetModulePathSlow(cachedFullPath, spitter)) { return false; } // this scope is required for c++20, bleh { auto indexA = cachedFullPath.find_last_of(spitter); if (indexA == AuString::npos) { return false; } cachedModule = cachedFullPath.substr(indexA + 1); cachedPartialPath = cachedFullPath.substr(0, indexA); } if (!cachedPartialPath.ends_with(AuFS::kPathSplitter)) { cachedPartialPath += AuFS::kPathSplitter; } returnCached: module = cachedModule; fullPath = cachedFullPath; partialPath = cachedPartialPath; return true; } AUKN_SYM bool GetProcName(AuString &executable) { AuString module, partial, full; try { if (!GetModulePath(module, partial, full)) { return false; } executable = module; } catch (...) { return false; } return true; } AUKN_SYM bool GetProcDirectory(AuString &path) { AuString module, partial, full; try { if (!GetModulePath(module, partial, full)) { return false; } path = partial; if (path.empty()) { return GetWorkingDirectory(path); } } catch (...) { return false; } return true; } AUKN_SYM bool GetProcFullPath(AuString &path) { AuString module, partial, full; try { if (!GetModulePath(module, partial, full)) { return false; } path = full; } catch (...) { return false; } return true; } }