Merge remote branch 'trenkel/collectd-4.10' into collectd-4.10
[collectd.git] / src / mysql.c
1 /**
2  * collectd - src/mysql.c
3  * Copyright (C) 2006-2009  Florian octo Forster
4  * Copyright (C) 2008       Mirko Buffoni
5  * Copyright (C) 2009       Doug MacEachern
6  * Copyright (C) 2009       Sebastian tokkee Harl
7  * Copyright (C) 2009       Rodolphe QuiĆ©deville
8  *
9  * This program is free software; you can redistribute it and/or modify it
10  * under the terms of the GNU General Public License as published by the
11  * Free Software Foundation; only version 2 of the License is applicable.
12  *
13  * This program is distributed in the hope that it will be useful, but
14  * WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License along
19  * with this program; if not, write to the Free Software Foundation, Inc.,
20  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
21  *
22  * Authors:
23  *   Florian octo Forster <octo at verplant.org>
24  *   Mirko Buffoni <briareos at eswat.org>
25  *   Doug MacEachern <dougm at hyperic.com>
26  *   Sebastian tokkee Harl <sh at tokkee.org>
27  *   Rodolphe QuiĆ©deville <rquiedeville at bearstech.com>
28  **/
29
30 #include "collectd.h"
31 #include "common.h"
32 #include "plugin.h"
33 #include "configfile.h"
34
35 #ifdef HAVE_MYSQL_H
36 #include <mysql.h>
37 #elif defined(HAVE_MYSQL_MYSQL_H)
38 #include <mysql/mysql.h>
39 #endif
40
41 /* TODO: Understand `Select_*' and possibly do that stuff as well.. */
42
43 struct mysql_database_s /* {{{ */
44 {
45         /* instance == NULL  =>  legacy mode */
46         char *instance;
47         char *host;
48         char *user;
49         char *pass;
50         char *database;
51         char *socket;
52         int   port;
53
54         int   master_stats;
55         int   slave_stats;
56
57         int   slave_notif;
58         int   slave_io_running;
59         int   slave_sql_running;
60
61         MYSQL *con;
62         int    state;
63 };
64 typedef struct mysql_database_s mysql_database_t; /* }}} */
65
66 static int mysql_read (user_data_t *ud);
67
68 static void mysql_database_free (void *arg) /* {{{ */
69 {
70         mysql_database_t *db;
71
72         DEBUG ("mysql plugin: mysql_database_free (arg = %p);", arg);
73
74         db = (mysql_database_t *) arg;
75
76         if (db == NULL)
77                 return;
78
79         if (db->con != NULL)
80                 mysql_close (db->con);
81
82         sfree (db->host);
83         sfree (db->user);
84         sfree (db->pass);
85         sfree (db->socket);
86         sfree (db->instance);
87         sfree (db->database);
88         sfree (db);
89 } /* }}} void mysql_database_free */
90
91 /* Configuration handling functions {{{
92  *
93  * <Plugin mysql>
94  *   <Database "plugin_instance1">
95  *     Host "localhost"
96  *     Port 22000
97  *     ...
98  *   </Database>
99  * </Plugin>
100  */
101
102 static int mysql_config_set_string (char **ret_string, /* {{{ */
103                                     oconfig_item_t *ci)
104 {
105         char *string;
106
107         if ((ci->values_num != 1)
108             || (ci->values[0].type != OCONFIG_TYPE_STRING))
109         {
110                 WARNING ("mysql plugin: The `%s' config option "
111                          "needs exactly one string argument.", ci->key);
112                 return (-1);
113         }
114
115         string = strdup (ci->values[0].value.string);
116         if (string == NULL)
117         {
118                 ERROR ("mysql plugin: strdup failed.");
119                 return (-1);
120         }
121
122         if (*ret_string != NULL)
123                 free (*ret_string);
124         *ret_string = string;
125
126         return (0);
127 } /* }}} int mysql_config_set_string */
128
129 static int mysql_config_set_int (int *ret_int, /* {{{ */
130                                  oconfig_item_t *ci)
131 {
132         if ((ci->values_num != 1)
133             || (ci->values[0].type != OCONFIG_TYPE_NUMBER))
134         {
135                 WARNING ("mysql plugin: The `%s' config option "
136                          "needs exactly one string argument.", ci->key);
137                 return (-1);
138         }
139
140         *ret_int = ci->values[0].value.number;
141
142         return (0);
143 } /* }}} int mysql_config_set_int */
144
145 static int mysql_config_set_boolean (int *ret_boolean, /* {{{ */
146                                 oconfig_item_t *ci)
147 {
148         int status = 0;
149
150         if (ci->values_num != 1)
151                 status = -1;
152
153         if (status == 0)
154         {
155                 if (ci->values[0].type == OCONFIG_TYPE_BOOLEAN)
156                         *ret_boolean = ci->values[0].value.boolean;
157                 else if (ci->values[0].type == OCONFIG_TYPE_STRING)
158                 {
159                         if (IS_TRUE (ci->values[0].value.string))
160                                 *ret_boolean = 1;
161                         else if (IS_FALSE (ci->values[0].value.string))
162                                 *ret_boolean = 0;
163                         else
164                                 status = -1;
165                 }
166                 else
167                         status = -1;
168         }
169
170         if (status != 0)
171         {
172                 WARNING ("mysql plugin: The `%s' config option "
173                         "needs exactly one boolean argument.", ci->key);
174                 return (-1);
175         }
176         return (0);
177 } /* }}} mysql_config_set_boolean */
178
179 static int mysql_config (oconfig_item_t *ci) /* {{{ */
180 {
181         mysql_database_t *db;
182         int plugin_block;
183         int status = 0;
184         int i;
185
186         if ((ci->values_num != 1)
187             || (ci->values[0].type != OCONFIG_TYPE_STRING))
188         {
189                 WARNING ("mysql plugin: The `Database' block "
190                          "needs exactly one string argument.");
191                 return (-1);
192         }
193
194         db = (mysql_database_t *) malloc (sizeof (*db));
195         if (db == NULL)
196         {
197                 ERROR ("mysql plugin: malloc failed.");
198                 return (-1);
199         }
200         memset (db, 0, sizeof (*db));
201
202         /* initialize all the pointers */
203         db->host     = NULL;
204         db->user     = NULL;
205         db->pass     = NULL;
206         db->database = NULL;
207         db->socket   = NULL;
208         db->con      = NULL;
209
210         /* trigger a notification, if it's not running */
211         db->slave_io_running  = 1;
212         db->slave_sql_running = 1;
213
214         plugin_block = 1;
215         if (strcasecmp ("Plugin", ci->key) == 0)
216         {
217                 db->instance = NULL;
218         }
219         else if (strcasecmp ("Database", ci->key) == 0)
220         {
221                 plugin_block = 0;
222                 status = mysql_config_set_string (&db->instance, ci);
223                 if (status != 0)
224                 {
225                         sfree (db);
226                         return (status);
227                 }
228                 assert (db->instance != NULL);
229         }
230         else
231         {
232                 ERROR ("mysql plugin: mysql_config: "
233                                 "Invalid key: %s", ci->key);
234                 return (-1);
235         }
236
237         /* Fill the `mysql_database_t' structure.. */
238         for (i = 0; i < ci->children_num; i++)
239         {
240                 oconfig_item_t *child = ci->children + i;
241
242                 if (strcasecmp ("Host", child->key) == 0)
243                         status = mysql_config_set_string (&db->host, child);
244                 else if (strcasecmp ("User", child->key) == 0)
245                         status = mysql_config_set_string (&db->user, child);
246                 else if (strcasecmp ("Password", child->key) == 0)
247                         status = mysql_config_set_string (&db->pass, child);
248                 else if (strcasecmp ("Port", child->key) == 0)
249                         status = mysql_config_set_int (&db->port, child);
250                 else if (strcasecmp ("Socket", child->key) == 0)
251                         status = mysql_config_set_string (&db->socket, child);
252                 /* Check if we're currently handling the `Plugin' block. If so,
253                  * handle `Database' _blocks_, too. */
254                 else if ((plugin_block != 0)
255                                 && (strcasecmp ("Database", child->key) == 0)
256                                 && (child->children != NULL))
257                 {
258                         /* If `plugin_block > 1', there has been at least one
259                          * `Database' block */
260                         plugin_block++;
261                         status = mysql_config (child);
262                 }
263                 /* Now handle ordinary `Database' options (without children) */
264                 else if ((strcasecmp ("Database", child->key) == 0)
265                                 && (child->children == NULL))
266                         status = mysql_config_set_string (&db->database, child);
267                 else if (strcasecmp ("MasterStats", child->key) == 0)
268                         status = mysql_config_set_boolean (&db->master_stats, child);
269                 else if (strcasecmp ("SlaveStats", child->key) == 0)
270                         status = mysql_config_set_boolean (&db->slave_stats, child);
271                 else if (strcasecmp ("SlaveNotifications", child->key) == 0)
272                         status = mysql_config_set_boolean (&db->slave_notif, child);
273                 else
274                 {
275                         WARNING ("mysql plugin: Option `%s' not allowed here.", child->key);
276                         status = -1;
277                 }
278
279                 if (status != 0)
280                         break;
281         }
282
283         /* Check if there were any `Database' blocks. */
284         if (plugin_block > 1)
285         {
286                 /* There were connection blocks. Don't use any legacy stuff. */
287                 if ((db->host != NULL)
288                         || (db->user != NULL)
289                         || (db->pass != NULL)
290                         || (db->database != NULL)
291                         || (db->socket != NULL)
292                         || (db->port != 0))
293                 {
294                         WARNING ("mysql plugin: At least one <Database> "
295                                         "block has been found. The legacy "
296                                         "configuration will be ignored.");
297                 }
298                 mysql_database_free (db);
299                 return (0);
300         }
301         else if (plugin_block != 0)
302         {
303                 WARNING ("mysql plugin: You're using the legacy "
304                                 "configuration options. Please consider "
305                                 "updating your configuration!");
306         }
307
308         /* Check that all necessary options have been given. */
309         while (status == 0)
310         {
311                 /* Zero is allowed and automatically handled by
312                  * `mysql_real_connect'. */
313                 if ((db->port < 0) || (db->port > 65535))
314                 {
315                         ERROR ("mysql plugin: Database %s: Port number out "
316                                         "of range: %i",
317                                         (db->instance != NULL)
318                                         ? db->instance
319                                         : "<legacy>",
320                                         db->port);
321                         status = -1;
322                 }
323                 break;
324         } /* while (status == 0) */
325
326         /* If all went well, register this database for reading */
327         if (status == 0)
328         {
329                 user_data_t ud;
330                 char cb_name[DATA_MAX_NAME_LEN];
331
332                 DEBUG ("mysql plugin: Registering new read callback: %s",
333                                 (db->database != NULL) ? db->database : "<default>");
334
335                 memset (&ud, 0, sizeof (ud));
336                 ud.data = (void *) db;
337                 ud.free_func = mysql_database_free;
338
339                 if (db->database != NULL)
340                         ssnprintf (cb_name, sizeof (cb_name), "mysql-%s",
341                                         db->database);
342                 else
343                         sstrncpy (cb_name, "mysql", sizeof (cb_name));
344
345                 plugin_register_complex_read (/* group = */ NULL, cb_name,
346                                               mysql_read,
347                                               /* interval = */ NULL, &ud);
348         }
349         else
350         {
351                 mysql_database_free (db);
352                 return (-1);
353         }
354
355         return (0);
356 } /* }}} int mysql_config */
357
358 /* }}} End of configuration handling functions */
359
360 static MYSQL *getconnection (mysql_database_t *db)
361 {
362         if (db->state != 0)
363         {
364                 int err;
365                 if ((err = mysql_ping (db->con)) != 0)
366                 {
367                         WARNING ("mysql_ping failed for %s: %s",
368                                         (db->instance != NULL)
369                                         ? db->instance
370                                         : "<legacy>",
371                                         mysql_error (db->con));
372                         db->state = 0;
373                 }
374                 else
375                 {
376                         db->state = 1;
377                         return (db->con);
378                 }
379         }
380
381         if ((db->con = mysql_init (db->con)) == NULL)
382         {
383                 ERROR ("mysql_init failed: %s", mysql_error (db->con));
384                 db->state = 0;
385                 return (NULL);
386         }
387
388         if (mysql_real_connect (db->con, db->host, db->user, db->pass,
389                                 db->database, db->port, db->socket, 0) == NULL)
390         {
391                 ERROR ("mysql plugin: Failed to connect to database %s "
392                                 "at server %s: %s",
393                                 (db->database != NULL) ? db->database : "<none>",
394                                 (db->host != NULL) ? db->host : "localhost",
395                                 mysql_error (db->con));
396                 db->state = 0;
397                 return (NULL);
398         }
399         else
400         {
401                 INFO ("mysql plugin: Successfully connected to database %s "
402                                 "at server %s (server version: %s, protocol version: %d)",
403                                 (db->database != NULL) ? db->database : "<none>",
404                                 mysql_get_host_info (db->con),
405                                 mysql_get_server_info (db->con),
406                                 mysql_get_proto_info (db->con));
407                 db->state = 1;
408                 return (db->con);
409         }
410 } /* static MYSQL *getconnection (mysql_database_t *db) */
411
412 static void set_host (mysql_database_t *db, char *buf, size_t buflen)
413 {
414         /* XXX legacy mode - use hostname_g */
415         if (db->instance == NULL)
416                 sstrncpy (buf, hostname_g, buflen);
417         else
418         {
419                 if ((db->host == NULL)
420                                 || (strcmp ("", db->host) == 0)
421                                 || (strcmp ("localhost", db->host) == 0))
422                         sstrncpy (buf, hostname_g, buflen);
423                 else
424                         sstrncpy (buf, db->host, buflen);
425         }
426 }
427
428 static void set_plugin_instance (mysql_database_t *db,
429                 char *buf, size_t buflen)
430 {
431         /* XXX legacy mode - no plugin_instance */
432         if (db->instance == NULL)
433                 sstrncpy (buf, "", buflen);
434         else
435                 sstrncpy (buf, db->instance, buflen);
436 }
437
438 static void submit (const char *type, const char *type_instance,
439                 value_t *values, size_t values_len, mysql_database_t *db)
440 {
441         value_list_t vl = VALUE_LIST_INIT;
442
443         vl.values     = values;
444         vl.values_len = values_len;
445
446         set_host (db, vl.host, sizeof (vl.host));
447
448         sstrncpy (vl.plugin, "mysql", sizeof (vl.plugin));
449         set_plugin_instance (db, vl.plugin_instance, sizeof (vl.plugin_instance));
450
451         sstrncpy (vl.type, type, sizeof (vl.type));
452         if (type_instance != NULL)
453                 sstrncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
454
455         plugin_dispatch_values (&vl);
456 } /* submit */
457
458 static void counter_submit (const char *type, const char *type_instance,
459                 counter_t value, mysql_database_t *db)
460 {
461         value_t values[1];
462
463         values[0].counter = value;
464         submit (type, type_instance, values, STATIC_ARRAY_SIZE (values), db);
465 } /* void counter_submit */
466
467 static void gauge_submit (const char *type, const char *type_instance,
468                 gauge_t value, mysql_database_t *db)
469 {
470         value_t values[1];
471
472         values[0].gauge = value;
473         submit (type, type_instance, values, STATIC_ARRAY_SIZE (values), db);
474 } /* void gauge_submit */
475
476 static void qcache_submit (counter_t hits, counter_t inserts,
477                 counter_t not_cached, counter_t lowmem_prunes,
478                 gauge_t queries_in_cache, mysql_database_t *db)
479 {
480         value_t values[5];
481
482         values[0].counter = hits;
483         values[1].counter = inserts;
484         values[2].counter = not_cached;
485         values[3].counter = lowmem_prunes;
486         values[4].gauge   = queries_in_cache;
487
488         submit ("mysql_qcache", NULL, values, STATIC_ARRAY_SIZE (values), db);
489 } /* void qcache_submit */
490
491 static void threads_submit (gauge_t running, gauge_t connected, gauge_t cached,
492                 counter_t created, mysql_database_t *db)
493 {
494         value_t values[4];
495
496         values[0].gauge   = running;
497         values[1].gauge   = connected;
498         values[2].gauge   = cached;
499         values[3].counter = created;
500
501         submit ("mysql_threads", NULL, values, STATIC_ARRAY_SIZE (values), db);
502 } /* void threads_submit */
503
504 static void traffic_submit (counter_t rx, counter_t tx, mysql_database_t *db)
505 {
506         value_t values[2];
507
508         values[0].counter = rx;
509         values[1].counter = tx;
510
511         submit ("mysql_octets", NULL, values, STATIC_ARRAY_SIZE (values), db);
512 } /* void traffic_submit */
513
514 static MYSQL_RES *exec_query (MYSQL *con, const char *query)
515 {
516         MYSQL_RES *res;
517
518         int query_len = strlen (query);
519
520         if (mysql_real_query (con, query, query_len))
521         {
522                 ERROR ("mysql plugin: Failed to execute query: %s",
523                                 mysql_error (con));
524                 INFO ("mysql plugin: SQL query was: %s", query);
525                 return (NULL);
526         }
527
528         res = mysql_store_result (con);
529         if (res == NULL)
530         {
531                 ERROR ("mysql plugin: Failed to store query result: %s",
532                                 mysql_error (con));
533                 INFO ("mysql plugin: SQL query was: %s", query);
534                 return (NULL);
535         }
536
537         return (res);
538 } /* exec_query */
539
540 static int mysql_read_master_stats (mysql_database_t *db, MYSQL *con)
541 {
542         MYSQL_RES *res;
543         MYSQL_ROW  row;
544
545         char *query;
546         int   field_num;
547         unsigned long long position;
548
549         query = "SHOW MASTER STATUS";
550
551         res = exec_query (con, query);
552         if (res == NULL)
553                 return (-1);
554
555         row = mysql_fetch_row (res);
556         if (row == NULL)
557         {
558                 ERROR ("mysql plugin: Failed to get master statistics: "
559                                 "`%s' did not return any rows.", query);
560                 return (-1);
561         }
562
563         field_num = mysql_num_fields (res);
564         if (field_num < 2)
565         {
566                 ERROR ("mysql plugin: Failed to get master statistics: "
567                                 "`%s' returned less than two columns.", query);
568                 return (-1);
569         }
570
571         position = atoll (row[1]);
572         counter_submit ("mysql_log_position", "master-bin", position, db);
573
574         row = mysql_fetch_row (res);
575         if (row != NULL)
576                 WARNING ("mysql plugin: `%s' returned more than one row - "
577                                 "ignoring further results.", query);
578
579         mysql_free_result (res);
580
581         return (0);
582 } /* mysql_read_master_stats */
583
584 static int mysql_read_slave_stats (mysql_database_t *db, MYSQL *con)
585 {
586         MYSQL_RES *res;
587         MYSQL_ROW  row;
588
589         char *query;
590         int   field_num;
591
592         /* WTF? libmysqlclient does not seem to provide any means to
593          * translate a column name to a column index ... :-/ */
594         const int READ_MASTER_LOG_POS_IDX   = 6;
595         const int SLAVE_IO_RUNNING_IDX      = 10;
596         const int SLAVE_SQL_RUNNING_IDX     = 11;
597         const int EXEC_MASTER_LOG_POS_IDX   = 21;
598         const int SECONDS_BEHIND_MASTER_IDX = 32;
599
600         query = "SHOW SLAVE STATUS";
601
602         res = exec_query (con, query);
603         if (res == NULL)
604                 return (-1);
605
606         row = mysql_fetch_row (res);
607         if (row == NULL)
608         {
609                 ERROR ("mysql plugin: Failed to get slave statistics: "
610                                 "`%s' did not return any rows.", query);
611                 return (-1);
612         }
613
614         field_num = mysql_num_fields (res);
615         if (field_num < 33)
616         {
617                 ERROR ("mysql plugin: Failed to get slave statistics: "
618                                 "`%s' returned less than 33 columns.", query);
619                 return (-1);
620         }
621
622         if (db->slave_stats)
623         {
624                 unsigned long long counter;
625                 double gauge;
626
627                 counter = atoll (row[READ_MASTER_LOG_POS_IDX]);
628                 counter_submit ("mysql_log_position", "slave-read", counter, db);
629
630                 counter = atoll (row[EXEC_MASTER_LOG_POS_IDX]);
631                 counter_submit ("mysql_log_position", "slave-exec", counter, db);
632
633                 if (row[SECONDS_BEHIND_MASTER_IDX] != NULL)
634                 {
635                         gauge = atof (row[SECONDS_BEHIND_MASTER_IDX]);
636                         gauge_submit ("time_offset", NULL, gauge, db);
637                 }
638         }
639
640         if (db->slave_notif)
641         {
642                 notification_t n = { 0, time (NULL), "", "",
643                         "mysql", "", "time_offset", "", NULL };
644
645                 char *io, *sql;
646
647                 io  = row[SLAVE_IO_RUNNING_IDX];
648                 sql = row[SLAVE_SQL_RUNNING_IDX];
649
650                 set_host (db, n.host, sizeof (n.host));
651                 set_plugin_instance (db,
652                                 n.plugin_instance, sizeof (n.plugin_instance));
653
654                 if (((io == NULL) || (strcasecmp (io, "yes") != 0))
655                                 && (db->slave_io_running))
656                 {
657                         n.severity = NOTIF_WARNING;
658                         ssnprintf (n.message, sizeof (n.message),
659                                         "slave I/O thread not started or not connected to master");
660                         plugin_dispatch_notification (&n);
661                         db->slave_io_running = 0;
662                 }
663                 else if (((io != NULL) && (strcasecmp (io, "yes") == 0))
664                                 && (! db->slave_io_running))
665                 {
666                         n.severity = NOTIF_OKAY;
667                         ssnprintf (n.message, sizeof (n.message),
668                                         "slave I/O thread started and connected to master");
669                         plugin_dispatch_notification (&n);
670                         db->slave_io_running = 1;
671                 }
672
673                 if (((sql == NULL) || (strcasecmp (sql, "yes") != 0))
674                                 && (db->slave_sql_running))
675                 {
676                         n.severity = NOTIF_WARNING;
677                         ssnprintf (n.message, sizeof (n.message),
678                                         "slave SQL thread not started");
679                         plugin_dispatch_notification (&n);
680                         db->slave_sql_running = 0;
681                 }
682                 else if (((sql != NULL) && (strcasecmp (sql, "yes") == 0))
683                                 && (! db->slave_sql_running))
684                 {
685                         n.severity = NOTIF_OKAY;
686                         ssnprintf (n.message, sizeof (n.message),
687                                         "slave SQL thread started");
688                         plugin_dispatch_notification (&n);
689                         db->slave_sql_running = 0;
690                 }
691         }
692
693         row = mysql_fetch_row (res);
694         if (row != NULL)
695                 WARNING ("mysql plugin: `%s' returned more than one row - "
696                                 "ignoring further results.", query);
697
698         mysql_free_result (res);
699
700         return (0);
701 } /* mysql_read_slave_stats */
702
703 static int mysql_read (user_data_t *ud)
704 {
705         mysql_database_t *db;
706         MYSQL     *con;
707         MYSQL_RES *res;
708         MYSQL_ROW  row;
709         char      *query;
710         int        field_num;
711
712         unsigned long long qcache_hits          = 0ULL;
713         unsigned long long qcache_inserts       = 0ULL;
714         unsigned long long qcache_not_cached    = 0ULL;
715         unsigned long long qcache_lowmem_prunes = 0ULL;
716         int qcache_queries_in_cache = -1;
717
718         int threads_running   = -1;
719         int threads_connected = -1;
720         int threads_cached    = -1;
721         unsigned long long threads_created = 0ULL;
722
723         unsigned long long traffic_incoming = 0ULL;
724         unsigned long long traffic_outgoing = 0ULL;
725
726         if ((ud == NULL) || (ud->data == NULL))
727         {
728                 ERROR ("mysql plugin: mysql_database_read: Invalid user data.");
729                 return (-1);
730         }
731
732         db = (mysql_database_t *) ud->data;
733
734         /* An error message will have been printed in this case */
735         if ((con = getconnection (db)) == NULL)
736                 return (-1);
737
738         query = "SHOW STATUS";
739         if (mysql_get_server_version (con) >= 50002)
740                 query = "SHOW GLOBAL STATUS";
741
742         res = exec_query (con, query);
743         if (res == NULL)
744                 return (-1);
745
746         field_num = mysql_num_fields (res);
747         while ((row = mysql_fetch_row (res)))
748         {
749                 char *key;
750                 unsigned long long val;
751
752                 key = row[0];
753                 val = atoll (row[1]);
754
755                 if (strncmp (key, "Com_", 
756                                   strlen ("Com_")) == 0)
757                 {
758                         if (val == 0ULL)
759                                 continue;
760
761                         /* Ignore `prepared statements' */
762                         if (strncmp (key, "Com_stmt_", strlen ("Com_stmt_")) != 0)
763                                 counter_submit ("mysql_commands", 
764                                                 key + strlen ("Com_"), 
765                                                 val, db);
766                 }
767                 else if (strncmp (key, "Handler_", 
768                                         strlen ("Handler_")) == 0)
769                 {
770                         if (val == 0ULL)
771                                 continue;
772
773                         counter_submit ("mysql_handler", 
774                                         key + strlen ("Handler_"), 
775                                         val, db);
776                 }
777                 else if (strncmp (key, "Qcache_",
778                                         strlen ("Qcache_")) == 0)
779                 {
780                         if (strcmp (key, "Qcache_hits") == 0)
781                                 qcache_hits = val;
782                         else if (strcmp (key, "Qcache_inserts") == 0)
783                                 qcache_inserts = val;
784                         else if (strcmp (key, "Qcache_not_cached") == 0)
785                                 qcache_not_cached = val;
786                         else if (strcmp (key, "Qcache_lowmem_prunes") == 0)
787                                 qcache_lowmem_prunes = val;
788                         else if (strcmp (key, "Qcache_queries_in_cache") == 0)
789                                 qcache_queries_in_cache = (int) val;
790                 }
791                 else if (strncmp (key, "Bytes_", 
792                                         strlen ("Bytes_")) == 0)
793                 {
794                         if (strcmp (key, "Bytes_received") == 0)
795                                 traffic_incoming += val;
796                         else if (strcmp (key, "Bytes_sent") == 0)
797                                 traffic_outgoing += val;
798                 }
799                 else if (strncmp (key, "Threads_", 
800                                         strlen ("Threads_")) == 0)
801                 {
802                         if (strcmp (key, "Threads_running") == 0)
803                                 threads_running = (int) val;
804                         else if (strcmp (key, "Threads_connected") == 0)
805                                 threads_connected = (int) val;
806                         else if (strcmp (key, "Threads_cached") == 0)
807                                 threads_cached = (int) val;
808                         else if (strcmp (key, "Threads_created") == 0)
809                                 threads_created = val;
810                 }
811                 else if (strncmp (key, "Table_locks_",
812                                         strlen ("Table_locks_")) == 0)
813                 {
814                         counter_submit ("mysql_locks",
815                                         key + strlen ("Table_locks_"),
816                                         val, db);
817                 }
818         }
819         mysql_free_result (res); res = NULL;
820
821         if ((qcache_hits != 0ULL)
822                         || (qcache_inserts != 0ULL)
823                         || (qcache_not_cached != 0ULL)
824                         || (qcache_lowmem_prunes != 0ULL))
825                 qcache_submit (qcache_hits, qcache_inserts, qcache_not_cached,
826                                qcache_lowmem_prunes, qcache_queries_in_cache, db);
827
828         if (threads_created != 0ULL)
829                 threads_submit (threads_running, threads_connected,
830                                 threads_cached, threads_created, db);
831
832         traffic_submit  (traffic_incoming, traffic_outgoing, db);
833
834         if (db->master_stats)
835                 mysql_read_master_stats (db, con);
836
837         if ((db->slave_stats) || (db->slave_notif))
838                 mysql_read_slave_stats (db, con);
839
840         return (0);
841 } /* int mysql_read */
842
843 void module_register (void)
844 {
845         plugin_register_complex_config ("mysql", mysql_config);
846 } /* void module_register */