Subversion Repositories vnag

Rev

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