xdotool comes to the rescue

In a previous post I explained how I implemented a method for adding my current location and the local time to my e-mail signature wherever I happen to be in the World, irrespective of the time on the laptop’s hardware clock and system clock. In that post I described how I created a keyboard shortcut using the Linux application AutoKey. Unfortunately AutoKey has not been updated for several years and no longer works properly in KDE Plasma 5 on my laptops. Therefore I decided to replace it with a KDE keyboard shortcut, and this is to explain how I did it.

First create a custom shortcut in KDE:

  1. ‘System Settings’ > ‘Shortcuts’ > ‘Custom Shortcuts’
  2. ‘Edit’ > ‘New’ > ‘Global Shortcut’ > ‘Command/URL’, and name the New Action ‘Insert current time’
  3. On the Comment pane for ‘Insert current time’, add the comment ‘Insert current time at specified location’ (without the quotes)
  4. On the Trigger pane, configure the shortcut to be Ctrl+Alt+Space
  5. On the Action pane, enter the Command/URL as ‘/home/fitzcarraldo/timezone_signature_GeoNames.sh‘ (without the quotes)
  6. Click ‘Apply’

Next modify the Bash script timezone_signature_GeoNames.sh so that it contains the following (obviously change the username and path to suit):

#!/bin/bash

place=$(kdialog --title "Current Location" --inputbox "Enter your location:")

placetime=$(perl /home/fitzcarraldo/now1.pl $place)

# xdotool does not output a space in a string, so we have to extract each field from the string
# and print each field individually, separated by a space character.

city=$(echo $placetime | awk -F "|" '{print $1}')
country=$(echo $placetime | awk -F "|" '{print $2}' | sed 's/[)(]//g')
region=$(echo $placetime | awk -F "|" '{print $4}')

datetime=$(/usr/bin/zdump $region | awk -F " " '{print $2" "$3" "$4" "$5" "$6" "$7}')
dayofweek=$(echo $datetime | awk -F " " '{print $1}')
month=$(echo $datetime | awk -F " " '{print $2}')
day=$(echo $datetime | awk -F " " '{print $3}')
time=$(echo $datetime | awk -F " " '{print $4}')
year=$(echo $datetime | awk -F " " '{print $5}')
timezone=$(echo $datetime | awk -F " " '{print $6}')

activewindow=$(xdotool getactivewindow)

xdotool type --window $activewindow "Sent from:"
for oneword in $city; do
    xdotool key --window $activewindow space
    sleep 0.1s
    xdotool type --window $activewindow --delay 100 $oneword
done
xdotool key --window $activewindow comma
for oneword in $country; do
    xdotool key --window $activewindow space
    sleep 0.1s
    xdotool type --window $activewindow --delay 100 $oneword
