diff options
Diffstat (limited to 'src/mysqlstat.c')
-rw-r--r-- | src/mysqlstat.c | 509 |
1 files changed, 289 insertions, 220 deletions
diff --git a/src/mysqlstat.c b/src/mysqlstat.c index 3e89424..938439a 100644 --- a/src/mysqlstat.c +++ b/src/mysqlstat.c @@ -1,4 +1,4 @@ -/* This file is part of Mysqlstat -*- autoconf -*- +/* This file is part of Mysqlstat * Copyright (C) 2014-2016 Sergey Poznyakoff * * Mysqlstat is free software; you can redistribute it and/or modify @@ -19,6 +19,7 @@ #include <mysql/mysql.h> #undef NDEBUG #include <assert.h> +#include "mysqlstat_mib.h" static char *config_file = CONFDIR "/mysqlstat.cnf"; @@ -27,7 +28,7 @@ struct mysqlstat_connection { }; static void * -xalloc(size_t s) +xmalloc(size_t s) { void *p = malloc(s); assert(p != NULL); @@ -65,7 +66,7 @@ mysqlstat_connect(void) mysql_options(&conn->mysql, MYSQL_OPT_RECONNECT, &t); if (!mysql_real_connect(&conn->mysql, NULL, NULL, NULL, NULL, 0, NULL, 0)) { - snmp_log(LOG_ERR, "can't connect to MySQL: %s", + snmp_log(LOG_ERR, "can't connect to MySQL: %s\n", mysql_error(&conn->mysql)); free(conn); conn = NULL; @@ -82,280 +83,348 @@ mysqlstat_disconnect(mysqlstat_connection_t conn) free(conn); } -struct syment { - char *name; - char *value; -}; - -static unsigned int hash_size[] = { - 7, 17, 37, 101, 229, 487, 1009, 2039, 4091, 8191, 16411 -}; +unsigned mysqlstat_cache_timeout = CACHE_TIMEOUT; -/* |max_rehash| keeps the number of entries in |hash_size| table. */ -static unsigned int max_rehash = sizeof(hash_size) / sizeof(hash_size[0]); +#define __cat2__(a,b) a ## b +#define ASSIGN_STRING(e,m,s) \ + do { \ + if (s) { \ + (e)->__cat2__(m,_len) = strlen(s); \ + (e)->m = xmalloc((e)->__cat2__(m,_len)); \ + memcpy((e)->m, s, (e)->__cat2__(m,_len)); \ + } else { \ + (e)->__cat2__(m,_len) = 0; \ + (e)->m = NULL; \ + } \ + } while (0) -struct hashtab { - uint32_t hc; - struct syment *tab; -}; - -static void -syment_free(struct syment *ent) -{ - free(ent->name); - ent->name = NULL; - free(ent->value); - ent->value = NULL; -} +#define ASSIGN_ROW(e,m,r) ASSIGN_STRING(e,m,(r)[__cat2__(FI_,m)]) -static int -syment_matches(struct syment *ent, char const *name) -{ - if (!ent->name) - return 0; - return strcmp(ent->name, name) == 0; -} +#define STREQ(pat,str) (strcmp(#pat, str) == 0) -static uint32_t -hash_string(const char *name, uint32_t hashsize) + +static long +val_Slave_IO_Running(char const *val) { - unsigned i; - - for (i = 0; *name; name++) { - i <<= 1; - i ^= *(unsigned char*) name; - } - return i % hashsize; + if (strcmp(val, "Yes") == 0) + return 1; + else if (strcmp(val, "No") == 0) + return 2; + else if (strcmp(val, "Connected") == 0) + return 3; + return 4; } -static uint32_t -find_insert_pos(struct hashtab *ht, struct syment *ent) +static long +val_bool(char const *val) { - uint32_t i; - uint32_t pos = hash_string(ent->name, hash_size[ht->hc]); - - for (i = pos; ht->tab[i].name; ) { - i = (i + 1) % hash_size[ht->hc]; - if (i == pos) - /* FIXME: Error message? */ - abort(); - } - return i; + if (strcmp(val, "Yes") == 0) + return 1; + return 2; } static void -rehash(struct hashtab *ht) +store_slave_status_row(struct replSlaveStatusTable_entry *ent, + char const *name, char const *value) { - struct syment *oldtab = ht->tab; - uint32_t i, n; - - assert(ht->hc + 1 < max_rehash); - n = hash_size[ht->hc]; - ++ht->hc; - ht->tab = xcalloc(hash_size[ht->hc], sizeof(ht->tab[0])); - for (i = 0; i < n; i++) { - if (oldtab[i].name) - ht->tab[find_insert_pos(ht, &oldtab[i])] = oldtab[i]; + if (STREQ(Slave_IO_State, name)) { + ASSIGN_STRING(ent, replSlaveIOState, value); + } else if (STREQ(Master_Host, name)) { + ASSIGN_STRING(ent, replMasterHost, value); + } else if (STREQ(Master_User, name)) { + ASSIGN_STRING(ent, replMasterUser, value); + } else if (STREQ(Master_Port, name)) { + ent->replMasterPort = strtoul(value, NULL, 10); + } else if (STREQ(Connect_Retry, name)) { + ent->replConnectRetry = strtoul(value, NULL, 10); + } else if (STREQ(Master_Log_File, name)) { + ASSIGN_STRING(ent, replMasterLogFile, value); + } else if (STREQ(Read_Master_Log_Pos, name)) { + ent->replReadMasterLogPos = strtoul(value, NULL, 10); + } else if (STREQ(Relay_Log_File, name)) { + ASSIGN_STRING(ent, replRelayLogFile, value); + } else if (STREQ(Relay_Log_Pos, name)) { + ent->replRelayLogPos = strtoul(value, NULL, 10); + } else if (STREQ(Relay_Master_Log_File, name)) { + ASSIGN_STRING(ent, replMasterLogFile, value); + } else if (STREQ(Slave_IO_Running, name)) { + ent->replSlaveIORunning = val_Slave_IO_Running(value); + } else if (STREQ(Slave_SQL_Running, name)) { + ent->replSlaveSQLRunning = val_bool(value); + } else if (STREQ(Replicate_Do_DB, name)) { + ASSIGN_STRING(ent, replReplicateDoDB, value); + } else if (STREQ(Replicate_Ignore_DB, name)) { + ASSIGN_STRING(ent, replReplicateIgnoreDB, value); + } else if (STREQ(Replicate_Do_Table, name)) { + ASSIGN_STRING(ent, replReplicateDoTable, value); + } else if (STREQ(Replicate_Ignore_Table, name)) { + ASSIGN_STRING(ent, replReplicateIgnoreTable, value); + } else if (STREQ(Replicate_Wild_Do_Table, name)) { + ASSIGN_STRING(ent, replReplicateWildDoTable, value); + } else if (STREQ(Replicate_Wild_Ignore_Table, name)) { + ASSIGN_STRING(ent, replReplicateWildIgnoreTable, value); + } else if (STREQ(Last_IO_Errno, name)) { + ent->replLastIOErrno = strtoul(value, NULL, 10); + } else if (STREQ(Last_IO_Error, name)) { + ASSIGN_STRING(ent, replLastIOError, value); + } else if (STREQ(Last_SQL_Errno, name)) { + ent->replLastSQLErrno = strtoul(value, NULL, 10); + } else if (STREQ(Last_SQL_Error, name)) { + ASSIGN_STRING(ent, replLastSQLError, value); + } else if (STREQ(Skip_Counter, name)) { + ent->replSkipCounter = strtoul(value, NULL, 10); + } else if (STREQ(Exec_Master_Log_Pos, name)) { + ent->replExecMasterLogPos = strtoul(value, NULL, 10); + } else if (STREQ(Relay_Log_Space, name)) { + uint64_t n = strtoull(value, NULL, 10); + ent->replRelayLogSpace.high = n >> 32; + ent->replRelayLogSpace.low = n & 0xffffffff; + } else if (STREQ(Until_Condition, name)) { + ASSIGN_STRING(ent, replUntilCondition, value); + } else if (STREQ(Until_Log_File, name)) { + ASSIGN_STRING(ent, replUntilLogFile, value); + } else if (STREQ(Until_Log_Pos, name)) { + ASSIGN_STRING(ent, replUntilLogPos, value); + } else if (STREQ(Master_SSL_Allowed, name)) { + ASSIGN_STRING(ent, replMasterSSLAllowed, value); + } else if (STREQ(Master_SSL_CA_File, name)) { + ASSIGN_STRING(ent, replMasterSSLCAFile, value); + } else if (STREQ(Master_SSL_CA_Path, name)) { + ASSIGN_STRING(ent, replMasterSSLCAPath, value); + } else if (STREQ(Master_SSL_Cert, name)) { + ASSIGN_STRING(ent, replMasterSSLCert, value); + } else if (STREQ(Master_SSL_Cipher, name)) { + ASSIGN_STRING(ent, replMasterSSLCipher, value); + } else if (STREQ(Master_SSL_Key, name)) { + ASSIGN_STRING(ent, replMasterSSLKey, value); + } else if (STREQ(Seconds_Behind_Master, name)) { + ent->replSecondsBehindMaster = strtoul(value, NULL, 10) * 100; + } else if (STREQ(Master_SSL_Verify_Server_Cert, name)) { + ent->replMasterSSLVerifyServerCert = val_bool(value); } - free(oldtab); } -static void -hashtab_remove(struct hashtab *ht, char const *name) +int +replSlaveStatusTable_load(netsnmp_cache *cache, void *vmagic) { - uint32_t pos, i, j, r; + mysqlstat_connection_t conn; + MYSQL_RES *res; + MYSQL_ROW row; + MYSQL_FIELD *fields; + unsigned int num_fields, i; + netsnmp_tdata *table = (netsnmp_tdata *) vmagic; + struct replSlaveStatusTable_entry *ent; + netsnmp_tdata_row *data_row; - pos = hash_string(name, hash_size[ht->hc]); - for (i = pos; ht->tab[i].name;) { - if (syment_matches(&ht->tab[i], name)) - break; - i = (i + 1) % hash_size[ht->hc]; - if (i == pos) - return;// ENOENT - } + conn = mysqlstat_connect(); + if (!conn) + return SNMP_ERR_NOSUCHNAME; - syment_free(&ht->tab[i]); - - for (;;) { - ht->tab[i].name = ht->tab[i].value = NULL; - j = i; - - do { - i = (i + 1) % hash_size[ht->hc]; - if (!ht->tab[i].name) - return; - r = hash_string(ht->tab[i].name, hash_size[ht->hc]); - } while ((j < r && r <= i) - || (i < j && j < r) || (r <= i && i < j)); - ht->tab[j] = ht->tab[i]; + DEBUGMSGTL(("mysqlstat", "Getting slave status\n")); + if (mysql_query(&conn->mysql, "SHOW SLAVE STATUS")) { + snmp_log(LOG_ERR, "can't get slave status: %s\n", + mysql_error(&conn->mysql)); + mysqlstat_disconnect(conn); + return SNMP_ERR_NOSUCHNAME; } -} -static char const * -hashtab_lookup(struct hashtab *ht, char const *name) -{ - uint32_t pos, i; - - pos = hash_string(name, hash_size[ht->hc]); - for (i = pos; ht->tab[i].name;) { - if (syment_matches(&ht->tab[i], name)) - return ht->tab[i].value; - i = (i + 1) % hash_size[ht->hc]; - if (i == pos) - break; + res = mysql_store_result(&conn->mysql); + if (mysql_num_rows(res) < 1) { + snmp_log(LOG_INFO, "empty slave status\n"); + return 0; } - return NULL; -} -static void -hashtab_install(struct hashtab *ht, char const *name, char const *value) -{ - uint32_t i, pos; - struct syment *ent; - - if (!value) - value = ""; - DEBUGMSGTL(("mysqlstat", "%s = %s\n", name, value)); - pos = hash_string(name, hash_size[ht->hc]); - for (i = pos; ht->tab[i].name;) { - if (syment_matches(&ht->tab[i], name)) { - free(ht->tab[i].value); - ht->tab[i].value = xstrdup(value); - return; - } - i = (i + 1) % hash_size[ht->hc]; - if (i == pos) { - rehash(ht); - i = pos = hash_string(name, hash_size[ht->hc]); - } + num_fields = mysql_num_fields(res); + fields = mysql_fetch_fields(res); + row = mysql_fetch_row(res); + + ent = SNMP_MALLOC_TYPEDEF(struct replSlaveStatusTable_entry); + if (!ent) { + mysqlstat_disconnect(conn); + return SNMP_ERR_GENERR; } - ht->tab[i].name = xstrdup(name); - ht->tab[i].value = xstrdup(value); -} + data_row = netsnmp_tdata_create_row(); + if (!data_row) { + SNMP_FREE(ent); + mysqlstat_disconnect(conn); + return SNMP_ERR_GENERR; + } + data_row->data = ent; + ent->replSlaveIndex = 0; + netsnmp_tdata_row_add_index(data_row, ASN_INTEGER, + &ent->replSlaveIndex, + sizeof(ent->replSlaveIndex)); -void -hashtab_clear(struct hashtab *ht) -{ - unsigned i, hs; + netsnmp_tdata_add_row(table, data_row); + + for (i = 0; i < num_fields; i++) + store_slave_status_row (ent, fields[i].name, row[i]); - hs = hash_size[ht->hc]; - for (i = 0; i < hs; i++) { - syment_free(&ht->tab[i]); - } + mysql_free_result(res); + mysqlstat_disconnect(conn); + return 0; } -static struct hashtab * -hashtab_create(void) +void +replSlaveStatusTable_entry_free(void *data) { - struct hashtab *ht = xalloc(sizeof(*ht)); - ht->hc = 0; - ht->tab = xcalloc(hash_size[ht->hc], sizeof(ht->tab[0])); - return ht; + struct replSlaveStatusTable_entry *ent = data; + free(ent->replSlaveIOState); + free(ent->replMasterLogFile); + free(ent->replRelayLogFile); + free(ent->replRelayMasterLogFile); + free(ent->replLastSQLError); + free(ent->replLastIOError); + free(ent->replMasterHost); + free(ent->replMasterUser); + free(ent->replReplicateDoDB); + free(ent->replReplicateIgnoreDB); + free(ent->replReplicateDoTable); + free(ent->replReplicateIgnoreTable); + free(ent->replReplicateWildDoTable); + free(ent->replReplicateWildIgnoreTable); + free(ent->replUntilCondition); + free(ent->replUntilLogFile); + free(ent->replUntilLogPos); + free(ent->replMasterSSLAllowed); + free(ent->replMasterSSLCAFile); + free(ent->replMasterSSLCAPath); + free(ent->replMasterSSLCert); + free(ent->replMasterSSLCipher); + free(ent->replMasterSSLKey); } -struct mysqlstat_cache { - void (*populate) (struct hashtab *); - struct hashtab *ht; - time_t ts; -}; -static unsigned cache_timeout = CACHE_TIMEOUT; +enum { + FI_processID, + FI_processUser, + FI_processHost, + FI_processDatabase, + FI_processCommand, + FI_processTime, + FI_processState, + FI_processInfo +}; -static char const * -mysqlstat_cache_get(struct mysqlstat_cache *cache, char const *name) -{ - time_t now = time(NULL); - struct syment *ent; - - if (!cache->ht) - cache->ht = hashtab_create(); - if (now - cache->ts > cache_timeout) { - hashtab_clear(cache->ht); - cache->populate(cache->ht); - cache->ts = now; - } - return hashtab_lookup(cache->ht, name); -} - -static char const * -val_Slave_IO_Running(char const *val) +static void +process_list_add(netsnmp_tdata *table_data, long idx, MYSQL_ROW mysql_row) { - if (strcmp(val, "Yes") == 0) - return "1"; - else if (strcmp(val, "No") == 0) - return "2"; - else if (strcmp(val, "Connected") == 0) - return "3"; - return 4; -} + struct processListTable_entry *ent; + netsnmp_tdata_row *data_row; -static char const * -val_bool(char const *val) -{ - if (strcmp(val, "Yes") == 0) - return "1"; - return "2"; + ent = SNMP_MALLOC_TYPEDEF(struct processListTable_entry); + if (!ent) + return; + + data_row = netsnmp_tdata_create_row(); + if (!data_row) { + SNMP_FREE(ent); + return; + } + data_row->data = ent; + + ent->processIndex = idx; + if (mysql_row[FI_processID]) + ent->processID = strtol(mysql_row[FI_processID], NULL, 10); + ASSIGN_ROW(ent, processUser, mysql_row); + ASSIGN_ROW(ent, processHost, mysql_row); + ASSIGN_ROW(ent, processDatabase, mysql_row); + ASSIGN_ROW(ent, processCommand, mysql_row); + if (mysql_row[FI_processTime]) + ent->processTime = strtol(mysql_row[FI_processTime], NULL, 10) * 100; + ASSIGN_ROW(ent, processState, mysql_row); + ASSIGN_ROW(ent, processInfo, mysql_row); + + netsnmp_tdata_row_add_index(data_row, ASN_INTEGER, + &ent->processIndex, + sizeof(ent->processIndex)); + + netsnmp_tdata_add_row(table_data, data_row); } -static void -populate_repl(struct hashtab *ht) +int +processListTable_load(netsnmp_cache *cache, void *vmagic) { mysqlstat_connection_t conn; MYSQL_RES *res; - MYSQL_ROW row; - MYSQL_FIELD *fields; - unsigned int num_fields, i; + unsigned int num_rows, i; + netsnmp_tdata *table = (netsnmp_tdata *) vmagic; + + DEBUGMSGTL(("mysqlstat", "Getting process list\n")); conn = mysqlstat_connect(); if (!conn) - return; + return SNMP_ERR_GENERR; - DEBUGMSGTL(("mysqlstat", "Getting slave status\n")); - if (mysql_query(&conn->mysql, "SHOW SLAVE STATUS")) { - snmp_log(LOG_ERR, "can't get slave status: %s", + if (mysql_query(&conn->mysql, "SHOW PROCESSLIST")) { + snmp_log(LOG_ERR, "can't get slave process list: %s\n", mysql_error(&conn->mysql)); - return; + mysqlstat_disconnect(conn); + return SNMP_ERR_NOSUCHNAME; } res = mysql_store_result(&conn->mysql); - if (mysql_num_rows(res) < 1) { - snmp_log(LOG_INFO, "empty slave status"); - return; - } + num_rows = mysql_num_rows(res); - num_fields = mysql_num_fields(res); - fields = mysql_fetch_fields(res); - row = mysql_fetch_row(res); - for (i = 0; i < num_fields; i++) { - if (row[i]) { - char const *val = row[i]; - /* FIXME: Move this conversion logic to mysqlstat_mib.mib2c */ - if (strcmp(fields[i].name, "Slave_IO_Running") == 0) { - val = val_Slave_IO_Running(val); - } else if (strcmp(fields[i].name, "Slave_SQL_Running") == 0) { - val = val_bool(val); - } else if (strcmp(fields[i].name, "Master_SSL_Verify_Server_Cert") == 0) { - val = val_bool(val); - } - hashtab_install(ht, fields[i].name, val); - } + for (i = 0; i < num_rows; i++) { + MYSQL_ROW row; + + mysql_data_seek(res, i); + row = mysql_fetch_row(res); + + process_list_add(table, i, row); } + mysql_free_result(res); mysqlstat_disconnect(conn); + + return 0; } - -static struct mysqlstat_cache cache[MYSQLSTAT_MAX_CACHE] = { - [MYSQLSTAT_CACHE_REPL] = { populate_repl } -}; -char const * -mysqlstat_get(int id, char const *name) +void +processListTable_entry_free(void *data) { - assert(id < sizeof(cache) / sizeof(cache[0])); - return mysqlstat_cache_get(cache + id, name); + struct processListTable_entry *ent = data; + free(ent->processHost); + free(ent->processUser); + free(ent->processCommand); + free(ent->processState); + free(ent->processInfo); } +#if 0 +unsigned replSlaveTable_timeout = CACHE_TIMEOUT; + +static void +replSlaveTable_entry_free(struct replSlaveTable_entry *ent) +{ +} + +int +replSlaveTable_load(netsnmp_cache *cache, void *vmagic) +{ +} + +void +replSlaveTable_free(netsnmp_cache *cache, void *vmagic) +{ + netsnmp_tdata *table = (netsnmp_tdata *) vmagic; + netsnmp_tdata_row *row; + + DEBUGMSGTL(("mysqlstat", "freeing table\n")); + while ((row = netsnmp_tdata_row_first(table))) { + struct replSlaveTable_entry *entry = row->data; + free(ent->replSlaveHost); + free(ent->replSlaveUser); + free(ent->replSlaveCommand); + free(ent->replSlaveState); + free(ent->replSlaveInfo); + SNMP_FREE(ent); + netsnmp_tdata_remove_and_delete_row(table, row); + } +} +#endif |