35 #include <mysql/mysql.h>
36 #include <mysql/errmsg.h>
49 #define RES_CONFIG_MYSQL_CONF "res_config_mysql.conf"
50 #define RES_CONFIG_MYSQL_CONF_OLD "res_mysql.conf"
54 #define ESCAPE_STRING(buf, var) \
56 struct ast_str *semi = ast_str_thread_get(&scratch2_buf, strlen(var) * 3 + 1); \
57 const char *chunk = var; \
58 ast_str_reset(semi); \
59 for (; *chunk; chunk++) { \
60 if (strchr(";^", *chunk)) { \
61 ast_str_append(&semi, 0, "^%02hhX", *chunk); \
63 ast_str_append(&semi, 0, "%c", *chunk); \
66 if (ast_str_strlen(semi) * 2 + 1 > ast_str_size(buf)) { \
67 ast_str_make_space(&(buf), ast_str_strlen(semi) * 2 + 1); \
69 mysql_real_escape_string(&dbh->handle, ast_str_buffer(buf), ast_str_buffer(semi), ast_str_strlen(semi)); \
81 enum requirements { RQ_WARN, RQ_CREATECLOSE, RQ_CREATECHAR };
87 char host[MAXHOSTNAMELEN];
96 enum requirements requirements;
120 static
int parse_config(
int reload);
121 static
int mysql_reconnect(struct mysql_conn *conn);
124 static
int load_mysql_config(struct
ast_config *config, const
char *category, struct mysql_conn *conn);
125 static
int require_mysql(const
char *database, const
char *tablename, va_list ap);
128 AST_CLI_DEFINE(handle_cli_realtime_mysql_status,
"Shows connection information for the MySQL RealTime driver"),
129 AST_CLI_DEFINE(handle_cli_realtime_mysql_cache,
"Shows cached tables within the MySQL realtime driver"),
132 static struct mysql_conn *find_database(
const char *database,
int for_write)
138 if ((ptr = strchr(database,
'/'))) {
144 strncpy(whichdb, database, ptr - database);
145 whichdb[ptr - database] =
'\0';
152 AST_RWLIST_TRAVERSE(&
databases, cur, list) {
153 if (!strcmp(cur->unique_name, whichdb)) {
154 ast_mutex_lock(&cur->lock);
162 #define release_database(a) ast_mutex_unlock(&(a)->lock)
164 static void destroy_table(
struct tables *table)
167 ast_mutex_lock(&table->lock);
171 ast_mutex_unlock(&table->lock);
172 ast_mutex_destroy(&table->lock);
176 static struct tables *find_table(
const char *database,
const char *tablename)
181 char *fname, *ftype, *flen, *fdflt, *fnull;
186 if (!(dbh = find_database(database, 1))) {
192 if (!strcasecmp(table->name, tablename)) {
193 ast_mutex_lock(&table->lock);
195 release_database(dbh);
203 if (!mysql_reconnect(dbh)) {
204 release_database(dbh);
210 ast_log(LOG_ERROR,
"Failed to query database '%s', table '%s' columns: %s\n", database, tablename, mysql_error(&dbh->handle));
211 release_database(dbh);
216 if (!(table =
ast_calloc(1,
sizeof(*table) + strlen(tablename) + 1))) {
217 ast_log(LOG_ERROR,
"Unable to allocate memory for new table structure\n");
218 release_database(dbh);
222 strcpy(table->name, tablename);
223 table->database = dbh;
224 ast_mutex_init(&table->lock);
227 if ((result = mysql_store_result(&dbh->handle))) {
228 while ((row = mysql_fetch_row(result))) {
233 ast_verb(4,
"Found column '%s' of type '%s'\n", fname, ftype);
239 if (!(column =
ast_calloc(1,
sizeof(*column) + strlen(fname) + strlen(ftype) + strlen(fdflt) + 3))) {
240 ast_log(LOG_ERROR,
"Unable to allocate column element %s for %s\n", fname, tablename);
241 destroy_table(table);
242 release_database(dbh);
247 if ((flen = strchr(ftype,
'('))) {
248 sscanf(flen,
"(%30d)", &column->len);
254 column->name = (
char *)column +
sizeof(*column);
255 column->type = (
char *)column +
sizeof(*column) + strlen(fname) + 1;
256 column->dflt = (
char *)column +
sizeof(*column) + strlen(fname) + 1 + strlen(ftype) + 1;
257 strcpy(column->name, fname);
258 strcpy(column->type, ftype);
259 strcpy(column->dflt, fdflt);
260 column->null = (strcmp(fnull,
"YES") == 0 ? 1 : 0);
263 mysql_free_result(result);
267 ast_mutex_lock(&table->lock);
269 release_database(dbh);
273 static void release_table(
struct tables *table)
276 ast_mutex_unlock(&table->lock);
280 static struct columns *find_column(
struct tables *table,
const char *colname)
285 if (strcmp(column->name, colname) == 0) {
293 static char *decode_chunk(
char *chunk)
296 for (; *chunk; chunk++) {
297 if (*chunk ==
'^' && strchr(
"0123456789ABCDEFabcdef", chunk[1]) && strchr(
"0123456789ABCDEFabcdef", chunk[2])) {
298 sscanf(chunk + 1,
"%02hhX", chunk);
299 memmove(chunk + 1, chunk + 3, strlen(chunk + 3) + 1);
305 #define IS_SQL_LIKE_CLAUSE(x) ((x) && ast_ends_with(x, " LIKE"))
308 static char *ESCAPE_CLAUSE =
" ESCAPE '\\\\'";
310 static struct ast_variable *realtime_mysql(
const char *database,
const char *table,
const struct ast_variable *rt_fields)
326 if (!(dbh = find_database(database, 0))) {
327 ast_log(LOG_WARNING,
"MySQL RealTime: Invalid database specified: %s (check res_mysql.conf)\n", database);
332 ast_log(LOG_WARNING,
"MySQL RealTime: No table specified.\n");
333 release_database(dbh);
339 ast_log(LOG_WARNING,
"MySQL RealTime: Realtime retrieval requires at least 1 parameter and 1 value to search on.\n");
340 release_database(dbh);
345 if (!mysql_reconnect(dbh)) {
346 release_database(dbh);
353 if (!strchr(field->
name,
' ')) {
357 if (IS_SQL_LIKE_CLAUSE(field->
name)) {
358 escape = ESCAPE_CLAUSE;
362 ESCAPE_STRING(buf, field->
value);
364 while ((field = field->
next)) {
366 if (!strchr(field->
name,
' ')) {
370 if (IS_SQL_LIKE_CLAUSE(field->
name)) {
371 escape = ESCAPE_CLAUSE;
374 ESCAPE_STRING(buf, field->
value);
382 ast_log(LOG_WARNING,
"MySQL RealTime: Failed to query database: %s\n", mysql_error(&dbh->handle));
383 release_database(dbh);
387 if ((result = mysql_store_result(&dbh->handle))) {
388 numFields = mysql_num_fields(result);
389 fields = mysql_fetch_fields(result);
391 while ((row = mysql_fetch_row(result))) {
392 for (i = 0; i < numFields; i++) {
394 if (row[i] == NULL) {
396 }
else if (ast_strlen_zero(row[i])) {
399 for (stringp = row[i], chunk = strsep(&stringp,
";"); chunk; chunk = strsep(&stringp,
";")) {
401 if ((prev->next = ast_variable_new(fields[i].
name, decode_chunk(chunk),
""))) {
405 prev = var = ast_variable_new(fields[i].name, decode_chunk(chunk),
"");
411 ast_debug(1,
"MySQL RealTime: Could not find any rows in table %s.\n", table);
414 release_database(dbh);
415 mysql_free_result(result);
420 static struct ast_config *realtime_multi_mysql(
const char *database,
const char *table,
const struct ast_variable *rt_fields)
429 const char *initfield = NULL;
439 if (!(dbh = find_database(database, 0))) {
440 ast_log(LOG_WARNING,
"MySQL RealTime: Invalid database specified: '%s' (check res_mysql.conf)\n", database);
445 ast_log(LOG_WARNING,
"MySQL RealTime: No table specified.\n");
446 release_database(dbh);
452 ast_log(LOG_WARNING,
"Out of memory!\n");
453 release_database(dbh);
459 ast_log(LOG_WARNING,
"MySQL RealTime: Realtime retrieval requires at least 1 parameter and 1 value to search on.\n");
461 release_database(dbh);
466 if ((op = strchr(initfield,
' '))) {
471 if (!mysql_reconnect(dbh)) {
472 release_database(dbh);
480 if (!strchr(field->
name,
' ')) {
484 if (IS_SQL_LIKE_CLAUSE(field->
name)) {
485 escape = ESCAPE_CLAUSE;
489 ESCAPE_STRING(buf, field->
value);
491 while ((field = field->
next)) {
493 if (!strchr(field->
name,
' ')) {
497 if (IS_SQL_LIKE_CLAUSE(field->
name)) {
498 escape = ESCAPE_CLAUSE;
501 ESCAPE_STRING(buf, field->
value);
513 ast_log(LOG_WARNING,
"MySQL RealTime: Failed to query database: %s\n", mysql_error(&dbh->handle));
514 release_database(dbh);
519 if ((result = mysql_store_result(&dbh->handle))) {
520 numFields = mysql_num_fields(result);
521 fields = mysql_fetch_fields(result);
523 while ((row = mysql_fetch_row(result))) {
529 for (i = 0; i < numFields; i++) {
530 if (ast_strlen_zero(row[i]))
532 for (stringp = row[i], chunk = strsep(&stringp,
";"); chunk; chunk = strsep(&stringp,
";")) {
533 if (chunk && !ast_strlen_zero(decode_chunk(
ast_strip(chunk)))) {
534 if (initfield && !strcmp(initfield, fields[i].name)) {
535 ast_category_rename(cat, chunk);
537 var = ast_variable_new(fields[i].name, chunk,
"");
538 ast_variable_append(cat, var);
545 ast_debug(1,
"MySQL RealTime: Could not find any rows in table %s.\n", table);
548 release_database(dbh);
549 mysql_free_result(result);
554 static int update_mysql(
const char *database,
const char *tablename,
const char *keyfield,
const char *lookup,
const struct ast_variable *rt_fields)
563 if (!(dbh = find_database(database, 1))) {
564 ast_log(LOG_WARNING,
"MySQL RealTime: Invalid database specified: '%s' (check res_mysql.conf)\n", database);
569 ast_log(LOG_WARNING,
"MySQL RealTime: No table specified.\n");
570 release_database(dbh);
574 if (!(table = find_table(database, tablename))) {
575 ast_log(LOG_ERROR,
"Table '%s' does not exist!!\n", tablename);
576 release_database(dbh);
580 if (!(column = find_column(table, keyfield))) {
581 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);
582 release_table(table);
583 release_database(dbh);
589 ast_log(LOG_WARNING,
"MySQL RealTime: Realtime update requires at least 1 parameter and 1 value to update.\n");
590 release_table(table);
591 release_database(dbh);
596 if (!(column = find_column(table, field->
name))) {
597 ast_log(LOG_ERROR,
"MySQL RealTime: Updating column '%s', but that column does not exist within the table '%s' (first pair MUST exist)!\n", field->
name, tablename);
598 release_table(table);
599 release_database(dbh);
604 if (!mysql_reconnect(dbh)) {
605 release_table(table);
606 release_database(dbh);
613 ESCAPE_STRING(buf, field->
value);
616 while ((field = field->
next)) {
618 if (!(column = find_column(table, field->
name))) {
619 ast_log(LOG_WARNING,
"Attempted to update column '%s' in table '%s', but column does not exist!\n", field->
name, tablename);
623 ESCAPE_STRING(buf, field->
value);
627 ESCAPE_STRING(buf, lookup);
634 ast_log(LOG_WARNING,
"MySQL RealTime: Failed to update database: %s\n", mysql_error(&dbh->handle));
635 release_table(table);
636 release_database(dbh);
640 numrows = mysql_affected_rows(&dbh->handle);
641 release_table(table);
642 release_database(dbh);
644 ast_debug(1,
"MySQL RealTime: Updated %" PRIu64
" rows on table: %s\n", numrows, tablename);
655 static int update2_mysql(
const char *database,
const char *tablename,
const struct ast_variable *lookup_fields,
const struct ast_variable *update_fields)
667 ast_log(LOG_WARNING,
"MySQL RealTime: No table specified.\n");
671 if (!(dbh = find_database(database, 1))) {
672 ast_log(LOG_ERROR,
"Invalid database specified: %s\n", database);
676 if (!(table = find_table(database, tablename))) {
677 ast_log(LOG_ERROR,
"Table '%s' does not exist!!\n", tablename);
678 release_database(dbh);
682 if (!sql || !buf || !where) {
683 release_database(dbh);
684 release_table(table);
692 if (!mysql_reconnect(dbh)) {
693 release_table(table);
694 release_database(dbh);
699 for (field = lookup_fields; field; field = field->
next) {
700 if (!(column = find_column(table, field->
name))) {
701 ast_log(LOG_ERROR,
"Updating on column '%s', but that column does not exist within the table '%s'!\n", field->
name, tablename);
702 release_table(table);
703 release_database(dbh);
706 ESCAPE_STRING(buf, field->
value);
712 for (field = update_fields; field; field = field->
next) {
714 if (!(column = find_column(table, field->
name))) {
715 ast_log(LOG_WARNING,
"Attempted to update column '%s' in table '%s', but column does not exist!\n", field->
name, tablename);
719 ESCAPE_STRING(buf, field->
value);
724 release_table(table);
732 ast_log(LOG_WARNING,
"MySQL RealTime: Failed to update database: %s\n", mysql_error(&dbh->handle));
733 release_table(table);
734 release_database(dbh);
738 numrows = mysql_affected_rows(&dbh->handle);
739 release_database(dbh);
741 ast_debug(1,
"MySQL RealTime: Updated %" PRIu64
" rows on table: %s\n", numrows, tablename);
752 static int store_mysql(
const char *database,
const char *table,
const struct ast_variable *rt_fields)
760 if (!(dbh = find_database(database, 1))) {
761 ast_log(LOG_WARNING,
"MySQL RealTime: Invalid database specified: '%s' (check res_mysql.conf)\n", database);
766 ast_log(LOG_WARNING,
"MySQL RealTime: No table specified.\n");
767 release_database(dbh);
772 ast_log(LOG_WARNING,
"MySQL RealTime: Realtime storage requires at least 1 parameter and 1 value to search on.\n");
773 release_database(dbh);
777 if (!mysql_reconnect(dbh)) {
778 release_database(dbh);
783 ESCAPE_STRING(buf, field->
value);
787 while ((field = field->
next)) {
788 ESCAPE_STRING(buf, field->
value);
798 ast_log(LOG_WARNING,
"MySQL RealTime: Failed to insert into database: %s\n", mysql_error(&dbh->handle));
799 release_database(dbh);
803 release_database(dbh);
805 ast_debug(1,
"MySQL RealTime: row inserted on table: %s\n", table);
810 static int destroy_mysql(
const char *database,
const char *table,
const char *keyfield,
const char *lookup,
const struct ast_variable *rt_fields)
818 if (!(dbh = find_database(database, 1))) {
819 ast_log(LOG_WARNING,
"MySQL RealTime: Invalid database specified: '%s' (check res_mysql.conf)\n", database);
824 ast_log(LOG_WARNING,
"MySQL RealTime: No table specified.\n");
825 release_database(dbh);
832 if (ast_strlen_zero(keyfield) || ast_strlen_zero(lookup)) {
833 ast_log(LOG_WARNING,
"MySQL RealTime: Realtime destroying requires at least 1 parameter and 1 value to search on.\n");
834 release_database(dbh);
839 if (!mysql_reconnect(dbh)) {
840 release_database(dbh);
846 ESCAPE_STRING(buf, lookup);
848 for (field = rt_fields; field; field = field->
next) {
849 ESCAPE_STRING(buf, field->
value);
857 ast_log(LOG_WARNING,
"MySQL RealTime: Failed to delete from database: %s\n", mysql_error(&dbh->handle));
858 release_database(dbh);
862 numrows = mysql_affected_rows(&dbh->handle);
863 release_database(dbh);
865 ast_debug(1,
"MySQL RealTime: Deleted %" PRIu64
" rows on table: %s\n", numrows, table);
876 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)
886 int last_cat_metric = 0;
890 if (!file || !strcmp(file, RES_CONFIG_MYSQL_CONF)) {
891 ast_log(LOG_WARNING,
"MySQL RealTime: Cannot configure myself.\n");
895 if (!(dbh = find_database(database, 0))) {
896 ast_log(LOG_WARNING,
"MySQL RealTime: Invalid database specified: '%s' (check res_mysql.conf)\n", database);
900 ast_str_set(&sql, 0,
"SELECT category, var_name, var_val, cat_metric FROM %s WHERE filename='%s' and commented=0 ORDER BY filename, category, cat_metric desc, var_metric asc, var_name, var_val, id", table, file);
905 if (!mysql_reconnect(dbh)) {
910 ast_log(LOG_WARNING,
"MySQL RealTime: Failed to query database. Check debug for more info.\n");
912 ast_debug(1,
"MySQL RealTime: Query Failed because: %s\n", mysql_error(&dbh->handle));
913 release_database(dbh);
917 if ((result = mysql_store_result(&dbh->handle))) {
918 num_rows = mysql_num_rows(result);
919 ast_debug(1,
"MySQL RealTime: Found %" PRIu64
" rows.\n", num_rows);
924 while ((row = mysql_fetch_row(result))) {
925 if (!strcmp(row[1],
"#include")) {
926 if (!ast_config_internal_load(row[2], cfg, config_flags,
"", who_asked)) {
927 mysql_free_result(result);
928 release_database(dbh);
934 if (strcmp(last, row[0]) || last_cat_metric != atoi(row[3])) {
939 strcpy(last, row[0]);
940 last_cat_metric = atoi(row[3]);
943 new_v = ast_variable_new(row[1], row[2],
"");
945 ast_variable_append(cur_cat, new_v);
948 ast_log(LOG_WARNING,
"MySQL RealTime: Could not find config '%s' in database.\n", file);
951 mysql_free_result(result);
952 release_database(dbh);
957 static int unload_mysql(
const char *database,
const char *tablename)
962 if (strcmp(cur->name, tablename) == 0) {
973 static int require_mysql(
const char *database,
const char *tablename, va_list ap)
976 struct tables *table = find_table(database, tablename);
983 ast_log(LOG_WARNING,
"Table %s not found in database. This table should exist if you're using realtime.\n", tablename);
987 while ((elm = va_arg(ap,
char *))) {
989 size = va_arg(ap,
int);
992 if (strcmp(column->name, elm) == 0) {
994 if (strncmp(column->type,
"char", 4) == 0 || strncmp(column->type,
"varchar", 7) == 0) {
995 if ((size > column->len) && column->len != -1) {
996 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);
999 }
else if (strcasestr(column->type,
"unsigned")) {
1001 ast_log(LOG_WARNING,
"Realtime table %s@%s: column '%s' cannot be type '%s' (need %s)\n",
1002 database, tablename, column->name, column->type,
1003 type == RQ_CHAR ?
"char" : type == RQ_FLOAT ?
"float" :
1004 type == RQ_DATETIME ?
"datetime" : type == RQ_DATE ?
"date" :
"a rather stiff drink");
1006 }
else if (strncasecmp(column->type,
"tinyint", 1) == 0) {
1007 if (type != RQ_UINTEGER1) {
1008 ast_log(LOG_WARNING,
"Realtime table %s@%s: column '%s' may not be large enough for " \
1009 "the required data length: %d (detected stringtype)\n", \
1010 tablename, database, column->name, size); \
1013 }
else if (strncasecmp(column->type,
"smallint", 1) == 0) {
1014 if (type != RQ_UINTEGER1 && type != RQ_INTEGER1 && type != RQ_UINTEGER2) {
1015 ast_log(LOG_WARNING,
"Realtime table %s@%s: column '%s' may not be large enough for " \
1016 "the required data length: %d (detected stringtype)\n", \
1017 tablename, database, column->name, size); \
1020 }
else if (strncasecmp(column->type,
"mediumint", 1) == 0) {
1021 if (type != RQ_UINTEGER1 && type != RQ_INTEGER1 &&
1022 type != RQ_UINTEGER2 && type != RQ_INTEGER2 &&
1023 type != RQ_UINTEGER3) {
1024 ast_log(LOG_WARNING,
"Realtime table %s@%s: column '%s' may not be large enough for " \
1025 "the required data length: %d (detected stringtype)\n", \
1026 tablename, database, column->name, size); \
1029 }
else if (strncasecmp(column->type,
"int", 1) == 0) {
1030 if (type != RQ_UINTEGER1 && type != RQ_INTEGER1 &&
1031 type != RQ_UINTEGER2 && type != RQ_INTEGER2 &&
1032 type != RQ_UINTEGER3 && type != RQ_INTEGER3 &&
1033 type != RQ_UINTEGER4) {
1034 ast_log(LOG_WARNING,
"Realtime table %s@%s: column '%s' may not be large enough for " \
1035 "the required data length: %d (detected stringtype)\n", \
1036 tablename, database, column->name, size); \
1039 }
else if (strncasecmp(column->type,
"bigint", 1) == 0) {
1040 if (type != RQ_UINTEGER1 && type != RQ_INTEGER1 &&
1041 type != RQ_UINTEGER2 && type != RQ_INTEGER2 &&
1042 type != RQ_UINTEGER3 && type != RQ_INTEGER3 &&
1043 type != RQ_UINTEGER4 && type != RQ_INTEGER4 &&
1044 type != RQ_UINTEGER8) {
1045 ast_log(LOG_WARNING,
"Realtime table %s@%s: column '%s' may not be large enough for " \
1046 "the required data length: %d (detected stringtype)\n", \
1047 tablename, database, column->name, size); \
1051 }
else if (strcasestr(column->type,
"int")) {
1053 ast_log(LOG_WARNING,
"Realtime table %s@%s: column '%s' cannot be type '%s' (need %s)\n",
1054 database, tablename, column->name, column->type,
1055 type == RQ_CHAR ?
"char" : type == RQ_FLOAT ?
"float" :
1056 type == RQ_DATETIME ?
"datetime" : type == RQ_DATE ?
"date" :
1057 "to get a life, rather than writing silly error messages");
1059 }
else if (strncasecmp(column->type,
"tinyint", 1) == 0) {
1060 if (type != RQ_INTEGER1) {
1061 ast_log(LOG_WARNING,
"Realtime table %s@%s: column '%s' may not be large enough for " \
1062 "the required data length: %d (detected stringtype)\n", \
1063 tablename, database, column->name, size); \
1066 }
else if (strncasecmp(column->type,
"smallint", 1) == 0) {
1067 if (type != RQ_UINTEGER1 && type != RQ_INTEGER1 && type != RQ_INTEGER2) {
1068 ast_log(LOG_WARNING,
"Realtime table %s@%s: column '%s' may not be large enough for " \
1069 "the required data length: %d (detected stringtype)\n", \
1070 tablename, database, column->name, size); \
1073 }
else if (strncasecmp(column->type,
"mediumint", 1) == 0) {
1074 if (type != RQ_UINTEGER1 && type != RQ_INTEGER1 &&
1075 type != RQ_UINTEGER2 && type != RQ_INTEGER2 &&
1076 type != RQ_INTEGER3) {
1077 ast_log(LOG_WARNING,
"Realtime table %s@%s: column '%s' may not be large enough for " \
1078 "the required data length: %d (detected stringtype)\n", \
1079 tablename, database, column->name, size); \
1082 }
else if (strncasecmp(column->type,
"int", 1) == 0) {
1083 if (type != RQ_UINTEGER1 && type != RQ_INTEGER1 &&
1084 type != RQ_UINTEGER2 && type != RQ_INTEGER2 &&
1085 type != RQ_UINTEGER3 && type != RQ_INTEGER3 &&
1086 type != RQ_INTEGER4) {
1087 ast_log(LOG_WARNING,
"Realtime table %s@%s: column '%s' may not be large enough for " \
1088 "the required data length: %d (detected stringtype)\n", \
1089 tablename, database, column->name, size); \
1092 }
else if (strncasecmp(column->type,
"bigint", 1) == 0) {
1093 if (type != RQ_UINTEGER1 && type != RQ_INTEGER1 &&
1094 type != RQ_UINTEGER2 && type != RQ_INTEGER2 &&
1095 type != RQ_UINTEGER3 && type != RQ_INTEGER3 &&
1096 type != RQ_UINTEGER4 && type != RQ_INTEGER4 &&
1097 type != RQ_INTEGER8) {
1098 ast_log(LOG_WARNING,
"Realtime table %s@%s: column '%s' may not be large enough for " \
1099 "the required data length: %d (detected stringtype)\n", \
1100 tablename, database, column->name, size); \
1104 }
else if (strncmp(column->type,
"float", 5) == 0) {
1106 ast_log(LOG_WARNING,
"Realtime table %s@%s: Column %s cannot be a %s\n", tablename, database, column->name, column->type);
1109 }
else if (strncmp(column->type,
"datetime", 8) == 0 || strncmp(column->type,
"timestamp", 9) == 0) {
1110 if (type != RQ_DATETIME) {
1111 ast_log(LOG_WARNING,
"Realtime table %s@%s: Column %s cannot be a %s\n", tablename, database, column->name, column->type);
1114 }
else if (strncmp(column->type,
"date", 4) == 0) {
1115 if (type != RQ_DATE) {
1116 ast_log(LOG_WARNING,
"Realtime table %s@%s: Column %s cannot be a %s\n", tablename, database, column->name, column->type);
1120 ast_log(LOG_WARNING,
"Possibly unsupported column type '%s' on column '%s'\n", column->type, column->name);
1128 ast_log(LOG_WARNING,
"Table %s requires a column '%s' of size '%d', but no such column exists.\n", tablename, elm, size);
1131 release_table(table);
1138 .load_func = config_mysql,
1139 .realtime_func = realtime_mysql,
1140 .realtime_multi_func = realtime_multi_mysql,
1141 .store_func = store_mysql,
1142 .destroy_func = destroy_mysql,
1143 .update_func = update_mysql,
1144 .update2_func = update2_mysql,
1145 .require_func = require_mysql,
1146 .unload_func = unload_mysql,
1149 static int load_module(
void)
1154 ast_verb(2,
"MySQL RealTime driver loaded.\n");
1159 static int unload_module(
void)
1166 ast_verb(2,
"MySQL RealTime unloaded.\n");
1169 while ((cur = AST_RWLIST_REMOVE_HEAD(&
databases, list))) {
1170 mysql_close(&cur->handle);
1171 ast_mutex_destroy(&cur->lock);
1179 destroy_table(table);
1186 static int reload(
void)
1189 ast_verb(2,
"MySQL RealTime reloaded.\n");
1193 static int parse_config(
int reload)
1200 if ((config =
ast_config_load(RES_CONFIG_MYSQL_CONF, config_flags)) == CONFIG_STATUS_FILEMISSING) {
1205 if (config == CONFIG_STATUS_FILEMISSING) {
1207 }
else if (config == CONFIG_STATUS_FILEUNCHANGED) {
1209 }
else if (config == CONFIG_STATUS_FILEINVALID) {
1210 ast_log(LOG_ERROR,
"Not %sloading " RES_CONFIG_MYSQL_CONF
"\n", reload ?
"re" :
"");
1216 AST_RWLIST_TRAVERSE(&
databases, cur, list) {
1217 if (!strcmp(cur->unique_name, catg)) {
1223 if (!(cur =
ast_calloc(1,
sizeof(*cur) + strlen(catg) + 1))) {
1224 ast_log(LOG_WARNING,
"Could not allocate space for MySQL database '%s'\n", catg);
1228 strcpy(cur->unique_name, catg);
1229 ast_mutex_init(&cur->lock);
1230 AST_RWLIST_INSERT_TAIL(&
databases, cur, list);
1233 load_mysql_config(config, catg, cur);
1242 static int load_mysql_config(
struct ast_config *config,
const char *category,
struct mysql_conn *conn)
1246 if (!(s = ast_variable_retrieve(config, category,
"dbuser"))) {
1247 ast_log(LOG_WARNING,
"MySQL RealTime: No database user found, using 'asterisk' as default.\n");
1252 if (!(s = ast_variable_retrieve(config, category,
"dbpass"))) {
1253 ast_log(LOG_WARNING,
"MySQL RealTime: No database password found, using 'asterisk' as default.\n");
1258 if (!(s = ast_variable_retrieve(config, category,
"dbhost"))) {
1259 ast_log(LOG_WARNING,
"MySQL RealTime: No database host found, using localhost via socket.\n");
1264 if (!(s = ast_variable_retrieve(config, category,
"dbname"))) {
1265 ast_log(LOG_WARNING,
"MySQL RealTime: No database name found, using 'asterisk' as default.\n");
1270 if (!(s = ast_variable_retrieve(config, category,
"dbport"))) {
1271 ast_log(LOG_WARNING,
"MySQL RealTime: No database port found, using 3306 as default.\n");
1274 conn->port = atoi(s);
1276 if (!(s = ast_variable_retrieve(config, category,
"dbsock"))) {
1277 if (ast_strlen_zero(conn->host)) {
1278 char *paths[3] = {
"/tmp/mysql.sock",
"/var/lib/mysql/mysql.sock",
"/var/run/mysqld/mysqld.sock" };
1281 for (i = 0; i < 3; i++) {
1282 if (!stat(paths[i], &st)) {
1283 ast_log(LOG_WARNING,
"MySQL RealTime: No database socket found, using '%s' as default.\n", paths[i]);
1288 ast_log(LOG_WARNING,
"MySQL RealTime: No database socket found (and unable to detect a suitable path).\n");
1295 if ((s = ast_variable_retrieve(config, category,
"dbcharset"))) {
1299 if (!(s = ast_variable_retrieve(config, category,
"requirements"))) {
1300 ast_log(LOG_WARNING,
"MySQL realtime: no requirements setting found, using 'warn' as default.\n");
1301 conn->requirements = RQ_WARN;
1302 }
else if (!strcasecmp(s,
"createclose")) {
1303 conn->requirements = RQ_CREATECLOSE;
1304 }
else if (!strcasecmp(s,
"createchar")) {
1305 conn->requirements = RQ_CREATECHAR;
1306 }
else if (!strcasecmp(s,
"warn")) {
1307 conn->requirements = RQ_WARN;
1309 ast_log(LOG_WARNING,
"MySQL realtime: unrecognized requirements setting '%s', using 'warn'\n", s);
1310 conn->requirements = RQ_WARN;
1313 if (!ast_strlen_zero(conn->host)) {
1314 ast_debug(1,
"MySQL RealTime host: %s\n", conn->host);
1315 ast_debug(1,
"MySQL RealTime port: %i\n", conn->port);
1317 ast_debug(1,
"MySQL RealTime socket: %s\n", conn->sock);
1318 ast_debug(1,
"MySQL RealTime database name: %s\n", conn->name);
1319 ast_debug(1,
"MySQL RealTime user: %s\n", conn->user);
1320 ast_debug(1,
"MySQL RealTime password: %s\n", conn->pass);
1321 if(!ast_strlen_zero(conn->charset))
1322 ast_debug(1,
"MySQL RealTime charset: %s\n", conn->charset);
1327 static int mysql_reconnect(
struct mysql_conn *conn)
1329 #ifdef MYSQL_OPT_RECONNECT
1330 my_bool trueval = 1;
1336 if ((!conn->connected) && (!ast_strlen_zero(conn->host) || !ast_strlen_zero(conn->sock)) && !ast_strlen_zero(conn->user) && !ast_strlen_zero(conn->name)) {
1337 if (!mysql_init(&conn->handle)) {
1338 ast_log(LOG_WARNING,
"MySQL RealTime: Insufficient memory to allocate MySQL resource.\n");
1339 conn->connected = 0;
1342 if(strlen(conn->charset) > 2){
1343 char set_names[255];
1344 char statement[512];
1345 snprintf(set_names,
sizeof(set_names),
"SET NAMES %s", conn->charset);
1346 mysql_real_escape_string(&conn->handle, statement, set_names,
sizeof(set_names));
1347 mysql_options(&conn->handle, MYSQL_INIT_COMMAND, set_names);
1348 mysql_options(&conn->handle, MYSQL_SET_CHARSET_NAME, conn->charset);
1351 if (mysql_real_connect(&conn->handle, conn->host, conn->user, conn->pass, conn->name, conn->port, conn->sock, 0)) {
1352 #ifdef MYSQL_OPT_RECONNECT
1355 mysql_options(&conn->handle, MYSQL_OPT_RECONNECT, &trueval);
1357 ast_debug(1,
"MySQL RealTime: Successfully connected to database.\n");
1358 conn->connected = 1;
1359 conn->connect_time = time(NULL);
1362 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));
1363 ast_debug(1,
"MySQL RealTime: Cannot Connect (%d): %s\n", mysql_errno(&conn->handle), mysql_error(&conn->handle));
1364 conn->connected = 0;
1365 conn->connect_time = 0;
1371 if (mysql_ping(&conn->handle) != 0 && (usleep(1) + 2 > 0) && mysql_ping(&conn->handle) != 0) {
1372 conn->connected = 0;
1373 conn->connect_time = 0;
1374 ast_log(LOG_ERROR,
"MySQL RealTime: Ping failed (%d). Trying an explicit reconnect.\n", mysql_errno(&conn->handle));
1375 ast_debug(1,
"MySQL RealTime: Server Error (%d): %s\n", mysql_errno(&conn->handle), mysql_error(&conn->handle));
1376 goto reconnect_tryagain;
1379 if (!conn->connected) {
1380 conn->connected = 1;
1381 conn->connect_time = time(NULL);
1384 if (mysql_select_db(&conn->handle, conn->name) != 0) {
1385 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));
1389 ast_debug(1,
"MySQL RealTime: Connection okay.\n");
1402 e->
command =
"realtime mysql cache";
1404 "Usage: realtime mysql cache [<database> <table>]\n"
1405 " Shows table cache for the MySQL RealTime driver\n";
1408 if (a->argc < 4 || a->argc > 5) {
1411 l = strlen(a->word);
1416 if (!strcasecmp(a->argv[3], cur->database->unique_name) && !strncasecmp(a->word, cur->name, l) && ++which > a->n) {
1425 AST_RWLIST_TRAVERSE(&
databases, cur, list) {
1426 if (!strncasecmp(a->word, cur->unique_name, l) && ++which > a->n) {
1440 ast_cli(a->fd,
"%20.20s %s\n", cur->database->unique_name, cur->name);
1443 }
else if (a->argc == 4) {
1448 if (!strcasecmp(cur->database->unique_name, a->argv[3])) {
1449 ast_cli(a->fd,
"%s\n", cur->name);
1455 ast_cli(a->fd,
"No tables cached within %s database\n", a->argv[3]);
1457 }
else if (a->argc == 5) {
1459 if ((cur = find_table(a->argv[3], a->argv[4]))) {
1461 ast_cli(a->fd,
"Columns for Table Cache '%s':\n", a->argv[3]);
1462 ast_cli(a->fd,
"%-20.20s %-20.20s %-3.3s\n",
"Name",
"Type",
"Len");
1464 ast_cli(a->fd,
"%-20.20s %-20.20s %3d\n", col->name, col->type, col->len);
1468 ast_cli(a->fd,
"No such table '%s'\n", a->argv[3]);
1476 char status[256], status2[100] =
"", type[20];
1478 int ctime = 0, found = 0;
1480 int l = 0, which = 0;
1484 e->
command =
"realtime mysql status";
1486 "Usage: realtime mysql status [<database>]\n"
1487 " Shows connection information for the MySQL RealTime driver\n";
1492 AST_RWLIST_TRAVERSE(&
databases, cur, list) {
1493 if (!strncasecmp(a->word, cur->unique_name, l) && ++which > a->n) {
1504 return CLI_SHOWUSAGE;
1507 AST_RWLIST_TRAVERSE(&
databases, cur, list) {
1508 if (a->argc == 3 || (a->argc == 4 && !strcasecmp(a->argv[3], cur->unique_name))) {
1511 if (mysql_reconnect(cur)) {
1512 snprintf(type,
sizeof(type),
"connected to");
1513 ctime = time(NULL) - cur->connect_time;
1515 snprintf(type,
sizeof(type),
"configured for");
1519 if (!ast_strlen_zero(cur->host)) {
1520 snprintf(status,
sizeof(status),
"%s %s %s@%s, port %d", cur->unique_name, type, cur->name, cur->host, cur->port);
1522 snprintf(status,
sizeof(status),
"%s %s %s on socket file %s", cur->unique_name, type, cur->name, cur->sock);
1525 if (!ast_strlen_zero(cur->user)) {
1526 snprintf(status2,
sizeof(status2),
" with username %s", cur->user);
1531 if (ctime > 31536000) {
1532 ast_cli(a->fd,
"%s%s for %.1f years.\n", status, status2, (
double)ctime / 31536000.0);
1533 }
else if (ctime > 86400 * 30) {
1534 ast_cli(a->fd,
"%s%s for %d days.\n", status, status2, ctime / 86400);
1535 }
else if (ctime > 86400) {
1536 ast_cli(a->fd,
"%s%s for %d days, %d hours.\n", status, status2, ctime / 86400, (ctime % 86400) / 3600);
1537 }
else if (ctime > 3600) {
1538 ast_cli(a->fd,
"%s%s for %d hours, %d minutes.\n", status, status2, ctime / 3600, (ctime % 3600) / 60);
1539 }
else if (ctime > 60) {
1540 ast_cli(a->fd,
"%s%s for %d minutes.\n", status, status2, ctime / 60);
1541 }
else if (ctime > -1) {
1542 ast_cli(a->fd,
"%s%s for %d seconds.\n", status, status2, ctime);
1544 ast_cli(a->fd,
"%s%s.\n", status, status2);
1551 ast_cli(a->fd,
"No connections configured.\n");
1556 AST_MODULE_INFO(
ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER,
"MySQL RealTime Configuration Driver",
1557 .support_level = AST_MODULE_SUPPORT_EXTENDED,
1558 .load = load_module,
1559 .unload = unload_module,
1562 .requires =
"extconfig",
struct ast_variable * next
#define AST_THREADSTORAGE(name)
Define a thread storage variable.
require_type
Types used in ast_realtime_require_field.
#define AST_LIST_LOCK(head)
Locks a list.
Asterisk locking-related definitions:
Asterisk main include file. File version handling, generic pbx functions.
String manipulation functions.
int ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
Unregister multiple commands.
descriptor for a cli entry.
#define AST_LIST_UNLOCK(head)
Attempts to unlock a list.
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
#define AST_RWLIST_RDLOCK(head)
Read locks a list.
Structure for variables, used for configurations and for channel variables.
#define AST_RWLIST_WRLOCK(head)
Write locks a list.
int ast_config_engine_deregister(struct ast_config_engine *del)
Deregister config engine.
int ast_config_engine_register(struct ast_config_engine *newconfig)
Register config engine.
int ast_str_append(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Append to a thread local dynamic string.
#define ast_cli_register_multiple(e, len)
Register multiple commands.
#define ast_strdup(str)
A wrapper for strdup()
char * ast_category_browse(struct ast_config *config, const char *prev_name)
Browse categories.
Definitions to aid in the use of thread local storage.
#define AST_LIST_TRAVERSE_SAFE_END
Closes a safe loop traversal block.
Configuration engine structure, used to define realtime drivers.
#define AST_RWLIST_HEAD_STATIC(name, type)
Defines a structure to be used to hold a read/write list of specified type, statically initialized...
void ast_category_append(struct ast_config *config, struct ast_category *category)
Appends a category to a config.
#define ast_category_new_anonymous()
Create a nameless category that is not backed by a file.
int ast_str_set(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Set a dynamic string using variable arguments.
Configuration File Parser.
#define ast_config_load(filename, flags)
Load a config file.
General Asterisk PBX channel definitions.
#define ast_strdupa(s)
duplicate a string in memory from the stack
char * ast_strip(char *s)
Strip leading/trailing whitespace from a string.
#define AST_LIST_REMOVE_CURRENT(field)
Removes the current entry from a list during a traversal.
#define ast_debug(level,...)
Log a DEBUG message.
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
struct ast_config * ast_config_new(void)
Create a new base configuration structure.
#define AST_LIST_HEAD_STATIC(name, type)
Defines a structure to be used to hold a list of specified type, statically initialized.
#define ast_alloca(size)
call __builtin_alloca to ensure we get gcc builtin semantics
#define AST_LIST_HEAD_NOLOCK(name, type)
Defines a structure to be used to hold a list of specified type (with no lock).
#define AST_LIST_INSERT_TAIL(head, elm, field)
Appends a list entry to the tail of a list.
Support for dynamic strings.
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
#define AST_LIST_ENTRY(type)
Declare a forward link structure inside a list entry.
#define ast_calloc(num, len)
A wrapper for calloc()
Support for logging to various files, console and syslog Configuration in file logger.conf.
#define ast_category_new_dynamic(name)
Create a category that is not backed by a file.
structure to hold users read from users.conf
Structure used to handle boolean flags.
size_t ast_str_strlen(const struct ast_str *buf)
Returns the current length of the string stored within buf.
#define AST_LIST_HEAD_INIT_NOLOCK(head)
Initializes a list head structure.
Standard Command Line Interface.
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Options provided by main asterisk program.
#define AST_RWLIST_UNLOCK(head)
Attempts to unlock a read/write based list.
#define AST_LIST_TRAVERSE_SAFE_BEGIN(head, var, field)
Loops safely over (traverses) the entries in a list.
struct ast_str * ast_str_thread_get(struct ast_threadstorage *ts, size_t init_len)
Retrieve a thread locally stored dynamic string.
void ast_config_destroy(struct ast_config *cfg)
Destroys a config.
int ast_rq_is_int(require_type type)
Check if require type is an integer type.
#define ASTERISK_GPL_KEY
The text the key() function should return.
Asterisk module definitions.
Structure for mutex and tracking information.