#ifndef lint
static char *sccsid = "%W%	MS/ACF	%E%";
#endif
#ifdef SUNOS41 /* wht@n4hgf2 */
#include <stdio.h>
runslip()
{
	fprintf(stderr,"Sorry, not working in this OS\n");
	return(1);
}
abort_slip()
{
}
#else /* wht@n4hgf2 */
/*
 *  tip/slip.c
 *
 *  This file contains the SLIP specific startup code.  It is designed
 *  with both BSD sockets and Sun Streams in mind, though the socket
 *  version has not been tested lately (but it SHOULD work).  The Streams
 *  code is for SunOS 4.0[.1].
 *
 *  The code to set the route (basically an internal shell script) may
 *  need tweeking on other systems, though it works on any of the
 *  systems I have run into.
 *
 *  The alarm/DCD code is a HACK to detect loss of the line in the
 *  absense of proper modems status signaling (broken in SunOS when
 *  using the dialout device, serial line minor devices 128-255).
 *  This has been reported, but I suspect that a fix will have to
 *  wait for SunOS 4.2 or maybe 4.1.?.
 *
 *  Doug Kingston <dpk@morgan.com>
 *  880219
 */

#include "tip.h"

#include <sys/socket.h>
#ifdef STREAMS
#include <sys/stropts.h>
#include <sys/sockio.h>
#include <sys/termios.h>	/* has defines that clash with ioctl.h */
#include <sys/slip.h>
#endif STREAMS
#include <netinet/in.h>
#include <net/if.h>

#include <arpa/inet.h>

#define	DCD_SETTLING_TIME 1	/* time between DCD change and status check */
#define	DCD_CHECK_INTERVAL 15	/* if > 0, time between automatic DCD checks */

int gotsig = 0;
sigfunc_t dcd_handler();

