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