Subversion Repositories vnag

Rev

Rev 87 | Rev 89 | 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
88 daniel-mar 14
      Revision 2023-11-05
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 {
88 daniel-mar 47
        /*public*/ const VNAG_VERSION = '2023-11-05';
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
1655
                // 10. UOM (unit of measurement) is one of:
31 daniel-mar 1656
 
1657
                // no unit specified - assume a number (int or float) of things (eg, users, processes, load averages)
1658
                $no_unit = ($uom === '');
1659
                // s - seconds (also us, ms)
1660
                $seconds = ($uom === 's') || ($uom === 'ms') || ($uom === 'us');
1661
                // % - percentage
1662
                $percentage = ($uom === '%');
1663
                // B - bytes (also KB, MB, TB)
1664
                // NOTE: GB is not in the official development guidelines,probably due to an error, so I've added them anyway
1665
                $bytes = ($uom === 'B') || ($uom === 'KB') || ($uom === 'MB') || ($uom === 'GB') || ($uom === 'TB');
1666
                // c - a continous counter (such as bytes transmitted on an interface)
1667
                $counter = ($uom === 'c');
1668
 
1669
                return ($no_unit || $seconds || $percentage || $bytes || $counter);
2 daniel-mar 1670
        }
1671
 
1672
        public function normalize($target=null) {
1673
                $res = clone $this;
1674
 
1675
                // The value is normalized to seconds or megabytes
1676
                if ($res->uom === 'ms') {
87 daniel-mar 1677
                        $res->uom = 's';
1678
                        $res->value /= 1000;
2 daniel-mar 1679
                }
1680
                if ($res->uom === 'us') {
87 daniel-mar 1681
                        $res->uom = 's';
1682
                        $res->value /= 1000 * 1000;
2 daniel-mar 1683
                }
1684
                if ($res->uom === 'B') {
87 daniel-mar 1685
                        $res->uom = 'MB';
1686
                        $res->value /= 1024 * 1024;
2 daniel-mar 1687
                }
1688
                if ($res->uom === 'KB') {
87 daniel-mar 1689
                        $res->uom = 'MB';
1690
                        $res->value /= 1024;
2 daniel-mar 1691
                }
1692
                if ($res->uom === 'GB') {
87 daniel-mar 1693
                        $res->uom = 'MB';
1694
                        $res->value *= 1024;
2 daniel-mar 1695
                }
1696
                if ($res->uom === 'TB') {
87 daniel-mar 1697
                        $res->uom = 'MB';
1698
                        $res->value *= 1024 * 1024;
2 daniel-mar 1699
                }
1700
                if ($res->uom === 'c') {
87 daniel-mar 1701
                        $res->uom = '';
2 daniel-mar 1702
                }
1703
 
1704
                // Now, if the user wishes, convert to another unit
1705
                if (!is_null($target)) {
1706
                        if ($res->uom == 'MB') {
1707
                                if ($target == 'B') {
1708
                                        $res->uom = 'B';
1709
                                        $res->value *= 1024 * 1024;
1710
                                } else if ($target == 'KB') {
1711
                                        $res->uom = 'KB';
1712
                                        $res->value *= 1024;
52 daniel-mar 1713
                                } else if ($target == 'MB') {
1714
                                        $res->uom = 'MB';
1715
                                        $res->value *= 1;
2 daniel-mar 1716
                                } else if ($target == 'GB') {
1717
                                        $res->uom = 'GB';
1718
                                        $res->value /= 1024;
1719
                                } else if ($target == 'TB') {
1720
                                        $res->uom = 'TB';
1721
                                        $res->value /= 1024 * 1024;
1722
                                } else {
1723
                                        throw new VNagUomConvertException($res->uom, $target);
1724
                                }
1725
                        } else if ($res->uom == 's') {
52 daniel-mar 1726
                                if ($target == 's') {
1727
                                        $res->uom = 's';
1728
                                        $res->value /= 1;
1729
                                } else if ($target == 'ms') {
2 daniel-mar 1730
                                        $res->uom = 'ms';
1731
                                        $res->value /= 1000;
1732
                                } else if ($target == 'us') {
1733
                                        $res->uom = 'us';
1734
                                        $res->value /= 1000 * 1000;
1735
                                } else {
1736
                                        throw new VNagUomConvertException($res->uom, $target);
1737
                                }
1738
                        } else {
1739
                                throw new VNagUomConvertException($res->uom, $target);
1740
                        }
1741
                }
1742
 
1743
                return $res;
1744
        }
