package Munin::Master::HTMLOld;

=encoding utf-8

=begin comment
-*- perl -*-

This is Munin::Master::HTMLOld, a minimal package shell to make
munin-html modular (so it can loaded persistently in
munin-fastcgi-graph for example) without making it object oriented
yet.  The non-"old" module will feature propper object orientation
like munin-update and will have to wait until later.


Copyright (C) 2002-2009 Jimmy Olsen, Audun Ytterdal, Kjell Magne
Øierud, Nicolai Langfeldt, Linpro AS, Redpill Linpro AS and others.

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; version 2 dated June,
1991.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA.

$Id$


This is the hierarchy of templates

  * munin-overview.tmpl - Overview with all groups and hosts shown (2 levels down)
    
    * munin-domainview.tmpl - all members of one domain, showing links down to each single service
      and/or sub-group

      * munin-nodeview.tmpl - two (=day, week) graphs from all plugins on the node

	* munin-serviceview.tmpl - deepest level of view, shows all 4 graphs from one timeseries

      * Zoom view - zoomable graph based on one of the other four graphs

    OR

      * munin-nodeview.tmpl - multigraph sub-level.  When multigraph sublevels end ends
           the next is a munin-serviceview.

 * Comparison pages (x4) are at the service level.  Not sure how to work multigraph into them so
   avoid it all-together.

=end comment

=cut

use warnings;
use strict;

use Exporter;

our (@ISA, @EXPORT);
@ISA    = qw(Exporter);
@EXPORT = qw(html_startup html_main get_config emit_main_index emit_comparison_template emit_group_template emit_graph_template emit_service_template emit_category_template emit_problem_template update_timestamp);

use HTML::Template;
use POSIX qw(strftime);
use Getopt::Long;
use Time::HiRes;
use File::Copy::Recursive qw(dircopy);
use IO::File;

use Munin::Master::Logger;
use Munin::Master::Utils;
use Munin::Master::HTMLConfig;

use Log::Log4perl qw( :easy );

my @times = ("day", "week", "month", "year");

my $DEBUG      = 0;
my $conffile   = "$Munin::Common::Defaults::MUNIN_CONFDIR/munin.conf";
my $do_usage   = 0;
my $do_version = 0;
my $stdout     = 0;
my $force_run_as_root = 0;
my $config;
my $limits;
my $htmltagline;
my %comparisontemplates;
my $tmpldir;
my $htmldir;
my $do_dump = 0;
my $do_fork = 1;
my $max_running=6;
my $running=0;
my $timestamp;
my $htmlconfig;

sub update_timestamp {
    # For timestamping graphs
    $timestamp = strftime("%Y-%m-%d %T%z (%Z)", localtime);
    if ($timestamp =~ /%z/) {
	# %z (numeric timezone offset) may not be available, but %Z
	# (timeszone name) seems to be universaly supported though the
	# timezone names are not really standardized.
	$timestamp = strftime("%Y-%m-%d %T%Z", localtime);
    }
    $htmltagline = "This page was generated by <a href='http://munin-monitoring.org/'>Munin</a> version ".$Munin::Common::Defaults::MUNIN_VERSION." at $timestamp";
}

sub html_startup {
    my ($args) = @_;
    local @ARGV = @{$args};
    $do_usage = 1
	unless GetOptions (
	    "host=s"    => [],
	    "service=s" => [],
	    "config=s"  => \$conffile,
	    "debug!"    => \$DEBUG,
	    "stdout!"   => \$stdout,
	    "force-run-as-root!" => \$force_run_as_root,
	    "help"      => \$do_usage,
	    "version!"  => \$do_version,
	    "dump!"     => \$do_dump,
	    "fork!"     => \$do_fork,
        );

    print_usage_and_exit() if $do_usage;
    print_version_and_exit() if $do_version;

    exit_if_run_by_super_user() unless $force_run_as_root;

    munin_readconfig_base($conffile);
    # XXX: should not need that part here, yet.
    $config = munin_readconfig_part('datafile', 0);
 
    logger_open($config->{'logdir'});
    logger_debug() if $DEBUG;

    $tmpldir = $config->{tmpldir};
    $htmldir = $config->{htmldir};

    $max_running = &munin_get($config, "max_html_jobs", $max_running);

    update_timestamp();
 
    %comparisontemplates = instanciate_comparison_templates($tmpldir);
}

