/* nistime.c - query and set time from NIST server */ /* ------------------------------ */ /* Feature test macros */ /* ------------------------------ */ /* #define _POSIX_SOURCE /* Always require POSIX standard */ /* ------------------------------ */ /* Standard include files */ /* ------------------------------ */ #define INCL_DOSDATETIME #include #include #include #include #include #include #include /* Will be OS/2 TCP/IP types.h file */ #include /* More TCP/IP include files */ #include #include /* ------------------------------ */ /* Local include files */ /* ------------------------------ */ /* ------------------------------- */ /* My local typedef's and defines */ /* ------------------------------- */ #define panic SysPanic(__FILE__, __LINE__) #define NIST_TIME_INET_ADDRESS "132.163.135.130" /* time.nist.gov */ #define NIST_TIME_PORT 13 /* specific port */ #define UNIX_EPOCH_MJD 40587 /* UNIX epoch date */ /* ------------------------------- */ /* My external function prototypes */ /* ------------------------------- */ /* ------------------------------- */ /* My internal function prototypes */ /* ------------------------------- */ static int sw(int argc, char *arg, char *let, long *val); static int ShiftTime(time_t diff); static void PrintUsage(void); /* ------------------------------- */ /* My usage of other external fncs */ /* ------------------------------- */ /* ------------------------------- */ /* Locally defined global vars */ /* ------------------------------- */ /* --------------------------------------------------------------------------- - client process to connect to the daytime service via port 13 from nist - time server. - - This OS/2 implementation is a port of the unix utility by the same name - obtained by anonymous FTP from time_a.timefreq.bldrdoc.gov. My apologies - to Judah Levine for the hack of his code. For questions about this OS/2 - implementation, contact: - - Michael Thompson - Cornell University - Dept. of Materials Science - 129 Bard Hall - tommy@msc.cornell.edu - - - The client process uses the time message received to check (and optionally - to set) the time of the local clock. the comparison assumes that the local - clock keeps time in seconds from 1/1/70 which is the UNIX standard, and it - computes the time difference by converting the received MJD to seconds - since 1/1/70 and adding the received hr, min and sec, also converted to - seconds. If the local machine keeps time in some other way, then the - comparison method will have to change, but the rest should be okay. - - This software was developed with US Government support and it may not be - sold, restricted or licensed. You may duplicate this program provided - that this notice remains in all of the copies, and you may give it to - others provided they understand and agree to this condition. - - This program and the time protocol it uses are under development and the - implementation may change without notice. - - For questions or additional information, contact: - - Judah Levine - Time and Frequency Division - NIST/847 - 325 Broadway - Boulder, Colorado 80303 - (303) 492 7785 - jlevine@time_a.timefreq.bldrdoc.gov --------------------------------------------------------------------------- */ int main(int argc, char *argv[]) { /* Internet socket access parameters */ char *cp = NIST_TIME_INET_ADDRESS; /* server address in . notation */ int pserv = NIST_TIME_PORT; /* port for time service */ long address; /* ip address */ struct sockaddr_in *sin; /* socket address structure */ int s; /* socket number */ int length; /* size of message */ char buf[132]; /* holds message */ time_t my_time, nist_time; /* Time since epoch */ time_t diff; /* time difference, local - NIST */ /* Values obtained from the internet message */ long mjd0 = UNIX_EPOCH_MJD; /* MJD of the UNIX EPOCH */ long mjd; /* holds parsed received mjd */ int yr,mo,dy,hr,min,sec; /* holds parsed received time */ int day_light, /* Daylight savings flag */ leap_sec, /* Leap second status */ health; /* Health of server status */ /* Local variables */ char achr; /* Random character */ char let; /* command-line letter */ long val; /* command line value */ /* --------------------------------------------------------------------------- - the following variables define what this program should do and what - output should be produced. The values below are the defaults which - may be changed by characters on the command line as described below. - The effect of each variable is as follows: - - Command-Line Switch effect - -m0 msg = 0 Do not produce any messages; only time difference is - written to standard output. - -m1 or -M = 1 Produce short messages. - -m2 = 2 Produce longer messages. - - -s0 set = 0 Do not adjust local time. - -s1 or -S = 1 Adjust local time if server is healthy. - -s2 = 2 Adjust local time even if server is unhealthy. - -s3 = 3 Ask operator first. --------------------------------------------------------------------------- */ int msg = 1; /* default is short messages */ int set = 3; /* default is ask before setting clock */ /* parse command line switches */ ++argv; --argc; /* Skip command name */ while (sw(argc, argv[0], &let, &val) != 0) { /* Switch present */ switch (let) { case '?': case 'h': case 'H': PrintUsage(); return(EXIT_SUCCESS); case 'm': msg=val; break; case 's': set=val; break; case 'S': set=1; break; case 'M': msg=1; break; default: fprintf(stderr, "\nSwitch %c not recognized.\n", let); break; } argc--; /* decrement argument counter */ argv++; /* and increment pointer */ } /* ----------------------------------------------------------------- -- (1) Make sure we are using the TZ variable -- (2) Convert xxx.xxx internet address to internal format -- (3) allocate space for socket info, and fill int -- (4) Create socket -- (5) Connect to server. Note this is a stream server and -- record boundaries are not preserved. -- (6) Query local time via time() call -- (7) Close socket and free allocated memory ----------------------------------------------------------------- */ _tzset(); /* Get timezone from TZ */ sock_init(); /* Initialize sockets */ if ( (address=inet_addr(cp)) == -1) { /* Internal format */ fprintf(stderr,"Internet address error.\n"); return(EXIT_FAILURE); } if ( (sin = calloc(sizeof(*sin), 1)) == NULL) { /* Allocate space */ fprintf(stderr, "Unable to allocate memory for socket info.\n"); return(EXIT_FAILURE); } sin->sin_family = AF_INET; /* Fill in request */ sin->sin_addr.s_addr = address; sin->sin_port = htons(pserv); /* Now, create socket, open and read */ if ( (s = socket(AF_INET, SOCK_STREAM, 0) ) < 0) { /* Get a socket */ fprintf(stderr,"Socket creation error.\n"); return(EXIT_FAILURE); } else if (connect(s, (struct sockaddr *) sin, sizeof(*sin) ) < 0) { fprintf(stderr, "Failed to connect to time server: %d\n", tcperrno()); return(EXIT_FAILURE); } if ( (length = recv(s, buf, sizeof(buf)-1, 0)) <= 0) { fprintf(stderr, "No response from NIST server.\n"); soclose(s); return(EXIT_FAILURE); } buf[length] = '\0'; /* terminate msg */ /* Immediately, get local time as well for comparison */ if ( (my_time = time(NULL)) == (time_t) -1) { fprintf(stderr, "Unable to determine local clock time\n"); return(EXIT_FAILURE); } /* And close socket, free allocated space */ soclose(s); /* Close port and free memory now */ free(sin); /* -------------------------------------------------------------------------- -- convert received time to seconds since EPOCH and subtract this from the -- local time in the same units. Since POSIX makes no specification about -- times less than 1 second, and the internet message only goes to seconds, -- look only this far and ignore smaller differences. Fortunately, the delay -- in the net should be no more than about 0.05 seconds (according to NIST). --------------------------------------------------------------------------- */ sscanf(buf," %ld %2d-%2d-%2d %2d:%2d:%2d %d %d %d", &mjd, &yr, &mo, &dy, &hr, &min, &sec, &day_light, &leap_sec, &health); nist_time = 86400*(mjd-mjd0) + 3600*hr + 60*min + sec; diff = my_time - nist_time; /* -------------------------------------------------------------------------- -- Output various reports to stdout depending on the setting of msg -- parameter. --------------------------------------------------------------------------- */ /* Output desired messages */ if (msg != 0) { printf(" Time message received:\n"); printf(" D L\n"); printf(" MJD YY MM DD HH MM SS ST S H Adv.%s", buf); } if (msg == 2) { /* longer messages selected */ if( (day_light == 0) || (day_light > 51) ) { printf("Standard Time now in effect\n"); if (day_light > 51) printf("Change to Daylight Saving Time in %d days\n",day_light-51); } if( (day_light <= 50) && (day_light > 1) ) { printf("Daylight Saving Time now in effect\n"); if (day_light > 1 && day_light != 50) printf("Change to Standard Time in %d days\n", day_light-1); } if (day_light == 1) printf("Standard time begins at 2am today\n"); if (day_light == 51) printf("Daylight Saving Time begins at 2am today\n"); if (leap_sec == 1) printf("Leap Second to be added at end of month\n"); if (leap_sec == 2) printf("Second to be dropped at end of month\n"); } if (msg == 0) { /* no messages except time diff */ printf("%ld", diff); } else { printf("Local Clock - NIST = %ld second(s)\n", diff); } /* -------------------------------------------------------------------------- -- Deal with possibly resetting the clock based on setting of set parameter --------------------------------------------------------------------------- */ if (set == 0 || diff == 0) { if (diff == 0 && msg != 0) printf("\n No adjustment is needed.\n"); return(EXIT_SUCCESS); } if( (health != 0) && (msg != 0) ) printf("\n Server is not healthy, adjustment not recommended."); achr = 'n'; /* default is not to set */ if (set == 3) { /* action depends on answer to query */ printf("Do you want to adjust the local clock ? [y/n] "); fflush(stdout); achr = getchar(); } if ( (set==2) || (set==1 && health==0) || (tolower(achr) == 'y') ) { if (ShiftTime(diff) != 0) { fprintf(stderr, "Unable to modify system clock\n"); return(EXIT_FAILURE); } else { if (msg != 0) printf("Local clock time reset to NIST standard\n"); } } return(EXIT_SUCCESS); } /* --------------------------------------------------------------------------- -- -- Inputs: diff - Time difference from current setting --------------------------------------------------------------------------- */ static int ShiftTime(time_t diff) { DATETIME os2_time; struct tm *tm; time_t newtime; int rc; DosGetDateTime(&os2_time); /* Fill in unknown components */ newtime = time(NULL) - diff; /* Get corrected time */ tm = localtime(&newtime); /* Fill in the structure */ os2_time.hours = tm->tm_hour; os2_time.minutes = tm->tm_min; os2_time.seconds = tm->tm_sec; os2_time.hundredths = 0; os2_time.day = tm->tm_mday; os2_time.month = tm->tm_mon + 1; os2_time.year = tm->tm_year + 1900; os2_time.weekday = tm->tm_wday; rc = DosSetDateTime(&os2_time); return(rc); } /* --------------------------------------------------------------------------- - this subroutine parses switches on the command line. - switches are of the form -. If one is found, a pointer to - the letter is returned in let and a pointer to the value in val as a - long integer. - - parameters argc and argv are passed in from the main calling program. - Note that if argc = 0, no arguments are left. - if a switch is decoded, the value of the function is 1, otherwise zero. - - a number following the letter is decoded as a decimal value unless it - has a leading x in which case it is decoded as hexadecimal. --------------------------------------------------------------------------- */ static int sw(int argc, char *arg, char *let, long *val) { /* either nothing is left or what is left is not a switch */ if( (argc == 0) || (*arg != '-') ) { *let = '\0'; *val = 0; return(0); } ++arg; /* Jump over the - sign */ *let = *arg++; /* Letter option */ if (*arg != 'x') { /* if next char is not x, decode number */ *val = atol(arg); } else { /* Use sscanf to interpret complex value */ sscanf(arg+1, "%lx", val); } return(1); } /* --------------------------------------------------------------------------- - Routine to print the usage in response to a -? option. --------------------------------------------------------------------------- */ static void PrintUsage(void) { fputs( " Syntax: nistime [ options ]\n" "\n" " Description:\n" " This program connects to the daytime service on the NIST time server\n" " time_a.timefreq.bldrdoc.gov using tcp/ip port 13. The time server\n" " returns the current time which is then compared with the time of the\n" " local clock. The difference may be used to adjust the time of the\n" " local clock using DosSetDateTime().\n" "\n" " Options:\n" " -m0 Terse mode. Only the time difference in seconds is written\n" " to standard output. A positive value means local clock is fast.\n" " -m1 Display short time messages. (also -M and default)\n" " -m2 Display verbose time messages.\n" "\n" " -s0 Do not set the local clock.\n" " -s1 Set the local clock if the server is healthy. (also -S)\n" " -s2 Set the local clock even if the server is not healthy.\n" " -s3 Query operator before setting the clock. (default)\n" "\n" "Bugs: (1) This version does not estimate transmission delays in the network.\n" " Not a big deal since time is set to the nearest second only.\n" " (2) Time is instantaneously reset and thus may be non-monotonic.\n" , stdout); return; }