AuroraRuntime/Source/Process/AuPaths.cpp

339 lines
8.3 KiB
C++

/***
Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved.
File: AuPaths.cpp
Date: 2021-7-14
Author: Reece
***/
#include <Source/RuntimeInternal.hpp>
#include "AuPaths.hpp"
#if defined(AURORA_IS_POSIX_DERIVED)
#include <stdlib.h>
#include <unistd.h>
#define _USE_GET_CWD
#endif
#include <Source/IO/FS/FS.hpp>
namespace Aurora::Process
{
static AuString gCachedModule, gCachedPartialPath, gCachedFullPath;
static AuInitOnce gInitOnce;
void ForceReplacePathHack(const AuString &path)
{
static AuInitOnce gInitOnce2;
gInitOnce2.Call([&]()
{
AuResetMember(gCachedModule);
AuResetMember(gCachedPartialPath);
AuResetMember(gCachedFullPath, path);
AuResetMember(gInitOnce);
});
}
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 (!AuEndsWith(path, 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/<app name>
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<AuString> 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)
{
if (!AuThreading::InitOnceLocker::TryLock(&gInitOnce, true))
{
gInitOnce.Wait();
}
else
{
bool bFailed {};
try
{
char spitter;
if (gCachedFullPath.size() ||
GetModulePathSlow(gCachedFullPath, spitter))
{
auto indexA = gCachedFullPath.find_last_of(spitter);
if (indexA != AuString::npos)
{
gCachedModule = gCachedFullPath.substr(indexA + 1);
gCachedPartialPath = gCachedFullPath.substr(0, indexA);
if (!AuEndsWith(gCachedPartialPath, AuFS::kPathSplitter))
{
gCachedPartialPath += AuFS::kPathSplitter;
}
}
else
{
bFailed = true;
}
}
else
{
bFailed = true;
}
}
catch (...)
{
bFailed = true;
SysPushErrorCatch();
}
AuThreading::InitOnceLocker::Finish(&gInitOnce, bFailed);
}
if (gInitOnce.IsUninitialized())
{
return false;
}
module = &gCachedModule;
fullPath = &gCachedFullPath;
partialPath = &gCachedPartialPath;
return true;
}
AUKN_SYM AuOptional<AuROString> GetProcessName()
{
const AuString *pModule, *pPartial, *pFull;
try
{
if (GetModulePath(pModule, pPartial, pFull) &&
pModule &&
pModule->size())
{
return *pModule;
}
}
catch (...)
{
SysPushErrorCatch();
}
return {};
}
AUKN_SYM AuOptional<AuROString> 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<AuROString> GetProcessFullPath()
{
const AuString *pModule, *pPartial, *pFull;
try
{
if (GetModulePath(pModule, pPartial, pFull) &&
pFull &&
pFull->size())
{
return *pFull;
}
}
catch (...)
{
SysPushErrorCatch();
}
return {};
}
}