collectd: Abort with an appropriate status in exit_usage().
[collectd.git] / src / mysql.c
1 /**
2  * collectd - src/mysql.c
3  * Copyright (C) 2006,2007  Florian octo Forster
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License as published by the
7  * Free Software Foundation; only version 2 of the License is applicable.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
17  *
18  * Authors:
19  *   Florian octo Forster <octo at verplant.org>
20  **/
21
22 #include "collectd.h"
23 #include "common.h"
24 #include "plugin.h"
25 #include "configfile.h"
26
27 #ifdef HAVE_MYSQL_H
28 #include <mysql.h>
29 #elif defined(HAVE_MYSQL_MYSQL_H)
30 #include <mysql/mysql.h>
31 #endif
32
33 /* TODO: Understand `Select_*' and possibly do that stuff as well.. */
34
35 static const char *config_keys[] =
36 {
37         "Host",
38         "User",
39         "Password",
40         "Database",
41         NULL
42 };
43 static int config_keys_num = 4;
44
45 static char *host = "localhost";
46 static char *user;
47 static char *pass;
48 static char *db = NULL;
49
50 static MYSQL *getconnection (void)
51 {
52         static MYSQL *con;
53         static int    state;
54
55         static int wait_for = 0;
56         static int wait_increase = 60;
57
58         if (state != 0)
59         {
60                 int err;
61                 if ((err = mysql_ping (con)) != 0)
62                 {
63                         WARNING ("mysql_ping failed: %s", mysql_error (con));
64                         state = 0;
65                 }
66                 else
67                 {
68                         state = 1;
69                         return (con);
70                 }
71         }
72
73         if (wait_for > 0)
74         {
75                 wait_for -= interval_g;
76                 return (NULL);
77         }
78
79         wait_for = wait_increase;
80         wait_increase *= 2;
81         if (wait_increase > 86400)
82                 wait_increase = 86400;
83
84         if ((con = mysql_init (con)) == NULL)
85         {
86                 ERROR ("mysql_init failed: %s", mysql_error (con));
87                 state = 0;
88                 return (NULL);
89         }
90
91         if (mysql_real_connect (con, host, user, pass, db, 0, NULL, 0) == NULL)
92         {
93                 ERROR ("mysql_real_connect failed: %s", mysql_error (con));
94                 state = 0;
95                 return (NULL);
96         }
97         else
98         {
99                 state = 1;
100                 wait_for = 0;
101                 wait_increase = 60;
102                 return (con);
103         }
104 } /* static MYSQL *getconnection (void) */
105
106 static int config (const char *key, const char *value)
107 {
108         if (strcasecmp (key, "host") == 0)
109                 return ((host = strdup (value)) == NULL ? 1 : 0);
110         else if (strcasecmp (key, "user") == 0)
111                 return ((user = strdup (value)) == NULL ? 1 : 0);
112         else if (strcasecmp (key, "password") == 0)
113                 return ((pass = strdup (value)) == NULL ? 1 : 0);
114         else if (strcasecmp (key, "database") == 0)
115                 return ((db = strdup (value)) == NULL ? 1 : 0);
116         else
117                 return (-1);
118 }
119
120 static void counter_submit (const char *type, const char *type_instance,
121                 counter_t value)
122 {
123         value_t values[1];
124         value_list_t vl = VALUE_LIST_INIT;
125
126         values[0].counter = value;
127
128         vl.values = values;
129         vl.values_len = 1;
130         vl.time = time (NULL);
131         sstrncpy (vl.host, hostname_g, sizeof (vl.host));
132         sstrncpy (vl.plugin, "mysql", sizeof (vl.plugin));
133         strncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
134
135         plugin_dispatch_values (type, &vl);
136 } /* void counter_submit */
137
138 static void qcache_submit (counter_t hits, counter_t inserts,
139                 counter_t not_cached, counter_t lowmem_prunes,
140                 gauge_t queries_in_cache)
141 {
142         value_t values[5];
143         value_list_t vl = VALUE_LIST_INIT;
144
145         values[0].counter = hits;
146         values[1].counter = inserts;
147         values[2].counter = not_cached;
148         values[3].counter = lowmem_prunes;
149         values[4].gauge   = queries_in_cache;
150
151         vl.values = values;
152         vl.values_len = 5;
153         vl.time = time (NULL);
154         sstrncpy (vl.host, hostname_g, sizeof (vl.host));
155         sstrncpy (vl.plugin, "mysql", sizeof (vl.plugin));
156
157         plugin_dispatch_values ("mysql_qcache", &vl);
158 } /* void qcache_submit */
159
160 static void threads_submit (gauge_t running, gauge_t connected, gauge_t cached,
161                 counter_t created)
162 {
163         value_t values[4];
164         value_list_t vl = VALUE_LIST_INIT;
165
166         values[0].gauge   = running;
167         values[1].gauge   = connected;
168         values[2].gauge   = cached;
169         values[3].counter = created;
170
171         vl.values = values;
172         vl.values_len = 4;
173         vl.time = time (NULL);
174         sstrncpy (vl.host, hostname_g, sizeof (vl.host));
175         sstrncpy (vl.plugin, "mysql", sizeof (vl.plugin));
176
177         plugin_dispatch_values ("mysql_threads", &vl);
178 } /* void threads_submit */
179
180 static void traffic_submit (counter_t rx, counter_t tx)
181 {
182         value_t values[2];
183         value_list_t vl = VALUE_LIST_INIT;
184
185         values[0].counter = rx;
186         values[1].counter = tx;
187
188         vl.values = values;
189         vl.values_len = 2;
190         vl.time = time (NULL);
191         sstrncpy (vl.host, hostname_g, sizeof (vl.host));
192         sstrncpy (vl.plugin, "mysql", sizeof (vl.plugin));
193
194         plugin_dispatch_values ("mysql_octets", &vl);
195 } /* void traffic_submit */
196
197 static int mysql_read (void)
198 {
199         MYSQL     *con;
200         MYSQL_RES *res;
201         MYSQL_ROW  row;
202         char      *query;
203         int        query_len;
204         int        field_num;
205
206         unsigned long long qcache_hits          = 0ULL;
207         unsigned long long qcache_inserts       = 0ULL;
208         unsigned long long qcache_not_cached    = 0ULL;
209         unsigned long long qcache_lowmem_prunes = 0ULL;
210         int qcache_queries_in_cache = -1;
211
212         int threads_running   = -1;
213         int threads_connected = -1;
214         int threads_cached    = -1;
215         unsigned long long threads_created = 0ULL;
216
217         unsigned long long traffic_incoming = 0ULL;
218         unsigned long long traffic_outgoing = 0ULL;
219
220         /* An error message will have been printed in this case */
221         if ((con = getconnection ()) == NULL)
222                 return (-1);
223
224         query = "SHOW STATUS";
225         if (mysql_get_server_version (con) >= 50002)
226                 query = "SHOW GLOBAL STATUS";
227
228         query_len = strlen (query);
229
230         if (mysql_real_query (con, query, query_len))
231         {
232                 ERROR ("mysql_real_query failed: %s\n",
233                                 mysql_error (con));
234                 return (-1);
235         }
236
237         if ((res = mysql_store_result (con)) == NULL)
238         {
239                 ERROR ("mysql_store_result failed: %s\n",
240                                 mysql_error (con));
241                 return (-1);
242         }
243
244         field_num = mysql_num_fields (res);
245         while ((row = mysql_fetch_row (res)))
246         {
247                 char *key;
248                 unsigned long long val;
249
250                 key = row[0];
251                 val = atoll (row[1]);
252
253                 if (strncmp (key, "Com_", 4) == 0)
254                 {
255                         if (val == 0ULL)
256                                 continue;
257
258                         /* Ignore `prepared statements' */
259                         if (strncmp (key, "Com_stmt_", 9) != 0)
260                                 counter_submit ("mysql_commands", key + 4, val);
261                 }
262                 else if (strncmp (key, "Handler_", 8) == 0)
263                 {
264                         if (val == 0ULL)
265                                 continue;
266
267                         counter_submit ("mysql_handler", key + 8, val);
268                 }
269                 else if (strncmp (key, "Qcache_", 7) == 0)
270                 {
271                         if (strcmp (key, "Qcache_hits") == 0)
272                                 qcache_hits = val;
273                         else if (strcmp (key, "Qcache_inserts") == 0)
274                                 qcache_inserts = val;
275                         else if (strcmp (key, "Qcache_not_cached") == 0)
276                                 qcache_not_cached = val;
277                         else if (strcmp (key, "Qcache_lowmem_prunes") == 0)
278                                 qcache_lowmem_prunes = val;
279                         else if (strcmp (key, "Qcache_queries_in_cache") == 0)
280                                 qcache_queries_in_cache = (int) val;
281                 }
282                 else if (strncmp (key, "Bytes_", 6) == 0)
283                 {
284                         if (strcmp (key, "Bytes_received") == 0)
285                                 traffic_incoming += val;
286                         else if (strcmp (key, "Bytes_sent") == 0)
287                                 traffic_outgoing += val;
288                 }
289                 else if (strncmp (key, "Threads_", 8) == 0)
290                 {
291                         if (strcmp (key, "Threads_running") == 0)
292                                 threads_running = (int) val;
293                         else if (strcmp (key, "Threads_connected") == 0)
294                                 threads_connected = (int) val;
295                         else if (strcmp (key, "Threads_cached") == 0)
296                                 threads_cached = (int) val;
297                         else if (strcmp (key, "Threads_created") == 0)
298                                 threads_created = val;
299                 }
300         }
301         mysql_free_result (res); res = NULL;
302
303         if ((qcache_hits != 0ULL)
304                         || (qcache_inserts != 0ULL)
305                         || (qcache_not_cached != 0ULL)
306                         || (qcache_lowmem_prunes != 0ULL))
307                 qcache_submit (qcache_hits, qcache_inserts, qcache_not_cached,
308                                 qcache_lowmem_prunes, qcache_queries_in_cache);
309
310         if (threads_created != 0ULL)
311                 threads_submit (threads_running, threads_connected,
312                                 threads_cached, threads_created);
313
314         traffic_submit  (traffic_incoming, traffic_outgoing);
315
316         /* mysql_close (con); */
317
318         return (0);
319 } /* int mysql_read */
320
321 void module_register (void)
322 {
323         plugin_register_config ("mysql", config, config_keys, config_keys_num);
324         plugin_register_read ("mysql", mysql_read);
325 } /* void module_register */