1745
 
1746
        public function compatibleWith(VNagValueUomPair $other) {
1747
                $a = $this->normalize();
1748
                $b = $other->normalize();
1749
 
1750
                return ($a->uom == $b->uom);
1751
        }
1752
 
1753
        public static function compare(VNagValueUomPair $left, VNagValueUomPair $right) {
1754
                $a = $left->normalize();
1755
                $b = $right->normalize();
1756
 
1757
                // FUT: Also accept mixed UOMs, e.g. MB and %
1758
                //      To translate between an absolute and a relative value, the
1759
                //      reference value (100%=?) needs to be passed through this comparison
1760
                //      function somehow.
1761
                if ($a->uom != $b->uom) throw new VNagMixedUomsNotImplemented($a->uom, $b->uom);
1762
 
1763
                if ($a->value  > $b->value) return  1;
1764
                if ($a->value == $b->value) return  0;
1765
                if ($a->value  < $b->value) return -1;
1766
        }
1767
}
1768
 
1769
class VNagPerformanceData {
1770
        // see https://nagios-plugins.org/doc/guidelines.html#AEN200
1771
        //     https://www.icinga.com/docs/icinga1/latest/en/perfdata.html#formatperfdata
1772
 
1773
        protected $label;
1774
        protected /*VNagValueUomPair*/ $value;
1775
        protected $warn = null;
1776
        protected $crit = null;
1777
        protected $min = null;
1778
        protected $max = null;
1779
 
1780
        public static function createByString($perfdata) {
87 daniel-mar 1781
                $perfdata = trim($perfdata);
2 daniel-mar 1782
 
1783
                $ary = explode('=',$perfdata);
1784
                if (count($ary) != 2) {
1785
                        throw new VNagInvalidPerformanceDataException(sprintf(VNagLang::$perfdata_line_invalid, $perfdata));
1786
                }
1787
                $label = $ary[0];
1788
                $bry = explode(';',$ary[1]);
1789
                if (substr($label,0,1) === "'") $label = substr($label, 1, strlen($label)-2);
1790
                $value = $bry[0];
1791
                $warn  = isset($bry[1]) ? $bry[1] : null;
1792
                $crit  = isset($bry[2]) ? $bry[2] : null;
1793
                $min   = isset($bry[3]) ? $bry[3] : null;
1794
                $max   = isset($bry[4]) ? $bry[4] : null;
1795
 
1796
                // Guideline "7. min and max are not required if UOM=%" makes no sense, because
1797
                // actually, all fields (except label and value) are optional.
1798
 
1799
                return new self($label, $value, $warn, $crit, $min, $max);
1800
        }
1801
 
1802
        public function __construct($label, $value/*may include UOM*/, $warn=null, $crit=null, $min=null, $max=null) {
1803
                // Not checked / Nothing to check:
1804
                // - 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
1805
                // - 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
1806
                // - 9. warn and crit are in the range format (see the Section called Threshold and ranges). Must be the same UOM
1807
                // - 7. min and max are not required if UOM=%
1808
 
1809
                // 2. label can contain any characters except the equals sign or single quote (')
1810
                if (strpos($label, '=') !== false) throw new VNagInvalidPerformanceDataException(VNagLang::$perfdata_label_equal_sign_forbidden);
1811
 
1812
                // 5. to specify a quote character, use two single quotes
1813
                $label = str_replace("'", "''", $label);
1814
 
1815
                // 8. value, min and max in class [-0-9.]. Must all be the same UOM.
1816
                //    value may be a literal "U" instead, this would indicate that the actual value couldn't be determined
1817
                /*
1818
                if (($value != 'U') && (!preg_match('|^[-0-9\\.]+$|', $value, $m))) {
1819
                        throw new VNagInvalidPerformanceDataException(VNagLang::$perfdata_value_must_be_in_class);
1820
                }
1821
                */
22 daniel-mar 1822
                $m = array();
2 daniel-mar 1823
                if ((!_empty($min)) && (!preg_match('|^[-0-9\\.]+$|', $min, $m))) {
1824
                        throw new VNagInvalidPerformanceDataException(VNagLang::$perfdata_min_must_be_in_class);
1825
                }
1826
                if ((!_empty($max)) && (!preg_match('|^[-0-9\\.]+$|', $max, $m))) {
1827
                        throw new VNagInvalidPerformanceDataException(VNagLang::$perfdata_max_must_be_in_class);
1828
                }
1829
 
1830
                // 10. UOM (unit of measurement) is one of ....
1831
                //     => This rule is checked in the VNagValueUomPair constructor.
1832
 
1833
                $this->label = $label;
1834
                $this->value = ($value == 'U') ? 'U' : new VNagValueUomPair($value);
1835
                $this->warn  = $warn;
1836
                $this->crit  = $crit;
1837
                $this->min   = $min;
1838
                $this->max   = $max;
1839
        }
1840
 
1841
        public function __toString() {
1842
                $label = $this->label;
1843
                $value = $this->value;
1844
                $warn  = $this->warn;
1845
                $crit  = $this->crit;
1846
                $min   = $this->min;
1847
                $max   = $this->max;
1848
 
1849
                // 5. to specify a quote character, use two single quotes
1850
                $label = str_replace("''", "'", $label);
1851
 
1852
                // 'label'=value[UOM];[warn];[crit];[min];[max]
1853
                // 3. the single quotes for the label are optional. Required if spaces are in the label
1854
                return "'$label'=$value".
1855
                       ';'.(is_null($warn) ? '' : $warn).
1856
                       ';'.(is_null($crit) ? '' : $crit).
1857
                       ';'.(is_null($min)  ? '' : $min).
1858
                       ';'.(is_null($max)  ? '' : $max);
1859
        }
1860
}
1861
 
