Subversion Repositories vnag

Rev

Rev 88 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

  1. <?php
  2.  
  3. /*
  4.  
  5.       VNag - Nagios Framework for PHP                  (C) 2014-2023
  6.       __     ___      _____ _     _       _     ____         __ _
  7.       \ \   / (_) __ |_   _| |__ (_)_ __ | | __/ ___|  ___  / _| |_
  8.        \ \ / /| |/ _` || | | '_ \| | '_ \| |/ /\___ \ / _ \| |_| __|
  9.         \ V / | | (_| || | | | | | | | | |   <  ___) | (_) |  _| |_
  10.          \_/  |_|\__,_||_| |_| |_|_|_| |_|_|\_\|____/ \___/|_|  \__|
  11.  
  12.       Developed by Daniel Marschall             www.viathinksoft.com
  13.       Licensed under the terms of the Apache 2.0 license
  14.       Revision 2023-12-17
  15.  
  16. */
  17.  
  18. /****************************************************************************************************
  19.  
  20. More information on how to develop your plugin, see doc/Plugin_Development.md
  21.  
  22. ****************************************************************************************************/
  23.  
  24. if (!VNag::is_http_mode()) error_reporting(E_ALL);
  25.  
  26. # If you want to use -t/--timeout with your module, you must add following line in your module code:
  27. // WONTFIX: declare(ticks=1) is deprecated? http://www.hackingwithphp.com/4/21/0/the-declare-function-and-ticks
  28. // WONTFIX: check is the main script used declare(ticks=1). (Not possible in PHP)
  29. declare(ticks=1);
  30.  
  31. # Attention: The -t/--timeout parameter does not respect the built-in set_time_limit() of PHP.
  32. # PHP should set this time limit to infinite.
  33. set_time_limit(0);
  34.  
  35. define('VNAG_JSONDATA_V1', 'oid:1.3.6.1.4.1.37476.2.3.1.1'); // {iso(1) identified-organization(3) dod(6) internet(1) private(4) enterprise(1) 37476 products(2) vnag(3) jsondata(1) v1(1)}
  36.  
  37. // Set this to an array to overwrite getopt() and $_REQUEST[], respectively.
  38. // Useful for mock tests.
  39. $OVERWRITE_ARGUMENTS = null;
  40.  
  41. function _empty($x) {
  42.         // Returns true for '' or null. Does not return true for value 0 or '0' (like empty() does)
  43.         return is_null($x) || (trim($x) == '');
  44. }
  45.  
  46. abstract class VNag {
  47.         /*public*/ const VNAG_VERSION = '2023-12-17';
  48.  
  49.         // Status 0..3 for STATUSMODEL_SERVICE (the default status model):
  50.         # The guideline states: "Higher-level errors (such as name resolution errors, socket timeouts, etc) are outside of the control of plugins and should generally NOT be reported as UNKNOWN states."
  51.         # We choose 4 as exitcode. The plugin developer is free to return any other status.
  52.         /*public*/ const STATUS_OK       = 0;
  53.         /*public*/ const STATUS_WARNING  = 1;
  54.         /*public*/ const STATUS_CRITICAL = 2;
  55.         /*public*/ const STATUS_UNKNOWN  = 3;
  56.         /*public*/ const STATUS_ERROR    = 4; // and upwards
  57.  
  58.         // Status 0..1 for STATUSMODEL_HOST:
  59.         // The page https://blog.centreon.com/good-practices-how-to-develop-monitoring-plugin-nagios/
  60.         // states that host plugins may return following status codes:
  61.         // 0=UP, 1=DOWN, Other=Maintains last known state
  62.         /*public*/ const STATUS_UP       = 0;
  63.         /*public*/ const STATUS_DOWN     = 1;
  64.         /*public*/ const STATUS_MAINTAIN = 2; // and upwards
  65.  
  66.         /*public*/ const VERBOSITY_SUMMARY                = 0;
  67.         /*public*/ const VERBOSITY_ADDITIONAL_INFORMATION = 1;
  68.         /*public*/ const VERBOSITY_CONFIGURATION_DEBUG    = 2;
  69.         /*public*/ const VERBOSITY_PLUGIN_DEBUG           = 3;
  70.         /*public*/ const MAX_VERBOSITY = self::VERBOSITY_PLUGIN_DEBUG;
  71.  
  72.         /*public*/ const STATUSMODEL_SERVICE = 0;
  73.         /*public*/ const STATUSMODEL_HOST    = 1;
  74.  
  75.         private $initialized = false;
  76.  
  77.         private $status = null;
  78.         private $messages = array(); // array of messages which are all put together into the headline, comma separated
  79.         private $verbose_info = ''; // all other lines
  80.         private $warningRanges = array();
  81.         private $criticalRanges = array();
  82.         private $performanceDataObjects = array();
  83.         private static $exitcode = 0;
  84.  
  85.         private $helpObj = null;
  86.         private $argHandler = null;
  87.  
  88.         /*public*/ const OUTPUT_NEVER     = 0;
  89.         /*public*/ const OUTPUT_SPECIAL   = 1; // illegal usage / help page, version page
  90.         /*public*/ const OUTPUT_NORMAL    = 2;
  91.         /*public*/ const OUTPUT_EXCEPTION = 4;
  92.         /*public*/ const OUTPUT_ALWAYS    = 7; // = OUTPUT_SPECIAL+OUTPUT_NORMAL+OUTPUT_EXCEPTION
  93.  
  94.         public $http_visual_output    = self::OUTPUT_ALWAYS; // show a human-readable panel? ...
  95.         public $http_invisible_output = self::OUTPUT_ALWAYS; // ... and/or output an invisible machine-readable tag?
  96.  
  97.         // $html_before and $html_after contain the output HTML which were sent by the user
  98.  
  99.         // before and after the visual output
  100.         protected $html_before = '';
  101.         protected $html_after = '';
  102.  
  103.         protected $statusmodel = self::STATUSMODEL_SERVICE;
  104.  
  105.         protected $show_status_in_headline = true;
  106.  
  107.         protected $default_status = self::STATUS_UNKNOWN;
  108.         protected $default_warning_range = null;
  109.         protected $default_critical_range = null;
  110.  
  111.         protected $argWarning;
  112.         protected $argCritical;
  113.         protected $argVersion;
  114.         protected $argVerbosity;
  115.         protected $argTimeout;
  116.         protected $argHelp;
  117.         protected $argUsage;
  118.  
  119.         // -----------------------------------------------------------
  120.  
  121.         // The ID will be used for writing AND reading of the machine-readable
  122.         // Nagios output embedded in a website. (A web-reader acts as proxy, so the
  123.         // input and output ID will be equal)
  124.         // Attention: Once you run run(), $id will be "used" and resetted to null.
  125.         // The ID can be any string, e.g. a GUID, an OID, a package name or something else.
  126.         // It should be unique. If you don't set an ID, a serial number (0, 1, 2, 3, ...) will be
  127.         // used for your outputs.
  128.         public $id = null;
  129.         protected static $http_serial_number = 0;
  130.  
  131.         // -----------------------------------------------------------
  132.  
  133.         // Private key: Optional feature used in writeInvisibleHTML (called by run in HTTP mode) in order to sign/encrypt the output
  134.         public $privkey = null;
  135.         public $privkey_password = null;
  136.         public $sign_algo = null; // default: OPENSSL_ALGO_SHA256
  137.  
  138.         // Public key: Optional feature used in a web-reader [readInvisibleHTML) to check the integrity of a message
  139.         public $pubkey = null;
  140.  
  141.         // -----------------------------------------------------------
  142.  
  143.         // These settings should be set by derivated classes where the user intuitively expects the
  144.         // warning (w) or critical (c) parameter to mean something else than defined in the development guidelines.
  145.         // Usually, the single value "-w X" means the same like "-w X:X", which means everything except X is bad.
  146.         // This behavior is VNag::SINGLEVALUE_RANGE_DEFAULT.
  147.         // But for plugins e.g. for checking disk space, the user expects the argument "-w X" to mean
  148.         // "everything below X is bad" (if X is defined as free disk space).
  149.         // So we would choose the setting VNag::SINGLEVALUE_RANGE_VAL_LT_X_BAD.
  150.         // Note: This setting is implemented as array, so that each range number (in case you want to have more
  151.         //       than one range, like in the PING plugin that checks latency and package loss)
  152.         //       can have its individual behavior for single values.
  153.         protected $warningSingleValueRangeBehaviors  = array(self::SINGLEVALUE_RANGE_DEFAULT);
  154.         protected $criticalSingleValueRangeBehaviors = array(self::SINGLEVALUE_RANGE_DEFAULT);
  155.  
  156.         // Default behavior according to the development guidelines:
  157.         //  x means  x:x, which means, everything except x% is bad.
  158.         // @x means @x:x, which means, x is bad and everything else is good.
  159.         const SINGLEVALUE_RANGE_DEFAULT = 0;
  160.  
  161.         // The single value x means, everything > x is bad. @x is not defined.
  162.         const SINGLEVALUE_RANGE_VAL_GT_X_BAD = 1;
  163.  
  164.         // The single value x means, everything >= x is bad. @x is not defined.
  165.         const SINGLEVALUE_RANGE_VAL_GE_X_BAD = 2;
  166.  
  167.         // The single value x means, everything < x is bad. @x is not defined.
  168.         const SINGLEVALUE_RANGE_VAL_LT_X_BAD = 3;
  169.  
  170.         // The single value x means, everything <= x is bad. @x is not defined.
  171.         const SINGLEVALUE_RANGE_VAL_LE_X_BAD = 4;
  172.  
  173.         // -----------------------------------------------------------
  174.  
  175.         // Encryption password: Optional feature used in writeInvisibleHTML (called by run in HTTP mode)
  176.         public $password_out = null;
  177.  
  178.         // Decryption password: Used in readInvisibleHTML to decrypt an encrypted machine-readable info
  179.         public $password_in = null;
  180.  
  181.         // -----------------------------------------------------------
  182.  
  183.         public static function is_http_mode() {
  184.                 return php_sapi_name() !== 'cli';
  185.         }
  186.  
  187.         public function getHelpManager() {
  188.                 return $this->helpObj;
  189.         }
  190.  
  191.         public function getArgumentHandler() {
  192.                 return $this->argHandler;
  193.         }
  194.  
  195.         public function outputHTML($text, $after_visual_output=true) {
  196.                 if ($this->is_http_mode()) {
  197.                         if ($this->initialized) {
  198.                                 if ($after_visual_output) {
  199.                                         $this->html_after .= $text;
  200.                                 } else {
  201.                                         $this->html_before .= $text;
  202.                                 }
  203.                         } else {
  204.                                 echo $text;
  205.                         }
  206.                 }
  207.         }
  208.  
  209.         protected function resetArguments() {
  210.                 $this->argWarning   = null;
  211.                 $this->argCritical  = null;
  212.                 $this->argVersion   = null;
  213.                 $this->argVerbosity = null;
  214.                 $this->argTimeout   = null;
  215.                 $this->argHelp      = null;
  216.                 // $this->argUsage  = null;
  217.  
  218.                 // Also remove cache
  219.                 $this->argWarning   = null;
  220.                 $this->argCritical  = null;
  221.         }
  222.  
  223.         // e.g. $args = "wcVvht"
  224.         public function registerExpectedStandardArguments($args) {
  225.                 $this->resetArguments();
  226.  
  227.                 for ($i=0; $i<strlen($args); $i++) {
  228.                         switch ($args[$i]) {
  229.                                 case 'w':
  230.                                         $this->addExpectedArgument($this->argWarning   = new VNagArgument('w', 'warning',  VNagArgument::VALUE_REQUIRED,  VNagLang::$argname_value, VNagLang::$warning_range));
  231.                                         break;
  232.                                 case 'c':
  233.                                         $this->addExpectedArgument($this->argCritical  = new VNagArgument('c', 'critical', VNagArgument::VALUE_REQUIRED,  VNagLang::$argname_value, VNagLang::$critical_range));
  234.                                         break;
  235.                                 case 'V':
  236.                                         $this->addExpectedArgument($this->argVersion   = new VNagArgument('V', 'version',  VNagArgument::VALUE_FORBIDDEN, null, VNagLang::$prints_version));
  237.                                         break;
  238.                                 case 'v':
  239.                                         // In HTTP: -vvv is &v[]=&v[]=&v[]=
  240.                                         $this->addExpectedArgument($this->argVerbosity = new VNagArgument('v', 'verbose',  VNagArgument::VALUE_FORBIDDEN, null, VNagLang::$verbosity_helptext));
  241.                                         break;
  242.                                 case 't':
  243.                                         // Attention: not every plugin supports it because of declare(ticks=1) needs to be written in the main script
  244.                                         $this->addExpectedArgument($this->argTimeout   = new VNagArgument('t', 'timeout',  VNagArgument::VALUE_REQUIRED,  VNagLang::$argname_seconds, VNagLang::$timeout_helptext));
  245.                                         break;
  246.                                 // case '?':
  247.                                 case 'h':
  248.                                         $this->addExpectedArgument($this->argHelp      = new VNagArgument('h', 'help',     VNagArgument::VALUE_FORBIDDEN, null, VNagLang::$help_helptext));
  249.                                         break;
  250.                                 default:
  251.                                         $letter = $args[$i];
  252.                                         throw new VNagInvalidStandardArgument(sprintf(VNagLang::$no_standard_arguments_with_letter, $letter));
  253.                                         #break;
  254.                         }
  255.                 }
  256.         }
  257.  
  258.         public function addExpectedArgument($argObj) {
  259.                 // Emulate C++ "friend" access to hidden functions
  260.  
  261.                 // $this->helpObj->_addOption($argObj);
  262.                 $helpObjAddEntryMethod = new ReflectionMethod($this->helpObj, '_addOption');
  263.                 $helpObjAddEntryMethod->setAccessible(true);
  264.                 $helpObjAddEntryMethod->invoke($this->helpObj, $argObj);
  265.  
  266.                 // $this->argHandler->_addExpectedArgument($argObj);
  267.                 $argHandlerAddEntryMethod = new ReflectionMethod($this->argHandler, '_addExpectedArgument');
  268.                 $argHandlerAddEntryMethod->setAccessible(true);
  269.                 $argHandlerAddEntryMethod->invoke($this->argHandler, $argObj);
  270.         }
  271.  
  272.         protected function createArgumentHandler() {
  273.                 $this->argHandler = new VNagArgumentHandler();
  274.         }
  275.  
  276.         protected function createHelpObject() {
  277.                 $this->helpObj = new VNagHelp();
  278.         }
  279.  
  280.         protected function checkInitialized() {
  281.                 if (!$this->initialized) throw new VNagFunctionCallOutsideSession();
  282.         }
  283.  
  284.         protected function getVerbosityLevel() {
  285.                 $this->checkInitialized(); // if (!$this->initialized) return false;
  286.  
  287.                 if (!isset($this->argVerbosity)) {
  288.                         //The verbose argument is always optional
  289.                         //throw new VNagRequiredArgumentNotRegistered('-v');
  290.                         return self::VERBOSITY_SUMMARY;
  291.                 } else {
  292.                         $level = $this->argVerbosity->count();
  293.                         if ($level > self::MAX_VERBOSITY) $level = self::MAX_VERBOSITY;
  294.                         return $level;
  295.                 }
  296.         }
  297.  
  298.         public function getWarningRange($argumentNumber=0) {
  299.                 $this->checkInitialized(); // if (!$this->initialized) return false;
  300.  
  301.                 if (!isset($this->warningRanges[$argumentNumber])) {
  302.                         if (!is_null($this->argWarning)) {
  303.                                 $warning = $this->argWarning->getValue();
  304.                                 if (!is_null($warning)) {
  305.                                         $vals = explode(',',$warning);
  306.                                         foreach ($vals as $number => $val) {
  307.                                                 if (_empty($val)) {
  308.                                                         $this->warningRanges[$number] = null;
  309.                                                 } else {
  310.                                                         $singleValueBehavior = isset($this->warningSingleValueRangeBehaviors[$number]) ? $this->warningSingleValueRangeBehaviors[$number] : VNag::SINGLEVALUE_RANGE_DEFAULT;
  311.                                                         $this->warningRanges[$number] = new VNagRange($val, $singleValueBehavior);
  312.                                                 }
  313.                                         }
  314.                                 } else {
  315.                                         $this->warningRanges[0] = $this->default_warning_range;
  316.                                 }
  317.                         } else {
  318.                                 return null;
  319.                         }
  320.                 }
  321.  
  322.                 if (isset($this->warningRanges[$argumentNumber])) {
  323.                         return $this->warningRanges[$argumentNumber];
  324.                 } else {
  325.                         return null;
  326.                 }
  327.         }
  328.  
  329.         public function getCriticalRange($argumentNumber=0) {
  330.                 $this->checkInitialized(); // if (!$this->initialized) return false;
  331.  
  332.                 if (!isset($this->criticalRanges[$argumentNumber])) {
  333.                         if (!is_null($this->argCritical)) {
  334.                                 $critical = $this->argCritical->getValue();
  335.                                 if (!is_null($critical)) {
  336.                                         $vals = explode(',',$critical);
  337.                                         foreach ($vals as $number => $val) {
  338.                                                 $singleValueBehavior = isset($this->criticalSingleValueRangeBehaviors[$number]) ? $this->criticalSingleValueRangeBehaviors[$number] : VNag::SINGLEVALUE_RANGE_DEFAULT;
  339.                                                 $this->criticalRanges[$number] = new VNagRange($val, $singleValueBehavior);
  340.                                         }
  341.                                 } else {
  342.                                         $this->criticalRanges[0] = $this->default_critical_range;
  343.                                 }
  344.                         } else {
  345.                                 return null;
  346.                         }
  347.                 }
  348.  
  349.                 if (isset($this->criticalRanges[$argumentNumber])) {
  350.                         return $this->criticalRanges[$argumentNumber];
  351.                 } else {
  352.                         return null;
  353.                 }
  354.         }
  355.  
  356.         public function checkAgainstWarningRange($values, $force=true, $autostatus=true, $argumentNumber=0) {
  357.                 $this->checkInitialized(); // if (!$this->initialized) return;
  358.  
  359.                 if (!$this->getArgumentHandler()->isArgRegistered('w')) {
  360.                         // Developer's mistake: The argument is not in the list of expected arguments
  361.                         throw new VNagRequiredArgumentNotRegistered('-w');
  362.                 }
  363.  
  364.                 $wr = $this->getWarningRange($argumentNumber);
  365.                 if (isset($wr)) {
  366.                         if ($wr->checkAlert($values)) {
  367.                                 if ($autostatus) $this->setStatus(VNag::STATUS_WARNING);
  368.                                 return true;
  369.                         } else {
  370.                                 if ($autostatus) $this->setStatus(VNag::STATUS_OK);
  371.                                 return false;
  372.                         }
  373.                 } else {
  374.                         if ($force) {
  375.                                 // User's mistake: They did not pass the argument to the plugin
  376.                                 if (($argumentNumber > 0) && (count($this->warningRanges) > 0)) {
  377.                                         throw new VNagInvalidArgumentException(sprintf(VNagLang::$too_few_warning_ranges, $argumentNumber+1));
  378.                                 } else {
  379.                                         throw new VNagRequiredArgumentMissing('-w');
  380.                                 }
  381.                         }
  382.                 }
  383.         }
  384.  
  385.         public function checkAgainstCriticalRange($values, $force=true, $autostatus=true, $argumentNumber=0) {
  386.                 $this->checkInitialized(); // if (!$this->initialized) return;
  387.  
  388.                 if (!$this->getArgumentHandler()->isArgRegistered('c')) {
  389.                         // Developer's mistake: The argument is not in the list of expected arguments
  390.                         throw new VNagRequiredArgumentNotRegistered('-c');
  391.                 }
  392.  
  393.                 $cr = $this->getCriticalRange($argumentNumber);
  394.                 if (isset($cr)) {
  395.                         if ($cr->checkAlert($values)) {
  396.                                 if ($autostatus) $this->setStatus(VNag::STATUS_CRITICAL);
  397.                                 return true;
  398.                         } else {
  399.                                 if ($autostatus) $this->setStatus(VNag::STATUS_OK);
  400.                                 return false;
  401.                         }
  402.                 } else {
  403.                         if ($force) {
  404.                                 // User's mistake: They did not pass the argument to the plugin
  405.                                 if (($argumentNumber > 0) && (count($this->warningRanges) > 0)) {
  406.                                         throw new VNagInvalidArgumentException(sprintf(VNagLang::$too_few_critical_ranges, $argumentNumber+1));
  407.                                 } else {
  408.                                         throw new VNagRequiredArgumentMissing('-c');
  409.                                 }
  410.                         }
  411.                 }
  412.         }
  413.  
  414.         protected static function getBaddestExitcode($code1, $code2) {
  415.                 return max($code1, $code2);
  416.         }
  417.  
  418.         # DO NOT CALL MANUALLY
  419.         # Unfortunately, this function has to be public, otherwise register_shutdown_function() wouldn't work
  420.         public static function _shutdownHandler() {
  421.                 if (!self::is_http_mode()) {
  422.                         exit((int)self::$exitcode);
  423.                 }
  424.         }
  425.  
  426.         protected function _exit($code) {
  427.                 self::$exitcode = $this->getBaddestExitcode($code, self::$exitcode);
  428.         }
  429.  
  430.         private $constructed = false;
  431.         function __construct() {
  432.                 $this->createHelpObject();
  433.                 $this->createArgumentHandler();
  434.  
  435.                 $this->addExpectedArgument($this->argUsage = new VNagArgument('?', '', VNagArgument::VALUE_FORBIDDEN, null, VNagLang::$prints_usage));
  436.  
  437.                 $this->constructed = true;
  438.         }
  439.  
  440.         function __destruct() {
  441.                 if (Timeouter::started()) {
  442.                         Timeouter::end();
  443.                 }
  444.         }
  445.  
  446.         public function run() {
  447.                 global $inside_vnag_run;
  448.  
  449.                 $inside_vnag_run = true;
  450.                 try {
  451.                         if (!$this->constructed) {
  452.                                 throw new VNagNotConstructed(VNagLang::$notConstructed);
  453.                         }
  454.  
  455.                         try {
  456.                                 $this->initialized = true;
  457.                                 $this->html_before = '';
  458.                                 $this->html_after = '';
  459.                                 $this->setStatus(null, true);
  460.                                 $this->messages = array();
  461.  
  462.                                 register_shutdown_function(array($this, '_shutdownHandler'));
  463.  
  464.                                 if ($this->argHandler->illegalUsage()) {
  465.                                         $content = $this->helpObj->printUsagePage();
  466.                                         $this->setStatus(VNag::STATUS_UNKNOWN);
  467.  
  468.                                         if ($this->is_http_mode()) {
  469.                                                 echo $this->html_before;
  470.                                                 if ($this->http_visual_output    & VNag::OUTPUT_SPECIAL) echo $this->writeVisualHTML($content);
  471.                                                 if ($this->http_invisible_output & VNag::OUTPUT_SPECIAL) echo $this->writeInvisibleHTML($content);
  472.                                                 echo $this->html_after;
  473.                                                 return; // cancel
  474.                                         } else {
  475.                                                 echo $content;
  476.                                                 return $this->_exit($this->status);
  477.                                         }
  478.                                 }
  479.  
  480.                                 if (!is_null($this->argVersion) && ($this->argVersion->available())) {
  481.                                         $content = $this->helpObj->printVersionPage();
  482.                                         $this->setStatus(VNag::STATUS_UNKNOWN);
  483.  
  484.                                         if ($this->is_http_mode()) {
  485.                                                 echo $this->html_before;
  486.                                                 if ($this->http_visual_output    & VNag::OUTPUT_SPECIAL) echo $this->writeVisualHTML($content);
  487.                                                 if ($this->http_invisible_output & VNag::OUTPUT_SPECIAL) echo $this->writeInvisibleHTML($content);
  488.                                                 echo $this->html_after;
  489.                                                 return; // cancel
  490.                                         } else {
  491.                                                 echo $content;
  492.                                                 return $this->_exit($this->status);
  493.                                         }
  494.                                 }
  495.  
  496.                                 if (!is_null($this->argHelp) && ($this->argHelp->available())) {
  497.                                         $content = $this->helpObj->printHelpPage();
  498.                                         $this->setStatus(VNag::STATUS_UNKNOWN);
  499.  
  500.                                         if ($this->is_http_mode()) {
  501.                                                 echo $this->html_before;
  502.                                                 if ($this->http_visual_output    & VNag::OUTPUT_SPECIAL) echo $this->writeVisualHTML($content);
  503.                                                 if ($this->http_invisible_output & VNag::OUTPUT_SPECIAL) echo $this->writeInvisibleHTML($content);
  504.                                                 echo $this->html_after;
  505.                                                 return; // cancel
  506.                                         } else {
  507.                                                 echo $content;
  508.                                                 return $this->_exit($this->status);
  509.                                         }
  510.                                 }
  511.  
  512.                                 // Initialize ranges (and check their validity)
  513.                                 $this->getWarningRange();
  514.                                 $this->getCriticalRange();
  515.  
  516.                                 if (!is_null($this->argTimeout)) {
  517.                                         $timeout = $this->argTimeout->getValue();
  518.                                         if (!is_null($timeout)) {
  519.                                                 Timeouter::start($timeout);
  520.                                         }
  521.                                 }
  522.  
  523.                                 ob_start();
  524.                                 $init_ob_level = ob_get_level();
  525.                                 try {
  526.                                         $this->cbRun();
  527.  
  528.                                         // This will NOT be put in the 'finally' block, because otherwise it would trigger if an Exception happened (Which clears the OB)
  529.                                         if (ob_get_level() < $init_ob_level) throw new VNagImplementationErrorException(VNagLang::$output_level_lowered);
  530.                                 } finally {
  531.                                         while (ob_get_level() > $init_ob_level) @ob_end_clean();
  532.                                 }
  533.  
  534.                                 if (is_null($this->status)) $this->setStatus($this->default_status,true);
  535.  
  536.                                 $outputType = VNag::OUTPUT_NORMAL;
  537.                         } catch (Exception $e) {
  538.                                 $this->handleException($e);
  539.                                 $outputType = VNag::OUTPUT_EXCEPTION;
  540.                         }
  541.  
  542.                         if ($this->is_http_mode()) {
  543.                                 echo $this->html_before;
  544.                                 if ($this->http_invisible_output & $outputType) {
  545.                                         echo $this->writeInvisibleHTML();
  546.                                 }
  547.                                 if ($this->http_visual_output & $outputType) {
  548.                                         echo $this->writeVisualHTML();
  549.                                 }
  550.                                 echo $this->html_after;
  551.                         } else {
  552.                                 echo $this->getNagiosConsoleText();
  553.                                 return $this->_exit($this->status);
  554.                         }
  555.  
  556.                         Timeouter::end();
  557.                 } finally {
  558.                         $inside_vnag_run = false;
  559.                 }
  560.         }
  561.  
  562.         private function getNagiosConsoleText() {
  563.                 // see https://nagios-plugins.org/doc/guidelines.html#AEN200
  564.                 // 1. space separated list of label/value pairs
  565.                 $ary_perfdata = $this->getPerformanceData();
  566.                 $performancedata_first = array_shift($ary_perfdata);
  567.                 $performancedata_rest  = implode(' ', $ary_perfdata);
  568.  
  569.                 $status_text = VNagLang::status($this->status, $this->statusmodel);
  570.                 if (_empty($this->getHeadline())) {
  571.                         $content = $status_text;
  572.                 } else {
  573.                         if ($this->show_status_in_headline) {
  574.                                 $content = $status_text.': '.$this->getHeadline();
  575.                         } else {
  576.                                 $content = $this->getHeadline();
  577.                         }
  578.                 }
  579.  
  580.                 if (!_empty($performancedata_first)) $content .= '|'.trim($performancedata_first);
  581.                 $content .= "\n";
  582.                 if (!_empty($this->verbose_info)) {
  583.                         //$content .= "\n".VNagLang::$verbose_info.":\n\n";
  584.                         $content .= trim($this->verbose_info);
  585.                 }
  586.                 if (!_empty($performancedata_rest)) $content .= '|'.trim($performancedata_rest);
  587.                 $content .= "\n";
  588.  
  589.                 return trim($content)."\n";
  590.         }
  591.  
  592.         abstract protected function cbRun();
  593.  
  594.         public function addPerformanceData($prefDataObj, $move_to_font=false, $verbosityLevel=VNag::VERBOSITY_SUMMARY) {
  595.                 $this->checkInitialized(); // if (!$this->initialized) return;
  596.  
  597.                 if ((!isset($this->argVerbosity)) && ($verbosityLevel > VNag::VERBOSITY_SUMMARY)) throw new VNagRequiredArgumentNotRegistered('-v');
  598.                 if (self::getVerbosityLevel() < $verbosityLevel) return false;
  599.  
  600.                 if ($move_to_font) {
  601.                         array_unshift($this->performanceDataObjects, $prefDataObj);
  602.                 } else {
  603.                         $this->performanceDataObjects[] = $prefDataObj;
  604.                 }
  605.  
  606.                 return true;
  607.         }
  608.  
  609.         public function getPerformanceData() {
  610.                 $this->checkInitialized(); // if (!$this->initialized) return null;
  611.  
  612.                 // see https://nagios-plugins.org/doc/guidelines.html#AEN200
  613.                 // 1. space separated list of label/value pairs
  614.                 return $this->performanceDataObjects;
  615.         }
  616.  
  617.         public function removePerformanceData($prefDataObj) {
  618.                 if (($key = array_search($prefDataObj, $this->performanceDataObjects, true)) !== false) {
  619.                         unset($this->performanceDataObjects[$key]);
  620.                         return true;
  621.                 } else {
  622.                         return false;
  623.                 }
  624.         }
  625.  
  626.         public function clearPerformanceData() {
  627.                 $this->performanceDataObjects = array();
  628.         }
  629.  
  630.         public function getVerboseInfo() {
  631.                 return $this->verbose_info;
  632.         }
  633.  
  634.         public function clearVerboseInfo() {
  635.                 $this->verbose_info = '';
  636.         }
  637.  
  638.         private function writeVisualHTML($special_content=null) {
  639.                 if (!_empty($special_content)) {
  640.                         $content = $special_content;
  641.                 } else {
  642.                         $content = strtoupper(VNagLang::$status.': '.VNagLang::status($this->status, $this->statusmodel))."\n\n";
  643.  
  644.                         $content .= strtoupper(VNagLang::$message).":\n";
  645.                         $status_text = VNagLang::status($this->status, $this->statusmodel);
  646.                         if (_empty($this->getHeadline())) {
  647.                                 $content .= $status_text;
  648.                         } else {
  649.                                 if ($this->show_status_in_headline) {
  650.                                         $content .= $status_text.': '.trim($this->getHeadline());
  651.                                 } else {
  652.                                         $content .= trim($this->getHeadline());
  653.                                 }
  654.                         }
  655.                         $content .= "\n\n";
  656.  
  657.                         if (!_empty($this->verbose_info)) {
  658.                                 $content .= strtoupper(VNagLang::$verbose_info).":\n".trim($this->verbose_info)."\n\n";
  659.                         }
  660.  
  661.                         $perfdata = $this->getPerformanceData();
  662.                         if (count($perfdata) > 0) {
  663.                                 $content .= strtoupper(VNagLang::$performance_data).":\n";
  664.                                 foreach ($perfdata as $pd) {
  665.                                         $content .= trim($pd)."\n";
  666.                                 }
  667.                                 $content .= "\n";
  668.                         }
  669.                 }
  670.  
  671.                 $colorinfo = '';
  672.                 $status = $this->getStatus();
  673.  
  674.                 if ($status == VNag::STATUS_OK)                 $colorinfo = ' style="background-color:green;color:white;font-weight:bold"';
  675.                 else if ($status == VNag::STATUS_WARNING)       $colorinfo = ' style="background-color:yellow;color:black;font-weight:bold"';
  676.                 else if ($status == VNag::STATUS_CRITICAL)      $colorinfo = ' style="background-color:red;color:white;font-weight:bold"';
  677.                 else if ($status == VNag::STATUS_ERROR)         $colorinfo = ' style="background-color:purple;color:white;font-weight:bold"';
  678.                 else /* if ($status == VNag::STATUS_UNKNOWN) */ $colorinfo = ' style="background-color:lightgray;color:black;font-weight:bold"';
  679.  
  680.                 $html_content = trim($content);
  681.                 $html_content = htmlentities($html_content);
  682.                 $html_content = str_replace(' ', '&nbsp;', $html_content);
  683.                 $html_content = nl2br($html_content);
  684.  
  685.                 // FUT: Allow individual design via CSS
  686.                 return '<table border="1" cellspacing="2" cellpadding="2" style="width:100%" class="vnag_table">'.
  687.                         '<tr'.$colorinfo.' class="vnag_title_row">'.
  688.                         '<td>'.VNagLang::$nagios_output.'</td>'.
  689.                         '</tr>'.
  690.                         '<tr class="vnag_message_row">'.
  691.                         '<td><code>'.
  692.                         $html_content.
  693.                         '</code></td>'.
  694.                         '</tr>'.
  695.                         '</table>';
  696.         }
  697.  
  698.         protected function readInvisibleHTML($html) {
  699.                 $this->checkInitialized(); // if (!$this->initialized) return;
  700.  
  701.                 $doc = new DOMDocument(); // Requires: aptitude install php-dom
  702.                 @$doc->loadHTML($html);   // added '@' because we don't want a warning for the non-standard <vnag> tag
  703.  
  704.                 $tags = $doc->getElementsByTagName('script');
  705.                 foreach ($tags as $tag) {
  706.                         $type = $tag->getAttribute('type');
  707.                         if ($type !== 'application/json') continue;
  708.  
  709.                         $json = $tag->nodeValue;
  710.                         if (!$json) continue;
  711.  
  712.                         $data = @json_decode($json,true);
  713.                         if (!is_array($data)) continue;
  714.  
  715.                         if (!isset($data['type'])) continue;
  716.                         if ($data['type'] === VNAG_JSONDATA_V1) {
  717.                                 if (!isset($data['datasets'])) throw new VNagWebInfoException(VNagLang::$dataset_missing);
  718.                                 foreach ($data['datasets'] as $dataset) {
  719.                                         $payload = base64_decode($dataset['payload']);
  720.                                         if (!$payload) {
  721.                                                 throw new VNagWebInfoException(VNagLang::$payload_not_base64);
  722.                                         }
  723.  
  724.                                         if (isset($dataset['encryption'])) {
  725.                                                 // The dataset is encrypted. We need to decrypt it first.
  726.  
  727.                                                 $cryptInfo = $dataset['encryption'];
  728.                                                 if (!is_array($cryptInfo)) {
  729.                                                         throw new VNagWebInfoException(VNagLang::$dataset_encryption_no_array);
  730.                                                 }
  731.  
  732.                                                 $password = is_null($this->password_in) ? '' : $this->password_in;
  733.  
  734.                                                 $salt = base64_decode($cryptInfo['salt']);
  735.  
  736.                                                 if ($cryptInfo['hash'] != hash('sha256',$salt.$password)) {
  737.                                                         if ($password == '') {
  738.                                                                 throw new VNagWebInfoException(VNagLang::$require_password);
  739.                                                         } else {
  740.                                                                 throw new VNagWebInfoException(VNagLang::$wrong_password);
  741.                                                         }
  742.                                                 }
  743.  
  744.                                                 if (!function_exists('openssl_decrypt')) {
  745.                                                         throw new VNagException(VNagLang::$openssl_missing);
  746.                                                 }
  747.  
  748.                                                 $payload = openssl_decrypt($payload, $cryptInfo['method'], $password, 0, $cryptInfo['iv']);
  749.                                         }
  750.  
  751.                                         if (!is_null($this->pubkey) && ($this->pubkey !== '')) {
  752.                                                 if (substr($this->pubkey,0,3) === '---') {
  753.                                                         $public_key = $this->pubkey;
  754.                                                 } else {
  755.                                                         if (!file_exists($this->pubkey)) {
  756.                                                                 throw new VNagInvalidArgumentException(sprintf(VNagLang::$pubkey_file_not_found, $this->pubkey));
  757.                                                         }
  758.  
  759.                                                         $public_key = @file_get_contents($this->pubkey);
  760.                                                         if (!$public_key) {
  761.                                                                 throw new VNagPublicKeyException(sprintf(VNagLang::$pubkey_file_not_readable, $this->pubkey));
  762.                                                         }
  763.                                                 }
  764.  
  765.                                                 if (!isset($dataset['signature'])) {
  766.                                                         throw new VNagSignatureException(VNagLang::$signature_missing);
  767.                                                 }
  768.  
  769.                                                 $signature = base64_decode($dataset['signature']);
  770.                                                 if (!$signature) {
  771.                                                         throw new VNagSignatureException(VNagLang::$signature_not_bas64);
  772.                                                 }
  773.  
  774.                                                 if (!function_exists('openssl_verify')) {
  775.                                                         throw new VNagException(VNagLang::$openssl_missing);
  776.                                                 }
  777.  
  778.                                                 $sign_algo = is_null($this->sign_algo) ? OPENSSL_ALGO_SHA256 : $this->sign_algo;
  779.                                                 if (!openssl_verify($payload, $signature, $public_key, $sign_algo)) {
  780.                                                         throw new VNagSignatureException(VNagLang::$signature_invalid);
  781.                                                 }
  782.                                         }
  783.  
  784.                                         $payload = @json_decode($payload,true);
  785.                                         if ($payload === null) {
  786.                                                 throw new VNagWebInfoException(VNagLang::$payload_not_json);
  787.                                         }
  788.  
  789.                                         if ($payload['id'] == $this->id) {
  790.                                                 return $payload;
  791.                                         }
  792.                                 }
  793.                         }
  794.                 }
  795.  
  796.                 return null;
  797.         }
  798.  
  799.         private function getNextMonitorID($peek=false) {
  800.                 $result = is_null($this->id) ? self::$http_serial_number : $this->id;
  801.  
  802.                 if (!$peek) {
  803.                         $this->id = null; // use manual ID only once
  804.                         self::$http_serial_number++;
  805.                 }
  806.  
  807.                 return $result;
  808.         }
  809.  
  810.         private function writeInvisibleHTML($special_content=null) {
  811.                 // 1. Create the payload
  812.  
  813.                 $payload['id'] = $this->getNextMonitorID();
  814.  
  815.                 $payload['status'] = $this->getStatus();
  816.  
  817.                 if (!_empty($special_content)) {
  818.                         $payload['text'] = $special_content;
  819.                 } else {
  820.                         $payload['headline'] = $this->getHeadline();
  821.                         $payload['verbose_info'] = $this->verbose_info;
  822.  
  823.                         $payload['performance_data'] = array();
  824.                         foreach ($this->performanceDataObjects as $perfdata) {
  825.                                 $payload['performance_data'][] = (string)$perfdata;
  826.                         }
  827.                 }
  828.  
  829.                 $payload = json_encode($payload);
  830.  
  831.                 // 2. Encode the payload as JSON and optionally sign and/or encrypt it
  832.  
  833.                 $dataset = array();
  834.  
  835.                 if (!is_null($this->privkey) && ($this->privkey !== '')) {
  836.                         if (!function_exists('openssl_pkey_get_private') || !function_exists('openssl_sign')) {
  837.                                 throw new VNagException(VNagLang::$openssl_missing);
  838.                         }
  839.  
  840.                         if (substr($this->privkey,0,3) === '---') {
  841.                                 $pkeyid = @openssl_pkey_get_private($this->privkey, $this->privkey_password);
  842.                                 if (!$pkeyid) {
  843.                                         throw new VNagPrivateKeyException(sprintf(VNagLang::$privkey_not_readable));
  844.                                 }
  845.                         } else {
  846.                                 if (!file_exists($this->privkey)) {
  847.                                         throw new VNagInvalidArgumentException(sprintf(VNagLang::$privkey_file_not_found, $this->privkey));
  848.                                 }
  849.                                 $pkeyid = @openssl_pkey_get_private('file://'.$this->privkey, $this->privkey_password);
  850.                                 if (!$pkeyid) {
  851.                                         throw new VNagPrivateKeyException(sprintf(VNagLang::$privkey_file_not_readable, $this->privkey));
  852.                                 }
  853.                         }
  854.  
  855.                         $signature = '';
  856.                         $sign_algo = is_null($this->sign_algo) ? OPENSSL_ALGO_SHA256 : $this->sign_algo;
  857.                         if (@openssl_sign($payload, $signature, $pkeyid, $sign_algo)) {
  858.                                 if (version_compare(PHP_VERSION, '8.0.0') < 0) {
  859.                                         openssl_free_key($pkeyid);
  860.                                 }
  861.  
  862.                                 $dataset['signature'] = base64_encode($signature);
  863.                         } else {
  864.                                 throw new VNagPrivateKeyException(sprintf(VNagLang::$signature_failed));
  865.                         }
  866.                 }
  867.  
  868.                 if (!is_null($this->password_out) && ($this->password_out !== '')) {
  869.                         if (!function_exists('openssl_encrypt')) {
  870.                                 throw new VNagException(VNagLang::$openssl_missing);
  871.                         }
  872.  
  873.                         $password = $this->password_out;
  874.  
  875.                         $method = 'aes-256-ofb';
  876.                         $iv = substr(hash('sha256', openssl_random_pseudo_bytes(32)), 0, 16);
  877.                         $salt = openssl_random_pseudo_bytes(32);
  878.  
  879.                         $cryptInfo = array();
  880.                         $cryptInfo['method'] = $method;
  881.                         $cryptInfo['iv'] = $iv;
  882.                         $cryptInfo['salt'] = base64_encode($salt);
  883.                         $cryptInfo['hash'] = hash('sha256', $salt.$password);
  884.  
  885.                         $payload = openssl_encrypt($payload, $method, $password, 0, $iv);
  886.                         $dataset['encryption'] = $cryptInfo;
  887.                 }
  888.  
  889.                 $dataset['payload'] = base64_encode($payload);
  890.  
  891.                 // 3. Encode everything as JSON+Base64 (again) and put it into the data block
  892.  
  893.                 $json = array();
  894.                 $json['type'] = VNAG_JSONDATA_V1;
  895.                 $json['datasets'] = array($dataset); // we only output 1 dataset. We could technically output more than one into this data block.
  896.  
  897.                 // Include the machine-readable information as data block
  898.                 // This method was chosen to support HTML 4.01, XHTML and HTML5 as well without breaking the standards
  899.                 // see https://stackoverflow.com/questions/51222713/using-an-individual-tag-without-breaking-the-standards/51223609#51223609
  900.                 return '<script type="application/json">'.
  901.                        json_encode($json).
  902.                        '</script>';
  903.         }
  904.  
  905.         protected function appendHeadline($msg) {
  906.                 $this->checkInitialized(); // if (!$this->initialized) return;
  907.  
  908.                 if (_empty($msg)) return false;
  909.                 $this->messages[] = $msg;
  910.  
  911.                 return true;
  912.         }
  913.  
  914.         protected function changeHeadline($msg) {
  915.                 $this->checkInitialized(); // if (!$this->initialized) return;
  916.  
  917.                 if (_empty($msg)) {
  918.                         $this->messages = array();
  919.                 } else {
  920.                         $this->messages = array($msg);
  921.                 }
  922.  
  923.                 return true;
  924.         }
  925.  
  926.         public function setHeadline($msg, $append=false, $verbosityLevel=VNag::VERBOSITY_SUMMARY) {
  927.                 $this->checkInitialized(); // if (!$this->initialized) return;
  928.  
  929.                 if ((!isset($this->argVerbosity)) && ($verbosityLevel > VNag::VERBOSITY_SUMMARY)) throw new VNagRequiredArgumentNotRegistered('-v');
  930.                 if (self::getVerbosityLevel() < $verbosityLevel) $msg = '';
  931.  
  932.                 if ($append) {
  933.                         return $this->appendHeadline($msg);
  934.                 } else {
  935.                         return $this->changeHeadline($msg);
  936.                 }
  937.         }
  938.  
  939.         public function getHeadline() {
  940.                 $this->checkInitialized(); // if (!$this->initialized) return '';
  941.  
  942.                 return implode(', ', $this->messages);
  943.         }
  944.  
  945.         public function addVerboseMessage($msg, $verbosityLevel=VNag::VERBOSITY_SUMMARY) {
  946.                 $this->checkInitialized(); // if (!$this->initialized) return;
  947.  
  948.                 if (self::getVerbosityLevel() >= $verbosityLevel) {
  949.                         $this->verbose_info .= $msg."\n";
  950.                 }
  951.         }
  952.  
  953.         public function setStatus($status, $force=false) {
  954.                 $this->checkInitialized(); // if (!$this->initialized) return;
  955.  
  956.                 if (($force) || is_null($this->status) || ($status > $this->status)) {
  957.                         $this->status = $status;
  958.                 }
  959.         }
  960.  
  961.         public function getStatus() {
  962.                 $this->checkInitialized(); // if (!$this->initialized) return;
  963.  
  964.                 return $this->status;
  965.         }
  966.  
  967.         protected static function exceptionText($exception) {
  968.                 // $this->checkInitialized(); // if (!$this->initialized) return false;
  969.  
  970.                 $class = get_class($exception);
  971.                 $msg = $exception->getMessage();
  972.  
  973.                 if (!_empty($msg)) {
  974.                         return sprintf(VNagLang::$exception_x, $msg, $class);
  975.                 } else {
  976.                         return sprintf(VNagLang::$unhandled_exception_without_msg, $class);
  977.                 }
  978.         }
  979.  
  980.         protected function handleException($exception) {
  981.                 $this->checkInitialized(); // if (!$this->initialized) return;
  982.  
  983.                 if (!VNag::is_http_mode()) {
  984.                         // On console output, remove anything we have written so far!
  985.                         while (ob_get_level() > 0) @ob_end_clean();
  986.                 }
  987.                 $this->clearVerboseInfo();
  988.                 $this->clearPerformanceData();
  989.  
  990.                 if ($exception instanceof VNagException) {
  991.                         $this->setStatus($exception->getStatus());
  992.                 } else {
  993.                         $this->setStatus(self::STATUS_ERROR);
  994.                 }
  995.  
  996.                 $this->setHeadline($this->exceptionText($exception), false);
  997.  
  998.                 if ($exception instanceof VNagImplementationErrorException) {
  999.                         $this->addVerboseMessage($exception->getTraceAsString(), VNag::VERBOSITY_SUMMARY);
  1000.                 } else {
  1001.                         if (isset($this->argVerbosity)) {
  1002.                                 $this->addVerboseMessage($exception->getTraceAsString(), VNag::VERBOSITY_ADDITIONAL_INFORMATION);
  1003.                         } else {
  1004.                                 // $this->addVerboseMessage($exception->getTraceAsString(), VNag::VERBOSITY_SUMMARY);
  1005.                         }
  1006.                 }
  1007.         }
  1008.  
  1009.         // This is not used by the framework itself, but can be useful for a lot of plugins
  1010.         // Note: For icinga2, the path is /var/lib/nagios/.vnag/cache/
  1011.         protected function get_cache_dir() {
  1012.                 $homedir = @getenv('HOME');
  1013.                 if ($homedir && is_dir($homedir)) {
  1014.                         $try = "$homedir/.vnag/cache";
  1015.                         if (is_dir($try)) return $try;
  1016.                         if (@mkdir($try,0777,true)) return $try;
  1017.                 }
  1018.  
  1019.                 $user = posix_getpwuid(posix_geteuid());
  1020.                 if (isset($user['dir']) && is_dir($user['dir'])) {
  1021.                         $homedir = $user['dir'];
  1022.                         $try = "$homedir/.vnag/cache";
  1023.                         if (is_dir($try)) return $try;
  1024.                         if (@mkdir($try,0777,true)) return $try;
  1025.                 }
  1026.  
  1027.                 if (isset($user['name']) && is_dir($user['name'])) {
  1028.                         $username = $user['name'];
  1029.                         $try = "/tmp/vnag/cache";
  1030.                         if (is_dir($try)) return $try;
  1031.                         if (@mkdir($try,0777,true)) return $try;
  1032.                 }
  1033.  
  1034.                 throw new VNagException("Cannot get cache dir"); // TODO: translate and own exception type
  1035.         }
  1036.  
  1037.         // This is not used by the framework itself, but can be useful for a lot of plugins
  1038.         protected function url_get_contents($url, $max_cache_time=1*60*60, $context=null) {
  1039.                 $cache_file = $this->get_cache_dir().'/'.hash('sha256',$url);
  1040.                 if (file_exists($cache_file) && (time()-filemtime($cache_file) < $max_cache_time)) {
  1041.                         $cont = @file_get_contents($cache_file);
  1042.                         if ($cont === false) throw new Exception("Failed to get contents from $cache_file");
  1043.                 } else {
  1044.                         $options = array(
  1045.                           'http'=>array(
  1046.                             'method'=>"GET",
  1047.                             'header'=>"Accept-language: en\r\n" .
  1048.                                       "User-Agent: Mozilla/5.0 (iPad; U; CPU OS 3_2 like Mac OS X; en-us) AppleWebKit/531.21.10 (KHTML, like Gecko) Version/4.0.4 Mobile/7B334b Safari/531.21.102011-10-16 20:23:10\r\n"
  1049.                           )
  1050.                         );
  1051.                         if (is_null($context)) $context = stream_context_create($options);
  1052.                         $cont = @file_get_contents($url, false, $context);
  1053.                         if ($cont === false) throw new Exception("Failed to get contents from $url");
  1054.                         file_put_contents($cache_file, $cont);
  1055.                 }
  1056.                 return $cont;
  1057.         }
  1058. }
  1059.  
  1060.  
  1061. class VNagException extends Exception {
  1062.         public function getStatus() {
  1063.                 return VNag::STATUS_ERROR;
  1064.         }
  1065. }
  1066.  
  1067. class VNagTimeoutException extends VNagException {}
  1068.  
  1069. class VNagWebInfoException extends VNagException {}
  1070. class VNagSignatureException extends VNagException {}
  1071. class VNagPublicKeyException extends VNagException {}
  1072. class VNagPrivateKeyException extends VNagException {}
  1073.  
  1074. // VNagInvalidArgumentException are exceptions which result from a wrong use
  1075. // of arguments by the USER (CLI arguments or HTTP parameters)
  1076. class VNagInvalidArgumentException extends VNagException {
  1077.         public function getStatus() {
  1078.                 return VNag::STATUS_UNKNOWN;
  1079.         }
  1080. }
  1081.  
  1082. class VNagValueUomPairSyntaxException extends VNagInvalidArgumentException {
  1083.         public function __construct($str) {
  1084.                 $e_msg = sprintf(VNagLang::$valueUomPairSyntaxError, $str);
  1085.                 parent::__construct($e_msg);
  1086.         }
  1087. }
  1088.  
  1089. class VNagInvalidTimeoutException extends VNagInvalidArgumentException {}
  1090.  
  1091. class VNagInvalidRangeException extends VNagInvalidArgumentException {
  1092.         public function __construct($msg) {
  1093.                 $e_msg = VNagLang::$range_is_invalid;
  1094.                 if (!_empty($msg)) $e_msg .= ': '.trim($msg);
  1095.                 parent::__construct($e_msg);
  1096.         }
  1097. }
  1098.  
  1099. class VNagInvalidShortOpt extends VNagImplementationErrorException {}
  1100. class VNagInvalidLongOpt extends VNagImplementationErrorException {}
  1101. class VNagInvalidValuePolicy extends VNagImplementationErrorException {}
  1102. class VNagIllegalStatusModel extends VNagImplementationErrorException {}
  1103. class VNagNotConstructed extends VNagImplementationErrorException {}
  1104.  
  1105. // To enforce that people use the API correctly, we report flaws in the implementation
  1106. // as Exception.
  1107. class VNagImplementationErrorException extends VNagException {}
  1108.  
  1109. class VNagInvalidStandardArgument extends VNagImplementationErrorException  {}
  1110. class VNagFunctionCallOutsideSession extends VNagImplementationErrorException {}
  1111. class VNagIllegalArgumentValuesException extends VNagImplementationErrorException {}
  1112.  
  1113. class VNagRequiredArgumentNotRegistered extends VNagImplementationErrorException {
  1114.         // Developer's mistake: The argument is not in the list of expected arguments
  1115.         public function __construct($required_argument) {
  1116.                 $e_msg = sprintf(VNagLang::$query_without_expected_argument, $required_argument);
  1117.                 parent::__construct($e_msg);
  1118.         }
  1119. }
  1120.  
  1121. class VNagRequiredArgumentMissing extends VNagInvalidArgumentException {
  1122.         // User's mistake: They did not pass the argument to the plugin
  1123.         public function __construct($required_argument) {
  1124.                 $e_msg = sprintf(VNagLang::$required_argument_missing, $required_argument);
  1125.                 parent::__construct($e_msg);
  1126.         }
  1127. }
  1128.  
  1129. class VNagUnknownUomException extends VNagInvalidArgumentException {
  1130.         public function __construct($uom) {
  1131.                 $e_msg = sprintf(VNagLang::$perfdata_uom_not_recognized, $uom);
  1132.                 parent::__construct($e_msg);
  1133.         }
  1134. }
  1135.  
  1136. class VNagNoCompatibleRangeUomFoundException extends VNagException {}
  1137.  
  1138. class VNagMixedUomsNotImplemented extends VNagInvalidArgumentException {
  1139.         public function __construct($uom1, $uom2) {
  1140.                 if (_empty($uom1)) $uom1 = VNagLang::$none;
  1141.                 if (_empty($uom2)) $uom2 = VNagLang::$none;
  1142.                 $e_msg = sprintf(VNagLang::$perfdata_mixed_uom_not_implemented, $uom1, $uom2);
  1143.                 parent::__construct($e_msg);
  1144.         }
  1145. }
  1146.  
  1147. class VNagUomConvertException extends VNagInvalidArgumentException {
  1148.         // It is unknown where the invalid UOM that was passed to the normalize() function came from,
  1149.         // so it is not clear what parent this Exception class should have...
  1150.         // If the value comes from the developer: VNagImplementationErrorException
  1151.         // If the value came from the user: VNagInvalidArgumentException
  1152.  
  1153.         public function __construct($uom1, $uom2) {
  1154.                 if (_empty($uom1)) $uom1 = VNagLang::$none;
  1155.                 if (_empty($uom2)) $uom2 = VNagLang::$none;
  1156.                 $e_msg = sprintf(VNagLang::$convert_x_y_error, $uom1, $uom2);
  1157.                 parent::__construct($e_msg);
  1158.         }
  1159. }
  1160.  
  1161. class VNagInvalidPerformanceDataException extends VNagInvalidArgumentException {
  1162.         public function __construct($msg) {
  1163.                 $e_msg = VNagLang::$performance_data_invalid;
  1164.                 if (!_empty($msg)) $e_msg .= ': '.trim($msg);
  1165.                 parent::__construct($e_msg);
  1166.         }
  1167. }
  1168.  
  1169. class Timeouter {
  1170.         // based on http://stackoverflow.com/questions/7493676/detecting-a-timeout-for-a-block-of-code-in-php
  1171.  
  1172.         private static $start_time = false;
  1173.         private static $timeout;
  1174.         private static $fired      = false;
  1175.         private static $registered = false;
  1176.  
  1177.         private function __construct() {
  1178.         }
  1179.  
  1180.         public static function start($timeout) {
  1181.                 if (!is_numeric($timeout) || ($timeout <= 0)) {
  1182.                         throw new VNagInvalidTimeoutException(sprintf(VNagLang::$timeout_value_invalid, $timeout));
  1183.                 }
  1184.  
  1185.                 self::$start_time = microtime(true);
  1186.                 self::$timeout    = (float) $timeout;
  1187.                 self::$fired      = false;
  1188.                 if (!self::$registered) {
  1189.                         self::$registered = true;
  1190.                         register_tick_function(array('Timeouter', 'tick'));
  1191.                 }
  1192.         }
  1193.  
  1194.         public static function started() {
  1195.                 return self::$registered;
  1196.         }
  1197.  
  1198.         public static function end() {
  1199.                 if (self::$registered) {
  1200.                         unregister_tick_function(array('Timeouter', 'tick'));
  1201.                         self::$registered = false;
  1202.                 }
  1203.         }
  1204.  
  1205.         public static function tick() {
  1206.                 if ((!self::$fired) && ((microtime(true) - self::$start_time) > self::$timeout)) {
  1207.                         self::$fired = true; // do not fire again
  1208.                         throw new VNagTimeoutException(VNagLang::$timeout_exception);
  1209.                 }
  1210.         }
  1211. }
  1212.  
  1213. class VNagArgument {
  1214.         const VALUE_FORBIDDEN = 0;
  1215.         const VALUE_REQUIRED  = 1;
  1216.         const VALUE_OPTIONAL  = 2;
  1217.  
  1218.         protected $shortopt;
  1219.         protected $longopts;
  1220.         protected $valuePolicy;
  1221.         protected $valueName;
  1222.         protected $helpText;
  1223.         protected $defaultValue = null;
  1224.  
  1225.         protected static $all_short = '';
  1226.         protected static $all_long = array();
  1227.  
  1228.         public function getShortOpt() {
  1229.                 return $this->shortopt;
  1230.         }
  1231.  
  1232.         public function getLongOpts() {
  1233.                 return $this->longopts;
  1234.         }
  1235.  
  1236.         public function getValuePolicy() {
  1237.                 return $this->valuePolicy;
  1238.         }
  1239.  
  1240.         public function getValueName() {
  1241.                 return $this->valueName;
  1242.         }
  1243.  
  1244.         public function getHelpText() {
  1245.                 return $this->helpText;
  1246.         }
  1247.  
  1248.         static private function validateShortOpt($shortopt) {
  1249.                 $m = array();
  1250.                 return preg_match('@^[a-zA-Z0-9\\+\\-\\?]$@', $shortopt, $m);
  1251.         }
  1252.  
  1253.         static private function validateLongOpt($longopt) {
  1254.                 // FUT: Check if this is accurate
  1255.                 $m = array();
  1256.                 return preg_match('@^[a-zA-Z0-9\\+\\-\\?]+$@', $longopt, $m);
  1257.         }
  1258.  
  1259.         // Note: Currently, we do not support following:
  1260.         // 1. How many times may a value be defined (it needs to be manually described in $helpText)
  1261.         // 2. Is this argument mandatory? (No exception will be thrown if the plugin will be started without this argument)
  1262.         public function __construct($shortopt, $longopts, $valuePolicy, $valueName, $helpText, $defaultValue=null) {
  1263.                 // Check if $valueName is defined correctly in regards to the policy $valuePolicy
  1264.                 switch ($valuePolicy) {
  1265.                         case VNagArgument::VALUE_FORBIDDEN:
  1266.                                 if (!_empty($valueName)) {
  1267.                                         throw new VNagImplementationErrorException(sprintf(VNagLang::$value_name_forbidden));
  1268.                                 }
  1269.                                 break;
  1270.                         case VNagArgument::VALUE_REQUIRED:
  1271.                                 if (_empty($valueName)) {
  1272.                                         throw new VNagImplementationErrorException(sprintf(VNagLang::$value_name_required));
  1273.                                 }
  1274.                                 break;
  1275.                         case VNagArgument::VALUE_OPTIONAL:
  1276.                                 if (_empty($valueName)) {
  1277.                                         throw new VNagImplementationErrorException(sprintf(VNagLang::$value_name_required));
  1278.                                 }
  1279.                                 break;
  1280.                         default:
  1281.                                 throw new VNagInvalidValuePolicy(sprintf(VNagLang::$illegal_valuepolicy, $valuePolicy));
  1282.                 }
  1283.  
  1284.                 // We'll check: Does the shortopt contain illegal characters?
  1285.                 // http://stackoverflow.com/questions/28522387/which-chars-are-valid-shortopts-for-gnu-getopt
  1286.                 // We do not filter +, - and ?, since we might need it for other methods, e.g. VNagArgumentHandler::_addExpectedArgument
  1287.                 if (!_empty($shortopt)) {
  1288.                         if (!self::validateShortOpt($shortopt)) {
  1289.                                 throw new VNagInvalidShortOpt(sprintf(VNagLang::$illegal_shortopt, $shortopt));
  1290.                         }
  1291.                 }
  1292.  
  1293.                 if (is_array($longopts)) { // $longopts is an array
  1294.                         foreach ($longopts as $longopt) {
  1295.                                 if (!self::validateLongOpt($longopt)) {
  1296.                                         throw new VNagInvalidLongOpt(sprintf(VNagLang::$illegal_longopt, $longopt));
  1297.                                 }
  1298.                         }
  1299.                 } else if (!_empty($longopts)) { // $longopts is a string
  1300.                         if (!self::validateLongOpt($longopts)) {
  1301.                                 throw new VNagInvalidLongOpt(sprintf(VNagLang::$illegal_longopt, $longopts));
  1302.                         }
  1303.                         $longopts = array($longopts);
  1304.                 } else {
  1305.                         $longopts = array();
  1306.                 }
  1307.  
  1308.                 # valuePolicy must be between 0..2 and being int
  1309.                 switch ($valuePolicy) {
  1310.                         case VNagArgument::VALUE_FORBIDDEN:
  1311.                                 $policyApdx = '';
  1312.                                 break;
  1313.                         case VNagArgument::VALUE_REQUIRED:
  1314.                                 $policyApdx = ':';
  1315.                                 break;
  1316.                         case VNagArgument::VALUE_OPTIONAL:
  1317.                                 $policyApdx = '::';
  1318.                                 break;
  1319.                         default:
  1320.                                 throw new VNagInvalidValuePolicy(sprintf(VNagLang::$illegal_valuepolicy, $valuePolicy));
  1321.                 }
  1322.  
  1323.                 if ((!is_null($shortopt)) && ($shortopt != '?')) self::$all_short .= $shortopt.$policyApdx;
  1324.                 if (is_array($longopts)) {
  1325.                         foreach ($longopts as $longopt) {
  1326.                                 self::$all_long[] = $longopt.$policyApdx;
  1327.                         }
  1328.                 }
  1329.  
  1330.                 $this->shortopt     = $shortopt;
  1331.                 $this->longopts     = $longopts;
  1332.                 $this->valuePolicy  = $valuePolicy;
  1333.                 $this->valueName    = $valueName;
  1334.                 $this->helpText     = $helpText;
  1335.                 $this->defaultValue = $defaultValue;
  1336.         }
  1337.  
  1338.         protected static function getOptions() {
  1339.                 // Attention: In PHP 5.6.19-0+deb8u1 (cli), $_REQUEST is always set, so we need is_http_mode() instead of isset($_REQUEST)!
  1340.                 global $OVERWRITE_ARGUMENTS;
  1341.  
  1342.                 if (!is_null($OVERWRITE_ARGUMENTS)) {
  1343.                         return $OVERWRITE_ARGUMENTS;
  1344.                 } else if (VNag::is_http_mode()) {
  1345.                         return $_REQUEST;
  1346.                 } else {
  1347.                         return getopt(self::$all_short, self::$all_long);
  1348.                 }
  1349.         }
  1350.  
  1351.         public function count() {
  1352.                 $options = self::getOptions();
  1353.  
  1354.                 $count = 0;
  1355.  
  1356.                 if (isset($options[$this->shortopt])) {
  1357.                         if (is_array($options[$this->shortopt])) {
  1358.                                 // e.g. -vvv
  1359.                                 $count += count($options[$this->shortopt]);
  1360.                         } else {
  1361.                                 // e.g. -v
  1362.                                 $count += 1;
  1363.                         }
  1364.                 }
  1365.  
  1366.                 if (!is_null($this->longopts)) {
  1367.                         foreach ($this->longopts as $longopt) {
  1368.                                 if (isset($options[$longopt])) {
  1369.                                         if (is_array($options[$longopt])) {
  1370.                                                 // e.g. --verbose --verbose --verbose
  1371.                                                 $count += count($options[$longopt]);
  1372.                                         } else {
  1373.                                                 // e.g. --verbose
  1374.                                                 $count += 1;
  1375.                                         }
  1376.                                 }
  1377.                         }
  1378.                 }
  1379.  
  1380.                 return $count;
  1381.         }
  1382.  
  1383.         public function available() {
  1384.                 $options = self::getOptions();
  1385.  
  1386.                 if (isset($options[$this->shortopt])) return true;
  1387.                 if (!is_null($this->longopts)) {
  1388.                         foreach ($this->longopts as $longopt) {
  1389.                                 if (isset($options[$longopt])) return true;
  1390.                         }
  1391.                 }
  1392.                 return false;
  1393.         }
  1394.  
  1395.         public function require() {
  1396.                 if (!$this->available() && is_null($this->defaultValue)) {
  1397.                         $opt = $this->shortopt;
  1398.                         $opt = !_empty($opt) ? '-'.$opt : (isset($this->longopts[0]) ? '--'.$this->longopts[0] : '?');
  1399.                         throw new VNagRequiredArgumentMissing($opt);
  1400.                 }
  1401.         }
  1402.  
  1403.         public function getValue() {
  1404.                 $options = self::getOptions();
  1405.  
  1406.                 if (isset($options[$this->shortopt])) {
  1407.                         $x = $options[$this->shortopt];
  1408.                         if (is_array($x) && (count($x) <= 1)) $options[$this->shortopt] = $options[$this->shortopt][0];
  1409.                         return $options[$this->shortopt];
  1410.                 }
  1411.  
  1412.                 if (!is_null($this->longopts)) {
  1413.                         foreach ($this->longopts as $longopt) {
  1414.                                 if (isset($options[$longopt])) {
  1415.                                         $x = $options[$longopt];
  1416.                                         if (is_array($x) && (count($x) <= 1)) $options[$longopt] = $options[$longopt][0];
  1417.                                         return $options[$longopt];
  1418.                                 }
  1419.                         }
  1420.                 }
  1421.  
  1422.                 return $this->defaultValue;
  1423.         }
  1424. }
  1425.  
  1426. class VNagArgumentHandler {
  1427.         protected $expectedArgs = array();
  1428.  
  1429.         // Will be called by VNag via ReflectionMethod (like C++ style friends), because it should not be called manually.
  1430.         // Use VNag's function instead (since it adds to the helpObj too)
  1431.         protected function _addExpectedArgument($argObj) {
  1432.                 // -? is always illegal, so it will trigger illegalUsage(). So we don't add it to the list of
  1433.                 // expected arguments, otherwise illegalUsage() would be true.
  1434.                 if ($argObj->getShortOpt() == '?') return false;
  1435.  
  1436.                 // GNU extensions with a special meaning
  1437.                 if ($argObj->getShortOpt() == '-') return false; // cancel parsing
  1438.                 if ($argObj->getShortOpt() == '+') return false; // enable POSIXLY_CORRECT
  1439.  
  1440.                 $this->expectedArgs[] = $argObj;
  1441.                 return true;
  1442.         }
  1443.  
  1444.         public function getArgumentObj($shortopt) {
  1445.                 foreach ($this->expectedArgs as $argObj) {
  1446.                         if ($argObj->getShortOpt() == $shortopt) return $argObj;
  1447.                 }
  1448.                 return null;
  1449.         }
  1450.  
  1451.         public function isArgRegistered($shortopt) {
  1452.                 return !is_null($this->getArgumentObj($shortopt));
  1453.         }
  1454.  
  1455.         public function illegalUsage() {
  1456.                 // In this function, we should check if $argv (resp. getopts) contains stuff which is not expected or illegal,
  1457.                 // so the script can show a usage information and quit the program.
  1458.  
  1459.                 // WONTFIX: PHP's horrible implementation of GNU's getopt does not allow following intended tasks:
  1460.                 // - check for illegal values/arguments (e.g. the argument -? which is always illegal)
  1461.                 // - check for missing values (e.g. -H instead of -H localhost )
  1462.                 // - check for unexpected arguments (e.g. -x if only -a -b -c are defined in $expectedArgs as expected arguments)
  1463.                 // - Of course, everything behind "--" may not be evaluated
  1464.                 // see also http://stackoverflow.com/questions/25388130/catch-unexpected-options-with-getopt
  1465.  
  1466.                 // So the only way is to do this stupid hard coded check for '-?'
  1467.                 // PHP sucks...
  1468.                 global $argv;
  1469.                 return (isset($argv[1])) && (($argv[1] == '-?') || ($argv[1] == '/?'));
  1470.         }
  1471. }
  1472.  
  1473. class VNagRange {
  1474.         // see https://nagios-plugins.org/doc/guidelines.html#THRESHOLDFORMAT
  1475.         // We allow UOMs inside the range definition, e.g. "-w @10M:50M"
  1476.  
  1477.         public /*VNagValueUomPair|'-inf'*/ $start;
  1478.         public /*VNagValueUomPair|'inf'*/ $end;
  1479.         public /*boolean*/ $warnInsideRange;
  1480.  
  1481.         public function __construct($rangeDef, $singleValueBehavior=VNag::SINGLEVALUE_RANGE_DEFAULT) {
  1482.                 $m = array();
  1483.                 //if (!preg_match('|(@){0,1}(\d+)(:){0,1}(\d+){0,1}|', $rangeDef, $m)) {
  1484.                 if (!preg_match('|^(@){0,1}([^:]+)(:){0,1}(.*)$|', $rangeDef, $m)) {
  1485.                         throw new VNagInvalidRangeException(sprintf(VNagLang::$range_invalid_syntax, $rangeDef));
  1486.                 }
  1487.  
  1488.                 $this->warnInsideRange = $m[1] === '@';
  1489.  
  1490.                 $this->start = null;
  1491.                 $this->end   = null;
  1492.  
  1493.                 if ($m[3] === ':') {
  1494.                         if ($m[2] === '~') {
  1495.                                 $this->start = '-inf';
  1496.                         } else {
  1497.                                 $this->start = new VNagValueUomPair($m[2]);
  1498.                         }
  1499.  
  1500.                         if (_empty($m[4])) {
  1501.                                 $this->end = 'inf';
  1502.                         } else {
  1503.                                 $this->end = new VNagValueUomPair($m[4]);
  1504.                         }
  1505.                 } else {
  1506.                         assert(_empty($m[4]));
  1507.                         assert(!_empty($m[2]));
  1508.  
  1509.                         $x = $m[2];
  1510.  
  1511.                         if ($singleValueBehavior == VNag::SINGLEVALUE_RANGE_DEFAULT) {
  1512.                                 // Default behavior according to the development guidelines:
  1513.                                 //  x means  0:x, which means, x>10 is bad
  1514.                                 // @x means @0:x, which means, x<=10 is bad
  1515.                                 $this->start = new VNagValueUomPair('0'.((new VNagValueUomPair($x))->getUom()));
  1516.                                 $this->end   = new VNagValueUomPair($x);
  1517.                         } else if ($singleValueBehavior == VNag::SINGLEVALUE_RANGE_VAL_GT_X_BAD) {
  1518.                                 // The single value x means, everything > x is bad. @x is not defined.
  1519.                                 if ($this->warnInsideRange) throw new VNagInvalidRangeException(VNagLang::$singlevalue_unexpected_at_symbol);
  1520.                                 $this->warnInsideRange = 0;
  1521.                                 $this->start = '-inf';
  1522.                                 $this->end   = new VNagValueUomPair($x);
  1523.                         } else if ($singleValueBehavior == VNag::SINGLEVALUE_RANGE_VAL_GE_X_BAD) {
  1524.                                 // The single value x means, everything >= x is bad. @x is not defined.
  1525.                                 if ($this->warnInsideRange) throw new VNagInvalidRangeException(VNagLang::$singlevalue_unexpected_at_symbol);
  1526.                                 $this->warnInsideRange = 1;
  1527.                                 $this->start = new VNagValueUomPair($x);
  1528.                                 $this->end   = 'inf';
  1529.                         } else if ($singleValueBehavior == VNag::SINGLEVALUE_RANGE_VAL_LT_X_BAD) {
  1530.                                 // The single value x means, everything < x is bad. @x is not defined.
  1531.                                 if ($this->warnInsideRange) throw new VNagInvalidRangeException(VNagLang::$singlevalue_unexpected_at_symbol);
  1532.                                 $this->warnInsideRange = 0;
  1533.                                 $this->start = new VNagValueUomPair($x);
  1534.                                 $this->end   = 'inf';
  1535.                         } else if ($singleValueBehavior == VNag::SINGLEVALUE_RANGE_VAL_LE_X_BAD) {
  1536.                                 // The single value x means, everything <= x is bad. @x is not defined.
  1537.                                 if ($this->warnInsideRange) throw new VNagInvalidRangeException(VNagLang::$singlevalue_unexpected_at_symbol);
  1538.                                 $this->warnInsideRange = 1;
  1539.                                 $this->start = '-inf';
  1540.                                 $this->end   = new VNagValueUomPair($x);
  1541.                         } else {
  1542.                                 throw new VNagException(VNagLang::$illegalSingleValueBehavior);
  1543.                         }
  1544.                 }
  1545.  
  1546.                 // Check if range is valid
  1547.                 if (is_null($this->start)) {
  1548.                         throw new VNagInvalidRangeException(VNagLang::$invalid_start_value);
  1549.                 }
  1550.                 if (is_null($this->end)) {
  1551.                         throw new VNagInvalidRangeException(VNagLang::$invalid_end_value);
  1552.                 }
  1553.                 if (($this->start instanceof VNagValueUomPair) && ($this->end instanceof VNagValueUomPair) &&
  1554.                     (VNagValueUomPair::compare($this->start,$this->end) > 0)) {
  1555.                         throw new VNagInvalidRangeException(VNagLang::$start_is_greater_than_end);
  1556.                 }
  1557.         }
  1558.  
  1559.         public function __toString() {
  1560.                 // Attention:
  1561.                 // - this function assumes that $start and $end are valid.
  1562.                 // - not the shortest result will be chosen
  1563.  
  1564.                 $ret = '';
  1565.                 if ($this->warnInsideRange) {
  1566.                         $ret = '@';
  1567.                 }
  1568.  
  1569.                 if ($this->start === '-inf') {
  1570.                         $ret .= '~';
  1571.                 } else {
  1572.                         $ret .= $this->start;
  1573.                 }
  1574.  
  1575.                 $ret .= ':';
  1576.  
  1577.                 if ($this->end !== 'inf') {
  1578.                         $ret .= $this->end;
  1579.                 }
  1580.  
  1581.                 return $ret;
  1582.         }
  1583.  
  1584.         public function checkAlert($values) {
  1585.                 $compatibleCount = 0;
  1586.  
  1587.                 if (!is_array($values)) $values = array($values);
  1588.                 foreach ($values as $value) {
  1589.                         if (!($value instanceof VNagValueUomPair)) $value = new VNagValueUomPair($value);
  1590.  
  1591.                         assert(($this->start === '-inf') || ($this->start instanceof VNagValueUomPair));
  1592.                         assert(($this->end   === 'inf' ) || ($this->end   instanceof VNagValueUomPair));
  1593.  
  1594.                         if (($this->start !== '-inf') && (!$this->start->compatibleWith($value))) continue;
  1595.                         if (($this->end   !== 'inf')  && (!$this->end->compatibleWith($value)))   continue;
  1596.                         $compatibleCount++;
  1597.  
  1598.                         if ($this->warnInsideRange) {
  1599.                                 return (($this->start === '-inf') || (VNagValueUomPair::compare($value,$this->start) >= 0)) &&
  1600.                                        (($this->end   === 'inf')  || (VNagValueUomPair::compare($value,$this->end)   <= 0));
  1601.                         } else {
  1602.                                 return (($this->start !== '-inf') && (VNagValueUomPair::compare($value,$this->start) <  0)) ||
  1603.                                        (($this->end   !== 'inf')  && (VNagValueUomPair::compare($value,$this->end)   >  0));
  1604.                         }
  1605.                 }
  1606.  
  1607.                 if ((count($values) > 0) and ($compatibleCount == 0)) {
  1608.                         throw new VNagNoCompatibleRangeUomFoundException(VNagLang::$no_compatible_range_uom_found);
  1609.                 }
  1610.  
  1611.                 return false;
  1612.         }
  1613. }
  1614.  
  1615. class VNagValueUomPair {
  1616.         protected $value;
  1617.         protected $uom;
  1618.         public $roundTo = -1;
  1619.  
  1620.         public function isRelative() {
  1621.                 return $this->uom === '%';
  1622.         }
  1623.  
  1624.         public function getValue() {
  1625.                 return $this->value;
  1626.         }
  1627.  
  1628.         public function getUom() {
  1629.                 return $this->uom;
  1630.         }
  1631.  
  1632.         public function __toString() {
  1633.                 if ($this->roundTo == -1) {
  1634.                         return $this->value.$this->uom;
  1635.                 } else {
  1636.                         return round($this->value,$this->roundTo).$this->uom;
  1637.                 }
  1638.         }
  1639.  
  1640.         public function __construct($str) {
  1641.                 $m = array();
  1642.                 if (!preg_match('/^([\d\.]+)(.*)$/ism', $str, $m)) {
  1643.                         throw new VNagValueUomPairSyntaxException($str);
  1644.                 }
  1645.                 $this->value = $m[1];
  1646.                 $this->uom = isset($m[2]) ? $m[2] : '';
  1647.  
  1648.                 if (!self::isKnownUOM($this->uom)) {
  1649.                         throw new VNagUnknownUomException($this->uom);
  1650.                 }
  1651.         }
  1652.  
  1653.         public static function isKnownUOM(string $uom) {
  1654.                 // see https://nagios-plugins.org/doc/guidelines.html#AEN200
  1655.                 // 10. UOM (unit of measurement)
  1656.                 // Previous definition:
  1657.                 //      "[UOM] is one of": (none), s, us, ms, %, B, KB, MB, TB, c
  1658.                 // New definition, introduced somewhere in 2019:
  1659.                 //      "[UOM] is one of" was replaced with "Some examples"
  1660.                 //      Added 17 Dec 2023: d, m, h, ns, PB, EB, ZB, YB
  1661.  
  1662.                 // no unit specified - assume a number (int or float) of things (eg, users, processes, load averages)
  1663.                 $no_unit = ($uom === '');
  1664.                 // s - seconds (also us, ms)
  1665.                 $seconds = ($uom === 'd') || ($uom === 'h') || ($uom === 'm') || ($uom === 's') || ($uom === 'ms') || ($uom === 'us') || ($uom === 'ns');
  1666.                 // % - percentage
  1667.                 $percentage = ($uom === '%');
  1668.                 // B - bytes (also KB, MB, TB)
  1669.                 // NOTE: GB is not in the official development guidelines,probably due to an error, so I've added them anyway
  1670.                 $bytes = ($uom === 'B') || ($uom === 'KB') || ($uom === 'MB') || ($uom === 'GB') || ($uom === 'TB') || ($uom === 'PB') || ($uom === 'EB') || ($uom === 'ZB') || ($uom === 'YB');
  1671.                 // c - a continous counter (such as bytes transmitted on an interface)
  1672.                 $counter = ($uom === 'c');
  1673.  
  1674.                 return ($no_unit || $seconds || $percentage || $bytes || $counter);
  1675.         }
  1676.  
  1677.         public function normalize($target=null) {
  1678.                 $res = clone $this;
  1679.  
  1680.                 // The value is normalized to seconds or megabytes
  1681.                 if ($res->uom === 'd') { // Added by DM 17 Dec 2023
  1682.                         $res->uom = 's';
  1683.                         $res->value *= 60 * 60 * 24;
  1684.                 }
  1685.                 if ($res->uom === 'h') { // Added by DM 17 Dec 2023
  1686.                         $res->uom = 's';
  1687.                         $res->value *= 60 * 60;
  1688.                 }
  1689.                 if ($res->uom === 'm') { // Added by DM 17 Dec 2023
  1690.                         $res->uom = 's';
  1691.                         $res->value *= 60;
  1692.                 }
  1693.                 if ($res->uom === 'ms') {
  1694.                         $res->uom = 's';
  1695.                         $res->value /= 1000;
  1696.                 }
  1697.                 if ($res->uom === 'us') {
  1698.                         $res->uom = 's';
  1699.                         $res->value /= 1000 * 1000;
  1700.                 }
  1701.                 if ($res->uom === 'ns') { // Added by DM 17 Dec 2023
  1702.                         $res->uom = 's';
  1703.                         $res->value /= 1000 * 1000 * 1000;
  1704.                 }
  1705.                 if ($res->uom === 'B') {
  1706.                         $res->uom = 'MB';
  1707.                         $res->value /= 1024 * 1024;
  1708.                 }
  1709.                 if ($res->uom === 'KB') {
  1710.                         $res->uom = 'MB';
  1711.                         $res->value /= 1024;
  1712.                 }
  1713.                 if ($res->uom === 'GB') {
  1714.                         $res->uom = 'MB';
  1715.                         $res->value *= 1024;
  1716.                 }
  1717.                 if ($res->uom === 'TB') {
  1718.                         $res->uom = 'MB';
  1719.                         $res->value *= 1024 * 1024;
  1720.                 }
  1721.                 if ($res->uom === 'PB') { // Added by DM 17 Dec 2023
  1722.                         $res->uom = 'MB';
  1723.                         $res->value *= 1024 * 1024 * 1024;
  1724.                 }
  1725.                 if ($res->uom === 'EB') { // Added by DM 17 Dec 2023
  1726.                         $res->uom = 'MB';
  1727.                         $res->value *= 1024 * 1024 * 1024 * 1024;
  1728.                 }
  1729.                 if ($res->uom === 'ZB') { // Added by DM 17 Dec 2023
  1730.                         $res->uom = 'MB';
  1731.                         $res->value *= 1024 * 1024 * 1024 * 1024 * 1024;
  1732.                 }
  1733.                 if ($res->uom === 'YB') { // Added by DM 17 Dec 2023
  1734.                         $res->uom = 'MB';
  1735.                         $res->value *= 1024 * 1024 * 1024 * 1024 * 1024 * 1024;
  1736.                 }
  1737.                 if ($res->uom === 'c') {
  1738.                         $res->uom = '';
  1739.                 }
  1740.  
  1741.                 // Now, if the user wishes, convert to another unit
  1742.                 if (!is_null($target)) {
  1743.                         if ($res->uom == 'MB') {
  1744.                                 if ($target == 'B') {
  1745.                                         $res->uom = 'B';
  1746.                                         $res->value *= 1024 * 1024;
  1747.                                 } else if ($target == 'KB') {
  1748.                                         $res->uom = 'KB';
  1749.                                         $res->value *= 1024;
  1750.                                 } else if ($target == 'MB') {
  1751.                                         $res->uom = 'MB';
  1752.                                         $res->value *= 1;
  1753.                                 } else if ($target == 'GB') {
  1754.                                         $res->uom = 'GB';
  1755.                                         $res->value /= 1024;
  1756.                                 } else if ($target == 'TB') {
  1757.                                         $res->uom = 'TB';
  1758.                                         $res->value /= 1024 * 1024;
  1759.                                 } else if ($target == 'PB') { // Added by DM 17 Dec 2023
  1760.                                         $res->uom = 'PB';
  1761.                                         $res->value /= 1024 * 1024 * 1024;
  1762.                                 } else if ($target == 'EB') { // Added by DM 17 Dec 2023
  1763.                                         $res->uom = 'EB';
  1764.                                         $res->value /= 1024 * 1024 * 1024 * 1024;
  1765.                                 } else if ($target == 'ZB') { // Added by DM 17 Dec 2023
  1766.                                         $res->uom = 'ZB';
  1767.                                         $res->value /= 1024 * 1024 * 1024 * 1024 * 1024;
  1768.                                 } else if ($target == 'YB') { // Added by DM 17 Dec 2023
  1769.                                         $res->uom = 'YB';
  1770.                                         $res->value /= 1024 * 1024 * 1024 * 1024 * 1024 * 1024;
  1771.                                 } else {
  1772.                                         throw new VNagUomConvertException($res->uom, $target);
  1773.                                 }
  1774.                         } else if ($res->uom == 's') {
  1775.                                 if ($target == 'd') { // Added by DM 17 Dec 2023
  1776.                                         $res->uom = 'd';
  1777.                                         $res->value *= 24 * 60 * 60;
  1778.                                 } else if ($target == 'h') { // Added by DM 17 Dec 2023
  1779.                                         $res->uom = 'h';
  1780.                                         $res->value *= 60 * 60;
  1781.                                 } else if ($target == 'm') { // Added by DM 17 Dec 2023
  1782.                                         $res->uom = 'm';
  1783.                                         $res->value *= 60;
  1784.                                 } else if ($target == 's') {
  1785.                                         $res->uom = 's';
  1786.                                         $res->value *= 1;
  1787.                                 } else if ($target == 'ms') {
  1788.                                         $res->uom = 'ms';
  1789.                                         $res->value /= 1000;
  1790.                                 } else if ($target == 'us') {
  1791.                                         $res->uom = 'us';
  1792.                                         $res->value /= 1000 * 1000;
  1793.                                 } else if ($target == 'ns') { // Added by DM 17 Dec 2023
  1794.                                         $res->uom = 'ns';
  1795.                                         $res->value /= 1000 * 1000 * 1000;
  1796.                                 } else {
  1797.                                         throw new VNagUomConvertException($res->uom, $target);
  1798.                                 }
  1799.                         } else {
  1800.                                 throw new VNagUomConvertException($res->uom, $target);
  1801.                         }
  1802.                 }
  1803.  
  1804.                 return $res;
  1805.         }
  1806.  
  1807.         public function compatibleWith(VNagValueUomPair $other) {
  1808.                 $a = $this->normalize();
  1809.                 $b = $other->normalize();
  1810.  
  1811.                 return ($a->uom == $b->uom);
  1812.         }
  1813.  
  1814.         public static function compare(VNagValueUomPair $left, VNagValueUomPair $right) {
  1815.                 $a = $left->normalize();
  1816.                 $b = $right->normalize();
  1817.  
  1818.                 // FUT: Also accept mixed UOMs, e.g. MB and %
  1819.                 //      To translate between an absolute and a relative value, the
  1820.                 //      reference value (100%=?) needs to be passed through this comparison
  1821.                 //      function somehow.
  1822.                 if ($a->uom != $b->uom) throw new VNagMixedUomsNotImplemented($a->uom, $b->uom);
  1823.  
  1824.                 if ($a->value  > $b->value) return  1;
  1825.                 if ($a->value == $b->value) return  0;
  1826.                 if ($a->value  < $b->value) return -1;
  1827.         }
  1828. }
  1829.  
  1830. class VNagPerformanceData {
  1831.         // see https://nagios-plugins.org/doc/guidelines.html#AEN200
  1832.         //     https://www.icinga.com/docs/icinga1/latest/en/perfdata.html#formatperfdata
  1833.  
  1834.         protected $label;
  1835.         protected /*VNagValueUomPair*/ $value;
  1836.         protected $warn = null;
  1837.         protected $crit = null;
  1838.         protected $min = null;
  1839.         protected $max = null;
  1840.  
  1841.         public static function createByString($perfdata) {
  1842.                 $perfdata = trim($perfdata);
  1843.  
  1844.                 $ary = explode('=',$perfdata);
  1845.                 if (count($ary) != 2) {
  1846.                         throw new VNagInvalidPerformanceDataException(sprintf(VNagLang::$perfdata_line_invalid, $perfdata));
  1847.                 }
  1848.                 $label = $ary[0];
  1849.                 $bry = explode(';',$ary[1]);
  1850.                 if (substr($label,0,1) === "'") $label = substr($label, 1, strlen($label)-2);
  1851.                 $value = $bry[0];
  1852.                 $warn  = isset($bry[1]) ? $bry[1] : null;
  1853.                 $crit  = isset($bry[2]) ? $bry[2] : null;
  1854.                 $min   = isset($bry[3]) ? $bry[3] : null;
  1855.                 $max   = isset($bry[4]) ? $bry[4] : null;
  1856.  
  1857.                 // Guideline "7. min and max are not required if UOM=%" makes no sense, because
  1858.                 // actually, all fields (except label and value) are optional.
  1859.  
  1860.                 return new self($label, $value, $warn, $crit, $min, $max);
  1861.         }
  1862.  
  1863.         public function __construct($label, $value/*may include UOM*/, $warn=null, $crit=null, $min=null, $max=null) {
  1864.                 // Not checked / Nothing to check:
  1865.                 // - 4. label length is arbitrary, but ideally the first 19 characters are unique (due to a limitation in RRD). Be aware of a limitation in the amount of data that NRPE returns to Nagios
  1866.                 // - 6. warn, crit, min or max may be null (for example, if the threshold is not defined or min and max do not apply). Trailing unfilled semicolons can be dropped
  1867.                 // - 9. warn and crit are in the range format (see the Section called Threshold and ranges). Must be the same UOM
  1868.                 // - 7. min and max are not required if UOM=%
  1869.  
  1870.                 // 2. label can contain any characters except the equals sign or single quote (')
  1871.                 if (strpos($label, '=') !== false) throw new VNagInvalidPerformanceDataException(VNagLang::$perfdata_label_equal_sign_forbidden);
  1872.  
  1873.                 // 5. to specify a quote character, use two single quotes
  1874.                 $label = str_replace("'", "''", $label);
  1875.  
  1876.                 // 8. value, min and max in class [-0-9.]. Must all be the same UOM.
  1877.                 //    value may be a literal "U" instead, this would indicate that the actual value couldn't be determined
  1878.                 /*
  1879.                 if (($value != 'U') && (!preg_match('|^[-0-9\\.]+$|', $value, $m))) {
  1880.                         throw new VNagInvalidPerformanceDataException(VNagLang::$perfdata_value_must_be_in_class);
  1881.                 }
  1882.                 */
  1883.                 $m = array();
  1884.                 if ((!_empty($min)) && (!preg_match('|^[-0-9\\.]+$|', $min, $m))) {
  1885.                         throw new VNagInvalidPerformanceDataException(VNagLang::$perfdata_min_must_be_in_class);
  1886.                 }
  1887.                 if ((!_empty($max)) && (!preg_match('|^[-0-9\\.]+$|', $max, $m))) {
  1888.                         throw new VNagInvalidPerformanceDataException(VNagLang::$perfdata_max_must_be_in_class);
  1889.                 }
  1890.  
  1891.                 // 10. UOM (unit of measurement) is one of ....
  1892.                 //     => This rule is checked in the VNagValueUomPair constructor.
  1893.  
  1894.                 $this->label = $label;
  1895.                 $this->value = ($value == 'U') ? 'U' : new VNagValueUomPair($value);
  1896.                 $this->warn  = $warn;
  1897.                 $this->crit  = $crit;
  1898.                 $this->min   = $min;
  1899.                 $this->max   = $max;
  1900.         }
  1901.  
  1902.         public function __toString() {
  1903.                 $label = $this->label;
  1904.                 $value = $this->value;
  1905.                 $warn  = $this->warn;
  1906.                 $crit  = $this->crit;
  1907.                 $min   = $this->min;
  1908.                 $max   = $this->max;
  1909.  
  1910.                 // 5. to specify a quote character, use two single quotes
  1911.                 $label = str_replace("''", "'", $label);
  1912.  
  1913.                 // 'label'=value[UOM];[warn];[crit];[min];[max]
  1914.                 // 3. the single quotes for the label are optional. Required if spaces are in the label
  1915.                 return "'$label'=$value".
  1916.                        ';'.(is_null($warn) ? '' : $warn).
  1917.                        ';'.(is_null($crit) ? '' : $crit).
  1918.                        ';'.(is_null($min)  ? '' : $min).
  1919.                        ';'.(is_null($max)  ? '' : $max);
  1920.         }
  1921. }
  1922.  
  1923. class VNagHelp {
  1924.         public $word_wrap_width = 80; // -1 = disable
  1925.         public $argument_indent = 7;
  1926.  
  1927.         public function printUsagePage() {
  1928.                 $usage = $this->getUsage();
  1929.  
  1930.                 if (_empty($usage)) {
  1931.                         $usage = VNagLang::$no_syntax_defined;
  1932.                 }
  1933.  
  1934.                 return trim($usage)."\n";
  1935.         }
  1936.  
  1937.         public function printVersionPage() {
  1938.                 $out = trim($this->getNameAndVersion())."\n";
  1939.  
  1940.                 if ($this->word_wrap_width > 0) $out = wordwrap($out, $this->word_wrap_width, "\n", false);
  1941.  
  1942.                 return $out;
  1943.         }
  1944.  
  1945.         static private function _conditionalLine($line, $terminator='', $prefix='') {
  1946.                 if (!_empty($line)) {
  1947.                         return trim($line).$terminator;
  1948.                 }
  1949.                 return '';
  1950.         }
  1951.  
  1952.         public function printHelpPage() {
  1953.                 $out  = '';
  1954.                 $out .= self::_conditionalLine($this->getNameAndVersion(), "\n");
  1955.                 $out .= self::_conditionalLine($this->getCopyright(), "\n");
  1956.                 $out .= ($out != '') ? "\n" : '';
  1957.                 $out .= self::_conditionalLine($this->getShortDescription(), "\n\n\n");
  1958.                 $out .= self::_conditionalLine($this->getUsage(), "\n\n");
  1959.  
  1960.                 $out .= VNagLang::$options."\n";
  1961.                 foreach ($this->options as $argObj) {
  1962.                         $out .= $this->printArgumentHelp($argObj);
  1963.                 }
  1964.  
  1965.                 $out .= self::_conditionalLine($this->getFootNotes(), "\n\n", "\n");
  1966.  
  1967.                 if ($this->word_wrap_width > 0) $out = wordwrap($out, $this->word_wrap_width, "\n", false);
  1968.  
  1969.                 return $out;
  1970.         }
  1971.  
  1972.         protected /* VNagArgument[] */ $options = array();
  1973.  
  1974.         // Will be called by VNag via ReflectionMethod (like C++ style friends), because it should not be called manually.
  1975.         // Use VNag's function instead (since it adds to the argHandler too)
  1976.         protected function _addOption($argObj) {
  1977.                 $this->options[] = $argObj;
  1978.         }
  1979.  
  1980.         # FUT: Automatic creation of usage page. Which arguments are necessary?
  1981.         protected function printArgumentHelp($argObj) {
  1982.                 $identifiers = array();
  1983.  
  1984.                 $shortopt = $argObj->getShortopt();
  1985.                 if (!_empty($shortopt)) $identifiers[] = '-'.$shortopt;
  1986.  
  1987.                 $longopts = $argObj->getLongopts();
  1988.                 if (!is_null($longopts)) {
  1989.                         foreach ($longopts as $longopt) {
  1990.                                 if (!_empty($longopt)) $identifiers[] = '--'.$longopt;
  1991.                         }
  1992.                 }
  1993.  
  1994.                 if (count($identifiers) == 0) return;
  1995.  
  1996.                 $valueName = $argObj->getValueName();
  1997.  
  1998.                 $arginfo = '';
  1999.                 switch ($argObj->getValuePolicy()) {
  2000.                         case VNagArgument::VALUE_FORBIDDEN:
  2001.                                 $arginfo = '';
  2002.                                 break;
  2003.                         case VNagArgument::VALUE_REQUIRED:
  2004.                                 $arginfo = '='.$valueName;
  2005.                                 break;
  2006.                         case VNagArgument::VALUE_OPTIONAL:
  2007.                                 $arginfo = '[='.$valueName.']';
  2008.                                 break;
  2009.                 }
  2010.  
  2011.                 $out = '';
  2012.                 $out .= implode(', ', $identifiers).$arginfo."\n";
  2013.  
  2014.                 // https://nagios-plugins.org/doc/guidelines.html#AEN302 recommends supporting a 80x23 screen resolution.
  2015.                 // While we cannot guarantee the vertical height, we can limit the width at least...
  2016.  
  2017.                 $content = trim($argObj->getHelpText());
  2018.                 if ($this->word_wrap_width > 0) $content = wordwrap($content, $this->word_wrap_width-$this->argument_indent, "\n", false);
  2019.                 $lines = explode("\n", $content);
  2020.  
  2021.                 foreach ($lines as $line) {
  2022.                         $out .= str_repeat(' ', $this->argument_indent).$line."\n";
  2023.                 }
  2024.                 $out .= "\n";
  2025.  
  2026.                 return $out;
  2027.         }
  2028.  
  2029.         // $pluginName should contain the name of the plugin, without version.
  2030.         protected $pluginName;
  2031.         public function setPluginName($pluginName) {
  2032.                 $this->pluginName = $this->replaceStuff($pluginName);
  2033.         }
  2034.         public function getPluginName() {
  2035.                 if (_empty($this->pluginName)) {
  2036.                         global $argv;
  2037.                         return basename($argv[0]);
  2038.                 } else {
  2039.                         return $this->pluginName;
  2040.                 }
  2041.         }
  2042.  
  2043.         // $version should contain the version, not the program name or copyright.
  2044.         protected $version;
  2045.         public function setVersion($version) {
  2046.                 $this->version = $this->replaceStuff($version);
  2047.         }
  2048.         public function getVersion() {
  2049.                 return $this->version;
  2050.         }
  2051.         public function getNameAndVersion() {
  2052.                 $ret = $this->getPluginName();
  2053.                 if (_empty($ret)) return null;
  2054.  
  2055.                 $ver = $this->getVersion();
  2056.                 if (!_empty($ver)) {
  2057.                         $ret = sprintf(VNagLang::$x_version_x, $ret, $ver);
  2058.                 }
  2059.                 $ret = trim($ret);
  2060.  
  2061.                 return $ret;
  2062.         }
  2063.  
  2064.         // $copyright should contain the copyright only, no program name or version.
  2065.         // $CURYEAR$ will be replaced by the current year
  2066.         protected $copyright;
  2067.         public function setCopyright($copyright) {
  2068.                 $this->copyright = $this->replaceStuff($copyright);
  2069.         }
  2070.  
  2071.         private function getVNagCopyright() {
  2072.                 if (VNag::is_http_mode()) {
  2073.                         $vts_email = 'www.viathinksoft.com'; // don't publish email address at web services because of spam bots
  2074.                 } else {
  2075.                         $vts_email = base64_decode('aW5mb0B2aWF0aGlua3NvZnQuZGU='); // protect email address from spambots which might parse this code
  2076.                 }
  2077.                 return "VNag Framework ".VNag::VNAG_VERSION." (C) 2014-".date('Y')." ViaThinkSoft <$vts_email>";
  2078.         }
  2079.  
  2080.         public function getCopyright() {
  2081.                 if (_empty($this->copyright)) {
  2082.                         return sprintf(VNagLang::$plugin_uses, $this->getVNagCopyright());
  2083.                 } else {
  2084.                         return trim($this->copyright)."\n".sprintf(VNagLang::$uses, $this->getVNagCopyright());
  2085.                 }
  2086.         }
  2087.  
  2088.         // $shortDescription should describe what this plugin does.
  2089.         protected $shortDescription;
  2090.         public function setShortDescription($shortDescription) {
  2091.                 $this->shortDescription = $this->replaceStuff($shortDescription);
  2092.         }
  2093.         public function getShortDescription() {
  2094.                 if (_empty($this->shortDescription)) {
  2095.                         return null;
  2096.                 } else {
  2097.                         $content = $this->shortDescription;
  2098.                         if ($this->word_wrap_width > 0) $content = wordwrap($content, $this->word_wrap_width, "\n", false);
  2099.                         return $content;
  2100.                 }
  2101.         }
  2102.  
  2103.         protected function replaceStuff($text) {
  2104.                 global $argv;
  2105.                 if (php_sapi_name() == 'cli') {
  2106.                         $text = str_replace('$SCRIPTNAME$', $argv[0], $text);
  2107.                 } else {
  2108.                         $text = str_replace('$SCRIPTNAME$', basename($_SERVER['SCRIPT_NAME']), $text);
  2109.                 }
  2110.                 $text = str_replace('$CURYEAR$', date('Y'), $text);
  2111.                 return $text;
  2112.         }
  2113.  
  2114.         // $syntax should contain the option syntax only, no explanations.
  2115.         // $SCRIPTNAME$ will be replaced by the actual script name
  2116.         // $CURYEAR$ will be replaced by the current year
  2117.         # FUT: Automatically generate syntax?
  2118.         protected $syntax;
  2119.         public function setSyntax($syntax) {
  2120.                 $syntax = $this->replaceStuff($syntax);
  2121.                 $this->syntax = $syntax;
  2122.         }
  2123.         public function getUsage() {
  2124.                 if (_empty($this->syntax)) {
  2125.                         return null;
  2126.                 } else {
  2127.                         return sprintf(VNagLang::$usage_x, $this->syntax);
  2128.                 }
  2129.         }
  2130.  
  2131.         // $footNotes can be contact information or other notes which should appear in --help
  2132.         protected $footNotes;
  2133.         public function setFootNotes($footNotes) {
  2134.                 $this->footNotes = $this->replaceStuff($footNotes);
  2135.         }
  2136.         public function getFootNotes() {
  2137.                 return $this->footNotes;
  2138.         }
  2139. }
  2140.  
  2141. class VNagLang {
  2142.         public static function status($code, $statusmodel) {
  2143.                 switch ($statusmodel) {
  2144.                         case VNag::STATUSMODEL_SERVICE:
  2145.                                 switch ($code) {
  2146.                                         case VNag::STATUS_OK:
  2147.                                                 return 'OK';
  2148.                                                 #break;
  2149.                                         case VNag::STATUS_WARNING:
  2150.                                                 return 'Warning';
  2151.                                                 #break;
  2152.                                         case VNag::STATUS_CRITICAL:
  2153.                                                 return 'Critical';
  2154.                                                 #break;
  2155.                                         case VNag::STATUS_UNKNOWN:
  2156.                                                 return 'Unknown';
  2157.                                                 #break;
  2158.                                         default:
  2159.                                                 return sprintf('Error (%d)', $code);
  2160.                                                 #break;
  2161.                                 }
  2162.                                 #break;
  2163.                         case VNag::STATUSMODEL_HOST:
  2164.                                 switch ($code) {
  2165.                                         case VNag::STATUS_UP:
  2166.                                                 return 'Up';
  2167.                                                 #break;
  2168.                                         case VNag::STATUS_DOWN:
  2169.                                                 return 'Down';
  2170.                                                 #break;
  2171.                                         default:
  2172.                                                 return sprintf('Maintain last state (%d)', $code);
  2173.                                                 #break;
  2174.                                 }
  2175.                                 #break;
  2176.                         default:
  2177.                                 throw new VNagIllegalStatusModel(sprintf(self::$illegal_statusmodel, $statusmodel));
  2178.                                 #break;
  2179.                 }
  2180.         }
  2181.  
  2182.         static $nagios_output = 'VNag-Output';
  2183.         static $verbose_info = 'Verbose information';
  2184.         static $status = 'Status';
  2185.         static $message = 'Message';
  2186.         static $performance_data = 'Performance data';
  2187.         static $status_ok = 'OK';
  2188.         static $status_warn = 'Warning';
  2189.         static $status_critical = 'Critical';
  2190.         static $status_unknown = 'Unknown';
  2191.         static $status_error = 'Error';
  2192.         static $unhandled_exception_without_msg = "Unhandled exception of type %s";
  2193.         static $plugin_uses = 'This plugin uses %s';
  2194.         static $uses = 'uses %s';
  2195.         static $x_version_x = '%s, version %s';
  2196.  
  2197.         // Argument names (help page)
  2198.         static $argname_value = 'value';
  2199.         static $argname_seconds = 'seconds';
  2200.  
  2201.         // Exceptions
  2202.         static $query_without_expected_argument = "The argument '%s' is queried, but was not added to the list of expected arguments. Please contact the plugin author.";
  2203.         static $required_argument_missing = "The argument '%s' is required.";
  2204.         static $performance_data_invalid = 'Performance data invalid.';
  2205.         static $no_standard_arguments_with_letter = "No standard argument with letter '%s' exists.";
  2206.         static $invalid_start_value = 'Invalid start value.';
  2207.         static $invalid_end_value = 'Invalid end value.';
  2208.         static $start_is_greater_than_end = 'Start is greater than end value.';
  2209.         static $value_name_forbidden = "Implementation error: You may not define a value name for the argument, because the value policy is VALUE_FORBIDDEN.";
  2210.         static $value_name_required = "Implementation error: Please define a name for the argument (so it can be shown in the help page).";
  2211.         static $illegal_shortopt = "Illegal shortopt '-%s'.";
  2212.         static $illegal_longopt = "Illegal longopt '--%s'.";
  2213.         static $illegal_valuepolicy = "valuePolicy has illegal value '%s'.";
  2214.         static $range_invalid_syntax = "Syntax error in range '%s'.";
  2215.         static $timeout_value_invalid = "Timeout value '%s' is invalid.";
  2216.         static $range_is_invalid = 'Range is invalid.';
  2217.         static $timeout_exception = 'Timeout!';
  2218.         static $perfdata_label_equal_sign_forbidden = 'Label may not contain an equal sign.';
  2219.         static $perfdata_value_must_be_in_class = 'Value must be in class [-0-9.] or be \'U\' if the actual value can\'t be determined.';
  2220.         static $perfdata_min_must_be_in_class = 'Min must be in class [-0-9.] or empty.';
  2221.         static $perfdata_max_must_be_in_class = 'Max must be in class [-0-9.] or empty.';
  2222.         static $perfdata_uom_not_recognized = 'UOM (unit of measurement) "%s" is not recognized.';
  2223.         static $perfdata_mixed_uom_not_implemented = 'Mixed UOMs (%s and %s) are currently not supported.';
  2224.         static $no_compatible_range_uom_found = 'Measured values are not compatible with the provided warning/critical parameter. Most likely, the UOM is incompatible.';
  2225.         static $exception_x = '%s (%s)';
  2226.         static $no_syntax_defined = 'The author of this plugin has not defined a syntax for this plugin.';
  2227.         static $usage_x = "Usage:\n%s";
  2228.         static $options = "Options:";
  2229.         static $illegal_statusmodel = "Invalid statusmodel %d.";
  2230.         static $none = '[none]';
  2231.         static $valueUomPairSyntaxError = 'Syntax error at "%s". Syntax must be Value[UOM].';
  2232.         static $too_few_warning_ranges = "You have too few warning ranges (currently trying to get element %d).";
  2233.         static $too_few_critical_ranges = "You have too few critical ranges (currently trying to get element %d).";
  2234.         static $dataset_missing = 'Dataset missing.';
  2235.         static $payload_not_base64 = 'The payload is not valid Base64.';
  2236.         static $payload_not_json = 'The payload is not valid JSON.';
  2237.         static $signature_missing = 'The signature is missing.';
  2238.         static $signature_not_bas64 = 'The signature is not valid Base64.';
  2239.         static $signature_invalid = 'The signature is invalid. The connection might have been tampered, or a different key is used.';
  2240.         static $pubkey_file_not_found = "Public key file %s was not found.";
  2241.         static $pubkey_file_not_readable = "Public key file %s is not readable.";
  2242.         static $privkey_file_not_found = "Private key file %s was not found.";
  2243.         static $privkey_not_readable = "Private key is not readable.";
  2244.         static $privkey_file_not_readable = "Private key file %s is not readable.";
  2245.         static $signature_failed = "Signature failed.";
  2246.         static $perfdata_line_invalid = "Performance data line %s is invalid.";
  2247.         static $singlevalue_unexpected_at_symbol = 'This plugin does not allow the @-symbol at ranges for single values.';
  2248.         static $illegalSingleValueBehavior = "Illegal value for 'singleValueBehavior'. Please contact the creator of the plugin.";
  2249.         static $dataset_encryption_no_array = 'Dataset encryption information invalid.';
  2250.         static $require_password = 'This resource is protected with a password. Please provide a password.';
  2251.         static $wrong_password = 'This resource is protected with a password. You have provided the wrong password, or it was changed.';
  2252.         static $convert_x_y_error = 'Cannot convert from UOM %s to UOM %s.';
  2253.         static $php_error = 'PHP has detected an error in the plugin. Please contact the plugin author.';
  2254.         static $output_level_lowered = "Output Buffer level lowered during cbRun(). Please contact the plugin author.";
  2255.         static $openssl_missing = "OpenSSL is missing. Therefore, encryption and signatures are not available.";
  2256.  
  2257.         // Help texts
  2258.         static $warning_range = 'Warning range';
  2259.         static $critical_range = 'Critical range';
  2260.         static $prints_version = 'Prints version';
  2261.         static $verbosity_helptext = 'Verbosity -v, -vv or -vvv';
  2262.         static $timeout_helptext = 'Sets timeout in seconds';
  2263.         static $help_helptext = 'Prints help page';
  2264.         static $prints_usage = 'Prints usage';
  2265.  
  2266.         static $notConstructed = 'Parent constructor not called with parent::__construct().';
  2267. }
  2268.  
  2269. function vnagErrorHandler($errorkind, $errortext, $file, $line) {
  2270.         // This function "converts" PHP runtime errors into Exceptions, which can then be handled by VNag::handleException()
  2271.         global $inside_vnag_run;
  2272.  
  2273.         if (!$inside_vnag_run && VNag::is_http_mode()) {
  2274.                 // We want to avoid that the VNag-Exception will show up in a website that contains
  2275.                 // an embedded VNag monitor, so if we are not inside a running VNag code,
  2276.                 // we will call the normal PHP error handler.
  2277.                 return false;
  2278.         }
  2279.  
  2280.         if (!(error_reporting() & $errorkind)) {
  2281.                 // Code is not included in error_reporting. Don't do anything.
  2282.                 return true;
  2283.         }
  2284.  
  2285.         // We want 100% clean scripts, so any error, warning or notice will shutdown the script
  2286.         // This also fixes the issue that PHP will end with result code 0, showing an error.
  2287.  
  2288.         // Error kinds see http://php.net/manual/en/errorfunc.constants.php
  2289.         if (defined('E_ERROR') && ($errorkind == E_ERROR)) $errorkind = 'Error';
  2290.         if (defined('E_WARNING') && ($errorkind == E_WARNING)) $errorkind = 'Warning';
  2291.         if (defined('E_PARSE') && ($errorkind == E_PARSE)) $errorkind = 'Parse';
  2292.         if (defined('E_NOTICE') && ($errorkind == E_NOTICE)) $errorkind = 'Notice';
  2293.         if (defined('E_CORE_ERROR') && ($errorkind == E_CORE_ERROR)) $errorkind = 'Core Error';
  2294.         if (defined('E_CORE_WARNING') && ($errorkind == E_CORE_WARNING)) $errorkind = 'Core Warning';
  2295.         if (defined('E_COMPILE_ERROR') && ($errorkind == E_COMPILE_ERROR)) $errorkind = 'Compile Error';
  2296.         if (defined('E_COMPILE_WARNING') && ($errorkind == E_COMPILE_WARNING)) $errorkind = 'Compile Warning';
  2297.         if (defined('E_USER_ERROR') && ($errorkind == E_USER_ERROR)) $errorkind = 'User Error';
  2298.         if (defined('E_USER_WARNING') && ($errorkind == E_USER_WARNING)) $errorkind = 'User Warning';
  2299.         if (defined('E_USER_NOTICE') && ($errorkind == E_USER_NOTICE)) $errorkind = 'User Notice';
  2300.         if (defined('E_STRICT') && ($errorkind == E_STRICT)) $errorkind = 'Strict';
  2301.         if (defined('E_RECOVERABLE_ERROR') && ($errorkind == E_RECOVERABLE_ERROR)) $errorkind = 'Recoverable Error';
  2302.         if (defined('E_DEPRECATED') && ($errorkind == E_DEPRECATED)) $errorkind = 'Deprecated';
  2303.         if (defined('E_USER_DEPRECATED') && ($errorkind == E_USER_DEPRECATED)) $errorkind = 'User Deprecated';
  2304.         throw new VNagException(VNagLang::$php_error . " $errortext at $file:$line (kind $errorkind)");
  2305.  
  2306.         // true = the PHP internal error handling will NOT be called.
  2307.         #return true;
  2308. }
  2309.  
  2310. $inside_vnag_run = false;
  2311. $old_error_handler = set_error_handler("vnagErrorHandler");
  2312.