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 0000000..03614ab --- /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 | ******************************************************************************/ | ||