/* Automatic SLIP/PPP line redialer.
 * Very simpleminded - If a specified interval goes by without any
 * receiver activity, ping the other end. If this repeatedly fails,
 * spit a file containing modem commands at the line and try again.
 *
 * Copyright 1991 Phil Karn, KA9Q
 */
#include <stdio.h>
#include "global.h"
#include "mbuf.h"
#include "iface.h"
#include "8250.h"
#include "asy.h"
#include "session.h"
#include "proc.h"
#include "tty.h"
#include "socket.h"
#include "commands.h"
#include "netuser.h"
#include "timer.h"

static int redial __ARGS((struct iface *ifp,char *file));
static void monitor __ARGS((int s,void *v1,void *v2));

int
dodialer(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	int s;
	struct iface *ifp;
	int32 target;
	int32 interval;
	int dev;
	struct asy *ap;

	if((ifp = if_lookup(argv[1])) == NULLIF){
		tprintf("Interface %s unknown\n",argv[1]);
		return 1;
	}
	for(dev=0,ap = Asy;dev < ASY_MAX;dev++,ap++)
		if(ap->iface == ifp)
			break;
	if(dev == ASY_MAX){
		tprintf("Interface %s not asy port\n",argv[1]);
		return 1;
	}
	if((interval = atol(argv[2])) == 0){
		if(ifp->supv != NULLPROC){
			alert(ifp->supv,1);
			tprintf("dialer terminated on %s\n",argv[1]);
		} else {
			tprintf("no dialer active on %s\n",argv[1]);
		}
		return 0;
	}
	if(argc < 5){
		tprintf("not enough args\n");
		return -1;
	}
	if(ifp->supv != NULLPROC){
		tprintf("dialer already active on %s\n",argv[1]);
		return 1;
	}
	if((target = resolve(argv[3])) == 0){
		tprintf(Badhost,argv[3]);
		return 1;
	}
	ifp->supv = Curproc;

	for(;;){
		if((s = pause(interval*1000)) != 0){	/* Wait a while */
			break;		/* We're being terminated */
		}
		if(secclock() - ifp->lastrecv > 4*interval){
			/* Receiver has remained idle after four pings;
			 * assume the connection has dropped.
			 */
			if(redial(ifp,argv[4]) != 0)
				break;
		}
		if(secclock() - ifp->lastrecv > interval){
			/* Receiver has been idle since last check */
			if((s = socket(AF_INET,SOCK_RAW,IPPROTO_ICMP)) == -1)
				continue;	/* Wait and try again */
			pingem(s,target,0,(int16)s,0);
			close_s(s);
		}
	}
	ifp->supv = NULLPROC;
	return 0;
} 
static int
redial(ifp,file)
struct iface *ifp;
char *file;
{
	FILE *fp;
	struct mbuf *bp;
	int32 delay;
	int c;
	int (*rawsave) __ARGS((struct iface *,struct mbuf *));
	struct proc *mon;
	int err_ret = 0;

	if((fp = fopen(file,READ_TEXT)) == NULLFILE){
		tprintf("redial: can't read %s\n",file);
		return -1;	/* Causes dialer proc to terminate */
	}
	/* Save output handler and temporarily redirect output to null */
	if(ifp->raw == dumppkt){
		tprintf("redial: tip or dialer already active on %s\n",ifp->name);
		return -1;
	}
	rawsave = ifp->raw;
	ifp->raw = dumppkt;

	/* Suspend the packet input driver. Note that the transmit driver
	 * is left running since we use it to send buffers to the line.
	 */
	suspend(ifp->rxproc);

	/* Start up monitor so we can see what's happening */
	mon = newproc("dial monitor",300,monitor,0,(void *)ifp,NULL,0);

	while((c = getc(fp)) != EOF){
		if(c == '\\'){
			/* delay escape */
			delay = 0;
			while(c = getc(fp),c != EOF && c != 'd'){
				delay = delay * 10 + (c-'0');
			}
			if(c == EOF)
				break;
			if(pause(delay) != 0){
				err_ret = 1;
				break;
			}
			continue;
		}
		if(c == EOF)
			break;	/* Normal termination */
		if(c == '\n')
			c = '\r';
		bp = pushdown(NULLBUF,1);
		bp->data[0] = c;
		asy_send(ifp->dev,bp);
	}
	fclose(fp);
	killproc(mon);
	ifp->raw = rawsave;
	resume(ifp->rxproc);
	return err_ret;
}
static void
monitor(s,v1,v2)
int s;
void *v1;
void *v2;
{
	int c;
	struct iface *ifp;

	ifp = (struct iface *)v1;
	while((c = get_asy(ifp->dev)) != -1){
		if(ifp->trace & IF_TRACE_IN)
			putchar(c & 0x7f);
	}
}
