git: a09e037bc30b - stable/12 - daemon: add some basic tests

From: Alan Somers <asomers_at_FreeBSD.org>
Date: Thu, 07 Oct 2021 19:26:37 UTC
The branch stable/12 has been updated by asomers:

URL: https://cgit.FreeBSD.org/src/commit/?id=a09e037bc30b8a76cb7007bc4883d62e64c90e0b

commit a09e037bc30b8a76cb7007bc4883d62e64c90e0b
Author:     Alan Somers <asomers@FreeBSD.org>
AuthorDate: 2021-03-17 16:35:00 +0000
Commit:     Alan Somers <asomers@FreeBSD.org>
CommitDate: 2021-10-07 17:42:37 +0000

    daemon: add some basic tests
    
    Sponsored by:   Axcient
    Differential Revision:  https://reviews.freebsd.org/D29316
    
    (cherry picked from commit 3b57d80c7aa924ed0fad565591f5292c2ac88da5)
---
 etc/mtree/BSD.tests.dist             |   2 +
 usr.sbin/daemon/Makefile             |   5 +
 usr.sbin/daemon/tests/Makefile       |   7 ++
 usr.sbin/daemon/tests/daemon_test.sh | 224 +++++++++++++++++++++++++++++++++++
 4 files changed, 238 insertions(+)

diff --git a/etc/mtree/BSD.tests.dist b/etc/mtree/BSD.tests.dist
index 207020665689..b25dfe78f0a2 100644
--- a/etc/mtree/BSD.tests.dist
+++ b/etc/mtree/BSD.tests.dist
@@ -1084,6 +1084,8 @@
     usr.sbin
         chown
         ..
+        daemon
+        ..
         etcupdate
         ..
         extattr
diff --git a/usr.sbin/daemon/Makefile b/usr.sbin/daemon/Makefile
index eb0d50280161..0bf34c351932 100644
--- a/usr.sbin/daemon/Makefile
+++ b/usr.sbin/daemon/Makefile
@@ -5,4 +5,9 @@ MAN=	daemon.8
 
 LIBADD=	util
 
+.include <src.opts.mk>
+
+HAS_TESTS=
+SUBDIR.${MK_TESTS}+= tests
+
 .include <bsd.prog.mk>
