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