mirror of
https://gitlab.com/Mr_Goldberg/goldberg_emulator
synced 2025-07-06 22:42:19 +08:00
Update Nemirtingas overlay to latest.
This commit is contained in:
593
overlay_experimental/System/System.cpp
Normal file
593
overlay_experimental/System/System.cpp
Normal file
@ -0,0 +1,593 @@
|
||||
/*
|
||||
* Copyright (C) Nemirtingas
|
||||
* This file is part of System.
|
||||
*
|
||||
* System is free software; you can redistribute it
|
||||
* and/or modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* System is distributed in the hope that it will be
|
||||
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with System; if not, see
|
||||
* <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "System.h"
|
||||
#include "Filesystem.h"
|
||||
#include "Encoding.hpp"
|
||||
#include "System_internals.h"
|
||||
|
||||
#if defined(SYSTEM_OS_WINDOWS)
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#define VC_EXTRALEAN
|
||||
#define NOMINMAX
|
||||
#include <Windows.h>
|
||||
#include <TlHelp32.h>
|
||||
#include <shellapi.h>
|
||||
#include <shlobj.h> // (shell32.lib) Infos about current user folders
|
||||
|
||||
inline bool handle_is_valid(HANDLE h)
|
||||
{
|
||||
return (h != (HANDLE)0 && h != (HANDLE)-1);
|
||||
}
|
||||
|
||||
#elif defined(SYSTEM_OS_LINUX) || defined(SYSTEM_OS_APPLE)
|
||||
#if defined(SYSTEM_OS_LINUX)
|
||||
#include <sys/sysinfo.h> // Get uptime (second resolution)
|
||||
#include <dirent.h>
|
||||
#else
|
||||
#include <sys/sysctl.h>
|
||||
#include <mach-o/dyld_images.h>
|
||||
#endif
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <pwd.h>
|
||||
#include <unistd.h>
|
||||
#include <dlfcn.h>
|
||||
|
||||
#else
|
||||
#error "unknown arch"
|
||||
#endif
|
||||
|
||||
#include <fstream>
|
||||
|
||||
namespace System {
|
||||
|
||||
std::chrono::microseconds GetUpTime()
|
||||
{
|
||||
return std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now() - GetBootTime());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace System {
|
||||
|
||||
#if defined(SYSTEM_OS_WINDOWS)
|
||||
|
||||
std::chrono::system_clock::time_point GetBootTime()
|
||||
{
|
||||
static std::chrono::system_clock::time_point boottime(std::chrono::system_clock::now() - std::chrono::milliseconds(GetTickCount64()));
|
||||
return boottime;
|
||||
}
|
||||
|
||||
std::vector<std::string> GetProcArgs()
|
||||
{
|
||||
std::vector<std::string> res;
|
||||
|
||||
LPWSTR* szArglist;
|
||||
int nArgs;
|
||||
|
||||
szArglist = CommandLineToArgvW(GetCommandLineW(), &nArgs);
|
||||
|
||||
res.reserve(nArgs);
|
||||
for (int i = 0; i < nArgs; ++i)
|
||||
{
|
||||
res.emplace_back(System::Encoding::WCharToUtf8(szArglist[i]));
|
||||
}
|
||||
|
||||
LocalFree(szArglist);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
std::string GetEnvVar(std::string const& var)
|
||||
{
|
||||
std::wstring wide(System::Encoding::Utf8ToWChar(var));
|
||||
std::wstring wVar;
|
||||
|
||||
DWORD size = GetEnvironmentVariableW(wide.c_str(), nullptr, 0);
|
||||
// Size can be 0, and the size includes the null char, so resize to size - 1
|
||||
if (size < 2)
|
||||
return std::string();
|
||||
|
||||
wVar.resize(size - 1);
|
||||
GetEnvironmentVariableW(wide.c_str(), &wVar[0], size);
|
||||
|
||||
return System::Encoding::WCharToUtf8(wVar);
|
||||
}
|
||||
|
||||
std::string GetUserdataPath()
|
||||
{
|
||||
WCHAR szPath[4096] = {};
|
||||
HRESULT hr = SHGetFolderPathW(NULL, CSIDL_APPDATA, NULL, 0, szPath);
|
||||
|
||||
if (FAILED(hr))
|
||||
return std::string();
|
||||
|
||||
return System::Encoding::WCharToUtf8(std::wstring(szPath));
|
||||
}
|
||||
|
||||
std::string GetExecutablePath()
|
||||
{
|
||||
std::string path;
|
||||
std::wstring wpath(4096, L'\0');
|
||||
|
||||
wpath.resize(GetModuleFileNameW(nullptr, &wpath[0], wpath.length()));
|
||||
return System::Encoding::WCharToUtf8(wpath);
|
||||
}
|
||||
|
||||
std::string GetModulePath()
|
||||
{
|
||||
std::string path;
|
||||
std::wstring wpath(4096, L'\0');
|
||||
HMODULE hModule;
|
||||
|
||||
if (GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT | GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (LPCWSTR)&GetModulePath, &hModule) != FALSE)
|
||||
{
|
||||
DWORD size = GetModuleFileNameW((HINSTANCE)hModule, &wpath[0], wpath.length());
|
||||
wpath.resize(size);
|
||||
}
|
||||
return System::Encoding::WCharToUtf8(wpath);
|
||||
}
|
||||
|
||||
std::vector<std::string> GetModules()
|
||||
{
|
||||
std::vector<std::string> paths;
|
||||
std::wstring wpath;
|
||||
DWORD size;
|
||||
HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, GetProcessId(GetCurrentProcess()));
|
||||
if (handle_is_valid(hSnap))
|
||||
{
|
||||
MODULEENTRY32W entry{};
|
||||
entry.dwSize = sizeof(entry);
|
||||
if (Module32FirstW(hSnap, &entry) != FALSE)
|
||||
{
|
||||
wpath.resize(4096);
|
||||
size = GetModuleFileNameW((HINSTANCE)entry.hModule, &wpath[0], wpath.length());
|
||||
wpath.resize(size);
|
||||
paths.emplace_back(System::Encoding::WCharToUtf8(wpath));
|
||||
|
||||
while (Module32NextW(hSnap, &entry) != FALSE)
|
||||
{
|
||||
wpath.resize(4096);
|
||||
size = GetModuleFileNameW((HINSTANCE)entry.hModule, &wpath[0], wpath.length());
|
||||
wpath.resize(size);
|
||||
paths.emplace_back(System::Encoding::WCharToUtf8(wpath));
|
||||
}
|
||||
}
|
||||
|
||||
CloseHandle(hSnap);
|
||||
}
|
||||
|
||||
return paths;
|
||||
}
|
||||
|
||||
#elif defined(SYSTEM_OS_LINUX) || defined(SYSTEM_OS_APPLE)
|
||||
#ifdef SYSTEM_OS_LINUX
|
||||
|
||||
std::chrono::system_clock::time_point GetBootTime()
|
||||
{
|
||||
static std::chrono::system_clock::time_point boottime(std::chrono::seconds(0));
|
||||
if (boottime == std::chrono::system_clock::time_point{})
|
||||
{
|
||||
std::ifstream uptime_file("/proc/uptime");
|
||||
|
||||
double uptime;
|
||||
if (uptime_file)
|
||||
{// Get uptime (millisecond resolution)
|
||||
uptime_file >> uptime;
|
||||
uptime_file.close();
|
||||
}
|
||||
else
|
||||
{// If we can't open /proc/uptime, fallback to sysinfo (second resolution)
|
||||
struct sysinfo infos;
|
||||
if (sysinfo(&infos) != 0)
|
||||
return boottime;
|
||||
|
||||
uptime = infos.uptime;
|
||||
}
|
||||
|
||||
std::chrono::system_clock::time_point now_tp = std::chrono::system_clock::now();
|
||||
std::chrono::system_clock::time_point uptime_tp(std::chrono::milliseconds(static_cast<uint64_t>(uptime * 1000)));
|
||||
|
||||
boottime = std::chrono::system_clock::time_point(now_tp - uptime_tp);
|
||||
}
|
||||
|
||||
return boottime;
|
||||
}
|
||||
|
||||
std::string GetExecutablePath()
|
||||
{
|
||||
std::string exec_path("./");
|
||||
|
||||
char link[2048] = {};
|
||||
if (readlink("/proc/self/exe", link, sizeof(link)) > 0)
|
||||
{
|
||||
exec_path = link;
|
||||
}
|
||||
|
||||
return exec_path;
|
||||
}
|
||||
|
||||
std::string GetModulePath()
|
||||
{
|
||||
std::string const self("/proc/self/map_files/");
|
||||
DIR* dir;
|
||||
struct dirent* dir_entry;
|
||||
std::string file_path;
|
||||
std::string res;
|
||||
uint64_t handle = (uint64_t)&GetModulePath;
|
||||
uint64_t low, high;
|
||||
char* tmp;
|
||||
|
||||
dir = opendir(self.c_str());
|
||||
if (dir != nullptr)
|
||||
{
|
||||
while ((dir_entry = readdir(dir)) != nullptr)
|
||||
{
|
||||
file_path = dir_entry->d_name;
|
||||
if (dir_entry->d_type != DT_LNK)
|
||||
{// Not a link
|
||||
continue;
|
||||
}
|
||||
|
||||
tmp = &file_path[0];
|
||||
low = strtoull(tmp, &tmp, 16);
|
||||
if ((tmp - file_path.c_str()) < file_path.length())
|
||||
{
|
||||
high = strtoull(tmp+1, nullptr, 16);
|
||||
if (low != 0 && high > low && low <= handle && handle <= high)
|
||||
{
|
||||
res = System::ExpandSymlink(self + file_path);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
std::vector<std::string> GetModules()
|
||||
{
|
||||
std::string const self("/proc/self/map_files/");
|
||||
std::vector<std::string> paths;
|
||||
|
||||
DIR* dir;
|
||||
struct dirent* dir_entry;
|
||||
std::string path;
|
||||
bool found;
|
||||
|
||||
dir = opendir(self.c_str());
|
||||
if (dir != nullptr)
|
||||
{
|
||||
while ((dir_entry = readdir(dir)) != nullptr)
|
||||
{
|
||||
if (dir_entry->d_type != DT_LNK)
|
||||
{// Not a link
|
||||
continue;
|
||||
}
|
||||
|
||||
found = false;
|
||||
path = System::ExpandSymlink(self + dir_entry->d_name);
|
||||
for (auto const& item : paths)
|
||||
{
|
||||
if (item == path)
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
paths.emplace_back(std::move(path));
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
}
|
||||
|
||||
return paths;
|
||||
}
|
||||
|
||||
std::vector<std::string> GetProcArgs()
|
||||
{
|
||||
std::vector<std::string> res;
|
||||
std::ifstream fcmdline("/proc/self/cmdline", std::ios::in | std::ios::binary);
|
||||
|
||||
if (fcmdline)
|
||||
{
|
||||
for (std::string line; std::getline(fcmdline, line, '\0');)
|
||||
{
|
||||
res.emplace_back(std::move(line));
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static int IsProcessTranslated()
|
||||
{
|
||||
int ret = 0;
|
||||
size_t size = sizeof(ret);
|
||||
|
||||
// Call the sysctl and if successful return the result
|
||||
if (sysctlbyname("sysctl.proc_translated", &ret, &size, NULL, 0) != -1)
|
||||
return ret;
|
||||
|
||||
// If "sysctl.proc_translated" is not present then must be native
|
||||
if (errno == ENOENT)
|
||||
return 0;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::chrono::system_clock::time_point GetBootTime()
|
||||
{
|
||||
static std::chrono::system_clock::time_point boottime{};
|
||||
if (boottime == std::chrono::system_clock::time_point{})
|
||||
{
|
||||
struct timeval boottime_tv;
|
||||
size_t len = sizeof(boottime_tv);
|
||||
int mib[2] = { CTL_KERN, KERN_BOOTTIME };
|
||||
if (sysctl(mib, sizeof(mib)/sizeof(*mib), &boottime_tv, &len, nullptr, 0) < 0)
|
||||
return boottime;
|
||||
|
||||
boottime = std::chrono::system_clock::time_point(
|
||||
std::chrono::seconds(boottime_tv.tv_sec) +
|
||||
std::chrono::microseconds(boottime_tv.tv_usec));
|
||||
}
|
||||
|
||||
return boottime;
|
||||
}
|
||||
|
||||
std::string GetExecutablePath()
|
||||
{
|
||||
std::string exec_path("./");
|
||||
|
||||
task_dyld_info dyld_info;
|
||||
task_t t;
|
||||
pid_t pid = getpid();
|
||||
task_for_pid(mach_task_self(), pid, &t);
|
||||
mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT;
|
||||
|
||||
if (task_info(t, TASK_DYLD_INFO, reinterpret_cast<task_info_t>(&dyld_info), &count) == KERN_SUCCESS)
|
||||
{
|
||||
dyld_all_image_infos *dyld_img_infos = reinterpret_cast<dyld_all_image_infos*>(dyld_info.all_image_info_addr);
|
||||
if (IsProcessTranslated() == 1)
|
||||
{
|
||||
for (int i = 0; i < dyld_img_infos->infoArrayCount; ++i)
|
||||
{
|
||||
exec_path = dyld_img_infos->infoArray[i].imageFilePath;
|
||||
if (strcasestr(exec_path.c_str(), "rosetta") != nullptr)
|
||||
continue;
|
||||
|
||||
// In case of a translated process (Rosetta maybe ?), the executable path is not the first entry.
|
||||
size_t pos;
|
||||
while ((pos = exec_path.find("/./")) != std::string::npos)
|
||||
{
|
||||
exec_path.replace(pos, 3, "/");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < dyld_img_infos->infoArrayCount; ++i)
|
||||
{
|
||||
// For now I don't know how to be sure to get the executable path
|
||||
// but looks like the 1st entry is the executable path
|
||||
exec_path = dyld_img_infos->infoArray[i].imageFilePath;
|
||||
size_t pos;
|
||||
while ((pos = exec_path.find("/./")) != std::string::npos)
|
||||
{
|
||||
exec_path.replace(pos, 3, "/");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return exec_path;
|
||||
}
|
||||
|
||||
// Workaround for MacOS, I don't know how to get module path from address.
|
||||
SYSTEM_EXPORT_API(SYSTEM_EXTERN_C, void, SYSTEM_MODE_EXPORT, SYSTEM_CALL_DEFAULT) GetModulePathPlaceholder() {}
|
||||
|
||||
std::string GetModulePath()
|
||||
{
|
||||
task_dyld_info dyld_info;
|
||||
task_t t;
|
||||
pid_t pid = getpid();
|
||||
task_for_pid(mach_task_self(), pid, &t);
|
||||
mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT;
|
||||
|
||||
if (task_info(t, TASK_DYLD_INFO, reinterpret_cast<task_info_t>(&dyld_info), &count) == KERN_SUCCESS)
|
||||
{
|
||||
dyld_all_image_infos* dyld_img_infos = reinterpret_cast<dyld_all_image_infos*>(dyld_info.all_image_info_addr);
|
||||
for (int i = 0; i < dyld_img_infos->infoArrayCount; ++i)
|
||||
{
|
||||
void* res = dlopen(dyld_img_infos->infoArray[i].imageFilePath, RTLD_NOW);
|
||||
if (res != nullptr)
|
||||
{
|
||||
void* placeholder = dlsym(res, "GetModulePathPlaceholder");
|
||||
dlclose(res);
|
||||
if(placeholder == (void*)&GetModulePathPlaceholder)
|
||||
{
|
||||
std::string res(dyld_img_infos->infoArray[i].imageFilePath);
|
||||
size_t pos;
|
||||
while((pos = res.find("/./")) != std::string::npos)
|
||||
{
|
||||
res.replace(pos, 3, "/");
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return std::string();
|
||||
}
|
||||
|
||||
std::vector<std::string> GetModules()
|
||||
{
|
||||
std::vector<std::string> paths;
|
||||
std::string path;
|
||||
size_t pos;
|
||||
task_dyld_info dyld_info;
|
||||
task_t t;
|
||||
pid_t pid = getpid();
|
||||
task_for_pid(mach_task_self(), pid, &t);
|
||||
mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT;
|
||||
|
||||
if (task_info(t, TASK_DYLD_INFO, reinterpret_cast<task_info_t>(&dyld_info), &count) == KERN_SUCCESS)
|
||||
{
|
||||
dyld_all_image_infos* dyld_img_infos = reinterpret_cast<dyld_all_image_infos*>(dyld_info.all_image_info_addr);
|
||||
for (int i = 0; i < dyld_img_infos->infoArrayCount; ++i)
|
||||
{
|
||||
path = dyld_img_infos->infoArray[i].imageFilePath;
|
||||
while ((pos = path.find("/./")) != std::string::npos)
|
||||
{
|
||||
path.replace(pos, 3, "/");
|
||||
}
|
||||
paths.emplace_back(std::move(path));
|
||||
}
|
||||
}
|
||||
|
||||
return paths;
|
||||
}
|
||||
|
||||
std::vector<std::string> GetProcArgs()
|
||||
{
|
||||
std::vector<std::string> res;
|
||||
int mib[3];
|
||||
int argmax;
|
||||
size_t size;
|
||||
int nargs;
|
||||
|
||||
mib[0] = CTL_KERN;
|
||||
mib[1] = KERN_ARGMAX;
|
||||
|
||||
size = sizeof(argmax);
|
||||
if (sysctl(mib, 2, &argmax, &size, NULL, 0) == -1)
|
||||
{
|
||||
return res;
|
||||
}
|
||||
|
||||
std::unique_ptr<char[]> procargs(new char[argmax]);
|
||||
if (procargs == nullptr)
|
||||
{
|
||||
return res;
|
||||
}
|
||||
|
||||
mib[0] = CTL_KERN;
|
||||
mib[1] = KERN_PROCARGS2;
|
||||
mib[2] = getpid();
|
||||
|
||||
size = (size_t)argmax;
|
||||
if (sysctl(mib, 3, procargs.get(), &size, NULL, 0) == -1)
|
||||
{
|
||||
return res;
|
||||
}
|
||||
|
||||
memcpy(&nargs, procargs.get(), sizeof(nargs));
|
||||
if (nargs <= 0)
|
||||
{
|
||||
return res;
|
||||
}
|
||||
|
||||
char* args_end = procargs.get() + size;
|
||||
char* arg_iterator = procargs.get() + sizeof(nargs);
|
||||
// Skip saved exec path
|
||||
while (*arg_iterator != '\0' && arg_iterator < args_end)
|
||||
{
|
||||
++arg_iterator;
|
||||
}
|
||||
// Skip trailing(s) '\0'
|
||||
while (*arg_iterator == '\0' && arg_iterator < args_end)
|
||||
{
|
||||
++arg_iterator;
|
||||
}
|
||||
|
||||
res.reserve(nargs);
|
||||
char* arg = arg_iterator;
|
||||
for (int i = 0; i < nargs && arg_iterator < args_end; ++arg_iterator)
|
||||
{
|
||||
if (*arg_iterator == '\0')
|
||||
{
|
||||
++i;
|
||||
res.emplace_back(arg);
|
||||
arg = arg_iterator + 1;
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
std::string GetUserdataPath()
|
||||
{
|
||||
std::string user_appdata_path;
|
||||
/*
|
||||
~/Library/Application Support/<application name>
|
||||
~/Library/Preferences/<application name>
|
||||
~/Library/<application name>/
|
||||
*/
|
||||
|
||||
struct passwd* user_entry = getpwuid(getuid());
|
||||
if (user_entry == nullptr || user_entry->pw_dir == nullptr)
|
||||
{
|
||||
char* env_var = getenv("HOME");
|
||||
if (env_var != nullptr)
|
||||
{
|
||||
user_appdata_path = env_var;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
user_appdata_path = user_entry->pw_dir;
|
||||
}
|
||||
|
||||
if (!user_appdata_path.empty())
|
||||
{
|
||||
#ifdef SYSTEM_OS_LINUX
|
||||
user_appdata_path = System::Filesystem::Join(user_appdata_path, ".config");
|
||||
#else
|
||||
user_appdata_path = System::Filesystem::Join(user_appdata_path, "Library", "Application Support");
|
||||
#endif
|
||||
}
|
||||
|
||||
return user_appdata_path;
|
||||
}
|
||||
|
||||
std::string GetEnvVar(std::string const& var)
|
||||
{
|
||||
char* env = getenv(var.c_str());
|
||||
if (env == nullptr)
|
||||
return std::string();
|
||||
|
||||
return env;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
}
|
Reference in New Issue
Block a user