Getting a Time Without NTP
One of the drawbacks of a Raspberry Pi is its lack of an on-board clock. After a reboot, its clock is set to midnight, New Year’s Day, 1970 UTC, the beginning of the Unix epoch, and then the kernel boots. However, it is possible to get the current time early in the boot process, with one condition: an NFS root filesystem.
What’s my motivation?
One thing I’ve noticed with my Raspberry Pi, is that its clock runs a little bit slow (losing seconds over weeks). However, the clock on my file server runs more than a little fast (gaining seconds over days). The clash appears when using “make” to build something. After a long enough uptime, editing a file can cause that file to have a modification time “in the future,” as the RPi sees it. The “make” program then issues a warning: “Clock skew detected. Your build may be incomplete.” I used a lame command involving SSH and “date” to set the clock on the RPi to agree with the server, but it became a nuisance to do this every time I saw the “make” warning.
Setting up an NTP service wasn’t an onerous task, but I never really wanted to do that. My file server isn’t connected to the Internet, so its time can’t be considered a stable time source. I just wanted to stop the warning messages. Mostly, I wanted a way to keep the clocks in sync, even if that meant they had the same, incorrect time.
NTP isn’t the only network time
Then the proverbial light came on. There are other ways to get another computer’s time, especially if another computer is a file server. In this case, having the root filesystem on an NFS server was a stupendous asset. Unix files have timestamps, and the “date” command can access the modification time of a particular file. Why not create a file, then immediately set the system time from that file? The local system may have a wildly incorrect time, but the time on the NFS server is really the one we care about, correct or not. Even with delays from shell script interpretation, we can probably get the time from the NFS server, with tenth-of-a-second resolution.
So how do we do it?
It’s very simple, actually. The lower task is to create a file; the middle task is to get that file’s timestamp (and then remove it, as a good user); and the higher task is to set the system time to that timestamp. Setting the system time requires root privileges; the command isn’t really effective otherwise.
So, as root, the following command will do the trick:
/bin/date -s "$(touch /.z ; sync ; /bin/date --rfc-3339=ns -r /.z ; rm /.z)"
This shell command accomplishes a few tricks. First, it creates a hidden file in the root filesystem, /.z. (The dot-zed filename isn’t fixed. It’s just what I came up with for my system.) The “sync” command makes sure the NFS server and client agree on, well, whatever. That is to say: It Works For Me. Then, the second “date” command gets the nanosecond-timestamp of the /.z file that we just created.
Once we have that timestamp, the first “date” command sets the system time to that timestamp. There may be some elapsed time between the “date” commands, but it can be ignored safely. The main intention is to get the client’s time close enough to the server’s time, to avoid any “clock skew” warnings.
Yes, this command works at boot, with an NFS root filesystem. I have the following in my ArmedSlack /etc/rc.d/rc.S script:
# Try to mount /proc: /sbin/mount -v proc /proc -n -t proc 2> /dev/null # On an NFS root, try to set time from NFS server if grep -q nfsroot /proc/cmdline ; then /usr/bin/date -s "$(touch /.z ; sync ; /usr/bin/date --rfc-3339=ns -r /.z ; rm -f /.z)" fi
The first two lines give a hint where to insert the following “if…fi” clause. The same could be done on any Linux system with an NFS root; finding the proper location in the boot scripts is left as an exercise for the reader.
What about after boot?
I also have the following line in the system crontab:
*/5 * * * * /usr/bin/date -s "$(/bin/touch /.z ; sync ; /usr/bin/date --rfc-3339=ns -r /.z ; rm /.z)" 1> /dev/null
This does the same thing, every five minutes. During a heavy build, like the kernel or glibc, the client-server time difference tends to become more pronounced. A small interruption every five minutes is a small price to pay for shutting off a nuisance warning.
What about other network filesystems?
I haven’t tested this method on them. I don’t know if it would work with CIFS (Samba), AndrewFS, Coda, or NCP (NetWare), and I’m not sure any of these can be used as a root filesystem in Linux. If you are using any of these as a root filesystem, and you try the above code on your Raspberry Pi, please report your results in the comments section below.