From 7da3a0309c7376e9be2a624ab593f2585c3881d9 Mon Sep 17 00:00:00 2001 From: Alexis Lopez Zubieta Date: Tue, 24 May 2022 14:06:15 -0500 Subject: [PATCH 01/28] Implement check-runtime binary to be used on app startup --- src/CMakeLists.txt | 1 + src/check-runtime/CMakeLists.txt | 9 ++++ src/check-runtime/main.cpp | 84 ++++++++++++++++++++++++++++++++ test/test-check-runtime.sh | 17 +++++++ 4 files changed, 111 insertions(+) create mode 100644 src/check-runtime/CMakeLists.txt create mode 100644 src/check-runtime/main.cpp create mode 100755 test/test-check-runtime.sh diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a22c9d9..aaf3e0c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,3 +1,4 @@ add_subdirectory(common) add_subdirectory(hooks) add_subdirectory(apprun) +add_subdirectory(check-runtime) \ No newline at end of file diff --git a/src/check-runtime/CMakeLists.txt b/src/check-runtime/CMakeLists.txt new file mode 100644 index 0000000..8642433 --- /dev/null +++ b/src/check-runtime/CMakeLists.txt @@ -0,0 +1,9 @@ +set(CMAKE_CXX_STANDARD 11) +add_definitions(-std=c++11) + +add_executable(check-runtime main.cpp) + +install( + TARGETS check-runtime + RUNTIME DESTINATION bin +) \ No newline at end of file diff --git a/src/check-runtime/main.cpp b/src/check-runtime/main.cpp new file mode 100644 index 0000000..f4259c5 --- /dev/null +++ b/src/check-runtime/main.cpp @@ -0,0 +1,84 @@ +#include +#include +#include +#include +#include +#include +#include + + +int get_next_SO_path(dl_phdr_info* info, size_t, void* p_SO_list) { + auto& SO_list = *static_cast*>(p_SO_list); + auto p_SO_path = realpath(info->dlpi_name, nullptr); + if (p_SO_path) { + SO_list.emplace_back(p_SO_path); + free(p_SO_path); + } + return 0; +} + +std::vector get_SO_realpaths() { + std::vector SO_paths; + dl_iterate_phdr(get_next_SO_path, &SO_paths); + return SO_paths; +} + +std::string get_libc_version_from_SO_path(const std::string& SO_path) { + std::regex rgx(R"(libc-(\d.\d+(.\d+)?))"); + std::smatch match; + + if (std::regex_search(SO_path.begin(), SO_path.end(), match, rgx)) + return match[1]; + + return {}; +} + +std::string get_libc_version_from_file_contents(const std::string& SO_path) { + std::regex rgx(R"(GNU C Library [^\n]* release version (\d.\d+(.\d+)?))"); + std::smatch match; + std::string libc_version = {}; + + std::ifstream so_file(SO_path); + if (so_file.is_open()) { + std::string line; + while (getline(so_file, line) && libc_version.empty()) { + if (std::regex_search(line, match, rgx)) + libc_version = match[1]; + } + + so_file.close(); + } + + + return libc_version; +} + +std::string get_libstdcpp_version_from_SO_path(const std::string& SO_path) { + std::regex rgx(R"(libstdc\+\+\.so\.(\d.\d+(.\d+)?))"); + std::smatch match; + + if (std::regex_search(SO_path.begin(), SO_path.end(), match, rgx)) + return match[1]; + + return {}; +} + +int main(int argc, char* argv[]) { + auto SO_paths = get_SO_realpaths(); + std::string libc_version; + std::string libstdcpp_version; + + for (auto const& SO_path : SO_paths) { + if (libc_version.empty() && SO_path.find("libc-") != std::string::npos) + libc_version = get_libc_version_from_SO_path(SO_path); + if (libc_version.empty() && SO_path.find("libc.so.6") != std::string::npos) + libc_version = get_libc_version_from_file_contents(SO_path); + if (libstdcpp_version.empty() && SO_path.find("libstdc++.so.") != std::string::npos) + libstdcpp_version = get_libstdcpp_version_from_SO_path(SO_path); + } + + std::cout << "LIBC: " << libc_version << std::endl; + std::cout << "LIBSTDC++: " << libstdcpp_version << std::endl; + + return 0; +} \ No newline at end of file diff --git a/test/test-check-runtime.sh b/test/test-check-runtime.sh new file mode 100755 index 0000000..4399a16 --- /dev/null +++ b/test/test-check-runtime.sh @@ -0,0 +1,17 @@ +#!/bin/bash +set -e + +TARGET_SYSTEMS=" + archlinux:latest + centos:6 centos:7 centos:8 + fedora:33 fedora:34 fedora:35 fedora:37 + opensuse/leap:latest opensuse/tumbleweed:latest + debian:stable-slim debian:unstable-slim debian:testing-slim + ubuntu:16.04 ubuntu:18.04 ubuntu:20.04 ubuntu:21.04 ubuntu:22.04 + " + +for TARGET in $TARGET_SYSTEMS; do + echo $TARGET + docker run --rm -v $PWD/src/check-runtime/check-runtime:/bin/check-runtime $TARGET /bin/check-runtime + echo "" +done From 8f44271c9c2506caf0731dfe09d23656f81734d4 Mon Sep 17 00:00:00 2001 From: Alexis Lopez Zubieta Date: Tue, 24 May 2022 19:17:36 -0500 Subject: [PATCH 02/28] Implement check in separated binaries for each lib --- src/check-runtime/CMakeLists.txt | 16 +++-- src/check-runtime/check-libc.c | 7 +++ src/check-runtime/check-libstdc++.cpp | 30 ++++++++++ src/check-runtime/main.cpp | 84 --------------------------- test/test-check-runtime.sh | 17 ------ test/test-runtime-checks-in-docker.sh | 28 +++++++++ 6 files changed, 77 insertions(+), 105 deletions(-) create mode 100644 src/check-runtime/check-libc.c create mode 100644 src/check-runtime/check-libstdc++.cpp delete mode 100644 src/check-runtime/main.cpp delete mode 100755 test/test-check-runtime.sh create mode 100755 test/test-runtime-checks-in-docker.sh diff --git a/src/check-runtime/CMakeLists.txt b/src/check-runtime/CMakeLists.txt index 8642433..8d1f81a 100644 --- a/src/check-runtime/CMakeLists.txt +++ b/src/check-runtime/CMakeLists.txt @@ -1,9 +1,17 @@ -set(CMAKE_CXX_STANDARD 11) -add_definitions(-std=c++11) +add_executable(check-libc check-libc.c) -add_executable(check-runtime main.cpp) +add_executable(check-libstdc++ check-libstdc++.cpp) +target_compile_options(check-libstdc++ PUBLIC $<$:-std=c++11>) + +# check-libstdc++ 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-libstdc++ POST_BUILD + COMMAND patchelf --add-needed libstdc++.so.6 $ + COMMENT "Adding libstdc++.so.6 as dependency" + ) install( - TARGETS check-runtime + TARGETS check-libc check-libstdc++ RUNTIME DESTINATION bin ) \ No newline at end of file diff --git a/src/check-runtime/check-libc.c b/src/check-runtime/check-libc.c new file mode 100644 index 0000000..200c23f --- /dev/null +++ b/src/check-runtime/check-libc.c @@ -0,0 +1,7 @@ +#include +#include + +int main() { + printf("%s\n", gnu_get_libc_version()); + return 0; +} \ No newline at end of file diff --git a/src/check-runtime/check-libstdc++.cpp b/src/check-runtime/check-libstdc++.cpp new file mode 100644 index 0000000..27686e7 --- /dev/null +++ b/src/check-runtime/check-libstdc++.cpp @@ -0,0 +1,30 @@ +#include +#include +#include +#include +#include + +static int extract_libstdcpp_version(struct dl_phdr_info* info, size_t, void* data) { + regmatch_t match[2] = {0x0}; + auto* regex = (regex_t*) data; + char* real_so_path = realpath(info->dlpi_name, nullptr); + + if (real_so_path != NULL && regexec(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); + printf("%s\n", version); + exit(EXIT_SUCCESS); + } + return 0; +} + +int main(int, char* []) { + regex_t regex; + if (regcomp(®ex, 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, ®ex); + exit(EXIT_FAILURE); +} \ No newline at end of file diff --git a/src/check-runtime/main.cpp b/src/check-runtime/main.cpp deleted file mode 100644 index f4259c5..0000000 --- a/src/check-runtime/main.cpp +++ /dev/null @@ -1,84 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - - -int get_next_SO_path(dl_phdr_info* info, size_t, void* p_SO_list) { - auto& SO_list = *static_cast*>(p_SO_list); - auto p_SO_path = realpath(info->dlpi_name, nullptr); - if (p_SO_path) { - SO_list.emplace_back(p_SO_path); - free(p_SO_path); - } - return 0; -} - -std::vector get_SO_realpaths() { - std::vector SO_paths; - dl_iterate_phdr(get_next_SO_path, &SO_paths); - return SO_paths; -} - -std::string get_libc_version_from_SO_path(const std::string& SO_path) { - std::regex rgx(R"(libc-(\d.\d+(.\d+)?))"); - std::smatch match; - - if (std::regex_search(SO_path.begin(), SO_path.end(), match, rgx)) - return match[1]; - - return {}; -} - -std::string get_libc_version_from_file_contents(const std::string& SO_path) { - std::regex rgx(R"(GNU C Library [^\n]* release version (\d.\d+(.\d+)?))"); - std::smatch match; - std::string libc_version = {}; - - std::ifstream so_file(SO_path); - if (so_file.is_open()) { - std::string line; - while (getline(so_file, line) && libc_version.empty()) { - if (std::regex_search(line, match, rgx)) - libc_version = match[1]; - } - - so_file.close(); - } - - - return libc_version; -} - -std::string get_libstdcpp_version_from_SO_path(const std::string& SO_path) { - std::regex rgx(R"(libstdc\+\+\.so\.(\d.\d+(.\d+)?))"); - std::smatch match; - - if (std::regex_search(SO_path.begin(), SO_path.end(), match, rgx)) - return match[1]; - - return {}; -} - -int main(int argc, char* argv[]) { - auto SO_paths = get_SO_realpaths(); - std::string libc_version; - std::string libstdcpp_version; - - for (auto const& SO_path : SO_paths) { - if (libc_version.empty() && SO_path.find("libc-") != std::string::npos) - libc_version = get_libc_version_from_SO_path(SO_path); - if (libc_version.empty() && SO_path.find("libc.so.6") != std::string::npos) - libc_version = get_libc_version_from_file_contents(SO_path); - if (libstdcpp_version.empty() && SO_path.find("libstdc++.so.") != std::string::npos) - libstdcpp_version = get_libstdcpp_version_from_SO_path(SO_path); - } - - std::cout << "LIBC: " << libc_version << std::endl; - std::cout << "LIBSTDC++: " << libstdcpp_version << std::endl; - - return 0; -} \ No newline at end of file diff --git a/test/test-check-runtime.sh b/test/test-check-runtime.sh deleted file mode 100755 index 4399a16..0000000 --- a/test/test-check-runtime.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/bash -set -e - -TARGET_SYSTEMS=" - archlinux:latest - centos:6 centos:7 centos:8 - fedora:33 fedora:34 fedora:35 fedora:37 - opensuse/leap:latest opensuse/tumbleweed:latest - debian:stable-slim debian:unstable-slim debian:testing-slim - ubuntu:16.04 ubuntu:18.04 ubuntu:20.04 ubuntu:21.04 ubuntu:22.04 - " - -for TARGET in $TARGET_SYSTEMS; do - echo $TARGET - docker run --rm -v $PWD/src/check-runtime/check-runtime:/bin/check-runtime $TARGET /bin/check-runtime - echo "" -done diff --git a/test/test-runtime-checks-in-docker.sh b/test/test-runtime-checks-in-docker.sh new file mode 100755 index 0000000..0c0d788 --- /dev/null +++ b/test/test-runtime-checks-in-docker.sh @@ -0,0 +1,28 @@ +#!/bin/bash +set -e + +TARGET_SYSTEMS=" + archlinux:latest + centos:6 centos:7 centos:8 + fedora:33 fedora:34 fedora:35 fedora:37 + opensuse/leap:latest opensuse/tumbleweed:latest + debian:stable-slim debian:unstable-slim debian:testing-slim + ubuntu:16.04 ubuntu:18.04 ubuntu:20.04 ubuntu:21.04 ubuntu:22.04 + " + +if [ ! -f $PWD/src/check-runtime/check-libc ]; then + echo "ERROR: Missing \"src/check-runtime/check-libc\", execute from the build dir." + exit 1 +fi + +if [ ! -f $PWD/src/check-runtime/check-libstdc++ ]; then + echo "ERROR: Missing \"src/check-runtime/check-libstdc++\", execute from the build dir." + exit 1 +fi + +for TARGET in $TARGET_SYSTEMS; do + echo $TARGET + docker run --rm -v $PWD/src/check-runtime/:/check-runtime "$TARGET" /check-runtime/check-libc + docker run --rm -v $PWD/src/check-runtime/:/check-runtime "$TARGET" /check-runtime/check-libstdc++ + echo "" +done From 5c292e03cdb167b455a77df373c8fd9cab2d0a07 Mon Sep 17 00:00:00 2001 From: Alexis Lopez Zubieta Date: Wed, 25 May 2022 14:19:32 -0500 Subject: [PATCH 03/28] Describe modules proposal --- docs/MODULES.md | 52 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 docs/MODULES.md diff --git a/docs/MODULES.md b/docs/MODULES.md new file mode 100644 index 0000000..8359e9b --- /dev/null +++ b/docs/MODULES.md @@ -0,0 +1,52 @@ +Modules +======= + +A module is a part of the bundle that will be included in the runtime only if certain restrictions are meet. The +restrictions are set by the module authors. + +----------- +The Problem +----------- + +Bundled applications require certain libraries from the host system such as libstdc++. In order to ensure a given +application work on a system with an older version the required version must be bundled and used at runtime. In case the +system ships a newer version of the library the system one must be used and the shipped one ignored. + +----------------- +Proposed Solution +----------------- + +AppDirs may contain given set of modules at `$APPDIR/opt/`. Each module will contain the following components: + +- binaries to be optionally used at runtime +- "check" app to tells whether the module should be used or not +- "config" file with the module settings + +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 contain a mandatory configuration group name `module` there will be +defined the additional `library_path`, `path_mappings` and other `environment` variables that could be required. + +Example: + +```shell +version = "1.0"; + +module: +{ + # library paths to be set + library_path = ( "${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"; + }; +}; +``` \ No newline at end of file From 4a5adb1e57e28e3106dd8d27b0a0e60053159b48 Mon Sep 17 00:00:00 2001 From: Alexis Lopez Zubieta Date: Wed, 25 May 2022 17:24:10 -0500 Subject: [PATCH 04/28] Update mock.AppDir to use config files and modules --- test/CMakeLists.txt | 2 +- test/utils/create_appdir.sh | 129 ++++++++++++++++++++++++++---------- 2 files changed, 96 insertions(+), 35 deletions(-) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 0da394c..4eccbf4 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -11,7 +11,7 @@ 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 + DEPENDS AppRun apprun_hooks hooks_inner_target_test check-libc check-libstdc++ ) # remove mock on make clean diff --git a/test/utils/create_appdir.sh b/test/utils/create_appdir.sh index 2d5d412..665581e 100755 --- a/test/utils/create_appdir.sh +++ b/test/utils/create_appdir.sh @@ -15,6 +15,8 @@ fi APPRUN=$(find "$BUILD_DIR/src" -name AppRun | head -n 1) APPRUN_HOOKS=$(find "$BUILD_DIR/src" -name libapprun_hooks.so | head -n 1) +CHECK_LIBC=$(find "$BUILD_DIR/src" -name check-libc | head -n 1) +CHECK_LIBSTDCPP=$(find "$BUILD_DIR/src" -name check-libstdc++ | head -n 1) TARGET_BIN=$(find "$BUILD_DIR/test" -name hooks_inner_target_test | head -n 1) if [ -z "$APPRUN" ]; then @@ -27,6 +29,16 @@ if [ -z "$APPRUN_HOOKS" ]; then exit 1 fi +if [ -z "$CHECK_LIBC" ]; then + echo "Missing check-libc path" + exit 1 +fi + +if [ -z "$CHECK_LIBSTDCPP" ]; then + echo "Missing check-libstdc++ path" + exit 1 +fi + if [ -z "$TARGET_BIN" ]; then echo "Missing TARGET_BIN path" exit 1 @@ -34,7 +46,7 @@ fi mkdir -p "$APPDIR" -function join_by { +function join_by() { local IFS="$1" shift echo "$*" @@ -46,48 +58,91 @@ function remove_duplicated_paths() { join_by ":" "${items[*]}" } -function create_compat_runtime() { - mkdir -p "$APPDIR_COMPAT_RUNTIME" +function patch_appdir_path_in_config() { + sed -i "s|$APPDIR|\$APPDIR|g" $1 +} + +function create_libc_module() { + LIBC_MODULE_PATH="$APPDIR/opt/libc" + mkdir -p "$LIBC_MODULE_PATH" # deploy linker - mkdir -p "$(dirname "${APPDIR_COMPAT_RUNTIME}"/"${APPDIR_LIBC_LINKER_PATH}")" - cp "/$APPDIR_LIBC_LINKER_PATH" "${APPDIR_COMPAT_RUNTIME}"/"${APPDIR_LIBC_LINKER_PATH}" + mkdir -p "$(dirname "${LIBC_MODULE_PATH}"/"${APPDIR_LIBC_LINKER_PATH}")" + cp "/$APPDIR_LIBC_LINKER_PATH" "${LIBC_MODULE_PATH}"/"${APPDIR_LIBC_LINKER_PATH}" # deploy app as interpreter - mkdir -p "$(dirname "$APPDIR_COMPAT_RUNTIME/$REL_BIN_PATH")" - ln -sf "../../../../$REL_BIN_PATH" "$APPDIR_COMPAT_RUNTIME/$REL_BIN_PATH" + mkdir -p "$(dirname "$LIBC_MODULE_PATH/$REL_BIN_PATH")" + ln -sf "../../../../$REL_BIN_PATH" "$LIBC_MODULE_PATH/$REL_BIN_PATH" # deploy dependencies DEPENDENCIES=$(ldd "$TARGET_BIN" | grep "=> " | 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 +164,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 +177,15 @@ 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 < Date: Wed, 25 May 2022 19:18:54 -0500 Subject: [PATCH 05/28] Move compare_version_strings to string_utils.h --- src/apprun/runtime_interpreter.c | 20 +------------------- src/apprun/runtime_interpreter.h | 2 -- src/common/string_utils.c | 19 +++++++++++++++++++ src/common/string_utils.h | 13 +++++++++++++ test/apprun/runtime_interpreter_test.c | 1 + 5 files changed, 34 insertions(+), 21 deletions(-) diff --git a/src/apprun/runtime_interpreter.c b/src/apprun/runtime_interpreter.c index fe06502..1ec8cab 100644 --- a/src/apprun/runtime_interpreter.c +++ b/src/apprun/runtime_interpreter.c @@ -43,6 +43,7 @@ #include "runtime_environment.h" #include "common/path.h" #include "common/appdir_environment.h" +#include "common/string_utils.h" char *parse_ld_trace_line_path(const char *line) { @@ -124,25 +125,6 @@ char *read_ld_version(char *path) { 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"); diff --git a/src/apprun/runtime_interpreter.h b/src/apprun/runtime_interpreter.h index bb214cf..08e55e2 100644 --- a/src/apprun/runtime_interpreter.h +++ b/src/apprun/runtime_interpreter.h @@ -35,8 +35,6 @@ // 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); 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/test/apprun/runtime_interpreter_test.c b/test/apprun/runtime_interpreter_test.c index 1ddcc80..b29a24d 100644 --- a/test/apprun/runtime_interpreter_test.c +++ b/test/apprun/runtime_interpreter_test.c @@ -30,6 +30,7 @@ #include "apprun/runtime_interpreter.h" #include "../common/tests_shared.h" +#include "common/string_utils.h" void test_parse_ld_trace_lib_path() { printf("%s: ", __PRETTY_FUNCTION__); From 97053c28744035b64eaadce84045ffcdb7902c51 Mon Sep 17 00:00:00 2001 From: Alexis Lopez Zubieta Date: Wed, 25 May 2022 19:25:34 -0500 Subject: [PATCH 06/28] Add libconfig as dependency --- CMakeLists.txt | 1 + cmake/get_libconfig.cmake | 27 +++++++++++++++++++++++++++ 2 files changed, 28 insertions(+) create mode 100644 cmake/get_libconfig.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 53221db..1897182 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,6 +7,7 @@ if (${CMAKE_BUILD_TYPE} MATCHES Profile) 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..4d20dbb --- /dev/null +++ b/cmake/get_libconfig.cmake @@ -0,0 +1,27 @@ +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_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_dependencies(libconfig libconfig.git) \ No newline at end of file From 85221cbade0fc2c5bf6535988e5e94f722d86fde Mon Sep 17 00:00:00 2001 From: Alexis Lopez Zubieta Date: Thu, 26 May 2022 19:31:55 -0500 Subject: [PATCH 07/28] Add modules documentation --- docs/MODULES.md | 52 ------------------------------- src/modules/README.md | 72 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+), 52 deletions(-) delete mode 100644 docs/MODULES.md create mode 100644 src/modules/README.md diff --git a/docs/MODULES.md b/docs/MODULES.md deleted file mode 100644 index 8359e9b..0000000 --- a/docs/MODULES.md +++ /dev/null @@ -1,52 +0,0 @@ -Modules -======= - -A module is a part of the bundle that will be included in the runtime only if certain restrictions are meet. The -restrictions are set by the module authors. - ------------ -The Problem ------------ - -Bundled applications require certain libraries from the host system such as libstdc++. In order to ensure a given -application work on a system with an older version the required version must be bundled and used at runtime. In case the -system ships a newer version of the library the system one must be used and the shipped one ignored. - ------------------ -Proposed Solution ------------------ - -AppDirs may contain given set of modules at `$APPDIR/opt/`. Each module will contain the following components: - -- binaries to be optionally used at runtime -- "check" app to tells whether the module should be used or not -- "config" file with the module settings - -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 contain a mandatory configuration group name `module` there will be -defined the additional `library_path`, `path_mappings` and other `environment` variables that could be required. - -Example: - -```shell -version = "1.0"; - -module: -{ - # library paths to be set - library_path = ( "${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"; - }; -}; -``` \ No newline at end of file diff --git a/src/modules/README.md b/src/modules/README.md new file mode 100644 index 0000000..ec49db4 --- /dev/null +++ b/src/modules/README.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 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_path`: 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_path = ( "${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"; + }; +}; +``` From a9a910dd7d66d7063192750ef8627b0a534f5dce Mon Sep 17 00:00:00 2001 From: Alexis Lopez Zubieta Date: Thu, 26 May 2022 19:33:20 -0500 Subject: [PATCH 08/28] Implement check glibc --- src/CMakeLists.txt | 2 +- src/check-runtime/CMakeLists.txt | 17 --------- src/check-runtime/check-libc.c | 7 ---- src/modules/CMakeLists.txt | 18 +++++++++ src/modules/check-glibc.c | 65 ++++++++++++++++++++++++++++++++ test/modules/CMakeLists.txt | 4 ++ test/modules/test-check-glibc.sh | 36 ++++++++++++++++++ 7 files changed, 124 insertions(+), 25 deletions(-) delete mode 100644 src/check-runtime/CMakeLists.txt delete mode 100644 src/check-runtime/check-libc.c create mode 100644 src/modules/CMakeLists.txt create mode 100644 src/modules/check-glibc.c create mode 100644 test/modules/CMakeLists.txt create mode 100755 test/modules/test-check-glibc.sh diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index aaf3e0c..c9f38ff 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,4 +1,4 @@ add_subdirectory(common) add_subdirectory(hooks) add_subdirectory(apprun) -add_subdirectory(check-runtime) \ No newline at end of file +add_subdirectory(modules) \ No newline at end of file diff --git a/src/check-runtime/CMakeLists.txt b/src/check-runtime/CMakeLists.txt deleted file mode 100644 index 8d1f81a..0000000 --- a/src/check-runtime/CMakeLists.txt +++ /dev/null @@ -1,17 +0,0 @@ -add_executable(check-libc check-libc.c) - -add_executable(check-libstdc++ check-libstdc++.cpp) -target_compile_options(check-libstdc++ PUBLIC $<$:-std=c++11>) - -# check-libstdc++ 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-libstdc++ POST_BUILD - COMMAND patchelf --add-needed libstdc++.so.6 $ - COMMENT "Adding libstdc++.so.6 as dependency" - ) - -install( - TARGETS check-libc check-libstdc++ - RUNTIME DESTINATION bin -) \ No newline at end of file diff --git a/src/check-runtime/check-libc.c b/src/check-runtime/check-libc.c deleted file mode 100644 index 200c23f..0000000 --- a/src/check-runtime/check-libc.c +++ /dev/null @@ -1,7 +0,0 @@ -#include -#include - -int main() { - printf("%s\n", gnu_get_libc_version()); - return 0; -} \ No newline at end of file diff --git a/src/modules/CMakeLists.txt b/src/modules/CMakeLists.txt new file mode 100644 index 0000000..7b05246 --- /dev/null +++ b/src/modules/CMakeLists.txt @@ -0,0 +1,18 @@ +add_executable(check-glibc check-glibc.c) +target_link_libraries(check-glibc PRIVATE libconfig common_objects) + +add_executable(check-glibstdc++ check-glibstdc++.cpp) +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..c076577 --- /dev/null +++ b/src/modules/check-glibc.c @@ -0,0 +1,65 @@ +#include +#include +#include +#include +#include + +#include + +#include "common/string_utils.h" + +char* resolve_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; +} + +char* read_required_glibc_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_config_path(); + const char* required_glibc_version = read_required_glibc_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/test/modules/CMakeLists.txt b/test/modules/CMakeLists.txt new file mode 100644 index 0000000..87581ee --- /dev/null +++ b/test/modules/CMakeLists.txt @@ -0,0 +1,4 @@ +add_test( + NAME TEST_CHECK_GLIBC + COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/test-check-glibc.sh $ +) \ 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..0a78ed2 --- /dev/null +++ b/test/modules/test-check-glibc.sh @@ -0,0 +1,36 @@ +#!/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" < Date: Thu, 26 May 2022 20:05:33 -0500 Subject: [PATCH 09/28] Implement check glibstdc++ --- src/check-runtime/check-libstdc++.cpp | 30 --------- src/modules/CMakeLists.txt | 7 ++- src/modules/check-glibstdc++.cpp | 88 +++++++++++++++++++++++++++ src/modules/common.c | 25 ++++++++ src/modules/common.h | 7 +++ test/modules/CMakeLists.txt | 5 ++ test/modules/common.sh | 19 ++++++ test/modules/test-check-glibc.sh | 6 +- test/modules/test-check-glibstdc++.sh | 39 ++++++++++++ 9 files changed, 192 insertions(+), 34 deletions(-) delete mode 100644 src/check-runtime/check-libstdc++.cpp create mode 100644 src/modules/check-glibstdc++.cpp create mode 100644 src/modules/common.c create mode 100644 src/modules/common.h create mode 100644 test/modules/common.sh create mode 100755 test/modules/test-check-glibstdc++.sh diff --git a/src/check-runtime/check-libstdc++.cpp b/src/check-runtime/check-libstdc++.cpp deleted file mode 100644 index 27686e7..0000000 --- a/src/check-runtime/check-libstdc++.cpp +++ /dev/null @@ -1,30 +0,0 @@ -#include -#include -#include -#include -#include - -static int extract_libstdcpp_version(struct dl_phdr_info* info, size_t, void* data) { - regmatch_t match[2] = {0x0}; - auto* regex = (regex_t*) data; - char* real_so_path = realpath(info->dlpi_name, nullptr); - - if (real_so_path != NULL && regexec(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); - printf("%s\n", version); - exit(EXIT_SUCCESS); - } - return 0; -} - -int main(int, char* []) { - regex_t regex; - if (regcomp(®ex, 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, ®ex); - exit(EXIT_FAILURE); -} \ No newline at end of file diff --git a/src/modules/CMakeLists.txt b/src/modules/CMakeLists.txt index 7b05246..e534676 100644 --- a/src/modules/CMakeLists.txt +++ b/src/modules/CMakeLists.txt @@ -1,7 +1,10 @@ -add_executable(check-glibc check-glibc.c) +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) +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 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/modules/CMakeLists.txt b/test/modules/CMakeLists.txt index 87581ee..0babb02 100644 --- a/test/modules/CMakeLists.txt +++ b/test/modules/CMakeLists.txt @@ -1,4 +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 index 0a78ed2..07cf543 100755 --- a/test/modules/test-check-glibc.sh +++ b/test/modules/test-check-glibc.sh @@ -23,14 +23,16 @@ check: EOF } +source $(dirname $0)/common.sh + ############### ## TEST CASES # ############### printf "Check on system glibc higher than required, expected ZERO as return value" setup_test_case "$TEST_DIR/test-check-glibc-with-lower-requirement" "1.0.0" -"$TEST_DIR/test-check-glibc-with-lower-requirement/check" && echo " PASS" +assert_success "$TEST_DIR/test-check-glibc-with-lower-requirement/check" printf "Check on system glibc lower than required, expected NON ZERO as return value" setup_test_case "$TEST_DIR/test-check-glibc-with-lower-requirement" "9999.0.0" -! "$TEST_DIR/test-check-glibc-with-lower-requirement/check" && echo " PASS" +assert_fail "$TEST_DIR/test-check-glibc-with-lower-requirement/check" diff --git a/test/modules/test-check-glibstdc++.sh b/test/modules/test-check-glibstdc++.sh new file mode 100755 index 0000000..3b80681 --- /dev/null +++ b/test/modules/test-check-glibstdc++.sh @@ -0,0 +1,39 @@ +#!/bin/bash + +set -e + +TEST_DIR="$PWD" +CHECK_GLIBSTDCPP_BIN=$1 +if [ -z "$CHECK_GLIBSTDCPP_BIN" ]; then + echo "Missing CHECK_GLIBSTDCPP_BIN" + exit 1 +fi + +function setup_test_case() { + TARGET_DIR=$1 + REQUIRED_GLIBSTDCPP=$2 + mkdir -p "$TARGET_DIR" + cp "$CHECK_GLIBSTDCPP_BIN" "$TARGET_DIR/check" + cat >"$TARGET_DIR/config" < Date: Thu, 26 May 2022 20:05:46 -0500 Subject: [PATCH 10/28] Use module common --- src/modules/check-glibc.c | 27 ++++----------------------- 1 file changed, 4 insertions(+), 23 deletions(-) diff --git a/src/modules/check-glibc.c b/src/modules/check-glibc.c index c076577..6aed75e 100644 --- a/src/modules/check-glibc.c +++ b/src/modules/check-glibc.c @@ -2,32 +2,13 @@ #include #include #include -#include #include #include "common/string_utils.h" +#include "common.h" -char* resolve_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; -} - -char* read_required_glibc_version_from_config(char* config_path) { +char* read_required_glibstdcpp_version_from_config(char* config_path) { config_t cfg; const char* str; @@ -53,8 +34,8 @@ char* read_required_glibc_version_from_config(char* config_path) { } int main() { - char* config_path = resolve_config_path(); - const char* required_glibc_version = read_required_glibc_version_from_config(config_path); + 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 From 4c899fdd7049a95c6b09a97bded976c540344e92 Mon Sep 17 00:00:00 2001 From: Alexis Lopez Zubieta Date: Thu, 26 May 2022 20:06:26 -0500 Subject: [PATCH 11/28] Update mock.AppDir to use the check module apps --- test/CMakeLists.txt | 12 +++++++----- test/utils/create_appdir.sh | 14 +++++++------- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 4eccbf4..a6cab03 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -8,11 +8,13 @@ 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 check-libc check-libstdc++ + 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 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/utils/create_appdir.sh b/test/utils/create_appdir.sh index 665581e..17d4e73 100755 --- a/test/utils/create_appdir.sh +++ b/test/utils/create_appdir.sh @@ -15,8 +15,8 @@ fi APPRUN=$(find "$BUILD_DIR/src" -name AppRun | head -n 1) APPRUN_HOOKS=$(find "$BUILD_DIR/src" -name libapprun_hooks.so | head -n 1) -CHECK_LIBC=$(find "$BUILD_DIR/src" -name check-libc | head -n 1) -CHECK_LIBSTDCPP=$(find "$BUILD_DIR/src" -name check-libstdc++ | head -n 1) +CHECK_LIBC=$(find "$BUILD_DIR/src" -name check-glibc | head -n 1) +CHECK_LIBSTDCPP=$(find "$BUILD_DIR/src" -name check-glibstdc++ | head -n 1) TARGET_BIN=$(find "$BUILD_DIR/test" -name hooks_inner_target_test | head -n 1) if [ -z "$APPRUN" ]; then @@ -30,7 +30,7 @@ if [ -z "$APPRUN_HOOKS" ]; then fi if [ -z "$CHECK_LIBC" ]; then - echo "Missing check-libc path" + echo "Missing check-glibc path" exit 1 fi @@ -95,7 +95,7 @@ function create_libc_module() { version = "1.0"; check: { - required_libc = "${APPDIR_LIBC_VERSION}"; + required_glibc = "$APPDIR_LIBC_VERSION"; }; module: { @@ -131,7 +131,7 @@ function create_libstdcpp_module() { version = "1.0"; check: { - required_libstdcpp = "${APPDIR_LIBSTDCPP_VERSION}"; + required_glibstdcpp = "${APPDIR_LIBSTDCPP_VERSION}"; }; module: { @@ -139,7 +139,7 @@ module: }; EOF - patch_appdir_path_in_config "$APPDIR/AppRun.config" + patch_appdir_path_in_config "$APPDIR/opt/libstdc++/config" } APPDIR_DEFAULT_RUNTIME="$APPDIR" @@ -177,7 +177,7 @@ cp "$APPRUN_HOOKS" "$APPDIR/lib/" # deploy AppRun cp "$APPRUN" "$APPDIR" -cat >$APPDIR/AppRun.config <"$APPDIR/AppRun.config" < Date: Fri, 10 Jun 2022 12:26:16 -0500 Subject: [PATCH 12/28] Drop unused file_utils.c --- src/apprun/runtime_interpreter.c | 2 - src/common/CMakeLists.txt | 3 +- src/common/file_utils.c | 105 ------------------------------- src/common/file_utils.h | 38 ----------- test/common/CMakeLists.txt | 14 ----- test/common/file_utils_test.c | 52 --------------- 6 files changed, 1 insertion(+), 213 deletions(-) delete mode 100644 src/common/file_utils.c delete mode 100644 src/common/file_utils.h delete mode 100644 test/common/file_utils_test.c diff --git a/src/apprun/runtime_interpreter.c b/src/apprun/runtime_interpreter.c index 1ec8cab..a166057 100644 --- a/src/apprun/runtime_interpreter.c +++ b/src/apprun/runtime_interpreter.c @@ -34,8 +34,6 @@ #include #include -#include "common/file_utils.h" -#include "common/string_list.h" #include "common/shell_utils.h" #include "hooks/environment.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/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; -} - - From a2cf2c9174787f15e364c45c57d098b19b980611 Mon Sep 17 00:00:00 2001 From: Alexis Lopez Zubieta Date: Fri, 10 Jun 2022 12:34:07 -0500 Subject: [PATCH 13/28] Drop unused read_ld_version glib version reading was moved into the check-glibc module --- src/apprun/runtime_interpreter.c | 23 ----------------------- src/apprun/runtime_interpreter.h | 2 -- test/apprun/runtime_interpreter_test.c | 11 ----------- 3 files changed, 36 deletions(-) diff --git a/src/apprun/runtime_interpreter.c b/src/apprun/runtime_interpreter.c index a166057..137c5d6 100644 --- a/src/apprun/runtime_interpreter.c +++ b/src/apprun/runtime_interpreter.c @@ -100,29 +100,6 @@ char *try_read_ld_version_string(FILE *fp) { 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); -} - void configure_embed_libc() { #ifdef DEBUG fprintf(stderr, "APPRUN_DEBUG: using appdir libc\n"); diff --git a/src/apprun/runtime_interpreter.h b/src/apprun/runtime_interpreter.h index 08e55e2..93f47e7 100644 --- a/src/apprun/runtime_interpreter.h +++ b/src/apprun/runtime_interpreter.h @@ -37,8 +37,6 @@ 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(); diff --git a/test/apprun/runtime_interpreter_test.c b/test/apprun/runtime_interpreter_test.c index b29a24d..abb040c 100644 --- a/test/apprun/runtime_interpreter_test.c +++ b/test/apprun/runtime_interpreter_test.c @@ -58,16 +58,6 @@ void test_validate_glibc_version_string() { 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__); @@ -92,7 +82,6 @@ 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; } From 2e8f357c43b84b0a341969bacf0aef7587ac25b2 Mon Sep 17 00:00:00 2001 From: Alexis Lopez Zubieta Date: Wed, 15 Jun 2022 16:57:36 -0500 Subject: [PATCH 14/28] Rewrite AppRun to use modules and libconfig --- CMakeLists.txt | 1 + cmake/get_libconfig.cmake | 6 +- src/modules/README.md => docs/MODULES.md | 4 +- src/apprun/CMakeLists.txt | 21 +- src/apprun/apprun.cpp | 266 +++++++++++++++++++++++ src/apprun/apprun.h | 136 ++++++++++++ src/apprun/main.c | 187 ---------------- src/apprun/main.cpp | 64 ++++++ src/apprun/runtime_environment.c | 96 -------- src/apprun/runtime_environment.h | 36 --- src/apprun/runtime_interpreter.c | 182 ---------------- src/apprun/runtime_interpreter.h | 48 ---- src/common/path.h | 10 + src/common/shell_utils.h | 15 +- src/common/string_list.h | 10 + test/CMakeLists.txt | 5 +- test/apprun/CMakeLists.txt | 10 - test/apprun/runtime_interpreter_test.c | 90 -------- test/hooks/CMakeLists.txt | 3 - test/utils/create_appdir.sh | 31 ++- 20 files changed, 543 insertions(+), 678 deletions(-) rename src/modules/README.md => docs/MODULES.md (94%) create mode 100644 src/apprun/apprun.cpp create mode 100644 src/apprun/apprun.h delete mode 100644 src/apprun/main.c create mode 100644 src/apprun/main.cpp delete mode 100644 src/apprun/runtime_environment.c delete mode 100644 src/apprun/runtime_environment.h delete mode 100644 src/apprun/runtime_interpreter.c delete mode 100644 src/apprun/runtime_interpreter.h delete mode 100644 test/apprun/CMakeLists.txt delete mode 100644 test/apprun/runtime_interpreter_test.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 1897182..5db5c44 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,7 @@ project(AppImageExecWrapper) cmake_minimum_required(VERSION 3.2) set(CMAKE_C_STANDARD 99) +set(CMAKE_CXX_STANDARD 17) if (${CMAKE_BUILD_TYPE} MATCHES Profile) message(STATUS "Coverage and profile build flags enabled") diff --git a/cmake/get_libconfig.cmake b/cmake/get_libconfig.cmake index 4d20dbb..24bc0dd 100644 --- a/cmake/get_libconfig.cmake +++ b/cmake/get_libconfig.cmake @@ -24,4 +24,8 @@ include_directories(${install_dir}/include) add_library(libconfig STATIC IMPORTED) set_property(TARGET libconfig PROPERTY IMPORTED_LOCATION ${install_dir}/lib/libconfig.a) -add_dependencies(libconfig libconfig.git) \ No newline at end of file +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/src/modules/README.md b/docs/MODULES.md similarity index 94% rename from src/modules/README.md rename to docs/MODULES.md index ec49db4..04d1c97 100644 --- a/src/modules/README.md +++ b/docs/MODULES.md @@ -38,7 +38,7 @@ 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_path`: optional string list, module library paths + - `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) @@ -58,7 +58,7 @@ check: module: { # library paths to be set - library_path = ( "${APPDIR}/opt/module_id/lib", "${APPDIR}/opt/module_id/usr/lib"); + 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") ); 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..1112a62 --- /dev/null +++ b/src/apprun/apprun.cpp @@ -0,0 +1,266 @@ +/************************************************************************** + * + * 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 "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); + } + + auto exec_argv_len = settings->exec.size(); + char** exec_argv = static_cast(malloc(sizeof(char*) * exec_argv_len + 1)); + for (int i = 0; i < exec_argv_len; i++) + exec_argv[i] = apprun_shell_expand_variables(settings->exec[i].c_str(), argv); + + // add 0 termination + exec_argv[exec_argv_len] = 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 / 2; i++) { + std::string orig = apprun_shell_expand_variables(settings->path_mappings[i * 2].c_str(), nullptr); + std::string dest = apprun_shell_expand_variables(settings->path_mappings[i * 2 + 1].c_str(), nullptr); + result.append(orig) + .append(":") + .append(dest) + .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::filesystem::directory_entry& module_dir) { + std::cout << "Checking module " << module_dir.path() << std::endl; + + // verify if the system has the required feature + auto module_check_path = module_dir.path() / "check"; + if (system(module_check_path.c_str()) != EXIT_SUCCESS) { + std::cout << "Enabling module " << module_dir.path() << std::endl; + // enable module + auto module_config_path = module_dir.path() / "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) { + 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; + } + } + + 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..6b52f2a --- /dev/null +++ b/src/apprun/apprun.h @@ -0,0 +1,136 @@ +/************************************************************************** + * + * 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 +#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::filesystem::directory_entry& 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..be39dd6 --- /dev/null +++ b/src/apprun/main.cpp @@ -0,0 +1,64 @@ +/************************************************************************** + * + * 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 "common/shell_utils.h" + +#include "apprun.h" + +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); + + // load modules if required + if (!settings->modules_dir.empty()) { + std::string module_path = apprun_shell_expand_variables(settings->modules_dir.c_str(), argv); + for (const auto& entry : std::filesystem::directory_iterator(module_path)) + setup_module(settings, entry); + } + + 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(); + apprun_set_private_env(name, value, orig_value, value); + } + + return launch(settings, argv); +} 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 137c5d6..0000000 --- a/src/apprun/runtime_interpreter.c +++ /dev/null @@ -1,182 +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/shell_utils.h" -#include "hooks/environment.h" - -#include "runtime_interpreter.h" -#include "runtime_environment.h" -#include "common/path.h" -#include "common/appdir_environment.h" -#include "common/string_utils.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; -} - -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 93f47e7..0000000 --- a/src/apprun/runtime_interpreter.h +++ /dev/null @@ -1,48 +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 - -bool is_linker_version_string_valid(char *buff); - -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/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/test/CMakeLists.txt b/test/CMakeLists.txt index a6cab03..c820429 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -2,14 +2,15 @@ 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} + 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++ ) 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 abb040c..0000000 --- a/test/apprun/runtime_interpreter_test.c +++ /dev/null @@ -1,90 +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" -#include "common/string_utils.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_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(); - - 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/utils/create_appdir.sh b/test/utils/create_appdir.sh index 17d4e73..cc0e824 100755 --- a/test/utils/create_appdir.sh +++ b/test/utils/create_appdir.sh @@ -7,7 +7,12 @@ if [ -z "$APPDIR" ]; then echo "Missing AppDir path" exit 1 fi -BUILD_DIR="$2" +MAPPED_APPDIR="$2" +if [ -z "$APPDIR" ]; then + echo "Missing MAPPED_APPDIR path" + exit 1 +fi +BUILD_DIR="$3" if [ -z "$BUILD_DIR" ]; then echo "Missing BUILD_DIR path" exit 1 @@ -99,7 +104,11 @@ check: }; module: { - library_path = ( "${APPDIR_LIBC_LIBRARY_PATH}" ); + runtime_dir = "$APPDIR/opt/libc"; + library_paths = [ "${APPDIR_LIBC_LIBRARY_PATH}" ]; + environment = { + CUSTOM_ENV_1 = "glibc"; + }; }; EOF patch_appdir_path_in_config "$APPDIR/opt/libc/config" @@ -135,7 +144,10 @@ check: }; module: { - library_path = ( "${target_dir}" ); + library_path = [ "${target_dir}" ]; + environment = { + CUSTOM_ENV = "glibstc++"; + }; }; EOF @@ -181,10 +193,15 @@ cat >"$APPDIR/AppRun.config" < Date: Wed, 15 Jun 2022 18:17:45 -0500 Subject: [PATCH 15/28] Lower c++ standard required to 14. Older system like ubuntu xenial may not be able to compile this binary. --- CMakeLists.txt | 2 +- src/apprun/apprun.cpp | 16 ++++++++++------ src/apprun/apprun.h | 3 +-- src/apprun/main.cpp | 29 ++++++++++++++++++++++++----- 4 files changed, 36 insertions(+), 14 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5db5c44..80e4bf0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ project(AppImageExecWrapper) cmake_minimum_required(VERSION 3.2) set(CMAKE_C_STANDARD 99) -set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD 14) if (${CMAKE_BUILD_TYPE} MATCHES Profile) message(STATUS "Coverage and profile build flags enabled") diff --git a/src/apprun/apprun.cpp b/src/apprun/apprun.cpp index 1112a62..e418b1a 100644 --- a/src/apprun/apprun.cpp +++ b/src/apprun/apprun.cpp @@ -148,15 +148,19 @@ void import_module_environment(AppRunSettings* const settings, const libconfig:: } } -void setup_module(AppRunSettings* settings, const std::filesystem::directory_entry& module_dir) { - std::cout << "Checking module " << module_dir.path() << std::endl; - +void setup_module(AppRunSettings* settings, const std::string& module_dir) { // verify if the system has the required feature - auto module_check_path = module_dir.path() / "check"; + 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.path() << std::endl; + std::cout << "Enabling module " << module_dir << std::endl; // enable module - auto module_config_path = module_dir.path() / "config"; + auto module_config_path = module_dir + "/config"; libconfig::Config module_config; try { diff --git a/src/apprun/apprun.h b/src/apprun/apprun.h index 6b52f2a..ce46a3f 100644 --- a/src/apprun/apprun.h +++ b/src/apprun/apprun.h @@ -31,7 +31,6 @@ #include #include #include -#include #define APPRUN_CONFIG_FILE_NAME "AppRun.config" @@ -69,7 +68,7 @@ AppRunSettings* load_config_file(const std::string& config_path); * @param settings * @param module_dir */ -void setup_module(AppRunSettings* settings, const std::filesystem::directory_entry& module_dir); +void setup_module(AppRunSettings* settings, const std::string& module_dir); /** diff --git a/src/apprun/main.cpp b/src/apprun/main.cpp index be39dd6..1749fcc 100644 --- a/src/apprun/main.cpp +++ b/src/apprun/main.cpp @@ -25,25 +25,25 @@ **************************************************************************/ -#include #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); - // load modules if required if (!settings->modules_dir.empty()) { - std::string module_path = apprun_shell_expand_variables(settings->modules_dir.c_str(), argv); - for (const auto& entry : std::filesystem::directory_iterator(module_path)) - setup_module(settings, entry); + enable_required_modules(settings); } const std::string& ld_library_path_value = generate_ld_library_path_value(settings); @@ -62,3 +62,22 @@ int main(int argc, char* argv[]) { 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); +} From fb921fb6ef53ceed3b5acb2f669645e3e457d672 Mon Sep 17 00:00:00 2001 From: Alexis Lopez Zubieta Date: Wed, 15 Jun 2022 21:19:25 -0500 Subject: [PATCH 16/28] Install git in docker images to allow cloning libconfig --- tooling/docker/Dockerfile.aarch64 | 2 +- tooling/docker/Dockerfile.gnueabihf | 2 +- tooling/docker/Dockerfile.i386 | 2 +- tooling/docker/Dockerfile.x86_64 | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tooling/docker/Dockerfile.aarch64 b/tooling/docker/Dockerfile.aarch64 index ff2536b..21df8e8 100644 --- a/tooling/docker/Dockerfile.aarch64 +++ b/tooling/docker/Dockerfile.aarch64 @@ -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.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 From 4e317eb8f5378c6a74ff85d99a1d822da2d062d3 Mon Sep 17 00:00:00 2001 From: Alexis Lopez Zubieta Date: Wed, 15 Jun 2022 21:19:47 -0500 Subject: [PATCH 17/28] Forward toolchaing to libconfig cmake configuration --- cmake/get_libconfig.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/get_libconfig.cmake b/cmake/get_libconfig.cmake index 24bc0dd..d902b82 100644 --- a/cmake/get_libconfig.cmake +++ b/cmake/get_libconfig.cmake @@ -1,7 +1,6 @@ cmake_minimum_required(VERSION 3.11) include(ExternalProject) - # Get the libconfig library externalproject_add( libconfig.git @@ -14,6 +13,7 @@ externalproject_add( -DBUILD_EXAMPLES=Off -DBUILD_TESTS=Off -DBUILD_SHARED_LIBS=Off + -DCMAKE_TOOLCHAIN_FILE:FILEPATH=${CMAKE_TOOLCHAIN_FILE} -DCMAKE_INSTALL_PREFIX:PATH= ) From a63506301fe5e8ab888726db52717186b29c4822 Mon Sep 17 00:00:00 2001 From: Alexis Lopez Zubieta Date: Wed, 15 Jun 2022 21:20:06 -0500 Subject: [PATCH 18/28] Add missing flags on i386 toolchain --- cmake/i386-toolchain.cmake | 36 +++++++++++++++++++----------------- 1 file changed, 19 insertions(+), 17 deletions(-) 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 From ef9207fe969fdebd17bc072d241362ed4e17948a Mon Sep 17 00:00:00 2001 From: Alexis Lopez Zubieta Date: Thu, 16 Jun 2022 11:38:20 -0500 Subject: [PATCH 19/28] Add module checks to artifacts --- .github/workflows/ci.yml | 2 ++ 1 file changed, 2 insertions(+) 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 From cb74605b5006fba6074f1a16b77ceb44250b3895 Mon Sep 17 00:00:00 2001 From: Alexis Lopez Zubieta Date: Thu, 16 Jun 2022 11:51:30 -0500 Subject: [PATCH 20/28] Report missing AppRun.config file --- src/apprun/apprun.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/apprun/apprun.cpp b/src/apprun/apprun.cpp index e418b1a..5f191cb 100644 --- a/src/apprun/apprun.cpp +++ b/src/apprun/apprun.cpp @@ -188,6 +188,12 @@ void setup_module(AppRunSettings* settings, const std::string& module_dir) { 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 From aa1da1f897f6e4190b3d46145f703e8e4b969598 Mon Sep 17 00:00:00 2001 From: Alexis Lopez Zubieta Date: Thu, 23 Jun 2022 17:13:06 -0500 Subject: [PATCH 21/28] Use AppDir as default runtime dir --- src/apprun/apprun.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/apprun/apprun.cpp b/src/apprun/apprun.cpp index 5f191cb..f8148d4 100644 --- a/src/apprun/apprun.cpp +++ b/src/apprun/apprun.cpp @@ -247,6 +247,9 @@ AppRunSettings* load_config_file(const std::string& config_path) { } } + // set default runtime dir + settings->runtime_dir = strdup(getenv("APPDIR")); + return settings; } From 34374f0a307a92058de140c5d99b91717c074164 Mon Sep 17 00:00:00 2001 From: Alexis Lopez Zubieta Date: Thu, 23 Jun 2022 17:14:00 -0500 Subject: [PATCH 22/28] Don't add extra quotes after expanding environment variables --- src/common/shell_utils.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/common/shell_utils.c b/src/common/shell_utils.c index 8583414..563a14c 100644 --- a/src/common/shell_utils.c +++ b/src/common/shell_utils.c @@ -93,9 +93,7 @@ char *apprun_argv_to_env(char *const *string_list) { memset(str, 0, str_size + 1); for (int i = 0; i < string_list_len; i++) { - strcat(str, "\""); strcat(str, string_list[i]); - strcat(str, "\""); if (i + 1 < string_list_len) strcat(str, " "); } From 8be8e7d948b13ef095090a58e0825af632e7c280 Mon Sep 17 00:00:00 2001 From: Alexis Lopez Zubieta Date: Mon, 27 Jun 2022 14:08:11 -0500 Subject: [PATCH 23/28] Expand environment variables values on load --- src/apprun/main.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/apprun/main.cpp b/src/apprun/main.cpp index 1749fcc..a64e777 100644 --- a/src/apprun/main.cpp +++ b/src/apprun/main.cpp @@ -57,7 +57,8 @@ int main(int argc, char* argv[]) { 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(); - apprun_set_private_env(name, value, orig_value, value); + 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); From f3f0356cb5b1c36dcd83e91106a65db770fe6b10 Mon Sep 17 00:00:00 2001 From: Alexis Lopez Zubieta Date: Mon, 27 Jun 2022 14:15:49 -0500 Subject: [PATCH 24/28] Add AppRun chdir debug message --- src/apprun/apprun.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/apprun/apprun.cpp b/src/apprun/apprun.cpp index f8148d4..4458043 100644 --- a/src/apprun/apprun.cpp +++ b/src/apprun/apprun.cpp @@ -58,6 +58,9 @@ int launch(AppRunSettings* settings, char** argv) { // 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 } auto exec_argv_len = settings->exec.size(); From 3fd45c3b6d98d99192b7337554e47c2594c92ee4 Mon Sep 17 00:00:00 2001 From: Alexis Lopez Zubieta Date: Tue, 28 Jun 2022 12:09:25 -0500 Subject: [PATCH 25/28] Revert "Don't add extra quotes after expanding environment variables" This reverts commit 34374f0a307a92058de140c5d99b91717c074164. --- src/common/shell_utils.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/common/shell_utils.c b/src/common/shell_utils.c index 563a14c..8583414 100644 --- a/src/common/shell_utils.c +++ b/src/common/shell_utils.c @@ -93,7 +93,9 @@ char *apprun_argv_to_env(char *const *string_list) { memset(str, 0, str_size + 1); for (int i = 0; i < string_list_len; i++) { + strcat(str, "\""); strcat(str, string_list[i]); + strcat(str, "\""); if (i + 1 < string_list_len) strcat(str, " "); } From b33b09db77cc59cff7738fe377e5bfecb386d94a Mon Sep 17 00:00:00 2001 From: Alexis Lopez Zubieta Date: Tue, 28 Jun 2022 12:12:35 -0500 Subject: [PATCH 26/28] Add check glibc and glibstdc++ to the release builds artifacts --- .github/workflows/pre-release.yml | 2 ++ .github/workflows/tagged-release.yml | 2 ++ 2 files changed, 4 insertions(+) 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 From ed71bcc12bfe997962307ff259b199fbcbf55a84 Mon Sep 17 00:00:00 2001 From: Alexis Lopez Zubieta Date: Tue, 28 Jun 2022 16:09:22 -0500 Subject: [PATCH 27/28] Fix execv arguments formatting on launch --- src/apprun/apprun.cpp | 37 ++++++++++++++++++++++++++++++------- 1 file changed, 30 insertions(+), 7 deletions(-) diff --git a/src/apprun/apprun.cpp b/src/apprun/apprun.cpp index 4458043..a1186e4 100644 --- a/src/apprun/apprun.cpp +++ b/src/apprun/apprun.cpp @@ -27,12 +27,12 @@ #include #include #include +#include +#include #include #include - -#include -#include +#include #include "apprun.h" @@ -63,13 +63,36 @@ int launch(AppRunSettings* settings, char** argv) { #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)); - for (int i = 0; i < exec_argv_len; i++) - exec_argv[i] = apprun_shell_expand_variables(settings->exec[i].c_str(), argv); + 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_argv_len] = nullptr; + exec_argv[exec_argc] = nullptr; #ifdef DEBUG fprintf(stderr, "APPRUN_DEBUG: executing "); @@ -99,7 +122,7 @@ std::string generate_path_mappings_env(AppRunSettings* settings) { std::string generate_ld_library_path_value(AppRunSettings* settings) { std::string result; - for (const auto& itr : settings->library_paths) { + for (const auto& itr: settings->library_paths) { std::string value = apprun_shell_expand_variables(itr.c_str(), nullptr); if (!result.empty()) result += ":"; From 6165e8278012ad2fe579219dc6c114226eae87f7 Mon Sep 17 00:00:00 2001 From: Alexis Lopez Zubieta Date: Thu, 30 Jun 2022 12:13:15 -0500 Subject: [PATCH 28/28] fix: path mappings env was not format properly --- docs/MODULES.md | 6 +++--- src/apprun/apprun.cpp | 9 +++------ test/utils/create_appdir.sh | 2 +- 3 files changed, 7 insertions(+), 10 deletions(-) diff --git a/docs/MODULES.md b/docs/MODULES.md index 04d1c97..eaf2bac 100644 --- a/docs/MODULES.md +++ b/docs/MODULES.md @@ -3,7 +3,7 @@ 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 this check fails. +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 @@ -14,7 +14,7 @@ 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 @@ -61,7 +61,7 @@ module: 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") ); + path_mappings = [ "/bin/app:${APPDIR}/opt/module_id/bin/app" ] ; # additional environment variables to be set environment: diff --git a/src/apprun/apprun.cpp b/src/apprun/apprun.cpp index a1186e4..15ee2d0 100644 --- a/src/apprun/apprun.cpp +++ b/src/apprun/apprun.cpp @@ -108,12 +108,9 @@ int launch(AppRunSettings* settings, char** argv) { 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 / 2; i++) { - std::string orig = apprun_shell_expand_variables(settings->path_mappings[i * 2].c_str(), nullptr); - std::string dest = apprun_shell_expand_variables(settings->path_mappings[i * 2 + 1].c_str(), nullptr); - result.append(orig) - .append(":") - .append(dest) + 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(";"); } diff --git a/test/utils/create_appdir.sh b/test/utils/create_appdir.sh index cc0e824..00f898a 100755 --- a/test/utils/create_appdir.sh +++ b/test/utils/create_appdir.sh @@ -196,7 +196,7 @@ runtime: exec = [ "\$APPDIR/usr/bin/script", "\$@" ]; linkers = [ "$APPDIR_LIBC_LINKER_PATH" ]; library_paths = [ "${APPDIR_LIBRARY_PATH}" ]; - path_mappings = [ "$MAPPED_APPDIR", "\$APPDIR" ]; + path_mappings = [ "$MAPPED_APPDIR:\$APPDIR" ]; environment = { CUSTOM_ENV = "custom environment variable value"; LD_PRELOAD = "libapprun_hooks.so:$LD_PRELOAD";