#
# Copyright (C) 1993, FORE Systems, Inc.
# @(#)$Id: sunos_install.pl,v 1.51 1995/02/27 16:53:36 asw Exp $
#
# install.pl -- install sba kernel 
#

$conf_includes = 0;
$conf_device = 0;
# log - put installation milestones in an already open logfile
sub log {
	if($logopen) {
		local(@date) = split(' ', `date`);
		print LOG "$date[1] $date[2] $date[5] $date[3]: $_[0]\n";
	}
}

# process - generalized routine to slurp up a file and pass it to
#	a helper function which edits the file.  process then rewrites
#	the file with a .tmp extension and adds the name to a list of files
#	that need to be put into place.
sub process {
	local($path, $handler) = @_;
	local(@contents);

	print "Modifying a copy of $path...\n";

	if(! -r $path || ! -w _ ) {
		warn "I cannot read or write $path\n";
		return -1;
	}

	unless(open(PATH, $path)) {
		warn "I cannot open $path\n";
		return -1;
	}
	@contents = <PATH>;		# slurp up the whole file
	close(PATH);
	&log("read file $path");

	if(eval($handler) == -1) {
		warn "I could not process $path properly\n";
		return -1;
	}

	unless(open(PATH, ">$path.tmp")) {
		warn "I could not create the file $path.tmp\n";
		return -1;
	}
	print PATH @contents;
	close(PATH);
	&log("wrote file $path.tmp");

	push(@toswap, $path);

	return 0;
}

# put_in_place - takes a list of file names that have been modified and
#	moves the newer version into place (path -> path.fore) (path.tmp -> path)
#
sub put_in_place {
	local($fail, $path);

	foreach $path (@toswap) {
#		print "putting $path in place\n";
		unless(rename("$path", "$path.fore")) {
			warn "I could not rename $path\n";
			$fail++;
		}
		&log("moved $path to $path.fore");

		unless(rename("$path.tmp", "$path")) {
			warn "I could not rename $path.tmp\n";
			$fail++;
		}
		&log("moved $path.tmp to $path");
	}

	return ($fail ? -1 : 0);
}

# in_proto_c - edits the contents of in_proto.c to increase the TCP
#	buffer sizes to 16K or greater
#
sub in_proto_c {
	local(*proto) = @_;

	foreach $_ (@proto) {
		if(/^\s*int\s*tcp_(send|recv)space\s*=\s*(.*);\s*$/) {
#			print $_ . "constant = " . eval($2) . "\n";
			if(eval($2) < 1024*16) {
				s/(=\s*)(.*);/${1}1024*16;/;
			}
		}
	}
	return 0;
}

# get_nsap - query the user for an NSAP address and return it
sub get_nsap {
	local ($str) = @_;
	local ($response);

	while (1) {
		print "Enter the NSAP address for $str\n";
		$response = <STDIN>;
		chop $response;

		# strip off any leading 0x and any dots
		$response =~ s/^0x//;
		$response =~ s/\.//g;

		if (length($response) != 40) {
			print "Response must be 40 characters\n";
			next;
		}
		if (grep(!/^[a-fA-F0-9]+$/, $response) != 0) {
			print "NSAP must consist of Hexadecimal digits.\n";
			next;
		}
		return $response;
	}
}

sub rc_sba200 {
    local(*rc) = @_;
    local($i, $j, $nsap, $config_info, $dev);
    local($newplace) = -1;

    for $i (0..$#rc) {
	@rc[$i] =~ s,<<<FIRMWARE_PATH>>>,$frmdst,g;
    }

    if(&yesorno("Would you like to configure Classical IP?", "n") == 0) {
        return;
    }

#   XXX take out this loop for now.  ILMI will only support one
#   physical board for the 3.0 release.  (uncomment the close brace, too)
#   foreach $i (0..1) {
        foreach $j (0..3) {
            $dev = pack("A2c", "qa", ord(a) + $i);
            if(&yesorno("Would you like to configure $dev$j", "y") == 0) {
                last;
            }

            # only prompt for address info once per board.  The same
            # registration method (and address) is used for each logical
            # interface associated with the board.
            if(($j == 0) && ($use_ilmi == 0)){
                $nsap = &get_nsap("this node");
                $config_info = "$frmdst/atmarp -n $nsap $dev$j\n";
                $newplace = $#rc + 1;
                splice(@rc, $newplace, 0, $config_info);
            }

            $nsap = &get_nsap("the ARP server");
            $config_info = "$frmdst/atmarp -p $nsap $dev$j\n";

            $newplace = $#rc + 1;
            splice(@rc, $newplace, 0, $config_info);
        }
#   }
}

sub get_udp_port {
    local ($str1, $str2) = @_;
    local ($response);

    while (1) {
	print "UDP port to use to $str1 SNMP messages $str2 TRAP-PDUs: ";
	$response = <STDIN>;
	chop $response;
	if ($response > 65535){
	    print "Response is out of range.\n";
	    next;
	}
	if (grep(!/^[0-9]+$/, $response) != 0) {
	    print "UDP port number must consist of digits only.\n";
	    next;
	}
	return $response;
    }
}



