* LTP.c	Local Time Protocol
 *
 * DRH	26 Apr 89	Initial.
 * DRH	17 Nov 90	Added timeout for client and GetVHB.
 * DAV	06 Jun 91	Added outstanding request counting to prevent
 					synchronization problems.
 *

* This program implements a very simple system date and time update program.
 * Both the server and client functions are encoded here.  The objective is 
 * to demonstrate the use of the Application Programming Interface (API) to 
 * InternetCT using the requestresponse model.  The code is selfcontained 
 * except for the reference to the InternetCT.h header file which is supplied 
 * with InternetCT. No references are made to external components other than
 * standard C functions and procedures and functions from CTOS.Lib.
 *
 * The implementation is very simple. One of the nodes in a network of CTOS 
 * based machines is designated the Local Time Protocol Server.  One which 
 * runs constantly or is the first to be turned on daily is the best choice.  
 * The other nodes run as clients requesting the time from the server and 
 * setting their system date and time accordingly.
 * 
 * The server name, the frequency of the requests and a timeout value for 
 * requests are initialization parameters.
 * 
 * The program can also be run as a one shot UPDATE.  The function of the 
 * program is determined by the parameters provided and the existance of an 
 * LTP_Server host name in the configuration of InternetCT.  For more   
 * detailed information see the initialization function below.
 *
 * For example, if some node known as Mega were to be the LTP_Server its 
 * entries in the Transport.Config file might be:
 *
 * host add Mega			1.1.1.1
 * host add LTP_Server		1.1.1.1
 *
 * No target server name would then need to be provided as a parameter to 
 * LTP.  If a name is provided in the Server name paramter, it overrides 
 * whatever may have been specified to InternetCT during configuration.
 *
 * If the program is running in UPDATE mode and is unable to connect with the 
 * LTP server, it sets the date and time from the last modification date and 
 * time found in the volumn home block (VHB).
 *
 * This program was writted for compilation with the Mark Williams C compiler
 * version 2.0.
 *
 * Command format:
 *		LTP
 *			[Server name (default = LTP_Server)]
 *			[Client request interval (default = 0)]
 *			[Client timeout (default = 10 seconds)]
 *


define LTP_PORT			2047
define ZERO_INDEX			0
define ZERO_REMOTE_PORT	0
define UPDATE				0
define CLIENT				1
define SERVER				2
define ERC_OK				0
define FALSE				0
define TRUE				1
define NULLCHAR			(char *)0

define MAX_WAIT_DEFAULT	10
define ONE_SECOND			10

include inetrq.h

define	min(x,y)			((x)<(y)?(x):(y))
define	max(x,y)			((x)>(y)?(x):(y))

typedef struct systemdateandtime {
	unsigned int				seconds;
	unsigned int				daytimes2;
} SDT;

typedef struct volumehomeblocktype {
	unsigned			checksum;
	unsigned long		lfasysimage;
	unsigned			cpagessysimage;
	unsigned long		lfabadblock;
	unsigned			cpagesbadblock;
	unsigned long		lfacrashdump;
	unsigned			cpagescrashdump;
	char				volname[13];
	char				passunsigned[13];
	unsigned long		lfavhb;
	unsigned long		lfainitialvhb;
	SDT					creationdate;
	SDT					moddate;
	unsigned long		lfamfd;
	unsigned			cpagesmfd;
	unsigned long		lfalog;
	unsigned			cpageslog;
	unsigned			currentlogpage;
	unsigned			currentlogbyte;
	unsigned long		lfafh;
	unsigned			cpagesfh;
	unsigned			altfhoffset;
	unsigned			ifreefh;
	unsigned			cfreefh;
	unsigned			clusterfactor;
	unsigned			extend_def;
	unsigned			allocskipcnt;
	unsigned long		lfaalloc;
	unsigned			cpagealloc;
	unsigned			lastallocpg;
	unsigned			lastallocwd;
	unsigned			lastallocbit;
	unsigned long		cfreepages;
	unsigned			idev;
	unsigned char		rglrudirentries[105];
	unsigned			magicword;
	unsigned char		bootsect;
	unsigned char		boothead;
	unsigned			bootcyl;
	unsigned			cpgboot;
	unsigned char		badblksect;
	unsigned char		badblkhd;
	unsigned			badblkcyl;
	unsigned			cpgbadblk;
	unsigned char		dumpsect;
	unsigned char		dumphd;
	unsigned			dumpcyl;
	unsigned			cpgdump;
	unsigned			bytepersect;
	unsigned			sectpertrack;
	unsigned			trackpercyl;
	unsigned			cylperdisk;
	unsigned char		interleave;
	unsigned			sectsizecode;
	unsigned			spiral;
	unsigned			startsect;
} VHB;

