From 989312339ea2e16579803a48700628c5469e327a Mon Sep 17 00:00:00 2001 From: Tar Committer Date: Mon, 12 Jan 2004 03:17:26 +0000 Subject: Imported from rancid-2.3.rc1.tar.gz. --- bin/lg.cgi.in | 867 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 867 insertions(+) create mode 100644 bin/lg.cgi.in (limited to 'bin/lg.cgi.in') diff --git a/bin/lg.cgi.in b/bin/lg.cgi.in new file mode 100644 index 0000000..ad08c68 --- /dev/null +++ b/bin/lg.cgi.in @@ -0,0 +1,867 @@ +#! @PERLV_PATH@ +## +## $Id: lg.cgi.in,v 1.47 2004/01/11 03:49:13 heas Exp $ +## +## Copyright (C) 1997-2004 by Terrapin Communications, Inc. +## All rights reserved. +## +## This software may be freely copied, modified and redistributed +## without fee for non-commerical purposes provided that this license +## remains intact and unmodified with any RANCID distribution. +## +## There is no warranty or other guarantee of fitness of this software. +## It is provided solely "as is". The author(s) disclaim(s) all +## responsibility and liability with respect to this software's usage +## or its effect upon hardware, computer systems, other software, or +## anything else. +## +## Except where noted otherwise, rancid was written by and is maintained by +## Henry Kilmer, John Heasley, Andrew Partan, Pete Whiting, and Austin Schutz. +## +# +# The original original lookingglass s/w was written by Ed Kern. It was +# a single script that used to be available at http://nitrous.digex.net/. +# Provided by permission and modified beyond recognition. +# +# Looking glass +# vars: query, router, args + +BEGIN { +$me = $0; +$me =~ s/.*\/(\S+)$/$1/; +} + +use CGI qw/:standard escapeHTML/; +use POSIX qw(strftime); +use Sys::Syslog; +use LockFile::Simple qw(lock trylock unlock); + +my($BASEDIR) = "@prefix@"; +my($SYSCONFDIR) = "@sysconfdir@"; +my($pingcmd) = "@LG_PING_CMD@"; + +my($query, $max_time_diff, $cache_dir, $cloginrc, @results); +my($type, $router_param, $remote_user, $arg, $router, $mfg); + +my($LG_CACHE_DIR, $LG_CLOGINRC, $LG_IMAGE, $LG_LOG, $LG_ROUTERDB, $LG_AS_REG); +my($LG_BGP_RT, $LG_CACHE_TIME, $LG_SINGLE, $LG_STRIP); + +if (!defined($ENV{HOME})) { $ENV{HOME} = "."; } + +# note: the following functions are duplicated between lgform.cgi and lg.cgi +# to avoid the need for module inclusion headaches from within a httpd context. +# it is just easier to be self-contained. +# SO, ANY CHANGES HERE SHOULD BE REFLECTED IN THE OTHER .cgi. + +# logging +sub dolog +{ + my($level, $msg) = @_; + + if (defined($LG_LOG) && $LG_LOG !~ /\//) { + openlog($me, "pid", $LG_LOG); + syslog($level, "%s", $msg); + closelog; + } else { + local(*LOG); + my($file); + if (defined($LG_LOG)) { + $file = $LG_LOG; + } else { + $file = "$cache_dir/lg.log"; + } + # log date, hostname, query, addr + if (open(LOG, ">>$file") == 0) { + # stderr, if all else fails + printf(STDERR "[" . strftime("%a %b %e %H:%M:%S %Y", gmtime) . + "] could not open log file $file: $!\n"); + printf(STDERR $msg); + } else { + printf(LOG $msg); + close(LOG); + } + } + return; +} + +# read LG configuration file +sub readconf +{ + my($conffile, $cmds); + local(*CONF); + if (defined($ENV{LG_CONF})) { + $conffile = $ENV{LG_CONF}; + } elsif (-e "lg.conf") { + $conffile = "lg.conf"; + } else { + $conffile = "$SYSCONFDIR/lg.conf"; + } + + if (! -f $conffile) { + return; + } + + if (open(CONF, "< $conffile")) { + while () { + next if (/^\s*(#|$)/); + $cmds .= $_; + } + close(CONF); + eval $cmds; + } else { + printf(STDERR "ERROR: couldn\'t open the configuration file: " . + "$conffile: $!\n"); + exit(1); + } + + return; +} + +# read router.db file +sub readrouters +{ + my($rtrdb); + local(*RTR); + + if (defined($LG_ROUTERDB)) { + $rtrdb = $LG_ROUTERDB; + } else { + $rtrdb = "$SYSCONFDIR/router.db"; + } + + if (! -f $rtrdb) { + my(@dirs, $dir); + # if the router.db file does not exist, try to compile the list from + # the rancid group router.db files. + local(*DIR); + if (! opendir(DIR, $BASEDIR)) { + dolog(LOG_ERR, "ERROR: couldn\'t read $BASEDIR: $!\n"); + } else { + while ($dir = readdir(DIR)) { + next if ($dir =~ /^(\.|\.\.|CVS|bin|etc|logs|util)$/); + push(@dirs, $dir) if (-d "$BASEDIR/$dir"); + } + closedir(DIR); + + foreach $dir (@dirs) { + if (! opendir(DIR, "$BASEDIR/$dir")) { + dolog(LOG_ERR, "ERROR: couldn\'t read $BASEDIR/$dir: $!\n"); + next; + } + closedir(DIR); + next if (! -f "$BASEDIR/$dir/router.db"); + if (open(RTR, "< $BASEDIR/$dir/router.db")) { + while () { + next if (/^\s*(#|$)/); + # fqdn:mfg:state + @record = split('\:', $_); + next if ($record[2] !~ /up/i || $record[1] !~ /(cisco|foundry|juniper)/); + push(@rtrlist, join(':', ($record[0], $record[1]))); + $rtrlabels{join(':', ($record[0], $record[1]))} = $record[0]; + } + close(RTR); + } else { + dolog(LOG_ERR, "ERROR: couldn\'t open the router.db " . + "file: $BASEDIR/$dir/router.db: $!\n"); + } + } + } + } else { + if (open(RTR, "< $rtrdb")) { + while () { + next if (/^\s*(#|$)/); + # fqdn:mfg:state + @record = split('\:', $_); + next if ($record[2] !~ /up/i || $record[1] !~ /(cisco|foundry|juniper)/); + push(@rtrlist, join(':', ($record[0], $record[1]))); + $rtrlabels{join(':', ($record[0], $record[1]))} = $record[0]; + } + close(RTR); + } else { + dolog(LOG_ERR, "ERROR: couldn\'t open the router.db file: " . + "$rtrdb: $!\n"); + exit(1); + } + } + + return; +} + +# the remaining functions are particular to lg.cgi. + +# return true if $router is a member of @rtrlist +sub arraymember { + my($rtrlist) = shift; + my($router) = shift; + my($r); + + foreach $r (@$rtrlist) { + $r = (split(':', $r))[0]; + return(1) if ($r eq $router); + } + + return(0); +} + +# check reachability and lock file before attempting to connect to device +# return non-zero on error. +sub DoRsh +{ + my ($router, $mfg, $cmd, $arg) = @_; + my($ctime) = time(); + my($val); + + my($lckobj) = LockFile::Simple->make(-delay => $lock_int, + -max => $max_lock_wait, -hold => $max_lock_hold); + + if ($pingcmd =~ /\d$/) { + `$pingcmd $router`; + } else { + `$pingcmd $router 56 1`; + } + if ($?) { + print "$router is unreachable. Try again later.\n"; + return(-1); + } + if (! $lckobj->lock("$cache_dir/$router")) { + print "$router is busy. Try again later.\n"; + return(-1); + } + $val = &DoCmd($router, $mfg, $cmd, $arg); + $lckobj->unlock("$cache_dir/$router"); + return($val); +} + +# run commands on the router. return non-zero on error. +sub DoCmd +{ + my($rtr, $mfg, $cmd, $arg) = @_; + local(*CMD); + + if ($mfg =~ /foundry/i) { + open(CMD, "sh -c \"flogin -f $cloginrc -c \'$cmd $arg\' $rtr\" 2>&1 |"); + } elsif ($mfg =~ /juniper/i) { + open(CMD, "sh -c \"jlogin -f $cloginrc -c \'$cmd $arg\' $rtr\" 2>&1 |"); + } else { + open(CMD, "sh -c \"clogin -noenable -f $cloginrc -c \'$cmd $arg\' $rtr\" 2>&1 |"); + } + while () { + tr/\015//d; + if (/^error:/i) { + dolog(LOG_ERR, $_); + if ($LG_STRIP) { undef(@results); } + push(@results, $_); + print @results; + return(-1); + } + push(@results, $_); + if (/$cmd/) { + ($prompt) = /^(\S*)[\#>]/; + if ($LG_STRIP) { + undef(@results); + } else { + print @results; + } + last; + } + } + + while () { + last if /^$prompt[\#>]/; + tr/\015//d; + print $_; + push(@results, $_); + } + while () {} + close(CMD); + + return(0); +} + +## +# Subroutine: Error +# Usage: &Error("msg")); +# Description: displays an error and exits. +## +sub Error { + my($msg) = @_; + + my($q) = new CGI(); + print $q->header; + if ($LG_STYLE) { + print $query->start_html(-title => "LookingGlass Results - $router", + -style => {'src' => $LG_STYLE}); + } else { + print $query->start_html(-title => "LookingGlass Results - $router"); + } + + # add the company image, LG_IMAGE + print $LG_IMAGE; + + + print < + Looking Glass Error: +

