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;
125 int status = STATE_UNKNOWN;
127 /* begin, by setting the parameters for a backend connection if the
128 * parameters are null, then the system will try to use reasonable
129 * defaults by looking up environment variables or, failing that,
130 * using hardwired constants */
132 pgoptions = NULL; /* special options to start up the backend server */
133 pgtty = NULL; /* debugging tty for the backend server */
135 setlocale (LC_ALL, "");
136 bindtextdomain (PACKAGE, LOCALEDIR);
137 textdomain (PACKAGE);
139 if (process_arguments (argc, argv) == ERROR)
140 usage4 (_("Could not parse arguments"));
142 /* Set signal handling and alarm */
143 if (signal (SIGALRM, timeout_alarm_handler) == SIG_ERR) {
144 usage4 (_("Cannot catch SIGALRM"));
145 }
146 alarm (timeout_interval);
148 /* make a connection to the database */
149 time (&start_time);
150 conn =
151 PQsetdbLogin (pghost, pgport, pgoptions, pgtty, dbName, pguser, pgpasswd);
152 time (&end_time);
153 elapsed_time = (int) (end_time - start_time);
155 /* check to see that the backend connection was successfully made */
156 if (PQstatus (conn) == CONNECTION_BAD) {
157 printf (_("CRITICAL - no connection to '%s' (%s).\n"),
158 dbName, PQerrorMessage (conn));
159 PQfinish (conn);
160 return STATE_CRITICAL;
161 }
162 else if (elapsed_time > tcrit) {
163 status = STATE_CRITICAL;
164 }
165 else if (elapsed_time > twarn) {
166 status = STATE_WARNING;
167 }
168 else {
169 status = STATE_OK;
170 }
171 PQfinish (conn);
172 printf (_(" %s - database %s (%d sec.)|%s\n"),
173 state_text(status), dbName, elapsed_time,
174 fperfdata("time", elapsed_time, "s",
175 (int)twarn, twarn, (int)tcrit, tcrit, TRUE, 0, FALSE,0));
176 return status;
177 }
181 /* process command-line arguments */
182 int
183 process_arguments (int argc, char **argv)
184 {
185 int c;
187 int option = 0;
188 static struct option longopts[] = {
189 {"help", no_argument, 0, 'h'},
190 {"version", no_argument, 0, 'V'},
191 {"timeout", required_argument, 0, 't'},
192 {"critical", required_argument, 0, 'c'},
193 {"warning", required_argument, 0, 'w'},
194 {"hostname", required_argument, 0, 'H'},
195 {"logname", required_argument, 0, 'l'},
196 {"password", required_argument, 0, 'p'},
197 {"authorization", required_argument, 0, 'a'},
198 {"port", required_argument, 0, 'P'},
199 {"database", required_argument, 0, 'd'},
200 {0, 0, 0, 0}
201 };
203 while (1) {
204 c = getopt_long (argc, argv, "hVt:c:w:H:P:d:l:p:a:",
205 longopts, &option);
207 if (c == EOF)
208 break;
210 switch (c) {
211 case '?': /* usage */
212 printf (_("%s: Unknown argument: %s\n\n"), progname, optarg);
213 print_usage ();
214 exit (STATE_UNKNOWN);
215 case 'h': /* help */
216 print_help ();
217 exit (STATE_OK);
218 case 'V': /* version */
219 print_revision (progname, revision);
220 exit (STATE_OK);
221 case 't': /* timeout period */
222 if (!is_integer (optarg))
223 usage2 (_("Timeout interval must be a positive integer"), optarg);
224 else
225 timeout_interval = atoi (optarg);
226 break;
227 case 'c': /* critical time threshold */
228 if (!is_nonnegative (optarg))
229 usage2 (_("Critical threshold must be a positive integer"), optarg);
230 else
231 tcrit = strtod (optarg, NULL);
232 break;
233 case 'w': /* warning time threshold */
234 if (!is_nonnegative (optarg))
235 usage2 (_("Critical threshold must be a positive integer"), optarg);
236 else
237 twarn = strtod (optarg, NULL);
238 break;
239 case 'H': /* host */
240 if (!is_host (optarg))
241 usage2 (_("Invalid hostname/address"), optarg);
242 else
243 pghost = optarg;
244 break;
245 case 'P': /* port */
246 if (!is_integer (optarg))
247 usage2 (_("Port must be a positive integer"), optarg);
248 else
249 pgport = optarg;
250 break;
251 case 'd': /* database name */
252 if (!is_pg_dbname (optarg)) /* checks length and valid chars */
253 usage2 (_("Database name is not valid"), optarg);
254 else /* we know length, and know optarg is terminated, so us strcpy */
255 strcpy (dbName, optarg);
256 break;
257 case 'l': /* login name */
258 if (!is_pg_logname (optarg))
259 usage2 (_("User name is not valid"), optarg);
260 else
261 pguser = optarg;
262 break;
263 case 'p': /* authentication password */
264 case 'a':
265 pgpasswd = optarg;
266 break;
267 }
268 }
270 return validate_arguments ();
271 }
274 /******************************************************************************
276 @@-
277 <sect3>
278 <title>validate_arguments</title>
280 <para>&PROTO_validate_arguments;</para>
282 <para>Given a database name, this function returns TRUE if the string
283 is a valid PostgreSQL database name, and returns false if it is
284 not.</para>
286 <para>Valid PostgreSQL database names are less than &NAMEDATALEN;
287 characters long and consist of letters, numbers, and underscores. The
288 first character cannot be a number, however.</para>
290 </sect3>
291 -@@
292 ******************************************************************************/
296 int
297 validate_arguments ()
298 {
299 return OK;
300 }
303 /******************************************************************************
305 @@-
306 <sect3>
307 <title>is_pg_dbname</title>
309 <para>&PROTO_is_pg_dbname;</para>
311 <para>Given a database name, this function returns TRUE if the string
312 is a valid PostgreSQL database name, and returns false if it is
313 not.</para>
315 <para>Valid PostgreSQL database names are less than &NAMEDATALEN;
316 characters long and consist of letters, numbers, and underscores. The
317 first character cannot be a number, however.</para>
319 </sect3>
320 -@@
321 ******************************************************************************/
325 int
326 is_pg_dbname (char *dbname)
327 {
328 char txt[NAMEDATALEN];
329 char tmp[NAMEDATALEN];
330 if (strlen (dbname) > NAMEDATALEN - 1)
331 return (FALSE);
332 strncpy (txt, dbname, NAMEDATALEN - 1);
333 txt[NAMEDATALEN - 1] = 0;
334 if (sscanf (txt, "%[_a-zA-Z]%[^_a-zA-Z0-9]", tmp, tmp) == 1)
335 return (TRUE);
336 if (sscanf (txt, "%[_a-zA-Z]%[_a-zA-Z0-9]%[^_a-zA-Z0-9]", tmp, tmp, tmp) ==
337 2) return (TRUE);
338 return (FALSE);
339 }
341 /**
343 the tango program should eventually create an entity here based on the
344 function prototype
346 @@-
347 <sect3>
348 <title>is_pg_logname</title>
350 <para>&PROTO_is_pg_logname;</para>
352 <para>Given a username, this function returns TRUE if the string is a
353 valid PostgreSQL username, and returns false if it is not. Valid PostgreSQL
354 usernames are less than &NAMEDATALEN; characters long and consist of
355 letters, numbers, dashes, and underscores, plus possibly some other
356 characters.</para>
358 <para>Currently this function only checks string length. Additional checks
359 should be added.</para>
361 </sect3>
362 -@@
363 ******************************************************************************/
367 int
368 is_pg_logname (char *username)
369 {
370 if (strlen (username) > NAMEDATALEN - 1)
371 return (FALSE);
372 return (TRUE);
373 }
375 /******************************************************************************
376 @@-
377 </sect2>
378 </sect1>
379 </article>
380 -@@
381 ******************************************************************************/
385 void
386 print_help (void)
387 {
388 char *myport;
390 asprintf (&myport, "%d", DEFAULT_PORT);
392 print_revision (progname, revision);
394 printf (COPYRIGHT, copyright, email);
396 printf (_("Test whether a PostgreSQL Database is accepting connections.\n\n"));
398 print_usage ();
400 printf (_(UT_HELP_VRSN));
402 printf (_(UT_HOST_PORT), 'P', myport);
404 printf (_(UT_IPv46));
406 printf (S_("\
407 -d, --database=STRING\n\
408 Database to check (default: %s)\n\
409 -l, --logname = STRING\n\
410 Login name of user\n\
411 -p, --password = STRING\n\
412 Password (BIG SECURITY ISSUE)\n"), DEFAULT_DB);
414 printf (_(UT_WARN_CRIT));
416 printf (_(UT_TIMEOUT), DEFAULT_SOCKET_TIMEOUT);
418 printf (_(UT_VERBOSE));
420 printf (S_("\nAll parameters are optional.\n\
421 \n\
422 This plugin tests a PostgreSQL DBMS to determine whether it is active and\n\
423 accepting queries. In its current operation, it simply connects to the\n\
424 specified database, and then disconnects. If no database is specified, it\n\
425 connects to the template1 database, which is present in every functioning \n\
426 PostgreSQL DBMS.\n"));
427 printf (S_("\n\
428 The plugin will connect to a local postmaster if no host is specified. To\n\
429 connect to a remote host, be sure that the remote postmaster accepts TCP/IP\n\
430 connections (start the postmaster with the -i option).\n"));
431 printf (S_("\n\
432 Typically, the nagios user (unless the --logname option is used) should be\n\
433 able to connect to the database without a password. The plugin can also send\n\
434 a password, but no effort is made to obsure or encrypt the password.\n"));
436 printf (_(UT_SUPPORT));
437 }
441 void
442 print_usage (void)
443 {
444 printf ("\
445 Usage: %s [-H <host>] [-P <port>] [-c <critical time>] [-w <warning time>]\n\
446 [-t <timeout>] [-d <database>] [-l <logname>] [-p <password>]\n", progname);
447 }