Merge branch 'collectd-4.4' into collectd-4.5
[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         sstrncpy (vl.type, type, sizeof (vl.type));
134         sstrncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
135
136         plugin_dispatch_values (&vl);
137 } /* void counter_submit */
138
139 static void qcache_submit (counter_t hits, counter_t inserts,
140                 counter_t not_cached, counter_t lowmem_prunes,
141                 gauge_t queries_in_cache)
142 {
143         value_t values[5];
144         value_list_t vl = VALUE_LIST_INIT;
145
146         values[0].counter = hits;
147         values[1].counter = inserts;
148         values[2].counter = not_cached;
149         values[3].counter = lowmem_prunes;
150         values[4].gauge   = queries_in_cache;
151
152         vl.values = values;
153         vl.values_len = 5;
154         vl.time = time (NULL);
155         sstrncpy (vl.host, hostname_g, sizeof (vl.host));
156         sstrncpy (vl.plugin, "mysql", sizeof (vl.plugin));
157         sstrncpy (vl.type, "mysql_qcache", sizeof (vl.type));
158
159         plugin_dispatch_values (&vl);
160 } /* void qcache_submit */
161
162 static void threads_submit (gauge_t running, gauge_t connected, gauge_t cached,
163                 counter_t created)
164 {
165         value_t values[4];
166         value_list_t vl = VALUE_LIST_INIT;
167
168         values[0].gauge   = running;
169         values[1].gauge   = connected;
170         values[2].gauge   = cached;
171         values[3].counter = created;
172
173         vl.values = values;
174         vl.values_len = 4;
175         vl.time = time (NULL);
176         sstrncpy (vl.host, hostname_g, sizeof (vl.host));
177         sstrncpy (vl.plugin, "mysql", sizeof (vl.plugin));
178         sstrncpy (vl.type, "mysql_threads", sizeof (vl.type));
179
180         plugin_dispatch_values (&vl);
181 } /* void threads_submit */
182
183 static void traffic_submit (counter_t rx, counter_t tx)
184 {
185         value_t values[2];
186         value_list_t vl = VALUE_LIST_INIT;
187
188         values[0].counter = rx;
189         values[1].counter = tx;
190
191         vl.values = values;
192         vl.values_len = 2;
193         vl.time = time (NULL);
194         sstrncpy (vl.host, hostname_g, sizeof (vl.host));
195         sstrncpy (vl.plugin, "mysql", sizeof (vl.plugin));
196         sstrncpy (vl.type, "mysql_octets", sizeof (vl.type));
197
198         plugin_dispatch_values (&vl);
199 } /* void traffic_submit */
200
201 static int mysql_read (void)
202 {
203         MYSQL     *con;
204         MYSQL_RES *res;
205         MYSQL_ROW  row;
206         char      *query;
207         int        query_len;
208         int        field_num;
209
210         unsigned long long qcache_hits          = 0ULL;
211         unsigned long long qcache_inserts       = 0ULL;
212         unsigned long long qcache_not_cached    = 0ULL;
213         unsigned long long qcache_lowmem_prunes = 0ULL;
214         int qcache_queries_in_cache = -1;
215
216         int threads_running   = -1;
217         int threads_connected = -1;
218         int threads_cached    = -1;
219         unsigned long long threads_created = 0ULL;
220
221         unsigned long long traffic_incoming = 0ULL;
222         unsigned long long traffic_outgoing = 0ULL;
223
224         /* An error message will have been printed in this case */
225         if ((con = getconnection ()) == NULL)
226                 return (-1);
227
228         query = "SHOW STATUS";
229         if (mysql_get_server_version (con) >= 50002)
230                 query = "SHOW GLOBAL STATUS";
231
232         query_len = strlen (query);
233
234         if (mysql_real_query (con, query, query_len))
235         {
236                 ERROR ("mysql_real_query failed: %s\n",
237                                 mysql_error (con));
238                 return (-1);
239         }
240
241         if ((res = mysql_store_result (con)) == NULL)
242         {
243                 ERROR ("mysql_store_result failed: %s\n",
244                                 mysql_error (con));
245                 return (-1);
246         }
247
248         field_num = mysql_num_fields (res);
249         while ((row = mysql_fetch_row (res)))
250         {
251                 char *key;
252                 unsigned long long val;
253
254                 key = row[0];
255                 val = atoll (row[1]);
256
257                 if (strncmp (key, "Com_", 4) == 0)
258                 {
259                         if (val == 0ULL)
260                                 continue;
261
262                         /* Ignore `prepared statements' */
263                         if (strncmp (key, "Com_stmt_", 9) != 0)
264                                 counter_submit ("mysql_commands", key + 4, val);
265                 }
266                 else if (strncmp (key, "Handler_", 8) == 0)
267                 {
268                         if (val == 0ULL)
269                                 continue;
270
271                         counter_submit ("mysql_handler", key + 8, val);
272                 }
273                 else if (strncmp (key, "Qcache_", 7) == 0)
274                 {
275                         if (strcmp (key, "Qcache_hits") == 0)
276                                 qcache_hits = val;
277                         else if (strcmp (key, "Qcache_inserts") == 0)
278                                 qcache_inserts = val;
279                         else if (strcmp (key, "Qcache_not_cached") == 0)
280                                 qcache_not_cached = val;
281                         else if (strcmp (key, "Qcache_lowmem_prunes") == 0)
282                                 qcache_lowmem_prunes = val;
283                         else if (strcmp (key, "Qcache_queries_in_cache") == 0)
284                                 qcache_queries_in_cache = (int) val;
285                 }
286                 else if (strncmp (key, "Bytes_", 6) == 0)
287                 {
288                         if (strcmp (key, "Bytes_received") == 0)
289                                 traffic_incoming += val;
290                         else if (strcmp (key, "Bytes_sent") == 0)
291                                 traffic_outgoing += val;
292                 }
293                 else if (strncmp (key, "Threads_", 8) == 0)
294                 {
295                         if (strcmp (key, "Threads_running") == 0)
296                                 threads_running = (int) val;
297                         else if (strcmp (key, "Threads_connected") == 0)
298                                 threads_connected = (int) val;
299                         else if (strcmp (key, "Threads_cached") == 0)
300                                 threads_cached = (int) val;
301                         else if (strcmp (key, "Threads_created") == 0)
302                                 threads_created = val;
303                 }
304         }
305         mysql_free_result (res); res = NULL;
306
307         if ((qcache_hits != 0ULL)
308                         || (qcache_inserts != 0ULL)
309                         || (qcache_not_cached != 0ULL)
310                         || (qcache_lowmem_prunes != 0ULL))
311                 qcache_submit (qcache_hits, qcache_inserts, qcache_not_cached,
312                                 qcache_lowmem_prunes, qcache_queries_in_cache);
313
314         if (threads_created != 0ULL)
315                 threads_submit (threads_running, threads_connected,
316                                 threads_cached, threads_created);
317
318         traffic_submit  (traffic_incoming, traffic_outgoing);
319
320         /* mysql_close (con); */
321
322         return (0);
323 } /* int mysql_read */
324
325 void module_register (void)
326 {
327         plugin_register_config ("mysql", config, config_keys, config_keys_num);
328         plugin_register_read ("mysql", mysql_read);
329 } /* void module_register */