1package iosxr;
2##
3## @PACKAGE@ @VERSION@
4@copyright@
5#
6#  RANCID - Really Awesome New Cisco confIg Differ
7#
8#  iosxr.pm - Cisco IOS-XR rancid procedures
9
10use 5.010;
11use strict 'vars';
12use warnings;
13no warnings 'uninitialized';
14require(Exporter);
15our @ISA = qw(Exporter);
16
17use rancid @VERSION@;
18
19our $proc;
20our $found_env;
21our $found_diag;
22our $found_version;
23our $config_register;			# configuration register value
24
25our $type;				# device model, from ShowVersion
26
27our $C0;				# output formatting control
28our $E0;
29our $H0;
30our $I0;
31
32@ISA = qw(Exporter rancid main);
33#XXX @Exporter::EXPORT = qw($VERSION @commandtable %commands @commands);
34
35# load-time initialization
36sub import {
37    0;
38}
39
40# post-open(collection file) initialization
41sub init {
42    $proc = "";
43    $found_env = 0;
44    $found_diag = 0;
45    $found_version = 0;
46    $config_register = undef;		# configuration register value
47
48    # XXX $type = undef;		# device model, from ShowVersion
49
50    $C0 = 0;				# output formatting control
51    $E0 = 0;
52    $H0 = 0;
53    $I0 = 0;
54
55    # add content lines and separators
56    ProcessHistory("","","","!RANCID-CONTENT-TYPE: $devtype\n!\n");
57    ProcessHistory("COMMENTS","keysort","B0","!\n");
58    ProcessHistory("COMMENTS","keysort","D0","!\n");
59    ProcessHistory("COMMENTS","keysort","F0","!\n");
60    ProcessHistory("COMMENTS","keysort","G0","!\n");
61
62    0;
63}
64
65# main loop of input of device output
66sub inloop {
67    my($INPUT, $OUTPUT) = @_;
68    my($cmd, $rval);
69
70TOP: while(<$INPUT>) {
71	tr/\015//d;
72CMD:	if (/[>#]\s?exit$/) {
73	    $clean_run = 1;
74	    last;
75	}
76	if (/^Error:/) {
77	    print STDOUT ("$host clogin error: $_");
78	    print STDERR ("$host clogin error: $_") if ($debug);
79	    $clean_run = 0;
80	    last;
81	}
82	while (/[>#]\s*($cmds_regexp)\s*$/) {
83	    $cmd = $1;
84	    if (!defined($prompt)) {
85		$prompt = ($_ =~ /^([^#]+#)/)[0];
86		$prompt =~ s/([][}{)(+\\])/\\$1/g;
87		print STDERR ("PROMPT MATCH: $prompt\n") if ($debug);
88	    }
89	    print STDERR ("HIT COMMAND:$_") if ($debug);
90	    if (! defined($commands{$cmd})) {
91		print STDERR "$host: found unexpected command - \"$cmd\"\n";
92		$clean_run = 0;
93		last TOP;
94	    }
95	    if (! defined(&{$commands{$cmd}})) {
96		printf(STDERR "$host: undefined function - \"%s\"\n",
97		       $commands{$cmd});
98		$clean_run = 0;
99		last TOP;
100	    }
101	    $rval = &{$commands{$cmd}}($INPUT, $OUTPUT, $cmd);
102	    delete($commands{$cmd});
103	    if ($rval == -1) {
104		$clean_run = 0;
105		last TOP;
106	    }
107	    if (defined($prompt)) {
108		if (/$prompt/) {
109		    goto CMD;
110		}
111	    }
112	}
113    }
114}
115
116# This routine parses "admin show version"
117sub ShowVersion {
118    my($INPUT, $OUTPUT, $cmd) = @_;
119    my($slave, $slaveslot);
120    print STDERR "    In ShowVersion: $_" if ($debug);
121
122    while (<$INPUT>) {
123	tr/\015//d;
124	if (/^$prompt/) { $found_version = 1; last};
125	next if (/^(\s*|\s*$cmd\s*)$/);
126	# filter the bloody cmd-line timestamp
127	next if (/^\w{3} \w{3,4} {1,3}\d{1,2} {1,2}\d{1,2}:\d+:\d+\.\d+ \S+$/);
128	return(1) if (/Line has invalid autocommand /);
129	return(1) if (/(Invalid (input|command) detected|Type help or )/i);
130	next if (/^(\e\[.\d+h)?sysadmin-vm:[^#]+# /i);			# NCS
131	return(1) if (/^(-+\^|syntax error: unknown argument)$/i);	# NCS
132	return(0) if ($found_version);		# Only do this routine once
133	return(-1) if (/command authorization failed/i);
134
135	if (/^Slave in slot (\d+) is running/) {
136	    $slave = " Slave:";
137	    $slaveslot = ", slot $1";
138	    next;
139	}
140	/^(Cisco )?IOS .* Software,? \(([A-Za-z0-9_-]*)\), .*Version\s+(.*)$/ &&
141	    ProcessHistory("COMMENTS","keysort","F1",
142		"!Image:$slave Software: $2, $3\n") && next;
143	/^([A-Za-z-0-9_]*) Synced to mainline version: (.*)$/ &&
144	    ProcessHistory("COMMENTS","keysort","F2",
145		"!Image:$slave $1 Synced to mainline version: $2\n") && next;
146	/^Compiled (.*)$/ &&
147	    ProcessHistory("COMMENTS","keysort","F3",
148		"!Image:$slave Compiled: $1\n") && next;
149	/^ROM: (IOS \S+ )?(System )?Bootstrap.*(Version.*)$/ &&
150	    ProcessHistory("COMMENTS","keysort","G1",
151		"!ROM Bootstrap: $3\n") && next;
152	/^Serial Number:\s+(.*)$/ &&
153	    ProcessHistory("COMMENTS","keysort","C1", "!$_") && next;
154
155	/^Activation Key:\s+(.*)$/ &&
156	    ProcessHistory("COMMENTS","keysort","C2", "!$_") && next;
157	/^ROM: \d+ Bootstrap .*(Version.*)$/ &&
158	    ProcessHistory("COMMENTS","keysort","G2",
159		"!ROM Image: Bootstrap $1\n!\n") && next;
160	/^ROM: .*(Version.*)$/ &&
161	    ProcessHistory("COMMENTS","keysort","G3","!ROM Image: $1\n") && next;
162	/^BOOTFLASH: .*(Version.*)$/ &&
163	    ProcessHistory("COMMENTS","keysort","G4","!BOOTFLASH: $1\n") && next;
164	/^BOOTLDR: .*(Version.*)$/ &&
165	    ProcessHistory("COMMENTS","keysort","G4","!BOOTLDR: $1\n") && next;
166	/^System image file is "([^\"]*)", booted via (\S*)/ &&
167# removed the booted source due to
168# CSCdk28131: cycling info in 'sh ver'
169#	ProcessHistory("COMMENTS","keysort","F4","!Image: booted via $2, $1\n") &&
170	    ProcessHistory("COMMENTS","keysort","F4","!Image: booted $1\n") &&
171	    next;
172	/^System image file is "([^\"]*)"$/ &&
173	    ProcessHistory("COMMENTS","keysort","F5","!Image: $1\n") && next;
174	if (/(\S+(?:\sseries)?)\s+(?:\(([^)]+)\)\s+processor|\(revision[^)]+\)).*\s+with (\S+k) bytes/i) {
175	    $proc = $1;
176	    my($cpu) = $2;
177	    my($mem) = $3;
178	    my($device) = "router";
179
180	    # the next line ought to be the more specific cpu info, grab it.
181	    # yet, some boards/IOS vers have a processor ID line between these
182	    # two.  grrr.  make sure we dont grab the "software" junk that
183	    # follows these lines by looking for "CPU at " or the 2600s
184	    # "processor: " unique string.  there are undoubtedly many other
185	    # incantations.  for a slave, we dont get this info, its just a
186	    # blank line.
187	    $_ = <$INPUT>;
188	    if (/processor board id/i) {
189		my($sn);
190
191		if (/processor board id (\S+)/i) {
192		    $sn = $1;
193		    $sn =~ s/,$//;
194		    ProcessHistory("COMMENTS","keysort","D9",
195				   "!Processor ID: $sn\n");
196		}
197		$_ = <$INPUT>;
198	    }
199	    $_ = "" if (! /(cpu at |processor: |$cpu processor,)/i);
200	    tr/\015//d;
201	    s/implementation/impl/i;
202	    if ($_ !~ /^\s*$/) {
203		chomp;
204		s/^/, /;
205	    }
206
207	    if ($proc =~ /1200[48]\/(GRP|PRP)/ || $proc =~ /1201[26]\/(GRP|PRP)/) {
208		$type = "12000";
209	    } elsif ($proc =~ /1201[26]-8R\/(GRP|PRP)/) {
210		$type = "12000";
211	    } elsif ($proc =~ /1240[48]\/(GRP|PRP)/ || $proc =~ /1241[06]\/(GRP|PRP)/) {
212		$type = "12400";
213            } elsif ($proc =~ /ASR9K/) {
214                $_ = <$INPUT>;
215                $type = $proc;
216                if (/(ASR\S+)\s+(\SC)\s+Chassis/) {
217                    $proc = $1;
218                } elsif (/(ASR\S+)\s+Chassis/) {
219                    $proc = $1;
220                } elsif (/(ASR)\s+(9\d+)\s+.*\s+Chassis with/) {
221		    tr/\015//d;
222                    chomp;
223                    $proc = $_;
224                } elsif (/(ASR)\s+(9\S+)\s+(\SC)\s+Chassis/) {
225                    $proc = "ASR-". $2;
226                } elsif (/(ASR)\s+(9\S+)\s+Chassis/) {
227                    $proc = "ASR-". $2;
228                } elsif (/(ASR\s+9922)/) {
229                    $proc = "ASR-9922";
230                }
231                $_ = "";
232	    } else {
233		$type = $proc;
234	    }
235
236	    print STDERR "TYPE = $type\n" if ($debug);
237	    ProcessHistory("COMMENTS","keysort","A1",
238		"!Chassis type:$slave $proc - a $type $device\n");
239	    ProcessHistory("COMMENTS","keysort","B1",
240		"!Memory:$slave main $mem\n");
241	    if (defined($cpu)) {
242		ProcessHistory("COMMENTS","keysort","A3",
243			       "!CPU:$slave $cpu$_$slaveslot\n");
244	    }
245	    next;
246	} elsif (/^cisco (\S+(?:\sseries)?)\s+(?:\(([^)]*)\)\s+processor)/i) {
247	    $proc = $1;
248	    my($cpu) = $2;
249	    my($device) = "router";
250
251	    print STDERR "TYPE = $type\n" if ($debug);
252	    ProcessHistory("COMMENTS","keysort","A1",
253		"!Chassis type: $proc - a $proc $device\n");
254	    if (defined($cpu) && length($cpu)) {
255		ProcessHistory("COMMENTS","keysort","A3",
256			       "!CPU:$cpu\n");
257	    }
258	}
259	/^(\d+[kK]) bytes of (non-volatile|NVRAM)/ &&
260	    ProcessHistory("COMMENTS","keysort","B3",
261		"!Memory: nvram $1\n") && next;
262	/^(\d+[kK]) bytes of flash memory/ &&
263	    ProcessHistory("COMMENTS","keysort","B5","!Memory: flash $1\n") &&
264	    next;
265	/^(\d+[kK]) bytes of .*flash partition/ &&
266	    ProcessHistory("COMMENTS","keysort","B6",
267		"!Memory: flash partition $1\n") && next;
268	/^(\d+[kK]) bytes of Flash internal/ &&
269	    ProcessHistory("COMMENTS","keysort","B4",
270		"!Memory: bootflash $1\n") && next;
271	if (/^(\d+[kK]) bytes of (Flash|ATA)?.*PCMCIA .*(slot|disk) ?(\d)/i) {
272	    ProcessHistory("COMMENTS","keysort","B7",
273		"!Memory: pcmcia $2 $3$4 $1\n");
274	    next;
275	}
276	if (/^(\d+[kK]) bytes of (slot|disk)(\d)/i) {
277	    ProcessHistory("COMMENTS","keysort","B7",
278		"!Memory: pcmcia $2$3 $1\n");
279	    next;
280	}
281	if (/^WARNING/) {
282	    if (!defined($I0)) {
283		$I0 = 1;
284		ProcessHistory("COMMENTS","keysort","I0","!\n");
285	    }
286	    ProcessHistory("COMMENTS","keysort","I1","! $_");
287	}
288	if (/^Configuration register is (.*)$/) {
289	    $config_register = $1;
290	    next;
291	}
292	if (/^Configuration register on node \S+ is (.*)$/) {
293	    $config_register = $1 if (length($config_register) < 1);
294	    next;
295	}
296    }
297    return(0);
298}
299
300# This routine parses "admin show diag".
301sub AdminShowDiag {
302    my($INPUT, $OUTPUT, $cmd) = @_;
303    print STDERR "    In AdminShowDiag: $_" if ($debug);
304
305    while (<$INPUT>) {
306	tr/\015//d;
307	last if (/^$prompt/);
308	next if (/^(\s*|\s*$cmd\s*)$/);
309	# filter the bloody cmd-line timestamp
310	next if (/^\w{3} \w{3,4} {1,3}\d{1,2} {1,2}\d{1,2}:\d+:\d+\.\d+ \S+$/);
311	return(1) if (/Line has invalid autocommand /);
312	return(1) if (/(Invalid (input|command) detected|Type help or )/i);
313	next if (/^(\e\[.\d+h)?sysadmin-vm:[^#]+# /i);			# NCS
314	return(1) if (/^(-+\^|syntax error: unknown argument)$/i);	# NCS
315	return(-1) if (/command authorization failed/i);
316
317	# skip blank lines
318	/^$/ && next;
319	# NCS junk
320	next if (/^\S+ connected from 127.0.0.1 using console /);
321	next if (/# terminal length 0$/);
322
323	s/^NODE //;
324	# wtf are these?
325	next if (/(New Deviation|UDI_VID|Board State)/);
326	# skip insertion time
327	next if (/insertion time/i);
328	# skip board h/w revision junk
329	next if (/^(\s{2}board |\s{3,})/i);
330
331	ProcessHistory("SLOT","","","!$_");
332    }
333    ProcessHistory("SLOT","","","!\n");
334    return(0);
335}
336
337# This routine parses "admin show running".
338sub AdminShowRunning {
339    my($INPUT, $OUTPUT, $cmd) = @_;
340    print STDERR "    In ShowRunning: $_" if ($debug);
341
342    while (<$INPUT>) {
343	tr/\015//d;
344	last if (/^$prompt/);
345	next if (/^(\s*|\s*$cmd\s*)$/);
346	# filter the bloody cmd-line timestamp
347	next if (/^\w{3} \w{3,4} {1,3}\d{1,2} {1,2}\d{1,2}:\d+:\d+\.\d+ \S+$/);
348	return(1) if (/Line has invalid autocommand /);
349	return(1) if (/(Invalid (input|command) detected|Type help or )/i);
350	next if (/^(\e\[.\d+h)?sysadmin-vm:[^#]+# /i);			# NCS
351	return(1) if (/^(-+\^|syntax error: unknown argument)$/i);	# NCS
352	return(-1) if (/command authorization failed/i);
353
354	# skip blank lines
355	/^$/ && next;
356	# NCS junk
357	next if (/^\S+ connected from 127.0.0.1 using console /);
358	next if (/# terminal length 0$/);
359
360	/^building configuration/i && next;
361	if (/^(\s*secret) / && $filter_pwds >= 2) {
362	    ProcessHistory("ASR","","","!$1 <removed>\n");
363	    next;
364	}
365
366	ProcessHistory("ASR","","","!$_");
367
368	/^end$/ && last;
369    }
370    ProcessHistory("ASR","","","!\n");
371    return(0);
372}
373
374# This routine parses "show diag".
375sub ShowDiag {
376    my($INPUT, $OUTPUT, $cmd) = @_;
377    print STDERR "    In ShowDiag: $_" if ($debug);
378
379    while (<$INPUT>) {
380	tr/\015//d;
381	last if (/^$prompt/);
382	next if (/^(\s*|\s*$cmd\s*)$/);
383	# filter the bloody cmd-line timestamp
384	next if (/^\w{3} \w{3,4} {1,3}\d{1,2} {1,2}\d{1,2}:\d+:\d+\.\d+ \S+$/);
385	return(1) if (/Line has invalid autocommand /);
386	return(1) if (/(Invalid (input|command) detected|Type help or )/i);
387	next if (/^(\e\[.\d+h)?sysadmin-vm:[^#]+# /i);			# NCS
388	return(1) if (/^(-+\^|syntax error: unknown argument)$/i);	# NCS
389	return(-1) if (/command authorization failed/i);
390
391	# skip blank lines
392	/^$/ && next;
393	# NCS junk
394	next if (/^\S+ connected from 127.0.0.1 using console /);
395	next if (/# terminal length 0$/);
396
397	s/^NODE //;
398	# wtf are these?
399	next if (/(New Deviation|UDI_VID|Board State)/);
400
401	ProcessHistory("SLOT","","","!$_");
402    }
403    ProcessHistory("SLOT","","","!\n");
404    return(0);
405}
406
407
408# This routine parses "admin show hw-module fpd location all" and
409# "show hw-module fpd" (eXR/eXR-lite).
410sub ShowHWfpd {
411    my($INPUT, $OUTPUT, $cmd) = @_;
412    print STDERR "    In ShowRunning: $_" if ($debug);
413
414    while (<$INPUT>) {
415	tr/\015//d;
416	last if (/^$prompt/);
417	next if (/^(\s*|\s*$cmd\s*)$/);
418	# filter the bloody cmd-line timestamp
419	next if (/^\w{3} \w{3,4} {1,3}\d{1,2} {1,2}\d{1,2}:\d+:\d+\.\d+ \S+$/);
420	return(1) if (/Line has invalid autocommand /);
421	return(1) if (/(Invalid (input|command) detected|Type help or )/i);
422	next if (/^(\e\[.\d+h)?sysadmin-vm:[^#]+# /i);			# NCS
423	return(1) if (/^(-+\^|syntax error: unknown argument)$/i);	# NCS
424	return(-1) if (/command authorization failed/i);
425
426	# skip blank lines
427	/^$/ && next;
428
429	# NCS junk
430	next if (/^\S+ connected from 127.0.0.1 using console /);
431	next if (/# terminal length 0$/);
432
433	/^notes:/i && last;
434
435	# remove trailing WS
436	s/\s+$/\n/;
437
438	ProcessHistory("FPD","","","!$_");
439    }
440    ProcessHistory("FPD","","","!\n");
441    return(0);
442}
443
444# This routine parses "show redundancy"
445sub ShowRedundancy {
446    my($INPUT, $OUTPUT, $cmd) = @_;
447    my($slave, $slaveslot);
448    print STDERR "    In ShowRedundancy: $_" if ($debug);
449
450TOP: while (<$INPUT>) {
451	tr/\015//d;
452	last if (/^$prompt/);
453	next if (/^(\s*|\s*$cmd\s*)$/);
454	# filter the bloody cmd-line timestamp
455	next if (/^\w{3} \w{3,4} {1,3}\d{1,2} {1,2}\d{1,2}:\d+:\d+\.\d+ \S+$/);
456	return(1) if (/Line has invalid autocommand /);
457	return(1) if (/(Invalid (input|command) detected|Type help or )/i);
458	next if (/^(\e\[.\d+h)?sysadmin-vm:[^#]+# /i);			# NCS
459	return(1) if (/^(-+\^|syntax error: unknown argument)$/i);	# NCS
460
461	# filter reload/boot info
462	if (/^reload and boot info/i) {
463	    while (<$INPUT>) {
464		tr/\015//d;
465		last TOP if (/^$prompt/);
466		last if (/^\s*$/);
467	    }
468	}
469	ProcessHistory("COMMENTS","keysort","F3", "!$_");
470    }
471    ProcessHistory("COMMENTS","keysort","F3", "!\n");
472    return(0);
473}
474
475# This routine parses "show install active"
476sub ShowInstallActive {
477    my($INPUT, $OUTPUT, $cmd) = @_;
478    print STDERR "    In ShowInstallActive: $_" if ($debug);
479
480    while (<$INPUT>) {
481	tr/\015//d;
482	last if (/^$prompt/);
483	next if (/^(\s*|\s*$cmd\s*)$/);
484	# filter the bloody cmd-line timestamp
485	next if (/^\w{3} \w{3,4} {1,3}\d{1,2} {1,2}\d{1,2}:\d+:\d+\.\d+ \S+$/);
486	return(1) if (/^\s*\^\s*$/);
487	return(1) if (/Line has invalid autocommand /);
488	return(1) if (/(Invalid (input|command) detected|Type help or )/i);
489	return(1) if (/^(-+\^|syntax error: unknown argument)$/i);	# NCS
490	return(-1) if (/command authorization failed/i);
491	return(-1) if (/install director is not up or ready/i);# temp error
492	return(-1) if (/failed to output install information /i);# temp error
493
494	# FPD upgrade in progress on some hardware, reload/configuration change
495	# on those is not recommended as it might cause HW programming failure
496	# and result in RMA of the hardware.
497	# FPD upgrade has ended.
498	next if (/fpd upgrade has ended/i ||
499		 /fpd upgrade in progress /i ||
500		 /might cause hw programming failure/i ||
501		 /result in rma of the hardware/i);	# useless FPD messages
502
503	ProcessHistory("COMMENTS","keysort","F5","!Image: $_") && next;
504    }
505    ProcessHistory("COMMENTS","keysort","F5","!\n");
506    return(0);
507}
508
509# This routine parses "show memory summary"
510sub ShowMemorySum {
511    my($INPUT, $OUTPUT, $cmd) = @_;
512    print STDERR "    In ShowMemorySum: $_" if ($debug);
513
514    # RP/0/RSP0/CPU0:r00.lab#show memory summary
515    #
516    # node:      node0_RSP0_CPU0
517    # ------------------------------------------------------------------
518    #
519    #  Physical Memory: 27426M total (22146M available)
520    #  Application Memory : 27426M (21819M available)
521    #  Image: 4M (bootram: 0M)
522    #  Reserved: 0M, IOMem: 0M, flashfsys: 0M
523    #  Total shared window: 140M
524    while (<$INPUT>) {
525	tr/\015//d;
526	last if (/^$prompt/);
527	next if (/^(\s*|\s*$cmd\s*)$/);
528	# filter the bloody cmd-line timestamp
529	next if (/^\w{3} \w{3,4} {1,3}\d{1,2} {1,2}\d{1,2}:\d+:\d+\.\d+ \S+$/);
530	return(1) if (/Line has invalid autocommand /);
531	return(1) if (/(Invalid (input|command) detected|Type help or )/i);
532	return(-1) if (/command authorization failed/i);
533
534	/physical memory:\s+(\d+[GMKB])\s+total/i &&
535	    ProcessHistory("COMMENTS","keysort","B1",
536			   "!Memory: main $1\n") && next;
537    }
538    return(0);
539}
540
541# This routine parses "admin show env all"
542sub ShowEnv {
543    my($INPUT, $OUTPUT, $cmd) = @_;
544    # Skip if this is not a 7500, 7200, or 7000.
545    print STDERR "    In ShowEnv: $_" if ($debug);
546
547    while (<$INPUT>) {
548	tr/\015//d;
549	if (/^$prompt/) { $found_env = 1; last};
550	# filter the bloody cmd-line timestamp
551	next if (/^\w{3} \w{3,4} {1,3}\d{1,2} {1,2}\d{1,2}:\d+:\d+\.\d+ \S+$/);
552	next if (/^(\s*|\s*$cmd\s*)$/);
553	next if (/^\w{3} \w{3,4} {1,3}\d{1,2} {1,2}\d{1,2}:\d+:\d+\.\d+ \S+$/);
554	return(1) if (/Line has invalid autocommand /);
555	return(1) if (/(Invalid (input|command) detected|Type help or )/i);
556	next if (/^(\e\[.\d+h)?sysadmin-vm:[^#]+# /i);			# NCS
557	return(1) if (/^(-+\^|syntax error: unknown argument)$/i);	# NCS
558	return(0) if ($found_env);		# Only do this routine once
559	return(-1) if (/command authorization failed/i);
560
561	if (!defined($E0)) {
562	    $E0 = 1;
563	    ProcessHistory("COMMENTS","keysort","E0","!\n");
564	}
565
566	if (/power budget summary for rack/i) {
567	    ProcessHistory("COMMENTS","keysort","E1","!Power: $_");
568	    while (<$INPUT>) {
569		tr/\015//d;
570		goto OUT if (/^$prompt/);
571		last if (/^altitude information/i);
572		ProcessHistory("COMMENTS","keysort","E1","!Power: $_");
573	    }
574	    next;
575	}
576	if (/power shelf \d+:/i) {
577	    my($sep) = 0;
578	    ProcessHistory("COMMENTS","keysort","E1","!Power: $_");
579	    while (<$INPUT>) {
580		tr/\015//d;
581		goto OUT if (/^$prompt/);
582		last if (/^altitude information/i);
583		last if (/^=+$/i && ($sep > 1));
584		$sep++ if (/^=+$/i);
585		if (/^(\s+\S+\s+\S+-(?:AC|DC)\s+)(\S+\s+\S+\s+\S+\s+\S+)(\s+.*)/) {
586		    $_ = sprintf("%s%-". length($2) ."s%s\n", $1, "", $3);
587		}
588		ProcessHistory("COMMENTS","keysort","E1","!Power: $_");
589	    }
590	    next;
591	}
592	/^Power Supply Information$/ && next;
593	/^\s*Power Module\s+Voltage\s+Current$/ && next;
594	/^\s*(Power [^:\n]+)$/ &&
595	    ProcessHistory("COMMENTS","keysort","E1","!Power: $1\n") && next;
596	/^\s*(Lower Power .*)/i &&
597	    ProcessHistory("COMMENTS","keysort","E2","!Power: $1\n") && next;
598	/^\s*(redundant .*)/i &&
599	    ProcessHistory("COMMENTS","keysort","E2","!Power: $1\n") && next;
600	/^\s*(RPS is .*)/i &&
601	    ProcessHistory("COMMENTS","keysort","E2","!Power: $1\n") && next;
602    }
603OUT:
604    ProcessHistory("COMMENTS","keysort","E3","!\n");
605    return(0);
606}
607
608# This routine parses "dir /all ((disk|slot)N|bootflash|nvram):"
609sub DirSlotN {
610    my($INPUT, $OUTPUT, $cmd) = @_;
611    print STDERR "    In DirSlotN: $_" if ($debug);
612
613    my($dev) = (/\s([^\s]+):/);
614
615    while (<$INPUT>) {
616	tr/\015//d;
617	last if (/^$prompt/);
618	next if (/^(\s*|\s*$cmd\s*)$/);
619	# filter the bloody cmd-line timestamp
620	next if (/^\w{3} \w{3,4} {1,3}\d{1,2} {1,2}\d{1,2}:\d+:\d+\.\d+ \S+$/);
621	return(1) if /^\s*\^\s*$/;
622	return(1) if (/Line has invalid autocommand /);
623	return(1) if (/(Invalid (input|command) detected|Type help or )/i);
624	return(1) if (/^(-+\^|syntax error: unknown argument)$/i);	# NCS
625	return(1) if (/(No such device|Error Sending Request)/i);
626	return(1) if (/: path doesnot exists/i);	# NCS
627	return(1) if (/: Path does not exist/i);	# NCS
628	return(1) if (/\%Error: No such file or directory/);
629	return(1) if (/\%Error: No route to host/);
630	return(1) if (/No space information available/);
631	# Corrupt flash
632	/\%Error calling getdents / &&
633	    ProcessHistory("FLASH","","","!Flash: $dev: $_") && next;
634	return(-1) if (/\%Error calling/);
635	return(-1) if (/(: device being squeezed|ATA_Status time out)/i); # busy
636	return(-1) if (/\%Error opening \S+:\S+ \(Device or resource busy\)/i);
637	return(-1) if (/command authorization failed/i);
638	return(1) if (/(Open device \S+ failed|Error opening \S+:)/);
639
640	# Filter dhcp database
641	if (/dhcp_[^. ]*\.txt/) {
642	    next;
643	}
644	# Filter debugging file dlbg.txt & dlbg.txt-1 only on ASR9k w/ XR
645	if ($proc =~ /ASR9K/ && /dlbg\.txt/) {
646	    next;
647	}
648	# filter frequently changing files from IOX bootflash, hardiska,
649	# and nvram
650	if ($dev =~ /(bootflash|disk[0-9]|harddisk|nvram)/) {
651	    if (/\s(\.python-history|aaa|\.bash_history|.sppdc)\s*$/ ||
652	        /\s(ce_switch.log\S*|cisco_support|errmsg_cont)\s*$/ ||
653	        /\s(genstr_cont|temp_cont|temp_cont|temp_static_data)\s*$/ ||
654	        /\s(uptime_cont|volt_cont|volt_hist)\s*$/) {
655		# change
656		# 57          -rw-  23100       <no date>              volt_cont
657		# 614788      drwx  4096        Fri Aug 20 12:06:25 2010  temp_cont
658		# to
659		# -rw-              <no date>              volt_cont
660		# drwx                                        temp_cont
661		if (/\s*\d+\s+(\S+\s+)(\d+)(\s+)(<no date>)(\s+)/) {
662		    my($a, $sz, $c, $dt, $d, $rem) = ($1, $2, $3, $4, $5, $');
663		    my($szl) = length($sz);
664		    my($fmt) = "%s%-". $szl ."s%s%s%s%s";
665		    $_ = sprintf($fmt, $c, $dt, $d, $rem);
666		    ProcessHistory("FLASH","keysort",$rem,"!Flash: $dev: $_");
667		    next;
668		} elsif (/\s*\d+\s+(\S+\s+\d+\s+)(\d+\s+\w+\s+\d+\s+\d+:\d+)/ ||
669		         /\s*\d+\s+(\S+\s+\d+\s+)(\d+\s+\w+\s+\d+\s+\d{4})/) {
670		    # XR >= 6.3; dir disk0:, but harddisk: is diff format.  wtf
671		    # drop fileno size, & date.
672		    # " 8002 drwxr-xr-x 2 4096 Jan 17 15:27 np"
673		    my($perm, $dt, $rem) = ($1, $2, $');
674		    my($dtl) = length($dt);
675		    my($fmt) = "%s%-". $dtl ."s%s";
676		    $_ = sprintf($fmt, $perm, "", $rem);
677		    ProcessHistory("FLASH","keysort",$rem,"!Flash: $dev: $_");
678		    next;
679		} elsif (/\s*\d+\s+(\S+\s+)(\d+)(\s+)(\w+ \w+\s+\d+ \d+:\d+:\d+ \d+)/) {
680		    my($b, $sz, $c, $dt, $rem) = ($1, $2, $3, $4, $');
681		    my($szl, $dtl) = (length($sz), length($dt));
682		    my($fmt) = "%s%-". $szl ."s%s%-". $dtl ."s%s";
683		    $_ = sprintf($fmt, $b, "", $c, "", $rem);
684		    ProcessHistory("FLASH","keysort",$rem,"!Flash: $dev: $_");
685		    next;
686		}
687	    } else {
688		if (/\s*\d+\s+(\S+\s+)(\d+)(\s+)(<no date>\s+)/) {
689		    my($sz, $c, $dt, $d, $rem) = ($1, $2, $3, $4, $');
690		    ProcessHistory("FLASH","keysort",$rem,"!Flash: $dev: $sz$c$dt$d$rem");
691		    next;
692		} elsif (/\s*\d+\s+(\S+\s+\d+\s+)(\d+\s+\w+\s+\d+\s+\d+:\d+)/ ||
693		         /\s*\d+\s+(\S+\s+\d+\s+)(\d+\s+\w+\s+\d+\s+\d{4})/) {
694		    # XR >= 6.3; dir disk0:, but harddisk: is diff format.  wtf
695		    my($perm, $dt, $rem) = ($1, $2, $');
696		    ProcessHistory("FLASH","keysort",$rem,"!Flash: $dev: $perm$dt$rem");
697		    next;
698		} elsif (/\s*\d+\s+(\S+\s+)(\d+)(\s+)(\w+ \w+\s+\d+ \d+:\d+:\d+ \d+)/) {
699		    # XR < 6.3 & etc.
700		    my($b, $sz, $c, $dt, $rem) = ($1, $2, $3, $4, $');
701		    ProcessHistory("FLASH","keysort",$rem,"!Flash: $dev: $b$sz$c$dt$rem");
702		    next;
703		}
704	    }
705	}
706
707	# XR: 822083584 bytes total (821081600 bytes free)
708	# eXR: 990484 kbytes total (813208 kbytes free)
709	if (/^\s*(\d+ k?bytes) total\s+\((\d+ k?bytes) free\)/i) {
710	    ProcessHistory("FLASH","","","!Flash: $dev: " .
711			   diskszsummary($1, $2) . "\n");
712	    next;
713	}
714
715	ProcessHistory("FLASH","","","!Flash: $dev: $_");
716    }
717    ProcessHistory("","","","!\n");
718    return(0);
719}
720
721# This routine parses "admin show variables boot"
722sub ShowBootVar {
723    my($INPUT, $OUTPUT, $cmd) = @_;
724    print STDERR "    In ShowBootVar: $_" if ($debug);
725
726    while (<$INPUT>) {
727	# delete non-ascii chars, except new line
728	tr/ -~\n//cd;
729	last if (/^$prompt/);
730	next if (/\s*$cmd\s*$/);
731	# filter the bloody cmd-line timestamp
732	next if (/^\w{3} \w{3,4} {1,3}\d{1,2} {1,2}\d{1,2}:\d+:\d+\.\d+ \S+$/);
733	return(1) if (/^\s*\^\s*$/);
734	return(1) if (/Line has invalid autocommand /);
735	return(1) if (/(Invalid (input|command) detected|Type help or )/i);
736	next if (/^(\e\[.\d+h)?sysadmin-vm:[^#]+# /i);			# NCS
737	return(1) if (/^(-+\^|syntax error: unknown argument)$/i);	# NCS
738	return(1) if (/Ambiguous command/i);
739	return(1) if (/(Open device \S+ failed|Error opening \S+:)/);
740	return(-1) if (/command authorization failed/i);
741
742	# skip blank lines
743	next if (/^\s*$/);
744	# NCS junk
745	next if (/^\S+ connected from 127.0.0.1 using console /);
746	next if (/# terminal length 0$/);
747
748	ProcessHistory("COMMENTS", "keysort", "C30", "! $_");
749    }
750    ProcessHistory("COMMENTS", "keysort", "C39", "!\n");
751
752    return(0);
753}
754
755# This routine parses "show controllers"
756sub ShowContAll {
757    my($INPUT, $OUTPUT, $cmd) = @_;
758    my($INT);
759    print STDERR "    In ShowContAll: $_" if ($debug);
760
761    while (<$INPUT>) {
762	tr/\015//d;
763	last if (/^$prompt/);
764	next if (/^(\s*|\s*$cmd\s*)$/);
765	# filter the bloody cmd-line timestamp
766	next if (/^\w{3} \w{3,4} {1,3}\d{1,2} {1,2}\d{1,2}:\d+:\d+\.\d+ \S+$/);
767	return(1) if (/(Invalid (input|command) detected|Type help or )/i);
768	return(1) if (/^(-+\^|syntax error: unknown argument)$/i);	# NCS
769	# return(1) if ($type =~ /^(12[40]|7[05])/);
770	return(-1) if (/command authorization failed/i);
771
772	if (/^Interface ([^ \n(]*)/) { $INT = "$1, "; next; }
773	/^(BRI unit \d)/ &&
774	    ProcessHistory("INT","","","!Interface: $1\n") && next;
775	/^LANCE unit \d, NIM/ &&
776	    ProcessHistory("INT","","","!Interface: $_") && next;
777	/^(LANCE unit \d)/ &&
778	    ProcessHistory("INT","","","!Interface: $1\n") && next;
779	/(Media Type is \S+),/ &&
780	    ProcessHistory("INT","","","!\t$1\n");
781	    if (/(M\dT[^ :]*:) show controller:$/) {
782		my($ctlr) = $1;
783		$_ = <$INPUT>; tr/\015//d; s/ subunit \d,//;
784		ProcessHistory("INT","","","!Interface: $ctlr $_");
785	    }
786	if (/^(\S+) : show controller:$/) {
787	    my($ctlr) = $1;
788	    $_ = <$INPUT>; tr/\015//d; s/ subunit \d,//;
789	    ProcessHistory("INT","","","!Interface: $ctlr: $_");
790	}
791	/^(HD unit \d), idb/ &&
792	    ProcessHistory("INT","","","!Interface: $1\n") && next;
793	/^HD unit \d, NIM/ &&
794	    ProcessHistory("INT","","","!Interface: $_") && next;
795	/^buffer size \d+  HD unit \d, (.*)/ &&
796	    ProcessHistory("INT","","","!\t$1\n") && next;
797	/^AM79970 / && ProcessHistory("INT","","","!Interface: $_") && next;
798	/^buffer size \d+  (Universal Serial: .*)/ &&
799	    ProcessHistory("INT","","","!\t$1\n") && next;
800	/^Hardware is (.*)/ &&
801	    ProcessHistory("INT","","","!Interface: $INT$1\n") && next;
802	/^(QUICC Serial unit \d),/ &&
803	    ProcessHistory("INT","","","!$1\n") && next;
804	/^QUICC Ethernet .*/ &&
805	    ProcessHistory("INT","","","!$_") && next;
806	/^DTE .*\.$/ &&
807	    ProcessHistory("INT","","","!\t$_") && next;
808	/^(cable type :.*),/ &&
809	    ProcessHistory("INT","","","!\t$1\n") && next;
810	/^(.* cable.*), received clockrate \d+$/ &&
811	    ProcessHistory("INT","","","!\t$1\n") && next;
812	/^.* cable.*$/ &&
813	    ProcessHistory("INT","","","!\t$_") && next;
814    }
815    return(0);
816}
817
818# This routine parses "show debug"
819sub ShowDebug {
820    my($INPUT, $OUTPUT, $cmd) = @_;
821    print STDERR "    In ShowDebug: $_" if ($debug);
822    my($lines) = 0;
823
824    while (<$INPUT>) {
825	tr/\015//d;
826	last if (/^$prompt/);
827	next if (/^(\s*|\s*$cmd\s*)$/);
828	# filter the bloody cmd-line timestamp
829	next if (/^\w{3} \w{3,4} {1,3}\d{1,2} {1,2}\d{1,2}:\d+:\d+\.\d+ \S+$/);
830	return(1) if (/Line has invalid autocommand /);
831	return(1) if (/(Invalid (input|command) detected|Type help or )/i);
832	return(1) if (/^(-+\^|syntax error: unknown argument)$/i);	# NCS
833	return(-1) if (/command authorization failed/i);
834
835	/^No matching debug flags set$/ && next;
836	/^No debug flags set$/ && next;
837	ProcessHistory("COMMENTS","keysort","J1","!DEBUG: $_");
838	$lines++;
839    }
840    if ($lines) {
841	ProcessHistory("COMMENTS","keysort","J0","!\n");
842    }
843    return(0);
844}
845
846# This routine parses "admin show install active"
847sub ShowInstallSummary {
848    my($INPUT, $OUTPUT, $cmd) = @_;
849    print STDERR "    In ShowInstallSummary: $_" if ($debug);
850
851    while (<$INPUT>) {
852	# delete non-ascii chars, except new line
853	tr/ -~\n//cd;
854	last if (/^$prompt/);
855	next if (/\s*$cmd\s*$/);
856	# filter the bloody cmd-line timestamp
857	next if (/^\w{3} \w{3,4} {1,3}\d{1,2} {1,2}\d{1,2}:\d+:\d+\.\d+ \S+$/);
858	return(1) if (/^\s*\^\s*$/);
859	return(1) if (/Line has invalid autocommand /);
860	return(1) if (/(Invalid (input|command) detected|Type help or )/i);
861	next if (/^(\e\[.\d+h)?sysadmin-vm:[^#]+# /i);			# NCS
862	return(1) if (/^(-+\^|syntax error: unknown argument)$/i);	# NCS
863	return(1) if (/Ambiguous command/i);
864	return(1) if (/(Open device \S+ failed|Error opening \S+:)/);
865	return(-1) if (/command authorization failed/i);
866	return(-1) if (/inventory summary cannot be output/i);	# temp error
867	return(-1) if (/currently affected by install operation/i);# temp error
868	return(-1) if (/install director is not up or ready/i);# temp error
869
870	# skip blank lines
871	next if (/^\s*$/);
872	# NCS junk
873	next if (/^\S+ connected from 127.0.0.1 using console /);
874	next if (/# terminal length 0$/);
875
876	ProcessHistory("COMMENTS", "keysort", "C15", "!Image: $_");
877    }
878    ProcessHistory("COMMENTS", "keysort", "C10", "!\n");
879    ProcessHistory("COMMENTS", "keysort", "C19", "!\n");
880
881    return(0);
882}
883
884# This routine parses "show inventory".
885sub ShowInventory {
886    my($INPUT, $OUTPUT, $cmd) = @_;
887    print STDERR "    In ShowInventory: $_" if ($debug);
888
889    while (<$INPUT>) {
890	# delete non-ascii chars, except new line
891	tr/ -~\n//cd;
892	return if (/^\s*\^$/);
893	last if (/^$prompt/);
894	next if (/^(\s*|\s*$cmd\s*)$/);
895	# filter the bloody cmd-line timestamp
896	next if (/^\w{3} \w{3,4} {1,3}\d{1,2} {1,2}\d{1,2}:\d+:\d+\.\d+ \S+$/);
897	return(1) if (/Line has invalid autocommand /);
898	return(1) if (/(Invalid (input|command) detected|Type help or )/i);
899	next if (/^(\e\[.\d+h)?sysadmin-vm:[^#]+# /i);			# NCS
900	return(1) if (/^(-+\^|syntax error: unknown argument)$/i);	# NCS
901	return(-1) if (/command authorization failed/i);
902
903	# NCS junk
904	next if (/^\S+ connected from 127.0.0.1 using console /);
905	next if (/# terminal length 0$/);
906
907	# remove spaces after quotes
908	s/\"\s+/\"/g;
909	if (/^(name: "[^"]*",) (descr: "[^"]+")/i) {
910	    ProcessHistory("INVENTORY","","", sprintf("!%-30s %s\n", $1, $2));
911	    next;
912	}
913
914	# XR flavor:
915	# NAME: "module 0/RSP0/CPU0", DESCR: "ASR9K Route Switch Processor with 880G/slot Fabric and 32GB"
916	# PID: A9K-RSP880-SE, VID: V01, SN: FOC1911VC2S
917	#
918	# eXR flavor:
919	# NAME: "0/RSP0-Motherboard", DESCR: "Motherboard Module"
920	# PID: N/A               , VID: N/A , SN: N/A
921	# split PID/VID/SN line
922	if (/^ ?PID: (\S*)\s*, VID: (\S*)\s*,\s* SN: (\S*)\s*$/) {
923	    my($pid,$vid,$sn) = ($1, $2, $3);
924	    my($entries) = "";
925	    # filter <empty>, "0x" and "N/A" lines
926	    if ($pid !~ /^(|0x|N\/A)$/) {
927		$entries .= "!PID: $pid\n";
928	    }
929	    if ($vid !~ /^(|0x|N\/A)$/) {
930		$entries .= "!VID: $vid\n";
931	    }
932	    if ($sn !~ /^(|0x|N\/A)$/) {
933		$entries .= "!SN: $sn\n";
934	    }
935	    ProcessHistory("INVENTORY","","", "$entries");
936	    next;
937	}
938	ProcessHistory("INVENTORY","","","!$_");
939    }
940    ProcessHistory("INVENTORY","","","!\n");
941
942    return(0);
943}
944
945# This routine parses "admin show license" & "admin show license udi"
946sub ShowLicense {
947    my($INPUT, $OUTPUT, $cmd) = @_;
948    print STDERR "    In ShowLicense: $_" if ($debug);
949
950    while (<$INPUT>) {
951	tr/\015//d;
952	# delete non-ascii chars, except new line
953	last if (/^$prompt/);
954	next if (/\s*$cmd\s*$/);
955	# filter the bloody cmd-line timestamp
956	next if (/^\w{3} \w{3,4} {1,3}\d{1,2} {1,2}\d{1,2}:\d+:\d+\.\d+ \S+$/);
957	return(1) if (/^\s*\^\s*$/);
958	next if (/^-+\^$/i);	# NCS cmd-line error pointer
959	return(1) if (/Line has invalid autocommand /);
960	return(1) if (/(Invalid (input|command) detected|Type help or )/i);
961	next if (/^(\e\[.\d+h)?sysadmin-vm:[^#]+# /i);			# NCS
962	return(1) if (/^syntax error: element does not exist$/i);	# NCS
963	return(1) if (/^syntax error: unknown argument$/i);		# NCS
964	return(1) if (/Ambiguous command/i);
965	return(1) if (/(Open device \S+ failed|Error opening \S+:)/);
966	return(-1) if (/command authorization failed/i);
967
968	# show lic on o/s w/o licenses
969	s/info\s*: //;
970
971	# file feature status line (ASR9k, others?)
972	next if (/\s*status: /i);
973
974	# skip blank lines
975	next if (/^\s*$/);
976	# NCS junk
977	next if (/^\S+ connected from 127.0.0.1 using console /);
978	next if (/# terminal length 0$/);
979
980	# filter the BS from license broker
981	next if (/(renewal attempt|communication attempt):/i);
982	next if (/next registration attempt:/i);		# timestamp
983	next if (/(period used:|requested time:)/i);		# show lic feature
984	if (/(^\s*(evaluation )?period (left|remaining):\s*)\d+/i) {
985	    ProcessHistory("COMMENTS","keysort","LICENSE","! $1<limited>\n");
986	    next;
987	}
988
989	# drop license counts
990	next if (/license usage:/i);
991	if (/(.*)count status/i) {
992	    my($hdr) = $1;
993	    my($len) = length($hdr);
994
995	    $hdr =~ s/\s*$//;
996	    ProcessHistory("COMMENTS", "keysort", "C20", "! $hdr\n");
997	    while (<$INPUT>) {
998		tr/\015//d;
999		return(0) if (/^$prompt/);
1000
1001		s/^(.{1,$len}).*/$1/;
1002		ProcessHistory("COMMENTS", "keysort", "C20", "! $_");
1003	    }
1004	    next;
1005	}
1006	ProcessHistory("COMMENTS", "keysort", "C20", "! $_");
1007    }
1008    ProcessHistory("COMMENTS", "keysort", "C20", "!\n");
1009
1010    return(0);
1011}
1012
1013# This routine parses "show rpl maximum"
1014sub ShowRPL {
1015    my($INPUT, $OUTPUT, $cmd) = @_;
1016    print STDERR "    In ShowRPL: $_" if ($debug);
1017
1018    while (<$INPUT>) {
1019	tr/\015//d;
1020	last if (/^$prompt/);
1021	next if (/^(\s*|\s*$cmd\s*)$/);
1022	# filter the bloody cmd-line timestamp
1023	next if (/^\w{3} \w{3,4} {1,3}\d{1,2} {1,2}\d{1,2}:\d+:\d+\.\d+ \S+$/);
1024	return(1) if /^\s*\^\s*$/;
1025	return(1) if (/Line has invalid autocommand /);
1026	return(1) if (/(Invalid (input|command) detected|Type help or )/i);
1027	return(1) if (/^(-+\^|syntax error: unknown argument)$/i);	# NCS
1028	return(1) if (/Ambiguous command/i);
1029	return(-1) if (/command authorization failed/i);
1030
1031	ProcessHistory("COMMENTS","keysort","RPLMAX","! $_");
1032    }
1033    ProcessHistory("COMMENTS","keysort","RPLMAX","!\n");
1034    return(0);
1035}
1036
1037# This routine parses "show vlan"
1038sub ShowVLAN {
1039    my($INPUT, $OUTPUT, $cmd) = @_;
1040    print STDERR "    In ShowVLAN: $_" if ($debug);
1041
1042    while (<$INPUT>) {
1043	tr/\015//d;
1044	last if (/^$prompt/);
1045	next if (/^(\s*|\s*$cmd\s*)$/);
1046	# filter the bloody cmd-line timestamp
1047	next if (/^\w{3} \w{3,4} {1,3}\d{1,2} {1,2}\d{1,2}:\d+:\d+\.\d+ \S+$/);
1048	return(1) if /^\s*\^\s*$/;
1049	return(1) if (/Line has invalid autocommand /);
1050	return(1) if (/(Invalid (input|command) detected|Type help or )/i);
1051	return(1) if (/^(-+\^|syntax error: unknown argument)$/i);	# NCS
1052	return(1) if (/Ambiguous command/i);
1053	return(-1) if (/command authorization failed/i);
1054
1055	ProcessHistory("COMMENTS","keysort","IO","!VLAN: $_");
1056    }
1057    ProcessHistory("COMMENTS","keysort","IO","!\n");
1058    return(0);
1059}
1060
1061# This routine processes a "write term"
1062sub WriteTerm {
1063    my($INPUT, $OUTPUT, $cmd) = @_;
1064    print STDERR "    In WriteTerm: $_" if ($debug);
1065    my($lineauto,$comment,$linecnt) = (0,0,0);
1066
1067    while (<$INPUT>) {
1068REPEAT:	tr/\015//d;
1069	last if (/^$prompt/);
1070	# filter the bloody cmd-line timestamp
1071	next if (/^\w{3} \w{3,4} {1,3}\d{1,2} {1,2}\d{1,2}:\d+:\d+\.\d+ \S+$/);
1072	return(1) if (!$linecnt && /^\s+\^\s*$/);
1073	return(1) if (/Line has invalid autocommand /);
1074	return(1) if (/(Invalid (input|command) detected|Type help or )/i);
1075	return(1) if (/^(-+\^|syntax error: unknown argument)$/i);	# NCS
1076	return(1) if (/\%Error: No such file or directory/);
1077	return(0) if ($found_end);		# Only do this routine once
1078	return(-1) if (/command authorization failed/i);
1079	return(-1) if (/% ?configuration buffer full/i);
1080	/^! no configuration change since last restart/i && next;
1081	# skip emtpy lines at the beginning
1082	if (!$linecnt && /^\s*$/) {
1083	    next;
1084	}
1085	if (!$linecnt && defined($config_register)) {
1086	    ProcessHistory("","","", "!\nconfig-register $config_register\n");
1087	}
1088
1089	# NCS junk
1090	next if (/^\S+ connected from 127.0.0.1 using console /);
1091	next if (/# terminal length 0$/);
1092
1093	/Non-Volatile memory is in use/ && return(-1); # NvRAM is locked
1094	/% Configuration buffer full, / && return(-1); # buffer is in use
1095	$linecnt++;
1096	$lineauto = 0 if (/^[^ ]/);
1097	# skip the crap
1098	if (/^(##+|(building|current) configuration)/i) {
1099	    while (<$INPUT>) {
1100		next if (/^Current configuration\s*:/i);
1101		next if (/^:/);
1102		next if (/^([%!].*|\s*)$/);
1103		next if (/^ip add.*ipv4:/);	# band-aid for 3620 12.0S
1104		last;
1105	    }
1106	    tr/\015//d;
1107	}
1108	# some versions have other crap mixed in with the bits in the
1109	# block above
1110	/^! (Last configuration|NVRAM config last)/ && next;
1111	# and for the ASA
1112	/^: (Written by \S+ at|Saved)/ && next;
1113
1114	# skip consecutive comment lines to avoid oscillating extra comment
1115	# line on some access servers.  grrr.
1116	if (/^!\s*$/) {
1117	    next if ($comment);
1118	    ProcessHistory("","","",$_);
1119	    $comment++;
1120	    next;
1121	}
1122	$comment = 0;
1123
1124	# Dog gone Cool matches to process the rest of the config
1125	/^tftp-server flash /   && next; # kill any tftp remains
1126	/^ntp clock-period /    && next; # kill ntp clock-period
1127	/^ length /		&& next; # kill length on serial lines
1128	/^ width /		&& next; # kill width on serial lines
1129	$lineauto = 1 if /^ modem auto/;
1130	/^ speed / && $lineauto	&& next; # kill speed on serial lines
1131	/^ clockrate /		&& next; # kill clockrate on serial interfaces
1132	if (/^(enable )?(password|passwd)( level \d+)? / && $filter_pwds >= 1) {
1133	    ProcessHistory("ENABLE","","","!$1$2$3 <removed>\n");
1134	    next;
1135	}
1136	if (/^(enable secret) / && $filter_pwds >= 2) {
1137	    ProcessHistory("ENABLE","","","!$1 <removed>\n");
1138	    next;
1139	}
1140	if (/^username (\S+)(\s.*)? secret /) {
1141	    if ($filter_pwds >= 2) {
1142		ProcessHistory("USER","keysort","$1",
1143			       "!username $1$2 secret <removed>\n");
1144	    } else {
1145		ProcessHistory("USER","keysort","$1","$_");
1146	    }
1147	    next;
1148	}
1149	if (/^username (\S+)(\s.*)? password ((\d) \S+|\S+)/) {
1150	    if ($filter_pwds >= 2) {
1151		ProcessHistory("USER","keysort","$1",
1152			       "!username $1$2 password <removed>\n");
1153	    } elsif ($filter_pwds >= 1 && $4 ne "5"){
1154		ProcessHistory("USER","keysort","$1",
1155			       "!username $1$2 password <removed>\n");
1156	    } else {
1157		ProcessHistory("USER","keysort","$1","$_");
1158	    }
1159	    next;
1160	}
1161	if (/^( set session-key (in|out)bound ah \d+ )/ && $filter_pwds >= 1) {
1162	    ProcessHistory("","","","!$1<removed>\n");
1163	    next;
1164	}
1165	if (/^( set session-key (in|out)bound esp \d+ (authenticator|cypher) )/
1166	    && $filter_pwds >= 1) {
1167	    ProcessHistory("","","","!$1<removed>\n");
1168	    next;
1169	}
1170	if (/^(\s*)password / && $filter_pwds >= 1) {
1171	    ProcessHistory("LINE-PASS","","","!$1password <removed>\n");
1172	    next;
1173	}
1174	if (/^(\s*)secret / && $filter_pwds >= 2) {
1175	    ProcessHistory("LINE-PASS","","","!$1secret <removed>\n");
1176	    next;
1177	}
1178	if (/^\s*neighbor (\S*) password / && $filter_pwds >= 1) {
1179	    ProcessHistory("","","","! neighbor $1 password <removed>\n");
1180	    next;
1181	}
1182	if (/^(ppp .* password) 7 .*/ && $filter_pwds >= 1) {
1183	    ProcessHistory("","","","!$1 <removed>\n"); next;
1184	}
1185	if (/^(ip ftp password) / && $filter_pwds >= 1) {
1186	    ProcessHistory("","","","!$1 <removed>\n"); next;
1187	}
1188	if (/^( ip ospf authentication-key) / && $filter_pwds >= 1) {
1189	    ProcessHistory("","","","!$1 <removed>\n"); next;
1190	}
1191	# isis passwords appear to be completely plain-text
1192	if (/^\s+isis password (\S+)( .*)?/ && $filter_pwds >= 1) {
1193	    ProcessHistory("","","","!isis password <removed>$2\n"); next;
1194	}
1195	if (/^\s+(domain-password|area-password) (\S+)( .*)?/
1196							&& $filter_pwds >= 1) {
1197	    ProcessHistory("","","","!$1 <removed>$3\n"); next;
1198	}
1199	# this is reversable, despite 'md5' in the cmd
1200	if (/^( ip ospf message-digest-key \d+ md5) / && $filter_pwds >= 1) {
1201	    ProcessHistory("","","","!$1 <removed>\n"); next;
1202	}
1203	# this is also reversable, despite 'md5 encrypted' in the cmd
1204	if (/^(  message-digest-key \d+ md5 (7|encrypted)) /
1205	    && $filter_pwds >= 1) {
1206	    ProcessHistory("","","","!$1 <removed>\n"); next;
1207	}
1208	if (/^((crypto )?isakmp key) \S+ / && $filter_pwds >= 1) {
1209	    ProcessHistory("","","","!$1 <removed> $'"); next;
1210	}
1211	# filter HSRP passwords
1212	if (/^(\s+standby \d+ authentication) / && $filter_pwds >= 1) {
1213	    ProcessHistory("","","","!$1 <removed>\n"); next;
1214	}
1215	# this appears in "measurement/sla" images
1216	if (/^(\s+key-string \d?)/ && $filter_pwds >= 1) {
1217	    ProcessHistory("","","","!$1 <removed>\n"); next;
1218	}
1219	if (/^( l2tp tunnel \S+ password)/ && $filter_pwds >= 1) {
1220	    ProcessHistory("","","","!$1 <removed>\n"); next;
1221	}
1222	# i am told these are plain-text on the PIX
1223	if (/^(vpdn username (\S+) password)/) {
1224	    if ($filter_pwds >= 1) {
1225		ProcessHistory("USER","keysort","$2","!$1 <removed>\n");
1226	    } else {
1227		ProcessHistory("USER","keysort","$2","$_");
1228	    }
1229	    next;
1230	}
1231	# ASA/PIX keys in more system:running-config
1232	if (/^( pre-shared-key | key |failover key ).*/ && $filter_pwds >= 1) {
1233	    ProcessHistory("","","","!$1 <removed> $'"); next;
1234	}
1235	if (/(\s+ldap-login-password )\S+(.*)/ && $filter_pwds >= 1) {
1236	    ProcessHistory("","","","!$1 <removed> $'"); next;
1237	}
1238	#
1239	if (/^( cable shared-secret )/ && $filter_pwds >= 1) {
1240	    ProcessHistory("","","","!$1 <removed>\n");
1241	    next;
1242	}
1243	/fair-queue individual-limit/ && next;
1244	# sort ip explicit-paths.
1245	if (/^ip explicit-path name (\S+)/) {
1246	    my($key) = $1;
1247	    my($expath) = $_;
1248	    while (<$INPUT>) {
1249		tr/\015//d;
1250		last if (/^$prompt/);
1251		last if (! /^(ip explicit-path name |[ !])/);
1252		if (/^ip explicit-path name (\S+)/) {
1253		    ProcessHistory("EXPATH","keysort","$key","$expath");
1254		    $key = $1;
1255		    $expath = $_;
1256		} else {
1257		    $expath .= $_;
1258		}
1259	    }
1260	    ProcessHistory("EXPATH","keysort","$key","$expath");
1261	}
1262	# sort route-maps
1263	if (/^route-map (\S+)/) {
1264	    my($key) = $1;
1265	    my($routemap) = $_;
1266	    while (<$INPUT>) {
1267		tr/\015//d;
1268		last if (/^$prompt/ || ! /^(route-map |[ !])/);
1269		if (/^route-map (\S+)/) {
1270		    ProcessHistory("ROUTEMAP","keysort","$key","$routemap");
1271		    $key = $1;
1272		    $routemap = $_;
1273		} else {
1274		    $routemap .= $_;
1275		}
1276	    }
1277	    ProcessHistory("ROUTEMAP","keysort","$key","$routemap");
1278	}
1279	# filter out any RCS/CVS tags to avoid confusing local CVS storage
1280	s/\$(Revision|Id):/ $1:/;
1281	# order access-lists
1282	/^access-list\s+(\d\d?)\s+(\S+)\s+(\S+)/ &&
1283	    ProcessHistory("ACL $1 $2","$aclsort","$3","$_") && next;
1284	# order extended access-lists
1285	/^access-list\s+(\d\d\d)\s+(\S+)\s+ip\s+host\s+(\S+)/ &&
1286	    ProcessHistory("EACL $1 $2","$aclsort","$3","$_") && next;
1287	/^access-list\s+(\d\d\d)\s+(\S+)\s+ip\s+(\d\S+)/ &&
1288	    ProcessHistory("EACL $1 $2","$aclsort","$3","$_") && next;
1289	/^access-list\s+(\d\d\d)\s+(\S+)\s+ip\s+any/ &&
1290	    ProcessHistory("EACL $1 $2","$aclsort","0.0.0.0","$_") && next;
1291	# sort ipv{4,6} access-lists
1292	if ($aclfilterseq && /^ipv(4|6) access-list (\S+)\s*$/) {
1293	    my($nlri, $key) = ($1, $2);
1294	    my($seq, $cmd);
1295	    ProcessHistory("ACL $nlri $key","","","$_");
1296	    while (<$INPUT>) {
1297		tr/\015//d;
1298		goto REPEAT if (/^$prompt/ || !/^(\s|\d|!)/);
1299		if (/^\s*!/) {
1300		    ProcessHistory("ACL $nlri $key !", "", "", "$_");
1301		    next;
1302		}
1303		# ipv4 access-list name
1304		#  remark NTP
1305   		#  deny ipv4 host 224.0.1.1 any
1306		#  deny ipv4 239.0.0.0 0.255.255.255 any
1307		#  permit udp any eq 123 any
1308		#  permit ipv4 nnn.nnn.nnn.nnn/nn any
1309		#  permit nnn.nnn.nnn.nnn/nn
1310		# ipv6 access-list name
1311		#  permit ipv6 host 2001:nnnn::nnnn any
1312		#  permit ipv6 2001:nnn::/nnn any
1313		#  permit 2001:nnnn::/64 any
1314		#  permit udp any eq 123 any
1315		my($seq, $cmd, $resid) = ($_ =~ /^\s*(\d+) (\w+) (.+)/);
1316		if ($cmd =~ /(permit|deny)/) {
1317		    my($ip);
1318		    my(@w) = ($resid =~ /(\S+) (\S+) (\S+\s)?(.+)/);
1319		    for (my($i) = 0; $i < $#w; $i++) {
1320			if ($w[$i] eq "any") {
1321			    if ($nlri eq "ipv4") {
1322				$ip = "255.255.255.255/32";
1323			    } else {
1324				$ip = "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128";
1325			    }
1326			    last;
1327			} elsif ($w[$i] =~ /^[:0-9]/ ||
1328				 $2[$i] =~ /^[a-fA-F]{1,4}:/) {
1329			    $ip = $w[$i];
1330			    $ip =~ s/\s+$//;		# trim trailing WS
1331			    last;
1332			}
1333		    }
1334		    ProcessHistory("ACL $nlri $key $cmd", "$aclsort", "$ip",
1335				   " $cmd $resid\n");
1336		} else {
1337		    ProcessHistory("ACL $nlri $key $cmd", "", "",
1338				   " $cmd $resid\n");
1339		}
1340	    }
1341	}
1342	# order arp lists
1343	/^arp\s+(\d+\.\d+\.\d+\.\d+)\s+/ &&
1344	    ProcessHistory("ARP","$aclsort","$1","$_") && next;
1345	/^ip prefix-list\s+(\S+)\s+seq\s+(\d+)\s+(permit|deny)\s+(\d\S+)(.*)/ &&
1346	    ProcessHistory("PACL $1 $3","$aclsort","$4","ip prefix-list $1 $3 $4$5\n")
1347	    && next;
1348	if ($aclfilterseq) {
1349	    /^ip(v4|v6) prefix-list\s+(\S+)\s+seq\s+(\d+)\s+(permit|deny)\s+(\S+)(.*)/ &&
1350		ProcessHistory("PACL $2 $4","$aclsort","$5","ip$1 prefix-list $2 $4 $5$6\n")
1351		&& next;
1352	}
1353	# order logging statements
1354	/^logging (\d+\.\d+\.\d+\.\d+)/ &&
1355	    ProcessHistory("LOGGING","ipsort","$1","$_") && next;
1356	# order/prune snmp-server host statements
1357	# we only prune lines of the form
1358	# snmp-server host a.b.c.d <community>
1359	if (/^snmp-server host (\d+\.\d+\.\d+\.\d+) /) {
1360	    if ($filter_commstr) {
1361		my($ip) = $1;
1362		my($line) = "snmp-server host $ip";
1363		my(@tokens) = split(' ', $');
1364		my($token);
1365		while ($token = shift(@tokens)) {
1366		    if ($token eq 'version') {
1367			$line .= " " . join(' ', ($token, shift(@tokens)));
1368			if ($token eq '3') {
1369			    $line .= " " . join(' ', ($token, shift(@tokens)));
1370			}
1371		    } elsif ($token eq 'vrf') {
1372			$line .= " " . join(' ', ($token, shift(@tokens)));
1373		    } elsif ($token =~ /^(informs?|traps?|(no)?auth)$/) {
1374			$line .= " " . $token;
1375		    } else {
1376			$line = "!$line " . join(' ', ("<removed>",
1377						 join(' ',@tokens)));
1378			last;
1379		    }
1380		}
1381		ProcessHistory("SNMPSERVERHOST","ipsort","$ip","$line\n");
1382	    } else {
1383		ProcessHistory("SNMPSERVERHOST","ipsort","$1","$_");
1384	    }
1385	    next;
1386	}
1387	if (/^(snmp-server community) (\S+)/) {
1388	    if ($filter_commstr) {
1389		ProcessHistory("SNMPSERVERCOMM","keysort","$_",
1390			       "!$1 <removed>$'") && next;
1391	    } else {
1392		ProcessHistory("SNMPSERVERCOMM","keysort","$_","$_") && next;
1393	    }
1394	}
1395	# prune tacacs/radius server keys
1396	if (/^((tacacs|radius)-server\s(\w*[-\s(\s\S+])*\s?key) (\d )?\w+/
1397	    && $filter_pwds >= 1) {
1398	    ProcessHistory("","","","!$1 <removed>$'"); next;
1399	}
1400	# order clns host statements
1401	/^clns host \S+ (\S+)/ &&
1402	    ProcessHistory("CLNS","keysort","$1","$_") && next;
1403	# order alias statements
1404	/^alias / && ProcessHistory("ALIAS","keysort","$_","$_") && next;
1405	# delete ntp auth password - this md5 is a reversable too
1406	if (/^(ntp authentication-key \d+ md5) / && $filter_pwds >= 1) {
1407	    ProcessHistory("","","","!$1 <removed>\n"); next;
1408	}
1409	# order ntp peers/servers
1410	if (/^ntp (server|peer) (\d+)\.(\d+)\.(\d+)\.(\d+)/) {
1411	    my($sortkey) = sprintf("$1 %03d%03d%03d%03d",$2,$3,$4,$5);
1412	    ProcessHistory("NTP","keysort",$sortkey,"$_");
1413	    next;
1414	}
1415	# order ip host statements
1416	/^ip host (\S+) / &&
1417	    ProcessHistory("IPHOST","keysort","$1","$_") && next;
1418	# order ip nat source static statements
1419	/^ip nat (\S+) source static (\S+)/ &&
1420	    ProcessHistory("IP NAT $1","ipsort","$2","$_") && next;
1421	# order atm map-list statements
1422	/^\s+ip\s+(\d+\.\d+\.\d+\.\d+)\s+atm-vc/ &&
1423	    ProcessHistory("ATM map-list","ipsort","$1","$_") && next;
1424	# order ip rcmd lines
1425	/^ip rcmd/ && ProcessHistory("RCMD","keysort","$_","$_") && next;
1426
1427	# system controller
1428	/^syscon address (\S*) (\S*)/ &&
1429	    ProcessHistory("","","","!syscon address $1 <removed>\n") &&
1430	    next;
1431	if (/^syscon password (\S*)/ && $filter_pwds >= 1) {
1432	    ProcessHistory("","","","!syscon password <removed>\n");
1433	    next;
1434	}
1435
1436	/^ *Cryptochecksum:/ && next;
1437
1438	# catch anything that wasnt matched above.
1439	ProcessHistory("","","","$_");
1440	# end of config.
1441	if (/^end$/) {
1442	    $found_end = 1;
1443	    return(0);
1444	}
1445    }
1446
1447    return(0);
1448}
1449
14501;
1451