diff --git a/os/run/chroot_loader.run b/os/run/chroot_loader.run new file mode 100644 index 0000000000..e7dfc0575a --- /dev/null +++ b/os/run/chroot_loader.run @@ -0,0 +1,139 @@ +# +# \brief Test for using chroot on Linux +# \author Norman Feske +# \date 2012-06-06 +# +# +if {![have_spec linux]} { puts "Run script requires Linux"; exit 0 } + +# +# Build +# + +build { core init app/chroot drivers/timer/linux test/timer + server/loader test/chroot_loader } + +if {[catch { exec which setcap }]} { + puts stderr "Error: setcap not available, please install the libcap2-bin package" + return 0 +} + + +create_boot_directory + +# +# Generate config +# + +set config { + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +} + +proc chroot_path { id } { return "/tmp/chroot-test-$id" } + +proc chroot_cwd_path { id } { return "[chroot_path $id][pwd]/[run_dir]" } + +proc chroot_genode_tmp_path { id } { return "[chroot_path $id]/tmp/genode-[exec id -u]" } + +proc cleanup_chroot { } { + foreach id { 1 2 } { + catch { exec sudo umount -l [chroot_cwd_path $id] } + catch { exec sudo umount -l [chroot_genode_tmp_path $id] } + catch { exec sudo umount -l [chroot_path $id]/lib } + catch { exec rm -rf [chroot_path $id] } + } +} + +# replace 'chroot_path' markers in config with actual paths +foreach id { 1 2 } { + regsub "chroot_path_$id" $config [chroot_path $id] config } + +install_config $config + +# +# Copy boot modules into run directory +# +# We cannot use the predefined 'build_boot_image' function here because +# this would create mere symlinks. However, we want to hardlink the +# run directory into the chroot environment. If the directory entries +# were symlinks, those would point to nowhere within the chroot. +# +foreach binary { core init chroot timer loader test-chroot_loader test-timer} { + exec cp -H bin/$binary [run_dir] } + +# +# Grant chroot permission to 'chroot' tool +# +# CAP_SYS_ADMIN is needed for bind mounting genode runtime directories +# CAP_SYS_CHROOT is needed to perform the chroot syscall +# +exec sudo setcap cap_sys_admin,cap_sys_chroot=ep [run_dir]/chroot + +# +# Setup chroot environment +# + +# start with fresh directory +cleanup_chroot +foreach id { 1 2 } { + exec mkdir -p [chroot_path $id] + exec mkdir -p [chroot_path $id]/lib + + # bind mount '/lib' as need libc within the chroot environment + exec sudo mount --bind /lib [chroot_path $id]/lib +} + +# +# Execute test case +# +run_genode_until {.*--- chroot-loader test finished ---\s*\n} 60 + +# +# Validate log output +# + +if {[regexp -all -- {--- timer test ---} $output] != 6} { + puts stderr "Number of spawned subsystems differs from 6" + exit 2 +} + +if {![regexp -- {chroot-1 -> test-timer] wait 2/10} $output]} { + puts stderr "Long-running timer test has made too little progress" + exit 3 +} + +# +# Remove artifacts created while running the test +# +cleanup_chroot + +# vi: set ft=tcl : diff --git a/os/src/test/chroot_loader/main.cc b/os/src/test/chroot_loader/main.cc new file mode 100644 index 0000000000..8b2fce76c5 --- /dev/null +++ b/os/src/test/chroot_loader/main.cc @@ -0,0 +1,174 @@ +/* + * \brief Test for dynamically starting chrooted subsystems via the loader + * \author Norman Feske + * \date 2012-06-06 + * + * This test creates two subsystems, each residing in a dedicated chroot + * environment, by combining the loader service with the chroot mechanism. + * One subsystem runs infinitely. The other subsystem will be repeatedly + * started and killed. + */ + +/* Genode includes */ +#include +#include +#include +#include + + +/******************************************************* + ** Helpers for obtaining test parameters from config ** + *******************************************************/ + +static char const *chroot_path_from_config(char const *node_name, + char *dst, Genode::size_t dst_len) +{ + Genode::config()->xml_node().sub_node(node_name) + .attribute("chroot_path").value(dst, dst_len); + return dst; +} + + +static char const *chroot_path_of_static_test() +{ + static char buf[1024]; + return chroot_path_from_config("static_test", buf, sizeof(buf)); +} + + +static char const *chroot_path_of_dynamic_test() +{ + static char buf[1024]; + return chroot_path_from_config("dynamic_test", buf, sizeof(buf)); +} + + +/********** + ** Test ** + **********/ + +/** + * Return format string used as template for the subsystem configuration. + * + * Note the format-string argument used for inserting the chroot path. + */ +static char const *config_template() +{ + return "\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "\n"; +} + + +/** + * Chroot subsystem corresponding to a loader session + */ +class Chroot_subsystem +{ + private: + + Loader::Connection _loader; + + char _label[32]; + + /** + * Import data as ROM module into the subsystem-specific ROM service + */ + void _import_rom_module(char const *name, void *ptr, Genode::size_t size) + { + using namespace Genode; + + Dataspace_capability ds = _loader.alloc_rom_module(name, size); + + /* fill ROM module with data */ + char *local_addr = env()->rm_session()->attach(ds); + memcpy(local_addr, ptr, size); + env()->rm_session()->detach(local_addr); + + _loader.commit_rom_module(name); + } + + public: + + Chroot_subsystem(char const *chroot_path, Genode::size_t ram_quota) + : + _loader(ram_quota) + { + using namespace Genode; + + /* + * Generate Genode configuration of the new subsystem and import + * it into the subsystem's loader session as a ROM module named + * "config". + */ + char buf[strlen(chroot_path) + strlen(config_template()) + 1]; + snprintf(buf, sizeof(buf), config_template(), chroot_path); + + _import_rom_module("config", buf, strlen(buf) + 1); + + /* + * Name of the Genode binary is start as the root of the new + * subsystem. + */ + char const *chroot_binary_name = "chroot"; + + /* + * Generate unique label name using a counter + * + * The label appears in the LOG output of the loaded subsystem. + * Technically, it does need to be unique. It is solely used + * for validating the test in the run script. + */ + static int cnt = 0; + snprintf(_label, sizeof(_label), "%s-%d", chroot_binary_name, ++cnt); + + /* start execution of new subsystem */ + _loader.start(chroot_binary_name, Loader::Session::Name(_label)); + } +}; + + +int main(int, char **) +{ + Genode::printf("--- chroot-loader test started ---\n"); + + static Chroot_subsystem static_subsystem(chroot_path_of_static_test(), + 2*1024*1024); + + static Timer::Connection timer; + + for (unsigned i = 0; i < 5; i++) { + + PLOG("dynamic test iteration %d", i); + + Chroot_subsystem subsystem(chroot_path_of_dynamic_test(), + 2*1024*1024); + + /* grant the subsystem one second of life */ + timer.msleep(1000); + + /* + * The local 'dynamic_subsystem' instance will be destructed at the of + * the loop body. + */ + } + + Genode::printf("--- chroot-loader test finished ---\n"); + return 0; +} diff --git a/os/src/test/chroot_loader/target.mk b/os/src/test/chroot_loader/target.mk new file mode 100644 index 0000000000..b59705e964 --- /dev/null +++ b/os/src/test/chroot_loader/target.mk @@ -0,0 +1,3 @@ +TARGET = test-chroot_loader +SRC_CC = main.cc +LIBS += cxx env