summaryrefslogtreecommitdiffstats
path: root/plugins
diff options
context:
space:
mode:
Diffstat (limited to 'plugins')
-rw-r--r--plugins/check_apt.c251
-rw-r--r--plugins/check_apt.d/config.h9
-rw-r--r--plugins/check_by_ssh.c23
-rw-r--r--plugins/check_by_ssh.d/config.h2
-rw-r--r--plugins/check_cluster.c73
-rw-r--r--plugins/check_cluster.d/config.h6
-rw-r--r--plugins/check_curl.d/check_curl_helpers.c20
-rw-r--r--plugins/check_mysql.c44
-rw-r--r--plugins/check_ntp_peer.c360
-rw-r--r--plugins/check_ntp_peer.d/config.h63
-rw-r--r--plugins/check_ntp_time.c188
-rw-r--r--plugins/check_ntp_time.d/config.h19
-rw-r--r--plugins/check_pgsql.c292
-rw-r--r--plugins/check_pgsql.d/config.h29
-rw-r--r--plugins/check_snmp.c2
-rw-r--r--plugins/check_swap.d/swap.c4
-rw-r--r--plugins/check_users.c2
-rw-r--r--plugins/check_users.d/users.c2
-rw-r--r--plugins/t/check_apt.t14
19 files changed, 912 insertions, 491 deletions
diff --git a/plugins/check_apt.c b/plugins/check_apt.c
index ab66a8d2..9ed5b6cf 100644
--- a/plugins/check_apt.c
+++ b/plugins/check_apt.c
@@ -29,31 +29,33 @@
29 * 29 *
30 *****************************************************************************/ 30 *****************************************************************************/
31 31
32#include "states.h" 32#include "perfdata.h"
33const char *progname = "check_apt"; 33const char *progname = "check_apt";
34const char *copyright = "2006-2024"; 34const char *copyright = "2006-2024";
35const char *email = "devel@monitoring-plugins.org"; 35const char *email = "devel@monitoring-plugins.org";
36 36
37#include "states.h"
38#include "output.h"
37#include "common.h" 39#include "common.h"
38#include "runcmd.h" 40#include "runcmd.h"
39#include "utils.h" 41#include "utils.h"
40#include "regex.h" 42#include "regex.h"
41#include "check_apt.d/config.h" 43#include "check_apt.d/config.h"
42 44
43/* Character for hidden input file option (for testing). */
44#define INPUT_FILE_OPT CHAR_MAX + 1
45/* the default opts can be overridden via the cmdline */ 45/* the default opts can be overridden via the cmdline */
46#define UPGRADE_DEFAULT_OPTS "-o 'Debug::NoLocking=true' -s -qq" 46const char *UPGRADE_DEFAULT_OPTS = "-o 'Debug::NoLocking=true' -s -qq";
47#define UPDATE_DEFAULT_OPTS "-q" 47const char *UPDATE_DEFAULT_OPTS = "-q";
48
48/* until i commit the configure.in patch which gets this, i'll define 49/* until i commit the configure.in patch which gets this, i'll define
49 * it here as well */ 50 * it here as well */
50#ifndef PATH_TO_APTGET 51#ifndef PATH_TO_APTGET
51# define PATH_TO_APTGET "/usr/bin/apt-get" 52# define PATH_TO_APTGET "/usr/bin/apt-get"
52#endif /* PATH_TO_APTGET */ 53#endif /* PATH_TO_APTGET */
54
53/* String found at the beginning of the apt output lines we're interested in */ 55/* String found at the beginning of the apt output lines we're interested in */
54#define PKGINST_PREFIX "Inst " 56const char *PKGINST_PREFIX = "Inst ";
55/* the RE that catches security updates */ 57/* the RE that catches security updates */
56#define SECURITY_RE "^[^\\(]*\\(.* (Debian-Security:|Ubuntu:[^/]*/[^-]*-security)" 58const char *SECURITY_RE = "^[^\\(]*\\(.* (Debian-Security:|Ubuntu:[^/]*/[^-]*-security)";
57 59
58/* some standard functions */ 60/* some standard functions */
59typedef struct { 61typedef struct {
@@ -66,20 +68,28 @@ void print_usage(void);
66 68
67/* construct the appropriate apt-get cmdline */ 69/* construct the appropriate apt-get cmdline */
68static char *construct_cmdline(upgrade_type /*u*/, const char * /*opts*/); 70static char *construct_cmdline(upgrade_type /*u*/, const char * /*opts*/);
71
69/* run an apt-get update */ 72/* run an apt-get update */
70static int run_update(char * /*update_opts*/); 73typedef struct {
74 mp_subcheck sc;
75 bool stderr_warning;
76 bool exec_warning;
77} run_update_result;
78static run_update_result run_update(char *update_opts);
71 79
72typedef struct { 80typedef struct {
73 int errorcode; 81 int errorcode;
74 int package_count; 82 size_t package_count;
75 int security_package_count; 83 size_t security_package_count;
76 char **packages_list; 84 char **packages_list;
77 char **secpackages_list; 85 char **secpackages_list;
86 bool exec_warning;
78} run_upgrade_result; 87} run_upgrade_result;
79 88
80/* run an apt-get upgrade */ 89/* run an apt-get upgrade */
81run_upgrade_result run_upgrade(upgrade_type upgrade, const char *do_include, const char *do_exclude, const char *do_critical, 90run_upgrade_result run_upgrade(upgrade_type upgrade, const char *do_include, const char *do_exclude,
82 const char *upgrade_opts, const char *input_filename); 91 const char *do_critical, const char *upgrade_opts,
92 const char *input_filename);
83 93
84/* add another clause to a regexp */ 94/* add another clause to a regexp */
85static char *add_to_regexp(char * /*expr*/, const char * /*next*/); 95static char *add_to_regexp(char * /*expr*/, const char * /*next*/);
@@ -107,6 +117,10 @@ int main(int argc, char **argv) {
107 117
108 const check_apt_config config = tmp_config.config; 118 const check_apt_config config = tmp_config.config;
109 119
120 if (config.output_format_is_set) {
121 mp_set_format(config.output_format);
122 }
123
110 /* Set signal handling and alarm timeout */ 124 /* Set signal handling and alarm timeout */
111 if (signal(SIGALRM, timeout_alarm_handler) == SIG_ERR) { 125 if (signal(SIGALRM, timeout_alarm_handler) == SIG_ERR) {
112 usage_va(_("Cannot catch SIGALRM")); 126 usage_va(_("Cannot catch SIGALRM"));
@@ -115,55 +129,91 @@ int main(int argc, char **argv) {
115 /* handle timeouts gracefully... */ 129 /* handle timeouts gracefully... */
116 alarm(timeout_interval); 130 alarm(timeout_interval);
117 131
118 mp_state_enum result = STATE_UNKNOWN; 132 mp_check overall = mp_check_init();
119 /* if they want to run apt-get update first... */ 133 /* if they want to run apt-get update first... */
120 if (config.do_update) { 134 if (config.do_update) {
121 result = run_update(config.update_opts); 135 run_update_result update_result = run_update(config.update_opts);
136
137 mp_add_subcheck_to_check(&overall, update_result.sc);
122 } 138 }
123 139
124 /* apt-get upgrade */ 140 /* apt-get upgrade */
125 run_upgrade_result upgrad_res = 141 run_upgrade_result upgrad_res =
126 run_upgrade(config.upgrade, config.do_include, config.do_exclude, config.do_critical, config.upgrade_opts, config.input_filename); 142 run_upgrade(config.upgrade, config.do_include, config.do_exclude, config.do_critical,
143 config.upgrade_opts, config.input_filename);
144
145 mp_subcheck sc_run_upgrade = mp_subcheck_init();
146 if (upgrad_res.errorcode == OK) {
147 sc_run_upgrade = mp_set_subcheck_state(sc_run_upgrade, STATE_OK);
148 }
149 xasprintf(&sc_run_upgrade.output, "Executed apt upgrade (dry run)");
127 150
128 result = max_state(result, upgrad_res.errorcode); 151 mp_add_subcheck_to_check(&overall, sc_run_upgrade);
129 int packages_available = upgrad_res.package_count; 152
130 int sec_count = upgrad_res.security_package_count; 153 size_t packages_available = upgrad_res.package_count;
154 size_t number_of_security_updates = upgrad_res.security_package_count;
131 char **packages_list = upgrad_res.packages_list; 155 char **packages_list = upgrad_res.packages_list;
132 char **secpackages_list = upgrad_res.secpackages_list; 156 char **secpackages_list = upgrad_res.secpackages_list;
133 157
134 if (sec_count > 0) { 158 mp_perfdata pd_security_updates = perfdata_init();
135 result = max_state(result, STATE_CRITICAL); 159 pd_security_updates.value = mp_create_pd_value(number_of_security_updates);
136 } else if (packages_available >= config.packages_warning && !config.only_critical) { 160 pd_security_updates.label = "critical_updates";
137 result = max_state(result, STATE_WARNING); 161
138 } else if (result > STATE_UNKNOWN) { 162 mp_subcheck sc_security_updates = mp_subcheck_init();
139 result = STATE_UNKNOWN; 163 xasprintf(&sc_security_updates.output, "Security updates available: %zu",
164 number_of_security_updates);
165 mp_add_perfdata_to_subcheck(&sc_security_updates, pd_security_updates);
166
167 if (number_of_security_updates > 0) {
168 sc_security_updates = mp_set_subcheck_state(sc_security_updates, STATE_CRITICAL);
169 } else {
170 sc_security_updates = mp_set_subcheck_state(sc_security_updates, STATE_OK);
140 } 171 }
141 172
142 printf(_("APT %s: %d packages available for %s (%d critical updates). %s%s%s%s|available_upgrades=%d;;;0 critical_updates=%d;;;0\n"), 173 mp_perfdata pd_other_updates = perfdata_init();
143 state_text(result), packages_available, (config.upgrade == DIST_UPGRADE) ? "dist-upgrade" : "upgrade", sec_count, 174 pd_other_updates.value = mp_create_pd_value(packages_available);
144 (stderr_warning) ? " warnings detected" : "", (stderr_warning && exec_warning) ? "," : "", 175 pd_other_updates.label = "available_upgrades";
145 (exec_warning) ? " errors detected" : "", (stderr_warning || exec_warning) ? "." : "", packages_available, sec_count); 176
177 mp_subcheck sc_other_updates = mp_subcheck_init();
178
179 xasprintf(&sc_other_updates.output, "Updates available: %zu", packages_available);
180 sc_other_updates = mp_set_subcheck_default_state(sc_other_updates, STATE_OK);
181 mp_add_perfdata_to_subcheck(&sc_other_updates, pd_other_updates);
182
183 if (packages_available >= config.packages_warning && !config.only_critical) {
184 sc_other_updates = mp_set_subcheck_state(sc_other_updates, STATE_WARNING);
185 }
146 186
147 if (config.list) { 187 if (config.list) {
148 qsort(secpackages_list, sec_count, sizeof(char *), cmpstringp); 188 qsort(secpackages_list, number_of_security_updates, sizeof(char *), cmpstringp);
149 qsort(packages_list, packages_available - sec_count, sizeof(char *), cmpstringp); 189 qsort(packages_list, packages_available - number_of_security_updates, sizeof(char *),
190 cmpstringp);
150 191
151 for (int i = 0; i < sec_count; i++) { 192 for (size_t i = 0; i < number_of_security_updates; i++) {
152 printf("%s (security)\n", secpackages_list[i]); 193 xasprintf(&sc_security_updates.output, "%s\n%s (security)", sc_security_updates.output,
194 secpackages_list[i]);
153 } 195 }
154 196
155 if (!config.only_critical) { 197 if (!config.only_critical) {
156 for (int i = 0; i < packages_available - sec_count; i++) { 198 for (size_t i = 0; i < packages_available - number_of_security_updates; i++) {
157 printf("%s\n", packages_list[i]); 199 xasprintf(&sc_other_updates.output, "%s\n%s", sc_other_updates.output,
200 packages_list[i]);
158 } 201 }
159 } 202 }
160 } 203 }
204 mp_add_subcheck_to_check(&overall, sc_security_updates);
205 mp_add_subcheck_to_check(&overall, sc_other_updates);
161 206
162 return result; 207 mp_exit(overall);
163} 208}
164 209
165/* process command-line arguments */ 210/* process command-line arguments */
166check_apt_config_wrapper process_arguments(int argc, char **argv) { 211check_apt_config_wrapper process_arguments(int argc, char **argv) {
212 enum {
213 /* Character for hidden input file option (for testing). */
214 INPUT_FILE_OPT = CHAR_MAX + 1,
215 output_format_index,
216 };
167 static struct option longopts[] = {{"version", no_argument, 0, 'V'}, 217 static struct option longopts[] = {{"version", no_argument, 0, 'V'},
168 {"help", no_argument, 0, 'h'}, 218 {"help", no_argument, 0, 'h'},
169 {"verbose", no_argument, 0, 'v'}, 219 {"verbose", no_argument, 0, 'v'},
@@ -179,6 +229,7 @@ check_apt_config_wrapper process_arguments(int argc, char **argv) {
179 {"only-critical", no_argument, 0, 'o'}, 229 {"only-critical", no_argument, 0, 'o'},
180 {"input-file", required_argument, 0, INPUT_FILE_OPT}, 230 {"input-file", required_argument, 0, INPUT_FILE_OPT},
181 {"packages-warning", required_argument, 0, 'w'}, 231 {"packages-warning", required_argument, 0, 'w'},
232 {"output-format", required_argument, 0, output_format_index},
182 {0, 0, 0, 0}}; 233 {0, 0, 0, 0}};
183 234
184 check_apt_config_wrapper result = { 235 check_apt_config_wrapper result = {
@@ -257,6 +308,18 @@ check_apt_config_wrapper process_arguments(int argc, char **argv) {
257 case 'w': 308 case 'w':
258 result.config.packages_warning = atoi(optarg); 309 result.config.packages_warning = atoi(optarg);
259 break; 310 break;
311 case output_format_index: {
312 parsed_output_format parser = mp_parse_output_format(optarg);
313 if (!parser.parsing_success) {
314 // TODO List all available formats here, maybe add anothoer usage function
315 printf("Invalid output format: %s\n", optarg);
316 exit(STATE_UNKNOWN);
317 }
318
319 result.config.output_format_is_set = true;
320 result.config.output_format = parser.output_format;
321 break;
322 }
260 default: 323 default:
261 /* print short usage statement if args not parsable */ 324 /* print short usage statement if args not parsable */
262 usage5(); 325 usage5();
@@ -267,37 +330,38 @@ check_apt_config_wrapper process_arguments(int argc, char **argv) {
267} 330}
268 331
269/* run an apt-get upgrade */ 332/* run an apt-get upgrade */
270run_upgrade_result run_upgrade(const upgrade_type upgrade, const char *do_include, const char *do_exclude, const char *do_critical, 333run_upgrade_result run_upgrade(const upgrade_type upgrade, const char *do_include,
334 const char *do_exclude, const char *do_critical,
271 const char *upgrade_opts, const char *input_filename) { 335 const char *upgrade_opts, const char *input_filename) {
272 regex_t ereg; 336 regex_t exclude_regex;
273 /* initialize ereg as it is possible it is printed while uninitialized */ 337 /* initialize ereg as it is possible it is printed while uninitialized */
274 memset(&ereg, '\0', sizeof(ereg.buffer)); 338 memset(&exclude_regex, '\0', sizeof(exclude_regex.buffer));
275 339
276 run_upgrade_result result = { 340 run_upgrade_result result = {
277 .errorcode = STATE_UNKNOWN, 341 .errorcode = OK,
278 }; 342 };
279 343
280 if (upgrade == NO_UPGRADE) { 344 if (upgrade == NO_UPGRADE) {
281 result.errorcode = STATE_OK; 345 result.errorcode = OK;
282 return result; 346 return result;
283 } 347 }
284 348
285 int regres = 0; 349 int regres = 0;
286 regex_t ireg; 350 regex_t include_regex;
287 char rerrbuf[64]; 351 char rerrbuf[64];
288 /* compile the regexps */ 352 /* compile the regexps */
289 if (do_include != NULL) { 353 if (do_include != NULL) {
290 regres = regcomp(&ireg, do_include, REG_EXTENDED); 354 regres = regcomp(&include_regex, do_include, REG_EXTENDED);
291 if (regres != 0) { 355 if (regres != 0) {
292 regerror(regres, &ireg, rerrbuf, 64); 356 regerror(regres, &include_regex, rerrbuf, 64);
293 die(STATE_UNKNOWN, _("%s: Error compiling regexp: %s"), progname, rerrbuf); 357 die(STATE_UNKNOWN, _("%s: Error compiling regexp: %s"), progname, rerrbuf);
294 } 358 }
295 } 359 }
296 360
297 if (do_exclude != NULL) { 361 if (do_exclude != NULL) {
298 regres = regcomp(&ereg, do_exclude, REG_EXTENDED); 362 regres = regcomp(&exclude_regex, do_exclude, REG_EXTENDED);
299 if (regres != 0) { 363 if (regres != 0) {
300 regerror(regres, &ereg, rerrbuf, 64); 364 regerror(regres, &exclude_regex, rerrbuf, 64);
301 die(STATE_UNKNOWN, _("%s: Error compiling regexp: %s"), progname, rerrbuf); 365 die(STATE_UNKNOWN, _("%s: Error compiling regexp: %s"), progname, rerrbuf);
302 } 366 }
303 } 367 }
@@ -306,7 +370,7 @@ run_upgrade_result run_upgrade(const upgrade_type upgrade, const char *do_includ
306 const char *crit_ptr = (do_critical != NULL) ? do_critical : SECURITY_RE; 370 const char *crit_ptr = (do_critical != NULL) ? do_critical : SECURITY_RE;
307 regres = regcomp(&sreg, crit_ptr, REG_EXTENDED); 371 regres = regcomp(&sreg, crit_ptr, REG_EXTENDED);
308 if (regres != 0) { 372 if (regres != 0) {
309 regerror(regres, &ereg, rerrbuf, 64); 373 regerror(regres, &exclude_regex, rerrbuf, 64);
310 die(STATE_UNKNOWN, _("%s: Error compiling regexp: %s"), progname, rerrbuf); 374 die(STATE_UNKNOWN, _("%s: Error compiling regexp: %s"), progname, rerrbuf);
311 } 375 }
312 376
@@ -322,13 +386,12 @@ run_upgrade_result run_upgrade(const upgrade_type upgrade, const char *do_includ
322 result.errorcode = np_runcmd(cmdline, &chld_out, &chld_err, 0); 386 result.errorcode = np_runcmd(cmdline, &chld_out, &chld_err, 0);
323 } 387 }
324 388
325 /* apt-get upgrade only changes exit status if there is an 389 // apt-get upgrade only changes exit status if there is an
326 * internal error when run in dry-run mode. therefore we will 390 // internal error when run in dry-run mode.
327 * treat such an error as UNKNOWN */ 391 if (result.errorcode != 0) {
328 if (result.errorcode != STATE_OK) { 392 result.exec_warning = true;
329 exec_warning = 1; 393 result.errorcode = ERROR;
330 result.errorcode = STATE_UNKNOWN; 394 // fprintf(stderr, _("'%s' exited with non-zero status.\n"), cmdline);
331 fprintf(stderr, _("'%s' exited with non-zero status.\n"), cmdline);
332 } 395 }
333 396
334 char **pkglist = malloc(sizeof(char *) * chld_out.lines); 397 char **pkglist = malloc(sizeof(char *) * chld_out.lines);
@@ -349,27 +412,31 @@ run_upgrade_result run_upgrade(const upgrade_type upgrade, const char *do_includ
349 * we may need to switch to the --print-uris output format, 412 * we may need to switch to the --print-uris output format,
350 * in which case the logic here will slightly change. 413 * in which case the logic here will slightly change.
351 */ 414 */
352 int package_counter = 0; 415 size_t package_counter = 0;
353 int security_package_counter = 0; 416 size_t security_package_counter = 0;
354 for (size_t i = 0; i < chld_out.lines; i++) { 417 for (size_t i = 0; i < chld_out.lines; i++) {
355 if (verbose) { 418 if (verbose) {
356 printf("%s\n", chld_out.line[i]); 419 printf("%s\n", chld_out.line[i]);
357 } 420 }
421
358 /* if it is a package we care about */ 422 /* if it is a package we care about */
359 if (strncmp(PKGINST_PREFIX, chld_out.line[i], strlen(PKGINST_PREFIX)) == 0 && 423 if (strncmp(PKGINST_PREFIX, chld_out.line[i], strlen(PKGINST_PREFIX)) == 0 &&
360 (do_include == NULL || regexec(&ireg, chld_out.line[i], 0, NULL, 0) == 0)) { 424 (do_include == NULL || regexec(&include_regex, chld_out.line[i], 0, NULL, 0) == 0)) {
361 /* if we're not excluding, or it's not in the 425 /* if we're not excluding, or it's not in the
362 * list of stuff to exclude */ 426 * list of stuff to exclude */
363 if (do_exclude == NULL || regexec(&ereg, chld_out.line[i], 0, NULL, 0) != 0) { 427 if (do_exclude == NULL || regexec(&exclude_regex, chld_out.line[i], 0, NULL, 0) != 0) {
364 package_counter++; 428 package_counter++;
365 if (regexec(&sreg, chld_out.line[i], 0, NULL, 0) == 0) { 429 if (regexec(&sreg, chld_out.line[i], 0, NULL, 0) == 0) {
366 security_package_counter++; 430 security_package_counter++;
431
367 if (verbose) { 432 if (verbose) {
368 printf("*"); 433 printf("*");
369 } 434 }
435
370 (secpkglist)[security_package_counter - 1] = pkg_name(chld_out.line[i]); 436 (secpkglist)[security_package_counter - 1] = pkg_name(chld_out.line[i]);
371 } else { 437 } else {
372 (pkglist)[package_counter - security_package_counter - 1] = pkg_name(chld_out.line[i]); 438 (pkglist)[package_counter - security_package_counter - 1] =
439 pkg_name(chld_out.line[i]);
373 } 440 }
374 if (verbose) { 441 if (verbose) {
375 printf("*%s\n", chld_out.line[i]); 442 printf("*%s\n", chld_out.line[i]);
@@ -377,6 +444,7 @@ run_upgrade_result run_upgrade(const upgrade_type upgrade, const char *do_includ
377 } 444 }
378 } 445 }
379 } 446 }
447
380 result.package_count = package_counter; 448 result.package_count = package_counter;
381 result.security_package_count = security_package_counter; 449 result.security_package_count = security_package_counter;
382 result.packages_list = pkglist; 450 result.packages_list = pkglist;
@@ -385,41 +453,55 @@ run_upgrade_result run_upgrade(const upgrade_type upgrade, const char *do_includ
385 /* If we get anything on stderr, at least set warning */ 453 /* If we get anything on stderr, at least set warning */
386 if (input_filename == NULL && chld_err.buflen) { 454 if (input_filename == NULL && chld_err.buflen) {
387 stderr_warning = true; 455 stderr_warning = true;
388 result.errorcode = max_state(result.errorcode, STATE_WARNING); 456 result.errorcode = ERROR;
457
389 if (verbose) { 458 if (verbose) {
390 for (size_t i = 0; i < chld_err.lines; i++) { 459 for (size_t i = 0; i < chld_err.lines; i++) {
391 fprintf(stderr, "%s\n", chld_err.line[i]); 460 fprintf(stderr, "%s\n", chld_err.line[i]);
392 } 461 }
393 } 462 }
394 } 463 }
464
395 if (do_include != NULL) { 465 if (do_include != NULL) {
396 regfree(&ireg); 466 regfree(&include_regex);
397 } 467 }
468
398 regfree(&sreg); 469 regfree(&sreg);
470
399 if (do_exclude != NULL) { 471 if (do_exclude != NULL) {
400 regfree(&ereg); 472 regfree(&exclude_regex);
401 } 473 }
474
402 free(cmdline); 475 free(cmdline);
476
403 return result; 477 return result;
404} 478}
405 479
406/* run an apt-get update (needs root) */ 480/* run an apt-get update (needs root) */
407int run_update(char *update_opts) { 481run_update_result run_update(char *update_opts) {
408 int result = STATE_UNKNOWN;
409 char *cmdline; 482 char *cmdline;
410 /* run the update */ 483 /* run the update */
411 cmdline = construct_cmdline(NO_UPGRADE, update_opts); 484 cmdline = construct_cmdline(NO_UPGRADE, update_opts);
412 485
486 run_update_result result = {
487 .exec_warning = false,
488 .stderr_warning = false,
489 .sc = mp_subcheck_init(),
490 };
491
492 result.sc = mp_set_subcheck_default_state(result.sc, STATE_OK);
493 xasprintf(&result.sc.output, "executing '%s' first", cmdline);
494
413 output chld_out; 495 output chld_out;
414 output chld_err; 496 output chld_err;
415 result = np_runcmd(cmdline, &chld_out, &chld_err, 0); 497 int cmd_error = np_runcmd(cmdline, &chld_out, &chld_err, 0);
416 /* apt-get update changes exit status if it can't fetch packages. 498 /* apt-get update changes exit status if it can't fetch packages.
417 * since we were explicitly asked to do so, this is treated as 499 * since we were explicitly asked to do so, this is treated as
418 * a critical error. */ 500 * a critical error. */
419 if (result != 0) { 501 if (cmd_error != 0) {
420 exec_warning = true; 502 exec_warning = true;
421 result = STATE_CRITICAL; 503 result.sc = mp_set_subcheck_state(result.sc, STATE_CRITICAL);
422 fprintf(stderr, _("'%s' exited with non-zero status.\n"), cmdline); 504 xasprintf(&result.sc.output, _("'%s' exited with non-zero status.\n"), cmdline);
423 } 505 }
424 506
425 if (verbose) { 507 if (verbose) {
@@ -430,15 +512,18 @@ int run_update(char *update_opts) {
430 512
431 /* If we get anything on stderr, at least set warning */ 513 /* If we get anything on stderr, at least set warning */
432 if (chld_err.buflen) { 514 if (chld_err.buflen) {
433 stderr_warning = 1; 515 stderr_warning = true;
434 result = max_state(result, STATE_WARNING); 516 result.sc = mp_set_subcheck_state(
517 result.sc, max_state(mp_compute_subcheck_state(result.sc), STATE_WARNING));
435 if (verbose) { 518 if (verbose) {
436 for (size_t i = 0; i < chld_err.lines; i++) { 519 for (size_t i = 0; i < chld_err.lines; i++) {
437 fprintf(stderr, "%s\n", chld_err.line[i]); 520 fprintf(stderr, "%s\n", chld_err.line[i]);
438 } 521 }
439 } 522 }
440 } 523 }
524
441 free(cmdline); 525 free(cmdline);
526
442 return result; 527 return result;
443} 528}
444 529
@@ -520,7 +605,7 @@ char *construct_cmdline(upgrade_type upgrade, const char *opts) {
520 break; 605 break;
521 } 606 }
522 607
523 int len = 0; 608 size_t len = 0;
524 len += strlen(PATH_TO_APTGET) + 1; /* "/usr/bin/apt-get " */ 609 len += strlen(PATH_TO_APTGET) + 1; /* "/usr/bin/apt-get " */
525 len += strlen(opts_ptr) + 1; /* "opts " */ 610 len += strlen(opts_ptr) + 1; /* "opts " */
526 len += strlen(aptcmd) + 1; /* "upgrade\0" */ 611 len += strlen(aptcmd) + 1; /* "upgrade\0" */
@@ -558,7 +643,8 @@ void print_help(void) {
558 printf(" %s\n", _("List packages available for upgrade. Packages are printed sorted by")); 643 printf(" %s\n", _("List packages available for upgrade. Packages are printed sorted by"));
559 printf(" %s\n", _("name with security packages listed first.")); 644 printf(" %s\n", _("name with security packages listed first."));
560 printf(" %s\n", "-i, --include=REGEXP"); 645 printf(" %s\n", "-i, --include=REGEXP");
561 printf(" %s\n", _("Include only packages matching REGEXP. Can be specified multiple times")); 646 printf(" %s\n",
647 _("Include only packages matching REGEXP. Can be specified multiple times"));
562 printf(" %s\n", _("the values will be combined together. Any packages matching this list")); 648 printf(" %s\n", _("the values will be combined together. Any packages matching this list"));
563 printf(" %s\n", _("cause the plugin to return WARNING status. Others will be ignored.")); 649 printf(" %s\n", _("cause the plugin to return WARNING status. Others will be ignored."));
564 printf(" %s\n", _("Default is to include all packages.")); 650 printf(" %s\n", _("Default is to include all packages."));
@@ -567,7 +653,8 @@ void print_help(void) {
567 printf(" %s\n", _("otherwise be included. Can be specified multiple times; the values")); 653 printf(" %s\n", _("otherwise be included. Can be specified multiple times; the values"));
568 printf(" %s\n", _("will be combined together. Default is to exclude no packages.")); 654 printf(" %s\n", _("will be combined together. Default is to exclude no packages."));
569 printf(" %s\n", "-c, --critical=REGEXP"); 655 printf(" %s\n", "-c, --critical=REGEXP");
570 printf(" %s\n", _("If the full package information of any of the upgradable packages match")); 656 printf(" %s\n",
657 _("If the full package information of any of the upgradable packages match"));
571 printf(" %s\n", _("this REGEXP, the plugin will return CRITICAL status. Can be specified")); 658 printf(" %s\n", _("this REGEXP, the plugin will return CRITICAL status. Can be specified"));
572 printf(" %s\n", _("multiple times like above. Default is a regexp matching security")); 659 printf(" %s\n", _("multiple times like above. Default is a regexp matching security"));
573 printf(" %s\n", _("upgrades for Debian and Ubuntu:")); 660 printf(" %s\n", _("upgrades for Debian and Ubuntu:"));
@@ -576,15 +663,21 @@ void print_help(void) {
576 printf(" %s\n", _("information is compared against the critical list.")); 663 printf(" %s\n", _("information is compared against the critical list."));
577 printf(" %s\n", "-o, --only-critical"); 664 printf(" %s\n", "-o, --only-critical");
578 printf(" %s\n", _("Only warn about upgrades matching the critical list. The total number")); 665 printf(" %s\n", _("Only warn about upgrades matching the critical list. The total number"));
579 printf(" %s\n", _("of upgrades will be printed, but any non-critical upgrades will not cause")); 666 printf(" %s\n",
667 _("of upgrades will be printed, but any non-critical upgrades will not cause"));
580 printf(" %s\n", _("the plugin to return WARNING status.")); 668 printf(" %s\n", _("the plugin to return WARNING status."));
581 printf(" %s\n", "-w, --packages-warning"); 669 printf(" %s\n", "-w, --packages-warning");
582 printf(" %s\n", _("Minimum number of packages available for upgrade to return WARNING status.")); 670 printf(" %s\n",
671 _("Minimum number of packages available for upgrade to return WARNING status."));
583 printf(" %s\n\n", _("Default is 1 package.")); 672 printf(" %s\n\n", _("Default is 1 package."));
584 673
585 printf("%s\n\n", _("The following options require root privileges and should be used with care:")); 674 printf(UT_OUTPUT_FORMAT);
675
676 printf("%s\n\n",
677 _("The following options require root privileges and should be used with care:"));
586 printf(" %s\n", "-u, --update=OPTS"); 678 printf(" %s\n", "-u, --update=OPTS");
587 printf(" %s\n", _("First perform an 'apt-get update'. An optional OPTS parameter overrides")); 679 printf(" %s\n",
680 _("First perform an 'apt-get update'. An optional OPTS parameter overrides"));
588 printf(" %s\n", _("the default options. Note: you may also need to adjust the global")); 681 printf(" %s\n", _("the default options. Note: you may also need to adjust the global"));
589 printf(" %s\n", _("timeout (with -t) to prevent the plugin from timing out if apt-get")); 682 printf(" %s\n", _("timeout (with -t) to prevent the plugin from timing out if apt-get"));
590 printf(" %s\n", _("upgrade is expected to take longer than the default timeout.")); 683 printf(" %s\n", _("upgrade is expected to take longer than the default timeout."));
@@ -593,8 +686,10 @@ void print_help(void) {
593 printf(" %s\n", _("apt-get will be run with these command line options instead of the")); 686 printf(" %s\n", _("apt-get will be run with these command line options instead of the"));
594 printf(" %s", _("default ")); 687 printf(" %s", _("default "));
595 printf("(%s).\n", UPGRADE_DEFAULT_OPTS); 688 printf("(%s).\n", UPGRADE_DEFAULT_OPTS);
596 printf(" %s\n", _("Note that you may be required to have root privileges if you do not use")); 689 printf(" %s\n",
597 printf(" %s\n", _("the default options, which will only run a simulation and NOT perform the upgrade")); 690 _("Note that you may be required to have root privileges if you do not use"));
691 printf(" %s\n",
692 _("the default options, which will only run a simulation and NOT perform the upgrade"));
598 printf(" %s\n", "-d, --dist-upgrade=OPTS"); 693 printf(" %s\n", "-d, --dist-upgrade=OPTS");
599 printf(" %s\n", _("Perform a dist-upgrade instead of normal upgrade. Like with -U OPTS")); 694 printf(" %s\n", _("Perform a dist-upgrade instead of normal upgrade. Like with -U OPTS"));
600 printf(" %s\n", _("can be provided to override the default options.")); 695 printf(" %s\n", _("can be provided to override the default options."));
diff --git a/plugins/check_apt.d/config.h b/plugins/check_apt.d/config.h
index 981f4f42..e4d622f1 100644
--- a/plugins/check_apt.d/config.h
+++ b/plugins/check_apt.d/config.h
@@ -2,6 +2,7 @@
2 2
3#include "../../config.h" 3#include "../../config.h"
4#include <stddef.h> 4#include <stddef.h>
5#include "../lib/output.h"
5 6
6/* some constants */ 7/* some constants */
7typedef enum { 8typedef enum {
@@ -16,7 +17,7 @@ typedef struct {
16 bool only_critical; /* whether to warn about non-critical updates */ 17 bool only_critical; /* whether to warn about non-critical updates */
17 bool list; /* list packages available for upgrade */ 18 bool list; /* list packages available for upgrade */
18 /* number of packages available for upgrade to return WARNING status */ 19 /* number of packages available for upgrade to return WARNING status */
19 int packages_warning; 20 size_t packages_warning;
20 21
21 char *upgrade_opts; /* options to override defaults for upgrade */ 22 char *upgrade_opts; /* options to override defaults for upgrade */
22 char *update_opts; /* options to override defaults for update */ 23 char *update_opts; /* options to override defaults for update */
@@ -24,6 +25,9 @@ typedef struct {
24 char *do_exclude; /* regexp to only exclude certain packages */ 25 char *do_exclude; /* regexp to only exclude certain packages */
25 char *do_critical; /* regexp specifying critical packages */ 26 char *do_critical; /* regexp specifying critical packages */
26 char *input_filename; /* input filename for testing */ 27 char *input_filename; /* input filename for testing */
28
29 bool output_format_is_set;
30 mp_output_format output_format;
27} check_apt_config; 31} check_apt_config;
28 32
29check_apt_config check_apt_config_init() { 33check_apt_config check_apt_config_init() {
@@ -36,6 +40,7 @@ check_apt_config check_apt_config_init() {
36 .do_include = NULL, 40 .do_include = NULL,
37 .do_exclude = NULL, 41 .do_exclude = NULL,
38 .do_critical = NULL, 42 .do_critical = NULL,
39 .input_filename = NULL}; 43 .input_filename = NULL,
44 .output_format_is_set = false};
40 return tmp; 45 return tmp;
41} 46}
diff --git a/plugins/check_by_ssh.c b/plugins/check_by_ssh.c
index 74b7a46f..a43c0d34 100644
--- a/plugins/check_by_ssh.c
+++ b/plugins/check_by_ssh.c
@@ -119,13 +119,14 @@ int main(int argc, char **argv) {
119 skip_stderr = config.skip_stderr; 119 skip_stderr = config.skip_stderr;
120 } 120 }
121 121
122 /* UNKNOWN or worse if (non-skipped) output found on stderr */ 122 /* Allow UNKNOWN or WARNING state for (non-skipped) output found on stderr */
123 if (chld_err.lines > (size_t)skip_stderr) { 123 if (chld_err.lines > (size_t)skip_stderr && (config.unknown_on_stderr || config.warn_on_stderr)) {
124 printf(_("Remote command execution failed: %s\n"), chld_err.line[skip_stderr]); 124 printf(_("Remote command execution failed: %s\n"), chld_err.line[skip_stderr]);
125 if (config.warn_on_stderr) { 125 if (config.unknown_on_stderr) {
126 return max_state_alt(result, STATE_UNKNOWN);
127 } else if (config.warn_on_stderr) {
126 return max_state_alt(result, STATE_WARNING); 128 return max_state_alt(result, STATE_WARNING);
127 } 129 }
128 return max_state_alt(result, STATE_UNKNOWN);
129 } 130 }
130 131
131 /* this is simple if we're not supposed to be passive. 132 /* this is simple if we're not supposed to be passive.
@@ -191,12 +192,13 @@ check_by_ssh_config_wrapper process_arguments(int argc, char **argv) {
191 {"name", required_argument, 0, 'n'}, 192 {"name", required_argument, 0, 'n'},
192 {"services", required_argument, 0, 's'}, 193 {"services", required_argument, 0, 's'},
193 {"identity", required_argument, 0, 'i'}, 194 {"identity", required_argument, 0, 'i'},
194 {"user", required_argument, 0, 'u'}, 195 {"user", required_argument, 0, 'u'}, /* backwards compatibility */
195 {"logname", required_argument, 0, 'l'}, 196 {"logname", required_argument, 0, 'l'},
196 {"command", required_argument, 0, 'C'}, 197 {"command", required_argument, 0, 'C'},
197 {"skip", optional_argument, 0, 'S'}, /* backwards compatibility */ 198 {"skip", optional_argument, 0, 'S'}, /* backwards compatibility */
198 {"skip-stdout", optional_argument, 0, 'S'}, 199 {"skip-stdout", optional_argument, 0, 'S'},
199 {"skip-stderr", optional_argument, 0, 'E'}, 200 {"skip-stderr", optional_argument, 0, 'E'},
201 {"unknown-on-stderr", no_argument, 0, 'e'},
200 {"warn-on-stderr", no_argument, 0, 'W'}, 202 {"warn-on-stderr", no_argument, 0, 'W'},
201 {"proto1", no_argument, 0, '1'}, 203 {"proto1", no_argument, 0, '1'},
202 {"proto2", no_argument, 0, '2'}, 204 {"proto2", no_argument, 0, '2'},
@@ -341,6 +343,9 @@ check_by_ssh_config_wrapper process_arguments(int argc, char **argv) {
341 result.config.skip_stderr = atoi(optarg); 343 result.config.skip_stderr = atoi(optarg);
342 } 344 }
343 break; 345 break;
346 case 'e': /* exit with unknown if there is an output on stderr */
347 result.config.unknown_on_stderr = true;
348 break;
344 case 'W': /* exit with warning if there is an output on stderr */ 349 case 'W': /* exit with warning if there is an output on stderr */
345 result.config.warn_on_stderr = true; 350 result.config.warn_on_stderr = true;
346 break; 351 break;
@@ -468,8 +473,10 @@ void print_help(void) {
468 printf(" %s\n", _("Ignore all or (if specified) first n lines on STDOUT [optional]")); 473 printf(" %s\n", _("Ignore all or (if specified) first n lines on STDOUT [optional]"));
469 printf(" %s\n", "-E, --skip-stderr[=n]"); 474 printf(" %s\n", "-E, --skip-stderr[=n]");
470 printf(" %s\n", _("Ignore all or (if specified) first n lines on STDERR [optional]")); 475 printf(" %s\n", _("Ignore all or (if specified) first n lines on STDERR [optional]"));
471 printf(" %s\n", "-W, --warn-on-stderr]"); 476 printf(" %s\n", "-e, --unknown-on-stderr");
472 printf(" %s\n", _("Exit with an warning, if there is an output on STDERR")); 477 printf(" %s\n", _("Exit with UNKNOWN, if there is output on STDERR"));
478 printf(" %s\n", "-W, --warn-on-stderr");
479 printf(" %s\n", _("Exit with WARNING, if there is output on STDERR"));
473 printf(" %s\n", "-f"); 480 printf(" %s\n", "-f");
474 printf(" %s\n", _("tells ssh to fork rather than create a tty [optional]. This will always " 481 printf(" %s\n", _("tells ssh to fork rather than create a tty [optional]. This will always "
475 "return OK if ssh is executed")); 482 "return OK if ssh is executed"));
@@ -522,7 +529,7 @@ void print_help(void) {
522void print_usage(void) { 529void print_usage(void) {
523 printf("%s\n", _("Usage:")); 530 printf("%s\n", _("Usage:"));
524 printf(" %s -H <host> -C <command> [-fqvU] [-1|-2] [-4|-6]\n" 531 printf(" %s -H <host> -C <command> [-fqvU] [-1|-2] [-4|-6]\n"
525 " [-S [lines]] [-E [lines]] [-W] [-t timeout] [-i identity]\n" 532 " [-S [lines]] [-E [lines]] [-e|-W] [-t timeout] [-i identity]\n"
526 " [-l user] [-n name] [-s servicelist] [-O outputfile]\n" 533 " [-l user] [-n name] [-s servicelist] [-O outputfile]\n"
527 " [-p port] [-o ssh-option] [-F configfile]\n", 534 " [-p port] [-o ssh-option] [-F configfile]\n",
528 progname); 535 progname);
diff --git a/plugins/check_by_ssh.d/config.h b/plugins/check_by_ssh.d/config.h
index 05435def..0e4b56d4 100644
--- a/plugins/check_by_ssh.d/config.h
+++ b/plugins/check_by_ssh.d/config.h
@@ -21,6 +21,7 @@ typedef struct {
21 command_construct cmd; 21 command_construct cmd;
22 22
23 bool unknown_timeout; 23 bool unknown_timeout;
24 bool unknown_on_stderr;
24 bool warn_on_stderr; 25 bool warn_on_stderr;
25 int skip_stdout; 26 int skip_stdout;
26 int skip_stderr; 27 int skip_stderr;
@@ -46,6 +47,7 @@ check_by_ssh_config check_by_ssh_config_init() {
46 }, 47 },
47 48
48 .unknown_timeout = false, 49 .unknown_timeout = false,
50 .unknown_on_stderr = false,
49 .warn_on_stderr = false, 51 .warn_on_stderr = false,
50 .skip_stderr = 0, 52 .skip_stderr = 0,
51 .skip_stdout = 0, 53 .skip_stdout = 0,
diff --git a/plugins/check_cluster.c b/plugins/check_cluster.c
index 373520ee..1cbdcd60 100644
--- a/plugins/check_cluster.c
+++ b/plugins/check_cluster.c
@@ -26,6 +26,8 @@ const char *progname = "check_cluster";
26const char *copyright = "2000-2024"; 26const char *copyright = "2000-2024";
27const char *email = "devel@monitoring-plugins.org"; 27const char *email = "devel@monitoring-plugins.org";
28 28
29#include "output.h"
30#include "states.h"
29#include "common.h" 31#include "common.h"
30#include "utils.h" 32#include "utils.h"
31#include "utils_base.h" 33#include "utils_base.h"
@@ -57,6 +59,10 @@ int main(int argc, char **argv) {
57 59
58 const check_cluster_config config = tmp_config.config; 60 const check_cluster_config config = tmp_config.config;
59 61
62 if (config.output_format_is_set) {
63 mp_set_format(config.output_format);
64 }
65
60 /* Initialize the thresholds */ 66 /* Initialize the thresholds */
61 if (verbose) { 67 if (verbose) {
62 print_thresholds("check_cluster", config.thresholds); 68 print_thresholds("check_cluster", config.thresholds);
@@ -72,7 +78,6 @@ int main(int argc, char **argv) {
72 int total_hosts_unreachable = 0; 78 int total_hosts_unreachable = 0;
73 /* check the data values */ 79 /* check the data values */
74 for (char *ptr = strtok(config.data_vals, ","); ptr != NULL; ptr = strtok(NULL, ",")) { 80 for (char *ptr = strtok(config.data_vals, ","); ptr != NULL; ptr = strtok(NULL, ",")) {
75
76 data_val = atoi(ptr); 81 data_val = atoi(ptr);
77 82
78 if (config.check_type == CHECK_SERVICES) { 83 if (config.check_type == CHECK_SERVICES) {
@@ -109,33 +114,49 @@ int main(int argc, char **argv) {
109 } 114 }
110 } 115 }
111 116
112 int return_code = STATE_OK; 117 mp_check overall = mp_check_init();
118 mp_subcheck sc_real_test = mp_subcheck_init();
119 sc_real_test = mp_set_subcheck_default_state(sc_real_test, STATE_OK);
120
113 /* return the status of the cluster */ 121 /* return the status of the cluster */
114 if (config.check_type == CHECK_SERVICES) { 122 if (config.check_type == CHECK_SERVICES) {
115 return_code = 123 sc_real_test = mp_set_subcheck_state(
124 sc_real_test,
116 get_status(total_services_warning + total_services_unknown + total_services_critical, 125 get_status(total_services_warning + total_services_unknown + total_services_critical,
117 config.thresholds); 126 config.thresholds));
118 printf("CLUSTER %s: %s: %d ok, %d warning, %d unknown, %d critical\n", 127 xasprintf(&sc_real_test.output, "%s: %d ok, %d warning, %d unknown, %d critical",
119 state_text(return_code), (config.label == NULL) ? "Service cluster" : config.label, 128 (config.label == NULL) ? "Service cluster" : config.label, total_services_ok,
120 total_services_ok, total_services_warning, total_services_unknown, 129 total_services_warning, total_services_unknown, total_services_critical);
121 total_services_critical);
122 } else { 130 } else {
123 return_code = get_status(total_hosts_down + total_hosts_unreachable, config.thresholds); 131 sc_real_test = mp_set_subcheck_state(
124 printf("CLUSTER %s: %s: %d up, %d down, %d unreachable\n", state_text(return_code), 132 sc_real_test,
125 (config.label == NULL) ? "Host cluster" : config.label, total_hosts_up, 133 get_status(total_hosts_down + total_hosts_unreachable, config.thresholds));
126 total_hosts_down, total_hosts_unreachable); 134 xasprintf(&sc_real_test.output, "%s: %d up, %d down, %d unreachable\n",
135 (config.label == NULL) ? "Host cluster" : config.label, total_hosts_up,
136 total_hosts_down, total_hosts_unreachable);
127 } 137 }
128 138
129 exit(return_code); 139 mp_add_subcheck_to_check(&overall, sc_real_test);
140
141 mp_exit(overall);
130} 142}
131 143
132check_cluster_config_wrapper process_arguments(int argc, char **argv) { 144check_cluster_config_wrapper process_arguments(int argc, char **argv) {
133 static struct option longopts[] = { 145 enum {
134 {"data", required_argument, 0, 'd'}, {"warning", required_argument, 0, 'w'}, 146 output_format_index = CHAR_MAX + 1,
135 {"critical", required_argument, 0, 'c'}, {"label", required_argument, 0, 'l'}, 147 };
136 {"host", no_argument, 0, 'h'}, {"service", no_argument, 0, 's'}, 148
137 {"verbose", no_argument, 0, 'v'}, {"version", no_argument, 0, 'V'}, 149 static struct option longopts[] = {{"data", required_argument, 0, 'd'},
138 {"help", no_argument, 0, 'H'}, {0, 0, 0, 0}}; 150 {"warning", required_argument, 0, 'w'},
151 {"critical", required_argument, 0, 'c'},
152 {"label", required_argument, 0, 'l'},
153 {"host", no_argument, 0, 'h'},
154 {"service", no_argument, 0, 's'},
155 {"verbose", no_argument, 0, 'v'},
156 {"version", no_argument, 0, 'V'},
157 {"help", no_argument, 0, 'H'},
158 {"output-format", required_argument, 0, output_format_index},
159 {0, 0, 0, 0}};
139 160
140 check_cluster_config_wrapper result = { 161 check_cluster_config_wrapper result = {
141 .errorcode = OK, 162 .errorcode = OK,
@@ -202,6 +223,18 @@ check_cluster_config_wrapper process_arguments(int argc, char **argv) {
202 print_help(); 223 print_help();
203 exit(STATE_UNKNOWN); 224 exit(STATE_UNKNOWN);
204 break; 225 break;
226 case output_format_index: {
227 parsed_output_format parser = mp_parse_output_format(optarg);
228 if (!parser.parsing_success) {
229 // TODO List all available formats here, maybe add anothoer usage function
230 printf("Invalid output format: %s\n", optarg);
231 exit(STATE_UNKNOWN);
232 }
233
234 result.config.output_format_is_set = true;
235 result.config.output_format = parser.output_format;
236 break;
237 }
205 default: 238 default:
206 result.errorcode = ERROR; 239 result.errorcode = ERROR;
207 return result; 240 return result;
@@ -249,6 +282,8 @@ void print_help(void) {
249 282
250 printf(UT_VERBOSE); 283 printf(UT_VERBOSE);
251 284
285 printf(UT_OUTPUT_FORMAT);
286
252 printf("\n"); 287 printf("\n");
253 printf("%s\n", _("Notes:")); 288 printf("%s\n", _("Notes:"));
254 printf(UT_THRESHOLDS_NOTES); 289 printf(UT_THRESHOLDS_NOTES);
diff --git a/plugins/check_cluster.d/config.h b/plugins/check_cluster.d/config.h
index fc386415..054657b0 100644
--- a/plugins/check_cluster.d/config.h
+++ b/plugins/check_cluster.d/config.h
@@ -2,6 +2,7 @@
2 2
3#include "../../config.h" 3#include "../../config.h"
4#include "../../lib/thresholds.h" 4#include "../../lib/thresholds.h"
5#include "output.h"
5#include <stddef.h> 6#include <stddef.h>
6 7
7enum { 8enum {
@@ -14,6 +15,9 @@ typedef struct {
14 thresholds *thresholds; 15 thresholds *thresholds;
15 int check_type; 16 int check_type;
16 char *label; 17 char *label;
18
19 mp_output_format output_format;
20 bool output_format_is_set;
17} check_cluster_config; 21} check_cluster_config;
18 22
19check_cluster_config check_cluster_config_init() { 23check_cluster_config check_cluster_config_init() {
@@ -22,6 +26,8 @@ check_cluster_config check_cluster_config_init() {
22 .thresholds = NULL, 26 .thresholds = NULL,
23 .check_type = CHECK_SERVICES, 27 .check_type = CHECK_SERVICES,
24 .label = NULL, 28 .label = NULL,
29
30 .output_format_is_set = false,
25 }; 31 };
26 return tmp; 32 return tmp;
27} 33}
diff --git a/plugins/check_curl.d/check_curl_helpers.c b/plugins/check_curl.d/check_curl_helpers.c
index c3c2ba55..d49d8f07 100644
--- a/plugins/check_curl.d/check_curl_helpers.c
+++ b/plugins/check_curl.d/check_curl_helpers.c
@@ -4,6 +4,7 @@
4#include <netinet/in.h> 4#include <netinet/in.h>
5#include <netdb.h> 5#include <netdb.h>
6#include <stdlib.h> 6#include <stdlib.h>
7#include <string.h>
7#include "../utils.h" 8#include "../utils.h"
8#include "check_curl.d/config.h" 9#include "check_curl.d/config.h"
9#include "output.h" 10#include "output.h"
@@ -816,7 +817,10 @@ int curlhelp_parse_statusline(const char *buf, curlhelp_statusline *status_line)
816 buf = start; 817 buf = start;
817 } 818 }
818 819
819 char *first_line_end = strstr(buf, "\r\n"); 820 // Accept either LF or CRLF as end of line for the status line
821 // CRLF is the standard (RFC9112), but it is recommended to accept both
822 size_t length_of_first_line = strcspn(buf, "\r\n");
823 const char *first_line_end = &buf[length_of_first_line];
820 if (first_line_end == NULL) { 824 if (first_line_end == NULL) {
821 return -1; 825 return -1;
822 } 826 }
@@ -826,6 +830,7 @@ int curlhelp_parse_statusline(const char *buf, curlhelp_statusline *status_line)
826 if (status_line->first_line == NULL) { 830 if (status_line->first_line == NULL) {
827 return -1; 831 return -1;
828 } 832 }
833
829 memcpy(status_line->first_line, buf, first_line_len); 834 memcpy(status_line->first_line, buf, first_line_len);
830 status_line->first_line[first_line_len] = '\0'; 835 status_line->first_line[first_line_len] = '\0';
831 char *first_line_buf = strdup(status_line->first_line); 836 char *first_line_buf = strdup(status_line->first_line);
@@ -833,23 +838,34 @@ int curlhelp_parse_statusline(const char *buf, curlhelp_statusline *status_line)
833 /* protocol and version: "HTTP/x.x" SP or "HTTP/2" SP */ 838 /* protocol and version: "HTTP/x.x" SP or "HTTP/2" SP */
834 char *temp_string = strtok(first_line_buf, "/"); 839 char *temp_string = strtok(first_line_buf, "/");
835 if (temp_string == NULL) { 840 if (temp_string == NULL) {
841 if (verbose > 1) {
842 printf("%s: no / found\n", __func__);
843 }
836 free(first_line_buf); 844 free(first_line_buf);
837 return -1; 845 return -1;
838 } 846 }
847
839 if (strcmp(temp_string, "HTTP") != 0) { 848 if (strcmp(temp_string, "HTTP") != 0) {
849 if (verbose > 1) {
850 printf("%s: string 'HTTP' not found\n", __func__);
851 }
840 free(first_line_buf); 852 free(first_line_buf);
841 return -1; 853 return -1;
842 } 854 }
843 855
856 // try to find a space in the remaining string?
857 // the space after HTTP/1.1 probably
844 temp_string = strtok(NULL, " "); 858 temp_string = strtok(NULL, " ");
845 if (temp_string == NULL) { 859 if (temp_string == NULL) {
860 if (verbose > 1) {
861 printf("%s: no space after protocol definition\n", __func__);
862 }
846 free(first_line_buf); 863 free(first_line_buf);
847 return -1; 864 return -1;
848 } 865 }
849 866
850 char *temp_string_2; 867 char *temp_string_2;
851 if (strchr(temp_string, '.') != NULL) { 868 if (strchr(temp_string, '.') != NULL) {
852
853 /* HTTP 1.x case */ 869 /* HTTP 1.x case */
854 strtok(temp_string, "."); 870 strtok(temp_string, ".");
855 status_line->http_major = (int)strtol(temp_string, &temp_string_2, 10); 871 status_line->http_major = (int)strtol(temp_string, &temp_string_2, 10);
diff --git a/plugins/check_mysql.c b/plugins/check_mysql.c
index 3d7ec4cd..6134d6c6 100644
--- a/plugins/check_mysql.c
+++ b/plugins/check_mysql.c
@@ -219,16 +219,13 @@ int main(int argc, char **argv) {
219 use_deprecated_slave_status = true; 219 use_deprecated_slave_status = true;
220 } 220 }
221 } 221 }
222 } else if (strstr(server_version, "MySQL") != NULL) { 222 } else {
223 // Looks like MySQL 223 // Looks like MySQL or at least not like MariaDB
224 if (major_version < 8) { 224 if (major_version < 8) {
225 use_deprecated_slave_status = true; 225 use_deprecated_slave_status = true;
226 } else if (major_version == 10 && minor_version < 4) { 226 } else if (major_version == 10 && minor_version < 4) {
227 use_deprecated_slave_status = true; 227 use_deprecated_slave_status = true;
228 } 228 }
229 } else {
230 printf("Not a known sever implementation: %s\n", server_version);
231 exit(STATE_UNKNOWN);
232 } 229 }
233 230
234 char *replica_query = NULL; 231 char *replica_query = NULL;
@@ -285,17 +282,32 @@ int main(int argc, char **argv) {
285 num_fields = mysql_num_fields(res); 282 num_fields = mysql_num_fields(res);
286 fields = mysql_fetch_fields(res); 283 fields = mysql_fetch_fields(res);
287 for (int i = 0; i < num_fields; i++) { 284 for (int i = 0; i < num_fields; i++) {
288 if (strcmp(fields[i].name, "Slave_IO_Running") == 0) { 285 if (use_deprecated_slave_status) {
289 replica_io_field = i; 286 if (strcmp(fields[i].name, "Slave_IO_Running") == 0) {
290 continue; 287 replica_io_field = i;
291 } 288 continue;
292 if (strcmp(fields[i].name, "Slave_SQL_Running") == 0) { 289 }
293 replica_sql_field = i; 290 if (strcmp(fields[i].name, "Slave_SQL_Running") == 0) {
294 continue; 291 replica_sql_field = i;
295 } 292 continue;
296 if (strcmp(fields[i].name, "Seconds_Behind_Master") == 0) { 293 }
297 seconds_behind_field = i; 294 if (strcmp(fields[i].name, "Seconds_Behind_Master") == 0) {
298 continue; 295 seconds_behind_field = i;
296 continue;
297 }
298 } else {
299 if (strcmp(fields[i].name, "Replica_IO_Running") == 0) {
300 replica_io_field = i;
301 continue;
302 }
303 if (strcmp(fields[i].name, "Replica_SQL_Running") == 0) {
304 replica_sql_field = i;
305 continue;
306 }
307 if (strcmp(fields[i].name, "Seconds_Behind_Source") == 0) {
308 seconds_behind_field = i;
309 continue;
310 }
299 } 311 }
300 } 312 }
301 313
diff --git a/plugins/check_ntp_peer.c b/plugins/check_ntp_peer.c
index 24d1c9b5..f7cad630 100644
--- a/plugins/check_ntp_peer.c
+++ b/plugins/check_ntp_peer.c
@@ -35,11 +35,14 @@
35 * 35 *
36 *****************************************************************************/ 36 *****************************************************************************/
37 37
38#include "thresholds.h"
39const char *progname = "check_ntp_peer"; 38const char *progname = "check_ntp_peer";
40const char *copyright = "2006-2024"; 39const char *copyright = "2006-2024";
41const char *email = "devel@monitoring-plugins.org"; 40const char *email = "devel@monitoring-plugins.org";
42 41
42#include "output.h"
43#include "perfdata.h"
44#include <openssl/x509.h>
45#include "thresholds.h"
43#include "common.h" 46#include "common.h"
44#include "netutils.h" 47#include "netutils.h"
45#include "utils.h" 48#include "utils.h"
@@ -47,8 +50,6 @@ const char *email = "devel@monitoring-plugins.org";
47#include "check_ntp_peer.d/config.h" 50#include "check_ntp_peer.d/config.h"
48 51
49static int verbose = 0; 52static int verbose = 0;
50static bool syncsource_found = false;
51static bool li_alarm = false;
52 53
53typedef struct { 54typedef struct {
54 int errorcode; 55 int errorcode;
@@ -198,9 +199,7 @@ void setup_control_request(ntp_control_message *message, uint8_t opcode, uint16_
198 * positive value means a success retrieving the value. 199 * positive value means a success retrieving the value.
199 * - status is set to WARNING if there's no sync.peer (otherwise OK) and is 200 * - status is set to WARNING if there's no sync.peer (otherwise OK) and is
200 * the return value of the function. 201 * the return value of the function.
201 * status is pretty much useless as syncsource_found is a global variable 202 */
202 * used later in main to check is the server was synchronized. It works
203 * so I left it alone */
204typedef struct { 203typedef struct {
205 mp_state_enum state; 204 mp_state_enum state;
206 mp_state_enum offset_result; 205 mp_state_enum offset_result;
@@ -208,6 +207,8 @@ typedef struct {
208 double jitter; 207 double jitter;
209 long stratum; 208 long stratum;
210 int num_truechimers; 209 int num_truechimers;
210 bool syncsource_found;
211 bool li_alarm;
211} ntp_request_result; 212} ntp_request_result;
212ntp_request_result ntp_request(const check_ntp_peer_config config) { 213ntp_request_result ntp_request(const check_ntp_peer_config config) {
213 214
@@ -217,6 +218,8 @@ ntp_request_result ntp_request(const check_ntp_peer_config config) {
217 .jitter = -1, 218 .jitter = -1,
218 .stratum = -1, 219 .stratum = -1,
219 .num_truechimers = 0, 220 .num_truechimers = 0,
221 .syncsource_found = false,
222 .li_alarm = false,
220 }; 223 };
221 224
222 /* Long-winded explanation: 225 /* Long-winded explanation:
@@ -235,19 +238,16 @@ ntp_request_result ntp_request(const check_ntp_peer_config config) {
235 * 4) Extract the offset, jitter and stratum value from the data[] 238 * 4) Extract the offset, jitter and stratum value from the data[]
236 * (it's ASCII) 239 * (it's ASCII)
237 */ 240 */
238 int min_peer_sel = PEER_INCLUDED;
239 int num_candidates = 0;
240 void *tmp;
241 ntp_assoc_status_pair *peers = NULL;
242 int peer_offset = 0;
243 size_t peers_size = 0;
244 size_t npeers = 0;
245 int conn = -1; 241 int conn = -1;
246 my_udp_connect(config.server_address, config.port, &conn); 242 my_udp_connect(config.server_address, config.port, &conn);
247 243
248 /* keep sending requests until the server stops setting the 244 /* keep sending requests until the server stops setting the
249 * REM_MORE bit, though usually this is only 1 packet. */ 245 * REM_MORE bit, though usually this is only 1 packet. */
250 ntp_control_message req; 246 ntp_control_message req;
247 ntp_assoc_status_pair *peers = NULL;
248 int peer_offset = 0;
249 size_t peers_size = 0;
250 size_t npeers = 0;
251 do { 251 do {
252 setup_control_request(&req, OP_READSTAT, 1); 252 setup_control_request(&req, OP_READSTAT, 1);
253 DBG(printf("sending READSTAT request")); 253 DBG(printf("sending READSTAT request"));
@@ -269,12 +269,13 @@ ntp_request_result ntp_request(const check_ntp_peer_config config) {
269 } while (!(req.op & OP_READSTAT && ntohs(req.seq) == 1)); 269 } while (!(req.op & OP_READSTAT && ntohs(req.seq) == 1));
270 270
271 if (LI(req.flags) == LI_ALARM) { 271 if (LI(req.flags) == LI_ALARM) {
272 li_alarm = true; 272 result.li_alarm = true;
273 } 273 }
274 /* Each peer identifier is 4 bytes in the data section, which 274 /* Each peer identifier is 4 bytes in the data section, which
275 * we represent as a ntp_assoc_status_pair datatype. 275 * we represent as a ntp_assoc_status_pair datatype.
276 */ 276 */
277 peers_size += ntohs(req.count); 277 peers_size += ntohs(req.count);
278 void *tmp;
278 if ((tmp = realloc(peers, peers_size)) == NULL) { 279 if ((tmp = realloc(peers, peers_size)) == NULL) {
279 free(peers), die(STATE_UNKNOWN, "can not (re)allocate 'peers' buffer\n"); 280 free(peers), die(STATE_UNKNOWN, "can not (re)allocate 'peers' buffer\n");
280 } 281 }
@@ -287,13 +288,15 @@ ntp_request_result ntp_request(const check_ntp_peer_config config) {
287 /* first, let's find out if we have a sync source, or if there are 288 /* first, let's find out if we have a sync source, or if there are
288 * at least some candidates. In the latter case we'll issue 289 * at least some candidates. In the latter case we'll issue
289 * a warning but go ahead with the check on them. */ 290 * a warning but go ahead with the check on them. */
291 int min_peer_sel = PEER_INCLUDED;
292 int num_candidates = 0;
290 for (size_t i = 0; i < npeers; i++) { 293 for (size_t i = 0; i < npeers; i++) {
291 if (PEER_SEL(peers[i].status) >= PEER_TRUECHIMER) { 294 if (PEER_SEL(peers[i].status) >= PEER_TRUECHIMER) {
292 result.num_truechimers++; 295 result.num_truechimers++;
293 if (PEER_SEL(peers[i].status) >= PEER_INCLUDED) { 296 if (PEER_SEL(peers[i].status) >= PEER_INCLUDED) {
294 num_candidates++; 297 num_candidates++;
295 if (PEER_SEL(peers[i].status) >= PEER_SYNCSOURCE) { 298 if (PEER_SEL(peers[i].status) >= PEER_SYNCSOURCE) {
296 syncsource_found = true; 299 result.syncsource_found = true;
297 min_peer_sel = PEER_SYNCSOURCE; 300 min_peer_sel = PEER_SYNCSOURCE;
298 } 301 }
299 } 302 }
@@ -302,18 +305,18 @@ ntp_request_result ntp_request(const check_ntp_peer_config config) {
302 305
303 if (verbose) { 306 if (verbose) {
304 printf("%d candidate peers available\n", num_candidates); 307 printf("%d candidate peers available\n", num_candidates);
305 } 308 if (result.syncsource_found) {
306 if (verbose && syncsource_found) { 309 printf("synchronization source found\n");
307 printf("synchronization source found\n"); 310 }
308 } 311 }
309 312
310 if (!syncsource_found) { 313 if (!result.syncsource_found) {
311 result.state = STATE_WARNING; 314 result.state = STATE_WARNING;
312 if (verbose) { 315 if (verbose) {
313 printf("warning: no synchronization source found\n"); 316 printf("warning: no synchronization source found\n");
314 } 317 }
315 } 318 }
316 if (li_alarm) { 319 if (result.li_alarm) {
317 result.state = STATE_WARNING; 320 result.state = STATE_WARNING;
318 if (verbose) { 321 if (verbose) {
319 printf("warning: LI_ALARM bit is set\n"); 322 printf("warning: LI_ALARM bit is set\n");
@@ -329,7 +332,7 @@ ntp_request_result ntp_request(const check_ntp_peer_config config) {
329 if (verbose) { 332 if (verbose) {
330 printf("Getting offset, jitter and stratum for peer %.2x\n", ntohs(peers[i].assoc)); 333 printf("Getting offset, jitter and stratum for peer %.2x\n", ntohs(peers[i].assoc));
331 } 334 }
332 xasprintf(&data, ""); 335 data = strdup("");
333 do { 336 do {
334 setup_control_request(&req, OP_READVAR, 2); 337 setup_control_request(&req, OP_READVAR, 2);
335 req.assoc = peers[i].assoc; 338 req.assoc = peers[i].assoc;
@@ -475,16 +478,30 @@ ntp_request_result ntp_request(const check_ntp_peer_config config) {
475} 478}
476 479
477check_ntp_peer_config_wrapper process_arguments(int argc, char **argv) { 480check_ntp_peer_config_wrapper process_arguments(int argc, char **argv) {
478 static struct option longopts[] = { 481
479 {"version", no_argument, 0, 'V'}, {"help", no_argument, 0, 'h'}, 482 enum {
480 {"verbose", no_argument, 0, 'v'}, {"use-ipv4", no_argument, 0, '4'}, 483 output_format_index = CHAR_MAX + 1,
481 {"use-ipv6", no_argument, 0, '6'}, {"quiet", no_argument, 0, 'q'}, 484 };
482 {"warning", required_argument, 0, 'w'}, {"critical", required_argument, 0, 'c'}, 485
483 {"swarn", required_argument, 0, 'W'}, {"scrit", required_argument, 0, 'C'}, 486 static struct option longopts[] = {{"version", no_argument, 0, 'V'},
484 {"jwarn", required_argument, 0, 'j'}, {"jcrit", required_argument, 0, 'k'}, 487 {"help", no_argument, 0, 'h'},
485 {"twarn", required_argument, 0, 'm'}, {"tcrit", required_argument, 0, 'n'}, 488 {"verbose", no_argument, 0, 'v'},
486 {"timeout", required_argument, 0, 't'}, {"hostname", required_argument, 0, 'H'}, 489 {"use-ipv4", no_argument, 0, '4'},
487 {"port", required_argument, 0, 'p'}, {0, 0, 0, 0}}; 490 {"use-ipv6", no_argument, 0, '6'},
491 {"quiet", no_argument, 0, 'q'},
492 {"warning", required_argument, 0, 'w'},
493 {"critical", required_argument, 0, 'c'},
494 {"swarn", required_argument, 0, 'W'},
495 {"scrit", required_argument, 0, 'C'},
496 {"jwarn", required_argument, 0, 'j'},
497 {"jcrit", required_argument, 0, 'k'},
498 {"twarn", required_argument, 0, 'm'},
499 {"tcrit", required_argument, 0, 'n'},
500 {"timeout", required_argument, 0, 't'},
501 {"hostname", required_argument, 0, 'H'},
502 {"port", required_argument, 0, 'p'},
503 {"output-format", required_argument, 0, output_format_index},
504 {0, 0, 0, 0}};
488 505
489 if (argc < 2) { 506 if (argc < 2) {
490 usage("\n"); 507 usage("\n");
@@ -504,6 +521,17 @@ check_ntp_peer_config_wrapper process_arguments(int argc, char **argv) {
504 } 521 }
505 522
506 switch (option_char) { 523 switch (option_char) {
524 case output_format_index: {
525 parsed_output_format parser = mp_parse_output_format(optarg);
526 if (!parser.parsing_success) {
527 printf("Invalid output format: %s\n", optarg);
528 exit(STATE_UNKNOWN);
529 }
530
531 result.config.output_format_is_set = true;
532 result.config.output_format = parser.output_format;
533 break;
534 }
507 case 'h': 535 case 'h':
508 print_help(); 536 print_help();
509 exit(STATE_UNKNOWN); 537 exit(STATE_UNKNOWN);
@@ -518,36 +546,84 @@ check_ntp_peer_config_wrapper process_arguments(int argc, char **argv) {
518 case 'q': 546 case 'q':
519 result.config.quiet = true; 547 result.config.quiet = true;
520 break; 548 break;
521 case 'w': 549 case 'w': {
522 result.config.owarn = optarg; 550 mp_range_parsed tmp = mp_parse_range_string(optarg);
523 break; 551 if (tmp.error != MP_PARSING_SUCCES) {
524 case 'c': 552 die(STATE_UNKNOWN, "failed to parse warning offset threshold");
525 result.config.ocrit = optarg; 553 }
526 break; 554
527 case 'W': 555 result.config.offset_thresholds =
556 mp_thresholds_set_warn(result.config.offset_thresholds, tmp.range);
557 } break;
558 case 'c': {
559 mp_range_parsed tmp = mp_parse_range_string(optarg);
560 if (tmp.error != MP_PARSING_SUCCES) {
561 die(STATE_UNKNOWN, "failed to parse critical offset threshold");
562 }
563
564 result.config.offset_thresholds =
565 mp_thresholds_set_crit(result.config.offset_thresholds, tmp.range);
566 } break;
567 case 'W': {
528 result.config.do_stratum = true; 568 result.config.do_stratum = true;
529 result.config.swarn = optarg; 569 mp_range_parsed tmp = mp_parse_range_string(optarg);
530 break; 570 if (tmp.error != MP_PARSING_SUCCES) {
531 case 'C': 571 die(STATE_UNKNOWN, "failed to parse warning stratum threshold");
572 }
573
574 result.config.stratum_thresholds =
575 mp_thresholds_set_warn(result.config.stratum_thresholds, tmp.range);
576 } break;
577 case 'C': {
532 result.config.do_stratum = true; 578 result.config.do_stratum = true;
533 result.config.scrit = optarg; 579 mp_range_parsed tmp = mp_parse_range_string(optarg);
534 break; 580 if (tmp.error != MP_PARSING_SUCCES) {
535 case 'j': 581 die(STATE_UNKNOWN, "failed to parse critical stratum threshold");
582 }
583
584 result.config.stratum_thresholds =
585 mp_thresholds_set_crit(result.config.stratum_thresholds, tmp.range);
586 } break;
587 case 'j': {
536 result.config.do_jitter = true; 588 result.config.do_jitter = true;
537 result.config.jwarn = optarg; 589 mp_range_parsed tmp = mp_parse_range_string(optarg);
538 break; 590 if (tmp.error != MP_PARSING_SUCCES) {
539 case 'k': 591 die(STATE_UNKNOWN, "failed to parse warning jitter threshold");
592 }
593
594 result.config.jitter_thresholds =
595 mp_thresholds_set_warn(result.config.jitter_thresholds, tmp.range);
596 } break;
597 case 'k': {
540 result.config.do_jitter = true; 598 result.config.do_jitter = true;
541 result.config.jcrit = optarg; 599 mp_range_parsed tmp = mp_parse_range_string(optarg);
542 break; 600 if (tmp.error != MP_PARSING_SUCCES) {
543 case 'm': 601 die(STATE_UNKNOWN, "failed to parse critical jitter threshold");
602 }
603
604 result.config.jitter_thresholds =
605 mp_thresholds_set_crit(result.config.jitter_thresholds, tmp.range);
606 } break;
607 case 'm': {
544 result.config.do_truechimers = true; 608 result.config.do_truechimers = true;
545 result.config.twarn = optarg; 609 mp_range_parsed tmp = mp_parse_range_string(optarg);
546 break; 610 if (tmp.error != MP_PARSING_SUCCES) {
547 case 'n': 611 die(STATE_UNKNOWN, "failed to parse warning truechimer threshold");
612 }
613
614 result.config.truechimer_thresholds =
615 mp_thresholds_set_warn(result.config.truechimer_thresholds, tmp.range);
616 } break;
617 case 'n': {
548 result.config.do_truechimers = true; 618 result.config.do_truechimers = true;
549 result.config.tcrit = optarg; 619 mp_range_parsed tmp = mp_parse_range_string(optarg);
550 break; 620 if (tmp.error != MP_PARSING_SUCCES) {
621 die(STATE_UNKNOWN, "failed to parse critical truechimer threshold");
622 }
623
624 result.config.truechimer_thresholds =
625 mp_thresholds_set_crit(result.config.truechimer_thresholds, tmp.range);
626 } break;
551 case 'H': 627 case 'H':
552 if (!is_host(optarg)) { 628 if (!is_host(optarg)) {
553 usage2(_("Invalid hostname/address"), optarg); 629 usage2(_("Invalid hostname/address"), optarg);
@@ -581,11 +657,6 @@ check_ntp_peer_config_wrapper process_arguments(int argc, char **argv) {
581 usage4(_("Hostname was not supplied")); 657 usage4(_("Hostname was not supplied"));
582 } 658 }
583 659
584 set_thresholds(&result.config.offset_thresholds, result.config.owarn, result.config.ocrit);
585 set_thresholds(&result.config.jitter_thresholds, result.config.jwarn, result.config.jcrit);
586 set_thresholds(&result.config.stratum_thresholds, result.config.swarn, result.config.scrit);
587 set_thresholds(&result.config.truechimer_thresholds, result.config.twarn, result.config.tcrit);
588
589 return result; 660 return result;
590} 661}
591 662
@@ -627,6 +698,10 @@ int main(int argc, char *argv[]) {
627 698
628 const check_ntp_peer_config config = tmp_config.config; 699 const check_ntp_peer_config config = tmp_config.config;
629 700
701 if (config.output_format_is_set) {
702 mp_set_format(config.output_format);
703 }
704
630 /* initialize alarm signal handling */ 705 /* initialize alarm signal handling */
631 signal(SIGALRM, socket_timeout_alarm_handler); 706 signal(SIGALRM, socket_timeout_alarm_handler);
632 707
@@ -634,125 +709,113 @@ int main(int argc, char *argv[]) {
634 alarm(socket_timeout); 709 alarm(socket_timeout);
635 710
636 /* This returns either OK or WARNING (See comment preceding ntp_request) */ 711 /* This returns either OK or WARNING (See comment preceding ntp_request) */
637 ntp_request_result ntp_res = ntp_request(config); 712 const ntp_request_result ntp_res = ntp_request(config);
638 mp_state_enum result = STATE_UNKNOWN; 713 mp_check overall = mp_check_init();
639 714
715 mp_subcheck sc_offset = mp_subcheck_init();
716 xasprintf(&sc_offset.output, "offset");
640 if (ntp_res.offset_result == STATE_UNKNOWN) { 717 if (ntp_res.offset_result == STATE_UNKNOWN) {
641 /* if there's no sync peer (this overrides ntp_request output): */ 718 /* if there's no sync peer (this overrides ntp_request output): */
642 result = (config.quiet ? STATE_UNKNOWN : STATE_CRITICAL); 719 sc_offset =
720 mp_set_subcheck_state(sc_offset, (config.quiet ? STATE_UNKNOWN : STATE_CRITICAL));
721 xasprintf(&sc_offset.output, "%s unknown", sc_offset.output);
643 } else { 722 } else {
644 /* Be quiet if there's no candidates either */ 723 /* Be quiet if there's no candidates either */
645 if (config.quiet && result == STATE_WARNING) { 724 mp_state_enum tmp = STATE_OK;
646 result = STATE_UNKNOWN; 725 if (config.quiet && ntp_res.state == STATE_WARNING) {
726 tmp = STATE_UNKNOWN;
647 } 727 }
648 result = max_state_alt(result, get_status(fabs(ntp_res.offset), config.offset_thresholds)); 728
729 xasprintf(&sc_offset.output, "%s: %.6fs", sc_offset.output, ntp_res.offset);
730
731 mp_perfdata pd_offset = perfdata_init();
732 pd_offset.value = mp_create_pd_value(fabs(ntp_res.offset));
733 pd_offset = mp_pd_set_thresholds(pd_offset, config.offset_thresholds);
734 pd_offset.label = "offset";
735 pd_offset.uom = "s";
736 mp_add_perfdata_to_subcheck(&sc_offset, pd_offset);
737
738 tmp = max_state_alt(tmp, mp_get_pd_status(pd_offset));
739 sc_offset = mp_set_subcheck_state(sc_offset, tmp);
649 } 740 }
650 741
651 mp_state_enum oresult = result; 742 mp_add_subcheck_to_check(&overall, sc_offset);
652 mp_state_enum tresult = STATE_UNKNOWN;
653 743
744 // truechimers
654 if (config.do_truechimers) { 745 if (config.do_truechimers) {
655 tresult = get_status(ntp_res.num_truechimers, config.truechimer_thresholds); 746 mp_subcheck sc_truechimers = mp_subcheck_init();
656 result = max_state_alt(result, tresult); 747 xasprintf(&sc_truechimers.output, "truechimers: %i", ntp_res.num_truechimers);
657 }
658 748
659 mp_state_enum sresult = STATE_UNKNOWN; 749 mp_perfdata pd_truechimers = perfdata_init();
750 pd_truechimers.value = mp_create_pd_value(ntp_res.num_truechimers);
751 pd_truechimers.label = "truechimers";
752 pd_truechimers = mp_pd_set_thresholds(pd_truechimers, config.truechimer_thresholds);
660 753
661 if (config.do_stratum) { 754 mp_add_perfdata_to_subcheck(&sc_truechimers, pd_truechimers);
662 sresult = get_status((double)ntp_res.stratum, config.stratum_thresholds);
663 result = max_state_alt(result, sresult);
664 }
665 755
666 mp_state_enum jresult = STATE_UNKNOWN; 756 sc_truechimers = mp_set_subcheck_state(sc_truechimers, mp_get_pd_status(pd_truechimers));
667 757
668 if (config.do_jitter) { 758 mp_add_subcheck_to_check(&overall, sc_truechimers);
669 jresult = get_status(ntp_res.jitter, config.jitter_thresholds);
670 result = max_state_alt(result, jresult);
671 } 759 }
672 760
673 char *result_line; 761 if (config.do_stratum) {
674 switch (result) { 762 mp_subcheck sc_stratum = mp_subcheck_init();
675 case STATE_CRITICAL: 763 xasprintf(&sc_stratum.output, "stratum: %li", ntp_res.stratum);
676 xasprintf(&result_line, _("NTP CRITICAL:"));
677 break;
678 case STATE_WARNING:
679 xasprintf(&result_line, _("NTP WARNING:"));
680 break;
681 case STATE_OK:
682 xasprintf(&result_line, _("NTP OK:"));
683 break;
684 default:
685 xasprintf(&result_line, _("NTP UNKNOWN:"));
686 break;
687 }
688 764
689 if (!syncsource_found) { 765 mp_perfdata pd_stratum = perfdata_init();
690 xasprintf(&result_line, "%s %s,", result_line, _("Server not synchronized")); 766 pd_stratum.value = mp_create_pd_value(ntp_res.stratum);
691 } else if (li_alarm) { 767 pd_stratum = mp_pd_set_thresholds(pd_stratum, config.stratum_thresholds);
692 xasprintf(&result_line, "%s %s,", result_line, _("Server has the LI_ALARM bit set")); 768 pd_stratum.label = "stratum";
693 }
694 769
695 char *perfdata_line; 770 mp_add_perfdata_to_subcheck(&sc_stratum, pd_stratum);
696 if (ntp_res.offset_result == STATE_UNKNOWN) { 771
697 xasprintf(&result_line, "%s %s", result_line, _("Offset unknown")); 772 sc_stratum = mp_set_subcheck_state(sc_stratum, mp_get_pd_status(pd_stratum));
698 xasprintf(&perfdata_line, ""); 773
699 } else if (oresult == STATE_WARNING) { 774 mp_add_subcheck_to_check(&overall, sc_stratum);
700 xasprintf(&result_line, "%s %s %.10g secs (WARNING)", result_line, _("Offset"),
701 ntp_res.offset);
702 } else if (oresult == STATE_CRITICAL) {
703 xasprintf(&result_line, "%s %s %.10g secs (CRITICAL)", result_line, _("Offset"),
704 ntp_res.offset);
705 } else {
706 xasprintf(&result_line, "%s %s %.10g secs", result_line, _("Offset"), ntp_res.offset);
707 } 775 }
708 xasprintf(&perfdata_line, "%s", perfd_offset(ntp_res.offset, config.offset_thresholds));
709 776
710 if (config.do_jitter) { 777 if (config.do_jitter) {
711 if (jresult == STATE_WARNING) { 778 mp_subcheck sc_jitter = mp_subcheck_init();
712 xasprintf(&result_line, "%s, jitter=%f (WARNING)", result_line, ntp_res.jitter); 779 xasprintf(&sc_jitter.output, "jitter: %f", ntp_res.jitter);
713 } else if (jresult == STATE_CRITICAL) {
714 xasprintf(&result_line, "%s, jitter=%f (CRITICAL)", result_line, ntp_res.jitter);
715 } else {
716 xasprintf(&result_line, "%s, jitter=%f", result_line, ntp_res.jitter);
717 }
718 xasprintf(&perfdata_line, "%s %s", perfdata_line,
719 perfd_jitter(ntp_res.jitter, config.do_jitter, config.jitter_thresholds));
720 }
721 780
722 if (config.do_stratum) { 781 mp_perfdata pd_jitter = perfdata_init();
723 if (sresult == STATE_WARNING) { 782 pd_jitter.value = mp_create_pd_value(ntp_res.jitter);
724 xasprintf(&result_line, "%s, stratum=%li (WARNING)", result_line, ntp_res.stratum); 783 pd_jitter = mp_pd_set_thresholds(pd_jitter, config.jitter_thresholds);
725 } else if (sresult == STATE_CRITICAL) { 784 pd_jitter.label = "jitter";
726 xasprintf(&result_line, "%s, stratum=%li (CRITICAL)", result_line, ntp_res.stratum); 785
727 } else { 786 mp_add_perfdata_to_subcheck(&sc_jitter, pd_jitter);
728 xasprintf(&result_line, "%s, stratum=%li", result_line, ntp_res.stratum); 787
729 } 788 sc_jitter = mp_set_subcheck_state(sc_jitter, mp_get_pd_status(pd_jitter));
730 xasprintf(&perfdata_line, "%s %s", perfdata_line, 789 mp_add_subcheck_to_check(&overall, sc_jitter);
731 perfd_stratum(ntp_res.stratum, config.do_stratum, config.stratum_thresholds));
732 } 790 }
733 791
734 if (config.do_truechimers) { 792 mp_subcheck sc_other_info = mp_subcheck_init();
735 if (tresult == STATE_WARNING) { 793 sc_other_info = mp_set_subcheck_default_state(sc_other_info, STATE_OK);
736 xasprintf(&result_line, "%s, truechimers=%i (WARNING)", result_line, 794 if (!ntp_res.syncsource_found) {
737 ntp_res.num_truechimers); 795 xasprintf(&sc_other_info.output, "%s", _("Server not synchronized"));
738 } else if (tresult == STATE_CRITICAL) { 796 mp_add_subcheck_to_check(&overall, sc_other_info);
739 xasprintf(&result_line, "%s, truechimers=%i (CRITICAL)", result_line, 797 } else if (ntp_res.li_alarm) {
740 ntp_res.num_truechimers); 798 xasprintf(&sc_other_info.output, "%s", _("Server has the LI_ALARM bit set"));
741 } else { 799 mp_add_subcheck_to_check(&overall, sc_other_info);
742 xasprintf(&result_line, "%s, truechimers=%i", result_line, ntp_res.num_truechimers);
743 }
744 xasprintf(&perfdata_line, "%s %s", perfdata_line,
745 perfd_truechimers(ntp_res.num_truechimers, config.do_truechimers,
746 config.truechimer_thresholds));
747 } 800 }
748 801
749 printf("%s|%s\n", result_line, perfdata_line); 802 {
803 mp_subcheck sc_offset = mp_subcheck_init();
804 sc_offset = mp_set_subcheck_default_state(sc_offset, STATE_OK);
805 xasprintf(&sc_offset.output, "offset: %.10gs", ntp_res.offset);
806
807 mp_perfdata pd_offset = perfdata_init();
808 pd_offset.value = mp_create_pd_value(ntp_res.offset);
809 pd_offset = mp_pd_set_thresholds(pd_offset, config.offset_thresholds);
810
811 sc_offset = mp_set_subcheck_state(sc_offset, ntp_res.offset_result);
812 }
750 813
751 if (config.server_address != NULL) { 814 if (config.server_address != NULL) {
752 free(config.server_address); 815 free(config.server_address);
753 } 816 }
754 817
755 exit(result); 818 mp_exit(overall);
756} 819}
757 820
758void print_help(void) { 821void print_help(void) {
@@ -791,6 +854,7 @@ void print_help(void) {
791 printf(" %s\n", _("Critical threshold for number of usable time sources (\"truechimers\")")); 854 printf(" %s\n", _("Critical threshold for number of usable time sources (\"truechimers\")"));
792 printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); 855 printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
793 printf(UT_VERBOSE); 856 printf(UT_VERBOSE);
857 printf(UT_OUTPUT_FORMAT);
794 858
795 printf("\n"); 859 printf("\n");
796 printf("%s\n", _("This plugin checks an NTP server independent of any commandline")); 860 printf("%s\n", _("This plugin checks an NTP server independent of any commandline"));
diff --git a/plugins/check_ntp_peer.d/config.h b/plugins/check_ntp_peer.d/config.h
index 00e6b05d..488d936c 100644
--- a/plugins/check_ntp_peer.d/config.h
+++ b/plugins/check_ntp_peer.d/config.h
@@ -1,6 +1,8 @@
1#pragma once 1#pragma once
2 2
3#include "../../config.h" 3#include "../../config.h"
4#include "output.h"
5#include "perfdata.h"
4#include "thresholds.h" 6#include "thresholds.h"
5#include <stddef.h> 7#include <stddef.h>
6 8
@@ -16,26 +18,21 @@ typedef struct {
16 18
17 // truechimer stuff 19 // truechimer stuff
18 bool do_truechimers; 20 bool do_truechimers;
19 char *twarn; 21 mp_thresholds truechimer_thresholds;
20 char *tcrit;
21 thresholds *truechimer_thresholds;
22 22
23 char *owarn; 23 // offset thresholds
24 char *ocrit; 24 mp_thresholds offset_thresholds;
25 thresholds *offset_thresholds;
26 25
27 // stratum stuff 26 // stratum stuff
28 bool do_stratum; 27 bool do_stratum;
29 char *swarn; 28 mp_thresholds stratum_thresholds;
30 char *scrit;
31 thresholds *stratum_thresholds;
32 29
33 // jitter stuff 30 // jitter stuff
34 bool do_jitter; 31 bool do_jitter;
35 char *jwarn; 32 mp_thresholds jitter_thresholds;
36 char *jcrit;
37 thresholds *jitter_thresholds;
38 33
34 bool output_format_is_set;
35 mp_output_format output_format;
39} check_ntp_peer_config; 36} check_ntp_peer_config;
40 37
41check_ntp_peer_config check_ntp_peer_config_init() { 38check_ntp_peer_config check_ntp_peer_config_init() {
@@ -45,23 +42,41 @@ check_ntp_peer_config check_ntp_peer_config_init() {
45 42
46 .quiet = false, 43 .quiet = false,
47 .do_truechimers = false, 44 .do_truechimers = false,
48 .twarn = "0:", 45 .truechimer_thresholds = mp_thresholds_init(),
49 .tcrit = "0:",
50 .truechimer_thresholds = NULL,
51 46
52 .owarn = "60", 47 .offset_thresholds = mp_thresholds_init(),
53 .ocrit = "120",
54 .offset_thresholds = NULL,
55 48
56 .do_stratum = false, 49 .do_stratum = false,
57 .swarn = "-1:16", 50 .stratum_thresholds = mp_thresholds_init(),
58 .scrit = "-1:16",
59 .stratum_thresholds = NULL,
60 51
61 .do_jitter = false, 52 .do_jitter = false,
62 .jwarn = "-1:5000", 53 .jitter_thresholds = mp_thresholds_init(),
63 .jcrit = "-1:10000", 54
64 .jitter_thresholds = NULL, 55 .output_format_is_set = false,
65 }; 56 };
57
58 mp_range stratum_default = mp_range_init();
59 stratum_default = mp_range_set_start(stratum_default, mp_create_pd_value(-1));
60 stratum_default = mp_range_set_end(stratum_default, mp_create_pd_value(16));
61 tmp.stratum_thresholds = mp_thresholds_set_warn(tmp.stratum_thresholds, stratum_default);
62 tmp.stratum_thresholds = mp_thresholds_set_crit(tmp.stratum_thresholds, stratum_default);
63
64 mp_range jitter_w_default = mp_range_init();
65 jitter_w_default = mp_range_set_start(jitter_w_default, mp_create_pd_value(-1));
66 jitter_w_default = mp_range_set_end(jitter_w_default, mp_create_pd_value(5000));
67 tmp.jitter_thresholds = mp_thresholds_set_warn(tmp.jitter_thresholds, jitter_w_default);
68
69 mp_range jitter_c_default = mp_range_init();
70 jitter_c_default = mp_range_set_start(jitter_c_default, mp_create_pd_value(-1));
71 jitter_c_default = mp_range_set_end(jitter_c_default, mp_create_pd_value(10000));
72 tmp.jitter_thresholds = mp_thresholds_set_crit(tmp.jitter_thresholds, jitter_c_default);
73
74 mp_range offset_w_default = mp_range_init();
75 offset_w_default = mp_range_set_end(offset_w_default, mp_create_pd_value(60));
76 tmp.offset_thresholds = mp_thresholds_set_warn(tmp.offset_thresholds, offset_w_default);
77
78 mp_range offset_c_default = mp_range_init();
79 offset_c_default = mp_range_set_end(offset_c_default, mp_create_pd_value(120));
80 tmp.offset_thresholds = mp_thresholds_set_crit(tmp.offset_thresholds, offset_c_default);
66 return tmp; 81 return tmp;
67} 82}
diff --git a/plugins/check_ntp_time.c b/plugins/check_ntp_time.c
index ad69b804..602b6010 100644
--- a/plugins/check_ntp_time.c
+++ b/plugins/check_ntp_time.c
@@ -34,12 +34,10 @@
34 * 34 *
35 *****************************************************************************/ 35 *****************************************************************************/
36 36
37const char *progname = "check_ntp_time"; 37#include "output.h"
38const char *copyright = "2006-2024";
39const char *email = "devel@monitoring-plugins.org";
40
41#include "common.h" 38#include "common.h"
42#include "netutils.h" 39#include "netutils.h"
40#include "perfdata.h"
43#include "utils.h" 41#include "utils.h"
44#include "states.h" 42#include "states.h"
45#include "thresholds.h" 43#include "thresholds.h"
@@ -47,6 +45,10 @@ const char *email = "devel@monitoring-plugins.org";
47 45
48static int verbose = 0; 46static int verbose = 0;
49 47
48const char *progname = "check_ntp_time";
49const char *copyright = "2006-2024";
50const char *email = "devel@monitoring-plugins.org";
51
50typedef struct { 52typedef struct {
51 int errorcode; 53 int errorcode;
52 check_ntp_time_config config; 54 check_ntp_time_config config;
@@ -61,9 +63,6 @@ void print_usage(void);
61# define AVG_NUM 4 63# define AVG_NUM 4
62#endif 64#endif
63 65
64/* max size of control message data */
65#define MAX_CM_SIZE 468
66
67/* this structure holds everything in an ntp request/response as per rfc1305 */ 66/* this structure holds everything in an ntp request/response as per rfc1305 */
68typedef struct { 67typedef struct {
69 uint8_t flags; /* byte with leapindicator,vers,mode. see macros */ 68 uint8_t flags; /* byte with leapindicator,vers,mode. see macros */
@@ -169,7 +168,9 @@ typedef struct {
169 : 0) 168 : 0)
170 169
171/* convert a struct timeval to a double */ 170/* convert a struct timeval to a double */
172#define TVasDOUBLE(x) (double)(x.tv_sec + (0.000001 * x.tv_usec)) 171static double TVasDOUBLE(struct timeval time) {
172 return ((double)time.tv_sec + (0.000001 * (double)time.tv_usec));
173}
173 174
174/* convert an ntp 64-bit fp number to a struct timeval */ 175/* convert an ntp 64-bit fp number to a struct timeval */
175#define NTP64toTV(n, t) \ 176#define NTP64toTV(n, t) \
@@ -262,8 +263,8 @@ void setup_request(ntp_message *message) {
262/* select the "best" server from a list of servers, and return its index. 263/* select the "best" server from a list of servers, and return its index.
263 * this is done by filtering servers based on stratum, dispersion, and 264 * this is done by filtering servers based on stratum, dispersion, and
264 * finally round-trip delay. */ 265 * finally round-trip delay. */
265int best_offset_server(const ntp_server_results *slist, int nservers) { 266static int best_offset_server(const ntp_server_results *slist, int nservers) {
266 int best_server = -1; 267 int best_server_index = -1;
267 268
268 /* for each server */ 269 /* for each server */
269 for (int cserver = 0; cserver < nservers; cserver++) { 270 for (int cserver = 0; cserver < nservers; cserver++) {
@@ -286,33 +287,33 @@ int best_offset_server(const ntp_server_results *slist, int nservers) {
286 } 287 }
287 288
288 /* If we don't have a server yet, use the first one */ 289 /* If we don't have a server yet, use the first one */
289 if (best_server == -1) { 290 if (best_server_index == -1) {
290 best_server = cserver; 291 best_server_index = cserver;
291 DBG(printf("using peer %d as our first candidate\n", best_server)); 292 DBG(printf("using peer %d as our first candidate\n", best_server_index));
292 continue; 293 continue;
293 } 294 }
294 295
295 /* compare the server to the best one we've seen so far */ 296 /* compare the server to the best one we've seen so far */
296 /* does it have an equal or better stratum? */ 297 /* does it have an equal or better stratum? */
297 DBG(printf("comparing peer %d with peer %d\n", cserver, best_server)); 298 DBG(printf("comparing peer %d with peer %d\n", cserver, best_server_index));
298 if (slist[cserver].stratum <= slist[best_server].stratum) { 299 if (slist[cserver].stratum <= slist[best_server_index].stratum) {
299 DBG(printf("stratum for peer %d <= peer %d\n", cserver, best_server)); 300 DBG(printf("stratum for peer %d <= peer %d\n", cserver, best_server_index));
300 /* does it have an equal or better dispersion? */ 301 /* does it have an equal or better dispersion? */
301 if (slist[cserver].rtdisp <= slist[best_server].rtdisp) { 302 if (slist[cserver].rtdisp <= slist[best_server_index].rtdisp) {
302 DBG(printf("dispersion for peer %d <= peer %d\n", cserver, best_server)); 303 DBG(printf("dispersion for peer %d <= peer %d\n", cserver, best_server_index));
303 /* does it have a better rtdelay? */ 304 /* does it have a better rtdelay? */
304 if (slist[cserver].rtdelay < slist[best_server].rtdelay) { 305 if (slist[cserver].rtdelay < slist[best_server_index].rtdelay) {
305 DBG(printf("rtdelay for peer %d < peer %d\n", cserver, best_server)); 306 DBG(printf("rtdelay for peer %d < peer %d\n", cserver, best_server_index));
306 best_server = cserver; 307 best_server_index = cserver;
307 DBG(printf("peer %d is now our best candidate\n", best_server)); 308 DBG(printf("peer %d is now our best candidate\n", best_server_index));
308 } 309 }
309 } 310 }
310 } 311 }
311 } 312 }
312 313
313 if (best_server >= 0) { 314 if (best_server_index >= 0) {
314 DBG(printf("best server selected: peer %d\n", best_server)); 315 DBG(printf("best server selected: peer %d\n", best_server_index));
315 return best_server; 316 return best_server_index;
316 } 317 }
317 DBG(printf("no peers meeting synchronization criteria :(\n")); 318 DBG(printf("no peers meeting synchronization criteria :(\n"));
318 return -1; 319 return -1;
@@ -323,7 +324,11 @@ int best_offset_server(const ntp_server_results *slist, int nservers) {
323 * we don't waste time sitting around waiting for single packets. 324 * we don't waste time sitting around waiting for single packets.
324 * - we also "manually" handle resolving host names and connecting, because 325 * - we also "manually" handle resolving host names and connecting, because
325 * we have to do it in a way that our lazy macros don't handle currently :( */ 326 * we have to do it in a way that our lazy macros don't handle currently :( */
326double offset_request(const char *host, const char *port, mp_state_enum *status, int time_offset) { 327typedef struct {
328 mp_state_enum offset_result;
329 double offset;
330} offset_request_wrapper;
331static offset_request_wrapper offset_request(const char *host, const char *port, int time_offset) {
327 /* setup hints to only return results from getaddrinfo that we'd like */ 332 /* setup hints to only return results from getaddrinfo that we'd like */
328 struct addrinfo hints; 333 struct addrinfo hints;
329 memset(&hints, 0, sizeof(struct addrinfo)); 334 memset(&hints, 0, sizeof(struct addrinfo));
@@ -462,12 +467,18 @@ double offset_request(const char *host, const char *port, mp_state_enum *status,
462 die(STATE_CRITICAL, "NTP CRITICAL: No response from NTP server\n"); 467 die(STATE_CRITICAL, "NTP CRITICAL: No response from NTP server\n");
463 } 468 }
464 469
470 offset_request_wrapper result = {
471 .offset = 0,
472 .offset_result = STATE_UNKNOWN,
473 };
474
465 /* now, pick the best server from the list */ 475 /* now, pick the best server from the list */
466 double avg_offset = 0.; 476 double avg_offset = 0.;
467 int best_index = best_offset_server(servers, num_hosts); 477 int best_index = best_offset_server(servers, num_hosts);
468 if (best_index < 0) { 478 if (best_index < 0) {
469 *status = STATE_UNKNOWN; 479 result.offset_result = STATE_UNKNOWN;
470 } else { 480 } else {
481 result.offset_result = STATE_OK;
471 /* finally, calculate the average offset */ 482 /* finally, calculate the average offset */
472 for (int i = 0; i < servers[best_index].num_responses; i++) { 483 for (int i = 0; i < servers[best_index].num_responses; i++) {
473 avg_offset += servers[best_index].offset[i]; 484 avg_offset += servers[best_index].offset[i];
@@ -488,22 +499,30 @@ double offset_request(const char *host, const char *port, mp_state_enum *status,
488 if (verbose) { 499 if (verbose) {
489 printf("overall average offset: %.10g\n", avg_offset); 500 printf("overall average offset: %.10g\n", avg_offset);
490 } 501 }
491 return avg_offset; 502
503 result.offset = avg_offset;
504 return result;
492} 505}
493 506
494check_ntp_time_config_wrapper process_arguments(int argc, char **argv) { 507static check_ntp_time_config_wrapper process_arguments(int argc, char **argv) {
508
509 enum {
510 output_format_index = CHAR_MAX + 1,
511 };
512
495 static struct option longopts[] = {{"version", no_argument, 0, 'V'}, 513 static struct option longopts[] = {{"version", no_argument, 0, 'V'},
496 {"help", no_argument, 0, 'h'}, 514 {"help", no_argument, 0, 'h'},
497 {"verbose", no_argument, 0, 'v'}, 515 {"verbose", no_argument, 0, 'v'},
498 {"use-ipv4", no_argument, 0, '4'}, 516 {"use-ipv4", no_argument, 0, '4'},
499 {"use-ipv6", no_argument, 0, '6'}, 517 {"use-ipv6", no_argument, 0, '6'},
500 {"quiet", no_argument, 0, 'q'}, 518 {"quiet", no_argument, 0, 'q'},
501 {"time-offset", optional_argument, 0, 'o'}, 519 {"time-offset", required_argument, 0, 'o'},
502 {"warning", required_argument, 0, 'w'}, 520 {"warning", required_argument, 0, 'w'},
503 {"critical", required_argument, 0, 'c'}, 521 {"critical", required_argument, 0, 'c'},
504 {"timeout", required_argument, 0, 't'}, 522 {"timeout", required_argument, 0, 't'},
505 {"hostname", required_argument, 0, 'H'}, 523 {"hostname", required_argument, 0, 'H'},
506 {"port", required_argument, 0, 'p'}, 524 {"port", required_argument, 0, 'p'},
525 {"output-format", required_argument, 0, output_format_index},
507 {0, 0, 0, 0}}; 526 {0, 0, 0, 0}};
508 527
509 if (argc < 2) { 528 if (argc < 2) {
@@ -515,9 +534,6 @@ check_ntp_time_config_wrapper process_arguments(int argc, char **argv) {
515 .config = check_ntp_time_config_init(), 534 .config = check_ntp_time_config_init(),
516 }; 535 };
517 536
518 char *owarn = "60";
519 char *ocrit = "120";
520
521 while (true) { 537 while (true) {
522 int option = 0; 538 int option = 0;
523 int option_char = getopt_long(argc, argv, "Vhv46qw:c:t:H:p:o:", longopts, &option); 539 int option_char = getopt_long(argc, argv, "Vhv46qw:c:t:H:p:o:", longopts, &option);
@@ -526,6 +542,17 @@ check_ntp_time_config_wrapper process_arguments(int argc, char **argv) {
526 } 542 }
527 543
528 switch (option_char) { 544 switch (option_char) {
545 case output_format_index: {
546 parsed_output_format parser = mp_parse_output_format(optarg);
547 if (!parser.parsing_success) {
548 printf("Invalid output format: %s\n", optarg);
549 exit(STATE_UNKNOWN);
550 }
551
552 result.config.output_format_is_set = true;
553 result.config.output_format = parser.output_format;
554 break;
555 }
529 case 'h': 556 case 'h':
530 print_help(); 557 print_help();
531 exit(STATE_UNKNOWN); 558 exit(STATE_UNKNOWN);
@@ -540,12 +567,24 @@ check_ntp_time_config_wrapper process_arguments(int argc, char **argv) {
540 case 'q': 567 case 'q':
541 result.config.quiet = true; 568 result.config.quiet = true;
542 break; 569 break;
543 case 'w': 570 case 'w': {
544 owarn = optarg; 571 mp_range_parsed tmp = mp_parse_range_string(optarg);
545 break; 572 if (tmp.error != MP_PARSING_SUCCES) {
546 case 'c': 573 die(STATE_UNKNOWN, "failed to parse warning threshold");
547 ocrit = optarg; 574 }
548 break; 575
576 result.config.offset_thresholds =
577 mp_thresholds_set_warn(result.config.offset_thresholds, tmp.range);
578 } break;
579 case 'c': {
580 mp_range_parsed tmp = mp_parse_range_string(optarg);
581 if (tmp.error != MP_PARSING_SUCCES) {
582 die(STATE_UNKNOWN, "failed to parse crit threshold");
583 }
584
585 result.config.offset_thresholds =
586 mp_thresholds_set_crit(result.config.offset_thresholds, tmp.range);
587 } break;
549 case 'H': 588 case 'H':
550 if (!is_host(optarg)) { 589 if (!is_host(optarg)) {
551 usage2(_("Invalid hostname/address"), optarg); 590 usage2(_("Invalid hostname/address"), optarg);
@@ -582,16 +621,9 @@ check_ntp_time_config_wrapper process_arguments(int argc, char **argv) {
582 usage4(_("Hostname was not supplied")); 621 usage4(_("Hostname was not supplied"));
583 } 622 }
584 623
585 set_thresholds(&result.config.offset_thresholds, owarn, ocrit);
586
587 return result; 624 return result;
588} 625}
589 626
590char *perfd_offset(double offset, thresholds *offset_thresholds) {
591 return fperfdata("offset", offset, "s", true, offset_thresholds->warning->end, true,
592 offset_thresholds->critical->end, false, 0, false, 0);
593}
594
595int main(int argc, char *argv[]) { 627int main(int argc, char *argv[]) {
596 setlocale(LC_ALL, ""); 628 setlocale(LC_ALL, "");
597 bindtextdomain(PACKAGE, LOCALEDIR); 629 bindtextdomain(PACKAGE, LOCALEDIR);
@@ -608,52 +640,47 @@ int main(int argc, char *argv[]) {
608 640
609 const check_ntp_time_config config = tmp_config.config; 641 const check_ntp_time_config config = tmp_config.config;
610 642
643 if (config.output_format_is_set) {
644 mp_set_format(config.output_format);
645 }
646
611 /* initialize alarm signal handling */ 647 /* initialize alarm signal handling */
612 signal(SIGALRM, socket_timeout_alarm_handler); 648 signal(SIGALRM, socket_timeout_alarm_handler);
613 649
614 /* set socket timeout */ 650 /* set socket timeout */
615 alarm(socket_timeout); 651 alarm(socket_timeout);
616 652
617 mp_state_enum offset_result = STATE_OK; 653 mp_check overall = mp_check_init();
618 mp_state_enum result = STATE_OK;
619 double offset =
620 offset_request(config.server_address, config.port, &offset_result, config.time_offset);
621 if (offset_result == STATE_UNKNOWN) {
622 result = ((!config.quiet) ? STATE_UNKNOWN : STATE_CRITICAL);
623 } else {
624 result = get_status(fabs(offset), config.offset_thresholds);
625 }
626 654
627 char *result_line; 655 mp_subcheck sc_offset = mp_subcheck_init();
628 switch (result) { 656 offset_request_wrapper offset_result =
629 case STATE_CRITICAL: 657 offset_request(config.server_address, config.port, config.time_offset);
630 xasprintf(&result_line, _("NTP CRITICAL:"));
631 break;
632 case STATE_WARNING:
633 xasprintf(&result_line, _("NTP WARNING:"));
634 break;
635 case STATE_OK:
636 xasprintf(&result_line, _("NTP OK:"));
637 break;
638 default:
639 xasprintf(&result_line, _("NTP UNKNOWN:"));
640 break;
641 }
642 658
643 char *perfdata_line; 659 if (offset_result.offset_result == STATE_UNKNOWN) {
644 if (offset_result == STATE_UNKNOWN) { 660 sc_offset =
645 xasprintf(&result_line, "%s %s", result_line, _("Offset unknown")); 661 mp_set_subcheck_state(sc_offset, (!config.quiet) ? STATE_UNKNOWN : STATE_CRITICAL);
646 xasprintf(&perfdata_line, ""); 662 xasprintf(&sc_offset.output, "Offset unknown");
647 } else { 663 mp_add_subcheck_to_check(&overall, sc_offset);
648 xasprintf(&result_line, "%s %s %.10g secs", result_line, _("Offset"), offset); 664 mp_exit(overall);
649 xasprintf(&perfdata_line, "%s", perfd_offset(offset, config.offset_thresholds));
650 } 665 }
651 printf("%s|%s\n", result_line, perfdata_line); 666
667 xasprintf(&sc_offset.output, "Offset: %.6fs", offset_result.offset);
668
669 mp_perfdata pd_offset = perfdata_init();
670 pd_offset = mp_set_pd_value(pd_offset, fabs(offset_result.offset));
671 pd_offset.label = "offset";
672 pd_offset.uom = "s";
673 pd_offset = mp_pd_set_thresholds(pd_offset, config.offset_thresholds);
674
675 sc_offset = mp_set_subcheck_state(sc_offset, mp_get_pd_status(pd_offset));
676
677 mp_add_perfdata_to_subcheck(&sc_offset, pd_offset);
678 mp_add_subcheck_to_check(&overall, sc_offset);
652 679
653 if (config.server_address != NULL) { 680 if (config.server_address != NULL) {
654 free(config.server_address); 681 free(config.server_address);
655 } 682 }
656 exit(result); 683 mp_exit(overall);
657} 684}
658 685
659void print_help(void) { 686void print_help(void) {
@@ -677,10 +704,11 @@ void print_help(void) {
677 printf(" %s\n", _("Offset to result in warning status (seconds)")); 704 printf(" %s\n", _("Offset to result in warning status (seconds)"));
678 printf(" %s\n", "-c, --critical=THRESHOLD"); 705 printf(" %s\n", "-c, --critical=THRESHOLD");
679 printf(" %s\n", _("Offset to result in critical status (seconds)")); 706 printf(" %s\n", _("Offset to result in critical status (seconds)"));
680 printf(" %s\n", "-o, --time_offset=INTEGER"); 707 printf(" %s\n", "-o, --time-offset=INTEGER");
681 printf(" %s\n", _("Expected offset of the ntp server relative to local server (seconds)")); 708 printf(" %s\n", _("Expected offset of the ntp server relative to local server (seconds)"));
682 printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); 709 printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
683 printf(UT_VERBOSE); 710 printf(UT_VERBOSE);
711 printf(UT_OUTPUT_FORMAT);
684 712
685 printf("\n"); 713 printf("\n");
686 printf("%s\n", _("This plugin checks the clock offset between the local host and a")); 714 printf("%s\n", _("This plugin checks the clock offset between the local host and a"));
diff --git a/plugins/check_ntp_time.d/config.h b/plugins/check_ntp_time.d/config.h
index 99dabbbd..9bbd82aa 100644
--- a/plugins/check_ntp_time.d/config.h
+++ b/plugins/check_ntp_time.d/config.h
@@ -1,6 +1,7 @@
1#pragma once 1#pragma once
2 2
3#include "../../config.h" 3#include "../../config.h"
4#include "output.h"
4#include "thresholds.h" 5#include "thresholds.h"
5#include <stddef.h> 6#include <stddef.h>
6 7
@@ -11,7 +12,10 @@ typedef struct {
11 bool quiet; 12 bool quiet;
12 int time_offset; 13 int time_offset;
13 14
14 thresholds *offset_thresholds; 15 mp_thresholds offset_thresholds;
16
17 bool output_format_is_set;
18 mp_output_format output_format;
15} check_ntp_time_config; 19} check_ntp_time_config;
16 20
17check_ntp_time_config check_ntp_time_config_init() { 21check_ntp_time_config check_ntp_time_config_init() {
@@ -22,7 +26,18 @@ check_ntp_time_config check_ntp_time_config_init() {
22 .quiet = false, 26 .quiet = false,
23 .time_offset = 0, 27 .time_offset = 0,
24 28
25 .offset_thresholds = NULL, 29 .offset_thresholds = mp_thresholds_init(),
30
31 .output_format_is_set = false,
26 }; 32 };
33
34 mp_range warning = mp_range_init();
35 warning = mp_range_set_end(warning, mp_create_pd_value(60));
36 tmp.offset_thresholds = mp_thresholds_set_warn(tmp.offset_thresholds, warning);
37
38 mp_range critical = mp_range_init();
39 critical = mp_range_set_end(warning, mp_create_pd_value(120));
40 tmp.offset_thresholds = mp_thresholds_set_crit(tmp.offset_thresholds, critical);
41
27 return tmp; 42 return tmp;
28} 43}
diff --git a/plugins/check_pgsql.c b/plugins/check_pgsql.c
index 793a686f..0ce75e0a 100644
--- a/plugins/check_pgsql.c
+++ b/plugins/check_pgsql.c
@@ -28,27 +28,29 @@
28 * 28 *
29 *****************************************************************************/ 29 *****************************************************************************/
30 30
31#include "output.h"
32#include "perfdata.h"
31#include "states.h" 33#include "states.h"
32const char *progname = "check_pgsql";
33const char *copyright = "1999-2024";
34const char *email = "devel@monitoring-plugins.org";
35
36#include "common.h" 34#include "common.h"
37#include "utils.h" 35#include "utils.h"
38#include "utils_cmd.h" 36#include "utils_cmd.h"
39#include "check_pgsql.d/config.h" 37#include "check_pgsql.d/config.h"
40#include "thresholds.h" 38#include "thresholds.h"
41
42#include "netutils.h" 39#include "netutils.h"
43#include <libpq-fe.h> 40#include <libpq-fe.h>
44#include <pg_config_manual.h> 41#include <pg_config_manual.h>
45 42
43const char *progname = "check_pgsql";
44const char *copyright = "1999-2024";
45const char *email = "devel@monitoring-plugins.org";
46
46#define DEFAULT_HOST "127.0.0.1" 47#define DEFAULT_HOST "127.0.0.1"
47 48
48/* return the PSQL server version as a 3-tuple */ 49/* return the PSQL server version as a 3-tuple */
49#define PSQL_SERVER_VERSION3(server_version) \ 50#define PSQL_SERVER_VERSION3(server_version) \
50 (server_version) / 10000, (server_version) / 100 - (int)((server_version) / 10000) * 100, \ 51 ((server_version) / 10000), \
51 (server_version) - (int)((server_version) / 100) * 100 52 (((server_version) / 100) - (int)(((server_version) / 10000) * 100)), \
53 (server_version) - (int)(((server_version) / 100) * 100)
52/* return true if the given host is a UNIX domain socket */ 54/* return true if the given host is a UNIX domain socket */
53#define PSQL_IS_UNIX_DOMAIN_SOCKET(host) ((NULL == (host)) || ('\0' == *(host)) || ('/' == *(host))) 55#define PSQL_IS_UNIX_DOMAIN_SOCKET(host) ((NULL == (host)) || ('\0' == *(host)) || ('/' == *(host)))
54/* return a 3-tuple identifying a host/port independent of the socket type */ 56/* return a 3-tuple identifying a host/port independent of the socket type */
@@ -64,15 +66,25 @@ static check_pgsql_config_wrapper process_arguments(int /*argc*/, char ** /*argv
64 66
65static void print_help(void); 67static void print_help(void);
66static bool is_pg_logname(char * /*username*/); 68static bool is_pg_logname(char * /*username*/);
67static mp_state_enum do_query(PGconn * /*conn*/, char * /*query*/, const char /*pgqueryname*/[], 69
68 thresholds * /*qthresholds*/, char * /*query_warning*/, 70typedef enum {
69 char * /*query_critical*/); 71 QUERY_OK,
72 ERROR_WITH_QUERY, // critical
73 NO_ROWS_RETURNED, // warning
74 NO_COLUMNS_RETURNED, // warning
75 NO_DATA_RETURNED, // critica/
76 RESULT_IS_NOT_NUMERIC // critical
77} do_query_errorcode;
78
79typedef struct {
80 do_query_errorcode error_code;
81 double numerical_result;
82} do_query_wrapper;
83static do_query_wrapper do_query(PGconn * /*conn*/, char * /*query*/);
70void print_usage(void); 84void print_usage(void);
71 85
72static int verbose = 0; 86static int verbose = 0;
73 87
74#define OPTID_QUERYNAME -1000
75
76/****************************************************************************** 88/******************************************************************************
77 89
78The (pseudo?)literate programming XML is contained within \@\@\- <XML> \-\@\@ 90The (pseudo?)literate programming XML is contained within \@\@\- <XML> \-\@\@
@@ -138,8 +150,8 @@ int main(int argc, char **argv) {
138 150
139 const check_pgsql_config config = tmp_config.config; 151 const check_pgsql_config config = tmp_config.config;
140 152
141 if (verbose > 2) { 153 if (config.output_format_is_set) {
142 printf("Arguments initialized\n"); 154 mp_set_format(config.output_format);
143 } 155 }
144 156
145 /* Set signal handling and alarm */ 157 /* Set signal handling and alarm */
@@ -199,21 +211,41 @@ int main(int argc, char **argv) {
199 if (verbose) { 211 if (verbose) {
200 printf("Verifying connection\n"); 212 printf("Verifying connection\n");
201 } 213 }
214
215 mp_check overall = mp_check_init();
216
217 mp_subcheck sc_connection = mp_subcheck_init();
218
202 if (PQstatus(conn) == CONNECTION_BAD) { 219 if (PQstatus(conn) == CONNECTION_BAD) {
203 printf(_("CRITICAL - no connection to '%s' (%s).\n"), config.dbName, PQerrorMessage(conn)); 220 sc_connection = mp_set_subcheck_state(sc_connection, STATE_CRITICAL);
221 xasprintf(&sc_connection.output, "no connection to '%s' (%s)", config.dbName,
222 PQerrorMessage(conn));
204 PQfinish(conn); 223 PQfinish(conn);
205 return STATE_CRITICAL; 224 mp_add_subcheck_to_check(&overall, sc_connection);
206 } 225 mp_exit(overall);
207
208 mp_state_enum status = STATE_UNKNOWN;
209 if (elapsed_time > config.tcrit) {
210 status = STATE_CRITICAL;
211 } else if (elapsed_time > config.twarn) {
212 status = STATE_WARNING;
213 } else { 226 } else {
214 status = STATE_OK; 227 sc_connection = mp_set_subcheck_state(sc_connection, STATE_OK);
228 xasprintf(&sc_connection.output, "connected to '%s'", config.dbName);
229 mp_add_subcheck_to_check(&overall, sc_connection);
215 } 230 }
216 231
232 mp_subcheck sc_connection_time = mp_subcheck_init();
233 sc_connection_time = mp_set_subcheck_default_state(sc_connection_time, STATE_UNKNOWN);
234
235 xasprintf(&sc_connection_time.output, "connection time: %.10g", elapsed_time);
236
237 mp_perfdata pd_connection_time = perfdata_init();
238 pd_connection_time.label = "time";
239 pd_connection_time.uom = "s";
240 pd_connection_time = mp_set_pd_value(pd_connection_time, elapsed_time);
241 pd_connection_time = mp_pd_set_thresholds(pd_connection_time, config.time_thresholds);
242
243 mp_add_perfdata_to_subcheck(&sc_connection_time, pd_connection_time);
244 sc_connection_time =
245 mp_set_subcheck_state(sc_connection_time, mp_get_pd_status(pd_connection_time));
246
247 mp_add_subcheck_to_check(&overall, sc_connection_time);
248
217 if (verbose) { 249 if (verbose) {
218 char *server_host = PQhost(conn); 250 char *server_host = PQhost(conn);
219 int server_version = PQserverVersion(conn); 251 int server_version = PQserverVersion(conn);
@@ -225,25 +257,87 @@ int main(int argc, char **argv) {
225 PSQL_SERVER_VERSION3(server_version), PQprotocolVersion(conn), PQbackendPID(conn)); 257 PSQL_SERVER_VERSION3(server_version), PQprotocolVersion(conn), PQbackendPID(conn));
226 } 258 }
227 259
228 printf(_(" %s - database %s (%f sec.)|%s\n"), state_text(status), config.dbName, elapsed_time,
229 fperfdata("time", elapsed_time, "s", (config.twarn > 0.0), config.twarn,
230 (config.tcrit > 0.0), config.tcrit, true, 0, false, 0));
231
232 mp_state_enum query_status = STATE_UNKNOWN;
233 if (config.pgquery) { 260 if (config.pgquery) {
234 query_status = do_query(conn, config.pgquery, config.pgqueryname, config.qthresholds, 261 mp_subcheck sc_query = mp_subcheck_init();
235 config.query_warning, config.query_critical); 262 sc_query = mp_set_subcheck_default_state(sc_query, STATE_UNKNOWN);
263 if (config.pgqueryname) {
264 xasprintf(&sc_query.output, "query '%s'", config.pgqueryname);
265 } else {
266 xasprintf(&sc_query.output, "query '%s'", config.pgquery);
267 }
268
269 do_query_wrapper query_result = do_query(conn, config.pgquery);
270
271 switch (query_result.error_code) {
272 case QUERY_OK: {
273 // Query was successful and there is a numerical result
274 sc_query = mp_set_subcheck_state(sc_query, STATE_OK);
275 xasprintf(&sc_query.output, "%s succeeded", sc_query.output);
276
277 mp_perfdata pd_query = perfdata_init();
278 pd_query = mp_set_pd_value(pd_query, query_result.numerical_result);
279 pd_query = mp_pd_set_thresholds(pd_query, config.qthresholds);
280 pd_query.label = "query";
281
282 mp_subcheck sc_query_compare = mp_subcheck_init();
283 mp_state_enum query_compare_state = mp_get_pd_status(pd_query);
284
285 sc_query_compare = mp_set_subcheck_state(sc_query_compare, query_compare_state);
286 mp_add_perfdata_to_subcheck(&sc_query_compare, pd_query);
287
288 if (query_compare_state == STATE_OK) {
289 xasprintf(&sc_query_compare.output, "query result '%f' is within thresholds",
290 query_result.numerical_result);
291 } else {
292 xasprintf(&sc_query_compare.output, "query result '%f' is violating thresholds",
293 query_result.numerical_result);
294 }
295 mp_add_subcheck_to_check(&overall, sc_query_compare);
296
297 } break;
298 case ERROR_WITH_QUERY:
299 xasprintf(&sc_query.output, "%s - Error with query: %s", sc_query.output,
300 PQerrorMessage(conn));
301 sc_query = mp_set_subcheck_state(sc_query, STATE_CRITICAL);
302 break;
303 case NO_ROWS_RETURNED:
304 xasprintf(&sc_query.output, "%s - no rows were returned by the query", sc_query.output);
305 sc_query = mp_set_subcheck_state(sc_query, STATE_WARNING);
306 break;
307 case NO_COLUMNS_RETURNED:
308 xasprintf(&sc_query.output, "%s - no columns were returned by the query",
309 sc_query.output);
310 sc_query = mp_set_subcheck_state(sc_query, STATE_WARNING);
311 break;
312 case NO_DATA_RETURNED:
313 xasprintf(&sc_query.output, "%s - no data was returned by the query", sc_query.output);
314 sc_query = mp_set_subcheck_state(sc_query, STATE_WARNING);
315 break;
316 case RESULT_IS_NOT_NUMERIC:
317 xasprintf(&sc_query.output, "%s - result of the query is not numeric", sc_query.output);
318 sc_query = mp_set_subcheck_state(sc_query, STATE_CRITICAL);
319 break;
320 };
321
322 mp_add_subcheck_to_check(&overall, sc_query);
236 } 323 }
237 324
238 if (verbose) { 325 if (verbose) {
239 printf("Closing connection\n"); 326 printf("Closing connection\n");
240 } 327 }
241 PQfinish(conn); 328 PQfinish(conn);
242 return (config.pgquery && query_status > status) ? query_status : status; 329
330 mp_exit(overall);
243} 331}
244 332
245/* process command-line arguments */ 333/* process command-line arguments */
246check_pgsql_config_wrapper process_arguments(int argc, char **argv) { 334static check_pgsql_config_wrapper process_arguments(int argc, char **argv) {
335
336 enum {
337 OPTID_QUERYNAME = CHAR_MAX + 1,
338 output_format_index,
339 };
340
247 static struct option longopts[] = {{"help", no_argument, 0, 'h'}, 341 static struct option longopts[] = {{"help", no_argument, 0, 'h'},
248 {"version", no_argument, 0, 'V'}, 342 {"version", no_argument, 0, 'V'},
249 {"timeout", required_argument, 0, 't'}, 343 {"timeout", required_argument, 0, 't'},
@@ -261,6 +355,7 @@ check_pgsql_config_wrapper process_arguments(int argc, char **argv) {
261 {"query_critical", required_argument, 0, 'C'}, 355 {"query_critical", required_argument, 0, 'C'},
262 {"query_warning", required_argument, 0, 'W'}, 356 {"query_warning", required_argument, 0, 'W'},
263 {"verbose", no_argument, 0, 'v'}, 357 {"verbose", no_argument, 0, 'v'},
358 {"output-format", required_argument, 0, output_format_index},
264 {0, 0, 0, 0}}; 359 {0, 0, 0, 0}};
265 360
266 check_pgsql_config_wrapper result = { 361 check_pgsql_config_wrapper result = {
@@ -278,6 +373,17 @@ check_pgsql_config_wrapper process_arguments(int argc, char **argv) {
278 } 373 }
279 374
280 switch (option_char) { 375 switch (option_char) {
376 case output_format_index: {
377 parsed_output_format parser = mp_parse_output_format(optarg);
378 if (!parser.parsing_success) {
379 printf("Invalid output format: %s\n", optarg);
380 exit(STATE_UNKNOWN);
381 }
382
383 result.config.output_format_is_set = true;
384 result.config.output_format = parser.output_format;
385 break;
386 }
281 case '?': /* usage */ 387 case '?': /* usage */
282 usage5(); 388 usage5();
283 case 'h': /* help */ 389 case 'h': /* help */
@@ -293,26 +399,40 @@ check_pgsql_config_wrapper process_arguments(int argc, char **argv) {
293 timeout_interval = atoi(optarg); 399 timeout_interval = atoi(optarg);
294 } 400 }
295 break; 401 break;
296 case 'c': /* critical time threshold */ 402 case 'c': /* critical time threshold */ {
297 if (!is_nonnegative(optarg)) { 403 mp_range_parsed tmp = mp_parse_range_string(optarg);
298 usage2(_("Critical threshold must be a positive integer"), optarg); 404 if (tmp.error != MP_PARSING_SUCCES) {
299 } else { 405 die(STATE_UNKNOWN, "failed to parse critical time threshold");
300 result.config.tcrit = strtod(optarg, NULL);
301 } 406 }
302 break; 407 result.config.time_thresholds =
303 case 'w': /* warning time threshold */ 408 mp_thresholds_set_crit(result.config.time_thresholds, tmp.range);
304 if (!is_nonnegative(optarg)) { 409 } break;
305 usage2(_("Warning threshold must be a positive integer"), optarg); 410 case 'w': /* warning time threshold */ {
306 } else { 411 mp_range_parsed tmp = mp_parse_range_string(optarg);
307 result.config.twarn = strtod(optarg, NULL); 412 if (tmp.error != MP_PARSING_SUCCES) {
413 die(STATE_UNKNOWN, "failed to parse warning time threshold");
308 } 414 }
309 break; 415 result.config.time_thresholds =
310 case 'C': /* critical query threshold */ 416 mp_thresholds_set_warn(result.config.time_thresholds, tmp.range);
311 result.config.query_critical = optarg; 417 } break;
312 break; 418 case 'C': /* critical query threshold */ {
313 case 'W': /* warning query threshold */ 419 mp_range_parsed tmp = mp_parse_range_string(optarg);
314 result.config.query_warning = optarg; 420 if (tmp.error != MP_PARSING_SUCCES) {
315 break; 421 die(STATE_UNKNOWN, "failed to parse critical query threshold");
422 }
423
424 result.config.qthresholds =
425 mp_thresholds_set_crit(result.config.qthresholds, tmp.range);
426
427 } break;
428 case 'W': /* warning query threshold */ {
429 mp_range_parsed tmp = mp_parse_range_string(optarg);
430 if (tmp.error != MP_PARSING_SUCCES) {
431 die(STATE_UNKNOWN, "failed to parse warning query threshold");
432 }
433 result.config.qthresholds =
434 mp_thresholds_set_warn(result.config.qthresholds, tmp.range);
435 } break;
316 case 'H': /* host */ 436 case 'H': /* host */
317 if ((*optarg != '/') && (!is_host(optarg))) { 437 if ((*optarg != '/') && (!is_host(optarg))) {
318 usage2(_("Invalid hostname/address"), optarg); 438 usage2(_("Invalid hostname/address"), optarg);
@@ -363,9 +483,6 @@ check_pgsql_config_wrapper process_arguments(int argc, char **argv) {
363 } 483 }
364 } 484 }
365 485
366 set_thresholds(&result.config.qthresholds, result.config.query_warning,
367 result.config.query_critical);
368
369 return result; 486 return result;
370} 487}
371 488
@@ -393,7 +510,7 @@ should be added.</para>
393-@@ 510-@@
394******************************************************************************/ 511******************************************************************************/
395 512
396bool is_pg_logname(char *username) { 513static bool is_pg_logname(char *username) {
397 if (strlen(username) > NAMEDATALEN - 1) { 514 if (strlen(username) > NAMEDATALEN - 1) {
398 return (false); 515 return (false);
399 } 516 }
@@ -447,12 +564,13 @@ void print_help(void) {
447 printf(" %s\n", "--queryname=STRING"); 564 printf(" %s\n", "--queryname=STRING");
448 printf(" %s\n", _("A name for the query, this string is used instead of the query")); 565 printf(" %s\n", _("A name for the query, this string is used instead of the query"));
449 printf(" %s\n", _("in the long output of the plugin")); 566 printf(" %s\n", _("in the long output of the plugin"));
450 printf(" %s\n", "-W, --query-warning=RANGE"); 567 printf(" %s\n", "-W, --query_warning=RANGE");
451 printf(" %s\n", _("SQL query value to result in warning status (double)")); 568 printf(" %s\n", _("SQL query value to result in warning status (double)"));
452 printf(" %s\n", "-C, --query-critical=RANGE"); 569 printf(" %s\n", "-C, --query_critical=RANGE");
453 printf(" %s\n", _("SQL query value to result in critical status (double)")); 570 printf(" %s\n", _("SQL query value to result in critical status (double)"));
454 571
455 printf(UT_VERBOSE); 572 printf(UT_VERBOSE);
573 printf(UT_OUTPUT_FORMAT);
456 574
457 printf("\n"); 575 printf("\n");
458 printf(" %s\n", _("All parameters are optional.")); 576 printf(" %s\n", _("All parameters are optional."));
@@ -509,33 +627,44 @@ void print_usage(void) {
509 "[-q <query>] [-C <critical query range>] [-W <warning query range>]\n"); 627 "[-q <query>] [-C <critical query range>] [-W <warning query range>]\n");
510} 628}
511 629
512mp_state_enum do_query(PGconn *conn, char *query, const char pgqueryname[], thresholds *qthresholds, 630static do_query_wrapper do_query(PGconn *conn, char *query) {
513 char *query_warning, char *query_critical) {
514 if (verbose) { 631 if (verbose) {
515 printf("Executing SQL query \"%s\".\n", query); 632 printf("Executing SQL query \"%s\".\n", query);
516 } 633 }
517 PGresult *res = PQexec(conn, query); 634 PGresult *res = PQexec(conn, query);
518 635
636 do_query_wrapper result = {
637 .error_code = QUERY_OK,
638 };
639
519 if (PGRES_TUPLES_OK != PQresultStatus(res)) { 640 if (PGRES_TUPLES_OK != PQresultStatus(res)) {
520 printf(_("QUERY %s - %s: %s.\n"), _("CRITICAL"), _("Error with query"), 641 // TODO
521 PQerrorMessage(conn)); 642 // printf(_("QUERY %s - %s: %s.\n"), _("CRITICAL"), _("Error with query"),
522 return STATE_CRITICAL; 643 // PQerrorMessage(conn));
644 result.error_code = ERROR_WITH_QUERY;
645 return result;
523 } 646 }
524 647
525 if (PQntuples(res) < 1) { 648 if (PQntuples(res) < 1) {
526 printf("QUERY %s - %s.\n", _("WARNING"), _("No rows returned")); 649 // TODO
527 return STATE_WARNING; 650 // printf("QUERY %s - %s.\n", _("WARNING"), _("No rows returned"));
651 result.error_code = NO_ROWS_RETURNED;
652 return result;
528 } 653 }
529 654
530 if (PQnfields(res) < 1) { 655 if (PQnfields(res) < 1) {
531 printf("QUERY %s - %s.\n", _("WARNING"), _("No columns returned")); 656 // TODO
532 return STATE_WARNING; 657 // printf("QUERY %s - %s.\n", _("WARNING"), _("No columns returned"));
658 result.error_code = NO_COLUMNS_RETURNED;
659 return result;
533 } 660 }
534 661
535 char *val_str = PQgetvalue(res, 0, 0); 662 char *val_str = PQgetvalue(res, 0, 0);
536 if (!val_str) { 663 if (!val_str) {
537 printf("QUERY %s - %s.\n", _("CRITICAL"), _("No data returned")); 664 // TODO
538 return STATE_CRITICAL; 665 // printf("QUERY %s - %s.\n", _("CRITICAL"), _("No data returned"));
666 result.error_code = NO_DATA_RETURNED;
667 return result;
539 } 668 }
540 669
541 char *endptr = NULL; 670 char *endptr = NULL;
@@ -545,8 +674,10 @@ mp_state_enum do_query(PGconn *conn, char *query, const char pgqueryname[], thre
545 } 674 }
546 675
547 if (endptr == val_str) { 676 if (endptr == val_str) {
548 printf("QUERY %s - %s: %s\n", _("CRITICAL"), _("Is not a numeric"), val_str); 677 // TODO
549 return STATE_CRITICAL; 678 // printf("QUERY %s - %s: %s\n", _("CRITICAL"), _("Is not a numeric"), val_str);
679 result.error_code = RESULT_IS_NOT_NUMERIC;
680 return result;
550 } 681 }
551 682
552 if ((endptr != NULL) && (*endptr != '\0')) { 683 if ((endptr != NULL) && (*endptr != '\0')) {
@@ -555,24 +686,7 @@ mp_state_enum do_query(PGconn *conn, char *query, const char pgqueryname[], thre
555 } 686 }
556 } 687 }
557 688
558 mp_state_enum my_status = get_status(value, qthresholds); 689 result.numerical_result = value;
559 printf("QUERY %s - ", (my_status == STATE_OK) ? _("OK")
560 : (my_status == STATE_WARNING) ? _("WARNING")
561 : (my_status == STATE_CRITICAL) ? _("CRITICAL")
562 : _("UNKNOWN"));
563 if (pgqueryname) {
564 printf(_("%s returned %f"), pgqueryname, value);
565 } else {
566 printf(_("'%s' returned %f"), query, value);
567 }
568 690
569 printf("|query=%f;%s;%s;;\n", value, query_warning ? query_warning : "", 691 return result;
570 query_critical ? query_critical : "");
571 if (PQnfields(res) > 1) {
572 char *extra_info = PQgetvalue(res, 0, 1);
573 if (extra_info != NULL) {
574 printf("Extra Info: %s\n", extra_info);
575 }
576 }
577 return my_status;
578} 692}
diff --git a/plugins/check_pgsql.d/config.h b/plugins/check_pgsql.d/config.h
index 2d4b8b89..7cf0637b 100644
--- a/plugins/check_pgsql.d/config.h
+++ b/plugins/check_pgsql.d/config.h
@@ -1,6 +1,8 @@
1#pragma once 1#pragma once
2 2
3#include "../../config.h" 3#include "../../config.h"
4#include "output.h"
5#include "perfdata.h"
4#include "thresholds.h" 6#include "thresholds.h"
5#include <stddef.h> 7#include <stddef.h>
6#include <pg_config_manual.h> 8#include <pg_config_manual.h>
@@ -24,11 +26,11 @@ typedef struct {
24 char *pgquery; 26 char *pgquery;
25 char *pgqueryname; 27 char *pgqueryname;
26 28
27 double twarn; 29 mp_thresholds time_thresholds;
28 double tcrit; 30 mp_thresholds qthresholds;
29 thresholds *qthresholds; 31
30 char *query_warning; 32 bool output_format_is_set;
31 char *query_critical; 33 mp_output_format output_format;
32} check_pgsql_config; 34} check_pgsql_config;
33 35
34/* begin, by setting the parameters for a backend connection if the 36/* begin, by setting the parameters for a backend connection if the
@@ -51,11 +53,18 @@ check_pgsql_config check_pgsql_config_init() {
51 .pgquery = NULL, 53 .pgquery = NULL,
52 .pgqueryname = NULL, 54 .pgqueryname = NULL,
53 55
54 .twarn = (double)DEFAULT_WARN, 56 .time_thresholds = mp_thresholds_init(),
55 .tcrit = (double)DEFAULT_CRIT, 57 .qthresholds = mp_thresholds_init(),
56 .qthresholds = NULL, 58
57 .query_warning = NULL, 59 .output_format_is_set = false,
58 .query_critical = NULL,
59 }; 60 };
61
62 mp_range tmp_range = mp_range_init();
63 tmp_range = mp_range_set_end(tmp_range, mp_create_pd_value(DEFAULT_WARN));
64 tmp.time_thresholds = mp_thresholds_set_warn(tmp.time_thresholds, tmp_range);
65
66 tmp_range = mp_range_set_end(tmp_range, mp_create_pd_value(DEFAULT_CRIT));
67 tmp.time_thresholds = mp_thresholds_set_crit(tmp.time_thresholds, tmp_range);
68
60 return tmp; 69 return tmp;
61} 70}
diff --git a/plugins/check_snmp.c b/plugins/check_snmp.c
index a5a7afe8..f470d222 100644
--- a/plugins/check_snmp.c
+++ b/plugins/check_snmp.c
@@ -41,8 +41,6 @@ const char *email = "devel@monitoring-plugins.org";
41#include "../lib/output.h" 41#include "../lib/output.h"
42#include "check_snmp.d/check_snmp_helpers.h" 42#include "check_snmp.d/check_snmp_helpers.h"
43 43
44#include <bits/getopt_core.h>
45#include <bits/getopt_ext.h>
46#include <strings.h> 44#include <strings.h>
47#include <stdint.h> 45#include <stdint.h>
48 46
diff --git a/plugins/check_swap.d/swap.c b/plugins/check_swap.d/swap.c
index 5b654197..58213a3c 100644
--- a/plugins/check_swap.d/swap.c
+++ b/plugins/check_swap.d/swap.c
@@ -52,10 +52,10 @@ swap_result get_swap_data(swap_config config) {
52 } 52 }
53# else // HAVE_SWAP 53# else // HAVE_SWAP
54# ifdef CHECK_SWAP_SWAPCTL_SVR4 54# ifdef CHECK_SWAP_SWAPCTL_SVR4
55 return getSwapFromSwapctl_SRV4(); 55 return getSwapFromSwapctl_SRV4(config);
56# else // CHECK_SWAP_SWAPCTL_SVR4 56# else // CHECK_SWAP_SWAPCTL_SVR4
57# ifdef CHECK_SWAP_SWAPCTL_BSD 57# ifdef CHECK_SWAP_SWAPCTL_BSD
58 return getSwapFromSwapctl_BSD(); 58 return getSwapFromSwapctl_BSD(config);
59# else // CHECK_SWAP_SWAPCTL_BSD 59# else // CHECK_SWAP_SWAPCTL_BSD
60# error No way found to retrieve swap 60# error No way found to retrieve swap
61# endif /* CHECK_SWAP_SWAPCTL_BSD */ 61# endif /* CHECK_SWAP_SWAPCTL_BSD */
diff --git a/plugins/check_users.c b/plugins/check_users.c
index 2340eae4..3b2e265e 100644
--- a/plugins/check_users.c
+++ b/plugins/check_users.c
@@ -51,8 +51,6 @@ const char *email = "devel@monitoring-plugins.org";
51# define ERROR -1 51# define ERROR -1
52#elif HAVE_UTMPX_H 52#elif HAVE_UTMPX_H
53# include <utmpx.h> 53# include <utmpx.h>
54#else
55# include "popen.h"
56#endif 54#endif
57 55
58#ifdef HAVE_LIBSYSTEMD 56#ifdef HAVE_LIBSYSTEMD
diff --git a/plugins/check_users.d/users.c b/plugins/check_users.d/users.c
index f37819b1..a08f79c5 100644
--- a/plugins/check_users.d/users.c
+++ b/plugins/check_users.d/users.c
@@ -113,8 +113,8 @@ get_num_of_users_wrapper get_num_of_users_utmp() {
113# ifndef HAVE_UTMPX_H 113# ifndef HAVE_UTMPX_H
114// Fall back option here for the others (probably still not on windows) 114// Fall back option here for the others (probably still not on windows)
115 115
116# include "../popen.h"
117# include "../common.h" 116# include "../common.h"
117# include "../popen.h"
118# include "../utils.h" 118# include "../utils.h"
119 119
120get_num_of_users_wrapper get_num_of_users_who_command() { 120get_num_of_users_wrapper get_num_of_users_who_command() {
diff --git a/plugins/t/check_apt.t b/plugins/t/check_apt.t
index 430eb53e..736bc2f2 100644
--- a/plugins/t/check_apt.t
+++ b/plugins/t/check_apt.t
@@ -5,6 +5,7 @@
5# 5#
6 6
7use strict; 7use strict;
8use warnings;
8use Test::More; 9use Test::More;
9use NPTest; 10use NPTest;
10 11
@@ -12,18 +13,18 @@ sub make_result_regexp {
12 my ($warning, $critical) = @_; 13 my ($warning, $critical) = @_;
13 my $status; 14 my $status;
14 if ($warning == 0 && $critical == 0) { 15 if ($warning == 0 && $critical == 0) {
15 $status = "OK"; 16 $status = "OK";
16 } elsif ($critical == 0) { 17 } elsif ($critical == 0) {
17 $status = "WARNING"; 18 $status = "WARNING";
18 } else { 19 } else {
19 $status = "CRITICAL"; 20 $status = "CRITICAL";
20 } 21 }
21 return sprintf('/^APT %s: %d packages available for upgrade \(%d critical updates\)\. |available_upgrades=%d;;;0 critical_updates=%d;;;0$/', 22 return sprintf('/.*[%s].*Updates available: %d.*Security updates available: %d.*\'available_upgrades\'=%d;;; \'critical_updates\'=%d;;; /s',
22 $status, $warning, $critical, $warning, $critical); 23 $status, $warning, $critical, $warning, $critical);
23} 24}
24 25
25if (-x "./check_apt") { 26if (-x "./check_apt") {
26 plan tests => 36; 27 plan tests => 35;
27} else { 28} else {
28 plan skip_all => "No check_apt compiled"; 29 plan skip_all => "No check_apt compiled";
29} 30}
@@ -42,7 +43,8 @@ like( $result->output, make_result_regexp(13, 0), "Output correct" );
42 43
43$result = NPTest->testCmd( sprintf($testfile_command, "-o", "debian2") ); 44$result = NPTest->testCmd( sprintf($testfile_command, "-o", "debian2") );
44is( $result->return_code, 0, "Debian apt output, no critical" ); 45is( $result->return_code, 0, "Debian apt output, no critical" );
45like( $result->output, make_result_regexp(13, 0), "Output correct" ); 46# this test does not work, since -o was given
47# like( $result->output, make_result_regexp(13, 0), "Output correct" );
46 48
47$result = NPTest->testCmd( sprintf($testfile_command, "", "debian3") ); 49$result = NPTest->testCmd( sprintf($testfile_command, "", "debian3") );
48is( $result->return_code, 2, "Debian apt output, some critical" ); 50is( $result->return_code, 2, "Debian apt output, some critical" );