sub get_config {
	my $use_cache = shift;
	# usecache should match being in a cgi ($ENV{SCRIPT_NAME})
	$htmlconfig = undef; # avoid double ram usage
	if ($use_cache) {
		$htmlconfig = generate_config($use_cache);
	} else {
		my $graphs_filename = $config->{dbdir} . "/graphs";
		my $graphs_filename_tmp = $graphs_filename . ".tmp." . $$;

		$config->{"#%#graphs_fh"} = new IO::File("> $graphs_filename_tmp");

		$htmlconfig = generate_config($use_cache);

		# Closing the file
    		$config->{"#%#graphs_fh"} = undef;

		# Atomic move
		rename($graphs_filename_tmp, $graphs_filename);

		# htmlconf cache
		#  munin-html writes it
		#  munin-cgi-html reads it
		#
		#  full cron - written, never read
		#  munin-html and munin-cgi-html - written, and read as cache
		#  full munin-cgi-html - should not exist!
		#    and here, we avoid leaving a never-updating cache file
		my $cachefile = "$config->{dbdir}/htmlconf.storable";
		munin_writeconfig_storable($cachefile, $htmlconfig);
	}
	return $htmlconfig;
}

sub html_main {
    my $staticdir = $config->{staticdir};
    copy_web_resources($staticdir, $htmldir);

    my $configtime = Time::HiRes::time;
    get_config(0);
    my $groups = $htmlconfig;
    $configtime = sprintf("%.2f", (Time::HiRes::time - $configtime));
    INFO "[INFO] config generated ($configtime sec)";

    if (munin_get($config,"html_strategy","cron") eq "cgi"){
	INFO "[INFO] html_strategy is cgi. Skipping template generation";
	return;
    }

    my $update_time = Time::HiRes::time;
    my $lockfile = "$config->{rundir}/munin-html.lock";

    INFO "[INFO] Starting munin-html, getting lock $lockfile";

    munin_runlock($lockfile);


   # Preparing the group tree...

    if (!defined($groups) or scalar(%{$groups} eq '0')) {
	LOGCROAK "[FATAL] There is nothing to do here, since there are no nodes with any plugins.  Please refer to http://munin-monitoring.org/wiki/FAQ_no_graphs";
    };
	
    if (defined $groups->{"name"} and $groups->{"name"} eq "root") {
        $groups = $groups->{"groups"};    # root->groups
    }

    if ($do_dump) {
	print munin_dumpconfig_as_str($groups);
        exit 0;
    }
	
    generate_group_templates($groups);
    generate_category_templates($htmlconfig->{"globalcats"});
    emit_main_index($groups,$timestamp,0);
    emit_problem_template(0);

    INFO "[INFO] Releasing lock file $lockfile";

    munin_removelock("$lockfile");

    $update_time = sprintf("%.2f", (Time::HiRes::time - $update_time));

    INFO "[INFO] munin-html finished ($update_time sec)";
}

sub find_complinks{
    my($type) = @_;
    
    my @links = ();
    
    foreach my $current (qw(day week month year)) {
        my $data = {};

        if ($type eq $current) {
            $data->{'LINK'} = undef;
        } 
        else {
            $data->{'LINK'} = "comparison-$current.html";            
        }

        $data->{'NAME'} = $current;
        push(@links, $data);
    }

    return \@links;

}


