[rancid] Re: Alcatel Omniswitch and Rancid

Alexander Bochmann ab at lists.gxis.de
Sat Jul 19 09:14:33 UTC 2008


Hi,

...on Fri, Jul 18, 2008 at 07:55:30PM -0700, David Newman wrote:

 > Per your posting:
 > http://www.shrubbery.net/pipermail/rancid-discuss/2008-June/003148.html
 > Yes, I'm very interesting in taking a look. Have you posted your changes 
 > anywhere?

No yet, but I already promised someone else to finally 
send the files out yesterday, so here we go.

The files are ologin and orancid, based on the rancid 2.3.2a7 
versions. There's lots of unused code in them, as I didn't 
get very far beyond tweaking my changes in. A new vendor "alcatel" 
is defined in rancid-fe. It's what you use in your router.db

Things that work:

Logs into an Omniswitch via ssh (I think telnet works too, but 
I can't actually remember right now - I'll check on monday) and 
runs the following commands:

show configuration status
show chassis
show module
show microcode 
show configuration snapshot all

Output is minimally parsed, not shure if the ProcessHistory() 
parameters are used in the way they were intended.

Things that should be fixed in some way:

Currently, the prompt needs to end in the characters "->" 
(which is the default I think, otherwise use session config prompt).

I had a strange problem with some older AOS versions where 
rancid couldn't find the prompt, a leftover from debugging 
that is that you will get a line (commented out) with the 
exit command at the end of your config if your system runs 
a more recent software release.

I'm not shure if I did something to the autoenable code, 
so set add autoenable 1 for your alcatel systems in cloginrc
to be shure.

Need to throw out all the unused code or try to merge with the 
main rancid scripts.

I hope it's of any use to you, otherwise just ask back 
and I'll try help getting it to work.

Alex.

-------------- next part --------------
A non-text attachment was scrubbed...
Name: rancid-fe.diff
Type: text/x-diff
Size: 488 bytes
Desc: not available
Url : http://www.shrubbery.net/pipermail/rancid-discuss/attachments/20080719/a2d18141/attachment.bin 
-------------- next part --------------
#! /usr/bin/expect --
##
## $Id: clogin.in,v 1.107 2006/12/08 21:28:25 heas Exp $
##
## rancid 2.3.2a7
## Copyright (C) 1997-2006 by Terrapin Communications, Inc.
## All rights reserved.
##
## This software may be freely copied, modified and redistributed
## without fee for non-commerical purposes provided that this license
## remains intact and unmodified with any RANCID distribution.
##
## There is no warranty or other guarantee of fitness of this software.
## It is provided solely "as is".  The author(s) disclaim(s) all
## responsibility and liability with respect to this software's usage
## or its effect upon hardware, computer systems, other software, or
## anything else.
##
## Except where noted otherwise, rancid was written by and is maintained by
## Henry Kilmer, John Heasley, Andrew Partan, Pete Whiting, and Austin Schutz.
##
#
# The login expect scripts were based on Erik Sherk's gwtn, by permission.
#
# ologin - OmniSwitch login hacks by Alexander Bochmann <ab+rancid at st.gxis.de>
#
# Most options are intuitive for logging into a Cisco router.
# The default is to enable (thus -noenable).  Some folks have
# setup tacacs to have a user login at priv-lvl = 15 (enabled)
# so the -autoenable flag was added for this case (don't go through
# the process of enabling and the prompt will be the "#" prompt.
# The default username password is the same as the vty password.
#

# Usage line
set usage "Usage: $argv0 \[-autoenable\] \[-noenable\] \[-c command\] \
\[-Evar=x\] \[-e enable-password\] \[-f cloginrc-file\] \[-p user-password\] \
\[-s script-file\] \[-t timeout\] \[-u username\] \
\[-v vty-password\] \[-w enable-username\] \[-x command-file\] \
\[-y ssh_cypher_type\] router \[router...\]\n"

# env(CLOGIN) may contain:
#	x == do not set xterm banner or name

# Password file
set password_file $env(HOME)/.cloginrc
# Default is to login to the router
set do_command 0
set do_script 0
# The default is to automatically enable
set avenable 0
# The default is that you login non-enabled (tacacs can have you login already
# enabled)
set avautoenable 1
# The default is to look in the password file to find the passwords.  This
# tracks if we receive them on the command line.
set do_passwd 1
set do_enapasswd 1
#
set send_human {.4 .4 .7 .3 5}

# Find the user in the ENV, or use the unix userid.
if {[ info exists env(CISCO_USER) ]} {
    set default_user $env(CISCO_USER)
} elseif {[ info exists env(USER) ]} {
    set default_user $env(USER)
} elseif {[ info exists env(LOGNAME) ]} {
    set default_user $env(LOGNAME)
} else {
    # This uses "id" which I think is portable.  At least it has existed
    # (without options) on all machines/OSes I've been on recently -
    # unlike whoami or id -nu.
    if [ catch {exec id} reason ] {
	send_error "\nError: could not exec id: $reason\n"
	exit 1
    }
    regexp {\(([^)]*)} "$reason" junk default_user
}
if {[ info exists env(CLOGINRC) ]} {
    set password_file $env(CLOGINRC)
}

# Sometimes routers take awhile to answer (the default is 10 sec)
set timeout 45

# Process the command line
for {set i 0} {$i < $argc} {incr i} {
    set arg [lindex $argv $i]

    switch  -glob -- $arg {
	# Username
	-u* -
	-U* {
	    if {! [  regexp .\[uU\](.+) $arg ignore user]} {
		incr i
		set username [ lindex $argv $i ]
	    }
	# VTY Password
	} -p* -
	-P* {
	    if {! [  regexp .\[pP\](.+) $arg ignore userpasswd]} {
		incr i
		set userpasswd [ lindex $argv $i ]
	    }
	    set do_passwd 0
	# VTY Password
	} -v* {
	    if {! [  regexp .\[vV\](.+) $arg ignore passwd]} {
		incr i
		set passwd [ lindex $argv $i ]
	    }
	    set do_passwd 0
	# Version string
	} -V* {
	    send_user "rancid 2.3.2a7\n"
	    exit 0
	# Enable Username
	} -w* -
	-W* {
	    if {! [  regexp .\[wW\](.+) $arg ignore enauser]} {
		incr i
		set enausername [ lindex $argv $i ]
	    }
	# Environment variable to pass to -s scripts
	} -E*
	{
	    if {[ regexp .\[E\](.+)=(.+) $arg ignore varname varvalue]} {
		set E$varname $varvalue
	    } else {
		send_user "\nError: invalid format for -E in $arg\n"
		exit 1
	    }
	# Enable Password
	} -e*
	{
	    if {! [  regexp .\[e\](.+) $arg ignore enapasswd]} {
		incr i
		set enapasswd [ lindex $argv $i ]
	    }
	    set do_enapasswd 0
	# Command to run.
	} -c* -
	-C* {
	    if {! [  regexp .\[cC\](.+) $arg ignore command]} {
		incr i
		set command [ lindex $argv $i ]
	    }
	    set do_command 1
	# Expect script to run.
	} -s* -
	-S* {
	    if {! [  regexp .\[sS\](.+) $arg ignore sfile]} {
		incr i
		set sfile [ lindex $argv $i ]
	    }
	    if { ! [ file readable $sfile ] } {
		send_user "\nError: Can't read $sfile\n"
		exit 1
	    }
	    set do_script 1
	# 'ssh -c' cypher type
	} -y* -
	-Y* {
	    if {! [  regexp .\[eE\](.+) $arg ignore cypher]} {
		incr i
		set cypher [ lindex $argv $i ]
	    }
	# alternate cloginrc file
	} -f* -
	-F* {
	    if {! [ regexp .\[fF\](.+) $arg ignore password_file]} {
		incr i
		set password_file [ lindex $argv $i ]
	    }
	# Timeout
	} -t* -
	-T* {
	    if {! [ regexp .\[tT\](.+) $arg ignore timeout]} {
		incr i
	        set timeout [ lindex $argv $i ]
	    }
	# Command file
	} -x* -
	-X {
	    if {! [  regexp .\[xX\](.+) $arg ignore cmd_file]} {
		incr i
		set cmd_file [ lindex $argv $i ]
	    }
	    if [ catch {set cmd_fd [open $cmd_file r]} reason ] {
		send_user "\nError: $reason\n"
		exit 1
	    }
	    set cmd_text [read $cmd_fd]
	    close $cmd_fd
	    set command [join [split $cmd_text \n] \;]
	    set do_command 1
	# Do we enable?
	} -noenable {
	    set avenable 0
	# Does tacacs automatically enable us?
	} -autoenable {
	    set avautoenable 1
	    set avenable 0
	} -* {
	    send_user "\nError: Unknown argument! $arg\n"
	    send_user $usage
	    exit 1
	} default {
	    break
	}
    }
}
# Process routers...no routers listed is an error.
if { $i == $argc } {
    send_user "\nError: $usage"
}

# Only be quiet if we are running a script (it can log its output
# on its own)
if { $do_script } {
    log_user 0
} else {
    log_user 1
}

#
# Done configuration/variable setting.  Now run with it...
#

# Sets Xterm title if interactive...if its an xterm and the user cares
proc label { host } {
    global env
    # if CLOGIN has an 'x' in it, don't set the xterm name/banner
    if [info exists env(CLOGIN)] {
	if {[string first "x" $env(CLOGIN)] != -1} { return }
    }
    # take host from ENV(TERM)
    if [info exists env(TERM)] {
	if [regexp \^(xterm|vs) $env(TERM) ignore ] {
	    send_user "\033]1;[lindex [split $host "."] 0]\a"
	    send_user "\033]2;$host\a"
	}
    }
}

# This is a helper function to make the password file easier to
# maintain.  Using this the password file has the form:
# add password sl*	pete cow
# add password at*	steve
# add password *	hanky-pie
proc add {var args} { global int_$var ; lappend int_$var $args}
proc include {args} {
    global env
    regsub -all "(^{|}$)" $args {} args
    if { [ regexp "^/" $args ignore ] == 0 } {
	set args $env(HOME)/$args
    }
    source_password_file $args
}

proc find {var router} {
    upvar int_$var list
    if { [info exists list] } {
	foreach line $list {
	    if { [string match [lindex $line 0] $router ] } {
		return [lrange $line 1 end]
	    }
	}
    }
    return {}
}

# Loads the password file.  Note that as this file is tcl, and that
# it is sourced, the user better know what to put in there, as it
# could install more than just password info...  I will assume however,
# that a "bad guy" could just as easy put such code in the clogin
# script, so I will leave .cloginrc as just an extention of that script
proc source_password_file { password_file } {
    global env
    if { ! [file exists $password_file] } {
	send_user "\nError: password file ($password_file) does not exist\n"
	exit 1
    }
    file stat $password_file fileinfo
    if { [expr ($fileinfo(mode) & 007)] != 0000 } {
	send_user "\nError: $password_file must not be world readable/writable\n"
	exit 1
    }
    if [ catch {source $password_file} reason ] {
	send_user "\nError: $reason\n"
	exit 1
    }
}

