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 *****************************************************************************/
19 #define DEFAULT_DB "template1"
20 #define DEFAULT_HOST "127.0.0.1"
22 enum {
23 DEFAULT_PORT = 5432,
24 DEFAULT_WARN = 2,
25 DEFAULT_CRIT = 8
26 };
28 #include "common.h"
29 #include "utils.h"
30 #include "netutils.h"
31 #include <libpq-fe.h>
33 int process_arguments (int, char **);
34 int validate_arguments (void);
35 void print_usage (void);
36 void print_help (void);
37 int is_pg_dbname (char *);
38 int is_pg_logname (char *);
40 char *pghost = NULL; /* host name of the backend server */
41 char *pgport = NULL; /* port of the backend server */
42 int default_port = DEFAULT_PORT;
43 char *pgoptions = NULL;
44 char *pgtty = NULL;
45 char dbName[NAMEDATALEN] = DEFAULT_DB;
46 char *pguser = NULL;
47 char *pgpasswd = NULL;
48 int twarn = DEFAULT_WARN;
49 int tcrit = DEFAULT_CRIT;
51 PGconn *conn;
52 /*PGresult *res;*/
54 const char *progname = "check_pgsql";
55 const char *revision = "$Revision$";
56 const char *copyright = "1999-2003";
57 const char *email = "nagiosplug-devel@lists.sourceforge.net";
58 \f
60 /******************************************************************************
62 The (psuedo?)literate programming XML is contained within \@\@\- <XML> \-\@\@
63 tags in the comments. With in the tags, the XML is assembled sequentially.
64 You can define entities in tags. You also have all the #defines available as
65 entities.
67 Please note that all tags must be lowercase to use the DocBook XML DTD.
69 @@-<article>
71 <sect1>
72 <title>Quick Reference</title>
73 <!-- The refentry forms a manpage -->
74 <refentry>
75 <refmeta>
76 <manvolnum>5<manvolnum>
77 </refmeta>
78 <refnamdiv>
79 <refname>&progname;</refname>
80 <refpurpose>&SUMMARY;</refpurpose>
81 </refnamdiv>
82 </refentry>
83 </sect1>
85 <sect1>
86 <title>FAQ</title>
87 </sect1>
89 <sect1>
90 <title>Theory, Installation, and Operation</title>
92 <sect2>
93 <title>General Description</title>
94 <para>
95 &DESCRIPTION;
96 </para>
97 </sect2>
99 <sect2>
100 <title>Future Enhancements</title>
101 <para>ToDo List</para>
102 <itemizedlist>
103 <listitem>Add option to get password from a secured file rather than the command line</listitem>
104 <listitem>Add option to specify the query to execute</listitem>
105 </itemizedlist>
106 </sect2>
109 <sect2>
110 <title>Functions</title>
111 -@@
112 ******************************************************************************/
113 \f
118 int
119 main (int argc, char **argv)
120 {
121 int elapsed_time, status;
123 /* begin, by setting the parameters for a backend connection if the
124 * parameters are null, then the system will try to use reasonable
125 * defaults by looking up environment variables or, failing that,
126 * using hardwired constants */
128 pgoptions = NULL; /* special options to start up the backend server */
129 pgtty = NULL; /* debugging tty for the backend server */
131 setlocale (LC_ALL, "");
132 bindtextdomain (PACKAGE, LOCALEDIR);
133 textdomain (PACKAGE);
135 if (process_arguments (argc, argv) == ERROR)
136 usage ("Could not parse arguments");
138 /* Set signal handling and alarm */
139 if (signal (SIGALRM, timeout_alarm_handler) == SIG_ERR) {
140 printf (_("Cannot catch SIGALRM"));
141 return STATE_UNKNOWN;
142 }
143 alarm (timeout_interval);
145 /* make a connection to the database */
146 time (&start_time);
147 conn =
148 PQsetdbLogin (pghost, pgport, pgoptions, pgtty, dbName, pguser, pgpasswd);
149 time (&end_time);
150 elapsed_time = (int) (end_time - start_time);
152 /* check to see that the backend connection was successfully made */
153 if (PQstatus (conn) == CONNECTION_BAD) {
154 printf (_("PGSQL: CRITICAL - no connection to '%s' (%s).\n"),
155 dbName, PQerrorMessage (conn));
156 PQfinish (conn);
157 return STATE_CRITICAL;
158 }
159 else if (elapsed_time > tcrit) {
160 status = STATE_CRITICAL;
161 }
162 else if (elapsed_time > twarn) {
163 status = STATE_WARNING;
164 }
165 else {
166 status = STATE_OK;
167 }
168 PQfinish (conn);
169 printf (_("PGSQL: %s - database %s (%d sec.)|%s\n"),
170 state_text(status), dbName, elapsed_time,
171 perfdata("time", (long)elapsed_time, "s",
172 twarn, (long)twarn, tcrit, (long)tcrit, TRUE, 0, FALSE,0));
173 return status;
174 }
175 \f
178 /* process command-line arguments */
179 int
180 process_arguments (int argc, char **argv)
181 {
182 int c;
184 int option = 0;
185 static struct option longopts[] = {
186 {"help", no_argument, 0, 'h'},
187 {"version", no_argument, 0, 'V'},
188 {"timeout", required_argument, 0, 't'},
189 {"critical", required_argument, 0, 'c'},
190 {"warning", required_argument, 0, 'w'},
191 {"hostname", required_argument, 0, 'H'},
192 {"logname", required_argument, 0, 'l'},
193 {"password", required_argument, 0, 'p'},
194 {"authorization", required_argument, 0, 'a'},
195 {"port", required_argument, 0, 'P'},
196 {"database", required_argument, 0, 'd'},
197 {0, 0, 0, 0}
198 };
200 while (1) {
201 c = getopt_long (argc, argv, "hVt:c:w:H:P:d:l:p:a:",
202 longopts, &option);
204 if (c == EOF)
205 break;
207 switch (c) {
208 case '?': /* usage */
209 usage3 (_("Unknown argument"), optopt);
210 break;
211 case 'h': /* help */
212 print_help ();
213 exit (STATE_OK);
214 case 'V': /* version */
215 print_revision (progname, revision);
216 exit (STATE_OK);
217 case 't': /* timeout period */
218 if (!is_integer (optarg))
219 usage2 (_("Timeout Interval must be an integer"), optarg);
220 else
221 timeout_interval = atoi (optarg);
222 break;
223 case 'c': /* critical time threshold */
224 if (!is_integer (optarg))
225 usage2 (_("Invalid critical threshold"), optarg);
226 else
227 tcrit = atoi (optarg);
228 break;
229 case 'w': /* warning time threshold */
230 if (!is_integer (optarg))
231 usage2 (_("Invalid critical threshold"), optarg);
232 else
233 twarn = atoi (optarg);
234 break;
235 case 'H': /* host */
236 if (!is_host (optarg))
237 usage2 (_("You gave an invalid host name"), optarg);
238 else
239 pghost = optarg;
240 break;
241 case 'P': /* port */
242 if (!is_integer (optarg))
243 usage2 (_("Port must be an integer"), optarg);
244 else
245 pgport = optarg;
246 break;
247 case 'd': /* database name */
248 if (!is_pg_dbname (optarg)) /* checks length and valid chars */
249 usage2 (_("Database name is not valid"), optarg);
250 else /* we know length, and know optarg is terminated, so us strcpy */
251 strcpy (dbName, optarg);
252 break;
253 case 'l': /* login name */
254 if (!is_pg_logname (optarg))
255 usage2 (_("user name is not valid"), optarg);
256 else
257 pguser = optarg;
258 break;
259 case 'p': /* authentication password */
260 case 'a':
261 pgpasswd = optarg;
262 break;
263 }
264 }
266 return validate_arguments ();
267 }
270 /******************************************************************************
272 @@-
273 <sect3>
274 <title>validate_arguments</title>
276 <para>&PROTO_validate_arguments;</para>
278 <para>Given a database name, this function returns TRUE if the string
279 is a valid PostgreSQL database name, and returns false if it is
280 not.</para>
282 <para>Valid PostgreSQL database names are less than &NAMEDATALEN;
283 characters long and consist of letters, numbers, and underscores. The
284 first character cannot be a number, however.</para>
286 </sect3>
287 -@@
288 ******************************************************************************/
290 int
291 validate_arguments ()
292 {
293 return OK;
294 }
295 \f
298 /******************************************************************************
300 @@-
301 <sect3>
302 <title>is_pg_dbname</title>
304 <para>&PROTO_is_pg_dbname;</para>
306 <para>Given a database name, this function returns TRUE if the string
307 is a valid PostgreSQL database name, and returns false if it is
308 not.</para>
310 <para>Valid PostgreSQL database names are less than &NAMEDATALEN;
311 characters long and consist of letters, numbers, and underscores. The
312 first character cannot be a number, however.</para>
314 </sect3>
315 -@@
316 ******************************************************************************/
318 int
319 is_pg_dbname (char *dbname)
320 {
321 char txt[NAMEDATALEN];
322 char tmp[NAMEDATALEN];
323 if (strlen (dbname) > NAMEDATALEN - 1)
324 return (FALSE);
325 strncpy (txt, dbname, NAMEDATALEN - 1);
326 txt[NAMEDATALEN - 1] = 0;
327 if (sscanf (txt, "%[_a-zA-Z]%[^_a-zA-Z0-9]", tmp, tmp) == 1)
328 return (TRUE);
329 if (sscanf (txt, "%[_a-zA-Z]%[_a-zA-Z0-9]%[^_a-zA-Z0-9]", tmp, tmp, tmp) ==
330 2) return (TRUE);
331 return (FALSE);
332 }
334 /**
336 the tango program should eventually create an entity here based on the
337 function prototype
339 @@-
340 <sect3>
341 <title>is_pg_logname</title>
343 <para>&PROTO_is_pg_logname;</para>
345 <para>Given a username, this function returns TRUE if the string is a
346 valid PostgreSQL username, and returns false if it is not. Valid PostgreSQL
347 usernames are less than &NAMEDATALEN; characters long and consist of
348 letters, numbers, dashes, and underscores, plus possibly some other
349 characters.</para>
351 <para>Currently this function only checks string length. Additional checks
352 should be added.</para>
354 </sect3>
355 -@@
356 ******************************************************************************/
358 int
359 is_pg_logname (char *username)
360 {
361 if (strlen (username) > NAMEDATALEN - 1)
362 return (FALSE);
363 return (TRUE);
364 }
366 /******************************************************************************
367 @@-
368 </sect2>
369 </sect1>
370 </article>
371 -@@
372 ******************************************************************************/
373 \f
378 void
379 print_help (void)
380 {
381 char *myport;
383 asprintf (&myport, "%d", DEFAULT_PORT);
385 print_revision (progname, revision);
387 printf (_(COPYRIGHT), copyright, email);
389 printf (_("Test whether a PostgreSQL DBMS is accepting connections.\n\n"));
391 print_usage ();
393 printf (_(UT_HELP_VRSN));
395 printf (_(UT_HOST_PORT), 'P', myport);
397 printf (_(UT_IPv46));
399 printf (S_("\
400 -d, --database=STRING\n\
401 Database to check (default: %s)\n\
402 -l, --logname = STRING\n\
403 Login name of user\n\
404 -p, --password = STRING\n\
405 Password (BIG SECURITY ISSUE)\n"), DEFAULT_DB);
407 printf (_(UT_WARN_CRIT));
409 printf (_(UT_TIMEOUT), DEFAULT_SOCKET_TIMEOUT);
411 printf (_(UT_VERBOSE));
413 printf (S_("\nAll parameters are optional.\n\
414 \n\
415 This plugin tests a PostgreSQL DBMS to determine whether it is active and\n\
416 accepting queries. In its current operation, it simply connects to the\n\
417 specified database, and then disconnects. If no database is specified, it\n\
418 connects to the template1 database, which is present in every functioning \n\
419 PostgreSQL DBMS.\n"));
420 printf (S_("\n\
421 The plugin will connect to a local postmaster if no host is specified. To\n\
422 connect to a remote host, be sure that the remote postmaster accepts TCP/IP\n\
423 connections (start the postmaster with the -i option).\n"));
424 printf (S_("\n\
425 Typically, the nagios user (unless the --logname option is used) should be\n\
426 able to connect to the database without a password. The plugin can also send\n\
427 a password, but no effort is made to obsure or encrypt the password.\n"));
429 printf (_(UT_SUPPORT));
430 }
435 void
436 print_usage (void)
437 {
438 printf (S_("\
439 Usage:\n %s [-H <host>] [-P <port>] [-c <critical time>] [-w <warning time>]\n\
440 [-t <timeout>]"), progname);
441 printf (S_("[-d <database>] [-l <logname>] [-p <password>]\n"));
442 printf (S_("\
443 %s (-h | --help) for detailed help\n\
444 %s (-V | --version) for version information\n"),
445 progname, progname);
446 }