mirror of
https://gitlab.com/chrony/chrony.git
synced 2025-12-03 16:55:07 -05:00
Don't allow the NTP support and asynchronous name resolving to be disabled. pthreads are now a hard requirement. NTP is the primary task of chrony. This functionality doesn't seem to be commonly disabled (allowing only refclocks and manual input). This removes rarely (if ever) used code and simplifies testing.
415 lines
9.8 KiB
Plaintext
415 lines
9.8 KiB
Plaintext
# Copyright (C) Miroslav Lichvar 2009
|
|
#
|
|
# This program is free software; you can redistribute it and/or modify
|
|
# it under the terms of version 2 of the GNU General Public License as
|
|
# published by the Free Software Foundation.
|
|
#
|
|
# This program is distributed in the hope that it will be useful, but
|
|
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
# General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License along
|
|
# with this program; if not, write to the Free Software Foundation, Inc.,
|
|
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
|
|
export LC_ALL=C
|
|
export PATH=${CHRONY_PATH:-../..}:$PATH
|
|
|
|
TEST_DIR=${TEST_DIR:-$(pwd)/tmp}
|
|
TEST_LIBDIR=${TEST_LIBDIR:-$TEST_DIR}
|
|
TEST_LOGDIR=${TEST_LOGDIR:-$TEST_DIR}
|
|
TEST_RUNDIR=${TEST_RUNDIR:-$TEST_DIR}
|
|
TEST_SCFILTER=${TEST_SCFILTER:-0}
|
|
TEST_ROOT_USER=${TEST_ROOT_USER:-root}
|
|
TEST_PRIVDROP_USER=${TEST_PRIVDROP_USER:-nobody}
|
|
|
|
test_start() {
|
|
local user=$(get_user)
|
|
|
|
check_chronyd_features CMDMON || test_skip "CMDMON support disabled"
|
|
|
|
[ "${#TEST_DIR}" -ge 5 ] || test_skip "invalid TEST_DIR"
|
|
|
|
rm -rf "$TEST_DIR"
|
|
mkdir -p "$TEST_DIR" && chmod 700 "$TEST_DIR" || test_skip "could not create $TEST_DIR"
|
|
|
|
[ -d "$TEST_LIBDIR" ] || test_skip "missing $TEST_LIBDIR"
|
|
[ -d "$TEST_LOGDIR" ] || test_skip "missing $TEST_LOGDIR"
|
|
[ -d "$TEST_RUNDIR" ] || test_skip "missing $TEST_RUNDIR"
|
|
|
|
rm -f "$TEST_LIBDIR"/* "$TEST_LOGDIR"/* "$TEST_RUNDIR"/*
|
|
|
|
if [ "$user" != "$TEST_ROOT_USER" ]; then
|
|
id -u "$user" > /dev/null 2> /dev/null || test_skip "missing user $user"
|
|
chown "$user:$(id -g "$user")" "$TEST_DIR" || test_skip "could not chown $TEST_DIR"
|
|
su "$user" -s /bin/sh -c "touch $TEST_DIR/test" 2> /dev/null || \
|
|
test_skip "$user cannot access $TEST_DIR"
|
|
rm "$TEST_DIR/test"
|
|
else
|
|
chown 0:0 "$TEST_DIR" || test_skip "could not chown $TEST_DIR"
|
|
fi
|
|
|
|
echo "Testing $*:"
|
|
}
|
|
|
|
test_pass() {
|
|
echo "PASS"
|
|
exit 0
|
|
}
|
|
|
|
test_fail() {
|
|
echo "FAIL"
|
|
exit 1
|
|
}
|
|
|
|
test_skip() {
|
|
local msg=$1
|
|
|
|
[ -n "$msg" ] && echo "SKIP ($msg)" || echo "SKIP"
|
|
exit 9
|
|
}
|
|
|
|
test_ok() {
|
|
pad_line
|
|
echo -e "\tOK"
|
|
return 0
|
|
}
|
|
|
|
test_bad() {
|
|
pad_line
|
|
echo -e "\tBAD"
|
|
return 1
|
|
}
|
|
|
|
test_error() {
|
|
pad_line
|
|
echo -e "\tERROR"
|
|
return 1
|
|
}
|
|
|
|
chronyd=$(command -v chronyd)
|
|
chronyc=$(command -v chronyc)
|
|
|
|
[ $EUID -eq 0 ] || test_skip "not root"
|
|
|
|
[ -x "$chronyd" ] || test_skip "chronyd not found"
|
|
[ -x "$chronyc" ] || test_skip "chronyc not found"
|
|
|
|
if netstat -aln > /dev/null 2> /dev/null; then
|
|
port_list_command="netstat -aln"
|
|
elif ss -atun > /dev/null 2> /dev/null; then
|
|
port_list_command="ss -atun"
|
|
else
|
|
test_skip "missing netstat or ss"
|
|
fi
|
|
|
|
# Default test testings
|
|
default_minimal_config=0
|
|
default_extra_chronyd_directives=""
|
|
default_extra_chronyd_options=""
|
|
default_clock_control=0
|
|
default_server=127.0.0.1
|
|
default_server_name=127.0.0.1
|
|
default_server_options=""
|
|
default_priv_drop=0
|
|
|
|
# Initialize test settings from their defaults
|
|
for defoptname in ${!default_*}; do
|
|
optname=${defoptname#default_}
|
|
[ -z "${!optname}" ] && declare "$optname"="${!defoptname}"
|
|
done
|
|
|
|
msg_length=0
|
|
pad_line() {
|
|
local line_length=56
|
|
[ $msg_length -lt $line_length ] && \
|
|
printf "%$((line_length - msg_length))s" ""
|
|
msg_length=0
|
|
}
|
|
|
|
# Print aligned message
|
|
test_message() {
|
|
local level=$1 eol=$2
|
|
shift 2
|
|
local msg="$*"
|
|
|
|
while [ "$level" -gt 0 ]; do
|
|
echo -n " "
|
|
level=$((level - 1))
|
|
msg_length=$((msg_length + 2))
|
|
done
|
|
echo -n "$msg"
|
|
|
|
msg_length=$((msg_length + ${#msg}))
|
|
if [ "$eol" -ne 0 ]; then
|
|
echo
|
|
msg_length=0
|
|
fi
|
|
}
|
|
|
|
# Check if chronyd has specified features
|
|
check_chronyd_features() {
|
|
local feature features
|
|
|
|
features=$($chronyd -v | sed 's/.*(\(.*\)).*/\1/')
|
|
|
|
for feature; do
|
|
echo "$features" | grep -q "+$feature" || return 1
|
|
done
|
|
}
|
|
|
|
# Print test settings which differ from default value
|
|
print_nondefaults() {
|
|
local defoptname optname
|
|
|
|
test_message 1 1 "non-default settings:"
|
|
for defoptname in ${!default_*}; do
|
|
optname=${defoptname#default_}
|
|
[ "${!defoptname}" = "${!optname}" ] || \
|
|
test_message 2 1 "$optname"=${!optname}
|
|
done
|
|
}
|
|
|
|
get_conffile() {
|
|
echo "$TEST_DIR/chronyd.conf"
|
|
}
|
|
|
|
get_pidfile() {
|
|
echo "$TEST_RUNDIR/chronyd.pid"
|
|
}
|
|
|
|
get_logfile() {
|
|
echo "$TEST_LOGDIR/chronyd.log"
|
|
}
|
|
|
|
get_cmdsocket() {
|
|
echo "$TEST_RUNDIR/chronyd.sock"
|
|
}
|
|
|
|
get_user() {
|
|
if [ "$priv_drop" -ne 0 ]; then
|
|
echo "$TEST_PRIVDROP_USER"
|
|
else
|
|
echo "$TEST_ROOT_USER"
|
|
fi
|
|
}
|
|
|
|
# Find a free port in the 10000-20000 range (their use is racy)
|
|
get_free_port() {
|
|
local port
|
|
|
|
while true; do
|
|
port=$((RANDOM % 10000 + 10000))
|
|
$port_list_command | grep -q '^\(tcp\|udp\).*[:.]'"$port " && continue
|
|
break
|
|
done
|
|
|
|
echo $port
|
|
}
|
|
|
|
generate_chrony_conf() {
|
|
local user ntpport cmdport
|
|
|
|
user=$(get_user)
|
|
ntpport=$(get_free_port)
|
|
cmdport=$(get_free_port)
|
|
|
|
echo "0.0 10000" > "$TEST_LIBDIR/driftfile"
|
|
echo "1 MD5 abcdefghijklmnopq" > "$TEST_DIR/keys"
|
|
chown "$user:$(id -g "$user")" "$TEST_LIBDIR/driftfile" "$TEST_DIR/keys"
|
|
echo "0.0" > "$TEST_DIR/tempcomp"
|
|
|
|
(
|
|
echo "pidfile $(get_pidfile)"
|
|
echo "bindcmdaddress $(get_cmdsocket)"
|
|
echo "port $ntpport"
|
|
echo "cmdport $cmdport"
|
|
|
|
echo "$extra_chronyd_directives"
|
|
|
|
[ "$minimal_config" -ne 0 ] && exit 0
|
|
|
|
echo "allow"
|
|
echo "cmdallow"
|
|
echo "local"
|
|
|
|
echo "server $server_name port $ntpport minpoll -6 maxpoll -6 $server_options"
|
|
|
|
[ "$server" = "127.0.0.1" ] && echo "bindacqaddress $server"
|
|
echo "bindaddress 127.0.0.1"
|
|
echo "bindcmdaddress 127.0.0.1"
|
|
echo "dumpdir $TEST_RUNDIR"
|
|
echo "logdir $TEST_LOGDIR"
|
|
echo "log tempcomp rawmeasurements refclocks statistics tracking rtc"
|
|
echo "logbanner 0"
|
|
echo "smoothtime 100.0 0.001"
|
|
echo "leapsectz right/UTC"
|
|
echo "dscp 46"
|
|
|
|
echo "include /dev/null"
|
|
echo "keyfile $TEST_DIR/keys"
|
|
echo "driftfile $TEST_LIBDIR/driftfile"
|
|
echo "tempcomp $TEST_DIR/tempcomp 0.1 0 0 0 0"
|
|
|
|
) > "$(get_conffile)"
|
|
}
|
|
|
|
get_chronyd_options() {
|
|
[ "$clock_control" -eq 0 ] && echo "-x"
|
|
echo "-l $(get_logfile)"
|
|
echo "-f $(get_conffile)"
|
|
echo "-u $(get_user)"
|
|
echo "-F $TEST_SCFILTER"
|
|
echo "$extra_chronyd_options"
|
|
}
|
|
|
|
# Start a chronyd instance
|
|
start_chronyd() {
|
|
local pid pidfile=$(get_pidfile) wrapper_options=""
|
|
|
|
print_nondefaults
|
|
test_message 1 0 "starting chronyd"
|
|
|
|
generate_chrony_conf
|
|
|
|
trap stop_chronyd EXIT
|
|
|
|
rm -f "$TEST_LOGDIR"/*.log
|
|
|
|
if [[ $CHRONYD_WRAPPER == *valgrind* ]]; then
|
|
wrapper_options="--log-file=$TEST_DIR/chronyd.valgrind --enable-debuginfod=no"
|
|
fi
|
|
|
|
$CHRONYD_WRAPPER $wrapper_options \
|
|
"$chronyd" $(get_chronyd_options) > "$TEST_DIR/chronyd.out" 2>&1
|
|
|
|
[ $? -eq 0 ] && [ -f "$pidfile" ] && ps -p "$(cat "$pidfile")" > /dev/null && test_ok || test_error
|
|
}
|
|
|
|
wait_for_sync() {
|
|
local prev_length
|
|
|
|
test_message 1 0 "waiting for synchronization"
|
|
prev_length=$msg_length
|
|
|
|
for i in $(seq 1 10); do
|
|
run_chronyc "ntpdata $server" > /dev/null 2>&1 || break
|
|
if check_chronyc_output "Total RX +: [1-9]" > /dev/null 2>&1; then
|
|
msg_length=$prev_length
|
|
test_ok
|
|
return
|
|
fi
|
|
sleep 1
|
|
done
|
|
|
|
msg_length=$prev_length
|
|
test_error
|
|
}
|
|
|
|
# Stop the chronyd instance
|
|
stop_chronyd() {
|
|
local pid pidfile
|
|
|
|
pidfile=$(get_pidfile)
|
|
[ -f "$pidfile" ] || return 0
|
|
|
|
pid=$(cat "$pidfile")
|
|
|
|
test_message 1 0 "stopping chronyd"
|
|
|
|
if ! kill "$pid" 2> /dev/null; then
|
|
test_error
|
|
return
|
|
fi
|
|
|
|
# Wait for the process to terminate (we cannot use "wait")
|
|
while ps -p "$pid" > /dev/null; do
|
|
sleep 0.1
|
|
done
|
|
|
|
test_ok
|
|
|
|
if [ -f "$TEST_DIR/chronyd.valgrind" ]; then
|
|
test_message 2 0 "checking valgrind report"
|
|
! grep -q 'ERROR SUMMARY: [^0]' "$TEST_DIR/chronyd.valgrind" && \
|
|
test_ok || test_bad
|
|
fi
|
|
}
|
|
|
|
# Check chronyd log for expected and unexpected messages
|
|
check_chronyd_messages() {
|
|
local logfile=$(get_logfile)
|
|
|
|
test_message 1 0 "checking chronyd messages"
|
|
|
|
grep -q 'chronyd exiting' "$logfile" && \
|
|
([ "$clock_control" -eq 0 ] || ! grep -q 'Disabled control of system clock' "$logfile") && \
|
|
([ "$clock_control" -ne 0 ] || grep -q 'Disabled control of system clock' "$logfile") && \
|
|
([ "$minimal_config" -ne 0 ] || grep -q 'Frequency .* read from' "$logfile") && \
|
|
grep -q 'chronyd exiting' "$logfile" && \
|
|
! (grep -v '^.\{19\}Z D:' "$logfile" | grep -q 'Could not') && \
|
|
! grep -q 'Disabled command socket' "$logfile" && \
|
|
test_ok || test_bad
|
|
}
|
|
|
|
# Check the number of messages matching a pattern in a specified file
|
|
check_chronyd_message_count() {
|
|
local count pattern=$1 min=$2 max=$3 logfile=$(get_logfile)
|
|
|
|
test_message 1 0 "checking message \"$pattern\""
|
|
|
|
count=$(grep "$pattern" "$(get_logfile)" | wc -l)
|
|
|
|
[ "$min" -le "$count" ] && [ "$count" -le "$max" ] && test_ok || test_bad
|
|
}
|
|
|
|
# Check the logs and dump file for measurements and a clock update
|
|
check_chronyd_files() {
|
|
test_message 1 0 "checking chronyd files"
|
|
|
|
grep -q " $server .* 111 111 1110 " "$TEST_LOGDIR/measurements.log" && \
|
|
[ -f "$TEST_LOGDIR/tempcomp.log" ] && [ "$(wc -l < "$TEST_LOGDIR/tempcomp.log")" -ge 2 ] && \
|
|
test_ok || test_bad
|
|
}
|
|
|
|
# Run a chronyc command
|
|
run_chronyc() {
|
|
local host=$chronyc_host options="-n -m" wrapper_options="" ret=0
|
|
|
|
test_message 1 0 "running chronyc $([ -n "$host" ] && echo "@$host ")$*"
|
|
|
|
if [ -z "$host" ]; then
|
|
host="$(get_cmdsocket)"
|
|
else
|
|
options="$options -p $(grep cmdport "$(get_conffile)" | awk '{print $2}')"
|
|
fi
|
|
|
|
if [[ $CHRONYC_WRAPPER == *valgrind* ]]; then
|
|
wrapper_options="--log-file=$TEST_DIR/chronyc.valgrind --enable-debuginfod=no"
|
|
fi
|
|
|
|
$CHRONYC_WRAPPER $wrapper_options \
|
|
"$chronyc" -h "$host" $options "$@" > "$TEST_DIR/chronyc.out" && \
|
|
test_ok || test_error
|
|
[ $? -ne 0 ] && ret=1
|
|
|
|
if [ -f "$TEST_DIR/chronyc.valgrind" ]; then
|
|
test_message 2 0 "checking valgrind report"
|
|
! grep -q 'ERROR SUMMARY: [^0]' "$TEST_DIR/chronyc.valgrind" && \
|
|
test_ok || test_bad
|
|
[ $? -ne 0 ] && ret=1
|
|
fi
|
|
|
|
return $ret
|
|
}
|
|
|
|
# Compare chronyc output with specified pattern
|
|
check_chronyc_output() {
|
|
local pattern=$1
|
|
|
|
test_message 1 0 "checking chronyc output"
|
|
|
|
[[ "$(cat "$TEST_DIR/chronyc.out")" =~ $pattern ]] && test_ok || test_bad
|
|
}
|