9

When I shutdown and power off the Raspberry Pi 3b and then power it up again the next day, I get wrong starting date/time printed when checking sudo systemctl status DustSensor. The printed date/time is from the date/time of the shutdown.

How can I delay the startup of services until the date/time is corrected from the NTP?

At shutdown and power off:

Feb 23 21:09:24 rp3b systemd[1]: Stopping DustSensor.service...

At startup (power on) the next day, still approx. the old date/time:

Feb 23 21:09:35 rp3b systemd[1]: Started DustSensor.service.

A bit later the date/time is corrected, but sudo systemctl status DustSensor gives the old date/time (not shown here):

Feb 23 21:09:51 rp3b systemd[826]: Started D-Bus User Message Bus.
Feb 24 09:17:48 rp3b systemd[1]: Time has been changed
Feb 24 09:17:48 rp3b systemd-timesyncd[277]: Synchronized to time server [2a01:4f8:c17:b8f::2]:123 (2.debian.pool.ntp.org).
Feb 24 09:17:48 rp3b systemd[1]: apt-daily.timer: Adding 1h 24min 33.643221s random time.
Feb 24 09:17:48 rp3b systemd[1]: apt-daily-upgrade.timer: Adding 22min 10.691517s random time.
Feb 24 09:17:48 rp3b systemd[826]: Time has been changed
Feb 24 09:17:48 rp3b systemd[1]: Reached target Network is Online.

Here's my DustSensor.service file:

[Service]
ExecStart=/home/pi/SDS011_Feinstaub_Sensor.py
WorkingDirectory=/home/pi
Restart=always
StandardOutput=syslog
StandardError=syslog
SyslogIdentifier=DustSensor
User=pi
Group=pi

[Install]
WantedBy=multi-user.target

With the suggestion and the example hello.service:

[Unit]
Description=Hello After Time Sync
After=time-sync.target

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/bin/echo "$(date) Hello world, just started"

[Install]
WantedBy=multi-user.target

I get this where only after starting the hello.service the date/time is updated

journalctl -b | egrep '(ello|anged|ync)'
Feb 28 10:48:54 rp3b systemd[1]: Starting Network Time Synchronization...
Feb 28 10:48:55 rp3b systemd[1]: Started Network Time Synchronization.
Feb 28 10:48:55 rp3b systemd[1]: Reached target System Time Synchronized.
Feb 28 10:49:00 rp3b systemd[1]: Starting Hello After Time Sync...
Feb 28 10:49:00 rp3b systemd[1]: Started Hello After Time Sync.
Feb 28 10:49:11 rp3b bluetoothd[570]: Failed to obtain handles for "Service Changed" characteristic
Feb 28 11:35:51 rp3b systemd[1]: Time has been changed
Feb 28 11:35:51 rp3b systemd[499]: Time has been changed
Feb 28 11:35:51 rp3b systemd-timesyncd[271]: Synchronized to time server [2a02:2028:ff01:14::13]:123 (2.debian.pool.ntp.org).

Also this shows the wrong starting time:

sudo systemctl status hello
\u25cf hello.service - Hello After Time Sync
   Loaded: loaded (/etc/systemd/system/hello.service; enabled; vendor preset: enabled)
   Active: active (exited) since Thu 2019-02-28 10:49:00 CET; 1h 16min ago
  Process: 355 ExecStart=/bin/echo $(date) Hello world, just started (code=exited, status=0/SUCCESS)
 Main PID: 355 (code=exited, status=0/SUCCESS)
   CGroup: /system.slice/hello.service

Feb 28 10:49:00 rp3b systemd[1]: Starting Hello After Time Sync...
Feb 28 10:49:00 rp3b systemd[1]: Started Hello After Time Sync.

monok
  • 225
  • 1
  • 3
  • 11

3 Answers3

14

The actual feature was added to systemd in https://github.com/systemd/systemd/pull/8494 which is included in version 239

See docs in [1]:

systemd-time-wait-sync is a system service that delays the start of units that depend on time-sync.target until the system time has been synchronized with an accurate time source by systemd-timesyncd.service.

This means that you need to both enable systemd-time-wait-sync and Wants=time-sync.target

[1] https://www.freedesktop.org/software/systemd/man/systemd-time-wait-sync.service.html

