Skip to content

Commit

Permalink
FEXInterpreter: Support portable installs
Browse files Browse the repository at this point in the history
  • Loading branch information
Sonicadvance1 committed Aug 29, 2024
1 parent 92ddc00 commit a6d589f
Show file tree
Hide file tree
Showing 4 changed files with 99 additions and 27 deletions.
14 changes: 14 additions & 0 deletions FEXCore/Scripts/config_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,20 @@ def print_man_environment_tail():
],
"''", True)

print_man_env_option(
"FEX_PORTABLE",
[
"Forces FEX in to a more portable installation",
"This does a couple of things:",
"- Skips executing binfmt_misc handlers through execve",
"- global config (typically installed in /usr/share/fex-emu/) disabled",
"- FEX_APP_CONFIG_LOCATION defaults to FEXInterpreter relative paths instead of {$HOME, $XDG_DATA_HOME}/.fex-emu/",
" - <FEXInterpreterPath>/fex-emu/"
"- FEX_APP_DATA_LOCATION defaults to FEXInterpreter relative path instead of {$HOME, $XDG_DATA_HOME}/.fex-emu/",
" - <FEXInterpreterPath>/fex-emu/"
],
"''", True)

def print_man_header():
header ='''.Dd {0}
.Dt FEX
Expand Down
51 changes: 33 additions & 18 deletions Source/Common/Config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -338,10 +338,13 @@ fextl::string RecoverGuestProgramFilename(fextl::string Program, bool ExecFDInte
}

ApplicationNames LoadConfig(fextl::unique_ptr<FEX::ArgLoader::ArgLoader> ArgsLoader, bool LoadProgramConfig, char** const envp,
bool ExecFDInterp, int ProgramFDFromEnv) {
FEX::Config::InitializeConfigs();
bool ExecFDInterp, int ProgramFDFromEnv, const PortableInformation* PortableInfo) {
const bool IsPortable = PortableInfo && PortableInfo->IsPortable;
FEX::Config::InitializeConfigs(PortableInfo);
FEXCore::Config::Initialize();
FEXCore::Config::AddLayer(CreateGlobalMainLayer());
if (!IsPortable) {
FEXCore::Config::AddLayer(CreateGlobalMainLayer());
}
FEXCore::Config::AddLayer(CreateMainLayer());

auto Args = ArgsLoader->Get();
Expand Down Expand Up @@ -391,15 +394,19 @@ ApplicationNames LoadConfig(fextl::unique_ptr<FEX::ArgLoader::ArgLoader> ArgsLoa
}
}

FEXCore::Config::AddLayer(CreateAppLayer(ProgramName, FEXCore::Config::LayerType::LAYER_GLOBAL_APP));
if (!IsPortable) {
FEXCore::Config::AddLayer(CreateAppLayer(ProgramName, FEXCore::Config::LayerType::LAYER_GLOBAL_APP));
}
FEXCore::Config::AddLayer(CreateAppLayer(ProgramName, FEXCore::Config::LayerType::LAYER_LOCAL_APP));

auto SteamID = getenv("SteamAppId");
if (SteamID) {
// If a SteamID exists then let's search for Steam application configs as well.
// We want to key off both the SteamAppId number /and/ the executable since we may not want to thunk all binaries.
fextl::string SteamAppName = fextl::fmt::format("Steam_{}_{}", SteamID, ProgramName);
FEXCore::Config::AddLayer(CreateAppLayer(SteamAppName, FEXCore::Config::LayerType::LAYER_GLOBAL_STEAM_APP));
if (!IsPortable) {
FEXCore::Config::AddLayer(CreateAppLayer(SteamAppName, FEXCore::Config::LayerType::LAYER_GLOBAL_STEAM_APP));
}
FEXCore::Config::AddLayer(CreateAppLayer(SteamAppName, FEXCore::Config::LayerType::LAYER_LOCAL_STEAM_APP));
}
}
Expand Down Expand Up @@ -473,12 +480,16 @@ const char* GetHomeDirectory() {
}
#endif

fextl::string GetDataDirectory() {
fextl::string DataDir {};
fextl::string GetDataDirectory(const PortableInformation* PortableInfo) {
const char* DataOverride = getenv("FEX_APP_DATA_LOCATION");

if (PortableInfo && PortableInfo->IsPortable && !DataOverride) {
return fextl::fmt::format("{}fex-emu/", PortableInfo->InterpreterPath);
}

fextl::string DataDir {};
const char* HomeDir = GetHomeDirectory();
const char* DataXDG = getenv("XDG_DATA_HOME");
const char* DataOverride = getenv("FEX_APP_DATA_LOCATION");
if (DataOverride) {
// Data override will override the complete directory
DataDir = DataOverride;
Expand All @@ -489,14 +500,18 @@ fextl::string GetDataDirectory() {
return DataDir;
}

fextl::string GetConfigDirectory(bool Global) {
fextl::string GetConfigDirectory(bool Global, const PortableInformation* PortableInfo) {
const char* ConfigOverride = getenv("FEX_APP_CONFIG_LOCATION");
if (PortableInfo && PortableInfo->IsPortable && (Global || (!Global && !ConfigOverride))) {
return fextl::fmt::format("{}fex-emu/", PortableInfo->InterpreterPath);
}

fextl::string ConfigDir;
if (Global) {
ConfigDir = GLOBAL_DATA_DIRECTORY;
} else {
const char* HomeDir = GetHomeDirectory();
const char* ConfigXDG = getenv("XDG_CONFIG_HOME");
const char* ConfigOverride = getenv("FEX_APP_CONFIG_LOCATION");
if (ConfigOverride) {
// Config override completely overrides the config directory
ConfigDir = ConfigOverride;
Expand All @@ -515,15 +530,15 @@ fextl::string GetConfigDirectory(bool Global) {
return ConfigDir;
}

fextl::string GetConfigFileLocation(bool Global) {
return GetConfigDirectory(Global) + "Config.json";
fextl::string GetConfigFileLocation(bool Global, const PortableInformation* PortableInfo) {
return GetConfigDirectory(Global, PortableInfo) + "Config.json";
}

void InitializeConfigs() {
FEXCore::Config::SetDataDirectory(GetDataDirectory());
FEXCore::Config::SetConfigDirectory(GetConfigDirectory(false), false);
FEXCore::Config::SetConfigDirectory(GetConfigDirectory(true), true);
FEXCore::Config::SetConfigFileLocation(GetConfigFileLocation(false), false);
FEXCore::Config::SetConfigFileLocation(GetConfigFileLocation(true), true);
void InitializeConfigs(const PortableInformation* PortableInfo) {
FEXCore::Config::SetDataDirectory(GetDataDirectory(PortableInfo));
FEXCore::Config::SetConfigDirectory(GetConfigDirectory(false, PortableInfo), false);
FEXCore::Config::SetConfigDirectory(GetConfigDirectory(true, PortableInfo), true);
FEXCore::Config::SetConfigFileLocation(GetConfigFileLocation(false, PortableInfo), false);
FEXCore::Config::SetConfigFileLocation(GetConfigFileLocation(true, PortableInfo), true);
}
} // namespace FEX::Config
15 changes: 10 additions & 5 deletions Source/Common/Config.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ struct ApplicationNames {
fextl::string ProgramName;
};

struct PortableInformation {
bool IsPortable;
std::string_view InterpreterPath;
};

/**
* @brief Loads the FEX and application configurations for the application that is getting ready to run.
*
Expand All @@ -40,15 +45,15 @@ struct ApplicationNames {
* @return The application name and path structure
*/
ApplicationNames LoadConfig(fextl::unique_ptr<FEX::ArgLoader::ArgLoader> ArgLoader, bool LoadProgramConfig, char** const envp,
bool ExecFDInterp, int ProgramFDFromEnv);
bool ExecFDInterp, int ProgramFDFromEnv, const PortableInformation* PortableInfo = nullptr);

const char* GetHomeDirectory();

fextl::string GetDataDirectory();
fextl::string GetConfigDirectory(bool Global);
fextl::string GetConfigFileLocation(bool Global);
fextl::string GetDataDirectory(const PortableInformation* PortableInfo = nullptr);
fextl::string GetConfigDirectory(bool Global, const PortableInformation* PortableInfo = nullptr);
fextl::string GetConfigFileLocation(bool Global, const PortableInformation* PortableInfo = nullptr);

void InitializeConfigs();
void InitializeConfigs(const PortableInformation* PortableInfo = nullptr);

/**
* @brief Loads the global FEX config
Expand Down
46 changes: 42 additions & 4 deletions Source/Tools/FEXLoader/FEXLoader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -194,15 +194,49 @@ void RootFSRedirect(fextl::string* Filename, const fextl::string& RootFS) {
}
}

FEX::Config::PortableInformation GetIsPortable() {
constexpr FEX::Config::PortableInformation BadResult {false, {}};
const char* PortableConfig = getenv("FEX_PORTABLE");
if (!PortableConfig) {
return BadResult;
}

uint32_t Value {};
std::string_view PortableView {PortableConfig};

if (std::from_chars(PortableView.data(), PortableView.data() + PortableView.size(), Value).ec != std::errc {} || Value == 0) {
return BadResult;
}

char SelfPath[PATH_MAX];
auto Result = readlink("/proc/self/exe", SelfPath, PATH_MAX);
if (Result == -1) {
return BadResult;
}

std::string_view SelfPathView {SelfPath, std::min<size_t>(PATH_MAX, Result)};

///< Extract the absolute path from the FEXInterpret path
return {true, SelfPathView.substr(0, SelfPathView.find_last_of("/") + 1)};
}

bool RanAsInterpreter(const char* Program) {
return ExecutedWithFD || FEXLOADER_AS_INTERPRETER;
}

static bool InterpreterInstalled {};

bool IsInterpreterInstalled() {
// The interpreter is installed if both the binfmt_misc handlers are available
// Or if we were originally executed with FD. Which means the interpreter is installed
return InterpreterInstalled;
}

return ExecutedWithFD || (access("/proc/sys/fs/binfmt_misc/FEX-x86", F_OK) == 0 && access("/proc/sys/fs/binfmt_misc/FEX-x86_64", F_OK) == 0);
void SetInterpreterInstalled(bool Portable) {
// The interpreter is installed if both the binfmt_misc handlers are available.
// Or if we were originally executed with FD. Which means the interpreter is installed.
// If portable is set then FEX ignores anything installed through binfmt_misc.
InterpreterInstalled =
!Portable &&
(ExecutedWithFD || (access("/proc/sys/fs/binfmt_misc/FEX-x86", F_OK) == 0 && access("/proc/sys/fs/binfmt_misc/FEX-x86_64", F_OK) == 0));
}

namespace FEX::TSO {
Expand Down Expand Up @@ -267,6 +301,10 @@ static int StealFEXFDFromEnv(const char* Env) {
int main(int argc, char** argv, char** const envp) {
auto SBRKPointer = FEXCore::Allocator::DisableSBRKAllocations();
FEXCore::Allocator::GLIBCScopedFault GLIBFaultScope;

const auto PortableInfo = GetIsPortable();
SetInterpreterInstalled(PortableInfo.IsPortable);

const bool IsInterpreter = RanAsInterpreter(argv[0]);

ExecutedWithFD = getauxval(AT_EXECFD) != 0;
Expand All @@ -280,7 +318,7 @@ int main(int argc, char** argv, char** const envp) {
argc, argv);
auto Args = ArgsLoader->Get();
auto ParsedArgs = ArgsLoader->GetParsedArgs();
auto Program = FEX::Config::LoadConfig(std::move(ArgsLoader), true, envp, ExecutedWithFD, FEXFD);
auto Program = FEX::Config::LoadConfig(std::move(ArgsLoader), true, envp, ExecutedWithFD, FEXFD, &PortableInfo);

if (Program.ProgramPath.empty() && FEXFD == -1) {
// Early exit if we weren't passed an argument
Expand Down

0 comments on commit a6d589f

Please sign in to comment.