/* IBM_PROLOG_BEGIN_TAG                                                   */
/* This is an automatically generated prolog.                             */
/*                                                                        */
/* bos720 src/bos/usr/bin/errlg/samples/syscatch/syscatch.c.S 1.1         */
/*                                                                        */
/* Licensed Materials - Property of IBM                                   */
/*                                                                        */
/* Restricted Materials of IBM                                            */
/*                                                                        */
/* COPYRIGHT International Business Machines Corp. 1995                   */
/* All Rights Reserved                                                    */
/*                                                                        */
/* US Government Users Restricted Rights - Use, duplication or            */
/* disclosure restricted by GSA ADP Schedule Contract with IBM Corp.      */
/*                                                                        */
/* IBM_PROLOG_END_TAG                                                     */
static char sccsid[] = "@(#)77	1.1  src/bos/usr/bin/errlg/samples/syscatch/syscatch.c.S, cmderrlg, bos720 7/28/95 15:51:40";
/*
 * COMPONENT_NAME: (CMDERRLG) Error catcher sample
 *
 * FUNCTIONS: Setup the modem and catch a call from an MP's
 *	Service processor
 *
 * ORIGINS: 27
 *
 * (C) COPYRIGHT International Business Machines Corp. 1995
 * All Rights Reserved
 * Licensed Materials - Property of IBM
 *
 * US Government Users Restricted Rights - Use, duplication or
 * disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
 */

#include <stdio.h>
#include <fcntl.h>
#include <sys/mode.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/devinfo.h>
#include <signal.h>
#include <sys/select.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/time.h>
#include <nl_types.h>
#include <rpc/rpc.h>
#include <sys/syslog.h>
#include <termios.h>
#include "sysguard.h"




/* function prototypes */
int		make_ready(char *tty_name, char *baud_rate);
void		get_initstr(char *msg);
void		monitor_modem(int filedesc);
int		process_call(int filedesc);
data_holder	*receive_prb_data(int fd);
char		*assemble_data(data_holder *first);
void 		freeholder(data_holder *first);
void		hangup(int filedesc);
void		send_nak();
int		initialize_port(int fd, int action, int baud);
void		exit_syscatch();


/* globals */
int	gfd;
int	nak_cnt;
int	nak_err;
char	gport[256];
char	gbaud[256];

main(argc, argv)
int	argc;
char	*argv[];
{
	int		filedesc;

	if(argc < 3)
	{
		fprintf(stderr,"Usage: sysguardcatch ttyX baud\n");
		exit(1);
	}
	if((filedesc = make_ready(argv[1], argv[2])) < 0)
	{
		exit(1);
	}
	strcpy(gbaud, argv[2]);

	signal(SIGTERM, exit_syscatch);
	signal(SIGDANGER, exit_syscatch);

	gfd = filedesc;
	monitor_modem(filedesc);

}
	
int		make_ready(char *tty_name, char *baud_rate)
{
	int	filedesc;
	int	baud;
	unsigned int	size;
	char	port[30];
	char	msg[256];
	int	i, rc;
	FILE	*fp;

	strcpy(port, "/dev/");
	strcat(port, tty_name);
	strcpy(gport, port);        /* make port name global */
	fprintf(stderr,"Making port %s ready\n", port);
	/* first attempt to lock the port for our exclusive use */
	if(ttylock(port) < 0)
	{
		fprintf(stderr,"Unable to lock port %s for syscatch.\n",
					port);
		fprintf(stderr,"Device may be in use by another process.\n");
		fprintf(stderr,"Should we try anyway? (Y or [N]) ");
		scanf("%c", msg);
		if(msg[0] != 'Y')
			return(DATA_ERR_RETURN);
	}
	filedesc = open(port, O_RDWR | O_NONBLOCK, 0);
	if(filedesc < 0)
	{
		fprintf(stderr,"%s %s %s\n", 
			"Device open returned",
			strerror(errno), tty_name);
		return(DATA_ERR_RETURN);
	}

	baud = atoi(baud_rate);
	initialize_port(filedesc, CLOCAL, baud);

	/* Initialize the modem. */
	alarm(10);
	get_initstr(msg);

	/* Write init to modem */
	fprintf(stderr,"Modem Init string is %s\n",msg);
	rc = write(filedesc, msg, strlen(msg));
	if(rc < 0)
	{
		if(errno == EINTR)
		{
			fprintf(stderr,"Time out writing init string\n");
		}
		else
		{
			fprintf(stderr,"Failure writing init string, %s\n", strerror(errno));
		}
		ttyunlock(port);
		exit(1);
	}
	
		
	alarm(0);    /* turn off the alarm */
			
	sleep(1);
	initialize_port(filedesc, 0, baud);


	/* drop DTR, hang up the phone */
	if(ioctl(filedesc, TIOCMBIC, TIOCM_DTR) < 0)
	{
		fprintf(stderr,"Turn OFF of DTR failure");
		return(DATA_ERR_RETURN);
	}
	sleep(1);	/* pause briefly */
		
	/* reassert DTR, make modem ready to answer incoming calls */
	if(ioctl(filedesc, TIOCMBIS, TIOCM_DTR) < 0)
	{
		fprintf(stderr,"%s %s\n", "Turn on DTR failure", 
				strerror(errno));
		return(DATA_ERR_RETURN);
	}
	sleep(1);	/* pause briefly */
		

	return(filedesc);

}

