EQGRP/Linux/bin/clocksync.pl
2017-04-08 16:05:14 +02:00

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