<?php
/*
 * Copyright (c) 2003-2022 Willem Dijkstra
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *    - Redistributions of source code must retain the above copyright
 *      notice, this list of conditions and the following disclaimer.
 *    - Redistributions in binary form must reproduce the above
 *      copyright notice, this list of conditions and the following
 *      disclaimer in the documentation and/or other materials provided
 *      with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 */

/*
 * Metric class
 *
 * Generates rrdtool recipes by rewriting a template using a set of variables.
 */
require_once(__DIR__ . '/class_session.inc');
require_once(__DIR__ . '/class_cache.inc');
require_once(__DIR__ . '/setup.inc');
require_once(__DIR__ . '/class_vars.inc');
require_once(__DIR__ . '/metric_template.inc');
require_once(__DIR__ . '/tools.inc');
require_once(__DIR__ . '/class_rrdtool.inc');

class Metric {
    public $template;
    public $vars;
    public $definition;

    function __construct($machine, $type, $start, $stop, $step) {
        $this->vars = new Vars();
        $this->vars->set('start', $start);
        $this->vars->set('stop', $stop);
        $this->vars->set('step', $step);
        $this->_generate_host($machine, $type);
    }

    function _generate_host($machine, $grep) {
        global $symon;

        if (!is_dir($symon["host_tree"])) {
            config_error("host_tree", "does not point to a directory");
            return 0;
        }

        if (is_file($symon["host_tree"]."/".$machine)) {
            runtime_error('$symon["host_tree"] should contain machine directories, not files. ($symon["host_tree"]/cpu0.rrd => $symon["host_tree"]/localhost/cpu0.rrd)');
        }

        if ($machine != '.' &&
            $machine != '..' &&
            is_dir($symon["host_tree"]."/".$machine)) {
            $this->vars->set('host', $machine);
            $this->vars->set('dir', $symon["host_tree"].'/'.$machine);
            $this->vars->set('name', $machine);
            $rrd_dir = dir($symon["host_tree"].'/'.$machine);
            $files = [];
            while ($item = $rrd_dir->read()) {
                if ($item != '.' &&
                    $item != '..' &&
                    preg_match("/.rrd$/", $item) &&
                    (preg_match("/^".$grep."/", $item))) {
                    $files[] = $item;
                }
            }
            natsort($files);
            /* get combined metrics */
            foreach ($symon['combine'] as $type => $enabled) {
                if ($enabled) {
                    $combine = [];
                    $newfiles = [];
                    foreach ($files as $key => $item) {
                        if (preg_match("/^".$type."[0-9_]/", $item)) {
                            $combine[] = $item;
                        } else {
                            $newfiles[] = $item;
                        }
                    }

                    $files = $newfiles;

                    if ($combine !== []) {
                        foreach ($combine as $key => $item) {
                            $this->vars->set('rrdfile'.$key, $symon["host_tree"].'/'.$machine.'/'.$item);
                        }
                    }
                }
            }

            foreach ($files as $key => $item) {
                $this->vars->set('rrdfile', $symon["host_tree"].'/'.$machine.'/'.$item);
            }
        }
    }

    function _makevars() {
        $n = null;
        $template = null;
        global $session;
        global $symon;

        $this->definition = '';

        if ($this->vars->defp('rrdfile')) {
            $this->vars->addvars(parse_filename($this->vars->get('name'), $this->vars->get('rrdfile')));
        } elseif ($this->vars->defp('rrdfile0')) {
            $n = 0;
            $this->vars->addvars(parse_filename($this->vars->get('name'), $this->vars->get('rrdfile0')));
            while ($this->vars->defp('rrdfile'.$n)) {
                $this->vars->addvars(parse_filename($this->vars->get('name'), $this->vars->get('rrdfile'.$n), $n));
                $n++;
            }
        }

        if (!$this->vars->defp('template')) {
            if ($this->vars->defp('rrdtype0')) {
                $template = get_metric_combined_template($this->vars->get('rrdtype0'), $n);
            } elseif ($this->vars->defp('rrdtype')) {
                $template = get_metric_single_template($this->vars->get('rrdtype'));
            } else {
                warning('metric: cannot load template: filename does not yield a type ('. $this->vars->get('rrdfile'));
                if (isset($symon['metric_debug'])) {
                    $this->_display();
                }
            }
        } else {
            $template = $this->vars->get('template');
        }

        if ($template != "") {
            $this->template = preg_split("/\n/", (string) $template);
        } elseif (isset($symon['metric_debug'])) {
            $this->_display();
            warning('metric: template not set');
        }
    }