1862
class VNagHelp {
1863
        public $word_wrap_width = 80; // -1 = disable
1864
        public $argument_indent = 7;
1865
 
1866
        public function printUsagePage() {
1867
                $usage = $this->getUsage();
1868
 
1869
                if (_empty($usage)) {
1870
                        $usage = VNagLang::$no_syntax_defined;
1871
                }
1872
 
1873
                return trim($usage)."\n";
1874
        }
1875
 
1876
        public function printVersionPage() {
1877
                $out = trim($this->getNameAndVersion())."\n";
1878
 
1879
                if ($this->word_wrap_width > 0) $out = wordwrap($out, $this->word_wrap_width, "\n", false);
1880
 
1881
                return $out;
1882
        }
1883
 
1884
        static private function _conditionalLine($line, $terminator='', $prefix='') {
1885
                if (!_empty($line)) {
1886
                        return trim($line).$terminator;
1887
                }
1888
                return '';
1889
        }
1890
 
1891
        public function printHelpPage() {
1892
                $out  = '';
1893
                $out .= self::_conditionalLine($this->getNameAndVersion(), "\n");
1894
                $out .= self::_conditionalLine($this->getCopyright(), "\n");
1895
                $out .= ($out != '') ? "\n" : '';
1896
                $out .= self::_conditionalLine($this->getShortDescription(), "\n\n\n");
1897
                $out .= self::_conditionalLine($this->getUsage(), "\n\n");
1898
 
1899
                $out .= VNagLang::$options."\n";
1900
                foreach ($this->options as $argObj) {
1901
                        $out .= $this->printArgumentHelp($argObj);
1902
                }
1903
 
1904
                $out .= self::_conditionalLine($this->getFootNotes(), "\n\n", "\n");
1905
 
1906
                if ($this->word_wrap_width > 0) $out = wordwrap($out, $this->word_wrap_width, "\n", false);
1907
 
1908
                return $out;
1909
        }
1910
 
1911
        protected /* VNagArgument[] */ $options = array();
1912
 
1913
        // Will be called by VNag via ReflectionMethod (like C++ style friends), because it should not be called manually.
1914
        // Use VNag's function instead (since it adds to the argHandler too)
1915
        protected function _addOption($argObj) {
1916
                $this->options[] = $argObj;
1917
        }
1918
 
1919
        # FUT: Automatic creation of usage page. Which arguments are necessary?
1920
        protected function printArgumentHelp($argObj) {
1921
                $identifiers = array();
1922
 
1923
                $shortopt = $argObj->getShortopt();
1924
                if (!_empty($shortopt)) $identifiers[] = '-'.$shortopt;
1925
 
1926
                $longopts = $argObj->getLongopts();
1927
                if (!is_null($longopts)) {
1928
                        foreach ($longopts as $longopt) {
1929
                                if (!_empty($longopt)) $identifiers[] = '--'.$longopt;
1930
                        }
1931
                }
1932
 
1933
                if (count($identifiers) == 0) return;
1934
 
1935
                $valueName = $argObj->getValueName();
1936
 
1937
                $arginfo = '';
1938
                switch ($argObj->getValuePolicy()) {
1939
                        case VNagArgument::VALUE_FORBIDDEN:
1940
                                $arginfo = '';
1941
                                break;
1942
                        case VNagArgument::VALUE_REQUIRED:
1943
                                $arginfo = '='.$valueName;
1944
                                break;
1945
                        case VNagArgument::VALUE_OPTIONAL:
1946
                                $arginfo = '[='.$valueName.']';
1947
                                break;
1948
                }
1949
 
1950
                $out = '';
1951
                $out .= implode(', ', $identifiers).$arginfo."\n";
1952
 
1953
                // https://nagios-plugins.org/doc/guidelines.html#AEN302 recommends supporting a 80x23 screen resolution.
1954
                // While we cannot guarantee the vertical height, we can limit the width at least...
1955
 
1956
                $content = trim($argObj->getHelpText());
1957
                if ($this->word_wrap_width > 0) $content = wordwrap($content, $this->word_wrap_width-$this->argument_indent, "\n", false);
1958
                $lines = explode("\n", $content);
1959
 
1960
                foreach ($lines as $line) {
1961
                        $out .= str_repeat(' ', $this->argument_indent).$line."\n";
1962
                }
1963
                $out .= "\n";
1964
 
1965
                return $out;
1966
        }
1967
 
1968
        // $pluginName should contain the name of the plugin, without version.
1969
        protected $pluginName;
1970
        public function setPluginName($pluginName) {
1971
                $this->pluginName = $this->replaceStuff($pluginName);
1972
        }
1973
        public function getPluginName() {
1974
                if (_empty($this->pluginName)) {
1975
                        global $argv;
1976
                        return basename($argv[0]);
1977
                } else {
1978
                        return $this->pluginName;
1979
                }
1980
        }
1981
 
1982
        // $version should contain the version, not the program name or copyright.
1983
        protected $version;
1984
        public function setVersion($version) {
1985
                $this->version = $this->replaceStuff($version);
1986
        }
1987
        public function getVersion() {
1988
                return $this->version;
1989
        }
1990
        public function getNameAndVersion() {
1991
                $ret = $this->getPluginName();
1992
                if (_empty($ret)) return null;
1993
 
1994
                $ver = $this->getVersion();
1995
                if (!_empty($ver)) {
1996
                        $ret = sprintf(VNagLang::$x_version_x, $ret, $ver);
1997
                }
1998
                $ret = trim($ret);
1999
 
2000
                return $ret;
2001
        }
2002
 
2003
        // $copyright should contain the copyright only, no program name or version.
2004
        // $CURYEAR$ will be replaced by the current year
2005
        protected $copyright;
2006
        public function setCopyright($copyright) {
2007
                $this->copyright = $this->replaceStuff($copyright);
2008
        }
2009
 
2010
        private function getVNagCopyright() {
2011
                if (VNag::is_http_mode()) {
2012
                        $vts_email = 'www.viathinksoft.com'; // don't publish email address at web services because of spam bots
2013
                } else {
2014
                        $vts_email = base64_decode('aW5mb0B2aWF0aGlua3NvZnQuZGU='); // protect email address from spambots which might parse this code
2015
                }
2016
                return "VNag Framework ".VNag::VNAG_VERSION." (C) 2014-".date('Y')." ViaThinkSoft <$vts_email>";
2017
        }
2018
 
2019
        public function getCopyright() {
2020
                if (_empty($this->copyright)) {
2021
                        return sprintf(VNagLang::$plugin_uses, $this->getVNagCopyright());
2022
                } else {
2023
                        return trim($this->copyright)."\n".sprintf(VNagLang::$uses, $this->getVNagCopyright());
2024
                }
2025
        }
2026
 
2027
        // $shortDescription should describe what this plugin does.
2028
        protected $shortDescription;
2029
        public function setShortDescription($shortDescription) {
2030
                $this->shortDescription = $this->replaceStuff($shortDescription);
2031
        }
2032
        public function getShortDescription() {
2033
                if (_empty($this->shortDescription)) {
2034
                        return null;
2035
                } else {
2036
                        $content = $this->shortDescription;
2037
                        if ($this->word_wrap_width > 0) $content = wordwrap($content, $this->word_wrap_width, "\n", false);
2038
                        return $content;
2039
                }
2040
        }
2041
 
2042
        protected function replaceStuff($text) {
2043
                global $argv;
73 daniel-mar 2044
                if (php_sapi_name() == 'cli') {
2045
                        $text = str_replace('$SCRIPTNAME$', $argv[0], $text);
2046
                } else {
2047
                        $text = str_replace('$SCRIPTNAME$', basename($_SERVER['SCRIPT_NAME']), $text);
2048
                }
2 daniel-mar 2049
                $text = str_replace('$CURYEAR$', date('Y'), $text);
2050
                return $text;
2051
        }
2052
 
2053
        // $syntax should contain the option syntax only, no explanations.
2054
        // $SCRIPTNAME$ will be replaced by the actual script name
2055
        // $CURYEAR$ will be replaced by the current year
2056
        # FUT: Automatically generate syntax?
2057
        protected $syntax;
2058
        public function setSyntax($syntax) {
2059
                $syntax = $this->replaceStuff($syntax);
2060
                $this->syntax = $syntax;
2061
        }
2062
        public function getUsage() {
2063
                if (_empty($this->syntax)) {
2064
                        return null;
2065
                } else {
2066
                        return sprintf(VNagLang::$usage_x, $this->syntax);
2067
                }
2068
        }
2069
 
2070
        // $footNotes can be contact information or other notes which should appear in --help
2071
        protected $footNotes;
2072
        public function setFootNotes($footNotes) {
2073
                $this->footNotes = $this->replaceStuff($footNotes);
2074
        }
2075
        public function getFootNotes() {
2076
                return $this->footNotes;
2077
        }
2078
}
2079
 
