diff options
Diffstat (limited to 'plugins/check_pgsql.c')
| -rw-r--r-- | plugins/check_pgsql.c | 497 |
1 files changed, 322 insertions, 175 deletions
diff --git a/plugins/check_pgsql.c b/plugins/check_pgsql.c index 6613634d..0ce75e0a 100644 --- a/plugins/check_pgsql.c +++ b/plugins/check_pgsql.c | |||
| @@ -28,62 +28,63 @@ | |||
| 28 | * | 28 | * |
| 29 | *****************************************************************************/ | 29 | *****************************************************************************/ |
| 30 | 30 | ||
| 31 | const char *progname = "check_pgsql"; | 31 | #include "output.h" |
| 32 | const char *copyright = "1999-2024"; | 32 | #include "perfdata.h" |
| 33 | const char *email = "devel@monitoring-plugins.org"; | 33 | #include "states.h" |
| 34 | |||
| 35 | #include "common.h" | 34 | #include "common.h" |
| 36 | #include "utils.h" | 35 | #include "utils.h" |
| 37 | #include "utils_cmd.h" | 36 | #include "utils_cmd.h" |
| 38 | 37 | #include "check_pgsql.d/config.h" | |
| 38 | #include "thresholds.h" | ||
| 39 | #include "netutils.h" | 39 | #include "netutils.h" |
| 40 | #include <libpq-fe.h> | 40 | #include <libpq-fe.h> |
| 41 | #include <pg_config_manual.h> | 41 | #include <pg_config_manual.h> |
| 42 | 42 | ||
| 43 | #define DEFAULT_DB "template1" | 43 | const char *progname = "check_pgsql"; |
| 44 | const char *copyright = "1999-2024"; | ||
| 45 | const char *email = "devel@monitoring-plugins.org"; | ||
| 46 | |||
| 44 | #define DEFAULT_HOST "127.0.0.1" | 47 | #define DEFAULT_HOST "127.0.0.1" |
| 45 | 48 | ||
| 46 | /* return the PSQL server version as a 3-tuple */ | 49 | /* return the PSQL server version as a 3-tuple */ |
| 47 | #define PSQL_SERVER_VERSION3(server_version) \ | 50 | #define PSQL_SERVER_VERSION3(server_version) \ |
| 48 | (server_version) / 10000, (server_version) / 100 - (int)((server_version) / 10000) * 100, \ | 51 | ((server_version) / 10000), \ |
| 49 | (server_version) - (int)((server_version) / 100) * 100 | 52 | (((server_version) / 100) - (int)(((server_version) / 10000) * 100)), \ |
| 53 | (server_version) - (int)(((server_version) / 100) * 100) | ||
| 50 | /* return true if the given host is a UNIX domain socket */ | 54 | /* return true if the given host is a UNIX domain socket */ |
| 51 | #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))) |
| 52 | /* 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 */ |
| 53 | #define PSQL_SOCKET3(host, port) \ | 57 | #define PSQL_SOCKET3(host, port) \ |
| 54 | ((NULL == (host)) || ('\0' == *(host))) ? DEFAULT_PGSOCKET_DIR : host, PSQL_IS_UNIX_DOMAIN_SOCKET(host) ? "/.s.PGSQL." : ":", port | 58 | ((NULL == (host)) || ('\0' == *(host))) ? DEFAULT_PGSOCKET_DIR : host, \ |
| 59 | PSQL_IS_UNIX_DOMAIN_SOCKET(host) ? "/.s.PGSQL." : ":", port | ||
| 55 | 60 | ||
| 56 | enum { | 61 | typedef struct { |
| 57 | DEFAULT_PORT = 5432, | 62 | int errorcode; |
| 58 | DEFAULT_WARN = 2, | 63 | check_pgsql_config config; |
| 59 | DEFAULT_CRIT = 8 | 64 | } check_pgsql_config_wrapper; |
| 60 | }; | 65 | static check_pgsql_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/); |
| 61 | 66 | ||
| 62 | static int process_arguments(int /*argc*/, char ** /*argv*/); | ||
| 63 | static void print_help(void); | 67 | static void print_help(void); |
| 64 | static bool is_pg_logname(char * /*username*/); | 68 | static bool is_pg_logname(char * /*username*/); |
| 65 | static int do_query(PGconn * /*conn*/, char * /*query*/); | 69 | |
| 70 | typedef enum { | ||
| 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 | |||
| 79 | typedef struct { | ||
| 80 | do_query_errorcode error_code; | ||
| 81 | double numerical_result; | ||
| 82 | } do_query_wrapper; | ||
| 83 | static do_query_wrapper do_query(PGconn * /*conn*/, char * /*query*/); | ||
| 66 | void print_usage(void); | 84 | void print_usage(void); |
| 67 | 85 | ||
| 68 | static char *pghost = NULL; /* host name of the backend server */ | ||
| 69 | static char *pgport = NULL; /* port of the backend server */ | ||
| 70 | static char *pgoptions = NULL; | ||
| 71 | static char *pgtty = NULL; | ||
| 72 | static char dbName[NAMEDATALEN] = DEFAULT_DB; | ||
| 73 | static char *pguser = NULL; | ||
| 74 | static char *pgpasswd = NULL; | ||
| 75 | static char *pgparams = NULL; | ||
| 76 | static double twarn = (double)DEFAULT_WARN; | ||
| 77 | static double tcrit = (double)DEFAULT_CRIT; | ||
| 78 | static char *pgquery = NULL; | ||
| 79 | static char *pgqueryname = NULL; | ||
| 80 | static char *query_warning = NULL; | ||
| 81 | static char *query_critical = NULL; | ||
| 82 | static thresholds *qthresholds = NULL; | ||
| 83 | static int verbose = 0; | 86 | static int verbose = 0; |
| 84 | 87 | ||
| 85 | #define OPTID_QUERYNAME -1000 | ||
| 86 | |||
| 87 | /****************************************************************************** | 88 | /****************************************************************************** |
| 88 | 89 | ||
| 89 | The (pseudo?)literate programming XML is contained within \@\@\- <XML> \-\@\@ | 90 | The (pseudo?)literate programming XML is contained within \@\@\- <XML> \-\@\@ |
| @@ -139,21 +140,19 @@ int main(int argc, char **argv) { | |||
| 139 | bindtextdomain(PACKAGE, LOCALEDIR); | 140 | bindtextdomain(PACKAGE, LOCALEDIR); |
| 140 | textdomain(PACKAGE); | 141 | textdomain(PACKAGE); |
| 141 | 142 | ||
| 142 | /* begin, by setting the parameters for a backend connection if the | ||
| 143 | * parameters are null, then the system will try to use reasonable | ||
| 144 | * defaults by looking up environment variables or, failing that, | ||
| 145 | * using hardwired constants */ | ||
| 146 | |||
| 147 | pgoptions = NULL; /* special options to start up the backend server */ | ||
| 148 | pgtty = NULL; /* debugging tty for the backend server */ | ||
| 149 | |||
| 150 | /* Parse extra opts if any */ | 143 | /* Parse extra opts if any */ |
| 151 | argv = np_extra_opts(&argc, argv, progname); | 144 | argv = np_extra_opts(&argc, argv, progname); |
| 152 | 145 | ||
| 153 | if (process_arguments(argc, argv) == ERROR) | 146 | check_pgsql_config_wrapper tmp_config = process_arguments(argc, argv); |
| 147 | if (tmp_config.errorcode == ERROR) { | ||
| 154 | usage4(_("Could not parse arguments")); | 148 | usage4(_("Could not parse arguments")); |
| 155 | if (verbose > 2) | 149 | } |
| 156 | printf("Arguments initialized\n"); | 150 | |
| 151 | const check_pgsql_config config = tmp_config.config; | ||
| 152 | |||
| 153 | if (config.output_format_is_set) { | ||
| 154 | mp_set_format(config.output_format); | ||
| 155 | } | ||
| 157 | 156 | ||
| 158 | /* Set signal handling and alarm */ | 157 | /* Set signal handling and alarm */ |
| 159 | if (signal(SIGALRM, timeout_alarm_handler) == SIG_ERR) { | 158 | if (signal(SIGALRM, timeout_alarm_handler) == SIG_ERR) { |
| @@ -162,25 +161,33 @@ int main(int argc, char **argv) { | |||
| 162 | alarm(timeout_interval); | 161 | alarm(timeout_interval); |
| 163 | 162 | ||
| 164 | char *conninfo = NULL; | 163 | char *conninfo = NULL; |
| 165 | if (pgparams) | 164 | if (config.pgparams) { |
| 166 | asprintf(&conninfo, "%s ", pgparams); | 165 | asprintf(&conninfo, "%s ", config.pgparams); |
| 167 | 166 | } | |
| 168 | asprintf(&conninfo, "%sdbname = '%s'", conninfo ? conninfo : "", dbName); | 167 | |
| 169 | if (pghost) | 168 | asprintf(&conninfo, "%sdbname = '%s'", conninfo ? conninfo : "", config.dbName); |
| 170 | asprintf(&conninfo, "%s host = '%s'", conninfo, pghost); | 169 | if (config.pghost) { |
| 171 | if (pgport) | 170 | asprintf(&conninfo, "%s host = '%s'", conninfo, config.pghost); |
| 172 | asprintf(&conninfo, "%s port = '%s'", conninfo, pgport); | 171 | } |
| 173 | if (pgoptions) | 172 | if (config.pgport) { |
| 174 | asprintf(&conninfo, "%s options = '%s'", conninfo, pgoptions); | 173 | asprintf(&conninfo, "%s port = '%s'", conninfo, config.pgport); |
| 174 | } | ||
| 175 | if (config.pgoptions) { | ||
| 176 | asprintf(&conninfo, "%s options = '%s'", conninfo, config.pgoptions); | ||
| 177 | } | ||
| 175 | /* if (pgtty) -- ignored by PQconnectdb */ | 178 | /* if (pgtty) -- ignored by PQconnectdb */ |
| 176 | if (pguser) | 179 | if (config.pguser) { |
| 177 | asprintf(&conninfo, "%s user = '%s'", conninfo, pguser); | 180 | asprintf(&conninfo, "%s user = '%s'", conninfo, config.pguser); |
| 181 | } | ||
| 178 | 182 | ||
| 179 | if (verbose) /* do not include password (see right below) in output */ | 183 | if (verbose) { /* do not include password (see right below) in output */ |
| 180 | printf("Connecting to PostgreSQL using conninfo: %s%s\n", conninfo, pgpasswd ? " password = <hidden>" : ""); | 184 | printf("Connecting to PostgreSQL using conninfo: %s%s\n", conninfo, |
| 185 | config.pgpasswd ? " password = <hidden>" : ""); | ||
| 186 | } | ||
| 181 | 187 | ||
| 182 | if (pgpasswd) | 188 | if (config.pgpasswd) { |
| 183 | asprintf(&conninfo, "%s password = '%s'", conninfo, pgpasswd); | 189 | asprintf(&conninfo, "%s password = '%s'", conninfo, config.pgpasswd); |
| 190 | } | ||
| 184 | 191 | ||
| 185 | /* make a connection to the database */ | 192 | /* make a connection to the database */ |
| 186 | struct timeval start_timeval; | 193 | struct timeval start_timeval; |
| @@ -193,30 +200,52 @@ int main(int argc, char **argv) { | |||
| 193 | --end_timeval.tv_sec; | 200 | --end_timeval.tv_sec; |
| 194 | end_timeval.tv_usec += 1000000; | 201 | end_timeval.tv_usec += 1000000; |
| 195 | } | 202 | } |
| 196 | double elapsed_time = | 203 | double elapsed_time = (double)(end_timeval.tv_sec - start_timeval.tv_sec) + |
| 197 | (double)(end_timeval.tv_sec - start_timeval.tv_sec) + (double)(end_timeval.tv_usec - start_timeval.tv_usec) / 1000000.0; | 204 | ((double)(end_timeval.tv_usec - start_timeval.tv_usec) / 1000000.0); |
| 198 | 205 | ||
| 199 | if (verbose) | 206 | if (verbose) { |
| 200 | printf("Time elapsed: %f\n", elapsed_time); | 207 | printf("Time elapsed: %f\n", elapsed_time); |
| 208 | } | ||
| 201 | 209 | ||
| 202 | /* check to see that the backend connection was successfully made */ | 210 | /* check to see that the backend connection was successfully made */ |
| 203 | if (verbose) | 211 | if (verbose) { |
| 204 | printf("Verifying connection\n"); | 212 | printf("Verifying connection\n"); |
| 205 | if (PQstatus(conn) == CONNECTION_BAD) { | ||
| 206 | printf(_("CRITICAL - no connection to '%s' (%s).\n"), dbName, PQerrorMessage(conn)); | ||
| 207 | PQfinish(conn); | ||
| 208 | return STATE_CRITICAL; | ||
| 209 | } | 213 | } |
| 210 | 214 | ||
| 211 | int status = STATE_UNKNOWN; | 215 | mp_check overall = mp_check_init(); |
| 212 | if (elapsed_time > tcrit) { | 216 | |
| 213 | status = STATE_CRITICAL; | 217 | mp_subcheck sc_connection = mp_subcheck_init(); |
| 214 | } else if (elapsed_time > twarn) { | 218 | |
| 215 | status = STATE_WARNING; | 219 | if (PQstatus(conn) == CONNECTION_BAD) { |
| 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)); | ||
| 223 | PQfinish(conn); | ||
| 224 | mp_add_subcheck_to_check(&overall, sc_connection); | ||
| 225 | mp_exit(overall); | ||
| 216 | } else { | 226 | } else { |
| 217 | 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); | ||
| 218 | } | 230 | } |
| 219 | 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 | |||
| 220 | if (verbose) { | 249 | if (verbose) { |
| 221 | char *server_host = PQhost(conn); | 250 | char *server_host = PQhost(conn); |
| 222 | int server_version = PQserverVersion(conn); | 251 | int server_version = PQserverVersion(conn); |
| @@ -224,25 +253,91 @@ int main(int argc, char **argv) { | |||
| 224 | printf("Successfully connected to database %s (user %s) " | 253 | printf("Successfully connected to database %s (user %s) " |
| 225 | "at server %s%s%s (server version: %d.%d.%d, " | 254 | "at server %s%s%s (server version: %d.%d.%d, " |
| 226 | "protocol version: %d, pid: %d)\n", | 255 | "protocol version: %d, pid: %d)\n", |
| 227 | PQdb(conn), PQuser(conn), PSQL_SOCKET3(server_host, PQport(conn)), PSQL_SERVER_VERSION3(server_version), | 256 | PQdb(conn), PQuser(conn), PSQL_SOCKET3(server_host, PQport(conn)), |
| 228 | PQprotocolVersion(conn), PQbackendPID(conn)); | 257 | PSQL_SERVER_VERSION3(server_version), PQprotocolVersion(conn), PQbackendPID(conn)); |
| 229 | } | 258 | } |
| 230 | 259 | ||
| 231 | printf(_(" %s - database %s (%f sec.)|%s\n"), state_text(status), dbName, elapsed_time, | 260 | if (config.pgquery) { |
| 232 | fperfdata("time", elapsed_time, "s", !!(twarn > 0.0), twarn, !!(tcrit > 0.0), tcrit, true, 0, false, 0)); | 261 | mp_subcheck sc_query = mp_subcheck_init(); |
| 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 | }; | ||
| 233 | 321 | ||
| 234 | int query_status = STATE_UNKNOWN; | 322 | mp_add_subcheck_to_check(&overall, sc_query); |
| 235 | if (pgquery) | 323 | } |
| 236 | query_status = do_query(conn, pgquery); | ||
| 237 | 324 | ||
| 238 | if (verbose) | 325 | if (verbose) { |
| 239 | printf("Closing connection\n"); | 326 | printf("Closing connection\n"); |
| 327 | } | ||
| 240 | PQfinish(conn); | 328 | PQfinish(conn); |
| 241 | return (pgquery && query_status > status) ? query_status : status; | 329 | |
| 330 | mp_exit(overall); | ||
| 242 | } | 331 | } |
| 243 | 332 | ||
| 244 | /* process command-line arguments */ | 333 | /* process command-line arguments */ |
| 245 | int process_arguments(int argc, char **argv) { | 334 | static 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 | |||
| 246 | static struct option longopts[] = {{"help", no_argument, 0, 'h'}, | 341 | static struct option longopts[] = {{"help", no_argument, 0, 'h'}, |
| 247 | {"version", no_argument, 0, 'V'}, | 342 | {"version", no_argument, 0, 'V'}, |
| 248 | {"timeout", required_argument, 0, 't'}, | 343 | {"timeout", required_argument, 0, 't'}, |
| @@ -260,16 +355,35 @@ int process_arguments(int argc, char **argv) { | |||
| 260 | {"query_critical", required_argument, 0, 'C'}, | 355 | {"query_critical", required_argument, 0, 'C'}, |
| 261 | {"query_warning", required_argument, 0, 'W'}, | 356 | {"query_warning", required_argument, 0, 'W'}, |
| 262 | {"verbose", no_argument, 0, 'v'}, | 357 | {"verbose", no_argument, 0, 'v'}, |
| 358 | {"output-format", required_argument, 0, output_format_index}, | ||
| 263 | {0, 0, 0, 0}}; | 359 | {0, 0, 0, 0}}; |
| 264 | 360 | ||
| 361 | check_pgsql_config_wrapper result = { | ||
| 362 | .errorcode = OK, | ||
| 363 | .config = check_pgsql_config_init(), | ||
| 364 | }; | ||
| 365 | |||
| 265 | while (true) { | 366 | while (true) { |
| 266 | int option = 0; | 367 | int option = 0; |
| 267 | int option_char = getopt_long(argc, argv, "hVt:c:w:H:P:d:l:p:a:o:q:C:W:v", longopts, &option); | 368 | int option_char = |
| 369 | getopt_long(argc, argv, "hVt:c:w:H:P:d:l:p:a:o:q:C:W:v", longopts, &option); | ||
| 268 | 370 | ||
| 269 | if (option_char == EOF) | 371 | if (option_char == EOF) { |
| 270 | break; | 372 | break; |
| 373 | } | ||
| 271 | 374 | ||
| 272 | 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 | } | ||
| 273 | case '?': /* usage */ | 387 | case '?': /* usage */ |
| 274 | usage5(); | 388 | usage5(); |
| 275 | case 'h': /* help */ | 389 | case 'h': /* help */ |
| @@ -279,68 +393,89 @@ int process_arguments(int argc, char **argv) { | |||
| 279 | print_revision(progname, NP_VERSION); | 393 | print_revision(progname, NP_VERSION); |
| 280 | exit(STATE_UNKNOWN); | 394 | exit(STATE_UNKNOWN); |
| 281 | case 't': /* timeout period */ | 395 | case 't': /* timeout period */ |
| 282 | if (!is_integer(optarg)) | 396 | if (!is_integer(optarg)) { |
| 283 | usage2(_("Timeout interval must be a positive integer"), optarg); | 397 | usage2(_("Timeout interval must be a positive integer"), optarg); |
| 284 | else | 398 | } else { |
| 285 | timeout_interval = atoi(optarg); | 399 | timeout_interval = atoi(optarg); |
| 400 | } | ||
| 286 | break; | 401 | break; |
| 287 | case 'c': /* critical time threshold */ | 402 | case 'c': /* critical time threshold */ { |
| 288 | if (!is_nonnegative(optarg)) | 403 | mp_range_parsed tmp = mp_parse_range_string(optarg); |
| 289 | usage2(_("Critical threshold must be a positive integer"), optarg); | 404 | if (tmp.error != MP_PARSING_SUCCES) { |
| 290 | else | 405 | die(STATE_UNKNOWN, "failed to parse critical time threshold"); |
| 291 | tcrit = strtod(optarg, NULL); | 406 | } |
| 292 | break; | 407 | result.config.time_thresholds = |
| 293 | case 'w': /* warning time threshold */ | 408 | mp_thresholds_set_crit(result.config.time_thresholds, tmp.range); |
| 294 | if (!is_nonnegative(optarg)) | 409 | } break; |
| 295 | usage2(_("Warning threshold must be a positive integer"), optarg); | 410 | case 'w': /* warning time threshold */ { |
| 296 | else | 411 | mp_range_parsed tmp = mp_parse_range_string(optarg); |
| 297 | twarn = strtod(optarg, NULL); | 412 | if (tmp.error != MP_PARSING_SUCCES) { |
| 298 | break; | 413 | die(STATE_UNKNOWN, "failed to parse warning time threshold"); |
| 299 | case 'C': /* critical query threshold */ | 414 | } |
| 300 | query_critical = optarg; | 415 | result.config.time_thresholds = |
| 301 | break; | 416 | mp_thresholds_set_warn(result.config.time_thresholds, tmp.range); |
| 302 | case 'W': /* warning query threshold */ | 417 | } break; |
| 303 | query_warning = optarg; | 418 | case 'C': /* critical query threshold */ { |
| 304 | break; | 419 | mp_range_parsed tmp = mp_parse_range_string(optarg); |
| 420 | if (tmp.error != MP_PARSING_SUCCES) { | ||
| 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; | ||
| 305 | case 'H': /* host */ | 436 | case 'H': /* host */ |
| 306 | if ((*optarg != '/') && (!is_host(optarg))) | 437 | if ((*optarg != '/') && (!is_host(optarg))) { |
| 307 | usage2(_("Invalid hostname/address"), optarg); | 438 | usage2(_("Invalid hostname/address"), optarg); |
| 308 | else | 439 | } else { |
| 309 | pghost = optarg; | 440 | result.config.pghost = optarg; |
| 441 | } | ||
| 310 | break; | 442 | break; |
| 311 | case 'P': /* port */ | 443 | case 'P': /* port */ |
| 312 | if (!is_integer(optarg)) | 444 | if (!is_integer(optarg)) { |
| 313 | usage2(_("Port must be a positive integer"), optarg); | 445 | usage2(_("Port must be a positive integer"), optarg); |
| 314 | else | 446 | } else { |
| 315 | pgport = optarg; | 447 | result.config.pgport = optarg; |
| 448 | } | ||
| 316 | break; | 449 | break; |
| 317 | case 'd': /* database name */ | 450 | case 'd': /* database name */ |
| 318 | if (strlen(optarg) >= NAMEDATALEN) { | 451 | if (strlen(optarg) >= NAMEDATALEN) { |
| 319 | usage2(_("Database name exceeds the maximum length"), optarg); | 452 | usage2(_("Database name exceeds the maximum length"), optarg); |
| 320 | } | 453 | } |
| 321 | snprintf(dbName, NAMEDATALEN, "%s", optarg); | 454 | snprintf(result.config.dbName, NAMEDATALEN, "%s", optarg); |
| 322 | break; | 455 | break; |
| 323 | case 'l': /* login name */ | 456 | case 'l': /* login name */ |
| 324 | if (!is_pg_logname(optarg)) | 457 | if (!is_pg_logname(optarg)) { |
| 325 | usage2(_("User name is not valid"), optarg); | 458 | usage2(_("User name is not valid"), optarg); |
| 326 | else | 459 | } else { |
| 327 | pguser = optarg; | 460 | result.config.pguser = optarg; |
| 461 | } | ||
| 328 | break; | 462 | break; |
| 329 | case 'p': /* authentication password */ | 463 | case 'p': /* authentication password */ |
| 330 | case 'a': | 464 | case 'a': |
| 331 | pgpasswd = optarg; | 465 | result.config.pgpasswd = optarg; |
| 332 | break; | 466 | break; |
| 333 | case 'o': | 467 | case 'o': |
| 334 | if (pgparams) | 468 | if (result.config.pgparams) { |
| 335 | asprintf(&pgparams, "%s %s", pgparams, optarg); | 469 | asprintf(&result.config.pgparams, "%s %s", result.config.pgparams, optarg); |
| 336 | else | 470 | } else { |
| 337 | asprintf(&pgparams, "%s", optarg); | 471 | asprintf(&result.config.pgparams, "%s", optarg); |
| 472 | } | ||
| 338 | break; | 473 | break; |
| 339 | case 'q': | 474 | case 'q': |
| 340 | pgquery = optarg; | 475 | result.config.pgquery = optarg; |
| 341 | break; | 476 | break; |
| 342 | case OPTID_QUERYNAME: | 477 | case OPTID_QUERYNAME: |
| 343 | pgqueryname = optarg; | 478 | result.config.pgqueryname = optarg; |
| 344 | break; | 479 | break; |
| 345 | case 'v': | 480 | case 'v': |
| 346 | verbose++; | 481 | verbose++; |
| @@ -348,9 +483,7 @@ int process_arguments(int argc, char **argv) { | |||
| 348 | } | 483 | } |
| 349 | } | 484 | } |
| 350 | 485 | ||
| 351 | set_thresholds(&qthresholds, query_warning, query_critical); | 486 | return result; |
| 352 | |||
| 353 | return OK; | ||
| 354 | } | 487 | } |
| 355 | 488 | ||
| 356 | /** | 489 | /** |
| @@ -377,9 +510,10 @@ should be added.</para> | |||
| 377 | -@@ | 510 | -@@ |
| 378 | ******************************************************************************/ | 511 | ******************************************************************************/ |
| 379 | 512 | ||
| 380 | bool is_pg_logname(char *username) { | 513 | static bool is_pg_logname(char *username) { |
| 381 | if (strlen(username) > NAMEDATALEN - 1) | 514 | if (strlen(username) > NAMEDATALEN - 1) { |
| 382 | return (false); | 515 | return (false); |
| 516 | } | ||
| 383 | return (true); | 517 | return (true); |
| 384 | } | 518 | } |
| 385 | 519 | ||
| @@ -394,7 +528,7 @@ bool is_pg_logname(char *username) { | |||
| 394 | void print_help(void) { | 528 | void print_help(void) { |
| 395 | char *myport; | 529 | char *myport; |
| 396 | 530 | ||
| 397 | xasprintf(&myport, "%d", DEFAULT_PORT); | 531 | xasprintf(&myport, "%d", 5432); |
| 398 | 532 | ||
| 399 | print_revision(progname, NP_VERSION); | 533 | print_revision(progname, NP_VERSION); |
| 400 | 534 | ||
| @@ -430,12 +564,13 @@ void print_help(void) { | |||
| 430 | printf(" %s\n", "--queryname=STRING"); | 564 | printf(" %s\n", "--queryname=STRING"); |
| 431 | 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")); |
| 432 | printf(" %s\n", _("in the long output of the plugin")); | 566 | printf(" %s\n", _("in the long output of the plugin")); |
| 433 | printf(" %s\n", "-W, --query-warning=RANGE"); | 567 | printf(" %s\n", "-W, --query_warning=RANGE"); |
| 434 | 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)")); |
| 435 | printf(" %s\n", "-C, --query-critical=RANGE"); | 569 | printf(" %s\n", "-C, --query_critical=RANGE"); |
| 436 | 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)")); |
| 437 | 571 | ||
| 438 | printf(UT_VERBOSE); | 572 | printf(UT_VERBOSE); |
| 573 | printf(UT_OUTPUT_FORMAT); | ||
| 439 | 574 | ||
| 440 | printf("\n"); | 575 | printf("\n"); |
| 441 | printf(" %s\n", _("All parameters are optional.")); | 576 | printf(" %s\n", _("All parameters are optional.")); |
| @@ -447,29 +582,39 @@ void print_help(void) { | |||
| 447 | 582 | ||
| 448 | printf(" %s\n", _("If a query is specified using the -q option, it will be executed after")); | 583 | printf(" %s\n", _("If a query is specified using the -q option, it will be executed after")); |
| 449 | printf(" %s\n", _("connecting to the server. The result from the query has to be numeric.")); | 584 | printf(" %s\n", _("connecting to the server. The result from the query has to be numeric.")); |
| 450 | printf(" %s\n", _("Multiple SQL commands, separated by semicolon, are allowed but the result ")); | 585 | printf(" %s\n", |
| 586 | _("Multiple SQL commands, separated by semicolon, are allowed but the result ")); | ||
| 451 | printf(" %s\n", _("of the last command is taken into account only. The value of the first")); | 587 | printf(" %s\n", _("of the last command is taken into account only. The value of the first")); |
| 452 | printf(" %s\n", _("column in the first row is used as the check result. If a second column is")); | 588 | printf(" %s\n", |
| 589 | _("column in the first row is used as the check result. If a second column is")); | ||
| 453 | printf(" %s\n", _("present in the result set, this is added to the plugin output with a")); | 590 | printf(" %s\n", _("present in the result set, this is added to the plugin output with a")); |
| 454 | printf(" %s\n", _("prefix of \"Extra Info:\". This information can be displayed in the system")); | 591 | printf(" %s\n", |
| 592 | _("prefix of \"Extra Info:\". This information can be displayed in the system")); | ||
| 455 | printf(" %s\n\n", _("executing the plugin.")); | 593 | printf(" %s\n\n", _("executing the plugin.")); |
| 456 | 594 | ||
| 457 | printf(" %s\n", _("See the chapter \"Monitoring Database Activity\" of the PostgreSQL manual")); | 595 | printf(" %s\n", _("See the chapter \"Monitoring Database Activity\" of the PostgreSQL manual")); |
| 458 | printf(" %s\n\n", _("for details about how to access internal statistics of the database server.")); | 596 | printf(" %s\n\n", |
| 597 | _("for details about how to access internal statistics of the database server.")); | ||
| 459 | 598 | ||
| 460 | printf(" %s\n", _("For a list of available connection parameters which may be used with the -o")); | 599 | printf(" %s\n", |
| 461 | printf(" %s\n", _("command line option, see the documentation for PQconnectdb() in the chapter")); | 600 | _("For a list of available connection parameters which may be used with the -o")); |
| 601 | printf(" %s\n", | ||
| 602 | _("command line option, see the documentation for PQconnectdb() in the chapter")); | ||
| 462 | printf(" %s\n", _("\"libpq - C Library\" of the PostgreSQL manual. For example, this may be")); | 603 | printf(" %s\n", _("\"libpq - C Library\" of the PostgreSQL manual. For example, this may be")); |
| 463 | printf(" %s\n", _("used to specify a service name in pg_service.conf to be used for additional")); | 604 | printf(" %s\n", |
| 605 | _("used to specify a service name in pg_service.conf to be used for additional")); | ||
| 464 | printf(" %s\n", _("connection parameters: -o 'service=<name>' or to specify the SSL mode:")); | 606 | printf(" %s\n", _("connection parameters: -o 'service=<name>' or to specify the SSL mode:")); |
| 465 | printf(" %s\n\n", _("-o 'sslmode=require'.")); | 607 | printf(" %s\n\n", _("-o 'sslmode=require'.")); |
| 466 | 608 | ||
| 467 | printf(" %s\n", _("The plugin will connect to a local postmaster if no host is specified. To")); | 609 | printf(" %s\n", _("The plugin will connect to a local postmaster if no host is specified. To")); |
| 468 | printf(" %s\n", _("connect to a remote host, be sure that the remote postmaster accepts TCP/IP")); | 610 | printf(" %s\n", |
| 611 | _("connect to a remote host, be sure that the remote postmaster accepts TCP/IP")); | ||
| 469 | printf(" %s\n\n", _("connections (start the postmaster with the -i option).")); | 612 | printf(" %s\n\n", _("connections (start the postmaster with the -i option).")); |
| 470 | 613 | ||
| 471 | printf(" %s\n", _("Typically, the monitoring user (unless the --logname option is used) should be")); | 614 | printf(" %s\n", |
| 472 | printf(" %s\n", _("able to connect to the database without a password. The plugin can also send")); | 615 | _("Typically, the monitoring user (unless the --logname option is used) should be")); |
| 616 | printf(" %s\n", | ||
| 617 | _("able to connect to the database without a password. The plugin can also send")); | ||
| 473 | printf(" %s\n", _("a password, but no effort is made to obscure or encrypt the password.")); | 618 | printf(" %s\n", _("a password, but no effort is made to obscure or encrypt the password.")); |
| 474 | 619 | ||
| 475 | printf(UT_SUPPORT); | 620 | printf(UT_SUPPORT); |
| @@ -482,64 +627,66 @@ void print_usage(void) { | |||
| 482 | "[-q <query>] [-C <critical query range>] [-W <warning query range>]\n"); | 627 | "[-q <query>] [-C <critical query range>] [-W <warning query range>]\n"); |
| 483 | } | 628 | } |
| 484 | 629 | ||
| 485 | int do_query(PGconn *conn, char *query) { | 630 | static do_query_wrapper do_query(PGconn *conn, char *query) { |
| 486 | if (verbose) | 631 | if (verbose) { |
| 487 | printf("Executing SQL query \"%s\".\n", query); | 632 | printf("Executing SQL query \"%s\".\n", query); |
| 633 | } | ||
| 488 | PGresult *res = PQexec(conn, query); | 634 | PGresult *res = PQexec(conn, query); |
| 489 | 635 | ||
| 636 | do_query_wrapper result = { | ||
| 637 | .error_code = QUERY_OK, | ||
| 638 | }; | ||
| 639 | |||
| 490 | if (PGRES_TUPLES_OK != PQresultStatus(res)) { | 640 | if (PGRES_TUPLES_OK != PQresultStatus(res)) { |
| 491 | printf(_("QUERY %s - %s: %s.\n"), _("CRITICAL"), _("Error with query"), PQerrorMessage(conn)); | 641 | // TODO |
| 492 | return STATE_CRITICAL; | 642 | // printf(_("QUERY %s - %s: %s.\n"), _("CRITICAL"), _("Error with query"), |
| 643 | // PQerrorMessage(conn)); | ||
| 644 | result.error_code = ERROR_WITH_QUERY; | ||
| 645 | return result; | ||
| 493 | } | 646 | } |
| 494 | 647 | ||
| 495 | if (PQntuples(res) < 1) { | 648 | if (PQntuples(res) < 1) { |
| 496 | printf("QUERY %s - %s.\n", _("WARNING"), _("No rows returned")); | 649 | // TODO |
| 497 | return STATE_WARNING; | 650 | // printf("QUERY %s - %s.\n", _("WARNING"), _("No rows returned")); |
| 651 | result.error_code = NO_ROWS_RETURNED; | ||
| 652 | return result; | ||
| 498 | } | 653 | } |
| 499 | 654 | ||
| 500 | if (PQnfields(res) < 1) { | 655 | if (PQnfields(res) < 1) { |
| 501 | printf("QUERY %s - %s.\n", _("WARNING"), _("No columns returned")); | 656 | // TODO |
| 502 | return STATE_WARNING; | 657 | // printf("QUERY %s - %s.\n", _("WARNING"), _("No columns returned")); |
| 658 | result.error_code = NO_COLUMNS_RETURNED; | ||
| 659 | return result; | ||
| 503 | } | 660 | } |
| 504 | 661 | ||
| 505 | char *val_str = PQgetvalue(res, 0, 0); | 662 | char *val_str = PQgetvalue(res, 0, 0); |
| 506 | if (!val_str) { | 663 | if (!val_str) { |
| 507 | printf("QUERY %s - %s.\n", _("CRITICAL"), _("No data returned")); | 664 | // TODO |
| 508 | return STATE_CRITICAL; | 665 | // printf("QUERY %s - %s.\n", _("CRITICAL"), _("No data returned")); |
| 666 | result.error_code = NO_DATA_RETURNED; | ||
| 667 | return result; | ||
| 509 | } | 668 | } |
| 510 | 669 | ||
| 511 | char *endptr = NULL; | 670 | char *endptr = NULL; |
| 512 | double value = strtod(val_str, &endptr); | 671 | double value = strtod(val_str, &endptr); |
| 513 | if (verbose) | 672 | if (verbose) { |
| 514 | printf("Query result: %f\n", value); | 673 | printf("Query result: %f\n", value); |
| 674 | } | ||
| 515 | 675 | ||
| 516 | if (endptr == val_str) { | 676 | if (endptr == val_str) { |
| 517 | printf("QUERY %s - %s: %s\n", _("CRITICAL"), _("Is not a numeric"), val_str); | 677 | // TODO |
| 518 | 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; | ||
| 519 | } | 681 | } |
| 520 | 682 | ||
| 521 | if ((endptr != NULL) && (*endptr != '\0')) { | 683 | if ((endptr != NULL) && (*endptr != '\0')) { |
| 522 | if (verbose) | 684 | if (verbose) { |
| 523 | printf("Garbage after value: %s.\n", endptr); | 685 | printf("Garbage after value: %s.\n", endptr); |
| 686 | } | ||
| 524 | } | 687 | } |
| 525 | 688 | ||
| 526 | int my_status = get_status(value, qthresholds); | 689 | result.numerical_result = value; |
| 527 | printf("QUERY %s - ", (my_status == STATE_OK) ? _("OK") | ||
| 528 | : (my_status == STATE_WARNING) ? _("WARNING") | ||
| 529 | : (my_status == STATE_CRITICAL) ? _("CRITICAL") | ||
| 530 | : _("UNKNOWN")); | ||
| 531 | if (pgqueryname) { | ||
| 532 | printf(_("%s returned %f"), pgqueryname, value); | ||
| 533 | } else { | ||
| 534 | printf(_("'%s' returned %f"), query, value); | ||
| 535 | } | ||
| 536 | 690 | ||
| 537 | printf("|query=%f;%s;%s;;\n", value, query_warning ? query_warning : "", query_critical ? query_critical : ""); | 691 | return result; |
| 538 | if (PQnfields(res) > 1) { | ||
| 539 | char *extra_info = PQgetvalue(res, 0, 1); | ||
| 540 | if (extra_info != NULL) { | ||
| 541 | printf("Extra Info: %s\n", extra_info); | ||
| 542 | } | ||
| 543 | } | ||
| 544 | return my_status; | ||
| 545 | } | 692 | } |
