diff --git a/.travis.yml b/.travis.yml index a1b6078..4e4536a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,9 +17,14 @@ install: script: - bash --version - - bash ./tests/unit-tests/unit-tests.sh - bash ./tests/checkstyle/checkstyle.sh - - junest --check ./bin/junest + - bash ./tests/unit-tests/unit-tests.sh + + # Multiple tests against different execution modes: + - junest -f -- ${PWD}/lib/checks/check.sh + - junest -u -- ${PWD}/lib/checks/check.sh --skip-aur-tests + - sudo -E ${PWD}/bin/junest -g -- ${PWD}/lib/checks/check.sh --run-root-tests - yes | junest --delete - - JUNEST_HOME=~/.junest-arm junest --check ./bin/junest --skip-root-tests + + - JUNEST_HOME=~/.junest-arm junest -f -- ./lib/checks/check.sh - yes | JUNEST_HOME=~/.junest-arm junest --delete diff --git a/README.md b/README.md index 98f0c9e..96c8b0b 100644 --- a/README.md +++ b/README.md @@ -11,13 +11,13 @@ The Arch Linux based distro that runs upon any Linux distros without root access |Project Status|Donation|Communication| |:------------:|:------:|:-----------:| -| [![Build status](https://api.travis-ci.org/fsquillace/junest.png?branch=master)](https://travis-ci.org/fsquillace/junest) [![OpenHub](https://www.openhub.net/p/junest/widgets/project_thin_badge.gif)](https://www.openhub.net/p/junest) | [![PayPal](https://img.shields.io/badge/PayPal-Donate%20a%20beer-blue.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=8LEHQKBCYTACY) [![Gratipay](https://img.shields.io/badge/Gratipay-Donate%20to%20JuNest-green.svg)](https://gratipay.com/junest/) | [![Join the gitter chat at https://gitter.im/fsquillace/junest](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/fsquillace/junest?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Join the IRC chat at https://webchat.freenode.net/?channels=junest](https://img.shields.io/badge/IRC-JuNest-yellow.svg)](https://webchat.freenode.net/?channels=junest) [![Join the group at https://groups.google.com/d/forum/junest](https://img.shields.io/badge/Google Groups-JuNest-red.svg)](https://groups.google.com/d/forum/junest) [![RSS](https://img.shields.io/badge/RSS-News-orange.svg)](http://fsquillace.github.io/junest-site/feed.xml) | +| [![Build status](https://api.travis-ci.org/fsquillace/junest.png?branch=master)](https://travis-ci.org/fsquillace/junest) [![OpenHub](https://www.openhub.net/p/junest/widgets/project_thin_badge.gif)](https://www.openhub.net/p/junest) | [![PayPal](https://img.shields.io/badge/PayPal-Donate%20a%20beer-blue.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=8LEHQKBCYTACY) [![Gratipay](https://img.shields.io/badge/Gratipay-Donate%20to%20JuNest-green.svg)](https://gratipay.com/junest/) | [![Join the gitter chat at https://gitter.im/fsquillace/junest](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/fsquillace/junest?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Join the IRC chat at https://webchat.freenode.net/?channels=junest](https://img.shields.io/badge/IRC-JuNest-yellow.svg)](https://webchat.freenode.net/?channels=junest) [![Join the group at https://groups.google.com/d/forum/junest](https://img.shields.io/badge/GoogleGroups-JuNest-red.svg)](https://groups.google.com/d/forum/junest) [![RSS](https://img.shields.io/badge/RSS-News-orange.svg)](http://fsquillace.github.io/junest-site/feed.xml) | **Table of Contents** - [Description](#description) - [Quickstart](#quickstart) - [Installation](#installation) -- [Dependencies](#dependencies) +- [Usage](#usage) - [Advanced usage](#advanced-usage) - [Internals](#internals) - [Troubleshooting](#troubleshooting) @@ -48,19 +48,22 @@ JuNest follows the [Arch Linux philosophy](https://wiki.archlinux.org/index.php/ Quickstart ========== -There are three different ways you can run JuNest: +The basic way to run JuNest is via the [Proot](https://wiki.archlinux.org/index.php/Proot) as the backend program: - As normal user - Allow to make basic operations: ```junest``` - As fakeroot - Allow to install/remove packages: ```junest -f``` -- As root - Allow to have fully root privileges inside JuNest environment (you need to be root for executing this): ```junest -r``` +To know more about the JuNest execution modes depending on the backend program +used, see the [Usage](#usage) section below. +After running JuNest +-------------------- If the JuNest image has not been downloaded yet, the script will download -the image and will place it to the default directory ~/.junest. -You can change the default directory by changing the environment variable *JUNEST\_HOME*. +the image from the repository and will place it to the default directory `~/.junest`. +You can change the default directory by changing the environment variable `JUNEST_HOME`. -If you are new on Archlinux and you are not familiar with *pacman* package manager +If you are new on Arch Linux and you are not familiar with `pacman` package manager visit the [pacman rosetta page](https://wiki.archlinux.org/index.php/Pacman_Rosetta). Installation @@ -90,7 +93,7 @@ Just clone the JuNest repo somewhere (for example in ~/.local/share/junest): ### Installation using AUR (Arch Linux only) ### If you are using an Arch Linux system you can, alternatively, install JuNest from the [AUR repository](https://aur.archlinux.org/): - yogurt -S junest-git + yaourt -S junest-git export PATH=/opt/junest/bin:$PATH ## Method two ## @@ -98,12 +101,72 @@ Alternatively, another installation method would be to directly download the JuN ARCH= mkdir ~/.junest - curl https://dl.dropboxusercontent.com/u/42449030/junest/junest-${ARCH}.tar.gz | tar -xz -C ~/.junest + curl https://s3-eu-west-1.amazonaws.com/junest-repo/junest-${ARCH}.tar.gz | tar -xz -C ~/.junest export PATH=~/.junest/opt/junest/bin:$PATH +Usage +===== +There are three different ways you can run JuNest depending on the backend program you decide to use. + +PRoot based +----------- +[Proot](https://wiki.archlinux.org/index.php/Proot) represents the default +program used for accessing to the JuNest environments. +The main reason to choose Proot as default backend program is because +it represents a portable solution that works well in most of GNU/Linux distros available. +One of the major drawbacks is the fact that Proot is not officially +supported anymore, therefore, Proot bugs may no longer be fixed. + +In order to run JuNest via Proot: + +- As normal user - Allow to make basic operations: ```junest``` + +- As fakeroot - Allow to install/remove packages: ```junest -f``` + +Linux namespaces based +---------------------- +The [Linux namespaces](http://man7.org/linux/man-pages/man7/namespaces.7.html) +represents the next generation backend program for JuNest. +The major drawback about the namespace is portability, as certain requirements +need to be satisfied: 1) Only starting from Linux 3.8, unprivileged processes can +create the required user and mount namespaces. +2) Moreover, the Linux kernel distro must have the user namespace enabled. +Hopefully, in the future the major GNU/Linux distros will start enabling such feature by default. +For instance, Ubuntu (version 14.04+) already has such feature enabled. + +In order to run JuNest via Linux namespaces: + +- As fakeroot - Allow to install/remove packages: ```junest -u``` + +Chroot based +------------ +This solution suits only for privileged users. JuNest provides the possibility +to run the environment via `chroot` program. +In particular, it uses a special program called `GRoot`, an enhanced `chroot` +wrapper that allows to bind mount directories specified by the user, such as +/proc, /sys, /dev, /tmp and $HOME, before +executing any programs inside the JuNest sandbox. In case the mounting will not +work, JuNest is even providing the possibility to run the environment directly via +the pure `chroot` command. + +In order to run JuNest via `chroot` solutions: + +- As root via `GRoot` - Allow to have fully root privileges inside JuNest environment (you need to be root for executing this): ```junest -g``` + +- As root via `chroot` - Allow to have fully root privileges inside JuNest environment (you need to be root for executing this): ```junest -r``` + +Execution modes comparison table +---------------- +The following table shows the capabilities that each backend program is able to perform: + +| | QEMU | Root privileges required | Manage Official Packages | Manage AUR Packages | Portability | Support | User modes | +| --- | ---- | ------------------------ | ------------------------ | ------------------- | ----------- | ------- | ---------- | +| **Proot** | YES | NO | YES | YES | YES | Poor | Normal user and `fakeroot` | +| **Linux Namespaces** | NO | NO | YES | NO | Poor | YES | `fakeroot` only | +| **Chroot** | NO | YES | YES | YES | YES | YES | `root` only | + Advanced usage ============== - ## Build image ## You can build a new JuNest image from scratch by running the following command: @@ -114,7 +177,7 @@ files in order to make JuNest working properly (such as pacman, yogurt and proot The option **-n** will skip the final validation tests if they are not needed. Remember that the script to build the image must run in an Arch Linux OS with arch-install-scripts, package-query, git and the base-devel packages installed. -To change the build directory just use the *JUNEST_TEMPDIR* (by default /tmp). +To change the build directory just use the `JUNEST_TEMPDIR` (by default /tmp). After creating the image junest-x86\_64.tar.gz you can install it by running: @@ -141,12 +204,12 @@ To bind a host directory to a guest location, you can use proot arguments: junest -p "-b /mnt/mydata:/home/user/mydata" -Check out the proot options with: +This will works with PRoot, Namespace and GRoot backend programs. +Check out the backend program options by passing `--help` option: - junest -p "--help" + junest [-u|-g] -p "--help" ## Systemd integration ## - Although JuNest has not been designed to be a complete container, it is even possible to virtualize the process tree thanks to the [systemd container](https://wiki.archlinux.org/index.php/Systemd-nspawn). The JuNest containter allows to run services inside the container that can be @@ -169,14 +232,14 @@ Internals There are two main chroot jail used in JuNest. The main one is [proot](https://wiki.archlinux.org/index.php/Proot) which allows unprivileged users to execute programs inside a sandbox and -jchroot, a small and portable version of +GRoot, a small and portable version of [arch-chroot](https://wiki.archlinux.org/index.php/Chroot) which is an enhanced chroot for privileged users that mounts the primary directories (i.e. /proc, /sys, /dev and /run) before executing any programs inside the sandbox. ## Automatic fallback to classic chroot ## -If jchroot fails for some reasons in the host system (i.e. it is not able to +If GRoot fails for some reasons in the host system (i.e. it is not able to mount one of the directories), JuNest automatically tries to fallback to the classic chroot. @@ -345,6 +408,18 @@ Troubleshooting > since JuNest will try to preserve the JuNest environment by assigning ownership > of the files to the real user. +## Not enabled User namespace or kernel too old ## + +> **Q**: Why do I get warning when I run JuNest via Linux namespaces? + + $> junest -u + User namespace is not enabled or Kernel too old (<3.8). Proceeding anyway... + +> **A**: This means that JuNest detected that the host OS either +> does not have a newer Linux version or the user namespace is not enabled. +> JuNest does not stop the execution of the program but it attempts to run it +> anyway. Try to use Proot as backend program in case is not possible to invoke namespaces. + More documentation ================== There are additional tutorials in the diff --git a/bin/groot b/bin/groot new file mode 100755 index 0000000..4033705 --- /dev/null +++ b/bin/groot @@ -0,0 +1,190 @@ +#!/bin/bash +# +# This script is the simplified and portable version of arch-chroot +# (https://wiki.archlinux.org/index.php/Change_root#Using_arch-chroot) +# + +set -e + +# JUNEST_BASE can be overridden for testing purposes. +# There is no need for doing it for normal usage. +JUNEST_BASE="${JUNEST_BASE:-$(readlink -f $(dirname $(readlink -f "$0"))/..)}" +NAME='GRoot' +CMD='groot' +DESCRIPTION="I am $NAME!" +CHROOTCMD=${CHROOTCMD:-chroot} +SHELL="/bin/sh" +MOUNT=mount +UMOUNT=umount +MOUNTPOINT=mountpoint +MKDIR=mkdir +TOUCH=touch +CUT=cut +SORT=sort +UNIQ=uniq +GREP=grep +MOUNTS_FILE=/proc/self/mounts + +NOT_EXISTING_FILE=103 +NOT_ABSOLUTE_PATH=111 +NO_ROOT_PRIVILEGES=110 + +source "${JUNEST_BASE}/lib/utils/utils.sh" + + +################################ MAIN FUNCTIONS ########################### + +function chroot_teardown() { + # Remove all mounts starting from the most nested ones. + # Suffix the CHROOTDIR with / to avoid umounting directories not belonging + # to CHROOTDIR. + for mp in $($GREP "${CHROOTDIR%/}/" $MOUNTS_FILE | $CUT -f2 -d' ' | $SORT -r | $UNIQ) + do + $UMOUNT $mp + done + $UMOUNT ${CHROOTDIR%/} +} + +function chroot_maybe_add_mount() { + local cond=$1 + shift + if eval "$cond"; then + $MOUNT "$@" + return + fi + return 1 +} + +function chroot_setup() { + $OPT_NO_UMOUNT || check_and_trap 'chroot_teardown' QUIT EXIT ABRT KILL TERM INT + + if ! chroot_maybe_add_mount "! $MOUNTPOINT -q '$CHROOTDIR'" --bind "$CHROOTDIR" "$CHROOTDIR" + then + warn "Failed mount of directories. $CHROOTDIR is already a mountpoint. Skipping it..." + return 0 + fi + + local re='(.*):(.*)' + for binds in ${BINDINGS[@]} + do + local host_path="" + local guest_path="" + if [[ $binds =~ $re ]] + then + local host_path="${BASH_REMATCH[1]}" + local guest_path="${BASH_REMATCH[2]}" + else + local host_path="$binds" + local guest_path="$binds" + fi + + create_node "${host_path}" "${CHROOTDIR}${guest_path}" + $MOUNT --rbind "${host_path}" "${CHROOTDIR}${guest_path}" + done +} + +function create_node() { + local src="$1" + local dst="$2" + if [[ ! -e $src ]] + then + die_on_status $NOT_EXISTING_FILE "${src} does not exist." + elif [[ $src != /* ]] + then + die_on_status $NOT_ABSOLUTE_PATH "${src} is not an absolute path." + elif [[ -f $src ]] + then + $TOUCH "$dst" + elif [[ -d $src ]] + then + $MKDIR -p "$dst" + fi +} + +function usage() { + cat < [command]] + +Options: + -b, --bind + Make the content of accessible in the guest rootfs. + + This option makes any file or directory of the host rootfs + accessible in the confined environment just as if it were part of + the guest rootfs. By default the host path is bound to the same + path in the guest rootfs but users can specify any other location + with the syntax: -b :. This option can + be invoked multiple times and the paths specified must be absolutes. + -n, --no-umount + Do not umount after chroot session finished. + + -h, --help Print this help message + + -V, --version Show the $NAME version + +If 'command' is unspecified, $CMD will launch $SHELL. + +EOF +} + +version() { + echo -e "$NAME $(cat $JUNEST_BASE/VERSION)" +} + +function parse_arguments() { + OPT_BIND=false + BINDINGS=() + OPT_NO_UMOUNT=false + OPT_HELP=false + OPT_VERSION=false + for opt in "$@" + do + case "$1" in + -b|--bind) OPT_BIND=true ; shift ; BINDINGS+=("$1") ; shift ;; + -n|--no-umount) OPT_NO_UMOUNT=true ; shift ;; + -h|--help) OPT_HELP=true ; shift ;; + -V|--version) OPT_VERSION=true ; shift ;; + -*) die "Invalid option $1" ;; + *) break ;; + esac + done + + if [[ ! -z $1 ]] + then + CHROOTDIR="$1" + shift + fi + ARGS=() + for arg in "$@" + do + ARGS+=("$arg") + done +} + +function is_user_root() { + (( EUID == 0 )) +} + +function execute_operation() { + $OPT_HELP && usage && return + $OPT_VERSION && version && return + + is_user_root || die_on_status $NO_ROOT_PRIVILEGES 'This script must be run with root privileges' + + [[ -d $CHROOTDIR ]] || die_on_status $NOT_EXISTING_FILE "Can't create chroot on non-directory $CHROOTDIR" + + chroot_setup "$CHROOTDIR" || die "Failed to setup chroot $CHROOTDIR" + + $CHROOTCMD "$CHROOTDIR" "${ARGS[@]}" +} + + +function main() { + parse_arguments "$@" + execute_operation +} + +main "$@" diff --git a/bin/jchroot b/bin/jchroot deleted file mode 100755 index fe43661..0000000 --- a/bin/jchroot +++ /dev/null @@ -1,98 +0,0 @@ -#!/bin/bash -# -# This script is the simplified and portable version of arch-chroot -# (https://wiki.archlinux.org/index.php/Change_root#Using_arch-chroot) -# - -set -e - -JUNEST_BASE="$(readlink -f $(dirname $(readlink -f "$0"))/..)" - -source "${JUNEST_BASE}/lib/utils/utils.sh" - -################################ MAIN FUNCTIONS ########################### - -chroot_add_mount() { - mount "$@" && CHROOT_ACTIVE_MOUNTS=("$2" "${CHROOT_ACTIVE_MOUNTS[@]}") -} - -chroot_maybe_add_mount() { - local cond=$1; shift - if eval "$cond"; then - chroot_add_mount "$@" - fi -} - -chroot_setup() { - CHROOT_ACTIVE_MOUNTS=() - [[ $(trap -p EXIT) ]] && die '(BUG): attempting to overwrite existing EXIT trap' - trap 'chroot_teardown' EXIT - - chroot_maybe_add_mount "! mountpoint -q '$1'" "$1" "$1" --bind && - chroot_add_mount proc "$1/proc" -t proc -o nosuid,noexec,nodev && - chroot_add_mount sys "$1/sys" -t sysfs -o nosuid,noexec,nodev,ro && - chroot_add_mount udev "$1/dev" -t devtmpfs -o mode=0755,nosuid && - chroot_add_mount devpts "$1/dev/pts" -t devpts -o mode=0620,gid=5,nosuid,noexec && - chroot_add_mount shm "$1/dev/shm" -t tmpfs -o mode=1777,nosuid,nodev && - chroot_add_mount run "$1/run" -t tmpfs -o nosuid,nodev,mode=0755 && - chroot_add_mount tmp "$1/tmp" -t tmpfs -o mode=1777,atime,nodev,nosuid && - mkdir -p "$1/$HOME" && - chroot_add_mount $HOME "$1/$HOME" --bind - - mkdir -p "$1/run/lock" -} - -chroot_teardown() { - umount "${CHROOT_ACTIVE_MOUNTS[@]}" - unset CHROOT_ACTIVE_MOUNTS -} - -usage() { - cat < Proot arguments (use $CMD -p \"--help\" to check out the proot options)" + echo -e "-g, --groot Run $NAME with root privileges via GRoot" + echo -e "-r, --root Run $NAME with root privileges via classic chroot" + echo -e "-u, --namespace Use Linux Namespace (with GRoot) instead of PRoot" + echo -e "-p, --backend-args Arguments for backend program (PRoot or GRoot)" + echo -e " ($CMD -p \"--help\" to check out the PRoot options" + echo -e " $CMD -g -p \"--help\" to check out the GRoot options" + echo -e " $CMD -r -p \"--help\" to check out the chroot options)" echo echo -e "Building options:" echo -e "-b, --build-image Build a $NAME image (must run in ArchLinux)" echo -e "-n, --disable-validation Disable the $NAME image validation" - echo -e "-c, --check <${CMD}_script> Validate the env located in ${JUNEST_HOME}" - echo -e " using ${CMD}_script. This will alterate the environment" - echo -e "-s, --skip-root-tests Skip the root tests during the validation process" echo echo -e "General options:" echo -e "-h, --help Show this help message" - echo -e "-v, --version Show the $NAME version" + echo -e "-V, --version Show the $NAME version" } version() { @@ -50,33 +59,17 @@ version() { check_cli(){ if $OPT_BUILD_IMAGE - then - if $OPT_DELETE || $OPT_HELP || $OPT_VERSION || $OPT_SETUP_FROM_FILE || \ - $OPT_FAKEROOT || $OPT_ROOT || $OPT_CHECK - then - die "The build image option must be used exclusively" - fi - fi - if $OPT_SKIP_ROOT_TEST then if $OPT_DELETE || $OPT_HELP || $OPT_VERSION || $OPT_SETUP_FROM_FILE || \ $OPT_FAKEROOT || $OPT_ROOT then - die "The skip root tests option must be used with either build image or check options" - fi - fi - if $OPT_CHECK - then - if $OPT_DELETE || $OPT_HELP || $OPT_VERSION || $OPT_SETUP_FROM_FILE || \ - $OPT_FAKEROOT || $OPT_ROOT || $OPT_BUILD_IMAGE - then - die "The validation image option must be used exclusively" + die "The build image option must be used exclusively" fi fi if $OPT_DISABLE_VALIDATION then if $OPT_DELETE || $OPT_HELP || $OPT_VERSION || $OPT_SETUP_FROM_FILE || \ - $OPT_FAKEROOT || $OPT_ROOT || $OPT_CHECK + $OPT_FAKEROOT || $OPT_ROOT then die "The disable validation option must be used with the build image option only" fi @@ -84,7 +77,7 @@ check_cli(){ if $OPT_DELETE then if $OPT_BUILD_IMAGE || $OPT_HELP || $OPT_VERSION || $OPT_SETUP_FROM_FILE || \ - $OPT_FAKEROOT || $OPT_ROOT || $OPT_DISABLE_VALIDATION || $OPT_CHECK + $OPT_FAKEROOT || $OPT_ROOT || $OPT_DISABLE_VALIDATION then die "The $NAME delete option must be used exclusively" fi @@ -92,7 +85,7 @@ check_cli(){ if $OPT_HELP then if $OPT_BUILD_IMAGE || $OPT_DELETE || $OPT_VERSION || $OPT_SETUP_FROM_FILE || \ - $OPT_FAKEROOT || $OPT_ROOT || $OPT_DISABLE_VALIDATION || $OPT_CHECK + $OPT_FAKEROOT || $OPT_ROOT || $OPT_DISABLE_VALIDATION then die "The $NAME help option must be used exclusively" fi @@ -100,7 +93,7 @@ check_cli(){ if $OPT_VERSION then if $OPT_BUILD_IMAGE || $OPT_DELETE || $OPT_HELP || $OPT_SETUP_FROM_FILE || \ - $OPT_FAKEROOT || $OPT_ROOT || $OPT_DISABLE_VALIDATION || $OPT_CHECK + $OPT_FAKEROOT || $OPT_ROOT || $OPT_DISABLE_VALIDATION then die "The $NAME version option must be used exclusively" fi @@ -109,10 +102,10 @@ check_cli(){ then die "You must access to $NAME with either fakeroot or root permissions" fi - if $OPT_PROOT_ARGS || $OPT_ARCH + if $OPT_BACKEND_ARGS || $OPT_ARCH then if $OPT_BUILD_IMAGE || $OPT_DELETE || $OPT_HELP || \ - $OPT_ROOT || $OPT_VERSION || $OPT_DISABLE_VALIDATION || $OPT_CHECK + $OPT_ROOT || $OPT_VERSION || $OPT_DISABLE_VALIDATION then die "Invalid syntax: Proot and arch args are not allowed with the other options" fi @@ -120,7 +113,7 @@ check_cli(){ if [ "$ARGS" != "" ] then if $OPT_BUILD_IMAGE || $OPT_DELETE || $OPT_HELP || $OPT_SETUP_FROM_FILE || \ - $OPT_VERSION || $OPT_DISABLE_VALIDATION || $OPT_CHECK + $OPT_VERSION || $OPT_DISABLE_VALIDATION then die "No arguments are needed. For the CLI syntax run: $CMD --help" fi @@ -135,15 +128,15 @@ function parse_arguments(){ IMAGE_FILE="" OPT_FAKEROOT=false OPT_ROOT=false - OPT_PROOT_ARGS=false - PROOT_ARGS="" + OPT_GROOT=false + OPT_USER_NAMESPACE=false + OPT_BACKEND_ARGS=false + BACKEND_ARGS="" OPT_ARCH=false ARCH_ARG="" OPT_BUILD_IMAGE=false OPT_DISABLE_VALIDATION=false - OPT_CHECK=false CHECK_ARG="" - OPT_SKIP_ROOT_TEST=false OPT_DELETE=false OPT_HELP=false OPT_VERSION=false @@ -153,15 +146,15 @@ function parse_arguments(){ -i|--setup-from-file) OPT_SETUP_FROM_FILE=true ; shift ; IMAGE_FILE=$1 ; shift ;; -f|--fakeroot) OPT_FAKEROOT=true ; shift ;; -r|--root) OPT_ROOT=true ; shift ;; - -p|--proot-args) OPT_PROOT_ARGS=true ; shift ; PROOT_ARGS=$1; shift ;; + -g|--groot) OPT_GROOT=true ; shift ;; + -u|--namespace) OPT_USER_NAMESPACE=true ; shift ;; + -p|--backend-args) OPT_BACKEND_ARGS=true ; shift ; BACKEND_ARGS=$1; shift ;; -a|--arch) OPT_ARCH=true ; shift ; ARCH_ARG=$1; shift ;; -b|--build-image) OPT_BUILD_IMAGE=true ; shift ;; -n|--disable-validation) OPT_DISABLE_VALIDATION=true ; shift ;; - -c|--check) OPT_CHECK=true ; shift ; CHECK_ARG=$1; shift ;; - -s|--skip-root-tests) OPT_SKIP_ROOT_TEST=true ; shift ;; -d|--delete) OPT_DELETE=true ; shift ;; -h|--help) OPT_HELP=true ; shift ;; - -v|--version) OPT_VERSION=true ; shift ;; + -V|--version) OPT_VERSION=true ; shift ;; --) shift ; break ;; -*) die "Invalid option $1" ;; *) break ;; @@ -180,14 +173,11 @@ function execute_operation(){ $OPT_VERSION && version && return if $OPT_BUILD_IMAGE; then - build_image_env $OPT_DISABLE_VALIDATION $OPT_SKIP_ROOT_TEST + build_image_env $OPT_DISABLE_VALIDATION return elif $OPT_DELETE; then delete_env return - elif $OPT_CHECK; then - check_env "${JUNEST_HOME}" "${CHECK_ARG}" $OPT_SKIP_ROOT_TEST - return fi if ! is_env_installed @@ -205,20 +195,28 @@ function execute_operation(){ [ -z "${ARCH_ARG}" ] || \ die "The option --arch cannot be specified since JuNest has already been downloaded in $JUNEST_HOME" - if $OPT_FAKEROOT; then - run_env_as_fakeroot "${PROOT_ARGS}" "${ARGS[@]}" + local run_env + if $OPT_USER_NAMESPACE; then + run_env=run_env_with_namespace + elif $OPT_FAKEROOT; then + run_env=run_env_as_fakeroot elif $OPT_ROOT; then - run_env_as_root "${ARGS[@]}" + run_env=run_env_as_chroot + elif $OPT_GROOT; then + run_env=run_env_as_groot else - run_env_as_user "${PROOT_ARGS}" "${ARGS[@]}" + run_env=run_env_as_user fi + + $run_env "${BACKEND_ARGS}" "${ARGS[@]}" + } -function cli() { +function main() { parse_arguments "$@" check_cli execute_operation } -cli "$@" +main "$@" # vim: set ts=4 sw=4 noet: diff --git a/lib/checks/check.sh b/lib/checks/check.sh new file mode 100755 index 0000000..4a0cd73 --- /dev/null +++ b/lib/checks/check.sh @@ -0,0 +1,62 @@ +#!/usr/bin/env bash +# +# This modules is used for: +# - Running checks against the building JuNest image +# - Integration tests on JuNest script against different execution modes (i.e. -f, -u, -r modes) +# +# Dependencies: +# - None +# +# vim: ft=sh + +set -eu + +OPT_RUN_ROOT_TESTS=${1:-false} +RUN_ROOT_TESTS=false +[[ ${OPT_RUN_ROOT_TESTS} == "--run-root-tests" ]] && RUN_ROOT_TESTS=true + +OPT_SKIP_AUR_TESTS=${1:-false} +SKIP_AUR_TESTS=false +[[ ${OPT_SKIP_AUR_TESTS} == "--skip-aur-tests" ]] && SKIP_AUR_TESTS=true + +JUNEST_HOME=${JUNEST_HOME:-$HOME/.junest} + +# JUNEST_BASE can be overridden for testing purposes. +# There is no need for doing it for normal usage. +JUNEST_BASE="${JUNEST_BASE:-$(readlink -f $(dirname $(readlink -f "$0"))/../..)}" + +source "${JUNEST_BASE}/lib/utils/utils.sh" +source "${JUNEST_BASE}/lib/core/common.sh" + +info "Validating JuNest located in ${JUNEST_HOME}..." + +info "Initial JuNest setup..." +echo "Server = ${DEFAULT_MIRROR}" >> /etc/pacman.d/mirrorlist +pacman --noconfirm -Syy +pacman --noconfirm -S base-devel + +info "Checking essential executables work..." +pacman -Qi pacman 1> /dev/null +yogurt -V 1> /dev/null +/opt/proot/proot-$ARCH --help 1> /dev/null + +repo_package1=tree +echo "Checking ${repo_package1} package from official repo..." +pacman --noconfirm -S ${repo_package1} +tree -L 1 +pacman --noconfirm -Rsn ${repo_package1} + +repo_package2=iftop +info "Checking ${repo_package2} package from official repo..." +pacman --noconfirm -S ${repo_package2} +$RUN_ROOT_TESTS && iftop -t -s 5 +pacman --noconfirm -Rsn ${repo_package2} + +if ! $SKIP_AUR_TESTS +then + aur_package=tcptraceroute + info "Checking ${aur_package} package from AUR repo..." + yogurt -A --noconfirm -S ${aur_package} + $RUN_ROOT_TESTS && tcptraceroute localhost + pacman --noconfirm -Rsn ${aur_package} +fi diff --git a/lib/core/build.sh b/lib/core/build.sh index 017ccad..497e928 100644 --- a/lib/core/build.sh +++ b/lib/core/build.sh @@ -40,7 +40,6 @@ function build_image_env(){ _check_package git local disable_validation=$1 - local skip_root_tests=$2 local maindir=$(TMPDIR=$JUNEST_TEMPDIR mktemp -d -t ${CMD}.XXXXXXXXXX) sudo mkdir -p ${maindir}/root @@ -51,7 +50,8 @@ function build_image_env(){ # All the essential executables (ln, mkdir, chown, etc) are in coreutils # yaourt requires sed # localedef (called by locale-gen) requires gzip - sudo pacstrap -G -M -d ${maindir}/root pacman coreutils libunistring archlinux-keyring sed gzip + # unshare command belongs to util-linux + sudo pacstrap -G -M -d ${maindir}/root pacman coreutils libunistring archlinux-keyring sed gzip util-linux sudo bash -c "echo 'Server = $DEFAULT_MIRROR' >> ${maindir}/root/etc/pacman.d/mirrorlist" sudo mkdir -p ${maindir}/root/run/lock @@ -69,11 +69,11 @@ function build_image_env(){ # sed command is required for locale-gen sudo ln -sf /usr/share/zoneinfo/posix/UTC ${maindir}/root/etc/localtime sudo bash -c "echo 'en_US.UTF-8 UTF-8' >> ${maindir}/root/etc/locale.gen" - sudo ${maindir}/root/opt/junest/bin/jchroot ${maindir}/root locale-gen + sudo ${maindir}/root/opt/junest/bin/groot ${maindir}/root locale-gen sudo bash -c "echo LANG=\"en_US.UTF-8\" >> ${maindir}/root/etc/locale.conf" info "Setting up the pacman keyring (this might take a while!)..." - sudo ${maindir}/root/opt/junest/bin/jchroot ${maindir}/root bash -c \ + sudo ${maindir}/root/opt/junest/bin/groot ${maindir}/root bash -c \ "pacman-key --init; pacman-key --populate archlinux; [ -e /etc/pacman.d/gnupg/S.gpg-agent ] && gpg-connect-agent -S /etc/pacman.d/gnupg/S.gpg-agent killagent /bye" sudo rm ${maindir}/root/var/cache/pacman/pkg/* @@ -88,7 +88,8 @@ function build_image_env(){ then mkdir -p ${maindir}/root_test $TAR -zxpf ${imagefile} -C "${maindir}/root_test" - check_env "${maindir}/root_test" "${maindir}/root_test/opt/${CMD}/bin/${CMD}" $skip_root_tests + JUNEST_HOME="${maindir}/root_test" ${maindir}/root_test/opt/${CMD}/bin/${CMD} -f ./lib/checks/check.sh + JUNEST_HOME="${maindir}/root_test" sudo -E ${maindir}/root_test/opt/${CMD}/bin/${CMD} -g ./lib/checks/check.sh --run-root-tests fi sudo cp ${maindir}/output/${imagefile} ${ORIGIN_WD} @@ -97,44 +98,3 @@ function build_image_env(){ trap - QUIT EXIT ABRT KILL TERM INT sudo rm -fr "$maindir" } - -function check_env(){ - local testdir=$1 - local cmd=$2 - local skip_root_tests=$3 - info "Validating ${NAME} located in ${testdir} using the ${cmd} script..." - echo "Server = ${DEFAULT_MIRROR}" >> ${testdir}/etc/pacman.d/mirrorlist - JUNEST_HOME=${testdir} ${cmd} -f pacman --noconfirm -Syy - - # Check most basic executables work - $skip_root_tests || JUNEST_HOME=${testdir} sudo -E ${cmd} -r pacman -Qi pacman 1> /dev/null - JUNEST_HOME=${testdir} ${cmd} -- pacman -Qi pacman 1> /dev/null - JUNEST_HOME=${testdir} ${cmd} -f -- pacman -Qi pacman 1> /dev/null - $skip_root_tests || JUNEST_HOME=${testdir} sudo -E ${cmd} -r yogurt -V 1> /dev/null - JUNEST_HOME=${testdir} ${cmd} -- yogurt -V 1> /dev/null - JUNEST_HOME=${testdir} ${cmd} -f -- yogurt -V 1> /dev/null - $skip_root_tests || JUNEST_HOME=${testdir} sudo -E ${cmd} -r /opt/proot/proot-$ARCH --help 1> /dev/null - JUNEST_HOME=${testdir} ${cmd} -- /opt/proot/proot-$ARCH --help 1> /dev/null - JUNEST_HOME=${testdir} ${cmd} -f -- /opt/proot/proot-$ARCH --help 1> /dev/null - - local repo_package=tree - info "Installing ${repo_package} package from official repo using proot..." - JUNEST_HOME=${testdir} ${cmd} -f pacman --noconfirm -S ${repo_package} - JUNEST_HOME=${testdir} ${cmd} tree - JUNEST_HOME=${testdir} ${cmd} -f tree - - local repo_package=iftop - info "Installing ${repo_package} package from official repo using root..." - JUNEST_HOME=${testdir} ${cmd} -f pacman --noconfirm -S ${repo_package} - $skip_root_tests || JUNEST_HOME=${testdir} sudo -E ${cmd} -r iftop -t -s 5 - - JUNEST_HOME=${testdir} ${cmd} -f pacman --noconfirm -S base-devel - local aur_package=tcptraceroute - info "Installing ${aur_package} package from AUR repo using proot..." - JUNEST_HOME=${testdir} ${cmd} -f -- yogurt -A --noconfirm -S ${aur_package} - $skip_root_tests || JUNEST_HOME=${testdir} sudo -E ${cmd} -r tcptraceroute localhost - - info "Removing the previous packages..." - JUNEST_HOME=${testdir} ${cmd} -f pacman --noconfirm -Rsn tcptraceroute tree iftop - -} diff --git a/lib/core/chroot.sh b/lib/core/chroot.sh index ff8465d..9a104aa 100644 --- a/lib/core/chroot.sh +++ b/lib/core/chroot.sh @@ -8,27 +8,12 @@ # # vim: ft=sh -####################################### -# Run JuNest as real root. -# -# Globals: -# JUNEST_HOME (RO) : The JuNest home directory. -# UID (RO) : The user ID. -# SUDO_USER (RO) : The sudo user ID. -# SUDO_GID (RO) : The sudo group ID. -# SH (RO) : Contains the default command to run in JuNest. -# Arguments: -# cmd ($@?) : The command to run inside JuNest environment. -# Default command is defined by SH variable. -# Returns: -# $ARCHITECTURE_MISMATCH : If host and JuNest architecture are different. -# Output: -# - : The command output. -####################################### -function run_env_as_root(){ - source ${JUNEST_HOME}/etc/junest/info - [ "$JUNEST_ARCH" != "$ARCH" ] && \ - die_on_status $ARCHITECTURE_MISMATCH "The host system architecture is not correct: $ARCH != $JUNEST_ARCH" +function _run_env_as_xroot(){ + local cmd=$1 + local backend_args="$2" + shift 2 + + check_same_arch local uid=$UID # SUDO_USER is more reliable compared to SUDO_UID @@ -41,5 +26,63 @@ function run_env_as_root(){ trap - QUIT EXIT ABRT KILL TERM INT trap "[ -z $uid ] || chown_cmd -R ${uid} ${JUNEST_HOME};" EXIT QUIT ABRT KILL TERM INT - JUNEST_ENV=1 chroot_cmd "$JUNEST_HOME" "${SH[@]}" "-c" "${main_cmd}" + copy_common_files + + check_nested_env + + JUNEST_ENV=1 $cmd $backend_args "$JUNEST_HOME" "${SH[@]}" "-c" "${main_cmd}" +} + +####################################### +# Run JuNest as real root via GRoot command. +# +# Globals: +# JUNEST_HOME (RO) : The JuNest home directory. +# UID (RO) : The user ID. +# SUDO_USER (RO) : The sudo user ID. +# SUDO_GID (RO) : The sudo group ID. +# SH (RO) : Contains the default command to run in JuNest. +# Arguments: +# backend_args ($1) : The arguments to pass to proot +# cmd ($2-?) : The command to run inside JuNest environment. +# Default command is defined by SH variable. +# Returns: +# $ARCHITECTURE_MISMATCH : If host and JuNest architecture are different. +# Output: +# - : The command output. +####################################### +function run_env_as_groot(){ + local backend_args="$1" + shift + + provide_common_bindings + local bindings=${RESULT} + unset RESULT + + _run_env_as_xroot "$GROOT $bindings" "$backend_args" "$@" +} + +####################################### +# Run JuNest as real root via chroot command. +# +# Globals: +# JUNEST_HOME (RO) : The JuNest home directory. +# UID (RO) : The user ID. +# SUDO_USER (RO) : The sudo user ID. +# SUDO_GID (RO) : The sudo group ID. +# SH (RO) : Contains the default command to run in JuNest. +# Arguments: +# backend_args ($1) : The arguments to pass to proot +# cmd ($2-?) : The command to run inside JuNest environment. +# Default command is defined by SH variable. +# Returns: +# $ARCHITECTURE_MISMATCH : If host and JuNest architecture are different. +# Output: +# - : The command output. +####################################### +function run_env_as_chroot(){ + local backend_args="$1" + shift + + _run_env_as_xroot chroot_cmd "$backend_args" "$@" } diff --git a/lib/core/common.sh b/lib/core/common.sh index d317933..d2ab487 100644 --- a/lib/core/common.sh +++ b/lib/core/common.sh @@ -7,8 +7,6 @@ # # vim: ft=sh -set -e - NAME='JuNest' CMD='junest' DESCRIPTION='The Arch Linux based distro that runs upon any Linux distros without root access' @@ -19,21 +17,11 @@ ARCHITECTURE_MISMATCH=104 ROOT_ACCESS_ERROR=105 NESTED_ENVIRONMENT=106 VARIABLE_NOT_SET=107 +NO_CONFIG_FOUND=108 -if [ "$JUNEST_ENV" == "1" ] -then - die_on_status $NESTED_ENVIRONMENT "Error: Nested ${NAME} environments are not allowed" -elif [ ! -z $JUNEST_ENV ] && [ "$JUNEST_ENV" != "0" ] -then - die_on_status $VARIABLE_NOT_SET "The variable JUNEST_ENV is not properly set" -fi - -[ -z ${JUNEST_HOME} ] && JUNEST_HOME=~/.${CMD} -[ -z ${JUNEST_BASE} ] && JUNEST_BASE=${JUNEST_HOME}/opt/junest -if [ -z ${JUNEST_TEMPDIR} ] || [ ! -d ${JUNEST_TEMPDIR} ] -then - JUNEST_TEMPDIR=/tmp -fi +JUNEST_HOME=${JUNEST_HOME:-~/.${CMD}} +JUNEST_BASE=${JUNEST_BASE:-${JUNEST_HOME}/opt/junest} +JUNEST_TEMPDIR=${JUNEST_TEMPDIR:-/tmp} # The update of the variable PATH ensures that the executables are # found on different locations @@ -78,8 +66,8 @@ SH=("/bin/sh" "--login") # List of executables that are run in the host OS: PROOT="${JUNEST_HOME}/opt/proot/proot-${ARCH}" -CHROOT=${JUNEST_BASE}/bin/jchroot -CLASSIC_CHROOT="chroot" +GROOT=${JUNEST_BASE}/bin/groot +CLASSIC_CHROOT=chroot WGET="wget --no-check-certificate" CURL="curl -L -J -O -k" TAR=tar @@ -89,6 +77,9 @@ RM=rm MKDIR=mkdir GETENT=getent CP=cp +# Used for checking user namespace in config.gz file +ZGREP=zgrep +UNSHARE=unshare LD_EXEC="$LD_LIB --library-path ${JUNEST_HOME}/usr/lib:${JUNEST_HOME}/lib" @@ -97,27 +88,58 @@ LD_EXEC="$LD_LIB --library-path ${JUNEST_HOME}/usr/lib:${JUNEST_HOME}/lib" # image. function ln_cmd(){ - $LN $@ || $LD_EXEC ${JUNEST_HOME}/usr/bin/$LN $@ + $LN "$@" || $LD_EXEC ${JUNEST_HOME}/usr/bin/$LN "$@" } function getent_cmd(){ - $GETENT $@ || $LD_EXEC ${JUNEST_HOME}/usr/bin/$GETENT $@ + $GETENT "$@" || $LD_EXEC ${JUNEST_HOME}/usr/bin/$GETENT "$@" } function cp_cmd(){ - $CP $@ || $LD_EXEC ${JUNEST_HOME}/usr/bin/$CP $@ + $CP "$@" || $LD_EXEC ${JUNEST_HOME}/usr/bin/$CP "$@" } function rm_cmd(){ - $RM $@ || $LD_EXEC ${JUNEST_HOME}/usr/bin/$RM $@ + $RM "$@" || $LD_EXEC ${JUNEST_HOME}/usr/bin/$RM "$@" } function chown_cmd(){ - $CHOWN $@ || $LD_EXEC ${JUNEST_HOME}/usr/bin/$CHOWN $@ + $CHOWN "$@" || $LD_EXEC ${JUNEST_HOME}/usr/bin/$CHOWN "$@" } function mkdir_cmd(){ - $MKDIR $@ || $LD_EXEC ${JUNEST_HOME}/usr/bin/$MKDIR $@ + $MKDIR "$@" || $LD_EXEC ${JUNEST_HOME}/usr/bin/$MKDIR "$@" +} + +function zgrep_cmd(){ + # No need for LD_EXEC as zgrep is a POSIX shell script + $ZGREP "$@" || ${JUNEST_HOME}/usr/bin/$ZGREP "$@" +} + +function download_cmd(){ + $WGET "$@" || $CURL "$@" +} + +function chroot_cmd(){ + $CLASSIC_CHROOT "$@" || $LD_EXEC ${JUNEST_HOME}/usr/bin/$CLASSIC_CHROOT "$@" +} + +function unshare_cmd(){ + # Most of the distros do not have the `unshare` command updated + # with --user option available. + # Hence, give priority to the `unshare` executable in JuNest image. + # Also, unshare provides an environment in which /bin/sh maps to dash shell, + # therefore it ignores all the remaining SH arguments (i.e. --login) as + # they are not supported by dash. + if $LD_EXEC ${JUNEST_HOME}/usr/bin/$UNSHARE --user "${SH[0]}" "-c" ":" + then + $LD_EXEC ${JUNEST_HOME}/usr/bin/$UNSHARE "${@}" + elif $UNSHARE --user "${SH[0]}" "-c" ":" + then + $UNSHARE "$@" + else + die "Error: Something went wrong with unshare command. Exiting" + fi } function proot_cmd(){ @@ -130,20 +152,60 @@ function proot_cmd(){ then PROOT_NO_SECCOMP=1 ${PROOT} ${proot_args} "${@}" else - die "Error: Check if the ${CMD} arguments are correct and if the kernel is too old use the option ${CMD} -p \"-k 3.10\"" + die "Error: Something went wrong with proot command. Exiting" fi } -function download_cmd(){ - $WGET $@ || $CURL $@ -} - -function chroot_cmd(){ - $CHROOT "$@" || $CLASSIC_CHROOT "$@" || $LD_EXEC ${JUNEST_HOME}/usr/bin/chroot "$@" -} - ############## COMMON FUNCTIONS ############### +####################################### +# Check if the executable is being running inside a JuNest environment. +# +# Globals: +# JUNEST_ENV (RO) : The boolean junest env check +# NESTED_ENVIRONMENT (RO) : The nest env exception +# VARIABLE_NOT_SET (RO) : The var not set exception +# NAME (RO) : The JuNest name +# Arguments: +# None +# Returns: +# VARIABLE_NOT_SET : If no JUNEST_ENV is not properly set +# NESTED_ENVIRONMENT : If the script is executed inside JuNest env +# Output: +# None +####################################### +function check_nested_env() { + if [[ $JUNEST_ENV == "1" ]] + then + die_on_status $NESTED_ENVIRONMENT "Error: Nested ${NAME} environments are not allowed" + elif [[ ! -z $JUNEST_ENV ]] && [[ $JUNEST_ENV != "0" ]] + then + die_on_status $VARIABLE_NOT_SET "The variable JUNEST_ENV is not properly set" + fi +} + +####################################### +# Check if the architecture between Host OS and Guest OS is the same. +# +# Globals: +# JUNEST_HOME (RO) : The JuNest home path. +# ARCHITECTURE_MISMATCH (RO) : The arch mismatch exception +# ARCH (RO) : The host OS arch +# JUNEST_ARCH (RO) : The JuNest arch +# Arguments: +# None +# Returns: +# ARCHITECTURE_MISMATCH : If arch between host and guest is not the same +# Output: +# None +####################################### +function check_same_arch() { + source ${JUNEST_HOME}/etc/junest/info + [ "$JUNEST_ARCH" != "$ARCH" ] && \ + die_on_status $ARCHITECTURE_MISMATCH "The host system architecture is not correct: $ARCH != $JUNEST_ARCH" + return 0 +} + ####################################### # Provide the proot common binding options for both normal user and fakeroot. # The list of bindings can be found in `proot --help`. This function excludes @@ -160,7 +222,7 @@ function chroot_cmd(){ # Output: # None ####################################### -function _provide_common_bindings(){ +function provide_common_bindings(){ RESULT="" local re='(.*):.*' for bind in "/dev" "/sys" "/proc" "/tmp" "$HOME" @@ -191,7 +253,7 @@ function _provide_common_bindings(){ # Output: # None ####################################### -function _copy_passwd_and_group(){ +function copy_passwd_and_group(){ # Enumeration of users/groups is disabled/limited depending on how nsswitch.conf # is configured. # Try to at least get the current user via `getent passwd $USER` since it uses @@ -200,27 +262,27 @@ function _copy_passwd_and_group(){ ! getent_cmd passwd ${USER} >> ${JUNEST_HOME}/etc/passwd then warn "getent command failed or does not exist. Binding directly from /etc/passwd." - _copy_file /etc/passwd ${JUNEST_HOME}/etc/passwd + copy_file /etc/passwd ${JUNEST_HOME}/etc/passwd fi if ! getent_cmd group > ${JUNEST_HOME}/etc/group then warn "getent command failed or does not exist. Binding directly from /etc/group." - _copy_file /etc/group ${JUNEST_HOME}/etc/group + copy_file /etc/group ${JUNEST_HOME}/etc/group fi return 0 } -function _copy_file() { +function copy_file() { local file="${1}" [[ -r "$file" ]] && cp_cmd "$file" "${JUNEST_HOME}/$file" return 0 } -function _copy_common_files() { - _copy_file /etc/host.conf - _copy_file /etc/hosts - _copy_file /etc/nsswitch.conf - _copy_file /etc/resolv.conf +function copy_common_files() { + copy_file /etc/host.conf + copy_file /etc/hosts + copy_file /etc/nsswitch.conf + copy_file /etc/resolv.conf return 0 } diff --git a/lib/core/namespace.sh b/lib/core/namespace.sh new file mode 100644 index 0000000..9f4596f --- /dev/null +++ b/lib/core/namespace.sh @@ -0,0 +1,100 @@ +#!/usr/bin/env bash +# +# This module contains all namespace functionalities for JuNest. +# +# http://man7.org/linux/man-pages/man7/namespaces.7.html +# http://man7.org/linux/man-pages/man2/unshare.2.html +# +# Dependencies: +# - lib/utils/utils.sh +# - lib/core/common.sh +# +# vim: ft=sh + +CONFIG_PROC_FILE="/proc/config.gz" +CONFIG_BOOT_FILE="/boot/config-$($UNAME -r)" + +function _is_user_namespace_enabled() { + local config_file="" + if [[ -e $CONFIG_PROC_FILE ]] + then + config_file=$CONFIG_PROC_FILE + elif [[ -e $CONFIG_BOOT_FILE ]] + then + config_file=$CONFIG_BOOT_FILE + else + return $NOT_EXISTING_FILE + fi + + if ! zgrep_cmd -q "CONFIG_USER_NS=y" $config_file + then + return $NO_CONFIG_FOUND + fi +} + +function _check_user_namespace() { + set +e + _is_user_namespace_enabled + case $? in + $NOT_EXISTING_FILE) warn "Could not understand if user namespace is enabled. No config.gz file found. Proceeding anyway..." ;; + $NO_CONFIG_FOUND) warn "User namespace is not enabled or Kernel too old (<3.8). Proceeding anyway..." ;; + esac + set -e +} + +function _run_env_with_namespace(){ + local backend_args="$1" + shift + + check_nested_env + + provide_common_bindings + local bindings=${RESULT} + unset RESULT + + # Use option -n in groot because umount do not work sometimes. + # As soon as the process terminates, the namespace + # will terminate too with its own mounted directories. + if [[ "$1" != "" ]] + then + JUNEST_ENV=1 unshare_cmd --mount --user --map-root-user $GROOT -n $bindings $backend_args "$JUNEST_HOME" "${SH[@]}" "-c" "$(insert_quotes_on_spaces "${@}")" + else + JUNEST_ENV=1 unshare_cmd --mount --user --map-root-user $GROOT -n $bindings $backend_args "$JUNEST_HOME" "${SH[@]}" + fi +} + + +####################################### +# Run JuNest as fakeroot user via user namespace. +# +# Globals: +# JUNEST_HOME (RO) : The JuNest home directory. +# GROOT (RO) : The groot program. +# SH (RO) : Contains the default command to run in JuNest. +# Arguments: +# backend_args ($1) : The arguments to pass to proot +# cmd ($2-?) : The command to run inside JuNest environment. +# Default command is defined by SH variable. +# Returns: +# $ARCHITECTURE_MISMATCH : If host and JuNest architecture are different. +# Depends on the unshare command outcome. +# Output: +# - : The command output. +####################################### +function run_env_with_namespace() { + local backend_args="$1" + shift + _check_user_namespace + + check_same_arch + + copy_common_files + copy_file /etc/hosts.equiv + copy_file /etc/netgroup + copy_file /etc/networks + # No need for localtime as it is setup during the image build + #copy_file /etc/localtime + copy_passwd_and_group + + _run_env_with_namespace "$backend_args" "$@" +} diff --git a/lib/core/proot.sh b/lib/core/proot.sh index 39d901c..f678d9d 100644 --- a/lib/core/proot.sh +++ b/lib/core/proot.sh @@ -12,6 +12,7 @@ function _run_env_with_proot(){ local proot_args="$1" shift + check_nested_env if [ "$1" != "" ] then JUNEST_ENV=1 proot_cmd "${proot_args}" "${SH[@]}" "-c" "$(insert_quotes_on_spaces "${@}")" @@ -44,30 +45,33 @@ function _run_env_with_qemu(){ # Run JuNest as fakeroot. # # Globals: -# JUNEST_HOME (RO) : The JuNest home directory. -# EUID (RO) : The user ID. -# SH (RO) : Contains the default command to run in JuNest. +# JUNEST_HOME (RO) : The JuNest home directory. +# EUID (RO) : The user ID. +# SH (RO) : Contains the default command to run in JuNest. # Arguments: -# cmd ($@?) : The command to run inside JuNest environment. +# backend_args ($1) : The arguments to pass to proot +# cmd ($2-?) : The command to run inside JuNest environment. # Default command is defined by SH variable. # Returns: -# $ROOT_ACCESS_ERROR : If the user is the real root. +# $ROOT_ACCESS_ERROR : If the user is the real root. # Output: -# - : The command output. +# - : The command output. ####################################### function run_env_as_fakeroot(){ (( EUID == 0 )) && \ - die_on_status $ROOT_ACCESS_ERROR "You cannot access with root privileges. Use --root option instead." + die_on_status $ROOT_ACCESS_ERROR "You cannot access with root privileges. Use --groot option instead." + local backend_args="$1" + shift - _copy_common_files + copy_common_files - _provide_common_bindings + provide_common_bindings local bindings=${RESULT} unset RESULT # An alternative is via -S option: #_run_env_with_qemu "-S ${JUNEST_HOME} $1" "${@:2}" - _run_env_with_qemu "-0 ${bindings} -r ${JUNEST_HOME} $1" "${@:2}" + _run_env_with_qemu "-0 ${bindings} -r ${JUNEST_HOME} $backend_args" "$@" } ####################################### @@ -78,7 +82,8 @@ function run_env_as_fakeroot(){ # EUID (RO) : The user ID. # SH (RO) : Contains the default command to run in JuNest. # Arguments: -# cmd ($@?) : The command to run inside JuNest environment. +# backend_args ($1) : The arguments to pass to proot +# cmd ($2-?) : The command to run inside JuNest environment. # Default command is defined by SH variable. # Returns: # $ROOT_ACCESS_ERROR : If the user is the real root. @@ -87,23 +92,25 @@ function run_env_as_fakeroot(){ ####################################### function run_env_as_user(){ (( EUID == 0 )) && \ - die_on_status $ROOT_ACCESS_ERROR "You cannot access with root privileges. Use --root option instead." + die_on_status $ROOT_ACCESS_ERROR "You cannot access with root privileges. Use --groot option instead." + local backend_args="$1" + shift # Files to bind are visible in `proot --help`. # This function excludes /etc/mtab file so that # it will not give conflicts with the related # symlink in the Arch Linux image. - _copy_common_files - _copy_file /etc/hosts.equiv - _copy_file /etc/netgroup - _copy_file /etc/networks + copy_common_files + copy_file /etc/hosts.equiv + copy_file /etc/netgroup + copy_file /etc/networks # No need for localtime as it is setup during the image build - #_copy_file /etc/localtime - _copy_passwd_and_group + #copy_file /etc/localtime + copy_passwd_and_group - _provide_common_bindings + provide_common_bindings local bindings=${RESULT} unset RESULT - _run_env_with_qemu "${bindings} -r ${JUNEST_HOME} $1" "${@:2}" + _run_env_with_qemu "${bindings} -r ${JUNEST_HOME} $backend_args" "$@" } diff --git a/lib/utils/utils.sh b/lib/utils/utils.sh index 840fbe9..00e2cb6 100644 --- a/lib/utils/utils.sh +++ b/lib/utils/utils.sh @@ -170,6 +170,20 @@ function ask(){ [ "$res" == "Y" ] } +function check_and_trap() { + local sigs="${@:2:${#@}}" + local traps="$(trap -p $sigs)" + [[ $traps ]] && die "Attempting to overwrite existing $sigs trap: $traps" + trap $@ +} + +function check_and_force_trap() { + local sigs="${@:2:${#@}}" + local traps="$(trap -p $sigs)" + [[ $traps ]] && warn "Attempting to overwrite existing $sigs trap: $traps" + trap $@ +} + function insert_quotes_on_spaces(){ # It inserts quotes between arguments. # Useful to preserve quotes on command diff --git a/tests/unit-tests/test-chroot.sh b/tests/unit-tests/test-chroot.sh index 04b22a7..aa491e5 100755 --- a/tests/unit-tests/test-chroot.sh +++ b/tests/unit-tests/test-chroot.sh @@ -18,6 +18,7 @@ function oneTimeSetUp(){ function setUp(){ cwdSetUp junestSetUp + init_mocks } function tearDown(){ @@ -25,28 +26,42 @@ function tearDown(){ cwdTearDown } -function test_run_env_as_root_different_arch(){ - echo "JUNEST_ARCH=XXX" > ${JUNEST_HOME}/etc/junest/info - assertCommandFailOnStatus 104 run_env_as_root pwd -} - -function _test_run_env_as_root() { +function init_mocks() { chroot_cmd() { [ "$JUNEST_ENV" != "1" ] && return 1 - echo $@ + echo "chroot_cmd $@" } - - assertCommandSuccess run_env_as_root $@ + GROOT=chroot_cmd } -function test_run_env_as_root_cmd(){ - _test_run_env_as_root pwd - assertEquals "$JUNEST_HOME /bin/sh --login -c pwd" "$(cat $STDOUTF)" +function test_run_env_as_groot_cmd(){ + assertCommandSuccess run_env_as_groot "" pwd + assertEquals "chroot_cmd -b $HOME -b /tmp -b /proc -b /sys -b /dev $JUNEST_HOME /bin/sh --login -c pwd" "$(cat $STDOUTF)" } -function test_run_env_as_classic_root_no_cmd(){ - _test_run_env_as_root - assertEquals "$JUNEST_HOME /bin/sh --login -c /bin/sh --login" "$(cat $STDOUTF)" +function test_run_env_as_groot_no_cmd(){ + assertCommandSuccess run_env_as_groot "" + assertEquals "chroot_cmd -b $HOME -b /tmp -b /proc -b /sys -b /dev $JUNEST_HOME /bin/sh --login -c /bin/sh --login" "$(cat $STDOUTF)" +} + +function test_run_env_as_groot_cmd_with_backend_args(){ + assertCommandSuccess run_env_as_groot "-n -b /home/blah" pwd + assertEquals "chroot_cmd -b $HOME -b /tmp -b /proc -b /sys -b /dev -n -b /home/blah $JUNEST_HOME /bin/sh --login -c pwd" "$(cat $STDOUTF)" +} + +function test_run_env_as_chroot_cmd(){ + assertCommandSuccess run_env_as_chroot "" pwd + assertEquals "chroot_cmd $JUNEST_HOME /bin/sh --login -c pwd" "$(cat $STDOUTF)" +} + +function test_run_env_as_chroot_no_cmd(){ + assertCommandSuccess run_env_as_chroot "" + assertEquals "chroot_cmd $JUNEST_HOME /bin/sh --login -c /bin/sh --login" "$(cat $STDOUTF)" +} + +function test_run_env_as_chroot_cmd_with_backend_args(){ + assertCommandSuccess run_env_as_chroot "-n -b /home/blah" pwd + assertEquals "chroot_cmd -n -b /home/blah $JUNEST_HOME /bin/sh --login -c pwd" "$(cat $STDOUTF)" } source $JUNEST_ROOT/tests/utils/shunit2 diff --git a/tests/unit-tests/test-cli.sh b/tests/unit-tests/test-cli.sh deleted file mode 100755 index db23eea..0000000 --- a/tests/unit-tests/test-cli.sh +++ /dev/null @@ -1,181 +0,0 @@ -#!/bin/bash -source "$(dirname $0)/../utils/utils.sh" - -source $(dirname $0)/../../bin/junest -h &> /dev/null - -# Disable the exiterr -set +e - -function oneTimeSetUp(){ - setUpUnitTests -} - -function setUp(){ - function is_env_installed(){ - return 0 - } -} - -## Mock functions ## -function usage(){ - echo "usage" -} -function version(){ - echo "version" -} -function build_image_env(){ - local disable_validation=$1 - local skip_root_tests=$2 - echo "build_image_env($disable_validation,$skip_root_tests)" -} -function check_env(){ - local env_home=$1 - local cmd_script=$2 - local skip_root_tests=$3 - echo "check_env($env_home,$cmd_script,$skip_root_tests)" -} -function delete_env(){ - echo "delete_env" -} -function setup_env_from_file(){ - echo "setup_env_from_file($1)" -} -function setup_env(){ - echo "setup_env($1)" -} -function run_env_as_fakeroot(){ - local proot_args="$1" - shift - echo "run_env_as_fakeroot($proot_args,$@)" -} -function run_env_as_root(){ - echo "run_env_as_root $@" -} -function run_env_as_user(){ - local proot_args="$1" - shift - echo "run_env_as_user($proot_args,$@)" -} - -function test_help(){ - assertCommandSuccess cli -h - assertEquals "usage" "$(cat $STDOUTF)" - assertCommandSuccess cli --help - assertEquals "usage" "$(cat $STDOUTF)" -} -function test_version(){ - assertCommandSuccess cli -v - assertEquals "version" "$(cat $STDOUTF)" - assertCommandSuccess cli --version - assertEquals "version" "$(cat $STDOUTF)" -} -function test_build_image_env(){ - assertCommandSuccess cli -b - assertEquals "build_image_env(false,false)" "$(cat $STDOUTF)" - assertCommandSuccess cli --build-image - assertEquals "build_image_env(false,false)" "$(cat $STDOUTF)" - assertCommandSuccess cli -b -s - assertEquals "build_image_env(false,true)" "$(cat $STDOUTF)" - assertCommandSuccess cli -b -n - assertEquals "build_image_env(true,false)" "$(cat $STDOUTF)" - assertCommandSuccess cli -b -n -s - assertEquals "build_image_env(true,true)" "$(cat $STDOUTF)" - assertCommandSuccess cli --build-image --disable-validation --skip-root-tests - assertEquals "build_image_env(true,true)" "$(cat $STDOUTF)" -} -function test_check_env(){ - assertCommandSuccess cli -c myscript - assertEquals "check_env(${JUNEST_HOME},myscript,false)" "$(cat $STDOUTF)" - assertCommandSuccess cli --check myscript - assertEquals "check_env(${JUNEST_HOME},myscript,false)" "$(cat $STDOUTF)" - assertCommandSuccess cli -c myscript -s - assertEquals "check_env(${JUNEST_HOME},myscript,true)" "$(cat $STDOUTF)" - assertCommandSuccess cli --check myscript --skip-root-tests - assertEquals "check_env(${JUNEST_HOME},myscript,true)" "$(cat $STDOUTF)" -} -function test_delete_env(){ - assertCommandSuccess cli -d - assertEquals "delete_env" "$(cat $STDOUTF)" - assertCommandSuccess cli --delete - assertEquals "delete_env" "$(cat $STDOUTF)" -} -function test_setup_env_from_file(){ - is_env_installed(){ - return 1 - } - assertCommandSuccess cli -i myimage - assertEquals "$(echo -e "setup_env_from_file(myimage)\nrun_env_as_user(,)")" "$(cat $STDOUTF)" - assertCommandSuccess cli --setup-from-file myimage - assertEquals "$(echo -e "setup_env_from_file(myimage)\nrun_env_as_user(,)")" "$(cat $STDOUTF)" - - is_env_installed(){ - return 0 - } - assertCommandFail cli -i myimage -} - -function test_setup_env(){ - is_env_installed(){ - return 1 - } - assertCommandSuccess cli -a arm - assertEquals "$(echo -e "setup_env(arm)\nrun_env_as_user(,)")" "$(cat $STDOUTF)" - assertCommandSuccess cli --arch arm - assertEquals "$(echo -e "setup_env(arm)\nrun_env_as_user(,)")" "$(cat $STDOUTF)" - assertCommandSuccess cli - assertEquals "$(echo -e "setup_env()\nrun_env_as_user(,)")" "$(cat $STDOUTF)" - - is_env_installed(){ - return 0 - } - assertCommandFail cli -a arm -} -function test_run_env_as_fakeroot(){ - assertCommandSuccess cli -f - assertEquals "run_env_as_fakeroot(,)" "$(cat $STDOUTF)" - assertCommandSuccess cli --fakeroot - assertEquals "run_env_as_fakeroot(,)" "$(cat $STDOUTF)" - - assertCommandSuccess cli -f -p "-b arg" - assertEquals "run_env_as_fakeroot(-b arg,)" "$(cat $STDOUTF)" - assertCommandSuccess cli -f -p "-b arg" -- command -kv - assertEquals "run_env_as_fakeroot(-b arg,command -kv)" "$(cat $STDOUTF)" - assertCommandSuccess cli -f command --as - assertEquals "run_env_as_fakeroot(,command --as)" "$(cat $STDOUTF)" - assertCommandFail cli -a "myarch" -f command --as -} -function test_run_env_as_user(){ - assertCommandSuccess cli - assertEquals "run_env_as_user(,)" "$(cat $STDOUTF)" - - assertCommandSuccess cli -p "-b arg" - assertEquals "run_env_as_user(-b arg,)" "$(cat $STDOUTF)" - assertCommandSuccess cli -p "-b arg" -- command -ll - assertEquals "run_env_as_user(-b arg,command -ll)" "$(cat $STDOUTF)" - assertCommandSuccess cli command -ls - assertEquals "run_env_as_user(,command -ls)" "$(cat $STDOUTF)" - - assertCommandFail cli -a "myarch" -- command -ls -} -function test_run_env_as_root(){ - assertCommandSuccess cli -r - assertEquals "run_env_as_root " "$(cat $STDOUTF)" - assertCommandSuccess cli -r command - assertEquals "run_env_as_root command" "$(cat $STDOUTF)" -} - -function test_check_cli(){ - assertCommandFail cli -b -h - assertCommandFail cli -b -c - assertCommandFail cli -d -s - assertCommandFail cli -n -v - assertCommandFail cli -d -r - assertCommandFail cli -h -f - assertCommandFail cli -v -i fsd - assertCommandFail cli -f -r - assertCommandFail cli -p args -v - assertCommandFail cli -a arch -v - assertCommandFail cli -d args -} - -source $(dirname $0)/../utils/shunit2 diff --git a/tests/unit-tests/test-common.sh b/tests/unit-tests/test-common.sh index ad871f5..cfa3475 100755 --- a/tests/unit-tests/test-common.sh +++ b/tests/unit-tests/test-common.sh @@ -20,10 +20,20 @@ function oneTimeTearDown(){ } function setUp(){ - ld_exec() { + ld_exec_mock() { echo "ld_exec $@" } - LD_EXEC=ld_exec + ld_exec_mock_false() { + echo "ld_exec $@" + return 1 + } + LD_EXEC=ld_exec_mock + + unshare_mock() { + echo "unshare $@" + } + UNSHARE=unshare_mock + } function test_ln(){ @@ -100,17 +110,40 @@ function test_mkdir(){ MKDIR=false LD_EXEC=false assertCommandFail mkdir_cmd -p new_dir/new_dir } +function test_zgrep(){ + ZGREP=echo assertCommandSuccess zgrep_cmd new_file + assertEquals "new_file" "$(cat $STDOUTF)" + + mkdir -p ${JUNEST_HOME}/usr/bin + touch ${JUNEST_HOME}/usr/bin/false + chmod +x ${JUNEST_HOME}/usr/bin/false + + echo -e "#!/bin/bash\necho zgrep" > ${JUNEST_HOME}/usr/bin/false + ZGREP=false assertCommandSuccess zgrep_cmd new_file + assertEquals "zgrep" "$(cat $STDOUTF)" + + echo -e "#!/bin/bash\nexit 1" > ${JUNEST_HOME}/usr/bin/false + ZGREP=false assertCommandFail zgrep_cmd new_file +} + +function test_unshare(){ + assertCommandSuccess unshare_cmd new_program + assertEquals "$(echo -e "ld_exec ${JUNEST_HOME}/usr/bin/$UNSHARE --user /bin/sh -c :\nld_exec ${JUNEST_HOME}/usr/bin/$UNSHARE new_program")" "$(cat $STDOUTF)" + + LD_EXEC=ld_exec_mock_false assertCommandSuccess unshare_cmd new_program + assertEquals "$(echo -e "ld_exec ${JUNEST_HOME}/usr/bin/unshare_mock --user /bin/sh -c :\nunshare --user /bin/sh -c :\nunshare new_program")" "$(cat $STDOUTF)" + + UNSHARE=false LD_EXEC=false assertCommandFail unshare_cmd new_program +} + function test_chroot(){ - CHROOT=echo assertCommandSuccess chroot_cmd root + CLASSIC_CHROOT=echo assertCommandSuccess chroot_cmd root assertEquals "root" "$(cat $STDOUTF)" - CHROOT=false CLASSIC_CHROOT=echo assertCommandSuccess chroot_cmd root - assertEquals "root" "$(cat $STDOUTF)" + CLASSIC_CHROOT=false assertCommandSuccess chroot_cmd root + assertEquals "ld_exec $JUNEST_HOME/usr/bin/false root" "$(cat $STDOUTF)" - CHROOT=false CLASSIC_CHROOT=false assertCommandSuccess chroot_cmd root - assertEquals "ld_exec $JUNEST_HOME/usr/bin/chroot root" "$(cat $STDOUTF)" - - CHROOT=false CLASSIC_CHROOT=false LD_EXEC=false assertCommandFail chroot_cmd root + CLASSIC_CHROOT=false LD_EXEC=false assertCommandFail chroot_cmd root } function test_proot_cmd_compat(){ @@ -143,7 +176,7 @@ function test_copy_passwd_and_group(){ getent_cmd_mock() { echo $@ } - GETENT=getent_cmd_mock assertCommandSuccess _copy_passwd_and_group + GETENT=getent_cmd_mock assertCommandSuccess copy_passwd_and_group assertEquals "$(echo -e "passwd\npasswd $USER")" "$(cat $JUNEST_HOME/etc/passwd)" assertEquals "group" "$(cat $JUNEST_HOME/etc/group)" } @@ -152,20 +185,31 @@ function test_copy_passwd_and_group_fallback(){ cp_cmd_mock() { echo $@ } - CP=cp_cmd_mock GETENT=false LD_EXEC=false assertCommandSuccess _copy_passwd_and_group + CP=cp_cmd_mock GETENT=false LD_EXEC=false assertCommandSuccess copy_passwd_and_group assertEquals "$(echo -e "/etc/passwd $JUNEST_HOME//etc/passwd\n/etc/group $JUNEST_HOME//etc/group")" "$(cat $STDOUTF)" } function test_copy_passwd_and_group_failure(){ - CP=false GETENT=false LD_EXEC=false assertCommandFailOnStatus 1 _copy_passwd_and_group + CP=false GETENT=false LD_EXEC=false assertCommandFailOnStatus 1 copy_passwd_and_group } function test_nested_env(){ - JUNEST_ENV=1 assertCommandFailOnStatus 106 bash -c "source $JUNEST_ROOT/lib/utils/utils.sh; source $JUNEST_ROOT/lib/core/common.sh" + JUNEST_ENV=1 assertCommandFailOnStatus 106 check_nested_env } function test_nested_env_not_set_variable(){ - JUNEST_ENV=aaa assertCommandFailOnStatus 107 bash -c "source $JUNEST_ROOT/lib/utils/utils.sh; source $JUNEST_ROOT/lib/core/common.sh" + JUNEST_ENV=aaa assertCommandFailOnStatus 107 check_nested_env } +function test_check_same_arch_not_same(){ + echo "JUNEST_ARCH=XXX" > ${JUNEST_HOME}/etc/junest/info + assertCommandFailOnStatus 104 check_same_arch +} + +function test_check_same_arch(){ + echo "JUNEST_ARCH=$ARCH" > ${JUNEST_HOME}/etc/junest/info + assertCommandSuccess check_same_arch +} + + source $JUNEST_ROOT/tests/utils/shunit2 diff --git a/tests/unit-tests/test-groot.sh b/tests/unit-tests/test-groot.sh new file mode 100755 index 0000000..c7b5f36 --- /dev/null +++ b/tests/unit-tests/test-groot.sh @@ -0,0 +1,161 @@ +#!/bin/bash +source "$(dirname $0)/../utils/utils.sh" + +JUNEST_BASE="$(readlink -f $(dirname $(readlink -f "$0"))/../..)" + +# Disable the exiterr +set +e + +function oneTimeSetUp(){ + setUpUnitTests +} + +function setUp(){ + # Attempt to source the files under test to revert variable overrides + source $JUNEST_BASE/bin/groot -h &> /dev/null + set +e + + cwdSetUp + mkdir -p chrootdir + + init_mocks +} + +function tearDown(){ + cwdTearDown +} + +## Mock functions ## +function init_mocks() { + function usage(){ + echo "usage" + } + function is_user_root() { + return 0 + } + function chroot() { + echo "chroot($@)" + } + function mountpoint() { + echo "mountpoint($@)" + # As default suppose the mountpoint does not exist + return 1 + } + function mount() { + echo "mount($@)" + } + function umount() { + echo "umount($@)" + } + function check_and_trap() { + echo "check_and_trap($@)" + } +} + +function test_help(){ + assertCommandSuccess main -h + assertEquals "usage" "$(cat $STDOUTF)" + assertCommandSuccess main --help + assertEquals "usage" "$(cat $STDOUTF)" +} +function test_version(){ + assertCommandSuccess main -V + assertEquals "$NAME $(cat $JUNEST_BASE/VERSION)" "$(cat $STDOUTF)" + assertCommandSuccess main --version + assertEquals "$NAME $(cat $JUNEST_BASE/VERSION)" "$(cat $STDOUTF)" +} +function test_groot_no_root(){ + is_user_root() { + return 1 + } + assertCommandFailOnStatus $NO_ROOT_PRIVILEGES main +} +function test_groot_no_directory(){ + assertCommandFailOnStatus $NOT_EXISTING_FILE main no-directory +} +function test_groot_mountpoint_exist(){ + mountpoint_mock() { + echo "mountpoint($@)" + } + MOUNTPOINT=mountpoint_mock + assertCommandSuccess main chrootdir + assertEquals "$(echo -e "check_and_trap(chroot_teardown QUIT EXIT ABRT KILL TERM INT)\nmountpoint(-q chrootdir)\nchroot(chrootdir)")" "$(cat $STDOUTF)" +} +function test_groot_mountpoint_does_not_exist(){ + assertCommandSuccess main chrootdir + assertEquals "$(echo -e "check_and_trap(chroot_teardown QUIT EXIT ABRT KILL TERM INT)\nmountpoint(-q chrootdir)\nmount(--bind chrootdir chrootdir)\nchroot(chrootdir)")" "$(cat $STDOUTF)" +} +function test_groot_with_bind(){ + assertCommandSuccess main -b /tmp chrootdir + [[ -d chrootdir/tmp ]] + assertEquals 0 $? + assertEquals "$(echo -e "check_and_trap(chroot_teardown QUIT EXIT ABRT KILL TERM INT)\nmountpoint(-q chrootdir)\nmount(--bind chrootdir chrootdir)\nmount(--rbind /tmp chrootdir/tmp)\nchroot(chrootdir)")" "$(cat $STDOUTF)" +} +function test_groot_with_bind_file(){ + touch file_src + assertCommandSuccess main -b ${PWD}/file_src:/file_src chrootdir + [[ -f chrootdir/file_src ]] + assertEquals 0 $? + assertEquals "$(echo -e "check_and_trap(chroot_teardown QUIT EXIT ABRT KILL TERM INT)\nmountpoint(-q chrootdir)\nmount(--bind chrootdir chrootdir)\nmount(--rbind ${PWD}/file_src chrootdir/file_src)\nchroot(chrootdir)")" "$(cat $STDOUTF)" +} +function test_groot_with_bind_not_existing_node(){ + assertCommandFailOnStatus $NOT_EXISTING_FILE main -b ${PWD}/file_src:/file_src chrootdir + assertEquals "$(echo -e "check_and_trap(chroot_teardown QUIT EXIT ABRT KILL TERM INT)\nmountpoint(-q chrootdir)\nmount(--bind chrootdir chrootdir)")" "$(cat $STDOUTF)" +} +function test_groot_with_bind_not_absolute_path_node(){ + touch file_src + assertCommandFailOnStatus $NOT_ABSOLUTE_PATH main -b file_src:/file_src chrootdir + assertEquals "$(echo -e "check_and_trap(chroot_teardown QUIT EXIT ABRT KILL TERM INT)\nmountpoint(-q chrootdir)\nmount(--bind chrootdir chrootdir)")" "$(cat $STDOUTF)" +} +function test_groot_with_bind_guest_host(){ + assertCommandSuccess main -b /tmp:/home/tmp chrootdir + [[ -d chrootdir/home/tmp ]] + assertEquals 0 $? + assertEquals "$(echo -e "check_and_trap(chroot_teardown QUIT EXIT ABRT KILL TERM INT)\nmountpoint(-q chrootdir)\nmount(--bind chrootdir chrootdir)\nmount(--rbind /tmp chrootdir/home/tmp)\nchroot(chrootdir)")" "$(cat $STDOUTF)" +} +function test_groot_with_multiple_bind(){ + assertCommandSuccess main -b /tmp:/home/tmp -b /dev chrootdir + [[ -d chrootdir/home/tmp ]] + assertEquals 0 $? + [[ -d chrootdir/dev ]] + assertEquals 0 $? + assertEquals "$(echo -e "check_and_trap(chroot_teardown QUIT EXIT ABRT KILL TERM INT)\nmountpoint(-q chrootdir)\nmount(--bind chrootdir chrootdir)\nmount(--rbind /tmp chrootdir/home/tmp)\nmount(--rbind /dev chrootdir/dev)\nchroot(chrootdir)")" "$(cat $STDOUTF)" +} +function test_groot_with_command(){ + assertCommandSuccess main chrootdir ls -la -h + assertEquals "$(echo -e "check_and_trap(chroot_teardown QUIT EXIT ABRT KILL TERM INT)\nmountpoint(-q chrootdir)\nmount(--bind chrootdir chrootdir)\nchroot(chrootdir ls -la -h)")" "$(cat $STDOUTF)" +} +function test_groot_with_bind_and_command(){ + assertCommandSuccess main -b /tmp:/home/tmp -b /dev chrootdir ls -la -h + [[ -d chrootdir/home/tmp ]] + assertEquals 0 $? + [[ -d chrootdir/dev ]] + assertEquals 0 $? + assertEquals "$(echo -e "check_and_trap(chroot_teardown QUIT EXIT ABRT KILL TERM INT)\nmountpoint(-q chrootdir)\nmount(--bind chrootdir chrootdir)\nmount(--rbind /tmp chrootdir/home/tmp)\nmount(--rbind /dev chrootdir/dev)\nchroot(chrootdir ls -la -h)")" "$(cat $STDOUTF)" +} +function test_groot_with_bind_no_umount(){ + assertCommandSuccess main -n chrootdir + assertEquals "$(echo -e "mountpoint(-q chrootdir)\nmount(--bind chrootdir chrootdir)\nchroot(chrootdir)")" "$(cat $STDOUTF)" +} +function test_groot_with_chroot_teardown(){ + echo -e "1 /home/mychroot/dev\n1 /home/mychroot/proc/fs1\n1 /home/mychroot\n1 /home/mychroot-no/dev\n1 /home/mychroot/dev/shm\n1 /home/mychroot/proc\n" > ./mounts + MOUNTS_FILE=./mounts + CHROOTDIR=/home/mychroot assertCommandSuccess chroot_teardown + assertEquals "$(echo -e "umount(/home/mychroot/proc/fs1) +umount(/home/mychroot/proc) +umount(/home/mychroot/dev/shm) +umount(/home/mychroot/dev) +umount(/home/mychroot)")" "$(cat $STDOUTF)" +} +function test_groot_with_chroot_teardown_with_trailing_slash(){ + echo -e "1 /home/mychroot/dev\n1 /home/mychroot/proc/fs1\n1 /home/mychroot\n1 /home/mychroot-no/dev\n1 /home/mychroot/dev/shm\n1 /home/mychroot/proc\n" > ./mounts + MOUNTS_FILE=./mounts + CHROOTDIR=/home/mychroot assertCommandSuccess chroot_teardown + assertEquals "$(echo -e "umount(/home/mychroot/proc/fs1) +umount(/home/mychroot/proc) +umount(/home/mychroot/dev/shm) +umount(/home/mychroot/dev) +umount(/home/mychroot)")" "$(cat $STDOUTF)" +} + +source $(dirname $0)/../utils/shunit2 diff --git a/tests/unit-tests/test-junest.sh b/tests/unit-tests/test-junest.sh new file mode 100755 index 0000000..2176cea --- /dev/null +++ b/tests/unit-tests/test-junest.sh @@ -0,0 +1,189 @@ +#!/bin/bash +source "$(dirname $0)/../utils/utils.sh" + +JUNEST_BASE="$(dirname $0)/../.." +source $JUNEST_BASE/bin/junest -h &> /dev/null + +# Disable the exiterr +set +e + +function oneTimeSetUp(){ + setUpUnitTests +} + +function setUp(){ + function is_env_installed(){ + return 0 + } +} + +## Mock functions ## +function usage(){ + echo "usage" +} +function version(){ + echo "version" +} +function build_image_env(){ + local disable_validation=$1 + echo "build_image_env($disable_validation)" +} +function delete_env(){ + echo "delete_env" +} +function setup_env_from_file(){ + echo "setup_env_from_file($1)" +} +function setup_env(){ + echo "setup_env($1)" +} +function run_env_as_fakeroot(){ + local backend_args="$1" + shift + echo "run_env_as_fakeroot($backend_args,$@)" +} +function run_env_as_groot(){ + echo "run_env_as_groot $@" +} +function run_env_as_chroot(){ + echo "run_env_as_chroot $@" +} +function run_env_as_user(){ + local backend_args="$1" + shift + echo "run_env_as_user($backend_args,$@)" +} +function run_env_with_namespace(){ + local backend_args="$1" + shift + echo "run_env_with_namespace($backend_args,$@)" +} + +function test_help(){ + assertCommandSuccess main -h + assertEquals "usage" "$(cat $STDOUTF)" + assertCommandSuccess main --help + assertEquals "usage" "$(cat $STDOUTF)" +} +function test_version(){ + assertCommandSuccess main -V + assertEquals "version" "$(cat $STDOUTF)" + assertCommandSuccess main --version + assertEquals "version" "$(cat $STDOUTF)" +} +function test_build_image_env(){ + assertCommandSuccess main -b + assertEquals "build_image_env(false)" "$(cat $STDOUTF)" + assertCommandSuccess main --build-image + assertEquals "build_image_env(false)" "$(cat $STDOUTF)" + assertCommandSuccess main -b -n + assertEquals "build_image_env(true)" "$(cat $STDOUTF)" + assertCommandSuccess main --build-image --disable-validation + assertEquals "build_image_env(true)" "$(cat $STDOUTF)" +} +function test_delete_env(){ + assertCommandSuccess main -d + assertEquals "delete_env" "$(cat $STDOUTF)" + assertCommandSuccess main --delete + assertEquals "delete_env" "$(cat $STDOUTF)" +} +function test_setup_env_from_file(){ + is_env_installed(){ + return 1 + } + assertCommandSuccess main -i myimage + assertEquals "$(echo -e "setup_env_from_file(myimage)\nrun_env_as_user(,)")" "$(cat $STDOUTF)" + assertCommandSuccess main --setup-from-file myimage + assertEquals "$(echo -e "setup_env_from_file(myimage)\nrun_env_as_user(,)")" "$(cat $STDOUTF)" + + is_env_installed(){ + return 0 + } + assertCommandFail main -i myimage +} + +function test_setup_env(){ + is_env_installed(){ + return 1 + } + assertCommandSuccess main -a arm + assertEquals "$(echo -e "setup_env(arm)\nrun_env_as_user(,)")" "$(cat $STDOUTF)" + assertCommandSuccess main --arch arm + assertEquals "$(echo -e "setup_env(arm)\nrun_env_as_user(,)")" "$(cat $STDOUTF)" + assertCommandSuccess main + assertEquals "$(echo -e "setup_env()\nrun_env_as_user(,)")" "$(cat $STDOUTF)" + + is_env_installed(){ + return 0 + } + assertCommandFail main -a arm +} +function test_run_env_as_fakeroot(){ + assertCommandSuccess main -f + assertEquals "run_env_as_fakeroot(,)" "$(cat $STDOUTF)" + assertCommandSuccess main --fakeroot + assertEquals "run_env_as_fakeroot(,)" "$(cat $STDOUTF)" + + assertCommandSuccess main -f -p "-b arg" + assertEquals "run_env_as_fakeroot(-b arg,)" "$(cat $STDOUTF)" + assertCommandSuccess main -f -p "-b arg" -- command -kv + assertEquals "run_env_as_fakeroot(-b arg,command -kv)" "$(cat $STDOUTF)" + assertCommandSuccess main -f command --as + assertEquals "run_env_as_fakeroot(,command --as)" "$(cat $STDOUTF)" + assertCommandFail main -a "myarch" -f command --as +} +function test_run_env_as_user(){ + assertCommandSuccess main + assertEquals "run_env_as_user(,)" "$(cat $STDOUTF)" + + assertCommandSuccess main -p "-b arg" + assertEquals "run_env_as_user(-b arg,)" "$(cat $STDOUTF)" + assertCommandSuccess main -p "-b arg" -- command -ll + assertEquals "run_env_as_user(-b arg,command -ll)" "$(cat $STDOUTF)" + assertCommandSuccess main command -ls + assertEquals "run_env_as_user(,command -ls)" "$(cat $STDOUTF)" + + assertCommandFail main -a "myarch" -- command -ls +} +function test_run_env_as_groot(){ + assertCommandSuccess main -g + assertEquals "run_env_as_groot " "$(cat $STDOUTF)" + assertCommandSuccess main -g command + assertEquals "run_env_as_groot command" "$(cat $STDOUTF)" +} +function test_run_env_as_chroot(){ + assertCommandSuccess main -r + assertEquals "run_env_as_chroot " "$(cat $STDOUTF)" + assertCommandSuccess main -r command + assertEquals "run_env_as_chroot command" "$(cat $STDOUTF)" +} + +function test_run_env_with_namespace(){ + assertCommandSuccess main -u -f + assertEquals "run_env_with_namespace(,)" "$(cat $STDOUTF)" + assertCommandSuccess main --namespace --fakeroot + assertEquals "run_env_with_namespace(,)" "$(cat $STDOUTF)" + + assertCommandSuccess main -u -f -p "-b arg" + assertEquals "run_env_with_namespace(-b arg,)" "$(cat $STDOUTF)" + assertCommandSuccess main -u -f -p "-b arg" -- command -kv + assertEquals "run_env_with_namespace(-b arg,command -kv)" "$(cat $STDOUTF)" + assertCommandSuccess main -u -f command --as + assertEquals "run_env_with_namespace(,command --as)" "$(cat $STDOUTF)" +} + +function test_check_cli(){ + assertCommandFail main -b -h + assertCommandFail main -b -c + assertCommandFail main -d -s + assertCommandFail main -n -v + assertCommandFail main -d -r + assertCommandFail main -h -f + assertCommandFail main -v -i fsd + assertCommandFail main -f -r + assertCommandFail main -p args -v + assertCommandFail main -a arch -v + assertCommandFail main -d args +} + +source $(dirname $0)/../utils/shunit2 diff --git a/tests/unit-tests/test-namespace.sh b/tests/unit-tests/test-namespace.sh new file mode 100755 index 0000000..9cc60a7 --- /dev/null +++ b/tests/unit-tests/test-namespace.sh @@ -0,0 +1,113 @@ +#!/bin/bash + +JUNEST_ROOT=$(readlink -f $(dirname $0)/../..) + +source "$JUNEST_ROOT/tests/utils/utils.sh" + +source "$JUNEST_ROOT/lib/utils/utils.sh" + +# Disable the exiterr +set +e + +function oneTimeSetUp(){ + setUpUnitTests +} + +## Mock functions ## +function init_mocks() { + function unshare_cmd(){ + echo "unshare $@" + } +} + +function setUp(){ + cwdSetUp + junestSetUp + + # Attempt to source the files under test to revert variable + # overrides (i.e. SH variable) + source "$JUNEST_ROOT/lib/core/common.sh" + source "$JUNEST_ROOT/lib/core/namespace.sh" + set +e + + init_mocks +} + +function tearDown(){ + junestTearDown + cwdTearDown +} + +function _test_copy_common_files() { + [[ -e /etc/hosts ]] && assertEquals "$(cat /etc/hosts)" "$(cat ${JUNEST_HOME}/etc/hosts)" + [[ -e /etc/host.conf ]] && assertEquals "$(cat /etc/host.conf)" "$(cat ${JUNEST_HOME}/etc/host.conf)" + [[ -e /etc/nsswitch.conf ]] && assertEquals "$(cat /etc/nsswitch.conf)" "$(cat ${JUNEST_HOME}/etc/nsswitch.conf)" + [[ -e /etc/resolv.conf ]] && assertEquals "$(cat /etc/resolv.conf)" "$(cat ${JUNEST_HOME}/etc/resolv.conf)" +} + +function _test_copy_remaining_files() { + [[ -e /etc/hosts.equiv ]] && assertEquals "$(cat /etc/hosts.equiv)" "$(cat ${JUNEST_HOME}/etc/hosts.equiv)" + [[ -e /etc/netgroup ]] && assertEquals "$(cat /etc/netgroup)" "$(cat ${JUNEST_HOME}/etc/netgroup)" + [[ -e /etc/networks ]] && assertEquals "$(cat /etc/networks)" "$(cat ${JUNEST_HOME}/etc/networks)" + + [[ -e ${JUNEST_HOME}/etc/passwd ]] + assertEquals 0 $? + [[ -e ${JUNEST_HOME}/etc/group ]] + assertEquals 0 $? +} + +function test_is_user_namespace_enabled_no_config_file(){ + CONFIG_PROC_FILE="blah" + CONFIG_BOOT_FILE="blah" + assertCommandFailOnStatus $NOT_EXISTING_FILE _is_user_namespace_enabled +} + +function test_is_user_namespace_enabled_no_config(){ + touch config + gzip config + CONFIG_PROC_FILE="config.gz" + CONFIG_BOOT_FILE="blah" + assertCommandFailOnStatus $NO_CONFIG_FOUND _is_user_namespace_enabled +} + +function test_is_user_namespace_enabled_with_config(){ + echo "CONFIG_USER_NS=y" > config + gzip config + CONFIG_PROC_FILE="config.gz" + CONFIG_BOOT_FILE="blah" + assertCommandSuccess _is_user_namespace_enabled +} + +function test_run_env_with_namespace() { + assertCommandSuccess run_env_with_namespace "" "" + assertEquals "unshare --mount --user --map-root-user $GROOT -n -b $HOME -b /tmp -b /proc -b /sys -b /dev $JUNEST_HOME /bin/sh --login" "$(cat $STDOUTF)" + + _test_copy_common_files + _test_copy_remaining_files +} + +function test_run_env_with_namespace_with_bindings() { + assertCommandSuccess run_env_with_namespace "-b /usr -b /lib:/tmp/lib" "" + assertEquals "unshare --mount --user --map-root-user $GROOT -n -b $HOME -b /tmp -b /proc -b /sys -b /dev -b /usr -b /lib:/tmp/lib $JUNEST_HOME /bin/sh --login" "$(cat $STDOUTF)" + + _test_copy_common_files + _test_copy_remaining_files +} + +function test_run_env_with_namespace_with_command() { + assertCommandSuccess run_env_with_namespace "" "ls -la" + assertEquals "unshare --mount --user --map-root-user $GROOT -n -b $HOME -b /tmp -b /proc -b /sys -b /dev $JUNEST_HOME /bin/sh --login -c \"ls -la\"" "$(cat $STDOUTF)" + + _test_copy_common_files + _test_copy_remaining_files +} + +function test_run_env_with_namespace_with_bindings_and_command() { + assertCommandSuccess run_env_with_namespace "-b /usr -b /lib:/tmp/lib" "ls -la" + assertEquals "unshare --mount --user --map-root-user $GROOT -n -b $HOME -b /tmp -b /proc -b /sys -b /dev -b /usr -b /lib:/tmp/lib $JUNEST_HOME /bin/sh --login -c \"ls -la\"" "$(cat $STDOUTF)" + + _test_copy_common_files + _test_copy_remaining_files +} + +source $JUNEST_ROOT/tests/utils/shunit2 diff --git a/tests/unit-tests/test-proot.sh b/tests/unit-tests/test-proot.sh index fdc83b2..6640035 100755 --- a/tests/unit-tests/test-proot.sh +++ b/tests/unit-tests/test-proot.sh @@ -29,6 +29,24 @@ function tearDown(){ cwdTearDown } +function _test_copy_common_files() { + [[ -e /etc/hosts ]] && assertEquals "$(cat /etc/hosts)" "$(cat ${JUNEST_HOME}/etc/hosts)" + [[ -e /etc/host.conf ]] && assertEquals "$(cat /etc/host.conf)" "$(cat ${JUNEST_HOME}/etc/host.conf)" + [[ -e /etc/nsswitch.conf ]] && assertEquals "$(cat /etc/nsswitch.conf)" "$(cat ${JUNEST_HOME}/etc/nsswitch.conf)" + [[ -e /etc/resolv.conf ]] && assertEquals "$(cat /etc/resolv.conf)" "$(cat ${JUNEST_HOME}/etc/resolv.conf)" +} + +function _test_copy_remaining_files() { + [[ -e /etc/hosts.equiv ]] && assertEquals "$(cat /etc/hosts.equiv)" "$(cat ${JUNEST_HOME}/etc/hosts.equiv)" + [[ -e /etc/netgroup ]] && assertEquals "$(cat /etc/netgroup)" "$(cat ${JUNEST_HOME}/etc/netgroup)" + [[ -e /etc/networks ]] && assertEquals "$(cat /etc/networks)" "$(cat ${JUNEST_HOME}/etc/networks)" + + [[ -e ${JUNEST_HOME}/etc/passwd ]] + assertEquals 0 $? + [[ -e ${JUNEST_HOME}/etc/group ]] + assertEquals 0 $? +} + function test_run_env_as_user(){ _run_env_with_qemu() { echo $@ @@ -40,19 +58,8 @@ function test_run_env_as_user(){ assertCommandSuccess run_env_as_user "-k 3.10" assertEquals "-b $HOME -b /tmp -b /proc -b /sys -b /dev -r ${JUNEST_HOME} -k 3.10" "$(cat $STDOUTF)" - [[ -e /etc/hosts ]] && assertEquals "$(cat /etc/hosts)" "$(cat ${JUNEST_HOME}/etc/hosts)" - [[ -e /etc/host.conf ]] && assertEquals "$(cat /etc/host.conf)" "$(cat ${JUNEST_HOME}/etc/host.conf)" - [[ -e /etc/nsswitch.conf ]] && assertEquals "$(cat /etc/nsswitch.conf)" "$(cat ${JUNEST_HOME}/etc/nsswitch.conf)" - [[ -e /etc/resolv.conf ]] && assertEquals "$(cat /etc/resolv.conf)" "$(cat ${JUNEST_HOME}/etc/resolv.conf)" - - [[ -e /etc/hosts.equiv ]] && assertEquals "$(cat /etc/hosts.equiv)" "$(cat ${JUNEST_HOME}/etc/hosts.equiv)" - [[ -e /etc/netgroup ]] && assertEquals "$(cat /etc/netgroup)" "$(cat ${JUNEST_HOME}/etc/netgroup)" - - [[ -e /etc/passwd ]] - assertEquals 0 $? - [[ -e /etc/group ]] - assertEquals 0 $? - + _test_copy_common_files + _test_copy_remaining_files } function test_run_env_as_fakeroot(){ @@ -66,10 +73,7 @@ function test_run_env_as_fakeroot(){ assertCommandSuccess run_env_as_fakeroot "-k 3.10" assertEquals "-0 -b ${HOME} -b /tmp -b /proc -b /sys -b /dev -r ${JUNEST_HOME} -k 3.10" "$(cat $STDOUTF)" - [[ -e /etc/hosts ]] && assertEquals "$(cat /etc/hosts)" "$(cat ${JUNEST_HOME}/etc/hosts)" - [[ -e /etc/host.conf ]] && assertEquals "$(cat /etc/host.conf)" "$(cat ${JUNEST_HOME}/etc/host.conf)" - [[ -e /etc/nsswitch.conf ]] && assertEquals "$(cat /etc/nsswitch.conf)" "$(cat ${JUNEST_HOME}/etc/nsswitch.conf)" - [[ -e /etc/resolv.conf ]] && assertEquals "$(cat /etc/resolv.conf)" "$(cat ${JUNEST_HOME}/etc/resolv.conf)" + _test_copy_common_files } function test_run_env_with_quotes(){ diff --git a/tests/unit-tests/test-utils.sh b/tests/unit-tests/test-utils.sh index 8a5a127..c0b1ebf 100755 --- a/tests/unit-tests/test-utils.sh +++ b/tests/unit-tests/test-utils.sh @@ -79,6 +79,28 @@ function test_ask_wrong_default_answer() { assertEquals 33 $? } +function test_check_and_trap_fail() { + trap echo EXIT + trap ls QUIT + assertCommandFailOnStatus 1 check_and_trap 'pwd' EXIT QUIT +} + +function test_check_and_trap() { + trap - EXIT QUIT + assertCommandSuccess check_and_trap 'echo' EXIT QUIT +} + +function test_check_and_force_trap_fail() { + trap echo EXIT + trap ls QUIT + assertCommandSuccess check_and_force_trap 'echo' EXIT QUIT +} + +function test_check_and_force_trap() { + trap - EXIT QUIT + assertCommandSuccess check_and_force_trap 'echo' EXIT QUIT +} + function test_insert_quotes_on_spaces(){ assertCommandSuccess insert_quotes_on_spaces this is "a test" assertEquals "this is \"a test\"" "$(cat $STDOUTF)" diff --git a/tests/utils/utils.sh b/tests/utils/utils.sh index b96000c..ed7cb8c 100644 --- a/tests/utils/utils.sh +++ b/tests/utils/utils.sh @@ -1,3 +1,4 @@ +OLD_CWD=${PWD} function cwdSetUp(){ ORIGIN_CWD=$(TMPDIR=/tmp mktemp -d -t junest-cwd.XXXXXXXXXX) cd $ORIGIN_CWD @@ -5,6 +6,7 @@ function cwdSetUp(){ function cwdTearDown(){ rm -rf $ORIGIN_CWD + cd $OLD_CWD } function junestSetUp(){ @@ -12,15 +14,12 @@ function junestSetUp(){ mkdir -p ${JUNEST_HOME}/etc/junest echo "JUNEST_ARCH=x86_64" > ${JUNEST_HOME}/etc/junest/info mkdir -p ${JUNEST_HOME}/etc/ca-certificates - trap - QUIT EXIT ABRT KILL TERM INT - trap "rm -rf ${JUNEST_HOME}" EXIT QUIT ABRT KILL TERM INT } function junestTearDown(){ # the CA directories are read only and can be deleted only by changing the mod [ -d ${JUNEST_HOME}/etc/ca-certificates ] && chmod -R +w ${JUNEST_HOME}/etc/ca-certificates rm -rf $JUNEST_HOME - trap - QUIT EXIT ABRT KILL TERM INT unset JUNEST_HOME }