diff --git a/LibreNMS/Data/Store/Rrd.php b/LibreNMS/Data/Store/Rrd.php index 3cf829b990..dbed308c7f 100644 --- a/LibreNMS/Data/Store/Rrd.php +++ b/LibreNMS/Data/Store/Rrd.php @@ -28,10 +28,12 @@ use Illuminate\Support\Str; use LibreNMS\Config; use LibreNMS\Data\Measure\Measurement; use LibreNMS\Exceptions\FileExistsException; +use LibreNMS\Exceptions\RrdGraphException; use LibreNMS\Proc; use LibreNMS\Util\Debug; use LibreNMS\Util\Rewrite; use Log; +use Symfony\Component\Process\Process; class Rrd extends BaseDatastore { @@ -545,25 +547,26 @@ class Rrd extends BaseDatastore /** * Generates a graph file at $graph_file using $options - * Opens its own rrdtool pipe. + * Graphs are a single command per run, so this just runs rrdtool * - * @param string $graph_file - * @param string $options - * @return string|int + * @param string $options + * @return string + * @throws \LibreNMS\Exceptions\FileExistsException + * @throws \LibreNMS\Exceptions\RrdGraphException */ - public function graph($graph_file, $options) + public function graph(string $options): string { - if ($this->init(false)) { - $cmd = $this->buildCommand('graph', $graph_file, $options); + $cmd = $this->buildCommand('graph', '-', $options); + $process = Process::fromShellCommandline(Config::get('rrdtool') . ' ' . $cmd, $this->rrd_dir); + $process->setTimeout(300); + $process->setIdleTimeout(300); + $process->run(); - $output = implode($this->sync_process->sendCommand($cmd)); - - d_echo("

$cmd

\n

command returned ($output)

"); - - return $output; - } else { - return 0; + if (! $process->isSuccessful()) { + throw new RrdGraphException($process->getErrorOutput(), $process->getExitCode(), $process->getOutput()); } + + return $process->getOutput(); } public function __destruct() diff --git a/LibreNMS/Exceptions/RrdGraphException.php b/LibreNMS/Exceptions/RrdGraphException.php new file mode 100644 index 0000000000..3317ac6ff9 --- /dev/null +++ b/LibreNMS/Exceptions/RrdGraphException.php @@ -0,0 +1,44 @@ +. + * + * @package LibreNMS + * @link http://librenms.org + * @copyright 2021 Tony Murray + * @author Tony Murray + */ + +namespace LibreNMS\Exceptions; + +use Exception; + +class RrdGraphException extends Exception +{ + protected $image_output; + + public function __construct($error, $exit_code, $image_output) + { + parent::__construct($error, $exit_code); + $this->image_output = $image_output; + } + + public function getImage() + { + return $this->image_output; + } +} diff --git a/includes/html/api_functions.inc.php b/includes/html/api_functions.inc.php index 1a4aa32b12..654d6a9b2e 100644 --- a/includes/html/api_functions.inc.php +++ b/includes/html/api_functions.inc.php @@ -81,8 +81,7 @@ function api_get_graph(array $vars) { global $dur; // Needed for callback within graph code - $auth = '1'; - $base64_output = ''; + $auth = true; // prevent ugly error for undefined graphs from being passed to the user [$type, $subtype] = extract_graph_type($vars['type']); @@ -99,10 +98,10 @@ function api_get_graph(array $vars) ob_end_clean(); if ($vars['output'] === 'base64') { - return api_success(['image' => $base64_output, 'content-type' => get_image_type()], 'image'); + return api_success(['image' => $image, 'content-type' => get_image_type(Config::get('webui.graph_type'))], 'image'); } - return response($image, 200, ['Content-Type' => get_image_type()]); + return response($image, 200, ['Content-Type' => get_image_type(Config::get('webui.graph_type'))]); } function check_bill_permission($bill_id, $callback) diff --git a/includes/html/functions.inc.php b/includes/html/functions.inc.php index 7c755886af..6eea38a32a 100644 --- a/includes/html/functions.inc.php +++ b/includes/html/functions.inc.php @@ -47,14 +47,6 @@ function var_get($v) return false; } -function data_uri($file, $mime) -{ - $contents = file_get_contents($file); - $base64 = base64_encode($contents); - - return 'data:' . $mime . ';base64,' . $base64; -}//end data_uri() - function toner2colour($descr, $percent) { $colour = \LibreNMS\Util\Colors::percentage(100 - $percent, null); @@ -465,19 +457,30 @@ function graph_error($text, $color = [128, 0, 0]) { global $vars; + $type = Config::get('webui.graph_type'); if (! Debug::isEnabled()) { - set_image_type(); + header('Content-type: ' . get_image_type($type)); } - $width = $vars['width'] ?? 150; - $height = $vars['height'] ?? 60; + $width = (int) ($vars['width'] ?? 150); + $height = (int) ($vars['height'] ?? 60); - if (Config::get('webui.graph_type') === 'svg') { + if ($type === 'svg') { $rgb = implode(', ', $color); - $font_size = 20; - $svg_x = 100; - $svg_y = min($font_size, $width ? (($height / $width) * $svg_x) : 1); - echo "$text"; + echo << + + + + $text + + + + +SVG; } else { $img = imagecreate($width, $height); imagecolorallocatealpha($img, 255, 255, 255, 127); // transparent background @@ -1020,18 +1023,14 @@ function eventlog_severity($eventlog_severity) } } // end eventlog_severity -function set_image_type() +/** + * Get the http content type of the image + * @param string $type svg or png + * @return string + */ +function get_image_type(string $type) { - header('Content-type: ' . get_image_type()); -} - -function get_image_type() -{ - if (Config::get('webui.graph_type') === 'svg') { - return 'image/svg+xml'; - } else { - return 'image/png'; - } + return $type === 'svg' ? 'image/svg+xml' : 'image/png'; } function get_oxidized_nodes_list() diff --git a/includes/html/graphs/device/sensor.inc.php b/includes/html/graphs/device/sensor.inc.php index 3e2923f43e..b5dc26ee5b 100644 --- a/includes/html/graphs/device/sensor.inc.php +++ b/includes/html/graphs/device/sensor.inc.php @@ -38,8 +38,8 @@ foreach (dbFetchRows('SELECT * FROM `sensors` WHERE `sensor_class` = ? AND `devi }//end switch $sensor_descr_fixed = \LibreNMS\Data\Store\Rrd::fixedSafeDescr($sensor['sensor_descr'], 12); - $rrd_file = get_sensor_rrd($device, $sensor); - $rrd_options .= " DEF:sensor{$sensor['sensor_id']}=$rrd_file:sensor:AVERAGE"; + $rrd_filename = get_sensor_rrd($device, $sensor); + $rrd_options .= " DEF:sensor{$sensor['sensor_id']}=$rrd_filename:sensor:AVERAGE"; $rrd_options .= " LINE1:sensor{$sensor['sensor_id']}#$colour:'$sensor_descr_fixed'"; $rrd_options .= " GPRINT:sensor{$sensor['sensor_id']}:LAST:%5.1lf$unit"; $rrd_options .= " GPRINT:sensor{$sensor['sensor_id']}:MIN:%5.1lf$unit"; diff --git a/includes/html/graphs/graph.inc.php b/includes/html/graphs/graph.inc.php index 3ca8e51422..32572e4e2e 100644 --- a/includes/html/graphs/graph.inc.php +++ b/includes/html/graphs/graph.inc.php @@ -2,6 +2,8 @@ use LibreNMS\Config; +global $debug; + // Push $_GET into $vars to be compatible with web interface naming foreach ($_GET as $name => $value) { $vars[$name] = $value; @@ -24,13 +26,12 @@ $legend = $vars['legend']; $output = (! empty($vars['output']) ? $vars['output'] : 'default'); $from = parse_at_time($_GET['from']) ?: Config::get('time.day'); $to = parse_at_time($_GET['to']) ?: Config::get('time.now'); -$graph_type = (isset($vars['graph_type']) ? $vars['graph_type'] : Config::get('webui.graph_type')); - $period = ($to - $from); -$base64_output = ''; $prev_from = ($from - $period); -$graphfile = Config::get('temp_dir') . '/' . strgen(); +$graph_image_type = $vars['graph_type'] ?? Config::get('webui.graph_type'); +$rrd_options = ''; +$auth = false; require Config::get('install_dir') . "/includes/html/graphs/$type/auth.inc.php"; @@ -47,61 +48,75 @@ if ($auth && is_customoid_graph($type, $subtype)) { // Graph Template Missing"); } -if ($error_msg) { +if (! empty($error_msg)) { // We have an error :( - graph_error($graph_error); -} elseif ($auth === null) { + graph_error($error_msg); + + return; +} + +if ($auth === null) { // We are unauthenticated :( graph_error($width < 200 ? 'No Auth' : 'No Authorization'); -} else { - // $rrd_options .= " HRULE:0#999999"; - if ($graph_type === 'svg') { - $rrd_options .= ' --imgformat=SVG'; - if ($width < 350) { - $rrd_options .= ' -m 0.75 -R light'; - } - } - if ($command_only) { - echo "
"; - echo "

RRDTool Command

"; - echo "
";
-        echo 'rrdtool ' . Rrd::buildCommand('graph', $graphfile, $rrd_options);
-        echo '
'; - $return = Rrd::graph($graphfile, $rrd_options); - echo "

RRDTool Output

"; - echo "
";
-        echo "$return";
-        echo '
'; - unlink($graphfile); - echo '
'; - } elseif ($no_file) { - graph_error($width < 200 ? 'No Data' : 'No Data file'); - } elseif ($rrd_options) { - Rrd::graph($graphfile, $rrd_options); - d_echo($rrd_cmd); - if (is_file($graphfile)) { - if (! \LibreNMS\Util\Debug::isEnabled()) { - set_image_type(); - if ($output === 'base64') { - $imagedata = file_get_contents($graphfile); - $base64_output = base64_encode($imagedata); - } else { - $fd = fopen($graphfile, 'r'); - fpassthru($fd); - fclose($fd); - } - } else { - echo `ls -l $graphfile`; - echo 'graph'; - } - unlink($graphfile); - } elseif (isset($rrd_filename) && ! Rrd::checkRrdExists($rrd_filename)) { - graph_error($width < 200 ? 'No Data' : 'No Data file'); - } else { - graph_error($width < 200 ? 'Draw Error' : 'Error Drawing Graph'); - } - } else { - graph_error($width < 200 ? 'Def Error' : 'Graph Definition Error'); + return; +} + +if ($graph_image_type === 'svg') { + $rrd_options .= ' --imgformat=SVG'; + if ($width < 350) { + $rrd_options .= ' -m 0.75 -R light'; + } +} + +// command output requested +if (! empty($command_only)) { + echo "
"; + echo "

RRDTool Command

"; + echo "
";
+    echo 'rrdtool ' . Rrd::buildCommand('graph', '-', $rrd_options);
+    echo '
'; + try { + Rrd::graph($rrd_options); + } catch (\LibreNMS\Exceptions\RrdGraphException $e) { + echo "

RRDTool Output

"; + echo "
";
+        echo $e->getMessage();
+        echo '
'; + } + echo '
'; + + return; +} + +// graph sent file not found flag +if (! empty($no_file)) { + graph_error($width < 200 ? 'No Data' : 'No Data file ' . $no_file); + + return; +} + +if (empty($rrd_options)) { + graph_error($width < 200 ? 'Def Error' : 'Graph Definition Error'); + + return; +} + +// Generating the graph! +try { + $image_data = Rrd::graph($rrd_options); + + // output the graph + if (\LibreNMS\Util\Debug::isEnabled()) { + echo 'graph'; + } else { + header('Content-type: ' . get_image_type(Config::get('webui.graph_type'))); + echo $output === 'base64' ? base64_encode($image_data) : $image_data; + } +} catch (\LibreNMS\Exceptions\RrdGraphException $e) { + if (isset($rrd_filename) && ! Rrd::checkRrdExists($rrd_filename)) { + graph_error($width < 200 ? 'No Data' : 'No Data file ' . basename($rrd_filename)); + } else { + graph_error($width < 200 ? 'Draw Error' : 'Error Drawing Graph: ' . $e->getMessage()); } } diff --git a/includes/html/pages/graphs.inc.php b/includes/html/pages/graphs.inc.php index ab58655ab5..702438be83 100644 --- a/includes/html/pages/graphs.inc.php +++ b/includes/html/pages/graphs.inc.php @@ -92,8 +92,8 @@ if (! $auth) { $link_array['page'] = 'graphs'; $link = \LibreNMS\Util\Url::generate($link_array); - echo ''; - echo '' . $text . '
'; + echo ''; + echo '' . $text . ''; echo ''; echo \LibreNMS\Util\Url::lazyGraphTag($graph_array); echo '';