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
28# $FreeBSD$
29
30atf_test_case both_pidfile cleanup
31both_pidfile_head() {
32	atf_set "descr" "daemon should write pid files for itself and its child"
33}
34both_pidfile_body() {
35	daemon -P daemon.pid -p sleep.pid sleep 300
36	atf_check -s exit:0 test -f daemon.pid
37	atf_check -s exit:0 -o match:"daemon: sleep" ps -p `cat daemon.pid`
38	atf_check -s exit:0 test -f sleep.pid
39	atf_check -s exit:0 -o match:"[0-9] sleep 300$" ps -p `cat sleep.pid`
40}
41both_pidfile_cleanup() {
42	if [ -f daemon.pid ]; then
43		daemon_pid=`cat daemon.pid`
44	fi
45	if [ -f sleep_pid ]; then
46		sleep_pid=`cat sleep.pid`
47	fi
48	[ -n "$sleep_pid" ] && kill $sleep_pid
49	# NB: killing the sleep should kill the daemon too, so we musn't fail
50	# the test if the second kill fails with ESRCH
51	[ -n "$daemon_pid" ] && kill $daemon_pid || true
52}
53
54atf_test_case chdir cleanup
55chdir_head() {
56	atf_set "descr" "daemon should chdir to /"
57}
58chdir_body() {
59	# Executing sleep by relative path will only work from /
60	daemon -p ${PWD}/sleep.pid -c bin/sleep 300
61	atf_check -s exit:0 test -f sleep.pid
62	atf_check -s exit:0 -o match:"[0-9] bin/sleep 300$" \
63		ps -p `cat sleep.pid`
64}
65chdir_cleanup() {
66	[ -f sleep.pid ] && kill `cat sleep.pid`
67}
68
69atf_test_case child_pidfile cleanup
70child_pidfile_head() {
71	atf_set "descr" "daemon should write its child's pid to a pidfile"
72}
73child_pidfile_body() {
74	daemon -p sleep.pid sleep 300
75	atf_check -s exit:0 test -f sleep.pid
76	atf_check -s exit:0 -o match:"[0-9] sleep 300$" ps -p `cat sleep.pid`
77}
78child_pidfile_cleanup() {
79	[ -f sleep.pid ] && kill `cat sleep.pid`
80}
81
82atf_test_case child_pidfile_lock cleanup
83child_pidfile_lock_head() {
84	atf_set "descr" "daemon should refuse to clobber an existing child"
85}
86child_pidfile_lock_body() {
87	daemon -p sleep.pid sleep 300
88	atf_check -s exit:0 test -f sleep.pid
89	atf_check -s not-exit:0 -e match:"process already running" \
90		daemon -p sleep.pid sleep 300
91}
92child_pidfile_lock_cleanup() {
93	[ -f sleep.pid ] && kill `cat sleep.pid`
94}
95
96atf_test_case newsyslog cleanup
97newsyslog_head() {
98	atf_set "descr" "daemon should close and reopen the output file on SIGHUP"
99}
100newsyslog_body() {
101	cat > child.sh <<HERE
102#! /bin/sh
103while true ; do
104	echo "my output"
105	sleep 0.1
106done
107HERE
108	chmod +x child.sh
109	daemon -P daemon.pid -H -o output_file ./child.sh
110	atf_check -s exit:0 test -f daemon.pid
111	sleep 0.2
112	mv output_file output_file.0
113	kill -HUP `cat daemon.pid`
114	sleep 0.2
115	atf_check -s exit:0 test -s output_file.0
116	atf_check -s exit:0 test -s output_file
117}
118newsyslog_cleanup() {
119	[ -f daemon.pid ] && kill `cat daemon.pid`
120}
121
122atf_test_case output_file
123output_file_head() {
124	atf_set "descr" "daemon should redirect stdout to a file"
125}
126output_file_body() {
127	daemon -o output_file seq 1 5
128	seq 1 5 > expected_file
129	atf_check -s exit:0 cmp output_file expected_file
130}
131
132atf_test_case restart_child cleanup
133restart_child_head() {
134	atf_set "descr" "daemon should restart a dead child"
135}
136restart_child_body() {
137	daemon -rP daemon.pid -p sleep.pid sleep 300
138	atf_check -s exit:0 test -f daemon.pid
139	atf_check -s exit:0 test -f sleep.pid
140	orig_sleep_pid=`cat sleep.pid`
141	kill $orig_sleep_pid
142	# Wait up to 10s for the daemon to restart the child.
143	for t in `seq 0 0.1 10`; do
144		new_sleep_pid=`cat sleep.pid`
145		[ "$orig_sleep_pid" -ne "$new_sleep_pid" ] && break
146		sleep 0.1
147	done
148	[ "$orig_sleep_pid" -ne "$new_sleep_pid" ] || \
149		atf_fail "child was not restarted"
150
151}
152restart_child_cleanup() {
153	[ -f daemon.pid ] && kill `cat daemon.pid`
154}
155
156atf_test_case supervisor_pidfile cleanup
157supervisor_pidfile_head() {
158	atf_set "descr" "daemon should write its own pid to a pidfile"
159}
160supervisor_pidfile_body() {
161	daemon -P daemon.pid sleep 300
162	atf_check -s exit:0 test -f daemon.pid
163	atf_check -s exit:0 -o match:"daemon: sleep" ps -p `cat daemon.pid`
164}
165supervisor_pidfile_cleanup() {
166	[ -f daemon.pid ] && kill `cat daemon.pid`
167}
168
169atf_test_case supervisor_pidfile_lock cleanup
170supervisor_pidfile_lock_head() {
171	atf_set "descr" "daemon should refuse to clobber an existing instance"
172}
173supervisor_pidfile_lock_body() {
174	daemon -P daemon.pid sleep 300
175	atf_check -s exit:0 test -f daemon.pid
176	atf_check -s not-exit:0 -e match:"process already running" \
177		daemon -p daemon.pid sleep 300
178}
179supervisor_pidfile_lock_cleanup() {
180	[ -f daemon.pid ] && kill `cat daemon.pid`
181}
182
183atf_test_case title cleanup
184title_head() {
185	atf_set "descr" "daemon should change its process title"
186}
187title_body() {
188	daemon -P daemon.pid -t "I'm a title!" sleep 300
189	atf_check -s exit:0 test -f daemon.pid
190	atf_check -s exit:0 -o match:"daemon: I'm a title!" \
191		ps -p `cat daemon.pid`
192}
193title_cleanup() {
194	[ -f daemon.pid ] && kill `cat daemon.pid`
195}
196
197atf_test_case user cleanup
198user_head() {
199	atf_set "descr" "daemon should drop privileges"
200	atf_set "require.user" "root"
201}
202user_body() {
203	daemon -p sleep.pid -u nobody sleep 300
204	atf_check -s exit:0 test -f sleep.pid
205	atf_check -s exit:0 -o match:"^nobody" ps -up `cat sleep.pid`
206}
207user_cleanup() {
208	[ -f sleep.pid ] && kill `cat sleep.pid`
209}
210
211
212atf_init_test_cases() {
213	atf_add_test_case both_pidfile
214	atf_add_test_case chdir
215	atf_add_test_case child_pidfile
216	atf_add_test_case child_pidfile_lock
217	atf_add_test_case newsyslog
218	atf_add_test_case output_file
219	atf_add_test_case restart_child
220	atf_add_test_case supervisor_pidfile
221	atf_add_test_case supervisor_pidfile_lock
222	atf_add_test_case title
223	atf_add_test_case user
224}
225