contrib/php-collection: Save/load graph list and fix HTTPS handling.
[collectd.git] / contrib / php-collection / functions.php
1 <?php // vim:fenc=utf-8:filetype=php:ts=4
2 /*
3  * Copyright (C) 2009  Bruno PrĂ©mont <bonbons AT linux-vserver.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
19 define('REGEXP_HOST', '/^[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(\\.[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/');
20 define('REGEXP_PLUGIN', '/^[a-zA-Z0-9_.-]+$/');
21
22 /**
23  * Read input variable from GET, POST or COOKIE taking
24  * care of magic quotes
25  * @name Name of value to return
26  * @array User-input array ($_GET, $_POST or $_COOKIE)
27  * @default Default value
28  * @return $default if name in unknown in $array, otherwise
29  *         input value with magic quotes stripped off
30  */
31 function read_var($name, &$array, $default = null) {
32         if (isset($array[$name])) {
33                 if (is_array($array[$name])) {
34                         if (get_magic_quotes_gpc()) {
35                                 $ret = array();
36                                 while (list($k, $v) = each($array[$name]))
37                                         $ret[stripslashes($k)] = stripslashes($v);
38                                 return $ret;
39                         } else
40                                 return $array[$name];
41                 } else if (is_string($array[$name]) && get_magic_quotes_gpc()) {
42                         return stripslashes($array[$name]);
43                 } else
44                         return $array[$name];
45         } else
46                 return $default;
47 }
48
49 /**
50  * Alphabetically compare host names, comparing label
51  * from tld to node name
52  */
53 function collectd_compare_host($a, $b) {
54         $ea = explode('.', $a);
55         $eb = explode('.', $b);
56         $i = count($ea) - 1;
57         $j = count($eb) - 1;
58         while ($i >= 0 && $j >= 0)
59                 if (($r = strcmp($ea[$i--], $eb[$j--])) != 0)
60                         return $r;
61         return 0;
62 }
63
64 /**
65  * Fetch list of hosts found in collectd's datadirs.
66  * @return Sorted list of hosts (sorted by label from rigth to left)
67  */
68 function collectd_list_hosts() {
69         global $config;
70
71         $hosts = array();
72         foreach($config['datadirs'] as $datadir)
73                 if ($d = @opendir($datadir)) {
74                         while (($dent = readdir($d)) !== false)
75                                 if ($dent != '.' && $dent != '..' && is_dir($datadir.'/'.$dent) && preg_match(REGEXP_HOST, $dent))
76                                         $hosts[] = $dent;
77                         closedir($d);
78                 } else
79                         error_log('Failed to open datadir: '.$datadir);
80         $hosts = array_unique($hosts);
81         usort($hosts, 'collectd_compare_host');
82         return $hosts;
83 }
84
85 /**
86  * Fetch list of plugins found in collectd's datadirs for given host.
87  * @arg_host Name of host for which to return plugins
88  * @return Sorted list of plugins (sorted alphabetically)
89  */
90 function collectd_list_plugins($arg_host) {
91         global $config;
92
93         $plugins = array();
94         foreach ($config['datadirs'] as $datadir)
95                 if (preg_match(REGEXP_HOST, $arg_host) && ($d = @opendir($datadir.'/'.$arg_host))) {
96                         while (($dent = readdir($d)) !== false)
97                                 if ($dent != '.' && $dent != '..' && is_dir($datadir.'/'.$arg_host.'/'.$dent)) {
98                                         if ($i = strpos($dent, '-'))
99                                                 $plugins[] = substr($dent, 0, $i);
100                                         else
101                                                 $plugins[] = $dent;
102                                 }
103                         closedir($d);
104                 }
105         $plugins = array_unique($plugins);
106         sort($plugins);
107         return $plugins;
108 }
109
110 /**
111  * Fetch list of plugin instances found in collectd's datadirs for given host+plugin
112  * @arg_host Name of host
113  * @arg_plugin Name of plugin
114  * @return Sorted list of plugin instances (sorted alphabetically)
115  */
116 function collectd_list_pinsts($arg_host, $arg_plugin) {
117         global $config;
118
119         $pinsts = array();
120         foreach ($config['datadirs'] as $datadir)
121                 if (preg_match(REGEXP_HOST, $arg_host) && ($d = opendir($datadir.'/'.$arg_host))) {
122                         while (($dent = readdir($d)) !== false)
123                                 if ($dent != '.' && $dent != '..' && is_dir($datadir.'/'.$arg_host.'/'.$dent)) {
124                                         if ($i = strpos($dent, '-')) {
125                                                 $plugin = substr($dent, 0, $i);
126                                                 $pinst  = substr($dent, $i+1);
127                                         } else {
128                                                 $plugin = $dent;
129                                                 $pinst  = '';
130                                         }
131                                         if ($plugin == $arg_plugin)
132                                                 $pinsts[] = $pinst;
133                                 }
134                         closedir($d);
135                 }
136         $pinsts = array_unique($pinsts);
137         sort($pinsts);
138         return $pinsts;
139 }
140
141 /**
142  * Fetch list of types found in collectd's datadirs for given host+plugin+instance
143  * @arg_host Name of host
144  * @arg_plugin Name of plugin
145  * @arg_pinst Plugin instance
146  * @return Sorted list of types (sorted alphabetically)
147  */
148 function collectd_list_types($arg_host, $arg_plugin, $arg_pinst) {
149         global $config;
150
151         $types = array();
152         $my_plugin = $arg_plugin . (strlen($arg_pinst) ? '-'.$arg_pinst : '');
153         if (!preg_match(REGEXP_PLUGIN, $my_plugin))
154                 return $types;
155         foreach ($config['datadirs'] as $datadir)
156                 if (preg_match(REGEXP_HOST, $arg_host) && ($d = @opendir($datadir.'/'.$arg_host.'/'.$my_plugin))) {
157                         while (($dent = readdir($d)) !== false)
158                                 if ($dent != '.' && $dent != '..' && is_file($datadir.'/'.$arg_host.'/'.$my_plugin.'/'.$dent) && substr($dent, strlen($dent)-4) == '.rrd') {
159                                         $dent = substr($dent, 0, strlen($dent)-4);
160                                         if ($i = strpos($dent, '-'))
161                                                 $types[] = substr($dent, 0, $i);
162                                         else
163                                                 $types[] = $dent;
164                                 }
165                         closedir($d);
166                 }
167         $types = array_unique($types);
168         sort($types);
169         return $types;
170 }
171
172 /**
173  * Fetch list of type instances found in collectd's datadirs for given host+plugin+instance+type
174  * @arg_host Name of host
175  * @arg_plugin Name of plugin
176  * @arg_pinst Plugin instance
177  * @arg_type Type
178  * @return Sorted list of type instances (sorted alphabetically)
179  */
180 function collectd_list_tinsts($arg_host, $arg_plugin, $arg_pinst, $arg_type) {
181         global $config;
182
183         $tinsts = array();
184         $my_plugin = $arg_plugin . (strlen($arg_pinst) ? '-'.$arg_pinst : '');
185         if (!preg_match(REGEXP_PLUGIN, $my_plugin))
186                 return $types;
187         foreach ($config['datadirs'] as $datadir)
188                 if (preg_match(REGEXP_HOST, $arg_host) && ($d = @opendir($datadir.'/'.$arg_host.'/'.$my_plugin))) {
189                         while (($dent = readdir($d)) !== false)
190                                 if ($dent != '.' && $dent != '..' && is_file($datadir.'/'.$arg_host.'/'.$my_plugin.'/'.$dent) && substr($dent, strlen($dent)-4) == '.rrd') {
191                                         $dent = substr($dent, 0, strlen($dent)-4);
192                                         if ($i = strpos($dent, '-')) {
193                                                 $type  = substr($dent, 0, $i);
194                                                 $tinst = substr($dent, $i+1);
195                                         } else {
196                                                 $type  = $dent;
197                                                 $tinst = '';
198                                         }
199                                         if ($type == $arg_type)
200                                                 $tinsts[] = $tinst;
201                                 }
202                         closedir($d);
203                 }
204         $tinsts = array_unique($tinsts);
205         sort($tinsts);
206         return $tinsts;
207 }
208
209 /**
210  * Parse symlinks in order to get an identifier that collectd understands
211  * (e.g. virtualisation is collected on host for individual VMs and can be
212  *  symlinked to the VM's hostname, support FLUSH for these by flushing
213  *  on the host-identifier instead of VM-identifier)
214  * @host Host name
215  * @plugin Plugin name
216  * @pinst Plugin instance
217  * @type Type name
218  * @tinst Type instance
219  * @return Identifier that collectd's FLUSH command understands
220  */
221 function collectd_identifier($host, $plugin, $pinst, $type, $tinst) {
222         global $config;
223         $rrd_realpath    = null;
224         $orig_identifier = sprintf('%s/%s%s%s/%s%s%s', $host, $plugin, strlen($pinst) ? '-' : '', $pinst, $type, strlen($tinst) ? '-' : '', $tinst);
225         $identifier      = null;
226         foreach ($config['datadirs'] as $datadir)
227                 if (is_file($datadir.'/'.$orig_identifier.'.rrd')) {
228                         $rrd_realpath = realpath($datadir.'/'.$orig_identifier.'.rrd');
229                         break;
230                 }
231         if ($rrd_realpath) {
232                 $identifier   = basename($rrd_realpath);
233                 $identifier   = substr($identifier, 0, strlen($identifier)-4);
234                 $rrd_realpath = dirname($rrd_realpath);
235                 $identifier   = basename($rrd_realpath).'/'.$identifier;
236                 $rrd_realpath = dirname($rrd_realpath);
237                 $identifier   = basename($rrd_realpath).'/'.$identifier;
238         }
239
240         if (is_null($identifier))
241                 return $orig_identifier;
242         else
243                 return $identifier;
244 }
245
246 /**
247  * Tell collectd that it should FLUSH all data it has regarding the
248  * graph we are about to generate.
249  * @host Host name
250  * @plugin Plugin name
251  * @pinst Plugin instance
252  * @type Type name
253  * @tinst Type instance
254  */
255 function collectd_flush($identifier) {
256         global $config;
257
258         if (!$config['collectd_sock'])
259                 return false;
260         if (is_null($identifier) || (is_array($identifier) && count($identifier) == 0) || !(is_string($identifier) || is_array($identifier)))
261                 return false;
262
263         if (is_null($host) || !is_string($host) || strlen($host) == 0)
264                 return false;
265         if (is_null($plugin) || !is_string($plugin) || strlen($plugin) == 0)
266                 return false;
267         if (is_null($pinst) || !is_string($pinst))
268                 return false;
269         if (is_null($type) || !is_string($type) || strlen($type) == 0)
270                 return false;
271         if (is_null($tinst) || (is_array($tinst) && count($tinst) == 0) || !(is_string($tinst) || is_array($tinst)))
272                 return false;
273
274         $u_errno  = 0;
275         $u_errmsg = '';
276         if ($socket = @fsockopen($config['collectd_sock'], 0, $u_errno, $u_errmsg)) {
277                 $cmd = 'FLUSH plugin=rrdtool';
278                 if (is_array($identifier)) {
279                         foreach ($identifier as $val)
280                                 $cmd .= sprintf(' identifier="%s"', $val);
281                 } else
282                         $cmd .= sprintf(' identifier="%s"', $identifier);
283                 $cmd .= "\n";
284
285                 $r = fwrite($socket, $cmd, strlen($cmd));
286                 if ($r === false || $r != strlen($cmd))
287                         error_log(sprintf("graph.php: Failed to write whole command to unix-socket: %d out of %d written", $r === false ? -1 : $r, strlen($cmd)));
288
289                 $resp = fgets($socket);
290                 if ($resp === false)
291                         error_log(sprintf("graph.php: Failed to read response from collectd for command: %s", trim($cmd)));
292
293                 $n = (int)$resp;
294                 while ($n-- > 0)
295                         fgets($socket);
296
297                 fclose($socket);
298         } else
299                 error_log(sprintf("graph.php: Failed to open unix-socket to collectd: %d: %s", $u_errno, $u_errmsg));
300 }
301
302 class CollectdColor {
303         private $r = 0;
304         private $g = 0;
305         private $b = 0;
306
307         function __construct($value = null) {
308                 if (is_null($value)) {
309                 } else if (is_array($value)) {
310                         if (isset($value['r']))
311                                 $this->r = $value['r'] > 0 ? ($value['r'] > 1 ? 1 : $value['r']) : 0;
312                         if (isset($value['g']))
313                                 $this->g = $value['g'] > 0 ? ($value['g'] > 1 ? 1 : $value['g']) : 0;
314                         if (isset($value['b']))
315                                 $this->b = $value['b'] > 0 ? ($value['b'] > 1 ? 1 : $value['b']) : 0;
316                 } else if (is_string($value)) {
317                         $matches = array();
318                         if ($value == 'random') {
319                                 $this->randomize();
320                         } else if (preg_match('/([0-9A-Fa-f][0-9A-Fa-f])([0-9A-Fa-f][0-9A-Fa-f])([0-9A-Fa-f][0-9A-Fa-f])/', $value, $matches)) {
321                                 $this->r = ('0x'.$matches[1]) / 255.0;
322                                 $this->g = ('0x'.$matches[2]) / 255.0;
323                                 $this->b = ('0x'.$matches[3]) / 255.0;
324                         }
325                 } else if (is_a($value, 'CollectdColor')) {
326                         $this->r = $value->r;
327                         $this->g = $value->g;
328                         $this->b = $value->b;
329                 }
330         }
331
332         function randomize() {
333                 $this->r = rand(0, 255) / 255.0;
334                 $this->g = rand(0, 255) / 255.0;
335                 $this->b = 0.0;
336                 $min = 0.0;
337                 $max = 1.0;
338
339                 if (($this->r + $this->g) < 1.0) {
340                         $min = 1.0 - ($this->r + $this->g);
341                 } else {
342                         $max = 2.0 - ($this->r + $this->g);
343                 }
344                 $this->b = $min + ((rand(0, 255)/255.0) * ($max - $min));
345         }
346
347         function fade($bkgnd = null, $alpha = 0.25) {
348                 if (is_null($bkgnd) || !is_a($bkgnd, 'CollectdColor')) {
349                         $bg_r = 1.0;
350                         $bg_g = 1.0;
351                         $bg_b = 1.0;
352                 } else {
353                         $bg_r = $bkgnd->r;
354                         $bg_g = $bkgnd->g;
355                         $bg_b = $bkgnd->b;
356                 }
357
358                 $this->r = $alpha * $this->r + ((1.0 - $alpha) * $bg_r);
359                 $this->g = $alpha * $this->g + ((1.0 - $alpha) * $bg_g);
360                 $this->b = $alpha * $this->b + ((1.0 - $alpha) * $bg_b);
361         }
362
363         function as_array() {
364                 return array('r'=>$this->r, 'g'=>$this->g, 'b'=>$this->b);
365         }
366
367         function as_string() {
368                 $r = (int)($this->r*255);
369                 $g = (int)($this->g*255);
370                 $b = (int)($this->b*255);
371                 return sprintf('%02x%02x%02x', $r > 255 ? 255 : $r, $g > 255 ? 255 : $g, $b > 255 ? 255 : $b);
372         }
373 }
374
375
376 /**
377  * Helper function to strip quotes from RRD output
378  * @str RRD-Info generated string
379  * @return String with one surrounding pair of quotes stripped
380  */
381 function rrd_strip_quotes($str) {
382         if ($str[0] == '"' && $str[strlen($str)-1] == '"')
383                 return substr($str, 1, strlen($str)-2);
384         else
385                 return $str;
386 }
387
388 /**
389  * Determine useful information about RRD file
390  * @file Name of RRD file to analyse
391  * @return Array describing the RRD file
392  */
393 function rrd_info($file) {
394         $info = array('filename'=>$file);
395
396         $rrd = popen(RRDTOOL.' info '.escapeshellarg($file), 'r');
397         if ($rrd) {
398                 while (($s = fgets($rrd)) !== false) {
399                         $p = strpos($s, '=');
400                         if ($p === false)
401                                 continue;
402                         $key = trim(substr($s, 0, $p));
403                         $value = trim(substr($s, $p+1));
404                         if (strncmp($key,'ds[', 3) == 0) {
405                                 /* DS definition */
406                                 $p = strpos($key, ']');
407                                 $ds = substr($key, 3, $p-3);
408                                 if (!isset($info['DS']))
409                                         $info['DS'] = array();
410                                 $ds_key = substr($key, $p+2);
411
412                                 if (strpos($ds_key, '[') === false) {
413                                         if (!isset($info['DS']["$ds"]))
414                                                 $info['DS']["$ds"] = array();
415                                         $info['DS']["$ds"]["$ds_key"] = rrd_strip_quotes($value);
416                                 }
417                         } else if (strncmp($key, 'rra[', 4) == 0) {
418                                 /* RRD definition */
419                                 $p = strpos($key, ']');
420                                 $rra = substr($key, 4, $p-4);
421                                 if (!isset($info['RRA']))
422                                         $info['RRA'] = array();
423                                 $rra_key = substr($key, $p+2);
424
425                                 if (strpos($rra_key, '[') === false) {
426                                         if (!isset($info['RRA']["$rra"]))
427                                                 $info['RRA']["$rra"] = array();
428                                         $info['RRA']["$rra"]["$rra_key"] = rrd_strip_quotes($value);
429                                 }
430                         } else if (strpos($key, '[') === false) {
431                                 $info[$key] = rrd_strip_quotes($value);
432                         }
433                 }
434                 pclose($rrd);
435         }
436         return $info;
437 }
438
439 function rrd_get_color($code, $line = true) {
440         global $config;
441         $name = ($line ? 'f_' : 'h_').$code;
442         if (!isset($config['rrd_colors'][$name])) {
443                 $c_f = new CollectdColor('random');
444                 $c_h = new CollectdColor($c_f);
445                 $c_h->fade();
446                 $config['rrd_colors']['f_'.$code] = $c_f->as_string();
447                 $config['rrd_colors']['h_'.$code] = $c_h->as_string();
448         }
449         return $config['rrd_colors'][$name];
450 }
451
452 /**
453  * Draw RRD file based on it's structure
454  * @host
455  * @plugin
456  * @pinst
457  * @type
458  * @tinst
459  * @opts
460  * @return Commandline to call RRDGraph in order to generate the final graph
461  */
462 function collectd_draw_rrd($host, $plugin, $pinst = null, $type, $tinst = null, $opts = array()) {
463         global $config;
464         $timespan_def = null;
465         if (!isset($opts['timespan']))
466                 $timespan_def = reset($config['timespan']);
467         else foreach ($config['timespan'] as &$ts)
468                 if ($ts['name'] == $opts['timespan'])
469                         $timespan_def = $ts;
470
471         if (!isset($opts['rrd_opts']))
472                 $opts['rrd_opts'] = array();
473         if (isset($opts['logarithmic']) && $opts['logarithmic'])
474                 array_unshift($opts['rrd_opts'], '-o');
475
476         $rrdinfo = null;
477         $rrdfile = sprintf('%s/%s%s%s/%s%s%s', $host, $plugin, is_null($pinst) ? '' : '-', $pinst, $type, is_null($tinst) ? '' : '-', $tinst);
478         foreach ($config['datadirs'] as $datadir)
479                 if (is_file($datadir.'/'.$rrdfile.'.rrd')) {
480                         $rrdinfo = rrd_info($datadir.'/'.$rrdfile.'.rrd');
481                         if (isset($rrdinfo['RRA']) && is_array($rrdinfo['RRA']))
482                                 break;
483                         else
484                                 $rrdinfo = null;
485                 }
486
487         if (is_null($rrdinfo))
488                 return false;
489
490         $graph = array();
491         $has_avg = false;
492         $has_max = false;
493         $has_min = false;
494         reset($rrdinfo['RRA']);
495         $l_max = 0;
496         while (list($k, $v) = each($rrdinfo['RRA'])) {
497                 if ($v['cf'] == 'MAX')
498                         $has_max = true;
499                 else if ($v['cf'] == 'AVERAGE')
500                         $has_avg = true;
501                 else if ($v['cf'] == 'MIN')
502                         $has_min = true;
503         }
504         reset($rrdinfo['DS']);
505         while (list($k, $v) = each($rrdinfo['DS'])) {
506                 if (strlen($k) > $l_max)
507                         $l_max = strlen($k);
508                 if ($has_min)
509                         $graph[] = sprintf('DEF:%s_min=%s:%s:MIN', $k, $rrdinfo['filename'], $k);
510                 if ($has_avg)
511                         $graph[] = sprintf('DEF:%s_avg=%s:%s:AVERAGE', $k, $rrdinfo['filename'], $k);
512                 if ($has_max)
513                         $graph[] = sprintf('DEF:%s_max=%s:%s:MAX', $k, $rrdinfo['filename'], $k);
514         }
515         if ($has_min && $has_max || $has_min && $has_avg || $has_avg && $has_max) {
516                 $n = 1;
517                 reset($rrdinfo['DS']);
518                 while (list($k, $v) = each($rrdinfo['DS'])) {
519                         $graph[] = sprintf('LINE:%s_%s', $k, $has_min ? 'min' : 'avg');
520                         $graph[] = sprintf('CDEF:%s_var=%s_%s,%s_%s,-', $k, $k, $has_max ? 'max' : 'avg', $k, $has_min ? 'min' : 'avg');
521                         $graph[] = sprintf('AREA:%s_var#%s::STACK', $k, rrd_get_color($n++, false));
522                 }
523         }
524
525         reset($rrdinfo['DS']);
526         $n = 1;
527         while (list($k, $v) = each($rrdinfo['DS'])) {
528                 $graph[] = sprintf('LINE1:%s_avg#%s:%s ', $k, rrd_get_color($n++, true), $k.substr('                  ', 0, $l_max-strlen($k)));
529                 if (isset($opts['tinylegend']) && $opts['tinylegend'])
530                         continue;
531                 if ($has_avg)
532                         $graph[] = sprintf('GPRINT:%s_avg:AVERAGE:%%5.1lf%%s Avg%s', $k, $has_max || $has_min || $has_avg ? ',' : "\\l");
533                 if ($has_min)
534                         $graph[] = sprintf('GPRINT:%s_min:MIN:%%5.1lf%%s Max%s', $k, $has_max || $has_avg ? ',' : "\\l");
535                 if ($has_max)
536                         $graph[] = sprintf('GPRINT:%s_max:MAX:%%5.1lf%%s Max%s', $k, $has_avg ? ',' : "\\l");
537                 if ($has_avg)
538                         $graph[] = sprintf('GPRINT:%s_avg:LAST:%%5.1lf%%s Last\\l', $k);
539         }
540
541         $rrd_cmd = array(RRDTOOL, 'graph', '-', '-a', 'PNG', '-w', $config['rrd_width'], '-h', $config['rrd_height'], '-s', -1*$timespan_def['seconds'], '-t', $rrdfile);
542         $rrd_cmd = array_merge($rrd_cmd, $config['rrd_opts'], $opts['rrd_opts'], $graph);
543
544         $cmd = RRDTOOL;
545         for ($i = 1; $i < count($rrd_cmd); $i++)
546                 $cmd .= ' '.escapeshellarg($rrd_cmd[$i]);
547
548         return $cmd;
549 }
550
551 /**
552  * Draw RRD file based on it's structure
553  * @timespan
554  * @host
555  * @plugin
556  * @pinst
557  * @type
558  * @tinst
559  * @opts
560  * @return Commandline to call RRDGraph in order to generate the final graph
561  */
562 function collectd_draw_generic($timespan, $host, $plugin, $pinst = null, $type, $tinst = null) {
563         global $config, $GraphDefs;
564         $timespan_def = null;
565         foreach ($config['timespan'] as &$ts)
566                 if ($ts['name'] == $timespan)
567                         $timespan_def = $ts;
568         if (is_null($timespan_def))
569                 $timespan_def = reset($config['timespan']);
570
571         if (!isset($GraphDefs[$type]))
572                 return false;
573
574         $rrd_file = sprintf('%s/%s%s%s/%s%s%s', $host, $plugin, is_null($pinst) ? '' : '-', $pinst, $type, is_null($tinst) ? '' : '-', $tinst);
575         $rrd_cmd  = array(RRDTOOL, 'graph', '-', '-a', 'PNG', '-w', $config['rrd_width'], '-h', $config['rrd_height'], '-s', -1*$timespan_def['seconds'], '-t', $rrd_file);
576         $rrd_cmd  = array_merge($rrd_cmd, $config['rrd_opts']);
577         $rrd_args = $GraphDefs[$type];
578
579         foreach ($config['datadirs'] as $datadir) {
580                 $file = $datadir.'/'.$rrd_file.'.rrd';
581                 if (!is_file($file))
582                         continue;
583
584                 $file = str_replace(":", "\\:", $file);
585                 $rrd_args = str_replace('{file}', $file, $rrd_args);
586
587                 $rrdgraph = array_merge($rrd_cmd, $rrd_args);
588                 $cmd = RRDTOOL;
589                 for ($i = 1; $i < count($rrdgraph); $i++)
590                         $cmd .= ' '.escapeshellarg($rrdgraph[$i]);
591
592                 return $cmd;
593         }
594         return false;
595 }
596
597 /**
598  * Draw stack-graph for set of RRD files
599  * @opts Graph options like colors
600  * @sources List of array(name, file, ds)
601  * @return Commandline to call RRDGraph in order to generate the final graph
602  */
603 function collectd_draw_meta_stack(&$opts, &$sources) {
604         global $config;
605         $timespan_def = null;
606         if (!isset($opts['timespan']))
607                 $timespan_def = reset($config['timespan']);
608         else foreach ($config['timespan'] as &$ts)
609                 if ($ts['name'] == $opts['timespan'])
610                         $timespan_def = $ts;
611
612         if (!isset($opts['title']))
613                 $opts['title'] = 'Unknown title';
614         if (!isset($opts['rrd_opts']))
615                 $opts['rrd_opts'] = array();
616         if (!isset($opts['colors']))
617                 $opts['colors'] = array();
618         if (isset($opts['logarithmic']) && $opts['logarithmic'])
619                 array_unshift($opts['rrd_opts'], '-o');
620
621         $cmd = array(RRDTOOL, 'graph', '-', '-a', 'PNG', '-w', $config['rrd_width'], '-h', $config['rrd_height'], '-s', -1*$timespan_def['seconds'], '-t', $opts['title']);
622         $cmd = array_merge($cmd, $config['rrd_opts'], $opts['rrd_opts']);
623         $max_inst_name = 0;
624
625         foreach($sources as &$inst_data) {
626                 $inst_name = $inst_data['name'];
627                 $file      = $inst_data['file'];
628                 $ds        = isset($inst_data['ds']) ? $inst_data['ds'] : 'value';
629
630                 if (strlen($inst_name) > $max_inst_name)
631                         $max_inst_name = strlen($inst_name);
632
633                 if (!is_file($file))
634                         continue;
635
636                 $cmd[] = 'DEF:'.$inst_name.'_min='.$file.':'.$ds.':MIN';
637                 $cmd[] = 'DEF:'.$inst_name.'_avg='.$file.':'.$ds.':AVERAGE';
638                 $cmd[] = 'DEF:'.$inst_name.'_max='.$file.':'.$ds.':MAX';
639                 $cmd[] = 'CDEF:'.$inst_name.'_nnl='.$inst_name.'_avg,UN,0,'.$inst_name.'_avg,IF';
640         }
641         $inst_data = end($sources);
642         $inst_name = $inst_data['name'];
643         $cmd[] = 'CDEF:'.$inst_name.'_stk='.$inst_name.'_nnl';
644
645         $inst_data1 = end($sources);
646         while (($inst_data0 = prev($sources)) !== false) {
647                 $inst_name0 = $inst_data0['name'];
648                 $inst_name1 = $inst_data1['name'];
649
650                 $cmd[] = 'CDEF:'.$inst_name0.'_stk='.$inst_name0.'_nnl,'.$inst_name1.'_stk,+';
651                 $inst_data1 = $inst_data0;
652         }
653
654         foreach($sources as &$inst_data) {
655                 $inst_name = $inst_data['name'];
656                 $legend = sprintf('%s', $inst_name);
657                 while (strlen($legend) < $max_inst_name)
658                         $legend .= ' ';
659                 $number_format = isset($opts['number_format']) ? $opts['number_format'] : '%6.1lf';
660
661                 if (isset($opts['colors'][$inst_name]))
662                         $line_color = new CollectdColor($opts['colors'][$inst_name]);
663                 else
664                         $line_color = new CollectdColor('random');
665                 $area_color = new CollectdColor($line_color);
666                 $area_color->fade();
667
668                 $cmd[] = 'AREA:'.$inst_name.'_stk#'.$area_color->as_string();
669                 $cmd[] = 'LINE1:'.$inst_name.'_stk#'.$line_color->as_string().':'.$legend;
670                 if (!(isset($opts['tinylegend']) && $opts['tinylegend'])) {
671                         $cmd[] = 'GPRINT:'.$inst_name.'_min:MIN:'.$number_format.' Min,';
672                         $cmd[] = 'GPRINT:'.$inst_name.'_avg:AVERAGE:'.$number_format.' Avg,';
673                         $cmd[] = 'GPRINT:'.$inst_name.'_max:MAX:'.$number_format.' Max,';
674                         $cmd[] = 'GPRINT:'.$inst_name.'_avg:LAST:'.$number_format.' Last\\l';
675                 }
676         }
677
678         $rrdcmd = RRDTOOL;
679         for ($i = 1; $i < count($cmd); $i++)
680                 $rrdcmd .= ' '.escapeshellarg($cmd[$i]);
681         return $rrdcmd;
682 }
683
684 /**
685  * Draw stack-graph for set of RRD files
686  * @opts Graph options like colors
687  * @sources List of array(name, file, ds)
688  * @return Commandline to call RRDGraph in order to generate the final graph
689  */
690 function collectd_draw_meta_line(&$opts, &$sources) {
691         global $config;
692         $timespan_def = null;
693         if (!isset($opts['timespan']))
694                 $timespan_def = reset($config['timespan']);
695         else foreach ($config['timespan'] as &$ts)
696                 if ($ts['name'] == $opts['timespan'])
697                         $timespan_def = $ts;
698
699         if (!isset($opts['title']))
700                 $opts['title'] = 'Unknown title';
701         if (!isset($opts['rrd_opts']))
702                 $opts['rrd_opts'] = array();
703         if (!isset($opts['colors']))
704                 $opts['colors'] = array();
705         if (isset($opts['logarithmic']) && $opts['logarithmic'])
706                 array_unshift($opts['rrd_opts'], '-o');
707
708         $cmd = array(RRDTOOL, 'graph', '-', '-a', 'PNG', '-w', $config['rrd_width'], '-h', $config['rrd_height'], '-s', -1*$timespan_def['seconds'], '-t', $opts['title']);
709         $cmd = array_merge($cmd, $config['rrd_opts'], $opts['rrd_opts']);
710         $max_inst_name = 0;
711
712         foreach ($sources as &$inst_data) {
713                 $inst_name = $inst_data['name'];
714                 $file      = $inst_data['file'];
715                 $ds        = isset($inst_data['ds']) ? $inst_data['ds'] : 'value';
716
717                 if (strlen($inst_name) > $max_inst_name)
718                         $max_inst_name = strlen($inst_name);
719
720                 if (!is_file($file))
721                         continue;
722
723                 $cmd[] = 'DEF:'.$inst_name.'_min='.$file.':'.$ds.':MIN';
724                 $cmd[] = 'DEF:'.$inst_name.'_avg='.$file.':'.$ds.':AVERAGE';
725                 $cmd[] = 'DEF:'.$inst_name.'_max='.$file.':'.$ds.':MAX';
726         }
727
728         foreach ($sources as &$inst_data) {
729                 $inst_name = $inst_data['name'];
730                 $legend = sprintf('%s', $inst_name);
731                 while (strlen($legend) < $max_inst_name)
732                         $legend .= ' ';
733                 $number_format = isset($opts['number_format']) ? $opts['number_format'] : '%6.1lf';
734
735                 if (isset($opts['colors'][$inst_name]))
736                         $line_color = new CollectdColor($opts['colors'][$inst_name]);
737                 else
738                         $line_color = new CollectdColor('random');
739
740                 $cmd[] = 'LINE1:'.$inst_name.'_avg#'.$line_color->as_string().':'.$legend;
741                 if (!(isset($opts['tinylegend']) && $opts['tinylegend'])) {
742                         $cmd[] = 'GPRINT:'.$inst_name.'_min:MIN:'.$number_format.' Min,';
743                         $cmd[] = 'GPRINT:'.$inst_name.'_avg:AVERAGE:'.$number_format.' Avg,';
744                         $cmd[] = 'GPRINT:'.$inst_name.'_max:MAX:'.$number_format.' Max,';
745                         $cmd[] = 'GPRINT:'.$inst_name.'_avg:LAST:'.$number_format.' Last\\l';
746                 }
747         }
748
749         $rrdcmd = RRDTOOL;
750         for ($i = 1; $i < count($cmd); $i++)
751                 $rrdcmd .= ' '.escapeshellarg($cmd[$i]);
752         return $rrdcmd;
753 }
754
755 ?>