123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800 |
- /*
- * Asterisk -- An open source telephony toolkit.
- *
- * Copyright (C) 1999-2005, Digium, Inc.
- *
- * Mark Spencer <markster@digium.com> - Asterisk Author
- * Matthew Boehm <mboehm@cytelcom.com> - MySQL RealTime Driver Author
- *
- * See http://www.asterisk.org for more information about
- * the Asterisk project. Please do not directly contact
- * any of the maintainers of this project for assistance;
- * the project provides a web site, mailing lists and IRC
- * channels for your use.
- *
- * This program is free software, distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
- /*!
- * \file
- * \brief MySQL CDR backend
- */
- /*** MODULEINFO
- <depend>mysqlclient</depend>
- <defaultenabled>no</defaultenabled>
- <support_level>extended</support_level>
- ***/
- #include "asterisk.h"
- ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
- #include <sys/stat.h>
- #include <mysql/mysql.h>
- #include <mysql/mysql_version.h>
- #include <mysql/errmsg.h>
- #include "asterisk/channel.h"
- #include "asterisk/logger.h"
- #include "asterisk/config.h"
- #include "asterisk/module.h"
- #include "asterisk/lock.h"
- #include "asterisk/options.h"
- #include "asterisk/cli.h"
- #include "asterisk/utils.h"
- #include "asterisk/threadstorage.h"
- #include "asterisk/strings.h"
- #define RES_CONFIG_MYSQL_CONF "res_config_mysql.conf"
- #define RES_CONFIG_MYSQL_CONF_OLD "res_mysql.conf"
- #define READHANDLE 0
- #define WRITEHANDLE 1
- #define ESCAPE_STRING(buf, var) \
- do { \
- struct ast_str *semi = ast_str_thread_get(&scratch2_buf, strlen(var) * 3 + 1); \
- const char *chunk = var; \
- ast_str_reset(semi); \
- for (; *chunk; chunk++) { \
- if (strchr(";^", *chunk)) { \
- ast_str_append(&semi, 0, "^%02hhX", *chunk); \
- } else { \
- ast_str_append(&semi, 0, "%c", *chunk); \
- } \
- } \
- if (ast_str_strlen(semi) * 2 + 1 > ast_str_size(buf)) { \
- ast_str_make_space(&(buf), ast_str_strlen(semi) * 2 + 1); \
- } \
- mysql_real_escape_string(&dbh->handle, ast_str_buffer(buf), ast_str_buffer(semi), ast_str_strlen(semi)); \
- } while (0)
- AST_THREADSTORAGE(sql_buf);
- AST_THREADSTORAGE(sql2_buf);
- AST_THREADSTORAGE(find_buf);
- AST_THREADSTORAGE(scratch_buf);
- AST_THREADSTORAGE(scratch2_buf);
- AST_THREADSTORAGE(modify_buf);
- AST_THREADSTORAGE(modify2_buf);
- AST_THREADSTORAGE(modify3_buf);
- enum requirements { RQ_WARN, RQ_CREATECLOSE, RQ_CREATECHAR };
- struct mysql_conn {
- AST_RWLIST_ENTRY(mysql_conn) list;
- ast_mutex_t lock;
- MYSQL handle;
- char host[50];
- char name[50];
- char user[50];
- char pass[50];
- char sock[50];
- char charset[50];
- int port;
- int connected;
- time_t connect_time;
- enum requirements requirements;
- char unique_name[0];
- };
- struct columns {
- char *name;
- char *type;
- char *dflt;
- char null;
- int len;
- AST_LIST_ENTRY(columns) list;
- };
- struct tables {
- ast_mutex_t lock;
- AST_LIST_HEAD_NOLOCK(mysql_columns, columns) columns;
- AST_LIST_ENTRY(tables) list;
- struct mysql_conn *database;
- char name[0];
- };
- static AST_LIST_HEAD_STATIC(mysql_tables, tables);
- static AST_RWLIST_HEAD_STATIC(databases, mysql_conn);
- static int parse_config(int reload);
- static int mysql_reconnect(struct mysql_conn *conn);
- static char *handle_cli_realtime_mysql_status(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
- static char *handle_cli_realtime_mysql_cache(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
- static int load_mysql_config(struct ast_config *config, const char *category, struct mysql_conn *conn);
- static int require_mysql(const char *database, const char *tablename, va_list ap);
- static int internal_require(const char *database, const char *table, ...) attribute_sentinel;
- static struct ast_cli_entry cli_realtime_mysql_status[] = {
- AST_CLI_DEFINE(handle_cli_realtime_mysql_status, "Shows connection information for the MySQL RealTime driver"),
- AST_CLI_DEFINE(handle_cli_realtime_mysql_cache, "Shows cached tables within the MySQL realtime driver"),
- };
- static struct mysql_conn *find_database(const char *database, int for_write)
- {
- char *whichdb;
- const char *ptr;
- struct mysql_conn *cur;
- if ((ptr = strchr(database, '/'))) {
- /* Multiple databases encoded within string */
- if (for_write) {
- whichdb = ast_strdupa(ptr + 1);
- } else {
- whichdb = alloca(ptr - database + 1);
- strncpy(whichdb, database, ptr - database);
- whichdb[ptr - database] = '\0';
- }
- } else {
- whichdb = ast_strdupa(database);
- }
- AST_RWLIST_RDLOCK(&databases);
- AST_RWLIST_TRAVERSE(&databases, cur, list) {
- if (!strcmp(cur->unique_name, whichdb)) {
- ast_mutex_lock(&cur->lock);
- break;
- }
- }
- AST_RWLIST_UNLOCK(&databases);
- return cur;
- }
- #define release_database(a) ast_mutex_unlock(&(a)->lock)
- static int internal_require(const char *database, const char *table, ...)
- {
- va_list ap;
- int res;
- va_start(ap, table);
- res = require_mysql(database, table, ap);
- va_end(ap);
- return res;
- }
- static void destroy_table(struct tables *table)
- {
- struct columns *column;
- ast_mutex_lock(&table->lock);
- while ((column = AST_LIST_REMOVE_HEAD(&table->columns, list))) {
- ast_free(column);
- }
- ast_mutex_unlock(&table->lock);
- ast_mutex_destroy(&table->lock);
- ast_free(table);
- }
- static struct tables *find_table(const char *database, const char *tablename)
- {
- struct columns *column;
- struct tables *table;
- struct ast_str *sql = ast_str_thread_get(&find_buf, 30);
- char *fname, *ftype, *flen, *fdflt, *fnull;
- struct mysql_conn *dbh;
- MYSQL_RES *result;
- MYSQL_ROW row;
- if (!(dbh = find_database(database, 1))) {
- return NULL;
- }
- AST_LIST_LOCK(&mysql_tables);
- AST_LIST_TRAVERSE(&mysql_tables, table, list) {
- if (!strcasecmp(table->name, tablename)) {
- ast_mutex_lock(&table->lock);
- AST_LIST_UNLOCK(&mysql_tables);
- release_database(dbh);
- return table;
- }
- }
- /* Not found, scan the table */
- ast_str_set(&sql, 0, "DESC %s", tablename);
- if (!mysql_reconnect(dbh)) {
- release_database(dbh);
- AST_LIST_UNLOCK(&mysql_tables);
- return NULL;
- }
- if (mysql_real_query(&dbh->handle, ast_str_buffer(sql), ast_str_strlen(sql))) {
- ast_log(LOG_ERROR, "Failed to query database '%s', table '%s' columns: %s\n", database, tablename, mysql_error(&dbh->handle));
- release_database(dbh);
- AST_LIST_UNLOCK(&mysql_tables);
- return NULL;
- }
- if (!(table = ast_calloc(1, sizeof(*table) + strlen(tablename) + 1))) {
- ast_log(LOG_ERROR, "Unable to allocate memory for new table structure\n");
- release_database(dbh);
- AST_LIST_UNLOCK(&mysql_tables);
- return NULL;
- }
- strcpy(table->name, tablename); /* SAFE */
- table->database = dbh;
- ast_mutex_init(&table->lock);
- AST_LIST_HEAD_INIT_NOLOCK(&table->columns);
- if ((result = mysql_store_result(&dbh->handle))) {
- while ((row = mysql_fetch_row(result))) {
- fname = row[0];
- ftype = row[1];
- fnull = row[2];
- fdflt = row[4];
- ast_verb(4, "Found column '%s' of type '%s'\n", fname, ftype);
- if (fdflt == NULL) {
- fdflt = "";
- }
- if (!(column = ast_calloc(1, sizeof(*column) + strlen(fname) + strlen(ftype) + strlen(fdflt) + 3))) {
- ast_log(LOG_ERROR, "Unable to allocate column element %s for %s\n", fname, tablename);
- destroy_table(table);
- release_database(dbh);
- AST_LIST_UNLOCK(&mysql_tables);
- return NULL;
- }
- if ((flen = strchr(ftype, '('))) {
- sscanf(flen, "(%30d)", &column->len);
- } else {
- /* Columns like dates, times, and timestamps don't have a length */
- column->len = -1;
- }
- column->name = (char *)column + sizeof(*column);
- column->type = (char *)column + sizeof(*column) + strlen(fname) + 1;
- column->dflt = (char *)column + sizeof(*column) + strlen(fname) + 1 + strlen(ftype) + 1;
- strcpy(column->name, fname);
- strcpy(column->type, ftype);
- strcpy(column->dflt, fdflt);
- column->null = (strcmp(fnull, "YES") == 0 ? 1 : 0);
- AST_LIST_INSERT_TAIL(&table->columns, column, list);
- }
- mysql_free_result(result);
- }
- AST_LIST_INSERT_TAIL(&mysql_tables, table, list);
- ast_mutex_lock(&table->lock);
- AST_LIST_UNLOCK(&mysql_tables);
- release_database(dbh);
- return table;
- }
- static void release_table(struct tables *table)
- {
- if (table) {
- ast_mutex_unlock(&table->lock);
- }
- }
- static struct columns *find_column(struct tables *table, const char *colname)
- {
- struct columns *column;
- AST_LIST_TRAVERSE(&table->columns, column, list) {
- if (strcmp(column->name, colname) == 0) {
- break;
- }
- }
- return column;
- }
- static char *decode_chunk(char *chunk)
- {
- char *orig = chunk;
- for (; *chunk; chunk++) {
- if (*chunk == '^' && strchr("0123456789ABCDEFabcdef", chunk[1]) && strchr("0123456789ABCDEFabcdef", chunk[2])) {
- sscanf(chunk + 1, "%02hhX", chunk);
- memmove(chunk + 1, chunk + 3, strlen(chunk + 3) + 1);
- }
- }
- return orig;
- }
- static struct ast_variable *realtime_mysql(const char *database, const char *table, va_list ap)
- {
- struct mysql_conn *dbh;
- MYSQL_RES *result;
- MYSQL_ROW row;
- MYSQL_FIELD *fields;
- int numFields, i;
- struct ast_str *sql = ast_str_thread_get(&sql_buf, 16);
- struct ast_str *buf = ast_str_thread_get(&scratch_buf, 16);
- char *stringp;
- char *chunk;
- char *op;
- const char *newparam, *newval;
- struct ast_variable *var=NULL, *prev=NULL;
- if (!(dbh = find_database(database, 0))) {
- ast_log(LOG_WARNING, "MySQL RealTime: Invalid database specified: %s (check res_mysql.conf)\n", database);
- return NULL;
- }
- if (!table) {
- ast_log(LOG_WARNING, "MySQL RealTime: No table specified.\n");
- release_database(dbh);
- return NULL;
- }
- /* Get the first parameter and first value in our list of passed paramater/value pairs */
- newparam = va_arg(ap, const char *);
- newval = va_arg(ap, const char *);
- if (!newparam || !newval) {
- ast_log(LOG_WARNING, "MySQL RealTime: Realtime retrieval requires at least 1 parameter and 1 value to search on.\n");
- release_database(dbh);
- return NULL;
- }
- /* Must connect to the server before anything else, as the escape function requires the mysql handle. */
- if (!mysql_reconnect(dbh)) {
- release_database(dbh);
- return NULL;
- }
- /* Create the first part of the query using the first parameter/value pairs we just extracted
- If there is only 1 set, then we have our query. Otherwise, loop thru the list and concat */
- if (!strchr(newparam, ' '))
- op = " =";
- else
- op = "";
- ESCAPE_STRING(buf, newval);
- ast_str_set(&sql, 0, "SELECT * FROM %s WHERE %s%s '%s'", table, newparam, op, ast_str_buffer(buf));
- while ((newparam = va_arg(ap, const char *))) {
- newval = va_arg(ap, const char *);
- if (!strchr(newparam, ' '))
- op = " =";
- else
- op = "";
- ESCAPE_STRING(buf, newval);
- ast_str_append(&sql, 0, " AND %s%s '%s'", newparam, op, ast_str_buffer(buf));
- }
- va_end(ap);
- ast_debug(1, "MySQL RealTime: Retrieve SQL: %s\n", ast_str_buffer(sql));
- /* Execution. */
- if (mysql_real_query(&dbh->handle, ast_str_buffer(sql), ast_str_strlen(sql))) {
- ast_log(LOG_WARNING, "MySQL RealTime: Failed to query database: %s\n", mysql_error(&dbh->handle));
- release_database(dbh);
- return NULL;
- }
- if ((result = mysql_store_result(&dbh->handle))) {
- numFields = mysql_num_fields(result);
- fields = mysql_fetch_fields(result);
- while ((row = mysql_fetch_row(result))) {
- for (i = 0; i < numFields; i++) {
- /* Encode NULL values separately from blank values, for the Realtime API */
- if (row[i] == NULL) {
- row[i] = "";
- } else if (ast_strlen_zero(row[i])) {
- row[i] = " ";
- }
- for (stringp = row[i], chunk = strsep(&stringp, ";"); chunk; chunk = strsep(&stringp, ";")) {
- if (prev) {
- if ((prev->next = ast_variable_new(fields[i].name, decode_chunk(chunk), ""))) {
- prev = prev->next;
- }
- } else {
- prev = var = ast_variable_new(fields[i].name, decode_chunk(chunk), "");
- }
- }
- }
- }
- } else {
- ast_debug(1, "MySQL RealTime: Could not find any rows in table %s.\n", table);
- }
- release_database(dbh);
- mysql_free_result(result);
- return var;
- }
- static struct ast_config *realtime_multi_mysql(const char *database, const char *table, va_list ap)
- {
- struct mysql_conn *dbh;
- MYSQL_RES *result;
- MYSQL_ROW row;
- MYSQL_FIELD *fields;
- int numFields, i;
- struct ast_str *sql = ast_str_thread_get(&sql_buf, 16);
- struct ast_str *buf = ast_str_thread_get(&scratch_buf, 16);
- const char *initfield = NULL;
- char *stringp;
- char *chunk;
- char *op;
- const char *newparam, *newval;
- struct ast_variable *var = NULL;
- struct ast_config *cfg = NULL;
- struct ast_category *cat = NULL;
- if (!(dbh = find_database(database, 0))) {
- ast_log(LOG_WARNING, "MySQL RealTime: Invalid database specified: '%s' (check res_mysql.conf)\n", database);
- return NULL;
- }
- if (!table) {
- ast_log(LOG_WARNING, "MySQL RealTime: No table specified.\n");
- release_database(dbh);
- return NULL;
- }
-
- if (!(cfg = ast_config_new())) {
- /* If I can't alloc memory at this point, why bother doing anything else? */
- ast_log(LOG_WARNING, "Out of memory!\n");
- release_database(dbh);
- return NULL;
- }
- /* Get the first parameter and first value in our list of passed paramater/value pairs */
- newparam = va_arg(ap, const char *);
- newval = va_arg(ap, const char *);
- if (!newparam || !newval) {
- ast_log(LOG_WARNING, "MySQL RealTime: Realtime retrieval requires at least 1 parameter and 1 value to search on.\n");
- ast_config_destroy(cfg);
- release_database(dbh);
- return NULL;
- }
- initfield = ast_strdupa(newparam);
- if (initfield && (op = strchr(initfield, ' '))) {
- *op = '\0';
- }
- /* Must connect to the server before anything else, as the escape function requires the mysql handle. */
- if (!mysql_reconnect(dbh)) {
- release_database(dbh);
- ast_config_destroy(cfg);
- return NULL;
- }
- /* Create the first part of the query using the first parameter/value pairs we just extracted
- If there is only 1 set, then we have our query. Otherwise, loop thru the list and concat */
- if (!strchr(newparam, ' '))
- op = " =";
- else
- op = "";
- ESCAPE_STRING(buf, newval);
- ast_str_set(&sql, 0, "SELECT * FROM %s WHERE %s%s '%s'", table, newparam, op, ast_str_buffer(buf));
- while ((newparam = va_arg(ap, const char *))) {
- newval = va_arg(ap, const char *);
- if (!strchr(newparam, ' ')) op = " ="; else op = "";
- ESCAPE_STRING(buf, newval);
- ast_str_append(&sql, 0, " AND %s%s '%s'", newparam, op, ast_str_buffer(buf));
- }
- if (initfield) {
- ast_str_append(&sql, 0, " ORDER BY %s", initfield);
- }
- va_end(ap);
- ast_debug(1, "MySQL RealTime: Retrieve SQL: %s\n", ast_str_buffer(sql));
- /* Execution. */
- if (mysql_real_query(&dbh->handle, ast_str_buffer(sql), ast_str_strlen(sql))) {
- ast_log(LOG_WARNING, "MySQL RealTime: Failed to query database: %s\n", mysql_error(&dbh->handle));
- release_database(dbh);
- ast_config_destroy(cfg);
- return NULL;
- }
- if ((result = mysql_store_result(&dbh->handle))) {
- numFields = mysql_num_fields(result);
- fields = mysql_fetch_fields(result);
- while ((row = mysql_fetch_row(result))) {
- var = NULL;
- cat = ast_category_new("", "", -1);
- if (!cat) {
- ast_log(LOG_WARNING, "Out of memory!\n");
- continue;
- }
- for (i = 0; i < numFields; i++) {
- if (ast_strlen_zero(row[i]))
- continue;
- for (stringp = row[i], chunk = strsep(&stringp, ";"); chunk; chunk = strsep(&stringp, ";")) {
- if (chunk && !ast_strlen_zero(decode_chunk(ast_strip(chunk)))) {
- if (initfield && !strcmp(initfield, fields[i].name)) {
- ast_category_rename(cat, chunk);
- }
- var = ast_variable_new(fields[i].name, chunk, "");
- ast_variable_append(cat, var);
- }
- }
- }
- ast_category_append(cfg, cat);
- }
- } else {
- ast_debug(1, "MySQL RealTime: Could not find any rows in table %s.\n", table);
- }
- release_database(dbh);
- mysql_free_result(result);
- return cfg;
- }
- static int update_mysql(const char *database, const char *tablename, const char *keyfield, const char *lookup, va_list ap)
- {
- struct mysql_conn *dbh;
- my_ulonglong numrows;
- const char *newparam, *newval;
- struct ast_str *sql = ast_str_thread_get(&sql_buf, 100), *buf = ast_str_thread_get(&scratch_buf, 100);
- struct tables *table;
- struct columns *column = NULL;
- if (!(dbh = find_database(database, 1))) {
- ast_log(LOG_WARNING, "MySQL RealTime: Invalid database specified: '%s' (check res_mysql.conf)\n", database);
- return -1;
- }
- if (!tablename) {
- ast_log(LOG_WARNING, "MySQL RealTime: No table specified.\n");
- release_database(dbh);
- return -1;
- }
- if (!(table = find_table(database, tablename))) {
- ast_log(LOG_ERROR, "Table '%s' does not exist!!\n", tablename);
- release_database(dbh);
- return -1;
- }
- if (!(column = find_column(table, keyfield))) {
- ast_log(LOG_ERROR, "MySQL RealTime: Updating on column '%s', but that column does not exist within the table '%s' (db '%s')!\n", keyfield, tablename, database);
- release_table(table);
- release_database(dbh);
- return -1;
- }
- /* Get the first parameter and first value in our list of passed paramater/value pairs */
- newparam = va_arg(ap, const char *);
- newval = va_arg(ap, const char *);
- if (!newparam || !newval) {
- ast_log(LOG_WARNING, "MySQL RealTime: Realtime update requires at least 1 parameter and 1 value to update.\n");
- release_table(table);
- release_database(dbh);
- return -1;
- }
- /* Check that the column exists in the table */
- if (!(column = find_column(table, newparam))) {
- ast_log(LOG_ERROR, "MySQL RealTime: Updating column '%s', but that column does not exist within the table '%s' (first pair MUST exist)!\n", newparam, tablename);
- release_table(table);
- release_database(dbh);
- return -1;
- }
- /* Must connect to the server before anything else, as the escape function requires the mysql handle. */
- if (!mysql_reconnect(dbh)) {
- release_table(table);
- release_database(dbh);
- return -1;
- }
- /* Create the first part of the query using the first parameter/value pairs we just extracted
- If there is only 1 set, then we have our query. Otherwise, loop thru the list and concat */
- ESCAPE_STRING(buf, newval);
- ast_str_set(&sql, 0, "UPDATE %s SET `%s` = '%s'", tablename, newparam, ast_str_buffer(buf));
- /* If the column length isn't long enough, give a chance to lengthen it. */
- if (strncmp(column->type, "char", 4) == 0 || strncmp(column->type, "varchar", 7) == 0) {
- internal_require(database, tablename, newparam, RQ_CHAR, ast_str_strlen(buf), SENTINEL);
- }
- while ((newparam = va_arg(ap, const char *))) {
- newval = va_arg(ap, const char *);
- /* If the column is not within the table, then skip it */
- if (!(column = find_column(table, newparam))) {
- ast_log(LOG_WARNING, "Attempted to update column '%s' in table '%s', but column does not exist!\n", newparam, tablename);
- continue;
- }
- ESCAPE_STRING(buf, newval);
- ast_str_append(&sql, 0, ", `%s` = '%s'", newparam, ast_str_buffer(buf));
- /* If the column length isn't long enough, give a chance to lengthen it. */
- if (strncmp(column->type, "char", 4) == 0 || strncmp(column->type, "varchar", 7) == 0) {
- internal_require(database, tablename, newparam, RQ_CHAR, ast_str_strlen(buf), SENTINEL);
- }
- }
- va_end(ap);
- ESCAPE_STRING(buf, lookup);
- ast_str_append(&sql, 0, " WHERE `%s` = '%s'", keyfield, ast_str_buffer(buf));
- ast_debug(1, "MySQL RealTime: Update SQL: %s\n", ast_str_buffer(sql));
- /* Execution. */
- if (mysql_real_query(&dbh->handle, ast_str_buffer(sql), ast_str_strlen(sql))) {
- ast_log(LOG_WARNING, "MySQL RealTime: Failed to update database: %s\n", mysql_error(&dbh->handle));
- release_table(table);
- release_database(dbh);
- return -1;
- }
- numrows = mysql_affected_rows(&dbh->handle);
- release_table(table);
- release_database(dbh);
- ast_debug(1, "MySQL RealTime: Updated %llu rows on table: %s\n", numrows, tablename);
- /* From http://dev.mysql.com/doc/mysql/en/mysql-affected-rows.html
- * An integer greater than zero indicates the number of rows affected
- * Zero indicates that no records were updated
- * -1 indicates that the query returned an error (although, if the query failed, it should have been caught above.)
- */
- return (int)numrows;
- }
- static int update2_mysql(const char *database, const char *tablename, va_list ap)
- {
- struct mysql_conn *dbh;
- my_ulonglong numrows;
- int first = 1;
- const char *newparam, *newval;
- struct ast_str *sql = ast_str_thread_get(&sql_buf, 100), *buf = ast_str_thread_get(&scratch_buf, 100);
- struct ast_str *where = ast_str_thread_get(&sql2_buf, 100);
- struct tables *table;
- struct columns *column = NULL;
- if (!tablename) {
- ast_log(LOG_WARNING, "MySQL RealTime: No table specified.\n");
- return -1;
- }
- if (!(dbh = find_database(database, 1))) {
- ast_log(LOG_ERROR, "Invalid database specified: %s\n", database);
- return -1;
- }
- if (!(table = find_table(database, tablename))) {
- ast_log(LOG_ERROR, "Table '%s' does not exist!!\n", tablename);
- release_database(dbh);
- return -1;
- }
- if (!sql || !buf || !where) {
- release_database(dbh);
- release_table(table);
- return -1;
- }
- ast_str_set(&sql, 0, "UPDATE %s SET", tablename);
- ast_str_set(&where, 0, "WHERE");
- /* Must connect to the server before anything else, as the escape function requires the mysql handle. */
- if (!mysql_reconnect(dbh)) {
- release_table(table);
- release_database(dbh);
- return -1;
- }
- while ((newparam = va_arg(ap, const char *))) {
- if (!(column = find_column(table, newparam))) {
- ast_log(LOG_ERROR, "Updating on column '%s', but that column does not exist within the table '%s'!\n", newparam, tablename);
- release_table(table);
- release_database(dbh);
- return -1;
- }
- if (!(newval = va_arg(ap, const char *))) {
- ast_log(LOG_ERROR, "Invalid arguments: no value specified for column '%s' on '%s@%s'\n", newparam, tablename, database);
- release_table(table);
- release_database(dbh);
- return -1;
- }
- ESCAPE_STRING(buf, newval);
- ast_str_append(&where, 0, "%s `%s` = '%s'", first ? "" : " AND", newparam, ast_str_buffer(buf));
- first = 0;
- /* If the column length isn't long enough, give a chance to lengthen it. */
- if (strncmp(column->type, "char", 4) == 0 || strncmp(column->type, "varchar", 7) == 0) {
- internal_require(database, tablename, newparam, RQ_CHAR, ast_str_strlen(buf), SENTINEL);
- }
- }
- first = 1;
- while ((newparam = va_arg(ap, const char *))) {
- if (!(newval = va_arg(ap, const char *))) {
- ast_log(LOG_ERROR, "Invalid arguments: no value specified for column '%s' on '%s@%s'\n", newparam, tablename, database);
- release_table(table);
- release_database(dbh);
- return -1;
- }
- /* If the column is not within the table, then skip it */
- if (!(column = find_column(table, newparam))) {
- ast_log(LOG_WARNING, "Attempted to update column '%s' in table '%s', but column does not exist!\n", newparam, tablename);
- continue;
- }
- ESCAPE_STRING(buf, newval);
- ast_str_append(&sql, 0, "%s `%s` = '%s'", first ? "" : ",", newparam, ast_str_buffer(buf));
- first = 0;
- /* If the column length isn't long enough, give a chance to lengthen it. */
- if (strncmp(column->type, "char", 4) == 0 || strncmp(column->type, "varchar", 7) == 0) {
- internal_require(database, tablename, newparam, RQ_CHAR, ast_str_strlen(buf), SENTINEL);
- }
- }
- va_end(ap);
- release_table(table);
- ast_str_append(&sql, 0, " %s", ast_str_buffer(where));
- ast_debug(1, "MySQL RealTime: Update SQL: %s\n", ast_str_buffer(sql));
- /* Execution. */
- if (mysql_real_query(&dbh->handle, ast_str_buffer(sql), ast_str_strlen(sql))) {
- ast_log(LOG_WARNING, "MySQL RealTime: Failed to update database: %s\n", mysql_error(&dbh->handle));
- release_table(table);
- release_database(dbh);
- return -1;
- }
- numrows = mysql_affected_rows(&dbh->handle);
- release_database(dbh);
- ast_debug(1, "MySQL RealTime: Updated %llu rows on table: %s\n", numrows, tablename);
- /* From http://dev.mysql.com/doc/mysql/en/mysql-affected-rows.html
- * An integer greater than zero indicates the number of rows affected
- * Zero indicates that no records were updated
- * -1 indicates that the query returned an error (although, if the query failed, it should have been caught above.)
- */
- return (int)numrows;
- }
-
- static int store_mysql(const char *database, const char *table, va_list ap)
- {
- struct mysql_conn *dbh;
- my_ulonglong insertid;
- struct ast_str *sql = ast_str_thread_get(&sql_buf, 16);
- struct ast_str *sql2 = ast_str_thread_get(&sql2_buf, 16);
- struct ast_str *buf = ast_str_thread_get(&scratch_buf, 16);
- const char *newparam, *newval;
- if (!(dbh = find_database(database, 1))) {
- ast_log(LOG_WARNING, "MySQL RealTime: Invalid database specified: '%s' (check res_mysql.conf)\n", database);
- return -1;
- }
- if (!table) {
- ast_log(LOG_WARNING, "MySQL RealTime: No table specified.\n");
- release_database(dbh);
- return -1;
- }
- /* Get the first parameter and first value in our list of passed paramater/value pairs */
- newparam = va_arg(ap, const char *);
- newval = va_arg(ap, const char *);
- if (!newparam || !newval) {
- ast_log(LOG_WARNING, "MySQL RealTime: Realtime storage requires at least 1 parameter and 1 value to search on.\n");
- release_database(dbh);
- return -1;
- }
- /* Must connect to the server before anything else, as the escape function requires the mysql handle. */
- if (!mysql_reconnect(dbh)) {
- release_database(dbh);
- return -1;
- }
- /* Create the first part of the query using the first parameter/value pairs we just extracted
- If there is only 1 set, then we have our query. Otherwise, loop thru the list and concat */
- ESCAPE_STRING(buf, newval);
- ast_str_set(&sql, 0, "INSERT INTO %s (`%s`", table, newparam);
- ast_str_set(&sql2, 0, ") VALUES ('%s'", ast_str_buffer(buf));
- internal_require(database, table, newparam, RQ_CHAR, ast_str_strlen(buf), SENTINEL);
- while ((newparam = va_arg(ap, const char *))) {
- if ((newval = va_arg(ap, const char *))) {
- ESCAPE_STRING(buf, newval);
- } else {
- ast_str_reset(buf);
- }
- if (internal_require(database, table, newparam, RQ_CHAR, ast_str_strlen(buf), SENTINEL) == 0) {
- ast_str_append(&sql, 0, ", `%s`", newparam);
- ast_str_append(&sql2, 0, ", '%s'", ast_str_buffer(buf));
- }
- }
- va_end(ap);
- ast_str_append(&sql, 0, "%s)", ast_str_buffer(sql2));
- ast_debug(1,"MySQL RealTime: Insert SQL: %s\n", ast_str_buffer(sql));
- /* Execution. */
- if (mysql_real_query(&dbh->handle, ast_str_buffer(sql), ast_str_strlen(sql))) {
- ast_log(LOG_WARNING, "MySQL RealTime: Failed to insert into database: %s\n", mysql_error(&dbh->handle));
- release_database(dbh);
- return -1;
- }
- /*!\note The return value is non-portable and may change in future versions. */
- insertid = mysql_insert_id(&dbh->handle);
- release_database(dbh);
- ast_debug(1, "MySQL RealTime: row inserted on table: %s, id: %llu\n", table, insertid);
- /* From http://dev.mysql.com/doc/mysql/en/mysql-affected-rows.html
- * An integer greater than zero indicates the number of rows affected
- * Zero indicates that no records were updated
- * -1 indicates that the query returned an error (although, if the query failed, it should have been caught above.)
- */
- return (int)insertid;
- }
- static int destroy_mysql(const char *database, const char *table, const char *keyfield, const char *lookup, va_list ap)
- {
- struct mysql_conn *dbh;
- my_ulonglong numrows;
- struct ast_str *sql = ast_str_thread_get(&sql_buf, 16);
- struct ast_str *buf = ast_str_thread_get(&scratch_buf, 16);
- const char *newparam, *newval;
- if (!(dbh = find_database(database, 1))) {
- ast_log(LOG_WARNING, "MySQL RealTime: Invalid database specified: '%s' (check res_mysql.conf)\n", database);
- return -1;
- }
- if (!table) {
- ast_log(LOG_WARNING, "MySQL RealTime: No table specified.\n");
- release_database(dbh);
- return -1;
- }
- /* Get the first parameter and first value in our list of passed paramater/value pairs */
- /* newparam = va_arg(ap, const char *);
- newval = va_arg(ap, const char *);*/
- if (ast_strlen_zero(keyfield) || ast_strlen_zero(lookup)) {
- ast_log(LOG_WARNING, "MySQL RealTime: Realtime destroying requires at least 1 parameter and 1 value to search on.\n");
- release_database(dbh);
- return -1;
- }
- /* Must connect to the server before anything else, as the escape function requires the mysql handle. */
- if (!mysql_reconnect(dbh)) {
- release_database(dbh);
- return -1;
- }
- /* Create the first part of the query using the first parameter/value pairs we just extracted
- If there is only 1 set, then we have our query. Otherwise, loop thru the list and concat */
- ESCAPE_STRING(buf, lookup);
- ast_str_set(&sql, 0, "DELETE FROM %s WHERE `%s` = '%s'", table, keyfield, ast_str_buffer(buf));
- while ((newparam = va_arg(ap, const char *))) {
- newval = va_arg(ap, const char *);
- ESCAPE_STRING(buf, newval);
- ast_str_append(&sql, 0, " AND `%s` = '%s'", newparam, ast_str_buffer(buf));
- }
- va_end(ap);
- ast_debug(1, "MySQL RealTime: Delete SQL: %s\n", ast_str_buffer(sql));
- /* Execution. */
- if (mysql_real_query(&dbh->handle, ast_str_buffer(sql), ast_str_strlen(sql))) {
- ast_log(LOG_WARNING, "MySQL RealTime: Failed to delete from database: %s\n", mysql_error(&dbh->handle));
- release_database(dbh);
- return -1;
- }
- numrows = mysql_affected_rows(&dbh->handle);
- release_database(dbh);
- ast_debug(1, "MySQL RealTime: Deleted %llu rows on table: %s\n", numrows, table);
- /* From http://dev.mysql.com/doc/mysql/en/mysql-affected-rows.html
- * An integer greater than zero indicates the number of rows affected
- * Zero indicates that no records were updated
- * -1 indicates that the query returned an error (although, if the query failed, it should have been caught above.)
- */
- return (int)numrows;
- }
-
- static struct ast_config *config_mysql(const char *database, const char *table, const char *file, struct ast_config *cfg, struct ast_flags config_flags, const char *unused, const char *who_asked)
- {
- struct mysql_conn *dbh;
- MYSQL_RES *result;
- MYSQL_ROW row;
- my_ulonglong num_rows;
- struct ast_variable *new_v;
- struct ast_category *cur_cat = NULL;
- struct ast_str *sql = ast_str_thread_get(&sql_buf, 200);
- char last[80] = "";
- int last_cat_metric = 0;
- ast_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
- if (!file || !strcmp(file, RES_CONFIG_MYSQL_CONF)) {
- ast_log(LOG_WARNING, "MySQL RealTime: Cannot configure myself.\n");
- return NULL;
- }
- if (!(dbh = find_database(database, 0))) {
- ast_log(LOG_WARNING, "MySQL RealTime: Invalid database specified: '%s' (check res_mysql.conf)\n", database);
- return NULL;
- }
- ast_str_set(&sql, 0, "SELECT category, var_name, var_val, cat_metric FROM %s WHERE filename='%s' and commented=0 ORDER BY filename, cat_metric desc, var_metric asc, category, var_name, var_val, id", table, file);
- ast_debug(1, "MySQL RealTime: Static SQL: %s\n", ast_str_buffer(sql));
- /* We now have our complete statement; Lets connect to the server and execute it. */
- if (!mysql_reconnect(dbh)) {
- return NULL;
- }
- if (mysql_real_query(&dbh->handle, ast_str_buffer(sql), ast_str_strlen(sql))) {
- ast_log(LOG_WARNING, "MySQL RealTime: Failed to query database. Check debug for more info.\n");
- ast_debug(1, "MySQL RealTime: Query: %s\n", ast_str_buffer(sql));
- ast_debug(1, "MySQL RealTime: Query Failed because: %s\n", mysql_error(&dbh->handle));
- release_database(dbh);
- return NULL;
- }
- if ((result = mysql_store_result(&dbh->handle))) {
- num_rows = mysql_num_rows(result);
- ast_debug(1, "MySQL RealTime: Found %llu rows.\n", num_rows);
- /* There might exist a better way to access the column names other than counting,
- * but I believe that would require another loop that we don't need. */
- while ((row = mysql_fetch_row(result))) {
- if (!strcmp(row[1], "#include")) {
- if (!ast_config_internal_load(row[2], cfg, config_flags, "", who_asked)) {
- mysql_free_result(result);
- release_database(dbh);
- return NULL;
- }
- continue;
- }
- if (strcmp(last, row[0]) || last_cat_metric != atoi(row[3])) {
- if (!(cur_cat = ast_category_new(row[0], "", -1))) {
- ast_log(LOG_WARNING, "Out of memory!\n");
- break;
- }
- strcpy(last, row[0]);
- last_cat_metric = atoi(row[3]);
- ast_category_append(cfg, cur_cat);
- }
- new_v = ast_variable_new(row[1], row[2], "");
- if (cur_cat)
- ast_variable_append(cur_cat, new_v);
- }
- } else {
- ast_log(LOG_WARNING, "MySQL RealTime: Could not find config '%s' in database.\n", file);
- }
- mysql_free_result(result);
- release_database(dbh);
- return cfg;
- }
- static int unload_mysql(const char *database, const char *tablename)
- {
- struct tables *cur;
- AST_LIST_LOCK(&mysql_tables);
- AST_LIST_TRAVERSE_SAFE_BEGIN(&mysql_tables, cur, list) {
- if (strcmp(cur->name, tablename) == 0) {
- AST_LIST_REMOVE_CURRENT(list);
- destroy_table(cur);
- break;
- }
- }
- AST_LIST_TRAVERSE_SAFE_END
- AST_LIST_UNLOCK(&mysql_tables);
- return cur ? 0 : -1;
- }
- static int modify_mysql(const char *database, const char *tablename, struct columns *column, require_type type, int len)
- {
- /*!\note Cannot use ANY of the same scratch space as is used in other functions, as this one is interspersed. */
- struct ast_str *sql = ast_str_thread_get(&modify_buf, 100), *escbuf = ast_str_thread_get(&modify2_buf, 100);
- struct ast_str *typestr = ast_str_thread_get(&modify3_buf, 30);
- int waschar = strncasecmp(column->type, "char", 4) == 0 ? 1 : 0;
- int wasvarchar = strncasecmp(column->type, "varchar", 7) == 0 ? 1 : 0;
- int res = 0;
- struct mysql_conn *dbh;
- if (!(dbh = find_database(database, 1))) {
- return -1;
- }
- do {
- if (type == RQ_CHAR || waschar || wasvarchar) {
- if (wasvarchar) {
- ast_str_set(&typestr, 0, "VARCHAR(%d)", len);
- } else {
- ast_str_set(&typestr, 0, "CHAR(%d)", len);
- }
- } else if (type == RQ_UINTEGER1) {
- ast_str_set(&typestr, 0, "tinyint(3) unsigned");
- } else if (type == RQ_INTEGER1) {
- ast_str_set(&typestr, 0, "tinyint(4)");
- } else if (type == RQ_UINTEGER2) {
- ast_str_set(&typestr, 0, "smallint(5) unsigned");
- } else if (type == RQ_INTEGER2) {
- ast_str_set(&typestr, 0, "smallint(6)");
- } else if (type == RQ_UINTEGER3) {
- ast_str_set(&typestr, 0, "mediumint(8) unsigned");
- } else if (type == RQ_INTEGER3) {
- ast_str_set(&typestr, 0, "mediumint(8)");
- } else if (type == RQ_UINTEGER4) {
- ast_str_set(&typestr, 0, "int(10) unsigned");
- } else if (type == RQ_INTEGER4) {
- ast_str_set(&typestr, 0, "int(11)");
- } else if (type == RQ_UINTEGER8) {
- ast_str_set(&typestr, 0, "bigint(19) unsigned");
- } else if (type == RQ_INTEGER8) {
- ast_str_set(&typestr, 0, "bigint(20)");
- } else if (type == RQ_DATETIME) {
- ast_str_set(&typestr, 0, "datetime");
- } else if (type == RQ_DATE) {
- ast_str_set(&typestr, 0, "date");
- } else if (type == RQ_FLOAT) {
- ast_str_set(&typestr, 0, "FLOAT(%d,2)", len);
- } else {
- ast_log(LOG_ERROR, "Unknown type (should NEVER happen)\n");
- res = -1;
- break;
- }
- ast_str_set(&sql, 0, "ALTER TABLE %s MODIFY `%s` %s", tablename, column->name, ast_str_buffer(typestr));
- if (!column->null) {
- ast_str_append(&sql, 0, " NOT NULL");
- }
- if (!ast_strlen_zero(column->dflt)) {
- ESCAPE_STRING(escbuf, column->dflt);
- ast_str_append(&sql, 0, " DEFAULT '%s'", ast_str_buffer(escbuf));
- }
- if (!mysql_reconnect(dbh)) {
- ast_log(LOG_ERROR, "Unable to add column: %s\n", ast_str_buffer(sql));
- res = -1;
- break;
- }
- /* Execution. */
- if (mysql_real_query(&dbh->handle, ast_str_buffer(sql), ast_str_strlen(sql))) {
- ast_log(LOG_WARNING, "MySQL RealTime: Failed to modify database: %s\n", mysql_error(&dbh->handle));
- ast_debug(1, "MySQL RealTime: Query: %s\n", ast_str_buffer(sql));
- res = -1;
- }
- } while (0);
- release_database(dbh);
- return res;
- }
- #define PICK_WHICH_ALTER_ACTION(stringtype) \
- if (table->database->requirements == RQ_WARN) { \
- ast_log(LOG_WARNING, "Realtime table %s@%s: column '%s' may not be large enough for " \
- "the required data length: %d (detected stringtype)\n", \
- tablename, database, column->name, size); \
- res = -1; \
- } else if (table->database->requirements == RQ_CREATECLOSE && modify_mysql(database, tablename, column, type, size) == 0) { \
- table_altered = 1; \
- } else if (table->database->requirements == RQ_CREATECHAR && modify_mysql(database, tablename, column, RQ_CHAR, size) == 0) { \
- table_altered = 1; \
- } else { \
- res = -1; \
- }
- static int require_mysql(const char *database, const char *tablename, va_list ap)
- {
- struct columns *column;
- struct tables *table = find_table(database, tablename);
- char *elm;
- int type, size, res = 0, table_altered = 0;
- if (!table) {
- ast_log(LOG_WARNING, "Table %s not found in database. This table should exist if you're using realtime.\n", tablename);
- return -1;
- }
- while ((elm = va_arg(ap, char *))) {
- type = va_arg(ap, require_type);
- size = va_arg(ap, int);
- AST_LIST_TRAVERSE(&table->columns, column, list) {
- if (strcmp(column->name, elm) == 0) {
- /* Char can hold anything, as long as it is large enough */
- if (strncmp(column->type, "char", 4) == 0 || strncmp(column->type, "varchar", 7) == 0) {
- if ((size > column->len) && column->len != -1) {
- if (table->database->requirements == RQ_WARN) {
- ast_log(LOG_WARNING, "Realtime table %s@%s: Column '%s' should be at least %d long, but is only %d long.\n", database, tablename, column->name, size, column->len);
- res = -1;
- } else if (modify_mysql(database, tablename, column, type, size) == 0) {
- table_altered = 1;
- } else {
- res = -1;
- }
- }
- } else if (strcasestr(column->type, "unsigned")) {
- if (!ast_rq_is_int(type)) {
- if (table->database->requirements == RQ_WARN) {
- ast_log(LOG_WARNING, "Realtime table %s@%s: column '%s' cannot be type '%s' (need %s)\n",
- database, tablename, column->name, column->type,
- type == RQ_CHAR ? "char" : type == RQ_FLOAT ? "float" :
- type == RQ_DATETIME ? "datetime" : type == RQ_DATE ? "date" : "a rather stiff drink");
- res = -1;
- } else if (table->database->requirements == RQ_CREATECLOSE && modify_mysql(database, tablename, column, type, size) == 0) {
- table_altered = 1;
- } else if (table->database->requirements == RQ_CREATECHAR && modify_mysql(database, tablename, column, RQ_CHAR, size) == 0) {
- table_altered = 1;
- } else {
- res = -1;
- }
- } else if (strncasecmp(column->type, "tinyint", 1) == 0) {
- if (type != RQ_UINTEGER1) {
- PICK_WHICH_ALTER_ACTION(unsigned tinyint)
- }
- } else if (strncasecmp(column->type, "smallint", 1) == 0) {
- if (type != RQ_UINTEGER1 && type != RQ_INTEGER1 && type != RQ_UINTEGER2) {
- PICK_WHICH_ALTER_ACTION(unsigned smallint)
- }
- } else if (strncasecmp(column->type, "mediumint", 1) == 0) {
- if (type != RQ_UINTEGER1 && type != RQ_INTEGER1 &&
- type != RQ_UINTEGER2 && type != RQ_INTEGER2 &&
- type != RQ_UINTEGER3) {
- PICK_WHICH_ALTER_ACTION(unsigned mediumint)
- }
- } else if (strncasecmp(column->type, "int", 1) == 0) {
- if (type != RQ_UINTEGER1 && type != RQ_INTEGER1 &&
- type != RQ_UINTEGER2 && type != RQ_INTEGER2 &&
- type != RQ_UINTEGER3 && type != RQ_INTEGER3 &&
- type != RQ_UINTEGER4) {
- PICK_WHICH_ALTER_ACTION(unsigned int)
- }
- } else if (strncasecmp(column->type, "bigint", 1) == 0) {
- if (type != RQ_UINTEGER1 && type != RQ_INTEGER1 &&
- type != RQ_UINTEGER2 && type != RQ_INTEGER2 &&
- type != RQ_UINTEGER3 && type != RQ_INTEGER3 &&
- type != RQ_UINTEGER4 && type != RQ_INTEGER4 &&
- type != RQ_UINTEGER8) {
- PICK_WHICH_ALTER_ACTION(unsigned bigint)
- }
- }
- } else if (strcasestr(column->type, "int")) {
- if (!ast_rq_is_int(type)) {
- if (table->database->requirements == RQ_WARN) {
- ast_log(LOG_WARNING, "Realtime table %s@%s: column '%s' cannot be type '%s' (need %s)\n",
- database, tablename, column->name, column->type,
- type == RQ_CHAR ? "char" : type == RQ_FLOAT ? "float" :
- type == RQ_DATETIME ? "datetime" : type == RQ_DATE ? "date" :
- "to get a life, rather than writing silly error messages");
- res = -1;
- } else if (table->database->requirements == RQ_CREATECLOSE && modify_mysql(database, tablename, column, type, size) == 0) {
- table_altered = 1;
- } else if (table->database->requirements == RQ_CREATECHAR && modify_mysql(database, tablename, column, RQ_CHAR, size) == 0) {
- table_altered = 1;
- } else {
- res = -1;
- }
- } else if (strncasecmp(column->type, "tinyint", 1) == 0) {
- if (type != RQ_INTEGER1) {
- PICK_WHICH_ALTER_ACTION(tinyint)
- }
- } else if (strncasecmp(column->type, "smallint", 1) == 0) {
- if (type != RQ_UINTEGER1 && type != RQ_INTEGER1 && type != RQ_INTEGER2) {
- PICK_WHICH_ALTER_ACTION(smallint)
- }
- } else if (strncasecmp(column->type, "mediumint", 1) == 0) {
- if (type != RQ_UINTEGER1 && type != RQ_INTEGER1 &&
- type != RQ_UINTEGER2 && type != RQ_INTEGER2 &&
- type != RQ_INTEGER3) {
- PICK_WHICH_ALTER_ACTION(mediumint)
- }
- } else if (strncasecmp(column->type, "int", 1) == 0) {
- if (type != RQ_UINTEGER1 && type != RQ_INTEGER1 &&
- type != RQ_UINTEGER2 && type != RQ_INTEGER2 &&
- type != RQ_UINTEGER3 && type != RQ_INTEGER3 &&
- type != RQ_INTEGER4) {
- PICK_WHICH_ALTER_ACTION(int)
- }
- } else if (strncasecmp(column->type, "bigint", 1) == 0) {
- if (type != RQ_UINTEGER1 && type != RQ_INTEGER1 &&
- type != RQ_UINTEGER2 && type != RQ_INTEGER2 &&
- type != RQ_UINTEGER3 && type != RQ_INTEGER3 &&
- type != RQ_UINTEGER4 && type != RQ_INTEGER4 &&
- type != RQ_INTEGER8) {
- PICK_WHICH_ALTER_ACTION(bigint)
- }
- }
- } else if (strncmp(column->type, "float", 5) == 0 && !ast_rq_is_int(type) && type != RQ_FLOAT) {
- if (table->database->requirements == RQ_WARN) {
- ast_log(LOG_WARNING, "Realtime table %s@%s: Column %s cannot be a %s\n", tablename, database, column->name, column->type);
- res = -1;
- } else if (table->database->requirements == RQ_CREATECLOSE && modify_mysql(database, tablename, column, type, size) == 0) {
- table_altered = 1;
- } else if (table->database->requirements == RQ_CREATECHAR && modify_mysql(database, tablename, column, RQ_CHAR, size) == 0) {
- table_altered = 1;
- } else {
- res = -1;
- }
- } else if ((strncmp(column->type, "datetime", 8) == 0 || strncmp(column->type, "timestamp", 9) == 0) && type != RQ_DATETIME) {
- if (table->database->requirements == RQ_WARN) {
- ast_log(LOG_WARNING, "Realtime table %s@%s: Column %s cannot be a %s\n", tablename, database, column->name, column->type);
- res = -1;
- } else if (table->database->requirements == RQ_CREATECLOSE && modify_mysql(database, tablename, column, type, size) == 0) {
- table_altered = 1;
- } else if (table->database->requirements == RQ_CREATECHAR && modify_mysql(database, tablename, column, RQ_CHAR, size) == 0) {
- table_altered = 1;
- } else {
- res = -1;
- }
- } else if ((strncmp(column->type, "date", 4) == 0) && type != RQ_DATE) {
- if (table->database->requirements == RQ_WARN) {
- ast_log(LOG_WARNING, "Realtime table %s@%s: Column %s cannot be a %s\n", tablename, database, column->name, column->type);
- res = -1;
- } else if (table->database->requirements == RQ_CREATECLOSE && modify_mysql(database, tablename, column, type, size) == 0) {
- table_altered = 1;
- } else if (table->database->requirements == RQ_CREATECHAR && modify_mysql(database, tablename, column, RQ_CHAR, size) == 0) {
- table_altered = 1;
- } else {
- res = -1;
- }
- } else { /* Other, possibly unsupported types? */
- if (table->database->requirements == RQ_WARN) {
- ast_log(LOG_WARNING, "Possibly unsupported column type '%s' on column '%s'\n", column->type, column->name);
- res = -1;
- } else if (table->database->requirements == RQ_CREATECLOSE && modify_mysql(database, tablename, column, type, size) == 0) {
- table_altered = 1;
- } else if (table->database->requirements == RQ_CREATECHAR && modify_mysql(database, tablename, column, RQ_CHAR, size) == 0) {
- table_altered = 1;
- } else {
- }
- }
- break;
- }
- }
- if (!column) {
- if (table->database->requirements == RQ_WARN) {
- ast_log(LOG_WARNING, "Table %s requires a column '%s' of size '%d', but no such column exists.\n", tablename, elm, size);
- } else {
- struct ast_str *sql = ast_str_thread_get(&modify_buf, 100), *fieldtype = ast_str_thread_get(&modify3_buf, 16);
- if (table->database->requirements == RQ_CREATECHAR || type == RQ_CHAR) {
- ast_str_set(&fieldtype, 0, "CHAR(%d)", size);
- } else if (type == RQ_UINTEGER1 || type == RQ_UINTEGER2 || type == RQ_UINTEGER3 || type == RQ_UINTEGER4 || type == RQ_UINTEGER8) {
- if (type == RQ_UINTEGER1) {
- ast_str_set(&fieldtype, 0, "TINYINT(3) UNSIGNED");
- } else if (type == RQ_UINTEGER2) {
- ast_str_set(&fieldtype, 0, "SMALLINT(5) UNSIGNED");
- } else if (type == RQ_UINTEGER3) {
- ast_str_set(&fieldtype, 0, "MEDIUMINT(8) UNSIGNED");
- } else if (type == RQ_UINTEGER4) {
- ast_str_set(&fieldtype, 0, "INT(10) UNSIGNED");
- } else if (type == RQ_UINTEGER8) {
- ast_str_set(&fieldtype, 0, "BIGINT(20) UNSIGNED");
- } else {
- ast_log(LOG_WARNING, "Somebody should check this code for a rather large bug... it's about to squash Tokyo.\n");
- continue;
- }
- } else if (ast_rq_is_int(type)) {
- if (type == RQ_INTEGER1) {
- ast_str_set(&fieldtype, 0, "TINYINT(3)");
- } else if (type == RQ_INTEGER2) {
- ast_str_set(&fieldtype, 0, "SMALLINT(5)");
- } else if (type == RQ_INTEGER3) {
- ast_str_set(&fieldtype, 0, "MEDIUMINT(8)");
- } else if (type == RQ_INTEGER4) {
- ast_str_set(&fieldtype, 0, "INT(10)");
- } else if (type == RQ_INTEGER8) {
- ast_str_set(&fieldtype, 0, "BIGINT(20)");
- } else {
- ast_log(LOG_WARNING, "Somebody should check this code for a rather large bug... it's about to eat Cincinnati.\n");
- continue;
- }
- } else if (type == RQ_FLOAT) {
- ast_str_set(&fieldtype, 0, "FLOAT");
- } else if (type == RQ_DATE) {
- ast_str_set(&fieldtype, 0, "DATE");
- } else if (type == RQ_DATETIME) {
- ast_str_set(&fieldtype, 0, "DATETIME");
- } else {
- continue;
- }
- ast_str_set(&sql, 0, "ALTER TABLE %s ADD COLUMN %s %s", tablename, elm, ast_str_buffer(fieldtype));
- ast_mutex_lock(&table->database->lock);
- if (!mysql_reconnect(table->database)) {
- ast_mutex_unlock(&table->database->lock);
- ast_log(LOG_ERROR, "Unable to add column: %s\n", ast_str_buffer(sql));
- continue;
- }
- /* Execution. */
- if (mysql_real_query(&table->database->handle, ast_str_buffer(sql), ast_str_strlen(sql))) {
- ast_log(LOG_WARNING, "MySQL RealTime: Failed to query database. Check debug for more info.\n");
- ast_debug(1, "MySQL RealTime: Query: %s\n", ast_str_buffer(sql));
- ast_debug(1, "MySQL RealTime: Query Failed because: %s\n", mysql_error(&table->database->handle));
- } else {
- table_altered = 1;
- }
- }
- }
- }
- release_table(table);
- /* If we altered the table, we must refresh the cache */
- if (table_altered) {
- unload_mysql(database, tablename);
- release_table(find_table(database, tablename));
- }
- return res;
- }
- static struct ast_config_engine mysql_engine = {
- .name = "mysql",
- .load_func = config_mysql,
- .realtime_func = realtime_mysql,
- .realtime_multi_func = realtime_multi_mysql,
- .store_func = store_mysql,
- .destroy_func = destroy_mysql,
- .update_func = update_mysql,
- .update2_func = update2_mysql,
- .require_func = require_mysql,
- .unload_func = unload_mysql,
- };
- static int load_module(void)
- {
- parse_config(0);
- ast_config_engine_register(&mysql_engine);
- if (option_verbose > 1)
- ast_verbose(VERBOSE_PREFIX_2 "MySQL RealTime driver loaded.\n");
- ast_cli_register_multiple(cli_realtime_mysql_status, sizeof(cli_realtime_mysql_status) / sizeof(struct ast_cli_entry));
- return 0;
- }
- static int unload_module(void)
- {
- struct mysql_conn *cur;
- struct tables *table;
- ast_cli_unregister_multiple(cli_realtime_mysql_status, sizeof(cli_realtime_mysql_status) / sizeof(struct ast_cli_entry));
- ast_config_engine_deregister(&mysql_engine);
- if (option_verbose > 1)
- ast_verbose(VERBOSE_PREFIX_2 "MySQL RealTime unloaded.\n");
- ast_module_user_hangup_all();
- usleep(1);
- AST_RWLIST_WRLOCK(&databases);
- while ((cur = AST_RWLIST_REMOVE_HEAD(&databases, list))) {
- mysql_close(&cur->handle);
- ast_mutex_destroy(&cur->lock);
- ast_free(cur);
- }
- AST_RWLIST_UNLOCK(&databases);
- /* Destroy cached table info */
- AST_LIST_LOCK(&mysql_tables);
- while ((table = AST_LIST_REMOVE_HEAD(&mysql_tables, list))) {
- destroy_table(table);
- }
- AST_LIST_UNLOCK(&mysql_tables);
- return 0;
- }
- static int reload(void)
- {
- parse_config(1);
- if (option_verbose > 1) {
- ast_verb(2, "MySQL RealTime reloaded.\n");
- }
- return 0;
- }
- static int parse_config(int reload)
- {
- struct ast_config *config = NULL;
- struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
- const char *catg;
- struct mysql_conn *cur;
- if ((config = ast_config_load(RES_CONFIG_MYSQL_CONF, config_flags)) == CONFIG_STATUS_FILEMISSING) {
- /* Support old config file name */
- config = ast_config_load(RES_CONFIG_MYSQL_CONF_OLD, config_flags);
- }
- if (config == CONFIG_STATUS_FILEMISSING) {
- return 0;
- } else if (config == CONFIG_STATUS_FILEUNCHANGED) {
- return 0;
- } else if (config == CONFIG_STATUS_FILEINVALID) {
- ast_log(LOG_ERROR, "Not %sloading " RES_CONFIG_MYSQL_CONF "\n", reload ? "re" : "");
- }
- AST_RWLIST_WRLOCK(&databases);
- for (catg = ast_category_browse(config, NULL); catg; catg = ast_category_browse(config, catg)) {
- /* Does this category already exist? */
- AST_RWLIST_TRAVERSE(&databases, cur, list) {
- if (!strcmp(cur->unique_name, catg)) {
- break;
- }
- }
- if (!cur) {
- if (!(cur = ast_calloc(1, sizeof(*cur) + strlen(catg) + 1))) {
- ast_log(LOG_WARNING, "Could not allocate space for MySQL database '%s'\n", catg);
- continue;
- }
- strcpy(cur->unique_name, catg); /* SAFE */
- ast_mutex_init(&cur->lock);
- AST_RWLIST_INSERT_TAIL(&databases, cur, list);
- }
- load_mysql_config(config, catg, cur);
- }
- AST_RWLIST_UNLOCK(&databases);
- ast_config_destroy(config);
- return 0;
- }
- static int load_mysql_config(struct ast_config *config, const char *category, struct mysql_conn *conn)
- {
- const char *s;
- if (!(s = ast_variable_retrieve(config, category, "dbuser"))) {
- ast_log(LOG_WARNING, "MySQL RealTime: No database user found, using 'asterisk' as default.\n");
- s = "asterisk";
- }
- ast_copy_string(conn->user, s, sizeof(conn->user));
- if (!(s = ast_variable_retrieve(config, category, "dbpass"))) {
- ast_log(LOG_WARNING, "MySQL RealTime: No database password found, using 'asterisk' as default.\n");
- s = "asterisk";
- }
- ast_copy_string(conn->pass, s, sizeof(conn->pass));
- if (!(s = ast_variable_retrieve(config, category, "dbhost"))) {
- ast_log(LOG_WARNING, "MySQL RealTime: No database host found, using localhost via socket.\n");
- s = "";
- }
- ast_copy_string(conn->host, s, sizeof(conn->host));
- if (!(s = ast_variable_retrieve(config, category, "dbname"))) {
- ast_log(LOG_WARNING, "MySQL RealTime: No database name found, using 'asterisk' as default.\n");
- s = "asterisk";
- }
- ast_copy_string(conn->name, s, sizeof(conn->name));
- if (!(s = ast_variable_retrieve(config, category, "dbport"))) {
- ast_log(LOG_WARNING, "MySQL RealTime: No database port found, using 3306 as default.\n");
- conn->port = 3306;
- } else
- conn->port = atoi(s);
- if (!(s = ast_variable_retrieve(config, category, "dbsock"))) {
- if (ast_strlen_zero(conn->host)) {
- char *paths[3] = { "/tmp/mysql.sock", "/var/lib/mysql/mysql.sock", "/var/run/mysqld/mysqld.sock" };
- struct stat st;
- int i;
- for (i = 0; i < 3; i++) {
- if (!stat(paths[i], &st)) {
- ast_log(LOG_WARNING, "MySQL RealTime: No database socket found, using '%s' as default.\n", paths[i]);
- ast_copy_string(conn->sock, paths[i], sizeof(conn->sock));
- }
- }
- if (i == 3) {
- ast_log(LOG_WARNING, "MySQL RealTime: No database socket found (and unable to detect a suitable path).\n");
- return 0;
- }
- }
- } else
- ast_copy_string(conn->sock, s, sizeof(conn->sock));
- if ((s = ast_variable_retrieve(config, category, "dbcharset"))) {
- ast_copy_string(conn->charset, s, sizeof(conn->charset));
- }
- if (!(s = ast_variable_retrieve(config, category, "requirements"))) {
- ast_log(LOG_WARNING, "MySQL realtime: no requirements setting found, using 'warn' as default.\n");
- conn->requirements = RQ_WARN;
- } else if (!strcasecmp(s, "createclose")) {
- conn->requirements = RQ_CREATECLOSE;
- } else if (!strcasecmp(s, "createchar")) {
- conn->requirements = RQ_CREATECHAR;
- } else if (!strcasecmp(s, "warn")) {
- conn->requirements = RQ_WARN;
- } else {
- ast_log(LOG_WARNING, "MySQL realtime: unrecognized requirements setting '%s', using 'warn'\n", s);
- conn->requirements = RQ_WARN;
- }
- if (!ast_strlen_zero(conn->host)) {
- ast_debug(1, "MySQL RealTime host: %s\n", conn->host);
- ast_debug(1, "MySQL RealTime port: %i\n", conn->port);
- } else
- ast_debug(1, "MySQL RealTime socket: %s\n", conn->sock);
- ast_debug(1, "MySQL RealTime database name: %s\n", conn->name);
- ast_debug(1, "MySQL RealTime user: %s\n", conn->user);
- ast_debug(1, "MySQL RealTime password: %s\n", conn->pass);
- if(conn->charset)
- ast_debug(1, "MySQL RealTime charset: %s\n", conn->charset);
- return 1;
- }
- static int mysql_reconnect(struct mysql_conn *conn)
- {
- #ifdef MYSQL_OPT_RECONNECT
- my_bool trueval = 1;
- #endif
- /* mutex lock should have been locked before calling this function. */
- reconnect_tryagain:
- if ((!conn->connected) && (!ast_strlen_zero(conn->host) || conn->sock) && !ast_strlen_zero(conn->user) && !ast_strlen_zero(conn->name)) {
- if (!mysql_init(&conn->handle)) {
- ast_log(LOG_WARNING, "MySQL RealTime: Insufficient memory to allocate MySQL resource.\n");
- conn->connected = 0;
- return 0;
- }
- if(conn->charset && strlen(conn->charset) > 2){
- char set_names[255];
- char statement[512];
- snprintf(set_names, sizeof(set_names), "SET NAMES %s", conn->charset);
- mysql_real_escape_string(&conn->handle, statement, set_names, sizeof(set_names));
- mysql_options(&conn->handle, MYSQL_INIT_COMMAND, set_names);
- mysql_options(&conn->handle, MYSQL_SET_CHARSET_NAME, conn->charset);
- }
- if (mysql_real_connect(&conn->handle, conn->host, conn->user, conn->pass, conn->name, conn->port, conn->sock, 0)) {
- #ifdef MYSQL_OPT_RECONNECT
- /* The default is no longer to automatically reconnect on failure,
- * (as of 5.0.3) so we have to set that option here. */
- mysql_options(&conn->handle, MYSQL_OPT_RECONNECT, &trueval);
- #endif
- ast_debug(1, "MySQL RealTime: Successfully connected to database.\n");
- conn->connected = 1;
- conn->connect_time = time(NULL);
- return 1;
- } else {
- ast_log(LOG_ERROR, "MySQL RealTime: Failed to connect database server %s on %s (err %d). Check debug for more info.\n", conn->name, !ast_strlen_zero(conn->host) ? conn->host : conn->sock, mysql_errno(&conn->handle));
- ast_debug(1, "MySQL RealTime: Cannot Connect (%d): %s\n", mysql_errno(&conn->handle), mysql_error(&conn->handle));
- conn->connected = 0;
- conn->connect_time = 0;
- return 0;
- }
- } else {
- /* MySQL likes to return an error, even if it reconnects successfully.
- * So the postman pings twice. */
- if (mysql_ping(&conn->handle) != 0 && (usleep(1) + 2 > 0) && mysql_ping(&conn->handle) != 0) {
- conn->connected = 0;
- conn->connect_time = 0;
- ast_log(LOG_ERROR, "MySQL RealTime: Ping failed (%d). Trying an explicit reconnect.\n", mysql_errno(&conn->handle));
- ast_debug(1, "MySQL RealTime: Server Error (%d): %s\n", mysql_errno(&conn->handle), mysql_error(&conn->handle));
- goto reconnect_tryagain;
- }
- if (!conn->connected) {
- conn->connected = 1;
- conn->connect_time = time(NULL);
- }
- if (mysql_select_db(&conn->handle, conn->name) != 0) {
- ast_log(LOG_WARNING, "MySQL RealTime: Unable to select database: %s. Still Connected (%u) - %s.\n", conn->name, mysql_errno(&conn->handle), mysql_error(&conn->handle));
- return 0;
- }
- ast_debug(1, "MySQL RealTime: Connection okay.\n");
- return 1;
- }
- }
- static char *handle_cli_realtime_mysql_cache(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
- {
- struct tables *cur;
- int l, which;
- char *ret = NULL;
- switch (cmd) {
- case CLI_INIT:
- e->command = "realtime mysql cache";
- e->usage =
- "Usage: realtime mysql cache [<database> <table>]\n"
- " Shows table cache for the MySQL RealTime driver\n";
- return NULL;
- case CLI_GENERATE:
- if (a->argc < 4 || a->argc > 5) {
- return NULL;
- }
- l = strlen(a->word);
- which = 0;
- if (a->argc == 5) {
- AST_LIST_LOCK(&mysql_tables);
- AST_LIST_TRAVERSE(&mysql_tables, cur, list) {
- if (!strcasecmp(a->argv[3], cur->database->unique_name) && !strncasecmp(a->word, cur->name, l) && ++which > a->n) {
- ret = ast_strdup(cur->name);
- break;
- }
- }
- AST_LIST_UNLOCK(&mysql_tables);
- } else {
- struct mysql_conn *cur;
- AST_RWLIST_RDLOCK(&databases);
- AST_RWLIST_TRAVERSE(&databases, cur, list) {
- if (!strncasecmp(a->word, cur->unique_name, l) && ++which > a->n) {
- ret = ast_strdup(cur->unique_name);
- break;
- }
- }
- AST_RWLIST_UNLOCK(&databases);
- }
- return ret;
- }
- if (a->argc == 3) {
- /* List of tables */
- AST_LIST_LOCK(&mysql_tables);
- AST_LIST_TRAVERSE(&mysql_tables, cur, list) {
- ast_cli(a->fd, "%20.20s %s\n", cur->database->unique_name, cur->name);
- }
- AST_LIST_UNLOCK(&mysql_tables);
- } else if (a->argc == 4) {
- int found = 0;
- /* List of tables */
- AST_LIST_LOCK(&mysql_tables);
- AST_LIST_TRAVERSE(&mysql_tables, cur, list) {
- if (!strcasecmp(cur->database->unique_name, a->argv[3])) {
- ast_cli(a->fd, "%s\n", cur->name);
- found = 1;
- }
- }
- AST_LIST_UNLOCK(&mysql_tables);
- if (!found) {
- ast_cli(a->fd, "No tables cached within %s database\n", a->argv[3]);
- }
- } else if (a->argc == 5) {
- /* List of columns */
- if ((cur = find_table(a->argv[3], a->argv[4]))) {
- struct columns *col;
- ast_cli(a->fd, "Columns for Table Cache '%s':\n", a->argv[3]);
- ast_cli(a->fd, "%-20.20s %-20.20s %-3.3s\n", "Name", "Type", "Len");
- AST_LIST_TRAVERSE(&cur->columns, col, list) {
- ast_cli(a->fd, "%-20.20s %-20.20s %3d\n", col->name, col->type, col->len);
- }
- release_table(cur);
- } else {
- ast_cli(a->fd, "No such table '%s'\n", a->argv[3]);
- }
- }
- return CLI_SUCCESS;
- }
- static char *handle_cli_realtime_mysql_status(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
- {
- char status[256], status2[100] = "", type[20];
- char *ret = NULL;
- int ctime = 0, found = 0;
- struct mysql_conn *cur;
- int l = 0, which = 0;
- switch (cmd) {
- case CLI_INIT:
- e->command = "realtime mysql status";
- e->usage =
- "Usage: realtime mysql status [<database>]\n"
- " Shows connection information for the MySQL RealTime driver\n";
- return NULL;
- case CLI_GENERATE:
- if (a->argc == 4) {
- AST_RWLIST_RDLOCK(&databases);
- AST_RWLIST_TRAVERSE(&databases, cur, list) {
- if (!strncasecmp(a->word, cur->unique_name, l) && ++which > a->n) {
- ret = ast_strdup(cur->unique_name);
- break;
- }
- }
- AST_RWLIST_UNLOCK(&databases);
- }
- return ret;
- }
- if (a->argc != 3)
- return CLI_SHOWUSAGE;
- AST_RWLIST_RDLOCK(&databases);
- AST_RWLIST_TRAVERSE(&databases, cur, list) {
- if (a->argc == 3 || (a->argc == 4 && !strcasecmp(a->argv[3], cur->unique_name))) {
- found = 1;
- if (mysql_reconnect(cur)) {
- snprintf(type, sizeof(type), "connected to");
- ctime = time(NULL) - cur->connect_time;
- } else {
- snprintf(type, sizeof(type), "configured for");
- ctime = -1;
- }
- if (!ast_strlen_zero(cur->host)) {
- snprintf(status, sizeof(status), "%s %s %s@%s, port %d", cur->unique_name, type, cur->name, cur->host, cur->port);
- } else {
- snprintf(status, sizeof(status), "%s %s %s on socket file %s", cur->unique_name, type, cur->name, cur->sock);
- }
- if (!ast_strlen_zero(cur->user)) {
- snprintf(status2, sizeof(status2), " with username %s", cur->user);
- } else {
- status2[0] = '\0';
- }
- if (ctime > 31536000) {
- ast_cli(a->fd, "%s%s for %.1f years.\n", status, status2, (double)ctime / 31536000.0);
- } else if (ctime > 86400 * 30) {
- ast_cli(a->fd, "%s%s for %d days.\n", status, status2, ctime / 86400);
- } else if (ctime > 86400) {
- ast_cli(a->fd, "%s%s for %d days, %d hours.\n", status, status2, ctime / 86400, (ctime % 86400) / 3600);
- } else if (ctime > 3600) {
- ast_cli(a->fd, "%s%s for %d hours, %d minutes.\n", status, status2, ctime / 3600, (ctime % 3600) / 60);
- } else if (ctime > 60) {
- ast_cli(a->fd, "%s%s for %d minutes.\n", status, status2, ctime / 60);
- } else if (ctime > -1) {
- ast_cli(a->fd, "%s%s for %d seconds.\n", status, status2, ctime);
- } else {
- ast_cli(a->fd, "%s%s.\n", status, status2);
- }
- }
- }
- AST_RWLIST_UNLOCK(&databases);
- if (!found) {
- ast_cli(a->fd, "No connections configured.\n");
- }
- return CLI_SUCCESS;
- }
- AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "MySQL RealTime Configuration Driver",
- .load = load_module,
- .unload = unload_module,
- .reload = reload,
- .load_pri = AST_MODPRI_REALTIME_DRIVER,
- );
|