runslip()
{
	int unit, s;
	struct ifreq ifr;
#ifdef STREAMS /* Using streams */
	struct termios tios;

	/* pop all streams modules */
	while (ioctl(FD, I_POP, 0) == 0)
		continue;

	/* set up the line parameters */
	if (ioctl(FD, TCGETS, (caddr_t)&tios) < 0) {
		perror("ioctl (TCGETS)");
		return(1);
	}
	tios.c_cflag = (tios.c_cflag&(CBAUD|CIBAUD))|CS8|CREAD|HUPCL;
	tios.c_iflag = IGNBRK;
	if (ioctl(FD, TCSETS, (caddr_t)&tios) < 0) {
		perror("ioctl (TCSETS)");
		return(1);
	}

	/* push the SLIP module */
	if (ioctl(FD, I_PUSH, "slip") < 0) {
		perror("ioctl (I_PUSH)");
		return(1);
	}

	/* find out what unit number we were assigned */
	if (ioctl(FD, SLIOGUNIT, (caddr_t)&unit) < 0) {
		perror("ioctl (SLIOGUNIT)");
		return(1);
	}
#else /* not streams */
	int ldisc = SLIPDISC;
	if (ioctl(FD, TIOCSETD, &ldisc) < 0) {
		perror("slipd: TIOCSETD");
		return(1);
	}
	if (ioctl(FD, TIOCGETD, &unit) < 0) {	/* Hack to get slip number */
		perror("slipd: TIOCGETD");
		return(1);
	}
#endif I_POP

	/* set the local and remote interface addresses */
	s = socket(AF_INET, SOCK_DGRAM, 0);

	(void) sprintf(ifr.ifr_name, "slip%d", unit);
	if (in_getaddr(DA, &ifr.ifr_addr) != 0 ||
	    ioctl(s, SIOCSIFDSTADDR, (caddr_t)&ifr) < 0) {
		perror("ioctl (SIOCSIFDSTADDR)");
		return(1);
	}

	/* this has the side-effect of marking the interface up */
	if (in_getaddr(SA, &ifr.ifr_addr) != 0 ||
	    ioctl(s, SIOCSIFADDR, (caddr_t)&ifr) < 0) {
		perror("ioctl (SIOCSIFADDR)");
		return(1);
	}

	if (in_getaddr(SM, &ifr.ifr_addr) != 0) {
		if (ioctl(s, SIOCSIFNETMASK, (caddr_t)&ifr) < 0) {
			perror("ioctl (SIOCSIFADDR)");
			return(1);
		}
	}

	/* Set up default route if desired and not already present */
	if (boolean(value(SETROUTE))) {
		char buf[256];
		char *argv[4];
		char *envp[2];
		int status;

		sprintf(buf, "if eval 'netstat -r|grep -s \"^default\"';\
			      then echo default route already installed;\
			      else route add 0 %s 3; fi", SA);
		argv[0] = "/bin/sh";
		argv[1] = "-c";
		argv[2] = buf;
		argv[3] = (char *)0;

		envp[0] = "PATH=/bin:/usr/bin:/usr/ucb:/etc:/usr/etc";
		envp[1] = (char *)0;

		switch (fork()) {
		case 0:		/* Child */
			execve(argv[0], argv, envp);
			exit(1);
		case -1:
			perror("tip: route add: fork");
			break;
		default:	/* Parent */
			wait(&status);
		}
	}

	fprintf(stderr, "\07[SLIP running]\r\n");

	/* set up signal handlers */
	(void) sigblock(sigmask(SIGALRM));
	(void) signal(SIGALRM, dcd_handler);
#if defined(SIGDCD) && SIGDCD > 0
	(void) signal(SIGDCD, dcd_handler);
#endif

	/* twiddle thumbs until we get a signal */
	while (1) {
		alarm(DCD_CHECK_INTERVAL);
		sigpause(0);
#if defined(SIGDCD) && SIGDCD > 0
		(void) sigblock(sigmask(SIGALRM)|sigmask(SIGDCD));
#else
		(void) sigblock(sigmask(SIGALRM));
#endif SIGDCD
		if (gotsig && lowdcd(FD)) {
			sleep(DCD_SETTLING_TIME);
			if (lowdcd(FD))
				break;
		}
		gotsig = 0;
	}
	return(0);
}

abort_slip()
{
#ifdef STREAMS
	/* pop the SLIP stream module */
	while(ioctl(FD, I_POP, 0) == 0);
#else
	int ldisc = 0;
	(void)ioctl(FD, TIOCSETD, &ldisc);
#endif
}

sigfunc_t
dcd_handler()
{
	gotsig = 1;
}

/* Use TIOCMGET to test if DCD is low on the port of the passed descriptor */
int
lowdcd(fd)
	int fd;
{
	int mbits;

	if (ioctl(fd, TIOCMGET, (caddr_t)&mbits) < 0)
		return 1;	/* port is dead, we die */
	return !(mbits & TIOCM_CAR);
}

in_getaddr(s, saddr)
	char *s;
	struct sockaddr *saddr;
{
	register struct sockaddr_in *sin = (struct sockaddr_in *)saddr;
	struct hostent *hp;
	struct netent *np;
	int val;
 
	sin->sin_family = AF_INET;
	val = inet_addr(s);
	if (val != -1) {
		sin->sin_addr.s_addr = val;
		return(0);
	}
	hp = gethostbyname(s);
	if (hp) {
		sin->sin_family = hp->h_addrtype;
		bcopy(hp->h_addr, (char *)&sin->sin_addr, hp->h_length);
		return(0);
	}
	np = getnetbyname(s);
	if (np) {
		sin->sin_family = np->n_addrtype;
		sin->sin_addr = inet_makeaddr(np->n_net, INADDR_ANY);
		return(0);
	}
	fprintf(stderr, "tip: in_getaddr: can't parse/lookup '%s'\n", s);
	return(1);
}
#endif /* wht@n4hgf2 */
