#!/usr/bin/perl

# NOTE!:  Before using this script, edit the file /etc/slipgetty.conf and
# change the "StartRemoteSlip" subroutine to suite your situation.

# This script is configured to run from init(8).  You will need to put
# a line such as this in /etc/ttys:
#
# com1	"/usr/local/lib/slipgetty /etc/slipgetty.conf"	unknown	on

sub Send {
	print @_[0];
	&DebugL("Sent: ",@_[0]);
}

sub Expect {
	local($s_in) = "";
	$wait_time = pop(@_) if ($#_ > $[);
	&DebugL("Looking for: ", @_ );
	local($qtime) = time + $wait_time;
	while (time < $qtime) {
		local($rin) = '';
		vec($rin,fileno(TTY),1) = 1;
		if (select($rin,undef,undef,1)) {
			if (sysread(TTY,$s_in,1,length($s_in)) < 1) {
				&DebugL("Error \"$!\" after: ",$s_in);
				return undef;
			}
			foreach $ss (@_) {
				if (index($s_in,$ss) >= 0) {
					&DebugL("Found ",$ss);
					&DebugL("   In ",$s_in);
					return $ss;
				}
			}
		}
	}
	&DebugL("Timeout after: ",$s_in);
	return undef;
}

sub Send_Expect_Timeout {
	&Send(shift);
	&Expect(@_);
}

sub Send_Expect_Timeout_Repeat {
	local($count) = pop @_;
	while (--$count > 0) {
		return 1 if (&Send_Expect_Timeout(@_));
	}
	return undef;
}


sub Sctty {
	if (syscall($SYS_setsid)) {
		&Debug("setsid()\n");
	} else {
		warn ("setsid(): \"$!\"");
	}
	if (ioctl(TTY, $TIOCSCTTY, 0)) {
		&Debug("Set controling tty...\n");
	} else {
		warn ("ioctl(TIOCSCTTY): \"$!\"");
	}
}

sub Hup {
        if ($can_syslog && $do_syslog)
        {
           &syslog('err', "SLIP caught HUP signal.");
        }
	&Debug("Caught signal!\n");
	&System('/bin/stty','-f',$tty,'tty','-clocal');
	&Exit(0);
}

sub Exit {
        if ($can_syslog && $do_syslog)
        {
           &syslog('err', "SLIP exiting.");
           &closelog();
        }
	&Debug("closing $tty\n");
	&System('/bin/stty','-f',$tty,'0');
	close(STDIN);
	close(STDOUT);
	close(STDERR);
	close(TTY);
	# sleep(10);
	exit @_;
}

sub StartLocalSlip {
	&Debug("Changing line discipline...\n");
	local($slipdisc) = pack("i",$SLIPDISC);
	local($inumstr) = '';
	if (!ioctl(TTY,$TIOCSETD,$slipdisc)) {
		warn("ioctl(TIOCSETD): \"$!\"");
		return undef;
	}
	if (!ioctl(TTY, $SLIOCGUNIT, $inumstr)) {
		warn(sprintf("ioctl(SLIOCGUNIT = 0x%08x): cant find interface number, \"$!\", using \"sl0\"", $SLIOCGUNIT));
		"sl0";
	} else {
		sprintf("sl%d",unpack("i",$inumstr));
	}
}

sub System {
	&DebugL("System: ",@_);
	if (system (@_) == 0) {
		return 1;
	} else {
		return undef
	};
}

sub Debug {
	printf STDERR @_;
}

sub DebugL {
	return 1 unless ($debug);
	local($sx) = shift . "\"";
	local($s);
	while (defined($s = shift)) {
		foreach $cx (split(//,$s)) {
			if (32 <= ord($cx) && ord($cx) <= 126) {
				$sx .= $cx
			} else {
				$sx .= sprintf("\\%03o",ord($cx));
			}
		}
		$sx .= "\" \"" if ($#_ >= $[);
	}
	&Debug("$sx\"\n");
}

sub CheckDefined {
	local($value,$default,$name,$file) = @_;
	$_[0] = &$name if(!$_[0] && defined(&$name));
	unless($_[0]) {
		$_[0] = $default;
		warn sprintf ("\$$name (normally found in $file) is undefined using 0x%x.\n",$_[0]);
	}
}

#
# main
#
$pause=			0;	# initial retry delay;
$debug=			0;
$wait_time=		5;
$do_syslog=             0;      # Do messages to syslog;
$can_syslog=            0;      # Sysloggin available or not;

#
# argument processing
#
$tty = "/dev/" . pop(@ARGV);
$conf = shift @ARGV;
$conf = "/etc/slipgetty.conf"
	unless (-f $conf);

do "sys/syscall.ph" || warn "Cant find sys/syscall.ph";
do "ioctl.pl" || warn "Cant find ioctl.pl";
do "sys/ioctl.ph" || do "ioctl.ph" || warn "Cant find either sys/ioctl.ph or ioctl.ph";
if ( (eval 'require "syslog.pl"', $@ eq '' ) )
{
   $can_syslog = 1;
}
else
{
   $can_syslog = 0;
   warn "Cannot find syslog.pl";     
}
require $conf;

if ($can_syslog && $do_syslog)
{
   &openlog("slipgetty",'cons,pid','daemon');
}

$SIG{'HUP'} = 'Hup';

if ($debug) {
	open(STDERR,">$debug");
	chown root, staff, "$debug" ||
		warn "Trouble chown'ing \"$debug\" to root";
	chmod 0400, "$debug" ||
		warn "Trouble chmod'ing \"$debug\" to 0400";
} else {
	open(STDERR,">/dev/null");
}
open(STDOUT,">/dev/null");	# for the benifit
open(STDIN,"</dev/null");	# of posterity...

#
# Double check that the above "do" commands really worked.
#
&CheckDefined($SYS_setsid,147,"SYS_setsid","sys/syscall.ph");
&CheckDefined($TIOCSETD,0x80047401,"TIOCSETD","ioctl.pl");
&CheckDefined($TIOCSCTTY,0x20007461,"TIOCSCTTY","ioctl.pl or sys/ioctl.ph");
&CheckDefined($SLIOCGUNIT,0x40047458,"SLIOCGUNIT","sys/if_slvar.ph");
&CheckDefined($SLIPDISC,4,"SLIPDISC","ioctl.pl or sys/ioctl.ph");

#
# Set line flags
#
&System("/bin/stty -f $tty 0");
&System("/bin/stty -f $tty $speed raw clocal");

#
# open line
#
&Debug("open(TTY,\"+>$tty\")\n");
unless (open(TTY,"+>$tty")) {
	warn ("Cant open $tty");
	&Exit(1);
}
select(TTY); $| = 1;

&Sctty;
sleep 1;	# XXX ??

if ($can_syslog && $do_syslog)
{
   &Debug("Syslogging\n");
   &syslog('err',"Dialing for SLIP connection.");
}
&Debug("Dialing...\n");
&StartRemoteSlip($tty) || &Exit(1);

($interface = &StartLocalSlip) || &Exit(1);
&Debug("Attached to $interface\n");
&Config($interface);

&Debug("sleeping...\n");
if ($lt_interval) {
	sleep $lt_interval while (&TestLink);
} else {
	sleep;
}