typedef struct pbcb_type {
	char						* pb;
	unsigned int				cb;
} PBCB;

*.< Local Functions >*

unsigned int			pbcbtou( );

void					initialize( );		
unsigned int			start_server( );
void					server( );
unsigned				client( );
unsigned int			main( );

*.< Imported Functions >*

extern unsigned int		tcpgetsetup( );			* ICT procedural interface *
extern unsigned int		hostnametoaddr( );

extern void			 	checkerc( );				* CTOS Lib 				*
extern void				exit( );
extern unsigned int		converttosys( );
extern unsigned int		delay( );
extern unsigned int		getdatetime( );
extern unsigned int		getusernumber( );
extern unsigned int		request( );
extern unsigned int		setpartitionname( );
extern unsigned int		wait( );

*.< Local Data >*

char					installed_banner[61] = LTP (version ;
char					linker_version[11];
char					installed_banner_end[] = ) installed;
char					as_server[] =  as SERVER\n;
char					as_client[] =  as CLIENT, Server = ;

char					msg_failed[] =
		Client FAILED. Date and time NOT set. ERC = u;


char					lookup_hostname[] = LTP_Server;
define SLOOKUP_HOSTNAME	10
unsigned long int		lookup_ip_addr;

unsigned long int		ip_addr;
char					hostname[13];
define SHOSTNAME	12
unsigned int			shostnameret;
unsigned int			mss;
unsigned int			window;
unsigned int			ttl;

char					partition_name[]		= LTP_Server;
char					client_partition_name[]	= LTP_Client;

unsigned int			function;

char					target_hostname[13];
unsigned char			ftarget_hostname = FALSE;

unsigned int			delay_factor;
unsigned int			max_wait;
unsigned char			tired_of_waiting = FALSE;

unsigned char			date_time_set;

unsigned int			exch;

SDT						sysdt;
char					clock_dtf[] = Www Nnn 0d, *y 0t:0m:0s;
char					dt[81];
char					line[81];

VHB						vhb;

IRCB					ircb;

unsigned int			inetct_rq_user_number;

*.< Imported Data >*

extern char						sbverrun[];			* from the Linker 		*

*====================.< Convert PBCB to Unsigned Int >=====================*
unsigned int pbcbtou( pb, cb)

char					* pb;
unsigned				cb;	
{
	unsigned			digit;
	unsigned			result;
	int					i;

	result = 0;
	i = 0;
	while( i < cb) {
		if( ( pb[i] < '0') || ( pb[i] > '9')) {
			return( 0);
		}
		digit = pb[i]  '0';
		if( result > ( 0xFFFF  digit)  10) {
			return( 0);
		}
		result = ( result * 10) + digit;
		i++;
	}
	return( result);
}