# rc_local - edit the contents of rc.local to add the sba-200 firmware
# 	download routines.
#
sub rc_local {
	local(*rc) = @_;
	local($i, $found, $download, @fore_installed);
	local($udp_recv) = 161;
	local($udp_send) = 162;
	local($newplace) = -1;

	@fore_installed = grep(/download fore systems/i, @rc);
	if(@fore_installed && ($newplace = &strip_old_download(*rc)) < 0) {
		print "I could not clear out the previous installation\n";
		return -1;
	}

	#
	# use the old location if you stripped something out, otherwise...
	#
	if($newplace == -1) {
		#
		# find the first blank line that is not a comment
		#
		comblank: for($i = 0; $i <= $#rc; $i++) {
			next comblank if $rc[$i] =~ /^\s*#/;
			last comblank if $rc[$i] =~ /^\s*$/;
		}
		# pathological case: no blanks, stuff at beginning of file
		if($i > $#rc) {
			$newplace = 0;
		} else {
			$newplace = $i;
		}
	}
		
	push(@download, "#\n# Download FORE Systems sba-200 firmware\n#\n");
	push(@download, "if [ -f $frmdst/rc.sba200 ]; then\n");
	push(@download, "\t$frmdst/rc.sba200\n");
	push(@download, "fi\n");

        splice(@rc, $newplace, 0, @download);

	@fore_installed = grep(/^##### .* FORE SNMPD ADDITIONS/, @rc);
	if(@fore_installed && ($newplace = &strip_old_snmpd_additions(*rc)) < 0) {
		print "I could not clear out the previous snmpd additions\n";
		return -1;
	}
	$config_info = "##### BEGIN FORE SNMPD ADDITIONS\n";
	$newplace = $#rc + 1;
	splice(@rc, $newplace, 0, $config_info);
	if (&yesorno("Would you like to use FORE's SNMP agent?", "y") != 0) {
	    undef @config_info;
	    push(@config_info, "SNMPDPID=`ps ax|egrep snmpd|egrep -v egrep|awk '{print \$1}'`\n");
	    push(@config_info, "if [ x\$SNMPDPID -ne x ] ; then kill -9 \$SNMPDPID; fi\n");

	    $newplace = $#rc + 1;
	    splice(@rc, $newplace, 0, @config_info);
	    if (&yesorno("Would you like to use the standard UDP ports for SNMP (161/162)?", "y") == 0){
		$udp_recv = &get_udp_port("receive", "not containing");
		$udp_send = &get_udp_port("send", "containing");
	    }
	    if (&yesorno("Will you be using ILMI for Address Registration?", "y") != 0){
		$use_ilmi = 1;

		if ($udp_recv != 161) {
		    $config_info = "$frmdst/snmpd -p $udp_recv\n";
		} else {
		    $config_info = "$frmdst/snmpd\n";
		}
	    }
	    else {
		$use_ilmi = 0;

		if ($udp_recv != 161) {
		    $config_info = "$frmdst/snmpd -n -p $udp_recv\n";
	 	} else {
		    $config_info = "$frmdst/snmpd -n\n";
		}
	    }

	    $newplace = $#rc + 1;
	    splice(@rc, $newplace, 0, $config_info);
	}
	else {
	    if (&yesorno("Will you be using ILMI for Address Registration?", "y") != 0){
		undef @config_info;
		$use_ilmi = 1;

		if(system("mv $frmdst/snmpd $frmdst/ilmid")) {
		    print "Error copying $frmdst/snmpd to $frmdst/ilmid\n";
		    return -1;
		}

		push(@config_info, "$frmdst/ilmid -i\n");
		$newplace = $#rc + 1;
		splice(@rc, $newplace, 0, @config_info);
	    }
	    else {
		$use_ilmi = 0;
	    }
	}
	$config_info = "##### END FORE SNMPD ADDITIONS\n";
	$newplace = $#rc + 1;
	splice(@rc, $newplace, 0, $config_info);

	return 0;
}

# strip_old_download - rip out any prior firmware download routines
#	return the index of the start of the old stuff or -1 on error
#
sub strip_old_download {
	local(*rc) = @_;
	local($top, $bottom, $len, $i) = (-1, -1);
	local(@rip, $line);

	print "\nIt seems that you already have an sba-200 initialization ".
		"in $rclocal\n";
	comment: for($i = 0; $i <= $#rc; $i++) {
		last comment if $rc[$i] =~ /download fore systems/i;
	}
	if($rc[$i-1] =~ /^\s*#\s*$/ && $rc[$i-2] =~ /^\s*$/) {
		$top = $i - 1;
	}
	downend: for($i; $i <= $#rc; $i++) {
		last downend if $rc[$i] =~ /^\s*$/;
	}
	$bottom = $i - 1 unless $i > $#rc;
	$len = ($bottom - $top) + 1;

	if($top == -1 || $bottom == -1 || $len > 8) {
		print "but I cannot understand the old entry\n";
		return -1;
	}

	print "but I will remove it before installing the new initialization.\n";
	@rip = splice(@rc, $top, $len);
	if(&yesorno("would you like to see them before I remove them?", "n")) {
		foreach $line (@rip) { print "      $line"; }
	}

	return $top;
}

# strip_old_snmpd_additions - rip out any prior snmpd additions 
#	return the index of the start of the old stuff or -1 on error
#
sub strip_old_snmpd_additions {
	local(*rc) = @_;
	local($i, $j, $x);
	local($found_start, $found_end) = (0, 0);
	
	print "\nIt seems that you already have FORE snmpd additions in $rclocal\n";
	for($i = 0; $i <= $#rc; $i++) {
		if ($rc[$i] =~ /^##### BEGIN FORE SNMPD ADDITIONS/){
		    $found_start = 1;
		    last;
		}
	}
	for ($j = $#rc; $j > $i; $j--){
	    if ($rc[$j] =~ /^##### END FORE SNMPD ADDITIONS/){
		$found_end = 1;
		last;
	    }
	}
	if (! $found_start || ! $found_end){
	    print "but I cannot understand the old entry\n";
	    return -1;
	}
	print "but I will remove them before installing the new initialization.\n";

	@rip = splice(@rc, $i, (($j - $i) + 1));

	if(&yesorno("would you like to see them before I remove them?", "n")) {
		foreach $line (@rip) { print "      $line"; }
	}

	return $#rc;
}

# conf_files -- edits the "files" file to add a fore_atm and fore_bpf if 
#	not preset, or replace the existing one if it is.
#
sub conf_files {
	local(*files) = @_;
	local($i, $atm_found, $bpf_found);
	local($atm_line) = "sunif/fore_atm.c\t\t\toptional fore_atm device-driver\n";
	local($bpf_line) = "sunif/fore_bpf.c\t\t\toptional fore_atm device-driver\n";

	for($i = 0; $i <= $#files; $i++) {
		if($files[$i] =~ m#sunif/sba_[12]00#) {
			$files[$i] = $atm_line;
			$atm_found++;
		}
		if($files[$i] =~ m#sunif/fore_atm#) {
			$files[$i] = $atm_line;
			$atm_found++;
		}
		if($files[$i] =~ m#sunif/fore_bpf#) {
			$files[$i] = $bpf_line;
			$bpf_found++;
		}
	}
	if(!$atm_found) {
		$files[$i] = $atm_line;
		$i++;
	}
	if(!$bpf_found) {
		$files[$i] = $bpf_line;
	}

	return 0;
}

# machine -- edits the <machine> configuration file to contain a reference
#	to fore_atm device driver; adds if not present, replaces if it is.
#
sub machine {
	local(*machine) = @_;
	local($i, $found);
	local($kernelname);

	for($i = 0; $i <= $#machine; $i++) {
		if($machine[$i] =~ m#fore_atm#) {
			$machine[$i] = "device-driver\tfore_atm\t\t# FORE SBA driver\n";
			$found++;
		}
		if($machine[$i] =~ /^config\s+(\S+)\s/) {
			$kernelname = $1;
		}
	}
	if(!$found) {
		$machine[$i] = "device-driver\tfore_atm\t\t# FORE SBA driver\n";
	}
	if($kernelname eq "") {
		print "I could not find a \"config\" line in the $MACHINE file\n";
		print "I will assume you are making a \"vmunix\"\n";
		$kernelname = "vmunix";
	}

	return $kernelname;
}

# conf_c - modify the conf.c contents to have fore's changes
# 	returns "devno,clonedev" on success
#	returns -1 on error
sub conf_c {
	local(*conf) = @_;
	local(@confc, @fore_installed, @clone, $devno, $clone);

	@fore_installed = grep(/fore/i, @conf);
	if(@fore_installed && &strip_old_conf(*conf) != 0) {
		print "I could not clear out the previous installation\n";
		return -1;
	}

	if(($devno = &cdevsw_table(*conf)) == -1) {
		print "I was unable to add an entry to the cdevsw table.\n";
		return -1;
	}

	#
	# grab the cloneopen device while looking at conf.c
	#
	@clone = grep(m!^\s*cloneopen\s*,\s*nodev\s*,.*\*/\s*$!,@conf);
	if(@clone && $clone[0] =~ m!/\*\s*(\d+)\s*\*/\s*$!) {
		$clone = $1;
#		print "I found the clone device at cdevsw entry $clone\n";
	} else {
		$clone = 37;
		warn "I couldn't find the clone device, I will assume it is at $clone\n";
	}
		
	&log("inserted the device driver at $clone,$devno");
	return "$clone,$devno";
}

# cdewsw__table -- parse the cdevsw table in conf.c and add or replace
#	for the fore_atm headers and table entries.
#
sub cdevsw_table {
	local(*conf) = @_;
	local($i, $conf_defines, $devno, $top, $guard);

        if ($conf_includes == 0 || $conf_device == 0){
# If we do not already know the two insertion points,
# find them.
            for($i = 0; $i <= $#conf; $i++) {
                last if $conf[$i] =~ /cdevsw/;
            }
            $conf_includes = $i - 3;
            for(     ; $i <= $#conf; $i++) {
                last if $conf[$i] =~ /^}\s*;/;
            }
            $conf_device = $i;
        }
        $i = $conf_device;
        if($i > $#conf) {
                warn "I could not find the end of the cdevsw table.\n";
                return -1;
        }
        ($top, $guard) = @conf[$i-4, $i-1];
        if($guard !~ /^\s*\}\s*,?\s*$/ || $top !~ m!/\*\s*(\d+)\s*\*/\s*$!) {
                warn "The last entry in the cdevsw table is not in standard form.\n";
                return -1;
        } else {
                $devno = $1;
        }

