diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f44f84c..e069e33 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -35,6 +35,8 @@ jobs: mkdir -p ${{runner.workspace}}/dist cp src/apprun/AppRun ${{runner.workspace}}/dist/AppRun-$BUILD_TYPE-$TARGET_ARCH cp src/hooks/libapprun_hooks.so ${{runner.workspace}}/dist/libapprun_hooks-$BUILD_TYPE-$TARGET_ARCH.so + cp src/modules/check-glibc ${{runner.workspace}}/dist/check-glibc-$BUILD_TYPE-$TARGET_ARCH + cp src/modules/check-glibstdc++ ${{runner.workspace}}/dist/check-glibstdc++-$BUILD_TYPE-$TARGET_ARCH - name: Upload Artifacts ${{ env.TARGET_ARCH }} uses: actions/upload-artifact@v2.1.4 diff --git a/.github/workflows/pre-release.yml b/.github/workflows/pre-release.yml index 5e1168d..52b2e92 100644 --- a/.github/workflows/pre-release.yml +++ b/.github/workflows/pre-release.yml @@ -33,6 +33,8 @@ jobs: mkdir -p ${{runner.workspace}}/dist cp src/apprun/AppRun ${{runner.workspace}}/dist/AppRun-$BUILD_TYPE-$TARGET_ARCH cp src/hooks/libapprun_hooks.so ${{runner.workspace}}/dist/libapprun_hooks-$BUILD_TYPE-$TARGET_ARCH.so + cp src/modules/check-glibc ${{runner.workspace}}/dist/check-glibc-$BUILD_TYPE-$TARGET_ARCH + cp src/modules/check-glibstdc++ ${{runner.workspace}}/dist/check-glibstdc++-$BUILD_TYPE-$TARGET_ARCH - name: Upload Artifacts ${{ env.TARGET_ARCH }} uses: actions/upload-artifact@v2.1.4 diff --git a/.github/workflows/tagged-release.yml b/.github/workflows/tagged-release.yml index bdd63c5..692e500 100644 --- a/.github/workflows/tagged-release.yml +++ b/.github/workflows/tagged-release.yml @@ -33,6 +33,8 @@ jobs: mkdir -p ${{runner.workspace}}/dist cp src/apprun/AppRun ${{runner.workspace}}/dist/AppRun-$BUILD_TYPE-$TARGET_ARCH cp src/hooks/libapprun_hooks.so ${{runner.workspace}}/dist/libapprun_hooks-$BUILD_TYPE-$TARGET_ARCH.so + cp src/modules/check-glibc ${{runner.workspace}}/dist/check-glibc-$BUILD_TYPE-$TARGET_ARCH + cp src/modules/check-glibstdc++ ${{runner.workspace}}/dist/check-glibstdc++-$BUILD_TYPE-$TARGET_ARCH - name: Upload Artifacts ${{ env.TARGET_ARCH }} uses: actions/upload-artifact@v2.1.4 diff --git a/CMakeLists.txt b/CMakeLists.txt index 53221db..80e4bf0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,12 +1,14 @@ project(AppImageExecWrapper) cmake_minimum_required(VERSION 3.2) set(CMAKE_C_STANDARD 99) +set(CMAKE_CXX_STANDARD 14) if (${CMAKE_BUILD_TYPE} MATCHES Profile) message(STATUS "Coverage and profile build flags enabled") set(CMAKE_C_FLAGS "-fprofile-arcs -ftest-coverage") endif (${CMAKE_BUILD_TYPE} MATCHES Profile) +include(cmake/get_libconfig.cmake) add_subdirectory(src) if (BUILD_TESTING) diff --git a/cmake/get_libconfig.cmake b/cmake/get_libconfig.cmake new file mode 100644 index 0000000..d902b82 --- /dev/null +++ b/cmake/get_libconfig.cmake @@ -0,0 +1,31 @@ +cmake_minimum_required(VERSION 3.11) +include(ExternalProject) + +# Get the libconfig library +externalproject_add( + libconfig.git + GIT_REPOSITORY https://github.com/hyperrealm/libconfig.git + GIT_TAG v1.7.3 + GIT_SHALLOW On + UPDATE_DISCONNECTED On + CMAKE_ARGS + -DCMAKE_INSTALL_PREFIX=/usr + -DBUILD_EXAMPLES=Off + -DBUILD_TESTS=Off + -DBUILD_SHARED_LIBS=Off + -DCMAKE_TOOLCHAIN_FILE:FILEPATH=${CMAKE_TOOLCHAIN_FILE} + -DCMAKE_INSTALL_PREFIX:PATH= +) + +ExternalProject_Get_Property(libconfig.git install_dir) +include_directories(${install_dir}/include) + + +add_library(libconfig STATIC IMPORTED) +set_property(TARGET libconfig PROPERTY IMPORTED_LOCATION ${install_dir}/lib/libconfig.a) + +add_library(libconfig++ STATIC IMPORTED) +set_property(TARGET libconfig++ PROPERTY IMPORTED_LOCATION ${install_dir}/lib/libconfig++.a) + +add_dependencies(libconfig libconfig.git) +add_dependencies(libconfig++ libconfig.git) \ No newline at end of file diff --git a/cmake/i386-toolchain.cmake b/cmake/i386-toolchain.cmake index f786296..fe56b62 100644 --- a/cmake/i386-toolchain.cmake +++ b/cmake/i386-toolchain.cmake @@ -1,21 +1,23 @@ -# this one is important -SET(CMAKE_SYSTEM_NAME Linux) -#this one not so much -SET(CMAKE_SYSTEM_VERSION 1) +set(CMAKE_SYSTEM_NAME Linux) +set(CMAKE_SYSTEM_PROCESSOR i386) -# specify the cross compiler -SET(CMAKE_C_COMPILER /usr/bin/gcc) -set(CMAKE_C_COMPILER_ARG1 "-m32") -SET(CMAKE_CXX_COMPILER /usr/bin/g++) -set(CMAKE_CXX_COMPILER_ARG1 "-m32") +set(CMAKE_C_FLAGS "-m32") +set(CMAKE_CXX_FLAGS "-m32") -# where is the target environment -SET(CMAKE_FIND_ROOT_PATH /usr/lib/i386-linux-gnu) -set(CMAKE_IGNORE_PATH /usr/lib/x86_64-linux-gnu/ /usr/lib/x86_64-linux-gnu/lib/) +# CMAKE_SHARED_LINKER_FLAGS, CMAKE_STATIC_LINKER_FLAGS etc. must not be set, but CMAKE_EXE_LINKER_FLAGS is necessary +set(CMAKE_EXE_LINKER_FLAGS "-m32") -# search for programs in the build host directories -SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +set(DEPENDENCIES_CFLAGS "-m32") +set(DEPENDENCIES_CPPFLAGS "-m32") +set(DEPENDENCIES_LDFLAGS "-m32") -# for libraries and headers in the target directories -SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) -SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) +# host = target system +# build = build system +# both must be specified +set(EXTRA_CONFIGURE_FLAGS "--host=i686-pc-linux-gnu" "--build=x86_64-pc-linux-gnu") + +# may help with some rare issues +set(CMAKE_PREFIX_PATH /usr/lib/i386-linux-gnu) + +# makes sure that at least on Ubuntu pkg-config will search for the :i386 packages +set(ENV{PKG_CONFIG_PATH} /usr/lib/i386-linux-gnu/pkgconfig/) \ No newline at end of file diff --git a/docs/MODULES.md b/docs/MODULES.md new file mode 100644 index 0000000..eaf2bac --- /dev/null +++ b/docs/MODULES.md @@ -0,0 +1,72 @@ +Modules +======= + +A module is a part of the bundle that will only be used at runtime if the host system doesn't provide a certain feature. +Each module will include a "check" app (or script). This will be executed by the AppRun to verify if the system has the +required feature. The module will be used only if this check fails. + +Modules provide a way of configuring the bundle at runtime. This is required to switch between the system and bundled +libraries depending on the system configuration. The simplest example is glibc, applications need to always use the +latest version therefore we must check if the system provides an older one in order to use the module. + +------------- +Specification +------------- + +An AppDir may contain given set of modules at `$APPDIR/opt/`. Each module will contain the following components: + +- "check" app +- "config" file +- binaries and resources to be optionally used at runtime + +check +----- +The check application has the sole purpose of guessing whether the system provides certain feature. It will be executed +on launch and must return a fast a possible. The application must exit with code 0 if the system has the expected +feature otherwise must return a non-zero value. + +The module will be enabled only if the system doesn't have the required feature (which means that the check returned a +non-zero value). + +config file +----------- + +The config file will follow the [libconfig format](https://hyperrealm.github.io/libconfig/libconfig_manual.html) and +will contain the settings required to activate the bundle at runtime. It may also include other information required by +other components such as the check binary. It will have the following structure: + +- `check`: optional group, may contain data required by the check app. +- `module`: mandatory group, module runtime settings + + - `library_paths`: optional string list, module library paths + - `path_mappings`: optional string pair list, path mapping instructions + - `environment`: optional map, environment variables + - `runtime_dir`: optional string, working directory to be set on execve instructions (reserved for the glibc module) + +Example: + +```shell +version = "1.0"; + +# check app configuration +check: +{ + required_lib_version = "2.0.0"; +}; + +# module runtime configuration +module: +{ + # library paths to be set + library_paths = ( "${APPDIR}/opt/module_id/lib", "${APPDIR}/opt/module_id/usr/lib"); + + # path_mappings to be set + path_mappings = [ "/bin/app:${APPDIR}/opt/module_id/bin/app" ] ; + + # additional environment variables to be set + environment: + { + MY_VAR: "MY_VAR_VALUE"; + }; +}; +``` diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a22c9d9..c9f38ff 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,3 +1,4 @@ add_subdirectory(common) add_subdirectory(hooks) add_subdirectory(apprun) +add_subdirectory(modules) \ No newline at end of file diff --git a/src/apprun/CMakeLists.txt b/src/apprun/CMakeLists.txt index 36e3ff4..02c25e6 100644 --- a/src/apprun/CMakeLists.txt +++ b/src/apprun/CMakeLists.txt @@ -1,26 +1,25 @@ add_library( - apprun_objects OBJECT + apprun_objects OBJECT - ../hooks/environment.c - runtime_environment.c - runtime_environment.h - runtime_interpreter.c + ../hooks/environment.c + apprun.cpp ) target_compile_definitions(apprun_objects PRIVATE "$<$:DEBUG>") target_include_directories(apprun_objects PUBLIC $) add_executable( - AppRun - main.c - $ - $ + AppRun + main.cpp + $ + $ ) target_compile_definitions(AppRun PRIVATE "$<$:DEBUG>") target_include_directories(AppRun PRIVATE $) +target_link_libraries(AppRun PRIVATE libconfig++ -static) install( - TARGETS AppRun - RUNTIME DESTINATION bin + TARGETS AppRun + RUNTIME DESTINATION bin ) add_dependencies(AppRun apprun_hooks) \ No newline at end of file diff --git a/src/apprun/apprun.cpp b/src/apprun/apprun.cpp new file mode 100644 index 0000000..15ee2d0 --- /dev/null +++ b/src/apprun/apprun.cpp @@ -0,0 +1,302 @@ +/************************************************************************** + * + * Copyright (c) 2022 Alexis Lopez Zubieta + * + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + **************************************************************************/ + +#include +#include +#include +#include +#include + +#include +#include +#include + + +#include "apprun.h" + + +std::string resolve_origin() { + std::string exec_path = realpath("/proc/self/exe", nullptr); + std::size_t path_separator_idx = exec_path.rfind('/'); + if (path_separator_idx != std::string::npos) + return exec_path.substr(0, path_separator_idx); + + return ""; +} + +int launch(AppRunSettings* settings, char** argv) { + if (!settings->runtime_dir.empty()) { + char* runtime_dir_value = apprun_shell_expand_variables(settings->runtime_dir.c_str(), argv); + apprun_set_private_env(APPRUN_ENV_RUNTIME, runtime_dir_value, {}, runtime_dir_value); + + char cwd[PATH_MAX] = {0x0}; + getcwd(cwd, PATH_MAX); + setenv(APPRUN_ENV_ORIGINAL_WORKDIR, cwd, 1); + + // set the runtime dir as working directory + chdir(runtime_dir_value); +#ifdef DEBUG + fprintf(stderr, "APPRUN_DEBUG: changing working directory to %s\n", runtime_dir_value); +#endif + } + + // format execution line + char args_line[PATH_MAX] = {0x0}; + auto exec_argv_len = settings->exec.size(); + for (int i = 0; i < exec_argv_len; i++) { + const char* arg = settings->exec[i].c_str(); + char* arg_expanded = apprun_shell_expand_variables(arg, argv); + if (i > 0) + strcat(args_line, " "); + + strcat(args_line, arg_expanded); + } + + // split execution line into arguments for execv + char** user_args = apprun_shell_split_arguments(args_line); + int user_args_len = apprun_string_list_len(reinterpret_cast(user_args)); + + char** exec_argv = static_cast(malloc(sizeof(char*) * exec_argv_len + 1)); + int exec_argc = 0; + + // copy the user arguments + for (int i = 0; i < user_args_len; i++) { + char* arg = user_args[i]; + if (strlen(arg) > 0) { + exec_argv[exec_argc] = arg; + exec_argc++; + } + } + + // add 0 termination + exec_argv[exec_argc] = nullptr; + +#ifdef DEBUG + fprintf(stderr, "APPRUN_DEBUG: executing "); + for (char** itr = exec_argv; itr != nullptr && *itr != nullptr; itr++) + fprintf(stderr, "\"%s\" ", *itr); + fprintf(stderr, "\n"); +#endif + int ret = execv(exec_argv[0], exec_argv); + fprintf(stderr, "APPRUN_ERROR: %s\n", strerror(errno)); + return ret; +} + +std::string generate_path_mappings_env(AppRunSettings* settings) { + std::string result; + size_t path_mappings_len = settings->path_mappings.size(); + for (int i = 0; i < path_mappings_len; i++) { + std::string value = apprun_shell_expand_variables(settings->path_mappings[i].c_str(), nullptr); + result.append(value) + .append(";"); + } + + return result; +} + +std::string generate_ld_library_path_value(AppRunSettings* settings) { + std::string result; + for (const auto& itr: settings->library_paths) { + std::string value = apprun_shell_expand_variables(itr.c_str(), nullptr); + if (!result.empty()) + result += ":"; + + result += value; + } + + const char* ld_library_path_original_value = getenv("LD_LIBRARY_PATH"); + if (ld_library_path_original_value != nullptr) { + if (!result.empty()) + result += ":"; + result += ld_library_path_original_value; + + } + + return result; +} + +/** + * Read library paths from module config file into AppRunSettings + * @param settings + * @param module_config + */ +void import_module_library_paths(AppRunSettings* settings, const libconfig::Config& module_config) { + auto& module_library_paths_setting = module_config.lookup("module.library_paths"); + auto module_library_paths_setting_len = module_library_paths_setting.getLength(); + for (int i = 0; i < module_library_paths_setting_len; i++) { + std::string library_path = module_library_paths_setting[i]; + settings->library_paths.push_back(library_path); + } +} + +/** + * Read environment from module config file into AppRunSettings + * @param settings + * @param module_config + */ +void import_module_environment(AppRunSettings* const settings, const libconfig::Config& module_config) { + auto& module_environment_setting = module_config.lookup("module.environment"); + auto module_environment_setting_len = module_environment_setting.getLength(); + for (int i = 0; i < module_environment_setting_len; i++) { + std::string name = module_environment_setting[i].getName(); + std::string value = module_environment_setting[i]; + + if (settings->environment.find(name) != settings->environment.end()) + std::cerr << "APPRUN WARNING: Overriding Environment " << name << std::endl; + + settings->environment[name] = value; + } +} + +void setup_module(AppRunSettings* settings, const std::string& module_dir) { + // verify if the system has the required feature + auto module_check_path = module_dir + "/check"; + + // ignore folders without check binary + if (access(module_check_path.c_str(), F_OK) != 0) + return; + + std::cout << "Checking module " << module_dir << std::endl; + if (system(module_check_path.c_str()) != EXIT_SUCCESS) { + std::cout << "Enabling module " << module_dir << std::endl; + // enable module + auto module_config_path = module_dir + "/config"; + + libconfig::Config module_config; + try { + module_config.readFile(module_config_path); + if (module_config.exists("module.library_paths")) + import_module_library_paths(settings, module_config); + + if (module_config.exists("module.environment")) + import_module_environment(settings, module_config); + + if (module_config.exists("module.runtime_dir")) { + std::string value = module_config.lookup("module.runtime_dir"); + settings->runtime_dir = value; + } + + } catch (libconfig::ParseException& ex) { + std::cerr << ex.getFile() << ":" << ex.getLine() << ":" << ex.getError() << "\n"; + exit(EXIT_FAILURE); + } catch (libconfig::SettingTypeException& ex) { + std::cerr << ex.getPath() << "\n"; + exit(EXIT_FAILURE); + } + } +} + + +AppRunSettings* load_config_file(const std::string& config_path) { + // ignore folders without check binary + if (access(config_path.c_str(), F_OK) != 0) { + std::cerr << "Missing config file: " << config_path << "\n"; + exit(EXIT_FAILURE); + } + + auto* settings = new AppRunSettings(); + + // load config file + libconfig::Config apprun_config; + apprun_config.readFile(config_path); + auto& runtime_settings = apprun_config.lookup("runtime"); + + // load exec line (required) + auto& exec_line = runtime_settings.lookup("exec"); + size_t exec_line_len = exec_line.getLength(); + for (int i = 0; i < exec_line_len; i++) { + std::string entry = exec_line[i]; + settings->exec.push_back(entry); + } + + // load modules_dir (optional) + if (runtime_settings.exists("modules_dir")) { + std::string modules_dir = runtime_settings.lookup("modules_dir"); + settings->modules_dir = modules_dir; + } + + // load library_paths (optional) + if (runtime_settings.exists("library_paths")) { + auto& library_paths = runtime_settings.lookup("library_paths"); + size_t library_paths_len = library_paths.getLength(); + for (int i = 0; i < library_paths_len; i++) { + std::string value = library_paths[i]; + settings->library_paths.push_back(value); + } + } + + // load path_mappings (optional) + if (runtime_settings.exists("path_mappings")) { + auto& path_mappings = runtime_settings.lookup("path_mappings"); + size_t path_mappings_len = path_mappings.getLength(); + for (int i = 0; i < path_mappings_len; i++) { + std::string value = path_mappings[i]; + settings->path_mappings.push_back(value); + } + } + + // load environment (optional) + if (runtime_settings.exists("environment")) { + auto& environment = runtime_settings.lookup("environment"); + size_t environment_len = environment.getLength(); + for (int i = 0; i < environment_len; i++) { + std::string key = environment[i].getName(); + std::string value = environment[i]; + + settings->environment[key] = value; + } + } + + // set default runtime dir + settings->runtime_dir = strdup(getenv("APPDIR")); + + return settings; +} + +void apprun_update_env(const std::string& name, const std::string& value) { + if (!value.empty()) + setenv(name.c_str(), value.c_str(), 1); + else + unsetenv(name.c_str()); +} + +void apprun_set_private_env(const std::string& name, + const std::string& value, + const std::string& orig_value, + const std::string& start_value) { + apprun_update_env(name, value); + + std::string orig_name = APPRUN_ENV_ORIG_PREFIX + name; + apprun_update_env(orig_name, orig_value); + + + std::string startup_name = APPRUN_ENV_STARTUP_PREFIX + name; + apprun_update_env(startup_name, start_value); + +#ifdef DEBUG + fprintf(stderr, "APPRUN_DEBUG: set env %s=%s\n", name.c_str(), value.c_str()); +#endif +} diff --git a/src/apprun/apprun.h b/src/apprun/apprun.h new file mode 100644 index 0000000..ce46a3f --- /dev/null +++ b/src/apprun/apprun.h @@ -0,0 +1,135 @@ +/************************************************************************** + * + * Copyright (c) 2022 Alexis Lopez Zubieta + * + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + **************************************************************************/ + +#ifndef APPIMAGEEXECWRAPPER_APPRUN_H +#define APPIMAGEEXECWRAPPER_APPRUN_H + +#include +#include +#include +#include + +#define APPRUN_CONFIG_FILE_NAME "AppRun.config" + +struct AppRunSettings { + std::string runtime_dir; + std::string modules_dir; + std::vector exec; + std::vector library_paths; + std::vector path_mappings; + std::map environment; +}; + +/** + * Resolve the directory containing the AppRun executable + * + * @return directory containing the AppRun executable + */ +std::string resolve_origin(); + +/** + * Read config file into AppRunSettings. + * + * @param config_path + * @return + */ +AppRunSettings* load_config_file(const std::string& config_path); + +/** + * Execute the module check binary and load the settings in case of failure. + * + * The check binary perform the sole function of verifying if the system can provide the feature (or libraries) that + * are contained in the module. + * + * See the docs/MODULES.md for more details + * @param settings + * @param module_dir + */ +void setup_module(AppRunSettings* settings, const std::string& module_dir); + + +/** + * Assemble the PATH_MAPPINGS environment variable from settings. + * + * Join strings from AppRunSettings.path_mappings in pair of two representing the origin and target of the mapping. + * Two paths are join using a colon (:) to represent a mapping, different pairs are concatenated using semicolon (;). + * + * @param settings + * @return PATH_MAPPINGS + */ +std::string generate_path_mappings_env(AppRunSettings* settings); + +/** + * Assemble the LD_LIBRARY_PATH environment variable from settings. + * + * Join strings from AppRunSettings.library_paths a colon (:) as separator. + * See man ld.so for details about LD_LIBRARY_PATH + * + * @param settings + * @return LD_LIBRARY_PATH + */ +std::string generate_ld_library_path_value(AppRunSettings* settings); + +/** + * Update environment variable. The variable will be removed if the value is empty. + * + * @param name + * @param value + */ +void apprun_update_env(const std::string& name, const std::string& value); + +/** + * Set a given environment variable and the complementary original value and start value. + * + * See docs/PRIVATE_ENVIRONMENT.md for more details about the function of such variables. + * + * @param name + * @param value + * @param orig_value + * @param start_value + */ +void apprun_set_private_env(const std::string& name, + const std::string& value, + const std::string& orig_value, + const std::string& start_value); + + +/** + * Activates the private AppRun private environment and executes the command specified at AppRunSettings.exec + * + * The private environment setup goes as follows: + * - Set runtime dir if required + * - Expand variables from exec + * - call execv + * + * @param settings + * @param argv + * @return Error in case of failure otherwise the function will not return as the process will be replaced. + */ +int launch(AppRunSettings* settings, char** argv); + + +#endif //APPIMAGEEXECWRAPPER_APPRUN_H diff --git a/src/apprun/main.c b/src/apprun/main.c deleted file mode 100644 index 2e6dd76..0000000 --- a/src/apprun/main.c +++ /dev/null @@ -1,187 +0,0 @@ -/************************************************************************** - * - * Copyright (c) 2020 Alexis Lopez Zubieta - * - * All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - **************************************************************************/ - -#include -#include -#include -#include -#include -#include - -#include "common/string_list.h" -#include "common/shell_utils.h" - -#include "runtime_environment.h" -#include "runtime_interpreter.h" -#include "common/path.h" -#include "common/appdir_environment.h" - -#define die(...) \ - do { \ - fprintf(stderr, "APPRUN ERROR: " __VA_ARGS__); \ - exit(1); \ - } while(0); - -char *resolve_apprun_path(); - -char *find_legacy_env_file(char *apprun_path); - -char *build_env_file_path(char *apprun_path, unsigned long i); - -char *resole_appdir_path(const char *root_env_file_path); - -char *find_module_env_file(char *apprun_path); - -void launch(); - -char *resolve_origin(const char *apprun_path); - -int main(int argc, char *argv[]) { - char *apprun_path = resolve_apprun_path(); - char *origin_path = resolve_origin(apprun_path); - apprun_env_set("ORIGIN", origin_path, NULL, origin_path); - - char *module_env_file_path = find_module_env_file(apprun_path); - if (module_env_file_path != NULL) - apprun_load_env_file(module_env_file_path, argv); - - if (module_env_file_path == NULL) { - char *legacy_env_file_path = find_legacy_env_file(apprun_path); - char *appdir_path = resole_appdir_path(legacy_env_file_path); - - apprun_env_set("APPDIR", appdir_path, NULL, appdir_path); - apprun_load_env_file(legacy_env_file_path, argv); - } - - setup_runtime(); - launch(); - - return 1; -} - -char *resolve_origin(const char *apprun_path) { - char *origin_path_end = strrchr(apprun_path, '/'); - char *origin_path = strndup(apprun_path, origin_path_end - apprun_path); - - return origin_path; -} - -char *resole_appdir_path(const char *root_env_file_path) { - char *appdir = getenv("APPDIR"); - if (appdir == NULL) { - if (root_env_file_path != NULL) { - char *idx = strrchr(root_env_file_path, '/'); - appdir = strndup(root_env_file_path, idx - root_env_file_path); - } else { - die("Could not resolve APPDIR\n"); - } - } - return appdir; -} - -char *find_module_env_file(char *apprun_path) { - const char apprun_env_extension[] = ".env"; - const unsigned long apprun_env_extension_len = strlen(apprun_env_extension); - - const unsigned long possible_path_len = strlen(apprun_path) + apprun_env_extension_len + 1; - char *possible_path = malloc(possible_path_len); - memset(possible_path, 0, possible_path_len); - - apprun_concat_path(possible_path, apprun_path); - strncat(possible_path, apprun_env_extension, apprun_env_extension_len); - -#ifdef DEBUG - fprintf(stderr, "APPRUN_DEBUG: Looking for %s file at: %s\n", apprun_env_extension, possible_path); -#endif - if (access(possible_path, F_OK) == 0) - return possible_path; - else - free(possible_path); - - - return NULL; -} - -char *resolve_apprun_path() { - char *apprun_path = realpath("/proc/self/exe", NULL); - -#ifdef DEBUG - fprintf(stderr, "APPRUN_DEBUG: APPRUN_PATH=%s\n", apprun_path); -#endif - return apprun_path; -} - -char *find_legacy_env_file(char *apprun_path) { - char *slash_idx = strrchr(apprun_path, '/'); - - if (slash_idx != NULL) { - char *possible_path = build_env_file_path(apprun_path, slash_idx - apprun_path); -#ifdef DEBUG - fprintf(stderr, "APPRUN_DEBUG: Looking for .env file at: %s\n", possible_path); -#endif - if (access(possible_path, F_OK) == 0) { - return possible_path; - } else { - free(possible_path); - } - } - - return NULL; -} - -char *build_env_file_path(char *apprun_path, unsigned long i) { - char env_file_name[] = ".env"; - unsigned long env_file_name_len = 4; - - unsigned possible_path_len = i + env_file_name_len + 2 /*ZERO TERMINATION*/; - char *possible_path = calloc(possible_path_len, sizeof(char)); - memset(possible_path, 0, possible_path_len); - strncat(possible_path, apprun_path, i + 1); - strncat(possible_path, env_file_name, env_file_name_len); - - return possible_path; -} - -void launch() { - char *exec_path = require_environment(APPDIR_EXEC_PATH_ENV); - char *exec_args = require_environment(APPDIR_EXEC_ARGS_ENV); - - char **user_args = apprun_shell_split_arguments(exec_args); - unsigned user_args_len = apprun_string_list_len(user_args); - char **argv = calloc(user_args_len + 2, sizeof(char *)); - argv[0] = exec_path; - for (int i = 0; i < user_args_len; i++) - argv[i + 1] = user_args[i]; - -#ifdef DEBUG - fprintf(stderr, "APPRUN_DEBUG: executing "); - for (char **itr = argv; itr != NULL && *itr != NULL; itr++) - fprintf(stderr, "\"%s\" ", *itr); - fprintf(stderr, "\n"); -#endif - int ret = execv(exec_path, argv); - fprintf(stderr, "APPRUN_ERROR: %s", strerror(errno)); -} diff --git a/src/apprun/main.cpp b/src/apprun/main.cpp new file mode 100644 index 0000000..a64e777 --- /dev/null +++ b/src/apprun/main.cpp @@ -0,0 +1,84 @@ +/************************************************************************** + * + * Copyright (c) 2020 Alexis Lopez Zubieta + * + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + **************************************************************************/ + + +#include +#include +#include +#include + +#include "common/shell_utils.h" + +#include "apprun.h" + +void enable_required_modules(AppRunSettings* settings); + +int main(int argc, char* argv[]) { + std::string origin_path = resolve_origin(); + apprun_set_private_env("APPDIR", origin_path, {}, origin_path); + std::string config_path = origin_path + "/" + APPRUN_CONFIG_FILE_NAME; + auto* settings = load_config_file(config_path); + + if (!settings->modules_dir.empty()) { + enable_required_modules(settings); + } + + const std::string& ld_library_path_value = generate_ld_library_path_value(settings); + settings->environment["LD_LIBRARY_PATH"] = ld_library_path_value; + + const std::string& path_mappings_env_value = generate_path_mappings_env(settings); + settings->environment[APPDIR_PATH_MAPPINGS_ENV] = path_mappings_env_value; + + // set runtime environment + for (const auto& entry: settings->environment) { + const std::string& name = entry.first; + const std::string& value = entry.second; + std::string orig_value = getenv(name.c_str()) != nullptr ? getenv(name.c_str()) : std::string(); + char * expanded_value = apprun_shell_expand_variables(value.c_str(), argv); + apprun_set_private_env(name, expanded_value, orig_value, expanded_value); + } + + return launch(settings, argv); +} + +void enable_required_modules(AppRunSettings* settings) { + struct dirent* entry = nullptr; + DIR* dir = nullptr; + + char* modules_dir_path = apprun_shell_expand_variables(settings->modules_dir.c_str(), nullptr); + dir = opendir(modules_dir_path); + + if (dir != nullptr) { + while ((entry = readdir(dir))) { + std::string module_path = strdup(modules_dir_path); + module_path += '/'; + module_path += entry->d_name; + setup_module(settings, module_path); + } + } + + closedir(dir); +} diff --git a/src/apprun/runtime_environment.c b/src/apprun/runtime_environment.c deleted file mode 100644 index a6b7188..0000000 --- a/src/apprun/runtime_environment.c +++ /dev/null @@ -1,96 +0,0 @@ -/************************************************************************** - * - * Copyright (c) 2020 Alexis Lopez Zubieta - * - * All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - **************************************************************************/ - -#include -#include -#include -#include "runtime_environment.h" - -#include "common/shell_utils.h" -#include "common/string_list.h" -#include "hooks/environment.h" - - -void apprun_update_env(const char *name, const char *value) { - if (value) - setenv(name, value, 1); - else - unsetenv(name); -} - -void apprun_env_set(const char *name, const char *value, const char *orig_value, const char *start_value) { - apprun_update_env(name, value); - - char *orig_name = apprun_prefix_str(APPRUN_ENV_ORIG_PREFIX, name); - apprun_update_env(orig_name, orig_value); - - - char *startup_name = apprun_prefix_str(APPRUN_ENV_STARTUP_PREFIX, name); - apprun_update_env(startup_name, start_value); - -#ifdef DEBUG - fprintf(stderr, "APPRUN_DEBUG: set env %s=%s\n", name, value); -#endif - - free(orig_name); - free(startup_name); -} - -void apprun_load_env_file(const char *path, char **argv) { - FILE *fp; - char *line = NULL; - size_t len = 0; - ssize_t read; - - fp = fopen(path, "r"); - if (fp == NULL) { - fprintf(stderr, "APPRUN ERROR: Unable to open file: %s", path); - exit(EXIT_FAILURE); - } - - while ((read = getline(&line, &len, fp)) != -1) { - if (len > 0 && line[0] != '#') { - // Remove trailing new line chars - while (read > 0 && line[read - 1] == '\n') { - line[read - 1] = '\0'; - read--; - } - - char *name = apprun_env_str_entry_extract_name(line); - char *value = apprun_env_str_entry_extract_value(line); - char *expanded_value = apprun_shell_expand_variables(value, argv); - - apprun_env_set(name, expanded_value, getenv(name), expanded_value); - - free(expanded_value); - free(value); - free(name); - } - } - - free(line); -} - diff --git a/src/apprun/runtime_environment.h b/src/apprun/runtime_environment.h deleted file mode 100644 index 7d53c18..0000000 --- a/src/apprun/runtime_environment.h +++ /dev/null @@ -1,36 +0,0 @@ -/************************************************************************** - * - * Copyright (c) 2020 Alexis Lopez Zubieta - * - * All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - **************************************************************************/ - -#ifndef APPRUN_RUNTIME_ENVIRONMENT_H -#define APPRUN_RUNTIME_ENVIRONMENT_H - -void apprun_env_set(const char *name, const char *value, const char *orig_value, const char *start_value); - -void apprun_load_env_file(const char *path, char **argv); - -void apprun_update_env(const char *name, const char *value); - -#endif //APPRUN_RUNTIME_ENVIRONMENT_H diff --git a/src/apprun/runtime_interpreter.c b/src/apprun/runtime_interpreter.c deleted file mode 100644 index fe06502..0000000 --- a/src/apprun/runtime_interpreter.c +++ /dev/null @@ -1,225 +0,0 @@ -/************************************************************************** - * - * Copyright (c) 2020 Alexis Lopez Zubieta - * - * All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - **************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "common/file_utils.h" -#include "common/string_list.h" -#include "common/shell_utils.h" -#include "hooks/environment.h" - -#include "runtime_interpreter.h" -#include "runtime_environment.h" -#include "common/path.h" -#include "common/appdir_environment.h" - - -char *parse_ld_trace_line_path(const char *line) { - char *path = NULL; - const char *path_start = strstr(line, "=> "); - if (path_start != NULL) { - path_start += 3; - } else { - path_start = line; - while (path_start != NULL && isspace(*path_start)) - path_start++; - } - - char *path_end = strstr(path_start, " ("); - - if (path_end != NULL) - path = strndup(path_start, path_end - path_start); - else - path = strdup(path_start); - - - return path; -} - -bool is_linker_version_string_valid(char *buff) { - unsigned buff_len = strlen(buff); - return (isdigit(buff[0]) && isdigit(buff[buff_len - 1])); -} - - -char *try_read_ld_version_string(FILE *fp) { - char glibc_version_prefix[] = "ld-"; - int c = 0; - for (int i = 1; i < strlen(glibc_version_prefix); i++) { - c = fgetc(fp); - if (c != glibc_version_prefix[i]) - return 0; - } - - char buff[256] = {0x0}; - for (int i = 0; i < 256; i++) { - c = fgetc(fp); - if (c == '\0' || c == -1) - break; - - buff[i] = c; - } - - char *suffix_idx = strstr(buff, ".so"); - if (suffix_idx) - *suffix_idx = 0; - - if (is_linker_version_string_valid(buff)) - return strdup(buff); - - return NULL; -} - -char *read_ld_version(char *path) { - char version[254] = {0x0}; - FILE *fp = fopen(path, "r"); - if (fp) { - int itr; - while ((itr = fgetc(fp)) != EOF) { - if (itr == 'l') { - char *new_version = try_read_ld_version_string(fp); - if (new_version != NULL) { - if (compare_version_strings(new_version, version) > 0) - strcpy(version, new_version); - - free(new_version); - } - } - } - - fclose(fp); - } - - return strdup(version); -} - -long compare_version_strings(const char *a, const char *b) { - if (a == NULL || b == NULL) - return a - b; - - long a_vals[3] = {0x0}; - long b_vals[3] = {0x0}; - - sscanf(a, "%ld.%ld.%ld", a_vals, a_vals + 1, a_vals + 2); - sscanf(b, "%ld.%ld.%ld", b_vals, b_vals + 1, b_vals + 2); - - for (int i = 0; i < 3; i++) { - long diff = a_vals[i] - b_vals[i]; - if (diff != 0) - return diff; - } - - return 0; -} - -void configure_embed_libc() { -#ifdef DEBUG - fprintf(stderr, "APPRUN_DEBUG: using appdir libc\n"); -#endif - char *ld_library_path = apprun_shell_expand_variables("$"APPDIR_LIBRARY_PATH_ENV":" - "$"APPDIR_LIBC_LIBRARY_PATH_ENV":" - "$"APPRUN_ENV_ORIG_PREFIX"LD_LIBRARY_PATH", NULL); - - apprun_env_set("LD_LIBRARY_PATH", ld_library_path, "", ld_library_path); - free(ld_library_path); -} - -void configure_system_libc() { -#ifdef DEBUG - fprintf(stderr, "APPRUN_DEBUG: using system libc\n"); -#endif - char *ld_library_path = apprun_shell_expand_variables("$"APPDIR_LIBRARY_PATH_ENV":" - "$"APPRUN_ENV_ORIG_PREFIX"LD_LIBRARY_PATH", NULL); - - apprun_env_set("LD_LIBRARY_PATH", ld_library_path, "", ld_library_path); - free(ld_library_path); -} - -char *require_environment(char *name) { - char *value = getenv(name); - if (value == NULL) { - fprintf(stderr, "APPRUN ERROR: Missing %s environment", name); - exit(1); - } - - return value; -} - -char *resolve_runtime_path(const char *prefix) { - char *appdir = require_environment("APPDIR"); - int appdir_len = strlen(appdir); - int runtime_prefix_len = strlen(prefix); - int extra_slash_len = 1; - - int path_len = appdir_len + extra_slash_len + runtime_prefix_len + 1; - char *path = calloc(path_len, sizeof(char)); - memset(path, 0, path_len); - - apprun_concat_path(path, appdir); - apprun_concat_path(path, prefix); - - return path; -} - -void setup_runtime() { - char *linkers = strdup(getenv(APPDIR_LIBC_LINKER_PATH_ENV)); - char *ld_relpath = strtok(linkers, APPDIR_LIBC_LINKER_PATH_ENV_SEPARATOR); - if (ld_relpath != NULL) { - const char *system_ld_version = gnu_get_libc_version(); - const char *appdir_ld_version = getenv(APPDIR_LIBC_VERSION_ENV); - -#ifdef DEBUG - fprintf(stderr, "APPRUN_DEBUG: interpreter \"%s\" \n", ld_relpath); - fprintf(stderr, "APPRUN_DEBUG: system ld(%s), appdir ld(%s) \n", system_ld_version, appdir_ld_version); -#endif - char *runtime_path = NULL; - if (compare_version_strings(system_ld_version, appdir_ld_version) > 0) { - runtime_path = resolve_runtime_path("runtime/default"); - configure_system_libc(); - } else { - runtime_path = resolve_runtime_path("runtime/compat"); - configure_embed_libc(); - } - - apprun_env_set("LD_PRELOAD", "libapprun_hooks.so", "", "libapprun_hooks.so"); - apprun_env_set(APPRUN_ENV_RUNTIME, runtime_path, "", runtime_path); - - char cwd[PATH_MAX]; - getcwd(cwd, PATH_MAX); - setenv(APPRUN_ENV_ORIGINAL_WORKDIR, cwd, 1); - chdir(runtime_path); - } -} - - diff --git a/src/apprun/runtime_interpreter.h b/src/apprun/runtime_interpreter.h deleted file mode 100644 index bb214cf..0000000 --- a/src/apprun/runtime_interpreter.h +++ /dev/null @@ -1,52 +0,0 @@ -/************************************************************************** - * - * Copyright (c) 2020 Alexis Lopez Zubieta - * - * All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - **************************************************************************/ - -#ifndef APPIMAGEEXECWRAPPER_RUNTIME_INTERPRETER_H -#define APPIMAGEEXECWRAPPER_RUNTIME_INTERPRETER_H - -#include - -// executable path and arguments - -// application library paths - -// libc specific - -long compare_version_strings(const char *a, const char *b); - -bool is_linker_version_string_valid(char *buff); - -char *read_ld_version(char *path); - -char *parse_ld_trace_line_path(const char *line); - -void setup_runtime(); - -void configure_system_libc(); - -char *require_environment(char *name); - -#endif //APPIMAGEEXECWRAPPER_RUNTIME_INTERPRETER_H diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 21db900..23cb81a 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -1,7 +1,6 @@ add_library( common_objects OBJECT - file_utils.c - string_list.c + string_list.c path.c string_utils.c shell_utils.c diff --git a/src/common/file_utils.c b/src/common/file_utils.c deleted file mode 100644 index 40b35be..0000000 --- a/src/common/file_utils.c +++ /dev/null @@ -1,105 +0,0 @@ -/************************************************************************** - * - * Copyright (c) 2020 Alexis Lopez Zubieta - * - * All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - **************************************************************************/ - -#include -#include -#include - -#include "file_utils.h" -#include "string_list.h" - -char **apprun_read_lines(FILE *fp) { - int array_len = 1024; - int count = 0; - char **result = apprun_string_list_alloc(array_len); - - if (fp) { - size_t len = 0; - ssize_t read; - - while ((read = getline(&result[count], &len, fp)) != -1) { - if (read > 0 && result[count][read - 1] == '\n') - result[count][read - 1] = 0; - count++; - if (count == array_len) { - array_len = array_len * 2; - char **old_array = result; - - result = apprun_string_list_alloc(array_len); - for (int i = 0; i < count; i++) - result[i] = old_array[i]; - - free(old_array); - } - } - } - - result[count] = NULL; - - return result; -} - -char **apprun_file_read_lines(const char *filename) { - char **result = NULL; - - FILE *fp = fopen(filename, "r"); - if (fp) { - result = apprun_read_lines(fp); - result = apprun_adjust_string_array_size(result); - fclose(fp); - } - - return result; -} - -void apprun_file_copy(const char *source_path, const char *target_path) { - FILE *source, *target; - - source = fopen(source_path, "r"); - target = fopen(target_path, "w"); - - if (source == NULL) { - fprintf(stderr, "Unable to read file: %s\n", source_path); - exit(1); - } - - if (target == NULL) { - fprintf(stderr, "Unable to write file: %s\n", target_path); - exit(1); - } - - - int ch; - while ((ch = fgetc(source)) != EOF) - fputc(ch, target); - - fclose(source); - fclose(target); -} - - - - diff --git a/src/common/file_utils.h b/src/common/file_utils.h deleted file mode 100644 index 42eae83..0000000 --- a/src/common/file_utils.h +++ /dev/null @@ -1,38 +0,0 @@ -/************************************************************************** - * - * Copyright (c) 2020 Alexis Lopez Zubieta - * - * All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - **************************************************************************/ - -#ifndef APPRUN_FILE_UTILS_H -#define APPRUN_FILE_UTILS_H - -#include - -char **apprun_read_lines(FILE *fp); - -char **apprun_file_read_lines(const char *filename); - -void apprun_file_copy(const char *source_path, const char *target_path); - -#endif //APPRUN_FILE_UTILS_H diff --git a/src/common/path.h b/src/common/path.h index 6d5a9fd..178b317 100644 --- a/src/common/path.h +++ b/src/common/path.h @@ -28,10 +28,20 @@ #ifndef APPRUN_PATH_H #define APPRUN_PATH_H +#ifdef __cplusplus +extern "C" +{ +#endif + #include bool apprun_is_path_child_of(const char *path, const char *base); void apprun_concat_path(char *dest, const char *path); + +#ifdef __cplusplus +} +#endif + #endif //APPRUN_PATH_H diff --git a/src/common/shell_utils.h b/src/common/shell_utils.h index 53952a6..98e5f9c 100644 --- a/src/common/shell_utils.h +++ b/src/common/shell_utils.h @@ -27,10 +27,19 @@ #ifndef APPIMAGEEXECWRAPPER_SHELL_UTILS_H #define APPIMAGEEXECWRAPPER_SHELL_UTILS_H -char *apprun_shell_expand_variables(char const *str, char **argv); +#ifdef __cplusplus +extern "C" +{ +#endif -char **apprun_shell_split_arguments(char const *str); +char* apprun_shell_expand_variables(char const* str, char** argv); -char *apprun_shell_resolve_var_value(char *const *argv, const char *var_name); +char** apprun_shell_split_arguments(char const* str); +char* apprun_shell_resolve_var_value(char* const* argv, const char* var_name); + + +#ifdef __cplusplus +} +#endif #endif //APPIMAGEEXECWRAPPER_SHELL_UTILS_H diff --git a/src/common/string_list.h b/src/common/string_list.h index 6583ff7..737857e 100644 --- a/src/common/string_list.h +++ b/src/common/string_list.h @@ -27,6 +27,11 @@ #ifndef APPRUN_STRING_LIST_H #define APPRUN_STRING_LIST_H +#ifdef __cplusplus +extern "C" +{ +#endif + #include char *apprun_prefix_str(const char *prefix, const char *str); @@ -47,4 +52,9 @@ char **apprun_string_list_dup(char *const *list); char *apprun_string_list_join(char *const *string_list, char *split); + +#ifdef __cplusplus +} +#endif + #endif //APPRUN_STRING_LIST_H diff --git a/src/common/string_utils.c b/src/common/string_utils.c index 8bf1e69..1a47a4d 100644 --- a/src/common/string_utils.c +++ b/src/common/string_utils.c @@ -28,6 +28,7 @@ #include #include #include +#include #include "string_utils.h" @@ -86,3 +87,21 @@ char *apprun_string_trim(char *str) { return result; } +long compare_version_strings(const char *a, const char *b) { + if (a == NULL || b == NULL) + return a - b; + + long a_vals[3] = {0x0}; + long b_vals[3] = {0x0}; + + sscanf(a, "%ld.%ld.%ld", a_vals, a_vals + 1, a_vals + 2); + sscanf(b, "%ld.%ld.%ld", b_vals, b_vals + 1, b_vals + 2); + + for (int i = 0; i < 3; i++) { + long diff = a_vals[i] - b_vals[i]; + if (diff != 0) + return diff; + } + + return 0; +} \ No newline at end of file diff --git a/src/common/string_utils.h b/src/common/string_utils.h index c510a77..46b2b53 100644 --- a/src/common/string_utils.h +++ b/src/common/string_utils.h @@ -40,4 +40,17 @@ bool apprun_string_is_all_blanks(const char *str); char *apprun_string_trim(char *str); +/** + * Compare version strings in the format X.X.X + * Version parts will be split and rested one at the time. + * If result differs from 0 it's returned. + * If the last part diff is 0 then 0 is returned. + * + * Returns: + * - 0: if versions are equal. + * - positive value if a is greater than b + * - negative value if a is lower than b + * */ +long compare_version_strings(const char *a, const char *b); + #endif //APPIMAGEEXECWRAPPER_STRING_UTILS_H diff --git a/src/modules/CMakeLists.txt b/src/modules/CMakeLists.txt new file mode 100644 index 0000000..e534676 --- /dev/null +++ b/src/modules/CMakeLists.txt @@ -0,0 +1,21 @@ +add_library(module_common OBJECT common.c) + +add_executable(check-glibc check-glibc.c $) +target_link_libraries(check-glibc PRIVATE libconfig common_objects) + +add_executable(check-glibstdc++ check-glibstdc++.cpp $) +target_link_libraries(check-glibstdc++ PRIVATE libconfig common_objects) +target_compile_options(check-glibstdc++ PUBLIC $<$:-std=c++11>) + +# check-glibstdc++ was written using the least amount of c++ in order to ensure backward compatibility with Centos 6 +# and it doesn't get linked to "libstdc++.so.6" therefore we need force the dependency in order to make it available at +# runtime for dl_iterate_phdr +add_custom_command(TARGET check-glibstdc++ POST_BUILD + COMMAND patchelf --add-needed libstdc++.so.6 $ + COMMENT "Adding libstdc++.so.6 as dependency" + ) + +install( + TARGETS check-glibc check-glibstdc++ + RUNTIME DESTINATION bin +) \ No newline at end of file diff --git a/src/modules/check-glibc.c b/src/modules/check-glibc.c new file mode 100644 index 0000000..6aed75e --- /dev/null +++ b/src/modules/check-glibc.c @@ -0,0 +1,46 @@ +#include +#include +#include +#include + +#include + +#include "common/string_utils.h" +#include "common.h" + +char* read_required_glibstdcpp_version_from_config(char* config_path) { + config_t cfg; + const char* str; + + config_init(&cfg); + + /* Read the file. If there is an error, report it and exit. */ + if (!config_read_file(&cfg, config_path)) { + fprintf(stderr, "ERROR:check-libc: %s:%d - %s\n", config_error_file(&cfg), config_error_line(&cfg), + config_error_text(&cfg)); + config_destroy(&cfg); + exit(EXIT_FAILURE); + } + + if (!config_lookup_string(&cfg, "check.required_glibc", &str)) { + fprintf(stderr, "ERROR:check-libc: missing config entry 'check.required_glibc'"); + exit(EXIT_FAILURE); + } + + char* glibc_version = strdup(str); + + config_destroy(&cfg); + return glibc_version; +} + +int main() { + char* config_path = resolve_module_config_path(); + const char* required_glibc_version = read_required_glibstdcpp_version_from_config(config_path); + const char* system_glibc_version = gnu_get_libc_version(); + + // the libc module must be used if the system version is lesser than the required + if (compare_version_strings(system_glibc_version, required_glibc_version) > 0) + return EXIT_SUCCESS; + else + return EXIT_FAILURE; +} \ No newline at end of file diff --git a/src/modules/check-glibstdc++.cpp b/src/modules/check-glibstdc++.cpp new file mode 100644 index 0000000..d9c21f9 --- /dev/null +++ b/src/modules/check-glibstdc++.cpp @@ -0,0 +1,88 @@ +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "common.h" +#include "common/string_utils.h" + +#ifdef __cplusplus +} +#endif + +typedef struct { + regex_t regex; + char* version; +} extract_libstdcpp_version_ctx_t; + +static int extract_libstdcpp_version(struct dl_phdr_info* info, size_t, void* data) { + regmatch_t match[2] = {0x0}; + auto* ctx = (extract_libstdcpp_version_ctx_t*) data; + char* real_so_path = realpath(info->dlpi_name, nullptr); + + if (real_so_path != NULL && regexec(&ctx->regex, real_so_path, 2, match, 0) == 0) { + char* version = strndup(real_so_path + match[1].rm_so, match[1].rm_eo - match[1].rm_so); + ctx->version = version; + return 1; + } + + return 0; +} + +char* extract_libstdcpp_version_from_runtime_libaries_paths() { + extract_libstdcpp_version_ctx_t extract_libstdcpp_version_ctx = {nullptr}; + + if (regcomp(&extract_libstdcpp_version_ctx.regex, R"(libstdc\+\+\.so\.([0-9]+\.[0-9]+(\.[0-9]+)?))", + REG_EXTENDED) != 0) { + printf("Regex compilation error."); + exit(EXIT_FAILURE); + } + + // iterate over the loaded libraries to resolve their paths and guess the libstdc++ version from it + dl_iterate_phdr(extract_libstdcpp_version, &extract_libstdcpp_version_ctx); + return extract_libstdcpp_version_ctx.version; +} + +char* read_required_glibstdcpp_version_from_config(char* config_path) { + config_t cfg; + const char* str; + + config_init(&cfg); + + /* Read the file. If there is an error, report it and exit. */ + if (!config_read_file(&cfg, config_path)) { + fprintf(stderr, "ERROR:check-glibstdc++: %s:%d - %s\n", config_error_file(&cfg), config_error_line(&cfg), + config_error_text(&cfg)); + config_destroy(&cfg); + exit(EXIT_FAILURE); + } + + if (!config_lookup_string(&cfg, "check.required_glibstdcpp", &str)) { + fprintf(stderr, "ERROR:check-glibstdc++: missing config entry 'check.required_glibstdcpp'"); + exit(EXIT_FAILURE); + } + + char* glibstdcpp_version = strdup(str); + + config_destroy(&cfg); + return glibstdcpp_version; +} + +int main(int, char* []) { + char* system_version = extract_libstdcpp_version_from_runtime_libaries_paths(); + + char* config_path = resolve_module_config_path(); + char* required_version = read_required_glibstdcpp_version_from_config(config_path); + + // the libc module must be used if the system version is lesser than the required + if (compare_version_strings(system_version, required_version) > 0) + return EXIT_SUCCESS; + else + return EXIT_FAILURE; +} \ No newline at end of file diff --git a/src/modules/common.c b/src/modules/common.c new file mode 100644 index 0000000..1d465ed --- /dev/null +++ b/src/modules/common.c @@ -0,0 +1,25 @@ +#include +#include +#include +#include + +#include "common.h" + +char* resolve_module_config_path() { + char* path = malloc(FILENAME_MAX * sizeof(char)); + if (realpath("/proc/self/exe", path) == NULL) { + fprintf(stderr, "ERROR:check-libc: Unable to find binary location"); + exit(EXIT_FAILURE); + } + + // replace binary name by 'config' + char* dir_slash_idx = strrchr(path, '/'); + *dir_slash_idx = 0; + strcat(dir_slash_idx, "/config"); + if (access(path, F_OK) != 0) { + fprintf(stderr, "ERROR:check-libc: Unable to find the config file at: %s", path); + exit(EXIT_FAILURE); + } + + return path; +} diff --git a/src/modules/common.h b/src/modules/common.h new file mode 100644 index 0000000..c61ebbe --- /dev/null +++ b/src/modules/common.h @@ -0,0 +1,7 @@ +#ifndef APPIMAGEEXECWRAPPER_COMMON_H +#define APPIMAGEEXECWRAPPER_COMMON_H + + +char* resolve_module_config_path(); + +#endif //APPIMAGEEXECWRAPPER_COMMON_H diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 0da394c..c820429 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -2,17 +2,20 @@ include_directories(${PROJECT_SOURCE_DIR}/src) set(APPDIR_MOCK_PATH "${CMAKE_CURRENT_BINARY_DIR}/mock.AppDir") +string(RANDOM LENGTH 6 MAPPED_APPDIR_PATH_HASH) +set(MAPPED_APPDIR_PATH "/AppDir-${MAPPED_APPDIR_PATH_HASH}") add_subdirectory(common) add_subdirectory(hooks) -add_subdirectory(apprun) add_custom_target( - mock.AppDir - COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/utils/create_appdir.sh ${APPDIR_MOCK_PATH} ${PROJECT_BINARY_DIR} - BYPRODUCTS ${APPDIR_MOCK_PATH}/AppRun - DEPENDS AppRun apprun_hooks hooks_inner_target_test + mock.AppDir + COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/utils/create_appdir.sh ${APPDIR_MOCK_PATH} ${MAPPED_APPDIR_PATH} ${PROJECT_BINARY_DIR} + BYPRODUCTS ${APPDIR_MOCK_PATH}/AppRun + DEPENDS AppRun apprun_hooks hooks_inner_target_test check-glibc check-glibstdc++ ) # remove mock on make clean -set_directory_properties(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES ${APPDIR_MOCK_PATH} ) \ No newline at end of file +set_directory_properties(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES ${APPDIR_MOCK_PATH}) + +add_subdirectory(modules) \ No newline at end of file diff --git a/test/apprun/CMakeLists.txt b/test/apprun/CMakeLists.txt deleted file mode 100644 index 0d4b299..0000000 --- a/test/apprun/CMakeLists.txt +++ /dev/null @@ -1,10 +0,0 @@ -add_executable( - runtime_interpreter_test - runtime_interpreter_test.c - ../common/tests_shared.c - $ - $ -) - -target_link_libraries(runtime_interpreter_test dl) -add_test(NAME TEST_RUNTIME_INTERPRETER COMMAND runtime_interpreter_test) diff --git a/test/apprun/runtime_interpreter_test.c b/test/apprun/runtime_interpreter_test.c deleted file mode 100644 index 1ddcc80..0000000 --- a/test/apprun/runtime_interpreter_test.c +++ /dev/null @@ -1,100 +0,0 @@ -/************************************************************************** - * - * Copyright (c) 2020 Alexis Lopez Zubieta - * - * All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - **************************************************************************/ - -#include -#include -#include - -#include "apprun/runtime_interpreter.h" -#include "../common/tests_shared.h" - -void test_parse_ld_trace_lib_path() { - printf("%s: ", __PRETTY_FUNCTION__); - - char *libc_path = parse_ld_trace_line_path(" libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xf7c8b000)"); - assert_str_eq(libc_path, "/lib/i386-linux-gnu/libc.so.6"); - free(libc_path); - - char *interpreter_path = parse_ld_trace_line_path(" /lib/ld-linux.so.2 (0xf7f36000)"); - assert_str_eq(interpreter_path, "/lib/ld-linux.so.2"); - free(interpreter_path); - - char *not_found_path = parse_ld_trace_line_path(" libidn.so.11 => not found"); - assert_str_eq(not_found_path, "not found"); - free(not_found_path); - - printf("Ok\n"); -} - -void test_validate_glibc_version_string() { - printf("%s: ", __PRETTY_FUNCTION__); - assert_true(is_linker_version_string_valid("2.3.4")); - assert_false(is_linker_version_string_valid("PRIVATE")); - - printf("Ok\n"); -} - -void test_read_ld_version() { - printf("%s: ", __PRETTY_FUNCTION__); - char *version = read_ld_version("/lib64/ld-linux-x86-64.so.2"); - const char *expected_version = gnu_get_libc_version(); - assert_str_eq(version, expected_version); - free(version); - - printf("Ok\n"); -} - -void test_compare_glib_version_strings() { - printf("%s: ", __PRETTY_FUNCTION__); - - assert_eq(compare_version_strings("1", "1"), 0); - assert_eq(compare_version_strings("2", "1"), 1); - assert_eq(compare_version_strings("1", "2"), -1); - assert_eq(compare_version_strings("1.1", "1.1"), 0); - assert_eq(compare_version_strings("1.2", "1.1"), 1); - assert_eq(compare_version_strings("1.1", "1.2"), -1); - assert_eq(compare_version_strings("1.2", "1.2.1"), -1); - assert_eq(compare_version_strings("1.2.1", "1.2.1"), 0); - assert_eq(compare_version_strings("1.2.1", "1.2"), 1); - - assert_true(compare_version_strings(NULL, "1") < 0); - assert_true(compare_version_strings("1", NULL) > 0); - assert_true(compare_version_strings(NULL, NULL) == 0); - - printf("Ok\n"); -} - -int main(int argc, char **argv, char *envp[]) { - test_parse_ld_trace_lib_path(); - test_validate_glibc_version_string(); - test_compare_glib_version_strings(); - test_read_ld_version(); - - return 0; -} - - - diff --git a/test/common/CMakeLists.txt b/test/common/CMakeLists.txt index df58314..7bd48d4 100644 --- a/test/common/CMakeLists.txt +++ b/test/common/CMakeLists.txt @@ -3,20 +3,6 @@ pkg_check_modules(CHECK REQUIRED check) include_directories(${CHECK_INCLUDE_DIRS}) -add_executable( - file_utils_test - file_utils_test.c - tests_shared.c - $ -) -target_compile_definitions( - file_utils_test PRIVATE - "TESTS_DIR=\"${CMAKE_CURRENT_SOURCE_DIR}/..\"" - "APPDIR_MOCK_PATH=\"${APPDIR_MOCK_PATH}\"" -) - -add_test(NAME TEST_FILE_UTILS COMMAND file_utils_test) - add_executable( path_test path_test.c diff --git a/test/common/file_utils_test.c b/test/common/file_utils_test.c deleted file mode 100644 index 39e4985..0000000 --- a/test/common/file_utils_test.c +++ /dev/null @@ -1,52 +0,0 @@ -/************************************************************************** - * - * Copyright (c) 2020 Alexis Lopez Zubieta - * - * All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - **************************************************************************/ - -#include -#include - -#include "tests_shared.h" -#include "common/file_utils.h" - -void test_apprun_file_read_lines() { - printf("%s: ", __PRETTY_FUNCTION__); - - char **res = apprun_file_read_lines(APPDIR_MOCK_PATH"/AppRun.env"); - assert_true(res != NULL); - - assert_str_eq(res[0], "APPDIR=$ORIGIN"); - - apprun_string_list_free(res); - - printf("Ok\n"); -} - -int main(int argc, char **argv) { - test_apprun_file_read_lines(); - - return 0; -} - - diff --git a/test/hooks/CMakeLists.txt b/test/hooks/CMakeLists.txt index 1a256f0..1fdfd56 100644 --- a/test/hooks/CMakeLists.txt +++ b/test/hooks/CMakeLists.txt @@ -11,9 +11,6 @@ target_link_libraries(environment_test apprun_hooks) add_test(NAME TEST_ENVIRONMENT COMMAND environment_test) -string(RANDOM LENGTH 6 MAPPED_APPDIR_PATH_HASH) -set(MAPPED_APPDIR_PATH "/AppDir-${MAPPED_APPDIR_PATH_HASH}") - # outer target add_executable(hooks_outer_target_test outer_target_test.c ../common/tests_shared.c $) target_compile_definitions( diff --git a/test/modules/CMakeLists.txt b/test/modules/CMakeLists.txt new file mode 100644 index 0000000..0babb02 --- /dev/null +++ b/test/modules/CMakeLists.txt @@ -0,0 +1,9 @@ +add_test( + NAME TEST_CHECK_GLIBC + COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/test-check-glibc.sh $ +) + +add_test( + NAME TEST_CHECK_GLIBSTDCPP + COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/test-check-glibstdc++.sh $ +) \ No newline at end of file diff --git a/test/modules/common.sh b/test/modules/common.sh new file mode 100644 index 0000000..2a74f41 --- /dev/null +++ b/test/modules/common.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +function assert_success() { + if $@; then + echo " [PASS]" + else + echo " [FAIL]" + exit 1 + fi +} + +function assert_fail() { + if $@; then + echo " [FAIL]" + exit 1 + else + echo " [PASS]" + fi +} \ No newline at end of file diff --git a/test/modules/test-check-glibc.sh b/test/modules/test-check-glibc.sh new file mode 100755 index 0000000..07cf543 --- /dev/null +++ b/test/modules/test-check-glibc.sh @@ -0,0 +1,38 @@ +#!/bin/bash + +set -e + +TEST_DIR="$PWD" +CHECK_GLIBC_BIN=$1 +if [ -z "$CHECK_GLIBC_BIN" ]; then + echo "Missing CHECK_GLIBC_BIN" + exit 1 +fi + +function setup_test_case() { + TARGET_DIR=$1 + REQUIRED_GLIBC=$2 + mkdir -p "$TARGET_DIR" + cp "$CHECK_GLIBC_BIN" "$TARGET_DIR/check" + cat >"$TARGET_DIR/config" <"$TARGET_DIR/config" < " | cut -d' ' -f 3- | cut -d ' ' -f 1) LIBRARY_PATHS="" for DEP in $DEPENDENCIES; do - LIBRARY_PATH=$(dirname "${APPDIR_COMPAT_RUNTIME}"/"${DEP}") + LIBRARY_PATH=$(dirname "${LIBC_MODULE_PATH}"/"${DEP}") LIBRARY_PATHS="${LIBRARY_PATHS}:${LIBRARY_PATH}" mkdir -p "$LIBRARY_PATH" - cp "$DEP" "${APPDIR_COMPAT_RUNTIME}/$DEP" + cp "$DEP" "${LIBC_MODULE_PATH}/$DEP" done APPDIR_LIBC_LIBRARY_PATH="$(remove_duplicated_paths "${LIBRARY_PATHS:1}")" APPDIR_LIBC_VERSION=$(ldd --version | grep GLIBC | rev | cut -d" " -f 1 | rev) - export APPDIR_LIBC_LIBRARY_PATH - export APPDIR_LIBC_VERSION + # deploy runtime tools + cp "$CHECK_LIBC" "$APPDIR/opt/libc/check" + + # deploy config file + cat >$APPDIR/opt/libc/config <$APPDIR/opt/libstdc++/config < "$APPDIR/usr/bin/script" +echo "#! $REL_BIN_PATH" >"$APPDIR/usr/bin/script" chmod +x "$APPDIR/usr/bin/script" - # read linker path from bin APPDIR_LIBC_LINKER_PATH=$(patchelf --print-interpreter "$DEPLOYED_BIN_PATH") APPDIR_LIBC_LINKER_PATH="${APPDIR_LIBC_LINKER_PATH:1}" @@ -110,8 +176,12 @@ APPDIR_LIBC_LINKER_PATH="${APPDIR_LIBC_LINKER_PATH:1}" # patch bin with a relative linker path patchelf --set-interpreter "$APPDIR_LIBC_LINKER_PATH" "$DEPLOYED_BIN_PATH" -create_compat_runtime -create_default_runtime +# make libstdc++.so.6 a dependency +patchelf --add-needed "libstdc++.so.6" "$DEPLOYED_BIN_PATH" + +setup_default_linker +create_libc_module +create_libstdcpp_module APPDIR_LIBRARY_PATH="$APPDIR/lib/" cp "$APPRUN_HOOKS" "$APPDIR/lib/" @@ -119,12 +189,20 @@ cp "$APPRUN_HOOKS" "$APPDIR/lib/" # deploy AppRun cp "$APPRUN" "$APPDIR" -echo "APPDIR=\$ORIGIN -APPDIR_EXEC_PATH=\$APPDIR/usr/bin/script -APPDIR_EXEC_ARGS=\$@ -APPDIR_LIBC_VERSION=$APPDIR_LIBC_VERSION -APPDIR_LIBC_LINKER_PATH=$APPDIR_LIBC_LINKER_PATH -APPDIR_LIBC_LIBRARY_PATH=$APPDIR_LIBC_LIBRARY_PATH -APPDIR_LIBRARY_PATH=$APPDIR_LIBRARY_PATH" >"$APPDIR/AppRun.env" - -sed -i "s|$APPDIR|\$APPDIR|g" "$APPDIR/AppRun.env" +cat >"$APPDIR/AppRun.config" </dev/null | gpg --dearmor - | tee /usr/share/keyrings/kitware-archive-keyring.gpg >/dev/null diff --git a/tooling/docker/Dockerfile.gnueabihf b/tooling/docker/Dockerfile.gnueabihf index 41f55e8..8875101 100644 --- a/tooling/docker/Dockerfile.gnueabihf +++ b/tooling/docker/Dockerfile.gnueabihf @@ -1,7 +1,7 @@ FROM ubuntu:16.04 RUN apt-get update -RUN apt-get install -y check g++ pkg-config apt-transport-https wget patchelf g++-multilib qemu-user-binfmt +RUN apt-get install -y check g++ pkg-config apt-transport-https wget patchelf g++-multilib qemu-user-binfmt git ### install latest cmake from Kitware ppa RUN wget -O - https://apt.kitware.com/keys/kitware-archive-latest.asc 2>/dev/null | gpg --dearmor - | tee /usr/share/keyrings/kitware-archive-keyring.gpg >/dev/null diff --git a/tooling/docker/Dockerfile.i386 b/tooling/docker/Dockerfile.i386 index 0ae97b9..0bc7cf6 100644 --- a/tooling/docker/Dockerfile.i386 +++ b/tooling/docker/Dockerfile.i386 @@ -1,7 +1,7 @@ FROM ubuntu:16.04 RUN apt-get update -RUN apt-get install -y check g++ pkg-config apt-transport-https wget patchelf g++-multilib libc6-dev-i386 +RUN apt-get install -y check g++ pkg-config apt-transport-https wget patchelf g++-multilib libc6-dev-i386 git ### install latest cmake from Kitware ppa RUN wget -O - https://apt.kitware.com/keys/kitware-archive-latest.asc 2>/dev/null | gpg --dearmor - | tee /usr/share/keyrings/kitware-archive-keyring.gpg >/dev/null diff --git a/tooling/docker/Dockerfile.x86_64 b/tooling/docker/Dockerfile.x86_64 index 38a2ffb..8d8a86c 100644 --- a/tooling/docker/Dockerfile.x86_64 +++ b/tooling/docker/Dockerfile.x86_64 @@ -1,7 +1,7 @@ FROM ubuntu:16.04 RUN apt-get update -RUN apt-get install -y check g++ pkg-config apt-transport-https wget patchelf +RUN apt-get install -y check g++ pkg-config apt-transport-https wget patchelf git ### install latest cmake from Kitware ppa RUN wget -O - https://apt.kitware.com/keys/kitware-archive-latest.asc 2>/dev/null | gpg --dearmor - | tee /usr/share/keyrings/kitware-archive-keyring.gpg >/dev/null