#!/usr/bin/perl
#
# nbs_time - Set system clock and CMOS from the NBS.
#
# Author: Kayvan Sylvan (kayvan@satyr.Sylvan.COM)
#
# $Id: nbs_time.pl,v 1.3 1994/02/01 08:12:28 kayvan Exp $

require 'open2.pl' || die "fatal error loading open2: $!\n";

#======================================================================
# Configuration.
#======================================================================

$LogFile = "/usr/adm/clocklog";	# the nbs_time log file
$CMOS_is_UTC = 1;		# set to zero if your CMOS is local time.
$NBS_Phone = "1-303-494-4774";	# Number of NBS.

$Clock_Program = "/etc/clock";	# CMOS setting program
if ($CMOS_is_UTC) {
    @Clock_Args = ( "-w", "-u" );
} else {
    @Clock_Args = ("-w");
};

$CU_Program = "/usr/bin/cu";	# program to call out to NBS
@CU_Args = ("-s", "1200", "-c", $NBS_Phone);

$SYS_stime = 25;		# stime() call number (25 for Linux)

#======================================================================
# This is where the action is. The main part of nbs_time.
#======================================================================

open(LOG, ">>$LogFile") || die "Couldn't open $LogFile: $!\n";
open(STDERR, ">/dev/null") || die "Couldn't redirect stderr: $!\n";

&open2(READ_NBS, WRITE_NBS, $CU_Program, @CU_Args); # start talking to NBS

$got_tick = 0; # Don't have one yet.
line: while ($nbs_line = <READ_NBS>)
{
    if (substr($nbs_line, 39, 3) eq "UTC") {
	$got_tick++;
	last line if ($got_tick == 3); # Third valid tick ensures sync.
    }
}
if ($got_tick != 3)		# Something went wrong.
{
    &TimeStamp(0);		# Stamp log.
    print LOG "$nbs_line\n";	# last cu line.
    print WRITE_NBS "~.\n";	# Disconnect from CU
    exit -1;			# Exit.
}

$new_time = &ConvertTick($nbs_line); # Got it. Return canonical time.

$got_tick = 0;			# Now, we re-sync.
tick: while ($nbs_line = <READ_NBS>)
{
    if (substr($nbs_line, 39, 3) eq "UTC") {
	$new_time++;		# Add a second for each tick.
	$got_tick++;
	last tick if ($got_tick == 3); # Third valid tick ensures sync.
    }
}
if ($got_tick != 3)		# Something went wrong.
{
    &TimeStamp(0);		# Stamp log.
    print LOG "$nbs_line\n";	# last cu line.
    print WRITE_NBS "~.\n";	# Disconnect from CU
    exit -1;			# Exit.
}

$cur_time = &SetTime($new_time); # Set time, returning old time.

$delta = $new_time - $cur_time;

&TimeStamp($cur_time);		# timestamp log.
if ($delta == 0) {
    print LOG "Clock is correct.\n";
} else {
    if ($delta < 0) {
	print LOG "Clock is ", -($delta), " seconds fast.\n";
    } else {
	print LOG "Clock is ", $delta, " seconds slow.\n";
    }
    &TimeStamp(0);
    print LOG "New time set.\n";
}

print WRITE_NBS "~.\n";		# Finish talking to NBS
close(LOG);			# Close LOG to flush it.
exit 0;				# Done!

#======================================================================
# Subroutines used by nbs_time.
#======================================================================

# ConvertTick
#    given the NBS string, generate a canonical time value (number of
#    seconds since 00:00 Jan 1, 1970) that corresponds to the time.
#
sub ConvertTick {
    local($line) = shift(@_);
    local($year, $month, $day, $hour, $minute, $second);
    local($return_time) = 0;
    local(@days_in_month);
    local($temp);

    @days_in_month = ( 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 );

    $start = index($line, ' '); $start++;
    $year = substr($line, $start, 2); $start += 3;
    $month = substr($line, $start, 2); $start += 3;
    $day = substr($line, $start, 2); $start += 3;
    $hour = substr($line, $start, 2); $start += 3;
    $minute = substr($line, $start, 2); $start += 3;
    $second = substr($line, $start, 2);

    $year += 1900;
    for ($temp = 1970; $temp < $year; $temp++)
    {
	$return_time += 365;
	if ((($temp % 4) == 0) && ($temp % 400)) { $return_time++; }
    }
    for ($temp = 1; $temp < $month; $temp++)
    {
	$return_time += $days_in_month[$temp];
    }
    if (($month > 2) && ((($year % 4) == 0) && ($year % 400))) {
	$return_time++;
    }
    $return_time += ($day - 1);
    $return_time = (24 * $return_time) + $hour;
    $return_time = (60 * $return_time) + $minute;
    $return_time = (60 * $return_time) + $second;

    return $return_time;
}

# TimeStamp --- Put time stamp into log file.
#
sub TimeStamp {
    local($t) = shift(@_);
    if ($t == 0) { $t = time; }
    local($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($t);
    printf LOG "%.2d/%.2d/%.2d", $mon + 1, $mday, $year;
    printf LOG "-%.2d:%.2d:%.2d  ", $hour, $min, $sec;
}

# SetTime --- Set the system time and CMOS time.
#
sub SetTime {
    local($t) = shift(@_);
    local($tempstr) = pack("l", $t); # Put it in pseudo-string for syscall
    $t = time;			     # Save time() right before stime().
    syscall($SYS_stime, $tempstr);   # Call stime() to set time
    &SetRTC();			     # Set the CMOS clock
    return $t;
}

# SetRTC --- Set the CMOS time by calling /etc/clock.
#
sub SetRTC {
    system $Clock_Program, @Clock_Args;
}