diff --git a/usr.sbin/daemon/tests/Makefile b/usr.sbin/daemon/tests/Makefile
new file mode 100644
index 000000000000..c687361ec4e7
--- /dev/null
+++ b/usr.sbin/daemon/tests/Makefile
@@ -0,0 +1,7 @@
+# $FreeBSD$
+
+PACKAGE=	tests
+
+ATF_TESTS_SH=	daemon_test
+
+.include <bsd.test.mk>
diff --git a/usr.sbin/daemon/tests/daemon_test.sh b/usr.sbin/daemon/tests/daemon_test.sh
new file mode 100644
index 000000000000..ab5d86e0e71b
--- /dev/null
+++ b/usr.sbin/daemon/tests/daemon_test.sh
@@ -0,0 +1,224 @@
+#!/bin/sh
+#
+# SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+#
+# Copyright (c) 2021 Axcient
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+#    notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+#    notice, this list of conditions and the following disclaimer in the
+#    documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+# $FreeBSD$
+
+atf_test_case both_pidfile cleanup
+both_pidfile_head() {
+	atf_set "descr" "daemon should write pid files for itself and its child"
+}
+both_pidfile_body() {
+	daemon -P daemon.pid -p sleep.pid sleep 300
+	atf_check -s exit:0 test -f daemon.pid
+	atf_check -s exit:0 -o match:"daemon: sleep" ps -p `cat daemon.pid`
+	atf_check -s exit:0 test -f sleep.pid
+	atf_check -s exit:0 -o match:"[0-9] sleep 300$" ps -p `cat sleep.pid`
+}
+both_pidfile_cleanup() {
+	if [ -f daemon.pid ]; then
+		daemon_pid=`cat daemon.pid`
+	fi
+	if [ -f sleep_pid ]; then
+		sleep_pid=`cat sleep.pid`
+	fi
+	[ -n "$sleep_pid" ] && kill $sleep_pid
+	# NB: killing the sleep should kill the daemon too, so we musn't fail
+	# the test if the second kill fails with ESRCH
+	[ -n "$daemon_pid" ] && kill $daemon_pid || true
+}
+
+atf_test_case chdir cleanup
+chdir_head() {
+	atf_set "descr" "daemon should chdir to /"
+}
+chdir_body() {
+	# Executing sleep by relative path will only work from /
+	daemon -p ${PWD}/sleep.pid -c bin/sleep 300
+	atf_check -s exit:0 test -f sleep.pid
+	atf_check -s exit:0 -o match:"[0-9] bin/sleep 300$" \
+		ps -p `cat sleep.pid`
+}
+chdir_cleanup() {
+	[ -f sleep.pid ] && kill `cat sleep.pid`
+}
+
+atf_test_case child_pidfile cleanup
+child_pidfile_head() {
+	atf_set "descr" "daemon should write its child's pid to a pidfile"
+}
+child_pidfile_body() {
+	daemon -p sleep.pid sleep 300
+	atf_check -s exit:0 test -f sleep.pid
+	atf_check -s exit:0 -o match:"[0-9] sleep 300$" ps -p `cat sleep.pid`
+}
+child_pidfile_cleanup() {
+	[ -f sleep.pid ] && kill `cat sleep.pid`
+}
+
+atf_test_case child_pidfile_lock cleanup
+child_pidfile_lock_head() {
+	atf_set "descr" "daemon should refuse to clobber an existing child"
+}
+child_pidfile_lock_body() {
+	daemon -p sleep.pid sleep 300
+	atf_check -s exit:0 test -f sleep.pid
+	atf_check -s not-exit:0 -e match:"process already running" \
+		daemon -p sleep.pid sleep 300
+}
+child_pidfile_lock_cleanup() {
+	[ -f sleep.pid ] && kill `cat sleep.pid`
+}
+
+atf_test_case newsyslog cleanup
+newsyslog_head() {
+	atf_set "descr" "daemon should close and reopen the output file on SIGHUP"
+}
+newsyslog_body() {
+	cat > child.sh <<HERE
+#! /bin/sh
+while true ; do
+	echo "my output"
+	sleep 0.1
+done
+HERE
+	chmod +x child.sh
+	daemon -P daemon.pid -H -o output_file ./child.sh
+	atf_check -s exit:0 test -f daemon.pid
+	sleep 0.2
+	mv output_file output_file.0
+	kill -HUP `cat daemon.pid`
+	sleep 0.2
+	atf_check -s exit:0 test -s output_file.0
+	atf_check -s exit:0 test -s output_file
+}
+newsyslog_cleanup() {
+	[ -f daemon.pid ] && kill `cat daemon.pid`
+}
+
+atf_test_case output_file
+output_file_head() {
+	atf_set "descr" "daemon should redirect stdout to a file"
+}
+output_file_body() {
+	daemon -o output_file seq 1 5
+	seq 1 5 > expected_file
+	atf_check -s exit:0 cmp output_file expected_file
+}
+
+atf_test_case restart_child cleanup
+restart_child_head() {
+	atf_set "descr" "daemon should restart a dead child"
+}
+restart_child_body() {
+	daemon -rP daemon.pid -p sleep.pid sleep 300
+	atf_check -s exit:0 test -f daemon.pid
+	atf_check -s exit:0 test -f sleep.pid
+	orig_sleep_pid=`cat sleep.pid`
+	kill $orig_sleep_pid
+	# Wait up to 10s for the daemon to restart the child.
+	for t in `seq 0 0.1 10`; do
+		new_sleep_pid=`cat sleep.pid`
+		[ "$orig_sleep_pid" -ne "$new_sleep_pid" ] && break
+		sleep 0.1
+	done
+	[ "$orig_sleep_pid" -ne "$new_sleep_pid" ] || \
+		atf_fail "child was not restarted"
+
+}
+restart_child_cleanup() {
+	[ -f daemon.pid ] && kill `cat daemon.pid`
+}
+
+atf_test_case supervisor_pidfile cleanup
+supervisor_pidfile_head() {
+	atf_set "descr" "daemon should write its own pid to a pidfile"
+}
+supervisor_pidfile_body() {
+	daemon -P daemon.pid sleep 300
+	atf_check -s exit:0 test -f daemon.pid
+	atf_check -s exit:0 -o match:"daemon: sleep" ps -p `cat daemon.pid`
+}
+supervisor_pidfile_cleanup() {
+	[ -f daemon.pid ] && kill `cat daemon.pid`
+}
+
+atf_test_case supervisor_pidfile_lock cleanup
+supervisor_pidfile_lock_head() {
+	atf_set "descr" "daemon should refuse to clobber an existing instance"
+}
+supervisor_pidfile_lock_body() {
+	daemon -P daemon.pid sleep 300
+	atf_check -s exit:0 test -f daemon.pid
+	atf_check -s not-exit:0 -e match:"process already running" \
+		daemon -p daemon.pid sleep 300
+}
+supervisor_pidfile_lock_cleanup() {
+	[ -f daemon.pid ] && kill `cat daemon.pid`
+}
+
+atf_test_case title cleanup
+title_head() {
+	atf_set "descr" "daemon should change its process title"
+}
+title_body() {
+	daemon -P daemon.pid -t "I'm a title!" sleep 300
+	atf_check -s exit:0 test -f daemon.pid
+	atf_check -s exit:0 -o match:"daemon: I'm a title!" \
+		ps -p `cat daemon.pid`
+}
+title_cleanup() {
+	[ -f daemon.pid ] && kill `cat daemon.pid`
+}
+
+atf_test_case user cleanup
+user_head() {
+	atf_set "descr" "daemon should drop privileges"
+	atf_set "require.user" "root"
+}
+user_body() {
+	daemon -p sleep.pid -u nobody sleep 300
+	atf_check -s exit:0 test -f sleep.pid
+	atf_check -s exit:0 -o match:"^nobody" ps -up `cat sleep.pid`
+}
+user_cleanup() {
+	[ -f sleep.pid ] && kill `cat sleep.pid`
+}
+
+
+atf_init_test_cases() {
+	atf_add_test_case both_pidfile
+	atf_add_test_case chdir
+	atf_add_test_case child_pidfile
+	atf_add_test_case child_pidfile_lock
+	atf_add_test_case newsyslog
+	atf_add_test_case output_file
+	atf_add_test_case restart_child
+	atf_add_test_case supervisor_pidfile
+	atf_add_test_case supervisor_pidfile_lock
+	atf_add_test_case title
+	atf_add_test_case user
+}