diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..8efae1f --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,2 @@ +github: fsquillace +custom: https://github.com/fsquillace/junest/blob/master/README.md#donating diff --git a/.gitignore b/.gitignore index 2d39c41..b47a408 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ *.swp -*pkg.tar.xz +*pkg.tar.* *.tar.gz *.SRCINFO diff --git a/.travis.yml b/.travis.yml index dceb835..fab23ba 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,31 +1,62 @@ -language: bash - sudo: required +os: linux + +cache: + directories: + - ~/.ccache + - ~/.pkg-cache + +services: +- docker env: + matrix: - TRAVIS_BASH_VERSION="4.0" before_install: - - ./tests/integ-tests/install-bash.sh "$TRAVIS_BASH_VERSION" + - ./ci/install-bash.sh "$TRAVIS_BASH_VERSION" + - sudo apt-get update + - sudo apt-get -y install awscli install: - PATH=$PWD/bin:$PATH - - junest setup - - junest -- echo "Installing JuNest (\$(uname -m))" - - JUNEST_HOME=~/.junest-arm junest setup --arch arm - - JUNEST_HOME=~/.junest-arm junest proot --fakeroot -- echo "Installing JuNest (\$(uname -m))" script: + ####################### + # Unit Tests + ####################### - bash --version - bash ./tests/checkstyle/checkstyle.sh - bash ./tests/unit-tests/unit-tests.sh + - shellcheck bin/junest lib/**/*.sh ci/*.sh tests/**/*.sh - # Multiple tests against different execution modes: - - junest proot --fakeroot -- ${PWD}/lib/checks/check.sh - - junest ns -- ${PWD}/lib/checks/check.sh - - sudo -E ${PWD}/bin/junest groot -- ${PWD}/lib/checks/check.sh --run-root-tests + # ARM with qemu does seem to work properly. Disabling integ tests for ARM for now. + #- export JUNEST_HOME=~/.junest-arm + #- junest setup --arch arm + #- junest proot --fakeroot -- echo "Installing JuNest (\$(uname -m))" + #- junest proot --fakeroot -- ${PWD}/lib/checks/check.sh --skip-aur-tests + #- junest proot -- ${PWD}/lib/checks/check.sh --skip-aur-tests --use-sudo + #- yes | junest setup --delete + + ####################### + # Build and validation + ####################### + - echo "$DOCKER_PASSWORD" | docker login --username "$DOCKER_USERNAME" --password-stdin + - docker run --rm -v "$(pwd):/build" -v ~/.ccache:/home/travis/.ccache -v ~/.pkg-cache:/var/cache/pacman/pkg --privileged archlinux:latest bash /build/ci/build_image.sh + + - "echo pacman pkg cache size: $(du -h ~/.pkg-cache|cut -f1) in $(ls ~/.pkg-cache|wc -l) files" + - ls -l + # Test the newly created JuNest image against Ubuntu host + - export JUNEST_HOME=~/.junest + - junest setup -i junest-x86_64.tar.gz + # TODO The check does not work at the moment: https://app.travis-ci.com/github/fsquillace/junest/builds/271706037 + # Disabling it in order to avoid having stale version of junest images. + - ${PWD}/lib/checks/check_all.sh - yes | junest setup --delete - # Disable arm because it fails when exiting from check.sh for apparent no reason - #- JUNEST_HOME=~/.junest-arm junest proot -f -- ./lib/checks/check.sh --skip-aur-tests - #- yes | JUNEST_HOME=~/.junest-arm junest setup --delete + +after_success: + ####################### + # Deploy and validation + ####################### + - ./ci/deploy.sh ./junest-x86_64.tar.gz diff --git a/README.md b/README.md index 856a9a8..1268f8b 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,13 @@ JuNest ====== -The lightweight Arch Linux based distro that runs upon any Linux distros without root access. + +> [!IMPORTANT] +> Starting from Ubuntu 23.10+, [unprivileged user namespaces has been restricted](https://ubuntu.com/blog/ubuntu-23-10-restricted-unprivileged-user-namespaces). +> If using JuNest within Ubuntu, you may need root privileges in order to enable it. +> Alternatively, you can access JuNest using the `proot` mode as described +> [below](#Proot-based). + +The lightweight Arch Linux based distro that runs, without root privileges, on top of any other Linux distro.

