More C++ Refactors
This commit is contained in:
parent
58a6706cf9
commit
57503d6a31
@ -62,6 +62,13 @@ string(CONCAT COMPILE_FLAGS_SETUP
|
|||||||
# Skip RPath
|
# Skip RPath
|
||||||
"set(CMAKE_SKIP_BUILD_RPATH TRUE)"
|
"set(CMAKE_SKIP_BUILD_RPATH TRUE)"
|
||||||
)
|
)
|
||||||
|
if(BUILD_ARM_COMPONENTS)
|
||||||
|
string(CONCAT COMPILE_FLAGS_SETUP
|
||||||
|
"${COMPILE_FLAGS_SETUP}\n"
|
||||||
|
# Disable C++11 String ABI
|
||||||
|
"add_compile_definitions(_GLIBCXX_USE_CXX11_ABI=0)"
|
||||||
|
)
|
||||||
|
endif()
|
||||||
cmake_language(EVAL CODE "${COMPILE_FLAGS_SETUP}")
|
cmake_language(EVAL CODE "${COMPILE_FLAGS_SETUP}")
|
||||||
|
|
||||||
# Fast Math
|
# Fast Math
|
||||||
@ -79,7 +86,7 @@ if(CMAKE_C_COMPILER_ID STREQUAL "GNU")
|
|||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Buld Dependencies
|
# Build Dependencies
|
||||||
add_subdirectory(dependencies)
|
add_subdirectory(dependencies)
|
||||||
|
|
||||||
# Build libreborn
|
# Build libreborn
|
||||||
@ -117,10 +124,10 @@ endif()
|
|||||||
if(BUILD_ARM_COMPONENTS)
|
if(BUILD_ARM_COMPONENTS)
|
||||||
install(EXPORT sdk DESTINATION "${MCPI_SDK_DIR}" FILE "sdk-targets.cmake" EXPORT_LINK_INTERFACE_LIBRARIES)
|
install(EXPORT sdk DESTINATION "${MCPI_SDK_DIR}" FILE "sdk-targets.cmake" EXPORT_LINK_INTERFACE_LIBRARIES)
|
||||||
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/sdk.cmake"
|
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/sdk.cmake"
|
||||||
|
# Sanity Check
|
||||||
|
"${ARM_SANITY_CHECK}\n"
|
||||||
# Compile Flags
|
# Compile Flags
|
||||||
"${COMPILE_FLAGS_SETUP}\n"
|
"${COMPILE_FLAGS_SETUP}\n"
|
||||||
# Snaity Check
|
|
||||||
"${ARM_SANITY_CHECK}\n"
|
|
||||||
# Log
|
# Log
|
||||||
"message(STATUS \"Using Reborn SDK v${MCPI_VERSION}\")\n"
|
"message(STATUS \"Using Reborn SDK v${MCPI_VERSION}\")\n"
|
||||||
# Include Targets
|
# Include Targets
|
||||||
|
@ -2,12 +2,15 @@ project(launcher)
|
|||||||
|
|
||||||
# Launcher
|
# Launcher
|
||||||
add_executable(launcher
|
add_executable(launcher
|
||||||
src/bootstrap.cpp
|
src/bootstrap/bootstrap.cpp
|
||||||
src/patchelf.cpp
|
src/bootstrap/mods.cpp
|
||||||
src/util.cpp
|
src/bootstrap/assets.cpp
|
||||||
src/crash-report.cpp
|
src/bootstrap/patchelf.cpp
|
||||||
src/sdk.cpp
|
src/bootstrap/debug.cpp
|
||||||
src/mods.cpp
|
src/util/util.cpp
|
||||||
|
src/util/sdk.cpp
|
||||||
|
src/logger/logger.cpp
|
||||||
|
src/logger/crash-report.cpp
|
||||||
src/options/parser.cpp
|
src/options/parser.cpp
|
||||||
src/main.cpp
|
src/main.cpp
|
||||||
src/client/configuration.cpp
|
src/client/configuration.cpp
|
||||||
@ -19,6 +22,8 @@ target_link_libraries(launcher reborn-util LIB_LIEF trampoline-headers)
|
|||||||
# RPath
|
# RPath
|
||||||
set_target_properties(launcher PROPERTIES INSTALL_RPATH "$ORIGIN/lib/native")
|
set_target_properties(launcher PROPERTIES INSTALL_RPATH "$ORIGIN/lib/native")
|
||||||
target_link_options(launcher PRIVATE "LINKER:--disable-new-dtags")
|
target_link_options(launcher PRIVATE "LINKER:--disable-new-dtags")
|
||||||
|
# Files
|
||||||
|
target_compile_definitions(launcher PRIVATE _FILE_OFFSET_BITS=64)
|
||||||
|
|
||||||
# Install
|
# Install
|
||||||
install(TARGETS launcher DESTINATION "${MCPI_INSTALL_DIR}")
|
install(TARGETS launcher DESTINATION "${MCPI_INSTALL_DIR}")
|
||||||
|
@ -1,190 +0,0 @@
|
|||||||
#define _FILE_OFFSET_BITS 64
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include <libreborn/libreborn.h>
|
|
||||||
|
|
||||||
#include "util.h"
|
|
||||||
#include "bootstrap.h"
|
|
||||||
#include "patchelf.h"
|
|
||||||
|
|
||||||
#define MCPI_BINARY "minecraft-pi"
|
|
||||||
|
|
||||||
#define REQUIRED_PAGE_SIZE 4096
|
|
||||||
|
|
||||||
// Debug Information
|
|
||||||
static void run_debug_command(const char *const command[], const char *prefix) {
|
|
||||||
int status = 0;
|
|
||||||
char *output = run_command(command, &status, nullptr);
|
|
||||||
if (output != nullptr) {
|
|
||||||
// Remove Newline
|
|
||||||
size_t length = strlen(output);
|
|
||||||
if (length > 0 && output[length - 1] == '\n') {
|
|
||||||
output[length - 1] = '\0';
|
|
||||||
}
|
|
||||||
|
|
||||||
// Print
|
|
||||||
DEBUG("%s: %s", prefix, output);
|
|
||||||
free(output);
|
|
||||||
}
|
|
||||||
if (!is_exit_status_success(status)) {
|
|
||||||
ERR("Unable To Gather Debug Information");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
static void print_debug_information() {
|
|
||||||
// System Information
|
|
||||||
const char *const command[] = {"uname", "-a", nullptr};
|
|
||||||
run_debug_command(command, "System Information");
|
|
||||||
|
|
||||||
// Version
|
|
||||||
DEBUG("Reborn Version: v%s", MCPI_VERSION);
|
|
||||||
|
|
||||||
// Architecture
|
|
||||||
const char *arch =
|
|
||||||
#ifdef __x86_64__
|
|
||||||
"AMD64"
|
|
||||||
#elif defined(__aarch64__)
|
|
||||||
"ARM64"
|
|
||||||
#elif defined(__arm__)
|
|
||||||
"ARM32"
|
|
||||||
#else
|
|
||||||
"Unknown"
|
|
||||||
#endif
|
|
||||||
;
|
|
||||||
DEBUG("Reborn Target Architecture: %s", arch);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bootstrap
|
|
||||||
void bootstrap(const options_t &options) {
|
|
||||||
// Debug Information
|
|
||||||
print_debug_information();
|
|
||||||
|
|
||||||
// Check Page Size
|
|
||||||
long page_size = sysconf(_SC_PAGESIZE);
|
|
||||||
if (page_size != REQUIRED_PAGE_SIZE) {
|
|
||||||
CONDITIONAL_ERR(!options.skip_pagesize_check, "Invalid page size! A page size of %ld bytes is required, but the system size is %ld bytes.", (long) REQUIRED_PAGE_SIZE, page_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get Binary Directory
|
|
||||||
const std::string binary_directory = get_binary_directory();
|
|
||||||
DEBUG("Binary Directory: %s", binary_directory.c_str());
|
|
||||||
|
|
||||||
// Copy SDK
|
|
||||||
if (!reborn_is_server()) {
|
|
||||||
copy_sdk(binary_directory, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set MCPI_REBORN_ASSETS_PATH
|
|
||||||
{
|
|
||||||
std::string assets_path = safe_realpath("/proc/self/exe");
|
|
||||||
chop_last_component(assets_path);
|
|
||||||
assets_path += "/data";
|
|
||||||
set_and_print_env(_MCPI_REBORN_ASSETS_PATH_ENV, assets_path.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Resolve Binary Path & Set MCPI_DIRECTORY
|
|
||||||
std::string original_game_binary;
|
|
||||||
std::string game_binary;
|
|
||||||
{
|
|
||||||
// Log
|
|
||||||
DEBUG("Resolving File Paths...");
|
|
||||||
|
|
||||||
// Resolve Full Binary Path
|
|
||||||
const std::string full_path = binary_directory + ("/" MCPI_BINARY);
|
|
||||||
original_game_binary = safe_realpath(full_path);
|
|
||||||
const char *custom_binary = getenv(MCPI_BINARY_ENV);
|
|
||||||
if (custom_binary != nullptr) {
|
|
||||||
game_binary = safe_realpath(custom_binary);
|
|
||||||
} else {
|
|
||||||
game_binary = original_game_binary;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Configure Preloaded Objects
|
|
||||||
std::vector<std::string> mcpi_ld_preload;
|
|
||||||
{
|
|
||||||
// Log
|
|
||||||
DEBUG("Locating Mods...");
|
|
||||||
|
|
||||||
// ARM Components
|
|
||||||
mcpi_ld_preload = bootstrap_mods(binary_directory);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Configure Library Search Path
|
|
||||||
std::vector<std::string> mcpi_ld_path;
|
|
||||||
{
|
|
||||||
// Log
|
|
||||||
DEBUG("Setting Linker Search Paths...");
|
|
||||||
|
|
||||||
// Library Search Path For ARM Components
|
|
||||||
{
|
|
||||||
// Add ARM Library Directory
|
|
||||||
mcpi_ld_path.push_back("lib/arm");
|
|
||||||
|
|
||||||
// Add ARM Sysroot Libraries (Ensure Priority) (Ignore On Actual ARM System)
|
|
||||||
#ifdef MCPI_USE_PREBUILT_ARMHF_TOOLCHAIN
|
|
||||||
mcpi_ld_path.push_back("sysroot/lib");
|
|
||||||
mcpi_ld_path.push_back("sysroot/lib/arm-linux-gnueabihf");
|
|
||||||
mcpi_ld_path.push_back("sysroot/usr/lib");
|
|
||||||
mcpi_ld_path.push_back("sysroot/usr/lib/arm-linux-gnueabihf");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Fix Paths
|
|
||||||
for (std::string &path : mcpi_ld_path) {
|
|
||||||
path = binary_directory + '/' + path;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fix MCPI Dependencies
|
|
||||||
char new_mcpi_exe_path[] = MCPI_PATCHED_DIR "/XXXXXX";
|
|
||||||
{
|
|
||||||
// Log
|
|
||||||
DEBUG("Patching ELF...");
|
|
||||||
|
|
||||||
// Find Linker
|
|
||||||
std::string linker = "/lib/ld-linux-armhf.so.3";
|
|
||||||
#ifdef MCPI_USE_PREBUILT_ARMHF_TOOLCHAIN
|
|
||||||
// Use ARM Sysroot Linker
|
|
||||||
linker = binary_directory + "/sysroot" + linker;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Patch
|
|
||||||
patch_mcpi_elf_dependencies(game_binary, new_mcpi_exe_path, linker, mcpi_ld_path, mcpi_ld_preload);
|
|
||||||
|
|
||||||
// Verify
|
|
||||||
if (!starts_with(new_mcpi_exe_path, MCPI_PATCHED_DIR)) {
|
|
||||||
IMPOSSIBLE();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set MCPI_VANILLA_ASSETS_PATH
|
|
||||||
{
|
|
||||||
std::string assets_path = original_game_binary;
|
|
||||||
chop_last_component(assets_path);
|
|
||||||
assets_path += "/data";
|
|
||||||
set_and_print_env(_MCPI_VANILLA_ASSETS_PATH_ENV, assets_path.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start Game
|
|
||||||
INFO("Starting Game...");
|
|
||||||
|
|
||||||
// Arguments
|
|
||||||
std::vector<std::string> args;
|
|
||||||
// Use Extra If Needed
|
|
||||||
#ifdef MCPI_BUILD_RUNTIME
|
|
||||||
args.push_back("runtime");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Specify MCPI Binary
|
|
||||||
args.push_back(new_mcpi_exe_path);
|
|
||||||
|
|
||||||
// Run
|
|
||||||
const char *new_argv[args.size() + 1];
|
|
||||||
for (std::vector<std::string>::size_type i = 0; i < args.size(); i++) {
|
|
||||||
new_argv[i] = args[i].c_str();
|
|
||||||
}
|
|
||||||
new_argv[args.size()] = nullptr;
|
|
||||||
safe_execvpe(new_argv, environ);
|
|
||||||
}
|
|
@ -1,10 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "options/parser.h"
|
|
||||||
|
|
||||||
void bootstrap(const options_t &options);
|
|
||||||
void copy_sdk(const std::string &binary_directory, bool log_with_debug);
|
|
||||||
std::vector<std::string> bootstrap_mods(const std::string &binary_directory);
|
|
15
launcher/src/bootstrap/assets.cpp
Normal file
15
launcher/src/bootstrap/assets.cpp
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
#include <libreborn/libreborn.h>
|
||||||
|
|
||||||
|
#include "bootstrap.h"
|
||||||
|
#include "../util/util.h"
|
||||||
|
|
||||||
|
// Setup Asset Paths
|
||||||
|
static void setup_path(const char *env_name, std::string assets_path) {
|
||||||
|
chop_last_component(assets_path);
|
||||||
|
assets_path += "/data";
|
||||||
|
set_and_print_env(env_name, assets_path.c_str());
|
||||||
|
}
|
||||||
|
void bootstrap_assets(const std::string &original_game_binary) {
|
||||||
|
setup_path(_MCPI_REBORN_ASSETS_PATH_ENV, safe_realpath("/proc/self/exe"));
|
||||||
|
setup_path(_MCPI_VANILLA_ASSETS_PATH_ENV, original_game_binary);
|
||||||
|
}
|
75
launcher/src/bootstrap/bootstrap.cpp
Normal file
75
launcher/src/bootstrap/bootstrap.cpp
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <libreborn/libreborn.h>
|
||||||
|
|
||||||
|
#include "../util/util.h"
|
||||||
|
#include "bootstrap.h"
|
||||||
|
|
||||||
|
#define MCPI_BINARY "minecraft-pi"
|
||||||
|
|
||||||
|
#define REQUIRED_PAGE_SIZE 4096
|
||||||
|
|
||||||
|
// Bootstrap
|
||||||
|
void bootstrap(const options_t &options) {
|
||||||
|
// Debug Information
|
||||||
|
print_debug_information();
|
||||||
|
|
||||||
|
// Check Page Size
|
||||||
|
const long page_size = sysconf(_SC_PAGESIZE);
|
||||||
|
if (page_size != REQUIRED_PAGE_SIZE) {
|
||||||
|
CONDITIONAL_ERR(!options.skip_pagesize_check, "Invalid page size! A page size of %ld bytes is required, but the system size is %ld bytes.", (long) REQUIRED_PAGE_SIZE, page_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get Binary Directory
|
||||||
|
const std::string binary_directory = get_binary_directory();
|
||||||
|
DEBUG("Binary Directory: %s", binary_directory.c_str());
|
||||||
|
|
||||||
|
// Copy SDK
|
||||||
|
if (!reborn_is_server()) {
|
||||||
|
copy_sdk(binary_directory, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolve Binary Path
|
||||||
|
DEBUG("Resolving File Paths...");
|
||||||
|
std::string original_game_binary = binary_directory + ("/" MCPI_BINARY);
|
||||||
|
original_game_binary = safe_realpath(original_game_binary);
|
||||||
|
const char *custom_binary = getenv(MCPI_BINARY_ENV);
|
||||||
|
const std::string game_binary = custom_binary ? safe_realpath(custom_binary) : original_game_binary;
|
||||||
|
|
||||||
|
// Configure Preloaded Objects
|
||||||
|
DEBUG("Locating Mods...");
|
||||||
|
const std::vector<std::string> mcpi_ld_preload = bootstrap_mods(binary_directory);
|
||||||
|
|
||||||
|
// Configure Library Search Path
|
||||||
|
DEBUG("Setting Linker Search Paths...");
|
||||||
|
const std::vector<std::string> mcpi_ld_path = get_ld_path(binary_directory);
|
||||||
|
|
||||||
|
// Assets
|
||||||
|
DEBUG("Finding Assets...");
|
||||||
|
bootstrap_assets(original_game_binary);
|
||||||
|
|
||||||
|
// Patch Binary
|
||||||
|
char new_mcpi_exe_path[] = MCPI_PATCHED_DIR "/XXXXXX";
|
||||||
|
DEBUG("Patching ELF...");
|
||||||
|
patch_mcpi_elf_dependencies(game_binary, new_mcpi_exe_path, get_new_linker(binary_directory), mcpi_ld_path, mcpi_ld_preload);
|
||||||
|
|
||||||
|
// Start Game
|
||||||
|
INFO("Starting Game...");
|
||||||
|
|
||||||
|
// Arguments
|
||||||
|
const std::vector<std::string> args {
|
||||||
|
#ifdef MCPI_BUILD_RUNTIME
|
||||||
|
"runtime",
|
||||||
|
#endif
|
||||||
|
new_mcpi_exe_path
|
||||||
|
};
|
||||||
|
|
||||||
|
// Run
|
||||||
|
const char *new_argv[args.size() + 1];
|
||||||
|
for (std::vector<std::string>::size_type i = 0; i < args.size(); i++) {
|
||||||
|
new_argv[i] = args[i].c_str();
|
||||||
|
}
|
||||||
|
new_argv[args.size()] = nullptr;
|
||||||
|
safe_execvpe(new_argv, environ);
|
||||||
|
}
|
20
launcher/src/bootstrap/bootstrap.h
Normal file
20
launcher/src/bootstrap/bootstrap.h
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "../options/parser.h"
|
||||||
|
|
||||||
|
#define MCPI_PATCHED_DIR "/tmp/.minecraft-pi-patched"
|
||||||
|
|
||||||
|
void bootstrap(const options_t &options);
|
||||||
|
// Debugging
|
||||||
|
void print_debug_information();
|
||||||
|
// Mods
|
||||||
|
std::vector<std::string> bootstrap_mods(const std::string &binary_directory);
|
||||||
|
// Assets
|
||||||
|
void bootstrap_assets(const std::string &original_game_binary);
|
||||||
|
// ELF
|
||||||
|
std::string get_new_linker(const std::string &binary_directory);
|
||||||
|
std::vector<std::string> get_ld_path(const std::string &binary_directory);
|
||||||
|
void patch_mcpi_elf_dependencies(const std::string &original_path, char *new_path, const std::string &interpreter, const std::vector<std::string> &rpath, const std::vector<std::string> &mods);
|
43
launcher/src/bootstrap/debug.cpp
Normal file
43
launcher/src/bootstrap/debug.cpp
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
#include <libreborn/libreborn.h>
|
||||||
|
|
||||||
|
#include "bootstrap.h"
|
||||||
|
|
||||||
|
// Debug Information
|
||||||
|
static void run_debug_command(const char *const command[], const char *prefix) {
|
||||||
|
int status = 0;
|
||||||
|
const std::vector<unsigned char> *output = run_command(command, &status);
|
||||||
|
if (!is_exit_status_success(status)) {
|
||||||
|
ERR("Unable To Gather Debug Information");
|
||||||
|
}
|
||||||
|
std::string output_str = (const char *) output->data();
|
||||||
|
delete output;
|
||||||
|
// Trim
|
||||||
|
const std::string::size_type length = output_str.length();
|
||||||
|
if (length > 0 && output_str[length - 1] == '\n') {
|
||||||
|
output_str.pop_back();
|
||||||
|
}
|
||||||
|
// Print
|
||||||
|
DEBUG("%s: %s", prefix, output_str.c_str());
|
||||||
|
}
|
||||||
|
void print_debug_information() {
|
||||||
|
// System Information
|
||||||
|
constexpr const char *const command[] = {"uname", "-a", nullptr};
|
||||||
|
run_debug_command(command, "System Information");
|
||||||
|
|
||||||
|
// Version
|
||||||
|
DEBUG("Reborn Version: v%s", MCPI_VERSION);
|
||||||
|
|
||||||
|
// Architecture
|
||||||
|
const char *arch =
|
||||||
|
#ifdef __x86_64__
|
||||||
|
"AMD64"
|
||||||
|
#elif defined(__aarch64__)
|
||||||
|
"ARM64"
|
||||||
|
#elif defined(__arm__)
|
||||||
|
"ARM32"
|
||||||
|
#else
|
||||||
|
"Unknown"
|
||||||
|
#endif
|
||||||
|
;
|
||||||
|
DEBUG("Reborn Target Architecture: %s", arch);
|
||||||
|
}
|
@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
#include <libreborn/libreborn.h>
|
#include <libreborn/libreborn.h>
|
||||||
|
|
||||||
#include "patchelf.h"
|
#include "bootstrap.h"
|
||||||
|
|
||||||
// Duplicate MCPI Executable Into /tmp
|
// Duplicate MCPI Executable Into /tmp
|
||||||
static void duplicate_mcpi_executable(char *new_path) {
|
static void duplicate_mcpi_executable(char *new_path) {
|
||||||
@ -43,12 +43,10 @@ void patch_mcpi_elf_dependencies(const std::string &original_path, char *new_pat
|
|||||||
duplicate_mcpi_executable(new_path);
|
duplicate_mcpi_executable(new_path);
|
||||||
|
|
||||||
// Load Binary
|
// Load Binary
|
||||||
std::unique_ptr<LIEF::ELF::Binary> binary = LIEF::ELF::Parser::parse(original_path);
|
const std::unique_ptr<LIEF::ELF::Binary> binary = LIEF::ELF::Parser::parse(original_path);
|
||||||
|
|
||||||
// Set Interpreter
|
// Set Interpreter
|
||||||
if (!interpreter.empty()) {
|
|
||||||
binary->interpreter(interpreter);
|
binary->interpreter(interpreter);
|
||||||
}
|
|
||||||
|
|
||||||
// Remove Existing Needed Libraries
|
// Remove Existing Needed Libraries
|
||||||
std::vector<std::string> to_remove;
|
std::vector<std::string> to_remove;
|
||||||
@ -96,3 +94,34 @@ void patch_mcpi_elf_dependencies(const std::string &original_path, char *new_pat
|
|||||||
ERR("Unable To Set File Permissions: %s: %s", new_path, strerror(errno));
|
ERR("Unable To Set File Permissions: %s: %s", new_path, strerror(errno));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Linker
|
||||||
|
std::string get_new_linker(const std::string &binary_directory) {
|
||||||
|
std::string linker = "/lib/ld-linux-armhf.so.3";
|
||||||
|
#ifdef MCPI_USE_PREBUILT_ARMHF_TOOLCHAIN
|
||||||
|
linker = binary_directory + "/sysroot" + linker;
|
||||||
|
#else
|
||||||
|
(void) binary_directory;
|
||||||
|
#endif
|
||||||
|
return linker;
|
||||||
|
}
|
||||||
|
std::vector<std::string> get_ld_path(const std::string &binary_directory) {
|
||||||
|
std::vector<std::string> mcpi_ld_path = {
|
||||||
|
// ARM Sysroot
|
||||||
|
#ifdef MCPI_USE_PREBUILT_ARMHF_TOOLCHAIN
|
||||||
|
"sysroot/lib",
|
||||||
|
"sysroot/lib/arm-linux-gnueabihf",
|
||||||
|
"sysroot/usr/lib",
|
||||||
|
"sysroot/usr/lib/arm-linux-gnueabihf",
|
||||||
|
#endif
|
||||||
|
// Libraries
|
||||||
|
"lib/arm"
|
||||||
|
};
|
||||||
|
// Fix Paths
|
||||||
|
for (std::string &path : mcpi_ld_path) {
|
||||||
|
path.insert(0, 1, '/');
|
||||||
|
path.insert(0, binary_directory);
|
||||||
|
}
|
||||||
|
// Return
|
||||||
|
return mcpi_ld_path;
|
||||||
|
}
|
@ -7,11 +7,11 @@
|
|||||||
#define CACHE_VERSION 0
|
#define CACHE_VERSION 0
|
||||||
|
|
||||||
// Load Cache
|
// Load Cache
|
||||||
typedef struct {
|
struct launcher_cache {
|
||||||
std::string username;
|
std::string username;
|
||||||
std::string render_distance;
|
std::string render_distance;
|
||||||
std::unordered_map<std::string, bool> feature_flags;
|
std::unordered_map<std::string, bool> feature_flags;
|
||||||
} launcher_cache;
|
};
|
||||||
extern launcher_cache empty_cache;
|
extern launcher_cache empty_cache;
|
||||||
launcher_cache load_cache();
|
launcher_cache load_cache();
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
#include <libreborn/libreborn.h>
|
#include <libreborn/libreborn.h>
|
||||||
|
|
||||||
#include "../util.h"
|
#include "../util/util.h"
|
||||||
#include "configuration.h"
|
#include "configuration.h"
|
||||||
#include "cache.h"
|
#include "cache.h"
|
||||||
|
|
||||||
@ -83,18 +83,16 @@ static void run_command_and_set_env(const char *env_name, const char *command[])
|
|||||||
reborn_check_display();
|
reborn_check_display();
|
||||||
// Run
|
// Run
|
||||||
int return_code;
|
int return_code;
|
||||||
char *output = run_command(command, &return_code, nullptr);
|
const std::vector<unsigned char> *output = run_command(command, &return_code);
|
||||||
if (output != nullptr) {
|
std::string output_str = (const char *) output->data();
|
||||||
|
delete output;
|
||||||
// Trim
|
// Trim
|
||||||
const size_t length = strlen(output);
|
const std::string::size_type length = output_str.length();
|
||||||
if (output[length - 1] == '\n') {
|
if (length > 0 && output_str[length - 1] == '\n') {
|
||||||
output[length - 1] = '\0';
|
output_str.pop_back();
|
||||||
}
|
}
|
||||||
// Set
|
// Set
|
||||||
set_and_print_env(env_name, output);
|
set_and_print_env(env_name, output_str.c_str());
|
||||||
// Free
|
|
||||||
free(output);
|
|
||||||
}
|
|
||||||
// Check Return Code
|
// Check Return Code
|
||||||
if (!is_exit_status_success(return_code)) {
|
if (!is_exit_status_success(return_code)) {
|
||||||
// Launch Interrupted
|
// Launch Interrupted
|
||||||
@ -161,7 +159,7 @@ void configure_client(const options_t &options) {
|
|||||||
if (options.use_default) {
|
if (options.use_default) {
|
||||||
// Use Default Feature Flags
|
// Use Default Feature Flags
|
||||||
set_env_if_unset(MCPI_FEATURE_FLAGS_ENV, [&cache]() {
|
set_env_if_unset(MCPI_FEATURE_FLAGS_ENV, [&cache]() {
|
||||||
std::string feature_flags = "";
|
std::string feature_flags;
|
||||||
load_available_feature_flags([&feature_flags, &cache](const std::string &flag) {
|
load_available_feature_flags([&feature_flags, &cache](const std::string &flag) {
|
||||||
bool value;
|
bool value;
|
||||||
// Strip Default Value
|
// Strip Default Value
|
||||||
|
@ -1,259 +0,0 @@
|
|||||||
#include <unistd.h>
|
|
||||||
#include <cstring>
|
|
||||||
#include <cerrno>
|
|
||||||
#include <cstdlib>
|
|
||||||
#include <cstdio>
|
|
||||||
#include <cstdint>
|
|
||||||
#include <csignal>
|
|
||||||
#include <poll.h>
|
|
||||||
#include <sys/ioctl.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <sys/prctl.h>
|
|
||||||
#include <ctime>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#include <libreborn/libreborn.h>
|
|
||||||
|
|
||||||
#include "crash-report.h"
|
|
||||||
|
|
||||||
// Show Crash Report Dialog
|
|
||||||
#define DIALOG_TITLE "Crash Report"
|
|
||||||
#define CRASH_REPORT_DIALOG_WIDTH "640"
|
|
||||||
#define CRASH_REPORT_DIALOG_HEIGHT "480"
|
|
||||||
static void show_report(const char *log_filename) {
|
|
||||||
// Fork
|
|
||||||
pid_t pid = fork();
|
|
||||||
if (pid == 0) {
|
|
||||||
// Child
|
|
||||||
setsid();
|
|
||||||
ALLOC_CHECK(freopen("/dev/null", "w", stdout));
|
|
||||||
ALLOC_CHECK(freopen("/dev/null", "w", stderr));
|
|
||||||
ALLOC_CHECK(freopen("/dev/null", "r", stdin));
|
|
||||||
const char *command[] = {
|
|
||||||
"zenity",
|
|
||||||
"--title", DIALOG_TITLE,
|
|
||||||
"--name", MCPI_APP_ID,
|
|
||||||
"--width", CRASH_REPORT_DIALOG_WIDTH,
|
|
||||||
"--height", CRASH_REPORT_DIALOG_HEIGHT,
|
|
||||||
"--text-info",
|
|
||||||
"--text", MCPI_APP_TITLE " has crashed!\n\nNeed help? Consider asking on the <a href=\"" MCPI_DISCORD_INVITE "\">Discord server</a>! <i>If you believe this is a problem with " MCPI_APP_TITLE " itself, please upload this crash report to the #bugs Discord channel.</i>",
|
|
||||||
"--filename", log_filename,
|
|
||||||
"--no-wrap",
|
|
||||||
"--font", "Monospace",
|
|
||||||
"--save-filename", MCPI_VARIANT_NAME "-crash-report.log",
|
|
||||||
"--ok-label", "Exit",
|
|
||||||
NULL
|
|
||||||
};
|
|
||||||
safe_execvpe(command, (const char *const *) environ);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Exit Handler
|
|
||||||
static pid_t child_pid = -1;
|
|
||||||
static void exit_handler(__attribute__((unused)) int signal) {
|
|
||||||
// Murder
|
|
||||||
kill(child_pid, SIGTERM);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Log File
|
|
||||||
static std::string log_filename;
|
|
||||||
static int log_fd;
|
|
||||||
static void setup_log_file() {
|
|
||||||
// Get Log Directory
|
|
||||||
const std::string home = std::string(getenv(_MCPI_HOME_ENV)) + get_home_subdirectory_for_game_data();
|
|
||||||
ensure_directory(home.c_str());
|
|
||||||
const std::string logs = home + "/logs";
|
|
||||||
ensure_directory(logs.c_str());
|
|
||||||
|
|
||||||
// Get Timestamp
|
|
||||||
time_t raw_time;
|
|
||||||
time(&raw_time);
|
|
||||||
const tm *time_info = localtime(&raw_time);
|
|
||||||
char time[512];
|
|
||||||
strftime(time, 512, "%Y-%m-%d", time_info);
|
|
||||||
|
|
||||||
// Get Log Filename
|
|
||||||
std::string file;
|
|
||||||
int num = 1;
|
|
||||||
do {
|
|
||||||
file = std::string(time) + '-' + std::to_string(num) + ".log";
|
|
||||||
log_filename = logs + '/' + file;
|
|
||||||
num++;
|
|
||||||
} while (access(log_filename.c_str(), F_OK) != -1);
|
|
||||||
|
|
||||||
// Create latest.log Symlink
|
|
||||||
const std::string latest_log = logs + "/latest.log";
|
|
||||||
unlink(latest_log.c_str());
|
|
||||||
if (symlink(file.c_str(), latest_log.c_str()) != 0) {
|
|
||||||
WARN("Unable To Create Latest Log Symlink: %s", strerror(errno));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create File
|
|
||||||
log_fd = open(log_filename.c_str(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
|
|
||||||
if (log_fd == -1) {
|
|
||||||
ERR("Unable To Create Log File: %s", strerror(errno));
|
|
||||||
}
|
|
||||||
reborn_set_log(log_fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Setup
|
|
||||||
#define PIPE_READ 0
|
|
||||||
#define PIPE_WRITE 1
|
|
||||||
#define BUFFER_SIZE 1024
|
|
||||||
static void safe_write(int fd, const void *buf, size_t size) {
|
|
||||||
const ssize_t bytes_written = write(fd, buf, size);
|
|
||||||
if (bytes_written < 0) {
|
|
||||||
ERR("Unable To Write Data: %s", strerror(errno));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void setup_crash_report() {
|
|
||||||
// Setup Logging
|
|
||||||
setup_log_file();
|
|
||||||
|
|
||||||
// Store Output
|
|
||||||
int output_pipe[2];
|
|
||||||
safe_pipe2(output_pipe, 0);
|
|
||||||
int error_pipe[2];
|
|
||||||
safe_pipe2(error_pipe, 0);
|
|
||||||
int input_pipe[2];
|
|
||||||
safe_pipe2(input_pipe, 0);
|
|
||||||
|
|
||||||
// Fork
|
|
||||||
pid_t ret = fork();
|
|
||||||
if (ret == -1) {
|
|
||||||
ERR("Unable To Fork: %s", strerror(errno));
|
|
||||||
} else if (ret == 0) {
|
|
||||||
// Child Process
|
|
||||||
|
|
||||||
// Pipe stdio
|
|
||||||
dup2(output_pipe[PIPE_WRITE], STDOUT_FILENO);
|
|
||||||
close(output_pipe[PIPE_READ]);
|
|
||||||
close(output_pipe[PIPE_WRITE]);
|
|
||||||
dup2(error_pipe[PIPE_WRITE], STDERR_FILENO);
|
|
||||||
close(error_pipe[PIPE_READ]);
|
|
||||||
close(error_pipe[PIPE_WRITE]);
|
|
||||||
dup2(input_pipe[PIPE_READ], STDIN_FILENO);
|
|
||||||
close(input_pipe[PIPE_READ]);
|
|
||||||
close(input_pipe[PIPE_WRITE]);
|
|
||||||
|
|
||||||
// Create New Process Group
|
|
||||||
setpgid(0, 0);
|
|
||||||
|
|
||||||
// Kill Child If Parent Exits First
|
|
||||||
prctl(PR_SET_PDEATHSIG, SIGKILL);
|
|
||||||
|
|
||||||
// Continue Execution
|
|
||||||
} else {
|
|
||||||
// Install Signal Handlers
|
|
||||||
child_pid = ret;
|
|
||||||
struct sigaction act_sigint = {};
|
|
||||||
act_sigint.sa_flags = SA_RESTART;
|
|
||||||
act_sigint.sa_handler = &exit_handler;
|
|
||||||
sigaction(SIGINT, &act_sigint, nullptr);
|
|
||||||
struct sigaction act_sigterm = {};
|
|
||||||
act_sigterm.sa_flags = SA_RESTART;
|
|
||||||
act_sigterm.sa_handler = &exit_handler;
|
|
||||||
sigaction(SIGTERM, &act_sigterm, nullptr);
|
|
||||||
|
|
||||||
// Close Unneeded File Descriptors
|
|
||||||
close(output_pipe[PIPE_WRITE]);
|
|
||||||
close(error_pipe[PIPE_WRITE]);
|
|
||||||
close(input_pipe[PIPE_READ]);
|
|
||||||
|
|
||||||
// Set Debug Tag
|
|
||||||
reborn_debug_tag = "(Crash Reporter) ";
|
|
||||||
|
|
||||||
// Setup Polling
|
|
||||||
const int number_fds = 3;
|
|
||||||
pollfd poll_fds[number_fds];
|
|
||||||
poll_fds[0].fd = output_pipe[PIPE_READ];
|
|
||||||
poll_fds[1].fd = error_pipe[PIPE_READ];
|
|
||||||
poll_fds[2].fd = STDIN_FILENO;
|
|
||||||
for (pollfd &poll_fd : poll_fds) {
|
|
||||||
poll_fd.events = POLLIN;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Poll Data
|
|
||||||
int status;
|
|
||||||
while (waitpid(ret, &status, WNOHANG) != ret) {
|
|
||||||
const int poll_ret = poll(poll_fds, number_fds, -1);
|
|
||||||
if (poll_ret == -1) {
|
|
||||||
if (errno == EINTR) {
|
|
||||||
continue;
|
|
||||||
} else {
|
|
||||||
ERR("Unable To Poll Data: %s", strerror(errno));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle Data
|
|
||||||
for (pollfd &poll_fd : poll_fds) {
|
|
||||||
if (poll_fd.revents != 0) {
|
|
||||||
if (poll_fd.revents & POLLIN) {
|
|
||||||
char buf[BUFFER_SIZE];
|
|
||||||
if (poll_fd.fd == STDIN_FILENO) {
|
|
||||||
// Data Available From stdin
|
|
||||||
int bytes_available;
|
|
||||||
if (ioctl(fileno(stdin), FIONREAD, &bytes_available) == -1) {
|
|
||||||
bytes_available = 0;
|
|
||||||
}
|
|
||||||
// Read
|
|
||||||
const ssize_t bytes_read = read(poll_fd.fd, buf, BUFFER_SIZE);
|
|
||||||
if (bytes_read == -1) {
|
|
||||||
ERR("Unable To Read Input: %s", strerror(errno));
|
|
||||||
}
|
|
||||||
// Write To Child
|
|
||||||
safe_write(input_pipe[PIPE_WRITE], buf, bytes_read);
|
|
||||||
} else {
|
|
||||||
// Data Available From Child's stdout/stderr
|
|
||||||
const ssize_t bytes_read = read(poll_fd.fd, buf, BUFFER_SIZE);
|
|
||||||
if (bytes_read == -1) {
|
|
||||||
ERR("Unable To Read Log Data: %s", strerror(errno));
|
|
||||||
}
|
|
||||||
// Print To Terminal
|
|
||||||
safe_write(poll_fd.fd == output_pipe[PIPE_READ] ? STDOUT_FILENO : STDERR_FILENO, buf, bytes_read);
|
|
||||||
// Write To log
|
|
||||||
safe_write(reborn_get_log_fd(), buf, bytes_read);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// File Descriptor No Longer Accessible
|
|
||||||
poll_fd.events = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close Pipes
|
|
||||||
close(output_pipe[PIPE_READ]);
|
|
||||||
close(error_pipe[PIPE_READ]);
|
|
||||||
close(input_pipe[PIPE_WRITE]);
|
|
||||||
|
|
||||||
// Check If Is Crash
|
|
||||||
const bool is_crash = !is_exit_status_success(status);
|
|
||||||
|
|
||||||
// Log Exit Code To log If Crash
|
|
||||||
if (is_crash) {
|
|
||||||
// Create Exit Code Log Line
|
|
||||||
char *exit_status = nullptr;
|
|
||||||
get_exit_status_string(status, &exit_status);
|
|
||||||
const std::string exit_code_line = "[CRASH]: Terminated" + std::string(exit_status) + '\n';
|
|
||||||
free(exit_status);
|
|
||||||
|
|
||||||
// Print Exit Code Log Line
|
|
||||||
safe_write(STDERR_FILENO, exit_code_line.c_str(), strlen(exit_code_line.c_str()));
|
|
||||||
// Write Exit Code Log Line
|
|
||||||
safe_write(reborn_get_log_fd(), exit_code_line.c_str(), strlen(exit_code_line.c_str()));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close Log File
|
|
||||||
close(log_fd);
|
|
||||||
unsetenv(_MCPI_LOG_FD_ENV);
|
|
||||||
|
|
||||||
// Show Crash Log
|
|
||||||
if (is_crash && !reborn_is_headless()) {
|
|
||||||
show_report(log_filename.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Exit
|
|
||||||
exit(WIFEXITED(status) ? WEXITSTATUS(status) : EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,11 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void setup_crash_report();
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
35
launcher/src/logger/crash-report.cpp
Normal file
35
launcher/src/logger/crash-report.cpp
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
#include <libreborn/libreborn.h>
|
||||||
|
|
||||||
|
#include "logger.h"
|
||||||
|
|
||||||
|
// Show Crash Report Dialog
|
||||||
|
#define DIALOG_TITLE "Crash Report"
|
||||||
|
#define CRASH_REPORT_DIALOG_WIDTH "640"
|
||||||
|
#define CRASH_REPORT_DIALOG_HEIGHT "480"
|
||||||
|
void show_report(const char *log_filename) {
|
||||||
|
// Fork
|
||||||
|
pid_t pid = fork();
|
||||||
|
if (pid == 0) {
|
||||||
|
// Child
|
||||||
|
setsid();
|
||||||
|
ALLOC_CHECK(freopen("/dev/null", "w", stdout));
|
||||||
|
ALLOC_CHECK(freopen("/dev/null", "w", stderr));
|
||||||
|
ALLOC_CHECK(freopen("/dev/null", "r", stdin));
|
||||||
|
const char *command[] = {
|
||||||
|
"zenity",
|
||||||
|
"--title", DIALOG_TITLE,
|
||||||
|
"--name", MCPI_APP_ID,
|
||||||
|
"--width", CRASH_REPORT_DIALOG_WIDTH,
|
||||||
|
"--height", CRASH_REPORT_DIALOG_HEIGHT,
|
||||||
|
"--text-info",
|
||||||
|
"--text", MCPI_APP_TITLE " has crashed!\n\nNeed help? Consider asking on the <a href=\"" MCPI_DISCORD_INVITE "\">Discord server</a>! <i>If you believe this is a problem with " MCPI_APP_TITLE " itself, please upload this crash report to the #bugs Discord channel.</i>",
|
||||||
|
"--filename", log_filename,
|
||||||
|
"--no-wrap",
|
||||||
|
"--font", "Monospace",
|
||||||
|
"--save-filename", MCPI_VARIANT_NAME "-crash-report.log",
|
||||||
|
"--ok-label", "Exit",
|
||||||
|
nullptr
|
||||||
|
};
|
||||||
|
safe_execvpe(command, (const char *const *) environ);
|
||||||
|
}
|
||||||
|
}
|
142
launcher/src/logger/logger.cpp
Normal file
142
launcher/src/logger/logger.cpp
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
#include <unistd.h>
|
||||||
|
#include <cstring>
|
||||||
|
#include <cerrno>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <csignal>
|
||||||
|
#include <poll.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <ctime>
|
||||||
|
#include <string>
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
#include <libreborn/libreborn.h>
|
||||||
|
|
||||||
|
#include "logger.h"
|
||||||
|
|
||||||
|
// Exit Handler
|
||||||
|
static pid_t child_pid = -1;
|
||||||
|
static void exit_handler(__attribute__((unused)) int signal) {
|
||||||
|
// Murder
|
||||||
|
kill(child_pid, SIGTERM);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log File
|
||||||
|
static std::string log_filename;
|
||||||
|
static int log_fd;
|
||||||
|
static void setup_log_file() {
|
||||||
|
// Get Log Directory
|
||||||
|
const std::string home = std::string(getenv(_MCPI_HOME_ENV)) + get_home_subdirectory_for_game_data();
|
||||||
|
ensure_directory(home.c_str());
|
||||||
|
const std::string logs = home + "/logs";
|
||||||
|
ensure_directory(logs.c_str());
|
||||||
|
|
||||||
|
// Get Timestamp
|
||||||
|
time_t raw_time;
|
||||||
|
time(&raw_time);
|
||||||
|
const tm *time_info = localtime(&raw_time);
|
||||||
|
char time[512];
|
||||||
|
strftime(time, 512, "%Y-%m-%d", time_info);
|
||||||
|
|
||||||
|
// Get Log Filename
|
||||||
|
std::string file;
|
||||||
|
int num = 1;
|
||||||
|
do {
|
||||||
|
file = std::string(time) + '-' + std::to_string(num) + ".log";
|
||||||
|
log_filename = logs + '/' + file;
|
||||||
|
num++;
|
||||||
|
} while (access(log_filename.c_str(), F_OK) != -1);
|
||||||
|
|
||||||
|
// Create latest.log Symlink
|
||||||
|
const std::string latest_log = logs + "/latest.log";
|
||||||
|
unlink(latest_log.c_str());
|
||||||
|
if (symlink(file.c_str(), latest_log.c_str()) != 0) {
|
||||||
|
WARN("Unable To Create Latest Log Symlink: %s", strerror(errno));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create File
|
||||||
|
log_fd = open(log_filename.c_str(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
|
||||||
|
if (log_fd == -1) {
|
||||||
|
ERR("Unable To Create Log File: %s", strerror(errno));
|
||||||
|
}
|
||||||
|
reborn_set_log(log_fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup
|
||||||
|
void setup_logger() {
|
||||||
|
// Setup Logging
|
||||||
|
setup_log_file();
|
||||||
|
|
||||||
|
// Fork
|
||||||
|
std::optional<Process> child = fork_with_stdio();
|
||||||
|
if (!child) {
|
||||||
|
// Child Process
|
||||||
|
|
||||||
|
// Create New Process Group
|
||||||
|
setpgid(0, 0);
|
||||||
|
|
||||||
|
// Continue Execution
|
||||||
|
} else {
|
||||||
|
// Set Debug Tag
|
||||||
|
reborn_debug_tag = "(Logger) ";
|
||||||
|
DEBUG("Writing To: %s", log_filename.c_str());
|
||||||
|
|
||||||
|
// Install Signal Handlers
|
||||||
|
child_pid = child->pid;
|
||||||
|
struct sigaction act_sigint = {};
|
||||||
|
act_sigint.sa_flags = SA_RESTART;
|
||||||
|
act_sigint.sa_handler = &exit_handler;
|
||||||
|
sigaction(SIGINT, &act_sigint, nullptr);
|
||||||
|
struct sigaction act_sigterm = {};
|
||||||
|
act_sigterm.sa_flags = SA_RESTART;
|
||||||
|
act_sigterm.sa_handler = &exit_handler;
|
||||||
|
sigaction(SIGTERM, &act_sigterm, nullptr);
|
||||||
|
|
||||||
|
// Poll
|
||||||
|
poll_fds({child->fds[0], child->fds[1], STDIN_FILENO}, [&child](const int i, const size_t size, unsigned char *buf) {
|
||||||
|
if (i == 0 || i == 1) {
|
||||||
|
// stdout/stderr
|
||||||
|
|
||||||
|
// Print To Terminal
|
||||||
|
safe_write(i == 0 ? STDOUT_FILENO : STDERR_FILENO, buf, size);
|
||||||
|
// Write To log
|
||||||
|
safe_write(reborn_get_log_fd(), buf, size);
|
||||||
|
} else {
|
||||||
|
// stdin
|
||||||
|
|
||||||
|
// Write To Child
|
||||||
|
safe_write(child->fds[2], buf, size);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Get Exit Status
|
||||||
|
const int status = child->close();
|
||||||
|
const bool is_crash = !is_exit_status_success(status);
|
||||||
|
|
||||||
|
// Log Exit Code To log If Crash
|
||||||
|
if (is_crash) {
|
||||||
|
// Create Exit Code Log Line
|
||||||
|
const std::string exit_status = get_exit_status_string(status);
|
||||||
|
const std::string exit_code_line = "[CRASH]: Terminated" + exit_status + '\n';
|
||||||
|
|
||||||
|
// Print Exit Code Log Line
|
||||||
|
safe_write(STDERR_FILENO, exit_code_line.c_str(), exit_code_line.size());
|
||||||
|
// Write Exit Code Log Line
|
||||||
|
safe_write(reborn_get_log_fd(), exit_code_line.c_str(), exit_code_line.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close Log File
|
||||||
|
close(log_fd);
|
||||||
|
unsetenv(_MCPI_LOG_FD_ENV);
|
||||||
|
|
||||||
|
// Show Crash Log
|
||||||
|
if (is_crash && !reborn_is_headless()) {
|
||||||
|
show_report(log_filename.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exit
|
||||||
|
exit(WIFEXITED(status) ? WEXITSTATUS(status) : EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
}
|
4
launcher/src/logger/logger.h
Normal file
4
launcher/src/logger/logger.h
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
void setup_logger();
|
||||||
|
void show_report(const char *log_filename);
|
@ -1,11 +1,10 @@
|
|||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <libreborn/libreborn.h>
|
#include <libreborn/libreborn.h>
|
||||||
#include <sys/stat.h>
|
|
||||||
|
|
||||||
#include "bootstrap.h"
|
#include "bootstrap/bootstrap.h"
|
||||||
#include "options/parser.h"
|
#include "options/parser.h"
|
||||||
#include "crash-report.h"
|
#include "logger/logger.h"
|
||||||
#include "util.h"
|
#include "util/util.h"
|
||||||
#include "client/configuration.h"
|
#include "client/configuration.h"
|
||||||
|
|
||||||
// Bind Options To Environmental Variable
|
// Bind Options To Environmental Variable
|
||||||
@ -45,7 +44,8 @@ static void setup_environment(const options_t &options) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Setup MCPI_HOME
|
// Setup MCPI_HOME
|
||||||
if (const char *custom_profile_directory = getenv(MCPI_PROFILE_DIRECTORY_ENV); custom_profile_directory != nullptr) {
|
const char *custom_profile_directory = getenv(MCPI_PROFILE_DIRECTORY_ENV);
|
||||||
|
if (custom_profile_directory != nullptr) {
|
||||||
// Custom Directory
|
// Custom Directory
|
||||||
custom_profile_directory = realpath(custom_profile_directory, nullptr);
|
custom_profile_directory = realpath(custom_profile_directory, nullptr);
|
||||||
ALLOC_CHECK(custom_profile_directory);
|
ALLOC_CHECK(custom_profile_directory);
|
||||||
@ -86,8 +86,8 @@ static void start_game(const options_t &options) {
|
|||||||
setvbuf(stdout, nullptr, _IONBF, 0);
|
setvbuf(stdout, nullptr, _IONBF, 0);
|
||||||
|
|
||||||
// Setup Crash Reporting
|
// Setup Crash Reporting
|
||||||
if (!options.disable_crash_report) {
|
if (!options.disable_logger) {
|
||||||
setup_crash_report();
|
setup_logger();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configure Client Options
|
// Configure Client Options
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
OPTION(debug, "debug", 'd', "Enable Debug Logging")
|
OPTION(debug, "debug", 'd', "Enable Debug Logging")
|
||||||
OPTION(copy_sdk, "copy-sdk", -2, "Extract Modding SDK And Exit")
|
OPTION(copy_sdk, "copy-sdk", -2, "Extract Modding SDK And Exit")
|
||||||
OPTION(disable_crash_report, "disable-crash-report", -1, "Disable Crash Report Dialog")
|
OPTION(disable_logger, "disable-logger", -1, "Disable Logger (And Crash Report Dialog)")
|
||||||
OPTION(use_default, "default", -3, "Skip Client-Mode Configuration Dialogs")
|
OPTION(use_default, "default", -3, "Skip Client-Mode Configuration Dialogs")
|
||||||
OPTION(no_cache, "no-cache", -4, "Disable Client-Mode Configuration Cache")
|
OPTION(no_cache, "no-cache", -4, "Disable Client-Mode Configuration Cache")
|
||||||
OPTION(wipe_cache, "wipe-cache", -5, "Wipe Cached Client-Mode Configuration And Exit")
|
OPTION(wipe_cache, "wipe-cache", -5, "Wipe Cached Client-Mode Configuration And Exit")
|
||||||
|
@ -1,8 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#define MCPI_PATCHED_DIR "/tmp/.minecraft-pi-patched"
|
|
||||||
|
|
||||||
void patch_mcpi_elf_dependencies(const std::string &original_path, char *new_path, const std::string &interpreter, const std::vector<std::string> &rpath, const std::vector<std::string> &mods);
|
|
@ -1,6 +1,6 @@
|
|||||||
#include <libreborn/libreborn.h>
|
#include <libreborn/libreborn.h>
|
||||||
|
|
||||||
#include "bootstrap.h"
|
#include "../bootstrap/bootstrap.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
// Log
|
// Log
|
@ -5,10 +5,8 @@
|
|||||||
// Simpler Version Of run_command()
|
// Simpler Version Of run_command()
|
||||||
void run_simple_command(const char *const command[], const char *error) {
|
void run_simple_command(const char *const command[], const char *error) {
|
||||||
int status = 0;
|
int status = 0;
|
||||||
char *output = run_command(command, &status, nullptr);
|
const std::vector<unsigned char> *output = run_command(command, &status);
|
||||||
if (output != nullptr) {
|
delete output;
|
||||||
free(output);
|
|
||||||
}
|
|
||||||
if (!is_exit_status_success(status)) {
|
if (!is_exit_status_success(status)) {
|
||||||
ERR("%s", error);
|
ERR("%s", error);
|
||||||
}
|
}
|
@ -7,3 +7,5 @@ void run_simple_command(const char *const command[], const char *error);
|
|||||||
void chop_last_component(std::string &str);
|
void chop_last_component(std::string &str);
|
||||||
std::string safe_realpath(const std::string &path);
|
std::string safe_realpath(const std::string &path);
|
||||||
std::string get_binary_directory();
|
std::string get_binary_directory();
|
||||||
|
|
||||||
|
void copy_sdk(const std::string &binary_directory, bool log_with_debug);
|
@ -6,12 +6,12 @@ configure_file(include/libreborn/config.h.in "${CMAKE_CURRENT_BINARY_DIR}/includ
|
|||||||
|
|
||||||
# Util
|
# Util
|
||||||
add_library(reborn-util SHARED
|
add_library(reborn-util SHARED
|
||||||
src/util/exec.c
|
src/util/exec.cpp
|
||||||
src/util/string.c
|
src/util/string.cpp
|
||||||
src/util/util.c
|
src/util/util.cpp
|
||||||
src/util/log.c
|
src/util/log.cpp
|
||||||
src/util/cp437.cpp
|
src/util/cp437.cpp
|
||||||
src/util/env.c
|
src/util/env.cpp
|
||||||
)
|
)
|
||||||
target_include_directories(
|
target_include_directories(
|
||||||
reborn-util
|
reborn-util
|
||||||
|
@ -1,16 +1,11 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define ENV(name, ...) extern const char *const name##_ENV;
|
#define ENV(name, ...) extern const char *const name##_ENV;
|
||||||
#include "env-list.h"
|
#include "env-list.h"
|
||||||
#undef ENV
|
#undef ENV
|
||||||
|
|
||||||
int is_env_var_internal(const char *env);
|
bool is_env_var_internal(const char *env);
|
||||||
void clear_internal_env_vars();
|
void clear_internal_env_vars();
|
||||||
|
|
||||||
#ifdef __cplusplus
|
// Set Environmental Variable
|
||||||
}
|
void set_and_print_env(const char *name, const char *value);
|
||||||
#endif
|
|
@ -1,23 +1,21 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <unistd.h>
|
#include <string>
|
||||||
#include <stdint.h>
|
#include <optional>
|
||||||
#include <errno.h>
|
#include <array>
|
||||||
#include <string.h>
|
#include <vector>
|
||||||
#include <sys/wait.h>
|
#include <functional>
|
||||||
#include <fcntl.h>
|
|
||||||
#include <signal.h>
|
|
||||||
|
|
||||||
#include "log.h"
|
// fork() With I/O
|
||||||
#include "string.h"
|
struct Process {
|
||||||
#include "util.h"
|
static constexpr int fd_count = 3;
|
||||||
|
Process(pid_t pid_, std::array<int, fd_count> fds_);
|
||||||
#ifdef __cplusplus
|
[[nodiscard]] int close() const;
|
||||||
extern "C" {
|
const pid_t pid;
|
||||||
#endif
|
const std::array<int, fd_count> fds;
|
||||||
|
};
|
||||||
// Set Environmental Variable
|
std::optional<Process> fork_with_stdio();
|
||||||
void set_and_print_env(const char *name, const char *value);
|
void poll_fds(const std::vector<int> &fds, const std::function<void(int, size_t, unsigned char *)> &on_data);
|
||||||
|
|
||||||
// Safe execvpe()
|
// Safe execvpe()
|
||||||
__attribute__((noreturn)) void safe_execvpe(const char *const argv[], const char *const envp[]);
|
__attribute__((noreturn)) void safe_execvpe(const char *const argv[], const char *const envp[]);
|
||||||
@ -26,12 +24,8 @@ __attribute__((noreturn)) void safe_execvpe(const char *const argv[], const char
|
|||||||
#define CHILD_PROCESS_TAG "(Child Process) "
|
#define CHILD_PROCESS_TAG "(Child Process) "
|
||||||
|
|
||||||
// Run Command And Get Output
|
// Run Command And Get Output
|
||||||
char *run_command(const char *const command[], int *exit_status, size_t *output_size);
|
std::vector<unsigned char> *run_command(const char *const command[], int *exit_status);
|
||||||
#define is_exit_status_success(status) (WIFEXITED(status) && WEXITSTATUS(status) == 0)
|
#define is_exit_status_success(status) (WIFEXITED(status) && WEXITSTATUS(status) == 0)
|
||||||
|
|
||||||
// Get Exit Status String
|
// Get Exit Status String
|
||||||
void get_exit_status_string(int status, char **out);
|
std::string get_exit_status_string(int status);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
@ -1,11 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <cstdio>
|
||||||
#include <stdlib.h>
|
#include <cstdlib>
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Log File
|
// Log File
|
||||||
int reborn_get_log_fd();
|
int reborn_get_log_fd();
|
||||||
@ -29,7 +25,3 @@ int reborn_get_debug_fd();
|
|||||||
WARN(__VA_ARGS__); \
|
WARN(__VA_ARGS__); \
|
||||||
} \
|
} \
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
// Patching Functions
|
|
||||||
|
|
||||||
#if defined(REBORN_HAS_PATCH_CODE) && defined(__cplusplus)
|
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
// Patching Functions
|
||||||
|
#if defined(REBORN_HAS_PATCH_CODE)
|
||||||
|
|
||||||
// Init
|
// Init
|
||||||
void reborn_init_patch();
|
void reborn_init_patch();
|
||||||
|
|
||||||
|
@ -1,19 +1,10 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#include <string>
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Sanitize String
|
// Sanitize String
|
||||||
void sanitize_string(char *str, int max_length, int allow_newlines);
|
void sanitize_string(std::string &str, int max_length, bool allow_newlines);
|
||||||
|
|
||||||
// CP437
|
// CP437
|
||||||
char *to_cp437(const char *input);
|
std::string to_cp437(const std::string &input);
|
||||||
char *from_cp437(const char *input);
|
std::string from_cp437(const std::string &input);
|
||||||
|
|
||||||
// Starts With
|
|
||||||
int starts_with(const char *str, const char *prefix);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
@ -1,16 +1,17 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <string.h>
|
#include <cstring>
|
||||||
#include <errno.h>
|
#include <cerrno>
|
||||||
#include <dlfcn.h>
|
#include <dlfcn.h>
|
||||||
|
#include <array>
|
||||||
|
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
|
||||||
// Check Memory Allocation
|
// Check Memory Allocation
|
||||||
#define ALLOC_CHECK(obj) \
|
#define ALLOC_CHECK(obj) \
|
||||||
{ \
|
{ \
|
||||||
if (obj == NULL) { \
|
if ((obj) == nullptr) { \
|
||||||
ERR("Memory Allocation Failed"); \
|
ERR("Memory Allocation Failed"); \
|
||||||
} \
|
} \
|
||||||
}
|
}
|
||||||
@ -41,23 +42,18 @@
|
|||||||
} \
|
} \
|
||||||
return func; \
|
return func; \
|
||||||
}
|
}
|
||||||
#ifdef __cplusplus
|
|
||||||
#define hooked_function_setup extern "C"
|
|
||||||
#else
|
|
||||||
#define hooked_function_setup
|
|
||||||
#endif
|
|
||||||
#define HOOK(name, return_type, args) \
|
#define HOOK(name, return_type, args) \
|
||||||
EXTERNAL_FUNC(name, return_type, args) \
|
EXTERNAL_FUNC(name, return_type, args) \
|
||||||
hooked_function_setup __attribute__((__used__)) return_type name args
|
extern "C" __attribute__((__used__)) return_type name args
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Safe Version Of pipe()
|
// Safe Version Of pipe()
|
||||||
void safe_pipe2(int pipefd[2], int flags);
|
struct Pipe {
|
||||||
|
Pipe();
|
||||||
|
const int read;
|
||||||
|
const int write;
|
||||||
|
};
|
||||||
// Check If Two Percentages Are Different Enough To Be Logged
|
// Check If Two Percentages Are Different Enough To Be Logged
|
||||||
int is_progress_difference_significant(int32_t new_val, int32_t old_val);
|
bool is_progress_difference_significant(int32_t new_val, int32_t old_val);
|
||||||
|
|
||||||
// Lock File
|
// Lock File
|
||||||
int lock_file(const char *file);
|
int lock_file(const char *file);
|
||||||
@ -65,8 +61,8 @@ void unlock_file(const char *file, int fd);
|
|||||||
|
|
||||||
// Access Configuration At Runtime
|
// Access Configuration At Runtime
|
||||||
const char *reborn_get_version();
|
const char *reborn_get_version();
|
||||||
int reborn_is_headless();
|
bool reborn_is_headless();
|
||||||
int reborn_is_server();
|
bool reborn_is_server();
|
||||||
|
|
||||||
// Check $DISPLAY
|
// Check $DISPLAY
|
||||||
void reborn_check_display();
|
void reborn_check_display();
|
||||||
@ -77,6 +73,5 @@ const char *get_home_subdirectory_for_game_data();
|
|||||||
// Make Sure Directory Exists
|
// Make Sure Directory Exists
|
||||||
void ensure_directory(const char *path);
|
void ensure_directory(const char *path);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
// Safe write()
|
||||||
}
|
void safe_write(int fd, const void *buf, size_t size);
|
||||||
#endif
|
|
@ -1,5 +1,4 @@
|
|||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <cstdlib>
|
|
||||||
|
|
||||||
#include "utf8.h"
|
#include "utf8.h"
|
||||||
|
|
||||||
@ -48,22 +47,22 @@ static uint32_t *get_cp437_characters_codepoint_map() {
|
|||||||
}
|
}
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
char *to_cp437(const char *input) {
|
std::string to_cp437(const std::string &input) {
|
||||||
// Convert To UTF-32 For Easier Parsing
|
// Convert To UTF-32 For Easier Parsing
|
||||||
std::u32string utf32_str = to_utf32(input);
|
const std::u32string utf32_str = to_utf32(input);
|
||||||
|
|
||||||
// Allocate String
|
// Allocate String
|
||||||
std::string cp437_str;
|
std::string cp437_str;
|
||||||
|
|
||||||
// Handle Characters
|
// Handle Characters
|
||||||
for (size_t i = 0; i < utf32_str.length(); i++) {
|
for (size_t i = 0; i < utf32_str.length(); i++) {
|
||||||
uint32_t codepoint = utf32_str[i];
|
const uint32_t codepoint = utf32_str[i];
|
||||||
bool valid = false;
|
bool valid = false;
|
||||||
for (int j = 0; j < CP437_CHARACTERS; j++) {
|
for (int j = 0; j < CP437_CHARACTERS; j++) {
|
||||||
uint32_t test_codepoint = get_cp437_characters_codepoint_map()[j];
|
const uint32_t test_codepoint = get_cp437_characters_codepoint_map()[j];
|
||||||
if (codepoint == test_codepoint) {
|
if (codepoint == test_codepoint) {
|
||||||
valid = true;
|
valid = true;
|
||||||
cp437_str += j;
|
cp437_str += char(j);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -73,19 +72,18 @@ char *to_cp437(const char *input) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Return
|
// Return
|
||||||
return strdup(cp437_str.c_str());
|
return cp437_str;
|
||||||
}
|
}
|
||||||
char *from_cp437(const char *raw_input) {
|
std::string from_cp437(const std::string &input) {
|
||||||
// Convert To UTF-32 For Easier Parsing
|
// Convert To UTF-32 For Easier Parsing
|
||||||
std::string input = raw_input;
|
|
||||||
std::u32string utf32_str;
|
std::u32string utf32_str;
|
||||||
|
|
||||||
// Handle Characters
|
// Handle Characters
|
||||||
for (size_t i = 0; i < input.length(); i++) {
|
for (size_t i = 0; i < input.length(); i++) {
|
||||||
unsigned char c = (unsigned char) input[i];
|
const unsigned char c = (unsigned char) input[i];
|
||||||
utf32_str += get_cp437_characters_codepoint_map()[(uint32_t) c];
|
utf32_str += char32_t(get_cp437_characters_codepoint_map()[(uint32_t) c]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert To UTF-8
|
// Convert To UTF-8
|
||||||
return strdup(to_utf8(utf32_str).c_str());
|
return to_utf8(utf32_str);
|
||||||
}
|
}
|
||||||
|
@ -1,20 +0,0 @@
|
|||||||
#include <libreborn/env.h>
|
|
||||||
#include <libreborn/exec.h>
|
|
||||||
|
|
||||||
// Define Constants
|
|
||||||
#define ENV(name, ...) const char *const name##_ENV = #name;
|
|
||||||
#include <libreborn/env-list.h>
|
|
||||||
#undef ENV
|
|
||||||
|
|
||||||
// Clear Internal Variables
|
|
||||||
int is_env_var_internal(const char *env) {
|
|
||||||
return env[0] == '_';
|
|
||||||
}
|
|
||||||
void clear_internal_env_vars() {
|
|
||||||
#define ENV(name, ...) \
|
|
||||||
if (is_env_var_internal(name##_ENV)) { \
|
|
||||||
set_and_print_env(name##_ENV, NULL); \
|
|
||||||
}
|
|
||||||
#include <libreborn/env-list.h>
|
|
||||||
#undef ENV
|
|
||||||
}
|
|
38
libreborn/src/util/env.cpp
Normal file
38
libreborn/src/util/env.cpp
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
#include <libreborn/env.h>
|
||||||
|
#include <libreborn/exec.h>
|
||||||
|
#include <libreborn/log.h>
|
||||||
|
|
||||||
|
// Define Constants
|
||||||
|
#define ENV(name, ...) const char *const name##_ENV = #name;
|
||||||
|
#include <libreborn/env-list.h>
|
||||||
|
#undef ENV
|
||||||
|
|
||||||
|
// Clear Internal Variables
|
||||||
|
bool is_env_var_internal(const char *env) {
|
||||||
|
return env[0] == '_';
|
||||||
|
}
|
||||||
|
void clear_internal_env_vars() {
|
||||||
|
DEBUG("Clearing Internal Environmental Variables...");
|
||||||
|
#define ENV(name, ...) \
|
||||||
|
if (is_env_var_internal(name##_ENV)) { \
|
||||||
|
set_and_print_env(name##_ENV, nullptr); \
|
||||||
|
}
|
||||||
|
#include <libreborn/env-list.h>
|
||||||
|
#undef ENV
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set Environmental Variable
|
||||||
|
static void setenv_safe(const char *name, const char *value) {
|
||||||
|
if (value != nullptr) {
|
||||||
|
setenv(name, value, 1);
|
||||||
|
} else {
|
||||||
|
unsetenv(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void set_and_print_env(const char *name, const char *value) {
|
||||||
|
// Set The Value
|
||||||
|
setenv_safe(name, value);
|
||||||
|
|
||||||
|
// Print New Value
|
||||||
|
DEBUG("Set %s = %s", name, value != NULL ? value : "(unset)");
|
||||||
|
}
|
@ -1,151 +0,0 @@
|
|||||||
#include <pthread.h>
|
|
||||||
#include <sys/prctl.h>
|
|
||||||
|
|
||||||
#include <libreborn/exec.h>
|
|
||||||
|
|
||||||
// Set Environmental Variable
|
|
||||||
static void setenv_safe(const char *name, const char *value) {
|
|
||||||
if (value != NULL) {
|
|
||||||
setenv(name, value, 1);
|
|
||||||
} else {
|
|
||||||
unsetenv(name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void set_and_print_env(const char *name, const char *value) {
|
|
||||||
// Set The Value
|
|
||||||
setenv_safe(name, value);
|
|
||||||
|
|
||||||
// Print New Value
|
|
||||||
DEBUG("Set %s = %s", name, value != NULL ? value : "(unset)");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Safe execvpe()
|
|
||||||
__attribute__((noreturn)) void safe_execvpe(const char *const argv[], const char *const envp[]) {
|
|
||||||
// Log
|
|
||||||
DEBUG("Running Command:");
|
|
||||||
for (int i = 0; argv[i] != NULL; i++) {
|
|
||||||
DEBUG(" %s", argv[i]);
|
|
||||||
}
|
|
||||||
// Run
|
|
||||||
int ret = execvpe(argv[0], (char *const *) argv, (char *const *) envp);
|
|
||||||
if (ret == -1) {
|
|
||||||
ERR("Unable To Execute Program: %s: %s", argv[0], strerror(errno));
|
|
||||||
} else {
|
|
||||||
IMPOSSIBLE();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run Command And Get Output
|
|
||||||
char *run_command(const char *const command[], int *exit_status, size_t *output_size) {
|
|
||||||
// Store Output
|
|
||||||
int output_pipe[2];
|
|
||||||
safe_pipe2(output_pipe, 0);
|
|
||||||
// Run
|
|
||||||
pid_t ret = fork();
|
|
||||||
if (ret == -1) {
|
|
||||||
ERR("Unable To Run Command: %s", strerror(errno));
|
|
||||||
} else if (ret == 0) {
|
|
||||||
// Child Process
|
|
||||||
|
|
||||||
// Set Debug Tag
|
|
||||||
reborn_debug_tag = CHILD_PROCESS_TAG;
|
|
||||||
|
|
||||||
// Pipe stdout
|
|
||||||
dup2(output_pipe[1], STDOUT_FILENO);
|
|
||||||
close(output_pipe[0]);
|
|
||||||
close(output_pipe[1]);
|
|
||||||
|
|
||||||
// Setup stderr
|
|
||||||
dup2(reborn_get_debug_fd(), STDERR_FILENO);
|
|
||||||
|
|
||||||
// Kill On Parent Death
|
|
||||||
prctl(PR_SET_PDEATHSIG, SIGKILL);
|
|
||||||
|
|
||||||
// Run
|
|
||||||
safe_execvpe(command, (const char *const *) environ);
|
|
||||||
} else {
|
|
||||||
// Read stdout
|
|
||||||
close(output_pipe[1]);
|
|
||||||
#define BUFFER_SIZE 1024
|
|
||||||
size_t size = BUFFER_SIZE;
|
|
||||||
char *output = malloc(size);
|
|
||||||
char buf[BUFFER_SIZE];
|
|
||||||
size_t position = 0;
|
|
||||||
ssize_t bytes_read = 0;
|
|
||||||
while ((bytes_read = read(output_pipe[0], buf, BUFFER_SIZE)) > 0) {
|
|
||||||
// Grow Output If Needed
|
|
||||||
size_t needed_size = position + bytes_read;
|
|
||||||
if (needed_size > size) {
|
|
||||||
// More Memory Needed
|
|
||||||
size_t new_size = size;
|
|
||||||
while (new_size < needed_size) {
|
|
||||||
new_size += BUFFER_SIZE;
|
|
||||||
}
|
|
||||||
char *new_output = realloc(output, new_size);
|
|
||||||
if (new_output == NULL) {
|
|
||||||
ERR("Unable To Grow Output Buffer");
|
|
||||||
} else {
|
|
||||||
output = new_output;
|
|
||||||
size = new_size;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy Data To Output
|
|
||||||
memcpy(output + position, buf, bytes_read);
|
|
||||||
position += bytes_read;
|
|
||||||
}
|
|
||||||
close(output_pipe[0]);
|
|
||||||
|
|
||||||
// Add NULL-Terminator To Output
|
|
||||||
size_t needed_size = position + 1;
|
|
||||||
if (needed_size > size) {
|
|
||||||
// More Memory Needed
|
|
||||||
size_t new_size = size + 1;
|
|
||||||
char *new_output = realloc(output, new_size);
|
|
||||||
if (new_output == NULL) {
|
|
||||||
ERR("Unable To Grow Output Buffer (For NULL-Terminator)");
|
|
||||||
} else {
|
|
||||||
output = new_output;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
output[position++] = '\0';
|
|
||||||
|
|
||||||
// Return Output Size
|
|
||||||
if (output_size != NULL) {
|
|
||||||
*output_size = position;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get Return Code
|
|
||||||
int status;
|
|
||||||
waitpid(ret, &status, 0);
|
|
||||||
if (exit_status != NULL) {
|
|
||||||
*exit_status = status;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set obj To NULL On asprintf() Failure
|
|
||||||
#define safe_asprintf(obj, ...) \
|
|
||||||
{ \
|
|
||||||
if (asprintf(obj, __VA_ARGS__) == -1) { \
|
|
||||||
*obj = NULL; \
|
|
||||||
} \
|
|
||||||
ALLOC_CHECK(*obj); \
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get Exit Status String
|
|
||||||
void get_exit_status_string(const int status, char **out) {
|
|
||||||
if (out != NULL) {
|
|
||||||
*out = NULL;
|
|
||||||
if (WIFEXITED(status)) {
|
|
||||||
safe_asprintf(out, ": Exit Code: %i", WEXITSTATUS(status));
|
|
||||||
} else if (WIFSIGNALED(status)) {
|
|
||||||
safe_asprintf(out, ": Signal: %i%s", WTERMSIG(status), WCOREDUMP(status) ? " (Core Dumped)" : "");
|
|
||||||
} else {
|
|
||||||
safe_asprintf(out, ": Terminated");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
176
libreborn/src/util/exec.cpp
Normal file
176
libreborn/src/util/exec.cpp
Normal file
@ -0,0 +1,176 @@
|
|||||||
|
#include <pthread.h>
|
||||||
|
#include <sys/prctl.h>
|
||||||
|
#include <cerrno>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/poll.h>
|
||||||
|
|
||||||
|
#include <libreborn/util.h>
|
||||||
|
#include <libreborn/log.h>
|
||||||
|
#include <libreborn/exec.h>
|
||||||
|
|
||||||
|
// Fork
|
||||||
|
Process::Process(const pid_t pid_, const std::array<int, 3> fds_): pid(pid_), fds(fds_) {}
|
||||||
|
int Process::close() const {
|
||||||
|
for (const int fd : fds) {
|
||||||
|
::close(fd);
|
||||||
|
}
|
||||||
|
int status;
|
||||||
|
waitpid(pid, &status, 0);
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
#define PIPE_READ 0
|
||||||
|
#define PIPE_WRITE 1
|
||||||
|
std::optional<Process> fork_with_stdio() {
|
||||||
|
// Store Output
|
||||||
|
const std::array<Pipe, Process::fd_count> pipes = {};
|
||||||
|
|
||||||
|
// Fork
|
||||||
|
const pid_t ret = fork();
|
||||||
|
if (ret == -1) {
|
||||||
|
ERR("Unable To Fork: %s", strerror(errno));
|
||||||
|
} else if (ret == 0) {
|
||||||
|
// Child Process
|
||||||
|
|
||||||
|
// Redirect stdio To Pipes
|
||||||
|
dup2(pipes[0].write, STDOUT_FILENO);
|
||||||
|
dup2(pipes[1].write, STDERR_FILENO);
|
||||||
|
dup2(pipes[2].read, STDIN_FILENO);
|
||||||
|
for (const Pipe &pipe : pipes) {
|
||||||
|
close(pipe.write);
|
||||||
|
close(pipe.read);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Kill Child If Parent Exits First
|
||||||
|
prctl(PR_SET_PDEATHSIG, SIGKILL);
|
||||||
|
|
||||||
|
// Continue Execution
|
||||||
|
return {};
|
||||||
|
} else {
|
||||||
|
// Parent Process
|
||||||
|
|
||||||
|
// Close Unneeded File Descriptors
|
||||||
|
close(pipes[0].write);
|
||||||
|
close(pipes[1].write);
|
||||||
|
close(pipes[2].read);
|
||||||
|
|
||||||
|
// Return
|
||||||
|
return Process(ret, {pipes[0].read, pipes[1].read, pipes[2].write});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#define BUFFER_SIZE 1024
|
||||||
|
void poll_fds(const std::vector<int> &fds, const std::function<void(int, size_t, unsigned char *)> &on_data) {
|
||||||
|
// Track Open FDs
|
||||||
|
int open_fds = int(fds.size());
|
||||||
|
|
||||||
|
// Setup Polling
|
||||||
|
pollfd *poll_fds = new pollfd[fds.size()];
|
||||||
|
for (std::vector<int>::size_type i = 0; i < fds.size(); i++) {
|
||||||
|
const int fd = fds[i];
|
||||||
|
poll_fds[i].fd = fd;
|
||||||
|
poll_fds[i].events = POLLIN;
|
||||||
|
if (fd == STDIN_FILENO) {
|
||||||
|
// Don't Wait For stdin To Close
|
||||||
|
open_fds--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Poll
|
||||||
|
while (open_fds > 0) {
|
||||||
|
const int poll_ret = poll(poll_fds, fds.size(), -1);
|
||||||
|
if (poll_ret == -1) {
|
||||||
|
if (errno == EINTR) {
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
ERR("Unable To Poll Data: %s", strerror(errno));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle Data
|
||||||
|
for (std::vector<int>::size_type i = 0; i < fds.size(); i++) {
|
||||||
|
pollfd &poll_fd = poll_fds[i];
|
||||||
|
if (poll_fd.revents != 0 && poll_fd.events != 0) {
|
||||||
|
if (poll_fd.revents & POLLIN) {
|
||||||
|
// Data Available From Child's stdout/stderr
|
||||||
|
unsigned char buf[BUFFER_SIZE];
|
||||||
|
const ssize_t bytes_read = read(poll_fd.fd, buf, BUFFER_SIZE);
|
||||||
|
if (bytes_read == -1) {
|
||||||
|
ERR("Unable To Read Data: %s", strerror(errno));
|
||||||
|
}
|
||||||
|
// Callback
|
||||||
|
on_data(int(i), size_t(bytes_read), buf);
|
||||||
|
} else {
|
||||||
|
// File Descriptor No Longer Accessible
|
||||||
|
poll_fd.events = 0;
|
||||||
|
open_fds--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cleanup
|
||||||
|
delete[] poll_fds;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Safe execvpe()
|
||||||
|
__attribute__((noreturn)) void safe_execvpe(const char *const argv[], const char *const envp[]) {
|
||||||
|
// Log
|
||||||
|
DEBUG("Running Command:");
|
||||||
|
for (int i = 0; argv[i] != nullptr; i++) {
|
||||||
|
DEBUG(" %s", argv[i]);
|
||||||
|
}
|
||||||
|
// Run
|
||||||
|
int ret = execvpe(argv[0], (char *const *) argv, (char *const *) envp);
|
||||||
|
if (ret == -1) {
|
||||||
|
ERR("Unable To Execute Program: %s: %s", argv[0], strerror(errno));
|
||||||
|
} else {
|
||||||
|
IMPOSSIBLE();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run Command And Get Output
|
||||||
|
std::vector<unsigned char> *run_command(const char *const command[], int *exit_status) {
|
||||||
|
// Run
|
||||||
|
const std::optional<Process> child = fork_with_stdio();
|
||||||
|
if (!child) {
|
||||||
|
// Child Process
|
||||||
|
reborn_debug_tag = CHILD_PROCESS_TAG;
|
||||||
|
// Run
|
||||||
|
safe_execvpe(command, (const char *const *) environ);
|
||||||
|
} else {
|
||||||
|
// Read stdout
|
||||||
|
std::vector<unsigned char> *output = new std::vector<unsigned char>;
|
||||||
|
poll_fds({child->fds[0], child->fds[1]}, [&output](const int i, const size_t size, unsigned char *buf) {
|
||||||
|
if (i == 0) {
|
||||||
|
// stdout
|
||||||
|
output->insert(output->end(), buf, buf + size);
|
||||||
|
} else {
|
||||||
|
// stderr
|
||||||
|
safe_write(reborn_get_debug_fd(), buf, size);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Get Exit Status
|
||||||
|
const int status = child->close();
|
||||||
|
if (exit_status != nullptr) {
|
||||||
|
*exit_status = status;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add NULL-Terminator To Output
|
||||||
|
output->push_back(0);
|
||||||
|
|
||||||
|
// Return
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get Exit Status String
|
||||||
|
std::string get_exit_status_string(const int status) {
|
||||||
|
if (WIFEXITED(status)) {
|
||||||
|
return ": Exit Code: " + std::to_string(WEXITSTATUS(status));
|
||||||
|
} else if (WIFSIGNALED(status)) {
|
||||||
|
return ": Signal: " + std::to_string(WTERMSIG(status)) + (WCOREDUMP(status) ? " (Core Dumped)" : "");
|
||||||
|
} else {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
@ -1,15 +1,24 @@
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <errno.h>
|
#include <string>
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include <libreborn/log.h>
|
#include <libreborn/log.h>
|
||||||
#include <libreborn/exec.h>
|
|
||||||
#include <libreborn/env.h>
|
#include <libreborn/env.h>
|
||||||
|
|
||||||
// Debug Tag
|
// Debug Tag
|
||||||
const char *reborn_debug_tag = "";
|
const char *reborn_debug_tag = "";
|
||||||
|
|
||||||
|
// /dev/null FD
|
||||||
|
static int null_fd = -1;
|
||||||
|
static void setup_null_fd() {
|
||||||
|
if (null_fd == -1) {
|
||||||
|
null_fd = open("/dev/null", O_WRONLY | O_APPEND);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
__attribute__((destructor)) static void close_null_fd() {
|
||||||
|
close(null_fd);
|
||||||
|
}
|
||||||
|
|
||||||
// Log File
|
// Log File
|
||||||
static int log_fd = -1;
|
static int log_fd = -1;
|
||||||
int reborn_get_log_fd() {
|
int reborn_get_log_fd() {
|
||||||
@ -18,10 +27,11 @@ int reborn_get_log_fd() {
|
|||||||
}
|
}
|
||||||
// Open Log File
|
// Open Log File
|
||||||
const char *fd_str = getenv(_MCPI_LOG_FD_ENV);
|
const char *fd_str = getenv(_MCPI_LOG_FD_ENV);
|
||||||
log_fd = fd_str ? atoi(fd_str) : open("/dev/null", O_WRONLY | O_APPEND);
|
if (fd_str) {
|
||||||
// Check FD
|
log_fd = std::stoi(fd_str);
|
||||||
if (log_fd < 0) {
|
} else {
|
||||||
ERR("Unable To Open Log: %s", strerror(errno));
|
setup_null_fd();
|
||||||
|
log_fd = null_fd;
|
||||||
}
|
}
|
||||||
// Return
|
// Return
|
||||||
return reborn_get_log_fd();
|
return reborn_get_log_fd();
|
||||||
@ -29,14 +39,12 @@ int reborn_get_log_fd() {
|
|||||||
void reborn_set_log(const int fd) {
|
void reborn_set_log(const int fd) {
|
||||||
// Set Variable
|
// Set Variable
|
||||||
log_fd = -1;
|
log_fd = -1;
|
||||||
char buf[128];
|
set_and_print_env(_MCPI_LOG_FD_ENV, std::to_string(fd).c_str());
|
||||||
sprintf(buf, "%i", fd);
|
|
||||||
set_and_print_env(_MCPI_LOG_FD_ENV, buf);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Debug Logging
|
// Debug Logging
|
||||||
static int should_print_debug_to_stderr() {
|
static bool should_print_debug_to_stderr() {
|
||||||
return getenv(MCPI_DEBUG_ENV) != NULL;
|
return getenv(MCPI_DEBUG_ENV) != nullptr;
|
||||||
}
|
}
|
||||||
int reborn_get_debug_fd() {
|
int reborn_get_debug_fd() {
|
||||||
return should_print_debug_to_stderr() ? STDERR_FILENO : reborn_get_log_fd();
|
return should_print_debug_to_stderr() ? STDERR_FILENO : reborn_get_log_fd();
|
@ -1,14 +1,12 @@
|
|||||||
#include <libreborn/string.h>
|
#include <libreborn/string.h>
|
||||||
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
// Sanitize String
|
// Sanitize String
|
||||||
void sanitize_string(char *str, const int max_length, const int allow_newlines) {
|
void sanitize_string(std::string &str, const int max_length, const bool allow_newlines) {
|
||||||
// Store Message Length
|
// Store Message Length
|
||||||
size_t length = strlen(str);
|
size_t length = str.size();
|
||||||
// Truncate Message
|
// Truncate Message
|
||||||
if (max_length >= 0 && length > ((size_t) max_length)) {
|
if (max_length >= 0 && length > ((size_t) max_length)) {
|
||||||
str[max_length] = '\0';
|
str = str.substr(0, max_length);
|
||||||
length = max_length;
|
length = max_length;
|
||||||
}
|
}
|
||||||
// Loop Through Message
|
// Loop Through Message
|
||||||
@ -21,8 +19,3 @@ void sanitize_string(char *str, const int max_length, const int allow_newlines)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Starts With
|
|
||||||
int starts_with(const char *str, const char *prefix) {
|
|
||||||
return strncmp(prefix, str, strlen(prefix)) == 0;
|
|
||||||
}
|
|
@ -7,31 +7,34 @@
|
|||||||
#include <libreborn/env.h>
|
#include <libreborn/env.h>
|
||||||
|
|
||||||
// Safe Version Of pipe()
|
// Safe Version Of pipe()
|
||||||
void safe_pipe2(int pipefd[2], int flags) {
|
Pipe::Pipe(): read(-1), write(-1) {
|
||||||
if (pipe2(pipefd, flags) != 0) {
|
int out[2];
|
||||||
|
if (pipe(out) != 0) {
|
||||||
ERR("Unable To Create Pipe: %s", strerror(errno));
|
ERR("Unable To Create Pipe: %s", strerror(errno));
|
||||||
}
|
}
|
||||||
|
const_cast<int &>(read) = out[0];
|
||||||
|
const_cast<int &>(write) = out[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check If Two Percentages Are Different Enough To Be Logged
|
// Check If Two Percentages Are Different Enough To Be Logged
|
||||||
#define SIGNIFICANT_PROGRESS 5
|
#define SIGNIFICANT_PROGRESS 5
|
||||||
int is_progress_difference_significant(int32_t new_val, int32_t old_val) {
|
bool is_progress_difference_significant(const int32_t new_val, const int32_t old_val) {
|
||||||
if (new_val != old_val) {
|
if (new_val != old_val) {
|
||||||
if (new_val == -1 || old_val == -1) {
|
if (new_val == -1 || old_val == -1) {
|
||||||
return 1;
|
return true;
|
||||||
} else if (new_val == 0 || new_val == 100) {
|
} else if (new_val == 0 || new_val == 100) {
|
||||||
return 1;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
return new_val - old_val >= SIGNIFICANT_PROGRESS;
|
return new_val - old_val >= SIGNIFICANT_PROGRESS;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return 0;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Lock File
|
// Lock File
|
||||||
int lock_file(const char *file) {
|
int lock_file(const char *file) {
|
||||||
int fd = open(file, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
|
const int fd = open(file, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
|
||||||
if (fd == -1) {
|
if (fd == -1) {
|
||||||
ERR("Unable To Open Lock File: %s: %s", file, strerror(errno));
|
ERR("Unable To Open Lock File: %s: %s", file, strerror(errno));
|
||||||
}
|
}
|
||||||
@ -51,25 +54,25 @@ void unlock_file(const char *file, const int fd) {
|
|||||||
const char *reborn_get_version() {
|
const char *reborn_get_version() {
|
||||||
return MCPI_VERSION;
|
return MCPI_VERSION;
|
||||||
}
|
}
|
||||||
int reborn_is_headless() {
|
bool reborn_is_headless() {
|
||||||
static int ret;
|
static bool ret;
|
||||||
static int is_set = 0;
|
static bool is_set = false;
|
||||||
if (!is_set) {
|
if (!is_set) {
|
||||||
ret = reborn_is_server();
|
ret = reborn_is_server();
|
||||||
if (getenv(_MCPI_FORCE_HEADLESS_ENV)) {
|
if (getenv(_MCPI_FORCE_HEADLESS_ENV)) {
|
||||||
ret = 1;
|
ret = true;
|
||||||
} else if (getenv(_MCPI_FORCE_NON_HEADLESS_ENV)) {
|
} else if (getenv(_MCPI_FORCE_NON_HEADLESS_ENV)) {
|
||||||
ret = 0;
|
ret = false;
|
||||||
}
|
}
|
||||||
is_set = 1;
|
is_set = true;
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
int reborn_is_server() {
|
bool reborn_is_server() {
|
||||||
static int ret;
|
static int ret;
|
||||||
static int is_set = 0;
|
static int is_set = 0;
|
||||||
if (!is_set) {
|
if (!is_set) {
|
||||||
ret = getenv(_MCPI_SERVER_MODE_ENV) != NULL;
|
ret = getenv(_MCPI_SERVER_MODE_ENV) != nullptr;
|
||||||
is_set = 1;
|
is_set = 1;
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
@ -84,7 +87,7 @@ void reborn_check_display() {
|
|||||||
|
|
||||||
// Home Subdirectory
|
// Home Subdirectory
|
||||||
const char *get_home_subdirectory_for_game_data() {
|
const char *get_home_subdirectory_for_game_data() {
|
||||||
if (getenv(MCPI_PROFILE_DIRECTORY_ENV) != NULL) {
|
if (getenv(MCPI_PROFILE_DIRECTORY_ENV) != nullptr) {
|
||||||
// No Subdirectory When Using Custom Profile Directory
|
// No Subdirectory When Using Custom Profile Directory
|
||||||
return "";
|
return "";
|
||||||
} else if (!reborn_is_server()) {
|
} else if (!reborn_is_server()) {
|
||||||
@ -112,3 +115,11 @@ void ensure_directory(const char *path) {
|
|||||||
ERR("Not A Directory: %s", path);
|
ERR("Not A Directory: %s", path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Write To FD
|
||||||
|
void safe_write(const int fd, const void *buf, const size_t size) {
|
||||||
|
const ssize_t bytes_written = write(fd, buf, size);
|
||||||
|
if (bytes_written < 0) {
|
||||||
|
ERR("Unable To Write Data: %s", strerror(errno));
|
||||||
|
}
|
||||||
|
}
|
@ -9,7 +9,7 @@ set(CORE_SRC
|
|||||||
src/events.cpp
|
src/events.cpp
|
||||||
src/offscreen.cpp
|
src/offscreen.cpp
|
||||||
src/audio/api.cpp
|
src/audio/api.cpp
|
||||||
src/audio/engine.c
|
src/audio/engine.cpp
|
||||||
src/audio/file.cpp
|
src/audio/file.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -44,7 +44,7 @@ void _media_audio_delete_sources() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Update Listener
|
// Update Listener
|
||||||
void media_audio_update(float volume, float x, float y, float z, float yaw) {
|
void media_audio_update(const float volume, const float x, const float y, const float z, const float yaw) {
|
||||||
// Check
|
// Check
|
||||||
if (_media_audio_is_loaded()) {
|
if (_media_audio_is_loaded()) {
|
||||||
// Update Listener Volume
|
// Update Listener Volume
|
||||||
@ -56,8 +56,8 @@ void media_audio_update(float volume, float x, float y, float z, float yaw) {
|
|||||||
AL_ERROR_CHECK();
|
AL_ERROR_CHECK();
|
||||||
|
|
||||||
// Update Listener Orientation
|
// Update Listener Orientation
|
||||||
float radian_yaw = yaw * (M_PI / 180);
|
const float radian_yaw = float(yaw * (M_PI / 180));
|
||||||
ALfloat orientation[] = {-sinf(radian_yaw), 0.0f, cosf(radian_yaw), 0.0f, 1.0f, 0.0f};
|
const ALfloat orientation[] = {-sinf(radian_yaw), 0.0f, cosf(radian_yaw), 0.0f, 1.0f, 0.0f};
|
||||||
alListenerfv(AL_ORIENTATION, orientation);
|
alListenerfv(AL_ORIENTATION, orientation);
|
||||||
AL_ERROR_CHECK();
|
AL_ERROR_CHECK();
|
||||||
|
|
||||||
@ -101,11 +101,11 @@ void media_audio_play(const char *source, const char *name, float x, float y, fl
|
|||||||
// Check
|
// Check
|
||||||
if (_media_audio_is_loaded()) {
|
if (_media_audio_is_loaded()) {
|
||||||
// Load Sound
|
// Load Sound
|
||||||
ALuint buffer = _media_audio_get_buffer(source, name);
|
const ALuint buffer = _media_audio_get_buffer(source, name);
|
||||||
if (volume > 0.0f && buffer) {
|
if (volume > 0.0f && buffer) {
|
||||||
// Get Source
|
// Get Source
|
||||||
ALuint al_source;
|
ALuint al_source;
|
||||||
if (idle_sources.size() > 0) {
|
if (!idle_sources.empty()) {
|
||||||
// Use Idle Source
|
// Use Idle Source
|
||||||
al_source = idle_sources.back();
|
al_source = idle_sources.back();
|
||||||
idle_sources.pop_back();
|
idle_sources.pop_back();
|
||||||
@ -114,7 +114,7 @@ void media_audio_play(const char *source, const char *name, float x, float y, fl
|
|||||||
alGenSources(1, &al_source);
|
alGenSources(1, &al_source);
|
||||||
// Special Out-Of-Memory Handling
|
// Special Out-Of-Memory Handling
|
||||||
{
|
{
|
||||||
ALenum err = alGetError();
|
const ALenum err = alGetError();
|
||||||
if (err == AL_OUT_OF_MEMORY) {
|
if (err == AL_OUT_OF_MEMORY) {
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
|
@ -1,11 +1,3 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
__attribute__((visibility("internal"))) void _media_audio_delete_sources();
|
__attribute__((visibility("internal"))) void _media_audio_delete_sources();
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
@ -9,8 +9,8 @@
|
|||||||
#include "api.h"
|
#include "api.h"
|
||||||
|
|
||||||
// Store Device
|
// Store Device
|
||||||
static ALCdevice *device = NULL;
|
static ALCdevice *device = nullptr;
|
||||||
static ALCcontext *context = NULL;
|
static ALCcontext *context = nullptr;
|
||||||
|
|
||||||
// Store State
|
// Store State
|
||||||
static int is_loaded = 0;
|
static int is_loaded = 0;
|
||||||
@ -21,14 +21,14 @@ int _media_audio_is_loaded() {
|
|||||||
// Init
|
// Init
|
||||||
void _media_audio_init() {
|
void _media_audio_init() {
|
||||||
// Open Device
|
// Open Device
|
||||||
device = alcOpenDevice(NULL);
|
device = alcOpenDevice(nullptr);
|
||||||
if (!device) {
|
if (!device) {
|
||||||
WARN("Unable To Load Audio Engine");
|
WARN("Unable To Load Audio Engine");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create Context
|
// Create Context
|
||||||
context = alcCreateContext(device, NULL);
|
context = alcCreateContext(device, nullptr);
|
||||||
ALCenum err = alcGetError(device);
|
ALCenum err = alcGetError(device);
|
||||||
if (err != ALC_NO_ERROR) {
|
if (err != ALC_NO_ERROR) {
|
||||||
ERR("Unable To Open Audio Context: %s", alcGetString(device, err));
|
ERR("Unable To Open Audio Context: %s", alcGetString(device, err));
|
||||||
@ -63,7 +63,7 @@ void _media_audio_cleanup() {
|
|||||||
_media_audio_delete_buffers();
|
_media_audio_delete_buffers();
|
||||||
|
|
||||||
// Deselect Context
|
// Deselect Context
|
||||||
alcMakeContextCurrent(NULL);
|
alcMakeContextCurrent(nullptr);
|
||||||
ALCenum err = alcGetError(device);
|
ALCenum err = alcGetError(device);
|
||||||
if (err != ALC_NO_ERROR) {
|
if (err != ALC_NO_ERROR) {
|
||||||
ERR("Unable To Deselect Audio Context: %s", alcGetString(device, err));
|
ERR("Unable To Deselect Audio Context: %s", alcGetString(device, err));
|
@ -2,14 +2,6 @@
|
|||||||
|
|
||||||
#include <AL/al.h>
|
#include <AL/al.h>
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
__attribute__((visibility("internal"))) void _media_audio_init();
|
__attribute__((visibility("internal"))) void _media_audio_init();
|
||||||
__attribute__((visibility("internal"))) void _media_audio_cleanup();
|
__attribute__((visibility("internal"))) void _media_audio_cleanup();
|
||||||
__attribute__((visibility("internal"))) int _media_audio_is_loaded();
|
__attribute__((visibility("internal"))) int _media_audio_is_loaded();
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
@ -14,13 +14,13 @@
|
|||||||
// Load Symbol From ELF File
|
// Load Symbol From ELF File
|
||||||
static void load_symbol(const char *source, const char *name, std::function<void(const unsigned char *, uint32_t)> callback) {
|
static void load_symbol(const char *source, const char *name, std::function<void(const unsigned char *, uint32_t)> callback) {
|
||||||
static std::unordered_map<std::string, std::unique_ptr<LIEF::ELF::Binary>> sources = {};
|
static std::unordered_map<std::string, std::unique_ptr<LIEF::ELF::Binary>> sources = {};
|
||||||
std::string cpp_source = source;
|
const std::string cpp_source = source;
|
||||||
if (sources.count(cpp_source) == 0) {
|
if (!sources.contains(cpp_source)) {
|
||||||
sources[cpp_source] = LIEF::ELF::Parser::parse(source);
|
sources[cpp_source] = LIEF::ELF::Parser::parse(source);
|
||||||
}
|
}
|
||||||
std::unique_ptr<LIEF::ELF::Binary> &binary = sources[cpp_source];
|
const std::unique_ptr<LIEF::ELF::Binary> &binary = sources[cpp_source];
|
||||||
const LIEF::ELF::Symbol *symbol = binary->get_dynamic_symbol(name);
|
const LIEF::ELF::Symbol *symbol = binary->get_dynamic_symbol(name);
|
||||||
if (symbol != NULL) {
|
if (symbol != nullptr) {
|
||||||
LIEF::span<const uint8_t> data = binary->get_content_from_virtual_address(symbol->value(), symbol->size(), LIEF::Binary::VA_TYPES::VA);
|
LIEF::span<const uint8_t> data = binary->get_content_from_virtual_address(symbol->value(), symbol->size(), LIEF::Binary::VA_TYPES::VA);
|
||||||
callback(data.data(), data.size());
|
callback(data.data(), data.size());
|
||||||
} else {
|
} else {
|
||||||
@ -73,8 +73,8 @@ static ALuint load_sound(const char *source, const char *name) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Load Data
|
// Load Data
|
||||||
int remaining_size = size - sizeof (audio_metadata);
|
const int remaining_size = size - sizeof (audio_metadata);
|
||||||
int data_size = meta->channels * meta->frames * meta->frame_size;
|
const int data_size = meta->channels * meta->frames * meta->frame_size;
|
||||||
if (remaining_size < data_size) {
|
if (remaining_size < data_size) {
|
||||||
WARN("Symbol Too Small To Contain Specified Audio Data: %s", name);
|
WARN("Symbol Too Small To Contain Specified Audio Data: %s", name);
|
||||||
return;
|
return;
|
||||||
|
@ -2,13 +2,5 @@
|
|||||||
|
|
||||||
#include <AL/al.h>
|
#include <AL/al.h>
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
__attribute__((visibility("internal"))) ALuint _media_audio_get_buffer(const char *source, const char *name);
|
__attribute__((visibility("internal"))) ALuint _media_audio_get_buffer(const char *source, const char *name);
|
||||||
__attribute__((visibility("internal"))) void _media_audio_delete_buffers();
|
__attribute__((visibility("internal"))) void _media_audio_delete_buffers();
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
@ -164,15 +164,13 @@ static void codepoint_to_utf8(unsigned char *const buffer, const unsigned int co
|
|||||||
static void glfw_char(__attribute__((unused)) GLFWwindow *window, const unsigned int codepoint) {
|
static void glfw_char(__attribute__((unused)) GLFWwindow *window, const unsigned int codepoint) {
|
||||||
// Convert
|
// Convert
|
||||||
size_t str_size = 4 /* Maximum UTF-8 character size */ + 1 /* NULL-terminator */;
|
size_t str_size = 4 /* Maximum UTF-8 character size */ + 1 /* NULL-terminator */;
|
||||||
char str[str_size] = {};
|
unsigned char str[str_size] = {};
|
||||||
codepoint_to_utf8((unsigned char *) str, codepoint);
|
codepoint_to_utf8(str, codepoint);
|
||||||
char *cp437_str = to_cp437(str);
|
std::string cp437_str = to_cp437((const char *) str);
|
||||||
// Send Event
|
// Send Event
|
||||||
for (int i = 0; cp437_str[i] != '\0'; i++) {
|
for (const char x : cp437_str) {
|
||||||
character_event(cp437_str[i]);
|
character_event(x);
|
||||||
}
|
}
|
||||||
// Free
|
|
||||||
free(cp437_str);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert Screen Coordinates To Pixels
|
// Convert Screen Coordinates To Pixels
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
char *override_get_path(const char *filename);
|
std::string override_get_path(std::string filename);
|
||||||
}
|
}
|
@ -26,9 +26,8 @@ std::string chat_send_api_command(const Minecraft *minecraft, const std::string
|
|||||||
|
|
||||||
// Send API Chat Command
|
// Send API Chat Command
|
||||||
static void send_api_chat_command(const Minecraft *minecraft, const char *str) {
|
static void send_api_chat_command(const Minecraft *minecraft, const char *str) {
|
||||||
char *utf_str = from_cp437(str);
|
const std::string utf_str = from_cp437(str);
|
||||||
const std::string command = std::string("chat.post(") + utf_str + ")\n";
|
const std::string command = std::string("chat.post(") + utf_str + ")\n";
|
||||||
free(utf_str);
|
|
||||||
chat_send_api_command(minecraft, command);
|
chat_send_api_command(minecraft, command);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -38,19 +37,13 @@ std::string _chat_get_prefix(const char *username) {
|
|||||||
}
|
}
|
||||||
void chat_send_message_to_clients(ServerSideNetworkHandler *server_side_network_handler, const char *username, const char *message) {
|
void chat_send_message_to_clients(ServerSideNetworkHandler *server_side_network_handler, const char *username, const char *message) {
|
||||||
std::string full_message = _chat_get_prefix(username) + message;
|
std::string full_message = _chat_get_prefix(username) + message;
|
||||||
char *raw_str = strdup(full_message.c_str());
|
sanitize_string(full_message, MAX_CHAT_MESSAGE_LENGTH, false);
|
||||||
ALLOC_CHECK(raw_str);
|
|
||||||
sanitize_string(raw_str, MAX_CHAT_MESSAGE_LENGTH, 0);
|
|
||||||
full_message = raw_str;
|
|
||||||
free(raw_str);
|
|
||||||
server_side_network_handler->displayGameMessage(full_message);
|
server_side_network_handler->displayGameMessage(full_message);
|
||||||
}
|
}
|
||||||
// Handle Chat packet Send
|
// Handle Chat packet Send
|
||||||
void chat_handle_packet_send(const Minecraft *minecraft, ChatPacket *packet) {
|
void chat_handle_packet_send(const Minecraft *minecraft, ChatPacket *packet) {
|
||||||
// Convert To CP-437
|
// Convert To CP-437
|
||||||
char *cp437_str = to_cp437(packet->message.c_str());
|
packet->message = to_cp437(packet->message);
|
||||||
packet->message = cp437_str;
|
|
||||||
free(cp437_str);
|
|
||||||
// Send
|
// Send
|
||||||
RakNetInstance *rak_net_instance = minecraft->rak_net_instance;
|
RakNetInstance *rak_net_instance = minecraft->rak_net_instance;
|
||||||
if (rak_net_instance->isServer()) {
|
if (rak_net_instance->isServer()) {
|
||||||
|
@ -1,10 +1,13 @@
|
|||||||
#include "game-mode-internal.h"
|
#include <cstdint>
|
||||||
|
|
||||||
#include <mods/init/init.h>
|
#include <mods/init/init.h>
|
||||||
#include <mods/feature/feature.h>
|
#include <mods/feature/feature.h>
|
||||||
|
|
||||||
#include <libreborn/libreborn.h>
|
#include <libreborn/libreborn.h>
|
||||||
#include <symbols/minecraft.h>
|
#include <symbols/minecraft.h>
|
||||||
|
|
||||||
|
#include "game-mode-internal.h"
|
||||||
|
|
||||||
static int is_survival = -1;
|
static int is_survival = -1;
|
||||||
|
|
||||||
// Patch Game Mode
|
// Patch Game Mode
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
#include <cstdint>
|
||||||
|
|
||||||
#include <libreborn/libreborn.h>
|
#include <libreborn/libreborn.h>
|
||||||
#include <symbols/minecraft.h>
|
#include <symbols/minecraft.h>
|
||||||
|
|
||||||
|
@ -10,10 +10,8 @@
|
|||||||
// Print Chat To Log
|
// Print Chat To Log
|
||||||
static void Gui_addMessage_injection(Gui_addMessage_t original, Gui *gui, const std::string &text) {
|
static void Gui_addMessage_injection(Gui_addMessage_t original, Gui *gui, const std::string &text) {
|
||||||
// Sanitize Message
|
// Sanitize Message
|
||||||
char *new_message = strdup(text.c_str());
|
std::string new_message = text;
|
||||||
ALLOC_CHECK(new_message);
|
sanitize_string(new_message, -1, true);
|
||||||
sanitize_string(new_message, -1, 1);
|
|
||||||
const std::string cpp_str = new_message;
|
|
||||||
|
|
||||||
// Process Message
|
// Process Message
|
||||||
static bool recursing = false;
|
static bool recursing = false;
|
||||||
@ -22,22 +20,18 @@ static void Gui_addMessage_injection(Gui_addMessage_t original, Gui *gui, const
|
|||||||
recursing = true;
|
recursing = true;
|
||||||
|
|
||||||
// Print Log Message
|
// Print Log Message
|
||||||
char *safe_message = from_cp437(new_message);
|
std::string safe_message = from_cp437(new_message);
|
||||||
fprintf(stderr, "[CHAT]: %s\n", safe_message);
|
fprintf(stderr, "[CHAT]: %s\n", safe_message.c_str());
|
||||||
free(safe_message);
|
|
||||||
|
|
||||||
// Call Original Method
|
// Call Original Method
|
||||||
original(gui, cpp_str);
|
original(gui, new_message);
|
||||||
|
|
||||||
// End Recursing
|
// End Recursing
|
||||||
recursing = false;
|
recursing = false;
|
||||||
} else {
|
} else {
|
||||||
// Call Original Method
|
// Call Original Method
|
||||||
original(gui, cpp_str);
|
original(gui, new_message);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Free
|
|
||||||
free(new_message);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Print Progress Reports
|
// Print Progress Reports
|
||||||
|
@ -34,13 +34,10 @@ static void LoginPacket_read_injection(LoginPacket_read_t original, LoginPacket
|
|||||||
const RakNet_RakString_SharedString *shared_string = rak_string->sharedString;
|
const RakNet_RakString_SharedString *shared_string = rak_string->sharedString;
|
||||||
const char *c_str = shared_string->c_str;
|
const char *c_str = shared_string->c_str;
|
||||||
// Sanitize
|
// Sanitize
|
||||||
char *new_username = strdup(c_str);
|
std::string new_username = c_str;
|
||||||
ALLOC_CHECK(new_username);
|
|
||||||
sanitize_string(new_username, MAX_USERNAME_LENGTH, 0);
|
sanitize_string(new_username, MAX_USERNAME_LENGTH, 0);
|
||||||
// Set New Username
|
// Set New Username
|
||||||
rak_string->Assign(new_username);
|
rak_string->Assign(new_username.c_str());
|
||||||
// Free
|
|
||||||
free(new_username);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fix RakNet::RakString Security Bug
|
// Fix RakNet::RakString Security Bug
|
||||||
|
@ -161,10 +161,8 @@ static void position_info(Font *font, const int width, const int height) {
|
|||||||
void open_url(const std::string &url) {
|
void open_url(const std::string &url) {
|
||||||
int return_code;
|
int return_code;
|
||||||
const char *command[] = {"xdg-open", url.c_str(), nullptr};
|
const char *command[] = {"xdg-open", url.c_str(), nullptr};
|
||||||
char *output = run_command(command, &return_code, nullptr);
|
const std::vector<unsigned char> *output = run_command(command, &return_code);
|
||||||
if (output != nullptr) {
|
delete output;
|
||||||
free(output);
|
|
||||||
}
|
|
||||||
if (!is_exit_status_success(return_code)) {
|
if (!is_exit_status_success(return_code)) {
|
||||||
WARN("Unable To Open URL: %s", url.c_str());
|
WARN("Unable To Open URL: %s", url.c_str());
|
||||||
}
|
}
|
||||||
|
@ -44,10 +44,6 @@ static const char *get_username() {
|
|||||||
}
|
}
|
||||||
return username;
|
return username;
|
||||||
}
|
}
|
||||||
static char *safe_username = nullptr;
|
|
||||||
__attribute__((destructor)) static void _free_safe_username() {
|
|
||||||
free(safe_username);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int render_distance;
|
static int render_distance;
|
||||||
// Configure Options
|
// Configure Options
|
||||||
@ -162,8 +158,9 @@ void init_options() {
|
|||||||
if (strcmp(Strings::default_username, "StevePi") != 0) {
|
if (strcmp(Strings::default_username, "StevePi") != 0) {
|
||||||
ERR("Default Username Is Invalid");
|
ERR("Default Username Is Invalid");
|
||||||
}
|
}
|
||||||
safe_username = to_cp437(username);
|
std::string *safe_username = new std::string;
|
||||||
patch_address((void *) &Strings::default_username, (void *) safe_username);
|
*safe_username = to_cp437(username);
|
||||||
|
patch_address((void *) &Strings::default_username, (void *) safe_username->c_str());
|
||||||
|
|
||||||
// Disable Autojump By Default
|
// Disable Autojump By Default
|
||||||
if (feature_has("Disable Autojump By Default", server_disabled)) {
|
if (feature_has("Disable Autojump By Default", server_disabled)) {
|
||||||
@ -210,7 +207,7 @@ void init_options() {
|
|||||||
// Replace String
|
// Replace String
|
||||||
patch_address((void *) &Strings::feedback_vibration_options_txt_name, (void *) "gfx_ao");
|
patch_address((void *) &Strings::feedback_vibration_options_txt_name, (void *) "gfx_ao");
|
||||||
// Loading
|
// Loading
|
||||||
const unsigned char offset = (unsigned char) offsetof(Options, ambient_occlusion);
|
constexpr unsigned char offset = (unsigned char) offsetof(Options, ambient_occlusion);
|
||||||
unsigned char gfx_ao_loading_patch[4] = {offset, 0x10, 0x84, 0xe2}; // "add r1, r4, #OFFSET"
|
unsigned char gfx_ao_loading_patch[4] = {offset, 0x10, 0x84, 0xe2}; // "add r1, r4, #OFFSET"
|
||||||
patch((void *) 0x193b8, gfx_ao_loading_patch);
|
patch((void *) 0x193b8, gfx_ao_loading_patch);
|
||||||
// Saving
|
// Saving
|
||||||
@ -223,7 +220,7 @@ void init_options() {
|
|||||||
// Replace String
|
// Replace String
|
||||||
patch_address((void *) &Strings::gfx_lowquality_options_txt_name, (void *) "gfx_anaglyph");
|
patch_address((void *) &Strings::gfx_lowquality_options_txt_name, (void *) "gfx_anaglyph");
|
||||||
// Loading
|
// Loading
|
||||||
const unsigned char offset = (unsigned char) offsetof(Options, anaglyph_3d);
|
constexpr unsigned char offset = (unsigned char) offsetof(Options, anaglyph_3d);
|
||||||
unsigned char gfx_anaglyph_loading_patch[4] = {offset, 0x10, 0x84, 0xe2}; // "add r1, r4, #OFFSET"
|
unsigned char gfx_anaglyph_loading_patch[4] = {offset, 0x10, 0x84, 0xe2}; // "add r1, r4, #OFFSET"
|
||||||
patch((void *) 0x19400, gfx_anaglyph_loading_patch);
|
patch((void *) 0x19400, gfx_anaglyph_loading_patch);
|
||||||
// Disable Loading Side Effects
|
// Disable Loading Side Effects
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
#include <libreborn/libreborn.h>
|
#include <libreborn/libreborn.h>
|
||||||
#include <symbols/minecraft.h>
|
#include <symbols/minecraft.h>
|
||||||
|
@ -12,18 +12,18 @@
|
|||||||
#include <mods/home/home.h>
|
#include <mods/home/home.h>
|
||||||
#include <mods/init/init.h>
|
#include <mods/init/init.h>
|
||||||
|
|
||||||
// Hook access
|
// Hook Functions
|
||||||
HOOK(access, int, (const char *pathname, int mode)) {
|
#define HOOK_OPEN(name, return_type, mode_type) \
|
||||||
char *new_path = override_get_path(pathname);
|
HOOK(name, return_type, (const char *filename, mode_type mode)) { \
|
||||||
// Open File
|
const std::string new_path = override_get_path(filename); \
|
||||||
const int ret = real_access()(new_path != nullptr ? new_path : pathname, mode);
|
/* Open File */ \
|
||||||
// Free Data
|
return_type ret = real_##name()(!new_path.empty() ? new_path.c_str() : filename, mode); \
|
||||||
if (new_path != nullptr) {
|
/* Return */ \
|
||||||
free(new_path);
|
return ret; \
|
||||||
}
|
}
|
||||||
// Return
|
HOOK_OPEN(fopen, FILE *, const char *)
|
||||||
return ret;
|
HOOK_OPEN(fopen64, FILE *, const char *)
|
||||||
}
|
HOOK_OPEN(access, int, int)
|
||||||
|
|
||||||
// Get Override Folder
|
// Get Override Folder
|
||||||
static std::string get_override_directory() {
|
static std::string get_override_directory() {
|
||||||
@ -32,9 +32,9 @@ static std::string get_override_directory() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get Override Path For File (If It Exists)
|
// Get Override Path For File (If It Exists)
|
||||||
char *override_get_path(const char *filename) {
|
std::string override_get_path(std::string filename) {
|
||||||
// Custom Skin
|
// Custom Skin
|
||||||
if (starts_with(filename, "data/images/$")) {
|
if (filename.starts_with("data/images/$")) {
|
||||||
// Fallback Texture
|
// Fallback Texture
|
||||||
filename = "data/images/mob/char.png";
|
filename = "data/images/mob/char.png";
|
||||||
}
|
}
|
||||||
@ -42,12 +42,12 @@ char *override_get_path(const char *filename) {
|
|||||||
// Get Asset Override Path
|
// Get Asset Override Path
|
||||||
const std::string overrides = get_override_directory();
|
const std::string overrides = get_override_directory();
|
||||||
|
|
||||||
// Data Prefiix
|
// Data Prefix
|
||||||
const std::string data_prefix = "data/";
|
const std::string data_prefix = "data/";
|
||||||
int data_prefix_length = data_prefix.length();
|
const int data_prefix_length = data_prefix.length();
|
||||||
|
|
||||||
// Folders To Check
|
// Folders To Check
|
||||||
std::string asset_folders[] = {
|
const std::string asset_folders[] = {
|
||||||
overrides,
|
overrides,
|
||||||
getenv(_MCPI_REBORN_ASSETS_PATH_ENV),
|
getenv(_MCPI_REBORN_ASSETS_PATH_ENV),
|
||||||
getenv(_MCPI_VANILLA_ASSETS_PATH_ENV),
|
getenv(_MCPI_VANILLA_ASSETS_PATH_ENV),
|
||||||
@ -72,39 +72,7 @@ char *override_get_path(const char *filename) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Return
|
// Return
|
||||||
if (new_path.empty()) {
|
return new_path;
|
||||||
return nullptr;
|
|
||||||
} else {
|
|
||||||
char *ret = strdup(new_path.c_str());
|
|
||||||
ALLOC_CHECK(ret);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hook fopen
|
|
||||||
HOOK(fopen, FILE *, (const char *filename, const char *mode)) {
|
|
||||||
char *new_path = override_get_path(filename);
|
|
||||||
// Open File
|
|
||||||
FILE *file = real_fopen()(new_path != nullptr ? new_path : filename, mode);
|
|
||||||
// Free Data
|
|
||||||
if (new_path != nullptr) {
|
|
||||||
free(new_path);
|
|
||||||
}
|
|
||||||
// Return File
|
|
||||||
return file;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hook fopen64
|
|
||||||
HOOK(fopen64, FILE *, (const char *filename, const char *mode)) {
|
|
||||||
char *new_path = override_get_path(filename);
|
|
||||||
// Open File
|
|
||||||
FILE *file = real_fopen64()(new_path != nullptr ? new_path : filename, mode);
|
|
||||||
// Free Data
|
|
||||||
if (new_path != nullptr) {
|
|
||||||
free(new_path);
|
|
||||||
}
|
|
||||||
// Return File
|
|
||||||
return file;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Init
|
// Init
|
||||||
|
@ -57,9 +57,7 @@ static ServerPropertyTypes &get_property_types() {
|
|||||||
// Get World Name
|
// Get World Name
|
||||||
static std::string get_world_name() {
|
static std::string get_world_name() {
|
||||||
const std::string name = get_server_properties().get_string(get_property_types().world_name);
|
const std::string name = get_server_properties().get_string(get_property_types().world_name);
|
||||||
char *safe_name_c = to_cp437(name.c_str());
|
std::string safe_name = to_cp437(name);
|
||||||
std::string safe_name = safe_name_c;
|
|
||||||
free(safe_name_c);
|
|
||||||
return safe_name;
|
return safe_name;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -116,11 +114,9 @@ static std::vector<Player *> get_players_in_level(Level *level) {
|
|||||||
return level->players;
|
return level->players;
|
||||||
}
|
}
|
||||||
// Get Player's Username
|
// Get Player's Username
|
||||||
static std::string get_player_username(Player *player) {
|
static std::string get_player_username(const Player *player) {
|
||||||
const std::string *username = &player->username;
|
const std::string *username = &player->username;
|
||||||
char *safe_username_c = from_cp437(username->c_str());
|
std::string safe_username = from_cp437(*username);
|
||||||
std::string safe_username = safe_username_c;
|
|
||||||
free(safe_username_c);
|
|
||||||
return safe_username;
|
return safe_username;
|
||||||
}
|
}
|
||||||
// Get Level From Minecraft
|
// Get Level From Minecraft
|
||||||
@ -307,12 +303,9 @@ std::vector<ServerCommand> *server_get_commands(Minecraft *minecraft, ServerSide
|
|||||||
.callback = [server_side_network_handler](const std::string &cmd) {
|
.callback = [server_side_network_handler](const std::string &cmd) {
|
||||||
// Format Message
|
// Format Message
|
||||||
const std::string message = "[Server] " + cmd;
|
const std::string message = "[Server] " + cmd;
|
||||||
char *safe_message = to_cp437(message.c_str());
|
std::string cpp_string = to_cp437(message);
|
||||||
std::string cpp_string = safe_message;
|
|
||||||
// Post Message To Chat
|
// Post Message To Chat
|
||||||
server_side_network_handler->displayGameMessage(cpp_string);
|
server_side_network_handler->displayGameMessage(cpp_string);
|
||||||
// Free
|
|
||||||
free(safe_message);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
// List Players
|
// List Players
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
#include <cstdint>
|
||||||
|
|
||||||
#include <symbols/minecraft.h>
|
#include <symbols/minecraft.h>
|
||||||
#include <libreborn/libreborn.h>
|
#include <libreborn/libreborn.h>
|
||||||
|
|
||||||
|
@ -19,15 +19,14 @@
|
|||||||
// Loading Pending Skins
|
// Loading Pending Skins
|
||||||
struct pending_skin {
|
struct pending_skin {
|
||||||
int32_t texture_id;
|
int32_t texture_id;
|
||||||
char *data;
|
const std::vector<unsigned char> *data;
|
||||||
int size;
|
|
||||||
};
|
};
|
||||||
static std::vector<pending_skin> &get_pending_skins() {
|
static std::vector<pending_skin> &get_pending_skins() {
|
||||||
static std::vector<pending_skin> pending_skins;
|
static std::vector<pending_skin> pending_skins;
|
||||||
return pending_skins;
|
return pending_skins;
|
||||||
}
|
}
|
||||||
static pthread_mutex_t pending_skins_lock = PTHREAD_MUTEX_INITIALIZER;
|
static pthread_mutex_t pending_skins_lock = PTHREAD_MUTEX_INITIALIZER;
|
||||||
static void load_pending_skins(Minecraft *minecraft) {
|
static void load_pending_skins(const Minecraft *minecraft) {
|
||||||
// Lock
|
// Lock
|
||||||
pthread_mutex_lock(&pending_skins_lock);
|
pthread_mutex_lock(&pending_skins_lock);
|
||||||
|
|
||||||
@ -35,7 +34,7 @@ static void load_pending_skins(Minecraft *minecraft) {
|
|||||||
for (const pending_skin &skin : get_pending_skins()) {
|
for (const pending_skin &skin : get_pending_skins()) {
|
||||||
// Read PNG Info
|
// Read PNG Info
|
||||||
int width = 0, height = 0, channels = 0;
|
int width = 0, height = 0, channels = 0;
|
||||||
stbi_uc *img = stbi_load_from_memory((unsigned char *) skin.data, skin.size, &width, &height, &channels, STBI_rgb_alpha);
|
stbi_uc *img = stbi_load_from_memory(skin.data->data(), skin.data->size(), &width, &height, &channels, STBI_rgb_alpha);
|
||||||
if (width != SKIN_WIDTH || height != SKIN_HEIGHT) {
|
if (width != SKIN_WIDTH || height != SKIN_HEIGHT) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -54,7 +53,7 @@ static void load_pending_skins(Minecraft *minecraft) {
|
|||||||
|
|
||||||
// Free
|
// Free
|
||||||
for (const pending_skin &skin : get_pending_skins()) {
|
for (const pending_skin &skin : get_pending_skins()) {
|
||||||
free(skin.data);
|
delete skin.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clear
|
// Clear
|
||||||
@ -87,11 +86,10 @@ static void *loader_thread(void *user_data) {
|
|||||||
const std::string url = get_skin_server() + '/' + data->name + ".png";
|
const std::string url = get_skin_server() + '/' + data->name + ".png";
|
||||||
int return_code;
|
int return_code;
|
||||||
const char *command[] = {"wget", "-O", "-", url.c_str(), nullptr};
|
const char *command[] = {"wget", "-O", "-", url.c_str(), nullptr};
|
||||||
size_t output_size = 0;
|
const std::vector<unsigned char> *output = run_command(command, &return_code);
|
||||||
char *output = run_command(command, &return_code, &output_size);
|
|
||||||
|
|
||||||
// Check Success
|
// Check Success
|
||||||
if (output != nullptr && is_exit_status_success(return_code)) {
|
if (is_exit_status_success(return_code)) {
|
||||||
// Success
|
// Success
|
||||||
DEBUG("Downloaded Skin: %s", data->name.c_str());
|
DEBUG("Downloaded Skin: %s", data->name.c_str());
|
||||||
|
|
||||||
@ -99,14 +97,12 @@ static void *loader_thread(void *user_data) {
|
|||||||
pending_skin skin = {};
|
pending_skin skin = {};
|
||||||
skin.texture_id = data->texture_id;
|
skin.texture_id = data->texture_id;
|
||||||
skin.data = output;
|
skin.data = output;
|
||||||
skin.size = (int) output_size;
|
|
||||||
pthread_mutex_lock(&pending_skins_lock);
|
pthread_mutex_lock(&pending_skins_lock);
|
||||||
get_pending_skins().push_back(skin);
|
get_pending_skins().push_back(skin);
|
||||||
pthread_mutex_unlock(&pending_skins_lock);
|
pthread_mutex_unlock(&pending_skins_lock);
|
||||||
} else {
|
} else {
|
||||||
// Failure
|
// Failure
|
||||||
WARN("Failed To Download Skin: %s", data->name.c_str());
|
WARN("Failed To Download Skin: %s", data->name.c_str());
|
||||||
free(output);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Free
|
// Free
|
||||||
@ -120,7 +116,7 @@ static int32_t Textures_assignTexture_injection(Textures_assignTexture_t origina
|
|||||||
const int32_t id = original(textures, name, data);
|
const int32_t id = original(textures, name, data);
|
||||||
|
|
||||||
// Load Skin
|
// Load Skin
|
||||||
if (starts_with(name.c_str(), "$")) {
|
if (name.starts_with("$")) {
|
||||||
loader_data *user_data = new loader_data;
|
loader_data *user_data = new loader_data;
|
||||||
user_data->name = name.substr(1);
|
user_data->name = name.substr(1);
|
||||||
DEBUG("Loading Skin: %s", user_data->name.c_str());
|
DEBUG("Loading Skin: %s", user_data->name.c_str());
|
||||||
|
@ -24,31 +24,21 @@ std::string _sound_get_source_file() {
|
|||||||
// Resolve
|
// Resolve
|
||||||
|
|
||||||
// Get Path
|
// Get Path
|
||||||
char *path = strdup(SOURCE_FILE_BASE);
|
const std::string path = SOURCE_FILE_BASE;
|
||||||
ALLOC_CHECK(path);
|
const std::string full_path = override_get_path(path);
|
||||||
|
|
||||||
// Handle Overrides
|
|
||||||
char *overridden_full_path = override_get_path(path);
|
|
||||||
if (overridden_full_path != nullptr) {
|
|
||||||
free(path);
|
|
||||||
path = overridden_full_path;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check If Sound Exists
|
// Check If Sound Exists
|
||||||
if (access(path, F_OK) == -1) {
|
if (access(full_path.c_str(), F_OK) == -1) {
|
||||||
// Fail
|
// Fail
|
||||||
WARN("Audio Source File Doesn't Exist: " SOURCE_FILE_BASE);
|
WARN("Audio Source File Doesn't Exist: %s", path.c_str());
|
||||||
source.assign("");
|
source = "";
|
||||||
info_sound_data_state = "Missing";
|
info_sound_data_state = "Missing";
|
||||||
} else {
|
} else {
|
||||||
// Set
|
// Set
|
||||||
source.assign(path);
|
source = full_path;
|
||||||
info_sound_data_state = "Loaded";
|
info_sound_data_state = "Loaded";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Free
|
|
||||||
free(path);
|
|
||||||
|
|
||||||
// Mark As Loaded
|
// Mark As Loaded
|
||||||
source_loaded = true;
|
source_loaded = true;
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
#include <libreborn/libreborn.h>
|
#include <libreborn/libreborn.h>
|
||||||
#include <mods/extend/extend.h>
|
#include <mods/extend/extend.h>
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
#include <cstdint>
|
||||||
|
|
||||||
#include <libreborn/libreborn.h>
|
#include <libreborn/libreborn.h>
|
||||||
|
|
||||||
#include <mods/feature/feature.h>
|
#include <mods/feature/feature.h>
|
||||||
|
@ -253,9 +253,6 @@ target_include_directories(
|
|||||||
"$<INSTALL_INTERFACE:${MCPI_SDK_INCLUDE_DIR}/symbols>"
|
"$<INSTALL_INTERFACE:${MCPI_SDK_INCLUDE_DIR}/symbols>"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Disable C++11 String ABI
|
|
||||||
target_compile_definitions(symbols PUBLIC -D_GLIBCXX_USE_CXX11_ABI=0)
|
|
||||||
|
|
||||||
# Install
|
# Install
|
||||||
install(TARGETS symbols DESTINATION "${MCPI_LIB_DIR}")
|
install(TARGETS symbols DESTINATION "${MCPI_LIB_DIR}")
|
||||||
# SDK
|
# SDK
|
||||||
|
Loading…
Reference in New Issue
Block a user