* NodeCheck.c		Check connectivity to a list of nodes by using TCP to open 
 *					connections.
 *
 * DRH	920605	Initial.
 * DRH	920908	Added fcontinuous and ferrors_beep.
 *				Changed to external format.
 *

* Operation 
 * 
 * Each of the noncomment lines in the input file is parsed for host name, 
 * TCP port, and timeout value.  A TCP connection is attempted to each in 
 * turn.  By default both successful and unsuccessful attempts are reported.  
 * A variety of options can be specified on the command line:
 *
 * 1. [Node list file]	Change the name of the input file from the default 
 *						[Sys]<INetCT>NodeCheck.Nodes.
 * 2. [Log filename]	Specify an output log file (replaces video output).
 * 3. [User String]		A text string can be provided that is added to the 
 *						separation banner.
 * 4. [Errors Only?]	Output can be limited to failed connections only.
 * 5. [Errors Beep?]	A beep can be sounded when an attempt fails .
 * 6. [Continuous?]		NodeCheck will loop through the input file 
 *						continuously.
 * 7. [Period]			A delay period (default 30 seconds) can be specified 
 *						between loops through the input file.
 *
 * Note:	The timeout value applies to the time it takes to reach reach the 
 *			ESTABLISHED state.  The close (or abort) processing is not timed.
 * 
 * Input file format:
 *
 * The input file contains lines that specify the host name and optionally TCP 
 * port and timeout values used by the program to test connectivity.  The 
 * default timeout is 10 seconds and the default TCP port is 7 (Echo).  Any 
 * blank line or line that begins with a  in column 1 is a comment.  White 
 * space (spaces or tabs) may be used as desired around the parameters on a 
 * line.  The paramters are positional, i.e., you cannot specify a port number 
 * without specifying a timeout value.  Some examples:
 * 
 * Column 1 +	
 *            |
 *            v	
 *            		Host Name	Timeout(10)		Port Number(7)
 *
 *					Sirius.GW
 *					PC			5				21 (FTP port)
 *					Far.Away	15
 * 
 * Output:
 * 
 * Node Check (920909)  User Text  92090910:49:59
 * Checking Sirius.GW       (Timeout =  10,  Port =   7) ... OK (0)
 * Checking PC              (Timeout =   5,  Port =  21) ... FAILED
 * 			InternetCT Close reset (Error 61537)
 * Checking Far.Away        (Timeout =  15,  Port =   7) ... FAILED, Timeout
 * 1 Succeeded, 2 Failed
 *
 * Successful connections show an elapsed time after the OK.  Sirius.GW is on 
 * an Ethernet LAN which accounts for the connection taking less than 1 second.
 *
 * The PC shows the normal result when there is no TCP server process 
 * listening at the designated port.  In this case there is no FTP server on 
 * the PC.  InternetCT errors are reported and the ERC is expanded.
 *
 * A summary is provided at the end of each loop through the input file.
 *

include stdio.h
include std.h
include inetrq.h
include timer.h
include NodeCheck.h

*.< Imported Functions >*

												* Lib>Modules.Lib			*
	
extern IRCB				* address_of_ircb();	* Lib>inetrq.c				*
extern unsigned int		inet_rq_open();
extern unsigned int		inet_rq_close();
extern unsigned int		inet_rq_send();
extern unsigned int		inet_rq_receive();
extern unsigned int		inet_rq_get_tcb();

extern unsigned int		message_initialize();	* Lib>message.c			*
extern unsigned char	* message_get();
extern unsigned char	* message_erc();

extern unsigned int		os_start_timer();		* Lib>timer.c				*
extern unsigned int		os_service_timer();
extern void				start_timer();
extern void				stop_timer();
extern void				tick();

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

extern void			 	checkerc();				* CTOS Lib 				*
extern void				exit();
extern unsigned int		getusernumber();
extern unsigned int		request();
extern unsigned int		wait();

extern char				* calloc();				* C lib *

*.< Local Functions >*

unsigned				read_kbd();
void					open_connection();
void					close_connection();
void					handle_timeout();
void					handle_ticks();
unsigned char			handle_requests();
void					restart();
void					handle_restart();
unsigned int			next_hostname();
void					put_banner();
void					initialize();		

*.< Local Data >*

char					version[40];

