xref: /freebsd/libexec/rc/tests/svcj_test.sh (revision b5dcd8f9d6ae702adbc1627839b5235a28839370)
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