summaryrefslogtreecommitdiffstats
path: root/plugins/check_pgsql.c
diff options
context:
space:
mode:
authorLorenz Kästle <12514511+RincewindsHat@users.noreply.github.com>2025-11-04 14:17:16 +0100
committerLorenz Kästle <12514511+RincewindsHat@users.noreply.github.com>2025-11-05 13:54:04 +0100
commitba6f90373333246bce196dbba494fa4af0bd9253 (patch)
tree8a5ee8d27269fc55d8196baab25b10412f5486d1 /plugins/check_pgsql.c
parent4191aa46a27cd37de40b0d8a5b8cd19a1c00e364 (diff)
downloadmonitoring-plugins-ba6f90373333246bce196dbba494fa4af0bd9253.tar.gz
check_pgsql: implement modern output
Diffstat (limited to 'plugins/check_pgsql.c')
-rw-r--r--plugins/check_pgsql.c273
1 files changed, 193 insertions, 80 deletions
diff --git a/plugins/check_pgsql.c b/plugins/check_pgsql.c
index e74c567d..bbabd062 100644
--- a/plugins/check_pgsql.c
+++ b/plugins/check_pgsql.c
@@ -28,6 +28,8 @@
28 * 28 *
29 *****************************************************************************/ 29 *****************************************************************************/
30 30
31#include "output.h"
32#include "perfdata.h"
31#include "states.h" 33#include "states.h"
32#include "common.h" 34#include "common.h"
33#include "utils.h" 35#include "utils.h"
@@ -46,8 +48,9 @@ const char *email = "devel@monitoring-plugins.org";
46 48
47/* return the PSQL server version as a 3-tuple */ 49/* return the PSQL server version as a 3-tuple */
48#define PSQL_SERVER_VERSION3(server_version) \ 50#define PSQL_SERVER_VERSION3(server_version) \
49 (server_version) / 10000, (server_version) / 100 - (int)((server_version) / 10000) * 100, \ 51 ((server_version) / 10000), \
50 (server_version) - (int)((server_version) / 100) * 100 52 (((server_version) / 100) - (int)(((server_version) / 10000) * 100)), \
53 (server_version) - (int)(((server_version) / 100) * 100)
51/* return true if the given host is a UNIX domain socket */ 54/* return true if the given host is a UNIX domain socket */
52#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)))
53/* 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 */
@@ -63,9 +66,21 @@ static check_pgsql_config_wrapper process_arguments(int /*argc*/, char ** /*argv
63 66
64static void print_help(void); 67static void print_help(void);
65static bool is_pg_logname(char * /*username*/); 68static bool is_pg_logname(char * /*username*/);
66static mp_state_enum do_query(PGconn * /*conn*/, char * /*query*/, const char /*pgqueryname*/[], 69
67 thresholds * /*qthresholds*/, char * /*query_warning*/, 70typedef enum {
68 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*/);
69void print_usage(void); 84void print_usage(void);
70 85
71static int verbose = 0; 86static int verbose = 0;
@@ -196,21 +211,41 @@ int main(int argc, char **argv) {
196 if (verbose) { 211 if (verbose) {
197 printf("Verifying connection\n"); 212 printf("Verifying connection\n");
198 } 213 }
214
215 mp_check overall = mp_check_init();
216
217 mp_subcheck sc_connection = mp_subcheck_init();
218
199 if (PQstatus(conn) == CONNECTION_BAD) { 219 if (PQstatus(conn) == CONNECTION_BAD) {
200 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));
201 PQfinish(conn); 223 PQfinish(conn);
202 return STATE_CRITICAL; 224 mp_add_subcheck_to_check(&overall, sc_connection);
203 } 225 mp_exit(overall);
204
205 mp_state_enum status = STATE_UNKNOWN;
206 if (elapsed_time > config.tcrit) {
207 status = STATE_CRITICAL;
208 } else if (elapsed_time > config.twarn) {
209 status = STATE_WARNING;
210 } else { 226 } else {
211 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);
212 } 230 }
213 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
214 if (verbose) { 249 if (verbose) {
215 char *server_host = PQhost(conn); 250 char *server_host = PQhost(conn);
216 int server_version = PQserverVersion(conn); 251 int server_version = PQserverVersion(conn);
@@ -222,25 +257,81 @@ int main(int argc, char **argv) {
222 PSQL_SERVER_VERSION3(server_version), PQprotocolVersion(conn), PQbackendPID(conn)); 257 PSQL_SERVER_VERSION3(server_version), PQprotocolVersion(conn), PQbackendPID(conn));
223 } 258 }
224 259
225 printf(_(" %s - database %s (%f sec.)|%s\n"), state_text(status), config.dbName, elapsed_time,
226 fperfdata("time", elapsed_time, "s", (config.twarn > 0.0), config.twarn,
227 (config.tcrit > 0.0), config.tcrit, true, 0, false, 0));
228
229 mp_state_enum query_status = STATE_UNKNOWN;
230 if (config.pgquery) { 260 if (config.pgquery) {
231 query_status = do_query(conn, config.pgquery, config.pgqueryname, config.qthresholds, 261 mp_subcheck sc_query = mp_subcheck_init();
232 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 succesful 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 withing 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);
233 } 323 }
234 324
235 if (verbose) { 325 if (verbose) {
236 printf("Closing connection\n"); 326 printf("Closing connection\n");
237 } 327 }
238 PQfinish(conn); 328 PQfinish(conn);
239 return (config.pgquery && query_status > status) ? query_status : status; 329
330 mp_exit(overall);
240} 331}
241 332
242/* process command-line arguments */ 333/* process command-line arguments */
243check_pgsql_config_wrapper process_arguments(int argc, char **argv) { 334static check_pgsql_config_wrapper process_arguments(int argc, char **argv) {
244 335
245 enum { 336 enum {
246 OPTID_QUERYNAME = CHAR_MAX + 1, 337 OPTID_QUERYNAME = CHAR_MAX + 1,
@@ -295,26 +386,40 @@ check_pgsql_config_wrapper process_arguments(int argc, char **argv) {
295 timeout_interval = atoi(optarg); 386 timeout_interval = atoi(optarg);
296 } 387 }
297 break; 388 break;
298 case 'c': /* critical time threshold */ 389 case 'c': /* critical time threshold */ {
299 if (!is_nonnegative(optarg)) { 390 mp_range_parsed tmp = mp_parse_range_string(optarg);
300 usage2(_("Critical threshold must be a positive integer"), optarg); 391 if (tmp.error != MP_PARSING_SUCCES) {
301 } else { 392 die(STATE_UNKNOWN, "failed to parse critical time threshold");
302 result.config.tcrit = strtod(optarg, NULL);
303 } 393 }
304 break; 394 result.config.time_thresholds =
305 case 'w': /* warning time threshold */ 395 mp_thresholds_set_crit(result.config.time_thresholds, tmp.range);
306 if (!is_nonnegative(optarg)) { 396 } break;
307 usage2(_("Warning threshold must be a positive integer"), optarg); 397 case 'w': /* warning time threshold */ {
308 } else { 398 mp_range_parsed tmp = mp_parse_range_string(optarg);
309 result.config.twarn = strtod(optarg, NULL); 399 if (tmp.error != MP_PARSING_SUCCES) {
400 die(STATE_UNKNOWN, "failed to parse warning time threshold");
310 } 401 }
311 break; 402 result.config.time_thresholds =
312 case 'C': /* critical query threshold */ 403 mp_thresholds_set_warn(result.config.time_thresholds, tmp.range);
313 result.config.query_critical = optarg; 404 } break;
314 break; 405 case 'C': /* critical query threshold */ {
315 case 'W': /* warning query threshold */ 406 mp_range_parsed tmp = mp_parse_range_string(optarg);
316 result.config.query_warning = optarg; 407 if (tmp.error != MP_PARSING_SUCCES) {
317 break; 408 die(STATE_UNKNOWN, "failed to parse critical query threshold");
409 }
410
411 result.config.qthresholds =
412 mp_thresholds_set_crit(result.config.qthresholds, tmp.range);
413
414 } break;
415 case 'W': /* warning query threshold */ {
416 mp_range_parsed tmp = mp_parse_range_string(optarg);
417 if (tmp.error != MP_PARSING_SUCCES) {
418 die(STATE_UNKNOWN, "failed to parse warning query threshold");
419 }
420 result.config.qthresholds =
421 mp_thresholds_set_warn(result.config.qthresholds, tmp.range);
422 } break;
318 case 'H': /* host */ 423 case 'H': /* host */
319 if ((*optarg != '/') && (!is_host(optarg))) { 424 if ((*optarg != '/') && (!is_host(optarg))) {
320 usage2(_("Invalid hostname/address"), optarg); 425 usage2(_("Invalid hostname/address"), optarg);
@@ -365,9 +470,6 @@ check_pgsql_config_wrapper process_arguments(int argc, char **argv) {
365 } 470 }
366 } 471 }
367 472
368 set_thresholds(&result.config.qthresholds, result.config.query_warning,
369 result.config.query_critical);
370
371 return result; 473 return result;
372} 474}
373 475
@@ -395,7 +497,7 @@ should be added.</para>
395-@@ 497-@@
396******************************************************************************/ 498******************************************************************************/
397 499
398bool is_pg_logname(char *username) { 500static bool is_pg_logname(char *username) {
399 if (strlen(username) > NAMEDATALEN - 1) { 501 if (strlen(username) > NAMEDATALEN - 1) {
400 return (false); 502 return (false);
401 } 503 }
@@ -449,9 +551,9 @@ void print_help(void) {
449 printf(" %s\n", "--queryname=STRING"); 551 printf(" %s\n", "--queryname=STRING");
450 printf(" %s\n", _("A name for the query, this string is used instead of the query")); 552 printf(" %s\n", _("A name for the query, this string is used instead of the query"));
451 printf(" %s\n", _("in the long output of the plugin")); 553 printf(" %s\n", _("in the long output of the plugin"));
452 printf(" %s\n", "-W, --query-warning=RANGE"); 554 printf(" %s\n", "-W, --query_warning=RANGE");
453 printf(" %s\n", _("SQL query value to result in warning status (double)")); 555 printf(" %s\n", _("SQL query value to result in warning status (double)"));
454 printf(" %s\n", "-C, --query-critical=RANGE"); 556 printf(" %s\n", "-C, --query_critical=RANGE");
455 printf(" %s\n", _("SQL query value to result in critical status (double)")); 557 printf(" %s\n", _("SQL query value to result in critical status (double)"));
456 558
457 printf(UT_VERBOSE); 559 printf(UT_VERBOSE);
@@ -511,33 +613,44 @@ void print_usage(void) {
511 "[-q <query>] [-C <critical query range>] [-W <warning query range>]\n"); 613 "[-q <query>] [-C <critical query range>] [-W <warning query range>]\n");
512} 614}
513 615
514mp_state_enum do_query(PGconn *conn, char *query, const char pgqueryname[], thresholds *qthresholds, 616static do_query_wrapper do_query(PGconn *conn, char *query) {
515 char *query_warning, char *query_critical) {
516 if (verbose) { 617 if (verbose) {
517 printf("Executing SQL query \"%s\".\n", query); 618 printf("Executing SQL query \"%s\".\n", query);
518 } 619 }
519 PGresult *res = PQexec(conn, query); 620 PGresult *res = PQexec(conn, query);
520 621
622 do_query_wrapper result = {
623 .error_code = QUERY_OK,
624 };
625
521 if (PGRES_TUPLES_OK != PQresultStatus(res)) { 626 if (PGRES_TUPLES_OK != PQresultStatus(res)) {
522 printf(_("QUERY %s - %s: %s.\n"), _("CRITICAL"), _("Error with query"), 627 // TODO
523 PQerrorMessage(conn)); 628 // printf(_("QUERY %s - %s: %s.\n"), _("CRITICAL"), _("Error with query"),
524 return STATE_CRITICAL; 629 // PQerrorMessage(conn));
630 result.error_code = ERROR_WITH_QUERY;
631 return result;
525 } 632 }
526 633
527 if (PQntuples(res) < 1) { 634 if (PQntuples(res) < 1) {
528 printf("QUERY %s - %s.\n", _("WARNING"), _("No rows returned")); 635 // TODO
529 return STATE_WARNING; 636 // printf("QUERY %s - %s.\n", _("WARNING"), _("No rows returned"));
637 result.error_code = NO_ROWS_RETURNED;
638 return result;
530 } 639 }
531 640
532 if (PQnfields(res) < 1) { 641 if (PQnfields(res) < 1) {
533 printf("QUERY %s - %s.\n", _("WARNING"), _("No columns returned")); 642 // TODO
534 return STATE_WARNING; 643 // printf("QUERY %s - %s.\n", _("WARNING"), _("No columns returned"));
644 result.error_code = NO_COLUMNS_RETURNED;
645 return result;
535 } 646 }
536 647
537 char *val_str = PQgetvalue(res, 0, 0); 648 char *val_str = PQgetvalue(res, 0, 0);
538 if (!val_str) { 649 if (!val_str) {
539 printf("QUERY %s - %s.\n", _("CRITICAL"), _("No data returned")); 650 // TODO
540 return STATE_CRITICAL; 651 // printf("QUERY %s - %s.\n", _("CRITICAL"), _("No data returned"));
652 result.error_code = NO_DATA_RETURNED;
653 return result;
541 } 654 }
542 655
543 char *endptr = NULL; 656 char *endptr = NULL;
@@ -547,8 +660,10 @@ mp_state_enum do_query(PGconn *conn, char *query, const char pgqueryname[], thre
547 } 660 }
548 661
549 if (endptr == val_str) { 662 if (endptr == val_str) {
550 printf("QUERY %s - %s: %s\n", _("CRITICAL"), _("Is not a numeric"), val_str); 663 // TODO
551 return STATE_CRITICAL; 664 // printf("QUERY %s - %s: %s\n", _("CRITICAL"), _("Is not a numeric"), val_str);
665 result.error_code = RESULT_IS_NOT_NUMERIC;
666 return result;
552 } 667 }
553 668
554 if ((endptr != NULL) && (*endptr != '\0')) { 669 if ((endptr != NULL) && (*endptr != '\0')) {
@@ -557,24 +672,22 @@ mp_state_enum do_query(PGconn *conn, char *query, const char pgqueryname[], thre
557 } 672 }
558 } 673 }
559 674
560 mp_state_enum my_status = get_status(value, qthresholds); 675 result.numerical_result = value;
561 printf("QUERY %s - ", (my_status == STATE_OK) ? _("OK")
562 : (my_status == STATE_WARNING) ? _("WARNING")
563 : (my_status == STATE_CRITICAL) ? _("CRITICAL")
564 : _("UNKNOWN"));
565 if (pgqueryname) {
566 printf(_("%s returned %f"), pgqueryname, value);
567 } else {
568 printf(_("'%s' returned %f"), query, value);
569 }
570 676
571 printf("|query=%f;%s;%s;;\n", value, query_warning ? query_warning : "", 677 // if (pgqueryname) {
572 query_critical ? query_critical : ""); 678 // printf(_("%s returned %f"), pgqueryname, value);
573 if (PQnfields(res) > 1) { 679 // } else {
574 char *extra_info = PQgetvalue(res, 0, 1); 680 // printf(_("'%s' returned %f"), query, value);
575 if (extra_info != NULL) { 681 // }
576 printf("Extra Info: %s\n", extra_info); 682
577 } 683 // printf("|query=%f;%s;%s;;\n", value, query_warning ? query_warning : "",
578 } 684 // query_critical ? query_critical : "");
579 return my_status; 685 // if (PQnfields(res) > 1) {
686 // char *extra_info = PQgetvalue(res, 0, 1);
687 // if (extra_info != NULL) {
688 // printf("Extra Info: %s\n", extra_info);
689 // }
690 // }
691
692 return result;
580} 693}