char					msg_file[]	= [Sys]<INetCT>NodeCheck.Msg.Bin;
char					erc_file[]	= [Sys]<INetCT>NodeCheck.Erc.Bin;

char					node_filename[MAX_FILENAME + 1] = 
												[Sys]<INetCT>NodeCheck.Nodes;

char					log_filename[MAX_FILENAME + 1];

FILE					* fp;
FILE					* log_fp;

unsigned char			ferrors_only;
unsigned char			ferrors_beep;
unsigned char			fcontinuous;
unsigned int			period = 30;
char					user_string[81];
char					hold_line[256];



	* used with TcpGetSetup *
unsigned long int		ip_addr;
char					hostname[HOSTNAMELEN + 1];
unsigned int			shostnameret;
unsigned int			mss;
unsigned int			window;
unsigned int			ttl;

	* set from the lines in NodeCheck.Nodes *
unsigned int			timeout;
unsigned int			port;

unsigned int			exch;
unsigned int			user_number;

IRCB					ircb;
REQUEST					* rq;
unsigned int			rq_code;
unsigned int			rq_erc;

	* the read keyboard request and character read *
RQ_READ_KBD				kbd;
char					key;

unsigned int			succeeded;
unsigned int			failed;

TIMER					timeout_timer;
TIMER					tick_timer;
TIMER					restart_timer;

unsigned int			ticks;
unsigned char			connection_active;

*.< Imported Data >*

extern char						sbverrun[];			* from the Linker 		*

*======================.======< Read Keyboard >=============================*
unsigned read_kbd()
{
	return( request( kbd));
}
*======================.=====< Open Connection >============================*
void open_connection( hostname, port, timeout)

char					* hostname;
unsigned int			port;
unsigned int			timeout;
{
		* Clear and relink the IRCB	*
	memset( ircb, '\0', sizeof(IRCB));
	ircb.self_ref = ircb;

		* Format the message *
	sprintf( hold_line, Checking 20s (Timeout = 3u,  Port = 3u) ... , 
					  	hostname, timeout, port);

		* Print it now if we're going to the video *
	if( ferrors_only) {
		if( log_fp == stdout) {
			fprintf( log_fp, hold_line);
			fflush( log_fp);
		}
	}

		* Issue the open *
	inet_rq_open(	ircb, exch, ZERO_INDEX, INETCT_OPEN_NO_WAIT, 
					ZERO_LOCAL_PORT, port, TCP_ACTIVE,
					hostname, strlen( hostname));

		* Initialize the tick count, start the timeout timer, etc. *
	ticks = 0;
	start_timer( timeout_timer);
	connection_active = TRUE;
}
*======================.=====< Close Connection >===========================*
void close_connection( close_abort)

unsigned int			close_abort;
{
	inet_rq_close(	ircb, close_abort);
	stop_timer( timeout_timer);
	connection_active = FALSE;
}
*======================.======< Handle Timeout >============================*
void handle_timeout( dummy)

char					* dummy;
{
	close_connection( INETCT_ABORT);
	if( log_fp = stdout || ferrors_only) {
		fprintf( log_fp, hold_line);
	}
	fprintf( log_fp, FAILED, Timeout\n);
	if( ferrors_beep) {
		beep();
	}
}
*======================.=======< Handle ticks >=============================*
void handle_ticks( dummy)

char					* dummy;
{
	ticks++;
	start_timer( tick_timer);
}
*======================.=====< Handle Requests >============================*
unsigned char handle_requests( rq_code, rq_erc, ircb)