2080
class VNagLang {
2081
        public static function status($code, $statusmodel) {
2082
                switch ($statusmodel) {
2083
                        case VNag::STATUSMODEL_SERVICE:
2084
                                switch ($code) {
2085
                                        case VNag::STATUS_OK:
2086
                                                return 'OK';
29 daniel-mar 2087
                                                #break;
2 daniel-mar 2088
                                        case VNag::STATUS_WARNING:
2089
                                                return 'Warning';
29 daniel-mar 2090
                                                #break;
2 daniel-mar 2091
                                        case VNag::STATUS_CRITICAL:
2092
                                                return 'Critical';
29 daniel-mar 2093
                                                #break;
2 daniel-mar 2094
                                        case VNag::STATUS_UNKNOWN:
2095
                                                return 'Unknown';
29 daniel-mar 2096
                                                #break;
2 daniel-mar 2097
                                        default:
2098
                                                return sprintf('Error (%d)', $code);
29 daniel-mar 2099
                                                #break;
2 daniel-mar 2100
                                }
29 daniel-mar 2101
                                #break;
2 daniel-mar 2102
                        case VNag::STATUSMODEL_HOST:
2103
                                switch ($code) {
2104
                                        case VNag::STATUS_UP:
2105
                                                return 'Up';
29 daniel-mar 2106
                                                #break;
2 daniel-mar 2107
                                        case VNag::STATUS_DOWN:
2108
                                                return 'Down';
29 daniel-mar 2109
                                                #break;
2 daniel-mar 2110
                                        default:
2111
                                                return sprintf('Maintain last state (%d)', $code);
29 daniel-mar 2112
                                                #break;
2 daniel-mar 2113
                                }
29 daniel-mar 2114
                                #break;
2 daniel-mar 2115
                        default:
2116
                                throw new VNagIllegalStatusModel(sprintf(self::$illegal_statusmodel, $statusmodel));
29 daniel-mar 2117
                                #break;
2 daniel-mar 2118
                }
2119
        }
2120
 
2121
        static $nagios_output = 'VNag-Output';
2122
        static $verbose_info = 'Verbose information';
2123
        static $status = 'Status';
2124
        static $message = 'Message';
2125
        static $performance_data = 'Performance data';
2126
        static $status_ok = 'OK';
2127
        static $status_warn = 'Warning';
2128
        static $status_critical = 'Critical';
2129
        static $status_unknown = 'Unknown';
2130
        static $status_error = 'Error';
2131
        static $unhandled_exception_without_msg = "Unhandled exception of type %s";
2132
        static $plugin_uses = 'This plugin uses %s';
2133
        static $uses = 'uses %s';
2134
        static $x_version_x = '%s, version %s';
2135
 
2136
        // Argument names (help page)
2137
        static $argname_value = 'value';
2138
        static $argname_seconds = 'seconds';
2139
 
2140
        // Exceptions
2141
        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.";
2142
        static $required_argument_missing = "The argument '%s' is required.";
2143
        static $performance_data_invalid = 'Performance data invalid.';
2144
        static $no_standard_arguments_with_letter = "No standard argument with letter '%s' exists.";
2145
        static $invalid_start_value = 'Invalid start value.';
2146
        static $invalid_end_value = 'Invalid end value.';
2147
        static $start_is_greater_than_end = 'Start is greater than end value.';
2148
        static $value_name_forbidden = "Implementation error: You may not define a value name for the argument, because the value policy is VALUE_FORBIDDEN.";
2149
        static $value_name_required = "Implementation error: Please define a name for the argument (so it can be shown in the help page).";
2150
        static $illegal_shortopt = "Illegal shortopt '-%s'.";
2151
        static $illegal_longopt = "Illegal longopt '--%s'.";
2152
        static $illegal_valuepolicy = "valuePolicy has illegal value '%s'.";
2153
        static $range_invalid_syntax = "Syntax error in range '%s'.";
2154
        static $timeout_value_invalid = "Timeout value '%s' is invalid.";
2155
        static $range_is_invalid = 'Range is invalid.';
2156
        static $timeout_exception = 'Timeout!';
2157
        static $perfdata_label_equal_sign_forbidden = 'Label may not contain an equal sign.';
2158
        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.';
2159
        static $perfdata_min_must_be_in_class = 'Min must be in class [-0-9.] or empty.';
2160
        static $perfdata_max_must_be_in_class = 'Max must be in class [-0-9.] or empty.';
2161
        static $perfdata_uom_not_recognized = 'UOM (unit of measurement) "%s" is not recognized.';
87 daniel-mar 2162
        static $perfdata_mixed_uom_not_implemented = 'Mixed UOMs (%s and %s) are currently not supported.';
2163
        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 2164
        static $exception_x = '%s (%s)';
2165
        static $no_syntax_defined = 'The author of this plugin has not defined a syntax for this plugin.';
2166
        static $usage_x = "Usage:\n%s";
2167
        static $options = "Options:";
2168
        static $illegal_statusmodel = "Invalid statusmodel %d.";
2169
        static $none = '[none]';
2170
        static $valueUomPairSyntaxError = 'Syntax error at "%s". Syntax must be Value[UOM].';
2171
        static $too_few_warning_ranges = "You have too few warning ranges (currently trying to get element %d).";
2172
        static $too_few_critical_ranges = "You have too few critical ranges (currently trying to get element %d).";
2173
        static $dataset_missing = 'Dataset missing.';
2174
        static $payload_not_base64 = 'The payload is not valid Base64.';
2175
        static $payload_not_json = 'The payload is not valid JSON.';
2176
        static $signature_missing = 'The signature is missing.';
2177
        static $signature_not_bas64 = 'The signature is not valid Base64.';
2178
        static $signature_invalid = 'The signature is invalid. The connection might have been tampered, or a different key is used.';
2179
        static $pubkey_file_not_found = "Public key file %s was not found.";
2180
        static $pubkey_file_not_readable = "Public key file %s is not readable.";
2181
        static $privkey_file_not_found = "Private key file %s was not found.";
24 daniel-mar 2182
        static $privkey_not_readable = "Private key is not readable.";
2 daniel-mar 2183
        static $privkey_file_not_readable = "Private key file %s is not readable.";
24 daniel-mar 2184
        static $signature_failed = "Signature failed.";
2 daniel-mar 2185
        static $perfdata_line_invalid = "Performance data line %s is invalid.";
2186
        static $singlevalue_unexpected_at_symbol = 'This plugin does not allow the @-symbol at ranges for single values.';
2187
        static $illegalSingleValueBehavior = "Illegal value for 'singleValueBehavior'. Please contact the creator of the plugin.";
2188
        static $dataset_encryption_no_array = 'Dataset encryption information invalid.';
2189
        static $require_password = 'This resource is protected with a password. Please provide a password.';
2190
        static $wrong_password = 'This resource is protected with a password. You have provided the wrong password, or it was changed.';
2191
        static $convert_x_y_error = 'Cannot convert from UOM %s to UOM %s.';
2192
        static $php_error = 'PHP has detected an error in the plugin. Please contact the plugin author.';
2193
        static $output_level_lowered = "Output Buffer level lowered during cbRun(). Please contact the plugin author.";
21 daniel-mar 2194
        static $openssl_missing = "OpenSSL is missing. Therefore, encryption and signatures are not available.";
2 daniel-mar 2195
 
2196
        // Help texts
2197
        static $warning_range = 'Warning range';
2198
        static $critical_range = 'Critical range';
2199
        static $prints_version = 'Prints version';
2200
        static $verbosity_helptext = 'Verbosity -v, -vv or -vvv';
2201
        static $timeout_helptext = 'Sets timeout in seconds';
2202
        static $help_helptext = 'Prints help page';
2203
        static $prints_usage = 'Prints usage';
2204
 
2205
        static $notConstructed = 'Parent constructor not called with parent::__construct().';
2206
}
2207
 
