Files
FEX-Emu--FEX/Source/Tools/FEXBash/FEXBash.cpp
T
Ryan Houdek 7ceadc6b5b Move config layers to the frontend
FEXCore has no need to understand how to load these layers. Which
requires json parsing.

Move these to the frontend which is already doing the configuration
layer setup and initialization tasks anyway.

Means FEXCore itself no longer needs to link to tiny-json which can be
left to the frontend.
2023-06-09 18:15:40 -07:00

112 lines
3.3 KiB
C++

/*
$info$
tags: Bin|FEXBash
desc: Launches bash under FEX and passes arguments via -c to it
$end_info$
*/
#include "ConfigDefines.h"
#include "Common/ArgumentLoader.h"
#include "Common/Config.h"
#include "Common/FEXServerClient.h"
#include <FEXCore/Config/Config.h>
#include <FEXCore/Utils/LogManager.h>
#include <FEXCore/fextl/string.h>
#include <filesystem>
#include <iterator>
#include <memory>
#include <stddef.h>
#include <string>
#include <unistd.h>
#include <vector>
int main(int argc, char **argv, char **const envp) {
FEXCore::Config::Initialize();
FEXCore::Config::AddLayer(FEX::Config::CreateGlobalMainLayer());
FEXCore::Config::AddLayer(FEX::Config::CreateMainLayer());
FEX::ArgLoader::LoadWithoutArguments(argc, argv);
FEXCore::Config::AddLayer(FEX::Config::CreateEnvironmentLayer(envp));
FEXCore::Config::Load();
// Reload the meta layer
FEXCore::Config::ReloadMetaLayer();
auto Args = FEX::ArgLoader::Get();
// Ensure FEXServer is setup before config options try to pull CONFIG_ROOTFS
if (!FEXServerClient::SetupClient(argv[0])) {
LogMan::Msg::EFmt("FEXServerClient: Failure to setup client");
return -1;
}
FEX_CONFIG_OPT(RootFSPath, ROOTFS);
std::vector<const char*> Argv;
fextl::string BinShPath = RootFSPath() + "/bin/sh";
fextl::string BinBashPath = RootFSPath() + "/bin/bash";
std::string FEXInterpreterPath = std::filesystem::path(argv[0]).parent_path().string() + "FEXInterpreter";
// Check if a local FEXInterpreter to FEXBash exists
// If it does then it takes priority over the installed one
if (!std::filesystem::exists(FEXInterpreterPath)) {
FEXInterpreterPath = FEXCore::Config::FindContainerPrefix() + FEXINTERPRETER_PATH;
}
const char *FEXArgs[] = {
FEXInterpreterPath.c_str(),
Args.empty() ? BinBashPath.c_str() : BinShPath.c_str(),
"-c",
};
// Remove -c argument if arguments are empty
// Lets us start an emulated bash instance
const size_t FEXArgsCount = std::size(FEXArgs) - (Args.empty() ? 1 : 0);
Argv.resize(Args.size() + FEXArgsCount);
// Pass in the FEXInterpreter arguments
for (size_t i = 0; i < FEXArgsCount; ++i) {
Argv[i] = FEXArgs[i];
}
// Bring in passed in arguments
for (size_t i = 0; i < Args.size(); ++i) {
Argv[i + FEXArgsCount] = Args[i].c_str();
}
// Set --norc when no arguments are passed so PS1 doesn't get overwritten
const char* NoRC = "--norc";
if (Args.empty()) {
Argv.emplace_back(NoRC);
}
Argv.emplace_back(nullptr);
// Prepend `FEXBash>` to PS1 to be less confusing about running under emulation
// In most cases PS1 isn't an environment variable, but instead a shell variable
// But in case the user has set the PS1 environment variable then still prepend
//
// To get the shell variables as an environment variable then you can do `PS1=$PS1 FEXBash`
std::vector<const char *> Envp{};
char *PS1Env{};
for (unsigned i = 0;; ++i) {
if (envp[i] == nullptr)
break;
if (strstr(envp[i], "PS1=") == envp[i]) {
PS1Env = envp[i];
}
else {
Envp.emplace_back(envp[i]);
}
}
std::string PS1 = "PS1=FEXBash-\\u@\\h:\\w> ";
if (PS1Env) {
PS1 += &PS1Env[strlen("PS1=")];
}
Envp.emplace_back(PS1.c_str());
Envp.emplace_back(nullptr);
return execve(Argv[0], const_cast<char *const*>(&Argv.at(0)), const_cast<char *const*>(&Envp[0]));
}