void
get_initstr(char *str)
{
#define INITFILE "syscatch_init"
#define DFLT_INITSTR "ATE0 Q1 &C1 &D2\r"
	int i;
	FILE *fp;

	i = 0;	/* 0 will mean nothing was read in. */

	if((fp = fopen(INITFILE, "r")) != NULL)
	{
		while(fgets(str,255,fp)) {
			/* Check for a comment line */
			if ((str[0] != '\0') && (str[0] != '\n') && (str[0] != '#'))
			{	/* Not null or a comment, use it. */
				strcat(str, "\n");
				while((i < 256) && (str[i] != '\0'))
				{
					if(str[i] == '\n')
						str[i] = '\r';
					i++;
				}
				break;
			}
		}

		/* Make sure we got a message */
		if (i==0) {
			fprintf(stderr,"Could not find an initialization string.\n  Using %s\n",DFLT_INITSTR);
			strcpy(str,DFLT_INITSTR);
		}
		fclose(fp);
	}
	else {	/* Didn't open */
		perror(INITFILE);
		fprintf(stderr,"Using %s\n",DFLT_INITSTR);
		strcpy(str,DFLT_INITSTR);
	}
}

void	monitor_modem(int filedesc)
{
	int		status;
	int		rc;

	while(1)            /* loop forever */
	{

		if(ioctl(filedesc, TIOCMGET, &status) < 0)
		{
			fprintf(stderr,"ERROR on ioctl\n");
			return;
		}	
		if((status & TIOCM_CAR) != 0)
		{
#ifdef DEBUG
			fprintf(stderr,"Incoming call detected\n");
#endif
			if(process_call(filedesc) != NORMAL_RETURN)
			{ 
				hangup(filedesc);
				sleep(5);
			}
		}
		else
		{
			sleep(2);
		}
	}

}
int	process_call(int filedesc)
{
	data_holder	*first_data;
	sysguard_prob_type	*sysguard_prob_data;
	char		*problem_data;
	unsigned int	magic;


	first_data = receive_prb_data(filedesc);

	/* don't hangup yet */

	if(first_data != NULL)
	{
		problem_data = (char *)assemble_data(first_data);

	}
	else	/* we got an error */
	{
		return(DATA_ERR_RETURN);		/* can't do much without the data */
	}

	memcpy(&magic, problem_data, sizeof(unsigned int));
	if(magic == BUMP_MAGIC_NUMBER)
	{
		hangup(filedesc);	/* sysguard can't receive status */
		sysguard_prob_data = (sysguard_prob_type *)problem_data;

		/* now print the received data out */
		printf("\nSysguard problem reported:\n");
		printf("Login ID:  %12.12s Password: %16.16s\n",
			sysguard_prob_data->login_id,
			sysguard_prob_data->passwd);
		printf("Voice Phone: %20.20s Dial In: %20.20s\n",
			sysguard_prob_data->voice_phone,
			sysguard_prob_data->callback);
		printf("Timestamp: %8.8s      Error Code: %4.4s\n",
			sysguard_prob_data->time,
			sysguard_prob_data->error_code);
		printf("Type:  %4.4s  Model: %3.3s  Serial: %5.5s\n",
			sysguard_prob_data->device_type,
			&sysguard_prob_data->device_type[4],
			&sysguard_prob_data->device_type[8]);
		printf("Uname: %10.10s\n",
			sysguard_prob_data->serial_no);
		printf("SRN: %64.64s\n",
			sysguard_prob_data->SRN_LCD);
		printf("%64.64s\n", sysguard_prob_data->abstract);
		

		free(sysguard_prob_data);
		return(NORMAL_RETURN);
	}
	else
	{
		fprintf(stderr,"Invalid magic number\n");
		hangup(filedesc);	/* sysguard can't receive status */
		free(problem_data);
		return(NORMAL_RETURN);
	}

}
data_holder	*receive_prb_data(int fd)
{
	char	ch;
	char	buffer[BUFSIZE];
	char	ackbuff[10];
	int	i, j, errcount;
	int	testcnt;
	int	k;
 	int 	sect;                  /* sector number received */
	int	total_sects;
	int	rc;
	data_holder	*first;
	data_holder	*current;

	

	sleep(1);
	ch = NAK;
	sect = 1;           /* start with sector 1 */
	total_sects = 0;
	first = NULL;
	testcnt = 0;

	if(write(fd, &ch, 1) < 0)
	{
		fprintf(stderr,"Write NAK failure\n");
		return(NULL);
	}
	do
	{
		
		nak_cnt = 0;
		signal(SIGALRM, send_nak);
		alarm(NAK_INTERVAL);
		nak_err = 0;
		while(ch != SOH)
		{
			if(nak_err)
			{
				alarm(0);
				fprintf(stderr,"Aborting Transfer\n");
				sleep(10);
				return(NULL);
			}
			if(read(fd, &ch, 1) < 0)
			{
				if(nak_cnt > MAX_NAKS)
				{
					fprintf(stderr,"No data received, timing out\n");
					return(NULL);
				}
				if(errno == EAGAIN)
				{
					continue;   /* no data was available */
				}
				fprintf(stderr,"%s %s\n", 
					"error reading port",
					strerror(errno));
				return(NULL);
			}
			ch = ch & RECEIVE_MASK;
			if(ch == EOT)
			{
#ifdef DEBUG
				fprintf(stderr,"Download completed\n");
#endif
				ch = ACK;
#ifdef FAULT_TOLERANCE
				memset(ackbuff, ch, 10);
				write(fd, ackbuff, 10);
#else
				write(fd, &ch, 1);
#endif
				sleep(1);
				alarm(0);
				return(first);
			}
			else
			{
#ifdef DEBUG 
				fprintf(stderr,"%x ", ch);
				testcnt++;
				if(testcnt > 20)
				{
					testcnt = 0;
					fprintf(stderr,"\n");
				}
#endif
			}
		}
#ifdef DEBUG
		fprintf(stderr,"SOH received\n");
#endif
		
		if(sect > MAX_SECTORS)
		{
			fprintf(stderr,"Maximum number of sectors exceeded");
			return(first);
		}

		i = 0;
		errcount = 0;
		alarm(0);
		do
		{  
			
			j = read(fd,&buffer[i],(BUFSIZE - i)); 
			if(j > 0)    /* if data was received */
			{
				for(k = i; k < (i + j); k++)
					buffer[k] &= RECEIVE_MASK;
				
				i = i+j;
			}
			if(j <= 0) 
			{
				if(errno != EAGAIN)   /* don't count this error */
					errcount++;
			}
			if(errcount > 10)
			{
				fprintf(stderr,"Read error maximum exceeded\n");
				return(NULL);
			}

	       } while (i < BUFSIZE);

		if((rc = perform_checks(sect, buffer, 1)) == NORMAL_RETURN)
		{
			if(total_sects == 0)
			{
				first = (data_holder *)malloc(sizeof(data_holder));
				if(first == NULL)
				{
					fprintf(stderr,"Unable to malloc space for first sector\n");
					return(NULL);
				}
				first->prev = NULL;
				current = first;
			}
			else
			{
				current->nxt = (data_holder *)malloc(sizeof(data_holder));
				if(current->nxt == NULL)
				{
					fprintf(stderr,"Unable to malloc space for subsequent sector\n");
					freeholder(first); /* Free storage */
					return(NULL);
				}
				current->nxt->prev = current;
				current = current->nxt;
				current->nxt = NULL;
			}
			memcpy(current->buffer, &buffer[2], SECSIZE);
			ch = ACK;
			sect++;
			total_sects++;
			if(sect > 255)    /* make this work with big data */
			{
				sect = 1;
			}
		}
		else if(rc == DATA_ERR_RETURN)
		{
			ch = NAK;
		}
		else
		{
			ch = ACK;     /* used for duplicate sectors only */
		}
#ifdef FAULT_TOLERANCE
		memset(ackbuff, ch, 10);
		if(write(fd, ackbuff, 10) < 0)
#else
		if(write(fd, &ch, 1) < 0)
#endif

		{
			fprintf(stderr,"Unable to write acknowledgment\n");
			freeholder(first); /* Free storage */
			return(NULL);
		}

	} while(1);

	

}
char	*assemble_data(data_holder *first)
{
	char		*data;
	data_holder	*next;
	int		i;
	int		j;
	int		sectors;

	/* first count the number of sectors */
	sectors = 0;
	for(next = first; next != NULL; next = next->nxt)
		sectors++;
	
	data = (char *)malloc((sectors + 1) * SECSIZE * sizeof(char));

	
	for(i = 0, next = first; next != NULL; 
				i++)
	{
		for(j = 0; j < SECSIZE; j++)
		{
			if(next->buffer[j] == '')
			{
				next->buffer[j] = '\0';
			}
		}
		memcpy(&data[i * SECSIZE], next->buffer, SECSIZE);
	
		next = next->nxt;
	}
	/* now free the linked list */
	freeholder(first);
	return(data);

}
/* Free the data_holder chain */
void freeholder(data_holder *first)
{
#ifdef DEBUG
	fprintf(stderr,"Freeing data_holders\n");
#endif
	for (; first; first=first->nxt) free(first);
}