unsigned int			rq_code;
unsigned int			rq_erc;
IRCB					* ircb;
{
	unsigned int		erc;

	switch( rq_code  REQUEST_BASE) {
		case ORC_OPEN_TCP_CONNECTION_NO_WAIT:
			if( rq_erc = ERC_OK) {
				stop_timer( timeout_timer);
				if( log_fp = stdout || ferrors_only) {
					fprintf( log_fp, hold_line);
				}
				fprintf( log_fp, FAILED\n\t\ts\n, message_erc( rq_erc));
				++failed;
				if( ferrors_beep) {
					beep();
				}
				if( ( erc = next_hostname(hostname,timeout,port)) = ERC_OK){
					fprintf( log_fp, u Succeded, u Failed\n, 
									 succeeded, failed);
					if( fcontinuous) {
						restart();
					} else {
						fclose( log_fp);
						exit( 0);
					}
				} else {
					open_connection( hostname, port, timeout);
				}
			} else {
				inet_rq_get_tcb( ircb);
			}
			break;

		case ORC_GET_TCB:
			if( rq_erc == ERC_OK) {
				if( ircb>tcb.state == ESTABLISHED) {
					close_connection( INETCT_CLOSE);
				} else {
					if( (ircb>tcb.state < ESTABLISHED) 
						(ircb>tcb.state = CLOSED)) {
						inet_rq_get_tcb( ircb);
					}
				}
			} else {
					* No need to close ABORTED connections *
				if( rq_erc = ERC_APP_RQ_ABORTED) {
					close_connection( INETCT_ABORT);
				}
				if( log_fp = stdout || ferrors_only) {
					fprintf( log_fp, hold_line);
				}
				fprintf( log_fp, FAILED\n\t\ts\n, message_erc(rq_erc));
				if( ferrors_beep) {
					beep();
				}
			}
			break;

		case ORC_ABORT_TCP_CONNECTION:
			++failed;
			if( ( erc = next_hostname( hostname, timeout, port)) = ERC_OK) {
				fprintf( log_fp, u Succeded, u Failed\n, 
								 succeeded, failed);
				if( fcontinuous) {
					restart();
				} else {
					fclose( log_fp);
					exit( 0);
				}
			} else {
				open_connection( hostname, port, timeout);
			}
			break;

		case ORC_CLOSE_TCP_CONNECTION:
			++succeeded;
			if( ferrors_only) {
				if( log_fp = stdout) {
					fprintf( log_fp, hold_line);
				}
				fprintf( log_fp, OK (u)\n, ticks);
			}
			if( ( erc = next_hostname( hostname, timeout, port)) = ERC_OK) {
				fprintf( log_fp, u Succeded, u Failed\n, 
								 succeeded, failed);
				if( fcontinuous) {
					restart();
				} else {
					fclose( log_fp);
					exit( 0);
				}
			} else {
				open_connection( hostname, port, timeout);
			}
			break;
	}
}
*======================.=========< Restart >================================*
void restart()
{
	fprintf( stdout, \nDelaying u seconds . . . , period);
	fflush( stdout);
	start_timer( restart_timer);
	succeeded = 0;
	failed = 0;
}
*======================.======< Handle Restart >============================*
void handle_restart( dummy)

char					* dummy;
{
	fprintf( stdout, Done\n);
	fflush( stdout);
	rewind( fp);
	if( next_hostname(hostname,timeout,port) == ERC_OK){
		put_banner();
		open_connection( hostname, port, timeout);
	} else {
		fclose( log_fp);
		exit( 0);
	}
}
*======================.======< Next Hostname >=============================*
unsigned int next_hostname( hostname, ptimeout, pport)