#       print "The last entry in the cdevsw table is $devno\n";
        $devno++;
#       print "I added the new FORE driver at entry number $devno\n";
#======================================================================
        $conf_defines = <<'EOT';
#include "fore_atm.h"
#if NFORE_ATM > 0
extern int fore_atm_mmap();
extern struct streamtab fore_atm_info;
#define sbatab &fore_atm_info
#define sba_segmap_2_1  0
#else   /* NFORE_ATM */
#define fore_atm_mmap   nodev
#define sbatab 0
#define sba_segmap_2_1  0
#endif  /* NFORE_ATM */

EOT
#----------------------------------------------------------------------
        $conf_cdevsw = "    {
        nodev,          nodev,          nodev,          nodev,          /*$devno*/
        nodev,          nodev,          nodev,          0,
        sbatab,         sba_segmap_2_1,
    },\n";
#======================================================================
        splice(@conf, $conf_device, 0, $conf_cdevsw);
        # SunOS 4.1.3 does not have a comma at the end of the last cdevsw entry
        $conf[$i-1] =~ s/\}\s*$/\},/ if $guard !~ /^\s*\}\s*,\s*$/;

        splice(@conf, $conf_includes, 0, $conf_defines);
	return $devno;
}

sub strip_old_conf {
	local(*conf) = @_;
	local($inctop, $incbot, $devtop, $devbot) = (0, 0, 0, 0);
	local(@rip, $line);

	print
		"\nIt seems that you already have an ATM device installed in conf.c\n";
	foreach($i = 0; $i <= $#conf; $i++) {
		$_ = $conf[$i];
		#
		# look for version 2.0
		#
		$inctop = $i if /^#include\s+"fore_atm.h"/;
		$incbot = $i if !$incbot && $inctop && /^\s*$/;
		$devtop = $i-1 if /fore_atm_cloneopen0\s*,/;
		$devbot = $i+1 if !$devbot && $devtop && /atmtab\s*,/;
		#
		# catch a version 2.1 reinstall
		#
		if(/^\s+sbatab\s*,/ && !$devtop) {
			$devtop = $i-3;
			$devbot = $i+1;
		}
	}

	#
	# we should have found all of the tag lines and their spans should
	# be reasonable
	#
	if(!$inctop || !$incbot || !$devtop || !$devbot ||
	  ($incbot - $inctop > 20 || $devbot - $devtop > 25)) {
		print "but I could not make sense of the entry.\n";
		return -1;
	}
	#
	# atmtab should be last entry of cdevsw
	#
#XXXX	if($conf[$devbot+1] !~ /^\s*};\s*$/) {
#XXXX		print "but it looks like you have installed another device\n".
#XXXX			  "after you installed the FORE Systems' driver.\n";
#XXXX		return -1;
#XXXX	}

	#
	# fix a wierd install bug (probably an earlier install.pl's fault)
	# which makes $devtop look like "}, {" instead of "},"
	#
	if($conf[$devtop] =~ /^\s*}\s*,\s*{\s*$/) {
		print "and line $devtop is in non-standard form, ";
		#
		# double check  (should be more stringent)
		#
		if($conf[$devtop+1] !~ /nodev/) {
			print "and I could not clean it up.\n";
			return -1;
		} else {
			$conf[$devtop] = "    },\n";
			$devtop++;
			print "but I was able to clean it up.\n";
		}
	}

	# rip the lines out
	print "I will be removing the old installation lines from conf.c\n";
	print "\nI found the old headers in lines $inctop to $incbot of conf.c\n";
	$len = ($incbot-$inctop)+1;
	@rip = splice(@conf, $inctop, $len);
	if(&yesorno("would you like to see them before I remove them?", "n")) {
		foreach $line (@rip) { print "      $line"; }
	}
	print
		"I found the old cdev entry in lines $devtop to $devbot of conf.c\n";
	@rip = splice(@conf, $devtop-$len, ($devbot-$devtop)+1);
	if(&yesorno("would you like to see them before I remove them?", "n")) {
		print @rip;
	}
        $conf_includes = $inctop;
        $conf_device = $devtop - (($incbot+1) - $inctop);

	return 0;
}

