/* * Module DTSS$PROVIDER.C * Version V2.0-103 */ /* * Copyright (c) 1990,1991,1992 by * Digital Equipment Corporation, Maynard, Mass. * * This software is furnished under a license and may be used and copied * only in accordance with the terms of such license and with the * inclusion of the above copyright notice. This software or any other * copies thereof may not be provided or otherwise made available to any * other person. No title to and ownership of the software is hereby * transferred. * * The information in this software is subject to change without notice * and should not be construed as a commitment by Digitial Equipment * Corporation. * * Digital assumes no responsibility for the use or reliablity of its * software on equipment which is not supplied by Digital. * * FACILITY: Distributed Time Synchronization Service (DTSS) * * ABSTRACT: * Implements a DECdts time provider which obtains time from * the following list of radio receivers, connected thru a * RS232 terminal interface. Please refer to the DECdts * Concepts Manual for information about the geographical * coverage of these radio providers. * * * * ==================================================================== * NTP time provider | TP_NTP * ==================================================================== * * TO COMPILE: * * $! * $! This DCL Command Procedure will compile and link some * $! of the DTSS NTP time provider example. * $! * $ BUILD_NTP: * $ CC/OBJ='name/G_FLOAT DTSS$PROVIDER.C * $ LINK/EXE='name 'name, - * SYS$INPUT:/OPTIONS * SYS$LIBRARY:UCX$IPC.OLB/LIB * SYS$LIBRARY:VAXCRTLG.OLB/LIB * SYS$SHARE:DTSS$SHR.EXE/SHARE * $ RETURN * * * * TO RUN: * * Four logical names are translated at program start from * the system logical name table which control the characteristics * of the time provider's operation, they are: * * DTSS$_TP_HOSTNAME : NTP Host from which we import the time. * This is set to the hostname that is running the NTP * provider which we want to use. * This logical is ignored by all but the NTP provider. * * Format: Any ASCII string. * * DTSS$_TP_INACCURACY : Systematic inaccuracy in ms. * Each radio clock specifies the amount of systematic * inaccuracy which is returned with each time-stamp. * This is usually proportional to the number of miles * the device is physically located from the transmission * center from which time is read. The inaccuracy specified * by this parameter is added to the times returned to the * time server. * * Format: a non-negative integer. * * DTSS$_TP_POLL_RATE : Rate in seconds that the device is * polled. This value will override the DTSS management * parameter 'synchronization hold down'. * * Format: an integer (seconds) in the range 1 second to 31 days; * * DTSS$_TP_TIME_STAMPS : This parameter specifies the number * of timestamps which are read and returned to the * time server at each synchronization. * * Format: an integer in the range 1 to 6; * * DTSS$_TP_MAXERROR : If the difference between the time * provider and the local clock differs more than * maxerror, then the timeprovider is considered faulty, * and its timestamp is ignored. * * Format: a non-negative integer (seconds). * * DTSS$_TP_FIRSTSYNC : If this value is TRUE, then the * DTSS$TP_MAXERROR parameter is ignored on the first * synchronization. * * Format: 0 , false; otherwise TRUE. * * DTSS$_TP_VERBOSE : Display info to std out * * Format: 0, false; otherwise TRUE. * * This is an example session, only setting the dtss$_tp_hostname * logical is required, all others are optional: * * ENVIRONMENT: VAX/VMS V5.5 * * AUTHOR: * * Distributed Processing Engineering - DPE/DTSS * * MODIFICATIONS: * * 28-Mar-2005 Nischay Code changes made for the case QXCM1000202046 * to rectify NTP time ptovider not synchronizing * with any NTP servers, if atleast one of them * failed. */ #pragma nostandard /* A VMS only Module */ #include /* standard I/O definitions */ #include /* I/O definitions */ #include /* error return value (SS$) definitions */ #include /* descriptor definitions */ #include /* Logical name definitions */ #include /* isxxx macros, etc. */ #include /* tm structure definition */ #include /* utc library routines & dtss tpi defs */ #include #include #include #include #include #include #include #define NOSERVER 0 /* DTSS Server will not be used when this symbol */ /* is set to 1. The provider will "loop", poling */ /* the external device. */ /* * Constants */ #define BUFSIZE 40 /* Length of ascii time string */ #define K_NS_PER_SEC (1000000000) #define K_MS_PER_SEC (1000) #define K_NS_PER_MS (K_NS_PER_SEC / K_MS_PER_SEC) #define LOGTOD(a) ((a) < 0 ? 1. / (1L << -(a)) : \ 1.*(1L << (int)(a))) /* log2 to double */ typedef struct IOsb { /* IO status Block */ unsigned short status; unsigned short byteCount; unsigned long devStatus; } IOsb; /* * Forward Definitions */ unsigned long InitIPC( unsigned short *tstpChan, unsigned short *tptsChan ); unsigned long SendReply( unsigned short , TPrspMsg * ); unsigned long ReadRequest( unsigned short , TPreqMsg * ); void InitializeTPdefaults(int *pollrate, int *timestamps, int *inaccuracy, int *tmorate, int *clockSet, int *retries, int *firstsynch, int *maxtperror, int *isverbose); int QueryProvider(int ttchan, struct utc *beforeTime, struct utc *TPtime, struct utc *afterTime, int idx); int ValidateTime(struct utc *systemTime, struct utc *externalTime); int SortHostsBasedOnResponse(int hostidx); int AlterRetries(int hostidx); /* * Global Variables */ static int ttchan; /* channel number of term */ $DESCRIPTOR(TSTPLOG, "DTSS$_TSTP_MBX"); /* tpts mailbox */ $DESCRIPTOR(TPTSLOG, "DTSS$_TPTS_MBX"); /* tstp mailbox */ $DESCRIPTOR(TPINACC, "DTSS$_TP_INACCURACY"); /* Inaccuracy parameter */ $DESCRIPTOR(TPRATE, "DTSS$_TP_POLL_RATE"); /* Poll rate parameter */ $DESCRIPTOR(TPSTAMPS, "DTSS$_TP_TIME_STAMPS"); /* Time stamps parameter */ $DESCRIPTOR(MAXERROR, "DTSS$_TP_MAXERROR"); /* Max error allowed on TP */ $DESCRIPTOR(FIRSTSYNC, "DTSS$_TP_FIRSTSYNC"); /* Always synch first time */ $DESCRIPTOR(VERBOSE, "DTSS$_TP_VERBOSE"); /* Print info out. */ #define K_MAX_HOSTNAME (2560) /* largest hostname user can use*/ #define K_MAX_HOSTS 16 char hostname[K_MAX_HOSTNAME]; /* Host name */ char *hosts[K_MAX_HOSTS]; int tsidx=0, nidx=0; unsigned int unresponsive_servers_map[K_MAX_HOSTS]; /* For each host init the retry count. And keep decrementing them when the server dosent respond. This way we know who is likely the candidate for next request. Then before coming to ReadTimes, sort the host based on this retry count. When the server responds, then increment the retry count. unresponsive_servers_map will be used for that. */ $DESCRIPTOR(NTP_HOSTNAME,"DTSS$_TP_HOSTNAME"); /* For NTP, a remote server */ $DESCRIPTOR(PROCESS_NAME, "DTSS$PROVIDER"); /* Process name when OK */ $DESCRIPTOR(LNM_TABLE,"LNM$SYSTEM_TABLE") ; /* Table to search */ unsigned long INACCURACY; /* base inaccuracy of TP */ unsigned long POLLRATE; /* seconds between TP syncs */ unsigned long TIMESTAMP; /* time stamps at each sync */ unsigned long TMORATE; /* timeout */ unsigned long CLOCKSET; /* allow clock sets? */ unsigned long RETRIES; /* retries allowed. */ unsigned long FIRSTSYNCH; /* always synch first time */ unsigned long MAXTPERROR; /* Max error on TP. */ unsigned long ISVERBOSE; /* Print info. */ unsigned long SYNCHCOUNT; /* current synch. number */ /* *++ * ReadTimes() * * Functional Description: * * Parse out time stamps from the radio clock input. * * Inputs: * * timeStamps - the number of timestamps to read * fd - radio clock file descriptor * * Outputs: * * tpTimeMsg - buffer to return the timestamp data. * * Value Returned: * * 1 - Success * 0 - Failure * *-- */ int ReadTimes( timeStamps, retries, ttchan, tpTimeMsg ) unsigned long timeStamps; unsigned long retries; int ttchan; TPtimeMsg *tpTimeMsg; { int i,j; /* temp, index */ unsigned long status; unsigned int loop_start = 0; /* * Now Read The Time Stamps. * Note that this loop will terminate having read timeStamps timestamps * or if none of the hosts respond */ i = 0; j = 0; while ( i < timeStamps ) { if (ISVERBOSE) printf("\nReadTimes: Query host %s",hosts[j]); status = QueryProvider((int)ttchan, &tpTimeMsg->timeStampList[i].beforeTime, &tpTimeMsg->timeStampList[i].TPtime, &tpTimeMsg->timeStampList[i].afterTime, j ); if (status & 1) { status = ValidateTime(&tpTimeMsg->timeStampList[i].afterTime, &tpTimeMsg->timeStampList[i].TPtime); if (!(status & 1)) PrintValidationError(&tpTimeMsg->timeStampList[i].afterTime, &tpTimeMsg->timeStampList[i].TPtime); } if (status & 1) { if (ISVERBOSE) printf("\nReadTimes: QueryProvider Succeded"); i += 1; } else { unresponsive_servers_map[j] = 0; if (ISVERBOSE) printf("\nReadTimes: QueryProvider failed"); } loop_start = j; do{ j = (j+1)%tsidx; /* Return Failure only if none of the servers responded and none of them have any retries left. */ if(loop_start == j && unresponsive_servers_map[j] == 0) return(0); }while(unresponsive_servers_map[j] == 0); if (ISVERBOSE) printf("\nReadTimes: Next host to query is %s",hosts[j]); } if (ISVERBOSE) printf("\nReadTimes: Succeded"); return(1); } /* * SortHostsBasedOnResponse routine will do the normal bubble * sort and sort the hosts based on the previous responses. * Previous responses are evaluated based on the retries left * for each host. */ int SortHostsBasedOnResponse(int hostidx) { int i=0; int j=0; int temp; int clean_pass=0; char *temp_hostname; if(hostidx <= 0) return (0); for(i = 0 ; !clean_pass && i < hostidx ; i++) { clean_pass =1; for(j = 0 ; j < hostidx-i-1 ; j++) if(unresponsive_servers_map[j] < unresponsive_servers_map[j+1]) { temp = unresponsive_servers_map[j]; unresponsive_servers_map[j] = unresponsive_servers_map[j+1]; unresponsive_servers_map[j+1] = temp; temp_hostname = hosts[j]; hosts[j] = hosts[j+1]; hosts[j+1] = temp_hostname; clean_pass =0; } } return 1; } /* * AlterRetries will bump up the retry counts for the hosts which * has '0' retry count. This is required because, ReadTimes will * will not consider the host with '0' retry count for querying. * Retry Signifies that the NTP server be retried in next * Synchronization cycle. */ int AlterRetries(int hostidx) { int i; if (hostidx <= 0) return(0); for(i = hostidx-1; i >= 0; i--) { if(unresponsive_servers_map[i] == 0) { unresponsive_servers_map[i]++; if(ISVERBOSE) printf("\nAlterRetries: Host:%s, retries = %d", hosts[i],unresponsive_servers_map[i]); } else { if(ISVERBOSE) printf("\nAlterRetries: Host:%s, retries = %d", hosts[i],unresponsive_servers_map[i]); /* This is done for optimization, we check servers in reverse order, and break when we find atleast one server that responded. In case of verbose we run through all of them. Due to this optimization, the sorted host order will be printed in reverse. */ if(!ISVERBOSE) break; } } return(1); } /* *++ * main() * * Functional Description: * * The Time Provider program communicates with the Distributed * Time Synchronization Service and with a time provider * device. This module implements the DECdts TP interface. * * * Inputs: * * None. * * Implicit Inputs: * * None. * * Outputs: * * None. * * Implicit Outputs: * * IPC with DTSS$SERVICE. * * Value Returned: * * None. * * Side Effects: * * None. * *-- */ main( argc, argv ) int argc; char *argv[]; { TPreqMsg timeServiceRequest; TPrspMsg TMOresponse; TPrspMsg timeResponse; unsigned long status; unsigned short tstpChan; unsigned short tptsChan; char value[255]; char *p = hostname; int i; short size; unsigned long attr = LNM$M_CASE_BLIND; /* attributes mask */ struct { short bufln0 ; short itmcd0 ; int *bufad0 ; short *rtnln0 ; short bufln1 ; short itmcd1 ; char *bufad1 ; short *rtnln1 ; long term; } itmlst={4, LNM$_INDEX, &tsidx, &size, 255, LNM$_STRING, value, &size, 0 }; /* * read the startup parameters */ InitializeTPdefaults((int *) &POLLRATE, (int *) &TIMESTAMP, (int *) &INACCURACY, (int *) &TMORATE, (int *) &CLOCKSET, (int *) &RETRIES, (int *) &FIRSTSYNCH, (int *) &MAXTPERROR, (int *) &ISVERBOSE); /* * keep count of synchs attempted. */ SYNCHCOUNT = 0; /* * Set the process name to dtss$provider */ if (!(status = SYS$SETPRN( &PROCESS_NAME ))&1) exit(status); status = SYS$TRNLNM(&attr, &LNM_TABLE, &TPRATE, 0, &itmlst ); if ((status & 1)) { value[size] = '\0'; POLLRATE = atoi( value ); } ; status = SYS$TRNLNM(&attr, &LNM_TABLE, &TPSTAMPS, 0, &itmlst ); if ((status & 1)) { value[size] = '\0'; TIMESTAMP = atoi( value ); if (TIMESTAMP > K_MAX_TIMESTAMPS || TIMESTAMP < K_MIN_TIMESTAMPS ) { fprintf(stderr,"The time stamp value is out of legal range\n"); fprintf(stderr,"Legal Range is %d to %d\n", K_MIN_TIMESTAMPS,K_MAX_TIMESTAMPS); exit(1); } } status = SYS$TRNLNM(&attr, &LNM_TABLE, &TPINACC, 0, &itmlst ); if ((status & 1)) { value[size] = '\0'; INACCURACY = atoi( value ) ; } ; status = SYS$TRNLNM(&attr, &LNM_TABLE, &TPRATE, 0, &itmlst ); if ((status & 1)) { value[size] = '\0'; POLLRATE = atoi( value ); } ; status = SYS$TRNLNM(&attr, &LNM_TABLE, &MAXERROR, 0, &itmlst ); if ((status & 1)) { value[size] = '\0'; MAXTPERROR = atoi( value ); } ; status = SYS$TRNLNM(&attr, &LNM_TABLE, &VERBOSE, 0, &itmlst ); if ((status & 1)) { value[size] = '\0'; ISVERBOSE = atoi( value ); } ; status = SYS$TRNLNM(&attr, &LNM_TABLE, &FIRSTSYNC, 0, &itmlst ); if ((status & 1)) { value[size] = '\0'; FIRSTSYNCH = atoi( value ); } ; for( tsidx = 0; tsidx < K_MAX_HOSTS; tsidx++ ) { value[0] = '\0'; status = SYS$TRNLNM(&attr, &LNM_TABLE, &NTP_HOSTNAME, 0, &itmlst ); if ((status & 1)&&(size>0)) { value[size] = '\0'; hosts[tsidx] = p; p=(char *)strcpy(p,value); p=p+size+1; } else break; } ; /* * Echo back the input parameters. */ fprintf(stderr,"PARAMETERS SET:\n"); for( i=0; i < tsidx; i++) { fprintf(stderr,"TP_HOSTNAME: %s\n", hosts[i]); unresponsive_servers_map[i] = 1; /* If want to implement the retries to each host we have use this RETRIES value in unresponsive_servers_map and alter ReadTimes to handle retries. = RETRIES; */ } fprintf(stderr,"TP_INACCURACY: %d milliseconds\n", INACCURACY); fprintf(stderr,"TP_POLL_RATE: %d seconds\n", POLLRATE); fprintf(stderr,"TP_TIME_STAMPS: %d timestamps\n", TIMESTAMP); fprintf(stderr,"TP_MAXERROR: %d seconds\n", MAXTPERROR); fprintf(stderr,"TP_FIRSTSYNC: %s\n", FIRSTSYNCH?"TRUE":"FALSE"); fprintf(stderr,"TP_VERBOSE: %s\n\n\n", ISVERBOSE?"TRUE":"FALSE"); /* * Initialize the interprocess communication. * Die if this fails. */ status = InitIPC( &tstpChan, &tptsChan ); if ( !(status & 1) ) exit( status ); /* * Prepare a time out response message to issue to the service * which will identify this process as active, and * tell the service how long it must wait for the time response. */ TMOresponse.version_major = K_TPI_VERSION_MAJOR; TMOresponse.version_minor = K_TPI_VERSION_MINOR; TMOresponse.status = K_TPI_SUCCESS; TMOresponse.TPmsgType = K_TPI_CTL_MESSAGE; TMOresponse.TPdata.TPctlMsg.nextPoll = POLLRATE; TMOresponse.TPdata.TPctlMsg.timeout = TMORATE; TMOresponse.TPdata.TPctlMsg.noClockSet = CLOCKSET; /* * Goto Work! * Issue a synchronous IO request to the server request * mailbox. When the the server responds, Reply to the * response mailbox with the set of TP times. Exit the * program if there is any error associated with the * IPC. * */ for (;;) { char daytime[40]; utc_t now; status = ReadRequest( tstpChan, &timeServiceRequest ); if ( !(status & 1) ) exit( status ); utc_gettime(&now); utc_asclocaltime(daytime, 40, &now); /* * increment the synchcount, * make sure it doesn't loop around to 1 again * since on synch #1 the MAX TP ERROR value * is ignored. */ SYNCHCOUNT++; SYNCHCOUNT = (SYNCHCOUNT==0)?2:SYNCHCOUNT; printf("%%DTSS-I-INISYNC, Sync #%d, %s ID: %d\n", SYNCHCOUNT, daytime, timeServiceRequest.TPsyncID); timeResponse.version_major = K_TPI_VERSION_MAJOR; timeResponse.version_minor = K_TPI_VERSION_MINOR; timeResponse.TPsyncID = timeServiceRequest.TPsyncID; timeResponse.TPdata.TimeMsg.timeStampCount = 0; timeResponse.status = K_TPI_SUCCESS; timeResponse.TPmsgType = K_TPI_TIME_MESSAGE; /* * Initialize the time provider hardware. */ status = InitNTP(); if ( !( status & 1 ) ) { timeResponse.status = K_TPI_FAILURE; if (!(status = SendReply ( tptsChan, &timeResponse ))&1) exit(status); } else { /* * Send the initial repsonse, informing the * the time service that we are alive and well, * and that it should wait for the time stamps * to arrive. */ TMOresponse.TPsyncID = timeServiceRequest.TPsyncID; status = SendReply( tptsChan, &TMOresponse ); if ( !(status & 1) ) exit( status ); status = ReadTimes(TIMESTAMP, RETRIES, ttchan, &timeResponse.TPdata.TimeMsg); if ( status & 1 ) { timeResponse.TPdata.TimeMsg.timeStampCount = TIMESTAMP; timeResponse.status = K_TPI_SUCCESS; } else { timeResponse.status = K_TPI_FAILURE; } status = SendReply( tptsChan, &timeResponse ); if ( !(status & 1) ) exit( status ); /* * Debugging output. */ if (ISVERBOSE) PrintTimes( &timeResponse ); /* * Call for the Time Provider Hardware to do any necessary * cleanup by calling ExitTP. */ CleanupNTP(); utc_gettime(&now); utc_asclocaltime(daytime, 40, &now); printf("%%DTSS-S-DONE, Sync complete, %s\n", daytime); /* I dont have any work until i get one more synch request. So do the below stuff... */ SortHostsBasedOnResponse(tsidx); AlterRetries(tsidx); } } } /* End of main program*/ /* *++ * ValidateTime() * * Functional Description: * * This routine confirms that the time returned from the system, is * within MAXTPERROR seconds of the time returned by the external * device. * * Inputs: * * systemTime - time returned by the system. * externalTime- time returned by the external device. * * Implicit Inputs: * * SYNCHCOUNT - current synch number. * FIRSTSYNCH - allow first synch to always validate. * MAXTPERROR - greatest difference between two times. * * Outputs: * * None. * * Implicit Outputs: * * None. * * Value Returned: * * Returns TRUE iff * (abs(externalTime - systemTime) <= MAXTPERROR) || * (FIRSTSYNCH && SYNCHCOUNT == 1) * * Side Effects: * * None. * *-- */ int ValidateTime(systemTime,externalTime) struct utc *systemTime; struct utc *externalTime; { struct utc result; struct timespec inaccsp; struct reltimespec timesp; /* * find the difference between the system time and the * time reported by the external clock. */ if (utc_subtime(&result,systemTime,externalTime) || utc_abstime(&result,&result) || utc_binreltime(×p,&inaccsp,&result)) exit(0); /* * if the difference is less than the max error, or * if first synch is enabled, return true. */ return( ((SYNCHCOUNT == 1) && (FIRSTSYNCH)) || (timesp.tv_sec <= MAXTPERROR) ); } /* *++ * InitIPC() * * Functional Description: * * This routine creates a temporary Mailbox to which the Time Service * Process will write its requests. * * Inputs: * * None. * * Implicit Inputs: * * None. * * Outputs: * * None. * * Implicit Outputs: * * None. * * Value Returned: * * None. * * Side Effects: * * None. * *-- */ unsigned long InitIPC( tstpChan, tptsChan ) unsigned short *tstpChan; unsigned short *tptsChan; { #if NOSERVER return(1); #else unsigned long status; /* * Create a request channel to the time service. Mark the mailbox * for delete so that it will go away when this process deassigns * its channel. Note that the service will only attach to the * mailbox while it is communicating with the TP, it is generally * detatched. */ status = SYS$CREMBX (1, /* prmflag */ tstpChan, /* channel */ sizeof(TPreqMsg), /* maxmsg */ sizeof(TPreqMsg)*2, /* bufquo */ 0x0330, /* promsk */ 0, /* access in current mode */ &TSTPLOG); /* DTSS$_TSTP_MBX */ if ( !(status & 1) ) return( status ); if ( !(( status = SYS$DELMBX ( *tstpChan ) ) & 1) ) return( status ); /* * Assign a channel to the TP to TS mailbox which was created by * the DTSS$Service process. */ status = SYS$ASSIGN (&TPTSLOG, /* TPtoTS mailbox */ tptsChan, /* channel */ 0, /* access mode */ 0); /* mailbox */ return ( status ); #endif } /* End of routine InitIPC */ /* *++ * SendReply * * Functional Description: * * This routine writes a response message to the Time Service Process's * response Mailbox. * * Inputs: * * tptsChan - The channel to the TS response mailbox * * timeResponse - the address of the response message which * is to be written to the TS. * * Implicit Inputs: * * None. * * Outputs: * * None. * * Implicit Outputs: * * None. * * Value Returned: * * unsigned long - the status of the write request. * * Side Effects: * * None. * *-- */ unsigned long SendReply( unsigned short tptsChan, TPrspMsg *timeResponse ) { IOsb iosb; unsigned long status; #if NOSERVER return(1); #else status = SYS$QIOW(0, /* efn */ tptsChan, /* chan */ IO$_WRITEVBLK | /* func */ IO$M_NORSWAIT | IO$M_NOW, &iosb, /* I/O status block */ 0, /* ast addr */ 0, /* ast prm */ timeResponse, /* P1 = buffer to write */ sizeof(TPrspMsg), /* P2 = size in bytes */ 0, /* P3 */ 0, /* P4 */ 0, /* P5 */ 0); /* P6 */ if ( !(status & 1) ) return( status ); if ( !(iosb.status & 1) ) return((unsigned long) iosb.status ); return( status ); #endif } /* End of routine SendReply */ /* *++ * ReadRequest * * Functional Description: * * This routine reads a request message written by the Time Service * process to the TSTP mailbox. * * Inputs: * * None. * * Implicit Inputs: * * The Channel to the request mailbox is attached. * * Outputs: * * timeRequest - the address of the structure which will * receive the Time Service request message. * * Implicit Outputs: * * None. * * Value Returned: * * unsigned long - the status of the read of the mailbox. * * Side Effects: * * None. * *-- */ unsigned long ReadRequest( unsigned short tstpChan, TPreqMsg *timeRequest ) { #if NOSERVER sleep(10); return(1); #else IOsb iosb; unsigned long status; status = SYS$QIOW(0, /* efn */ tstpChan, /* chan */ IO$_READVBLK, /* func */ &iosb, /* I/O status block */ 0, /* ast addr */ 0, /* ast prm */ timeRequest, /* P1 = buffer to write */ sizeof(TPreqMsg), /* P2 = size in bytes */ 0, /* P3 */ 0, /* P4 */ 0, /* P5 */ 0); /* P6 */ if ( !(status & 1) ) return( status ); if ( !(iosb.status & 1) ) return((unsigned long) iosb.status ); /* * Check that we got the size message we expected. */ if (iosb.byteCount != sizeof(TPreqMsg)) { (void) fprintf(stderr, "rqstMsg size %d, expected %d\n", iosb.byteCount, sizeof(TPreqMsg)); return( SS$_NODATA ); } if (timeRequest->version_major != K_TPI_VERSION_MAJOR || timeRequest->version_minor != K_TPI_VERSION_MINOR) { (void) fprintf(stderr,"TPI version mismatch \n"); return( SS$_NODATA ); } return( status ); #endif } /* End of routine ReadRequest */ /* ******************************************************** ** * NTP Specific Routines. ******************************************************** ** */ /* * DESCRIPTION OF THE * NETWORK TIME PROTOCOL (NTP) * * The Network Time Protocol is an Internet recommended standard for * distributing time. This provider assumes the user is familiar with * the NTP protocol and has a NTP server available as a time source. * * -------------------------------------------------------- ------------------ */ /* * FD macros */ #ifndef FD_SET typedef struct fd_set{char fds_bits[4];} fd_set; #define NFDBITS 32 #define FD_SETSIZE 32 #define FD_SET(n, p) ((p)->fds_bits[(n)/NFDBITS] |= (1 << ((n) % NFDBITS))) #define FD_CLR(n, p) ((p)->fds_bits[(n)/NFDBITS] &= ~(1 << ((n) % NFDBITS))) #define FD_ISSET(n, p) ((p)->fds_bits[(n)/NFDBITS] & (1 << ((n) % NFDBITS))) #define FD_ZERO(p) memset((char *)(p), 0, sizeof(*(p))) #endif /* * Conversion factors */ #define K_NS_PER_SEC (1000000000) #define K_MS_PER_SEC (1000) #define K_NS_PER_MS (K_NS_PER_SEC / K_MS_PER_SEC) #define K_100NS_PER_MS (10000) /* * Literals */ #define K_NTP_TIMEOUT (15) /* allow NTP server 15 * * seconds to respond */ #define K_NTP_RETRY (3) /* Two tries per request */ #define K_NTP_JAN_1970 (2208988800) /* 1970 - 1900 in seconds */ #define K_DTS_NTP_STRATUM (8) /* Stratum reported by DTS * * when it gives time to NTP */ #define NTP_POLL_RATE 300 /* seconds */ #define NTP_TIME_STAMPS 4 /* int, 1-6 */ #define NTP_INACCURACY 0 /* milliseconds,inaccuracy to add */ #define NTP_TIMEOUT 180 /* seconds */ #define NTP_NOCLOCKSET 0 /* int, t/f - 1/0 */ #define NTP_RETRIES 4 /* int > 0 */ #define NTP_FIRSTSYNCH 1 /* int > 0 */ #define NTP_MAXTPERROR 30 /* seconds */ #define NTP_ISVERBOSE 1 /* int > 0 */ /* * NTP message parameters */ #define K_NTP_VERSION3 (3) /* version */ #define K_NTP_CLIENT (3) /* Mode: client */ #define K_NTP_UNSPECIFIED (0) /* Stratum unspecified */ #define K_NTP_DEF_SERVICE_PORT (123) /* default endpoint of NTP */ /* * Structure definitions for NTP fixed point values * * 0 1 2 3 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Integer Part | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Fraction Part | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * * 0 1 2 3 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Integer Part | Fraction Part | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ struct l_fixedpt { u_long int_part; u_long fraction; }; struct s_fixedpt { u_short sint_part; u_short sfraction; }; /* * NTP packet definitions * * 0 1 2 3 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * |LI | VN |Mode | Stratum | Poll | Precision | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Synchronizing Distance | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Estimated Drift Rate | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Reference Clock Identifier | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | | * | Reference Timestamp (64 bits) | * | | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | | * | Originate Timestamp (64 bits) | * | | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | | * | Receive Timestamp (64 bits) | * | | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | | * | Transmit Timestamp (64 bits) | * | | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ struct ntpdata { unsigned int mode:3; /* mode, version, leap, status */ unsigned int version:3; unsigned int leap_status:2; u_char stratum; /* Stratum level */ u_char poll; /* poll value */ int precision:8; struct s_fixedpt distance; struct s_fixedpt drift; struct in_addr refid; struct l_fixedpt reftime; struct l_fixedpt org; struct l_fixedpt rec; struct l_fixedpt xmt; }; static struct ntpdata ntp_data; /* NTP message structure */ struct ntpdata *pkt = &ntp_data; /* NTP message pointer */ struct sockaddr_in dst; /* Socket structure for NTP */ struct sockaddr_in dsts[K_MAX_HOSTS]; /* Socket structure for NTP */ struct sockaddr_in socket_in = {AF_INET}; int dstlen = sizeof(dst); /* Size of dst struct */ struct sockaddr tstpSockAddr_un; /* UNIX domain socket for TSTP */ struct servent *sp; /* Service structure */ struct hostent *hp; /* Host structure */ void cvt_ntp_utc(struct utc *, struct l_fixedpt *, struct s_fixedpt *); void cvt_utc_ntp(struct l_fixedpt *, struct utc *); /* *++ * InitializeTPdefaults() * * Functional Description: * * Initialize the default input parameters for the external time * provider. * * * * Inputs: * Implicit Inputs: * * * Outputs: * * pollrate - int to be set to the default poll rate * timestamp- int to be set to the default number of * timestamps to read from the external time provider. * inaccuracy-int to be set to the default inaccuracy for the * external time provider (systematic error). * tmorate -int to be set to the default time-out rate (the * number of seconds the dtss server must wait for * the data response). * clockset - int to be set to true if the system clock should not * be affected by the results of the TP synch, false if * the clock can be modified by the results of the TP * synch. * retries - int to be set to the number of times the program is * allowed to resend a request after the * external time source has returned a failure. * firstsync- boolean, assume that the time returned by the external * clock is correct on the first synchronization * maxtperror-if the difference between the local clock and the * time provider differs by more than maxtperror, the * external clock is assumed faulty. * local clock and the time provider * isverbose -boolean, > 0 indicates that informational messages * will be displayed. * * * * Implicit Outputs: * none. * * Value Returned: * none. * * Side Effects: * none. * *-- */ void InitializeTPdefaults(pollrate,timestamp, inaccuracy,tmorate, clockSet,retries, firstsynch,maxtperror, isverbose) int *pollrate, *timestamp, *inaccuracy, *tmorate, *clockSet, *retries, *firstsynch, *maxtperror, *isverbose; { *pollrate = NTP_POLL_RATE; /* seconds */ *timestamp = NTP_TIME_STAMPS; /* int, 1-6 */ *inaccuracy= NTP_INACCURACY; /* seconds */ *tmorate = NTP_TIMEOUT; /* seconds */ *clockSet = NTP_NOCLOCKSET; /* int, t/f - 1/0 */ *retries = NTP_RETRIES; /* int > 0 */ *firstsynch= NTP_FIRSTSYNCH; /* int > 0 */ *maxtperror= NTP_MAXTPERROR; /* seconds */ *isverbose = NTP_ISVERBOSE; /* int > 0 */ } /* End of routine InitializeTPdefaults */ CleanupNTP() { int retval; retval = shutdown(ttchan,2); if (retval == -1) perror("shutdown"); retval = close(ttchan); if (retval) perror("close"); } InitNTP() { int service_port; int i; int found_atleast_one = 0; struct in_addr temp = {0}; /* * Create a socket and bind it to ntp/udp */ if ((ttchan = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { fprintf(stderr,"dtssprovider: NTP socket - %d",errno); return(0); } #if 0 sp = getservbyname("ntp", "udp"); if (sp <= NULL) { (void) fprintf(stderr, "\ndtssprovider: UDP/NTP: service unknown"); (void) fprintf(stderr, "\ndtssprovider: Assuming NTP port is %d", K_NTP_DEF_SERVICE_PORT); dst.sin_port = htons(K_NTP_DEF_SERVICE_PORT); dsts[0].sin_port = htons(K_NTP_DEF_SERVICE_PORT); } else { dst.sin_port = htons(sp->s_port); dsts[0].sin_port = htons(sp->s_port); } #else dst.sin_port = htons(K_NTP_DEF_SERVICE_PORT); #endif /* * Lookup host name and address */ for(i=0; ih_addrtype; dst.sin_addr = *((struct in_addr *)hp->h_addr); dsts[i].sin_family = hp->h_addrtype; dsts[i].sin_addr = *((struct in_addr *)hp->h_addr); dsts[i].sin_port = htons(K_NTP_DEF_SERVICE_PORT); found_atleast_one++; } } /* If we were not able to find atleast one host, then better to exit. Let us keep that as the last option. Else simply keep trying when ever a synch request comes in. */ if(!found_atleast_one) {close(ttchan); return (0);} if (ISVERBOSE) printf("\nInitNTP: Atleast one server address is resolved"); return(1); } /* *++ * QueryProvider() * * Functional Description: * * Query an NTP server for the time. Generate the timestamp triplet. * * Inputs: * * fd - file descriptor to read. * * Outputs: * * providerTime - return utc time, constructed from the NTP response. * beforeTime - the utc just before the request is sent. * afterTime - the utc just after the reply is received. * * Value Returned: * * -1 Unequivocal failure - Stratum too far from TP. * 0 Success * 1 Timeout failure - retry warranted. * *-- */ int QueryProvider(fd, beforeTime, providerTime, afterTime, idx) int fd; struct utc *providerTime; struct utc *beforeTime; struct utc *afterTime; int idx; { struct timeval timeout; /* time out value for select */ fd_set readfds; /* mask of file descriptors */ int n; /* file descriptor selected on */ int nfds = fd+1; /* number of fds to select on */ struct utc ref, t1, t2, t3, t4; /* NTP timestamps */ struct utc localTime, skew; timespec_t disp; /* NTP dispersion */ timespec_t timesp, inaccsp; /* Temps for time computation */ double dist, drift; struct l_fixedpt org_verify; char timeloc[UTC_MAX_STR_LEN], timerem[UTC_MAX_STR_LEN], skewstr[UTC_MAX_STR_LEN]; memset((char *)pkt, 0, sizeof(ntp_data)); FD_ZERO(&readfds); FD_SET(fd, &readfds); timeout.tv_sec = K_NTP_TIMEOUT; timeout.tv_usec = 0; pkt->version = K_NTP_VERSION3; pkt->leap_status = 0; pkt->mode = K_NTP_CLIENT; pkt->stratum = K_NTP_UNSPECIFIED; pkt->poll = 0; /* * Fill in timestamp */ (void) utc_gettime(beforeTime); cvt_utc_ntp(&pkt->xmt, beforeTime); org_verify = pkt->xmt; /* * Record 'before' timestamp */ (void) utc_gettime(beforeTime); if (ISVERBOSE) printf("\nQueryProvider: Query %s\n",hosts[idx]); if ((sendto(fd, (char *)pkt, sizeof(ntp_data), 0, (struct sockaddr *) &dsts[idx], dstlen)) < 0) { perror("dtssprovider: NTP sendto"); /* I might get a chance to send the request to other servers in the list. So try for them. Just return failure for this host alone */ /* exit(1);*/ if (ISVERBOSE) printf("\nQueryProvider: Try reading time with other servers"); return(0); } /* * Wait for the reply by watching the file descriptor */ #ifdef vax if ((n = select(nfds, (int *)&readfds, (int *)0, (int *)0, &timeout)) < 0){ #else if ((n = select(nfds, &readfds, (fd_set *)0, (fd_set *)0, &timeout)) < 0){ #endif perror("dtssprovider: NTP select"); exit(1); } /* * Record 'after' timestamp */ (void) utc_gettime(afterTime); if (n == 0) { (void) fprintf(stderr, "\n\t* Timeout *\n"); return(0); } if ((recvfrom(fd, (char *)pkt, sizeof (ntp_data), 0, (struct sockaddr *) &socket_in, #ifdef vax (int *) &dstlen)) < 0) { #else (unsigned int *) &dstlen)) < 0) { #endif perror("dtssprovider: NTP recvfrom"); exit(1); } if (ISVERBOSE) { (void) printf("Packet from: [%s]\n", inet_ntoa(socket_in.sin_addr)); if (pkt->stratum < 2) { char *refid_p = (char *) &pkt->refid; char refid_str[8]="abcdefg"; refid_str[0]=refid_p[0]; refid_str[1]=refid_p[1]; refid_str[2]=refid_p[2]; refid_str[3]=refid_p[3]; refid_str[4]='\0'; (void) printf("Version: %d Mode: %d Leap: %d Stratum: %d Ref: %s\n", pkt->version, pkt->mode, pkt->leap_status, &pkt->stratum, refid_str); } else { (void) printf("Version: %d Mode: %d Leap: %d Stratum: %d Ref: %s\n", pkt->version, pkt->mode, pkt->leap_status, pkt->stratum, inet_ntoa(pkt->refid)); } } cvt_ntp_utc(&ref, &pkt->reftime, &pkt->drift); if (ISVERBOSE) { dist = (double)ntohs((u_short) pkt->distance.sint_part) + (double)ntohs((u_short) pkt->distance.sfraction)/65536.; drift = (double)ntohs((u_short) pkt->drift.sint_part) + (double)ntohs((u_short) pkt->drift.sfraction)/65536.; (void) printf("Precision: %d (%fms)\tPoll: %d\n", pkt->precision, 1000.0*LOGTOD(pkt->precision), 1<poll); (void) printf("Distance: %f Drift: %f\n", dist, drift); } /* * Check stratum of timestamp */ if ((org_verify.fraction != pkt->org.fraction)|| (org_verify.int_part != pkt->org.int_part)) {printf("originate stamps differ\n"); return(0);}; if (pkt->stratum >= K_DTS_NTP_STRATUM) {printf("stratum invalid\n"); return(0);}; if (pkt->leap_status == 3) {printf("leap indicates server not synched\n"); return(0);}; /* * Compute ntp time from information in the ntp message */ t1 = *beforeTime; cvt_ntp_utc(&t2, &pkt->rec, &pkt->drift); cvt_ntp_utc(&t3, &pkt->xmt, &pkt->drift); t4 = *afterTime; if (ISVERBOSE) PrintNTPTimes( &t1, &t2, &t3, &t4); utc_boundtime(providerTime, &t2, &t3); utc_ascgmtime(timerem,UTC_MAX_STR_LEN,providerTime); utc_boundtime(&localTime, &t1, &t4); utc_ascgmtime(timeloc,UTC_MAX_STR_LEN,&localTime); utc_subtime(&skew, &localTime, providerTime); utc_ascreltime(skewstr,UTC_MAX_STR_LEN,&skew); printf("%%DTSS-I-NTP, NTP time %s: %s\n", hosts[idx], timerem); printf("-DTSS-I-SKEW, Skew %s: %s\n", timeloc, skewstr); return(1); } /* *++ * cvt_utc_ntp() * * Functional Description: * * Convert a utc timestamp into an NTP timestamp. * * Inputs: * * utc - pointer to utc timestamp. * * Outputs: * * ntp - pointer to ntp timestamp. (See NTP spec for format). * * Value Returned: * * None. * *-- */ void cvt_utc_ntp(struct l_fixedpt *ntp, struct utc *utc ) { timespec_t timesp; (void) utc_bintime(×p, (timespec_t *)0, (long *)0, utc); ntp->int_part = ntohl((u_long) (K_NTP_JAN_1970 + timesp.tv_sec)); ntp->fraction = ntohl((u_long) ((float) timesp.tv_nsec * 4.294967296)); } /* *++ * cvt_ntp_utc() * * Functional Description: * * Convert an NTP timestamp into a utc timestamp. * * Inputs: * * ntp - pointer to ntp timestamp. (See NTP spec for format). * * Outputs: * * utc - pointer to utc timestamp. * * Value Returned: * * None. * *-- */ void cvt_ntp_utc( struct utc *utc, struct l_fixedpt *ntp , struct s_fixedpt *inacc) { timespec_t timesp, inaccsp; timesp.tv_sec = ntohl(ntp->int_part) - K_NTP_JAN_1970; timesp.tv_nsec = ((u_long)ntohl(ntp->fraction)) / 4.294967296; inaccsp.tv_sec = ntohs((u_short)inacc->sint_part); inaccsp.tv_nsec = (double)ntohs((u_short)inacc->sfraction) * (double)K_NS_PER_SEC / 65536.0; (void) utc_mkbintime(utc, ×p, &inaccsp, 0L); } /* ********************************************************** * output Routines. ********************************************************** */ /* *++ * PrintTimes() * * Functional Description: * * Print the Result of a synchronization. * * * Inputs: * * before,after,tptimes - time values used in the synch. * * Implicit Inputs: * * None. * * Outputs: * * None. * * Implicit Outputs: * * None. * * Value Returned: * * None. * * Side Effects: * * None. *-- */ PrintTimes( timeResponse ) TPrspMsg *timeResponse; { int i; char timestr[UTC_MAX_STR_LEN]; if ( timeResponse->status == K_TPI_FAILURE ) { (void) fprintf(stdout, "K_TPI_FAILURE\n"); (void) fprintf(stdout, "********************* %u *********************\n", SYNCHCOUNT); return; } (void) printf("\nTPsuccessful\n"); (void) printf("Serial Number : %d\n", timeResponse->TPsyncID); (void) printf("Time Stamps : %d\n", timeResponse->TPdata.TimeMsg.timeStampCount); for ( i = 0; i < timeResponse->TPdata.TimeMsg.timeStampCount; i++) { (void) printf("Before Time\t:"); utc_ascgmtime(timestr,UTC_MAX_STR_LEN, &timeResponse->TPdata.TimeMsg.timeStampList[i].beforeTime); (void) printf("%s\nTP Time\t\t:",timestr); utc_ascgmtime(timestr,UTC_MAX_STR_LEN, &timeResponse->TPdata.TimeMsg.timeStampList[i].TPtime); (void) printf("%s\nAfter Time\t:",timestr); utc_ascgmtime(timestr,UTC_MAX_STR_LEN, &timeResponse->TPdata.TimeMsg.timeStampList[i].afterTime); (void) printf("%s\n",timestr); } (void)printf("******************************************\n"); } /* *++ * PrintNTPTimes() * * Functional Description: * * Print the Result of a synchronization. * * * Inputs: * * before,after,tptimes - time values used in the synch. * * Implicit Inputs: * * None. * * Outputs: * * None. * * Implicit Outputs: * * None. * * Value Returned: * * None. * * Side Effects: * * None. *-- */ PrintNTPTimes( t1, t2, t3, t4 ) struct utc *t1, *t2, *t3, *t4; { int i; char timestr[UTC_MAX_STR_LEN]; struct utc temp, us, them; utc_ascgmtime(timestr,UTC_MAX_STR_LEN, t1); (void) printf("Before Time:\t%s\n",timestr); utc_ascgmtime(timestr,UTC_MAX_STR_LEN, t2); (void) printf("TP Time 1:\t%s\n",timestr); utc_ascgmtime(timestr,UTC_MAX_STR_LEN, t3); (void) printf("TP Time 2:\t%s\n",timestr); utc_ascgmtime(timestr,UTC_MAX_STR_LEN, t4); (void) printf("After Time:\t%s\n",timestr); utc_subtime(&temp, t4, t1); utc_ascreltime(timestr,UTC_MAX_STR_LEN,&temp); (void) printf("Provider Int:\t%s\n",timestr); utc_subtime(&temp, t3, t2); utc_ascreltime(timestr,UTC_MAX_STR_LEN,&temp); (void) printf("TP Interval:\t%s\n",timestr); utc_boundtime(&us, t1, t4); utc_ascgmtime(timestr,UTC_MAX_STR_LEN,&us); (void) printf("Midpoint-us:\t%s\n",timestr); utc_boundtime(&them, t2, t3); utc_ascgmtime(timestr,UTC_MAX_STR_LEN,&them); (void) printf("Midpoint-them:\t%s\n",timestr); utc_subtime(&temp, &us, &them); utc_ascreltime(timestr,UTC_MAX_STR_LEN,&temp); (void) printf("Skew us-them:\t%s\n",timestr); } /* *++ * PrintValidationError() * * Functional Description: * * The timeprovider is returning invalid times, report * to the user. * * Inputs: * * None. * * Implicit Inputs: * * SYNCHCOUNT,MAXTPERROR. * * Outputs: * * None. * * Implicit Outputs: * * * Value Returned: * * None. * * Side Effects: * * None. * *-- */ PrintValidationError(systemTime,externalTime) struct utc *systemTime; struct utc *externalTime; { char timestr[UTC_MAX_STR_LEN]; fprintf(stderr,"\n!!!!!!!!! DTSS: EXTERNAL TIME SOURCE IS FAULTY !!!!!!!!!\n"); fprintf(stderr,"The external time did not validate\n"); fprintf(stderr,"Synchronization Count: %u\n",SYNCHCOUNT); fprintf(stderr,"Error Tolerance: %u\n",MAXTPERROR); utc_ascgmtime(timestr,UTC_MAX_STR_LEN,systemTime); fprintf(stderr,"System Time: %s\n",timestr); utc_ascgmtime(timestr,UTC_MAX_STR_LEN,externalTime); fprintf(stderr,"External Device Time: %s\n",timestr); fprintf(stderr,"\n!!!!!!!!!!!!!!!!!!\n"); }