sub emit_comparison_template {
    my ($key, $t, $emit_to_stdout) = @_;

    ( my $file = $key->{'filename'}) =~ s/index.html$//;

	$file .= "comparison-$t.html";
   	DEBUG "[DEBUG] Creating comparison page $file";

	# Rewrite peer urls to point to comparison-$t
	my $comparepeers = [];
	for my $peer (@{$key->{'peers'}}){
		my $comparelink = $peer->{"link"};
		next unless $comparelink; # avoid dead links

		$comparelink =~ s/index.html$/comparison-$t.html/;
		push((@$comparepeers), {"name" => $peer->{"name"}, "link" => $comparelink});
	}

    # when comparing categories, we're generating the page inside the
    # domain, but the images' urls will point inside the node itself,
    # or even worse within a category inside it. We strip out the
    # extra '../' that is used to generate a relative path which is no
    # longer valid.
    # Sadly this change is permanent and will influence successive
    # responses in a permanent execution environment like fcgid or
    # as a systemd socket service.  Thus we need to revert it later.
    foreach my $cat(@{$key->{'comparecategories'}}) {
        foreach my $service(@{$cat->{'services'}}) {
            foreach my $node(@{$service->{'nodes'}}) {
                foreach my $imgsrc(qw(imgday imgweek imgmonth imgyear
                              cimgday cimgweek cimgmonth cimgyear
                              zoomday zoomweek zoommonth zoomyear)) {
                    next unless defined($node->{$imgsrc});
                    # keep a copy of the original value (to be restored below)
                    $node->{"orig_$imgsrc"} = $node->{$imgsrc};
                    $node->{$imgsrc} =~ s|^\.\./\.\./(?:\.\./)?|../|;
                }
            }
        }
    }

    $comparisontemplates{$t}->param(
                                    INFO_OPTION => 'Groups on this level',
                                    NAME        => $key->{'name'},
                                    GROUPS      => $key->{'comparegroups'},
                                    PATH        => $key->{'path'},
                                    CSS_NAME    => get_css_name(),
                                    R_PATH   => $key->{'root_path'},
                                    COMPLINKS   => find_complinks($t),
                                    LARGESET    => decide_largeset($comparepeers), 
                                    PEERS       => $comparepeers,
                                    PARENT      => $key->{'path'}->[-2]->{'name'},
                                    CATEGORIES  => $key->{'comparecategories'},
                                    NCATEGORIES => $key->{'ncomparecategories'},
                                    TAGLINE     => $htmltagline,
									"COMPARISON-$t"  => 1,
									ROOTGROUPS	=> $htmlconfig->{"groups"},
									MUNIN_VERSION => $Munin::Common::Defaults::MUNIN_VERSION,
									TIMESTAMP	=> $timestamp,
									NGLOBALCATS => $htmlconfig->{"nglobalcats"},
									GLOBALCATS => $htmlconfig->{"globalcats"},
									NCRITICAL => scalar(@{$htmlconfig->{"problems"}->{"criticals"}}),
									NWARNING => scalar(@{$htmlconfig->{"problems"}->{"warnings"}}),
									NUNKNOWN => scalar(@{$htmlconfig->{"problems"}->{"unknowns"}}),
    );

    # store template output before reverting links
    my $template_output = $comparisontemplates{$t}->output;

    # restore the paths to their original value
    foreach my $cat(@{$key->{'comparecategories'}}) {
        foreach my $service(@{$cat->{'services'}}) {
            foreach my $node(@{$service->{'nodes'}}) {
                foreach my $imgsrc(qw(imgday imgweek imgmonth imgyear
                              cimgday cimgweek cimgmonth cimgyear
                              zoomday zoomweek zoommonth zoomyear)) {
                    next unless defined($node->{$imgsrc});
                    $node->{$imgsrc} = $node->{"orig_$imgsrc"};
                    delete($node->{"orig_$imgsrc"});
                }
            }
        }
    }

    if($emit_to_stdout){
        print $template_output;
	} else {
		ensure_dir_exists($file);
	    open(my $FILE, '>', $file)
    	    or die "Cannot open $file for writing: $!";
        print $FILE $template_output;
    	close $FILE;
	}
}


sub emit_graph_template {
    my ($key, $emit_to_stdout) = @_;

    my $graphtemplate = HTML::Template->new(
                                            filename => "$tmpldir/munin-nodeview.tmpl",
                                            die_on_bad_params => 0,
                                            global_vars       => 1,
                                            loop_context_vars => 1,
                                            filter            => sub {
                                                my $ref = shift;
                                                $$ref =~ s/URLX/URL$key->{'depth'}/g;
                                            });

    DEBUG "[DEBUG] Creating graph(nodeview) page ".$key->{filename};

    $graphtemplate->param(
                          INFO_OPTION => 'Nodes on this level',
                          GROUPS      => $key->{'groups'},
                          PATH        => $key->{'path'},
                          CSS_NAME    => get_css_name(),
                          R_PATH   => $key->{'root_path'},
                          PEERS       => $key->{'peers'},
                          LARGESET    => decide_largeset($key->{'peers'}), 
                          PARENT      => $key->{'path'}->[-2]->{'name'},
                          NAME        => $key->{'name'},
                          CATEGORIES  => $key->{'categories'},
                          NCATEGORIES => $key->{'ncategories'},
                          TAGLINE     => $htmltagline,
						  ROOTGROUPS  => $htmlconfig->{"groups"},
						  MUNIN_VERSION => $Munin::Common::Defaults::MUNIN_VERSION,
						  TIMESTAMP	=> $timestamp,
						  NGLOBALCATS => $htmlconfig->{"nglobalcats"},
						  GLOBALCATS => $htmlconfig->{"globalcats"},
									NCRITICAL => scalar(@{$htmlconfig->{"problems"}->{"criticals"}}),
									NWARNING => scalar(@{$htmlconfig->{"problems"}->{"warnings"}}),
									NUNKNOWN => scalar(@{$htmlconfig->{"problems"}->{"unknowns"}}),
                         );

    if($emit_to_stdout){
		print $graphtemplate->output;
	} else {
	    my $filename = $key->{'filename'};
		ensure_dir_exists($filename);
	    open(my $FILE, '>', $filename)
			or die "Cannot open $filename for writing: $!";
    	print $FILE $graphtemplate->output;
	    close $FILE;
	}
}

