"Fossies" - the Fresh Open Source Software Archive

Member "munin-2.0.67/master/lib/Munin/Master/GraphOld.pm" (22 Feb 2021, 63367 Bytes) of package /linux/misc/munin-2.0.67.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Perl source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "GraphOld.pm" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 2.0.66_vs_2.0.67.

    1 package Munin::Master::GraphOld;
    2 
    3 # -*- cperl -*-
    4 
    5 =encoding utf-8
    6 
    7 =begin comment
    8 
    9 This is Munin::Master::GraphOld, a package shell to make munin-graph
   10 modular (so it can loaded persistently in munin-cgi-graph for example)
   11 without making it object oriented yet.  The non "old" module will
   12 feature propper object orientation like munin-update and will have to
   13 wait until later.
   14 
   15 Copyright (C) 2002-2010 Jimmy Olsen, Audun Ytterdal, Kjell Magne
   16 Øierud, Nicolai Langfeldt, Linpro AS, Redpill Linpro AS and others.
   17 
   18 This program is free software; you can redistribute it and/or
   19 modify it under the terms of the GNU General Public License
   20 as published by the Free Software Foundation; version 2 dated June,
   21 1991.
   22 
   23 This program is distributed in the hope that it will be useful,
   24 but WITHOUT ANY WARRANTY; without even the implied warranty of
   25 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   26 GNU General Public License for more details.
   27 
   28 You should have received a copy of the GNU General Public License
   29 along with this program.  If not, see <http://www.gnu.org/licenses/>.
   30 
   31 =end comment
   32 
   33 =cut
   34 
   35 use warnings;
   36 use strict;
   37 
   38 use Exporter;
   39 
   40 our (@ISA, @EXPORT);
   41 @ISA    = qw(Exporter);
   42 @EXPORT = qw(graph_startup graph_check_cron graph_main graph_config);
   43 
   44 use IO::Socket;
   45 use IO::Handle;
   46 use RRDs;
   47 use POSIX qw(strftime);
   48 use Digest::MD5;
   49 use Getopt::Long;
   50 use Time::HiRes;
   51 use Text::ParseWords;
   52 
   53 # For UTF-8 handling (plugins are assumed to use Latin 1)
   54 if ($RRDs::VERSION >= 1.3) {
   55     use Encode;
   56     use Encode::Guess;
   57     Encode->import;
   58     Encode::Guess->import;
   59 }
   60 
   61 use Munin::Master::Logger;
   62 use Munin::Master::Utils;
   63 use Munin::Common::Defaults;
   64 
   65 use Log::Log4perl qw( :easy );
   66 
   67 # RRDtool 1.2 requires \\: in comments
   68 my $RRDkludge = $RRDs::VERSION < 1.2 ? '' : '\\';
   69 
   70 # And RRDtool 1.2.* draws lines with crayons so we hack
   71 # the LINE* options a bit.
   72 my $LINEkluge = 0;
   73 if ($RRDs::VERSION >= 1.2 and $RRDs::VERSION < 1.3) {
   74 
   75     # Only kluge the line widths in RRD 1.2*
   76     $LINEkluge = 1;
   77 }
   78 
   79 # RRD 1.3 has a "ADDNAN" operator which evaluates n + NaN = n instead of = NaN.
   80 my $AddNAN = '+';
   81 if ($RRDs::VERSION >= 1.3) {
   82     $AddNAN = 'ADDNAN';
   83 }
   84 
   85 # the ":dashes" syntax for LINEs is supported since rrdtool 1.5.3
   86 my $RRDLineThresholdAttribute = ($RRDs::VERSION < 1.50003) ? '' : ':dashes';
   87 
   88 # Force drawing of "graph no".
   89 my $force_graphing = 0;
   90 my $force_lazy     = 1;
   91 my $do_usage       = 0;
   92 my $do_version     = 0;
   93 my $cron           = 0;
   94 my $list_images    = 0;
   95 my $output_file    = undef;
   96 my $log_file       = undef;
   97 my $skip_locking   = 0;
   98 my $skip_stats     = 0;
   99 my $stdout         = 0;
  100 my $force_run_as_root = 0;
  101 my $conffile       = $Munin::Common::Defaults::MUNIN_CONFDIR . "/munin.conf";
  102 my $libdir         = $Munin::Common::Defaults::MUNIN_LIBDIR;
  103 # Note: Nothing by default is more convenient and elliminates code while
  104 # for cgi graphing - but it breaks how munin-graph expected stuff to work.
  105 # I think.
  106 my %draw           = (
  107     'day'      => 0,
  108     'week'     => 0,
  109     'month'    => 0,
  110     'year'     => 0,
  111     'sumyear'  => 0,
  112     'sumweek'  => 0,
  113     'pinpoint' => 0,
  114 );
  115 my %init_draw = %draw;
  116 my $pinpoint = {};
  117 
  118 my ($size_x, $size_y, $full_size_mode, $only_graph);
  119 my ($lower_limit, $upper_limit);
  120 
  121 my %PALETTE;    # Hash of available palettes
  122 my @COLOUR;     # Array of actuall colours to use
  123 
  124 {
  125     no warnings;
  126     $PALETTE{'old'} = [    # This is the old munin palette.  It lacks contrast.
  127         qw(22ff22 0022ff ff0000 00aaaa ff00ff
  128             ffa500 cc0000 0000cc 0080C0 8080C0 FF0080
  129             800080 688e23 408080 808000 000000 00FF00
  130             0080FF FF8000 800000 FB31FB
  131             )];
  132 
  133     $PALETTE{'default'} = [   # New default palette.Better contrast,more colours
  134             #Greens Blues   Oranges Dk yel  Dk blu  Purple  lime    Reds    Gray
  135         qw(00CC00 0066B3 FF8000 FFCC00 330099 990099 CCFF00 FF0000 808080
  136             008F00 00487D B35A00 B38F00         6B006B 8FB300 B30000 BEBEBE
  137             80FF80 80C9FF FFC080 FFE680 AA80FF EE00CC FF8080
  138             666600 FFBFFF 00FFCC CC6699 999900
  139             )];      # Line variations: Pure, earthy, dark pastel, misc colours
  140 }
  141 
  142 my $range_colour  = "22ff22";
  143 my $single_colour = "00aa00";
  144 
  145 # Use 400 x RRA step, in order to have 1px per RRA sample.
  146 my %times = (
  147     "day"   => "-2000m",  # (i.e. -33h20m)
  148     "week"  => "-12000m", # (i.e. -8d13h20m)
  149     "month" => "-48000m", # (i.e. -33d8h)
  150     "year"  => "-400d",
  151     "pinpoint"  => "dummy",
  152 );
  153 
  154 my %resolutions = (
  155     "day"   => "300",
  156     "week"  => "1500",
  157     "month" => "7200",
  158     "year"  => "86400"
  159 );
  160 
  161 my %sumtimes = (    # time => [ label, seconds-in-period ]
  162     "week" => ["hour", 12],
  163     "year" => ["day",  288]);
  164 
  165 # Limit graphing to certain hosts and/or services
  166 my @limit_hosts    = ();
  167 my @limit_services = ();
  168 my $only_fqn = '';
  169 
  170 my $watermark = "Munin " . $Munin::Common::Defaults::MUNIN_VERSION;
  171 
  172 # RRD param for RRDCACHED_ADDRESS
  173 my @rrdcached_params;
  174 
  175 my $running     = 0;
  176 my $max_running = 6;
  177 my $do_fork     = 1;
  178 
  179 # "global" Configuration hash
  180 my $config = undef;
  181 
  182 # stats file handle
  183 my $STATS;
  184 
  185 my @init_limit_hosts = @limit_hosts;
  186 my @init_limit_services = @limit_services;
  187 
  188 sub process_pinpoint {
  189     my ($pinpoint, $arg_name, $arg_value) = @_;
  190     # XXX - Special hack^h^h^h^h treatment for --pinpoint
  191     if ($arg_value && $arg_value =~ m/^(\d+),(\d+)$/ ) {
  192     # "pinpoint" replaces all the other timing options
  193     $draw{'day'}=0;
  194     $draw{'week'}=0;
  195     $draw{'month'}=0;
  196     $draw{'year'}=0;
  197     $draw{'sumweek'}=0;
  198     $draw{'sumyear'}=0;
  199     $draw{'pinpoint'}=1;
  200     $$pinpoint->{'start'} = $1; # preparsed values
  201     $$pinpoint->{'end'} = $2;
  202     }
  203 }
  204 
  205 
  206 sub process_fqn {
  207     my ($fqn, $arg) = @_;
  208 
  209     # Reset what to draw whenever we specify a new fqn
  210 
  211     $draw{'day'} = $draw{'week'} = $draw{'month'} = $draw{'year'} =
  212       $draw{'sumweek'} = $draw{'sumyear'} = $draw{'pinpoint'} = 0;
  213 
  214     return $arg;
  215 }
  216 
  217 
  218 sub graph_startup {
  219 
  220     # Parse options and set up.  Stuff that is usually only needed once.
  221     #
  222     # Do once pr. run, pr possebly once pr. graph in the case of
  223     # munin-cgi-graph
  224 
  225     # Localise the stuff, overwise it will be stacked up with CGI
  226     %draw = %init_draw;
  227     @limit_hosts = @init_limit_hosts;
  228     @limit_services = @init_limit_services;
  229 
  230     $pinpoint       = undef;
  231     my $pinpointopt    = undef;
  232 
  233     $force_graphing = 0;
  234     $force_lazy     = 1;
  235     $do_usage       = 0;
  236     $do_version     = 0;
  237     $cron           = 0;
  238     $list_images    = 0;
  239     $output_file    = undef;
  240     $log_file       = undef;
  241     $skip_locking   = 0;
  242     $skip_stats     = 0;
  243     $stdout         = 0;
  244 
  245     $size_x         = undef;
  246     $size_y         = undef;
  247     $full_size_mode = undef;
  248     $only_graph     = undef;
  249     $lower_limit    = undef;
  250     $upper_limit    = undef;
  251 
  252     # Get options
  253     my ($args) = @_;
  254     local @ARGV = @{$args};
  255 
  256     # NOTE!  Some of these options are available in graph_main too
  257     # if you make changes here, make them there too.
  258 
  259     my $debug;
  260     &print_usage_and_exit
  261         unless GetOptions (
  262                 "force!"        => \$force_graphing,
  263                 "lazy!"         => \$force_lazy,
  264                 "host=s"        => \@limit_hosts,
  265                 "service=s"     => \@limit_services,
  266                 "only-fqn=s"    => sub{ $only_fqn = process_fqn(@_); },
  267                 "config=s"      => \$conffile,
  268                 "stdout!"       => \$stdout,
  269                 "force-run-as-root!" => \$force_run_as_root,
  270                 "day!"          => \$draw{'day'},
  271                 "week!"         => \$draw{'week'},
  272                 "month!"        => \$draw{'month'},
  273                 "year!"         => \$draw{'year'},
  274                 "pinpoint=s"    => sub{ process_pinpoint(\$pinpoint,@_); },
  275                 "sumweek!"      => \$draw{'sumweek'},
  276                 "sumyear!"      => \$draw{'sumyear'},
  277         "size_x=i"      => \$size_x,
  278         "size_y=i"      => \$size_y,
  279         "full_size_mode!"=> \$full_size_mode,
  280         "only_graph!"=> \$only_graph,
  281         "upper_limit=s" => \$upper_limit,
  282         "lower_limit=s" => \$lower_limit,
  283                 "list-images!"  => \$list_images,
  284                 "o|output-file=s"  => \$output_file,
  285                 "l|log-file=s"  => \$log_file,
  286                 "skip-locking!" => \$skip_locking,
  287                 "skip-stats!"   => \$skip_stats,
  288                 "version!"      => \$do_version,
  289                 "cron!"         => \$cron,
  290                 "fork!"         => \$do_fork,
  291                 "n=n"           => \$max_running,
  292                 "help"          => \$do_usage,
  293                 "debug!"        => \$debug,
  294         );
  295 
  296     if ($do_version) {
  297         print_version_and_exit();
  298     }
  299 
  300     if ($do_usage) {
  301       print_usage_and_exit();
  302     }
  303 
  304     exit_if_run_by_super_user() unless $force_run_as_root;
  305 
  306     # Only read $config once (thx Jani M.)
  307     #
  308     # FIXME - the loaded $config is stale within 5 minutes.
  309     # we either need to die or restart ourselves when this
  310     # happens.
  311     if (!defined($config)) {
  312     munin_readconfig_base($conffile);
  313     # XXX: check if it needs datafile at that point
  314     $config = munin_readconfig_part('datafile', 0);
  315     }
  316 
  317     $config->{debug} = $debug;
  318 
  319     my $palette = &munin_get($config, "palette", "default");
  320 
  321     $max_running = &munin_get($config, "max_graph_jobs", $max_running);
  322 
  323     if ($config->{"rrdcached_socket"}) {
  324         if ($RRDs::VERSION >= 1.3){
  325         # Using the RRDCACHED_ADDRESS environnement variable, as
  326                 # it is way less intrusive than the command line args.
  327                 $ENV{RRDCACHED_ADDRESS} = $config->{"rrdcached_socket"};
  328         } else {
  329             ERROR "[ERROR] RRDCached feature ignored: RRD version must be at least 1.3. Version found: " . $RRDs::VERSION;
  330         }
  331     }
  332 
  333 
  334     if ($max_running == 0) {
  335         $do_fork = 0;
  336     }
  337 
  338     if (defined($PALETTE{$palette})) {
  339         @COLOUR = @{$PALETTE{$palette}};
  340     }
  341     else {
  342         die "Unknown palette named by 'palette' keyword: $palette\n";
  343     }
  344 
  345     return $config;
  346 }
  347 
  348 sub graph_check_cron {
  349 
  350     # Are we running from cron and do we have matching graph_strategy
  351     if (&munin_get($config, "graph_strategy", "cron") ne "cron" and $cron) {
  352 
  353         # Strategy mismatch: We're run from cron, but munin.conf says
  354         # we use dynamic graph generation
  355         return 0;
  356     }
  357 
  358     # Strategy match:
  359     return 1;
  360 }
  361 
  362 
  363 sub graph_main {
  364 
  365     my ($args) = @_;
  366     local @ARGV = @{$args};
  367 
  368     # The loaded $config is stale within 5 minutes.
  369     # So, we need to reread it when this happens.
  370     $config = munin_readconfig_part('datafile');
  371 
  372     # Reset an eventual custom size
  373     $size_x         = undef;
  374     $size_y         = undef;
  375     $full_size_mode = undef;
  376     $only_graph     = undef;
  377     $lower_limit    = undef;
  378     $upper_limit    = undef;
  379     $pinpoint       = undef;
  380 
  381     # XXX [DEBUG]
  382     my $debug = undef;
  383 
  384     GetOptions (
  385                 "host=s"        => \@limit_hosts,
  386                 "only-fqn=s"    => sub { $only_fqn = process_fqn(@_); },
  387                 "day!"          => \$draw{'day'},
  388                 "week!"         => \$draw{'week'},
  389                 "month!"        => \$draw{'month'},
  390                 "year!"         => \$draw{'year'},
  391                 "pinpoint=s"    => sub{ process_pinpoint(\$pinpoint,@_); },
  392                 "sumweek!"      => \$draw{'sumweek'},
  393                 "sumyear!"      => \$draw{'sumyear'},
  394                 "o|output-file=s"  => \$output_file,
  395 
  396                 # XXX [DEBUG]
  397                 "debug!"  => \$debug,
  398 
  399         "size_x=i"      => \$size_x,
  400         "size_y=i"      => \$size_y,
  401         "full_size_mode!"=> \$full_size_mode,
  402         "only_graph!"   => \$only_graph,
  403         "upper_limit=s" => \$upper_limit,
  404         "lower_limit=s" => \$lower_limit,
  405         );
  406 
  407     # XXX [DEBUG]
  408     logger_debug() if $debug;
  409 
  410     my $graph_time = Time::HiRes::time;
  411 
  412     munin_runlock("$config->{rundir}/munin-graph.lock") unless $skip_locking;
  413 
  414     unless ($skip_stats) {
  415         open($STATS, '>', "$config->{dbdir}/munin-graph.stats.tmp")
  416             or WARN "[WARNING] Unable to open $config->{dbdir}/munin-graph.stats.tmp";
  417         autoflush $STATS 1;
  418     }
  419 
  420     process_work(@limit_hosts);
  421 
  422     $graph_time = sprintf("%.2f", (Time::HiRes::time - $graph_time));
  423 
  424     rename(
  425         "$config->{dbdir}/munin-graph.stats.tmp",
  426         "$config->{dbdir}/munin-graph.stats"
  427     );
  428     close $STATS unless $skip_stats;
  429 
  430     munin_removelock("$config->{rundir}/munin-graph.lock") unless $skip_locking;
  431 
  432     $running = wait_for_remaining_children($running);
  433 }
  434 
  435 
  436 # --------------------------------------------------------------------------
  437 
  438 sub get_title {
  439     my $service = shift;
  440     my $scale   = shift;
  441 
  442     my $scale_text;
  443     if ($pinpoint) {
  444         my $start_text = localtime($pinpoint->{"start"});
  445         my $end_text = localtime($pinpoint->{"end"});
  446         $scale_text = "from $start_text to $end_text";
  447     } else {
  448         $scale_text = "by " . $scale;
  449     }
  450 
  451     my $title = munin_get($service, "graph_title", $service);
  452 
  453     # Substitute ${graph_period} in title
  454     my $period = munin_get($service, "graph_period", "second");
  455     $title =~ s/\$\{graph_period\}/$period/g;
  456 
  457     return ("$title - $scale_text");
  458 }
  459 
  460 sub get_custom_graph_args {
  461     my $service = shift;
  462     my $result  = [];
  463 
  464     my $args = munin_get($service, "graph_args");
  465     if (defined $args) {
  466         my $result = [ grep /\S/, &quotewords('\s+', 0, $args) ];
  467         return $result;
  468     }
  469     else {
  470         return;
  471     }
  472 }
  473 
  474 # insert these arguments after all others
  475 # needed for your own VDEF/CDEF/DEF combinations
  476 sub get_custom_graph_args_after {
  477     my $service = shift;
  478     my $result  = [];
  479 
  480     my $args = munin_get($service, "graph_args_after");
  481     if (defined $args) {
  482         my $result = [&quotewords('\s+', 0, $args)];
  483         return $result;
  484     }
  485     else {
  486         return;
  487     }
  488 }
  489 
  490 # set a graph end point in the future
  491 # needed for CDEF TREND and PREDICT
  492 sub get_end_offset {
  493     my $service = shift;
  494 
  495     # get number of seconds in future
  496     return munin_get($service, "graph_future", 0);
  497 }
  498 
  499 sub get_vlabel {
  500     my $service = shift;
  501     my $scale   = munin_get($service, "graph_period", "second");
  502     my $res     = munin_get($service, "graph_vlabel",
  503         munin_get($service, "graph_vtitle"));
  504 
  505     if (defined $res) {
  506         $res =~ s/\$\{graph_period\}/$scale/g;
  507     }
  508     return $res;
  509 }
  510 
  511 sub should_scale {
  512     my $service = shift;
  513     my $ret;
  514 
  515     if (!defined($ret = munin_get_bool($service, "graph_scale"))) {
  516         $ret = !munin_get_bool($service, "graph_noscale", 0);
  517     }
  518 
  519     return $ret;
  520 }
  521 
  522 sub get_header {
  523     my $service = shift;
  524     my $scale   = shift;
  525     my $sum     = shift;
  526     my $result  = [];
  527     my $tmp_field;
  528 
  529     # Picture filename
  530     push @$result, get_picture_filename($service, $scale, $sum || undef);
  531 
  532     # Title
  533     push @$result, ("--title", get_title($service, $scale));
  534 
  535     # When to start the graph
  536     if ($pinpoint) {
  537         push @$result, "--start", $pinpoint->{start};
  538         push @$result, "--end", $pinpoint->{end};
  539     } else {
  540         push @$result, "--start", $times{$scale};
  541     }
  542 
  543     # Custom graph args, vlabel and graph title
  544     if (defined($tmp_field = get_custom_graph_args($service))) {
  545         push(@$result, @{$tmp_field});
  546     }
  547     if (defined($tmp_field = get_vlabel($service))) {
  548         push @$result, ("--vertical-label", $tmp_field);
  549     }
  550 
  551     push @$result, '--slope-mode' if $RRDs::VERSION >= 1.2;
  552 
  553     push @$result, "--height", ($size_y || munin_get($service, "graph_height", "175"));
  554     push @$result, "--width",  ($size_x || munin_get($service, "graph_width",  "400"));
  555 
  556     push @$result, "--full-size-mode" if ($full_size_mode);
  557     push @$result, "--only-graph" if ($only_graph);
  558 
  559     push @$result,"--rigid" if (defined $lower_limit || defined $upper_limit);
  560 
  561     push @$result, "--imgformat", "PNG";
  562     push @$result, "--lazy" if ($force_lazy);
  563 
  564     push(@$result, "--units-exponent", "0") if (!should_scale($service));
  565 
  566     return $result;
  567 }
  568 
  569 sub get_sum_command {
  570     my $field = shift;
  571     return munin_get($field, "sum");
  572 }
  573 
  574 sub get_stack_command {
  575     my $field = shift;
  576     return munin_get($field, "stack");
  577 }
  578 
  579 sub expand_specials {
  580     my $service = shift;
  581     my $order   = shift;
  582 
  583     my $preproc = [];
  584     my $single  ;
  585 
  586     # Test if already expanded
  587     {
  588         my $cached = $service->{"#%#expand_specials"};
  589     if (defined $cached) {
  590         DEBUG "[DEBUG] expand_specials(): already processed " . munin_dumpconfig_as_str($cached);
  591             return $cached;
  592     }
  593     DEBUG "[DEBUG] expand_specials(): not processed, proceeding for " . munin_dumpconfig_as_str($service);
  594     }
  595 
  596     # we have to compute the result;
  597     my $result = [];
  598 
  599     my $fieldnum = 0;
  600 
  601     for my $field (@$order) {    # Search for 'specials'...
  602         my $tmp_field;
  603 
  604         if ($field =~ /^-(.+)$/) {    # Invisible field
  605             $field = $1;
  606             munin_set_var_loc($service, [$field, "graph"], "no");
  607         }
  608 
  609         $fieldnum++;
  610         if ($field =~ /^([^=]+)=(.+)$/) {    # Aliased in graph_order
  611             my $fname = $1;
  612             my $spath = $2;
  613             my $src   = munin_get_node_partialpath($service, $spath);
  614             my $sname = munin_get_node_name($src);
  615 
  616             if(!defined $src) {
  617             ERROR "[ERROR] Failed to find $fname source at $spath, skipping field";
  618         next;
  619         }
  620             DEBUG "[DEBUG] Copying settings from $sname to $fname.";
  621 
  622             foreach my $foption ("draw", "type", "rrdfile", "fieldname", "info")
  623             {
  624                 if (!defined $service->{$fname}->{$foption}) {
  625                     if (defined $src->{$foption}) {
  626                         munin_set_var_loc($service, [$fname, $foption],
  627                             $src->{$foption});
  628                     }
  629                 }
  630             }
  631 
  632             if (!defined $service->{$fname}->{"label"}) {
  633                 munin_set_var_loc($service, [$fname, "label"], $fname);
  634             }
  635             munin_set_var_loc(
  636                 $service,
  637                 [$fname, "filename"],
  638                 munin_get_rrd_filename($src));
  639 
  640         }
  641         elsif (defined($tmp_field = get_stack_command($service->{$field}))) {
  642         # Aliased with .stack
  643             DEBUG "[DEBUG] expand_specials ($tmp_field): Doing stack...";
  644 
  645             my @spc_stack = ();
  646             foreach my $pre (split(/\s+/, $tmp_field)) {
  647                 (my $name = $pre) =~ s/=.+//;
  648 
  649                 # Auto selects the .draw
  650                 my $draw = (!@spc_stack) ? munin_get($service->{$field}, "draw", "LINE1") : "STACK";
  651                 munin_set_var_loc($service, [$name, "draw"], $draw);
  652 
  653                 # Don't process this field later
  654                 munin_set_var_loc($service, [$field, "process"], "0");
  655 
  656                 push(@spc_stack, $name);
  657                 push(@$preproc,  $pre);
  658                 push @$result, "$name.label";
  659                 push @$result, "$name.draw";
  660                 push @$result, "$name.cdef";
  661 
  662                 munin_set_var_loc($service, [$name, "label"], $name);
  663                 munin_set_var_loc($service, [$name, "cdef"], "$name,UN,0,$name,IF");
  664                 if (munin_get($service->{$field}, "cdef")
  665                     and !munin_get_bool($service->{$name}, "onlynullcdef", 0)) {
  666                     DEBUG "[DEBUG] NotOnlynullcdef ($field)...";
  667                     $service->{$name}->{"cdef"} .= "," . $service->{$field}->{"cdef"};
  668                     $service->{$name}->{"cdef"} =~ s/\b$field\b/$name/g;
  669                 }
  670                 else {
  671                     DEBUG "[DEBUG] Onlynullcdef ($field)...";
  672                     munin_set_var_loc($service, [$name, "onlynullcdef"], 1);
  673                     push @$result, "$name.onlynullcdef";
  674                 }
  675             }
  676         } # if get_stack_command
  677         elsif (defined($tmp_field = get_sum_command($service->{$field}))) {
  678             my @spc_stack = ();
  679             my $last_name = "";
  680             DEBUG "[DEBUG] expand_specials ($tmp_field): Doing sum...";
  681 
  682             if (@$order == 1
  683                 or (@$order == 2 and munin_get($field, "negative", 0))) {
  684                 $single = 1;
  685             }
  686 
  687             foreach my $pre (split(/\s+/, $tmp_field)) {
  688                 (my $path = $pre) =~ s/.+=//;
  689                 my $name = "z" . $fieldnum . "_" . scalar(@spc_stack);
  690                 $last_name = $name;
  691 
  692                 munin_set_var_loc($service, [$name, "cdef"],
  693                     "$name,UN,0,$name,IF");
  694                 munin_set_var_loc($service, [$name, "graph"], "no");
  695                 munin_set_var_loc($service, [$name, "label"], $name);
  696                 push @$result, "$name.cdef";
  697                 push @$result, "$name.graph";
  698                 push @$result, "$name.label";
  699 
  700                 push(@spc_stack, $name);
  701                 push(@$preproc,  "$name=$pre");
  702             }
  703             $service->{$last_name}->{"cdef"} .=
  704         "," . join(",$AddNAN,", @spc_stack[0 .. @spc_stack - 2]) .
  705         ",$AddNAN";
  706 
  707             if (my $tc = munin_get($service->{$field}, "cdef", 0))
  708             {    # Oh bugger...
  709                 DEBUG "[DEBUG] Oh bugger...($field)...\n";
  710                 $tc =~ s/\b$field\b/$service->{$last_name}->{"cdef"}/;
  711                 $service->{$last_name}->{"cdef"} = $tc;
  712             }
  713             munin_set_var_loc($service, [$field, "process"], "0");
  714             munin_set_var_loc(
  715                 $service,
  716                 [$last_name, "draw"],
  717                 munin_get($service->{$field}, "draw"));
  718             munin_set_var_loc(
  719                 $service,
  720                 [$last_name, "colour"],
  721                 munin_get($service->{$field}, "colour"));
  722             munin_set_var_loc(
  723                 $service,
  724                 [$last_name, "label"],
  725                 munin_get($service->{$field}, "label"));
  726             munin_set_var_loc(
  727                 $service,
  728                 [$last_name, "graph"],
  729                 munin_get($service->{$field}, "graph", "yes"));
  730 
  731             if (my $tmp = munin_get($service->{$field}, "negative")) {
  732                 munin_set_var_loc($service, [$last_name, "negative"], $tmp);
  733             }
  734 
  735             munin_set_var_loc($service, [$field, "realname"], $last_name);
  736 
  737         }
  738         elsif (my $nf = munin_get($service->{$field}, "negative", 0)) {
  739             if (  !munin_get_bool($service->{$nf}, "graph", 1)
  740                 or munin_get_bool($service->{$nf}, "skipdraw", 0)) {
  741                 munin_set_var_loc($service, [$nf, "graph"], "no");
  742             }
  743         }
  744     } # for (@$order)
  745 
  746     # Return & save it for future use
  747     $service->{"#%#expand_specials"} = {
  748     "added" => $result,
  749     "preprocess" => $preproc,
  750     "single" => $single,
  751     };
  752     return $service->{"#%#expand_specials"};
  753 }
  754 
  755 
  756 sub single_value {
  757     my $service = shift;
  758 
  759     my $graphable = munin_get($service, "graphable", 0);
  760     if (!$graphable) {
  761         foreach my $field (@{munin_get_field_order($service)}) {
  762             DEBUG "[DEBUG] single_value: Checking field \"$field\".";
  763             $graphable++ if munin_draw_field($service->{$field});
  764         }
  765         munin_set_var_loc($service, ["graphable"], $graphable);
  766     }
  767     DEBUG "[DEBUG] service "
  768       . join(' :: ', @{munin_get_node_loc($service)})
  769     . " has $graphable elements.";
  770     return ($graphable == 1);
  771 }
  772 
  773 
  774 sub get_field_name {
  775     my $name = shift;
  776 
  777     $name = substr(Digest::MD5::md5_hex($name), -15)
  778         if (length $name > 15);
  779 
  780     return $name;
  781 }
  782 
  783 
  784 sub process_work {
  785     my (@hosts) = @_;
  786 
  787     # Make array of what is probably needed to graph
  788 
  789     my $work_array = [];
  790 
  791     if ($only_fqn) {
  792     push @$work_array, munin_find_node_by_fqn($config,$only_fqn);
  793     } elsif (@hosts) {
  794         foreach my $nodename (@hosts) {
  795             push @$work_array,
  796                 map {@{munin_find_field($_->{$nodename}, "graph_title")}}
  797                 @{munin_find_field($config, $nodename)};
  798         }
  799     } else {
  800     FATAL "[FATAL] In process_work, no fqn and no hosts!";
  801     }
  802 
  803     # @$work_array contains copy of (or pointer to) each service to be graphed.
  804     for my $service (@$work_array) {
  805 
  806         # Want to avoid forking for that
  807         next if (skip_service($service));
  808 
  809         # Fork (or not) and run the anonymous sub afterwards.
  810         fork_and_work(sub {process_service($service);});
  811     }
  812 }
  813 
  814 
  815 sub process_field {
  816     my $field = shift;
  817     return munin_get_bool($field, "process", 1);
  818 }
  819 
  820 
  821 sub fork_and_work {
  822     my ($work) = @_;
  823 
  824     if (!$do_fork) {
  825 
  826         # We're not forking.  Do work and return.
  827         DEBUG "[DEBUG] Doing work synchronously";
  828         &$work;
  829         return;
  830     }
  831 
  832     # Make sure we don't fork too much
  833     while ($running >= $max_running) {
  834         DEBUG
  835             "[DEBUG] Too many forks ($running/$max_running), wait for something to get done";
  836         look_for_child("block");
  837         --$running;
  838     }
  839 
  840     my $pid = fork();
  841 
  842     if (!defined $pid) {
  843         ERROR "[ERROR] fork failed: $!";
  844         die "fork failed: $!";
  845     }
  846 
  847     if ($pid == 0) {
  848 
  849         # This block does the real work.  Since we're forking exit
  850         # afterwards.
  851 
  852         &$work;
  853 
  854         # See?!
  855 
  856         exit 0;
  857 
  858     }
  859     else {
  860         ++$running;
  861         DEBUG "[DEBUG] Forked: $pid. Now running $running/$max_running";
  862         while ($running and look_for_child()) {
  863             --$running;
  864         }
  865     }
  866 }
  867 
  868 sub remove_dups {
  869     my @ret;
  870     my %keys;
  871     for my $order (@_) {
  872                 (my $name = $order) =~ s/=.+//;
  873         push @ret, $order unless ($keys{$name} ++);
  874     }
  875 
  876     return @ret;
  877 }
  878 
  879 sub _sanitise_fieldname {
  880     # http://munin-monitoring.org/wiki/notes_on_datasource_names
  881     my ($name) = @_;
  882 
  883     $name =~ s/^[^A-Za-z_]/_/;
  884     $name =~ s/[^A-Za-z0-9_]/_/g;
  885 
  886     return $name;
  887 }
  888 
  889 sub process_service {
  890     my ($service) = @_;
  891 
  892     # See if we should skip the service
  893     return if (skip_service($service));
  894 
  895     # Make my graphs
  896     my $sname        = munin_get_node_name($service);
  897     my $skeypath     = munin_get_keypath($service);
  898     my $service_time = Time::HiRes::time;
  899     my $lastupdate   = 0;
  900     my $now          = time;
  901     my $fnum         = 0;
  902     my @rrd;
  903 
  904     DEBUG "[DEBUG] Node name: $sname\n";
  905 
  906     my $field_count   = 0;
  907     my $max_field_len = 0;
  908     my @field_order   = ();
  909     my $rrdname;
  910 
  911     @field_order = @{munin_get_field_order($service)};
  912 
  913     # Array to keep 'preprocess'ed fields.
  914     DEBUG "[DEBUG] Expanding specials for $sname: \""
  915         . join("\",\"", @field_order) . "\".";
  916 
  917     my $expanded_result = expand_specials($service, \@field_order);
  918     my $force_single_value = $expanded_result->{single};
  919     my @added =  @{ $expanded_result->{added} };
  920 
  921     # put preprocessed fields in front
  922     unshift @field_order, @{ $expanded_result->{preprocess} };
  923 
  924     # Remove duplicates, while retaining the order
  925     @field_order = remove_dups ( @field_order );
  926 
  927     # Get max label length
  928     DEBUG "[DEBUG] Checking field lengths for $sname: \"" . join('","', @field_order) . '".';
  929     $max_field_len = munin_get_max_label_length($service, \@field_order);
  930 
  931     # Global headers makes the value tables easier to read no matter how
  932     # wide the labels are.
  933     my $global_headers = 1;
  934 
  935     # Default format for printing under graph.
  936     my $avgformat;
  937     my $rrdformat = $avgformat = "%6.2lf";
  938 
  939     if (munin_get($service, "graph_args", "") =~ /--base\s+1024/) {
  940 
  941         # If the base unit is 1024 then 1012.56 is a valid
  942         # number to show.  That's 7 positions, not 6.
  943         $rrdformat = $avgformat = "%7.2lf";
  944     }
  945 
  946     # Plugin specified complete printf format
  947     $rrdformat = munin_get($service, "graph_printf", $rrdformat);
  948 
  949     my $rrdscale = '';
  950     if (munin_get_bool($service, "graph_scale", 1)) {
  951         $rrdscale = '%s';
  952     }
  953 
  954     # Array to keep negative data until we're finished with positive.
  955     my @rrd_negatives = ();
  956 
  957     my $filename;
  958     my %total_pos;
  959     my %total_neg;
  960     my $autostacking = 0;
  961 
  962     DEBUG "[DEBUG] Treating fields \"" . join("\",\"", @field_order) . "\".";
  963     for my $fname (@field_order) {
  964         my $path  = undef;
  965         my $field = undef;
  966 
  967         if ($fname =~ s/=(.+)//) {
  968             $path = $1;
  969         }
  970         $field = munin_get_node($service, [$fname]);
  971 
  972         next if (!defined $field or !$field or !process_field($field));
  973         DEBUG "[DEBUG] Processing field \"$fname\" ["
  974             . munin_get_node_name($field) . "].";
  975 
  976         my $fielddraw = munin_get($field, "draw", "LINE1");
  977 
  978         if ($field_count == 0 and $fielddraw eq 'STACK') {
  979 
  980             # Illegal -- first field is a STACK
  981             DEBUG "ERROR: First field (\"$fname\") of graph "
  982                 . join(' :: ', munin_get_node_loc($service))
  983                 . " is STACK. STACK can only be drawn after a LINEx or AREA.";
  984             $fielddraw = "LINE1";
  985         }
  986 
  987         if ($fielddraw eq 'AREASTACK') {
  988             if ($autostacking == 0) {
  989                 $fielddraw    = 'AREA';
  990                 $autostacking = 1;
  991             }
  992             else {
  993                 $fielddraw = 'STACK';
  994             }
  995         }
  996 
  997         if ($fielddraw =~ /LINESTACK(|\d+|\d+\.\d+)$/) {
  998             if ($autostacking == 0) {
  999                 $fielddraw    = "LINE$1";
 1000                 $autostacking = 1;
 1001             }
 1002             else {
 1003                 $fielddraw = 'STACK';
 1004             }
 1005         }
 1006 
 1007         # Getting name of rrd file
 1008         $filename = munin_get_rrd_filename($field, $path);
 1009     if (! $filename) {
 1010         ERROR "[ERROR] filename is empty for " . munin_dumpconfig_as_str($field) . ", $path";
 1011         # Ignore this field
 1012         next;
 1013     }
 1014 
 1015     if(!defined $filename) {
 1016         ERROR "[ERROR] Failed getting filename for $path, skipping field";
 1017         next;
 1018     }
 1019     # Here it is OK to flush the rrdcached, since we'll flush it anyway
 1020     # with graph
 1021         my $update = RRDs::last(@rrdcached_params, $filename);
 1022         $update = 0 if !defined $update;
 1023         if ($update > $lastupdate) {
 1024             $lastupdate = $update;
 1025         }
 1026 
 1027         # It does not look like $fieldname.rrdfield is possible to set
 1028         my $rrdfield = munin_get($field, "rrdfield", "42");
 1029 
 1030         my $single_value = $force_single_value || single_value($service);
 1031     
 1032     # XXX - single_value is wrong for some multigraph, disabling it for now
 1033     $single_value = 0;
 1034 
 1035         my $has_negative = munin_get($field, "negative");
 1036 
 1037         # Trim the fieldname to make room for other field names.
 1038     
 1039         $rrdname = &get_field_name($fname);
 1040 
 1041         reset_cdef($service, $rrdname);
 1042         if ($rrdname ne $fname) {
 1043 
 1044             # A change was made
 1045             munin_set($field, "cdef_name", $rrdname);
 1046         }
 1047 
 1048         # Push will place the DEF too far down for some CDEFs to work
 1049         unshift(@rrd, "DEF:g$rrdname=" . $filename . ":" . $rrdfield . ":AVERAGE");
 1050         unshift(@rrd, "DEF:i$rrdname=" . $filename . ":" . $rrdfield . ":MIN");
 1051         unshift(@rrd, "DEF:a$rrdname=" . $filename . ":" . $rrdfield . ":MAX");
 1052 
 1053         if (munin_get_bool($field, "onlynullcdef", 0)) {
 1054             push(@rrd,
 1055                 "CDEF:c$rrdname=g$rrdname"
 1056                     . (($now - $update) > 900 ? ",POP,UNKN" : ""));
 1057         }
 1058 
 1059         if (    munin_get($field, "type", "GAUGE") ne "GAUGE"
 1060             and graph_by_minute($service)) {
 1061             push(@rrd, expand_cdef($service, \$rrdname, "$fname,60,*"));
 1062         }
 1063         if (    munin_get($field, "type", "GAUGE") ne "GAUGE"
 1064             and graph_by_hour($service)) {
 1065             push(@rrd, expand_cdef($service, \$rrdname, "$fname,3600,*"));
 1066         }
 1067 
 1068         if (my $tmpcdef = munin_get($field, "cdef")) {
 1069             push(@rrd, expand_cdef($service, \$rrdname, $tmpcdef));
 1070             push(@rrd, "CDEF:c$rrdname=g$rrdname");
 1071             DEBUG "[DEBUG] Field name after cdef set to $rrdname";
 1072         }
 1073         elsif (!munin_get_bool($field, "onlynullcdef", 0)) {
 1074             push(@rrd,
 1075                 "CDEF:c$rrdname=g$rrdname"
 1076                     . (($now - $update) > 900 ? ",POP,UNKN" : ""));
 1077         }
 1078 
 1079         next if !munin_draw_field($field);
 1080         DEBUG "[DEBUG] Drawing field \"$fname\".";
 1081 
 1082         if ($single_value) {
 1083 
 1084             # Only one field. Do min/max range.
 1085             push(@rrd, "CDEF:min_max_diff=a$rrdname,i$rrdname,-");
 1086             push(@rrd, "CDEF:re_zero=min_max_diff,min_max_diff,-")
 1087                 if !munin_get($field, "negative");
 1088 
 1089             push(@rrd, "AREA:i$rrdname#ffffff");
 1090             push(@rrd, "STACK:min_max_diff#$range_colour");
 1091             push(@rrd, "LINE1:re_zero#000000")
 1092                 if !munin_get($field, "negative");
 1093         }
 1094 
 1095         # Push "global" headers or not
 1096         if ($has_negative and !@rrd_negatives and $global_headers < 2) {
 1097 
 1098             # Always for -/+ graphs
 1099             push(@rrd, "COMMENT:" . (" " x $max_field_len));
 1100             push(@rrd, "COMMENT:Cur (-/+)");
 1101             push(@rrd, "COMMENT:Min (-/+)");
 1102             push(@rrd, "COMMENT:Avg (-/+)");
 1103             push(@rrd, "COMMENT:Max (-/+) \\j");
 1104             $global_headers = 2;    # Avoid further headers/labels
 1105         }
 1106         elsif ($global_headers == 1) {
 1107 
 1108             # Or when we want to.
 1109             push(@rrd, "COMMENT:" . (" " x $max_field_len));
 1110             push(@rrd, "COMMENT: Cur$RRDkludge:");
 1111             push(@rrd, "COMMENT:Min$RRDkludge:");
 1112             push(@rrd, "COMMENT:Avg$RRDkludge:");
 1113             push(@rrd, "COMMENT:Max$RRDkludge:  \\j");
 1114             $global_headers = 2;    # Avoid further headers/labels
 1115         }
 1116 
 1117         my $colour = munin_get($field, "colour");
 1118 
 1119     if ($colour && $colour =~ /^COLOUR(\d+)$/) {
 1120         $colour = $COLOUR[$1 % @COLOUR];
 1121     }
 1122     
 1123     # Select a default colour if no explict one
 1124     $colour ||= ($single_value) ? $single_colour : $COLOUR[$field_count % @COLOUR];
 1125         my $warn_colour = $single_value ? "ff0000" : $colour;
 1126 
 1127         # colour needed for transparent predictions and trends
 1128         munin_set($field, "colour", $colour);
 1129         $field_count++;
 1130 
 1131         my $tmplabel = munin_get($field, "label", $fname);
 1132 
 1133     # Substitute ${graph_period}
 1134     my $period  = munin_get($service, "graph_period", "second");
 1135     $tmplabel =~ s/\$\{graph_period\}/$period/g;
 1136 
 1137         push(@rrd,
 1138                   $fielddraw
 1139                 . ":g$rrdname"
 1140                 . "#$colour" . ":"
 1141                 . escape($tmplabel)
 1142                 . (" " x ($max_field_len + 1 - length $tmplabel)));
 1143 
 1144         # Check for negative fields (typically network (or disk) traffic)
 1145         if ($has_negative) {
 1146             my $negfieldname
 1147                 = orig_to_cdef($service, munin_get($field, "negative"));
 1148             my $negfield = $service->{_sanitise_fieldname(munin_get($field, "negative"))};
 1149             if (my $tmpneg = munin_get($negfield, "realname")) {
 1150                 $negfieldname = $tmpneg;
 1151                 $negfield     = $service->{$negfieldname};
 1152             }
 1153 
 1154             if (!@rrd_negatives) {
 1155 
 1156                 # zero-line, to redraw zero afterwards.
 1157                 push(@rrd_negatives, "CDEF:re_zero=g$negfieldname,UN,0,0,IF");
 1158             }
 1159 
 1160             push(@rrd_negatives, "CDEF:ng$negfieldname=g$negfieldname,-1,*");
 1161 
 1162             if ($single_value) {
 1163 
 1164                 # Only one field. Do min/max range.
 1165                 push(@rrd,
 1166                     "CDEF:neg_min_max_diff=i$negfieldname,a$negfieldname,-");
 1167                 push(@rrd, "CDEF:ni$negfieldname=i$negfieldname,-1,*");
 1168                 push(@rrd, "AREA:ni$negfieldname#ffffff");
 1169                 push(@rrd, "STACK:neg_min_max_diff#$range_colour");
 1170             }
 1171 
 1172             push(@rrd_negatives, $fielddraw . ":ng$negfieldname#$colour");
 1173 
 1174             # Draw HRULEs
 1175             my $linedef = munin_get($negfield, "line");
 1176             if ($linedef) {
 1177                 my ($number, $ldcolour, $label) = split(/:/, $linedef, 3);
 1178                 unshift(@rrd_negatives,
 1179                     "HRULE:" . $number . ($ldcolour ? "#$ldcolour" : "#$colour"));
 1180             }
 1181             elsif (my $tmpwarn = munin_get($negfield, "warning")) {
 1182 
 1183                 my ($warn_min, $warn_max) = split(':', $tmpwarn,2);
 1184 
 1185                 if (defined($warn_min) and $warn_min ne '') {
 1186                     unshift(@rrd, "HRULE:${warn_min}#${warn_colour}${RRDLineThresholdAttribute}");
 1187                 }
 1188                 if (defined($warn_max) and $warn_max ne '') {
 1189                     unshift(@rrd, "HRULE:${warn_max}#${warn_colour}${RRDLineThresholdAttribute}");
 1190                 }
 1191             }
 1192 
 1193             push(@rrd,
 1194                 "GPRINT:c$negfieldname:LAST:$rrdformat" . $rrdscale . "/\\g");
 1195             push(@rrd, "GPRINT:c$rrdname:LAST:$rrdformat" . $rrdscale . "");
 1196             push(@rrd,
 1197                 "GPRINT:i$negfieldname:MIN:$rrdformat" . $rrdscale . "/\\g");
 1198             push(@rrd, "GPRINT:i$rrdname:MIN:$rrdformat" . $rrdscale . "");
 1199             push(@rrd,
 1200                       "GPRINT:g$negfieldname:AVERAGE:$avgformat"
 1201                     . $rrdscale
 1202                     . "/\\g");
 1203             push(@rrd, "GPRINT:g$rrdname:AVERAGE:$avgformat" . $rrdscale . "");
 1204             push(@rrd,
 1205                 "GPRINT:a$negfieldname:MAX:$rrdformat" . $rrdscale . "/\\g");
 1206             push(@rrd, "GPRINT:a$rrdname:MAX:$rrdformat" . $rrdscale . "\\j");
 1207             push(@{$total_pos{'min'}}, "i$rrdname");
 1208             push(@{$total_pos{'avg'}}, "g$rrdname");
 1209             push(@{$total_pos{'max'}}, "a$rrdname");
 1210             push(@{$total_neg{'min'}}, "i$negfieldname");
 1211             push(@{$total_neg{'avg'}}, "g$negfieldname");
 1212             push(@{$total_neg{'max'}}, "a$negfieldname");
 1213         }
 1214         else {
 1215             push(@rrd, "COMMENT: Cur$RRDkludge:") unless $global_headers;
 1216             push(@rrd, "GPRINT:c$rrdname:LAST:$rrdformat" . $rrdscale . "");
 1217             push(@rrd, "COMMENT: Min$RRDkludge:") unless $global_headers;
 1218             push(@rrd, "GPRINT:i$rrdname:MIN:$rrdformat" . $rrdscale . "");
 1219             push(@rrd, "COMMENT: Avg$RRDkludge:") unless $global_headers;
 1220             push(@rrd, "GPRINT:g$rrdname:AVERAGE:$avgformat" . $rrdscale . "");
 1221             push(@rrd, "COMMENT: Max$RRDkludge:") unless $global_headers;
 1222             push(@rrd, "GPRINT:a$rrdname:MAX:$rrdformat" . $rrdscale . "\\j");
 1223             push(@{$total_pos{'min'}}, "i$rrdname");
 1224             push(@{$total_pos{'avg'}}, "g$rrdname");
 1225             push(@{$total_pos{'max'}}, "a$rrdname");
 1226         }
 1227 
 1228         # Draw HRULEs
 1229         my $linedef = munin_get($field, "line");
 1230         if ($linedef) {
 1231             my ($number, $ldcolour, $label) = split(/:/, $linedef, 3);
 1232             $label =~ s/:/\\:/g if defined $label;
 1233             unshift(
 1234                 @rrd,
 1235                 "HRULE:"
 1236                     . $number
 1237                     . (
 1238                     $ldcolour ? "#$ldcolour"
 1239                     : ((defined $single_value and $single_value) ? "#ff0000"
 1240                         : "#$colour"))
 1241                     . ((defined $label and length($label)) ? ":$label" : ""),
 1242                 "COMMENT: \\j"
 1243             );
 1244         }
 1245         elsif (my $tmpwarn = munin_get($field, "warning")) {
 1246 
 1247             my ($warn_min, $warn_max) = split(':', $tmpwarn,2);
 1248 
 1249             if (defined($warn_min) and $warn_min ne '') {
 1250                 unshift(@rrd, "HRULE:${warn_min}#${warn_colour}${RRDLineThresholdAttribute}");
 1251             }
 1252             if (defined($warn_max) and $warn_max ne '') {
 1253                 unshift(@rrd, "HRULE:${warn_max}#${warn_colour}${RRDLineThresholdAttribute}");
 1254             }
 1255         }
 1256     }
 1257 
 1258     my $graphtotal = munin_get($service, "graph_total");
 1259     if (@rrd_negatives) {
 1260         push(@rrd, @rrd_negatives);
 1261         push(@rrd, "LINE1:re_zero#000000");    # Redraw zero.
 1262         if (    defined $graphtotal
 1263             and exists $total_pos{'min'}
 1264             and exists $total_neg{'min'}
 1265             and @{$total_pos{'min'}}
 1266             and @{$total_neg{'min'}}) {
 1267 
 1268             push(@rrd,
 1269                       "CDEF:ipostotal="
 1270                     . join(",", @{$total_pos{'min'}})
 1271                     . (",$AddNAN" x (@{$total_pos{'min'}} - 1)));
 1272             push(@rrd,
 1273                       "CDEF:gpostotal="
 1274                     . join(",", @{$total_pos{'avg'}})
 1275                     . (",$AddNAN" x (@{$total_pos{'avg'}} - 1)));
 1276             push(@rrd,
 1277                       "CDEF:apostotal="
 1278                     . join(",", @{$total_pos{'max'}})
 1279                     . (",$AddNAN" x (@{$total_pos{'max'}} - 1)));
 1280             push(@rrd,
 1281                       "CDEF:inegtotal="
 1282                     . join(",", @{$total_neg{'min'}})
 1283                     . (",$AddNAN" x (@{$total_neg{'min'}} - 1)));
 1284             push(@rrd,
 1285                       "CDEF:gnegtotal="
 1286                     . join(",", @{$total_neg{'avg'}})
 1287                     . (",$AddNAN" x (@{$total_neg{'avg'}} - 1)));
 1288             push(@rrd,
 1289                       "CDEF:anegtotal="
 1290                     . join(",", @{$total_neg{'max'}})
 1291                     . (",$AddNAN" x (@{$total_neg{'max'}} - 1)));
 1292             push(@rrd,
 1293                 "LINE1:gpostotal#000000:$graphtotal"
 1294                     . (" " x ($max_field_len - length($graphtotal) + 1)));
 1295             push(@rrd, "GPRINT:gnegtotal:LAST:$rrdformat" . $rrdscale . "/\\g");
 1296             push(@rrd, "GPRINT:gpostotal:LAST:$rrdformat" . $rrdscale . "");
 1297             push(@rrd, "GPRINT:inegtotal:MIN:$rrdformat" . $rrdscale . "/\\g");
 1298             push(@rrd, "GPRINT:ipostotal:MIN:$rrdformat" . $rrdscale . "");
 1299             push(@rrd,
 1300                 "GPRINT:gnegtotal:AVERAGE:$avgformat" . $rrdscale . "/\\g");
 1301             push(@rrd, "GPRINT:gpostotal:AVERAGE:$avgformat" . $rrdscale . "");
 1302             push(@rrd, "GPRINT:anegtotal:MAX:$rrdformat" . $rrdscale . "/\\g");
 1303             push(@rrd, "GPRINT:apostotal:MAX:$rrdformat" . $rrdscale . "\\j");
 1304         }
 1305     }
 1306     elsif ( defined $graphtotal
 1307         and exists $total_pos{'min'}
 1308         and @{$total_pos{'min'}}) {
 1309         push(@rrd,
 1310                   "CDEF:ipostotal="
 1311                 . join(",", @{$total_pos{'min'}})
 1312                 . (",$AddNAN" x (@{$total_pos{'min'}} - 1)));
 1313         push(@rrd,
 1314                   "CDEF:gpostotal="
 1315                 . join(",", @{$total_pos{'avg'}})
 1316                 . (",$AddNAN" x (@{$total_pos{'avg'}} - 1)));
 1317         push(@rrd,
 1318                   "CDEF:apostotal="
 1319                 . join(",", @{$total_pos{'max'}})
 1320                 . (",$AddNAN" x (@{$total_pos{'max'}} - 1)));
 1321 
 1322         push(@rrd,
 1323             "LINE1:gpostotal#000000:$graphtotal"
 1324                 . (" " x ($max_field_len - length($graphtotal) + 1)));
 1325         push(@rrd, "COMMENT: Cur$RRDkludge:") unless $global_headers;
 1326         push(@rrd, "GPRINT:gpostotal:LAST:$rrdformat" . $rrdscale . "");
 1327         push(@rrd, "COMMENT: Min$RRDkludge:") unless $global_headers;
 1328         push(@rrd, "GPRINT:ipostotal:MIN:$rrdformat" . $rrdscale . "");
 1329         push(@rrd, "COMMENT: Avg$RRDkludge:") unless $global_headers;
 1330         push(@rrd, "GPRINT:gpostotal:AVERAGE:$avgformat" . $rrdscale . "");
 1331         push(@rrd, "COMMENT: Max$RRDkludge:") unless $global_headers;
 1332         push(@rrd, "GPRINT:apostotal:MAX:$rrdformat" . $rrdscale . "\\j");
 1333     }
 1334         
 1335     # insert these graph args in the end
 1336     if (defined(my $tmp_field = get_custom_graph_args_after($service))) {
 1337         push(@rrd, @{$tmp_field});
 1338     }
 1339 
 1340 
 1341 
 1342     my $nb_graphs_drawn = 0;
 1343     for my $time (keys %times) {
 1344         next unless ($draw{$time});
 1345         my $picfilename = get_picture_filename($service, $time);
 1346     DEBUG "[DEBUG] Looking into drawing $picfilename";
 1347         (my $picdirname = $picfilename) =~ s/\/[^\/]+$//;
 1348 
 1349         DEBUG "[DEBUG] Picture filename: $picfilename";
 1350 
 1351         my @complete = get_fonts();
 1352 
 1353     # Watermarks introduced in RRD 1.2.13.
 1354         push(@complete, '-W', $watermark) if $RRDs::VERSION >= 1.2013;
 1355 
 1356         # Do the header (title, vtitle, size, etc...), but IN THE BEGINNING
 1357         unshift @complete, @{get_header($service, $time)};
 1358 
 1359         if ($LINEkluge) {
 1360             @rrd = map {
 1361                 my $line = $_;
 1362                 $line =~ s/LINE3:/LINE2.2:/;
 1363                 $line =~ s/LINE2:/LINE1.6:/;
 1364 
 1365                 # LINE1 is thin enough.
 1366                 $line;
 1367             } @rrd;
 1368         }
 1369         push @complete, @rrd;
 1370 
 1371         # graph end in future
 1372         push (@complete, handle_trends($time, $lastupdate, $pinpoint, $service, $RRDkludge, @added));
 1373 
 1374         # Make sure directory exists
 1375         munin_mkdir_p($picdirname, oct(777));
 1376 
 1377         # Since version 1.3 rrdtool uses libpango which needs its input
 1378         # as utf8 string. So we assume that every input is in latin1
 1379         # and decode it to perl's internal representation and then to utf8.
 1380 
 1381         if ($RRDs::VERSION >= 1.3) {
 1382             @complete = map {
 1383                 my $str = $_;
 1384                 my $utf8 = guess_encoding($str, 'utf8');
 1385                 ref $utf8 ? $str : encode("utf8", (decode("latin1", $_)));
 1386             } @complete;
 1387         }
 1388 
 1389     # Surcharging the graphing limits
 1390     my ($upper_limit_overrided, $lower_limit_overrided);
 1391     for (my $index = 0; $index <= $#complete; $index++) {
 1392         if ($complete[$index] =~ /^(--upper-limit|-u)$/ && (defined $upper_limit)) {
 1393             $upper_limit = get_scientific($upper_limit);
 1394             $complete[$index + 1] = $upper_limit;
 1395             $upper_limit_overrided = 1;
 1396         }
 1397         if ($complete[$index] =~ /^(--lower-limit|-l)$/ && (defined $lower_limit)) {
 1398             $lower_limit = get_scientific($lower_limit);
 1399             $complete[$index + 1] = $lower_limit;
 1400             $lower_limit_overrided = 1;
 1401         }
 1402     }
 1403 
 1404     # Add the limit if not present
 1405     if (defined $upper_limit && ! $upper_limit_overrided) {
 1406         push @complete, "--upper-limit", $upper_limit;
 1407     }
 1408     if (defined $lower_limit && ! $lower_limit_overrided) {
 1409         push @complete, "--lower-limit", $lower_limit;
 1410     }
 1411 
 1412     DEBUG "\n\nrrdtool 'graph' '" . join("' \\\n\t'", @rrdcached_params, @complete) . "'\n";
 1413     $nb_graphs_drawn ++;
 1414         RRDs::graph(@rrdcached_params, @complete);
 1415         if (my $ERROR = RRDs::error) {
 1416             ERROR "[RRD ERROR] Unable to graph $picfilename : $ERROR";
 1417             # ALWAYS dumps the cmd used when an error occurs.
 1418             # Otherwise, it will be difficult to debug post-mortem
 1419             ERROR "[RRD ERROR] rrdtool 'graph' '" . join("' \\\n\t'", @rrdcached_params, @complete) . "'\n";
 1420         }
 1421         elsif (!-f $picfilename) {
 1422         ERROR "[RRD ERROR] rrdtool graph did not generate the image (make sure there are data to graph).\n";
 1423         }
 1424         else {
 1425 
 1426             # Set time of png file to the time of the last update of
 1427             # the rrd file.  This makes http's If-Modified-Since more
 1428             # reliable, esp. in combination with munin-*cgi-graph.
 1429 
 1430         # Since this disrupts rrd's --lazy option we're disableing
 1431         # it unless we (munin-graph) were specially asked --lazy.
 1432         # This way --lazy continues to work as expected, and since
 1433         # CGI uses --nolazy, http IMS are also working as expected.
 1434             if (! $force_lazy) {
 1435                 DEBUG "[DEBUG] setting time on $picfilename";
 1436                 utime $lastupdate, $lastupdate, $picfilename;
 1437             }
 1438 
 1439             if ($list_images) {
 1440                 # Command-line option to list images created
 1441                 print $picfilename. "\n";
 1442             }
 1443         }
 1444     }
 1445 
 1446     if (munin_get_bool($service, "graph_sums", 0)) {
 1447         foreach my $time (keys %sumtimes) {
 1448             my $picfilename = get_picture_filename($service, $time, 1);
 1449         DEBUG "Looking into drawing $picfilename";
 1450             (my $picdirname = $picfilename) =~ s/\/[^\/]+$//;
 1451             next unless ($draw{"sum" . $time});
 1452             my @rrd_sum;
 1453             push @rrd_sum, @{get_header($service, $time, 1)};
 1454 
 1455             # graph end in future
 1456             push (@rrd_sum, handle_trends($time, $lastupdate, $pinpoint, $service, $RRDkludge, @added));
 1457 
 1458             push @rrd_sum, @rrd;
 1459 
 1460             my $labelled = 0;
 1461             my @defined  = ();
 1462             for (my $index = 0; $index <= $#rrd_sum; $index++) {
 1463                 if ($rrd_sum[$index] =~ /^(--vertical-label|-v)$/) {
 1464                     (my $label = munin_get($service, "graph_vlabel"))
 1465                         =~ s/\$\{graph_period\}/$sumtimes{$time}[0]/g;
 1466                     splice(@rrd_sum, $index, 2, ("--vertical-label", $label));
 1467                     $index++;
 1468                     $labelled++;
 1469                 }
 1470                 elsif ($rrd_sum[$index]
 1471                     =~ /^(LINE[123]|STACK|AREA|GPRINT):([^#:]+)([#:].+)$/) {
 1472                     my ($pre, $fname, $post) = ($1, $2, $3);
 1473                     next if $fname eq "re_zero";
 1474                     if ($post =~ /^:AVERAGE/) {
 1475                         splice(@rrd_sum, $index, 1, $pre . ":x$fname" . $post);
 1476                         $index++;
 1477                         next;
 1478                     }
 1479                     next if grep /^x$fname$/, @defined;
 1480                     push @defined, "x$fname";
 1481                     my @replace;
 1482 
 1483                     if (munin_get($service->{$fname}, "type", "GAUGE") ne
 1484                         "GAUGE") {
 1485                         if ($time eq "week") {
 1486 
 1487                             # Every plot is half an hour. Add two plots and multiply, to get per hour
 1488                             if (graph_by_minute($service)) {
 1489 
 1490                                 # Already multiplied by 60
 1491                                 push @replace,
 1492                                     "CDEF:x$fname=PREV($fname),UN,0,PREV($fname),IF,$fname,+,5,*,6,*";
 1493                             } elsif (graph_by_hour($service)) {
 1494                                 # Already multiplied by 3600, have to *divide* by 12
 1495                                 push @replace,
 1496                                     "CDEF:x$fname=PREV($fname),UN,0,PREV($fname),IF,$fname,+,12,/,6,*";
 1497                             }
 1498                             else {
 1499                                 push @replace,
 1500                                     "CDEF:x$fname=PREV($fname),UN,0,PREV($fname),IF,$fname,+,300,*,6,*";
 1501                             }
 1502                         }
 1503                         else {
 1504 
 1505                             # Every plot is one day exactly. Just multiply.
 1506                             if (graph_by_minute($service)) {
 1507 
 1508                                 # Already multiplied by 60
 1509                                 push @replace, "CDEF:x$fname=$fname,5,*,288,*";
 1510                             } elsif (graph_by_hour($service)) {
 1511                                 # Already multiplied by 3600, have to *divide* by 12
 1512                                 push @replace, "CDEF:x$fname=$fname,12,/,288,*";
 1513                             }
 1514                             else {
 1515                                 push @replace,
 1516                                     "CDEF:x$fname=$fname,300,*,288,*";
 1517                             }
 1518                         }
 1519                     }
 1520                     push @replace, $pre . ":x$fname" . $post;
 1521                     splice(@rrd_sum, $index, 1, @replace);
 1522                     $index++;
 1523                 }
 1524                 elsif (
 1525                     $rrd_sum[$index] =~ /^(--lower-limit|--upper-limit|-l|-u)$/)
 1526                 {
 1527                     $index++;
 1528                     $rrd_sum[$index]
 1529                         = $rrd_sum[$index] * 300 * $sumtimes{$time}->[1];
 1530                 }
 1531             }
 1532 
 1533 
 1534             unless ($labelled) {
 1535                 my $label = munin_get($service, "graph_vlabel_sum_$time",
 1536                     $sumtimes{$time}->[0]);
 1537                 unshift @rrd_sum, "--vertical-label", $label;
 1538             }
 1539 
 1540         DEBUG "[DEBUG] \n\nrrdtool graph '" . join("' \\\n\t'", @rrd_sum) . "'\n";
 1541 
 1542             # Make sure directory exists
 1543             munin_mkdir_p($picdirname, oct(777));
 1544 
 1545         $nb_graphs_drawn ++;
 1546             RRDs::graph(@rrdcached_params, @rrd_sum);
 1547 
 1548             if (my $ERROR = RRDs::error) {
 1549                 ERROR "[RRD ERROR(sum)] Unable to graph "
 1550                     . get_picture_filename($service, $time)
 1551                     . ": $ERROR";
 1552             }
 1553             elsif ($list_images) {
 1554                 # Command-line option to list images created
 1555                 print get_picture_filename ($service, $time, 1), "\n";
 1556             }
 1557         } # foreach (keys %sumtimes)
 1558     } # if graph_sums
 1559 
 1560     $service_time = sprintf("%.2f", (Time::HiRes::time - $service_time));
 1561     DEBUG "[DEBUG] Graphed service $skeypath ($service_time sec for $nb_graphs_drawn graphs)";
 1562     print $STATS "GS|$service_time\n" unless $skip_stats;
 1563 
 1564     foreach (@added) {
 1565         delete $service->{$_} if exists $service->{$_};
 1566     }
 1567     @added = ();
 1568 }
 1569 
 1570 # sets enddate --end and draws a vertical ruler if enddate is in the future
 1571 # future trends are also added to the graph here
 1572 sub handle_trends {
 1573     my $time = shift;
 1574     my $lastupdate = shift;
 1575     my $pinpoint = shift;
 1576     my $service = shift;
 1577     my $RRDkludge = shift;
 1578     my @added = @_;
 1579     my @complete;
 1580 
 1581     # enddate possibly in future
 1582     my $futuretime = $pinpoint ? 0 : $resolutions{$time} * get_end_offset($service);
 1583     my $enddate = time + ($futuretime);
 1584     DEBUG "[DEBUG] lastupdate: $lastupdate, enddate: $enddate\n";
 1585 
 1586     # future begins at this horizontal ruler
 1587     if ($enddate > $lastupdate) {
 1588         push(@complete, "VRULE:$lastupdate#999999");
 1589     }
 1590 
 1591     # create trends/predictions
 1592     foreach my $field (@{munin_find_field($service, "label")}) {
 1593         my $fieldname = munin_get_node_name($field);
 1594     my $colour = $single_colour;
 1595 
 1596     # Skip virtual fieldnames, otherwise beware of $hash->{foo}{bar}.
 1597     #
 1598     # On a sidenote, what's the output of the following code ?
 1599     # perl -e '$a = {}; if ($a->{foo}{bar}) { print "Found Foo/Bar\n"; } \
 1600     #        if ($a->{foo}) { print "Found Foo\n"; }'
 1601     next if ! defined $service->{$fieldname};
 1602 
 1603         if (defined $service->{$fieldname}{'colour'}) {
 1604             $colour = "$service->{$fieldname}{'colour'}66";
 1605         }
 1606 
 1607         my $rrd_fieldname = get_field_name($fieldname);
 1608 
 1609         my $cdef = "";
 1610         if (defined $service->{$fieldname}{'cdef'}) {
 1611             $cdef = "cdef";
 1612         }
 1613 
 1614         #trends
 1615         if (defined $service->{$fieldname}{'trend'} and $service->{$fieldname}{'trend'} eq 'yes') {
 1616             push (@complete, "CDEF:t$rrd_fieldname=c$cdef$rrd_fieldname,$futuretime,TRENDNAN");
 1617             push (@complete, "LINE1:t$rrd_fieldname#$colour:$service->{$fieldname}->{'label'} trend\\l");
 1618             DEBUG "[DEBUG] set trend for $fieldname\n";
 1619         }
 1620 
 1621         #predictions
 1622         if (defined $service->{$fieldname}{'predict'}) {
 1623             #arguments: pattern length (e.g. 1 day), smoothing (multiplied with timeresolution)
 1624             my @predict = split(",", $service->{$fieldname}{'predict'});
 1625             my $predictiontime = int ($futuretime / $predict[0]) + 2; #2 needed for 1 day
 1626             my $smooth = $predict[1]*$resolutions{$time};
 1627             push (@complete, "CDEF:p$rrd_fieldname=$predict[0],-$predictiontime,$smooth,c$cdef$rrd_fieldname,PREDICT");
 1628             push (@complete, "LINE1:p$rrd_fieldname#$colour:$service->{$fieldname}->{'label'} prediction\\l");
 1629             DEBUG "[DEBUG] set prediction for $fieldname\n";
 1630         }
 1631     }
 1632 
 1633 
 1634     push(@complete,
 1635         "COMMENT:Last update$RRDkludge: "
 1636         . RRDescape(scalar localtime($lastupdate))
 1637         . "\\r");
 1638 
 1639     # If pinpointing, --end should *NOT* be changed
 1640     if (! $pinpoint) {
 1641             if (@added) { # stop one period earlier if it's a .sum or .stack
 1642                 push @complete, "--end",
 1643                     (int(($enddate-$resolutions{$time}) / $resolutions{$time})) * $resolutions{$time};
 1644             } else {
 1645                 push @complete, "--end",
 1646                     (int($enddate / $resolutions{$time})) * $resolutions{$time};
 1647             }
 1648     }
 1649 
 1650     return @complete;
 1651 }
 1652 
 1653 sub get_fonts {
 1654     # Set up rrdtool graph font options according to RRD version.
 1655     my @options;
 1656 
 1657     if ($RRDs::VERSION < 1.2) {
 1658     # RRD before 1.2, no font options
 1659     } elsif ($RRDs::VERSION < 1.3) {
 1660     # RRD 1.2
 1661     # The RRD 1.2 documentation says you can identify font family
 1662     # names but I never got that to work, but full font path worked
 1663     @options = (
 1664         '--font', "LEGEND:7:$libdir/DejaVuSansMono.ttf",
 1665         '--font', "UNIT:7:$libdir/DejaVuSans.ttf",
 1666         '--font', "AXIS:7:$libdir/DejaVuSans.ttf",
 1667            );
 1668     } else {
 1669     # At least 1.3
 1670     @options = (
 1671         '--font', 'DEFAULT:0:DejaVuSans,DejaVu Sans,DejaVu LGC Sans,Bitstream Vera Sans',
 1672         '--font', 'LEGEND:7:DejaVuSansMono,DejaVu Sans Mono,DejaVu LGC Sans Mono,Bitstream Vera Sans Mono,monospace',
 1673         # Colors coordinated with CSS.
 1674         '--color',  'BACK#F0F0F0',   # Area around the graph
 1675         '--color',  'FRAME#F0F0F0',  # Line around legend spot
 1676         '--color',  'CANVAS#FFFFFF', # Graph background, max contrast
 1677         '--color',  'FONT#666666',   # Some kind of gray
 1678         '--color',  'AXIS#CFD6F8',   # And axis like html boxes
 1679         '--color',  'ARROW#CFD6F8',  # And arrow, ditto.
 1680            );
 1681     }
 1682 
 1683     if ($RRDs::VERSION >= 1.4) {
 1684     # RRD 1.4 has border, adding it
 1685     push @options, (
 1686         '--border',  '0',
 1687            );
 1688     }
 1689 
 1690     return @options;
 1691 };
 1692 
 1693 
 1694 sub graph_by_minute {
 1695     my $service = shift;
 1696 
 1697     return (munin_get($service, "graph_period", "second") eq "minute");
 1698 }
 1699 
 1700 sub graph_by_hour {
 1701     my $service = shift;
 1702 
 1703     return (munin_get($service, "graph_period", "second") eq "hour");
 1704 }
 1705 
 1706 sub orig_to_cdef {
 1707     my $service   = shift;
 1708     my $fieldname = shift;
 1709     my $original_fieldname = shift || $fieldname;
 1710 
 1711     return unless ref($service) eq "HASH";
 1712 
 1713     if (defined $service->{$fieldname} && defined $service->{$fieldname}->{"cdef_name"}) {
 1714         return orig_to_cdef($service, $service->{$fieldname}->{"cdef_name"}, $original_fieldname);
 1715     }
 1716     # For unknown reasons the sanitizing of fieldnames in the context of RRD field names is not
 1717     # applied consistently (maybe it should not be applied at all).
 1718     # Thus we need to apply it here in the same way, as it seems to be applied at other places.
 1719     if (_sanitise_fieldname($original_fieldname) ne $original_fieldname) {
 1720         return get_field_name(_sanitise_fieldname($fieldname));
 1721     } else {
 1722         return get_field_name($fieldname);
 1723     }
 1724 }
 1725 
 1726 sub reset_cdef {
 1727     my $service = shift;
 1728     my $fieldname = shift;
 1729     return unless ref($service) eq "HASH";
 1730     if (defined $service->{$fieldname} && defined $service->{$fieldname}->{"cdef_name"}) {
 1731         reset_cdef($service, $service->{$fieldname}->{"cdef_name"});
 1732         delete $service->{$fieldname}->{"cdef_name"};
 1733     }
 1734 }
 1735 
 1736 sub ends_with {
 1737     my ($src, $searched) = @_;
 1738 
 1739     my $is_ending = (substr($src, - length($searched)) eq $searched);
 1740     return $is_ending;
 1741 }
 1742 
 1743 
 1744 sub skip_service {
 1745     my $service = shift;
 1746     my $fqn   = munin_get_node_fqn($service);
 1747 
 1748     # Skip if we've limited services with the omnipotent cli option only-fqn
 1749     return 1 if ($only_fqn and ! ends_with($fqn, $only_fqn));
 1750     DEBUG "[DEBUG] $fqn is in ($only_fqn)\n";
 1751 
 1752     # Skip if we've limited services with cli options
 1753     return 1
 1754       if (@limit_services and
 1755       ! (grep { ends_with($fqn, $_) } @limit_services));
 1756 
 1757     DEBUG "[DEBUG] $fqn is in (" . join(",", @limit_services) . ")\n";
 1758 
 1759     # Always graph if --force is present
 1760     return 0 if $force_graphing;
 1761 
 1762     # See if we should skip it because of conf-options
 1763     return 1
 1764         if (munin_get($service, "graph", "yes") eq "on-demand"
 1765         or !munin_get_bool($service, "graph", 1));
 1766 
 1767     # Don't skip
 1768     return 0;
 1769 }
 1770 
 1771 
 1772 sub expand_cdef {
 1773     my $service    = shift;
 1774     my $cfield_ref = shift;
 1775     my $cdef       = shift;
 1776 
 1777     my $new_field = &get_field_name("cdef$$cfield_ref");
 1778 
 1779     my ($max, $min, $avg) = (
 1780         "CDEF:a$new_field=$cdef", "CDEF:i$new_field=$cdef",
 1781         "CDEF:g$new_field=$cdef"
 1782     );
 1783 
 1784     foreach my $field (@{munin_find_field($service, "label")}) {
 1785         my $fieldname = munin_get_node_name($field);
 1786         my $rrdname = &orig_to_cdef($service, $fieldname);
 1787         if ($cdef =~ /\b$fieldname\b/) {
 1788             $max =~ s/(?<=[,=(])$fieldname(?=[,=)]|$)/a$rrdname/g;
 1789             $min =~ s/(?<=[,=(])$fieldname(?=[,=)]|$)/i$rrdname/g;
 1790             $avg =~ s/(?<=[,=(])$fieldname(?=[,=)]|$)/g$rrdname/g;
 1791         }
 1792     }
 1793 
 1794     munin_set_var_loc($service, [$$cfield_ref, "cdef_name"], $new_field);
 1795     $$cfield_ref = $new_field;
 1796 
 1797     return ($max, $min, $avg);
 1798 }
 1799 
 1800 sub parse_path {
 1801     my ($path, $domain, $node, $service, $field) = @_;
 1802     my $filename = "unknown";
 1803 
 1804     if ($path =~ /^\s*([^:]*):([^:]*):([^:]*):([^:]*)\s*$/) {
 1805         $filename = munin_get_filename($config, $1, $2, $3, $4);
 1806     }
 1807     elsif ($path =~ /^\s*([^:]*):([^:]*):([^:]*)\s*$/) {
 1808         $filename = munin_get_filename($config, $domain, $1, $2, $3);
 1809     }
 1810     elsif ($path =~ /^\s*([^:]*):([^:]*)\s*$/) {
 1811         $filename = munin_get_filename($config, $domain, $node, $1, $2);
 1812     }
 1813     elsif ($path =~ /^\s*([^:]*)\s*$/) {
 1814         $filename = munin_get_filename($config, $domain, $node, $service, $1);
 1815     }
 1816     return $filename;
 1817 }
 1818 
 1819 # Wrapper for munin_get_picture_filename to handle pinpoint
 1820 sub get_picture_filename {
 1821     my $of;
 1822     if (defined $output_file) { $of=$output_file; goto exit_label; }
 1823 
 1824     $of = munin_get_picture_filename(@_);
 1825 
 1826   exit_label:
 1827 
 1828     return $of;
 1829 }
 1830 
 1831 sub escape {
 1832     my $text = shift;
 1833     return if not defined $text;
 1834     $text =~ s/\\/\\\\/g;
 1835     $text =~ s/:/\\:/g;
 1836     return $text;
 1837 }
 1838 
 1839 sub get_scientific {
 1840     my $value = shift;
 1841     $value =~ s/m/e-03/;
 1842     $value =~ s/k/e+03/;
 1843     $value =~ s/M/e+06/;
 1844     $value =~ s/G/e+09/;
 1845     return $value;
 1846 }
 1847 
 1848 sub RRDescape {
 1849     my $text = shift;
 1850     return $RRDs::VERSION < 1.2 ? $text : escape($text);
 1851 }
 1852 
 1853 
 1854 sub print_usage_and_exit {
 1855     print "Usage: $0 [options]
 1856 
 1857 Options:
 1858     --[no]fork          Do not fork.  By default munin-graph forks sub
 1859                         processes for drawing graphs to utilize available
 1860                         cores and I/O bandwidth. [--fork]
 1861     --n n               Max number of concurrent processes [$max_running]
 1862     --[no]force     Force drawing of graphs that are not usually
 1863             drawn due to options in the config file. [--noforce]
 1864     --[no]lazy      Only redraw graphs when needed. [--lazy]
 1865     --help      View this message.
 1866     --version       View version information.
 1867     --debug     View debug messages.
 1868     --[no]cron      Behave as expected when run from cron. (Used internally
 1869             in Munin.)
 1870     --host <host>       Limit graphed hosts to <host>. Multiple --host options
 1871                         may be supplied.
 1872     --only-fqn <FQN>    For internal use with CGI graphing.  Graph only a
 1873                         single fully qualified named graph, e.g. --only-fqn
 1874                           root/Backend/dafnes.example.com/diskstats_iops
 1875                         Always use with the correct --host option.
 1876     --config <file> Use <file> as configuration file. [$conffile]
 1877     --[no]list-images   List the filenames of the images created.
 1878                 [--nolist-images]
 1879     --output-file  -o   Output graph file. (used for CGI graphing)
 1880     --log-file     -l   Output log file. (used for CGI graphing)
 1881     --[no]day       Create day-graphs.   [--day]
 1882     --[no]week      Create week-graphs.  [--week]
 1883     --[no]month     Create month-graphs. [--month]
 1884     --[no]year      Create year-graphs.  [--year]
 1885     --[no]sumweek   Create summarised week-graphs.  [--summweek]
 1886     --[no]sumyear   Create summarised year-graphs.  [--sumyear]
 1887     --pinpoint <start,stop> Create custom-graphs. <start,stop> is the standard unix Epoch. [not active]
 1888     --size_x <pixels>   Sets the X size of the graph in pixels [175]
 1889     --size_y <pixels>   Sets the Y size of the graph in pixels [400]
 1890     --lower_limit <lim> Sets the lower limit of the graph
 1891     --upper_limit <lim> Sets the upper limit of the graph
 1892 
 1893 NOTE! --pinpoint and --only-fqn must not be combined with
 1894 --[no]<day|week|month|year> options.  The result of doing that is
 1895 undefined.
 1896 
 1897 ";
 1898     exit 0;
 1899 }
 1900 
 1901 1;