2 # SPDX-License-Identifier: LGPL-2.1-or-later
7 # shellcheck source=test/units/test-control.sh
8 .
"$(dirname "$0")"/test-control.sh
9 # shellcheck source=test/units/util.sh
10 .
"$(dirname "$0")"/util.sh
12 testcase_timedatectl
() {
13 timedatectl
--no-pager --help
17 timedatectl
--no-ask-password
18 timedatectl status
--machine=testuser@.
host
21 timedatectl show
--all
22 timedatectl show
-p NTP
23 timedatectl show
-p NTP
--value
24 timedatectl list-timezones
26 if ! systemd-detect-virt
-qc; then
27 systemctl
enable --runtime --now systemd-timesyncd
28 timedatectl timesync-status
29 timedatectl show-timesync
34 if [[ -f /tmp
/timezone.bak
]]; then
35 mv /tmp
/timezone.bak
/etc
/timezone
44 # Debian/Ubuntu specific file
45 if [[ -f /etc
/timezone
]]; then
46 mv /etc
/timezone
/tmp
/timezone.bak
49 trap restore_timezone RETURN
51 if [[ -L /etc
/localtime
]]; then
52 ORIG_TZ
=$
(readlink
/etc
/localtime |
sed 's#^.*zoneinfo/##')
53 echo "original tz: $ORIG_TZ"
56 echo 'timedatectl works'
57 assert_in
"Local time:" "$(timedatectl --no-pager)"
59 echo 'change timezone'
60 assert_eq
"$(timedatectl --no-pager set-timezone Europe/Kyiv 2>&1)" ""
61 assert_eq
"$(readlink /etc/localtime | sed 's#^.*zoneinfo/##')" "Europe/Kyiv"
62 if [[ -f /etc
/timezone
]]; then
63 assert_eq
"$(cat /etc/timezone)" "Europe/Kyiv"
65 assert_in
"Time zone: Europe/Kyiv \(EES*T, \+0[0-9]00\)" "$(timedatectl)"
67 if [[ -n "$ORIG_TZ" ]]; then
68 echo 'reset timezone to original'
69 assert_eq
"$(timedatectl set-timezone "$ORIG_TZ" 2>&1)" ""
70 assert_eq
"$(readlink /etc/localtime | sed 's#^.*zoneinfo/##')" "$ORIG_TZ"
71 if [[ -f /etc
/timezone
]]; then
72 assert_eq
"$(cat /etc/timezone)" "$ORIG_TZ"
78 if [[ -e /etc
/adjtime.bak
]]; then
79 mv /etc
/adjtime.bak
/etc
/adjtime
85 check_adjtime_not_exist
() {
86 if [[ -e /etc
/adjtime
]]; then
87 echo "/etc/adjtime unexpectedly exists." >&2
93 # test setting UTC vs. LOCAL in /etc/adjtime
94 if [[ -e /etc
/adjtime
]]; then
95 mv /etc
/adjtime
/etc
/adjtime.bak
98 trap restore_adjtime RETURN
100 echo 'no adjtime file'
102 timedatectl set-local-rtc
0
103 check_adjtime_not_exist
104 timedatectl set-local-rtc
1
105 assert_eq
"$(cat /etc/adjtime)" "0.0 0 0
108 timedatectl set-local-rtc
0
109 check_adjtime_not_exist
111 echo 'UTC set in adjtime file'
112 printf '0.0 0 0\n0\nUTC\n' >/etc
/adjtime
113 timedatectl set-local-rtc
0
114 assert_eq
"$(cat /etc/adjtime)" "0.0 0 0
117 timedatectl set-local-rtc
1
118 assert_eq
"$(cat /etc/adjtime)" "0.0 0 0
122 echo 'non-zero values in adjtime file'
123 printf '0.1 123 0\n0\nLOCAL\n' >/etc
/adjtime
124 timedatectl set-local-rtc
0
125 assert_eq
"$(cat /etc/adjtime)" "0.1 123 0
128 timedatectl set-local-rtc
1
129 assert_eq
"$(cat /etc/adjtime)" "0.1 123 0
133 echo 'fourth line adjtime file'
134 printf '0.0 0 0\n0\nLOCAL\nsomethingelse\n' >/etc
/adjtime
135 timedatectl set-local-rtc
0
136 assert_eq
"$(cat /etc/adjtime)" "0.0 0 0
140 timedatectl set-local-rtc
1
141 assert_eq
"$(cat /etc/adjtime)" "0.0 0 0
146 echo 'no final newline in adjtime file'
147 printf '0.0 0 0\n0\nUTC' >/etc
/adjtime
148 timedatectl set-local-rtc
0
149 check_adjtime_not_exist
150 printf '0.0 0 0\n0\nUTC' >/etc
/adjtime
151 timedatectl set-local-rtc
1
152 assert_eq
"$(cat /etc/adjtime)" "0.0 0 0
156 echo 'only one line in adjtime file'
157 printf '0.0 0 0\n' >/etc
/adjtime
158 timedatectl set-local-rtc
0
159 check_adjtime_not_exist
160 printf '0.0 0 0\n' >/etc
/adjtime
161 timedatectl set-local-rtc
1
162 assert_eq
"$(cat /etc/adjtime)" "0.0 0 0
166 echo 'only one line in adjtime file, no final newline'
167 printf '0.0 0 0' >/etc
/adjtime
168 timedatectl set-local-rtc
0
169 check_adjtime_not_exist
170 printf '0.0 0 0' >/etc
/adjtime
171 timedatectl set-local-rtc
1
172 assert_eq
"$(cat /etc/adjtime)" "0.0 0 0
176 echo 'only two lines in adjtime file'
177 printf '0.0 0 0\n0\n' >/etc
/adjtime
178 timedatectl set-local-rtc
0
179 check_adjtime_not_exist
180 printf '0.0 0 0\n0\n' >/etc
/adjtime
181 timedatectl set-local-rtc
1
182 assert_eq
"$(cat /etc/adjtime)" "0.0 0 0
186 echo 'only two lines in adjtime file, no final newline'
187 printf '0.0 0 0\n0' >/etc
/adjtime
188 timedatectl set-local-rtc
0
189 check_adjtime_not_exist
190 printf '0.0 0 0\n0' >/etc
/adjtime
191 timedatectl set-local-rtc
1
192 assert_eq
"$(cat /etc/adjtime)" "0.0 0 0
196 echo 'unknown value in 3rd line of adjtime file'
197 printf '0.0 0 0\n0\nFOO\n' >/etc
/adjtime
198 timedatectl set-local-rtc
0
199 check_adjtime_not_exist
200 printf '0.0 0 0\n0\nFOO\n' >/etc
/adjtime
201 timedatectl set-local-rtc
1
202 assert_eq
"$(cat /etc/adjtime)" "0.0 0 0
211 [[ "$(busctl get-property org.freedesktop.timedate1 /org/freedesktop/timedate1 org.freedesktop.timedate1 NTP)" == "b $value" ]] && return 0
218 assert_timedated_signal
() {
219 local timestamp
="${1:?}"
221 local args
=(-q -n 1 --since="$timestamp" -p info
-t busctl
)
226 if journalctl
"${args[@]}" --grep .
; then
227 [[ "$(journalctl "${args[@]}" -o cat | jq -r '.payload.data[1].NTP.data')" == "$value" ]];
237 assert_timesyncd_state
() {
241 [[ "$(systemctl show systemd-timesyncd.service -P ActiveState)" == "$state" ]] && return 0
249 # timesyncd has ConditionVirtualization=!container by default; drop/mock that for testing
250 if systemd-detect-virt
--container --quiet; then
251 systemctl disable
--quiet --now systemd-timesyncd
252 mkdir
-p /run
/systemd
/system
/systemd-timesyncd.service.d
253 cat >/run
/systemd
/system
/systemd-timesyncd.service.d
/container.conf
<<EOF
255 ConditionVirtualization=
261 ExecStart=sleep infinity
263 systemctl daemon-reload
266 systemd-run
--unit busctl-monitor.service
--service-type=notify \
267 busctl monitor
--json=short
--match="type=signal,sender=org.freedesktop.timedate1,member=PropertiesChanged,path=/org/freedesktop/timedate1"
270 ts
="$(date +"%F
%T.
%6N
")"
271 timedatectl set-ntp false
272 assert_timedated_signal
"$ts" "false"
273 assert_timesyncd_state
"inactive"
275 assert_rc
3 systemctl is-active
--quiet systemd-timesyncd
278 ts
="$(date +"%F
%T.
%6N
")"
279 timedatectl set-ntp true
280 assert_timedated_signal
"$ts" "true"
282 assert_timesyncd_state
"active"
283 assert_rc
0 systemctl is-active
--quiet systemd-timesyncd
286 ts
="$(date +"%F
%T.
%6N
")"
287 timedatectl set-ntp false
288 assert_timedated_signal
"$ts" "false"
290 assert_rc
3 systemctl is-active
--quiet systemd-timesyncd
292 systemctl stop busctl-monitor.service
293 rm -rf /run
/systemd
/system
/systemd-timesyncd.service.d
/
294 systemctl daemon-reload
297 assert_timesyncd_signal
() {
298 local timestamp
="${1:?}"
299 local property
="${2:?}"
301 local args
=(-q --since="$timestamp" -p info
-t busctl
)
306 if journalctl
"${args[@]}" --grep .
; then
307 [[ "$(journalctl "${args[@]}" -o cat | jq -r ".payload.data
[1].
$property.data |
join(\" \")")" == "$value" ]];
317 assert_networkd_ntp
() {
318 local interface
="${1:?}"
320 # Go through the array of NTP servers and for each entry do:
321 # - if the entry is an IPv4 address, join the Address array into a dot separated string
322 # - if the entry is a server address, select it unchanged
323 # These steps produce an array of strings, that is then joined into a space-separated string
324 # Note: this doesn't support IPv6 addresses, since converting them to a string is a bit more
325 # involved than a simple join(), but let's leave that to another time
326 local expr='[.NTP[] | (select(.Family == 2).Address | join(".")), select(has("Server")).Server] | join(" ")'
328 [[ "$(networkctl status "$interface" --json=short | jq -r "$expr")" == "$value" ]]
331 testcase_timesyncd
() {
332 if systemd-detect-virt
-cq; then
333 echo "This test case requires a VM, skipping..."
337 if ! command -v networkctl
>/dev
/null
; then
338 echo "This test requires systemd-networkd, skipping..."
342 # Create a dummy interface managed by networkd, so we can configure link NTP servers
343 mkdir
-p /run
/systemd
/network
/
344 cat >/etc
/systemd
/network
/10-ntp99.netdev
<<EOF
349 cat >/etc
/systemd
/network
/10-ntp99.network
<<EOF
357 systemctl unmask systemd-timesyncd systemd-networkd
358 systemctl restart systemd-timesyncd
359 systemctl restart systemd-networkd
360 networkctl status ntp99
362 systemd-run
--unit busctl-monitor.service
--service-type=notify \
363 busctl monitor
--json=short
--match="type=signal,sender=org.freedesktop.timesync1,member=PropertiesChanged,path=/org/freedesktop/timesync1"
368 ts
="$(date +"%F
%T.
%6N
")"
369 timedatectl ntp-servers ntp99
10.0.0.1
370 assert_networkd_ntp ntp99
10.0.0.1
371 assert_timesyncd_signal
"$ts" LinkNTPServers
10.0.0.1
372 # Setting NTP servers to the same value shouldn't emit a PropertiesChanged signal
373 ts
="$(date +"%F
%T.
%6N
")"
374 timedatectl ntp-servers ntp99
10.0.0.1
375 assert_networkd_ntp ntp99
10.0.0.1
376 (! assert_timesyncd_signal
"$ts" LinkNTPServers
10.0.0.1)
378 ts
="$(date +"%F
%T.
%6N
")"
379 timedatectl ntp-servers ntp99
10.0.0.1 192.168.0.99
380 assert_networkd_ntp ntp99
"10.0.0.1 192.168.0.99"
381 assert_timesyncd_signal
"$ts" LinkNTPServers
"10.0.0.1 192.168.0.99"
382 # Multiple IPs + servers
383 ts
="$(date +"%F
%T.
%6N
")"
384 timedatectl ntp-servers ntp99
10.0.0.1 192.168.0.99 foo.localhost foo
10.11.12.13
385 assert_networkd_ntp ntp99
"10.0.0.1 192.168.0.99 foo.localhost foo 10.11.12.13"
386 assert_timesyncd_signal
"$ts" LinkNTPServers
"10.0.0.1 192.168.0.99 foo.localhost foo 10.11.12.13"
390 # There's no user-facing API that allows changing this property (afaik), so let's
391 # call SetRuntimeNTPServers() directly to test things out. The inner workings should
392 # be exactly the same as in the previous case, so do just one test to make sure
394 ts
="$(date +"%F
%T.
%6N
")"
395 busctl call org.freedesktop.timesync1
/org
/freedesktop
/timesync1 org.freedesktop.timesync1.Manager \
396 SetRuntimeNTPServers as
4 "10.0.0.1" foo
"192.168.99.1" bar
397 servers
="$(busctl get-property org.freedesktop.timesync1 /org/freedesktop/timesync1 org.freedesktop.timesync1.Manager RuntimeNTPServers)"
398 [[ "$servers" == 'as 4 "10.0.0.1" "foo" "192.168.99.1" "bar"' ]]
399 assert_timesyncd_signal
"$ts" RuntimeNTPServers
"10.0.0.1 foo 192.168.99.1 bar"
402 systemctl stop systemd-networkd systemd-timesyncd
403 rm -f /run
/systemd
/network
/ntp99.
*