Subversion Repositories vnag

Rev

Rev 55 | Go to most recent revision | Blame | Last modification | View Log | RSS feed

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