#!/usr/bin/perl # # clockspeed-action: clockspeed helper tool # # by David Harris on Thu Jul 18 2002 # updated on Fri Oct 24 2003 # updated on Mon Mar 8 2004 # # Version 2.02 # # Copyright (c) 2002,2003,2004 DRH Internet Inc. # All Rights Reserved. # use strict; use Symbol (); use Sys::Syslog (); use constant SERVER_CONF_FILE => "/etc/clockspeed.ntpserver.conf"; use constant CLOCKSPEED_PATH => "/usr/local/clockspeed/bin"; use constant CLOCKSPEED_CHECK_RUNNING => "svstat /service/clockspeed | grep '/service/clockspeed: up' >/dev/null"; use constant CLOCKSPEED_ADJUST => "/usr/local/clockspeed/adjust"; use constant FATAL => "clockspeed-action: fatal: "; use constant WARN => "clockspeed-action: error: "; use constant SYSLOG_PROGRAM => "clockspeed-action"; $ENV{'PATH'} = "/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin"; $|++; &main; sub help { my $message = shift; print STDERR "$0: error: $message\n\n" if ( defined $message ); print STDERR <; $line = "" if ( not defined $line ); close FILE; chomp $line; return $line; } sub clockspeed_running { return ( system(CLOCKSPEED_CHECK_RUNNING) == 0 ); } sub hostname_to_ip { my $host = shift; if ( $host =~ /^\d+\.\d+\.\d+\.\d+$/ ) { return $host; } elsif ( $host =~ /^[a-zA-Z0-9_.-]+$/ ) { my @data = gethostbyname($host); die FATAL."unable to lookup hostname ($host)\n" if ( ! @data ); return join(".", unpack('C4', $data[4] )); } else { die FATAL."bad hostname ($host)\n"; } } sub get_correction { my $server = shift; my $server_ip = hostname_to_ip($server); my $buf; my $fh = Symbol::gensym(); open($fh, "/usr/local/bin/setuidgid nobody @{[CLOCKSPEED_PATH]}/sntpclock $server_ip |") or die FATAL."unable to open pipe from sntpclock: $!"; binmode($fh); my $total_read = 0; while ( $total_read < 16 ) { my $read = sysread($fh, $buf, 16 - $total_read, $total_read); die FATAL."unable to read buf" if ( $read == 0 ); $total_read += $read; } close($fh); return $buf; } sub compute_tai_diff { my $buf = shift; die FATAL."buf is wrong size" if ( length $buf != 16 ); my @data = unpack("NNNN", $buf); my $atto = 0; my $nano = 0; my $when = 0; $atto += $data[3]; if ($atto > 999999999) { $atto -= 1000000000; ++$nano; } $nano += $data[2]; if ($nano > 999999999) { $nano -= 1000000000; ++$when; } my $u = $data[1]; if ($u < 2147483648) { $when += $u; } else { $when -= (4294967295 + 1 - $u); } return $when + $nano/1000000000 + $atto/(1000000000*1000000000); } sub write_correction_to_file { my $correction = shift; my $file = shift; my $fh = Symbol::gensym(); open($fh, $file) or die FATAL."unable to open file/pipe to ($file): $!"; binmode($fh); syswrite($fh, $correction, length($correction)) == length($correction) or die FATAL."unable to write to file/pipe: $!"; close($fh); } sub send_to_syslog { my $message = shift; Sys::Syslog::setlogsock('unix'); Sys::Syslog::openlog(SYSLOG_PROGRAM, 'cons', 'user'); Sys::Syslog::syslog('info', $message); Sys::Syslog::closelog(); } sub main { ## Get the command line actions help() if ( not @ARGV ); my $action = shift(@ARGV); my $server = shift(@ARGV) if ( @ARGV ); ## Read the configuration $server = read_first_line(SERVER_CONF_FILE) if ( not defined $server ); die FATAL."no ntp server specified\n" if ( not defined $server or $server eq "" ); ## Do the action if ( $action eq "setclock" ) { die FATAL."the setclock action requires clockspeed be not running\n" if ( clockspeed_running() ); my $correction = get_correction($server); my $diff = compute_tai_diff($correction); write_correction_to_file($correction, "| @{[CLOCKSPEED_PATH]}/clockadd"); print "set clock with diff = $diff\n"; } elsif ( $action eq "showdiff" ) { my $correction = get_correction($server); print "diff: " . compute_tai_diff($correction) . "\n"; write_correction_to_file($correction, qq[| @{[CLOCKSPEED_PATH]}/clockview ] . q[| perl -pe ' s|^before:|local: |; s|^after:|server:|; ' ]); } elsif ( $action eq "logdiff" ) { my $correction = get_correction($server); my $diff = compute_tai_diff($correction); send_to_syslog("clock difference is $diff with ntp server $server"); } elsif ( $action eq "sethwclock" ) { system("hwclock", "--systohc") == 0 or die FATAL."bad return value from hwclock"; send_to_syslog("set hardware clock to system clock"); } elsif ( $action eq "getmeasurement" ) { die FATAL."the setclock action requires clockspeed be running\n" if ( ! clockspeed_running() ); my $correction = get_correction($server); my $diff = compute_tai_diff($correction); write_correction_to_file($correction, "> @{[CLOCKSPEED_ADJUST]}"); my $msg = "fed clock correction of $diff to clockspeed"; send_to_syslog($msg); print "$msg\n"; } else { help("bad action"); } }