xref: /freebsd/usr.sbin/daemon/tests/daemon_test.sh (revision 9ab59e900c1dd693b4d14285389a035e81341789)
1#!/bin/sh
2#
3# SPDX-License-Identifier: BSD-2-Clause
4#
5# Copyright (c) 2021 Axcient
6#
7# Redistribution and use in source and binary forms, with or without
8# modification, are permitted provided that the following conditions
9# are met:
10# 1. Redistributions of source code must retain the above copyright
11#    notice, this list of conditions and the following disclaimer.
12# 2. Redistributions in binary form must reproduce the above copyright
13#    notice, this list of conditions and the following disclaimer in the
14#    documentation and/or other materials provided with the distribution.
15#
16# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26# SUCH DAMAGE.
27
28atf_test_case both_pidfile cleanup
29both_pidfile_head() {
30	atf_set "descr" "daemon should write pid files for itself and its child"
31}
32both_pidfile_body() {
33	daemon -P daemon.pid -p sleep.pid sleep 300
34	atf_check -s exit:0 test -f daemon.pid
35	atf_check -s exit:0 -o match:"daemon: sleep" ps -p `cat daemon.pid`
36	atf_check -s exit:0 test -f sleep.pid
37	atf_check -s exit:0 -o match:"[0-9] sleep 300$" ps -p `cat sleep.pid`
38}
39both_pidfile_cleanup() {
40	if [ -f daemon.pid ]; then
41		daemon_pid=`cat daemon.pid`
42	fi
43	if [ -f sleep_pid ]; then
44		sleep_pid=`cat sleep.pid`
45	fi
46	[ -n "$sleep_pid" ] && kill $sleep_pid
47	# NB: killing the sleep should kill the daemon too, so we musn't fail
48	# the test if the second kill fails with ESRCH
49	[ -n "$daemon_pid" ] && kill $daemon_pid || true
50}
51
52atf_test_case chdir cleanup
53chdir_head() {
54	atf_set "descr" "daemon should chdir to /"
55}
56chdir_body() {
57	# Executing sleep by relative path will only work from /
58	daemon -p ${PWD}/sleep.pid -c bin/sleep 300
59	atf_check -s exit:0 test -f sleep.pid
60	atf_check -s exit:0 -o match:"[0-9] bin/sleep 300$" \
61		ps -p `cat sleep.pid`
62}
63chdir_cleanup() {
64	[ -f sleep.pid ] && kill `cat sleep.pid`
65}
66
67atf_test_case child_pidfile cleanup
68child_pidfile_head() {
69	atf_set "descr" "daemon should write its child's pid to a pidfile"
70}
71child_pidfile_body() {
72	daemon -p sleep.pid sleep 300
73	atf_check -s exit:0 test -f sleep.pid
74	atf_check -s exit:0 -o match:"[0-9] sleep 300$" ps -p `cat sleep.pid`
75}
76child_pidfile_cleanup() {
77	[ -f sleep.pid ] && kill `cat sleep.pid`
78}
79
80atf_test_case child_pidfile_lock cleanup
81child_pidfile_lock_head() {
82	atf_set "descr" "daemon should refuse to clobber an existing child"
83}
84child_pidfile_lock_body() {
85	daemon -p sleep.pid sleep 300
86	atf_check -s exit:0 test -f sleep.pid
87	atf_check -s not-exit:0 -e match:"process already running" \
88		daemon -p sleep.pid sleep 300
89}
90child_pidfile_lock_cleanup() {
91	[ -f sleep.pid ] && kill `cat sleep.pid`
92}
93
94atf_test_case newsyslog cleanup
95newsyslog_head() {
96	atf_set "descr" "daemon should close and reopen the output file on SIGHUP"
97}
98newsyslog_body() {
99	cat > child.sh <<HERE
100#! /bin/sh
101while true ; do
102	echo "my output"
103	sleep 0.1
104done
105HERE
106	chmod +x child.sh
107	daemon -P daemon.pid -H -o output_file ./child.sh
108	atf_check -s exit:0 test -f daemon.pid
109	sleep 0.2
110	mv output_file output_file.0
111	kill -HUP `cat daemon.pid`
112	sleep 0.2
113	atf_check -s exit:0 test -s output_file.0
114	atf_check -s exit:0 test -s output_file
115}
116newsyslog_cleanup() {
117	[ -f daemon.pid ] && kill `cat daemon.pid`
118}
119
120atf_test_case output_file
121output_file_head() {
122	atf_set "descr" "daemon should redirect stdout to a file"
123}
124output_file_body() {
125	daemon -o output_file seq 1 5
126	seq 1 5 > expected_file
127	atf_check -s exit:0 cmp output_file expected_file
128}
129
130atf_test_case restart_child cleanup
131restart_child_head() {
132	atf_set "descr" "daemon should restart a dead child"
133}
134restart_child_body() {
135	daemon -rP daemon.pid -p sleep.pid sleep 300
136	atf_check -s exit:0 test -f daemon.pid
137	atf_check -s exit:0 test -f sleep.pid
138	orig_sleep_pid=`cat sleep.pid`
139	kill $orig_sleep_pid
140	# Wait up to 10s for the daemon to restart the child.
141	for t in `seq 0 0.1 10`; do
142		if [ -s "sleep.pid" ]; then
143			new_sleep_pid=`cat sleep.pid`
144			[ "$orig_sleep_pid" -ne "$new_sleep_pid" ] && break
145		fi
146
147		sleep 0.1
148	done
149	[ "$orig_sleep_pid" -ne "$new_sleep_pid" ] || \
150		atf_fail "child was not restarted"
151
152}
153restart_child_cleanup() {
154	[ -f daemon.pid ] && kill `cat daemon.pid`
155}
156
157atf_test_case restart_hang cleanup
158restart_hang_head() {
159	atf_set "descr" "daemon should terminate with SIGTERM even pending child restart"
160}
161restart_hang_body() {
162	daemon -rP daemon.pid -R 10 -p sleep.pid sleep 300
163	atf_check -s exit:0 test -f daemon.pid
164	atf_check -s exit:0 test -f sleep.pid
165	read sleep_pid < sleep.pid
166	1>&2 echo "$sleep_pid"
167	kill "$sleep_pid"
168
169	# Wait up to 5s for the child to exit
170	for t in `seq 0 0.1 5`; do
171		[ ! -s "sleep.pid" ] && break
172		sleep 0.1
173	done
174
175	atf_check test ! -s "sleep.pid"
176
177	read daemon_pid < daemon.pid
178	kill -TERM "$daemon_pid"
179
180	# Wait up to 10s for the daemon to terminate
181	for t in `seq 0 0.1 10`; do
182		[ ! -f "daemon.pid" ] && break
183		sleep 0.1
184	done
185
186	atf_check test ! -f "daemon.pid"
187	atf_check test ! -f "sleep.pid"
188}
189restart_hang_cleanup() {
190	[ -s daemon.pid ] && kill -9 `cat daemon.pid`
191	true
192}
193
194atf_test_case supervisor_pidfile cleanup
195supervisor_pidfile_head() {
196	atf_set "descr" "daemon should write its own pid to a pidfile"
197}
198supervisor_pidfile_body() {
199	daemon -P daemon.pid sleep 300
200	atf_check -s exit:0 test -f daemon.pid
201	atf_check -s exit:0 -o match:"daemon: sleep" ps -p `cat daemon.pid`
202}
203supervisor_pidfile_cleanup() {
204	[ -f daemon.pid ] && kill `cat daemon.pid`
205}
206
207atf_test_case supervisor_pidfile_lock cleanup
208supervisor_pidfile_lock_head() {
209	atf_set "descr" "daemon should refuse to clobber an existing instance"
210}
211supervisor_pidfile_lock_body() {
212	daemon -P daemon.pid sleep 300
213	atf_check -s exit:0 test -f daemon.pid
214	atf_check -s not-exit:0 -e match:"process already running" \
215		daemon -p daemon.pid sleep 300
216}
217supervisor_pidfile_lock_cleanup() {
218	[ -f daemon.pid ] && kill `cat daemon.pid`
219}
220
221atf_test_case title cleanup
222title_head() {
223	atf_set "descr" "daemon should change its process title"
224}
225title_body() {
226	daemon -P daemon.pid -t "I'm a title!" sleep 300
227	atf_check -s exit:0 test -f daemon.pid
228	atf_check -s exit:0 -o match:"daemon: I'm a title!" \
229		ps -p `cat daemon.pid`
230}
231title_cleanup() {
232	[ -f daemon.pid ] && kill `cat daemon.pid`
233}
234
235atf_test_case user cleanup
236user_head() {
237	atf_set "descr" "daemon should drop privileges"
238	atf_set "require.user" "root"
239}
240user_body() {
241	daemon -p sleep.pid -u nobody sleep 300
242	atf_check -s exit:0 test -f sleep.pid
243	atf_check -s exit:0 -o match:"^nobody" ps -up `cat sleep.pid`
244}
245user_cleanup() {
246	[ -f sleep.pid ] && kill `cat sleep.pid`
247}
248
249
250atf_init_test_cases() {
251	atf_add_test_case both_pidfile
252	atf_add_test_case chdir
253	atf_add_test_case child_pidfile
254	atf_add_test_case child_pidfile_lock
255	atf_add_test_case newsyslog
256	atf_add_test_case output_file
257	atf_add_test_case restart_child
258	atf_add_test_case restart_hang
259	atf_add_test_case supervisor_pidfile
260	atf_add_test_case supervisor_pidfile_lock
261	atf_add_test_case title
262	atf_add_test_case user
263}
264