home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
OS/2 Shareware BBS: 35 Internet
/
35-Internet.zip
/
nistime2.zip
/
nistime.c
< prev
next >
Wrap
C/C++ Source or Header
|
2001-11-02
|
22KB
|
554 lines
/* nistime.c - query and set time from NIST server */
#define VERSION_STRING "Version 0.2d 02-nov-2001"
/* ------------------------------ */
/* Feature test macros */
/* ------------------------------ */
/* #define _POSIX_SOURCE /* Always require POSIX standard */
/* ------------------------------ */
/* Standard include files */
/* ------------------------------ */
#define INCL_DOSDATETIME
#define INCL_DOSPROCESS
#include <os2.h>
#include <stdio.h>
#include <time.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
#include <math.h>
#include <types.h> /* Will be OS/2 TCP/IP types.h file */
#include <sys/socket.h> /* More TCP/IP include files */
#include <netinet/in.h>
/* #include <arpa/inet.h> */
#include <netdb.h> /* struct hostent */
/* ------------------------------ */
/* Local include files */
/* ------------------------------ */
/* ------------------------------- */
/* My local typedef's and defines */
/* ------------------------------- */
#define panic SysPanic(__FILE__, __LINE__)
#define NIST_TIME_HOST "time-a.nist.gov"
#define NIST_TIME_PORT 13 /* specific port */
#define UNIX_EPOCH_MJD 40587 /* UNIX epoch date */
#define NIST_PKT_DELAY 50.0 /* network delay, msec */
#define DELAY_MAX_DFLT 250.0 /* max. network delay, msec */
#define DIFF_MIN 0.550 /* adjustment threshold, sec */
#define BUF_SIZE 2000 /* message buffer size*/
#define notdiffer() (fabs( diff) < DIFF_MIN)
#define HEALTH_OK 0
#define MSG_TERSE 0
#define MSG_SHORT 1
#define MSG_VERBOSE 2
#define MSG_INVALID 3
#define SET_NEVER 0
#define SET_HEALTHY 1
#define SET_ALWAYS 2
#define SET_ASK_USER 3
#define SET_INVALID 4
/* successful returns */
#define EXIT_OK 0 /* clock not adjusted */
#define EXIT_ADJUSTED 1 /* clock was adjusted */
/* non-recoverable errors */
#define ERROR_INVALID_ARGUMENT 100
#define ERROR_CANNOT_SET_CLOCK 101
#define ERROR_MEMORY_ALLOCATION 110
#define ERROR_SOCKET_CREATION 111
/* possibly recoverable with a different server name/address */
#define ERROR_INTERNET_ADDRESS 200
#define ERROR_HOST_NAME_RESOLUTION 201
#define ERROR_FAILED_TO_CONNECT 210
#define ERROR_NOTHING_RECEIVED 211
#define ERROR_SERVER_NOT_HEALTHY 212
#define ERROR_MESSAGE_FORMAT 213
/* possibly recoverable by retrying with current server */
#define ERROR_NETWORK_DELAY 300
/* ------------------------------- */
/* My external function prototypes */
/* ------------------------------- */
/* ------------------------------- */
/* My internal function prototypes */
/* ------------------------------- */
static int sw(int argc, char *arg, char *let, long *val);
static int ShiftTime( double diff);
double MakeTime( DATETIME *pt);
void OneShot( ULONG msec);
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
-
- Further modified 31-oct-2001 to add a few useful features, by:
- Pieter Bras
- Cambridge, MA
- pbras@pobox.com
-
- 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; /* server name or addr in . notation */
int pserv = NIST_TIME_PORT; /* port for time service */
struct sockaddr_in *sin; /* socket address structure */
int s; /* socket number */
int length; /* size of message */
char *buf; /* holds message */
struct in_addr address; /* ip address */
struct hostent *hp; /* host data */
DATETIME dt1, dt2;
time_t nist_time; /* Time since epoch */
double msadj; /* msec adjustment to nist_time */
double my_ltime, nist_ltime; /* local time, seconds */
double diff; /* time difference, local - NIST */
double delay; /* network delay, msec */
/* 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 */
float ms_adv;
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
-
- -dnnn Fail if round-trip network delay exceeds nnn milliseconds.
-
- -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 = MSG_SHORT; /* default: short messages */
int set = SET_ASK_USER; /* default: ask before setting clock */
double delay_max = DELAY_MAX_DFLT; /* default network delay, msec */
/* 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_OK);
/* message options */
case 'M':
val = MSG_SHORT;
case 'm':
if( (val >= 0) && (val < MSG_INVALID) )
msg=val;
break;
/* clock-setting options */
case 'S':
val= SET_HEALTHY;
case 's':
if( (val >= 0) && (val < SET_INVALID) )
set=val;
break;
/* max. round-trip network delay (msec) */
case 'd':
if( val > 0)
delay_max = val;
break;
default:
fprintf( stderr, "\nSwitch %c not recognized.\n", let);
return( ERROR_INVALID_ARGUMENT);
break;
}
argc--; /* decrement argument counter */
argv++; /* and increment pointer */
}
/* anything left on the command line assumed to be a time server name/addr */
cp = argc > 0? argv[0]: NIST_TIME_HOST; /* server name or addr */
/* -----------------------------------------------------------------
-- (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 */
/* allocate a big message buffer, in case we get an oversize message */
if( (buf = calloc( BUF_SIZE, 1)) == NULL) {
fprintf( stderr, "Unable to allocate memory for message buffer.\n");
return( ERROR_MEMORY_ALLOCATION);
}
/* if addr is all dotted-decimal chars, use that as IP addr */
if( strspn( cp, "0123456789.") == strlen( cp)) {
if( (address.s_addr = inet_addr( cp)) == -1) { /* Internal format */
fprintf( stderr, "Internet address error: %s\n", cp);
return( ERROR_INTERNET_ADDRESS);
}
}
/* if addr is not all dotted-decimal chars, try to resolve name to IP addr */
else {
hp = gethostbyname( cp);
if( hp)
address.s_addr = *( (long int *) hp->h_addr);
else {
fprintf( stderr, "Can't resolve host name: %s\n", cp);
return( ERROR_HOST_NAME_RESOLUTION);
}
}
if( msg == MSG_VERBOSE) {
cp = inet_ntoa( address);
printf( "Trying time server: %s", cp == (char *) -1? "unknown": cp);
hp = gethostbyaddr( (char *) &address.s_addr, sizeof( address.s_addr),
AF_INET);
printf( " hostname: %s\n", hp? hp->h_name: "(unable to resolve)");
}
if( (sin = calloc( sizeof( *sin), 1)) == NULL) { /* Allocate space */
fprintf( stderr, "Unable to allocate memory for socket info.\n");
return( ERROR_MEMORY_ALLOCATION);
}
sin->sin_family = AF_INET; /* Fill in request */
sin->sin_addr.s_addr = address.s_addr;
sin->sin_port = htons( pserv);
/* Now, create socket, open and read */
if( (s = socket( AF_INET, SOCK_STREAM, 0)) < 0) { /* Get a TCP socket */
psock_errno( "Socket creation error");
return( ERROR_SOCKET_CREATION);
}
DosGetDateTime( &dt1);
if( connect( s, (struct sockaddr *) sin, sizeof( *sin) ) < 0) {
psock_errno( "Failed to connect to NIST server");
soclose( s);
return( ERROR_FAILED_TO_CONNECT);
}
if( (length = recv( s, buf, BUF_SIZE-1, 0)) <= 0) {
psock_errno( "Nothing received from NIST server");
soclose( s);
return( ERROR_NOTHING_RECEIVED);
}
buf[length] = '\0'; /* terminate msg */
/* Immediately, get local time as well for comparison */
DosGetDateTime( &dt2);
my_ltime = MakeTime( &dt2);
delay = (my_ltime - MakeTime( &dt1))*1000.; /* round-trip msec */
if( msg == MSG_VERBOSE)
printf( "round-trip delay = %d msec.\n", (int) (delay+0.5));
if( delay > delay_max) {
fprintf( stderr, "Network delay too long.\n");
return( ERROR_NETWORK_DELAY);
}
/* And close socket, free allocated space */
soclose( s); /* Close port and free memory now */
free( sin);
/* --------------------------------------------------------------------------
-- Make sure the message is in NIST format.
-- Convert received time to seconds since EPOCH.
-- The seconds value gives the time to the next full second. The msec error
-- is given by msADV, so it must be subtracted from NIST seconds. Note that
-- NIST reduces the actual error value by 50 msec to allow for network delays.
--------------------------------------------------------------------------- */
if( strstr( buf, "UTC(NIST" ) == 0) {
fprintf( stderr, "Received message not in NIST format.\n");
return( ERROR_MESSAGE_FORMAT);
}
sscanf(buf," %ld %2d-%2d-%2d %2d:%2d:%2d %d %d %d %f", &mjd, &yr,
&mo, &dy, &hr, &min, &sec, &day_light, &leap_sec, &health, &ms_adv);
nist_time = 86400*(mjd-mjd0) + 3600*hr + 60*min + sec;
/* Adjust msADV for measured network delay, and apply to NIST seconds */
msadj = ms_adv + NIST_PKT_DELAY - 0.35*delay;
nist_ltime = mktime( localtime( &nist_time)) - msadj/1000.;
/* calculate offset (seconds): (system time) - (NIST time) */
diff = my_ltime - nist_ltime;
/* --------------------------------------------------------------------------
-- Output various reports to stdout depending on the setting of msg parameter.
--------------------------------------------------------------------------- */
/* Output desired messages */
if( msg != MSG_TERSE) {
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 == MSG_VERBOSE) { /* 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");
}
cp = (msg == MSG_TERSE)? "%.2f\n": "Local Clock - NIST = %.2f second(s)\n";
printf( cp, diff);
/* --------------------------------------------------------------------------
-- Deal with possibly resetting the clock based on setting of set parameter.
--------------------------------------------------------------------------- */
if( (set == SET_NEVER) || notdiffer() ) {
if( notdiffer() && (msg != MSG_TERSE) )
printf("\n %s\n", health == HEALTH_OK? "No adjustment is needed.":
"No adjustment; server is not healthy.");
return( health == HEALTH_OK? EXIT_OK: ERROR_SERVER_NOT_HEALTHY);
}
if( (health != HEALTH_OK) && (msg != MSG_TERSE) )
printf( "\n Server is not healthy, adjustment not recommended.");
achr = 'n'; /* default is not to set */
if( set == SET_ASK_USER) { /* action depends on answer to query */
printf( "Do you want to adjust the local clock ? [y/n] ");
fflush( stdout);
achr = getchar();
}
if( (set == SET_ALWAYS)
|| ( (set == SET_HEALTHY) && (health == HEALTH_OK) )
|| (tolower( achr) == 'y') ) {
if( ShiftTime( diff) != 0) {
fprintf( stderr, "Unable to modify system clock\n");
return( ERROR_CANNOT_SET_CLOCK);
}
if( msg != MSG_TERSE)
printf( "Local clock time reset to NIST standard\n");
return( EXIT_ADJUSTED);
}
return( health == HEALTH_OK? EXIT_OK: ERROR_SERVER_NOT_HEALTHY);
}
/* ---------------------------------------------------------------------------
-- Inputs: diff - Time difference from current setting
--------------------------------------------------------------------------- */
static int ShiftTime( double diff)
{
DATETIME os2_time;
struct tm *tm;
time_t new_time;
double ltime, lsec;
int rc = 0;
/* wait for a safe moment to set the clock */
while( 1) {
DosGetDateTime( &os2_time);
if( os2_time.hundredths < 40)
break;
OneShot( 10*( 100 - os2_time.hundredths));
}
os2_time.hundredths = 0;
ltime = MakeTime( &os2_time) - diff;
lsec = floor( ltime + 0.5); /* round to nearest sec */
new_time = (time_t) lsec;
tm = localtime( &new_time); /* Fill in the structure */
os2_time.hours = tm->tm_hour;
os2_time.minutes = tm->tm_min;
os2_time.seconds = tm->tm_sec;
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);
if( rc)
fprintf( stderr, "error from DosSetDateTime: rc = %d\n", rc);
return( rc);
}
/* convert OS/2 DATETIME values to a (double) Unix time */
double MakeTime( DATETIME *pt)
{
struct tm t;
double d;
t.tm_year = pt->year - 1900;
t.tm_mon = pt->month - 1;
t.tm_mday = pt->day;
t.tm_hour = pt->hours;
t.tm_min = pt->minutes;
t.tm_sec = pt->seconds;
d = (double) mktime( &t) + (pt->hundredths)/100.;
return( d);
}
/* delay for some msec value */
void OneShot( ULONG msec)
{
DosSleep( msec); /* probably good enough */
}
/* ---------------------------------------------------------------------------
- this subroutine parses switches on the command line.
- switches are of the form -<letter><value>. 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(
"NISTIME for OS/2 Warp " VERSION_STRING "\n"
"\n"
"Syntax: nistime [ options ] [ server ]\n"
"\n"
" Connects to the daytime service on NIST time server " NIST_TIME_HOST "\n"
" using tcp/ip port 13. The server returns the current time which is compared\n"
" with the local clock. The difference may be used to adjust the local clock.\n"
" The optional 'server' argument specifies an alternate NIST time server;\n"
" it may be entered as host name or dotted-decimal IP address.\n"
"\n"
"Options:\n"
" -dnnn Fail if round-trip network delay exceeds nnn msec. (default: 250)\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"
" -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"
"Bugs:\n"
" (1) Time is set to the nearest second only (+/- 0.5-second error).\n"
" (2) Time is instantaneously reset and thus may be non-monotonic.\n"
, stdout);
return;
}
/* <eof> */