diff --git a/.gitignore b/.gitignore index bb65f480..9137e14c 100644 --- a/.gitignore +++ b/.gitignore @@ -33,6 +33,9 @@ GTAGS /Makefile.in /stamp-h1 +/test-driver +/test-suite.log + # TroglOS build system /.config /.stamp diff --git a/.travis.yml b/.travis.yml index ea1eeae5..81540442 100644 --- a/.travis.yml +++ b/.travis.yml @@ -59,3 +59,7 @@ script: - size /tmp/sbin/initctl - size /tmp/sbin/reboot - LD_LIBRARY_PATH=/tmp/lib /tmp/sbin/initctl -h + - unshare -V + - nsenter -V + - chroot --version + - 'LD_LIBRARY_PATH=/tmp/lib make distcheck || (cat finit-4.0-beta1/_build/sub/test/test-suite.log; exit 1)' diff --git a/AUTHORS b/AUTHORS index c43e34b8..cb0dadba 100644 --- a/AUTHORS +++ b/AUTHORS @@ -5,6 +5,7 @@ Major Contributors: * Tobias Waldekranz * Mattias Walström * Jonas Johansson + * Jacques de Laval Original Author(s): * Claudio Matsuoka diff --git a/Makefile.am b/Makefile.am index 7d1d02e1..a1ccda65 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,5 +1,5 @@ ACLOCAL_AMFLAGS = -I m4 -SUBDIRS = plugins src +SUBDIRS = plugins src test doc_DATA = README.md LICENSE contrib/finit.conf EXTRA_DIST = README.md LICENSE ChangeLog.md contrib/finit.conf diff --git a/configure.ac b/configure.ac index 474c0aea..25ec3cc6 100644 --- a/configure.ac +++ b/configure.ac @@ -12,7 +12,9 @@ AC_CONFIG_FILES([Makefile contrib/void/Makefile contrib/void/finit.d/Makefile contrib/void/finit.d/available/Makefile doc/Makefile plugins/Makefile - src/Makefile]) + src/Makefile + test/test.env + test/Makefile]) # Older versions of autoconf (<2.58) do not have AC_CONFIG_MACRO_DIR() #m4_include([m4/plugin.m4]) @@ -157,12 +159,14 @@ AS_IF([test "x$with_config" != "xno"], [ conf=$sysconfdir/finit.conf])]) AC_EXPAND_DIR(conf_path, "$conf") AC_DEFINE_UNQUOTED(FINIT_CONF, "$conf_path", [Main Finit configuration file, default /etc/finit.conf]) +AC_SUBST(FINIT_CONF, "$conf_path") AS_IF([test "x$with_rcsd" != "xno"], [ AS_IF([test "x$rcsd" = "xyes"], [ rcsd=$sysconfdir/finit.d])]) AC_EXPAND_DIR(rcsd_path, "$rcsd") AC_DEFINE_UNQUOTED(FINIT_RCSD, "$rcsd_path", [Finit rcS.d/, default /etc/finit.d]) +AC_SUBST(FINIT_RCSD, "$rcsd_path") AS_IF([test "x$with_rc_local" != "xno"], [ AS_IF([test "x$rclocal" = "xyes"], [ diff --git a/test/.gitignore b/test/.gitignore new file mode 100644 index 00000000..04ae7baf --- /dev/null +++ b/test/.gitignore @@ -0,0 +1,9 @@ +# GNU configure and build system +*.log +*.trs +/Makefile +/Makefile.in + +/test.env +/checkself.sh +/tenv-root diff --git a/test/Makefile.am b/test/Makefile.am new file mode 100644 index 00000000..d0a0bd0e --- /dev/null +++ b/test/Makefile.am @@ -0,0 +1,33 @@ +EXTRA_DIST = tenv/lib.sh tenv/exec.sh tenv/start.sh +EXTRA_DIST += tenv/root.mk tenv/busybox-x86_64.md5 +EXTRA_DIST += tenv/chrootsetup.sh +EXTRA_DIST += setup-root.sh +EXTRA_DIST += common/service.conf common/service.sh +EXTRA_DIST += checkself.sh +EXTRA_DIST += add-remove-dynamic-service.sh +EXTRA_DIST += add-remove-dynamic-service-sub-config.sh +EXTRA_DIST += start-stop-service.sh +EXTRA_DIST += start-stop-service-sub-config.sh + +AM_TESTS_ENVIRONMENT = TENV_ROOT='$(abs_builddir)/tenv-root/'; +AM_TESTS_ENVIRONMENT += export TENV_ROOT; +AM_TESTS_ENVIRONMENT += export LD_LIBRARY_PATH; +AM_TESTS_ENVIRONMENT += top_builddir=$(top_builddir); +AM_TESTS_ENVIRONMENT += export top_builddir; + +checkself.sh: + echo '#!/bin/sh' > $@ + echo shellcheck $(addprefix '"$$srcdir"'/,$(filter %.sh,$(filter-out $@, $(EXTRA_DIST)))) >> $@ + chmod +x $@ + +TEST_EXTENSIONS = .sh +TESTS = checkself.sh +TESTS += setup-root.sh +TESTS += add-remove-dynamic-service.sh +TESTS += add-remove-dynamic-service-sub-config.sh +TESTS += start-stop-service.sh +TESTS += start-stop-service-sub-config.sh + +clean-local: + -rm -rf $(builddir)/tenv-root/ + -rm -f checkself.sh diff --git a/test/README.md b/test/README.md new file mode 100644 index 00000000..42fdf422 --- /dev/null +++ b/test/README.md @@ -0,0 +1,26 @@ +Tests +===== + +Finit comes with a set of tests and a small framework for running them. +Contributors are encouraged to write new tests when implementing features, or +fixing bugs. + +Each test is run in isolation, in it's own namespace. Finit will therefore +be able to be launched as PID 1. Since it's also running with it's own root +directory it will be able to function properly without having any super user +privileges in the host environment. + +Running tests +------------- + +To run the test suite, first [build](../doc/build.md) Finit - then simply run: + + make check + +`make check` will set up the required assets for the test environment, and then +run the full set of tests. The environment is not removed afterwards so at +this point individual tests can be executed without having to run the entire +test suite, which is handy when developing new tests or debugging existing +test. To execute an individual test, simply invoke the script containing it: + + ./test/name-of-the-test.sh diff --git a/test/add-remove-dynamic-service-sub-config.sh b/test/add-remove-dynamic-service-sub-config.sh new file mode 100755 index 00000000..24df838c --- /dev/null +++ b/test/add-remove-dynamic-service-sub-config.sh @@ -0,0 +1,36 @@ +#!/bin/sh + +set -eu + +TEST_DIR=$(dirname "$0") + +# shellcheck source=/dev/null +. "$TEST_DIR/tenv/lib.sh" + +test_teardown() { + say "Test done $(date)" + say "Running test teardown." + + texec rm -f "$FINIT_RCSD/service.conf" + texec rm -f /test_assets/service.sh +} + +say "Test start $(date)" + +cp "$TEST_DIR"/common/service.sh "$TENV_ROOT"/test_assets/ + +say "Add a dynamic service in $FINIT_RCSD/service.conf" +texec sh -c "echo 'service [2345] kill:20 log /test_assets/service.sh' > $FINIT_RCSD/service.conf" + +say 'Reload Finit' +texec sh -c "initctl reload" + +retry 'assert_num_children 1 service.sh' + +say 'Remove the dynamic service from /etc/finit.d/service.conf' +texec sh -c "echo > $FINIT_RCSD/service.conf" + +say 'Reload Finit' +texec sh -c "initctl reload" + +retry 'assert_num_children 0 service.sh' diff --git a/test/add-remove-dynamic-service.sh b/test/add-remove-dynamic-service.sh new file mode 100755 index 00000000..b3a8bbc5 --- /dev/null +++ b/test/add-remove-dynamic-service.sh @@ -0,0 +1,36 @@ +#!/bin/sh + +set -eu + +TEST_DIR=$(dirname "$0") + +# shellcheck source=/dev/null +. "$TEST_DIR/tenv/lib.sh" + +test_teardown() { + say "Test done $(date)" + say "Running test teardown." + + texec rm -f "$FINIT_CONF" + texec rm -f /test_assets/service.sh +} + +say "Test start $(date)" + +cp "$TEST_DIR"/common/service.sh "$TENV_ROOT"/test_assets/ + +say "Add a dynamic service in $FINIT_CONF" +texec sh -c "echo 'service [2345] kill:20 log /test_assets/service.sh' > $FINIT_CONF" + +say 'Reload Finit' +texec sh -c "initctl reload" + +retry 'assert_num_children 1 service.sh' + +say 'Remove the dynamic service from /etc/finit.conf' +texec sh -c "echo > $FINIT_CONF" + +say 'Reload Finit' +texec sh -c "initctl reload" + +retry 'assert_num_children 0 service.sh' diff --git a/test/common/service.conf b/test/common/service.conf new file mode 100644 index 00000000..3fd97586 --- /dev/null +++ b/test/common/service.conf @@ -0,0 +1 @@ +service [2345] kill:20 log /test_assets/service.sh diff --git a/test/common/service.sh b/test/common/service.sh new file mode 100755 index 00000000..6b267e7f --- /dev/null +++ b/test/common/service.sh @@ -0,0 +1,7 @@ +#!/bin/sh + +set -eu + +while true; do + sleep 5 +done diff --git a/test/setup-root.sh b/test/setup-root.sh new file mode 100755 index 00000000..b4ca97da --- /dev/null +++ b/test/setup-root.sh @@ -0,0 +1,8 @@ +#!/bin/sh + +set -eu + +# shellcheck disable=SC2154 +make -C "$top_builddir" DESTDIR="$TENV_ROOT" install +# shellcheck disable=SC2154 +FINITBIN="$(pwd)/$top_builddir/src/finit" DEST="$TENV_ROOT" make -f "$srcdir/tenv/root.mk" diff --git a/test/start-stop-service-sub-config.sh b/test/start-stop-service-sub-config.sh new file mode 100755 index 00000000..c91ce955 --- /dev/null +++ b/test/start-stop-service-sub-config.sh @@ -0,0 +1,38 @@ +#!/bin/sh + +set -eu + +TEST_DIR=$(dirname "$0") + +# shellcheck source=/dev/null +. "$TEST_DIR/tenv/lib.sh" + +test_teardown() { + say "Test done $(date)" + say "Running test teardown." + + texec rm -f "$FINIT_RCSD/service.conf" + texec rm -f /test_assets/service.sh +} + +say "Test start $(date)" + +cp "$TEST_DIR"/common/service.sh "$TENV_ROOT"/test_assets/ + +say "Add service stanza in $FINIT_RCSD/service.conf" +texec sh -c "echo 'service [2345] kill:20 log /test_assets/service.sh' > $FINIT_RCSD/service.conf" + +say 'Reload Finit' +texec sh -c "initctl reload" + +retry 'assert_num_children 1 service.sh' + +say 'Stop the service' +texec sh -c "initctl stop service.sh" + +retry 'assert_num_children 0 service.sh' + +say 'Start the service again' +texec sh -c "initctl start service.sh" + +retry 'assert_num_children 1 service.sh' diff --git a/test/start-stop-service.sh b/test/start-stop-service.sh new file mode 100755 index 00000000..64177133 --- /dev/null +++ b/test/start-stop-service.sh @@ -0,0 +1,38 @@ +#!/bin/sh + +set -eu + +TEST_DIR=$(dirname "$0") + +# shellcheck source=/dev/null +. "$TEST_DIR/tenv/lib.sh" + +test_teardown() { + say "Test done $(date)" + say "Running test teardown." + + texec rm -f "$FINIT_CONF" + texec rm -f /test_assets/service.sh +} + +say "Test start $(date)" + +cp "$TEST_DIR"/common/service.sh "$TENV_ROOT"/test_assets/ + +say "Add service stanza in $FINIT_CONF" +texec sh -c "echo 'service [2345] kill:20 log /test_assets/service.sh' > $FINIT_CONF" + +say 'Reload Finit' +texec sh -c "initctl reload" + +retry 'assert_num_children 1 service.sh' + +say 'Stop the service' +texec sh -c "initctl stop service.sh" + +retry 'assert_num_children 0 service.sh' + +say 'Start the service again' +texec sh -c "initctl start service.sh" + +retry 'assert_num_children 1 service.sh' diff --git a/test/tenv/busybox-x86_64.md5 b/test/tenv/busybox-x86_64.md5 new file mode 100644 index 00000000..0ecfec84 --- /dev/null +++ b/test/tenv/busybox-x86_64.md5 @@ -0,0 +1 @@ +ea7e2353af0067a926d3e021e504290e busybox-x86_64 diff --git a/test/tenv/chrootsetup.sh b/test/tenv/chrootsetup.sh new file mode 100755 index 00000000..f465e897 --- /dev/null +++ b/test/tenv/chrootsetup.sh @@ -0,0 +1,49 @@ +#!/bin/sh +# Setup the things (mount /proc, /tmp, etc.) that needs to be set up from +# within the namespace/chroot and then start PID 1. +# +# Copyright (c) 2021 Jacques de Laval +# +# 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. + +set -eu + +# It would have been nice to mount /dev as devtmpfs, but unfortunately +# that's not possible if you're not privileged. For now tmpfs will +# have to do. +mount -n -t tmpfs none /dev + +mount -t proc none /proc +mount -t sysfs none /sys + +if [ "$(ls -A /tmp)" ]; then + mkdir -p /tmp.shadow + mount --bind /tmp /tmp.shadow + mount -t tmpfs none /tmp + cp -a /tmp.shadow/* /tmp/ +else + mount -t tmpfs none /tmp +fi + +mkdir -p "$FINIT_RCSD" + +tty=/dev/$(cat /sys/class/tty/console/active) +mkfifo "$tty" + +exec "$@" diff --git a/test/tenv/enter.sh b/test/tenv/enter.sh new file mode 100755 index 00000000..f1188a3c --- /dev/null +++ b/test/tenv/enter.sh @@ -0,0 +1,27 @@ +#!/bin/sh + +set -eu + +cmd="" +if [ "$#" -lt 1 ]; then + cmd="/bin/sh" +else + cmd=$1 + shift +fi + +TEST_DIR=$(dirname "$0")/.. +TENV_ROOT="${TENV_ROOT:-$(pwd)/${TEST_DIR}/tenv-root}" +PID_FILE="$TENV_ROOT/running_test.pid" + +if [ -f "$PID_FILE" ] ; then + target=$(cat "$PID_FILE") + target=$(pgrep -P "$target") +else + echo "No test running!" + exit 1 +fi + +source "$TEST_DIR/test.env" + +"$(dirname "$0")"/exec.sh "$target" "$cmd" "$@" diff --git a/test/tenv/exec.sh b/test/tenv/exec.sh new file mode 100755 index 00000000..17eadadb --- /dev/null +++ b/test/tenv/exec.sh @@ -0,0 +1,57 @@ +#!/bin/sh +# Execute a given command from withing the test environment. +# +# Copyright (c) 2021 Jacques de Laval +# +# 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. + +set -eu + +if [ "$#" -lt 2 ]; then + echo "Usage:" + echo " $0 [target-pid] [command [arguments]]" + exit 1 +fi +target="$1" +shift + +TEST_DIR=$(dirname "$0"../) +TENV_ROOT="${TENV_ROOT:-$(pwd)/${TEST_DIR}/tenv-root}" + +nsenter=$(command -v nsenter) +chroot=$(command -v chroot) + +export PS1='\w \$ ' +export PS2='> ' +export PS3='#? ' +export PS4='+ ' + +PATH="$TESTENV_PATH" +export PATH + +"$nsenter" \ + --preserve-credentials \ + --user \ + --mount \ + --uts \ + --ipc \ + --net \ + --pid \ + -w -t "$target" \ + "$chroot" "$TENV_ROOT" "$@" diff --git a/test/tenv/lib.sh b/test/tenv/lib.sh new file mode 100755 index 00000000..444cac5a --- /dev/null +++ b/test/tenv/lib.sh @@ -0,0 +1,144 @@ +#!/bin/sh +# Small framework for executing tests in a test environment. +# +# Copyright (c) 2021 Jacques de Laval +# +# 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. + +assert_num_children() { + assert "$1 services are running" "$(texec pgrep -P 1 "$2" | wc -l)" -eq "$1" +} + +texec() { + # shellcheck disable=SC2154 + "$TEST_DIR/tenv/exec.sh" "$finit_pid" "$@" +} + +pause() { + echo "Press any key to continue... " + read -r REPLY +} + +toggle_finit_debug() { + say 'Toggle finit debug' + texec initctl debug + sleep 0.5 +} + +color_reset='\e[0m' +fg_red='\e[1;31m' +fg_green='\e[1;32m' +fg_yellow='\e[1;33m' +log() { + printf "< %s > %b%b%b %s\n" "$0" "$1" "$2" "$color_reset" "$3" +} + +assert() { + __assert_msg=$1 + shift + if [ ! "$@" ]; then + log "$fg_red" ✘ "$__assert_msg ($*)" + return 1 + fi + log "$fg_green" ✔ "$__assert_msg" +} + +retry() { + __retry_cmd=$1 + shift + case "$#" in + 2) + __retry_n=$1 + __retry_sleep=$2 + ;; + 1) + __retry_n=$1 + __retry_sleep=0.1 + ;; + *) + __retry_n=50 + __retry_sleep=0.1 + ;; + esac + + for _ in $(seq 1 "$__retry_n"); do + sleep "$__retry_sleep" + __retry_cmd_out=$(eval "$__retry_cmd") && \ + echo "$__retry_cmd_out" && \ + return 0 + done + __retry_cmd_exit="$?" + echo "$__retry_cmd_out" + return "$__retry_cmd_exit" +} + +say() { + log "$fg_yellow" • "$@" +} + +teardown() { + test_status="$?" + + if type test_teardown > /dev/null 2>&1 ; then + test_teardown + fi + + log "$color_reset" '--' '' + if [ "$test_status" -eq 0 ]; then + log "$fg_green" 'TEST PASS' '' + else + log "$fg_red" 'TEST FAIL' '' + fi + if [ -n "${finit_pid+x}" ]; then + texec kill -SIGUSR2 1 + fi + + wait + + if [ -d "$TENV_ROOT/var/lock" ]; then + chmod +r "$TENV_ROOT/var/lock" + fi + rm -f "$TENV_ROOT"/running_test.pid +} + +trap teardown EXIT + +TENV_ROOT="${TENV_ROOT:-$(pwd)/${TEST_DIR}/tenv-root}" +export TENV_ROOT + +# shellcheck source=/dev/null +. "$TENV_ROOT/../test.env" + +# Setup test environment +if [ -n "${DEBUG:-}" ]; then + FINIT_ARGS="${FINIT_ARGS:-} finit.debug=on" +fi +# shellcheck disable=2086 +"$TEST_DIR/tenv/start.sh" finit ${FINIT_ARGS:-} & +finit_ppid=$! +echo "$finit_ppid" > "$TENV_ROOT"/running_test.pid + +>&2 echo "Hint: Execute 'TENV_ROOT=$TENV_ROOT $TEST_DIR/tenv/enter.sh' to enter the test namespace" +log "$color_reset" 'Setup of test environment done' '' + +finit_pid=$(retry "pgrep -P $finit_ppid") + +tty=/dev/$(texec cat /sys/class/tty/console/active) +texec cat "$tty" & +sleep 1 diff --git a/test/tenv/root.mk b/test/tenv/root.mk new file mode 100644 index 00000000..7469dcf9 --- /dev/null +++ b/test/tenv/root.mk @@ -0,0 +1,91 @@ +# Root file system for test environment. +# +# Copyright (c) 2021 Jacques de Laval +# +# 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. + +DEST ?= ../tenv-root +srcdir ?= ../ + +ARCH ?= x86_64 + +FINITBIN ?= ./sbin/finit + +BBVER ?= 1.31.0 +BBBIN = busybox-$(ARCH) +BBURL ?= https://www.busybox.net/downloads/binaries/$(BBVER)-defconfig-multiarch-musl/$(BBBIN) + +binaries = $(DEST)/bin/cat \ + $(DEST)/bin/cp \ + $(DEST)/bin/date \ + $(DEST)/bin/echo \ + $(DEST)/bin/env \ + $(DEST)/bin/find \ + $(DEST)/bin/kill \ + $(DEST)/bin/ls \ + $(DEST)/bin/mkdir \ + $(DEST)/bin/mkfifo \ + $(DEST)/bin/mknod \ + $(DEST)/bin/mount \ + $(DEST)/bin/printf \ + $(DEST)/bin/pgrep \ + $(DEST)/bin/ps \ + $(DEST)/bin/rm \ + $(DEST)/bin/sh \ + $(DEST)/bin/sleep \ + $(DEST)/bin/top \ + $(DEST)/bin/touch + +dirs = $(DEST)/bin \ + $(DEST)/dev \ + $(DEST)/etc \ + $(DEST)/proc \ + $(DEST)/sbin \ + $(DEST)/sys \ + $(DEST)/test_assets \ + $(DEST)/tmp + +_libs_src = $(shell ldd $(FINITBIN) | grep -Eo '/[^ ]+') +libs = $(foreach path,$(_libs_src),$(abspath $(DEST))$(path)) + +all: $(dirs) $(binaries) $(libs) $(DEST)/bin/chrootsetup.sh + +$(dirs): + mkdir -p $@ + +$(DEST)/bin/$(BBBIN).md5: + cp $(srcdir)/tenv/$(notdir $@) $@ + +$(DEST)/bin/$(BBBIN): $(DEST)/bin/$(BBBIN).md5 + wget -O $@ $(BBURL) + chmod +x $@ + cd $(dir $@); \ + md5sum -c $(BBBIN).md5 + +$(DEST)/bin/chrootsetup.sh: + cp $(srcdir)/tenv/$(notdir $@) $@ + +$(binaries): $(DEST)/bin/$(BBBIN) + cd $(DEST)/bin; \ + rm -f $(notdir $@); \ + ln -s $(BBBIN) $(notdir $@) + +$(libs): + mkdir -p $(dir $@) + cp $(patsubst $(abspath $(DEST))%,%,$@) $@ diff --git a/test/tenv/start.sh b/test/tenv/start.sh new file mode 100755 index 00000000..63558444 --- /dev/null +++ b/test/tenv/start.sh @@ -0,0 +1,48 @@ +#!/bin/sh +# Start a new test environment. +# +# Copyright (c) 2021 Jacques de Laval +# +# 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. + +set -eu + +TEST_DIR=$(dirname "$0"../) +TENV_ROOT="${TENV_ROOT:-$(pwd)/${TEST_DIR}/tenv-root}" + +unshare=$(command -v unshare) +chroot=$(command -v chroot) + +export PS1='\w \$ ' +export PS2='> ' +export PS3='#? ' +export PS4='+ ' + +PATH="$TESTENV_PATH" +export PATH + +# Not supported by Busybox unshare: +# --cgroup --time +exec "$unshare" \ + --user --map-root-user \ + --fork --pid --mount-proc \ + --mount \ + --mount-proc \ + --uts --ipc --net \ + "$chroot" "$TENV_ROOT" /bin/chrootsetup.sh "$@" diff --git a/test/test.env.in b/test/test.env.in new file mode 100644 index 00000000..356e06df --- /dev/null +++ b/test/test.env.in @@ -0,0 +1,6 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ + +export TESTENV_PATH=/bin:/sbin:"@bindir@":"@sbindir@" +export FINIT_CONF="@FINIT_CONF@" +export FINIT_RCSD="@FINIT_RCSD@"