root/src/modules/postgres.c

Revision 88a71780101cbf23034aa0cb840f9f0368fda2dd, 9.0 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 5 years ago)

fixes #126

  • Property mode set to 100644
Line 
1 /*
2  * Copyright (c) 2007, OmniTI Computer Consulting, Inc.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  *     * Redistributions of source code must retain the above copyright
10  *       notice, this list of conditions and the following disclaimer.
11  *     * Redistributions in binary form must reproduce the above
12  *       copyright notice, this list of conditions and the following
13  *       disclaimer in the documentation and/or other materials provided
14  *       with the distribution.
15  *     * Neither the name OmniTI Computer Consulting, Inc. nor the names
16  *       of its contributors may be used to endorse or promote products
17  *       derived from this software without specific prior written
18  *       permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32
33 #include "noit_defines.h"
34
35 #include <stdio.h>
36 #include <unistd.h>
37 #include <errno.h>
38 #include <assert.h>
39 #include <math.h>
40
41 #include "noit_module.h"
42 #include "noit_check.h"
43 #include "noit_check_tools.h"
44 #include "utils/noit_log.h"
45 #include "utils/noit_hash.h"
46
47 #include <libpq-fe.h>
48
49 /* Ripped from postgres 8.3.3 */
50 #ifndef BOOLOID
51 #define BOOLOID                  16
52 #endif
53 #ifndef INT2OID
54 #define INT2OID                  21
55 #endif
56 #ifndef INT4OID
57 #define INT4OID                  23
58 #endif
59 #ifndef INT8OID
60 #define INT8OID                  20
61 #endif
62 #ifndef FLOAT4OID
63 #define FLOAT4OID                700
64 #endif
65 #ifndef FLOAT8OID
66 #define FLOAT8OID                701
67 #endif
68 #ifndef NUMERICOID
69 #define NUMERICOID               1700
70 #endif
71
72 typedef struct {
73   noit_module_t *self;
74   noit_check_t *check;
75   PGconn *conn;
76   PGresult *result;
77   int rv;
78   noit_hash_table attrs;
79   int timed_out;
80   char *error;
81 } postgres_check_info_t;
82
83 static noit_log_stream_t nlerr = NULL;
84 static noit_log_stream_t nldeb = NULL;
85
86 static void postgres_cleanup(noit_module_t *self, noit_check_t *check) {
87   postgres_check_info_t *ci = check->closure;
88   if(ci) {
89     if(ci->result) PQclear(ci->result);
90     if(ci->conn) PQfinish(ci->conn);
91     noit_check_release_attrs(&ci->attrs);
92     if(ci->error) free(ci->error);
93     memset(ci, 0, sizeof(*ci));
94   }
95 }
96 static void postgres_log_results(noit_module_t *self, noit_check_t *check) {
97   stats_t current;
98   struct timeval duration;
99   postgres_check_info_t *ci = check->closure;
100
101   noit_check_stats_clear(&current);
102
103   gettimeofday(&current.whence, NULL);
104   sub_timeval(current.whence, check->last_fire_time, &duration);
105   current.duration = duration.tv_sec * 1000 + duration.tv_usec / 1000;
106   current.available = NP_UNAVAILABLE;
107   current.state = NP_BAD;
108   if(ci->error) current.status = ci->error;
109   else if(ci->timed_out) current.status = "timeout";
110   else if(ci->rv == PGRES_COMMAND_OK) {
111     current.available = NP_AVAILABLE;
112     current.state = NP_GOOD;
113     current.status = "command ok";
114   }
115   else if(ci->rv == PGRES_TUPLES_OK) {
116     current.available = NP_AVAILABLE;
117     current.state = NP_GOOD;
118     current.status = "tuples ok";
119   }
120   else current.status = "internal error";
121
122   if(ci->rv == PGRES_TUPLES_OK) {
123     /* metrics */
124     int nrows, ncols, i, j;
125     nrows = PQntuples(ci->result);
126     ncols = PQnfields(ci->result);
127     for (i=0; i<nrows; i++) {
128       noitL(nldeb, "postgres: row %d [%d cols]:\n", i, ncols);
129       if(ncols<2) continue;
130       if(PQgetisnull(ci->result, i, 0)) continue;
131       for (j=1; j<ncols; j++) {
132         Oid coltype;
133         int iv, *piv;
134         int64_t lv, *plv;
135         double dv, *pdv;
136         char *sv;
137         char mname[128];
138  
139         snprintf(mname, sizeof(mname), "%s`%s",
140                  PQgetvalue(ci->result, i, 0), PQfname(ci->result, j));
141         coltype = PQftype(ci->result, j);
142         noitL(nldeb, "postgres:   col %d (%s) type %d:\n", j, mname, coltype);
143         switch(coltype) {
144           case BOOLOID:
145             if(PQgetisnull(ci->result, i, j)) piv = NULL;
146             else {
147               iv = strcmp(PQgetvalue(ci->result, i, j), "f") ? 1 : 0;
148               piv = &iv;
149             }
150             noit_stats_set_metric(&current, mname, METRIC_INT32, piv);
151             break;
152           case INT2OID:
153           case INT4OID:
154           case INT8OID:
155             if(PQgetisnull(ci->result, i, j)) plv = NULL;
156             else {
157               lv = strtoll(PQgetvalue(ci->result, i, j), NULL, 10);
158               plv = &lv;
159             }
160             noit_stats_set_metric(&current, mname, METRIC_INT64, plv);
161           case FLOAT4OID:
162           case FLOAT8OID:
163           case NUMERICOID:
164             if(PQgetisnull(ci->result, i, j)) pdv = NULL;
165             else {
166               dv = atof(PQgetvalue(ci->result, i, j));
167               pdv = &dv;
168             }
169             noit_stats_set_metric(&current, mname, METRIC_DOUBLE, pdv);
170           default:
171             if(PQgetisnull(ci->result, i, j)) sv = NULL;
172             else sv = PQgetvalue(ci->result, i, j);
173             noit_stats_set_metric(&current, mname, METRIC_GUESS, sv);
174             break;
175         }
176       }
177     }
178   }
179   noit_check_set_stats(self, check, &current);
180 }
181
182 #define FETCH_CONFIG_OR(key, str) do { \
183   if(!noit_hash_retr_str(check->config, #key, strlen(#key), &key)) \
184     key = str; \
185 } while(0)
186
187 #define AVAIL_BAIL(str) do { \
188   ci->timed_out = 0; \
189   ci->error = strdup(str); \
190   return 0; \
191 } while(0)
192
193 static int postgres_drive_session(eventer_t e, int mask, void *closure,
194                                   struct timeval *now) {
195   const char *dsn, *sql;
196   char sql_buff[8192];
197   char dsn_buff[512];
198   postgres_check_info_t *ci = closure;
199   noit_check_t *check = ci->check;
200
201   if(mask & (EVENTER_READ | EVENTER_WRITE)) {
202     /* this case is impossible from the eventer.  It is called as
203      * such on the synchronous completion of the event.
204      */
205     postgres_log_results(ci->self, ci->check);
206     postgres_cleanup(ci->self, ci->check);
207     check->flags &= ~NP_RUNNING;
208     return 0;
209   }
210   switch(mask) {
211     case EVENTER_ASYNCH_WORK:
212       FETCH_CONFIG_OR(dsn, "");
213       noit_check_interpolate(dsn_buff, sizeof(dsn_buff), dsn,
214                              &ci->attrs, check->config);
215       ci->conn = PQconnectdb(dsn_buff);
216       if(!ci->conn) AVAIL_BAIL("PQconnectdb failed");
217       if(PQstatus(ci->conn) != CONNECTION_OK)
218         AVAIL_BAIL(PQerrorMessage(ci->conn));
219
220       FETCH_CONFIG_OR(sql, "");
221       noit_check_interpolate(sql_buff, sizeof(sql_buff), sql,
222                              &ci->attrs, check->config);
223       ci->result = PQexec(ci->conn, sql_buff);
224       if(!ci->result) AVAIL_BAIL("PQexec failed");
225       ci->rv = PQresultStatus(ci->result);
226       switch(ci->rv) {
227        case PGRES_TUPLES_OK:
228        case PGRES_COMMAND_OK:
229         break;
230        default:
231         AVAIL_BAIL(PQresultErrorMessage(ci->result));
232       }
233       ci->timed_out = 0;
234       return 0;
235       break;
236     case EVENTER_ASYNCH_CLEANUP:
237       /* This sets us up for a completion call. */
238       e->mask = EVENTER_READ | EVENTER_WRITE;
239       break;
240     default:
241       abort();
242   }
243   return 0;
244 }
245
246 static int postgres_initiate(noit_module_t *self, noit_check_t *check) {
247   postgres_check_info_t *ci = check->closure;
248   struct timeval __now;
249
250   /* We cannot be running */
251   assert(!(check->flags & NP_RUNNING));
252   check->flags |= NP_RUNNING;
253
254   ci->self = self;
255   ci->check = check;
256
257   ci->timed_out = 1;
258   ci->rv = -1;
259   noit_check_make_attrs(check, &ci->attrs);
260   gettimeofday(&__now, NULL);
261   memcpy(&check->last_fire_time, &__now, sizeof(__now));
262
263   /* Register a handler for the worker */
264   noit_check_run_full_asynch(check, postgres_drive_session);
265   return 0;
266 }
267
268 static int postgres_initiate_check(noit_module_t *self, noit_check_t *check,
269                                    int once, noit_check_t *parent) {
270   if(!check->closure) check->closure = calloc(1, sizeof(postgres_check_info_t));
271   INITIATE_CHECK(postgres_initiate, self, check);
272   return 0;
273 }
274
275 static int postgres_onload(noit_image_t *self) {
276   nlerr = noit_log_stream_find("error/postgres");
277   nldeb = noit_log_stream_find("debug/postgres");
278   if(!nlerr) nlerr = noit_stderr;
279   if(!nldeb) nldeb = noit_debug;
280
281   eventer_name_callback("http/postgres_drive_session", postgres_drive_session);
282   return 0;
283 }
284
285 #include "postgres.xmlh"
286 noit_module_t postgres = {
287   {
288     NOIT_MODULE_MAGIC,
289     NOIT_MODULE_ABI_VERSION,
290     "postgres",
291     "PostgreSQL Checker",
292     postgres_xml_description,
293     postgres_onload
294   },
295   NULL,
296   NULL,
297   postgres_initiate_check,
298   postgres_cleanup
299 };
300
Note: See TracBrowser for help on using the browser.