# Log into the router.
# returns: 0 on success, 1 on failure, -1 if rsh was used successfully
proc login { router user userpswd passwd enapasswd cmethod cyphertype } {
    global spawn_id in_proc do_command do_script platform
    global prompt u_prompt p_prompt e_prompt sshcmd
    set in_proc 1
    set uprompt_seen 0

    # try each of the connection methods in $cmethod until one is successful
    set progs [llength $cmethod]
    foreach prog [lrange $cmethod 0 end] {
	incr progs -1
	if [string match "telnet*" $prog] {
	    regexp {telnet(:([^[:space:]]+))*} $prog command suffix port
	    if {"$port" == ""} {
		set retval [ catch {spawn telnet $router} reason ]
	    } else {
		set retval [ catch {spawn telnet $router $port} reason ]
	    }
	    if { $retval } {
		send_user "\nError: telnet failed: $reason\n"
		return 1
	    }
	} elseif [string match "ssh*" $prog] {
	    regexp {ssh(:([^[:space:]]+))*} $prog command suffix port
	    if {"$port" == ""} {
		set retval [ catch {spawn $sshcmd -c $cyphertype -x -l $user $router} reason ]

	    } else {
		set retval [ catch {spawn $sshcmd -c $cyphertype -x -l $user -p $port $router} reason ]
	    }
	    if { $retval } {
		send_user "\nError: $sshcmd failed: $reason\n"
		return 1
	    }
	} elseif ![string compare $prog "rsh"] {
	    global command

	    if { ! $do_command } {
		if { [llength $cmethod] == 1 } {
		    send_user "\nError: rsh is an invalid method for -x and "
		    send_user "interactive logins\n"
		}
		if { $progs == 0 } {
		    return 1
		}
		continue;
	    }

	    set commands [split $command \;]
	    set num_commands [llength $commands]
	    set rshfail 0
	    for {set i 0} {$i < $num_commands && !$rshfail} { incr i} {
		log_user 0
		set retval [ catch {spawn rsh $user@$router [lindex $commands $i] } reason ]
		if { $retval } {
		    send_user "\nError: rsh failed: $reason\n"
		    log_user 1; return 1
		}
		send_user "$router# [lindex $commands $i]\n"

		# rcmd does not get a pager and no prompts, so we just have to
		# look for failures & lines.
		expect {
		  "Connection refused"	{ catch {close}; wait;
					  send_user "\nError: Connection\
						    Refused ($prog): $router\n"
					  set rshfail 1
					}
		  -re "(Connection closed by|Connection to \[^\n\r]+ closed)" {
					  catch {close}; wait;
					  send_user "\nError: Connection\
						    closed ($prog): $router\n"
					  set rshfail 1
					}
		  "Host is unreachable"	{ catch {close}; wait;
					  send_user "\nError: Host Unreachable:\
						    $router\n"
					  set rshfail 1
					}
		  "No address associated with" {
					  catch {close}; wait;
					  send_user "\nError: Unknown host\
						    $router\n"
					  set rshfail 1
					}
		  -re "\b+"		{ exp_continue }
		  -re "\[\n\r]+"	{ send_user -- "$expect_out(buffer)"
					  exp_continue
					}
		  timeout		{ catch {close}; wait
					  send_user "\nError: TIMEOUT reached\n"
					  set rshfail 1
					}
		  eof			{ catch {close}; wait }
		}
		log_user 1
	    }
	    if { $rshfail } {
		if { !$progs } {
		    return 1
		} else {
		    continue
		}
	    }
	    # fake the end of the session for rancid.
	    send_user "$router# exit\n"
	    # return rsh "success"
	    return -1
	} else {
	    send_user "\nError: unknown connection method: $prog\n"
	    return 1
	}
	sleep 0.3

	# This helps cleanup each expect clause.
	expect_after {
	    timeout {
		send_user "\nError: TIMEOUT reached\n"
		catch {close}; wait
		if { $in_proc} {
		    return 1
		} else {
		    continue
		}
	    } eof {
		send_user "\nError: EOF received\n"
		catch {close}; wait
		if { $in_proc} {
		    return 1
		} else {
		    continue
		}
	    }
	}

    # Here we get a little tricky.  There are several possibilities:
    # the router can ask for a username and passwd and then
    # talk to the TACACS server to authenticate you, or if the
    # TACACS server is not working, then it will use the enable
    # passwd.  Or, the router might not have TACACS turned on,
    # then it will just send the passwd.
    # if telnet fails with connection refused, try ssh
    expect {
	-re "(Connection refused|Secure connection \[^\n\r]+ refused)" {
	    catch {close}; wait
	    if !$progs {
		send_user "\nError: Connection Refused ($prog): $router\n"
		return 1
	    }
	}
	-re "(Connection closed by|Connection to \[^\n\r]+ closed)" {
	    catch {close}; wait
	    if !$progs {
		send_user "\nError: Connection closed ($prog): $router\n"
		return 1
	    }
	}
	eof { send_user "\nError: Couldn't login: $router\n"; wait; return 1 }
	-nocase "unknown host\r" {
	    catch {close};
	    send_user "\nError: Unknown host $router\n"; wait; return 1
	}
	"Host is unreachable" {
	    catch {close};
	    send_user "\nError: Host Unreachable: $router\n"; wait; return 1
	}
	"No address associated with name" {
	    catch {close};
	    send_user "\nError: Unknown host $router\n"; wait; return 1
	}
	-re "(Host key not found |The authenticity of host .* be established).*\(yes\/no\)\?" {
	    send "yes\r"
	    send_user "\nHost $router added to the list of known hosts.\n"
	    exp_continue }
	-re "HOST IDENTIFICATION HAS CHANGED.* \(yes\/no\)\?"   {
	    send "no\r"
	    send_user "\nError: The host key for $router has changed.  Update the SSH known_hosts file accordingly.\n"
	    return 1 }
	-re "Offending key for .* \(yes\/no\)\?"   {
	    send "no\r"
	    send_user "\nError: host key mismatch for $router.  Update the SSH known_hosts file accordingly.\n"
	    return 1 }
	-re "(denied|Sorry)"	{
				  send_user "\nError: Check your passwd for $router\n"
				  catch {close}; wait; return 1
				}
	"Login failed"		{
				  send_user "\nError: Check your passwd for $router\n"
				  return 1
				}
	-re "% (Bad passwords|Authentication failed)"	{
				  send_user "\nError: Check your passwd for $router\n"
				  return 1
				}
	"Press any key to continue." {
				  # send_user "Pressing the ANY key\n"
				  send "\r"
				  exp_continue
				}
	-re "Enter Selection: " {
				  # Catalyst 1900s have some lame menu.  Enter
				  # K to reach a command-line.
				  send "K\r"
				  exp_continue
				}
	-re "Last login:"	{
				  exp_continue
				}
	-re "@\[^\r\n]+ $p_prompt"	{
				  # ssh pwd prompt
				  sleep 1
				  send "$userpswd\r"
				  exp_continue
				}
	-re "$u_prompt"		{
				  send "$user\r"
				  set uprompt_seen 1
				  exp_continue
				}
	-re "$p_prompt"		{
				  sleep 1
				  if {$uprompt_seen == 1} {
					send "$userpswd\r"
				  } else {
					send "$passwd\r"
				  }
				  exp_continue
				}
	-re "$prompt"		{ break; }
	"Login invalid"		{
				  send_user "\nError: Invalid login: $router\n";
				  catch {close}; wait; return 1
				}
     }
    }

    set in_proc 0
    return 0
}

# Enable
proc do_enable { enauser enapasswd } {
    global prompt in_proc
    global u_prompt e_prompt
    set in_proc 1

    send "enable\r"
    expect {
	-re "$u_prompt"	{ send "$enauser\r"; exp_continue}
	-re "$e_prompt"	{ send "$enapasswd\r"; exp_continue}
	"#"		{ set prompt "#" }
	"(enable)"	{ set prompt "> (enable) " }
	-re "(denied|Sorry|Incorrect)"	{
			  # % Access denied - from local auth and poss. others
			  send_user "\nError: Check your Enable passwd\n";
			  return 1
			}
	"% Error in authentication" {
			  send_user "\nError: Check your Enable passwd\n"
			  return 1
			}
	"% Bad passwords" {
			  send_user "\nError: Check your Enable passwd\n"
			  return 1
			}
    }
    # We set the prompt variable (above) so script files don't need
    # to know what it is.
    set in_proc 0
    return 0
}