+ $msg +
+


+ $LG_INFO +EOF + + print $q->end_html; + exit(0); +} + +# convert an ipv4 address mask to prefix length +sub mask2len { + my($mask) = shift; + my($a, $b, $c, $d) = split('\.', $mask); + my($p, $len); + + $p = ~ (($a << 24) + ($b << 16) + ($c << 8) + $d); + for ($len = 32; $p > 0; $len --) { + $p = $p >> 1; + } + + return($len); +} + +# end the page and exit. +sub end_page { + + print < + + +END + + print $query->end_html; + exit(0); +} + +# start the page and log the transaction... +sub start_page { + my($mfg) = @_; + my($cmd); + + my($timestr) = strftime("%a %b %e %H:%M:%S %Y", gmtime); + dolog(LOG_INFO, sprintf("%s %s %s %s\n", + $ENV{REMOTE_HOST}, $ENV{REMOTE_ADDR}, $ENV{REMOTE_USER}, + "- - [$timestr] $type $router $arg")); + print $query->header; + if ($LG_STYLE) { + print $query->start_html(-title =>"LookingGlass form", + -style => {'src' => $LG_STYLE}); + } else { + print $query->start_html(-title =>"LookingGlass from"); + } + + $timestr = strftime("%a %b %e %H:%M:%S %Y %Z", gmtime); + + # add the company image, LG_IMAGE + print $LG_IMAGE; + + if ($mfg =~ /foundry/i) { + $cmd = $foundryCmd{$type}; + } elsif ($mfg =~ /juniper/i) { + $cmd = $juniperCmd{$type}; + } else { + $cmd = $ciscoCmd{$type}; + } + + print < +

