An automated clockspeed setup using daemontools

by Frank Tegtmeyer <fte@fte.to>

Daniel J. Bernsteins clockspeed package provides a good way to synchronize the system clock of one or a group of computers. It lacks a way do do the necessary measurements in an automated way. This article describes an automated setup using daemontools from the same author.

Overview

The clockspeed package by Daniel J. Bernstein works by adjusting the systems clock to a reference clock. The adjustments are done using measurements against the reference clock in increasing intervals.
The precondition for this is that the reference clock and the local system clock have to run persistently fast or slow.

The solution is usable for a host synchronizing against a NTP reference source and for stations in a LAN environment synchronizing against a master server running Bernsteins taiclockd server. The daemontools setup for the taicklockd server is not described here because it is simple and very similar to the one for the clockspeed daemon.

Wayne Marshall built a very well done package based on this article. I recommend to check it out (after reading this page) at his clocksd page.

The solutions principle

To have the clockspeed daemon running all the time a daemontools setup is used which creates a clockspeed service. Should the daemon crash for any reason (I doubt it ever will) the supervise process will restart it within a few seconds.

For the measurements there is a second service defined that starts a long running shell script. After doing a measurement the shell script sleeps a given time before doing the next measurement. The sleep time is doubled for every loop until a defined maximum time is reached. After that the script sleeps always for that maximum time between measurements.

The implementation

The installation of clockspeed is done using the installation instructions inside the package. All files are installed under /usr/local/clockspeed.

The configuration file

Several scripts use the configuration file that is created as /etc/clockspeed.conf:

# `uname -n` for a single host
MASTER='myhost'
# not used for a single host
MASTER_IP='213.61.218.4'
# Your NTP-source (IP address)
SNTP_IP=`dnsip ntp2.ptb.de | sed 's/ .*//'`
# the installation directory
CLOCK_DIR='/usr/local/clockspeed'

Please note that the first two entries are used for taiclock clients in a local area network - they are not used in a single host setup. At the opposite the SNTP_IP entry is not used for taiclock clients.

SNTP_IP has to be an IP address in dotted format. I use ntp2.ptb.de as source, that's one of the two NTP servers of the Physikalisch Technische Bundesanstalt in Germany. They changed the IP addresses of their servers a while ago and I found several of my servers unsynchronized some day because of that. So I use the output of the dnsip program from now on. This will fail also under some circumstances but that's not the topic here. Feel free to set a fixed IP address.

Helper script someclock

Someclock is the script that differentiates between the NTP-client setup and the taicklock-client setup. It is used in the clockspeed and in the measurement service.
The script is stored as /usr/local/clockspeed/bin/someclock:

#! /bin/sh

. /etc/clockspeed.conf
PATH="$CLOCK_DIR/bin":/usr/bin:/bin
export PATH

case `uname -n` in
$MASTER)
        sntpclock $SNTP_IP
        ;;
*)
        taiclock $MASTER_IP
        ;;
esac

As you can see the output of uname -n is used to decide if the system is using NTP or taiclock. Possibly there would be a semantically better way to do this. The setup of /etc/clockspeed.conf and this script has to be seen with a local area network in mind where the one server that synchronizes against the NTP source acts as master for all the taiclock clients.

The clockspeed service

Clockspeed is started as usual by a run script for daemontools - the script is located under /usr/local/clockspeed/service_clockspeed and does only start the clockspeed daemon:
#!/bin/sh 

. /etc/clockspeed.conf  
$CLOCK_DIR/bin/someclock | $CLOCK_DIR/bin/clockadd 
exec /usr/local/clockspeed/bin/clockspeed 

The script first set's the systems clock with the time it gets from the reference clock and then executes the clockspeed daemon. Because there is no logging output by the clockspeed daemon itself the log service isn't necessary.

The service is started by linking it into the /service directory:

ln -s /usr/local/clockspeed/service_clockspeed /service/clockspeed

The adjustment input for the clockspeed daemon is provided by writing the time measurements into the pipe /usr/local/clockspeed/adjust that is created (and read) by the clockspeed deamon.

To improve security the measurements are done under the account sntp and group sntp. On a SuSE-Linux system these are added with the commands:

groupadd sntp
useradd -g sntp -d /var/lib -s /bin/false sntp; passwd -l sntp

For users of other distributions or systems: The user gets a home directory where he cannot write and a shell that instantly terminates (/bin/true is also a candidate for that). Additionally the account is locked after creation so that nobody can use this account for interactive sessions.

The measurement pipe is made writeble for the sntp group (after the clockspeed daemon was started):

chgrp sntp /usr/local/clockspeed/adjust
chmod 620 /usr/local/clockspeed/adjust

The measurement service

The real measurement script is /usr/local/clockspeed/service_adjust/clockadjust which is divided from the run script to enable an easy way to switch the uid for it. It runs under user sntp:

#!/bin/sh
#
# script to do the time measurements for clockspeed
# automatically - it runs forever
# Frank Tegtmeyer <fte@fte.to>, based on an idea
# of Tim Goodwin

exec 2>&1

. /etc/clockspeed.conf
PATH="$CLOCK_DIR/bin":/usr/bin:/bin export PATH

maximum_wait=7884000
sleep=600

while true
do
   . /etc/clockspeed.conf
   # wait a while
   echo Sleeping $sleep seconds ...
   sleep $sleep
   # get new measurement
   someclock | tee $CLOCK_DIR/adjust | clockview
   clockview < $CLOCK_DIR/etc/atto
   echo '----------------------'
   # double the time
   sleep=`expr $sleep + $sleep`
   # cut the value
   if [ $sleep -gt $maximum_wait ]
     then
     sleep=$maximum_wait
   fi
done

The logging output of this service is caught by the matching log/run file which logs under /var/log/clockadjust. here is an example using a special log user (file /usr/local/clockspeed/service_adjust/log/run):

#!/bin/sh
exec setuidgid sntplog /command/multilog t /var/log/clockadjust

The adjustment service is controlled by it's run script /usr/local/clockspeed/service_adjust/run. It first drops it's privileges by changing it's UID to the sntp user and then executes the measurement script:

#!/bin/sh
#
exec /command/setuidgid sntp \
 /usr/local/clockspeed/service_adjust/clockadjust

Timezone correction

Because clockspeed uses TAI as it's time base, the time zone file has to be one from the "right" subdirectory. Under Linux and for Germany you have to do the following:

rm /etc/localtime
ln -s /usr/share/zoneinfo/right/Europe/Berlin /etc/localtime

Of course you have to pick the right zone matching your location.

Hints

Don't forget to make the run files and other scripts executable :)

Don't forget to create the log directory /var/log/clockadjust and make it writeable for the user used for logging.

qmail has a leapseconds bug, that creates a time offset in Received headers.

Credits

Thanks to Tim Goodwin for the idea and initial version. Thanks to Gerrit Pape for putting some pressure on me to make this public. I always wanted to do it but never managed to steal the necessary time.
If you have improvements (marginal or substantial) please tell me and I will include you in this section.

Changes history

2003-04-06
HTML errors, thanks to Cory Wright
2003-02-20
Link to Wayne Marshalls clocksd package
2002-09-16
some reordering, thanks to Gerrit Pape
2002-03-10
first version