2208
function vnagErrorHandler($errorkind, $errortext, $file, $line) {
20 daniel-mar 2209
        // This function "converts" PHP runtime errors into Exceptions, which can then be handled by VNag::handleException()
19 daniel-mar 2210
        global $inside_vnag_run;
20 daniel-mar 2211
 
19 daniel-mar 2212
        if (!$inside_vnag_run && VNag::is_http_mode()) {
2213
                // We want to avoid that the VNag-Exception will show up in a website that contains
2214
                // an embedded VNag monitor, so if we are not inside a running VNag code,
2215
                // we will call the normal PHP error handler.
2216
                return false;
2217
        }
20 daniel-mar 2218
 
2 daniel-mar 2219
        if (!(error_reporting() & $errorkind)) {
2220
                // Code is not included in error_reporting. Don't do anything.
19 daniel-mar 2221
                return true;
2 daniel-mar 2222
        }
2223
 
2224
        // We want 100% clean scripts, so any error, warning or notice will shutdown the script
2225
        // This also fixes the issue that PHP will end with result code 0, showing an error.
2226
 
2227
        // Error kinds see http://php.net/manual/en/errorfunc.constants.php
2228
        if (defined('E_ERROR') && ($errorkind == E_ERROR)) $errorkind = 'Error';
2229
        if (defined('E_WARNING') && ($errorkind == E_WARNING)) $errorkind = 'Warning';
2230
        if (defined('E_PARSE') && ($errorkind == E_PARSE)) $errorkind = 'Parse';
2231
        if (defined('E_NOTICE') && ($errorkind == E_NOTICE)) $errorkind = 'Notice';
2232
        if (defined('E_CORE_ERROR') && ($errorkind == E_CORE_ERROR)) $errorkind = 'Core Error';
2233
        if (defined('E_CORE_WARNING') && ($errorkind == E_CORE_WARNING)) $errorkind = 'Core Warning';
2234
        if (defined('E_COMPILE_ERROR') && ($errorkind == E_COMPILE_ERROR)) $errorkind = 'Compile Error';
2235
        if (defined('E_COMPILE_WARNING') && ($errorkind == E_COMPILE_WARNING)) $errorkind = 'Compile Warning';
2236
        if (defined('E_USER_ERROR') && ($errorkind == E_USER_ERROR)) $errorkind = 'User Error';
2237
        if (defined('E_USER_WARNING') && ($errorkind == E_USER_WARNING)) $errorkind = 'User Warning';
2238
        if (defined('E_USER_NOTICE') && ($errorkind == E_USER_NOTICE)) $errorkind = 'User Notice';
2239
        if (defined('E_STRICT') && ($errorkind == E_STRICT)) $errorkind = 'Strict';
2240
        if (defined('E_RECOVERABLE_ERROR') && ($errorkind == E_RECOVERABLE_ERROR)) $errorkind = 'Recoverable Error';
2241
        if (defined('E_DEPRECATED') && ($errorkind == E_DEPRECATED)) $errorkind = 'Deprecated';
2242
        if (defined('E_USER_DEPRECATED') && ($errorkind == E_USER_DEPRECATED)) $errorkind = 'User Deprecated';
2243
        throw new VNagException(VNagLang::$php_error . " $errortext at $file:$line (kind $errorkind)");
2244
 
19 daniel-mar 2245
        // true = the PHP internal error handling will NOT be called.
29 daniel-mar 2246
        #return true;
2 daniel-mar 2247
}
2248
 
19 daniel-mar 2249
$inside_vnag_run = false;
2 daniel-mar 2250
$old_error_handler = set_error_handler("vnagErrorHandler");