Looking Glass Results - $router +

+
+ +
+ Date: $timestr +

+ Query: $cmd +
+HEAD + + if ($arg) { print "Argument(s): $arg\n"; } + print "

\n"; + + print < + +

+

+END
+
+    return;
+}
+
+
+# Main()
+# read the configuration file if it exists.
+readconf();
+
+# The script will now cache the results as simple files in the $cache_dir,
+# named after the type of query (queries must, of course, be one word with
+# no spaces).  Modify $LG_CACHE_TIME to set the lifetime for cache entries.
+# for most web servers, cache_dir must be writable by uid nobody
+if (defined($LG_CACHE_DIR)) {
+    $cache_dir = $LG_CACHE_DIR;
+} else {
+    $cache_dir = "./tmp";
+}
+
+# read routers table to get @rtrlist
+readrouters();
+
+# when to display cache?  max time difference (in seconds)
+if (defined($LG_CACHE_TIME)) {
+    $max_time_diff = $LG_CACHE_TIME;
+} else {
+    $max_time_diff = "600" ;
+}
+
+# max seconds to wait for a 'router' lock to free up
+$max_lock_wait = 30;
+$lock_int = 5;
+$max_lock_hold = 300;
+
+# clogin setup
+if (defined($LG_CLOGINRC)) {
+    $cloginrc = $LG_CLOGINRC;
+} else {
+    $cloginrc = "$ENV(HOME)/.cloginrc";
+}
+
+$query = new CGI;
+
+# get form data and validate
+$type = ($query->param('query'))[0];
+$router_param = ($query->param('router'))[0];
+$remote_user = $ENV{REMOTE_USER};
+$arg = ($query->param('args'))[0];
+# handle multiple args
+$arg =~ s/["'`]//g;			# these are BS in any arg for any query
+@arg = split(' ', $arg);
+
+# verify router, commands, arguments, etc.
+($router, $mfg) = split(':', $router_param);
+if (!defined($type) || !defined($router) || $router eq "") {
+    &Error("You must at least choose a Query and a router.  Try buying " .
+								"a clue.\n");
+}
+
+if ($arg !~ /^[-A-Za-z0-9|_\/ \.^\$]*$/) {
+    &Error("Funny characters in argument; ignoring.\n");
+}
+if (length($arg) >= 50) {
+    &Error("Argument string too long; ignoring. \n");
+}
+
+if (! arraymember(\@rtrlist, $router)) {
+    my($timestr) = strftime("%a %b %e %H:%M:%S %Y", gmtime);
+    dolog(LOG_WARNING, sprintf("%s %s %s %s\n",
+	$ENV{REMOTE_HOST}, $ENV{REMOTE_ADDR}, $ENV{REMOTE_USER},
+	"- - [$timestr] lg.cgi: attempt to access $router\n"));
+    Error("access to $router not permitted");
+}
+
+# conversion of command "type" passed from lgform.cgi to the vendor's syntax.
+if ($mfg =~ /cisco/i) {
+    %mfgCmd = (
+		# Debug Queries
+		log => "show logging",
+		# Interface Queries
+		framerelay => "show frame-relay pvc",
+		interface => "show interface",
+		intbrief => "show ip interface",	# switch in {interface}
+		# Routing Queries
+		damp => "show ip bgp dampened-paths",
+		neighbor => "show ip bgp neighbor",
+		# Multicast Queries
+		mbgp => "show ip mbgp",
+		mbgpsum => "show ip mbgp summary",
+		mneighbor => "show ip bgp neighbor",
+		msdp => "show ip msdp summary",
+		msdpsa => "show ip msdp sa-cache",
+		msess => "show ip sdr",
+		mroute => "show ip mroute",
+		pim_interface => "show ip pim interface",
+		pim_neighbor => "show ip pim neighbor",
+		pim_rp => "show ip pim rp mapping",
+		# IPv6 Queries
+		#
+		#acl => "show access-list",
+		#aspath => "show ip as-path-access-list",
+		#communitylist => "show ip community-list",
+		ping => "ping",
+		prefix => "show ip bgp",
+		prefixlist => "show ip prefix-list",
+		regex => "show ip bgp regex",
+		route => "show ip route",
+		routemap => "show route-map",
+		rpf => "show ip rpf",
+		summary => "show ip bgp summary",
+		trace => "traceroute",
+		v6_bgp => "show bgp ipv6",
+		v6_interface => "show ipv6 interface",
+		v6_summary => "show bgp ipv6 summary"
+    );
+} elsif ($mfg =~ /foundry/i) {
+    %mfgCmd = (
+		# Debug Queries
+		log => "show log",
+		ping => "ping",
+		trace => "traceroute",
+		# Interface Queries
+		#framerelay => "show frame-relay pvc",	# no compatible command
+		interface => "show interface",
+		# Routing Queries
+		damp => "show ip bgp dampened-paths",
+		neighbor => "show ip bgp neighbor",
+		#regex => "show ip bgp aspath-regex",
+		route => "show ip route",
+		summary => "show ip bgp summary",
+		# Multicast Queries
+		#mbgp => "show ip mbgp",
+		#mbgpsum => "show bgp summary",
+		#mneighbor => "show ip bgp neighbor",
+		mroute => "show ip mroute",
+		msdp => "show ip msdp summary",
+		msdpsa => "show ip msdp sa-cache",
+		msess => "show ip sdr",
+		pim_interface => "show ip pim interface",
+		pim_neighbor => "show ip pim neighbor",
+		pim_rp => "show ip pim rp mapping",
+		rpf => "show ip rpf",
+		# IPv6 Queries
+		# v6_bgp => "show bgp ipv6",
+		# v6_interface => "show ipv6 interface",
+		# v6_summary => "show bgp ipv6 summary"
+		#
+		#acl => "show access-list",
+		#aspath => "show ip as-path-access-list",
+		#communitylist => "show ip community-list",
+		routemap => "show route-map",
+		prefix => "show ip bgp",
+		prefixlist => "show ip prefix-list"
+	);
+} elsif ($mfg =~ /juniper/i) {
+    %mfgCmd = (
+		# Debug Queries
+		log => "show log messages",
+		ping => "ping rapid count 5",
+		trace => "traceroute",
+		# Interface Queries
+		framerelay => "show frame-relay pvc",
+		interface => "show interface",
+		#intbrief => "show ip interface",	# switch in {interface}
+		# Routing Queries
+		damp => "show route damping suppressed terse table inet.0",
+		neighbor => "show bgp neighbor",
+		regex => "show route table inet.0 aspath-regex",
+		summary => "show bgp summary",
+		# Multicast Queries
+		mbgp => "show route table inet.2 terse",
+		mbgpsum => "show bgp summary",
+		mneighbor => "show bgp neighbor",
+		mroute => "show multicast route extensive",
+		msdp => "show msdp",
+		msdpsa => "show msdp source-active",
+		msess => "show multicast sessions",
+		pim_interface => "show pim interface",
+		pim_neighbor => "show pim neighbors",
+		pim_rp => "show pim rps",
+		pim_join => "show pim join",
+		rpf => "show multicast rpf",
+		# IPv6 Queries
+		v6_bgp => "show route table inet6.0",
+		v6_interface => "show interface",
+		v6_summary => "show bgp summary",
+		#
+		#acl => "show access-list",
+		#aspath => "show ip as-path-access-list",
+		#communitylist => "show ip community-list",
+		prefix => "show route table inet.0",
+		prefixlist => "show policy",
+		route => "show route table inet.0 terse",
+		routemap => "show policy"
+	);
+}
+
+# construct Display command from configuration
+%cmdDisp=();
+foreach $qtype (sort keys(%$queries)) {
+    next if (! scalar(%{$queries->{$qtype}}));
+    foreach $sub_type (sort keys(%{$queries->{$qtype}})) {
+	$cmdDisp{$sub_type} = $queries->{$qtype}->{$sub_type};
+    }
+}
+
+# make sure the command is not disabled
+if (! defined($cmdDisp{$type})) {
+    &Error("Unknown command type: $type\n");
+}
+
+# not all cmds/queries are implemented for all platforms
+if (! defined($mfgCmd{$type})) {
+    Error("$cmdDisp{$type} not implemented for $mfg or no suitable " .
+						"equivalent exists.  sorry.\n");
+}
+$cmd = $mfgCmd{$type};
+
+# handle each query/command type
+if ($type eq "prefix" || $type eq "mbgp" || $type eq "route" ) {
+    if ($arg[0] !~ /^\d+\.\d+\.\d+\.\d+$/) {
+	&Error("The IP address \"$arg[0]\" is not valid and lacking an " .
+				"address would over-burden our router.\n");
+    } elsif (defined($arg[1]) && $arg[1] !~ /^\d+\.\d+\.\d+\.\d+$/) {
+	&Error("The IP netmask \"$arg[1]\" is not valid.\n");
+    }
+    if ($mfg =~ /juniper/i && defined($arg[1])) {
+	$arg = $arg[0] . "/" . mask2len($arg[1]);
+    }
+} elsif ($type eq "v6_route" ){
+    # XXX: is this check of the address arg correct and pedantic?
+    if ($arg[0] !~ /[0-9a-fA-F:]+$/) {
+	&Error("The IPv6 address \"$arg[0]\" is not valid.\n");
+    }
+} elsif ($type eq "framerelay") {
+    if ($mfg =~ /juniper/) {
+	&Error("Juniper does not have a show frame-relay pvc command.  " .
+						"Use show interface.\n");
+    }
+    if ($arg[0] > 15 && $arg[0] < 1024) {
+	$arg = $arg[0];
+    } else {
+	undef($arg);
+    }
+} elsif ($type eq "interface" || $type eq "v6_interface") {
+
+	# XXX: wtf is arg[1]?
+#    if ($arg[1] =~ /[-\/0-9:.]+/) {
+#       $arg = $arg[0] . " " . $arg[1];
+#    } else {
+
+    if ($mfg =~ /(cisco|foundry)/) {
+	if ($arg[0] !~ /^b[^ ]+[0-9]/i && $arg[0] =~ /^b/i) {
+	    $type = "intbrief";
+	    $arg = "brief";
+	} else {
+	    $arg = $arg[0];
+	}
+    } elsif ($mfg =~ /juniper/) {
+	my($optind) = 0;
+	# arg 0 may be an intf name or a display option, but there can
+	# only be 2 args
+	$arg = "";
+	while ($optind <= $#arg && $optind < 2) {
+	    $arg[$optind] =~ s/brief/terse/;
+	    if ($arg[$optind] =~ /^([a-z0-9]{2}\-\d+\/\d+\/\d+(:\d+)?)/i) {
+		$arg .= " $1";
+	    } elsif ($arg[$optind] =~ /^det/i) {
+		$arg .= " detail";
+	    } elsif ($arg[$optind] =~ /^ter/i) {
+		$arg .= " terse";
+	    } elsif ($arg[$optind] =~ /^ext/i) {
+		$arg .= " extensive";
+	    }
+	    $optind += 1;
+	}
+    }
+} elsif ($type eq "log") {
+    if ($arg[0] =~ /^\s*\|?$/) {
+	shift(@arg);
+    }
+    $arg[0] =~ s/^\s*\|?//;
+    if ($arg[0] !~ /^\s*$/) {
+	if ($mfg =~ /cisco/i) {
+	    $arg = " | include " . join(' ', @arg);
+	} elsif ($mfg =~ /juniper/i) {
+	    $arg = " | match \"" . join(' ', @arg) . "\"";
+	} else {
+	    undef($arg);
+	}
+    } else {
+	undef($arg);
+    }
+} elsif ($type eq "ping" || $type eq "trace") {
+    if ($arg[0] !~ /^\d+\.\d+\.\d+\.\d+$/) {
+	if ($arg[0] !~ /^[A-Za-z0-9._-]+$/) {
+	    &Error("That argument ($arg[0]) is not valid.\n");
+	}
+    }
+    $arg = $arg[0];
+} elsif ($type eq "aspath" || $type eq "communitylist") {
+    if ($arg[0] !~ /^\d+$/ || ($arg[0] < 1 && $arg[0] > 199)) {
+	&Error("That argument ($arg[0]) is not valid.\n");
+    }
+    $arg = $arg[0];
+} elsif ($type eq "acl") {
+    if ($arg[0] !~ /^\d+$/ || ($arg[0] < 100 && $arg[0] > 199) ||
+					($arg[0] < 1300 && $arg[0] > 2699)) {
+	&Error("That argument ($arg[0]) is not valid.\n");
+    }
+    $arg = $arg[0];
+    # don't show the jewels
+    # XXX: this error msg is useless, but show acl is un-implemented.
+    &Error($mfg) if ($arg == 98 || $arg == 99);
+} elsif ($type eq "prefixlist" || $type eq "routemap") {
+    if ($arg[0] !~ /^[0-9A-Za-z][^\s\"]*$/) {
+	&Error("That argument ($arg[0]) is not valid.\n");
+    }
+    $arg = $arg[0];
+} elsif ($type eq "regex") {
+    # bgp as-path regex
+    $arg = $arg[0];
+    if ($#arg >= 1) {
+	for ($n = 1; $n <= $#arg; $n++) { $arg .= " " . $arg[$n]; }
+    }
+    # remove leading/trailing whitespace
+    $arg =~ s/^\s*//; $arg =~ s/\s*$//;
+    if ($arg !~ /^[0-9_ ^.*+?[\])\(-]*\$?$/ || $arg =~ /^\s*$/) {
+	&Error("That argument ($arg[0]) is not valid.\n");
+    }
+    # pathetic excuses for lookups
+    if ($arg =~ /^[_.* ^]*(\*|1|701|1239|1280|1740|3561|5462|10303)+[_\$]*$/ ||
+	$arg =~ /^[_.* ^]*(1|701|1239|1280|1740|3561|5462|10303)+[_ .]*[\[*.]/) {
+	&Error("Get real.  Such a query has potential to over-burden our " .
+		"router.\nLook that up on your own router.\n");
+    }
+    if ($mfg =~ /juniper/) {
+	$arg =~ s/_/ /g;
+	# pre-junos 4.4 does not allow anchors
+	if ($arg =~ /\^\$/) {
+	    $arg =~ "()";
+	} else {
+	    $arg =~ s/[\$^]/ /g;
+	}
+	$arg = "\"$arg\"";
+    }
+    # escape any ()s
+    $arg =~ s/([\(\)])/\\$1/g;
+} elsif ($type eq "neighbor") {
+    if ($arg[0] !~ /^\d+\.\d+\.\d+\.\d+$/) {
+	if ($arg[0] !~ /([A-Za-z0-9-]*.)*[A-Za-z0-9-]*.(com|edu|net|org)/) {
+	    &Error("That argument ($arg[0]) is not valid.\n");
+	}
+    }
+    $arg = $arg[0];
+    if (defined($arg[1]) && $arg[1] =~ /^(a|ro|f|re)/) {
+	if ($mfg =~ /juniper/) {
+	    if ($arg[1] =~ /^a/) {
+		if (defined($LG_BGP_RT)) {
+		    $cmd = "show route table inet.0 all advertising-protocol ".
+			"bgp";
+		}
+	    } elsif ($arg[1] =~ /^f/) {
+		if (defined($LG_BGP_RT)) {
+		    $cmd = "show route damping table inet.0 all ".
+			"receive-protocol bgp";
+		}
+	    } elsif ($arg[1] =~ /^r/) {
+		if (defined($LG_BGP_RT)) {
+		    $cmd = "show route table inet.0 all receive-protocol bgp";
+		}
+	    }
+	} else {
+	    if ($arg[1] =~ /^a/) {
+		if (defined($LG_BGP_RT)) { $arg .= " advertised-routes"; }
+	    } elsif ($arg[1] =~ /^f/) {
+		$arg .= " flap-statistics";
+	    } elsif ($arg[1] =~ /^ro/) {
+		if (defined($LG_BGP_RT)) { $arg .= " routes"; }
+	    } elsif ($arg[1] =~ /^re/) {
+		if (defined($LG_BGP_RT)) { $arg .= " received-routes"; }
+	    }
+	}
+    }
+} elsif ($type eq "mneighbor") {
+    if ($arg[0] !~ /^\d+\.\d+\.\d+\.\d+$/) {
+	if ($arg[0] !~ /([A-Za-z0-9-]*.)*[A-Za-z0-9-]*.(com|edu|net|org)/) {
+	    &Error("That argument ($arg[0]) is not valid.\n");
+	}
+    }
+    $arg = $arg[0];
+    if (defined($arg[1]) && $arg[1] =~ /^(a|ro|f|re)/) {
+	if ($mfg =~ /juniper/) {
+	    if ($arg[1] =~ /^a/) {
+		$cmd .= " advertised-routes";
+	    } elsif ($arg[1] =~ /^f/) {
+		$cmd .= " flap-statistics";
+	    } elsif ($arg[1] =~ /^ro/) {
+		$cmd .= " routes";
+	    } elsif ($arg[1] =~ /^re/) {
+		$cmd .= " received-routes";
+	    }
+	} else {
+	    if ($arg[1] =~ /^a/) {
+		$arg .= " advertised-routes";
+	    } elsif ($arg[1] =~ /^f/) {
+		$arg .= " flap-statistics";
+	    } elsif ($arg[1] =~ /^ro/) {
+		$arg .= " routes";
+	    } elsif ($arg[1] =~ /^re/) {
+		$arg .= " received-routes";
+	    }
+	}
+    }
+} elsif ($type eq "damp" || $type eq "summary" || $type eq "mbgpsum") {
+    undef($arg);
+}
+
+# make stdout unbuffered, so result page streams.
+$| = 1;
+start_page();
+
+# cache the following
+if ($type eq "summary" || $type eq "mbgpsu" || $type eq "damp"
+							|| $type eq "log") {
+    if (!$arg) {
+	# cache requests with no addr/argument
+	local(*CACHE);
+
+	my($file) = "$cache_dir/$type" ;
+	$file =~ s/\s+/_/g;
+	$file .= "_$router";
+
+	if (-e $file) {
+	    # see if cache exists
+	    @stat = stat($file);
+	    $ftime = $stat[9];
+	    $dtime = time() - $stat[9];
+
+	    # see if we are within cache time
+	    if ($dtime <= $max_time_diff) {
+		if (open(CACHE, "<$file") == 0) {
+		    dolog(LOG_ERR, "couldnt open cache file $file: $!\n");
+		} else {
+		    print "From cache (number of seconds old (max " .
+			"$max_time_diff)): $dtime\n\n";
+		    while () { print $_; }
+		    close(CACHE);
+		    &end_page();
+		}
+	    }
+	}
+
+	# else, execute command and save to a new cache file
+	if (! &DoRsh($router, $mfg, $cmd, $arg)) {
+	    if (open(CACHE, ">$file") == 0) {
+		dolog(LOG_ERR, "couldnt create cache file $file: $!\n");
+		exit(1);
+	    } else {
+		printf(CACHE "@results");
+		close(CACHE);
+	    }
+	}
+    } else {
+	&DoRsh($router, $mfg, $cmd, $arg);
+    }
+    &end_page();
+} else {
+    &DoRsh($router, $mfg, $cmd, $arg);
+    &end_page();
+}
+
+exit(0);
-- 
cgit