Merge remote-tracking branch 'origin/collectd-4.10' into collectd-5.3
[collectd.git] / contrib / collection3 / bin / index.cgi
index b0001d2..4723af9 100755 (executable)
@@ -1,6 +1,7 @@
 #!/usr/bin/perl
 
-# Copyright (C) 2008  Florian octo Forster <octo at verplant.org>
+# Copyright (C) 2008-2011  Florian Forster
+# Copyright (C) 2011       noris network AG
 #
 # 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
 # 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.
+#
+# Authors:
+#   Florian "octo" Forster <octo at collectd.org>
 
 use strict;
 use warnings;
-use lib ('../lib');
 use utf8;
+use vars (qw($BASE_DIR));
+
+BEGIN
+{
+  if (defined $ENV{'SCRIPT_FILENAME'})
+  {
+    if ($ENV{'SCRIPT_FILENAME'} =~ m{^(/.+)/bin/[^/]+$})
+    {
+      $::BASE_DIR = $1;
+      unshift (@::INC, "$::BASE_DIR/lib");
+    }
+  }
+}
 
-use FindBin ('$RealBin');
+use Carp (qw(cluck confess));
 use CGI (':cgi');
 use CGI::Carp ('fatalsToBrowser');
 use HTML::Entities ('encode_entities');
 
 use Data::Dumper;
 
-use Collectd::Graph::TypeLoader (qw(tl_read_config tl_load_type));
+use Collectd::Graph::Config (qw(gc_read_config gc_get_scalar));
+use Collectd::Graph::TypeLoader (qw(tl_load_type));
 use Collectd::Graph::Common (qw(get_files_from_directory get_all_hosts
       get_timespan_selection get_selected_files get_host_selection
-      get_plugin_selection));
+      get_plugin_selection flush_files));
 use Collectd::Graph::Type ();
 
-our $Debug = param ('debug') ? 1 : 0;
-
 our $TimeSpans =
 {
   Hour  =>        3600,
@@ -44,23 +59,90 @@ our $TimeSpans =
   Year  => 366 * 86400
 };
 
-my $action = param ('action') || 'list_hosts';
-our %Actions =
+my %Actions =
 (
   list_hosts => \&action_list_hosts,
   show_selection => \&action_show_selection
 );
 
-if (!exists ($Actions{$action}))
+sub base_dir
+{
+  if (defined $::BASE_DIR)
+  {
+    return ($::BASE_DIR);
+  }
+
+  if (!defined ($ENV{'SCRIPT_FILENAME'}))
+  {
+    return;
+  }
+
+  if ($ENV{'SCRIPT_FILENAME'} =~ m{^(/.+)/bin/[^/]+$})
+  {
+    $::BASE_DIR = $1;
+    return ($::BASE_DIR);
+  }
+
+  return;
+}
+
+sub lib_dir
+{
+  my $base = base_dir ();
+
+  if ($base)
+  {
+    return "$base/lib";
+  }
+  else
+  {
+    return "../lib";
+  }
+}
+
+sub sysconf_dir
 {
-  print STDERR "No such action: $action\n";
-  exit 1;
+  my $base = base_dir ();
+
+  if ($base)
+  {
+    return "$base/etc";
+  }
+  else
+  {
+    return "../etc";
+  }
 }
 
-tl_read_config ("$RealBin/../etc/collection3.conf");
+sub init
+{
+  my $lib_dir = lib_dir ();
+  my $sysconf_dir = sysconf_dir ();
 
-$Actions{$action}->();
-exit (0);
+  if (!grep { $lib_dir eq $_ } (@::INC))
+  {
+    unshift (@::INC, $lib_dir);
+  }
+
+  gc_read_config ("$sysconf_dir/collection.conf");
+}
+
+sub main
+{
+  my $Debug = param ('debug') ? 1 : 0;
+  my $action = param ('action') || 'list_hosts';
+
+  if (!exists ($Actions{$action}))
+  {
+    print STDERR "No such action: $action\n";
+    return (1);
+  }
+
+  init ();
+
+  $Actions{$action}->();
+  return (1);
+} # sub main
 
 sub can_handle_xhtml
 {
@@ -102,16 +184,23 @@ sub can_handle_xhtml
   }
 } # can_handle_xhtml
 