void	hangup(int filedesc)
{
	int		status;
	int		baud;
	int		attempts;

	/* drop DTR, hang up the phone */

#ifdef DEBUG
	fprintf(stderr,"Running hangup\n");
#endif
	alarm(0);	/* turn off alarm clock */
	if(ioctl(filedesc, TIOCMBIC, TIOCM_DTR) < 0)
	{
		fprintf(stderr,"Turn OFF of DTR failure while hanging up\n");
		return;
	}
	sleep(1);
	if(ioctl(filedesc, TIOCMBIS, TIOCM_DTR) < 0)
	{
		fprintf(stderr,"Turn ON of DTR failure while hanging up\n");
		return;
	}
	sleep(1);
	if(ioctl(filedesc, TIOCMGET, &status) < 0)
	{
		fprintf(stderr,"ERROR on ioctl\n");
		return;
	}	
	attempts = 0;
	while((status & TIOCM_CAR) != 0)
	{
		attempts++;
		if(attempts > 5)
		{
			exit_syscatch();
		}
#ifdef DEBUG
		fprintf(stderr,"Failure to hang up, exiting...\n");
		fprintf(stderr,"We were unable to successfully hang up the phone.\n");
		fprintf(stderr,"It may be necessary to hang up manually and restart.\n");
		fprintf(stderr,"Attempting software hangup sequence\n");
#endif
		baud = atoi(gbaud);
		initialize_port(filedesc, CLOCAL, baud);
		write(filedesc, "+++", 3);
		sleep(2);
		write(filedesc, "ATH\r", 4);
		sleep(2);
		initialize_port(filedesc, 0, baud);
		

		if(ioctl(filedesc, TIOCMBIC, TIOCM_DTR) < 0)
		{
		}
		if(ioctl(filedesc, TIOCMGET, &status) < 0)
		{
			fprintf(stderr,"ERROR on ioctl\n");
			return;
		}	
	}

}
void	send_nak()
{
	char	ch;

	ch = NAK;
	if(write(gfd, &ch, 1) < 0)
	{
		fprintf(stderr,"Error sending NAK\n");
		nak_err = 1;
		return;
	}
	nak_cnt++;
	signal(SIGALRM, send_nak);
	alarm(NAK_INTERVAL);
}
int		initialize_port(int fd, int action, int baud)
{

	int			i;
	struct termios		tty_ctrl;

	tty_ctrl.c_iflag = IGNPAR | IGNBRK;		/* ignore parity errors */
	tty_ctrl.c_oflag = 0;			/* clear output flag */
	tty_ctrl.c_cflag = CREAD;
	tty_ctrl.c_cc[VINTR] = -1; 
	tty_ctrl.c_cc[VQUIT] = -1;
	tty_ctrl.c_cc[VERASE] = -1;
	tty_ctrl.c_cc[VKILL] = -1;
	tty_ctrl.c_cc[VMIN] = 1;		/* minimum number of characters to be read */
	tty_ctrl.c_cc[VTIME] = 0;
	tty_ctrl.c_cc[VEOL2] = -1;

	for(i = VSUSP; i < NCCS; i++)
		tty_ctrl.c_cc[i] = -1;
	
	switch(baud)
	{
		case 1200:
			tty_ctrl.c_cflag |= B1200;
			break;
		case 2400:
			tty_ctrl.c_cflag |= B2400;
			break;
		case 4800:
			tty_ctrl.c_cflag |= B4800;
			break;
		case 9600:
			tty_ctrl.c_cflag |= B9600;
			break;
		case 19200:
			tty_ctrl.c_cflag |= B19200;
			break;
		case 38400:
			tty_ctrl.c_cflag |= B38400;
			break;
		default:
			tty_ctrl.c_cflag |= B9600;    	/* set baud */
	}
	tty_ctrl.c_cflag |= CS8;		/* 8 data bits */
	tty_ctrl.c_cflag &= ~PARENB;		/* no parity */


	tty_ctrl.c_lflag &= ~ECHO;		/* don't echo */

	tty_ctrl.c_iflag &= ~(IXON+IXOFF);       /* turn off software flow control */
	tty_ctrl.c_cc[VSTART] = (char)0x11;     /* ctrl-Q */
	tty_ctrl.c_cc[VSTOP] = (char)0x13;      /* ctrl-S */
	if(action == CLOCAL)
		tty_ctrl.c_cflag |= CLOCAL;
	if(action == HUPCL)
		tty_ctrl.c_cflag |= HUPCL;
	
	return(tcsetattr(fd, TCSANOW, &tty_ctrl));

}
int	perform_checks(int sector, char *buffer, int log)
{
	int	sectornum;      /* received sector number */
	int	sectornumcomp;  /* 1's complement of sector number */
	int	checksum, calsum;	/* received and calculated checksums */
	char	*bufptr;
	char	msgbuf[80];

	sectornum = buffer[0] & RECEIVE_MASK;
	sectornumcomp = buffer[1] & RECEIVE_MASK;

	checksum = buffer[BUFSIZE - 1] & RECEIVE_MASK;

	/* first check sector numbers */
	if((sectornum + sectornumcomp) != RECEIVE_MASK)
	{
		if(log)
		{
			fprintf(stderr,"Invalid sector and complement\n");
		}
		else
		{
			fprintf(stderr, "Invalid sector and complement\n");
		}
		return(DATA_ERR_RETURN);
	}
	if(sectornum < sector)    /* did we already get this one? */
	{
#ifdef DEBUG
		fprintf(stderr,"sectornum = %d, sector = %d\n",
			sectornum, sector);
#endif
		if(log)
		{
			fprintf(stderr,"Duplicate sector received, expected %d, received %d",
				sector, sectornum);
		}
		else
		{
			fprintf(stderr, "Duplicate sector\n");
		}
		return(DUP_RETURN);   /* just go on */
	}
	if(sectornum > sector)
	{
		if(log)
		{
			fprintf(stderr,"Sector missing\n");
		}
		else
		{
			fprintf(stderr, "Sector Missing\n");
		}
		return(DATA_ERR_RETURN);
	}

	/* calculate the checksum */
	calsum = 0;
	bufptr = &buffer[2];
	while(bufptr < &buffer[2] + SECSIZE)
	{
		*bufptr = *bufptr & RECEIVE_MASK;   /* keep it to eight bits */
		calsum = (calsum + *bufptr);
		bufptr++;
	}
	calsum &= RECEIVE_MASK;
	if(checksum != calsum)
	{
		if(log)
		{
			fprintf(stderr,"Checksum error\n");
		}
		else
		{
			fprintf(stderr, "Checksum error\n");
		}
		return(DATA_ERR_RETURN);
	}
	

	return(NORMAL_RETURN);
}
void	exit_syscatch()
{
	if(ioctl(gfd, TIOCMBIC, TIOCM_DTR) < 0)
	{
		fprintf(stderr,"Turn OFF of DTR failure while hanging up\n");
	}

	close(gfd);
	if(ttyunlock(gport) < 0)
	{
		fprintf(stderr,"Unable to unlock port\n");
	}
	else
	{
		fprintf(stderr,"Port %s unlocked, exiting.....\n", gport);
	}

	exit(0);
}
