/*                                              3 August 2005.  SMS.
 *
 *    The following is a tidied version of the original.  It compiles
 * cleanly on VMS Alpha V7.3-1 with Compaq C V6.5-001, this way:
 *
 *    $ CC DTSS$PROVIDER_NTP.C
 *    $ LINK DTSS$PROVIDER_NTP /OPTIONS
 *
 * where DTSS$PROVIDER_NTP.OPT is:
 *
 *    DTSS$PROVIDER_NTP.OBJ
 *    SYS$SHARE:DTSS$SHR.EXE /SHAREABLE
 */

/*--------------------------------------------------------------------*/

/*
 * 	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
 *
 */

#pragma  nostandard		/* A VMS only Module                    */
#include <stdio.h>		/* standard I/O definitions             */
#include <iodef>		/* I/O definitions                      */
#include <ssdef>		/* error return value (SS$) definitions */
#include <descrip>		/* descriptor definitions               */
#include <lnmdef>		/* Logical name definitions             */
#include <ctype>		/* isxxx macros, etc.                   */
#include <time.h>		/* tm structure definition              */
#include <string.h>		/* string definitions                   */
#include "dtss$provider.h"	/* utc library routines & dtss tpi defs  */
#include <errno.h>
#include <types.h>
#include <socket.h>
#include <in.h>
#include <netdb.h>
#include <inet.h>
#include <ucx$inetdef.h>

#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);

/* 
 * 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;
$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          */

int INACCURACY;                                 /* base inaccuracy of TP    */
int POLLRATE;                                   /* seconds between TP syncs */
int TIMESTAMP;                                  /* time stamps at each sync */
int TMORATE;                                    /* timeout                  */
int CLOCKSET;                                   /* allow clock sets?        */
int RETRIES;                                    /* retries allowed.         */
int FIRSTSYNCH;                                 /* always synch first time  */
int MAXTPERROR;                                 /* Max error on TP.         */
int ISVERBOSE;                                  /* Print info.              */
int 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 */
    int retry;
    unsigned long status;

    retry = retries;

    /*
     * Now Read The Time Stamps.  
     * Note that this loop will terminate having read timeStamps timestamps
     * or exhusting retry retries. and thus is non-infinite.     */

    i = 0;
    while ( i < timeStamps ) {
	j=i;
      	if (i+1==timeStamps || i>=tsidx)
    	  {
	  j = nidx;
	  nidx = nidx + 1;
	  if (nidx >= tsidx) nidx=0;
	  }

	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) {
	    i += 1;
	} else {
	    if (retry-- == 0)
		return(0);
	}
    }

    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(&POLLRATE,
			 &TIMESTAMP,
			 &INACCURACY,
			 &TMORATE,
			 &CLOCKSET,
			 &RETRIES,
			 &FIRSTSYNCH,
			 &MAXTPERROR,
			 &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=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]);
    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);

	}	       
    }		       
}
/* 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(&timesp,&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};

unsigned 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;

    /*
     * 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, "dtssprovider: UDP/NTP: service unknown\n");
	(void) fprintf(stderr, "dtssprovider: 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; i<tsidx; i++)
      	{
    	hp = gethostbyname(hosts[i]);
    	if (hp <= (struct hostent *)NULL)
	    {
    	    (void) fprintf(stderr, "dtssprovider: No such host %s : %d\n",
				   hostname,errno);
	    close(ttchan);
            return(0);
    	    }
    	dst.sin_family = hp->h_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);
	}

    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 ((sendto(fd, (char *)pkt, sizeof(ntp_data), 0,
		(struct sockaddr *) &dsts[idx], dstlen)) < 0) {
	perror("dtssprovider: NTP sendto");
	exit(1);
    }

    /*
     * Wait for the reply by watching the file descriptor
     */
    if ((n = select(nfds, &readfds, (fd_set *)0, (fd_set *)0, &timeout)) < 0){
	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, &dstlen)) < 0) {
	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<<pkt->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(&timesp, (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, &timesp, &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("TPsuccessful\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");
    
}