*======================.=======< Initialize >==============================*
void initialize( )
{
	PBCB				pbcb;
	unsigned int		erc;
	unsigned int		i;

	*	Get the linker version for display *
	
	for( i = sbverrun[0], linker_version[i] = '\0'; i > 0; i) {
		linker_version[i1] = sbverrun[i];
	}

	strcat( installed_banner, linker_version);
	strcat( installed_banner, installed_banner_end);


	*	Get our hostname. *
	
	checkerc( tcpgetsetup( ip_addr, mss, window, ttl,
			  hostname, SHOSTNAME, shostnameret));

	*  Check to see if the default server hostname is
	 *	defined.  If it is, then it becomes the target by default.
	 *

	if( hostnametoaddr( lookup_hostname, SLOOKUP_HOSTNAME,
						lookup_ip_addr) == ERC_OK) {
		strcpy( target_hostname, lookup_hostname);
		ftarget_hostname = TRUE;
	}


	* Was a target server name provided?  If it was, it replaces the default 
	 * target hostname LTP_Server.
	 *
	 
	if( rgparam( 1, 0, pbcb) == ERC_OK) {
		i = min( pbcb.cb, SHOSTNAME);
		memcpy( target_hostname, pbcb.pb, i);
		target_hostname[i] = '\0';
		ftarget_hostname = TRUE;
	}

	* Check Delay Factor.  If not 0, we want to be a client.  If it is 0,
	 * assume for the moment that we want to do a one shot UPDATE.
	 *

	if( rgparam( 2, 0, pbcb) = ERC_OK) {
		delay_factor = 0;
	} else {
		delay_factor = pbcbtou( pbcb.pb, pbcb.cb) * ONE_SECOND;
	}
	if( delay_factor = 0) {
		function = CLIENT;
		strcat( installed_banner, as_client);
	} else {
		function = UPDATE;
		delay_factor = 10 * ONE_SECOND;
	}

	* Setup the maximum wait time for client connection activity.  If a 
	 * response is not received within this number of seconds, the connection 
	 * is aborted.
	 *

	if( rgparam( 3, 0, pbcb) == ERC_OK) {
		max_wait = pbcbtou( pbcb.pb, pbcb.cb);
	} else {
		max_wait = MAX_WAIT_DEFAULT;
	}


	* If we don't have a target hostname by this point, use our hostname,
	 * i.e., we will become the SERVER.  Make sure the host name provided is
	 * defined to InternetCT by looking up its IP address.
	 *

	if( ftarget_hostname) {
		strcpy( target_hostname, hostname);
		lookup_ip_addr = ip_addr;
	} else {
		if( hostnametoaddr( target_hostname, strlen(target_hostname),
							lookup_ip_addr) = ERC_OK) {
			checkerc( ERC_CON_NO_HOST_EXISTS);
		}
	}

	* If the target host name is the same as ours we are the SERVER.
	 * Otherwise, we want to execute as either a CLIENT or one shot UPDATE as 
	 * determined earlier.  Set up the installed banner accordingly.
	 *

	if(	lookup_ip_addr == ip_addr) {
		function = SERVER;
		strcat( installed_banner, as_server);
	} else {
		strcat( installed_banner, target_hostname);
		strcat( installed_banner, \n);
		strcpy( partition_name, client_partition_name);
	}
}

*======================.=======< LTP Server >==============================*
void server( )
{
	REQUEST				* rq;
	unsigned char		rq_wait = FALSE;

	* The normal sequence of events for the SERVER is:
	 *	1. Get a response from our passive open. Get the system date and time.
	 *	   Send the date and time to the requesting client.
	 *	2. Receive the response from the send and close the connection.
	 *	3. Receive the response from the close and do another passive 
	 *	   open.
	 * With this scenerio any connection attempt between the time we get the 
	 * open response and the time we issue another passive open will be 
	 * greeted by a CLOSE_RESET from the transport service because there is 
	 * nobody waiting at our port.  However, because things happen so fast 
	 * and the client processes are designed to keep trying in the event of 
	 * failure, in practice very few connections are missed.  This is 
	 * not, however, a good design for more serious servers.  They should do 
	 * another passive open as soon as a response is received.
	 *

	if( inet_rq_open( ircb, exch, ZERO_INDEX, INETCT_OPEN_WAIT, LTP_PORT,
			ZERO_REMOTE_PORT, TCP_PASSIVE, NULLCHAR, 0) = 0) {
		delay( 300);					* wait 30 seconds *
		return;
	}
	for( ; ; ) {

		checkerc( wait( exch, rq));

		ircb.rqs_out;
check_leave:
		if( rq_wait) {
			if( ircb.rqs_out == 0) {
				return;
			}
			continue;
		}

		switch( rq>rqcode  REQUEST_BASE) {
		case ORC_OPEN_TCP_CONNECTION:
			if( rq>ercret = ERC_OK) {
				rq_wait = TRUE;
				goto check_leave;
			}
			getdatetime( sysdt);
			if( inet_rq_send( ircb, FALSE, FALSE, sysdt, 4) = ERC_OK) {
				inet_rq_close( ircb, INETCT_ABORT);
				goto check_leave;
			}
			break;
		case ORC_SEND_TCP_MESSAGE:
			inet_rq_close( ircb, INETCT_CLOSE);
			break;
		case ORC_CLOSE_TCP_CONNECTION:
			rq_wait = TRUE;
			goto check_leave;
			break;
		}
	}
}

