[comp.sys.3b1] nbs_time

gak@gakbox.Corp.Sun.COM (Richard Stueven) (05/17/91)

Two weeks ago, I sent the attached message to the author of the
"nbs_time" program.  I never received a response, so I'm going to
assume he's no loger with us, and post the message here with the hope
that nobody else encounters the problem that I had.

(NB:  Like I say below, I don't know if the problem was caused by
nbs_time, a billing system error, or a telephone network error, but the
changes I made should ensure that nbs_time won't be at fault in the
future.)

have fun
gak
========================================================================
From gak Thu May  2 10:27:59 1991
To: druhi!rafb@att.com
Subject: nbs_time

Bob,

I found your nbs_time program very useful, until I got a bill for a
$200+ phone call to the NBS number this month.  Now, I don't know
whether the 1159 minute connection was due to a billing problem, a
network problem, or a bug in the code, but it prompted me to examine
the code a little more closely.

I found a couple of places that have the potential to either hang the
connection open or to go into an infinite loop under the right
circumstances.  I added some code to set a 60 second alarm and to drop
the connection if the alarm expires.  The updated code is attached.  If
you think it's OK (I've already tested it to my own satisfaction) I
invite you to re-post it to Usenet.

If you have any questions about the changes, you can reach me at
gak@Corp.Sun.COM or attmail!gak, or on 415-336-5703.

thx
Richard Stueven

----- Begin Included Message -----

/*
 *  NAME:  nbs_time - set system clock to National Bureau of Standards Time.
 *
 *  WHAT:
 *      This program sets the system clock to the time maintained by the
 *      National Bureau of Standards (WWV Boulder CO).
 *      It assumes that the system clock is currently set to the correct
 *      day and year.
 */

#include <stdio.h>
#include <time.h>
#include <sys/syslocal.h>
#include <sys/rtc.h>
#include <sys/signal.h>

/* function definitions to keep lint quiet */

long time(), convert_tick(), atol(); 
char *getenv();
void check_tz(), log_err(), set_time(), set_rtc(), disc_nbs(), exit();

int pid;                /* process id of child */
extern char *tzname[2]; /* time zone names */

#define LOG     "/usr/adm/clocklog"
#define TZF     "/etc/TZ"
#define SEC     60

FILE *Nbs_rd;   /* file descriptor for reading from NBS */
FILE *Nbs_wt;   /* file descriptor for writing to NBS   */

char Mon_size[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };

#define LEAP_YR(yr) ((((yr) % 4) == 0) && (((yr) % 400) != 0))

main()
{
    int timed_out(); /* Alarm handler */

    (void)signal(SIGALRM, timed_out);

    /* Start log for this run */

    (void)freopen(LOG, "a", stdout);
    setbuf(stdout, NULL);               /* unbuffered output to log    */

    check_tz();
    if(call_nbs() < 0)  /* Call the National Bureau of Standards */
    {
        exit(1);
    }
    set_time();         /* Set the system clock on this machine. */
    disc_nbs();         /* Disconnect the call to NBS            */
    return(0);
}

int call_nbs()
{
    int p1[2];          /* pipe from cu process */
    int p2[2];          /* pipe to cu process   */

    if(pipe(p1) < 0 || pipe(p2) < 0)    /* create the pipes */
    {
        log_err(0L, "Cannot create pipes.\n", 0, 0);
        return(-1);     /* pipe create failed */
    }

    (void)setpgrp();    /* Set process group ID, just in case */
                        /* something happens and we have to   */
                        /* terminate after the fork()         */
    if((pid = fork()) < 0)
    {
        log_err(0L, "Cannot fork for child process.\n", 0, 0);
        return(-1);     /* fork failed */
    }
    if(pid != 0)        /* parent process */
    {
        (void)close(p1[1]);             /* close the unused end of pipe 1  */
        (void)close(p2[0]);             /* close the unused end of pipe 2  */
        Nbs_rd = fdopen(p1[0], "r");    /* save the pipe end for NBS read  */
        Nbs_wt = fdopen(p2[1], "w");    /* save the pipe end for NBS write */
        setbuf(Nbs_rd, NULL);           /* set unbuffered mode */
        setbuf(Nbs_wt, NULL);           /* set unbuffered mode */
        return(0);
    }

    /* this is the child process - set up stdin, stdout, stderr and exec cu */

    (void)close(p1[0]);         /* close the unused end of pipe 1  */
    (void)close(p2[1]);         /* close the unused end of pipe 2  */
    (void)close(0); (void)dup(p2[0]);   /* set up pipe 2 as stdin  */
    (void)close(1); (void)dup(p1[1]);   /* set up pipe 1 as stdout */
    (void)close(2); (void)dup(p1[1]);   /* set up pipe 1 as stderr */
    (void)execl("/usr/bin/cu", "cu", "-s1200", NBS_PH, 0);
    return(-1);
}

