Subversion Repositories vnag

Rev

Rev 27 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

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