#! /usr/bin/perl -w # -*- mode: Perl -*- ################################################################## # MRTG 2.17.7 --- Index Generator ################################################################## # # This reads a mrtg.cfg file form std input or cmdline argument # and it takes a regexp on the cmdline to specify which # targets to look at. # # from this info it produces a router index on stdout or # on the filename specified by the --output option # ################################################################## # Distributed under the GNU General Public License # Copyright 2000 by Tobias Oetiker ################################################################## $main::GRAPHFMT="png"; require 5.005; use strict; # DEBUG TARGETS # base - basic program flow #@main::DEBUG=qw(base); BEGIN { # Automatic OS detection ... do NOT touch if ( $^O =~ /^(?:(ms)?(dos|win(32|nt)?))/i ) { $main::OS = 'NT'; $main::SL = '\\'; $main::PS = ';'; } elsif ( $^O =~ /^NetWare$/i ) { $main::OS = 'NW'; $main::SL = '/'; $main::PS = ';'; } elsif ( $^O =~ /^VMS$/i ) { $main::OS = 'VMS'; $main::SL = '.'; $main::PS = ':'; } else { $main::OS = 'UNIX'; $main::SL = '/'; $main::PS = ':'; } } use FindBin; use lib "${FindBin::Bin}"; use lib "${FindBin::Bin}${main::SL}..${main::SL}lib${main::SL}mrtg2"; use MRTG_lib "2.100016"; use Getopt::Long; use Pod::Usage; #---------------------------------------------------------------------- sub decimal_sort($$) { my $a = $_[0]; my $b = $_[1]; $a =~ s/([0-9]+)/sprintf("%09u",${1})/ge; $b =~ s/([0-9]+)/sprintf("%09u",${1})/ge; return $a cmp $b; } #---------------------------------------------------------------------- my @argv = @ARGV; my $argz = "$0"; foreach my $ar (@argv) { if ($ar =~ /[ |()]/ ) { $ar = sprintf qq{"%s"}, $ar; } $argz .= " $ar"; } main(); exit 0; sub main { # default options my %opt = ( sort => 'original', show => 'day', section => 'h1', columns => 2, addhead => '', bodyopt => 'bgcolor="#ffffff" text="#000000" '. 'link="#000000" vlink="#000000" alink="#000000"', title => 'MRTG Index Page', headlevel => 1, pagetopend => '', pagetop => '', pageend => '', prefix => '', rrdviewer => '/cgi-bin/14all.cgi', optlog => 1, bold => 1, boldon => '', boldoff => '', div => 'DIV', imgborder => 1, cellspacing => 10, nolegend => 0, ); $opt{headon} = ""; $opt{headoff} = ""; # load real options options(\%opt); #adapt some defaults to the current options die "ERROR: --autoprefix requires --output" if ($opt{autoprefix} and !defined $opt{output}); $opt{pageend} = $opt{pagetopend} if (defined $opt{pagetopend} and not $opt{pageend}); $opt{pagetop} = $opt{pagetopend} if (defined $opt{pagetopend} and not $opt{pagetop}); $opt{boldon} = $opt{boldoff} = "" if (!$opt{bold}); $opt{picfirst} = (defined $opt{picfirst}?1:0); if ($opt{compact}) { $opt{imgborder} = 0; $opt{cellspacing} = 0; $opt{headon} = $opt{boldon}; $opt{headoff} = $opt{boldoff}; } if ($opt{sidebyside}) { $opt{div} = 'td'; } # slurp config files my %rcfg; my %cfg; my @target; my @routers; my $cfgfile; my %files; while (@ARGV) { $cfgfile = shift @ARGV; readcfg($cfgfile,\@routers,\%cfg,\%rcfg); if (($opt{sectionhost}) or ($opt{perhost})) { #We need to cache the "hostname" as appeared in cfgfile, #since it does change in cfgcheck (for ex. if multiple #overlapping cfgfiles are specified) for my $targ (@routers) { if ( !defined $rcfg{host}{$targ} and !($rcfg{target}{$targ} =~ m/(?{$targ} = $pref if (! defined $rcfg{prefixes}->{$targ}); } } } # generate index page genindex(\@routers, \%cfg, \%rcfg, \%opt, \%files); } sub cleanurl ($) { my $url = shift; $url =~ s|([^/.][^/.]*/)\.\./\1|$1|g; return $url; } #Take a path the mrtg (usually the mrtg output directory) and the overview #file, find the relative path from the overview to the directory sub subpath ($$) { my $sub = shift; my $out = shift; my @s=split /$main::SL/,$sub; my @o=split /$main::SL/,$out; pop @o; #Last is a filename; for my $i (0..$#s) { #cut common dirs if (defined $s[0] and defined $o[0] and $s[0] eq $o[0] ) { shift @s; shift @o; } } my $ret = join $main::SL,@s; for my $i (0..$#o) { $ret = "..$main::SL$ret"; # ".." == "Directory below this one" for # dos, windows, unix. What about VMS ? # Is this correct ? HEH } $ret .= $main::SL; #Possibly this should be "/" in order not #to break on platforms !unix, since it will be #used for generating urls ? #Don't degenerate in "/" when really no prefix is needed. $ret = "" if ($ret eq $main::SL); return $ret; } sub genindex ($$$$) { my $routers = shift; my $cfg = shift; my $rcfg = shift; my $opt = shift; my $cfgfile = shift; my $index; my $metaCmdLine; # ----------------------------------------------------------- # keep only the items our users want (--filter) # ----------------------------------------------------------- my @filtered; ITEM: foreach my $item (@{$routers}) { foreach my $filter (@{$$opt{filter}}) { if ($filter =~ /(.+)([=!]~)(.+)/) { my ($area,$comp,$regex) = ($1,$2,$3); my $value; for ($area) { /^title|pagetop$/ && do { $value = $$rcfg{$area}{$item}; last }; /^name$/ && do { $value = $item; last }; die "ERROR: unknown filter area $_\n"; }; for ($comp) { /^=~$/ && do { next ITEM unless $value =~ /$regex/; last }; /^!~$/ && do { next ITEM unless $value !~ /$regex/; last }; die "ERROR: unknown comparison operator $_\n"; }; } else { die "ERROR: invalid filter expression $filter\n"; } } push @filtered, $item; }; # ----------------------------------------------------------- # get items into proper order (--sort) # ----------------------------------------------------------- my @order; for ($$opt{sort}) { /^original$/ && do {@order = @filtered; last}; /^name$/ && do { @order = sort { decimal_sort($a,$b); } @filtered; last}; /^title$/ && do { @order = sort { decimal_sort($$rcfg{title}{$a}, $$rcfg{title}{$b}) || $a cmp $b } @filtered; last; }; /^descr(iption)?$/ && do { @order = sort { $$rcfg{pagetop}{$a} =~ m[Description:\s*(?:\S+\s+)?(.+?)]i; my $aval = lc $1; $$rcfg{pagetop}{$b} =~ m[Description:\s*(?:\S+\s+)?(.+?)]i; my $bval = lc $1; $aval cmp $bval; } @filtered; last; }; die "ERROR: unknown sort order '$$opt{sort}'\n"; } die "ERROR: did not find any matching data in cfg file\n" unless @order; # ----------------------------------------------------------- # issue page top # ----------------------------------------------------------- my $interval =$$cfg{'interval'} ? $$cfg{'interval'} : 5; my $expiration = &expistr($interval); my $refresh = $$cfg{'refresh'} ? $$cfg{'refresh'} : 300; for ($$opt{show}) { $refresh = /^week$/ && 1800 || /^month$/ && 7200 || /^year$/ && 86400 || $refresh ; } my $gifPath = ''; if ($$cfg{icondir} || $$opt{icondir}) { $gifPath = $$opt{icondir} || $$cfg{icondir}; #lets make sure there is a trailing path separator $gifPath =~ s|/*$|/|; } else { $gifPath = "$$cfg{imagehtml}"; } if ($$opt{optlog}) { $metaCmdLine = $argz; } else { $metaCmdLine = ""; } $metaCmdLine =~ s/&/&/g; # Must be first, otherwise it will affect the following changes $metaCmdLine =~ s/"/"/g; $metaCmdLine =~ s//>/g; my $headeradd = $$opt{headeradd} || ""; $index = < $$opt{title} $headeradd ECHO $index .= < /* commandline was: $argz */ /* sorry, no style, just abusing this to place the commandline and pass validation */ ECHO $index .= < ECHO $index .= <$$opt{subtitle}

