root/src/modules/postgres.c

Revision c39dbcdedc357e87352f7eb1438aa96afdcbd09e, 7.1 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 10 years ago)

make the loading mechanism generic so that we can load something other than checkers. refs #28

  • Property mode set to 100644
Line 
1 /*
2  * Copyright (c) 2007, OmniTI Computer Consulting, Inc.
3  * All rights reserved.
4  */
5
6 #include "noit_defines.h"
7
8 #include <stdio.h>
9 #include <unistd.h>
10 #include <errno.h>
11 #include <assert.h>
12 #include <math.h>
13
14 #include "noit_module.h"
15 #include "noit_check.h"
16 #include "noit_check_tools.h"
17 #include "utils/noit_log.h"
18 #include "utils/noit_hash.h"
19
20 #include <libpq-fe.h>
21 #include <postgres.h>
22 #include <fmgr.h>
23 #include <catalog/pg_type.h>
24
25 typedef struct {
26   noit_module_t *self;
27   noit_check_t *check;
28   PGconn *conn;
29   PGresult *result;
30   int rv;
31   noit_hash_table attrs;
32   int timed_out;
33   char *error;
34 } postgres_check_info_t;
35
36 static noit_log_stream_t nlerr = NULL;
37 static noit_log_stream_t nldeb = NULL;
38
39 static void postgres_cleanup(noit_module_t *self, noit_check_t *check) {
40   postgres_check_info_t *ci = check->closure;
41   if(ci) {
42     if(ci->result) PQclear(ci->result);
43     if(ci->conn) PQfinish(ci->conn);
44     noit_check_release_attrs(&ci->attrs);
45     if(ci->error) free(ci->error);
46     memset(ci, 0, sizeof(*ci));
47   }
48 }
49 static void postgres_log_results(noit_module_t *self, noit_check_t *check) {
50   stats_t current;
51   struct timeval duration;
52   postgres_check_info_t *ci = check->closure;
53
54   noit_check_stats_clear(&current);
55
56   gettimeofday(&current.whence, NULL);
57   sub_timeval(current.whence, check->last_fire_time, &duration);
58   current.duration = duration.tv_sec * 1000 + duration.tv_usec / 1000;
59   current.available = NP_UNAVAILABLE;
60   current.state = NP_BAD;
61   if(ci->error) current.status = ci->error;
62   else if(ci->timed_out) current.status = "timeout";
63   else if(ci->rv == PGRES_COMMAND_OK) {
64     current.available = NP_AVAILABLE;
65     current.state = NP_GOOD;
66     current.status = "command ok";
67   }
68   else if(ci->rv == PGRES_TUPLES_OK) {
69     current.available = NP_AVAILABLE;
70     current.state = NP_GOOD;
71     current.status = "tuples ok";
72   }
73   else current.status = "internal error";
74
75   if(ci->rv == PGRES_TUPLES_OK) {
76     /* metrics */
77     int nrows, ncols, i, j;
78     nrows = PQntuples(ci->result);
79     ncols = PQnfields(ci->result);
80     for (i=0; i<nrows; i++) {
81       noitL(nldeb, "postgres: row %d [%d cols]:\n", i, ncols);
82       if(ncols<2) continue;
83       if(PQgetisnull(ci->result, i, 0)) continue;
84       for (j=1; j<ncols; j++) {
85         Oid coltype;
86         int iv, *piv;
87         int64_t lv, *plv;
88         double dv, *pdv;
89         char *sv;
90         char mname[128];
91  
92         snprintf(mname, sizeof(mname), "%s`%s",
93                  PQgetvalue(ci->result, i, 0), PQfname(ci->result, j));
94         coltype = PQftype(ci->result, j);
95         noitL(nldeb, "postgres:   col %d (%s) type %d:\n", j, mname, coltype);
96         switch(coltype) {
97           case BOOLOID:
98             if(PQgetisnull(ci->result, i, j)) piv = NULL;
99             else {
100               iv = strcmp(PQgetvalue(ci->result, i, j), "f") ? 1 : 0;
101               piv = &iv;
102             }
103             noit_stats_set_metric(&current, mname, METRIC_INT32, piv);
104             break;
105           case INT2OID:
106           case INT4OID:
107           case INT8OID:
108             if(PQgetisnull(ci->result, i, j)) plv = NULL;
109             else {
110               lv = strtoll(PQgetvalue(ci->result, i, j), NULL, 10);
111               plv = &lv;
112             }
113             noit_stats_set_metric(&current, mname, METRIC_INT64, plv);
114           case FLOAT4OID:
115           case FLOAT8OID:
116           case NUMERICOID:
117             if(PQgetisnull(ci->result, i, j)) pdv = NULL;
118             else {
119               dv = atof(PQgetvalue(ci->result, i, j));
120               pdv = &dv;
121             }
122             noit_stats_set_metric(&current, mname, METRIC_DOUBLE, pdv);
123           default:
124             if(PQgetisnull(ci->result, i, j)) sv = NULL;
125             else sv = PQgetvalue(ci->result, i, j);
126             noit_stats_set_metric(&current, mname, METRIC_GUESS, sv);
127             break;
128         }
129       }
130     }
131   }
132   noit_check_set_stats(self, check, &current);
133 }
134
135 #define FETCH_CONFIG_OR(key, str) do { \
136   if(!noit_hash_retrieve(check->config, #key, strlen(#key), (void **)&key)) \
137     key = str; \
138 } while(0)
139
140 #define AVAIL_BAIL(str) do { \
141   ci->timed_out = 0; \
142   ci->error = strdup(str); \
143   return 0; \
144 } while(0)
145
146 static int postgres_drive_session(eventer_t e, int mask, void *closure,
147                                   struct timeval *now) {
148   const char *dsn, *sql;
149   char sql_buff[8192];
150   char dsn_buff[512];
151   postgres_check_info_t *ci = closure;
152   noit_check_t *check = ci->check;
153
154   if(mask & (EVENTER_READ | EVENTER_WRITE)) {
155     /* this case is impossible from the eventer.  It is called as
156      * such on the synchronous completion of the event.
157      */
158     postgres_log_results(ci->self, ci->check);
159     postgres_cleanup(ci->self, ci->check);
160     check->flags &= ~NP_RUNNING;
161     return 0;
162   }
163   switch(mask) {
164     case EVENTER_ASYNCH_WORK:
165       FETCH_CONFIG_OR(dsn, "");
166       noit_check_interpolate(dsn_buff, sizeof(dsn_buff), dsn,
167                              &ci->attrs, check->config);
168       ci->conn = PQconnectdb(dsn_buff);
169       if(!ci->conn) AVAIL_BAIL("PQconnectdb failed");
170       if(PQstatus(ci->conn) != CONNECTION_OK)
171         AVAIL_BAIL(PQerrorMessage(ci->conn));
172
173       FETCH_CONFIG_OR(sql, "");
174       noit_check_interpolate(sql_buff, sizeof(sql_buff), sql,
175                              &ci->attrs, check->config);
176       ci->result = PQexec(ci->conn, sql_buff);
177       if(!ci->result) AVAIL_BAIL("PQexec failed");
178       ci->rv = PQresultStatus(ci->result);
179       switch(ci->rv) {
180        case PGRES_TUPLES_OK:
181        case PGRES_COMMAND_OK:
182         break;
183        default:
184         AVAIL_BAIL(PQresultErrorMessage(ci->result));
185       }
186       ci->timed_out = 0;
187       return 0;
188       break;
189     case EVENTER_ASYNCH_CLEANUP:
190       /* This sets us up for a completion call. */
191       e->mask = EVENTER_READ | EVENTER_WRITE;
192       break;
193     default:
194       abort();
195   }
196   return 0;
197 }
198
199 static int postgres_initiate(noit_module_t *self, noit_check_t *check) {
200   postgres_check_info_t *ci = check->closure;
201   struct timeval __now;
202
203   /* We cannot be running */
204   assert(!(check->flags & NP_RUNNING));
205   check->flags |= NP_RUNNING;
206
207   ci->self = self;
208   ci->check = check;
209
210   ci->timed_out = 1;
211   ci->rv = -1;
212   noit_check_make_attrs(check, &ci->attrs);
213   gettimeofday(&__now, NULL);
214   memcpy(&check->last_fire_time, &__now, sizeof(__now));
215
216   /* Register a handler for the worker */
217   noit_check_run_full_asynch(check, postgres_drive_session);
218   return 0;
219 }
220
221 static int postgres_initiate_check(noit_module_t *self, noit_check_t *check,
222                                    int once, noit_check_t *parent) {
223   if(!check->closure) check->closure = calloc(1, sizeof(postgres_check_info_t));
224   INITIATE_CHECK(postgres_initiate, self, check);
225   return 0;
226 }
227
228 static int postgres_onload(noit_image_t *self) {
229   nlerr = noit_log_stream_find("error/postgres");
230   nldeb = noit_log_stream_find("debug/postgres");
231   if(!nlerr) nlerr = noit_stderr;
232   if(!nldeb) nldeb = noit_debug;
233
234   eventer_name_callback("http/postgres_drive_session", postgres_drive_session);
235   return 0;
236 }
237
238 noit_module_t postgres = {
239   {
240     NOIT_MODULE_MAGIC,
241     NOIT_MODULE_ABI_VERSION,
242     "postgres",
243     "PostgreSQL Checker",
244     postgres_onload
245   },
246   NULL,
247   NULL,
248   postgres_initiate_check,
249   postgres_cleanup
250 };
251
Note: See TracBrowser for help on using the browser.