httpdate - set local date and time from a web server
While ntp may be a great protocol, I find it quite bloated and slow for the simple purpose of just setting a local date and time to a reference clock. I do not need 20ms accuracy on a notebook's clock . Thus I use(d) rdate for a decade now but the public rdate servers are slowly dying out. So I'm replacing it more and more with htpdate which works quite nicely. It's written in C and a perl alternative is available on the author's site. There is also a forked windows version of it available.
Developing a bit larger bash script (which syncs a few servers), I wondered whether I could realize the time sync part in bash as well.
It's quite possible:
- # open a tcp connection to www.google.com
- exec 3<>/dev/tcp/www.google.com/80
- # say hello HTTP-style
- echo -e "GET / HTTP/1.0\n\n">&3
- # parse for a Date: line and with a bit of magic throw the date-string at the date command
- LC_ALL=C LANG=en date --rfc-2822 --utc -s "$(head <&3 | grep -i "Date: " | sed -e s/Date\:\ //I)"
- # close the tcp connection
- exec 3<&-
Simple, eh?
A stand-alone version is available as httpdate and looks like this in v0.1:
- #!/bin/bash
- #
- # Purpose: display or set date from a host via http
- #
- # Requires: bash, ping, head, date, grep & sed
- # Autor: Daniel Lange, http://daniel-lange.com/software/
- # License: GPL v3 or later, http://www.gnu.org/licenses/
- #
- # Version: v0.1, 08-10-22, initial release
- #
- if [ "$1" = "-h" -o "$1" = "--help" ] ; then
- echo "$0 v0.1 (c) Daniel Lange, 2008. Released under the GNU GPL v3 or later."
- echo "Usage: $0 <-d|-s> <HOST>"
- echo " -d = display date (default)"
- echo " -s = set date (may require root privileges)"
- echo " HOST = FQDN of the server to retrieve the Date: header from"
- echo " (default: www.google.com)"
- exit 0
- fi
- if [ "$1" != "-d" -a "$1" != "-s" ] ; then
- echo "Error: either -d or -s expected as first argument".
- exit 1
- fi
- if [ "$#" -gt 2 ] ; then
- echo "Error: too many arguments, use -h for help"
- exit 2
- fi
- DATE_OPTION="$1"
- DATE_HOST="${2:-www.google.com}"
- ping -q -c 1 -w 10 -W 10 $DATE_HOST > /dev/null 2>&1
- if [ $? -ne 0 ] ; then
- echo "Error: Host $DATE_HOST not found (or ping missing)."
- exit 3
- fi
- exec 3<>/dev/tcp/$DATE_HOST/80
- echo -e "GET / HTTP/1.0\n\n">&3
- LC_ALL=C LANG=en_EN date --rfc-2822 --utc $DATE_OPTION "$(head <&3 | grep -i "Date: " | sed -e s/Date\:\ //I)"
- exec 3<&-
Update:
27.09.09: Just FYI: The Debian/Ubuntu (before 09.10, launchpad bug entry) bash does not have the capability to use/dev/tcp
. It's been compiled with –disable-net-redirections
.
Comments
Display comments as Linear | Threaded
Jorge Ventura on :
Hi Daniel, I am trying to use your script httpdate but I don't have /dev/tcp in my Debian...
To tell you the truth I never heard about /dev/tcp. Do you have any pointer to me???
Thank you, Ventura
P.S: Your script is very useful. I have a situation where the ntp port is not open in the firewall.
Daniel Lange on :
Dear Ventura,
Debian has deliberately compiled bash to disable the /dev/tcp functionality (--disable-net-redirections). See http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=146464 for the corresponding bug. So you'd need to re-compile bash manually. Or use Ubuntu. Or most other distros. Easier is probably using the C or perl version of htpdate linked at the beginning of the blog entry.
Philipp Gühring on :
I could need a httpdate that works with Proxies (using the http_proxy or https_proxy environment variable ...)
Daniel Lange on :
If the http proxy doesn't require authentication a simple amendment like
exec 3/dev/tcp/$PROXY_IP/$PROXY_PORT
echo -e "GET http://www.google.com HTTP/1.0\n\n">&3
will do.
If the proxy requires authentication you need to construct an "Authorization:" Header and send that after the GET line.
For details see e.g. http://en.wikipedia.org/wiki/Basic_access_authentication#Client_side .
Misel on :
I almost coded this myself but figured someone must have done this before.
My reasoning wasn't the dislike for NTP, though. I operate a RaspberryPi behind a rather strict firewall that only allows HTTP(S) to the outside. So this is an easy way to set the time, when NTP is not available.
Thank you very much.