Skip to content

Instantly share code, notes, and snippets.

@davidkrider
Created November 7, 2012 16:12
Show Gist options
  • Save davidkrider/4032499 to your computer and use it in GitHub Desktop.
Save davidkrider/4032499 to your computer and use it in GitHub Desktop.

Revisions

  1. David Krider revised this gist Nov 7, 2012. 1 changed file with 1 addition and 43 deletions.
    44 changes: 1 addition & 43 deletions vrancid
    Original file line number Diff line number Diff line change
    @@ -1,48 +1,6 @@
    #! /usr/bin/perl
    ##
    ## $Id: rancid.in 2272 2011-01-10 20:01:58Z heas $
    ##
    ## rancid 2.3.6
    ## Copyright (c) 1997-2009 by Terrapin Communications, Inc.
    ## All rights reserved.
    ##
    ## This code is derived from software contributed to and maintained by
    ## Terrapin Communications, Inc. by Henry Kilmer, John Heasley, Andrew Partan,
    ## Pete Whiting, Austin Schutz, and Andrew Fort.
    ##
    ## Redistribution and use in source and binary forms, with or without
    ## modification, are permitted provided that the following conditions
    ## are met:
    ## 1. Redistributions of source code must retain the above copyright
    ## notice, this list of conditions and the following disclaimer.
    ## 2. Redistributions in binary form must reproduce the above copyright
    ## notice, this list of conditions and the following disclaimer in the
    ## documentation and/or other materials provided with the distribution.
    ## 3. All advertising materials mentioning features or use of this software
    ## must display the following acknowledgement:
    ## This product includes software developed by Terrapin Communications,
    ## Inc. and its contributors for RANCID.
    ## 4. Neither the name of Terrapin Communications, Inc. nor the names of its
    ## contributors may be used to endorse or promote products derived from
    ## this software without specific prior written permission.
    ## 5. It is requested that non-binding fixes and modifications be contributed
    ## back to Terrapin Communications, Inc.
    ##
    ## THIS SOFTWARE IS PROVIDED BY Terrapin Communications, INC. AND CONTRIBUTORS
    ## ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
    ## TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
    ## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COMPANY OR CONTRIBUTORS
    ## BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    ## CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    ## SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    ## INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    ## CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    ## ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    ## POSSIBILITY OF SUCH DAMAGE.
    #
    # RANCID - Really Awesome New Cisco confIg Differ
    #
    # usage: rancid [-dV] [-l] [-f filename | hostname]
    # usage: vrancid [-dV] [-l] [-f filename | hostname]
    #
    use Getopt::Std;
    getopts('dflV');
  2. David Krider created this gist Nov 7, 2012.
    649 changes: 649 additions & 0 deletions vrancid
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,649 @@
    #! /usr/bin/perl
    ##
    ## $Id: rancid.in 2272 2011-01-10 20:01:58Z heas $
    ##
    ## rancid 2.3.6
    ## Copyright (c) 1997-2009 by Terrapin Communications, Inc.
    ## All rights reserved.
    ##
    ## This code is derived from software contributed to and maintained by
    ## Terrapin Communications, Inc. by Henry Kilmer, John Heasley, Andrew Partan,
    ## Pete Whiting, Austin Schutz, and Andrew Fort.
    ##
    ## Redistribution and use in source and binary forms, with or without
    ## modification, are permitted provided that the following conditions
    ## are met:
    ## 1. Redistributions of source code must retain the above copyright
    ## notice, this list of conditions and the following disclaimer.
    ## 2. Redistributions in binary form must reproduce the above copyright
    ## notice, this list of conditions and the following disclaimer in the
    ## documentation and/or other materials provided with the distribution.
    ## 3. All advertising materials mentioning features or use of this software
    ## must display the following acknowledgement:
    ## This product includes software developed by Terrapin Communications,
    ## Inc. and its contributors for RANCID.
    ## 4. Neither the name of Terrapin Communications, Inc. nor the names of its
    ## contributors may be used to endorse or promote products derived from
    ## this software without specific prior written permission.
    ## 5. It is requested that non-binding fixes and modifications be contributed
    ## back to Terrapin Communications, Inc.
    ##
    ## THIS SOFTWARE IS PROVIDED BY Terrapin Communications, INC. AND CONTRIBUTORS
    ## ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
    ## TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
    ## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COMPANY OR CONTRIBUTORS
    ## BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    ## CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    ## SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    ## INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    ## CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    ## ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    ## POSSIBILITY OF SUCH DAMAGE.
    #
    # RANCID - Really Awesome New Cisco confIg Differ
    #
    # usage: rancid [-dV] [-l] [-f filename | hostname]
    #
    use Getopt::Std;
    getopts('dflV');
    if ($opt_V) {
    print "rancid 2.3.6\n";
    exit(0);
    }
    $log = $opt_l;
    $debug = $opt_d;
    $file = $opt_f;
    $host = $ARGV[0];
    $proc = "";
    $ios = "IOS";
    $clean_run = 0;
    $found_end = 0;
    $found_version = 0;
    $found_env = 0;
    $found_diag = 0;
    $timeo = 90; # clogin timeout in seconds

    my(@commandtable, %commands, @commands);# command lists
    my($aclsort) = ("ipsort"); # ACL sorting mode
    my($config_register); # configuration register value
    my($filter_commstr); # SNMP community string filtering
    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,@string) = (@_);
    if ((($new_hist_tag ne $hist_tag) || ($new_command ne $command))
    && scalar(%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 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 (!$linecnt && /^\s+\^\s*$/);
    next if (/^\s*$cmd\s*$/);
    return(1) if (/Line has invalid autocommand /);
    return(1) if (/(Invalid (input|command) detected|Type help or )/i);
    return(1) if (/\%Error: No such file or directory/);
    return(1) if (/(Open device \S+ failed|Error opening \S+:)/);
    return(0) if ($found_end); # Only do this routine once
    return(-1) if (/command authorization failed/i);
    return(-1) if (/% ?configuration buffer full/i);
    # the pager can not be disabled per-session on the PIX
    if (/^(<-+ More -+>)/) {
    my($len) = length($1);
    s/^$1\s{$len}//;
    }
    /^! no configuration change since last restart/i && next;
    # skip emtpy lines at the beginning
    if (!$linecnt && /^\s*$/) {
    next;
    }
    if (!$linecnt && defined($config_register)) {
    ProcessHistory("","","", "!\nconfig-register $config_register\n");
    }
    /Non-Volatile memory is in use/ && return(-1); # NvRAM is locked
    /% Configuration buffer full, / && return(-1); # buffer is in use
    $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;
    }
    tr/\015//d;
    }
    # skip ASA 5520 configuration author line
    /^: written by /i && next;
    # some versions have other crap mixed in with the bits in the
    # block above
    /^! (Last configuration|NVRAM config last)/ && next;
    # and for the ASA
    /^: (Written by \S+ at|Saved)/ && next;

    # skip consecutive comment lines to avoid oscillating extra comment
    # line on some access servers. grrr.
    if (/^!\s*$/) {
    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;
    }
    # filter auto "rogue ap" configuration lines
    /^rogue ap classify / && 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;
    }
    # ASA/PIX keys in more system:running-config
    if (/^( pre-shared-key | key |failover key ).*/ && $filter_pwds >= 1) {
    ProcessHistory("","","","!$1 <removed> $'"); next;
    }
    if (/(\s+ldap-login-password )\S+(.*)/ && $filter_pwds >= 1) {
    ProcessHistory("","","","!$1 <removed> $'"); 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","$aclsort","$3","$_") && next;
    # order extended access-lists
    /^access-list\s+(\d\d\d)\s+(\S+)\s+ip\s+host\s+(\S+)/ &&
    ProcessHistory("EACL $1 $2","$aclsort","$3","$_") && next;
    /^access-list\s+(\d\d\d)\s+(\S+)\s+ip\s+(\d\S+)/ &&
    ProcessHistory("EACL $1 $2","$aclsort","$3","$_") && next;
    /^access-list\s+(\d\d\d)\s+(\S+)\s+ip\s+any/ &&
    ProcessHistory("EACL $1 $2","$aclsort","0.0.0.0","$_") && next;
    # order arp lists
    /^arp\s+(\d+\.\d+\.\d+\.\d+)\s+/ &&
    ProcessHistory("ARP","$aclsort","$1","$_") && next;
    /^ip(v6)? prefix-list\s+(\S+)\s+seq\s+(\d+)\s+(permit|deny)\s+(\S+)(\/.*)$/
    && ProcessHistory("PACL $2 $4","$aclsort","$5",
    "ip$1 prefix-list $2 $4 $5$6\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 ($filter_commstr) {
    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(' ',@tokens)));
    last;
    }
    }
    ProcessHistory("SNMPSERVERHOST","ipsort","$ip","$line\n");
    } else {
    ProcessHistory("SNMPSERVERHOST","ipsort","$1","$_");
    }
    next;
    }
    if (/^(snmp-server community) (\S+)/) {
    if ($filter_commstr) {
    ProcessHistory("SNMPSERVERCOMM","keysort","$_",
    "!$1 <removed>$'") && next;
    } else {
    ProcessHistory("SNMPSERVERCOMM","keysort","$_","$_") && next;
    }
    }
    # prune tacacs/radius server keys
    if (/^((tacacs|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;
    }

    /^ *Cryptochecksum:/ && next;

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

    return(0);
    }

    # dummy function
    sub DoNothing {print STDOUT;}

    # Main
    @commandtable = (
    {'show configuration' => '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(";",@commands);
    $cmds_regexp = join("|", map quotemeta($_), @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 clogin -t $timeo -c\"$cisco_cmds\" $host\n" if ($debug);
    print STDOUT "executing clogin -t $timeo -c\"$cisco_cmds\" $host\n" if ($log);
    if (defined($ENV{NOPIPE})) {
    system "clogin -t $timeo -c \"$cisco_cmds\" $host </dev/null > $host.raw 2>&1" || die "clogin failed for $host: $!\n";
    open(INPUT, "< $host.raw") || die "clogin failed for $host: $!\n";
    } else {
    open(INPUT,"clogin -t $timeo -c \"$cisco_cmds\" $host </dev/null |") || die "clogin failed for $host: $!\n";
    }
    }

    # determine ACL sorting mode
    if ($ENV{"ACLSORT"} =~ /no/i) {
    $aclsort = "";
    }
    # determine community string filtering mode
    if (defined($ENV{"NOCOMMSTR"}) &&
    ($ENV{"NOCOMMSTR"} =~ /yes/i || $ENV{"NOCOMMSTR"} =~ /^$/)) {
    $filter_commstr = 1;
    } else {
    $filter_commstr = 0;
    }
    # 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: vyatta\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 (/[>#]\s?exit$/) {
    $clean_run = 1;
    last;
    }
    if (/^Error:/) {
    print STDOUT ("$host clogin error: $_");
    print STDERR ("$host clogin error: $_") if ($debug);
    $clean_run = 0;
    last;
    }
    while (/[>#]\s*($cmds_regexp)\s*$/) {
    $cmd = $1;
    if (!defined($prompt)) {
    $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;
    }
    }
    }
    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);
    }
    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);
    }