Revert "curl_xml.c: avoid using uninitalized variable in error message"
[collectd.git] / contrib / collection3 / bin / index.cgi
1 #!/usr/bin/perl
2
3 # Copyright (C) 2008  Florian octo Forster <octo at verplant.org>
4 #
5 # This program is free software; you can redistribute it and/or modify it under
6 # the terms of the GNU General Public License as published by the Free Software
7 # Foundation; only version 2 of the License is applicable.
8 #
9 # This program is distributed in the hope that it will be useful, but WITHOUT
10 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11 # FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
12 # details.
13 #
14 # You should have received a copy of the GNU General Public License along with
15 # this program; if not, write to the Free Software Foundation, Inc.,
16 # 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
17
18 use strict;
19 use warnings;
20 use lib ('../lib');
21 use utf8;
22
23 use Carp (qw(cluck confess));
24 use FindBin ('$RealBin');
25 use CGI (':cgi');
26 use CGI::Carp ('fatalsToBrowser');
27 use HTML::Entities ('encode_entities');
28
29 use Data::Dumper;
30
31 use Collectd::Graph::Config (qw(gc_read_config gc_get_scalar));
32 use Collectd::Graph::TypeLoader (qw(tl_load_type));
33 use Collectd::Graph::Common (qw(get_files_from_directory get_all_hosts
34       get_timespan_selection get_selected_files get_host_selection
35       get_plugin_selection flush_files));
36 use Collectd::Graph::Type ();
37
38 our $Debug = param ('debug') ? 1 : 0;
39
40 our $TimeSpans =
41 {
42   Hour  =>        3600,
43   Day   =>       86400,
44   Week  =>   7 * 86400,
45   Month =>  31 * 86400,
46   Year  => 366 * 86400
47 };
48
49 my $action = param ('action') || 'list_hosts';
50 our %Actions =
51 (
52   list_hosts => \&action_list_hosts,
53   show_selection => \&action_show_selection
54 );
55
56 if (!exists ($Actions{$action}))
57 {
58   print STDERR "No such action: $action\n";
59   exit 1;
60 }
61
62 gc_read_config ("$RealBin/../etc/collection.conf");
63
64 $Actions{$action}->();
65 exit (0);
66
67 sub can_handle_xhtml
68 {
69   my %types = ();
70
71   if (!defined $ENV{'HTTP_ACCEPT'})
72   {
73     return;
74   }
75
76   for (split (',', $ENV{'HTTP_ACCEPT'}))
77   {
78     my $type = lc ($_);
79     my $q = 1.0;
80
81     if ($type =~ m#^([^;]+);q=([0-9\.]+)$#)
82     {
83       $type = $1;
84       $q = 0.0 + $2;
85     }
86     $types{$type} = $q;
87   }
88
89   if (!defined ($types{'application/xhtml+xml'}))
90   {
91     return;
92   }
93   elsif (!defined ($types{'text/html'}))
94   {
95     return (1);
96   }
97   elsif ($types{'application/xhtml+xml'} < $types{'text/html'})
98   {
99     return;
100   }
101   else
102   {
103     return (1);
104   }
105 } # can_handle_xhtml
106
107 {my $html_started;
108 sub start_html
109 {
110   return if ($html_started);
111
112   my $end;
113   my $begin;
114   my $timespan;
115
116   $end = time ();
117   $timespan = get_timespan_selection ();
118   $begin = $end - $timespan;
119
120   if (can_handle_xhtml ())
121   {
122     print <<HTML;
123 Content-Type: application/xhtml+xml; charset=UTF-8
124
125 <?xml version="1.0" encoding="UTF-8"?>
126 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
127     "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
128 <html xmlns="http://www.w3.org/1999/xhtml"
129     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
130     xsi:schemaLocation="http://www.w3.org/MarkUp/SCHEMA/xhtml11.xsd"
131     xml:lang="en">
132 HTML
133   }
134   else
135   {
136     print <<HTML;
137 Content-Type: text/html; charset=UTF-8
138
139 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
140     "http://www.w3.org/TR/html4/strict.dtd">
141 <html>
142 HTML
143   }
144   print <<HTML;
145   <head>
146     <title>collection.cgi, Version 3</title>
147     <link rel="icon" href="../share/shortcut-icon.png" type="image/png" />
148     <link rel="stylesheet" href="../share/style.css" type="text/css" />
149     <script type="text/javascript" src="../share/navigate.js"></script>
150   </head>
151   <body onload="nav_init ($begin, $end);">
152 HTML
153   $html_started = 1;
154 }}
155
156 sub end_html
157 {
158   print <<HTML;
159   </body>
160 </html>
161 HTML
162 }
163
164 sub contains_invalid_chars
165 {
166   my $str = shift;
167
168   for (split (m//, $str))
169   {
170     my $n = ord ($_);
171
172     # Whitespace is allowed.
173     if (($n >= 9) && ($n <= 13))
174     {
175       next;
176     }
177     elsif ($n < 32)
178     {
179       return (1);
180     }
181   }
182
183   return;
184 }
185
186 sub show_selector
187 {
188   my $timespan_selection = get_timespan_selection ();
189   my $host_selection = get_host_selection ();
190   my $plugin_selection = get_plugin_selection ();
191
192   print <<HTML;
193     <form action="${\script_name ()}" method="get">
194       <fieldset>
195         <legend>Data selection</legend>
196         <select name="hostname" multiple="multiple" size="15">
197 HTML
198   for (sort (keys %$host_selection))
199   {
200     next if contains_invalid_chars ($_);
201     my $host = encode_entities ($_);
202     my $selected = $host_selection->{$_}
203     ? ' selected="selected"'
204     : '';
205     print qq#          <option value="$host"$selected>$host</option>\n#;
206   }
207   print <<HTML;
208         </select>
209         <select name="plugin" multiple="multiple" size="15">
210 HTML
211   for (sort (keys %$plugin_selection))
212   {
213     next if contains_invalid_chars ($_);
214     my $plugin = encode_entities ($_);
215     my $selected = $plugin_selection->{$_}
216     ? ' selected="selected"'
217     : '';
218     print qq#          <option value="$plugin"$selected>$plugin</option>\n#;
219   }
220   print <<HTML;
221         </select>
222         <select name="timespan">
223 HTML
224   for (sort { $TimeSpans->{$a} <=> $TimeSpans->{$b} } (keys (%$TimeSpans)))
225   {
226     next if contains_invalid_chars ($_);
227     my $name = encode_entities ($_);
228     my $value = $TimeSpans->{$_};
229     my $selected = ($value == $timespan_selection)
230     ? ' selected="selected"'
231     : '';
232     print qq#          <option value="$value"$selected>$name</option>\n#;
233   }
234   print <<HTML;
235         </select>
236         <input type="hidden" name="action" value="show_selection" />
237         <input type="submit" name="ok_button" value="OK" />
238       </fieldset>
239     </form>
240 HTML
241 } # show_selector
242
243 sub action_list_hosts
244 {
245   start_html ();
246   show_selector ();
247
248   my @hosts = get_all_hosts ();
249   print "    <ul>\n";
250   for (sort @hosts)
251   {
252     my $url = encode_entities (script_name () . "?action=show_selection;hostname=$_");
253     next if contains_invalid_chars ($_);
254     my $name = encode_entities ($_);
255     print qq#      <li><a href="$url">$name</a></li>\n#;
256   }
257   print "    </ul>\n";
258
259   end_html ();
260 } # action_list_hosts
261
262 =head1 MODULE LOADING
263
264 This script makes use of the various B<Collectd::Graph::Type::*> modules. If a
265 file like C<foo.rrd> is encountered it tries to load the
266 B<Collectd::Graph::Type::Foo> module and, if that fails, falls back to the
267 B<Collectd::Graph::Type> base class.
268
269 If you want to create a specialized graph for a certain type, you have to
270 create a new module which inherits from the B<Collectd::Graph::Type> base
271 class. A description of provided (and used) methods can be found in the inline
272 documentation of the B<Collectd::Graph::Type> module.
273
274 There are other, more specialized, "abstract" classes that possibly better fit
275 your need. Unfortunately they are not yet documented.
276
277 =over 4
278
279 =item B<Collectd::Graph::Type::GenericStacked>
280
281 Specialized class that groups files by their plugin instance and stacks them on
282 top of each other. Example types that inherit from this class are
283 B<Collectd::Graph::Type::Cpu> and B<Collectd::Graph::Type::Memory>.
284
285 =item B<Collectd::Graph::Type::GenericIO>
286
287 Specialized class for input/output graphs. This class can only handle files
288 with exactly two data sources, input and output. Example types that inherit
289 from this class are B<Collectd::Graph::Type::DiskOctets> and
290 B<Collectd::Graph::Type::IfOctets>.
291
292 =back
293
294 =cut
295
296 sub action_show_selection
297 {
298   start_html ();
299   show_selector ();
300
301   my $all_files;
302   my $timespan;
303
304   my $types = {};
305
306   my $id_counter = 0;
307
308   $all_files = get_selected_files ();
309   $timespan = get_timespan_selection ();
310
311   if ($Debug)
312   {
313     print "<pre>", Data::Dumper->Dump ([$all_files], ['all_files']), "</pre>\n";
314   }
315
316   # Send FLUSH command to the daemon if necessary and possible.
317   flush_files ($all_files,
318       begin => time () - $timespan,
319       end => time (),
320       addr => gc_get_scalar ('UnixSockAddr', undef),
321       interval => gc_get_scalar ('Interval', 10));
322
323   for (@$all_files)
324   {
325     my $file = $_;
326     my $type = ucfirst (lc ($file->{'type'}));
327
328     $type =~ s/[^A-Za-z0-9_]//g;
329     $type =~ s/_([A-Za-z0-9])/\U$1\E/g;
330
331     if (!defined ($types->{$type}))
332     {
333       $types->{$type} = tl_load_type ($file->{'type'});
334       if (!$types->{$type})
335       {
336         cluck ("tl_load_type (" . $file->{'type'} . ") failed");
337         next;
338       }
339     }
340
341     $types->{$type}->addFiles ($file);
342   }
343 #print STDOUT Data::Dumper->Dump ([$types], ['types']);
344
345   print qq#    <table>\n#;
346   for (sort (keys %$types))
347   {
348     my $type = $_;
349     my $graphs_num = $types->{$type}->getGraphsNum ();
350
351     for (my $i = 0; $i < $graphs_num; $i++)
352     {
353       my $args = $types->{$type}->getGraphArgs ($i);
354       my $url = encode_entities ("graph.cgi?$args;begin=-$timespan");
355       my $id = sprintf ("graph%04i", $id_counter++);
356
357       print "      <tr>\n";
358       print "        <td rowspan=\"$graphs_num\">$type</td>\n" if ($i == 0);
359       print <<EOF;
360         <td>
361           <div class="graph_canvas">
362             <div class="graph_float">
363               <img id="${id}" class="graph_image"
364                 alt="A graph"
365                 src="$url" />
366               <div class="controls zoom">
367                 <div title="Earlier"
368                   onclick="nav_move_earlier ('${id}');">&#x2190;</div>
369                 <div title="Zoom out"
370                   onclick="nav_zoom_out ('${id}');">-</div>
371                 <div title="Zoom in"
372                   onclick="nav_zoom_in ('${id}');">+</div>
373                 <div title="Later"
374                   onclick="nav_move_later ('${id}');">&#x2192;</div>
375               </div>
376               <div class="controls preset">
377                 <div title="Show current hour"
378                   onclick="nav_time_reset ('${id}', 3600);">H</div>
379                 <div title="Show current day"
380                   onclick="nav_time_reset ('${id}', 86400);">D</div>
381                 <div title="Show current week"
382                   onclick="nav_time_reset ('${id}', 7 * 86400);">W</div>
383                 <div title="Show current month"
384                   onclick="nav_time_reset ('${id}', 31 * 86400);">M</div>
385                 <div title="Show current year"
386                   onclick="nav_time_reset ('${id}', 366 * 86400);">Y</div>
387                 <div title="Set all images to this timespan"
388                   onclick="nav_set_reference ('${id}');">!</div>
389               </div>
390             </div>
391           </div>
392         </td>
393 EOF
394       # print qq#        <td><img src="$url" /></td>\n#;
395       print "      </tr>\n";
396     }
397   }
398
399   print "    </table>\n";
400   end_html ();
401 }
402
403 =head1 SEE ALSO
404
405 L<Collectd::Graph::Type>
406
407 =head1 AUTHOR AND LICENSE
408
409 Copyright (c) 2008 by Florian Forster
410 E<lt>octoE<nbsp>atE<nbsp>verplant.orgE<gt>. Licensed under the terms of the GNU
411 General Public License, VersionE<nbsp>2 (GPLv2).
412
413 =cut
414
415 # vim: set shiftwidth=2 softtabstop=2 tabstop=8 :