/*** 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 !defined(AURORA_IS_MODERNNT_DERIVED) if (!AuTryResize(path, 0x4000)) { return false; } #endif #if defined(AURORA_IS_MODERNNT_DERIVED) std::wstring eh; if (!AuTryResize(eh, 0x4000)) { return false; } auto length = ::GetCurrentDirectoryW(eh.size(), eh.data()); if (length == 0) { return false; } path = AuMove(Locale::ConvertFromWChar(eh.data(), length)); try { if (!path.ends_with(AuFS::kPathSplitter)) { path += AuFS::kPathSplitter; } } catch (...) { SysPushErrorCatch(); return false; } 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())); // downsize shouldn't throw 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(const AuString *&module, const AuString *&partialPath, const AuString *&fullPath) { static AuString cachedModule, cachedPartialPath, cachedFullPath; static AuInitOnce gInitOnce; if (!AuThreading::InitOnceLocker::TryLock(&gInitOnce, true)) { gInitOnce.Wait(); } else { bool bFailed {}; try { char spitter; if (GetModulePathSlow(cachedFullPath, spitter)) { auto indexA = cachedFullPath.find_last_of(spitter); if (indexA != AuString::npos) { cachedModule = cachedFullPath.substr(indexA + 1); cachedPartialPath = cachedFullPath.substr(0, indexA); if (!cachedPartialPath.ends_with(AuFS::kPathSplitter)) { cachedPartialPath += AuFS::kPathSplitter; } } else { bFailed = true; } } else { bFailed = true; } } catch (...) { bFailed = true; SysPushErrorCatch(); } AuThreading::InitOnceLocker::Finish(&gInitOnce, bFailed); } if (gInitOnce.IsUninitialized()) { return false; } module = &cachedModule; fullPath = &cachedFullPath; partialPath = &cachedPartialPath; return true; } AUKN_SYM AuOptional GetProcessName() { const AuString *pModule, *pPartial, *pFull; try { if (GetModulePath(pModule, pPartial, pFull) && pModule && pModule->size()) { return *pModule; } } catch (...) { SysPushErrorCatch(); } return {}; } AUKN_SYM AuOptional GetProcessDirectory() { const AuString *pModule, *pPartial, *pFull; try { if (GetModulePath(pModule, pPartial, pFull) && pPartial && pPartial->size()) { return *pPartial; } { static AuString cwd; static AuInitOnce gInitOnce; gInitOnce.Call([]() { GetWorkingDirectory(cwd); }); if (cwd.size()) { return cwd; } } } catch (...) { SysPushErrorCatch(); } return {}; } AUKN_SYM AuOptional GetProcessFullPath() { const AuString *pModule, *pPartial, *pFull; try { if (GetModulePath(pModule, pPartial, pFull) && pFull && pFull->size()) { return *pFull; } } catch (...) { SysPushErrorCatch(); } return {}; } }