Code

make status code extensible (thanks to Chris Wilson <chris@netservers.co.uk>)
[nagiosplug.git] / plugins / check_pgsql.c
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)
121         int elapsed_time;
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"), dbName,
155                                                 PQerrorMessage (conn));
156                 PQfinish (conn);
157                 return STATE_CRITICAL;
158         }
159         else if (elapsed_time > tcrit) {
160                 PQfinish (conn);
161                 printf (_("PGSQL: CRITICAL - database %s (%d sec.)\n"), dbName,
162                                                 elapsed_time);
163                 return STATE_CRITICAL;
164         }
165         else if (elapsed_time > twarn) {
166                 PQfinish (conn);
167                 printf (_("PGSQL: WARNING - database %s (%d sec.)\n"), dbName, elapsed_time);
168                 return STATE_WARNING;
169         }
170         else {
171                 PQfinish (conn);
172                 printf (_("PGSQL: ok - database %s (%d sec.)\n"), dbName, elapsed_time);
173                 return STATE_OK;
174         }
176 \f
179 /* process command-line arguments */
180 int
181 process_arguments (int argc, char **argv)
183         int c;
185         int option = 0;
186         static struct option longopts[] = {
187                 {"help", no_argument, 0, 'h'},
188                 {"version", no_argument, 0, 'V'},
189                 {"timeout", required_argument, 0, 't'},
190                 {"critical", required_argument, 0, 'c'},
191                 {"warning", required_argument, 0, 'w'},
192                 {"hostname", required_argument, 0, 'H'},
193                 {"logname", required_argument, 0, 'l'},
194                 {"password", required_argument, 0, 'p'},
195                 {"authorization", required_argument, 0, 'a'},
196                 {"port", required_argument, 0, 'P'},
197                 {"database", required_argument, 0, 'd'},
198                 {0, 0, 0, 0}
199         };
201         while (1) {
202                 c = getopt_long (argc, argv, "hVt:c:w:H:P:d:l:p:a:",
203                                  longopts, &option);
205                 if (c == EOF)
206                         break;
208                 switch (c) {
209                 case '?':     /* usage */
210                         usage3 (_("Unknown argument"), optopt);
211                         break;
212                 case 'h':     /* help */
213                         print_help ();
214                         exit (STATE_OK);
215                 case 'V':     /* version */
216                         print_revision (progname, revision);
217                         exit (STATE_OK);
218                 case 't':     /* timeout period */
219                         if (!is_integer (optarg))
220                                 usage2 (_("Timeout Interval must be an integer"), optarg);
221                         else
222                                 timeout_interval = atoi (optarg);
223                         break;
224                 case 'c':     /* critical time threshold */
225                         if (!is_integer (optarg))
226                                 usage2 (_("Invalid critical threshold"), optarg);
227                         else
228                                 tcrit = atoi (optarg);
229                         break;
230                 case 'w':     /* warning time threshold */
231                         if (!is_integer (optarg))
232                                 usage2 (_("Invalid critical threshold"), optarg);
233                         else
234                                 twarn = atoi (optarg);
235                         break;
236                 case 'H':     /* host */
237                         if (!is_host (optarg))
238                                 usage2 (_("You gave an invalid host name"), optarg);
239                         else
240                                 pghost = optarg;
241                         break;
242                 case 'P':     /* port */
243                         if (!is_integer (optarg))
244                                 usage2 (_("Port must be an integer"), optarg);
245                         else
246                                 pgport = optarg;
247                         break;
248                 case 'd':     /* database name */
249                         if (!is_pg_dbname (optarg)) /* checks length and valid chars */
250                                 usage2 (_("Database name is not valid"), optarg);
251                         else /* we know length, and know optarg is terminated, so us strcpy */
252                                 strcpy (dbName, optarg);
253                         break;
254                 case 'l':     /* login name */
255                         if (!is_pg_logname (optarg))
256                                 usage2 (_("user name is not valid"), optarg);
257                         else
258                                 pguser = optarg;
259                         break;
260                 case 'p':     /* authentication password */
261                 case 'a':
262                         pgpasswd = optarg;
263                         break;
264                 }
265         }
267         return validate_arguments ();
271 /******************************************************************************
273 @@-
274 <sect3>
275 <title>validate_arguments</title>
277 <para>&PROTO_validate_arguments;</para>
279 <para>Given a database name, this function returns TRUE if the string
280 is a valid PostgreSQL database name, and returns false if it is
281 not.</para>
283 <para>Valid PostgreSQL database names are less than &NAMEDATALEN;
284 characters long and consist of letters, numbers, and underscores. The
285 first character cannot be a number, however.</para>
287 </sect3>
288 -@@
289 ******************************************************************************/
291 int
292 validate_arguments ()
294         return OK;
296 \f
299 /******************************************************************************
301 @@-
302 <sect3>
303 <title>is_pg_dbname</title>
305 <para>&PROTO_is_pg_dbname;</para>
307 <para>Given a database name, this function returns TRUE if the string
308 is a valid PostgreSQL database name, and returns false if it is
309 not.</para>
311 <para>Valid PostgreSQL database names are less than &NAMEDATALEN;
312 characters long and consist of letters, numbers, and underscores. The
313 first character cannot be a number, however.</para>
315 </sect3>
316 -@@
317 ******************************************************************************/
319 int
320 is_pg_dbname (char *dbname)
322         char txt[NAMEDATALEN];
323         char tmp[NAMEDATALEN];
324         if (strlen (dbname) > NAMEDATALEN - 1)
325                 return (FALSE);
326         strncpy (txt, dbname, NAMEDATALEN - 1);
327         txt[NAMEDATALEN - 1] = 0;
328         if (sscanf (txt, "%[_a-zA-Z]%[^_a-zA-Z0-9]", tmp, tmp) == 1)
329                 return (TRUE);
330         if (sscanf (txt, "%[_a-zA-Z]%[_a-zA-Z0-9]%[^_a-zA-Z0-9]", tmp, tmp, tmp) ==
331                         2) return (TRUE);
332         return (FALSE);
335 /**
337 the tango program should eventually create an entity here based on the 
338 function prototype
340 @@-
341 <sect3>
342 <title>is_pg_logname</title>
344 <para>&PROTO_is_pg_logname;</para>
346 <para>Given a username, this function returns TRUE if the string is a
347 valid PostgreSQL username, and returns false if it is not. Valid PostgreSQL
348 usernames are less than &NAMEDATALEN; characters long and consist of
349 letters, numbers, dashes, and underscores, plus possibly some other
350 characters.</para>
352 <para>Currently this function only checks string length. Additional checks
353 should be added.</para>
355 </sect3>
356 -@@
357 ******************************************************************************/
359 int
360 is_pg_logname (char *username)
362         if (strlen (username) > NAMEDATALEN - 1)
363                 return (FALSE);
364         return (TRUE);
367 /******************************************************************************
368 @@-
369 </sect2> 
370 </sect1>
371 </article>
372 -@@
373 ******************************************************************************/
374 \f
379 void
380 print_help (void)
382         char *myport;
384         asprintf (&myport, "%d", DEFAULT_PORT);
386         print_revision (progname, revision);
388         printf (_(COPYRIGHT), copyright, email);
390         printf (_("Test whether a PostgreSQL DBMS is accepting connections.\n\n"));
392         print_usage ();
394         printf (_(UT_HELP_VRSN));
396         printf (_(UT_HOST_PORT), 'P', myport);
398         printf (_(UT_IPv46));
400         printf (S_("\
401   -d, --database=STRING\n\
402     Database to check (default: %s)\n\
403   -l, --logname = STRING\n\
404     Login name of user\n\
405   -p, --password = STRING\n\
406     Password (BIG SECURITY ISSUE)\n"), DEFAULT_DB);
408         printf (_(UT_WARN_CRIT));
410         printf (_(UT_TIMEOUT), DEFAULT_SOCKET_TIMEOUT);
412         printf (_(UT_VERBOSE));
414         printf (S_("\nAll parameters are optional.\n\
415 \n\
416 This plugin tests a PostgreSQL DBMS to determine whether it is active and\n\
417 accepting queries. In its current operation, it simply connects to the\n\
418 specified database, and then disconnects. If no database is specified, it\n\
419 connects to the template1 database, which is present in every functioning \n\
420 PostgreSQL DBMS.\n"));
421         printf (S_("\n\
422 The plugin will connect to a local postmaster if no host is specified. To\n\
423 connect to a remote host, be sure that the remote postmaster accepts TCP/IP\n\
424 connections (start the postmaster with the -i option).\n"));
425         printf (S_("\n\
426 Typically, the nagios user (unless the --logname option is used) should be\n\
427 able to connect to the database without a password. The plugin can also send\n\
428 a password, but no effort is made to obsure or encrypt the password.\n"));
430         printf (_(UT_SUPPORT));
436 void
437 print_usage (void)
439         printf (S_("\
440 Usage:\n %s [-H <host>] [-P <port>] [-c <critical time>] [-w <warning time>]\n\
441             [-t <timeout>]"), progname);
442         printf (S_("[-d <database>] [-l <logname>] [-p <password>]\n"));
443         printf (S_("\
444          %s (-h | --help) for detailed help\n\
445          %s (-V | --version) for version information\n"),
446                                         progname, progname);