*======================.=======< LTP Client >==============================*
unsigned client( )
{
	REQUEST				* rq;
	unsigned int		have_waited = 0;
	unsigned char		rq_wait = FALSE;
	unsigned char		reset_tried = FALSE;
	unsigned			erc;

	* The normal sequence of events for the CLIENT is:
	 * 1. Get a response from our active open.  Receive the 4 bytes which are 
	 *    the system date and time.  Close the connection if anything went 
	 *    wrong.
	 * 2. If the receive was successful, update the date and time and remember 
	 *    that we were successful.  The one shot UPDATE quits as soon as it 
	 *    is successful.  In any event, close the connection.
	 * 3. When the close response comes just return.
	 *

	tired_of_waiting = FALSE;
	date_time_set = FALSE;
	
try_reset:
	if( ( erc = inet_rq_open( ircb, exch, 0, FALSE, 0, LTP_PORT, TCP_ACTIVE,
			target_hostname, strlen( target_hostname))) = ERC_OK) {
		return( erc);
	}
			
	for( ; ; ) {

		* Check for a response for one of our API requests.  If there is one, 
		 * process it.  Otherwise, if we don't get a response within 
		 * 'max_wait' time, abort the connection.
		 * Notice that once we decide we are tired_of_waiting and issue the 
		 * abort request the checkdelay loop will continue until a response 
		 * is returned.  Getting a response from a close or abort is assured 
		 * by the API.
		 *

		if( check( exch, rq) = ERC_OK) {
			delay( ONE_SECOND);
			if(  tired_of_waiting) {
				if( ++have_waited == max_wait) {
					inet_rq_close( ircb, INETCT_ABORT);
					tired_of_waiting = TRUE;
					rq_wait = TRUE;
				}
			}
			continue;
		}
		ircb.rqs_out;
check_leave:
		if( rq_wait) {
			if( ircb.rqs_out == 0) {
				return( erc);
			}
			continue;
		}

		switch( rq>rqcode  REQUEST_BASE) {
		case ORC_OPEN_TCP_CONNECTION:
			if( rq>ercret = ERC_OK) {
				erc = rq>ercret;
				rq_wait = TRUE;
				if( rq>ercret == ERC_CLOSE_RESET) {
					if( reset_tried) {
						reset_tried = TRUE;
						delay( 100);		* wait 10 seconds *
						goto try_reset;
					}
					tired_of_waiting = TRUE;
				}
				goto check_leave;
			}
			if( inet_rq_receive( ircb, sysdt, 4) = ERC_OK) {
				inet_rq_close( ircb, INETCT_ABORT);
				goto check_leave;
			}
			break;
		case ORC_RECEIVE_TCP_MESSAGE:
			if( ircb.smsgret == 4) {
				date_time_set = TRUE;
				setdatetime( sysdt.seconds, sysdt.daytimes2);
			}
			inet_rq_close( ircb, INETCT_CLOSE);
			break;
		case ORC_CLOSE_TCP_CONNECTION:
			rq_wait = TRUE;
			goto check_leave;
		}
	}
}

*======================.==========< Main >=================================*
unsigned int main( )
{
	unsigned int		cb;
	unsigned			erc;
	
	initialize( );

	switch( function) {
	case SERVER:
	case CLIENT:
		setpartitionname( inetct_rq_user_number,
						  partition_name, strlen( partition_name));
		outputtovid0( installed_banner, strlen( installed_banner));
		checkerc( converttosys( ));
		exit( );
		break;
	default:
		break;
	}

	getusernumber( inetct_rq_user_number);
	
	checkerc( allocexch( exch));

	switch( function) {
	case SERVER:
		for( ; ; ) {
			server( );
		}
		break;
	case CLIENT:
		for( ; ; ) {
			client( );
			delay( delay_factor);
		}
		break;
	case UPDATE:
		erc = client( );
		if( date_time_set) {
			nlsformatdatetime( NULLCHAR, sysdt, clock_dtf, 
							   strlen( clock_dtf), dt, 80, cb);
			dt[cb] = '\0';
			cb = sprintf( line, 
					 Date and time from s set to: s, 
					 target_hostname, dt);
		} else if( tired_of_waiting) {
			getvhb( Sys, 3, vhb, sizeof( VHB));
			setdatetime( vhb.moddate.seconds, vhb.moddate.daytimes2);
			getdatetime( sysdt);
			nlsformatdatetime( NULLCHAR, sysdt, clock_dtf, 
							   strlen( clock_dtf), dt, 80, cb);
			dt[cb] = '\0';
			cb = sprintf( line, 
					  Date and time from VHB set to: s, 
					  dt);
		} else {
			cb = sprintf( line, msg_failed, erc);
		}
		outputtovid0( line, cb);
		break;
	default:
		break;
	}
	exit( );
}

s 
	 * nobody waiting at our port.  However, because things happen so fast 
	 * and the client processes are designed to keep trying in the event of 
	 * failure, in practice very few connections are missed.  This is 
	 * not, however, a good design for more serious servers.  They should do 
	 * another passive open as soon as a response is received.
	 *

	if( inet_rq_open( ircb, exch, ZERO_INDEX, INETCT_OPEN_