done
xdotool key --window $activewindow Return
xdotool type --window $activewindow "Local time now: "
xdotool type --window $activewindow $dayofweek
xdotool type --window $activewindow " "
xdotool type --window $activewindow $month
xdotool type --window $activewindow " "
xdotool type --window $activewindow $day
xdotool type --window $activewindow " "
xdotool type --window $activewindow $time
xdotool type --window $activewindow " "
xdotool type --window $activewindow $year
xdotool type --window $activewindow " "
if [ ${timezone:0:1} = "-" ]; then
    timezone="UTC-"${timezone#*-}
elif [ ${timezone:0:1} = "+" ]; then
    timezone="UTC+"${timezone#*+}
fi
xdotool type --window $activewindow $timezone
xdotool type --window $activewindow " "
xdotool key --window $activewindow Return
xdotool key --window $activewindow Return
echo

The Perl script now1.pl is listed in my my earlier post. Notice that the script timezone_signature_GeoNames.sh in my earlier post was much simpler. This was because the AutoKey shortcut took care of sending the text to the currently active window. Without AutoKey, I now had to do this myself in the script timezone_signature_GeoNames.sh, and the command xdotool came to the rescue. The developer explains what xdotool does as follows:

This tool lets you simulate keyboard input and mouse activity, move and resize windows, etc. It does this using X11’s XTEST extension and other Xlib functions.

Additionally, you can search for windows and move, resize, hide, and modify window properties like the title. If your window manager supports it, you can use xdotool to switch desktops, move windows between desktops, and change the number of desktops.

So I installed xdotool via the Gentoo package manager:

# emerge xdotool
# eix xdotool
[I] x11-misc/xdotool
     Available versions:  3.20150503.1-r1^t ~3.20160805.1^t {examples}
     Installed versions:  3.20150503.1-r1^t(22:51:30 02/04/17)(-examples)
     Homepage:            http://www.semicomplete.com/projects/xdotool/
     Description:         Simulate keyboard input and mouse activity, move and resize windows

Anyway, my Bash script using xdotool works a treat with Thunderbird (and KWrite, LibreOffice Writer, etc.). I used to experience a problem with certain characters, for example a colon was printed as a semi-colon (see the xdotool bug report xdotool writes the wrong case #121), but that no longer happens in my current KDE Plasma 5 installation:

Sent from: Galeão International Airport, Brazil
Local time now: Thu Jul 6 15:11:40 2017 UTC-03

What a useful tool xdotool is!

How to stop the clock in a VirtualBox virtual machine from drifting

I found that the clock in a VirtualBox virtual machine was drifting significantly from real time. As shown below, it is possible to synchronise the clock in a virtual machine to the clock in the host machine. Note that you must have VirtualBox Guest Additions installed in the virtual machine, so, if you have not already done that, do it first.

Issue the commands on the host machine. In the example commands shown below, the virtual machine name is ‘TestVM’. Obviously change the name in the command to whatever it is you have named your virtual machine. The grep commands are simply to check that the required change to the .vbox XML file has occurred.

The setextradata command forces the virtual machine to get its time of day from the host machine by setting GetHostTimeDisabled to zero (0 = enabled, 1 = disabled). The command stores the parameter in the .vbox XML file of the virtual machine, ensuring that it is set every time the virtual machine boots.

The guestproperty command forces the virtual machine to re-synchronise its clock to the host machine every ten seconds (ten thousand milliseconds). The command also stores the parameter in the .vbox XML file of the virtual machine.

user $ VBoxManage setextradata "TestVM"
"VBoxInternal/Devices/VMMDev/0/Config/GetHostTimeDisabled" 0
user $ grep GetHostTimeDisabled $HOME/VirtualBox-VMs/TestVM/TestVM.vbox

user $ VBoxManage guestproperty set "TestVM" "/VirtualBox/GuestAdd/VBoxService/--timesync-set-threshold" 10000
user $ grep timesync $HOME/VirtualBox-VMs/TestVM/TestVM.vbox

Using a keyboard shortcut in Linux to add an e-mail signature giving current location and local time

In my previous post I showed how to find the current time at any town or city Worldwide from the command line in Gentoo Linux. My interest in a command to do this is not to use it on the command line per se, but to use the command in a keyboard shortcut to insert a signature at the end of my e-mails.

I have to travel internationally frequently because of my work, but I leave my laptop’s hardware clock set to UTC and the system clock set to the local time of my home town. This means that, irrespective of where I am in the World, the e-mail client (Thunderbird, in my case) uses the local time of my home town in e-mail headers and calenders. It is not practical to reconfigure Linux for each timezone I happen to be in (see my post Configuring the Linux clock), and, in any case, I want the file system’s timestamps to use one timezone only and all the timestamps in my e-mails and the e-mail client’s calender to use one timezone only, so there is less chance of me getting confused. I could have configured the installation to use UTC for the system clock, but I prefer the system clock to use the timezone of my home town. Of course, even though the system clock is always set to the timezone of my home town, on the Panel clock I select the timezone of the location where I happen to be, so that the Panel clock displays the local time in that timezone.

I wanted to be able to insert a signature at the end of each e-mail, stating my current location and the current time at that location, so that the person receiving the e-mail could tell from where in the World I sent the e-mail and the local time it was sent, as that local time could differ from the time shown in the e-mail header. For example, let us assume that Jane, who lives in the UK and whose system clock is configured for the timezone Europe/London, is making a brief visit to Perth, Australia and sends an e-mail to Dave in the UK at 06:36 on 11 October (Perth time). The e-mail below illustrates the type of signature I wanted to achieve.

Subject: Site visit
From: Jane <jane@acompany.com>
To: Dave <dave@acompany.com>
Date: Sat Oct 10 2015 23:36:40 GMT+0100 (BST)

Hello Dave,

This is to let you know that I have just arrived in Perth and will be
visiting site at 09:00 local time to speak to the client. Tomorrow p.m.
I have a meeting scheduled with our local project manager, so I would
appreciate it if you would e-mail the latest documentation to me. I will
not have spare time until I’m in my room at the hotel tonight but will
read the documents tomorrow a.m. in readiness for the meeting with
the local project manager. Thanks in advance.

Regards,
Jane
Current location: Perth (Australia)
Local time now: Sat Oct 11 06:36:31 2015 AWST

As you can see above, because the OS on Jane’s and Dave’s laptops is configured for the timezone Europe/London, the e-mail header shows the current time in the UK when the e-mail was sent, which was October 10, 23:36 British Summer Time (22:36 UTC), and the signature shows the corresponding local time in Perth, Australia, which was October 11, 06:36 Australian Western Standard Time. It becomes even more confusing if the computer of the person receiving the e-mail is configured for a third timezone. For example, let’s say Dave is based in Seattle, USA rather than the UK. His e-mail client would then display the time in that timezone when the e-mail was sent. This is usually my case, i.e. my Linux installation is configured for Timezone1 but I happen to be in Timezone2 when I send an e-mail to someone who is based in Timezone3 and whose OS is configured for that timezone.

I wanted to use a keyboard shortcut to add a signature to the end of my e-mails, as shown above. I therefore created the Bash script listed below, which I named timezone_signature_GeoNames.sh:

#!/bin/bash

location=$(kdialog --title "Current Location" --inputbox "Enter your location:")

localtime=$(perl /home/fitzcarraldo/now1.pl $location)
place=`echo $localtime | cut -d'|' -f1`
place=$place" "`echo $localtime | cut -d'|' -f2`
timezone=`echo $localtime | cut -d'|' -f4`

if [ $location != "" ]; then
  echo -n "Current location: "
  echo $place
  echo -n "Local time now:"
  /usr/sbin/zdump ${timezone} | cut -d' ' -f2-
fi
echo

Notice that the Bash script uses the GUI dialogues utility kdialog to display a pop-up window prompting me to enter the name of a town/city. As I am using KDE I opted to use a dialogues utility developed for use in KDE, but I could have used Zenity instead.

The Perl script now1.pl is a variant of the Perl script now.pl described in my previous post, modified very slightly in order to facilitate formatting of the output by the Bash script, and is listed below.

#!perl

use strict;
use warnings;

use DateTime;
use Geo::GeoNames;
use URI::Escape;
use Encode;

binmode STDOUT, ':encoding(UTF-8)';

my $city = decode("UTF-8", @ARGV ? shift : 'London');
# N.B. Replace London with your home town/city.

my $geo = Geo::GeoNames->new( username => '************' );
# N.B. Replace the asterisks with your GeoNames user name.

my $result = $geo->search(
q       => uri_escape_utf8($city),
maxRows => 1,
style   => 'FULL'
);

defined $result->[0] or die "Unrecognized city '$city'\n";

my $city_name    = $result->[0]->{name};
my $country_name = $result->[0]->{countryName};
my $time_zone    = $result->[0]->{timezone}{content};
my $time_now     = DateTime->now( time_zone => $time_zone );

#print "$city_name ($country_name) $time_now ($time_zone)\n";

print "$city_name|($country_name)|$time_now|$time_zone\n";

exit 0;

The only thing remaining was to configure a keyboard shortcut to launch the Bash script. I opted to use the key combination Ctrl-Alt-z for the shortcut. As I am using KDE I could have used KDE’s ‘System Settings’ > ‘Shortcuts and Gestures’> ‘Custom Shortcuts’ to specify the shortcut and the name of the script it launches. However, as I also use AutoKey for various shortcuts, I opted to use that instead, so I used the AutoKey GUI to create a shortcut named ‘Insert Current Time’ to use the following command:

output = system.exec_command("/home/fitzcarraldo/timezone_signature_GeoNames.sh")
keyboard.send_keys(output)

Use

I compose my e-mails as usual, and, after entering my name at the end of the e-mail, I press Ctrl-Alt-z. A window then pops up prompting me to enter my current location, which I do and then click on ‘OK’. The location and current time at that location are then added to the end of the e-mail, and it just remains for me to click on the ‘Send’ button in the e-mail client’s window. As the Perl script now1.pl uses the Internet to access the GeoNames database, my laptop must be connected to the Internet when I use the shortcut.

KDialog window that pops up when I use the keyboard shortcut

KDialog window that pops up when I use the keyboard shortcut

If the town/city name consists of more than one word (Rio de Janeiro, for example) then replace spaces with hyphens when you enter the location name in the pop-up window (Rio-de-Janeiro, for example) and then the keyboard shortcut will return the correct location and local time:

Current location: Rio de Janeiro (Brazil)
Local time now: Fri Oct 16 09:28:19 2015 BRT

Find the time now at any town or city Worldwide from the command line in Gentoo Linux

In my hunt for a command to return the current time at any town or city in the World, I recently found a Perl script now.pl posted in 2012 by Jim Monty on grokbase. The script uses a Perl module to access the database of the GeoNames Web site. To use the module you need to have a user account at the GeoNames Web site and be connected to the Internet. The script also uses the modules URI::Escape and DateTime.

In the case of Gentoo Linux, ebuilds for some of the Perl modules used by now.pl are not available in the main Portage tree, so I installed them from a Portage local overlay, as explained below.

First I created in my local overlay an ebuild for the Perl module Geo::GeoNames and then merged it:

# mkdir -p /usr/local/portage/dev-perl/Geo-GeoNames
# cd /usr/local/portage/dev-perl/Geo-GeoNames
# nano -w Geo-GeoNames-1.01.ebuild
# ebuild Geo-GeoNames-1.01.ebuild manifest
# emerge --ask Geo-GeoNames

The ebuild Geo-GeoNames-1.01.ebuild I created is listed below:

EAPI=5

MODULE_AUTHOR=BDFOY
inherit perl-module

DESCRIPTION="Provides a perl interface to the webservices found at http://api.geonames.org"

SLOT="0"
KEYWORDS="alpha amd64 ~arm hppa ia64 ~mips ppc ppc64 ~s390 ~sh sparc x86 ~amd64-linux ~x86-linux ~ppc-macos ~x86-macos ~sparc-solaris"
IUSE=""

RDEPEND="
        dev-perl/Module-Build
        "
DEPEND="${RDEPEND}"

SRC_TEST=do

I’m not sure if I declared the correct dependencies in DEPEND and RDEPEND, but the Geo::GeoNames module is merged in my installation and functions correctly.

The Perl script also uses the Perl module URI::Escape, which I found out is part of the package dev-perl/URI in the Portage main tree and was already installed, so I did not need to do anything further as far as that was concerned.

Next I needed to install the Perl module Date::Time. Fortunately there is an ebuild for dev-perl/DateTime in the main Portage tree, so I merged that package directly:

# emerge --ask DateTime

Then I surfed to the GeoNames Web site and registered for a user account. My thanks go to the people who provide and maintain the site and database.

I then created a file /home/fitzcarraldo/now.pl containing the Perl script listed in Jim Monty’s post of Aug 19, 2012 on the Web page: [DateTime] Is there timezone data for any Indian cities such as Mumbai, Dehli, &c.?. My thanks also go to Jim Monty for posting his script.

My initial attempts at running now.pl resulted in an error message warning about a missing Mojo::UserAgent Perl module. I therefore needed to install the package Mojolicious but, unfortunately, the main Portage tree does not have an ebuild for it. I could have either added a third-party overlay (e.g. srcshelton) which contains a Mojolicious ebuild or downloaded the ebuild and put it in my local overlay. I opted for the latter, and merged it:

# mkdir -p /usr/local/portage/dev-perl/Mojolicious
# cd /usr/local/portage/dev-perl/Mojolicious
# cp /home/fitzcarraldo/Downloads/Mojolicious-5.30.ebuild .
# ebuild Mojolicious-5.30.ebuild manifest
# emerge --ask Mojolicious

Update July 24, 2017: the package Mojolicious is now in the Portage main tree, so you can now merge that instead of adding it to a local overlay.

Despite the examples given in Jim Monty’s post using now.pl with place names containing diacritics, in my case the script could not handle them, so I made a couple of small modifications, and the script I’m using is show below:

#!perl
use strict;
use warnings;

use DateTime;
use Geo::GeoNames;
use URI::Escape;
use Encode;

binmode STDOUT, ':encoding(UTF-8)';

my $city = decode("UTF-8", @ARGV ? shift : 'London');

my $geo = Geo::GeoNames->new( username => '***********' );
# N.B. Replace the asterisks with your GeoNames user name.

my $result = $geo->search(
q       => uri_escape_utf8($city),
maxRows => 1,
style   => 'FULL'
);

defined $result->[0] or die "Unrecognized city '$city'\n";

my $city_name    = $result->[0]->{name};
my $country_name = $result->[0]->{countryName};
my $time_zone    = $result->[0]->{timezone}{content};
my $time_now     = DateTime->now( time_zone => $time_zone );

print "$city_name ($country_name) $time_now ($time_zone)\n";

exit 0;

I made the script executable and ensured my user account could use it:

# chmod +x /home/fitzcarraldo/now.pl
# chown fitzcarraldo:fitzcarraldo /home/fitzcarraldo/now.pl

Now if I enter the name of a town or city anywhere in the World while my laptop is connected to the Internet, the script prints the town/city name, country, local time and the time zone’s name as given in the zoneinfo database (a.k.a. ‘Olsen database’, ‘tz database‘ and ‘IANA time zone database’):

$ cd
$ perl now.pl
London (United Kingdom) 2015-09-25T23:10:16 (Europe/London)
$ perl now.pl "London Canada"
London (Canada) 2015-09-25T18:10:22 (America/Toronto)
$ perl now.pl Paris
Paris (France) 2015-09-26T00:10:29 (Europe/Paris)
$ perl now.pl "New York"
New York (United States) 2015-09-25T18:10:41 (America/New_York)
$ perl now.pl Tokyo
Tokyo (Japan) 2015-09-26T07:10:50 (Asia/Tokyo)
$ perl now.pl "Mexico City"
Mexico City (Mexico) 2015-09-25T17:10:59 (America/Mexico_City)
$ perl now.pl "Kuala Lumpur"
Kuala Lumpur (Malaysia) 2015-09-26T06:11:09 (Asia/Kuala_Lumpur)
$ perl now.pl "São Paulo"
São Paulo (Brazil) 2015-09-25T19:11:29 (America/Sao_Paulo)
$ perl now.pl Maceio
Maceió (Brazil) 2015-09-25T19:11:39 (America/Maceio)
$ perl now.pl Maceió
Maceió (Brazil) 2015-09-25T19:11:48 (America/Maceio)
$ perl now.pl "Várzea Grande"
Várzea Grande (Brazil) 2015-09-25T18:12:04 (America/Cuiaba)
$ perl now.pl "Mos Eisley"
Unrecognized city 'Mos Eisley'

now.pl works by first using the Geo::GeoNames module to look up in the GeoNames database via the Internet the time zone for the town/city you have specified, then using the Date::Time module to look up the time in that time zone from the zoneinfo data in your installation, based on the time now in your installation’s system clock. In other words, if your system clock is, for example, 3 minutes ahead of actual time then the time returned by now.pl for the relevant time zone would also be 3 minutes fast. But if your system clock is correct, the script would return an accurate time for the requested town/city.

My interest in finding a command that returns the current time at any town or city around the Globe was because I wanted to create a keyboard shortcut to insert a signature in my e-mails, displaying my current location and the local time wherever I happened to be (I have to travel internationally frequently because of my work). In my next post I will explain how I created such an e-mail signature.

Synchronise your Gentoo Linux clock with an Internet time server

There are a number of ways to synchronise Gentoo Linux with a time server on the Internet. Here I look at a few alternatives.

ntp-client

ntp-client and the NTP daemon ntpd are installed when you install the package net-misc/ntp. Although I have read on some Web sites that /etc/init.d/ntp-client should be added to the default runlevel in order to read the time from an NTP server (once-only, during start-up), this in fact does not work because usually the network connection is not up by the time the ntp-client initscript runs. Bear in mind that ntp-client does not run continuously; it syncs once with an external time server if there is a network connection, and that’s it.

NetworkManager Dispatcher

If you are using NetworkManager, an elegant solution is to use NetworkManagerDispatcher to restart ntp-client in order to resync your system clock every time a network connection comes up. This is my favoured solution for laptops; see further on for how to configure your machine to do this.

cronjob

Another way would be to create a cronjob to run periodically the ‘/etc/init.d/ntp-client restart‘ command or the ‘ntpd -q‘ command (the -q option means “set the time and quit”).

Wait a while after start up

A ‘quick-and-dirty’ method, which I have used sometimes to synchronise a laptop’s system clock every time it boots, would be to delay running ntp-client until the network is up by putting e.g. the command below in a file 10_ntp-client.start in the directory /etc/local.d/ (10 seconds is usually enough time for a wired or wireless connection to my home network to be established):

#!/bin/bash
sleep 10s && /etc/init.d/ntp-client restart

Don’t forget to make it executable:

# chmod 744 /etc/local.d/10_ntp-client.start

NTP daemon

Regarding the NTP daemon, it is possible to configure this from the command line, rather than via a Desktop Environment GUI, to run at start-up and continue running to adjust your system clock. The command:

# rc-update add ntpd default

will add the daemon’s initscript to the default runlevel so that it is launched automatically at the next startup, and the command:

# /etc/init.d/ntpd start

will start the daemon running right now.

Note that, by default, the NTP daemon won’t correct, all in one go, a time difference between your system clock and the remote NTP server if that difference is above a certain size. However, if you want to override the default behaviour, i.e. allow the NTP daemon to make a large first adjustment to the system clock, you can set the environment variable NTPD_OPTS in the file /etc/conf.d/ntpd as follows:

NTPD_OPTS="-g"
# The -g option enables ntpd to make large adjustments.

This would mean that you would not need to run ntp-client before ntpd. However, if you run ntp-client automatically — either once after start-up or periodically — then that would be good enough for the typical Desktop user, and could be an alternative to having a continuously-running NTP daemon. Nothing stops you doing both if you want, of course.

Updating the hardware clock

If you make clock_systohc="YES" in the file /etc/conf.d/hwclock then the time in the system clock will be written to the BIOS (CMOS) clock (a.k.a. hardware clock) when you shut down your PC.

How to configure NetworkManager Dispatcher to synchronise the system clock only when a network connection is made

If you’re using a machine that is permanently connected to a network, running the NTP daemon makes sense. But what if you have a machine that is not always connected to a network when it is powered up? I have a laptop and I don’t want the NTP daemon running all the time. But I would like my laptop to synchronise with an external time server once after start up when I connect to the Internet. NetworkManager has a handy tool called NetworkManager Dispatcher for doing just this.

If you have installed NetworkManager, you’ll find there is an initscript /usr/portage/net-misc/networkmanager/files/NetworkManagerDispatcher. Copy it to the directory /etc/init.d/ and give it the necessary restrictive permissions:

# cp /usr/portage/net-misc/networkmanager/files/NetworkManagerDispatcher /etc/init.d/
# chmod 744 NetworkManagerDispatcher

You no longer need to perform the above step regarding a NetworkManagerDispatcher initscript. See my update of September 23, 2014 at the bottom of this post.

Then create a shell script called e.g. 99_ntp-client in the directory /etc/NetworkManager/dispatcher.d/ to be run by NetworkManagerDispatcher when a network connection is established, containing the following code:

#!/bin/bash

INTERFACE=$1 # The interface which is brought up or down
STATUS=$2 # The new state of the interface

case "$STATUS" in
    'up') # $INTERFACE is up
        echo "System time before starting ntp-client:" > /home/fitzcarraldo/ntp-client.txt
        date >> /home/fitzcarraldo/ntp-client.txt
        echo "Starting ntp-client:" >> /home/fitzcarraldo/ntp-client.txt
        rc-config restart ntp-client &>> /home/fitzcarraldo/ntp-client.txt
        echo "System time after starting ntp-client:" >> /home/fitzcarraldo/ntp-client.txt
        date >> /home/fitzcarraldo/ntp-client.txt
        ;;
    'down') # $INTERFACE is down
        # Check for active interface and down if no one active
        if [ ! `nm-tool|grep State|cut -f2 -d' '` = "connected" ]; then
            echo "Stopping ntp-client at:" > /home/fitzcarraldo/ntp-client.txt
            date >> /home/fitzcarraldo/ntp-client.txt
            rc-config stop ntp-client &>> /home/fitzcarraldo/ntp-client.txt
        fi
        ;;
esac

The nm-tool command no longer exists, so use the nmcli command instead in the script. See further down for a new version of the script.

Make the root user the owner of the script, and only allow the root user to write to it and execute it:

# cd /etc/NetworkManager/dispatcher.d/
# chown root:root 99_ntp-client
# chmod 744 99_ntp-client

Then add NetworkManagerDispatcher to the default runlevel so that it will be launched every time you boot your machine:

# rc-update add NetworkManagerDispatcher default

You no longer need to perform the above step regarding a NetworkManagerDispatcher initscript. See my update of September 23, 2014 at the bottom of this post.

As the package net-misc/ntp installs both /etc/init.d/ntpd and /etc/init.d/net-client, users could optionally add the NTP daemon ntpd to the default runlevel too if desired, which would provide continuous, incremental adjustments to the system clock once net-client has done its one-shot adjustment each time a network comes up:

# rc-update add ntpd default

But users who don’t leave their PCs on for days on end — or who use laptops — can ignore the above step and just stick with the NetworkManagerDispatcher and net-client solution, whereas users who leave their machines on for days or weeks on end can also use the NTP daemon to keep the system clock in sync in between the times when ntp-client has synchronised.

Don’t forget to delete ntp-client from the start-up level if you are using NetworkManagerDispatcher to run it:

# rc-update del ntp-client

Notice that the script /etc/NetworkManager/dispatcher.d/99_ntp-client logs some information in a text file ntp-client.txt in my home directory which I can check. Here is an example of what ntp-client.txt contains after I select a network (or it is selected automatically) following start up of my laptop:

System time before starting ntp-client:
Sun Jun 3 19:24:08 BST 2012
Starting ntp-client:
Restarting init script
* Setting clock via the NTP client 'ntpd' ...ntpd: time slew +0.067178s
[ ok ]
System time after starting ntp-client:
Sun Jun 3 19:24:17 BST 2012

As you can see above, the ntpd command was executed once by NetworkManagerDispatcher and made a small adjustment to the system time on my laptop.

Replacing ntpdate with ntpd in ntp-client

Just for the fun of it, I changed /etc/conf.d/ntp-client to use the command ntpd instead of ntpdate, even though the ntpdate command works fine. Anyway, here’s my /etc/conf.d/ntp-client file these days:

NTPCLIENT_CMD="ntpd"
NTPCLIENT_OPTS="-g -q"

I have added the -g option so that the ntpd command can make large adjustments to the system time if it is way off the actual time. This is useful at the beginning and end of Daylight Saving Time, or if you dual boot with Windows. Here is an example of the former when I powered up my laptop the morning after the clocks changed from BST to GMT at the end of Summer 2010:

$ cat /home/fitzcarraldo/ntp-client.txt
System time before starting ntp-client:
Sun Oct 31 09:37:23 GMT 2010
Starting ntp-client:
Starting init script
* Setting clock via the NTP client 'ntpd'...ntpd: time set -3600.122381s
[ ok ]
System time after starting ntp-client:
Sun Oct 31 08:37:30 GMT 2010

You can specify the NTP server or NTP server pool in the file /etc/ntp.conf, but the default server pool already specified in that file should work. Note again that, when ntpd is run with the -q option, it synchronises the system clock once and terminates, i.e. it is not running as a daemon.

UPDATE (September 23, 2014): Things have become easier since I wrote the above post. These days it is no longer necessary to create a NetworkManagerDispatcher initscript; you just need to put the shell script you want NetworkManagerDispatcher to execute into the directory /etc/NetworkManager/dispatcher.d/, give it attributes and permissions as shown in this post, and NetworkManager will take care of everything.

UPDATE (October 1, 2019): The nm-tool command no longer exists, so use the nmcli command instead in the script. The new version is shown below.

#!/bin/bash

INTERFACE=$1 # The interface which is brought up or down
STATUS=$2 # The new state of the interface

case "$STATUS" in
    'up') # $INTERFACE is up
        echo "----------" > /home/fitzcarraldo/ntp-client.txt
        echo "Interface $INTERFACE" >> /home/fitzcarraldo/ntp-client.txt
        echo -n "System time before starting ntp-client: " >> /home/fitzcarraldo/ntp-client.txt
        date >> /home/fitzcarraldo/ntp-client.txt
        echo "Starting ntp-client:" >> /home/fitzcarraldo/ntp-client.txt
        rc-config restart ntp-client &>> /home/fitzcarraldo/ntp-client.txt
        echo -n "System time after starting ntp-client: " >> /home/fitzcarraldo/ntp-client.txt
        date >> /home/fitzcarraldo/ntp-client.txt
        echo "----------" >> /home/fitzcarraldo/ntp-client.txt
        ;;
    'down') # $INTERFACE is down
        # Check for active interface and down if no one active
        if [ `nmcli c | grep -v "\-\-" | grep -v "NAME.*UUID.*TYPE.*DEVICE" | wc -l` -eq 0 ]; then
            echo "----------" > /home/fitzcarraldo/ntp-client.txt
            echo "Interface $INTERFACE" >> /home/fitzcarraldo/ntp-client.txt
            echo -n "Stopping ntp-client at: " >> /home/fitzcarraldo/ntp-client.txt
            date >> /home/fitzcarraldo/ntp-client.txt
            rc-config stop ntp-client &>> /home/fitzcarraldo/ntp-client.txt
            echo "----------" >> /home/fitzcarraldo/ntp-client.txt
        fi
        ;;
esac