#!/usr/local/bin/perl
# -*- perl -*-

# Copyright (c) 2000 by Jeff Weisberg
# Author: Jeff Weisberg <jaw @ tcp4me.com>
# Function: archive router configs

use Socket;

# where do you want snapshots saved?
$SNAPSHOTDIR = "/home/sysadm/conf/snapshot";

# on cisco:
#  ip rcmd rsh-enable
#  ip rcmd remote-host ciscouser IP_ADDRESS unixuser enable
# an access list might be a good idea as well

$luser = "unixuser";
$ruser = "ciscouser";

# substitute your own usernames, of course

# substitute your own routers, or pass on cmdline
@routers = qw(
	      gw-bm1.example.net
	      gw-bm2.example.net
	      gw-bm3.example.net
	      );

# ASLO NOTE: in order to RCS the snapshots, RCS must be installed

################################################################

@routers = @ARGV if @ARGV;

chdir $SNAPSHOTDIR;

$TIMEOUT = 120;

$proto = 6;  # tcp
$port = 514; # rsh

foreach $r (@routers){
    print STDERR "$r\n";

    $ip = resolve($r);
    if( !$ip ){
	print STDERR "could not resolve $r\n";
	next;
    }
    
    socket(S, PF_INET, SOCK_STREAM, $proto) || die "socket: $!";
    select S; $| = 1; select STDOUT;

    # get a reserved port
    for $lp (512 .. 1023){
	$sin = sockaddr_in($lp, INADDR_ANY);
	if( bind(S, $sin) == 0 ){
	    $! = '';
	    last;
	}
    }
    if( $! ){
	close S;
	print STDERR "could not bind to local port: $!\n";
	next;
    }
    
    $sin = sockaddr_in($port, $ip);
    if( ! connect(S, $sin) ){
	close S;
	print STDERR "$r - connect failed: $!\n";
	next;
    }

    # speak the rcmd (rsh) protocol to do "show run"
    print S "0\0$luser\0$ruser\0show run\0";
    
    rename $r, "$r.bak";
    open(FILE, "> $r");

    $bad = 1;
    $itime = time();
    $conf = '';
    
    while( 1 ){
	if( time() > $itime + $TIMEOUT ){
	    print STDERR "time out\n";
	    last;
	}
	$rfds = $wfds = "\0\0\0\0";
	vec($rfds, fileno(S), 1) = 1;
	$i = select($rfds, undef, undef, ($itime + $TIMEOUT - time()));
	if( $i == -1 ){
	    if( $! != 4 ){
		print STDERR "error: $!\n";
		close S;
		last;
	    }
	    next;
	}

	$i = sysread S, $line, 8192;
	if( $i == 0 ){
	    # eof
	    # print STDERR "unexpected eof\n";
	    sleep 5;
	    seek S, 0, 1;
	}

	$conf .= $line;
	
        if( $conf =~ /^end/m ){
	    $bad = 0;
	    last;
	}
    }
    close S;

    $_ = $conf;
    
    tr/\0//d;
    tr/\r//d;
    if( /ntp clock-period/m ){
	# ignore minor clock fluctuations
	s/ntp clock-period .*$/! ntp clock-period <not saved>/m;
    }
    if( /^username /m ){
	# ignore pwd
	s/^(username .*) [^ ]+$/! \1 <not saved>/mg;
    }
    print FILE $_;

    close FILE;
    close S;

    if( ! $bad ){
	system( "rcsdiff -q $r" );
	system( "echo . | ci -l -mperiodic $r >/dev/null 2>&1" );
	unlink "$r.bak";
    }else{
	print STDERR "\treverting\n";
	rename $r, "$r.xxx";
	rename "$r.bak", $r;
    }
}


sub resolve {
    my( $a ) = @_;
    my( $ip, @o );

    if( $a =~ /^\d+\.\d+\.\d+\.\d+$/ ){
        @o = split( /\./, $a );
        $ip = pack ("CCCC", @o);
    }else{
        $ip = gethostbyname($a);
        if( length($ip) != 4){
            sleep(1);
            $ip = gethostbyname($a);
        }
    }
    $ip;
}