# Run commands given on the command line.
proc run_commands { prompt command } {
    global in_proc platform
    set in_proc 1

    # If the prompt is (enable), then we are on a switch and the
    # command is "set length 0"; otherwise its "term length 0".
    # skip if its an extreme (since the pager can not be disabled on a
    # per-vty basis).
    if { [ string compare "extreme" "$platform" ] } {
	if [ regexp -- ".*> .*enable" "$prompt" ] {
#	    send "set length 0\r"
	    # This is ugly, but reduces code duplication, allowing the
	    # subsequent expects to handle everything as normal.
	    set command "set logging session disable;$command"
	} else {
#	    send "term length 0\r"
	    send "no more\r"
	}
	# escape any parens in the prompt, such as "(enable)"
	regsub -all {[)(]} $prompt {\\&} reprompt
	# match cisco config mode prompts too, such as router(config-if)#,
	# but catalyst does not change in this fashion.
	regsub -all {^(.{1,11}).*([#>])$} $reprompt {\1([^#>\r\n]+)?[#>](\\([^)\\r\\n]+\\))?} reprompt
	expect {
	    -re $reprompt	{}
	    -re "\[\n\r]+"	{ exp_continue }
	}
    } else {
	regsub -all "\[)(]" $prompt {\\&} reprompt
    }

    # this is the only way i see to get rid of more prompts in o/p..grrrrr
    log_user 0
    # Is this a multi-command?
    if [ string match "*\;*" "$command" ] {
	set commands [split $command \;]
	set num_commands [llength $commands]
	# the pager can not be turned off on the PIX, so we have to look
	# for the "More" prompt.  the extreme is equally obnoxious, with a
	# global switch in the config.
	for {set i 0} {$i < $num_commands} { incr i} {
	    send "[subst -nocommands [lindex $commands $i]]\r"
	    expect {
		-re "\b+"			{ exp_continue }
		-re "^\[^\n\r *]*$reprompt"	{ send_user -- "$expect_out(buffer)"
						}
		-re "^\[^\n\r]*$reprompt."	{ send_user -- "$expect_out(buffer)"
						  exp_continue }
		-re "^--More--\r\n"		{ # specific match c1900 pager
						  send " "
						  exp_continue }
		-re "\[\n\r]+"			{ send_user -- "$expect_out(buffer)"
						  exp_continue }
		-re "\[^\r\n]*Press <SPACE> to cont\[^\r\n]*"	{
						  send " "
						  # bloody ^[[2K after " "
						  expect {
							-re "^\[^\r\n]*\r" {}
							}
						  exp_continue
						}
		-re "^ *--More--\[^\n\r]*"	{
						  send " "
						  exp_continue }
		-re "^<-+ More -+>\[^\n\r]*"	{
						  send_user -- "$expect_out(buffer)"
						  send " "
						  exp_continue }
	    }
	}
    } else {
	# the pager can not be turned off on the PIX, so we have to look
	# for the "More" prompt.  the extreme is equally obnoxious, with a
	# global switch in the config.
	send "[subst -nocommands $command]\r"
	expect {
		-re "\b+"			{ exp_continue }
		-re "^\[^\n\r *]*$reprompt"	{ send_user -- "$expect_out(buffer)"
						}
		-re "^\[^\n\r]*$reprompt."	{ send_user -- "$expect_out(buffer)"
						  exp_continue }
		-re "^--More--\r\n"		{ # specific match c1900 pager
						  send " "
						  exp_continue }
		-re "\[\n\r]+"			{ send_user -- "$expect_out(buffer)"
						  exp_continue }
		-re "\[^\r\n]*Press <SPACE> to cont\[^\r\n]*"	{
						  send " "
						  # bloody ^[[2K after " "
						  expect {
							-re "^\[^\r\n]*\r" {}
							}
						  exp_continue
						}
		-re "^ *--More--\[^\n\r]*"	{
						  send " "
						  exp_continue }
		-re "^<-+ More -+>\[^\n\r]*"	{
						  send_user -- "$expect_out(buffer)"
						  send " "
						  exp_continue }
	}
    }
    log_user 1

    if { [ string compare "extreme" "$platform" ] } {
	send -h "exit\r"
    } else {
	send -h "quit\r"
    }
    expect {
	-re "^\[^\n\r *]*$reprompt"		{
						  # the Cisco CE and Jnx ERX
						  # return to non-enabled mode
						  # on exit in enabled mode.
						  send -h "exit\r"
						  exp_continue;
						}
	"Would you like to save them now"	{ # Force10
						  send "n\r"
						  exp_continue
						}
	"Configuration changes have occurred.*"	{ # Cisco CSS
						  send "n\r"
						  exp_continue
						}
	"Do you wish to save your configuration changes" {
						  send "n\r"
						  exp_continue
						}
	-re "\[\n\r]+"				{ exp_continue }
	timeout					{ catch {close}; wait
						  return 0
						}
	eof					{ return 0 }
    }
    set in_proc 0
}

#
# For each router... (this is main loop)
#
source_password_file $password_file
set in_proc 0
foreach router [lrange $argv $i end] {
    set router [string tolower $router]
    # attempt at platform switching.
    set platform ""
    send_user -- "$router\n"

    # Figure out the prompt.
    # autoenable is off by default.  If we have it defined, it was done
    # on the command line.  If it is not specifically set on the command
    # line, check the password file.
    if $avautoenable {
	set autoenable 1
	set enable 0
	set prompt "(#| \\(enable\\))"
    } else {
	set ae [find autoenable $router]
	if { "$ae" == "1" } {
	    set autoenable 1
	    set enable 0
	    set prompt "(#| \\(enable\\))"
	} else {
	    set autoenable 0
	    set enable $avenable
	    set prompt ">"
	}
    }
    # OmniSwitch
    set prompt "->"

    # look for noenable option in .cloginrc
    if { [find noenable $router] != "" } {
	set enable 0
    }

    # Figure out passwords
    if { $do_passwd || $do_enapasswd } {
      set pswd [find password $router]
      if { [llength $pswd] == 0 } {
	send_user -- "\nError: no password for $router in $password_file.\n"
	continue
      }
      if { $enable && $do_enapasswd && $autoenable == 0 && [llength $pswd] < 2 } {
	send_user -- "\nError: no enable password for $router in $password_file.\n"
	continue
      }
      set passwd [join [lindex $pswd 0] ""]
      set enapasswd [join [lindex $pswd 1] ""]
    } else {
	set passwd $userpasswd
	set enapasswd $enapasswd
    }

    # Figure out username
    if {[info exists username]} {
      # command line username
      set ruser $username
    } else {
      set ruser [join [find user $router] ""]
      if { "$ruser" == "" } { set ruser $default_user }
    }

    # Figure out username's password (if different from the vty password)
    if {[info exists userpasswd]} {
      # command line username
      set userpswd $userpasswd
    } else {
      set userpswd [join [find userpassword $router] ""]
      if { "$userpswd" == "" } { set userpswd $passwd }
    }

    # Figure out enable username
    if {[info exists enausername]} {
      # command line enausername
      set enauser $enausername
    } else {
      set enauser [join [find enauser $router] ""]
      if { "$enauser" == "" } { set enauser $ruser }
    }

    # Figure out prompts
    set u_prompt [find userprompt $router]
    if { "$u_prompt" == "" } {
	set u_prompt "(Username|Login|login|user name):"
    } else {
	set u_prompt [join [lindex $u_prompt 0] ""]
    }
    set p_prompt [find passprompt $router]
    if { "$p_prompt" == "" } {
	set p_prompt "(\[Pp]assword|passwd)\ for.*method:"
    } else {
	set p_prompt [join [lindex $p_prompt 0] ""]
    }
    set e_prompt [find enableprompt $router]
    if { "$e_prompt" == "" } {
	set e_prompt "\[Pp]assword:"
    } else {
	set e_prompt [join [lindex $e_prompt 0] ""]
    }

    # Figure out cypher type
    if {[info exists cypher]} {
        # command line cypher type
        set cyphertype $cypher
    } else {
        set cyphertype [find cyphertype $router]
        if { "$cyphertype" == "" } { set cyphertype "3des" }
    }

    # Figure out connection method
    set cmethod [find method $router]
    if { "$cmethod" == "" } { set cmethod {{telnet} {ssh}} }

    # Figure out the SSH executable name
    set sshcmd [find sshcmd $router]
    if { "$sshcmd" == "" } { set sshcmd {ssh} }

    # Login to the router
    if {[login $router $ruser $userpswd $passwd $enapasswd $cmethod $cyphertype]} {
	# if login failed or rsh was unsuccessful, move on to the next device
	continue
    }
    if { $enable } {
	if {[do_enable $enauser $enapasswd]} {
	    if { $do_command || $do_script } {
		catch {close}; catch {wait}
		continue
	    }
	}
    }
    # we are logged in, now figure out the full prompt
    send "\r"
    expect {
	-re "\[\r\n]+"		{ exp_continue; }
	-re "^(.+\[:.])1 ($prompt)" { # stoopid extreme cmd-line numbers and
				  # prompt based on state of config changes,
				  # which may have an * at the beginning.
				  set junk $expect_out(1,string)
				  regsub -all "^\\\* " $expect_out(1,string) {} junk
				  set prompt ".? ?$junk\[0-9]+ $expect_out(2,string)";
				  set platform "extreme"
				}
	-re "^.+$prompt"	{ set junk $expect_out(0,string);
				  regsub -all "\[\]\[]" $junk {\\&} prompt;
				}
	-re "^.+> \\\(enable\\\)"	{
				  set junk $expect_out(0,string);
				  regsub -all "\[\]\[]" $junk {\\&} prompt;
				}
	-re "^.*->"		{ # our configured OmniSwitch prompt
				  set junk $expect_out(0,string);
				  regsub -all "\[\]\[]" $junk {\\&} prompt;
				}

    }

    if { $do_command } {
	if {[run_commands $prompt $command]} {
	    continue
	}
    } elseif { $do_script } {
	# If the prompt is (enable), then we are on a switch and the
	# command is "set length 0"; otherwise its "term length 0".
	if [ regexp -- ".*> .*enable" "$prompt" ] {
	    send "set length 0\r"
	    send "set logging session disable\r"
	} else {
	    send "term length 0\r"
	}
	expect -re $prompt	{}
	source $sfile
	close
    } else {
	label $router
	log_user 1
	interact
    }

    # End of for each router
    wait
    sleep 0.3
}
exit 0
-------------- next part --------------
#! /usr/bin/perl
##
## $Id: rancid.in,v 1.218 2006/10/05 04:27:43 heas Exp $
##
## rancid 2.3.2a7
## Copyright (C) 1997-2006 by Terrapin Communications, Inc.
## All rights reserved.
##
## This software may be freely copied, modified and redistributed
## without fee for non-commerical purposes provided that this license
## remains intact and unmodified with any RANCID distribution.
##
## There is no warranty or other guarantee of fitness of this software.
## It is provided solely "as is".  The author(s) disclaim(s) all
## responsibility and liability with respect to this software's usage
## or its effect upon hardware, computer systems, other software, or
## anything else.
##
## Except where noted otherwise, rancid was written by and is maintained by
## Henry Kilmer, John Heasley, Andrew Partan, Pete Whiting, and Austin Schutz.
##
#
#  RANCID - Really Awesome New Cisco confIg Differ
#
# usage: rancid [-dV] [-l] [-f filename | hostname]
#
# Alcatel OmniSwitch hack by Alexander Bochmann <ab+rancid at st.gxis.de>
#
use Getopt::Std;
getopts('dflV');
if ($opt_V) {
    print "rancid 2.3.2a7\n";
    exit(0);
}
$log = $opt_l;
$debug = $opt_d;
$file = $opt_f;
$host = $ARGV[0];
$clean_run = 0;
$found_end = 0;
$found_version = 0;
$found_env = 0;
$found_diag = 0;
$timeo = 90;				# ologin timeout in seconds

my(@commandtable, %commands, @commands);# command lists
my(%filter_pwds);			# password filtering mode

# This routine is used to print out the router configuration
sub ProcessHistory {
    my($new_hist_tag,$new_command,$command_string, at string) = (@_);
    if ((($new_hist_tag ne $hist_tag) || ($new_command ne $command))
	&& defined %history) {
	print eval "$command \%history";
	undef %history;
    }
    if (($new_hist_tag) && ($new_command) && ($command_string)) {
	if ($history{$command_string}) {
	    $history{$command_string} = "$history{$command_string}@string";
	} else {
	    $history{$command_string} = "@string";
	}
    } elsif (($new_hist_tag) && ($new_command)) {
	$history{++$#history} = "@string";
    } else {
	print "@string";
    }
    $hist_tag = $new_hist_tag;
    $command = $new_command;
    1;
}

sub numerically { $a <=> $b; }

# This is a sort routine that will sort numerically on the
# keys of a hash as if it were a normal array.
sub keynsort {
    local(%lines) = @_;
    local($i) = 0;
    local(@sorted_lines);
    foreach $key (sort numerically keys(%lines)) {
	$sorted_lines[$i] = $lines{$key};
	$i++;
    }
    @sorted_lines;
}

# This is a sort routine that will sort on the
# keys of a hash as if it were a normal array.
sub keysort {
    local(%lines) = @_;
    local($i) = 0;
    local(@sorted_lines);
    foreach $key (sort keys(%lines)) {
	$sorted_lines[$i] = $lines{$key};
	$i++;
    }
    @sorted_lines;
}

# This is a sort routine that will sort on the
# values of a hash as if it were a normal array.
sub valsort{
    local(%lines) = @_;
    local($i) = 0;
    local(@sorted_lines);
    foreach $key (sort values %lines) {
	$sorted_lines[$i] = $key;
	$i++;
    }
    @sorted_lines;
}

# This is a numerical sort routine (ascending).
sub numsort {
    local(%lines) = @_;
    local($i) = 0;
    local(@sorted_lines);
    foreach $num (sort {$a <=> $b} keys %lines) {
	$sorted_lines[$i] = $lines{$num};
	$i++;
    }
    @sorted_lines;
}

# This is a sort routine that will sort on the
# ip address when the ip address is anywhere in
# the strings.
sub ipsort {
    local(%lines) = @_;
    local($i) = 0;
    local(@sorted_lines);
    foreach $addr (sort sortbyipaddr keys %lines) {
	$sorted_lines[$i] = $lines{$addr};
	$i++;
    }
    @sorted_lines;
}

# These two routines will sort based upon IP addresses
sub ipaddrval {
    my(@a) = ($_[0] =~ m#^(\d+)\.(\d+)\.(\d+)\.(\d+)$#);
    $a[3] + 256 * ($a[2] + 256 * ($a[1] +256 * $a[0]));
}
sub sortbyipaddr {
    &ipaddrval($a) <=> &ipaddrval($b);
}

# This routine parses "show configuration status"
sub ShowCfgstat {
    print STDERR "    In ShowCfgstat: $_" if ($debug);

    while (<INPUT>) {
	tr/\015//d;
	# only want the "Running configuration and saved configuration..." line
	if (/^Running configuration and saved configuration/) {
		ProcessHistory("COMMENTS","keysort","A1","!Cfg: $_");
		ProcessHistory("COMMENTS","keysort","A1","! \n");
		$found_end++;
		last;
	}
    }
    return(0);
}

# This routine parses "show configuration snapshot"
sub ShowConfig {
    print STDERR "    In ShowConfig: $_" if ($debug);

    while (<INPUT>) {
	tr/\015//d;
	next if (/^(\s*|\s*$cmd\s*)$/);
	next if (/^$/); # drop empty lines
	return(1) if /Line has invalid autocommand /;
	return(1) if /(Invalid input detected|Type help or )/;
	if (/^$prompt/) {
		ProcessHistory("","","","!$_");
		$found_end++;
		last;
	}
	# catch all output
	ProcessHistory("","","","$_");
    }
    return(0);
}

# This routine parses "show chassis"
sub ShowChassis {
    print STDERR "    In ShowChassis: $_" if ($debug);

    while (<INPUT>) {
	tr/\015//d;
	if (/^$prompt/) {
		ProcessHistory("COMMENTS","keysort","F1","! \n");
		last;
	}
	next if (/^(\s*|\s*$cmd\s*)$/);
	next if (/^$/); # drop empty lines
	return(1) if /Line has invalid autocommand /;
	return(1) if /(Invalid input detected|Type help or )/;
	ProcessHistory("COMMENTS","keysort","F1","!Hw: $_") && next;
    }
    return(0);
}

# This routine parses "show module"
sub ShowModule {
    print STDERR "    In ShowModule: $_" if ($debug);

    while (<INPUT>) {
	tr/\015//d;
	if (/^$prompt/) {
		ProcessHistory("COMMENTS","keysort","F2","! \n");
		last;
	}
	next if (/^(\s*|\s*$cmd\s*)$/);
	next if (/^$/); # drop empty lines
	return(1) if /Line has invalid autocommand /;
	return(1) if /(Invalid input detected|Type help or )/;
	ProcessHistory("COMMENTS","keysort","F2","!Mod: $_") && next;
    }
    return(0);
}

# This routine parses "show microcode"
sub ShowMicrocode {
    print STDERR "    In ShowMicrocode: $_" if ($debug);

    while (<INPUT>) {
	tr/\015//d;
	if (/^$prompt/) {
		ProcessHistory("COMMENTS","keysort","F3","! \n");
		last;
	}
	next if (/^(\s*|\s*$cmd\s*)$/);
	next if (/^$/); # drop empty lines
	return(1) if /Line has invalid autocommand /;
	return(1) if /(Invalid input detected|Type help or )/;
	ProcessHistory("COMMENTS","keysort","F3","!Sw: $_") && next;
    }
    return(0);
}

# This routine parses "show hardware info"
sub ShowHw {
    print STDERR "    In ShowHw: $_" if ($debug);

    while (<INPUT>) {
	tr/\015//d;
	last if (/^$prompt/);
	next if (/^(\s*|\s*$cmd\s*)$/);
	return(1) if /Line has invalid autocommand /;
	return(1) if /(Invalid input detected|Type help or )/;
	ProcessHistory("COMMENTS","keysort","F5","!Hw: $_") && next;
    }
    return(0);
}

# This routine parses "show version"
sub ShowVersion {
    print STDERR "    In ShowVersion: $_" if ($debug);
    my($slaveslot);

    while (<INPUT>) {
	tr/\015//d;
	if (/^$prompt/) { $found_version=1; last};
	next if (/^(\s*|\s*$cmd\s*)$/);
	return(1) if /Line has invalid autocommand /;
	return(1) if /(Invalid input detected|Type help or )/;
	return(-1) if (/command authorization failed/i);
	return(0) if ($found_version);		# Only do this routine once
	# the pager can not be disabled per-session on the PIX
	if (/^(<-+ More -+>)/) {
	    my($len) = length($1);
	    s/^$1\s{$len}//;
	}

	if (/^Slave in slot (\d+) is running/) {
	    $slave = " Slave:";
	    $slaveslot = ", slot $1";
	    next;
	}
	if (/^Application and Content Networking Software/) { $type="CE"; }
	/^Application and Content Networking Software Release /i &&
	    ProcessHistory("COMMENTS","keysort","F1", "!Image: $_") && next;
	/^Cisco Secure PIX /i &&
	    ProcessHistory("COMMENTS","keysort","F1", "!Image: $_") && next;
	# PIX 6 fail-over license, as in "This PIX has an Unrestricted (UR)
	# license."  PIX 7 as "his platform has ..."
	/^This (PIX|platform) has an?\s+(.*)$/ &&
	    ProcessHistory("COMMENTS","keysort","D1", "!$_") && next;
	/^(Cisco )?IOS .* Software,? \(([A-Za-z-0-9_]*)\), .*Version\s+(.*)$/ &&
	    ProcessHistory("COMMENTS","keysort","F1",
		"!Image:$slave Software: $2, $3\n") && next;
	/^([A-Za-z-0-9_]*) Synced to mainline version: (.*)$/ &&
	    ProcessHistory("COMMENTS","keysort","F2",
		"!Image:$slave $1 Synced to mainline version: $2\n") && next;
	/^Compiled (.*)$/ &&
	    ProcessHistory("COMMENTS","keysort","F3",
		"!Image:$slave Compiled: $1\n") && next;
	/^ROM: (IOS \S+ )?(System )?Bootstrap.*(Version.*)$/ &&
	    ProcessHistory("COMMENTS","keysort","G1",
		"!ROM Bootstrap: $3\n") && next;
	if (/^Hardware:\s+(.*), (.* RAM), CPU (.*)$/) {
	    ProcessHistory("COMMENTS","keysort","A1",
		"!Chassis type: $1 - a PIX\n");
	    ProcessHistory("COMMENTS","keysort","A2",
		"!CPU: $3\n");
	    ProcessHistory("COMMENTS","keysort","B1", "!Memory: $2\n");
	}
	/^Serial Number:\s+(.*)$/ &&
	    ProcessHistory("COMMENTS","keysort","C1", "!$_") && next;
	# More PIX stuff
	/^Encryption hardware device\s+:\s+(.*)/ &&
	    ProcessHistory("COMMENTS","keysort","A3", "!Encryption: $1\n") &&
	    next;
	/^running activation key\s*:\s+(.*)/i &&
	    ProcessHistory("COMMENTS","keysort","D2", "!Key: $1\n") &&
	    next;
	# Flash on the PIX or FWSM (FireWall Switch Module)
	/^Flash(\s+\S+)+ \@ 0x\S+,\s+(\S+)/ &&
	    ProcessHistory("COMMENTS","keysort","B2", "!Memory: Flash $2\n") &&
	    next;
	# CatOS 3500xl stuff
	/^System serial number(:\s+.*)$/ &&
	    ProcessHistory("COMMENTS","keysort","C1", "!Serial Number$1\n") &&
	    next;
	/^Model / &&
	    ProcessHistory("COMMENTS","keysort","C2", "!$_") && next;
	/^Motherboard / &&
	    ProcessHistory("COMMENTS","keysort","C3", "!$_") && next;
	/^Power supply / &&
	    ProcessHistory("COMMENTS","keysort","C4", "!$_") && next;

	/^Activation Key:\s+(.*)$/ &&
	    ProcessHistory("COMMENTS","keysort","C2", "!$_") && next;
	/^ROM: \d+ Bootstrap .*(Version.*)$/ &&
	    ProcessHistory("COMMENTS","keysort","G2",
		"!ROM Image: Bootstrap $1\n!\n") && next;
	/^ROM: .*(Version.*)$/ &&
	    ProcessHistory("COMMENTS","keysort","G3","!ROM Image: $1\n") && next;
	/^BOOTFLASH: .*(Version.*)$/ &&
	    ProcessHistory("COMMENTS","keysort","G4","!BOOTFLASH: $1\n") && next;
	/^BOOTLDR: .*(Version.*)$/ &&
	    ProcessHistory("COMMENTS","keysort","G4","!BOOTLDR: $1\n") && next;
	/^System image file is "([^\"]*)", booted via (\S*)/ &&
# removed the booted source due to
# CSCdk28131: cycling info in 'sh ver'
#	ProcessHistory("COMMENTS","keysort","F4","!Image: booted via $2, $1\n") &&
	    ProcessHistory("COMMENTS","keysort","F4","!Image: booted $1\n") &&
	    next;
	/^System image file is "([^\"]*)"$/ &&
	    ProcessHistory("COMMENTS","keysort","F5","!Image: $1\n") && next;
	if (/(\S+)\s+(?:\((\S+)\)\s+processor|\(revision[^)]+\)).*\s+with (\S+k) bytes/i) {
	    my($proc) = $1;
	    my($cpu) = $2;
	    my($mem) = $3;
	    my($device) = "router";

	    # the next line ought to be the more specific cpu info, grab it.
	    # yet, some boards/IOS vers have a processor ID line between these
	    # two.  grrr.  make sure we dont grab the "software" junk that
	    # follows these lines by looking for "CPU at " or the 2600s
	    # "processor: " unique string.  there are undoubtedly many other
	    # incantations.  for a slave, we dont get this info and its just a
	    # blank line.
	    $_ = <INPUT>;
	    if (/processor board id/i) {
		my($sn);

		if (/processor board id (\S+)/i) {
		    $sn = $1;
		    $sn =~ s/,$//;
		    ProcessHistory("COMMENTS","keysort","D9",
							"!Processor ID: $sn\n");
		}
		$_ = <INPUT>;
	    }
	    $_ = "" if (! /(cpu at |processor: |$cpu processor,)/i);
	    tr/\015//d;
	    s/implementation/impl/i;
	    if ($_ !~ /^\s*$/) {
		chomp;
		s/^/, /;
	    }

	    if ($proc eq "CSC") {
		$type = "AGS";
	    } elsif ($proc eq "CSC4") {
		$type = "AGS+";
	    } elsif ($proc =~ /^(AS)?25[12][12]/) {
		$type = "2500";
	    } elsif ($proc =~ /261[01]/ || $proc =~ /262[01]/ ) {
		$type = "2600";
	    } elsif ($proc =~ /^36[0246][0-9]/) {
		$type = "3600";
	    } elsif ($proc =~ /^37/) {
		$type = "3700";
	    } elsif ($proc =~ /^38/) {
		$type = "3800";
	    } elsif ($proc eq "RSP7000") {
		$type = "7500";
	    } elsif ($proc =~ /RSP\d/) {
		$type = "7500";
	    } elsif ($proc eq "RP1") {
		$type = "7000";
	    } elsif ($proc eq "RP") {
		$type = "7000";
	    } elsif ($proc =~ /720[246]/) {
		$type = "7200";
	    } elsif ($proc =~ /1200[48]\/GRP/ || $proc =~ /1201[26]\/GRP/) {
		$type = "12000";
	    } elsif ($proc =~ /1201[26]-8R\/GRP/) {
		$type = "12000";
	    } elsif ($proc =~ /WS-C29/) {
		$type = "2900XL";
		$device = "switch";
	    } elsif ($proc =~ /WS-C355/) {
		$type = "3550";
		$device = "switch";
	    } elsif ($proc =~ /WS-C35/) {
		$type = "3500XL";
		$device = "switch";
	    } elsif ($proc =~ /WS-C45/) {
		$type = "4500";
		$device = "switch";
	    } elsif ($proc =~ /6000/) {
		$type = "6000";
		$device = "switch";
	    } elsif ($proc =~ /CISCO76/) {
		$type = "7600";
		$device = "router";
	    } elsif ($proc =~ /1900/) {
		$type = "1900";
		$device = "switch";
	    } elsif ( $proc =~ /^73/) {
		$type = "7300";
	    } else {
		$type = $proc;
	    }
	    print STDERR "TYPE = $type\n" if ($debug);
	    ProcessHistory("COMMENTS","keysort","A1",
		"!Chassis type:$slave $proc - a $type $device\n");
	    ProcessHistory("COMMENTS","keysort","B1",
		"!Memory:$slave main $mem\n");
	    if (defined($cpu)) {
		ProcessHistory("COMMENTS","keysort","A3",
			       "!CPU:$slave $cpu$_$slaveslot\n");
	    }
	    next;
	}
	if (/(\S+) Silicon\s*Switch Processor/) {
	    if (!defined($C0)) {
		$C0=1; ProcessHistory("COMMENTS","keysort","C0","!\n");
	    }
	    ProcessHistory("COMMENTS","keysort","C2","!SSP: $1\n");
	    $ssp = 1;
	    $sspmem = $1;
	    next;
	}
	/^(\d+[kK]) bytes of multibus/ &&
	    ProcessHistory("COMMENTS","keysort","B2",
		"!Memory: multibus $1\n") && next;
	/^(\d+[kK]) bytes of (non-volatile|NVRAM)/ &&
	    ProcessHistory("COMMENTS","keysort","B3",
		"!Memory: nvram $1\n") && next;
	/^(\d+[kK]) bytes of flash memory/ &&
	    ProcessHistory("COMMENTS","keysort","B5","!Memory: flash $1\n") &&
	    next;
	/^(\d+[kK]) bytes of .*flash partition/ &&
	    ProcessHistory("COMMENTS","keysort","B6",
		"!Memory: flash partition $1\n") && next;
	/^(\d+[kK]) bytes of Flash internal/ &&
	    ProcessHistory("COMMENTS","keysort","B4",
		"!Memory: bootflash $1\n") && next;
	if (/^(\d+[kK]) bytes of (Flash|ATA)?.*PCMCIA .*(slot|disk) ?(\d)/i) {
	    ProcessHistory("COMMENTS","keysort","B7",
		"!Memory: pcmcia $2 $3$4 $1\n");
	    next;
	}
	if (/^WARNING/) {
	    if (!defined($I0)) {
		$I0=1;
		ProcessHistory("COMMENTS","keysort","I0","!\n");
	    }
	    ProcessHistory("COMMENTS","keysort","I1","! $_");
	}
	if (/^Configuration register is (.*)$/) {
	    $config_register=$1;
	    next;
	}
	if (/^Configuration register on node \S+ is (.*)$/) {
	    $config_register=$1 if $config_register eq "";
	    next;
	}
    }
    return(0);
}

# This routine parses "show redundancy"
sub ShowRedundancy {
    print STDERR "    In ShowRedundancy: $_" if ($debug);

    while (<INPUT>) {
	tr/\015//d;
	last if (/^$prompt/);
	next if (/^(\s*|\s*$cmd\s*)$/);
	return(1) if /Line has invalid autocommand /;
	return(1) if /(Invalid input detected|Type help or )/;
	# the pager can not be disabled per-session on the PIX
	if (/^(<-+ More -+>)/) {
	    my($len) = length($1);
	    s/^$1\s{$len}//;
	}

	/^IOS .* Software \(([A-Za-z-0-9_]*)\), .*Version\s+(.*)$/ &&
	    ProcessHistory("COMMENTS","keysort","F1",
		"!Image:$slave Software: $1, $2\n") && next;
	/^Compiled (.*)$/ &&
	    ProcessHistory("COMMENTS","keysort","F3",
		"!Image:$slave Compiled: $1\n") && next;
    }
    return(0);
}

# This routine parses "show IDprom"
sub ShowIDprom {
    my($tmp);

    print STDERR "    In ShowIDprom: $_" if ($debug);

    while (<INPUT>) {
	tr/\015//d;
	last if (/^$prompt/);
	next if (/^(\s*|\s*$cmd\s*)$/);
	return(1) if /Line has invalid autocommand /;
	return(1) if /(Invalid input detected|Type help or )/;
	# the pager can not be disabled per-session on the PIX
	if (/^(<-+ More -+>)/) {
	    my($len) = length($1);
	    s/^$1\s{$len}//;
	}

	/FRU is .(.*)\'/ && ($tmp = $1);
	/Product Number = .(.*)\'/ &&
		ProcessHistory("COMMENTS","keysort","D0",
				"!Catalyst Chassis type: $1, $tmp\n");
	/Serial Number = .([0-9A-Za-z]+)/ &&
		ProcessHistory("COMMENTS","keysort","D1",
				"!Catalyst Chassis S/N: $1\n");
	/Manufacturing Assembly Number = .([-0-9]+)/ && ($tmp = $1);
	/Manufacturing Assembly Revision = .(.*)\'/ && ($tmp .= ", rev " . $1);
	/Hardware Revision = ([0-9.]+)/ &&
		ProcessHistory("COMMENTS","keysort","D2",
				"!Catalyst Chassis assembly: $tmp, ver $1\n");
    }
    return(0);
}

# This routine parses "show install active"
sub ShowInstallActive {
    print STDERR "    In ShowInstallActive: $_" if ($debug);

    while (<INPUT>) {
	tr/\015//d;
	last if (/^$prompt/);
	next if (/^(\s*|\s*$cmd\s*)$/);
	return(1) if /^\s*\^\s*$/;
	return(1) if /Line has invalid autocommand /;
	return(1) if /(Invalid input detected|Type help or )/;
	return(-1) if (/command authorization failed/i);
	# the pager can not be disabled per-session on the PIX
	if (/^(<-+ More -+>)/) {
	    my($len) = length($1);
	    s/^$1\s{$len}//;
	}

	ProcessHistory("COMMENTS","keysort","F5","!Image: $_") && next;
    }
    return(0);
}

# This routine parses "show env all"
sub ShowEnv {
    # Skip if this is not a 7500, 7200, or 7000.
    print STDERR "    In ShowEnv: $_" if ($debug);

    while (<INPUT>) {
	tr/\015//d;
	if (/^$prompt/) { $found_env=1; last};
	next if (/^(\s*|\s*$cmd\s*)$/);
	#return(1) if ($type !~ /^7/);
	return(1) if /Line has invalid autocommand /;
	return(1) if /(Invalid input detected|Type help or )/;
	return(-1) if (/command authorization failed/i);
	return(0) if ($found_env);		# Only do this routine once
	# the pager can not be disabled per-session on the PIX
	if (/^(<-+ More -+>)/) {
	    my($len) = length($1);
	    s/^$1\s{$len}//;
	}

	if (!defined($E0)) {
	    $E0=1;
	    ProcessHistory("COMMENTS","keysort","E0","!\n");
	}
	if (/^Arbiter type (\d), backplane type (\S+)/) {
	    if (!defined($C0)) {
		$C0=1; ProcessHistory("COMMENTS","keysort","C0","!\n");
	    }
	    ProcessHistory("COMMENTS","keysort","C1",
		"!Enviromental Arbiter Type: $1\n");
	    ProcessHistory("COMMENTS","keysort","A2",
		"!Chassis type: $2 backplane\n");
	    next;
	}
	/^Power Supply Information$/ && next;
	/^\s*Power Module\s+Voltage\s+Current$/ && next;
	/^\s*(Power [^:\n]+)$/ &&
	    ProcessHistory("COMMENTS","keysort","E1","!Power: $1\n") && next;
	/^\s*(Lower Power .*)/i &&
	    ProcessHistory("COMMENTS","keysort","E2","!Power: $1\n") && next;
	/^\s*(redundant .*)/i &&
	    ProcessHistory("COMMENTS","keysort","E2","!Power: $1\n") && next;
	/^\s*(RPS is .*)/i &&
	    ProcessHistory("COMMENTS","keysort","E2","!Power: $1\n") && next;
    }
    ProcessHistory("COMMENTS","","","!\n");
    return(0);
}

# This routine parses "show rsp chassis-info" for the rsp
# This will create arrays for hw info.
sub ShowRSP {
    print STDERR "    In ShowRSP: $_" if ($debug);

    while (<INPUT>) {
	tr/\015//d;
	last if (/^$prompt/);
	next if (/^(\s*|\s*$cmd\s*)$/);
	return(-1) if (/command authorization failed/i);
	# return(1) if ($type !~ /^12[40]/);
	# the pager can not be disabled per-session on the PIX
	if (/^(<-+ More -+>)/) {
	    my($len) = length($1);
	    s/^$1\s{$len}//;
	}
	/^$/ && next;

	/^\s+Chassis model: (\S+)/ &&
	    ProcessHistory("COMMENTS","keysort","D1",
				"!RSP Chassis model: $1\n") &&
	    next;
	/^\s+Chassis S\/N: (.*)$/ &&
	    ProcessHistory("COMMENTS","keysort","D2",
				"!RSP Chassis S/N: $1\n") &&
	    next;
    }

    return(0);
}

# This routine parses "show gsr chassis-info" for the gsr
# This will create arrays for hw info.
sub ShowGSR {
    # Skip if this is not a 1200n.
    print STDERR "    In ShowGSR: $_" if ($debug);

    while (<INPUT>) {
	tr/\015//d;
	last if (/^$prompt/);
	next if (/^(\s*|\s*$cmd\s*)$/);
	return(-1) if (/command authorization failed/i);
	# return(1) if ($type !~ /^12[40]/);
	# the pager can not be disabled per-session on the PIX
	if (/^(<-+ More -+>)/) {
	    my($len) = length($1);
	    s/^$1\s{$len}//;
	}
	/^$/ && next;

	/^\s+Chassis: type (\S+) Fab Ver: (\S+)/ &&
	    ProcessHistory("COMMENTS","keysort","D1",
				"!GSR Chassis type: $1 Fab Ver: $2\n") &&
	    next;
	/^\s+Chassis S\/N: (.*)$/ &&
	    ProcessHistory("COMMENTS","keysort","D2",
				"!GSR Chassis S/N: $1\n") &&
	    next;
	/^\s+PCA: (\S+)\s*rev: (\S+)\s*dev: \S+\s*HW ver: (\S+)$/ &&
	    ProcessHistory("COMMENTS","keysort","D3",
				"!GSR Backplane PCA: $1, rev $2, ver $3\n") &&
	    next;
	/^\s+Backplane S\/N: (\S+)$/ &&
	    ProcessHistory("COMMENTS","keysort","D4",
				"!GSR Backplane S/N: $1\n") &&
	    next;
    }
    ProcessHistory("COMMENTS","","","!\n");
    return(0);
}

# This routine parses "show boot"
sub ShowBoot {
    # Pick up boot variables if 7000/7200/7500/12000/2900/3500;
    # otherwise pick up bootflash.
    print STDERR "    In ShowBoot: $_" if ($debug);

    while (<INPUT>) {
	tr/\015//d;
	last if (/^$prompt/);
	next if (/^(\s*|\s*$cmd\s*)$/);
	return(1) if /^\s*\^\s*$/;
	return(1) if /Line has invalid autocommand /;
	return(1) if /(Invalid input detected|Type help or )/;
	return(1) if /Ambiguous command/i;
	return(1) if /(Open device \S+ failed|Error opening \S+:)/;
	return(-1) if (/command authorization failed/i);
	# the pager can not be disabled per-session on the PIX
	if (/^(<-+ More -+>)/) {
	    my($len) = length($1);
	    s/^$1\s{$len}//;
	}

	next if /CONFGEN variable/;
	if (!defined($H0)) {
	    $H0=1; ProcessHistory("COMMENTS","keysort","H0","!\n");
	}
	if ($type !~ /^(12[04]|7)/) {
	    if ($type !~ /^(29|35)00/) {
		ProcessHistory("COMMENTS","keysort","H2","!BootFlash: $_");
	    } else {
		ProcessHistory("COMMENTS","keysort","H1","!Variable: $_");
	    }
	} elsif (/variable/) {
	    ProcessHistory("COMMENTS","keysort","H1","!Variable: $_");
	}
    }
    ProcessHistory("COMMENTS","","","!\n");
    return(0);
}

# This routine parses "show flash"
sub ShowFlash {
    # skip if this is 7000, 7200, 7500, or 12000; else we end up with
    # redundant data from dir /all slot0:
    print STDERR "    In ShowFlash: $_" if ($debug);

    while (<INPUT>) {
	tr/\015//d;
	last if (/^$prompt/);
	next if (/^(\s*|\s*$cmd\s*)$/);
	return(1) if ($type =~ /^(12[40]|7)/);
	return(1) if /^\s*\^\s*$/;
	return(1) if /Line has invalid autocommand /;
	return(1) if /(Invalid input detected|Type help or )/;
	return(-1) if (/command authorization failed/i);
	# the pager can not be disabled per-session on the PIX
	if (/^(<-+ More -+>)/) {
	    my($len) = length($1);
	    s/^$1\s{$len}//;
	}

	/\s+(multiple-fs|nv_hdr|vlan\.dat)$/ && next;
	ProcessHistory("FLASH","","","!Flash: $_");
    }
    ProcessHistory("","","","!\n");
    return;
}

# This routine parses "dir /all ((disk|slot)N|bootflash|nvram):"
sub DirSlotN {
    # Skip if this is not a 3600, 7000, 7200, 7500, or 12000.
    print STDERR "    In DirSlotN: $_" if ($debug);

    my($dev) = (/\s([^\s]+):/);

    while (<INPUT>) {
	tr/\015//d;
	last if (/^$prompt/);
	next if (/^(\s*|\s*$cmd\s*)$/);
	# return(1) if ($type !~ /^(12[40]|7|36)/);
	return(1) if /^\s*\^\s*$/;
	return(1) if /Line has invalid autocommand /;
	return(1) if /(Invalid input detected|Type help or )/;
	return(1) if /(No such device|Error Sending Request)/i;
	return(1) if /\%Error: No such file or directory/;
	return(1) if /No space information available/;
	return(-1) if /\%Error calling/;
	return(-1) if /(: device being squeezed|ATA_Status time out)/i; # busy
	return(-1) if (/command authorization failed/i);
	return(1) if /(Open device \S+ failed|Error opening \S+:)/;
	# the pager can not be disabled per-session on the PIX
	if (/^(<-+ More -+>)/) {
	    my($len) = length($1);
	    s/^$1\s{$len}//;
	}

	ProcessHistory("FLASH","","","!Flash: $dev: $_");
    }
    ProcessHistory("","","","!\n");
    return(0);
}

# This routine parses "show controllers"
sub ShowContAll {
    # Skip if this is a 70[01]0, 7500, or 12000.
    print STDERR "    In ShowContAll: $_" if ($debug);

    while (<INPUT>) {
	tr/\015//d;
	last if (/^$prompt/);
	next if (/^(\s*|\s*$cmd\s*)$/);
	# return(1) if ($type =~ /^(12[40]|7[05])/);
	return(-1) if (/command authorization failed/i);
	# the pager can not be disabled per-session on the PIX
	if (/^(<-+ More -+>)/) {
	    my($len) = length($1);
	    s/^$1\s{$len}//;
	}

	if (/^Interface ([^ \n(]*)/) { $INT = "$1, "; next; }
	/^(BRI unit \d)/ &&
	    ProcessHistory("INT","","","!Interface: $1\n") && next;
	/^LANCE unit \d, NIM/ &&
	    ProcessHistory("INT","","","!Interface: $_") && next;
	/^(LANCE unit \d)/ &&
	    ProcessHistory("INT","","","!Interface: $1\n") && next;
	/(Media Type is \S+),/ &&
	    ProcessHistory("INT","","","!\t$1\n");
	    if (/(M\dT[^ :]*:) show controller:$/) {
		my($ctlr) = $1;
		$_ = <INPUT>; tr/\015//d; s/ subunit \d,//;
		ProcessHistory("INT","","","!Interface: $ctlr $_");
	    }
	if (/^(\S+) : show controller:$/) {
	    my($ctlr) = $1;
	    $_ = <INPUT>; tr/\015//d; s/ subunit \d,//;
	    ProcessHistory("INT","","","!Interface: $ctlr: $_");
	}
	/^(HD unit \d), idb/ &&
	    ProcessHistory("INT","","","!Interface: $1\n") && next;
	/^HD unit \d, NIM/ &&
	    ProcessHistory("INT","","","!Interface: $_") && next;
	/^buffer size \d+  HD unit \d, (.*)/ &&
	    ProcessHistory("INT","","","!\t$1\n") && next;
	/^AM79970 / && ProcessHistory("INT","","","!Interface: $_") && next;
	/^buffer size \d+  (Universal Serial: .*)/ &&
	    ProcessHistory("INT","","","!\t$1\n") && next;
	/^Hardware is (.*)/ &&
	    ProcessHistory("INT","","","!Interface: $INT$1\n") && next;
	/^(QUICC Serial unit \d),/ &&
	    ProcessHistory("INT","","","!$1\n") && next;
	/^QUICC Ethernet .*/ &&
	    ProcessHistory("INT","","","!$_") && next;
	/^DTE .*\.$/ &&
	    ProcessHistory("INT","","","!\t$_") && next;
	/^(cable type :.*),/ &&
	    ProcessHistory("INT","","","!\t$1\n") && next;
	/^(.* cable.*), received clockrate \d+$/ &&
	    ProcessHistory("INT","","","!\t$1\n") && next;
	/^.* cable.*$/ &&
	    ProcessHistory("INT","","","!\t$_") && next;
    }
    return(0);
}

# This routine parses "show controllers cbus"
# Some of this is printed out in ShowDiagbus.
sub ShowContCbus {
    # Skip if this is not a 7000 or 7500.
    print STDERR "    In ShowContCbus: $_" if ($debug);

    while (<INPUT>) {
	tr/\015//d;
	last if (/^$prompt/);
	next if (/^(\s*|\s*$cmd\s*)$/);
	#return(1) if ($type !~ /^7[05]0/);
	return(-1) if (/command authorization failed/i);
	# the pager can not be disabled per-session on the PIX
	if (/^(<-+ More -+>)/) {
	    my($len) = length($1);
	    s/^$1\s{$len}//;
	}

	if (/^\s*slot(\d+): ([^,]+), hw (\S+), sw (\S+), ccb/) {
	    $slot = $1;
	    $board{$slot} = $2;
	    $hwver{$slot} = $3;
	    $hwucode{$slot} = $4;
	} elsif (/^\s*(\S+) (\d+), hardware version (\S+), microcode version (\S+)/) {
	    $slot = $2;
	    $board{$slot} = $1;
	    $hwver{$slot} = $3;
	    $hwucode{$slot} = $4;
	} elsif (/(Microcode .*)/) {
	    $ucode{$slot} = $1;
	} elsif (/(software loaded .*)/) {
	    $ucode{$slot} = $1;
	} elsif (/(\d+) Kbytes of main memory, (\d+) Kbytes cache memory/) {
	    $hwmemd{$slot} = $1;
	    $hwmemc{$slot} = $2;
	} elsif (/byte buffers/) {
	    chop;
	    s/^\s*//;
	    $hwbuf{$slot} = $_;
	} elsif (/Interface (\d+) - (\S+ \S+),/) {
	    $interface = $1;
	    ProcessHistory("HW","","",
		"!\n!Int $interface: in slot $slot, named $2\n"); next;
	} elsif (/(\d+) buffer RX queue threshold, (\d+) buffer TX queue limit, buffer size (\d+)/) {
	    ProcessHistory("HW","","","!Int $interface: rxq $1, txq $2, bufsize $3\n");
	    next;
	}
    }
    return(0);
}

# This routine parses "show debug"
sub ShowDebug {
    print STDERR "    In ShowDebug: $_" if ($debug);
    my($lines) = 0;

    while (<INPUT>) {
	tr/\015//d;
	last if (/^$prompt/);
	next if (/^(\s*|\s*$cmd\s*)$/);
	return(1) if /Line has invalid autocommand /;
	return(1) if /(Invalid input detected|Type help or )/;
	return(-1) if (/command authorization failed/i);
	# the pager can not be disabled per-session on the PIX
	if (/^(<-+ More -+>)/) {
	    my($len) = length($1);
	    s/^$1\s{$len}//;
	}

	/^No matching debug flags set$/ && next;
	ProcessHistory("COMMENTS","keysort","J1","!DEBUG: $_");
	$lines++;
    }
    if ($lines) {
	ProcessHistory("COMMENTS","keysort","J0","!\n");
    }
    return(0);
}

# This routine parses "show diagbus"
# This will create arrarys for hw info.
sub ShowDiagbus {
    # Skip if this is not a 7000, 70[01]0, or 7500.
    print STDERR "    In ShowDiagbus: $_" if ($debug);

    while (<INPUT>) {
	tr/\015//d;
	last if (/^$prompt/);
	next if (/^(\s*|\s*$cmd\s*)$/);
	#return(1) if ($type !~ /^7[05]/);
	return(1) if /Line has invalid autocommand /;
	return(1) if /(Invalid input detected|Type help or )/;
	return(-1) if (/command authorization failed/i);
	# the pager can not be disabled per-session on the PIX
	if (/^(<-+ More -+>)/) {
	    my($len) = length($1);
	    s/^$1\s{$len}//;
	}

	if (/^\s*Slot (\d+):/i) {
	    $slot = $1;
	    next;
	} elsif (/^\s*Slot (\d+) \(virtual\):/i) {
	    $slot = $1;
	    next;
	} elsif (/^\s*(.*Processor.*|.*controller|.*controler|.*Chassis Interface)(, FRU\s?:.*)?, HW rev (\S+), board revision (\S+)/i) {
	    $board = $1;
	    $hwver = $3;
	    $boardrev = $4;
	    if ($board =~ /Processor/) {
		if ($board =~ /7000 Route\/Switch/) {
		    $board = "RSP7000";
		} elsif ($board =~ /Route\/Switch Processor (\d)/) {
		    $board = "RSP$1";
		} elsif ($board =~ /Route/) {
		    $board = "RP";
		} elsif ($board =~ /Silicon Switch/) {
		    $board = "SSP";
		} elsif ($board =~ /Switch/) {
		    $board = "SP";
		    $board = "SSP $sspmem" if $ssp;
		} elsif ($board =~ /ATM/) {
		    $board = "AIP";
		}
	    } elsif ($board =~ /(.*) controller/i) {
		$board = $1;
	    }
	    # hwucode{$slot} defined in ShowContCbus
	    if (defined $hwucode{$slot}) {
		ProcessHistory("SLOT","","","!\n!Slot $slot/$board: hvers $hwver rev $boardrev ucode $hwucode{$slot}\n");
	    } else {
		ProcessHistory("SLOT","","","!\n!Slot $slot/$board: hvers $hwver rev $boardrev\n");
	    }
	    # These are also from the ShowContCbus
	    ProcessHistory("SLOT","","","!Slot $slot/$board: $ucode{$slot}\n") if (defined $ucode{$slot});
	    ProcessHistory("SLOT","","","!Slot $slot/$board: memd $hwmemd{$slot}, cache $hwmemc{$slot}\n")
	    if ((defined $hwmemd{$slot}) && (defined $hwmemc{$slot}));
	    ProcessHistory("SLOT","","","!Slot $slot/$board: $hwbuf{$slot}\n") if (defined $hwbuf{$slot});
	    next;
	}
	/Serial number: (\S+)\s*Part number: (\S+)/ &&
	    ProcessHistory("SLOT","","",
			"!Slot $slot/$board: part $2, serial $1\n") &&
	    next;
	/^\s*Controller Memory Size: (.*)$/ &&
	    ProcessHistory("SLOT","","","!Slot $slot/$board: $1\n") &&
	    next;
	if (/PA Bay (\d) Information/) {
	    $pano = $1;
	    if ("PA" =~ /$board/) {
		($s,$c) = split(/\//,$board);
		$board = "$s/$c/PA $pano";
	    } else {
		$board =~ s/\/PA \d//;
		$board = "$board/PA $pano";
	    }
	    next;
	}
	/\s+(.*) (IP|PA), (\d) ports?,( \S+,)? (FRU\s?: )?(\S+)/ &&
	    ProcessHistory("SLOT","","","!Slot $slot/$board: type $6, $3 ports\n") &&
	    next;
	/\s+(.*) (IP|PA)( \(\S+\))?, (\d) ports?/ &&
	    ProcessHistory("SLOT","","","!Slot $slot/$board: type $1$3, $4 ports\n") &&
	    next;
	/^\s*HW rev (\S+), Board revision (\S+)/ &&
	    ProcessHistory("SLOT","","","!Slot $slot/$board: hvers $1 rev $2\n") &&
	    next;
	/Serial number: (\S+)\s*Part number: (\S+)/ &&
	    ProcessHistory("SLOT","","","!Slot $slot/$board: part $2, serial $1\n") && next;
    }
    return(0);
}

# This routine parses "show diag" for the gsr, 7200, 3700, 3600, 2600.
# This will create arrarys for hw info.
sub ShowDiag {
    # Skip if this is not a 12000.
    print STDERR "    In ShowDiag: $_" if ($debug);

    while (<INPUT>) {
REDUX:	tr/\015//d;
	if (/^$prompt/) { $found_diag=1; last};
	next if (/^(\s*|\s*$cmd\s*)$/);
	# return(1) if ($type !~ /^(12[40]|720|36|26)/);
	return(1) if /Line has invalid autocommand /;
	return(1) if /(Invalid input detected|Type help or )/;
	return(-1) if (/command authorization failed/i);
	return(0) if ($found_diag);		# Only do this routine once
	/^$/ && next;
	# the pager can not be disabled per-session on the PIX
	if (/^(<-+ More -+>)/) {
	    my($len) = length($1);
	    s/^$1\s{$len}//;
	}

	s/Port Packet Over SONET/POS/;
	if (/^\s*SLOT\s+(\d+)\s+\((.*)\): (.*)/) {
	    $slot = $1;
	    ProcessHistory("SLOT","","","!\n");
	    ProcessHistory("SLOT","keysort","A","!Slot $slot: $3\n");
	    next;
	}
	if (/^\s*NODE\s+(\S+) : (.*)/) {
	    $slot = $1;
	    ProcessHistory("SLOT","","","!\n");
	    ProcessHistory("SLOT","keysort","A","!Slot $slot: $2\n");
	    next;
	}
	if (/^\s*PLIM\s+(\S+) : (.*)/) {
	    $slot = $1 . " PLIM";
	    ProcessHistory("SLOT","","","!\n");
	    ProcessHistory("SLOT","keysort","A","!Slot $slot: $2\n");
	    next;
	}
	if (/^\s*RACK\s+(\S+) : (.*)/) {
	    $slot = "Rack/" . $1;
	    ProcessHistory("SLOT","","","!\n");
	    ProcessHistory("SLOT","keysort","A","!Slot $slot: $2\n");
	    next;
	}
	if (/^\s+MAIN:\s* type \S+,\s+(.*)/) {
	    local($part) = $1;
	    $_ = <INPUT>;
	    if (/^\s+(HW version|Design Release) (\S+)\s+S\/N (\S+)/i) {
		ProcessHistory("SLOT","keysort","AM","!Slot $slot/MAIN: part $part, serial $3\n");
		ProcessHistory("SLOT","keysort","AM","!Slot $slot/MAIN: hvers $2\n");
	    } else {
		ProcessHistory("SLOT","keysort","AM","!Slot $slot/MAIN: part $part\n");
		goto REDUX;
	    }
	    next;
	}
	if (/^\s+MAIN:\s* board type \S+$/) {
	    $_ = <INPUT>;
	    tr/\015//d;
	    if (/^\s+(.+)$/) {
		local($part) = $1;
		$_ = <INPUT>;
		tr/\015//d;
		if (/^\s+dev (.*)$/) {
		    local($dev) = $1;
		    $_ = <INPUT>;
		    if (/^\s+S\/N (\S+)/) {
			ProcessHistory("SLOT","keysort","AM","!Slot $slot/MAIN: part $part, dev $dev, serial $1\n");
		    } else {
			ProcessHistory("SLOT","keysort","AM","!Slot $slot/MAIN: part $part, dev $dev\n");
			goto REDUX;
		   }
		} else {
		    ProcessHistory("SLOT","keysort","AM","!Slot $slot/MAIN: part $part\n");
		    goto REDUX;
		}
	    } else {
		goto REDUX;
	    }
	    next;
	}
	if (/^c3700\s+(io-board|mid-plane)/i) {
	    $slot=$1;
	    ProcessHistory("SLOT","","","!\n");
	    ProcessHistory("SLOT","keysort","A","!Slot $slot: part $1\n");
	    next;
	}
	if (/ Engine:\s+(.*)/) {
	    ProcessHistory("SLOT","keysort","AE","!Slot $slot/Engine: $1\n");
	}
	if (/FRU:\s+Linecard\/Module:\s+(\S+)/) {
	    ProcessHistory("SLOT","keysort","AF","!Slot $slot/FRU: Linecard/Module: $1\n");
	    next;
	}
	if (/\s+Processor Memory:\s+(\S+)/) {
	    ProcessHistory("SLOT","keysort","AF","!Slot $slot/FRU: Processor Memory: $1\n");
	    next;
	}
	if (/\s+Packet Memory:\s+(\S+)/) {
	    ProcessHistory("SLOT","keysort","AF","!Slot $slot/FRU: Packet Memory: $1\n");
	    next;
	}
	if (/^\s+PCA:\s+(.*)/) {
	    local($part) = $1;
	    $_ = <INPUT>;
	    if (/^\s+(HW version|design release) (\S+)\s+S\/N (\S+)/i) {
		ProcessHistory("SLOT","keysort","C1","!Slot $slot/PCA: part $part, serial $3\n");
		ProcessHistory("SLOT","keysort","C2","!Slot $slot/PCA: hvers $2\n");
	    } else {
		ProcessHistory("SLOT","keysort","C1","!Slot $slot/PCA: part $part\n");
		goto REDUX;
	    }
	    next;
	}
	if (/^\s+MBUS: .*\)\s+(.*)/) {
	    local($tmp) = "!Slot $slot/MBUS: part $1";
	    $_ = <INPUT>;
	    /^\s+HW version (\S+)\s+S\/N (\S+)/ &&
		ProcessHistory("SLOT","keysort","MB1","$tmp, serial $2\n") &&
		ProcessHistory("SLOT","keysort","MB2","!Slot $slot/MBUS: hvers $1\n");
	    next;
	}
	if (/^\s+MBUS Agent Software version (.*)/) {
	    ProcessHistory("SLOT","keysort","MB3","!Slot $slot/MBUS: software $1\n");
	    next;
	}
	if (/^\s+PLD: (.*)/) {
	    ProcessHistory("SLOT","keysort","P","!Slot $slot/PLD: $1\n");
	    next;
	}
	if (/^\s+MONLIB: (.*)/) {
	    ProcessHistory("SLOT","keysort","Q","!Slot $slot/MONLIB: $1\n");
	    next;
	}
	if (/^\s+ROM Monitor version (.*)/) {
	    ProcessHistory("SLOT","keysort","R","!Slot $slot/ROM Monitor: version $1\n");
	    next;
	}
	if (/^\s+ROMMON: Version (.*)/) {
	    ProcessHistory("SLOT","keysort","R","!Slot $slot/ROMMON: version $1\n");
	    next;
	}
	if (/^\s+Fabric Downloader version used (.*)/) {
	    ProcessHistory("SLOT","keysort","Z","!Slot $slot/Fabric Downloader: version $1\n");
	    next;
	}
	if (/^\s+DRAM size: (\d+)/) {
	    local($dram) = $1 / 1048576;
	    $_ = <INPUT>;
	    if (/^\s+FrFab SDRAM size: (\d+)/) {
		ProcessHistory("SLOT","keysort","MB4","!Slot $slot/MBUS: $dram Mbytes DRAM, "
			   . $1 / 1024 . " Kbytes SDRAM\n");
	    } else {
		ProcessHistory("SLOT","keysort","MB4","!Slot $slot/MBUS: $dram Mbytes DRAM\n");
		goto REDUX;
	    }
	    next;
	}
	# 7200, 3600, 2600, and 1700 stuff
	if (/^(Slot)\s+(\d+(\/\d+)?):/
	    || /^\s+(WIC|VIC|WIC\/VIC) Slot (\d):/
	    || /^(Encryption AIM) (\d):/) {
	    if ($1 eq "WIC") {
		$WIC = "/$2";
	    } elsif ($1 eq "VIC") {
		$WIC = "/$2";
	    } elsif ($1 eq "WIC/VIC") {
		$WIC = "/$2";
	    } elsif ($1 eq "DSP") {
		$WIC = "/$2";
	    } elsif ($1 eq "Encryption AIM") {
		$slot = "$2";
		undef($WIC);
		ProcessHistory("SLOT","","","!\n");
		ProcessHistory("SLOT","keysort","B","!Slot $slot: type $1\n");
		next;
	    } else {
		$slot = $2;
		undef($WIC);
	    }
	    $_ = <INPUT>; tr/\015//d;

	    # clean up hideous 7200/etc formats to look more like 7500 output
	    s/Fast-ethernet on C7200 I\/O card/FE-IO/;
	    s/ with MII or RJ45/-TX/;
	    s/Fast-ethernet /100Base/; s/[)(]//g;
	    s/intermediate reach/IR/i;

	    ProcessHistory("SLOT","","","!\n");
	    /\s+(.*) port adapter,?\s+(\d+)\s+/i &&
		ProcessHistory("SLOT","keysort","B","!Slot $slot: type $1, $2 ports\n") && next;
	    # I/O controller with no interfaces
	    /\s+(.*)\s+port adapter\s*$/i &&
		ProcessHistory("SLOT","keysort","B","!Slot $slot: type $1, 0 ports\n") && next;
	    /\s+(.*)\s+daughter card(.*)$/ &&
		ProcessHistory("SLOT","keysort","B","!Slot $slot$WIC: type $1$2\n") && next;
	    /\s+(FT1)$/ &&
		ProcessHistory("SLOT","keysort","B","!Slot $slot$WIC: type $1\n") && next;
	    # handle WICs lacking "daughter card" in the 2nd line of their
	    # show diag o/p
	    if (defined($WIC)) {
		s/^\s+//;
		ProcessHistory("SLOT","keysort","B","!Slot $slot$WIC: type $_");
	    }
	    next;
	} elsif (/^\s+(.* (DSP) Module) Slot (\d):/) {
	    # The 1760 (at least) has yet another format...where it has two
	    # dedicated DSP slots, and thus two slot 0s.
	    my($TYPE) = $1;
	    $WIC = "/$3";
	    ProcessHistory("SLOT","","","!\n");
	    ProcessHistory("SLOT","keysort","B",
					"!Slot $slot$WIC: type $TYPE\n");
	    next;
	}
	# yet another format.  seen on 2600s w/ 12.1, but appears to be all
	# 12.1, including 7200s & 3700s.  Sometimes the PCB serial appears
	# before the hardware revision.
	if (/(pcb serial number|hardware revision)\s+:\s+(\S+)$/i) {
	    my($hw, $pn, $rev, $sn);
	    if ($1 =~ /^pcb/i) {
		$sn = $2;
	    } else {
		$hw = $2;
	    }
	    while (<INPUT>) {
		tr/\015//d;

		if (/0x..: / || /^$/) {
		    # no effing idea why break does not work there
		    goto PerlSucks;
		}
		if (/hardware revision\s+:\s+(\S+)/i) { $hw = $1; }
		if (/part number\s+:\s+(\S+)/i) { $pn = $1; }
		if (/board revision\s+:\s+(\S+)/i) { $rev = $1; }
		if (/pcb serial number\s+:\s+(\S+)/i) { $sn = $1; }
	    }
PerlSucks:
	    ProcessHistory("SLOT","keysort","B","!Slot $slot$WIC: hvers $hw rev $rev\n");
	    ProcessHistory("SLOT","keysort","C","!Slot $slot$WIC: part $pn, serial $sn\n");
	}
	/revision\s+(\S+).*revision\s+(\S+)/ &&
	    ProcessHistory("SLOT","keysort","C","!Slot $slot$WIC: hvers $1 rev $2\n") &&
	    next;
	/number\s+(\S+)\s+Part number\s+(\S+)/ &&
	    ProcessHistory("SLOT","keysort","D","!Slot $slot$WIC: part $2, serial $1\n") &&
	    next;
    }
    ProcessHistory("SLOT","","","!\n");
    return(0);
}

# This routine parses "show inventory".
sub ShowInventory {
    print STDERR "    In ShowInventory: $_" if ($debug);

    while (<INPUT>) {
	tr/\015//d;
	return if (/^\s*\^$/);
	last if (/^$prompt/);
	next if (/^(\s*|\s*$cmd\s*)$/);
	return(1) if /Line has invalid autocommand /;
	return(1) if /(Invalid input detected|Type help or )/;
	return(-1) if (/command authorization failed/i);
	# the pager can not be disabled per-session on the PIX
	if (/^(<-+ More -+>)/) {
	    my($len) = length($1);
	    s/^$1\s{$len}//;
	}

	if (/^(NAME: "[^"]*",) (DESCR: "[^"]+")/) {
	    ProcessHistory("INVENTORY","","", sprintf("!%-30s %s\n", $1, $2));
	    next;
	}
	# split PID/VID/SN line
	if (/^PID: (\S*)\s*, VID: (\S*)\s*, SN: (\S*)\s*$/) {
	    my($entries) = "";
	    $entries .= "!PID: $1\n" if ($1);
	    $entries .= "!VID: $2\n" if ($2);
	    $entries .= "!SN: $3\n" if ($3);
	    ProcessHistory("INVENTORY","","", "$entries");
	    next;
	}
	ProcessHistory("INVENTORY","","","!$_");
    }
    ProcessHistory("INVENTORY","","","!\n");

    return(0);
}

# This routine parses "show module".
#sub ShowModule {
#    print STDERR "    In ShowModule: $_" if ($debug);
#
#    my(@lines);
#    my($slot, $pa);
#
#    while (<INPUT>) {
#	tr/\015//d;
#	return if (/^\s*\^$/);
#	last if (/online diag status/i);
#	last if (/^$prompt/);
#	next if (/^(\s*|\s*$cmd\s*)$/);
#	return(-1) if (/command authorization failed/i);
#	# the pager can not be disabled per-session on the PIX
#	if (/^(<-+ More -+>)/) {
#	    my($len) = length($1);
#	    s/^$1\s{$len}//;
#	}
#
#	# match slot/card info line
#	if (/^ *(\d+)\s+(\d+)\s+(.*)\s+(\S+)\s+(\S+)\s*$/) {
#	    $lines[$1 * 1000] .= "!Slot $1: type $3, $2 ports\n!Slot $1: part $4, serial $5\n";
#	    $lines[$1 * 1000] =~ s/\s+,/,/g;
#	    next;
#	}
#	# now match the Revs in the second paragraph of o/p and stick it in
#	# the array with the previous bits...grumble.
#	if (/^ *(\d+)\s+\S+\s+to\s+\S+\s+(\S+)\s+(\S*)\s+(\S+)(\s+\S+)?\s*$/) {
#	    $lines[$1 * 1000] .= "!Slot $1: hvers $2, firmware $3, sw $4\n";
#	    $lines[$1 * 1000] =~ s/\s+,/,/g;
#	    next;
#	}
#	# grab the sub-modules, if any
#	if (/^\s+(\d+)\s(.*)\s+(\S+)\s+(\S+)\s+(\S+)\s+\S+\s*$/) {
#	    my($idx);
#	    $pa = 0 if ($1 != $slot);
#	    $slot = $1;
#	    $idx = $1 * 1000 + $1 * 10 + $pa;
#	    $lines[$idx] .= "!Slot $1/$pa: type $2\n";
#	    $lines[$idx] .= "!Slot $slot/$pa: part $3, serial $4\n";
#	    $lines[$idx] .= "!Slot $slot/$pa: hvers $5\n";
#	    $pa++;
#	}
#    }
#    foreach $slot (@lines) {
#	next if ($slot =~ /^\s*$/);
#	ProcessHistory("Module","","","$slot!\n");
#    }
#
#    return(0);
#}

# This routine parses "show spe version".
sub ShowSpeVersion {
    print STDERR "    In ShowSpeVersion: $_" if ($debug);

    while (<INPUT>) {
	tr/\015//d;
	last if (/^$prompt/);
	next if (/^(\s*|\s*$cmd\s*)$/);
	return(1) if /^\s*\^\s*$/;
	return(1) if /Line has invalid autocommand /;
	return(1) if /(Invalid input detected|Type help or )/;
	return(-1) if (/command authorization failed/i);

	ProcessHistory("MODEM","","","!Modem: $_") && next;
    }
    ProcessHistory("MODEM","","","!\n");
    return(0);
}

# This routine parses "show c7200" for the 7200
# This will create arrays for hw info.
sub ShowC7200 {
    # Skip if this is not a 7200.
    print STDERR "    In ShowC7200: $_" if ($debug);

    while (<INPUT>) {
	tr/\015//d;
	last if (/^$prompt/);
	next if (/^(\s*|\s*$cmd\s*)$/);
	#return(1) if ($type !~ /^72/);
	return(-1) if (/command authorization failed/i);
	/^$/ && next;
	# the pager can not be disabled per-session on the PIX
	if (/^(<-+ More -+>)/) {
	    my($len) = length($1);
	    s/^$1\s{$len}//;
	}

	if (/^(C7200 )?Midplane EEPROM:/) {
	    $_ = <INPUT>;
	    /revision\s+(\S+).*revision\s+(\S+)/;
	    ProcessHistory("SLOT","","","!Slot Midplane: hvers $1 rev $2\n");
	    $_ = <INPUT>;
	    /number\s+(\S+)\s+Part number\s+(\S+)/;
	    ProcessHistory("SLOT","","","!Slot Midplane: part $2, serial $1\n!\n");
	    next;
	}
	if (/C720\d(VXR)? CPU EEPROM:/) {
	    my ($hvers,$rev,$part,$serial);
	    # npe400s report their cpu eeprom info differently w/ 12.0.21S
	    while (<INPUT>) {
		/Hardware Revision\s+: (\S+)/ && ($hvers = $1) && next;
		/Board Revision\s+: (\S+)/ && ($rev = $1) && next;
		/Part Number\s+: (\S+)/ && ($part = $1) && next;
		/Serial Number\s+: (\S+)/ && ($serial = $1) && next;
		/revision\s+(\S+).*revision\s+(\S+)/ &&
		    ($hvers = $1, $rev = $2) && next;
		/number\s+(\S+)\s+Part number\s+(\S+)/ &&
		    ($serial = $1, $part = $2) && next;
		/^\s*$/ && last;
	    }
	    ProcessHistory("SLOT","","","!Slot CPU: hvers $hvers rev $rev\n");
	    ProcessHistory("SLOT","","","!Slot CPU: part $part, serial $serial\n!\n");
	    next;
	}
    }
    return(0);
}

# This routine parses "show vtp status"
sub ShowVTP {
    print STDERR "    In ShowVTP: $_" if ($debug);

    while (<INPUT>) {
	tr/\015//d;
	last if (/^$prompt/);
	next if (/^(\s*|\s*$cmd\s*)$/);
	return(1) if /^\s*\^\s*$/;
	return(1) if /Line has invalid autocommand /;
	return(1) if /(Invalid input detected|Type help or )/;
	#return(1) if ($type !~ /^(2900XL|3500XL|6000)$/);
	return(-1) if (/command authorization failed/i);
	next if (/^Configuration last modified by/);
	# the pager can not be disabled per-session on the PIX
	if (/^(<-+ More -+>)/) {
	    my($len) = length($1);
	    s/^$1\s{$len}//;
	}

	if (/^VTP Operating Mode\s+:\s+(Transparent|Server)/) {
	    $DO_SHOW_VLAN = 1;
	}
	ProcessHistory("COMMENTS","keysort","I0","!VTP: $_");
    }
    ProcessHistory("COMMENTS","keysort","I0","!\n");
    return(0);
}

# This routine parses "show vlan"
sub ShowVLAN {
    print STDERR "    In ShowVLAN: $_" if ($debug);

    ($_=<INPUT>,return(1)) if (!$DO_SHOW_VLAN);

    while (<INPUT>) {
	tr/\015//d;
	last if (/^$prompt/);
	next if (/^(\s*|\s*$cmd\s*)$/);
	return(1) if /^\s*\^\s*$/;
	return(1) if /Line has invalid autocommand /;
	return(1) if /(Invalid input detected|Type help or )/;
	return(1) if /Ambiguous command/i;
	# newer releases (~12.1(9)) place the vlan config in the normal
	# configuration (write term).
	return(1) if ($type =~ /^(3550|4500|7600)$/);
	#return(1) if ($type !~ /^(2900XL|3500XL|6000)$/);
	return(-1) if (/command authorization failed/i);
	# the pager can not be disabled per-session on the PIX
	if (/^(<-+ More -+>)/) {
	    my($len) = length($1);
	    s/^$1\s{$len}//;
	}

	ProcessHistory("COMMENTS","keysort","IO","!VLAN: $_");
    }
    ProcessHistory("COMMENTS","keysort","IO","!\n");
    return(0);
}

# This routine processes a "write term"
sub WriteTerm {
    print STDERR "    In WriteTerm: $_" if ($debug);
    my($lineauto,$comment,$linecnt) = (0,0,0);

    while (<INPUT>) {
	tr/\015//d;
	last if (/^$prompt/);
	return(1) if /Line has invalid autocommand /;
	return(1) if (/(Invalid input detected|Type help or )/i);
	return(-1) if (/command authorization failed/i);
	# the pager can not be disabled per-session on the PIX
	if (/^(<-+ More -+>)/) {
	    my($len) = length($1);
	    s/^$1\s{$len}//;
	}

	/Non-Volatile memory is in use/  && return(-1); # NvRAM is locked
	return(0) if ($found_end);		# Only do this routine once
	$linecnt++;
	$lineauto = 0 if (/^[^ ]/);
	# skip the crap
	if (/^(##+$|(Building|Current) configuration)/i) {
	    while (<INPUT>) {
		next if (/^Current configuration\s*:/i);
		next if (/^:/);
		next if (/^([%!].*|\s*)$/);
		next if (/^ip add.*ipv4:/);	# band-aid for 3620 12.0S
		last;
	    }
	    if (defined($config_register)) {
		ProcessHistory("","","","!\nconfig-register $config_register\n");
	    }
	    tr/\015//d;
	}
	# some versions have other crap mixed in with the bits in the
	# block above
	/^! (Last configuration|NVRAM config last)/ && next;

	# skip consecutive comment lines to avoid oscillating extra comment
	# line on some access servers.  grrr.
	if (/^!/) {
	    next if ($comment);
	    ProcessHistory("","","",$_);
	    $comment++;
	    next;
	}
	$comment = 0;

	# Dog gone Cool matches to process the rest of the config
	/^tftp-server flash /   && next; # kill any tftp remains
	/^ntp clock-period /    && next; # kill ntp clock-period
	/^ length /		&& next; # kill length on serial lines
	/^ width /		&& next; # kill width on serial lines
	$lineauto = 1 if /^ modem auto/;
	/^ speed / && $lineauto	&& next; # kill speed on serial lines
	/^ clockrate /		&& next; # kill clockrate on serial interfaces
	if (/^(enable )?(password|passwd)( level \d+)? / && $filter_pwds >= 1) {
	    ProcessHistory("ENABLE","","","!$1$2$3 <removed>\n");
	    next;
	}
	if (/^(enable secret) / && $filter_pwds >= 2) {
	    ProcessHistory("ENABLE","","","!$1 <removed>\n");
	    next;
	}
	if (/^username (\S+)(\s.*)? secret /) {
	    if ($filter_pwds >= 2) {
		ProcessHistory("USER","keysort","$1","!username $1$2 secret <removed>\n");
	    } else {
		ProcessHistory("USER","keysort","$1","$_");
	    }
	    next;
	}
	if (/^username (\S+)(\s.*)? password ((\d) \S+|\S+)/) {
	    if ($filter_pwds >= 2) {
		ProcessHistory("USER","keysort","$1","!username $1$2 password <removed>\n");
	    } elsif ($filter_pwds >= 1 && $4 ne "5"){
		ProcessHistory("USER","keysort","$1","!username $1$2 password <removed>\n");
	    } else {
		ProcessHistory("USER","keysort","$1","$_");
	    }
	    next;
	}
	# cisco AP w/ IOS
	if (/^(wlccp \S+ username (\S+)(\s.*)? password) (\d \S+|\S+)/) {
	    if ($filter_pwds >= 1) {
		ProcessHistory("USER","keysort","$2","!$1 <removed>\n");
	    } else {
		ProcessHistory("USER","keysort","$2","$_");
	    }
	    next;
	}
	if (/^( set session-key (in|out)bound ah \d+ )/ && $filter_pwds >= 1) {
	    ProcessHistory("","","","!$1<removed>\n");
	    next;
	}
	if (/^( set session-key (in|out)bound esp \d+ (authenticator|cypher) )/ && $filter_pwds >= 1) {
	    ProcessHistory("","","","!$1<removed>\n");
	    next;
	}
	if (/^(\s*)password / && $filter_pwds >= 1) {
	    ProcessHistory("LINE-PASS","","","!$1password <removed>\n");
	    next;
	}
	if (/^(\s*)secret / && $filter_pwds >= 2) {
	    ProcessHistory("LINE-PASS","","","!$1secret <removed>\n");
	    next;
	}
	if (/^\s*neighbor (\S*) password / && $filter_pwds >= 1) {
	    ProcessHistory("","","","! neighbor $1 password <removed>\n");
	    next;
	}
	if (/^(ppp .* password) 7 .*/ && $filter_pwds >= 1) {
	    ProcessHistory("","","","!$1 <removed>\n"); next;
	}
	if (/^(ip ftp password) / && $filter_pwds >= 1) {
	    ProcessHistory("","","","!$1 <removed>\n"); next;
	}
	if (/^( ip ospf authentication-key) / && $filter_pwds >= 1) {
	    ProcessHistory("","","","!$1 <removed>\n"); next;
	}
	# isis passwords appear to be completely plain-text
	if (/^\s+isis password (\S+)( .*)?/ && $filter_pwds >= 1) {
	    ProcessHistory("","","","!isis password <removed>$2\n"); next;
	}
	if (/^\s+(domain-password|area-password) (\S+)( .*)?/
							&& $filter_pwds >= 1) {
	    ProcessHistory("","","","!$1 <removed>$3\n"); next;
	}
	# this is reversable, despite 'md5' in the cmd
	if (/^( ip ospf message-digest-key \d+ md5) / && $filter_pwds >= 1) {
	    ProcessHistory("","","","!$1 <removed>\n"); next;
	}
	# this is also reversable, despite 'md5 encrypted' in the cmd
	if (/^(  message-digest-key \d+ md5 (7|encrypted)) / && $filter_pwds >= 1) {
	    ProcessHistory("","","","!$1 <removed>\n"); next;
	}
	if (/^((crypto )?isakmp key) \S+ / && $filter_pwds >= 1) {
	    ProcessHistory("","","","!$1 <removed> $'"); next;
	}
	# filter HSRP passwords
	if (/^(\s+standby \d+ authentication) / && $filter_pwds >= 1) {
	    ProcessHistory("","","","!$1 <removed>\n"); next;
	}
	# this appears in "measurement/sla" images
	if (/^(\s+key-string \d?)/ && $filter_pwds >= 1) {
	    ProcessHistory("","","","!$1 <removed>\n"); next;
	}
	if (/^( l2tp tunnel \S+ password)/ && $filter_pwds >= 1) {
	    ProcessHistory("","","","!$1 <removed>\n"); next;
	}
	# i am told these are plain-text on the PIX
	if (/^(vpdn username (\S+) password)/) {
	    if ($filter_pwds >= 1) {
		ProcessHistory("USER","keysort","$2","!$1 <removed>\n");
	    } else {
		ProcessHistory("USER","keysort","$2","$_");
	    }
	    next;
	}
	if (/^( cable shared-secret )/ && $filter_pwds >= 1) {
	    ProcessHistory("","","","!$1 <removed>\n");
	    next;
	}
	/fair-queue individual-limit/ && next;
	# sort ip explicit-paths.
	if (/^ip explicit-path name (\S+)/) {
	    my($key) = $1;
	    my($expath) = $_;
	    while (<INPUT>) {
		tr/\015//d;
		last if (/^$prompt/);
		last if (/^$prompt/ || ! /^(ip explicit-path name |[ !])/);
		if (/^ip explicit-path name (\S+)/) {
		    ProcessHistory("EXPATH","keysort","$key","$expath");
		    $key = $1;
		    $expath = $_;
		} else  {
		    $expath .= $_;
		}
	    }
	    ProcessHistory("EXPATH","keysort","$key","$expath");
	}
	# sort route-maps
	if (/^route-map (\S+)/) {
	    my($key) = $1;
	    my($routemap) = $_;
	    while (<INPUT>) {
		tr/\015//d;
		last if (/^$prompt/ || ! /^(route-map |[ !])/);
		if (/^route-map (\S+)/) {
		    ProcessHistory("ROUTEMAP","keysort","$key","$routemap");
		    $key = $1;
		    $routemap = $_;
		} else  {
		    $routemap .= $_;
		}
	    }
	    ProcessHistory("ROUTEMAP","keysort","$key","$routemap");
	}
	# filter out any RCS/CVS tags to avoid confusing local CVS storage
	s/\$(Revision|Id):/ $1:/;
	# order access-lists
	/^access-list\s+(\d\d?)\s+(\S+)\s+(\S+)/ &&
	    ProcessHistory("ACL $1 $2","ipsort","$3","$_") && next;
	# order extended access-lists
	/^access-list\s+(\d\d\d)\s+(\S+)\s+ip\s+host\s+(\S+)/ &&
	    ProcessHistory("EACL $1 $2","ipsort","$3","$_") && next;
	/^access-list\s+(\d\d\d)\s+(\S+)\s+ip\s+(\d\S+)/ &&
	    ProcessHistory("EACL $1 $2","ipsort","$3","$_") && next;
	/^access-list\s+(\d\d\d)\s+(\S+)\s+ip\s+any/ &&
	    ProcessHistory("EACL $1 $2","ipsort","0.0.0.0","$_") && next;
	# order arp lists
	/^arp\s+(\d+\.\d+\.\d+\.\d+)\s+/ &&
	    ProcessHistory("ARP","ipsort","$1","$_") && next;
	/^ip prefix-list\s+(\S+)\s+seq\s+(\d+)\s+(permit|deny)\s+(\d\S+)(\/.*)$/ &&
	    ProcessHistory("PACL $1 $3","ipsort","$4","ip prefix-list $1 $3 $4$5\n")
	    && next;
	# order logging statements
	/^logging (\d+\.\d+\.\d+\.\d+)/ &&
	    ProcessHistory("LOGGING","ipsort","$1","$_") && next;
	# order/prune snmp-server host statements
	# we only prune lines of the form
	# snmp-server host a.b.c.d <community>
	if (/^snmp-server host (\d+\.\d+\.\d+\.\d+) /) {
	    if (defined($ENV{'NOCOMMSTR'})) {
		my($ip) = $1;
		my($line) = "snmp-server host $ip";
		my(@tokens) = split(' ', $');
		my($token);
		while ($token = shift(@tokens)) {
		    if ($token eq 'version') {
			$line .= " " . join(' ', ($token, shift(@tokens)));
			if ($token eq '3') {
			    $line .= " " . join(' ', ($token, shift(@tokens)));
			}
		    } elsif ($token eq 'vrf') {
			$line .= " " . join(' ', ($token, shift(@tokens)));
		    } elsif ($token =~ /^(informs?|traps?|(no)?auth)$/) {
			$line .= " " . $token;
		    } else {
			$line = "!$line " . join(' ', ("<removed>", join(' ', at tokens)));
			last;
		    }
		}
		ProcessHistory("SNMPSERVERHOST","ipsort","$ip","$line\n");
	    } else {
		ProcessHistory("SNMPSERVERHOST","ipsort","$1","$_");
	    }
	    next;
	}
	if (/^(snmp-server community) (\S+)/) {
	    if (defined($ENV{'NOCOMMSTR'})) {
		ProcessHistory("SNMPSERVERCOMM","keysort","$_","!$1 <removed>$'") && next;
	    } else {
		ProcessHistory("SNMPSERVERCOMM","keysort","$_","$_") && next;
	    }
	}
	# prune tacacs/radius server keys
	if (/^((tacacs-server|radius-server)\s(\w*[-\s(\s\S+])*\s?key) \d \w+/
							&& $filter_pwds >= 1) {
	    ProcessHistory("","","","!$1 <removed>$'"); next;
	}
	# order clns host statements
	/^clns host \S+ (\S+)/ &&
	    ProcessHistory("CLNS","keysort","$1","$_") && next;
	# order alias statements
	/^alias / && ProcessHistory("ALIAS","keysort","$_","$_") && next;
	# delete ntp auth password - this md5 is a reversable too
	if (/^(ntp authentication-key \d+ md5) / && $filter_pwds >= 1) {
	    ProcessHistory("","","","!$1 <removed>\n"); next;
	}
	# order ntp peers/servers
	if (/^ntp (server|peer) (\d+)\.(\d+)\.(\d+)\.(\d+)/) {
	    $sortkey = sprintf("$1 %03d%03d%03d%03d",$2,$3,$4,$5);
	    ProcessHistory("NTP","keysort",$sortkey,"$_");
	    next;
	}
	# order ip host statements
	/^ip host (\S+) / &&
	    ProcessHistory("IPHOST","keysort","$1","$_") && next;
	# order ip nat source static statements
	/^ip nat (\S+) source static (\S+)/ &&
	    ProcessHistory("IP NAT $1","ipsort","$2","$_") && next;
	# order atm map-list statements
	/^\s+ip\s+(\d+\.\d+\.\d+\.\d+)\s+atm-vc/ &&
	    ProcessHistory("ATM map-list","ipsort","$1","$_") && next;
	# order ip rcmd lines
	/^ip rcmd/ && ProcessHistory("RCMD","keysort","$_","$_") && next;

	# system controller
	/^syscon address (\S*) (\S*)/ &&
	    ProcessHistory("","","","!syscon address $1 <removed>\n") &&
	    next;
	if (/^syscon password (\S*)/ && $filter_pwds >= 1) {
	    ProcessHistory("","","","!syscon password <removed>\n");
	    next;
	}

	# catch anything that wasnt matched above.
	ProcessHistory("","","","$_");
	# end of config.  the ": " game is for the PIX
	if (/^(: +)?end$/) {
	    $found_end = 1;
	    return(1);
	}
    }
    # The ContentEngine lacks a definitive "end of config" marker.  If we
    # know that it is a CE and we have seen at least 5 lines of write term
    # o/p, we can be reasonably sure that we got the config.
    if ($type =~ /^CE$/ && $linecnt > 5) {
	$found_end = 1;
	return(1);
    }

    return(0);
}

# dummy function
sub DoNothing {print STDOUT;}

# Main
@commandtable = (
#	{'show hardware info'			=> 'ShowHw'},
	{'show configuration status'		=> 'ShowCfgstat'},
	{'show chassis'				=> 'ShowChassis'},
	{'show module'				=> 'ShowModule'},
	{'show microcode'			=> 'ShowMicrocode'},
	{'show configuration snapshot all'	=> 'ShowConfig'},
#	{'admin show version'		=> 'ShowVersion'},
#	{'show version'			=> 'ShowVersion'},
#	{'show redundancy secondary'	=> 'ShowRedundancy'},
#	{'show idprom backplane',	=> 'ShowIDprom'},
#	{'show install active'		=> 'ShowInstallActive'},
#	{'admin show env all'		=> 'ShowEnv'},
#	{'show env all'			=> 'ShowEnv'},
#	{'show rsp chassis-info',	=> 'ShowRSP'},
#	{'show gsr chassis'		=> 'ShowGSR'},
#	{'show diag chassis-info'	=> 'ShowGSR'},
#	{'show boot'			=> 'ShowBoot'},
#	{'show bootvar'			=> 'ShowBoot'},
#	{'admin show variables boot'	=> 'ShowBoot'},
#	{'show variables boot'		=> 'ShowBoot'},
#	{'show flash'			=> 'ShowFlash'},
#	{'dir /all nvram:'		=> 'DirSlotN'},
#	{'dir /all bootflash:'		=> 'DirSlotN'},
#	{'dir /all slot0:'		=> 'DirSlotN'},
#	{'dir /all disk0:'		=> 'DirSlotN'},
#	{'dir /all slot1:'		=> 'DirSlotN'},
#	{'dir /all disk1:'		=> 'DirSlotN'},
#	{'dir /all slot2:'		=> 'DirSlotN'},
#	{'dir /all disk2:'		=> 'DirSlotN'},
#	{'dir /all harddisk:'		=> 'DirSlotN'},
#	{'dir /all harddiska:'		=> 'DirSlotN'},
#	{'dir /all harddiskb:'		=> 'DirSlotN'},
#	{'dir /all sup-bootflash:'	=> 'DirSlotN'},		# cat 6500-ios
#	{'dir /all sup-microcode:'	=> 'DirSlotN'},		# cat 6500-ios
#	{'dir /all slavenvram:'		=> 'DirSlotN'},
#	{'dir /all slavebootflash:'	=> 'DirSlotN'},
#	{'dir /all slaveslot0:'		=> 'DirSlotN'},
#	{'dir /all slavedisk0:'		=> 'DirSlotN'},
#	{'dir /all slaveslot1:'		=> 'DirSlotN'},
#	{'dir /all slavedisk1:'		=> 'DirSlotN'},
#	{'dir /all slaveslot2:'		=> 'DirSlotN'},
#	{'dir /all slavedisk2:'		=> 'DirSlotN'},
#	{'dir /all slavesup-bootflash:'	=> 'DirSlotN'},		# cat 7609
#	{'dir /all sec-nvram:'		=> 'DirSlotN'},
#	{'dir /all sec-bootflash:'	=> 'DirSlotN'},
#	{'dir /all sec-slot0:'		=> 'DirSlotN'},
#	{'dir /all sec-disk0:'		=> 'DirSlotN'},
#	{'dir /all sec-slot1:'		=> 'DirSlotN'},
#	{'dir /all sec-disk1:'		=> 'DirSlotN'},
#	{'dir /all sec-slot2:'		=> 'DirSlotN'},
#	{'dir /all sec-disk2:'		=> 'DirSlotN'},
#	{'show controllers'		=> 'ShowContAll'},
#	{'show controllers cbus'	=> 'ShowContCbus'},
#	{'show diagbus'			=> 'ShowDiagbus'},
#	{'admin show diag'		=> 'ShowDiag'},
#	{'show diag'			=> 'ShowDiag'},
#	{'show module'			=> 'ShowModule'},	# cat 6500-ios
#	{'show spe version'		=> 'ShowSpeVersion'},
#	{'show c7200'			=> 'ShowC7200'},
#	{'show inventory raw'		=> 'ShowInventory'},
#	{'show vtp status'		=> 'ShowVTP'},
#	{'show vlan'			=> 'ShowVLAN'},
#	{'show vlan-switch'		=> 'ShowVLAN'},
#	{'show debug'			=> 'ShowDebug'},
#	{'show running-config'		=> 'WriteTerm'},
#	{'write term'			=> 'WriteTerm'},
);
# Use an array to preserve the order of the commands and a hash for mapping
# commands to the subroutine and track commands that have been completed.
@commands = map(keys(%$_), @commandtable);
%commands = map(%$_, @commandtable);

$cisco_cmds=join(";", at commands);
$cmds_regexp=join("|", at commands);

if (length($host) == 0) {
    if ($file) {
	print(STDERR "Too few arguments: file name required\n");
	exit(1);
    } else {
	print(STDERR "Too few arguments: host name required\n");
	exit(1);
    }
}
open(OUTPUT,">$host.new") || die "Can't open $host.new for writing: $!\n";
select(OUTPUT);
# make OUTPUT unbuffered if debugging
if ($debug) { $| = 1; }

if ($file) {
    print STDERR "opening file $host\n" if ($debug);
    print STDOUT "opening file $host\n" if ($log);
    open(INPUT,"<$host") || die "open failed for $host: $!\n";
} else {
    print STDERR "executing ologin -t $timeo -c\"$cisco_cmds\" $host\n" if ($debug);
    print STDOUT "executing ologin -t $timeo -c\"$cisco_cmds\" $host\n" if ($log);
    if (defined($ENV{NOPIPE})) {
	system "ologin -t $timeo -c \"$cisco_cmds\" $host </dev/null > $host.raw 2>&1" || die "ologin failed for $host: $!\n";
	open(INPUT, "< $host.raw") || die "ologin failed for $host: $!\n";
    } else {
	open(INPUT,"ologin -t $timeo -c \"$cisco_cmds\" $host </dev/null |") || die "ologin failed for $host: $!\n";
    }
}

# determine password filtering mode
if ($ENV{"FILTER_PWDS"} =~ /no/i) {
    $filter_pwds = 0;
} elsif ($ENV{"FILTER_PWDS"} =~ /all/i) {
    $filter_pwds = 2;
} else {
    $filter_pwds = 1;
}

ProcessHistory("","","","!RANCID-CONTENT-TYPE: alcatel\n!\n");
ProcessHistory("COMMENTS","keysort","B0","!\n");
ProcessHistory("COMMENTS","keysort","D0","!\n");
ProcessHistory("COMMENTS","keysort","F0","!\n");
ProcessHistory("COMMENTS","keysort","G0","!\n");
TOP: while(<INPUT>) {
    tr/\015//d;
    if (/^Error:/) {
	print STDOUT ("$host ologin error: $_");
	print STDERR ("$host ologin error: $_") if ($debug);
	$clean_run=0;
	last;
    }
    while (/->\s*($cmds_regexp)\s*$/) {
	$cmd = $1;
	if (!defined($prompt)) {
#	    $prompt = ($_ =~ /^([^#]+#)/)[0];
	    $prompt = ($_ =~ /^([^>]+->)/)[0];
	    $prompt =~ s/([][}{)(\\])/\\$1/g;
	    print STDERR ("PROMPT MATCH: $prompt\n") if ($debug);
	}
	print STDERR ("HIT COMMAND:$_") if ($debug);
	if (! defined($commands{$cmd})) {
	    print STDERR "$host: found unexpected command - \"$cmd\"\n";
	    $clean_run = 0;
	    last TOP;
	}
	$rval = &{$commands{$cmd}};
	delete($commands{$cmd});
	if ($rval == -1) {
	    $clean_run = 0;
	    last TOP;
	}
    }
    if (/([>#]\s?exit|->exit|Connection\ closed\ by\ CliShell)$/) {
	$clean_run=1;
	last;
    }
}
print STDOUT "Done $logincmd: $_\n" if ($log);
# Flush History
ProcessHistory("","","","");
# Cleanup
close(INPUT);
close(OUTPUT);

if (defined($ENV{NOPIPE})) {
    unlink("$host.raw") if (! $debug);
}

# check for completeness
if (scalar(%commands) || !$clean_run || !$found_end) {
    if (scalar(%commands)) {
	printf(STDOUT "$host: missed cmd(s): %s\n", join(',', keys(%commands)));
	printf(STDERR "$host: missed cmd(s): %s\n", join(',', keys(%commands))) if ($debug);
    }
print STDERR "clean $clean_run end $found_end\n";
    if (!$clean_run || !$found_end) {
	print STDOUT "$host: End of run not found\n";
	print STDERR "$host: End of run not found\n" if ($debug);
	system("/usr/bin/tail -1 $host.new");
    }
    unlink "$host.new" if (! $debug);
}


More information about the Rancid-discuss mailing list