Code

use fperfdata
[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 double twarn = (double)DEFAULT_WARN;
49 double tcrit = (double)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, 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                 fperfdata("time", elapsed_time, "s",
172                          (int)twarn, twarn, (int)tcrit, tcrit, TRUE, 0, FALSE,0));
173         return status;
175 \f
178 /* process command-line arguments */
179 int
180 process_arguments (int argc, char **argv)
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_nonnegative (optarg))
225                                 usage2 (_("Invalid critical threshold"), optarg);
226                         else
227                                 tcrit = strtod (optarg, NULL);
228                         break;
229                 case 'w':     /* warning time threshold */
230                         if (!is_nonnegative (optarg))
231                                 usage2 (_("Invalid critical threshold"), optarg);
232                         else
233                                 twarn = strtod (optarg, NULL);
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 ();
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 ()
293         return OK;
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)
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);
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)
361         if (strlen (username) > NAMEDATALEN - 1)
362                 return (FALSE);
363         return (TRUE);
366 /******************************************************************************
367 @@-
368 </sect2> 
369 </sect1>
370 </article>
371 -@@
372 ******************************************************************************/
373 \f
378 void
379 print_help (void)
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));
435 void
436 print_usage (void)
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);