Subversion Repositories vnag

Rev

Rev 89 | Details | Compare with Previous | Last modification | View Log | RSS feed

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