dea39cf9702ef831939046a5ec32277e43a9f7f9
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 *****************************************************************************/
26 const char *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"
33 #define DEFAULT_DB "template1"
34 #define DEFAULT_HOST "127.0.0.1"
36 enum {
37 DEFAULT_PORT = 5432,
38 DEFAULT_WARN = 2,
39 DEFAULT_CRIT = 8,
40 DEFAULT_TIMEOUT = 30
41 };
43 #include "config.h"
44 #include "common.h"
45 #include "utils.h"
46 #include <libpq-fe.h>
48 int process_arguments (int, char **);
49 int validate_arguments (void);
50 void print_usage (void);
51 void print_help (void);
52 int is_pg_dbname (char *);
53 int is_pg_logname (char *);
55 char *pghost = NULL; /* host name of the backend server */
56 char *pgport = NULL; /* port of the backend server */
57 int default_port = DEFAULT_PORT;
58 char *pgoptions = NULL;
59 char *pgtty = NULL;
60 char dbName[NAMEDATALEN] = DEFAULT_DB;
61 char *pguser = NULL;
62 char *pgpasswd = NULL;
63 int twarn = DEFAULT_WARN;
64 int tcrit = DEFAULT_CRIT;
66 PGconn *conn;
67 /*PGresult *res;*/
68 \f
70 /******************************************************************************
72 The (psuedo?)literate programming XML is contained within \@\@\- <XML> \-\@\@
73 tags in the comments. With in the tags, the XML is assembled sequentially.
74 You can define entities in tags. You also have all the #defines available as
75 entities.
77 Please note that all tags must be lowercase to use the DocBook XML DTD.
79 @@-<article>
81 <sect1>
82 <title>Quick Reference</title>
83 <!-- The refentry forms a manpage -->
84 <refentry>
85 <refmeta>
86 <manvolnum>5<manvolnum>
87 </refmeta>
88 <refnamdiv>
89 <refname>&progname;</refname>
90 <refpurpose>&SUMMARY;</refpurpose>
91 </refnamdiv>
92 </refentry>
93 </sect1>
95 <sect1>
96 <title>FAQ</title>
97 </sect1>
99 <sect1>
100 <title>Theory, Installation, and Operation</title>
102 <sect2>
103 <title>General Description</title>
104 <para>
105 &DESCRIPTION;
106 </para>
107 </sect2>
109 <sect2>
110 <title>Future Enhancements</title>
111 <para>ToDo List</para>
112 <itemizedlist>
113 <listitem>Add option to get password from a secured file rather than the command line</listitem>
114 <listitem>Add option to specify the query to execute</listitem>
115 </itemizedlist>
116 </sect2>
119 <sect2>
120 <title>Functions</title>
121 -@@
122 ******************************************************************************/
123 \f
127 void
128 print_help (void)
129 {
130 print_revision (progname, REVISION);
131 printf
132 ("Copyright (c) %s %s <%s>\n\n%s\n",
133 COPYRIGHT, AUTHOR, EMAIL, SUMMARY);
134 print_usage ();
135 printf (_("\
136 \nOptions:\n\
137 -H, --hostname=ADDRESS\n\
138 Host name argument for servers using host headers (use numeric\n\
139 address if possible to bypass DNS lookup).\n\
140 -P, --port=INTEGER\n\
141 Port number (default: %d)\n\
142 -4, --use-ipv4\n\
143 Use IPv4 connection\n\
144 -6, --use-ipv6\n\
145 Use IPv6 connection\n"), DEFAULT_PORT);
146 printf (S_("\
147 -d, --database=STRING\n\
148 Database to check (default: %s)\n\
149 -l, --logname = STRING\n\
150 Login name of user\n\
151 -p, --password = STRING\n\
152 Password (BIG SECURITY ISSUE)\n\n"), DEFAULT_DB);
153 printf (S_("\nOptions:\n\
154 -c, --critical=INTEGER\n\
155 Exit STATE_CRITICAL if connection time exceeds threshold (default: %d)\n\
156 -w, --warning=INTEGER\n\
157 Exit STATE_WARNING if connection time exceeds threshold (default: %d)\n\
158 -t, --timeout=INTEGER\n\
159 Terminate test if timeout limit is exceeded (default: %d)\n"),
160 DEFAULT_WARN, DEFAULT_CRIT, DEFAULT_TIMEOUT);
161 printf (_("\
162 -v, --verbose\n\
163 Show details for command-line debugging (Nagios may truncate output)\n\
164 -h, --help\n\
165 Print detailed help screen\n\
166 -V, --version\n\
167 Print version information\n\n"));
168 printf (S_("All parameters are optional.\n\
169 \n\
170 This plugin tests a PostgreSQL DBMS to determine whether it is active and\n\
171 accepting queries. In its current operation, it simply connects to the\n\
172 specified database, and then disconnects. If no database is specified, it\n\
173 connects to the template1 database, which is present in every functioning \n\
174 PostgreSQL DBMS.\n"));
175 printf (S_("\n\
176 The plugin will connect to a local postmaster if no host is specified. To\n\
177 connect to a remote host, be sure that the remote postmaster accepts TCP/IP\n\
178 connections (start the postmaster with the -i option).\n"));
179 printf (S_("\n\
180 Typically, the nagios user (unless the --logname option is used) should be\n\
181 able to connect to the database without a password. The plugin can also send\n\
182 a password, but no effort is made to obsure or encrypt the password.\n"));
184 support ();
185 }
187 void
188 print_usage (void)
189 {
190 printf (S_("\
191 Usage:\n %s [-H <host>] [-P <port>] [-c <critical time>] [-w <warning time>]\n\
192 [-t <timeout>]"), progname);
193 printf (S_("[-d <database>] [-l <logname>] [-p <password>]\n"));
194 printf (S_("\
195 %s (-h | --help) for detailed help\n\
196 %s (-V | --version) for version information\n"),
197 progname, progname);
198 }
200 int
201 main (int argc, char **argv)
202 {
203 int elapsed_time;
205 /* begin, by setting the parameters for a backend connection if the
206 * parameters are null, then the system will try to use reasonable
207 * defaults by looking up environment variables or, failing that,
208 * using hardwired constants */
210 pgoptions = NULL; /* special options to start up the backend server */
211 pgtty = NULL; /* debugging tty for the backend server */
213 if (process_arguments (argc, argv) == ERROR)
214 usage ("Could not parse arguments");
216 /* Set signal handling and alarm */
217 if (signal (SIGALRM, timeout_alarm_handler) == SIG_ERR) {
218 printf (_("Cannot catch SIGALRM"));
219 return STATE_UNKNOWN;
220 }
221 alarm (timeout_interval);
223 /* make a connection to the database */
224 time (&start_time);
225 conn =
226 PQsetdbLogin (pghost, pgport, pgoptions, pgtty, dbName, pguser, pgpasswd);
227 time (&end_time);
228 elapsed_time = (int) (end_time - start_time);
230 /* check to see that the backend connection was successfully made */
231 if (PQstatus (conn) == CONNECTION_BAD) {
232 printf (_("PGSQL: CRITICAL - no connection to '%s' (%s).\n"), dbName,
233 PQerrorMessage (conn));
234 PQfinish (conn);
235 return STATE_CRITICAL;
236 }
237 else if (elapsed_time > tcrit) {
238 PQfinish (conn);
239 printf (_("PGSQL: CRITICAL - database %s (%d sec.)\n"), dbName,
240 elapsed_time);
241 return STATE_CRITICAL;
242 }
243 else if (elapsed_time > twarn) {
244 PQfinish (conn);
245 printf (_("PGSQL: WARNING - database %s (%d sec.)\n"), dbName, elapsed_time);
246 return STATE_WARNING;
247 }
248 else {
249 PQfinish (conn);
250 printf (_("PGSQL: ok - database %s (%d sec.)\n"), dbName, elapsed_time);
251 return STATE_OK;
252 }
253 }
254 \f
257 /* process command-line arguments */
258 int
259 process_arguments (int argc, char **argv)
260 {
261 int c;
263 int option_index = 0;
264 static struct option long_options[] = {
265 {"help", no_argument, 0, 'h'},
266 {"version", no_argument, 0, 'V'},
267 {"timeout", required_argument, 0, 't'},
268 {"critical", required_argument, 0, 'c'},
269 {"warning", required_argument, 0, 'w'},
270 {"hostname", required_argument, 0, 'H'},
271 {"logname", required_argument, 0, 'l'},
272 {"password", required_argument, 0, 'p'},
273 {"authorization", required_argument, 0, 'a'},
274 {"port", required_argument, 0, 'P'},
275 {"database", required_argument, 0, 'd'},
276 {0, 0, 0, 0}
277 };
279 while (1) {
280 c = getopt_long (argc, argv, "hVt:c:w:H:P:d:l:p:a:",
281 long_options, &option_index);
283 if (c == EOF)
284 break;
286 switch (c) {
287 case '?': /* usage */
288 usage3 (_("Unknown argument"), optopt);
289 case 'h': /* help */
290 print_help ();
291 exit (STATE_OK);
292 case 'V': /* version */
293 print_revision (progname, REVISION);
294 exit (STATE_OK);
295 case 't': /* timeout period */
296 if (!is_integer (optarg))
297 usage2 (_("Timeout Interval must be an integer"), optarg);
298 timeout_interval = atoi (optarg);
299 break;
300 case 'c': /* critical time threshold */
301 if (!is_integer (optarg))
302 usage2 (_("Invalid critical threshold"), optarg);
303 tcrit = atoi (optarg);
304 break;
305 case 'w': /* warning time threshold */
306 if (!is_integer (optarg))
307 usage2 (_("Invalid critical threshold"), optarg);
308 twarn = atoi (optarg);
309 break;
310 case 'H': /* host */
311 if (!is_host (optarg))
312 usage2 (_("You gave an invalid host name"), optarg);
313 pghost = optarg;
314 break;
315 case 'P': /* port */
316 if (!is_integer (optarg))
317 usage2 (_("Port must be an integer"), optarg);
318 pgport = optarg;
319 break;
320 case 'd': /* database name */
321 if (!is_pg_dbname (optarg))
322 usage2 (_("Database name is not valid"), optarg);
323 strncpy (dbName, optarg, NAMEDATALEN - 1);
324 dbName[NAMEDATALEN - 1] = 0;
325 break;
326 case 'l': /* login name */
327 if (!is_pg_logname (optarg))
328 usage2 (_("user name is not valid"), optarg);
329 pguser = optarg;
330 break;
331 case 'p': /* authentication password */
332 case 'a':
333 pgpasswd = optarg;
334 break;
335 }
336 }
338 return validate_arguments ();
339 }
342 /******************************************************************************
344 @@-
345 <sect3>
346 <title>validate_arguments</title>
348 <para>&PROTO_validate_arguments;</para>
350 <para>Given a database name, this function returns TRUE if the string
351 is a valid PostgreSQL database name, and returns false if it is
352 not.</para>
354 <para>Valid PostgreSQL database names are less than &NAMEDATALEN;
355 characters long and consist of letters, numbers, and underscores. The
356 first character cannot be a number, however.</para>
358 </sect3>
359 -@@
360 ******************************************************************************/
362 int
363 validate_arguments ()
364 {
365 return OK;
366 }
367 \f
370 /******************************************************************************
372 @@-
373 <sect3>
374 <title>is_pg_dbname</title>
376 <para>&PROTO_is_pg_dbname;</para>
378 <para>Given a database name, this function returns TRUE if the string
379 is a valid PostgreSQL database name, and returns false if it is
380 not.</para>
382 <para>Valid PostgreSQL database names are less than &NAMEDATALEN;
383 characters long and consist of letters, numbers, and underscores. The
384 first character cannot be a number, however.</para>
386 </sect3>
387 -@@
388 ******************************************************************************/
390 int
391 is_pg_dbname (char *dbname)
392 {
393 char txt[NAMEDATALEN];
394 char tmp[NAMEDATALEN];
395 if (strlen (dbname) > NAMEDATALEN - 1)
396 return (FALSE);
397 strncpy (txt, dbname, NAMEDATALEN - 1);
398 txt[NAMEDATALEN - 1] = 0;
399 if (sscanf (txt, "%[_a-zA-Z]%[^_a-zA-Z0-9]", tmp, tmp) == 1)
400 return (TRUE);
401 if (sscanf (txt, "%[_a-zA-Z]%[_a-zA-Z0-9]%[^_a-zA-Z0-9]", tmp, tmp, tmp) ==
402 2) return (TRUE);
403 return (FALSE);
404 }
406 /**
408 the tango program should eventually create an entity here based on the
409 function prototype
411 @@-
412 <sect3>
413 <title>is_pg_logname</title>
415 <para>&PROTO_is_pg_logname;</para>
417 <para>Given a username, this function returns TRUE if the string is a
418 valid PostgreSQL username, and returns false if it is not. Valid PostgreSQL
419 usernames are less than &NAMEDATALEN; characters long and consist of
420 letters, numbers, dashes, and underscores, plus possibly some other
421 characters.</para>
423 <para>Currently this function only checks string length. Additional checks
424 should be added.</para>
426 </sect3>
427 -@@
428 ******************************************************************************/
430 int
431 is_pg_logname (char *username)
432 {
433 if (strlen (username) > NAMEDATALEN - 1)
434 return (FALSE);
435 return (TRUE);
436 }
438 /******************************************************************************
439 @@-
440 </sect2>
441 </sect1>
442 </article>
443 -@@
444 ******************************************************************************/