├── Kyuafile ├── examples ├── Linux.conf ├── FreeBSD.conf ├── Darwin.conf └── NetBSD.conf ├── .gitignore ├── .travis.yml ├── AUTHORS ├── modules ├── Kyuafile ├── empty.subr ├── linux_native.subr ├── linux_native_test.sh ├── netbsd_native_test.sh ├── freebsd_native.subr ├── netbsd_native.subr ├── freebsd_native_test.sh ├── netbsd_release.subr ├── darwin_native_test.sh ├── netbsd_release_test.sh └── darwin_native.subr ├── CONTRIBUTORS ├── COPYING ├── CONTRIBUTING.md ├── README ├── NEWS ├── admin ├── travis-build.sh ├── clean-all.sh └── travis-install-deps.sh ├── test_utils.sh ├── configure.ac ├── INSTALL ├── Makefile.am ├── sandboxctl.8.in ├── sandboxctl.conf.5.in ├── sandboxctl.sh ├── sandbox.subr.in ├── sandbox_test.sh └── sandboxctl_test.sh /Kyuafile: -------------------------------------------------------------------------------- 1 | syntax(2) 2 | 3 | test_suite("sandboxctl") 4 | 5 | atf_test_program{name="sandbox_test"} 6 | atf_test_program{name="sandboxctl_test"} 7 | 8 | include("modules/Kyuafile") 9 | -------------------------------------------------------------------------------- /examples/Linux.conf: -------------------------------------------------------------------------------- 1 | # Configuration file for sandboxctl(8). 2 | # 3 | # This configuration file provides a sample setup to build a Linux sandbox. 4 | 5 | SANDBOX_TYPE=linux-native 6 | SANDBOX_ROOT=/var/chroot/sandboxctl/default 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *_test 2 | Makefile 3 | Makefile.in 4 | aclocal.m4 5 | admin/install-sh 6 | admin/missing 7 | autom4te.cache 8 | config.log 9 | config.status 10 | configure 11 | default.conf 12 | sandbox.subr 13 | sandboxctl 14 | sandboxctl.8 15 | sandboxctl.conf.5 16 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | dist: trusty 2 | sudo: required 3 | 4 | before_install: 5 | - ./admin/travis-install-deps.sh 6 | 7 | env: 8 | - SHELL_NAME=bash 9 | - SHELL_NAME=dash 10 | - SHELL_NAME=pdksh 11 | - SHELL_NAME=zsh 12 | 13 | script: 14 | - ./admin/travis-build.sh 15 | -------------------------------------------------------------------------------- /examples/FreeBSD.conf: -------------------------------------------------------------------------------- 1 | # Configuration file for sandboxctl(8). 2 | # 3 | # This configuration file provides a sample setup to build a FreeBSD sandbox. 4 | 5 | SANDBOX_TYPE=freebsd-native 6 | SANDBOX_ROOT=/var/chroot/sandboxctl/default 7 | 8 | # Sample settings for the freebsd-native sandbox type. 9 | #FREEBSD_NATIVE_SOURCE=/usr/src 10 | -------------------------------------------------------------------------------- /examples/Darwin.conf: -------------------------------------------------------------------------------- 1 | # Configuration file for sandboxctl(8). 2 | # 3 | # This configuration file provides a sample setup to build a macOS sandbox. 4 | 5 | SANDBOX_TYPE=darwin-native 6 | SANDBOX_ROOT=/var/tmp/sandbox 7 | 8 | # Sample settings for the darwin-native sandbox type. 9 | if [ -d /Applications/Xcode.app ]; then 10 | DARWIN_NATIVE_WITH_XCODE=true 11 | fi 12 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | # This is the official list of sandboxctl authors for copyright purposes. 2 | # 3 | # This file is distinct from the CONTRIBUTORS files; see the latter for 4 | # an explanation. 5 | # 6 | # Names are sorted alphabetically and should be added to this file as: 7 | # 8 | # * Name 9 | # * Organization 10 | 11 | * Google Inc. 12 | -------------------------------------------------------------------------------- /examples/NetBSD.conf: -------------------------------------------------------------------------------- 1 | # Configuration file for sandboxctl(8). 2 | # 3 | # This configuration file provides a sample setup to build a NetBSD sandbox. 4 | 5 | SANDBOX_TYPE=netbsd-release 6 | SANDBOX_ROOT=/var/chroot/sandboxctl/default 7 | 8 | # Sample settings for the netbsd-native sandbox type. 9 | #NETBSD_NATIVE_RELEASEDIR="/home/sysbuild/release/$(uname -m)" 10 | 11 | # Sample settings for the netbsd-release sandbox type. 12 | #NETBSD_RELEASE_RELEASEDIR="/home/sysbuild/release/$(uname -m)" 13 | #NETBSD_RELEASE_SETS="base comp" 14 | -------------------------------------------------------------------------------- /modules/Kyuafile: -------------------------------------------------------------------------------- 1 | syntax(2) 2 | 3 | test_suite("sandboxctl") 4 | 5 | if fs.exists("darwin_native_test") then 6 | atf_test_program{name="darwin_native_test"} 7 | end 8 | if fs.exists("freebsd_native_test") then 9 | atf_test_program{name="freebsd_native_test"} 10 | end 11 | if fs.exists("linux_native_test") then 12 | atf_test_program{name="linux_native_test"} 13 | end 14 | if fs.exists("netbsd_native_test") then 15 | atf_test_program{name="netbsd_native_test"} 16 | end 17 | if fs.exists("netbsd_release_test") then 18 | atf_test_program{name="netbsd_release_test"} 19 | end 20 | -------------------------------------------------------------------------------- /CONTRIBUTORS: -------------------------------------------------------------------------------- 1 | # This is the list of people who have agreed to one of the CLAs and can 2 | # contribute patches to the sandboxctl project. 3 | # 4 | # The AUTHORS file lists the copyright holders; this file lists people. 5 | # For example: Google employees are listed here but not in AUTHORS 6 | # because Google holds the copyright. 7 | # 8 | # See the following links for details on the CLA: 9 | # 10 | # https://developers.google.com/open-source/cla/individual 11 | # https://developers.google.com/open-source/cla/corporate 12 | # 13 | # Names are sorted by last name and should be added as: 14 | # 15 | # * Name 16 | 17 | * Julio Merino 18 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | Copyright 2013 Google Inc. 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are 6 | met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | * Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the distribution. 13 | * Neither the name of Google Inc. nor the names of its contributors 14 | may be used to endorse or promote products derived from this software 15 | without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Want to contribute? Great! First, read this page (including the small print 4 | at the end). 5 | 6 | ## Before you contribute 7 | 8 | Before we can use your code, you must sign the [Google Individual Contributor 9 | License Agreement](https://cla.developers.google.com/about/google-individual) 10 | (CLA), which you can do online. 11 | 12 | The CLA is necessary mainly because you own the copyright to your changes, even 13 | after your contribution becomes part of our codebase, so we need your 14 | permission to use and distribute your code. We also need to be sure of various 15 | other things: for instance that you'll tell us if you know that your code 16 | infringes on other people's patents. You don't have to sign the CLA until 17 | after you've submitted your code for review and a member has approved it, but 18 | you must do it before we can put your code into our codebase. 19 | 20 | Before you start working on a larger contribution, you should get in touch with 21 | us first through the issue tracker with your idea so that we can help out and 22 | possibly guide you. Coordinating up front makes it much easier to avoid 23 | frustration later on. 24 | 25 | ## Code reviews 26 | 27 | All submissions, including submissions by project members, require review. 28 | We use Github pull requests for this purpose. 29 | 30 | ## The small print 31 | 32 | Contributions made by corporations are covered by a different agreement than 33 | the one above, the [Software Grant and Corporate Contributor License 34 | Agreement](https://cla.developers.google.com/about/google-corporate). 35 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | sandboxctl is a tool to interact with chroot-based sandboxes. 2 | 3 | The sandboxctl tool provides an automated mechanism to create and 4 | interact with chroot-based sandboxes. These sandboxes can be transient 5 | (e.g. to run a single command within them) or long-lived (e.g. to run a 6 | system service in a safe manner). 7 | 8 | Sandboxes can be created using multiple mechanisms, which range from 9 | extracting fresh distribution sets to bind-mounting the outer file 10 | systems inside the sandbox. The specific mechanism depends on the 11 | features supported by the underlying operating system and the chosen 12 | sandbox type. Currently, sandboxctl includes support for FreeBSD, 13 | Linux, macOS (Darwin), and NetBSD. 14 | 15 | Each sandbox is defined via a configuration file provided by the user 16 | and a system-specific template provided by this package. The user 17 | configuration is simple and semantically-rich, allowing the system 18 | administrator to not worry about the environment-specific details. 19 | 20 | This is not an official Google product. 21 | 22 | For further information on the contents of this distribution file, 23 | please refer to the following other documents: 24 | 25 | * AUTHORS: List of authors and contributors to this project. 26 | * COPYING: License information. 27 | * INSTALL: Compilation and installation instructions. 28 | * NEWS: List of major changes between formal releases. 29 | 30 | For general project information, please visit: 31 | 32 | https://github.com/jmmv/sandboxctl/ 33 | 34 | Once installed, please open the sandboxctl(8) and sandboxctl.conf(5) 35 | manual pages for usage and configuration information. 36 | -------------------------------------------------------------------------------- /modules/empty.subr: -------------------------------------------------------------------------------- 1 | # Copyright 2016 Google Inc. 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions are 6 | # met: 7 | # 8 | # * Redistributions of source code must retain the above copyright 9 | # notice, this list of conditions and the following disclaimer. 10 | # * Redistributions in binary form must reproduce the above copyright 11 | # notice, this list of conditions and the following disclaimer in the 12 | # documentation and/or other materials provided with the distribution. 13 | # * Neither the name of Google Inc. nor the names of its contributors 14 | # may be used to endorse or promote products derived from this software 15 | # without specific prior written permission. 16 | # 17 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | # \file empty.subr 30 | # Sandbox type to create an empty sandbox. 31 | 32 | # Intentionally empty; use all default hooks. 33 | -------------------------------------------------------------------------------- /NEWS: -------------------------------------------------------------------------------- 1 | Changes in version 1.1 2 | ====================== 3 | 4 | **STILL UNDER DEVELOPMENT; NOT RELEASED YET.** 5 | 6 | * Fixed the `darwin-native` type to allow accessing the Oracle JRE from 7 | within the sandbox. 8 | 9 | * Expose `/usr/local` in `darwin-native` so that third-party software 10 | installed on the host, such as OSXFUSE, can be found. 11 | 12 | * Expose an empty `/private/var/mail` directory within the `darwin-native` 13 | sandbox so that programs that expect the directory to exist can work. 14 | 15 | * Issue 2: Fixed access to the system keychain on `darwin-native` 16 | sandboxes, which allows, among other things, for HTTPS downloads to 17 | work properly. 18 | 19 | * Issue 4: Fixed the `netbsd-native` type to recognize a `/boot` directory 20 | and to ignore the lack of `/netbsd`, as is the case for 21 | NetBSD/evbarm-earmv7hf 7.1 Raspberry Pi images. 22 | 23 | * Issue 5: Fixed DNS resolution in `darwin-native` sandboxes, which 24 | apparently got broken in the macOS Sierra 10.12.4 update because SIP 25 | now prevents modifying the running mDNSResponder instance. 26 | 27 | * Issue 7: Fixed execution of compilers and other Xcode tools within a 28 | `darwin-native` sandbox starting with High Sierra. Unfortunately, we 29 | now require SIP to be disabled for these tools to work. 30 | 31 | * Added support for XZ-compressed files to `sandbox_extract`. 32 | 33 | * Added support for XZ-compressed release sets to `netbsd-native` and 34 | `netbsd-release`. As a side-effect of this change, the 35 | `NETBSD_RELEASE_SETS` setting in `netbsd-release` now takes a list 36 | of set names *without* their extension. 37 | 38 | 39 | Changes in version 1.0 40 | ====================== 41 | 42 | **Released on 2017-02-17.** 43 | 44 | * First public release. 45 | -------------------------------------------------------------------------------- /admin/travis-build.sh: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | # Copyright 2017 Google Inc. 3 | # All rights reserved. 4 | # 5 | # Redistribution and use in source and binary forms, with or without 6 | # modification, are permitted provided that the following conditions are 7 | # met: 8 | # 9 | # * Redistributions of source code must retain the above copyright 10 | # notice, this list of conditions and the following disclaimer. 11 | # * Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # * Neither the name of Google Inc. nor the names of its contributors 15 | # may be used to endorse or promote products derived from this software 16 | # without specific prior written permission. 17 | # 18 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | 30 | set -e -x 31 | 32 | autoreconf -isv -I/usr/local/share/aclocal 33 | 34 | ./configure 35 | 36 | cat >kyua.conf <&2 34 | exit 1 35 | fi 36 | 37 | if [ ! -f configure ]; then 38 | echo "${Prog_Name}: configure not found; nothing to clean?" 1>&2 39 | exit 1 40 | fi 41 | 42 | [ -f Makefile ] || ./configure 43 | make distclean 44 | 45 | # Top-level directory. 46 | rm -f Makefile.in 47 | rm -f aclocal.m4 48 | rm -rf autom4te.cache 49 | rm -f configure 50 | rm -f sandboxctl-*.tar.gz 51 | 52 | # admin directory. 53 | rm -f admin/install-sh 54 | rm -f admin/missing 55 | 56 | # Files and directories spread all around the tree. 57 | find . -name '#*' | xargs rm -rf 58 | find . -name '*~' | xargs rm -rf 59 | 60 | # Show remaining files. 61 | if [ -n "${GIT}" ]; then 62 | echo ">>> untracked and ignored files" 63 | "${GIT}" status --porcelain --ignored | grep -E '^(\?\?|!!)' || true 64 | fi 65 | -------------------------------------------------------------------------------- /admin/travis-install-deps.sh: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | # Copyright 2017 Google Inc. 3 | # All rights reserved. 4 | # 5 | # Redistribution and use in source and binary forms, with or without 6 | # modification, are permitted provided that the following conditions are 7 | # met: 8 | # 9 | # * Redistributions of source code must retain the above copyright 10 | # notice, this list of conditions and the following disclaimer. 11 | # * Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # * Neither the name of Google Inc. nor the names of its contributors 15 | # may be used to endorse or promote products derived from this software 16 | # without specific prior written permission. 17 | # 18 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | 30 | set -e -x 31 | 32 | sudo apt-get update -qq 33 | sudo apt-get install -y liblua5.2-0 libsqlite3-0 pkg-config ${SHELL_NAME} 34 | 35 | install_from_head() { 36 | local user="${1}"; shift 37 | local repo="${1}"; shift 38 | 39 | git clone "https://github.com/${user}/${repo}" 40 | 41 | cd "${repo}" 42 | autoreconf -isv -I/usr/local/share/aclocal 43 | ./configure \ 44 | --without-atf \ 45 | PKG_CONFIG_PATH="/usr/local/lib/pkgconfig" \ 46 | "${@}" 47 | make 48 | sudo make install 49 | cd - 50 | 51 | rm -rf "${repo}" 52 | } 53 | 54 | install_kyua() { 55 | local name="20170225-usr-local-kyua-ubuntu-14-04-amd64-clang.tar.gz" 56 | wget "http://dl.bintray.com/jmmv/kyua/${name}" || return 1 57 | sudo tar -xzvp -C / -f "${name}" 58 | rm -f "${name}" 59 | } 60 | 61 | install_kyua 62 | 63 | shell_path="$(which ${SHELL_NAME})" 64 | if [ ${?} -ne 0 ]; then 65 | echo "Failed to resolve path to shell ${SHELL_NAME}" 1>&2 66 | exit 1 67 | fi 68 | install_from_head jmmv shtk SHTK_SHELL="${shell_path}" 69 | -------------------------------------------------------------------------------- /test_utils.sh: -------------------------------------------------------------------------------- 1 | # Copyright 2016 Google Inc. 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions are 6 | # met: 7 | # 8 | # * Redistributions of source code must retain the above copyright 9 | # notice, this list of conditions and the following disclaimer. 10 | # * Redistributions in binary form must reproduce the above copyright 11 | # notice, this list of conditions and the following disclaimer in the 12 | # documentation and/or other materials provided with the distribution. 13 | # * Neither the name of Google Inc. nor the names of its contributors 14 | # may be used to endorse or promote products derived from this software 15 | # without specific prior written permission. 16 | # 17 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | # \file test_utils.sh 30 | # Miscellaneous test-only utilities. 31 | 32 | : ${SANDBOXCTL_SHTK_MODULESDIR:="__SANDBOXCTL_SHTK_MODULESDIR__"} 33 | SHTK_MODULESPATH="${SANDBOXCTL_SHTK_MODULESDIR}" shtk_import sandbox 34 | _SANDBOX_BINDFS_EXTRA_OPTS=,direct_io 35 | 36 | 37 | # Isolate the sandbox type under test so that sandboxctl only sees that one. 38 | # 39 | # \post SANDBOXCTL_MODULESDIR is modified to point to the temporary directory 40 | # and is exported so that invocations of sandboxctl can use the new value. 41 | isolate_module() { 42 | local module="${1}"; shift 43 | 44 | mkdir modules 45 | ln -s "${SANDBOXCTL_MODULESDIR}/${module}.subr" modules/ 46 | SANDBOXCTL_MODULESDIR="$(pwd)/modules"; export SANDBOXCTL_MODULESDIR 47 | } 48 | 49 | 50 | # Mounts a tmpfs file system for testing purposes. 51 | # 52 | # \param mount_point Directory in which to mount the tmpfs file system. 53 | mount_tmpfs() { 54 | local mount_point="${1}"; shift 55 | 56 | case "$(uname -s)" in 57 | Darwin) 58 | # Assume sandbox_bindfs works properly for simplicity here. 59 | # If not, we'll get all kinds of failures and we'll also get the 60 | # unit tests for sandbox_bindfs to fail, which will help in 61 | # understanding the problem. 62 | local tmpdir="$(mktemp -d -t fake_tmpfs)" 63 | sandbox_bindfs -o rw "${tmpdir}" "${mount_point}" 64 | ;; 65 | 66 | FreeBSD|Linux|NetBSD) 67 | mount -t tmpfs tmpfs "${mount_point}" \ 68 | || atf_fail "Failed to mount a tmpfs file system" 69 | ;; 70 | 71 | *) 72 | atf_skip "Don't know how to mount a tmpfs file system" 73 | ;; 74 | esac 75 | } 76 | -------------------------------------------------------------------------------- /modules/linux_native.subr: -------------------------------------------------------------------------------- 1 | # Copyright 2017 Google Inc. 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions are 6 | # met: 7 | # 8 | # * Redistributions of source code must retain the above copyright 9 | # notice, this list of conditions and the following disclaimer. 10 | # * Redistributions in binary form must reproduce the above copyright 11 | # notice, this list of conditions and the following disclaimer in the 12 | # documentation and/or other materials provided with the distribution. 13 | # * Neither the name of Google Inc. nor the names of its contributors 14 | # may be used to endorse or promote products derived from this software 15 | # without specific prior written permission. 16 | # 17 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | # \file linux_native.subr 30 | # Sandbox type to null-mount a live Linux system. 31 | # 32 | # This sandbox type is pretty permissive in the file system layout to try 33 | # to support a wide variety of Linux distributions. 34 | 35 | shtk_import cli 36 | shtk_import sandbox 37 | 38 | # List of directories to null-mount in read-only mode. 39 | # 40 | # Only the directories that exist will be remounted, so this list tries 41 | # to cover multiple potential file system layouts. 42 | _LINUX_NATIVE_BINDFS_MOUNTS=" 43 | bin boot dev lib lib32 lib64 libdata libexec proc run sbin sys 44 | usr/bin usr/include usr/lib usr/lib32 usr/lib64 usr/libdata usr/libexec 45 | usr/sbin usr/share" 46 | 47 | 48 | # Sets up a Linux sandbox that uses null mounts. 49 | # 50 | # \param root Path to the sandbox directory. 51 | linux_native_create() { 52 | local root="${1}"; shift 53 | 54 | for dir in ${_LINUX_NATIVE_BINDFS_MOUNTS}; do 55 | if [ -e "/${dir}" ]; then 56 | mkdir -p "${root}/${dir}" || return 57 | fi 58 | done 59 | 60 | mkdir "${root}/tmp" 61 | chmod 1777 "${root}/tmp" 62 | 63 | for dir in etc root; do 64 | shtk_cli_debug "Copying ${dir} into the sandbox" 65 | mkdir "${root}/${dir}" 66 | tar -c -C "/${dir}" -f - . | tar -xp -C "${root}/${dir}" -f - || return 67 | done 68 | 69 | shtk_cli_debug "Replicating /var layout within the sandbox" 70 | mkdir "${root}/var" 71 | find /var -path "${root}" -prune -o -type d -exec mkdir -p "${root}{}" \; \ 72 | || return 73 | } 74 | 75 | 76 | # Enters a Linux sandbox that uses null mounts. 77 | # 78 | # \param root Path to the sandbox directory. 79 | linux_native_mount() { 80 | local root="${1}"; shift 81 | 82 | for dir in ${_LINUX_NATIVE_BINDFS_MOUNTS}; do 83 | if [ -e "/${dir}" ]; then 84 | sandbox_bindfs "/${dir}" "${root}/${dir}" || return 85 | fi 86 | done 87 | } 88 | -------------------------------------------------------------------------------- /modules/linux_native_test.sh: -------------------------------------------------------------------------------- 1 | #! __ATF_SH__ 2 | # Copyright 2017 Google Inc. 3 | # All rights reserved. 4 | # 5 | # Redistribution and use in source and binary forms, with or without 6 | # modification, are permitted provided that the following conditions are 7 | # met: 8 | # 9 | # * Redistributions of source code must retain the above copyright 10 | # notice, this list of conditions and the following disclaimer. 11 | # * Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # * Neither the name of Google Inc. nor the names of its contributors 15 | # may be used to endorse or promote products derived from this software 16 | # without specific prior written permission. 17 | # 18 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | 30 | # \file linux_native_test.sh 31 | # Integration tests for the linux_native sandbox type. 32 | 33 | 34 | # Paths to installed files. 35 | # 36 | # Can be overriden for test purposes only. 37 | : ${SANDBOXCTL_MODULESDIR="__SANDBOXCTL_MODULESDIR__"} 38 | 39 | 40 | atf_test_case config__builtins 41 | config__builtins_body() { 42 | isolate_module linux_native 43 | 44 | cat >expout <custom.conf </tmp/example.html' 79 | [ -f sandbox/tmp/testfile ] || atf_fail 'Test file not created as expected' 80 | [ -f sandbox/tmp/sufile ] || atf_fail 'Test file not created as expected' 81 | dd if=/dev/zero of=testfile bs=1k count=1 82 | cmp -s sandbox/tmp/testfile testfile || atf_fail 'Test file invalid' 83 | grep -i '/dev/null \ 84 | || atf_fail 'Invalid response from example.com; bad DNS configuration?' 85 | 86 | atf_check sandboxctl -c custom.conf destroy 87 | rm custom.conf 88 | } 89 | integration_cleanup() { 90 | [ ! -f custom.conf ] || sandboxctl -c custom.conf destroy || true 91 | } 92 | 93 | 94 | atf_init_test_cases() { 95 | atf_add_test_case config__builtins 96 | 97 | atf_add_test_case integration 98 | } 99 | -------------------------------------------------------------------------------- /modules/netbsd_native_test.sh: -------------------------------------------------------------------------------- 1 | #! __ATF_SH__ 2 | # Copyright 2013 Google Inc. 3 | # All rights reserved. 4 | # 5 | # Redistribution and use in source and binary forms, with or without 6 | # modification, are permitted provided that the following conditions are 7 | # met: 8 | # 9 | # * Redistributions of source code must retain the above copyright 10 | # notice, this list of conditions and the following disclaimer. 11 | # * Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # * Neither the name of Google Inc. nor the names of its contributors 15 | # may be used to endorse or promote products derived from this software 16 | # without specific prior written permission. 17 | # 18 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | 30 | # \file netbsd_native_test.sh 31 | # Integration tests for the netbsd_native sandbox type. 32 | 33 | 34 | # Paths to installed files. 35 | # 36 | # Can be overriden for test purposes only. 37 | : ${SANDBOXCTL_MODULESDIR="__SANDBOXCTL_MODULESDIR__"} 38 | 39 | 40 | atf_test_case config__builtins 41 | config__builtins_body() { 42 | isolate_module netbsd_native 43 | 44 | cat >expout <custom.conf </dev/null \ 88 | || atf_fail 'Invalid response from example.com; bad DNS configuration?' 89 | 90 | atf_check sandboxctl -c custom.conf destroy 91 | rm custom.conf 92 | } 93 | integration_cleanup() { 94 | [ ! -f custom.conf ] || sandboxctl -c custom.conf destroy || true 95 | } 96 | 97 | 98 | atf_init_test_cases() { 99 | atf_add_test_case config__builtins 100 | 101 | atf_add_test_case integration 102 | } 103 | -------------------------------------------------------------------------------- /modules/freebsd_native.subr: -------------------------------------------------------------------------------- 1 | # Copyright 2016 Google Inc. 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions are 6 | # met: 7 | # 8 | # * Redistributions of source code must retain the above copyright 9 | # notice, this list of conditions and the following disclaimer. 10 | # * Redistributions in binary form must reproduce the above copyright 11 | # notice, this list of conditions and the following disclaimer in the 12 | # documentation and/or other materials provided with the distribution. 13 | # * Neither the name of Google Inc. nor the names of its contributors 14 | # may be used to endorse or promote products derived from this software 15 | # without specific prior written permission. 16 | # 17 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | # \file freebsd_native.subr 30 | # Sandbox type to null-mount a live FreeBSD system. 31 | 32 | shtk_import cli 33 | shtk_import sandbox 34 | 35 | # List of directories to null-mount in read-only mode. 36 | _FREEBSD_NATIVE_BINDFS_MOUNTS=" 37 | bin lib libexec sbin 38 | usr/bin usr/include usr/lib usr/libdata usr/libexec usr/sbin usr/share" 39 | 40 | 41 | # Prints the configuration variables exposed by this sandbox type. 42 | freebsd_native_config_vars() { 43 | echo "FREEBSD_NATIVE_SOURCE" 44 | } 45 | 46 | 47 | # Sets defaults for the configuration variables used by this sandbox type. 48 | freebsd_native_set_defaults() { 49 | # Remember to update sandboxctl.conf(5) if you change any default values. 50 | shtk_config_set FREEBSD_NATIVE_SOURCE "/usr/src" 51 | } 52 | 53 | 54 | # Sets up a FreeBSD sandbox that uses null mounts. 55 | # 56 | # \param root Path to the sandbox directory. 57 | freebsd_native_create() { 58 | local root="${1}"; shift 59 | 60 | local source="$(shtk_config_get FREEBSD_NATIVE_SOURCE)" 61 | shtk_cli_debug "Bootstrapping sandbox from ${source}" 62 | 63 | quiet() { "${@}"; } 64 | shtk_cli_log_level debug || quiet() { "${@}" >/dev/null 2>&1; } 65 | 66 | if [ -e "${source}/Makefile.inc1" ]; then 67 | for target in distrib-dirs _obj everything distribution; do 68 | MAKEOBJDIRPREFIX="${root}/usr/obj" quiet make -C "${source}" \ 69 | DESTDIR="${root}" SUBDIR_OVERRIDE=etc "${target}" \ 70 | || shtk_cli_error "make ${target} failed" 71 | done 72 | elif [ -e "${source}/base.txz" ]; then 73 | # TODO(jmmv): Extend sandbox_extract to support xz files and use it here 74 | # instead of the direct call to tar. 75 | quiet tar -xJvf "${source}/base.txz" -C "${root}" ./etc ./root ./var 76 | quiet mtree -deU -f "${root}/etc/mtree/BSD.root.dist" -p "${root}" 77 | quiet mtree -deU -f "${root}/etc/mtree/BSD.usr.dist" -p "${root}/usr" 78 | quiet mtree -deU -f "${root}/etc/mtree/BSD.var.dist" -p "${root}/var" 79 | else 80 | shtk_cli_error "Cannot find ${source}" 81 | fi 82 | chflags -R noschg,nouchg "${root}" ${quiet} 83 | rm -rf "${root}/usr/obj" ${quiet} 84 | } 85 | 86 | 87 | # Enters a FreeBSD sandbox that uses null mounts. 88 | # 89 | # \param root Path to the sandbox directory. 90 | freebsd_native_mount() { 91 | local root="${1}"; shift 92 | 93 | cmp -s /etc/resolv.conf "${root}/etc" || cp /etc/resolv.conf "${root}/etc" 94 | 95 | for dir in ${_FREEBSD_NATIVE_BINDFS_MOUNTS}; do 96 | sandbox_bindfs "/${dir}" "${root}/${dir}" 97 | done 98 | 99 | mount -t devfs devfs "${root}/dev" 100 | 101 | grep "^root:.*:/bin/sh$" "${root}/etc/passwd" >/dev/null 2>&1 \ 102 | || chroot "${root}" chsh -s /bin/sh root 103 | } 104 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | dnl Copyright 2013 Google Inc. 2 | dnl All rights reserved. 3 | dnl 4 | dnl Redistribution and use in source and binary forms, with or without 5 | dnl modification, are permitted provided that the following conditions are 6 | dnl met: 7 | dnl 8 | dnl * Redistributions of source code must retain the above copyright 9 | dnl notice, this list of conditions and the following disclaimer. 10 | dnl * Redistributions in binary form must reproduce the above copyright 11 | dnl notice, this list of conditions and the following disclaimer in the 12 | dnl documentation and/or other materials provided with the distribution. 13 | dnl * Neither the name of Google Inc. nor the names of its contributors 14 | dnl may be used to endorse or promote products derived from this software 15 | dnl without specific prior written permission. 16 | dnl 17 | dnl THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | dnl "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | dnl LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | dnl A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | dnl OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | dnl SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | dnl LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | dnl DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | dnl THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | dnl (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | dnl OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | AC_INIT([sandboxctl], [1.1], 30 | [https://github.com/jmmv/sandboxctl/issues/], [sandboxctl], 31 | [https://github.com/jmmv/sandboxctl/]) 32 | AC_PREREQ([2.65]) 33 | 34 | 35 | AC_COPYRIGHT([Copyright 2013 Google Inc.]) 36 | AC_CONFIG_AUX_DIR([admin]) 37 | AC_CONFIG_FILES([Makefile]) 38 | AC_CONFIG_SRCDIR([sandboxctl.sh]) 39 | 40 | 41 | AM_INIT_AUTOMAKE([1.9 check-news foreign subdir-objects -Wall]) 42 | 43 | 44 | AC_MSG_CHECKING([for operating system name]) 45 | osname="$(uname -s)" 46 | AC_MSG_RESULT([${osname}]) 47 | case "${osname}" in 48 | Darwin|FreeBSD|Linux|NetBSD) 49 | ;; 50 | *) 51 | AC_MSG_ERROR([Operating system ${osname} is not supported]) 52 | ;; 53 | esac 54 | AM_CONDITIONAL([SANDBOXCTL_OS_DARWIN], [test "${osname}" = Darwin]) 55 | AM_CONDITIONAL([SANDBOXCTL_OS_FREEBSD], [test "${osname}" = FreeBSD]) 56 | AM_CONDITIONAL([SANDBOXCTL_OS_LINUX], [test "${osname}" = Linux]) 57 | AM_CONDITIONAL([SANDBOXCTL_OS_NETBSD], [test "${osname}" = NetBSD]) 58 | AC_SUBST([SANDBOXCTL_OS], [${osname}]) 59 | 60 | 61 | case "${osname}" in 62 | Darwin) 63 | AC_PATH_PROG([BINDFS], [bindfs]) 64 | if test -z "${BINDFS}"; then 65 | AC_MSG_ERROR([bindfs is required but was not found]) 66 | fi 67 | if ! test -e "${BINDFS}"; then 68 | AC_MSG_ERROR([bindfs in ${BINDFS} does not exist]) 69 | fi 70 | ;; 71 | esac 72 | 73 | 74 | AC_ARG_VAR([SANDBOXCTL_CONFSUBDIR], 75 | [Subdirectory of sysconfdir under which to look for files]) 76 | if test x"${SANDBOXCTL_CONFSUBDIR-unset}" = x"unset"; then 77 | SANDBOXCTL_CONFSUBDIR=sandboxctl 78 | else 79 | case "${SANDBOXCTL_CONFSUBDIR}" in 80 | /*) 81 | AC_MSG_ERROR([SANDBOXCTL_CONFSUBDIR must hold a relative path]) 82 | ;; 83 | *) 84 | ;; 85 | esac 86 | fi 87 | if test x"${SANDBOXCTL_CONFSUBDIR}" = x""; then 88 | AC_SUBST(sandboxctl_confdir, \${sysconfdir}) 89 | else 90 | AC_SUBST(sandboxctl_confdir, \${sysconfdir}/\${SANDBOXCTL_CONFSUBDIR}) 91 | fi 92 | 93 | 94 | m4_ifndef([PKG_CHECK_MODULES], 95 | [m4_fatal([Cannot find pkg.m4; see the INSTALL document for help])]) 96 | 97 | m4_ifndef([SHTK_CHECK], 98 | [m4_fatal([Cannot find shtk.m4; see the INSTALL document for help])]) 99 | SHTK_CHECK([>= 1.7]) 100 | 101 | m4_ifndef([ATF_CHECK_SH], 102 | [m4_fatal([Cannot find atf-sh.m4; see the INSTALL document for help])]) 103 | ATF_CHECK_SH([>= 0.17]) 104 | m4_ifndef([ATF_ARG_WITH], 105 | [m4_fatal([Cannot find atf-common.m4; see the INSTALL document for help])]) 106 | ATF_ARG_WITH 107 | 108 | 109 | AC_PATH_PROG([KYUA], [kyua]) 110 | AM_CONDITIONAL([HAVE_KYUA], [test -n "${KYUA}"]) 111 | AC_ARG_VAR([KYUA_FLAGS], 112 | [Additional Kyua flags to use at 'make (|dist|install)check' time]) 113 | 114 | 115 | AC_SUBST(pkgtestsdir, \${testsdir}/sandboxctl) 116 | AC_SUBST(testsdir, \${exec_prefix}/tests) 117 | 118 | 119 | AC_OUTPUT 120 | -------------------------------------------------------------------------------- /modules/netbsd_native.subr: -------------------------------------------------------------------------------- 1 | # Copyright 2013 Google Inc. 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions are 6 | # met: 7 | # 8 | # * Redistributions of source code must retain the above copyright 9 | # notice, this list of conditions and the following disclaimer. 10 | # * Redistributions in binary form must reproduce the above copyright 11 | # notice, this list of conditions and the following disclaimer in the 12 | # documentation and/or other materials provided with the distribution. 13 | # * Neither the name of Google Inc. nor the names of its contributors 14 | # may be used to endorse or promote products derived from this software 15 | # without specific prior written permission. 16 | # 17 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | # \file netbsd_native.subr 30 | # Sandbox type to null-mount a live NetBSD system. 31 | 32 | shtk_import cli 33 | shtk_import config 34 | shtk_import sandbox 35 | 36 | # Prints the list of directories to null-mount in read-only mode. 37 | _netbsd_native_bindfs_mounts() { 38 | echo bin lib libdata libexec sbin 39 | echo usr/bin usr/include usr/lib usr/libdata usr/libexec usr/sbin usr/share 40 | 41 | # The distribution images for NetBSD/evbarm-earmv7hf 7.1 have /boot 42 | # directory containing the kernel (which is a separate partition) instead of 43 | # a /netbsd file. Other ports that require the kernel to be placed on 44 | # dedicated partitions may suffer from this same oddity. 45 | [ ! -d /boot ] || echo boot 46 | } 47 | 48 | 49 | # Prints the configuration variables exposed by this sandbox type. 50 | netbsd_native_config_vars() { 51 | echo "NETBSD_NATIVE_RELEASEDIR" 52 | } 53 | 54 | 55 | # Sets defaults for the configuration variables used by this sandbox type. 56 | netbsd_native_set_defaults() { 57 | # Remember to update sandboxctl.conf(5) if you change any default values. 58 | shtk_config_set NETBSD_NATIVE_RELEASEDIR \ 59 | "/home/sysbuild/release/$(uname -m)" 60 | } 61 | 62 | 63 | # Guesses the format of the release sets and returns their extension. 64 | # 65 | # \param releasedir Path to the root directory of the release files. 66 | _netbsd_release_sets_format() { 67 | local releasedir="${1}"; shift 68 | 69 | if [ -e "${releasedir}/binary/sets/base.tar.xz" ]; then 70 | echo "tar.xz" 71 | else 72 | echo "tgz" 73 | fi 74 | } 75 | 76 | 77 | # Sets up a NetBSD sandbox that uses null mounts. 78 | # 79 | # \param root Path to the sandbox directory. 80 | netbsd_native_create() { 81 | local root="${1}"; shift 82 | 83 | local releasedir="$(shtk_config_get NETBSD_NATIVE_RELEASEDIR)" 84 | [ -d "${releasedir}" ] || shtk_cli_error "Release directory ${releasedir}" \ 85 | "does not exist" 86 | 87 | for dir in $(_netbsd_native_bindfs_mounts); do 88 | mkdir -p "${root}/${dir}" || return 89 | done 90 | mkdir "${root}/home" 91 | 92 | # /netbsd should exist according to hier(7), but if it doesn't, silently 93 | # ignore it. See note above on /boot for details. 94 | if [ -e /netbsd ]; then 95 | cp /netbsd "${root}/netbsd" || return 96 | fi 97 | 98 | local ext="$(_netbsd_release_sets_format "${releasedir}")" 99 | 100 | sandbox_extract "${releasedir}/binary/sets/base.${ext}" "${root}" \ 101 | ./etc ./root ./tmp ./var 102 | sandbox_extract "${releasedir}/binary/sets/etc.${ext}" "${root}" 103 | 104 | shtk_cli_debug "Creating device nodes" 105 | ( cd "${root}/dev" && ./MAKEDEV all ) \ 106 | || shtk_cli_error "MAKEDEV failed" 107 | } 108 | 109 | 110 | # Enters a NetBSD sandbox that uses null mounts. 111 | # 112 | # \param root Path to the sandbox directory. 113 | netbsd_native_mount() { 114 | local root="${1}"; shift 115 | 116 | cmp -s /etc/resolv.conf "${root}/etc/resolv.conf" \ 117 | || cp /etc/resolv.conf "${root}/etc" 118 | 119 | for dir in $(_netbsd_native_bindfs_mounts); do 120 | sandbox_bindfs "/${dir}" "${root}/${dir}" 121 | done 122 | } 123 | -------------------------------------------------------------------------------- /modules/freebsd_native_test.sh: -------------------------------------------------------------------------------- 1 | #! __ATF_SH__ 2 | # Copyright 2016 Google Inc. 3 | # All rights reserved. 4 | # 5 | # Redistribution and use in source and binary forms, with or without 6 | # modification, are permitted provided that the following conditions are 7 | # met: 8 | # 9 | # * Redistributions of source code must retain the above copyright 10 | # notice, this list of conditions and the following disclaimer. 11 | # * Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # * Neither the name of Google Inc. nor the names of its contributors 15 | # may be used to endorse or promote products derived from this software 16 | # without specific prior written permission. 17 | # 18 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | 30 | # \file freebsd_native_test.sh 31 | # Integration tests for the freebsd_native sandbox type. 32 | 33 | 34 | # Paths to installed files. 35 | # 36 | # Can be overriden for test purposes only. 37 | : ${SANDBOXCTL_MODULESDIR="__SANDBOXCTL_MODULESDIR__"} 38 | 39 | 40 | atf_test_case config__builtins 41 | config__builtins_body() { 42 | isolate_module freebsd_native 43 | 44 | cat >expout <>custom.conf <sandbox/tmp/checks.sh <&2; exit 1; } 81 | 82 | pw useradd -n testuser -s /bin/sh -m -w no || fail "User addition failed" 83 | dd if=/dev/zero of=/tmp/testfile bs=1k count=1 || fail "Devices failed" 84 | su root -c "touch /tmp/sufile" || fail "su failed" 85 | fetch -o /tmp/example.html http://example.com/ 86 | 87 | [ "\${SHELL}" = /bin/sh ] || fail "SHELL is \${SHELL} but should be /bin/sh" 88 | EOF 89 | chmod +x sandbox/tmp/checks.sh 90 | 91 | grep "^testuser" sandbox/etc/passwd \ 92 | && atf_fail 'passwd already contains test user' 93 | 94 | atf_check -e ignore sandboxctl -c custom.conf run /tmp/checks.sh 95 | 96 | grep "^testuser" sandbox/etc/passwd \ 97 | || atf_fail 'passwd was not correctly updated' 98 | [ -f sandbox/tmp/testfile ] || atf_fail 'Test file not created as expected' 99 | [ -f sandbox/tmp/sufile ] || atf_fail 'Test file not created as expected' 100 | dd if=/dev/zero of=testfile bs=1k count=1 101 | cmp -s sandbox/tmp/testfile testfile || atf_fail 'Test file invalid' 102 | grep -i '/dev/null \ 103 | || atf_fail 'Invalid response from example.com; bad DNS configuration?' 104 | 105 | atf_check sandboxctl -c custom.conf destroy 106 | rm custom.conf 107 | } 108 | 109 | 110 | common_integration_cleanup() { 111 | [ ! -f custom.conf ] || sandboxctl -c custom.conf destroy || true 112 | } 113 | 114 | 115 | atf_test_case integration_srcdir cleanup 116 | integration__srcdir_head() { 117 | atf_set "require.config" "freebsd_srcdir" 118 | atf_set "require.user" "root" 119 | } 120 | integration__srcdir_body() { 121 | common_integration_body "$(atf_config_get freebsd_srcdir)" 122 | } 123 | integration__srcdir_cleanup() { 124 | common_integration_cleanup 125 | } 126 | 127 | 128 | atf_test_case integration_sets cleanup 129 | integration__sets_head() { 130 | atf_set "require.config" "freebsd_releasedir" 131 | atf_set "require.user" "root" 132 | } 133 | integration__sets_body() { 134 | common_integration_body "$(atf_config_get freebsd_releasedir)" 135 | } 136 | integration__sets_cleanup() { 137 | common_integration_cleanup 138 | } 139 | 140 | 141 | atf_init_test_cases() { 142 | atf_add_test_case config__builtins 143 | 144 | atf_add_test_case integration__srcdir 145 | atf_add_test_case integration__sets 146 | } 147 | -------------------------------------------------------------------------------- /modules/netbsd_release.subr: -------------------------------------------------------------------------------- 1 | # Copyright 2013 Google Inc. 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions are 6 | # met: 7 | # 8 | # * Redistributions of source code must retain the above copyright 9 | # notice, this list of conditions and the following disclaimer. 10 | # * Redistributions in binary form must reproduce the above copyright 11 | # notice, this list of conditions and the following disclaimer in the 12 | # documentation and/or other materials provided with the distribution. 13 | # * Neither the name of Google Inc. nor the names of its contributors 14 | # may be used to endorse or promote products derived from this software 15 | # without specific prior written permission. 16 | # 17 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | # \file netbsd_release.subr 30 | # Sandbox type to extract release sets. 31 | 32 | shtk_import cli 33 | shtk_import config 34 | shtk_import sandbox 35 | 36 | 37 | # Prints the configuration variables exposed by this sandbox type. 38 | netbsd_release_config_vars() { 39 | echo "NETBSD_RELEASE_RELEASEDIR" 40 | echo "NETBSD_RELEASE_SETS" 41 | } 42 | 43 | 44 | # Sets defaults for the configuration variables used by this sandbox type. 45 | netbsd_release_set_defaults() { 46 | # Remember to update sandboxctl.conf(5) if you change any default values. 47 | shtk_config_set NETBSD_RELEASE_RELEASEDIR \ 48 | "/home/sysbuild/release/$(uname -m)" 49 | } 50 | 51 | 52 | # Guesses the format of the release sets and returns their extension. 53 | # 54 | # \param releasedir Path to the root directory of the release files. 55 | _netbsd_release_sets_format() { 56 | local releasedir="${1}"; shift 57 | 58 | if [ -e "${releasedir}/binary/sets/base.tar.xz" ]; then 59 | echo "tar.xz" 60 | else 61 | echo "tgz" 62 | fi 63 | } 64 | 65 | 66 | # Computes the collection of sets to extract for the release. 67 | # 68 | # If the user has specified NETBSD_SETS in the configuration file, we respect 69 | # their choice. 70 | # 71 | # Otherwise, we automatically expand all the sets in the release directory. 72 | # Because a release directory can include more than one kern-* set, we prefer a 73 | # kern-GENERIC if it exists; else, we pick the first name in lexicographical 74 | # order. 75 | # 76 | # \param releasedir Path to the root directory of the release files. 77 | # 78 | # \post Prints the name of the detected sets, including their extensions. 79 | _netbsd_release_get_sets() { 80 | local releasedir="${1}"; shift 81 | 82 | local ext="$(_netbsd_release_sets_format "${releasedir}")" 83 | 84 | if shtk_config_has NETBSD_RELEASE_SETS; then 85 | # sandboxctl 1.0 used to require the set names to be suffixed with .tgz 86 | # but we do not require this any longer: starting from NetBSD 8.99.x, 87 | # the release can be built with .tar.xz, so we need to auto-detect which 88 | # one is in use. For backwards compatibility, keep any extensions given 89 | # by the user. 90 | set -- $(shtk_config_get NETBSD_RELEASE_SETS) 91 | for set_name in "${@}"; do 92 | case "${set_name}" in 93 | *.*) echo "${set_name}" ;; 94 | *) echo "${set_name}.${ext}" ;; 95 | esac 96 | done 97 | else 98 | local kernel_set= 99 | for name in $(cd "${releasedir}/binary/sets" \ 100 | && echo *.${ext} | fmt 1 | sort) 101 | do 102 | case "${name}" in 103 | kern-GENERIC.*) 104 | kernel_set="${name}" 105 | ;; 106 | kern-*) 107 | [ -n "${kernel_set}" ] || kernel_set="${name}" 108 | ;; 109 | *) 110 | echo "${name}" 111 | ;; 112 | esac 113 | done 114 | [ -z "${kernel_set}" ] || echo "${kernel_set}" 115 | fi 116 | } 117 | 118 | 119 | # Sets up a NetBSD sandbox that uses release sets. 120 | # 121 | # \param root Path to the sandbox directory. 122 | netbsd_release_create() { 123 | local root="${1}"; shift 124 | 125 | local releasedir="$(shtk_config_get NETBSD_RELEASE_RELEASEDIR)" 126 | [ -d "${releasedir}" ] || shtk_cli_error \ 127 | "Release directory ${releasedir} does not exist" 128 | 129 | local sets="$(_netbsd_release_get_sets "${releasedir}")" 130 | 131 | for tar_set in ${sets}; do 132 | sandbox_extract "${releasedir}/binary/sets/${tar_set}" \ 133 | "${root}" || shtk_cli_error "Failed to extract ${tar_set}" 134 | done 135 | 136 | shtk_cli_debug "Creating device nodes" 137 | chroot "${root}" /bin/sh -c "cd /dev && ./MAKEDEV all" 138 | } 139 | 140 | 141 | # Enters a NetBSD sandbox that uses release sets. 142 | # 143 | # \param root Path to the sandbox directory. 144 | netbsd_release_mount() { 145 | local root="${1}"; shift 146 | 147 | cmp -s /etc/resolv.conf "${root}/etc/resolv.conf" \ 148 | || cp /etc/resolv.conf "${root}/etc" 149 | } 150 | -------------------------------------------------------------------------------- /INSTALL: -------------------------------------------------------------------------------- 1 | Introduction 2 | ============ 3 | 4 | sandboxctl uses the GNU Automake and GNU Autoconf utilities as its build 5 | system. These are used only when building the package from the source 6 | code tree. If you want to install sandboxctl from a prebuilt package 7 | provided by your operating system, you do not need to read this 8 | document. 9 | 10 | For the impatient: 11 | 12 | $ ./configure 13 | $ make 14 | $ make check 15 | Gain root privileges 16 | # make install 17 | Drop root privileges 18 | $ make installcheck 19 | 20 | Or alternatively, install as a regular user into your home directory: 21 | 22 | $ ./configure --prefix ~/local 23 | $ make 24 | $ make check 25 | $ make install 26 | $ make installcheck 27 | 28 | 29 | Dependencies 30 | ============ 31 | 32 | To build and use sandboxctl successfully you need: 33 | 34 | * bindfs (only on macOS). 35 | * shtk 1.7 or greater. 36 | * pkg-config. 37 | 38 | Optionally, if you want to build and run the tests (recommended), you 39 | need: 40 | 41 | * ATF 0.17 or greater. 42 | * Kyua 0.6 or greater. 43 | 44 | If you are building sandboxctl from the code on the repository, you will 45 | also need the following tools: 46 | 47 | * GNU Autoconf. 48 | * GNU Automake. 49 | 50 | 51 | Regenerating the build system 52 | ============================= 53 | 54 | This is not necessary if you are building from a formal release 55 | distribution file. 56 | 57 | On the other hand, if you are building sandboxctl from code extracted 58 | from the repository, you must first regenerate the files used by the 59 | build system. You will also need to do this if you modify configure.ac, 60 | Makefile.am or any of the other build system files. To do this, simply 61 | run: 62 | 63 | $ autoreconf -i -s 64 | 65 | If ATF and/or shtk are installed in a different prefix than Autoconf, 66 | you will also need to tell autoreconf where the ATF and shtk M4 macros 67 | are located. Otherwise, the configure script will be incomplete and 68 | will show confusing syntax errors mentioning, for example, ATF_CHECK_SH. 69 | To fix this, you have to run autoreconf in the following manner, 70 | replacing '' and '' with the appropriate path: 71 | 72 | $ autoreconf -i -s -I /share/aclocal \ 73 | -I /share/aclocal 74 | 75 | 76 | General build procedure 77 | ======================= 78 | 79 | To build and install the source package, you must follow these steps: 80 | 81 | 1. Configure the sources to adapt to your operating system. This is 82 | done using the 'configure' script located on the sources' top 83 | directory, and it is usually invoked without arguments unless you 84 | want to change the installation prefix. More details on this 85 | procedure are given on a later section. 86 | 87 | 2. Build the sources to generate the binaries and scripts. Simply run 88 | 'make' on the sources' top directory after configuring them. No 89 | problems should arise. 90 | 91 | 3. Install the library by running 'make install'. You may need to 92 | become root to issue this step. 93 | 94 | 4. Issue any manual installation steps that may be required. These are 95 | described later in their own section. 96 | 97 | 5. Check that the installed library works by running 'make 98 | installcheck'. You do not need to be root to do this. 99 | 100 | 101 | Configuration flags 102 | =================== 103 | 104 | The most common, standard flags given to 'configure' are: 105 | 106 | * --prefix=directory 107 | Possible values: Any path 108 | Default: /usr/local 109 | 110 | Specifies where the library (scripts and all associated files) will 111 | be installed. 112 | 113 | * --sysconfdir=directory 114 | Possible values: Any path 115 | Default: /usr/local/etc 116 | 117 | Specifies where the installed programs will look for configuration 118 | files. '/sandboxctl' will be appended to the given path unless 119 | SANDBOXCTL_CONFSUBDIR is redefined as explained later on. 120 | 121 | * --help 122 | Shows information about all available flags and exits immediately, 123 | without running any configuration tasks. 124 | 125 | The following environment variables are specific to sandboxctl's 126 | 'configure' script: 127 | 128 | * BINDFS (macOS only) 129 | Possible values: empty, an absolute path. 130 | Default: empty. 131 | 132 | Specifies the path to the bindfs binary. If not specified, the 133 | configure script will try to look for this in the path and abort if 134 | not found. 135 | 136 | * KYUA_FLAGS: 137 | Possible values: empty, multiple flags as one string. 138 | Default: empty. 139 | 140 | Specifies additional flags to pass to Kyua when running any of the 141 | `check`, `installcheck` or `distcheck` targets on this source tree. 142 | This setting is exclusively used to customize the test runs of Kyua 143 | itself and has no effect whatsoever on the built product. 144 | 145 | * SANDBOXCTL_CONFSUBDIR 146 | Possible values: empty, a relative path. 147 | Default: sandboxctl. 148 | 149 | Specifies the subdirectory of the configuration directory (given by 150 | the --sysconfdir argument) under which sandboxctl will search for its 151 | configuration files. 152 | 153 | The following flags are specific to sandboxctl's 'configure' script: 154 | 155 | * --with-atf 156 | Possible values: yes, no, auto. 157 | Default: auto. 158 | 159 | Enables usage of ATF to build (and later install) the tests. 160 | 161 | Setting this to 'yes' causes the configure script to look for ATF 162 | unconditionally and abort if not found. Setting this to 'auto' lets 163 | configure perform the best decision based on availability of ATF. 164 | Setting this to 'no' explicitly disables ATF usage. 165 | 166 | When support for tests is enabled, the build process will generate the 167 | test programs and will later install them into the tests tree. 168 | Running 'make check' or 'make installcheck' from within the source 169 | directory will cause these tests to be run with Kyua (assuming it is 170 | also installed). 171 | 172 | 173 | Run the tests! 174 | ============== 175 | 176 | Lastly, after a successful installation (and assuming you built the 177 | sources with support for ATF), you should periodically run the tests 178 | from the final location to ensure things remain stable. Do so as 179 | follows: 180 | 181 | $ kyua test -k /usr/local/tests/sandboxctl/Kyuafile 182 | 183 | And if you see any tests fail, do not hesitate to report them in: 184 | 185 | https://github.com/jmmv/sandboxctl/issues/ 186 | 187 | Thank you! 188 | -------------------------------------------------------------------------------- /modules/darwin_native_test.sh: -------------------------------------------------------------------------------- 1 | #! __ATF_SH__ 2 | # Copyright 2016 Google Inc. 3 | # All rights reserved. 4 | # 5 | # Redistribution and use in source and binary forms, with or without 6 | # modification, are permitted provided that the following conditions are 7 | # met: 8 | # 9 | # * Redistributions of source code must retain the above copyright 10 | # notice, this list of conditions and the following disclaimer. 11 | # * Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # * Neither the name of Google Inc. nor the names of its contributors 15 | # may be used to endorse or promote products derived from this software 16 | # without specific prior written permission. 17 | # 18 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | 30 | # \file darwin_native_test.sh 31 | # Integration tests for the darwin_native sandbox type. 32 | 33 | 34 | # Paths to installed files. 35 | # 36 | # Can be overriden for test purposes only. 37 | : ${SANDBOXCTL_MODULESDIR="__SANDBOXCTL_MODULESDIR__"} 38 | 39 | 40 | atf_test_case config__builtins 41 | config__builtins_body() { 42 | isolate_module darwin_native 43 | 44 | cat >expout <custom.conf </tmp/getconf \ 88 | && su root -c "touch /tmp/sufile" \ 89 | && cp /etc/resolv.conf /tmp/resolv.conf \ 90 | && curl example.com >/tmp/example.html \ 91 | && curl -IsS https://github.com/ | head -n 1 >/tmp/github.head' 92 | [ -f sandbox/tmp/testfile ] || atf_fail 'Test file not created as expected' 93 | [ -s sandbox/tmp/getconf ] || atf_fail 'Test file not created as expected' 94 | [ -f sandbox/tmp/sufile ] || atf_fail 'Test file not created as expected' 95 | [ -s sandbox/tmp/resolv.conf ] || atf_fail 'resolv.conf is bogus' 96 | dd if=/dev/zero of=testfile bs=1k count=1 97 | cmp -s sandbox/tmp/testfile testfile || atf_fail 'Test file invalid' 98 | grep -i '/dev/null \ 99 | || atf_fail 'Invalid response from example.com; bad DNS configuration?' 100 | grep -i 'HTTP.* OK' sandbox/tmp/github.head >/dev/null \ 101 | || atf_fail 'Invalid response from github.com; bad security settings?' 102 | 103 | atf_check sandboxctl -c custom.conf destroy 104 | rm custom.conf 105 | } 106 | integration_cleanup() { 107 | [ ! -f custom.conf ] || sandboxctl -c custom.conf destroy || true 108 | } 109 | 110 | 111 | atf_test_case integration__with_xcode cleanup 112 | integration__with_xcode_head() { 113 | atf_set "require.files" "/Applications/Xcode.app" 114 | atf_set "require.user" "root" 115 | atf_set "timeout" "900" 116 | } 117 | integration__with_xcode_body() { 118 | [ "$(uname -s)" = 'Darwin' ] || atf_skip "Requires a Darwin host" 119 | 120 | cat >custom.conf <sandbox/tmp/hello.c < 131 | int main(void) { 132 | printf("Hello, Xcode!\n"); 133 | return 0; 134 | } 135 | EOF 136 | 137 | atf_check -o match:'Hello, Xcode!' -e ignore \ 138 | sandboxctl -c custom.conf run /bin/sh -c \ 139 | 'xcodebuild -license accept \ 140 | ; cc -o /tmp/hello /tmp/hello.c \ 141 | && /tmp/hello' 142 | [ -x sandbox/tmp/hello ] || atf_fail 'Test binary not created as expected' 143 | 144 | atf_check sandboxctl -c custom.conf destroy 145 | rm custom.conf 146 | } 147 | integration_cleanup() { 148 | [ ! -f custom.conf ] || sandboxctl -c custom.conf destroy || true 149 | } 150 | 151 | 152 | atf_init_test_cases() { 153 | atf_add_test_case config__builtins 154 | 155 | atf_add_test_case integration__basic 156 | atf_add_test_case integration__with_xcode 157 | } 158 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | # Copyright 2013 Google Inc. 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions are 6 | # met: 7 | # 8 | # * Redistributions of source code must retain the above copyright 9 | # notice, this list of conditions and the following disclaimer. 10 | # * Redistributions in binary form must reproduce the above copyright 11 | # notice, this list of conditions and the following disclaimer in the 12 | # documentation and/or other materials provided with the distribution. 13 | # * Neither the name of Google Inc. nor the names of its contributors 14 | # may be used to endorse or promote products derived from this software 15 | # without specific prior written permission. 16 | # 17 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | PHONY_TARGETS = 30 | 31 | doc_DATA = AUTHORS CONTRIBUTING.md CONTRIBUTORS COPYING NEWS README 32 | noinst_DATA = INSTALL README 33 | EXTRA_DIST = $(doc_DATA) INSTALL README 34 | 35 | BUILD_SED = \ 36 | sed -e 's,__ATF_SH__,$(ATF_SH),g' \ 37 | -e 's,__BINDFS__,$(BINDFS),g' \ 38 | -e 's,__SANDBOXCTL_ETCDIR__,$(sandboxctl_confdir),g' \ 39 | -e 's,__SANDBOXCTL_MODULESDIR__,$(pkgdatadir),g' \ 40 | -e 's,__SANDBOXCTL_SHTK_MODULESDIR__,$(pkgdatadir)/shtk,g' \ 41 | -e 's,__SHTK__,$(SHTK_TOOL),g' 42 | BUILD_SCRIPT = \ 43 | cat $${sources} | $(BUILD_SED) | \ 44 | $(SHTK_TOOL) build -o "$${target}" - 45 | BUILD_FILE = \ 46 | cat $${sources} | $(BUILD_SED) >"$${target}" 47 | BUILD_MAN = \ 48 | cat $${sources} | $(BUILD_SED) | awk ' \ 49 | BEGIN { skip = 0 } \ 50 | /^@if $(SANDBOXCTL_OS)$$/ { skip = 0; next } \ 51 | /^@if / { skip = 1; next } \ 52 | /^@endif$$/ { skip = 0; next } \ 53 | { if (!skip) print } \ 54 | ' >"$${target}" 55 | BUILD_TEST = \ 56 | cat $${sources} | $(BUILD_SED) \ 57 | | $(SHTK_TOOL) build -s '$(ATF_SH)' -m '' -o "$${target}" - 58 | 59 | dist_pkgdata_DATA = modules/empty.subr 60 | if SANDBOXCTL_OS_DARWIN 61 | dist_pkgdata_DATA += modules/darwin_native.subr 62 | else 63 | EXTRA_DIST += modules/darwin_native.subr 64 | endif 65 | if SANDBOXCTL_OS_FREEBSD 66 | dist_pkgdata_DATA += modules/freebsd_native.subr 67 | else 68 | EXTRA_DIST += modules/freebsd_native.subr 69 | endif 70 | if SANDBOXCTL_OS_LINUX 71 | dist_pkgdata_DATA += modules/linux_native.subr 72 | else 73 | EXTRA_DIST += modules/linux_native.subr 74 | endif 75 | if SANDBOXCTL_OS_NETBSD 76 | dist_pkgdata_DATA += modules/netbsd_native.subr 77 | dist_pkgdata_DATA += modules/netbsd_release.subr 78 | else 79 | EXTRA_DIST += modules/netbsd_native.subr 80 | EXTRA_DIST += modules/netbsd_release.subr 81 | endif 82 | 83 | nodist_sandboxctl_conf_DATA = default.conf 84 | CLEANFILES = default.conf 85 | EXTRA_DIST += examples/Darwin.conf 86 | EXTRA_DIST += examples/FreeBSD.conf 87 | EXTRA_DIST += examples/Linux.conf 88 | EXTRA_DIST += examples/NetBSD.conf 89 | default.conf: $(srcdir)/examples/$(SANDBOXCTL_OS).conf 90 | $(AM_V_GEN)cp $(srcdir)/examples/$(SANDBOXCTL_OS).conf default.conf 91 | 92 | EXTRA_DIST += test_utils.sh 93 | 94 | shtk_modulesdir = $(pkgdatadir)/shtk 95 | nodist_shtk_modules_DATA = sandbox.subr 96 | CLEANFILES += sandbox.subr 97 | EXTRA_DIST += sandbox.subr.in 98 | sandbox.subr: $(srcdir)/sandbox.subr.in 99 | sources="$(srcdir)/sandbox.subr.in" target=sandbox.subr; $(BUILD_FILE) 100 | 101 | sbin_SCRIPTS = sandboxctl 102 | CLEANFILES += sandboxctl 103 | EXTRA_DIST += sandboxctl.sh 104 | sandboxctl: $(srcdir)/sandboxctl.sh 105 | sources="$(srcdir)/sandboxctl.sh" target=sandboxctl; $(BUILD_SCRIPT) 106 | 107 | man_MANS = sandboxctl.8 108 | CLEANFILES += sandboxctl.8 109 | EXTRA_DIST += sandboxctl.8.in 110 | sandboxctl.8: $(srcdir)/sandboxctl.8.in 111 | sources="$(srcdir)/sandboxctl.8.in" target=sandboxctl.8; $(BUILD_MAN) 112 | 113 | man_MANS += sandboxctl.conf.5 114 | CLEANFILES += sandboxctl.conf.5 115 | EXTRA_DIST += sandboxctl.conf.5.in 116 | sandboxctl.conf.5: $(srcdir)/sandboxctl.conf.5.in 117 | sources="$(srcdir)/sandboxctl.conf.5.in" target=sandboxctl.conf.5; \ 118 | $(BUILD_MAN) 119 | 120 | if WITH_ATF 121 | dist_pkgtests_DATA = Kyuafile 122 | 123 | pkgtests_SCRIPTS = sandbox_test 124 | CLEANFILES += sandbox_test 125 | EXTRA_DIST += sandbox_test.sh 126 | sandbox_test: $(srcdir)/sandbox_test.sh $(srcdir)/test_utils.sh 127 | sources="$(srcdir)/sandbox_test.sh $(srcdir)/test_utils.sh"; \ 128 | target=sandbox_test; $(BUILD_TEST) 129 | 130 | pkgtests_SCRIPTS += sandboxctl_test 131 | CLEANFILES += sandboxctl_test 132 | EXTRA_DIST += sandboxctl_test.sh 133 | sandboxctl_test: $(srcdir)/sandboxctl_test.sh $(srcdir)/test_utils.sh 134 | sources="$(srcdir)/sandboxctl_test.sh $(srcdir)/test_utils.sh" \ 135 | target=sandboxctl_test; $(BUILD_TEST) 136 | 137 | modulestestsdir = $(pkgtestsdir)/modules 138 | dist_modulestests_DATA = modules/Kyuafile 139 | 140 | modulestests_SCRIPTS = 141 | 142 | EXTRA_DIST += modules/darwin_native_test.sh 143 | if SANDBOXCTL_OS_DARWIN 144 | modulestests_SCRIPTS += modules/darwin_native_test 145 | CLEANFILES += modules/darwin_native_test 146 | modules/darwin_native_test: $(srcdir)/modules/darwin_native_test.sh \ 147 | $(srcdir)/test_utils.sh 148 | test -d modules || mkdir -p modules 149 | sources="$(srcdir)/modules/darwin_native_test.sh $(srcdir)/test_utils.sh" \ 150 | target=modules/darwin_native_test; $(BUILD_TEST) 151 | endif 152 | 153 | EXTRA_DIST += modules/freebsd_native_test.sh 154 | if SANDBOXCTL_OS_FREEBSD 155 | modulestests_SCRIPTS += modules/freebsd_native_test 156 | CLEANFILES += modules/freebsd_native_test 157 | modules/freebsd_native_test: $(srcdir)/modules/freebsd_native_test.sh \ 158 | $(srcdir)/test_utils.sh 159 | test -d modules || mkdir -p modules 160 | sources="$(srcdir)/modules/freebsd_native_test.sh $(srcdir)/test_utils.sh" \ 161 | target=modules/freebsd_native_test; $(BUILD_TEST) 162 | endif 163 | 164 | EXTRA_DIST += modules/linux_native_test.sh 165 | if SANDBOXCTL_OS_LINUX 166 | modulestests_SCRIPTS += modules/linux_native_test 167 | CLEANFILES += modules/linux_native_test 168 | modules/linux_native_test: $(srcdir)/modules/linux_native_test.sh \ 169 | $(srcdir)/test_utils.sh 170 | test -d modules || mkdir -p modules 171 | sources="$(srcdir)/modules/linux_native_test.sh $(srcdir)/test_utils.sh" \ 172 | target=modules/linux_native_test; $(BUILD_TEST) 173 | endif 174 | 175 | EXTRA_DIST += modules/netbsd_native_test.sh 176 | if SANDBOXCTL_OS_NETBSD 177 | modulestests_SCRIPTS += modules/netbsd_native_test 178 | CLEANFILES += modules/netbsd_native_test 179 | modules/netbsd_native_test: $(srcdir)/modules/netbsd_native_test.sh \ 180 | $(srcdir)/test_utils.sh 181 | test -d modules || mkdir -p modules 182 | sources="$(srcdir)/modules/netbsd_native_test.sh $(srcdir)/test_utils.sh" \ 183 | target=modules/netbsd_native_test; $(BUILD_TEST) 184 | endif 185 | 186 | if SANDBOXCTL_OS_NETBSD 187 | modulestests_SCRIPTS += modules/netbsd_release_test 188 | CLEANFILES += modules/netbsd_release_test 189 | EXTRA_DIST += modules/netbsd_release_test.sh 190 | modules/netbsd_release_test: $(srcdir)/modules/netbsd_release_test.sh \ 191 | $(srcdir)/test_utils.sh 192 | test -d modules || mkdir -p modules 193 | sources="$(srcdir)/modules/netbsd_release_test.sh $(srcdir)/test_utils.sh" \ 194 | target=modules/netbsd_release_test; $(BUILD_TEST) 195 | endif 196 | 197 | if HAVE_KYUA 198 | CHECK_ENVIRONMENT = PATH=$(abs_top_builddir):$${PATH} 199 | CHECK_ENVIRONMENT += SANDBOXCTL_MODULESDIR=$(abs_top_srcdir)/modules 200 | CHECK_ENVIRONMENT += SANDBOXCTL_SHTK_MODULESDIR=$(abs_top_builddir) 201 | INSTALLCHECK_ENVIRONMENT = PATH=$(prefix)/sbin:$${PATH} 202 | 203 | # Allow the caller to override the configuration file passed to our test runs 204 | # below (or any other argument for that matter). 205 | KYUA_FLAGS ?= 206 | 207 | check-local: check-kyua 208 | PHONY_TARGETS += check-kyua 209 | check-kyua: 210 | $(CHECK_ENVIRONMENT) $(TESTS_ENVIRONMENT) \ 211 | $(KYUA) $(KYUA_FLAGS) test \ 212 | --kyuafile='$(top_srcdir)/Kyuafile' --build-root='$(top_builddir)' 213 | 214 | installcheck-local: installcheck-kyua 215 | PHONY_TARGETS += installcheck-kyua 216 | installcheck-kyua: 217 | cd $(pkgtestsdir) && $(INSTALLCHECK_ENVIRONMENT) $(TESTS_ENVIRONMENT) \ 218 | $(KYUA) $(KYUA_FLAGS) test 219 | else # HAVE_KYUA 220 | PHONY_TARGETS += missing-kyua 221 | missing-kyua: 222 | @echo "WARNING: kyua not found; no tests run" 223 | 224 | check-local: missing-kyua 225 | installcheck-local: missing-kyua 226 | endif # HAVE_KYUA 227 | else # WITH_ATF 228 | dist-hook: forbid-dist 229 | PHONY_TARGETS += forbid-dist 230 | forbid-dist: 231 | @echo "Sorry; cannot make dist without atf." 232 | @false 233 | endif # WITH_ATF 234 | 235 | PHONY_TARGETS += clean-all 236 | clean-all: 237 | GIT="$(GIT)" $(SH) $(srcdir)/admin/clean-all.sh 238 | 239 | .PHONY: $(PHONY_TARGETS) 240 | -------------------------------------------------------------------------------- /sandboxctl.8.in: -------------------------------------------------------------------------------- 1 | .\" Copyright 2013 Google Inc. 2 | .\" All rights reserved. 3 | .\" 4 | .\" Redistribution and use in source and binary forms, with or without 5 | .\" modification, are permitted provided that the following conditions are 6 | .\" met: 7 | .\" 8 | .\" * Redistributions of source code must retain the above copyright 9 | .\" notice, this list of conditions and the following disclaimer. 10 | .\" * Redistributions in binary form must reproduce the above copyright 11 | .\" notice, this list of conditions and the following disclaimer in the 12 | .\" documentation and/or other materials provided with the distribution. 13 | .\" * Neither the name of Google Inc. nor the names of its contributors 14 | .\" may be used to endorse or promote products derived from this software 15 | .\" without specific prior written permission. 16 | .\" 17 | .\" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | .\" "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | .\" LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | .\" A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | .\" OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | .\" SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | .\" LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | .\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | .\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | .\" OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | .Dd February 9, 2017 29 | .Dt SANDBOXCTL 8 30 | .Os 31 | .Sh NAME 32 | .Nm sandboxctl 33 | .Nd creates and manages chroot-based sandboxed environments 34 | .Sh SYNOPSIS 35 | .Nm 36 | .Op Fl c Ar config_name 37 | .Op Fl o Ar variable=value 38 | .Op Fl v 39 | config 40 | .Op Ar variable 41 | .Nm 42 | .Op Fl c Ar config_name 43 | .Op Fl o Ar variable=value 44 | .Op Fl v 45 | create 46 | .Nm 47 | .Op Fl c Ar config_name 48 | .Op Fl o Ar variable=value 49 | .Op Fl v 50 | destroy 51 | .Nm 52 | .Op Fl c Ar config_name 53 | .Op Fl o Ar variable=value 54 | .Op Fl v 55 | mount 56 | .Nm 57 | .Op Fl c Ar config_name 58 | .Op Fl o Ar variable=value 59 | .Op Fl v 60 | run 61 | .Ar ... 62 | .Nm 63 | .Op Fl c Ar config_name 64 | .Op Fl o Ar variable=value 65 | .Op Fl v 66 | shell 67 | .Nm 68 | .Op Fl c Ar config_name 69 | .Op Fl o Ar variable=value 70 | .Op Fl v 71 | unmount 72 | .Op Fl f 73 | .Op Fl f 74 | .Sh DESCRIPTION 75 | .Nm 76 | creates and manages chroot-based sandboxed environments in a portable manner 77 | based on a simple configuration file. 78 | .Pp 79 | Because 80 | .Xr chroot 8 81 | is a privileged system command, you must be root to use this tool. 82 | .Pp 83 | The following options apply to all commands: 84 | .Bl -tag -width XXXX 85 | .It Fl c Ar config_name 86 | Specifies the configuration file to use. 87 | The format of the file is described in 88 | .Xr sandboxctl.conf 5 . 89 | .Pp 90 | If 91 | .Ar config_name 92 | includes any directory separator (i.e. one or more slashes) or the 93 | .Sq .conf 94 | suffix, then the argument specifies the path to the configuration file. 95 | .Pp 96 | If 97 | .Ar config_name 98 | is a bare name without any directory components nor the extension, then the 99 | configuration file will be looked up in 100 | .Pa __SANDBOXCTL_ETCDIR__/.conf . 101 | The file must exist in this case. 102 | .It Fl o Ar variable=value 103 | Applies an override to the loaded configuration. 104 | .Pp 105 | The 106 | .Ar variable 107 | part of the argument must be any of the recognized configuration variables 108 | described in 109 | .Xr sandboxctl.conf 5 . 110 | The 111 | .Ar value , 112 | if not empty, specifies the value to set the configuration variable to. 113 | If 114 | .Ar value 115 | is empty, then the configuration variable is unset. 116 | .It Fl v 117 | Enables verbose mode. 118 | .El 119 | .Ss Sandbox types 120 | Sandboxes managed by 121 | .Nm 122 | have a type associated to them. 123 | The sandbox type etermines how the sandbox is constructed and provides all the 124 | operating system-specific knowledge to do so. 125 | There is one or more sandbox types for every supported operating system. 126 | .Pp 127 | For example: on 128 | .Nx , 129 | there is sandbox type to create an environment that reproduces the host system, 130 | and there also is a sandbox type that recreates a fresh installation of the 131 | system. 132 | Sandbox types are described in 133 | .Xr sandboxctl.conf 5 134 | along with all the settings that affect their behavior. 135 | .Ss Sandbox lifecycle 136 | A sandbox managed by 137 | .Nm 138 | goes through these operations: 139 | .Bl -enum 140 | .It 141 | .Em Configure : 142 | The administrator sets up a configuration file to define the sandbox. 143 | See 144 | .Xr sandboxctl.conf 5 145 | for details. 146 | .It 147 | .Em Create : 148 | Prepares the on-disk layout but does not mount any file systems within it (which 149 | may be necessary depending on the type of the sandbox). 150 | This operation happens once per sandbox. 151 | The sandbox is created with the 152 | .Sq create 153 | command. 154 | .It 155 | .Em Enter or mount : 156 | Performs preparatory work to enter the sandbox, such as mounting any filesystems 157 | within the sandbox or copying dynamic files. 158 | This operation happens every time the sandbox is entered for interaction as 159 | described in the next command. 160 | The sandbox can be manually mounted with the 161 | .Sq mount 162 | command, which is provided for convenience, but you should rarely do this 163 | by hand. 164 | .It 165 | .Em Interact : 166 | The administator can issue commands within the sandbox via the 167 | .Sq run 168 | command or open a shell via the 169 | .Sq shell 170 | command. 171 | .It 172 | .Em Leave or unmount : 173 | Performs the opposite work of the 174 | .Em Enter 175 | step. 176 | In particular, unmounts any file systems that may be left mounted in the 177 | sandbox. 178 | This operation happens every time the interaction step completes. 179 | The sandbox can be manually unmounted with the 180 | .Sq unmount 181 | command, which is provided for convenience, but you should rarely do this 182 | by hand. 183 | .It 184 | .Em Destroy : 185 | The on-disk layout for the sandbox is completely removed. 186 | This operation cannot be executed while there is any active sandbox in the 187 | .Em Interact 188 | stage. 189 | The sandbox is destroyed with the 190 | .Sq destroy 191 | command. 192 | .El 193 | .Pp 194 | Note that sandbox entry/leave operations are reference-counted and can happen 195 | concurrently. 196 | .Nm 197 | ensures that only the first concurrent interaction step executes the entry step 198 | and that only the last concurrent interaction step executes the leave step. 199 | If you get a warning saying that the 200 | .Em sandbox is still in use , 201 | it means that another instance of 202 | .Nm 203 | is still running for the given sandbox. 204 | .Ss The config command 205 | The config command dumps the loaded configuration to the standard output. 206 | The format of the output is not a script, so it cannot be fed back into 207 | .Nm . 208 | The purpose of this command is to aid in debugging the configuration of the 209 | tool, particularly for those configuration files that use shell logic to 210 | determine the value of the variables they define. 211 | .Pp 212 | A single 213 | .Ar variable 214 | name can be provided as an argument to this command, in which case the value 215 | of this variable is printed verbatim to the standard output. 216 | It is an error if the given variable is not defined. 217 | .Pp 218 | Note that the output of this command varies across operating systems depending 219 | on the sandbox types available. 220 | .Ss The create command 221 | Creates the sandbox, which must not yet exist. 222 | .Ss The destroy command 223 | Destroys the sandbox, which must exist and must not be active. 224 | In other words, no other 225 | .Nm 226 | instance may be running the 227 | .Sq run 228 | or 229 | .Sq shell 230 | commmands. 231 | .Ss The mount command 232 | Mounts sandbox-specific file systems and prepares the sandbox to be usable. 233 | This command is only provided for completeness and convenience; you should not 234 | need to use it by hand: the 235 | .Sq run 236 | and 237 | .Sq shell 238 | commands take care of automatically doing this for you at startup time. 239 | .Ss The run command 240 | Executes a given command inside the sandbox. 241 | The sandbox must exist. 242 | This operation always mounts the sandbox before running the command and unmounts 243 | it after completion. 244 | The shell is forced to 245 | .Pa /bin/sh 246 | and the environment is completely cleaned except for a few key variables. 247 | .Ss The shell command 248 | Executes an interactive shell inside the sandbox. 249 | The sandbox must exist. 250 | This operation always mounts the sandbox before running the shell and unmounts 251 | it after completion. 252 | The shell is forced to 253 | .Pa /bin/sh 254 | and the environment is completely cleaned except for a few key variables. 255 | .Ss The unmount command 256 | Unmounts sandbox-specific file systems. 257 | This command is only provided for completeness and convenience; you should not 258 | need to use it by hand: the 259 | .Sq run 260 | and 261 | .Sq shell 262 | commands take care of automatically doing this for you at exit time. 263 | .Pp 264 | The following options apply to the 265 | .Sq unmount 266 | command: 267 | .Bl -tag -width XXXX 268 | .It Fl f 269 | If given once, causes 270 | .Nm 271 | to disregard any existing locks on the sandbox and unmounts the file systems 272 | within it. 273 | If given twice, causes the unmount operations to also be forced; see the 274 | .Fl f 275 | flag in 276 | .Xr umount 8 277 | for more details about what this involves. 278 | .Pp 279 | You can use this flag to clean up the state of the sandbox if it ever becomes 280 | inconsistent, which is a (rare) possibility because 281 | .Nm 282 | cannot make hard guarantees on the consistency of the sandbox in case of 283 | failures during a regular unmount. 284 | .Pp 285 | .Em Warning : 286 | You should only use this flag if you are certain that no other concurrent 287 | instances of 288 | .Nm 289 | are still running for the given sandbox. 290 | .El 291 | .Sh FILES 292 | .Bl -tag -width XXXX 293 | .It Pa __SANDBOXCTL_ETCDIR__/ 294 | Directory containing all system-wide configuration files. 295 | .It Pa __SANDBOXCTL_ETCDIR__/default.conf 296 | Default configuration file to load if the user does not specify any other. 297 | .El 298 | .Sh SEE ALSO 299 | .Xr sandboxctl.conf 5 , 300 | .Xr chroot 8 301 | .Sh AUTHORS 302 | The 303 | .Nm 304 | utility was developed by 305 | .An Julio Merino 306 | .Aq jmmv@google.com . 307 | -------------------------------------------------------------------------------- /modules/netbsd_release_test.sh: -------------------------------------------------------------------------------- 1 | #! __ATF_SH__ 2 | # Copyright 2013 Google Inc. 3 | # All rights reserved. 4 | # 5 | # Redistribution and use in source and binary forms, with or without 6 | # modification, are permitted provided that the following conditions are 7 | # met: 8 | # 9 | # * Redistributions of source code must retain the above copyright 10 | # notice, this list of conditions and the following disclaimer. 11 | # * Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # * Neither the name of Google Inc. nor the names of its contributors 15 | # may be used to endorse or promote products derived from this software 16 | # without specific prior written permission. 17 | # 18 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | 30 | # \file netbsd_release_test.sh 31 | # Integration tests for the netbsd_release sandbox type. 32 | 33 | 34 | # Paths to installed files. 35 | # 36 | # Can be overriden for test purposes only. 37 | : ${SANDBOXCTL_MODULESDIR="__SANDBOXCTL_MODULESDIR__"} 38 | 39 | 40 | # Creates a fake set with a single file in it. 41 | # 42 | # \param releasedir Path to the root of the release directory. 43 | # \param name Basename of the set to create. 44 | create_set() { 45 | local releasedir="${1}"; shift 46 | local name="${1}"; shift 47 | 48 | local flag 49 | case "${name}" in 50 | *.tar.xz) flag=J ;; 51 | *.tgz) flag=z ;; 52 | esac 53 | 54 | touch "${name}.cookie" 55 | tar "-c${flag}" -f "${releasedir}/binary/sets/${name}" "${name}.cookie" 56 | rm "${name}.cookie" 57 | } 58 | 59 | 60 | # Guesses the format of the release sets and returns their extension. 61 | # 62 | # \param releasedir Path to the root directory of the release files. 63 | guess_sets_format() { 64 | local releasedir="${1}"; shift 65 | 66 | if [ -e "${releasedir}/binary/sets/base.tar.xz" ]; then 67 | echo "tar.xz" 68 | else 69 | echo "tgz" 70 | fi 71 | } 72 | 73 | 74 | atf_test_case config__builtins 75 | config__builtins_body() { 76 | isolate_module netbsd_release 77 | 78 | cat >expout <custom.conf </dev/null \ 124 | || atf_fail 'Invalid response from example.com; bad DNS configuration?' 125 | 126 | atf_check sandboxctl -c custom.conf destroy 127 | rm custom.conf 128 | } 129 | integration_cleanup() { 130 | [ ! -f custom.conf ] || sandboxctl -c custom.conf destroy || true 131 | } 132 | 133 | 134 | atf_test_case auto_sets 135 | auto_sets_head() { 136 | atf_set "require.config" "netbsd_releasedir" 137 | atf_set "require.user" "root" 138 | } 139 | auto_sets_body() { 140 | [ "$(uname -s)" = 'NetBSD' ] || atf_skip "Requires a NetBSD host" 141 | 142 | local ext="$(guess_sets_format "$(atf_config_get netbsd_releasedir)")" 143 | 144 | mkdir -p release/binary/sets 145 | ln -s "$(atf_config_get netbsd_releasedir)/binary/sets/base.${ext}" \ 146 | release/binary/sets/ 147 | ln -s "$(atf_config_get netbsd_releasedir)/binary/sets/etc.${ext}" \ 148 | release/binary/sets/ 149 | touch release/binary/sets/NOT-A-SET 150 | create_set release extra1."${ext}" 151 | create_set release extra2."${ext}" 152 | create_set release kern-A."${ext}" 153 | create_set release kern-GENERIC."${ext}" 154 | create_set release kern-Z."${ext}" 155 | 156 | cat >custom.conf <custom.conf <"release/binary/sets/${set_name}.tgz" 245 | other_ext=tgz 246 | ;; 247 | tgz) 248 | gzip -cd "${src}" \ 249 | | xz -c0 >"release/binary/sets/${set_name}.tar.xz" 250 | other_ext=tar.xz 251 | ;; 252 | *) 253 | atf_fail "Don't know how to handle format ${ext}" 254 | esac 255 | done 256 | create_set release foo."${other_ext}" 257 | create_set release bar."${ext}" 258 | 259 | cat >custom.conf </dev/null || true 117 | return 1 118 | fi 119 | if ! ( shtk_config_run_hook post_create_hook ); then 120 | sandboxctl_destroy || true 121 | return 1 122 | fi 123 | } 124 | 125 | 126 | # Destroys the sandbox. 127 | # 128 | # This does not attempt to unmount the sandbox if still mounted, and will abort 129 | # loudly if it is. 130 | sandboxctl_destroy() { 131 | [ ${#} -eq 0 ] || shtk_cli_usage_error "destroy does not take any arguments" 132 | 133 | local type 134 | type="$(shtk_config_get SANDBOX_TYPE)" || exit 135 | local root 136 | root="$(shtk_config_get SANDBOX_ROOT)" || exit 137 | 138 | [ -d "${root}" ] || shtk_cli_error "Cannot destroy a non-existent sandbox" 139 | 140 | ! sandbox_has_mounts "${root}" || shtk_cli_error "File systems appear to" \ 141 | "still be mounted; refusing to destroy" 142 | 143 | shtk_config_run_hook pre_destroy_hook 144 | sandbox_dispatch "${type}" "${root}" destroy 145 | 146 | sandbox_destroy "${root}" 147 | } 148 | 149 | 150 | # Mounts the sandbox. 151 | # 152 | # The sandbox must have been created first with the 'create' command. Running 153 | # multiple mount operations from different clients is reasonably safe as we 154 | # record how many clients have called this. 155 | sandboxctl_mount() { 156 | [ ${#} -eq 0 ] || shtk_cli_usage_error "mount does not take any arguments" 157 | 158 | local type 159 | type="$(shtk_config_get SANDBOX_TYPE)" || exit 160 | local root 161 | root="$(shtk_config_get SANDBOX_ROOT)" || exit 162 | 163 | [ -d "${root}" ] || shtk_cli_error "Cannot mount a non-existent sandbox" 164 | 165 | if sandbox_enter "${root}"; then 166 | if sandbox_has_mounts "${root}"; then 167 | sandbox_leave "${root}" 168 | shtk_cli_error "Sandbox in inconsistent state; mounts found but" \ 169 | "is not locked" 170 | fi 171 | 172 | local ret=0 173 | ( 174 | sandbox_dispatch "${type}" "${root}" mount 175 | shtk_config_run_hook post_mount_hook 176 | ) || ret=${?} 177 | if [ ${ret} -ne 0 ]; then 178 | sandboxctl_unmount 179 | exit ${ret} 180 | fi 181 | fi 182 | } 183 | 184 | 185 | # Unmounts the sandbox. 186 | # 187 | # The sandbox must exist. Running multiple unmount operations from different 188 | # clients is reasonably safe as we have recorded how many clients did so. 189 | sandboxctl_unmount() { 190 | local force_leave= 191 | local force_unmount= 192 | local OPTIND 193 | while getopts ':f' arg "${@}"; do 194 | case "${arg}" in 195 | f) # Force leave if given once; force unmount if given twice. 196 | if [ -n "${force_leave}" ]; then 197 | force_unmount=-f 198 | else 199 | force_leave=-f 200 | fi 201 | ;; 202 | 203 | \?) 204 | shtk_cli_usage_error "Unknown option -${OPTARG} in unmount" 205 | ;; 206 | esac 207 | done 208 | shift $((${OPTIND} - 1)) 209 | OPTIND=1 # Should not be necessary due to the 'local' above. 210 | 211 | [ ${#} -eq 0 ] || shtk_cli_usage_error "unmount does not take any arguments" 212 | 213 | local type 214 | type="$(shtk_config_get SANDBOX_TYPE)" || exit 215 | local root 216 | root="$(shtk_config_get SANDBOX_ROOT)" || exit 217 | 218 | [ -d "${root}" ] || shtk_cli_error "Cannot unmount a non-existent sandbox" 219 | 220 | if sandbox_leave ${force_leave} "${root}"; then 221 | shtk_config_run_hook pre_unmount_hook 222 | sandbox_dispatch "${type}" "${root}" unmount 223 | sandbox_unmount_dirs ${force_unmount} "${root}" 224 | else 225 | shtk_cli_warning "Sandbox still in use by another process; file" \ 226 | "systems may still be mounted!" 227 | fi 228 | } 229 | 230 | 231 | # Executes chroot to enter the sandbox. 232 | # 233 | # \param ... Additional arguments to chroot, if any. 234 | # 235 | # \return The exit status of the executed command. 236 | _sandboxctl_chroot() { 237 | _SANDBOXCTL_DID_UNMOUNT=false 238 | unmount_on_signal() { 239 | if [ "${_SANDBOXCTL_DID_UNMOUNT}" = false ]; then 240 | sandboxctl_unmount 241 | _SANDBOXCTL_DID_UNMOUNT=true 242 | fi 243 | } 244 | shtk_cleanup_register unmount_on_signal 245 | 246 | sandboxctl_mount 247 | local ret=0 248 | env -i \ 249 | HOME=/tmp \ 250 | PATH="${PATH}" \ 251 | SHELL=/bin/sh \ 252 | TERM="${TERM}" \ 253 | USER="${USER}" \ 254 | chroot "$(shtk_config_get SANDBOX_ROOT)" "${@}" || ret="${?}" 255 | sandboxctl_unmount 256 | _SANDBOXCTL_DID_UNMOUNT=true 257 | return "${ret}" 258 | } 259 | 260 | 261 | # Runs the given command inside the sandbox. 262 | # 263 | # \param binary Path to the binary to run, relative to the sandbox. 264 | # \param ... Additional arguments to the binary. 265 | # 266 | # \return The exit status of the executed command. 267 | sandboxctl_run() { 268 | [ ${#} -gt 0 ] || shtk_cli_usage_error "run requires at least one argument" 269 | 270 | _sandboxctl_chroot "${@}" 271 | } 272 | 273 | 274 | # Runs an interactive shell inside the sandbox. 275 | # 276 | # \return The exit status of the shell. 277 | sandboxctl_shell() { 278 | [ ${#} -eq 0 ] || shtk_cli_usage_error "shell does not take any arguments" 279 | 280 | PS1="sandbox# " _sandboxctl_chroot 281 | } 282 | 283 | 284 | # Loads the configuration file specified in the command line. 285 | # 286 | # \param config_name Name of the desired configuration. It can be either a 287 | # configuration name (no slashes) or a path. 288 | sandboxctl_config_load() { 289 | local config_name="${1}"; shift 290 | 291 | local config_file= 292 | case "${config_name}" in 293 | */*|*.conf) 294 | config_file="${config_name}" 295 | ;; 296 | 297 | *) 298 | config_file="${SANDBOXCTL_ETCDIR}/${config_name}.conf" 299 | [ -e "${config_file}" ] \ 300 | || shtk_cli_usage_error "Cannot locate configuration named" \ 301 | "'${config_name}'" 302 | ;; 303 | esac 304 | shtk_config_load "${config_file}" 305 | } 306 | 307 | 308 | # Entry point to the program. 309 | # 310 | # \param ... Command-line arguments to be processed. 311 | # 312 | # \return An exit code to be returned to the user. 313 | main() { 314 | local config_name="default" 315 | 316 | sandbox_load_types "${SANDBOXCTL_MODULESDIR}" 317 | shtk_config_init ${SANDBOXCTL_CONFIG_VARS} $(sandbox_call_types config_vars) 318 | 319 | local OPTIND 320 | while getopts ':c:o:v' arg "${@}"; do 321 | case "${arg}" in 322 | c) # Name of the configuration to load. 323 | config_name="${OPTARG}" 324 | ;; 325 | 326 | o) # Override for a particular configuration variable. 327 | shtk_config_override "${OPTARG}" 328 | ;; 329 | 330 | v) # Be verbose. 331 | shtk_cli_set_log_level debug 332 | ;; 333 | 334 | :) 335 | shtk_cli_usage_error "Missing argument to option -${OPTARG}" 336 | ;; 337 | 338 | \?) 339 | shtk_cli_usage_error "Unknown option -${OPTARG}" 340 | ;; 341 | esac 342 | done 343 | shift $((${OPTIND} - 1)) 344 | OPTIND=1 # Should not be necessary due to the 'local' above. 345 | 346 | [ ${#} -ge 1 ] || shtk_cli_usage_error "No command specified" 347 | 348 | local exit_code=0 349 | 350 | local command="${1}"; shift 351 | case "${command}" in 352 | config|create|destroy|mount|run|shell|unmount) 353 | sandboxctl_set_defaults 354 | sandboxctl_config_load "${config_name}" 355 | "sandboxctl_${command}" "${@}" || exit_code="${?}" 356 | ;; 357 | 358 | *) 359 | shtk_cli_usage_error "Unknown command ${command}" 360 | ;; 361 | esac 362 | 363 | return "${exit_code}" 364 | } 365 | -------------------------------------------------------------------------------- /sandbox.subr.in: -------------------------------------------------------------------------------- 1 | # Copyright 2013 Google Inc. 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions are 6 | # met: 7 | # 8 | # * Redistributions of source code must retain the above copyright 9 | # notice, this list of conditions and the following disclaimer. 10 | # * Redistributions in binary form must reproduce the above copyright 11 | # notice, this list of conditions and the following disclaimer in the 12 | # documentation and/or other materials provided with the distribution. 13 | # * Neither the name of Google Inc. nor the names of its contributors 14 | # may be used to endorse or promote products derived from this software 15 | # without specific prior written permission. 16 | # 17 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | # \file sandbox.subr 30 | # Routines to interact with sandboxes. 31 | # 32 | # This module contains generic code to interact with sandboxes and utility 33 | # functions that sandboxes can call to prepare their contents. The code in this 34 | # file is OS-independent and has no specific knowledge on how to create the 35 | # various types of sandboxes that depend on the underlying OS. 36 | # 37 | # "Sandbox types" implement the logic to construct sandboxes on various OSes and 38 | # with different configurations. These types are shipped as invidual modules 39 | # that can be loaded dynamically. Such modules can make free use of shtk and 40 | # the functions in this file. 41 | # 42 | # Consider a sandbox type A. Such sandbox type must be implemented in a file 43 | # named A.subr and must provide the following functions: 44 | # 45 | # A_config_vars(): Prints the set of configuration variables that the sandbox 46 | # type requires for its operation. All of these variables should be 47 | # prefixed with A (in uppercase) to prevent conflicts among various modules. 48 | # A_set_defaults(): Initializes the configuration variables to their default 49 | # values. 50 | # A(): The main dispatcher of sandbox-specific actions. This dispatcher takes 51 | # the directory in which the sandbox lives and the action to perform on it, 52 | # which can be any of the values listed in _SANDBOX_ACTIONS. 53 | 54 | shtk_import bool 55 | shtk_import cli 56 | shtk_import list 57 | 58 | 59 | # List of valid sandbox actions. These are the hooks that the sandbox type 60 | # dispatcher must implement. 61 | _SANDBOX_ACTIONS="create destroy mount unmount" 62 | 63 | 64 | # Extra options to pass to bindfs, prefixed with a comma if not empty. Used 65 | # for testing purposes only. 66 | _SANDBOX_BINDFS_EXTRA_OPTS= 67 | 68 | 69 | # Sandbox types registered by the application. Use sandbox_load_types to add 70 | # entries to this list. 71 | _SANDBOX_TYPES= 72 | 73 | 74 | # Loads a set of sandbox modules from a directory. 75 | # 76 | # \param typesdir Directory from which to load the modules. All files with a 77 | # .subr extension are loaded. 78 | sandbox_load_types() { 79 | local typesdir="${1}"; shift 80 | 81 | if [ -d "${typesdir}" ]; then 82 | for typefile in "${typesdir}"/*.subr; do 83 | [ "${typefile}" != "${typesdir}/*.subr" ] || break 84 | 85 | local typename="$(basename "${typefile}" | sed -e 's,.subr$,,')" 86 | 87 | # Keep in sync with the module-level comment describing the 88 | # interface of the sandbox modules. 89 | eval "${typename}_config_vars() { true; }" 90 | eval "${typename}_set_defaults() { true; }" 91 | eval "${typename}_create() { true; }" 92 | eval "${typename}_destroy() { true; }" 93 | eval "${typename}_mount() { true; }" 94 | eval "${typename}_unmount() { true; }" 95 | 96 | . "${typefile}" || shtk_cli_error "Failed to load ${typefile}" 97 | _SANDBOX_TYPES="${_SANDBOX_TYPES} ${typename}" 98 | done 99 | fi 100 | } 101 | 102 | 103 | # Calls a function on all registered types. 104 | # 105 | # This is supposed to be used only by the container program to initialize the 106 | # various modules. For this reason, if any of the hooks reports an error, 107 | # execution is immediately terminated. 108 | # 109 | # \param hook Name of the function to invoke. The actual function called is 110 | # _. 111 | sandbox_call_types() { 112 | local hook="${1}"; shift 113 | 114 | for sandbox_type in ${_SANDBOX_TYPES}; do 115 | "${sandbox_type}_${hook}" "${@}" \ 116 | || shtk_cli_error "Call to ${hook} on ${sandbox_type} failed" 117 | done 118 | } 119 | 120 | 121 | # Dispatches a sandbox action to a type-specific handler. 122 | # 123 | # \param type Type of the sandbox being handled. 124 | # \param root Path to the sandbox directory. 125 | # \param action Name of the action to dispatch to the handler. 126 | sandbox_dispatch() { 127 | local type="${1}"; shift 128 | local root="${1}"; shift 129 | local action="${1}"; shift 130 | 131 | shtk_list_contains "${action}" ${_SANDBOX_ACTIONS} \ 132 | || shtk_cli_error "Cannot dispatch unknown action ${action}" 133 | 134 | local function_name="$(echo "${type}" | tr - _)" 135 | if shtk_list_contains "${function_name}" ${_SANDBOX_TYPES}; then 136 | ( "${function_name}_${action}" "${root}" ) \ 137 | || shtk_cli_error "Failed to ${action} sandbox type ${type}" 138 | else 139 | shtk_cli_error "Invalid sandbox type '${type}'" 140 | fi 141 | } 142 | 143 | 144 | # Records that the sandbox is in use by one more user. 145 | # 146 | # \param root Path to the sandbox to lock. 147 | # 148 | # \return True if we are the first user to enter the sandbox or false if 149 | # somebody else was already using it, in which case we just accounted ourselves 150 | # as a new user. 151 | sandbox_enter() { 152 | local root="${1}"; shift 153 | 154 | local lock="${root}/.sandbox_lock" 155 | if [ -f "${lock}" ]; then 156 | echo "1" >>"${lock}" || shtk_cli_error "Failed to lock sandbox" 157 | return 1 158 | else 159 | echo "1" >"${lock}" || shtk_cli_error "Failed to lock sandbox" 160 | return 0 161 | fi 162 | } 163 | 164 | 165 | # Records that the sandbox is in use by one fewer user. 166 | # 167 | # \param -f Forcibly leave the sandbox. This just causes the lock to be removed 168 | # regardless of any other potential concurrent instances of sandboxctl. 169 | # \param root Path to the sandbox to unlock. 170 | # 171 | # \return True if we were the last user to leave the sandbox, false otherwise. 172 | sandbox_leave() { 173 | local force=no 174 | local OPTIND 175 | while getopts ':f' arg "${@}"; do 176 | case "${arg}" in 177 | f) # Force destruction of the lock. 178 | force=yes 179 | ;; 180 | 181 | \?) 182 | shtk_cli_usage_error "Unknown option -${OPTARG}" 183 | ;; 184 | esac 185 | done 186 | shift $((${OPTIND} - 1)) 187 | OPTIND=1 # Should not be necessary due to the 'local' above. 188 | 189 | local root="${1}"; shift 190 | 191 | local lock="${root}/.sandbox_lock" 192 | if shtk_bool_check "${force}"; then 193 | if [ -f "${lock}" ]; then 194 | shtk_cli_warning "Sandbox locked; forcibly destroying lock" 195 | rm -f "${lock}" 196 | fi 197 | return 0 198 | else 199 | [ -f "${lock}" ] || shtk_cli_error "Sandbox not locked" 200 | sed -e 1d "${lock}" >"${lock}.tmp" \ 201 | || shtk_cli_error "Failed to unlock sandbox" 202 | if [ -s "${lock}.tmp" ]; then 203 | mv "${lock}.tmp" "${lock}" \ 204 | || shtk_cli_error "Failed to unlock sandbox" 205 | return 1 206 | else 207 | rm -f "${lock}.tmp" "${lock}" 208 | return 0 209 | fi 210 | fi 211 | } 212 | 213 | 214 | # Checks if the sandbox still has any mount points. 215 | # 216 | # \param root Path to the sandbox root. 217 | # 218 | # \return True if the given directory is a mount point or has any file systems 219 | # mounted within it; false otherwise. 220 | sandbox_has_mounts() { 221 | local root="${1}"; shift 222 | 223 | [ -d "${root}" ] || return 1 224 | 225 | local real_root="$(cd "${root}" && pwd -P)" 226 | LANG=C LC_ALL=C mount | grep -E " on ${real_root}(/[^ ]+| )" >/dev/null 2>&1 227 | } 228 | 229 | 230 | # Unmounts all file systems. 231 | # 232 | # \param root Path to the sandbox root. 233 | sandbox_unmount_dirs() { 234 | local force_flags= 235 | local OPTIND 236 | while getopts ':f' arg "${@}"; do 237 | case "${arg}" in 238 | f) # Force-unmount file systems. 239 | case "$(uname -s)" in 240 | Linux) force_flags="-f -l" ;; 241 | *) force_flags=-f ;; 242 | esac 243 | ;; 244 | 245 | \?) 246 | shtk_cli_usage_error "Unknown option -${OPTARG}" 247 | ;; 248 | esac 249 | done 250 | shift $((${OPTIND} - 1)) 251 | OPTIND=1 # Should not be necessary due to the 'local' above. 252 | 253 | local root="${1}"; shift 254 | 255 | # To get the list of directories, we have to scan the output of mount, line 256 | # by line, and take out the parts we know are not part of the directory. 257 | # This is so that we respect spaces in mount points. Fragile, indeed, but 258 | # we have no better mechanism from here. 259 | local real_root="$(cd "${root}" && pwd -P)" 260 | LANG=C LC_ALL=C mount \ 261 | | sed -e 's,^.* on ,,;s, ([^)]*)$,,;s, type .*$,,' \ 262 | | grep -E "^${real_root}/.+" \ 263 | | while read dir 264 | do 265 | shtk_cli_debug "Unmounting ${dir}" 266 | local retries=50 267 | while [ ${retries} -gt 0 ]; do 268 | # This may fail e.g. if the file system is busy. We don't check for 269 | # error here because we want to retry this a few times. We will 270 | # only determine that the unmount failed if we run out of tries. 271 | umount ${force_flags} "${dir}" || shtk_cli_debug "umount failed" 272 | sandbox_has_mounts "${dir}" || break 273 | 274 | shtk_cli_debug "Mount point ${dir} has not disappeared yet; waiting" 275 | sleep .5 276 | retries=$((${retries} - 1)) 277 | done 278 | [ ${retries} -gt 0 ] || shtk_cli_error "Failed to unmount ${dir}" 279 | done 280 | } 281 | 282 | 283 | # Destroys a sandbox. 284 | # 285 | # This ensures that the sandbox is not still mounted and deletes its contents. 286 | # There are several provisions here to ensure we do not delete more than we 287 | # should. 288 | # 289 | # \param root Path to the sandbox. 290 | sandbox_destroy() { 291 | local root="${1}"; shift 292 | 293 | local real_root="$(cd "${root}" && pwd -P)" 294 | [ "${real_root}" != / ] || \ 295 | shtk_abort "Attempting to delete /; something really bad happened" 296 | 297 | ! sandbox_has_mounts "${real_root}" || shtk_abort "Attempting to delete" \ 298 | "an still-mounted sandbox; this should not have happened" 299 | 300 | # Attempt to weaken the permissions of the sandbox so that the recursive rm 301 | # call below has higher chances of succees. This is useful when running 302 | # tests as non-root, but also for real sandboxes where files may have been 303 | # installed with high protection bits. 304 | # 305 | # Note that this is a best-effort operation, hence why it's silent: if we 306 | # fail here and we do not fail in the rm call below... great! 307 | 308 | case "$(uname -s)" in 309 | Darwin|FreeBSD|NetBSD) 310 | chflags -R noschg,nouchg "${real_root}" 2>/dev/null || true 311 | ;; 312 | Linux) 313 | chattr -R -i "${real_root}" 2>/dev/null || true 314 | ;; 315 | esac 316 | chmod -R u+rwx "${real_root}" 2>/dev/null || true 317 | 318 | case "$(uname -s)" in 319 | FreeBSD|NetBSD) 320 | # Be extra paranoid: use NetBSD's -x flag to avoid crossing 321 | # mount points. We don't want the remove operation to 322 | # "leak" outside of the sandbox, especially during the 323 | # development of sandboxctl. 324 | rm -xrf "${real_root}" || shtk_cli_error "Failed to destroy ${root}" 325 | ;; 326 | 327 | Linux) 328 | # Be extra paranoid: use GNU's --one-file-system flag to avoid 329 | # crossing mount points. We don't want the remove operation to 330 | # "leak" outside of the sandbox, especially during the development 331 | # of sandboxctl. 332 | rm -rf --one-file-system "${real_root}" \ 333 | || shtk_cli_error "Failed to destroy ${root}" 334 | ;; 335 | 336 | *) 337 | rm -rf "${real_root}" || shtk_cli_error "Failed to destroy ${root}" 338 | ;; 339 | esac 340 | } 341 | 342 | 343 | # Extracts a compressed tarball into a directory. 344 | # 345 | # This is a public function usable within the hooks in the configuration file. 346 | # 347 | # \param tgz_file Path to the tarball to extract. 348 | # \param dir Path to the directory into which to extract the tarball. 349 | # \param ... Optional list of files to extract. 350 | sandbox_extract() { 351 | [ ${#} -ge 2 ] || shtk_cli_error "sandbox_extract: syntax error;" \ 352 | "expecting tarball and target" 353 | local file="${1}"; shift 354 | local dir="${1}"; shift 355 | 356 | if [ ${#} -gt 0 ]; then 357 | shtk_cli_debug "Extracting ${file} into ${dir} (contents: ${*})" 358 | else 359 | shtk_cli_debug "Extracting ${file} into ${dir}" 360 | fi 361 | 362 | local format # File compression format as indicated by tar's flag. 363 | case "${file}" in 364 | *.tar.gz|*.tgz) format=z ;; 365 | *.tar.xz|*.txz) format=J ;; 366 | *) shtk_cli_error "Cannot determine compression format for ${file}" 367 | esac 368 | 369 | local tar_extra_flags= 370 | case "$(uname -s)" in 371 | NetBSD) 372 | tar_extra_flags=e # Stop on first error. 373 | ;; 374 | esac 375 | 376 | if shtk_cli_log_level debug && which progress >/dev/null 2>&1 \ 377 | && [ "${format}" = z ]; then 378 | progress -zf "${file}" tar "-${tar_extra_flags}xp" -C "${dir}" \ 379 | -f - "${@}" || shtk_cli_error "Extraction of ${file} failed" 380 | else 381 | tar "-${tar_extra_flags}x${format}p" -f "${file}" -C "${dir}" "${@}" \ 382 | || shtk_cli_error "Extraction of ${file} failed" 383 | fi 384 | } 385 | 386 | 387 | # Mounts a bindfs-type file system. 388 | # 389 | # This is a public function usable within the hooks in the configuration file. 390 | # 391 | # \param -o ro|rw Whether to mount the file system in read-only or read-write 392 | # mode. The default is "ro". 393 | # \param source Directory to bind. 394 | # \param mount_point Path where to mount the source directory. 395 | sandbox_bindfs() { 396 | local mode=ro 397 | local OPTIND 398 | while getopts ':o:' arg "${@}"; do 399 | case "${arg}" in 400 | o) # Mount options. 401 | case "${OPTARG}" in 402 | ro|rw) 403 | mode="${OPTARG}" 404 | ;; 405 | 406 | *) 407 | shtk_cli_usage_error "Unsupported mount option" \ 408 | "${OPTARG}" 409 | ;; 410 | esac 411 | ;; 412 | 413 | \?) 414 | shtk_cli_usage_error "Unknown option -${OPTARG}" 415 | ;; 416 | esac 417 | done 418 | shift $((${OPTIND} - 1)) 419 | OPTIND=1 # Should not be necessary due to the 'local' above. 420 | 421 | [ ${#} -eq 2 ] || shtk_cli_error "sandbox_bindfs: syntax error;" \ 422 | "expecting source and mount point" 423 | local source="${1}"; shift 424 | local mount_point="${1}"; shift 425 | 426 | shtk_cli_debug "Bind-mounting ${source} onto ${mount_point} (${mode})" 427 | case "$(uname -s)" in 428 | Darwin) 429 | local bindfs='__BINDFS__' 430 | [ -n "${bindfs}" ] || shtk_abort "bindfs should have been" \ 431 | "detected at configuration time" 432 | 433 | ( 434 | # Starting from macOS Mojave (or maybe even High Sierra), 435 | # running a complex binary like xcodebuild within the sandbox 436 | # causes bindfs to run out of file descriptors. I haven't 437 | # tracked down why that's the case: maybe there are newer 438 | # lower limits, or maybe some library used by bindfs needs to 439 | # open more files. Just raise the limit here. 440 | local limit="$(ulimit -H -n)" 441 | if [ "${limit}" = unlimited ]; then 442 | # Even if the hard limit is "unlimited", we can't tell 443 | # ulimit to set that value: instead, we have to limit our 444 | # request to what the kernel real limits are. 445 | limit="$(sysctl -n kern.maxfilesperproc)" 446 | fi 447 | ulimit -S -n "${limit}" 448 | 449 | exec "${bindfs}" -o "${mode}${_SANDBOX_BINDFS_EXTRA_OPTS}" \ 450 | "${source}" "${mount_point}" \ 451 | || shtk_cli_error "Failed to bind ${mount_point}" 452 | ) 453 | 454 | local retries=50 455 | while [ ${retries} -gt 0 ] && ! sandbox_has_mounts "${mount_point}" 456 | do 457 | shtk_cli_debug "bindfs mount point ${mount_point} has not" \ 458 | "appeared yet; waiting" 459 | sleep .1 460 | retries=$((${retries} - 1)) 461 | done 462 | [ ${retries} -gt 0 ] || shtk_cli_error "bindfs failed to come up" 463 | ;; 464 | 465 | FreeBSD) 466 | mount -t nullfs -o "${mode}" "${source}" "${mount_point}" \ 467 | || shtk_cli_error "Failed to bind ${mount_point}" 468 | ;; 469 | 470 | Linux) 471 | mount --bind "${source}" "${mount_point}" \ 472 | || shtk_cli_error "Failed to bind ${mount_point}" 473 | if [ "${mode}" != rw ]; then 474 | mount -o "bind,remount,${mode}" "${mount_point}" \ 475 | || shtk_cli_error "Failed to bind ${mount_point}" 476 | fi 477 | ;; 478 | 479 | NetBSD) 480 | mount -t null -o "${mode}" "${source}" "${mount_point}" \ 481 | || shtk_cli_error "Failed to bind ${mount_point}" 482 | ;; 483 | 484 | *) 485 | shtk_abort "Unsupported OS for sandbox_bindfs" 486 | ;; 487 | esac 488 | } 489 | -------------------------------------------------------------------------------- /sandbox_test.sh: -------------------------------------------------------------------------------- 1 | # Copyright 2013 Google Inc. 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions are 6 | # met: 7 | # 8 | # * Redistributions of source code must retain the above copyright 9 | # notice, this list of conditions and the following disclaimer. 10 | # * Redistributions in binary form must reproduce the above copyright 11 | # notice, this list of conditions and the following disclaimer in the 12 | # documentation and/or other materials provided with the distribution. 13 | # * Neither the name of Google Inc. nor the names of its contributors 14 | # may be used to endorse or promote products derived from this software 15 | # without specific prior written permission. 16 | # 17 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | : ${SANDBOXCTL_SHTK_MODULESDIR:="__SANDBOXCTL_SHTK_MODULESDIR__"} 30 | SHTK_MODULESPATH="${SANDBOXCTL_SHTK_MODULESDIR}" shtk_import sandbox 31 | _SANDBOX_BINDFS_EXTRA_OPTS=,direct_io 32 | 33 | 34 | # Creates fake sandbox types and loads them. 35 | # 36 | # The fake1 sandbox type has three functions (action1, action2 and action3) to 37 | # be called with sandbox_call_types. action1 and action2 return success while 38 | # action3 returns an error. The main dispatcher of the sandbox type returns 39 | # success. 40 | # 41 | # The fake2 sandbox type is the same as the fake1 type but the dispatcher raises 42 | # an error. 43 | setup_fake_types() { 44 | mkdir modules 45 | cat >>modules/fake1.subr <>modules/fake2.subr <modules/fake1.subr 99 | echo "fake2() { true; }" >modules/fake2.subr 100 | echo "fake3() { true; }" >modules/fake3.foo 101 | sandbox_load_types modules 102 | 103 | fake1 || atf_fail "fake1 not loaded" 104 | fake2 || atf_fail "fake2 not loaded" 105 | if fake3; then 106 | atf_fail "fake3 loaded but it should not have been" 107 | fi 108 | } 109 | 110 | 111 | atf_test_case load_types__default_hooks 112 | load_types__default_hooks_body() { 113 | mkdir modules 114 | echo "true" >modules/fake1.subr 115 | sandbox_load_types modules 116 | for hook in fake1_config_vars fake1_set_defaults \ 117 | fake1_create fake1_destroy fake1_mount fake1_unmount; do 118 | if ! "${hook}" >out; then 119 | atf_fail "Default unimplemented ${hook} failed" 120 | fi 121 | atf_check -o empty cat out 122 | done 123 | } 124 | 125 | 126 | atf_test_case load_types__error 127 | load_types__error_body() { 128 | mkdir modules 129 | echo "fake1() { true; }" >modules/fake1.subr 130 | echo "invalid file" >modules/fake2.subr 131 | echo "fake3() { true; }" >modules/fake3.subr 132 | if ( sandbox_load_types modules ); then 133 | atf_fail "sandbox_load_types should have failed" 134 | fi 135 | } 136 | 137 | 138 | atf_test_case call_types__ok 139 | call_types__ok_body() { 140 | setup_fake_types 141 | 142 | sandbox_call_types action1 arg1 arg2 >output 143 | cat >expout <output 150 | cat >expout <output 2>error ); then 163 | atf_fail "sandbox_call_types succeeded but it should have failed" 164 | fi 165 | atf_check -o inline:"fake1_action3: arg1\n" cat output 166 | atf_check -o match:"E:.*action3 on fake1 failed" cat error 167 | } 168 | 169 | 170 | atf_test_case dispatch__type__ok 171 | dispatch__type__ok_body() { 172 | setup_fake_types 173 | for action in create mount unmount destroy; do 174 | sandbox_dispatch fake1 "the/sandbox" "${action}" >out 2>err 175 | atf_check -o inline:"fake1 handler ${action}: the/sandbox\n" cat out 176 | [ ! -s err ] || atf_fail "Error messages should not have been printed" 177 | done 178 | } 179 | 180 | 181 | atf_test_case dispatch__type__error 182 | dispatch__type__error_body() { 183 | setup_fake_types 184 | for action in create mount unmount destroy; do 185 | if ( sandbox_dispatch fake2 "the/sandbox" "${action}" >out 2>err ); then 186 | atf_fail "Handler failed but error was not captured" 187 | fi 188 | atf_check -o inline:"fake2 handler ${action}: the/sandbox\n" cat out 189 | atf_check -o match:"Failed to ${action} sandbox type fake2" cat err 190 | done 191 | } 192 | 193 | 194 | atf_test_case dispatch__unknown_type 195 | dispatch__unknown_type_body() { 196 | setup_fake_types 197 | if ( sandbox_dispatch fake3 "the/sandbox" create >out 2>err ); then 198 | atf_fail "Dispatch on unknown type worked but should not have" 199 | fi 200 | atf_check -o match:"Invalid sandbox type 'fake3'" cat err 201 | } 202 | 203 | 204 | atf_test_case dispatch__unknown_action 205 | dispatch__unknown_action_body() { 206 | if ( sandbox_dispatch empty the/directory foobar ) 2>err 207 | then 208 | atf_fail "Invalid action was not captured" 209 | fi 210 | atf_check -o match:'unknown action foobar' cat err 211 | } 212 | 213 | 214 | atf_test_case enter_leave__once 215 | enter_leave__once_body() { 216 | local root="$(pwd)/sandbox" 217 | mkdir -p "${root}" 218 | 219 | sandbox_enter "${root}" || atf_fail "enter did not lock sandbox" 220 | [ -f "${root}/.sandbox_lock" ] || atf_fail "Lock file not created" 221 | sandbox_leave "${root}" || atf_fail "leave did not unlock sandbox" 222 | [ ! -f "${root}/.sandbox_lock" ] || atf_fail "Lock file not deleted" 223 | } 224 | 225 | 226 | atf_test_case enter_leave__nested 227 | enter_leave__nested_body() { 228 | local root="$(pwd)/sandbox" 229 | mkdir -p "${root}" 230 | 231 | sandbox_enter "${root}" || atf_fail "enter did not lock sandbox" 232 | 233 | if sandbox_enter "${root}"; then 234 | atf_fail "enter relocked already-in-use sandbox" 235 | fi 236 | if sandbox_leave "${root}"; then 237 | atf_fail "leave unlocked still-in-use sandbox" 238 | fi 239 | 240 | if sandbox_enter "${root}"; then 241 | atf_fail "enter relocked already-in-use sandbox" 242 | 243 | if sandbox_enter "${root}"; then 244 | atf_fail "enter relocked already-in-use sandbox" 245 | fi 246 | if sandbox_leave "${root}"; then 247 | atf_fail "leave unlocked still-in-use sandbox" 248 | fi 249 | fi 250 | if sandbox_leave "${root}"; then 251 | atf_fail "leave unlocked still-in-use sandbox" 252 | fi 253 | 254 | sandbox_leave "${root}" || atf_fail "leave did not unlock sandbox" 255 | [ ! -f "${root}/.sandbox_lock" ] || atf_fail "Lock file not deleted" 256 | } 257 | 258 | 259 | atf_test_case enter__error 260 | enter__error_body() { 261 | local root="$(pwd)/sandbox" 262 | 263 | if ( sandbox_enter "${root}" ) 2>err; then 264 | atf_fail "sandbox_enter did not raise an error" 265 | fi 266 | atf_check -o match:'E: Failed to lock sandbox' cat err 267 | [ ! -f "${root}/.sandbox_lock" ] || atf_fail "Lock file created" 268 | } 269 | 270 | 271 | atf_test_case leave__error 272 | leave__error_head() { 273 | atf_set "require.user" "unprivileged" 274 | } 275 | leave__error_body() { 276 | local root="$(pwd)/sandbox" 277 | mkdir -p "${root}" 278 | sandbox_enter "${root}" 279 | chmod 555 "${root}" 280 | 281 | if ( sandbox_leave "${root}" ) 2>err; then 282 | chmod 755 "${root}" 283 | atf_fail "sandbox_leave did not raise an error" 284 | fi 285 | chmod 755 "${root}" 286 | atf_check -o match:'E: Failed to unlock sandbox' cat err 287 | [ -f "${root}/.sandbox_lock" ] || atf_fail "Lock file deleted" 288 | } 289 | 290 | 291 | atf_test_case leave__force 292 | leave__force_head() { 293 | atf_set "require.user" "unprivileged" 294 | } 295 | leave__force_body() { 296 | local root="$(pwd)/sandbox" 297 | mkdir -p "${root}" 298 | sandbox_enter "${root}" 299 | touch "${root}/.sandbox_lock.tmp" 300 | chmod 555 "${root}/.sandbox_lock.tmp" 301 | 302 | if ( sandbox_leave "${root}" ) 2>err; then 303 | chmod 755 "${root}" 304 | atf_fail "sandbox_leave did not raise an error" 305 | fi 306 | if ( ! sandbox_leave -f "${root}" ) 2>err; then 307 | atf_fail "sandbox_leave -f raised an error" 308 | fi 309 | [ ! -f "${root}/.sandbox_lock" ] || atf_fail "Lock file not deleted" 310 | } 311 | 312 | 313 | atf_test_case leave__not_entered 314 | leave__not_entered_body() { 315 | local root="$(pwd)/sandbox" 316 | mkdir -p "${root}" 317 | 318 | if ( sandbox_leave "${root}" ) 2>err; then 319 | atf_fail "sandbox_leave did not raise an error" 320 | fi 321 | atf_check -o match:'E: Sandbox not locked' cat err 322 | [ ! -f "${root}/.sandbox_lock" ] || atf_fail "Lock file created" 323 | } 324 | 325 | 326 | atf_test_case leave__unknown_flag 327 | leave__unknown_flag_body() { 328 | if ( sandbox_leave -k 2>err ); then 329 | atf_fail "sandbox_leave did not raise an error" 330 | fi 331 | atf_check -o match:'E: Unknown option -k' cat err 332 | } 333 | 334 | 335 | atf_test_case has_mounts__no cleanup 336 | has_mounts__no_head() { 337 | atf_set "require.user" "root" 338 | } 339 | has_mounts__no_body() { 340 | mkdir -p sandbox/tmp 341 | mkdir -p sandbox2/tmp 342 | mount_tmpfs sandbox2/tmp 343 | if sandbox_has_mounts sandbox; then 344 | atf_fail "sandbox_has_mount should have returned false" 345 | fi 346 | } 347 | has_mounts__no_cleanup() { 348 | umount sandbox2/tmp >/dev/null 2>&1 || true 349 | } 350 | 351 | 352 | atf_test_case has_mounts__yes cleanup 353 | has_mounts__yes_head() { 354 | atf_set "require.user" "root" 355 | } 356 | has_mounts__yes_body() { 357 | mkdir -p sandbox/tmp 358 | mount_tmpfs sandbox/tmp 359 | 360 | sandbox_has_mounts sandbox || atf_fail "sandbox_has_mount should have" \ 361 | "returned true" 362 | } 363 | has_mounts__yes_cleanup() { 364 | umount sandbox/tmp >/dev/null 2>&1 || true 365 | } 366 | 367 | 368 | atf_test_case has_mounts__yes_indirect cleanup 369 | has_mounts__yes_indirect_head() { 370 | atf_set "require.user" "root" 371 | } 372 | has_mounts__yes_indirect_body() { 373 | mkdir -p sandbox/tmp 374 | ln -s sandbox other 375 | mount_tmpfs other/tmp 376 | 377 | sandbox_has_mounts other || atf_fail "sandbox_has_mount should have" \ 378 | "returned true" 379 | } 380 | has_mounts__yes_indirect_cleanup() { 381 | umount other/tmp >/dev/null 2>&1 || true 382 | umount sandbox/tmp >/dev/null 2>&1 || true 383 | } 384 | 385 | 386 | atf_test_case unmount_dirs__ok cleanup 387 | unmount_dirs__ok_head() { 388 | atf_set "require.user" "root" 389 | } 390 | unmount_dirs__ok_body() { 391 | for dir in sandbox/first/dir sandbox/second/nested/dir sandbox2/tmp; do 392 | echo "${dir}" >>dirs 393 | mkdir -p "${dir}" 394 | mount_tmpfs "${dir}" 395 | touch "${dir}/cookie" 396 | done 397 | 398 | sandbox_unmount_dirs sandbox || atf_fail "Failed to unmount sandbox" 399 | if [ -n "$(find sandbox -name cookie)" ]; then 400 | atf_fail "File systems seem to be left mounted" 401 | fi 402 | 403 | [ -f sandbox2/tmp/cookie ] || atf_fail "File systems outside of the" \ 404 | "sandbox were unmounted" 405 | } 406 | unmount_dirs__ok_cleanup() { 407 | for dir in $(cat dirs); do 408 | umount "${dir}" >/dev/null 2>&1 || true 409 | done 410 | } 411 | 412 | 413 | atf_test_case unmount_dirs__ok_indirect cleanup 414 | unmount_dirs__ok_indirect_head() { 415 | atf_set "require.user" "root" 416 | } 417 | unmount_dirs__ok_indirect_body() { 418 | mkdir -p sandbox/first/dir 419 | ln -s sandbox other 420 | mount_tmpfs other/first/dir 421 | touch other/first/dir/cookie 422 | 423 | sandbox_unmount_dirs other || atf_fail "Failed to unmount sandbox" 424 | if [ -n "$(find other/first -name cookie)" ]; then 425 | atf_fail "File systems seem to be left mounted" 426 | fi 427 | } 428 | unmount_dirs__ok_indirect_cleanup() { 429 | umount sandbox/first/dir >/dev/null 2>&1 || true 430 | umount other/first/dir >/dev/null 2>&1 || true 431 | } 432 | 433 | 434 | atf_test_case unmount_dirs__ok_respect_spaces cleanup 435 | unmount_dirs__ok_respect_spaces_head() { 436 | atf_set "require.user" "root" 437 | } 438 | unmount_dirs__ok_respect_spaces_body() { 439 | for dir in 'sandbox/fi rst/dir' 'sandbox/se cond/nested/dir'; do 440 | echo "${dir}" >>dirs 441 | mkdir -p "${dir}" 442 | mount_tmpfs "${dir}" 443 | touch "${dir}/cookie" 444 | done 445 | 446 | sandbox_unmount_dirs sandbox || atf_fail "Failed to unmount sandbox" 447 | if [ -n "$(find sandbox -name cookie)" ]; then 448 | atf_fail "File systems seem to be left mounted" 449 | fi 450 | } 451 | unmount_dirs__ok_respect_spaces_cleanup() { 452 | cat dirs | while read dir; do 453 | umount "${dir}" >/dev/null 2>&1 || true 454 | done 455 | } 456 | 457 | 458 | atf_test_case unmount_dirs__busy cleanup 459 | unmount_dirs__busy_head() { 460 | atf_set "require.user" "root" 461 | } 462 | unmount_dirs__busy_body() { 463 | mkdir -p sandbox/tmp 464 | mount_tmpfs sandbox/tmp 465 | 466 | cd sandbox/tmp 467 | sleep 300 & # Keep the mount point busy. 468 | local busy_pid="${!}" 469 | cd - 470 | echo "${busy_pid}" >busy.pid 471 | 472 | # The test strategy below is to ensure that umount(8) gets called multiple 473 | # times until the mount point is not busy any longer. To do this, we stub 474 | # out the umount(8) command so that the subprocess is killed after a few 475 | # attempts. 476 | local umount_tries=10 477 | local real_umount="$(which umount)" 478 | umount() { 479 | umount_tries=$((${umount_tries} - 1)) 480 | [ ${umount_tries} -gt 0 ] || kill "${busy_pid}" 481 | "${real_umount}" "${@}" 482 | } 483 | 484 | sandbox_unmount_dirs sandbox 2>err || atf_fail "Failed to unmount sandbox" 485 | atf_check \ 486 | -o match:"umount failed" \ 487 | -o match:"sandbox/tmp.* has not disappeared yet; waiting" \ 488 | cat err 489 | 490 | [ ! -e sandbox/tmp/foo ] || atf_fail "File systems seem to be left mounted" 491 | } 492 | unmount_dirs__busy_cleanup() { 493 | [ ! -f busy.pid ] || kill -9 "$(cat busy.pid)" >/dev/null 2>&1 494 | umount sandbox/tmp >/dev/null 2>&1 || true 495 | } 496 | 497 | 498 | atf_test_case unmount_dirs__slow cleanup 499 | unmount_dirs__slow_head() { 500 | atf_set "require.user" "root" 501 | } 502 | unmount_dirs__slow_body() { 503 | mkdir -p sandbox/tmp 504 | mount_tmpfs sandbox/tmp 505 | touch sandbox/tmp/foo 506 | 507 | local real_umount="$(which umount)" 508 | umount() { 509 | ( sleep 1; "${real_umount}" "${@}" ) & 510 | } 511 | 512 | sandbox_unmount_dirs sandbox 2>err || atf_fail "Failed to unmount sandbox" 513 | atf_check \ 514 | -o match:"sandbox/tmp.* has not disappeared yet; waiting" \ 515 | -o not-match:"umount failed" \ 516 | cat err 517 | 518 | [ ! -e sandbox/tmp/foo ] || atf_fail "File systems seem to be left mounted" 519 | } 520 | unmount_dirs__slow_cleanup() { 521 | umount sandbox/tmp >/dev/null 2>&1 || true 522 | } 523 | 524 | 525 | atf_test_case unmount_dirs__force cleanup 526 | unmount_dirs__force_head() { 527 | atf_set "require.user" "root" 528 | } 529 | unmount_dirs__force_body() { 530 | mkdir -p sandbox/tmp 531 | mount_tmpfs sandbox/tmp 532 | touch sandbox/tmp/foo 533 | 534 | ( cd sandbox/tmp && sleep 300 ) & # Keep the mount point busy. 535 | 536 | if ( sandbox_unmount_dirs sandbox ) 2>err; then 537 | atf_fail "sandbox_unmount_dirs did not raise an error" 538 | fi 539 | [ -e sandbox/tmp/foo ] || atf_fail "File systems were prematurely unmounted" 540 | 541 | if ( ! sandbox_unmount_dirs -f sandbox ) 2>err; then 542 | atf_fail "sandbox_unmount_dirs -f raised an error" 543 | fi 544 | [ ! -e sandbox/tmp/foo ] || atf_fail "File systems seem to be left mounted" 545 | } 546 | unmount_dirs__force_cleanup() { 547 | umount sandbox/tmp >/dev/null 2>&1 || true 548 | } 549 | 550 | 551 | atf_test_case unmount_dirs__error cleanup 552 | unmount_dirs__error_head() { 553 | atf_set "require.user" "root" 554 | } 555 | unmount_dirs__error_body() { 556 | mkdir -p sandbox/mnt 557 | mount_tmpfs sandbox/mnt 558 | 559 | cd sandbox/mnt 560 | sleep 600 & 561 | local pid="${!}" 562 | cd - 563 | echo "${pid}" >pid 564 | 565 | if ( sandbox_unmount_dirs sandbox 2>err ); then 566 | atf_fail "sandbox_unmount_dirs did not raise an error" 567 | fi 568 | kill -9 "${pid}" 569 | wait "${pid}" 570 | sandbox_unmount_dirs sandbox || atf_fail "Failed to unmount sandbox" 571 | } 572 | unmount_dirs__error_cleanup() { 573 | kill -9 "$(cat pid)" 2>&1 || true 574 | umount sandbox/mnt >/dev/null 2>&1 || true 575 | } 576 | 577 | 578 | atf_test_case unmount_dirs__unknown_flag 579 | unmount_dirs__unknown_flag_body() { 580 | if ( sandbox_unmount_dirs -k foo 2>err ); then 581 | atf_fail "sandbox_unmount_dirs did not raise an error" 582 | fi 583 | atf_check -o match:'E: Unknown option -k' cat err 584 | } 585 | 586 | 587 | atf_test_case destroy__ok 588 | destroy__ok_body() { 589 | mkdir -p sandbox/tmp 590 | touch sandbox/tmp/foo 591 | chmod 555 sandbox/tmp 592 | 593 | sandbox_destroy sandbox || atf_fail "sandbox_destroy failed" 594 | [ ! -d sandbox ] || atf_fail "sandbox not deleted" 595 | } 596 | 597 | 598 | atf_test_case destroy__protected_files cleanup 599 | destroy__protected_files_head() { 600 | case "$(uname -s)" in 601 | Linux) 602 | atf_set "require.progs" "chattr" 603 | ;; 604 | *) 605 | atf_set "require.progs" "chflags" 606 | ;; 607 | esac 608 | atf_set "require.user" "root" 609 | } 610 | destroy__protected_files_body() { 611 | mkdir -p sandbox/tmp 612 | touch sandbox/tmp/foo 613 | touch sandbox/tmp/bar 614 | case "$(uname -s)" in 615 | Linux) 616 | chattr +i sandbox/tmp/foo sandbox/tmp/bar 617 | ;; 618 | *) 619 | chflags schg sandbox/tmp/foo 620 | chflags uchg sandbox/tmp/bar 621 | ;; 622 | esac 623 | 624 | sandbox_destroy sandbox || atf_fail "sandbox_destroy failed" 625 | [ ! -d sandbox ] || atf_fail "sandbox not deleted" 626 | } 627 | destroy__protected_files_cleanup() { 628 | chflags noschg sandbox/tmp/foo >/dev/null 2>&1 || true 629 | chflags nouchg sandbox/tmp/bar >/dev/null 2>&1 || true 630 | } 631 | 632 | 633 | atf_test_case destroy__abort_if_still_mounted cleanup 634 | destroy__abort_if_still_mounted_head() { 635 | atf_set "require.user" "root" 636 | } 637 | destroy__abort_if_still_mounted_body() { 638 | cat >script.sh </dev/null 2>&1 || true 656 | } 657 | 658 | 659 | atf_test_case destroy__abort_if_root 660 | destroy__abort_if_root_body() { 661 | cat >script.sh </dev/null || true 674 | atf_check -s not-exit:0 \ 675 | -e match:"script: A: Attempting to delete /" ./script "${candidate}" 676 | [ ! -f chmod-called ] || atf_fail "chmod called; did not abort" 677 | done 678 | } 679 | 680 | 681 | # Creates a test tarball with some files in it. 682 | # 683 | # \param path Path to the tarball to be created. 684 | # \param format Compression format as indicated by tar's flag. 685 | create_test_tar() { 686 | local path="${1}"; shift 687 | local format="${1}"; shift 688 | 689 | mkdir dir1 dir2 690 | touch dir1/file1 dir1/file2 dir2/file1 691 | 692 | mkdir -p "$(dirname "${path}")" 693 | tar "-c${format}f" "${path}" dir1 dir2 694 | 695 | rm -rf dir1 dir2 696 | } 697 | 698 | 699 | atf_test_case extract__all__not_verbose 700 | extract__all__not_verbose_body() { 701 | create_test_tar dist/test.tgz z 702 | 703 | mkdir destdir 704 | sandbox_extract dist/test.tgz destdir 705 | [ -f destdir/dir1/file1 ] || atf_fail "File missing after extraction" 706 | [ -f destdir/dir1/file2 ] || atf_fail "File missing after extraction" 707 | [ -f destdir/dir2/file1 ] || atf_fail "File missing after extraction" 708 | } 709 | 710 | 711 | atf_test_case extract__all__verbose 712 | extract__all__verbose_body() { 713 | shtk_cli_set_log_level debug 714 | extract__all__not_verbose_body 715 | } 716 | 717 | 718 | atf_test_case extract__all__formats 719 | extract__all__formats_body() { 720 | create_test_tar dist/test.tar.gz z 721 | create_test_tar dist/test.tgz z 722 | create_test_tar dist/test.tar.xz J 723 | create_test_tar dist/test.txz J 724 | 725 | for f in dist/test.tar.gz dist/test.tgz dist/test.tar.xz dist/test.txz; do 726 | mkdir destdir 727 | sandbox_extract "${f}" destdir 728 | [ -f destdir/dir1/file1 ] || atf_fail "File missing after extraction" 729 | [ -f destdir/dir1/file2 ] || atf_fail "File missing after extraction" 730 | [ -f destdir/dir2/file1 ] || atf_fail "File missing after extraction" 731 | rm -rf destdir 732 | done 733 | } 734 | 735 | 736 | atf_test_case extract__some__not_verbose 737 | extract__some__not_verbose_body() { 738 | create_test_tar dist/test.tgz z 739 | 740 | mkdir destdir 741 | sandbox_extract dist/test.tgz "$(pwd)/destdir" dir1/file2 dir2 742 | [ ! -f destdir/dir1/file1 ] || atf_fail "Unexpected file extracted" 743 | [ -f destdir/dir1/file2 ] || atf_fail "File missing after extraction" 744 | [ -f destdir/dir2/file1 ] || atf_fail "File missing after extraction" 745 | } 746 | 747 | 748 | atf_test_case extract__some__verbose 749 | extract__some__verbose_body() { 750 | shtk_cli_set_log_level debug 751 | extract__some__not_verbose_body 752 | } 753 | 754 | 755 | atf_test_case extract__error__not_verbose 756 | extract__error__not_verbose_body() { 757 | create_test_tar dist/test.tgz z 758 | 759 | if ( sandbox_extract dist/test.tgz destdir ) 2>err; then 760 | atf_fail "sandbox_extract did not raise an error" 761 | fi 762 | atf_check -o match:'E: Extraction of dist/test.tgz failed' cat err 763 | [ ! -d destdir ] || atf_fail "destdir unexpectedly created" 764 | } 765 | 766 | 767 | atf_test_case extract__error__verbose 768 | extract__error__verbose_body() { 769 | shtk_cli_set_log_level debug 770 | extract__error__not_verbose_body 771 | } 772 | 773 | 774 | atf_test_case extract__bad_args 775 | extract__bad_args_body() { 776 | if ( sandbox_extract foo 2>err ); then 777 | atf_fail "sandbox_extract did not raise an error" 778 | fi 779 | atf_check -o match:'E: sandbox_extract: syntax error' cat err 780 | } 781 | 782 | 783 | atf_test_case bindfs__ok__default_ro cleanup 784 | bindfs__ok__default_ro_head() { 785 | atf_set "require.user" "root" 786 | } 787 | bindfs__ok__default_ro_body() { 788 | shtk_abort() { atf_skip "${@}"; } 789 | 790 | mkdir real 791 | echo first >real/file 792 | 793 | mkdir -p sandbox/mnt 794 | sandbox_bindfs "${@}" real sandbox/mnt || atf_fail "Failed to mount bindfs" 795 | 796 | if echo second >sandbox/mnt/file; then 797 | atf_fail "bindfs did not default to read-only mode" 798 | fi 799 | atf_check -o inline:'first\n' cat real/file 800 | atf_check -o inline:'first\n' cat sandbox/mnt/file 801 | echo third >real/file 802 | atf_check -o inline:'third\n' cat real/file 803 | atf_check -o inline:'third\n' cat sandbox/mnt/file 804 | } 805 | bindfs__ok__default_ro_cleanup() { 806 | umount sandbox/mnt >/dev/null 2>&1 || true 807 | } 808 | 809 | 810 | atf_test_case bindfs__ok__ro_mode cleanup 811 | bindfs__ok__ro_mode_head() { 812 | bindfs__ok__default_ro_head 813 | } 814 | bindfs__ok__ro_mode_body() { 815 | bindfs__ok__default_ro_body -o ro 816 | } 817 | bindfs__ok__ro_mode_cleanup() { 818 | bindfs__ok__default_ro_cleanup 819 | } 820 | 821 | 822 | atf_test_case bindfs__ok__rw_mode cleanup 823 | bindfs__ok__rw_mode_head() { 824 | atf_set "require.user" "root" 825 | } 826 | bindfs__ok__rw_mode_body() { 827 | shtk_abort() { atf_skip "${@}"; } 828 | 829 | mkdir real 830 | echo first >real/file 831 | 832 | mkdir -p sandbox/mnt 833 | sandbox_bindfs -o rw real sandbox/mnt || atf_fail "Failed to mount bindfs" 834 | 835 | echo second >sandbox/mnt/file || atf_fail "bindfs not in read-write mode" 836 | atf_check -o inline:'second\n' cat real/file 837 | atf_check -o inline:'second\n' cat sandbox/mnt/file 838 | echo third >real/file 839 | atf_check -o inline:'third\n' cat real/file 840 | atf_check -o inline:'third\n' cat sandbox/mnt/file 841 | } 842 | bindfs__ok__rw_mode_cleanup() { 843 | umount sandbox/mnt >/dev/null 2>&1 || true 844 | } 845 | 846 | 847 | atf_test_case bindfs__mount_fails cleanup 848 | bindfs__mount_fails_head() { 849 | atf_set "require.user" "root" 850 | } 851 | bindfs__mount_fails_body() { 852 | shtk_abort() { atf_skip "${@}"; } 853 | 854 | if ( sandbox_bindfs -o rw real sandbox/mnt 2>err ); then 855 | atf_fail "sandbox_bindfs did not raise an error" 856 | fi 857 | atf_check -o match:'E: Failed to bind sandbox/mnt' cat err 858 | } 859 | bindfs__mount_fails_cleanup() { 860 | umount sandbox/mnt >/dev/null 2>&1 || true 861 | } 862 | 863 | 864 | atf_test_case bindfs__invalid_option 865 | bindfs__invalid_option_body() { 866 | if ( sandbox_bindfs -o rww bar 2>err ); then 867 | atf_fail "sandbox_bindfs did not raise an error" 868 | fi 869 | atf_check -o match:'E: Unsupported mount option rww' cat err 870 | } 871 | 872 | 873 | atf_test_case bindfs__unknown_flag 874 | bindfs__unknown_flag_body() { 875 | if ( sandbox_bindfs -k foo bar 2>err ); then 876 | atf_fail "sandbox_bindfs did not raise an error" 877 | fi 878 | atf_check -o match:'E: Unknown option -k' cat err 879 | } 880 | 881 | 882 | atf_test_case bindfs__bad_args 883 | bindfs__bad_args_body() { 884 | if ( sandbox_bindfs foo 2>err ); then 885 | atf_fail "sandbox_bindfs did not raise an error" 886 | fi 887 | atf_check -o match:'E: sandbox_bindfs: syntax error' cat err 888 | 889 | if ( sandbox_bindfs foo bar baz 2>err ); then 890 | atf_fail "sandbox_bindfs did not raise an error" 891 | fi 892 | atf_check -o match:'E: sandbox_bindfs: syntax error' cat err 893 | } 894 | 895 | 896 | atf_init_test_cases() { 897 | shtk_cli_set_log_level debug 898 | 899 | atf_add_test_case load_types__none__no_dir 900 | atf_add_test_case load_types__none__empty_dir 901 | atf_add_test_case load_types__ok 902 | atf_add_test_case load_types__default_hooks 903 | atf_add_test_case load_types__error 904 | 905 | atf_add_test_case call_types__ok 906 | atf_add_test_case call_types__error 907 | 908 | atf_add_test_case dispatch__type__ok 909 | atf_add_test_case dispatch__type__error 910 | atf_add_test_case dispatch__unknown_type 911 | atf_add_test_case dispatch__unknown_action 912 | 913 | atf_add_test_case enter_leave__once 914 | atf_add_test_case enter_leave__nested 915 | 916 | atf_add_test_case enter__error 917 | 918 | atf_add_test_case leave__force 919 | atf_add_test_case leave__error 920 | atf_add_test_case leave__not_entered 921 | atf_add_test_case leave__unknown_flag 922 | 923 | atf_add_test_case has_mounts__no 924 | atf_add_test_case has_mounts__yes 925 | atf_add_test_case has_mounts__yes_indirect 926 | 927 | atf_add_test_case unmount_dirs__ok 928 | atf_add_test_case unmount_dirs__ok_indirect 929 | atf_add_test_case unmount_dirs__ok_respect_spaces 930 | atf_add_test_case unmount_dirs__busy 931 | atf_add_test_case unmount_dirs__slow 932 | atf_add_test_case unmount_dirs__force 933 | atf_add_test_case unmount_dirs__error 934 | atf_add_test_case unmount_dirs__unknown_flag 935 | 936 | atf_add_test_case destroy__ok 937 | atf_add_test_case destroy__protected_files 938 | atf_add_test_case destroy__abort_if_still_mounted 939 | atf_add_test_case destroy__abort_if_root 940 | 941 | atf_add_test_case extract__all__not_verbose 942 | atf_add_test_case extract__all__verbose 943 | atf_add_test_case extract__all__formats 944 | atf_add_test_case extract__some__not_verbose 945 | atf_add_test_case extract__some__verbose 946 | atf_add_test_case extract__error__not_verbose 947 | atf_add_test_case extract__error__verbose 948 | atf_add_test_case extract__bad_args 949 | 950 | atf_add_test_case bindfs__ok__default_ro 951 | atf_add_test_case bindfs__ok__ro_mode 952 | atf_add_test_case bindfs__ok__rw_mode 953 | atf_add_test_case bindfs__mount_fails 954 | atf_add_test_case bindfs__invalid_option 955 | atf_add_test_case bindfs__unknown_flag 956 | atf_add_test_case bindfs__bad_args 957 | } 958 | -------------------------------------------------------------------------------- /sandboxctl_test.sh: -------------------------------------------------------------------------------- 1 | #! __ATF_SH__ 2 | # Copyright 2013 Google Inc. 3 | # All rights reserved. 4 | # 5 | # Redistribution and use in source and binary forms, with or without 6 | # modification, are permitted provided that the following conditions are 7 | # met: 8 | # 9 | # * Redistributions of source code must retain the above copyright 10 | # notice, this list of conditions and the following disclaimer. 11 | # * Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # * Neither the name of Google Inc. nor the names of its contributors 15 | # may be used to endorse or promote products derived from this software 16 | # without specific prior written permission. 17 | # 18 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | 30 | # \file sandboxctl_test.sh 31 | # Tests for the sandboxctl.sh script. 32 | # 33 | # The tests in this file should be OS-agnostic. They validate the generic 34 | # behavior of the tool using a mock sandbox type. Every supported sandbox 35 | # is tested in a separate test program. 36 | 37 | 38 | # Paths to installed files. 39 | # 40 | # Can be overriden for test purposes only. 41 | : ${SANDBOXCTL_MODULESDIR="__SANDBOXCTL_MODULESDIR__"} 42 | 43 | 44 | # Creates a configuration using a mock sandbox type. 45 | # 46 | # This mock sandbox type records all actions performed in the given file to 47 | # the standard output. This log can later be used to validate that every 48 | # handler has been called in the right order and the expected number of times. 49 | # 50 | # \param config_file Name of the configuration file to create. 51 | # \param sandbox_root Path to the root of the sandbox to be created. 52 | create_config_with_mock_type() { 53 | local config_file="${1}"; shift 54 | local sandbox_root="${1}"; shift 55 | 56 | mkdir modules 57 | cat >"modules/mock.subr" <"${config_file}" <>"${config_file}" <chroot <expout <expout <my-file 159 | atf_check -o match:"SANDBOX_ROOT = the-root" sandboxctl -c ./my-file config 160 | } 161 | 162 | 163 | atf_test_case config__path__extension 164 | config__path__extension_body() { 165 | mkdir system 166 | export SANDBOXCTL_ETCDIR="$(pwd)/system" 167 | 168 | echo "SANDBOX_ROOT=another-root" >my-file.conf 169 | atf_check -o match:"SANDBOX_ROOT = another-root" sandboxctl \ 170 | -c my-file.conf config 171 | } 172 | 173 | 174 | atf_test_case config__name__system_directory 175 | config__name__system_directory_body() { 176 | mkdir system 177 | export SANDBOXCTL_ETCDIR="$(pwd)/system" 178 | 179 | echo "SANDBOX_TYPE=some-type" >system/foo.conf 180 | atf_check -o match:"SANDBOX_TYPE = some-type" sandboxctl -c foo config 181 | } 182 | 183 | 184 | atf_test_case config__name__not_found 185 | config__name__not_found_body() { 186 | mkdir system 187 | export SANDBOXCTL_ETCDIR="$(pwd)/system" 188 | 189 | cat >experr <custom.conf <expout <expout <experr <expout <>custom.conf 311 | 312 | atf_check -s exit:1 \ 313 | -e match:"sandboxctl: E: Invalid sandbox type .*unknown-type" \ 314 | sandboxctl -c custom.conf create 315 | 316 | [ ! -d "$(pwd)/sandbox" ] || atf_fail "Sandbox was created" 317 | } 318 | 319 | 320 | atf_test_case create__fail_in_type 321 | create__fail_in_type_body() { 322 | create_config_with_mock_type custom.conf "$(pwd)/sandbox" failing 323 | echo 'mock_create() { echo "mock_create ${*}"; exit 1; }' >>custom.conf 324 | echo 'post_create_hook() { echo "custom post_create_hook"; }' >>custom.conf 325 | 326 | cat >expout <>custom.conf 343 | 344 | cat >expout <expout </dev/null 2>&1 || true 415 | } 416 | 417 | 418 | atf_test_case destroy__validate_config 419 | destroy__validate_config_body() { 420 | test_validate_config destroy 421 | } 422 | 423 | 424 | atf_test_case destroy__fail_type 425 | destroy__fail_type_body() { 426 | create_config_with_mock_type custom.conf "$(pwd)/sandbox" failing 427 | echo 'mock_destroy() { echo "mock_destroy ${*}"; exit 1; }' >>custom.conf 428 | echo 'pre_destroy_hook() { echo "custom pre_destroy_hook"; }' >>custom.conf 429 | 430 | mkdir sandbox 431 | 432 | cat >expout <>custom.conf 449 | 450 | mkdir sandbox 451 | 452 | cat >expout <expout </dev/null || atf_fail "File systems were" \ 517 | "unmounted but should not have been" 518 | } 519 | mount__already_mounted_cleanup() { 520 | umount sandbox/mnt >/dev/null 2>&1 || true 521 | } 522 | 523 | 524 | atf_test_case mount__validate_config 525 | mount__validate_config_body() { 526 | test_validate_config mount 527 | } 528 | 529 | 530 | atf_test_case mount__fail_type 531 | mount__fail_type_body() { 532 | create_config_with_mock_type custom.conf "$(pwd)/sandbox" failing 533 | echo 'mock_mount() { echo "mock_mount ${*}"; exit 1; }' >>custom.conf 534 | echo 'post_mount_hook() { echo "custom post_mount_hook"; }' >>custom.conf 535 | 536 | mkdir sandbox 537 | 538 | cat >expout <>custom.conf 555 | 556 | mkdir sandbox 557 | 558 | cat >expout <expout </dev/null 2>&1 || true 670 | } 671 | 672 | 673 | atf_test_case unmount__validate_config 674 | unmount__validate_config_body() { 675 | test_validate_config unmount 676 | } 677 | 678 | 679 | atf_test_case unmount__fail_type 680 | unmount__fail_type_body() { 681 | create_config_with_mock_type custom.conf "$(pwd)/sandbox" failing 682 | echo 'mock_unmount() { echo "mock_unmount ${*}"; exit 1; }' >>custom.conf 683 | echo 'pre_unmount_hook() { echo "custom pre_unmount_hook"; }' >>custom.conf 684 | 685 | mkdir sandbox 686 | atf_check -o ignore sandboxctl -c custom.conf mount 687 | 688 | cat >expout <>custom.conf 705 | 706 | mkdir sandbox 707 | atf_check -o ignore sandboxctl -c custom.conf mount 708 | 709 | cat >expout <experr <experr <expout <expout <sandbox/fail.sh <expout <sandbox/wait.sh <out 2>err & 860 | local pid="${!}" 861 | 862 | while [ ! -e sandbox/cookie ]; do 863 | echo "sandbox/cookie not found; waiting for sandbox to come up" 864 | sleep .1 865 | done 866 | 867 | echo "Sending ${signal} to sandboxctl PID ${pid}" 868 | kill "-${signal}" "${pid}" 869 | rm sandbox/cookie 870 | wait "${pid}" 871 | 872 | cat >expout <expout <expout <expout <out 2>err & 972 | local pid="${!}" 973 | 974 | while [ ! -e sandbox/cookie ]; do 975 | echo "sandbox/cookie not found; waiting for sandbox to come up" 976 | sleep .1 977 | done 978 | 979 | echo "Sending ${signal} to sandboxctl PID ${pid}" 980 | kill "-${signal}" "${pid}" 981 | rm sandbox/cookie 982 | wait "${pid}" 983 | 984 | cat >expout <experr <experr <experr <