1*b5dcd8f9SAlexander Leidinger#- 2*b5dcd8f9SAlexander Leidinger# SPDX-License-Identifier: BSD-2-Clause 3*b5dcd8f9SAlexander Leidinger# 4*b5dcd8f9SAlexander Leidinger# Copyright (c) 2026 Alexander Leidinger <netchild@FreeBSD.org> 5*b5dcd8f9SAlexander Leidinger# 6*b5dcd8f9SAlexander Leidinger# Redistribution and use in source and binary forms, with or without 7*b5dcd8f9SAlexander Leidinger# modification, are permitted provided that the following conditions 8*b5dcd8f9SAlexander Leidinger# are met: 9*b5dcd8f9SAlexander Leidinger# 1. Redistributions of source code must retain the above copyright 10*b5dcd8f9SAlexander Leidinger# notice, this list of conditions and the following disclaimer. 11*b5dcd8f9SAlexander Leidinger# 2. Redistributions in binary form must reproduce the above copyright 12*b5dcd8f9SAlexander Leidinger# notice, this list of conditions and the following disclaimer in the 13*b5dcd8f9SAlexander Leidinger# documentation and/or other materials provided with the distribution. 14*b5dcd8f9SAlexander Leidinger# 15*b5dcd8f9SAlexander Leidinger# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16*b5dcd8f9SAlexander Leidinger# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17*b5dcd8f9SAlexander Leidinger# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18*b5dcd8f9SAlexander Leidinger# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19*b5dcd8f9SAlexander Leidinger# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20*b5dcd8f9SAlexander Leidinger# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21*b5dcd8f9SAlexander Leidinger# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22*b5dcd8f9SAlexander Leidinger# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23*b5dcd8f9SAlexander Leidinger# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24*b5dcd8f9SAlexander Leidinger# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25*b5dcd8f9SAlexander Leidinger# SUCH DAMAGE. 26*b5dcd8f9SAlexander Leidinger# 27*b5dcd8f9SAlexander Leidinger# Service jail behaviour of run_rc_command(). Each case generates an rc.d 28*b5dcd8f9SAlexander Leidinger# script, drives it the way an operator would, and inspects the jail and the 29*b5dcd8f9SAlexander Leidinger# process that came out. 30*b5dcd8f9SAlexander Leidinger# 31*b5dcd8f9SAlexander Leidinger# Every case does that inside a chroot of its own, built in its ATF work 32*b5dcd8f9SAlexander Leidinger# directory, because the paths a service jail uses are absolute and would 33*b5dcd8f9SAlexander Leidinger# otherwise be the running system's: service(8) searches /etc/rc.d and 34*b5dcd8f9SAlexander Leidinger# ${local_startup} only, and a service jail re-enters its own script through 35*b5dcd8f9SAlexander Leidinger# it, so a fixture below the work directory is unreachable from inside the 36*b5dcd8f9SAlexander Leidinger# jail. The chroot is read-only nullfs mounts of the system directories plus 37*b5dcd8f9SAlexander Leidinger# a copied /etc, and rc.subr's "jail -c path=/" then roots the service jail 38*b5dcd8f9SAlexander Leidinger# at it. Nothing outside the work directory is written at any point. 39*b5dcd8f9SAlexander Leidinger 40*b5dcd8f9SAlexander Leidinger# svcj_svcname 41*b5dcd8f9SAlexander Leidinger# The service name for the current case. It becomes a jail name and a 42*b5dcd8f9SAlexander Leidinger# shell variable prefix, so it is reduced to alphanumerics. 43*b5dcd8f9SAlexander Leidingersvcj_svcname() 44*b5dcd8f9SAlexander Leidinger{ 45*b5dcd8f9SAlexander Leidinger echo "svcjt$(atf_get ident | tr -cd '[:alnum:]')" 46*b5dcd8f9SAlexander Leidinger} 47*b5dcd8f9SAlexander Leidinger 48*b5dcd8f9SAlexander Leidinger# svcj_chroot 49*b5dcd8f9SAlexander Leidinger# The root of this case's chroot, on the host. 50*b5dcd8f9SAlexander Leidingersvcj_chroot() 51*b5dcd8f9SAlexander Leidinger{ 52*b5dcd8f9SAlexander Leidinger echo "$(pwd)/svcjtestchroot" 53*b5dcd8f9SAlexander Leidinger} 54*b5dcd8f9SAlexander Leidinger 55*b5dcd8f9SAlexander Leidinger# svcj_rcpath 56*b5dcd8f9SAlexander Leidinger# This case's rc.d script, as seen from inside the chroot. The chroot's 57*b5dcd8f9SAlexander Leidinger# /etc is a copy, so using /etc/rc.d here cannot disturb the running 58*b5dcd8f9SAlexander Leidinger# system's, and an rcorder(8) run over /etc/rc.d/* -- which another test 59*b5dcd8f9SAlexander Leidinger# in this directory does -- never sees the fixture. 60*b5dcd8f9SAlexander Leidingersvcj_rcpath() 61*b5dcd8f9SAlexander Leidinger{ 62*b5dcd8f9SAlexander Leidinger echo "/etc/rc.d/$(svcj_svcname)" 63*b5dcd8f9SAlexander Leidinger} 64*b5dcd8f9SAlexander Leidinger 65*b5dcd8f9SAlexander Leidinger# svcj_workdir 66*b5dcd8f9SAlexander Leidinger# Where a case keeps its pid file and its markers, as seen from inside 67*b5dcd8f9SAlexander Leidinger# the chroot. 68*b5dcd8f9SAlexander Leidingersvcj_workdir() 69*b5dcd8f9SAlexander Leidinger{ 70*b5dcd8f9SAlexander Leidinger echo "/var/run/svcjt.$(svcj_svcname)" 71*b5dcd8f9SAlexander Leidinger} 72*b5dcd8f9SAlexander Leidinger 73*b5dcd8f9SAlexander Leidinger# svcj_hostrc / svcj_hostdir 74*b5dcd8f9SAlexander Leidinger# The same two paths as seen from outside, for the assertions. 75*b5dcd8f9SAlexander Leidingersvcj_hostrc() 76*b5dcd8f9SAlexander Leidinger{ 77*b5dcd8f9SAlexander Leidinger echo "$(svcj_chroot)$(svcj_rcpath)" 78*b5dcd8f9SAlexander Leidinger} 79*b5dcd8f9SAlexander Leidinger 80*b5dcd8f9SAlexander Leidingersvcj_hostdir() 81*b5dcd8f9SAlexander Leidinger{ 82*b5dcd8f9SAlexander Leidinger echo "$(svcj_chroot)$(svcj_workdir)" 83*b5dcd8f9SAlexander Leidinger} 84*b5dcd8f9SAlexander Leidinger 85*b5dcd8f9SAlexander Leidinger# svcj_require 86*b5dcd8f9SAlexander Leidinger# Requirements shared by every case: root, and room for one more jail. 87*b5dcd8f9SAlexander Leidingersvcj_require() 88*b5dcd8f9SAlexander Leidinger{ 89*b5dcd8f9SAlexander Leidinger local max cur 90*b5dcd8f9SAlexander Leidinger 91*b5dcd8f9SAlexander Leidinger if [ "$(id -u)" -ne 0 ]; then 92*b5dcd8f9SAlexander Leidinger atf_skip "creating a service jail requires root" 93*b5dcd8f9SAlexander Leidinger fi 94*b5dcd8f9SAlexander Leidinger # rc.subr bounds itself against children.max only when it is already 95*b5dcd8f9SAlexander Leidinger # jailed, so the check is made under the same condition. 96*b5dcd8f9SAlexander Leidinger if [ "$(sysctl -n security.jail.jailed)" -ne 0 ]; then 97*b5dcd8f9SAlexander Leidinger max=$(sysctl -n security.jail.children.max) 98*b5dcd8f9SAlexander Leidinger cur=$(sysctl -n security.jail.children.cur) 99*b5dcd8f9SAlexander Leidinger if [ "$max" -eq 0 ] || [ $((max - cur)) -eq 0 ]; then 100*b5dcd8f9SAlexander Leidinger atf_skip "no child jail available inside this jail" 101*b5dcd8f9SAlexander Leidinger fi 102*b5dcd8f9SAlexander Leidinger fi 103*b5dcd8f9SAlexander Leidinger} 104*b5dcd8f9SAlexander Leidinger 105*b5dcd8f9SAlexander Leidinger# svcj_mkchroot 106*b5dcd8f9SAlexander Leidinger# Build this case's chroot. Whether it can be built is decided by 107*b5dcd8f9SAlexander Leidinger# trying, not by asking whether this is a jail: a jail with allow.mount 108*b5dcd8f9SAlexander Leidinger# and the nullfs and devfs sub-options set can run these cases, and a 109*b5dcd8f9SAlexander Leidinger# machine whose kernel has no nullfs cannot, jailed or not. 110*b5dcd8f9SAlexander Leidingersvcj_mkchroot() 111*b5dcd8f9SAlexander Leidinger{ 112*b5dcd8f9SAlexander Leidinger local c d 113*b5dcd8f9SAlexander Leidinger 114*b5dcd8f9SAlexander Leidinger c=$(svcj_chroot) 115*b5dcd8f9SAlexander Leidinger mkdir -p "$c" || atf_fail "cannot create $c" 116*b5dcd8f9SAlexander Leidinger # This becomes a root directory, and the kernel checks search 117*b5dcd8f9SAlexander Leidinger # permission on it for every lookup an unprivileged process inside 118*b5dcd8f9SAlexander Leidinger # makes. At 0700 the ${name}_user cases fail in su(1). 119*b5dcd8f9SAlexander Leidinger chmod 0755 "$c" 120*b5dcd8f9SAlexander Leidinger : > "$(pwd)/svcj.mounts" 121*b5dcd8f9SAlexander Leidinger 122*b5dcd8f9SAlexander Leidinger for d in bin sbin lib libexec usr; do 123*b5dcd8f9SAlexander Leidinger mkdir -p "$c/$d" || atf_fail "cannot create $c/$d" 124*b5dcd8f9SAlexander Leidinger if ! mount -t nullfs -o ro "/$d" "$c/$d"; then 125*b5dcd8f9SAlexander Leidinger svcj_umount 126*b5dcd8f9SAlexander Leidinger atf_skip "cannot nullfs-mount /$d here" 127*b5dcd8f9SAlexander Leidinger fi 128*b5dcd8f9SAlexander Leidinger # Read-only is a safety property. A recursive delete of this 129*b5dcd8f9SAlexander Leidinger # tree with a mount still under it would run through the 130*b5dcd8f9SAlexander Leidinger # mount. 131*b5dcd8f9SAlexander Leidinger echo "$c/$d" >> "$(pwd)/svcj.mounts" 132*b5dcd8f9SAlexander Leidinger done 133*b5dcd8f9SAlexander Leidinger 134*b5dcd8f9SAlexander Leidinger mkdir -p "$c/dev" || atf_fail "cannot create $c/dev" 135*b5dcd8f9SAlexander Leidinger if ! mount -t devfs devfs "$c/dev"; then 136*b5dcd8f9SAlexander Leidinger svcj_umount 137*b5dcd8f9SAlexander Leidinger atf_skip "cannot mount devfs here" 138*b5dcd8f9SAlexander Leidinger fi 139*b5dcd8f9SAlexander Leidinger echo "$c/dev" >> "$(pwd)/svcj.mounts" 140*b5dcd8f9SAlexander Leidinger 141*b5dcd8f9SAlexander Leidinger mkdir -p "$c/etc" "$c/var/run" "$c/var/log" "$c/var/tmp" \ 142*b5dcd8f9SAlexander Leidinger "$c/var/empty" "$c/tmp" "$c/root" || atf_fail "cannot populate $c" 143*b5dcd8f9SAlexander Leidinger chmod 01777 "$c/tmp" "$c/var/tmp" 144*b5dcd8f9SAlexander Leidinger 145*b5dcd8f9SAlexander Leidinger # /etc is copied rather than mounted because the fixture, the pid 146*b5dcd8f9SAlexander Leidinger # files and rc.conf all live in it. 147*b5dcd8f9SAlexander Leidinger cp -a /etc/. "$c/etc/" || atf_fail "cannot copy /etc into $c" 148*b5dcd8f9SAlexander Leidinger rm -rf "$c/etc/rc.conf.d" 149*b5dcd8f9SAlexander Leidinger mkdir -p "$c/etc/rc.conf.d" || atf_fail "cannot create rc.conf.d" 150*b5dcd8f9SAlexander Leidinger echo 'hostname="svcjtest"' > "$c/etc/rc.conf" 151*b5dcd8f9SAlexander Leidinger} 152*b5dcd8f9SAlexander Leidinger 153*b5dcd8f9SAlexander Leidinger# svcj_umount 154*b5dcd8f9SAlexander Leidinger# Unmount what svcj_mkchroot mounted, in reverse. The verdict is what 155*b5dcd8f9SAlexander Leidinger# is still mounted afterwards, not what umount(8) returned. 156*b5dcd8f9SAlexander Leidingersvcj_umount() 157*b5dcd8f9SAlexander Leidinger{ 158*b5dcd8f9SAlexander Leidinger local f d 159*b5dcd8f9SAlexander Leidinger 160*b5dcd8f9SAlexander Leidinger f="$(pwd)/svcj.mounts" 161*b5dcd8f9SAlexander Leidinger [ -f "$f" ] || return 0 162*b5dcd8f9SAlexander Leidinger for d in $(tail -r "$f"); do 163*b5dcd8f9SAlexander Leidinger umount "$d" 2>/dev/null || umount -f "$d" 2>/dev/null 164*b5dcd8f9SAlexander Leidinger done 165*b5dcd8f9SAlexander Leidinger if mount | grep -q -F " on $(svcj_chroot)"; then 166*b5dcd8f9SAlexander Leidinger return 1 167*b5dcd8f9SAlexander Leidinger fi 168*b5dcd8f9SAlexander Leidinger : > "$f" 169*b5dcd8f9SAlexander Leidinger return 0 170*b5dcd8f9SAlexander Leidinger} 171*b5dcd8f9SAlexander Leidinger 172*b5dcd8f9SAlexander Leidinger# svcj_fixture 173*b5dcd8f9SAlexander Leidinger# Build the chroot and install this case's rc.d script into it, reading 174*b5dcd8f9SAlexander Leidinger# the case-specific lines from standard input. Those lines go after 175*b5dcd8f9SAlexander Leidinger# load_rc_config, so that they win over rc.conf, and before 176*b5dcd8f9SAlexander Leidinger# run_rc_command. 177*b5dcd8f9SAlexander Leidingersvcj_fixture() 178*b5dcd8f9SAlexander Leidinger{ 179*b5dcd8f9SAlexander Leidinger local svc c wd 180*b5dcd8f9SAlexander Leidinger 181*b5dcd8f9SAlexander Leidinger svc=$(svcj_svcname) 182*b5dcd8f9SAlexander Leidinger 183*b5dcd8f9SAlexander Leidinger # Recorded for the cleanup routine. 184*b5dcd8f9SAlexander Leidinger echo "$svc" > "$(pwd)/svcj.name" 185*b5dcd8f9SAlexander Leidinger 186*b5dcd8f9SAlexander Leidinger svcj_mkchroot 187*b5dcd8f9SAlexander Leidinger c=$(svcj_chroot) 188*b5dcd8f9SAlexander Leidinger wd=$(svcj_workdir) 189*b5dcd8f9SAlexander Leidinger mkdir -p "$c$wd" || atf_fail "cannot create $c$wd" 190*b5dcd8f9SAlexander Leidinger chmod 0777 "$c$wd" 191*b5dcd8f9SAlexander Leidinger 192*b5dcd8f9SAlexander Leidinger { 193*b5dcd8f9SAlexander Leidinger echo "#!/bin/sh" 194*b5dcd8f9SAlexander Leidinger echo "# PROVIDE: $svc" 195*b5dcd8f9SAlexander Leidinger echo ". /etc/rc.subr" 196*b5dcd8f9SAlexander Leidinger echo "name=\"$svc\"" 197*b5dcd8f9SAlexander Leidinger echo "rcvar=\"${svc}_enable\"" 198*b5dcd8f9SAlexander Leidinger echo "load_rc_config \$name" 199*b5dcd8f9SAlexander Leidinger echo "pidfile=\"$wd/$svc.pid\"" 200*b5dcd8f9SAlexander Leidinger cat 201*b5dcd8f9SAlexander Leidinger echo "run_rc_command \"\$1\"" 202*b5dcd8f9SAlexander Leidinger } > "$(svcj_hostrc)" 203*b5dcd8f9SAlexander Leidinger chmod 0755 "$(svcj_hostrc)" 204*b5dcd8f9SAlexander Leidinger} 205*b5dcd8f9SAlexander Leidinger 206*b5dcd8f9SAlexander Leidinger# svcj_daemon_lines 207*b5dcd8f9SAlexander Leidinger# The stock daemon used by most cases: one sleep(1), pid in ${pidfile}. 208*b5dcd8f9SAlexander Leidingersvcj_daemon_lines() 209*b5dcd8f9SAlexander Leidinger{ 210*b5dcd8f9SAlexander Leidinger echo 'command="/usr/sbin/daemon"' 211*b5dcd8f9SAlexander Leidinger echo 'command_args="-p $pidfile -- /bin/sleep 300"' 212*b5dcd8f9SAlexander Leidinger echo 'procname="/bin/sleep"' 213*b5dcd8f9SAlexander Leidinger} 214*b5dcd8f9SAlexander Leidinger 215*b5dcd8f9SAlexander Leidinger# svcj_jid 216*b5dcd8f9SAlexander Leidinger# The jid of this case's service jail, empty when there is none. 217*b5dcd8f9SAlexander Leidingersvcj_jid() 218*b5dcd8f9SAlexander Leidinger{ 219*b5dcd8f9SAlexander Leidinger jls -j "svcj-$(svcj_svcname)" jid 2>/dev/null 220*b5dcd8f9SAlexander Leidinger} 221*b5dcd8f9SAlexander Leidinger 222*b5dcd8f9SAlexander Leidinger# svcj_pid 223*b5dcd8f9SAlexander Leidinger# The pid the daemon recorded, empty when there is none yet. daemon(8) 224*b5dcd8f9SAlexander Leidinger# writes the file from a child, so a read right after the rc.d script 225*b5dcd8f9SAlexander Leidinger# returns can lose the race; wait for it. 226*b5dcd8f9SAlexander Leidingersvcj_pid() 227*b5dcd8f9SAlexander Leidinger{ 228*b5dcd8f9SAlexander Leidinger local f pid junk 229*b5dcd8f9SAlexander Leidinger 230*b5dcd8f9SAlexander Leidinger f="$(svcj_hostdir)/$(svcj_svcname).pid" 231*b5dcd8f9SAlexander Leidinger svcj_wait_file "$f" || return 1 232*b5dcd8f9SAlexander Leidinger read pid junk < "$f" 233*b5dcd8f9SAlexander Leidinger echo "$pid" 234*b5dcd8f9SAlexander Leidinger} 235*b5dcd8f9SAlexander Leidinger 236*b5dcd8f9SAlexander Leidinger# svcj_wait_file <path> 237*b5dcd8f9SAlexander Leidinger# True once the file exists and has content. The bound is a 238*b5dcd8f9SAlexander Leidinger# deadline rather than a delay: it costs nothing on a machine that 239*b5dcd8f9SAlexander Leidinger# is quick, and 30s is far enough above what a loaded one needs 240*b5dcd8f9SAlexander Leidinger# that expiry means the thing waited for is not coming. 241*b5dcd8f9SAlexander Leidingersvcj_wait_file() 242*b5dcd8f9SAlexander Leidinger{ 243*b5dcd8f9SAlexander Leidinger local i 244*b5dcd8f9SAlexander Leidinger 245*b5dcd8f9SAlexander Leidinger i=0 246*b5dcd8f9SAlexander Leidinger while [ $i -lt 300 ]; do 247*b5dcd8f9SAlexander Leidinger [ -s "$1" ] && return 0 248*b5dcd8f9SAlexander Leidinger sleep 0.1 249*b5dcd8f9SAlexander Leidinger i=$((i + 1)) 250*b5dcd8f9SAlexander Leidinger done 251*b5dcd8f9SAlexander Leidinger return 1 252*b5dcd8f9SAlexander Leidinger} 253*b5dcd8f9SAlexander Leidinger 254*b5dcd8f9SAlexander Leidinger# svcj_gone <pid> 255*b5dcd8f9SAlexander Leidinger# True once the pid is no longer alive. 256*b5dcd8f9SAlexander Leidingersvcj_gone() 257*b5dcd8f9SAlexander Leidinger{ 258*b5dcd8f9SAlexander Leidinger local i 259*b5dcd8f9SAlexander Leidinger 260*b5dcd8f9SAlexander Leidinger i=0 261*b5dcd8f9SAlexander Leidinger while [ $i -lt 300 ]; do 262*b5dcd8f9SAlexander Leidinger kill -0 "$1" 2>/dev/null || return 0 263*b5dcd8f9SAlexander Leidinger sleep 0.1 264*b5dcd8f9SAlexander Leidinger i=$((i + 1)) 265*b5dcd8f9SAlexander Leidinger done 266*b5dcd8f9SAlexander Leidinger return 1 267*b5dcd8f9SAlexander Leidinger} 268*b5dcd8f9SAlexander Leidinger 269*b5dcd8f9SAlexander Leidingersvcj_cleanup() 270*b5dcd8f9SAlexander Leidinger{ 271*b5dcd8f9SAlexander Leidinger local svc c pid junk 272*b5dcd8f9SAlexander Leidinger 273*b5dcd8f9SAlexander Leidinger [ -f "$(pwd)/svcj.name" ] || return 0 274*b5dcd8f9SAlexander Leidinger read svc junk < "$(pwd)/svcj.name" 275*b5dcd8f9SAlexander Leidinger c=$(svcj_chroot) 276*b5dcd8f9SAlexander Leidinger 277*b5dcd8f9SAlexander Leidinger jail -R "svcj-$svc" 2>/dev/null 278*b5dcd8f9SAlexander Leidinger if [ -f "$c/var/run/svcjt.$svc/$svc.pid" ]; then 279*b5dcd8f9SAlexander Leidinger read pid junk < "$c/var/run/svcjt.$svc/$svc.pid" 280*b5dcd8f9SAlexander Leidinger if [ -n "$pid" ]; then 281*b5dcd8f9SAlexander Leidinger kill -9 "$pid" 2>/dev/null 282*b5dcd8f9SAlexander Leidinger svcj_gone "$pid" 283*b5dcd8f9SAlexander Leidinger fi 284*b5dcd8f9SAlexander Leidinger fi 285*b5dcd8f9SAlexander Leidinger 286*b5dcd8f9SAlexander Leidinger if svcj_umount; then 287*b5dcd8f9SAlexander Leidinger rm -rf "$c" 288*b5dcd8f9SAlexander Leidinger else 289*b5dcd8f9SAlexander Leidinger # Leave the tree alone while anything is still mounted under 290*b5dcd8f9SAlexander Leidinger # it. Kyua's own work directory cleanup does not unmount 291*b5dcd8f9SAlexander Leidinger # either, so a recursive delete here would run through the 292*b5dcd8f9SAlexander Leidinger # mount. 293*b5dcd8f9SAlexander Leidinger echo "svcj_test: still mounted under $c, not removing it" >&2 294*b5dcd8f9SAlexander Leidinger mount | grep -F " on $c" >&2 295*b5dcd8f9SAlexander Leidinger return 1 296*b5dcd8f9SAlexander Leidinger fi 297*b5dcd8f9SAlexander Leidinger} 298*b5dcd8f9SAlexander Leidinger 299*b5dcd8f9SAlexander Leidingeratf_test_case start_stop cleanup 300*b5dcd8f9SAlexander Leidingerstart_stop_head() 301*b5dcd8f9SAlexander Leidinger{ 302*b5dcd8f9SAlexander Leidinger atf_set "descr" "A service with \${name}_svcj=YES starts inside" \ 303*b5dcd8f9SAlexander Leidinger "svcj-\${name}, and stop removes both the process and the jail" 304*b5dcd8f9SAlexander Leidinger atf_set "require.user" "root" 305*b5dcd8f9SAlexander Leidinger} 306*b5dcd8f9SAlexander Leidingerstart_stop_body() 307*b5dcd8f9SAlexander Leidinger{ 308*b5dcd8f9SAlexander Leidinger local svc c rcs jid pid 309*b5dcd8f9SAlexander Leidinger 310*b5dcd8f9SAlexander Leidinger svcj_require 311*b5dcd8f9SAlexander Leidinger svc=$(svcj_svcname) 312*b5dcd8f9SAlexander Leidinger 313*b5dcd8f9SAlexander Leidinger svcj_fixture <<-EOF 314*b5dcd8f9SAlexander Leidinger $(svcj_daemon_lines) 315*b5dcd8f9SAlexander Leidinger ${svc}_svcj="YES" 316*b5dcd8f9SAlexander Leidinger EOF 317*b5dcd8f9SAlexander Leidinger c=$(svcj_chroot) 318*b5dcd8f9SAlexander Leidinger rcs=$(svcj_rcpath) 319*b5dcd8f9SAlexander Leidinger 320*b5dcd8f9SAlexander Leidinger atf_check -s exit:0 -o ignore -e ignore /usr/sbin/chroot "$c" "$rcs" \ 321*b5dcd8f9SAlexander Leidinger onestart 322*b5dcd8f9SAlexander Leidinger 323*b5dcd8f9SAlexander Leidinger jid=$(svcj_jid) 324*b5dcd8f9SAlexander Leidinger [ -n "$jid" ] || atf_fail "no service jail svcj-$svc was created" 325*b5dcd8f9SAlexander Leidinger pid=$(svcj_pid) || atf_fail "the daemon wrote no pid file" 326*b5dcd8f9SAlexander Leidinger 327*b5dcd8f9SAlexander Leidinger # The daemon really is in that jail, and not merely on the host. 328*b5dcd8f9SAlexander Leidinger atf_check -o inline:"$jid\n" \ 329*b5dcd8f9SAlexander Leidinger /bin/sh -c "ps -o jid= -p $pid | tr -d ' '" 330*b5dcd8f9SAlexander Leidinger 331*b5dcd8f9SAlexander Leidinger # And that jail is rooted at this case's chroot. 332*b5dcd8f9SAlexander Leidinger atf_check -o inline:"$c\n" jls -j "svcj-$svc" path 333*b5dcd8f9SAlexander Leidinger 334*b5dcd8f9SAlexander Leidinger atf_check -s exit:0 -o ignore -e ignore /usr/sbin/chroot "$c" "$rcs" \ 335*b5dcd8f9SAlexander Leidinger onestop 336*b5dcd8f9SAlexander Leidinger svcj_gone "$pid" || atf_fail "the daemon survived stop" 337*b5dcd8f9SAlexander Leidinger [ -z "$(svcj_jid)" ] || atf_fail "svcj-$svc survived stop" 338*b5dcd8f9SAlexander Leidinger} 339*b5dcd8f9SAlexander Leidingerstart_stop_cleanup() 340*b5dcd8f9SAlexander Leidinger{ 341*b5dcd8f9SAlexander Leidinger svcj_cleanup 342*b5dcd8f9SAlexander Leidinger} 343*b5dcd8f9SAlexander Leidinger 344*b5dcd8f9SAlexander Leidingeratf_test_case all_enable cleanup 345*b5dcd8f9SAlexander Leidingerall_enable_head() 346*b5dcd8f9SAlexander Leidinger{ 347*b5dcd8f9SAlexander Leidinger atf_set "descr" "svcj_all_enable=YES puts a service that has no" \ 348*b5dcd8f9SAlexander Leidinger "\${name}_svcj of its own into a service jail" 349*b5dcd8f9SAlexander Leidinger atf_set "require.user" "root" 350*b5dcd8f9SAlexander Leidinger} 351*b5dcd8f9SAlexander Leidingerall_enable_body() 352*b5dcd8f9SAlexander Leidinger{ 353*b5dcd8f9SAlexander Leidinger local svc c rcs 354*b5dcd8f9SAlexander Leidinger 355*b5dcd8f9SAlexander Leidinger svcj_require 356*b5dcd8f9SAlexander Leidinger svc=$(svcj_svcname) 357*b5dcd8f9SAlexander Leidinger 358*b5dcd8f9SAlexander Leidinger svcj_fixture <<-EOF 359*b5dcd8f9SAlexander Leidinger $(svcj_daemon_lines) 360*b5dcd8f9SAlexander Leidinger svcj_all_enable="YES" 361*b5dcd8f9SAlexander Leidinger EOF 362*b5dcd8f9SAlexander Leidinger c=$(svcj_chroot) 363*b5dcd8f9SAlexander Leidinger rcs=$(svcj_rcpath) 364*b5dcd8f9SAlexander Leidinger 365*b5dcd8f9SAlexander Leidinger atf_check -s exit:0 -o ignore -e ignore /usr/sbin/chroot "$c" "$rcs" \ 366*b5dcd8f9SAlexander Leidinger onestart 367*b5dcd8f9SAlexander Leidinger 368*b5dcd8f9SAlexander Leidinger [ -n "$(svcj_jid)" ] || atf_fail "no service jail svcj-$svc" 369*b5dcd8f9SAlexander Leidinger} 370*b5dcd8f9SAlexander Leidingerall_enable_cleanup() 371*b5dcd8f9SAlexander Leidinger{ 372*b5dcd8f9SAlexander Leidinger svcj_cleanup 373*b5dcd8f9SAlexander Leidinger} 374*b5dcd8f9SAlexander Leidinger 375*b5dcd8f9SAlexander Leidingeratf_test_case all_enable_quiet cleanup 376*b5dcd8f9SAlexander Leidingerall_enable_quiet_head() 377*b5dcd8f9SAlexander Leidinger{ 378*b5dcd8f9SAlexander Leidinger atf_set "descr" "svcj_all_enable=YES does not make rc.subr complain" \ 379*b5dcd8f9SAlexander Leidinger "about an unset \${name}_svcj" 380*b5dcd8f9SAlexander Leidinger atf_set "require.user" "root" 381*b5dcd8f9SAlexander Leidinger} 382*b5dcd8f9SAlexander Leidingerall_enable_quiet_body() 383*b5dcd8f9SAlexander Leidinger{ 384*b5dcd8f9SAlexander Leidinger local svc c rcs 385*b5dcd8f9SAlexander Leidinger 386*b5dcd8f9SAlexander Leidinger svcj_require 387*b5dcd8f9SAlexander Leidinger svc=$(svcj_svcname) 388*b5dcd8f9SAlexander Leidinger 389*b5dcd8f9SAlexander Leidinger svcj_fixture <<-EOF 390*b5dcd8f9SAlexander Leidinger $(svcj_daemon_lines) 391*b5dcd8f9SAlexander Leidinger svcj_all_enable="YES" 392*b5dcd8f9SAlexander Leidinger EOF 393*b5dcd8f9SAlexander Leidinger c=$(svcj_chroot) 394*b5dcd8f9SAlexander Leidinger rcs=$(svcj_rcpath) 395*b5dcd8f9SAlexander Leidinger 396*b5dcd8f9SAlexander Leidinger # The warning is the visible half of the defect above, and the half 397*b5dcd8f9SAlexander Leidinger # an administrator sees once per service on every boot. 398*b5dcd8f9SAlexander Leidinger atf_check -s exit:0 -o ignore -e not-match:'is not set properly' \ 399*b5dcd8f9SAlexander Leidinger /usr/sbin/chroot "$c" "$rcs" onestart 400*b5dcd8f9SAlexander Leidinger} 401*b5dcd8f9SAlexander Leidingerall_enable_quiet_cleanup() 402*b5dcd8f9SAlexander Leidinger{ 403*b5dcd8f9SAlexander Leidinger svcj_cleanup 404*b5dcd8f9SAlexander Leidinger} 405*b5dcd8f9SAlexander Leidinger 406*b5dcd8f9SAlexander Leidingeratf_test_case all_enable_rcconfd cleanup 407*b5dcd8f9SAlexander Leidingerall_enable_rcconfd_head() 408*b5dcd8f9SAlexander Leidinger{ 409*b5dcd8f9SAlexander Leidinger atf_set "descr" "svcj_all_enable=YES set in a configuration file" \ 410*b5dcd8f9SAlexander Leidinger "enables a service jail" 411*b5dcd8f9SAlexander Leidinger atf_set "require.user" "root" 412*b5dcd8f9SAlexander Leidinger} 413*b5dcd8f9SAlexander Leidingerall_enable_rcconfd_body() 414*b5dcd8f9SAlexander Leidinger{ 415*b5dcd8f9SAlexander Leidinger local svc c rcs 416*b5dcd8f9SAlexander Leidinger 417*b5dcd8f9SAlexander Leidinger svcj_require 418*b5dcd8f9SAlexander Leidinger svc=$(svcj_svcname) 419*b5dcd8f9SAlexander Leidinger 420*b5dcd8f9SAlexander Leidinger svcj_fixture <<-EOF 421*b5dcd8f9SAlexander Leidinger $(svcj_daemon_lines) 422*b5dcd8f9SAlexander Leidinger EOF 423*b5dcd8f9SAlexander Leidinger c=$(svcj_chroot) 424*b5dcd8f9SAlexander Leidinger rcs=$(svcj_rcpath) 425*b5dcd8f9SAlexander Leidinger 426*b5dcd8f9SAlexander Leidinger # The other svcj_all_enable cases set the variable inside the rc.d 427*b5dcd8f9SAlexander Leidinger # script. This one goes through load_rc_config and rc.conf.d. 428*b5dcd8f9SAlexander Leidinger echo 'svcj_all_enable="YES"' > "$c/etc/rc.conf.d/$svc" 429*b5dcd8f9SAlexander Leidinger 430*b5dcd8f9SAlexander Leidinger atf_check -s exit:0 -o ignore -e ignore /usr/sbin/chroot "$c" "$rcs" \ 431*b5dcd8f9SAlexander Leidinger onestart 432*b5dcd8f9SAlexander Leidinger 433*b5dcd8f9SAlexander Leidinger [ -n "$(svcj_jid)" ] || atf_fail "no service jail svcj-$svc" 434*b5dcd8f9SAlexander Leidinger} 435*b5dcd8f9SAlexander Leidingerall_enable_rcconfd_cleanup() 436*b5dcd8f9SAlexander Leidinger{ 437*b5dcd8f9SAlexander Leidinger svcj_cleanup 438*b5dcd8f9SAlexander Leidinger} 439*b5dcd8f9SAlexander Leidinger 440*b5dcd8f9SAlexander Leidingeratf_test_case stop_as_user cleanup 441*b5dcd8f9SAlexander Leidingerstop_as_user_head() 442*b5dcd8f9SAlexander Leidinger{ 443*b5dcd8f9SAlexander Leidinger atf_set "descr" "stop terminates the process of a service jail whose" \ 444*b5dcd8f9SAlexander Leidinger "\${name}_user is set" 445*b5dcd8f9SAlexander Leidinger atf_set "require.user" "root" 446*b5dcd8f9SAlexander Leidinger} 447*b5dcd8f9SAlexander Leidingerstop_as_user_body() 448*b5dcd8f9SAlexander Leidinger{ 449*b5dcd8f9SAlexander Leidinger local svc c rcs pid 450*b5dcd8f9SAlexander Leidinger 451*b5dcd8f9SAlexander Leidinger svcj_require 452*b5dcd8f9SAlexander Leidinger svc=$(svcj_svcname) 453*b5dcd8f9SAlexander Leidinger 454*b5dcd8f9SAlexander Leidinger svcj_fixture <<-EOF 455*b5dcd8f9SAlexander Leidinger $(svcj_daemon_lines) 456*b5dcd8f9SAlexander Leidinger ${svc}_svcj="YES" 457*b5dcd8f9SAlexander Leidinger ${svc}_user="nobody" 458*b5dcd8f9SAlexander Leidinger EOF 459*b5dcd8f9SAlexander Leidinger c=$(svcj_chroot) 460*b5dcd8f9SAlexander Leidinger rcs=$(svcj_rcpath) 461*b5dcd8f9SAlexander Leidinger 462*b5dcd8f9SAlexander Leidinger atf_check -s exit:0 -o ignore -e ignore /usr/sbin/chroot "$c" "$rcs" \ 463*b5dcd8f9SAlexander Leidinger onestart 464*b5dcd8f9SAlexander Leidinger pid=$(svcj_pid) || atf_fail "the daemon wrote no pid file" 465*b5dcd8f9SAlexander Leidinger atf_check -o inline:"nobody\n" \ 466*b5dcd8f9SAlexander Leidinger /bin/sh -c "ps -o user= -p $pid | tr -d ' '" 467*b5dcd8f9SAlexander Leidinger 468*b5dcd8f9SAlexander Leidinger # The signal is sent from the host, as ${name}_user, against a process 469*b5dcd8f9SAlexander Leidinger # in a subordinate jail. Since 8a5ceebece03 that needs either 470*b5dcd8f9SAlexander Leidinger # PRIV_SIGNAL_DIFFJAIL or allow.unprivileged_parent_tampering on the 471*b5dcd8f9SAlexander Leidinger # target jail, and a service jail is created with neither. 472*b5dcd8f9SAlexander Leidinger atf_check -s exit:0 -o ignore -e ignore /usr/sbin/chroot "$c" "$rcs" \ 473*b5dcd8f9SAlexander Leidinger onestop 474*b5dcd8f9SAlexander Leidinger svcj_gone "$pid" || atf_fail "the daemon survived stop" 475*b5dcd8f9SAlexander Leidinger [ -z "$(svcj_jid)" ] || atf_fail "svcj-$svc survived stop" 476*b5dcd8f9SAlexander Leidinger} 477*b5dcd8f9SAlexander Leidingerstop_as_user_cleanup() 478*b5dcd8f9SAlexander Leidinger{ 479*b5dcd8f9SAlexander Leidinger svcj_cleanup 480*b5dcd8f9SAlexander Leidinger} 481*b5dcd8f9SAlexander Leidinger 482*b5dcd8f9SAlexander Leidingeratf_test_case svcj_toggle_after_stop cleanup 483*b5dcd8f9SAlexander Leidingersvcj_toggle_after_stop_head() 484*b5dcd8f9SAlexander Leidinger{ 485*b5dcd8f9SAlexander Leidinger atf_set "descr" "\${name}_svcj may be changed between a stop and the" \ 486*b5dcd8f9SAlexander Leidinger "next start, and the service ends up jailed" 487*b5dcd8f9SAlexander Leidinger atf_set "require.user" "root" 488*b5dcd8f9SAlexander Leidinger} 489*b5dcd8f9SAlexander Leidingersvcj_toggle_after_stop_body() 490*b5dcd8f9SAlexander Leidinger{ 491*b5dcd8f9SAlexander Leidinger local svc c rcs pid 492*b5dcd8f9SAlexander Leidinger 493*b5dcd8f9SAlexander Leidinger svcj_require 494*b5dcd8f9SAlexander Leidinger svc=$(svcj_svcname) 495*b5dcd8f9SAlexander Leidinger 496*b5dcd8f9SAlexander Leidinger # ${name}_svcj is only meaningful for a service that is not running: 497*b5dcd8f9SAlexander Leidinger # it decides where the next start puts the process, and stop reads it 498*b5dcd8f9SAlexander Leidinger # again to decide where to send the signal. Changing it underneath a 499*b5dcd8f9SAlexander Leidinger # running service therefore has no supported answer, and this case 500*b5dcd8f9SAlexander Leidinger # pins the sequence that does -- stop, change, start. 501*b5dcd8f9SAlexander Leidinger svcj_fixture <<-EOF 502*b5dcd8f9SAlexander Leidinger $(svcj_daemon_lines) 503*b5dcd8f9SAlexander Leidinger ${svc}_svcj="NO" 504*b5dcd8f9SAlexander Leidinger EOF 505*b5dcd8f9SAlexander Leidinger c=$(svcj_chroot) 506*b5dcd8f9SAlexander Leidinger rcs=$(svcj_rcpath) 507*b5dcd8f9SAlexander Leidinger 508*b5dcd8f9SAlexander Leidinger atf_check -s exit:0 -o ignore -e ignore /usr/sbin/chroot "$c" "$rcs" \ 509*b5dcd8f9SAlexander Leidinger onestart 510*b5dcd8f9SAlexander Leidinger pid=$(svcj_pid) || atf_fail "the daemon wrote no pid file" 511*b5dcd8f9SAlexander Leidinger [ -z "$(svcj_jid)" ] || 512*b5dcd8f9SAlexander Leidinger atf_fail "a jail was created although ${svc}_svcj was NO" 513*b5dcd8f9SAlexander Leidinger 514*b5dcd8f9SAlexander Leidinger atf_check -s exit:0 -o ignore -e ignore /usr/sbin/chroot "$c" "$rcs" \ 515*b5dcd8f9SAlexander Leidinger onestop 516*b5dcd8f9SAlexander Leidinger svcj_gone "$pid" || atf_fail "the unjailed daemon survived stop" 517*b5dcd8f9SAlexander Leidinger 518*b5dcd8f9SAlexander Leidinger atf_check -s exit:0 sed -i "" "s/${svc}_svcj=\"NO\"/${svc}_svcj=\"YES\"/" \ 519*b5dcd8f9SAlexander Leidinger "$(svcj_hostrc)" 520*b5dcd8f9SAlexander Leidinger rm -f "$(svcj_hostdir)/$svc.pid" 521*b5dcd8f9SAlexander Leidinger 522*b5dcd8f9SAlexander Leidinger atf_check -s exit:0 -o ignore -e ignore /usr/sbin/chroot "$c" "$rcs" \ 523*b5dcd8f9SAlexander Leidinger onestart 524*b5dcd8f9SAlexander Leidinger pid=$(svcj_pid) || atf_fail "the daemon wrote no pid file" 525*b5dcd8f9SAlexander Leidinger [ -n "$(svcj_jid)" ] || 526*b5dcd8f9SAlexander Leidinger atf_fail "no jail although ${svc}_svcj is now YES" 527*b5dcd8f9SAlexander Leidinger 528*b5dcd8f9SAlexander Leidinger atf_check -s exit:0 -o ignore -e ignore /usr/sbin/chroot "$c" "$rcs" \ 529*b5dcd8f9SAlexander Leidinger onestop 530*b5dcd8f9SAlexander Leidinger svcj_gone "$pid" || atf_fail "the jailed daemon survived stop" 531*b5dcd8f9SAlexander Leidinger [ -z "$(svcj_jid)" ] || atf_fail "svcj-$svc survived stop" 532*b5dcd8f9SAlexander Leidinger} 533*b5dcd8f9SAlexander Leidingersvcj_toggle_after_stop_cleanup() 534*b5dcd8f9SAlexander Leidinger{ 535*b5dcd8f9SAlexander Leidinger svcj_cleanup 536*b5dcd8f9SAlexander Leidinger} 537*b5dcd8f9SAlexander Leidinger 538*b5dcd8f9SAlexander Leidingeratf_test_case reload_as_user cleanup 539*b5dcd8f9SAlexander Leidingerreload_as_user_head() 540*b5dcd8f9SAlexander Leidinger{ 541*b5dcd8f9SAlexander Leidinger atf_set "descr" "reload signals the process of a service jail whose" \ 542*b5dcd8f9SAlexander Leidinger "\${name}_user is set" 543*b5dcd8f9SAlexander Leidinger atf_set "require.user" "root" 544*b5dcd8f9SAlexander Leidinger} 545*b5dcd8f9SAlexander Leidingerreload_as_user_body() 546*b5dcd8f9SAlexander Leidinger{ 547*b5dcd8f9SAlexander Leidinger local svc c rcs pid 548*b5dcd8f9SAlexander Leidinger 549*b5dcd8f9SAlexander Leidinger svcj_require 550*b5dcd8f9SAlexander Leidinger svc=$(svcj_svcname) 551*b5dcd8f9SAlexander Leidinger 552*b5dcd8f9SAlexander Leidinger svcj_fixture <<-EOF 553*b5dcd8f9SAlexander Leidinger $(svcj_daemon_lines) 554*b5dcd8f9SAlexander Leidinger extra_commands="reload" 555*b5dcd8f9SAlexander Leidinger ${svc}_svcj="YES" 556*b5dcd8f9SAlexander Leidinger ${svc}_user="nobody" 557*b5dcd8f9SAlexander Leidinger EOF 558*b5dcd8f9SAlexander Leidinger c=$(svcj_chroot) 559*b5dcd8f9SAlexander Leidinger rcs=$(svcj_rcpath) 560*b5dcd8f9SAlexander Leidinger 561*b5dcd8f9SAlexander Leidinger atf_check -s exit:0 -o ignore -e ignore /usr/sbin/chroot "$c" "$rcs" \ 562*b5dcd8f9SAlexander Leidinger onestart 563*b5dcd8f9SAlexander Leidinger pid=$(svcj_pid) || atf_fail "the daemon wrote no pid file" 564*b5dcd8f9SAlexander Leidinger 565*b5dcd8f9SAlexander Leidinger # reload builds its signal with the same helper as stop and fails for 566*b5dcd8f9SAlexander Leidinger # the same reason. sleep(1) has no handler for SIGHUP, so the process 567*b5dcd8f9SAlexander Leidinger # going away is the evidence that the signal was delivered. 568*b5dcd8f9SAlexander Leidinger atf_check -s exit:0 -o ignore -e ignore /usr/sbin/chroot "$c" "$rcs" \ 569*b5dcd8f9SAlexander Leidinger onereload 570*b5dcd8f9SAlexander Leidinger svcj_gone "$pid" || atf_fail "the daemon did not receive the signal" 571*b5dcd8f9SAlexander Leidinger} 572*b5dcd8f9SAlexander Leidingerreload_as_user_cleanup() 573*b5dcd8f9SAlexander Leidinger{ 574*b5dcd8f9SAlexander Leidinger svcj_cleanup 575*b5dcd8f9SAlexander Leidinger} 576*b5dcd8f9SAlexander Leidinger 577*b5dcd8f9SAlexander Leidingeratf_test_case restart_cmd cleanup 578*b5dcd8f9SAlexander Leidingerrestart_cmd_head() 579*b5dcd8f9SAlexander Leidinger{ 580*b5dcd8f9SAlexander Leidinger atf_set "descr" "A service jail's own restart_cmd is executed" 581*b5dcd8f9SAlexander Leidinger atf_set "require.user" "root" 582*b5dcd8f9SAlexander Leidinger} 583*b5dcd8f9SAlexander Leidingerrestart_cmd_body() 584*b5dcd8f9SAlexander Leidinger{ 585*b5dcd8f9SAlexander Leidinger local svc c rcs wd 586*b5dcd8f9SAlexander Leidinger 587*b5dcd8f9SAlexander Leidinger svcj_require 588*b5dcd8f9SAlexander Leidinger svc=$(svcj_svcname) 589*b5dcd8f9SAlexander Leidinger wd=$(svcj_workdir) 590*b5dcd8f9SAlexander Leidinger 591*b5dcd8f9SAlexander Leidinger svcj_fixture <<-EOF 592*b5dcd8f9SAlexander Leidinger $(svcj_daemon_lines) 593*b5dcd8f9SAlexander Leidinger ${svc}_svcj="YES" 594*b5dcd8f9SAlexander Leidinger restart_cmd="${svc}_restart" 595*b5dcd8f9SAlexander Leidinger ${svc}_restart() 596*b5dcd8f9SAlexander Leidinger { 597*b5dcd8f9SAlexander Leidinger : > "$wd/restart.ran" 598*b5dcd8f9SAlexander Leidinger } 599*b5dcd8f9SAlexander Leidinger EOF 600*b5dcd8f9SAlexander Leidinger c=$(svcj_chroot) 601*b5dcd8f9SAlexander Leidinger rcs=$(svcj_rcpath) 602*b5dcd8f9SAlexander Leidinger 603*b5dcd8f9SAlexander Leidinger atf_check -s exit:0 -o ignore -e ignore /usr/sbin/chroot "$c" "$rcs" \ 604*b5dcd8f9SAlexander Leidinger onestart 605*b5dcd8f9SAlexander Leidinger 606*b5dcd8f9SAlexander Leidinger # A service jail's custom methods are dispatched through a case 607*b5dcd8f9SAlexander Leidinger # statement whose restart branch is empty, so the method is skipped 608*b5dcd8f9SAlexander Leidinger # and the exit status is still zero. 609*b5dcd8f9SAlexander Leidinger atf_check -s exit:0 -o ignore -e ignore /usr/sbin/chroot "$c" "$rcs" \ 610*b5dcd8f9SAlexander Leidinger onerestart 611*b5dcd8f9SAlexander Leidinger [ -f "$(svcj_hostdir)/restart.ran" ] || 612*b5dcd8f9SAlexander Leidinger atf_fail "restart_cmd was not run" 613*b5dcd8f9SAlexander Leidinger} 614*b5dcd8f9SAlexander Leidingerrestart_cmd_cleanup() 615*b5dcd8f9SAlexander Leidinger{ 616*b5dcd8f9SAlexander Leidinger svcj_cleanup 617*b5dcd8f9SAlexander Leidinger} 618*b5dcd8f9SAlexander Leidinger 619*b5dcd8f9SAlexander Leidingeratf_test_case status_cmd cleanup 620*b5dcd8f9SAlexander Leidingerstatus_cmd_head() 621*b5dcd8f9SAlexander Leidinger{ 622*b5dcd8f9SAlexander Leidinger atf_set "descr" "A service jail's own status_cmd is executed" 623*b5dcd8f9SAlexander Leidinger atf_set "require.user" "root" 624*b5dcd8f9SAlexander Leidinger} 625*b5dcd8f9SAlexander Leidingerstatus_cmd_body() 626*b5dcd8f9SAlexander Leidinger{ 627*b5dcd8f9SAlexander Leidinger local svc c rcs wd 628*b5dcd8f9SAlexander Leidinger 629*b5dcd8f9SAlexander Leidinger svcj_require 630*b5dcd8f9SAlexander Leidinger svc=$(svcj_svcname) 631*b5dcd8f9SAlexander Leidinger wd=$(svcj_workdir) 632*b5dcd8f9SAlexander Leidinger 633*b5dcd8f9SAlexander Leidinger svcj_fixture <<-EOF 634*b5dcd8f9SAlexander Leidinger $(svcj_daemon_lines) 635*b5dcd8f9SAlexander Leidinger ${svc}_svcj="YES" 636*b5dcd8f9SAlexander Leidinger status_cmd="${svc}_status" 637*b5dcd8f9SAlexander Leidinger ${svc}_status() 638*b5dcd8f9SAlexander Leidinger { 639*b5dcd8f9SAlexander Leidinger : > "$wd/status.ran" 640*b5dcd8f9SAlexander Leidinger } 641*b5dcd8f9SAlexander Leidinger EOF 642*b5dcd8f9SAlexander Leidinger c=$(svcj_chroot) 643*b5dcd8f9SAlexander Leidinger rcs=$(svcj_rcpath) 644*b5dcd8f9SAlexander Leidinger 645*b5dcd8f9SAlexander Leidinger atf_check -s exit:0 -o ignore -e ignore /usr/sbin/chroot "$c" "$rcs" \ 646*b5dcd8f9SAlexander Leidinger onestart 647*b5dcd8f9SAlexander Leidinger 648*b5dcd8f9SAlexander Leidinger atf_check -s exit:0 -o ignore -e ignore /usr/sbin/chroot "$c" "$rcs" \ 649*b5dcd8f9SAlexander Leidinger onestatus 650*b5dcd8f9SAlexander Leidinger [ -f "$(svcj_hostdir)/status.ran" ] || 651*b5dcd8f9SAlexander Leidinger atf_fail "status_cmd was not run" 652*b5dcd8f9SAlexander Leidinger} 653*b5dcd8f9SAlexander Leidingerstatus_cmd_cleanup() 654*b5dcd8f9SAlexander Leidinger{ 655*b5dcd8f9SAlexander Leidinger svcj_cleanup 656*b5dcd8f9SAlexander Leidinger} 657*b5dcd8f9SAlexander Leidinger 658*b5dcd8f9SAlexander Leidingeratf_test_case restart_cmd_when_stopped cleanup 659*b5dcd8f9SAlexander Leidingerrestart_cmd_when_stopped_head() 660*b5dcd8f9SAlexander Leidinger{ 661*b5dcd8f9SAlexander Leidinger atf_set "descr" "restart on a stopped service that has its own" \ 662*b5dcd8f9SAlexander Leidinger "restart_cmd starts it, rather than failing in the absent jail" 663*b5dcd8f9SAlexander Leidinger atf_set "require.user" "root" 664*b5dcd8f9SAlexander Leidinger} 665*b5dcd8f9SAlexander Leidingerrestart_cmd_when_stopped_body() 666*b5dcd8f9SAlexander Leidinger{ 667*b5dcd8f9SAlexander Leidinger local svc c rcs wd jid pid 668*b5dcd8f9SAlexander Leidinger 669*b5dcd8f9SAlexander Leidinger svcj_require 670*b5dcd8f9SAlexander Leidinger svc=$(svcj_svcname) 671*b5dcd8f9SAlexander Leidinger wd=$(svcj_workdir) 672*b5dcd8f9SAlexander Leidinger 673*b5dcd8f9SAlexander Leidinger svcj_fixture <<-EOF 674*b5dcd8f9SAlexander Leidinger $(svcj_daemon_lines) 675*b5dcd8f9SAlexander Leidinger ${svc}_svcj="YES" 676*b5dcd8f9SAlexander Leidinger restart_cmd="${svc}_restart" 677*b5dcd8f9SAlexander Leidinger ${svc}_restart() 678*b5dcd8f9SAlexander Leidinger { 679*b5dcd8f9SAlexander Leidinger : > "$wd/restart.ran" 680*b5dcd8f9SAlexander Leidinger } 681*b5dcd8f9SAlexander Leidinger EOF 682*b5dcd8f9SAlexander Leidinger c=$(svcj_chroot) 683*b5dcd8f9SAlexander Leidinger rcs=$(svcj_rcpath) 684*b5dcd8f9SAlexander Leidinger 685*b5dcd8f9SAlexander Leidinger # Nothing is running, so there is no service jail to enter. A 686*b5dcd8f9SAlexander Leidinger # restart is a stop and a start, and only the start half has 687*b5dcd8f9SAlexander Leidinger # anything to do. 688*b5dcd8f9SAlexander Leidinger atf_check -s exit:0 -o ignore -e ignore /usr/sbin/chroot "$c" "$rcs" \ 689*b5dcd8f9SAlexander Leidinger onerestart 690*b5dcd8f9SAlexander Leidinger 691*b5dcd8f9SAlexander Leidinger jid=$(svcj_jid) 692*b5dcd8f9SAlexander Leidinger [ -n "$jid" ] || atf_fail "restart did not create svcj-$svc" 693*b5dcd8f9SAlexander Leidinger pid=$(svcj_pid) || atf_fail "restart did not start the daemon" 694*b5dcd8f9SAlexander Leidinger atf_check -o inline:"$jid\n" \ 695*b5dcd8f9SAlexander Leidinger /bin/sh -c "ps -o jid= -p $pid | tr -d ' '" 696*b5dcd8f9SAlexander Leidinger 697*b5dcd8f9SAlexander Leidinger # The script's own method is for restarting something that runs. 698*b5dcd8f9SAlexander Leidinger if [ -f "$(svcj_hostdir)/restart.ran" ]; then 699*b5dcd8f9SAlexander Leidinger atf_fail "restart_cmd ran although nothing was running" 700*b5dcd8f9SAlexander Leidinger fi 701*b5dcd8f9SAlexander Leidinger} 702*b5dcd8f9SAlexander Leidingerrestart_cmd_when_stopped_cleanup() 703*b5dcd8f9SAlexander Leidinger{ 704*b5dcd8f9SAlexander Leidinger svcj_cleanup 705*b5dcd8f9SAlexander Leidinger} 706*b5dcd8f9SAlexander Leidinger 707*b5dcd8f9SAlexander Leidingeratf_test_case status_cmd_when_stopped cleanup 708*b5dcd8f9SAlexander Leidingerstatus_cmd_when_stopped_head() 709*b5dcd8f9SAlexander Leidinger{ 710*b5dcd8f9SAlexander Leidinger atf_set "descr" "status on a stopped service that has its own" \ 711*b5dcd8f9SAlexander Leidinger "status_cmd fails and creates no service jail" 712*b5dcd8f9SAlexander Leidinger atf_set "require.user" "root" 713*b5dcd8f9SAlexander Leidinger} 714*b5dcd8f9SAlexander Leidingerstatus_cmd_when_stopped_body() 715*b5dcd8f9SAlexander Leidinger{ 716*b5dcd8f9SAlexander Leidinger local svc c rcs wd 717*b5dcd8f9SAlexander Leidinger 718*b5dcd8f9SAlexander Leidinger svcj_require 719*b5dcd8f9SAlexander Leidinger svc=$(svcj_svcname) 720*b5dcd8f9SAlexander Leidinger wd=$(svcj_workdir) 721*b5dcd8f9SAlexander Leidinger 722*b5dcd8f9SAlexander Leidinger svcj_fixture <<-EOF 723*b5dcd8f9SAlexander Leidinger $(svcj_daemon_lines) 724*b5dcd8f9SAlexander Leidinger ${svc}_svcj="YES" 725*b5dcd8f9SAlexander Leidinger status_cmd="${svc}_status" 726*b5dcd8f9SAlexander Leidinger ${svc}_status() 727*b5dcd8f9SAlexander Leidinger { 728*b5dcd8f9SAlexander Leidinger : > "$wd/status.ran" 729*b5dcd8f9SAlexander Leidinger } 730*b5dcd8f9SAlexander Leidinger EOF 731*b5dcd8f9SAlexander Leidinger c=$(svcj_chroot) 732*b5dcd8f9SAlexander Leidinger rcs=$(svcj_rcpath) 733*b5dcd8f9SAlexander Leidinger 734*b5dcd8f9SAlexander Leidinger # Unlike restart there is nothing else to do here: a service that is 735*b5dcd8f9SAlexander Leidinger # not running has no status to report from inside a jail that does 736*b5dcd8f9SAlexander Leidinger # not exist. 737*b5dcd8f9SAlexander Leidinger atf_check -s not-exit:0 -o ignore -e ignore /usr/sbin/chroot "$c" \ 738*b5dcd8f9SAlexander Leidinger "$rcs" onestatus 739*b5dcd8f9SAlexander Leidinger [ -z "$(svcj_jid)" ] || atf_fail "status created svcj-$svc" 740*b5dcd8f9SAlexander Leidinger if [ -f "$(svcj_hostdir)/status.ran" ]; then 741*b5dcd8f9SAlexander Leidinger atf_fail "status_cmd ran outside the service jail" 742*b5dcd8f9SAlexander Leidinger fi 743*b5dcd8f9SAlexander Leidinger} 744*b5dcd8f9SAlexander Leidingerstatus_cmd_when_stopped_cleanup() 745*b5dcd8f9SAlexander Leidinger{ 746*b5dcd8f9SAlexander Leidinger svcj_cleanup 747*b5dcd8f9SAlexander Leidinger} 748*b5dcd8f9SAlexander Leidinger 749*b5dcd8f9SAlexander Leidingeratf_test_case stop_removes_dead_jail cleanup 750*b5dcd8f9SAlexander Leidingerstop_removes_dead_jail_head() 751*b5dcd8f9SAlexander Leidinger{ 752*b5dcd8f9SAlexander Leidinger atf_set "descr" "A service jail is gone once its only process is," \ 753*b5dcd8f9SAlexander Leidinger "and stop then behaves like an unjailed service" 754*b5dcd8f9SAlexander Leidinger atf_set "require.user" "root" 755*b5dcd8f9SAlexander Leidinger} 756*b5dcd8f9SAlexander Leidingerstop_removes_dead_jail_body() 757*b5dcd8f9SAlexander Leidinger{ 758*b5dcd8f9SAlexander Leidinger local svc c rcs pid 759*b5dcd8f9SAlexander Leidinger 760*b5dcd8f9SAlexander Leidinger svcj_require 761*b5dcd8f9SAlexander Leidinger svc=$(svcj_svcname) 762*b5dcd8f9SAlexander Leidinger 763*b5dcd8f9SAlexander Leidinger svcj_fixture <<-EOF 764*b5dcd8f9SAlexander Leidinger $(svcj_daemon_lines) 765*b5dcd8f9SAlexander Leidinger ${svc}_svcj="YES" 766*b5dcd8f9SAlexander Leidinger EOF 767*b5dcd8f9SAlexander Leidinger c=$(svcj_chroot) 768*b5dcd8f9SAlexander Leidinger rcs=$(svcj_rcpath) 769*b5dcd8f9SAlexander Leidinger 770*b5dcd8f9SAlexander Leidinger atf_check -s exit:0 -o ignore -e ignore /usr/sbin/chroot "$c" "$rcs" \ 771*b5dcd8f9SAlexander Leidinger onestart 772*b5dcd8f9SAlexander Leidinger pid=$(svcj_pid) || atf_fail "the daemon wrote no pid file" 773*b5dcd8f9SAlexander Leidinger 774*b5dcd8f9SAlexander Leidinger # A daemon that died on its own. 775*b5dcd8f9SAlexander Leidinger atf_check -s exit:0 kill -9 "$pid" 776*b5dcd8f9SAlexander Leidinger svcj_gone "$pid" || atf_fail "the daemon could not be killed" 777*b5dcd8f9SAlexander Leidinger 778*b5dcd8f9SAlexander Leidinger # The jail is already gone at this point, and not because stop did 779*b5dcd8f9SAlexander Leidinger # anything: jail(8) clears the temporary "persist" parameter once 780*b5dcd8f9SAlexander Leidinger # exec.start has run, so a service jail is reaped together with its 781*b5dcd8f9SAlexander Leidinger # last process. stop then takes its "not running?" branch and exits 782*b5dcd8f9SAlexander Leidinger # 1, which is the same answer it gives for an unjailed service. 783*b5dcd8f9SAlexander Leidinger [ -z "$(svcj_jid)" ] || 784*b5dcd8f9SAlexander Leidinger atf_fail "svcj-$svc outlived its only process" 785*b5dcd8f9SAlexander Leidinger 786*b5dcd8f9SAlexander Leidinger atf_check -s exit:1 -o ignore -e ignore /usr/sbin/chroot "$c" "$rcs" \ 787*b5dcd8f9SAlexander Leidinger onestop 788*b5dcd8f9SAlexander Leidinger [ -z "$(svcj_jid)" ] || atf_fail "svcj-$svc survived stop" 789*b5dcd8f9SAlexander Leidinger} 790*b5dcd8f9SAlexander Leidingerstop_removes_dead_jail_cleanup() 791*b5dcd8f9SAlexander Leidinger{ 792*b5dcd8f9SAlexander Leidinger svcj_cleanup 793*b5dcd8f9SAlexander Leidinger} 794*b5dcd8f9SAlexander Leidinger 795*b5dcd8f9SAlexander Leidingeratf_test_case stop_orphan_jail_with_child cleanup 796*b5dcd8f9SAlexander Leidingerstop_orphan_jail_with_child_head() 797*b5dcd8f9SAlexander Leidinger{ 798*b5dcd8f9SAlexander Leidinger atf_set "descr" "stop removes the service jail when the process it" \ 799*b5dcd8f9SAlexander Leidinger "tracks is gone but another process is still in the jail" 800*b5dcd8f9SAlexander Leidinger atf_set "require.user" "root" 801*b5dcd8f9SAlexander Leidinger} 802*b5dcd8f9SAlexander Leidingerstop_orphan_jail_with_child_body() 803*b5dcd8f9SAlexander Leidinger{ 804*b5dcd8f9SAlexander Leidinger local svc c rcs wd pid 805*b5dcd8f9SAlexander Leidinger 806*b5dcd8f9SAlexander Leidinger svcj_require 807*b5dcd8f9SAlexander Leidinger svc=$(svcj_svcname) 808*b5dcd8f9SAlexander Leidinger wd=$(svcj_workdir) 809*b5dcd8f9SAlexander Leidinger 810*b5dcd8f9SAlexander Leidinger # The service leaves a second process behind in the jail, which is 811*b5dcd8f9SAlexander Leidinger # what an ordinary daemon with a worker or a helper does. Only the 812*b5dcd8f9SAlexander Leidinger # first one is in ${pidfile}. 813*b5dcd8f9SAlexander Leidinger svcj_fixture <<-EOF 814*b5dcd8f9SAlexander Leidinger command="/usr/sbin/daemon" 815*b5dcd8f9SAlexander Leidinger command_args="-p \$pidfile -- /bin/sh $wd/child.sh" 816*b5dcd8f9SAlexander Leidinger procname="/bin/sleep" 817*b5dcd8f9SAlexander Leidinger ${svc}_svcj="YES" 818*b5dcd8f9SAlexander Leidinger EOF 819*b5dcd8f9SAlexander Leidinger c=$(svcj_chroot) 820*b5dcd8f9SAlexander Leidinger rcs=$(svcj_rcpath) 821*b5dcd8f9SAlexander Leidinger cat > "$(svcj_hostdir)/child.sh" <<-EOF 822*b5dcd8f9SAlexander Leidinger sleep 600 >/dev/null 2>&1 & 823*b5dcd8f9SAlexander Leidinger echo started > $wd/child.started 824*b5dcd8f9SAlexander Leidinger exec sleep 300 825*b5dcd8f9SAlexander Leidinger EOF 826*b5dcd8f9SAlexander Leidinger 827*b5dcd8f9SAlexander Leidinger atf_check -s exit:0 -o ignore -e ignore /usr/sbin/chroot "$c" "$rcs" \ 828*b5dcd8f9SAlexander Leidinger onestart 829*b5dcd8f9SAlexander Leidinger pid=$(svcj_pid) || atf_fail "the daemon wrote no pid file" 830*b5dcd8f9SAlexander Leidinger 831*b5dcd8f9SAlexander Leidinger # ${pidfile} is written before the service has run, so the helper 832*b5dcd8f9SAlexander Leidinger # is not there yet. Killing the tracked process first would take 833*b5dcd8f9SAlexander Leidinger # the jail with it and measure the single-process case instead. 834*b5dcd8f9SAlexander Leidinger svcj_wait_file "$(svcj_hostdir)/child.started" || 835*b5dcd8f9SAlexander Leidinger atf_fail "the helper never started" 836*b5dcd8f9SAlexander Leidinger 837*b5dcd8f9SAlexander Leidinger atf_check -s exit:0 kill -9 "$pid" 838*b5dcd8f9SAlexander Leidinger svcj_gone "$pid" || atf_fail "the tracked process could not be killed" 839*b5dcd8f9SAlexander Leidinger 840*b5dcd8f9SAlexander Leidinger # Unlike the single-process case, the jail is still alive here: the 841*b5dcd8f9SAlexander Leidinger # helper holds it open. So this is the shape in which an orphaned 842*b5dcd8f9SAlexander Leidinger # service jail is actually reachable. 843*b5dcd8f9SAlexander Leidinger [ -n "$(svcj_jid)" ] || 844*b5dcd8f9SAlexander Leidinger atf_fail "the helper did not keep svcj-$svc alive; " \ 845*b5dcd8f9SAlexander Leidinger "this case is no longer testing what it says" 846*b5dcd8f9SAlexander Leidinger 847*b5dcd8f9SAlexander Leidinger /usr/sbin/chroot "$c" "$rcs" onestop >/dev/null 2>&1 848*b5dcd8f9SAlexander Leidinger 849*b5dcd8f9SAlexander Leidinger # stop returns from its "not running?" branch before reaching the 850*b5dcd8f9SAlexander Leidinger # jail removal, so the jail and its helper survive -- and the next 851*b5dcd8f9SAlexander Leidinger # start fails because svcj-${name} already exists. 852*b5dcd8f9SAlexander Leidinger [ -z "$(svcj_jid)" ] || atf_fail "svcj-$svc survived stop" 853*b5dcd8f9SAlexander Leidinger} 854*b5dcd8f9SAlexander Leidingerstop_orphan_jail_with_child_cleanup() 855*b5dcd8f9SAlexander Leidinger{ 856*b5dcd8f9SAlexander Leidinger svcj_cleanup 857*b5dcd8f9SAlexander Leidinger} 858*b5dcd8f9SAlexander Leidinger 859*b5dcd8f9SAlexander Leidingeratf_test_case audit_user cleanup 860*b5dcd8f9SAlexander Leidingeraudit_user_head() 861*b5dcd8f9SAlexander Leidinger{ 862*b5dcd8f9SAlexander Leidinger atf_set "descr" "\${name}_audit_user works for a service in a" \ 863*b5dcd8f9SAlexander Leidinger "service jail" 864*b5dcd8f9SAlexander Leidinger atf_set "require.user" "root" 865*b5dcd8f9SAlexander Leidinger} 866*b5dcd8f9SAlexander Leidingeraudit_user_body() 867*b5dcd8f9SAlexander Leidinger{ 868*b5dcd8f9SAlexander Leidinger local svc c rcs 869*b5dcd8f9SAlexander Leidinger 870*b5dcd8f9SAlexander Leidinger svcj_require 871*b5dcd8f9SAlexander Leidinger svc=$(svcj_svcname) 872*b5dcd8f9SAlexander Leidinger 873*b5dcd8f9SAlexander Leidinger # Control: the same setaudit(8) invocation has to work outside, or 874*b5dcd8f9SAlexander Leidinger # what the service jail does with it says nothing. Where it does not 875*b5dcd8f9SAlexander Leidinger # -- no audit support, or an enclosing jail without allow.setaudit -- 876*b5dcd8f9SAlexander Leidinger # it skips. 877*b5dcd8f9SAlexander Leidinger setaudit -U -a root /usr/bin/true 2>/dev/null || 878*b5dcd8f9SAlexander Leidinger atf_skip "setaudit(8) does not work here, so the service jail" \ 879*b5dcd8f9SAlexander Leidinger "cannot be asked about it" 880*b5dcd8f9SAlexander Leidinger 881*b5dcd8f9SAlexander Leidinger # setaudit(8) is prefixed to the command inside the jail, where 882*b5dcd8f9SAlexander Leidinger # PRIV_AUDIT_GETAUDIT and PRIV_AUDIT_SETAUDIT need allow.setaudit. 883*b5dcd8f9SAlexander Leidinger # That is not in a jail's default allow set, so the service can only 884*b5dcd8f9SAlexander Leidinger # work if the option below asks for it. 885*b5dcd8f9SAlexander Leidinger svcj_fixture <<-EOF 886*b5dcd8f9SAlexander Leidinger $(svcj_daemon_lines) 887*b5dcd8f9SAlexander Leidinger ${svc}_svcj="YES" 888*b5dcd8f9SAlexander Leidinger ${svc}_svcj_options="setaudit" 889*b5dcd8f9SAlexander Leidinger ${svc}_audit_user="root" 890*b5dcd8f9SAlexander Leidinger EOF 891*b5dcd8f9SAlexander Leidinger c=$(svcj_chroot) 892*b5dcd8f9SAlexander Leidinger rcs=$(svcj_rcpath) 893*b5dcd8f9SAlexander Leidinger 894*b5dcd8f9SAlexander Leidinger atf_check -s exit:0 -o ignore -e ignore /usr/sbin/chroot "$c" "$rcs" \ 895*b5dcd8f9SAlexander Leidinger onestart 896*b5dcd8f9SAlexander Leidinger svcj_pid > /dev/null || atf_fail "the daemon did not start" 897*b5dcd8f9SAlexander Leidinger} 898*b5dcd8f9SAlexander Leidingeraudit_user_cleanup() 899*b5dcd8f9SAlexander Leidinger{ 900*b5dcd8f9SAlexander Leidinger svcj_cleanup 901*b5dcd8f9SAlexander Leidinger} 902*b5dcd8f9SAlexander Leidinger 903*b5dcd8f9SAlexander Leidingeratf_test_case nice_negative cleanup 904*b5dcd8f9SAlexander Leidingernice_negative_head() 905*b5dcd8f9SAlexander Leidinger{ 906*b5dcd8f9SAlexander Leidinger atf_set "descr" "A negative \${name}_nice is dropped for a service in" \ 907*b5dcd8f9SAlexander Leidinger "a service jail" 908*b5dcd8f9SAlexander Leidinger atf_set "require.user" "root" 909*b5dcd8f9SAlexander Leidinger} 910*b5dcd8f9SAlexander Leidingernice_negative_body() 911*b5dcd8f9SAlexander Leidinger{ 912*b5dcd8f9SAlexander Leidinger local svc c rcs pid 913*b5dcd8f9SAlexander Leidinger 914*b5dcd8f9SAlexander Leidinger svcj_require 915*b5dcd8f9SAlexander Leidinger svc=$(svcj_svcname) 916*b5dcd8f9SAlexander Leidinger 917*b5dcd8f9SAlexander Leidinger svcj_fixture <<-EOF 918*b5dcd8f9SAlexander Leidinger $(svcj_daemon_lines) 919*b5dcd8f9SAlexander Leidinger ${svc}_svcj="YES" 920*b5dcd8f9SAlexander Leidinger ${svc}_nice="-5" 921*b5dcd8f9SAlexander Leidinger EOF 922*b5dcd8f9SAlexander Leidinger c=$(svcj_chroot) 923*b5dcd8f9SAlexander Leidinger rcs=$(svcj_rcpath) 924*b5dcd8f9SAlexander Leidinger 925*b5dcd8f9SAlexander Leidinger atf_check -s exit:0 -o ignore -e ignore /usr/sbin/chroot "$c" "$rcs" \ 926*b5dcd8f9SAlexander Leidinger onestart 927*b5dcd8f9SAlexander Leidinger pid=$(svcj_pid) || atf_fail "the daemon wrote no pid file" 928*b5dcd8f9SAlexander Leidinger 929*b5dcd8f9SAlexander Leidinger # Raising a process' priority needs PRIV_SCHED_SETPRIORITY, which a 930*b5dcd8f9SAlexander Leidinger # jail is never granted. nice(1) only warns when setpriority(2) 931*b5dcd8f9SAlexander Leidinger # fails and execs the command regardless, so the request is dropped 932*b5dcd8f9SAlexander Leidinger # and the service comes up at whatever its login class gives it -- 933*b5dcd8f9SAlexander Leidinger # the behaviour rc.conf(5) documents. Asserted as "not the value 934*b5dcd8f9SAlexander Leidinger # asked for" rather than as a literal, because the login class 935*b5dcd8f9SAlexander Leidinger # supplies the priority actually in use. 936*b5dcd8f9SAlexander Leidinger atf_check -o match:'^-?[0-9]+$' -o not-inline:"-5\n" \ 937*b5dcd8f9SAlexander Leidinger /bin/sh -c "ps -o nice= -p $pid | tr -d ' '" 938*b5dcd8f9SAlexander Leidinger} 939*b5dcd8f9SAlexander Leidingernice_negative_cleanup() 940*b5dcd8f9SAlexander Leidinger{ 941*b5dcd8f9SAlexander Leidinger svcj_cleanup 942*b5dcd8f9SAlexander Leidinger} 943*b5dcd8f9SAlexander Leidinger 944*b5dcd8f9SAlexander Leidingeratf_test_case oomprotect cleanup 945*b5dcd8f9SAlexander Leidingeroomprotect_head() 946*b5dcd8f9SAlexander Leidinger{ 947*b5dcd8f9SAlexander Leidinger atf_set "descr" "\${name}_oomprotect protects a service that runs in" \ 948*b5dcd8f9SAlexander Leidinger "a service jail" 949*b5dcd8f9SAlexander Leidinger atf_set "require.user" "root" 950*b5dcd8f9SAlexander Leidinger} 951*b5dcd8f9SAlexander Leidingeroomprotect_body() 952*b5dcd8f9SAlexander Leidinger{ 953*b5dcd8f9SAlexander Leidinger local svc c rcs pid 954*b5dcd8f9SAlexander Leidinger 955*b5dcd8f9SAlexander Leidinger svcj_require 956*b5dcd8f9SAlexander Leidinger # Decided by trying it, not by asking whether this is a jail: 957*b5dcd8f9SAlexander Leidinger # protect(1) needs PRIV_VM_MADV_PROTECT, and what matters is whether 958*b5dcd8f9SAlexander Leidinger # this process has it. 959*b5dcd8f9SAlexander Leidinger protect /usr/bin/true 2>/dev/null || 960*b5dcd8f9SAlexander Leidinger atf_skip "protect(1) is not permitted here" 961*b5dcd8f9SAlexander Leidinger svc=$(svcj_svcname) 962*b5dcd8f9SAlexander Leidinger 963*b5dcd8f9SAlexander Leidinger svcj_fixture <<-EOF 964*b5dcd8f9SAlexander Leidinger $(svcj_daemon_lines) 965*b5dcd8f9SAlexander Leidinger ${svc}_svcj="YES" 966*b5dcd8f9SAlexander Leidinger ${svc}_oomprotect="yes" 967*b5dcd8f9SAlexander Leidinger EOF 968*b5dcd8f9SAlexander Leidinger c=$(svcj_chroot) 969*b5dcd8f9SAlexander Leidinger rcs=$(svcj_rcpath) 970*b5dcd8f9SAlexander Leidinger 971*b5dcd8f9SAlexander Leidinger atf_check -s exit:0 -o ignore -e ignore /usr/sbin/chroot "$c" "$rcs" \ 972*b5dcd8f9SAlexander Leidinger onestart 973*b5dcd8f9SAlexander Leidinger pid=$(svcj_pid) || atf_fail "the daemon wrote no pid file" 974*b5dcd8f9SAlexander Leidinger 975*b5dcd8f9SAlexander Leidinger # protect(1) is skipped inside the jail and applied afterwards from 976*b5dcd8f9SAlexander Leidinger # outside, against the pid rc.subr found in the jail. 977*b5dcd8f9SAlexander Leidinger atf_check -o match:'^..1..... .......0$' -e empty \ 978*b5dcd8f9SAlexander Leidinger ps -p "$pid" -ax -o flags,flags2 979*b5dcd8f9SAlexander Leidinger} 980*b5dcd8f9SAlexander Leidingeroomprotect_cleanup() 981*b5dcd8f9SAlexander Leidinger{ 982*b5dcd8f9SAlexander Leidinger svcj_cleanup 983*b5dcd8f9SAlexander Leidinger} 984*b5dcd8f9SAlexander Leidinger 985*b5dcd8f9SAlexander Leidingeratf_test_case options_sysvipc_conflict cleanup 986*b5dcd8f9SAlexander Leidingeroptions_sysvipc_conflict_head() 987*b5dcd8f9SAlexander Leidinger{ 988*b5dcd8f9SAlexander Leidinger atf_set "descr" "Asking for both sysvipc and sysvipcnew fails without" \ 989*b5dcd8f9SAlexander Leidinger "creating a jail" 990*b5dcd8f9SAlexander Leidinger atf_set "require.user" "root" 991*b5dcd8f9SAlexander Leidinger} 992*b5dcd8f9SAlexander Leidingeroptions_sysvipc_conflict_body() 993*b5dcd8f9SAlexander Leidinger{ 994*b5dcd8f9SAlexander Leidinger local svc c rcs 995*b5dcd8f9SAlexander Leidinger 996*b5dcd8f9SAlexander Leidinger svcj_require 997*b5dcd8f9SAlexander Leidinger svc=$(svcj_svcname) 998*b5dcd8f9SAlexander Leidinger 999*b5dcd8f9SAlexander Leidinger svcj_fixture <<-EOF 1000*b5dcd8f9SAlexander Leidinger $(svcj_daemon_lines) 1001*b5dcd8f9SAlexander Leidinger ${svc}_svcj="YES" 1002*b5dcd8f9SAlexander Leidinger ${svc}_svcj_options="sysvipc sysvipcnew" 1003*b5dcd8f9SAlexander Leidinger EOF 1004*b5dcd8f9SAlexander Leidinger c=$(svcj_chroot) 1005*b5dcd8f9SAlexander Leidinger rcs=$(svcj_rcpath) 1006*b5dcd8f9SAlexander Leidinger 1007*b5dcd8f9SAlexander Leidinger atf_check -s exit:1 -o match:'more than one sysvipc option' -e ignore \ 1008*b5dcd8f9SAlexander Leidinger /usr/sbin/chroot "$c" "$rcs" onestart 1009*b5dcd8f9SAlexander Leidinger [ -z "$(svcj_jid)" ] || atf_fail "a jail was created anyway" 1010*b5dcd8f9SAlexander Leidinger} 1011*b5dcd8f9SAlexander Leidingeroptions_sysvipc_conflict_cleanup() 1012*b5dcd8f9SAlexander Leidinger{ 1013*b5dcd8f9SAlexander Leidinger svcj_cleanup 1014*b5dcd8f9SAlexander Leidinger} 1015*b5dcd8f9SAlexander Leidinger 1016*b5dcd8f9SAlexander Leidingeratf_test_case options_unknown cleanup 1017*b5dcd8f9SAlexander Leidingeroptions_unknown_head() 1018*b5dcd8f9SAlexander Leidinger{ 1019*b5dcd8f9SAlexander Leidinger atf_set "descr" "An unrecognised \${name}_svcj_options keyword is" \ 1020*b5dcd8f9SAlexander Leidinger "reported" 1021*b5dcd8f9SAlexander Leidinger atf_set "require.user" "root" 1022*b5dcd8f9SAlexander Leidinger} 1023*b5dcd8f9SAlexander Leidingeroptions_unknown_body() 1024*b5dcd8f9SAlexander Leidinger{ 1025*b5dcd8f9SAlexander Leidinger local svc c rcs 1026*b5dcd8f9SAlexander Leidinger 1027*b5dcd8f9SAlexander Leidinger svcj_require 1028*b5dcd8f9SAlexander Leidinger svc=$(svcj_svcname) 1029*b5dcd8f9SAlexander Leidinger 1030*b5dcd8f9SAlexander Leidinger svcj_fixture <<-EOF 1031*b5dcd8f9SAlexander Leidinger $(svcj_daemon_lines) 1032*b5dcd8f9SAlexander Leidinger ${svc}_svcj="YES" 1033*b5dcd8f9SAlexander Leidinger ${svc}_svcj_options="net_basic nosuchoption" 1034*b5dcd8f9SAlexander Leidinger EOF 1035*b5dcd8f9SAlexander Leidinger c=$(svcj_chroot) 1036*b5dcd8f9SAlexander Leidinger rcs=$(svcj_rcpath) 1037*b5dcd8f9SAlexander Leidinger 1038*b5dcd8f9SAlexander Leidinger # The keyword is reported and the service starts with the options 1039*b5dcd8f9SAlexander Leidinger # that were understood. Whether a typo should instead be fatal is a 1040*b5dcd8f9SAlexander Leidinger # policy question; this pins today's answer so that changing it is 1041*b5dcd8f9SAlexander Leidinger # deliberate. 1042*b5dcd8f9SAlexander Leidinger atf_check -s exit:0 -e ignore \ 1043*b5dcd8f9SAlexander Leidinger -o match:'unknown service jail option: nosuchoption' \ 1044*b5dcd8f9SAlexander Leidinger /usr/sbin/chroot "$c" "$rcs" onestart 1045*b5dcd8f9SAlexander Leidinger [ -n "$(svcj_jid)" ] || atf_fail "no service jail svcj-$svc" 1046*b5dcd8f9SAlexander Leidinger} 1047*b5dcd8f9SAlexander Leidingeroptions_unknown_cleanup() 1048*b5dcd8f9SAlexander Leidinger{ 1049*b5dcd8f9SAlexander Leidinger svcj_cleanup 1050*b5dcd8f9SAlexander Leidinger} 1051*b5dcd8f9SAlexander Leidinger 1052*b5dcd8f9SAlexander Leidingeratf_test_case options_ipaddrs cleanup 1053*b5dcd8f9SAlexander Leidingeroptions_ipaddrs_head() 1054*b5dcd8f9SAlexander Leidinger{ 1055*b5dcd8f9SAlexander Leidinger atf_set "descr" "\${name}_svcj_ipaddrs restricts the service jail to" \ 1056*b5dcd8f9SAlexander Leidinger "the listed addresses" 1057*b5dcd8f9SAlexander Leidinger atf_set "require.user" "root" 1058*b5dcd8f9SAlexander Leidinger} 1059*b5dcd8f9SAlexander Leidingeroptions_ipaddrs_body() 1060*b5dcd8f9SAlexander Leidinger{ 1061*b5dcd8f9SAlexander Leidinger local svc c rcs 1062*b5dcd8f9SAlexander Leidinger 1063*b5dcd8f9SAlexander Leidinger svcj_require 1064*b5dcd8f9SAlexander Leidinger # A jail can only hand an address to a child jail if it holds it 1065*b5dcd8f9SAlexander Leidinger # itself. 1066*b5dcd8f9SAlexander Leidinger ifconfig lo0 inet 2>/dev/null | grep -q 'inet 127\.0\.0\.1 ' || 1067*b5dcd8f9SAlexander Leidinger atf_skip "127.0.0.1 is not available here" 1068*b5dcd8f9SAlexander Leidinger svc=$(svcj_svcname) 1069*b5dcd8f9SAlexander Leidinger 1070*b5dcd8f9SAlexander Leidinger svcj_fixture <<-EOF 1071*b5dcd8f9SAlexander Leidinger $(svcj_daemon_lines) 1072*b5dcd8f9SAlexander Leidinger ${svc}_svcj="YES" 1073*b5dcd8f9SAlexander Leidinger ${svc}_svcj_options="netv4" 1074*b5dcd8f9SAlexander Leidinger ${svc}_svcj_ipaddrs="127.0.0.1" 1075*b5dcd8f9SAlexander Leidinger EOF 1076*b5dcd8f9SAlexander Leidinger c=$(svcj_chroot) 1077*b5dcd8f9SAlexander Leidinger rcs=$(svcj_rcpath) 1078*b5dcd8f9SAlexander Leidinger 1079*b5dcd8f9SAlexander Leidinger atf_check -s exit:0 -o ignore -e ignore /usr/sbin/chroot "$c" "$rcs" \ 1080*b5dcd8f9SAlexander Leidinger onestart 1081*b5dcd8f9SAlexander Leidinger atf_check -o inline:"127.0.0.1\n" jls -j "svcj-$svc" ip4.addr 1082*b5dcd8f9SAlexander Leidinger} 1083*b5dcd8f9SAlexander Leidingeroptions_ipaddrs_cleanup() 1084*b5dcd8f9SAlexander Leidinger{ 1085*b5dcd8f9SAlexander Leidinger svcj_cleanup 1086*b5dcd8f9SAlexander Leidinger} 1087*b5dcd8f9SAlexander Leidinger 1088*b5dcd8f9SAlexander Leidingeratf_test_case extra_command_placement cleanup 1089*b5dcd8f9SAlexander Leidingerextra_command_placement_head() 1090*b5dcd8f9SAlexander Leidinger{ 1091*b5dcd8f9SAlexander Leidinger atf_set "descr" "A script's own extra command runs on the host unless" \ 1092*b5dcd8f9SAlexander Leidinger "the script declares \${name}_\${cmd}_svcj_enable" 1093*b5dcd8f9SAlexander Leidinger atf_set "require.user" "root" 1094*b5dcd8f9SAlexander Leidinger} 1095*b5dcd8f9SAlexander Leidingerextra_command_placement_body() 1096*b5dcd8f9SAlexander Leidinger{ 1097*b5dcd8f9SAlexander Leidinger local svc c rcs wd jid 1098*b5dcd8f9SAlexander Leidinger 1099*b5dcd8f9SAlexander Leidinger svcj_require 1100*b5dcd8f9SAlexander Leidinger svc=$(svcj_svcname) 1101*b5dcd8f9SAlexander Leidinger wd=$(svcj_workdir) 1102*b5dcd8f9SAlexander Leidinger 1103*b5dcd8f9SAlexander Leidinger # The probe reports the jail id of the SERVICE's own process, as seen 1104*b5dcd8f9SAlexander Leidinger # from wherever the probe itself is running. The kernel answers that 1105*b5dcd8f9SAlexander Leidinger # question relative to the asking process: fill_kinfo_proc() reports a 1106*b5dcd8f9SAlexander Leidinger # jail id of 0 for a process in the asker's own prison, and the real 1107*b5dcd8f9SAlexander Leidinger # prison id otherwise. So the daemon reads as the jail's own jid from 1108*b5dcd8f9SAlexander Leidinger # outside and as 0 from inside -- which asserts that the method ran in 1109*b5dcd8f9SAlexander Leidinger # *the service's* jail, not merely somewhere jailed. 1110*b5dcd8f9SAlexander Leidinger # ${name}_${cmd}_svcj_enable is a declaration by the author of the rc 1111*b5dcd8f9SAlexander Leidinger # script, not a knob for the administrator, so both halves below set 1112*b5dcd8f9SAlexander Leidinger # it in the script itself. It is read only on the branch that a 1113*b5dcd8f9SAlexander Leidinger # service with ${name}_svcj=YES reaches, which is why a script may 1114*b5dcd8f9SAlexander Leidinger # set it unconditionally. 1115*b5dcd8f9SAlexander Leidinger svcj_fixture <<-EOF 1116*b5dcd8f9SAlexander Leidinger $(svcj_daemon_lines) 1117*b5dcd8f9SAlexander Leidinger ${svc}_svcj="YES" 1118*b5dcd8f9SAlexander Leidinger extra_commands="probe" 1119*b5dcd8f9SAlexander Leidinger probe_cmd="${svc}_probe" 1120*b5dcd8f9SAlexander Leidinger ${svc}_probe_svcj_enable="NO" 1121*b5dcd8f9SAlexander Leidinger ${svc}_probe() 1122*b5dcd8f9SAlexander Leidinger { 1123*b5dcd8f9SAlexander Leidinger ps -o jid= -p \$(cat "\$pidfile") | tr -d ' ' > "$wd/probe.out" 1124*b5dcd8f9SAlexander Leidinger } 1125*b5dcd8f9SAlexander Leidinger EOF 1126*b5dcd8f9SAlexander Leidinger c=$(svcj_chroot) 1127*b5dcd8f9SAlexander Leidinger rcs=$(svcj_rcpath) 1128*b5dcd8f9SAlexander Leidinger 1129*b5dcd8f9SAlexander Leidinger atf_check -s exit:0 -o ignore -e ignore /usr/sbin/chroot "$c" "$rcs" \ 1130*b5dcd8f9SAlexander Leidinger onestart 1131*b5dcd8f9SAlexander Leidinger jid=$(svcj_jid) 1132*b5dcd8f9SAlexander Leidinger [ -n "$jid" ] || atf_fail "no service jail svcj-$svc was created" 1133*b5dcd8f9SAlexander Leidinger [ "$jid" -ne 0 ] || atf_fail "svcj-$svc has jid 0, so this case " \ 1134*b5dcd8f9SAlexander Leidinger "cannot tell inside from outside" 1135*b5dcd8f9SAlexander Leidinger 1136*b5dcd8f9SAlexander Leidinger # Not declared: the method runs outside the service jail, and sees 1137*b5dcd8f9SAlexander Leidinger # the daemon carrying the jail's id. 1138*b5dcd8f9SAlexander Leidinger atf_check -s exit:0 -o ignore -e ignore /usr/sbin/chroot "$c" "$rcs" \ 1139*b5dcd8f9SAlexander Leidinger oneprobe 1140*b5dcd8f9SAlexander Leidinger atf_check -o inline:"$jid\n" cat "$(svcj_hostdir)/probe.out" 1141*b5dcd8f9SAlexander Leidinger 1142*b5dcd8f9SAlexander Leidinger # Declared: it runs inside, in the same prison as the daemon. 1143*b5dcd8f9SAlexander Leidinger rm -f "$(svcj_hostdir)/probe.out" 1144*b5dcd8f9SAlexander Leidinger atf_check -s exit:0 sed -i "" \ 1145*b5dcd8f9SAlexander Leidinger "s/${svc}_probe_svcj_enable=\"NO\"/${svc}_probe_svcj_enable=\"YES\"/" \ 1146*b5dcd8f9SAlexander Leidinger "$(svcj_hostrc)" 1147*b5dcd8f9SAlexander Leidinger atf_check -s exit:0 -o ignore -e ignore /usr/sbin/chroot "$c" "$rcs" \ 1148*b5dcd8f9SAlexander Leidinger oneprobe 1149*b5dcd8f9SAlexander Leidinger atf_check -o inline:"0\n" cat "$(svcj_hostdir)/probe.out" 1150*b5dcd8f9SAlexander Leidinger} 1151*b5dcd8f9SAlexander Leidingerextra_command_placement_cleanup() 1152*b5dcd8f9SAlexander Leidinger{ 1153*b5dcd8f9SAlexander Leidinger svcj_cleanup 1154*b5dcd8f9SAlexander Leidinger} 1155*b5dcd8f9SAlexander Leidinger 1156*b5dcd8f9SAlexander Leidingeratf_init_test_cases() 1157*b5dcd8f9SAlexander Leidinger{ 1158*b5dcd8f9SAlexander Leidinger atf_add_test_case start_stop 1159*b5dcd8f9SAlexander Leidinger atf_add_test_case all_enable 1160*b5dcd8f9SAlexander Leidinger atf_add_test_case all_enable_quiet 1161*b5dcd8f9SAlexander Leidinger atf_add_test_case all_enable_rcconfd 1162*b5dcd8f9SAlexander Leidinger atf_add_test_case stop_as_user 1163*b5dcd8f9SAlexander Leidinger atf_add_test_case svcj_toggle_after_stop 1164*b5dcd8f9SAlexander Leidinger atf_add_test_case reload_as_user 1165*b5dcd8f9SAlexander Leidinger atf_add_test_case restart_cmd 1166*b5dcd8f9SAlexander Leidinger atf_add_test_case status_cmd 1167*b5dcd8f9SAlexander Leidinger atf_add_test_case restart_cmd_when_stopped 1168*b5dcd8f9SAlexander Leidinger atf_add_test_case status_cmd_when_stopped 1169*b5dcd8f9SAlexander Leidinger atf_add_test_case stop_removes_dead_jail 1170*b5dcd8f9SAlexander Leidinger atf_add_test_case stop_orphan_jail_with_child 1171*b5dcd8f9SAlexander Leidinger atf_add_test_case audit_user 1172*b5dcd8f9SAlexander Leidinger atf_add_test_case nice_negative 1173*b5dcd8f9SAlexander Leidinger atf_add_test_case oomprotect 1174*b5dcd8f9SAlexander Leidinger atf_add_test_case options_sysvipc_conflict 1175*b5dcd8f9SAlexander Leidinger atf_add_test_case options_unknown 1176*b5dcd8f9SAlexander Leidinger atf_add_test_case options_ipaddrs 1177*b5dcd8f9SAlexander Leidinger atf_add_test_case extra_command_placement 1178*b5dcd8f9SAlexander Leidinger} 1179