root/src/modules/mysql.c

Revision 88a71780101cbf23034aa0cb840f9f0368fda2dd, 10.2 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 6 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 <mysql.h>
48
49 typedef struct {
50   noit_module_t *self;
51   noit_check_t *check;
52   MYSQL *conn;
53   MYSQL_RES *result;
54   int rv;
55   noit_hash_table attrs;
56   int timed_out;
57   char *error;
58 } mysql_check_info_t;
59
60 static noit_log_stream_t nlerr = NULL;
61 static noit_log_stream_t nldeb = NULL;
62
63 static void mysql_cleanup(noit_module_t *self, noit_check_t *check) {
64   mysql_check_info_t *ci = check->closure;
65   if(ci) {
66     if(ci->result) mysql_free_result(ci->result);
67     if(ci->conn) mysql_close(ci->conn);
68     noit_check_release_attrs(&ci->attrs);
69     if(ci->error) free(ci->error);
70     memset(ci, 0, sizeof(*ci));
71   }
72 }
73 static void mysql_log_results(noit_module_t *self, noit_check_t *check) {
74   stats_t current;
75   struct timeval duration;
76   mysql_check_info_t *ci = check->closure;
77
78   noit_check_stats_clear(&current);
79
80   gettimeofday(&current.whence, NULL);
81   sub_timeval(current.whence, check->last_fire_time, &duration);
82   current.duration = duration.tv_sec * 1000 + duration.tv_usec / 1000;
83   current.available = NP_UNAVAILABLE;
84   current.state = NP_BAD;
85   if(ci->error) current.status = ci->error;
86   else if(ci->timed_out) current.status = "timeout";
87   else if(ci->rv == 0) {
88     current.available = NP_AVAILABLE;
89     current.state = NP_GOOD;
90     current.status = "no rows, ok";
91   }
92   else {
93     current.available = NP_AVAILABLE;
94     current.state = NP_GOOD;
95     current.status = "got rows, ok";
96   }
97
98   if(ci->rv > 0) {
99     /* metrics */
100     int nrows, ncols, i, j;
101     nrows = ci->rv;
102     ncols = mysql_num_fields(ci->result);
103     MYSQL_FIELD *cdesc = mysql_fetch_fields(ci->result);
104     for (i=0; i<nrows; i++) {
105       noitL(nldeb, "mysql: row %d [%d cols]:\n", i, ncols);
106       if(ncols<2) continue;
107       MYSQL_ROW row = mysql_fetch_row(ci->result);
108       if(NULL == row[0]) continue;
109       for (j=1; j<ncols; j++) {
110         enum enum_field_types coltype;
111         int iv, *piv;
112         int64_t lv, *plv;
113         double dv, *pdv;
114         char *sv;
115         char mname[128];
116  
117         snprintf(mname, sizeof(mname), "%s`%s", row[0], cdesc[j].name);
118         coltype = cdesc[j].type;
119         noitL(nldeb, "mysql:   col %d (%s) type %d:\n", j, mname, coltype);
120         switch(coltype) {
121           case FIELD_TYPE_TINY:
122           case FIELD_TYPE_SHORT:
123           case FIELD_TYPE_LONG:
124             if(!row[j]) piv = NULL;
125             else {
126               iv = strtol(row[j], NULL, 10);
127               piv = &iv;
128             }
129             noit_stats_set_metric(&current, mname, METRIC_INT32, piv);
130             break;
131           case FIELD_TYPE_INT24:
132           case FIELD_TYPE_LONGLONG:
133             if(!row[j]) plv = NULL;
134             else {
135               lv = strtoll(row[j], NULL, 10);
136               plv = &lv;
137             }
138             noit_stats_set_metric(&current, mname, METRIC_INT64, plv);
139             break;
140           case FIELD_TYPE_DECIMAL:
141           case FIELD_TYPE_FLOAT:
142           case FIELD_TYPE_DOUBLE:
143             if(!row[j]) pdv = NULL;
144             else {
145               dv = atof(row[j]);
146               pdv = &dv;
147             }
148             noit_stats_set_metric(&current, mname, METRIC_DOUBLE, pdv);
149             break;
150           default:
151             if(!row[j]) sv = NULL;
152             else sv = row[j];
153             noit_stats_set_metric(&current, mname, METRIC_GUESS, sv);
154             break;
155         }
156       }
157     }
158   }
159   noit_check_set_stats(self, check, &current);
160 }
161
162 #define FETCH_CONFIG_OR(key, str) do { \
163   if(!noit_hash_retrieve(check->config, #key, strlen(#key), (void **)&key)) \
164     key = str; \
165 } while(0)
166
167 #define AVAIL_BAIL(str) do { \
168   ci->timed_out = 0; \
169   ci->error = strdup(str); \
170   return 0; \
171 } while(0)
172
173 static char *
174 __noit__strndup(const char *src, int len) {
175   int slen;
176   char *dst;
177   for(slen = 0; slen < len; slen++)
178     if(src[slen] == '\0') break;
179   dst = malloc(slen + 1);
180   memcpy(dst, src, slen);
181   dst[slen] = '\0';
182   return dst;
183 }
184
185 void mysql_parse_dsn(const char *dsn, noit_hash_table *h) {
186   const char *a=dsn, *b=NULL, *c=NULL;
187   while (a && (NULL != (b = strchr(a, '=')))) {
188     char *key, *val=NULL;
189     key = __noit__strndup(a, b-a);
190     b++;
191     if (b) {
192       if (NULL != (c = strchr(b, ' '))) {
193         val = __noit__strndup(b, c-b);
194       } else {
195         val = strdup(b);
196       }
197     }
198     noit_hash_replace(h, key, key?strlen(key):0, val, free, free);
199     a = c;
200     if (a) a++;
201   }
202 }
203
204 static int mysql_drive_session(eventer_t e, int mask, void *closure,
205                                   struct timeval *now) {
206   const char *dsn, *sql;
207   char sql_buff[8192];
208   char dsn_buff[512];
209   mysql_check_info_t *ci = closure;
210   noit_check_t *check = ci->check;
211   noit_hash_table dsn_h = NOIT_HASH_EMPTY;
212   const char *host=NULL;
213   const char *user=NULL;
214   const char *password=NULL;
215   const char *dbname=NULL;
216   const char *port_s=NULL;
217   const char *socket=NULL;
218   u_int32_t port;
219   unsigned int timeout;
220
221   if(mask & (EVENTER_READ | EVENTER_WRITE)) {
222     /* this case is impossible from the eventer.  It is called as
223      * such on the synchronous completion of the event.
224      */
225     mysql_log_results(ci->self, ci->check);
226     mysql_cleanup(ci->self, ci->check);
227     check->flags &= ~NP_RUNNING;
228     return 0;
229   }
230   switch(mask) {
231     case EVENTER_ASYNCH_WORK:
232       FETCH_CONFIG_OR(dsn, "");
233       noit_check_interpolate(dsn_buff, sizeof(dsn_buff), dsn,
234                              &ci->attrs, check->config);
235
236       mysql_parse_dsn(dsn_buff, &dsn_h);
237       noit_hash_retrieve(&dsn_h, "host", strlen("host"), (void**)&host);
238       noit_hash_retrieve(&dsn_h, "user", strlen("user"), (void**)&user);
239       noit_hash_retrieve(&dsn_h, "password", strlen("password"), (void**)&password);
240       noit_hash_retrieve(&dsn_h, "dbname", strlen("dbname"), (void**)&dbname);
241       noit_hash_retrieve(&dsn_h, "port", strlen("port"), (void**)&port_s);
242       port = port_s ? strtol(port_s, NULL, 10) : 3306;
243       noit_hash_retrieve(&dsn_h, "socket", strlen("socket"), (void**)&socket);
244
245       ci->conn = mysql_init(NULL); /* allocate us a handle */
246       if(!ci->conn) AVAIL_BAIL("mysql_init failed");
247       timeout = check->timeout / 1000;
248       mysql_options(ci->conn, MYSQL_OPT_CONNECT_TIMEOUT, (const char *)&timeout);
249       if(!mysql_real_connect(ci->conn, host, user, password,
250                              dbname, port, socket, CLIENT_IGNORE_SIGPIPE)) {
251         noitL(noit_stderr, "error during mysql_real_connect: %s\n",
252           mysql_error(ci->conn));
253         AVAIL_BAIL(mysql_error(ci->conn));
254       }
255       if(mysql_ping(ci->conn))
256         AVAIL_BAIL(mysql_error(ci->conn));
257
258       FETCH_CONFIG_OR(sql, "");
259       noit_check_interpolate(sql_buff, sizeof(sql_buff), sql,
260                              &ci->attrs, check->config);
261       if (mysql_query(ci->conn, sql_buff))
262         AVAIL_BAIL(mysql_error(ci->conn));
263       ci->result = mysql_store_result(ci->conn);
264       if(!ci->result) AVAIL_BAIL("mysql_store_result failed");
265       ci->rv = mysql_num_rows(ci->result);
266       ci->timed_out = 0;
267       return 0;
268       break;
269     case EVENTER_ASYNCH_CLEANUP:
270       /* This sets us up for a completion call. */
271       e->mask = EVENTER_READ | EVENTER_WRITE;
272       break;
273     default:
274       abort();
275   }
276   return 0;
277 }
278
279 static int mysql_initiate(noit_module_t *self, noit_check_t *check) {
280   mysql_check_info_t *ci = check->closure;
281   struct timeval __now;
282
283   /* We cannot be running */
284   assert(!(check->flags & NP_RUNNING));
285   check->flags |= NP_RUNNING;
286
287   ci->self = self;
288   ci->check = check;
289
290   ci->timed_out = 1;
291   ci->rv = -1;
292   noit_check_make_attrs(check, &ci->attrs);
293   gettimeofday(&__now, NULL);
294   memcpy(&check->last_fire_time, &__now, sizeof(__now));
295
296   /* Register a handler for the worker */
297   noit_check_run_full_asynch(check, mysql_drive_session);
298   return 0;
299 }
300
301 static int mysql_initiate_check(noit_module_t *self, noit_check_t *check,
302                                    int once, noit_check_t *parent) {
303   if(!check->closure) check->closure = calloc(1, sizeof(mysql_check_info_t));
304   INITIATE_CHECK(mysql_initiate, self, check);
305   return 0;
306 }
307
308 static int mysql_onload(noit_image_t *self) {
309   nlerr = noit_log_stream_find("error/mysql");
310   nldeb = noit_log_stream_find("debug/mysql");
311   if(!nlerr) nlerr = noit_stderr;
312   if(!nldeb) nldeb = noit_debug;
313
314   eventer_name_callback("mysql/mysql_drive_session", mysql_drive_session);
315   return 0;
316 }
317
318 #include "mysql.xmlh"
319 noit_module_t mysql = {
320   {
321     NOIT_MODULE_MAGIC,
322     NOIT_MODULE_ABI_VERSION,
323     "mysql",
324     "MySQL Checker",
325     mysql_xml_description,
326     mysql_onload
327   },
328   NULL,
329   NULL,
330   mysql_initiate_check,
331   mysql_cleanup
332 };
333
Note: See TracBrowser for help on using the browser.