393 lines
11 KiB
Perl
Executable file
393 lines
11 KiB
Perl
Executable file
#!/usr/bin/env perl
|
|
# $Id: clocksync.pl,v 1.3 2006/06/06 23:11:31 user Exp $
|
|
##
|
|
$VER = "1.0.2.4";
|
|
# DEFAULTS
|
|
$defintf = "ppp";
|
|
@ntptargets =("us.pool.ntp.org","ch.pool.ntp.org","pool.ntp.org");
|
|
#DEBUG: DBG ONLY, set this to ppp for dialup
|
|
myinit() ;
|
|
#
|
|
mylog("Starting v$VER");
|
|
unless (($intf,@rest) = checkppp(240)) {
|
|
mylog("No IP address yet on ${defintf}[01] for four minutes. Waiting at most another 6 minutes");
|
|
unless (($intf) = checkppp(360)) {
|
|
mydie("ABORTING: CANNOT confirm valid ${defintf}[01] IP address for another six minutes (ten total)");
|
|
}
|
|
}
|
|
sleep 1;
|
|
sleep 3; # add a few more seconds here since we are no longer pinging $testip
|
|
my $count=0;
|
|
my $result=0;
|
|
my ($servername,$server,%errlog) = ();
|
|
while (! $result) {
|
|
foreach $ntptarget (@ntptargets) {
|
|
my @name_lookup = gethostbyname($ntptarget);
|
|
my @ips = map { inet_ntoa($_) } @name_lookup[ 4 .. $#name_lookup ];
|
|
if (@ips) {
|
|
mylog("$ntptarget resolved to: @ips");
|
|
} else {
|
|
mylog("Cannot resolve $ntptarget");
|
|
$result = 1;
|
|
}
|
|
foreach $ip (@ips) {
|
|
`ntpdate -v $ip 2>&1`;
|
|
if ($result = $?) {
|
|
$errlog{$result} .= " $ip";
|
|
}
|
|
unless ($result) {
|
|
$servername = $ntptarget;
|
|
$server = $ip;
|
|
last;
|
|
}
|
|
}
|
|
last if ($server);
|
|
last unless $result;
|
|
}
|
|
last if ($server);
|
|
last unless $result;
|
|
$count+=3;
|
|
sleep 3;
|
|
last if $count > 30;
|
|
}
|
|
|
|
if ($result) {
|
|
foreach $err (sort keys %errlog) {
|
|
mylog("ntpdate $ip returned $err for EACH OF these IPs:$errlog{$result}");
|
|
}
|
|
mydie("FAILED--exiting");
|
|
}
|
|
|
|
if ($server) {
|
|
mylog("Successful ntp connection to $servername/$server");
|
|
} else {
|
|
mylog("FAILED? Did this work? WTF? result=$result but server=$server and servername=$servername");
|
|
}
|
|
|
|
$count=0;
|
|
$result=0;
|
|
while (! `hwclock --systohc`) {
|
|
$result = $?;
|
|
last unless $result;
|
|
$count+=3;
|
|
sleep 3;
|
|
last if $count > 30;
|
|
}
|
|
|
|
if ($result) {
|
|
mydie("FAILED: ntpdate $ntptarget worked but then hwclock --systohc returned $result");
|
|
}
|
|
|
|
# allow time for clock to settle so timestamp in log below is new one
|
|
sleep 3;
|
|
unlink("/tmp/clocksync.done");
|
|
copy("/tmp/clocksync.log","/tmp/clocksync.done");
|
|
|
|
sub checkppp {
|
|
# Return () if no ppp address, otherwise
|
|
# Return ($ispname,$ispip,$testip,$ispcitystate,$ispphone)
|
|
local ($wait) = (@_);
|
|
my $isdown = "";
|
|
if ($wait eq "down") {
|
|
$isdown = $wait;
|
|
$wait = 0;
|
|
# give interface a couple seconds to drop entirely
|
|
sleep 2;
|
|
}
|
|
my (@intf,$ispname,$ispip,$testip,$ispcitystate,$ispphone) = ();
|
|
my $intf = $defintf;
|
|
$wait = 3 * int($wait / 3); # force a multiple of 3
|
|
my $origwait = $wait;
|
|
while ( 1 ) {
|
|
$blah=`ps -ef | egrep "setupisp|wvdial.conf" | grep -v grep`;
|
|
if ($wait and $blah) {
|
|
#TODO: Is this right? Is wvdial gone once we have ppp?
|
|
sleep 3 ;
|
|
$wait -= 3;
|
|
next if $wait;
|
|
mylog("ERROR: Unable do detect just dialed ISP for $origwait seconds");
|
|
return ();
|
|
}
|
|
@intf=($defintf."0",$defintf."1");
|
|
($testip) = `route -n` =~ /\n\s*0.0.0.0\s*(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})/;
|
|
$testip = "" if ($testip eq "0.0.0.0");
|
|
unless ($testip) {
|
|
my @lines = grep { "^nameserver" } `cat /etc/resolv.conf`;
|
|
($testip) = $lines[0] =~ / (\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})/;
|
|
}
|
|
foreach $intf (@intf) {
|
|
my $ifconfig = `ifconfig $intf 2>&1`;
|
|
my ($ispip) = $ifconfig =~ /inet addr:\s*(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})/;
|
|
# If nothing else we ping our own IP as a test
|
|
$testip = $ispip unless $testip;
|
|
$testip = $ispip;
|
|
if ($ispip) {
|
|
# my ($loss) = `ping -nc1 $testip 2>&1` =~ /\s(\d+)\% .*loss/;
|
|
$loss = 0 ;
|
|
if (length($loss) and $loss == 0) {
|
|
my $ispinfo = "/tmp/isp_info";
|
|
if (-M $ispinfo > 0) {
|
|
# We log an error if using PPP but /tmp/isp seems old.
|
|
mylog("$ispinfo timestamp seems too old for a new dial? Using it anyway...")
|
|
unless $opt_e;
|
|
}
|
|
($ispcitystate,$ispphone,$ispname) = ("unknown,UN","911");
|
|
if (open(TMPIN,"$ispinfo")) {
|
|
chomp($ispname = <TMPIN>);
|
|
$ispname =~ s/,/\./g;
|
|
chomp($ispcitystate = <TMPIN>);
|
|
unless ($ispcitystate =~ /,\s*[A-Z]{2}\s*$/) {
|
|
chomp(my $ispstate = <TMPIN>);
|
|
$ispcitystate .= " $ispstate";
|
|
}
|
|
$ispcitystate =~ s/,//g;
|
|
chomp($ispphone = <TMPIN>);
|
|
$ispphone =~ s/,//g;
|
|
close(TMPIN);
|
|
}
|
|
# Ensure none of these entries have commas (we comma delimit elsewhere)
|
|
$ispip =~ s/,/\./g;
|
|
$testip =~ s/,/\./g;
|
|
mylog("Verified $intf IP=$ispip \n".
|
|
#"and pinged testip=$testip:\n".
|
|
"(\$intf,\$ispname,\t\$ispip,".
|
|
# "\t\$testip,".
|
|
"\t\$ispcitystate,\t\$ispphone)=\n".
|
|
"($intf,$ispname,\t$ispip,".
|
|
#"\t$testip,".
|
|
"\t$ispcitystate,\t$ispphone)");
|
|
return ($intf,$ispname,$ispip,$testip,$ispcitystate,$ispphone) ;
|
|
}
|
|
}
|
|
}#foreach (@intf)
|
|
$wait -= 3;
|
|
last unless ($wait > 0);
|
|
sleep 3;
|
|
}
|
|
if ($isdown) {
|
|
mylog("confirmed no live ${defintf}[01] interface after isp($action)");
|
|
} else {
|
|
mylog("testip=$testip, but could not verify my IP address on (@intf)");
|
|
}
|
|
return ();
|
|
}#checkppp
|
|
|
|
sub myinit {
|
|
use File::Basename qw(basename dirname);
|
|
require "getopts.pl";
|
|
require Time::Local;
|
|
use Socket;
|
|
use File::Copy ;
|
|
$COLOR_SUCCESS="\033[2;32m";
|
|
$COLOR_FAILURE="\033[2;31m";
|
|
$COLOR_WARNING="\033[1;33m";
|
|
$COLOR_NORMAL="\033[0;39m";
|
|
$COLOR_NOTE="\033[0;34m";
|
|
$prog = basename $0 ;
|
|
$vertext = "$prog version $VER\n" ;
|
|
$| = 1;
|
|
$opdir = "/current" ;
|
|
$opup = "$opdir/up" ;
|
|
$opbin = "$opdir/bin" ;
|
|
$opetc = "$opdir/etc" ;
|
|
$opdown = "$opdir/down" ;
|
|
$optmp = "$opdir/tmp" ;
|
|
chomp($hostname = `hostname`);
|
|
$ext = "$$" ; # some uniqueness to me
|
|
$gsoptions = "@ARGV" ;
|
|
$calledas = "$0 @ARGV";
|
|
$origargs = "@ARGV" ;
|
|
$optstr = "hvei:f" ;
|
|
mydie("bad option(s)") if (! Getopts( $optstr ) ) ;
|
|
$defhowoften = 3;
|
|
$howoften = $defhowoften;
|
|
$howoften = 0 if $opt_f;
|
|
my @tmpntps = @ntptargets;
|
|
shift(@tmpntps);
|
|
$usagetext = "Usage: $prog [options]
|
|
|
|
OPTIONS
|
|
|
|
-e Use eth[01] as the interfaces on which to look for a valid
|
|
IP address. (NOTE: DNS must work for $prog to work,
|
|
unless -i is used.)
|
|
-i IP IP address to try first as an NTP server instead of
|
|
$ntptarget (still tries named targets if that IP fails).
|
|
-f Force $prog to run regardless of last time it ran on
|
|
this host.
|
|
|
|
If $prog was run on this host less than $defhowoften days ago, it
|
|
merely exits, doing nothing. (Use -f to force $prog to execute
|
|
regardless of last time it ran.)
|
|
|
|
Otherwise, $prog will wait up to ten minutes for a valid $defintf[01] IP
|
|
address. Once it sees one, it attempts to set the system clock
|
|
via the command:
|
|
|
|
\tntpdate $ntptargets[0]
|
|
|
|
If that fails, these ntp servers are also tried until one works:
|
|
\t@tmpntps
|
|
|
|
If ntpdate works, $prog then sets the hwclock to match with:
|
|
|
|
\thwclock --systohc
|
|
|
|
";
|
|
usage() if ($opt_h or $opt_v);
|
|
$defintf = "eth" if ($opt_e);
|
|
if ($opt_i) {
|
|
mydie("Malformed IP -i $opt_i")
|
|
unless ipcheck($opt_i);
|
|
unshift @ntptargets,$opt_i;
|
|
}
|
|
close(STDOUT);
|
|
close(STDERR);
|
|
fork() and exit; # background self
|
|
if (-e "/tmp/clocksync.done") {
|
|
$age = -M _;
|
|
if ($age < $howoften) { # if under 3 days old skip clocksync entirely
|
|
$age = secstostr($age * 24 * 60 * 60);
|
|
mydie("Skipping clocksync: done $age ago on this host ( < $howoften days)");
|
|
}
|
|
}
|
|
}#myinit
|
|
|
|
|
|
|
|
sub mydie {
|
|
local ($what,$color,$color2,$what2) = (@_) ;
|
|
$color = $COLOR_FAILURE unless $color ;
|
|
$color2 = $color unless $color2 ;
|
|
$what2 = " $what2" if ($what2) ;
|
|
if (@_) {
|
|
mylog($what,$color,$color2,$what2);
|
|
}
|
|
exit 1;
|
|
}#mydie
|
|
|
|
sub dbg {
|
|
mylog("$prog[$$]: ${COLOR_FAILURE}DBGwarn\a: @_$COLOR_NORMAL\n") ;
|
|
}#dbg
|
|
|
|
sub mylog {
|
|
my ($pause,$pausecmd)=("");
|
|
my $where = undef;
|
|
my $timestamp="";
|
|
$timestamp= timestamp()." " unless ($notimestamp or $ENV{NOTIMESTAMP});
|
|
while (
|
|
$_[$#_] =~ /^STD/ or
|
|
$_[$#_] =~ /OUT$/
|
|
) {
|
|
$where = pop(@_);
|
|
}
|
|
#DBG: $where = stderr
|
|
$where = STDERR;
|
|
local ($what,$color,$color2,$what2) = (@_) ;
|
|
$color = $COLOR_NOTE unless $color ;
|
|
$color2 = $color unless $color2 ;
|
|
$what2 = " $what2" if ($what2) ;
|
|
open(OUT,">> /tmp/clocksync.log")
|
|
or mydie("FAILED: Cannot open /tmp/clocksync.log: $!");
|
|
if (lc $what eq "starting" or lc $what eq "init") {
|
|
sleep 1; # allows a tail -f to see the first line of file after it echos
|
|
# the error: tail: /tmp/clocksync.log: file truncated
|
|
}
|
|
$where = OUT;
|
|
print $where "$timestamp${prog}[$hostname:$$]$what2: $what\n" ;
|
|
close(OUT);
|
|
}#mylog
|
|
|
|
|
|
|
|
sub usage {
|
|
if ($nextextfile and $ext and -e $nextextfile) {
|
|
my $newfile = $nextextfile;
|
|
$newfile =~ s/\.$ext$//;
|
|
rename($nextextfile,$newfile);
|
|
}
|
|
my $output = "";
|
|
$output .= "\nFATAL ERROR: @_\n" if ( @_ );
|
|
$usagetext = $gsusagetext if ($gsusagetext and $nopen_mypid) ;
|
|
$usagetext = $gsusagetext if ($gsusagetext and !$usagetext) ;
|
|
$usagetext .= $gsusagetextlong if ($longhelp and $gsusagetextlong);
|
|
$output .= $usagetext unless $opt_v;
|
|
$output .= $vertext ;
|
|
$output .= "\nFATAL ERROR: @_\n" if ( @_ );
|
|
print STDOUT $output;
|
|
exit;
|
|
}#usage
|
|
sub dammit {
|
|
my $duh = "@_";
|
|
if (open(TMPOUT,">> /tmp/dammit")) {
|
|
print TMPOUT "@_\n";
|
|
close(TMPOUT);
|
|
} else {
|
|
`echo -e "$duh" >> /tmp/dammit`;
|
|
}
|
|
}
|
|
|
|
sub timestamp {
|
|
my ($sec,$min,$hr,$mday,$mon,$year,$wday,$yday,$isdst,$monstr) =
|
|
gmtime();
|
|
$year+=1900;
|
|
$mon++;
|
|
return sprintf("%4d-%02d-%02d %02d:%02d:%02d ",
|
|
$year,$mon,$mday,$hr,$min,$sec);
|
|
}#timestamp
|
|
|
|
sub ipcheck {
|
|
# returns 1 iff $ipstr is in dotted decimal notation with each
|
|
# octet between 0 and 255 inclusive (i.e. 0.0.0.0 and 255.255.255.255 are valid)
|
|
my $maxval=255;
|
|
my $minval=0;
|
|
while ($_[$#_] =~ /no/) {
|
|
if ($_[$#_] =~ /no255/) {
|
|
pop(@_);
|
|
$maxval=254;
|
|
}
|
|
if ($_[$#_] =~ /no0/) {
|
|
pop(@_);
|
|
$minval=1;
|
|
}
|
|
}
|
|
local($ipstr,$minoctets,$maxoctets) = @_;
|
|
$minoctets=abs(int($minoctets)) if defined $minoctets;
|
|
$maxoctets=abs(int($maxoctets)) if defined $maxoctets;
|
|
unless($minoctets) {
|
|
$minoctets=4 ;
|
|
}
|
|
unless (defined $maxoctets and $maxoctets <= 4 and $maxoctets > 0) {
|
|
$maxoctets=4;
|
|
}
|
|
# strip trailing "." if partial IPs allowed
|
|
$ipstr =~ s/\.$// if ($maxoctets < 4) ;
|
|
# need -1 in following split to keep null trailing fields (to reject "1.2.3.4.")
|
|
my @octets=split(/\./,$ipstr,-1);
|
|
return 0 if (@octets < $minoctets or @octets > $maxoctets);
|
|
foreach (@octets) {
|
|
# return 0 if (empty or nondigits or <0 or >$maxval)
|
|
return 0 if (( /\D/ ) || $_ < $minval || $_ > $maxval);
|
|
# next line allows partial IPs ending in ".", ignore last
|
|
return 0 if ($minoctets == 4 and $_ eq "");
|
|
}
|
|
return 1;
|
|
} #ipcheck
|
|
|
|
sub secstostr {
|
|
# given seconds returns string in form "[#d][#h][#m]#s"
|
|
my ($secs) = (@_);
|
|
my $sign = $secs < 0 ? -1 : 1 ;
|
|
$secs=abs(int($secs));
|
|
my $d = int($secs/(24*60*60));
|
|
$secs -= $d * (24*60*60);
|
|
my $h = int($secs/(60*60));
|
|
$secs -= $h * (60*60);
|
|
my $m = int($secs/(60));
|
|
$secs -= $m * (60);
|
|
my $s = $secs;
|
|
$d = $d ? "${d}d " : "";
|
|
$h = $h ? "${h}h " : "";
|
|
$m = $m ? "${m}m " : "";
|
|
return "$d$h$m${s}s";
|
|
}#secstostr
|