Subversion Repositories oidplus

Rev

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

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