/* * Module DTSS$PROVIDER.C * Version V1.0-1 */ /* * 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 dials ACTS * on any modem supported by the "SET HOST/DTE" interface * at 1200 baud, and obtains the current UTC * * ENVIRONMENT: VAX/VMS V5.5 * * AUTHORS: * Distributed Systems Services (DSS) * * DATE: January 5, 1990 * * Modified: * 3-Jan-00, Bob Wheater * Fix case 73629, year field in TPquery routine does not handle year 2000 * correctly. */ /* * To Build: * * $ CC DTSS$PROVIDER_ACTS.C * $ LINK DTSS$PROVIDER_ACTS.OBJ, SYS$INPUT:/OPT - * SYS$SHARE:DTSS$SHR.EXE/SHARE * SYS$LIBRARY:VAXCRTL/SHARE * ^Z * * * To Run Interactively: * * $ run/nodebug [-.exe]dtss$provider_acts.exe * usage: dtss_acts_provider_vms -d device-name * [-m Modem Type, default : DMCL] * [-n Telephone Number, default : T83034944774] * [-r seconds of dialrate, range 10 : 2678400, default 21600] * [-p seconds of pollrate, range 10 : 2678400, default 21600] * [-i milliseconds of inaccuracy, range 30 : 300000,default 100] * [-t timestamps, range 1 : 6, default 4] * [-o output trace, range 0(false) : 1(true), * default 0(false)] * [-e maximum error, ignore timestamps from modem that * differ from the system time by more than maximum error: * default 120 seconds] * [-f first synch, ignore maximum error on the very first * synchronization, * default 1 (true)] * * * *Enter the Device Name: _TTA2: *Enter the Modem type[DMCL]: DMCL *Enter the telephone number[T83034944774]: T13034944774 *Enter the Dial Rate[21600(sec)]: *Enter the Poll Rate[21600(sec)]: *Enter the Inaccuracy[100(ms)]: *Enter the Timestamp Count[4]: *Disallow clock set[0(Y:1,N:0)] : *Output Trace [0(Y:1,N:0)] : * *-----------------------* * * Explaination of inputs: * Device Name - This is the name of the device to which the modem * is attached. * * Modem Type - This is the type of the modem. This name corresponds * to the name of the modem dialer, in this example * SYS$LIBRARY:DTE_DMCL.EXE. If it is possible to * set host/dte to the modem, then the modem type is * supported on you system. The current list of * standard VMS dte libraries is: * DF03 - sys$library:dte_df03.exe * DF112 - sys$library:dte_df112.exe * DF242 - sys$library:dte_df242.exe * DMCL - sys$library:dte_dmcl.exe * The examples in SYS$EXAMPLES show how to implement * a DTE library for specific modems. * * Tel. num - This is the telephone number for the ACTS service. * The telephone number is "303-494-4774". Additional * characters depend upon the protocols set up by your * local telephone service. An optional "T" character * specifies that the phone is touch-tone (DCML). * * Dial Rate - This specifies how often the time provider is allowed * to dial out. The value is given in seconds with a * default value of 21600 seconds. (6hrs) This will * limit the number of phone calls. Decreasing this * value will help synchronize your node's time. * * Poll Rate - This specifies how often the time provider program * makes itself available to the service. Generally, * when a time provider program is active on the network, * the server on which the provider runs does not contact * any other servers. * Given that the dial rate on telephone based time * providers can be very long, this may not be the * best behavior for a telephone time provider. * The poll rate attribute specifies when the local * time server will next attempt to synchronize. * If the dial rate and the poll rate are the same * values, then the local server will only synchronize * with the telephone time provider. * If the poll rate is LESS than the dial rate, * then the local server will synchronize with the * telephone time provider, wait Poll Rate seconds, * and then synchronize with the local & global set. * When dial rate seconds have elapsed, the local * will synchronize with the telephone provider, * repeating the cycle. * * Inaccuracy - This is the systematic inaccuracy contained in * the time stamp returned by the telephone service. * 100 ms is a conservative figure for the ACTS service. * * Time Stamp Cnt - This specifies the number of time stamps which are * read from the time service. * * Output Trace - This parameter specifies whether or not debugging * is generated by the time provider. * * To Run Detached, use the command line interface: * * $ run/det -d _TTA2 -t 13034944774/priority=10/process_name=DTSS$PROVIDER */ /* * DESCRIPTION OF THE * AUTOMATED COMPUTER TELEPHONE SERVICE (ACTS) * * * The following is transmitted (at 1200 baud) after completion of the * telephone connection. * * ? = HELP * National Bureau of Standards * Telephone Time Service * * D L D * MJD YR MO DA H M S ST S UT1 msADV OTM * 47222 88-03-02 21:39:15 83 0 +.3 045.0 UTC(NIST) * * 47222 88-03-02 21:39:16 83 0 +.3 045.0 UTC(NIST) * * 47222 88-03-02 21:39:17 83 0 +.3 045.0 UTC(NIST) * * 47222 88-03-02 21:39:18 83 0 +.3 045.0 UTC(NIST) * * 47222 88-03-02 21:39:19 83 0 +.3 037.6 UTC(NIST) # * 47222 88-03-02 21:39:20 83 0 +.3 037.6 UTC(NIST) # * etc..etc...etc....... * * UTC = Universal Time Coordinated, the official world time referred to the * zero meridian. * _________________________________________________________________________ * * DST = Daylight savings time characters, valid for the continental U.S., are * set as follows: * 00 = We are on standard time (ST). 50 = We are on DST. * 99 to 51 = Now on ST, go to DST when your local time is 2:00 am and the * count is 51. The count is decremented daily at 00 (UTC). * 49 to 01 = Now on DST, go to ST when your local time is 2:00 am and the * count is 01. The count is decremented daily at 00 (UTC). * The two DST characters provide up to 48 days advance notice of a change in * time. The count remains at 00 or 50 at other times. * _________________________________________________________________________ * * LS = Leap second flag is set to "1" to indicate that a leap second is to be * added as 23:59:60 (UTC) on the last day of the current UTC month. The LS * flag will be reset to "0" starting with 23:59:60 (UTC). The flag will * remain on for the entire month before the second is added. Leap seconds * are added as needed at the end of any month. Usually June and/or December * are chosen. * * The leap second flag will be set to a "2" to indicate that a leap second * is to be deleted at 23:59:58--00:00:00 on the last day of the current * month. (This latter provision is included per international recommendation * however it is not likely to be required in the near future.) * __________________________________________________________________________ * * DUT1 = Approximate difference between earth rotation time (UT1) and UTC, in * steps of 0.1 second. DUT1 = UT1 - UTC * ___________________________________________________________________________ * * MJD = Modified Julian Date, often used to tag certain scientific data. * ___________________________________________________________________________ * * The full time format is sent at 1200 baud, 8 bit, 1 stop, no parity. * The HH:MM:SS msADV time format at 300 baud is also 8 bit, 1 stop, no parity. * ___________________________________________________________________________ * * Maximum on line time will be 55 seconds. If all lines are busy at any time, * the oldest call will be terminated if it has been on line more than 15 * seconds, else, the call that first reaches 15 seconds will be terminated. * ___________________________________________________________________________ * * Current time is valid at the "on-time" marker (OTM), either "*" or "#". * The nominal on-time marker (*) will be transmitted 45 ms early to account * for the 8 ms required to send 1 character at 1200 baud, plus an additional * 7 ms for delay from NBS to the user, and approximately 30 ms "scrambler" * delay inherent in 1200 baud modems. If the caller echoes all characters, * NBS will measure the round trip delay and advance the on-time marker so * that the midpoint of the stop bit arrives at the user on time. The amount * of msADV will reflect the actual required advance in milliseconds and the * OTM will be a "#". The NBS system requires 4 consecutive delay * measurements which are consistent before switching from "*" to "#". * If the user has a 1200 baud modem with the same internal delay as that used * by NBS, then the "#" OTM should arrive at the user within +-2 ms of the * correct time. However, NBS has studied different brands of 1200 baud * modems and found internal delays from 24 ms to 40 ms and offsets of the * "#" OTM of +-10 ms. For many computer users, +-10 ms accuracy should be * more than adequate since many computer internal clocks can only be set with * granularity of 20 to 50 ms. In any case, the repeatability of the offset * for the "#" OTM should be within +-2 ms, if the dial-up path is reciprocal * and the user doesn't change the brand or model of modem used. This should * be true even if the dial-up path on one day is a land-line of less than * 40 ms (one way) and on the next day is a satellite link of 260 to 300 ms. * In the rare event that the path is one way by satellite and the other way * by land line with a round trip measurement in the range of 90 to 260 ms, * the OTM will remain a "*" indicating 45 ms advance. For the user who wants * the best possible accuracy at the OTM, NBS offers an alternate 300 baud * service with only HH:MM:SS MSADV and OTM. To use the alternate service, * simply call at 300 baud. Because of the simple FSK modulation scheme used * at 300 baud, all modems tested had the same delay within about 1 ms. * ___________________________________________________________________________ * * For user comments write: * NBS-ACTS * Time and Frequency Division * Mail Stop 524 * 325 Broadway * Boulder, CO 80303 * * Software for setting (PC)DOS compatible machines is available * on a 360-kbyte diskette for $35.00 from: * NBS Office of Standard Reference Materials * B311-Chemistry Bldg, NBS, Gaithersburg, MD, 20899, (301) 975-6776 * * -------------------------------------------------------------------------- */ /* Include Files: */ #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 /* terminal characteristic defs */ #include /* xterminal characteristic defs */ #include /* device class definitions */ #include /* tm structure definition */ #include "utc.h" /* utc library routines */ #include "dtssprovider.h" /* TPI declarations */ /* * MACROS */ #define ExitA(status) \ { \ char msg[255+1]; \ \ (void) sprintf(msg, \ "\nFatal error %d at line %d in file %s\n", \ status, \ __LINE__, \ __FILE__); \ (void) fprintf(stderr, "%s", msg); \ (void) abort(); \ } /* * Tracing Macro, Execute the code only if * the TRACING flag is set. */ #define TraceIf( Code ) if ( TRACING ) { Code } /* * Get the next argument * from the command line interface. */ #define GETNEXTARG(argcount) if (++i >= argcount) { PrintHelp (); ExitA(1); } /* * 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_MODEM_TIMEOUT (5) /* allow modem 5 seconds to hangup phone */ #define K_DIAL_RESP_TMO (20) /* allow 20 seconds for modem to place * * phone call. */ #define K_DIAL_ATT_TMO (60) /* allow 60 seconds for modem to attach * * to phone call. */ #define K_ACTS_1200_SIZE (51) /* size of the ACTS data string */ /* * Maximum/Minimum number of time stamps returned by TP */ #define K_MIN_INACCURACY (30) /* Allow for at least 30 ms of * * inaccuracy in each ACTS time * * response. */ #define K_MAX_INACCURACY (5 * 60 * K_MS_PER_SEC) /* Allow for at most 5 minutes * * of inacc in each ACTS resp. */ /* defined in DTSSPROVIDER.H #define K_MAX_TIMESTAMPS (6) * Read the clock at most 6 * * times each dialup. * #define K_MIN_TIMESTAMPS (1) * Read the clock at least 1 * * time each dialup. * */ #define K_MIN_POLLRATE (K_MODEM_TIMEOUT * 2) /* Minimum dialup frequency. */ #define K_MAX_POLLRATE (31*24*60*60) /* Maximum dialup frequency of * * 31 days. */ #define K_MIN_DIALRATE (K_MODEM_TIMEOUT * 2) /* Minimum dialup frequency. */ #define K_MAX_DIALRATE (31*24*60*60) /* Maximum dialup frequency of * * 31 days. */ #define K_MAX_MODEM_NUMBER (26) /* largest dial number a modem * * can use. */ #define K_MAX_MODEM_TYPE (6) /* largest modem type string. */ #define K_MAX_DEV_NAME (255) /* largest device name string. */ #define K_DEF_FIRSTSYNCH (1) /* default firstsynch to true */ #define K_DEF_MAXTPERROR (60) /* max tp error of 60 seconds */ #define K_MIN_MAXTPERROR (1) /* minimum tp error */ /* * Default Control/Config Values */ #define K_DEF_PHONE_NUM "T83034944774" /* default phone number - * * Using DTN rules. */ #define K_DEF_MODEM_TYPE "DMCL" /* DF242 dialer type */ #define K_DEF_INACCURACY (100) /* 100 ms inacc. in each * * ACTS Time Response */ #define K_DEF_TMORATE (2 * 60) /* Let service wait 2 minutes* * for diaup connect to * * succeed. */ #define K_DEF_POLLRATE (6*60*60) /* dial out each 6 hrs * * to get a new * * time. */ #define K_DEF_DIALRATE (6*60*60) /* dial out each 6 hrs * * to get a new * * time. */ #define K_DEF_TIMESTAMPS (3) /* Read the ACTS clock * * 4 times each dialup. */ #define K_DEF_NOCLOCKSET (0) /* Allow the clock to * * be set. */ #define K_DEF_TRACING (0) /* Time Provider tracing */ /* * Typedef declarations */ typedef union TermCharBuff { /* terminal characteristics buffer */ union ttdef tt; union tt2def tt2; } TermCharBuff; typedef struct IOsb { /* IO status Block */ unsigned short status; unsigned short byteCount; unsigned long devStatus; } IOsb; typedef struct TermSet { /* termination chars for ACTS data */ int mask1; int mask2; } TermSet; /* * Forward Definitions/Prototypes */ InitTP( unsigned short *modemChan ); ExitTP( unsigned short *modemChan); void InitIPC( unsigned short *tstpChan, unsigned short *tptsChan); ExitIPC( unsigned short tptsChan ); QueryTP( unsigned short *modemChan, struct utc *beforeTime, struct utc *TPtime, struct utc *afterTime ); SendReply( unsigned short tptsChan, TPrspMsg *timeResponse ); ReadRequest( unsigned short tptsChan, TPreqMsg *timeRequest ); DialModem ( unsigned short *modemChan, char *number , char *type); WriteModem( unsigned short *modemChan, char *buffer, int bufferLen ); ReadModem ( unsigned short *modemChan, char *buffer, int transferSize, TermSet *termSet, int tmo, int *bytesRead, int purge); ReadTimedModem ( unsigned short *modemChan, char *buffer, int transferSize, TermSet *termSet, int tmo, int *bytesRead, struct utc *beforeTime, struct utc *afterTime ); InitModemPort ( unsigned short *modemChan ); void PrintTimes( TPrspMsg *timeResponse ); void PrintHelp(); void InterActiveInput(); void CommandLineInput( int fargc, char **fargv ); int ValidateTime(struct utc *systemTime, struct utc *externalTime); /* * Global Variables */ char devicename[K_MAX_DEV_NAME+1]; /* name of TP device */ $DESCRIPTOR( TPDEVICE, devicename ); /* device descriptor */ $DESCRIPTOR( TSTPLOG, "DTSS$_TSTP_MBX"); /* tpts mailbox */ $DESCRIPTOR( TPTSLOG, "DTSS$_TPTS_MBX"); /* tstp mailbox */ unsigned long TIMESTAMP = K_DEF_TIMESTAMPS; /* time stamps at each sync */ unsigned long TMORATE = K_DEF_TMORATE; /* dialup connect timeout */ unsigned long POLLRATE = K_DEF_POLLRATE; /* seconds between TP syncs */ unsigned long DIALRATE = K_DEF_DIALRATE; /* seconds between TP calls */ unsigned long NOCLOCKSET = K_DEF_NOCLOCKSET; /* can we set the clock? */ unsigned long TRACING = K_DEF_TRACING; /* are we tracing output */ unsigned long INACCURACY = K_DEF_INACCURACY; /* user input Inaccuracy */ unsigned long MAXTPERROR = K_DEF_MAXTPERROR; /* error tollerance */ unsigned long FIRSTSYNCH = K_DEF_FIRSTSYNCH; /* ignore error toll on first synchronization */ unsigned long SYNCHCOUNT; /* number of synchs completed */ unsigned long INACC; /* inacc in 100NS of a second */ char ACTSnum[K_MAX_MODEM_NUMBER+1]; /* ACTS dialup number */ char modemType[K_MAX_MODEM_TYPE+1]; /* Modem type string */ char termBuf; /* buff for reading the term char */ TermSet termSet = { 0, -1 }; /* termination chars - all CTL */ TermSet termLF = { 0, 0x00000400 }; /* termination char - LF */ TermSet termCR = { 0, 0x00002000 }; /* termination char - CR */ struct tm INACC_TM; /* inaccuracy of each acts reading */ /* *++ * main() * * Functional Description: * * Main Loop for the TP. * * * Inputs: * * None * * Implicit Inputs: * * None * * Outputs: * * None * * Implicit Outputs: * * None * * Value Returned: * * None * * Side Effects: * * None * *-- */ main( argc, argv ) int argc; char *argv[]; { TPreqMsg timeServiceRequest; TPrspMsg TMOresponse; TPrspMsg timeResponse; unsigned long status; unsigned short modemChan; unsigned short tptsChan; unsigned short tstpChan; int i; unsigned int dialDiffTime[2]; time_t dialTime; /* * Set the TPdevice to 'notset' */ TPDEVICE.dsc$w_length = 0; /* ** Copy the default phone number */ strcpy(ACTSnum, K_DEF_PHONE_NUM); /* ** Copy the default modem type */ strcpy(modemType, K_DEF_MODEM_TYPE); /* ** keep count of synchs attempted. */ SYNCHCOUNT = 0; /* ** Get the arguments */ if (argc < 2) { PrintHelp (); InterActiveInput(); } else CommandLineInput( argc, argv ); /* ** Echo back the input parameters. */ fprintf(stderr,"PARAMETERS SET:\n"); fprintf(stderr,"TP_DEVICE: %s\n",devicename); fprintf(stderr,"TP_NUMBER: %s\n",ACTSnum); fprintf(stderr,"TP_INACCURACY: %d seconds\n", INACCURACY); fprintf(stderr,"TP_POLL_RATE: %d seconds\n", POLLRATE); fprintf(stderr,"TP_DIAL_RATE: %d seconds\n", DIALRATE); 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_TRACING: %s\n\n\n", TRACING?"TRUE":"FALSE"); /* ** Convert the inaccuracy into ** a TM struct for use with API */ INACC_TM.tm_year = INACC_TM.tm_mon = INACC_TM.tm_mday = 0; INACC_TM.tm_min = INACCURACY / (60 * K_MS_PER_SEC ); INACCURACY -= ( INACC_TM.tm_min * 60 * K_MS_PER_SEC ); INACC_TM.tm_sec = INACCURACY / K_MS_PER_SEC; INACCURACY -= ( INACC_TM.tm_sec * K_MS_PER_SEC ); INACC = INACCURACY * K_NS_PER_MS; /* ** Compute the difference between the pollrate and the dialrate ** as a VMS relative time. */ if (DIALRATE - POLLRATE > 0) { int days; int hours; int minutes; int seconds; int temp; char timStr[25]; $DESCRIPTOR(timBuf,timStr); temp = DIALRATE; days = temp / (24 * 60 * 60); temp = temp - days * (24 * 60 * 60); hours = temp / ( 60 * 60 ); temp = temp - hours * (60 * 60); minutes = temp / 60; seconds = temp - (minutes * 60); sprintf(timStr, "%02d %02d:%02d:%02d", days,hours,minutes,seconds); timBuf.dsc$w_length = strlen(timStr); if (!((status = SYS$BINTIM(&timBuf,dialDiffTime))&1)) ExitA(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.TPmsgType = K_TPI_CTL_MESSAGE; TMOresponse.TPdata.TPctlMsg.nextPoll = POLLRATE; TMOresponse.TPdata.TPctlMsg.timeout = TMORATE; TMOresponse.TPdata.TPctlMsg.noClockSet = NOCLOCKSET; /* ** 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. */ /* ** Initialize the interprocess communication. ** Die if this fails. */ InitIPC( &tstpChan, &tptsChan ); for (;;) { /* ** increment the synchcount, ** make sure it doesn't loop around to 1 again. */ SYNCHCOUNT++; SYNCHCOUNT = (SYNCHCOUNT==0)?2:SYNCHCOUNT; /* ** Wait on a synchronous read for the ** service to request a time. */ TraceIf((void) printf( "IPC WAIT\n");); status = ReadRequest( tstpChan, &timeServiceRequest ); if ( !(status&1)) ExitA(status); TraceIf((void) printf("IPC WAKE\n");); /* ** 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.status = K_TPI_SUCCESS; TMOresponse.TPsyncID = timeServiceRequest.TPsyncID; status = SendReply( tptsChan, &TMOresponse ); if (!(status & 1)) ExitA( status ); /* ** Initialize the message fields */ timeResponse.version_major = K_TPI_VERSION_MAJOR; timeResponse.version_minor = K_TPI_VERSION_MINOR; timeResponse.TPsyncID = timeServiceRequest.TPsyncID; timeResponse.TPdata.TimeMsg.timeStampCount = 0; timeResponse.TPmsgType = K_TPI_TIME_MESSAGE; /* ** Connect to and initialize the time provider ** device. */ status = InitTP(&modemChan); if ( status & 1 ) { TraceIf((void) printf( "MODEM CONNECTION COMPLETE\n");); timeResponse.status = K_TPI_SUCCESS; for ( i = 0; i < TIMESTAMP; i++ ) { status = QueryTP( &modemChan, &timeResponse.TPdata.TimeMsg.timeStampList[i].beforeTime, &timeResponse.TPdata.TimeMsg.timeStampList[i].TPtime, &timeResponse.TPdata.TimeMsg.timeStampList[i].afterTime ); if ( ! (status & 1) ) { TraceIf((void) printf("Query Failure %d\n", status);); break; } if (status & 1) { status = ValidateTime(&timeResponse.TPdata.TimeMsg.timeStampList[i].afterTime, &timeResponse.TPdata.TimeMsg.timeStampList[i].TPtime); if (!(status & 1)) { PrintValidationError(&timeResponse.TPdata.TimeMsg.timeStampList[i].afterTime, &timeResponse.TPdata.TimeMsg.timeStampList[i].TPtime); break; } } timeResponse.TPdata.TimeMsg.timeStampCount++; } } else { /* If we failed to do anything before we got the first timestamp, ** the modem probably went out to lunch. Allow the next poll to ** retry. */ } TraceIf((void) printf("Query Complete\n");); /* ** There must be at least TIMESTAMP messages ** to be successful! */ if ( timeResponse.TPdata.TimeMsg.timeStampCount != TIMESTAMP ) { timeResponse.status = K_TPI_FAILURE; } /* ** Send the reply that contains the times */ status = SendReply( tptsChan, &timeResponse ); if (!(status & 1)) ExitA( status ); TraceIf(printf("Reply written : \n"); PrintTimes( &timeResponse );); /* ** Call for the Time Provider Hardware to do any necessary ** cleanup by calling ExitTP. */ ExitTP( &modemChan); if ((DIALRATE - POLLRATE > 0) && (timeResponse.status == K_TPI_SUCCESS)); { /* ** Put the process to sleep after deleting the TSTP mailbox. ** The server will not attemp to contact the time provider ** until the IPC mailbox is restored. */ if (!((status = SYS$CLREF( 0 )) & 1)) ExitA(status); if (!((status = SYS$SETIMR( 0, dialDiffTime, 0, 0, 0))&1)) ExitA(status); if (!((status = ExitIPC(tstpChan))&1)) ExitA(status); if (!((status = ExitIPC(tptsChan))&1)) ExitA(status); if (!((status = SYS$WAITFR(0))&1)) ExitA(status); InitIPC(&tstpChan,&tptsChan); } } } /* 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: * * None. * * Outputs: * * SYNCHCOUNT - current synch number. * FIRSTSYNCH - allow first synch to always validate. * MAXTPERROR - greatest difference between two times. * * Implicit Outputs: * * None. * * Value Returned: * * None. * * Side Effects: * * None. * *-- */ int ValidateTime(systemTime,externalTime) struct utc *systemTime; struct utc *externalTime; { utc_t 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) ); } /* ********************************************************** * VMS IPC Specific Routines. ********************************************************** */ /* *++ * InitTP() * * Functional Description: * * Initialize the Time Provider Port and Modem. * Assign a channel to the terminal port and Dial the Modem. * Prepare the Input stream for further reading * * Inputs: * * None. * * Implicit Inputs: * * modemChan - the address of a channel descriptor for the * the modem port. * * Outputs: * * None. * * Implicit Outputs: * * None. * * Value Returned: * * SS$_NORMAL - modem is initialized and ready to be read * SS$_ABORT - modem could not be initialized. * All Values returned from SYS$ASSIGN * All Values returned from SYS$QIO * * * Side Effects: * * None. *-- */ InitTP( modemChan) unsigned short *modemChan; { unsigned long status; char *charBufStr; char flushChar; IOsb iosb; char header[K_ACTS_1200_SIZE*2+1]; int bytesRead; int i; /* * Assign a channel to the terminal port device connected to the * modem. */ status = SYS$ASSIGN(&TPDEVICE, /* device name descriptor */ modemChan, /* channel number */ 0, /* access mode */ 0); /* mailbox */ if ( !(status & 1) ) ExitA( status ); /* * Set the terminal Characteristics * and clear the modem port */ status = InitModemPort( modemChan ); if (!(status&1)) ExitA(status); /* * Dial the ACTS number */ status = DialModem( modemChan, ACTSnum , modemType); if (!(status&1)) return(status); /* * Read Away the three NBS header lines */ for( i = 1; i<10; i++ ) { if (!((status = ReadModem( modemChan, header, K_ACTS_1200_SIZE * 2, &termLF, 20,&bytesRead,TRUE)) & 1)) ExitA( status ); if ( header[bytesRead-2] == '*' || header[bytesRead-2] == '#' ) return( SS$_NORMAL ); } return ( SS$_ABORT ); } /* End of routine initTP */ /* *++ * ExitTP() * * Functional Description: * * Exit a terminal session with the modem device. * * Inputs: * * modemChan - the address of the modem channel index. * * Implicit Inputs: * * None. * * Outputs: * * None. * * Implicit Outputs: * * None. * * Value Returned: * * All Values returned by SYS$DASSGN * * Side Effects: * * None. *-- */ ExitTP( modemChan ) unsigned short *modemChan; { unsigned long status; IOsb iosb; /* * Reset the terminal port characteristics. * Make sure we hang up the phone. */ status = SYS$DASSGN( *modemChan ); sleep( K_MODEM_TIMEOUT ); return status; } /* End of routine ExitTP */ /* *++ * ExitIPC() * * Functional Description: * * Deassign the tpts mailbox channel. * * Inputs: * * tptsChan - the address of the tpts channel index. * * Implicit Inputs: * * None. * * Outputs: * * None. * * Implicit Outputs: * * None. * * Value Returned: * * All Values returned by SYS$DASSGN * * Side Effects: * None. *-- */ ExitIPC( unsigned short tptsChan ) { unsigned long status; /* * Deassign all channels */ status = SYS$DASSGN( tptsChan ); return( status ); } /* *++ * void 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. * *-- */ void InitIPC( tstpChan, tptsChan ) unsigned short *tstpChan; unsigned short *tptsChan; { 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) ) ExitA(status); if ( !(( status = SYS$DELMBX( *tstpChan )) & 1) ) ExitA(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 */ if (!(status & 1)) ExitA(status); } /* End of routine InitIPC */ /* *++ * SendReply() * * Functional Description: * * This routine writes a response message to the Time Service Process's * response Mailbox. * * Inputs: * * timeResponse - the address of the response message which * is to be written to the TS. * * Implicit Inputs: * * The Channel to the TS response mailbox is connected. * * Outputs: * * None. * * Implicit Outputs: * * None. * * Value Returned: * * All Values Returned by SYS$QIOW * * Side Effects: * * None. * *-- */ SendReply( unsigned short tptsChan, TPrspMsg *timeResponse ) { IOsb iosb; unsigned long status; status = SYS$QIOW(0, /* efn */ tptsChan, /* chan */ IO$_WRITEVBLK | /* func code */ 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(iosb.status); return( status ); } /* 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: * * All Values Returned by SYS$QIOW * SS$_NODATA - there was no data read on the mailbox, * there was a message size mismatch, or the * message TPI version number does not match * the expected value. * SS$_NORMAL - Success * * Side Effects: * * None. * *-- */ ReadRequest( unsigned short tptsChan, TPreqMsg *timeRequest ) { IOsb iosb; unsigned long status; status = SYS$QIOW(0, /* efn */ tptsChan, /* channel for read */ IO$_READVBLK, /* func code */ &iosb, /* IO status blk */ 0, /* AST address */ 0, /* AST prm */ timeRequest, /* P1 - read buffer */ sizeof(TPreqMsg), /* P2 - read size in bytes */ 0, /* P3 */ 0, /* P4 */ 0, /* P5 */ 0); /* P6 */ if ( ! (status & 1 ) ) return ( status ); if ( ! (iosb.status & 1) ) return ( 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 ); } /* End of routine ReadRequest */ /* ********************************************************** * NIST Specific Routines. ********************************************************** */ /* *++ * QueryTP() * * Functional Description: * * * Inputs: * * None. * * Implicit Inputs: * * ttchan - The active channel to Modem's device. * * Outputs: * * TPtime - The Time returned by ACTS. * beforetime - The time just before the TPtime was read. * aftertime - The time just after the TPtime was read. * * Implicit Outputs: * * None. * * Value Returned: * * All values returned by SYS$QIOW * SS$_NORMAL - Success * SS$_IVTIME - The time read from acts is not in the * expected format. * * Side Effects: * * None. * *-- */ int QueryTP( modemChan, beforeTime, TPtime, afterTime ) unsigned short *modemChan; struct utc *TPtime; struct utc *beforeTime; struct utc *afterTime; { char actsBuff[K_ACTS_1200_SIZE+1]; unsigned long status; int mjd,year,mon,day,hour,minute,second,st,s; char colon,dash,otm[K_ACTS_1200_SIZE],nbs[K_ACTS_1200_SIZE]; double ut1,msAdv; int count; int bytesRead; struct tm actsTM; status = ReadTimedModem( modemChan, actsBuff, K_ACTS_1200_SIZE, &termLF, 10, &bytesRead, beforeTime, afterTime); if ( !(status&1)) { TraceIf((void) printf("Read Time Failure\n");); return(status); } actsBuff[bytesRead] = '\0'; /* * Make sure this is a numerical time */ if ( ((count = sscanf( actsBuff, "%d%d%c%d%c%d%d%c%d%c%d%d%d%f%f%s%s", &mjd,&actsTM.tm_year,&dash,&actsTM.tm_mon,&dash, &actsTM.tm_mday,&actsTM.tm_hour, &colon,&actsTM.tm_min,&colon,&actsTM.tm_sec, &st,&s,&ut1,&msAdv, nbs,otm )) != 17) || (otm[0] != '*' && otm[0] != '#') || colon != ':' || dash != '-') { TraceIf((void) printf("Time Format Failure: %s \n", actsBuff);); return(SS$_IVTIME); } /* * fix year 2000 wraparound */ if ( actsTM.tm_year < 90 ) actsTM.tm_year += 100; /* * Write back the OTM */ if (!((status = WriteModem( modemChan, otm, 1 ))&1)) ExitA(status); /* * Convert the data returned by ACTS into a struct utc. * Call the DTSS API */ actsTM.tm_mon -= 1; if (utc_mkgmtime( TPtime, &actsTM, 0, &INACC_TM, INACC) != 0) ExitA(SS$_IVTIME); return( SS$_NORMAL ); } /* ********************************************************** * Modem routines - Terminal port rtns ********************************************************** */ /* *++ * DialModem() * * Functional Description: * * Place a phone call to the specfied number and wait for the connection * to be established no more than a finite amount of time. * * Inputs: * * modemChan - an unsigned word, the channel to the device to which * the modem is connected. * number - The telephone number which is dialed. * * Implicit Inputs: * * * Outputs: * * * Implicit Outputs: * * * Value Returned: * * All values returned by SYS$QIOW * SS$_NORMAL - Success * SS$_ABORT - The phone call was not accepted or could not * be placed. * * * Side Effects: * * None. * *-- */ DialModem( modemChan, number , type) unsigned short *modemChan; char *number; char *type; { char dialNumber[K_MAX_MODEM_NUMBER + 1]; /* MODEM max dial */ $DESCRIPTOR(dialDesc,dialNumber); /* Descriptor for dial_routine */ char image[16] = "DTE_"; $DESCRIPTOR(imageDesc,image); $DESCRIPTOR(symbol,"DIAL_ROUTINE"); int (*dial_routine)(); $DESCRIPTOR(nullDevice,"NLA0:"); unsigned short nullChan; unsigned int status; /* * Build descriptor of the number */ strcpy( &dialNumber[0], number ); dialDesc.dsc$w_length = strlen(dialNumber); /* * Build descriptor of the image */ strcpy( &image[4], type); imageDesc.dsc$w_length = strlen(image); status = lib$find_image_symbol ( &imageDesc, /* image name descriptor */ &symbol, /* symbol name descriptor */ &dial_routine); /* returned address */ if ( !(status & 1)) return(status); /* * Assign a channel to the null device */ status = SYS$ASSIGN(&nullDevice, /* device name descriptor */ &nullChan, /* channel number */ 0, /* access mode */ 0); /* mailbox */ if ( !(status & 1)) return(status); /* * Dial the number */ status = (dial_routine)(&dialDesc, *modemChan, nullChan); if ( !(status & 1)) { TraceIf((void) printf("Dial Failure\n");); return(status); } return(SS$_NORMAL); /* connect success */ } /* *++ * WriteModem() * * Functional Description: * * Write data out to the modem over the specified device channel. * * Inputs: * * modemChan - unsigned word, the channel to the modem's device. * writeBuff - the address of a string to write. * bufLen - the number of bytes to write. * * Implicit Inputs: * * * Outputs: * * * Implicit Outputs: * * * Value Returned: * * All values returned by SYS$QIOW * * Side Effects: * * None. * *-- */ WriteModem ( modemChan, writeBuf, bufLen ) char *writeBuf; unsigned short *modemChan; int bufLen; { unsigned long status; IOsb iosb; /* * Write the data to the modem. */ status = SYS$QIOW(0, *modemChan, IO$_WRITEVBLK | IO$M_NOFORMAT, &iosb, 0, 0, writeBuf, /* P1 */ bufLen, /* P2 */ 0, 0, 0, 0); /* * Check status returns */ if ( !(status & 1)) return( status ); if ( !(iosb.status & 1)) return( status ); return( SS$_NORMAL ); } /* *++ * InitModemPort() * * Functional Description: * * Set the terminal port characteristic to values that will support * a data transter with ACTS using any modem. * * Inputs: * * modemChan - the channel connected to the modem device. * * Implicit Inputs: * * * Outputs: * * * Implicit Outputs: * * * Value Returned: * * All Values returned by SYS$QIO * SS$_IVDEVNAME - the device is not a terminal. * SS$_ABORT - the modem can not be read using the terminal * SS$_NORMAL - Success * * Side Effects: * * The terminal port characteristics are permanently set. * *-- */ InitModemPort( modemChan ) unsigned short *modemChan; { struct SetCharBuf{ char class; char type; short pagewidth; int termChar; int eTermChar; } setCharBuf; IOsb iosb; unsigned long status; char charBufStr[12]; int temp,temp2; char flushChar; int bytesRead; /* * Get the terminal port characteristics. */ status = SYS$QIOW( 0, /* efn */ *modemChan, /* channel */ IO$_SENSEMODE, /* func code */ &iosb, /* I/O status block */ 0,0, &setCharBuf, /* P1 - characteristics buffer */ 12, /* P2 - 12 */ 0,0,0,0); /* P3..P6 */ if ( ! (status & 1 )) ExitA(status); if ( ! (iosb.status & 1 )) ExitA(iosb.status); if ( setCharBuf.class != DC$_TERM ) ExitA( SS$_IVDEVNAM ); /* * Make sure the appropriate terminal characteristics * are set correctly */ setCharBuf.termChar |= ( TT$M_EIGHTBIT | TT$M_MODEM | TT$M_NOECHO | TT$M_LOWER | TT$M_NOBRDCST | TT$M_PASSALL ); setCharBuf.termChar &=~( TT$M_HOSTSYNC | TT$M_READSYNC | TT$M_TTSYNC ); setCharBuf.eTermChar |= (TT2$M_PASTHRU | TT2$M_HANGUP ); setCharBuf.eTermChar &= ~(TT2$M_AUTOBAUD | TT2$M_LOCALECHO); status = SYS$QIOW (0, /* efn */ *modemChan, IO$_SETMODE, /* func */ &iosb, /* I/O status block */ 0, 0, &setCharBuf, /* P1 - set characteristics */ 12, /* P2 - size of user buff (12) */ TT$C_BAUD_1200, /* P3 - start the modem at 1200 */ 0, /* P4 */ 0, /* P5 */ 0); /* P6 */ if ( ! (status & 1 )) ExitA(status); if ( ! (iosb.status & 1)) ExitA(iosb.status); /* * Flush anything that may have been left over in the port's * typeahead buffer. */ for(;;) { status = ReadModem ( modemChan, &flushChar, 1, 0, 0, &bytesRead,TRUE); if ( status == SS$_NODATA || status == SS$_TIMEOUT ) break; if (!(status & 1)) return( SS$_ABORT ); } return( SS$_NORMAL ); } /* *++ * ReadModem() * * Functional Description: * * Read data from the modem port optionally terminated by timer * or termination character. Optionally purge the type-ahead * buffer. * * Inputs: * modemChan - The channel over which data is to be read. * transfersiz- The desired number of bytes to be read. * termSet - The termination character set. If NULL then * no termchars are used. * tmo - Timeout on the read. * Purgetypeahead- * True if a type ahead purge is to be done. * * * Implicit Inputs: * * * Outputs: * readBuff - The address of a buffer this will be filled * by the read operation. * bytesread- The number of bytes that were actually read. * * * Implicit Outputs: * * * Value Returned: * * All Values returned by SYS$QIOW * * Side Effects: * * None. * *-- */ ReadModem ( modemChan, readBuf, transferSize, termSet, tmo, bytesRead, purgeTypeAhead) unsigned short *modemChan; char *readBuf; int transferSize; TermSet *termSet; int tmo; int *bytesRead; int purgeTypeAhead; { IOsb iosb; unsigned long status; unsigned long status1; /* * Attempt to read a full record from the input stream. * purge away all previous records reading the remainder of * the current record from the input stream. * Issue a second QIO without purge to read the next full record * from the input stream. */ if ( purgeTypeAhead ) { status = SYS$QIOW(0, *modemChan, IO$_READVBLK | IO$M_TIMED | IO$M_PURGE, &iosb, 0, 0, readBuf, transferSize, tmo, termSet, 0, 0); if ( !(status & 1) ) return(status); if ( !(iosb.status & 1) ) return(iosb.status); } /* * Read the next N characters from the input * stream. */ status = SYS$QIOW( 0, *modemChan, IO$_READVBLK | IO$M_TIMED, &iosb, 0, 0, readBuf, transferSize, tmo, termSet, 0,0); /* P4-P6 */ if ( !(status & 1) ) return(status); if ( !(iosb.status & 1) ) return(iosb.status); *bytesRead = iosb.byteCount; /* * read away the term char */ if ( termSet != 0 ) { status = SYS$QIO(0, *modemChan, IO$_READVBLK, 0, 0, 0, &termBuf, 1, 0, termSet, 0, 0); } return ( SS$_NORMAL ); } /* *++ * ReadTimedModem() * * Functional Description: * * Read a timestamp from ACTS. * * Inputs: * modemChan - The channel over which data is to be read. * readBuff - The address of a buffer this will be filled * by the read operation. * transfersiz- The desired number of bytes to be read. * termSet - The termination character set. If NULL then * no termchars are used. * tmo - Timeout on the read. * * * * Implicit Inputs: * * * Outputs: * * beforetime- the utc time just before the ACTS on-time marker * arrived. * aftertime - the utc time just after the ACTS on-time marker * arrived. * bytesread - the number of bytes read. * * Implicit Outputs: * * * Value Returned: * * All Values returned by SYS$QIOW * SS$_ABORT - Could not find the ACTS On time marker and thus could * not read a time buffer. * SS$_NORMAL- Success * * Side Effects: * * None. * *-- */ ReadTimedModem ( modemChan, readBuf, transferSize, termSet, tmo, bytesRead, beforeTime, afterTime) unsigned short *modemChan; char *readBuf; int transferSize; TermSet *termSet; int tmo; int *bytesRead; struct utc *beforeTime, *afterTime; { IOsb iosb; unsigned long status; struct utc tempBeforeTime[2]; int tempBeforeIndex = 0; char onTime; int i; long delta[2] = { -1, -1 }; /* VMS Delta time of 100 NS */ if ( utc_gettime( &tempBeforeTime[0] ) || utc_gettime( &tempBeforeTime[1] )) ExitA( status ); /* * Purge away the typeahead buffer */ status = SYS$QIOW( 0, *modemChan, IO$_READVBLK | IO$M_TIMED | IO$M_PURGE, &iosb, 0, 0, readBuf, transferSize, tmo, termSet, 0,0); if ( !(status & 1) ) return( status ); if ( !(iosb.status & 1) ) return( iosb.status ); /* * Read one character at a time, looking for the * one time markers (*,#) */ *bytesRead = 0; onTime = FALSE; SYS$SETEF(1); /* Set EF 1 */ for(i=0; i < transferSize; i++) { status = SYS$QIO(2, /* Set EF 2 on next character read */ *modemChan, IO$_READVBLK | IO$M_TIMED, &iosb, 0, 0, &readBuf[i], 1, /* transfer 1 char at a time */ tmo, 0, /* no terminal set */ 0, 0); while (1) { SYS$WFLOR(1, 6); /* Wait for either EF 1 or EF 2 */ if ( SYS$READEF(2, &status) == SS$_WASSET) break; tempBeforeIndex != tempBeforeIndex; if ( utc_gettime( &tempBeforeTime[tempBeforeIndex]) ) ExitA( status ); SYS$SETIMR(1, /* Set EF 1 on the next tick */ delta, 0, 0, 0); } if ( utc_gettime( afterTime ) ) ExitA( status ); if ( !(status & 1)) return(status); if ( !(iosb.status & 1)) return(status); *bytesRead += 1; if ( readBuf[i] == '*' || readBuf[i] == '#') { onTime = TRUE; break; } } /* for */ /* * If we were not able to read the on time marker, * signal an abort back to our caller. */ if ( ! onTime ) { return(SS$_ABORT); } *beforeTime = tempBeforeTime[!tempBeforeIndex]; return ( SS$_NORMAL ); } /* ********************************************************** * IO Routines. ********************************************************** */ /* *++ * CommandLineInput() * * Functional Description: * * Set the Time provider control parameters using the * user's command line parameters. * * Inputs: * * fargc - argc from main. * fargv - argv from main. * * Implicit Inputs: * * None. * * Outputs: * * None. * * Implicit Outputs: * * The configuration/control parameters are set using the * command line argument interface. * * Value Returned: * * None. * * Side Effects: * * None. * *-- */ void CommandLineInput( fargc, fargv ) int fargc; char **fargv; { int i; for (i=1 ; i K_MAX_DIALRATE ) { (void) fprintf(stderr, "-r:Dialrate value is out of legal range\n"); PrintHelp (); ExitA(1); } break; case 'P': case 'p': GETNEXTARG(fargc); if ( 1 != sscanf(fargv[i],"%d",&POLLRATE) || POLLRATE < K_MIN_POLLRATE || POLLRATE > K_MAX_POLLRATE ) { (void) fprintf(stderr, "-p:Pollrate value is out of legal range\n"); PrintHelp (); ExitA(1); } break; case 'I': case 'i': GETNEXTARG(fargc); if ( 1 != sscanf(fargv[i], "%d", &INACCURACY ) || INACCURACY < K_MIN_INACCURACY || INACCURACY > K_MAX_INACCURACY ) { (void) fprintf(stderr,"-i:Inaccuracy value is out of legal range\n"); PrintHelp(); ExitA(1); } break; case 'T': case 't': GETNEXTARG(fargc); if ( 1 != sscanf(fargv[i], "%d", &TIMESTAMP) || TIMESTAMP < K_MIN_TIMESTAMPS || TIMESTAMP > K_MAX_TIMESTAMPS ) { (void) fprintf(stderr,"-t:Time stamp count is out of legal range\n"); PrintHelp(); ExitA(1); } break; case 'D': case 'd': GETNEXTARG(fargc); strncpy( devicename, fargv[i], K_MAX_DEV_NAME); TPDEVICE.dsc$w_length = strlen( devicename ); break; case 'N': case 'n': GETNEXTARG(fargc); strncpy( ACTSnum, fargv[i], K_MAX_MODEM_NUMBER ); break; case 'M': case 'm': GETNEXTARG(fargc); strncpy( modemType, fargv[i], K_MAX_MODEM_TYPE ); break; case 'F': case 'f': GETNEXTARG(fargc); if ( 1 != sscanf(fargv[i], "%d", &FIRSTSYNCH)) { (void) fprintf(stderr,"-f:First Synchronization is out of legal range\n"); PrintHelp(); ExitA(1); } break; case 'E': case 'e': GETNEXTARG(fargc); if ( 1 != sscanf(fargv[i], "%d", &MAXTPERROR) || MAXTPERROR <= K_MIN_MAXTPERROR) { (void) fprintf(stderr,"-e: Error Tollerance is out of legal range\n"); PrintHelp(); ExitA(1); } break; default: PrintHelp(); ExitA(1); } /* switch */ } /* else */ } /* for */ if ( TPDEVICE.dsc$w_length == 0 ) { (void) fprintf( stderr, "The modem device must be specified\n"); PrintHelp(); ExitA(1); } } /* *++ * InteractiveInput() * * Functional Description: * * Set the Time provider control parameters using the * user's interactive responses. * * Inputs: * * None. * * Implicit Inputs: * * None. * * Outputs: * * None. * * Implicit Outputs: * * The configuration/control parameters are set using an * interactive interface. * * Value Returned: * * None. * * Side Effects: * * None. * *-- */ void InterActiveInput() { char inputLine[512]; (void) printf("\n\n\nEnter the Device Name: "); (void) fgets( inputLine, 512, stdin ); (void) sscanf(inputLine, "%255s", devicename ); TPDEVICE.dsc$w_length = strlen(devicename); (void) printf("Enter the Modem type[%s]: ",modemType ); (void) fgets( inputLine, 512, stdin ); (void) sscanf(inputLine, "%6s", modemType); (void) printf("Enter the telephone number[%s]: ",ACTSnum ); (void) fgets( inputLine, 512, stdin ); (void) sscanf(inputLine, "%26s", ACTSnum); (void) printf("Enter the Dial Rate[%d(sec)]: ", DIALRATE); (void) fgets( inputLine, 512, stdin ); (void) sscanf(inputLine, "%d", &DIALRATE ); if ( DIALRATE < K_MIN_DIALRATE || DIALRATE > K_MAX_DIALRATE ) { (void) fprintf(stderr, "-p:Dialrate value is out of legal range\n"); ExitA(1); } (void) printf("Enter the Poll Rate[%d(sec)]: ", POLLRATE); (void) fgets( inputLine, 512, stdin ); (void) sscanf(inputLine, "%d", &POLLRATE ); if ( POLLRATE < K_MIN_POLLRATE || POLLRATE > K_MAX_POLLRATE ) { (void) fprintf(stderr, "-p:Pollrate value is out of legal range\n"); ExitA(1); } (void) printf("Enter the Inaccuracy[%d(ms)]: ", INACCURACY); (void) fgets( inputLine, 512, stdin ); (void) sscanf(inputLine, "%d", &INACCURACY ); if ( INACCURACY < K_MIN_INACCURACY || INACCURACY > K_MAX_INACCURACY ) { (void) fprintf(stderr, "-i:Inaccuracy value is out of legal range\n"); ExitA(1); } (void) printf( "Enter the Timestamp Count[%d]: ", TIMESTAMP); (void) fgets( inputLine, 512, stdin ); (void) sscanf(inputLine, "%d",&TIMESTAMP); if (TIMESTAMP < K_MIN_TIMESTAMPS || TIMESTAMP > K_MAX_TIMESTAMPS ) { (void) fprintf(stderr, "-t:Time stamp count is out of legal range\n"); ExitA(1); } (void) printf( "Output Trace [%d(Y:1,N:0)] : ", TRACING); (void) fgets( inputLine, 512, stdin ); (void) sscanf(inputLine, "%d",&TRACING); (void) printf("Error Tollerance [%d] : ",MAXTPERROR); (void) fgets( inputLine, 512, stdin ); (void) sscanf(inputLine, "%d",&MAXTPERROR); if (MAXTPERROR < K_MIN_MAXTPERROR) { (void) fprintf(stderr, "-e:Error Tollerance is out of legal range\n"); ExitA(1); } (void) printf("Ignore Error Tollerance on First Synch. [%d] : ", FIRSTSYNCH); (void) fgets( inputLine, 512, stdin ); (void) sscanf(inputLine, "%d",&FIRSTSYNCH); } /* *++ * PrintHelp() * * Functional Description: * * Print out help text to the user. * * Inputs: * * None. * * Implicit Inputs: * * None. * * Outputs: * * None. * * Implicit Outputs: * * None. * * Value Returned: * * None. * * Side Effects: * * None. * *-- */ void PrintHelp() { (void) fprintf(stderr, "usage:\tdtss_acts_provider_vms -d device-name\n"); (void) fprintf(stderr, "\t\t[-m Modem Type, default : DMCL]\n"); (void) fprintf(stderr, "\t\t[-n Telephone Number, default : T83034944774]\n"); (void) fprintf(stderr, "\t\t[-r seconds of dialrate, range %d : %d, default %d]\n", K_MIN_DIALRATE,K_MAX_DIALRATE,K_DEF_DIALRATE); (void) fprintf(stderr, "\t\t[-p seconds of pollrate, range %d : %d, default %d]\n", K_MIN_POLLRATE,K_MAX_POLLRATE,K_DEF_POLLRATE); (void) fprintf(stderr, "\t\t[-i milliseconds of inaccuracy, range %d : %d, default %d]\n", K_MIN_INACCURACY, K_MAX_INACCURACY, K_DEF_INACCURACY); (void) fprintf(stderr, "\t\t[-t timestamps, range %d : %d, default %d]\n", K_MIN_TIMESTAMPS,K_MAX_TIMESTAMPS,K_DEF_TIMESTAMPS); (void) fprintf(stderr, "\t\t[-o output trace, range %d(false) : %d(true), \ default %d(false)]\n", FALSE,TRUE,K_DEF_TRACING); (void) fprintf(stderr, "\t\t[-f firstsynch, range %d : %d, default %d]\n", TRUE,FALSE,K_DEF_FIRSTSYNCH); (void) fprintf(stderr, "\t\t[-e errortolerance, min %d, default %d]\n", K_MIN_MAXTPERROR,K_DEF_TIMESTAMPS); } /* End of routine PrintHelp */ /* *++ * PrintTimes() * * Functional Description: * * Print out the contents of a time response message * * Inputs: * * timeresponse - the time response message to print out. * * Implicit Inputs: * * None. * * Outputs: * * None. * * Implicit Outputs: * * * Value Returned: * * None. * * Side Effects: * * None. * *-- */ void 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) fprintf(stdout, "TPsuccessful\n"); (void) fprintf(stdout, "Serial Number : %d\n", timeResponse->TPsyncID); (void) fprintf(stdout, "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) fprintf(stdout,"%s\nAfter Time\t:",timestr); utc_ascgmtime(timestr,UTC_MAX_STR_LEN, &timeResponse->TPdata.TimeMsg.timeStampList[i].afterTime); (void) fprintf(stdout,"%s\n",timestr); } fprintf(stdout, "******************************************\n"); } /* *++ * 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"); } #pragma standard