sub config_name {
	local(@motd, @build, $model);
	local($resp);
	if(open(MOTD, "/etc/motd")) {
		@motd = <MOTD>;
		close(MOTD);
		@build = grep(/^SunOS Release/, @motd);
		if($#build == 0 && $build[0] =~ /^SunOS Release.*\((.*)\)/) {
			$model = $1;
		}
	}

	print
	  "Each different kernel configuration under SunOS has a name to\n".
	  "identify it.  You can either modify an existing kernel\n".
	  "configuration, or create a new configuration file and modify that.\n\n";

	if($model ne "" && -f "/usr/kvm/sys/$ARCHK/conf/$model") {
		print
			"You are currently running the kernel configuration called ".
			"$model.\nIf you would like to modify this kernel configuration,\n".
			"answer \"$model\" now; if you want to create a new ".
			"configuration,\nanswer with another name.\n\n";
	} else {
		print
			"I cannot find the name of the currently running kernel ".
			"configuration.\nYou must give me a name for the new ".
			"configuration.\n\n";
		$model = "";
	}

	do {
	if($model eq "") { $default = "ATM"; } else { $default = $model; }
	print "What is the name of the configuration you wish to make? ";
	print "[$default] ";
	chop($_ = <STDIN>); 
	if(/^\s*$/) {
		$model = $default;
	} else { 
		tr/a-z/A-Z/;
		$model = $_;
	}

	if(-f "/usr/kvm/sys/$ARCHK/conf/$model") {
		print "\nThe kernel configuration $model already exists.\n";
		$resp = &yesorno("Are you sure you want to modify $model?", "n");
	} else {
		do {
			print "The kernel configuration $model does not yet exist.\n";
			print "I need to copy an existing kernel configuration to use\n";
			print "as a base for the FORE ATM kernel.\n";
			print "What kernel configuration should I use as a base for $_? ";
			print "[GENERIC] ";
			chop($oldmodel = <STDIN>);
			$oldmodel = "GENERIC" if $oldmodel =~ /^\s*$/;
			$oldmodel =~ tr/a-z/A-Z/;
			print "$oldmodel does not exist\n" unless
			  -f "/usr/kvm/sys/$ARCHK/conf/$oldmodel";
		} until -f "/usr/kvm/sys/$ARCHK/conf/$oldmodel";

		if(system("cp /usr/kvm/sys/$ARCHK/conf/$oldmodel ".
		  "/usr/kvm/sys/$ARCHK/conf/$model")) {
			&give_up(0);
		}
		&log("copied /usr/kvm/sys/$ARCHK/conf/$oldmodel to /usr/cvm/sys/$ARCHK/conf/$model");
		$resp = 1;
	}
	$conf_clone = 0;
	open(IN, "</usr/kvm/sys/$ARCHK/conf/$model") ||
	  die "Unable to open /usr/kvm/sys/$ARCHK/conf/$model for reading.";

	while (<IN>) {
		if ((grep(/pseudo-device/, $_)) && (grep(/clone/, $_)) &&
		  (!grep(/^#/, $_))) {
			$conf_clone = 1;
		}
	}
	close(IN);

	if (! $conf_clone) {
		$resp = 0;
		print "\nThe kernel configuration $model does not contain the clone\n";
		print "pseudo-device needed for our devices.  Please specify a kernel\n";
		print "configuration with this pseudo-device configured.\n";
		print "The following line must be present in the kernel configuration file:\n\n";
		print "\tpseudo-device   clone           # clone device\n\n";
	}
	} until $resp == 1;

	return $model;
}

sub give_up {
	local($repair, $path, $fail) = @_;
	print
		"\n\aI could not automatically install the new ATM kernel.\n\n".
		"A log file of all changes that I have made can be found in\n".
		"fore_install.log\n\n".
		"If you want to rerun this kernel configuration program\n".
		"(after fixing any errors) type the command:\n\n".
		"         ./fore_install kernel-config\n\n".
		"Or you may wish to follow the manual installation as\n".
		"described in Appendix A of the SBus ATM Computer Interface\n".
        "User's Manual.\n\n".
		"Please contact FORE Systems' technical support staff\n".
		"if you require further assistance.\n";

	&log("encountered an error, terminating installation");

	if($repair) {
		print
		"\nI will try to put things back the way they were before I started.\n";
		&log("attempting to restore modified files");
		foreach $path (@toswap) {
			if(rename("$path.fore", "$path")) {
				print "restored $path\n";
				&log("restored $path to its prior state");
			} else {
				warn "I could not put $path.fore back in $path\n";
				&log("failed to restore $path to its prior state");
				$fail++;
			}
		}
		print "I was ".($fail ? "not " : "") . "able to clean everything up.\n";
	}

	exit(1);
}

# yesorno - ask a yes/no question until a y/n response.  provide default.
#	return 1 for yes
#	return 0 for no
#
sub yesorno {
	local($prompt, $default) = @_;
	local($response);

	do {
		printf("%s [%s]: ", $prompt, $default);
		$response = <STDIN>;
		chop $response;
		$response = $default if length($response) == 0;
	} until($response =~ /^[yn]/i);

	return $response =~ /^y/i;
}

# make_directory - create a directory with the given name and permissions.
#    log success or failure, and exit on failure.
sub make_directory {
    local($dir, $perm) = @_;

    if(mkdir($dir, $perm) != 1) {
	&log("error creating directory $dir: $!");
	print "I could not create $dir ($!) -- Installation terminated.\n";
	exit(1);
    } else {
	&log("created directory $dir");
    }
}

# rename_to_dot_old - rename the given directory so that it has a ".old"
#    extension.  Ask if it's ok to do so, and exit if not.
sub rename_to_dot_old {
    local($dir) = @_;
    local($ftype);

    $ftype = ((-d $dir) ? "directory" : "file");
    print "The $ftype $dir exists.  I am about to rename $dir\n";
    print "to $dir.old.\n\n";
    exit(0) unless &yesorno("okay to rename $dir", "n");

    if (-d "$dir.old" || -f "$dir.old") {
	    print "$dir.old exists.  I am about to remove it.\n\n";
	    exit(0) unless &yesorno("okay to remove $dir.old", "n");
	    system "rm -rf $dir.old";
    }
    if(system("mv $dir $dir.old")) {
	    &log("error renaming $dir to $dir.old: $!");
	    print "I could not rename $dir ($!) -- Installation terminated.\n";
	    exit(1);
    } else {
	    &log("renamed $dir to $dir.old");
    }
}

# copy_files - copy the list of files from one directory to the other.
#       log the copy.  exit on error.
sub copy_files {
    local($fromdir, $todir, @files) = @_;
    local($prog);

    foreach $prog (@files) {
	if(system("cp $fromdir/$prog $todir/$prog")) {
	    &log("error copying $fromdir/$prog $todir/$prog");	
	    print "Could not install $todir/$prog\n";
	    &give_up(0);
	} else {
	    &log("installed $fromdir/$prog in $todir/$prog");
	    print "     $todir/$prog\n";
	}
    }
}

# copy_all_files - copy all the files in the directory fromdir to the
#     directory todir.  log the copy.  exit on error.
sub copy_all_files {
    local($fromdir, $todir) = @_;

    if(system("cp $fromdir/* $todir")) {
	&log("error copying files from $fromdir to $todir");	
	print "Could not install $todir\n";
	&give_up(0);
    } else {
	&log("installed $fromdir in $todir");
	print "     $todir\n";
    }
}

# main line code begins here

if(open(LOG, ">>./fore_install.log")) {
	$oldfh = select(LOG); $| = 1; select($oldfh);
	$logopen = 1;
} else {
	$logopen = 0;
}

&log("Begining kernel installation");

if($>) {
	print "You must be running as root to install a new kernel.\n";
	exit(1);
}

$ARCHK = `arch -k`; chop $ARCHK;
$OS_RELEASE = `uname -r`; chop $OS_RELEASE;

if($ARCHK !~ /^sun4[cm]$/) {
	print "This installation will only work on a sun4c or sun4m architecture\n";
	exit(1);
}

$MACHINE = &config_name;

$pwd = `pwd`; chop($pwd); if ($x = readlink($pwd)) { $pwd = $x; }
$use_ilmi = 0;
$fconf = "/usr/kvm/sys/sun/conf.c";
$fprot = "/usr/kvm/sys/netinet/in_proto.c";
$ffile = "/usr/kvm/sys/$ARCHK/conf/files";
$fsunif = "/usr/kvm/sys/sunif";
$fbpf = "fore_bpf.c";
$fmach = "/usr/kvm/sys/$ARCHK/conf/$MACHINE";
$object= "./kernel/fore_atm.$ARCHK.o";
$objdst= "/usr/kvm/sys/$ARCHK/OBJ/fore_atm.o";
$rclocal= "/etc/rc.local";
$etc = "./etc";
$man = "./man";
$firmw = "./kernel";
$frmdst_default= "/usr/etc/fore";
$util_default="/usr/fore";
$rcsba200 = "./etc/rc.sba200";
$rename = "./install/fore_if_rename";
$foredevice_default = "fa";
$foredevice_orig = "FORE_IF_NAME";
$foredevice = $foredevice_default;
$bpf_include_def = "/sys/net";
@loader = ("ncomm", "objcopy", "sba200.ucode1", "sba200.ucode2", "sba200e.ucode1", 
		"sba200e.ucode2", "atmarp", "snmpd");
@etcutil = ("adinfo", "asxmon", "atmarp", "atmconfig", "atmstat", "cchan",
	    "cpath", "cport", "looptest", "topology");

if(($OS_RELEASE =~ /^4.1.3_U1$/) && ($ARCHK =~ /^sun4m$/)) {
	$object= "./kernel/fore_atm.sun4m_413u1.o";
}

print "\nInto which directory should I install the firmware?\n";
print "(The directory must be local to this machine.) [$frmdst_default]: ";
chop($frmdst = <STDIN>);
if ($frmdst eq "") {
    $frmdst= $frmdst_default;
}
if ($x = readlink($frmdst)) {
    print "$frmdst is the same as $x\n";
    $frmdst = $x;
}

# don't initialize variables depending on $frmdst until here
$rcdst = "$frmdst/rc.sba200";
$frmsav= "$frmdst.old";

if(-e $frmdst && $frmdst ne $pwd) {
    &rename_to_dot_old($frmdst);
}

if($frmdst ne $pwd) {
    &make_directory($frmdst, 0755);
}

print "Under which directory should I install utilities and man pages?\n";
print "[$util_default]: ";
chop($utildst = <STDIN>);
if ($utildst eq "") {
    $utildst= $util_default;
}
if ($x = readlink($utildst)) {
    print "$utildst is the same as $x\n";
    $utildst = $x;
}

if(-e $utildst && $utildst ne $pwd) {
    &rename_to_dot_old($utildst);
}

if($utildst ne $pwd) {
    &make_directory($utildst, 0755);

    &make_directory("$utildst/man", 0755);
    &make_directory("$utildst/man/man4", 0755);
    &copy_all_files("$man/man4", "$utildst/man/man4");
    &make_directory("$utildst/man/man5", 0755);
    &copy_all_files("$man/man5", "$utildst/man/man5");
    &make_directory("$utildst/man/man8", 0755);
    &copy_all_files("$man/man8", "$utildst/man/man8");

    &make_directory("$utildst/etc", 0755);
    &copy_files($etc, "$utildst/etc", @etcutil);
}

if(system("chgrp kmem $utildst/etc/atmarp")) {
        &log ("error changing group of $utildst/etc/atmarp");
        print "Could not change group of $utildst/etc/atmarp to kmem";
        &give_up(0);
}
if(system("chmod g+s $utildst/etc/atmarp")) {
        &log("error changing mod of $utildst/etc/atmarp to g+o");
        print "Could not change mod of $utildst/etc/atmarp to g+o";
        &give_up(0);
}

print "\nEnter the name for the FORE ATM interfaces [$foredevice]: ";
chop($if_name = <STDIN>);
if ($if_name eq "") {
    $if_name = $foredevice;
}
$foredevice = substr($if_name, 0, 8);

if ( $if_name eq $foredevice ) {
    print "The interfaces will be called $foredevice.\n";
} else {
    print "WARNING: Due to system limitations, the interfaces will be called $foredevice.\n";
}

print "\nInstalling new files...\n";

&copy_files($etc, $frmdst, @loader);

if(system("$rename $object $foredevice_default $foredevice_orig $foredevice ".
			"$objdst")) {
	&log("error copying $object to $objdst");
	print "Could not put the device object in place\n";
	&give_up(0);
} else {
	&log("installed $object in $objdst");
	print "     $objdst\n";
}

if(system("sed -e 's/$foredevice_orig/$foredevice/' $rcsba200 > $rcdst")) {
	&log("error copying $rcsba200 to $rcdst");
	print "Could not put the firmware download script in place.\n";
	&give_up(0);
} else {
	&log("installed $rcsba200 in $rcdst");
	print "     $rcdst\n";
}

print "done.\n\n";
	
print "\nChanging group ownership of atmarp...\n\n";

if(system("chgrp kmem $frmdst/atmarp")) {
        &log ("error changing group of $frmdst/atmarp");
        print "Could not change group of $frmdst/atmarp to kmem";
        &give_up(0);
}
if(system("chmod g+s $frmdst/atmarp")) {
        &log("error changing mod of $frmdst/atmarp to g+o");
        print "Could not change mod of $frmdst/atmarp to g+o";
        &give_up(0);
}


while (1) {
    if (&yesorno("Include support for the Berkeley Packet Filter (BPF)? ", "n"))
    {
	print "Location of bpf.h? ";
	print "[$bpf_include_def] ";
	$_ = <STDIN>;
	if ($_ ne "\n") {
	    chop($_);
	    $bpf_include_def = $_;
	}
	if(! (-f "$bpf_include_def/bpf.h")) {
	    print "Could not open include file \"$bpf_include_def/bpf.h\"\n";
	    $bpf_include_def = "/sys/net";
	    next;
	}
	open(IN, "<$firmw/$fbpf") || die "Unable to open $firmw/$fbpf for reading ($!)\n";
	open(OUT, ">$fsunif/$fbpf") || die "Unable to open $fsunif/$fbpf for writing ($!)\n";
	while (<IN>) {
	    ($tmp = $_) =~ s/BPF_INCLUDE/$bpf_include_def/;
	    if (!grep(/BPF_SUPPORT/, $tmp)) {
		print OUT $tmp;
	    }
        }
	close(IN);
	close(OUT);
	print "Installed $fbpf in $fsunif with BPF support.\n";
    } else {
	if(system("cp $firmw/$fbpf $fsunif")) {
	    &log("error copying $fbpf to $fsunif");
	    print "Could not put the source file in place\n";
	    &give_up(0);
 	} else {
	    &log("installed $firmw/$fbpf in $fsunif without BPF support.");
	    print "Installed $firmw/$fbpf in $fsunif without BPF support.\n";
	}
    }
    last;
}
print "Modifying copies of configuration files...\n\n";

if(&process($fconf, '$node = &conf_c(*contents)')) {
	print "Error modifying $fconf\n";
	$error++;
} elsif(&process($fprot, '&in_proto_c(*contents)')) {
	print "Error modifying $fprot\n";
	$error++;
} elsif(&process($ffile, '&conf_files(*contents)')) {
	print "Error modifying $ffile\n";
	$error++;
} elsif(&process($fmach, '$newkernel = &machine(*contents)')) {
	print "Error modifying $fmach\n";
	$error++;
} elsif(&process($rclocal, '&rc_local(*contents)')) {
	print "Error modifying $rclocal\n";
	$error++;
} elsif(&process($rcdst, '&rc_sba200(*contents)')) {
	print "Error modifying $rcdst\n";
	$error++;
}

if($error) {
	&give_up(0); 
} else {
	print <<"EOF";

I have correctly modified copies of the following files:
      $fconf
      $fprot
      $ffile
      $fmach
      $rclocal
      $rcdst
I am about to replace the old versions of these
files with the new versions.  I will save the
old versions in files with a ".fore" extension.
(e.g. $fconf.fore)

EOF
	&give_up(0) unless &yesorno("okay to continue?", "n");
	&put_in_place;
}

if(system("chmod a+x $rcdst")) {
        &log("error changing mod of $rcdst to a+x");
        print "Could not change mod of $rcdst to a+x";
        &give_up(0);
}

chdir("/usr/kvm/sys/$ARCHK/conf");
print "\nConfiguring $MACHINE... please wait\n";
&log("building configuration makefile for $MACHINE");
if(system("config $MACHINE")) {
	print "Error running config on $MACHINE\n";
	&give_up(1);
}

chdir("/usr/kvm/sys/$ARCHK/$MACHINE");
print "\nBuilding $MACHINE... please wait\n";
&log("building new kernel for $MACHINE");
if(system("make") || !(-f $newkernel)) {
	print "Error building new $newkernel for $MACHINE\n";
	&give_up(1);
}

print "\nThe kernel has been built.  I am going to save your old kernel\n";
print "In /vmunix.orig and place the new kernel in /vmunix.\n";

&give_up(0) unless &yesorno("okay to proceed?", "n");

&log("moving /vmunix to /vmunix.orig");
if(rename("/vmunix", "/vmunix.orig") == 0) {
	print "I could not move the old kernel out of the way\n";
	&give_up(0);
}
&log("moving the new $newkernel to /vmunix");
if(system("mv ./$newkernel /vmunix")) {
	print "I could not put the new kernel into place\n";
	&give_up(0);
}

print "Creating device nodes in /dev...\n";
@devices = </dev/{$foredevice[0-9],qa[a-z][0-9]}>;
if(@devices) {
	print "I found the following devices already present:\n";
	foreach (@devices) {
		print "\t$_\n";
	}
	&give_up(0) unless &yesorno("can I remove them?", "n");
	foreach (@devices) {
		unlink $_;
		&log("removing device node $_");
	}
}

foreach $i (0..1) {
	($major, $minor) = $node =~ /(\d+),(\d+)/;
	if(system("mknod /dev/$foredevice$i c $major $minor")) {
		print "could not create /dev/$foredevice$i\n";
	} else {
		chmod(0666, "/dev/$foredevice$i");
		&log("created device node /dev/$foredevice$i");
		if($i > 0) {
		    next;
		}
		foreach $j (0..3) {
			# make the strings qaa, qab, etc.
			$dev = pack("A2c", "qa", ord(a) + $i);
			if(system("mknod /dev/$dev$j c $major $minor")) {
				print "could not create /dev/$dev$j\n";
			} else {
				chmod(0666, "/dev/$dev$j");
				&log("created device node /dev/$dev$j");
			}
		}
	}
}

&log("Kernel installation complete");
print <<EOF;

I have completed the configuration and installation of The new
FORE ATM kernel.  For these changes to take effect, the system must
be rebooted.  When the system reboots, you should see a message
similar to:

        fa0 at SBus slot 1 0x0 pri 2

if you have a SBA-x00 card installed in one of the SBus slots.
EOF

exit(0) if !&yesorno("Should I reboot the system now?", "y");

print <<EOF;

After the machine successfully reboots, please remember to follow
section 4, "Network Interface Configuration" of the User's Manual.

EOF
&log("rebooting system");
close(LOG) if $logopen;
exec "reboot";
die "I could not reboot the machine\n";