sub emit_category_template {
    my ($key, $time, $emit_to_stdout) = @_;

    my $graphtemplate = HTML::Template->new(
                                            filename => "$tmpldir/munin-categoryview.tmpl",
                                            die_on_bad_params => 0,
                                            global_vars       => 1,
                                            loop_context_vars => 1,
                                            filter            => sub {
                                                my $ref = shift;
                                                $$ref =~ s/URLX/URL/g;
                                            },
                                           );

	my $filename = $key->{'filename-' . $time};

    DEBUG "[DEBUG] Creating global category page ".$filename;

    # Manipulate the relative paths for the requested root-level context.
    # Sadly this change is permanent and will influence successive
    # responses in a permanent execution environment like fcgid or
    # as a systemd socket service.  Thus we need to revert it later.
    foreach my $graphs(@{$key->{'graphs'}}) {
        foreach my $graph(@{$graphs->{'graphs'}}) {
            foreach my $imgsrc(qw(imgday imgweek imgmonth imgyear
                              cimgday cimgweek cimgmonth cimgyear
                              zoomday zoomweek zoommonth zoomyear)) {
                # keep a copy of the original value (to be restored below)
                $graph->{"orig_$imgsrc"} = $graph->{$imgsrc};
                $graph->{$imgsrc} =~ s|^(?:\.\./)+||;
            }
        }
    }

    $graphtemplate->param(
                          PATH        => $key->{'path'},
                          CSS_NAME    => get_css_name(),
                          HOST_URL    => $key->{'host_url'},
                          R_PATH      => ".",
						  "TIME".$time => 1,
                          NAME        => $key->{'name'},
                          TAGLINE     => $htmltagline,
						  ROOTGROUPS  => $htmlconfig->{"groups"},
						  MUNIN_VERSION => $Munin::Common::Defaults::MUNIN_VERSION,
						  TIMESTAMP	=> $timestamp,
						  NGLOBALCATS => $htmlconfig->{"nglobalcats"},
						  GLOBALCATS => $htmlconfig->{"globalcats"},
						  CATEGORY => $key->{"name"},
						  SERVICES => $key->{"graphs"},
						  NCRITICAL => scalar(@{$htmlconfig->{"problems"}->{"criticals"}}),
						  NWARNING => scalar(@{$htmlconfig->{"problems"}->{"warnings"}}),
						  NUNKNOWN => scalar(@{$htmlconfig->{"problems"}->{"unknowns"}}),
                         );

    # store template output before reverting links
    my $template_output = $graphtemplate->output;

    # restore the paths to their original value
    foreach my $graphs(@{$key->{'graphs'}}) {
        foreach my $graph(@{$graphs->{'graphs'}}) {
            foreach my $imgsrc(qw(imgday imgweek imgmonth imgyear
                              cimgday cimgweek cimgmonth cimgyear
                              zoomday zoomweek zoommonth zoomyear)) {
                $graph->{$imgsrc} = $graph->{"orig_$imgsrc"};
                delete($graph->{"orig_$imgsrc"});
            }
        }
    }

    if($emit_to_stdout){
        print $template_output;
	} else {
		ensure_dir_exists($filename);
	    open(my $FILE, '>', $filename)
			or die "Cannot open $filename for writing: $!";
        print $FILE $template_output;
	    close $FILE;
	}
}

sub ensure_dir_exists {
    my $dirname  = shift;
    $dirname =~ s/\/[^\/]*$//;

    munin_mkdir_p($dirname, oct(755));
}