void disc_nbs()
{
    (void)fprintf(Nbs_wt, "~.\n");      /* write the cu disconnect command */
    return;
}

/*
 *  The NSB output includes some header information (several lines) followed
 *  followed by a clock tick every second.  The clock tick contains all of
 *  the information needed to set the unix clock.  The additional fields are
 *  ignored.
 *
 *      00000000001111111111222222222233333333334444444444
 *      01234567890123456789012345678901234567890123456789
 *      jdate yr-mo-dd hh:mm:ss ds l cor msadv tz        v
 *
 *      47588 89-03-03 20:33:45 81 0 -.2 045.0 UTC(NIST) *
 */

void set_time()
{
    long new_time;      /* the converted time       */
    long cur_time;      /* current system time      */
    short got_tick;     /* how many ticks seen?     */
    char buf[80];       /* buffer for NSB tick data */
    long delta;         /* time delta               */
    char *dir;          /* is clock slow or fast    */
    int rc;             /* return code from stime() */
    unsigned alarm();

    (void)alarm((unsigned)SEC);
    got_tick = 0;       /* don't have one yet */
    while(fgets(buf, 80, Nbs_rd) != NULL)
    {
        if(strncmp(&buf[39], "UTC", 3) == 0)
        {
            if(++got_tick >= 3)
            {
                break;  /* take the third valid tick to insure sync */
            }
        }
    }

    (void)alarm((unsigned)0);

    if(got_tick == 0)
    {
        /* print last cu error line */

        log_err(0L, "%s", buf, 0);
        return;
    }

    /* got a tick, so convert it to seconds since 1/1/70 00:00:00 GMT */

    new_time = convert_tick(buf);

    /* now re-sync with the source */

    (void)alarm((unsigned)SEC);
    got_tick = 0;       /* don't have a tick yet */
    while(fgets(buf, 80, Nbs_rd) != NULL)
    {
        if(strncmp(&buf[39], "UTC", 3) == 0)
        {
            new_time++;         /* add a second for each tick we read */
            if(++got_tick >= 3)
            {
                break;  /* take the third valid tick to insure sync */
            }
        }
    }

    (void)alarm((unsigned)0);

    (void)time(&cur_time);      /* get current time */
    rc = stime(&new_time);      /* set the time */

    delta = new_time - cur_time;        /* get delta */
    if(delta != 0L)
    {
        dir = "slow";
        if(delta < 0L)
        {
            dir = "fast";
            delta = -delta;
        }
        log_err(cur_time, "Clock is %ld seconds %s.", delta, dir);
        if(rc < 0)
        {
            log_err(cur_time, "Clock not set; permission denied.\n", 0, 0);
        }
        else
        {
            log_err(new_time, "New time set.\n", 0, 0);
        }
    }
    else
    {
        log_err(0L, "Clock is correct.\n", 0, 0);
    }
    set_rtc();  /* sync the hardware clock */
    return;
}

long convert_tick(buf)
char *buf;
{
    short i;            /* generic loop variable   */
    short year;         /* current year from tick  */
    short mon;          /* current month from tick */
    long new_time;      /* the converted time      */

    /* convert tick to seconds since 1/1/70 00:00:00 GMT */

    new_time = 0L;

    /* add on years, new_time contains days */

    year  = 1900 + atoi(buf + 6);
    for(i = 1970; i < year; i++)
    {
        new_time = new_time + 365L;
        if(LEAP_YR(i))
        {
            new_time = new_time + 1L;   /* add the leap day */
        }
    }

    /* add on months this year, new_time in days */

    mon = atoi(buf + 9) - 1;
    for(i = 0; i < mon; i++)
    {
        new_time = new_time + (long)Mon_size[i];
    }
    if(mon > 1 && LEAP_YR(year))
    {
        new_time = new_time + 1L;       /* add the leap day */
    }

    /* add on days this month, convert new_time to hours */

    new_time = (new_time + (atol(buf + 12) - 1)) * 24L;

    /* add hours this day, convert new_time to minutes */

    new_time = (new_time + atol(buf + 15)) * 60L;

    /* add minutes this hour, convert new_time to seconds */

    new_time = (new_time + atol(buf + 18)) * 60L;

    /* add seconds this minute */

    new_time = new_time + atol(buf + 21);
    return(new_time);
}

