diff options
Diffstat (limited to '')
-rwxr-xr-x | test/system/001-minimal | 13 | ||||
-rwxr-xr-x | test/system/002-extended | 13 | ||||
-rwxr-xr-x | test/system/003-memlock | 15 | ||||
-rwxr-xr-x | test/system/004-priority | 15 | ||||
-rwxr-xr-x | test/system/006-privdrop | 17 | ||||
-rwxr-xr-x | test/system/007-cmdmon | 188 | ||||
-rwxr-xr-x | test/system/008-confload | 83 | ||||
-rwxr-xr-x | test/system/009-binddevice | 24 | ||||
-rwxr-xr-x | test/system/010-nts | 66 | ||||
-rwxr-xr-x | test/system/011-systemd | 140 | ||||
-rwxr-xr-x | test/system/099-scfilter | 24 | ||||
-rwxr-xr-x | test/system/100-clockupdate | 30 | ||||
-rwxr-xr-x | test/system/101-rtc | 19 | ||||
-rwxr-xr-x | test/system/102-hwtimestamp | 28 | ||||
-rwxr-xr-x | test/system/103-refclock | 19 | ||||
-rwxr-xr-x | test/system/104-systemdirs | 19 | ||||
-rwxr-xr-x | test/system/199-scfilter | 24 | ||||
-rwxr-xr-x | test/system/run | 64 | ||||
-rw-r--r-- | test/system/test.common | 375 |
19 files changed, 1176 insertions, 0 deletions
diff --git a/test/system/001-minimal b/test/system/001-minimal new file mode 100755 index 0000000..107fa3f --- /dev/null +++ b/test/system/001-minimal @@ -0,0 +1,13 @@ +#!/usr/bin/env bash + +. ./test.common + +test_start "minimal configuration" + +minimal_config=1 + +start_chronyd || test_fail +stop_chronyd || test_fail +check_chronyd_messages || test_fail + +test_pass diff --git a/test/system/002-extended b/test/system/002-extended new file mode 100755 index 0000000..7a6734f --- /dev/null +++ b/test/system/002-extended @@ -0,0 +1,13 @@ +#!/usr/bin/env bash + +. ./test.common + +test_start "extended configuration" + +start_chronyd || test_fail +wait_for_sync || test_fail +stop_chronyd || test_fail +check_chronyd_messages || test_fail +check_chronyd_files || test_fail + +test_pass diff --git a/test/system/003-memlock b/test/system/003-memlock new file mode 100755 index 0000000..e4ab1bf --- /dev/null +++ b/test/system/003-memlock @@ -0,0 +1,15 @@ +#!/usr/bin/env bash + +. ./test.common + +test_start "memory locking" + +extra_chronyd_options="-m" + +start_chronyd || test_fail +wait_for_sync || test_fail +stop_chronyd || test_fail +check_chronyd_messages || test_fail +check_chronyd_files || test_fail + +test_pass diff --git a/test/system/004-priority b/test/system/004-priority new file mode 100755 index 0000000..bf8a04b --- /dev/null +++ b/test/system/004-priority @@ -0,0 +1,15 @@ +#!/usr/bin/env bash + +. ./test.common + +test_start "process priority" + +extra_chronyd_options="-P 1" + +start_chronyd || test_fail +wait_for_sync || test_fail +stop_chronyd || test_fail +check_chronyd_messages || test_fail +check_chronyd_files || test_fail + +test_pass diff --git a/test/system/006-privdrop b/test/system/006-privdrop new file mode 100755 index 0000000..6d7b0c9 --- /dev/null +++ b/test/system/006-privdrop @@ -0,0 +1,17 @@ +#!/usr/bin/env bash + +. ./test.common + +check_chronyd_features PRIVDROP || test_skip "PRIVDROP support disabled" + +user="nobody" + +test_start "dropping of root privileges" + +minimal_config=1 + +start_chronyd || test_fail +stop_chronyd || test_fail +check_chronyd_messages || test_fail + +test_pass diff --git a/test/system/007-cmdmon b/test/system/007-cmdmon new file mode 100755 index 0000000..f9541d3 --- /dev/null +++ b/test/system/007-cmdmon @@ -0,0 +1,188 @@ +#!/usr/bin/env bash + +. ./test.common + +test_start "chronyc commands" + +start_chronyd || test_fail +wait_for_sync || test_fail + +has_ipv6=$(check_chronyd_features IPV6 && ping6 -c 1 ::1 > /dev/null 2>&1 && echo 1 || echo 0) + +for command in \ + "allow 1.2.3.4" \ + "deny 1.2.3.4" \ + "cmddeny" \ + "cmdallow" \ + "cmddeny 1.2.3.4" \ + "cmdallow 1.2.3.4" \ + "add server 127.123.1.1" \ + "delete 127.123.1.1" \ + "burst 1/1" \ + "cyclelogs" \ + "dfreq 1.0e-3" \ + "doffset -0.1" \ + "dump" \ + "offline" \ + "local off" \ + "local" \ + "online" \ + "onoffline" \ + "maxdelay $server 1e-1" \ + "maxdelaydevratio $server 5.0" \ + "maxdelayratio $server 3.0" \ + "maxpoll $server 12" \ + "maxupdateskew $server 10.0" \ + "minpoll $server 10" \ + "minstratum $server 1" \ + "polltarget $server 10" \ + "refresh" \ + "rekey" \ + "reload sources" \ + "reselect" \ + "reselectdist 1e-3" \ + "reset sources" \ + "selectopts $server -noselect +trust +prefer +require" \ + "smoothtime reset" \ + "smoothtime activate" \ +; do + run_chronyc "$command" || test_fail + check_chronyc_output "^200 OK$" || test_fail +done + +run_chronyc "accheck $server" || test_fail +check_chronyc_output "^208 Access allowed$" || test_fail +run_chronyc "accheck 1.2.3.4" || test_fail +check_chronyc_output "^209 Access denied$" || test_fail + +run_chronyc "cmdaccheck 1.2.3.4" || test_fail +check_chronyc_output "^208 Access allowed$" || test_fail + +run_chronyc "authdata" || test_fail +check_chronyc_output "^Name/IP address Mode KeyID Type KLen Last Atmp NAK Cook CLen +========================================================================= +127\.0\.0\.1 - 0 0 0 - 0 0 0 0$" \ + || test_chronyc + +run_chronyc "clients" || test_fail +check_chronyc_output "^Hostname NTP Drop Int IntL Last Cmd Drop Int Last +=============================================================================== +127\.0\.0\.1 [0-9 ]+ 0 [-0-9 ]+ - [ 0-9]+ 0 0 - -$" \ + || test_fail + +run_chronyc "ntpdata $server" || test_fail +check_chronyc_output "^Remote address : 127\.0\.0\.1 \(7F000001\) +Remote port : [0-9]+ +Local address : 127\.0\.0\.1 \(7F000001\) +Leap status : Normal +Version : 4 +Mode : Server +Stratum : 10 +Poll interval : (-6|[0-9]+) \([0-9]+ seconds\) +Precision : [0-9 +-]+ \(0\.[0-9]+ seconds\) +Root delay : 0\.000000 seconds +Root dispersion : 0\.000000 seconds +Reference ID : 7F7F0101 \(\) +Reference time : [A-Za-z0-9: ]+ +Offset : [+-]0\.......... seconds +Peer delay : 0\.......... seconds +Peer dispersion : 0\.......... seconds +Response time : 0\.......... seconds +Jitter asymmetry: \+0\.00 +NTP tests : 111 111 1110 +Interleaved : No +Authenticated : No +TX timestamping : (Daemon|Kernel) +RX timestamping : (Daemon|Kernel) +Total TX : [0-9]+ +Total RX : [0-9]+ +Total valid RX : [0-9]+ +Total good RX : [0-9]+$" || test_fail + +run_chronyc "selectdata" || test_fail +check_chronyc_output "^S Name/IP Address Auth COpts EOpts Last Score Interval Leap +======================================================================= +s 127\.0\.0\.1 N -PTR- -PTR- 0 1\.0 \+0ns \+0ns \?$" || test_fail + +run_chronyc "serverstats" || test_fail +check_chronyc_output "^NTP packets received : [0-9]+ +NTP packets dropped : 0 +Command packets received : [0-9]+ +Command packets dropped : 0 +Client log records dropped : 0 +NTS-KE connections accepted: 0 +NTS-KE connections dropped : 0 +Authenticated NTP packets : 0 +Interleaved NTP packets : 0 +NTP timestamps held : 0 +NTP timestamp span : 0 +NTP daemon RX timestamps : 0 +NTP daemon TX timestamps : [0-9]+ +NTP kernel RX timestamps : [0-9]+ +NTP kernel TX timestamps : 0 +NTP hardware RX timestamps : 0 +NTP hardware TX timestamps : 0$"|| test_fail + +run_chronyc "manual on" || test_fail +check_chronyc_output "^200 OK$" || test_fail + +run_chronyc "settime now" || test_fail +check_chronyc_output "^200 OK +Clock was.*$" || test_fail + +run_chronyc "manual delete 0" || test_fail +check_chronyc_output "^200 OK$" || test_fail + +run_chronyc "settime now" || test_fail +check_chronyc_output "^200 OK +Clock was.*$" || test_fail + +run_chronyc "manual list" || test_fail +check_chronyc_output "^210 n_samples = 1 +# Date Time\(UTC\) Slewed Original Residual +======================================================= + 0.*$" || test_fail + +run_chronyc "manual reset" || test_fail +check_chronyc_output "^200 OK$" || test_fail + +run_chronyc "manual off" || test_fail +check_chronyc_output "^200 OK$" || test_fail + +run_chronyc "shutdown" || test_fail +check_chronyc_output "^200 OK$" || test_fail + +stop_chronyd || test_fail +check_chronyd_messages || test_fail +start_chronyd || test_fail + +run_chronyc "makestep" && test_fail +check_chronyc_output "500 Failure" || test_fail + +run_chronyc "trimrtc" && test_fail +check_chronyc_output "513 RTC driver not running" || test_fail + +run_chronyc "writertc" && test_fail +check_chronyc_output "513 RTC driver not running" || test_fail + +chronyc_host=127.0.0.1 + +run_chronyc "tracking" || test_fail +check_chronyc_output "^Reference ID" || test_fail + +run_chronyc "makestep" && test_fail +check_chronyc_output "^501 Not authorised$" || test_fail + +if [ "$has_ipv6" = "1" ]; then + chronyc_host=::1 + + run_chronyc "tracking" || test_fail + check_chronyc_output "^Reference ID" || test_fail + + run_chronyc "makestep" && test_fail + check_chronyc_output "^501 Not authorised$" || test_fail +fi + +stop_chronyd || test_fail + +test_pass diff --git a/test/system/008-confload b/test/system/008-confload new file mode 100755 index 0000000..7e80698 --- /dev/null +++ b/test/system/008-confload @@ -0,0 +1,83 @@ +#!/usr/bin/env bash + +. ./test.common + +test_start "loading of configuration" + +minimal_config=1 +extra_chronyd_directives=" +include $TEST_DIR/conf1.d/conf.1 +confdir $TEST_DIR/conf1.d +confdir $TEST_DIR/conf2.d $TEST_DIR/conf3.d $TEST_DIR/conf4.d +sourcedir $TEST_DIR/conf5.d +include $TEST_DIR/conf1.d/conf.2" + +mkdir $TEST_DIR/conf{1,2,3,4,5}.d + +echo "server 127.123.1.1" > $TEST_DIR/conf1.d/conf.1 +echo "server 127.123.1.2" > $TEST_DIR/conf1.d/conf.2 +echo "server 127.123.1.3" > $TEST_DIR/conf1.d/3.conf +echo "server 127.123.1.4" > $TEST_DIR/conf1.d/4.conf +echo "server 127.123.2.2" > $TEST_DIR/conf2.d/2.conf +echo "server 127.123.2.3" > $TEST_DIR/conf2.d/3.conf +echo "server 127.123.3.1" > $TEST_DIR/conf3.d/1.conf +echo "server 127.123.3.2" > $TEST_DIR/conf3.d/2.conf +echo "server 127.123.3.3" > $TEST_DIR/conf3.d/3.conf +echo "server 127.123.4.1" > $TEST_DIR/conf4.d/1.conf +echo "server 127.123.4.2" > $TEST_DIR/conf4.d/2.conf +echo "server 127.123.4.3" > $TEST_DIR/conf4.d/3.conf +echo "server 127.123.4.4" > $TEST_DIR/conf4.d/4.conf +echo "server 127.123.5.1" > $TEST_DIR/conf5.d/1.sources +echo "server 127.123.5.2" > $TEST_DIR/conf5.d/2.sources +echo "server 127.123.5.3" > $TEST_DIR/conf5.d/3.sources +echo "server 127.123.5.4" > $TEST_DIR/conf5.d/4.sources +echo "server 127.123.5.5" > $TEST_DIR/conf5.d/5.sources + +start_chronyd || test_fail + +run_chronyc "sources" || test_fail +check_chronyc_output "^[^=]* +=* +.. 127\.123\.1\.1 [^^]* +.. 127\.123\.1\.3 [^^]* +.. 127\.123\.1\.4 [^^]* +.. 127\.123\.3\.1 [^^]* +.. 127\.123\.2\.2 [^^]* +.. 127\.123\.2\.3 [^^]* +.. 127\.123\.4\.4 [^^]* +.. 127\.123\.1\.2 [^^]* +.. 127\.123\.5\.1 [^^]* +.. 127\.123\.5\.2 [^^]* +.. 127\.123\.5\.3 [^^]* +.. 127\.123\.5\.4 [^^]* +.. 127\.123\.5\.5 [^^]*$" || test_fail + +rm $TEST_DIR/conf5.d/1.sources +echo "server 127.123.5.2 minpoll 5" > $TEST_DIR/conf5.d/2.sources +echo "server 127.123.5.3 minpoll 7" > $TEST_DIR/conf5.d/3.sources +echo > $TEST_DIR/conf5.d/4.sources +echo "server 127.123.5.5" >> $TEST_DIR/conf5.d/5.sources +echo "server 127.123.5.6" > $TEST_DIR/conf5.d/6.sources + +run_chronyc "reload sources" || test_fail + +run_chronyc "sources" || test_fail +check_chronyc_output "^[^=]* +=* +.. 127\.123\.1\.1 [^^]* +.. 127\.123\.1\.3 [^^]* +.. 127\.123\.1\.4 [^^]* +.. 127\.123\.3\.1 [^^]* +.. 127\.123\.2\.2 [^^]* +.. 127\.123\.2\.3 [^^]* +.. 127\.123\.4\.4 [^^]* +.. 127\.123\.1\.2 *[05] 6 [^^]* +.. 127\.123\.5\.5 [^^]* +.. 127\.123\.5\.2 *[05] 5 [^^]* +.. 127\.123\.5\.3 *[05] 7 [^^]* +.. 127\.123\.5\.6 [^^]*$" || test_fail + +stop_chronyd || test_fail +check_chronyd_message_count "Could not add source" 1 1 || test_fail + +test_pass diff --git a/test/system/009-binddevice b/test/system/009-binddevice new file mode 100755 index 0000000..fc64ae2 --- /dev/null +++ b/test/system/009-binddevice @@ -0,0 +1,24 @@ +#!/usr/bin/env bash + +. ./test.common + +[ "$(uname -s)" = "Linux" ] || test_skip "non-Linux system" + +test_start "binddevice directives" + +extra_chronyd_directives=" +binddevice lo +bindacqdevice lo +bindcmddevice lo" + +start_chronyd || test_fail +wait_for_sync || test_fail + +run_chronyc "ntpdata $server" || test_fail +check_chronyc_output "^Remote address" || test_fail + +stop_chronyd || test_fail +check_chronyd_messages || test_fail +check_chronyd_files || test_fail + +test_pass diff --git a/test/system/010-nts b/test/system/010-nts new file mode 100755 index 0000000..8d92bbc --- /dev/null +++ b/test/system/010-nts @@ -0,0 +1,66 @@ +#!/usr/bin/env bash + +. ./test.common + +check_chronyd_features NTS || test_skip "NTS support disabled" +certtool --help &> /dev/null || test_skip "certtool missing" + +test_start "NTS authentication" + +cat > $TEST_DIR/cert.cfg <<EOF +cn = "chrony-nts-test" +dns_name = "chrony-nts-test" +ip_address = "$server" +serial = 001 +activation_date = "$[$(date '+%Y') - 1]-01-01 00:00:00 UTC" +expiration_date = "$[$(date '+%Y') + 2]-01-01 00:00:00 UTC" +signing_key +encryption_key +EOF + +certtool --generate-privkey --key-type=ed25519 --outfile $TEST_DIR/server.key \ + &> $TEST_DIR/certtool.log +certtool --generate-self-signed --load-privkey $TEST_DIR/server.key \ + --template $TEST_DIR/cert.cfg --outfile $TEST_DIR/server.crt &>> $TEST_DIR/certtool.log +chown $user $TEST_DIR/server.* + +ntpport=$(get_free_port) +ntsport=$(get_free_port) + +server_options="port $ntpport nts ntsport $ntsport" +extra_chronyd_directives=" +port $ntpport +ntsport $ntsport +ntsserverkey $TEST_DIR/server.key +ntsservercert $TEST_DIR/server.crt +ntstrustedcerts $TEST_DIR/server.crt +ntsdumpdir $TEST_LIBDIR +ntsprocesses 3" + +start_chronyd || test_fail +wait_for_sync || test_fail + +run_chronyc "authdata" || test_fail +check_chronyc_output "^Name/IP address Mode KeyID Type KLen Last Atmp NAK Cook CLen +========================================================================= +127\.0\.0\.1 NTS 1 (30|15) (128|256) [0-9] 0 0 [78] ( 64|100)$" || test_fail + +stop_chronyd || test_fail +check_chronyd_messages || test_fail +check_chronyd_files || test_fail + +server_options="port $ntpport nts ntsport $((ntsport + 1))" + +start_chronyd || test_fail +wait_for_sync || test_fail + +run_chronyc "authdata" || test_fail +check_chronyc_output "^Name/IP address Mode KeyID Type KLen Last Atmp NAK Cook CLen +========================================================================= +127\.0\.0\.1 NTS 1 (30|15) (128|256) [0-9] 0 0 [78] ( 64|100)$" || test_fail + +stop_chronyd || test_fail +check_chronyd_messages || test_fail +check_chronyd_files || test_fail + +test_pass diff --git a/test/system/011-systemd b/test/system/011-systemd new file mode 100755 index 0000000..1049966 --- /dev/null +++ b/test/system/011-systemd @@ -0,0 +1,140 @@ +#!/usr/bin/env bash + +. ./test.common + +check_chronyd_features NTS || test_skip "NTS support disabled" +certtool --help &> /dev/null || test_skip "certtool missing" +check_chronyd_features DEBUG || test_skip "DEBUG support disabled" +systemd-socket-activate -h &> /dev/null || test_skip "systemd-socket-activate missing" +has_ipv6=$(check_chronyd_features IPV6 && ping6 -c 1 ::1 > /dev/null 2>&1 && echo 1 || echo 0) + +test_start "systemd socket activation" + +cat > $TEST_DIR/cert.cfg <<EOF +cn = "chrony-nts-test" +dns_name = "chrony-nts-test" +ip_address = "$server" +$([ "$has_ipv6" = "1" ] && echo 'ip_address = "::1"') +serial = 001 +activation_date = "$[$(date '+%Y') - 1]-01-01 00:00:00 UTC" +expiration_date = "$[$(date '+%Y') + 2]-01-01 00:00:00 UTC" +signing_key +encryption_key +EOF + +certtool --generate-privkey --key-type=ed25519 --outfile $TEST_DIR/server.key \ + &> $TEST_DIR/certtool.log +certtool --generate-self-signed --load-privkey $TEST_DIR/server.key \ + --template $TEST_DIR/cert.cfg --outfile $TEST_DIR/server.crt &>> $TEST_DIR/certtool.log +chown $user $TEST_DIR/server.* + +ntpport=$(get_free_port) +ntsport=$(get_free_port) + +server_options="port $ntpport nts ntsport $ntsport" +extra_chronyd_directives=" +port $ntpport +ntsport $ntsport +ntsserverkey $TEST_DIR/server.key +ntsservercert $TEST_DIR/server.crt +ntstrustedcerts $TEST_DIR/server.crt +ntsdumpdir $TEST_LIBDIR +ntsprocesses 3" + +if [ "$has_ipv6" = "1" ]; then + extra_chronyd_directives="$extra_chronyd_directives + bindaddress ::1 + server ::1 minpoll -6 maxpoll -6 $server_options" +fi + +# enable debug logging +extra_chronyd_options="-L -1" +# Hack to trigger systemd-socket-activate to activate the service. Normally, +# chronyd.service would be configured with the WantedBy= directive so it starts +# without waiting for socket activation. +# (https://0pointer.de/blog/projects/socket-activation.html). +for i in $(seq 10); do + sleep 1 + (echo "wake up" > /dev/udp/127.0.0.1/$ntpport) 2>/dev/null + (echo "wake up" > /dev/tcp/127.0.0.1/$ntsport) 2>/dev/null +done & + +# Test with UDP sockets (unfortunately systemd-socket-activate doesn't support +# both datagram and stream sockets in the same invocation: +# https://github.com/systemd/systemd/issues/9983). +CHRONYD_WRAPPER="systemd-socket-activate \ + --datagram \ + --listen 127.0.0.1:$ntpport \ + --listen 127.0.0.1:$ntsport" +if [ "$has_ipv6" = "1" ]; then + CHRONYD_WRAPPER="$CHRONYD_WRAPPER \ + --listen [::1]:$ntpport \ + --listen [::1]:$ntsport" +fi + +start_chronyd || test_fail +wait_for_sync || test_fail + +if [ "$has_ipv6" = "1" ]; then + run_chronyc "ntpdata ::1" || test_fail + check_chronyc_output "Total RX +: [1-9]" || test_fail +fi +run_chronyc "authdata" || test_fail +check_chronyc_output "^Name/IP address Mode KeyID Type KLen Last Atmp NAK Cook CLen +=========================================================================\ +$([ "$has_ipv6" = "1" ] && printf "\n%s\n" '::1 NTS 1 (30|15) (128|256) [0-9] 0 0 [78] ( 64|100)') +127\.0\.0\.1 NTS 1 (30|15) (128|256) [0-9] 0 0 [78] ( 64|100)$" || test_fail + +stop_chronyd || test_fail +# DGRAM ntpport socket should be used +check_chronyd_message_count "Reusing UDPv4 socket fd=3 local=127.0.0.1:$ntpport" 1 1 || test_fail +# DGRAM ntsport socket should be ignored +check_chronyd_message_count "Reusing TCPv4 socket fd=4 local=127.0.0.1:$ntsport" 0 0 || test_fail +if [ "$has_ipv6" = "1" ]; then + # DGRAM ntpport socket should be used + check_chronyd_message_count "Reusing UDPv6 socket fd=5 local=\[::1\]:$ntpport" 1 1 || test_fail + # DGRAM ntsport socket should be ignored + check_chronyd_message_count "Reusing TCPv6 socket fd=6 local=\[::1\]:$ntsport" 0 0 || test_fail +fi + +check_chronyd_messages || test_fail +check_chronyd_files || test_fail + +# Test with TCP sockets +CHRONYD_WRAPPER="systemd-socket-activate \ + --listen 127.0.0.1:$ntpport \ + --listen 127.0.0.1:$ntsport" +if [ "$has_ipv6" = "1" ]; then + CHRONYD_WRAPPER="$CHRONYD_WRAPPER \ + --listen [::1]:$ntpport \ + --listen [::1]:$ntsport" +fi + +start_chronyd || test_fail +wait_for_sync || test_fail + +if [ "$has_ipv6" = "1" ]; then + run_chronyc "ntpdata ::1" || test_fail + check_chronyc_output "Total RX +: [1-9]" || test_fail +fi +run_chronyc "authdata" || test_fail +check_chronyc_output "^Name/IP address Mode KeyID Type KLen Last Atmp NAK Cook CLen +=========================================================================\ +$([ "$has_ipv6" = "1" ] && printf "\n%s\n" '::1 NTS 1 (30|15) (128|256) [0-9] 0 0 [78] ( 64|100)') +127\.0\.0\.1 NTS 1 (30|15) (128|256) [0-9] 0 0 [78] ( 64|100)$" || test_fail + +stop_chronyd || test_fail +# STREAM ntpport should be ignored +check_chronyd_message_count "Reusing TCPv4 socket fd=3 local=127.0.0.1:$ntpport" 0 0 || test_fail +# STREAM ntsport should be used +check_chronyd_message_count "Reusing TCPv4 socket fd=4 local=127.0.0.1:$ntsport" 1 1 || test_fail +if [ "$has_ipv6" = "1" ]; then + # STREAM ntpport should be ignored + check_chronyd_message_count "Reusing TCPv6 socket fd=5 local=\[::1\]:$ntpport" 0 0 || test_fail + # STREAM ntsport should be used + check_chronyd_message_count "Reusing TCPv6 socket fd=6 local=\[::1\]:$ntsport" 1 1 || test_fail +fi +check_chronyd_messages || test_fail +check_chronyd_files || test_fail + +test_pass diff --git a/test/system/099-scfilter b/test/system/099-scfilter new file mode 100755 index 0000000..7be02bd --- /dev/null +++ b/test/system/099-scfilter @@ -0,0 +1,24 @@ +#!/usr/bin/env bash + +. ./test.common + +check_chronyd_features SCFILTER || test_skip "SCFILTER support disabled" + +test_start "system call filter in non-destructive tests" + +for level in 1 2 -1 -2; do + test_message 1 1 "level $level:" + for test in 0[0-8][0-9]-*[^_]; do + test_message 2 0 "$test" + TEST_SCFILTER=$level "./$test" > /dev/null 2> /dev/null + result=$? + + if [ $result != 0 ] && [ $result != 9 ] ; then + test_bad + test_fail + fi + test_ok + done +done + +test_pass diff --git a/test/system/100-clockupdate b/test/system/100-clockupdate new file mode 100755 index 0000000..191e461 --- /dev/null +++ b/test/system/100-clockupdate @@ -0,0 +1,30 @@ +#!/usr/bin/env bash + +. ./test.common + +test_start "update of system clock" + +clock_control=1 +minimal_config=1 + +start_chronyd || test_fail +run_chronyc "dfreq 1e-3" || test_fail +check_chronyc_output "200 OK" || test_fail + +before=$(date '+%s') +run_chronyc "doffset -1.0" || test_fail +check_chronyc_output "200 OK" || test_fail +run_chronyc "makestep" || test_fail +check_chronyc_output "200 OK" || test_fail +after=$(date '+%s') + +test_message 1 0 "checking system clock" +[ "$before" -lt "$after" ] && test_ok || test_bad || test_fail + +run_chronyc "doffset 1.0" || test_fail +run_chronyc "makestep" || test_fail +stop_chronyd || test_fail +check_chronyd_messages || test_fail +check_chronyd_message_count "System clock was stepped by" 2 2 || test_fail + +test_pass diff --git a/test/system/101-rtc b/test/system/101-rtc new file mode 100755 index 0000000..68bce68 --- /dev/null +++ b/test/system/101-rtc @@ -0,0 +1,19 @@ +#!/usr/bin/env bash + +. ./test.common + +check_chronyd_features RTC || test_skip "RTC support disabled" +[ -c "/dev/rtc" ] || test_skip "missing /dev/rtc" + +test_start "real-time clock" + +minimal_config=1 +extra_chronyd_options="-s" +extra_chronyd_directives="rtcfile $TEST_DIR/rtcfile" +echo "1 $(date +%s) 0.0 0.0" > "$TEST_DIR/rtcfile" + +start_chronyd || test_fail +stop_chronyd || test_fail +check_chronyd_message_count "\(clock off from RTC\|RTC time before last\|Could not \(enable\|disable\) RTC interrupt\)" 1 1 || test_fail + +test_pass diff --git a/test/system/102-hwtimestamp b/test/system/102-hwtimestamp new file mode 100755 index 0000000..6b86d20 --- /dev/null +++ b/test/system/102-hwtimestamp @@ -0,0 +1,28 @@ +#!/usr/bin/env bash + +. ./test.common + +[ "$(uname -s)" = "Linux" ] || test_skip "non-Linux system" + +hwts_iface="" +for iface_path in /sys/class/net/*; do + iface=$(basename "$iface_path") + if ethtool -T "$iface" 2> /dev/null | grep -q ' all\($\| \)'; then + hwts_iface="$iface" + break + fi +done + +[ -n "$hwts_iface" ] || test_skip "no HW timestamping interface found" + +test_start "hardware timestamping" + +minimal_config=1 +extra_chronyd_directives="hwtimestamp $hwts_iface" + +start_chronyd || test_fail +stop_chronyd || test_fail +check_chronyd_messages || test_fail +check_chronyd_message_count "Enabled HW timestamping on $hwts_iface" 1 1 || test_fail + +test_pass diff --git a/test/system/103-refclock b/test/system/103-refclock new file mode 100755 index 0000000..e5b74e0 --- /dev/null +++ b/test/system/103-refclock @@ -0,0 +1,19 @@ +#!/usr/bin/env bash + +. ./test.common + +check_chronyd_features REFCLOCK || test_skip "refclock support disabled" + +test_start "reference clocks" + +extra_chronyd_directives=" +refclock SOCK $TEST_DIR/refclock.sock +refclock SHM 100" + +start_chronyd || test_fail +wait_for_sync || test_fail +stop_chronyd || test_fail +check_chronyd_messages || test_fail +check_chronyd_files || test_fail + +test_pass diff --git a/test/system/104-systemdirs b/test/system/104-systemdirs new file mode 100755 index 0000000..15508dc --- /dev/null +++ b/test/system/104-systemdirs @@ -0,0 +1,19 @@ +#!/usr/bin/env bash + +TEST_LIBDIR=${CHRONY_LIBDIR:-/var/lib/chrony} +TEST_LOGDIR=${CHRONY_LOGDIR:-/var/log/chrony} +TEST_RUNDIR=${CHRONY_RUNDIR:-/var/run/chrony} + +. ./test.common + +user=$(ls -ld "$TEST_RUNDIR" 2> /dev/null | awk '{print $3}') + +test_start "system directories" + +start_chronyd || test_fail +wait_for_sync || test_fail +stop_chronyd || test_fail +check_chronyd_messages || test_fail +check_chronyd_files || test_fail + +test_pass diff --git a/test/system/199-scfilter b/test/system/199-scfilter new file mode 100755 index 0000000..40441b7 --- /dev/null +++ b/test/system/199-scfilter @@ -0,0 +1,24 @@ +#!/usr/bin/env bash + +. ./test.common + +check_chronyd_features SCFILTER || test_skip "SCFILTER support disabled" + +test_start "system call filter in destructive tests" + +for level in 1 2 -1 -2; do + test_message 1 1 "level $level:" + for test in 1[0-8][0-9]-*[^_]; do + test_message 2 0 "$test" + TEST_SCFILTER=$level "./$test" > /dev/null 2> /dev/null + result=$? + + if [ $result != 0 ] && [ $result != 9 ] ; then + test_bad + test_fail + fi + test_ok + done +done + +test_pass diff --git a/test/system/run b/test/system/run new file mode 100755 index 0000000..5516ed4 --- /dev/null +++ b/test/system/run @@ -0,0 +1,64 @@ +#!/usr/bin/env bash + +print_help() { + echo "$1 [-a] [-d] [TEST]..." +} + +run_test() { + local result name=$1 + + if [ $destructive -ne 1 ] && [[ "$name" == 1[0-9][0-9]-* ]]; then + echo "SKIP (destructive test)" + return 9 + fi + + ./$name + result=$? + + if [ $result -ne 0 -a $result -ne 9 ]; then + if [ $abort_on_fail -ne 0 ]; then + exit 1 + fi + fi + + return $result +} + +passed=() failed=() skipped=() + +abort_on_fail=0 +destructive=0 + +while getopts ":ad" opt; do + case $opt in + a) abort_on_fail=1;; + d) destructive=1;; + *) print_help "$0"; exit 3;; + esac +done + +shift $[$OPTIND - 1] + +[ $# -gt 0 ] && tests=($@) || tests=([0-9]*-*[^_]) + +for test in "${tests[@]}"; do + printf "%s " "$test" + run_test $test + result=$? + echo + + case $result in + 0) passed=(${passed[@]} $test);; + 9) skipped=(${skipped[@]} $test);; + *) failed=(${failed[@]} $test);; + esac +done + +echo +echo "SUMMARY:" +echo " TOTAL $[${#passed[@]} + ${#failed[@]} + ${#skipped[@]}]" +echo " PASSED ${#passed[@]}" +echo " FAILED ${#failed[@]} (${failed[@]})" +echo " SKIPPED ${#skipped[@]} (${skipped[@]})" + +[ ${#failed[@]} -eq 0 ] diff --git a/test/system/test.common b/test/system/test.common new file mode 100644 index 0000000..c389b48 --- /dev/null +++ b/test/system/test.common @@ -0,0 +1,375 @@ +# 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_start() { + check_chronyd_features NTP CMDMON || test_skip "NTP/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" != "root" ]; 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_user=root + +# 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" +} + +# 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 ntpport cmdport + + 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 $user" + echo "-F $TEST_SCFILTER" + echo "$extra_chronyd_options" +} + +# Start a chronyd instance +start_chronyd() { + local pid pidfile=$(get_pidfile) + + print_nondefaults + test_message 1 0 "starting chronyd" + + generate_chrony_conf + + trap stop_chronyd EXIT + + rm -f "$TEST_LOGDIR"/*.log + + $CHRONYD_WRAPPER "$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 +} + +# 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" + + 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 + + $CHRONYC_WRAPPER "$chronyc" -h "$host" $options "$@" > "$TEST_DIR/chronyc.out" && \ + test_ok || test_error +} + +# 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 +} |