diff --git a/CMakeLists.txt b/CMakeLists.txt index 4c090fb19d..679036c055 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -62,6 +62,13 @@ string(CONCAT COMPILE_FLAGS_SETUP # Skip RPath "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}") # Fast Math @@ -79,7 +86,7 @@ if(CMAKE_C_COMPILER_ID STREQUAL "GNU") endif() endif() -# Buld Dependencies +# Build Dependencies add_subdirectory(dependencies) # Build libreborn @@ -117,10 +124,10 @@ endif() if(BUILD_ARM_COMPONENTS) install(EXPORT sdk DESTINATION "${MCPI_SDK_DIR}" FILE "sdk-targets.cmake" EXPORT_LINK_INTERFACE_LIBRARIES) file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/sdk.cmake" + # Sanity Check + "${ARM_SANITY_CHECK}\n" # Compile Flags "${COMPILE_FLAGS_SETUP}\n" - # Snaity Check - "${ARM_SANITY_CHECK}\n" # Log "message(STATUS \"Using Reborn SDK v${MCPI_VERSION}\")\n" # Include Targets diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index d06e7355bf..df84563530 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -2,12 +2,15 @@ project(launcher) # Launcher add_executable(launcher - src/bootstrap.cpp - src/patchelf.cpp - src/util.cpp - src/crash-report.cpp - src/sdk.cpp - src/mods.cpp + src/bootstrap/bootstrap.cpp + src/bootstrap/mods.cpp + src/bootstrap/assets.cpp + src/bootstrap/patchelf.cpp + src/bootstrap/debug.cpp + src/util/util.cpp + src/util/sdk.cpp + src/logger/logger.cpp + src/logger/crash-report.cpp src/options/parser.cpp src/main.cpp src/client/configuration.cpp @@ -19,6 +22,8 @@ target_link_libraries(launcher reborn-util LIB_LIEF trampoline-headers) # RPath set_target_properties(launcher PROPERTIES INSTALL_RPATH "$ORIGIN/lib/native") target_link_options(launcher PRIVATE "LINKER:--disable-new-dtags") +# Files +target_compile_definitions(launcher PRIVATE _FILE_OFFSET_BITS=64) # Install install(TARGETS launcher DESTINATION "${MCPI_INSTALL_DIR}") diff --git a/launcher/src/bootstrap.cpp b/launcher/src/bootstrap.cpp deleted file mode 100644 index 12bb22eaf3..0000000000 --- a/launcher/src/bootstrap.cpp +++ /dev/null @@ -1,190 +0,0 @@ -#define _FILE_OFFSET_BITS 64 - -#include -#include - -#include - -#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 mcpi_ld_preload; - { - // Log - DEBUG("Locating Mods..."); - - // ARM Components - mcpi_ld_preload = bootstrap_mods(binary_directory); - } - - // Configure Library Search Path - std::vector 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 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::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); -} diff --git a/launcher/src/bootstrap.h b/launcher/src/bootstrap.h deleted file mode 100644 index 2982a51a94..0000000000 --- a/launcher/src/bootstrap.h +++ /dev/null @@ -1,10 +0,0 @@ -#pragma once - -#include -#include - -#include "options/parser.h" - -void bootstrap(const options_t &options); -void copy_sdk(const std::string &binary_directory, bool log_with_debug); -std::vector bootstrap_mods(const std::string &binary_directory); diff --git a/launcher/src/bootstrap/assets.cpp b/launcher/src/bootstrap/assets.cpp new file mode 100644 index 0000000000..8fdda31a0c --- /dev/null +++ b/launcher/src/bootstrap/assets.cpp @@ -0,0 +1,15 @@ +#include + +#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); +} \ No newline at end of file diff --git a/launcher/src/bootstrap/bootstrap.cpp b/launcher/src/bootstrap/bootstrap.cpp new file mode 100644 index 0000000000..29c3c8a236 --- /dev/null +++ b/launcher/src/bootstrap/bootstrap.cpp @@ -0,0 +1,75 @@ +#include +#include + +#include + +#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 mcpi_ld_preload = bootstrap_mods(binary_directory); + + // Configure Library Search Path + DEBUG("Setting Linker Search Paths..."); + const std::vector 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 args { +#ifdef MCPI_BUILD_RUNTIME + "runtime", +#endif + new_mcpi_exe_path + }; + + // Run + const char *new_argv[args.size() + 1]; + for (std::vector::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); +} diff --git a/launcher/src/bootstrap/bootstrap.h b/launcher/src/bootstrap/bootstrap.h new file mode 100644 index 0000000000..9edf448e92 --- /dev/null +++ b/launcher/src/bootstrap/bootstrap.h @@ -0,0 +1,20 @@ +#pragma once + +#include +#include + +#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 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 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 &rpath, const std::vector &mods); \ No newline at end of file diff --git a/launcher/src/bootstrap/debug.cpp b/launcher/src/bootstrap/debug.cpp new file mode 100644 index 0000000000..d87adafbd0 --- /dev/null +++ b/launcher/src/bootstrap/debug.cpp @@ -0,0 +1,43 @@ +#include + +#include "bootstrap.h" + +// Debug Information +static void run_debug_command(const char *const command[], const char *prefix) { + int status = 0; + const std::vector *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); +} \ No newline at end of file diff --git a/launcher/src/mods.cpp b/launcher/src/bootstrap/mods.cpp similarity index 100% rename from launcher/src/mods.cpp rename to launcher/src/bootstrap/mods.cpp diff --git a/launcher/src/patchelf.cpp b/launcher/src/bootstrap/patchelf.cpp similarity index 73% rename from launcher/src/patchelf.cpp rename to launcher/src/bootstrap/patchelf.cpp index 8ac235f741..b4d3a83820 100644 --- a/launcher/src/patchelf.cpp +++ b/launcher/src/bootstrap/patchelf.cpp @@ -9,7 +9,7 @@ #include -#include "patchelf.h" +#include "bootstrap.h" // Duplicate MCPI Executable Into /tmp 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); // Load Binary - std::unique_ptr binary = LIEF::ELF::Parser::parse(original_path); + const std::unique_ptr binary = LIEF::ELF::Parser::parse(original_path); // Set Interpreter - if (!interpreter.empty()) { - binary->interpreter(interpreter); - } + binary->interpreter(interpreter); // Remove Existing Needed Libraries std::vector 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)); } } + +// 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 get_ld_path(const std::string &binary_directory) { + std::vector 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; +} \ No newline at end of file diff --git a/launcher/src/client/cache.h b/launcher/src/client/cache.h index 3e05c16498..1637acbda7 100644 --- a/launcher/src/client/cache.h +++ b/launcher/src/client/cache.h @@ -7,11 +7,11 @@ #define CACHE_VERSION 0 // Load Cache -typedef struct { +struct launcher_cache { std::string username; std::string render_distance; std::unordered_map feature_flags; -} launcher_cache; +}; extern launcher_cache empty_cache; launcher_cache load_cache(); diff --git a/launcher/src/client/configuration.cpp b/launcher/src/client/configuration.cpp index bd6578b39b..b3dab56aaa 100644 --- a/launcher/src/client/configuration.cpp +++ b/launcher/src/client/configuration.cpp @@ -9,7 +9,7 @@ #include -#include "../util.h" +#include "../util/util.h" #include "configuration.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(); // Run int return_code; - char *output = run_command(command, &return_code, nullptr); - if (output != nullptr) { - // Trim - const size_t length = strlen(output); - if (output[length - 1] == '\n') { - output[length - 1] = '\0'; - } - // Set - set_and_print_env(env_name, output); - // Free - free(output); + const std::vector *output = run_command(command, &return_code); + 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(); } + // Set + set_and_print_env(env_name, output_str.c_str()); // Check Return Code if (!is_exit_status_success(return_code)) { // Launch Interrupted @@ -161,7 +159,7 @@ void configure_client(const options_t &options) { if (options.use_default) { // Use Default Feature Flags 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) { bool value; // Strip Default Value diff --git a/launcher/src/crash-report.cpp b/launcher/src/crash-report.cpp deleted file mode 100644 index 3f9f7924bc..0000000000 --- a/launcher/src/crash-report.cpp +++ /dev/null @@ -1,259 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#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 Discord server! If you believe this is a problem with " MCPI_APP_TITLE " itself, please upload this crash report to the #bugs Discord channel.", - "--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); - } -} diff --git a/launcher/src/crash-report.h b/launcher/src/crash-report.h deleted file mode 100644 index 4042a60d8d..0000000000 --- a/launcher/src/crash-report.h +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -#ifdef __cplusplus -extern "C" { -#endif - -void setup_crash_report(); - -#ifdef __cplusplus -} -#endif diff --git a/launcher/src/logger/crash-report.cpp b/launcher/src/logger/crash-report.cpp new file mode 100644 index 0000000000..155de74c24 --- /dev/null +++ b/launcher/src/logger/crash-report.cpp @@ -0,0 +1,35 @@ +#include + +#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 Discord server! If you believe this is a problem with " MCPI_APP_TITLE " itself, please upload this crash report to the #bugs Discord channel.", + "--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); + } +} \ No newline at end of file diff --git a/launcher/src/logger/logger.cpp b/launcher/src/logger/logger.cpp new file mode 100644 index 0000000000..a100c24c89 --- /dev/null +++ b/launcher/src/logger/logger.cpp @@ -0,0 +1,142 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#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 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); + } +} diff --git a/launcher/src/logger/logger.h b/launcher/src/logger/logger.h new file mode 100644 index 0000000000..5989ac0e13 --- /dev/null +++ b/launcher/src/logger/logger.h @@ -0,0 +1,4 @@ +#pragma once + +void setup_logger(); +void show_report(const char *log_filename); \ No newline at end of file diff --git a/launcher/src/main.cpp b/launcher/src/main.cpp index 43f8d7d6f9..e20d4f0625 100644 --- a/launcher/src/main.cpp +++ b/launcher/src/main.cpp @@ -1,11 +1,10 @@ #include #include -#include -#include "bootstrap.h" +#include "bootstrap/bootstrap.h" #include "options/parser.h" -#include "crash-report.h" -#include "util.h" +#include "logger/logger.h" +#include "util/util.h" #include "client/configuration.h" // Bind Options To Environmental Variable @@ -45,7 +44,8 @@ static void setup_environment(const options_t &options) { } // 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_profile_directory = realpath(custom_profile_directory, nullptr); ALLOC_CHECK(custom_profile_directory); @@ -86,8 +86,8 @@ static void start_game(const options_t &options) { setvbuf(stdout, nullptr, _IONBF, 0); // Setup Crash Reporting - if (!options.disable_crash_report) { - setup_crash_report(); + if (!options.disable_logger) { + setup_logger(); } // Configure Client Options diff --git a/launcher/src/options/option-list.h b/launcher/src/options/option-list.h index 52329aba14..df7636f502 100644 --- a/launcher/src/options/option-list.h +++ b/launcher/src/options/option-list.h @@ -1,6 +1,6 @@ OPTION(debug, "debug", 'd', "Enable Debug Logging") 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(no_cache, "no-cache", -4, "Disable Client-Mode Configuration Cache") OPTION(wipe_cache, "wipe-cache", -5, "Wipe Cached Client-Mode Configuration And Exit") diff --git a/launcher/src/patchelf.h b/launcher/src/patchelf.h deleted file mode 100644 index 55ee213c95..0000000000 --- a/launcher/src/patchelf.h +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once - -#include -#include - -#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 &rpath, const std::vector &mods); diff --git a/launcher/src/sdk.cpp b/launcher/src/util/sdk.cpp similarity index 97% rename from launcher/src/sdk.cpp rename to launcher/src/util/sdk.cpp index a28e61ffb2..e801f310e6 100644 --- a/launcher/src/sdk.cpp +++ b/launcher/src/util/sdk.cpp @@ -1,6 +1,6 @@ #include -#include "bootstrap.h" +#include "../bootstrap/bootstrap.h" #include "util.h" // Log diff --git a/launcher/src/util.cpp b/launcher/src/util/util.cpp similarity index 89% rename from launcher/src/util.cpp rename to launcher/src/util/util.cpp index 849f79094f..cbea1560c0 100644 --- a/launcher/src/util.cpp +++ b/launcher/src/util/util.cpp @@ -5,10 +5,8 @@ // Simpler Version Of run_command() void run_simple_command(const char *const command[], const char *error) { int status = 0; - char *output = run_command(command, &status, nullptr); - if (output != nullptr) { - free(output); - } + const std::vector *output = run_command(command, &status); + delete output; if (!is_exit_status_success(status)) { ERR("%s", error); } diff --git a/launcher/src/util.h b/launcher/src/util/util.h similarity index 76% rename from launcher/src/util.h rename to launcher/src/util/util.h index 63e0aae71f..4bffa54b9f 100644 --- a/launcher/src/util.h +++ b/launcher/src/util/util.h @@ -7,3 +7,5 @@ void run_simple_command(const char *const command[], const char *error); void chop_last_component(std::string &str); std::string safe_realpath(const std::string &path); std::string get_binary_directory(); + +void copy_sdk(const std::string &binary_directory, bool log_with_debug); \ No newline at end of file diff --git a/libreborn/CMakeLists.txt b/libreborn/CMakeLists.txt index 9c44a61921..0b54d7740c 100644 --- a/libreborn/CMakeLists.txt +++ b/libreborn/CMakeLists.txt @@ -6,12 +6,12 @@ configure_file(include/libreborn/config.h.in "${CMAKE_CURRENT_BINARY_DIR}/includ # Util add_library(reborn-util SHARED - src/util/exec.c - src/util/string.c - src/util/util.c - src/util/log.c + src/util/exec.cpp + src/util/string.cpp + src/util/util.cpp + src/util/log.cpp src/util/cp437.cpp - src/util/env.c + src/util/env.cpp ) target_include_directories( reborn-util diff --git a/libreborn/include/libreborn/env.h b/libreborn/include/libreborn/env.h index 7145d78604..d74aed37ce 100644 --- a/libreborn/include/libreborn/env.h +++ b/libreborn/include/libreborn/env.h @@ -1,16 +1,11 @@ #pragma once -#ifdef __cplusplus -extern "C" { -#endif - #define ENV(name, ...) extern const char *const name##_ENV; #include "env-list.h" #undef ENV -int is_env_var_internal(const char *env); +bool is_env_var_internal(const char *env); void clear_internal_env_vars(); -#ifdef __cplusplus -} -#endif \ No newline at end of file +// Set Environmental Variable +void set_and_print_env(const char *name, const char *value); \ No newline at end of file diff --git a/libreborn/include/libreborn/exec.h b/libreborn/include/libreborn/exec.h index e90b132066..491e519e1f 100644 --- a/libreborn/include/libreborn/exec.h +++ b/libreborn/include/libreborn/exec.h @@ -1,23 +1,21 @@ #pragma once -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include -#include "log.h" -#include "string.h" -#include "util.h" - -#ifdef __cplusplus -extern "C" { -#endif - -// Set Environmental Variable -void set_and_print_env(const char *name, const char *value); +// fork() With I/O +struct Process { + static constexpr int fd_count = 3; + Process(pid_t pid_, std::array fds_); + [[nodiscard]] int close() const; + const pid_t pid; + const std::array fds; +}; +std::optional fork_with_stdio(); +void poll_fds(const std::vector &fds, const std::function &on_data); // Safe execvpe() __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) " // Run Command And Get Output -char *run_command(const char *const command[], int *exit_status, size_t *output_size); +std::vector *run_command(const char *const command[], int *exit_status); #define is_exit_status_success(status) (WIFEXITED(status) && WEXITSTATUS(status) == 0) // Get Exit Status String -void get_exit_status_string(int status, char **out); - -#ifdef __cplusplus -} -#endif +std::string get_exit_status_string(int status); \ No newline at end of file diff --git a/libreborn/include/libreborn/log.h b/libreborn/include/libreborn/log.h index 27c57392d9..b833bb73f4 100644 --- a/libreborn/include/libreborn/log.h +++ b/libreborn/include/libreborn/log.h @@ -1,11 +1,7 @@ #pragma once -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif +#include +#include // Log File int reborn_get_log_fd(); @@ -29,7 +25,3 @@ int reborn_get_debug_fd(); WARN(__VA_ARGS__); \ } \ } - -#ifdef __cplusplus -} -#endif diff --git a/libreborn/include/libreborn/patch.h b/libreborn/include/libreborn/patch.h index 56ad38f294..eff308e6e9 100644 --- a/libreborn/include/libreborn/patch.h +++ b/libreborn/include/libreborn/patch.h @@ -1,11 +1,10 @@ #pragma once -// Patching Functions - -#if defined(REBORN_HAS_PATCH_CODE) && defined(__cplusplus) - #include +// Patching Functions +#if defined(REBORN_HAS_PATCH_CODE) + // Init void reborn_init_patch(); diff --git a/libreborn/include/libreborn/string.h b/libreborn/include/libreborn/string.h index b809cb7d6e..5f69f9d2b1 100644 --- a/libreborn/include/libreborn/string.h +++ b/libreborn/include/libreborn/string.h @@ -1,19 +1,10 @@ #pragma once -#ifdef __cplusplus -extern "C" { -#endif +#include // 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 -char *to_cp437(const char *input); -char *from_cp437(const char *input); - -// Starts With -int starts_with(const char *str, const char *prefix); - -#ifdef __cplusplus -} -#endif +std::string to_cp437(const std::string &input); +std::string from_cp437(const std::string &input); \ No newline at end of file diff --git a/libreborn/include/libreborn/util.h b/libreborn/include/libreborn/util.h index 25a883c50c..0abeb0c02c 100644 --- a/libreborn/include/libreborn/util.h +++ b/libreborn/include/libreborn/util.h @@ -1,16 +1,17 @@ #pragma once #include -#include -#include +#include +#include #include +#include #include "log.h" // Check Memory Allocation #define ALLOC_CHECK(obj) \ { \ - if (obj == NULL) { \ + if ((obj) == nullptr) { \ ERR("Memory Allocation Failed"); \ } \ } @@ -41,23 +42,18 @@ } \ return func; \ } -#ifdef __cplusplus -#define hooked_function_setup extern "C" -#else -#define hooked_function_setup -#endif #define HOOK(name, return_type, args) \ EXTERNAL_FUNC(name, return_type, args) \ - hooked_function_setup __attribute__((__used__)) return_type name args - -#ifdef __cplusplus -extern "C" { -#endif + extern "C" __attribute__((__used__)) return_type name args // 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 -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 int lock_file(const char *file); @@ -65,8 +61,8 @@ void unlock_file(const char *file, int fd); // Access Configuration At Runtime const char *reborn_get_version(); -int reborn_is_headless(); -int reborn_is_server(); +bool reborn_is_headless(); +bool reborn_is_server(); // Check $DISPLAY void reborn_check_display(); @@ -77,6 +73,5 @@ const char *get_home_subdirectory_for_game_data(); // Make Sure Directory Exists void ensure_directory(const char *path); -#ifdef __cplusplus -} -#endif +// Safe write() +void safe_write(int fd, const void *buf, size_t size); \ No newline at end of file diff --git a/libreborn/src/util/cp437.cpp b/libreborn/src/util/cp437.cpp index bb9062f2c9..1f44c3b18e 100644 --- a/libreborn/src/util/cp437.cpp +++ b/libreborn/src/util/cp437.cpp @@ -1,5 +1,4 @@ #include -#include #include "utf8.h" @@ -48,22 +47,22 @@ static uint32_t *get_cp437_characters_codepoint_map() { } return map; } -char *to_cp437(const char *input) { +std::string to_cp437(const std::string &input) { // Convert To UTF-32 For Easier Parsing - std::u32string utf32_str = to_utf32(input); + const std::u32string utf32_str = to_utf32(input); // Allocate String std::string cp437_str; // Handle Characters 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; 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) { valid = true; - cp437_str += j; + cp437_str += char(j); break; } } @@ -73,19 +72,18 @@ char *to_cp437(const char *input) { } // 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 - std::string input = raw_input; std::u32string utf32_str; // Handle Characters for (size_t i = 0; i < input.length(); i++) { - unsigned char c = (unsigned char) input[i]; - utf32_str += get_cp437_characters_codepoint_map()[(uint32_t) c]; + const unsigned char c = (unsigned char) input[i]; + utf32_str += char32_t(get_cp437_characters_codepoint_map()[(uint32_t) c]); } // Convert To UTF-8 - return strdup(to_utf8(utf32_str).c_str()); + return to_utf8(utf32_str); } diff --git a/libreborn/src/util/env.c b/libreborn/src/util/env.c deleted file mode 100644 index f771a8af75..0000000000 --- a/libreborn/src/util/env.c +++ /dev/null @@ -1,20 +0,0 @@ -#include -#include - -// Define Constants -#define ENV(name, ...) const char *const name##_ENV = #name; -#include -#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 -#undef ENV -} \ No newline at end of file diff --git a/libreborn/src/util/env.cpp b/libreborn/src/util/env.cpp new file mode 100644 index 0000000000..5744ade959 --- /dev/null +++ b/libreborn/src/util/env.cpp @@ -0,0 +1,38 @@ +#include +#include +#include + +// Define Constants +#define ENV(name, ...) const char *const name##_ENV = #name; +#include +#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 +#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)"); +} \ No newline at end of file diff --git a/libreborn/src/util/exec.c b/libreborn/src/util/exec.c deleted file mode 100644 index ead1683c10..0000000000 --- a/libreborn/src/util/exec.c +++ /dev/null @@ -1,151 +0,0 @@ -#include -#include - -#include - -// 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"); - } - } -} diff --git a/libreborn/src/util/exec.cpp b/libreborn/src/util/exec.cpp new file mode 100644 index 0000000000..b0175c5627 --- /dev/null +++ b/libreborn/src/util/exec.cpp @@ -0,0 +1,176 @@ +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +// Fork +Process::Process(const pid_t pid_, const std::array 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 fork_with_stdio() { + // Store Output + const std::array 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 &fds, const std::function &on_data) { + // Track Open FDs + int open_fds = int(fds.size()); + + // Setup Polling + pollfd *poll_fds = new pollfd[fds.size()]; + for (std::vector::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::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 *run_command(const char *const command[], int *exit_status) { + // Run + const std::optional 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 *output = new std::vector; + 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 ""; + } +} diff --git a/libreborn/src/util/log.c b/libreborn/src/util/log.cpp similarity index 52% rename from libreborn/src/util/log.c rename to libreborn/src/util/log.cpp index c0e57101bf..2235a08e28 100644 --- a/libreborn/src/util/log.c +++ b/libreborn/src/util/log.cpp @@ -1,15 +1,24 @@ #include #include -#include -#include +#include #include -#include #include // 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 static int log_fd = -1; int reborn_get_log_fd() { @@ -18,10 +27,11 @@ int reborn_get_log_fd() { } // Open Log File const char *fd_str = getenv(_MCPI_LOG_FD_ENV); - log_fd = fd_str ? atoi(fd_str) : open("/dev/null", O_WRONLY | O_APPEND); - // Check FD - if (log_fd < 0) { - ERR("Unable To Open Log: %s", strerror(errno)); + if (fd_str) { + log_fd = std::stoi(fd_str); + } else { + setup_null_fd(); + log_fd = null_fd; } // Return return reborn_get_log_fd(); @@ -29,14 +39,12 @@ int reborn_get_log_fd() { void reborn_set_log(const int fd) { // Set Variable log_fd = -1; - char buf[128]; - sprintf(buf, "%i", fd); - set_and_print_env(_MCPI_LOG_FD_ENV, buf); + set_and_print_env(_MCPI_LOG_FD_ENV, std::to_string(fd).c_str()); } // Debug Logging -static int should_print_debug_to_stderr() { - return getenv(MCPI_DEBUG_ENV) != NULL; +static bool should_print_debug_to_stderr() { + return getenv(MCPI_DEBUG_ENV) != nullptr; } int reborn_get_debug_fd() { return should_print_debug_to_stderr() ? STDERR_FILENO : reborn_get_log_fd(); diff --git a/libreborn/src/util/string.c b/libreborn/src/util/string.cpp similarity index 59% rename from libreborn/src/util/string.c rename to libreborn/src/util/string.cpp index 5dfe4a0927..7cba6521de 100644 --- a/libreborn/src/util/string.c +++ b/libreborn/src/util/string.cpp @@ -1,14 +1,12 @@ #include -#include - // 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 - size_t length = strlen(str); + size_t length = str.size(); // Truncate Message if (max_length >= 0 && length > ((size_t) max_length)) { - str[max_length] = '\0'; + str = str.substr(0, max_length); length = max_length; } // Loop Through Message @@ -20,9 +18,4 @@ 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; -} +} \ No newline at end of file diff --git a/libreborn/src/util/util.c b/libreborn/src/util/util.cpp similarity index 72% rename from libreborn/src/util/util.c rename to libreborn/src/util/util.cpp index 7bbfccec4e..583acdc768 100644 --- a/libreborn/src/util/util.c +++ b/libreborn/src/util/util.cpp @@ -7,31 +7,34 @@ #include // Safe Version Of pipe() -void safe_pipe2(int pipefd[2], int flags) { - if (pipe2(pipefd, flags) != 0) { +Pipe::Pipe(): read(-1), write(-1) { + int out[2]; + if (pipe(out) != 0) { ERR("Unable To Create Pipe: %s", strerror(errno)); } + const_cast(read) = out[0]; + const_cast(write) = out[1]; } // Check If Two Percentages Are Different Enough To Be Logged #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 == -1 || old_val == -1) { - return 1; + return true; } else if (new_val == 0 || new_val == 100) { - return 1; + return true; } else { return new_val - old_val >= SIGNIFICANT_PROGRESS; } } else { - return 0; + return false; } } // Lock 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) { 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() { return MCPI_VERSION; } -int reborn_is_headless() { - static int ret; - static int is_set = 0; +bool reborn_is_headless() { + static bool ret; + static bool is_set = false; if (!is_set) { ret = reborn_is_server(); if (getenv(_MCPI_FORCE_HEADLESS_ENV)) { - ret = 1; + ret = true; } else if (getenv(_MCPI_FORCE_NON_HEADLESS_ENV)) { - ret = 0; + ret = false; } - is_set = 1; + is_set = true; } return ret; } -int reborn_is_server() { +bool reborn_is_server() { static int ret; static int is_set = 0; if (!is_set) { - ret = getenv(_MCPI_SERVER_MODE_ENV) != NULL; + ret = getenv(_MCPI_SERVER_MODE_ENV) != nullptr; is_set = 1; } return ret; @@ -84,7 +87,7 @@ void reborn_check_display() { // Home Subdirectory 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 return ""; } else if (!reborn_is_server()) { @@ -111,4 +114,12 @@ void ensure_directory(const char *path) { if (!is_dir) { 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)); + } } \ No newline at end of file diff --git a/media-layer/core/CMakeLists.txt b/media-layer/core/CMakeLists.txt index 3fcc565c07..187beb9e52 100644 --- a/media-layer/core/CMakeLists.txt +++ b/media-layer/core/CMakeLists.txt @@ -9,7 +9,7 @@ set(CORE_SRC src/events.cpp src/offscreen.cpp src/audio/api.cpp - src/audio/engine.c + src/audio/engine.cpp src/audio/file.cpp ) diff --git a/media-layer/core/src/audio/api.cpp b/media-layer/core/src/audio/api.cpp index 3d2655d56b..6d44ca7e0e 100644 --- a/media-layer/core/src/audio/api.cpp +++ b/media-layer/core/src/audio/api.cpp @@ -44,7 +44,7 @@ void _media_audio_delete_sources() { } // 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 if (_media_audio_is_loaded()) { // Update Listener Volume @@ -56,8 +56,8 @@ void media_audio_update(float volume, float x, float y, float z, float yaw) { AL_ERROR_CHECK(); // Update Listener Orientation - float radian_yaw = yaw * (M_PI / 180); - ALfloat orientation[] = {-sinf(radian_yaw), 0.0f, cosf(radian_yaw), 0.0f, 1.0f, 0.0f}; + const float radian_yaw = float(yaw * (M_PI / 180)); + const ALfloat orientation[] = {-sinf(radian_yaw), 0.0f, cosf(radian_yaw), 0.0f, 1.0f, 0.0f}; alListenerfv(AL_ORIENTATION, orientation); AL_ERROR_CHECK(); @@ -101,11 +101,11 @@ void media_audio_play(const char *source, const char *name, float x, float y, fl // Check if (_media_audio_is_loaded()) { // Load Sound - ALuint buffer = _media_audio_get_buffer(source, name); + const ALuint buffer = _media_audio_get_buffer(source, name); if (volume > 0.0f && buffer) { // Get Source ALuint al_source; - if (idle_sources.size() > 0) { + if (!idle_sources.empty()) { // Use Idle Source al_source = idle_sources.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); // Special Out-Of-Memory Handling { - ALenum err = alGetError(); + const ALenum err = alGetError(); if (err == AL_OUT_OF_MEMORY) { return; } else { diff --git a/media-layer/core/src/audio/api.h b/media-layer/core/src/audio/api.h index 8bcb90f6fe..dd90975f35 100644 --- a/media-layer/core/src/audio/api.h +++ b/media-layer/core/src/audio/api.h @@ -1,11 +1,3 @@ #pragma once -#ifdef __cplusplus -extern "C" { -#endif - __attribute__((visibility("internal"))) void _media_audio_delete_sources(); - -#ifdef __cplusplus -} -#endif diff --git a/media-layer/core/src/audio/engine.c b/media-layer/core/src/audio/engine.cpp similarity index 90% rename from media-layer/core/src/audio/engine.c rename to media-layer/core/src/audio/engine.cpp index e663b295ea..fd4cdd6807 100644 --- a/media-layer/core/src/audio/engine.c +++ b/media-layer/core/src/audio/engine.cpp @@ -9,8 +9,8 @@ #include "api.h" // Store Device -static ALCdevice *device = NULL; -static ALCcontext *context = NULL; +static ALCdevice *device = nullptr; +static ALCcontext *context = nullptr; // Store State static int is_loaded = 0; @@ -21,14 +21,14 @@ int _media_audio_is_loaded() { // Init void _media_audio_init() { // Open Device - device = alcOpenDevice(NULL); + device = alcOpenDevice(nullptr); if (!device) { WARN("Unable To Load Audio Engine"); return; } // Create Context - context = alcCreateContext(device, NULL); + context = alcCreateContext(device, nullptr); ALCenum err = alcGetError(device); if (err != ALC_NO_ERROR) { ERR("Unable To Open Audio Context: %s", alcGetString(device, err)); @@ -63,7 +63,7 @@ void _media_audio_cleanup() { _media_audio_delete_buffers(); // Deselect Context - alcMakeContextCurrent(NULL); + alcMakeContextCurrent(nullptr); ALCenum err = alcGetError(device); if (err != ALC_NO_ERROR) { ERR("Unable To Deselect Audio Context: %s", alcGetString(device, err)); diff --git a/media-layer/core/src/audio/engine.h b/media-layer/core/src/audio/engine.h index 69e70f0c8d..1aba3c26b3 100644 --- a/media-layer/core/src/audio/engine.h +++ b/media-layer/core/src/audio/engine.h @@ -2,14 +2,6 @@ #include -#ifdef __cplusplus -extern "C" { -#endif - __attribute__((visibility("internal"))) void _media_audio_init(); __attribute__((visibility("internal"))) void _media_audio_cleanup(); -__attribute__((visibility("internal"))) int _media_audio_is_loaded(); - -#ifdef __cplusplus -} -#endif +__attribute__((visibility("internal"))) int _media_audio_is_loaded(); \ No newline at end of file diff --git a/media-layer/core/src/audio/file.cpp b/media-layer/core/src/audio/file.cpp index 9cdb9ed1f0..ae2643311f 100644 --- a/media-layer/core/src/audio/file.cpp +++ b/media-layer/core/src/audio/file.cpp @@ -14,13 +14,13 @@ // Load Symbol From ELF File static void load_symbol(const char *source, const char *name, std::function callback) { static std::unordered_map> sources = {}; - std::string cpp_source = source; - if (sources.count(cpp_source) == 0) { + const std::string cpp_source = source; + if (!sources.contains(cpp_source)) { sources[cpp_source] = LIEF::ELF::Parser::parse(source); } - std::unique_ptr &binary = sources[cpp_source]; + const std::unique_ptr &binary = sources[cpp_source]; const LIEF::ELF::Symbol *symbol = binary->get_dynamic_symbol(name); - if (symbol != NULL) { + if (symbol != nullptr) { LIEF::span data = binary->get_content_from_virtual_address(symbol->value(), symbol->size(), LIEF::Binary::VA_TYPES::VA); callback(data.data(), data.size()); } else { @@ -73,8 +73,8 @@ static ALuint load_sound(const char *source, const char *name) { } // Load Data - int remaining_size = size - sizeof (audio_metadata); - int data_size = meta->channels * meta->frames * meta->frame_size; + const int remaining_size = size - sizeof (audio_metadata); + const int data_size = meta->channels * meta->frames * meta->frame_size; if (remaining_size < data_size) { WARN("Symbol Too Small To Contain Specified Audio Data: %s", name); return; diff --git a/media-layer/core/src/audio/file.h b/media-layer/core/src/audio/file.h index f68f8cbf65..63d9366c0a 100644 --- a/media-layer/core/src/audio/file.h +++ b/media-layer/core/src/audio/file.h @@ -2,13 +2,5 @@ #include -#ifdef __cplusplus -extern "C" { -#endif - __attribute__((visibility("internal"))) ALuint _media_audio_get_buffer(const char *source, const char *name); __attribute__((visibility("internal"))) void _media_audio_delete_buffers(); - -#ifdef __cplusplus -} -#endif diff --git a/media-layer/core/src/events.cpp b/media-layer/core/src/events.cpp index 67f6562b42..01b04ff03f 100644 --- a/media-layer/core/src/events.cpp +++ b/media-layer/core/src/events.cpp @@ -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) { // Convert size_t str_size = 4 /* Maximum UTF-8 character size */ + 1 /* NULL-terminator */; - char str[str_size] = {}; - codepoint_to_utf8((unsigned char *) str, codepoint); - char *cp437_str = to_cp437(str); + unsigned char str[str_size] = {}; + codepoint_to_utf8(str, codepoint); + std::string cp437_str = to_cp437((const char *) str); // Send Event - for (int i = 0; cp437_str[i] != '\0'; i++) { - character_event(cp437_str[i]); + for (const char x : cp437_str) { + character_event(x); } - // Free - free(cp437_str); } // Convert Screen Coordinates To Pixels diff --git a/mods/include/mods/override/override.h b/mods/include/mods/override/override.h index d2aae2cfc3..be620444e6 100644 --- a/mods/include/mods/override/override.h +++ b/mods/include/mods/override/override.h @@ -1,5 +1,7 @@ #pragma once +#include + extern "C" { -char *override_get_path(const char *filename); +std::string override_get_path(std::string filename); } \ No newline at end of file diff --git a/mods/src/chat/chat.cpp b/mods/src/chat/chat.cpp index 9053fc336c..aec91989c6 100644 --- a/mods/src/chat/chat.cpp +++ b/mods/src/chat/chat.cpp @@ -26,9 +26,8 @@ std::string chat_send_api_command(const Minecraft *minecraft, const std::string // Send API Chat Command 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"; - free(utf_str); 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) { std::string full_message = _chat_get_prefix(username) + message; - char *raw_str = strdup(full_message.c_str()); - ALLOC_CHECK(raw_str); - sanitize_string(raw_str, MAX_CHAT_MESSAGE_LENGTH, 0); - full_message = raw_str; - free(raw_str); + sanitize_string(full_message, MAX_CHAT_MESSAGE_LENGTH, false); server_side_network_handler->displayGameMessage(full_message); } // Handle Chat packet Send void chat_handle_packet_send(const Minecraft *minecraft, ChatPacket *packet) { // Convert To CP-437 - char *cp437_str = to_cp437(packet->message.c_str()); - packet->message = cp437_str; - free(cp437_str); + packet->message = to_cp437(packet->message); // Send RakNetInstance *rak_net_instance = minecraft->rak_net_instance; if (rak_net_instance->isServer()) { diff --git a/mods/src/game-mode/game-mode.cpp b/mods/src/game-mode/game-mode.cpp index 59b037ae7d..26834c28ba 100644 --- a/mods/src/game-mode/game-mode.cpp +++ b/mods/src/game-mode/game-mode.cpp @@ -1,10 +1,13 @@ -#include "game-mode-internal.h" +#include + #include #include #include #include +#include "game-mode-internal.h" + static int is_survival = -1; // Patch Game Mode diff --git a/mods/src/input/attack.cpp b/mods/src/input/attack.cpp index 7d3eede759..942c50bced 100644 --- a/mods/src/input/attack.cpp +++ b/mods/src/input/attack.cpp @@ -1,3 +1,5 @@ +#include + #include #include diff --git a/mods/src/misc/logging.cpp b/mods/src/misc/logging.cpp index ee6ddbfaa1..f5d0120ba2 100644 --- a/mods/src/misc/logging.cpp +++ b/mods/src/misc/logging.cpp @@ -10,10 +10,8 @@ // Print Chat To Log static void Gui_addMessage_injection(Gui_addMessage_t original, Gui *gui, const std::string &text) { // Sanitize Message - char *new_message = strdup(text.c_str()); - ALLOC_CHECK(new_message); - sanitize_string(new_message, -1, 1); - const std::string cpp_str = new_message; + std::string new_message = text; + sanitize_string(new_message, -1, true); // Process Message static bool recursing = false; @@ -22,22 +20,18 @@ static void Gui_addMessage_injection(Gui_addMessage_t original, Gui *gui, const recursing = true; // Print Log Message - char *safe_message = from_cp437(new_message); - fprintf(stderr, "[CHAT]: %s\n", safe_message); - free(safe_message); + std::string safe_message = from_cp437(new_message); + fprintf(stderr, "[CHAT]: %s\n", safe_message.c_str()); // Call Original Method - original(gui, cpp_str); + original(gui, new_message); // End Recursing recursing = false; } else { // Call Original Method - original(gui, cpp_str); + original(gui, new_message); } - - // Free - free(new_message); } // Print Progress Reports diff --git a/mods/src/misc/misc.cpp b/mods/src/misc/misc.cpp index fb06175983..b0879dd9e2 100644 --- a/mods/src/misc/misc.cpp +++ b/mods/src/misc/misc.cpp @@ -34,13 +34,10 @@ static void LoginPacket_read_injection(LoginPacket_read_t original, LoginPacket const RakNet_RakString_SharedString *shared_string = rak_string->sharedString; const char *c_str = shared_string->c_str; // Sanitize - char *new_username = strdup(c_str); - ALLOC_CHECK(new_username); + std::string new_username = c_str; sanitize_string(new_username, MAX_USERNAME_LENGTH, 0); // Set New Username - rak_string->Assign(new_username); - // Free - free(new_username); + rak_string->Assign(new_username.c_str()); } // Fix RakNet::RakString Security Bug diff --git a/mods/src/options/info.cpp b/mods/src/options/info.cpp index 74d3533a75..4f33219d74 100644 --- a/mods/src/options/info.cpp +++ b/mods/src/options/info.cpp @@ -161,10 +161,8 @@ static void position_info(Font *font, const int width, const int height) { void open_url(const std::string &url) { int return_code; const char *command[] = {"xdg-open", url.c_str(), nullptr}; - char *output = run_command(command, &return_code, nullptr); - if (output != nullptr) { - free(output); - } + const std::vector *output = run_command(command, &return_code); + delete output; if (!is_exit_status_success(return_code)) { WARN("Unable To Open URL: %s", url.c_str()); } diff --git a/mods/src/options/options.cpp b/mods/src/options/options.cpp index 9aa7fccac4..7117412531 100644 --- a/mods/src/options/options.cpp +++ b/mods/src/options/options.cpp @@ -44,10 +44,6 @@ static const char *get_username() { } return username; } -static char *safe_username = nullptr; -__attribute__((destructor)) static void _free_safe_username() { - free(safe_username); -} static int render_distance; // Configure Options @@ -162,8 +158,9 @@ void init_options() { if (strcmp(Strings::default_username, "StevePi") != 0) { ERR("Default Username Is Invalid"); } - safe_username = to_cp437(username); - patch_address((void *) &Strings::default_username, (void *) safe_username); + std::string *safe_username = new std::string; + *safe_username = to_cp437(username); + patch_address((void *) &Strings::default_username, (void *) safe_username->c_str()); // Disable Autojump By Default if (feature_has("Disable Autojump By Default", server_disabled)) { @@ -210,7 +207,7 @@ void init_options() { // Replace String patch_address((void *) &Strings::feedback_vibration_options_txt_name, (void *) "gfx_ao"); // 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" patch((void *) 0x193b8, gfx_ao_loading_patch); // Saving @@ -223,7 +220,7 @@ void init_options() { // Replace String patch_address((void *) &Strings::gfx_lowquality_options_txt_name, (void *) "gfx_anaglyph"); // 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" patch((void *) 0x19400, gfx_anaglyph_loading_patch); // Disable Loading Side Effects diff --git a/mods/src/options/ui.cpp b/mods/src/options/ui.cpp index 1400b2ae11..457137dd16 100644 --- a/mods/src/options/ui.cpp +++ b/mods/src/options/ui.cpp @@ -1,4 +1,5 @@ #include +#include #include #include diff --git a/mods/src/override/override.cpp b/mods/src/override/override.cpp index ab0bbee159..a3e744be19 100644 --- a/mods/src/override/override.cpp +++ b/mods/src/override/override.cpp @@ -12,18 +12,18 @@ #include #include -// Hook access -HOOK(access, int, (const char *pathname, int mode)) { - char *new_path = override_get_path(pathname); - // Open File - const int ret = real_access()(new_path != nullptr ? new_path : pathname, mode); - // Free Data - if (new_path != nullptr) { - free(new_path); +// Hook Functions +#define HOOK_OPEN(name, return_type, mode_type) \ + HOOK(name, return_type, (const char *filename, mode_type mode)) { \ + const std::string new_path = override_get_path(filename); \ + /* Open File */ \ + return_type ret = real_##name()(!new_path.empty() ? new_path.c_str() : filename, mode); \ + /* Return */ \ + return ret; \ } - // Return - return ret; -} +HOOK_OPEN(fopen, FILE *, const char *) +HOOK_OPEN(fopen64, FILE *, const char *) +HOOK_OPEN(access, int, int) // Get Override Folder static std::string get_override_directory() { @@ -32,9 +32,9 @@ static std::string get_override_directory() { } // 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 - if (starts_with(filename, "data/images/$")) { + if (filename.starts_with("data/images/$")) { // Fallback Texture filename = "data/images/mob/char.png"; } @@ -42,12 +42,12 @@ char *override_get_path(const char *filename) { // Get Asset Override Path const std::string overrides = get_override_directory(); - // Data Prefiix + // Data Prefix const std::string data_prefix = "data/"; - int data_prefix_length = data_prefix.length(); + const int data_prefix_length = data_prefix.length(); // Folders To Check - std::string asset_folders[] = { + const std::string asset_folders[] = { overrides, getenv(_MCPI_REBORN_ASSETS_PATH_ENV), getenv(_MCPI_VANILLA_ASSETS_PATH_ENV), @@ -72,39 +72,7 @@ char *override_get_path(const char *filename) { } // Return - if (new_path.empty()) { - 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; + return new_path; } // Init diff --git a/mods/src/server/server.cpp b/mods/src/server/server.cpp index 624aa293a6..553f169799 100644 --- a/mods/src/server/server.cpp +++ b/mods/src/server/server.cpp @@ -57,9 +57,7 @@ static ServerPropertyTypes &get_property_types() { // Get World Name static std::string get_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 = safe_name_c; - free(safe_name_c); + std::string safe_name = to_cp437(name); return safe_name; } @@ -116,11 +114,9 @@ static std::vector get_players_in_level(Level *level) { return level->players; } // 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; - char *safe_username_c = from_cp437(username->c_str()); - std::string safe_username = safe_username_c; - free(safe_username_c); + std::string safe_username = from_cp437(*username); return safe_username; } // Get Level From Minecraft @@ -307,12 +303,9 @@ std::vector *server_get_commands(Minecraft *minecraft, ServerSide .callback = [server_side_network_handler](const std::string &cmd) { // Format Message const std::string message = "[Server] " + cmd; - char *safe_message = to_cp437(message.c_str()); - std::string cpp_string = safe_message; + std::string cpp_string = to_cp437(message); // Post Message To Chat server_side_network_handler->displayGameMessage(cpp_string); - // Free - free(safe_message); } }); // List Players diff --git a/mods/src/shading/normals.cpp b/mods/src/shading/normals.cpp index bb4dc8dc86..afec0ca44e 100644 --- a/mods/src/shading/normals.cpp +++ b/mods/src/shading/normals.cpp @@ -1,3 +1,5 @@ +#include + #include #include diff --git a/mods/src/skin/loader.cpp b/mods/src/skin/loader.cpp index 4653d55ea1..83feef0960 100644 --- a/mods/src/skin/loader.cpp +++ b/mods/src/skin/loader.cpp @@ -19,15 +19,14 @@ // Loading Pending Skins struct pending_skin { int32_t texture_id; - char *data; - int size; + const std::vector *data; }; static std::vector &get_pending_skins() { static std::vector pending_skins; return pending_skins; } 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 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()) { // Read PNG Info 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) { continue; } @@ -54,7 +53,7 @@ static void load_pending_skins(Minecraft *minecraft) { // Free for (const pending_skin &skin : get_pending_skins()) { - free(skin.data); + delete skin.data; } // Clear @@ -87,11 +86,10 @@ static void *loader_thread(void *user_data) { const std::string url = get_skin_server() + '/' + data->name + ".png"; int return_code; const char *command[] = {"wget", "-O", "-", url.c_str(), nullptr}; - size_t output_size = 0; - char *output = run_command(command, &return_code, &output_size); + const std::vector *output = run_command(command, &return_code); // Check Success - if (output != nullptr && is_exit_status_success(return_code)) { + if (is_exit_status_success(return_code)) { // Success DEBUG("Downloaded Skin: %s", data->name.c_str()); @@ -99,14 +97,12 @@ static void *loader_thread(void *user_data) { pending_skin skin = {}; skin.texture_id = data->texture_id; skin.data = output; - skin.size = (int) output_size; pthread_mutex_lock(&pending_skins_lock); get_pending_skins().push_back(skin); pthread_mutex_unlock(&pending_skins_lock); } else { // Failure WARN("Failed To Download Skin: %s", data->name.c_str()); - free(output); } // Free @@ -120,7 +116,7 @@ static int32_t Textures_assignTexture_injection(Textures_assignTexture_t origina const int32_t id = original(textures, name, data); // Load Skin - if (starts_with(name.c_str(), "$")) { + if (name.starts_with("$")) { loader_data *user_data = new loader_data; user_data->name = name.substr(1); DEBUG("Loading Skin: %s", user_data->name.c_str()); diff --git a/mods/src/sound/sound.cpp b/mods/src/sound/sound.cpp index c9259e2ec3..312777fae3 100644 --- a/mods/src/sound/sound.cpp +++ b/mods/src/sound/sound.cpp @@ -24,31 +24,21 @@ std::string _sound_get_source_file() { // Resolve // Get Path - char *path = strdup(SOURCE_FILE_BASE); - ALLOC_CHECK(path); - - // Handle Overrides - char *overridden_full_path = override_get_path(path); - if (overridden_full_path != nullptr) { - free(path); - path = overridden_full_path; - } + const std::string path = SOURCE_FILE_BASE; + const std::string full_path = override_get_path(path); // Check If Sound Exists - if (access(path, F_OK) == -1) { + if (access(full_path.c_str(), F_OK) == -1) { // Fail - WARN("Audio Source File Doesn't Exist: " SOURCE_FILE_BASE); - source.assign(""); + WARN("Audio Source File Doesn't Exist: %s", path.c_str()); + source = ""; info_sound_data_state = "Missing"; } else { // Set - source.assign(path); + source = full_path; info_sound_data_state = "Loaded"; } - // Free - free(path); - // Mark As Loaded source_loaded = true; diff --git a/mods/src/textures/lava.cpp b/mods/src/textures/lava.cpp index c178bdf962..a7292a44ca 100644 --- a/mods/src/textures/lava.cpp +++ b/mods/src/textures/lava.cpp @@ -1,4 +1,5 @@ #include +#include #include #include diff --git a/mods/src/touch/touch.cpp b/mods/src/touch/touch.cpp index e4236ae511..76e12c9047 100644 --- a/mods/src/touch/touch.cpp +++ b/mods/src/touch/touch.cpp @@ -1,3 +1,5 @@ +#include + #include #include diff --git a/symbols/CMakeLists.txt b/symbols/CMakeLists.txt index d632290d07..f596abbb0d 100644 --- a/symbols/CMakeLists.txt +++ b/symbols/CMakeLists.txt @@ -253,9 +253,6 @@ target_include_directories( "$" ) -# Disable C++11 String ABI -target_compile_definitions(symbols PUBLIC -D_GLIBCXX_USE_CXX11_ABI=0) - # Install install(TARGETS symbols DESTINATION "${MCPI_LIB_DIR}") # SDK