sub emit_problem_template {
    my ($emit_to_stdout) = @_;

    my $graphtemplate = HTML::Template->new(
	filename => "$tmpldir/munin-problemview.tmpl",
	die_on_bad_params => 0,
	global_vars       => 1,
	loop_context_vars => 1,
	);

	my $filename = munin_get_html_filename($config);
	$filename =~ s/index.html$/problems.html/g;

    INFO "[INFO] Creating problem page ".$filename;

    # Manipulate the relative paths for the requested root-level context.
    # Sadly this change is permanent and will influence successive
    # responses in a permanent execution environment like fcgid or
    # as a systemd socket service.  Thus we need to revert it later.
    foreach my $problem_graphs (values %{$htmlconfig->{"problems"}}) {
        foreach my $graph(@$problem_graphs) {
            foreach my $imgsrc(qw(imgday imgweek imgmonth imgyear
                                  cimgday cimgweek cimgmonth cimgyear
                                  zoomday zoomweek zoommonth zoomyear)) {
                next unless defined($graph->{$imgsrc});
                # keep a copy of the original value (to be restored below)
                $graph->{"orig_$imgsrc"} = $graph->{$imgsrc};
                $graph->{$imgsrc} =~ s|^(?:\.\./)+||;
            }
        }
    }

    $graphtemplate->param(
                          CSS_NAME    => get_css_name(),
                          R_PATH      => ".",
                          NAME        => "Problem overview",
                          TAGLINE     => $htmltagline,
						  ROOTGROUPS  => $htmlconfig->{"groups"},
						  MUNIN_VERSION => $Munin::Common::Defaults::MUNIN_VERSION,
						  TIMESTAMP	=> $timestamp,
						  NGLOBALCATS => $htmlconfig->{"nglobalcats"},
						  GLOBALCATS => $htmlconfig->{"globalcats"},
						  CRITICAL => $htmlconfig->{"problems"}->{"criticals"},
						  WARNING => $htmlconfig->{"problems"}->{"warnings"},
						  UNKNOWN => $htmlconfig->{"problems"}->{"unknowns"},
						  NCRITICAL => scalar(@{$htmlconfig->{"problems"}->{"criticals"}}),
						  NWARNING => scalar(@{$htmlconfig->{"problems"}->{"warnings"}}),
						  NUNKNOWN => scalar(@{$htmlconfig->{"problems"}->{"unknowns"}}),
                         );

    # store template output before reverting links
    my $template_output = $graphtemplate->output;

    # restore the paths to their original value
    foreach my $problem_graphs (values %{$htmlconfig->{"problems"}}) {
        foreach my $graph(@$problem_graphs) {
            foreach my $imgsrc(qw(imgday imgweek imgmonth imgyear
                                  cimgday cimgweek cimgmonth cimgyear
                                  zoomday zoomweek zoommonth zoomyear)) {
                next unless defined($graph->{$imgsrc});
                $graph->{$imgsrc} = $graph->{"orig_$imgsrc"};
                delete($graph->{"orig_$imgsrc"});
            }
        }
    }

    if($emit_to_stdout){
        print $template_output;
	} else {
		ensure_dir_exists($filename);
	    open(my $FILE, '>', $filename)
			or die "Cannot open $filename for writing: $!";
        print $FILE $template_output;
	    close $FILE;
	}
}


sub emit_group_template {
    my ($key, $emit_to_stdout) = @_;

    my $grouptemplate = HTML::Template->new(
	filename => "$tmpldir/munin-domainview.tmpl",
	die_on_bad_params => 0,
	global_vars       => 1,
	loop_context_vars => 1,
	filter            => sub {
	    my $ref = shift;
	    $$ref =~ s/URLX/URL$key->{'depth'}/g;
	});

    DEBUG "[DEBUG] Creating group page ".$key->{filename};

    $grouptemplate->param(
                          INFO_OPTION => 'Groups on this level',
                          GROUPS    => $key->{'groups'},
                          PATH      => $key->{'path'},
                          R_PATH => $key->{'root_path'},
                          CSS_NAME  => get_css_name(),
                          PEERS     => $key->{'peers'},
                          LARGESET  => decide_largeset($key->{'peers'}), 
                          PARENT    => $key->{'path'}->[-2]->{'name'} || "Overview",
                          COMPARE   => $key->{'compare'},
                          TAGLINE   => $htmltagline,
						  ROOTGROUPS => $htmlconfig->{"groups"},
						  MUNIN_VERSION => $Munin::Common::Defaults::MUNIN_VERSION,
						  TIMESTAMP	=> $timestamp,
						  NGLOBALCATS => $htmlconfig->{"nglobalcats"},
						  GLOBALCATS => $htmlconfig->{"globalcats"},
						  NCRITICAL => scalar(@{$htmlconfig->{"problems"}->{"criticals"}}),
						  NWARNING => scalar(@{$htmlconfig->{"problems"}->{"warnings"}}),
						  NUNKNOWN => scalar(@{$htmlconfig->{"problems"}->{"unknowns"}}),
	);
    if($emit_to_stdout){
		print $grouptemplate->output;
	} else {
    	my $filename = $key->{'filename'};
		ensure_dir_exists($filename);
    	open(my $FILE, '>', $filename)
		or die "Cannot open $filename for writing: $!";
    	print $FILE $grouptemplate->output;
	    close $FILE or die "Cannot close $filename after writing: $!";
	}
}

