1#!/usr/local/bin/perl
2##
3## @PACKAGE@ @VERSION@
4## Copyright (c) @COPYYEARS@ by Henry Kilmer and John Heasley
5## All rights reserved.
6##
7## This code is derived from software contributed to and maintained by
8## Henry Kilmer, John Heasley, Andrew Partan,
9## Pete Whiting, Austin Schutz, and Andrew Fort.
10##
11## Redistribution and use in source and binary forms, with or without
12## modification, are permitted provided that the following conditions
13## are met:
14## 1. Redistributions of source code must retain the above copyright
15##    notice, this list of conditions and the following disclaimer.
16## 2. Redistributions in binary form must reproduce the above copyright
17##    notice, this list of conditions and the following disclaimer in the
18##    documentation and/or other materials provided with the distribution.
19## 3. Neither the name of RANCID nor the names of its
20##    contributors may be used to endorse or promote products derived from
21##    this software without specific prior written permission.
22##
23## THIS SOFTWARE IS PROVIDED BY Henry Kilmer, John Heasley AND CONTRIBUTORS
24## ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
25## TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
26## PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COMPANY OR CONTRIBUTORS
27## BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28## CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
29## SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
30## INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
31## CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32## ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33## POSSIBILITY OF SUCH DAMAGE.
34##
35## It is the request of the authors, but not a condition of license, that
36## parties packaging or redistributing RANCID NOT distribute altered versions
37## of the etc/rancid.types.base file nor alter how this file is processed nor
38## when in relation to etc/rancid.types.conf.  The goal of this is to help
39## suppress our support costs.  If it becomes a problem, this could become a
40## condition of license.
41#
42#  The expect login scripts were based on Erik Sherk's gwtn, by permission.
43#
44#  The original looking glass software was written by Ed Kern, provided by
45#  permission and modified beyond recognition.
46#
47# hacked version of Hank's rancid - this one tries to deal with Xirrus arrays.
48#
49#  RANXID - Really Awesome New Xirrus confIg Differ
50#
51# usage: xirancid [-dltCV] [-f filename | hostname]
52#
53use Getopt::Std;
54getopts('dflt:CV');
55if ($opt_V) {
56    print "@PACKAGE@ @VERSION@\n";
57    exit(0);
58}
59$log = $opt_l;
60$debug = $opt_d;
61$file = $opt_f;
62$host = $ARGV[0];
63$clean_run = 0;
64$timeo = 90;				# xilogin timeout in seconds
65
66my(@commandtable, %commands, @commands);# command lists
67my($filter_commstr);			# SNMP community string filtering
68my($filter_osc);			# oscillating data filtering mode
69my($filter_pwds);			# password filtering mode
70
71# This routine is used to print out the router configuration
72sub ProcessHistory {
73    my($new_hist_tag,$new_command,$command_string,@string) = (@_);
74    if ((($new_hist_tag ne $hist_tag) || ($new_command ne $command))
75	&& scalar(%history)) {
76	print eval "$command \%history";
77	undef %history;
78    }
79    if (($new_hist_tag) && ($new_command) && ($command_string)) {
80	if ($history{$command_string}) {
81	    $history{$command_string} = "$history{$command_string}@string";
82	} else {
83	    $history{$command_string} = "@string";
84	}
85    } elsif (($new_hist_tag) && ($new_command)) {
86	$history{++$#history} = "@string";
87    } else {
88	print "@string";
89    }
90    $hist_tag = $new_hist_tag;
91    $command = $new_command;
92    1;
93}
94
95# This routine parses "show config"
96sub ShowConfig {
97    print STDERR "    In ShowConfig: $_\n" if ($debug);
98
99    while (<INPUT>) {
100	tr/\015//d;
101	last if (/^$prompt/);
102	next if (/^(\s*|\s*$cmd\s*)$/);
103	ProcessHistory("","","","$_");
104    }
105    return(0);
106}
107
108# Main
109@commandtable = (
110	{'show boot-env' 			=> "ShowConfig"},
111	{'show running-config inc-defaults'	=> "ShowConfig"}
112);
113# Use an array to preserve the order of the commands and a hash for mapping
114# commands to the subroutine and track commands that have been completed.
115@commands = map(keys(%$_), @commandtable);
116%commands = map(%$_, @commandtable);
117$commandcnt = scalar(keys %commands);
118
119$xirrus_cmds=join(";",@commands);
120$cmds_regexp = join("|", map quotemeta($_), @commands);
121
122if (length($host) == 0) {
123    if ($file) {
124	print(STDERR "Too few arguments: file name required\n");
125	exit(1);
126    } else {
127	print(STDERR "Too few arguments: host name required\n");
128	exit(1);
129    }
130}
131if ($opt_C) {
132    print "xilogin -t $timeo -c\'$xirrus_cmds\' $host\n";
133    exit(0);
134}
135open(OUTPUT,">$host.new") || die "Can't open $host.new for writing: $!\n";
136select(OUTPUT);
137# make OUTPUT unbuffered if debugging
138if ($debug) { $| = 1; }
139
140if ($file) {
141    print(STDERR "opening file $host\n") if ($debug || $log);
142    open(INPUT,"<$host") || die "open failed for $host: $!\n";
143} else {
144    print(STDERR "executing xilogin -t $timeo -c\"$xirrus_cmds\" $host\n") if ($debug || $log);
145    system "xilogin -t $timeo -c \"$xirrus_cmds\" $host </dev/null > $host.raw 2>&1" || die "xilogin failed for $host: $!\n";
146    open(INPUT, "< $host.raw") || die "xilogin failed for $host: $!\n";
147}
148
149# determine community string filtering mode
150if (defined($ENV{"NOCOMMSTR"}) &&
151    ($ENV{"NOCOMMSTR"} =~ /yes/i || $ENV{"NOCOMMSTR"} =~ /^$/)) {
152    $filter_commstr = 1;
153} else {
154    $filter_commstr = 0;
155}
156# determine oscillating data filtering mode
157if (defined($ENV{"FILTER_OSC"}) && $ENV{"FILTER_OSC"} =~ /no/i) {
158    $filter_osc = 0;
159} else {
160    $filter_osc = 1;
161}
162# determine password filtering mode
163if ($ENV{"FILTER_PWDS"} =~ /no/i) {
164    $filter_pwds = 0;
165} elsif ($ENV{"FILTER_PWDS"} =~ /all/i) {
166    $filter_pwds = 2;
167} else {
168    $filter_pwds = 1;
169}
170
171ProcessHistory("","","","!RANCID-CONTENT-TYPE: xirrus\n!\n");
172TOP: while(<INPUT>) {
173    tr/\015//d;
174    if (/^end/)  {
175	$clean_run=1;
176        last;
177    }
178    if (/^Error:/) {
179	print STDOUT ("$host xilogin error: $_");
180	print STDERR ("$host xilogin error: $_") if ($debug);
181	$clean_run=0;
182	last;
183    }
184    while (/[>#]\s*($cmds_regexp)\s*$/) {
185	$cmd = $1;
186	if (!defined($prompt)) {
187	    $prompt = ($_ =~ /^([^:]+:)/)[0];
188	}
189	print STDERR ("HIT COMMAND:$_") if ($debug);
190	if (! defined($commands{$cmd})) {
191	    print STDERR "$host: found unexpected command - \"$cmd\"\n";
192	    $clean_run = 0;
193	    last TOP;
194	}
195	$rval = &{$commands{$cmd}}(*INPUT, *OUTPUT, $cmd);
196	delete($commands{$cmd});
197	if ($rval == -1) {
198	    $clean_run = 0;
199	    last TOP;
200	}
201    }
202}
203print STDOUT "Done $logincmd: $_\n" if ($log);
204# Flush History
205ProcessHistory("","","","");
206# Cleanup
207close(INPUT);
208close(OUTPUT);
209
210unlink("$host.raw") if (! $debug);
211
212# check for completeness
213if (scalar(%commands) || !$clean_run ) {
214    if (scalar(keys %commands) eq $commandcnt) {
215	printf(STDERR "$host: missed cmd(s): all commands\n");
216    } elsif (scalar(%commands)) {
217	my($count, $i) = 0;
218	for ($i = 0; $i < $#commands; $i++) {
219	    if ($commands{$commands[$i]}) {
220		if (!$count) {
221		    printf(STDERR "$host: missed cmd(s): %s", $commands[$i]);
222		} else {
223		    printf(STDERR ", %s", $commands[$i]);
224		}
225		$count++;
226	    }
227	}
228	if ($count) {
229	    printf(STDERR "\n");
230	}
231    }
232    if (!$clean_run) {
233	print(STDERR "$host: End of run not found\n");
234	if ($debug) {
235	    print(STDERR "$host: clean_run is false\n") if (!$clean_run);
236	}
237	system("/usr/bin/tail -1 $host.new");
238    }
239    unlink "$host.new" if (! $debug);
240}
241