1 | <?php |
||
2 | |||
3 | /* |
||
4 | * Copyright (C) 2009 Bruno Prémont <bonbons AT linux-vserver.org> |
||
5 | * |
||
6 | * This program is free software; you can redistribute it and/or modify it under |
||
7 | * the terms of the GNU General Public License as published by the Free Software |
||
8 | * Foundation; only version 2 of the License is applicable. |
||
9 | * |
||
10 | * This program is distributed in the hope that it will be useful, but WITHOUT |
||
11 | * ANY WARRANTY; without even the implied wadsnty of MERCHANTABILITY or FITNESS |
||
12 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more |
||
13 | * details. |
||
14 | * |
||
15 | * You should have received a copy of the GNU General Public License along with |
||
16 | * this program; if not, write to the Free Software Foundation, Inc., |
||
17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
||
18 | */ |
||
19 | |||
20 | use LibreNMS\Config; |
||
21 | |||
22 | require 'includes/html/collectd/config.php'; |
||
23 | require 'includes/html/collectd/functions.php'; |
||
24 | require 'includes/html/collectd/definitions.php'; |
||
25 | |||
26 | function makeTextBlock($text, $fontfile, $fontsize, $width) |
||
27 | { |
||
28 | // TODO: handle explicit line-break! |
||
29 | $words = explode(' ', $text); |
||
30 | $lines = [$words[0]]; |
||
31 | $currentLine = 0; |
||
32 | foreach ($words as $word) { |
||
33 | $lineSize = imagettfbbox($fontsize, 0, $fontfile, $lines[$currentLine] . ' ' . $word); |
||
34 | if (($lineSize[2] - $lineSize[0]) < $width) { |
||
35 | $lines[$currentLine] .= ' ' . $word; |
||
36 | } else { |
||
37 | $currentLine++; |
||
38 | $lines[$currentLine] = $word; |
||
39 | } |
||
40 | } |
||
41 | |||
42 | error_log(sprintf('Handles message "%s", %d words => %d/%d lines', $text, count($words), $currentLine, count($lines))); |
||
43 | |||
44 | return implode("\n", $lines); |
||
45 | }//end makeTextBlock() |
||
46 | |||
47 | /** |
||
48 | * No RRD files found that could match request |
||
49 | * |
||
50 | * @code HTTP error code |
||
51 | * @code_msg Short text description of HTTP error code |
||
52 | * @title Title for fake RRD graph |
||
53 | * @msg Complete error message to display in place of graph content |
||
54 | */ |
||
55 | function error($code, $code_msg, $title, $msg) |
||
56 | { |
||
57 | header(sprintf('HTTP/1.0 %d %s', $code, $code_msg)); |
||
58 | header('Pragma: no-cache'); |
||
59 | header('Expires: Mon, 01 Jan 2008 00:00:00 CET'); |
||
60 | header('Content-Type: image/png'); |
||
61 | $w = (Config::get('rrd_width') + 81); |
||
62 | $h = (Config::get('rrd_height') + 79); |
||
63 | |||
64 | $png = imagecreate($w, $h); |
||
65 | $c_bkgnd = imagecolorallocate($png, 240, 240, 240); |
||
66 | $c_fgnd = imagecolorallocate($png, 255, 255, 255); |
||
67 | $c_blt = imagecolorallocate($png, 208, 208, 208); |
||
68 | $c_brb = imagecolorallocate($png, 160, 160, 160); |
||
69 | $c_grln = imagecolorallocate($png, 114, 114, 114); |
||
70 | $c_grarr = imagecolorallocate($png, 128, 32, 32); |
||
71 | $c_txt = imagecolorallocate($png, 0, 0, 0); |
||
72 | $c_etxt = imagecolorallocate($png, 64, 0, 0); |
||
73 | |||
74 | if (function_exists('imageantialias')) { |
||
75 | imageantialias($png, true); |
||
76 | } |
||
77 | |||
78 | imagefilledrectangle($png, 0, 0, $w, $h, $c_bkgnd); |
||
79 | imagefilledrectangle($png, 51, 33, ($w - 31), ($h - 47), $c_fgnd); |
||
80 | imageline($png, 51, 30, 51, ($h - 43), $c_grln); |
||
81 | imageline($png, 48, ($h - 46), ($w - 28), ($h - 46), $c_grln); |
||
82 | imagefilledpolygon($png, [49, 30, 51, 26, 53, 30], 3, $c_grarr); |
||
83 | imagefilledpolygon($png, [$w - 28, $h - 48, $w - 24, $h - 46, $w - 28, $h - 44], 3, $c_grarr); |
||
84 | imageline($png, 0, 0, $w, 0, $c_blt); |
||
85 | imageline($png, 0, 1, $w, 1, $c_blt); |
||
86 | imageline($png, 0, 0, 0, $h, $c_blt); |
||
87 | imageline($png, 1, 0, 1, $h, $c_blt); |
||
88 | imageline($png, ($w - 1), 0, ($w - 1), $h, $c_brb); |
||
89 | imageline($png, ($w - 2), 1, ($w - 2), $h, $c_brb); |
||
90 | imageline($png, 1, ($h - 2), $w, ($h - 2), $c_brb); |
||
91 | imageline($png, 0, ($h - 1), $w, ($h - 1), $c_brb); |
||
92 | |||
93 | imagestring($png, 4, ceil(($w - strlen($title) * imagefontwidth(4)) / 2), 10, $title, $c_txt); |
||
94 | imagestring($png, 5, 60, 35, sprintf('%s [%d]', $code_msg, $code), $c_etxt); |
||
95 | if (function_exists('imagettfbbox') && is_file(Config::get('error_font'))) { |
||
96 | // Detailled error message |
||
97 | $errorfont = Config::get('error_font'); |
||
98 | $fmt_msg = makeTextBlock($msg, $errorfont, 10, ($w - 86)); |
||
99 | $fmtbox = imagettfbbox(12, 0, $errorfont, $fmt_msg); |
||
100 | imagettftext($png, 10, 0, 55, (35 + 3 + imagefontwidth(5) - $fmtbox[7] + $fmtbox[1]), $c_txt, $errorfont, $fmt_msg); |
||
101 | } else { |
||
102 | imagestring($png, 4, 53, (35 + 6 + imagefontwidth(5)), $msg, $c_txt); |
||
103 | } |
||
104 | |||
105 | imagepng($png); |
||
106 | imagedestroy($png); |
||
107 | }//end error() |
||
108 | |||
109 | /** |
||
110 | * No RRD files found that could match request |
||
111 | */ |
||
112 | function error404($title, $msg) |
||
113 | { |
||
114 | return error(404, 'Not found', $title, $msg); |
||
115 | }//end error404() |
||
116 | |||
117 | function error500($title, $msg) |
||
118 | { |
||
119 | return error(500, 'Not found', $title, $msg); |
||
120 | }//end error500() |
||
121 | |||
122 | /** |
||
123 | * Incomplete / invalid request |
||
124 | */ |
||
125 | function error400($title, $msg) |
||
126 | { |
||
127 | return error(400, 'Bad request', $title, $msg); |
||
128 | }//end error400() |
||
129 | |||
130 | // Process input arguments |
||
131 | // $host = read_var('host', $_GET, null); |
||
132 | $host = $device['hostname']; |
||
133 | if (is_null($host)) { |
||
134 | return error400('?/?-?/?', 'Missing host name'); |
||
135 | } elseif (! is_string($host)) { |
||
136 | return error400('?/?-?/?', 'Expecting exactly 1 host name'); |
||
137 | } elseif (strlen($host) == 0) { |
||
138 | return error400('?/?-?/?', 'Host name may not be blank'); |
||
139 | } |
||
140 | |||
141 | $plugin = read_var('c_plugin', $_GET, null); |
||
142 | if (is_null($plugin)) { |
||
143 | return error400($host . '/?-?/?', 'Missing plugin name'); |
||
144 | } elseif (! is_string($plugin)) { |
||
0 ignored issues
–
show
introduced
by
Loading history...
|
|||
145 | return error400($host . '/?-?/?', 'Plugin name must be a string'); |
||
146 | } elseif (strlen($plugin) == 0) { |
||
147 | return error400($host . '/?-?/?', 'Plugin name may not be blank'); |
||
148 | } |
||
149 | |||
150 | $pinst = read_var('c_plugin_instance', $_GET, ''); |
||
151 | if (! is_string($pinst)) { |
||
0 ignored issues
–
show
|
|||
152 | return error400($host . '/' . $plugin . '-?/?', 'Plugin instance name must be a string'); |
||
153 | } |
||
154 | |||
155 | $type = read_var('c_type', $_GET, ''); |
||
156 | if (is_null($type)) { |
||
157 | return error400($host . '/' . $plugin . (strlen($pinst) ? '-' . $pinst : '') . '/?', 'Missing type name'); |
||
158 | } elseif (! is_string($type)) { |
||
0 ignored issues
–
show
|
|||
159 | return error400($host . '/' . $plugin . (strlen($pinst) ? '-' . $pinst : '') . '/?', 'Type name must be a string'); |
||
160 | } elseif (strlen($type) == 0) { |
||
161 | return error400($host . '/' . $plugin . (strlen($pinst) ? '-' . $pinst : '') . '/?', 'Type name may not be blank'); |
||
162 | } |
||
163 | |||
164 | $tinst = read_var('c_type_instance', $_GET, ''); |
||
165 | |||
166 | $graph_identifier = $host . '/' . $plugin . (strlen($pinst) ? '-' . $pinst : '') . '/' . $type . (strlen($tinst) ? '-' . $tinst : '-*'); |
||
167 | |||
168 | $timespan = read_var('timespan', $_GET, Config::get('timespan.0.name')); |
||
169 | $timespan_ok = false; |
||
170 | foreach (Config::get('timespan') as &$ts) { |
||
171 | if ($ts['name'] == $timespan) { |
||
172 | $timespan_ok = true; |
||
173 | } |
||
174 | } |
||
175 | |||
176 | if (! $timespan_ok) { |
||
177 | return error400($graph_identifier, 'Unknown timespan requested'); |
||
178 | } |
||
179 | |||
180 | $logscale = (bool) read_var('logarithmic', $_GET, false); |
||
181 | $tinylegend = (bool) read_var('tinylegend', $_GET, false); |
||
182 | |||
183 | // Check that at least 1 RRD exists for the specified request |
||
184 | $all_tinst = collectd_list_tinsts($host, $plugin, $pinst, $type); |
||
185 | if (count($all_tinst) == 0) { |
||
186 | return error404($graph_identifier, 'No rrd file found for graphing'); |
||
187 | } |
||
188 | |||
189 | // Now that we are read, do the bulk work |
||
190 | load_graph_definitions($logscale, $tinylegend); |
||
191 | |||
192 | $pinst = strlen($pinst) == 0 ? null : $pinst; |
||
193 | $tinst = strlen($tinst) == 0 ? null : $tinst; |
||
194 | |||
195 | $opts = []; |
||
196 | $opts['timespan'] = $timespan; |
||
197 | if ($logscale) { |
||
198 | $opts['logarithmic'] = 1; |
||
199 | } |
||
200 | |||
201 | if ($tinylegend) { |
||
202 | $opts['tinylegend'] = 1; |
||
203 | } |
||
204 | |||
205 | $rrd_cmd = false; |
||
206 | if (isset($MetaGraphDefs[$type])) { |
||
207 | $identifiers = []; |
||
208 | foreach ($all_tinst as &$atinst) { |
||
209 | $identifiers[] = collectd_identifier($host, $plugin, $type, is_null($pinst) ? '' : $pinst, $atinst); |
||
210 | } |
||
211 | |||
212 | collectd_flush($identifiers); |
||
213 | $rrd_cmd = $MetaGraphDefs[$type]($host, $plugin, $pinst, $type, $all_tinst, $opts); |
||
214 | } else { |
||
215 | if (! in_array(is_null($tinst) ? '' : $tinst, $all_tinst)) { |
||
216 | return error404($host . '/' . $plugin . (! is_null($pinst) ? '-' . $pinst : '') . '/' . $type . (! is_null($tinst) ? '-' . $tinst : ''), 'No rrd file found for graphing'); |
||
217 | } |
||
218 | |||
219 | collectd_flush(collectd_identifier($host, $plugin, $type, is_null($pinst) ? '' : $pinst, is_null($tinst) ? '' : $tinst)); |
||
220 | if (isset($GraphDefs[$type])) { |
||
221 | $rrd_cmd = collectd_draw_generic($timespan, $host, $plugin, $type, $pinst, $tinst); |
||
222 | } else { |
||
223 | $rrd_cmd = collectd_draw_rrd($host, $plugin, $type, $pinst, $tinst); |
||
224 | } |
||
225 | } |
||
226 | |||
227 | if (isset($rrd_cmd)) { |
||
228 | $from = (int) ($_GET['from'] ?? time() - 86400); |
||
229 | $to = (int) ($_GET['to'] ?? time()); |
||
230 | |||
231 | $rrd_cmd .= ' -s ' . $from . ' -e ' . $to; |
||
232 | } |
||
233 | |||
234 | if ($_GET['legend'] == 'no') { |
||
235 | $rrd_cmd .= ' -g '; |
||
236 | } |
||
237 | |||
238 | if ($height < '99') { |
||
239 | $rrd_cmd .= ' --only-graph '; |
||
240 | } |
||
241 | |||
242 | if ($width <= '300') { |
||
243 | $rrd_cmd .= ' --font LEGEND:7:' . Config::get('mono_font') . ' --font AXIS:6:' . Config::get('mono_font') . ' '; |
||
244 | } else { |
||
245 | $rrd_cmd .= ' --font LEGEND:8:' . Config::get('mono_font') . ' --font AXIS:7:' . Config::get('mono_font') . ' '; |
||
246 | } |
||
247 | |||
248 | if (isset($_GET['debug'])) { |
||
249 | header('Content-Type: text/plain; charset=utf-8'); |
||
250 | printf("Would have executed:\n%s\n", $rrd_cmd); |
||
251 | |||
252 | return 0; |
||
253 | } elseif ($rrd_cmd) { |
||
254 | header('Content-Type: image/png'); |
||
255 | header('Cache-Control: max-age=60'); |
||
256 | $rt = 0; |
||
257 | passthru($rrd_cmd, $rt); |
||
258 | if ($rt !== 0) { |
||
259 | return error500($graph_identifier, 'RRD failed to generate the graph: ' . $rt); |
||
260 | } |
||
261 | |||
262 | return $rt; |
||
263 | } else { |
||
264 | return error500($graph_identifier, 'Failed to tell RRD how to generate the graph'); |
||
265 | } |
||
266 |