    function render() {
        global $cache;
        global $symon;

        $this->_makevars();

        if (isset($symon['metric_debug'])) {
            print "<pre>";
            $this->_display();
            print "</pre>";
        }

        $this->_preprocess();

        if (isset($symon['metric_debug'])) {
            print "<pre>\xa preprocessed template = ";
            if ($this->definition !== null && $this->definition != "") {
                foreach ($this->definition as $line) {
                    print "\xa    '$line'";
                }
            }
            print "</pre>";
        }

        $this->_constrain();

        if (isset($symon['metric_debug'])) {
            print "<pre>\xa constrained = ";
            if ($this->definition !== null && $this->definition != "") {
                foreach ($this->definition as $line) {
                    print "\xa    '$line'";
                }
            }
            print "</pre>";
        }
        $r = new RRDTool();
        $r->export($this->definition);
    }

    /* fill template with variables */
    function _preprocess() {
        $definition = '';
        if ($this->template === null || !is_array($this->template)) {
            return;
        }
        reset($this->template);

        foreach ($this->template as $t) {
            $startpos = strpos((string) $t, "%", 0);
            $endpos = $startpos + 1 > strlen((string) $t) ? false : strpos((string) $t, "%", $startpos + 1);
            while ($startpos !== false && $endpos !== false) {
                $leader = substr((string) $t, 0, $startpos);
                $key = substr((string) $t, $startpos + 1, $endpos - $startpos - 1);
                $tailer = substr((string) $t, $endpos + 1);

                $definition .= $leader;

                if ($this->vars->defp($key)) {
                    $definition .= $this->vars->get($key);
                } else {
                    $definition .= "%" . $key;
                    $tailer = "%" . $tailer;
                }

                $t = $tailer;

                $startpos=strpos($t, "%", 0);
                $endpos = $startpos + 1 > strlen($t) ? false : strpos($t, "%", $startpos + 1);
            }

            $definition .= $t . "\n";
        }

        $this->definition = preg_split("/\n/", $definition);
    }

    function _constrain() {
        global $symon;

        $definition = '';
        if (!is_array($this->definition)) {
            return 1;
        }

        reset($this->definition);
        foreach ($this->definition as $t) {
            if (preg_match("/^([^-]*)(-[^ ]+) ([^ ]+)(.*)$/", (string) $t)) {
                while (preg_match("/^([^-]*)(-[^ ]+) ([^ ]+)(.*)$/", (string) $t, $match)) {
                    $definition .= $match[1];
                    foreach ($symon['constraints'] as $k => $v) {
                        if ($k == $match[2]) {
                            if (is_array($symon['constraints'][$k])
                                && isset($symon['constraints'][$k]["max"])
                                && ($match[3] > $symon['constraints'][$k]["max"])) {
                                $match[3] = $symon['constraints'][$k]["max"];
                            }
                            if (is_array($symon['constraints'][$k])
                                && isset($symon['constraints'][$k]["min"])
                                && ($match[3] < $symon['constraints'][$k]["min"])) {
                                $match[3] = $symon['constraints'][$k]["min"];
                            }
                        }
                    }
                    $definition .= $match[2].' '.$match[3];
                    $t = $match[4];
                }
            }
            $definition .= $t . "\n";
        }
        $this->definition = preg_split("/\n/", $definition);
    }

    function _display() {
        print "\xa metric ";
        if ($this->template !== null) {
            print "\xa  template=";
            foreach ($this->template as $line) {
                print "\xa    '$line'";
            }
        }
        if ($this->vars !== null) {
            $vars = $this->vars->tostring();
            if (strlen((string) $vars) > 0) {
                print $vars;
            }
            print ";";
        }
    }
}
?>
