#!/bin/bash
VERSION="1.003"
LEAPSRC="ftp://time.nist.gov/pub/leap-seconds.list"
MAXTRIES=6
INTERVAL=10
NTPCONF=/etc/ntp.conf
PREFETCH="60 days"
RESTART=
TMPFILE="/tmp/leap-seconds.$$.tmp"
LOGFAC=daemon
PATHLIST="/opt/sbin:/opt/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:"
REQUIREDCMDS=" wget logger tr sed shasum"
SELF="`basename $0`"
function displayHelp {
cat <<EOF
Usage: $SELF [options] [leapfile]
Verifies and if necessary, updates leap-second definition file
All arguments are optional: Default (or current value) shown:
-s Specify the URL of the master copy to download
$LEAPSRC
-4 Use only IPv4
-6 Use only IPv6
-p 4|6
Prefer IPv4 or IPv6 (as specified) addresses, but use either
-d Specify the filename on the local system
$LEAPFILE
-e Specify how long before expiration the file is to be refreshed
Units are required, e.g. "-e 60 days" Note that larger values
imply more frequent refreshes.
"$PREFETCH"
-f Specify location of ntp.conf (used to make sure leapfile directive is
present and to default leapfile)
$NTPCONF
-F Force update even if current file is OK and not close to expiring.
-c Command to restart NTP after installing a new file
<none> - ntpd checks file daily
-r Specify number of times to retry on get failure
$MAXTRIES
-i Specify number of minutes between retries
$INTERVAL
-l Use syslog for output (Implied if CRONJOB is set)
-L Don't use syslog for output
-P Specify the syslog facility for logging
$LOGFAC
-t Name of temporary file used in validation
$TMPFILE
-q Only report errors to stdout
-v Verbose output
-z Specify path for utilities
$PATHLIST
-Z Only use system path
$SELF will validate the file currently on the local system
Ordinarily, the file is found using the "leapfile" directive in $NTPCONF.
However, an alternate location can be specified on the command line.
If the file does not exist, is not valid, has expired, or is expiring soon,
a new copy will be downloaded. If the new copy validates, it is installed and
NTP is (optionally) restarted.
If the current file is acceptable, no download or restart occurs.
-c can also be used to invoke another script to perform administrative
functions, e.g. to copy the file to other local systems.
This can be run as a cron job. As the file is rarely updated, and leap
seconds are announced at least one month in advance (usually longer), it
need not be run more frequently than about once every three weeks.
For cron-friendly behavior, define CRONJOB=1 in the crontab.
This script depends on$REQUIREDCMDS
Version $VERSION
EOF
return 0
}
SYSLOG="$CRONJOB"
if [ "$1" = "--help" ]; then
displayHelp
exit 0
fi
while getopts 46p:P:s:e:f:Fc:r:i:lLt:hqvz:Z opt; do
case $opt in
4)
PROTO="-4"
;;
6)
PROTO="-6"
;;
p)
if [ "$OPTARG" = '4' -o "$OPTARG" = '6' ]; then
PREFER="--prefer-family=IPv$OPTARG"
else
echo "Invalid -p $OPTARG" >&2
exit 1;
fi
;;
P)
LOGFAC="$OPTARG"
;;
s)
LEAPSRC="$OPTARG"
;;
e)
PREFETCH="$OPTARG"
;;
f)
NTPCONF="$OPTARG"
;;
F)
FORCE="Y"
;;
c)
RESTART="$OPTARG"
;;
r)
MAXTRIES="$OPTARG"
;;
i)
INTERVAL="$OPTARG"
;;
t)
TMPFILE="$OPTARG"
;;
l)
SYSLOG="y"
;;
L)
SYSLOG=
;;
h)
displayHelp
exit 0
;;
q)
QUIET="Y"
;;
v)
VERBOSE="Y"
;;
z)
PATHLIST="$OPTARG:"
;;
Z)
PATHLIST=
;;
*)
echo "$SELF -h for usage" >&2
exit 1
;;
esac
done
shift $((OPTIND-1))
export PATH="$PATHLIST$PATH"
for P in $REQUIREDCMDS ; do
if >/dev/null 2>&1 which "$P" ; then
continue
fi
[ "$P" = "logger" ] && continue
echo "FATAL: missing $P command, please install"
exit 1
done
if ! LOGGER="`2>/dev/null which logger`" ; then
LOGGER=
fi
function log {
if [ -z "$SYSLOG" -o -z "$LOGGER" ]; then
if [ -n "$QUIET" -a \( "$1" = "info" -o "$1" = "notice" -o "$1" = "debug" \) ]; then
return 0
fi
echo "`echo \"$1\" | tr a-z A-Z`: $2"
return 0
fi
local S
if [ -n "$CRONJOB" -a \( "$1" != "info" \) -a \( "$1" != "debug" \) ] || [ -n "$VERBOSE" ]; then
S="-s"
fi
$LOGGER $S -t "$SELF[$$]" -p "$LOGFAC.$1" "$2"
}
INTERVAL=$(( $INTERVAL *1 ))
function verifySHA {
if [ ! -f "$1" ]; then
return 1
fi
local RAW="`sed $1 -e'/^\\([0-9]\\|#[\$@h]\)/!d' -e'/^#[\$@h]/!s/#.*\$//g'`"
local DATA="`echo \"$RAW\" | sed -e'/^#h/d' -e's/^#[\$@]//g' | tr -d '[:space:]'`"
local DSHA="`echo -n \"$DATA\" | shasum | sed -e's/[? *].*$//'`"
local FSHA="`echo \"$RAW\" | sed -e'/^#h/!d' -e's/^#h//' -e's/[ ] */ 0x/g'`"
FSHA=`printf '%08x%08x%08x%08x%08x' $FSHA`
if [ -n "$FSHA" -a \( "$FSHA" = "$DSHA" \) ]; then
if [ -n "$2" ]; then
log "info" "Checksum of $1 validated"
fi
else
log "error" "Checksum of $1 is invalid:"
[ -z "$FSHA" ] && FSHA="(no checksum record found in file)"
log "error" "EXPECTED: $FSHA"
log "error" "COMPUTED: $DSHA"
return 1
fi
EXPIRES="`echo \"$RAW\" | sed -e'/^#@/!d' -e's/^#@//' | tr -d '[:space:]'`"
EXPIRES="$(($EXPIRES - 2208988800 ))"
if [ $EXPIRES -lt `date -u +%s` ]; then
log "notice" "File expired on `date -u -d \"Jan 1, 1970 00:00:00 +0000 + $EXPIRES seconds\"`"
return 2
fi
}
if ! [ -f "$NTPCONF" ]; then
log "critical" "Missing ntp configuration $NTPCONF"
exit 1
fi
LEAPFILE="`sed $NTPCONF -e'/^ *leapfile *.*$/!d' -e's/^ *leapfile *//'`"
if [ -z "$LEAPFILE" ]; then
log "error" "$NTPCONF does not specify a leapfile"
fi
if [ -n "$1" ]; then
if [ "$1" != "$LEAPFILE" ]; then
log "notice" "Requested install to $1, but $NTPCONF specifies $LEAPFILE"
fi
LEAPFILE="$1"
fi
if [ -n "$FORCE" ] || ! verifySHA $LEAPFILE "$VERBOSE" || [ $EXPIRES -lt `date -d "NOW + $PREFETCH" +%s` ] ; then
TRY=0
while true; do
TRY=$(( $TRY + 1 ))
if [ -n "$VERBOSE" ]; then
log "info" "Attempting download from $LEAPSRC, try $TRY.."
fi
if wget $PROTO $PREFER -o ${TMPFILE}.log $LEAPSRC -O $TMPFILE ; then
log "info" "Download of $LEAPSRC succeeded"
if [ -n "$VERBOSE" ]; then
cat ${TMPFILE}.log
fi
if ! verifySHA $TMPFILE "$VERBOSE" ; then
log "warning" "Downloaded file $TMPFILE rejected -- saved for diagnosis"
cat ${TMPFILE}.log
rm -f ${TMPFILE}.log
exit 1
fi
rm -f ${TMPFILE}.log
REFFILE="$LEAPFILE"
if [ ! -f $LEAPFILE ]; then
log "notice" "$LEAPFILE was missing, creating new copy - check permissions"
touch $LEAPFILE
REFFILE="$NTPCONF"
fi
chmod --reference $REFFILE $TMPFILE
chown --reference $REFFILE $TMPFILE
( which selinuxenabled && selinuxenabled && which chcon ) >/dev/null 2>&1
if [ $? == 0 ] ; then
chcon --reference $REFFILE $TMPFILE
fi
if mv -f $TMPFILE $LEAPFILE ; then
log "notice" "Installed new $LEAPFILE from $LEAPSRC"
else
log "error" "Install $TMPFILE => $LEAPFILE failed -- saved for diagnosis"
exit 1
fi
if [ -n "$RESTART" ]; then
if [ -n "$VERBOSE" ]; then
log "info" "Attempting restart action: $RESTART"
fi
R="$( 2>&1 $RESTART )"
if [ $? -eq 0 ]; then
log "notice" "Restart action succeeded"
if [ -n "$VERBOSE" -a -n "$R" ]; then
log "info" "$R"
fi
else
log "error" "Restart action failed"
if [ -n "$R" ]; then
log "error" "$R"
fi
exit 2
fi
fi
exit 0
fi
rm -f $TMPFILE
if [ $TRY -ge $MAXTRIES ]; then
break;
fi
if [ -n "$VERBOSE" ]; then
cat ${TMPFILE}.log
log "info" "Waiting $INTERVAL minutes before retrying..."
fi
sleep $(( $INTERVAL * 60))
done
log "warning" "Download from $LEAPSRC failed after $TRY attempts"
if [ -f ${TMPFILE}.log ]; then
cat ${TMPFILE}.log
rm -f ${TMPFILE}.log $TMPFILE
fi
exit 1
fi
log "info" "Not time to replace $LEAPFILE"
exit 0