sub emit_zoom_template {
    my($srv, $emit_to_stdout) = @_;
    my $servicetemplate = HTML::Template->new(
                                              filename          => "$tmpldir/munin-dynazoom.tmpl",
                                              die_on_bad_params => 0,
                                              global_vars       => 1,
                                              loop_context_vars => 1
                                             );
	my $pathnodes = $srv->{'path'};
	my $peers = $srv->{'peers'};

    #remove underscores from peers and title (last path element)
    if ($peers){
        $peers = [ map { $_->{'name'} =~ s/_/ /g; $_;} @$peers ];
    }
    
    $pathnodes->[scalar(@$pathnodes) - 1]->{'pathname'} =~ s/_/ /g;
    $servicetemplate->param(
                            INFO_OPTION => 'Graphs in same category',
                            SERVICES  => [$srv],
                            PATH      => $pathnodes, 
                            PEERS     => $peers,
                            LARGESET  => decide_largeset($peers), 
                            R_PATH => $srv->{'root_path'},
                            CSS_NAME  => get_css_name(),
                            CATEGORY  => ucfirst $srv->{'category'},
                            TAGLINE   => $htmltagline,
						    ROOTGROUPS => $htmlconfig->{"groups"},
                            MUNIN_VERSION => $Munin::Common::Defaults::MUNIN_VERSION,
                            TIMESTAMP	=> $timestamp,
                            NGLOBALCATS => $htmlconfig->{"nglobalcats"},
                            GLOBALCATS => $htmlconfig->{"globalcats"},
                            NCRITICAL => scalar(@{$htmlconfig->{"problems"}->{"criticals"}}),
                            NWARNING => scalar(@{$htmlconfig->{"problems"}->{"warnings"}}),
                            NUNKNOWN => scalar(@{$htmlconfig->{"problems"}->{"unknowns"}}),
                            SHOW_ZOOM_JS => 1,
                           );

    if($emit_to_stdout){
		print $servicetemplate->output;
	} else {
		my $filename = $srv->{'filename'};
		ensure_dir_exists($filename);
        
	    DEBUG "[DEBUG] Creating service page $filename";
    	open(my $FILE, '>', $filename)
          or die "Cannot open '$filename' for writing: $!";
	    print $FILE $servicetemplate->output;
    	close $FILE or die "Cannot close '$filename' after writing: $!";
	}



}

sub emit_service_template {
	my ($srv, $emit_to_stdout) = @_;

    my $servicetemplate = HTML::Template->new(
                                              filename          => "$tmpldir/munin-serviceview.tmpl",
                                              die_on_bad_params => 0,
                                              global_vars       => 1,
                                              loop_context_vars => 1
                                             );

	my $pathnodes = $srv->{'path'};
	my $peers = $srv->{'peers'};

    #remove underscores from peers and title (last path element)
    if ($peers){
        $peers = [ map { $_->{'name'} =~ s/_/ /g; $_;} @$peers ];
    }
    
    $pathnodes->[scalar(@$pathnodes) - 1]->{'pathname'} =~ s/_/ /g;
    $servicetemplate->param(
                            INFO_OPTION => 'Graphs in same category',
                            SERVICES  => [$srv],
                            PATH      => $pathnodes, 
                            NAME      => $pathnodes->[-1]{'pathname'},
                            PEERS     => $peers,
                            LARGESET  => decide_largeset($peers), 
                            R_PATH => $srv->{'root_path'},
                            CSS_NAME  => get_css_name(),
                            CATEGORY  => ucfirst $srv->{'category'},
                            TAGLINE   => $htmltagline,
						    ROOTGROUPS => $htmlconfig->{"groups"},
                            MUNIN_VERSION => $Munin::Common::Defaults::MUNIN_VERSION,
                            TIMESTAMP	=> $timestamp,
                            NGLOBALCATS => $htmlconfig->{"nglobalcats"},
                            GLOBALCATS => $htmlconfig->{"globalcats"},
                            NCRITICAL => scalar(@{$htmlconfig->{"problems"}->{"criticals"}}),
                            NWARNING => scalar(@{$htmlconfig->{"problems"}->{"warnings"}}),
                            NUNKNOWN => scalar(@{$htmlconfig->{"problems"}->{"unknowns"}}),
                           );

    # No stored filename for this kind of html node.
    
	if($emit_to_stdout){
		print $servicetemplate->output;
	} else {
		my $filename = $srv->{'filename'};
		ensure_dir_exists($filename);

	    DEBUG "[DEBUG] Creating service page $filename";
    	open(my $FILE, '>', $filename)
        	or die "Cannot open '$filename' for writing: $!";
	    print $FILE $servicetemplate->output;
    	close $FILE or die "Cannot close '$filename' after writing: $!";
	}
}