jarondl
  • 256
  • 1
  • 4
  • 1
    Thanks, good to know. We will use it with next Raspbian Buster. Raspbian Stretch now is still using systemd 232 :-( – Ingo Mar 11 '19 at 00:47
  • 1
    "Wants=time-sync.target" and add "After=time-sync.target" too. If not, both units are started at the same time, but not the target before the time sincronization, this is the key. Check it with "systemd-analyze plot" > output.svg" the startup sequence. – rfmoz Mar 20 '20 at 15:46
11

UPDATE:
Please note the answer from @jarondl. There is a new systemd-time-wait-sync service that we can use since Raspbian Buster.


To syncronize with the time service there is a special time-sync.target. In man systemd.special you will find:

time-sync.target
Services responsible for synchronizing the system clock from a remote source (such as NTP client implementations) should pull in this target and order themselves before it. All services where correct time is essential should be ordered after this unit, but not pull it in. systemd automatically adds dependencies of type After= for this target unit to all SysV init script service units with an LSB header referring to the "$time" facility.

But this does not help much because this is only for initializing the time-sync service. After intitializing it needs some time to connect to the time server and get the clock synchronized to it. The systemd-timesyncd.service notifies it to the systemd manager so you will find something like this in the journal:

systemd-timesyncd[403]: Synchronized to time server 144.76.60.190:123 (2.debian.pool.ntp.org).

I have looked for systemd-notify if we can start an unit depending on this notify but haven't found anything. So I will look into the journal if I find the notify. If not then the Unit will fail to start but after a time it will restart and try again. Modify your Unit so it looks like this:

[Unit]
Description=SDS011 Feinstaub Sensor
After=time-sync.target

[Service]
Restart=on-failure
RestartSec=30
ExecStartPre=/bin/bash -c '/bin/journalctl -b -u systemd-timesyncd | /bin/grep -q "systemd-timesyncd.* Synchronized to time server"'
ExecStart=/home/pi/SDS011_Feinstaub_Sensor.py
WorkingDirectory=/home/pi
SyslogIdentifier=DustSensor
User=pi
Group=pi

[Install]
WantedBy=multi-user.target

You do not need StandardOutput=syslog and StandardError=syslog. That's default.

Ingo
  • 40,606
  • 15
  • 76
  • 189
  • Unfortunately, this didn't help: `Feb 27 09:05:01 rp3b systemd[1]: Started DustSensor.` still comes before `Feb 27 09:46:11 rp3b systemd[1]: Time has been changed` resulting in pretending the service started 45 mins ago whereas the system was booted 5 mins ago. – monok Feb 27 '19 at 09:02
  • @monok On the research why your service fails to start after time sync I have found that there is a special target. So using `systemd-timesyncd.service` was not the right dependency. I have updated the answer. – Ingo Feb 27 '19 at 11:28
  • This time it does not start. I see `Feb 27 14:01:24 rp3b systemd-timesyncd[277]: Synchronized to time server [2a02:c205:2009:8290::1]:123 (2.debian.pool.ntp.org). `. Does it has to to with missing target.wants? I see when I execute `ls timers.target.wants` `apt-daily.timer apt-daily-upgrade.timer` – monok Feb 27 '19 at 13:14
  • @monok No, the docu says: "*All services where correct time is essential should be ordered after this unit, but not pull it in.*" "Pull in" means `Wants=` and that should **not** be used for services starting after time sync. And as you see the time sync service is started and don't need `Wants=`. What can you see with `systemctl status DustSensor.service` why it does not start? I have updated the answer with the test unit I have used to verify. – Ingo Feb 27 '19 at 14:00
  • in my previous post I was not correct saying the service wasn't started. It was started but the reported date/time was wrong and I didn't see an output. Using your hello example I see this after reboot from `sudo systemctl status hello`: `Active: active (exited) since Thu 2019-02-28 10:49:00 CET; 53min ago`...`Feb 28 10:49:00 rp3b systemd[1]: Starting Hello After Time Sync... Feb 28 10:49:00 rp3b systemd[1]: Started Hello After Time Sync.`. – monok Feb 28 '19 at 10:47
  • @monok The `hello.service` started after time sync and is executed at the current date/time, isn't it? Have you used `journalctl -b` and searched for `Hello`? What's the problem now? – Ingo Feb 28 '19 at 10:54
  • @monok OK I understand the problem now. I have modified the answer. – Ingo Mar 01 '19 at 02:09
1

If you are willing to install ntpstat on your pi then you can accomplish this in another way.

The steps you then need to follow are:

  • install ntpstat
  • Create a shell script that calls ntpstat every second and only exits when it has a exit code of 0 (indicating that the time on the device has connected to a time server and syncronized the time
  • Create a oneshot systemd service that executes the above shell script
  • Modify your time synced dependent service to run only after the ntpstat service has run

Install ntpstat

sudo apt-get install ntpstat

Create the shell script that queries ntpstat

/usr/local/bin/check_time_synced.sh

#!/bin/bash

ntpstat
while
     printf "Time not synced yet\n"
     sleep 1s
     ntpstat
     (( $? ))
do
    :
done

printf "Successfully synchronized the time.\n"

Create the systemd oneshot service that calls the above script

/etc/systemd/system/ntpsync.service

[Unit]
Description=Checks the status of the ntp daemon. Only return when the time has been successfully synced
After=network-online.target
Wants=network-online.target

[Service]
ExecStart=/usr/local/bin/check_time_synced.sh
Type=oneshot

[Install]
WantedBy=multi-user.target

And enable the service

sudo systemctl enable ntpsync.service

Modify your own date time dependant service to start only after the ntpsync.service

[Unit]
Description=Hello After Time Sync
After=ntpsync.service

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/bin/echo "$(date) Hello world, just started"

[Install]
WantedBy=multi-user.target

After this, reboot the pi and your service should only start when the unit has the correct time.

Grubby
  • 11
  • 2