Home | History | Annotate | Line # | Download | only in update-leap
      1      1.1  christos #! @PATH_PERL@ -w
      2  1.1.1.4  christos # @configure_input
      3      1.1  christos 
      4  1.1.1.3  christos # Copyright (C) 2015, 2017 Network Time Foundation
      5      1.1  christos # Author: Harlan Stenn
      6  1.1.1.3  christos #
      7  1.1.1.3  christos # General cleanup and https support: Paul McMath
      8  1.1.1.3  christos #
      9      1.1  christos # Original shell version:
     10      1.1  christos # Copyright (C) 2014 Timothe Litt litt at acm dot org
     11  1.1.1.3  christos #
     12      1.1  christos # This script may be freely copied, used and modified providing that
     13      1.1  christos # this notice and the copyright statement are included in all copies
     14      1.1  christos # and derivative works.  No warranty is offered, and use is entirely at
     15      1.1  christos # your own risk.  Bugfixes and improvements would be appreciated by the
     16      1.1  christos # author.
     17      1.1  christos 
     18  1.1.1.3  christos ######## BEGIN #########
     19      1.1  christos use strict;
     20      1.1  christos 
     21  1.1.1.3  christos # Core modules
     22      1.1  christos use Digest::SHA qw(sha1_hex);
     23  1.1.1.3  christos use File::Basename;
     24      1.1  christos use File::Copy qw(move);
     25  1.1.1.3  christos use File::Temp qw(tempfile);
     26      1.1  christos use Getopt::Long qw(:config auto_help no_ignore_case bundling);
     27  1.1.1.3  christos use Sys::Syslog qw(:standard :macros);
     28      1.1  christos 
     29  1.1.1.3  christos # External modules
     30  1.1.1.3  christos use HTTP::Tiny 0.056;
     31  1.1.1.3  christos use Net::SSLeay 1.49;
     32  1.1.1.3  christos use IO::Socket::SSL 1.56;
     33  1.1.1.3  christos 
     34  1.1.1.3  christos my $VERSION = '1.004';
     35  1.1.1.3  christos 
     36  1.1.1.3  christos my $RUN_DIR = '/tmp';
     37  1.1.1.3  christos my $RUN_UID = 0;
     38  1.1.1.3  christos my $TMP_FILE;
     39  1.1.1.3  christos my $TMP_FH;
     40  1.1.1.3  christos my $FILE_MODE = 0644;
     41  1.1.1.3  christos 
     42  1.1.1.3  christos ######## DEFAULT CONFIGURATION ##########
     43  1.1.1.3  christos # LEAP FILE SRC URIS
     44  1.1.1.3  christos #    HTTPS - (default)
     45  1.1.1.3  christos #    	https://www.ietf.org/timezones/data/leap-seconds
     46  1.1.1.3  christos #    HTTP - No TLS/SSL - (not recommended)
     47  1.1.1.3  christos #	http://www.ietf.org/timezones/data/leap-seconds.list
     48      1.1  christos 
     49  1.1.1.3  christos my $LEAPSRC = 'https://www.ietf.org/timezones/data/leap-seconds.list';
     50      1.1  christos my $LEAPFILE;
     51      1.1  christos 
     52      1.1  christos # How many times to try to download new file
     53  1.1.1.3  christos my $MAXTRIES = 6;
     54  1.1.1.3  christos my $INTERVAL = 10;
     55      1.1  christos 
     56  1.1.1.3  christos my $NTPCONF='/etc/ntp.conf';
     57      1.1  christos 
     58      1.1  christos # How long (in days) before expiration to get updated file
     59  1.1.1.3  christos my $PREFETCH = 60;
     60  1.1.1.3  christos my $EXPIRES;
     61  1.1.1.3  christos my $FORCE;
     62      1.1  christos 
     63  1.1.1.3  christos # Output Flags
     64  1.1.1.3  christos my $QUIET;
     65  1.1.1.3  christos my $DEBUG;
     66  1.1.1.3  christos my $SYSLOG;
     67  1.1.1.3  christos my $TOTERM;
     68  1.1.1.3  christos my $LOGFAC = 'LOG_USER';
     69  1.1.1.3  christos 
     70  1.1.1.3  christos ######### PARSE/SET OPTIONS #########
     71  1.1.1.3  christos my %SSL_OPTS;
     72  1.1.1.3  christos my %SSL_ATTRS = (
     73  1.1.1.3  christos     verify_SSL => 1,  
     74  1.1.1.3  christos     SSL_options => \%SSL_OPTS,
     75  1.1.1.3  christos );
     76      1.1  christos 
     77  1.1.1.3  christos our(%opt);
     78      1.1  christos 
     79  1.1.1.3  christos GetOptions(\%opt,	
     80  1.1.1.3  christos 	'C=s',
     81  1.1.1.3  christos 	'D=s',
     82  1.1.1.3  christos 	'e:60',
     83  1.1.1.3  christos 	'F',
     84  1.1.1.3  christos 	'f=s',
     85  1.1.1.3  christos 	'h|help',
     86  1.1.1.3  christos 	'i:10',
     87  1.1.1.3  christos 	'L=s',
     88  1.1.1.3  christos 	'l=s',
     89  1.1.1.3  christos 	'q',
     90  1.1.1.3  christos 	'r:6',
     91  1.1.1.3  christos 	's',
     92  1.1.1.3  christos 	't',
     93  1.1.1.3  christos 	'u=s',
     94  1.1.1.3  christos 	'v',
     95  1.1.1.3  christos 	);
     96      1.1  christos 
     97  1.1.1.3  christos $LOGFAC   = $opt{l} if defined $opt{l};
     98  1.1.1.3  christos $LEAPSRC  = $opt{u} if defined $opt{u};
     99  1.1.1.3  christos $LEAPFILE = $opt{L} if defined $opt{L};
    100  1.1.1.3  christos $PREFETCH = $opt{e} if defined $opt{e};
    101  1.1.1.3  christos $NTPCONF  = $opt{f} if defined $opt{f};
    102  1.1.1.3  christos $MAXTRIES = $opt{r} if defined $opt{r};
    103  1.1.1.3  christos $INTERVAL = $opt{i} if defined $opt{i};
    104  1.1.1.3  christos 
    105  1.1.1.3  christos $FORCE   = 1 if defined $opt{F};
    106  1.1.1.3  christos $DEBUG	 = 1 if defined $opt{v};
    107  1.1.1.3  christos $QUIET   = 1 if defined $opt{q};
    108  1.1.1.3  christos $SYSLOG  = 1 if defined $opt{s};
    109  1.1.1.3  christos $TOTERM  = 1 if defined $opt{t};
    110  1.1.1.3  christos 
    111  1.1.1.3  christos $SSL_OPTS{SSL_ca_file} = $opt{C} if (defined($opt{C}));
    112  1.1.1.3  christos $SSL_OPTS{SSL_ca_path} = $opt{D} if (defined($opt{D}));
    113  1.1.1.3  christos 
    114  1.1.1.3  christos ###############
    115  1.1.1.3  christos ## START MAIN
    116  1.1.1.3  christos ###############
    117  1.1.1.3  christos my $PROG = basename($0);
    118  1.1.1.3  christos 
    119  1.1.1.3  christos # Logging - Default is to use syslog(3) if STDOUT isn't 
    120  1.1.1.3  christos # connected to a tty.
    121  1.1.1.3  christos if ($SYSLOG || !-t STDOUT) {
    122  1.1.1.3  christos     $SYSLOG = 1;
    123  1.1.1.3  christos     openlog($PROG, 'pid', $LOGFAC);
    124  1.1.1.3  christos } 
    125  1.1.1.3  christos else {
    126  1.1.1.3  christos     $TOTERM = 1;
    127  1.1.1.3  christos }
    128      1.1  christos 
    129  1.1.1.3  christos if (defined $opt{q} && defined $opt{v}) {
    130  1.1.1.3  christos     log_fatal(LOG_ERR, '-q and -v options mutually exclusive');
    131  1.1.1.3  christos }
    132      1.1  christos 
    133  1.1.1.3  christos if (defined $opt{L} && defined $opt{f}) {
    134  1.1.1.3  christos     log_fatal(LOG_ERR, '-L and -f options mutually exclusive');
    135  1.1.1.3  christos }
    136      1.1  christos 
    137  1.1.1.3  christos $SIG{INT} = \&signal_catcher;
    138  1.1.1.3  christos $SIG{TERM} = \&signal_catcher;
    139  1.1.1.3  christos $SIG{QUIT} = \&signal_catcher;
    140  1.1.1.3  christos 
    141  1.1.1.3  christos # Take some security precautions
    142  1.1.1.3  christos close STDIN;
    143  1.1.1.3  christos 
    144  1.1.1.3  christos # Show help
    145  1.1.1.3  christos if (defined $opt{h}) {
    146  1.1.1.3  christos     show_help();
    147  1.1.1.3  christos     exit 0;
    148  1.1.1.3  christos }
    149      1.1  christos 
    150  1.1.1.3  christos if ($< != $RUN_UID) {
    151  1.1.1.3  christos     log_fatal(LOG_ERR, 'User ' . getpwuid($<) . " (UID $<) tried to run $PROG");
    152  1.1.1.3  christos }
    153      1.1  christos 
    154  1.1.1.3  christos chdir $RUN_DIR || log_fatal("Failed to change dir to $RUN_DIR");
    155      1.1  christos 
    156  1.1.1.3  christos # Parse ntp.conf for path to leapfile if not set by user
    157  1.1.1.3  christos if (! $LEAPFILE) {
    158      1.1  christos 
    159  1.1.1.3  christos     open my $LF, '<', $NTPCONF || log_fatal(LOG_ERR, "Can't open <$NTPCONF>: $!");
    160      1.1  christos 
    161  1.1.1.3  christos     while (<$LF>) {
    162  1.1.1.3  christos 	chomp;
    163  1.1.1.3  christos 	$LEAPFILE = $1 if /^ *leapfile\s+"(\S+)"/;
    164  1.1.1.3  christos     }
    165  1.1.1.3  christos     close $LF;
    166      1.1  christos 
    167  1.1.1.3  christos     if (! $LEAPFILE) {
    168  1.1.1.3  christos 	log_fatal(LOG_ERR, "No leapfile directive in $NTPCONF; leapfile location not known"); 
    169  1.1.1.3  christos     }
    170  1.1.1.3  christos }
    171      1.1  christos 
    172  1.1.1.3  christos -s $LEAPFILE || logger(LOG_DEBUG, "Leapfile $LEAPFILE is empty");
    173      1.1  christos 
    174  1.1.1.3  christos # Download new file if:
    175  1.1.1.3  christos #   1. file doesn't exist
    176  1.1.1.3  christos #   2. invoked w/ force flag (-F)
    177  1.1.1.3  christos #   3. current file isn't valid
    178  1.1.1.3  christos #   4. current file expired or expires soon
    179      1.1  christos 
    180  1.1.1.3  christos if ( !-e $LEAPFILE || $FORCE || ! verifySHA($LEAPFILE) || 
    181  1.1.1.3  christos 	( $EXPIRES lt ( $PREFETCH * 86400 + time() ) )) {
    182      1.1  christos 
    183  1.1.1.3  christos     for (my $try = 1; $try <= $MAXTRIES; $try++) {
    184  1.1.1.3  christos 	logger(LOG_DEBUG, "Attempting download from $LEAPSRC, try $try..");
    185      1.1  christos 
    186  1.1.1.3  christos 	($TMP_FH, $TMP_FILE) = tempfile(UNLINK => 1, SUFFIX => '.list');
    187      1.1  christos 
    188  1.1.1.3  christos 	if (retrieve_file($TMP_FH)) {
    189      1.1  christos 
    190  1.1.1.3  christos             if ( verifySHA($TMP_FILE) ) {
    191  1.1.1.3  christos 		move_file($TMP_FILE, $LEAPFILE);
    192  1.1.1.3  christos 		chmod $FILE_MODE, $LEAPFILE; 
    193  1.1.1.3  christos 		logger(LOG_INFO, "Installed new $LEAPFILE from $LEAPSRC");
    194  1.1.1.3  christos 	    }
    195  1.1.1.3  christos 	    else {
    196  1.1.1.3  christos                 logger(LOG_ERR, "Downloaded file $TMP_FILE rejected -- saved for diagnosis");
    197  1.1.1.3  christos 		move_file($TMP_FILE, 'leap-seconds.list_corrupt');
    198  1.1.1.3  christos 		exit 1;
    199  1.1.1.3  christos             }
    200  1.1.1.3  christos 	    # Fall through
    201  1.1.1.3  christos             exit 0;
    202  1.1.1.3  christos 	}
    203  1.1.1.3  christos 
    204  1.1.1.3  christos 	# Failure
    205  1.1.1.3  christos 	unlink $TMP_FILE;
    206  1.1.1.3  christos 	logger(LOG_INFO, "Download failed. Waiting $INTERVAL minutes before retrying...");
    207  1.1.1.3  christos         sleep $INTERVAL * 60 ;
    208  1.1.1.3  christos     }
    209  1.1.1.3  christos 
    210  1.1.1.3  christos     # Failed and out of retries
    211  1.1.1.3  christos     log_fatal(LOG_ERR, "Download from $LEAPSRC failed after $MAXTRIES attempts");
    212  1.1.1.3  christos }
    213      1.1  christos 
    214  1.1.1.3  christos logger(LOG_INFO, "Not time to replace $LEAPFILE");
    215      1.1  christos 
    216  1.1.1.3  christos exit 0;
    217      1.1  christos 
    218  1.1.1.3  christos ######## SUB ROUTINES #########
    219  1.1.1.3  christos sub move_file {
    220      1.1  christos 
    221  1.1.1.3  christos     (my $src, my $dst) = @_;
    222  1.1.1.3  christos 
    223  1.1.1.3  christos     if ( move($src, $dst) ) {
    224  1.1.1.3  christos 	logger(LOG_DEBUG, "Moved $src to $dst");
    225  1.1.1.3  christos     } 
    226  1.1.1.3  christos     else {
    227  1.1.1.3  christos 	log_fatal(LOG_ERR, "Moving $src to $dst failed: $!");
    228  1.1.1.3  christos     }
    229  1.1.1.3  christos }
    230  1.1.1.3  christos 
    231  1.1.1.3  christos # Removes temp file if terminating signal recv'd
    232  1.1.1.3  christos sub signal_catcher {
    233  1.1.1.3  christos     my $signame = shift;
    234  1.1.1.3  christos 
    235  1.1.1.3  christos     close $TMP_FH;
    236  1.1.1.3  christos     unlink $TMP_FILE;
    237  1.1.1.3  christos     log_fatal(LOG_INFO, "Recv'd SIG${signame}. Terminating.");
    238  1.1.1.3  christos }	    
    239  1.1.1.3  christos 
    240  1.1.1.3  christos sub log_fatal {
    241  1.1.1.3  christos     my ($p, $msg) = @_;
    242  1.1.1.3  christos     logger($p, $msg);
    243  1.1.1.3  christos     exit 1;
    244  1.1.1.3  christos }
    245      1.1  christos 
    246      1.1  christos sub logger {
    247  1.1.1.3  christos     my ($p, $msg) = @_;
    248  1.1.1.3  christos 
    249  1.1.1.3  christos     # Suppress LOG_DEBUG msgs unless $DEBUG set
    250  1.1.1.3  christos     return if (!$DEBUG && $p eq LOG_DEBUG);
    251  1.1.1.3  christos 
    252  1.1.1.3  christos     # Suppress all but LOG_ERR msgs if $QUIET set
    253  1.1.1.3  christos     return if ($QUIET && $p ne LOG_ERR);
    254      1.1  christos 
    255  1.1.1.3  christos     if ($TOTERM) {
    256  1.1.1.3  christos         if ($p eq LOG_ERR) {	# errors should go to STDERR
    257  1.1.1.3  christos 	    print STDERR "$msg\n";
    258  1.1.1.3  christos 	}
    259  1.1.1.3  christos 	else {
    260  1.1.1.3  christos 	    print STDOUT "$msg\n";
    261      1.1  christos 	}
    262      1.1  christos     }
    263      1.1  christos 
    264  1.1.1.3  christos     if ($SYSLOG) {
    265  1.1.1.3  christos 	syslog($p, $msg)
    266      1.1  christos     }
    267      1.1  christos }
    268      1.1  christos 
    269  1.1.1.3  christos #################################
    270  1.1.1.3  christos # Connect to server and retrieve file
    271  1.1.1.3  christos #
    272  1.1.1.3  christos # Since we make as many as $MAXTRIES attempts to connect to the remote
    273  1.1.1.3  christos # server to download the file, the network socket should be closed after
    274  1.1.1.3  christos # each attempt, rather than let it be reused (because it may be in some
    275  1.1.1.3  christos # unknown state).
    276  1.1.1.3  christos #
    277  1.1.1.3  christos # HTTP::Tiny doesn't export a method to explicitly close a connected
    278  1.1.1.3  christos # socket, therefore, we instantiate the lexically scoped $http object in
    279  1.1.1.3  christos # a function; when the function returns, the object goes out of scope
    280  1.1.1.3  christos # and is destroyed, closing the socket.
    281  1.1.1.3  christos sub retrieve_file {
    282  1.1.1.3  christos 
    283  1.1.1.3  christos     my $fh = shift;
    284  1.1.1.3  christos     my $http;
    285  1.1.1.3  christos 
    286  1.1.1.3  christos     if ($LEAPSRC =~ /^https\S+/) {
    287  1.1.1.3  christos 	$http = HTTP::Tiny->new(%SSL_ATTRS);
    288  1.1.1.3  christos 	(my $ok, my $why) = $http->can_ssl;
    289  1.1.1.3  christos 	log_fatal(LOG_ERR, "TLS/SSL config error: $why") if ! $ok;
    290  1.1.1.3  christos     } 
    291  1.1.1.3  christos     else {
    292  1.1.1.3  christos 	$http = HTTP::Tiny->new();
    293  1.1.1.3  christos     }
    294  1.1.1.3  christos 
    295  1.1.1.3  christos     my $reply = $http->get($LEAPSRC);
    296  1.1.1.3  christos 
    297  1.1.1.3  christos     if ($reply->{success}) {
    298  1.1.1.3  christos 	logger(LOG_DEBUG, "Download of $LEAPSRC succeeded");
    299  1.1.1.3  christos 	print $fh $reply->{content} || 
    300  1.1.1.3  christos 	    log_fatal(LOG_ERR, "Couldn't write new file contents to temp file: $!");
    301  1.1.1.3  christos 	close $fh;
    302  1.1.1.3  christos 	return 1;
    303  1.1.1.3  christos     } 
    304  1.1.1.3  christos     else {
    305  1.1.1.3  christos 	close $fh;
    306  1.1.1.3  christos 	return 0;
    307  1.1.1.3  christos     }
    308  1.1.1.3  christos }
    309      1.1  christos 
    310  1.1.1.3  christos ########################
    311      1.1  christos # Validate a leap-seconds file checksum
    312      1.1  christos #
    313  1.1.1.3  christos # File format: (full description in file)
    314  1.1.1.3  christos # Pound sign (#) marks comments, EXCEPT:
    315  1.1.1.3  christos # 	#$ number : the NTP date of the last update
    316  1.1.1.3  christos # 	#@ number : the NTP date that the file expires
    317  1.1.1.3  christos # 	#h hex hex hex hex hex : the SHA-1 checksum of the data & dates, 
    318  1.1.1.3  christos #	   excluding whitespace w/o leading zeroes
    319  1.1.1.3  christos #
    320  1.1.1.3  christos # Date (seconds since 1900) leaps : leaps is the # of seconds to add
    321  1.1.1.3  christos #  for times >= Date 
    322      1.1  christos # Date lines have comments.
    323      1.1  christos #
    324      1.1  christos # Returns:
    325  1.1.1.3  christos #   0	Invalid Checksum/Expired
    326  1.1.1.3  christos #   1	File is valid
    327      1.1  christos 
    328      1.1  christos sub verifySHA {
    329      1.1  christos 
    330  1.1.1.3  christos     my $file = shift;
    331  1.1.1.3  christos     my $fh;
    332  1.1.1.3  christos     my $data;
    333      1.1  christos     my $FSHA;
    334      1.1  christos 
    335  1.1.1.3  christos     open $fh, '<', $file || log_fatal(LOG_ERR, "Can't open $file: $!");
    336  1.1.1.3  christos 
    337      1.1  christos     # Remove comments, except those that are markers for last update,
    338      1.1  christos     # expires and hash
    339  1.1.1.3  christos     while (<$fh>) {
    340      1.1  christos 	if (/^#\$/) {
    341  1.1.1.3  christos 	    s/^..//;
    342  1.1.1.3  christos 	    $data .= $_;
    343      1.1  christos 	}
    344      1.1  christos 	elsif (/^#\@/) {
    345  1.1.1.3  christos 	    s/^..//;
    346  1.1.1.3  christos 	    $data .= $_;
    347  1.1.1.3  christos 	    s/\s+//g;
    348  1.1.1.3  christos 	    $EXPIRES = $_ - 2208988800;
    349      1.1  christos 	}
    350      1.1  christos 	elsif (/^#h\s+([[:xdigit:]]+)\s+([[:xdigit:]]+)\s+([[:xdigit:]]+)\s+([[:xdigit:]]+)\s+([[:xdigit:]]+)/) {
    351  1.1.1.3  christos 	    chomp;
    352  1.1.1.3  christos 	    $FSHA = sprintf("%08s%08s%08s%08s%08s", $1, $2, $3, $4, $5);
    353      1.1  christos 	}
    354      1.1  christos 	elsif (/^#/) {
    355  1.1.1.3  christos 	    # ignore it
    356      1.1  christos 	}
    357      1.1  christos 	elsif (/^\d/) {
    358  1.1.1.3  christos 	    s/#.*$//;
    359  1.1.1.3  christos 	    $data .= $_;
    360  1.1.1.3  christos 	} 
    361  1.1.1.3  christos 	else {
    362  1.1.1.3  christos 	    chomp;
    363  1.1.1.3  christos 	    print "Unexpected line: <$_>\n";
    364      1.1  christos 	}
    365      1.1  christos     }
    366  1.1.1.3  christos     close $fh;
    367      1.1  christos 
    368      1.1  christos     if ( $EXPIRES < time() ) {
    369  1.1.1.3  christos         logger(LOG_DEBUG, 'File expired on ' . gmtime($EXPIRES));
    370  1.1.1.3  christos         return 0;
    371      1.1  christos     }
    372      1.1  christos 
    373  1.1.1.3  christos     if (! $FSHA) {
    374  1.1.1.3  christos 	logger(LOG_NOTICE, "no checksum record found in file");
    375  1.1.1.3  christos 	return 0;
    376      1.1  christos     }
    377      1.1  christos 
    378  1.1.1.3  christos     # Remove all white space
    379  1.1.1.3  christos     $data =~ s/\s//g;
    380      1.1  christos 
    381  1.1.1.3  christos     # Compute the SHA hash of the data, removing the marker and filename
    382  1.1.1.3  christos     # Computed in binary mode, which shouldn't matter since whitespace has been removed
    383  1.1.1.3  christos     my $DSHA = sha1_hex($data);
    384      1.1  christos 
    385  1.1.1.3  christos     if ($FSHA eq $DSHA) {
    386  1.1.1.3  christos 	logger(LOG_DEBUG, "Checksum of $file validated");
    387  1.1.1.3  christos 	return 1;
    388  1.1.1.3  christos     } 
    389  1.1.1.3  christos     else {
    390  1.1.1.3  christos         logger(LOG_NOTICE, "Checksum of $file is invalid EXPECTED: $FSHA COMPUTED: $DSHA");
    391  1.1.1.3  christos         return 0;
    392      1.1  christos     }
    393      1.1  christos }
    394      1.1  christos 
    395  1.1.1.3  christos sub show_help {
    396  1.1.1.3  christos print <<EOF
    397      1.1  christos 
    398  1.1.1.3  christos Usage: $PROG [options]
    399      1.1  christos 
    400  1.1.1.3  christos Verifies and if necessary, updates leap-second definition file
    401      1.1  christos 
    402  1.1.1.3  christos All arguments are optional:  Default (or current value) shown:
    403  1.1.1.3  christos     -C    Absolute path to CA Cert (see SSL/TLS Considerations)
    404  1.1.1.3  christos     -D    Path to a CAdir (see SSL/TLS Considerations)
    405  1.1.1.3  christos     -e    Specify how long (in days) before expiration the file is to be
    406  1.1.1.3  christos     	  refreshed.  Note that larger values imply more frequent refreshes.
    407  1.1.1.3  christos           $PREFETCH
    408  1.1.1.3  christos     -F    Force update even if current file is OK and not close to expiring.
    409  1.1.1.3  christos     -f    Absolute path ntp.conf file (default /etc/ntp.conf)
    410  1.1.1.3  christos           $NTPCONF
    411  1.1.1.3  christos     -h    show help
    412  1.1.1.3  christos     -i    Specify number of minutes between retries
    413  1.1.1.3  christos           $INTERVAL
    414  1.1.1.3  christos     -L    Absolute path to leapfile on the local system
    415  1.1.1.3  christos 	  (overrides value in ntp.conf)
    416  1.1.1.3  christos     -l    Specify the syslog(3) facility for logging
    417  1.1.1.3  christos           $LOGFAC
    418  1.1.1.3  christos     -q    Only report errors (cannot be used with -v)
    419  1.1.1.3  christos     -r    Specify number of attempts to retrieve file
    420  1.1.1.3  christos           $MAXTRIES
    421  1.1.1.3  christos     -s    Send output to syslog(3) - implied if STDOUT has no tty or redirected
    422  1.1.1.3  christos     -t    Send output to terminal - implied if STDOUT attached to terminal
    423  1.1.1.3  christos     -u    Specify the URL of the master copy to download
    424  1.1.1.3  christos           $LEAPSRC
    425  1.1.1.3  christos     -v    Verbose - show debug messages (cannot be used with -q)
    426      1.1  christos 
    427  1.1.1.3  christos The following options are not (yet) implemented in the perl version:
    428  1.1.1.3  christos     -4    Use only IPv4
    429  1.1.1.3  christos     -6    Use only IPv6
    430  1.1.1.3  christos     -c    Command to restart NTP after installing a new file
    431  1.1.1.3  christos           <none> - ntpd checks file daily
    432  1.1.1.3  christos     -p 4|6
    433  1.1.1.3  christos           Prefer IPv4 or IPv6 (as specified) addresses, but use either
    434      1.1  christos 
    435  1.1.1.3  christos $PROG will validate the file currently on the local system.
    436      1.1  christos 
    437  1.1.1.3  christos Ordinarily, the leapfile is found using the 'leapfile' directive in
    438  1.1.1.3  christos $NTPCONF.  However, an alternate location can be specified on the
    439  1.1.1.3  christos command line with the -L flag.
    440  1.1.1.3  christos 
    441  1.1.1.3  christos If the leapfile does not exist, is not valid, has expired, or is
    442  1.1.1.3  christos expiring soon, a new copy will be downloaded.  If the new copy is
    443  1.1.1.3  christos valid, it is installed.
    444      1.1  christos 
    445  1.1.1.3  christos If the current file is acceptable, no download or restart occurs.
    446      1.1  christos 
    447  1.1.1.3  christos This can be run as a cron job.  As the file is rarely updated, and
    448  1.1.1.3  christos leap seconds are announced at least one month in advance (usually
    449  1.1.1.3  christos longer), it need not be run more frequently than about once every
    450  1.1.1.3  christos three weeks.
    451  1.1.1.3  christos 
    452  1.1.1.3  christos SSL/TLS Considerations
    453  1.1.1.3  christos -----------------------
    454  1.1.1.3  christos The perl modules can usually locate the CA certificate used to verify
    455  1.1.1.3  christos the peer's identity.
    456  1.1.1.3  christos 
    457  1.1.1.3  christos On BSDs, the default is typically the file /etc/ssl/certs.pem.  On
    458  1.1.1.3  christos Linux, the location is typically a path to a CAdir - a directory of
    459  1.1.1.3  christos symlinks named according to a hash of the certificates' subject names.
    460  1.1.1.3  christos 
    461  1.1.1.3  christos The -C or -D options are available to pass in a location if no CA cert
    462  1.1.1.3  christos is found in the default location.
    463  1.1.1.3  christos 
    464  1.1.1.3  christos External Dependencies
    465  1.1.1.3  christos ---------------------
    466  1.1.1.3  christos The following perl modules are required:
    467  1.1.1.3  christos HTTP::Tiny 	- version >= 0.056
    468  1.1.1.3  christos IO::Socket::SSL - version >= 1.56
    469  1.1.1.3  christos NET::SSLeay 	- version >= 1.49
    470      1.1  christos 
    471  1.1.1.3  christos Version: $VERSION
    472      1.1  christos 
    473  1.1.1.3  christos EOF
    474      1.1  christos }
    475      1.1  christos 
    476