As I am running chrony
instead of the old ntpd
, I didn't have an automated way of getting the kernel parameter right, so I looked into an alternative.
As the offset between TAI and UTC is relatively constant (changes < once per year) it is possible to statically set the kernel parameter, and then using the CLOCK_TAI clock in an application will give the correct value.
There is a test application for setting the kernel offset in the kernel sources, in tools/testing/selftests/timers/set-tai.c
. And, assuming you have the tzdata
package installed, there is a file with the offset between UTC and TAI in /usr/share/zoneinfo/leap-seconds.list
.
I chopped down the kernel test application so the main became:
int main(int argc, char **argv)
{
int i, ret;
ret = get_tai();
printf("tai offset started at %i\n", ret);
if (argc < 2)
{
printf("New offset not given, not setting\n");
}
else
{
i = strtol(argv[1],NULL,10);
printf("Attempting to set TAI offset to %d\n",i);
printf("Checking tai offsets can be properly set: ");
ret = set_tai(i);
ret = get_tai();
if (ret != i) {
printf("[FAILED] expected: %i got %i\n", i, ret);
return EXIT_FAILURE;
}
}
printf("[OK]\n");
return EXIT_SUCCESS;
}
Then, for my use case, it was just a matter of extracting the correct value from the leap-seconds.list
file and running set-tai
with this as a parameter (in /etc/rc.local
to get it to happen at boot time). An example way of doing this is:
TAI_OFFSET=$(grep -v '^#' /usr/share/zoneinfo/leap-seconds.list | tail -1 | awk '{ print $2 }')
if [ -x /usr/local/sbin/set-tai ]; then
/usr/local/sbin/set-tai $TAI_OFFSET
fi
Please be aware that for the above snippet to work correctly the leap-seconds.list file is assumed to have the currently valid TAI_OFFSET at the last non-comment type line. If ever more current lines are in the file or other than current date offsets are in question then some more advanced parsing method needs to be used.
Hope this is useful to someone else!