Code

de76735bf1cc14c5a2c498321f7deca53e424546
[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  $Id$
18  
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)
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;
180 /* process command-line arguments */
181 int
182 process_arguments (int argc, char **argv)
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 ();
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 ()
298         return OK;
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)
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);
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)
369         if (strlen (username) > NAMEDATALEN - 1)
370                 return (FALSE);
371         return (TRUE);
374 /******************************************************************************
375 @@-
376 </sect2> 
377 </sect1>
378 </article>
379 -@@
380 ******************************************************************************/
384 void
385 print_help (void)
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));
440 void
441 print_usage (void)
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);