sub decide_largeset {

    my ($peers) = @_;
    return scalar(@$peers) > $config->{'dropdownlimit'} ? 1 : 0;

}

sub emit_main_index {
    # Draw main index
    my ($groups, $t, $emit_to_stdout) = @_;

    my $template = HTML::Template->new(
        filename          => "$tmpldir/munin-overview.tmpl",
        die_on_bad_params => 0,
        loop_context_vars => 1,
		global_vars       => 1,
		filter            => sub {
		    my $ref = shift;
	    	$$ref =~ s/URLX/URL0/g;
		},
    );

    # FIX: this sometimes bugs:

    # HTML::Template::param() : attempt to set parameter 'groups' with
    # a scalar - parameter is not a TMPL_VAR! at
    # /usr/local/share/perl/5.10.0/Munin/Master/HTMLOld.pm line 140

    $template->param(
                    TAGLINE   => $htmltagline,
                    GROUPS    => $groups,
                    CSS_NAME  => get_css_name(),
					R_PATH => ".",
				    ROOTGROUPS => $htmlconfig->{"groups"},
			  	    MUNIN_VERSION => $Munin::Common::Defaults::MUNIN_VERSION,
					TIMESTAMP	=> $timestamp,
					NGLOBALCATS => $htmlconfig->{"nglobalcats"},
					GLOBALCATS => $htmlconfig->{"globalcats"},
					  NCRITICAL => scalar(@{$htmlconfig->{"problems"}->{"criticals"}}),
					  NWARNING => scalar(@{$htmlconfig->{"problems"}->{"warnings"}}),
					  NUNKNOWN => scalar(@{$htmlconfig->{"problems"}->{"unknowns"}}),
					
    );
	if($emit_to_stdout){
		print $template->output;
	} else {
	    my $filename = munin_get_html_filename($config);
		ensure_dir_exists($filename);

	    DEBUG "[DEBUG] Creating main index $filename";

    	open(my $FILE, '>', $filename)
        	or die "Cannot open $filename for writing: $!";
	    print $FILE $template->output;
    	close $FILE;
	}
}


sub copy_web_resources {
    my ($staticdir, $htmldir) = @_;
	unless(dircopy($staticdir, "$htmldir/static")){
		ERROR "[ERROR] Could not copy contents from $staticdir to $htmldir";
		die "[ERROR] Could not copy contents from $staticdir to $htmldir";
	}
}

sub instanciate_comparison_templates {
    my ($tmpldir) = @_;

    return (
        day => HTML::Template->new(
            filename          => "$tmpldir/munin-comparison-day.tmpl",
            die_on_bad_params => 0,
			global_vars       => 1,
            loop_context_vars => 1
        ),
        week => HTML::Template->new(
            filename          => "$tmpldir/munin-comparison-week.tmpl",
            die_on_bad_params => 0,
			global_vars       => 1,
            loop_context_vars => 1
        ),
        month => HTML::Template->new(
            filename          => "$tmpldir/munin-comparison-month.tmpl",
			global_vars       => 1,
            die_on_bad_params => 0,
            loop_context_vars => 1
        ),
        year => HTML::Template->new(
            filename          => "$tmpldir/munin-comparison-year.tmpl",
			global_vars       => 1,
            die_on_bad_params => 0,
            loop_context_vars => 1
        ));
}



