/*                                              25 September 2000.  SMS.
 *
 *    The following is a tidied version of the original.  It compiles
 * cleanly on VMS V7.2 with DEC C V6.0-001, this way:
 *
 *    $ CC DTSS$NTP_PROVIDER.C
 *    $ LINK DTSS$NTP_PROVIDER /OPTIONS
 *
 * where DTSS$NTP_PROVIDER.OPT is:
 *
 *    DTSS$NTP_PROVIDER.OBJ
 *    SYS$SHARE:DTSS$SHR.EXE /SHAREABLE
 */

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

/*
 * 	Module DTSS$PROVIDER.C
 *	Version V1.0-2
 */

/*
 * 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) Recommended: 600
 *
 *         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 "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)

typedef struct IOsb {				/* IO status Block */
    unsigned short status;
    unsigned short byteCount;
    unsigned int  devStatus;
    } IOsb;

/* 
 * 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       (255)	/* largest hostname user can use*/
char hostname[K_MAX_HOSTNAME+1];         /* Host name			*/
$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    */

/***********************************************************
 * 		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        (60)	   /* allow NTP server 60	*
					    * 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     60     /* seconds        */
#define NTP_TIME_STAMPS    4     /* int, 1-6       */
#define NTP_INACCURACY    300    /* seconds        */
#define NTP_TIMEOUT       60     /* 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_VERSION1	     (0x0b)	   /* Status bits that encode	*
					    * version			*/
#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_int int_part;
	u_int 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  |0 0 0|    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 {
	u_char status;		/* status of local clock and leap info */
	u_char stratum;		/* Stratum level */
	u_char poll;		/* poll value */
	int precision:8;
	struct s_fixedpt distance;
	u_int drift;
	u_int 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 socketin = {AF_INET};

int dstlen = sizeof(dst);		 /* Size of dst struct		*/

struct sockaddr tstpSockAddr_un;	/* UNIX domain socket for TSTP  */
struct servent *sp;			/* Service structure          */
struct hostent *hp;			/* Host structure             */

void cvt_ntp_utc(struct utc *utc, struct l_fixedpt *ntp);
void cvt_utc_ntp(struct l_fixedpt *ntp, struct utc *utc);

InitNTP()
{
int service_port;

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

    }
    else {
        dst.sin_port = htons(sp->s_port);
    }
#else
	dst.sin_port = htons(K_NTP_DEF_SERVICE_PORT);
#endif

    /*
     * Lookup host name and address
     */

    hp = gethostbyname(hostname);
    if (hp == 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);
    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( ntp, utc )
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_int) (K_NTP_JAN_1970 + timesp.tv_sec));
    ntp->fraction = ntohl((u_int) ((float) timesp.tv_nsec *
                                    4.294967295));
}    

/*
 *++
 *  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( utc, ntp )
struct utc *utc;
struct l_fixedpt *ntp;
{
    timespec_t timesp;

    timesp.tv_sec = ntohl(ntp->int_part) - K_NTP_JAN_1970;
    timesp.tv_nsec = ((u_int)ntohl(ntp->fraction)) / 4.294967295;
    (void) utc_mkbintime(utc, &timesp, (timespec_t *)0, 0L);
}    


/*
 *++
 *  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 int InitIPC( unsigned short *tstpChan, unsigned short *tptsChan )
{
#if NOSERVER
    return(1);
#else
    unsigned int 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:
 *
 *	int - the status of the write request.
 *
 *  Side Effects:
 *
 *	None.
 *
 *--
 */
