1<?php 2/* 3 +-------------------------------------------------------------------------+ 4 | Copyright (C) 2004-2021 The Cacti Group | 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; either version 2 | 9 | of the License, or (at your option) any later version. | 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 | Cacti: The Complete RRDtool-based Graphing Solution | 17 +-------------------------------------------------------------------------+ 18 | This code is designed, written, and maintained by the Cacti Group. See | 19 | about.php and/or the AUTHORS file for specific developer information. | 20 +-------------------------------------------------------------------------+ 21 | http://www.cacti.net/ | 22 +-------------------------------------------------------------------------+ 23*/ 24 25define('RRD_NL', " \\\n"); 26define('MAX_FETCH_CACHE_SIZE', 5); 27 28if (read_config_option('storage_location')) { 29 /* load crypt libraries only if the Cacti RRDtool Proxy Server is in use */ 30 set_include_path($config['include_path'] . '/vendor/phpseclib/'); 31 include_once('Math/BigInteger.php'); 32 include_once('Crypt/Base.php'); 33 include_once('Crypt/Hash.php'); 34 include_once('Crypt/Random.php'); 35 include_once('Crypt/RSA.php'); 36 include_once('Crypt/Rijndael.php'); 37 38 global $encryption; 39 $encryption = true; 40} 41 42function escape_command($command) { 43 return $command; # we escape every single argument now, no need for 'special' escaping 44 #return preg_replace("/(\\\$|`)/", "", $command); # current cacti code 45 #TODO return preg_replace((\\\$(?=\w+|\*|\@|\#|\?|\-|\\\$|\!|\_|[0-9]|\(.*\))|`(?=.*(?=`)))","$2", $command); #suggested by ldevantier to allow for a single $ 46} 47 48/** set the language environment variable for rrdtool functions 49 * @param string $lang - the desired language to set 50 * @return null 51 */ 52function rrdtool_set_language($lang = -1) { 53 global $prev_lang; 54 55 $prev_lang = getenv('LANG'); 56 57 if ($lang == -1) { 58 putenv('LANG=' . str_replace('-', '_', CACTI_LOCALE) . '.UTF-8'); 59 } else { 60 putenv('LANG=en_EN.UTF-8'); 61 } 62} 63 64/** restore the default language environment variable after rrdtool functions 65 * @return null 66 */ 67function rrdtool_reset_language() { 68 global $prev_lang; 69 70 putenv('LANG=' . $prev_lang); 71} 72 73function rrd_init($output_to_term = true) { 74 global $config; 75 76 $args = func_get_args(); 77 $force_storage_location_local = (isset($config['force_storage_location_local']) && $config['force_storage_location_local'] === true ) ? true : false; 78 $function = ($force_storage_location_local === false && read_config_option('storage_location')) ? '__rrd_proxy_init' : '__rrd_init'; 79 return call_user_func_array($function, $args); 80} 81 82function __rrd_init($output_to_term = true) { 83 global $config; 84 85 /* set the rrdtool default font */ 86 if (read_config_option('path_rrdtool_default_font')) { 87 putenv('RRD_DEFAULT_FONT=' . read_config_option('path_rrdtool_default_font')); 88 } 89 90 rrdtool_set_language(); 91 92 if ($output_to_term) { 93 $command = read_config_option('path_rrdtool') . ' - '; 94 } elseif ($config['cacti_server_os'] == 'win32') { 95 $command = read_config_option('path_rrdtool') . ' - > nul'; 96 } else { 97 $command = read_config_option('path_rrdtool') . ' - > /dev/null 2>&1'; 98 } 99 100 return popen($command, 'w'); 101} 102 103function __rrd_proxy_init($logopt = 'WEBLOG') { 104 global $encryption; 105 $terminator = "_EOT_\r\n"; 106 $encryption = true; 107 $rsa = new \phpseclib\Crypt\RSA(); 108 109 $rrdp_socket = @socket_create(AF_INET, SOCK_STREAM, SOL_TCP); 110 if ($rrdp_socket === false) { 111 cacti_log('CACTI2RRDP ERROR: Unable to create socket to connect to RRDtool Proxy Server', false, $logopt, POLLER_VERBOSITY_LOW); 112 return false; 113 } 114 115 if ( read_config_option('rrdp_load_balancing') == 'on' ) { 116 $rrdp_id = rand(1,2); 117 $rrdp = @socket_connect($rrdp_socket, (($rrdp_id == 1 ) ? read_config_option('rrdp_server') : read_config_option('rrdp_server_backup')), (($rrdp_id == 1 ) ? read_config_option('rrdp_port') : read_config_option('rrdp_port_backup')) ); 118 } else { 119 $rrdp_id = 1; 120 $rrdp = @socket_connect($rrdp_socket, read_config_option('rrdp_server'), read_config_option('rrdp_port')); 121 } 122 123 if ($rrdp === false) { 124 /* log entry ... */ 125 cacti_log('CACTI2RRDP ERROR: Unable to connect to RRDtool Proxy Server #' . $rrdp_id, false, $logopt, POLLER_VERBOSITY_LOW); 126 127 /* ... and try to use backup path */ 128 $rrdp_id = ($rrdp_id + 1) % 2; 129 $rrdp = @socket_connect($rrdp_socket, (($rrdp_id == 1 ) ? read_config_option('rrdp_server') : read_config_option('rrdp_server_backup')), (($rrdp_id == 1 ) ? read_config_option('rrdp_port') : read_config_option('rrdp_port_backup')) ); 130 131 if ($rrdp === false) { 132 cacti_log('CACTI2RRDP ERROR: Unable to connect to RRDtool Proxy Server #' . $rrdp_id, false, $logopt, POLLER_VERBOSITY_LOW); 133 return false; 134 } 135 } 136 137 $rrdp_fingerprint = ($rrdp_id == 1 ) ? read_config_option('rrdp_fingerprint') : read_config_option('rrdp_fingerprint_backup'); 138 139 socket_write($rrdp_socket, read_config_option('rsa_public_key') . $terminator); 140 141 /* read public key being returned by the proxy server */ 142 $rrdp_public_key = ''; 143 while(1) { 144 $recv = socket_read($rrdp_socket, 1000, PHP_BINARY_READ ); 145 if ($recv === false) { 146 /* timeout */ 147 cacti_log('CACTI2RRDP ERROR: Public RSA Key Exchange - Time-out while reading', false, $logopt, POLLER_VERBOSITY_LOW); 148 $rrdp_public_key = false; 149 break; 150 } elseif ($recv == '') { 151 cacti_log('CACTI2RRDP ERROR: Session closed by Proxy.', false, $logopt, POLLER_VERBOSITY_LOW); 152 /* session closed by Proxy */ 153 break; 154 } else { 155 $rrdp_public_key .= $recv; 156 if (strpos($rrdp_public_key, $terminator) !== false) { 157 $rrdp_public_key = trim(trim($rrdp_public_key, $terminator)); 158 break; 159 } 160 } 161 } 162 163 $rsa->loadKey($rrdp_public_key); 164 $fingerprint = $rsa->getPublicKeyFingerprint(); 165 166 if ($rrdp_fingerprint != $fingerprint) { 167 cacti_log('CACTI2RRDP ERROR: Mismatch RSA Fingerprint.', false, $logopt, POLLER_VERBOSITY_LOW); 168 return false; 169 } else { 170 $rrdproxy = array($rrdp_socket, $rrdp_public_key); 171 /* set the rrdtool default font */ 172 if (read_config_option('path_rrdtool_default_font')) { 173 rrdtool_execute("setenv RRD_DEFAULT_FONT '" . read_config_option('path_rrdtool_default_font') . "'", false, RRDTOOL_OUTPUT_NULL, $rrdproxy, $logopt = 'WEBLOG'); 174 } 175 176 /* disable encryption */ 177 $encryption = rrdtool_execute('setcnn encryption off', false, RRDTOOL_OUTPUT_BOOLEAN, $rrdproxy, $logopt = 'WEBLOG') ? false : true; 178 return $rrdproxy; 179 } 180} 181 182function rrd_close() { 183 global $config; 184 $args = func_get_args(); 185 $force_storage_location_local = (isset($config['force_storage_location_local']) && $config['force_storage_location_local'] === true) ? true : false; 186 $function = ($force_storage_location_local === false && read_config_option('storage_location')) ? '__rrd_proxy_close' : '__rrd_close'; 187 return call_user_func_array($function, $args); 188} 189 190function __rrd_close($rrdtool_pipe) { 191 /* close the rrdtool file descriptor */ 192 if (is_resource($rrdtool_pipe)) { 193 pclose($rrdtool_pipe); 194 } 195 196 rrdtool_reset_language(); 197} 198 199function __rrd_proxy_close($rrdp) { 200 /* close the rrdtool proxy server connection */ 201 $terminator = "_EOT_\r\n"; 202 if ($rrdp) { 203 socket_write($rrdp[0], encrypt('quit', $rrdp[1]) . $terminator); 204 @socket_shutdown($rrdp[0], 2); 205 @socket_close($rrdp[0]); 206 return; 207 } 208} 209 210function encrypt($output, $rsa_key) { 211 global $encryption; 212 213 if($encryption) { 214 $rsa = new \phpseclib\Crypt\RSA(); 215 $aes = new \phpseclib\Crypt\Rijndael(); 216 $aes_key = \phpseclib\Crypt\Random::string(192); 217 218 $aes->setKey($aes_key); 219 $ciphertext = base64_encode($aes->encrypt($output)); 220 $rsa->loadKey($rsa_key); 221 $aes_key = base64_encode($rsa->encrypt($aes_key)); 222 $aes_key_length = str_pad(dechex(strlen($aes_key)),3,'0',STR_PAD_LEFT); 223 224 return $aes_key_length . $aes_key . $ciphertext; 225 }else { 226 return $output; 227 } 228} 229 230function decrypt($input){ 231 global $encryption; 232 233 if($encryption) { 234 $rsa = new \phpseclib\Crypt\RSA(); 235 $aes = new \phpseclib\Crypt\Rijndael(); 236 237 $rsa_private_key = read_config_option('rsa_private_key'); 238 239 $aes_key_length = hexdec(substr($input,0,3)); 240 $aes_key = base64_decode(substr($input,3,$aes_key_length)); 241 $ciphertext = base64_decode(substr($input,3+$aes_key_length)); 242 243 $rsa->loadKey( $rsa_private_key ); 244 $aes_key = $rsa->decrypt($aes_key); 245 $aes->setKey($aes_key); 246 $plaintext = $aes->decrypt($ciphertext); 247 248 return $plaintext; 249 }else { 250 return $input; 251 } 252} 253 254function rrdtool_execute() { 255 global $config; 256 257 $args = func_get_args(); 258 $force_storage_location_local = (isset($config['force_storage_location_local']) && $config['force_storage_location_local'] === true) ? true : false; 259 $function = ($force_storage_location_local === false && read_config_option('storage_location')) ? '__rrd_proxy_execute' : '__rrd_execute'; 260 261 return call_user_func_array($function, $args); 262} 263 264function __rrd_execute($command_line, $log_to_stdout, $output_flag, $rrdtool_pipe = false, $logopt = 'WEBLOG') { 265 global $config; 266 267 static $last_command; 268 269 if (!is_numeric($output_flag)) { 270 $output_flag = RRDTOOL_OUTPUT_STDOUT; 271 } 272 273 /* WIN32: before sending this command off to rrdtool, get rid 274 of all of the backslash (\) characters. Unix does not care; win32 does. 275 Also make sure to replace all of the backslashes at the end of the line, 276 but make sure not to get rid of newlines (\n) that are supposed to be 277 in there (text format) */ 278 $command_line = str_replace("\\\n", ' ', $command_line); 279 280 /* output information to the log file if appropriate */ 281 cacti_log('CACTI2RRD: ' . read_config_option('path_rrdtool') . " $command_line", $log_to_stdout, $logopt, POLLER_VERBOSITY_DEBUG); 282 283 $debug = ''; 284 /* if we want to see the error output from rrdtool; make sure to specify this */ 285 if ($config['cacti_server_os'] != 'win32') { 286 if (($output_flag == RRDTOOL_OUTPUT_STDERR || $output_flag == RRDTOOL_OUTPUT_RETURN_STDERR) && !is_resource($rrdtool_pipe)) { 287 $debug .= ' 2>&1'; 288 } 289 } 290 291 /* use popen to eliminate the zombie issue */ 292 if ($config['cacti_server_os'] == 'unix') { 293 $pipe_mode = 'r'; 294 } else { 295 $pipe_mode = 'rb'; 296 } 297 298 /* an empty $rrdtool_pipe array means no fp is available */ 299 if (!is_resource($rrdtool_pipe)) { 300 if (substr($command_line, 0, 5) == 'fetch' || substr($command_line, 0, 4) == 'info') { 301 rrdtool_set_language('en'); 302 } else { 303 rrdtool_set_language(); 304 } 305 306 cacti_session_close(); 307 if (is_file(read_config_option('path_rrdtool')) && is_executable(read_config_option('path_rrdtool'))) { 308 $descriptorspec = array( 309 0 => array('pipe', 'r'), 310 1 => array('pipe', 'w') 311 ); 312 313 $process = proc_open(read_config_option('path_rrdtool') . ' - ' . $debug, $descriptorspec, $pipes); 314 315 if (!is_resource($process)) { 316 unset($process); 317 } else { 318 fwrite($pipes[0], escape_command($command_line) . "\r\nquit\r\n"); 319 fclose($pipes[0]); 320 $fp = $pipes[1]; 321 } 322 } else { 323 cacti_log("ERROR: RRDtool executable not found, not executable or error in path '" . read_config_option('path_rrdtool') . "'. No output written to RRDfile."); 324 } 325 326 rrdtool_reset_language(); 327 } else { 328 $i = 0; 329 while (1) { 330 if (fwrite($rrdtool_pipe, escape_command(" $command_line") . "\r\n") === false) { 331 cacti_log("ERROR: Detected RRDtool Crash on '$command_line'. Last command was '$last_command'"); 332 333 /* close the invalid pipe */ 334 rrd_close($rrdtool_pipe); 335 336 /* open a new rrdtool process */ 337 $rrdtool_pipe = rrd_init(); 338 339 if ($i > 4) { 340 cacti_log("FATAL: RRDtool Restart Attempts Exceeded. Giving up on '$command_line'."); 341 342 break; 343 } else { 344 $i++; 345 } 346 347 continue; 348 } else { 349 fflush($rrdtool_pipe); 350 351 break; 352 } 353 } 354 } 355 356 /* store the last command to provide rrdtool segfault diagnostics */ 357 $last_command = $command_line; 358 359 if (!isset($fp)) { 360 return; 361 } 362 363 switch ($output_flag) { 364 case RRDTOOL_OUTPUT_STDOUT: 365 case RRDTOOL_OUTPUT_GRAPH_DATA: 366 $output = ''; 367 while (!feof($fp)) { 368 $output .= fgets($fp, 4096); 369 } 370 371 if (isset($process)) { 372 fclose($fp); 373 proc_close($process); 374 } 375 376 rrdtool_trim_output($output); 377 378 return $output; 379 break; 380 case RRDTOOL_OUTPUT_STDERR: 381 case RRDTOOL_OUTPUT_RETURN_STDERR: 382 $output = fgets($fp, 1000000); 383 384 if (isset($process)) { 385 fclose($fp); 386 proc_close($process); 387 } 388 389 rrdtool_trim_output($output); 390 391 if (substr($output, 1, 3) == 'PNG') { 392 return 'OK'; 393 } 394 395 if (substr($output, 0, 5) == '<?xml') { 396 return 'SVG/XML Output OK'; 397 } 398 399 if ($output_flag == RRDTOOL_OUTPUT_RETURN_STDERR) { 400 return $output; 401 } else { 402 print $output; 403 } 404 405 break; 406 case RRDTOOL_OUTPUT_NULL: 407 default: 408 return; 409 break; 410 } 411} 412 413function rrdtool_trim_output(&$output) { 414 /* When using RRDtool with proc_open for long strings 415 * and using the '-' to handle standard in from inside 416 * the process, RRDtool automatically appends stderr 417 * to stdout for batch programs to parse the output 418 * string. So, therefore, we have to prune that 419 * output. 420 */ 421 $okpos = strrpos($output, 'OK u:'); 422 if ($okpos !== false) { 423 $output = substr($output, 0, $okpos); 424 } 425} 426 427function __rrd_proxy_execute($command_line, $log_to_stdout, $output_flag, $rrdp='', $logopt = 'WEBLOG') { 428 global $config, $encryption; 429 430 static $last_command; 431 $end_of_packet = "_EOP_\r\n"; 432 $end_of_sequence = "_EOT_\r\n"; 433 434 if (!is_numeric($output_flag)) { 435 $output_flag = RRDTOOL_OUTPUT_STDOUT; 436 } 437 438 /* WIN32: before sending this command off to rrdtool, get rid 439 of all of the '\' characters. Unix does not care; win32 does. 440 Also make sure to replace all of the fancy "\"s at the end of the line, 441 but make sure not to get rid of the "\n"s that are supposed to be 442 in there (text format) */ 443 $command_line = str_replace(array($config['rra_path'], "\\\n"), array('.', ' '), $command_line); 444 445 /* output information to the log file if appropriate */ 446 cacti_log('CACTI2RRDP: ' . read_config_option('path_rrdtool') . " $command_line", $log_to_stdout, $logopt, POLLER_VERBOSITY_DEBUG); 447 448 /* store the last command to provide rrdtool segfault diagnostics */ 449 $last_command = $command_line; 450 $rrdp_auto_close = false; 451 452 if (!$rrdp) { 453 $rrdp = __rrd_proxy_init($logopt); 454 $rrdp_auto_close = true; 455 } 456 457 if (!$rrdp) { 458 cacti_log('CACTI2RRDP ERROR: Unable to connect to RRDtool proxy.', $log_to_stdout, $logopt, POLLER_VERBOSITY_LOW); 459 return null; 460 } else { 461 cacti_log('CACTI2RRDP NOTE: Connection to RRDtool proxy has already been established.', $log_to_stdout, $logopt, POLLER_VERBOSITY_DEBUG); 462 } 463 464 $rrdp_socket = $rrdp[0]; 465 $rrdp_public_key = $rrdp[1]; 466 467 if (strlen($command_line) >= 8192) { 468 $command_line = gzencode($command_line, 1); 469 } 470 socket_write($rrdp_socket, encrypt($command_line, $rrdp_public_key) . $end_of_sequence); 471 472 $input = ''; 473 $output = ''; 474 475 while(1) { 476 $recv = socket_read($rrdp_socket, 100000, PHP_BINARY_READ ); 477 if ($recv === false) { 478 cacti_log('CACTI2RRDP ERROR: Data Transfer - Time-out while reading.', $log_to_stdout, $logopt, POLLER_VERBOSITY_LOW); 479 break; 480 } elseif ($recv == '') { 481 /* session closed by Proxy */ 482 if ($output) { 483 cacti_log('CACTI2RRDP ERROR: Session closed by Proxy.', $log_to_stdout, $logopt, POLLER_VERBOSITY_LOW); 484 } 485 break; 486 } else { 487 $input .= $recv; 488 if (strpos($input, $end_of_sequence) !== false) { 489 $input = str_replace($end_of_sequence, '', $input); 490 $transactions = explode($end_of_packet, $input); 491 foreach ($transactions as $transaction) { 492 $packet = $transaction; 493 $transaction = decrypt($transaction); 494 if($transaction === false){ 495 cacti_log("CACTI2RRDP ERROR: Proxy message decryption failed: ###". $packet . '###', $log_to_stdout, $logopt, POLLER_VERBOSITY_LOW); 496 break 2; 497 } 498 if(strpos($transaction, "\x1f\x8b") === 0) { 499 $transaction = gzdecode($transaction); 500 } 501 $output .= $transaction; 502 if (substr_count($output, 'OK u') || substr_count($output, 'ERROR:')) { 503 cacti_log('RRDP: ' . $output, $log_to_stdout, $logopt, POLLER_VERBOSITY_DEBUG); 504 break 2; 505 } 506 } 507 } 508 } 509 } 510 511 if ($rrdp_auto_close) { 512 __rrd_proxy_close($rrdp); 513 } 514 515 switch ($output_flag) { 516 case RRDTOOL_OUTPUT_NULL: 517 return; 518 case RRDTOOL_OUTPUT_STDOUT: 519 case RRDTOOL_OUTPUT_GRAPH_DATA: 520 return rtrim(substr($output, 0, strpos($output, 'OK u'))); 521 break; 522 case RRDTOOL_OUTPUT_STDERR: 523 if (substr($output, 1, 3) == 'PNG') { 524 return 'OK'; 525 } 526 if (substr($output, 0, 5) == 'GIF87') { 527 return 'OK'; 528 } 529 if (substr($output, 0, 5) == '<?xml') { 530 return 'SVG/XML Output OK'; 531 } 532 print $output; 533 break; 534 case RRDTOOL_OUTPUT_BOOLEAN : 535 return (substr_count($output, 'OK u')) ? true : false; 536 break; 537 } 538} 539 540function rrdtool_function_interface_speed($data_local) { 541 $ifHighSpeed = db_fetch_cell_prepared('SELECT field_value 542 FROM host_snmp_cache 543 WHERE host_id = ? 544 AND snmp_query_id = ? 545 AND snmp_index = ? 546 AND field_name="ifHighSpeed"', 547 array($data_local['host_id'], $data_local['snmp_query_id'], $data_local['snmp_index']) 548 ); 549 550 $ifSpeed = db_fetch_cell_prepared('SELECT field_value 551 FROM host_snmp_cache 552 WHERE host_id = ? 553 AND snmp_query_id = ? 554 AND snmp_index = ? 555 AND field_name="ifSpeed"', 556 array($data_local['host_id'], $data_local['snmp_query_id'], $data_local['snmp_index']) 557 ); 558 559 if (!empty($ifHighSpeed)) { 560 $speed = $ifHighSpeed * 1000000; 561 } elseif (!empty($ifSpeed)) { 562 $speed = $ifSpeed; 563 } else { 564 $speed = read_config_option('default_interface_speed'); 565 566 if (empty($speed)) { 567 $speed = '10000000000000'; 568 } else { 569 $speed = $speed * 1000000; 570 } 571 } 572 573 return $speed; 574} 575 576function rrdtool_function_create($local_data_id, $show_source, $rrdtool_pipe = false) { 577 global $config, $data_source_types, $consolidation_functions, $encryption; 578 579 include ($config['include_path'] . '/global_arrays.php'); 580 581 $data_source_path = get_data_source_path($local_data_id, true); 582 583 /* ok, if that passes lets check to make sure an rra does not already 584 exist, the last thing we want to do is overright data! */ 585 if ($show_source != true) { 586 if (read_config_option('storage_location')) { 587 if (rrdtool_execute("file_exists $data_source_path", true, RRDTOOL_OUTPUT_BOOLEAN, $rrdtool_pipe, 'POLLER')) { 588 return -1; 589 } 590 } elseif (file_exists($data_source_path)) { 591 return -1; 592 } 593 } 594 595 /* the first thing we must do is make sure there is at least one 596 rra associated with this data source... * 597 UPDATE: As of version 0.6.6, we are splitting this up into two 598 SQL strings because of the multiple DS per RRD support. This is 599 not a big deal however since this function gets called once per 600 data source */ 601 602 $rras = db_fetch_assoc_prepared('SELECT dtd.rrd_step, dsp.x_files_factor, 603 dspr.steps, dspr.rows, dspc.consolidation_function_id, 604 (dspr.rows*dspr.steps) AS rra_order 605 FROM data_template_data AS dtd 606 LEFT JOIN data_source_profiles AS dsp 607 ON dtd.data_source_profile_id=dsp.id 608 LEFT JOIN data_source_profiles_rra AS dspr 609 ON dsp.id=dspr.data_source_profile_id 610 LEFT JOIN data_source_profiles_cf AS dspc 611 ON dsp.id=dspc.data_source_profile_id 612 WHERE dtd.local_data_id = ? 613 AND (dspr.steps IS NOT NULL OR dspr.rows IS NOT NULL) 614 ORDER BY dspc.consolidation_function_id, rra_order', 615 array($local_data_id) 616 ); 617 618 /* if we find that this DS has no RRA associated; get out */ 619 if (cacti_sizeof($rras) <= 0) { 620 cacti_log("ERROR: There are no RRA's assigned to local_data_id: $local_data_id."); 621 return false; 622 } 623 624 /* create the "--step" line */ 625 $create_ds = RRD_NL . '--start 0 --step '. $rras[0]['rrd_step'] . ' ' . RRD_NL; 626 627 /* query the data sources to be used in this .rrd file */ 628 $data_sources = db_fetch_assoc_prepared('SELECT id, rrd_heartbeat, 629 rrd_minimum, rrd_maximum, data_source_type_id 630 FROM data_template_rrd 631 WHERE local_data_id = ? 632 ORDER BY local_data_template_rrd_id', 633 array($local_data_id) 634 ); 635 636 /* ONLY make a new DS entry if: 637 - There is multiple data sources and this item is not the main one. 638 - There is only one data source (then use it) */ 639 640 if (cacti_sizeof($data_sources)) { 641 $data_local = db_fetch_row_prepared('SELECT host_id, 642 snmp_query_id, snmp_index 643 FROM data_local 644 WHERE id = ?', 645 array($local_data_id) 646 ); 647 648 $speed = rrdtool_function_interface_speed($data_local); 649 650 foreach ($data_sources as $data_source) { 651 /* use the cacti ds name by default or the user defined one, if entered */ 652 $data_source_name = get_data_source_item_name($data_source['id']); 653 654 // Trim the data source maximum 655 $data_source['rrd_maximum'] = trim($data_source['rrd_maximum']); 656 657 if ($data_source['rrd_maximum'] == 'U') { 658 /* in case no maximum is given, use "Undef" value */ 659 $data_source['rrd_maximum'] = 'U'; 660 } elseif (strpos($data_source['rrd_maximum'], '|query_') !== false) { 661 /* in case a query variable is given, evaluate it */ 662 if ($data_source['rrd_maximum'] == '|query_ifSpeed|' || $data_source['rrd_maximum'] == '|query_ifHighSpeed|') { 663 $data_source['rrd_maximum'] = $speed; 664 } else { 665 $data_source['rrd_maximum'] = substitute_snmp_query_data($data_source['rrd_maximum'], $data_local['host_id'], $data_local['snmp_query_id'], $data_local['snmp_index']); 666 } 667 } elseif ($data_source['rrd_maximum'] != 'U' && (int)$data_source['rrd_maximum'] <= (int)$data_source['rrd_minimum']) { 668 /* max > min required, but take care of an "Undef" value */ 669 if ($data_source['data_source_type_id'] == 1 || $data_source['data_source_type_id'] == 4) { 670 $data_source['rrd_maximum'] = 'U'; 671 } else { 672 $data_source['rrd_maximum'] = (int)$data_source['rrd_minimum'] + 1; 673 } 674 } 675 676 /* min==max==0 won't work with rrdtool */ 677 if ($data_source['rrd_minimum'] == 0 && $data_source['rrd_maximum'] == 0) { 678 $data_source['rrd_maximum'] = 'U'; 679 } 680 681 $create_ds .= "DS:$data_source_name:" . $data_source_types[$data_source['data_source_type_id']] . ':' . $data_source['rrd_heartbeat'] . ':' . $data_source['rrd_minimum'] . ':' . $data_source['rrd_maximum'] . RRD_NL; 682 } 683 } 684 685 $create_rra = ''; 686 /* loop through each available RRA for this DS */ 687 foreach ($rras as $rra) { 688 $create_rra .= 'RRA:' . $consolidation_functions[$rra['consolidation_function_id']] . ':' . $rra['x_files_factor'] . ':' . $rra['steps'] . ':' . $rra['rows'] . RRD_NL; 689 } 690 691 /* check for structured path configuration, if in place verify directory 692 exists and if not create it. 693 */ 694 if (read_config_option('extended_paths') == 'on') { 695 if (read_config_option('storage_location')) { 696 if (false === rrdtool_execute('is_dir ' . dirname($data_source_path), true, RRDTOOL_OUTPUT_BOOLEAN, $rrdtool_pipe, 'POLLER') ) { 697 if (false === rrdtool_execute('mkdir ' . dirname($data_source_path), true, RRDTOOL_OUTPUT_BOOLEAN, $rrdtool_pipe, 'POLLER') ) { 698 cacti_log("ERROR: Unable to create directory '" . dirname($data_source_path) . "'", false); 699 } 700 } 701 } elseif (!is_dir(dirname($data_source_path))) { 702 if ($config['is_web'] == false) { 703 if (mkdir(dirname($data_source_path), 0775)) { 704 if ($config['cacti_server_os'] != 'win32') { 705 $owner_id = fileowner($config['rra_path']); 706 $group_id = filegroup($config['rra_path']); 707 708 if ((chown(dirname($data_source_path), $owner_id)) && 709 (chgrp(dirname($data_source_path), $group_id))) { 710 /* permissions set ok */ 711 } else { 712 cacti_log("ERROR: Unable to set directory permissions for '" . dirname($data_source_path) . "'", false); 713 } 714 } 715 } else { 716 cacti_log("ERROR: Unable to create directory '" . dirname($data_source_path) . "'", false); 717 } 718 } else { 719 cacti_log("WARNING: Poller has not created structured path '" . dirname($data_source_path) . "' yet.", false); 720 } 721 } 722 } 723 724 if ($show_source == true) { 725 return read_config_option('path_rrdtool') . ' create' . RRD_NL . "$data_source_path$create_ds$create_rra"; 726 } else { 727 rrdtool_execute("create $data_source_path $create_ds$create_rra", true, RRDTOOL_OUTPUT_STDOUT, $rrdtool_pipe, 'POLLER'); 728 } 729} 730 731function rrdtool_function_update($update_cache_array, $rrdtool_pipe = false) { 732 /* lets count the number of rrd files processed */ 733 $rrds_processed = 0; 734 735 foreach ($update_cache_array as $rrd_path => $rrd_fields) { 736 $create_rrd_file = false; 737 738 if (is_array($rrd_fields['times']) && cacti_sizeof($rrd_fields['times'])) { 739 /* create the rrd if one does not already exist */ 740 if (read_config_option('storage_location')) { 741 $file_exists = rrdtool_execute("file_exists $rrd_path" , true, RRDTOOL_OUTPUT_BOOLEAN, $rrdtool_pipe, 'POLLER'); 742 } else { 743 $file_exists = file_exists($rrd_path); 744 } 745 746 ksort($rrd_fields['times']); 747 748 if ($file_exists === false) { 749 $times = array_keys($rrd_fields['times']); 750 rrdtool_function_create($rrd_fields['local_data_id'], false, $rrdtool_pipe); 751 $create_rrd_file = true; 752 } 753 754 foreach ($rrd_fields['times'] as $update_time => $field_array) { 755 if (empty($update_time)) { 756 /* default the rrdupdate time to now */ 757 $rrd_update_values = 'N:'; 758 } else { 759 $rrd_update_values = $update_time . ':'; 760 } 761 762 $rrd_update_template = ''; 763 764 foreach ($field_array as $field_name => $value) { 765 if ($rrd_update_template != '') { 766 $rrd_update_template .= ':'; 767 $rrd_update_values .= ':'; 768 } 769 770 $rrd_update_template .= $field_name; 771 772 /* if we have "invalid data", give rrdtool an Unknown (U) */ 773 if (!isset($value) || !is_numeric($value)) { 774 $value = 'U'; 775 } 776 777 $rrd_update_values .= $value; 778 } 779 780 if (cacti_version_compare(get_rrdtool_version(),'1.5','>=')) { 781 $update_options='--skip-past-updates'; 782 } else { 783 $update_options=''; 784 } 785 786 rrdtool_execute("update $rrd_path $update_options --template $rrd_update_template $rrd_update_values", true, RRDTOOL_OUTPUT_STDOUT, $rrdtool_pipe, 'POLLER'); 787 $rrds_processed++; 788 } 789 } 790 } 791 792 return $rrds_processed; 793} 794 795function rrdtool_function_tune($rrd_tune_array) { 796 global $config, $data_source_types; 797 798 include($config['include_path'] . '/global_arrays.php'); 799 800 $data_source_name = get_data_source_item_name($rrd_tune_array['data_source_id']); 801 $data_source_type = $data_source_types[$rrd_tune_array['data-source-type']]; 802 $data_source_path = get_data_source_path($rrd_tune_array['data_source_id'], true); 803 804 $rrd_tune = ''; 805 if ($rrd_tune_array['heartbeat'] != '') { 806 $rrd_tune .= " --heartbeat $data_source_name:" . $rrd_tune_array['heartbeat']; 807 } 808 809 if ($rrd_tune_array['minimum'] != '') { 810 $rrd_tune .= " --minimum $data_source_name:" . $rrd_tune_array['minimum']; 811 } 812 813 if ($rrd_tune_array['maximum'] != '') { 814 $rrd_tune .= " --maximum $data_source_name:" . $rrd_tune_array['maximum']; 815 } 816 817 if ($rrd_tune_array['data-source-type'] != '') { 818 $rrd_tune .= " --data-source-type $data_source_name:" . $data_source_type; 819 } 820 821 if ($rrd_tune_array['data-source-rename'] != '') { 822 $rrd_tune .= " --data-source-rename $data_source_name:" . $rrd_tune_array['data-source-rename']; 823 } 824 825 if ($rrd_tune != '') { 826 if (file_exists($data_source_path) == true) { 827 if (is_file(read_config_option('path_rrdtool')) && is_executable(read_config_option('path_rrdtool'))) { 828 $fp = popen(read_config_option('path_rrdtool') . " tune $data_source_path $rrd_tune", 'r'); 829 pclose($fp); 830 831 cacti_log('CACTI2RRD: ' . read_config_option('path_rrdtool') . " tune $data_source_path $rrd_tune", false, 'WEBLOG', POLLER_VERBOSITY_DEBUG); 832 } else { 833 cacti_log("ERROR: RRDtool executable not found, not executable or error in path '" . read_config_option('path_rrdtool') . "'. No output written to RRDfile."); 834 } 835 } 836 } 837} 838 839/* rrdtool_function_fetch - given a data source, return all of its data in an array 840 @arg $local_data_id - the data source to fetch data for 841 @arg $start_time - the start time to use for the data calculation. this value can 842 either be absolute (unix timestamp) or relative (to now) 843 @arg $end_time - the end time to use for the data calculation. this value can 844 either be absolute (unix timestamp) or relative (to now) 845 @arg $resolution - the accuracy of the data measured in seconds 846 @arg $show_unknown - Show unknown 'NAN' values in the output as 'U' 847 @arg $rrdtool_file - Don't force Cacti to calculate the file 848 @arg $cf - Specify the consolidation function to use 849 @arg $rrdtool_pipe - a pipe to an rrdtool command 850 @returns - (array) an array containing all data in this data source broken down 851 by each data source item. the maximum of all data source items is included in 852 an item called 'nth_percentile_maximum'. The array will look as follows: 853 854 $fetch_array['data_source_names'][0] = 'ds1' 855 $fetch_array['data_source_names'][1] = 'ds2' 856 $fetch_array['data_source_names'][2] = 'nth_percentile_maximum' 857 $fetch_array['start_time'] = $timestamp; 858 $fetch_array['end_time'] = $timestamp; 859 $fetch_array['values'][$dsindex1][...] = $value; 860 $fetch_array['values'][$dsindex2][...] = $value; 861 $fetch_array['values'][$nth_index][...] = $value; 862 863 Again, the 'nth_percentile_maximum' will have the maximum value amoungst all the 864 data sources for each set of data. So, if you have traffic_in and traffic_out, 865 each member element in the array will have the maximum of traffic_in and traffic_out 866 in it. 867 */ 868function rrdtool_function_fetch($local_data_id, $start_time, $end_time, $resolution = 0, $show_unknown = false, $rrdtool_file = null, $cf = 'AVERAGE', $rrdtool_pipe = false) { 869 global $config; 870 871 include_once($config['library_path'] . '/boost.php'); 872 873 /* validate local data id */ 874 if (empty($local_data_id) && is_null($rrdtool_file)) { 875 return array(); 876 } 877 878 $time = time(); 879 880 /* initialize fetch array */ 881 $fetch_array = array(); 882 883 /* check if we have been passed a file instead of lodal data source to look up */ 884 if (is_null($rrdtool_file)) { 885 $data_source_path = get_data_source_path($local_data_id, true); 886 } else { 887 $data_source_path = $rrdtool_file; 888 } 889 890 // Find the correct resolution 891 if ($resolution == 0) { 892 $resolution = rrdtool_function_get_resstep($local_data_id, $start_time, $end_time, 'res'); 893 } 894 895 /* update the rrdfile if performing a fetch */ 896 boost_fetch_cache_check($local_data_id, $rrdtool_pipe); 897 898 /* build and run the rrdtool fetch command with all of our data */ 899 $cmd_line = "fetch $data_source_path $cf -s $start_time -e $end_time"; 900 if ($resolution > 0) { 901 $cmd_line .= " -r $resolution"; 902 } 903 904 $output = rrdtool_execute($cmd_line, false, RRDTOOL_OUTPUT_STDOUT, $rrdtool_pipe); 905 $output = explode("\n", $output); 906 907 $first = true; 908 $count = 0; 909 910 if (cacti_sizeof($output)) { 911 $timestamp = 0; 912 foreach($output as $line) { 913 $line = trim($line); 914 $max_array = array(); 915 916 if ($first) { 917 /* get the data source names */ 918 $fetch_array['data_source_names'] = preg_split('/\s+/', $line); 919 $first = false; 920 } elseif ($line != '') { 921 /* process the data sources into an array */ 922 $parts = explode(':', $line); 923 $timestamp = $parts[0]; 924 $data = explode(' ', trim($parts[1])); 925 926 if (!isset($fetch_array['timestamp']['start_time'])) { 927 $fetch_array['timestamp']['start_time'] = $timestamp; 928 } 929 930 /* process out bad data */ 931 foreach($data as $index => $number) { 932 if (strtolower($number) == 'nan' || strtolower($number) == '-nan') { 933 if ($show_unknown) { 934 $fetch_array['values'][$index][$timestamp] = 'U'; 935 } 936 } else { 937 $fetch_array['values'][$index][$timestamp] = $number + 0; 938 } 939 } 940 } 941 } 942 943 $fetch_array['timestamp']['end_time'] = $timestamp; 944 } 945 946 return $fetch_array; 947} 948 949function rrd_function_process_graph_options($graph_start, $graph_end, &$graph, &$graph_data_array) { 950 global $config, $image_types; 951 952 include($config['include_path'] . '/global_arrays.php'); 953 954 /* define some variables */ 955 $scale = ''; 956 $rigid = ''; 957 $unit_value = ''; 958 $version = get_rrdtool_version(); 959 $unit_exponent_value = ''; 960 961 if ($graph['auto_scale'] == 'on') { 962 switch ($graph['auto_scale_opts']) { 963 case '1': /* autoscale ignores lower, upper limit */ 964 $scale = '--alt-autoscale' . RRD_NL; 965 break; 966 case '2': /* autoscale-max, accepts a given lower limit */ 967 $scale = '--alt-autoscale-max' . RRD_NL; 968 if (is_numeric($graph['lower_limit'])) { 969 $scale .= '--lower-limit=' . cacti_escapeshellarg($graph['lower_limit']) . RRD_NL; 970 } 971 break; 972 case '3': /* autoscale-min, accepts a given upper limit */ 973 $scale = '--alt-autoscale-min' . RRD_NL; 974 if ( is_numeric($graph['upper_limit'])) { 975 $scale .= '--upper-limit=' . cacti_escapeshellarg($graph['upper_limit']) . RRD_NL; 976 } 977 break; 978 case '4': /* auto_scale with limits */ 979 $scale = '--alt-autoscale' . RRD_NL; 980 if ( is_numeric($graph['upper_limit'])) { 981 $scale .= '--upper-limit=' . cacti_escapeshellarg($graph['upper_limit']) . RRD_NL; 982 } 983 if ( is_numeric($graph['lower_limit'])) { 984 $scale .= '--lower-limit=' . cacti_escapeshellarg($graph['lower_limit']) . RRD_NL; 985 } 986 break; 987 } 988 } else { 989 if ($graph['upper_limit'] != '') { 990 $scale = '--upper-limit=' . cacti_escapeshellarg($graph['upper_limit']) . RRD_NL; 991 } 992 if ($graph['lower_limit'] != '') { 993 $scale .= '--lower-limit=' . cacti_escapeshellarg($graph['lower_limit']) . RRD_NL; 994 } 995 } 996 997 if ($graph['auto_scale_log'] == 'on') { 998 $scale .= '--logarithmic' . RRD_NL; 999 } 1000 1001 /* --units=si only defined for logarithmic y-axis scaling, even if it doesn't hurt on linear graphs */ 1002 if ($graph['scale_log_units'] == 'on' && $graph['auto_scale_log'] == 'on') { 1003 $scale .= '--units=si' . RRD_NL; 1004 } 1005 1006 if ($graph['auto_scale_rigid'] == 'on') { 1007 $rigid = '--rigid' . RRD_NL; 1008 } 1009 1010 if ($graph['unit_value'] != '') { 1011 $unit_value = '--y-grid=' . cacti_escapeshellarg($graph['unit_value']) . RRD_NL; 1012 } 1013 1014 if (preg_match('/^[0-9]+$/', $graph['unit_exponent_value'])) { 1015 $unit_exponent_value = '--units-exponent=' . cacti_escapeshellarg($graph['unit_exponent_value']) . RRD_NL; 1016 } 1017 1018 /* 1019 * optionally you can specify and array that overrides some of the db's values, lets set 1020 * that all up here 1021 */ 1022 1023 /* override: graph height (in pixels) */ 1024 if (isset($graph_data_array['graph_height'])) { 1025 $graph_height = $graph_data_array['graph_height']; 1026 } else { 1027 $graph_height = $graph['height']; 1028 } 1029 1030 /* override: graph width (in pixels) */ 1031 if (isset($graph_data_array['graph_width'])) { 1032 $graph_width = $graph_data_array['graph_width']; 1033 } else { 1034 $graph_width = $graph['width']; 1035 } 1036 1037 /* override: skip drawing the legend? */ 1038 if (isset($graph_data_array['graph_nolegend'])) { 1039 $graph_legend = '--no-legend' . RRD_NL; 1040 } else { 1041 $graph_legend = ''; 1042 } 1043 1044 /* export options */ 1045 if (isset($graph_data_array['export'])) { 1046 $graph_opts = $graph_data_array['export_filename'] . RRD_NL; 1047 } else { 1048 if (empty($graph_data_array['output_filename'])) { 1049 $graph_opts = '-' . RRD_NL; 1050 } else { 1051 $graph_opts = $graph_data_array['output_filename'] . RRD_NL; 1052 } 1053 } 1054 1055 if (isset($graph_data_array['image_format']) && $graph_data_array['image_format'] == 'png') { 1056 $graph['image_format_id'] = 1; 1057 } 1058 1059 /* basic graph options */ 1060 $graph_opts .= 1061 '--imgformat=' . $image_types[$graph['image_format_id']] . RRD_NL . 1062 '--start=' . cacti_escapeshellarg($graph_start) . RRD_NL . 1063 '--end=' . cacti_escapeshellarg($graph_end) . RRD_NL; 1064 1065 $graph_opts .= '--pango-markup ' . RRD_NL; 1066 1067 if (read_config_option('rrdtool_watermark') == 'on') { 1068 $graph_opts .= '--disable-rrdtool-tag ' . RRD_NL; 1069 } 1070 1071 foreach($graph as $key => $value) { 1072 switch($key) { 1073 case 'title_cache': 1074 if (!empty($value)) { 1075 $graph_opts .= '--title=' . cacti_escapeshellarg(html_escape($value)) . RRD_NL; 1076 } 1077 break; 1078 case 'alt_y_grid': 1079 if ($value == CHECKED) { 1080 $graph_opts .= '--alt-y-grid' . RRD_NL; 1081 } 1082 break; 1083 case 'unit_value': 1084 if (!empty($value)) { 1085 $graph_opts .= '--y-grid=' . cacti_escapeshellarg($value) . RRD_NL; 1086 } 1087 break; 1088 case 'unit_exponent_value': 1089 if (preg_match('/^[0-9]+$/', $value)) { 1090 $graph_opts .= '--units-exponent=' . $value . RRD_NL; 1091 } 1092 break; 1093 case 'height': 1094 if (isset($graph_data_array['graph_height']) && preg_match('/^[0-9]+$/', $graph_data_array['graph_height'])) { 1095 $graph_opts .= '--height=' . $graph_data_array['graph_height'] . RRD_NL; 1096 } else { 1097 $graph_opts .= '--height=' . $value . RRD_NL; 1098 } 1099 break; 1100 case 'width': 1101 if (isset($graph_data_array['graph_width']) && preg_match('/^[0-9]+$/', $graph_data_array['graph_width'])) { 1102 $graph_opts .= '--width=' . $graph_data_array['graph_width'] . RRD_NL; 1103 } else { 1104 $graph_opts .= '--width=' . $value . RRD_NL; 1105 } 1106 break; 1107 case 'graph_nolegend': 1108 if (isset($graph_data_array['graph_nolegend'])) { 1109 $graph_opts .= '--no-legend' . RRD_NL; 1110 } else { 1111 $graph_opts .= ''; 1112 } 1113 break; 1114 case 'base_value': 1115 if ($value == 1000 || $value == 1024) { 1116 $graph_opts .= '--base=' . $value . RRD_NL; 1117 } 1118 break; 1119 case 'vertical_label': 1120 if (!empty($value)) { 1121 $graph_opts .= '--vertical-label=' . cacti_escapeshellarg(html_escape($value)) . RRD_NL; 1122 } 1123 break; 1124 case 'slope_mode': 1125 if ($value == CHECKED) { 1126 $graph_opts .= '--slope-mode' . RRD_NL; 1127 } 1128 break; 1129 case 'right_axis': 1130 if (!empty($value)) { 1131 $graph_opts .= '--right-axis ' . cacti_escapeshellarg($value) . RRD_NL; 1132 } 1133 break; 1134 case 'right_axis_label': 1135 if (!empty($value)) { 1136 $graph_opts .= '--right-axis-label ' . cacti_escapeshellarg($value) . RRD_NL; 1137 } 1138 break; 1139 case 'right_axis_format': 1140 if (!empty($value)) { 1141 $format = db_fetch_cell_prepared('SELECT gprint_text from graph_templates_gprint WHERE id = ?', array($value)); 1142 $graph_opts .= '--right-axis-format ' . cacti_escapeshellarg(trim(str_replace('%s', '', $format))) . RRD_NL; 1143 } 1144 break; 1145 case 'no_gridfit': 1146 if ($value == CHECKED) { 1147 $graph_opts .= '--no-gridfit' . RRD_NL; 1148 } 1149 break; 1150 case 'unit_length': 1151 if (!empty($value)) { 1152 $graph_opts .= '--units-length ' . cacti_escapeshellarg($value) . RRD_NL; 1153 } 1154 break; 1155 case 'tab_width': 1156 if (!empty($value)) { 1157 $graph_opts .= '--tabwidth ' . cacti_escapeshellarg($value) . RRD_NL; 1158 } 1159 break; 1160 case 'dynamic_labels': 1161 if ($value == CHECKED) { 1162 $graph_opts .= '--dynamic-labels' . RRD_NL; 1163 } 1164 break; 1165 case 'force_rules_legend': 1166 if ($value == CHECKED) { 1167 $graph_opts .= '--force-rules-legend' . RRD_NL; 1168 } 1169 break; 1170 case 'legend_position': 1171 if (cacti_version_compare($version, '1.4', '>=')) { 1172 if (!empty($value)) { 1173 $graph_opts .= '--legend-position ' . cacti_escapeshellarg($value) . RRD_NL; 1174 } 1175 } 1176 break; 1177 case 'legend_direction': 1178 if (cacti_version_compare($version, '1.4', '>=')) { 1179 if (!empty($value)) { 1180 $graph_opts .= '--legend-direction ' . cacti_escapeshellarg($value) . RRD_NL; 1181 } 1182 } 1183 break; 1184 case 'left_axis_formatter': 1185 if (cacti_version_compare($version, '1.4', '>=')) { 1186 if (!empty($value)) { 1187 $graph_opts .= '--left-axis-formatter ' . cacti_escapeshellarg($value) . RRD_NL; 1188 } 1189 } 1190 break; 1191 case 'right_axis_formatter': 1192 if (cacti_version_compare($version, '1.4', '>=')) { 1193 if (!empty($value)) { 1194 $graph_opts .= '--right-axis-formatter ' . cacti_escapeshellarg($value) . RRD_NL; 1195 } 1196 } 1197 break; 1198 } 1199 } 1200 1201 $graph_opts .= "$rigid" . trim("$scale$unit_value$unit_exponent_value$graph_legend", "\n\r " . RRD_NL) . RRD_NL; 1202 1203 /* add a date to the graph legend */ 1204 $graph_opts .= rrdtool_function_format_graph_date($graph_data_array); 1205 1206 /* process theme and font styling options */ 1207 $graph_opts .= rrdtool_function_theme_font_options($graph_data_array); 1208 1209 /* Replace "|query_*|" in the graph command to replace e.g. vertical_label. */ 1210 $graph_opts = rrd_substitute_host_query_data($graph_opts, $graph, array()); 1211 1212 /* provide smooth lines */ 1213 if ($graph['slope_mode'] == 'on') { 1214 $graph_opts .= '--slope-mode' . RRD_NL; 1215 } 1216 1217 /* if the user desires a wartermark set it */ 1218 if (read_config_option('graph_watermark') != '') { 1219 $graph_opts .= '--watermark ' . cacti_escapeshellarg(read_config_option('graph_watermark')) . RRD_NL; 1220 } 1221 1222 return $graph_opts; 1223} 1224 1225function rrdtool_function_graph($local_graph_id, $rra_id, $graph_data_array, $rrdtool_pipe = false, &$xport_meta = array(), $user = 0) { 1226 global $config, $consolidation_functions, $graph_item_types, $encryption; 1227 1228 include_once($config['library_path'] . '/cdef.php'); 1229 include_once($config['library_path'] . '/vdef.php'); 1230 include_once($config['library_path'] . '/graph_variables.php'); 1231 include_once($config['library_path'] . '/boost.php'); 1232 include_once($config['library_path'] . '/xml.php'); 1233 include($config['include_path'] . '/global_arrays.php'); 1234 1235 /* prevent command injection 1236 * This function prepares an rrdtool graph statement to be executed by the web server. 1237 * We have to take care, that the attacker does not insert shell code. 1238 * As some rrdtool parameters accept "Cacti variables", we have to perform the 1239 * variable substitution prior to vulnerability checks. 1240 * We will enclose all parameters in quotes and substitute quotation marks within 1241 * those parameters. 1242 */ 1243 1244 /* before we do anything; make sure the user has permission to view this graph, 1245 if not then get out */ 1246 if ($user > 0) { 1247 if (!is_graph_allowed($local_graph_id, $user)) { 1248 return 'GRAPH ACCESS DENIED'; 1249 } 1250 } 1251 1252 if (getenv('LANG') == '') { 1253 putenv('LANG=' . str_replace('-', '_', CACTI_LOCALE) . '.UTF-8'); 1254 } 1255 1256 /* check the purge the boost poller output cache, and check for a live image file if caching is enabled */ 1257 $graph_data = boost_graph_cache_check($local_graph_id, $rra_id, $rrdtool_pipe, $graph_data_array, false); 1258 if ($graph_data !== false) { 1259 return $graph_data; 1260 } 1261 1262 if (empty($graph_data_array['graph_start'])) { 1263 $graph_data_array['graph_start'] = -86400; 1264 } 1265 1266 if (empty($graph_data_array['graph_end'])) { 1267 $graph_data_array['graph_end'] = -300; 1268 } 1269 1270 $local_data_ids = array_rekey( 1271 db_fetch_assoc_prepared('SELECT dtr.local_data_id 1272 FROM graph_templates_item AS gti 1273 INNER JOIN data_template_rrd AS dtr 1274 ON gti.task_item_id = dtr.id 1275 WHERE dtr.local_data_id > 0 1276 AND gti.local_graph_id = ?', 1277 array($local_graph_id)), 1278 'local_data_id', 'local_data_id' 1279 ); 1280 1281 $ds_step = rrdtool_function_get_resstep($local_data_ids, $graph_data_array['graph_start'], $graph_data_array['graph_end'], 'step'); 1282 1283 /* if no rra was specified, we need to figure out which one RRDtool will choose using 1284 * "best-fit" resolution fit algorithm */ 1285 if (empty($rra_id)) { 1286 if (empty($graph_data_array['graph_start']) || empty($graph_data_array['graph_end'])) { 1287 $rra['rows'] = 600; 1288 $rra['steps'] = 1; 1289 $rra['timespan'] = 86400; 1290 } else { 1291 /* get a list of RRAs related to this graph */ 1292 $rras = get_associated_rras($local_graph_id); 1293 1294 if (cacti_sizeof($rras)) { 1295 foreach ($rras as $unchosen_rra) { 1296 /* the timespan specified in the RRA "timespan" field may not be accurate */ 1297 $real_timespan = ($ds_step * $unchosen_rra['steps'] * $unchosen_rra['rows']); 1298 1299 /* make sure the current start/end times fit within each RRA's timespan */ 1300 if ($graph_data_array['graph_end'] - $graph_data_array['graph_start'] <= $real_timespan && time() - $graph_data_array['graph_start'] <= $real_timespan) { 1301 /* is this RRA better than the already chosen one? */ 1302 if (isset($rra) && $unchosen_rra['steps'] < $rra['steps']) { 1303 $rra = $unchosen_rra; 1304 } elseif (!isset($rra)) { 1305 $rra = $unchosen_rra; 1306 } 1307 } 1308 } 1309 } 1310 1311 if (!isset($rra)) { 1312 $rra['rows'] = 600; 1313 $rra['steps'] = 1; 1314 $rra['timespan'] = 86400; 1315 } 1316 } 1317 } else { 1318 $rra = db_fetch_row_prepared('SELECT 1319 dspr.rows, dsp.step, dspr.steps 1320 FROM data_source_profiles_rra AS dspr 1321 INNER JOIN data_source_profiles AS dsp 1322 ON dspr.data_source_profile_id=dsp.id 1323 WHERE dspr.id = ?', 1324 array($rra_id) 1325 ); 1326 1327 if (isset($rra['steps'])) { 1328 $rra['timespan'] = $rra['rows'] * $rra['step'] * $rra['steps']; 1329 } else { 1330 $rra['timespan'] = 86400; 1331 $rra['steps'] = 1; 1332 $rra['rows'] = 600; 1333 } 1334 } 1335 1336 if (!isset($graph_data_array['export_realtime']) && isset($rra['steps'])) { 1337 $rra_seconds = ($ds_step * $rra['steps']); 1338 } else { 1339 $rra_seconds = 5; 1340 } 1341 1342 $graph = db_fetch_row_prepared('SELECT gl.id AS local_graph_id, gl.host_id, 1343 gl.snmp_query_id, gl.snmp_index, gtg.title_cache, gtg.vertical_label, 1344 gtg.slope_mode, gtg.auto_scale, gtg.auto_scale_opts, gtg.auto_scale_log, 1345 gtg.scale_log_units, gtg.auto_scale_rigid, gtg.auto_padding, gtg.base_value, 1346 gtg.upper_limit, gtg.lower_limit, gtg.height, gtg.width, gtg.image_format_id, 1347 gtg.unit_value, gtg.unit_exponent_value, gtg.alt_y_grid, 1348 gtg.right_axis, gtg.right_axis_label, gtg.right_axis_format, gtg.no_gridfit, 1349 gtg.unit_length, gtg.tab_width, gtg.dynamic_labels, gtg.force_rules_legend, 1350 gtg.legend_position, gtg.legend_direction, gtg.right_axis_formatter, 1351 gtg.left_axis_formatter 1352 FROM graph_templates_graph AS gtg 1353 INNER JOIN graph_local AS gl 1354 ON gl.id=gtg.local_graph_id 1355 WHERE gtg.local_graph_id = ?', 1356 array($local_graph_id) 1357 ); 1358 1359 /* handle the case where the graph has been deleted */ 1360 if (!cacti_sizeof($graph)) { 1361 return false; 1362 } 1363 1364 /* lets make that sql query... */ 1365 $graph_items = db_fetch_assoc_prepared('SELECT gti.id AS graph_templates_item_id, 1366 gti.cdef_id, gti.vdef_id, gti.text_format, gti.value, gti.hard_return, 1367 gti.consolidation_function_id, gti.graph_type_id, gtgp.gprint_text, 1368 colors.hex, gti.alpha, gti.line_width, gti.dashes, gti.shift, 1369 gti.dash_offset, gti.textalign, dl.snmp_query_id, dl.snmp_index, 1370 dtr.id AS data_template_rrd_id, dtr.local_data_id, 1371 dtr.rrd_minimum, dtr.rrd_maximum, dtr.data_source_name, dtr.local_data_template_rrd_id 1372 FROM graph_templates_item AS gti 1373 LEFT JOIN data_template_rrd AS dtr 1374 ON gti.task_item_id=dtr.id 1375 LEFT JOIN data_local AS dl 1376 ON dl.id = dtr.local_data_id 1377 LEFT JOIN colors 1378 ON gti.color_id=colors.id 1379 LEFT JOIN graph_templates_gprint AS gtgp 1380 ON gti.gprint_id=gtgp.id 1381 WHERE gti.local_graph_id = ? 1382 ORDER BY gti.sequence', 1383 array($local_graph_id) 1384 ); 1385 1386 /* variables for use below */ 1387 $graph_defs = ''; 1388 $txt_graph_items = ''; 1389 $pad_number = 0; 1390 1391 /* override: graph start time */ 1392 if (!isset($graph_data_array['graph_start']) || $graph_data_array['graph_start'] == '0') { 1393 $graph_start = -($rra['timespan']); 1394 } else { 1395 $graph_start = $graph_data_array['graph_start']; 1396 } 1397 1398 /* override: graph end time */ 1399 if (!isset($graph_data_array['graph_end']) || $graph_data_array['graph_end'] == '0') { 1400 $graph_end = -($rra_seconds); 1401 } else { 1402 $graph_end = $graph_data_array['graph_end']; 1403 } 1404 1405 /* +++++++++++++++++++++++ GRAPH OPTIONS +++++++++++++++++++++++ */ 1406 1407 if (!isset($graph_data_array['export_csv'])) { 1408 $graph_opts = rrd_function_process_graph_options($graph_start, $graph_end, $graph, $graph_data_array); 1409 } else { 1410 /* basic export options */ 1411 $graph_opts = 1412 '--start=' . cacti_escapeshellarg($graph_start) . RRD_NL . 1413 '--end=' . cacti_escapeshellarg($graph_end) . RRD_NL . 1414 '--maxrows=10000' . RRD_NL; 1415 } 1416 1417 /* +++++++++++++++++++++++ LEGEND: MAGIC +++++++++++++++++++++++ */ 1418 1419 $realtimeCachePath = read_config_option('realtime_cache_path'); 1420 $dateTimeFormat = read_config_option('graph_dateformat'); 1421 $dateTime = date($dateTimeFormat, strtotime(read_config_option('date'))); 1422 1423 /* the following fields will be searched for graph variables */ 1424 $variable_fields = array( 1425 'text_format' => array( 1426 'process_no_legend' => false 1427 ), 1428 'value' => array( 1429 'process_no_legend' => true 1430 ), 1431 'cdef_cache' => array( 1432 'process_no_legend' => true 1433 ), 1434 'vdef_cache' => array( 1435 'process_no_legend' => true 1436 ) 1437 ); 1438 1439 $i = 0; 1440 $j = 0; 1441 $nth = 0; 1442 $sum = 0; 1443 $last_graph_cf = array(); 1444 if (cacti_sizeof($graph_items)) { 1445 /* we need to add a new column 'cf_reference', so unless PHP 5 is used, this foreach syntax is required */ 1446 foreach ($graph_items as $key => $graph_item) { 1447 /* mimic the old behavior: LINE[123], AREA and STACK items use the CF specified in the graph item */ 1448 switch ($graph_item['graph_type_id']) { 1449 case GRAPH_ITEM_TYPE_LINE1: 1450 case GRAPH_ITEM_TYPE_LINE2: 1451 case GRAPH_ITEM_TYPE_LINE3: 1452 case GRAPH_ITEM_TYPE_LINESTACK: 1453 case GRAPH_ITEM_TYPE_TIC: 1454 case GRAPH_ITEM_TYPE_AREA: 1455 case GRAPH_ITEM_TYPE_STACK: 1456 $graph_cf = generate_graph_best_cf($graph_item['local_data_id'], $graph_item['consolidation_function_id'], $rra_seconds); 1457 /* remember the last CF for this data source for use with GPRINT 1458 * if e.g. an AREA/AVERAGE and a LINE/MAX is used 1459 * we will have AVERAGE first and then MAX, depending on GPRINT sequence */ 1460 $last_graph_cf['data_source_name']['local_data_template_rrd_id'] = $graph_cf; 1461 /* remember this for second foreach loop */ 1462 $graph_items[$key]['cf_reference'] = $graph_cf; 1463 1464 break; 1465 case GRAPH_ITEM_TYPE_GPRINT: 1466 /* ATTENTION! 1467 * the 'CF' given on graph_item edit screen for GPRINT is indeed NOT a real 'CF', 1468 * but an aggregation function 1469 * see 'man rrdgraph_data' for the correct VDEF based notation 1470 * so our task now is to 'guess' the very graph_item, this GPRINT is related to 1471 * and to use that graph_item's CF */ 1472 if (isset($last_graph_cf['data_source_name']['local_data_template_rrd_id'])) { 1473 $graph_cf = $last_graph_cf['data_source_name']['local_data_template_rrd_id']; 1474 /* remember this for second foreach loop */ 1475 $graph_items[$key]['cf_reference'] = $graph_cf; 1476 } else { 1477 $graph_cf = generate_graph_best_cf($graph_item['local_data_id'], $graph_item['consolidation_function_id'], $rra_seconds); 1478 /* remember this for second foreach loop */ 1479 $graph_items[$key]['cf_reference'] = $graph_cf; 1480 } 1481 break; 1482 case GRAPH_ITEM_TYPE_GPRINT_AVERAGE: 1483 $graph_cf = $graph_item['consolidation_function_id']; 1484 $graph_items[$key]['cf_reference'] = $graph_cf; 1485 break; 1486 case GRAPH_ITEM_TYPE_GPRINT_LAST: 1487 $graph_cf = $graph_item['consolidation_function_id']; 1488 $graph_items[$key]['cf_reference'] = $graph_cf; 1489 break; 1490 case GRAPH_ITEM_TYPE_GPRINT_MAX: 1491 $graph_cf = $graph_item['consolidation_function_id']; 1492 $graph_items[$key]['cf_reference'] = $graph_cf; 1493 break; 1494 case GRAPH_ITEM_TYPE_GPRINT_MIN: 1495 $graph_cf = $graph_item['consolidation_function_id']; 1496 $graph_items[$key]['cf_reference'] = $graph_cf; 1497 break; 1498 default: 1499 /* all other types are based on the best matching CF */ 1500 $graph_cf = generate_graph_best_cf($graph_item['local_data_id'], $graph_item['consolidation_function_id'], $rra_seconds); 1501 /* remember this for second foreach loop */ 1502 $graph_items[$key]['cf_reference'] = $graph_cf; 1503 break; 1504 } 1505 1506 if (!empty($graph_item['local_data_id']) && !isset($cf_ds_cache[$graph_item['data_template_rrd_id']][$graph_cf])) { 1507 /* use a user-specified ds path if one is entered */ 1508 if (isset($graph_data_array['export_realtime'])) { 1509 $data_source_path = $realtimeCachePath . '/user_' . hash('sha256',session_id()) . '_' . $graph_item['local_data_id'] . '.rrd'; 1510 } else { 1511 $data_source_path = get_data_source_path($graph_item['local_data_id'], true); 1512 } 1513 1514 /* FOR WIN32: Escape all colon for drive letters (ex. D\:/path/to/rra) */ 1515 $data_source_path = rrdtool_escape_string($data_source_path); 1516 1517 if (!empty($data_source_path)) { 1518 /* NOTE: (Update) Data source DEF names are created using the graph_item_id; then passed 1519 to a function that matches the digits with letters. rrdtool likes letters instead 1520 of numbers in DEF names; especially with CDEFs. CDEFs are created 1521 the same way, except a 'cdef' is put on the beginning of the hash */ 1522 $graph_defs .= 'DEF:' . generate_graph_def_name(strval($i)) . '=' . cacti_escapeshellarg($data_source_path) . ':' . cacti_escapeshellarg($graph_item['data_source_name'], true) . ':' . $consolidation_functions[$graph_cf] . RRD_NL; 1523 1524 $cf_ds_cache[$graph_item['data_template_rrd_id']][$graph_cf] = "$i"; 1525 1526 $i++; 1527 } 1528 } 1529 1530 /* cache cdef value here to support data query variables in the cdef string */ 1531 if (empty($graph_item['cdef_id'])) { 1532 $graph_item['cdef_cache'] = ''; 1533 $graph_items[$j]['cdef_cache'] = ''; 1534 } else { 1535 $cdef = get_cdef($graph_item['cdef_id']); 1536 1537 $graph_item['cdef_cache'] = $cdef; 1538 $graph_items[$j]['cdef_cache'] = $cdef; 1539 } 1540 1541 /* cache vdef value here */ 1542 if (empty($graph_item['vdef_id'])) { 1543 $graph_item['vdef_cache'] = ''; 1544 $graph_items[$j]['vdef_cache'] = ''; 1545 } else { 1546 $vdef = get_vdef($graph_item['vdef_id']); 1547 1548 $graph_item['vdef_cache'] = $vdef; 1549 $graph_items[$j]['vdef_cache'] = $vdef; 1550 } 1551 1552 /* +++++++++++++++++++++++ LEGEND: TEXT SUBSTITUTION (<>) +++++++++++++++++++++++ */ 1553 1554 /* note the current item_id for easy access */ 1555 $graph_item_id = $graph_item['graph_templates_item_id']; 1556 1557 /* loop through each field that we want to substitute values for: 1558 currently: text format and value */ 1559 foreach ($variable_fields as $field_name => $field_array) { 1560 /* certain fields do not require values when the legend is not to be shown */ 1561 if ($field_array['process_no_legend'] == false && isset($graph_data_array['graph_nolegend'])) { 1562 continue; 1563 } 1564 1565 $graph_variables[$field_name][$graph_item_id] = $graph_item[$field_name]; 1566 1567 $search = array(); 1568 $replace = array(); 1569 1570 /* date/time substitution */ 1571 if (strstr($graph_variables[$field_name][$graph_item_id], '|date_time|')) { 1572 $search[] = '|date_time|'; 1573 $replace[] = $dateTime; 1574 } 1575 1576 /* data source title substitution */ 1577 if (strstr($graph_variables[$field_name][$graph_item_id], '|data_source_title|')) { 1578 $search[] = '|data_source_title|'; 1579 $replace[] = get_data_source_title($graph_item['local_data_id']); 1580 } 1581 1582 /* data query variables */ 1583 $graph_variables[$field_name][$graph_item_id] = rrd_substitute_host_query_data($graph_variables[$field_name][$graph_item_id], $graph, $graph_item); 1584 1585 /* Nth percentile */ 1586 if (preg_match_all('/\|([0-9]{1,2}):(bits|bytes):(\d):(current|total|max|total_peak|all_max_current|all_max_peak|aggregate_max|aggregate_sum|aggregate_current|aggregate_peak|aggregate):(\d)?\|/', $graph_variables[$field_name][$graph_item_id], $matches, PREG_SET_ORDER)) { 1587 foreach ($matches as $match) { 1588 $search[] = $match[0]; 1589 $value = variable_nth_percentile($match, $graph, $graph_item, $graph_items, $graph_start, $graph_end); 1590 $replace[] = $value; 1591 1592 if ($field_name == 'value') { 1593 $xport_meta['NthPercentile'][$nth]['format'] = $match[0]; 1594 $xport_meta['NthPercentile'][$nth]['value'] = str_replace($match[0], $value, $graph_variables[$field_name][$graph_item_id]); 1595 $nth++; 1596 } 1597 } 1598 } 1599 1600 /* bandwidth summation */ 1601 if (preg_match_all('/\|sum:(\d|auto):(current|total|atomic):(\d):(\d+|auto)\|/', $graph_variables[$field_name][$graph_item_id], $matches, PREG_SET_ORDER)) { 1602 foreach ($matches as $match) { 1603 $search[] = $match[0]; 1604 $value = variable_bandwidth_summation($match, $graph, $graph_item, $graph_items, $graph_start, $graph_end, $rra['steps'], $ds_step); 1605 $replace[] = $value; 1606 1607 if ($field_name == 'text_format') { 1608 $xport_meta['Summation'][$sum]['format'] = $match[0]; 1609 $xport_meta['Summation'][$sum]['value'] = str_replace($match[0], $value, $graph_variables[$field_name][$graph_item_id]); 1610 $sum++; 1611 } 1612 } 1613 } 1614 1615 if (cacti_count($search)) { 1616 $graph_variables[$field_name][$graph_item_id] = str_replace($search, $replace, $graph_variables[$field_name][$graph_item_id]); 1617 } 1618 } 1619 1620 /* if we are not displaying a legend there is no point in us even processing the auto padding, 1621 text format stuff. */ 1622 if (!isset($graph_data_array['graph_nolegend'])) { 1623 /* set hard return variable if selected (\n) */ 1624 if ($graph_item['hard_return'] == 'on') { 1625 $hardreturn[$graph_item_id] = "\\n"; 1626 } else { 1627 $hardreturn[$graph_item_id] = ''; 1628 } 1629 } 1630 1631 $j++; 1632 } 1633 } 1634 1635 /* +++++++++++++++++++++++ LEGEND: AUTO PADDING (<>) +++++++++++++++++++++++ */ 1636 if (cacti_sizeof($graph_items)) { 1637 /* we need to add a new column 'cf_reference', so unless PHP 5 is used, this foreach 1638 * syntax is required 1639 */ 1640 foreach ($graph_items as $key => $graph_item) { 1641 /* note the current item_id for easy access */ 1642 $graph_item_id = $graph_item['graph_templates_item_id']; 1643 1644 /* if we are not displaying a legend there is no point in us even processing the 1645 * auto padding, text format stuff. 1646 */ 1647 if (!isset($graph_data_array['graph_nolegend'])) { 1648 /* PADDING: remember this is not perfect! its main use is for the basic graph setup of: 1649 * AREA - GPRINT-CURRENT - GPRINT-AVERAGE - GPRINT-MAXIMUM \n 1650 * of course it can be used in other situations, however may not work as intended. 1651 * If you have any additions to this small peice of code, feel free to send them to me. 1652 */ 1653 if ($graph['auto_padding'] == 'on') { 1654 /* only applies to AREA, STACK and LINEs */ 1655 if (preg_match('/(AREA|STACK|LINE[123])/', $graph_item_types[$graph_item['graph_type_id']])) { 1656 $text_format_length = mb_strlen(trim($graph_variables['text_format'][$graph_item_id]), 'UTF-8'); 1657 1658 if ($text_format_length > $pad_number) { 1659 $pad_number = $text_format_length; 1660 } 1661 } 1662 } 1663 } 1664 } 1665 } 1666 /* +++++++++++++++++++++++ LEGEND: AUTO PADDING (<>) +++++++++++++++++++++++ */ 1667 1668 /* +++++++++++++++++++++++ GRAPH ITEMS: CDEF +++++++++++++++++++++++ */ 1669 1670 $i = 0; 1671 1672 /* hack for rrdtool 1.2.x support */ 1673 $graph_item_stack_type = ''; 1674 1675 if (cacti_sizeof($graph_items)) { 1676 foreach ($graph_items as $graph_item) { 1677 /* hack around RRDtool behavior in first RRA */ 1678 $graph_cf = generate_graph_best_cf($graph_item['local_data_id'], $graph_item['consolidation_function_id'], $rra_seconds); 1679 1680 /* first we need to check if there is a DEF for the current data source/cf combination. if so, 1681 we will use that */ 1682 if (isset($cf_ds_cache[$graph_item['data_template_rrd_id']][$graph_cf])) { 1683 $cf_id = $graph_item['consolidation_function_id']; 1684 } else { 1685 /* if there is not a DEF defined for the current data source/cf combination, then we will have to 1686 improvise. choose the first available cf in the following order: AVERAGE, MAX, MIN, LAST */ 1687 if (isset($cf_ds_cache[$graph_item['data_template_rrd_id']][1])) { 1688 $cf_id = 1; /* CF: AVERAGE */ 1689 } elseif (isset($cf_ds_cache[$graph_item['data_template_rrd_id']][3])) { 1690 $cf_id = 3; /* CF: MAX */ 1691 } elseif (isset($cf_ds_cache[$graph_item['data_template_rrd_id']][2])) { 1692 $cf_id = 2; /* CF: MIN */ 1693 } elseif (isset($cf_ds_cache[$graph_item['data_template_rrd_id']][4])) { 1694 $cf_id = 4; /* CF: LAST */ 1695 } else { 1696 $cf_id = 1; /* CF: AVERAGE */ 1697 } 1698 } 1699 /* now remember the correct CF reference */ 1700 $cf_id = $graph_item['cf_reference']; 1701 1702 /* +++++++++++++++++++++++ GRAPH ITEMS: CDEF START +++++++++++++++++++++++ */ 1703 1704 /* make cdef string here; a note about CDEFs in cacti. A CDEF is neither unique to a 1705 data source of global cdef, but is unique when those two variables combine. */ 1706 $cdef_graph_defs = ''; 1707 1708 if ((!empty($graph_item['cdef_id'])) && (!isset($cdef_cache[$graph_item['cdef_id']][$graph_item['data_template_rrd_id']][$cf_id]))) { 1709 1710 $cdef_string = $graph_variables['cdef_cache'][$graph_item['graph_templates_item_id']]; 1711 $magic_item = array(); 1712 $already_seen = array(); 1713 $sources_seen = array(); 1714 1715 $count_all_ds_dups = 0; 1716 $count_all_ds_nodups = 0; 1717 $count_similar_ds_dups = 0; 1718 $count_similar_ds_nodups = 0; 1719 1720 /* if any of those magic variables are requested ... */ 1721 if (preg_match('/(ALL_DATA_SOURCES_(NO)?DUPS|SIMILAR_DATA_SOURCES_(NO)?DUPS)/', $cdef_string) || 1722 preg_match('/(COUNT_ALL_DS_(NO)?DUPS|COUNT_SIMILAR_DS_(NO)?DUPS)/', $cdef_string)) { 1723 1724 /* now walk through each case to initialize array*/ 1725 if (preg_match('/ALL_DATA_SOURCES_DUPS/', $cdef_string)) { 1726 $magic_item['ALL_DATA_SOURCES_DUPS'] = ''; 1727 } 1728 1729 if (preg_match('/ALL_DATA_SOURCES_NODUPS/', $cdef_string)) { 1730 $magic_item['ALL_DATA_SOURCES_NODUPS'] = ''; 1731 } 1732 1733 if (preg_match('/SIMILAR_DATA_SOURCES_DUPS/', $cdef_string)) { 1734 $magic_item['SIMILAR_DATA_SOURCES_DUPS'] = ''; 1735 } 1736 1737 if (preg_match('/SIMILAR_DATA_SOURCES_NODUPS/', $cdef_string)) { 1738 $magic_item['SIMILAR_DATA_SOURCES_NODUPS'] = ''; 1739 } 1740 1741 if (preg_match('/COUNT_ALL_DS_DUPS/', $cdef_string)) { 1742 $magic_item['COUNT_ALL_DS_DUPS'] = ''; 1743 } 1744 1745 if (preg_match('/COUNT_ALL_DS_NODUPS/', $cdef_string)) { 1746 $magic_item['COUNT_ALL_DS_NODUPS'] = ''; 1747 } 1748 1749 if (preg_match('/COUNT_SIMILAR_DS_DUPS/', $cdef_string)) { 1750 $magic_item['COUNT_SIMILAR_DS_DUPS'] = ''; 1751 } 1752 1753 if (preg_match('/COUNT_SIMILAR_DS_NODUPS/', $cdef_string)) { 1754 $magic_item['COUNT_SIMILAR_DS_NODUPS'] = ''; 1755 } 1756 1757 /* loop over all graph items */ 1758 foreach($graph_items as $gi_check) { 1759 /* only work on graph items, omit GRPINTs, COMMENTs and stuff */ 1760 if ((preg_match('/(AREA|STACK|LINE[123])/', $graph_item_types[$gi_check['graph_type_id']])) && (!empty($gi_check['data_template_rrd_id']))) { 1761 /* if the user screws up CF settings, PHP will generate warnings if left unchecked */ 1762 1763 /* matching consolidation function? */ 1764 if (isset($cf_ds_cache[$gi_check['data_template_rrd_id']][$cf_id])) { 1765 $def_name = generate_graph_def_name(strval($cf_ds_cache[$gi_check['data_template_rrd_id']][$cf_id])); 1766 1767 /* do we need ALL_DATA_SOURCES_DUPS? */ 1768 if (isset($magic_item['ALL_DATA_SOURCES_DUPS'])) { 1769 $magic_item['ALL_DATA_SOURCES_DUPS'] .= ($count_all_ds_dups == 0 ? '' : ',') . 'TIME,' . (time() - $rra_seconds) . ",GT,$def_name,$def_name,UN,0,$def_name,IF,IF"; /* convert unknowns to '0' first */ 1770 } 1771 1772 /* do we need COUNT_ALL_DS_DUPS? */ 1773 if (isset($magic_item['COUNT_ALL_DS_DUPS'])) { 1774 $magic_item['COUNT_ALL_DS_DUPS'] .= ($count_all_ds_dups == 0 ? '' : ',') . 'TIME,' . (time() - $rra_seconds) . ",GT,1,$def_name,UN,0,1,IF,IF"; /* convert unknowns to '0' first */ 1775 } 1776 1777 $count_all_ds_dups++; 1778 1779 /* check if this item also qualifies for NODUPS */ 1780 if (!isset($already_seen[$def_name])) { 1781 if (isset($magic_item['ALL_DATA_SOURCES_NODUPS'])) { 1782 $magic_item['ALL_DATA_SOURCES_NODUPS'] .= ($count_all_ds_nodups == 0 ? '' : ',') . 'TIME,' . (time() - $rra_seconds) . ",GT,$def_name,$def_name,UN,0,$def_name,IF,IF"; /* convert unknowns to '0' first */ 1783 } 1784 1785 if (isset($magic_item['COUNT_ALL_DS_NODUPS'])) { 1786 $magic_item['COUNT_ALL_DS_NODUPS'] .= ($count_all_ds_nodups == 0 ? '' : ',') . 'TIME,' . (time() - $rra_seconds) . ",GT,1,$def_name,UN,0,1,IF,IF"; /* convert unknowns to '0' first */ 1787 } 1788 1789 $count_all_ds_nodups++; 1790 $already_seen[$def_name] = true; 1791 } 1792 1793 /* check for SIMILAR data sources */ 1794 if ($graph_item['data_source_name'] == $gi_check['data_source_name']) { 1795 /* do we need SIMILAR_DATA_SOURCES_DUPS? */ 1796 if (isset($magic_item['SIMILAR_DATA_SOURCES_DUPS']) && ($graph_item['data_source_name'] == $gi_check['data_source_name'])) { 1797 $magic_item['SIMILAR_DATA_SOURCES_DUPS'] .= ($count_similar_ds_dups == 0 ? '' : ',') . 'TIME,' . (time() - $rra_seconds) . ",GT,$def_name,$def_name,UN,0,$def_name,IF,IF"; /* convert unknowns to '0' first */ 1798 } 1799 1800 /* do we need COUNT_SIMILAR_DS_DUPS? */ 1801 if (isset($magic_item['COUNT_SIMILAR_DS_DUPS']) && ($graph_item['data_source_name'] == $gi_check['data_source_name'])) { 1802 $magic_item['COUNT_SIMILAR_DS_DUPS'] .= ($count_similar_ds_dups == 0 ? '' : ',') . 'TIME,' . (time() - $rra_seconds) . ",GT,1,$def_name,UN,0,1,IF,IF"; /* convert unknowns to '0' first */ 1803 } 1804 1805 $count_similar_ds_dups++; 1806 1807 /* check if this item also qualifies for NODUPS */ 1808 if (!isset($sources_seen[$gi_check['data_template_rrd_id']])) { 1809 if (isset($magic_item['SIMILAR_DATA_SOURCES_NODUPS'])) { 1810 $magic_item['SIMILAR_DATA_SOURCES_NODUPS'] .= ($count_similar_ds_nodups == 0 ? '' : ',') . 'TIME,' . (time() - $rra_seconds) . ",GT,$def_name,$def_name,UN,0,$def_name,IF,IF"; /* convert unknowns to '0' first */ 1811 } 1812 1813 if (isset($magic_item['COUNT_SIMILAR_DS_NODUPS']) && ($graph_item['data_source_name'] == $gi_check['data_source_name'])) { 1814 $magic_item['COUNT_SIMILAR_DS_NODUPS'] .= ($count_similar_ds_nodups == 0 ? '' : ',') . 'TIME,' . (time() - $rra_seconds) . ",GT,1,$def_name,UN,0,1,IF,IF"; /* convert unknowns to '0' first */ 1815 } 1816 1817 $count_similar_ds_nodups++; 1818 $sources_seen[$gi_check['data_template_rrd_id']] = true; 1819 } 1820 } # SIMILAR data sources 1821 } # matching consolidation function? 1822 } # only work on graph items, omit GRPINTs, COMMENTs and stuff 1823 } # loop over all graph items 1824 1825 /* if there is only one item to total, don't even bother with the summation. 1826 * Otherwise cdef=a,b,c,+,+ is fine. */ 1827 if ($count_all_ds_dups > 1 && isset($magic_item['ALL_DATA_SOURCES_DUPS'])) { 1828 $magic_item['ALL_DATA_SOURCES_DUPS'] .= str_repeat(',+', ($count_all_ds_dups - 2)) . ',+'; 1829 } 1830 1831 if ($count_all_ds_nodups > 1 && isset($magic_item['ALL_DATA_SOURCES_NODUPS'])) { 1832 $magic_item['ALL_DATA_SOURCES_NODUPS'] .= str_repeat(',+', ($count_all_ds_nodups - 2)) . ',+'; 1833 } 1834 1835 if ($count_similar_ds_dups > 1 && isset($magic_item['SIMILAR_DATA_SOURCES_DUPS'])) { 1836 $magic_item['SIMILAR_DATA_SOURCES_DUPS'] .= str_repeat(',+', ($count_similar_ds_dups - 2)) . ',+'; 1837 } 1838 1839 if ($count_similar_ds_nodups > 1 && isset($magic_item['SIMILAR_DATA_SOURCES_NODUPS'])) { 1840 $magic_item['SIMILAR_DATA_SOURCES_NODUPS'] .= str_repeat(',+', ($count_similar_ds_nodups - 2)) . ',+'; 1841 } 1842 1843 if ($count_all_ds_dups > 1 && isset($magic_item['COUNT_ALL_DS_DUPS'])) { 1844 $magic_item['COUNT_ALL_DS_DUPS'] .= str_repeat(',+', ($count_all_ds_dups - 2)) . ',+'; 1845 } 1846 1847 if ($count_all_ds_nodups > 1 && isset($magic_item['COUNT_ALL_DS_NODUPS'])) { 1848 $magic_item['COUNT_ALL_DS_NODUPS'] .= str_repeat(',+', ($count_all_ds_nodups - 2)) . ',+'; 1849 } 1850 1851 if ($count_similar_ds_dups > 1 && isset($magic_item['COUNT_SIMILAR_DS_DUPS'])) { 1852 $magic_item['COUNT_SIMILAR_DS_DUPS'] .= str_repeat(',+', ($count_similar_ds_dups - 2)) . ',+'; 1853 } 1854 1855 if ($count_similar_ds_nodups > 1 && isset($magic_item['COUNT_SIMILAR_DS_NODUPS'])) { 1856 $magic_item['COUNT_SIMILAR_DS_NODUPS'] .= str_repeat(',+', ($count_similar_ds_nodups - 2)) . ',+'; 1857 } 1858 } 1859 1860 /* allow automatic rate calculations on raw guage data */ 1861 if (isset($graph_item['local_data_id'])) { 1862 $cdef_string = str_replace('CURRENT_DATA_SOURCE_PI', db_fetch_cell_prepared('SELECT rrd_step FROM data_template_data WHERE local_data_id = ?', array($graph_item['local_data_id'])), $cdef_string); 1863 } else { 1864 $cdef_string = str_replace('CURRENT_DATA_SOURCE_PI', read_config_option('poller_interval'), $cdef_string); 1865 } 1866 1867 $cdef_string = str_replace('CURRENT_DATA_SOURCE', generate_graph_def_name(strval((isset($cf_ds_cache[$graph_item['data_template_rrd_id']][$cf_id]) ? $cf_ds_cache[$graph_item['data_template_rrd_id']][$cf_id] : '0'))), $cdef_string); 1868 1869 /* allow automatic rate calculations on raw guage data */ 1870 if (isset($graph_item['local_data_id'])) { 1871 $cdef_string = str_replace('ALL_DATA_SOURCES_DUPS_PI', db_fetch_cell_prepared('SELECT rrd_step FROM data_template_data WHERE local_data_id = ?', array($graph_item['local_data_id'])), $cdef_string); 1872 } else { 1873 $cdef_string = str_replace('ALL_DATA_SOURCES_DUPS_PI', read_config_option('poller_interval'), $cdef_string); 1874 } 1875 1876 /* ALL|SIMILAR_DATA_SOURCES(NO)?DUPS are to be replaced here */ 1877 if (isset($magic_item['ALL_DATA_SOURCES_DUPS'])) { 1878 $cdef_string = str_replace('ALL_DATA_SOURCES_DUPS', $magic_item['ALL_DATA_SOURCES_DUPS'], $cdef_string); 1879 } 1880 1881 /* allow automatic rate calculations on raw guage data */ 1882 if (isset($graph_item['local_data_id'])) { 1883 $cdef_string = str_replace('ALL_DATA_SOURCES_NODUPS_PI', db_fetch_cell_prepared('SELECT rrd_step FROM data_template_data WHERE local_data_id = ?', array($graph_item['local_data_id'])), $cdef_string); 1884 } else { 1885 $cdef_string = str_replace('ALL_DATA_SOURCES_NODUPS_PI', read_config_option('poller_interval'), $cdef_string); 1886 } 1887 1888 if (isset($magic_item['ALL_DATA_SOURCES_NODUPS'])) { 1889 $cdef_string = str_replace('ALL_DATA_SOURCES_NODUPS', $magic_item['ALL_DATA_SOURCES_NODUPS'], $cdef_string); 1890 } 1891 1892 /* allow automatic rate calculations on raw guage data */ 1893 if (isset($graph_item['local_data_id'])) { 1894 $cdef_string = str_replace('SIMILAR_DATA_SOURCES_DUPS_PI', db_fetch_cell_prepared('SELECT rrd_step FROM data_template_data WHERE local_data_id = ?', array($graph_item['local_data_id'])), $cdef_string); 1895 } else { 1896 $cdef_string = str_replace('SIMILAR_DATA_SOURCES_DUPS_PI', read_config_option('poller_interval'), $cdef_string); 1897 } 1898 1899 if (isset($magic_item['SIMILAR_DATA_SOURCES_DUPS'])) { 1900 $cdef_string = str_replace('SIMILAR_DATA_SOURCES_DUPS', $magic_item['SIMILAR_DATA_SOURCES_DUPS'], $cdef_string); 1901 } 1902 1903 if (isset($graph_item['local_data_id'])) { 1904 $cdef_string = str_replace('SIMILAR_DATA_SOURCES_NODUPS_PI', db_fetch_cell_prepared('SELECT rrd_step FROM data_template_data WHERE local_data_id = ?', array($graph_item['local_data_id'])), $cdef_string); 1905 } else { 1906 $cdef_string = str_replace('SIMILAR_DATA_SOURCES_NODUPS_PI', read_config_option('poller_id'), $cdef_string); 1907 } 1908 1909 if (isset($magic_item['SIMILAR_DATA_SOURCES_NODUPS'])) { 1910 $cdef_string = str_replace('SIMILAR_DATA_SOURCES_NODUPS', $magic_item['SIMILAR_DATA_SOURCES_NODUPS'], $cdef_string); 1911 } 1912 1913 /* COUNT_ALL|SIMILAR_DATA_SOURCES(NO)?DUPS are to be replaced here */ 1914 if (isset($magic_item['COUNT_ALL_DS_DUPS'])) { 1915 $cdef_string = str_replace('COUNT_ALL_DS_DUPS', $magic_item['COUNT_ALL_DS_DUPS'], $cdef_string); 1916 } 1917 1918 if (isset($magic_item['COUNT_ALL_DS_NODUPS'])) { 1919 $cdef_string = str_replace('COUNT_ALL_DS_NODUPS', $magic_item['COUNT_ALL_DS_NODUPS'], $cdef_string); 1920 } 1921 1922 if (isset($magic_item['COUNT_SIMILAR_DS_DUPS'])) { 1923 $cdef_string = str_replace('COUNT_SIMILAR_DS_DUPS', $magic_item['COUNT_SIMILAR_DS_DUPS'], $cdef_string); 1924 } 1925 1926 if (isset($magic_item['COUNT_SIMILAR_DS_NODUPS'])) { 1927 $cdef_string = str_replace('COUNT_SIMILAR_DS_NODUPS', $magic_item['COUNT_SIMILAR_DS_NODUPS'], $cdef_string); 1928 } 1929 1930 /* data source item variables */ 1931 $cdef_string = str_replace('CURRENT_DS_MINIMUM_VALUE', (empty($graph_item['rrd_minimum']) ? '0' : $graph_item['rrd_minimum']), $cdef_string); 1932 $cdef_string = str_replace('CURRENT_DS_MAXIMUM_VALUE', (empty($graph_item['rrd_maximum']) ? '0' : $graph_item['rrd_maximum']), $cdef_string); 1933 $cdef_string = str_replace('CURRENT_GRAPH_MINIMUM_VALUE', (empty($graph['lower_limit']) ? '0' : $graph['lower_limit']), $cdef_string); 1934 $cdef_string = str_replace('CURRENT_GRAPH_MAXIMUM_VALUE', (empty($graph['upper_limit']) ? '0' : $graph['upper_limit']), $cdef_string); 1935 1936 if ((strpos($cdef_string, '|query_ifHighSpeed|') !== false) || 1937 (strpos($cdef_string, '|query_ifSpeed|') !== false)) { 1938 $local_data = db_fetch_row_prepared('SELECT * 1939 FROM data_local 1940 WHERE id = ?', 1941 array($graph_item['local_data_id'])); 1942 1943 $speed = rrdtool_function_interface_speed($local_data); 1944 1945 $cdef_string = str_replace(array('|query_ifHighSpeed|','|query_ifSpeed|'), array($speed, $speed), $cdef_string); 1946 } 1947 1948 /* replace query variables in cdefs */ 1949 $cdef_string = rrd_substitute_host_query_data($cdef_string, $graph, $graph_item); 1950 1951 /* make the initial 'virtual' cdef name: 'cdef' + [a,b,c,d...] */ 1952 $cdef_graph_defs .= 'CDEF:cdef' . generate_graph_def_name(strval($i)) . '='; 1953 /* prohibit command injection and provide platform specific quoting */ 1954 $cdef_graph_defs .= cacti_escapeshellarg(sanitize_cdef($cdef_string), true); 1955 $cdef_graph_defs .= " \\\n"; 1956 1957 /* the CDEF cache is so we do not create duplicate CDEF's on a graph */ 1958 $cdef_cache[$graph_item['cdef_id']][$graph_item['data_template_rrd_id']][$cf_id] = $i; 1959 } 1960 1961 /* add the cdef string to the end of the def string */ 1962 $graph_defs .= $cdef_graph_defs; 1963 1964 /* +++++++++++++++++++++++ GRAPH ITEMS: CDEFs END +++++++++++++++++++++++ */ 1965 1966 /* +++++++++++++++++++++++ GRAPH ITEMS: VDEFs START +++++++++++++++++++++++ */ 1967 1968 /* make vdef string here, copied from cdef stuff */ 1969 $vdef_graph_defs = ''; 1970 1971 if ((!empty($graph_item['vdef_id'])) && (!isset($vdef_cache[$graph_item['vdef_id']][$graph_item['cdef_id']][$graph_item['data_template_rrd_id']][$cf_id]))) { 1972 $vdef_string = $graph_variables['vdef_cache'][$graph_item['graph_templates_item_id']]; 1973 /* do we refer to a CDEF within this VDEF? */ 1974 if ($graph_item['cdef_id'] != '0') { 1975 /* 'calculated' VDEF: use (cached) CDEF as base, only way to get calculations into VDEFs */ 1976 $vdef_string = 'cdef' . str_replace('CURRENT_DATA_SOURCE', generate_graph_def_name(strval(isset($cdef_cache[$graph_item['cdef_id']][$graph_item['data_template_rrd_id']][$cf_id]) ? $cdef_cache[$graph_item['cdef_id']][$graph_item['data_template_rrd_id']][$cf_id] : '0')), $vdef_string); 1977 } else { 1978 /* 'pure' VDEF: use DEF as base */ 1979 $vdef_string = str_replace('CURRENT_DATA_SOURCE', generate_graph_def_name(strval(isset($cf_ds_cache[$graph_item['data_template_rrd_id']][$cf_id]) ? $cf_ds_cache[$graph_item['data_template_rrd_id']][$cf_id] : '0')), $vdef_string); 1980 } 1981 1982 # TODO: It would be possible to refer to a CDEF, but that's all. So ALL_DATA_SOURCES_NODUPS and stuff can't be used directly! 1983 # $vdef_string = str_replace('ALL_DATA_SOURCES_NODUPS', $magic_item['ALL_DATA_SOURCES_NODUPS'], $vdef_string); 1984 # $vdef_string = str_replace('ALL_DATA_SOURCES_DUPS', $magic_item['ALL_DATA_SOURCES_DUPS'], $vdef_string); 1985 # $vdef_string = str_replace('SIMILAR_DATA_SOURCES_NODUPS', $magic_item['SIMILAR_DATA_SOURCES_NODUPS'], $vdef_string); 1986 # $vdef_string = str_replace('SIMILAR_DATA_SOURCES_DUPS', $magic_item['SIMILAR_DATA_SOURCES_DUPS'], $vdef_string); 1987 1988 /* make the initial 'virtual' vdef name */ 1989 $vdef_graph_defs .= 'VDEF:vdef' . generate_graph_def_name(strval($i)) . '='; 1990 $vdef_graph_defs .= cacti_escapeshellarg(sanitize_cdef($vdef_string)); 1991 $vdef_graph_defs .= " \\\n"; 1992 1993 /* the VDEF cache is so we do not create duplicate VDEFs on a graph, 1994 * but take info account, that same VDEF may use different CDEFs 1995 * so index over VDEF_ID, CDEF_ID per DATA_TEMPLATE_RRD_ID, lvm */ 1996 $vdef_cache[$graph_item['vdef_id']][$graph_item['cdef_id']][$graph_item['data_template_rrd_id']][$cf_id] = $i; 1997 } 1998 1999 /* add the cdef string to the end of the def string */ 2000 $graph_defs .= $vdef_graph_defs; 2001 2002 /* +++++++++++++++++++++++ GRAPH ITEMS: VDEFs END +++++++++++++++++++++++ */ 2003 2004 /* note the current item_id for easy access */ 2005 $graph_item_id = $graph_item['graph_templates_item_id']; 2006 2007 /* we put this in a variable so it can be manipulated before mainly used 2008 if we want to skip it, like below */ 2009 $current_graph_item_type = $graph_item_types[$graph_item['graph_type_id']]; 2010 2011 /* IF this graph item has a data source... get a DEF name for it, or the cdef if that applies 2012 to this graph item */ 2013 if ($graph_item['cdef_id'] == '0') { 2014 if (isset($cf_ds_cache[$graph_item['data_template_rrd_id']][$cf_id])) { 2015 $data_source_name = generate_graph_def_name(strval($cf_ds_cache[$graph_item['data_template_rrd_id']][$cf_id])); 2016 } else { 2017 $data_source_name = ''; 2018 } 2019 } else { 2020 $data_source_name = 'cdef' . generate_graph_def_name(strval($cdef_cache[$graph_item['cdef_id']][$graph_item['data_template_rrd_id']][$cf_id])); 2021 } 2022 2023 /* IF this graph item has a data source... get a DEF name for it, or the vdef if that applies 2024 to this graph item */ 2025 if ($graph_item['vdef_id'] == '0') { 2026 /* do not overwrite $data_source_name that stems from cdef above */ 2027 } else { 2028 $data_source_name = 'vdef' . generate_graph_def_name(strval($vdef_cache[$graph_item['vdef_id']][$graph_item['cdef_id']][$graph_item['data_template_rrd_id']][$cf_id])); 2029 } 2030 2031 /* to make things easier... if there is no text format set; set blank text */ 2032 if (!isset($graph_variables['text_format'][$graph_item_id])) { 2033 $graph_variables['text_format'][$graph_item_id] = ''; 2034 } 2035 2036 if (!isset($hardreturn[$graph_item_id])) { 2037 $hardreturn[$graph_item_id] = ''; 2038 } 2039 2040 /* +++++++++++++++++++++++ GRAPH ITEMS +++++++++++++++++++++++ */ 2041 2042 /* most of the calculations have been done above. now we have for print everything out 2043 in an RRDtool-friendly fashion */ 2044 2045 $need_rrd_nl = true; 2046 2047 /* initialize color support */ 2048 $graph_item_color_code = ''; 2049 if (!empty($graph_item['hex'])) { 2050 $graph_item_color_code = '#' . $graph_item['hex']; 2051 $graph_item_color_code .= $graph_item['alpha']; 2052 } 2053 2054 /* initialize dash support */ 2055 $dash = ''; 2056 if ($graph_item['graph_type_id'] == GRAPH_ITEM_TYPE_LINE1 || 2057 $graph_item['graph_type_id'] == GRAPH_ITEM_TYPE_LINE2 || 2058 $graph_item['graph_type_id'] == GRAPH_ITEM_TYPE_LINE3 || 2059 $graph_item['graph_type_id'] == GRAPH_ITEM_TYPE_LINESTACK || 2060 $graph_item['graph_type_id'] == GRAPH_ITEM_TYPE_HRULE || 2061 $graph_item['graph_type_id'] == GRAPH_ITEM_TYPE_VRULE) { 2062 if (!empty($graph_item['dashes'])) { 2063 $dash .= ':dashes=' . $graph_item['dashes']; 2064 } 2065 2066 if (!empty($graph_item['dash_offset'])) { 2067 $dash .= ':dash-offset=' . $graph_item['dash_offset']; 2068 } 2069 } 2070 2071 if (!isset($graph_data_array['export_csv'])) { 2072 switch($graph_item['graph_type_id']) { 2073 case GRAPH_ITEM_TYPE_COMMENT: 2074 if (!isset($graph_data_array['graph_nolegend'])) { 2075 $comments = array(); 2076 2077 $comment_arg = rrd_substitute_host_query_data($graph_variables['text_format'][$graph_item_id], $graph, $graph_item); 2078 2079 // Check for a wrapping comment 2080 $max = read_config_option('max_title_length') - 20; 2081 if (strlen($comment_arg) > $max) { 2082 $comments = explode("\n", wordwrap($comment_arg, $max)); 2083 }else{ 2084 $comments[] = $comment_arg; 2085 } 2086 2087 foreach($comments as $comment) { 2088 # next, compute the argument of the COMMENT statement and perform injection counter measures 2089 if (trim($comment) == '') { # an empty COMMENT must be treated with care 2090 $comment = cacti_escapeshellarg(' ' . $hardreturn[$graph_item_id]); 2091 } else { 2092 $comment = cacti_escapeshellarg(rrdtool_escape_string(html_escape($comment)) . $hardreturn[$graph_item_id]); 2093 } 2094 2095 # create rrdtool specific command line 2096 $txt_graph_items .= $graph_item_types[$graph_item['graph_type_id']] . ':' . $comment . ' '; 2097 } 2098 } 2099 2100 break; 2101 case GRAPH_ITEM_TYPE_TEXTALIGN: 2102 if (!isset($graph_data_array['graph_nolegend'])) { 2103 if (!empty($graph_item['textalign'])) { 2104 $txt_graph_items .= $graph_item_types[$graph_item['graph_type_id']] . ':' . $graph_item['textalign']; 2105 } 2106 } 2107 2108 break; 2109 case GRAPH_ITEM_TYPE_GPRINT: 2110 $text_format = rrdtool_escape_string(html_escape($graph_variables['text_format'][$graph_item_id]), false); 2111 2112 if ($graph_item['vdef_id'] == '0') { 2113 $txt_graph_items .= $graph_item_types[$graph_item['graph_type_id']] . ':' . $data_source_name . ':' . $consolidation_functions[$graph_item['consolidation_function_id']] . ':' . cacti_escapeshellarg($text_format . $graph_item['gprint_text'] . $hardreturn[$graph_item_id]) . ' '; 2114 } else { 2115 $txt_graph_items .= $graph_item_types[$graph_item['graph_type_id']] . ':' . $data_source_name . ':' . cacti_escapeshellarg($text_format . $graph_item['gprint_text'] . $hardreturn[$graph_item_id]) . ' '; 2116 } 2117 2118 break; 2119 case GRAPH_ITEM_TYPE_GPRINT_AVERAGE: 2120 if (!isset($graph_data_array['graph_nolegend'])) { 2121 $text_format = rrdtool_escape_string(html_escape($graph_variables['text_format'][$graph_item_id])); 2122 2123 if ($graph_item['vdef_id'] == '0') { 2124 $txt_graph_items .= 'GPRINT:' . $data_source_name . ':AVERAGE:' . cacti_escapeshellarg($text_format . $graph_item['gprint_text'] . $hardreturn[$graph_item_id]) . ' '; 2125 } else { 2126 $txt_graph_items .= 'GPRINT:' . $data_source_name . ':' . cacti_escapeshellarg($text_format . $graph_item['gprint_text'] . $hardreturn[$graph_item_id]) . ' '; 2127 } 2128 } 2129 2130 break; 2131 case GRAPH_ITEM_TYPE_GPRINT_LAST: 2132 if (!isset($graph_data_array['graph_nolegend'])) { 2133 $text_format = rrdtool_escape_string(html_escape($graph_variables['text_format'][$graph_item_id])); 2134 2135 if ($graph_item['vdef_id'] == '0') { 2136 $txt_graph_items .= 'GPRINT:' . $data_source_name . ':LAST:' . cacti_escapeshellarg($text_format . $graph_item['gprint_text'] . $hardreturn[$graph_item_id]) . ' '; 2137 } else { 2138 $txt_graph_items .= 'GPRINT:' . $data_source_name . ':' . cacti_escapeshellarg($text_format . $graph_item['gprint_text'] . $hardreturn[$graph_item_id]) . ' '; 2139 } 2140 } 2141 2142 break; 2143 case GRAPH_ITEM_TYPE_GPRINT_MAX: 2144 if (!isset($graph_data_array['graph_nolegend'])) { 2145 $text_format = rrdtool_escape_string(html_escape($graph_variables['text_format'][$graph_item_id])); 2146 2147 if ($graph_item['vdef_id'] == '0') { 2148 $txt_graph_items .= 'GPRINT:' . $data_source_name . ':MAX:' . cacti_escapeshellarg($text_format . $graph_item['gprint_text'] . $hardreturn[$graph_item_id]) . ' '; 2149 } else { 2150 $txt_graph_items .= 'GPRINT:' . $data_source_name . ':' . cacti_escapeshellarg($text_format . $graph_item['gprint_text'] . $hardreturn[$graph_item_id]) . ' '; 2151 } 2152 } 2153 2154 break; 2155 case GRAPH_ITEM_TYPE_GPRINT_MIN: 2156 if (!isset($graph_data_array['graph_nolegend'])) { 2157 $text_format = rrdtool_escape_string(html_escape($graph_variables['text_format'][$graph_item_id])); 2158 2159 if ($graph_item['vdef_id'] == '0') { 2160 $txt_graph_items .= 'GPRINT:' . $data_source_name . ':MIN:' . cacti_escapeshellarg($text_format . $graph_item['gprint_text'] . $hardreturn[$graph_item_id]) . ' '; 2161 } else { 2162 $txt_graph_items .= 'GPRINT:' . $data_source_name . ':' . cacti_escapeshellarg($text_format . $graph_item['gprint_text'] . $hardreturn[$graph_item_id]) . ' '; 2163 } 2164 } 2165 2166 break; 2167 case GRAPH_ITEM_TYPE_AREA: 2168 $text_format = rrdtool_escape_string(html_escape($graph_variables['text_format'][$graph_item_id] != '' ? str_pad($graph_variables['text_format'][$graph_item_id], $pad_number):'')); 2169 2170 $txt_graph_items .= $graph_item_types[$graph_item['graph_type_id']] . ':' . $data_source_name . $graph_item_color_code . ':' . cacti_escapeshellarg($text_format . $hardreturn[$graph_item_id]) . ' '; 2171 2172 if ($graph_item['shift'] == CHECKED && abs($graph_item['value']) > 0) { # create a SHIFT statement 2173 $txt_graph_items .= RRD_NL . 'SHIFT:' . $data_source_name . ':' . $graph_item['value']; 2174 } 2175 2176 break; 2177 case GRAPH_ITEM_TYPE_STACK: 2178 $text_format = rrdtool_escape_string(html_escape($graph_variables['text_format'][$graph_item_id] != '' ? str_pad($graph_variables['text_format'][$graph_item_id],$pad_number):'')); 2179 2180 $txt_graph_items .= 'AREA:' . $data_source_name . $graph_item_color_code . ':' . cacti_escapeshellarg($text_format . $hardreturn[$graph_item_id]) . ':STACK'; 2181 2182 if ($graph_item['shift'] == CHECKED && $graph_item['value'] > 0) { # create a SHIFT statement 2183 $txt_graph_items .= RRD_NL . 'SHIFT:' . $data_source_name . ':' . $graph_item['value']; 2184 } 2185 2186 break; 2187 case GRAPH_ITEM_TYPE_LINE1: 2188 case GRAPH_ITEM_TYPE_LINE2: 2189 case GRAPH_ITEM_TYPE_LINE3: 2190 $text_format = rrdtool_escape_string(html_escape($graph_variables['text_format'][$graph_item_id] != '' ? str_pad($graph_variables['text_format'][$graph_item_id], $pad_number):'')); 2191 2192 $txt_graph_items .= $graph_item_types[$graph_item['graph_type_id']] . ':' . $data_source_name . $graph_item_color_code . ':' . cacti_escapeshellarg($text_format . $hardreturn[$graph_item_id]) . ' '; 2193 2194 if ($graph_item['shift'] == CHECKED && $graph_item['value'] > 0) { # create a SHIFT statement 2195 $txt_graph_items .= RRD_NL . 'SHIFT:' . $data_source_name . ':' . $graph_item['value']; 2196 } 2197 2198 break; 2199 case GRAPH_ITEM_TYPE_LINESTACK: 2200 $text_format = rrdtool_escape_string(html_escape($graph_variables['text_format'][$graph_item_id] != '' ? str_pad($graph_variables['text_format'][$graph_item_id], $pad_number):'')); 2201 2202 $txt_graph_items .= 'LINE' . $graph_item['line_width'] . ':' . $data_source_name . $graph_item_color_code . ':' . cacti_escapeshellarg($text_format . $hardreturn[$graph_item_id]) . ':STACK' . $dash; 2203 2204 if ($graph_item['shift'] == CHECKED && $graph_item['value'] > 0) { # create a SHIFT statement 2205 $txt_graph_items .= RRD_NL . 'SHIFT:' . $data_source_name . ':' . $graph_item['value']; 2206 } 2207 2208 break; 2209 case GRAPH_ITEM_TYPE_TIC: 2210 $_fraction = (empty($graph_item['graph_type_id']) ? '' : (':' . $graph_item['value'])); 2211 $_legend = ':' . cacti_escapeshellarg(rrdtool_escape_string(html_escape($graph_variables['text_format'][$graph_item_id])) . $hardreturn[$graph_item_id]); 2212 $txt_graph_items .= $graph_item_types[$graph_item['graph_type_id']] . ':' . $data_source_name . $graph_item_color_code . $_fraction . $_legend; 2213 2214 break; 2215 case GRAPH_ITEM_TYPE_HRULE: 2216 /* perform variable substitution; if this does not return a number, rrdtool will FAIL! */ 2217 $substitute = strip_alpha(rrd_substitute_host_query_data($graph_variables['value'][$graph_item_id], $graph, $graph_item)); 2218 2219 $text_format = rrdtool_escape_string(html_escape(rrd_substitute_host_query_data($graph_variables['text_format'][$graph_item_id], $graph, $graph_item))); 2220 2221 /* don't break rrdtool if the strip_alpha() returns false */ 2222 if ($substitute !== false) { 2223 $graph_variables['value'][$graph_item_id] = $substitute; 2224 } else { 2225 $graph_variables['value'][$graph_item_id] = '0'; 2226 } 2227 2228 $txt_graph_items .= $graph_item_types[$graph_item['graph_type_id']] . ':' . $graph_variables['value'][$graph_item_id] . $graph_item_color_code . ':' . cacti_escapeshellarg($text_format . $hardreturn[$graph_item_id]) . '' . $dash; 2229 2230 break; 2231 case GRAPH_ITEM_TYPE_VRULE: 2232 if (substr_count($graph_item['value'], ':')) { 2233 $value_array = explode(':', $graph_item['value']); 2234 2235 if ($value_array[0] < 0) { 2236 $value = date('U') - (-3600 * $value_array[0]) - 60 * $value_array[1]; 2237 } else { 2238 $value = date('U', mktime($value_array[0],$value_array[1],0)); 2239 } 2240 2241 $txt_graph_items .= $graph_item_types[$graph_item['graph_type_id']] . ':' . $value . $graph_item_color_code . ':' . cacti_escapeshellarg(rrdtool_escape_string(html_escape($graph_variables['text_format'][$graph_item_id])) . $hardreturn[$graph_item_id]) . $dash; 2242 } elseif (is_numeric($graph_item['value'])) { 2243 $value = $graph_item['value']; 2244 2245 $txt_graph_items .= $graph_item_types[$graph_item['graph_type_id']] . ':' . $value . $graph_item_color_code . ':' . cacti_escapeshellarg(rrdtool_escape_string(html_escape($graph_variables['text_format'][$graph_item_id])) . $hardreturn[$graph_item_id]) . $dash; 2246 } 2247 2248 break; 2249 default: 2250 $need_rrd_nl = false; 2251 } 2252 } else { 2253 if (preg_match('/^(AREA|AREA:STACK|LINE[123]|STACK)$/', $graph_item_types[$graph_item['graph_type_id']])) { 2254 /* give all export items a name */ 2255 if (trim($graph_variables['text_format'][$graph_item_id]) == '') { 2256 $legend_name = 'col' . $j . '-' . $data_source_name; 2257 } else { 2258 $legend_name = $graph_variables['text_format'][$graph_item_id]; 2259 } 2260 $stacked_columns['col' . $j] = ($graph_item_types[$graph_item['graph_type_id']] == 'STACK') ? 1 : 0; 2261 $j++; 2262 2263 $txt_graph_items .= 'XPORT:' . cacti_escapeshellarg($data_source_name) . ':' . str_replace(':', '', cacti_escapeshellarg($legend_name)) ; 2264 } else { 2265 $need_rrd_nl = false; 2266 } 2267 } 2268 2269 $i++; 2270 2271 if (($i < cacti_sizeof($graph_items)) && ($need_rrd_nl)) { 2272 $txt_graph_items .= RRD_NL; 2273 } 2274 } 2275 } 2276 2277 if (!isset($graph_data_array['export_csv']) || $graph_data_array['export_csv'] != true) { 2278 $graph_array = api_plugin_hook_function('rrd_graph_graph_options', array('graph_opts' => $graph_opts, 'graph_defs' => $graph_defs, 'txt_graph_items' => $txt_graph_items, 'graph_id' => $local_graph_id, 'start' => $graph_start, 'end' => $graph_end)); 2279 2280 if (!empty($graph_array)) { 2281 $graph_defs = $graph_array['graph_defs']; 2282 $txt_graph_items = $graph_array['txt_graph_items']; 2283 $graph_opts = $graph_array['graph_opts']; 2284 } 2285 2286 /* either print out the source or pass the source onto rrdtool to get us a nice PNG */ 2287 if (isset($graph_data_array['print_source'])) { 2288 $source_command_line = read_config_option('path_rrdtool') . ' graph ' . $graph_opts . $graph_defs . $txt_graph_items; 2289 $source_command_line_lengths = strlen(str_replace("\\\n", ' ', $source_command_line)); 2290 print '<PRE>' . html_escape($source_command_line) . '</PRE>'; 2291 print '<span class="textInfo">' . 'RRDtool Command lengths = ' . $source_command_line_lengths . ' charaters.</span><br>'; 2292 if ( $config['cacti_server_os'] == 'win32' && $source_command_line_lengths > 8191 ) { 2293 print '<PRE>' . 'Warning: The Cacti OS is Windows system, RRDtool Command lengths should not exceed 8191 charaters.' . '</PRE>'; 2294 } 2295 } else { 2296 if (isset($graph_data_array['graphv'])) { 2297 $graph = 'graphv'; 2298 } else { 2299 $graph = 'graph'; 2300 } 2301 2302 if (isset($graph_data_array['get_error'])) { 2303 return rrdtool_execute("graph $graph_opts$graph_defs$txt_graph_items", false, RRDTOOL_OUTPUT_STDERR); 2304 } elseif (isset($graph_data_array['export'])) { 2305 rrdtool_execute("graph $graph_opts$graph_defs$txt_graph_items", false, RRDTOOL_OUTPUT_NULL, $rrdtool_pipe); 2306 2307 return 0; 2308 } elseif (isset($graph_data_array['export_realtime'])) { 2309 $output_flag = RRDTOOL_OUTPUT_GRAPH_DATA; 2310 $output = rrdtool_execute("graph $graph_opts$graph_defs$txt_graph_items", false, $output_flag, $rrdtool_pipe); 2311 2312 if ($fp = fopen($graph_data_array['export_realtime'], 'w')) { 2313 fwrite($fp, $output, strlen($output)); 2314 fclose($fp); 2315 chmod($graph_data_array['export_realtime'], 0644); 2316 } 2317 2318 return $output; 2319 } else { 2320 $graph_data_array = boost_prep_graph_array($graph_data_array); 2321 2322 if (!isset($graph_data_array['output_flag'])) { 2323 $output_flag = RRDTOOL_OUTPUT_GRAPH_DATA; 2324 } else { 2325 $output_flag = $graph_data_array['output_flag']; 2326 } 2327 2328 $output = rrdtool_execute("$graph $graph_opts$graph_defs$txt_graph_items", false, $output_flag, $rrdtool_pipe); 2329 2330 boost_graph_set_file($output, $local_graph_id, $rra_id); 2331 2332 return $output; 2333 } 2334 } 2335 } else { 2336 $output_flag = RRDTOOL_OUTPUT_STDOUT; 2337 2338 $xport_array = rrdxport2array(rrdtool_execute("xport $graph_opts$graph_defs$txt_graph_items", false, $output_flag, $rrdtool_pipe)); 2339 2340 /* add host and graph information */ 2341 $xport_array['meta']['stacked_columns']= $stacked_columns; 2342 $xport_array['meta']['title_cache'] = $graph['title_cache']; 2343 $xport_array['meta']['vertical_label'] = $graph['vertical_label']; 2344 $xport_array['meta']['local_graph_id'] = $local_graph_id; 2345 $xport_array['meta']['host_id'] = $graph['host_id']; 2346 2347 return $xport_array; 2348 } 2349} 2350 2351function rrdtool_escape_string($text, $ignore_percent = true) { 2352 if ($ignore_percent) { 2353 return str_replace(array('"', ':'), array('\"', '\:'), $text); 2354 } else { 2355 return str_replace(array('"', ':', '%'), array('\"', '\:', '%%'), $text); 2356 } 2357} 2358 2359function rrdtool_function_xport($local_graph_id, $rra_id, $xport_data_array, &$xport_meta, $user = 0) { 2360 return rrdtool_function_graph($local_graph_id, $rra_id, $xport_data_array, null, $xport_meta, $user); 2361} 2362 2363function rrdtool_function_format_graph_date(&$graph_data_array) { 2364 global $datechar; 2365 2366 $graph_legend = ''; 2367 /* setup date format */ 2368 $date_fmt = read_user_setting('default_date_format',read_config_option('default_date_format')); 2369 $dateCharSetting = read_user_setting('default_datechar',read_config_option('default_datechar')); 2370 $datecharacter = $datechar[$dateCharSetting]; 2371 2372 switch ($date_fmt) { 2373 case GD_MO_D_Y: 2374 $graph_date = 'm' . $datecharacter . 'd' . $datecharacter . 'Y H:i:s'; 2375 break; 2376 case GD_MN_D_Y: 2377 $graph_date = 'M' . $datecharacter . 'd' . $datecharacter . 'Y H:i:s'; 2378 break; 2379 case GD_D_MO_Y: 2380 $graph_date = 'd' . $datecharacter . 'm' . $datecharacter . 'Y H:i:s'; 2381 break; 2382 case GD_D_MN_Y: 2383 $graph_date = 'd' . $datecharacter . 'M' . $datecharacter . 'Y H:i:s'; 2384 break; 2385 case GD_Y_MO_D: 2386 $graph_date = 'Y' . $datecharacter . 'm' . $datecharacter . 'd H:i:s'; 2387 break; 2388 case GD_Y_MN_D: 2389 $graph_date = 'Y' . $datecharacter . 'M' . $datecharacter . 'd H:i:s'; 2390 break; 2391 } 2392 2393 /* display the timespan for zoomed graphs */ 2394 if ((isset($graph_data_array['graph_start'])) && (isset($graph_data_array['graph_end']))) { 2395 if (($graph_data_array['graph_start'] < 0) && ($graph_data_array['graph_end'] < 0)) { 2396 $graph_legend = "COMMENT:\"From " . str_replace(':', '\:', date($graph_date, time()+$graph_data_array['graph_start'])) . ' To ' . str_replace(':', '\:', date($graph_date, time()+$graph_data_array['graph_end'])) . "\\c\"" . RRD_NL . "COMMENT:\" \\n\"" . RRD_NL; 2397 } elseif (($graph_data_array['graph_start'] >= 0) && ($graph_data_array['graph_end'] >= 0)) { 2398 $graph_legend = "COMMENT:\"From " . str_replace(':', '\:', date($graph_date, $graph_data_array['graph_start'])) . ' To ' . str_replace(':', '\:', date($graph_date, $graph_data_array['graph_end'])) . "\\c\"" . RRD_NL . "COMMENT:\" \\n\"" . RRD_NL; 2399 } 2400 } 2401 2402 return $graph_legend; 2403} 2404 2405function rrdtool_function_theme_font_options(&$graph_data_array) { 2406 global $config; 2407 2408 /* implement theme colors */ 2409 $graph_opts = ''; 2410 $themefonts = array(); 2411 2412 if (isset($graph_data_array['graph_theme'])) { 2413 $rrdtheme = $config['base_path'] . '/include/themes/' . $graph_data_array['graph_theme'] . '/rrdtheme.php'; 2414 } else { 2415 $rrdtheme = $config['base_path'] . '/include/themes/' . get_selected_theme() . '/rrdtheme.php'; 2416 } 2417 2418 if (file_exists($rrdtheme) && is_readable($rrdtheme)) { 2419 $rrdversion = get_rrdtool_version(); 2420 include($rrdtheme); 2421 2422 if (isset($rrdcolors)) { 2423 foreach($rrdcolors as $colortag => $color) { 2424 $graph_opts .= '--color ' . strtoupper($colortag) . '#' . strtoupper($color) . RRD_NL; 2425 } 2426 } 2427 2428 if (isset($rrdborder) && cacti_version_compare($rrdversion,'1.4','>=')) { 2429 $graph_opts .= "--border $rrdborder " ; 2430 } 2431 2432 if (isset($rrdfonts)) { 2433 $themefonts = $rrdfonts; 2434 } 2435 } 2436 2437 /* title fonts */ 2438 $graph_opts .= rrdtool_function_set_font('title', ((!empty($graph_data_array['graph_nolegend'])) ? $graph_data_array['graph_nolegend'] : ''), $themefonts); 2439 2440 /* axis fonts */ 2441 $graph_opts .= rrdtool_function_set_font('axis', '', $themefonts); 2442 2443 /* legend fonts */ 2444 $graph_opts .= rrdtool_function_set_font('legend', '', $themefonts); 2445 2446 /* unit fonts */ 2447 $graph_opts .= rrdtool_function_set_font('unit', '', $themefonts); 2448 2449 /* watermark fonts */ 2450 if (isset($rrdversion) && cacti_version_compare($rrdversion,'1.3','>')) { 2451 $graph_opts .= rrdtool_function_set_font('watermark', '', $themefonts); 2452 } 2453 2454 return $graph_opts; 2455} 2456 2457function rrdtool_set_font($type, $no_legend = '', $themefonts = array()) { 2458 return rrdtool_function_set_font($type, $no_legend, $themefonts); 2459} 2460 2461function rrdtool_function_set_font($type, $no_legend, $themefonts) { 2462 global $config; 2463 2464 if (read_config_option('font_method') == 0) { 2465 if (read_user_setting('custom_fonts') == 'on') { 2466 $font = read_user_setting($type . '_font'); 2467 $size = read_user_setting($type . '_size'); 2468 } else { 2469 $font = read_config_option($type . '_font'); 2470 $size = read_config_option($type . '_size'); 2471 } 2472 } elseif (isset($themefonts[$type]['font']) && isset($themefonts[$type]['size'])) { 2473 $font = $themefonts[$type]['font']; 2474 $size = $themefonts[$type]['size']; 2475 } else { 2476 return; 2477 } 2478 2479 if ($font != '') { 2480 /* verifying all possible pango font params is too complex to be tested here 2481 * so we only escape the font 2482 */ 2483 $font = cacti_escapeshellarg($font); 2484 } 2485 2486 if ($type == 'title') { 2487 if (!empty($no_legend)) { 2488 $size = $size * .70; 2489 } elseif (($size <= 4) || !is_numeric($size)) { 2490 $size = 12; 2491 } 2492 } elseif (($size <= 4) || !is_numeric($size)) { 2493 $size = 8; 2494 } 2495 2496 return '--font ' . strtoupper($type) . ':' . floatval($size) . ':' . $font . RRD_NL; 2497} 2498 2499function rrd_substitute_host_query_data($txt_graph_item, $graph, $graph_item) { 2500 /* replace host variables in graph elements */ 2501 $host_id = 0; 2502 2503 if (empty($graph['host_id'])) { 2504 /* if graph has no associated host determine host_id from graph item data source */ 2505 if (isset($graph_item['local_data_id']) && !empty($graph_item['local_data_id'])) { 2506 $host_id = db_fetch_cell_prepared('SELECT host_id 2507 FROM data_local 2508 WHERE id = ?', 2509 array($graph_item['local_data_id'])); 2510 } 2511 } else { 2512 $host_id = $graph['host_id']; 2513 } 2514 2515 $txt_graph_item = substitute_host_data($txt_graph_item, '|', '|', $host_id); 2516 2517 /* replace query variables in graph elements */ 2518 if (strpos($txt_graph_item, '|query_') !== false){ 2519 if(isset($graph_item['snmp_query_id'])) { 2520 $txt_graph_item = substitute_snmp_query_data($txt_graph_item, $host_id, $graph_item['snmp_query_id'], $graph_item['snmp_index']); 2521 } else if (isset($graph['snmp_query_id'])) { 2522 $txt_graph_item = substitute_snmp_query_data($txt_graph_item, $host_id, $graph['snmp_query_id'], $graph['snmp_index']); 2523 } 2524 } 2525 2526 /* replace query variables in graph elements */ 2527 if (strpos($txt_graph_item, '|input_') !== false && isset($graph_item['local_data_id'])) { 2528 return substitute_data_input_data($txt_graph_item, $graph, $graph_item['local_data_id']); 2529 } else { 2530 return $txt_graph_item; 2531 } 2532} 2533 2534function rrdtool_function_get_resstep($local_data_ids, $graph_start, $graph_end, $type = 'res') { 2535 if (!is_array($local_data_ids)) { 2536 $local_data_ids = array($local_data_ids); 2537 } 2538 2539 $time = time(); 2540 2541 if ($graph_start < 0) { 2542 $graph_start = $time + $graph_start; 2543 } 2544 2545 if ($graph_end < 0) { 2546 $graph_end = $time + $graph_end; 2547 } 2548 2549 if (cacti_sizeof($local_data_ids)) { 2550 foreach($local_data_ids as $local_data_id) { 2551 $data_source_info = db_fetch_assoc_prepared('SELECT dsp.step, dspr.steps, dspr.rows, dspr.timespan 2552 FROM data_source_profiles AS dsp 2553 INNER JOIN data_source_profiles_rra AS dspr 2554 ON dspr.data_source_profile_id=dsp.id 2555 INNER JOIN data_template_data AS dtd 2556 ON dtd.data_source_profile_id=dsp.id 2557 WHERE dtd.local_data_id = ? 2558 ORDER BY step, steps ASC', 2559 array($local_data_id)); 2560 2561 if (cacti_sizeof($data_source_info)) { 2562 foreach($data_source_info as $resolution) { 2563 if ($graph_start > ($time - ($resolution['step'] * $resolution['steps'] * $resolution['rows']))) { 2564 if ($type == 'res') { 2565 return $resolution['step'] * $resolution['steps']; 2566 } else { 2567 return $resolution['step']; 2568 } 2569 } 2570 } 2571 } 2572 } 2573 } 2574 2575 return 0; 2576} 2577 2578/** given a data source id, return rrdtool info array 2579 * @param $local_data_id - data source id 2580 * @return - (array) an array containing all data from rrdtool info command 2581 */ 2582function rrdtool_function_info($local_data_id) { 2583 /* Get the path to rrdtool file */ 2584 $data_source_path = get_data_source_path($local_data_id, true); 2585 2586 /* Execute rrdtool info command */ 2587 $cmd_line = ' info ' . $data_source_path; 2588 $output = rrdtool_execute($cmd_line, RRDTOOL_OUTPUT_NULL, RRDTOOL_OUTPUT_STDOUT); 2589 if ($output == '') { 2590 return false; 2591 } 2592 2593 /* Hack for i18n */ 2594 if (strpos($output, ',') !== false) { 2595 $output = str_replace(',', '.', $output); 2596 } 2597 2598 /* Parse the output */ 2599 $matches = array(); 2600 $rrd_info = array('rra' => array(), 'ds' => array()); 2601 $output = explode("\n", $output); 2602 2603 foreach ($output as $line) { 2604 $line = trim($line); 2605 if (preg_match('/^ds\[(\S+)\]\.(\S+) = (\S+)$/', $line, $matches)) { 2606 $rrd_info['ds'][$matches[1]][$matches[2]] = trim($matches[3], '"'); 2607 } elseif (preg_match('/^rra\[(\S+)\]\.(\S+)\[(\S+)\]\.(\S+) = (\S+)$/', $line, $matches)) { 2608 $rrd_info['rra'][$matches[1]][$matches[2]][$matches[3]][$matches[4]] = trim($matches[5], '"'); 2609 } elseif (preg_match('/^rra\[(\S+)\]\.(\S+) = (\S+)$/', $line, $matches)) { 2610 $rrd_info['rra'][$matches[1]][$matches[2]] = trim($matches[3], '"'); 2611 } elseif (preg_match("/^(\S+) = \"(\S+)\"$/", $line, $matches)) { 2612 $rrd_info[$matches[1]] = trim($matches[2], '"'); 2613 } elseif (preg_match('/^(\S+) = (\S+)$/', $line, $matches)) { 2614 $rrd_info[$matches[1]] = trim($matches[2], '"'); 2615 } 2616 } 2617 2618 $output = ''; 2619 $matches = array(); 2620 2621 /* Return parsed values */ 2622 return $rrd_info; 2623} 2624 2625/** rrdtool_function_contains_cf verifies if the RRDfile contains the 'MAX' consolidation function 2626 * @param $local_data_id the id of the data source 2627 * @param $cf the consolidation function to search for 2628 * @return boolean true or false depending on the result 2629 */ 2630function rrdtool_function_contains_cf($local_data_id, $cf) { 2631 $info = rrdtool_function_info($local_data_id); 2632 2633 if (cacti_sizeof($info)) { 2634 if (isset($info['rra'])) { 2635 foreach($info['rra'] as $ds) { 2636 if ($ds['cf'] == $cf) { 2637 return true; 2638 } 2639 } 2640 } 2641 } 2642 2643 return false; 2644} 2645 2646/** rrdtool_cacti_compare compares cacti information to rrd file information 2647 * @param $data_source_id the id of the data source 2648 * @param $info rrdtool info as an array 2649 * @return array build like $info defining html class in case of error 2650 */ 2651function rrdtool_cacti_compare($data_source_id, &$info) { 2652 global $data_source_types, $consolidation_functions; 2653 2654 /* get cacti header information for given data source id */ 2655 $cacti_header_array = db_fetch_row_prepared('SELECT 2656 local_data_template_data_id, rrd_step, data_source_profile_id 2657 FROM data_template_data 2658 WHERE local_data_id = ?', 2659 array($data_source_id)); 2660 2661 /* get cacti DS information */ 2662 $cacti_ds_array = db_fetch_assoc_prepared('SELECT data_source_name, data_source_type_id, 2663 rrd_heartbeat, rrd_maximum, rrd_minimum 2664 FROM data_template_rrd 2665 WHERE local_data_id = ?', 2666 array($data_source_id)); 2667 2668 /* get cacti RRA information */ 2669 $cacti_rra_array = db_fetch_assoc_prepared('SELECT 2670 dspc.consolidation_function_id AS cf, 2671 dsp.x_files_factor AS xff, 2672 dspr.steps AS steps, 2673 dspr.rows AS `rows` 2674 FROM data_source_profiles AS dsp 2675 INNER JOIN data_source_profiles_cf AS dspc 2676 ON dsp.id=dspc.data_source_profile_id 2677 INNER JOIN data_source_profiles_rra AS dspr 2678 ON dsp.id=dspr.data_source_profile_id 2679 WHERE dsp.id = ? 2680 ORDER BY dspc.consolidation_function_id, dspr.steps', 2681 array($cacti_header_array['data_source_profile_id'])); 2682 2683 $diff = array(); 2684 /* ----------------------------------------------------------------------------------- 2685 * header information 2686 -----------------------------------------------------------------------------------*/ 2687 if ($cacti_header_array['rrd_step'] != $info['step']) { 2688 $diff['step'] = __("Required RRD step size is '%s'", $cacti_header_array['rrd_step']); 2689 } 2690 2691 /* ----------------------------------------------------------------------------------- 2692 * data source information 2693 -----------------------------------------------------------------------------------*/ 2694 if (cacti_sizeof($cacti_ds_array) > 0) { 2695 $data_local = db_fetch_row_prepared('SELECT host_id, 2696 snmp_query_id, snmp_index 2697 FROM data_local 2698 WHERE id = ?', 2699 array($data_source_id) 2700 ); 2701 2702 $speed = rrdtool_function_interface_speed($data_local); 2703 2704 foreach ($cacti_ds_array as $key => $data_source) { 2705 $ds_name = $data_source['data_source_name']; 2706 2707 /* try to print matching rrd file's ds information */ 2708 if (isset($info['ds'][$ds_name]) ) { 2709 if (!isset($info['ds'][$ds_name]['seen'])) { 2710 $info['ds'][$ds_name]['seen'] = true; 2711 } else { 2712 continue; 2713 } 2714 2715 $ds_type = trim($info['ds'][$ds_name]['type'], '"'); 2716 if ($data_source_types[$data_source['data_source_type_id']] != $ds_type) { 2717 $diff['ds'][$ds_name]['type'] = __("Type for Data Source '%s' should be '%s'", $ds_name, $data_source_types[$data_source['data_source_type_id']]); 2718 $diff['tune'][] = $info['filename'] . ' ' . '--data-source-type ' . $ds_name . ':' . $data_source_types[$data_source['data_source_type_id']]; 2719 } 2720 2721 if ($data_source['rrd_heartbeat'] != $info['ds'][$ds_name]['minimal_heartbeat']) { 2722 $diff['ds'][$ds_name]['minimal_heartbeat'] = __("Heartbeat for Data Source '%s' should be '%s'", $ds_name, $data_source['rrd_heartbeat']); 2723 $diff['tune'][] = $info['filename'] . ' ' . '--heartbeat ' . $ds_name . ':' . $data_source['rrd_heartbeat']; 2724 } 2725 2726 if ($data_source['rrd_minimum'] != $info['ds'][$ds_name]['min']) { 2727 if (($data_source['rrd_minimum'] == '0' || $data_source['rrd_maximum'] == 'U') && 2728 $info['ds'][$ds_name]['min'] == 'NaN') { 2729 $info['ds'][$ds_name]['min'] = 'U'; 2730 } 2731 } 2732 2733 if ($data_source['rrd_minimum'] != $info['ds'][$ds_name]['min']) { 2734 $diff['ds'][$ds_name]['min'] = __("RRD minimum for Data Source '%s' should be '%s'", $ds_name, $data_source['rrd_minimum']); 2735 $diff['tune'][] = $info['filename'] . ' ' . '--minimum ' . $ds_name . ':' . $data_source['rrd_minimum']; 2736 } 2737 2738 // Trim the max value 2739 $data_source['rrd_maximum'] = trim($data_source['rrd_maximum']); 2740 2741 if ($data_source['rrd_maximum'] != $info['ds'][$ds_name]['max']) { 2742 if ($data_source['rrd_maximum'] == '|query_ifSpeed|' || 2743 $data_source['rrd_maximum'] == '|query_ifHighSpeed|') { 2744 $data_source['rrd_maximum'] = $speed; 2745 } elseif (($data_source['rrd_maximum'] == '0' || $data_source['rrd_maximum'] == 'U') && 2746 $info['ds'][$ds_name]['max'] == 'NaN') { 2747 $info['ds'][$ds_name]['max'] = 'U'; 2748 } else { 2749 $data_source['rrd_maximum'] = substitute_snmp_query_data($data_source['rrd_maximum'], $data_local['host_id'], $data_local['snmp_query_id'], $data_local['snmp_index']); 2750 } 2751 } 2752 2753 if ($data_source['rrd_maximum'] != $info['ds'][$ds_name]['max']) { 2754 $diff['ds'][$ds_name]['max'] = __("RRD maximum for Data Source '%s' should be '%s'", $ds_name, $data_source['rrd_maximum']); 2755 $diff['tune'][] = $info['filename'] . ' ' . '--maximum ' . $ds_name . ':' . $data_source['rrd_maximum']; 2756 } 2757 } else { 2758 # cacti knows this ds, but the rrd file does not 2759 $info['ds'][$ds_name]['type'] = $data_source_types[$data_source['data_source_type_id']]; 2760 $info['ds'][$ds_name]['minimal_heartbeat'] = $data_source['rrd_heartbeat']; 2761 $info['ds'][$ds_name]['min'] = $data_source['rrd_minimum']; 2762 $info['ds'][$ds_name]['max'] = $data_source['rrd_maximum']; 2763 $info['ds'][$ds_name]['seen'] = true; 2764 $diff['ds'][$ds_name]['error'] = __("DS '%s' missing in RRDfile", $ds_name); 2765 } 2766 } 2767 } 2768 2769 /* print all data sources still known to the rrd file (no match to cacti ds will happen here) */ 2770 if (cacti_sizeof($info['ds']) > 0) { 2771 foreach ($info['ds'] as $ds_name => $data_source) { 2772 if (!isset($data_source['seen'])) { 2773 $diff['ds'][$ds_name]['error'] = __("DS '%s' missing in Cacti definition", $ds_name); 2774 } 2775 } 2776 } 2777 2778 /* ----------------------------------------------------------------------------------- 2779 * RRA information 2780 -----------------------------------------------------------------------------------*/ 2781 $resize = true; # assume a resize operation as long as no rra duplicates are found 2782 2783 /* scan cacti rra information for duplicates of (CF, STEPS) */ 2784 if (cacti_sizeof($cacti_rra_array) > 0) { 2785 for ($i=0; $i<= cacti_sizeof($cacti_rra_array)-1; $i++) { 2786 $cf = $cacti_rra_array[$i]['cf']; 2787 $steps = $cacti_rra_array[$i]['steps']; 2788 foreach($cacti_rra_array as $cacti_rra_id => $cacti_rra) { 2789 if ($cf == $cacti_rra['cf'] && $steps == $cacti_rra['steps'] && ($i != $cacti_rra_id)) { 2790 $diff['rra'][$i]['error'] = __("Cacti RRA '%s' has same CF/steps (%s, %s) as '%s'", $i, $consolidation_functions[$cf], $steps, $cacti_rra_id); 2791 $diff['rra'][$cacti_rra_id]['error'] = __("Cacti RRA '%s' has same CF/steps (%s, %s) as '%s'", $cacti_rra_id, $consolidation_functions[$cf], $steps, $i); 2792 $resize = false; 2793 } 2794 } 2795 } 2796 } 2797 2798 /* scan file rra information for duplicates of (CF, PDP_PER_ROWS) */ 2799 if (cacti_sizeof($info['rra']) > 0) { 2800 for ($i=0; $i<= cacti_sizeof($info['rra'])-1; $i++) { 2801 $cf = $info['rra'][$i]['cf']; 2802 $steps = $info['rra'][$i]['pdp_per_row']; 2803 foreach($info['rra'] as $file_rra_id => $file_rra) { 2804 if (($cf == $file_rra['cf']) && ($steps == $file_rra['pdp_per_row']) && ($i != $file_rra_id)) { 2805 $diff['rra'][$i]['error'] = __("File RRA '%s' has same CF/steps (%s, %s) as '%s'", $i, $cf, $steps, $file_rra_id); 2806 $diff['rra'][$file_rra_id]['error'] = __("File RRA '%s' has same CF/steps (%s, %s) as '%s'", $file_rra_id, $cf, $steps, $i); 2807 $resize = false; 2808 } 2809 } 2810 } 2811 } 2812 2813 /* print all RRAs known to cacti and add those from matching rrd file */ 2814 if (cacti_sizeof($cacti_rra_array) > 0) { 2815 foreach($cacti_rra_array as $cacti_rra_id => $cacti_rra) { 2816 /* find matching rra info from rrd file 2817 * do NOT assume, that rra sequence is kept ($cacti_rra_id != $file_rra_id may happen)! 2818 * Match is assumed, if CF and STEPS/PDP_PER_ROW match; so go for it */ 2819 foreach ($info['rra'] as $file_rra_id => $file_rra) { 2820 /* in case of mismatch, $file_rra['pdp_per_row'] might not be defined */ 2821 if (!isset($file_rra['pdp_per_row'])) { 2822 $file_rra['pdp_per_row'] = 0; 2823 } 2824 2825 if ($consolidation_functions[$cacti_rra['cf']] == trim($file_rra['cf'], '"') && 2826 $cacti_rra['steps'] == $file_rra['pdp_per_row']) { 2827 2828 if (isset($info['rra'][$file_rra_id]['seen'])) { 2829 continue; 2830 } 2831 2832 # mark both rra id's as seen to avoid printing them as non-matching 2833 $info['rra'][$file_rra_id]['seen'] = true; 2834 $cacti_rra_array[$cacti_rra_id]['seen'] = true; 2835 2836 if ($cacti_rra['xff'] != $file_rra['xff']) { 2837 $diff['rra'][$file_rra_id]['xff'] = __("XFF for cacti RRA id '%s' should be '%s'", $cacti_rra_id, $cacti_rra['xff']); 2838 } 2839 2840 if ($cacti_rra['rows'] != $file_rra['rows'] && $resize) { 2841 $diff['rra'][$file_rra_id]['rows'] = __("Number of rows for Cacti RRA id '%s' should be '%s'", $cacti_rra_id, $cacti_rra['rows']); 2842 if ($cacti_rra['rows'] > $file_rra['rows']) { 2843 $diff['resize'][] = $info['filename'] . ' ' . $cacti_rra_id . ' GROW ' . ($cacti_rra['rows'] - $file_rra['rows']); 2844 } else { 2845 $diff['resize'][] = $info['filename'] . ' ' . $cacti_rra_id . ' SHRINK ' . ($file_rra['rows'] - $cacti_rra['rows']); 2846 } 2847 } 2848 } 2849 } 2850 # if cacti knows an rra that has no match, consider this as an error 2851 if (!isset($cacti_rra_array[$cacti_rra_id]['seen'])) { 2852 # add to info array for printing, the index $cacti_rra_id has no real meaning 2853 $info['rra']['cacti_' . $cacti_rra_id]['cf'] = $consolidation_functions[$cacti_rra['cf']]; 2854 $info['rra']['cacti_' . $cacti_rra_id]['steps'] = $cacti_rra['steps']; 2855 $info['rra']['cacti_' . $cacti_rra_id]['xff'] = $cacti_rra['xff']; 2856 $info['rra']['cacti_' . $cacti_rra_id]['rows'] = $cacti_rra['rows']; 2857 $diff['rra']['cacti_' . $cacti_rra_id]['error'] = __("RRA '%s' missing in RRDfile", $cacti_rra_id); 2858 } 2859 } 2860 } 2861 2862 # if the rrd file has an rra that has no cacti match, consider this as an error 2863 if (cacti_sizeof($info['rra']) > 0) { 2864 foreach ($info['rra'] as $file_rra_id => $file_rra) { 2865 if (!isset($info['rra'][$file_rra_id]['seen'])) { 2866 $diff['rra'][$file_rra_id]['error'] = __("RRA '%s' missing in Cacti definition", $file_rra_id); 2867 } 2868 } 2869 } 2870 2871 return $diff; 2872 2873} 2874 2875/** take output from rrdtool info array and build html table 2876 * @param array $info_array - array of rrdtool info data 2877 * @param array $diff - array of differences between definition and current rrd file settings 2878 * @return string - html code 2879 */ 2880function rrdtool_info2html($info_array, $diff=array()) { 2881 global $config; 2882 2883 include_once($config['library_path'] . '/time.php'); 2884 2885 html_start_box(__('RRD File Information'), '100%', '', '3', 'center', ''); 2886 2887 # header data 2888 $header_items = array( 2889 array('display' =>__('Header'), 'align' => 'left'), 2890 array('display' => '', 'align' => 'left') 2891 ); 2892 2893 html_header($header_items, 1); 2894 2895 # add human readable timestamp 2896 if (isset($info_array['last_update'])) { 2897 $info_array['last_update'] .= ' [' . date(CACTI_DATE_TIME_FORMAT, $info_array['last_update']) . ']'; 2898 } 2899 2900 $loop = array( 2901 'filename' => $info_array['filename'], 2902 'rrd_version' => $info_array['rrd_version'], 2903 'step' => $info_array['step'], 2904 'last_update' => $info_array['last_update']); 2905 2906 foreach ($loop as $key => $value) { 2907 form_alternate_row($key, true); 2908 form_selectable_cell($key, 'key'); 2909 form_selectable_cell($value, 'value', '', ((isset($diff[$key]) ? 'color:red' : ''))); 2910 form_end_row(); 2911 } 2912 2913 html_end_box(); 2914 2915 # data sources 2916 $header_items = array( 2917 array('display' => __('Data Source Items'), 'align' => 'left'), 2918 array('display' => __('Type'), 'align' => 'left'), 2919 array('display' => __('Minimal Heartbeat'), 'align' => 'right'), 2920 array('display' => __('Min'), 'align' => 'right'), 2921 array('display' => __('Max'), 'align' => 'right'), 2922 array('display' => __('Last DS'), 'align' => 'right'), 2923 array('display' => __('Value'), 'align' => 'right'), 2924 array('display' => __('Unknown Sec'), 'align' => 'right') 2925 ); 2926 2927 html_start_box('', '100%', '', '3', 'center', ''); 2928 2929 html_header($header_items, 1); 2930 2931 if (cacti_sizeof($info_array['ds'])) { 2932 foreach ($info_array['ds'] as $key => $value) { 2933 form_alternate_row('line' . $key, true); 2934 2935 form_selectable_cell($key, 'name', '', (isset($diff['ds'][$key]['error']) ? 'color:red' : '')); 2936 form_selectable_cell((isset($value['type']) ? $value['type'] : ''), 'type', '', (isset($diff['ds'][$key]['type']) ? 'color:red' : '')); 2937 form_selectable_cell((isset($value['minimal_heartbeat']) ? $value['minimal_heartbeat'] : ''), 'minimal_heartbeat', '', (isset($diff['ds'][$key]['minimal_heartbeat']) ? 'color:red, text-align:right' : 'text-align:right')); 2938 2939 if (isset($value['min'])) { 2940 if ($value['min'] == 'U') { 2941 form_selectable_cell($value['min'], 'min', '', 'right'); 2942 } elseif (is_numeric($value['min'])) { 2943 form_selectable_cell(number_format_i18n($value['min']), 'min', '', 'right'); 2944 } else { 2945 form_selectable_cell($value['min'], 'min', '', 'color:red;text-align:right'); 2946 } 2947 } else { 2948 form_selectable_cell(__('Unknown'), 'min', '', 'color:red;text-align:right'); 2949 } 2950 2951 if (isset($value['max'])) { 2952 if ($value['max'] == 'U') { 2953 form_selectable_cell($value['max'], 'max', '', 'right'); 2954 } elseif (is_numeric($value['max'])) { 2955 form_selectable_cell(number_format_i18n($value['max']), 'max', '', 'right'); 2956 } else { 2957 form_selectable_cell($value['max'], 'max', '', 'color:red;text-align:right'); 2958 } 2959 } else { 2960 form_selectable_cell(__('Unknown'), 'max', '', 'color:red;text-align:right'); 2961 } 2962 2963 form_selectable_cell((isset($value['last_ds']) && is_numeric($value['last_ds']) ? number_format_i18n($value['last_ds']) : (isset($value['last_ds']) ? $value['last_ds']:'')), 'last_ds', '', 'text-align:right'); 2964 form_selectable_cell((isset($value['value']) ? is_numeric($value['value']) ? number_format_i18n($value['value']) : $value['value'] : ''), 'value', '', 'text-align:right'); 2965 form_selectable_cell((isset($value['unknown_sec']) && is_numeric($value['unknown_sec']) ? number_format_i18n($value['unknown_sec']) : (isset($value['unknown_sec']) ? $value['unknown_sec']:'')), 'unknown_sec', '', 'text-align:right'); 2966 2967 form_end_row(); 2968 } 2969 } 2970 2971 html_end_box(); 2972 2973 # round robin archive 2974 $header_items = array( 2975 array('display' => __('Round Robin Archive'), 'align' => 'left'), 2976 array('display' => __('Consolidation Function'), 'align' => 'left'), 2977 array('display' => __('Rows'), 'align' => 'right'), 2978 array('display' => __('Cur Row'), 'align' => 'right'), 2979 array('display' => __('PDP per Row'), 'align' => 'right'), 2980 array('display' => __('X-Files Factor'), 'align' => 'right'), 2981 array('display' => __('CDP Prep Value (0)'), 'align' => 'right'), 2982 array('display' => __('CDP Unknown Data points (0)'), 'align' => 'right') 2983 ); 2984 2985 html_start_box('', '100%', '', '3', 'center', ''); 2986 2987 html_header($header_items, 1); 2988 2989 if (cacti_sizeof($info_array['rra'])) { 2990 foreach ($info_array['rra'] as $key => $value) { 2991 form_alternate_row('line_' . $key, true); 2992 2993 form_selectable_cell($key, 'name', '', (isset($diff['rra'][$key]['error']) ? 'color:red' : '')); 2994 form_selectable_cell((isset($value['cf']) ? $value['cf'] : ''), 'cf'); 2995 form_selectable_cell((isset($value['rows']) ? $value['rows'] : ''), 'rows', '', (isset($diff['rra'][$key]['rows']) ? 'color:red;text-align:right' : 'text-align:right')); 2996 form_selectable_cell((isset($value['cur_row']) ? $value['cur_row'] : ''), 'cur_row', '', 'text-align:right'); 2997 form_selectable_cell((isset($value['pdp_per_row']) ? $value['pdp_per_row'] : ''), 'pdp_per_row', '', 'text-align:right'); 2998 form_selectable_cell((isset($value['xff']) ? floatval($value['xff']) : ''), 'xff', '', (isset($diff['rra'][$key]['xff']) ? 'color:red;text-align:right' : 'text-align:right')); 2999 form_selectable_cell((isset($value['cdp_prep'][0]['value']) ? (strtolower($value['cdp_prep'][0]['value']) == 'nan') ? $value['cdp_prep'][0]['value'] : floatval($value['cdp_prep'][0]['value']) : ''), 'value', '', 'text-align:right'); 3000 form_selectable_cell((isset($value['cdp_prep'][0]['unknown_datapoints'])? $value['cdp_prep'][0]['unknown_datapoints'] : ''), 'unknown_datapoints', '', 'text-align:right'); 3001 3002 form_end_row(); 3003 } 3004 } 3005 3006 html_end_box(); 3007} 3008 3009/** rrdtool_tune - create rrdtool tune/resize commands 3010 * html+cli enabled 3011 * @param $rrd_file - rrd file name 3012 * @param $diff - array of discrepancies between cacti setttings and rrd file info 3013 * @param $show_source - only show text+commands or execute all commands, execute is for cli mode only! 3014 */ 3015function rrdtool_tune($rrd_file, $diff, $show_source = true) { 3016 function print_leaves($array, $nl) { 3017 foreach ($array as $key => $line) { 3018 if (!is_array($line)) { 3019 print $line . $nl; 3020 } else { 3021 if ($key === 'tune') continue; 3022 if ($key === 'resize') continue; 3023 print_leaves($line, $nl); 3024 } 3025 } 3026 3027 } 3028 3029 3030 $cmd = array(); 3031 # for html/cli mode 3032 if (CACTI_CLI) { 3033 $nl = "\n"; 3034 } else { 3035 $nl = '<br/>'; 3036 } 3037 3038 if ($show_source && cacti_sizeof($diff)) { 3039 # print error descriptions 3040 print_leaves($diff, $nl); 3041 } 3042 3043 if (isset($diff['tune']) && cacti_sizeof($diff['tune'])) { 3044 # create tune commands 3045 foreach ($diff['tune'] as $line) { 3046 if ($show_source == true) { 3047 print read_config_option('path_rrdtool') . ' tune ' . $line . $nl; 3048 } else { 3049 rrdtool_execute("tune $line", true, RRDTOOL_OUTPUT_STDOUT); 3050 } 3051 } 3052 } 3053 3054 if (isset($diff['resize']) && cacti_sizeof($diff['resize'])) { 3055 # each resize goes into an extra line 3056 foreach ($diff['resize'] as $line) { 3057 if ($show_source == true) { 3058 print read_config_option('path_rrdtool') . ' resize ' . $line . $nl; 3059 print __('rename %s to %s', dirname($rrd_file) . '/resize.rrd', $rrd_file) . $nl; 3060 } else { 3061 rrdtool_execute("resize $line", true, RRDTOOL_OUTPUT_STDOUT); 3062 rename(dirname($rrd_file) . '/resize.rrd', $rrd_file); 3063 } 3064 } 3065 } 3066} 3067 3068/** Given a data source id, check the rrdtool file to the data source definition 3069 * @param $data_source_id - data source id 3070 * @return - (array) an array containing issues with the rrdtool file definition vs data source 3071 */ 3072function rrd_check($data_source_id) { 3073 global $rrd_tune_array, $data_source_types; 3074 3075 $data_source_name = get_data_source_item_name($rrd_tune_array['data_source_id']); 3076 $data_source_type = $data_source_types[$rrd_tune_array['data-source-type']]; 3077 $data_source_path = get_data_source_path($rrd_tune_array['data_source_id'], true); 3078} 3079 3080/** Given a data source id, update the rrdtool file to match the data source definition 3081 * @param $data_source_id - data source id 3082 * @return - 1 success, 2 false 3083 */ 3084function rrd_repair($data_source_id) { 3085 global $rrd_tune_array, $data_source_types; 3086 3087 $data_source_name = get_data_source_item_name($rrd_tune_array['data_source_id']); 3088 $data_source_type = $data_source_types[$rrd_tune_array['data-source-type']]; 3089 $data_source_path = get_data_source_path($rrd_tune_array['data_source_id'], true); 3090} 3091 3092/** add a (list of) datasource(s) to an (array of) rrd file(s) 3093 * @param array $file_array - array of rrd files 3094 * @param array $ds_array - array of datasouce parameters 3095 * @param bool $debug - debug mode 3096 * @return mixed - success (bool) or error message (array) 3097 */ 3098function rrd_datasource_add($file_array, $ds_array, $debug) { 3099 global $data_source_types, $consolidation_functions; 3100 3101 $rrdtool_pipe = rrd_init(); 3102 3103 /* iterate all given rrd files */ 3104 foreach ($file_array as $file) { 3105 /* create a DOM object from an rrdtool dump */ 3106 $dom = new domDocument; 3107 $dom->loadXML(rrdtool_execute("dump $file", false, RRDTOOL_OUTPUT_STDOUT, $rrdtool_pipe, 'UTIL')); 3108 if (!$dom) { 3109 $check['err_msg'] = __('Error while parsing the XML of rrdtool dump'); 3110 return $check; 3111 } 3112 3113 /* rrdtool dump depends on rrd file version: 3114 * version 0001 => RRDtool 1.0.x 3115 * version 0003 => RRDtool 1.2.x, 1.3.x, 1.4.x, 1.5.x, 1.6.x 3116 */ 3117 $version = trim($dom->getElementsByTagName('version')->item(0)->nodeValue); 3118 3119 /* now start XML processing */ 3120 foreach ($ds_array as $ds) { 3121 /* first, append the <DS> strcuture in the rrd header */ 3122 if ($ds['type'] === $data_source_types[DATA_SOURCE_TYPE_COMPUTE]) { 3123 rrd_append_compute_ds($dom, $version, $ds['name'], $ds['type'], $ds['cdef']); 3124 } else { 3125 rrd_append_ds($dom, $version, $ds['name'], $ds['type'], $ds['heartbeat'], $ds['min'], $ds['max']); 3126 } 3127 /* now work on the <DS> structure as part of the <cdp_prep> tree */ 3128 rrd_append_cdp_prep_ds($dom, $version); 3129 /* add <V>alues to the <database> tree */ 3130 rrd_append_value($dom); 3131 } 3132 3133 if ($debug) { 3134 echo $dom->saveXML(); 3135 } else { 3136 /* for rrdtool restore, we need a file, so write the XML to disk */ 3137 $xml_file = $file . '.xml'; 3138 $rc = $dom->save($xml_file); 3139 /* verify, if write was successful */ 3140 if ($rc === false) { 3141 $check['err_msg'] = __('ERROR while writing XML file: %s', $xml_file); 3142 return $check; 3143 } else { 3144 /* are we allowed to write the rrd file? */ 3145 if (is_writable($file)) { 3146 /* restore the modified XML to rrd */ 3147 rrdtool_execute("restore -f $xml_file $file", false, RRDTOOL_OUTPUT_STDOUT, $rrdtool_pipe, 'UTIL'); 3148 /* scratch that XML file to avoid filling up the disk */ 3149 unlink($xml_file); 3150 cacti_log('Added Data Source(s) to RRDfile: ' . $file, false, 'UTIL'); 3151 } else { 3152 $check['err_msg'] = __('ERROR: RRDfile %s not writeable', $file); 3153 return $check; 3154 } 3155 } 3156 } 3157 } 3158 3159 rrd_close($rrdtool_pipe); 3160 3161 return true; 3162} 3163 3164/** delete a (list of) rra(s) from an (array of) rrd file(s) 3165 * @param array $file_array - array of rrd files 3166 * @param array $rra_array - array of rra parameters 3167 * @param bool $debug - debug mode 3168 * @return mixed - success (bool) or error message (array) 3169 */ 3170function rrd_rra_delete($file_array, $rra_array, $debug) { 3171 $rrdtool_pipe = rrd_init(); 3172 3173 /* iterate all given rrd files */ 3174 foreach ($file_array as $file) { 3175 /* create a DOM document from an rrdtool dump */ 3176 $dom = new domDocument; 3177 $dom->loadXML(rrdtool_execute("dump $file", false, RRDTOOL_OUTPUT_STDOUT, $rrdtool_pipe, 'UTIL')); 3178 if (!$dom) { 3179 $check['err_msg'] = __('Error while parsing the XML of RRDtool dump'); 3180 return $check; 3181 } 3182 3183 /* now start XML processing */ 3184 foreach ($rra_array as $rra) { 3185 rrd_delete_rra($dom, $rra, $debug); 3186 } 3187 3188 if ($debug) { 3189 echo $dom->saveXML(); 3190 } else { 3191 /* for rrdtool restore, we need a file, so write the XML to disk */ 3192 $xml_file = $file . '.xml'; 3193 $rc = $dom->save($xml_file); 3194 /* verify, if write was successful */ 3195 if ($rc === false) { 3196 $check['err_msg'] = __('ERROR while writing XML file: %s', $xml_file); 3197 return $check; 3198 } else { 3199 /* are we allowed to write the rrd file? */ 3200 if (is_writable($file)) { 3201 /* restore the modified XML to rrd */ 3202 rrdtool_execute("restore -f $xml_file $file", false, RRDTOOL_OUTPUT_STDOUT, $rrdtool_pipe, 'UTIL'); 3203 /* scratch that XML file to avoid filling up the disk */ 3204 unlink($xml_file); 3205 cacti_log('Deleted RRA(s) from RRDfile: ' . $file, false, 'UTIL'); 3206 } else { 3207 $check['err_msg'] = __('ERROR: RRDfile %s not writeable', $file); 3208 return $check; 3209 } 3210 } 3211 } 3212 } 3213 3214 rrd_close($rrdtool_pipe); 3215 3216 return true; 3217} 3218 3219/** clone a (list of) rra(s) from an (array of) rrd file(s) 3220 * @param array $file_array - array of rrd files 3221 * @param string $cf - new consolidation function 3222 * @param array $rra_array - array of rra parameters 3223 * @param bool $debug - debug mode 3224 * @return mixed - success (bool) or error message (array) 3225 */ 3226function rrd_rra_clone($file_array, $cf, $rra_array, $debug) { 3227 $rrdtool_pipe = rrd_init(); 3228 3229 /* iterate all given rrd files */ 3230 foreach ($file_array as $file) { 3231 /* create a DOM document from an rrdtool dump */ 3232 $dom = new domDocument; 3233 $dom->loadXML(rrdtool_execute("dump $file", false, RRDTOOL_OUTPUT_STDOUT, $rrdtool_pipe, 'UTIL')); 3234 if (!$dom) { 3235 $check['err_msg'] = __('Error while parsing the XML of RRDtool dump'); 3236 return $check; 3237 } 3238 3239 /* now start XML processing */ 3240 foreach ($rra_array as $rra) { 3241 rrd_copy_rra($dom, $cf, $rra, $debug); 3242 } 3243 3244 if ($debug) { 3245 echo $dom->saveXML(); 3246 } else { 3247 /* for rrdtool restore, we need a file, so write the XML to disk */ 3248 $xml_file = $file . '.xml'; 3249 $rc = $dom->save($xml_file); 3250 /* verify, if write was successful */ 3251 if ($rc === false) { 3252 $check['err_msg'] = __('ERROR while writing XML file: %s', $xml_file); 3253 return $check; 3254 } else { 3255 /* are we allowed to write the rrd file? */ 3256 if (is_writable($file)) { 3257 /* restore the modified XML to rrd */ 3258 rrdtool_execute("restore -f $xml_file $file", false, RRDTOOL_OUTPUT_STDOUT, $rrdtool_pipe, 'UTIL'); 3259 /* scratch that XML file to avoid filling up the disk */ 3260 unlink($xml_file); 3261 cacti_log('Deleted RRA(s) from RRDfile: ' . $file, false, 'UTIL'); 3262 } else { 3263 $check['err_msg'] = __('ERROR: RRDfile %s not writeable', $file); 3264 return $check; 3265 } 3266 } 3267 } 3268 } 3269 3270 rrd_close($rrdtool_pipe); 3271 3272 return true; 3273} 3274 3275/** appends a <DS> subtree to an RRD XML structure 3276 * @param object $dom - the DOM object, where the RRD XML is stored 3277 * @param string $version- rrd file version 3278 * @param string $name - name of the new ds 3279 * @param string $type - type of the new ds 3280 * @param int $min_hb - heartbeat of the new ds 3281 * @param string $min - min value of the new ds or [NaN|U] 3282 * @param string $max - max value of the new ds or [NaN|U] 3283 * @return object - modified DOM 3284 */ 3285function rrd_append_ds($dom, $version, $name, $type, $min_hb, $min, $max) { 3286 /* rrdtool version dependencies */ 3287 if ($version === RRD_FILE_VERSION1) { 3288 $last_ds = 'U'; 3289 } 3290 elseif ($version === RRD_FILE_VERSION3) { 3291 $last_ds = 'UNKN'; 3292 } 3293 3294 /* create <DS> subtree */ 3295 $new_dom = new DOMDocument; 3296 /* pretty print */ 3297 $new_dom->formatOutput = true; 3298 /* this defines the new node structure */ 3299 $new_dom->loadXML(" 3300 <ds> 3301 <name> $name </name> 3302 <type> $type </type> 3303 <minimal_heartbeat> $min_hb </minimal_heartbeat> 3304 <min> $min </min> 3305 <max> $max </max> 3306 3307 <!-- PDP Status --> 3308 <last_ds> $last_ds </last_ds> 3309 <value> 0.0000000000e+00 </value> 3310 <unknown_sec> 0 </unknown_sec> 3311 </ds>"); 3312 3313 /* create a node element from new document */ 3314 $new_node = $new_dom->getElementsByTagName('ds')->item(0); 3315 #echo $new_dom->saveXML(); # print new node 3316 3317 /* get XPATH notation required for positioning */ 3318 #$xpath = new DOMXPath($dom); 3319 /* get XPATH for entry where new node will be inserted 3320 * which is the <rra> entry */ 3321 #$insert = $xpath->query('/rrd/rra')->item(0); 3322 $insert = $dom->getElementsByTagName('rra')->item(0); 3323 3324 /* import the new node */ 3325 $new_node = $dom->importNode($new_node, true); 3326 /* and insert it at the correct place */ 3327 $insert->parentNode->insertBefore($new_node, $insert); 3328} 3329 3330/** COMPUTE DS: appends a <DS> subtree to an RRD XML structure 3331 * @param object $dom - the DOM object, where the RRD XML is stored 3332 * @param string $version- rrd file version 3333 * @param string $name - name of the new ds 3334 * @param string $type - type of the new ds 3335 * @param int $cdef - the cdef rpn used for COMPUTE 3336 * @return object - modified DOM 3337 */ 3338function rrd_append_compute_ds($dom, $version, $name, $type, $cdef) { 3339 /* rrdtool version dependencies */ 3340 if ($version === RRD_FILE_VERSION1) { 3341 $last_ds = 'U'; 3342 } 3343 elseif ($version === RRD_FILE_VERSION3) { 3344 $last_ds = 'UNKN'; 3345 } 3346 3347 /* create <DS> subtree */ 3348 $new_dom = new DOMDocument; 3349 /* pretty print */ 3350 $new_dom->formatOutput = true; 3351 /* this defines the new node structure */ 3352 $new_dom->loadXML(" 3353 <ds> 3354 <name> $name </name> 3355 <type> $type </type> 3356 <cdef> $cdef </cdef> 3357 3358 <!-- PDP Status --> 3359 <last_ds> $last_ds </last_ds> 3360 <value> 0.0000000000e+00 </value> 3361 <unknown_sec> 0 </unknown_sec> 3362 </ds>"); 3363 3364 /* create a node element from new document */ 3365 $new_node = $new_dom->getElementsByTagName('ds')->item(0); 3366 3367 /* get XPATH notation required for positioning */ 3368 #$xpath = new DOMXPath($dom); 3369 /* get XPATH for entry where new node will be inserted 3370 * which is the <rra> entry */ 3371 #$insert = $xpath->query('/rrd/rra')->item(0); 3372 $insert = $dom->getElementsByTagName('rra')->item(0); 3373 3374 /* import the new node */ 3375 $new_node = $dom->importNode($new_node, true); 3376 /* and insert it at the correct place */ 3377 $insert->parentNode->insertBefore($new_node, $insert); 3378} 3379 3380/** append a <DS> subtree to the <CDP_PREP> subtrees of a RRD XML structure 3381 * @param object $dom - the DOM object, where the RRD XML is stored 3382 * @param string $version - rrd file version 3383 * @return object - the modified DOM object 3384 */ 3385function rrd_append_cdp_prep_ds($dom, $version) { 3386 /* get all <cdp_prep><ds> entries */ 3387 #$cdp_prep_list = $xpath->query('/rrd/rra/cdp_prep'); 3388 $cdp_prep_list = $dom->getElementsByTagName('rra')->item(0)->getElementsByTagName('cdp_prep'); 3389 3390 /* get XPATH notation required for positioning */ 3391 #$xpath = new DOMXPath($dom); 3392 3393 /* get XPATH for source <ds> entry */ 3394 #$src_ds = $xpath->query('/rrd/rra/cdp_prep/ds')->item(0); 3395 $src_ds = $dom->getElementsByTagName('rra')->item(0)->getElementsByTagName('cdp_prep')->item(0)->getElementsByTagName('ds')->item(0); 3396 /* clone the source ds entry to preserve RRDtool notation */ 3397 $new_ds = $src_ds->cloneNode(true); 3398 3399 /* rrdtool version dependencies */ 3400 if ($version === RRD_FILE_VERSION3) { 3401 $new_ds->getElementsByTagName('primary_value')->item(0)->nodeValue = ' NaN '; 3402 $new_ds->getElementsByTagName('secondary_value')->item(0)->nodeValue = ' NaN '; 3403 } 3404 3405 /* the new node always has default entries */ 3406 $new_ds->getElementsByTagName('value')->item(0)->nodeValue = ' NaN '; 3407 $new_ds->getElementsByTagName('unknown_datapoints')->item(0)->nodeValue = ' 0 '; 3408 3409 3410 /* iterate all entries found, equals 'number of <rra>' times 'number of <ds>' */ 3411 if ($cdp_prep_list->length) { 3412 foreach ($cdp_prep_list as $cdp_prep) { 3413 /* $cdp_prep now points to the next <cdp_prep> XML Element 3414 * and append new ds entry at end of <cdp_prep> child list */ 3415 $cdp_prep->appendChild($new_ds); 3416 } 3417 } 3418} 3419 3420/** append a <V>alue element to the <DATABASE> subtrees of a RRD XML structure 3421 * @param object $dom - the DOM object, where the RRD XML is stored 3422 * @return object - the modified DOM object 3423 */ 3424function rrd_append_value($dom) { 3425 /* get XPATH notation required for positioning */ 3426 #$xpath = new DOMXPath($dom); 3427 3428 /* get all <cdp_prep><ds> entries */ 3429 #$itemList = $xpath->query('/rrd/rra/database/row'); 3430 $itemList = $dom->getElementsByTagName('row'); 3431 3432 /* create <V> entry to preserve RRDtool notation */ 3433 $new_v = $dom->createElement('v', ' NaN '); 3434 3435 /* iterate all entries found, equals 'number of <rra>' times 'number of <ds>' */ 3436 if ($itemList->length) { 3437 foreach ($itemList as $item) { 3438 /* $item now points to the next <cdp_prep> XML Element 3439 * and append new ds entry at end of <cdp_prep> child list */ 3440 $item->appendChild($new_v); 3441 } 3442 } 3443} 3444 3445/** delete an <RRA> subtree from the <RRD> XML structure 3446 * @param object $dom - the DOM document, where the RRD XML is stored 3447 * @param array $rra_parm - a single rra parameter set, given by the user 3448 * @return object - the modified DOM object 3449 */ 3450function rrd_delete_rra($dom, $rra_parm) { 3451 /* find all RRA DOMNodes */ 3452 $rras = $dom->getElementsByTagName('rra'); 3453 3454 /* iterate all entries found */ 3455 $nb = $rras->length; 3456 for ($pos = 0; $pos < $nb; $pos++) { 3457 /* retrieve all RRA DOMNodes one by one */ 3458 $rra = $rras->item($pos); 3459 $cf = $rra->getElementsByTagName('cf')->item(0)->nodeValue; 3460 $pdp_per_row = $rra->getElementsByTagName('pdp_per_row')->item(0)->nodeValue; 3461 $xff = $rra->getElementsByTagName('xff')->item(0)->nodeValue; 3462 $rows = $rra->getElementsByTagName('row')->length; 3463 3464 if ($cf == $rra_parm['cf'] && 3465 $pdp_per_row == $rra_parm['pdp_per_row'] && 3466 $xff == $rra_parm['xff'] && 3467 $rows == $rra_parm['rows']) { 3468 print(__("RRA (CF=%s, ROWS=%d, PDP_PER_ROW=%d, XFF=%1.2f) removed from RRD file", $cf, $rows, $pdp_per_row, $xff)) . PHP_EOL; 3469 /* we need the parentNode for removal operation */ 3470 $parent = $rra->parentNode; 3471 $parent->removeChild($rra); 3472 break; /* do NOT accidentally remove more than one element, else loop back to forth */ 3473 } 3474 } 3475 return $dom; 3476} 3477 3478/** clone an <RRA> subtree of the <RRD> XML structure, replacing cf 3479 * @param object $dom - the DOM document, where the RRD XML is stored 3480 * @param string $cf - new consolidation function 3481 * @param array $rra_parm - a single rra parameter set, given by the user 3482 * @return object - the modified DOM object 3483 */ 3484function rrd_copy_rra($dom, $cf, $rra_parm) { 3485 /* find all RRA DOMNodes */ 3486 $rras = $dom->getElementsByTagName('rra'); 3487 3488 /* iterate all entries found */ 3489 $nb = $rras->length; 3490 for ($pos = 0; $pos < $nb; $pos++) { 3491 /* retrieve all RRA DOMNodes one by one */ 3492 $rra = $rras->item($pos); 3493 $_cf = $rra->getElementsByTagName('cf')->item(0)->nodeValue; 3494 $_pdp_per_row = $rra->getElementsByTagName('pdp_per_row')->item(0)->nodeValue; 3495 $_xff = $rra->getElementsByTagName('xff')->item(0)->nodeValue; 3496 $_rows = $rra->getElementsByTagName('row')->length; 3497 3498 if ($_cf == $rra_parm['cf'] && 3499 $_pdp_per_row == $rra_parm['pdp_per_row'] && 3500 $_xff == $rra_parm['xff'] && 3501 $_rows == $rra_parm['rows']) { 3502 print(__("RRA (CF=%s, ROWS=%d, PDP_PER_ROW=%d, XFF=%1.2f) adding to RRD file", $cf, $_rows, $_pdp_per_row, $_xff)) . PHP_EOL; 3503 /* we need the parentNode for append operation */ 3504 $parent = $rra->parentNode; 3505 3506 /* get a clone of the matching RRA */ 3507 $new_rra = $rra->cloneNode(true); 3508 /* and find the 'old' cf */ 3509 #$old_cf = $new_rra->getElementsByTagName('cf')->item(0); 3510 /* now replace old cf with new one */ 3511 #$old_cf->childNodes->item(0)->replaceData(0,20,$cf); 3512 $new_rra->getElementsByTagName('cf')->item(0)->nodeValue = $cf; 3513 3514 /* append new rra entry at end of the list */ 3515 $parent->appendChild($new_rra); 3516 break; /* do NOT accidentally clone more than one element, else loop back to forth */ 3517 } 3518 } 3519 3520 return $dom; 3521} 3522 3523function rrdtool_parse_error($string) { 3524 global $config; 3525 3526 if (preg_match('/ERROR. opening \'(.*)\': (No such|Permiss).*/', $string, $matches)) { 3527 if (cacti_sizeof($matches) >= 2) { 3528 $filename = $matches[1]; 3529 $rra_name = basename($filename); 3530 $rra_path = dirname($filename) . "/"; 3531 if (!is_resource_writable($rra_path)) { 3532 $message = __('Website does not have write access to %s, may be unable to create/update RRDs', 'folder'); 3533 $rra_name = str_replace($config['base_path'],'', $rra_path); 3534 $rra_path = ""; 3535 } else { 3536 if (stripos($filename, $config['base_path']) >= 0) { 3537 $rra_file = str_replace($config['base_path'] . '/rra/', '', $filename); 3538 $rra_name = basename($rra_file); 3539 $rra_path = dirname($rra_file); 3540 } else { 3541 $rra_name = basename($filename); 3542 $rra_path = __('(Custom)'); 3543 } 3544 3545 if (!is_resource_writable($filename)) { 3546 $message = __('Website does not have write access to %s, may be unable to create/update RRDs', 'data file'); 3547 } else { 3548 $message = __('Failed to open data file, poller may not have run yet'); 3549 } 3550 3551 $rra_path = '(' . __('RRA Folder') . ': ' . ((empty($rra_path) || $rra_path == ".") ? __('Root') : $rra_path) . ')'; 3552 } 3553 3554 $string = $message . ":\n\0x27\n" . $rra_name; 3555 if (!empty($rra_path)) { 3556 $string .= "\n" . $rra_path; 3557 } 3558 } 3559 } 3560 return $string; 3561} 3562 3563function rrdtool_create_error_image($string, $width = '', $height = '') { 3564 global $config; 3565 3566 $string = rrdtool_parse_error($string); 3567 3568 /* put image in buffer */ 3569 ob_start(); 3570 3571 $image_data = false; 3572 $font_color = '000000'; 3573 $font_size = 8; 3574 $back_color = 'F3F3F3'; 3575 $shadea = 'CBCBCB'; 3576 $shadeb = '999999'; 3577 3578 if ($config['cacti_server_os'] == 'unix') { 3579 $font_file = '/usr/share/fonts/dejavu/DejaVuSans.ttf'; 3580 } else { 3581 $font_file = 'C:/Windows/Fonts/Arial.ttf'; 3582 } 3583 3584 $themefile = $config['base_path'] . '/include/themes/' . get_selected_theme() . '/rrdtheme.php'; 3585 3586 if (file_exists($themefile) && is_readable($themefile)) { 3587 include($themefile); 3588 3589 if (isset($rrdfonts['legend']['size'])) { 3590 $font_size = $rrdfonts['legend']['size']; 3591 } 3592 3593 if (isset($rrdcolors['font'])) { 3594 $font_color = $rrdcolors['font']; 3595 } 3596 3597 if (isset($rrdcolors['canvas'])) { 3598 $back_color = $rrdcolors['canvas']; 3599 } 3600 3601 if (isset($rrdcolors['shadea'])) { 3602 $shadea = $rrdcolors['shadea']; 3603 } 3604 3605 if (isset($rrdcolors['shadeb'])) { 3606 $shadeb = $rrdcolors['shadeb']; 3607 } 3608 } 3609 3610 $image = imagecreatetruecolor(450, 200); 3611 imagesavealpha($image, true); 3612 3613 /* create a transparent color */ 3614 $transparent = imagecolorallocatealpha($image, 0, 0, 0, 127); 3615 imagefill($image, 0, 0, $transparent); 3616 3617 /* background the entire image with the frame */ 3618 list($red, $green, $blue) = sscanf($shadeb, '%02x%02x%02x'); 3619 $shadeb = imagecolorallocate($image, $red, $green, $blue); 3620 imagefill($image, 0, 0, $shadeb); 3621 3622 /* set the background color */ 3623 list($red, $green, $blue) = sscanf($shadea, '%02x%02x%02x'); 3624 $shadea = imagecolorallocate($image, $red, $green, $blue); 3625 imagefilledrectangle($image, 1, 1, 448, 198, $shadea); 3626 3627 /* set the background color */ 3628 list($red, $green, $blue) = sscanf($back_color, '%02x%02x%02x'); 3629 $back_color = imagecolorallocate($image, $red, $green, $blue); 3630 imagefilledrectangle($image, 2, 2, 447, 197, $back_color); 3631 3632 /* allocate the image */ 3633 $logo = imagecreatefrompng($config['base_path'] . '/images/cacti_error_image.png'); 3634 3635 /* merge the two images */ 3636 imagecopy($image, $logo, 0, 0, 0, 0, 450, 200); 3637 3638 /* set the background color */ 3639 list($red, $green, $blue) = sscanf($font_color, '%02x%02x%02x'); 3640 $text_color = imagecolorallocate($image, $red, $green, $blue); 3641 3642 /* see the size of the string */ 3643 $string = trim($string); 3644 $maxstring = (450 - (125 + 10)) / ($font_size / 0.9); 3645 $stringlen = strlen($string) * $font_size; 3646 $padding = 5; 3647 3648 if ($stringlen > $maxstring) { 3649 $cstring = wordwrap($string, $maxstring, "\n", true); 3650 $strings = explode("\n", $cstring); 3651 $strings = array_reverse($strings); 3652 $lines = cacti_sizeof($strings); 3653 } elseif (strlen(trim($string)) == 0) { 3654 $strings = array(__('Unknown RRDtool Error')); 3655 $lines = 1; 3656 } else { 3657 $strings = array($string); 3658 $lines = 1; 3659 } 3660 3661 /* setup the text position, image is 450x200, we start at 125 pixels from the left */ 3662 $xpos = 125; 3663 $texth = ($lines * $font_size + (($lines - 1) * $padding)); 3664 $ypos = round((200 / 2) + ($texth / 2),0); 3665 3666 /* set the font of the image */ 3667 if (file_exists($font_file) && is_readable($font_file) && function_exists('imagettftext')) { 3668 foreach($strings as $string) { 3669 if (trim($string) != '') { 3670 if (@imagettftext($image, $font_size, 0, $xpos, $ypos, $text_color, $font_file, $string) === false) { 3671 cacti_log('TTF text overlay failed'); 3672 } 3673 $ypos -= ($font_size + $padding); 3674 } 3675 } 3676 } else { 3677 foreach($strings as $string) { 3678 if (trim($string) != '') { 3679 if (@imagestring($image, $font_size, $xpos, $ypos, $string, $text_color) === false) { 3680 cacti_log('Text overlay failed'); 3681 } 3682 $ypos -= ($font_size + $padding); 3683 } 3684 } 3685 } 3686 3687 if ($width != '' && $height != '') { 3688 $nimage = imagecreatetruecolor($width, $height); 3689 imagecopyresized($nimage, $image, 0, 0, 0, 0, $width, $height, 450, 200); 3690 3691 /* create the image */ 3692 imagepng($image); 3693 } else { 3694 /* create the image */ 3695 imagepng($image); 3696 } 3697 3698 /* get the image from the buffer */ 3699 $image_data = ob_get_contents(); 3700 3701 /* destroy the image object */ 3702 imagedestroy($image); 3703 imagedestroy($logo); 3704 if (isset($nimage)) { 3705 imagedestroy($nimage); 3706 } 3707 3708 /* flush the buffer */ 3709 ob_end_clean(); 3710 3711 return $image_data; 3712} 3713