Ticket #3: munin-cgi-graph

File munin-cgi-graph, 8.4 kB (added by blueyed, 3 years ago)

For reference: my munin-cgi-graph (derived from the patch)

Line 
1 #!/usr/bin/perl  -Tw
2 # -*- perl -*-
3 #
4 # Copyright (C) 2004 Jimmy Olsen
5 #
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; version 2 dated June,
9 # 1991.
10 #
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 # GNU General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
19 #
20 # $Id: munin-cgi-graph.in 1448 2008-02-07 21:43:39Z janl $
21 #
22 # Please see http://munin.projects.linpro.no/wiki/CgiHowto for how to
23 # use this, and how to convert it to fastcgi which will improve speed.
24 #
25
26 use RRDs;
27 use Munin;
28 use strict;
29 use IO::Handle;
30 use Date::Manip;
31 use POSIX qw(strftime);
32 use IPC::SysV qw(IPC_CREAT);
33 use CGI::Fast;
34
35 my $GRAPHER = "/usr/share/munin/munin-graph";
36 my $conffile = "/etc/munin/munin.conf";
37
38 my %TIMES   = ( "day"   => ["--noweek", "--nomonth", "--noyear", "--nosumweek", "--nosumyear"],
39                 "week"  => ["--noday", "--nomonth", "--noyear", "--nosumweek", "--nosumyear"],
40                 "month" => ["--noday", "--noweek", "--noyear", "--nosumweek", "--nosumyear"],
41                 "year"  => ["--noday", "--noweek", "--nomonth", "--nosumweek", "--nosumyear"],
42                 "week-sum"  => ["--noday", "--nomonth", "--noyear", "--noweek", "--nosumyear"],
43                 "year-sum"  => ["--noday", "--noweek", "--nomonth", "--nosumweek", "--noyear"]
44             );
45
46 my %period  = ( "day"   => 300,
47                 "week"  => 1800,
48                 "month" => 7200,
49                 "year"  => 86400,
50                 "week-sum" => 1800,
51                 "year-sum" => 86400
52             );
53
54 my $log = new IO::Handle;
55 my $scale = "day";
56 my $host  = "";
57 my $serv  = "";
58 my $dom   = "";
59 my $lock  = "";
60 my $IPC_KEY = 9340;
61
62 my $config = &munin_readconfig ($conffile);
63
64
65 # BEGIN FAST-CGI LOOP:
66 while (new CGI::Fast)
67 {
68     my $path = $ENV{PATH_INFO} || "";
69     $path =~ s/^\///;
70     ($dom, $host, $serv) = split /\//, $path;
71     ($serv, $scale) = split /-/, $serv, 2;
72     $scale =~ s/\.png$//;
73
74     if (! &verify_parameters ($dom, $host, $serv, $scale))
75     {
76         print "Status: 500\n";
77         print "Content-Type: text/html\n";
78         print "\n";
79         print "Invalid parameters!";
80         next;
81     }
82
83     my $filename = get_picture_filename ($config, $dom, $host, $serv, $scale);
84
85     my $time = time;
86
87     # If a "Cache-Control: no-cache" header gets send, we regenerate the image in every case:
88     my $no_cache = defined($ENV{HTTP_CACHE_CONTROL}) && $ENV{HTTP_CACHE_CONTROL} =~ /no-cache/i;
89
90     if ($no_cache || ! &graph_usable ($filename, $time))
91     {
92         my $ret = (&draw_graph ($host, $serv, $TIMES{$scale}) || "Unknown error");
93         if (! -f $filename)
94         {
95         ::logger ("Warning: Could not draw graph \"$host-$serv-$scale.png\": $ret");
96         print "Status: 500\n";
97         print "Content-Type: image/png\n";
98         print "\n";
99         next;
100         }
101     }
102
103     my @stats = stat ($filename);
104     my $last_modified = strftime ("%a, %d %b %Y %H:%M:%S %Z", localtime ($stats[9]));
105     # "Expires" has to use last modified time as base:
106     my $expires = strftime ("%a, %d %b %Y %H:%M:%S GMT", gmtime($stats[9]+($period{$scale}-($stats[9]%$period{$scale}))));
107
108     # Check for If-Modified-Since and send 304 if not changed:
109     if (defined $ENV{HTTP_IF_MODIFIED_SINCE} and
110         !&modified ($ENV{HTTP_IF_MODIFIED_SINCE}, $stats[9]-1))
111     {
112         print "Status: 304\n";
113         print "Content-Type: image/png\n";
114         print "Expires: ", $expires, "\n";
115         print "Last-Modified: $last_modified\n";
116         print "\n";
117         next;
118     }
119
120     print "Content-Type: image/png\n";
121     print "Expires: ", $expires, "\n";
122     print "Last-Modified: $last_modified\n";
123     print "\n";
124
125     # Try to police the number of concurrent rrdgraph instances.  The
126     # third value is the default maximum.
127
128     # Fox kindly submitted a patch to convert to SysV IPC semaphores.
129     # Lovely! (ticket #499).
130
131     my $max_cgi_graph_jobs = &munin_get ($config, "max_cgi_graph_jobs" , 6, $dom);
132
133     my $opstring;
134
135     # Get semaphore handle
136     my $semid = semget($IPC_KEY, 0, 0 );
137
138     if(!$semid) {
139         # Or create it if needed
140         $semid = semget($IPC_KEY, 1 , 0666 | &IPC_CREAT ) ||
141             die "Cannot create semaphore: $!";
142
143         # And initialize to max_cgi_graph_jobs
144         $opstring = pack("s!s!s!",0, $max_cgi_graph_jobs,0);
145         semop($semid,$opstring) || die "Cannot semop: $!";
146     }
147
148     # Decrement, or lock/hang/yield if already 0
149     $opstring = pack("s!s!s!",0, -1, 0);
150     semop($semid,$opstring);
151
152     &graph ($filename);
153
154     # Increment (and release waiting processes)
155     $opstring = pack("s!s!s!",0, 1, 0);
156     semop($semid,$opstring);
157 }
158 # END FAST-CGI LOOP
159
160
161 sub graph {
162     my $filename = shift;
163
164     open (GRAPH, $filename) or die "Warning: Could not open picture file \"$filename\" for reading: $!\n";
165     print while (<GRAPH>);
166     close (GRAPH);
167 }
168
169 sub get_picture_filename {
170     my $config  = shift;
171     my $domain  = shift;
172     my $name    = shift;
173     my $service = shift;
174     my $scale   = shift;
175
176     return "$config->{'htmldir'}/$domain/$name-$service-$scale.png";
177 }
178
179 sub logger_open {
180     my $dirname = shift;
181
182     if (!$log->opened)
183     {
184         unless (open ($log, ">>$dirname/munin-cgi-graph.log"))
185         {
186             print STDERR "Warning: Could not open log file \"$dirname/munin-cgi-graph.log\" for writing: $!";
187         }
188     }
189 }
190
191 sub logger {
192   my ($comment) = @_;
193   my $now = strftime ("%b %d %H:%M:%S", localtime);
194
195   if ($log->opened)
196   {
197           print $log "$now - $comment\n";
198   }
199   else
200   {
201           if (defined $config->{logdir})
202           {
203                   if (open ($log, ">>$config->{logdir}/munin-cgi-graph.log"))
204                   {
205                           print $log "$now - $comment\n";
206                   }
207                   else
208                   {
209                           print STDERR "Warning: Could not open log file \"$config->{logdir}/munin-cgi-graph.log\" for wr
210 iting: $!";
211                           print STDERR "$now - $comment\n";
212                   }
213           }
214           else
215           {
216                   print STDERR "$now - $comment\n";
217           }
218     }
219 }
220
221
222 sub verify_parameters
223 {
224         my $dom   = shift;
225         my $host  = shift;
226         my $serv  = shift;
227         my $scale = shift;
228
229         if (!$dom)
230         {
231                 print STDERR "Warning: Request for graph without specifying domain. Bailing out.\n";
232                 return 0;
233         }
234         if (!$host)
235         {
236                 print STDERR "Warning: Request for graph without specifying host. Bailing out.\n";
237                 return 0;
238         }
239         if (!$serv)
240         {
241                 print STDERR "Warning: Request for graph without specifying service. Bailing out.\n";
242                 return 0;
243         }
244
245         if (!$scale)
246         {
247                 print STDERR "Warning: Request for graph without specifying scale. Bailing out.\n";
248                 return 0;
249         }
250         else
251         {
252                 if (!defined $TIMES{$scale})
253                 {
254                         print STDERR "Warning: Weird scale setting \"$scale\". Bailing out.\n";
255                         return 0;
256                 }
257         }
258         return 1;
259 }
260
261 sub graph_usable {
262     my $filename = shift;
263     my $time     = shift;
264
265     if (-f $filename) {
266         my @stats = stat (_);
267         # $stats[9] holds the "last update" time and this needs to be in the last update period:
268         if ($stats[9] > ($time - $time%$period{$scale})) {
269 #print STDERR "Skipping munin-graph-run for \"$filename\".\n";
270 #print STDERR ("Graph unexpired for $scale. ($stats[9] , $time, ". ($time%$period{$scale}). ", ". ($time - $time%$period{$scale}). ").\n");
271             return 1;
272         } else {
273 #print STDERR ("Graph expired for $scale. ($stats[9] , $time, ". ($time%$period{$scale}). ", ". ($time - $time%$period{$scale}). ").\n");
274             return 0;
275         }
276     }
277     return 0;
278 }
279
280 sub draw_graph {
281     my $host  = shift;
282     my $serv  = shift;
283     my $scale = shift;
284
285     $serv =~ s/[^\w_\/"'\[\]\(\)+=-]/_/; $serv =~ /^(.+)$/; $serv = $1; #"
286     $host =~ s/[^\w_\/"'\[\]\(\)+=-]/_/; $host =~ /^(.+)$/; $host = $1; #"
287
288     my @params = ($GRAPHER);
289     push @params, @$scale;
290     push @params, "--skip-locking", "--skip-stats", "--nolazy";
291     push @params, "--host", $host, "--service", $serv;
292     push @params, "STDERR>&STDOUT";
293
294     my $file = "/dev/null";
295     unless (open (IN, "-|"))
296     {
297         %ENV=();
298         exec @params;
299     }
300     $file = join (' ', <IN>);
301     close (IN);
302     return $file;
303 }
304
305 sub modified {
306 # Format of since_string If-Modified-Since: Wed, 23 Jun 2004 16:11:06 GMT
307
308     my $since_string = shift;
309     my $created      = shift;
310     my $ifmodsec = &UnixDate (&ParseDateString ($since_string), "%s");
311
312     return 1 if ($ifmodsec < $created);
313     return 0;
314 }
315
316 # vim: syntax=perl ts=8