unsigned int SendReply( unsigned short tptsChan, TPrspMsg *timeResponse )
{
    IOsb iosb;
    unsigned int 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( 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:
 *
 *	int - the status of the read of the mailbox.
 *
 *  Side Effects:
 *
 *	None.
 *
 *--
 */
unsigned int ReadRequest( unsigned short tstpChan, TPreqMsg *timeRequest )
{
#if NOSERVER
    sleep(10);
    return(1);
#else
    IOsb iosb;
    unsigned int 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( 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 */


/*
 *++
 *  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( int *pollrate,
                           int *timestamp,
			   int *inaccuracy,
                           int *tmorate,
			   int *clockSet,
                           int *retries,
			   int *firstsynch,
                           int *maxtperror,
			   int *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 */


void CleanupNTP( void)
{
int retval;

    retval = shutdown(ttchan,2);
    if (retval == -1)
        perror("shutdown");
    retval = close(ttchan);
    if (retval)
        perror("close");
}


/*
 *++
 *  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( int fd,
                   struct utc *beforeTime,
                   struct utc *providerTime, 
                   struct utc *afterTime)
{
    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                  */
    timespec_t synchdist;		/* NTP synchronization distance    */
    timespec_t timesp, inaccsp;		/* Temps for time computation      */


    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->status = K_NTP_VERSION1;
    pkt->stratum = K_NTP_UNSPECIFIED;
    pkt->poll = 6;

    /*
     * Record 'before' timestamp
     */
    (void) utc_gettime(beforeTime);
    cvt_utc_ntp(&pkt->xmt, beforeTime);

    if ((sendto(fd, (char *)pkt, sizeof(ntp_data), 0,
		(struct sockaddr *) &dst, 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);
    }

    if (n == 0) {
	(void) fprintf(stderr, "\n\t* Timeout *\n");
	return(0);
    }

    if ((recvfrom(fd, (char *)pkt, sizeof (ntp_data), 0,
	          (struct sockaddr *) &socketin,
                  (unsigned int *)&dstlen)) < 0) {
	perror("dtssprovider: NTP recvfrom");
	exit(1);
    }

    /*
     * Record 'after' timestamp
     */
    (void) utc_gettime(afterTime);
    (void) printf("Packet from: [%s]\n", inet_ntoa(socketin.sin_addr));
    (void) printf("Status: %d\tStratum: %d\n",
		      (int)(pkt->status & 0x03f), pkt->stratum);

    /*
     * Check stratum of timestamp
     */

    if (pkt->stratum >= K_DTS_NTP_STRATUM)
	return(0);

    /*
     * Compute ntp time from information in the ntp message
     */

    cvt_ntp_utc(&ref, &pkt->reftime);
    t1 = *beforeTime;
    cvt_ntp_utc(&t2, &pkt->rec);
    cvt_ntp_utc(&t3, &pkt->xmt);
    t4 = *afterTime;
    synchdist.tv_sec = ntohs((u_short) pkt->distance.sint_part);
    synchdist.tv_nsec =
	(u_int) ( (double)ntohs((u_short) pkt->distance.sfraction) *
		   (double)K_NS_PER_SEC / 65536.0 );

    (void) utc_bintime(&timesp, (timespec_t *)0, (long *)0, &t2);

    inaccsp.tv_sec = synchdist.tv_sec + INACCURACY/K_MS_PER_SEC;
    inaccsp.tv_nsec = synchdist.tv_nsec + 
			((INACCURACY%K_MS_PER_SEC) * K_NS_PER_MS);

    if (inaccsp.tv_nsec > K_NS_PER_SEC) {
	inaccsp.tv_sec++;
	inaccsp.tv_nsec -= K_NS_PER_SEC;
    };
    
    (void) utc_mkbintime(providerTime, &timesp, &inaccsp, 0L);

    return(1);
}

/*
 *++
 *  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( 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) );
}      


/*
 *++
 *  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 int timeStamps;
unsigned int retries;
int ttchan;
TPtimeMsg  *tpTimeMsg;
{
    int i;                          /* temp, index */
    int retry;
    int 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 ) {
	status = QueryProvider((int)ttchan, 
			       
&tpTimeMsg->timeStampList[i].beforeTime, 
			       
&tpTimeMsg->timeStampList[i].TPtime,  
			       
&tpTimeMsg->timeStampList[i].afterTime );
	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);
}

/*
 **********************************************************
 * 		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");
}


/*
 *++
 *  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");
    
}

/*
 *++
 *  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 int status;
    unsigned short tstpChan;
    unsigned short tptsChan;
    char value[255];
    int i;
    short size;
    unsigned int attr = LNM$M_CASE_BLIND;	/* attributes mask */ 
    struct {
	short bufln ;
	short itmcd ;
	char *bufad ;
	short *rtnln ;
	int term;
    } itmlst = { 255, LNM$_STRING, &value[ 0], &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 );
    } ;
    
    value[0] = '\0';
    status = SYS$TRNLNM(&attr, &LNM_TABLE, &NTP_HOSTNAME, 0, &itmlst );
    if ((status & 1))
    {
	value[size] = '\0';
	strcpy(hostname,value);
    }
    else
    {
	fprintf( stderr, " No hostname defined.  Sts = %%x%08x.\n",
	 status);
    }
    
    /*
     * Echo back the input parameters.
     */
    fprintf(stderr,"PARAMETERS SET:\n");
    fprintf(stderr,"TP_HOSTNAME:        %s\n", hostname);
    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 (;;)
    {
	status = ReadRequest( tstpChan, &timeServiceRequest );
        if ( !(status & 1) )
	    exit( status );

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

	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();
	 }	       
     }		       
}
/* End of main program*/
    
#pragma standard
/*  DEC/CMS REPLACEMENT HISTORY, Element DTSS$NTP_PROVIDER.C */