=4.0)](https://www.gnu.org/software/bash/) - [GNU coreutils](https://www.gnu.org/software/coreutils/) -The minimum recommended Linux kernel of the host OS is 2.6.32 on x86 (64 bit) -and ARM architectures. It is still possible to run JuNest on lower -2.6.x host OS kernels but errors may appear, and some applications may -crash. For further information, read the [Troubleshooting](#troubleshooting) -section below. - - -## Method one (Recommended) ## +## Installation from git repository ## Just clone the JuNest repo somewhere (for example in ~/.local/share/junest): - git clone git://github.com/fsquillace/junest ~/.local/share/junest - export PATH=~/.local/share/junest/bin:$PATH +```sh +git clone https://github.com/fsquillace/junest.git ~/.local/share/junest +export PATH=~/.local/share/junest/bin:$PATH +``` + +Optionally you want to use the wrappers to run commands +installed in JuNest directly from host: + +```sh +export PATH="$PATH:~/.junest/usr/bin_wrappers" +``` +Update your `~/.bashrc` or `~/.zshrc` to get always the wrappers available. ### 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/packages/junest-git/). -After installing junest will be located in `/opt/junest/` +JuNest will be located in `/opt/junest/` -## Method two ## -Alternatively, another installation method would be to directly download the JuNest image and place it to the default directory `~/.junest`: +Quickstart +========== - ARCH= - mkdir ~/.junest - curl https://s3-eu-west-1.amazonaws.com/junest-repo/junest/junest-${ARCH}.tar.gz | tar -xz -C ~/.junest - export PATH=~/.junest/opt/junest/bin:$PATH +Setup environment +----------------- + +The first operation required is to install the JuNest environment in the +location of your choice via `JUNEST_HOME` environment variable +(it must contain an absolute path) which by +default is `~/.junest`: + +```sh +junest setup +``` + +The script will download the image from the repository and will place it to the default directory `~/.junest`. + +Access to environment +--------------------- + +JuNest uses the Linux namespaces (aka `ns`) as the default backend program. To access via `ns` just type: + +```sh +junest +``` + +You can use the command `sudo` to acquire fakeroot privileges and +install/remove packages. + +Alternatively, you can access fakeroot privileges without using `sudo` all the +time with the `-f` (or `--fakeroot`) option: + +```sh +junest -f +``` + +Another execution mode is via [Proot](https://wiki.archlinux.org/index.php/Proot): + +```sh +junest proot [-f] +``` + +There are multiple backend programs, each with its own pros/cons. +To know more about the JuNest execution modes depending on the backend program +used, see the [Usage](#usage) section below. + +Run JuNest installed programs directly from host OS +--------------------------------------- + +Programs installed within JuNest can be accessible directly from host machine +without entering into a JuNest session +(namely, no need to call `junest` command first). +For instance, supposing the host OS is an Ubuntu distro you can directly +run `pacman` by simply updating the `PATH` variable: + +```sh +export PATH="$PATH:~/.junest/usr/bin_wrappers" +sudoj pacman -S htop +htop +``` + +By default the wrappers use `ns` mode. To use the `ns --fakeroot` you can use the convenient command helper `sudoj`. +For more control on backend modes you can use the `JUNEST_ARGS` environment variable too. +For instance, if you want to run `iftop` with real root privileges: + +``` +sudoj pacman -S iftop +sudo JUNEST_ARGS="groot" iftop +``` + +Bin wrappers can be always recreated (e.g. in case for some reasons they get +corrupted) with: + +``` +junest create-bin-wrappers -f +``` + +Bin wrappers are automatically generated each time they get installed inside JuNest. +This only works for executables located in `/usr/bin` path. +For executables in other locations (say `/usr/mybinpath`) you can only create +wrappers manually by executing the command: + +``` +junest create-bin-wrappers --bin-path /usr/mybinpath +``` + +Obviously, to get access to the corresponding bin wrappers you will need to +update your `PATH` variable accordingly: + +``` +export PATH="$PATH:~/.junest/usr/mybinpath_wrappers" +``` + +Install packages from AUR +------------------------- + +In `ns` mode, you can easily install package from [AUR](https://aur.archlinux.org/) repository +using the already available [`yay`](https://aur.archlinux.org/packages/yay/) +command. In `proot` mode, JuNest does no longer support the building of AUR packages. + +**Remember** that in order to build packages from AUR, `base-devel` package group is required +first: + +```sh +pacman -S base-devel +``` + +JuNest uses a modified version of `sudo` provided by `junest/sudo-fake`. And the original `core/sudo` +package will be ignored **(and must not be installed)** during the installation of `base-devel`. + +Have fun! +--------- + +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). Usage ===== @@ -172,14 +240,18 @@ provides the state of the user namespace on several GNU/Linux distros. In order to run JuNest via Linux namespaces: -- As fakeroot - Allow to install/remove packages: `junest ns` or `junest` +- As normal user - Allow to make basic operations or install/remove packages +with `sudo` command: `junest ns` or `junest` +- As fakeroot - Allow to install/remove packages: `junest ns -f` or `junest -f` + +This mode is based on the fantastic +[`bubblewrap`](https://github.com/containers/bubblewrap) command. PRoot based ----------- [Proot](https://wiki.archlinux.org/index.php/Proot) 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. +solution which allows unprivileged users to execute programs inside a sandbox +and works well in most of GNU/Linux distros available. In order to run JuNest via Proot: @@ -187,13 +259,21 @@ In order to run JuNest via Proot: - As fakeroot - Allow to install/remove packages: `junest proot -f` +In `proot` mode, the minimum recommended Linux kernel for the host OS is 2.6.32 on x86 (64 bit) +and ARM architectures. It is still possible to run JuNest on lower +2.6.x host OS kernels but errors may appear, and some applications may +crash. For further information, read the [Troubleshooting](#troubleshooting) +section below. + 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` +In particular, it uses a special program called `GRoot`, a small and portable +version of +[arch-chroot](https://wiki.archlinux.org/index.php/Chroot) wrapper, that allows to bind mount directories specified by the user, such as -`/proc`, `/sys`, `/dev`, `/tmp` and `$HOME`, before +`/proc`, `/sys`, `/dev`, `/tmp`, `/run/user/` 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. @@ -210,8 +290,8 @@ The following table shows the capabilities that each backend program is able to | | QEMU | Root privileges required | Manage Official Packages | Manage AUR Packages | Portability | Support | User modes | | --- | ---- | ------------------------ | ------------------------ | ------------------- | ----------- | ------- | ---------- | -| **Linux Namespaces** | NO | NO | YES | YES | Poor | YES | `fakeroot` only | -| **Proot** | YES | NO | YES | YES | YES | Poor | Normal user and `fakeroot` | +| **Linux Namespaces** | NO | NO | YES | YES | Poor | YES | Normal user and `fakeroot` | +| **Proot** | YES | NO | YES | NO | YES | YES | Normal user and `fakeroot` | | **Chroot** | NO | YES | YES | YES | YES | YES | `root` only | Advanced usage @@ -257,13 +337,19 @@ armv7l ``` ## Bind directories ## -To bind a host directory to a guest location, you can use proot arguments: +To bind a host directory to a guest location: + +```sh +junest -b "--bind /home/user/mydata /mnt/mydata" +``` + +Or using proot arguments: ```sh junest proot -b "-b /mnt/mydata:/home/user/mydata" ``` -The option `-b` to provide options to the backeng program will work with PRoot, Namespace and GRoot backend programs. +The option `-b` to provide options to the backend program will work with PRoot, Namespace and GRoot backend programs. Check out the backend program options by passing `--help` option: ```sh @@ -291,21 +377,6 @@ Related wiki page: 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 -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 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. - ## Automatic fallback for all the dependent host OS executables ## JuNest attempts first to run the executables in the host OS located in different positions (`/usr/bin`, `/bin`, `/usr/sbin` and `/sbin`). @@ -313,10 +384,10 @@ As a fallback it tries to run the same executable if it is available in the JuNe environment. ## Automatic building of the JuNest images ## -There is not periodic automation build of the JuNest images yet. -This was due to the difficulty to automate builds for arm architecture. -The JuNest image for the `x86_64` is built periodically every once every three -months. +There is a periodic automation build of the JuNest images for `x86_64` arch +only. +The JuNest image for `arm` architecture may not be always up to date because +the build is performed manually. ## Static QEMU binaries ## There are static QEMU binaries included in JuNest image that allows to run JuNest @@ -338,9 +409,22 @@ For Arch Linux related FAQs take a look at the [General troubleshooting page](ht > In order to install AUR packages you need to install the package group `base-devel` first > that contains all the essential packages for compiling from source code (such as gcc, make, patch, etc): - #> pacman -S --ignore sudo base-devel + #> pacman -S base-devel -> Remember to ignore `sudo` as it conflicts with `sudo-fake` package. +> Remember to not install `core/sudo` as it conflicts with `junest/sudo-fake` package. + +## Can't set user and group as root + +> **Q**: In ns mode when installing package I get the following error: + + warning: warning given when extracting /usr/file... (Can't set user=0/group=0 for + /usr/file...) + +> **A**: This is because as fakeroot is not possible to set the owner/group of +> files as root. The package will still be installed correctly even though this +> message is showed. + +## Could not change the root directory in pacman ## No servers configured for repository ## @@ -450,7 +534,7 @@ For Arch Linux related FAQs take a look at the [General troubleshooting page](ht > have *suid* permission, you can list the commands from your JuNest environment > with the following command: - $> find /usr/bin -perm +4000 + $> find /usr/bin -perm /4000 ## No characters are visible on a graphic application ## @@ -518,6 +602,11 @@ Contributions are welcome! You could help improving JuNest in the following ways - [Suggesting Enhancements](CONTRIBUTING.md#suggesting-enhancements) - [Writing Code](CONTRIBUTING.md#your-first-code-contribution) +Donating +======== +To sustain the project please consider funding by donations through +the [GitHub Sponsors page](https://github.com/sponsors/fsquillace/). + Authors ======= JuNest was originally created in late 2014 by [Filippo Squillace (feel.sqoox@gmail.com)](https://github.com/fsquillace). diff --git a/VERSION b/VERSION index a8907c0..ef13716 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -7.0.2 +7.4.10 diff --git a/bin/groot b/bin/groot deleted file mode 100755 index cb4952f..0000000 --- a/bin/groot +++ /dev/null @@ -1,242 +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 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 -MKDIR=mkdir -TOUCH=touch -CUT=cut -SORT=sort -UNIQ=uniq -CAT=cat -READLINK=readlink -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 is_mountpoint() { - local mountpoint="$1" - for mp in $($CAT $MOUNTS_FILE | $CUT -f2 -d' ' | $SORT -r | $UNIQ) - do - [[ $mp == $mountpoint ]] && return 0 - done - return 1 -} - -function chroot_teardown() { - # Remove all mounts starting from the most nested ones. - # Suffix the CHROOTDIR with / to avoid umounting directories not belonging - # to CHROOTDIR. - local normalized_chrootdir="$($READLINK -f ${CHROOTDIR})/" - local final_res=0 - for mp in $($CAT $MOUNTS_FILE | $CUT -f2 -d' ' | $SORT -r | $UNIQ) - do - if [[ $mp =~ ^${normalized_chrootdir}.* ]] && is_mountpoint "$mp" - then - $UMOUNT $mp || final_res=$? - fi - done - $UMOUNT ${CHROOTDIR%/} - - return $final_res -} - -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 "! is_mountpoint '$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_directory "${host_path}" "${guest_path}" - done -} - -function mount_directory() { - local host_path=$($READLINK -f "$1") - local guest_path="$2" - - if ! $OPT_AVOID_BIND - then - $MOUNT $OPT_BIND "${host_path}" "${CHROOTDIR}${guest_path}" - return 0 - fi - - case "$host_path" in - /proc) $MOUNT proc "${CHROOTDIR}${guest_path}" -t proc ;; - /sys) $MOUNT sys "${CHROOTDIR}${guest_path}" -t sysfs ;; - /dev) $MOUNT udev "${CHROOTDIR}${guest_path}" -t devtmpfs; $MOUNT devpts "${guest_path}/pts" -t devpts; $MOUNT shm "${guest_path}/shm" -t tmpfs ;; - /run) $MOUNT run "${CHROOTDIR}${guest_path}" -t tmpfs ;; - /tmp) $MOUNT tmp "${CHROOTDIR}${guest_path}" -t tmpfs ;; - *) $MOUNT $OPT_BIND "${host_path}" "${CHROOTDIR}${guest_path}" ;; - esac - - return 0 -} - -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. - - -r, --recursive - Use rbind instead of bind. - - -i, --avoid-bind - Attempt to avoid mount --bind for common directories and use - proper mount fstype instead. Detected directories with - corresponding fstype are: /proc (proc), /sys (sysfs), - /dev (devtmpfs), /tmp (tmpfs), /run (tmpfs). - - -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() { - BINDINGS=() - OPT_NO_UMOUNT=false - OPT_RECURSIVE=false - OPT_BIND="--bind" - OPT_AVOID_BIND=false - OPT_HELP=false - OPT_VERSION=false - for opt in "$@" - do - case "$1" in - -b|--bind) shift ; BINDINGS+=("$1") ; shift ;; - -n|--no-umount) OPT_NO_UMOUNT=true ; shift ;; - -r|--recursive) OPT_BIND="--rbind" ; shift ;; - -i|--avoid-bind) OPT_AVOID_BIND=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/junest b/bin/junest index 8ea3b19..4084fe0 100755 --- a/bin/junest +++ b/bin/junest @@ -1,4 +1,5 @@ #!/usr/bin/env bash +# shellcheck disable=SC1091 # # This file is part of JuNest (https://github.com/fsquillace/junest). # @@ -7,7 +8,7 @@ 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"))/..)}" +JUNEST_BASE="${JUNEST_BASE:-$(readlink -f "$(dirname "$(readlink -f "$0")")"/..)}" source "${JUNEST_BASE}/lib/utils/utils.sh" source "${JUNEST_BASE}/lib/core/common.sh" @@ -17,13 +18,14 @@ source "${JUNEST_BASE}/lib/core/setup.sh" source "${JUNEST_BASE}/lib/core/chroot.sh" source "${JUNEST_BASE}/lib/core/namespace.sh" source "${JUNEST_BASE}/lib/core/proot.sh" +source "${JUNEST_BASE}/lib/core/wrappers.sh" ################################### ### General functions ### ################################### usage() { - echo -e "$NAME (v$(cat $JUNEST_BASE/VERSION)): $DESCRIPTION" + echo -e "$NAME (v$(cat "$JUNEST_BASE"/VERSION)): $DESCRIPTION" echo echo -e "Usage: $CMD [action] [options] [--] [command]" echo @@ -38,23 +40,28 @@ usage() { echo -e " Defaults to the host architecture ($ARCH)" echo -e " -d, --delete Delete $NAME from ${JUNEST_HOME}" echo - echo -e " n[s] Access via Linux Namespaces using GRoot (Default action)" - echo -e " -b, --backend-args Arguments for GRoot backend program" - echo -e " ($CMD groot -b \"--help\" to check out the GRoot options)" + echo -e " n[s] Access via Linux Namespaces using BubbleWrap (Default action)" + echo -e " -f, --fakeroot Run $NAME with fakeroot privileges" + echo -e " --backend-command Bwrap command to use" + echo -e " -b, --backend-args Arguments for bwrap backend program" + echo -e " ($CMD ns -b \"--help\" to check out the bwrap options)" echo -e " -n, --no-copy-files Do not copy common etc files into $NAME environment" echo echo -e " p[root] Access via PRoot" echo -e " -f, --fakeroot Run $NAME with fakeroot privileges" + echo -e " --backend-command PRoot command to use" echo -e " -b, --backend-args Arguments for PRoot backend program" echo -e " ($CMD proot -b \"--help\" to check out the PRoot options)" echo -e " -n, --no-copy-files Do not copy common etc files into $NAME environment" echo echo -e " g[root] Access with root privileges via GRoot" + echo -e " --backend-command GRoot command to use" echo -e " -b, --backend-args Arguments for GRoot backend program" echo -e " ($CMD groot -b \"--help\" to check out the GRoot options)" echo -e " -n, --no-copy-files Do not copy common etc files into $NAME environment" echo echo -e " r[oot] Access with root privileges via classic chroot" + echo -e " --backend-command Chroot command to use" echo -e " -b, --backend-args Arguments for chroot backend program" echo -e " ($CMD root -b \"--help\" to check out the chroot options)" echo -e " -n, --no-copy-files Do not copy common etc files into $NAME environment" @@ -62,16 +69,23 @@ usage() { echo -e " b[uild] Build a $NAME image (must run in ArchLinux)" echo -e " -n, --disable-check Disable the $NAME image check" echo + echo -e " create-bin-wrappers Create a bin wrappers directory according to --bin-path option" + echo -e " Default path is $JUNEST_HOME/usr/bin_wrappers" + echo -e " -f, --force Create the wrapper files even if they already exist" + echo -e " -p, --bin-path The source directory where executable are located in JuNest" + echo -e " Default value is: /usr/bin" + echo } version() { - echo -e "$NAME $(cat $JUNEST_BASE/VERSION)" + echo -e "$NAME $(cat "$JUNEST_BASE"/VERSION)" } function parse_arguments(){ # Actions ACT_SETUP=false ACT_BUILD=false + ACT_CREATE_WRAPPERS=false ACT_NAMESPACE=false ACT_PROOT=false ACT_GROOT=false @@ -82,6 +96,7 @@ function parse_arguments(){ case "$1" in s|setup) ACT_SETUP=true ; shift ;; b|build) ACT_BUILD=true ; shift ;; + create-bin-wrappers) ACT_CREATE_WRAPPERS=true ; shift ;; n|ns) ACT_NAMESPACE=true ; shift ;; p|proot) ACT_PROOT=true ; shift ;; g|groot) ACT_GROOT=true ; shift ;; @@ -97,6 +112,9 @@ function parse_arguments(){ elif $ACT_BUILD then _parse_build_opts "$@" + elif $ACT_CREATE_WRAPPERS + then + _parse_create_wrappers_opts "$@" elif $ACT_NAMESPACE then _parse_ns_opts "$@" @@ -114,15 +132,16 @@ function parse_arguments(){ function _parse_root_opts() { # Options: - OPT_BACKEND_ARGS=false BACKEND_ARGS="" OPT_NO_COPY_FILES=false + BACKEND_COMMAND="" while [[ -n "$1" ]] do case "$1" in - -b|--backend-args) OPT_BACKEND_ARGS=true ; shift ; BACKEND_ARGS=$1; shift ;; + -b|--backend-args) shift ; BACKEND_ARGS=$1; shift ;; -n|--no-copy-files) OPT_NO_COPY_FILES=true ; shift ;; + --backend-command) shift; BACKEND_COMMAND="$1"; shift ;; --) shift ; break ;; -*) die "Invalid option $1" ;; *) break ;; @@ -138,15 +157,18 @@ function _parse_root_opts() { function _parse_ns_opts() { # Options: - OPT_BACKEND_ARGS=false + OPT_FAKEROOT=false BACKEND_ARGS="" OPT_NO_COPY_FILES=false + BACKEND_COMMAND="" while [[ -n "$1" ]] do case "$1" in - -b|--backend-args) OPT_BACKEND_ARGS=true ; shift ; BACKEND_ARGS=$1; shift ;; + -f|--fakeroot) OPT_FAKEROOT=true ; shift ;; + -b|--backend-args) shift ; BACKEND_ARGS=$1; shift ;; -n|--no-copy-files) OPT_NO_COPY_FILES=true ; shift ;; + --backend-command) shift; BACKEND_COMMAND="$1"; shift ;; --) shift ; break ;; -*) die "Invalid option $1" ;; *) break ;; @@ -163,27 +185,24 @@ function _parse_ns_opts() { function _parse_proot_opts() { # Options: OPT_FAKEROOT=false - OPT_BACKEND_ARGS=false BACKEND_ARGS="" OPT_NO_COPY_FILES=false + BACKEND_COMMAND="" while [[ -n "$1" ]] do case "$1" in -f|--fakeroot) OPT_FAKEROOT=true ; shift ;; - -b|--backend-args) OPT_BACKEND_ARGS=true ; shift ; BACKEND_ARGS=$1; shift ;; + -b|--backend-args) shift ; BACKEND_ARGS=$1; shift ;; -n|--no-copy-files) OPT_NO_COPY_FILES=true ; shift ;; + --backend-command) shift; BACKEND_COMMAND="$1"; shift ;; --) shift ; break ;; -*) die "Invalid option $1" ;; *) break ;; esac done - ARGS=() - for arg in "$@" - do - ARGS+=("$arg") - done + ARGS=("$@") } function _parse_build_opts() { @@ -197,17 +216,29 @@ function _parse_build_opts() { done } +function _parse_create_wrappers_opts() { + OPT_FORCE=false + OPT_BIN_PATH="" + while [[ -n "$1" ]] + do + case "$1" in + -f|--force) OPT_FORCE=true ; shift ;; + -p|--bin-path) shift ; OPT_BIN_PATH="$1" ; shift ;; + *) die "Invalid option $1" ;; + esac + done +} + function _parse_setup_opts() { OPT_FROM_FILE=false IMAGE_FILE="" - OPT_ARCH=false ARCH_ARG="" OPT_DELETE=false while [[ -n "$1" ]] do case "$1" in -i|--from-file) OPT_FROM_FILE=true ; shift ; IMAGE_FILE=$1 ; shift ;; - -a|--arch) OPT_ARCH=true ; shift ; ARCH_ARG=$1; shift ;; + -a|--arch) shift ; ARCH_ARG=$1; shift ;; -d|--delete) OPT_DELETE=true ; shift ;; *) die "Invalid option $1" ;; esac @@ -219,6 +250,7 @@ function execute_operation() { $ACT_VERSION && version && return if $ACT_BUILD; then + # shellcheck disable=SC2086 build_image_env $OPT_DISABLE_CHECK return fi @@ -233,10 +265,11 @@ function execute_operation() { fi if $OPT_FROM_FILE; then - setup_env_from_file $IMAGE_FILE + setup_env_from_file "$IMAGE_FILE" else - setup_env $ARCH_ARG + setup_env "$ARCH_ARG" fi + create_wrappers fi return @@ -248,14 +281,24 @@ function execute_operation() { die "Error: The image is still not installed in $JUNEST_HOME. Run this first: $CMD setup" fi + if $ACT_CREATE_WRAPPERS; then + # shellcheck disable=SC2086 + create_wrappers $OPT_FORCE "$OPT_BIN_PATH" + exit + fi + local run_env if $ACT_NAMESPACE; then - run_env=run_env_with_namespace + if $OPT_FAKEROOT; then + run_env=run_env_as_bwrap_fakeroot + else + run_env=run_env_as_bwrap_user + fi elif $ACT_PROOT; then if $OPT_FAKEROOT; then - run_env=run_env_as_fakeroot + run_env=run_env_as_proot_fakeroot else - run_env=run_env_as_user + run_env=run_env_as_proot_user fi elif $ACT_GROOT; then run_env=run_env_as_groot @@ -263,8 +306,11 @@ function execute_operation() { run_env=run_env_as_chroot fi - $run_env "${BACKEND_ARGS}" $OPT_NO_COPY_FILES "${ARGS[@]}" - + # Call create_wrappers in case new bin files have been created + # shellcheck disable=SC2064 + trap "PATH=$PATH create_wrappers" EXIT QUIT TERM + # shellcheck disable=SC2086 + $run_env "$BACKEND_COMMAND" "${BACKEND_ARGS}" $OPT_NO_COPY_FILES "${ARGS[@]}" } function main() { diff --git a/bin/sudoj b/bin/sudoj new file mode 100755 index 0000000..aa43e15 --- /dev/null +++ b/bin/sudoj @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +# +# This file is part of JuNest (https://github.com/fsquillace/junest). +# + +export PATH="${PATH}:${JUNEST_HOME}/usr/bin_wrappers" + +JUNEST_ARGS="ns --fakeroot" "$@" diff --git a/ci/build_image.sh b/ci/build_image.sh new file mode 100755 index 0000000..f9cda95 --- /dev/null +++ b/ci/build_image.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bash + +set -ex + +pacman -Sy --noconfirm sudo + +# Create a travis user +echo "travis ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers.d/travis +chmod 'u=r,g=r,o=' /etc/sudoers.d/travis +groupadd --gid "2000" "travis" +useradd --create-home --uid "2000" --gid "2000" --shell /usr/bin/false "travis" + +# Here do not make any validation (-n) because it will be done later on in the Ubuntu host directly +cd /build +runuser -u travis -- /build/bin/junest build -n diff --git a/ci/deploy.sh b/ci/deploy.sh new file mode 100755 index 0000000..a45d0a1 --- /dev/null +++ b/ci/deploy.sh @@ -0,0 +1,48 @@ +#!/usr/bin/env bash + +set -e + +IMG_PATH=$1 + +set -ux + +MAX_OLD_IMAGES=5 +ENDPOINT="https://8da1bcd84e423c9b013b69fe1e8b4675.r2.cloudflarestorage.com" + +# ARCH can be one of: x86, x86_64, arm +HOST_ARCH=$(uname -m) +if [ "$HOST_ARCH" == "i686" ] || [ "$HOST_ARCH" == "i386" ] +then + ARCH="x86" +elif [ "$HOST_ARCH" == "x86_64" ] +then + ARCH="x86_64" +elif [[ $HOST_ARCH =~ .*(arm).* ]] +then + ARCH="arm" +else + echo "Unknown architecture ${HOST_ARCH}" >&2 + exit 11 +fi + +if [[ "$TRAVIS_BRANCH" == "master" ]] +then + + export AWS_DEFAULT_REGION=auto + # Upload image + # The put is done via a temporary filename in order to prevent outage on the + # production file for a longer period of time. + img_name=$(basename "${IMG_PATH}") + aws s3 --endpoint-url="$ENDPOINT" cp "${IMG_PATH}" s3://junest-repo/junest/ + + DATE=$(date +'%Y-%m-%d-%H-%M-%S') + aws s3 --endpoint-url="$ENDPOINT" cp "${IMG_PATH}" "s3://junest-repo/junest/${img_name}.${DATE}" + + # Cleanup old images + aws s3 --endpoint-url="$ENDPOINT" ls s3://junest-repo/junest/junest-${ARCH}.tar.gz. | awk '{print $4}' | head -n -${MAX_OLD_IMAGES} | xargs -I {} aws s3 --endpoint-url="$ENDPOINT" rm "s3://junest-repo/junest/{}" + + # Test the newly deployed image can be downloaded correctly + junest setup + junest -- echo "Installed JuNest (\$(uname -m))" + yes | junest setup --delete +fi diff --git a/ci/install-bash.sh b/ci/install-bash.sh new file mode 100755 index 0000000..b766123 --- /dev/null +++ b/ci/install-bash.sh @@ -0,0 +1,13 @@ +#!/bin/sh +set -ex + +VERSION=$1 + +cd /tmp +wget "http://ftp.gnu.org/gnu/bash/bash-$VERSION.tar.gz" + +tar -zxf "bash-$VERSION.tar.gz" +cd /tmp/bash-"$VERSION"* +./configure +make +sudo make install diff --git a/lib/checks/check.sh b/lib/checks/check.sh index 9ee7505..789206e 100755 --- a/lib/checks/check.sh +++ b/lib/checks/check.sh @@ -1,4 +1,5 @@ #!/usr/bin/env bash +# shellcheck disable=SC1091 # # This modules is used for: # - Running checks against the building JuNest image @@ -9,21 +10,32 @@ # # vim: ft=sh -set -eu +set -ex + -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 +USE_SUDO=false +while [[ -n "$1" ]] +do + case "$1" in + --run-root-tests) RUN_ROOT_TESTS=true ; shift ;; + --skip-aur-tests) SKIP_AUR_TESTS=true ; shift ;; + --use-sudo) USE_SUDO=true ; shift ;; + *) die "Invalid option $1" ;; + esac +done + +set -u + +SUDO="" +[[ -n $USE_SUDO ]] && SUDO="sudo" 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"))/../..)}" +JUNEST_BASE="${JUNEST_BASE:-$(readlink -f "$(dirname "$(readlink -f "$0")")"/../..)}" source "${JUNEST_BASE}/lib/utils/utils.sh" source "${JUNEST_BASE}/lib/core/common.sh" @@ -33,59 +45,58 @@ info "Validating JuNest located in ${JUNEST_HOME}..." info "Initial JuNest setup..." # The following ensures that the gpg agent gets killed (if exists) # otherwise it is not possible to exit from the session -trap "[[ -e /etc/pacman.d/gnupg/S.gpg-agent ]] && gpg-connect-agent -S /etc/pacman.d/gnupg/S.gpg-agent killagent /bye" QUIT EXIT ABRT KILL TERM INT +trap "[[ -e /etc/pacman.d/gnupg/S.gpg-agent ]] && gpg-connect-agent -S /etc/pacman.d/gnupg/S.gpg-agent killagent /bye" QUIT EXIT ABRT TERM INT -echo "Server = ${DEFAULT_MIRROR}" >> /etc/pacman.d/mirrorlist -pacman --noconfirm -Syy +prepare_archlinux "$SUDO" -pacman-key --init - -pacman --noconfirm -S archlinux-keyring -pacman-key --populate archlinux - -pacman --noconfirm -S archlinuxarm-keyring || echo "No ARM keyring detected" -pacman-key --populate archlinuxarm || echo "No ARM keyring detected" - -pacman --noconfirm -Su -pacman --noconfirm -S grep coreutils -pacman --noconfirm -S $(pacman -Sg base-devel | cut -d ' ' -f 2 | grep -v sudo) +PACMAN_OPTIONS="--noconfirm --disable-download-timeout" +# shellcheck disable=SC2086 +$SUDO pacman $PACMAN_OPTIONS -S grep coreutils +# shellcheck disable=SC2086 +# shellcheck disable=SC2046 +$SUDO pacman $PACMAN_OPTIONS -Syu --ignore sudo base-devel info "Checking basic executables work..." -pacman -Qi pacman 1> /dev/null -/opt/makepkg/bin/makepkg --help 1> /dev/null -/opt/proot/proot-$ARCH --help 1> /dev/null +$SUDO pacman -Qi pacman 1> /dev/null +/usr/bin/groot --help 1> /dev/null + +# Test FAKEROOTDONTTRYCHOWN is set to true by default +set +u +if [[ -z $FAKEROOTKEY ]] +then + fakeroot chown root /tmp +else + chown root /tmp +fi +set -u repo_package1=tree echo "Checking ${repo_package1} package from official repo..." -pacman --noconfirm -S ${repo_package1} +# shellcheck disable=SC2086 +$SUDO pacman $PACMAN_OPTIONS -S ${repo_package1} tree -L 1 -pacman --noconfirm -Rsn ${repo_package1} +# shellcheck disable=SC2086 +$SUDO pacman $PACMAN_OPTIONS -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} +# shellcheck disable=SC2086 +$SUDO pacman $PACMAN_OPTIONS -S ${repo_package2} +if $RUN_ROOT_TESTS +then + # Time it out given that sometimes it gets stuck after few seconds. + $SUDO timeout 10 iftop -t -s 5 || true +fi +# shellcheck disable=SC2086 +$SUDO pacman $PACMAN_OPTIONS -Rsn ${repo_package2} if ! $SKIP_AUR_TESTS then aur_package=tcptraceroute info "Checking ${aur_package} package from AUR repo..." - maindir=$(mktemp -d -t ${CMD}.XXXXXXXXXX) - builtin cd ${maindir} - curl -L -J -O -k "https://aur.archlinux.org/cgit/aur.git/plain/PKGBUILD?h=${aur_package}" - /opt/makepkg/bin/makepkg -sfc --noconfirm - - pacman --noconfirm -U ${aur_package}*.pkg.tar.xz - pacman --noconfirm -Rsn ${aur_package} -fi - -# The following ensures that the gpg agent gets killed (if exists) -# otherwise it is not possible to exit from the session -if [[ -e /etc/pacman.d/gnupg/S.gpg-agent ]] -then - gpg-connect-agent -S /etc/pacman.d/gnupg/S.gpg-agent killagent /bye || echo "GPG agent did not close properly" - echo "GPG agent closed" + yay --noconfirm -S ${aur_package} + # shellcheck disable=SC2086 + $SUDO pacman $PACMAN_OPTIONS -Rsn ${aur_package} fi exit 0 diff --git a/lib/checks/check_all.sh b/lib/checks/check_all.sh new file mode 100755 index 0000000..13e9237 --- /dev/null +++ b/lib/checks/check_all.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env bash +# Multiple tests against different execution modes + +set -ex + +# 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")")"/../..)}" + +JUNEST_SCRIPT=${JUNEST_SCRIPT:-${JUNEST_BASE}/bin/junest} + +CHECK_SCRIPT=${JUNEST_BASE}/lib/checks/check.sh + +$JUNEST_SCRIPT proot --fakeroot -- "$CHECK_SCRIPT" --skip-aur-tests +$JUNEST_SCRIPT proot -- "$CHECK_SCRIPT" --skip-aur-tests --use-sudo +# Test the backend command option +$JUNEST_SCRIPT proot --backend-command "$JUNEST_HOME/usr/bin/proot-x86_64" -- exit +$JUNEST_SCRIPT ns --fakeroot -- "$CHECK_SCRIPT" --skip-aur-tests +$JUNEST_SCRIPT ns -- "$CHECK_SCRIPT" --use-sudo +# Test the backend command option +$JUNEST_SCRIPT ns --backend-command "$JUNEST_HOME/usr/bin/bwrap" -- exit +sudo -E "$JUNEST_SCRIPT" groot -- "$CHECK_SCRIPT" --run-root-tests --skip-aur-tests + +# Test the wrappers work +"$JUNEST_SCRIPT" create-bin-wrappers --force +"$JUNEST_HOME"/usr/bin_wrappers/pacman --help + +"$JUNEST_SCRIPT" create-bin-wrappers --force --bin-path /usr/bin/core_perl/ +"$JUNEST_HOME"/usr/bin/core_perl_wrappers/shasum --help + +"${JUNEST_BASE}/bin/sudoj" pacman -Syu diff --git a/lib/core/build.sh b/lib/core/build.sh index faf7f08..65a9474 100644 --- a/lib/core/build.sh +++ b/lib/core/build.sh @@ -8,100 +8,134 @@ # # vim: ft=sh -function _check_package(){ - if ! pacman -Qq $1 > /dev/null - then - die "Package $1 must be installed" - fi -} - -function _install_pkg_from_aur(){ - local maindir=$1 - local pkgname=$2 - local installname=$3 - mkdir -p ${maindir}/packages/${pkgname} - builtin cd ${maindir}/packages/${pkgname} - $CURL "https://aur.archlinux.org/cgit/aur.git/plain/PKGBUILD?h=${pkgname}" - [ -z "${installname}" ] || $CURL "https://aur.archlinux.org/cgit/aur.git/plain/${installname}?h=${pkgname}" - makepkg -sfcd - sudo pacman --noconfirm --root ${maindir}/root -U ${pkgname}*.pkg.tar.xz -} - function _install_pkg(){ + # This function allows to install packages from AUR. + # At the moment is not used. local maindir=$1 local pkgbuilddir=$2 - builtin cd ${pkgbuilddir} + # Generate a working directory because sources will be downloaded to there + working_dir=$(TMPDIR=/tmp mktemp -d -t junest-wd.XXXXXXXXXX) + cp -R "$pkgbuilddir"/* "$working_dir" + builtin cd "${working_dir}" || return 1 makepkg -sfcd - sudo pacman --noconfirm --root ${maindir}/root -U *.pkg.tar.xz + makepkg --printsrcinfo > "${pkgbuilddir}"/.SRCINFO + sudo pacman --noconfirm --root "${maindir}"/root -U ./*.pkg.tar.* +} + +function _prepare() { + # ArchLinux System initialization + prepare_archlinux + # curl is used to download pacman.conf file + sudo pacman -S --noconfirm git arch-install-scripts haveged curl } function build_image_env(){ + set -x umask 022 # The function must runs on ArchLinux with non-root privileges. + # This is because installing AUR packages can be done by normal users only. (( EUID == 0 )) && \ die "You cannot build with root privileges." - _check_package arch-install-scripts - _check_package gcc + _prepare local disable_validation=$1 - local maindir=$(TMPDIR=$JUNEST_TEMPDIR mktemp -d -t ${CMD}.XXXXXXXXXX) - sudo mkdir -p ${maindir}/root - trap - QUIT EXIT ABRT KILL TERM INT - trap "sudo rm -rf ${maindir}; die \"Error occurred when installing ${NAME}\"" EXIT QUIT ABRT KILL TERM INT + local maindir + maindir=$(TMPDIR=$JUNEST_TEMPDIR mktemp -d -t "${CMD}".XXXXXXXXXX) + sudo mkdir -p "${maindir}"/root + trap - QUIT EXIT ABRT TERM INT + # shellcheck disable=SC2064 + trap "sudo rm -rf ${maindir}; die \"Error occurred when installing ${NAME}\"" EXIT QUIT ABRT TERM INT info "Installing pacman and its dependencies..." - # The archlinux-keyring and libunistring are due to missing dependencies declaration in ARM archlinux # All the essential executables (ln, mkdir, chown, etc) are in coreutils - # unshare command belongs to util-linux - sudo pacstrap -G -M -d ${maindir}/root pacman coreutils libunistring archlinux-keyring util-linux - sudo bash -c "echo 'Server = $DEFAULT_MIRROR' >> ${maindir}/root/etc/pacman.d/mirrorlist" - sudo mkdir -p ${maindir}/root/run/lock + # bwrap command belongs to bubblewrap + sudo pacstrap -G -M "${maindir}"/root pacman coreutils bubblewrap - # AUR packages requires non-root user to be compiled. proot fakes the user to 10 - _install_pkg ${maindir} "$JUNEST_BASE/pkgs/sudo-fake" + if [[ ${ARCH} != "arm" ]] + then + # x86_64 does not have any mirror set by default... + sudo bash -c "echo 'Server = $DEFAULT_MIRROR' >> ${maindir}/root/etc/pacman.d/mirrorlist" + fi + sudo mkdir -p "${maindir}"/root/run/lock - info "Install ${NAME} script..." - _install_pkg_from_aur ${maindir} "${CMD}-git" "${CMD}.install" + # For some reasons, pacstrap does not create the pacman.conf file, + # I could not reproduce the issue locally though: + # https://app.travis-ci.com/github/fsquillace/junest/builds/268216346 + [[ -e "${maindir}"/root/etc/pacman.conf ]] || sudo curl "https://gitlab.archlinux.org/archlinux/packaging/packages/pacman/-/raw/main/pacman.conf" -o "${maindir}/root/etc/pacman.conf" + + # Pacman/pacstrap bug: https://gitlab.archlinux.org/archlinux/packaging/packages/arch-install-scripts/-/issues/3 + sudo sed -i '/^DownloadUser = alpm$/d' "${maindir}"/root/etc/pacman.conf + + sudo tee -a "${maindir}"/root/etc/pacman.conf < ${maindir}/root/etc/${CMD}/info" + # Related to: https://github.com/fsquillace/junest/issues/305 + sudo bash -c "echo 'export FAKEROOTDONTTRYCHOWN=true' > ${maindir}/root/etc/profile.d/junest.sh" info "Generating the locales..." - # sed command is required for locale-gen - # localedef (called by locale-gen) requires gzip - sudo pacman --noconfirm --root ${maindir}/root -S sed gzip - sudo ln -sf /usr/share/zoneinfo/posix/UTC ${maindir}/root/etc/localtime + # sed command is required for locale-gen but it is required by fakeroot + # and cannot be removed + # localedef (called by locale-gen) requires gzip but it is supposed to be + # already installed as systemd already depends on it + sudo pacman --noconfirm --root "${maindir}"/root -S sed gzip + 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/groot ${maindir}/root locale-gen + sudo "${maindir}"/root/bin/groot "${maindir}"/root locale-gen sudo bash -c "echo LANG=\"en_US.UTF-8\" >> ${maindir}/root/etc/locale.conf" - sudo pacman --noconfirm --root ${maindir}/root -Rsn sed gzip info "Setting up the pacman keyring (this might take a while!)..." - sudo ${maindir}/root/opt/junest/bin/groot -b /dev ${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" + if [[ $(uname -m) == *"arm"* ]] + then + sudo pacman -S --noconfirm --root "${maindir}"/root archlinuxarm-keyring + else + sudo pacman -S --noconfirm --root "${maindir}"/root archlinux-keyring + fi + sudo mount --bind "${maindir}"/root "${maindir}"/root + sudo arch-chroot "${maindir}"/root bash -c ' + set -e + pacman-key --init; + for keyring_file in /usr/share/pacman/keyrings/*.gpg; + do + keyring=$(basename $keyring_file | cut -f 1 -d "."); + pacman-key --populate $keyring; + done;' + sudo umount "${maindir}"/root - sudo rm ${maindir}/root/var/cache/pacman/pkg/* + sudo rm "${maindir}"/root/var/cache/pacman/pkg/* # This is needed on system with busybox tar command. # If the file does not have write permission, the tar command to extract files fails. - sudo chmod -R u+rw ${maindir}/root/ + sudo chmod -R u+rw "${maindir}"/root/ - mkdir -p ${maindir}/output - builtin cd ${maindir}/output + mkdir -p "${maindir}"/output + builtin cd "${maindir}"/output || return 1 local imagefile="${CMD}-${ARCH}.tar.gz" info "Compressing image to ${imagefile}..." - sudo $TAR -zcpf ${imagefile} -C ${maindir}/root . + sudo "$TAR" -zcpf "${imagefile}" -C "${maindir}"/root . if ! $disable_validation then - mkdir -p ${maindir}/root_test - $TAR -zxpf ${imagefile} -C "${maindir}/root_test" - JUNEST_HOME="${maindir}/root_test" ${JUNEST_BASE}/bin/${CMD} proot -f ${JUNEST_BASE}/lib/checks/check.sh - JUNEST_HOME="${maindir}/root_test" ${JUNEST_BASE}/bin/${CMD} ns ${JUNEST_BASE}/lib/checks/check.sh - JUNEST_HOME="${maindir}/root_test" sudo -E ${JUNEST_BASE}/bin/${CMD} groot ${JUNEST_BASE}/lib/checks/check.sh --run-root-tests + mkdir -p "${maindir}"/root_test + $TAR -zxpf "${imagefile}" -C "${maindir}/root_test" + JUNEST_HOME="${maindir}/root_test" "${JUNEST_BASE}"/lib/checks/check_all.sh fi - sudo cp ${maindir}/output/${imagefile} ${ORIGIN_WD} + sudo cp "${maindir}"/output/"${imagefile}" "${ORIGIN_WD}" - builtin cd ${ORIGIN_WD} + builtin cd "${ORIGIN_WD}" || return 1 trap - QUIT EXIT ABRT KILL TERM INT sudo rm -fr "$maindir" + + set +x } diff --git a/lib/core/chroot.sh b/lib/core/chroot.sh index eb584f0..c2237a9 100644 --- a/lib/core/chroot.sh +++ b/lib/core/chroot.sh @@ -18,21 +18,23 @@ function _run_env_as_xroot(){ local uid=$UID # SUDO_USER is more reliable compared to SUDO_UID - [ -z $SUDO_USER ] || uid=$SUDO_USER:$SUDO_GID + [[ -z $SUDO_USER ]] || uid=$SUDO_USER:$SUDO_GID - local main_cmd="${SH[@]}" - [ "$1" != "" ] && main_cmd="$(insert_quotes_on_spaces "$@")" + local args=() + [[ "$1" != "" ]] && args=("-c" "$(insert_quotes_on_spaces "${@}")") # With chown the ownership of the files is assigned to the real user trap - QUIT EXIT ABRT KILL TERM INT - trap "[ -z $uid ] || chown_cmd -R ${uid} ${JUNEST_HOME};" EXIT QUIT ABRT KILL TERM INT + # shellcheck disable=SC2064 + trap "[ -z $uid ] || chown_cmd -R ${uid} ${JUNEST_HOME};" EXIT QUIT ABRT TERM INT if ! $no_copy_files then copy_common_files fi - JUNEST_ENV=1 $cmd $backend_args "$JUNEST_HOME" "${SH[@]}" "-c" "${main_cmd}" + # shellcheck disable=SC2086 + JUNEST_ENV=1 $cmd $backend_args "$JUNEST_HOME" "${DEFAULT_SH[@]}" "${args[@]}" } ####################################### @@ -43,13 +45,13 @@ function _run_env_as_xroot(){ # 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. +# DEFAULT_SH (RO) : Contains the default command to run in JuNest. # Arguments: # backend_args ($1) : The arguments to pass to backend program # no_copy_files ($2?) : If false it will copy some files in /etc # from host to JuNest environment. # cmd ($3-?) : The command to run inside JuNest environment. -# Default command is defined by SH variable. +# Default command is defined by DEFAULT_SH variable. # Returns: # $ARCHITECTURE_MISMATCH : If host and JuNest architecture are different. # Output: @@ -58,15 +60,16 @@ function _run_env_as_xroot(){ function run_env_as_groot(){ check_nested_env - local backend_args="$1" - local no_copy_files="$2" - shift 2 + local backend_command="${1:-$GROOT}" + local backend_args="$2" + local no_copy_files="$3" + shift 3 provide_common_bindings local bindings=${RESULT} unset RESULT - _run_env_as_xroot "$GROOT $bindings" "$backend_args" "$no_copy_files" "$@" + _run_env_as_xroot "$backend_command $bindings" "$backend_args" "$no_copy_files" "$@" } ####################################### @@ -77,13 +80,13 @@ function run_env_as_groot(){ # 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. +# DEFAULT_SH (RO) : Contains the default command to run in JuNest. # Arguments: # backend_args ($1) : The arguments to pass to backend program # no_copy_files ($2?) : If false it will copy some files in /etc # from host to JuNest environment. # cmd ($3-?) : The command to run inside JuNest environment. -# Default command is defined by SH variable. +# Default command is defined by DEFAULT_SH variable. # Returns: # $ARCHITECTURE_MISMATCH : If host and JuNest architecture are different. # Output: @@ -92,9 +95,10 @@ function run_env_as_groot(){ function run_env_as_chroot(){ check_nested_env - local backend_args="$1" - local no_copy_files="$2" - shift 2 + local backend_command="${1:-chroot_cmd}" + local backend_args="$2" + local no_copy_files="$3" + shift 3 - _run_env_as_xroot chroot_cmd "$backend_args" "$no_copy_files" "$@" + _run_env_as_xroot "$backend_command" "$backend_args" "$no_copy_files" "$@" } diff --git a/lib/core/common.sh b/lib/core/common.sh index 601e46c..df79bec 100644 --- a/lib/core/common.sh +++ b/lib/core/common.sh @@ -1,4 +1,6 @@ #!/usr/bin/env bash +# shellcheck disable=SC2034 +# shellcheck disable=SC1091 # # This module contains all common functionalities for JuNest. # @@ -21,28 +23,29 @@ NO_CONFIG_FOUND=108 UNPRIVILEGED_USERNS_DISABLED=109 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 -PATH=$PATH:/usr/bin:/bin:/usr/sbin:/sbin +PATH=/usr/bin:/bin:/usr/local/bin:/usr/sbin:/sbin:${HOME}/.local/bin:"$PATH" # The executable uname is essential in order to get the architecture # of the host system, so a fallback mechanism cannot be used for it. -UNAME=uname +UNAME="uname" ARCH_LIST=('x86_64' 'x86' 'arm') HOST_ARCH=$($UNAME -m) -if [ $HOST_ARCH == "i686" ] || [ $HOST_ARCH == "i386" ] +# To check all available architectures look here: +# https://wiki.archlinux.org/index.php/PKGBUILD#arch +if [[ $HOST_ARCH == "i686" ]] || [[ $HOST_ARCH == "i386" ]] then ARCH="x86" LD_LIB="${JUNEST_HOME}/lib/ld-linux.so.2" -elif [ $HOST_ARCH == "x86_64" ] +elif [[ $HOST_ARCH == "x86_64" ]] then ARCH="x86_64" LD_LIB="${JUNEST_HOME}/lib64/ld-linux-x86-64.so.2" -elif [[ $HOST_ARCH =~ .*(arm).* ]] +elif [[ $HOST_ARCH =~ .*(arm).* ]] || [[ $HOST_ARCH == "aarch64" ]] then ARCH="arm" LD_LIB="${JUNEST_HOME}/lib/ld-linux-armhf.so.3" @@ -50,9 +53,11 @@ else die "Unknown architecture ${HOST_ARCH}" fi -MAIN_REPO=https://s3-eu-west-1.amazonaws.com/${CMD}-repo +MAIN_REPO=https://link.storjshare.io/s/jvb5tgarnjtt565fffa44spvyuga/junest-repo +MAIN_REPO=https://pub-a2af2344e8554f6c807bc3db355ae622.r2.dev ENV_REPO=${MAIN_REPO}/${CMD} -DEFAULT_MIRROR='https://mirrors.kernel.org/archlinux/$repo/os/$arch' +# shellcheck disable=SC2016 +DEFAULT_MIRROR='https://mirror.rackspace.com/archlinux/$repo/os/$arch' ORIGIN_WD=$(pwd) @@ -63,24 +68,26 @@ ORIGIN_WD=$(pwd) # different locations in the host OS. # List of executables that are run inside JuNest: -SH=("/bin/sh" "--login") +DEFAULT_SH=("/bin/sh" "--login") # List of executables that are run in the host OS: -PROOT="${JUNEST_HOME}/opt/proot/proot-${ARCH}" -GROOT=${JUNEST_BASE}/bin/groot +BWRAP="${JUNEST_HOME}/usr/bin/bwrap" +PROOT="${JUNEST_HOME}/usr/bin/proot-${ARCH}" +GROOT="${JUNEST_HOME}/usr/bin/groot" CLASSIC_CHROOT=chroot -WGET="wget --no-check-certificate" +WGET="wget --content-disposition --no-check-certificate" CURL="curl -L -J -O -k" -TAR=tar +TAR="tar" CHOWN="chown" -LN=ln -RM=rm -MKDIR=mkdir -GETENT=getent -CP=cp +LN="ln" +RM="rm" +MKDIR="mkdir" +GETENT="getent" +CP="cp" +ID="id" # Used for checking user namespace in config.gz file -ZGREP=zgrep -UNSHARE=unshare +ZGREP="zgrep" +UNSHARE="unshare" LD_EXEC="$LD_LIB --library-path ${JUNEST_HOME}/usr/lib:${JUNEST_HOME}/lib" @@ -89,32 +96,32 @@ 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 "$@" + $ZGREP "$@" || "${JUNEST_HOME}"/usr/bin/$ZGREP "$@" } function download_cmd(){ @@ -122,7 +129,7 @@ function download_cmd(){ } function chroot_cmd(){ - $CLASSIC_CHROOT "$@" || $LD_EXEC ${JUNEST_HOME}/usr/bin/$CLASSIC_CHROOT "$@" + $CLASSIC_CHROOT "$@" || $LD_EXEC "${JUNEST_HOME}"/usr/bin/$CLASSIC_CHROOT "$@" } function unshare_cmd(){ @@ -130,26 +137,37 @@ function unshare_cmd(){ # 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 + # therefore it ignores all the remaining DEFAULT_SH arguments (i.e. --login) as # they are not supported by dash. - if $LD_EXEC ${JUNEST_HOME}/usr/bin/$UNSHARE --user "${SH[0]}" "-c" ":" + if $LD_EXEC "${JUNEST_HOME}"/usr/bin/$UNSHARE --user "${DEFAULT_SH[0]}" "-c" ":" then - $LD_EXEC ${JUNEST_HOME}/usr/bin/$UNSHARE "${@}" - elif $UNSHARE --user "${SH[0]}" "-c" ":" + $LD_EXEC "${JUNEST_HOME}"/usr/bin/$UNSHARE "${@}" + elif $UNSHARE --user "${DEFAULT_SH[0]}" "-c" ":" then $UNSHARE "$@" else - die "Error: Something went wrong with unshare command. Exiting" + die "Error: Something went wrong while executing unshare command. Exiting" + fi +} + +function bwrap_cmd(){ + if $LD_EXEC "$BWRAP" --dev-bind / / "${DEFAULT_SH[0]}" "-c" ":" + then + $LD_EXEC "$BWRAP" "${@}" + else + die "Error: Something went wrong while executing bwrap command. Exiting" fi } function proot_cmd(){ local proot_args="$1" shift - if ${PROOT} ${proot_args} "${SH[@]}" "-c" ":" + # shellcheck disable=SC2086 + if ${PROOT} ${proot_args} "${DEFAULT_SH[@]}" "-c" ":" then + # shellcheck disable=SC2086 ${PROOT} ${proot_args} "${@}" - elif PROOT_NO_SECCOMP=1 ${PROOT} ${proot_args} "${SH[@]}" "-c" ":" + elif PROOT_NO_SECCOMP=1 ${PROOT} ${proot_args} "${DEFAULT_SH[@]}" "-c" ":" then warn "Warn: Proot is not properly working. Disabling SECCOMP and expect the application to run slowly in particular when it uses syscalls intensively." warn "Try to use Linux namespace instead as it is more reliable: junest ns" @@ -181,7 +199,7 @@ 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" ]] + elif [[ -n $JUNEST_ENV ]] && [[ $JUNEST_ENV != "0" ]] then die_on_status $VARIABLE_NOT_SET "The variable JUNEST_ENV is not properly set" fi @@ -203,7 +221,7 @@ function check_nested_env() { # None ####################################### function check_same_arch() { - source ${JUNEST_HOME}/etc/junest/info + 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 @@ -228,7 +246,7 @@ function check_same_arch() { function provide_common_bindings(){ RESULT="" local re='(.*):.*' - for bind in "/dev" "/sys" "/proc" "/tmp" "$HOME" + for bind in "/dev" "/sys" "/proc" "/tmp" "$HOME" "/run/user/$($ID -u)" do if [[ $bind =~ $re ]] then @@ -261,24 +279,26 @@ function copy_passwd_and_group(){ # is configured. # Try to at least get the current user via `getent passwd $USER` since it uses # a more reliable and faster system call (getpwnam(3)). - if ! getent_cmd passwd > ${JUNEST_HOME}/etc/passwd || \ - ! getent_cmd passwd ${USER} >> ${JUNEST_HOME}/etc/passwd + if ! getent_cmd passwd > "${JUNEST_HOME}"/etc/passwd || \ + ! 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 fi - if ! getent_cmd group > ${JUNEST_HOME}/etc/group + 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 fi return 0 } function copy_file() { local file="${1}" - [[ -r "$file" ]] && cp_cmd "$file" "${JUNEST_HOME}/$file" + # -f option ensure to remove destination file if it cannot be opened + # https://github.com/fsquillace/junest/issues/284 + [[ -r "$file" ]] && cp_cmd -f "$file" "${JUNEST_HOME}/$file" return 0 } @@ -289,3 +309,27 @@ function copy_common_files() { copy_file /etc/resolv.conf return 0 } + +function prepare_archlinux() { + local sudo=${1:-sudo} + local pacman_options="--noconfirm --disable-download-timeout" + + # shellcheck disable=SC2086 + $sudo pacman $pacman_options -Syy + + $sudo pacman-key --init + + if [[ $(uname -m) == *"arm"* ]] + then + # shellcheck disable=SC2086 + $sudo pacman $pacman_options -S archlinuxarm-keyring + $sudo pacman-key --populate archlinuxarm + else + # shellcheck disable=SC2086 + $sudo pacman $pacman_options -S archlinux-keyring + $sudo pacman-key --populate archlinux + fi + + # shellcheck disable=SC2086 + $sudo pacman $pacman_options -Su +} diff --git a/lib/core/namespace.sh b/lib/core/namespace.sh index 7943faa..70763bd 100644 --- a/lib/core/namespace.sh +++ b/lib/core/namespace.sh @@ -1,9 +1,8 @@ #!/usr/bin/env bash # -# This module contains all namespace functionalities for JuNest. +# This module contains functionalities for accessing to JuNest via bubblewrap. # -# http://man7.org/linux/man-pages/man7/namespaces.7.html -# http://man7.org/linux/man-pages/man2/unshare.2.html +# https://github.com/containers/bubblewrap # # Dependencies: # - lib/utils/utils.sh @@ -11,11 +10,29 @@ # # vim: ft=sh +# shellcheck disable=SC2027 +COMMON_BWRAP_OPTION="--bind "$JUNEST_HOME" / --bind "$HOME" "$HOME" --bind /tmp /tmp --bind /sys /sys --bind /proc /proc --dev-bind-try /dev /dev --bind-try "/run/user/$($ID -u)" "/run/user/$($ID -u)" --unshare-user-try" CONFIG_PROC_FILE="/proc/config.gz" CONFIG_BOOT_FILE="/boot/config-$($UNAME -r)" PROC_USERNS_CLONE_FILE="/proc/sys/kernel/unprivileged_userns_clone" +PROC_USERNS_FILE="/proc/$$/ns/user" function _is_user_namespace_enabled() { + if [[ -L $PROC_USERNS_FILE ]] + then + return 0 + fi + + if [[ -e $PROC_USERNS_CLONE_FILE ]] + then + # `-q` option in zgrep may cause a gzip: stdout: Broken pipe + # Use redirect to /dev/null instead + if zgrep_cmd "1" "$PROC_USERNS_CLONE_FILE" > /dev/null + then + return 0 + fi + fi + local config_file="" if [[ -e $CONFIG_PROC_FILE ]] then @@ -24,83 +41,101 @@ function _is_user_namespace_enabled() { then config_file=$CONFIG_BOOT_FILE else - return $NOT_EXISTING_FILE + return "$NOT_EXISTING_FILE" fi - if ! zgrep_cmd -q "CONFIG_USER_NS=y" $config_file + # `-q` option in zgrep may cause a gzip: stdout: Broken pipe + # Use redirect to /dev/null instead + if ! zgrep_cmd "CONFIG_USER_NS=y" "$config_file" > /dev/null then - return $NO_CONFIG_FOUND + return "$NO_CONFIG_FOUND" fi - if [[ ! -e $PROC_USERNS_CLONE_FILE ]] - then - return 0 - fi - - if ! zgrep_cmd -q "1" $PROC_USERNS_CLONE_FILE - then - return $UNPRIVILEGED_USERNS_DISABLED - fi - - return 0 + return "$UNPRIVILEGED_USERNS_DISABLED" } 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 "Unprivileged user namespace is disabled at kernel compile time or kernel too old (<3.8). Proceeding anyway..." ;; - $UNPRIVILEGED_USERNS_DISABLED) warn "Unprivileged user namespace disabled. Root permissions are required to enable it: sudo sysctl kernel.unprivileged_userns_clone=1" ;; + "$NOT_EXISTING_FILE") warn "Could not understand if user namespace is enabled. No config.gz file found. Proceeding anyway..." ;; + "$NO_CONFIG_FOUND") warn "Unprivileged user namespace is disabled at kernel compile time or kernel too old (<3.8). Proceeding anyway..." ;; + "$UNPRIVILEGED_USERNS_DISABLED") warn "Unprivileged user namespace disabled. Root permissions are required to enable it: sudo sysctl kernel.unprivileged_userns_clone=1" ;; esac set -e } -function _run_env_with_namespace(){ - local backend_args="$1" - shift - provide_common_bindings - local bindings=${RESULT} - unset RESULT +####################################### +# Run JuNest as fakeroot via bwrap +# +# Globals: +# JUNEST_HOME (RO) : The JuNest home directory. +# DEFAULT_SH (RO) : Contains the default command to run in JuNest. +# BWRAP (RO): : The location of the bwrap binary. +# Arguments: +# backend_args ($1) : The arguments to pass to bwrap +# no_copy_files ($2?) : If false it will copy some files in /etc +# from host to JuNest environment. +# cmd ($3-?) : The command to run inside JuNest environment. +# Default command is defined by DEFAULT_SH variable. +# Returns: +# $ARCHITECTURE_MISMATCH : If host and JuNest architecture are different. +# $ROOT_ACCESS_ERROR : If the user is the real root. +# Output: +# - : The command output. +####################################### +function run_env_as_bwrap_fakeroot(){ + check_nested_env - # 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" != "" ]] + local backend_command="${1:-$BWRAP}" + local backend_args="$2" + local no_copy_files="$3" + shift 3 + + _check_user_namespace + + check_same_arch + + if ! $no_copy_files then - JUNEST_ENV=1 unshare_cmd --mount --user --map-root-user $GROOT --no-umount --recursive $bindings $backend_args "$JUNEST_HOME" "${SH[@]}" "-c" "$(insert_quotes_on_spaces "${@}")" - else - JUNEST_ENV=1 unshare_cmd --mount --user --map-root-user $GROOT --no-umount --recursive $bindings $backend_args "$JUNEST_HOME" "${SH[@]}" + copy_common_files fi + + local args=() + [[ "$1" != "" ]] && args=("-c" "$(insert_quotes_on_spaces "${@}")") + + # Fix PATH to /usr/bin to make sudo working and avoid polluting with host related bin paths + # shellcheck disable=SC2086 + PATH="/usr/bin" BWRAP="${backend_command}" JUNEST_ENV=1 bwrap_cmd $COMMON_BWRAP_OPTION --cap-add ALL --uid 0 --gid 0 $backend_args sudo "${DEFAULT_SH[@]}" "${args[@]}" } ####################################### -# Run JuNest as fakeroot user via user namespace. +# Run JuNest as normal user via bwrap. # # Globals: # JUNEST_HOME (RO) : The JuNest home directory. -# GROOT (RO) : The groot program. -# SH (RO) : Contains the default command to run in JuNest. +# DEFAULT_SH (RO) : Contains the default command to run in JuNest. +# BWRAP (RO): : The location of the bwrap binary. # Arguments: -# backend_args ($1) : The arguments to pass to groot +# backend_args ($1) : The arguments to pass to bwrap # no_copy_files ($2?) : If false it will copy some files in /etc # from host to JuNest environment. # cmd ($3-?) : The command to run inside JuNest environment. -# Default command is defined by SH variable. +# Default command is defined by DEFAULT_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() { +function run_env_as_bwrap_user() { check_nested_env - local backend_args="$1" - local no_copy_files="$2" - shift 2 + local backend_command="${1:-$BWRAP}" + local backend_args="$2" + local no_copy_files="$3" + shift 3 _check_user_namespace @@ -117,5 +152,14 @@ function run_env_with_namespace() { copy_passwd_and_group fi - _run_env_with_namespace "$backend_args" "$@" + local args=() + [[ "$1" != "" ]] && args=("-c" "$(insert_quotes_on_spaces "${@}")") + + # Resets PATH to avoid polluting with host related bin paths + # shellcheck disable=SC2086 + PATH='' BWRAP="${backend_command}" JUNEST_ENV=1 bwrap_cmd $COMMON_BWRAP_OPTION $backend_args "${DEFAULT_SH[@]}" "${args[@]}" } + + + + diff --git a/lib/core/proot.sh b/lib/core/proot.sh index 69502eb..b6c1c8f 100644 --- a/lib/core/proot.sh +++ b/lib/core/proot.sh @@ -1,4 +1,5 @@ #!/usr/bin/env bash +# shellcheck disable=SC1091 # # This module contains all proot functionalities for JuNest. # @@ -9,35 +10,39 @@ # vim: ft=sh function _run_env_with_proot(){ - local proot_args="$1" - shift + local backend_command="${1:-$PROOT}" + local backend_args="$2" + shift 2 - if [ "$1" != "" ] - then - JUNEST_ENV=1 proot_cmd "${proot_args}" "${SH[@]}" "-c" "$(insert_quotes_on_spaces "${@}")" - else - JUNEST_ENV=1 proot_cmd "${proot_args}" "${SH[@]}" - fi + local args=() + [[ "$1" != "" ]] && args=("-c" "$(insert_quotes_on_spaces "${@}")") + + # Resets PATH to avoid polluting with host related bin paths + PATH='' PROOT="${backend_command}" JUNEST_ENV=1 proot_cmd "${backend_args}" "${DEFAULT_SH[@]}" "${args[@]}" } function _run_env_with_qemu(){ - local proot_args="$1" - source ${JUNEST_HOME}/etc/junest/info + local backend_command="$1" + local backend_args="$2" + shift 2 + + source "${JUNEST_HOME}"/etc/junest/info if [ "$JUNEST_ARCH" != "$ARCH" ] then local qemu_bin="qemu-$JUNEST_ARCH-static-$ARCH" local qemu_symlink="/tmp/${qemu_bin}-$RANDOM" trap - QUIT EXIT ABRT KILL TERM INT - trap "[ -e ${qemu_symlink} ] && rm_cmd -f ${qemu_symlink}" EXIT QUIT ABRT KILL TERM INT + # shellcheck disable=SC2064 + trap "[ -e ${qemu_symlink} ] && rm_cmd -f ${qemu_symlink}" EXIT QUIT ABRT TERM INT warn "Emulating $NAME via QEMU..." - [ -e ${qemu_symlink} ] || \ - ln_cmd -s ${JUNEST_HOME}/opt/qemu/${qemu_bin} ${qemu_symlink} - proot_args="-q ${qemu_symlink} $proot_args" + [[ -e ${qemu_symlink} ]] || \ + ln_cmd -s "${JUNEST_HOME}/bin/${qemu_bin}" "${qemu_symlink}" + backend_args="-q ${qemu_symlink} $backend_args" fi - shift - _run_env_with_proot "$proot_args" "${@}" + + _run_env_with_proot "${backend_command}" "$backend_args" "${@}" } ####################################### @@ -46,26 +51,27 @@ function _run_env_with_qemu(){ # Globals: # JUNEST_HOME (RO) : The JuNest home directory. # EUID (RO) : The user ID. -# SH (RO) : Contains the default command to run in JuNest. +# DEFAULT_SH (RO) : Contains the default command to run in JuNest. # Arguments: # backend_args ($1) : The arguments to pass to proot # no_copy_files ($2?) : If false it will copy some files in /etc # from host to JuNest environment. # cmd ($3-?) : The command to run inside JuNest environment. -# Default command is defined by SH variable. +# Default command is defined by DEFAULT_SH variable. # Returns: # $ROOT_ACCESS_ERROR : If the user is the real root. # Output: # - : The command output. ####################################### -function run_env_as_fakeroot(){ +function run_env_as_proot_fakeroot(){ (( EUID == 0 )) && \ - die_on_status $ROOT_ACCESS_ERROR "You cannot access with root privileges. Use --groot option instead." + die_on_status "$ROOT_ACCESS_ERROR" "You cannot access with root privileges. Use --groot option instead." check_nested_env - local backend_args="$1" - local no_copy_files="$2" - shift 2 + local backend_command="$1" + local backend_args="$2" + local no_copy_files="$3" + shift 3 if ! $no_copy_files then @@ -78,7 +84,7 @@ function run_env_as_fakeroot(){ # An alternative is via -S option: #_run_env_with_qemu "-S ${JUNEST_HOME} $1" "${@:2}" - _run_env_with_qemu "-0 ${bindings} -r ${JUNEST_HOME} $backend_args" "$@" + _run_env_with_qemu "$backend_command" "-0 ${bindings} -r ${JUNEST_HOME} $backend_args" "$@" } ####################################### @@ -87,26 +93,27 @@ function run_env_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. +# DEFAULT_SH (RO) : Contains the default command to run in JuNest. # Arguments: # backend_args ($1) : The arguments to pass to proot # no_copy_files ($2?) : If false it will copy some files in /etc # from host to JuNest environment. # cmd ($3-?) : The command to run inside JuNest environment. -# Default command is defined by SH variable. +# Default command is defined by DEFAULT_SH variable. # Returns: # $ROOT_ACCESS_ERROR : If the user is the real root. # Output: # - : The command output. ####################################### -function run_env_as_user(){ +function run_env_as_proot_user(){ (( EUID == 0 )) && \ - die_on_status $ROOT_ACCESS_ERROR "You cannot access with root privileges. Use --groot option instead." + die_on_status "$ROOT_ACCESS_ERROR" "You cannot access with root privileges. Use --groot option instead." check_nested_env - local backend_args="$1" - local no_copy_files="$2" - shift 2 + local backend_command="$1" + local backend_args="$2" + local no_copy_files="$3" + shift 3 if ! $no_copy_files then @@ -127,5 +134,5 @@ function run_env_as_user(){ local bindings=${RESULT} unset RESULT - _run_env_with_qemu "${bindings} -r ${JUNEST_HOME} $backend_args" "$@" + _run_env_with_qemu "$backend_command" "${bindings} -r ${JUNEST_HOME} $backend_args" "$@" } diff --git a/lib/core/setup.sh b/lib/core/setup.sh index 2cbfdf1..58c6122 100644 --- a/lib/core/setup.sh +++ b/lib/core/setup.sh @@ -22,7 +22,7 @@ # None ####################################### function is_env_installed(){ - [ -d "$JUNEST_HOME" ] && [ "$(ls -A $JUNEST_HOME)" ] && return 0 + [[ -d "$JUNEST_HOME" ]] && [[ "$(ls -A "$JUNEST_HOME")" ]] && return 0 return 1 } @@ -30,7 +30,7 @@ function is_env_installed(){ function _cleanup_build_directory(){ local maindir=$1 check_not_null "$maindir" - builtin cd $ORIGIN_WD + builtin cd "$ORIGIN_WD" || return 1 trap - QUIT EXIT ABRT KILL TERM INT rm_cmd -fr "$maindir" } @@ -40,7 +40,8 @@ function _prepare_build_directory(){ local maindir=$1 check_not_null "$maindir" trap - QUIT EXIT ABRT KILL TERM INT - trap "rm_cmd -rf ${maindir}; die \"Error occurred when installing ${NAME}\"" EXIT QUIT ABRT KILL TERM INT + # shellcheck disable=SC2064 + trap "rm_cmd -rf ${maindir}; die \"Error occurred when installing ${NAME}\"" EXIT QUIT ABRT TERM INT } @@ -51,11 +52,18 @@ function _setup_env(){ is_env_installed && die "Error: ${NAME} has been already installed in $JUNEST_HOME" mkdir_cmd -p "${JUNEST_HOME}" - $TAR -zxpf ${imagepath} -C ${JUNEST_HOME} - info "The default mirror URL is ${DEFAULT_MIRROR}." + $TAR -zxpf "${imagepath}" -C "${JUNEST_HOME}" + info "${NAME} installed successfully!" + echo + info "Default mirror URL set to: ${DEFAULT_MIRROR}" + info "You can change the pacman mirror URL in /etc/pacman.d/mirrorlist according to your location:" + info " \$EDITOR ${JUNEST_HOME}/etc/pacman.d/mirrorlist" + echo info "Remember to refresh the package databases from the server:" info " pacman -Syy" - info "${NAME} installed successfully" + echo + info "To install packages from AUR follow the wiki here:" + info "https://github.com/fsquillace/junest#install-packages-from-aur" } @@ -70,7 +78,6 @@ function _setup_env(){ # the JuNest system from the image. # ENV_REPO (RO) : URL of the site containing JuNest images. # NAME (RO) : The JuNest name. -# DEFAULT_MIRROR (RO) : Arch Linux URL mirror. # Arguments: # arch ($1?) : The JuNest architecture image to download. # Defaults to the host architecture @@ -81,21 +88,22 @@ function _setup_env(){ ####################################### function setup_env(){ local arch=${1:-$ARCH} - contains_element $arch "${ARCH_LIST[@]}" || \ - die_on_status $NOT_AVAILABLE_ARCH "The architecture is not one of: ${ARCH_LIST[@]}" + contains_element "$arch" "${ARCH_LIST[@]}" || \ + die_on_status "$NOT_AVAILABLE_ARCH" "The architecture is not one of: ${ARCH_LIST[*]}" - local maindir=$(TMPDIR=$JUNEST_TEMPDIR mktemp -d -t ${CMD}.XXXXXXXXXX) - _prepare_build_directory $maindir + local maindir + maindir=$(TMPDIR=$JUNEST_TEMPDIR mktemp -d -t "${CMD}".XXXXXXXXXX) + _prepare_build_directory "$maindir" info "Downloading ${NAME}..." - builtin cd ${maindir} + builtin cd "${maindir}" || return 1 local imagefile=${CMD}-${arch}.tar.gz - download_cmd ${ENV_REPO}/${imagefile} + download_cmd "${ENV_REPO}/${imagefile}" info "Installing ${NAME}..." - _setup_env ${maindir}/${imagefile} + _setup_env "${maindir}/${imagefile}" - _cleanup_build_directory ${maindir} + _cleanup_build_directory "${maindir}" } ####################################### @@ -105,7 +113,6 @@ function setup_env(){ # JUNEST_HOME (RO) : The JuNest home directory in which JuNest needs # to be installed. # NAME (RO) : The JuNest name. -# DEFAULT_MIRROR (RO) : Arch Linux URL mirror. # Arguments: # imagefile ($1) : The JuNest image file. # Returns: @@ -116,10 +123,10 @@ function setup_env(){ function setup_env_from_file(){ local imagefile=$1 check_not_null "$imagefile" - [ ! -e ${imagefile} ] && die_on_status $NOT_EXISTING_FILE "Error: The ${NAME} image file ${imagefile} does not exist" + [[ ! -e ${imagefile} ]] && die_on_status "$NOT_EXISTING_FILE" "Error: The ${NAME} image file ${imagefile} does not exist" info "Installing ${NAME} from ${imagefile}..." - _setup_env ${imagefile} + _setup_env "${imagefile}" } ####################################### @@ -136,18 +143,18 @@ function setup_env_from_file(){ ####################################### function delete_env(){ ! ask "Are you sure to delete ${NAME} located in ${JUNEST_HOME}" "N" && return - if mountpoint -q ${JUNEST_HOME} + if mountpoint -q "${JUNEST_HOME}" then info "There are mounted directories inside ${JUNEST_HOME}" - if ! umount --force ${JUNEST_HOME} + if ! umount --force "${JUNEST_HOME}" then error "Cannot umount directories in ${JUNEST_HOME}" die "Try to delete ${NAME} using root permissions" fi fi # the CA directories are read only and can be deleted only by changing the mod - chmod -R +w ${JUNEST_HOME}/etc/ca-certificates - if rm_cmd -rf ${JUNEST_HOME} + chmod -R +w "${JUNEST_HOME}"/etc/ca-certificates + if rm_cmd -rf "${JUNEST_HOME}" then info "${NAME} deleted in ${JUNEST_HOME}" else diff --git a/lib/core/wrappers.sh b/lib/core/wrappers.sh new file mode 100644 index 0000000..1fe955c --- /dev/null +++ b/lib/core/wrappers.sh @@ -0,0 +1,60 @@ +#!/usr/bin/env bash +# +# Dependencies: +# None +# +# vim: ft=sh + +####################################### +# Create bin wrappers +# +# Globals: +# JUNEST_HOME (RO) : The JuNest home directory. +# Arguments: +# force ($1?) : Create bin wrappers even if the bin file exists. +# Defaults to false. +# Returns: +# None +# Output: +# None +####################################### +function create_wrappers() { + local force=${1:-false} + local bin_path=${2:-/usr/bin} + bin_path=${bin_path%/} + mkdir -p "${JUNEST_HOME}${bin_path}_wrappers" + # Arguments inside a variable (i.e. `JUNEST_ARGS`) separated by quotes + # are not recognized normally unless using `eval`. More info here: + # https://github.com/fsquillace/junest/issues/262 + # https://github.com/fsquillace/junest/pull/287 + cat < "${JUNEST_HOME}/usr/bin/junest_wrapper" +#!/usr/bin/env bash + +eval "junest_args_array=(\${JUNEST_ARGS:-ns})" +junest "\${junest_args_array[@]}" -- \$(basename \${0}) "\$@" +EOF + chmod +x "${JUNEST_HOME}/usr/bin/junest_wrapper" + + cd "${JUNEST_HOME}${bin_path}" || return 1 + for file in * + do + [[ -d $file ]] && continue + # Symlinks outside junest appear as broken even though they are correct + # within a junest session. The following do not skip broken symlinks: + [[ -x $file || -L $file ]] || continue + if [[ -e ${JUNEST_HOME}${bin_path}_wrappers/$file ]] && ! $force + then + continue + fi + rm -f "${JUNEST_HOME}${bin_path}_wrappers/$file" + ln -s "${JUNEST_HOME}/usr/bin/junest_wrapper" "${JUNEST_HOME}${bin_path}_wrappers/$file" + done + + # Remove wrappers no longer needed + cd "${JUNEST_HOME}${bin_path}_wrappers" || return 1 + for file in * + do + [[ -e ${JUNEST_HOME}${bin_path}/$file || -L ${JUNEST_HOME}${bin_path}/$file ]] || rm -f "$file" + done + +} diff --git a/lib/utils/utils.sh b/lib/utils/utils.sh index 00e2cb6..5659568 100644 --- a/lib/utils/utils.sh +++ b/lib/utils/utils.sh @@ -50,7 +50,7 @@ function echoerr() { # Message printed to stderr. ####################################### function die() { - error $@ + error "$@" exit 1 } @@ -70,8 +70,8 @@ function die() { function die_on_status() { status=$1 shift - error $@ - exit $status + error "$@" + exit "$status" } ####################################### @@ -87,7 +87,7 @@ function die_on_status() { # Message printed to stderr. ####################################### function error() { - echoerr -e "\033[1;31m$@\033[0m" + echoerr -e "\033[1;31m$*\033[0m" } ####################################### @@ -104,7 +104,7 @@ function error() { ####################################### function warn() { # $@: msg (mandatory) - str: Message to print - echoerr -e "\033[1;33m$@\033[0m" + echoerr -e "\033[1;33m$*\033[0m" } ####################################### @@ -120,7 +120,7 @@ function warn() { # Message printed to stdout. ####################################### function info(){ - echo -e "\033[1;36m$@\033[0m" + echo -e "\033[1;36m$*\033[0m" } ####################################### @@ -142,12 +142,12 @@ function info(){ function ask(){ local question=$1 local default_answer=$2 - check_not_null $question + check_not_null "$question" - if [ ! -z "$default_answer" ] + if [ -n "$default_answer" ] then local answers="Y y N n" - [[ "$answers" =~ "$default_answer" ]] || { error "The default answer: $default_answer is wrong."; return $WRONG_ANSWER; } + [[ "$answers" =~ $default_answer ]] || { error "The default answer: $default_answer is wrong."; return $WRONG_ANSWER; } fi local default="Y" @@ -156,12 +156,13 @@ function ask(){ local other="n" [ "$default" == "N" ] && other="y" - local prompt=$(info "$question (${default}/${other})> ") + local prompt + prompt=$(info "$question (${default}/${other})> ") local res="none" while [ "$res" != "Y" ] && [ "$res" != "N" ] && [ "$res" != "" ]; do - read -p "$prompt" res + read -r -p "$prompt" res res=$(echo "$res" | tr '[:lower:]' '[:upper:]') done @@ -170,36 +171,31 @@ 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 # to be used inside sh -c/bash -c - C='' + local C="" whitespace="[[:space:]]" for i in "$@" do if [[ $i =~ $whitespace ]] then - C="$C \"$i\"" + temp_C="\"$i\"" else - C="$C $i" + temp_C="$i" fi + + # Handle edge case when C is empty to avoid adding an extra space + if [[ -z $C ]] + then + C="$temp_C" + else + C="$C $temp_C" + fi + done - echo $C + echo "$C" } contains_element () { diff --git a/pkgs/sudo-fake/PKGBUILD b/pkgs/sudo-fake/PKGBUILD deleted file mode 100644 index 99fa18b..0000000 --- a/pkgs/sudo-fake/PKGBUILD +++ /dev/null @@ -1,45 +0,0 @@ -# Maintainer: Filippo Squillace -# More details on how to change this file: -# https://wiki.archlinux.org/index.php/PKGBUILD -# https://wiki.archlinux.org/index.php/Creating_packages -# https://wiki.archlinux.org/index.php/Arch_User_Repository#Submitting_packages - -pkgname=sudo-fake -pkgver=0.1.0 -pkgrel=1 -pkgdesc="Simple script that bypasses sudo and execute the actual command. Useful for fakeroot environments." -arch=('any') -url="" -license=('GPL') -groups=() -depends=() -makedepends=() -provides=('sudo') -conflicts=('sudo') -backup=() -options=() -#install= -source=() -md5sums=() -noextract=() - -package() { - install -d -m 755 "${pkgdir}/usr/bin/" - cat < "${pkgdir}/usr/bin/sudo" -#!/bin/bash -for opt in "\$@" -do - case "\$1" in - --) shift ; break ;; - -*) shift ;; - *) break ;; - esac -done - -[[ -z "\${@}" ]] || "\${@}" -EOF - - chmod 755 "${pkgdir}/usr/bin/sudo" -} - -# vim:set ts=2 sw=2 et: diff --git a/tests/checkstyle/checkstyle.sh b/tests/checkstyle/checkstyle.sh index 27cb82a..4f71965 100755 --- a/tests/checkstyle/checkstyle.sh +++ b/tests/checkstyle/checkstyle.sh @@ -1,6 +1,7 @@ #!/usr/bin/env bash +# shellcheck disable=SC1091 -source "$(dirname $0)/../utils/utils.sh" +source "$(dirname "$0")/../utils/utils.sh" # Disable the exiterr set +e @@ -10,12 +11,12 @@ function oneTimeSetUp(){ } function test_check_no_tabs(){ - assertCommandFailOnStatus 1 grep -R "$(printf '\t')" $(dirname $0)/../../bin/* - assertEquals "" "$(cat $STDOUTF)" - assertEquals "" "$(cat $STDERRF)" - assertCommandFailOnStatus 1 grep -R "$(printf '\t')" $(dirname $0)/../../lib/* - assertEquals "" "$(cat $STDOUTF)" - assertEquals "" "$(cat $STDERRF)" + assertCommandFailOnStatus 1 grep -R "$(printf '\t')" "$(dirname "$0")"/../../bin/* + assertEquals "" "$(cat "$STDOUTF")" + assertEquals "" "$(cat "$STDERRF")" + assertCommandFailOnStatus 1 grep -R "$(printf '\t')" "$(dirname "$0")"/../../lib/* + assertEquals "" "$(cat "$STDOUTF")" + assertEquals "" "$(cat "$STDERRF")" } -source $(dirname $0)/../utils/shunit2 +source "$(dirname "$0")"/../utils/shunit2 diff --git a/tests/integ-tests/install-bash.sh b/tests/integ-tests/install-bash.sh deleted file mode 100755 index 575c9c5..0000000 --- a/tests/integ-tests/install-bash.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/sh -set -ex - -VERSION=$1 - -cd /tmp -wget http://ftp.gnu.org/gnu/bash/bash-$VERSION.tar.gz - -tar -zxf bash-$VERSION.tar.gz -cd /tmp/bash-$VERSION* -./configure -make -sudo make install diff --git a/tests/unit-tests/test-chroot.sh b/tests/unit-tests/test-chroot.sh index b2e5e70..3739c58 100755 --- a/tests/unit-tests/test-chroot.sh +++ b/tests/unit-tests/test-chroot.sh @@ -1,6 +1,7 @@ #!/bin/bash +# shellcheck disable=SC1091 -JUNEST_ROOT=$(readlink -f $(dirname $0)/../..) +JUNEST_ROOT=$(readlink -f "$(dirname "$0")"/../..) source "$JUNEST_ROOT/tests/utils/utils.sh" @@ -28,25 +29,37 @@ function tearDown(){ function init_mocks() { chroot_cmd() { + # shellcheck disable=SC2317 [ "$JUNEST_ENV" != "1" ] && return 1 - echo "chroot_cmd $@" + # shellcheck disable=SC2317 + echo "chroot_cmd $*" } + # shellcheck disable=SC2034 GROOT=chroot_cmd + mychroot() { + # shellcheck disable=SC2317 + echo mychroot "$*" + } } function test_run_env_as_groot_cmd(){ - assertCommandSuccess run_env_as_groot "" "false" pwd - assertEquals "chroot_cmd -b $HOME -b /tmp -b /proc -b /sys -b /dev $JUNEST_HOME /bin/sh --login -c pwd" "$(cat $STDOUTF)" + assertCommandSuccess run_env_as_groot "" "" "false" pwd + assertEquals "chroot_cmd -b /run/user/$(id -u) -b $HOME -b /tmp -b /proc -b /sys -b /dev $JUNEST_HOME /bin/sh --login -c pwd" "$(cat "$STDOUTF")" } function test_run_env_as_groot_no_cmd(){ - assertCommandSuccess run_env_as_groot "" "false" "" - assertEquals "chroot_cmd -b $HOME -b /tmp -b /proc -b /sys -b /dev $JUNEST_HOME /bin/sh --login -c /bin/sh --login" "$(cat $STDOUTF)" + assertCommandSuccess run_env_as_groot "" "" "false" "" + assertEquals "chroot_cmd -b /run/user/$(id -u) -b $HOME -b /tmp -b /proc -b /sys -b /dev $JUNEST_HOME /bin/sh --login" "$(cat "$STDOUTF")" +} + +function test_run_env_as_groot_with_backend_command(){ + assertCommandSuccess run_env_as_groot "mychroot" "" "false" "" + assertEquals "mychroot -b /run/user/$(id -u) -b $HOME -b /tmp -b /proc -b /sys -b /dev $JUNEST_HOME /bin/sh --login" "$(cat "$STDOUTF")" } function test_run_env_as_groot_no_copy(){ - assertCommandSuccess run_env_as_groot "" "true" pwd - assertEquals "chroot_cmd -b $HOME -b /tmp -b /proc -b /sys -b /dev $JUNEST_HOME /bin/sh --login -c pwd" "$(cat $STDOUTF)" + assertCommandSuccess run_env_as_groot "" "" "true" pwd + assertEquals "chroot_cmd -b /run/user/$(id -u) -b $HOME -b /tmp -b /proc -b /sys -b /dev $JUNEST_HOME /bin/sh --login -c pwd" "$(cat "$STDOUTF")" [[ ! -e ${JUNEST_HOME}/etc/hosts ]] assertEquals 0 $? @@ -60,28 +73,33 @@ function test_run_env_as_groot_no_copy(){ function test_run_env_as_groot_nested_env(){ JUNEST_ENV=1 - assertCommandFailOnStatus 106 run_env_as_groot "" "false" "" + assertCommandFailOnStatus 106 run_env_as_groot "" "" "false" "" unset JUNEST_ENV } function test_run_env_as_groot_cmd_with_backend_args(){ - assertCommandSuccess run_env_as_groot "-n -b /home/blah" "false" 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)" + assertCommandSuccess run_env_as_groot "" "-n -b /home/blah" "false" pwd + assertEquals "chroot_cmd -b /run/user/$(id -u) -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 "" "false" pwd - assertEquals "chroot_cmd $JUNEST_HOME /bin/sh --login -c pwd" "$(cat $STDOUTF)" + assertCommandSuccess run_env_as_chroot "" "" "false" 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 "" "false" "" - assertEquals "chroot_cmd $JUNEST_HOME /bin/sh --login -c /bin/sh --login" "$(cat $STDOUTF)" + assertCommandSuccess run_env_as_chroot "" "" "false" "" + assertEquals "chroot_cmd $JUNEST_HOME /bin/sh --login" "$(cat "$STDOUTF")" +} + +function test_run_env_as_chroot_with_backend_command(){ + assertCommandSuccess run_env_as_chroot "mychroot" "" "false" "" + assertEquals "mychroot $JUNEST_HOME /bin/sh --login" "$(cat "$STDOUTF")" } function test_run_env_as_chroot_no_copy(){ - assertCommandSuccess run_env_as_chroot "" "true" pwd - assertEquals "chroot_cmd $JUNEST_HOME /bin/sh --login -c pwd" "$(cat $STDOUTF)" + assertCommandSuccess run_env_as_chroot "" "" "true" pwd + assertEquals "chroot_cmd $JUNEST_HOME /bin/sh --login -c pwd" "$(cat "$STDOUTF")" [[ ! -e ${JUNEST_HOME}/etc/hosts ]] assertEquals 0 $? @@ -95,13 +113,13 @@ function test_run_env_as_chroot_no_copy(){ function test_run_env_as_choot_nested_env(){ JUNEST_ENV=1 - assertCommandFailOnStatus 106 run_env_as_chroot "" "false" "" + assertCommandFailOnStatus 106 run_env_as_chroot "" "" "false" "" unset JUNEST_ENV } function test_run_env_as_chroot_cmd_with_backend_args(){ - assertCommandSuccess run_env_as_chroot "-n -b /home/blah" "false" pwd - assertEquals "chroot_cmd -n -b /home/blah $JUNEST_HOME /bin/sh --login -c pwd" "$(cat $STDOUTF)" + assertCommandSuccess run_env_as_chroot "" "-n -b /home/blah" "false" pwd + assertEquals "chroot_cmd -n -b /home/blah $JUNEST_HOME /bin/sh --login -c pwd" "$(cat "$STDOUTF")" } -source $JUNEST_ROOT/tests/utils/shunit2 +source "$JUNEST_ROOT"/tests/utils/shunit2 diff --git a/tests/unit-tests/test-common.sh b/tests/unit-tests/test-common.sh index dfff6f4..89c1a1e 100755 --- a/tests/unit-tests/test-common.sh +++ b/tests/unit-tests/test-common.sh @@ -1,6 +1,7 @@ #!/bin/bash +# shellcheck disable=SC1091 -JUNEST_ROOT=$(readlink -f $(dirname $0)/../..) +JUNEST_ROOT=$(readlink -f "$(dirname "$0")"/../..) source "$JUNEST_ROOT/tests/utils/utils.sh" @@ -21,47 +22,59 @@ function oneTimeTearDown(){ function setUp(){ ld_exec_mock() { - echo "ld_exec $@" + # shellcheck disable=SC2317 + echo "ld_exec $*" } + # shellcheck disable=SC2317 ld_exec_mock_false() { - echo "ld_exec $@" + echo "ld_exec $*" return 1 } + # shellcheck disable=SC2034 LD_EXEC=ld_exec_mock unshare_mock() { - echo "unshare $@" + # shellcheck disable=SC2317 + echo "unshare $*" } + # shellcheck disable=SC2034 UNSHARE=unshare_mock + # shellcheck disable=SC2317 + bwrap_mock() { + echo "bwrap $*" + } + # shellcheck disable=SC2034 + BWRAP=bwrap_mock + } function test_ln(){ - LN=echo assertCommandSuccess ln_cmd -s ln_file new_file - assertEquals "-s ln_file new_file" "$(cat $STDOUTF)" + LN="echo" assertCommandSuccess ln_cmd -s ln_file new_file + assertEquals "-s ln_file new_file" "$(cat "$STDOUTF")" LN=false assertCommandSuccess ln_cmd -s ln_file new_file - assertEquals "ld_exec ${JUNEST_HOME}/usr/bin/false -s ln_file new_file" "$(cat $STDOUTF)" + assertEquals "ld_exec ${JUNEST_HOME}/usr/bin/false -s ln_file new_file" "$(cat "$STDOUTF")" LN=false LD_EXEC=false assertCommandFail ln_cmd } function test_getent(){ - GETENT=echo assertCommandSuccess getent_cmd passwd - assertEquals "passwd" "$(cat $STDOUTF)" + GETENT="echo" assertCommandSuccess getent_cmd passwd + assertEquals "passwd" "$(cat "$STDOUTF")" - GETENT=false assertCommandSuccess getent_cmd passwd - assertEquals "ld_exec ${JUNEST_HOME}/usr/bin/false passwd" "$(cat $STDOUTF)" + GETENT="false" assertCommandSuccess getent_cmd passwd + assertEquals "ld_exec ${JUNEST_HOME}/usr/bin/false passwd" "$(cat "$STDOUTF")" GETENT=false LD_EXEC=false assertCommandFail getent_cmd } function test_cp(){ - CP=echo assertCommandSuccess cp_cmd passwd - assertEquals "passwd" "$(cat $STDOUTF)" + CP="echo" assertCommandSuccess cp_cmd passwd + assertEquals "passwd" "$(cat "$STDOUTF")" CP=false assertCommandSuccess cp_cmd passwd - assertEquals "ld_exec ${JUNEST_HOME}/usr/bin/false passwd" "$(cat $STDOUTF)" + assertEquals "ld_exec ${JUNEST_HOME}/usr/bin/false passwd" "$(cat "$STDOUTF")" CP=false LD_EXEC=false assertCommandFail cp_cmd } @@ -71,7 +84,9 @@ function test_download(){ CURL=/bin/false assertCommandSuccess download_cmd + # shellcheck disable=SC2034 WGET=/bin/false + # shellcheck disable=SC2034 CURL=/bin/true assertCommandSuccess download_cmd @@ -79,69 +94,77 @@ function test_download(){ } function test_rm(){ - RM=echo assertCommandSuccess rm_cmd rm_file - assertEquals "rm_file" "$(cat $STDOUTF)" + RM="echo" assertCommandSuccess rm_cmd rm_file + assertEquals "rm_file" "$(cat "$STDOUTF")" - RM=false assertCommandSuccess rm_cmd rm_file - assertEquals "ld_exec ${JUNEST_HOME}/usr/bin/false rm_file" "$(cat $STDOUTF)" + RM="false" assertCommandSuccess rm_cmd rm_file + assertEquals "ld_exec ${JUNEST_HOME}/usr/bin/false rm_file" "$(cat "$STDOUTF")" RM=false LD_EXEC=false assertCommandFail rm_cmd rm_file } function test_chown(){ - local id=$(id -u) + local id + id=$(id -u) - CHOWN=echo assertCommandSuccess chown_cmd $id chown_file - assertEquals "$id chown_file" "$(cat $STDOUTF)" + CHOWN="echo" assertCommandSuccess chown_cmd "$id" chown_file + assertEquals "$id chown_file" "$(cat "$STDOUTF")" - CHOWN=false assertCommandSuccess chown_cmd $id chown_file - assertEquals "ld_exec ${JUNEST_HOME}/usr/bin/false $id chown_file" "$(cat $STDOUTF)" + CHOWN="false" assertCommandSuccess chown_cmd "$id" chown_file + assertEquals "ld_exec ${JUNEST_HOME}/usr/bin/false $id chown_file" "$(cat "$STDOUTF")" - CHOWN=false LD_EXEC=false assertCommandFail chown_cmd $id chown_file + CHOWN=false LD_EXEC=false assertCommandFail chown_cmd "$id" chown_file } function test_mkdir(){ - MKDIR=echo assertCommandSuccess mkdir_cmd -p new_dir/new_dir - assertEquals "-p new_dir/new_dir" "$(cat $STDOUTF)" + MKDIR="echo" assertCommandSuccess mkdir_cmd -p new_dir/new_dir + assertEquals "-p new_dir/new_dir" "$(cat "$STDOUTF")" MKDIR=false assertCommandSuccess mkdir_cmd -p new_dir/new_dir - assertEquals "ld_exec ${JUNEST_HOME}/usr/bin/false -p new_dir/new_dir" "$(cat $STDOUTF)" + assertEquals "ld_exec ${JUNEST_HOME}/usr/bin/false -p new_dir/new_dir" "$(cat "$STDOUTF")" 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)" + 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 + 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 + echo -e "#!/bin/bash\necho zgrep" > "${JUNEST_HOME}"/usr/bin/false ZGREP=false assertCommandSuccess zgrep_cmd new_file - assertEquals "zgrep" "$(cat $STDOUTF)" + assertEquals "zgrep" "$(cat "$STDOUTF")" - echo -e "#!/bin/bash\nexit 1" > ${JUNEST_HOME}/usr/bin/false + 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)" + 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)" + 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_bwrap(){ + assertCommandSuccess bwrap_cmd new_program + assertEquals "$(echo -e "ld_exec $BWRAP --dev-bind / / /bin/sh -c :\nld_exec $BWRAP new_program")" "$(cat "$STDOUTF")" + + BWRAP=false LD_EXEC=false assertCommandFail bwrap_cmd new_program +} + function test_chroot(){ - CLASSIC_CHROOT=echo assertCommandSuccess chroot_cmd root - assertEquals "root" "$(cat $STDOUTF)" + 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)" + assertEquals "ld_exec $JUNEST_HOME/usr/bin/false root" "$(cat "$STDOUTF")" CLASSIC_CHROOT=false LD_EXEC=false assertCommandFail chroot_cmd root } @@ -154,38 +177,43 @@ function test_proot_cmd_compat(){ function test_proot_cmd_seccomp(){ envv(){ + # shellcheck disable=SC2317 env } PROOT=envv assertCommandSuccess proot_cmd cmd - assertEquals "" "$(cat $STDOUTF | grep "^PROOT_NO_SECCOMP")" + assertEquals "" "$(grep "^PROOT_NO_SECCOMP" "$STDOUTF")" envv(){ + # shellcheck disable=SC2317 env | grep "^PROOT_NO_SECCOMP" } + # shellcheck disable=SC2034 PROOT=envv assertCommandSuccess proot_cmd cmd # The variable PROOT_NO_SECCOMP will be produced # twice due to the fallback mechanism assertEquals "PROOT_NO_SECCOMP=1 -PROOT_NO_SECCOMP=1" "$(cat $STDOUTF | grep "^PROOT_NO_SECCOMP")" +PROOT_NO_SECCOMP=1" "$(grep "^PROOT_NO_SECCOMP" "$STDOUTF")" } function test_copy_passwd_and_group(){ getent_cmd_mock() { - echo $@ + # shellcheck disable=SC2317 + echo "$*" } 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)" + assertEquals "$(echo -e "passwd\npasswd $USER")" "$(cat "$JUNEST_HOME"/etc/passwd)" + assertEquals "group" "$(cat "$JUNEST_HOME"/etc/group)" } function test_copy_passwd_and_group_fallback(){ cp_cmd_mock() { - echo $@ + # shellcheck disable=SC2317 + echo "$*" } 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)" + assertEquals "$(echo -e "-f /etc/passwd $JUNEST_HOME//etc/passwd\n-f /etc/group $JUNEST_HOME//etc/group")" "$(cat "$STDOUTF")" } function test_copy_passwd_and_group_failure(){ @@ -201,14 +229,14 @@ function test_nested_env_not_set_variable(){ } function test_check_same_arch_not_same(){ - echo "JUNEST_ARCH=XXX" > ${JUNEST_HOME}/etc/junest/info + 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 + echo "JUNEST_ARCH=$ARCH" > "${JUNEST_HOME}"/etc/junest/info assertCommandSuccess check_same_arch } -source $JUNEST_ROOT/tests/utils/shunit2 +source "$JUNEST_ROOT"/tests/utils/shunit2 diff --git a/tests/unit-tests/test-groot.sh b/tests/unit-tests/test-groot.sh deleted file mode 100755 index 5fb2580..0000000 --- a/tests/unit-tests/test-groot.sh +++ /dev/null @@ -1,248 +0,0 @@ -#!/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 mountpoint_mock() { - echo "mountpoint($@)" - } - function mount() { - echo "mount($@)" - } - function umount() { - echo "umount($@)" - } - function check_and_trap() { - echo "check_and_trap($@)" - } - - # As default suppose the mountpoint "chrootdir" does not exist - echo -e "1 /home/mychroot/dev\n" > ./mounts - MOUNTS_FILE=./mounts -} - -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(){ - echo -e "1 /home/mychroot/dev\n1 chrootdir\n" > ./mounts - MOUNTS_FILE=./mounts - assertCommandSuccess main chrootdir - assertEquals "$(echo -e "check_and_trap(chroot_teardown QUIT EXIT ABRT KILL TERM INT)\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)\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)\nmount(--bind chrootdir chrootdir)\nmount(--bind /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)\nmount(--bind chrootdir chrootdir)\nmount(--bind ${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)\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)\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)\nmount(--bind chrootdir chrootdir)\nmount(--bind /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)\nmount(--bind chrootdir chrootdir)\nmount(--bind /tmp chrootdir/home/tmp)\nmount(--bind /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)\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)\nmount(--bind chrootdir chrootdir)\nmount(--bind /tmp chrootdir/home/tmp)\nmount(--bind /dev chrootdir/dev)\nchroot(chrootdir ls -la -h)")" "$(cat $STDOUTF)" -} -function test_groot_with_bind_no_umount(){ - assertCommandSuccess main -n chrootdir - assertEquals "$(echo -e "mount(--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_umount_failure(){ - function umount() { - echo "umount($@)" - [[ "$1" == "/home/mychroot/dev/shm" ]] && return 128 - return 0 - } - UMOUNT=umount - 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 assertCommandFailOnStatus 128 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)" -} -function test_groot_with_chroot_teardown_mountpoint_failure(){ - is_mountpoint() { - [[ $1 == "/home/mychroot/dev/shm" ]] && return 128 - return 0 - } - 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) -umount(/home/mychroot)")" "$(cat $STDOUTF)" -} - -function test_groot_with_rbind(){ - assertCommandSuccess main -r -b /tmp chrootdir - [[ -d chrootdir/tmp ]] - assertEquals 0 $? - assertEquals "$(echo -e "check_and_trap(chroot_teardown QUIT EXIT ABRT KILL TERM INT)\nmount(--bind chrootdir chrootdir)\nmount(--rbind /tmp chrootdir/tmp)\nchroot(chrootdir)")" "$(cat $STDOUTF)" -} - -function test_groot_with_avoid_bind_proc(){ - assertCommandSuccess main -i -b /proc chrootdir - [[ -d chrootdir/proc ]] - assertEquals 0 $? - assertEquals "$(echo -e "check_and_trap(chroot_teardown QUIT EXIT ABRT KILL TERM INT)\nmount(--bind chrootdir chrootdir)\nmount(proc chrootdir/proc -t proc)\nchroot(chrootdir)")" "$(cat $STDOUTF)" -} - -function test_groot_with_avoid_bind_dev(){ - assertCommandSuccess main -i -b /dev chrootdir - [[ -d chrootdir/dev ]] - assertEquals 0 $? - assertEquals "$(echo -e "check_and_trap(chroot_teardown QUIT EXIT ABRT KILL TERM INT)\nmount(--bind chrootdir chrootdir)\nmount(udev chrootdir/dev -t devtmpfs)\nmount(devpts /dev/pts -t devpts)\nmount(shm /dev/shm -t tmpfs)\nchroot(chrootdir)")" "$(cat $STDOUTF)" -} - -function test_groot_with_avoid_bind_sys(){ - assertCommandSuccess main -i -b /sys chrootdir - [[ -d chrootdir/sys ]] - assertEquals 0 $? - assertEquals "$(echo -e "check_and_trap(chroot_teardown QUIT EXIT ABRT KILL TERM INT)\nmount(--bind chrootdir chrootdir)\nmount(sys chrootdir/sys -t sysfs)\nchroot(chrootdir)")" "$(cat $STDOUTF)" -} - -function test_groot_with_avoid_bind_run(){ - assertCommandSuccess main -i -b /run chrootdir - [[ -d chrootdir/run ]] - assertEquals 0 $? - assertEquals "$(echo -e "check_and_trap(chroot_teardown QUIT EXIT ABRT KILL TERM INT)\nmount(--bind chrootdir chrootdir)\nmount(run chrootdir/run -t tmpfs)\nchroot(chrootdir)")" "$(cat $STDOUTF)" -} - -function test_groot_with_avoid_bind_tmp(){ - assertCommandSuccess main -i -b /tmp chrootdir - [[ -d chrootdir/tmp ]] - assertEquals 0 $? - assertEquals "$(echo -e "check_and_trap(chroot_teardown QUIT EXIT ABRT KILL TERM INT)\nmount(--bind chrootdir chrootdir)\nmount(tmp chrootdir/tmp -t tmpfs)\nchroot(chrootdir)")" "$(cat $STDOUTF)" -} - -function test_groot_with_avoid_bind_combined(){ - assertCommandSuccess main -i -b /tmp -b /usr chrootdir - cat $STDERRF - [[ -d chrootdir/tmp ]] - assertEquals 0 $? - [[ -d chrootdir/usr ]] - assertEquals 0 $? - assertEquals "$(echo -e "check_and_trap(chroot_teardown QUIT EXIT ABRT KILL TERM INT)\nmount(--bind chrootdir chrootdir)\nmount(tmp chrootdir/tmp -t tmpfs)\nmount(--bind /usr chrootdir/usr)\nchroot(chrootdir)")" "$(cat $STDOUTF)" -} - -source $(dirname $0)/../utils/shunit2 diff --git a/tests/unit-tests/test-junest.sh b/tests/unit-tests/test-junest.sh index 98c6cb2..07a92b1 100755 --- a/tests/unit-tests/test-junest.sh +++ b/tests/unit-tests/test-junest.sh @@ -1,8 +1,10 @@ #!/bin/bash -source "$(dirname $0)/../utils/utils.sh" +# shellcheck disable=SC1091 -JUNEST_BASE="$(dirname $0)/../.." -source $JUNEST_BASE/bin/junest -h &> /dev/null +source "$(dirname "$0")/../utils/utils.sh" + +JUNEST_BASE="$(dirname "$0")/../.." +source "$JUNEST_BASE"/bin/junest -h &> /dev/null # Disable the exiterr set +e @@ -12,100 +14,143 @@ function oneTimeSetUp(){ } function setUp(){ + ## Mock functions ## + # shellcheck disable=SC2317 + function usage(){ + echo "usage" + } + # shellcheck disable=SC2317 + function version(){ + echo "version" + } + # shellcheck disable=SC2317 + function build_image_env(){ + local disable_check=$1 + echo "build_image_env($disable_check)" + } + # shellcheck disable=SC2317 + function delete_env(){ + echo "delete_env" + } + # shellcheck disable=SC2317 + function setup_env_from_file(){ + echo "setup_env_from_file($1)" + } + # shellcheck disable=SC2317 + function setup_env(){ + echo "setup_env($1)" + } + # shellcheck disable=SC2317 + function run_env_as_proot_fakeroot(){ + local backend_command="$1" + local backend_args="$2" + local no_copy_files="$3" + shift 3 + echo "run_env_as_proot_fakeroot($backend_command,$backend_args,$no_copy_files,$*)" + } + # shellcheck disable=SC2317 + function run_env_as_groot(){ + local backend_command="$1" + local backend_args="$2" + local no_copy_files="$3" + shift 3 + echo "run_env_as_groot($backend_command,$backend_args,$no_copy_files,$*)" + } + # shellcheck disable=SC2317 + function run_env_as_chroot(){ + local backend_command="$1" + local backend_args="$2" + local no_copy_files="$3" + shift 3 + echo "run_env_as_chroot($backend_command,$backend_args,$no_copy_files,$*)" + } + # shellcheck disable=SC2317 + function run_env_as_proot_user(){ + local backend_command="$1" + local backend_args="$2" + local no_copy_files="$3" + shift 3 + echo "run_env_as_proot_user($backend_command,$backend_args,$no_copy_files,$*)" + } + # shellcheck disable=SC2317 + function run_env_as_bwrap_fakeroot(){ + local backend_command="$1" + local backend_args="$2" + local no_copy_files="$3" + shift 3 + echo "run_env_as_bwrap_fakeroot($backend_command,$backend_args,$no_copy_files,$*)" + } + # shellcheck disable=SC2317 + function run_env_as_bwrap_user(){ + local backend_command="$1" + local backend_args="$2" + local no_copy_files="$3" + shift 3 + echo "run_env_as_bwrap_user($backend_command,$backend_args,$no_copy_files,$*)" + } + # shellcheck disable=SC2317 function is_env_installed(){ return 0 } -} - -## Mock functions ## -function usage(){ - echo "usage" -} -function version(){ - echo "version" -} -function build_image_env(){ - local disable_check=$1 - echo "build_image_env($disable_check)" -} -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" - local no_copy_files="$2" - shift 2 - echo "run_env_as_fakeroot($backend_args,$no_copy_files,$@)" -} -function run_env_as_groot(){ - local backend_args="$1" - local no_copy_files="$2" - shift 2 - echo "run_env_as_groot($backend_args,$no_copy_files,$@)" -} -function run_env_as_chroot(){ - local backend_args="$1" - local no_copy_files="$2" - shift 2 - echo "run_env_as_chroot($backend_args,$no_copy_files,$@)" -} -function run_env_as_user(){ - local backend_args="$1" - local no_copy_files="$2" - shift 2 - echo "run_env_as_user($backend_args,$no_copy_files,$@)" -} -function run_env_with_namespace(){ - local backend_args="$1" - local no_copy_files="$2" - shift 2 - echo "run_env_with_namespace($backend_args,$no_copy_files,$@)" + # shellcheck disable=SC2317 + function create_wrappers(){ + : + } } function test_help(){ assertCommandSuccess main -h - assertEquals "usage" "$(cat $STDOUTF)" + assertEquals "usage" "$(cat "$STDOUTF")" assertCommandSuccess main --help - assertEquals "usage" "$(cat $STDOUTF)" + assertEquals "usage" "$(cat "$STDOUTF")" } function test_version(){ assertCommandSuccess main -V - assertEquals "version" "$(cat $STDOUTF)" + assertEquals "version" "$(cat "$STDOUTF")" assertCommandSuccess main --version - assertEquals "version" "$(cat $STDOUTF)" + assertEquals "version" "$(cat "$STDOUTF")" } function test_build_image_env(){ assertCommandSuccess main b - assertEquals "build_image_env(false)" "$(cat $STDOUTF)" + assertEquals "build_image_env(false)" "$(cat "$STDOUTF")" assertCommandSuccess main build - assertEquals "build_image_env(false)" "$(cat $STDOUTF)" + assertEquals "build_image_env(false)" "$(cat "$STDOUTF")" assertCommandSuccess main b -n - assertEquals "build_image_env(true)" "$(cat $STDOUTF)" + assertEquals "build_image_env(true)" "$(cat "$STDOUTF")" assertCommandSuccess main build --disable-check - assertEquals "build_image_env(true)" "$(cat $STDOUTF)" + assertEquals "build_image_env(true)" "$(cat "$STDOUTF")" +} + +function test_create_wrappers(){ + # shellcheck disable=SC2317 + function create_wrappers(){ + local force=$1 + echo "create_wrappers($force)" + } + assertCommandSuccess main create-bin-wrappers + assertEquals "create_wrappers(false)" "$(cat "$STDOUTF")" + + assertCommandSuccess main create-bin-wrappers --force + assertEquals "create_wrappers(true)" "$(cat "$STDOUTF")" } function test_delete_env(){ assertCommandSuccess main s -d - assertEquals "delete_env" "$(cat $STDOUTF)" + assertEquals "delete_env" "$(cat "$STDOUTF")" assertCommandSuccess main setup --delete - assertEquals "delete_env" "$(cat $STDOUTF)" + assertEquals "delete_env" "$(cat "$STDOUTF")" } function test_setup_env_from_file(){ + # shellcheck disable=SC2317 is_env_installed(){ return 1 } assertCommandSuccess main s -i myimage - assertEquals "setup_env_from_file(myimage)" "$(cat $STDOUTF)" + assertEquals "setup_env_from_file(myimage)" "$(cat "$STDOUTF")" assertCommandSuccess main setup --from-file myimage - assertEquals "setup_env_from_file(myimage)" "$(cat $STDOUTF)" + assertEquals "setup_env_from_file(myimage)" "$(cat "$STDOUTF")" + # shellcheck disable=SC2317 is_env_installed(){ return 0 } @@ -113,41 +158,49 @@ function test_setup_env_from_file(){ } function test_setup_env(){ + # shellcheck disable=SC2317 is_env_installed(){ return 1 } assertCommandSuccess main s - assertEquals "setup_env()" "$(cat $STDOUTF)" + assertEquals "setup_env()" "$(cat "$STDOUTF")" assertCommandSuccess main setup - assertEquals "setup_env()" "$(cat $STDOUTF)" + assertEquals "setup_env()" "$(cat "$STDOUTF")" assertCommandSuccess main s -a arm - assertEquals "setup_env(arm)" "$(cat $STDOUTF)" + assertEquals "setup_env(arm)" "$(cat "$STDOUTF")" assertCommandSuccess main setup --arch arm - assertEquals "setup_env(arm)" "$(cat $STDOUTF)" + assertEquals "setup_env(arm)" "$(cat "$STDOUTF")" + # shellcheck disable=SC2317 is_env_installed(){ return 0 } assertCommandFail main setup -a arm } -function test_run_env_as_fakeroot(){ +function test_run_env_as_proot_fakeroot(){ assertCommandSuccess main p -f - assertEquals "run_env_as_fakeroot(,false,)" "$(cat $STDOUTF)" + assertEquals "run_env_as_proot_fakeroot(,,false,)" "$(cat "$STDOUTF")" assertCommandSuccess main proot --fakeroot - assertEquals "run_env_as_fakeroot(,false,)" "$(cat $STDOUTF)" + assertEquals "run_env_as_proot_fakeroot(,,false,)" "$(cat "$STDOUTF")" assertCommandSuccess main p -f -n - assertEquals "run_env_as_fakeroot(,true,)" "$(cat $STDOUTF)" + assertEquals "run_env_as_proot_fakeroot(,,true,)" "$(cat "$STDOUTF")" + + assertCommandSuccess main p -f --backend-command blah + assertEquals "run_env_as_proot_fakeroot(blah,,false,)" "$(cat "$STDOUTF")" + assertCommandSuccess main proot -f --backend-command blah + assertEquals "run_env_as_proot_fakeroot(blah,,false,)" "$(cat "$STDOUTF")" assertCommandSuccess main proot -f -b "-b arg" - assertEquals "run_env_as_fakeroot(-b arg,false,)" "$(cat $STDOUTF)" + assertEquals "run_env_as_proot_fakeroot(,-b arg,false,)" "$(cat "$STDOUTF")" assertCommandSuccess main proot -f -b "-b arg" -- command -kv - assertEquals "run_env_as_fakeroot(-b arg,false,command -kv)" "$(cat $STDOUTF)" + assertEquals "run_env_as_proot_fakeroot(,-b arg,false,command -kv)" "$(cat "$STDOUTF")" assertCommandSuccess main proot -f command --as - assertEquals "run_env_as_fakeroot(,false,command --as)" "$(cat $STDOUTF)" + assertEquals "run_env_as_proot_fakeroot(,,false,command --as)" "$(cat "$STDOUTF")" assertCommandSuccess main proot -f -- command --as - assertEquals "run_env_as_fakeroot(,false,command --as)" "$(cat $STDOUTF)" + assertEquals "run_env_as_proot_fakeroot(,,false,command --as)" "$(cat "$STDOUTF")" + # shellcheck disable=SC2317 is_env_installed(){ return 1 } @@ -156,19 +209,25 @@ function test_run_env_as_fakeroot(){ function test_run_env_as_user(){ assertCommandSuccess main proot - assertEquals "run_env_as_user(,false,)" "$(cat $STDOUTF)" + assertEquals "run_env_as_proot_user(,,false,)" "$(cat "$STDOUTF")" assertCommandSuccess main p -n - assertEquals "run_env_as_user(,true,)" "$(cat $STDOUTF)" + assertEquals "run_env_as_proot_user(,,true,)" "$(cat "$STDOUTF")" + + assertCommandSuccess main p --backend-command blah + assertEquals "run_env_as_proot_user(blah,,false,)" "$(cat "$STDOUTF")" + assertCommandSuccess main proot --backend-command blah + assertEquals "run_env_as_proot_user(blah,,false,)" "$(cat "$STDOUTF")" assertCommandSuccess main proot -b "-b arg" - assertEquals "run_env_as_user(-b arg,false,)" "$(cat $STDOUTF)" + assertEquals "run_env_as_proot_user(,-b arg,false,)" "$(cat "$STDOUTF")" assertCommandSuccess main proot -b "-b arg" -- command -ll - assertEquals "run_env_as_user(-b arg,false,command -ll)" "$(cat $STDOUTF)" + assertEquals "run_env_as_proot_user(,-b arg,false,command -ll)" "$(cat "$STDOUTF")" assertCommandSuccess main proot command -ls - assertEquals "run_env_as_user(,false,command -ls)" "$(cat $STDOUTF)" + assertEquals "run_env_as_proot_user(,,false,command -ls)" "$(cat "$STDOUTF")" assertCommandSuccess main proot -- command -ls - assertEquals "run_env_as_user(,false,command -ls)" "$(cat $STDOUTF)" + assertEquals "run_env_as_proot_user(,,false,command -ls)" "$(cat "$STDOUTF")" + # shellcheck disable=SC2317 is_env_installed(){ return 1 } @@ -177,16 +236,23 @@ function test_run_env_as_user(){ function test_run_env_as_groot(){ assertCommandSuccess main g - assertEquals "run_env_as_groot(,false,)" "$(cat $STDOUTF)" + assertEquals "run_env_as_groot(,,false,)" "$(cat "$STDOUTF")" assertCommandSuccess main g -n - assertEquals "run_env_as_groot(,true,)" "$(cat $STDOUTF)" + assertEquals "run_env_as_groot(,,true,)" "$(cat "$STDOUTF")" assertCommandSuccess main g -b "-b arg" - assertEquals "run_env_as_groot(-b arg,false,)" "$(cat $STDOUTF)" - assertCommandSuccess main groot command - assertEquals "run_env_as_groot(,false,command)" "$(cat $STDOUTF)" - assertCommandSuccess main groot -- command - assertEquals "run_env_as_groot(,false,command)" "$(cat $STDOUTF)" + assertEquals "run_env_as_groot(,-b arg,false,)" "$(cat "$STDOUTF")" + assertCommandSuccess main g --backend-command blah + assertEquals "run_env_as_groot(blah,,false,)" "$(cat "$STDOUTF")" + assertCommandSuccess main groot --backend-command blah + assertEquals "run_env_as_groot(blah,,false,)" "$(cat "$STDOUTF")" + + assertCommandSuccess main groot command + assertEquals "run_env_as_groot(,,false,command)" "$(cat "$STDOUTF")" + assertCommandSuccess main groot -- command + assertEquals "run_env_as_groot(,,false,command)" "$(cat "$STDOUTF")" + + # shellcheck disable=SC2317 is_env_installed(){ return 1 } @@ -195,51 +261,107 @@ function test_run_env_as_groot(){ function test_run_env_as_chroot(){ assertCommandSuccess main r - assertEquals "run_env_as_chroot(,false,)" "$(cat $STDOUTF)" + assertEquals "run_env_as_chroot(,,false,)" "$(cat "$STDOUTF")" assertCommandSuccess main r -b "-b arg" - assertEquals "run_env_as_chroot(-b arg,false,)" "$(cat $STDOUTF)" - assertCommandSuccess main root command - assertEquals "run_env_as_chroot(,false,command)" "$(cat $STDOUTF)" - assertCommandSuccess main root -- command - assertEquals "run_env_as_chroot(,false,command)" "$(cat $STDOUTF)" + assertEquals "run_env_as_chroot(,-b arg,false,)" "$(cat "$STDOUTF")" + assertCommandSuccess main r --backend-command blah + assertEquals "run_env_as_chroot(blah,,false,)" "$(cat "$STDOUTF")" + assertCommandSuccess main root --backend-command blah + assertEquals "run_env_as_chroot(blah,,false,)" "$(cat "$STDOUTF")" + + assertCommandSuccess main root command + assertEquals "run_env_as_chroot(,,false,command)" "$(cat "$STDOUTF")" + assertCommandSuccess main root -- command + assertEquals "run_env_as_chroot(,,false,command)" "$(cat "$STDOUTF")" + + # shellcheck disable=SC2317 is_env_installed(){ return 1 } assertCommandFail main root -f } -function test_run_env_with_namespace(){ +function test_run_env_as_bwrap_fakeroot(){ + assertCommandSuccess main n -f + assertEquals "run_env_as_bwrap_fakeroot(,,false,)" "$(cat "$STDOUTF")" + assertCommandSuccess main ns -f + assertEquals "run_env_as_bwrap_fakeroot(,,false,)" "$(cat "$STDOUTF")" + assertCommandSuccess main ns -n -f + assertEquals "run_env_as_bwrap_fakeroot(,,true,)" "$(cat "$STDOUTF")" + + assertCommandSuccess main ns -f -b "-b arg" + assertEquals "run_env_as_bwrap_fakeroot(,-b arg,false,)" "$(cat "$STDOUTF")" + assertCommandSuccess main ns -f -b "-b arg" -- command -kv + assertEquals "run_env_as_bwrap_fakeroot(,-b arg,false,command -kv)" "$(cat "$STDOUTF")" + assertCommandSuccess main ns -f command --as + assertEquals "run_env_as_bwrap_fakeroot(,,false,command --as)" "$(cat "$STDOUTF")" + assertCommandSuccess main ns -f -- command --as + assertEquals "run_env_as_bwrap_fakeroot(,,false,command --as)" "$(cat "$STDOUTF")" + + assertCommandSuccess main ns -f --backend-command blah + assertEquals "run_env_as_bwrap_fakeroot(blah,,false,)" "$(cat "$STDOUTF")" + assertCommandSuccess main -f --backend-command blah + assertEquals "run_env_as_bwrap_fakeroot(blah,,false,)" "$(cat "$STDOUTF")" + + assertCommandSuccess main -f + assertEquals "run_env_as_bwrap_fakeroot(,,false,)" "$(cat "$STDOUTF")" + assertCommandSuccess main -f + assertEquals "run_env_as_bwrap_fakeroot(,,false,)" "$(cat "$STDOUTF")" + + assertCommandSuccess main -f -b "-b arg" + assertEquals "run_env_as_bwrap_fakeroot(,-b arg,false,)" "$(cat "$STDOUTF")" + assertCommandSuccess main -f -b "-b arg" -- command -kv + assertEquals "run_env_as_bwrap_fakeroot(,-b arg,false,command -kv)" "$(cat "$STDOUTF")" + assertCommandSuccess main -f command --as + assertEquals "run_env_as_bwrap_fakeroot(,,false,command --as)" "$(cat "$STDOUTF")" + assertCommandSuccess main -f -- command --as + assertEquals "run_env_as_bwrap_fakeroot(,,false,command --as)" "$(cat "$STDOUTF")" + + # shellcheck disable=SC2317 + is_env_installed(){ + return 1 + } + assertCommandFail main ns -f +} + +function test_run_env_as_bwrap_user(){ assertCommandSuccess main n - assertEquals "run_env_with_namespace(,false,)" "$(cat $STDOUTF)" + assertEquals "run_env_as_bwrap_user(,,false,)" "$(cat "$STDOUTF")" assertCommandSuccess main ns - assertEquals "run_env_with_namespace(,false,)" "$(cat $STDOUTF)" + assertEquals "run_env_as_bwrap_user(,,false,)" "$(cat "$STDOUTF")" assertCommandSuccess main ns -n - assertEquals "run_env_with_namespace(,true,)" "$(cat $STDOUTF)" + assertEquals "run_env_as_bwrap_user(,,true,)" "$(cat "$STDOUTF")" assertCommandSuccess main ns -b "-b arg" - assertEquals "run_env_with_namespace(-b arg,false,)" "$(cat $STDOUTF)" + assertEquals "run_env_as_bwrap_user(,-b arg,false,)" "$(cat "$STDOUTF")" assertCommandSuccess main ns -b "-b arg" -- command -kv - assertEquals "run_env_with_namespace(-b arg,false,command -kv)" "$(cat $STDOUTF)" + assertEquals "run_env_as_bwrap_user(,-b arg,false,command -kv)" "$(cat "$STDOUTF")" assertCommandSuccess main ns command --as - assertEquals "run_env_with_namespace(,false,command --as)" "$(cat $STDOUTF)" + assertEquals "run_env_as_bwrap_user(,,false,command --as)" "$(cat "$STDOUTF")" assertCommandSuccess main ns -- command --as - assertEquals "run_env_with_namespace(,false,command --as)" "$(cat $STDOUTF)" + assertEquals "run_env_as_bwrap_user(,,false,command --as)" "$(cat "$STDOUTF")" + + assertCommandSuccess main ns --backend-command blah + assertEquals "run_env_as_bwrap_user(blah,,false,)" "$(cat "$STDOUTF")" + assertCommandSuccess main --backend-command blah + assertEquals "run_env_as_bwrap_user(blah,,false,)" "$(cat "$STDOUTF")" assertCommandSuccess main - assertEquals "run_env_with_namespace(,false,)" "$(cat $STDOUTF)" + assertEquals "run_env_as_bwrap_user(,,false,)" "$(cat "$STDOUTF")" assertCommandSuccess main - assertEquals "run_env_with_namespace(,false,)" "$(cat $STDOUTF)" + assertEquals "run_env_as_bwrap_user(,,false,)" "$(cat "$STDOUTF")" assertCommandSuccess main -b "-b arg" - assertEquals "run_env_with_namespace(-b arg,false,)" "$(cat $STDOUTF)" + assertEquals "run_env_as_bwrap_user(,-b arg,false,)" "$(cat "$STDOUTF")" assertCommandSuccess main -b "-b arg" -- command -kv - assertEquals "run_env_with_namespace(-b arg,false,command -kv)" "$(cat $STDOUTF)" + assertEquals "run_env_as_bwrap_user(,-b arg,false,command -kv)" "$(cat "$STDOUTF")" assertCommandSuccess main command --as - assertEquals "run_env_with_namespace(,false,command --as)" "$(cat $STDOUTF)" + assertEquals "run_env_as_bwrap_user(,,false,command --as)" "$(cat "$STDOUTF")" assertCommandSuccess main -- command --as - assertEquals "run_env_with_namespace(,false,command --as)" "$(cat $STDOUTF)" + assertEquals "run_env_as_bwrap_user(,,false,command --as)" "$(cat "$STDOUTF")" + # shellcheck disable=SC2317 is_env_installed(){ return 1 } @@ -258,4 +380,4 @@ function test_invalid_option(){ assertCommandFail main s --no-option } -source $(dirname $0)/../utils/shunit2 +source "$(dirname "$0")"/../utils/shunit2 diff --git a/tests/unit-tests/test-namespace.sh b/tests/unit-tests/test-namespace.sh index 4ecc794..7a845aa 100755 --- a/tests/unit-tests/test-namespace.sh +++ b/tests/unit-tests/test-namespace.sh @@ -1,6 +1,7 @@ #!/bin/bash +# shellcheck disable=SC1091 -JUNEST_ROOT=$(readlink -f $(dirname $0)/../..) +JUNEST_ROOT=$(readlink -f "$(dirname "$0")"/../..) source "$JUNEST_ROOT/tests/utils/utils.sh" @@ -15,8 +16,9 @@ function oneTimeSetUp(){ ## Mock functions ## function init_mocks() { - function unshare_cmd(){ - echo "unshare $@" + # shellcheck disable=SC2317 + function bwrap_cmd(){ + echo "$BWRAP $*" } } @@ -39,16 +41,16 @@ function tearDown(){ } 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)" + [[ -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 /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 $? @@ -57,59 +59,78 @@ function _test_copy_remaining_files() { } function test_is_user_namespace_enabled_no_config_file(){ + PROC_USERNS_FILE="blah" + PROC_USERNS_CLONE_FILE="blah" CONFIG_PROC_FILE="blah" CONFIG_BOOT_FILE="blah" - assertCommandFailOnStatus $NOT_EXISTING_FILE _is_user_namespace_enabled + assertCommandFailOnStatus "$NOT_EXISTING_FILE" _is_user_namespace_enabled } function test_is_user_namespace_enabled_no_config(){ + PROC_USERNS_FILE="blah" + PROC_USERNS_CLONE_FILE="blah" touch config gzip config + # shellcheck disable=SC2034 CONFIG_PROC_FILE="config.gz" + # shellcheck disable=SC2034 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" - PROC_USERNS_CLONE_FILE="not-existing-file" - assertCommandSuccess _is_user_namespace_enabled + assertCommandFailOnStatus "$NO_CONFIG_FOUND" _is_user_namespace_enabled } function test_is_user_namespace_enabled_with_userns_clone_file_disabled(){ - echo "CONFIG_USER_NS=y" > config - gzip config - CONFIG_PROC_FILE="config.gz" - CONFIG_BOOT_FILE="blah" + PROC_USERNS_FILE="blah" PROC_USERNS_CLONE_FILE="unprivileged_userns_clone" echo "0" > $PROC_USERNS_CLONE_FILE - assertCommandFailOnStatus $UNPRIVILEGED_USERNS_DISABLED _is_user_namespace_enabled + assertCommandFailOnStatus "$UNPRIVILEGED_USERNS_DISABLED" _is_user_namespace_enabled } function test_is_user_namespace_enabled_with_userns_clone_file_enabled(){ - echo "CONFIG_USER_NS=y" > config - gzip config - CONFIG_PROC_FILE="config.gz" - CONFIG_BOOT_FILE="blah" PROC_USERNS_CLONE_FILE="unprivileged_userns_clone" echo "1" > $PROC_USERNS_CLONE_FILE assertCommandSuccess _is_user_namespace_enabled } -function test_run_env_with_namespace() { - assertCommandSuccess run_env_with_namespace "" "false" "" - assertEquals "unshare --mount --user --map-root-user $GROOT --no-umount --recursive -b $HOME -b /tmp -b /proc -b /sys -b /dev $JUNEST_HOME /bin/sh --login" "$(cat $STDOUTF)" +function test_is_user_namespace_enabled_with_proc_userns_file_existing(){ + PROC_USERNS_FILE="user" + ln -s . $PROC_USERNS_FILE + PROC_USERNS_CLONE_FILE="blah" + assertCommandSuccess _is_user_namespace_enabled +} + +function test_run_env_as_bwrap_fakeroot() { + assertCommandSuccess run_env_as_bwrap_fakeroot "" "" "false" + assertEquals "$BWRAP $COMMON_BWRAP_OPTION --cap-add ALL --uid 0 --gid 0 sudo /bin/sh --login" "$(cat "$STDOUTF")" + + _test_copy_common_files +} + +function test_run_env_as_bwrap_fakeroot_with_backend_command() { + assertCommandSuccess run_env_as_bwrap_fakeroot "mybwrap" "" "false" + assertEquals "mybwrap $COMMON_BWRAP_OPTION --cap-add ALL --uid 0 --gid 0 sudo /bin/sh --login" "$(cat "$STDOUTF")" + + _test_copy_common_files +} + +function test_run_env_as_bwrap_user() { + assertCommandSuccess run_env_as_bwrap_user "" "" "false" + assertEquals "$BWRAP $COMMON_BWRAP_OPTION /bin/sh --login" "$(cat "$STDOUTF")" _test_copy_common_files _test_copy_remaining_files } -function test_run_env_with_namespace_no_copy() { - assertCommandSuccess run_env_with_namespace "" "true" "" - assertEquals "unshare --mount --user --map-root-user $GROOT --no-umount --recursive -b $HOME -b /tmp -b /proc -b /sys -b /dev $JUNEST_HOME /bin/sh --login" "$(cat $STDOUTF)" +function test_run_env_as_bwrap_user_with_backend_command() { + assertCommandSuccess run_env_as_bwrap_user "mybwrap" "" "false" + assertEquals "mybwrap $COMMON_BWRAP_OPTION /bin/sh --login" "$(cat "$STDOUTF")" + + _test_copy_common_files + _test_copy_remaining_files +} + +function test_run_env_as_bwrap_fakeroot_no_copy() { + assertCommandSuccess run_env_as_bwrap_fakeroot "" "" "true" "" + assertEquals "$BWRAP $COMMON_BWRAP_OPTION --cap-add ALL --uid 0 --gid 0 sudo /bin/sh --login" "$(cat "$STDOUTF")" [[ ! -e ${JUNEST_HOME}/etc/hosts ]] assertEquals 0 $? @@ -133,34 +154,88 @@ function test_run_env_with_namespace_no_copy() { assertEquals 0 $? } -function test_run_env_with_namespace_with_bindings() { - assertCommandSuccess run_env_with_namespace "-b /usr -b /lib:/tmp/lib" "false" "" - assertEquals "unshare --mount --user --map-root-user $GROOT --no-umount --recursive -b $HOME -b /tmp -b /proc -b /sys -b /dev -b /usr -b /lib:/tmp/lib $JUNEST_HOME /bin/sh --login" "$(cat $STDOUTF)" +function test_run_env_as_bwrap_user_no_copy() { + assertCommandSuccess run_env_as_bwrap_user "" "" "true" "" + assertEquals "$BWRAP $COMMON_BWRAP_OPTION /bin/sh --login" "$(cat "$STDOUTF")" + + [[ ! -e ${JUNEST_HOME}/etc/hosts ]] + assertEquals 0 $? + [[ ! -e ${JUNEST_HOME}/etc/host.conf ]] + assertEquals 0 $? + [[ ! -e ${JUNEST_HOME}/etc/nsswitch.conf ]] + assertEquals 0 $? + [[ ! -e ${JUNEST_HOME}/etc/resolv.conf ]] + assertEquals 0 $? + + [[ ! -e ${JUNEST_HOME}/etc/hosts.equiv ]] + assertEquals 0 $? + [[ ! -e ${JUNEST_HOME}/etc/netgroup ]] + assertEquals 0 $? + [[ ! -e ${JUNEST_HOME}/etc/networks ]] + assertEquals 0 $? + + [[ ! -e ${JUNEST_HOME}/etc/passwd ]] + assertEquals 0 $? + [[ ! -e ${JUNEST_HOME}/etc/group ]] + assertEquals 0 $? +} + +function test_run_env_as_bwrap_fakeroot_with_backend_args() { + assertCommandSuccess run_env_as_bwrap_fakeroot "" "--bind /usr /usr" "false" + assertEquals "$BWRAP $COMMON_BWRAP_OPTION --cap-add ALL --uid 0 --gid 0 --bind /usr /usr sudo /bin/sh --login" "$(cat "$STDOUTF")" + + _test_copy_common_files +} + +function test_run_env_as_bwrap_user_with_backend_args() { + assertCommandSuccess run_env_as_bwrap_user "" "--bind /usr /usr" "false" + assertEquals "$BWRAP $COMMON_BWRAP_OPTION --bind /usr /usr /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 "" "false" "ls -la" - assertEquals "unshare --mount --user --map-root-user $GROOT --no-umount --recursive -b $HOME -b /tmp -b /proc -b /sys -b /dev $JUNEST_HOME /bin/sh --login -c \"ls -la\"" "$(cat $STDOUTF)" +function test_run_env_as_bwrap_fakeroot_with_command() { + assertCommandSuccess run_env_as_bwrap_fakeroot "" "" "false" "ls -la" + assertEquals "$BWRAP $COMMON_BWRAP_OPTION --cap-add ALL --uid 0 --gid 0 sudo /bin/sh --login -c \"ls -la\"" "$(cat "$STDOUTF")" + + _test_copy_common_files +} + +function test_run_env_as_bwrap_user_with_command() { + assertCommandSuccess run_env_as_bwrap_user "" "" "false" "ls -la" + assertEquals "$BWRAP $COMMON_BWRAP_OPTION /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" "false" "ls -la" - assertEquals "unshare --mount --user --map-root-user $GROOT --no-umount --recursive -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)" +function test_run_env_as_bwrap_fakeroot_with_backend_args_and_command() { + assertCommandSuccess run_env_as_bwrap_fakeroot "" "--bind /usr /usr" "false" "ls -la" + assertEquals "$BWRAP $COMMON_BWRAP_OPTION --cap-add ALL --uid 0 --gid 0 --bind /usr /usr sudo /bin/sh --login -c \"ls -la\"" "$(cat "$STDOUTF")" + + _test_copy_common_files +} + +function test_run_env_as_bwrap_user_with_backend_args_and_command() { + assertCommandSuccess run_env_as_bwrap_user "" "--bind /usr /usr" "false" "ls -la" + assertEquals "$BWRAP $COMMON_BWRAP_OPTION --bind /usr /usr /bin/sh --login -c \"ls -la\"" "$(cat "$STDOUTF")" _test_copy_common_files _test_copy_remaining_files } -function test_run_env_with_namespace_nested_env(){ +function test_run_env_as_bwrap_fakeroot_nested_env(){ JUNEST_ENV=1 - assertCommandFailOnStatus 106 run_env_with_namespace "" "false" "" + assertCommandFailOnStatus 106 run_env_as_bwrap_fakeroot "" "" "false" "" unset JUNEST_ENV } -source $JUNEST_ROOT/tests/utils/shunit2 +function test_run_env_as_bwrap_user_nested_env(){ + # shellcheck disable=SC2034 + JUNEST_ENV=1 + assertCommandFailOnStatus 106 run_env_as_bwrap_user "" "" "false" "" + unset JUNEST_ENV +} + +source "$JUNEST_ROOT"/tests/utils/shunit2 diff --git a/tests/unit-tests/test-proot.sh b/tests/unit-tests/test-proot.sh index a951cd6..0f4f11a 100755 --- a/tests/unit-tests/test-proot.sh +++ b/tests/unit-tests/test-proot.sh @@ -1,6 +1,7 @@ #!/bin/bash +# shellcheck disable=SC1091 -JUNEST_ROOT=$(readlink -f $(dirname $0)/../..) +JUNEST_ROOT=$(readlink -f "$(dirname "$0")"/../..) source "$JUNEST_ROOT/tests/utils/utils.sh" @@ -30,16 +31,16 @@ function tearDown(){ } 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)" + [[ -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 /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 $? @@ -47,27 +48,51 @@ function _test_copy_remaining_files() { assertEquals 0 $? } -function test_run_env_as_user(){ +function test_run_env_as_proot_user(){ + # shellcheck disable=SC2317 _run_env_with_qemu() { - echo $@ + # shellcheck disable=SC2086 + # shellcheck disable=SC2048 + echo $* } - assertCommandSuccess run_env_as_user "-k 3.10" "false" "/usr/bin/mkdir" "-v" "/newdir2" - assertEquals "-b $HOME -b /tmp -b /proc -b /sys -b /dev -r ${JUNEST_HOME} -k 3.10 /usr/bin/mkdir -v /newdir2" "$(cat $STDOUTF)" + assertCommandSuccess run_env_as_proot_user "" "-k 3.10" "false" "/usr/bin/mkdir" "-v" "/newdir2" + assertEquals "-b /run/user/$(id -u) -b $HOME -b /tmp -b /proc -b /sys -b /dev -r ${JUNEST_HOME} -k 3.10 /usr/bin/mkdir -v /newdir2" "$(cat "$STDOUTF")" SH=("/usr/bin/echo") - assertCommandSuccess run_env_as_user "-k 3.10" "false" - assertEquals "-b $HOME -b /tmp -b /proc -b /sys -b /dev -r ${JUNEST_HOME} -k 3.10" "$(cat $STDOUTF)" + assertCommandSuccess run_env_as_proot_user "" "-k 3.10" "false" + assertEquals "-b /run/user/$(id -u) -b $HOME -b /tmp -b /proc -b /sys -b /dev -r ${JUNEST_HOME} -k 3.10" "$(cat "$STDOUTF")" _test_copy_common_files _test_copy_remaining_files } -function test_run_env_as_user_no_copy(){ +function test_run_env_as_proot_user_with_backend_command(){ + # shellcheck disable=SC2317 _run_env_with_qemu() { - echo $@ + # shellcheck disable=SC2086 + # shellcheck disable=SC2048 + echo $* } - assertCommandSuccess run_env_as_user "-k 3.10" "true" "/usr/bin/mkdir" "-v" "/newdir2" - assertEquals "-b $HOME -b /tmp -b /proc -b /sys -b /dev -r ${JUNEST_HOME} -k 3.10 /usr/bin/mkdir -v /newdir2" "$(cat $STDOUTF)" + assertCommandSuccess run_env_as_proot_user "myproot" "-k 3.10" "false" "/usr/bin/mkdir" "-v" "/newdir2" + assertEquals "myproot -b /run/user/$(id -u) -b $HOME -b /tmp -b /proc -b /sys -b /dev -r ${JUNEST_HOME} -k 3.10 /usr/bin/mkdir -v /newdir2" "$(cat "$STDOUTF")" + + SH=("/usr/bin/echo") + assertCommandSuccess run_env_as_proot_user "myproot" "-k 3.10" "false" + assertEquals "myproot -b /run/user/$(id -u) -b $HOME -b /tmp -b /proc -b /sys -b /dev -r ${JUNEST_HOME} -k 3.10" "$(cat "$STDOUTF")" + + _test_copy_common_files + _test_copy_remaining_files +} + +function test_run_env_as_proot_user_no_copy(){ + # shellcheck disable=SC2317 + _run_env_with_qemu() { + # shellcheck disable=SC2086 + # shellcheck disable=SC2048 + echo $* + } + assertCommandSuccess run_env_as_proot_user "" "-k 3.10" "true" "/usr/bin/mkdir" "-v" "/newdir2" + assertEquals "-b /run/user/$(id -u) -b $HOME -b /tmp -b /proc -b /sys -b /dev -r ${JUNEST_HOME} -k 3.10 /usr/bin/mkdir -v /newdir2" "$(cat "$STDOUTF")" [[ ! -e ${JUNEST_HOME}/etc/hosts ]] assertEquals 0 $? @@ -91,69 +116,105 @@ function test_run_env_as_user_no_copy(){ assertEquals 0 $? } -function test_run_env_as_user_nested_env(){ +function test_run_env_as_proot_user_nested_env(){ JUNEST_ENV=1 - assertCommandFailOnStatus 106 run_env_as_user "" "false" + assertCommandFailOnStatus 106 run_env_as_proot_user "" "" "false" unset JUNEST_ENV } -function test_run_env_as_fakeroot(){ +function test_run_env_as_proot_fakeroot(){ + # shellcheck disable=SC2317 _run_env_with_qemu() { - echo $@ + # shellcheck disable=SC2086 + # shellcheck disable=SC2048 + echo $* } - assertCommandSuccess run_env_as_fakeroot "-k 3.10" "false" "/usr/bin/mkdir" "-v" "/newdir2" - assertEquals "-0 -b ${HOME} -b /tmp -b /proc -b /sys -b /dev -r ${JUNEST_HOME} -k 3.10 /usr/bin/mkdir -v /newdir2" "$(cat $STDOUTF)" + assertCommandSuccess run_env_as_proot_fakeroot "" "-k 3.10" "false" "/usr/bin/mkdir" "-v" "/newdir2" + assertEquals "-0 -b /run/user/$(id -u) -b ${HOME} -b /tmp -b /proc -b /sys -b /dev -r ${JUNEST_HOME} -k 3.10 /usr/bin/mkdir -v /newdir2" "$(cat "$STDOUTF")" SH=("/usr/bin/echo") - assertCommandSuccess run_env_as_fakeroot "-k 3.10" "false" - assertEquals "-0 -b ${HOME} -b /tmp -b /proc -b /sys -b /dev -r ${JUNEST_HOME} -k 3.10" "$(cat $STDOUTF)" + assertCommandSuccess run_env_as_proot_fakeroot "" "-k 3.10" "false" + assertEquals "-0 -b /run/user/$(id -u) -b ${HOME} -b /tmp -b /proc -b /sys -b /dev -r ${JUNEST_HOME} -k 3.10" "$(cat "$STDOUTF")" _test_copy_common_files } -function test_run_env_as_fakeroot_nested_env(){ +function test_run_env_as_proot_fakeroot_with_backend_command(){ + # shellcheck disable=SC2317 + _run_env_with_qemu() { + # shellcheck disable=SC2086 + # shellcheck disable=SC2048 + echo $* + } + assertCommandSuccess run_env_as_proot_fakeroot "myproot" "-k 3.10" "false" "/usr/bin/mkdir" "-v" "/newdir2" + assertEquals "myproot -0 -b /run/user/$(id -u) -b ${HOME} -b /tmp -b /proc -b /sys -b /dev -r ${JUNEST_HOME} -k 3.10 /usr/bin/mkdir -v /newdir2" "$(cat "$STDOUTF")" + + # shellcheck disable=SC2034 + SH=("/usr/bin/echo") + assertCommandSuccess run_env_as_proot_fakeroot "myproot" "-k 3.10" "false" + assertEquals "myproot -0 -b /run/user/$(id -u) -b ${HOME} -b /tmp -b /proc -b /sys -b /dev -r ${JUNEST_HOME} -k 3.10" "$(cat "$STDOUTF")" + + _test_copy_common_files +} + +function test_run_env_as_proot_fakeroot_nested_env(){ JUNEST_ENV=1 - assertCommandFailOnStatus 106 run_env_as_fakeroot "" "false" "" + assertCommandFailOnStatus 106 run_env_as_proot_fakeroot "" "" "false" "" unset JUNEST_ENV } function test_run_env_with_quotes(){ + # shellcheck disable=SC2317 _run_env_with_qemu() { - echo $@ + # shellcheck disable=SC2086 + # shellcheck disable=SC2048 + echo $* } - assertCommandSuccess run_env_as_user "-k 3.10" "false" "bash" "-c" "/usr/bin/mkdir -v /newdir2" - assertEquals "-b ${HOME} -b /tmp -b /proc -b /sys -b /dev -r ${JUNEST_HOME} -k 3.10 bash -c /usr/bin/mkdir -v /newdir2" "$(cat $STDOUTF)" + assertCommandSuccess run_env_as_proot_user "" "-k 3.10" "false" "bash" "-c" "/usr/bin/mkdir -v /newdir2" + assertEquals "-b /run/user/$(id -u) -b ${HOME} -b /tmp -b /proc -b /sys -b /dev -r ${JUNEST_HOME} -k 3.10 bash -c /usr/bin/mkdir -v /newdir2" "$(cat "$STDOUTF")" } function test_run_env_with_proot_args(){ + # shellcheck disable=SC2317 proot_cmd() { [ "$JUNEST_ENV" != "1" ] && return 1 - echo $@ + # shellcheck disable=SC2086 + # shellcheck disable=SC2048 + echo $* } - assertCommandSuccess _run_env_with_proot --help - assertEquals "--help /bin/sh --login" "$(cat $STDOUTF)" + assertCommandSuccess _run_env_with_proot "" "--help" + assertEquals "--help /bin/sh --login" "$(cat "$STDOUTF")" - assertCommandSuccess _run_env_with_proot --help mycommand - assertEquals "--help /bin/sh --login -c mycommand" "$(cat $STDOUTF)" + assertCommandSuccess _run_env_with_proot "" "--help" mycommand + assertEquals "--help /bin/sh --login -c mycommand" "$(cat "$STDOUTF")" assertCommandFail _run_env_with_proot } function test_qemu() { - echo "JUNEST_ARCH=arm" > ${JUNEST_HOME}/etc/junest/info + echo "JUNEST_ARCH=arm" > "${JUNEST_HOME}"/etc/junest/info + # shellcheck disable=SC2317 rm_cmd() { - echo $@ + # shellcheck disable=SC2086 + # shellcheck disable=SC2048 + echo $* } + # shellcheck disable=SC2317 ln_cmd() { - echo $@ + # shellcheck disable=SC2086 + # shellcheck disable=SC2048 + echo $* } + # shellcheck disable=SC2317 _run_env_with_proot() { - echo $@ + # shellcheck disable=SC2086 + # shellcheck disable=SC2048 + echo $* } - RANDOM=100 ARCH=x86_64 assertCommandSuccess _run_env_with_qemu "" - assertEquals "$(echo -e "-s $JUNEST_HOME/opt/qemu/qemu-arm-static-x86_64 /tmp/qemu-arm-static-x86_64-100\n-q /tmp/qemu-arm-static-x86_64-100")" "$(cat $STDOUTF)" + RANDOM=100 ARCH=x86_64 assertCommandSuccess _run_env_with_qemu "" "" + assertEquals "$(echo -e "-s $JUNEST_HOME/bin/qemu-arm-static-x86_64 /tmp/qemu-arm-static-x86_64-100\n-q /tmp/qemu-arm-static-x86_64-100")" "$(cat "$STDOUTF")" } -source $JUNEST_ROOT/tests/utils/shunit2 +source "$JUNEST_ROOT"/tests/utils/shunit2 diff --git a/tests/unit-tests/test-setup.sh b/tests/unit-tests/test-setup.sh index a15dbca..de2df75 100755 --- a/tests/unit-tests/test-setup.sh +++ b/tests/unit-tests/test-setup.sh @@ -1,6 +1,7 @@ #!/bin/bash +# shellcheck disable=SC1091 -JUNEST_ROOT=$(readlink -f $(dirname $0)/../..) +JUNEST_ROOT=$(readlink -f "$(dirname "$0")"/../..) source "$JUNEST_ROOT/tests/utils/utils.sh" @@ -26,24 +27,27 @@ function tearDown(){ } function test_is_env_installed(){ - rm -rf $JUNEST_HOME/* + rm -rf "${JUNEST_HOME:?}"/* assertCommandFail is_env_installed - touch $JUNEST_HOME/just_file + touch "$JUNEST_HOME"/just_file assertCommandSuccess is_env_installed } function test_setup_env(){ - rm -rf $JUNEST_HOME/* + rm -rf "${JUNEST_HOME:?}"/* + # shellcheck disable=SC2317 wget_mock(){ # Proof that the setup is happening # inside $JUNEST_TEMPDIR - local cwd=${PWD#${JUNEST_TEMPDIR}} - local parent_dir=${PWD%${cwd}} + local cwd=${PWD#"${JUNEST_TEMPDIR}"} + local parent_dir=${PWD%"${cwd}"} assertEquals "$JUNEST_TEMPDIR" "${parent_dir}" touch file - tar -czvf ${CMD}-${ARCH}.tar.gz file + tar -czvf "${CMD}-${ARCH}".tar.gz file } + # shellcheck disable=SC2034 WGET=wget_mock + # shellcheck disable=SC2119 setup_env 1> /dev/null assertTrue "[ -e $JUNEST_HOME/file ]" @@ -52,10 +56,10 @@ function test_setup_env(){ function test_setup_env_from_file(){ - rm -rf $JUNEST_HOME/* + rm -rf "${JUNEST_HOME:?}"/* touch file - tar -czvf ${CMD}-${ARCH}.tar.gz file 1> /dev/null - assertCommandSuccess setup_env_from_file ${CMD}-${ARCH}.tar.gz + tar -czvf "${CMD}-${ARCH}".tar.gz file 1> /dev/null + assertCommandSuccess setup_env_from_file "${CMD}-${ARCH}.tar.gz" assertTrue "[ -e $JUNEST_HOME/file ]" } @@ -64,10 +68,10 @@ function test_setup_env_from_file_not_existing_file(){ } function test_setup_env_from_file_with_absolute_path(){ - rm -rf $JUNEST_HOME/* + rm -rf "${JUNEST_HOME:?}"/* touch file - tar -czf ${CMD}-${ARCH}.tar.gz file - assertCommandSuccess setup_env_from_file ${CMD}-${ARCH}.tar.gz + tar -czf "${CMD}-${ARCH}".tar.gz file + assertCommandSuccess setup_env_from_file "${CMD}-${ARCH}.tar.gz" assertTrue "[ -e $JUNEST_HOME/file ]" } @@ -78,4 +82,4 @@ function test_delete_env(){ assertCommandFail is_env_installed } -source $JUNEST_ROOT/tests/utils/shunit2 +source "$JUNEST_ROOT"/tests/utils/shunit2 diff --git a/tests/unit-tests/test-utils.sh b/tests/unit-tests/test-utils.sh index c0b1ebf..03e602a 100755 --- a/tests/unit-tests/test-utils.sh +++ b/tests/unit-tests/test-utils.sh @@ -1,10 +1,13 @@ #!/bin/bash -source "$(dirname $0)/../utils/utils.sh" +# shellcheck disable=SC1091 + +source "$(dirname "$0")/../utils/utils.sh" unset HOME -export HOME=$(TMPDIR=/tmp mktemp -d -t pearl-user-home.XXXXXXX) +export HOME +HOME=$(TMPDIR=/tmp mktemp -d -t pearl-user-home.XXXXXXX) -source "$(dirname $0)/../../lib/utils/utils.sh" +source "$(dirname "$0")/../../lib/utils/utils.sh" # Disable the exiterr set +e @@ -20,37 +23,42 @@ function test_check_not_null(){ function test_echoerr(){ assertCommandSuccess echoerr "Test" - assertEquals "Test" "$(cat $STDERRF)" + assertEquals "Test" "$(cat "$STDERRF")" } function test_error(){ assertCommandSuccess error "Test" - local expected=$(echo -e "\033[1;31mTest\033[0m") - assertEquals "$expected" "$(cat $STDERRF)" + local expected + expected=$(echo -e "\033[1;31mTest\033[0m") + assertEquals "$expected" "$(cat "$STDERRF")" } function test_warn(){ assertCommandSuccess warn "Test" - local expected=$(echo -e "\033[1;33mTest\033[0m") - assertEquals "$expected" "$(cat $STDERRF)" + local expected + expected=$(echo -e "\033[1;33mTest\033[0m") + assertEquals "$expected" "$(cat "$STDERRF")" } function test_info(){ assertCommandSuccess info "Test" - local expected=$(echo -e "\033[1;36mTest\033[0m") - assertEquals "$expected" "$(cat $STDOUTF)" + local expected + expected=$(echo -e "\033[1;36mTest\033[0m") + assertEquals "$expected" "$(cat "$STDOUTF")" } function test_die(){ assertCommandFail die "Test" - local expected=$(echo -e "\033[1;31mTest\033[0m") - assertEquals "$expected" "$(cat $STDERRF)" + local expected + expected=$(echo -e "\033[1;31mTest\033[0m") + assertEquals "$expected" "$(cat "$STDERRF")" } function test_die_on_status(){ assertCommandFailOnStatus 222 die_on_status 222 "Test" - local expected=$(echo -e "\033[1;31mTest\033[0m") - assertEquals "$expected" "$(cat $STDERRF)" + local expected + expected=$(echo -e "\033[1;31mTest\033[0m") + assertEquals "$expected" "$(cat "$STDERRF")" } function test_ask_null_question(){ @@ -79,34 +87,12 @@ 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)" + assertEquals "this is \"a test\"" "$(cat "$STDOUTF")" assertCommandSuccess insert_quotes_on_spaces this is 'a test' - assertEquals "this is \"a test\"" "$(cat $STDOUTF)" + assertEquals "this is \"a test\"" "$(cat "$STDOUTF")" } function test_contains_element(){ @@ -116,4 +102,4 @@ function test_contains_element(){ assertCommandFailOnStatus 1 contains_element "blabla" "${array[@]}" } -source $(dirname $0)/../utils/shunit2 +source "$(dirname "$0")"/../utils/shunit2 diff --git a/tests/unit-tests/test-wrappers.sh b/tests/unit-tests/test-wrappers.sh new file mode 100755 index 0000000..ee9776f --- /dev/null +++ b/tests/unit-tests/test-wrappers.sh @@ -0,0 +1,139 @@ +#!/bin/bash +# shellcheck disable=SC1091 + +source "$(dirname "$0")/../utils/utils.sh" + +source "$(dirname "$0")/../../lib/core/wrappers.sh" + +# Disable the exiterr +set +e + +function oneTimeSetUp(){ + setUpUnitTests +} + +function setUp(){ + junestSetUp +} + +function tearDown(){ + junestTearDown +} + +function test_create_wrappers_empty_bin(){ + assertCommandSuccess create_wrappers + assertEquals "" "$(cat "$STDOUTF")" + assertTrue "bin_wrappers does not exist" "[ -e $JUNEST_HOME/usr/bin_wrappers ]" +} + +function test_create_wrappers_not_executable_file(){ + touch "$JUNEST_HOME"/usr/bin/myfile + assertCommandSuccess create_wrappers + assertEquals "" "$(cat "$STDOUTF")" + assertTrue "bin_wrappers should exist" "[ -e $JUNEST_HOME/usr/bin_wrappers ]" + assertTrue "myfile wrapper should not exist" "[ ! -x $JUNEST_HOME/usr/bin_wrappers/myfile ]" +} + +function test_create_wrappers_directory(){ + mkdir -p "$JUNEST_HOME"/usr/bin/mydir + assertCommandSuccess create_wrappers + assertEquals "" "$(cat "$STDOUTF")" + assertTrue "bin_wrappers should exist" "[ -e $JUNEST_HOME/usr/bin_wrappers ]" + assertTrue "mydir wrapper should not exist" "[ ! -e $JUNEST_HOME/usr/bin_wrappers/mydir ]" +} + +function test_create_wrappers_broken_link(){ + ln -s /opt/myapp/bin/cmd "$JUNEST_HOME"/usr/bin/cmd + assertCommandSuccess create_wrappers + assertEquals "" "$(cat "$STDOUTF")" + assertTrue "bin_wrappers should exist" "[ -e $JUNEST_HOME/usr/bin_wrappers ]" + assertTrue "cmd wrapper should exist" "[ -x $JUNEST_HOME/usr/bin_wrappers/cmd ]" +} + +function test_create_wrappers_executable_file(){ + touch "$JUNEST_HOME"/usr/bin/myfile + chmod +x "$JUNEST_HOME"/usr/bin/myfile + assertCommandSuccess create_wrappers + assertEquals "" "$(cat "$STDOUTF")" + assertTrue "bin_wrappers should exist" "[ -e $JUNEST_HOME/usr/bin_wrappers ]" + assertTrue "myfile wrapper should exist" "[ -x $JUNEST_HOME/usr/bin_wrappers/myfile ]" +} + +function test_create_wrappers_verify_content(){ + # Test for: + # https://github.com/fsquillace/junest/issues/262 + # https://github.com/fsquillace/junest/issues/292 + touch "$JUNEST_HOME"/usr/bin/myfile + chmod +x "$JUNEST_HOME"/usr/bin/myfile + export JUNEST_ARGS="ns --fakeroot -b '--bind /run /run2'" + assertCommandSuccess create_wrappers + assertEquals "" "$(cat "$STDOUTF")" + + # Mock junest command to capture the actual output generated from myfile script + # shellcheck disable=SC2317 + junest(){ + for arg in "$@" + do + echo "$arg" + done + } + assertEquals "ns +--fakeroot +-b +--bind /run /run2 +-- +test-wrappers.sh +pacman +-Rsn +neovim +new package" "$(source "$JUNEST_HOME"/usr/bin_wrappers/myfile pacman -Rsn neovim 'new package')" +} + +function test_create_wrappers_already_exist(){ + touch "$JUNEST_HOME"/usr/bin/myfile + chmod +x "$JUNEST_HOME"/usr/bin/myfile + mkdir -p "$JUNEST_HOME"/usr/bin_wrappers + echo "original" > "$JUNEST_HOME"/usr/bin_wrappers/myfile + chmod +x "$JUNEST_HOME"/usr/bin_wrappers/myfile + assertCommandSuccess create_wrappers false + assertEquals "" "$(cat "$STDOUTF")" + assertTrue "bin_wrappers should exist" "[ -e $JUNEST_HOME/usr/bin_wrappers ]" + assertTrue "myfile wrapper should exist" "[ -x $JUNEST_HOME/usr/bin_wrappers/myfile ]" + assertEquals "original" "$(cat "$JUNEST_HOME"/usr/bin_wrappers/myfile)" +} + +function test_create_wrappers_forced_already_exist(){ + echo "new" > "$JUNEST_HOME"/usr/bin/myfile + chmod +x "$JUNEST_HOME"/usr/bin/myfile + mkdir -p "$JUNEST_HOME"/usr/bin_wrappers + echo "original" > "$JUNEST_HOME"/usr/bin_wrappers/myfile + chmod +x "$JUNEST_HOME"/usr/bin_wrappers/myfile + assertCommandSuccess create_wrappers true + assertEquals "" "$(cat "$STDOUTF")" + assertTrue "bin_wrappers should exist" "[ -e $JUNEST_HOME/usr/bin_wrappers ]" + assertTrue "myfile wrapper should exist" "[ -x $JUNEST_HOME/usr/bin_wrappers/myfile ]" + assertNotEquals "original" "$(cat "$JUNEST_HOME"/usr/bin_wrappers/myfile)" +} + +function test_create_wrappers_executable_no_longer_exist(){ + mkdir -p "$JUNEST_HOME"/usr/bin_wrappers + touch "$JUNEST_HOME"/usr/bin_wrappers/myfile + chmod +x "$JUNEST_HOME"/usr/bin_wrappers/myfile + assertCommandSuccess create_wrappers + assertEquals "" "$(cat "$STDOUTF")" + assertTrue "bin_wrappers should exist" "[ -e $JUNEST_HOME/usr/bin_wrappers ]" + assertTrue "myfile wrapper should not exist" "[ ! -x $JUNEST_HOME/usr/bin_wrappers/myfile ]" +} + +function test_create_wrappers_custom_bin_path(){ + mkdir -p "$JUNEST_HOME"/usr/mybindir + touch "$JUNEST_HOME"/usr/mybindir/myfile + chmod +x "$JUNEST_HOME"/usr/mybindir/myfile + assertCommandSuccess create_wrappers false /usr/mybindir/ + assertEquals "" "$(cat "$STDOUTF")" + assertTrue "bin_wrappers should exist" "[ -e $JUNEST_HOME/usr/mybindir_wrappers ]" + assertTrue "myfile wrapper should exist" "[ -x $JUNEST_HOME/usr/mybindir_wrappers/myfile ]" +} + + +source "$(dirname "$0")"/../utils/shunit2 diff --git a/tests/unit-tests/unit-tests.sh b/tests/unit-tests/unit-tests.sh index acf1bba..e90ed22 100755 --- a/tests/unit-tests/unit-tests.sh +++ b/tests/unit-tests/unit-tests.sh @@ -1,6 +1,7 @@ #!/bin/bash tests_succeded=true -for tst in $(ls $(dirname $0)/test* | grep -v $(basename $0)) +# shellcheck disable=SC2010 +for tst in $(ls "$(dirname "$0")"/test* | grep -v "$(basename "$0")") do $tst || tests_succeded=false done diff --git a/tests/utils/shunit2 b/tests/utils/shunit2 index d6e7503..e4c719c 100644 --- a/tests/utils/shunit2 +++ b/tests/utils/shunit2 @@ -987,7 +987,7 @@ _shunit_extractTestFunctions() # extract the lines with test function names, strip of anything besides the # function name, and output everything on a single line. _shunit_regex_='^[ ]*(function )*test[A-Za-z0-9_]* *\(\)' - egrep "${_shunit_regex_}" "${_shunit_script_}" \ + grep -E "${_shunit_regex_}" "${_shunit_script_}" \ |sed 's/^[^A-Za-z0-9_]*//;s/^function //;s/\([A-Za-z0-9_]*\).*/\1/g' \ |xargs diff --git a/tests/utils/utils.sh b/tests/utils/utils.sh index ed7cb8c..ab37bd9 100644 --- a/tests/utils/utils.sh +++ b/tests/utils/utils.sh @@ -1,25 +1,28 @@ +#!/usr/bin/env bash + OLD_CWD=${PWD} function cwdSetUp(){ ORIGIN_CWD=$(TMPDIR=/tmp mktemp -d -t junest-cwd.XXXXXXXXXX) - cd $ORIGIN_CWD + cd "$ORIGIN_CWD" || return 1 } function cwdTearDown(){ - rm -rf $ORIGIN_CWD - cd $OLD_CWD + rm -rf "$ORIGIN_CWD" + cd "$OLD_CWD" || return 1 } function junestSetUp(){ JUNEST_HOME=$(TMPDIR=/tmp mktemp -d -t junest-home.XXXXXXXXXX) - mkdir -p ${JUNEST_HOME}/etc/junest - echo "JUNEST_ARCH=x86_64" > ${JUNEST_HOME}/etc/junest/info - mkdir -p ${JUNEST_HOME}/etc/ca-certificates + mkdir -p "${JUNEST_HOME}/usr/bin" + mkdir -p "${JUNEST_HOME}/etc/junest" + echo "JUNEST_ARCH=x86_64" > "${JUNEST_HOME}/etc/junest/info" + mkdir -p "${JUNEST_HOME}/etc/ca-certificates" } 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 + [ -d "${JUNEST_HOME}/etc/ca-certificates" ] && chmod -R +w "${JUNEST_HOME}/etc/ca-certificates" + rm -rf "$JUNEST_HOME" unset JUNEST_HOME } @@ -31,15 +34,17 @@ function setUpUnitTests(){ } function assertCommandSuccess(){ + # shellcheck disable=SC2091 $(set -e - "$@" > $STDOUTF 2> $STDERRF + "$@" > "$STDOUTF" 2> "$STDERRF" ) assertTrue "The command $1 did not return 0 exit status" $? } function assertCommandFail(){ + # shellcheck disable=SC2091 $(set -e - "$@" > $STDOUTF 2> $STDERRF + "$@" > "$STDOUTF" 2> "$STDERRF" ) assertFalse "The command $1 returned 0 exit status" $? } @@ -49,8 +54,9 @@ function assertCommandFail(){ function assertCommandFailOnStatus(){ local status=$1 shift + # shellcheck disable=SC2091 $(set -e - "$@" > $STDOUTF 2> $STDERRF + "$@" > "$STDOUTF" 2> "$STDERRF" ) - assertEquals $status $? + assertEquals "$status" $? }