1 /******************************************************************************
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; either version 2 of the License, or
6 (at your option) any later version.
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
13 You should have received a copy of the GNU General Public License
14 along with this program; if not, write to the Free Software
15 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17 $Id$
19 *****************************************************************************/
21 const char *progname = "check_pgsql";
22 const char *revision = "$Revision$";
23 const char *copyright = "1999-2004";
24 const char *email = "nagiosplug-devel@lists.sourceforge.net";
26 #include "common.h"
27 #include "utils.h"
29 #include "netutils.h"
30 #include <libpq-fe.h>
32 #define DEFAULT_DB "template1"
33 #define DEFAULT_HOST "127.0.0.1"
35 enum {
36 DEFAULT_PORT = 5432,
37 DEFAULT_WARN = 2,
38 DEFAULT_CRIT = 8
39 };
43 int process_arguments (int, char **);
44 int validate_arguments (void);
45 void print_usage (void);
46 void print_help (void);
47 int is_pg_dbname (char *);
48 int is_pg_logname (char *);
50 char *pghost = NULL; /* host name of the backend server */
51 char *pgport = NULL; /* port of the backend server */
52 int default_port = DEFAULT_PORT;
53 char *pgoptions = NULL;
54 char *pgtty = NULL;
55 char dbName[NAMEDATALEN] = DEFAULT_DB;
56 char *pguser = NULL;
57 char *pgpasswd = NULL;
58 double twarn = (double)DEFAULT_WARN;
59 double tcrit = (double)DEFAULT_CRIT;
61 PGconn *conn;
62 /*PGresult *res;*/
65 /******************************************************************************
67 The (psuedo?)literate programming XML is contained within \@\@\- <XML> \-\@\@
68 tags in the comments. With in the tags, the XML is assembled sequentially.
69 You can define entities in tags. You also have all the #defines available as
70 entities.
72 Please note that all tags must be lowercase to use the DocBook XML DTD.
74 @@-<article>
76 <sect1>
77 <title>Quick Reference</title>
78 <!-- The refentry forms a manpage -->
79 <refentry>
80 <refmeta>
81 <manvolnum>5<manvolnum>
82 </refmeta>
83 <refnamdiv>
84 <refname>&progname;</refname>
85 <refpurpose>&SUMMARY;</refpurpose>
86 </refnamdiv>
87 </refentry>
88 </sect1>
90 <sect1>
91 <title>FAQ</title>
92 </sect1>
94 <sect1>
95 <title>Theory, Installation, and Operation</title>
97 <sect2>
98 <title>General Description</title>
99 <para>
100 &DESCRIPTION;
101 </para>
102 </sect2>
104 <sect2>
105 <title>Future Enhancements</title>
106 <para>ToDo List</para>
107 <itemizedlist>
108 <listitem>Add option to get password from a secured file rather than the command line</listitem>
109 <listitem>Add option to specify the query to execute</listitem>
110 </itemizedlist>
111 </sect2>
114 <sect2>
115 <title>Functions</title>
116 -@@
117 ******************************************************************************/
121 int
122 main (int argc, char **argv)
123 {
124 int elapsed_time, status;
126 /* begin, by setting the parameters for a backend connection if the
127 * parameters are null, then the system will try to use reasonable
128 * defaults by looking up environment variables or, failing that,
129 * using hardwired constants */
131 pgoptions = NULL; /* special options to start up the backend server */
132 pgtty = NULL; /* debugging tty for the backend server */
134 setlocale (LC_ALL, "");
135 bindtextdomain (PACKAGE, LOCALEDIR);
136 textdomain (PACKAGE);
138 if (process_arguments (argc, argv) != TRUE)
139 usage4 (_("Could not parse arguments"));
141 /* Set signal handling and alarm */
142 if (signal (SIGALRM, timeout_alarm_handler) == SIG_ERR) {
143 usage4 (_("Cannot catch SIGALRM"));
144 }
145 alarm (timeout_interval);
147 /* make a connection to the database */
148 time (&start_time);
149 conn =
150 PQsetdbLogin (pghost, pgport, pgoptions, pgtty, dbName, pguser, pgpasswd);
151 time (&end_time);
152 elapsed_time = (int) (end_time - start_time);
154 /* check to see that the backend connection was successfully made */
155 if (PQstatus (conn) == CONNECTION_BAD) {
156 printf (_("CRITICAL - no connection to '%s' (%s).\n"),
157 dbName, PQerrorMessage (conn));
158 PQfinish (conn);
159 return STATE_CRITICAL;
160 }
161 else if (elapsed_time > tcrit) {
162 status = STATE_CRITICAL;
163 }
164 else if (elapsed_time > twarn) {
165 status = STATE_WARNING;
166 }
167 else {
168 status = STATE_OK;
169 }
170 PQfinish (conn);
171 printf (_(" %s - database %s (%d sec.)|%s\n"),
172 state_text(status), dbName, elapsed_time,
173 fperfdata("time", elapsed_time, "s",
174 (int)twarn, twarn, (int)tcrit, tcrit, TRUE, 0, FALSE,0));
175 return status;
176 }
180 /* process command-line arguments */
181 int
182 process_arguments (int argc, char **argv)
183 {
184 int c;
186 int option = 0;
187 static struct option longopts[] = {
188 {"help", no_argument, 0, 'h'},
189 {"version", no_argument, 0, 'V'},
190 {"timeout", required_argument, 0, 't'},
191 {"critical", required_argument, 0, 'c'},
192 {"warning", required_argument, 0, 'w'},
193 {"hostname", required_argument, 0, 'H'},
194 {"logname", required_argument, 0, 'l'},
195 {"password", required_argument, 0, 'p'},
196 {"authorization", required_argument, 0, 'a'},
197 {"port", required_argument, 0, 'P'},
198 {"database", required_argument, 0, 'd'},
199 {0, 0, 0, 0}
200 };
202 while (1) {
203 c = getopt_long (argc, argv, "hVt:c:w:H:P:d:l:p:a:",
204 longopts, &option);
206 if (c == EOF)
207 break;
209 switch (c) {
210 case '?': /* usage */
211 printf (_("%s: Unknown argument: %s\n\n"), progname, optarg);
212 print_usage ();
213 exit (STATE_UNKNOWN);
214 case 'h': /* help */
215 print_help ();
216 exit (STATE_OK);
217 case 'V': /* version */
218 print_revision (progname, revision);
219 exit (STATE_OK);
220 case 't': /* timeout period */
221 if (!is_integer (optarg))
222 usage2 (_("Timeout interval must be a positive integer"), optarg);
223 else
224 timeout_interval = atoi (optarg);
225 break;
226 case 'c': /* critical time threshold */
227 if (!is_nonnegative (optarg))
228 usage2 (_("Critical threshold must be a positive integer"), optarg);
229 else
230 tcrit = strtod (optarg, NULL);
231 break;
232 case 'w': /* warning time threshold */
233 if (!is_nonnegative (optarg))
234 usage2 (_("Critical threshold must be a positive integer"), optarg);
235 else
236 twarn = strtod (optarg, NULL);
237 break;
238 case 'H': /* host */
239 if (!is_host (optarg))
240 usage2 (_("Invalid hostname/address"), optarg);
241 else
242 pghost = optarg;
243 break;
244 case 'P': /* port */
245 if (!is_integer (optarg))
246 usage2 (_("Port must be a positive integer"), optarg);
247 else
248 pgport = optarg;
249 break;
250 case 'd': /* database name */
251 if (!is_pg_dbname (optarg)) /* checks length and valid chars */
252 usage2 (_("Database name is not valid"), optarg);
253 else /* we know length, and know optarg is terminated, so us strcpy */
254 strcpy (dbName, optarg);
255 break;
256 case 'l': /* login name */
257 if (!is_pg_logname (optarg))
258 usage2 (_("user name is not valid"), optarg);
259 else
260 pguser = optarg;
261 break;
262 case 'p': /* authentication password */
263 case 'a':
264 pgpasswd = optarg;
265 break;
266 }
267 }
269 return validate_arguments ();
270 }
273 /******************************************************************************
275 @@-
276 <sect3>
277 <title>validate_arguments</title>
279 <para>&PROTO_validate_arguments;</para>
281 <para>Given a database name, this function returns TRUE if the string
282 is a valid PostgreSQL database name, and returns false if it is
283 not.</para>
285 <para>Valid PostgreSQL database names are less than &NAMEDATALEN;
286 characters long and consist of letters, numbers, and underscores. The
287 first character cannot be a number, however.</para>
289 </sect3>
290 -@@
291 ******************************************************************************/
295 int
296 validate_arguments ()
297 {
298 return OK;
299 }
302 /******************************************************************************
304 @@-
305 <sect3>
306 <title>is_pg_dbname</title>
308 <para>&PROTO_is_pg_dbname;</para>
310 <para>Given a database name, this function returns TRUE if the string
311 is a valid PostgreSQL database name, and returns false if it is
312 not.</para>
314 <para>Valid PostgreSQL database names are less than &NAMEDATALEN;
315 characters long and consist of letters, numbers, and underscores. The
316 first character cannot be a number, however.</para>
318 </sect3>
319 -@@
320 ******************************************************************************/
324 int
325 is_pg_dbname (char *dbname)
326 {
327 char txt[NAMEDATALEN];
328 char tmp[NAMEDATALEN];
329 if (strlen (dbname) > NAMEDATALEN - 1)
330 return (FALSE);
331 strncpy (txt, dbname, NAMEDATALEN - 1);
332 txt[NAMEDATALEN - 1] = 0;
333 if (sscanf (txt, "%[_a-zA-Z]%[^_a-zA-Z0-9]", tmp, tmp) == 1)
334 return (TRUE);
335 if (sscanf (txt, "%[_a-zA-Z]%[_a-zA-Z0-9]%[^_a-zA-Z0-9]", tmp, tmp, tmp) ==
336 2) return (TRUE);
337 return (FALSE);
338 }
340 /**
342 the tango program should eventually create an entity here based on the
343 function prototype
345 @@-
346 <sect3>
347 <title>is_pg_logname</title>
349 <para>&PROTO_is_pg_logname;</para>
351 <para>Given a username, this function returns TRUE if the string is a
352 valid PostgreSQL username, and returns false if it is not. Valid PostgreSQL
353 usernames are less than &NAMEDATALEN; characters long and consist of
354 letters, numbers, dashes, and underscores, plus possibly some other
355 characters.</para>
357 <para>Currently this function only checks string length. Additional checks
358 should be added.</para>
360 </sect3>
361 -@@
362 ******************************************************************************/
366 int
367 is_pg_logname (char *username)
368 {
369 if (strlen (username) > NAMEDATALEN - 1)
370 return (FALSE);
371 return (TRUE);
372 }
374 /******************************************************************************
375 @@-
376 </sect2>
377 </sect1>
378 </article>
379 -@@
380 ******************************************************************************/
384 void
385 print_help (void)
386 {
387 char *myport;
389 asprintf (&myport, "%d", DEFAULT_PORT);
391 print_revision (progname, revision);
393 printf (_(COPYRIGHT), copyright, email);
395 printf (_("Test whether a PostgreSQL Database is accepting connections.\n\n"));
397 print_usage ();
399 printf (_(UT_HELP_VRSN));
401 printf (_(UT_HOST_PORT), 'P', myport);
403 printf (_(UT_IPv46));
405 printf (S_("\
406 -d, --database=STRING\n\
407 Database to check (default: %s)\n\
408 -l, --logname = STRING\n\
409 Login name of user\n\
410 -p, --password = STRING\n\
411 Password (BIG SECURITY ISSUE)\n"), DEFAULT_DB);
413 printf (_(UT_WARN_CRIT));
415 printf (_(UT_TIMEOUT), DEFAULT_SOCKET_TIMEOUT);
417 printf (_(UT_VERBOSE));
419 printf (S_("\nAll parameters are optional.\n\
420 \n\
421 This plugin tests a PostgreSQL DBMS to determine whether it is active and\n\
422 accepting queries. In its current operation, it simply connects to the\n\
423 specified database, and then disconnects. If no database is specified, it\n\
424 connects to the template1 database, which is present in every functioning \n\
425 PostgreSQL DBMS.\n"));
426 printf (S_("\n\
427 The plugin will connect to a local postmaster if no host is specified. To\n\
428 connect to a remote host, be sure that the remote postmaster accepts TCP/IP\n\
429 connections (start the postmaster with the -i option).\n"));
430 printf (S_("\n\
431 Typically, the nagios user (unless the --logname option is used) should be\n\
432 able to connect to the database without a password. The plugin can also send\n\
433 a password, but no effort is made to obsure or encrypt the password.\n"));
435 printf (_(UT_SUPPORT));
436 }
440 void
441 print_usage (void)
442 {
443 printf (S_("\
444 Usage:\n %s [-H <host>] [-P <port>] [-c <critical time>] [-w <warning time>]\n\
445 [-t <timeout>]"), progname);
446 printf (S_("[-d <database>] [-l <logname>] [-p <password>]\n"));
447 printf (S_("\
448 %s (-h | --help) for detailed help\n\
449 %s (-V | --version) for version information\n"),
450 progname, progname);
451 }