Subversion Repositories vnag

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

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