minecraft-pi-reborn/launcher/src/bootstrap.c

459 lines
13 KiB
C
Raw Normal View History

2021-06-17 21:32:24 +00:00
#define _FILE_OFFSET_BITS 64
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <dirent.h>
#include <errno.h>
#include <sys/stat.h>
#include <libreborn/libreborn.h>
#include "bootstrap.h"
2022-03-25 02:47:34 +00:00
#include "patchelf.h"
2022-05-14 02:36:12 +00:00
#include "crash-report.h"
2021-06-17 21:32:24 +00:00
// Set Environmental Variable
#define PRESERVE_ENVIRONMENTAL_VARIABLE(name) \
{ \
char *original_env_value = getenv(name); \
if (original_env_value != NULL) { \
setenv("ORIGINAL_" name, original_env_value, 1); \
} \
}
static void trim(char **value) {
// Remove Trailing Colon
int length = strlen(*value);
if ((*value)[length - 1] == ':') {
(*value)[length - 1] = '\0';
}
if ((*value)[0] == ':') {
*value = &(*value)[1];
}
}
2022-03-10 03:08:47 +00:00
void set_and_print_env(const char *name, char *value) {
2021-06-17 21:32:24 +00:00
// Set Variable With No Trailing Colon
2022-03-10 03:08:47 +00:00
static const char *unmodified_name_prefix = "MCPI_";
2022-03-25 02:47:34 +00:00
if (!starts_with(name, unmodified_name_prefix)) {
2022-03-10 03:08:47 +00:00
trim(&value);
}
2021-06-17 21:32:24 +00:00
// Print New Value
2022-03-10 03:37:37 +00:00
DEBUG("Set %s = %s", name, value);
2021-06-17 21:32:24 +00:00
// Set The Value
setenv(name, value, 1);
}
2021-12-18 02:04:05 +00:00
#ifndef __ARM_ARCH
#define PASS_ENVIRONMENTAL_VARIABLE_TO_QEMU(name) \
{ \
char *old_value = getenv("QEMU_SET_ENV"); \
char *new_value = NULL; \
/* Pass Variable */ \
safe_asprintf(&new_value, "%s%s%s=%s", old_value == NULL ? "" : old_value, old_value == NULL ? "" : ",", name, getenv(name)); \
setenv("QEMU_SET_ENV", new_value, 1); \
free(new_value); \
/* Reset Variable */ \
RESET_ENVIRONMENTAL_VARIABLE(name); \
}
#endif
2021-06-17 21:32:24 +00:00
// Get Environmental Variable
static char *get_env_safe(const char *name) {
// Get Variable Or Blank String If Not Set
char *ret = getenv(name);
return ret != NULL ? ret : "";
}
// Get All Mods In Folder
static void load(char **ld_preload, char *folder) {
int folder_name_length = strlen(folder);
// Retry Until Successful
while (1) {
// Open Folder
DIR *dp = opendir(folder);
if (dp != NULL) {
// Loop Through Folder
struct dirent *entry = NULL;
errno = 0;
while (1) {
errno = 0;
entry = readdir(dp);
if (entry != NULL) {
// Check If File Is Regular
if (entry->d_type == DT_REG) {
// Get Full Name
int name_length = strlen(entry->d_name);
int total_length = folder_name_length + name_length;
char name[total_length + 1];
// Concatenate Folder Name And File Name
for (int i = 0; i < folder_name_length; i++) {
name[i] = folder[i];
}
for (int i = 0; i < name_length; i++) {
name[folder_name_length + i] = entry->d_name[i];
}
// Add Terminator
name[total_length] = '\0';
// Check If File Is Executable
int result = access(name, R_OK);
if (result == 0) {
// Add To LD_PRELOAD
string_append(ld_preload, ":%s", name);
} else if (result == -1 && errno != 0) {
// Fail
INFO("Unable To Acesss: %s: %s", name, strerror(errno));
errno = 0;
}
}
} else if (errno != 0) {
// Error Reading Contents Of Folder
ERR("Error Reading Directory: %s: %s", folder, strerror(errno));
} else {
// Done!
break;
}
}
// Close Folder
closedir(dp);
// Exit Function
return;
} else if (errno == ENOENT) {
// Folder Doesn't Exists, Attempt Creation
int ret = mkdir(folder, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
if (ret != 0) {
// Unable To Create Folder
ERR("Error Creating Directory: %s: %s", folder, strerror(errno));
}
// Continue Retrying
} else {
// Unable To Open Folder
ERR("Error Opening Directory: %s: %s", folder, strerror(errno));
}
}
}
2022-03-09 23:47:31 +00:00
#define MCPI_BINARY "minecraft-pi"
#define QEMU_BINARY "qemu-arm"
2021-06-17 21:32:24 +00:00
2022-05-14 02:36:12 +00:00
// Exit Handler
static void exit_handler(__attribute__((unused)) int signal_id) {
// Pass Signal To Child
murder_children();
while (wait(NULL) > 0) {}
_exit(EXIT_SUCCESS);
}
2022-03-10 04:29:37 +00:00
// Pre-Bootstrap
void pre_bootstrap(int argc, char *argv[]) {
2022-06-05 20:13:49 +00:00
// Disable stdout Buffering
setvbuf(stdout, NULL, _IONBF, 0);
// Print Version
for (int i = 1; i < argc; i++) {
if (strcmp(argv[i], "--version") == 0 || strcmp(argv[i], "-v") == 0) {
// Print
2022-06-10 01:31:40 +00:00
printf("Reborn v%s\n", MCPI_VERSION);
fflush(stdout);
exit(EXIT_SUCCESS);
}
}
2022-03-15 01:51:38 +00:00
// GTK Dark Mode
#ifndef MCPI_SERVER_MODE
2022-03-15 01:51:38 +00:00
set_and_print_env("GTK_THEME", "Adwaita:dark");
#endif
// Debug Zenity
{
const char *is_debug = getenv("MCPI_DEBUG");
if (is_debug != NULL && strlen(is_debug) > 0) {
set_and_print_env("ZENITY_DEBUG", "1");
}
}
2022-03-09 23:47:31 +00:00
// AppImage
#ifdef MCPI_IS_APPIMAGE_BUILD
2022-05-14 02:36:12 +00:00
{
char *owd = getenv("OWD");
if (owd != NULL && chdir(owd) != 0) {
ERR("AppImage: Unable To Fix Current Directory: %s", strerror(errno));
}
2022-03-09 23:47:31 +00:00
}
#endif
2022-03-12 01:02:38 +00:00
// Get Binary Directory
char *binary_directory = get_binary_directory();
// Configure PATH
{
// Add Library Directory
char *new_path;
safe_asprintf(&new_path, "%s/bin", binary_directory);
// Add Existing PATH
{
char *value = get_env_safe("PATH");
if (strlen(value) > 0) {
string_append(&new_path, ":%s", value);
}
}
// Set And Free
set_and_print_env("PATH", new_path);
free(new_path);
}
// Free Binary Directory
free(binary_directory);
2022-05-14 02:36:12 +00:00
// Setup Crash Reports
setup_crash_report();
// Install Signal Handlers
struct sigaction act_sigint;
memset((void *) &act_sigint, 0, sizeof (struct sigaction));
act_sigint.sa_flags = SA_RESTART;
act_sigint.sa_handler = &exit_handler;
sigaction(SIGINT, &act_sigint, NULL);
struct sigaction act_sigterm;
memset((void *) &act_sigterm, 0, sizeof (struct sigaction));
act_sigterm.sa_flags = SA_RESTART;
act_sigterm.sa_handler = &exit_handler;
sigaction(SIGTERM, &act_sigterm, NULL);
2022-03-10 04:29:37 +00:00
}
2022-06-25 21:30:08 +00:00
// Copy SDK Into ~/.minecraft-pi
static void run_simple_command(const char *const command[], const char *error) {
int status = 0;
char *output = run_command(command, &status);
if (output != NULL) {
free(output);
}
if (!is_exit_status_success(status)) {
ERR("%s", error);
}
}
static void copy_sdk(char *binary_directory) {
// Output Directory
char *output = NULL;
safe_asprintf(&output, "%s" HOME_SUBDIRECTORY_FOR_GAME_DATA "/sdk/" MCPI_SDK_DIR, getenv("HOME"));
// Source Directory
char *source = NULL;
safe_asprintf(&source, "%s/sdk/.", binary_directory);
// Clean
{
const char *const command[] = {"rm", "-rf", output, NULL};
run_simple_command(command, "Unable To Clean SDK Output Directory");
}
// Make Directory
{
const char *const command[] = {"mkdir", "-p", output, NULL};
run_simple_command(command, "Unable To Create SDK Output Directory");
}
// Copy
{
const char *const command[] = {"cp", "-ar", source, output, NULL};
run_simple_command(command, "Unable To Copy SDK");
}
// Free
free(output);
free(source);
}
2022-03-10 04:29:37 +00:00
// Bootstrap
void bootstrap(int argc, char *argv[]) {
2022-04-15 01:12:42 +00:00
INFO("Configuring Game...");
2022-03-09 23:47:31 +00:00
2021-06-17 21:32:24 +00:00
// Get Binary Directory
char *binary_directory = get_binary_directory();
2022-06-25 21:30:08 +00:00
// Copy SDK
copy_sdk(binary_directory);
2022-05-30 02:54:57 +00:00
// Set MCPI_REBORN_ASSETS_PATH
{
char *assets_path = realpath("/proc/self/exe", NULL);
ALLOC_CHECK(assets_path);
chop_last_component(&assets_path);
string_append(&assets_path, "/data");
set_and_print_env("MCPI_REBORN_ASSETS_PATH", assets_path);
free(assets_path);
}
2022-03-25 02:47:34 +00:00
// Resolve Binary Path & Set MCPI_DIRECTORY
2022-05-30 02:54:57 +00:00
char *resolved_path = NULL;
2022-03-25 02:47:34 +00:00
{
// Log
2022-04-15 01:12:42 +00:00
DEBUG("Resolving File Paths...");
2022-03-25 02:47:34 +00:00
// Resolve Full Binary Path
char *full_path = NULL;
safe_asprintf(&full_path, "%s/" MCPI_BINARY, binary_directory);
2022-05-30 02:54:57 +00:00
resolved_path = realpath(full_path, NULL);
2022-03-25 02:47:34 +00:00
ALLOC_CHECK(resolved_path);
free(full_path);
}
// Fix MCPI Dependencies
2022-06-04 02:25:22 +00:00
char new_mcpi_exe_path[] = MCPI_PATCHED_DIR "/XXXXXX";
2022-03-25 02:47:34 +00:00
{
// Log
2022-04-15 01:12:42 +00:00
DEBUG("Patching ELF Dependencies...");
2022-03-25 02:47:34 +00:00
// Find Linker
char *linker = NULL;
2022-05-03 02:44:10 +00:00
// Select Linker
2022-05-11 22:24:03 +00:00
#ifdef MCPI_BUNDLE_ARMHF_SYSROOT
2022-05-03 02:44:10 +00:00
// Use ARM Sysroot Linker
safe_asprintf(&linker, "%s/sysroot/lib/ld-linux-armhf.so.3", binary_directory);
#else
// Use Current Linker
char *exe = realpath("/proc/self/exe", NULL);
ALLOC_CHECK(exe);
linker = patch_get_interpreter(exe);
free(exe);
2022-03-25 02:47:34 +00:00
#endif
// Patch
2022-06-04 02:25:22 +00:00
patch_mcpi_elf_dependencies(resolved_path, new_mcpi_exe_path, linker);
2022-03-25 02:47:34 +00:00
// Free Linker Path
2022-04-15 01:12:42 +00:00
if (linker != NULL) {
free(linker);
}
2022-03-25 02:47:34 +00:00
// Verify
2022-06-04 02:25:22 +00:00
if (!starts_with(new_mcpi_exe_path, MCPI_PATCHED_DIR)) {
2022-03-25 02:47:34 +00:00
IMPOSSIBLE();
}
}
2022-05-30 02:54:57 +00:00
// Set MCPI_VANILLA_ASSETS_PATH
{
char *assets_path = strdup(resolved_path);
ALLOC_CHECK(assets_path);
chop_last_component(&assets_path);
string_append(&assets_path, "/data");
set_and_print_env("MCPI_VANILLA_ASSETS_PATH", assets_path);
free(assets_path);
}
// Free Resolved Path
free(resolved_path);
2021-06-17 21:32:24 +00:00
// Configure LD_LIBRARY_PATH
{
2022-03-25 02:47:34 +00:00
// Log
2022-04-15 01:12:42 +00:00
DEBUG("Setting Linker Search Paths...");
2022-03-25 02:47:34 +00:00
2022-03-09 23:47:31 +00:00
// Preserve
2021-06-17 21:32:24 +00:00
PRESERVE_ENVIRONMENTAL_VARIABLE("LD_LIBRARY_PATH");
2022-03-09 23:47:31 +00:00
char *new_ld_path = NULL;
2021-06-17 21:32:24 +00:00
// Add Library Directory
safe_asprintf(&new_ld_path, "%s/lib", binary_directory);
2022-03-09 23:47:31 +00:00
2022-05-11 22:24:03 +00:00
// Add ARM Sysroot Libraries (Ensure Priority) (Ignore On Actual ARM System)
#ifdef MCPI_BUNDLE_ARMHF_SYSROOT
string_append(&new_ld_path, ":%s/sysroot/lib:%s/sysroot/lib/arm-linux-gnueabihf:%s/sysroot/usr/lib:%s/sysroot/usr/lib/arm-linux-gnueabihf", binary_directory, binary_directory, binary_directory, binary_directory);
2022-05-03 02:44:10 +00:00
#endif
2022-03-09 23:47:31 +00:00
2022-04-10 00:01:16 +00:00
// Add LD_LIBRARY_PATH
2021-06-17 21:32:24 +00:00
{
2022-03-25 02:47:34 +00:00
char *value = get_env_safe("LD_LIBRARY_PATH");
2021-06-17 21:32:24 +00:00
if (strlen(value) > 0) {
string_append(&new_ld_path, ":%s", value);
}
}
2022-03-09 23:47:31 +00:00
2021-06-17 21:32:24 +00:00
// Set And Free
set_and_print_env("LD_LIBRARY_PATH", new_ld_path);
free(new_ld_path);
}
// Configure LD_PRELOAD
{
2022-03-25 02:47:34 +00:00
// Log
2022-04-15 01:12:42 +00:00
DEBUG("Locating Mods...");
2022-03-25 02:47:34 +00:00
2022-03-09 23:47:31 +00:00
// Preserve
2021-06-17 21:32:24 +00:00
PRESERVE_ENVIRONMENTAL_VARIABLE("LD_PRELOAD");
char *new_ld_preload = NULL;
2022-05-01 04:25:29 +00:00
// ~/.minecraft-pi/mods
{
// Get Mods Folder
char *mods_folder = NULL;
2022-05-01 04:25:29 +00:00
safe_asprintf(&mods_folder, "%s" HOME_SUBDIRECTORY_FOR_GAME_DATA "/mods/", getenv("HOME"));
// Load Mods From ./mods
load(&new_ld_preload, mods_folder);
// Free Mods Folder
free(mods_folder);
}
2022-05-01 04:25:29 +00:00
// Built-In Mods
{
// Get Mods Folder
char *mods_folder = NULL;
2022-05-01 04:25:29 +00:00
safe_asprintf(&mods_folder, "%s/mods/", binary_directory);
// Load Mods From ./mods
load(&new_ld_preload, mods_folder);
// Free Mods Folder
free(mods_folder);
}
2021-06-17 21:32:24 +00:00
2022-04-20 22:21:29 +00:00
// Add LD_PRELOAD
{
char *value = get_env_safe("LD_PRELOAD");
if (strlen(value) > 0) {
string_append(&new_ld_preload, ":%s", value);
}
}
2021-06-17 21:32:24 +00:00
// Set LD_PRELOAD
set_and_print_env("LD_PRELOAD", new_ld_preload);
free(new_ld_preload);
}
// Free Binary Directory
free(binary_directory);
2022-03-09 23:47:31 +00:00
// Start Game
2022-04-15 01:12:42 +00:00
INFO("Starting Game...");
2022-03-09 23:47:31 +00:00
// Arguments
2022-03-25 02:47:34 +00:00
int argv_start = 1; // argv = &new_args[argv_start]
const char *new_args[argv_start /* 1 Potential Prefix Argument (QEMU) */ + argc + 1 /* NULL-Terminator */]; //
2022-03-09 23:47:31 +00:00
// Copy Existing Arguments
for (int i = 1; i < argc; i++) {
new_args[i + argv_start] = argv[i];
}
// NULL-Terminator
new_args[argv_start + argc] = NULL;
// Set Executable Argument
2022-06-04 02:25:22 +00:00
new_args[argv_start] = new_mcpi_exe_path;
2022-03-09 23:47:31 +00:00
// Non-ARM Systems Need QEMU
#ifndef __ARM_ARCH
argv_start--;
new_args[argv_start] = QEMU_BINARY;
2021-12-18 02:04:05 +00:00
// Prevent QEMU From Being Modded
PASS_ENVIRONMENTAL_VARIABLE_TO_QEMU("LD_LIBRARY_PATH");
PASS_ENVIRONMENTAL_VARIABLE_TO_QEMU("LD_PRELOAD");
2022-03-09 23:47:31 +00:00
#endif
// Run
2022-03-14 23:09:25 +00:00
const char **new_argv = &new_args[argv_start];
safe_execvpe(new_argv, (const char *const *) environ);
2021-06-17 21:32:24 +00:00
}