char					* hostname;
unsigned int			* ptimeout;
unsigned int			* pport;
{
	unsigned int		count;

	do{
		if( fgets( hold_line, sizeof(hold_line), fp) == NULL) {
			return( ERC_EOF);
		} else {
			if( *hold_line == '') {
				count = 0;
				continue;
			}
			count = sscanf( hold_line, s u u, hostname, ptimeout, pport);
			switch( count) {
				case EOF:
					count = 0;
					break;
				case 0:
					break;
				case 1:
					*ptimeout = 10;			* FALL THROUGH *
				case 2:
					*pport = TCP_ECHO_PORT;
					break;
			}
		}
	} while( count == 0);

	timeout_timer.start = *ptimeout * ONE_SECOND;
	return( ERC_OK);
}
*======================.=======< Put Banner >==============================*
void put_banner()
{
	unsigned long int	sysdt;
	unsigned int		cbret;
	static char			format[] = 0y0o0d0t:0m:0s;

	memcpy( version, sbverrun[1], min( sbverrun[0], (sizeof(version)1)));
	GetDateTime( sysdt);
	NlsFormatDateTime( NULLCHAR, sysdt, format, strlen( format),
					   hold_line, sizeof( hold_line) 1, cbret);
	hold_line[cbret] = '\0';
	fprintf( log_fp, \ns s s\n, version, user_string, hold_line);
}
*======================.=======< Initialize >==============================*
void initialize()
{
	unsigned int		i;
	unsigned int		erc;

	get_param_string( 2, 0, log_filename, MAX_FILENAME);
	if( ( log_fp = fopen( log_filename, a+)) == NULL) {
		log_fp = stdout;
	}
	get_param_string( 1, 0, node_filename, MAX_FILENAME);
	if( ( fp = fopen( node_filename, r)) == NULL) {
		fprintf( log_fp, NO FILE\n);
		fclose( log_fp);
		exit( 203);
	}
	get_param_string( 3, 0, user_string, 80);
	get_param_yes_no( 4, 0, ferrors_only);
	get_param_yes_no( 5, 0, ferrors_beep);
	get_param_yes_no( 6, 0, fcontinuous);
	get_param_unsigned( 7, 0, period);

	CheckErc( message_initialize( msg_file, erc_file, 2048, 1024, 256));

	CheckErc( AllocExch( exch));
	GetUserNumber( user_number);

	kbd.rq.usernum = user_number;
	kbd.rq.scntinfo = 6;
	kbd.rq.rtcode = 0;
	kbd.rq.nreqpbcb = 0;
	kbd.rq.nresppbcb = 1;
	kbd.rq.exchresp = exch;
	kbd.rq.ercret = 0;
	kbd.rq.rqcode = READ_KBD;
	kbd.key = key;
	kbd.skey = 1;

	put_banner();

		* Get our host name.  It will be the default target if no host name is
		 * provided via the first parameter.  Go ping yourself?
		 *
	checkerc( tcpgetsetup( ip_addr, mss, window, ttl,
						   hostname, HOSTNAMELEN, shostnameret));


		* Start the Real Time Clock (RTC) OS timer.  The minimum granularity 
		 * such a timer is 110 of a second.  The third parameter, tick, is 
		 * name of a function called by the os timer at each tick of the for 
		 * the clock.
		 *
	os_start_timer( TIMER_GRANULARITY, exch, tick);

		* The timeout timer defines when to consider the response to a 
		 * request overdue.  When this happens the connection is aborted and 
		 * we start again.  Every time the timer goes off it calls 
		 * handle_timeout().
		 *
	timeout_timer.func = handle_timeout;
	timeout_timer.arg = NULLCHAR;

		* Ticks are used to count tenths of a second for timing the requests. 
		 * The tick timer period is set to one os_timer period.  In this case 
		 * 110 of a second.  Every time the timer goes off it calls 
		 * handle_ticks().
		 *
	tick_timer.start = 1;
	tick_timer.func = handle_ticks;
	tick_timer.arg = NULLCHAR;

	restart_timer.func = handle_restart;
	restart_timer.arg = NULLCHAR;
	restart_timer.start = period * ONE_SECOND;

	start_timer( tick_timer);

	printf( \377PF);					* turn off video pause facility *

	request( kbd);						* start reading the keyboard *

	
	if( ( erc = next_hostname( hostname, timeout, port)) = ERC_OK) {
		fprintf( log_fp, Problem with node name file s\n, 
						 message_erc( erc));
		fclose( log_fp);
		exit( erc);
	} else {
		open_connection( hostname, port, timeout);
	}
}
*======================.==========< Main >=================================*
unsigned int main()
{
	IRCB				* ircb;
	unsigned int		erc;
	
	initialize();

	for(;;) {
		checkerc( wait( exch, rq));

		rq_code = rq>rqcode;
		rq_erc = rq>ercret;
		
		switch( rq>rqcode) {
			case OPENRTCLOCK:
				os_service_timer();
				break;
			case READ_KBD:
				if( (key == KEY_CANCEL) || (key == KEY_FINISH)) {
					if( connection_active) {
						close_connection( INETCT_ABORT);
						stop_timer( timeout_timer);
						if( log_fp = stdout || ferrors_only) {
							fprintf( log_fp, hold_line);
						}
						fprintf( log_fp, Aborting\n);
						fflush( log_fp);
					}
					exit( 0);
				} else {
					request( kbd);
					beep();
				}
				break;
			default:
				if( ( ircb = address_of_ircb( rq)) == NULLIRCB) {
					continue;
				}
				handle_requests( rq_code, rq_erc, ircb);
				break;
		}
	}
}

void handle_restart( dummy)

char					* dummy;
{
	fprintf( stdout, Done\n)