ECHO $index .= < ECHO # ----------------------------------------------------------- # print the graph items # ----------------------------------------------------------- my $itemnr = 0; my $first = $order[0]; my $host = $$rcfg{host}{$first}; if ($host){ $index .= "$$opt{headon}Interfaces of $host $$opt{headoff}" if $$opt{perhost}; } else { $index .= "$$opt{headon}Special Items$$opt{headoff}" if $$opt{perhost}; } foreach my $item (@order) { if ($$opt{perhost}) { my $newhost = $$rcfg{host}{$item} || 'unspecified host'; if (!($host eq $newhost)) { $host = $newhost; if ($host){ $index .= "$$opt{headon}Interfaces of $host $$opt{headoff}\n"; } else { $index .= "$$opt{headon}Special Items$$opt{headoff}\n"; } $index .= "\n"; $itemnr=0; } } $$opt{prefix} = $$rcfg{prefixes}->{$item} if ($$opt{autoprefix}); $itemnr++; $index .= ""; my $dirrel = "../" x ($$rcfg{'directory_web'}{$item} =~ tr|/|/|); # --- produce graph section title --- my $section; for ($$opt{section}) { /^h1$/ && do{ if ($$rcfg{pagetop}{$item} =~ m[+]*>(.+?)Description:\s*\Q$section\E\s*([^< ][^<]+?),i and $section = $1; last; }; /^portname$/ && do{ $section = "No Portname for $item"; $$rcfg{pagetop}{$item} =~ m,Port Name:\s*(.*?),i and $section = $1; last; }; /^ifname$/ && do{ $section = "No Portname for $item"; $$rcfg{pagetop}{$item} =~ m,ifname:\s*(.*?),i and $section = $1; last; }; die "ERROR: unknown sectioning type $_\n"; }; if (defined $$rcfg{host}{$item} and !($section =~ m/\b\Q$$rcfg{host}{$item}\E\b/i)) { $section = ucfirst $$rcfg{host}{$item} . ": $section"; } # --- write the actual graph ---- die "ERROR: Unknown show type $$opt{show}\n" unless $$opt{show} =~ /^day|week|month|year|none$/; my $image = "$item-$$opt{show}.${main::GRAPHFMT}" if $$opt{show} ne 'none'; $index .= "<$$opt{div}>" if (!$$opt{sidebyside}); if (not $image) { if ($$cfg{logformat} eq 'rrdtool') { my $sep = $$opt{rrdviewer} =~ /\?/ ? '&' : '?'; $index .= "". "$$opt{boldon}". "$section$$opt{boldoff}"; } else { $index .= "$$opt{boldon}". "". "$section$$opt{boldoff}\n<$$opt{div}>"; } } else { #loop used for reversing (text,images) to (images,text) if req. for my $picfirstloop (1,0) { if ( $picfirstloop^$$opt{picfirst} ) { $index .= "$$opt{boldon}$itemnr. $$opt{boldoff}" if $$opt{enumerate}; if ($$opt{clicktext}) { $index .= "$$opt{boldon}"; $index .= $section; $index .= "$$opt{boldoff}"; } else { $index .= "$$opt{boldon}$section$$opt{boldoff}"; } $index .= "\n<$$opt{div}>" if $picfirstloop; } if ( !($picfirstloop^$$opt{picfirst}) ) { # figure show name for rrd viewer if ($$cfg{logformat} eq 'rrdtool') { my $sep = $$opt{rrdviewer} =~ /\?/ ? '&' : '?'; $index .= "". "\"$item" } else { $index .= ""; $index .= "\"$item"; } $index .= "\n<$$opt{div}>" if $picfirstloop; } } } $index .= "" if (!$$opt{sidebyside}); $index .= "\n"; # --- new table column if necessary ---- if (($itemnr) % $$opt{columns} == 0) { $index .= "\n\n"; } } # ----------------------------------------------------------- # print page end # ----------------------------------------------------------- my $VERSION = "2.17.7"; $index .= < ECHO $index .= <
MRTG Multi Router Traffic Grapher
version $VERSION Tobias Oetiker <tobi\@oetiker.ch> and Dave Rand <dlr\@bungi.com>
ECHO $index .= < ECHO # ----------------------------------------------------------- # write out the index page # ----------------------------------------------------------- if ($$opt{output}) { debug ('base', "Writing $$opt{output}"); open X, ">$$opt{output}" or die "ERROR: creating $$opt{output}: $!\n"; print X $index; close X; } else { print $index; } } sub options ($) { my $opt = shift; my @options = ( 'help|?', 'man', 'output=s', 'filter=s@', 'addhead=s', 'title=s', 'subtitle=s', 'bodyopt=s', 'pagetopend=s', 'pagetop=s', 'pageend=s', 'columns=i', 'perhost!', 'sort=s', 'enumerate', 'width=i', 'height=i', 'show=s', 'section=s', 'version', 'prefix=s', 'headeradd=s', 'clicktext!', 'optlog!', 'compact!', 'headlevel=i', 'bold!', 'picfirst!', 'sidebyside!', 'nolegend', 'autoprefix!', 'sectionhost!', 'icondir=s', 'rrdviewer=s'); #generate --option-file from --option for ( grep /=s$/,@options ) { my $fileopt = $_; $fileopt =~ s/=s$/-file=s/; push @options, $fileopt; } GetOptions( $opt, @options ) or pod2usage(-verbose => 1); if ($$opt{prefix}){ $$opt{prefix} .= '/'; $$opt{prefix} =~ s|/+$|/|; } die ("Indexmaker for mrtg-2.17.7\n") if $$opt{version}; pod2usage(-exitval => 1, -verbose => 2) if $$opt{man}; pod2usage(-verbose => 1) if not @ARGV; #take care of --fileoption --> --option for my $fileopt ( grep /-file$/, keys %{$opt} ) { my $orgopt = $fileopt; $orgopt =~ s/-file$//; $$opt{$orgopt} = &readfile($$opt{$fileopt}); } } #return the contents of a file sub readfile($) { my $file = shift; open F,"<$file" or die "ERROR: can\'t open $file for read, $!"; my $sl = $/; $/ = undef; my $string = ; $/ = $sl; close F; return $string; } __END__ =pod =head1 NAME indexmaker - Creates index files for mrtg web sites (mrtg-2.17.7) =head1 SYNOPSIS indexmaker [options] mrtg.cfg [other.cfg ...] =head1 OPTIONS --output=filename set output filename (default: stdout) --filter title=~regexp select targets by matching regexp against titles --filter pagetop=~regexp select targets by matching regexp against pagetop --filter name=~regexp select targets by matchin regexp against name --addhead=text insert this text between and --title=text set title of generated index file --subtitle=text add a subtitle to the generated index file --bodyopt=text set body tag options --headlevel=number use at top of page (default: 1) --pagetop=text insert this text between and

