[asterisk-addons-commits] tilghman: trunk r638 - in /trunk: configs/ res/
SVN commits to the Asterisk addons project
asterisk-addons-commits at lists.digium.com
Fri Jun 6 10:35:16 CDT 2008
Author: tilghman
Date: Fri Jun 6 10:35:16 2008
New Revision: 638
URL: http://svn.digium.com/view/asterisk-addons?view=rev&rev=638
Log:
Add adaptive realtime capabilities to the mysql native driver
Modified:
trunk/configs/res_mysql.conf.sample
trunk/res/res_config_mysql.c
Modified: trunk/configs/res_mysql.conf.sample
URL: http://svn.digium.com/view/asterisk-addons/trunk/configs/res_mysql.conf.sample?view=diff&rev=638&r1=637&r2=638
==============================================================================
--- trunk/configs/res_mysql.conf.sample (original)
+++ trunk/configs/res_mysql.conf.sample Fri Jun 6 10:35:16 2008
@@ -6,6 +6,21 @@
; to the local host is assumed and dbsock is used instead of TCP/IP
; to connect to the server.
;
+; In addition to a [general] section, this file may also be configured
+; with separate [read] and [write] sections, which permits separation
+; of read-only queries from queries which alter the database. This
+; enables the use of master and slave replication servers for
+; scalability.
+;
+; The requirements parameter is available only in Asterisk 1.6.1 and
+; later and must be present in the [general] context. It specifies
+; the behavior when a column name is required by the system. The
+; default behavior is "warn" and simply sends a warning to the logger
+; that the column does not exist (or is of the wrong type or precision).
+; The other two possibilities are "createclose", which adds the column
+; with the right type and length, and "createchar", which adds the
+; column as a char type, with the appropriate length to accept the data.
+;
[general]
;dbhost = 127.0.0.1
;dbname = asterisk
@@ -13,3 +28,4 @@
;dbpass = mypass
;dbport = 3306
;dbsock = /tmp/mysql.sock
+;requirements=warn ; or createclose or createchar
Modified: trunk/res/res_config_mysql.c
URL: http://svn.digium.com/view/asterisk-addons/trunk/res/res_config_mysql.c?view=diff&rev=638&r1=637&r2=638
==============================================================================
--- trunk/res/res_config_mysql.c (original)
+++ trunk/res/res_config_mysql.c Fri Jun 6 10:35:16 2008
@@ -77,18 +77,130 @@
time_t connect_time;
} dbread, dbwrite;
-static int parse_config(void);
+struct columns {
+ char *name;
+ char *type;
+ 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;
+ char name[0];
+};
+
+static AST_LIST_HEAD_STATIC(mysql_tables, tables);
+
+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);
+
+enum { RQ_WARN, RQ_CREATECLOSE, RQ_CREATECHAR } requirements;
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"),
};
-/*
- { "realtime", "mysql", "status", NULL }, realtime_mysql_status,
- "Shows connection information for the MySQL RealTime driver", cli_realtime_mysql_status_usage, NULL };
-*/
+
+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 *tablename)
+{
+ struct columns *column;
+ struct tables *table;
+ struct ast_str *sql = ast_str_create(30);
+ char *fname, *ftype, *flen;
+ MYSQL_RES *result;
+ MYSQL_ROW row;
+
+ 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);
+ return table;
+ }
+ }
+
+ /* Not found, scan the table */
+ ast_str_set(&sql, 0, "DESC %s", tablename);
+
+ ast_mutex_lock(&dbread.lock);
+ if (!mysql_reconnect(&dbread)) {
+ ast_mutex_unlock(&dbread.lock);
+ AST_LIST_UNLOCK(&mysql_tables);
+ return NULL;
+ }
+
+ if (mysql_real_query(&dbread.handle, sql->str, sql->used)) {
+ ast_log(LOG_ERROR, "Failed to query database columns: %s\n", mysql_error(&dbread.handle));
+ AST_LIST_UNLOCK(&mysql_tables);
+ ast_mutex_unlock(&dbread.lock);
+ 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");
+ AST_LIST_UNLOCK(&mysql_tables);
+ ast_mutex_unlock(&dbread.lock);
+ return NULL;
+ }
+ strcpy(table->name, tablename); /* SAFE */
+ ast_mutex_init(&table->lock);
+ AST_LIST_HEAD_INIT_NOLOCK(&table->columns);
+
+ if ((result = mysql_store_result(&dbread.handle))) {
+ while ((row = mysql_fetch_row(result))) {
+ fname = row[0];
+ ftype = row[1];
+ ast_verb(4, "Found column '%s' of type '%s'\n", fname, ftype);
+
+ if (!(column = ast_calloc(1, sizeof(*column) + strlen(fname) + strlen(ftype) + 2))) {
+ ast_log(LOG_ERROR, "Unable to allocate column element for %s, %s\n", tablename, fname);
+ destroy_table(table);
+ AST_LIST_UNLOCK(&mysql_tables);
+ ast_mutex_unlock(&dbread.lock);
+ return NULL;
+ }
+
+ if ((flen = strchr(ftype, '('))) {
+ *flen++ = '\0';
+ sscanf(flen, "%d", &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;
+ strcpy(column->name, fname);
+ strcpy(column->type, ftype);
+ AST_LIST_INSERT_TAIL(&table->columns, column, list);
+ }
+ mysql_free_result(result);
+ }
+
+ ast_mutex_unlock(&dbread.lock);
+ AST_LIST_INSERT_TAIL(&mysql_tables, table, list);
+ ast_mutex_lock(&table->lock);
+ AST_LIST_UNLOCK(&mysql_tables);
+ return table;
+}
+
static struct ast_variable *realtime_mysql(const char *database, const char *table, va_list ap)
{
MYSQL_RES *result;
@@ -317,16 +429,41 @@
return cfg;
}
-static int update_mysql(const char *database, const char *table, const char *keyfield, const char *lookup, va_list ap)
+static int update_mysql(const char *database, const char *tablename, const char *keyfield, const char *lookup, va_list ap)
{
my_ulonglong numrows;
- char sql[512];
- char buf[511]; /* Keep this size uneven as it is 2n+1. */
int valsz;
const char *newparam, *newval;
-
- if (!table) {
+ struct ast_str *sql = ast_str_create(100), *buf = ast_str_create(513);
+ struct tables *table;
+ struct columns *column = NULL;
+
+ if (!tablename) {
ast_log(LOG_WARNING, "MySQL RealTime: No table specified.\n");
+ ast_free(sql);
+ ast_free(buf);
+ return -1;
+ }
+
+ if (!(table = find_table(tablename))) {
+ ast_log(LOG_ERROR, "Table '%s' does not exist!!\n", tablename);
+ ast_free(sql);
+ ast_free(buf);
+ return -1;
+ }
+
+ /* Check that the column exists in the table */
+ AST_LIST_TRAVERSE(&table->columns, column, list) {
+ if (strcmp(column->name, keyfield) == 0) {
+ break;
+ }
+ }
+
+ if (!column) {
+ ast_log(LOG_ERROR, "PostgreSQL RealTime: Updating on column '%s', but that column does not exist within the table '%s'!\n", keyfield, tablename);
+ ast_mutex_unlock(&table->lock);
+ ast_free(sql);
+ ast_free(buf);
return -1;
}
@@ -335,51 +472,96 @@
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");
- return -1;
+ ast_mutex_unlock(&table->lock);
+ ast_free(sql);
+ ast_free(buf);
+ return -1;
+ }
+
+ /* Check that the column exists in the table */
+ AST_LIST_TRAVERSE(&table->columns, column, list) {
+ if (strcmp(column->name, newparam) == 0) {
+ break;
+ }
+ }
+
+ if (!column) {
+ ast_log(LOG_ERROR, "PostgreSQL RealTime: Updating on column '%s', but that column does not exist within the table '%s'!\n", newparam, tablename);
+ ast_mutex_unlock(&table->lock);
+ ast_free(sql);
+ ast_free(buf);
+ return -1;
}
/* Must connect to the server before anything else, as the escape function requires the mysql handle. */
ast_mutex_lock(&dbwrite.lock);
if (!mysql_reconnect(&dbwrite)) {
+ ast_mutex_unlock(&table->lock);
ast_mutex_unlock(&dbwrite.lock);
+ ast_free(sql);
+ ast_free(buf);
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 */
- if ((valsz = strlen (newval)) * 2 + 1 > sizeof(buf))
- valsz = (sizeof(buf) - 1) / 2;
- mysql_real_escape_string(&dbwrite.handle, buf, newval, valsz);
- snprintf(sql, sizeof(sql), "UPDATE %s SET %s = '%s'", table, newparam, buf);
- while((newparam = va_arg(ap, const char *))) {
+ if ((valsz = strlen(newval)) * 2 + 1 > buf->len) {
+ ast_str_make_space(&buf, valsz * 2 + 1);
+ }
+ mysql_real_escape_string(&dbwrite.handle, buf->str, newval, valsz);
+ ast_str_set(&sql, 0, "UPDATE %s SET %s = '%s'", tablename, newparam, buf->str);
+
+ while ((newparam = va_arg(ap, const char *))) {
newval = va_arg(ap, const char *);
- if ((valsz = strlen (newval)) * 2 + 1 > sizeof(buf))
- valsz = (sizeof(buf) - 1) / 2;
- mysql_real_escape_string(&dbwrite.handle, buf, newval, valsz);
- snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), ", %s = '%s'", newparam, buf);
+
+ /* If the column is not within the table, then skip it */
+ AST_LIST_TRAVERSE(&table->columns, column, list) {
+ if (strcmp(column->name, newparam) == 0) {
+ break;
+ }
+ }
+
+ if (!column) {
+ ast_log(LOG_WARNING, "Attempted to update column '%s' in table '%s', but column does not exist!\n", newparam, tablename);
+ continue;
+ }
+
+ if ((valsz = strlen(newval)) * 2 + 1 > buf->len) {
+ ast_str_make_space(&buf, valsz * 2 + 1);
+ }
+ mysql_real_escape_string(&dbwrite.handle, buf->str, newval, valsz);
+ ast_str_append(&sql, 0, ", %s = '%s'", newparam, buf->str);
}
va_end(ap);
- if ((valsz = strlen (lookup)) * 2 + 1 > sizeof(buf))
- valsz = (sizeof(buf) - 1) / 2;
- mysql_real_escape_string(&dbwrite.handle, buf, lookup, valsz);
- snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " WHERE %s = '%s'", keyfield, buf);
-
- ast_debug(1,"MySQL RealTime: Update SQL: %s\n", sql);
+
+ if ((valsz = strlen(lookup)) * 2 + 1 > buf->len) {
+ ast_str_make_space(&buf, valsz * 2 + 1);
+ }
+ mysql_real_escape_string(&dbwrite.handle, buf->str, lookup, valsz);
+ ast_str_append(&sql, 0, " WHERE %s = '%s'", keyfield, buf->str);
+
+ ast_debug(1, "MySQL RealTime: Update SQL: %s\n", sql->str);
/* Execution. */
- if (mysql_real_query(&dbwrite.handle, sql, strlen(sql))) {
+ if (mysql_real_query(&dbwrite.handle, sql->str, sql->used)) {
ast_log(LOG_WARNING, "MySQL RealTime: Failed to query database. Check debug for more info.\n");
- ast_debug(1, "MySQL RealTime: Query: %s\n", sql);
+ ast_debug(1, "MySQL RealTime: Query: %s\n", sql->str);
ast_debug(1, "MySQL RealTime: Query Failed because: %s\n", mysql_error(&dbwrite.handle));
ast_mutex_unlock(&dbwrite.lock);
- return -1;
- }
+ ast_mutex_unlock(&table->lock);
+ ast_free(sql);
+ ast_free(buf);
+ return -1;
+ }
+
+ ast_free(sql);
+ ast_free(buf);
numrows = mysql_affected_rows(&dbwrite.handle);
ast_mutex_unlock(&dbwrite.lock);
- ast_debug(1,"MySQL RealTime: Updated %llu rows on table: %s\n", numrows, table);
+ 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
@@ -578,7 +760,7 @@
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. */
+ * but I believe that would require another loop that we don't need. */
while ((row = mysql_fetch_row(result))) {
if (!strcmp(row[1], "#include")) {
@@ -615,6 +797,129 @@
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 require_mysql(const char *database, const char *tablename, va_list ap)
+{
+ struct columns *column;
+ struct tables *table = find_table(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 || strcmp(column->type, "bpchar") == 0)) {
+ if ((size > column->len) && column->len != -1) {
+ ast_log(LOG_WARNING, "Column '%s' should be at least %d long, but is only %d long.\n", column->name, size, column->len);
+ res = -1;
+ }
+ } else if (strcasestr(column->type, "int")) {
+ int typesize = (
+ !strncasecmp(column->type, "tiny", 1) ? 3 :
+ !strncasecmp(column->type, "small", 1) ? 5 :
+ !strncasecmp(column->type, "medium", 1) ? 8 :
+ !strncasecmp(column->type, "int", 1) ? 11 :
+ 23); /* bigint */
+ /* Integers can hold only other integers */
+ if (type == RQ_INTEGER && (typesize < size)) {
+ ast_log(LOG_WARNING, "Column '%s' may not be large enough for the required data length: %d (detected only %d)\n", column->name, size, typesize);
+ res = -1;
+ } else if (type != RQ_INTEGER) {
+ ast_log(LOG_WARNING, "Column '%s' is of the incorrect type: (need %s(%d) but saw %s)\n", column->name, type == RQ_CHAR ? "char" : "something else ", size, column->type);
+ res = -1;
+ }
+ } else if (strncmp(column->type, "float", 5) == 0 && type != RQ_INTEGER && type != RQ_FLOAT) {
+ ast_log(LOG_WARNING, "Column %s cannot be a %s\n", column->name, column->type);
+ res = -1;
+ } else { /* There are other types that no module implements yet */
+ ast_log(LOG_WARNING, "Possibly unsupported column type '%s' on column '%s'\n", column->type, column->name);
+ res = -1;
+ }
+ break;
+ }
+ }
+
+ if (!column) {
+ if (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_create(100), *fieldtype = ast_str_create(16);
+
+ if (requirements == RQ_CREATECHAR || type == RQ_CHAR) {
+ ast_str_set(&fieldtype, 0, "CHAR(%d)", size);
+ } else if (type == RQ_INTEGER) {
+ ast_str_set(&fieldtype, 0, "%sINT(%d)", size <= 3 ? "TINY" : size <= 5 ? "SMALL" : size <= 8 ? "MEDIUM" : size <= 11 ? "" : "BIG", size);
+ } 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 {
+ ast_free(sql);
+ ast_free(fieldtype);
+ continue;
+ }
+ ast_str_set(&sql, 0, "ALTER TABLE %s ADD COLUMN %s %s", tablename, elm, fieldtype->str);
+
+ ast_mutex_lock(&dbwrite.lock);
+ if (!mysql_reconnect(&dbwrite)) {
+ ast_mutex_unlock(&dbwrite.lock);
+ ast_log(LOG_ERROR, "Unable to add column: %s\n", sql->str);
+ ast_free(sql);
+ ast_free(fieldtype);
+ continue;
+ }
+
+ /* Execution. */
+ if (mysql_real_query(&dbwrite.handle, sql->str, sql->used)) {
+ ast_log(LOG_WARNING, "MySQL RealTime: Failed to query database. Check debug for more info.\n");
+ ast_debug(1, "MySQL RealTime: Query: %s\n", sql->str);
+ ast_debug(1, "MySQL RealTime: Query Failed because: %s\n", mysql_error(&dbwrite.handle));
+ ast_mutex_unlock(&dbwrite.lock);
+ } else {
+ table_altered = 1;
+ }
+
+ ast_free(sql);
+ ast_free(fieldtype);
+ }
+ }
+ }
+ ast_mutex_unlock(&table->lock);
+
+ /* If we altered the table, we must refresh the cache */
+ if (table_altered) {
+ unload_mysql(database, tablename);
+ table = find_table(tablename);
+ ast_mutex_unlock(&table->lock);
+ }
+ return res;
+}
+
static struct ast_config_engine mysql_engine = {
.name = "mysql",
.load_func = config_mysql,
@@ -622,13 +927,15 @@
.realtime_multi_func = realtime_multi_mysql,
.store_func = store_mysql,
.destroy_func = destroy_mysql,
- .update_func = update_mysql
+ .update_func = update_mysql,
+ .require_func = require_mysql,
+ .unload_func = unload_mysql,
};
static int load_module(void)
{
int writeokay = 0;
- parse_config();
+ parse_config(0);
ast_mutex_init(&dbread.lock);
ast_mutex_init(&dbwrite.lock);
@@ -653,6 +960,8 @@
static int unload_module(void)
{
+ 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)
@@ -667,6 +976,13 @@
mysql_close(&dbwrite.handle);
mysql_close(&dbread.handle);
+ /* 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;
}
@@ -678,7 +994,7 @@
ast_mutex_lock(&dbread.lock);
mysql_close(&dbwrite.handle);
mysql_close(&dbread.handle);
- parse_config();
+ parse_config(1);
if (!mysql_reconnect(&dbwrite))
ast_log(LOG_WARNING, "MySQL RealTime: Cannot connect write handle: %s\n", mysql_error(&dbwrite.handle));
@@ -720,16 +1036,18 @@
return 0;
}
-static int parse_config(void)
+static int parse_config(int reload)
{
struct ast_config *config = NULL;
- struct ast_flags config_flags = { 0 };
- const char *catg = "write";
+ struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
+ const char *catg = "write", *s;
int haswriteconfig = 0;
if (!(config = ast_config_load(RES_CONFIG_MYSQL_CONF, config_flags))) {
set_defaults(&dbwrite);
set_defaults(&dbread);
+ return 0;
+ } else if (config == CONFIG_STATUS_FILEUNCHANGED) {
return 0;
}
@@ -757,6 +1075,19 @@
} else
load_mysql_config(config, "read", &dbread);
+ if (!(s = ast_variable_retrieve(config, "general", "requirements"))) {
+ ast_log(LOG_WARNING, "MySQL realtime: no requirements setting found, using 'warn' as default.\n");
+ requirements = RQ_WARN;
+ } else if (!strcasecmp(s, "createclose")) {
+ requirements = RQ_CREATECLOSE;
+ } else if (!strcasecmp(s, "createchar")) {
+ requirements = RQ_CREATECHAR;
+ } else if (!strcasecmp(s, "warn")) {
+ requirements = RQ_WARN;
+ } else {
+ ast_log(LOG_WARNING, "MySQL realtime: unrecognized requirements setting '%s', using 'warn'\n", s);
+ requirements = RQ_WARN;
+ }
ast_config_destroy(config);
return 0;
@@ -870,6 +1201,60 @@
}
}
+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 [<table>]\n"
+ " Shows table cache for the MySQL RealTime driver\n";
+ return NULL;
+ case CLI_GENERATE:
+ if (a->argc != 4) {
+ return NULL;
+ }
+ l = strlen(a->word);
+ which = 0;
+ AST_LIST_LOCK(&mysql_tables);
+ AST_LIST_TRAVERSE(&mysql_tables, cur, list) {
+ if (!strncasecmp(a->word, cur->name, l) && ++which > a->n) {
+ ret = ast_strdup(cur->name);
+ break;
+ }
+ }
+ AST_LIST_UNLOCK(&mysql_tables);
+ 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, "%s\n", cur->name);
+ }
+ AST_LIST_UNLOCK(&mysql_tables);
+ } else if (a->argc == 4) {
+ /* List of columns */
+ if ((cur = find_table(a->argv[3]))) {
+ 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);
+ }
+ ast_mutex_unlock(&cur->lock);
+ } else {
+ ast_cli(a->fd, "No such table '%s'\n", a->argv[3]);
+ }
+ }
+ return 0;
+}
+
static char *handle_cli_realtime_mysql_status(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
char status[256], status2[100] = "";
More information about the asterisk-addons-commits
mailing list