void set_rtc()
{
    long cur_time;      /* current time on software clock        */
    struct rtc rtc;     /* structure for setting real time clock */
    struct tm *tm;      /* pointer to localtime() structure      */

    /* now set the hardware clock */

    (void)time(&cur_time);
    tm = localtime(&cur_time);  /* convert time to local time */

    rtc.wkday = tm->tm_wday;
    rtc.yr10  = tm->tm_year / 10;
    rtc.yr1   = tm->tm_year % 10;
    rtc.mon10 = (tm->tm_mon + 1) / 10;
    rtc.mon1  = (tm->tm_mon + 1) % 10;
    rtc.day10 = tm->tm_mday / 10;
    rtc.day1  = tm->tm_mday % 10;
    rtc.hr10  = tm->tm_hour / 10;
    rtc.hr1   = tm->tm_hour % 10;
    rtc.min10 = tm->tm_min / 10;
    rtc.min1  = tm->tm_min % 10;
    rtc.sec10 = tm->tm_sec / 10;
    rtc.sec1  = tm->tm_sec % 10;

    (void)syslocal(SYSL_WRTRTC, &rtc);  /* write to RTC */
    return;
}

/* VARARGS2 */

void log_err(tim, fmt, arg1, arg2)
long tim;               /* time for time stamp (0 means current time) */
char *fmt;              /* printf style format argument               */
int arg1;               /* first printf argument                      */
int arg2;               /* second printf argument                     */
{
    struct tm *tm;      /* pointer to local time structure */

    if(tim == 0L)
    {
        (void)time(&tim);       /* get current time */
    }
    tm = localtime(&tim);       /* convert tim to local time */

    /* generate time stamp */
    (void)printf("%.2d/%.2d/%.2d", tm->tm_mon + 1, tm->tm_mday, tm->tm_year); 
    (void)printf("-%.2d:%.2d:%.2d  ", tm->tm_hour, tm->tm_min, tm->tm_sec);
    (void)printf(fmt, arg1, arg2);
    (void)printf("\n");
    return;
}

void check_tz()
{
    char *p;    /* pointer for TZ string */

    if((p = getenv("TZ")) == NULL)
    {
        if(freopen(TZF, "r", stdin) != NULL)
        {
            p = "TZ=          ";        /* enough space for TZ=XST-nnXDT */
            if(gets(p+3) != NULL)
            {
                if(putenv(p) == 0)
                {
                    log_err(0L, "Set %s.", p, 0);
                    return;
                }
            }
        }
        log_err(0L, "Using Default TZ (%s).", tzname[0], 0);
    }
    return;
}

int timed_out()
{
    log_err(0L, "Timed out.\n", 0, 0);
    (void)kill(0, SIGTERM);
    exit(2);
}

----- End Included Message -----
---------------------------------------------------------------------------
--
I guess there's some things           |       Seems like the more I think I know
I'm not meant to understand           |                  The more I find I don't
Ain't life a riot?  Ain't love grand? |  Every answer opens up so many questions
Richard Stueven                 gak@Corp.Sun.COM                 ...!attmail!gak

dold@mitisft.Convergent.COM (Clarence Dold) (05/19/91)

in article <4111@jethro.Corp.Sun.COM>, gak@gakbox.Corp.Sun.COM (Richard Stueven) says:

... some source code ...

>  *  NAME:  nbs_time - set system clock to National Bureau of Standards Time.
...
>     tm = localtime(&cur_time);  /* convert time to local time */
...
>     (void)syslocal(SYSL_WRTRTC, &rtc);  /* write to RTC */

To make this run on the Convergent 'S/Series' CTIX, change localtime() to
gmtime(), and WRTRC to WRTC.

I also point my SIGALARM at disc_nbs, but that is trivial.
The check_tz routine needs to be different, although if $TZ is set, it
doesn't matter.

I also have a routine to write the RTC on the Convergent SPC / 
Unisys U6000 (low end), which I might post soon as part of my 
net-synchronization routine for non-ntp sites.

-- 
---
Clarence A Dold - dold@tsmiti.Convergent.COM
               ...pyramid!ctnews!tsmiti!dold