...

--pageend=text insert this text after the main body --pagetopend=text use this text for pagetop or pageend if undefined --nolegend do not add the Mrtg legend at the end of the page --columns=number show graphs in a table with x columns (default: 2) --perhost show graphs of the same host on a row --compact try to make a vertically more compact page --optlog log the used command line in the page (default: log) --sort=title sort graphs by title --sort=name sort graphs by their name --sort=descr sort graphs by their description --sort=original leave as is (default) --enumerate add a sequence number to the title of each graph --picfirst place pictures before text (default: text first) --width=number set width of graphs (default: not set) --height=number --sidebyside place text / pictures side by side (default: above/below) --bold use bold text (default: bold) --clicktext make the text link to the inner page (like the image) --show=day pick which graph to show in the index (default) --show=week --show=month --show=year --show=none --section=h1 h1 tag from pagetop as section heading (default) --section=title title as section headings for graphs --section=name graph name as section heading --section=descr graph description as section heading --section=ifname interface name (ifName) as section heading --section=portname port name entry in pagetop as section heading --sectionhost Try to prepend the host to the section heading if missing --rrdviewer=path path to rrdviewer (default: /cgi-bin/14all.cgi) --icondir=path path to icondir --prefix=path path from the location of the index.html to the graphs --headeradd=string add string to the html page header --autoprefix try to set prefix automatically ---file=file read string argument for option from file =head1 DESCRIPTION B can create web pages which display the status of an array of mrtg interface status pages. =over =item B<--output> I set output filename (default: stdout) =item B<--filter> (B|B<pagetop>|B<name>)(B<=~>|B<!~>)I<regexp> Several filters may get set. Each filter can match agains the contents of a specific section of the mrtg config file. B<Name> refers to the bit in square brackets (option[name]: bla). Depending on the match operator chosen (B<=~> or B<!~>) the match will be positive or negative. Note that some shells consider B<!> a special character. It may be necessary to type B<\!~> instead. =item B<--title> I<text> Set title of generated index file (default: regexp) =item B<--bodyopt> I<text> The value of this argument gets appended to the E<lt>BODYE<gt> tag. This allows you to set document colors. By default this option is set to bgcolor="#ffffff" text="#000000" link="#000000" vlink="#000000" alink="#000000" =item B<--columns> I<number> Display graphs in a table with I<number> columns (default: 2) =item B<--sort> B<title>|B<name>|B<descr>|B<original> Sort the graphs in the page either by B<title>, by B<name>, by interface B<descr>iption, or leave them as is. =item B<--enumerate> Add a sequence number to the title of each graph =item B<--width> I<number> Set width of graphs =item B<--height> I<number> Set the height of the graphs =item B<--show> B<day>|B<week>|B<month>|B<year>|B<none> Select which graph to show in the index page. You can supress images completely with B<--show=none>. =item B<--section> B<h1>|B<title>|B<name>|B<description>|B<portname> Select what to use as the title for each graph in the page. B<h1> is the H1 section from pagetop, B<title> is the graph title, B<name> is the bit in square brackets (option[name]: bla), and B<descr> or B<description> is the text from the Description field of the PageTop (the Cisco description text if it's available, otherwise just the interface description). B<portname> is the C<Port Name:> from pagetop. =item B<--sectionhost> Extract the hostname from the target line (this does not work if the target is a mathematial expression). Prepend the hostname (and a colon) to the section if not already present. =item B<--rrdviewer> I<path> If you have set the B<LogFormat: rrdtool> property in the mrtg.cfg file, the index will take this into account. The only thing you must tell it is the path to your grapher cgi. (default: /cgi-bin/14all.cgi) =item B<--prefix> I<path> By default we assume that the file generated by indexmaker is stored in I<WorkDir>. If you want to store it somewhere else, specify how to reach I<WorkDir> from the place where the Index is stored. Note that you have to use '/' as path separator as this will be used in urls. Speaking of which, you can even enter a whole url. =item B<--autoprefix> I<path> Requires --output. Try to generate the prefix automatically by comparision of the path to the output file set with --output and the Htmldir set in the configuration files. Particulary useful when multiple configuration files are specified, with different Htmldir settings. =item B<--optlog> Default is logging in the generated page the command line, suppress with --nooptlog . Useful if the commandline contains a complex --pagetop=string which could confuse simple browsers. =item B<--someoption-file> I<filename> For any someoption which takes a I<string> as parameter you can read the string from a file by adding <-file> to the option keyword. The whole content of the file will be read and used as the I<string>. The file must exist. =back =head1 AUTHOR Tobias Oetiker E<lt>tobi@oetiker.chE<gt> =head1 LICENSE GNU General Public License =head1 COPYRIGHT 2000-2001 Tobias Oetiker E<lt>tobi@oetiker.chE<gt> =cut