diff options
Diffstat (limited to 'plugins/check_pgsql.c')
| -rw-r--r-- | plugins/check_pgsql.c | 440 |
1 files changed, 440 insertions, 0 deletions
diff --git a/plugins/check_pgsql.c b/plugins/check_pgsql.c new file mode 100644 index 00000000..03614ab1 --- /dev/null +++ b/plugins/check_pgsql.c | |||
| @@ -0,0 +1,440 @@ | |||
| 1 | /****************************************************************************** | ||
| 2 | * | ||
| 3 | * Program: PostgreSQL plugin for Nagios | ||
| 4 | * License: GPL | ||
| 5 | * | ||
| 6 | * License Information: | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or modify | ||
| 9 | * it under the terms of the GNU General Public License as published by | ||
| 10 | * the Free Software Foundation; either version 2 of the License, or | ||
| 11 | * (at your option) any later version. | ||
| 12 | * | ||
| 13 | * This program is distributed in the hope that it will be useful, | ||
| 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 16 | * GNU General Public License for more details. | ||
| 17 | * | ||
| 18 | * You should have received a copy of the GNU General Public License | ||
| 19 | * along with this program; if not, write to the Free Software | ||
| 20 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
| 21 | * | ||
| 22 | * $Id$ | ||
| 23 | * | ||
| 24 | *****************************************************************************/ | ||
| 25 | |||
| 26 | #define PROGNAME "check_pgsql" | ||
| 27 | #define REVISION "$Revision$" | ||
| 28 | #define COPYRIGHT "1999-2001" | ||
| 29 | #define AUTHOR "Karl DeBisschop" | ||
| 30 | #define EMAIL "kdebisschop@users.sourceforge.net" | ||
| 31 | #define SUMMARY "Tests to see if a PostgreSQL DBMS is accepting connections.\n" | ||
| 32 | |||
| 33 | #define OPTIONS "\ | ||
| 34 | \[-c critical_time] [-w warning_time] [-t timeout] [-H host]\n\ | ||
| 35 | [-P port] [-d database] [-l logname] [-p password]" | ||
| 36 | |||
| 37 | #define LONGOPTIONS "\ | ||
| 38 | -c, --critical=INTEGER\n\ | ||
| 39 | Exit STATE_CRITICAL if connection time exceeds threshold (default: %d)\n\ | ||
| 40 | -w, --warning=INTEGER\n\ | ||
| 41 | Exit STATE_WARNING if connection time exceeds threshold (default: %d)\n\ | ||
| 42 | -t, --timeout=INTEGER\n\ | ||
| 43 | Terminate test if timeout limit is exceeded (default: %d)\n\ | ||
| 44 | -H, --hostname=STRING\n\ | ||
| 45 | Name or numeric IP address of machine running backend\n\ | ||
| 46 | -P, --port=INTEGER\n\ | ||
| 47 | Port running backend (default: %s)\n\ | ||
| 48 | -d, --database=STRING\n\ | ||
| 49 | Database to check (default: %s)\n\ | ||
| 50 | -l, --logname = STRING\n\ | ||
| 51 | Login name of user\n\ | ||
| 52 | -p, --password = STRING\n\ | ||
| 53 | Password (BIG SECURITY ISSUE)\n" | ||
| 54 | |||
| 55 | #define DESCRIPTION "All parameters are optional.\n\ | ||
| 56 | \n\ | ||
| 57 | This plugin tests a PostgreSQL DBMS to determine whether it is active and\n\ | ||
| 58 | accepting queries. In its current operation, it simply connects to the\n\ | ||
| 59 | specified database, and then disconnects. If no database is specified, it\n\ | ||
| 60 | connects to the template1 database, which is present in every functioning \n\ | ||
| 61 | PostgreSQL DBMS.\n\ | ||
| 62 | \n\ | ||
| 63 | The plugin will connect to a local postmaster if no host is specified. To\n\ | ||
| 64 | connect to a remote host, be sure that the remote postmaster accepts TCP/IP\n\ | ||
| 65 | connections (start the postmaster with the -i option).\n\ | ||
| 66 | \n\ | ||
| 67 | Typically, the nagios user (unless the --logname option is used) should be\n\ | ||
| 68 | able to connect to the database without a password. The plugin can also send\n\ | ||
| 69 | a password, but no effort is made to obsure or encrypt the password.\n" | ||
| 70 | |||
| 71 | #define DEFAULT_DB "template1" | ||
| 72 | #define DEFAULT_HOST "127.0.0.1" | ||
| 73 | #define DEFAULT_PORT "5432" | ||
| 74 | #define DEFAULT_WARN 2 | ||
| 75 | #define DEFAULT_CRIT 8 | ||
| 76 | #define DEFAULT_TIMEOUT 30 | ||
| 77 | |||
| 78 | #include "config.h" | ||
| 79 | #include "common.h" | ||
| 80 | #include "utils.h" | ||
| 81 | #include <netdb.h> | ||
| 82 | #include <libpq-fe.h> | ||
| 83 | |||
| 84 | int process_arguments (int, char **); | ||
| 85 | int validate_arguments (void); | ||
| 86 | void print_usage (void); | ||
| 87 | void print_help (void); | ||
| 88 | int is_pg_dbname (char *); | ||
| 89 | int is_pg_logname (char *); | ||
| 90 | |||
| 91 | char *pghost = NULL; /* host name of the backend server */ | ||
| 92 | char *pgport = NULL; /* port of the backend server */ | ||
| 93 | char default_port[4] = DEFAULT_PORT; | ||
| 94 | char *pgoptions = NULL; | ||
| 95 | char *pgtty = NULL; | ||
| 96 | char dbName[NAMEDATALEN] = DEFAULT_DB; | ||
| 97 | char *pguser = NULL; | ||
| 98 | char *pgpasswd = NULL; | ||
| 99 | int twarn = DEFAULT_WARN; | ||
| 100 | int tcrit = DEFAULT_CRIT; | ||
| 101 | |||
| 102 | PGconn *conn; | ||
| 103 | /*PGresult *res;*/ | ||
| 104 | |||
| 105 | |||
| 106 | /****************************************************************************** | ||
| 107 | |||
| 108 | The (psuedo?)literate programming XML is contained within \@\@\- <XML> \-\@\@ | ||
| 109 | tags in the comments. With in the tags, the XML is assembled sequentially. | ||
| 110 | You can define entities in tags. You also have all the #defines available as | ||
| 111 | entities. | ||
| 112 | |||
| 113 | Please note that all tags must be lowercase to use the DocBook XML DTD. | ||
| 114 | |||
| 115 | @@-<article> | ||
| 116 | |||
| 117 | <sect1> | ||
| 118 | <title>Quick Reference</title> | ||
| 119 | <!-- The refentry forms a manpage --> | ||
| 120 | <refentry> | ||
| 121 | <refmeta> | ||
| 122 | <manvolnum>5<manvolnum> | ||
| 123 | </refmeta> | ||
| 124 | <refnamdiv> | ||
| 125 | <refname>&PROGNAME;</refname> | ||
| 126 | <refpurpose>&SUMMARY;</refpurpose> | ||
| 127 | </refnamdiv> | ||
| 128 | </refentry> | ||
| 129 | </sect1> | ||
| 130 | |||
| 131 | <sect1> | ||
| 132 | <title>FAQ</title> | ||
| 133 | </sect1> | ||
| 134 | |||
| 135 | <sect1> | ||
| 136 | <title>Theory, Installation, and Operation</title> | ||
| 137 | |||
| 138 | <sect2> | ||
| 139 | <title>General Description</title> | ||
| 140 | <para> | ||
| 141 | &DESCRIPTION; | ||
| 142 | </para> | ||
| 143 | </sect2> | ||
| 144 | |||
| 145 | <sect2> | ||
| 146 | <title>Future Enhancements</title> | ||
| 147 | <para>ToDo List</para> | ||
| 148 | <itemizedlist> | ||
| 149 | <listitem>Add option to get password from a secured file rather than the command line</listitem> | ||
| 150 | <listitem>Add option to specify the query to execute</listitem> | ||
| 151 | </itemizedlist> | ||
| 152 | </sect2> | ||
| 153 | |||
| 154 | |||
| 155 | <sect2> | ||
| 156 | <title>Functions</title> | ||
| 157 | -@@ | ||
| 158 | ******************************************************************************/ | ||
| 159 | |||
| 160 | int | ||
| 161 | main (int argc, char **argv) | ||
| 162 | { | ||
| 163 | int elapsed_time; | ||
| 164 | |||
| 165 | /* begin, by setting the parameters for a backend connection if the | ||
| 166 | * parameters are null, then the system will try to use reasonable | ||
| 167 | * defaults by looking up environment variables or, failing that, | ||
| 168 | * using hardwired constants */ | ||
| 169 | |||
| 170 | pgoptions = NULL; /* special options to start up the backend server */ | ||
| 171 | pgtty = NULL; /* debugging tty for the backend server */ | ||
| 172 | |||
| 173 | if (process_arguments (argc, argv) == ERROR) | ||
| 174 | usage ("Could not parse arguments"); | ||
| 175 | |||
| 176 | /* Set signal handling and alarm */ | ||
| 177 | if (signal (SIGALRM, timeout_alarm_handler) == SIG_ERR) { | ||
| 178 | printf ("Cannot catch SIGALRM"); | ||
| 179 | return STATE_UNKNOWN; | ||
| 180 | } | ||
| 181 | alarm (timeout_interval); | ||
| 182 | |||
| 183 | /* make a connection to the database */ | ||
| 184 | time (&start_time); | ||
| 185 | conn = | ||
| 186 | PQsetdbLogin (pghost, pgport, pgoptions, pgtty, dbName, pguser, pgpasswd); | ||
| 187 | time (&end_time); | ||
| 188 | elapsed_time = (int) (end_time - start_time); | ||
| 189 | |||
| 190 | /* check to see that the backend connection was successfully made */ | ||
| 191 | if (PQstatus (conn) == CONNECTION_BAD) { | ||
| 192 | printf ("PGSQL: CRITICAL - no connection to '%s' (%s).\n", dbName, | ||
| 193 | PQerrorMessage (conn)); | ||
| 194 | PQfinish (conn); | ||
| 195 | return STATE_CRITICAL; | ||
| 196 | } | ||
| 197 | else if (elapsed_time > tcrit) { | ||
| 198 | PQfinish (conn); | ||
| 199 | printf ("PGSQL: CRITICAL - database %s (%d sec.)\n", dbName, | ||
| 200 | elapsed_time); | ||
| 201 | return STATE_CRITICAL; | ||
| 202 | } | ||
| 203 | else if (elapsed_time > twarn) { | ||
| 204 | PQfinish (conn); | ||
| 205 | printf ("PGSQL: WARNING - database %s (%d sec.)\n", dbName, elapsed_time); | ||
| 206 | return STATE_WARNING; | ||
| 207 | } | ||
| 208 | else { | ||
| 209 | PQfinish (conn); | ||
| 210 | printf ("PGSQL: ok - database %s (%d sec.)\n", dbName, elapsed_time); | ||
| 211 | return STATE_OK; | ||
| 212 | } | ||
| 213 | } | ||
| 214 | |||
| 215 | |||
| 216 | |||
| 217 | |||
| 218 | void | ||
| 219 | print_help (void) | ||
| 220 | { | ||
| 221 | print_revision (PROGNAME, REVISION); | ||
| 222 | printf | ||
| 223 | ("Copyright (c) %s %s <%s>\n\n%s\n", | ||
| 224 | COPYRIGHT, AUTHOR, EMAIL, SUMMARY); | ||
| 225 | print_usage (); | ||
| 226 | printf | ||
| 227 | ("\nOptions:\n" LONGOPTIONS "\n" DESCRIPTION "\n", | ||
| 228 | DEFAULT_WARN, DEFAULT_CRIT, DEFAULT_TIMEOUT, DEFAULT_PORT, DEFAULT_DB); | ||
| 229 | support (); | ||
| 230 | } | ||
| 231 | |||
| 232 | void | ||
| 233 | print_usage (void) | ||
| 234 | { | ||
| 235 | printf ("Usage:\n" " %s %s\n" | ||
| 236 | #ifdef HAVE_GETOPT_H | ||
| 237 | " %s (-h | --help) for detailed help\n" | ||
| 238 | " %s (-V | --version) for version information\n", | ||
| 239 | #else | ||
| 240 | " %s -h for detailed help\n" | ||
| 241 | " %s -V for version information\n", | ||
| 242 | #endif | ||
| 243 | PROGNAME, OPTIONS, PROGNAME, PROGNAME); | ||
| 244 | } | ||
| 245 | |||
| 246 | |||
| 247 | |||
| 248 | /* process command-line arguments */ | ||
| 249 | int | ||
| 250 | process_arguments (int argc, char **argv) | ||
| 251 | { | ||
| 252 | int c; | ||
| 253 | |||
| 254 | #ifdef HAVE_GETOPT_H | ||
| 255 | int option_index = 0; | ||
| 256 | static struct option long_options[] = { | ||
| 257 | {"help", no_argument, 0, 'h'}, | ||
| 258 | {"version", no_argument, 0, 'V'}, | ||
| 259 | {"timeout", required_argument, 0, 't'}, | ||
| 260 | {"critical", required_argument, 0, 'c'}, | ||
| 261 | {"warning", required_argument, 0, 'w'}, | ||
| 262 | {"hostname", required_argument, 0, 'H'}, | ||
| 263 | {"logname", required_argument, 0, 'l'}, | ||
| 264 | {"password", required_argument, 0, 'p'}, | ||
| 265 | {"authorization", required_argument, 0, 'a'}, | ||
| 266 | {"port", required_argument, 0, 'P'}, | ||
| 267 | {"database", required_argument, 0, 'd'}, | ||
| 268 | {0, 0, 0, 0} | ||
| 269 | }; | ||
| 270 | #endif | ||
| 271 | |||
| 272 | while (1) { | ||
| 273 | #ifdef HAVE_GETOPT_H | ||
| 274 | c = getopt_long (argc, argv, "+?hVt:c:w:H:P:d:l:p:a:", | ||
| 275 | long_options, &option_index); | ||
| 276 | #else | ||
| 277 | c = getopt (argc, argv, "+?hVt:c:w:H:P:d:l:p:a:"); | ||
| 278 | #endif | ||
| 279 | if (c == EOF) | ||
| 280 | break; | ||
| 281 | |||
| 282 | switch (c) { | ||
| 283 | case '?': /* help */ | ||
| 284 | usage2 ("Unknown argument", optarg); | ||
| 285 | case 'h': /* help */ | ||
| 286 | print_help (); | ||
| 287 | exit (STATE_OK); | ||
| 288 | case 'V': /* version */ | ||
| 289 | print_revision (PROGNAME, REVISION); | ||
| 290 | exit (STATE_OK); | ||
| 291 | case 't': /* timeout period */ | ||
| 292 | if (!is_integer (optarg)) | ||
| 293 | usage2 ("Timeout Interval must be an integer", optarg); | ||
| 294 | timeout_interval = atoi (optarg); | ||
| 295 | break; | ||
| 296 | case 'c': /* critical time threshold */ | ||
| 297 | if (!is_integer (optarg)) | ||
| 298 | usage2 ("Invalid critical threshold", optarg); | ||
| 299 | tcrit = atoi (optarg); | ||
| 300 | break; | ||
| 301 | case 'w': /* warning time threshold */ | ||
| 302 | if (!is_integer (optarg)) | ||
| 303 | usage2 ("Invalid critical threshold", optarg); | ||
| 304 | twarn = atoi (optarg); | ||
| 305 | break; | ||
| 306 | case 'H': /* host */ | ||
| 307 | if (!is_host (optarg)) | ||
| 308 | usage2 ("You gave an invalid host name", optarg); | ||
| 309 | pghost = optarg; | ||
| 310 | break; | ||
| 311 | case 'P': /* port */ | ||
| 312 | if (!is_integer (optarg)) | ||
| 313 | usage2 ("Port must be an integer", optarg); | ||
| 314 | pgport = optarg; | ||
| 315 | break; | ||
| 316 | case 'd': /* database name */ | ||
| 317 | if (!is_pg_dbname (optarg)) | ||
| 318 | usage2 ("Database name is not valid", optarg); | ||
| 319 | strncpy (dbName, optarg, NAMEDATALEN - 1); | ||
| 320 | dbName[NAMEDATALEN - 1] = 0; | ||
| 321 | break; | ||
| 322 | case 'l': /* login name */ | ||
| 323 | if (!is_pg_logname (optarg)) | ||
| 324 | usage2 ("user name is not valid", optarg); | ||
| 325 | pguser = optarg; | ||
| 326 | break; | ||
| 327 | case 'p': /* authentication password */ | ||
| 328 | case 'a': | ||
| 329 | pgpasswd = optarg; | ||
| 330 | break; | ||
| 331 | } | ||
| 332 | } | ||
| 333 | |||
| 334 | return validate_arguments (); | ||
| 335 | } | ||
| 336 | |||
| 337 | |||
| 338 | /****************************************************************************** | ||
| 339 | |||
| 340 | @@- | ||
| 341 | <sect3> | ||
| 342 | <title>validate_arguments</title> | ||
| 343 | |||
| 344 | <para>&PROTO_validate_arguments;</para> | ||
| 345 | |||
| 346 | <para>Given a database name, this function returns TRUE if the string | ||
| 347 | is a valid PostgreSQL database name, and returns false if it is | ||
| 348 | not.</para> | ||
| 349 | |||
| 350 | <para>Valid PostgreSQL database names are less than &NAMEDATALEN; | ||
| 351 | characters long and consist of letters, numbers, and underscores. The | ||
| 352 | first character cannot be a number, however.</para> | ||
| 353 | |||
| 354 | </sect3> | ||
| 355 | -@@ | ||
| 356 | ******************************************************************************/ | ||
| 357 | |||
| 358 | int | ||
| 359 | validate_arguments () | ||
| 360 | { | ||
| 361 | return OK; | ||
| 362 | } | ||
| 363 | |||
| 364 | |||
| 365 | |||
| 366 | /****************************************************************************** | ||
| 367 | |||
| 368 | @@- | ||
| 369 | <sect3> | ||
| 370 | <title>is_pg_dbname</title> | ||
| 371 | |||
| 372 | <para>&PROTO_is_pg_dbname;</para> | ||
| 373 | |||
| 374 | <para>Given a database name, this function returns TRUE if the string | ||
| 375 | is a valid PostgreSQL database name, and returns false if it is | ||
| 376 | not.</para> | ||
| 377 | |||
| 378 | <para>Valid PostgreSQL database names are less than &NAMEDATALEN; | ||
| 379 | characters long and consist of letters, numbers, and underscores. The | ||
| 380 | first character cannot be a number, however.</para> | ||
| 381 | |||
| 382 | </sect3> | ||
| 383 | -@@ | ||
| 384 | ******************************************************************************/ | ||
| 385 | |||
| 386 | int | ||
| 387 | is_pg_dbname (char *dbname) | ||
| 388 | { | ||
| 389 | char txt[NAMEDATALEN]; | ||
| 390 | char tmp[NAMEDATALEN]; | ||
| 391 | if (strlen (dbname) > NAMEDATALEN - 1) | ||
| 392 | return (FALSE); | ||
| 393 | strncpy (txt, dbname, NAMEDATALEN - 1); | ||
| 394 | txt[NAMEDATALEN - 1] = 0; | ||
| 395 | if (sscanf (txt, "%[_a-zA-Z]%[^_a-zA-Z0-9]", tmp, tmp) == 1) | ||
| 396 | return (TRUE); | ||
| 397 | if (sscanf (txt, "%[_a-zA-Z]%[_a-zA-Z0-9]%[^_a-zA-Z0-9]", tmp, tmp, tmp) == | ||
| 398 | 2) return (TRUE); | ||
| 399 | return (FALSE); | ||
| 400 | } | ||
| 401 | |||
| 402 | /** | ||
| 403 | |||
| 404 | the tango program should eventually create an entity here based on the | ||
| 405 | function prototype | ||
| 406 | |||
| 407 | @@- | ||
| 408 | <sect3> | ||
| 409 | <title>is_pg_logname</title> | ||
| 410 | |||
| 411 | <para>&PROTO_is_pg_logname;</para> | ||
| 412 | |||
| 413 | <para>Given a username, this function returns TRUE if the string is a | ||
| 414 | valid PostgreSQL username, and returns false if it is not. Valid PostgreSQL | ||
| 415 | usernames are less than &NAMEDATALEN; characters long and consist of | ||
| 416 | letters, numbers, dashes, and underscores, plus possibly some other | ||
| 417 | characters.</para> | ||
| 418 | |||
| 419 | <para>Currently this function only checks string length. Additional checks | ||
| 420 | should be added.</para> | ||
| 421 | |||
| 422 | </sect3> | ||
| 423 | -@@ | ||
| 424 | ******************************************************************************/ | ||
| 425 | |||
| 426 | int | ||
| 427 | is_pg_logname (char *username) | ||
| 428 | { | ||
| 429 | if (strlen (username) > NAMEDATALEN - 1) | ||
| 430 | return (FALSE); | ||
| 431 | return (TRUE); | ||
| 432 | } | ||
| 433 | |||
| 434 | /****************************************************************************** | ||
| 435 | @@- | ||
| 436 | </sect2> | ||
| 437 | </sect1> | ||
| 438 | </article> | ||
| 439 | -@@ | ||
| 440 | ******************************************************************************/ | ||
