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