-{my $html_started;
+my $html_started;
 sub start_html
 {
   return if ($html_started);
 
+  my $end;
+  my $begin;
+  my $timespan;
+
+  $end = time ();
+  $timespan = get_timespan_selection ();
+  $begin = $end - $timespan;
+
   if (can_handle_xhtml ())
   {
+    print header (-Content_Type => 'application/xhtml+xml; charset=UTF-8');
     print <<HTML;
-Content-Type: application/xhtml+xml; charset=UTF-8
-
 <?xml version="1.0" encoding="UTF-8"?>
 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
     "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
@@ -123,9 +212,8 @@ HTML
   }
   else
   {
+    print header (-Content_Type => 'text/html; charset=UTF-8');
     print <<HTML;
-Content-Type: text/html; charset=UTF-8
-
 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
     "http://www.w3.org/TR/html4/strict.dtd">
 <html>
@@ -136,11 +224,12 @@ HTML
     <title>collection.cgi, Version 3</title>
     <link rel="icon" href="../share/shortcut-icon.png" type="image/png" />
     <link rel="stylesheet" href="../share/style.css" type="text/css" />
+    <script type="text/javascript" src="../share/navigate.js"></script>
   </head>
-  <body>
+  <body onload="nav_init ($begin, $end);">
 HTML
   $html_started = 1;
-}}
+}
 
 sub end_html
 {
@@ -148,6 +237,29 @@ sub end_html
   </body>
 </html>
 HTML
+  $html_started = 0;
+}
+
+sub contains_invalid_chars
+{
+  my $str = shift;
+
+  for (split (m//, $str))
+  {
+    my $n = ord ($_);
+
+    # Whitespace is allowed.
+    if (($n >= 9) && ($n <= 13))
+    {
+      next;
+    }
+    elsif ($n < 32)
+    {
+      return (1);
+    }
+  }
+
+  return;
 }
 
 sub show_selector
@@ -164,6 +276,7 @@ sub show_selector
 HTML
   for (sort (keys %$host_selection))
   {
+    next if contains_invalid_chars ($_);
     my $host = encode_entities ($_);
     my $selected = $host_selection->{$_}
     ? ' selected="selected"'
@@ -176,6 +289,7 @@ HTML
 HTML
   for (sort (keys %$plugin_selection))
   {
+    next if contains_invalid_chars ($_);
     my $plugin = encode_entities ($_);
     my $selected = $plugin_selection->{$_}
     ? ' selected="selected"'
@@ -188,6 +302,7 @@ HTML
 HTML
   for (sort { $TimeSpans->{$a} <=> $TimeSpans->{$b} } (keys (%$TimeSpans)))
   {
+    next if contains_invalid_chars ($_);
     my $name = encode_entities ($_);
     my $value = $TimeSpans->{$_};
     my $selected = ($value == $timespan_selection)
@@ -214,6 +329,7 @@ sub action_list_hosts
   for (sort @hosts)
   {
     my $url = encode_entities (script_name () . "?action=show_selection;hostname=$_");
+    next if contains_invalid_chars ($_);
     my $name = encode_entities ($_);
     print qq#      <li><a href="$url">$name</a></li>\n#;
   }
@@ -261,32 +377,42 @@ sub action_show_selection
   start_html ();
   show_selector ();
 
-  my $ident = {};
-
   my $all_files;
+  my $timespan;
+
   my $types = {};
 
+  my $id_counter = 0;
+
   $all_files = get_selected_files ();
+  $timespan = get_timespan_selection ();
 
-  if ($Debug)
+  if (param ('debug'))
   {
     print "<pre>", Data::Dumper->Dump ([$all_files], ['all_files']), "</pre>\n";
   }
 
+  # Send FLUSH command to the daemon if necessary and possible.
+  flush_files ($all_files,
+      begin => time () - $timespan,
+      end => time (),
+      addr => gc_get_scalar ('UnixSockAddr', undef),
+      interval => gc_get_scalar ('Interval', 10));
+
   for (@$all_files)
   {
     my $file = $_;
     my $type = ucfirst (lc ($file->{'type'}));
 
-    $type =~ s/[^A-Za-z_]//g;
-    $type =~ s/_([A-Za-z])/\U$1\E/g;
+    $type =~ s/[^A-Za-z0-9_]//g;
+    $type =~ s/_([A-Za-z0-9])/\U$1\E/g;
 
     if (!defined ($types->{$type}))
     {
       $types->{$type} = tl_load_type ($file->{'type'});
       if (!$types->{$type})
       {
-        cluck ("tl_load_type (" . $file->{'type'} . ") failed");
+        warn ("tl_load_type (" . $file->{'type'} . ") failed");
         next;
       }
     }
@@ -299,19 +425,58 @@ sub action_show_selection
   for (sort (keys %$types))
   {
     my $type = $_;
-    my $graphs_num = $types->{$type}->getGraphsNum ();
 
-    my $timespan = get_timespan_selection ();
+    if (!defined ($types->{$type}))
+    {
+      next;
+    }
+
+    my $graphs_num = $types->{$type}->getGraphsNum ();
 
     for (my $i = 0; $i < $graphs_num; $i++)
     {
       my $args = $types->{$type}->getGraphArgs ($i);
       my $url = encode_entities ("graph.cgi?$args;begin=-$timespan");
+      my $id = sprintf ("graph%04i", $id_counter++);
 
       print "      <tr>\n";
       print "        <td rowspan=\"$graphs_num\">$type</td>\n" if ($i == 0);
-
-      print qq#        <td><img src="$url" /></td>\n#;
+      print <<EOF;
+        <td>
+          <div class="graph_canvas">
+            <div class="graph_float">
+              <img id="${id}" class="graph_image"
+                alt="A graph"
+                src="$url" />
+              <div class="controls zoom">
+                <div title="Earlier"
+                  onclick="nav_move_earlier ('${id}');">&#x2190;</div>
+                <div title="Zoom out"
+                  onclick="nav_zoom_out ('${id}');">-</div>
+                <div title="Zoom in"
+                  onclick="nav_zoom_in ('${id}');">+</div>
+                <div title="Later"
+                  onclick="nav_move_later ('${id}');">&#x2192;</div>
+              </div>
+              <div class="controls preset">
+                <div title="Show current hour"
+                  onclick="nav_time_reset ('${id}', 3600);">H</div>
+                <div title="Show current day"
+                  onclick="nav_time_reset ('${id}', 86400);">D</div>
+                <div title="Show current week"
+                  onclick="nav_time_reset ('${id}', 7 * 86400);">W</div>
+                <div title="Show current month"
+                  onclick="nav_time_reset ('${id}', 31 * 86400);">M</div>
+                <div title="Show current year"
+                  onclick="nav_time_reset ('${id}', 366 * 86400);">Y</div>
+                <div title="Set all images to this timespan"
+                  onclick="nav_set_reference ('${id}');">!</div>
+              </div>
+            </div>
+          </div>
+       </td>
+EOF
+      # print qq#        <td><img src="$url" /></td>\n#;
       print "      </tr>\n";
     }
   }
@@ -320,6 +485,8 @@ sub action_show_selection
   end_html ();
 }
 
+main ();
+
 =head1 SEE ALSO
 
 L<Collectd::Graph::Type>