· 8 years ago · Nov 13, 2017, 11:56 PM
1## Pete Whiting, Austin Schutz, and Andrew Fort.
2##
3## Redistribution and use in source and binary forms, with or without
4## modification, are permitted provided that the following conditions
5## are met:
6## 1. Redistributions of source code must retain the above copyright
7## notice, this list of conditions and the following disclaimer.
8## 2. Redistributions in binary form must reproduce the above copyright
9## notice, this list of conditions and the following disclaimer in the
10## documentation and/or other materials provided with the distribution.
11## 3. All advertising materials mentioning features or use of this software
12## must display the following acknowledgement:
13## This product includes software developed by Terrapin Communications,
14## Inc. and its contributors for RANCID.
15## 4. Neither the name of Terrapin Communications, Inc. nor the names of its
16## contributors may be used to endorse or promote products derived from
17## this software without specific prior written permission.
18## 5. It is requested that non-binding fixes and modifications be contributed
19## back to Terrapin Communications, Inc.
20##
21## THIS SOFTWARE IS PROVIDED BY Terrapin Communications, INC. AND CONTRIBUTORS
22## ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
23## TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COMPANY OR CONTRIBUTORS
25## BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26## CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27## SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28## INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29## CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30## ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31## POSSIBILITY OF SUCH DAMAGE.
32##
33## It is the request of the authors, but not a condition of license, that
34## parties packaging or redistributing RANCID NOT distribute altered versions
35## of the etc/rancid.types.base file nor alter how this file is processed nor
36## when in relation to etc/rancid.types.conf. The goal of this is to help
37## suppress our support costs. If it becomes a problem, this could become a
38## condition of license.
39#
40# The expect login scripts were based on Erik Sherk's gwtn, by permission.
41#
42# The original looking glass software was written by Ed Kern, provided by
43# permission and modified beyond recognition.
44#
45# RANCID - Really Awesome New Cisco confIg Differ
46#
47# plogin - poly-login; use router.db files and rancid.{base,types}.conf
48# configurations to determine which login script to execute.
49#
50use 5.0010;
51use strict 'vars';
52use warnings;
53no warnings 'uninitialized';
54use Exporter;
55use Getopt::Long;
56# use Getopt::Long qw(:config no_ignore_case bundling);
57Getopt::Long::Configure ("bundling", "no_ignore_case");
58our($opt_d, $opt_S, $opt_V, $opt_autoenable, $opt_noenable, $opt_c, @opt_E, $opt_e,
59 $opt_f, $opt_h, $opt_p, $opt_r, $opt_s, $opt_t, $opt_u, $opt_v, $opt_w,
60 $opt_x, $opt_y, $opt_z);
61GetOptions('d' => \$opt_d, 'S' => \$opt_S, 'V' => \$opt_V,
62 'autoenable' => \$opt_autoenable, 'noenable' => \$opt_noenable,
63 'c=s' => \$opt_c, "E=s@" => \@opt_E, 'e=s' => \$opt_e, 'f=s' => \$opt_f,
64 'h=s' => \$opt_h,'p=s' => \$opt_p, 'r=s' => \$opt_r, 's=s' => \$opt_s,
65 't=s' => \$opt_t, 'u=s' => \$opt_u, 'v=s' => \$opt_v, 'w=s' => \$opt_w,
66 'x=s' => \$opt_x, 'y=s' => \$opt_y, 'z=s' => \$opt_z);
67my($BASEDIR, $LIST_OF_GROUPS, $cmd, $i, $j, @routers);
68BEGIN {
69 push(@INC, "/usr/local/rancid/lib/rancid");
70}
71use rancid;
72our @ISA = qw(Exporter rancid);
73
74sub usage()
75{
76 print STDERR "plogin [-dSV] [-autoenable] [-noenable] [-c command] [-Evar=x] [-e enable-password] [-f cloginrc-file] [-p user-password] [-r passphrase] [-s script-file] [-t timeout] [-u username] [-v vty-password] [-w enable-username] [-x command-file] [-y ssh_cypher_type] [-z device_type] router [router...]\n";
77 exit 64;
78}
79
80# make OUTPUT unbuffered if debugging
81if ($opt_d) { $| = 1; }
82
83if ($opt_h) {
84 usage();
85}
86
87# option handling initialization
88if ($opt_V) {
89 print "plogin: rancid 3.4.1\n";
90 # do not exit; exec the script with -V and it will exit
91}
92$cmd .= " -d" if ($opt_d);
93$cmd .= " -S" if ($opt_S);
94$cmd .= " -V" if ($opt_V);
95$cmd .= " -autoenable" if ($opt_autoenable);
96$cmd .= " -noenable" if ($opt_noenable);
97$cmd .= " -c '$opt_c'" if (length($opt_c));
98foreach $i (@opt_E) {
99 $cmd .= " -E$i";
100}
101$cmd .= " -e '$opt_e'" if (length($opt_e));
102$cmd .= " -f '$opt_f'" if (length($opt_f));
103$cmd .= " -p '$opt_p'" if (length($opt_p));
104$cmd .= " -r '$opt_r'" if (length($opt_r));
105$cmd .= " -s '$opt_s'" if (length($opt_s));
106$cmd .= " -t $opt_s" if (length($opt_t));
107$cmd .= " -u '$opt_u'" if (length($opt_u));
108$cmd .= " -v '$opt_v'" if (length($opt_v));
109$cmd .= " -w '$opt_w'" if (length($opt_w));
110$cmd .= " -x '$opt_x'" if (length($opt_x));
111$cmd .= " -y '$opt_y'" if (length($opt_y));
112$devtype = $opt_z;
113foreach $i (@ARGV) {
114 $cmd .= " $i";
115 push(@routers, $i);
116}
117
118# what is the device type, supplied or looked-up
119if ($opt_z) {
120 $devtype = $opt_z;
121} else {
122 # Look in router.dbs for device type.
123 # read rancid.conf for BASEDIR and LIST_OF_GROUPS?
124 my($ENVFILE) = "/usr/local/rancid/etc/rancid.conf";
125 open(INPUT, "< $ENVFILE") || die "Could not open $ENVFILE: $!";
126 close(INPUT);
127 open(INPUT,
128 "sh -c \'. $ENVFILE; echo \"BASEDIR=\$BASEDIR\"; echo \"LIST_OF_GROUPS=\$LIST_OF_GROUPS\"\' |") ||
129 die "Could not open $ENVFILE: $!";
130 while (<INPUT>) {
131 chomp;
132 s/#.$//;
133 s/^\s+//;
134 my($varname, $value) = split('=');
135 if ($varname eq "BASEDIR") {
136 $BASEDIR = $value;
137 }
138 if ($varname eq "LIST_OF_GROUPS") {
139 $LIST_OF_GROUPS = $value;
140 $LIST_OF_GROUPS =~ s/^\s+//;
141 $LIST_OF_GROUPS =~ s/\s+$//;
142 }
143 }
144 close(INPUT);
145
146 # read each router.db for the routername and thus the devtype
147 foreach $i (split(/\s+/, $LIST_OF_GROUPS)) {
148 if (!open(INPUT, "< $BASEDIR/$i/router.db")) {
149 warn "Could not open $BASEDIR/$i/router.db: $!";
150 next;
151 }
152 while (<INPUT>) {
153 chomp;
154 s/#.$//;
155 s/^\s+//;
156 my($router, $type, $state) = split('\;');
157 foreach $j (@routers) {
158 if ($router eq $j) {
159 $devtype = $type;
160 close(INPUT);
161 goto FOUND;
162 }
163 }
164 }
165 close(INPUT);
166 }
167}
168
169FOUND:
170if (length($devtype) < 1) {
171 die "Couldn't find device type by hostname in router.dbs\n";
172}
173
174# load device type spec, build @commandtable and load modules
175if (loadtype($devtype)) {
176 die "Couldn't load device type spec for $rancid::devtype\n";
177}
178if (! defined($lscript)) {
179 die "login script not defined for device type $rancid::devtype\n";
180}
181exec($lscript . " $cmd") ||
182printf(STDERR "exec($script) failed router manufacturer $devtype: $!\n");
183exit(-1);
184#! /usr/bin/perl
185##
186## $Id: prancid.in 2279 2011-01-31 22:41:00Z heas $
187##
188## rancid 2.3.8
189## Copyright (c) 1997-2008 by Terrapin Communications, Inc.
190## All rights reserved.
191##
192## This code is derived from software contributed to and maintained by
193## Terrapin Communications, Inc. by Henry Kilmer, John Heasley, Andrew Partan,
194## Pete Whiting, Austin Schutz, and Andrew Fort.
195##
196## Redistribution and use in source and binary forms, with or without
197## modification, are permitted provided that the following conditions
198## are met:
199## 1. Redistributions of source code must retain the above copyright
200## notice, this list of conditions and the following disclaimer.
201## 2. Redistributions in binary form must reproduce the above copyright
202## notice, this list of conditions and the following disclaimer in the
203## documentation and/or other materials provided with the distribution.
204## 3. All advertising materials mentioning features or use of this software
205## must display the following acknowledgement:
206## This product includes software developed by Terrapin Communications,
207## Inc. and its contributors for RANCID.
208## 4. Neither the name of Terrapin Communications, Inc. nor the names of its
209## contributors may be used to endorse or promote products derived from
210## this software without specific prior written permission.
211## 5. It is requested that non-binding fixes and modifications be contributed
212## back to Terrapin Communications, Inc.
213##
214## THIS SOFTWARE IS PROVIDED BY Terrapin Communications, INC. AND CONTRIBUTORS
215## ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
216## TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
217## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COMPANY OR CONTRIBUTORS
218## BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
219## CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
220## SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
221## INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
222## CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
223## ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
224## POSSIBILITY OF SUCH DAMAGE.
225#
226# This version of rancid tries to deal with Prockets.
227#
228# RANCID - Really Awesome New Cisco confIg Differ
229#
230# usage: rancid [-dV] [-l] [-f filename | hostname]
231#
232use Getopt::Std;
233getopts('dflV');
234if ($opt_V) {
235 print "rancid 2.3.8\n";
236 exit(0);
237}
238$log = $opt_l;
239$debug = $opt_d;
240$file = $opt_f;
241$host = $ARGV[0];
242$clean_run = 0;
243$found_end = 0;
244$timeo = 90; # clogin timeout in seconds
245
246my(@commandtable, %commands, @commands);# command lists
247my($aclsort) = ("ipsort"); # ACL sorting mode
248my($filter_commstr); # SNMP community string filtering
249my($filter_pwds); # password filtering mode
250my($platform); # platform/cpu type
251
252# This routine is used to print out the router configuration
253sub ProcessHistory {
254 my($new_hist_tag,$new_command,$command_string,@string) = (@_);
255 if ((($new_hist_tag ne $hist_tag) || ($new_command ne $command))
256 && scalar(%history)) {
257 print eval "$command \%history";
258 undef %history;
259 }
260 if (($new_hist_tag) && ($new_command) && ($command_string)) {
261 if ($history{$command_string}) {
262 $history{$command_string} = "$history{$command_string}@string";
263 } else {
264 $history{$command_string} = "@string";
265 }
266 } elsif (($new_hist_tag) && ($new_command)) {
267 $history{++$#history} = "@string";
268 } else {
269 print "@string";
270 }
271 $hist_tag = $new_hist_tag;
272 $command = $new_command;
273 1;
274}
275
276sub numerically { $a <=> $b; }
277
278# This is a sort routine that will sort numerically on the
279# keys of a hash as if it were a normal array.
280sub keynsort {
281 local(%lines) = @_;
282 local($i) = 0;
283 local(@sorted_lines);
284 foreach $key (sort numerically keys(%lines)) {
285 $sorted_lines[$i] = $lines{$key};
286 $i++;
287 }
288 @sorted_lines;
289}
290
291# This is a sort routine that will sort on the
292# keys of a hash as if it were a normal array.
293sub keysort {
294 local(%lines) = @_;
295 local($i) = 0;
296 local(@sorted_lines);
297 foreach $key (sort keys(%lines)) {
298 $sorted_lines[$i] = $lines{$key};
299 $i++;
300 }
301 @sorted_lines;
302}
303
304# This is a sort routine that will sort on the
305# values of a hash as if it were a normal array.
306sub valsort{
307 local(%lines) = @_;
308 local($i) = 0;
309 local(@sorted_lines);
310 foreach $key (sort values %lines) {
311 $sorted_lines[$i] = $key;
312 $i++;
313 }
314 @sorted_lines;
315}
316
317# This is a numerical sort routine (ascending).
318sub numsort {
319 local(%lines) = @_;
320 local($i) = 0;
321 local(@sorted_lines);
322 foreach $num (sort {$a <=> $b} keys %lines) {
323 $sorted_lines[$i] = $lines{$num};
324 $i++;
325 }
326 @sorted_lines;
327}
328
329# This is a sort routine that will sort on the
330# ip address when the ip address is anywhere in
331# the strings.
332sub ipsort {
333 local(%lines) = @_;
334 local($i) = 0;
335 local(@sorted_lines);
336 foreach $addr (sort sortbyipaddr keys %lines) {
337 $sorted_lines[$i] = $lines{$addr};
338 $i++;
339 }
340 @sorted_lines;
341}
342
343# These two routines will sort based upon IP addresses
344sub ipaddrval {
345 my(@a) = ($_[0] =~ m#^(\d+)\.(\d+)\.(\d+)\.(\d+)$#);
346 $a[3] + 256 * ($a[2] + 256 * ($a[1] +256 * $a[0]));
347}
348sub sortbyipaddr {
349 &ipaddrval($a) <=> &ipaddrval($b);
350}
351
352# This routine parses "show version"
353sub ShowVersion {
354 print STDERR " In ShowVersion: $_" if ($debug);
355
356 while (<INPUT>) {
357 tr/\015//d;
358 last if(/^$prompt/);
359 next if(/^(\s*|\s*$cmd\s*)$/);
360 return(-1) if (/command authorization failed/i);
361
362 if (/(lynxos|kernel) Version: .* (\S+)/i) {
363 $platform = $2;
364 }
365 /Procket/ && ProcessHistory("COMMENTS","keysort","B0", "! $_") && next;
366 /System Uptime:/ && next;
367 /Protocol Uptime:/ && next;
368 ProcessHistory("COMMENTS","keysort","B0", "!$_") && next;
369
370 }
371 return(0);
372}
373
374# This routine parses "show package"
375sub ShowPackage {
376 print STDERR " In ShowPackage: $_" if ($debug);
377
378 while (<INPUT>) {
379 tr/\015//d;
380 last if(/^$prompt/);
381 next if(/^(\s*|\s*$cmd\s*)$/);
382 return(-1) if (/command authorization failed/i);
383
384 ProcessHistory("COMMENTS","keysort","C0", "! $_") && next;
385
386 }
387 return(0);
388}
389
390# This routine parses "show hardware"
391sub ShowHardware {
392 print STDERR " In ShowHardware: $_" if ($debug);
393
394 while (<INPUT>) {
395 tr/\015//d;
396 last if(/^$prompt/);
397 next if(/^(\s*|\s*$cmd\s*)$/);
398 # skip show hardware on titanium
399 return(0) if ($platform =~ /i386/i);
400 return(-1) if (/command authorization failed/i);
401 return(-1) if (/cli: couldn.t communicate with/);
402
403 ProcessHistory("COMMENTS","keysort","D0", "! $_") && next;
404
405 }
406 return(0);
407}
408
409# This routine parses "show inventory"
410sub ShowInventory {
411 print STDERR " In ShowInventory: $_" if ($debug);
412
413 while (<INPUT>) {
414 tr/\015//d;
415 last if(/^$prompt/);
416 next if(/^(\s*|\s*$cmd\s*)$/);
417 return(0) if (/^\s+\^/);
418 return(-1) if (/command authorization failed/i);
419
420 /Procket/ && ProcessHistory("COMMENTS","keysort","E0", "! $_") && next;
421 /System Uptime:/ && next;
422 /Protocol Uptime:/ && next;
423 ProcessHistory("COMMENTS","keysort","E0", "!$_") && next;
424
425 }
426 return(0);
427}
428
429# This routine processes a "write term"
430sub WriteTerm {
431 print STDERR " In WriteTerm: $_" if ($debug);
432
433 while (<INPUT>) {
434 tr/\015//d;
435 last if(/^$prompt/);
436 return(-1) if (/command authorization failed/i);
437
438 /Non-Volatile memory is in use/ && return(-1); # NvRAM is locked
439 # skip the crap
440 if (/^(##+$|(Building|Current) configuration)/i) {
441 while (<INPUT>) {
442 next if (/^Current configuration\s*:/i);
443 next if (/^([%!].*|\s*)$/);
444 last;
445 }
446 tr/\015//d;
447 }
448 # some versions have other crap mixed in with the bits in the
449 # block above
450 /^! Last Changed:/ && next;
451
452 # Dog gone Cool matches to process the rest of the config
453# /^tftp-server flash / && next; # kill any tftp remains
454# /^ntp clock-period / && next; # kill ntp clock-period
455# /^ length / && next; # kill length on serial lines
456# /^ width / && next; # kill width on serial lines
457# /^ clockrate / && next; # kill clockrate on serial interfaces
458
459 if (/^(enable secret( level \d)?) / && $filter_pwds >= 2) {
460 ProcessHistory("ENABLE","","","!$1 <removed>\n");
461 next;
462 }
463 if (/^username (\S+)(\s.*)? password ((\d) \S+|\S+)/) {
464 if ($filter_pwds == 2) {
465 ProcessHistory("USER","keysort","$1","!username $1$2 password <removed>\n");
466 } elsif ($filter_pwds == 1 && $4 ne "5"){
467 ProcessHistory("USER","keysort","$1","!username $1$2 password <removed>\n");
468 } else {
469 ProcessHistory("USER","keysort","$1","$_");
470 }
471 next;
472 }
473
474 # prune passwords {bgp, ...}
475 if (/^(\s*)password / && $filter_pwds >= 1) {
476 ProcessHistory("","","","!$1password <removed>\n");
477 next;
478 }
479 # prune authentication keys {vrrp vrid N, router isis...}
480 if (/^(\s*authentication \S+ key) / && $filter_pwds >= 1) {
481 ProcessHistory("","","","!$1 <removed>\n");
482 next;
483 }
484 if (/^(\s*authentication-key) \d \S+( .*)/ && $filter_pwds >= 1) {
485 ProcessHistory("","","","!$1 <removed>$2\n");
486 next;
487 }
488
489# if (/^\s*neighbor (\S*) password / && $filter_pwds >= 1) {
490# ProcessHistory("","","","! neighbor $1 password <removed>\n");
491# next;
492# }
493# if (/^(ppp .* password) 7 .*/ && $filter_pwds >= 1) {
494# ProcessHistory("","","","!$1 <removed>\n"); next;
495# }
496# if (/^(ip ftp password) / && $filter_pwds >= 1) {
497# ProcessHistory("","","","!$1 <removed>\n"); next;
498# }
499
500 # prune ospf keys
501 if (/^( ip ospf authentication-key) / && $filter_pwds >= 1) {
502 ProcessHistory("","","","!$1 <removed>\n"); next;
503 }
504 # this is reversable, despite 'md5' in the cmd
505 if (/^( ip ospf message-digest-key \d+ md5) / && $filter_pwds >= 1) {
506 ProcessHistory("","","","!$1 <removed>\n"); next;
507 }
508 # this is reversable, despite 'md5' in the cmd
509 if (/^(\s*message-digest-key \d+ md5) / && $filter_pwds >= 1) {
510 ProcessHistory("","","","!$1 <removed>\n"); next;
511 }
512
513# if (/^((crypto )?isakmp key) \S+ / && $filter_pwds >= 1) {
514# ProcessHistory("","","","!$1 <removed> $'"); next;
515# }
516
517 # sort ip explicit-paths.
518 if (/^ip explicit-path name (\S+)/) {
519 my($key) = $1;
520 my($expath) = $_;
521 while (<INPUT>) {
522 tr/\015//d;
523 last if (/^$prompt/);
524 last if (/^$prompt/ || ! /^(ip explicit-path name |[ !])/);
525 if (/^ip explicit-path name (\S+)/) {
526 ProcessHistory("EXPATH","keysort","$key","$expath");
527 $key = $1;
528 $expath = $_;
529 } else {
530 $expath .= $_;
531 }
532 }
533 ProcessHistory("EXPATH","keysort","$key","$expath");
534 }
535
536 # sort route-maps
537 if (/^route-map (\S+)/) {
538 my($key) = $1;
539 my($routemap) = $_;
540 while (<INPUT>) {
541 tr/\015//d;
542 last if (/^$prompt/ || ! /^(route-map |[ !])/);
543 if (/^route-map (\S+)/) {
544 ProcessHistory("ROUTEMAP","keysort","$key","$routemap");
545 $key = $1;
546 $routemap = $_;
547 } else {
548 $routemap .= $_;
549 }
550 }
551 ProcessHistory("ROUTEMAP","keysort","$key","$routemap");
552 }
553
554 # filter out any RCS/CVS tags to avoid confusing local CVS storage
555 s/\$(Revision|Id):/ $1:/;
556
557# # order access-lists
558# /^access-list\s+(\d\d?)\s+(\S+)\s+(\S+)/ &&
559# ProcessHistory("ACL $1 $2","$aclsort","$3","$_") && next;
560# # order extended access-lists
561# /^access-list\s+(\d\d\d)\s+(\S+)\s+ip\s+host\s+(\S+)/ &&
562# ProcessHistory("EACL $1 $2","$aclsort","$3","$_") && next;
563# /^access-list\s+(\d\d\d)\s+(\S+)\s+ip\s+(\d\S+)/ &&
564# ProcessHistory("EACL $1 $2","$aclsort","$3","$_") && next;
565# /^access-list\s+(\d\d\d)\s+(\S+)\s+ip\s+any/ &&
566# ProcessHistory("EACL $1 $2","$aclsort","0.0.0.0","$_") && next;
567# # order arp lists
568# /^arp\s+(\d+\.\d+\.\d+\.\d+)\s+/ &&
569# ProcessHistory("ARP","$aclsort","$1","$_") && next;
570# /^ip prefix-list\s+(\S+)\s+seq\s+(\d+)\s+(permit|deny)\s+(\d\S+)(\/.*)$/ &&
571# ProcessHistory("PACL $1 $3","$aclsort","$4","ip prefix-list $1 $3 $4$5\n")
572# && next;
573
574 # order logging statements
575 /^logging (\d+\.\d+\.\d+\.\d+)/ &&
576 ProcessHistory("LOGGING","ipsort","$1","$_") && next;
577
578 # order/prune snmp-server host statements
579 # we only prune lines of the form
580 # snmp-server host a.b.c.d <community>
581 if (/^snmp-server host (\d+\.\d+\.\d+\.\d+) /) {
582 if ($filter_commstr) {
583 my($ip) = $1;
584 my($line) = "snmp-server host $ip";
585 my(@tokens) = split(' ', $');
586 my($token);
587 while ($token = shift(@tokens)) {
588 if ($token eq 'version') {
589 $line .= " " . join(' ', ($token, shift(@tokens)));
590 } elsif ($token =~ /^(informs?|traps?|(no)?auth)$/) {
591 $line .= " " . $token;
592 } else {
593 $line = "!$line " . join(' ', ("<removed>", join(' ',@tokens)));
594 last;
595 }
596 }
597 ProcessHistory("SNMPSERVERHOST","ipsort","$ip","$line\n");
598 } else {
599 ProcessHistory("SNMPSERVERHOST","ipsort","$1","$_");
600 }
601 next;
602 }
603 if (/^(snmp-server community) (\S+)/) {
604 if ($filter_commstr) {
605 ProcessHistory("SNMPSERVERCOMM","keysort","$_","!$1 <removed>$'") && next;
606 } else {
607 ProcessHistory("SNMPSERVERCOMM","keysort","$_","$_") && next;
608 }
609 }
610
611 # prune tacacs/radius server keys
612 if (/^(tacacs-server|radius-server) key / && $filter_pwds >= 1) {
613 ProcessHistory("","","","!$1 key <removed>\n"); next;
614 }
615 if (/^(tacacs-server host \S+( .*)? key) (\d )?\S+/
616 && $filter_pwds >= 1) {
617 ProcessHistory("","","","!$1 <removed>\n"); next;
618 }
619
620 # order clns host statements
621# /^clns host \S+ (\S+)/ &&
622# ProcessHistory("CLNS","keysort","$1","$_") && next;
623
624 # prune vrrp password
625 if (/^( ip vrrp authentication .* key) / && $filter_pwds >= 1) {
626 ProcessHistory("","","","!$1 <removed>\n"); next;
627 }
628 # prune isis password
629 if (/^( isis authentication-key) \d \S+/ && $filter_pwds >= 1) {
630 ProcessHistory("","","","!$1 <removed>$'"); next;
631 }
632 # prune msdp password
633 if (/^(ip msdp password \S+) / && $filter_pwds >= 1) {
634 ProcessHistory("","","","!$1 <removed>\n"); next;
635 }
636 # delete ntp auth password - this md5 is a reversable too
637 if (/^(ntp authentication-key \d+ md5) / && $filter_pwds >= 1) {
638 ProcessHistory("","","","!$1 <removed>\n"); next;
639 }
640 # order ntp peers/servers
641 if (/^ntp (server|peer) (\d+)\.(\d+)\.(\d+)\.(\d+)/) {
642 $sortkey = sprintf("$1 %03d%03d%03d%03d",$2,$3,$4,$5);
643 ProcessHistory("NTP","keysort",$sortkey,"$_");
644 next;
645 }
646
647# # order ip host line statements
648# /^ip host line(\d+)/ &&
649# ProcessHistory("IPHOST","numsort","$1","$_") && next;
650# # order ip nat source static statements
651# /^ip nat (\S+) source static (\S+)/ &&
652# ProcessHistory("IP NAT $1","ipsort","$2","$_") && next;
653# # order atm map-list statements
654# /^\s+ip\s+(\d+\.\d+\.\d+\.\d+)\s+atm-vc/ &&
655# ProcessHistory("ATM map-list","ipsort","$1","$_") && next;
656# # order ip rcmd lines
657# /^ip rcmd/ && ProcessHistory("RCMD","keysort","$_","$_") && next;
658
659 # catch anything that wasnt matched above.
660 ProcessHistory("","","","$_");
661 # end of config.
662 if (/^end$/) {
663 $found_end = 1;
664 return(1);
665 }
666 }
667 return(0);
668}
669
670# dummy function
671sub DoNothing {print STDOUT;}
672
673# Main
674@commandtable = (
675 {'show version all' => 'ShowVersion'},
676 {'show package' => 'ShowPackage'},
677 {'show hardware' => 'ShowHardware'},
678 {'show inventory' => 'ShowInventory'},
679 {'write term' => 'WriteTerm'}
680);
681# Use an array to preserve the order of the commands and a hash for mapping
682# commands to the subroutine and track commands that have been completed.
683@commands = map(keys(%$_), @commandtable);
684%commands = map(%$_, @commandtable);
685
686$cisco_cmds=join(";",@commands);
687$cmds_regexp = join("|", map quotemeta($_), @commands);
688
689if (length($host) == 0) {
690 if ($file) {
691 print(STDERR "Too few arguments: file name required\n");
692 exit(1);
693 } else {
694 print(STDERR "Too few arguments: host name required\n");
695 exit(1);
696 }
697}
698open(OUTPUT,">$host.new") || die "Can't open $host.new for writing: $!\n";
699select(OUTPUT);
700# make OUTPUT unbuffered if debugging
701if ($debug) { $| = 1; }
702
703if ($file) {
704 print STDERR "opening file $host\n" if ($debug);
705 print STDOUT "opening file $host\n" if ($log);
706 open(INPUT,"<$host") || die "open failed for $host: $!\n";
707} else {
708 print STDERR "executing clogin -t $timeo -c\"$cisco_cmds\" $host\n" if ($debug);
709 print STDOUT "executing clogin -t $timeo -c\"$cisco_cmds\" $host\n" if ($log);
710 if (defined($ENV{NOPIPE}) && $ENV{NOPIPE} =~ /^YES/i) {
711 system "clogin -t $timeo -c \"$cisco_cmds\" $host </dev/null > $host.raw 2>&1" || die "clogin failed for $host: $!\n";
712 open(INPUT, "< $host.raw") || die "clogin failed for $host: $!\n";
713 } else {
714 open(INPUT,"clogin -t $timeo -c \"$cisco_cmds\" $host </dev/null |") || die "clogin failed for $host: $!\n";
715 }
716}
717
718# determine ACL sorting mode
719if ($ENV{"ACLSORT"} =~ /no/i) {
720 $aclsort = "";
721}
722# determine community string filtering mode
723if (defined($ENV{"NOCOMMSTR"}) &&
724 ($ENV{"NOCOMMSTR"} =~ /yes/i || $ENV{"NOCOMMSTR"} =~ /^$/)) {
725 $filter_commstr = 1;
726} else {
727 $filter_commstr = 0;
728}
729# determine password filtering mode
730if ($ENV{"FILTER_PWDS"} =~ /no/i) {
731 $filter_pwds = 0;
732} elsif ($ENV{"FILTER_PWDS"} =~ /all/i) {
733 $filter_pwds = 2;
734} else {
735 $filter_pwds = 1;
736}
737
738ProcessHistory("","","","!RANCID-CONTENT-TYPE: procket\n!\n");
739ProcessHistory("COMMENTS","keysort","B0","!\n"); # show version
740ProcessHistory("COMMENTS","keysort","C0","!\n"); # show package
741ProcessHistory("COMMENTS","keysort","D0","!\n"); # show hardware
742ProcessHistory("COMMENTS","keysort","E0","!\n"); # show inventory
743ProcessHistory("COMMENTS","keysort","Z0","!\n");
744TOP: while(<INPUT>) {
745 tr/\015//d;
746 if (/\#\s?exit$/) {
747 $clean_run=1;
748 last;
749 }
750 if (/^Error:/) {
751 print STDOUT ("$host clogin error: $_");
752 print STDERR ("$host clogin error: $_") if ($debug);
753 $clean_run=0;
754 last;
755 }
756 while (/#\s*($cmds_regexp)\s*$/) {
757 $cmd = $1;
758 if (!defined($prompt)) {$prompt = ($_ =~ /^([^#]+#)/)[0]; }
759 print STDERR ("HIT COMMAND:$_") if ($debug);
760 if (! defined($commands{$cmd})) {
761 print STDERR "$host: found unexpected command - \"$cmd\"\n";
762 $clean_run = 0;
763 last TOP;
764 }
765 $rval = &{$commands{$cmd}};
766 delete($commands{$cmd});
767 if ($rval == -1) {
768 $clean_run = 0;
769 last TOP;
770 }
771 }
772}
773print STDOUT "Done $logincmd: $_\n" if ($log);
774# Flush History
775ProcessHistory("","","","");
776# Cleanup
777close(INPUT);
778close(OUTPUT);
779
780if (defined($ENV{NOPIPE}) && $ENV{NOPIPE} =~ /^YES/i) {
781 unlink("$host.raw") if (! $debug);
782}
783
784# check for completeness
785if (scalar(%commands) || !$clean_run || !$found_end) {
786 if (scalar(%commands)) {
787 printf(STDOUT "$host: missed cmd(s): %s\n", join(',', keys(%commands)));
788 printf(STDERR "$host: missed cmd(s): %s\n", join(',', keys(%commands))) if ($debug);
789 }
790 if (!$clean_run || !$found_end) {
791 print STDOUT "$host: End of run not found\n";
792 print STDERR "$host: End of run not found\n" if ($debug);
793 system("/usr/bin/tail -1 $host.new");
794 }
795 unlink "$host.new" if (! $debug);
796}
797#! /usr/bin/perl
798##
799## $Id: rancid.in 3123 2015-05-28 22:39:32Z heas $
800##
801## rancid 3.4.1
802## Copyright (c) 1997-2016 by Terrapin Communications, Inc.
803## All rights reserved.
804##
805## This code is derived from software contributed to and maintained by
806## Terrapin Communications, Inc. by Henry Kilmer, John Heasley, Andrew Partan,
807## Pete Whiting, Austin Schutz, and Andrew Fort.
808##
809## Redistribution and use in source and binary forms, with or without
810## modification, are permitted provided that the following conditions
811## are met:
812## 1. Redistributions of source code must retain the above copyright
813## notice, this list of conditions and the following disclaimer.
814## 2. Redistributions in binary form must reproduce the above copyright
815## notice, this list of conditions and the following disclaimer in the
816## documentation and/or other materials provided with the distribution.
817## 3. All advertising materials mentioning features or use of this software
818## must display the following acknowledgement:
819## This product includes software developed by Terrapin Communications,
820## Inc. and its contributors for RANCID.
821## 4. Neither the name of Terrapin Communications, Inc. nor the names of its
822## contributors may be used to endorse or promote products derived from
823## this software without specific prior written permission.
824## 5. It is requested that non-binding fixes and modifications be contributed
825## back to Terrapin Communications, Inc.
826##
827## THIS SOFTWARE IS PROVIDED BY Terrapin Communications, INC. AND CONTRIBUTORS
828## ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
829## TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
830## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COMPANY OR CONTRIBUTORS
831## BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
832## CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
833## SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
834## INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
835## CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
836## ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
837## POSSIBILITY OF SUCH DAMAGE.
838#
839# RANCID - Really Awesome New Cisco confIg Differ
840#
841# rancid - generalized rancid module; command schedule is derived from the
842# rancid.types.{base,conf} configurations.
843#
844# usage: rancid [-dhltCV] -t device_type [-f filename | hostname]
845#
846use 5.010;
847use strict 'vars';
848use warnings;
849no warnings 'uninitialized';
850use Exporter;
851use Getopt::Std;
852our($opt_d, $opt_f, $opt_h, $opt_l, $opt_t, $opt_C, $opt_V);
853getopts('dfhlt:CV');
854BEGIN {
855 push(@INC, "/usr/local/rancid/lib/rancid");
856}
857use rancid;
858our @ISA = qw(Exporter rancid);
859
860sub usage()
861{
862 print STDERR "rancid [-dhltCV] -t device_type [-f filename | hostname]\n";
863 exit 64;
864}
865
866if ($opt_h) {
867 usage();
868}
869
870# basic initialization
871rancidinit();
872
873# load device type spec, build @commandtable and load modules
874if (loadtype($devtype)) {
875 die "Couldn't load device type spec for $rancid::devtype\n";
876}
877if (! defined($lscript)) {
878 die "login script not defined for device type $rancid::devtype\n";
879}
880
881# open the temporary file for the digested output
882open(OUTPUT,">$host.new") || die "Can't open $host.new for writing: $!\n";
883select(OUTPUT);
884if (length($#modules)) {
885 my($module);
886
887 foreach $module (@modules) {
888 (my $file = $module) =~ s/::/\//g;
889 my($err) = 0;
890
891 # call module->init(); we expect 0 as success, as god intended it
892 eval "\$err = ". $module ."::init();";
893 if ($@) {
894 printf(STDERR "loadtype: initializing $module failed: %s\n", $@);
895 exit 1;
896 } elsif ($err) {
897 printf(STDERR "loadtype: %s::init() returned failure\n", $module);
898 exit 1;
899 }
900 }
901}
902
903# open the input, a pre-collected file or start a login for a login stream or
904# temporary file
905if ($file) {
906 print STDERR "opening file $host\n" if ($debug);
907 print STDOUT "opening file $host\n" if ($log);
908 open(INPUT,"<$host") || die "open failed for $host: $!\n";
909} else {
910 print STDERR "executing $lscript -t $timeo -c\"$commandstr\" $host\n" if ($debug);
911 print STDOUT "executing $lscript -t $timeo -c\"$commandstr\" $host\n" if ($log);
912 if (defined($ENV{NOPIPE}) && $ENV{NOPIPE} =~ /^YES/i) {
913 system "$lscript -t $timeo -c \"$commandstr\" $host </dev/null > $host.raw 2>&1" || die "clogin failed for $host: $!\n";
914 open(INPUT, "< $host.raw") || die "clogin failed for $host: $!\n";
915 } else {
916 open(INPUT,"$lscript -t $timeo -c \"$commandstr\" $host </dev/null |") || die "clogin failed for $host: $!\n";
917 }
918}
919
920# loop over the input using the provided input/main loop
921if (!defined($inloop) || length($inloop) < 1) {
922 die "inloop is not configured for device type $devtype";
923}
924eval($inloop ."(*INPUT, *OUTPUT);") && die "${inloop} failed: $@\n";
925
926print STDOUT "Done $lscript: $_\n" if ($log);
927# Flush History
928ProcessHistory("","","","");
929# Cleanup
930close(INPUT);
931close(OUTPUT);
932
933if (defined($ENV{NOPIPE}) && $ENV{NOPIPE} =~ /^YES/i) {
934 unlink("$host.raw") if (! $debug);
935}
936
937# check for completeness
938if (scalar(%commands) || !$clean_run || !$found_end) {
939 if (scalar(keys %commands) eq $commandcnt) {
940 printf(STDERR "$host: missed cmd(s): all commands\n");
941 } elsif (scalar(%commands)) {
942 printf(STDOUT "$host: missed cmd(s): %s\n", join(',', keys(%commands)));
943 printf(STDERR "$host: missed cmd(s): %s\n", join(',', keys(%commands))) if ($debug);
944 }
945 if (!$clean_run || !$found_end) {
946 print STDOUT "$host: End of run not found\n";
947 print STDERR "$host: End of run not found\n" if ($debug);
948 system("/usr/bin/tail -1 $host.new");
949 }
950 unlink "$host.new" if (! $debug);
951}
952#! /bin/sh
953##
954## $Id: rancid-cvs.in 3055 2015-03-12 18:24:07Z heas $
955##
956## rancid 3.4.1
957## Copyright (c) 1997-2016 by Terrapin Communications, Inc.
958## All rights reserved.
959##
960## This code is derived from software contributed to and maintained by
961## Terrapin Communications, Inc. by Henry Kilmer, John Heasley, Andrew Partan,
962## Pete Whiting, Austin Schutz, and Andrew Fort.
963##
964## Redistribution and use in source and binary forms, with or without
965## modification, are permitted provided that the following conditions
966## are met:
967## 1. Redistributions of source code must retain the above copyright
968## notice, this list of conditions and the following disclaimer.
969## 2. Redistributions in binary form must reproduce the above copyright
970## notice, this list of conditions and the following disclaimer in the
971## documentation and/or other materials provided with the distribution.
972## 3. All advertising materials mentioning features or use of this software
973## must display the following acknowledgement:
974## This product includes software developed by Terrapin Communications,
975## Inc. and its contributors for RANCID.
976## 4. Neither the name of Terrapin Communications, Inc. nor the names of its
977## contributors may be used to endorse or promote products derived from
978## this software without specific prior written permission.
979## 5. It is requested that non-binding fixes and modifications be contributed
980## back to Terrapin Communications, Inc.
981##
982## THIS SOFTWARE IS PROVIDED BY Terrapin Communications, INC. AND CONTRIBUTORS
983## ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
984## TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
985## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COMPANY OR CONTRIBUTORS
986## BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
987## CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
988## SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
989## INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
990## CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
991## ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
992## POSSIBILITY OF SUCH DAMAGE.
993#
994# Create all of the misc files & dirs needed for each group and import them
995# into CVS or Subversion.
996#
997# rancid-cvs
998#
999
1000# Read in the environment
1001ENVFILE="/usr/local/rancid/etc/rancid.conf"
1002
1003# print a usage message to stderr
1004pr_usage() {
1005 echo "usage: $0 [-V] [-f config_file] [group [group ...]]" >&2;
1006}
1007
1008# command-line options
1009# -V print version string
1010if [ $# -ge 1 ] ; then
1011 while [ 1 ] ; do
1012 case $1 in
1013 -V)
1014 echo "rancid 3.4.1"
1015 exit 0
1016 ;;
1017 -f)
1018 shift
1019 # next arg is the alternate config file name
1020 ENVFILE="$1"
1021 if [ -z $ENVFILE ]; then
1022 pr_usage
1023 exit 1
1024 fi
1025 shift
1026 ;;
1027 -*)
1028 echo "unknown option: $1" >&2
1029 pr_usage
1030 exit 1
1031 ;;
1032 *)
1033 break;
1034 ;;
1035 esac
1036 done
1037fi
1038
1039. $ENVFILE
1040
1041# Base dir
1042if [ ! -d $BASEDIR ]; then
1043 mkdir -p $BASEDIR ||
1044 (echo "Could not create local state directory: $BASEDIR"; exit 1)
1045fi
1046
1047cd $BASEDIR
1048
1049# RCS system
1050RCSSYS=${RCSSYS:=cvs};
1051if [ $RCSSYS != "cvs" -a $RCSSYS != "svn" -a $RCSSYS != "git" ] ; then
1052 echo "$RCSSYS is not a valid value for RCSSYS. See rancid.conf(5)." >&2
1053 exit 1
1054fi
1055
1056# Top level RCS stuff
1057case $RCSSYS in
1058cvs )
1059 if [ ! -d $CVSROOT ]; then
1060 cvs -d $CVSROOT init
1061 fi
1062 ;;
1063svn )
1064 if echo "$CVSROOT" | grep -q "://"; then
1065 # do nothing because CVSROOT is some sort of a URL
1066 # also assume the repository has already been provisioned
1067 :
1068 else
1069 if ! svn ls "file://$CVSROOT" >/dev/null 2>&1; then
1070 svnadmin create $CVSROOT --fs-type fsfs
1071 fi
1072 CVSROOT="file://$CVSROOT"
1073 fi
1074 ;;
1075git)
1076 if echo "$CVSROOT" | grep -q "://"; then
1077 # do nothing because CVSROOT is some sort of a URL
1078 # also assume the repository has already been provisioned
1079 :
1080 else
1081 if [ ! -d $CVSROOT ]; then
1082 mkdir $CVSROOT
1083 fi
1084 fi
1085 ;;
1086esac
1087
1088# Log dir
1089if [ ! -d logs ]; then
1090 mkdir logs
1091fi
1092
1093# Which groups to do
1094if [ $# -ge 1 ] ; then
1095 LIST_OF_GROUPS="$*"; export LIST_OF_GROUPS
1096elif [ "$LIST_OF_GROUPS" = "" ] ; then
1097 echo "LIST_OF_GROUPS is empty in $ENVFILE"
1098 exit 1
1099fi
1100
1101for GROUP in `echo $LIST_OF_GROUPS` ;
1102do
1103 DIR=$BASEDIR/$GROUP
1104
1105 # Directory for the group and the configs
1106 if [ ! -d $DIR ]; then
1107 mkdir -p $DIR
1108 cd $DIR
1109 case $RCSSYS in
1110 cvs )
1111 cvs import -m "$GROUP" $GROUP new rancid
1112 cd $BASEDIR
1113 cvs checkout $GROUP
1114 ;;
1115 svn )
1116 svn import -m "$GROUP" . $CVSROOT/$GROUP
1117 cd $BASEDIR
1118 svn checkout $CVSROOT/$GROUP $GROUP
1119 cd $DIR
1120 svn update
1121 ;;
1122 git )
1123 git init --bare $CVSROOT/$GROUP
1124 git clone $CVSROOT/$GROUP .
1125 git config --global user.name RANCiD
1126 git config --global user.email $USER$MAILDOMAIN
1127 git config --global push.default current
1128 ;;
1129 esac
1130 fi
1131 cd $DIR
1132 if [ ! -d configs ]; then
1133 rm -f configs
1134 mkdir configs
1135 if [ $RCSSYS = "git" ]; then
1136 cat > configs/.gitignore <<EOF
1137.old
1138*.new
1139*.raw
1140EOF
1141 fi
1142 $RCSSYS add configs
1143 $RCSSYS commit -m 'new' configs
1144 if [ $RCSSYS = "git" ]; then
1145 git push
1146 fi
1147 fi
1148
1149 # config and status files
1150 if [ ! -f routers.all ]; then
1151 touch routers.all
1152 fi
1153 if [ ! -f routers.down ]; then
1154 touch routers.down
1155 fi
1156 if [ ! -f routers.up ]; then
1157 touch routers.up
1158 fi
1159 if [ ! -f router.db ]; then
1160 touch router.db
1161 $RCSSYS add router.db
1162 $RCSSYS commit -m 'new' router.db
1163 if [ $RCSSYS = "git" ]; then
1164 git push
1165 fi
1166 fi
1167done
1168#! /usr/bin/perl
1169##
1170## $Id: rancid-fe.in 3018 2015-01-11 05:51:49Z heas $
1171##
1172## rancid 3.4.1
1173## Copyright (c) 1997-2016 by Terrapin Communications, Inc.
1174## All rights reserved.
1175##
1176## This code is derived from software contributed to and maintained by
1177## Terrapin Communications, Inc. by Henry Kilmer, John Heasley, Andrew Partan,
1178## Pete Whiting, Austin Schutz, and Andrew Fort.
1179##
1180## Redistribution and use in source and binary forms, with or without
1181## modification, are permitted provided that the following conditions
1182## are met:
1183## 1. Redistributions of source code must retain the above copyright
1184## notice, this list of conditions and the following disclaimer.
1185## 2. Redistributions in binary form must reproduce the above copyright
1186## notice, this list of conditions and the following disclaimer in the
1187## documentation and/or other materials provided with the distribution.
1188## 3. All advertising materials mentioning features or use of this software
1189## must display the following acknowledgement:
1190## This product includes software developed by Terrapin Communications,
1191## Inc. and its contributors for RANCID.
1192## 4. Neither the name of Terrapin Communications, Inc. nor the names of its
1193## contributors may be used to endorse or promote products derived from
1194## this software without specific prior written permission.
1195## 5. It is requested that non-binding fixes and modifications be contributed
1196## back to Terrapin Communications, Inc.
1197##
1198## THIS SOFTWARE IS PROVIDED BY Terrapin Communications, INC. AND CONTRIBUTORS
1199## ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
1200## TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
1201## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COMPANY OR CONTRIBUTORS
1202## BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
1203## CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
1204## SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
1205## INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
1206## CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
1207## ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
1208## POSSIBILITY OF SUCH DAMAGE.
1209#
1210# rancid-FE - front-end to rancid/jrancid/etc. for use with par.
1211#
1212# usage: rancid-fe <router>;<vendor>
1213#
1214
1215require 5;
1216
1217my($script);
1218my($router, $devtype) = split('\;', $ARGV[0]);
1219$devtype =~ tr/[A-Z]/[a-z]/;
1220if (! length($devtype)) {
1221 printf(STDERR "unknown router manufacturer for $router: $devtype\n");
1222 exit(-1);
1223}
1224
1225# XXX use rancid::loadtype() to parse these files.
1226foreach $file ("/usr/local/rancid/etc/rancid.types.base",
1227 "/usr/local/rancid/etc/rancid.types.conf") {
1228 open(INPUT, "< $file") || die "Could not open $file: $!";
1229 while (<INPUT>) {
1230 chomp;
1231 my($type, $directive, $value) = split('\;');
1232 $type =~ tr/[A-Z]/[a-z]/;
1233 $directive =~ tr/[A-Z]/[a-z]/;
1234 if ($type eq $devtype && $directive eq "script") {
1235 $script = $value;
1236 close(INPUT);
1237 goto FOUND;
1238 }
1239 }
1240 close(INPUT);
1241}
1242
1243FOUND:
1244if (! defined($script)) {
1245 printf(STDERR "unknown router manufacturer for $router: $devtype\n");
1246 exit(-1);
1247} else {
1248 exec($script . " $router");
1249}
1250
1251printf(STDERR "exec($script) failed router manufacturer $devtype: $!\n");
1252exit(-1);
1253#! /bin/sh
1254##
1255## $Id: rancid-run.in 3232 2016-01-21 17:46:40Z heas $
1256##
1257## rancid 3.4.1
1258## Copyright (c) 1997-2016 by Terrapin Communications, Inc.
1259## All rights reserved.
1260##
1261## This code is derived from software contributed to and maintained by
1262## Terrapin Communications, Inc. by Henry Kilmer, John Heasley, Andrew Partan,
1263## Pete Whiting, Austin Schutz, and Andrew Fort.
1264##
1265## Redistribution and use in source and binary forms, with or without
1266## modification, are permitted provided that the following conditions
1267## are met:
1268## 1. Redistributions of source code must retain the above copyright
1269## notice, this list of conditions and the following disclaimer.
1270## 2. Redistributions in binary form must reproduce the above copyright
1271## notice, this list of conditions and the following disclaimer in the
1272## documentation and/or other materials provided with the distribution.
1273## 3. All advertising materials mentioning features or use of this software
1274## must display the following acknowledgement:
1275## This product includes software developed by Terrapin Communications,
1276## Inc. and its contributors for RANCID.
1277## 4. Neither the name of Terrapin Communications, Inc. nor the names of its
1278## contributors may be used to endorse or promote products derived from
1279## this software without specific prior written permission.
1280## 5. It is requested that non-binding fixes and modifications be contributed
1281## back to Terrapin Communications, Inc.
1282##
1283## THIS SOFTWARE IS PROVIDED BY Terrapin Communications, INC. AND CONTRIBUTORS
1284## ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
1285## TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
1286## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COMPANY OR CONTRIBUTORS
1287## BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
1288## CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
1289## SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
1290## INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
1291## CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
1292## ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
1293## POSSIBILITY OF SUCH DAMAGE.
1294#
1295# Run rancid for each of the rancid groups defined by $LIST_OF_GROUPS in
1296# /usr/local/rancid/etc/rancid.conf or those specified on the command-line.
1297#
1298
1299# Default ENVFILE, overrideable with -f flag.
1300ENVFILE="/usr/local/rancid/etc/rancid.conf"
1301
1302TMPDIR=${TMPDIR:=/tmp}; export TMPDIR
1303
1304# control_rancid argv
1305CR_ARGV=""; export CR_ARGV
1306
1307# print a usage message to stderr
1308pr_usage() {
1309 echo "usage: $0 [-V] [-f config_file] [-r device_name] [-m mail rcpt] [group [group ...]]" >&2;
1310}
1311
1312# command-line options
1313# -V
1314# -f <config file name>
1315# -m <mailto address>
1316# -r <device name>
1317if [ $# -ge 1 ] ; then
1318 while [ 1 ] ; do
1319 case $1 in
1320 -V)
1321 echo "rancid 3.4.1"
1322 exit 0
1323 ;;
1324 -f)
1325 shift
1326 # next arg is the alternate config file name
1327 ENVFILE="$1"
1328 if [ -z $ENVFILE ]; then
1329 pr_usage
1330 exit 1
1331 fi
1332 CR_ARGV="$CR_ARGV -f $1"; export CR_ARGV
1333 shift
1334 ;;
1335 -m)
1336 shift
1337 # next arg is the mailto name
1338 CR_ARGV="$CR_ARGV -m $1"; export CR_ARGV
1339 shift
1340 ;;
1341 -r)
1342 shift
1343 # next arg is the device name
1344 CR_ARGV="$CR_ARGV -r $1"; export CR_ARGV
1345 shift
1346 ;;
1347 --)
1348 shift; break;
1349 ;;
1350 -h)
1351 pr_usage
1352 exit
1353 ;;
1354 -*)
1355 echo "unknown option: $1" >&2
1356 pr_usage
1357 exit 1
1358 ;;
1359 *)
1360 break;
1361 ;;
1362 esac
1363 done
1364fi
1365
1366. $ENVFILE
1367
1368# SENDMAIL location
1369SENDMAIL=${SENDMAIL:=sendmail};
1370
1371if [ $# -ge 1 ] ; then
1372 LIST_OF_GROUPS="$*"; export LIST_OF_GROUPS
1373elif [ "$LIST_OF_GROUPS" = "" ] ; then
1374 echo "LIST_OF_GROUPS is empty in $ENVFILE"
1375 exit 1
1376fi
1377
1378if [ ! -d $LOGDIR ] ; then
1379 mkdir $LOGDIR || (echo "Could not create log directory: $LOGDIR"; exit 1)
1380fi
1381
1382for GROUP in $LIST_OF_GROUPS
1383do
1384
1385 LOCKFILE=$TMPDIR/.$GROUP.run.lock
1386
1387 (
1388 echo starting: `date`
1389 echo
1390
1391 if [ -f $LOCKFILE ]
1392 then
1393 echo hourly config diffs failed: $LOCKFILE exists
1394 ls -l $LOCKFILE
1395
1396 # Send email if the lock file is old.
1397 if [ "X$LOCKTIME" = "X" ] ; then
1398 LOCKTIME=4
1399 fi
1400 GRPOLDFILE=`mktemp -q $TMPDIR/.$GROUP.XXXXXX`
1401 if [ $? -ne 0 ] ; then
1402 echo "Could not create temporary file for error email" >&2
1403 exit 1
1404 fi
1405 perl -e "\$t = (stat(\"$LOCKFILE\"))[9]; print \"OLD\\n\" if (time() - \$t >= $LOCKTIME*60*60);" > $GRPOLDFILE
1406 if [ -s $TMPDIR/.$GROUP.old ]
1407 then
1408 (
1409 echo "To: rancid-admin-${GROUP}${MAILDOMAIN}"
1410 echo "Subject: rancid hung - $GROUP"
1411 echo "Precedence: bulk"
1412 echo "Auto-submitted: auto-generated"
1413 echo "X-Auto-Response-Suppress: All"
1414 echo ""
1415
1416 cat <<END
1417rancid $GROUP hung on `hostname`? Old lockfile still exists:
1418`ls -l $LOCKFILE`
1419END
1420 ) | $SENDMAIL -t $MAILOPTS
1421 fi
1422 rm -f $GRPOLDFILE
1423 exit 1
1424 else
1425 trap 'rm -fr $LOCKFILE;exit 1' 1 2 3 6 10 15
1426 perl -e 'use POSIX;sysopen(FH, $ARGV[1], O_RDWR|O_CREAT|O_EXCL, 0660) or exit 1;print FH "$ARGV[0]\n";' -- $$ $LOCKFILE
1427 if [ $? -eq 0 ] ; then
1428 control_rancid $CR_ARGV $GROUP
1429 trap '' 1 2 3 6 10 15
1430 rm -f $LOCKFILE
1431 fi
1432 trap '' 1 2 3 6 10 15
1433 fi
1434
1435 echo
1436 echo ending: `date`
1437 ) >$LOGDIR/$GROUP.`date +%Y%m%d.%H%M%S` 2>&1
1438done
1439#! /usr/bin/expect --
1440##
1441## $Id: rblogin.in 3201 2015-11-05 00:48:01Z heas $
1442##
1443## rancid 3.4.1
1444## Copyright (c) 1997-2016 by Terrapin Communications, Inc.
1445## All rights reserved.
1446##
1447## This code is derived from software contributed to and maintained by
1448## Terrapin Communications, Inc. by Henry Kilmer, John Heasley, Andrew Partan,
1449## Pete Whiting, Austin Schutz, and Andrew Fort.
1450##
1451## Redistribution and use in source and binary forms, with or without
1452## modification, are permitted provided that the following conditions
1453## are met:
1454## 1. Redistributions of source code must retain the above copyright
1455## notice, this list of conditions and the following disclaimer.
1456## 2. Redistributions in binary form must reproduce the above copyright
1457## notice, this list of conditions and the following disclaimer in the
1458## documentation and/or other materials provided with the distribution.
1459## 3. All advertising materials mentioning features or use of this software
1460## must display the following acknowledgement:
1461## This product includes software developed by Terrapin Communications,
1462## Inc. and its contributors for RANCID.
1463## 4. Neither the name of Terrapin Communications, Inc. nor the names of its
1464## contributors may be used to endorse or promote products derived from
1465## this software without specific prior written permission.
1466## 5. It is requested that non-binding fixes and modifications be contributed
1467## back to Terrapin Communications, Inc.
1468##
1469## THIS SOFTWARE IS PROVIDED BY Terrapin Communications, INC. AND CONTRIBUTORS
1470## ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
1471## TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
1472## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COMPANY OR CONTRIBUTORS
1473## BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
1474## CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
1475## SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
1476## INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
1477## CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
1478## ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
1479## POSSIBILITY OF SUCH DAMAGE.
1480##
1481## It is the request of the authors, but not a condition of license, that
1482## parties packaging or redistributing RANCID NOT distribute altered versions
1483## of the etc/rancid.types.base file nor alter how this file is processed nor
1484## when in relation to etc/rancid.types.conf. The goal of this is to help
1485## suppress our support costs. If it becomes a problem, this could become a
1486## condition of license.
1487#
1488# The expect login scripts were based on Erik Sherk's gwtn, by permission.
1489#
1490# The original looking glass software was written by Ed Kern, provided by
1491# permission and modified beyond recognition.
1492#
1493# rblogin - Riverbed Steelhead login; this is starting from clogin r3115, with
1494# a few changes for UI bugs.
1495#
1496# Most options are intuitive for logging into a Cisco router.
1497# The default is to enable (thus -noenable). Some folks have
1498# setup tacacs to have a user login at priv-lvl = 15 (enabled)
1499# so the -autoenable flag was added for this case (don't go through
1500# the process of enabling and the prompt will be the "#" prompt.
1501# The default username password is the same as the vty password.
1502#
1503
1504# Sometimes routers take awhile to answer (the default is 10 sec)
1505set timeoutdflt 45
1506# Some CLIs having problems if we write too fast (Extreme, PIX, Cat)
1507set send_human {.2 .1 .4 .2 1}
1508
1509# env(CLOGIN) may contain:
1510# x == do not set xterm banner or name
1511
1512# Find the user in the ENV, or use the unix userid.
1513if {[info exists env(CISCO_USER)]} {
1514 set default_user $env(CISCO_USER)
1515} elseif {[info exists env(USER)]} {
1516 set default_user $env(USER)
1517} elseif {[info exists env(LOGNAME)]} {
1518 set default_user $env(LOGNAME)
1519} else {
1520 # This uses "id" which I think is portable. At least it has existed
1521 # (without options) on all machines/OSes I've been on recently -
1522 # unlike whoami or id -nu.
1523 if [catch {exec id} reason] {
1524 send_error "\nError: could not exec id: $reason\n"
1525 exit 1
1526 }
1527 regexp {\(([^)]*)} "$reason" junk default_user
1528}
1529if {[info exists env(CLOGINRC)]} {
1530 set password_file $env(CLOGINRC)
1531}
1532
1533# Usage line
1534set usage "Usage: $argv0 \[-dhSV\] \[-m|M\] \[-autoenable\] \[-noenable\] \
1535\[-c command\] \[-Evar=x\] \[-e enable-password\] \[-f cloginrc-file\] \
1536\[-p user-password\] \[-r passphrase\] \[-s script-file\] \[-t timeout\] \
1537\[-u username\] \[-v vty-password\] \[-w enable-username\] \[-x command-file\] \
1538\[-y ssh_cypher_type\] router \[router...\]\n"
1539
1540# Password file
1541set password_file $env(HOME)/.cloginrc
1542# Default is to login to the router
1543set do_command 0
1544set do_script 0
1545# The default is to automatically enable
1546set avenable 1
1547# The default is that you login non-enabled (tacacs can have you login already
1548# enabled)
1549set avautoenable 0
1550# The default is to look in the password file to find the passwords. This
1551# tracks if we receive them on the command line.
1552set do_passwd 1
1553set do_enapasswd 1
1554# Save config, if prompted
1555set do_saveconfig 0
1556# cloginrc debugging knob
1557set do_cloginrcdbg 0
1558# intialize cloginrc parsing stacks
1559set int_file {}
1560set int_lineno {}
1561
1562# Process the command line
1563for {set i 0} {$i < $argc} {incr i} {
1564 set arg [lindex $argv $i]
1565
1566 switch -glob -- $arg {
1567 # Expect debug mode
1568 -d* {
1569 exp_internal 1
1570 # Help
1571 } -h* {
1572 send_user "$usage"
1573 exit 0
1574 # Command to run.
1575 } -c* {
1576 if {! [regexp .\[cC\](.+) $arg ignore command]} {
1577 incr i
1578 set command [lindex $argv $i]
1579 }
1580 set do_command 1
1581 # Environment variable to pass to -s scripts
1582 } -E* {
1583 if {[regexp .\[E\](.+)=(.+) $arg ignore varname varvalue]} {
1584 set E$varname $varvalue
1585 } else {
1586 send_user "\nError: invalid format for -E in $arg\n"
1587 exit 1
1588 }
1589 # alternate cloginrc file
1590 } -f* {
1591 if {! [regexp .\[fF\](.+) $arg ignore password_file]} {
1592 incr i
1593 set password_file [lindex $argv $i]
1594 }
1595 # VTY Password
1596 } -p* {
1597 if {! [regexp .\[pP\](.+) $arg ignore userpasswd]} {
1598 incr i
1599 set userpasswd [lindex $argv $i]
1600 }
1601 set do_passwd 0
1602 # cloginrc debugging knobs
1603 } -m* {
1604 set do_cloginrcdbg 1
1605 } -M* {
1606 set do_cloginrcdbg 2
1607 # ssh passphrase
1608 } -r* {
1609 if {! [regexp .\[rR\](.+) $arg ignore passphrase]} {
1610 incr i
1611 set avpassphrase [lindex $argv $i]
1612 }
1613 # Expect script to run.
1614 } -s* {
1615 if {! [regexp .\[sS\](.+) $arg ignore sfile]} {
1616 incr i
1617 set sfile [lindex $argv $i]
1618 }
1619 if { ! [file readable $sfile] } {
1620 send_user "\nError: Can't read $sfile\n"
1621 exit 1
1622 }
1623 set do_script 1
1624 # save config on exit
1625 } -S* {
1626 # may not be supported by the script and may not be applicable to
1627 # the platform
1628 set do_saveconfig 1
1629 # Timeout
1630 } -t* {
1631 if {! [regexp .\[tT\](.+) $arg ignore timeout]} {
1632 incr i
1633 set timeoutdflt [lindex $argv $i]
1634 }
1635 # Username
1636 } -u* {
1637 if {! [regexp .\[uU\](.+) $arg ignore user]} {
1638 incr i
1639 set username [lindex $argv $i]
1640 }
1641 # VTY Password
1642 } -v* {
1643 # some scripts ignore -v, like jlogin
1644 if {! [regexp .\[vV\](.+) $arg ignore passwd]} {
1645 incr i
1646 set passwd [lindex $argv $i]
1647 }
1648 set do_passwd 0
1649 # Version string
1650 } -V* {
1651 send_user "rancid 3.4.1\n"
1652 exit 0
1653 # Enable Username
1654 } -w* {
1655 if {! [regexp .\[wW\](.+) $arg ignore enauser]} {
1656 incr i
1657 set enausername [lindex $argv $i]
1658 }
1659 # Enable Password
1660 } -e* {
1661 if {! [regexp .\[e\](.+) $arg ignore enapasswd]} {
1662 incr i
1663 set enapasswd [lindex $argv $i]
1664 }
1665 set do_enapasswd 0
1666 # 'ssh -c' cypher type
1667 } -y* {
1668 if {! [regexp .\[eE\](.+) $arg ignore cypher]} {
1669 incr i
1670 set cypher [lindex $argv $i]
1671 }
1672 # Command file
1673 } -x* {
1674 if {! [regexp .\[xX\](.+) $arg ignore cmd_file]} {
1675 incr i
1676 set cmd_file [lindex $argv $i]
1677 }
1678 if [catch {set cmd_fd [open $cmd_file r]} reason] {
1679 send_user "\nError: $reason\n"
1680 exit 1
1681 }
1682 set cmd_text [read $cmd_fd]
1683 close $cmd_fd
1684 set command [join [split $cmd_text \n] \;]
1685 set do_command 1
1686 # Do we enable?
1687 } -noenable {
1688 set avenable 0
1689 # Does tacacs automatically enable us?
1690 } -autoenable {
1691 set avautoenable 1
1692 set avenable 0
1693 } -* {
1694 send_user "\nError: Unknown argument! $arg\n"
1695 send_user $usage
1696 exit 1
1697 } default {
1698 break
1699 }
1700 }
1701}
1702# Process routers...no routers listed is an error.
1703if { $i == $argc } {
1704 send_user "\nError: $usage"
1705}
1706
1707# Only be quiet if we are running a script (it can log its output
1708# on its own)
1709if { $do_script } {
1710 log_user 0
1711} else {
1712 log_user 1
1713}
1714
1715#
1716# Done configuration/variable setting. Now run with it...
1717#
1718
1719# Sets Xterm title if interactive...if its an xterm and the user cares
1720proc label { host } {
1721 global env
1722 # if CLOGIN has an 'x' in it, don't set the xterm name/banner
1723 if [info exists env(CLOGIN)] {
1724 if {[string first "x" $env(CLOGIN)] != -1} { return }
1725 }
1726 # take host from ENV(TERM)
1727 if [info exists env(TERM)] {
1728 if [regexp \^(xterm|vs) $env(TERM) ignore] {
1729 send_user "\033]1;[lindex [split $host "."] 0]\a"
1730 send_user "\033]2;$host\a"
1731 }
1732 }
1733}
1734
1735# This is a helper function to make the password file easier to
1736# maintain. Using this the password file has the form:
1737# add password sl* pete cow
1738# add password at* steve
1739# add password * hanky-pie
1740proc add {var args} {
1741 global int_file int_lineno int_$var
1742 set file [lindex $int_file 0]
1743 set lineno [lindex $int_lineno 0]
1744 lappend int_$var "$var:$file:$lineno: $args"
1745}
1746proc include {args} {
1747 global env
1748 regsub -all "(^{|}$)" $args {} args
1749 if { [regexp "^/" $args ignore] == 0 } {
1750 set args $env(HOME)/$args
1751 }
1752 source_password_file $args
1753}
1754
1755proc find {var router} {
1756 global do_cloginrcdbg
1757 upvar int_$var list
1758 if { [info exists list] } {
1759 foreach line $list {
1760 if { [string match -nocase [lindex $line 1] $router] } {
1761 if { $do_cloginrcdbg > 0 } {
1762 send_error -- [join [list [lindex $line 0] [lrange $line 1 end] "\r\n"]]
1763 }
1764 if { $do_cloginrcdbg == 2 } {
1765 # save return value
1766 if {! [info exists result]} {
1767 set result [lrange $line 2 end]
1768 }
1769 } else {
1770 return [lrange $line 2 end]
1771 }
1772 }
1773 }
1774 }
1775
1776 if { $do_cloginrcdbg == 2 } {
1777 if {[info exists result]} {
1778 return $result
1779 }
1780 }
1781 return {}
1782}
1783
1784# Loads the password file. Note that as this file is tcl, and that
1785# it is sourced, the user better know what to put in there, as it
1786# could install more than just password info... I will assume however,
1787# that a "bad guy" could just as easy put such code in the clogin
1788# script, so I will leave .cloginrc as just an extention of that script
1789proc source_password_file { file } {
1790 global env int_file int_lineno
1791 if { ! [file exists $file] } {
1792 send_user "\nError: password file ($file) does not exist\n"
1793 exit 1
1794 }
1795 file stat $file fileinfo
1796 if { [expr ($fileinfo(mode) & 007)] != 0000 } {
1797 send_user "\nError: $file must not be world readable/writable\n"
1798 exit 1
1799 }
1800 if [catch {set fd [open $file "r"]} reason] {
1801 send_user "\nError: $reason\n"
1802 exit 1
1803 }
1804 set int_file [linsert $int_file 0 $file]
1805 set int_lineno [linsert $int_lineno 0 0]
1806 while { [gets $fd line] >= 0 } {
1807 set tmp [lindex $int_lineno 0]; incr tmp
1808 lset int_lineno 0 $tmp
1809 eval $line
1810 }
1811 set int_file [lrange $int_file 1 end]
1812 set int_lineno [lrange $int_lineno 1 end]
1813 close $fd
1814}
1815
1816# Log into the router.
1817# returns: 0 on success, 1 on failure, -1 if rsh was used successfully
1818proc login { router user userpswd passwd enapasswd cmethod cyphertype identfile } {
1819 global command spawn_id in_proc do_command do_script platform passphrase
1820 global prompt prompt_match u_prompt p_prompt e_prompt sshcmd
1821 set in_proc 1
1822 set uprompt_seen 0
1823
1824 # try each of the connection methods in $cmethod until one is successful
1825 set progs [llength $cmethod]
1826 foreach prog [lrange $cmethod 0 end] {
1827 incr progs -1
1828 if [string match "telnet*" $prog] {
1829 regexp {telnet(:([^[:space:]]+))*} $prog methcmd suffix port
1830 if {"$port" == ""} {
1831 set retval [catch {spawn telnet $router} reason]
1832 } else {
1833 set retval [catch {spawn telnet $router $port} reason]
1834 }
1835 if { $retval } {
1836 send_user "\nError: telnet failed: $reason\n"
1837 return 1
1838 }
1839 } elseif [string match "ssh*" $prog] {
1840 # ssh to the router & try to login with or without an identfile.
1841 regexp {ssh(:([^[:space:]]+))*} $prog methcmd suffix port
1842 set cmd $sshcmd
1843 if {"$port" != ""} {
1844 set cmd "$cmd -p $port"
1845 }
1846 if {"$identfile" != ""} {
1847 set cmd "$cmd -i $identfile"
1848 }
1849 set retval [catch {eval spawn [split "$cmd -c $cyphertype -x -l $user $router" { }]} reason]
1850 if { $retval } {
1851 send_user "\nError: $cmd failed: $reason\n"
1852 return 1
1853 }
1854 } elseif ![string compare $prog "rsh"] {
1855 if { ! $do_command } {
1856 if { [llength $cmethod] == 1 } {
1857 send_user "\nError: rsh is an invalid method for -x and "
1858 send_user "interactive logins\n"
1859 }
1860 if { $progs == 0 } {
1861 return 1
1862 }
1863 continue;
1864 }
1865
1866 # handle escaped ;s in commands, and ;; and ^;
1867 regsub -all {([^\\]);;} $command "\\1;\u002;" esccommand
1868 regsub {^;} $esccommand "\u002;" command
1869 set sep "\\1\u001"
1870 regsub -all {([^\\])\;} $command "$sep" esccommand
1871 set sep "\u001"
1872 set commands [split $esccommand $sep]
1873 set num_commands [llength $commands]
1874 set rshfail 0
1875 for {set i 0} {$i < $num_commands && !$rshfail} { incr i} {
1876 log_user 0
1877 set retval [catch {spawn rsh $user@$router [lindex $commands $i] } reason]
1878 if { $retval } {
1879 send_user "\nError: rsh failed: $reason\n"
1880 log_user 1; return 1
1881 }
1882 send_user "$router# [lindex $commands $i]\n"
1883
1884 # rcmd does not get a pager and no prompts, so we just have to
1885 # look for failures & lines.
1886 expect {
1887 "Connection refused" { catch {close}; catch {wait};
1888 send_user "\nError: Connection\
1889 Refused ($prog): $router\n"
1890 set rshfail 1
1891 }
1892 -re "(Connection closed by|Connection to \[^\n\r]+ closed)" {
1893 catch {close}; catch {wait};
1894 send_user "\nError: Connection\
1895 closed ($prog): $router\n"
1896 set rshfail 1
1897 }
1898 "Host is unreachable" { catch {close}; catch {wait};
1899 send_user "\nError: Host Unreachable:\
1900 $router\n"
1901 set rshfail 1
1902 }
1903 "No address associated with" {
1904 catch {close}; catch {wait};
1905 send_user "\nError: Unknown host\
1906 $router\n"
1907 set rshfail 1
1908 }
1909 -re "\b+" { exp_continue }
1910 -re "\[\n\r]+" { send_user -- "$expect_out(buffer)"
1911 exp_continue
1912 }
1913 timeout { catch {close}; catch {wait};
1914 send_user "\nError: TIMEOUT reached\n"
1915 set rshfail 1
1916 }
1917 eof { catch {close}; catch {wait}; }
1918 }
1919 log_user 1
1920 }
1921 if { $rshfail } {
1922 if { !$progs } {
1923 return 1
1924 } else {
1925 continue
1926 }
1927 }
1928 # fake the end of the session for rancid.
1929 send_user "$router# exit\n"
1930 # return rsh "success"
1931 return -1
1932 } else {
1933 send_user "\nError: unknown connection method: $prog\n"
1934 return 1
1935 }
1936 sleep 0.3
1937
1938 # This helps cleanup each expect clause.
1939 expect_after {
1940 timeout {
1941 send_user "\nError: TIMEOUT reached\n"
1942 catch {close}; catch {wait};
1943 if { $in_proc} {
1944 return 1
1945 } else {
1946 continue
1947 }
1948 } eof {
1949 send_user "\nError: EOF received\n"
1950 catch {close}; catch {wait};
1951 if { $in_proc} {
1952 return 1
1953 } else {
1954 continue
1955 }
1956 }
1957 }
1958
1959 # Here we get a little tricky. There are several possibilities:
1960 # the router can ask for a username and passwd and then
1961 # talk to the TACACS server to authenticate you, or if the
1962 # TACACS server is not working, then it will use the enable
1963 # passwd. Or, the router might not have TACACS turned on,
1964 # then it will just send the passwd.
1965 # if telnet fails with connection refused, try ssh
1966 expect {
1967 -re "^<-+ More -+>\[^\n\r]*" {
1968 # ASA will use the pager for long banners
1969 send " ";
1970 exp_continue
1971 }
1972 -re "(Connection refused|Secure connection \[^\n\r]+ refused)" {
1973 catch {close}; catch {wait};
1974 if !$progs {
1975 send_user "\nError: Connection Refused ($prog): $router\n"
1976 return 1
1977 }
1978 }
1979 -re "(Connection closed by|Connection to \[^\n\r]+ closed)" {
1980 catch {close}; catch {wait};
1981 if !$progs {
1982 send_user "\nError: Connection closed ($prog): $router\n"
1983 return 1
1984 }
1985 }
1986 eof { send_user "\nError: Couldn't login: $router\n"; wait; return 1 }
1987 -nocase "unknown host\r" {
1988 send_user "\nError: Unknown host $router\n";
1989 catch {close}; catch {wait};
1990 return 1
1991 }
1992 "Host is unreachable" {
1993 send_user "\nError: Host Unreachable: $router\n";
1994 catch {close}; catch {wait};
1995 return 1
1996 }
1997 "No address associated with name" {
1998 send_user "\nError: Unknown host $router\n";
1999 catch {close}; catch {wait};
2000 return 1
2001 }
2002 -re "(Host key not found |The authenticity of host .* be established).* \\(yes/no\\)\\?" {
2003 send "yes\r"
2004 send_user "\nHost $router added to the list of known hosts.\n"
2005 exp_continue
2006 }
2007 -re "HOST IDENTIFICATION HAS CHANGED.* \\(yes/no\\)\\?" {
2008 send "no\r"
2009 send_user "\nError: The host key for $router has changed. Update the SSH known_hosts file accordingly.\n"
2010 catch {close}; catch {wait};
2011 return 1
2012 }
2013 -re "HOST IDENTIFICATION HAS CHANGED\[^\n\r]+" {
2014 send_user "\nError: The host key for $router has changed. Update the SSH known_hosts file accordingly.\n"
2015 return 1
2016 }
2017 -re "Offending key for .* \\(yes/no\\)\\?" {
2018 send "no\r"
2019 send_user "\nError: host key mismatch for $router. Update the SSH known_hosts file accordingly.\n"
2020 catch {close}; catch {wait};
2021 return 1
2022 }
2023 -nocase -re "^warning: remote host denied authentication agent forwarding." {
2024 exp_continue;
2025 }
2026 -re "(denied|Sorry)" {
2027 send_user "\nError: Check your passwd for $router\n"
2028 catch {close}; catch {wait}; return 1
2029 }
2030 "Login failed" {
2031 send_user "\nError: Check your passwd for $router\n"
2032 catch {close}; catch {wait}; return 1
2033 }
2034 -re "% (Bad passwords|Authentication failed)" {
2035 send_user "\nError: Check your passwd for $router\n"
2036 catch {close}; catch {wait}; return 1
2037 }
2038 "Press any key to continue" {
2039 # send_user "Pressing the ANY key\n"
2040 send "\r"
2041 exp_continue
2042 }
2043 -re "Enter Selection: " {
2044 # Catalyst 1900s have some lame menu. Enter
2045 # K to reach a command-line.
2046 send "K\r"
2047 exp_continue
2048 }
2049 -re "Last login:" {
2050 exp_continue
2051 }
2052 -re "Press the <tab> key \[^\r\n]+\[\r\n]+" {
2053 exp_continue
2054 }
2055 -re "@\[^\r\n]+ $p_prompt" {
2056 # ssh pwd prompt
2057 sleep 1
2058 send -- "$userpswd\r"
2059 exp_continue
2060 }
2061 -re "Enter passphrase.*: " {
2062 # sleep briefly to allow time for stty -echo
2063 sleep .3
2064 send -- "$passphrase\r"
2065 exp_continue
2066 }
2067 -re "$u_prompt" {
2068 send -- "$user\r"
2069 set uprompt_seen 1
2070 exp_continue
2071 }
2072 -re "$p_prompt" {
2073 sleep 1
2074 if {$uprompt_seen == 1} {
2075 send -- "$userpswd\r"
2076 } else {
2077 send -- "$passwd\r"
2078 }
2079 exp_continue
2080 }
2081 -re "$prompt" {
2082 set prompt_match $expect_out(0,string);
2083 break;
2084 }
2085 "Login invalid" {
2086 send_user "\nError: Invalid login: $router\n";
2087 catch {close}; catch {wait}; return 1
2088 }
2089 -re "\[^\r\n]*\[\r\n]+" { exp_continue; }
2090 }
2091 }
2092
2093 set in_proc 0
2094 return 0
2095}
2096
2097# Enable
2098proc do_enable { enauser enapasswd } {
2099 global do_saveconfig in_proc
2100 global prompt u_prompt e_prompt enacmd
2101 set in_proc 1
2102
2103 send "$enacmd\r"
2104 expect {
2105 -re "$u_prompt" { send -- "$enauser\r"; exp_continue}
2106 -re "$e_prompt" { send -- "$enapasswd\r"; exp_continue}
2107 "#" { set prompt "#" }
2108 "(enable)" { set prompt "> \\(enable\\) " }
2109 "% Invalid input" {
2110 send_user "\nError: Unrecognized command, check your enable command\n";
2111 return 1
2112 }
2113 -re "(denied|Sorry|Incorrect)" {
2114 # % Access denied - from local auth and poss. others
2115 send_user "\nError: Check your Enable passwd\n";
2116 return 1
2117 }
2118 "% Error in authentication" {
2119 send_user "\nError: Check your Enable passwd\n"
2120 return 1
2121 }
2122 "% Bad passwords" {
2123 send_user "\nError: Check your Enable passwd\n"
2124 return 1
2125 }
2126 }
2127 # We set the prompt variable (above) so script files don't need
2128 # to know what it is.
2129 set in_proc 0
2130 return 0
2131}
2132
2133# Run commands given on the command line.
2134proc run_commands { prompt command } {
2135 global do_saveconfig in_proc platform
2136 set in_proc 1
2137
2138 if { [string compare "extreme" "$platform"] } {
2139 # match cisco config mode prompts too, such as router(config-if)#,
2140 # but catalyst does not change in this fashion.
2141 regsub -lineanchor -- {^(.{1,11}).*([#>])$} $prompt {\1} reprompt
2142 regsub -all -- {[\\]$} $reprompt {} reprompt
2143 append reprompt {([^#>\r\n]+)?[#>](\\([^)\\r\\n]+\\))?}
2144 } else {
2145 set reprompt $prompt
2146 }
2147
2148 # this is the only way i see to get rid of more prompts in o/p..grrrrr
2149 log_user 0
2150
2151 # handle escaped ;s in commands, and ;; and ^;
2152 regsub -all {([^\\]);;} $command "\\1;\u002;" esccommand
2153 regsub {^;} $esccommand "\u002;" command
2154 set sep "\\1\u001"
2155 regsub -all {([^\\]);} $command "$sep" esccommand
2156 set sep "\u001"
2157 set commands [split $esccommand $sep]
2158 set num_commands [llength $commands]
2159 # the pager can not be turned off on the PIX, so we have to look
2160 # for the "More" prompt. the extreme is equally obnoxious in pre-12.3 XOS,
2161 # with a global switch in the config.
2162 for {set i 0} {$i < $num_commands} { incr i} {
2163 if { [lindex $commands $i] == "\u002" } {
2164 send -- "\r"
2165 } else {
2166 send -- "[subst -nocommands [lindex $commands $i]]\r"
2167 }
2168 expect {
2169 -re "\b+" { exp_continue }
2170 -re "^<<Service needs \[^>]+>>" {
2171 # for whatever reason, the riverbed prints
2172 # this warning in show peers o/p, but doesnt
2173 # follow it with a CR, so the following
2174 # prompt is not at the BOL, confusing the
2175 # parsing script. so, filter it. XXX
2176 exp_continue;
2177 }
2178 -re "^\[^\n\r *]*$reprompt" { send_user -- "$expect_out(buffer)"
2179 }
2180 -re "^\[^\n\r]*$reprompt." { send_user -- "$expect_out(buffer)"
2181 exp_continue
2182 }
2183 -re "^--More--\[\r\n]+" { # specific match c1900 pager
2184 send " "
2185 exp_continue
2186 }
2187 -re "\[^\r\n]*\[\n\r]+" { send_user -- "$expect_out(buffer)"
2188 exp_continue
2189 }
2190 -re "\[^\r\n]*Press <SPACE> to cont\[^\r\n]*" {
2191 send " "
2192 # bloody ^[[2K after " "
2193 expect {
2194 -re "^\[^\r\n]*\r" {}
2195 }
2196 exp_continue
2197 }
2198 -re "^ *--More--\[^\n\r]*" {
2199 send " "
2200 exp_continue }
2201 -re "^<-+ More -+>\[^\n\r]*" {
2202 send_user -- "$expect_out(buffer)"
2203 send " "
2204 exp_continue }
2205 }
2206 }
2207 log_user 1
2208
2209 if { [string compare "extreme" "$platform"] } {
2210 send -h "exit\r"
2211 } else {
2212 send -h "quit\r"
2213 }
2214 expect {
2215 -re "^\[^\n\r *]*$reprompt" {
2216 # the Cisco CE and Jnx ERX
2217 # return to non-enabled mode
2218 # on exit in enabled mode.
2219 send -h "exit\r"
2220 exp_continue;
2221 }
2222 "The system has unsaved changes" { # Force10 SFTOS
2223 if {$do_saveconfig} {
2224 catch {send "y\r"}
2225 } else {
2226 catch {send "n\r"}
2227 }
2228 exp_continue
2229 }
2230 "Would you like to save them now" { # Force10
2231 if {$do_saveconfig} {
2232 catch {send "y\r"}
2233 } else {
2234 catch {send "n\r"}
2235 }
2236 exp_continue
2237 }
2238 -re "(Profile|Configuration) changes have occurred.*" {
2239 # Cisco CSS
2240 if {$do_saveconfig} {
2241 catch {send "y\r"}
2242 } else {
2243 catch {send "n\r"}
2244 }
2245 exp_continue
2246 }
2247 "Do you wish to save your configuration changes" {
2248 if {$do_saveconfig} {
2249 catch {send "y\r"}
2250 } else {
2251 catch {send "n\r"}
2252 }
2253 exp_continue
2254 }
2255 -re "\[\n\r]+" { exp_continue }
2256 timeout { catch {close}; catch {wait};
2257 return 1
2258 }
2259 eof { return 0 }
2260 }
2261 set in_proc 0
2262}
2263
2264#
2265# For each router... (this is main loop)
2266#
2267source_password_file $password_file
2268set in_proc 0
2269set exitval 0
2270set prompt_match ""
2271foreach router [lrange $argv $i end] {
2272 set router [string tolower $router]
2273 # attempt at platform switching.
2274 set platform ""
2275 send_user -- "$router\n"
2276
2277 # device timeout
2278 set timeout [find timeout $router]
2279 if { [llength $timeout] == 0 } {
2280 set timeout $timeoutdflt
2281 }
2282
2283 # Default prompt.
2284 set prompt [join [find prompt $router] ""]
2285 if { [llength $prompt] == 0 } {
2286 set prompt "(>|#| \\(enable\\))"
2287 }
2288
2289 # look for autoenable option in .cloginrc & cmd-line
2290 set ae [find autoenable $router]
2291 if { "$ae" == "1" || $avautoenable } {
2292 set autoenable 1
2293 } else {
2294 set autoenable 0
2295 }
2296 # look for enable options in .cloginrc & cmd-line
2297 if { $avenable == 0 } {
2298 set enable 0
2299 } else {
2300 set ne [find noenable $router]
2301 if { "$ne" == "1" || "$autoenable" == "1" } {
2302 set enable 0
2303 } else {
2304 set enable 1
2305 }
2306 }
2307
2308 # Figure out passwords
2309 if { $do_passwd || $do_enapasswd } {
2310 set pswd [find password $router]
2311 if { [llength $pswd] == 0 } {
2312 send_user -- "\nError: no password for $router in $password_file.\n"
2313 continue
2314 }
2315 if { $enable && $do_enapasswd && $autoenable == 0 && [llength $pswd] < 2 } {
2316 send_user -- "\nError: no enable password for $router in $password_file.\n"
2317 continue
2318 }
2319 set passwd [join [lindex $pswd 0] ""]
2320 set enapasswd [join [lindex $pswd 1] ""]
2321 } else {
2322 set passwd $userpasswd
2323 set enapasswd $enapasswd
2324 }
2325
2326 # Figure out username
2327 if {[info exists username]} {
2328 # command line username
2329 set ruser $username
2330 } else {
2331 set ruser [join [find user $router] ""]
2332 if { "$ruser" == "" } { set ruser $default_user }
2333 }
2334
2335 # Figure out username's password (if different from the vty password)
2336 if {[info exists userpasswd]} {
2337 # command line username
2338 set userpswd $userpasswd
2339 } else {
2340 set userpswd [join [find userpassword $router] ""]
2341 if { "$userpswd" == "" } { set userpswd $passwd }
2342 }
2343
2344 # Figure out enable username
2345 if {[info exists enausername]} {
2346 # command line enausername
2347 set enauser $enausername
2348 } else {
2349 set enauser [join [find enauser $router] ""]
2350 if { "$enauser" == "" } { set enauser $ruser }
2351 }
2352
2353 # Figure out enable command
2354 set enacmd [join [find enablecmd $router] ""]
2355 if { "$enacmd" == "" } { set enacmd "enable" }
2356
2357 # Figure out prompts
2358 set u_prompt [find userprompt $router]
2359 if { "$u_prompt" == "" } {
2360 set u_prompt "(\[Uu]sername|Login|login|user name|User):"
2361 } else {
2362 set u_prompt [join [lindex $u_prompt 0] ""]
2363 }
2364 set p_prompt [find passprompt $router]
2365 if { "$p_prompt" == "" } {
2366 set p_prompt "(\[Pp]assword|passwd|Enter password for \[^ :]+):"
2367 } else {
2368 set p_prompt [join [lindex $p_prompt 0] ""]
2369 }
2370 set e_prompt [find enableprompt $router]
2371 if { "$e_prompt" == "" } {
2372 set e_prompt "\[Pp]assword:"
2373 } else {
2374 set e_prompt [join [lindex $e_prompt 0] ""]
2375 }
2376
2377 # Figure out identity file to use
2378 set identfile [join [lindex [find identity $router] 0] ""]
2379
2380 # Figure out passphrase to use
2381 if {[info exists avpassphrase]} {
2382 set passphrase $avpassphrase
2383 } else {
2384 set passphrase [join [lindex [find passphrase $router] 0] ""]
2385 }
2386 if { ! [string length "$passphrase"]} {
2387 set passphrase $passwd
2388 }
2389
2390 # Figure out cypher type
2391 if {[info exists cypher]} {
2392 # command line cypher type
2393 set cyphertype $cypher
2394 } else {
2395 set cyphertype [find cyphertype $router]
2396 if { "$cyphertype" == "" } { set cyphertype "3des" }
2397 }
2398
2399 # Figure out connection method
2400 set cmethod [find method $router]
2401 if { "$cmethod" == "" } { set cmethod {{telnet} {ssh}} }
2402
2403 # Figure out the SSH executable name
2404 set sshcmd [join [lindex [find sshcmd $router] 0] ""]
2405 if { "$sshcmd" == "" } { set sshcmd {ssh} }
2406
2407 # if [-mM], skip do not login
2408 if { $do_cloginrcdbg > 0 } { continue; }
2409
2410 # Login to the router
2411 if {[login $router $ruser $userpswd $passwd $enapasswd $cmethod $cyphertype $identfile]} {
2412 incr exitval
2413 # if login failed or rsh was unsuccessful, move on to the next device
2414 continue
2415 }
2416 # Figure out the prompt.
2417 if { [regexp -- "(#| \\(enable\\))" $prompt_match junk] == 1 } {
2418 set enable 0
2419 }
2420 if { $enable } {
2421 if {[do_enable $enauser $enapasswd]} {
2422 if { $do_command || $do_script } {
2423 incr exitval
2424 catch {close}; catch {wait};
2425 continue
2426 }
2427 }
2428 }
2429 # we are logged in, now figure out the full prompt
2430 send "\r"
2431 regsub -all {^(\^*)(.*)} $prompt {\2} reprompt
2432 expect {
2433 -re "\[\r\n]+" { exp_continue; }
2434 -re "^(.+\[:.])1 ($reprompt)" { # stoopid extreme cmd-line numbers and
2435 # prompt based on state of config changes,
2436 # which may have an * at the beginning.
2437 set junk $expect_out(1,string)
2438 regsub -all "^\\\* " $expect_out(1,string) {} junk
2439 regsub -all "\[\]\[\(\)]" $junk {\\&} junk;
2440 set prompt ".? ?$junk\[0-9]+ $expect_out(2,string)";
2441 set platform "extreme"
2442 }
2443 -re "^.+$reprompt" { set junk $expect_out(0,string);
2444 regsub -all "\[\]\[\(\)+]" $junk {\\&} prompt;
2445 }
2446 }
2447 if { $do_command || $do_script } {
2448 if { [string compare "extreme" "$platform"] } {
2449 # If the prompt is (enable), then we are on a switch and the
2450 # command is "set length 0"; otherwise its "terminal length 0".
2451 if [regexp -- ".*> .*enable" "$prompt"] {
2452 send "set length 0\r"
2453 expect -re $prompt {}
2454 # XXX This causes the riverbed to reprint the prompt after the
2455 # existing prompt, which confuses the expect script.
2456 # send "set width 132\r"
2457 # expect -re $prompt {}
2458 send "set logging session disable\r"
2459 } else {
2460 send "terminal length 0\r"
2461 # XXX This causes the riverbed to reprint the prompt after the
2462 # existing prompt, which confuses the expect script.
2463 # expect -re $prompt {}
2464 # send "terminal width 132\r"
2465 }
2466 expect -re $prompt {}
2467 } else {
2468 send "disable clipaging\r"
2469 expect -re $prompt {}
2470 }
2471 }
2472 if { $do_command } {
2473 if {[run_commands $prompt $command]} {
2474 incr exitval
2475 continue
2476 }
2477 } elseif { $do_script } {
2478 source $sfile
2479 catch {close};
2480 } else {
2481 label $router
2482 log_user 1
2483 interact
2484 }
2485
2486 # End of for each router
2487 catch {wait};
2488 sleep 0.3
2489}
2490exit $exitval
2491#! /usr/bin/expect --
2492##
2493## $Id: rivlogin.in 3102 2015-04-23 17:18:18Z heas $
2494##
2495## rancid 3.4.1
2496## Copyright (c) 1997-2016 by Terrapin Communications, Inc.
2497## All rights reserved.
2498##
2499## This code is derived from software contributed to and maintained by
2500## Terrapin Communications, Inc. by Henry Kilmer, John Heasley, Andrew Partan,
2501## Pete Whiting, Austin Schutz, and Andrew Fort.
2502##
2503## Redistribution and use in source and binary forms, with or without
2504## modification, are permitted provided that the following conditions
2505## are met:
2506## 1. Redistributions of source code must retain the above copyright
2507## notice, this list of conditions and the following disclaimer.
2508## 2. Redistributions in binary form must reproduce the above copyright
2509## notice, this list of conditions and the following disclaimer in the
2510## documentation and/or other materials provided with the distribution.
2511## 3. All advertising materials mentioning features or use of this software
2512## must display the following acknowledgement:
2513## This product includes software developed by Terrapin Communications,
2514## Inc. and its contributors for RANCID.
2515## 4. Neither the name of Terrapin Communications, Inc. nor the names of its
2516## contributors may be used to endorse or promote products derived from
2517## this software without specific prior written permission.
2518## 5. It is requested that non-binding fixes and modifications be contributed
2519## back to Terrapin Communications, Inc.
2520##
2521## THIS SOFTWARE IS PROVIDED BY Terrapin Communications, INC. AND CONTRIBUTORS
2522## ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
2523## TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
2524## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COMPANY OR CONTRIBUTORS
2525## BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2526## CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2527## SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2528## INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2529## CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2530## ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
2531## POSSIBILITY OF SUCH DAMAGE.
2532##
2533## It is the request of the authors, but not a condition of license, that
2534## parties packaging or redistributing RANCID NOT distribute altered versions
2535## of the etc/rancid.types.base file nor alter how this file is processed nor
2536## when in relation to etc/rancid.types.conf. The goal of this is to help
2537## suppress our support costs. If it becomes a problem, this could become a
2538## condition of license.
2539#
2540# The expect login scripts were based on Erik Sherk's gwtn, by permission.
2541#
2542# The original looking glass software was written by Ed Kern, provided by
2543# permission and modified beyond recognition.
2544#
2545# rivlogin - Riverstone (and Enterasys SSR) login
2546#
2547# Based upon rscmd (see nmops.org)
2548# rscmd - Riverstone Networks Automated login
2549# by Mike MacFaden, Kiran Addepalli
2550# Riverstone Networks, 2000
2551#
2552# Returned to the RANCID crowd by andrew fort
2553
2554# Usage line
2555set usage "Error: Usage: $argv0 \[-dV\] \[-noenable\] \
2556\[-f cloginrc-file\] \[-c command\] \[-Evar=x\] \[-s script-file\] \
2557\[-x command-file\] \[-t timeout\] \[-o output-file\] \
2558router \[router...\]\n"
2559
2560# program diagnostics
2561set verbose 0
2562set success 1
2563set config 0
2564
2565# in seconds to wait for data back from device
2566set timeoutdflt 10
2567# Some CLIs having problems if we write too fast (Extreme, PIX, Cat)
2568set send_human {.2 .1 .4 .2 1}
2569set tempfile "/tmp/rivlogin.[exec date]"
2570
2571# cli command prompt
2572set my_prompt ">"
2573set enable_prompt "\#"
2574
2575set default_user ""
2576set output_file ""
2577set conf_prompt "*\(config\)# "
2578set logging 0
2579set config_mode 0
2580# Save config, if prompted
2581set do_saveconfig 0
2582
2583# Password file for routers to access
2584set password_file $env(HOME)/.cloginrc
2585
2586# If no -c or -s specified, just automate router login ala rsh
2587set do_command 0
2588set do_script 0
2589set log_user 0
2590
2591# The default CLI mode to login to is "enable" mode
2592set avenable 1
2593
2594# The default is to look in the password file to find the passwords. This
2595# tracks if we receive them on the command line.
2596set do_passwd 1
2597set do_enapasswd 1
2598#
2599set send_human {.4 .4 .7 .3 5}
2600
2601# Find the user in the ENV, or use the unix userid.
2602if {[ info exists env(CISCO_USER) ]} {
2603 set default_user $env(CISCO_USER)
2604} elseif {[ info exists env(USER) ]} {
2605 set default_user $env(USER)
2606} elseif {[ info exists env(LOGNAME) ]} {
2607 set default_user $env(LOGNAME)
2608} else {
2609 # This uses "id" which I think is portable. At least it has existed
2610 # (without options) on all machines/OSes I've been on recently -
2611 # unlike whoami or id -nu.
2612 if [ catch {exec id} reason ] {
2613 send_error "\nError: could not exec id: $reason\n"
2614 exit 1
2615 }
2616 regexp {\(([^)]*)} "$reason" junk default_user
2617}
2618if {[ info exists env(CLOGINRC) ]} {
2619 set password_file $env(CLOGINRC)
2620}
2621
2622# Procedures Section
2623
2624
2625#
2626# Sets Xterm title if interactive...if its an xterm and the user cares
2627#
2628proc label { host } {
2629 global env
2630
2631 # if CLOGIN has an 'x' in it, don't set the xterm name/banner
2632 if [info exists env(CLOGIN)] {
2633 if {[string first "x" $env(CLOGIN)] != -1} { return }
2634 }
2635
2636 if [info exists env(TERM)] {
2637 if [regexp \^(xterm|vs) $env(TERM) ignore ] {
2638 send_user "\033]1;[lindex [split $host "."] 0]\a"
2639 send_user "\033]2;$host\a"
2640 }
2641 }
2642}
2643
2644# This is a helper function to make the password file easier to
2645# maintain.
2646# NOTES: Using this the password file has the form:
2647# add password sl* pete cow
2648# add password at* steve
2649# add password * hanky-pie
2650proc add { var args } {
2651
2652 global $var
2653 lappend $var $args
2654}
2655
2656# Loads the password file. Note that as this file is tcl, and that
2657# it is sourced, the user better know what to put in there, as it
2658# could install more than just password info... I will assume however,
2659# that a "bad guy" could just as easy put such code in the clogin
2660# script, so I will leave .cloginrc as just an extention of that script
2661proc source_password_file { } {
2662 global env password_file read_password_file
2663
2664 if { [info exists read_password_file] } {
2665 return 1
2666 }
2667
2668 if { [info exists password_file] == 0 } {
2669 set password_file $env(HOME)/.cloginrc
2670 }
2671
2672 set read_password_file 1
2673 file stat $password_file fileinfo
2674
2675 if { [expr ($fileinfo(mode) & 007)] != 0000 } {
2676 puts "ERROR: $password_file must not be group or world readable and writable\n"
2677 return 1
2678 }
2679
2680 source $password_file
2681}
2682
2683# pre: var is x, router is y
2684# post: return routerr entry from database else null string
2685proc find { var router } {
2686
2687 if {[ source_password_file ] == 0 } {
2688 return {}
2689 }
2690
2691 upvar $var list
2692 if { [info exists list] } {
2693 foreach line $list {
2694 if { [string match -nocase [lindex $line 0] $router ] } {
2695 return [lrange $line 1 end]
2696 }
2697 }
2698 }
2699 return {}
2700}
2701
2702# pre: login completed ok
2703# post: terminate login session by closing tcp connection
2704proc auto_exit { } {
2705
2706 global telnet_id
2707
2708 if { $verbose == 1 } {
2709 puts "DEBUG: auto_exit closing connection to pid $telnet_id\n"
2710 }
2711 close -i telnet_id
2712}
2713
2714# perform login basic to a router
2715# pre: args are valid, router is reachable via network
2716# post: return 0 on successful login, else 1
2717#
2718# NOTE: a number of globals are setup: my_prompt, telnet_id are key
2719# and paging of cli output is disabled
2720proc login { router user userpswd passwd enapasswd } {
2721
2722 global login_array
2723 global telnet_id
2724 global expect_out
2725 global spawn_id
2726 global verbose
2727 global config verbose my_prompt
2728
2729 if { $verbose == 1 } {
2730 puts "DEBUG: login router = $router"
2731 puts "DEBUG: login username = $user"
2732 puts "DEBUG: login userpasswd = $userpswd"
2733 puts "DEBUG: login passwd = $passwd"
2734 puts "DEBUG: login enapasswd = $enapasswd"
2735 }
2736
2737 spawn -noecho telnet $router
2738 set telnet_id $spawn_id
2739
2740 if { $telnet_id == 0 } {
2741 puts "ERROR: login: spawn telnet session failed.\n"
2742 return 1
2743 }
2744
2745 # wait for initial 'Press RETURN to...' response
2746 sleep 0.3
2747
2748 expect "*"
2749 send "\r"
2750
2751 # If password fails 3 times then expect again
2752 set pass_attempt 0
2753
2754 expect {
2755 -re ".*> " { }
2756 "Password:" {
2757 incr pass_attempt
2758 send -- "$passwd\r"
2759 exp_continue
2760 }
2761 "Username: " {
2762 set pattempt 0
2763
2764 send -- "$user\r"
2765 expect {
2766 "Password: " {
2767 incr pattempt
2768 if {$pattempt == 1} {
2769 send -- "$userpswd\r";
2770 } else {
2771 send -- "$enapasswd\r";
2772 }
2773 exp_continue
2774 }
2775 -re ".*> " { exp_continue;}
2776 }
2777 }
2778 "%TELNETD-W-BADPASSWD" {
2779 puts "ERROR: bad userid or password to telnet."
2780 return 1
2781 }
2782 "%CONS-W-AUTH_PASSWD" {
2783 exp_continue
2784 }
2785 "% Authentication failed." {
2786 puts "ERROR: bad userid or password to telnet."
2787 return 1
2788 }
2789 "Authentication Failed:" {
2790 puts "ERROR: bad userid or password to radius/tacacs+"
2791 return 1
2792 }
2793 "Connection closed *" {
2794 if {$pass_attempt == 3} {
2795 puts "ERROR: Maximum attempts for password reached. Check password. Exiting.";
2796 puts $expect_out(0,string);
2797 return 1
2798 }
2799 }
2800 timeout {
2801 puts "ERROR: Timeout on login. Exiting.";
2802 return 1
2803 }
2804 eof {
2805 puts "ERROR: device closed telnet connection during login"
2806 return 1
2807 }
2808 }
2809
2810 # save my_prompt for later use
2811 send "\r"
2812 expect -re ".*> "
2813 set abc "$expect_out(buffer)"
2814 set my_prompt "[lindex $abc 0]"
2815 regexp {(.*[^>])} $my_prompt my
2816
2817 return 0;
2818}
2819
2820
2821# pre: login completed ok
2822# post: turn off paging of commands
2823proc disable_cli_paging { } {
2824 global my_prompt
2825
2826 send "cli set terminal rows 0\r"
2827
2828 expect {
2829 "$my_prompt" {return 0 }
2830 }
2831 return 1
2832}
2833
2834# pre: login returned 0, prompt at top level
2835# post: turn off command completion return 0
2836# on error, return 1
2837proc disable_cmd_autocomplete { } {
2838 global my_prompt
2839
2840 send "cli set command completion off\r"
2841 expect {
2842 $my_prompt { }
2843
2844 timeout {
2845 puts "ERROR:disable_cmd_autocomplete(TIMEOUT)";
2846 return 0;
2847 }
2848 }
2849
2850 return 0
2851}
2852
2853# pre: login returned 0, do_enable returned 0, cli is in enable or config mode
2854# post: issues logout cli to device, returns 0
2855proc logout { prompt } {
2856 global config_mode enable_prompt
2857
2858 # in case of not being at root cmd...
2859 # verify top level prompt state, move to it if necessary
2860 if { $config_mode == 1 } {
2861 send "exit\r"
2862
2863 expect {
2864 "Do you want*" {
2865 send "no\r"
2866 }
2867 "$enable_prompt" { }
2868 timeout { puts "ERROR: logout: timeout from config mode\n" }
2869 eof { puts "ERROR: device dropped connection\n" }
2870 }
2871 set config_mode 0
2872 }
2873
2874 send "logout\r"
2875 expect {
2876 "Are you sure*" {
2877 send "yes\r"
2878 return 0
2879 }
2880 }
2881}
2882
2883# pre: current mode allows transition to enable mode
2884# post: enable mode entered, my_prompt updated, return 0 else 1
2885proc do_enable { enauser enapasswd userpswd } {
2886 global expect_out verbose
2887 global my_prompt enable_prompt
2888 set enable_prompt [ string trimright $my_prompt ">" ]
2889 set enable_prompt $enable_prompt\#
2890 set uses_username 0;
2891
2892 if { $verbose == 1 } {
2893 puts "DEBUG: do_enable: my_prompt = $my_prompt ena_prompt = $enable_prompt"
2894 }
2895
2896 send "enable\r"
2897
2898 expect {
2899 Username: {
2900 set uses_username 1;
2901 send -- "$enauser\r";
2902 exp_continue
2903 }
2904 Password: {
2905 if {$uses_username == 1} {
2906 send -- "$userpswd\r";
2907 } else {
2908 send -- "$enapasswd\r";
2909 }
2910 exp_continue
2911 }
2912 "$my_prompt" {
2913 puts "ERROR: do_enable failed to gain enable mode."
2914 return 1
2915 }
2916 "CONS-W-AUTH_PASSWD" { send -- "$enapasswd\r"; }
2917 "$enable_prompt " { }
2918 "%SYS-W-NOPASSWD*" { }
2919 "Authentication Failed: Access Denied" {
2920 puts "ERROR: Bad user or password for enable mode."
2921 return 1
2922 }
2923 }
2924
2925 set my_prompt $enable_prompt
2926 return 0
2927}
2928
2929# pre: current mode allows transition to enable mode
2930# post: enable mode entered, my_prompt updated, return 0 else 1
2931proc do_configure { } {
2932 global expect_out verbose config_mode
2933 global my_prompt
2934 set config_prompt [ string trimright $my_prompt "\#" ]
2935 set config_prompt $config_prompt\(config\)\#
2936
2937 if { $verbose == 1 } {
2938 puts "DEBUG: do_config: my_prompt = $my_prompt cfg_prompt = $config_prompt"
2939 }
2940
2941 send "configure\r"
2942 expect {
2943 "$config_prompt " { }
2944 "$my_prompt" { }
2945 eof { return 1}
2946 timeout { return 1}
2947 }
2948
2949 set config_mode 1
2950 set my_prompt $config_prompt
2951 return 0
2952}
2953
2954# track sent/received from device to output_file
2955# pre: outut_file is valid filename w/write access
2956# post: logfile open, global var logging == 1, return 0 , else 1
2957proc start_logfile { output_file } {
2958 global logging
2959
2960 if { [ string length $output_file ] != 0 } {
2961 set rc [ catch { log_file -noappend $output_file } errMsg ]
2962
2963 if { $rc != 0 } {
2964 puts "ERROR: open file $output_file for write access failed. $errMsg\n"
2965 return 1
2966 }
2967 set logging 1
2968 }
2969
2970 return 0
2971}
2972
2973proc run_commands { prompt cmdstring } {
2974 global sendstring
2975
2976 # handle escaped ;s in commands, and ;; and ^;
2977 regsub -all {([^\\]);;} $command "\\1;\u002;" esccommand
2978 regsub {^;} $esccommand "\u002;" command
2979 set sep "\\1\u001"
2980 regsub -all {([^\\])\;} $command "$sep" esccommand
2981 set sep "\u001"
2982 set commands [split $esccommand $sep]
2983 set num_commands [llength $cmdstring]
2984
2985 for {set i 0} {$i < $num_commands} { incr i} {
2986 regsub -- {[ ]*([^\.]*)} [subst -nocommands [lindex $commands $i]] {\1} sendstring
2987
2988 if {[ run_single_command $prompt $sendstring ] == 1} {
2989 puts "ERROR: command '$sendstring' not processed by device. Check previous error messages."
2990 return 1
2991 }
2992 }
2993 return 0
2994}
2995
2996# Run commands given on the command line
2997# pre: prompt is current system cli prompt, cmdstring is command to execute
2998# post: return 0 on success else 1
2999# NOTE: output from router ends up in output_file if specified
3000# expect internal input buffer is reset to "" after each command
3001proc run_single_command { prompt cmdstring } {
3002 global verbose
3003 set rc 0
3004 set seen_prompt 0
3005 set seen_timeout 0
3006 set need_ays 0
3007 set delay 0
3008
3009 if {$verbose == 1} {
3010 puts "DEBUG: run_commands: prompt=$prompt \"$cmdstring\" "
3011 }
3012
3013 # ays == "are you sure" - must send back yes
3014 if { [string compare $cmdstring "save startup" ] == 0 } {
3015 set need_ays 1
3016 set delay 1
3017 if {$verbose == 1} {
3018 puts "DEBUG: save startup cmd seen, set need_ays = 1"
3019 }
3020 }
3021
3022 # TODO: add case for copy command to startup, also prompts for ok
3023
3024 # TODO: if we see config command: system set name note it
3025 # if we see a save active, then update system prompts as well
3026
3027 send "$cmdstring\r"
3028
3029 if { $delay == 1} {
3030 sleep 1
3031 }
3032
3033 expect {
3034 "%CLI-E-IVCMD*" {
3035 puts "ERROR: run_commands(command rejected by device)\n"
3036 set rc 1
3037 }
3038 "%CLI-E-FACUNKNWN*" {
3039 puts "ERROR: run_commands(command rejected by device)\n"
3040 set rc 1
3041 }
3042 "%SYS-I-ADDFAILED*" {
3043 puts "ERROR: run_commands(command rejected by device)\n"
3044 set rc 1
3045 }
3046 "%TFTP-E-REMOTE,*" {
3047 puts "ERROR: run_commands(command rejected by device)\n"
3048 set rc 1
3049 }
3050 "%SYS-E-PRIMARY_NO_SUCH_IMAGE*" {
3051 puts "ERROR: run_commands(command rejected by device)\n"
3052 set rc 1
3053 }
3054 "want to overwrite " {
3055 send "yes\r"
3056 if {$verbose == 1} {
3057 puts "DEBUG: got overwrite question, set need_ays to 0"
3058 }
3059 set need_ays 0
3060 }
3061 "%CONFIG-E-DUPLICATE,*" { }
3062 "$prompt" {
3063 if { $seen_prompt == 0 } {
3064 set seen_prompt 1
3065 }
3066
3067 if {$verbose == 1} {
3068 puts "DEBUG: saw double prompt, exiting expect loop\n"
3069 }
3070
3071 if { $need_ays == 1 } {
3072 exp_continue
3073 }
3074 }
3075 -re ".* More: m,<space> --- Quit: q --- One line: <return> ---" {
3076 send "q"
3077 exp_continue
3078 }
3079 timeout {
3080 if {$verbose == 1} {
3081 puts "DEBUG: timeout occured for the $seen_time time\n"
3082 }
3083
3084 if { $seen_timeout == 0 } {
3085 set seen_timeout 1
3086 send "\r\r"
3087 exp_continue
3088 }
3089
3090 puts "ERROR:run_commands(TIMEOUT)"
3091 set rc 1
3092 }
3093 eof {
3094 puts "ERROR:run_commands(connection closed by device)\n"
3095 set rc 1
3096 }
3097 "\n" { exp_continue }
3098 }
3099
3100 # clear input buffer of any remaining data
3101 expect "*"
3102 return $rc
3103}
3104
3105
3106# pre: RSTONE_USER env var is set
3107# post: update global "default_user" to this string
3108proc init_userid { } {
3109 global default_user
3110
3111 if {[ info exists env(RSTONE_USER) ] } {
3112 set default_user $env(RSTONE_USER)
3113 } else {
3114 # This uses "id" which I think is portable. At least it has existed
3115 # (without options) on all machines/OSes I've been on recently -
3116 # unlike whoami or id -nu.
3117 regexp {\(([^)]*)} [exec id] junk default_user
3118 }
3119}
3120
3121proc source_script_file { filename } {
3122 global my_prompt
3123
3124 expect -re "$my_prompt" {}
3125
3126 source $filename
3127}
3128
3129# pre: login completed ok, filename contains set of cli commands one per line
3130# post: each command is extracted from filename and sent to device
3131# return 0 on success, return 1 on error
3132# NOTE: for scripts that begin with "configure", change the mode to configure
3133# before executing the following commands
3134proc process_script_file { filename } {
3135 global my_prompt verbose
3136 set rc 0
3137 set ifile ""
3138
3139 set rc [ catch { set ifile [ open $filename r] } errMsg ]
3140
3141 if { $rc != 0 } {
3142 puts "ERROR: process_script_file: open script file $filename for read access failed. $errMsg\n"
3143 return 1
3144 }
3145
3146 set line_cnt 0
3147
3148 while { [eof $ifile] != 1 } {
3149
3150 set bytes [ gets $ifile cmd ]
3151 incr line_cnt
3152
3153 if { $bytes < 0 } {
3154 break
3155 } elseif { $bytes == 0 } {
3156 continue
3157 }
3158
3159 if { $verbose == 1 } {
3160 puts "DEBUG: line:$line_cnt cmd = $cmd\n"
3161 }
3162
3163 # skip comments in script files
3164 if { [regexp "^\#" $cmd] != 1 } {
3165
3166 # puts "$cmd rc = [string compare $cmd "configure" ]\n"
3167
3168 if { [string compare $cmd "configure" ] == 0 } {
3169
3170 do_configure
3171
3172 } else {
3173 if {[ run_commands $my_prompt $cmd ] == 1} {
3174 puts "ERROR: line $line_cnt in $filename not processed by device. Check previous error msgs."
3175 set rc 1
3176 break
3177 }
3178 }
3179 }
3180 }
3181
3182 close $ifile
3183 return $rc
3184}
3185
3186# pre: filename is valid file
3187# post: remove extended ascii sequences and other cruft
3188# and prepend a header: rscmd: ip-addr : date
3189# TODO: should watch all file commands more closely
3190proc strip_log { filename router } {
3191 global tempfile
3192
3193 set rc [ catch { set ifile [ open $filename r] } errMsg ]
3194
3195 if { $rc != 0 } {
3196 puts "ERROR: strip_log: open script file $filename for read access failed. $errMsg\n"
3197 return 1
3198 }
3199 set rc [ catch { set ofile [ open $tempfile w] } errMsg ]
3200
3201 if { $rc != 0 } {
3202 puts "ERROR: strip_log: open temp file $tempfile for write access failed. $errMsg\n"
3203 return 1
3204 }
3205
3206 set nl 0
3207
3208 puts $ofile "rscmd: $router : [exec date]"
3209
3210 while { [eof $ifile] != 1 } {
3211 set bytes [ gets $ifile cmd ]
3212 if { $bytes <= 0 } {
3213 break
3214 }
3215 incr nl
3216 if { $nl <= 2 } {
3217 continue
3218 }
3219
3220 regsub -all -- "\r" $cmd "" newcmd
3221 puts $ofile $newcmd
3222 }
3223
3224 close $ifile
3225 close $ofile
3226 set rc 0
3227 file copy -force $tempfile $filename
3228 file delete $tempfile
3229 return $rc
3230}
3231
3232#
3233# main section
3234#
3235if { $verbose == 1 } {
3236 puts "\n\nrscmd: Version 1.1 started on [exec date]"
3237 puts "[exec uname -a]"
3238 puts "Expect Version: [exp_version]\n"
3239}
3240
3241# send input like in a fast and consistent human style
3242set send_human {.1 .3 1 .05 2}
3243
3244# initialize default_user variable
3245init_userid
3246
3247# Parse Command Line
3248for {set idx 0} {$idx < $argc} {incr idx} {
3249 set arg [lindex $argv $idx]
3250
3251 switch -glob -- $arg {
3252 -c* {
3253 if {! [ regexp .\[cC\](.+) $arg ignore command]} {
3254 incr idx
3255 set command [ lindex $argv $idx ]
3256 }
3257 set do_command 1
3258 # Expect debug mode
3259 } -d* {
3260 exp_internal 1
3261 # Environment variable to pass to -s scripts
3262 } -E*
3263 {
3264 if {[ regexp .\[E\](.+)=(.+) $arg ignore varname varvalue]} {
3265 set E$varname $varvalue
3266 } else {
3267 send_user "Error: invalid format for -E in $arg\n"
3268 exit 1
3269 }
3270 # Expect script to run
3271 } -s* {
3272 if {! [ regexp .\[sS\](.+) $arg ignore sfile]} {
3273 incr idx
3274 set sfile [ lindex $argv $idx ]
3275 }
3276
3277 if { ! [ file exists $sfile ] } {
3278 puts "ERROR: invalid argument script file \"$sfile\" does not exist.\n"
3279 exit 1
3280 }
3281 if { ! [ file readable $sfile ] } {
3282 puts "ERROR: invalid argument script file \"$sfile\" permissions disallow read access.\n"
3283 exit 1
3284 }
3285
3286 set do_script 1
3287 # save config on exit
3288 } -S* {
3289 set do_saveconfig 1
3290 # Version string
3291 } -V* {
3292 send_user "rancid 3.4.1\n"
3293 exit 0
3294 # Command file
3295 } -x* {
3296 if {! [ regexp .\[xX\](.+) $arg ignore cmd_file]} {
3297 incr idx
3298 set cmd_file [ lindex $argv $idx ]
3299 }
3300 if [ catch {set cmd_fd [open $cmd_file r]} reason ] {
3301 send_user "\nError: $reason\n"
3302 exit 1
3303 }
3304 set cmd_text [read $cmd_fd]
3305 close $cmd_fd
3306 set command [join [split $cmd_text \n] \;]
3307 set do_command 1
3308 } -f* {
3309 if {! [ regexp .\[fF\](.+) $arg ignore password_file]} {
3310 incr idx
3311 set password_file [ lindex $argv $idx ]
3312 }
3313 } -o* {
3314 if {! [ regexp .\[fF\](.+) $arg ignore password_file]} {
3315 incr idx
3316 set output_file [ lindex $argv $idx ]
3317 if { $verbose == 1 } {
3318 puts "DEBUG: output file: $output_file"
3319 }
3320 }
3321 # Timeout
3322 } -t* {
3323 incr idx
3324 set timeoutdflt [ lindex $argv $idx ]
3325 # Do we enable?
3326 } -noenable {
3327 set avenable 0
3328 } -* {
3329 send_user "Error: Unkown argument! $arg\n"
3330 send_user $usage
3331 exit 1
3332 } default {
3333 break
3334 }
3335 }
3336}
3337
3338# Verify at least one router is specified
3339#
3340if { $idx == $argc } {
3341 puts "\n$usage"
3342 exit 1
3343}
3344
3345# main loop
3346set exitval 0
3347foreach router [lrange $argv $idx end] {
3348 set router [string tolower $router]
3349 send_user -- "$router\n"
3350
3351 # device timeout
3352 set timeout [find timeout $router]
3353 if { [llength $timeout] == 0 } {
3354 set timeout $timeoutdflt
3355 }
3356
3357 # Figure out passwords
3358 if {$verbose == 1} {
3359 puts "DEBUG: do_passwd = $do_passwd\n"
3360 puts "DEBUG: do_enablepasswd = $do_enapasswd\n"
3361 }
3362
3363 # look for noenable option in .cloginrc
3364 if { [find noenable $router] == "1" } {
3365 set enable 0
3366 } else {
3367 set enable $avenable
3368 }
3369
3370 if { $do_passwd || $do_enapasswd } {
3371 set pswd [find password $router]
3372 if { [llength $pswd] == 0 } {
3373 puts "ERROR: - no password for $router in $password_file.\n"
3374 exit 1
3375 }
3376 if { $do_enapasswd && [llength $pswd] < 2 } {
3377 puts "ERROR: no enable password found for $router in $password_file."
3378 exit 1
3379 }
3380
3381 set passwd [join [lindex $pswd 0] ""]
3382 set enapasswd [join [lindex $pswd 1] ""]
3383 } else {
3384 set passwd $userpasswd
3385 set enapasswd $enapasswd
3386 }
3387
3388 # Figure out user to login with if necessary
3389 if {[info exists username]} {
3390 # command line username
3391 set user $username
3392 } else {
3393 set user [join [find user $router] ""]
3394 if { "$user" == "" } { set user $default_user }
3395 }
3396
3397 # Figure out username's password
3398 if {[info exists userpasswd]} {
3399 # command line username
3400 set userpswd $userpasswd
3401 } else {
3402 set userpswd [join [find userpassword $router] ""]
3403 if { "$userpswd" == "" } { set userpswd $passwd }
3404 }
3405
3406 # Figure out enable username
3407 if {[info exists enausername]} {
3408 # command line enausername
3409 set enauser $enausername
3410 } else {
3411 set enauser [join [find enauser $router] ""]
3412 if { "$enauser" == "" } { set enauser $user }
3413 }
3414
3415 # Login to the router, set my_prompt to router's cmd prompt
3416 if {[login $router $user $userpswd $passwd $enapasswd ]} {
3417 incr exitval
3418 if { $verbose == 1 } {
3419 puts "DEBUG: login to $router failed\n"
3420 }
3421 exit 1
3422 }
3423
3424 if {$verbose == 1 } {
3425 puts "DEBUG: login completed ok\n"
3426 }
3427
3428 if { $enable == 1 } {
3429 if { [do_enable $enauser $enapasswd $userpswd] == 1} {
3430 incr exitval
3431 if { $verbose == 1 } {
3432 puts "DEBUG: switch to enable mode on $router failed\n"
3433 }
3434 exit 1
3435 }
3436 }
3437
3438 # run in one of three modes
3439 if { $do_command } {
3440 disable_cmd_autocomplete
3441 disable_cli_paging
3442
3443 if { [start_logfile $output_file] != 0 } {
3444 exit 1
3445 }
3446
3447 if {[ run_commands $my_prompt $command ]} {
3448 incr exitval
3449 log_file
3450 exit 1
3451 } else {
3452 logout $my_prompt
3453 }
3454 } elseif { $do_script } {
3455 disable_cmd_autocomplete
3456 disable_cli_paging
3457
3458 if {[ start_logfile $output_file] != 0 } {
3459 exit 1
3460 }
3461
3462# if { [process_script_file $sfile] == 1}{
3463# puts "DEBUG: logfile $output_file closed on error\n"
3464# logout $my_prompt
3465# exit 1
3466# }
3467
3468 source_script_file $sfile
3469
3470 logout $my_prompt
3471 } else {
3472 label $router
3473 log_user 1
3474
3475 if {[ start_logfile $output_file] != 0 } {
3476 exit 1
3477 }
3478 interact
3479 log_file
3480 }
3481
3482 if { $verbose == 1 } {
3483 puts "DEBUG: exiting normally.\n"
3484 }
3485
3486 if { $logging == 1} {
3487 log_file
3488 strip_log $output_file $router
3489 }
3490
3491 # End of for each router
3492 catch {wait};
3493 sleep 0.3
3494}
3495exit $exitval
3496#! /usr/bin/perl
3497##
3498## $Id: rivrancid.in 3018 2015-01-11 05:51:49Z heas $
3499##
3500## rancid 3.4.1
3501## Copyright (c) 1997-2016 by Terrapin Communications, Inc.
3502## All rights reserved.
3503##
3504## This code is derived from software contributed to and maintained by
3505## Terrapin Communications, Inc. by Henry Kilmer, John Heasley, Andrew Partan,
3506## Pete Whiting, Austin Schutz, and Andrew Fort.
3507##
3508## Redistribution and use in source and binary forms, with or without
3509## modification, are permitted provided that the following conditions
3510## are met:
3511## 1. Redistributions of source code must retain the above copyright
3512## notice, this list of conditions and the following disclaimer.
3513## 2. Redistributions in binary form must reproduce the above copyright
3514## notice, this list of conditions and the following disclaimer in the
3515## documentation and/or other materials provided with the distribution.
3516## 3. All advertising materials mentioning features or use of this software
3517## must display the following acknowledgement:
3518## This product includes software developed by Terrapin Communications,
3519## Inc. and its contributors for RANCID.
3520## 4. Neither the name of Terrapin Communications, Inc. nor the names of its
3521## contributors may be used to endorse or promote products derived from
3522## this software without specific prior written permission.
3523## 5. It is requested that non-binding fixes and modifications be contributed
3524## back to Terrapin Communications, Inc.
3525##
3526## THIS SOFTWARE IS PROVIDED BY Terrapin Communications, INC. AND CONTRIBUTORS
3527## ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
3528## TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
3529## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COMPANY OR CONTRIBUTORS
3530## BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
3531## CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
3532## SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
3533## INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
3534## CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
3535## ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
3536## POSSIBILITY OF SUCH DAMAGE.
3537#
3538# Amazingly hacked version of Hank's rancid - this one tries to
3539# deal with Cabletron, Riverstone and Enterasys routers/switches
3540#
3541# 10/23/2002 -- Initial changes for Riverstone/Cabletron support
3542# Jim Meehan -- jmeehan@vpizza.org
3543#
3544# RANCID - Really Awesome New Cisco confIg Differ
3545#
3546# usage: rivrancid [-dltCV] [-f filename | hostname]
3547#
3548use Getopt::Std;
3549getopts('dflt:CV');
3550if ($opt_V) {
3551 print "rancid 3.4.1\n";
3552 exit(0);
3553}
3554$log = $opt_l;
3555$debug = $opt_d;
3556$file = $opt_f;
3557$host = $ARGV[0];
3558$clean_run = 0;
3559$found_end = 0;
3560$timeo = 90; # rivlogin timeout in seconds
3561
3562my(@commandtable, %commands, @commands);# command lists
3563my($aclsort) = ("ipsort"); # ACL sorting mode
3564my($filter_commstr); # SNMP community string filtering
3565my($filter_pwds); # password filtering mode
3566
3567# This routine is used to print out the router configuration
3568sub ProcessHistory {
3569 my($new_hist_tag,$new_command,$command_string,@string) = (@_);
3570 if ((($new_hist_tag ne $hist_tag) || ($new_command ne $command))
3571 && scalar(%history)) {
3572 print eval "$command \%history";
3573 undef %history;
3574 }
3575 if (($new_hist_tag) && ($new_command) && ($command_string)) {
3576 if ($history{$command_string}) {
3577 $history{$command_string} = "$history{$command_string}@string";
3578 } else {
3579 $history{$command_string} = "@string";
3580 }
3581 } elsif (($new_hist_tag) && ($new_command)) {
3582 $history{++$#history} = "@string";
3583 } else {
3584 print "@string";
3585 }
3586 $hist_tag = $new_hist_tag;
3587 $command = $new_command;
3588 1;
3589}
3590
3591sub numerically { $a <=> $b; }
3592
3593# This is a sort routine that will sort numerically on the
3594# keys of a hash as if it were a normal array.
3595sub keynsort {
3596 local(%lines) = @_;
3597 local($i) = 0;
3598 local(@sorted_lines);
3599 foreach $key (sort numerically keys(%lines)) {
3600 $sorted_lines[$i] = $lines{$key};
3601 $i++;
3602 }
3603 @sorted_lines;
3604}
3605
3606# This is a sort routine that will sort on the
3607# keys of a hash as if it were a normal array.
3608sub keysort {
3609 local(%lines) = @_;
3610 local($i) = 0;
3611 local(@sorted_lines);
3612 foreach $key (sort keys(%lines)) {
3613 $sorted_lines[$i] = $lines{$key};
3614 $i++;
3615 }
3616 @sorted_lines;
3617}
3618
3619# This is a sort routine that will sort on the
3620# values of a hash as if it were a normal array.
3621sub valsort{
3622 local(%lines) = @_;
3623 local($i) = 0;
3624 local(@sorted_lines);
3625 foreach $key (sort values %lines) {
3626 $sorted_lines[$i] = $key;
3627 $i++;
3628 }
3629 @sorted_lines;
3630}
3631
3632# This is a numerical sort routine (ascending).
3633sub numsort {
3634 local(%lines) = @_;
3635 local($i) = 0;
3636 local(@sorted_lines);
3637 foreach $num (sort {$a <=> $b} keys %lines) {
3638 $sorted_lines[$i] = $lines{$num};
3639 $i++;
3640 }
3641 @sorted_lines;
3642}
3643
3644# This is a sort routine that will sort on the
3645# ip address when the ip address is anywhere in
3646# the strings.
3647sub ipsort {
3648 local(%lines) = @_;
3649 local($i) = 0;
3650 local(@sorted_lines);
3651 foreach $addr (sort sortbyipaddr keys %lines) {
3652 $sorted_lines[$i] = $lines{$addr};
3653 $i++;
3654 }
3655 @sorted_lines;
3656}
3657
3658# These two routines will sort based upon IP addresses
3659sub ipaddrval {
3660 my(@a) = ($_[0] =~ m#^(\d+)\.(\d+)\.(\d+)\.(\d+)$#);
3661 $a[3] + 256 * ($a[2] + 256 * ($a[1] +256 * $a[0]));
3662}
3663sub sortbyipaddr {
3664 &ipaddrval($a) <=> &ipaddrval($b);
3665}
3666
3667# This routine parses "system show version"
3668sub ShowVersion {
3669 my($slot);
3670
3671 print STDERR " In ShowVersion: $_" if ($debug);
3672
3673 while (<INPUT>) {
3674 tr/\015//d;
3675 next if /^\s*$/;
3676 last if(/^$prompt/);
3677
3678 ProcessHistory("VERSION","","","!SW: $_");
3679 }
3680 ProcessHistory("VERSION","","","!\n");
3681 return(0);
3682}
3683
3684# This routine parses "system show hardware"
3685sub ShowHardware {
3686 print STDERR " In ShowHardware: $_" if ($debug);
3687
3688 while (<INPUT>) {
3689 tr/\015//d;
3690 last if (/^$prompt/);
3691 ProcessHistory("HARDWARE","","","!HW: $_");
3692 }
3693 ProcessHistory("","","","!\n");
3694 return(0);
3695}
3696
3697# This routine parses "system show uptime"
3698sub ShowUptime {
3699 print STDERR " In ShowUptime: $_" if ($debug);
3700
3701 while (<INPUT>) {
3702 tr/\015//d;
3703 last if(/^$prompt/);
3704 next if /^\s*$/;
3705 next if /System up/;
3706 ProcessHistory("UPTIME","","","!UPTIME: $_");
3707 }
3708 ProcessHistory("","","","!\n");
3709 return;
3710}
3711
3712# This routine processes a "system show active"
3713sub ShowActive {
3714 print STDERR " In ShowActive: $_" if ($debug);
3715
3716 while (<INPUT>) {
3717 tr/\015//d;
3718
3719 # Remove leading whitespace and/or line numbers
3720 s/^\s*(\d+\D: )*//;
3721
3722 # Riverstone/Cabletron doesn't have an "end" line, so
3723 # we need to set $clean_run here
3724 if (/^$prompt/) {
3725 $clean_run = 1;
3726 last;
3727 }
3728
3729 next if (/Running system configuration/);
3730
3731 # filter out any RCS/CVS tags to avoid confusing local CVS storage
3732 s/\$(Revision|Id):/ $1:/;
3733
3734 if (/^(.*hashed-password \S+)/ && $filter_pwds == 2) {
3735 ProcessHistory("","","","! $1 <removed>\n");
3736 next;
3737 }
3738
3739 if (/^(snmp set community )\S+/ && $filter_commstr) {
3740 ProcessHistory("","","","! $1<removed>$'");
3741 next;
3742 }
3743
3744 ProcessHistory("","","","$_");
3745 }
3746 return;
3747}
3748
3749# Main
3750@commandtable = (
3751 {'system show uptime' => 'ShowUptime'},
3752 {'system show version' => 'ShowVersion'},
3753 {'system show hardware' => 'ShowHardware'},
3754 {'system show active-config' => 'ShowActive'}
3755);
3756# Use an array to preserve the order of the commands and a hash for mapping
3757# commands to the subroutine and track commands that have been completed.
3758@commands = map(keys(%$_), @commandtable);
3759%commands = map(%$_, @commandtable);
3760$commandcnt = scalar(keys %commands);
3761
3762$commandstr=join(";",@commands);
3763$cmds_regexp = join("|", map quotemeta($_), @commands);
3764
3765if (length($host) == 0) {
3766 if ($file) {
3767 print(STDERR "Too few arguments: file name required\n");
3768 exit(1);
3769 } else {
3770 print(STDERR "Too few arguments: host name required\n");
3771 exit(1);
3772 }
3773}
3774if ($opt_C) {
3775 print "rivlogin -t $timeo -c\'$commandstr\' $host\n";
3776 exit(0);
3777}
3778open(OUTPUT,">$host.new") || die "Can't open $host.new for writing: $!\n";
3779select(OUTPUT);
3780# make OUTPUT unbuffered if debugging
3781if ($debug) { $| = 1; }
3782
3783if ($file) {
3784 print STDERR "opening file $host\n" if ($debug);
3785 print STDOUT "opening file $host\n" if ($log);
3786 open(INPUT,"<$host") || die "open failed for $host: $!\n";
3787} else {
3788 print STDERR "executing rivlogin -t $timeo -c\"$commandstr\" $host\n" if ($debug);
3789 print STDOUT "executing rivlogin -t $timeo -c\"$commandstr\" $host\n" if ($log);
3790 if (defined($ENV{NOPIPE}) && $ENV{NOPIPE} =~ /^YES/i) {
3791 system "rivlogin -t $timeo -c \"$commandstr\" $host </dev/null > $host.raw 2>&1" || die "rivlogin failed for $host: $!\n";
3792 open(INPUT, "< $host.raw") || die "rivlogin failed for $host: $!\n";
3793 } else {
3794 open(INPUT,"rivlogin -t $timeo -c \"$commandstr\" $host </dev/null |") || die "rivlogin failed for $host: $!\n";
3795 }
3796}
3797
3798# determine ACL sorting mode
3799if ($ENV{"ACLSORT"} =~ /no/i) {
3800 $aclsort = "";
3801}
3802# determine community string filtering mode
3803if (defined($ENV{"NOCOMMSTR"}) &&
3804 ($ENV{"NOCOMMSTR"} =~ /yes/i || $ENV{"NOCOMMSTR"} =~ /^$/)) {
3805 $filter_commstr = 1;
3806} else {
3807 $filter_commstr = 0;
3808}
3809# determine password filtering mode
3810if ($ENV{"FILTER_PWDS"} =~ /no/i) {
3811 $filter_pwds = 0;
3812} elsif ($ENV{"FILTER_PWDS"} =~ /all/i) {
3813 $filter_pwds = 2;
3814} else {
3815 $filter_pwds = 1;
3816}
3817
3818ProcessHistory("","","","!RANCID-CONTENT-TYPE: riverstone\n!\n");
3819TOP: while(<INPUT>) {
3820 tr/\015//d;
3821 last if ($clean_run);
3822 if (/^Error:/) {
3823 print STDOUT ("$host rivlogin error: $_");
3824 print STDERR ("$host rivlogin error: $_") if ($debug);
3825 $clean_run=0;
3826 last;
3827 }
3828
3829 $kradcount++;
3830
3831 while (/\033(\[\?25l)/) {
3832 s/\033\[\?25l//g;
3833 #print STDERR "krad $1\n";
3834 #print STDERR $_;
3835 #print STDERR $kradcount;
3836 next;
3837 }
3838
3839
3840 while (/#\s*($cmds_regexp)\s*$/) {
3841 $cmd = $1;
3842 if (!defined($prompt)) {
3843 $prompt = ($_ =~ /^([^#]+#)/)[0];
3844 $prompt =~ s/([}{)(\\])/\\$1/g;
3845 }
3846 print STDERR ("HIT COMMAND:$_") if ($debug);
3847 if (! defined($commands{$cmd})) {
3848 print STDERR "$host: found unexpected command - \"$cmd\"\n";
3849 $clean_run = 0;
3850 last TOP;
3851 }
3852 $rval = &{$commands{$cmd}}(*INPUT, *OUTPUT, $cmd);
3853 delete($commands{$cmd});
3854 if ($rval == -1) {
3855 $clean_run = 0;
3856 last TOP;
3857 }
3858 }
3859}
3860print STDOUT "Done $logincmd: $_\n" if ($log);
3861# Flush History
3862ProcessHistory("","","","");
3863# Cleanup
3864close(INPUT);
3865close(OUTPUT);
3866
3867if (defined($ENV{NOPIPE}) && $ENV{NOPIPE} =~ /^YES/i) {
3868 unlink("$host.raw") if (! $debug);
3869}
3870
3871# check for completeness
3872if (scalar(%commands) || !$clean_run) {
3873 if (scalar(keys %commands) eq $commandcnt) {
3874 printf(STDERR "$host: missed cmd(s): all commands\n");
3875 } elsif (scalar(%commands)) {
3876 printf(STDOUT "$host: missed cmd(s): %s\n", join(',', keys(%commands)));
3877 printf(STDERR "$host: missed cmd(s): %s\n", join(',', keys(%commands))) if ($debug);
3878 }
3879 if (!$clean_run) {
3880 print STDOUT "$host: End of run not found\n";
3881 print STDERR "$host: End of run not found\n" if ($debug);
3882 system("/usr/bin/tail -1 $host.new");
3883 }
3884 unlink "$host.new" if (! $debug);
3885}
3886#! /usr/bin/perl
3887##
3888## $Id: rrancid.in 3018 2015-01-11 05:51:49Z heas $
3889##
3890## rancid 3.4.1
3891## Copyright (c) 1997-2016 by Terrapin Communications, Inc.
3892## All rights reserved.
3893##
3894## This code is derived from software contributed to and maintained by
3895## Terrapin Communications, Inc. by Henry Kilmer, John Heasley, Andrew Partan,
3896## Pete Whiting, Austin Schutz, and Andrew Fort.
3897##
3898## Redistribution and use in source and binary forms, with or without
3899## modification, are permitted provided that the following conditions
3900## are met:
3901## 1. Redistributions of source code must retain the above copyright
3902## notice, this list of conditions and the following disclaimer.
3903## 2. Redistributions in binary form must reproduce the above copyright
3904## notice, this list of conditions and the following disclaimer in the
3905## documentation and/or other materials provided with the distribution.
3906## 3. All advertising materials mentioning features or use of this software
3907## must display the following acknowledgement:
3908## This product includes software developed by Terrapin Communications,
3909## Inc. and its contributors for RANCID.
3910## 4. Neither the name of Terrapin Communications, Inc. nor the names of its
3911## contributors may be used to endorse or promote products derived from
3912## this software without specific prior written permission.
3913## 5. It is requested that non-binding fixes and modifications be contributed
3914## back to Terrapin Communications, Inc.
3915##
3916## THIS SOFTWARE IS PROVIDED BY Terrapin Communications, INC. AND CONTRIBUTORS
3917## ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
3918## TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
3919## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COMPANY OR CONTRIBUTORS
3920## BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
3921## CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
3922## SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
3923## INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
3924## CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
3925## ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
3926## POSSIBILITY OF SUCH DAMAGE.
3927#
3928# hacked version of Hank's rancid - this one tries to deal with redbacks.
3929#
3930# RANCID - Really Awesome New Cisco confIg Differ
3931#
3932# usage: rrancid [-dltCV] [-f filename | hostname]
3933#
3934use Getopt::Std;
3935getopts('dflt:CV');
3936if ($opt_V) {
3937 print "rancid 3.4.1\n";
3938 exit(0);
3939}
3940$log = $opt_l;
3941$debug = $opt_d;
3942$file = $opt_f;
3943$host = $ARGV[0];
3944$clean_run = 0;
3945$found_end = 0;
3946$timeo = 90; # clogin timeout in seconds
3947
3948my(@commandtable, %commands, @commands);# command lists
3949my($aclsort) = ("ipsort"); # ACL sorting mode
3950my($filter_commstr); # SNMP community string filtering
3951my($filter_pwds); # password filtering mode
3952
3953# This routine is used to print out the router configuration
3954sub ProcessHistory {
3955 my($new_hist_tag,$new_command,$command_string,@string) = (@_);
3956 if ((($new_hist_tag ne $hist_tag) || ($new_command ne $command))
3957 && scalar(%history)) {
3958 print eval "$command \%history";
3959 undef %history;
3960 }
3961 if (($new_hist_tag) && ($new_command) && ($command_string)) {
3962 if ($history{$command_string}) {
3963 $history{$command_string} = "$history{$command_string}@string";
3964 } else {
3965 $history{$command_string} = "@string";
3966 }
3967 } elsif (($new_hist_tag) && ($new_command)) {
3968 $history{++$#history} = "@string";
3969 } else {
3970 print "@string";
3971 }
3972 $hist_tag = $new_hist_tag;
3973 $command = $new_command;
3974 1;
3975}
3976
3977sub numerically { $a <=> $b; }
3978
3979# This is a sort routine that will sort numerically on the
3980# keys of a hash as if it were a normal array.
3981sub keynsort {
3982 local(%lines) = @_;
3983 local($i) = 0;
3984 local(@sorted_lines);
3985 foreach $key (sort numerically keys(%lines)) {
3986 $sorted_lines[$i] = $lines{$key};
3987 $i++;
3988 }
3989 @sorted_lines;
3990}
3991
3992# This is a sort routine that will sort on the
3993# keys of a hash as if it were a normal array.
3994sub keysort {
3995 local(%lines) = @_;
3996 local($i) = 0;
3997 local(@sorted_lines);
3998 foreach $key (sort keys(%lines)) {
3999 $sorted_lines[$i] = $lines{$key};
4000 $i++;
4001 }
4002 @sorted_lines;
4003}
4004
4005# This is a sort routine that will sort on the
4006# values of a hash as if it were a normal array.
4007sub valsort{
4008 local(%lines) = @_;
4009 local($i) = 0;
4010 local(@sorted_lines);
4011 foreach $key (sort values %lines) {
4012 $sorted_lines[$i] = $key;
4013 $i++;
4014 }
4015 @sorted_lines;
4016}
4017
4018# This is a numerical sort routine (ascending).
4019sub numsort {
4020 local(%lines) = @_;
4021 local($i) = 0;
4022 local(@sorted_lines);
4023 foreach $num (sort {$a <=> $b} keys %lines) {
4024 $sorted_lines[$i] = $lines{$num};
4025 $i++;
4026 }
4027 @sorted_lines;
4028}
4029
4030# This is a sort routine that will sort on the
4031# ip address when the ip address is anywhere in
4032# the strings.
4033sub ipsort {
4034 local(%lines) = @_;
4035 local($i) = 0;
4036 local(@sorted_lines);
4037 foreach $addr (sort sortbyipaddr keys %lines) {
4038 $sorted_lines[$i] = $lines{$addr};
4039 $i++;
4040 }
4041 @sorted_lines;
4042}
4043
4044# These two routines will sort based upon IP addresses
4045sub ipaddrval {
4046 my(@a) = ($_[0] =~ m#^(\d+)\.(\d+)\.(\d+)\.(\d+)$#);
4047 $a[3] + 256 * ($a[2] + 256 * ($a[1] +256 * $a[0]));
4048}
4049sub sortbyipaddr {
4050 &ipaddrval($a) <=> &ipaddrval($b);
4051}
4052
4053# This routine parses "show version"
4054sub ShowVersion {
4055 print STDERR " In ShowVersion: $_" if ($debug);
4056
4057 while (<INPUT>) {
4058 tr/\015//d;
4059 last if(/^$prompt/);
4060
4061 /(Copyright|uptime|restarted|^\s*$)/ && next;
4062 /(Up Time|Boot Time)/ && next;
4063 ProcessHistory("COMMENTS","keysort","A1","!Image: $_");
4064 }
4065 return;
4066}
4067
4068# This routine parses "dir /<fsys>"
4069sub DirFlash {
4070 print STDERR " In DirFlash: $_" if ($debug);
4071
4072 my($dev) = (/\/(.*)$/);
4073 while (<INPUT>) {
4074 tr/\015//d;
4075 last if(/^$prompt/);
4076 /^\s*$/ && next;
4077
4078 /(Can\'t open|No such device)/ && return;
4079 /is not a valid path on a local file system/ && return;
4080 ProcessHistory("FLASH","keysort",$dev,"!Flash: $dev: $_");
4081 }
4082 ProcessHistory("FLASH","keysort",$dev,"!\n");
4083 return;
4084}
4085
4086# This routine parses "show hardware"
4087sub ShowHardware {
4088 print STDERR " In ShowHardware: $_" if ($debug);
4089
4090 while (<INPUT>) {
4091 tr/\015//d;
4092 last if(/^$prompt/);
4093 s/\s*$/\n/;
4094
4095 /Can not retrieve information/ && return;
4096 /^Temperature:/ && next;
4097 /^Voltage/ && next;
4098 /^$/ && next;
4099 #ProcessHistory("","","","!Chassis: $_") && next;
4100 ProcessHistory("COMMENTS","keysort","B1","!Chassis: $_");
4101 }
4102 ProcessHistory("COMMENTS","keysort","B1","!\n");
4103 return;
4104}
4105
4106# This routine parses "show chassis"
4107sub ShowChassis {
4108 print STDERR " In ShowChassis: $_" if ($debug);
4109
4110 while (<INPUT>) {
4111 tr/\015//d;
4112 last if(/^$prompt/);
4113 last if(/^invalid input at/i);
4114 next if(/^(\s*|\s*$cmd\s*|\s+\^)$/);
4115
4116 ProcessHistory("COMMENTS","keysort","C1","!Chassis: $_");
4117 }
4118 ProcessHistory("COMMENTS","keysort","C1","!\n");
4119 return;
4120}
4121
4122# This routine parses "show slot table"
4123sub ShowSlotTable {
4124 print STDERR " In ShowSlotTable: $_" if ($debug);
4125
4126 while (<INPUT>) {
4127 tr/\015//d;
4128 last if(/^$prompt/);
4129
4130 /(Slot Table|^$)/ && next;
4131 /^\s+\^/ && next;
4132 /Invalid input at/ && return;
4133 s/^\s*//;
4134 ProcessHistory("COMMENTS","keysort","D1","!Slot Table: $_");
4135 }
4136 ProcessHistory("COMMENTS","keysort","D1","!\n");
4137 return;
4138}
4139
4140# This routine processes a "write term"
4141sub WriteTerm {
4142 print STDERR " In WriteTerm: $_" if ($debug);
4143
4144 while (<INPUT>) {
4145 tr/\015//d;
4146 last if(/^$prompt/);
4147 next if(/^\s*$/);
4148
4149# /Non-Volatile memory is in use/ && return(-1); # NvRAM is locked
4150 # Dog gone Cool matches to process the rest of the config
4151 /^! last updated: .*$/ && next; # kill last updated line
4152 /^Building configuration/ && next; # kill Building config line
4153 /^Current configuration/ && next; # kill Current config line
4154 /^! Configuration last changed by user / && next;
4155 /^ length / && next; # kill length on serial lines
4156 /^ width / && next; # kill width on serial lines
4157 # filter out any RCS/CVS tags to avoid confusing local CVS storage
4158 s/\$(Revision|Id):/ $1:/;
4159 # order access-lists
4160 /^access-list\s+(\d\d?)\s+(\S+)\s+(\S+)/ &&
4161 ProcessHistory("ACL $1 $2","$aclsort","$3","$_") && next;
4162 # prune snmp community statements
4163 if (/^snmp (group|community) (\S+)/) {
4164 if ($filter_commstr) {
4165 ProcessHistory("SNMPSERVERCOMM","keysort","$_","!snmp $1 <removed>$'") && next;
4166 } else {
4167 ProcessHistory("SNMPSERVERCOMM","keysort","$_","$_") && next;
4168 }
4169 }
4170 ProcessHistory("","","","$_");
4171 # end of config
4172 if (/^end$/) {
4173 $found_end = 1;
4174 last;
4175 }
4176 }
4177 return;
4178}
4179
4180# Main subroutine that splits up the work
4181# All Subs return the name of the next function to use.
4182# If the sub returns a new funtion name, that name will be used
4183# else the main loop keeps using the current function
4184sub FlailHelplessly {
4185 print STDERR "Flailing: $_" if ($debug);
4186 print STDOUT "Flailing: $_" if ($log);
4187 /#(show version)$/ && delete($commands{$1}) && return("ShowVersion");
4188 /#(show hardware)$/ && delete($commands{$1}) && return("ShowHardware");
4189 /#(show chassis)$/ && delete($commands{$1}) && return("ShowChassis");
4190 /#(show slot table)$/ && delete($commands{$1}) && return("ShowSlotTable");
4191 /#(show config)$/ && delete($commands{$1}) && return("WriteTerm");
4192 return "FlailHelplessly";
4193}
4194
4195# Main
4196@commandtable = (
4197 {'show version' => 'ShowVersion'},
4198 {'dir /flash' => 'DirFlash'},
4199 {'dir /pcmcia0' => 'DirFlash'},
4200 {'dir /pcmcia1' => 'DirFlash'},
4201 {'show hardware' => 'ShowHardware'},
4202 {'show chassis' => 'ShowChassis'},
4203 {'show slot table' => 'ShowSlotTable'},
4204 {'show config' => 'WriteTerm'}
4205);
4206# Use an array to preserve the order of the commands and a hash for mapping
4207# commands to the subroutine and track commands that have been completed.
4208@commands = map(keys(%$_), @commandtable);
4209%commands = map(%$_, @commandtable);
4210$commandcnt = scalar(keys %commands);
4211
4212$redback_cmds=join(";",@commands);
4213$cmds_regexp = join("|", map quotemeta($_), @commands);
4214
4215if (length($host) == 0) {
4216 if ($file) {
4217 print(STDERR "Too few arguments: file name required\n");
4218 exit(1);
4219 } else {
4220 print(STDERR "Too few arguments: host name required\n");
4221 exit(1);
4222 }
4223}
4224if ($opt_C) {
4225 print "clogin -t $timeo -c\'$commandstr\' $host\n";
4226 exit(0);
4227}
4228open(OUTPUT,">$host.new") || die "Can't open $host.new for writing: $!\n";
4229select(OUTPUT);
4230# make OUTPUT unbuffered
4231if ($debug) { $| = 1; }
4232
4233if ($file) {
4234 print STDERR "opening file $host\n" if ($debug);
4235 print STDOUT "opening file $host\n" if ($log);
4236 open(INPUT,"<$host") || die "open failed for $host: $!\n";
4237} else {
4238 print STDERR "executing clogin -t $timeo -c\"$redback_cmds\" $host\n" if ($debug);
4239 print STDOUT "executing clogin -t $timeo -c\"$redback_cmds\" $host\n" if ($log);
4240
4241 if (defined($ENV{NOPIPE}) && $ENV{NOPIPE} =~ /^YES/i) {
4242 system "clogin -t $timeo -c \"$redback_cmds\" $host </dev/null > $host.raw" || die "clogin failed for $host: $!\n";
4243 open(INPUT, "< $host.raw") || die "clogin failed for $host: $!\n";
4244 } else {
4245 open(INPUT,"clogin -t $timeo -c \"$redback_cmds\" $host </dev/null |") || die "clogin failed for $host: $!\n";
4246 }
4247}
4248
4249# determine ACL sorting mode
4250if ($ENV{"ACLSORT"} =~ /no/i) {
4251 $aclsort = "";
4252}
4253# determine community string filtering mode
4254if (defined($ENV{"NOCOMMSTR"}) &&
4255 ($ENV{"NOCOMMSTR"} =~ /yes/i || $ENV{"NOCOMMSTR"} =~ /^$/)) {
4256 $filter_commstr = 1;
4257} else {
4258 $filter_commstr = 0;
4259}
4260# determine password filtering mode
4261if ($ENV{"FILTER_PWDS"} =~ /no/i) {
4262 $filter_pwds = 0;
4263} elsif ($ENV{"FILTER_PWDS"} =~ /all/i) {
4264 $filter_pwds = 2;
4265} else {
4266 $filter_pwds = 1;
4267}
4268
4269ProcessHistory("","","","!RANCID-CONTENT-TYPE: redback\n!\n");
4270ProcessHistory("COMMENTS","keysort","B0","!\n");
4271ProcessHistory("COMMENTS","keysort","C0","!\n");
4272ProcessHistory("COMMENTS","keysort","E0","!\n");
4273while(<INPUT>) {
4274 tr/\015//d;
4275 if (/\#exit$/) {
4276 $clean_run=1;
4277 last;
4278 }
4279 if (/^Error:/) {
4280 print STDOUT ("$host clogin error: $_");
4281 print STDERR ("$host clogin error: $_") if ($debug);
4282 $clean_run=0;
4283 last;
4284 }
4285 while (/#\s*($cmds_regexp)\s*$/) {
4286 $cmd = $1;
4287 if (!defined($prompt)) {
4288 $prompt = ($_ =~ /^([^#]*#)/)[0];
4289 $prompt =~ s/([][}{)(\\])/\\$1/g; # quote the damn []'s
4290 print STDERR ("PROMPT MATCH: $prompt\n") if ($debug);
4291 }
4292 print STDERR ("HIT COMMAND:$_") if ($debug);
4293 if (! defined($commands{$cmd})) {
4294 print STDERR "$host: found unexpected command - \"$cmd\"\n";
4295 $clean_run = 0;
4296 last;
4297 }
4298 $rval = &{$commands{$cmd}}(*INPUT, *OUTPUT, $cmd);
4299 delete($commands{$cmd});
4300 if ($rval == -1) {
4301 $clean_run = 0;
4302 last;
4303 }
4304 }
4305}
4306print STDOUT "Done $logincmd: $_\n" if ($log);
4307# Flush History
4308ProcessHistory("","","","");
4309# Cleanup
4310close(INPUT);
4311close(OUTPUT);
4312
4313if (defined($ENV{NOPIPE}) && $ENV{NOPIPE} =~ /^YES/i) {
4314 unlink("$host.raw") if (! $debug);
4315}
4316
4317# check for completeness
4318if (scalar(%commands) || !$clean_run || !$found_end) {
4319 if (scalar(keys %commands) eq $commandcnt) {
4320 printf(STDERR "$host: missed cmd(s): all commands\n");
4321 } elsif (scalar(%commands)) {
4322 printf(STDOUT "$host: missed cmd(s): %s\n", join(',', keys(%commands)));
4323 printf(STDERR "$host: missed cmd(s): %s\n", join(',', keys(%commands))) if ($debug);
4324 }
4325 if (!$clean_run || !$found_end) {
4326 print STDOUT "$host: End of run not found\n";
4327 print STDERR "$host: End of run not found\n" if ($debug);
4328 system("/usr/bin/tail -1 $host.new");
4329 }
4330 unlink "$host.new" if (! $debug);
4331}
4332#! /usr/bin/perl
4333##
4334## $Id: rtftpcopy.in 3187 2015-10-19 23:08:51Z heas $
4335##
4336## rancid 3.4.1
4337## Copyright (c) 1997-2016 by Terrapin Communications, Inc.
4338## All rights reserved.
4339##
4340## This code is derived from software contributed to and maintained by
4341## Terrapin Communications, Inc. by Henry Kilmer, John Heasley, Andrew Partan,
4342## Pete Whiting, Austin Schutz, and Andrew Fort.
4343##
4344## Redistribution and use in source and binary forms, with or without
4345## modification, are permitted provided that the following conditions
4346## are met:
4347## 1. Redistributions of source code must retain the above copyright
4348## notice, this list of conditions and the following disclaimer.
4349## 2. Redistributions in binary form must reproduce the above copyright
4350## notice, this list of conditions and the following disclaimer in the
4351## documentation and/or other materials provided with the distribution.
4352## 3. All advertising materials mentioning features or use of this software
4353## must display the following acknowledgement:
4354## This product includes software developed by Terrapin Communications,
4355## Inc. and its contributors for RANCID.
4356## 4. Neither the name of Terrapin Communications, Inc. nor the names of its
4357## contributors may be used to endorse or promote products derived from
4358## this software without specific prior written permission.
4359## 5. It is requested that non-binding fixes and modifications be contributed
4360## back to Terrapin Communications, Inc.
4361##
4362## THIS SOFTWARE IS PROVIDED BY Terrapin Communications, INC. AND CONTRIBUTORS
4363## ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
4364## TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
4365## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COMPANY OR CONTRIBUTORS
4366## BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
4367## CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
4368## SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
4369## INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
4370## CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
4371## ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
4372## POSSIBILITY OF SUCH DAMAGE.
4373#
4374# copy configs from tftpboot within rancid; assumes CWD=<group>/configs and
4375# that /bin/domainname is the same domainname as the routers, which is
4376# replaced with "-confg" to form the filename expected in /tftpboot.
4377#
4378# usage: rtftpcopy [-dltCV] [-f filename | hostname]
4379#
4380use Getopt::Std;
4381getopts('dflt:CV');
4382if ($opt_V) {
4383 print "rancid 3.4.1\n";
4384 exit(0);
4385}
4386$log = $opt_l;
4387$debug = $opt_d;
4388$file = $opt_f;
4389$host = $ARGV[0];
4390$domain = system("/bin/domainname");
4391$domain =~ s/[.]/\\./g;
4392if ($file) {
4393 $srcfile = $host;
4394 $host =~ s/^.*\///;
4395} else {
4396 $srcfile = $host;
4397 $srcfile =~ s/$domain$/-confg/;
4398}
4399
4400if (length($host) == 0) {
4401 if ($file) {
4402 print(STDERR "Too few arguments: file name required\n");
4403 exit(1);
4404 } else {
4405 print(STDERR "Too few arguments: host name required\n");
4406 exit(1);
4407 }
4408}
4409if ($opt_C) {
4410 if ($file) {
4411 print "cp $srcfile $host.new\n";
4412 } else {
4413 print "cp /tftpboot/$srcfile $host.new\n";
4414 }
4415 exit(0);
4416}
4417
4418if ($file) {
4419 print STDERR "copying file $host\n" if ($debug);
4420 print STDOUT "copying file $host\n" if ($log);
4421 system("/bin/cp $srcfile $host.new");
4422} else {
4423 print STDERR "copying file $host\n" if ($debug);
4424 print STDOUT "copying file $host\n" if ($log);
4425 system("/bin/cp /tftpboot/$srcfile $host.new") == 0;
4426}
4427if (!$?) {
4428 print STDERR "copy failed: $!\n";
4429}
4430print STDOUT "Done cp: $_\n" if ($log);
4431
4432# check for completeness
4433if (scalar(%commands) || !$clean_run || !$found_end) {
4434 if (scalar(keys %commands) eq $commandcnt) {
4435 printf(STDERR "$host: missed cmd(s): all commands\n");
4436 } elsif (scalar(%commands)) {
4437 printf(STDOUT "$host: missed cmd(s): %s\n", join(',', keys(%commands)));
4438 printf(STDERR "$host: missed cmd(s): %s\n", join(',', keys(%commands))) if ($debug);
4439 }
4440 if (!$clean_run || !$found_end) {
4441 print STDOUT "$host: End of run not found\n";
4442 print STDERR "$host: End of run not found\n" if ($debug);
4443 system("/usr/bin/tail -1 $host.new");
4444 }
4445 unlink "$host.new" if (! $debug);
4446}
4447#! /usr/bin/perl
4448##
4449## $Id: srancid.in 3229 2016-01-19 22:51:52Z heas $
4450##
4451## rancid 3.4.1
4452## Copyright (c) 1997-2016 by Terrapin Communications, Inc.
4453## All rights reserved.
4454##
4455## This code is derived from software contributed to and maintained by
4456## Terrapin Communications, Inc. by Henry Kilmer, John Heasley, Andrew Partan,
4457## Pete Whiting, Austin Schutz, and Andrew Fort.
4458##
4459## Redistribution and use in source and binary forms, with or without
4460## modification, are permitted provided that the following conditions
4461## are met:
4462## 1. Redistributions of source code must retain the above copyright
4463## notice, this list of conditions and the following disclaimer.
4464## 2. Redistributions in binary form must reproduce the above copyright
4465## notice, this list of conditions and the following disclaimer in the
4466## documentation and/or other materials provided with the distribution.
4467## 3. All advertising materials mentioning features or use of this software
4468## must display the following acknowledgement:
4469## This product includes software developed by Terrapin Communications,
4470## Inc. and its contributors for RANCID.
4471## 4. Neither the name of Terrapin Communications, Inc. nor the names of its
4472## contributors may be used to endorse or promote products derived from
4473## this software without specific prior written permission.
4474## 5. It is requested that non-binding fixes and modifications be contributed
4475## back to Terrapin Communications, Inc.
4476##
4477## THIS SOFTWARE IS PROVIDED BY Terrapin Communications, INC. AND CONTRIBUTORS
4478## ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
4479## TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
4480## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COMPANY OR CONTRIBUTORS
4481## BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
4482## CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
4483## SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
4484## INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
4485## CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
4486## ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
4487## POSSIBILITY OF SUCH DAMAGE.
4488#
4489# Pretty huge hack to take care of Dell (aka. SMC) Switch configs; started by
4490# d_pfleger@juniper.net
4491#
4492# RANCID - Really Awesome New Cisco confIg Differ
4493#
4494# usage: srancid [-dltCV] [-f filename | hostname]
4495#
4496# Code tested and working fine on these models:
4497#
4498# DELL PowerConnect M8024 / M8024-k
4499# DELL PowerConnect M6348
4500# DELL PowerConnect N2048, N4032F and N4064.
4501# DELL PowerConnect 62xx
4502# DELL 34xx (partially; configuration is incomplete)
4503#
4504use Getopt::Std;
4505getopts('dflt:CV');
4506if ($opt_V) {
4507 print "rancid 3.4.1\n";
4508 exit(0);
4509}
4510$log = $opt_l;
4511$debug = $opt_d;
4512$file = $opt_f;
4513$host = $ARGV[0];
4514$found_end = 0;
4515$timeo = 90; # hlogin timeout in seconds
4516
4517my(@commandtable, %commands, @commands);# command lists
4518my($aclsort) = ("ipsort"); # ACL sorting mode
4519my($filter_commstr); # SNMP community string filtering
4520my($filter_pwds); # password filtering mode
4521
4522# This routine is used to print out the router configuration
4523sub ProcessHistory {
4524 my($new_hist_tag,$new_command,$command_string,@string) = (@_);
4525 if ((($new_hist_tag ne $hist_tag) || ($new_command ne $command))
4526 && scalar(%history)) {
4527 print eval "$command \%history";
4528 undef %history;
4529 }
4530 if (($new_hist_tag) && ($new_command) && ($command_string)) {
4531 if ($history{$command_string}) {
4532 $history{$command_string} = "$history{$command_string}@string";
4533 } else {
4534 $history{$command_string} = "@string";
4535 }
4536 } elsif (($new_hist_tag) && ($new_command)) {
4537 $history{++$#history} = "@string";
4538 } else {
4539 print "@string";
4540 }
4541 $hist_tag = $new_hist_tag;
4542 $command = $new_command;
4543 1;
4544}
4545
4546sub numerically { $a <=> $b; }
4547
4548# This is a sort routine that will sort numerically on the
4549# keys of a hash as if it were a normal array.
4550sub keynsort {
4551 local(%lines) = @_;
4552 local($i) = 0;
4553 local(@sorted_lines);
4554 foreach $key (sort numerically keys(%lines)) {
4555 $sorted_lines[$i] = $lines{$key};
4556 $i++;
4557 }
4558 @sorted_lines;
4559}
4560
4561# This is a sort routine that will sort on the
4562# keys of a hash as if it were a normal array.
4563sub keysort {
4564 local(%lines) = @_;
4565 local($i) = 0;
4566 local(@sorted_lines);
4567 foreach $key (sort keys(%lines)) {
4568 $sorted_lines[$i] = $lines{$key};
4569 $i++;
4570 }
4571 @sorted_lines;
4572}
4573
4574# This is a sort routine that will sort on the
4575# values of a hash as if it were a normal array.
4576sub valsort{
4577 local(%lines) = @_;
4578 local($i) = 0;
4579 local(@sorted_lines);
4580 foreach $key (sort values %lines) {
4581 $sorted_lines[$i] = $key;
4582 $i++;
4583 }
4584 @sorted_lines;
4585}
4586
4587# This is a numerical sort routine (ascending).
4588sub numsort {
4589 local(%lines) = @_;
4590 local($i) = 0;
4591 local(@sorted_lines);
4592 foreach $num (sort {$a <=> $b} keys %lines) {
4593 $sorted_lines[$i] = $lines{$num};
4594 $i++;
4595 }
4596 @sorted_lines;
4597}
4598
4599# This is a sort routine that will sort on the
4600# ip address when the ip address is anywhere in
4601# the strings.
4602sub ipsort {
4603 local(%lines) = @_;
4604 local($i) = 0;
4605 local(@sorted_lines);
4606 foreach $addr (sort sortbyipaddr keys %lines) {
4607 $sorted_lines[$i] = $lines{$addr};
4608 $i++;
4609 }
4610 @sorted_lines;
4611}
4612
4613# These two routines will sort based upon IP addresses
4614sub ipaddrval {
4615 my(@a) = ($_[0] =~ m#^(\d+)\.(\d+)\.(\d+)\.(\d+)$#);
4616 $a[3] + 256 * ($a[2] + 256 * ($a[1] +256 * $a[0]));
4617}
4618sub sortbyipaddr {
4619 &ipaddrval($a) <=> &ipaddrval($b);
4620}
4621
4622# This routine parses "dir"
4623sub Dir {
4624 print STDERR " In Dir: $_" if ($debug);
4625 $_ =~ s/^[^#]*//;
4626 ProcessHistory("COMMENTS","keysort","D1","!\n! $_");
4627
4628 while (<INPUT>) {
4629 s/^\s+\015//g;
4630 tr/\015//d;
4631 next if /^\s*$/;
4632 last if(/$prompt/);
4633 # pager remnants like: ^H^H^H ^H^H^H content
4634 s/[\b]+\s*[\b]*//g;
4635
4636 ProcessHistory("COMMENTS","keysort","D1","! $_");
4637 }
4638 ProcessHistory("COMMENTS","keysort","D1","!\n");
4639 return(0);
4640}
4641
4642sub ShowVer {
4643 print STDERR " In ShowVer: $_" if ($debug);
4644
4645 while (<INPUT>) {
4646 tr/\015//d;
4647 next if /^\s*$/;
4648 last if(/$prompt/);
4649 # pager remnants like: ^H^H^H ^H^H^H content
4650 s/[\b]+\s*[\b]*//g;
4651
4652 # Remove Uptime
4653 / up time/i && next;
4654
4655 ProcessHistory("COMMENTS","keysort","B1","! $_");
4656 }
4657 return(0);
4658}
4659
4660sub ShowSys {
4661 print STDERR " In ShowSys: $_" if ($debug);
4662 $_ =~ s/^[^#]*//;
4663 ProcessHistory("COMMENTS","keysort","C1","!\n! $_");
4664
4665 while (<INPUT>) {
4666 s/^\s+\015//g;
4667 tr/\015//d;
4668 next if /^\s*$/;
4669 last if(/$prompt/);
4670 # pager remnants like: ^H^H^H ^H^H^H content
4671 s/[\b]+\s*[\b]*//g;
4672
4673 # Remove Uptime
4674 / up time/i && next;
4675
4676 # filter temperature sensor info for Dell 6428 stacks
4677 # /Temperature Sensors:/
4678 if (/Temperature \(Celsius\)/) {
4679 ProcessHistory("COMMENTS","keysort","C1","! $_");
4680 ProcessHistory("COMMENTS","keysort","C1","! Unit\tStatus\n");
4681 ProcessHistory("COMMENTS","keysort","C1","! ----\t------\n");
4682 while (<INPUT>) {
4683 s/^\s+\015//g;
4684 tr/\015//d;
4685 /(\d+)\s+\d+\s+(.*)$/ &&
4686 ProcessHistory("COMMENTS","keysort","C1","! $1\t$2\n");
4687 /^\s*$/ && last;
4688 }
4689 } elsif (/Temperature/) {
4690 # Filter temperature sensor info for Dell M6348 and M8024 blade
4691 # switches.
4692 #
4693 # M6348 and M8024 sample lines:
4694 # Unit Description Temperature Status
4695 # (Celsius)
4696 # ---- ----------- ----------- ------
4697 # 1 System 39 Good
4698 # 2 System 39 Good
4699 ProcessHistory("COMMENTS","keysort","C1",
4700 "! Unit\tDescription\tStatus\n");
4701 ProcessHistory("COMMENTS","keysort","C1",
4702 "! ----\t-----------\t------\n");
4703 while (<INPUT>) {
4704 /\(celsius\)/i && next;
4705 s/^\s+\015//g;
4706 tr/\015//d;
4707 /(\d+)\s+(\w+)\s+\d+\s+(.*)$/ &&
4708 ProcessHistory("COMMENTS","keysort","C1","! $1\t$2\t\t$3\n");
4709 /^\s*$/ && last;
4710 }
4711 }
4712
4713 /system description: (.*)/i &&
4714 ProcessHistory("COMMENTS","keysort","A1", "!Chassis type: $1\n") &&
4715 next;
4716
4717 ProcessHistory("COMMENTS","keysort","C1","! $_");
4718 }
4719 return(0);
4720}
4721
4722sub ShowVlan {
4723 print STDERR " In ShowVlan: $_" if ($debug);
4724 $_ =~ s/^[^#]*//;
4725 ProcessHistory("COMMENTS","keysort","D1","!\n! $_");
4726
4727 while (<INPUT>) {
4728 s/^\s+\015//g;
4729 tr/\015//d;
4730 next if /^\s*$/;
4731 last if(/$prompt/);
4732 # pager remnants like: ^H^H^H ^H^H^H content
4733 s/[\b]+\s*[\b]*//g;
4734
4735 # Remove Uptime
4736 / up time/i && next;
4737 ProcessHistory("COMMENTS","keysort","D1","! $_");
4738 }
4739 return(0);
4740}
4741
4742# This routine processes a "write term" (aka show running-configuration)
4743sub WriteTerm {
4744 my($comment) = (0);
4745 print STDERR " In ShowRun: $_" if ($debug);
4746
4747 while (<INPUT>) {
4748 tr/\015//d;
4749 next if /^\s*$/;
4750 last if(/$prompt/);
4751 # pager remnants like: ^H^H^H ^H^H^H content
4752 s/[\b]+\s*[\b]*//g;
4753
4754 # skip consecutive comment lines
4755 if (/^!/) {
4756 next if ($comment);
4757 ProcessHistory("","","",$_);
4758 $comment++;
4759 next;
4760 }
4761 $comment = 0;
4762
4763 /^building running-config/ && next;
4764 /^------+/ && ProcessHistory("","","","!$_") && next;
4765 /^router configuration/i && ProcessHistory("","","","!$_") && next;
4766 /^oob host config/i && ProcessHistory("","","","!$_") && next;
4767 /^empty configuration/i && ProcessHistory("","","","!$_") && next;
4768
4769 if (/^password (\S+) encrypted/ && $filter_pwds > 1) {
4770 ProcessHistory("","","","!password <removed> encrypted\n");
4771 next;
4772 }
4773 if (/^password (\S+)$/ && $filter_pwds >= 1) {
4774 ProcessHistory("","","","!password <removed>\n");
4775 next;
4776 }
4777
4778 if (/^(enable password level \d+) (\S+) encrypted/ && $filter_pwds > 1){
4779 ProcessHistory("","","","!$1 <removed> encrypted\n");
4780 next;
4781 }
4782 if (/^(enable password level \d+) (\S+)$/ && $filter_pwds >= 1) {
4783 ProcessHistory("","","","!$1 <removed> $'\n");
4784 next;
4785 }
4786
4787 # order/prune snmp-server host statements
4788 # we only prune lines of the form
4789 # snmp-server host a.b.c.d <community>
4790 if (/^(snmp-server host) (\d+\.\d+\.\d+\.\d+) (\S+)/) {
4791 if ($filter_commstr) {
4792 ProcessHistory("SNMPSERVERHOST","ipsort",
4793 "$2","!$1 $2 <removed>$'");
4794 } else {
4795 ProcessHistory("SNMPSERVERHOST","ipsort","$2","$_");
4796 }
4797 next;
4798 }
4799 if (/^(snmp-server community) (\S+)/) {
4800 if ($filter_commstr) {
4801 ProcessHistory("SNMPSERVERCOMM","keysort",
4802 "$_","!$1 <removed>$'") && next;
4803 } else {
4804 ProcessHistory("SNMPSERVERCOMM","keysort","$2","$_") && next;
4805 }
4806 }
4807
4808 # prune tacacs/radius server keys
4809 if (/^(tacacs-server|radius-server) key \w+/ && $filter_pwds >= 1) {
4810 ProcessHistory("","","","!$1 <removed>$'"); next;
4811 }
4812
4813 ProcessHistory("","","","$_");
4814 }
4815 $found_end = 1;
4816 return(1);
4817}
4818
4819# Main
4820@commandtable = (
4821 {'show system' => 'ShowSys'},
4822 {'show version' => 'ShowVer'},
4823 {'dir' => 'Dir'},
4824 {'show vlan' => 'ShowVlan'},
4825 {'show running-config' => 'WriteTerm'}
4826);
4827# Use an array to preserve the order of the commands and a hash for mapping
4828# commands to the subroutine and track commands that have been completed.
4829@commands = map(keys(%$_), @commandtable);
4830%commands = map(%$_, @commandtable);
4831$commandcnt = scalar(keys %commands);
4832
4833$commandstr=join(";",@commands);
4834$cmds_regexp = join("|", map quotemeta($_), @commands);
4835
4836if (length($host) == 0) {
4837 if ($file) {
4838 print(STDERR "Too few arguments: file name required\n");
4839 exit(1);
4840 } else {
4841 print(STDERR "Too few arguments: host name required\n");
4842 exit(1);
4843 }
4844}
4845if ($opt_C) {
4846 print "hlogin -t $timeo -c\'$commandstr\' $host\n";
4847 exit(0);
4848}
4849open(OUTPUT,">$host.new") || die "Can't open $host.new for writing: $!\n";
4850select(OUTPUT);
4851# make OUTPUT unbuffered if debugging
4852if ($debug) { $| = 1; }
4853
4854if ($file) {
4855 print STDERR "opening file $host\n" if ($debug);
4856 print STDOUT "opening file $host\n" if ($log);
4857 open(INPUT,"<$host") || die "open failed for $host: $!\n"; } else {
4858 print STDERR "executing hlogin -t $timeo -c\"$commandstr\" $host\n" if ($debug);
4859 print STDOUT "executing hlogin -t $timeo -c\"$commandstr\" $host\n" if ($log);
4860 if (defined($ENV{NOPIPE}) && $ENV{NOPIPE} =~ /^YES/i) {
4861 system "hlogin -t $timeo -c \"$commandstr\" $host </dev/null > $host.raw 2>&1" || die "hlogin failed for $host: $!\n";
4862 open(INPUT, "< $host.raw") || die "hlogin failed for $host: $!\n";
4863 } else {
4864 open(INPUT,"hlogin -t $timeo -c \"$commandstr\" $host </dev/null |") || die "hlogin failed for $host: $!\n";
4865 }
4866}
4867
4868# determine ACL sorting mode
4869if ($ENV{"ACLSORT"} =~ /no/i) {
4870 $aclsort = "";
4871}
4872# determine community string filtering mode
4873if (defined($ENV{"NOCOMMSTR"}) &&
4874 ($ENV{"NOCOMMSTR"} =~ /yes/i || $ENV{"NOCOMMSTR"} =~ /^$/)) {
4875 $filter_commstr = 1;
4876} else {
4877 $filter_commstr = 0;
4878}
4879# determine password filtering mode
4880if ($ENV{"FILTER_PWDS"} =~ /no/i) {
4881 $filter_pwds = 0;
4882} elsif ($ENV{"FILTER_PWDS"} =~ /all/i) {
4883 $filter_pwds = 2;
4884} else {
4885 $filter_pwds = 1;
4886}
4887
4888ProcessHistory("","","","!RANCID-CONTENT-TYPE: smc\n!\n");
4889ProcessHistory("COMMENTS","keysort","A0","!\n");
4890ProcessHistory("COMMENTS","keysort","B0","!\n");
4891ProcessHistory("COMMENTS","keysort","C0","!\n");
4892ProcessHistory("COMMENTS","keysort","D0","!\n");
4893TOP: while(<INPUT>) {
4894 tr/\015//d;
4895 if (/^Error:/) {
4896 print STDOUT ("$host hlogin error: $_");
4897 print STDERR ("$host hlogin error: $_") if ($debug);
4898 last;
4899 }
4900 while (/#\s*($cmds_regexp)\s*$/) {
4901 $cmd = $1;
4902 if (!defined($prompt)) {
4903 $prompt = ($_ =~ /^([^#]+#)/)[0];
4904 $prompt =~ s/([][}{)(\\])/\\$1/g;
4905 print STDERR ("PROMPT MATCH: $prompt\n") if ($debug);
4906 }
4907 print STDERR ("HIT COMMAND:$_") if ($debug);
4908 if (!defined($commands{$cmd})) {
4909 print STDERR "$host: found unexpected command - \"$cmd\"\n";
4910 last TOP;
4911 }
4912 $rval = &{$commands{$cmd}}(*INPUT, *OUTPUT, $cmd);
4913 delete($commands{$cmd});
4914 if ($rval == -1) {
4915 last TOP;
4916 }
4917 }
4918}
4919print STDOUT "Done $logincmd: $_\n" if ($log);
4920# Flush History
4921ProcessHistory("","","","");
4922# Cleanup
4923close(INPUT);
4924close(OUTPUT);
4925
4926if (defined($ENV{NOPIPE}) && $ENV{NOPIPE} =~ /^YES/i) {
4927 unlink("$host.raw") if (! $debug);
4928}
4929
4930# check for completeness
4931if (scalar(%commands) || !$found_end) {
4932 if (scalar(keys %commands) eq $commandcnt) {
4933 printf(STDERR "$host: missed cmd(s): all commands\n");
4934 } elsif (scalar(%commands)) {
4935 printf(STDOUT "$host: missed cmd(s): %s\n", join(',', keys(%commands)));
4936 printf(STDERR "$host: missed cmd(s): %s\n", join(',', keys(%commands))) if ($debug);
4937 }
4938 if (!$found_end) {
4939 print STDOUT "$found_end: found end\n";
4940 print STDOUT "$host: End of run not found\n";
4941 print STDERR "$host: End of run not found\n" if ($debug);
4942 system("/usr/bin/tail -1 $host.new");
4943 }
4944 unlink "$host.new" if (! $debug);
4945}
4946#! /usr/bin/expect --
4947##
4948## $Id: tlogin.in 3201 2015-11-05 00:48:01Z heas $
4949##
4950## rancid 3.4.1
4951## Copyright (c) 1997-2016 by Terrapin Communications, Inc.
4952## All rights reserved.
4953##
4954## This code is derived from software contributed to and maintained by
4955## Terrapin Communications, Inc. by Henry Kilmer, John Heasley, Andrew Partan,
4956## Pete Whiting, Austin Schutz, and Andrew Fort.
4957##
4958## Redistribution and use in source and binary forms, with or without
4959## modification, are permitted provided that the following conditions
4960## are met:
4961## 1. Redistributions of source code must retain the above copyright
4962## notice, this list of conditions and the following disclaimer.
4963## 2. Redistributions in binary form must reproduce the above copyright
4964## notice, this list of conditions and the following disclaimer in the
4965## documentation and/or other materials provided with the distribution.
4966## 3. All advertising materials mentioning features or use of this software
4967## must display the following acknowledgement:
4968## This product includes software developed by Terrapin Communications,
4969## Inc. and its contributors for RANCID.
4970## 4. Neither the name of Terrapin Communications, Inc. nor the names of its
4971## contributors may be used to endorse or promote products derived from
4972## this software without specific prior written permission.
4973## 5. It is requested that non-binding fixes and modifications be contributed
4974## back to Terrapin Communications, Inc.
4975##
4976## THIS SOFTWARE IS PROVIDED BY Terrapin Communications, INC. AND CONTRIBUTORS
4977## ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
4978## TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
4979## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COMPANY OR CONTRIBUTORS
4980## BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
4981## CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
4982## SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
4983## INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
4984## CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
4985## ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
4986## POSSIBILITY OF SUCH DAMAGE.
4987##
4988## It is the request of the authors, but not a condition of license, that
4989## parties packaging or redistributing RANCID NOT distribute altered versions
4990## of the etc/rancid.types.base file nor alter how this file is processed nor
4991## when in relation to etc/rancid.types.conf. The goal of this is to help
4992## suppress our support costs. If it becomes a problem, this could become a
4993## condition of license.
4994#
4995# The expect login scripts were based on Erik Sherk's gwtn, by permission.
4996#
4997# The original looking glass software was written by Ed Kern, provided by
4998# permission and modified beyond recognition.
4999#
5000# The login expect scripts were based on Erik Sherk's gwtn, by permission.
5001#
5002# tlogin - Netopis login
5003#
5004# Modified by Ed Ravin for Netopia.
5005
5006# Usage line
5007set usage "Usage: $argv0 \[-dSV\] \[-autoenable\] \[-noenable\] \[-c command\] \
5008\[-Evar=x\] \[-e enable-password\] \[-f cloginrc-file\] \[-p user-password\] \
5009\[-s script-file\] \[-t timeout\] \[-u username\] \
5010\[-V\] \[-v vty-password\] \[-w enable-username\] \[-x command-file\] \
5011\[-y ssh_cypher_type\] router \[router...\]\n"
5012
5013# env(CLOGIN) may contain:
5014# x == do not set xterm banner or name
5015
5016# Password file
5017set password_file $env(HOME)/.cloginrc
5018# Default is to login to the router
5019set do_command 0
5020set do_script 0
5021# The default is to automatically enable
5022set avenable 1
5023# The default is that you login non-enabled (tacacs can have you login already
5024# enabled)
5025set avautoenable 0
5026# The default is to look in the password file to find the passwords. This
5027# tracks if we receive them on the command line.
5028set do_passwd 1
5029set do_enapasswd 1
5030# Sometimes routers take awhile to answer (the default is 10 sec)
5031set timeoutdflt 45
5032# Some CLIs having problems if we write too fast (Extreme, PIX, Cat)
5033set send_human {.2 .1 .4 .2 1}
5034# attempt at platform switching.
5035set platform ""
5036
5037# Find the user in the ENV, or use the unix userid.
5038if {[info exists env(CISCO_USER)] } {
5039 set default_user $env(CISCO_USER)
5040} elseif {[info exists env(USER)]} {
5041 set default_user $env(USER)
5042} elseif {[info exists env(LOGNAME)]} {
5043 set default_user $env(LOGNAME)
5044} else {
5045 # This uses "id" which I think is portable. At least it has existed
5046 # (without options) on all machines/OSes I've been on recently -
5047 # unlike whoami or id -nu.
5048 if [catch {exec id} reason] {
5049 send_error "\nError: could not exec id: $reason\n"
5050 exit 1
5051 }
5052 regexp {\(([^)]*)} "$reason" junk default_user
5053}
5054
5055# Process the command line
5056for {set i 0} {$i < $argc} {incr i} {
5057 set arg [lindex $argv $i]
5058
5059 switch -glob -- $arg {
5060 } -d {
5061 exp_internal 1
5062 # Username
5063 } -u* {
5064 if {! [regexp .\[uU\](.+) $arg ignore user]} {
5065 incr i
5066 set username [lindex $argv $i]
5067 }
5068 # VTY Password
5069 } -p* {
5070 if {! [regexp .\[pP\](.+) $arg ignore userpasswd]} {
5071 incr i
5072 set userpasswd [lindex $argv $i]
5073 }
5074 set do_passwd 0
5075 # ssh passphrase
5076 } -r* {
5077 # ignore -r
5078 # VTY Password
5079 } -v* {
5080 if {! [regexp .\[vV\](.+) $arg ignore passwd]} {
5081 incr i
5082 set passwd [lindex $argv $i]
5083 }
5084 set do_passwd 0
5085 # Enable Username
5086 } -w* {
5087 if {! [regexp .\[wW\](.+) $arg ignore enauser]} {
5088 incr i
5089 set enausername [lindex $argv $i]
5090 }
5091 # Environment variable to pass to -s scripts
5092 } -E* {
5093 if {[regexp .\[E\](.+)=(.+) $arg ignore varname varvalue]} {
5094 set E$varname $varvalue
5095 } else {
5096 send_user "\nError: invalid format for -E in $arg\n"
5097 exit 1
5098 }
5099 # Enable Password
5100 } -e* {
5101 if {! [regexp .\[e\](.+) $arg ignore enapasswd]} {
5102 incr i
5103 set enapasswd [lindex $argv $i]
5104 }
5105 set do_enapasswd 0
5106 # Command to run.
5107 } -c* {
5108 if {! [regexp .\[cC\](.+) $arg ignore command]} {
5109 incr i
5110 set command [lindex $argv $i]
5111 }
5112 set do_command 1
5113 # Expect script to run.
5114 } -s* {
5115 if {! [regexp .\[sS\](.+) $arg ignore sfile]} {
5116 incr i
5117 set sfile [lindex $argv $i]
5118 }
5119 if { ! [file readable $sfile] } {
5120 send_user "\nError: Can't read $sfile\n"
5121 exit 1
5122 }
5123 set do_script 1
5124 # save config on exit
5125 } -S* {
5126 set do_saveconfig 1
5127 # 'ssh -c' cypher type
5128 } -y* {
5129 if {! [regexp .\[eE\](.+) $arg ignore cypher]} {
5130 incr i
5131 set cypher [lindex $argv $i]
5132 }
5133 # alternate cloginrc file
5134 } -f* {
5135 if {! [regexp .\[fF\](.+) $arg ignore password_file]} {
5136 incr i
5137 set password_file [lindex $argv $i]
5138 }
5139 # Timeout
5140 } -t* {
5141 if {! [regexp .\[tT\](.+) $arg ignore timeout]} {
5142 incr i
5143 set timeoutdflt [lindex $argv $i]
5144 }
5145 # Command file
5146 } -x* {
5147 if {! [regexp .\[xX\](.+) $arg ignore cmd_file]} {
5148 incr i
5149 set cmd_file [lindex $argv $i]
5150 }
5151 if [catch {set cmd_fd [open $cmd_file r]} reason] {
5152 send_user "\nError: $reason\n"
5153 exit 1
5154 }
5155 set cmd_text [read $cmd_fd]
5156 close $cmd_fd
5157 set command [join [split $cmd_text \n] \;]
5158 set do_command 1
5159 # Version string
5160 } -V* {
5161 send_user "rancid 3.4.1\n"
5162 exit 0
5163 # Do we enable?
5164 } -noenable {
5165 set avenable 0
5166 # Does tacacs automatically enable us?
5167 } -autoenable {
5168 set avautoenable 1
5169 set avenable 0
5170 } -* {
5171 send_user "\nError: Unknown argument! $arg\n"
5172 send_user $usage
5173 exit 1
5174 } default {
5175 break
5176 }
5177 }
5178}
5179# Process routers...no routers listed is an error.
5180if { $i == $argc } {
5181 send_user "\nError: $usage"
5182}
5183
5184# Only be quiet if we are running a script (it can log its output
5185# on its own)
5186if { $do_script } {
5187 log_user 0
5188} else {
5189 log_user 1
5190}
5191
5192#
5193# Done configuration/variable setting. Now run with it...
5194#
5195
5196# Sets Xterm title if interactive...if its an xterm and the user cares
5197proc label { host } {
5198 global env
5199 # if CLOGIN has an 'x' in it, don't set the xterm name/banner
5200 if [info exists env(CLOGIN)] {
5201 if {[string first "x" $env(CLOGIN)] != -1} { return }
5202 }
5203 # take host from ENV(TERM)
5204 if [info exists env(TERM)] {
5205 if [regexp \^(xterm|vs) $env(TERM) ignore] {
5206 send_user "\033]1;[lindex [split $host "."] 0]\a"
5207 send_user "\033]2;$host\a"
5208 }
5209 }
5210}
5211
5212# This is a helper function to make the password file easier to
5213# maintain. Using this the password file has the form:
5214# add password sl* pete cow
5215# add password at* steve
5216# add password * hanky-pie
5217proc add {var args} { global int_$var ; lappend int_$var $args}
5218proc include {args} {
5219 global env
5220 regsub -all "(^{|}$)" $args {} args
5221 if { [regexp "^/" $args ignore] == 0 } {
5222 set args $env(HOME)/$args
5223 }
5224 source_password_file $args
5225}
5226
5227proc find {var router} {
5228 upvar int_$var list
5229 if { [info exists list] } {
5230 foreach line $list {
5231 if { [string match -nocase [lindex $line 0] $router] } {
5232 return [lrange $line 1 end]
5233 }
5234 }
5235 }
5236 return {}
5237}
5238
5239# Loads the password file. Note that as this file is tcl, and that
5240# it is sourced, the user better know what to put in there, as it
5241# could install more than just password info... I will assume however,
5242# that a "bad guy" could just as easy put such code in the clogin
5243# script, so I will leave .cloginrc as just an extention of that script
5244proc source_password_file { password_file } {
5245 global env
5246 if { ! [file exists $password_file] } {
5247 send_user "\nError: password file ($password_file) does not exist\n"
5248 exit 1
5249 }
5250 file stat $password_file fileinfo
5251 if { [expr ($fileinfo(mode) & 007)] != 0000 } {
5252 send_user "\nError: $password_file must not be world readable/writable\n"
5253 exit 1
5254 }
5255 if [catch {source $password_file} reason] {
5256 send_user "\nError: $reason\n"
5257 exit 1
5258 }
5259}
5260
5261# Log into the router.
5262proc login { router user userpswd passwd enapasswd cmethod cyphertype } {
5263 global spawn_id in_proc do_command do_script platform
5264 global prompt u_prompt p_prompt e_prompt sshcmd usercmd usercmd_chat
5265 global otpinuse
5266 set in_proc 1
5267 set uprompt_seen 0
5268
5269 # try each of the connection methods in $cmethod until one is successful
5270 set progs [llength $cmethod]
5271 foreach prog [lrange $cmethod 0 end] {
5272 if [string match "telnet*" $prog] {
5273 regexp {telnet(:([^[:space:]]+))*} $prog command suffix port
5274 if {"$port" == ""} {
5275 set retval [catch {spawn telnet $router} reason]
5276 } else {
5277 set retval [catch {spawn telnet $router $port} reason]
5278 }
5279 if { $retval } {
5280 send_user "\nError: telnet failed: $reason\n"
5281 exit 1
5282 }
5283 } elseif [string match "ssh*" $prog] {
5284 regexp {ssh(:([^[:space:]]+))*} $prog command suffix port
5285 set cmd $sshcmd
5286 if {"$port" != ""} {
5287 set cmd "$cmd -p $port"
5288 }
5289 set retval [catch {eval spawn [split "$cmd -c $cyphertype -x -l $user $router" { }]} reason]
5290 if { $retval } {
5291 send_user "\nError: $cmd failed: $reason\n"
5292 exit 1
5293 }
5294 } elseif [string match "usercmd" $prog] { # user supplies connect cmd
5295 set retval [catch {eval spawn $usercmd} reason]
5296 if { $retval } {
5297 send_user "\nError: '$usercmd' failed: $reason\n"
5298 exit 1
5299 }
5300 if { [llength $usercmd_chat] > 0 } {
5301 #send_user "\nExecuting usercmd_chat: $usercmd_chat\n"
5302 sleep 0.3
5303 foreach {i j} $usercmd_chat {
5304 expect {
5305 -re $i { eval send -- "\"$j\""}
5306 timeout { send "\r"; send_user "\nTimeout in usercmd_chat waiting for -re $i: punting with CR\n"; break }
5307 }
5308 }
5309 }
5310 } elseif ![string compare $prog "rsh"] {
5311 if [catch {spawn rsh -l $user $router} reason] {
5312 send_user "\nError: rsh failed: $reason\n"
5313 exit 1
5314 }
5315 } else {
5316 puts "\nError: unknown connection method: $prog"
5317 return 1
5318 }
5319 incr progs -1
5320 sleep 0.3
5321
5322 # This helps cleanup each expect clause.
5323 expect_after {
5324 timeout {
5325 send_user "\nError: TIMEOUT reached\n"
5326 catch {close}; wait
5327 if { $in_proc} {
5328 return 1
5329 } else {
5330 continue
5331 }
5332 } eof {
5333 send_user "\nError: EOF received\n"
5334 catch {close}; wait
5335 if { $in_proc} {
5336 return 1
5337 } else {
5338 continue
5339 }
5340 }
5341 }
5342
5343 # Here we get a little tricky. There are several possibilities:
5344 # the router can ask for a username and passwd and then
5345 # talk to the TACACS server to authenticate you, or if the
5346 # TACACS server is not working, then it will use the enable
5347 # passwd. Or, the router might not have TACACS turned on,
5348 # then it will just send the passwd.
5349 # if telnet fails with connection refused, try ssh
5350 expect {
5351 -re "(Connection refused|Secure connection \[^\n\r]+ refused)" {
5352 catch {close}; wait
5353 if !$progs {
5354 send_user "\nError: Connection Refused ($prog): $router\n"
5355 return 1
5356 }
5357 }
5358 -re "(Connection closed by|Connection to \[^\n\r]+ closed)" {
5359 catch {close}; wait
5360 if !$progs {
5361 send_user "\nError: Connection closed ($prog): $router\n"
5362 return 1
5363 }
5364 }
5365 eof { send_user "\nError: Couldn't login: $router\n"; wait; return 1 }
5366 -nocase "unknown host\r" {
5367 catch {close};
5368 send_user "\nError: Unknown host $router\n"; wait; return 1
5369 }
5370 "Host is unreachable" {
5371 catch {close};
5372 send_user "\nError: Host Unreachable: $router\n"; wait; return 1
5373 }
5374 "No address associated with name" {
5375 catch {close};
5376 send_user "\nError: Unknown host $router\n"; wait; return 1
5377 }
5378 -re "(Host key not found |The authenticity of host .* be established).* \\(yes/no\\)\\?" {
5379 send "yes\r"
5380 send_user "\nHost $router added to the list of known hosts.\n"
5381 exp_continue }
5382 -re "HOST IDENTIFICATION HAS CHANGED.* \\(yes/no\\)\\?" {
5383 send "no\r"
5384 send_user "\nError: The host key for $router has changed. Update the SSH known_hosts file accordingly.\n"
5385 return 1 }
5386 -re "HOST IDENTIFICATION HAS CHANGED\[^\n\r]+" {
5387 send_user "\nError: The host key for $router has changed. Update the SSH known_hosts file accordingly.\n"
5388 return 1
5389 }
5390 -re "Offending key for .* \\(yes/no\\)\\?" {
5391 send "no\r"
5392 send_user "\nError: host key mismatch for $router. Update the SSH known_hosts file accordingly.\n"
5393 return 1 }
5394 -nocase -re "^warning: remote host denied authentication agent forwarding." {
5395 exp_continue;
5396 }
5397 -re "(denied|Sorry)" {
5398 send_user "\nError: Check your passwd for $router\n"
5399 catch {close}; wait; return 1
5400 }
5401 "Login failed" {
5402 send_user "\nError: Check your passwd for $router\n"
5403 return 1
5404 }
5405 -re "% (Bad passwords|Authentication failed)" {
5406 send_user "\nError: Check your passwd for $router\n"
5407 return 1
5408 }
5409 "Press any key to continue." {
5410 # send_user "Pressing the ANY key\n"
5411 send "\r"
5412 exp_continue
5413 }
5414 -re "Enter Selection: " {
5415 # Catalyst 1900s have some lame menu. Enter
5416 # K to reach a command-line.
5417 send "K\r"
5418 exp_continue;
5419 }
5420 -re "Netopia.*always start from this main screen" {
5421 # send control-N to escape from the Playskool menu
5422 send -- "\x0e"
5423 set platform "netopia"
5424 set prompt "#"
5425 set autoenable 1
5426 return 0
5427 }
5428 -re "@\[^\r\n]+ $p_prompt" {
5429 # ssh pwd prompt
5430 sleep 1
5431 send -- "$userpswd\r"
5432 exp_continue
5433 }
5434 -re "$u_prompt" {
5435 send -- "$user\r"
5436 set uprompt_seen 1
5437 exp_continue
5438 }
5439 -re "(s/key|otp-\[0-9a-zA-Z]+) +\[0-9]+ +\[-0-9a-zA-Z]+\[ \r\n]" {
5440 if { !$otpinuse} {
5441 exp_continue
5442 }
5443 set challenge $expect_out(0,string)
5444 regsub {[ \r\n]$} $challenge {} challenge
5445 if [catch {exec otphelper $router "$challenge"} userpswd] {
5446 send_error "\nError: login: 'otphelper $router $challenge' failed.\nRun otphelper standalone to diagnose further.\n"
5447 exit 1
5448 }
5449 exp_continue
5450 }
5451 -re "$p_prompt" {
5452 sleep 1
5453 if {$uprompt_seen == 1} {
5454 send -- "$userpswd\r"
5455 } else {
5456 send -- "$passwd\r"
5457 }
5458 exp_continue
5459 }
5460 -re "$prompt" { break; }
5461 "Login invalid" {
5462 send_user "\nError: Invalid login: $router\n";
5463 catch {close}; wait; return 1
5464 }
5465 }
5466 }
5467
5468 set in_proc 0
5469 return 0
5470}
5471
5472# Enable
5473proc do_enable { enauser enapasswd } {
5474 global prompt in_proc
5475 global u_prompt e_prompt
5476 global router otpinuse
5477 set in_proc 1
5478
5479 send "enable\r"
5480 expect {
5481 -re "(s/key|otp-\[0-9a-zA-Z]+) +\[0-9]+ +\[-0-9a-zA-Z]+\[ \r\n]" {
5482 if { !$otpinuse} {
5483 exp_continue
5484 }
5485 set challenge $expect_out(0,string)
5486 regsub {[ \r\n]$} $challenge {} challenge
5487 if [catch {exec otphelper $router "$challenge"} enapasswd] {
5488 send_error "\nError: enable: 'otphelper $router $challenge' failed.\nRun otphelper standalone to diagnose further.\n"
5489 exit 1
5490 }
5491 exp_continue
5492 }
5493 -re "$u_prompt" { send -- "$enauser\r"; exp_continue}
5494 -re "$e_prompt" { send -- "$enapasswd\r"; exp_continue}
5495 "#" { set prompt "#" }
5496 "(enable)" { set prompt "> (enable) " }
5497 -re "(denied|Sorry|Incorrect)" {
5498 # % Access denied - from local auth and poss. others
5499 send_user "\nError: Check your Enable passwd\n";
5500 return 1
5501 }
5502 "% Error in authentication" {
5503 send_user "\nError: Check your Enable passwd\n"
5504 return 1
5505 }
5506 "% Bad passwords" {
5507 send_user "\nError: Check your Enable passwd\n"
5508 return 1
5509 }
5510 }
5511 # We set the prompt variable (above) so script files don't need
5512 # to know what it is.
5513 set in_proc 0
5514 return 0
5515}
5516
5517# Run commands given on the command line.
5518proc run_commands { prompt command } {
5519 global in_proc platform
5520 set in_proc 1
5521
5522 # If the prompt is (enable), then we are on a switch and the
5523 # command is "set length 0"; otherwise its "term length 0".
5524 # skip if its an extreme (since the pager can not be disabled on a
5525 # per-vty basis).
5526 if { [string compare "extreme" "$platform"] } {
5527 if [regexp -- ".*> .*enable" "$prompt"] {
5528 send "set length 0\r"
5529 # This is ugly, but reduces code duplication, allowing the
5530 # subsequent expects to handle everything as normal.
5531 set command "set logging session disable;$command"
5532 } elseif { ![string compare "netopia" "$platform"] } {
5533 # kludge - should instead skip re-sensing prompt if platform netopia
5534 set prompt "#"
5535 } else {
5536 send "term length 0\r"
5537 }
5538 # match cisco config mode prompts too, such as router(config-if)#
5539 regsub -all {^(.{1,14}).*([#>])$} $prompt {\1} reprompt
5540 # escape any parens in the prompt, such as "(enable)"
5541 regsub -all {[)(]} $reprompt {\\&} reprompt
5542 append reprompt {([^#>\r\n]+)?[#>](\\([^)\\r\\n]+\\))?}
5543 expect {
5544 -re $reprompt {}
5545 -re "\[\n\r]+" { exp_continue }
5546 }
5547 } else {
5548 regsub -all "\[)(]" $prompt {\\&} reprompt
5549 }
5550
5551 # this is the only way i see to get rid of more prompts in o/p..grrrrr
5552 log_user 0
5553
5554 # handle escaped ;s in commands, and ;; and ^;
5555 regsub -all {([^\\]);;} $command "\\1;\u002;" esccommand
5556 regsub {^;} $esccommand "\u002;" command
5557 set sep "\\1\u001"
5558 regsub -all {([^\\])\;} $command "$sep" esccommand
5559 set sep "\u001"
5560 set commands [split $esccommand $sep]
5561 set num_commands [llength $commands]
5562 # the pager can not be turned off on the PIX, so we have to look
5563 # for the "More" prompt. the extreme is equally obnoxious, with a
5564 # global switch in the config.
5565 for {set i 0} {$i < $num_commands} { incr i} {
5566 send -- "[subst -nocommands [lindex $commands $i]]\r"
5567 expect {
5568 -re "\b+" { exp_continue }
5569 -re "^\[^\n\r *]*$reprompt" { send_user -- "$expect_out(buffer)"
5570 }
5571 -re "^\[^\n\r]*$reprompt." { send_user -- "$expect_out(buffer)"
5572 exp_continue }
5573 -re "\[\n\r]+" { send_user -- "$expect_out(buffer)"
5574 exp_continue }
5575 -re "\[^\r\n]*Press <SPACE> to cont\[^\r\n]*" {
5576 send " "
5577 # bloody ^[[2K after " "
5578 expect {
5579 -re "^\[^\r\n]*\r" {}
5580 }
5581 exp_continue
5582 }
5583 -re "^ *--More--\[^\n\r]*" {
5584 send " "
5585 exp_continue }
5586 -re "^<-+ More -+>\[^\n\r]*" {
5587 send_user -- "$expect_out(buffer)"
5588 send " "
5589 exp_continue }
5590 }
5591 }
5592 log_user 1
5593
5594 if { [string compare "extreme" "$platform"] } {
5595 send "exit\r"
5596 } else {
5597 send "quit\r"
5598 }
5599 expect {
5600 -re "^\[^\n\r *]*$reprompt" {
5601 # the Cisco CE and Jnx ERX
5602 # return to non-enabled mode
5603 # on exit in enabled mode.
5604 send "exit\r"
5605 exp_continue;
5606 }
5607 "Do you wish to save your configuration changes" {
5608 send "n\r"
5609 exp_continue
5610 }
5611 -re "\[\n\r]+" { exp_continue }
5612 timeout { close; return 0 }
5613 eof { return 0 }
5614 }
5615 set in_proc 0
5616}
5617
5618#
5619# For each router... (this is main loop)
5620#
5621source_password_file $password_file
5622set in_proc 0
5623foreach router [lrange $argv $i end] {
5624 set router [string tolower $router]
5625 send_user "$router\n"
5626
5627 # device timeout
5628 set timeout [find timeout $router]
5629 if { [llength $timeout] == 0 } {
5630 set timeout $timeoutdflt
5631 }
5632
5633 # Figure out prompt.
5634 # Since autoenable is off by default, if we have it defined, it
5635 # was done on the command line. If it is not specifically set on the
5636 # command line, check the password file.
5637 if $avautoenable {
5638 set autoenable 1
5639 set enable 0
5640 set prompt "(#| \\(enable\\))"
5641 } else {
5642 set ae [find autoenable $router]
5643 if { "$ae" == "1" } {
5644 set autoenable 1
5645 set enable 0
5646 set prompt "(#| \\(enable\\))"
5647 } else {
5648 set autoenable 0
5649 set enable $avenable
5650 set prompt ">"
5651 }
5652 }
5653
5654 # look for noenable option in .cloginrc
5655 if { [find noenable $router] == "1" } {
5656 set enable 0
5657 }
5658
5659 # is OTP in use? If so, bypass password checks
5660 set otpinuse 0
5661 if { [find otp_secret $router] != "" } {
5662 set otpinuse 1
5663 }
5664
5665 # Figure out passwords
5666 if { $do_passwd || $do_enapasswd } {
5667 set pswd [find password $router]
5668 if { [llength $pswd] == 0 && !$otpinuse} {
5669 send_user "\nError: no password for $router in $password_file.\n"
5670 continue
5671 }
5672 if { $enable && $do_enapasswd && $autoenable == 0 && [llength $pswd] < 2 && !$otpinuse } {
5673 send_user "\nError: no enable password for $router in $password_file.\n"
5674 continue
5675 }
5676 set passwd [join [lindex $pswd 0] ""]
5677 set enapasswd [join [lindex $pswd 1] ""]
5678 }
5679
5680 # Figure out username
5681 if {[info exists username]} {
5682 # command line username
5683 set ruser $username
5684 } else {
5685 set ruser [join [find user $router] ""]
5686 if { "$ruser" == "" } { set ruser $default_user }
5687 }
5688
5689 # Figure out username's password (if different from the vty password)
5690 if {[info exists userpasswd]} {
5691 # command line username
5692 set userpswd $userpasswd
5693 } else {
5694 set userpswd [join [find userpassword $router] ""]
5695 if { "$userpswd" == "" } { set userpswd $passwd }
5696 }
5697
5698 # Figure out enable username
5699 if {[info exists enausername]} {
5700 # command line enausername
5701 set enauser $enausername
5702 } else {
5703 set enauser [join [find enauser $router] ""]
5704 if { "$enauser" == "" } { set enauser $ruser }
5705 }
5706
5707 # Figure out prompts
5708 set u_prompt [find userprompt $router]
5709 if { "$u_prompt" == "" } {
5710 set u_prompt "(Username|Login|login|user name|name):"
5711 } else {
5712 set u_prompt [join [lindex $u_prompt 0] ""]
5713 }
5714 set p_prompt [find passprompt $router]
5715 if { "$p_prompt" == "" } {
5716 set p_prompt "(\[Pp]assword|passwd):"
5717 } else {
5718 set p_prompt [join [lindex $p_prompt 0] ""]
5719 }
5720 set e_prompt [find enableprompt $router]
5721 if { "$e_prompt" == "" } {
5722 set e_prompt "\[Pp]assword:"
5723 } else {
5724 set e_prompt [join [lindex $e_prompt 0] ""]
5725 }
5726
5727 # Figure out cypher type
5728 if {[info exists cypher]} {
5729 # command line cypher type
5730 set cyphertype $cypher
5731 } else {
5732 set cyphertype [find cyphertype $router]
5733 if { "$cyphertype" == "" } { set cyphertype "3des" }
5734 }
5735
5736 # Figure out connection method
5737 set cmethod [find method $router]
5738 if { "$cmethod" == "" } { set cmethod {{telnet} {ssh}} }
5739
5740 # Figure out the SSH executable name
5741 set sshcmd [join [lindex [find sshcmd $router] 0] ""]
5742 if { "$sshcmd" == "" } { set sshcmd {ssh} }
5743
5744 # If user provides a router-specific connection method, use it
5745 set usercmd [find usercmd $router]
5746 set usercmd_chat [find usercmd_chat $router]
5747
5748 # Login to the router
5749 if {[login $router $ruser $userpswd $passwd $enapasswd $cmethod $cyphertype]} {
5750 continue
5751 }
5752 if { $enable } {
5753 if {[do_enable $enauser $enapasswd]} {
5754 if { $do_command || $do_script } {
5755 close; wait
5756 continue
5757 }
5758 }
5759 }
5760 # we are logged in, now figure out the full prompt
5761 send "\r"
5762 expect {
5763 -re "\[\r\n]+" { exp_continue; }
5764 -re "^(.+:)1 $prompt" { # stoopid extreme cmd-line numbers and
5765 # prompt based on state of config changes,
5766 # which may have an * at the beginning.
5767 set junk $expect_out(1,string)
5768 regsub -all "^\\\* " $expect_out(1,string) {} junk
5769 set prompt ".? ?$junk\[0-9]+ $expect_out(2,string)";
5770 set platform "extreme"
5771 }
5772 -re "^.+$prompt" { set junk $expect_out(0,string);
5773 regsub -all "\[\]\[]" $junk {\\&} prompt;
5774 }
5775 -re "^.+> \\\(enable\\\)" {
5776 set junk $expect_out(0,string);
5777 regsub -all "\[\]\[]" $junk {\\&} prompt;
5778 }
5779 }
5780
5781 if { $do_command } {
5782 if {[run_commands $prompt $command]} {
5783 continue
5784 }
5785 } elseif { $do_script } {
5786 # If the prompt is (enable), then we are on a switch and the
5787 # command is "set length 0"; otherwise its "term length 0".
5788 if [regexp -- ".*> .*enable" "$prompt"] {
5789 send "set length 0\r"
5790 send "set logging session disable\r"
5791 } elseif { ![string compare "netopia" "$platform"] } {
5792 # do nothing
5793 } else {
5794 send "term length 0\r"
5795 }
5796 expect -re $prompt {}
5797 source $sfile
5798 close
5799 } else {
5800 label $router
5801 log_user 1
5802 interact
5803 }
5804
5805 # End of for each router
5806 wait
5807 sleep 0.3
5808}
5809exit 0
5810#! /usr/bin/expect --
5811##
5812## $Id: tntlogin.in 2376 2012-01-31 22:42:14Z heas $
5813##
5814## rancid 2.3.8
5815## Copyright (c) 1997-2011 by Terrapin Communications, Inc.
5816## All rights reserved.
5817##
5818## This code is derived from software contributed to and maintained by
5819## Terrapin Communications, Inc. by Henry Kilmer, John Heasley, Andrew Partan,
5820## Pete Whiting, Austin Schutz, and Andrew Fort.
5821##
5822## Redistribution and use in source and binary forms, with or without
5823## modification, are permitted provided that the following conditions
5824## are met:
5825## 1. Redistributions of source code must retain the above copyright
5826## notice, this list of conditions and the following disclaimer.
5827## 2. Redistributions in binary form must reproduce the above copyright
5828## notice, this list of conditions and the following disclaimer in the
5829## documentation and/or other materials provided with the distribution.
5830## 3. All advertising materials mentioning features or use of this software
5831## must display the following acknowledgement:
5832## This product includes software developed by Terrapin Communications,
5833## Inc. and its contributors for RANCID.
5834## 4. Neither the name of Terrapin Communications, Inc. nor the names of its
5835## contributors may be used to endorse or promote products derived from
5836## this software without specific prior written permission.
5837## 5. It is requested that non-binding fixes and modifications be contributed
5838## back to Terrapin Communications, Inc.
5839##
5840## THIS SOFTWARE IS PROVIDED BY Terrapin Communications, INC. AND CONTRIBUTORS
5841## ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
5842## TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
5843## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COMPANY OR CONTRIBUTORS
5844## BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
5845## CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
5846## SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
5847## INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
5848## CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
5849## ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
5850## POSSIBILITY OF SUCH DAMAGE.
5851#
5852# The expect login scripts were based on Erik Sherk's gwtn, by permission.
5853#
5854# Modified by P B Matthews.
5855
5856# Usage line
5857set usage "Usage: $argv0 \[-dSV\] \[-c command\] \[-Evar=x\] \
5858\[-f cloginrc-file\] \[-s script-file\] \[-t timeout\] \[-u username\] \
5859\[-v vty-password\] \[-x command-file\] \[-y ssh_cypher_type\] router \
5860\[router...\]\n"
5861
5862# env(CLOGIN) may contain:
5863# x == do not set xterm banner or name
5864
5865# Password file
5866set password_file $env(HOME)/.cloginrc
5867# Default is to login to the router
5868set do_command 0
5869set do_script 0
5870# The default is to automatically enable
5871set avenable 0
5872# The default is that you login non-enabled (tacacs can have you login already
5873# enabled)
5874set avautoenable 1
5875# The default is to look in the password file to find the passwords. This
5876# tracks if we receive them on the command line.
5877set do_passwd 1
5878# Save config, if prompted
5879set do_saveconfig 0
5880# Sometimes routers take awhile to answer (the default is 10 sec)
5881set timeoutdflt 45
5882
5883# Find the user in the ENV, or use the unix userid.
5884if {[ info exists env(CISCO_USER) ]} {
5885 set default_user $env(CISCO_USER)
5886} elseif {[ info exists env(USER) ]} {
5887 set default_user $env(USER)
5888} elseif {[ info exists env(LOGNAME) ]} {
5889 set default_user $env(LOGNAME)
5890} else {
5891 # This uses "id" which I think is portable. At least it has existed
5892 # (without options) on all machines/OSes I've been on recently -
5893 # unlike whoami or id -nu.
5894 if [ catch {exec id} reason ] {
5895 send_error "\nError: could not exec id: $reason\n"
5896 exit 1
5897 }
5898 regexp {\(([^)]*)} "$reason" junk default_user
5899}
5900if {[ info exists env(CLOGINRC) ]} {
5901 set password_file $env(CLOGINRC)
5902}
5903
5904# Process the command line
5905for {set i 0} {$i < $argc} {incr i} {
5906 set arg [lindex $argv $i]
5907
5908 switch -glob -- $arg {
5909 # Expect debug mode
5910 -d* {
5911 exp_internal 1
5912 # Username
5913 } -u* {
5914 if {! [ regexp .\[uU\](.+) $arg ignore user]} {
5915 incr i
5916 set username [ lindex $argv $i ]
5917 }
5918 # VTY Password
5919 } -v* {
5920 if {! [ regexp .\[vV\](.+) $arg ignore passwd]} {
5921 incr i
5922 set passwd [ lindex $argv $i ]
5923 }
5924 set do_passwd 0
5925 # ssh passphrase
5926 } -r* {
5927 # ignore -r
5928 # Version string
5929 } -V* {
5930 send_user "rancid 2.3.8\n"
5931 exit 0
5932 # Enable Username
5933 } -w* {
5934 # ignore -w
5935 # Environment variable to pass to -s scripts
5936 } -E* {
5937 if {[ regexp .\[E\](.+)=(.+) $arg ignore varname varvalue]} {
5938 incr i
5939 set E$varname $varvalue
5940 } else {
5941 send_user "\nError: invalid format for -E in $arg\n"
5942 exit 1
5943 }
5944 # Enable Password
5945 } -e* {
5946 # ignore -e
5947 # Command to run.
5948 } -c* {
5949 if {! [ regexp .\[cC\](.+) $arg ignore command]} {
5950 incr i
5951 set command [ lindex $argv $i ]
5952 }
5953 set do_command 1
5954 # Expect script to run.
5955 } -s* {
5956 if {! [ regexp .\[sS\](.+) $arg ignore sfile]} {
5957 incr i
5958 set sfile [ lindex $argv $i ]
5959 }
5960 if { ! [ file readable $sfile ] } {
5961 send_user "\nError: Can't read $sfile\n"
5962 exit 1
5963 }
5964 set do_script 1
5965 # save config on exit
5966 } -S* {
5967 set do_saveconfig 1
5968 # 'ssh -c' cypher type
5969 } -y* {
5970 if {! [ regexp .\[eE\](.+) $arg ignore cypher]} {
5971 incr i
5972 set cypher [ lindex $argv $i ]
5973 }
5974 # alternate cloginrc file
5975 } -f* {
5976 if {! [ regexp .\[fF\](.+) $arg ignore password_file]} {
5977 incr i
5978 set password_file [ lindex $argv $i ]
5979 }
5980 # Timeout
5981 } -t* {
5982 if {! [ regexp .\[tT\](.+) $arg ignore timeout]} {
5983 incr i
5984 set timeoutdflt [ lindex $argv $i ]
5985 }
5986 # Command file
5987 } -x* {
5988 if {! [ regexp .\[xX\](.+) $arg ignore cmd_file]} {
5989 incr i
5990 set cmd_file [ lindex $argv $i ]
5991 }
5992 if [ catch {set cmd_fd [open $cmd_file r]} reason ] {
5993 send_user "\nError: $reason\n"
5994 exit 1
5995 }
5996 set cmd_text [read $cmd_fd]
5997 close $cmd_fd
5998 set command [join [split $cmd_text \n] \;]
5999 set do_command 1
6000 # Do we enable?
6001 } -noenable {
6002 # ignore -noenable
6003 # Does tacacs automatically enable us?
6004 } -autoenable {
6005 # ignore -autoenable
6006 } -* {
6007 send_user "\nError: Unknown argument! $arg\n"
6008 send_user $usage
6009 exit 1
6010 } default {
6011 break
6012 }
6013 }
6014}
6015# Process routers...no routers listed is an error.
6016if { $i == $argc } {
6017 send_user "\nError: $usage"
6018}
6019
6020# Only be quiet if we are running a script (it can log its output
6021# on its own)
6022if { $do_script } {
6023 log_user 0
6024} else {
6025 log_user 1
6026}
6027
6028#
6029# Done configuration/variable setting. Now run with it...
6030#
6031
6032# Sets Xterm title if interactive...if its an xterm and the user cares
6033proc label { host } {
6034 global env
6035 # if CLOGIN has an 'x' in it, don't set the xterm name/banner
6036 if [info exists env(CLOGIN)] {
6037 if {[string first "x" $env(CLOGIN)] != -1} { return }
6038 }
6039 # take host from ENV(TERM)
6040 if [info exists env(TERM)] {
6041 if [regexp \^(xterm|vs) $env(TERM) ignore ] {
6042 send_user "\033]1;[lindex [split $host "."] 0]\a"
6043 send_user "\033]2;$host\a"
6044 }
6045 }
6046}
6047
6048# This is a helper function to make the password file easier to
6049# maintain. Using this the password file has the form:
6050# add password sl* pete cow
6051# add password at* steve
6052# add password * hanky-pie
6053proc add {var args} { global int_$var ; lappend int_$var $args}
6054proc include {args} {
6055 global env
6056 regsub -all "(^{|}$)" $args {} args
6057 if { [ regexp "^/" $args ignore ] == 0 } {
6058 set args $env(HOME)/$args
6059 }
6060 source_password_file $args
6061}
6062
6063proc find {var router} {
6064 upvar int_$var list
6065 if { [info exists list] } {
6066 foreach line $list {
6067 if { [string match [lindex $line 0] $router ] } {
6068 return [lrange $line 1 end]
6069 }
6070 }
6071 }
6072 return {}
6073}
6074
6075# Loads the password file. Note that as this file is tcl, and that
6076# it is sourced, the user better know what to put in there, as it
6077# could install more than just password info... I will assume however,
6078# that a "bad guy" could just as easy put such code in the clogin
6079# script, so I will leave .cloginrc as just an extention of that script
6080proc source_password_file { password_file } {
6081 global env
6082 if { ! [file exists $password_file] } {
6083 send_user "\nError: password file ($password_file) does not exist\n"
6084 exit 1
6085 }
6086 file stat $password_file fileinfo
6087 if { [expr ($fileinfo(mode) & 007)] != 0000 } {
6088 send_user "\nError: $password_file must not be world readable/writable\n"
6089 exit 1
6090 }
6091 if [ catch {source $password_file} reason ] {
6092 send_user "\nError: $reason\n"
6093 exit 1
6094 }
6095}
6096
6097# Log into the router.
6098# returns: 0 on success, 1 on failure
6099proc login { router user userpswd passwd prompt cmethod cyphertype } {
6100 global spawn_id in_proc do_command do_script
6101 global u_prompt p_prompt sshcmd
6102 set in_proc 1
6103 set uprompt_seen 0
6104
6105 # try each of the connection methods in $cmethod until one is successful
6106 set progs [llength $cmethod]
6107 foreach prog [lrange $cmethod 0 end] {
6108 incr progs -1
6109 if [string match "telnet*" $prog] {
6110 regexp {telnet(:([^[:space:]]+))*} $prog command suffix port
6111 if {"$port" == ""} {
6112 set retval [ catch {spawn telnet $router} reason ]
6113 } else {
6114 set retval [ catch {spawn telnet $router $port} reason ]
6115 }
6116 if { $retval } {
6117 send_user "\nError: telnet failed: $reason\n"
6118 return 1
6119 }
6120 } elseif ![string compare $prog "ssh"] {
6121 regexp {ssh(:([^[:space:]]+))*} $prog methcmd suffix port
6122 set cmd $sshcmd
6123 if {"$port" != ""} {
6124 set cmd "$cmd -p $port"
6125 }
6126 set retval [ catch {eval spawn [split "$cmd -c $cyphertype -x -l $user $router" { }]} reason ]
6127 if { $retval } {
6128 send_user "\nError: $cmd failed: $reason\n"
6129 return 1
6130 }
6131 } elseif ![string compare $prog "rsh"] {
6132 send_error "\nError: unsupported method: rsh\n"
6133 if { $progs == 0 } {
6134 return 1
6135 }
6136 continue;
6137 } else {
6138 send_user "\nError: unknown connection method: $prog\n"
6139 return 1
6140 }
6141 sleep 0.3
6142
6143 # This helps cleanup each expect clause.
6144 expect_after {
6145 timeout {
6146 send_user "\nError: TIMEOUT reached\n"
6147 catch {close}; catch {wait};
6148 if { $in_proc} {
6149 return 1
6150 } else {
6151 continue
6152 }
6153 } eof {
6154 send_user "\nError: EOF received\n"
6155 catch {close}; catch {wait};
6156 if { $in_proc} {
6157 return 1
6158 } else {
6159 continue
6160 }
6161 }
6162 }
6163
6164 expect {
6165 "Connection refused" {
6166 catch {close}; catch {wait};
6167 sleep 0.3
6168 expect eof
6169 send_user "\nError: Connection Refused\n"; wait; return 1
6170 }
6171 eof { send_user "\nError: Couldn't login\n"; wait; return 1
6172 } "Unknown host\r\n" {
6173 expect eof
6174 send_user "\nError: Unknown host\n"; wait; return 1
6175 } "Host is unreachable" {
6176 expect eof
6177 send_user "\nError: Host Unreachable!\n"; wait; return 1
6178 } "No address associated with name" {
6179 expect eof
6180 send_user "\nError: Unknown host\n"; wait; return 1
6181 }
6182 -re "(Host key not found |The authenticity of host .* be established).* \\(yes/no\\)\\?" {
6183 send "yes\r"
6184 send_user "\nHost $router added to the list of known hosts.\n"
6185 exp_continue
6186 }
6187 -re "HOST IDENTIFICATION HAS CHANGED.* \\(yes/no\\)\\?" {
6188 send "no\r"
6189 send_user "\nError: The host key for $router has changed. Update the SSH known_hosts file accordingly.\n"
6190 return 1
6191 }
6192 -re "HOST IDENTIFICATION HAS CHANGED\[^\n\r]+" {
6193 send_user "\nError: The host key for $router has changed. Update the SSH known_hosts file accordingly.\n"
6194 return 1
6195 }
6196 -re "Offending key for .* \\(yes/no\\)\\?" {
6197 send "no\r"
6198 send_user "\nError: host key mismatch for $router. Update the SSH known_hosts file accordingly.\n"
6199 return 1
6200 }
6201 -re "$u_prompt" {
6202 send -- "$user\r"
6203 set uprompt_seen 1
6204 exp_continue
6205 }
6206 -re "$p_prompt" {
6207 sleep 1
6208 if {$uprompt_seen == 1} {
6209 send -- "$userpswd\r"
6210 } else {
6211 send -- "$passwd\r"
6212 }
6213 exp_continue
6214 }
6215 -re "^Confirm seeing above note" {
6216 send "y\r"
6217 exp_continue
6218 }
6219 "Password incorrect" { send_user "\nError: Check your password for $router\n";
6220 catch {close}; catch {wait}; return 1 }
6221 -re "$prompt" { break; }
6222 denied { send_user "\nError: Check your passwd for $router\n"
6223 catch {close}; catch {wait}; return 1
6224 }
6225 "\r\n" { exp_continue; }
6226 }
6227 }
6228 set in_proc 0
6229 return 0
6230}
6231
6232# Run commands given on the command line.
6233proc run_commands { prompt command } {
6234 global in_proc
6235 set in_proc 1
6236
6237 send "lines 0\r"
6238 expect -re $prompt {}
6239 regsub -all "\[)(]" $prompt {\\&} reprompt
6240
6241 set commands [split $command \;]
6242 set num_commands [llength $commands]
6243 for {set i 0} {$i < $num_commands} { incr i} {
6244 send -- "[subst -nocommands [lindex $commands $i]]\r"
6245 expect {
6246 -re "^\[^\n\r]*$reprompt" {}
6247 -re "^\[^\n\r ]*>>.*$reprompt" { exp_continue }
6248 -re "\[\n\r]+" { exp_continue }
6249 }
6250 }
6251
6252 send "quit\r"
6253# expect {
6254# -re "^WARNING: the current user has insufficient rights to view password fields. A configuration saved under this circumstance should not be used to restore profiles containing passwords. Save anyway? [y/n]"
6255# {
6256# send "y\r"
6257 exp_continue
6258 }
6259 "\n" { exp_continue }
6260 "\[^\n\r *]*Session terminated" { return 0 }
6261 timeout { catch {close}; catch {wait};
6262 return 0
6263 }
6264 eof { return 0 }
6265 }
6266 set in_proc 0
6267}
6268
6269#
6270# For each router... (this is main loop)
6271#
6272source_password_file $password_file
6273set in_proc 0
6274set exitval 0
6275foreach router [lrange $argv $i end] {
6276 set router [string tolower $router]
6277 send_user "$router\n"
6278
6279 # device timeout
6280 set timeout [find timeout $router]
6281 if { [llength $timeout] == 0 } {
6282 set timeout $timeoutdflt
6283 }
6284
6285 # Figure out prompt.
6286 set prompt "admin>"
6287 # TNT only "enables" based on the password used at login time
6288 set autoenable 1
6289 set enable 0
6290
6291 # Figure out passwords
6292 if { $do_passwd } {
6293 set pswd [find password $router]
6294 if { [llength $pswd] == 0 } {
6295 send_user "\nError - no password for $router in $password_file.\n"
6296 continue
6297 }
6298 set passwd [join [lindex $pswd 0] ""]
6299 }
6300
6301 # Figure out username
6302 if {[info exists username]} {
6303 # command line username
6304 set ruser $username
6305 } else {
6306 set ruser [join [find user $router] ""]
6307 if { "$ruser" == "" } { set ruser $default_user }
6308 }
6309
6310 # Figure out username's password (if different from the vty password)
6311 if {[info exists userpasswd]} {
6312 # command line username
6313 set userpswd $userpasswd
6314 } else {
6315 set userpswd [join [find userpassword $router] ""]
6316 if { "$userpswd" == "" } { set userpswd $passwd }
6317 }
6318
6319 # Figure out prompts
6320 set u_prompt [find userprompt $router]
6321 if { "$u_prompt" == "" } {
6322 set u_prompt "(User|Username|login| Login):"
6323 } else {
6324 set u_prompt [join [lindex $u_prompt 0] ""]
6325 }
6326
6327 set p_prompt [find passprompt $router]
6328 if { "$p_prompt" == "" } {
6329 set p_prompt "\[Pp]assword:"
6330 } else {
6331 set p_prompt [join [lindex $p_prompt 0] ""]
6332 }
6333
6334 # Figure out cypher type
6335 if {[info exists cypher]} {
6336 # command line cypher type
6337 set cyphertype $cypher
6338 } else {
6339 set cyphertype [find cyphertype $router]
6340 if { "$cyphertype" == "" } { set cyphertype "3des" }
6341 }
6342
6343 # Figure out connection method
6344 set cmethod [find method $router]
6345 if { "$cmethod" == "" } { set cmethod {{telnet} {ssh}} }
6346
6347 # Figure out the SSH executable name
6348 set sshcmd [join [lindex [find sshcmd $router] 0] ""]
6349 if { "$sshcmd" == "" } { set sshcmd {ssh} }
6350
6351 # Login to the router
6352 if {[login $router $ruser $userpswd $passwd $prompt $cmethod $cyphertype]} {
6353 incr exitval
6354 continue
6355 }
6356
6357 if { $do_command } {
6358 if {[run_commands $prompt $command]} {
6359 incr exitval
6360 continue
6361 }
6362 } elseif { $do_script } {
6363 expect -re $prompt {}
6364 source $sfile
6365 send "y\r"
6366 catch {close};
6367 } else {
6368 label $router
6369 log_user 1
6370 interact
6371 }
6372
6373 # End of for each router
6374 catch {wait};
6375 sleep 0.3
6376}
6377exit $exitval
6378#! /usr/bin/perl
6379##
6380## $Id: tntrancid.in 2279 2011-01-31 22:41:00Z heas $
6381##
6382## rancid 2.3.8
6383## Copyright (c) 1997-2008 by Terrapin Communications, Inc.
6384## All rights reserved.
6385##
6386## This code is derived from software contributed to and maintained by
6387## Terrapin Communications, Inc. by Henry Kilmer, John Heasley, Andrew Partan,
6388## Pete Whiting, Austin Schutz, and Andrew Fort.
6389##
6390## Redistribution and use in source and binary forms, with or without
6391## modification, are permitted provided that the following conditions
6392## are met:
6393## 1. Redistributions of source code must retain the above copyright
6394## notice, this list of conditions and the following disclaimer.
6395## 2. Redistributions in binary form must reproduce the above copyright
6396## notice, this list of conditions and the following disclaimer in the
6397## documentation and/or other materials provided with the distribution.
6398## 3. All advertising materials mentioning features or use of this software
6399## must display the following acknowledgement:
6400## This product includes software developed by Terrapin Communications,
6401## Inc. and its contributors for RANCID.
6402## 4. Neither the name of Terrapin Communications, Inc. nor the names of its
6403## contributors may be used to endorse or promote products derived from
6404## this software without specific prior written permission.
6405## 5. It is requested that non-binding fixes and modifications be contributed
6406## back to Terrapin Communications, Inc.
6407##
6408## THIS SOFTWARE IS PROVIDED BY Terrapin Communications, INC. AND CONTRIBUTORS
6409## ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
6410## TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
6411## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COMPANY OR CONTRIBUTORS
6412## BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
6413## CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
6414## SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
6415## INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
6416## CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
6417## ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
6418## POSSIBILITY OF SUCH DAMAGE.
6419#
6420# Modified by Paul B Matthews & Richard Vander Reyden.
6421# I'm suprised it still works....
6422#
6423# RANCID - Really Awesome New Cisco confIg Differ
6424#
6425# usage: tntrancid [-dV] [-l] [-f filename | hostname]
6426#
6427use Getopt::Std;
6428getopts('dflV');
6429if ($opt_V) {
6430 print "rancid 2.3.8\n";
6431 exit(0);
6432}
6433$log = $opt_l;
6434$debug = $opt_d;
6435$file = $opt_f;
6436$host = $ARGV[0];
6437$clean_run = 0;
6438$found_end = 0;
6439$timeo = 90; # tntlogin timeout in seconds
6440$prompt = "admin> ";
6441$always_y = "y"; # cause its a pain.
6442
6443my(@commandtable, %commands, @commands);# command lists
6444my($aclsort) = ("ipsort"); # ACL sorting mode
6445my($filter_commstr); # SNMP community string filtering
6446my($filter_pwds); # password filtering mode
6447
6448# This routine is used to print out the router configuration
6449sub ProcessHistory {
6450 my($new_hist_tag,$new_command,$command_string,@string) = (@_);
6451 if ((($new_hist_tag ne $hist_tag) || ($new_command ne $command))
6452 && scalar(%history)) {
6453 print eval "$command \%history";
6454 undef %history;
6455 }
6456 if (($new_hist_tag) && ($new_command) && ($command_string)) {
6457 if ($history{$command_string}) {
6458 $history{$command_string} = "$history{$command_string}@string";
6459 } else {
6460 $history{$command_string} = "@string";
6461 }
6462 } elsif (($new_hist_tag) && ($new_command)) {
6463 $history{++$#history} = "@string";
6464 } else {
6465 print "@string";
6466 }
6467 $hist_tag = $new_hist_tag;
6468 $command = $new_command;
6469 1;
6470}
6471
6472sub numerically { $a <=> $b; }
6473
6474# This is a sort routine that will sort numerically on the
6475# keys of a hash as if it were a normal array.
6476sub keynsort {
6477 local(%lines) = @_;
6478 local($i) = 0;
6479 local(@sorted_lines);
6480 foreach $key (sort numerically keys(%lines)) {
6481 $sorted_lines[$i] = $lines{$key};
6482 $i++;
6483 }
6484 @sorted_lines;
6485}
6486
6487# This is a sort routine that will sort on the
6488# keys of a hash as if it were a normal array.
6489sub keysort {
6490 local(%lines) = @_;
6491 local($i) = 0;
6492 local(@sorted_lines);
6493 foreach $key (sort keys(%lines)) {
6494 $sorted_lines[$i] = $lines{$key};
6495 $i++;
6496 }
6497 @sorted_lines;
6498}
6499
6500# This is a sort routine that will sort on the
6501# values of a hash as if it were a normal array.
6502sub valsort{
6503 local(%lines) = @_;
6504 local($i) = 0;
6505 local(@sorted_lines);
6506 foreach $key (sort values %lines) {
6507 $sorted_lines[$i] = $key;
6508 $i++;
6509 }
6510 @sorted_lines;
6511}
6512
6513# This is a numerical sort routine (ascending).
6514sub numsort {
6515 local(%lines) = @_;
6516 local($i) = 0;
6517 local(@sorted_lines);
6518 foreach $num (sort {$a <=> $b} keys %lines) {
6519 $sorted_lines[$i] = $lines{$num};
6520 $i++;
6521 }
6522 @sorted_lines;
6523}
6524
6525# This is a sort routine that will sort on the
6526# ip address when the ip address is anywhere in
6527# the strings.
6528sub ipsort {
6529 local(%lines) = @_;
6530 local($i) = 0;
6531 local(@sorted_lines);
6532 foreach $addr (sort sortbyipaddr keys %lines) {
6533 $sorted_lines[$i] = $lines{$addr};
6534 $i++;
6535 }
6536 @sorted_lines;
6537}
6538
6539# These two routines will sort based upon IP addresses
6540sub ipaddrval {
6541 my(@a) = ($_[0] =~ m#^(\d+)\.(\d+)\.(\d+)\.(\d+)$#);
6542 $a[3] + 256 * ($a[2] + 256 * ($a[1] +256 * $a[0]));
6543}
6544sub sortbyipaddr {
6545 &ipaddrval($a) <=> &ipaddrval($b);
6546}
6547
6548# This routine processes a "save con"
6549sub SaveConf {
6550 print STDERR " In SaveConf: $_" if ($debug);
6551
6552 while (<INPUT>) {
6553 tr/\015//d;
6554 last if(/^$prompt/);
6555
6556 # Leave software revision, but strip out saved date,
6557 # which causes rancid to think it changes each poll
6558 if (/^; saved from /) {
6559 ProcessHistory("","","","$_");
6560 next;
6561 }
6562 /^; saved / && next;
6563
6564 # catch anything that wasnt match above.
6565 ProcessHistory("","","","$_");
6566
6567 # end of config
6568 if (/; profiles saved$/) {
6569 printf STDERR " End SaveConf: $_" if ($debug);
6570 $found_end = 1;
6571 print STDOUT "$found_end = found_end within test\n";
6572 return(1);
6573 }
6574 # XXX what is the purpose of this?
6575 $found_end = 1;
6576#### print STDOUT "$found_end = found_end at test\n";
6577 }
6578 $found_end = 1;
6579 return(0);
6580}
6581$found_end = 1;
6582print STDOUT "$found_end = found_end at end test\n";
6583
6584
6585
6586
6587# dummy function
6588sub DoNothing {print STDOUT;}
6589
6590# Main
6591@commandtable = (
6592 {'save con' => 'SaveConf'}
6593);
6594# Use an array to preserve the order of the commands and a hash for mapping
6595# commands to the subroutine and track commands that have been completed.
6596@commands = map(keys(%$_), @commandtable);
6597%commands = map(%$_, @commandtable);
6598
6599$tnt_cmds=join(";",@commands);
6600$cmds_regexp = join("|", map quotemeta($_), @commands);
6601
6602if (length($host) == 0) {
6603 if ($file) {
6604 print(STDERR "Too few arguments: file name required\n");
6605 exit(1);
6606 } else {
6607 print(STDERR "Too few arguments: host name required\n");
6608 exit(1);
6609 }
6610}
6611open(OUTPUT,">$host.new") || die "Can't open $host.new for writing: $!\n";
6612select(OUTPUT);
6613# make OUTPUT unbuffered if debugging
6614if ($debug) { $| = 1; }
6615
6616if ($file) {
6617 print STDERR "opening file $host\n" if ($debug);
6618 print STDOUT "opening file $host\n" if ($log);
6619 open(INPUT,"<$host") || die "open failed for $host: $!\n";
6620} else {
6621 print STDERR "executing tntlogin -t $timeo -c\"$tnt_cmds\" $host\n" if ($debug);
6622 print STDOUT "executing tntlogin -t $timeo -c\"$tnt_cmds\" $host\n" if ($log);
6623 if (defined($ENV{NOPIPE}) && $ENV{NOPIPE} =~ /^YES/i) {
6624 system "tntlogin -t $timeo -c \"$tnt_cmds\" $host </dev/null > $host.raw 2>&1" || die "tntlogin failed for $host: $!\n";
6625 open(INPUT, "< $host.raw") || die "tntlogin failed for $host: $!\n";
6626 } else {
6627 open(INPUT,"tntlogin -t $timeo -c \"$tnt_cmds\" $host </dev/null |") || die "tntlogin failed for $host: $!\n";
6628 }
6629}
6630
6631# determine ACL sorting mode
6632if ($ENV{"ACLSORT"} =~ /no/i) {
6633 $aclsort = "";
6634}
6635# determine community string filtering mode
6636if (defined($ENV{"NOCOMMSTR"}) &&
6637 ($ENV{"NOCOMMSTR"} =~ /yes/i || $ENV{"NOCOMMSTR"} =~ /^$/)) {
6638 $filter_commstr = 1;
6639} else {
6640 $filter_commstr = 0;
6641}
6642# determine password filtering mode
6643if ($ENV{"FILTER_PWDS"} =~ /no/i) {
6644 $filter_pwds = 0;
6645} elsif ($ENV{"FILTER_PWDS"} =~ /all/i) {
6646 $filter_pwds = 2;
6647} else {
6648 $filter_pwds = 1;
6649}
6650
6651ProcessHistory("","","","#RANCID-CONTENT-TYPE: tnt\n#\n");
6652ProcessHistory("COMMENTS","keysort","X0",";\n");
6653TOP: while(<INPUT>) {
6654 tr/\015//d;
6655
6656 if (/^Error:/) {
6657 print STDOUT ("$host tntlogin error: $_");
6658 print STDERR ("$host tntlogin error: $_") if ($debug);
6659 $clean_run=0;
6660 last;
6661 }
6662 while (/$prompt\s*($cmds_regexp)\s*$/) {
6663 $cmd = $1;
6664 if (!defined($prompt)) {
6665 $prompt = ($_ =~ /^([^#]+#)/)[0];
6666 $prompt =~ s/([][}{)(\\])/\\$1/g;
6667 $prompt =~ s/:(\d+ ?)#/:\\d+ ?#/;
6668 print STDERR ("PROMPT MATCH: $prompt\n") if ($debug);
6669 }
6670 print STDERR ("HIT COMMAND:$_") if ($debug);
6671 if (! defined($commands{$cmd})) {
6672 print STDERR "$host: found unexpected command - \"$cmd\"\n";
6673 $clean_run = 0;
6674 last TOP;
6675 }
6676 $rval = &{$commands{$cmd}};
6677 delete($commands{$cmd});
6678 if ($rval == -1) {
6679 printf STDERR "rval = -1\n" if ($debug);
6680 $clean_run = 0;
6681 last TOP;
6682 }
6683 }
6684}
6685print STDOUT "Done $logincmd: $_\n" if ($log);
6686# Flush History
6687ProcessHistory("","","","");
6688# Cleanup
6689$clean_run = 1;
6690print STDOUT "$clean_run - clean\n";
6691close(INPUT);
6692close(OUTPUT);
6693
6694if (defined($ENV{NOPIPE}) && $ENV{NOPIPE} =~ /^YES/i) {
6695 unlink("$host.raw") if (! $debug);
6696}
6697
6698# check for completeness
6699if (scalar(%commands) || !$clean_run || !$found_end) {
6700 if (scalar(%commands)) {
6701 printf(STDOUT "$host: missed cmd(s): %s\n", join(',', keys(%commands)));
6702 printf(STDERR "$host: missed cmd(s): %s\n", join(',', keys(%commands))) if ($debug);
6703 }
6704 if (!$clean_run || !$found_end) {
6705 print STDOUT "$host: End of run not found\n";
6706 print STDERR "$host: End of run not found\n" if ($debug);
6707 system("/usr/bin/tail -1 $host.new");
6708 }
6709 unlink "$host.new" if (! $debug);
6710}
6711#! /usr/bin/perl
6712##
6713## $Id: trancid.in 2880 2014-09-29 18:23:24Z heas $
6714##
6715## rancid 3.4.1
6716## Copyright (c) 1997-2016 by Terrapin Communications, Inc.
6717## All rights reserved.
6718##
6719## This code is derived from software contributed to and maintained by
6720## Terrapin Communications, Inc. by Henry Kilmer, John Heasley, Andrew Partan,
6721## Pete Whiting, Austin Schutz, and Andrew Fort.
6722##
6723## Redistribution and use in source and binary forms, with or without
6724## modification, are permitted provided that the following conditions
6725## are met:
6726## 1. Redistributions of source code must retain the above copyright
6727## notice, this list of conditions and the following disclaimer.
6728## 2. Redistributions in binary form must reproduce the above copyright
6729## notice, this list of conditions and the following disclaimer in the
6730## documentation and/or other materials provided with the distribution.
6731## 3. All advertising materials mentioning features or use of this software
6732## must display the following acknowledgement:
6733## This product includes software developed by Terrapin Communications,
6734## Inc. and its contributors for RANCID.
6735## 4. Neither the name of Terrapin Communications, Inc. nor the names of its
6736## contributors may be used to endorse or promote products derived from
6737## this software without specific prior written permission.
6738## 5. It is requested that non-binding fixes and modifications be contributed
6739## back to Terrapin Communications, Inc.
6740##
6741## THIS SOFTWARE IS PROVIDED BY Terrapin Communications, INC. AND CONTRIBUTORS
6742## ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
6743## TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
6744## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COMPANY OR CONTRIBUTORS
6745## BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
6746## CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
6747## SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
6748## INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
6749## CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
6750## ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
6751## POSSIBILITY OF SUCH DAMAGE.
6752#
6753# RANCID - Really Awesome New Cisco confIg Differ
6754#
6755# usage: trancid [-dltCV] [-f filename | hostname]
6756#
6757# Modified by Ed Ravin for Netopia.
6758use Getopt::Std;
6759getopts('dflt:CV');
6760if ($opt_V) {
6761 print "rancid 3.4.1\n";
6762 exit(0);
6763}
6764$log = $opt_l;
6765$debug = $opt_d;
6766$file = $opt_f;
6767$host = $ARGV[0];
6768$clean_run = 0;
6769$found_end = 0;
6770$found_version = 0;
6771$found_env = 0;
6772$found_diag = 0;
6773$timeo = 90; # clogin timeout in seconds
6774
6775my(%filter_pwds); # password filtering mode
6776
6777# This routine is used to print out the router configuration
6778sub ProcessHistory {
6779 my($new_hist_tag,$new_command,$command_string,@string)=(@_);
6780 if((($new_hist_tag ne $hist_tag) || ($new_command ne $command))
6781 && scalar(%history)) {
6782 print eval "$command \%history";
6783 undef %history;
6784 }
6785 if (($new_hist_tag) && ($new_command) && ($command_string)) {
6786 if ($history{$command_string}) {
6787 $history{$command_string} = "$history{$command_string}@string";
6788 } else {
6789 $history{$command_string} = "@string";
6790 }
6791 } elsif (($new_hist_tag) && ($new_command)) {
6792 $history{++$#history} = "@string";
6793 } else {
6794 print "@string";
6795 }
6796 $hist_tag = $new_hist_tag;
6797 $command = $new_command;
6798 1;
6799}
6800
6801sub numerically { $a <=> $b; }
6802
6803# This is a sort routing that will sort numerically on the
6804# keys of a hash as if it were a normal array.
6805sub keynsort {
6806 local(%lines)=@_;
6807 local($i) = 0;
6808 local(@sorted_lines);
6809 foreach $key (sort numerically keys(%lines)) {
6810 $sorted_lines[$i] = $lines{$key};
6811 $i++;
6812 }
6813 @sorted_lines;
6814}
6815
6816# This is a sort routing that will sort on the
6817# keys of a hash as if it were a normal array.
6818sub keysort {
6819 local(%lines)=@_;
6820 local($i) = 0;
6821 local(@sorted_lines);
6822 foreach $key (sort keys(%lines)) {
6823 $sorted_lines[$i] = $lines{$key};
6824 $i++;
6825 }
6826 @sorted_lines;
6827}
6828
6829# This is a sort routing that will sort on the
6830# values of a hash as if it were a normal array.
6831sub valsort{
6832 local(%lines)=@_;
6833 local($i) = 0;
6834 local(@sorted_lines);
6835 foreach $key (sort values %lines) {
6836 $sorted_lines[$i] = $key;
6837 $i++;
6838 }
6839 @sorted_lines;
6840}
6841
6842# This is a numerical sort routing (ascending).
6843sub numsort {
6844 local(%lines)=@_;
6845 local($i) = 0;
6846 local(@sorted_lines);
6847 foreach $num (sort {$a <=> $b} keys %lines) {
6848 $sorted_lines[$i] = $lines{$num};
6849 $i++;
6850 }
6851 @sorted_lines;
6852}
6853
6854# This is a sort routine that will sort on the
6855# ip address when the ip address is anywhere in
6856# the strings.
6857sub ipsort {
6858 local(%lines)=@_;
6859 local($i) = 0;
6860 local(@sorted_lines);
6861 foreach $addr (sort sortbyipaddr keys %lines) {
6862 $sorted_lines[$i] = $lines{$addr};
6863 $i++;
6864 }
6865 @sorted_lines;
6866}
6867
6868# These two routines will sort based upon IP addresses
6869sub ipaddrval {
6870 my(@a) = ($_[0] =~ m#^(\d+)\.(\d+)\.(\d+)\.(\d+)$#);
6871 $a[3]+256*($a[2]+256*($a[1]+256*$a[0]));
6872}
6873sub sortbyipaddr {
6874 &ipaddrval($a) <=> &ipaddrval($b);
6875}
6876
6877# This routine parses "show version"
6878sub ShowVersion {
6879 print STDERR " In ShowVersion: $_" if ($debug);
6880
6881 while (<INPUT>) {
6882 tr/\015//d;
6883 if (/^$prompt/) { $found_version=1; last};
6884 next if(/^(\s*|\s*$cmd\s*)$/);
6885 return(1) if /(Invalid input detected|Type help or )/;
6886 return(-1) if (/command authorization failed/i);
6887 return(0) if ($found_version); # Only do this routine once
6888
6889 # no pagers in Netopia-land, but shouldn't hurt
6890 s/^<-+ More -+>\s*//;
6891
6892### sample output:
6893### #show version
6894### cli version: 01.00d00
6895### firmware version: 05.03.09f06
6896### hardware version: 01.00f00
6897### mib version: 01.00f00
6898### html version: 01.01d07
6899
6900 ProcessHistory("COMMENTS","keysort","A1","# $_");
6901 }
6902
6903 return(0);
6904}
6905
6906sub ShowConfig {
6907 print STDERR " In ShowConfig: $_" if ($debug);
6908
6909 while (<INPUT>) {
6910 tr/\015//d;
6911 if (/^$prompt/) { $found_end=1; $clean_run=1; return 0};
6912 next if(/^(\s*|\s*$cmd\s*)$/);
6913 return(1) if /(Invalid input detected|Type help or )/;
6914 return(-1) if (/command authorization failed/i);
6915
6916 # no pagers in Netopia-land, but shouldn't hurt
6917 s/^<-+ More -+>\s*//;
6918
6919 # no post-processing needed - just file it
6920 ProcessHistory("","","","$_");
6921 }
6922}
6923
6924# Main
6925@commandtable=(
6926 {'show version' => "ShowVersion"},
6927 {'show config' => "ShowConfig"},
6928);
6929
6930# Use array to preserve order of commands, and hash for mapping to subroutine
6931my (%commands, @commands);
6932foreach (@commandtable) {
6933 push @commands, (keys(%{$_}))[0];
6934 $commands{$commands[$#commands]}= (values(%{$_}))[0];
6935};
6936$commandstr=join(";",@commands);
6937$cmds_regexp = join("|", map quotemeta($_), @commands);
6938$commandcnt = scalar(keys %commands);
6939
6940if (length($host) == 0) {
6941 if ($file) {
6942 print(STDERR "Too few arguments: file name required\n");
6943 exit(1);
6944 } else {
6945 print(STDERR "Too few arguments: host name required\n");
6946 exit(1);
6947 }
6948}
6949if ($opt_C) {
6950 print "tlogin -t $timeo -c\'$commandstr\' $host\n";
6951 exit(0);
6952}
6953open(OUTPUT,">$host.new") || die "Can't open $host.new for writing: $!\n";
6954select(OUTPUT);
6955# make OUTPUT unbuffered if debugging
6956if ($debug) { $| = 1; }
6957
6958if ($file) {
6959 print STDERR "opening file $host\n" if ($debug);
6960 print STDOUT "opening file $host\n" if ($log);
6961 open(INPUT,"<$host") || die "open failed for $host: $!\n";
6962} else {
6963 print STDERR "executing tlogin -t $timeo -c\"$commandstr\" $host\n" if ($debug);
6964 print STDOUT "executing tlogin -t $timeo -c\"$commandstr\" $host\n" if ($log);
6965 if (defined($ENV{NOPIPE}) && $ENV{NOPIPE} =~ /^YES/i) {
6966 system "tlogin -t $timeo -c \"$commandstr\" $host </dev/null > $host.raw 2>&1" || die "tlogin failed for $host: $!\n";
6967 open(INPUT, "< $host.raw") || die "tlogin failed for $host: $!\n";
6968 } else {
6969 open(INPUT,"tlogin -t $timeo -c \"$commandstr\" $host </dev/null |") || die "tlogin failed for $host: $!\n";
6970 }
6971}
6972
6973# determine password filtering mode
6974if ($ENV{"FILTER_PWDS"} =~ /no/i) {
6975 $filter_pwds = 0;
6976} elsif ($ENV{"FILTER_PWDS"} =~ /all/i) {
6977 $filter_pwds = 2;
6978} else {
6979 $filter_pwds = 1;
6980}
6981
6982ProcessHistory("","","","#RANCID-CONTENT-TYPE: netopia\n#\n");
6983ProcessHistory("COMMENTS","keysort","B0","#\n");
6984ProcessHistory("COMMENTS","keysort","D0","#\n");
6985ProcessHistory("COMMENTS","keysort","F0","#\n");
6986ProcessHistory("COMMENTS","keysort","G0","#\n");
6987TOP: while(<INPUT>) {
6988 tr/\015//d;
6989 if (/[>#]\s?exit$/) {
6990 $clean_run=1;
6991 last;
6992 }
6993 if (/^Error:/) {
6994 print STDOUT ("$host tlogin error: $_");
6995 print STDERR ("$host tlogin error: $_") if ($debug);
6996 $clean_run=0;
6997 last;
6998 }
6999 while (/#\s*($cmds_regexp)\s*$/) {
7000 $cmd = $1;
7001 if (!defined($prompt)) {
7002 $prompt = "#"; # crude but effective
7003 $prompt =~ s/([][}{)(\\])/\\$1/g;
7004 print STDERR ("PROMPT MATCH: $prompt\n") if ($debug);
7005 }
7006 print STDERR ("HIT COMMAND:$_") if ($debug);
7007 if (! defined($commands{$cmd})) {
7008 print STDERR "$host: found unexpected command - \"$cmd\"\n";
7009 $clean_run = 0;
7010 last TOP;
7011 }
7012 $rval = &{$commands{$cmd}}(*INPUT, *OUTPUT, $cmd);
7013 delete($commands{$cmd});
7014 if ($rval == -1) {
7015 $clean_run = 0;
7016 last TOP;
7017 }
7018 }
7019}
7020print STDOUT "Done $logincmd: $_\n" if ($log);
7021# Flush History
7022ProcessHistory("","","","");
7023# Cleanup
7024close(INPUT);
7025close(OUTPUT);
7026
7027if (defined($ENV{NOPIPE}) && $ENV{NOPIPE} =~ /^YES/i) {
7028 unlink("$host.raw") if (! $debug);
7029}
7030
7031# check for completeness
7032if (scalar(%commands) || !$clean_run || !$found_end) {
7033 if (scalar(keys %commands) eq $commandcnt) {
7034 printf(STDERR "$host: missed cmd(s): all commands\n");
7035 } elsif (scalar(%commands)) {
7036 printf(STDOUT "$host: missed cmd(s): %s\n", join(',', keys(%commands)));
7037 printf(STDERR "$host: missed cmd(s): %s\n", join(',', keys(%commands))) if ($debug);
7038 }
7039 if (!$clean_run || !$found_end) {
7040 print STDOUT "$host: End of run not found\n";
7041 print STDERR "$host: End of run not found\n" if ($debug);
7042 system("/usr/bin/tail -1 $host.new");
7043 }
7044 unlink "$host.new" if (! $debug);
7045}
7046#! /usr/bin/expect --
7047##
7048## $Id: wlogin.in 3201 2015-11-05 00:48:01Z heas $
7049##
7050## rancid 3.4.1
7051## Copyright (c) 1997-2016 by Terrapin Communications, Inc.
7052## All rights reserved.
7053##
7054## This code is derived from software contributed to and maintained by
7055## Terrapin Communications, Inc. by Henry Kilmer, John Heasley, Andrew Partan,
7056## Pete Whiting, Austin Schutz, and Andrew Fort.
7057##
7058## Redistribution and use in source and binary forms, with or without
7059## modification, are permitted provided that the following conditions
7060## are met:
7061## 1. Redistributions of source code must retain the above copyright
7062## notice, this list of conditions and the following disclaimer.
7063## 2. Redistributions in binary form must reproduce the above copyright
7064## notice, this list of conditions and the following disclaimer in the
7065## documentation and/or other materials provided with the distribution.
7066## 3. All advertising materials mentioning features or use of this software
7067## must display the following acknowledgement:
7068## This product includes software developed by Terrapin Communications,
7069## Inc. and its contributors for RANCID.
7070## 4. Neither the name of Terrapin Communications, Inc. nor the names of its
7071## contributors may be used to endorse or promote products derived from
7072## this software without specific prior written permission.
7073## 5. It is requested that non-binding fixes and modifications be contributed
7074## back to Terrapin Communications, Inc.
7075##
7076## THIS SOFTWARE IS PROVIDED BY Terrapin Communications, INC. AND CONTRIBUTORS
7077## ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
7078## TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
7079## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COMPANY OR CONTRIBUTORS
7080## BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
7081## CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
7082## SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
7083## INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
7084## CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
7085## ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
7086## POSSIBILITY OF SUCH DAMAGE.
7087##
7088## It is the request of the authors, but not a condition of license, that
7089## parties packaging or redistributing RANCID NOT distribute altered versions
7090## of the etc/rancid.types.base file nor alter how this file is processed nor
7091## when in relation to etc/rancid.types.conf. The goal of this is to help
7092## suppress our support costs. If it becomes a problem, this could become a
7093## condition of license.
7094#
7095# The expect login scripts were based on Erik Sherk's gwtn, by permission.
7096#
7097# The original looking glass software was written by Ed Kern, provided by
7098# permission and modified beyond recognition.
7099#
7100# wlogin - Cisco Wireless Lan Controller login
7101#
7102# Modified from clogin for use with WLCs 4/17/2008 - Josh Yost
7103#
7104# Most options are intuitive for logging into a Cisco router.
7105# The default is to enable (thus -noenable). Some folks have
7106# setup tacacs to have a user login at priv-lvl = 15 (enabled)
7107# so the -autoenable flag was added for this case (don't go through
7108# the process of enabling and the prompt will be the "#" prompt.
7109# The default username password is the same as the vty password.
7110#
7111
7112# Usage line
7113set usage "Usage: $argv0 \[-dSV\] \[-autoenable\] \[-noenable\] \[-c command\] \
7114\[-Evar=x\] \[-e enable-password\] \[-f cloginrc-file\] \[-p user-password\] \
7115\[-r passphrase\] \[-s script-file\] \[-t timeout\] \[-u username\] \
7116\[-v vty-password\] \[-w enable-username\] \[-x command-file\] \
7117\[-y ssh_cypher_type\] router \[router...\]\n"
7118
7119# env(CLOGIN) may contain:
7120# x == do not set xterm banner or name
7121
7122# Password file
7123set password_file $env(HOME)/.cloginrc
7124# Default is to login to the router
7125set do_command 0
7126set do_script 0
7127# The default is to automatically enable
7128set avenable 1
7129# The default is that you login non-enabled (tacacs can have you login already
7130# enabled)
7131set avautoenable 0
7132# The default is to look in the password file to find the passwords. This
7133# tracks if we receive them on the command line.
7134set do_passwd 1
7135set do_enapasswd 1
7136# Sometimes routers take awhile to answer (the default is 10 sec)
7137set timeoutdflt 10
7138# Some CLIs having problems if we write too fast (Extreme, PIX, Cat)
7139set send_human {.2 .1 .4 .2 1}
7140
7141# Find the user in the ENV, or use the unix userid.
7142if {[ info exists env(CISCO_USER) ]} {
7143 set default_user $env(CISCO_USER)
7144} elseif {[ info exists env(USER) ]} {
7145 set default_user $env(USER)
7146} elseif {[ info exists env(LOGNAME) ]} {
7147 set default_user $env(LOGNAME)
7148} else {
7149 # This uses "id" which I think is portable. At least it has existed
7150 # (without options) on all machines/OSes I've been on recently -
7151 # unlike whoami or id -nu.
7152 if [ catch {exec id} reason ] {
7153 send_error "\nError: could not exec id: $reason\n"
7154 exit 1
7155 }
7156 regexp {\(([^)]*)} "$reason" junk default_user
7157}
7158
7159# Sometimes routers take awhile to answer (the default is 10 sec)
7160set timeout 45
7161
7162# Process the command line
7163for {set i 0} {$i < $argc} {incr i} {
7164 set arg [lindex $argv $i]
7165
7166 switch -glob -- $arg {
7167 # Command to run.
7168 -c* {
7169 if {! [ regexp .\[cC\](.+) $arg ignore command]} {
7170 incr i
7171 set command [ lindex $argv $i ]
7172 }
7173 set do_command 1
7174 # Expect debug mode
7175 } -d* {
7176 exp_internal 1
7177 # Environment variable to pass to -s scripts
7178 } -E*
7179 {
7180 if {[ regexp .\[E\](.+)=(.+) $arg ignore varname varvalue]} {
7181 set E$varname $varvalue
7182 } else {
7183 send_user "\nError: invalid format for -E in $arg\n"
7184 exit 1
7185 }
7186 # alternate cloginrc file
7187 } -f* {
7188 if {! [ regexp .\[fF\](.+) $arg ignore password_file]} {
7189 incr i
7190 set password_file [ lindex $argv $i ]
7191 }
7192 # VTY Password
7193 } -p* -
7194 -P* {
7195 if {! [ regexp .\[pP\](.+) $arg ignore userpasswd]} {
7196 incr i
7197 set userpasswd [ lindex $argv $i ]
7198 }
7199 set do_passwd 0
7200 # Enable Password
7201 } -e*
7202 {
7203 if {! [ regexp .\[e\](.+) $arg ignore enapasswd]} {
7204 incr i
7205 set enapasswd [ lindex $argv $i ]
7206 }
7207 set do_enapasswd 0
7208 # Expect script to run.
7209 } -s* {
7210 if {! [ regexp .\[sS\](.+) $arg ignore sfile]} {
7211 incr i
7212 set sfile [ lindex $argv $i ]
7213 }
7214 if { ! [ file readable $sfile ] } {
7215 send_user "\nError: Can't read $sfile\n"
7216 exit 1
7217 }
7218 set do_script 1
7219 # save config on exit
7220 } -S* {
7221 set do_saveconfig 1
7222 # Timeout
7223 } -t* {
7224 if {! [ regexp .\[tT\](.+) $arg ignore timeout]} {
7225 incr i
7226 set timeout [ lindex $argv $i ]
7227 }
7228 # VTY Password
7229 } -v* {
7230 if {! [ regexp .\[vV\](.+) $arg ignore passwd]} {
7231 incr i
7232 set passwd [ lindex $argv $i ]
7233 }
7234 set do_passwd 0
7235 # Version string
7236 } -V* {
7237 send_user "rancid 3.4.1\n"
7238 exit 0
7239 # Enable Username
7240 } -w* -
7241 -W* {
7242 if {! [ regexp .\[wW\](.+) $arg ignore enauser]} {
7243 incr i
7244 set enausername [ lindex $argv $i ]
7245 }
7246 # Username
7247 } -u* {
7248 if {! [ regexp .\[uU\](.+) $arg ignore user]} {
7249 incr i
7250 set username [ lindex $argv $i ]
7251 }
7252 # Timeout
7253 } -t* {
7254 if {! [regexp .\[tT\](.+) $arg ignore timeout]} {
7255 incr i
7256 set timeoutdflt [lindex $argv $i]
7257 }
7258 # Command file
7259 } -x* {
7260 if {! [ regexp .\[xX\](.+) $arg ignore cmd_file]} {
7261 incr i
7262 set cmd_file [ lindex $argv $i ]
7263 }
7264 if [ catch {set cmd_fd [open $cmd_file r]} reason ] {
7265 send_user "\nError: $reason\n"
7266 exit 1
7267 }
7268 set cmd_text [read $cmd_fd]
7269 close $cmd_fd
7270 set command [join [split $cmd_text \n] \;]
7271 set do_command 1
7272 # 'ssh -c' cypher type
7273 } -y* {
7274 if {! [ regexp .\[eE\](.+) $arg ignore cypher]} {
7275 incr i
7276 set cypher [ lindex $argv $i ]
7277 }
7278 # Do we enable?
7279 } -noenable {
7280 set avenable 0
7281 # Does tacacs automatically enable us?
7282 } -autoenable {
7283 set avautoenable 1
7284 set avenable 0
7285 } -* {
7286 send_user "\nError: Unknown argument! $arg\n"
7287 send_user $usage
7288 exit 1
7289 } default {
7290 break
7291 }
7292 }
7293}
7294# Process routers...no routers listed is an error.
7295if { $i == $argc } {
7296 send_user "\nError: $usage"
7297}
7298
7299# Only be quiet if we are running a script (it can log its output
7300# on its own)
7301if { $do_script } {
7302 log_user 0
7303} else {
7304 log_user 1
7305}
7306
7307#
7308# Done configuration/variable setting. Now run with it...
7309#
7310
7311# Sets Xterm title if interactive...if its an xterm and the user cares
7312proc label { host } {
7313 global env
7314 # if CLOGIN has an 'x' in it, don't set the xterm name/banner
7315 if [info exists env(CLOGIN)] {
7316 if {[string first "x" $env(CLOGIN)] != -1} { return }
7317 }
7318 # take host from ENV(TERM)
7319 if [info exists env(TERM)] {
7320 if [regexp \^(xterm|vs) $env(TERM) ignore ] {
7321 send_user "\033]1;[lindex [split $host "."] 0]\a"
7322 send_user "\033]2;$host\a"
7323 }
7324 }
7325}
7326
7327# This is a helper function to make the password file easier to
7328# maintain. Using this the password file has the form:
7329# add password sl* pete cow
7330# add password at* steve
7331# add password * hanky-pie
7332proc add {var args} { global int_$var ; lappend int_$var $args}
7333proc include {args} {
7334 global env
7335 regsub -all "(^{|}$)" $args {} args
7336 if { [ regexp "^/" $args ignore ] == 0 } {
7337 set args $env(HOME)/$args
7338 }
7339 source_password_file $args
7340}
7341
7342proc find {var router} {
7343 upvar int_$var list
7344 if { [info exists list] } {
7345 foreach line $list {
7346 if { [string match -nocase [lindex $line 0] $router ] } {
7347 return [lrange $line 1 end]
7348 }
7349 }
7350 }
7351 return {}
7352}
7353
7354# Loads the password file. Note that as this file is tcl, and that
7355# it is sourced, the user better know what to put in there, as it
7356# could install more than just password info... I will assume however,
7357# that a "bad guy" could just as easy put such code in the clogin
7358# script, so I will leave .cloginrc as just an extention of that script
7359proc source_password_file { password_file } {
7360 global env
7361 if { ! [file exists $password_file] } {
7362 send_user "\nError: password file ($password_file) does not exist\n"
7363 exit 1
7364 }
7365 file stat $password_file fileinfo
7366 if { [expr ($fileinfo(mode) & 007)] != 0000 } {
7367 send_user "\nError: $password_file must not be world readable/writable\n"
7368 exit 1
7369 }
7370 if [ catch {source $password_file} reason ] {
7371 send_user "\nError: $reason\n"
7372 exit 1
7373 }
7374}
7375
7376# Log into the router.
7377# returns: 0 on success, 1 on failure, -1 if rsh was used successfully
7378proc login { router user userpswd passwd enapasswd cmethod cyphertype } {
7379 global spawn_id in_proc do_command do_script platform
7380 global prompt u_prompt p_prompt e_prompt sshcmd
7381 set in_proc 1
7382 set uprompt_seen 0
7383
7384 # try each of the connection methods in $cmethod until one is successful
7385 set progs [llength $cmethod]
7386 foreach prog [lrange $cmethod 0 end] {
7387 incr progs -1
7388 if [string match "telnet*" $prog] {
7389 regexp {telnet(:([^[:space:]]+))*} $prog methcmd suffix port
7390 if {"$port" == ""} {
7391 set retval [catch {spawn telnet $router} reason]
7392 } else {
7393 set retval [catch {spawn telnet $router $port} reason]
7394 }
7395 if { $retval } {
7396 send_user "\nError: telnet failed: $reason\n"
7397 return 1
7398 }
7399 } elseif [string match "ssh*" $prog] {
7400 # ssh to the router & try to login
7401 regexp {ssh(:([^[:space:]]+))*} $prog methcmd suffix port
7402 set cmd $sshcmd
7403 if {"$port" != ""} {
7404 set cmd "$cmd -p $port"
7405 }
7406 set retval [catch {eval spawn [split "$cmd -c $cyphertype -x -l $user $router" { }]} reason]
7407 if { $retval } {
7408 send_user "\nError: $cmd failed: $reason\n"
7409 return 1
7410 }
7411 } elseif ![string compare $prog "rsh"] {
7412 global command
7413
7414 if { ! $do_command } {
7415 if { [llength $cmethod] == 1 } {
7416 send_user "\nError: rsh is an invalid method for -x and "
7417 send_user "interactive logins\n"
7418 }
7419 if { $progs == 0 } {
7420 return 1
7421 }
7422 continue;
7423 }
7424
7425 # handle escaped ;s in commands, and ;; and ^;
7426 regsub -all {([^\\]);;} $command "\\1;\u002;" esccommand
7427 regsub {^;} $esccommand "\u002;" command
7428 set sep "\\1\u001"
7429 regsub -all {([^\\])\;} $command "$sep" esccommand
7430 set sep "\u001"
7431 set commands [split $esccommand $sep]
7432 set num_commands [llength $commands]
7433 set rshfail 0
7434 for {set i 0} {$i < $num_commands && !$rshfail} { incr i} {
7435 log_user 0
7436 set retval [ catch {spawn rsh $user@$router [lindex $commands $i] } reason ]
7437 if { $retval } {
7438 send_user "\nError: rsh failed: $reason\n"
7439 log_user 1; return 1
7440 }
7441 send_user "$router# [lindex $commands $i]\n"
7442
7443 # rcmd does not get a pager and no prompts, so we just have to
7444 # look for failures & lines.
7445 expect {
7446 "Connection refused" { catch {close}; wait;
7447 send_user "\nError: Connection\
7448 Refused ($prog): $router\n"
7449 set rshfail 1
7450 }
7451 -re "(Connection closed by|Connection to \[^\n\r]+ closed)" {
7452 catch {close}; wait;
7453 send_user "\nError: Connection\
7454 closed ($prog): $router\n"
7455 set rshfail 1
7456 }
7457 "Host is unreachable" { catch {close}; wait;
7458 send_user "\nError: Host Unreachable:\
7459 $router\n"
7460 set rshfail 1
7461 }
7462 "No address associated with" {
7463 catch {close}; wait;
7464 send_user "\nError: Unknown host\
7465 $router\n"
7466 set rshfail 1
7467 }
7468 -re "\b+" { exp_continue }
7469 -re "\[\n\r]+" { send_user -- "$expect_out(buffer)"
7470 exp_continue
7471 }
7472 timeout { catch {close}; wait
7473 send_user "\nError: TIMEOUT reached\n"
7474 set rshfail 1
7475 }
7476 eof { catch {close}; wait }
7477 }
7478 log_user 1
7479 }
7480 if { $rshfail } {
7481 if { !$progs } {
7482 return 1
7483 } else {
7484 continue
7485 }
7486 }
7487 # fake the end of the session for rancid.
7488 send_user "$router# logout\n"
7489 # return rsh "success"
7490 return -1
7491 } else {
7492 send_user "\nError: unknown connection method: $prog\n"
7493 return 1
7494 }
7495 sleep 0.3
7496
7497 # This helps cleanup each expect clause.
7498 expect_after {
7499 timeout {
7500 send_user "\nError: TIMEOUT reached\n"
7501 catch {close}; wait
7502 if { $in_proc} {
7503 return 1
7504 } else {
7505 continue
7506 }
7507 } eof {
7508 send_user "\nError: EOF received\n"
7509 catch {close}; wait
7510 if { $in_proc} {
7511 return 1
7512 } else {
7513 continue
7514 }
7515 }
7516 }
7517
7518 # Here we get a little tricky. There are several possibilities:
7519 # the router can ask for a username and passwd and then
7520 # talk to the TACACS server to authenticate you, or if the
7521 # TACACS server is not working, then it will use the enable
7522 # passwd. Or, the router might not have TACACS turned on,
7523 # then it will just send the passwd.
7524 # if telnet fails with connection refused, try ssh
7525 expect {
7526 -re "(Connection refused|Secure connection \[^\n\r]+ refused)" {
7527 catch {close}; wait
7528 if !$progs {
7529 send_user "\nError: Connection Refused ($prog): $router\n"
7530 return 1
7531 }
7532 }
7533 -re "(Connection closed by|Connection to \[^\n\r]+ closed)" {
7534 catch {close}; wait
7535 if !$progs {
7536 send_user "\nError: Connection closed ($prog): $router\n"
7537 return 1
7538 }
7539 }
7540 eof { send_user "\nError: Couldn't login: $router\n"; wait; return 1 }
7541 -nocase "unknown host\r" {
7542 catch {close};
7543 send_user "\nError: Unknown host $router\n"; wait; return 1
7544 }
7545 "Host is unreachable" {
7546 catch {close};
7547 send_user "\nError: Host Unreachable: $router\n"; wait; return 1
7548 }
7549 "No address associated with name" {
7550 catch {close};
7551 send_user "\nError: Unknown host $router\n"; wait; return 1
7552 }
7553 -re "(Host key not found |The authenticity of host .* be established).*\(yes\/no\)\?" {
7554 send "yes\r"
7555 send_user "\nHost $router added to the list of known hosts.\n"
7556 exp_continue }
7557 -re "HOST IDENTIFICATION HAS CHANGED.* \(yes\/no\)\?" {
7558 send "no\r"
7559 send_user "\nError: The host key for $router has changed. Update the SSH known_hosts file accordingly.\n"
7560 return 1 }
7561 -re "Offending key for .* \(yes\/no\)\?" {
7562 send "no\r"
7563 send_user "\nError: host key mismatch for $router. Update the SSH known_hosts file accordingly.\n"
7564 return 1 }
7565 -nocase -re "^warning: remote host denied authentication agent forwarding." {
7566 exp_continue;
7567 }
7568 -re "(denied|Sorry)" {
7569 send_user "\nError: Check your passwd for $router\n"
7570 catch {close}; wait; return 1
7571 }
7572 "Login failed" {
7573 send_user "\nError: Check your passwd for $router\n"
7574 return 1
7575 }
7576 -re "% (Bad passwords|Authentication failed)" {
7577 send_user "\nError: Check your passwd for $router\n"
7578 return 1
7579 }
7580 "Press any key to continue." {
7581 # send_user "Pressing the ANY key\n"
7582 send "\r"
7583 exp_continue
7584 }
7585 -re "Enter Selection: " {
7586 # Catalyst 1900s have some lame menu. Enter
7587 # K to reach a command-line.
7588 send "K\r"
7589 exp_continue;
7590 }
7591 -re "@\[^\r\n]+ $p_prompt" {
7592 # ssh pwd prompt
7593 sleep 1
7594 send "$userpswd\r"
7595 exp_continue
7596 }
7597 -re "$u_prompt" {
7598 send "$user\r"
7599 set uprompt_seen 1
7600 exp_continue
7601 }
7602 -re "$p_prompt" {
7603 sleep 1
7604 if {$uprompt_seen == 1} {
7605 send "$userpswd\r"
7606 } else {
7607 send "$passwd\r"
7608 }
7609 exp_continue
7610 }
7611 -re "$prompt" { break; }
7612 "Login invalid" {
7613 send_user "\nError: Invalid login: $router\n";
7614 catch {close}; wait; return 1
7615 }
7616 }
7617 }
7618
7619 set in_proc 0
7620 return 0
7621}
7622
7623# Enable
7624proc do_enable { enauser enapasswd } {
7625 global prompt in_proc
7626 global u_prompt e_prompt
7627 set in_proc 1
7628
7629 send "enable\r"
7630 expect {
7631 -re "$u_prompt" { send "$enauser\r"; exp_continue}
7632 -re "$e_prompt" { send "$enapasswd\r"; exp_continue}
7633 "#" { set prompt "#" }
7634 "(enable)" { set prompt "> (enable) " }
7635 -re "(denied|Sorry|Incorrect)" {
7636 # % Access denied - from local auth and poss. others
7637 send_user "\nError: Check your Enable passwd\n";
7638 return 1
7639 }
7640 "% Error in authentication" {
7641 send_user "\nError: Check your Enable passwd\n"
7642 return 1
7643 }
7644 "% Bad passwords" {
7645 send_user "\nError: Check your Enable passwd\n"
7646 return 1
7647 }
7648 }
7649 # We set the prompt variable (above) so script files don't need
7650 # to know what it is.
7651 set in_proc 0
7652 return 0
7653}
7654
7655# Run commands given on the command line.
7656proc run_commands { prompt command } {
7657 global in_proc platform
7658 set in_proc 1
7659
7660 # If the prompt is (enable), then we are on a switch and the
7661 # command is "set length 0"; otherwise its "term length 0".
7662 # skip if its an extreme (since the pager can not be disabled on a
7663 # per-vty basis).
7664 if { [ string compare "extreme" "$platform" ] } {
7665 if [ regexp -- ".*> .*enable" "$prompt" ] {
7666 send "set length 0\r"
7667 # This is ugly, but reduces code duplication, allowing the
7668 # subsequent expects to handle everything as normal.
7669 set command "set logging session disable;$command"
7670 } else {
7671 send "term length 0\r"
7672 }
7673 # match cisco config mode prompts too, such as router(config-if)#,
7674 # but catalyst does not change in this fashion.
7675 regsub -all {^(.{1,11}).*([#>])$} $prompt {\1} reprompt
7676 # escape any parens in the prompt, such as "(enable)"
7677 regsub -all {[)(]} $reprompt {\\&} reprompt
7678 append reprompt {([^#>\r\n]+)?[#>](\\([^)\\r\\n]+\\))?}
7679 expect {
7680 -re $reprompt {}
7681 -re "\[\n\r]+" { exp_continue }
7682 }
7683 } else {
7684 regsub -all "\[)(]" $prompt {\\&} reprompt
7685 }
7686
7687 # this is the only way i see to get rid of more prompts in o/p..grrrrr
7688 log_user 0
7689
7690 # handle escaped ;s in commands, and ;; and ^;
7691 regsub -all {([^\\]);;} $command "\\1;\u002;" esccommand
7692 regsub {^;} $esccommand "\u002;" command
7693 set sep "\\1\u001"
7694 regsub -all {([^\\])\;} $command "$sep" esccommand
7695 set sep "\u001"
7696 set commands [split $esccommand $sep]
7697 set num_commands [llength $commands]
7698 # the pager can not be turned off on the PIX, so we have to look
7699 # for the "More" prompt. the extreme is equally obnoxious, with a
7700 # global switch in the config.
7701 for {set i 0} {$i < $num_commands} { incr i} {
7702 send "[subst -nocommands [lindex $commands $i]]\r"
7703 expect {
7704 -re "\b+" { exp_continue }
7705 -re "^\[^\n\r *]*$reprompt" { send_user -- "$expect_out(buffer)"
7706 }
7707 -re "^\[^\n\r]*$reprompt." { send_user -- "$expect_out(buffer)"
7708 exp_continue }
7709 -re "^--More--\r\n" { # specific match c1900 pager
7710 send " "
7711 exp_continue }
7712 -re "\[\n\r]+" { send_user -- "$expect_out(buffer)"
7713 exp_continue }
7714 -re "\[^\r\n]*Press <SPACE> to cont\[^\r\n]*" {
7715 send " "
7716 # bloody ^[[2K after " "
7717 expect {
7718 -re "^\[^\r\n]*\r" {}
7719 }
7720 exp_continue
7721 }
7722 -re "^Press Enter to continue\.\.\.\[^\n\r]*" {
7723 send "\r"
7724 exp_continue }
7725 -re "^Press Enter to continue or \[^\n\r]* abort" {
7726 send "\r"
7727 exp_continue }
7728 -re "^--More or \[^\n\r]* abort" {
7729 send " "
7730 exp_continue }
7731 -re "^ *--More--\[^\n\r]*" {
7732 send " "
7733 exp_continue }
7734 -re "^<-+ More -+>\[^\n\r]*" {
7735 send_user -- "$expect_out(buffer)"
7736 send " "
7737 exp_continue }
7738 }
7739 }
7740 log_user 1
7741
7742 # Send an Unconditional CTRL Z to exit out of any context the WLC prompt
7743 # may be in
7744 send "\032"
7745
7746 expect {
7747 -re "(.+)>" { # the Cisco CE and Jnx ERX
7748 # return to non-enabled mode
7749 # on exit in enabled mode.
7750 send "logout\r"
7751 exp_continue;
7752 }
7753 -re "Would you like to save them .+" {
7754 send "n\r"
7755 exp_continue
7756 }
7757 -re "\[\n\r]+" { exp_continue }
7758 timeout { return 0 }
7759 eof { return 0 }
7760 }
7761 set in_proc 0
7762}
7763
7764#
7765# For each router... (this is main loop)
7766#
7767source_password_file $password_file
7768set in_proc 0
7769foreach router [lrange $argv $i end] {
7770 set router [string tolower $router]
7771 # attempt at platform switching.
7772 set platform ""
7773 send_user -- "$router\n"
7774
7775 # device timeout
7776 set timeout [find timeout $router]
7777 if { [llength $timeout] == 0 } {
7778 set timeout $timeoutdflt
7779 }
7780
7781 # Figure out the prompt.
7782 # autoenable is off by default. If we have it defined, it was done
7783 # on the command line. If it is not specifically set on the command
7784 # line, check the password file.
7785 if $avautoenable {
7786 set autoenable 1
7787 set enable 0
7788 set prompt "(#| \\(enable\\))"
7789 } else {
7790 set ae [find autoenable $router]
7791 if { "$ae" == "1" } {
7792 set autoenable 1
7793 set enable 0
7794 set prompt "(#| \\(enable\\)|>)"
7795 } else {
7796 set autoenable 0
7797 set enable $avenable
7798 set prompt ">"
7799 }
7800 }
7801
7802 # look for noenable option in .cloginrc
7803 if { [find noenable $router] != "" } {
7804 set enable 0
7805 }
7806
7807 # Figure out passwords
7808 if { $do_passwd || $do_enapasswd } {
7809 set pswd [find password $router]
7810 if { [llength $pswd] == 0 } {
7811 send_user -- "\nError: no password for $router in $password_file.\n"
7812 continue
7813 }
7814 if { $enable && $do_enapasswd && $autoenable == 0 && [llength $pswd] < 2 } {
7815 send_user -- "\nError: no enable password for $router in $password_file.\n"
7816 continue
7817 }
7818 set passwd [join [lindex $pswd 0] ""]
7819 set enapasswd [join [lindex $pswd 1] ""]
7820 }
7821
7822 # Figure out username
7823 if {[info exists username]} {
7824 # command line username
7825 set ruser $username
7826 } else {
7827 set ruser [join [find user $router] ""]
7828 if { "$ruser" == "" } { set ruser $default_user }
7829 }
7830
7831 # Figure out username's password (if different from the vty password)
7832 if {[info exists userpasswd]} {
7833 # command line username
7834 set userpswd $userpasswd
7835 } else {
7836 set userpswd [join [find userpassword $router] ""]
7837 if { "$userpswd" == "" } { set userpswd $passwd }
7838 }
7839
7840 # Figure out enable username
7841 if {[info exists enausername]} {
7842 # command line enausername
7843 set enauser $enausername
7844 } else {
7845 set enauser [join [find enauser $router] ""]
7846 if { "$enauser" == "" } { set enauser $ruser }
7847 }
7848
7849 # Figure out prompts
7850 set u_prompt [find userprompt $router]
7851 if { "$u_prompt" == "" } {
7852 set u_prompt "(Username|Login|login|user name|User):"
7853 } else {
7854 set u_prompt [join [lindex $u_prompt 0] ""]
7855 }
7856 set p_prompt [find passprompt $router]
7857 if { "$p_prompt" == "" } {
7858 set p_prompt "(\[Pp]assword|passwd):"
7859 } else {
7860 set p_prompt [join [lindex $p_prompt 0] ""]
7861 }
7862 set e_prompt [find enableprompt $router]
7863 if { "$e_prompt" == "" } {
7864 set e_prompt "\[Pp]assword:"
7865 } else {
7866 set e_prompt [join [lindex $e_prompt 0] ""]
7867 }
7868
7869 # Figure out cypher type
7870 if {[info exists cypher]} {
7871 # command line cypher type
7872 set cyphertype $cypher
7873 } else {
7874 set cyphertype [find cyphertype $router]
7875 if { "$cyphertype" == "" } { set cyphertype "3des" }
7876 }
7877
7878 # Figure out connection method
7879 set cmethod [find method $router]
7880 if { "$cmethod" == "" } { set cmethod {{telnet} {ssh}} }
7881
7882 # Figure out the SSH executable name
7883 set sshcmd [join [lindex [find sshcmd $router] 0] ""]
7884 if { "$sshcmd" == "" } { set sshcmd {ssh} }
7885
7886 # Login to the router
7887 if {[login $router $ruser $userpswd $passwd $enapasswd $cmethod $cyphertype]} {
7888 # if login failed or rsh was successful, move on to the next device
7889 continue
7890 }
7891 if { $enable } {
7892 if {[do_enable $enauser $enapasswd]} {
7893 if { $do_command || $do_script } {
7894 close; wait
7895 continue
7896 }
7897 }
7898 }
7899 # we are logged in, now figure out the full prompt
7900 send "\r"
7901 expect {
7902 -re "\[\r\n]+" { exp_continue; }
7903 -re "^(.+\[:.])1 $prompt" { # stoopid extreme cmd-line numbers and
7904 # prompt based on state of config changes,
7905 # which may have an * at the beginning.
7906 set junk $expect_out(1,string)
7907 regsub -all "^\\\* " $expect_out(1,string) {} junk
7908 set prompt ".? ?$junk\[0-9]+ $expect_out(2,string)";
7909 set platform "extreme"
7910 }
7911 -re "^.+$prompt" { set junk $expect_out(0,string);
7912 regsub -all "\[\]\[]" $junk {\\&} prompt;
7913 }
7914 -re "^.+> \\\(enable\\\)" {
7915 set junk $expect_out(0,string);
7916 regsub -all "\[\]\[]" $junk {\\&} prompt;
7917 }
7918 -re "\(Cisco Controller\) \>" {
7919 set junk $expect_out(0,string);
7920 regsub -all "\[\]\[]" $junk {\\&} prompt;
7921 }
7922
7923 }
7924
7925 if { $do_command } {
7926 if {[run_commands $prompt $command]} {
7927 continue
7928 }
7929 } elseif { $do_script } {
7930 # If the prompt is (enable), then we are on a switch and the
7931 # command is "set length 0"; otherwise its "term length 0".
7932 if [ regexp -- ".*> .*enable" "$prompt" ] {
7933 send "set length 0\r"
7934 send "set logging session disable\r"
7935 } else {
7936 send "term length 0\r"
7937 }
7938 expect -re $prompt {}
7939 source $sfile
7940 close
7941 } else {
7942 label $router
7943 log_user 1
7944 interact
7945 }
7946
7947 # End of for each router
7948 wait
7949 sleep 0.3
7950}
7951exit 0
7952
7953#! /usr/bin/expect --
7954##
7955## $Id: xilogin.in 2255 2010-10-06 20:31:24Z heas $
7956##
7957## rancid 3.4.1
7958## Copyright (c) 1997-2016 by Terrapin Communications, Inc.
7959## All rights reserved.
7960##
7961## This code is derived from software contributed to and maintained by
7962## Terrapin Communications, Inc. by Henry Kilmer, John Heasley, Andrew Partan,
7963## Pete Whiting, Austin Schutz, and Andrew Fort.
7964##
7965## Redistribution and use in source and binary forms, with or without
7966## modification, are permitted provided that the following conditions
7967## are met:
7968## 1. Redistributions of source code must retain the above copyright
7969## notice, this list of conditions and the following disclaimer.
7970## 2. Redistributions in binary form must reproduce the above copyright
7971## notice, this list of conditions and the following disclaimer in the
7972## documentation and/or other materials provided with the distribution.
7973## 3. All advertising materials mentioning features or use of this software
7974## must display the following acknowledgement:
7975## This product includes software developed by Terrapin Communications,
7976## Inc. and its contributors for RANCID.
7977## 4. Neither the name of Terrapin Communications, Inc. nor the names of its
7978## contributors may be used to endorse or promote products derived from
7979## this software without specific prior written permission.
7980## 5. It is requested that non-binding fixes and modifications be contributed
7981## back to Terrapin Communications, Inc.
7982##
7983## THIS SOFTWARE IS PROVIDED BY Terrapin Communications, INC. AND CONTRIBUTORS
7984## ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
7985## TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
7986## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COMPANY OR CONTRIBUTORS
7987## BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
7988## CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
7989## SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
7990## INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
7991## CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
7992## ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
7993## POSSIBILITY OF SUCH DAMAGE.
7994##
7995## It is the request of the authors, but not a condition of license, that
7996## parties packaging or redistributing RANCID NOT distribute altered versions
7997## of the etc/rancid.types.base file nor alter how this file is processed nor
7998## when in relation to etc/rancid.types.conf. The goal of this is to help
7999## suppress our support costs. If it becomes a problem, this could become a
8000## condition of license.
8001#
8002# The expect login scripts were based on Erik Sherk's gwtn, by permission.
8003#
8004# The original looking glass software was written by Ed Kern, provided by
8005# permission and modified beyond recognition.
8006#
8007# xilogin - Xirrus login
8008#
8009# Most options are intuitive for logging into a Xirrus array.
8010# The default is to not enable.
8011#
8012
8013# Usage line
8014set usage "Usage: $argv0 \[-dSV\] \[-autoenable\] \[-noenable\] \[-c command\] \
8015\[-Evar=x\] \[-e enable-password\] \[-f cloginrc-file\] \[-p user-password\] \
8016\[-r passphrase\] \[-s script-file\] \[-t timeout\] \[-u username\] \
8017\[-v vty-password\] \[-w enable-username\] \[-x command-file\] \
8018\[-y ssh_cypher_type\] router \[router...\]\n"
8019
8020# env(CLOGIN) may contain:
8021# x == do not set xterm banner or name
8022
8023# Password file
8024set password_file $env(HOME)/.cloginrc
8025# Default is to login to the router
8026set do_command 0
8027set do_script 0
8028# The default is to automatically enable
8029set avenable 1
8030# The default is that you login non-enabled (tacacs can have you login already
8031# enabled)
8032set avautoenable 0
8033# The default is to look in the password file to find the passwords. This
8034# tracks if we receive them on the command line.
8035set do_passwd 1
8036set do_enapasswd 1
8037# Save config, if prompted
8038set do_saveconfig 0
8039# Sometimes routers take awhile to answer (the default is 10 sec)
8040set timeoutdflt 45
8041# Some CLIs having problems if we write too fast (Extreme, PIX, Cat)
8042set send_human {.2 .1 .4 .2 1}
8043
8044# Find the user in the ENV, or use the unix userid.
8045if {[info exists env(CISCO_USER)]} {
8046 set default_user $env(CISCO_USER)
8047} elseif {[info exists env(USER)]} {
8048 set default_user $env(USER)
8049} elseif {[info exists env(LOGNAME)]} {
8050 set default_user $env(LOGNAME)
8051} else {
8052 # This uses "id" which I think is portable. At least it has existed
8053 # (without options) on all machines/OSes I've been on recently -
8054 # unlike whoami or id -nu.
8055 if [catch {exec id} reason] {
8056 send_error "\nError: could not exec id: $reason\n"
8057 exit 1
8058 }
8059 regexp {\(([^)]*)} "$reason" junk default_user
8060}
8061if {[info exists env(CLOGINRC)]} {
8062 set password_file $env(CLOGINRC)
8063}
8064
8065# Process the command line
8066for {set i 0} {$i < $argc} {incr i} {
8067 set arg [lindex $argv $i]
8068
8069 switch -glob -- $arg {
8070 # Expect debug mode
8071 -d* {
8072 exp_internal 1
8073 # Username
8074 } -u* {
8075 if {! [regexp .\[uU\](.+) $arg ignore user]} {
8076 incr i
8077 set username [lindex $argv $i]
8078 }
8079 # VTY Password
8080 } -p* {
8081 if {! [regexp .\[pP\](.+) $arg ignore userpasswd]} {
8082 incr i
8083 set userpasswd [lindex $argv $i]
8084 }
8085 set do_passwd 0
8086 # ssh passphrase
8087 } -r* {
8088 if {! [regexp .\[rR\](.+) $arg ignore passphrase]} {
8089 incr i
8090 set vapassphrase [lindex $argv $i]
8091 }
8092 # VTY Password
8093 } -v* {
8094 if {! [regexp .\[vV\](.+) $arg ignore passwd]} {
8095 incr i
8096 set passwd [lindex $argv $i]
8097 }
8098 set do_passwd 0
8099 # Version string
8100 } -V* {
8101 send_user "rancid 3.4.1\n";
8102 exit 0
8103 # Enable Username
8104 } -w* {
8105 if {! [regexp .\[wW\](.+) $arg ignore enauser]} {
8106 incr i
8107 set enausername [lindex $argv $i]
8108 }
8109 # Environment variable to pass to -s scripts
8110 } -E* {
8111 if {[regexp .\[E\](.+)=(.+) $arg ignore varname varvalue]} {
8112 set E$varname $varvalue
8113 } else {
8114 send_user "\nError: invalid format for -E in $arg\n"
8115 exit 1
8116 }
8117 # Enable Password
8118 } -e* {
8119 if {! [regexp .\[e\](.+) $arg ignore enapasswd]} {
8120 incr i
8121 set enapasswd [lindex $argv $i]
8122 }
8123 set do_enapasswd 0
8124 # Command to run.
8125 } -c* {
8126 if {! [regexp .\[cC\](.+) $arg ignore command]} {
8127 incr i
8128 set command [lindex $argv $i]
8129 }
8130 set do_command 1
8131 # Expect script to run.
8132 } -s* {
8133 if {! [regexp .\[sS\](.+) $arg ignore sfile]} {
8134 incr i
8135 set sfile [lindex $argv $i]
8136 }
8137 if { ! [file readable $sfile] } {
8138 send_user "\nError: Can't read $sfile\n"
8139 exit 1
8140 }
8141 set do_script 1
8142 # save config on exit
8143 } -S* {
8144 set do_saveconfig 1
8145 # 'ssh -c' cypher type
8146 } -y* {
8147 if {! [regexp .\[eE\](.+) $arg ignore cypher]} {
8148 incr i
8149 set cypher [lindex $argv $i]
8150 }
8151 # alternate cloginrc file
8152 } -f* {
8153 if {! [regexp .\[fF\](.+) $arg ignore password_file]} {
8154 incr i
8155 set password_file [lindex $argv $i]
8156 }
8157 # Timeout
8158 } -t* {
8159 if {! [regexp .\[tT\](.+) $arg ignore timeout]} {
8160 incr i
8161 set timeoutdflt [lindex $argv $i]
8162 }
8163 # Command file
8164 } -x* {
8165 if {! [regexp .\[xX\](.+) $arg ignore cmd_file]} {
8166 incr i
8167 set cmd_file [lindex $argv $i]
8168 }
8169 if [catch {set cmd_fd [open $cmd_file r]} reason] {
8170 send_user "\nError: $reason\n"
8171 exit 1
8172 }
8173 set cmd_text [read $cmd_fd]
8174 close $cmd_fd
8175 set command [join [split $cmd_text \n] \;]
8176 set do_command 1
8177 # Do we enable?
8178 } -noenable {
8179 set avenable 0
8180 # Does tacacs automatically enable us?
8181 } -autoenable {
8182 set avautoenable 1
8183 set avenable 0
8184 } -* {
8185 send_user "\nError: Unknown argument! $arg\n"
8186 send_user $usage
8187 exit 1
8188 } default {
8189 break
8190 }
8191 }
8192}
8193# Process routers...no routers listed is an error.
8194if { $i == $argc } {
8195 send_user "\nError: $usage"
8196}
8197
8198# Only be quiet if we are running a script (it can log its output
8199# on its own)
8200if { $do_script } {
8201 log_user 0
8202} else {
8203 log_user 1
8204}
8205
8206#
8207# Done configuration/variable setting. Now run with it...
8208#
8209
8210# Sets Xterm title if interactive...if its an xterm and the user cares
8211proc label { host } {
8212 global env
8213 # if CLOGIN has an 'x' in it, don't set the xterm name/banner
8214 if [info exists env(CLOGIN)] {
8215 if {[string first "x" $env(CLOGIN)] != -1} { return }
8216 }
8217 # take host from ENV(TERM)
8218 if [info exists env(TERM)] {
8219 if [regexp \^(xterm|vs) $env(TERM) ignore] {
8220 send_user "\033]1;[lindex [split $host "."] 0]\a"
8221 send_user "\033]2;$host\a"
8222 }
8223 }
8224}
8225
8226# This is a helper function to make the password file easier to
8227# maintain. Using this the password file has the form:
8228# add password sl* pete cow
8229# add password at* steve
8230# add password * hanky-pie
8231proc add {var args} { global int_$var ; lappend int_$var $args}
8232proc include {args} {
8233 global env
8234 regsub -all "(^{|}$)" $args {} args
8235 if { [regexp "^/" $args ignore] == 0 } {
8236 set args $env(HOME)/$args
8237 }
8238 source_password_file $args
8239}
8240
8241proc find {var router} {
8242 upvar int_$var list
8243 if { [info exists list] } {
8244 foreach line $list {
8245 if { [string match -nocase [lindex $line 0] $router] } {
8246 return [lrange $line 1 end]
8247 }
8248 }
8249 }
8250 return {}
8251}
8252
8253# Loads the password file. Note that as this file is tcl, and that
8254# it is sourced, the user better know what to put in there, as it
8255# could install more than just password info... I will assume however,
8256# that a "bad guy" could just as easy put such code in the xilogin
8257# script, so I will leave .cloginrc as just an extention of that script
8258proc source_password_file { password_file } {
8259 global env
8260 if { ! [file exists $password_file] } {
8261 send_user "\nError: password file ($password_file) does not exist\n"
8262 exit 1
8263 }
8264 file stat $password_file fileinfo
8265 if { [expr ($fileinfo(mode) & 007)] != 0000 } {
8266 send_user "\nError: $password_file must not be world readable/writable\n"
8267 exit 1
8268 }
8269 if [catch {source $password_file} reason] {
8270 send_user "\nError: $reason\n"
8271 exit 1
8272 }
8273}
8274
8275# Log into the router.
8276# returns: 0 on success, 1 on failure, -1 if rsh was used successfully
8277proc login { router user userpswd passwd enapasswd cmethod cyphertype identfile } {
8278 global command spawn_id in_proc do_command do_script passphrase
8279 global prompt u_prompt p_prompt sshcmd
8280 set in_proc 1
8281 set uprompt_seen 0
8282
8283 # try each of the connection methods in $cmethod until one is successful
8284 set progs [llength $cmethod]
8285 foreach prog [lrange $cmethod 0 end] {
8286 incr progs -1
8287 if [string match "telnet*" $prog] {
8288 regexp {telnet(:([^[:space:]]+))*} $prog methcmd suffix port
8289 if {"$port" == ""} {
8290 set retval [catch {spawn telnet $router} reason]
8291 } else {
8292 set retval [catch {spawn telnet $router $port} reason]
8293 }
8294 if { $retval } {
8295 send_user "\nError: telnet failed: $reason\n"
8296 return 1
8297 }
8298 } elseif [string match "ssh*" $prog] {
8299 # ssh to the router & try to login with or without an identfile.
8300 regexp {ssh(:([^[:space:]]+))*} $prog methcmd suffix port
8301 set cmd $sshcmd
8302 if {"$port" != ""} {
8303 set cmd "$cmd -p $port"
8304 }
8305 if {"$identfile" != ""} {
8306 set cmd "$cmd -i $identfile"
8307 }
8308 set retval [catch {eval spawn [split "$cmd -c $cyphertype -x -l $user $router" { }]} reason]
8309 if { $retval } {
8310 send_user "\nError: $cmd failed: $reason\n"
8311 return 1
8312 }
8313 } elseif ![string compare $prog "rsh"] {
8314 if { ! $do_command } {
8315 if { [llength $cmethod] == 1 } {
8316 send_user "\nError: rsh is an invalid method for -x and "
8317 send_user "interactive logins\n"
8318 }
8319 if { $progs == 0 } {
8320 return 1
8321 }
8322 continue;
8323 }
8324
8325 # handle escaped ;s in commands, and ;; and ^;
8326 regsub -all {([^\\]);;} $command "\\1;\u002;" esccommand
8327 regsub {^;} $esccommand "\u002;" command
8328 set sep "\\1\u001"
8329 regsub -all {([^\\])\;} $command "$sep" esccommand
8330 set sep "\u001"
8331 set commands [split $esccommand $sep]
8332 set num_commands [llength $commands]
8333 set rshfail 0
8334 for {set i 0} {$i < $num_commands && !$rshfail} { incr i} {
8335 log_user 0
8336 set retval [catch {spawn rsh $user@$router [lindex $commands $i] } reason]
8337 if { $retval } {
8338 send_user "\nError: rsh failed: $reason\n"
8339 log_user 1; return 1
8340 }
8341 send_user "$router# [lindex $commands $i]\n"
8342
8343 # rcmd does not get a pager and no prompts, so we just have to
8344 # look for failures & lines.
8345 expect {
8346 "Connection refused" { catch {close}; catch {wait};
8347 send_user "\nError: Connection\
8348 Refused ($prog): $router\n"
8349 set rshfail 1
8350 }
8351 -re "(Connection closed by|Connection to \[^\n\r]+ closed)" {
8352 catch {close}; catch {wait};
8353 send_user "\nError: Connection\
8354 closed ($prog): $router\n"
8355 set rshfail 1
8356 }
8357 "Host is unreachable" { catch {close}; catch {wait};
8358 send_user "\nError: Host Unreachable:\
8359 $router\n"
8360 set rshfail 1
8361 }
8362 "No address associated with" {
8363 catch {close}; catch {wait};
8364 send_user "\nError: Unknown host\
8365 $router\n"
8366 set rshfail 1
8367 }
8368 -re "\b+" { exp_continue }
8369 -re "\[\n\r]+" { send_user -- "$expect_out(buffer)"
8370 exp_continue
8371 }
8372 timeout { catch {close}; catch {wait};
8373 send_user "\nError: TIMEOUT reached\n"
8374 set rshfail 1
8375 }
8376 eof { catch {close}; catch {wait}; }
8377 }
8378 log_user 1
8379 }
8380 if { $rshfail } {
8381 if { !$progs } {
8382 return 1
8383 } else {
8384 continue
8385 }
8386 }
8387 # fake the end of the session for rancid.
8388 send_user "$router# exit\n"
8389 # return rsh "success"
8390 return -1
8391 } else {
8392 send_user "\nError: unknown connection method: $prog\n"
8393 return 1
8394 }
8395 sleep 0.3
8396
8397 # This helps cleanup each expect clause.
8398 expect_after {
8399 timeout {
8400 send_user "\nError: TIMEOUT reached\n"
8401 catch {close}; catch {wait};
8402 if { $in_proc} {
8403 return 1
8404 } else {
8405 continue
8406 }
8407 } eof {
8408 send_user "\nError: EOF received\n"
8409 catch {close}; catch {wait};
8410 if { $in_proc} {
8411 return 1
8412 } else {
8413 continue
8414 }
8415 }
8416 }
8417
8418 # Here we get a little tricky. There are several possibilities:
8419 # the router can ask for a username and passwd and then
8420 # talk to the TACACS server to authenticate you, or if the
8421 # TACACS server is not working, then it will use the enable
8422 # passwd. Or, the router might not have TACACS turned on,
8423 # then it will just send the passwd.
8424 # if telnet fails with connection refused, try ssh
8425 expect {
8426 -re "(Connection refused|Secure connection \[^\n\r]+ refused)" {
8427 catch {close}; catch {wait};
8428 if !$progs {
8429 send_user "\nError: Connection Refused ($prog): $router\n"
8430 return 1
8431 }
8432 }
8433 -re "(Connection closed by|Connection to \[^\n\r]+ closed)" {
8434 catch {close}; catch {wait};
8435 if !$progs {
8436 send_user "\nError: Connection closed ($prog): $router\n"
8437 return 1
8438 }
8439 }
8440 eof { send_user "\nError: Couldn't login: $router\n"; wait; return 1 }
8441 -nocase "unknown host\r" {
8442 send_user "\nError: Unknown host $router\n";
8443 catch {close}; catch {wait};
8444 return 1
8445 }
8446 "Host is unreachable" {
8447 send_user "\nError: Host Unreachable: $router\n";
8448 catch {close}; catch {wait};
8449 return 1
8450 }
8451 "No address associated with name" {
8452 send_user "\nError: Unknown host $router\n";
8453 catch {close}; catch {wait};
8454 return 1
8455 }
8456 -re "(Host key not found |The authenticity of host .* be established).* \\(yes/no\\)\\?" {
8457 send "yes\r"
8458 send_user "\nHost $router added to the list of known hosts.\n"
8459 exp_continue
8460 }
8461 -re "HOST IDENTIFICATION HAS CHANGED.* \\(yes/no\\)\\?" {
8462 send "no\r"
8463 send_user "\nError: The host key for $router has changed. Update the SSH known_hosts file accordingly.\n"
8464 catch {close}; catch {wait};
8465 return 1
8466 }
8467 -re "HOST IDENTIFICATION HAS CHANGED\[^\n\r]+" {
8468 send_user "\nError: The host key for $router has changed. Update the SSH known_hosts file accordingly.\n"
8469 return 1
8470 }
8471 -re "Offending key for .* \\(yes/no\\)\\?" {
8472 send "no\r"
8473 send_user "\nError: host key mismatch for $router. Update the SSH known_hosts file accordingly.\n"
8474 catch {close}; catch {wait};
8475 return 1
8476 }
8477 -nocase -re "^warning: remote host denied authentication agent forwarding." {
8478 exp_continue;
8479 }
8480 -nocase -re "^warning:" { exp_continue }
8481 -re "(denied|Sorry)" {
8482 send_user "\nError: Check your passwd for $router\n"
8483 catch {close}; catch {wait}; return 1
8484 }
8485 "Login failed" {
8486 send_user "\nError: Check your passwd for $router\n"
8487 catch {close}; catch {wait}; return 1
8488 }
8489 -re "% (Bad passwords|Authentication failed)" {
8490 send_user "\nError: Check your passwd for $router\n"
8491 catch {close}; catch {wait}; return 1
8492 }
8493 "Press any key to continue" {
8494 # send_user "Pressing the ANY key\n"
8495 send "\r"
8496 exp_continue
8497 }
8498 -re "Enter Selection: " {
8499 # Catalyst 1900s have some lame menu. Enter
8500 # K to reach a command-line.
8501 send "K\r"
8502 exp_continue
8503 }
8504 -re "Last login:" {
8505 exp_continue
8506 }
8507 -re "@\[^\r\n]+ $p_prompt" {
8508 # ssh pwd prompt
8509 sleep 1
8510 send -- "$userpswd\r"
8511 exp_continue
8512 }
8513 -re "Enter passphrase.*: " {
8514 # sleep briefly to allow time for stty -echo
8515 sleep .3
8516 send -- "$passphrase\r"
8517 exp_continue
8518 }
8519 -re "$u_prompt" {
8520 send -- "$user\r"
8521 set uprompt_seen 1
8522 exp_continue
8523 }
8524 -re "$p_prompt" {
8525 sleep 1
8526 if {$uprompt_seen == 1} {
8527 send -- "$userpswd\r"
8528 } else {
8529 send -- "$passwd\r"
8530 }
8531 exp_continue
8532 }
8533 -re "$prompt" { break; }
8534 "Login invalid" {
8535 send_user "\nError: Invalid login: $router\n";
8536 catch {close}; catch {wait}; return 1
8537 }
8538 }
8539 }
8540
8541 set in_proc 0
8542 return 0
8543}
8544
8545# New subroutine to provide "login" command capabilities, using the enable user and enable password
8546# Login
8547proc do_login { enauser enapasswd } {
8548 global prompt in_proc
8549 global u_prompt
8550 set in_proc 1
8551
8552 send "login\r"
8553 expect {
8554 -re "$u_prompt" { send "$enauser\r"; exp_continue}
8555 "#" { set prompt "#" }
8556 "(login)" { set prompt "> (login) " }
8557 -re "(denied|Sorry|Incorrect)" {
8558 # % Access denied - from local auth and poss. others
8559 send_user "\nError: Check your Login passwd\n";
8560 return 1
8561 }
8562 "% Error in authentication" {
8563 send_user "\nError: Check your Login passwd\n"
8564 return 1
8565 }
8566 "% Bad passwords" {
8567 send_user "\nError: Check your Login passwd\n"
8568 return 1
8569 }
8570 }
8571 # We set the prompt variable (above) so script files don't need
8572 # to know what it is.
8573 set in_proc 0
8574 return 0
8575}
8576
8577# Run commands given on the command line.
8578proc run_commands { prompt command } {
8579 global do_saveconfig in_proc
8580 set in_proc 1
8581
8582 # Disable paging
8583 send "more off \r"
8584 # escape any parens in the prompt, such as "(enable)"
8585 regsub -all {[)(]} $prompt {\\&} reprompt
8586 expect {
8587 -re $reprompt {}
8588 -re "\[\n\r]+" { exp_continue }
8589 }
8590
8591 # Disable command auto-completion
8592 send "auto-complete off \r"
8593 expect {
8594 -re $reprompt {}
8595 -re "\[\n\r]+" { exp_continue }
8596 }
8597
8598 # this is the only way i see to get rid of more prompts in o/p..grrrrr
8599 log_user 0
8600
8601 # handle escaped ;s in commands, and ;; and ^;
8602 regsub -all {([^\\]);;} $command "\\1;\u002;" esccommand
8603 regsub {^;} $esccommand "\u002;" command
8604 set sep "\\1\u001"
8605 regsub -all {([^\\])\;} $command "$sep" esccommand
8606 set sep "\u001"
8607 set commands [split $esccommand $sep]
8608 set num_commands [llength $commands]
8609 # the pager can not be turned off on the PIX, so we have to look
8610 # for the "More" prompt. the extreme is equally obnoxious, with a
8611 # global switch in the config.
8612 for {set i 0} {$i < $num_commands} { incr i} {
8613 send -- "[subst -nocommands [lindex $commands $i]]\r"
8614 expect {
8615 -re "\b+" { exp_continue }
8616 -re "^\[^\n\r *]*$reprompt" { send_user -- "$expect_out(buffer)"
8617 }
8618 -re "^\[^\n\r]*$reprompt." { send_user -- "$expect_out(buffer)"
8619 exp_continue
8620 }
8621 -re "^--MORE--\[\r\n]+" { # match Xirrus pager
8622 send " "
8623 exp_continue
8624 }
8625 -re "\[\n\r]+" { send_user -- "$expect_out(buffer)"
8626 exp_continue
8627 }
8628 }
8629 }
8630 log_user 1
8631
8632 send -h "quit\r"
8633 expect {
8634 "Do you want to save changes to flash" { # Xirrus
8635 if {$do_saveconfig} {
8636 catch {send "y\r"}
8637 } else {
8638 catch {send "n\r"}
8639 }
8640 exp_continue
8641 }
8642 -re "\[\n\r]+" { exp_continue }
8643 timeout { catch {close}; catch {wait};
8644 return 0
8645 }
8646 eof { return 0 }
8647 }
8648 set in_proc 0
8649}
8650
8651#
8652# For each router... (this is main loop)
8653#
8654source_password_file $password_file
8655set in_proc 0
8656set exitval 0
8657foreach router [lrange $argv $i end] {
8658 set router [string tolower $router]
8659 send_user -- "$router\n"
8660
8661 # device timeout
8662 set timeout [find timeout $router]
8663 if { [llength $timeout] == 0 } {
8664 set timeout $timeoutdflt
8665 }
8666
8667 # Default prompt.
8668 set prompt "(>|#)"
8669
8670 # Figure out passwords
8671 if { $do_passwd || $do_enapasswd } {
8672 set pswd [find password $router]
8673 if { [llength $pswd] == 0 } {
8674 send_user -- "\nError: no password for $router in $password_file.\n"
8675 continue
8676 }
8677 set passwd [join [lindex $pswd 0] ""]
8678 set enapasswd [join [lindex $pswd 1] ""]
8679 } else {
8680 set passwd $userpasswd
8681 set enapasswd $enapasswd
8682 }
8683
8684 # Figure out username
8685 if {[info exists username]} {
8686 # command line username
8687 set ruser $username
8688 } else {
8689 set ruser [join [find user $router] ""]
8690 if { "$ruser" == "" } { set ruser $default_user }
8691 }
8692
8693 # Figure out username's password (if different from the vty password)
8694 if {[info exists userpasswd]} {
8695 # command line username
8696 set userpswd $userpasswd
8697 } else {
8698 set userpswd [join [find userpassword $router] ""]
8699 if { "$userpswd" == "" } { set userpswd $passwd }
8700 }
8701
8702 # Figure out prompts
8703 set u_prompt [find userprompt $router]
8704 if { "$u_prompt" == "" } {
8705 set u_prompt "(Username|Login|login|user name|User):"
8706 } else {
8707 set u_prompt [join [lindex $u_prompt 0] ""]
8708 }
8709 set p_prompt [find passprompt $router]
8710 if { "$p_prompt" == "" } {
8711 set p_prompt "(\[Pp]assword|passwd|Enter password for \[^ :]+):"
8712 } else {
8713 set p_prompt [join [lindex $p_prompt 0] ""]
8714 }
8715
8716 # Figure out identity file to use
8717 set identfile [join [lindex [find identity $router] 0] ""]
8718
8719 # Figure out passphrase to use
8720 if {[info exists avpassphrase]} {
8721 set passphrase $avpassphrase
8722 } else {
8723 set passphrase [join [lindex [find passphrase $router] 0] ""]
8724 }
8725 if { ! [string length "$passphrase"]} {
8726 set passphrase $passwd
8727 }
8728
8729 # Figure out cypher type
8730 if {[info exists cypher]} {
8731 # command line cypher type
8732 set cyphertype $cypher
8733 } else {
8734 set cyphertype [find cyphertype $router]
8735 if { "$cyphertype" == "" } { set cyphertype "3des" }
8736 }
8737
8738 # Figure out connection method
8739 set cmethod [find method $router]
8740 if { "$cmethod" == "" } { set cmethod {{telnet} {ssh}} }
8741
8742 # Figure out the SSH executable name
8743 set sshcmd [join [lindex [find sshcmd $router] 0] ""]
8744 if { "$sshcmd" == "" } { set sshcmd {ssh} }
8745
8746 # Login to the router
8747 if {[login $router $ruser $userpswd $passwd $enapasswd $cmethod $cyphertype $identfile]} {
8748 incr exitval
8749 # if login failed or rsh was unsuccessful, move on to the next device
8750 continue
8751 }
8752
8753 # we are logged in, now figure out the full prompt
8754 send "\r"
8755 expect {
8756 -re "\[\r\n]+" { exp_continue; }
8757 -re "^.+$prompt" { set junk $expect_out(0,string);
8758 regsub -all "\[\]\[\(\)]" $junk {\\&} prompt;
8759 }
8760 }
8761
8762 if { $do_command } {
8763 if {[run_commands $prompt $command]} {
8764 incr exitval
8765 continue
8766 }
8767 } elseif { $do_script } {
8768 source $sfile
8769 catch {close};
8770 } else {
8771 label $router
8772 log_user 1
8773 interact
8774 }
8775
8776 # End of for each router
8777 catch {wait};
8778 sleep 0.3
8779}
8780exit $exitval
8781#! /usr/bin/perl
8782##
8783## $Id: xirancid.in 2246 2010-09-08 01:36:07Z heas $
8784##
8785## rancid 3.4.1
8786## Copyright (c) 1997-2016 by Terrapin Communications, Inc.
8787## All rights reserved.
8788##
8789## This code is derived from software contributed to and maintained by
8790## Terrapin Communications, Inc. by Henry Kilmer, John Heasley, Andrew Partan,
8791## Pete Whiting, Austin Schutz, and Andrew Fort.
8792##
8793## Redistribution and use in source and binary forms, with or without
8794## modification, are permitted provided that the following conditions
8795## are met:
8796## 1. Redistributions of source code must retain the above copyright
8797## notice, this list of conditions and the following disclaimer.
8798## 2. Redistributions in binary form must reproduce the above copyright
8799## notice, this list of conditions and the following disclaimer in the
8800## documentation and/or other materials provided with the distribution.
8801## 3. All advertising materials mentioning features or use of this software
8802## must display the following acknowledgement:
8803## This product includes software developed by Terrapin Communications,
8804## Inc. and its contributors for RANCID.
8805## 4. Neither the name of Terrapin Communications, Inc. nor the names of its
8806## contributors may be used to endorse or promote products derived from
8807## this software without specific prior written permission.
8808## 5. It is requested that non-binding fixes and modifications be contributed
8809## back to Terrapin Communications, Inc.
8810##
8811## THIS SOFTWARE IS PROVIDED BY Terrapin Communications, INC. AND CONTRIBUTORS
8812## ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
8813## TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
8814## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COMPANY OR CONTRIBUTORS
8815## BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
8816## CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
8817## SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
8818## INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
8819## CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
8820## ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
8821## POSSIBILITY OF SUCH DAMAGE.
8822#
8823# hacked version of Hank's rancid - this one tries to deal with Xirrus arrays.
8824#
8825# RANXID - Really Awesome New Xirrus confIg Differ
8826#
8827# usage: xirancid [-dltCV] [-f filename | hostname]
8828#
8829use Getopt::Std;
8830getopts('dflt:CV');
8831if ($opt_V) {
8832 print "rancid 3.4.1\n";
8833 exit(0);
8834}
8835$log = $opt_l;
8836$debug = $opt_d;
8837$file = $opt_f;
8838$host = $ARGV[0];
8839$clean_run = 0;
8840$timeo = 90; # xilogin timeout in seconds
8841
8842my(@commandtable, %commands, @commands);# command lists
8843
8844# This routine is used to print out the router configuration
8845sub ProcessHistory {
8846 my($new_hist_tag,$new_command,$command_string,@string) = (@_);
8847 if ((($new_hist_tag ne $hist_tag) || ($new_command ne $command))
8848 && scalar(%history)) {
8849 print eval "$command \%history";
8850 undef %history;
8851 }
8852 if (($new_hist_tag) && ($new_command) && ($command_string)) {
8853 if ($history{$command_string}) {
8854 $history{$command_string} = "$history{$command_string}@string";
8855 } else {
8856 $history{$command_string} = "@string";
8857 }
8858 } elsif (($new_hist_tag) && ($new_command)) {
8859 $history{++$#history} = "@string";
8860 } else {
8861 print "@string";
8862 }
8863 $hist_tag = $new_hist_tag;
8864 $command = $new_command;
8865 1;
8866}
8867
8868# This routine parses "show config"
8869sub ShowConfig {
8870 print STDERR " In ShowConfig: $_\n" if ($debug);
8871
8872 while (<INPUT>) {
8873 tr/\015//d;
8874 last if (/^$prompt/);
8875 next if (/^(\s*|\s*$cmd\s*)$/);
8876 ProcessHistory("","","","$_");
8877 }
8878 return(0);
8879}
8880
8881# Main
8882@commandtable = (
8883 {'show boot-env' => "ShowConfig"},
8884 {'show running-config inc-defaults' => "ShowConfig"}
8885);
8886# Use an array to preserve the order of the commands and a hash for mapping
8887# commands to the subroutine and track commands that have been completed.
8888@commands = map(keys(%$_), @commandtable);
8889%commands = map(%$_, @commandtable);
8890$commandcnt = scalar(keys %commands);
8891
8892$xirrus_cmds=join(";",@commands);
8893$cmds_regexp = join("|", map quotemeta($_), @commands);
8894
8895if (length($host) == 0) {
8896 if ($file) {
8897 print(STDERR "Too few arguments: file name required\n");
8898 exit(1);
8899 } else {
8900 print(STDERR "Too few arguments: host name required\n");
8901 exit(1);
8902 }
8903}
8904if ($opt_C) {
8905 print "xilogin -t $timeo -c\'$xirrus_cmds\' $host\n";
8906 exit(0);
8907}
8908open(OUTPUT,">$host.new") || die "Can't open $host.new for writing: $!\n";
8909select(OUTPUT);
8910# make OUTPUT unbuffered if debugging
8911if ($debug) { $| = 1; }
8912
8913if ($file) {
8914 print STDERR "opening file $host\n" if ($debug);
8915 print STDOUT "opening file $host\n" if ($log);
8916 open(INPUT,"<$host") || die "open failed for $host: $!\n";
8917} else {
8918 print STDERR "executing xilogin -t $timeo -c\"$xirrus_cmds\" $host\n" if ($debug);
8919 print STDOUT "executing xilogin -t $timeo -c\"$xirrus_cmds\" $host\n" if ($log);
8920 if (defined($ENV{NOPIPE})) {
8921 system "xilogin -t $timeo -c \"$xirrus_cmds\" $host </dev/null > $host.raw 2>&1" || die "xilogin failed for $host: $!\n";
8922 open(INPUT, "< $host.raw") || die "xilogin failed for $host: $!\n";
8923 } else {
8924 open(INPUT,"xilogin -t $timeo -c \"$xirrus_cmds\" $host </dev/null |") || die "xilogin failed for $host: $!\n";
8925 }
8926}
8927
8928ProcessHistory("","","","!RANCID-CONTENT-TYPE: xirrus\n!\n");
8929TOP: while(<INPUT>) {
8930 tr/\015//d;
8931 if (/^end/) {
8932 $clean_run=1;
8933 last;
8934 }
8935 if (/^Error:/) {
8936 print STDOUT ("$host xilogin error: $_");
8937 print STDERR ("$host xilogin error: $_") if ($debug);
8938 $clean_run=0;
8939 last;
8940 }
8941 while (/[>#]\s*($cmds_regexp)\s*$/) {
8942 $cmd = $1;
8943 if (!defined($prompt)) {
8944 $prompt = ($_ =~ /^([^:]+:)/)[0];
8945 }
8946 print STDERR ("HIT COMMAND:$_") if ($debug);
8947 if (! defined($commands{$cmd})) {
8948 print STDERR "$host: found unexpected command - \"$cmd\"\n";
8949 $clean_run = 0;
8950 last TOP;
8951 }
8952 $rval = &{$commands{$cmd}}(*INPUT, *OUTPUT, $cmd);
8953 delete($commands{$cmd});
8954 if ($rval == -1) {
8955 $clean_run = 0;
8956 last TOP;
8957 }
8958 }
8959}
8960print STDOUT "Done $logincmd: $_\n" if ($log);
8961# Flush History
8962ProcessHistory("","","","");
8963# Cleanup
8964close(INPUT);
8965close(OUTPUT);
8966
8967if (defined($ENV{NOPIPE})) {
8968 unlink("$host.raw") if (! $debug);
8969}
8970
8971# check for completeness
8972if (scalar(%commands) || !$clean_run ) {
8973 if (scalar(keys %commands) eq $commandcnt) {
8974 printf(STDERR "$host: missed cmd(s): all commands\n");
8975 } elsif (scalar(%commands)) {
8976 printf(STDOUT "$host: missed cmd(s): %s\n", join(',', keys(%commands)));
8977 printf(STDERR "$host: missed cmd(s): %s\n", join(',', keys(%commands))) if ($debug);
8978 }
8979 if (!$clean_run ) {
8980 print STDOUT "$host: End of run not found\n";
8981 print STDERR "$host: End of run not found\n" if ($debug);
8982 system("/usr/bin/tail -1 $host.new");
8983 }
8984 unlink "$host.new" if (! $debug);
8985}
8986#! /usr/bin/perl
8987##
8988## $Id: xrancid.in 3018 2015-01-11 05:51:49Z heas $
8989##
8990## rancid 3.4.1
8991## Copyright (c) 1997-2016 by Terrapin Communications, Inc.
8992## All rights reserved.
8993##
8994## This code is derived from software contributed to and maintained by
8995## Terrapin Communications, Inc. by Henry Kilmer, John Heasley, Andrew Partan,
8996## Pete Whiting, Austin Schutz, and Andrew Fort.
8997##
8998## Redistribution and use in source and binary forms, with or without
8999## modification, are permitted provided that the following conditions
9000## are met:
9001## 1. Redistributions of source code must retain the above copyright
9002## notice, this list of conditions and the following disclaimer.
9003## 2. Redistributions in binary form must reproduce the above copyright
9004## notice, this list of conditions and the following disclaimer in the
9005## documentation and/or other materials provided with the distribution.
9006## 3. All advertising materials mentioning features or use of this software
9007## must display the following acknowledgement:
9008## This product includes software developed by Terrapin Communications,
9009## Inc. and its contributors for RANCID.
9010## 4. Neither the name of Terrapin Communications, Inc. nor the names of its
9011## contributors may be used to endorse or promote products derived from
9012## this software without specific prior written permission.
9013## 5. It is requested that non-binding fixes and modifications be contributed
9014## back to Terrapin Communications, Inc.
9015##
9016## THIS SOFTWARE IS PROVIDED BY Terrapin Communications, INC. AND CONTRIBUTORS
9017## ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
9018## TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
9019## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COMPANY OR CONTRIBUTORS
9020## BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
9021## CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
9022## SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
9023## INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
9024## CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
9025## ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
9026## POSSIBILITY OF SUCH DAMAGE.
9027#
9028# RANCID - Really Awesome New Cisco confIg Differ
9029#
9030# usage: xrancid [-dltCV] [-f filename | hostname]
9031#
9032use Getopt::Std;
9033getopts('dflt:CV');
9034if ($opt_V) {
9035 print "rancid 3.4.1\n";
9036 exit(0);
9037}
9038$log = $opt_l;
9039$debug = $opt_d;
9040$file = $opt_f;
9041$host = $ARGV[0];
9042$clean_run = 0;
9043$found_end = 0;
9044$timeo = 90; # clogin timeout in seconds
9045
9046my(@commandtable, %commands, @commands);# command lists
9047my($aclsort) = ("ipsort"); # ACL sorting mode
9048my($filter_commstr); # SNMP community string filtering
9049my($filter_pwds); # password filtering mode
9050
9051# This routine is used to print out the router configuration
9052sub ProcessHistory {
9053 my($new_hist_tag,$new_command,$command_string,@string) = (@_);
9054 if ((($new_hist_tag ne $hist_tag) || ($new_command ne $command))
9055 && scalar(%history)) {
9056 print eval "$command \%history";
9057 undef %history;
9058 }
9059 if (($new_hist_tag) && ($new_command) && ($command_string)) {
9060 if ($history{$command_string}) {
9061 $history{$command_string} = "$history{$command_string}@string";
9062 } else {
9063 $history{$command_string} = "@string";
9064 }
9065 } elsif (($new_hist_tag) && ($new_command)) {
9066 $history{++$#history} = "@string";
9067 } else {
9068 print "@string";
9069 }
9070 $hist_tag = $new_hist_tag;
9071 $command = $new_command;
9072 1;
9073}
9074
9075sub numerically { $a <=> $b; }
9076
9077# This is a sort routine that will sort numerically on the
9078# keys of a hash as if it were a normal array.
9079sub keynsort {
9080 local(%lines) = @_;
9081 local($i) = 0;
9082 local(@sorted_lines);
9083 foreach $key (sort numerically keys(%lines)) {
9084 $sorted_lines[$i] = $lines{$key};
9085 $i++;
9086 }
9087 @sorted_lines;
9088}
9089
9090# This is a sort routine that will sort on the
9091# keys of a hash as if it were a normal array.
9092sub keysort {
9093 local(%lines) = @_;
9094 local($i) = 0;
9095 local(@sorted_lines);
9096 foreach $key (sort keys(%lines)) {
9097 $sorted_lines[$i] = $lines{$key};
9098 $i++;
9099 }
9100 @sorted_lines;
9101}
9102
9103# This is a sort routine that will sort on the
9104# values of a hash as if it were a normal array.
9105sub valsort{
9106 local(%lines) = @_;
9107 local($i) = 0;
9108 local(@sorted_lines);
9109 foreach $key (sort values %lines) {
9110 $sorted_lines[$i] = $key;
9111 $i++;
9112 }
9113 @sorted_lines;
9114}
9115
9116# This is a numerical sort routine (ascending).
9117sub numsort {
9118 local(%lines) = @_;
9119 local($i) = 0;
9120 local(@sorted_lines);
9121 foreach $num (sort {$a <=> $b} keys %lines) {
9122 $sorted_lines[$i] = $lines{$num};
9123 $i++;
9124 }
9125 @sorted_lines;
9126}
9127
9128# This is a sort routine that will sort on the
9129# ip address when the ip address is anywhere in
9130# the strings.
9131sub ipsort {
9132 local(%lines) = @_;
9133 local($i) = 0;
9134 local(@sorted_lines);
9135 foreach $addr (sort sortbyipaddr keys %lines) {
9136 $sorted_lines[$i] = $lines{$addr};
9137 $i++;
9138 }
9139 @sorted_lines;
9140}
9141
9142# These two routines will sort based upon IP addresses
9143sub ipaddrval {
9144 my(@a) = ($_[0] =~ m#^(\d+)\.(\d+)\.(\d+)\.(\d+)$#);
9145 $a[3] + 256 * ($a[2] + 256 * ($a[1] +256 * $a[0]));
9146}
9147sub sortbyipaddr {
9148 &ipaddrval($a) <=> &ipaddrval($b);
9149}
9150
9151# This routine parses "show version"
9152sub ShowVersion {
9153 print STDERR " In ShowVersion: $_" if ($debug);
9154
9155 while (<INPUT>) {
9156 tr/\015//d;
9157 last if(/^$prompt/);
9158 next if(/^(\s*|\s*$cmd\s*)$/);
9159
9160 /^\S+ Serial Number:/i &&
9161 ProcessHistory("COMMENTS","keysort","B0","#$_") && next;
9162 /^(\S+) Power Supply ([^:]+):\s+(.*)/i &&
9163 ProcessHistory("COMMENTS","keysort","C0","#Power: $1 $2 $3\n")
9164 && next;
9165 /^image\s*:\s*(.*)\s+by\s+/i &&
9166 ProcessHistory("COMMENTS","keysort","D0","#Image: $1\n")
9167 && next;
9168 /^bootrom\s+:\s+(.*)/i &&
9169 ProcessHistory("COMMENTS","keysort","D1","#\n#Bootrom: $1\n")
9170 && next;
9171
9172#heas: need to collect this from show vers for ShowSlot where rev #s are excluded
9173#SLOT 1 : 702005-06 0025S-00877 CPLD Rev <FF>
9174#SLOT 2 : 702005-06 0021S-00131 CPLD Rev 02
9175#SLOT 3 : 702009-06 0024S-00170 CPLD Rev <FF>
9176#SLOT 4 : 702009-06 0024S-00319 CPLD Rev <FF>
9177 }
9178 return(0);
9179}
9180
9181# This routine parses "show memory"
9182sub ShowMemory {
9183 print STDERR " In ShowMemory: $_" if ($debug);
9184
9185 while (<INPUT>) {
9186 tr/\015//d;
9187 last if(/^$prompt/);
9188 next if(/^(\s*|\s*$cmd\s*)$/);
9189
9190 /^Total DRAM (Size|\(KB\)): (.*)/ &&
9191 ProcessHistory("COMMENTS","keysort","B1","#\n#Memory: $2\n")
9192 }
9193 return(0);
9194}
9195
9196# This routine parses "show diag"
9197sub ShowDiag {
9198 print STDERR " In ShowDiag: $_" if ($debug);
9199
9200 while (<INPUT>) {
9201 tr/\015//d;
9202 last if (/^$prompt/);
9203 last if (/^Syntax error at token/);
9204 next if (/^(\s*|\s*$cmd\s*)$/);
9205
9206 /platform\s+:\s+(.*)$/i &&
9207 ProcessHistory("COMMENTS","keysort","A0","#Chassis type: $1\n") &&
9208 next;
9209 /(\S+) part no\s+:\s+(.*)$/i &&
9210 ProcessHistory("COMMENTS","keysort","E0","#$1 PN: $2\n") &&
9211 next;
9212 /(\S+ \S+) no\s+:\s+(.*)$/i &&
9213 ProcessHistory("COMMENTS","keysort","E0","#$1 PN: $2\n") &&
9214 next;
9215 /(mac address)\s+:\s+(.*)$/i &&
9216 ProcessHistory("COMMENTS","keysort","B0","#$1: $2\n") &&
9217 next;
9218 }
9219 return(0);
9220}
9221
9222# This routine parses "show slot"
9223sub ShowSlot {
9224 print STDERR " In ShowSlot: $_" if ($debug);
9225
9226 while (<INPUT>) {
9227 tr/\015//d;
9228 last if (/^$prompt/);
9229 next if (/^(\s*|\s*$cmd\s*)$/);
9230
9231 if (/^Slot\s+(\d+)\s+/i) {
9232 my($slot) = $1;
9233 my($hwtype, $conftype, $sn, $state);
9234 ProcessHistory("COMMENTS","keysort","F$slot","#\n");
9235 while (<INPUT>) {
9236 tr/\015//d;
9237 last if (/^$prompt/ || /^\s*$/);
9238 if (/State:\s+(.*)$/i) { $state = $1; }
9239 if (/serial number:\s+(.*)$/i) { $sn = $1; }
9240 if (/hw module type:\s+(.*)$/i) { $hwtype = $1; }
9241 if (/configured type:\s+(.*)$/i) { $conftype = $1; }
9242 }
9243 ProcessHistory("COMMENTS","keysort","F$slot","#Slot $slot: type "
9244 . "$hwtype,"
9245 . " $conftype\n#Slot $slot: serial $sn\n#Slot $slot: state "
9246 . " $state\n");
9247 return if (/^$prompt/);
9248 next;
9249 }
9250 }
9251 return(0);
9252}
9253
9254# This routine parses "show switch"
9255sub ShowSwitch {
9256 print STDERR " In ShowSwitch: $_" if ($debug);
9257
9258 while (<INPUT>) {
9259 tr/\015//d;
9260 last if(/^$prompt/);
9261 next if(/^(\s*|\s*$cmd\s*)$/);
9262
9263 /^\s*$/i && next;
9264 /^(primary|secondary) configuration:/i && next;
9265 /^(boot |next reboot)/i && next;
9266 /^(auto |qos mode|sys\S*:|temperature|time)/i && next;
9267
9268 /^power supply: (.*)/i &&
9269 ProcessHistory("COMMENTS","keysort","C0","#$1") && next;
9270 /^license/i && ProcessHistory("COMMENTS","keysort","D0","#Image: $_")
9271 && next;
9272 s/^software image (\S+):/Image: $1:/i &&
9273 ProcessHistory("COMMENTS","keysort","D0","#$_") && next;
9274 /^\S+ software version:/i &&
9275 ProcessHistory("COMMENTS","keysort","D0","#Image: $_") && next;
9276 /^(\S+ )?software/i &&
9277 ProcessHistory("COMMENTS","keysort","D0","# $_") && next;
9278 /System MAC:\s+(.*)$/ &&
9279 ProcessHistory("COMMENTS","keysort","B0","#MAC: $1\n") &&
9280 next;
9281 /System Type:\s+(.*)$/ &&
9282 ProcessHistory("COMMENTS","keysort","A0","#Chassis type: $1\n")
9283 && next;
9284 /^(Image Selected):\s+(\S+)/ &&
9285 ProcessHistory("COMMENTS","keysort","A0","#$1: $2\n") && next;
9286 /^(Image Booted):\s+(\S+)/ &&
9287 ProcessHistory("COMMENTS","keysort","A0","#$1: $2\n") && next;
9288 /^(Primary (\S+ )?ver):\s+(\S+)/i &&
9289 ProcessHistory("COMMENTS","keysort","A0","#$1: $3\n") && next;
9290 /^(Secondary (\S+ )?ver):\s+(\S+)/i &&
9291 ProcessHistory("COMMENTS","keysort","A0","#$1: $3\n") && next;
9292 /^(Config Selected):\s+(\S+)/ &&
9293 ProcessHistory("COMMENTS","keysort","A0","#$1: $2\n") && next;
9294 /^(Config Booted):\s+(\S+)/ &&
9295 ProcessHistory("COMMENTS","keysort","A0","#$1: $2\n") && next;
9296 }
9297 return(0);
9298}
9299
9300# This routine processes a "show configuration {detail}"
9301sub WriteTerm {
9302 my($lines) = 0;
9303 print STDERR " In WriteTerm: $_" if ($debug);
9304 my($comment) = 1; # strip extra comments, esp to preserve chassis type
9305
9306 while (<INPUT>) {
9307 tr/\015//d;
9308 last if(/^$prompt/);
9309 next if(/^\s*$/);
9310 return(0) if (/^syntax error at token /i);
9311 return(0) if (/^%% Invalid input detected at /i);
9312 return(0) if (/^%% Ambiguous command:/i);
9313 # the pager can not be disabled per-session on the PIX
9314 s/^<-+ More -+>\s*//;
9315 return(0) if ($found_end);
9316
9317 s/^\s*$/#/;
9318 next if (/full detail configuration/i);
9319
9320 # filter extra comments and lead comments in config so we can preserve
9321 # the chassis type at the top of muched o/p before the process history
9322 # key changes.
9323 if (/^#\s*$/) {
9324 if ($comment) {
9325 next;
9326 } else {
9327 $comment++;
9328 }
9329 } else {
9330 $comment = 0;
9331 }
9332 $lines++;
9333
9334 # Dog gone Cool matches to process the rest of the config
9335 # some chassis report their chassis type in show diag...oh, but
9336 # others do not. grab it here, if available. so, nothing else
9337 # can change the keysort key until this is grabbed. sigh.
9338 /# (\S+) configuration generated/i &&
9339 ProcessHistory("COMMENTS","keysort","A0","#Chassis type: $1\n") &&
9340 ($comment = 0) && next;
9341 /configuration generated/i && next;
9342 /# system name/i && next;
9343 /# software version/i && next;
9344
9345 if (/((create|configure) account \S+ \S+) / && $filter_pwds >= 2) {
9346 ProcessHistory("COMMENTS","keysort","H0","# $1 <key removed>\n");
9347 next;
9348 }
9349 if (/configure ssh2 key/ && $filter_pwds >= 1) {
9350 ProcessHistory("COMMENTS","keysort","H0","# $_# <key removed>\n");
9351 while (<INPUT>) {
9352 if (/^(#|enable|conf|disable|unconf)/) {
9353 tr/\015//d;
9354 last;
9355 }
9356 }
9357 }
9358
9359 # filter out any RCS/CVS tags to avoid confusing local CVS storage
9360 s/\$(Revision|Id):/ $1:/;
9361 if (/^((config|configure) bgp (neighbor|peer-group) \S+ password encrypted)/i && $filter_pwds >= 1) {
9362 ProcessHistory("COMMENTS","keysort","H0","# $1 <removed>\n");
9363 next;
9364 }
9365
9366 # order logging statements
9367 /^configure syslog add logging (\d+\.\d+\.\d+\.\d+)/ &&
9368 ProcessHistory("LOGGING","ipsort","$1","$_") && next;
9369 # order/prune snmp-server host statements
9370 # we only prune lines of the form
9371
9372 # configure snmp add trapreceiver a.b.c.d <community>
9373 if (/^(configure snmp add trapreceiver )(\d+\.\d+\.\d+\.\d+) (community) \S+/) {
9374 if ($filter_commstr) {
9375 ProcessHistory("SNMPSVRHOST","ipsort","$2","# $1$2 $3 <removed> $'\n");
9376 } else {
9377 ProcessHistory("SNMPSVRHOST","ipsort","$2","$_\n");
9378 }
9379 next;
9380 }
9381 if (/^(configure snmp community (readonly|readwrite)( encrypted)?) (\S+)/) {
9382 if ($filter_commstr) {
9383 ProcessHistory("SNMPSVRCOMM","keysort","$_","#$1 <removed>$'");
9384 next;
9385 } else {
9386 ProcessHistory("SNMPSVRCOMM","keysort","$_","$_") && next;
9387 }
9388 }
9389 # order/prune tacacs/radius server statements
9390 if (/^(configure radius (primary|secondary) (tacacs-server|radius-server) shared-secret encrypted)/ && $filter_pwds >= 1) {
9391 ProcessHistory("COMMENTS","keysort","H0","# $1 <removed>\n");
9392 next;
9393 }
9394
9395 # catch anything that wasnt match above.
9396 ProcessHistory("COMMENTS","keysort","H0","$_");
9397 # end of config
9398 if (/^# End of configuration file/i) {
9399 printf STDERR " End WriteTerm: $_" if ($debug);
9400 $found_end = 1;
9401 return(0);
9402 }
9403 }
9404
9405 printf STDERR " End WriteTerm: $_" if ($debug);
9406
9407 if ($lines < 3) {
9408 printf(STDERR "ERROR: $host configuration appears to be truncated.\n");
9409 $found_end = 0;
9410 return(-1);
9411 }
9412 $found_end = 1;
9413
9414 return(0);
9415}
9416
9417# Main
9418@commandtable = (
9419 {'show version' => 'ShowVersion'},
9420 {'show memory' => 'ShowMemory'},
9421 {'show diag' => 'ShowDiag'},
9422 {'show switch' => 'ShowSwitch'},
9423 {'show slot' => 'ShowSlot'},
9424 {'show configuration detail' => 'WriteTerm'},
9425 {'show configuration' => 'WriteTerm'},
9426);
9427# Use an array to preserve the order of the commands and a hash for mapping
9428# commands to the subroutine and track commands that have been completed.
9429@commands = map(keys(%$_), @commandtable);
9430%commands = map(%$_, @commandtable);
9431$commandcnt = scalar(keys %commands);
9432
9433$commandstr = join(";",@commands);
9434$cmds_regexp = join("|", map quotemeta($_), @commands);
9435
9436if (length($host) == 0) {
9437 if ($file) {
9438 print(STDERR "Too few arguments: file name required\n");
9439 exit(1);
9440 } else {
9441 print(STDERR "Too few arguments: host name required\n");
9442 exit(1);
9443 }
9444}
9445if ($opt_C) {
9446 print "clogin -t $timeo -c\'$commandstr\' $host\n";
9447 exit(0);
9448}
9449open(OUTPUT,">$host.new") || die "Can't open $host.new for writing: $!\n";
9450select(OUTPUT);
9451# make OUTPUT unbuffered if debugging
9452if ($debug) { $| = 1; }
9453
9454if ($file) {
9455 print STDERR "opening file $host\n" if ($debug);
9456 print STDOUT "opening file $host\n" if ($log);
9457 open(INPUT,"<$host") || die "open failed for $host: $!\n";
9458} else {
9459 print STDERR "executing clogin -t $timeo -c \"$commandstr\" $host\n" if ($debug);
9460 print STDOUT "executing clogin -t $timeo -c \"$commandstr\" $host\n" if ($log);
9461 if (defined($ENV{NOPIPE}) && $ENV{NOPIPE} =~ /^YES/i) {
9462 system "clogin -t $timeo -c \"$commandstr\" $host </dev/null > $host.raw 2>&1" || die "clogin failed for $host: $!\n";
9463 open(INPUT, "< $host.raw") || die "clogin failed for $host: $!\n";
9464 } else {
9465 open(INPUT,"clogin -t $timeo -c \"$commandstr\" $host </dev/null |") || die "clogin failed for $host: $!\n";
9466 }
9467}
9468
9469# determine ACL sorting mode
9470if ($ENV{"ACLSORT"} =~ /no/i) {
9471 $aclsort = "";
9472}
9473# determine community string filtering mode
9474if (defined($ENV{"NOCOMMSTR"}) &&
9475 ($ENV{"NOCOMMSTR"} =~ /yes/i || $ENV{"NOCOMMSTR"} =~ /^$/)) {
9476 $filter_commstr = 1;
9477} else {
9478 $filter_commstr = 0;
9479}
9480# determine password filtering mode
9481if ($ENV{"FILTER_PWDS"} =~ /no/i) {
9482 $filter_pwds = 0;
9483} elsif ($ENV{"FILTER_PWDS"} =~ /all/i) {
9484 $filter_pwds = 2;
9485} else {
9486 $filter_pwds = 1;
9487}
9488
9489ProcessHistory("","","","#RANCID-CONTENT-TYPE: extreme\n#\n");
9490ProcessHistory("COMMENTS","keysort","B0","#\n"); # chassis info
9491ProcessHistory("COMMENTS","keysort","C0","#\n"); # power supply info
9492ProcessHistory("COMMENTS","keysort","D0","#\n"); # image name
9493ProcessHistory("COMMENTS","keysort","E0","#\n"); # h/w info
9494ProcessHistory("COMMENTS","keysort","F0","#\n"); # slot info
9495ProcessHistory("COMMENTS","keysort","H0","#\n"); # config
9496ProcessHistory("COMMENTS","keysort","X0","#\n");
9497TOP: while(<INPUT>) {
9498 tr/\015//d;
9499 # note: this match sucks rocks, but currently the extreme bits are
9500 # unreliable about echoing the 'exit\n' command. this match might really
9501 # be a bad idea, but instead rely upon WriteTerm's found_end?
9502 if (/($prompt\s?(quit|exit)|Connection( to \S+)? closed)/ && $found_end) {
9503 $clean_run = 1;
9504 last;
9505 }
9506 if (/^Error:/) {
9507 print STDOUT ("$host clogin error: $_");
9508 print STDERR ("$host clogin error: $_") if ($debug);
9509 $clean_run = 0;
9510 last;
9511 }
9512 while (/$prompt\s*($cmds_regexp)\s*$/) {
9513 $cmd = $1;
9514 if (!defined($prompt)) {
9515 $prompt = ($_ =~ /^([^#]+#)/)[0];
9516 $prompt =~ s/([][}{)(\\])/\\$1/g;
9517 $prompt =~ s/[:.](\d+ ?)#/[:.]\\d+ ?#/;
9518 $prompt =~ s/\*/\\\*/;
9519 print STDERR ("PROMPT MATCH: $prompt\n") if ($debug);
9520 }
9521 print STDERR ("HIT COMMAND:$_") if ($debug);
9522 if (! defined($commands{$cmd})) {
9523 print STDERR "$host: found unexpected command - \"$cmd\"\n";
9524 $clean_run = 0;
9525 last TOP;
9526 }
9527 $rval = &{$commands{$cmd}}(*INPUT, *OUTPUT, $cmd);
9528 delete($commands{$cmd});
9529 if ($rval == -1) {
9530 printf STDERR "rval = -1\n" if ($debug);
9531 $clean_run = 0;
9532 last TOP;
9533 }
9534 }
9535}
9536print STDOUT "Done $logincmd: $_\n" if ($log);
9537# Flush History
9538ProcessHistory("","","","");
9539# Cleanup
9540close(INPUT);
9541close(OUTPUT);
9542
9543if (defined($ENV{NOPIPE}) && $ENV{NOPIPE} =~ /^YES/i) {
9544 unlink("$host.raw") if (! $debug);
9545}
9546
9547# check for completeness
9548if (scalar(%commands) || !$clean_run || !$found_end) {
9549 if (scalar(keys %commands) eq $commandcnt) {
9550 printf(STDERR "$host: missed cmd(s): all commands\n");
9551 } elsif (scalar(%commands)) {
9552 printf(STDOUT "$host: missed cmd(s): %s\n", join(',', keys(%commands)));
9553 printf(STDERR "$host: missed cmd(s): %s\n", join(',', keys(%commands))) if ($debug);
9554 }
9555 if (!$clean_run || !$found_end) {
9556 print STDOUT "$host: End of run not found\n";
9557 print STDERR "$host: End of run not found\n" if ($debug);
9558 system("/usr/bin/tail -1 $host.new");
9559 }
9560 unlink "$host.new" if (! $debug);
9561}
9562#! /usr/bin/perl
9563##
9564## $Id: xrrancid.in 2369 2012-01-30 21:06:03Z heas $
9565##
9566## rancid 2.3.8
9567## Copyright (c) 1997-2010 by Terrapin Communications, Inc.
9568## All rights reserved.
9569##
9570## This code is derived from software contributed to and maintained by
9571## Terrapin Communications, Inc. by Henry Kilmer, John Heasley, Andrew Partan,
9572## Pete Whiting, Austin Schutz, and Andrew Fort.
9573##
9574## Redistribution and use in source and binary forms, with or without
9575## modification, are permitted provided that the following conditions
9576## are met:
9577## 1. Redistributions of source code must retain the above copyright
9578## notice, this list of conditions and the following disclaimer.
9579## 2. Redistributions in binary form must reproduce the above copyright
9580## notice, this list of conditions and the following disclaimer in the
9581## documentation and/or other materials provided with the distribution.
9582## 3. All advertising materials mentioning features or use of this software
9583## must display the following acknowledgement:
9584## This product includes software developed by Terrapin Communications,
9585## Inc. and its contributors for RANCID.
9586## 4. Neither the name of Terrapin Communications, Inc. nor the names of its
9587## contributors may be used to endorse or promote products derived from
9588## this software without specific prior written permission.
9589## 5. It is requested that non-binding fixes and modifications be contributed
9590## back to Terrapin Communications, Inc.
9591##
9592## THIS SOFTWARE IS PROVIDED BY Terrapin Communications, INC. AND CONTRIBUTORS
9593## ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
9594## TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
9595## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COMPANY OR CONTRIBUTORS
9596## BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
9597## CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
9598## SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
9599## INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
9600## CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
9601## ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
9602## POSSIBILITY OF SUCH DAMAGE.
9603#
9604# RANCID - Really Awesome New Cisco confIg Differ
9605#
9606# usage: rancid [-dV] [-l] [-f filename | hostname]
9607#
9608use Getopt::Std;
9609getopts('dflV');
9610if ($opt_V) {
9611 print "rancid 2.3.8\n";
9612 exit(0);
9613}
9614$log = $opt_l;
9615$debug = $opt_d;
9616$file = $opt_f;
9617$host = $ARGV[0];
9618$proc = "";
9619$clean_run = 0;
9620$found_end = 0;
9621$found_version = 0;
9622$found_env = 0;
9623$found_diag = 0;
9624$timeo = 90; # clogin timeout in seconds
9625
9626my(@commandtable, %commands, @commands);# command lists
9627my($aclsort) = ("ipsort"); # ACL sorting mode
9628my($config_register); # configuration register value
9629my($filter_commstr); # SNMP community string filtering
9630my($filter_pwds); # password filtering mode
9631
9632# This routine is used to print out the router configuration
9633sub ProcessHistory {
9634 my($new_hist_tag,$new_command,$command_string,@string) = (@_);
9635 if ((($new_hist_tag ne $hist_tag) || ($new_command ne $command))
9636 && scalar(%history)) {
9637 print eval "$command \%history";
9638 undef %history;
9639 }
9640 if (($new_hist_tag) && ($new_command) && ($command_string)) {
9641 if ($history{$command_string}) {
9642 $history{$command_string} = "$history{$command_string}@string";
9643 } else {
9644 $history{$command_string} = "@string";
9645 }
9646 } elsif (($new_hist_tag) && ($new_command)) {
9647 $history{++$#history} = "@string";
9648 } else {
9649 print "@string";
9650 }
9651 $hist_tag = $new_hist_tag;
9652 $command = $new_command;
9653 1;
9654}
9655
9656sub numerically { $a <=> $b; }
9657
9658# This is a sort routine that will sort numerically on the
9659# keys of a hash as if it were a normal array.
9660sub keynsort {
9661 local(%lines) = @_;
9662 local($i) = 0;
9663 local(@sorted_lines);
9664 foreach $key (sort numerically keys(%lines)) {
9665 $sorted_lines[$i] = $lines{$key};
9666 $i++;
9667 }
9668 @sorted_lines;
9669}
9670
9671# This is a sort routine that will sort on the
9672# keys of a hash as if it were a normal array.
9673sub keysort {
9674 local(%lines) = @_;
9675 local($i) = 0;
9676 local(@sorted_lines);
9677 foreach $key (sort keys(%lines)) {
9678 $sorted_lines[$i] = $lines{$key};
9679 $i++;
9680 }
9681 @sorted_lines;
9682}
9683
9684# This is a sort routine that will sort on the
9685# values of a hash as if it were a normal array.
9686sub valsort{
9687 local(%lines) = @_;
9688 local($i) = 0;
9689 local(@sorted_lines);
9690 foreach $key (sort values %lines) {
9691 $sorted_lines[$i] = $key;
9692 $i++;
9693 }
9694 @sorted_lines;
9695}
9696
9697# This is a numerical sort routine (ascending).
9698sub numsort {
9699 local(%lines) = @_;
9700 local($i) = 0;
9701 local(@sorted_lines);
9702 foreach $num (sort {$a <=> $b} keys %lines) {
9703 $sorted_lines[$i] = $lines{$num};
9704 $i++;
9705 }
9706 @sorted_lines;
9707}
9708
9709# This is a sort routine that will sort on the
9710# ip address when the ip address is anywhere in
9711# the strings.
9712sub ipsort {
9713 local(%lines) = @_;
9714 local($i) = 0;
9715 local(@sorted_lines);
9716 foreach $addr (sort sortbyipaddr keys %lines) {
9717 $sorted_lines[$i] = $lines{$addr};
9718 $i++;
9719 }
9720 @sorted_lines;
9721}
9722
9723# These two routines will sort based upon IP addresses
9724sub ipaddrval {
9725 my(@a) = ($_[0] =~ m#^(\d+)\.(\d+)\.(\d+)\.(\d+)$#);
9726 $a[3] + 256 * ($a[2] + 256 * ($a[1] +256 * $a[0]));
9727}
9728sub sortbyipaddr {
9729 &ipaddrval($a) <=> &ipaddrval($b);
9730}
9731
9732# This routine parses "admin show version"
9733sub ShowVersion {
9734 print STDERR " In ShowVersion: $_" if ($debug);
9735 my($slaveslot);
9736
9737 while (<INPUT>) {
9738 tr/\015//d;
9739 if (/^$prompt/) { $found_version = 1; last};
9740 next if (/^(\s*|\s*$cmd\s*)$/);
9741 return(1) if (/Line has invalid autocommand /);
9742 return(1) if (/(Invalid (input|command) detected|Type help or )/i);
9743 return(0) if ($found_version); # Only do this routine once
9744 return(-1) if (/command authorization failed/i);
9745
9746 if (/^Slave in slot (\d+) is running/) {
9747 $slave = " Slave:";
9748 $slaveslot = ", slot $1";
9749 next;
9750 }
9751 /^(Cisco )?IOS .* Software,? \(([A-Za-z0-9_-]*)\), .*Version\s+(.*)$/ &&
9752 ProcessHistory("COMMENTS","keysort","F1",
9753 "!Image:$slave Software: $2, $3\n") && next;
9754 /^([A-Za-z-0-9_]*) Synced to mainline version: (.*)$/ &&
9755 ProcessHistory("COMMENTS","keysort","F2",
9756 "!Image:$slave $1 Synced to mainline version: $2\n") && next;
9757 /^Compiled (.*)$/ &&
9758 ProcessHistory("COMMENTS","keysort","F3",
9759 "!Image:$slave Compiled: $1\n") && next;
9760 /^ROM: (IOS \S+ )?(System )?Bootstrap.*(Version.*)$/ &&
9761 ProcessHistory("COMMENTS","keysort","G1",
9762 "!ROM Bootstrap: $3\n") && next;
9763 if (/^Hardware:\s+(.*), (.* RAM), CPU (.*)$/) {
9764 ProcessHistory("COMMENTS","keysort","A1",
9765 "!Chassis type: $1 - a PIX\n");
9766 ProcessHistory("COMMENTS","keysort","A2",
9767 "!CPU: $3\n");
9768 ProcessHistory("COMMENTS","keysort","B1", "!Memory: $2\n");
9769 }
9770 /^Serial Number:\s+(.*)$/ &&
9771 ProcessHistory("COMMENTS","keysort","C1", "!$_") && next;
9772 # More PIX stuff
9773 /^Encryption hardware device\s+:\s+(.*)/ &&
9774 ProcessHistory("COMMENTS","keysort","A3", "!Encryption: $1\n") &&
9775 next;
9776 /^running activation key\s*:\s+(.*)/i &&
9777 ProcessHistory("COMMENTS","keysort","D2", "!Key: $1\n") &&
9778 next;
9779 # Flash on the PIX or FWSM (FireWall Switch Module)
9780 /^Flash(\s+\S+)+ \@ 0x\S+,\s+(\S+)/ &&
9781 ProcessHistory("COMMENTS","keysort","B2", "!Memory: Flash $2\n") &&
9782 next;
9783 # CatOS 3500xl stuff
9784 /^System serial number(:\s+.*)$/ &&
9785 ProcessHistory("COMMENTS","keysort","C1", "!Serial Number$1\n") &&
9786 next;
9787 /^Model / &&
9788 ProcessHistory("COMMENTS","keysort","C2", "!$_") && next;
9789 /^Motherboard / &&
9790 ProcessHistory("COMMENTS","keysort","C3", "!$_") && next;
9791 /^Power supply / &&
9792 ProcessHistory("COMMENTS","keysort","C4", "!$_") && next;
9793
9794 /^Activation Key:\s+(.*)$/ &&
9795 ProcessHistory("COMMENTS","keysort","C2", "!$_") && next;
9796 /^ROM: \d+ Bootstrap .*(Version.*)$/ &&
9797 ProcessHistory("COMMENTS","keysort","G2",
9798 "!ROM Image: Bootstrap $1\n!\n") && next;
9799 /^ROM: .*(Version.*)$/ &&
9800 ProcessHistory("COMMENTS","keysort","G3","!ROM Image: $1\n") && next;
9801 /^BOOTFLASH: .*(Version.*)$/ &&
9802 ProcessHistory("COMMENTS","keysort","G4","!BOOTFLASH: $1\n") && next;
9803 /^BOOTLDR: .*(Version.*)$/ &&
9804 ProcessHistory("COMMENTS","keysort","G4","!BOOTLDR: $1\n") && next;
9805 /^System image file is "([^\"]*)", booted via (\S*)/ &&
9806# removed the booted source due to
9807# CSCdk28131: cycling info in 'sh ver'
9808# ProcessHistory("COMMENTS","keysort","F4","!Image: booted via $2, $1\n") &&
9809 ProcessHistory("COMMENTS","keysort","F4","!Image: booted $1\n") &&
9810 next;
9811 /^System image file is "([^\"]*)"$/ &&
9812 ProcessHistory("COMMENTS","keysort","F5","!Image: $1\n") && next;
9813 if (/(\S+(?:\sseries)?)\s+(?:\((\S+)\)\s+processor|\(revision[^)]+\)).*\s+with (\S+k) bytes/i) {
9814 $proc = $1;
9815 my($cpu) = $2;
9816 my($mem) = $3;
9817 my($device) = "router";
9818
9819 # the next line ought to be the more specific cpu info, grab it.
9820 # yet, some boards/IOS vers have a processor ID line between these
9821 # two. grrr. make sure we dont grab the "software" junk that
9822 # follows these lines by looking for "CPU at " or the 2600s
9823 # "processor: " unique string. there are undoubtedly many other
9824 # incantations. for a slave, we dont get this info, its just a
9825 # blank line.
9826 $_ = <INPUT>;
9827 if (/processor board id/i) {
9828 my($sn);
9829
9830 if (/processor board id (\S+)/i) {
9831 $sn = $1;
9832 $sn =~ s/,$//;
9833 ProcessHistory("COMMENTS","keysort","D9",
9834 "!Processor ID: $sn\n");
9835 }
9836 $_ = <INPUT>;
9837 }
9838 $_ = "" if (! /(cpu at |processor: |$cpu processor,)/i);
9839 tr/\015//d;
9840 s/implementation/impl/i;
9841 if ($_ !~ /^\s*$/) {
9842 chomp;
9843 s/^/, /;
9844 }
9845
9846 if ($proc eq "CSC") {
9847 $type = "AGS";
9848 } elsif ($proc eq "CSC4") {
9849 $type = "AGS+";
9850 } elsif ($proc =~ /1900/) {
9851 $type = "1900";
9852 $device = "switch";
9853 } elsif ($proc =~ /^(AS)?25[12][12]/) {
9854 $type = "2500";
9855 } elsif ($proc =~ /261[01]/ || $proc =~ /262[01]/ ) {
9856 $type = "2600";
9857 } elsif ($proc =~ /WS-C29/) {
9858 $type = "2900XL";
9859 $device = "switch";
9860 } elsif ($proc =~ /WS-C355/) {
9861 $type = "3550";
9862 $device = "switch";
9863 } elsif ($proc =~ /WS-C35/) {
9864 $type = "3500XL";
9865 $device = "switch";
9866 } elsif ($proc =~ /^36[0246][0-9]/) {
9867 $type = "3600";
9868 } elsif ($proc =~ /^37/) {
9869 $type = "3700";
9870 } elsif ($proc =~ /^38/) {
9871 $type = "3800";
9872 } elsif ($proc =~ /WS-C45/) {
9873 $type = "4500";
9874 $device = "switch";
9875 } elsif ( $proc =~ /^AS5300/) {
9876 $type = "AS5300";
9877 } elsif ( $proc =~ /^AS5350/) {
9878 $type = "AS5350";
9879 } elsif ( $proc =~ /^AS5400/) {
9880 $type = "AS5400";
9881 } elsif ($proc =~ /6000/) {
9882 $type = "6000";
9883 $device = "switch";
9884 } elsif ($proc eq "WK-C65") {
9885 $type = "6500";
9886 } elsif ($proc eq "RP") {
9887 $type = "7000";
9888 } elsif ($proc eq "RP1") {
9889 $type = "7000";
9890 } elsif ($proc =~ /720[246]/) {
9891 $type = "7200";
9892 } elsif ( $proc =~ /^73/) {
9893 $type = "7300";
9894 } elsif ($proc eq "RSP7000") {
9895 $type = "7500";
9896 } elsif ($proc =~ /RSP\d/) {
9897 $type = "7500";
9898 } elsif ($proc =~ /OSR-76/) {
9899 $type = "7600";
9900 } elsif ($proc =~ /CISCO76/) {
9901 $type = "7600";
9902 } elsif ($proc =~ /1200[48]\/(GRP|PRP)/ || $proc =~ /1201[26]\/(GRP|PRP)/) {
9903 $type = "12000";
9904 } elsif ($proc =~ /1201[26]-8R\/(GRP|PRP)/) {
9905 $type = "12000";
9906 } elsif ($proc =~ /1240[48]\/(GRP|PRP)/ || $proc =~ /1241[06]\/(GRP|PRP)/) {
9907 $type = "12400";
9908 } else {
9909 $type = $proc;
9910 }
9911
9912 print STDERR "TYPE = $type\n" if ($debug);
9913 ProcessHistory("COMMENTS","keysort","A1",
9914 "!Chassis type:$slave $proc - a $type $device\n");
9915 ProcessHistory("COMMENTS","keysort","B1",
9916 "!Memory:$slave main $mem\n");
9917 if (defined($cpu)) {
9918 ProcessHistory("COMMENTS","keysort","A3",
9919 "!CPU:$slave $cpu$_$slaveslot\n");
9920 }
9921 next;
9922 }
9923 if (/(\S+) Silicon\s*Switch Processor/) {
9924 if (!defined($C0)) {
9925 $C0 = 1; ProcessHistory("COMMENTS","keysort","C0","!\n");
9926 }
9927 ProcessHistory("COMMENTS","keysort","C2","!SSP: $1\n");
9928 $ssp = 1;
9929 $sspmem = $1;
9930 next;
9931 }
9932 /^(\d+[kK]) bytes of multibus/ &&
9933 ProcessHistory("COMMENTS","keysort","B2",
9934 "!Memory: multibus $1\n") && next;
9935 /^(\d+[kK]) bytes of (non-volatile|NVRAM)/ &&
9936 ProcessHistory("COMMENTS","keysort","B3",
9937 "!Memory: nvram $1\n") && next;
9938 /^(\d+[kK]) bytes of flash memory/ &&
9939 ProcessHistory("COMMENTS","keysort","B5","!Memory: flash $1\n") &&
9940 next;
9941 /^(\d+[kK]) bytes of .*flash partition/ &&
9942 ProcessHistory("COMMENTS","keysort","B6",
9943 "!Memory: flash partition $1\n") && next;
9944 /^(\d+[kK]) bytes of Flash internal/ &&
9945 ProcessHistory("COMMENTS","keysort","B4",
9946 "!Memory: bootflash $1\n") && next;
9947 if (/^(\d+[kK]) bytes of (Flash|ATA)?.*PCMCIA .*(slot|disk) ?(\d)/i) {
9948 ProcessHistory("COMMENTS","keysort","B7",
9949 "!Memory: pcmcia $2 $3$4 $1\n");
9950 next;
9951 }
9952 if (/^(\d+[kK]) bytes of (slot|disk)(\d)/i) {
9953 ProcessHistory("COMMENTS","keysort","B7",
9954 "!Memory: pcmcia $2$3 $1\n");
9955 next;
9956 }
9957 if (/^WARNING/) {
9958 if (!defined($I0)) {
9959 $I0 = 1;
9960 ProcessHistory("COMMENTS","keysort","I0","!\n");
9961 }
9962 ProcessHistory("COMMENTS","keysort","I1","! $_");
9963 }
9964 if (/^Configuration register is (.*)$/) {
9965 $config_register = $1;
9966 next;
9967 }
9968 if (/^Configuration register on node \S+ is (.*)$/) {
9969 $config_register = $1 if (length($config_register) < 1);
9970 next;
9971 }
9972 }
9973 return(0);
9974}
9975
9976# This routine parses "admin show diag".
9977# This will create arrays for hw info.
9978sub AdminShowDiag {
9979 print STDERR " In ShowDiag: $_" if ($debug);
9980
9981 while (<INPUT>) {
9982 tr/\015//d;
9983 if (/^$prompt/) { $found_diag = 1; last};
9984 next if (/^(\s*|\s*$cmd\s*)$/);
9985 return(1) if (/Line has invalid autocommand /);
9986 return(1) if (/(Invalid (input|command) detected|Type help or )/i);
9987 return(-1) if (/command authorization failed/i);
9988 /^$/ && next;
9989
9990 s/^NODE //;
9991 # wtf are these?
9992 next if (/(New Deviation|UDI_VID|Board State)/);
9993 # skip insertion time
9994 next if (/insertion time/i);
9995 # skip board h/w revision junk
9996 next if (/^(\s{2}board |\s{3,})/i);
9997
9998 ProcessHistory("SLOT","","","!$_");
9999 }
10000 ProcessHistory("SLOT","","","!\n");
10001 return(0);
10002}
10003
10004# This routine parses "admin show running".
10005# This will create arrays for hw info.
10006sub AdminShowRunning {
10007 print STDERR " In ShowRunning: $_" if ($debug);
10008
10009 while (<INPUT>) {
10010 tr/\015//d;
10011 if (/^$prompt/) { $found_diag = 1; last};
10012 next if (/^(\s*|\s*$cmd\s*)$/);
10013 return(1) if (/Line has invalid autocommand /);
10014 return(1) if (/(Invalid (input|command) detected|Type help or )/i);
10015 return(-1) if (/command authorization failed/i);
10016 /^$/ && next;
10017
10018 /^building configuration/i && next;
10019 if (/^(\s*secret) / && $filter_pwds >= 2) {
10020 ProcessHistory("SLOT","","","!$1 <removed>\n");
10021 next;
10022 }
10023 /^end$/ && last;
10024
10025 ProcessHistory("SLOT","","","!$_");
10026 }
10027 ProcessHistory("SLOT","","","!\n");
10028 return(0);
10029}
10030
10031# This routine parses "admin show redundancy"
10032sub ShowRedundancy {
10033 print STDERR " In ShowRedundancy: $_" if ($debug);
10034
10035 while (<INPUT>) {
10036 tr/\015//d;
10037 last if (/^$prompt/);
10038 next if (/^(\s*|\s*$cmd\s*)$/);
10039 return(1) if (/Line has invalid autocommand /);
10040 return(1) if (/(Invalid (input|command) detected|Type help or )/i);
10041
10042 if (/^Version information for secondary in slot (\d+):/) {
10043 $slave = " Slave:";
10044 $slaveslot = ", slot $1";
10045 next;
10046 }
10047
10048 /^IOS .* Software \(([A-Za-z0-9_-]*)\), .*Version\s+(.*)$/ &&
10049 ProcessHistory("COMMENTS","keysort","F1",
10050 "!Image:$slave Software: $1, $2\n") && next;
10051 /^Compiled (.*)$/ &&
10052 ProcessHistory("COMMENTS","keysort","F3",
10053 "!Image:$slave Compiled: $1\n") && next;
10054 }
10055 return(0);
10056}
10057
10058# This routine parses "show install active"
10059sub ShowInstallActive {
10060 print STDERR " In ShowInstallActive: $_" if ($debug);
10061
10062 while (<INPUT>) {
10063 tr/\015//d;
10064 last if (/^$prompt/);
10065 next if (/^(\s*|\s*$cmd\s*)$/);
10066 return(1) if (/^\s*\^\s*$/);
10067 return(1) if (/Line has invalid autocommand /);
10068 return(1) if (/(Invalid (input|command) detected|Type help or )/i);
10069 return(-1) if (/command authorization failed/i);
10070
10071 ProcessHistory("COMMENTS","keysort","F5","!Image: $_") && next;
10072 }
10073 return(0);
10074}
10075
10076# This routine parses "admin show env all"
10077sub ShowEnv {
10078 # Skip if this is not a 7500, 7200, or 7000.
10079 print STDERR " In ShowEnv: $_" if ($debug);
10080
10081 while (<INPUT>) {
10082 tr/\015//d;
10083 if (/^$prompt/) { $found_env = 1; last};
10084 next if (/^(\s*|\s*$cmd\s*)$/);
10085 #return(1) if ($type !~ /^7/);
10086 return(1) if (/Line has invalid autocommand /);
10087 return(1) if (/(Invalid (input|command) detected|Type help or )/i);
10088 return(0) if ($found_env); # Only do this routine once
10089 return(-1) if (/command authorization failed/i);
10090
10091 if (!defined($E0)) {
10092 $E0 = 1;
10093 ProcessHistory("COMMENTS","keysort","E0","!\n");
10094 }
10095 if (/^Arbiter type (\d), backplane type (\S+)/) {
10096 if (!defined($C0)) {
10097 $C0 = 1; ProcessHistory("COMMENTS","keysort","C0","!\n");
10098 }
10099 ProcessHistory("COMMENTS","keysort","C1",
10100 "!Enviromental Arbiter Type: $1\n");
10101 ProcessHistory("COMMENTS","keysort","A2",
10102 "!Chassis type: $2 backplane\n");
10103 next;
10104 }
10105 /^Power Supply Information$/ && next;
10106 /^\s*Power Module\s+Voltage\s+Current$/ && next;
10107 /^\s*(Power [^:\n]+)$/ &&
10108 ProcessHistory("COMMENTS","keysort","E1","!Power: $1\n") && next;
10109 /^\s*(Lower Power .*)/i &&
10110 ProcessHistory("COMMENTS","keysort","E2","!Power: $1\n") && next;
10111 /^\s*(redundant .*)/i &&
10112 ProcessHistory("COMMENTS","keysort","E2","!Power: $1\n") && next;
10113 /^\s*(RPS is .*)/i &&
10114 ProcessHistory("COMMENTS","keysort","E2","!Power: $1\n") && next;
10115 }
10116 ProcessHistory("COMMENTS","","","!\n");
10117 return(0);
10118}
10119
10120# This routine parses "dir /all ((disk|slot)N|bootflash|nvram):"
10121sub DirSlotN {
10122 print STDERR " In DirSlotN: $_" if ($debug);
10123
10124 my($dev) = (/\s([^\s]+):/);
10125
10126 while (<INPUT>) {
10127 tr/\015//d;
10128 last if (/^$prompt/);
10129 next if (/^(\s*|\s*$cmd\s*)$/);
10130 return(1) if /^\s*\^\s*$/;
10131 return(1) if (/Line has invalid autocommand /);
10132 return(1) if (/(Invalid (input|command) detected|Type help or )/i);
10133 return(1) if (/(No such device|Error Sending Request)/i);
10134 return(1) if (/\%Error: No such file or directory/);
10135 return(1) if (/No space information available/);
10136 # Corrupt flash
10137 /\%Error calling getdents / &&
10138 ProcessHistory("FLASH","","","!Flash: $dev: $_") && next;
10139 return(-1) if (/\%Error calling/);
10140 return(-1) if (/(: device being squeezed|ATA_Status time out)/i); # busy
10141 return(-1) if (/\%Error opening \S+:\S+ \(Device or resource busy\)/i);
10142 return(-1) if (/command authorization failed/i);
10143 return(1) if (/(Open device \S+ failed|Error opening \S+:)/);
10144 # filter frequently changing files from IOX bootflash
10145 if ($dev =~ /bootflash/) {
10146 next if (/temp_cont\s*$/);
10147 next if (/uptime_cont\s*$/);
10148 }
10149 # Filter dhcp database
10150 if (/dhcp_[^. ]*\.txt/) {
10151 next;
10152 }
10153 # Filter debugging file dlbg.txt & dlbg.txt-1 only on ASR9k w/ XR
10154 if ($proc =~ /ASR9K/ && /dlbg\.txt/) {
10155 next;
10156 }
10157 if (/.*\((\d+) bytes free\)/) {
10158 my($tmp) = $1;
10159 if ($tmp >= (1024 * 1024 * 1024)) {
10160 $tmp = int($tmp / (1024 * 1024 * 1024));
10161 s/$1 bytes free/$tmp GB free/;
10162 } else {
10163 $tmp = int($tmp / (1024 * 1024));
10164 s/$1 bytes free/$tmp MB free/;
10165 }
10166 }
10167
10168 ProcessHistory("FLASH","","","!Flash: $dev: $_");
10169 }
10170 ProcessHistory("","","","!\n");
10171 return(0);
10172}
10173
10174# This routine parses "admin show variables boot"
10175sub ShowBootVar {
10176 print STDERR " In ShowBootVar: $_" if ($debug);
10177
10178 while (<INPUT>) {
10179 # delete non-ascii chars, except new line
10180 tr/ -~\n//cd;
10181 last if (/^$prompt/);
10182 next if (/\s*$cmd\s*$/);
10183 return(1) if (/^\s*\^\s*$/);
10184 return(1) if (/Line has invalid autocommand /);
10185 return(1) if (/(Invalid (input|command) detected|Type help or )/i);
10186 return(1) if (/Ambiguous command/i);
10187 return(1) if (/(Open device \S+ failed|Error opening \S+:)/);
10188 return(-1) if (/command authorization failed/i);
10189
10190 # skip blank lines
10191 next if (/^\s*$/);
10192
10193 ProcessHistory("COMMENTS", "keysort", "C30", "! $_");
10194 }
10195 ProcessHistory("COMMENTS", "keysort", "C39", "!\n");
10196
10197 return(0);
10198}
10199
10200# This routine parses "show controllers"
10201sub ShowContAll {
10202 # Skip if this is a 70[01]0, 7500, or 12000.
10203 print STDERR " In ShowContAll: $_" if ($debug);
10204
10205 while (<INPUT>) {
10206 tr/\015//d;
10207 last if (/^$prompt/);
10208 next if (/^(\s*|\s*$cmd\s*)$/);
10209 return(1) if (/(Invalid (input|command) detected|Type help or )/i);
10210 # return(1) if ($type =~ /^(12[40]|7[05])/);
10211 return(-1) if (/command authorization failed/i);
10212
10213 if (/^Interface ([^ \n(]*)/) { $INT = "$1, "; next; }
10214 /^(BRI unit \d)/ &&
10215 ProcessHistory("INT","","","!Interface: $1\n") && next;
10216 /^LANCE unit \d, NIM/ &&
10217 ProcessHistory("INT","","","!Interface: $_") && next;
10218 /^(LANCE unit \d)/ &&
10219 ProcessHistory("INT","","","!Interface: $1\n") && next;
10220 /(Media Type is \S+),/ &&
10221 ProcessHistory("INT","","","!\t$1\n");
10222 if (/(M\dT[^ :]*:) show controller:$/) {
10223 my($ctlr) = $1;
10224 $_ = <INPUT>; tr/\015//d; s/ subunit \d,//;
10225 ProcessHistory("INT","","","!Interface: $ctlr $_");
10226 }
10227 if (/^(\S+) : show controller:$/) {
10228 my($ctlr) = $1;
10229 $_ = <INPUT>; tr/\015//d; s/ subunit \d,//;
10230 ProcessHistory("INT","","","!Interface: $ctlr: $_");
10231 }
10232 /^(HD unit \d), idb/ &&
10233 ProcessHistory("INT","","","!Interface: $1\n") && next;
10234 /^HD unit \d, NIM/ &&
10235 ProcessHistory("INT","","","!Interface: $_") && next;
10236 /^buffer size \d+ HD unit \d, (.*)/ &&
10237 ProcessHistory("INT","","","!\t$1\n") && next;
10238 /^AM79970 / && ProcessHistory("INT","","","!Interface: $_") && next;
10239 /^buffer size \d+ (Universal Serial: .*)/ &&
10240 ProcessHistory("INT","","","!\t$1\n") && next;
10241 /^Hardware is (.*)/ &&
10242 ProcessHistory("INT","","","!Interface: $INT$1\n") && next;
10243 /^(QUICC Serial unit \d),/ &&
10244 ProcessHistory("INT","","","!$1\n") && next;
10245 /^QUICC Ethernet .*/ &&
10246 ProcessHistory("INT","","","!$_") && next;
10247 /^DTE .*\.$/ &&
10248 ProcessHistory("INT","","","!\t$_") && next;
10249 /^(cable type :.*),/ &&
10250 ProcessHistory("INT","","","!\t$1\n") && next;
10251 /^(.* cable.*), received clockrate \d+$/ &&
10252 ProcessHistory("INT","","","!\t$1\n") && next;
10253 /^.* cable.*$/ &&
10254 ProcessHistory("INT","","","!\t$_") && next;
10255 }
10256 return(0);
10257}
10258
10259# This routine parses "show debug"
10260sub ShowDebug {
10261 print STDERR " In ShowDebug: $_" if ($debug);
10262 my($lines) = 0;
10263
10264 while (<INPUT>) {
10265 tr/\015//d;
10266 last if (/^$prompt/);
10267 next if (/^(\s*|\s*$cmd\s*)$/);
10268 return(1) if (/Line has invalid autocommand /);
10269 return(1) if (/(Invalid (input|command) detected|Type help or )/i);
10270 return(-1) if (/command authorization failed/i);
10271
10272 /^No matching debug flags set$/ && next;
10273 /^No debug flags set$/ && next;
10274 ProcessHistory("COMMENTS","keysort","J1","!DEBUG: $_");
10275 $lines++;
10276 }
10277 if ($lines) {
10278 ProcessHistory("COMMENTS","keysort","J0","!\n");
10279 }
10280 return(0);
10281}
10282
10283# This routine parses "admin show install summary"
10284sub ShowInstallSummary {
10285 print STDERR " In ShowInstallSummary: $_" if ($debug);
10286
10287 while (<INPUT>) {
10288 # delete non-ascii chars, except new line
10289 tr/ -~\n//cd;
10290 last if (/^$prompt/);
10291 next if (/\s*$cmd\s*$/);
10292 return(1) if (/^\s*\^\s*$/);
10293 return(1) if (/Line has invalid autocommand /);
10294 return(1) if (/(Invalid (input|command) detected|Type help or )/i);
10295 return(1) if (/Ambiguous command/i);
10296 return(1) if (/(Open device \S+ failed|Error opening \S+:)/);
10297 return(-1) if (/command authorization failed/i);
10298
10299 # skip blank lines
10300 next if (/^\s*$/);
10301
10302 ProcessHistory("COMMENTS", "keysort", "C15", "! $_");
10303 }
10304 ProcessHistory("COMMENTS", "keysort", "C10", "!\n");
10305 ProcessHistory("COMMENTS", "keysort", "C19", "!\n");
10306
10307 return(0);
10308}
10309
10310# This routine parses "show inventory".
10311sub ShowInventory {
10312 print STDERR " In ShowInventory: $_" if ($debug);
10313
10314 while (<INPUT>) {
10315 # delete non-ascii chars, except new line
10316 tr/ -~\n//cd;
10317 return if (/^\s*\^$/);
10318 last if (/^$prompt/);
10319 next if (/^(\s*|\s*$cmd\s*)$/);
10320 return(1) if (/Line has invalid autocommand /);
10321 return(1) if (/(Invalid (input|command) detected|Type help or )/i);
10322 return(-1) if (/command authorization failed/i);
10323
10324 # remove spaces after quotes
10325 s/\"\s+/\"/g;
10326 if (/^(NAME: "[^"]*",) (DESCR: "[^"]+")/) {
10327 ProcessHistory("INVENTORY","","", sprintf("!%-30s %s\n", $1, $2));
10328 next;
10329 }
10330 # split PID/VID/SN line
10331 if (/^PID: (\S*)\s*, VID: (\S*)\s*, SN: (\S*)\s*$/) {
10332 my($pid,$vid,$sn) = ($1, $2, $3);
10333 my($entries) = "";
10334 # filter <empty>, "0x" and "N/A" lines
10335 if ($pid !~ /^(|0x|N\/A)$/) {
10336 $entries .= "!PID: $pid\n";
10337 }
10338 if ($vid !~ /^(|0x|N\/A)$/) {
10339 $entries .= "!VID: $vid\n";
10340 }
10341 if ($sn !~ /^(|0x|N\/A)$/) {
10342 $entries .= "!SN: $sn\n";
10343 }
10344 ProcessHistory("INVENTORY","","", "$entries");
10345 next;
10346 }
10347 ProcessHistory("INVENTORY","","","!$_");
10348 }
10349 ProcessHistory("INVENTORY","","","!\n");
10350
10351 return(0);
10352}
10353
10354# This routine parses "admin show license"
10355sub ShowLicense {
10356 print STDERR " In ShowLicense: $_" if ($debug);
10357
10358 while (<INPUT>) {
10359 # delete non-ascii chars, except new line
10360 tr/ -~\n//cd;
10361 last if (/^$prompt/);
10362 next if (/\s*$cmd\s*$/);
10363 return(1) if (/^\s*\^\s*$/);
10364 return(1) if (/Line has invalid autocommand /);
10365 return(1) if (/(Invalid (input|command) detected|Type help or )/i);
10366 return(1) if (/Ambiguous command/i);
10367 return(1) if (/(Open device \S+ failed|Error opening \S+:)/);
10368 return(-1) if (/command authorization failed/i);
10369
10370 # skip blank lines
10371 next if (/^\s*$/);
10372
10373 ProcessHistory("COMMENTS", "keysort", "C20", "! $_");
10374 }
10375 ProcessHistory("COMMENTS", "keysort", "C29", "!\n");
10376
10377 return(0);
10378}
10379
10380# This routine parses "show rpl maximum"
10381sub ShowRPL {
10382 print STDERR " In ShowRPL: $_" if ($debug);
10383
10384 while (<INPUT>) {
10385 tr/\015//d;
10386 last if (/^$prompt/);
10387 next if (/^(\s*|\s*$cmd\s*)$/);
10388 return(1) if /^\s*\^\s*$/;
10389 return(1) if (/Line has invalid autocommand /);
10390 return(1) if (/(Invalid (input|command) detected|Type help or )/i);
10391 return(1) if (/Ambiguous command/i);
10392 return(-1) if (/command authorization failed/i);
10393
10394 ProcessHistory("COMMENTS","keysort","RPLMAX","! $_");
10395 }
10396 ProcessHistory("COMMENTS","keysort","RPLMAX","!\n");
10397 return(0);
10398}
10399
10400# This routine parses "show vlan"
10401sub ShowVLAN {
10402 print STDERR " In ShowVLAN: $_" if ($debug);
10403
10404 ($_ = <INPUT>, return(1)) if (!$DO_SHOW_VLAN);
10405
10406 while (<INPUT>) {
10407 tr/\015//d;
10408 last if (/^$prompt/);
10409 next if (/^(\s*|\s*$cmd\s*)$/);
10410 return(1) if /^\s*\^\s*$/;
10411 return(1) if (/Line has invalid autocommand /);
10412 return(1) if (/(Invalid (input|command) detected|Type help or )/i);
10413 return(1) if (/Ambiguous command/i);
10414 return(-1) if (/command authorization failed/i);
10415
10416 ProcessHistory("COMMENTS","keysort","IO","!VLAN: $_");
10417 }
10418 ProcessHistory("COMMENTS","keysort","IO","!\n");
10419 return(0);
10420}
10421
10422# This routine processes a "write term"
10423sub WriteTerm {
10424 print STDERR " In WriteTerm: $_" if ($debug);
10425 my($lineauto,$comment,$linecnt) = (0,0,0);
10426
10427 while (<INPUT>) {
10428 tr/\015//d;
10429 last if (/^$prompt/);
10430 return(1) if (!$linecnt && /^\s+\^\s*$/);
10431 return(1) if (/Line has invalid autocommand /);
10432 return(1) if (/(Invalid (input|command) detected|Type help or )/i);
10433 return(1) if (/\%Error: No such file or directory/);
10434 return(0) if ($found_end); # Only do this routine once
10435 return(-1) if (/command authorization failed/i);
10436 return(-1) if (/% ?configuration buffer full/i);
10437 /^! no configuration change since last restart/i && next;
10438 # skip emtpy lines at the beginning
10439 if (!$linecnt && /^\s*$/) {
10440 next;
10441 }
10442 if (!$linecnt && defined($config_register)) {
10443 ProcessHistory("","","", "!\nconfig-register $config_register\n");
10444 }
10445
10446 /Non-Volatile memory is in use/ && return(-1); # NvRAM is locked
10447 /% Configuration buffer full, / && return(-1); # buffer is in use
10448 $linecnt++;
10449 $lineauto = 0 if (/^[^ ]/);
10450 # skip the crap
10451 if (/^(##+|(building|current) configuration)/i) {
10452 while (<INPUT>) {
10453 next if (/^Current configuration\s*:/i);
10454 next if (/^:/);
10455 next if (/^([%!].*|\s*)$/);
10456 next if (/^ip add.*ipv4:/); # band-aid for 3620 12.0S
10457 last;
10458 }
10459 tr/\015//d;
10460 }
10461 # some versions have other crap mixed in with the bits in the
10462 # block above
10463 /^! (Last configuration|NVRAM config last)/ && next;
10464 # and for the ASA
10465 /^: (Written by \S+ at|Saved)/ && next;
10466
10467 # skip consecutive comment lines to avoid oscillating extra comment
10468 # line on some access servers. grrr.
10469 if (/^!\s*$/) {
10470 next if ($comment);
10471 ProcessHistory("","","",$_);
10472 $comment++;
10473 next;
10474 }
10475 $comment = 0;
10476
10477 # Dog gone Cool matches to process the rest of the config
10478 /^tftp-server flash / && next; # kill any tftp remains
10479 /^ntp clock-period / && next; # kill ntp clock-period
10480 /^ length / && next; # kill length on serial lines
10481 /^ width / && next; # kill width on serial lines
10482 $lineauto = 1 if /^ modem auto/;
10483 /^ speed / && $lineauto && next; # kill speed on serial lines
10484 /^ clockrate / && next; # kill clockrate on serial interfaces
10485 if (/^(enable )?(password|passwd)( level \d+)? / && $filter_pwds >= 1) {
10486 ProcessHistory("ENABLE","","","!$1$2$3 <removed>\n");
10487 next;
10488 }
10489 if (/^(enable secret) / && $filter_pwds >= 2) {
10490 ProcessHistory("ENABLE","","","!$1 <removed>\n");
10491 next;
10492 }
10493 if (/^username (\S+)(\s.*)? secret /) {
10494 if ($filter_pwds >= 2) {
10495 ProcessHistory("USER","keysort","$1",
10496 "!username $1$2 secret <removed>\n");
10497 } else {
10498 ProcessHistory("USER","keysort","$1","$_");
10499 }
10500 next;
10501 }
10502 if (/^username (\S+)(\s.*)? password ((\d) \S+|\S+)/) {
10503 if ($filter_pwds >= 2) {
10504 ProcessHistory("USER","keysort","$1",
10505 "!username $1$2 password <removed>\n");
10506 } elsif ($filter_pwds >= 1 && $4 ne "5"){
10507 ProcessHistory("USER","keysort","$1",
10508 "!username $1$2 password <removed>\n");
10509 } else {
10510 ProcessHistory("USER","keysort","$1","$_");
10511 }
10512 next;
10513 }
10514 if (/^( set session-key (in|out)bound ah \d+ )/ && $filter_pwds >= 1) {
10515 ProcessHistory("","","","!$1<removed>\n");
10516 next;
10517 }
10518 if (/^( set session-key (in|out)bound esp \d+ (authenticator|cypher) )/
10519 && $filter_pwds >= 1) {
10520 ProcessHistory("","","","!$1<removed>\n");
10521 next;
10522 }
10523 if (/^(\s*)password / && $filter_pwds >= 1) {
10524 ProcessHistory("LINE-PASS","","","!$1password <removed>\n");
10525 next;
10526 }
10527 if (/^(\s*)secret / && $filter_pwds >= 2) {
10528 ProcessHistory("LINE-PASS","","","!$1secret <removed>\n");
10529 next;
10530 }
10531 if (/^\s*neighbor (\S*) password / && $filter_pwds >= 1) {
10532 ProcessHistory("","","","! neighbor $1 password <removed>\n");
10533 next;
10534 }
10535 if (/^(ppp .* password) 7 .*/ && $filter_pwds >= 1) {
10536 ProcessHistory("","","","!$1 <removed>\n"); next;
10537 }
10538 if (/^(ip ftp password) / && $filter_pwds >= 1) {
10539 ProcessHistory("","","","!$1 <removed>\n"); next;
10540 }
10541 if (/^( ip ospf authentication-key) / && $filter_pwds >= 1) {
10542 ProcessHistory("","","","!$1 <removed>\n"); next;
10543 }
10544 # isis passwords appear to be completely plain-text
10545 if (/^\s+isis password (\S+)( .*)?/ && $filter_pwds >= 1) {
10546 ProcessHistory("","","","!isis password <removed>$2\n"); next;
10547 }
10548 if (/^\s+(domain-password|area-password) (\S+)( .*)?/
10549 && $filter_pwds >= 1) {
10550 ProcessHistory("","","","!$1 <removed>$3\n"); next;
10551 }
10552 # this is reversable, despite 'md5' in the cmd
10553 if (/^( ip ospf message-digest-key \d+ md5) / && $filter_pwds >= 1) {
10554 ProcessHistory("","","","!$1 <removed>\n"); next;
10555 }
10556 # this is also reversable, despite 'md5 encrypted' in the cmd
10557 if (/^( message-digest-key \d+ md5 (7|encrypted)) /
10558 && $filter_pwds >= 1) {
10559 ProcessHistory("","","","!$1 <removed>\n"); next;
10560 }
10561 if (/^((crypto )?isakmp key) \S+ / && $filter_pwds >= 1) {
10562 ProcessHistory("","","","!$1 <removed> $'"); next;
10563 }
10564 # filter HSRP passwords
10565 if (/^(\s+standby \d+ authentication) / && $filter_pwds >= 1) {
10566 ProcessHistory("","","","!$1 <removed>\n"); next;
10567 }
10568 # this appears in "measurement/sla" images
10569 if (/^(\s+key-string \d?)/ && $filter_pwds >= 1) {
10570 ProcessHistory("","","","!$1 <removed>\n"); next;
10571 }
10572 if (/^( l2tp tunnel \S+ password)/ && $filter_pwds >= 1) {
10573 ProcessHistory("","","","!$1 <removed>\n"); next;
10574 }
10575 # i am told these are plain-text on the PIX
10576 if (/^(vpdn username (\S+) password)/) {
10577 if ($filter_pwds >= 1) {
10578 ProcessHistory("USER","keysort","$2","!$1 <removed>\n");
10579 } else {
10580 ProcessHistory("USER","keysort","$2","$_");
10581 }
10582 next;
10583 }
10584 # ASA/PIX keys in more system:running-config
10585 if (/^( pre-shared-key | key |failover key ).*/ && $filter_pwds >= 1) {
10586 ProcessHistory("","","","!$1 <removed> $'"); next;
10587 }
10588 if (/(\s+ldap-login-password )\S+(.*)/ && $filter_pwds >= 1) {
10589 ProcessHistory("","","","!$1 <removed> $'"); next;
10590 }
10591 #
10592 if (/^( cable shared-secret )/ && $filter_pwds >= 1) {
10593 ProcessHistory("","","","!$1 <removed>\n");
10594 next;
10595 }
10596 /fair-queue individual-limit/ && next;
10597 # sort ip explicit-paths.
10598 if (/^ip explicit-path name (\S+)/) {
10599 my($key) = $1;
10600 my($expath) = $_;
10601 while (<INPUT>) {
10602 tr/\015//d;
10603 last if (/^$prompt/);
10604 last if (! /^(ip explicit-path name |[ !])/);
10605 if (/^ip explicit-path name (\S+)/) {
10606 ProcessHistory("EXPATH","keysort","$key","$expath");
10607 $key = $1;
10608 $expath = $_;
10609 } else {
10610 $expath .= $_;
10611 }
10612 }
10613 ProcessHistory("EXPATH","keysort","$key","$expath");
10614 }
10615 # sort route-maps
10616 if (/^route-map (\S+)/) {
10617 my($key) = $1;
10618 my($routemap) = $_;
10619 while (<INPUT>) {
10620 tr/\015//d;
10621 last if (/^$prompt/ || ! /^(route-map |[ !])/);
10622 if (/^route-map (\S+)/) {
10623 ProcessHistory("ROUTEMAP","keysort","$key","$routemap");
10624 $key = $1;
10625 $routemap = $_;
10626 } else {
10627 $routemap .= $_;
10628 }
10629 }
10630 ProcessHistory("ROUTEMAP","keysort","$key","$routemap");
10631 }
10632 # filter out any RCS/CVS tags to avoid confusing local CVS storage
10633 s/\$(Revision|Id):/ $1:/;
10634 # order access-lists
10635 /^access-list\s+(\d\d?)\s+(\S+)\s+(\S+)/ &&
10636 ProcessHistory("ACL $1 $2","$aclsort","$3","$_") && next;
10637 # order extended access-lists
10638 /^access-list\s+(\d\d\d)\s+(\S+)\s+ip\s+host\s+(\S+)/ &&
10639 ProcessHistory("EACL $1 $2","$aclsort","$3","$_") && next;
10640 /^access-list\s+(\d\d\d)\s+(\S+)\s+ip\s+(\d\S+)/ &&
10641 ProcessHistory("EACL $1 $2","$aclsort","$3","$_") && next;
10642 /^access-list\s+(\d\d\d)\s+(\S+)\s+ip\s+any/ &&
10643 ProcessHistory("EACL $1 $2","$aclsort","0.0.0.0","$_") && next;
10644 # sort ipv{4,6} access-lists
10645 if (/^ipv(4|6) access-list (\S+)\s*$/) {
10646 my($nlri, $key) = ($1, $2);
10647 my($seq, $cmd);
10648 ProcessHistory("ACL $nlri $key","","","$_");
10649 while (<INPUT>) {
10650 tr/\015//d;
10651 last if (/^$prompt/ || /^\S/);
10652 ($seq, $cmd, $misc, $ip) = ($_ =~ /^\s+(\d+) (\w+) (.*\s)(\w+)/);
10653 if ($cmd =~ /(permit|deny)/) {
10654 ProcessHistory("ACL $nlri $key $cmd","$aclsort","$ip",
10655 " $cmd $misc$ip\n");
10656 } else {
10657 ProcessHistory("ACL $nlri $key","",""," $cmd $misc$ip\n");
10658 }
10659 }
10660 }
10661 # order arp lists
10662 /^arp\s+(\d+\.\d+\.\d+\.\d+)\s+/ &&
10663 ProcessHistory("ARP","$aclsort","$1","$_") && next;
10664 /^ip prefix-list\s+(\S+)\s+seq\s+(\d+)\s+(permit|deny)\s+(\d\S+)(\/.*)$/ &&
10665 ProcessHistory("PACL $1 $3","$aclsort","$4","ip prefix-list $1 $3 $4$5\n")
10666 && next;
10667 # order logging statements
10668 /^logging (\d+\.\d+\.\d+\.\d+)/ &&
10669 ProcessHistory("LOGGING","ipsort","$1","$_") && next;
10670 # order/prune snmp-server host statements
10671 # we only prune lines of the form
10672 # snmp-server host a.b.c.d <community>
10673 if (/^snmp-server host (\d+\.\d+\.\d+\.\d+) /) {
10674 if ($filter_commstr) {
10675 my($ip) = $1;
10676 my($line) = "snmp-server host $ip";
10677 my(@tokens) = split(' ', $');
10678 my($token);
10679 while ($token = shift(@tokens)) {
10680 if ($token eq 'version') {
10681 $line .= " " . join(' ', ($token, shift(@tokens)));
10682 if ($token eq '3') {
10683 $line .= " " . join(' ', ($token, shift(@tokens)));
10684 }
10685 } elsif ($token eq 'vrf') {
10686 $line .= " " . join(' ', ($token, shift(@tokens)));
10687 } elsif ($token =~ /^(informs?|traps?|(no)?auth)$/) {
10688 $line .= " " . $token;
10689 } else {
10690 $line = "!$line " . join(' ', ("<removed>",
10691 join(' ',@tokens)));
10692 last;
10693 }
10694 }
10695 ProcessHistory("SNMPSERVERHOST","ipsort","$ip","$line\n");
10696 } else {
10697 ProcessHistory("SNMPSERVERHOST","ipsort","$1","$_");
10698 }
10699 next;
10700 }
10701 if (/^(snmp-server community) (\S+)/) {
10702 if ($filter_commstr) {
10703 ProcessHistory("SNMPSERVERCOMM","keysort","$_",
10704 "!$1 <removed>$'") && next;
10705 } else {
10706 ProcessHistory("SNMPSERVERCOMM","keysort","$_","$_") && next;
10707 }
10708 }
10709 # prune tacacs/radius server keys
10710 if (/^((tacacs|radius)-server\s(\w*[-\s(\s\S+])*\s?key) (\d )?\w+/
10711 && $filter_pwds >= 1) {
10712 ProcessHistory("","","","!$1 <removed>$'"); next;
10713 }
10714 # order clns host statements
10715 /^clns host \S+ (\S+)/ &&
10716 ProcessHistory("CLNS","keysort","$1","$_") && next;
10717 # order alias statements
10718 /^alias / && ProcessHistory("ALIAS","keysort","$_","$_") && next;
10719 # delete ntp auth password - this md5 is a reversable too
10720 if (/^(ntp authentication-key \d+ md5) / && $filter_pwds >= 1) {
10721 ProcessHistory("","","","!$1 <removed>\n"); next;
10722 }
10723 # order ntp peers/servers
10724 if (/^ntp (server|peer) (\d+)\.(\d+)\.(\d+)\.(\d+)/) {
10725 $sortkey = sprintf("$1 %03d%03d%03d%03d",$2,$3,$4,$5);
10726 ProcessHistory("NTP","keysort",$sortkey,"$_");
10727 next;
10728 }
10729 # order ip host statements
10730 /^ip host (\S+) / &&
10731 ProcessHistory("IPHOST","keysort","$1","$_") && next;
10732 # order ip nat source static statements
10733 /^ip nat (\S+) source static (\S+)/ &&
10734 ProcessHistory("IP NAT $1","ipsort","$2","$_") && next;
10735 # order atm map-list statements
10736 /^\s+ip\s+(\d+\.\d+\.\d+\.\d+)\s+atm-vc/ &&
10737 ProcessHistory("ATM map-list","ipsort","$1","$_") && next;
10738 # order ip rcmd lines
10739 /^ip rcmd/ && ProcessHistory("RCMD","keysort","$_","$_") && next;
10740
10741 # system controller
10742 /^syscon address (\S*) (\S*)/ &&
10743 ProcessHistory("","","","!syscon address $1 <removed>\n") &&
10744 next;
10745 if (/^syscon password (\S*)/ && $filter_pwds >= 1) {
10746 ProcessHistory("","","","!syscon password <removed>\n");
10747 next;
10748 }
10749
10750 /^ *Cryptochecksum:/ && next;
10751
10752 # catch anything that wasnt matched above.
10753 ProcessHistory("","","","$_");
10754 # end of config.
10755 if (/^end$/) {
10756 $found_end = 1;
10757 return(0);
10758 }
10759 }
10760
10761 return(0);
10762}
10763
10764# This routine processes a "write term"
10765sub FilterAll {
10766 print STDERR " In FilterAll: $_" if ($debug);
10767
10768 while (<INPUT>) {
10769 tr/\015//d;
10770 last if (/^$prompt/);
10771 return(1) if (/Line has invalid autocommand /);
10772 return(1) if (/(Invalid (input|command) detected|Type help or )/i);
10773 return(1) if (/\%Error: No such file or directory/);
10774 return(-1) if (/command authorization failed/i);
10775 return(-1) if (/% ?configuration buffer full/i);
10776 }
10777}
10778
10779# dummy function
10780sub DoNothing {print STDOUT;}
10781
10782# Main
10783@commandtable = (
10784 # Disable timestamps in output
10785 {'terminal no-timestamp' => 'FilterAll' }, # XR 3.6 style
10786 {'terminal exec prompt no-timestamp' => 'FilterAll' }, # XR 3.8 style
10787
10788 {'admin show version' => 'ShowVersion'},
10789
10790 # XR IMAGE commands
10791 {'admin show install summary' => 'ShowInstallSummary'},
10792 {'admin show license' => 'ShowLicense'},
10793 {'admin show variables boot' => 'ShowBootVar'},
10794
10795 {'show redundancy secondary' => 'ShowRedundancy'},
10796 {'show install active' => 'ShowInstallActive'},
10797 {'admin show env all' => 'ShowEnv'},
10798 {'dir /all nvram:' => 'DirSlotN'},
10799 {'dir /all bootflash:' => 'DirSlotN'},
10800 {'dir /all compactflash:' => 'DirSlotN'},
10801 {'dir /all compactflasha:' => 'DirSlotN'},
10802 {'dir /all slot0:' => 'DirSlotN'},
10803 {'dir /all disk0:' => 'DirSlotN'},
10804 {'dir /all disk0a:' => 'DirSlotN'},
10805 {'dir /all slot1:' => 'DirSlotN'},
10806 {'dir /all disk1:' => 'DirSlotN'},
10807 {'dir /all disk1a:' => 'DirSlotN'},
10808 {'dir /all slot2:' => 'DirSlotN'},
10809 {'dir /all disk2:' => 'DirSlotN'},
10810 {'dir /all harddisk:' => 'DirSlotN'},
10811 {'dir /all harddiska:' => 'DirSlotN'},
10812 {'dir /all harddiskb:' => 'DirSlotN'},
10813 {'dir /all slavenvram:' => 'DirSlotN'},
10814 {'dir /all slavebootflash:' => 'DirSlotN'},
10815 {'dir /all slaveslot0:' => 'DirSlotN'},
10816 {'dir /all slavedisk0:' => 'DirSlotN'},
10817 {'dir /all slaveslot1:' => 'DirSlotN'},
10818 {'dir /all slavedisk1:' => 'DirSlotN'},
10819 {'dir /all slaveslot2:' => 'DirSlotN'},
10820 {'dir /all slavedisk2:' => 'DirSlotN'},
10821 {'dir /all sec-nvram:' => 'DirSlotN'},
10822 {'dir /all sec-bootflash:' => 'DirSlotN'},
10823 {'dir /all sec-slot0:' => 'DirSlotN'},
10824 {'dir /all sec-disk0:' => 'DirSlotN'},
10825 {'dir /all sec-slot1:' => 'DirSlotN'},
10826 {'dir /all sec-disk1:' => 'DirSlotN'},
10827 {'dir /all sec-slot2:' => 'DirSlotN'},
10828 {'dir /all sec-disk2:' => 'DirSlotN'},
10829 {'show controllers' => 'ShowContAll'},
10830 {'admin show running' => 'AdminShowRunning'},
10831 {'admin show diag' => 'AdminShowDiag'},
10832 {'admin show inventory raw' => 'ShowInventory'},
10833 {'show vlan' => 'ShowVLAN'},
10834 {'show debug' => 'ShowDebug'},
10835 {'show rpl maximum' => 'ShowRPL'},
10836 {'show running-config' => 'WriteTerm'},
10837);
10838# Use an array to preserve the order of the commands and a hash for mapping
10839# commands to the subroutine and track commands that have been completed.
10840@commands = map(keys(%$_), @commandtable);
10841%commands = map(%$_, @commandtable);
10842
10843$cisco_cmds = join(";",@commands);
10844$cmds_regexp = join("|", map quotemeta($_), @commands);
10845
10846if (length($host) == 0) {
10847 if ($file) {
10848 print(STDERR "Too few arguments: file name required\n");
10849 exit(1);
10850 } else {
10851 print(STDERR "Too few arguments: host name required\n");
10852 exit(1);
10853 }
10854}
10855open(OUTPUT,">$host.new") || die "Can't open $host.new for writing: $!\n";
10856select(OUTPUT);
10857# make OUTPUT unbuffered if debugging
10858if ($debug) { $| = 1; }
10859
10860if ($file) {
10861 print STDERR "opening file $host\n" if ($debug);
10862 print STDOUT "opening file $host\n" if ($log);
10863 open(INPUT,"<$host") || die "open failed for $host: $!\n";
10864} else {
10865 print STDERR "executing clogin -t $timeo -c\"$cisco_cmds\" $host\n" if ($debug);
10866 print STDOUT "executing clogin -t $timeo -c\"$cisco_cmds\" $host\n" if ($log);
10867 if (defined($ENV{NOPIPE}) && $ENV{NOPIPE} =~ /^YES/i) {
10868 system "clogin -t $timeo -c \"$cisco_cmds\" $host </dev/null > $host.raw 2>&1" || die "clogin failed for $host: $!\n";
10869 open(INPUT, "< $host.raw") || die "clogin failed for $host: $!\n";
10870 } else {
10871 open(INPUT,"clogin -t $timeo -c \"$cisco_cmds\" $host </dev/null |") || die "clogin failed for $host: $!\n";
10872 }
10873}
10874
10875# determine ACL sorting mode
10876if ($ENV{"ACLSORT"} =~ /no/i) {
10877 $aclsort = "";
10878}
10879# determine community string filtering mode
10880if (defined($ENV{"NOCOMMSTR"}) &&
10881 ($ENV{"NOCOMMSTR"} =~ /yes/i || $ENV{"NOCOMMSTR"} =~ /^$/)) {
10882 $filter_commstr = 1;
10883} else {
10884 $filter_commstr = 0;
10885}
10886# determine password filtering mode
10887if ($ENV{"FILTER_PWDS"} =~ /no/i) {
10888 $filter_pwds = 0;
10889} elsif ($ENV{"FILTER_PWDS"} =~ /all/i) {
10890 $filter_pwds = 2;
10891} else {
10892 $filter_pwds = 1;
10893}
10894
10895ProcessHistory("","","","!RANCID-CONTENT-TYPE: cisco-xr\n!\n");
10896ProcessHistory("COMMENTS","keysort","B0","!\n");
10897ProcessHistory("COMMENTS","keysort","D0","!\n");
10898ProcessHistory("COMMENTS","keysort","F0","!\n");
10899ProcessHistory("COMMENTS","keysort","G0","!\n");
10900TOP: while(<INPUT>) {
10901 tr/\015//d;
10902 if (/[>#]\s?exit$/) {
10903 $clean_run = 1;
10904 last;
10905 }
10906 if (/^Error:/) {
10907 print STDOUT ("$host clogin error: $_");
10908 print STDERR ("$host clogin error: $_") if ($debug);
10909 $clean_run = 0;
10910 last;
10911 }
10912 while (/#\s*($cmds_regexp)\s*$/) {
10913 $cmd = $1;
10914 if (!defined($prompt)) {
10915 $prompt = ($_ =~ /^([^#]+#)/)[0];
10916 $prompt =~ s/([][}{)(\\])/\\$1/g;
10917 print STDERR ("PROMPT MATCH: $prompt\n") if ($debug);
10918 }
10919 print STDERR ("HIT COMMAND:$_") if ($debug);
10920 if (! defined($commands{$cmd})) {
10921 print STDERR "$host: found unexpected command - \"$cmd\"\n";
10922 $clean_run = 0;
10923 last TOP;
10924 }
10925 $rval = &{$commands{$cmd}};
10926 delete($commands{$cmd});
10927 if ($rval == -1) {
10928 $clean_run = 0;
10929 last TOP;
10930 }
10931 }
10932}
10933print STDOUT "Done $logincmd: $_\n" if ($log);
10934# Flush History
10935ProcessHistory("","","","");
10936# Cleanup
10937close(INPUT);
10938close(OUTPUT);
10939
10940if (defined($ENV{NOPIPE}) && $ENV{NOPIPE} =~ /^YES/i) {
10941 unlink("$host.raw") if (! $debug);
10942}
10943
10944# check for completeness
10945if (scalar(%commands) || !$clean_run || !$found_end) {
10946 if (scalar(%commands)) {
10947 printf(STDOUT "$host: missed cmd(s): %s\n", join(',', keys(%commands)));
10948 printf(STDERR "$host: missed cmd(s): %s\n", join(',', keys(%commands))) if ($debug);
10949 }
10950 if (!$clean_run || !$found_end) {
10951 print STDOUT "$host: End of run not found\n";
10952 print STDERR "$host: End of run not found\n" if ($debug);
10953 system("/usr/bin/tail -1 $host.new");
10954 }
10955 unlink "$host.new" if (! $debug);
10956}
10957#! /usr/bin/perl
10958##
10959## $Id: zrancid.in 3135 2015-07-02 18:59:15Z heas $
10960##
10961## rancid 3.4.1
10962## Copyright (c) 1997-2016 by Terrapin Communications, Inc.
10963## All rights reserved.
10964##
10965## This code is derived from software contributed to and maintained by
10966## Terrapin Communications, Inc. by Henry Kilmer, John Heasley, Andrew Partan,
10967## Pete Whiting, Austin Schutz, and Andrew Fort.
10968##
10969## Redistribution and use in source and binary forms, with or without
10970## modification, are permitted provided that the following conditions
10971## are met:
10972## 1. Redistributions of source code must retain the above copyright
10973## notice, this list of conditions and the following disclaimer.
10974## 2. Redistributions in binary form must reproduce the above copyright
10975## notice, this list of conditions and the following disclaimer in the
10976## documentation and/or other materials provided with the distribution.
10977## 3. All advertising materials mentioning features or use of this software
10978## must display the following acknowledgement:
10979## This product includes software developed by Terrapin Communications,
10980## Inc. and its contributors for RANCID.
10981## 4. Neither the name of Terrapin Communications, Inc. nor the names of its
10982## contributors may be used to endorse or promote products derived from
10983## this software without specific prior written permission.
10984## 5. It is requested that non-binding fixes and modifications be contributed
10985## back to Terrapin Communications, Inc.
10986##
10987## THIS SOFTWARE IS PROVIDED BY Terrapin Communications, INC. AND CONTRIBUTORS
10988## ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
10989## TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
10990## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COMPANY OR CONTRIBUTORS
10991## BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
10992## CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
10993## SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
10994## INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
10995## CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
10996## ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
10997## POSSIBILITY OF SUCH DAMAGE.
10998#
10999# This version of rancid tries to deal with zebra s/w.
11000#
11001# RANCID - Really Awesome New Cisco confIg Differ
11002#
11003# usage: zrancid [-dltCV] [-f filename | hostname]
11004#
11005use Getopt::Std;
11006getopts('dflt:CV');
11007if ($opt_V) {
11008 print "rancid 3.4.1\n";
11009 exit(0);
11010}
11011$log = $opt_l;
11012$debug = $opt_d;
11013$file = $opt_f;
11014$host = $ARGV[0];
11015$clean_run = 0;
11016$found_end = 0;
11017$timeo = 90; # clogin timeout in seconds
11018
11019my(@commandtable, %commands, @commands);# command lists
11020my($aclsort) = ("ipsort"); # ACL sorting mode
11021my($filter_commstr); # SNMP community string filtering
11022my($filter_pwds); # password filtering mode
11023
11024# force a terminal type so as not to confuse Linux
11025$ENV{'TERM'} = "ansi";
11026
11027# This routine is used to print out the router configuration
11028sub ProcessHistory {
11029 my($new_hist_tag,$new_command,$command_string,@string) = (@_);
11030 if ((($new_hist_tag ne $hist_tag) || ($new_command ne $command))
11031 && scalar(%history)) {
11032 print eval "$command \%history";
11033 undef %history;
11034 }
11035 if (($new_hist_tag) && ($new_command) && ($command_string)) {
11036 if ($history{$command_string}) {
11037 $history{$command_string} = "$history{$command_string}@string";
11038 } else {
11039 $history{$command_string} = "@string";
11040 }
11041 } elsif (($new_hist_tag) && ($new_command)) {
11042 $history{++$#history} = "@string";
11043 } else {
11044 print "@string";
11045 }
11046 $hist_tag = $new_hist_tag;
11047 $command = $new_command;
11048 1;
11049}
11050
11051sub numerically { $a <=> $b; }
11052
11053# This is a sort routine that will sort numerically on the
11054# keys of a hash as if it were a normal array.
11055sub keynsort {
11056 local(%lines) = @_;
11057 local($i) = 0;
11058 local(@sorted_lines);
11059 foreach $key (sort numerically keys(%lines)) {
11060 $sorted_lines[$i] = $lines{$key};
11061 $i++;
11062 }
11063 @sorted_lines;
11064}
11065
11066# This is a sort routine that will sort on the
11067# keys of a hash as if it were a normal array.
11068sub keysort {
11069 local(%lines) = @_;
11070 local($i) = 0;
11071 local(@sorted_lines);
11072 foreach $key (sort keys(%lines)) {
11073 $sorted_lines[$i] = $lines{$key};
11074 $i++;
11075 }
11076 @sorted_lines;
11077}
11078
11079# This is a sort routine that will sort on the
11080# values of a hash as if it were a normal array.
11081sub valsort{
11082 local(%lines) = @_;
11083 local($i) = 0;
11084 local(@sorted_lines);
11085 foreach $key (sort values %lines) {
11086 $sorted_lines[$i] = $key;
11087 $i++;
11088 }
11089 @sorted_lines;
11090}
11091
11092# This is a numerical sort routine (ascending).
11093sub numsort {
11094 local(%lines) = @_;
11095 local($i) = 0;
11096 local(@sorted_lines);
11097 foreach $num (sort {$a <=> $b} keys %lines) {
11098 $sorted_lines[$i] = $lines{$num};
11099 $i++;
11100 }
11101 @sorted_lines;
11102}
11103
11104# This is a sort routine that will sort on the
11105# ip address when the ip address is anywhere in
11106# the strings.
11107sub ipsort {
11108 local(%lines) = @_;
11109 local($i) = 0;
11110 local(@sorted_lines);
11111 foreach $addr (sort sortbyipaddr keys %lines) {
11112 $sorted_lines[$i] = $lines{$addr};
11113 $i++;
11114 }
11115 @sorted_lines;
11116}
11117
11118# These two routines will sort based upon IP addresses
11119sub ipaddrval {
11120 my(@a) = ($_[0] =~ m#^(\d+)\.(\d+)\.(\d+)\.(\d+)$#);
11121 $a[3] + 256 * ($a[2] + 256 * ($a[1] +256 * $a[0]));
11122}
11123sub sortbyipaddr {
11124 &ipaddrval($a) <=> &ipaddrval($b);
11125}
11126
11127# This routine parses "show version"
11128sub ShowVersion {
11129 print STDERR " In ShowVersion: $_" if ($debug);
11130
11131 while (<INPUT>) {
11132 tr/\015//d;
11133 last if(/^$prompt/);
11134 next if(/^(\s*|\s*$cmd\s*)$/);
11135 return(-1) if (/command authorization failed/i);
11136
11137 ProcessHistory("COMMENTS","keysort","B0", "!$_") && next;
11138
11139 }
11140 return(0);
11141}
11142
11143# This routine processes a "write term"
11144sub WriteTerm {
11145 print STDERR " In WriteTerm: $_" if ($debug);
11146
11147 while (<INPUT>) {
11148 tr/\015//d;
11149 last if(/^$prompt/);
11150 return(-1) if (/command authorization failed/i);
11151
11152 /Non-Volatile memory is in use/ && return(-1); # NvRAM is locked
11153 # skip the crap
11154 if (/^(##+$|(Building|Current) configuration)/i) {
11155 while (<INPUT>) {
11156 next if (/^Current configuration\s*:/i);
11157 next if (/^([%!].*|\s*)$/);
11158 last;
11159 }
11160 tr/\015//d;
11161 }
11162 # some versions have other crap mixed in with the bits in the
11163 # block above
11164 /^! Last Changed:/ && next;
11165
11166 # Dog gone Cool matches to process the rest of the config
11167# /^tftp-server flash / && next; # kill any tftp remains
11168# /^ntp clock-period / && next; # kill ntp clock-period
11169# /^ length / && next; # kill length on serial lines
11170# /^ width / && next; # kill width on serial lines
11171# /^ clockrate / && next; # kill clockrate on serial interfaces
11172
11173 if (/^((enable )?password( \d)?) / && $filter_pwds >= 1) {
11174 ProcessHistory("ENABLE","","","!$1 <removed>\n");
11175 next;
11176 }
11177 if (/^username (\S+)(\s.*)? password ((\d) \S+|\S+)/) {
11178 if ($filter_pwds == 2) {
11179 ProcessHistory("USER","keysort","$1","!username $1$2 password <removed>\n");
11180 } elsif ($filter_pwds == 1 && $4 ne "5"){
11181 ProcessHistory("USER","keysort","$1","!username $1$2 password <removed>\n");
11182 } else {
11183 ProcessHistory("USER","keysort","$1","$_");
11184 }
11185 next;
11186 }
11187
11188 # prune passwords {bgp, ...}
11189 if (/^\s*neighbor (\S*) password / && $filter_pwds >= 1) {
11190 ProcessHistory("","","","! neighbor $1 password <removed>\n");
11191 next;
11192 }
11193 # sort route-maps
11194 if (/^route-map (\S+)/) {
11195 my($key) = $1;
11196 my($routemap) = $_;
11197 while (<INPUT>) {
11198 tr/\015//d;
11199 last if (/^$prompt/ || ! /^(route-map |[ !])/);
11200 if (/^route-map (\S+)/) {