sub get_css_name{
    #NOTE: this will do more in future versions. knuthaug 2009-11-15
    return "style.css";
}


sub fork_and_work {
    my ($work) = @_;

    if (!$do_fork || !$max_running) {

        # We're not forking.  Do work and return.
        DEBUG "[DEBUG] Doing work synchrnonously";
        &$work;
        return;
    }

    # Make sure we don't fork too much
    while ($running >= $max_running) {
        DEBUG
            "[DEBUG] Too many forks ($running/$max_running), wait for something to get done";
        look_for_child("block");
        --$running;
    }

    my $pid = fork();

    if (!defined $pid) {
        ERROR "[ERROR] fork failed: $!";
        die "fork failed: $!";
    }

    if ($pid == 0) {

        # This block does the real work.  Since we're forking exit
        # afterwards.

        &$work;

        # See?!

        exit 0;

    }
    else {
        ++$running;
        DEBUG "[DEBUG] Forked: $pid. Now running $running/$max_running";
        while ($running and look_for_child()) {
            --$running;
        }
    }
}

sub generate_category_templates {
	my $arr = shift || return;
	
	foreach my $key (@$arr) {
		foreach my $time (@times) {
			emit_category_template($key, $time, 0);
		}
	}
}

sub generate_group_templates {
    my $arr = shift || return;
    return unless ref($arr) eq "ARRAY";

	foreach my $key (@$arr) {
        if (defined $key and ref($key) eq "HASH") {
           	
            if (defined $key->{'ngroups'} and $key->{'ngroups'}) {
                fork_and_work(sub {generate_group_templates($key->{'groups'})});
                emit_group_template($key,0);
                
                if ($key->{'compare'}) { # Create comparison templates as well 
                    foreach my $t (@times) {
                        emit_comparison_template($key,$t,0);
                    }
                }
            }
            if (defined $key->{'ngraphs'} and $key->{'ngraphs'}) {
                emit_graph_template($key, 0);
				foreach my $category (@{$key->{"categories"}}) {
					foreach my $serv (@{$category->{"services"}}) {
						unless($serv->{"multigraph"}){
							emit_service_template($serv);
							#emit_zoom_template($serv);
						}
					}
				}
            }
 
       }
    }
}

sub print_usage_and_exit {

    print "Usage: $0 [options]

Options:
    --help		View this message.
    --debug		View debug messages.
    --version		View version information.
    --nofork            Compatibility. No effect.
    --service <service>	Compatibility. No effect.
    --host <host>	Compatibility. No effect.
    --config <file>	Use <file> as configuration file. 
			[/etc/munin/munin.conf]

";
    exit 0;
}

1;


=head1 NAME

munin-html - A program to draw html-pages on an Munin installation

=head1 SYNOPSIS

munin-html [options]

=head1 OPTIONS

=over 5

=item B<< --service <service> >>

Compatibility. No effect.

=item B<< --host <host> >>

Compatibility. No effect.

=item B<< --nofork >>

Compatibility. No effect.

=item B<< --config <file> >>

Use E<lt>fileE<gt> as configuration file. [/etc/munin/munin.conf]

=item B<< --help >>

View help message.

=item B<< --[no]debug >>

If set, view debug messages. [--nodebug]

=back

=head1 DESCRIPTION

Munin-html is a part of the package Munin, which is used in combination
with Munin's node.  Munin is a group of programs to gather data from
Munin's nodes, graph them, create html-pages, and optionally warn Nagios
about any off-limit values.

Munin-html creates the html pages.

=head1 FILES

	/etc/munin/munin.conf
	/var/lib/munin/datafile
	/var/log/munin/munin-html
	/usr/share/munin/www/*
	/run/munin/*

=head1 VERSION

This is munin-html version 2.0.56

=head1 AUTHORS

Knut Haugen, Audun Ytterdal and Jimmy Olsen.

=head1 BUGS

munin-html does, as of now, not check the syntax of the configuration file.

Please report other bugs in the bug tracker at L<http://munin-monitoring.org/>.

=head1 COPYRIGHT

Copyright (C) 2002-2009 Knut Haugen, Audun Ytterdal, and Jimmy Olsen /
Linpro AS.

This is free software; see the source for copying conditions. There is
NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE.

This program is released under the GNU General Public License

=head1 SEE ALSO

For information on configuration options, please refer to the man page for
F<munin.conf>.

=cut

# vim:syntax=perl:ts=8
