Subversion Repositories vnag

Rev

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

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