Subversion Repositories vnag

Rev

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