Asterisk - The Open Source Telephony Project GIT-master-67613d1
Data Structures | Macros | Enumerations | Functions | Variables
res_config_mysql.c File Reference

MySQL CDR backend. More...

#include "asterisk.h"
#include <sys/stat.h>
#include <mysql/mysql.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"
Include dependency graph for res_config_mysql.c:

Go to the source code of this file.

Data Structures

struct  columns
 
struct  databases
 
struct  tables::mysql_columns
 
struct  mysql_conn
 
struct  mysql_tables
 
struct  tables
 

Macros

#define ESCAPE_STRING(buf, var)
 
#define IS_SQL_LIKE_CLAUSE(x)   ((x) && ast_ends_with(x, " LIKE"))
 
#define READHANDLE   0
 
#define release_database(a)   ast_mutex_unlock(&(a)->lock)
 
#define RES_CONFIG_MYSQL_CONF   "res_config_mysql.conf"
 
#define RES_CONFIG_MYSQL_CONF_OLD   "res_mysql.conf"
 
#define WRITEHANDLE   1
 

Enumerations

enum  requirements { RQ_WARN , RQ_CREATECLOSE , RQ_CREATECHAR }
 

Functions

static void __init_find_buf (void)
 
static void __init_modify2_buf (void)
 
static void __init_modify3_buf (void)
 
static void __init_modify_buf (void)
 
static void __init_scratch2_buf (void)
 
static void __init_scratch_buf (void)
 
static void __init_sql2_buf (void)
 
static void __init_sql_buf (void)
 
static void __reg_module (void)
 
static void __unreg_module (void)
 
struct ast_moduleAST_MODULE_SELF_SYM (void)
 
static struct ast_configconfig_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)
 
static char * decode_chunk (char *chunk)
 
static int destroy_mysql (const char *database, const char *table, const char *keyfield, const char *lookup, const struct ast_variable *rt_fields)
 
static void destroy_table (struct tables *table)
 
static struct columnsfind_column (struct tables *table, const char *colname)
 
static struct mysql_connfind_database (const char *database, int for_write)
 
static struct tablesfind_table (const char *database, const char *tablename)
 
static char * handle_cli_realtime_mysql_cache (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 
static char * handle_cli_realtime_mysql_status (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 
static int load_module (void)
 
static int load_mysql_config (struct ast_config *config, const char *category, struct mysql_conn *conn)
 
static int mysql_reconnect (struct mysql_conn *conn)
 
static int parse_config (int reload)
 
static struct ast_configrealtime_multi_mysql (const char *database, const char *table, const struct ast_variable *rt_fields)
 
static struct ast_variablerealtime_mysql (const char *database, const char *table, const struct ast_variable *rt_fields)
 
static void release_table (struct tables *table)
 
static int reload (void)
 
static int require_mysql (const char *database, const char *tablename, va_list ap)
 
static int store_mysql (const char *database, const char *table, const struct ast_variable *rt_fields)
 
static int unload_module (void)
 
static int unload_mysql (const char *database, const char *tablename)
 
static int update2_mysql (const char *database, const char *tablename, const struct ast_variable *lookup_fields, const struct ast_variable *update_fields)
 
static int update_mysql (const char *database, const char *tablename, const char *keyfield, const char *lookup, const struct ast_variable *rt_fields)
 

Variables

static struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_LOAD_ORDER , .description = "MySQL RealTime Configuration Driver" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = AST_BUILDOPT_SUM, .support_level = AST_MODULE_SUPPORT_EXTENDED, .load = load_module, .unload = unload_module, .reload = reload, .load_pri = AST_MODPRI_REALTIME_DRIVER, .requires = "extconfig", }
 
static const struct ast_module_infoast_module_info = &__mod_info
 
static struct ast_cli_entry cli_realtime_mysql_status []
 
static struct databases databases = { .first = NULL, .last = NULL, .lock = { PTHREAD_RWLOCK_INITIALIZER , NULL, {1, 0} } , }
 
static char * ESCAPE_CLAUSE = " ESCAPE '\\\\'"
 
static struct ast_threadstorage find_buf = { .once = PTHREAD_ONCE_INIT , .key_init = __init_find_buf , .custom_init = NULL , }
 
static struct ast_threadstorage modify2_buf = { .once = PTHREAD_ONCE_INIT , .key_init = __init_modify2_buf , .custom_init = NULL , }
 
static struct ast_threadstorage modify3_buf = { .once = PTHREAD_ONCE_INIT , .key_init = __init_modify3_buf , .custom_init = NULL , }
 
static struct ast_threadstorage modify_buf = { .once = PTHREAD_ONCE_INIT , .key_init = __init_modify_buf , .custom_init = NULL , }
 
static struct ast_config_engine mysql_engine
 
static struct mysql_tables mysql_tables = { .first = NULL, .last = NULL, .lock = { PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP , NULL, {1, 0} } , }
 
static struct ast_threadstorage scratch2_buf = { .once = PTHREAD_ONCE_INIT , .key_init = __init_scratch2_buf , .custom_init = NULL , }
 
static struct ast_threadstorage scratch_buf = { .once = PTHREAD_ONCE_INIT , .key_init = __init_scratch_buf , .custom_init = NULL , }
 
static struct ast_threadstorage sql2_buf = { .once = PTHREAD_ONCE_INIT , .key_init = __init_sql2_buf , .custom_init = NULL , }
 
static struct ast_threadstorage sql_buf = { .once = PTHREAD_ONCE_INIT , .key_init = __init_sql_buf , .custom_init = NULL , }
 

Detailed Description

MySQL CDR backend.

Definition in file res_config_mysql.c.

Macro Definition Documentation

◆ ESCAPE_STRING

#define ESCAPE_STRING (   buf,
  var 
)

Definition at line 54 of file res_config_mysql.c.

◆ IS_SQL_LIKE_CLAUSE

#define IS_SQL_LIKE_CLAUSE (   x)    ((x) && ast_ends_with(x, " LIKE"))

Definition at line 305 of file res_config_mysql.c.

◆ READHANDLE

#define READHANDLE   0

Definition at line 51 of file res_config_mysql.c.

◆ release_database

#define release_database (   a)    ast_mutex_unlock(&(a)->lock)

Definition at line 162 of file res_config_mysql.c.

◆ RES_CONFIG_MYSQL_CONF

#define RES_CONFIG_MYSQL_CONF   "res_config_mysql.conf"

Definition at line 49 of file res_config_mysql.c.

◆ RES_CONFIG_MYSQL_CONF_OLD

#define RES_CONFIG_MYSQL_CONF_OLD   "res_mysql.conf"

Definition at line 50 of file res_config_mysql.c.

◆ WRITEHANDLE

#define WRITEHANDLE   1

Definition at line 52 of file res_config_mysql.c.

Enumeration Type Documentation

◆ requirements

Enumerator
RQ_WARN 
RQ_CREATECLOSE 
RQ_CREATECHAR 

Definition at line 81 of file res_config_mysql.c.

@ RQ_CREATECHAR
@ RQ_WARN
@ RQ_CREATECLOSE

Function Documentation

◆ __init_find_buf()

static void __init_find_buf ( void  )
static

Definition at line 74 of file res_config_mysql.c.

◆ __init_modify2_buf()

static void __init_modify2_buf ( void  )
static

Definition at line 78 of file res_config_mysql.c.

◆ __init_modify3_buf()

static void __init_modify3_buf ( void  )
static

Definition at line 79 of file res_config_mysql.c.

◆ __init_modify_buf()

static void __init_modify_buf ( void  )
static

Definition at line 77 of file res_config_mysql.c.

◆ __init_scratch2_buf()

static void __init_scratch2_buf ( void  )
static

Definition at line 76 of file res_config_mysql.c.

◆ __init_scratch_buf()

static void __init_scratch_buf ( void  )
static

Definition at line 75 of file res_config_mysql.c.

◆ __init_sql2_buf()

static void __init_sql2_buf ( void  )
static

Definition at line 73 of file res_config_mysql.c.

◆ __init_sql_buf()

static void __init_sql_buf ( void  )
static

Definition at line 72 of file res_config_mysql.c.

◆ __reg_module()

static void __reg_module ( void  )
static

Definition at line 1563 of file res_config_mysql.c.

◆ __unreg_module()

static void __unreg_module ( void  )
static

Definition at line 1563 of file res_config_mysql.c.

◆ AST_MODULE_SELF_SYM()

struct ast_module * AST_MODULE_SELF_SYM ( void  )

Definition at line 1563 of file res_config_mysql.c.

◆ config_mysql()

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 
)
static

Definition at line 876 of file res_config_mysql.c.

877{
878 struct mysql_conn *dbh;
879 MYSQL_RES *result;
880 MYSQL_ROW row;
881 uint64_t num_rows;
882 struct ast_variable *new_v;
883 struct ast_category *cur_cat = NULL;
884 struct ast_str *sql = ast_str_thread_get(&sql_buf, 200);
885 char last[80] = "";
886 int last_cat_metric = 0;
887
889
890 if (!file || !strcmp(file, RES_CONFIG_MYSQL_CONF)) {
891 ast_log(LOG_WARNING, "MySQL RealTime: Cannot configure myself.\n");
892 return NULL;
893 }
894
895 if (!(dbh = find_database(database, 0))) {
896 ast_log(LOG_WARNING, "MySQL RealTime: Invalid database specified: '%s' (check res_mysql.conf)\n", database);
897 return NULL;
898 }
899
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);
901
902 ast_debug(1, "MySQL RealTime: Static SQL: %s\n", ast_str_buffer(sql));
903
904 /* We now have our complete statement; Lets connect to the server and execute it. */
905 if (!mysql_reconnect(dbh)) {
906 return NULL;
907 }
908
909 if (mysql_real_query(&dbh->handle, ast_str_buffer(sql), ast_str_strlen(sql))) {
910 ast_log(LOG_WARNING, "MySQL RealTime: Failed to query database. Check debug for more info.\n");
911 ast_debug(1, "MySQL RealTime: Query: %s\n", ast_str_buffer(sql));
912 ast_debug(1, "MySQL RealTime: Query Failed because: %s\n", mysql_error(&dbh->handle));
913 release_database(dbh);
914 return NULL;
915 }
916
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);
920
921 /* There might exist a better way to access the column names other than counting,
922 * but I believe that would require another loop that we don't need. */
923
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);
929 return NULL;
930 }
931 continue;
932 }
933
934 if (strcmp(last, row[0]) || last_cat_metric != atoi(row[3])) {
935 cur_cat = ast_category_new_dynamic(row[0]);
936 if (!cur_cat) {
937 break;
938 }
939 strcpy(last, row[0]);
940 last_cat_metric = atoi(row[3]);
941 ast_category_append(cfg, cur_cat);
942 }
943 new_v = ast_variable_new(row[1], row[2], "");
944 if (cur_cat)
945 ast_variable_append(cur_cat, new_v);
946 }
947 } else {
948 ast_log(LOG_WARNING, "MySQL RealTime: Could not find config '%s' in database.\n", file);
949 }
950
951 mysql_free_result(result);
952 release_database(dbh);
953
954 return cfg;
955}
struct sla_ringing_trunk * last
Definition: app_sla.c:332
#define ast_log
Definition: astobj2.c:42
static char * table
Definition: cdr_odbc.c:55
static PGresult * result
Definition: cel_pgsql.c:84
void ast_category_append(struct ast_config *config, struct ast_category *category)
Appends a category to a config.
Definition: extconf.c:2833
void ast_variable_append(struct ast_category *category, struct ast_variable *variable)
Definition: extconf.c:1177
#define ast_variable_new(name, value, filename)
struct ast_config * ast_config_internal_load(const char *configfile, struct ast_config *cfg, struct ast_flags flags, const char *suggested_incl_file, const char *who_asked)
Definition: main/config.c:3279
#define ast_category_new_dynamic(name)
Create a category that is not backed by a file.
@ CONFIG_FLAG_FILEUNCHANGED
#define ast_debug(level,...)
Log a DEBUG message.
#define LOG_WARNING
#define RES_CONFIG_MYSQL_CONF
static struct mysql_conn * find_database(const char *database, int for_write)
static int mysql_reconnect(struct mysql_conn *conn)
#define release_database(a)
static struct ast_threadstorage sql_buf
#define NULL
Definition: resample.c:96
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:761
int ast_str_set(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Set a dynamic string using variable arguments.
Definition: strings.h:1113
size_t ast_str_strlen(const struct ast_str *buf)
Returns the current length of the string stored within buf.
Definition: strings.h:730
struct ast_str * ast_str_thread_get(struct ast_threadstorage *ts, size_t init_len)
Retrieve a thread locally stored dynamic string.
Definition: strings.h:909
Support for dynamic strings.
Definition: strings.h:623
Structure for variables, used for configurations and for channel variables.
#define ast_clear_flag(p, flag)
Definition: utils.h:77

References ast_category_append(), ast_category_new_dynamic, ast_clear_flag, ast_config_internal_load(), ast_debug, ast_log, ast_str_buffer(), ast_str_set(), ast_str_strlen(), ast_str_thread_get(), ast_variable_append(), ast_variable_new, CONFIG_FLAG_FILEUNCHANGED, make_ari_stubs::file, find_database(), last, LOG_WARNING, mysql_reconnect(), NULL, release_database, RES_CONFIG_MYSQL_CONF, result, sql_buf, and table.

◆ decode_chunk()

static char * decode_chunk ( char *  chunk)
static

Definition at line 293 of file res_config_mysql.c.

294{
295 char *orig = 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);
300 }
301 }
302 return orig;
303}

Referenced by realtime_multi_mysql(), and realtime_mysql().

◆ destroy_mysql()

static int destroy_mysql ( const char *  database,
const char *  table,
const char *  keyfield,
const char *  lookup,
const struct ast_variable rt_fields 
)
static

Definition at line 810 of file res_config_mysql.c.

811{
812 struct mysql_conn *dbh;
813 uint64_t numrows;
814 struct ast_str *sql = ast_str_thread_get(&sql_buf, 16);
816 const struct ast_variable *field;
817
818 if (!(dbh = find_database(database, 1))) {
819 ast_log(LOG_WARNING, "MySQL RealTime: Invalid database specified: '%s' (check res_mysql.conf)\n", database);
820 return -1;
821 }
822
823 if (!table) {
824 ast_log(LOG_WARNING, "MySQL RealTime: No table specified.\n");
825 release_database(dbh);
826 return -1;
827 }
828
829 /* Get the first parameter and first value in our list of passed paramater/value pairs */
830 /* newparam = va_arg(ap, const char *);
831 newval = va_arg(ap, const char *);*/
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);
835 return -1;
836 }
837
838 /* Must connect to the server before anything else, as the escape function requires the mysql handle. */
839 if (!mysql_reconnect(dbh)) {
840 release_database(dbh);
841 return -1;
842 }
843
844 /* Create the first part of the query using the first parameter/value pairs we just extracted
845 If there is only 1 set, then we have our query. Otherwise, loop thru the list and concat */
846 ESCAPE_STRING(buf, lookup);
847 ast_str_set(&sql, 0, "DELETE FROM %s WHERE `%s` = '%s'", table, keyfield, ast_str_buffer(buf));
848 for (field = rt_fields; field; field = field->next) {
849 ESCAPE_STRING(buf, field->value);
850 ast_str_append(&sql, 0, " AND `%s` = '%s'", field->name, ast_str_buffer(buf));
851 }
852
853 ast_debug(1, "MySQL RealTime: Delete SQL: %s\n", ast_str_buffer(sql));
854
855 /* Execution. */
856 if (mysql_real_query(&dbh->handle, ast_str_buffer(sql), ast_str_strlen(sql))) {
857 ast_log(LOG_WARNING, "MySQL RealTime: Failed to delete from database: %s\n", mysql_error(&dbh->handle));
858 release_database(dbh);
859 return -1;
860 }
861
862 numrows = mysql_affected_rows(&dbh->handle);
863 release_database(dbh);
864
865 ast_debug(1, "MySQL RealTime: Deleted %" PRIu64 " rows on table: %s\n", numrows, table);
866
867 /* From http://dev.mysql.com/doc/mysql/en/mysql-affected-rows.html
868 * An integer greater than zero indicates the number of rows affected
869 * Zero indicates that no records were updated
870 * -1 indicates that the query returned an error (although, if the query failed, it should have been caught above.)
871 */
872
873 return (int)numrows;
874}
char buf[BUFSIZE]
Definition: eagi_proxy.c:66
#define ESCAPE_STRING(buf, var)
static struct ast_threadstorage scratch_buf
int ast_str_append(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Append to a thread local dynamic string.
Definition: strings.h:1139
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition: strings.h:65
struct ast_variable * next

References ast_debug, ast_log, ast_str_append(), ast_str_buffer(), ast_str_set(), ast_str_strlen(), ast_str_thread_get(), ast_strlen_zero(), buf, ESCAPE_STRING, find_database(), LOG_WARNING, mysql_reconnect(), ast_variable::name, ast_variable::next, release_database, scratch_buf, sql_buf, table, and ast_variable::value.

◆ destroy_table()

static void destroy_table ( struct tables table)
static

Definition at line 164 of file res_config_mysql.c.

165{
166 struct columns *column;
167 ast_mutex_lock(&table->lock);
168 while ((column = AST_LIST_REMOVE_HEAD(&table->columns, list))) {
169 ast_free(column);
170 }
171 ast_mutex_unlock(&table->lock);
172 ast_mutex_destroy(&table->lock);
174}
#define ast_free(a)
Definition: astmm.h:180
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
Definition: linkedlists.h:833
#define ast_mutex_unlock(a)
Definition: lock.h:190
#define ast_mutex_destroy(a)
Definition: lock.h:188
#define ast_mutex_lock(a)
Definition: lock.h:189
struct columns::@4 list

References ast_free, AST_LIST_REMOVE_HEAD, ast_mutex_destroy, ast_mutex_lock, ast_mutex_unlock, columns::list, and table.

Referenced by find_table(), unload_module(), and unload_mysql().

◆ find_column()

static struct columns * find_column ( struct tables table,
const char *  colname 
)
static

Definition at line 280 of file res_config_mysql.c.

281{
282 struct columns *column;
283
284 AST_LIST_TRAVERSE(&table->columns, column, list) {
285 if (strcmp(column->name, colname) == 0) {
286 break;
287 }
288 }
289
290 return column;
291}
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
Definition: linkedlists.h:491

References AST_LIST_TRAVERSE, columns::list, columns::name, and table.

Referenced by update2_mysql(), and update_mysql().

◆ find_database()

static struct mysql_conn * find_database ( const char *  database,
int  for_write 
)
static

Definition at line 132 of file res_config_mysql.c.

133{
134 char *whichdb;
135 const char *ptr;
136 struct mysql_conn *cur;
137
138 if ((ptr = strchr(database, '/'))) {
139 /* Multiple databases encoded within string */
140 if (for_write) {
141 whichdb = ast_strdupa(ptr + 1);
142 } else {
143 whichdb = ast_alloca(ptr - database + 1);
144 strncpy(whichdb, database, ptr - database);
145 whichdb[ptr - database] = '\0';
146 }
147 } else {
148 whichdb = ast_strdupa(database);
149 }
150
152 AST_RWLIST_TRAVERSE(&databases, cur, list) {
153 if (!strcmp(cur->unique_name, whichdb)) {
154 ast_mutex_lock(&cur->lock);
155 break;
156 }
157 }
159 return cur;
160}
#define ast_alloca(size)
call __builtin_alloca to ensure we get gcc builtin semantics
Definition: astmm.h:288
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:298
#define AST_RWLIST_RDLOCK(head)
Read locks a list.
Definition: linkedlists.h:78
#define AST_RWLIST_UNLOCK(head)
Attempts to unlock a read/write based list.
Definition: linkedlists.h:151
#define AST_RWLIST_TRAVERSE
Definition: linkedlists.h:494

References ast_alloca, ast_mutex_lock, AST_RWLIST_RDLOCK, AST_RWLIST_TRAVERSE, AST_RWLIST_UNLOCK, and ast_strdupa.

Referenced by config_mysql(), destroy_mysql(), find_table(), realtime_multi_mysql(), realtime_mysql(), store_mysql(), update2_mysql(), and update_mysql().

◆ find_table()

static struct tables * find_table ( const char *  database,
const char *  tablename 
)
static

Definition at line 176 of file res_config_mysql.c.

177{
178 struct columns *column;
179 struct tables *table;
180 struct ast_str *sql = ast_str_thread_get(&find_buf, 30);
181 char *fname, *ftype, *flen, *fdflt, *fnull;
182 struct mysql_conn *dbh;
183 MYSQL_RES *result;
184 MYSQL_ROW row;
185
186 if (!(dbh = find_database(database, 1))) {
187 return NULL;
188 }
189
192 if (!strcasecmp(table->name, tablename)) {
193 ast_mutex_lock(&table->lock);
195 release_database(dbh);
196 return table;
197 }
198 }
199
200 /* Not found, scan the table */
201 ast_str_set(&sql, 0, "DESC %s", tablename);
202
203 if (!mysql_reconnect(dbh)) {
204 release_database(dbh);
206 return NULL;
207 }
208
209 if (mysql_real_query(&dbh->handle, ast_str_buffer(sql), ast_str_strlen(sql))) {
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);
213 return NULL;
214 }
215
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);
220 return NULL;
221 }
222 strcpy(table->name, tablename); /* SAFE */
223 table->database = dbh;
224 ast_mutex_init(&table->lock);
226
227 if ((result = mysql_store_result(&dbh->handle))) {
228 while ((row = mysql_fetch_row(result))) {
229 fname = row[0];
230 ftype = row[1];
231 fnull = row[2];
232 fdflt = row[4];
233 ast_verb(4, "Found column '%s' of type '%s'\n", fname, ftype);
234
235 if (fdflt == NULL) {
236 fdflt = "";
237 }
238
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);
242 release_database(dbh);
244 return NULL;
245 }
246
247 if ((flen = strchr(ftype, '('))) {
248 sscanf(flen, "(%30d)", &column->len);
249 } else {
250 /* Columns like dates, times, and timestamps don't have a length */
251 column->len = -1;
252 }
253
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);
261 AST_LIST_INSERT_TAIL(&table->columns, column, list);
262 }
263 mysql_free_result(result);
264 }
265
267 ast_mutex_lock(&table->lock);
269 release_database(dbh);
270 return table;
271}
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:202
#define LOG_ERROR
#define ast_verb(level,...)
#define AST_LIST_HEAD_INIT_NOLOCK(head)
Initializes a list head structure.
Definition: linkedlists.h:681
#define AST_LIST_INSERT_TAIL(head, elm, field)
Appends a list entry to the tail of a list.
Definition: linkedlists.h:731
#define AST_LIST_LOCK(head)
Locks a list.
Definition: linkedlists.h:40
#define AST_LIST_UNLOCK(head)
Attempts to unlock a list.
Definition: linkedlists.h:140
#define ast_mutex_init(pmutex)
Definition: lock.h:186
static struct ast_threadstorage find_buf
static void destroy_table(struct tables *table)

References ast_calloc, AST_LIST_HEAD_INIT_NOLOCK, AST_LIST_INSERT_TAIL, AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_log, ast_mutex_init, ast_mutex_lock, ast_str_buffer(), ast_str_set(), ast_str_strlen(), ast_str_thread_get(), ast_verb, destroy_table(), columns::dflt, find_buf, find_database(), columns::len, LOG_ERROR, mysql_reconnect(), columns::name, columns::null, NULL, release_database, result, table, and columns::type.

Referenced by handle_cli_realtime_mysql_cache(), require_mysql(), update2_mysql(), and update_mysql().

◆ handle_cli_realtime_mysql_cache()

static char * handle_cli_realtime_mysql_cache ( struct ast_cli_entry e,
int  cmd,
struct ast_cli_args a 
)
static

Definition at line 1394 of file res_config_mysql.c.

1395{
1396 struct tables *cur;
1397 int l, which;
1398 char *ret = NULL;
1399
1400 switch (cmd) {
1401 case CLI_INIT:
1402 e->command = "realtime mysql cache";
1403 e->usage =
1404 "Usage: realtime mysql cache [<database> <table>]\n"
1405 " Shows table cache for the MySQL RealTime driver\n";
1406 return NULL;
1407 case CLI_GENERATE:
1408 if (a->argc < 4 || a->argc > 5) {
1409 return NULL;
1410 }
1411 l = strlen(a->word);
1412 which = 0;
1413 if (a->argc == 5) {
1416 if (!strcasecmp(a->argv[3], cur->database->unique_name) && !strncasecmp(a->word, cur->name, l) && ++which > a->n) {
1417 ret = ast_strdup(cur->name);
1418 break;
1419 }
1420 }
1422 } else {
1423 struct mysql_conn *cur;
1425 AST_RWLIST_TRAVERSE(&databases, cur, list) {
1426 if (!strncasecmp(a->word, cur->unique_name, l) && ++which > a->n) {
1427 ret = ast_strdup(cur->unique_name);
1428 break;
1429 }
1430 }
1432 }
1433 return ret;
1434 }
1435
1436 if (a->argc == 3) {
1437 /* List of tables */
1439 AST_LIST_TRAVERSE(&mysql_tables, cur, list) {
1440 ast_cli(a->fd, "%20.20s %s\n", cur->database->unique_name, cur->name);
1441 }
1443 } else if (a->argc == 4) {
1444 int found = 0;
1445 /* List of tables */
1447 AST_LIST_TRAVERSE(&mysql_tables, cur, list) {
1448 if (!strcasecmp(cur->database->unique_name, a->argv[3])) {
1449 ast_cli(a->fd, "%s\n", cur->name);
1450 found = 1;
1451 }
1452 }
1454 if (!found) {
1455 ast_cli(a->fd, "No tables cached within %s database\n", a->argv[3]);
1456 }
1457 } else if (a->argc == 5) {
1458 /* List of columns */
1459 if ((cur = find_table(a->argv[3], a->argv[4]))) {
1460 struct columns *col;
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");
1463 AST_LIST_TRAVERSE(&cur->columns, col, list) {
1464 ast_cli(a->fd, "%-20.20s %-20.20s %3d\n", col->name, col->type, col->len);
1465 }
1466 release_table(cur);
1467 } else {
1468 ast_cli(a->fd, "No such table '%s'\n", a->argv[3]);
1469 }
1470 }
1471 return CLI_SUCCESS;
1472}
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:241
#define CLI_SUCCESS
Definition: cli.h:44
void ast_cli(int fd, const char *fmt,...)
Definition: clicompat.c:6
@ CLI_INIT
Definition: cli.h:152
@ CLI_GENERATE
Definition: cli.h:153
static struct tables * find_table(const char *database, const char *tablename)
static void release_table(struct tables *table)
char * command
Definition: cli.h:186
const char * usage
Definition: cli.h:177
char name[0]
struct mysql_conn * database
struct tables::@5 list
static struct test_val a

References a, ast_cli(), AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, AST_RWLIST_RDLOCK, AST_RWLIST_TRAVERSE, AST_RWLIST_UNLOCK, ast_strdup, CLI_GENERATE, CLI_INIT, CLI_SUCCESS, tables::columns, ast_cli_entry::command, tables::database, find_table(), columns::len, columns::list, tables::list, columns::name, tables::name, NULL, release_table(), columns::type, and ast_cli_entry::usage.

◆ handle_cli_realtime_mysql_status()

static char * handle_cli_realtime_mysql_status ( struct ast_cli_entry e,
int  cmd,
struct ast_cli_args a 
)
static

Definition at line 1474 of file res_config_mysql.c.

1475{
1476 char status[256], status2[100] = "", type[20];
1477 char *ret = NULL;
1478 int ctime = 0, found = 0;
1479 struct mysql_conn *cur;
1480 int l = 0, which = 0;
1481
1482 switch (cmd) {
1483 case CLI_INIT:
1484 e->command = "realtime mysql status";
1485 e->usage =
1486 "Usage: realtime mysql status [<database>]\n"
1487 " Shows connection information for the MySQL RealTime driver\n";
1488 return NULL;
1489 case CLI_GENERATE:
1490 if (a->argc == 4) {
1492 AST_RWLIST_TRAVERSE(&databases, cur, list) {
1493 if (!strncasecmp(a->word, cur->unique_name, l) && ++which > a->n) {
1494 ret = ast_strdup(cur->unique_name);
1495 break;
1496 }
1497 }
1499 }
1500 return ret;
1501 }
1502
1503 if (a->argc != 3)
1504 return CLI_SHOWUSAGE;
1505
1507 AST_RWLIST_TRAVERSE(&databases, cur, list) {
1508 if (a->argc == 3 || (a->argc == 4 && !strcasecmp(a->argv[3], cur->unique_name))) {
1509 found = 1;
1510
1511 if (mysql_reconnect(cur)) {
1512 snprintf(type, sizeof(type), "connected to");
1513 ctime = time(NULL) - cur->connect_time;
1514 } else {
1515 snprintf(type, sizeof(type), "configured for");
1516 ctime = -1;
1517 }
1518
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);
1521 } else {
1522 snprintf(status, sizeof(status), "%s %s %s on socket file %s", cur->unique_name, type, cur->name, cur->sock);
1523 }
1524
1525 if (!ast_strlen_zero(cur->user)) {
1526 snprintf(status2, sizeof(status2), " with username %s", cur->user);
1527 } else {
1528 status2[0] = '\0';
1529 }
1530
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);
1543 } else {
1544 ast_cli(a->fd, "%s%s.\n", status, status2);
1545 }
1546 }
1547 }
1549
1550 if (!found) {
1551 ast_cli(a->fd, "No connections configured.\n");
1552 }
1553 return CLI_SUCCESS;
1554}
jack_status_t status
Definition: app_jack.c:146
static const char type[]
Definition: chan_ooh323.c:109
#define CLI_SHOWUSAGE
Definition: cli.h:45

References a, ast_cli(), AST_RWLIST_RDLOCK, AST_RWLIST_TRAVERSE, AST_RWLIST_UNLOCK, ast_strdup, ast_strlen_zero(), CLI_GENERATE, CLI_INIT, CLI_SHOWUSAGE, CLI_SUCCESS, ast_cli_entry::command, mysql_reconnect(), NULL, status, type, and ast_cli_entry::usage.

◆ load_module()

static int load_module ( void  )
static

Definition at line 1149 of file res_config_mysql.c.

1150{
1151 parse_config(0);
1152
1154 ast_verb(2, "MySQL RealTime driver loaded.\n");
1156 return 0;
1157}
#define ast_cli_register_multiple(e, len)
Register multiple commands.
Definition: cli.h:265
int ast_config_engine_register(struct ast_config_engine *newconfig)
Register config engine.
Definition: main/config.c:3157
static int parse_config(int reload)
static struct ast_config_engine mysql_engine
static struct ast_cli_entry cli_realtime_mysql_status[]
descriptor for a cli entry.
Definition: cli.h:171

References ast_cli_register_multiple, ast_config_engine_register(), ast_verb, cli_realtime_mysql_status, mysql_engine, and parse_config().

◆ load_mysql_config()

static int load_mysql_config ( struct ast_config config,
const char *  category,
struct mysql_conn conn 
)
static

Definition at line 1242 of file res_config_mysql.c.

1243{
1244 const char *s;
1245
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");
1248 s = "asterisk";
1249 }
1250 ast_copy_string(conn->user, s, sizeof(conn->user));
1251
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");
1254 s = "asterisk";
1255 }
1256 ast_copy_string(conn->pass, s, sizeof(conn->pass));
1257
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");
1260 s = "";
1261 }
1262 ast_copy_string(conn->host, s, sizeof(conn->host));
1263
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");
1266 s = "asterisk";
1267 }
1268 ast_copy_string(conn->name, s, sizeof(conn->name));
1269
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");
1272 conn->port = 3306;
1273 } else
1274 conn->port = atoi(s);
1275
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" };
1279 struct stat st;
1280 int i;
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]);
1284 ast_copy_string(conn->sock, paths[i], sizeof(conn->sock));
1285 }
1286 }
1287 if (i == 3) {
1288 ast_log(LOG_WARNING, "MySQL RealTime: No database socket found (and unable to detect a suitable path).\n");
1289 return 0;
1290 }
1291 }
1292 } else
1293 ast_copy_string(conn->sock, s, sizeof(conn->sock));
1294
1295 if ((s = ast_variable_retrieve(config, category, "dbcharset"))) {
1296 ast_copy_string(conn->charset, s, sizeof(conn->charset));
1297 }
1298
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;
1308 } else {
1309 ast_log(LOG_WARNING, "MySQL realtime: unrecognized requirements setting '%s', using 'warn'\n", s);
1310 conn->requirements = RQ_WARN;
1311 }
1312
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);
1316 } else
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);
1323
1324 return 1;
1325}
static PGconn * conn
Definition: cdr_pgsql.c:87
static const char config[]
Definition: chan_ooh323.c:111
const char * ast_variable_retrieve(struct ast_config *config, const char *category, const char *variable)
Definition: main/config.c:783
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:425

References ast_copy_string(), ast_debug, ast_log, ast_strlen_zero(), ast_variable_retrieve(), config, conn, LOG_WARNING, RQ_CREATECHAR, RQ_CREATECLOSE, and RQ_WARN.

Referenced by parse_config().

◆ mysql_reconnect()

static int mysql_reconnect ( struct mysql_conn conn)
static

Definition at line 1327 of file res_config_mysql.c.

1328{
1329#ifdef MYSQL_OPT_RECONNECT
1330 my_bool trueval = 1;
1331#endif
1332
1333 /* mutex lock should have been locked before calling this function. */
1334
1335reconnect_tryagain:
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;
1340 return 0;
1341 }
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);
1349 }
1350
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
1353 /* The default is no longer to automatically reconnect on failure,
1354 * (as of 5.0.3) so we have to set that option here. */
1355 mysql_options(&conn->handle, MYSQL_OPT_RECONNECT, &trueval);
1356#endif
1357 ast_debug(1, "MySQL RealTime: Successfully connected to database.\n");
1358 conn->connected = 1;
1359 conn->connect_time = time(NULL);
1360 return 1;
1361 } else {
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;
1366 return 0;
1367 }
1368 } else {
1369 /* MySQL likes to return an error, even if it reconnects successfully.
1370 * So the postman pings twice. */
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;
1377 }
1378
1379 if (!conn->connected) {
1380 conn->connected = 1;
1381 conn->connect_time = time(NULL);
1382 }
1383
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));
1386 return 0;
1387 }
1388
1389 ast_debug(1, "MySQL RealTime: Connection okay.\n");
1390 return 1;
1391 }
1392}

References ast_debug, ast_log, ast_strlen_zero(), conn, LOG_ERROR, LOG_WARNING, and NULL.

Referenced by config_mysql(), destroy_mysql(), find_table(), handle_cli_realtime_mysql_status(), realtime_multi_mysql(), realtime_mysql(), store_mysql(), update2_mysql(), and update_mysql().

◆ parse_config()

static int parse_config ( int  reload)
static

Definition at line 1193 of file res_config_mysql.c.

1194{
1195 struct ast_config *config = NULL;
1196 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
1197 const char *catg;
1198 struct mysql_conn *cur;
1199
1201 /* Support old config file name */
1203 }
1204
1206 return 0;
1207 } else if (config == CONFIG_STATUS_FILEUNCHANGED) {
1208 return 0;
1209 } else if (config == CONFIG_STATUS_FILEINVALID) {
1210 ast_log(LOG_ERROR, "Not %sloading " RES_CONFIG_MYSQL_CONF "\n", reload ? "re" : "");
1211 }
1212
1214 for (catg = ast_category_browse(config, NULL); catg; catg = ast_category_browse(config, catg)) {
1215 /* Does this category already exist? */
1216 AST_RWLIST_TRAVERSE(&databases, cur, list) {
1217 if (!strcmp(cur->unique_name, catg)) {
1218 break;
1219 }
1220 }
1221
1222 if (!cur) {
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);
1225 continue;
1226 }
1227
1228 strcpy(cur->unique_name, catg); /* SAFE */
1229 ast_mutex_init(&cur->lock);
1230 AST_RWLIST_INSERT_TAIL(&databases, cur, list);
1231 }
1232
1233 load_mysql_config(config, catg, cur);
1234 }
1236
1238
1239 return 0;
1240}
#define ast_config_load(filename, flags)
Load a config file.
char * ast_category_browse(struct ast_config *config, const char *prev_name)
Browse categories.
Definition: extconf.c:3326
#define CONFIG_STATUS_FILEMISSING
#define CONFIG_STATUS_FILEUNCHANGED
#define CONFIG_STATUS_FILEINVALID
void ast_config_destroy(struct ast_config *cfg)
Destroys a config.
Definition: extconf.c:1289
#define AST_RWLIST_WRLOCK(head)
Write locks a list.
Definition: linkedlists.h:52
#define AST_RWLIST_INSERT_TAIL
Definition: linkedlists.h:741
static int load_mysql_config(struct ast_config *config, const char *category, struct mysql_conn *conn)
#define RES_CONFIG_MYSQL_CONF_OLD
static int reload(void)
Structure used to handle boolean flags.
Definition: utils.h:199

References ast_calloc, ast_category_browse(), ast_config_destroy(), ast_config_load, ast_log, ast_mutex_init, AST_RWLIST_INSERT_TAIL, AST_RWLIST_TRAVERSE, AST_RWLIST_UNLOCK, AST_RWLIST_WRLOCK, config, CONFIG_FLAG_FILEUNCHANGED, CONFIG_STATUS_FILEINVALID, CONFIG_STATUS_FILEMISSING, CONFIG_STATUS_FILEUNCHANGED, load_mysql_config(), LOG_ERROR, LOG_WARNING, NULL, reload(), RES_CONFIG_MYSQL_CONF, and RES_CONFIG_MYSQL_CONF_OLD.

Referenced by load_module(), and reload().

◆ realtime_multi_mysql()

static struct ast_config * realtime_multi_mysql ( const char *  database,
const char *  table,
const struct ast_variable rt_fields 
)
static

Definition at line 420 of file res_config_mysql.c.

421{
422 struct mysql_conn *dbh;
423 MYSQL_RES *result;
424 MYSQL_ROW row;
425 MYSQL_FIELD *fields;
426 int numFields, i;
427 struct ast_str *sql = ast_str_thread_get(&sql_buf, 16);
429 const char *initfield = NULL;
430 char *stringp;
431 char *chunk;
432 char *op;
433 char *escape = "";
434 const struct ast_variable *field = rt_fields;
435 struct ast_variable *var = NULL;
436 struct ast_config *cfg = NULL;
437 struct ast_category *cat = NULL;
438
439 if (!(dbh = find_database(database, 0))) {
440 ast_log(LOG_WARNING, "MySQL RealTime: Invalid database specified: '%s' (check res_mysql.conf)\n", database);
441 return NULL;
442 }
443
444 if (!table) {
445 ast_log(LOG_WARNING, "MySQL RealTime: No table specified.\n");
446 release_database(dbh);
447 return NULL;
448 }
449
450 if (!(cfg = ast_config_new())) {
451 /* If I can't alloc memory at this point, why bother doing anything else? */
452 ast_log(LOG_WARNING, "Out of memory!\n");
453 release_database(dbh);
454 return NULL;
455 }
456
457 /* Get the first parameter and first value in our list of passed paramater/value pairs */
458 if (!field) {
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);
462 return NULL;
463 }
464
465 initfield = ast_strdupa(field->name);
466 if ((op = strchr(initfield, ' '))) {
467 *op = '\0';
468 }
469
470 /* Must connect to the server before anything else, as the escape function requires the mysql handle. */
471 if (!mysql_reconnect(dbh)) {
472 release_database(dbh);
474 return NULL;
475 }
476
477 /* Create the first part of the query using the first parameter/value pairs we just extracted
478 If there is only 1 set, then we have our query. Otherwise, loop thru the list and concat */
479
480 if (!strchr(field->name, ' ')) {
481 op = " =";
482 } else {
483 op = "";
484 if (IS_SQL_LIKE_CLAUSE(field->name)) {
485 escape = ESCAPE_CLAUSE;
486 }
487 }
488
489 ESCAPE_STRING(buf, field->value);
490 ast_str_set(&sql, 0, "SELECT * FROM %s WHERE %s%s '%s'%s", table, field->name, op, ast_str_buffer(buf), escape);
491 while ((field = field->next)) {
492 escape = "";
493 if (!strchr(field->name, ' ')) {
494 op = " =";
495 } else {
496 op = "";
497 if (IS_SQL_LIKE_CLAUSE(field->name)) {
498 escape = ESCAPE_CLAUSE;
499 }
500 }
501 ESCAPE_STRING(buf, field->value);
502 ast_str_append(&sql, 0, " AND %s%s '%s'%s", field->name, op, ast_str_buffer(buf), escape);
503 }
504
505 if (initfield) {
506 ast_str_append(&sql, 0, " ORDER BY %s", initfield);
507 }
508
509 ast_debug(1, "MySQL RealTime: Retrieve SQL: %s\n", ast_str_buffer(sql));
510
511 /* Execution. */
512 if (mysql_real_query(&dbh->handle, ast_str_buffer(sql), ast_str_strlen(sql))) {
513 ast_log(LOG_WARNING, "MySQL RealTime: Failed to query database: %s\n", mysql_error(&dbh->handle));
514 release_database(dbh);
516 return NULL;
517 }
518
519 if ((result = mysql_store_result(&dbh->handle))) {
520 numFields = mysql_num_fields(result);
521 fields = mysql_fetch_fields(result);
522
523 while ((row = mysql_fetch_row(result))) {
524 var = NULL;
526 if (!cat) {
527 continue;
528 }
529 for (i = 0; i < numFields; i++) {
530 if (ast_strlen_zero(row[i]))
531 continue;
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);
536 }
537 var = ast_variable_new(fields[i].name, chunk, "");
539 }
540 }
541 }
542 ast_category_append(cfg, cat);
543 }
544 } else {
545 ast_debug(1, "MySQL RealTime: Could not find any rows in table %s.\n", table);
546 }
547
548 release_database(dbh);
549 mysql_free_result(result);
550
551 return cfg;
552}
#define var
Definition: ast_expr2f.c:605
static const char name[]
Definition: format_mp3.c:68
char * strsep(char **str, const char *delims)
void ast_category_rename(struct ast_category *cat, const char *name)
Definition: main/config.c:1451
struct ast_config * ast_config_new(void)
Create a new base configuration structure.
Definition: extconf.c:3274
#define ast_category_new_anonymous()
Create a nameless category that is not backed by a file.
static char * ESCAPE_CLAUSE
#define IS_SQL_LIKE_CLAUSE(x)
static char * decode_chunk(char *chunk)
char * ast_strip(char *s)
Strip leading/trailing whitespace from a string.
Definition: strings.h:223

References ast_category_append(), ast_category_new_anonymous, ast_category_rename(), ast_config_destroy(), ast_config_new(), ast_debug, ast_log, ast_str_append(), ast_str_buffer(), ast_str_set(), ast_str_strlen(), ast_str_thread_get(), ast_strdupa, ast_strip(), ast_strlen_zero(), ast_variable_append(), ast_variable_new, buf, decode_chunk(), ESCAPE_CLAUSE, ESCAPE_STRING, find_database(), IS_SQL_LIKE_CLAUSE, LOG_WARNING, mysql_reconnect(), name, ast_variable::name, ast_variable::next, NULL, release_database, result, scratch_buf, sql_buf, strsep(), table, ast_variable::value, and var.

◆ realtime_mysql()

static struct ast_variable * realtime_mysql ( const char *  database,
const char *  table,
const struct ast_variable rt_fields 
)
static

Definition at line 310 of file res_config_mysql.c.

311{
312 struct mysql_conn *dbh;
313 MYSQL_RES *result;
314 MYSQL_ROW row;
315 MYSQL_FIELD *fields;
316 int numFields, i;
317 struct ast_str *sql = ast_str_thread_get(&sql_buf, 16);
319 char *stringp;
320 char *chunk;
321 char *op;
322 char *escape = "";
323 const struct ast_variable *field = rt_fields;
324 struct ast_variable *var=NULL, *prev=NULL;
325
326 if (!(dbh = find_database(database, 0))) {
327 ast_log(LOG_WARNING, "MySQL RealTime: Invalid database specified: %s (check res_mysql.conf)\n", database);
328 return NULL;
329 }
330
331 if (!table) {
332 ast_log(LOG_WARNING, "MySQL RealTime: No table specified.\n");
333 release_database(dbh);
334 return NULL;
335 }
336
337 /* Get the first parameter and first value in our list of passed paramater/value pairs */
338 if (!field) {
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);
341 return NULL;
342 }
343
344 /* Must connect to the server before anything else, as the escape function requires the mysql handle. */
345 if (!mysql_reconnect(dbh)) {
346 release_database(dbh);
347 return NULL;
348 }
349
350 /* Create the first part of the query using the first parameter/value pairs we just extracted
351 If there is only 1 set, then we have our query. Otherwise, loop thru the list and concat */
352
353 if (!strchr(field->name, ' ')) {
354 op = " =";
355 } else {
356 op = "";
357 if (IS_SQL_LIKE_CLAUSE(field->name)) {
358 escape = ESCAPE_CLAUSE;
359 }
360 }
361
362 ESCAPE_STRING(buf, field->value);
363 ast_str_set(&sql, 0, "SELECT * FROM %s WHERE %s%s '%s'%s", table, field->name, op, ast_str_buffer(buf), escape);
364 while ((field = field->next)) {
365 escape = "";
366 if (!strchr(field->name, ' ')) {
367 op = " =";
368 } else {
369 op = "";
370 if (IS_SQL_LIKE_CLAUSE(field->name)) {
371 escape = ESCAPE_CLAUSE;
372 }
373 }
374 ESCAPE_STRING(buf, field->value);
375 ast_str_append(&sql, 0, " AND %s%s '%s'%s", field->name, op, ast_str_buffer(buf), escape);
376 }
377
378 ast_debug(1, "MySQL RealTime: Retrieve SQL: %s\n", ast_str_buffer(sql));
379
380 /* Execution. */
381 if (mysql_real_query(&dbh->handle, ast_str_buffer(sql), ast_str_strlen(sql))) {
382 ast_log(LOG_WARNING, "MySQL RealTime: Failed to query database: %s\n", mysql_error(&dbh->handle));
383 release_database(dbh);
384 return NULL;
385 }
386
387 if ((result = mysql_store_result(&dbh->handle))) {
388 numFields = mysql_num_fields(result);
389 fields = mysql_fetch_fields(result);
390
391 while ((row = mysql_fetch_row(result))) {
392 for (i = 0; i < numFields; i++) {
393 /* Encode NULL values separately from blank values, for the Realtime API */
394 if (row[i] == NULL) {
395 row[i] = "";
396 } else if (ast_strlen_zero(row[i])) {
397 row[i] = " ";
398 }
399 for (stringp = row[i], chunk = strsep(&stringp, ";"); chunk; chunk = strsep(&stringp, ";")) {
400 if (prev) {
401 if ((prev->next = ast_variable_new(fields[i].name, decode_chunk(chunk), ""))) {
402 prev = prev->next;
403 }
404 } else {
405 prev = var = ast_variable_new(fields[i].name, decode_chunk(chunk), "");
406 }
407 }
408 }
409 }
410 } else {
411 ast_debug(1, "MySQL RealTime: Could not find any rows in table %s.\n", table);
412 }
413
414 release_database(dbh);
415 mysql_free_result(result);
416
417 return var;
418}

References ast_debug, ast_log, ast_str_append(), ast_str_buffer(), ast_str_set(), ast_str_strlen(), ast_str_thread_get(), ast_strlen_zero(), ast_variable_new, buf, decode_chunk(), ESCAPE_CLAUSE, ESCAPE_STRING, find_database(), IS_SQL_LIKE_CLAUSE, LOG_WARNING, mysql_reconnect(), name, ast_variable::name, ast_variable::next, NULL, release_database, result, scratch_buf, sql_buf, strsep(), table, ast_variable::value, and var.

◆ release_table()

static void release_table ( struct tables table)
static

Definition at line 273 of file res_config_mysql.c.

274{
275 if (table) {
276 ast_mutex_unlock(&table->lock);
277 }
278}

References ast_mutex_unlock, and table.

Referenced by handle_cli_realtime_mysql_cache(), require_mysql(), update2_mysql(), and update_mysql().

◆ reload()

static int reload ( void  )
static

◆ require_mysql()

static int require_mysql ( const char *  database,
const char *  tablename,
va_list  ap 
)
static

Definition at line 973 of file res_config_mysql.c.

974{
975 struct columns *column;
976 struct tables *table = find_table(database, tablename);
977 char *elm;
978 int type;
979 int size;
980 int res = 0;
981
982 if (!table) {
983 ast_log(LOG_WARNING, "Table %s not found in database. This table should exist if you're using realtime.\n", tablename);
984 return -1;
985 }
986
987 while ((elm = va_arg(ap, char *))) {
988 type = va_arg(ap, require_type);
989 size = va_arg(ap, int);
990
991 AST_LIST_TRAVERSE(&table->columns, column, list) {
992 if (strcmp(column->name, elm) == 0) {
993 /* Char can hold anything, as long as it is large enough */
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);
997 res = -1;
998 }
999 } else if (strcasestr(column->type, "unsigned")) {
1000 if (!ast_rq_is_int(type)) {
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");
1005 res = -1;
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); \
1011 res = -1; \
1012 }
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); \
1018 res = -1; \
1019 }
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); \
1027 res = -1; \
1028 }
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); \
1037 res = -1; \
1038 }
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); \
1048 res = -1; \
1049 }
1050 }
1051 } else if (strcasestr(column->type, "int")) {
1052 if (!ast_rq_is_int(type)) {
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");
1058 res = -1;
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); \
1064 res = -1; \
1065 }
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); \
1071 res = -1; \
1072 }
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); \
1080 res = -1; \
1081 }
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); \
1090 res = -1; \
1091 }
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); \
1101 res = -1; \
1102 }
1103 }
1104 } else if (strncmp(column->type, "float", 5) == 0) {
1105 if (!ast_rq_is_int(type) && type != RQ_FLOAT) {
1106 ast_log(LOG_WARNING, "Realtime table %s@%s: Column %s cannot be a %s\n", tablename, database, column->name, column->type);
1107 res = -1;
1108 }
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);
1112 res = -1;
1113 }
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);
1117 res = -1;
1118 }
1119 } else { /* Other, possibly unsupported types? */
1120 ast_log(LOG_WARNING, "Possibly unsupported column type '%s' on column '%s'\n", column->type, column->name);
1121 res = -1;
1122 }
1123 break;
1124 }
1125 }
1126
1127 if (!column) {
1128 ast_log(LOG_WARNING, "Table %s requires a column '%s' of size '%d', but no such column exists.\n", tablename, elm, size);
1129 }
1130 }
1132
1133 return res;
1134}
char * strcasestr(const char *, const char *)
int ast_rq_is_int(require_type type)
Check if require type is an integer type.
require_type
Types used in ast_realtime_require_field.

References AST_LIST_TRAVERSE, ast_log, ast_rq_is_int(), tables::database, find_table(), columns::len, tables::list, LOG_WARNING, columns::name, release_table(), RQ_CHAR, RQ_DATE, RQ_DATETIME, RQ_FLOAT, RQ_INTEGER1, RQ_INTEGER2, RQ_INTEGER3, RQ_INTEGER4, RQ_INTEGER8, RQ_UINTEGER1, RQ_UINTEGER2, RQ_UINTEGER3, RQ_UINTEGER4, RQ_UINTEGER8, strcasestr(), table, type, and columns::type.

◆ store_mysql()

static int store_mysql ( const char *  database,
const char *  table,
const struct ast_variable rt_fields 
)
static

Definition at line 752 of file res_config_mysql.c.

753{
754 struct mysql_conn *dbh;
755 struct ast_str *sql = ast_str_thread_get(&sql_buf, 16);
756 struct ast_str *sql2 = ast_str_thread_get(&sql2_buf, 16);
758 const struct ast_variable *field = rt_fields;
759
760 if (!(dbh = find_database(database, 1))) {
761 ast_log(LOG_WARNING, "MySQL RealTime: Invalid database specified: '%s' (check res_mysql.conf)\n", database);
762 return -1;
763 }
764
765 if (!table) {
766 ast_log(LOG_WARNING, "MySQL RealTime: No table specified.\n");
767 release_database(dbh);
768 return -1;
769 }
770 /* Get the first parameter and first value in our list of passed paramater/value pairs */
771 if (!field) {
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);
774 return -1;
775 }
776 /* Must connect to the server before anything else, as the escape function requires the mysql handle. */
777 if (!mysql_reconnect(dbh)) {
778 release_database(dbh);
779 return -1;
780 }
781 /* Create the first part of the query using the first parameter/value pairs we just extracted
782 If there is only 1 set, then we have our query. Otherwise, loop thru the list and concat */
783 ESCAPE_STRING(buf, field->value);
784 ast_str_set(&sql, 0, "INSERT INTO %s (`%s`", table, field->name);
785 ast_str_set(&sql2, 0, ") VALUES ('%s'", ast_str_buffer(buf));
786
787 while ((field = field->next)) {
788 ESCAPE_STRING(buf, field->value);
789
790 ast_str_append(&sql, 0, ", `%s`", field->name);
791 ast_str_append(&sql2, 0, ", '%s'", ast_str_buffer(buf));
792 }
793 ast_str_append(&sql, 0, "%s)", ast_str_buffer(sql2));
794 ast_debug(1,"MySQL RealTime: Insert SQL: %s\n", ast_str_buffer(sql));
795
796 /* Execution. */
797 if (mysql_real_query(&dbh->handle, ast_str_buffer(sql), ast_str_strlen(sql))) {
798 ast_log(LOG_WARNING, "MySQL RealTime: Failed to insert into database: %s\n", mysql_error(&dbh->handle));
799 release_database(dbh);
800 return -1;
801 }
802
803 release_database(dbh);
804
805 ast_debug(1, "MySQL RealTime: row inserted on table: %s\n", table);
806
807 return 1;
808}
static struct ast_threadstorage sql2_buf

References ast_debug, ast_log, ast_str_append(), ast_str_buffer(), ast_str_set(), ast_str_strlen(), ast_str_thread_get(), buf, ESCAPE_STRING, find_database(), LOG_WARNING, mysql_reconnect(), ast_variable::name, ast_variable::next, release_database, scratch_buf, sql2_buf, sql_buf, table, and ast_variable::value.

◆ unload_module()

static int unload_module ( void  )
static

Definition at line 1159 of file res_config_mysql.c.

1160{
1161 struct mysql_conn *cur;
1162 struct tables *table;
1163
1166 ast_verb(2, "MySQL RealTime unloaded.\n");
1167
1169 while ((cur = AST_RWLIST_REMOVE_HEAD(&databases, list))) {
1170 mysql_close(&cur->handle);
1171 ast_mutex_destroy(&cur->lock);
1172 ast_free(cur);
1173 }
1175
1176 /* Destroy cached table info */
1180 }
1182
1183 return 0;
1184}
int ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
Unregister multiple commands.
Definition: clicompat.c:30
int ast_config_engine_deregister(struct ast_config_engine *del)
Deregister config engine.
Definition: main/config.c:3173
#define AST_RWLIST_REMOVE_HEAD
Definition: linkedlists.h:844

References ast_cli_unregister_multiple(), ast_config_engine_deregister(), ast_free, AST_LIST_LOCK, AST_LIST_REMOVE_HEAD, AST_LIST_UNLOCK, ast_mutex_destroy, AST_RWLIST_REMOVE_HEAD, AST_RWLIST_UNLOCK, AST_RWLIST_WRLOCK, ast_verb, cli_realtime_mysql_status, destroy_table(), tables::list, mysql_engine, and table.

◆ unload_mysql()

static int unload_mysql ( const char *  database,
const char *  tablename 
)
static

Definition at line 957 of file res_config_mysql.c.

958{
959 struct tables *cur;
962 if (strcmp(cur->name, tablename) == 0) {
964 destroy_table(cur);
965 break;
966 }
967 }
970 return cur ? 0 : -1;
971}
#define AST_LIST_TRAVERSE_SAFE_END
Closes a safe loop traversal block.
Definition: linkedlists.h:615
#define AST_LIST_TRAVERSE_SAFE_BEGIN(head, var, field)
Loops safely over (traverses) the entries in a list.
Definition: linkedlists.h:529
#define AST_LIST_REMOVE_CURRENT(field)
Removes the current entry from a list during a traversal.
Definition: linkedlists.h:557

References AST_LIST_LOCK, AST_LIST_REMOVE_CURRENT, AST_LIST_TRAVERSE_SAFE_BEGIN, AST_LIST_TRAVERSE_SAFE_END, AST_LIST_UNLOCK, destroy_table(), tables::list, and tables::name.

◆ update2_mysql()

static int update2_mysql ( const char *  database,
const char *  tablename,
const struct ast_variable lookup_fields,
const struct ast_variable update_fields 
)
static

Definition at line 655 of file res_config_mysql.c.

656{
657 struct mysql_conn *dbh;
658 uint64_t numrows;
659 int first;
660 const struct ast_variable *field;
661 struct ast_str *sql = ast_str_thread_get(&sql_buf, 100), *buf = ast_str_thread_get(&scratch_buf, 100);
662 struct ast_str *where = ast_str_thread_get(&sql2_buf, 100);
663 struct tables *table;
664 struct columns *column = NULL;
665
666 if (!tablename) {
667 ast_log(LOG_WARNING, "MySQL RealTime: No table specified.\n");
668 return -1;
669 }
670
671 if (!(dbh = find_database(database, 1))) {
672 ast_log(LOG_ERROR, "Invalid database specified: %s\n", database);
673 return -1;
674 }
675
676 if (!(table = find_table(database, tablename))) {
677 ast_log(LOG_ERROR, "Table '%s' does not exist!!\n", tablename);
678 release_database(dbh);
679 return -1;
680 }
681
682 if (!sql || !buf || !where) {
683 release_database(dbh);
685 return -1;
686 }
687
688 ast_str_set(&sql, 0, "UPDATE %s SET", tablename);
689 ast_str_set(&where, 0, "WHERE");
690
691 /* Must connect to the server before anything else, as the escape function requires the mysql handle. */
692 if (!mysql_reconnect(dbh)) {
694 release_database(dbh);
695 return -1;
696 }
697
698 first = 1;
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);
703 release_database(dbh);
704 return -1;
705 }
706 ESCAPE_STRING(buf, field->value);
707 ast_str_append(&where, 0, "%s `%s` = '%s'", first ? "" : " AND", field->name, ast_str_buffer(buf));
708 first = 0;
709 }
710
711 first = 1;
712 for (field = update_fields; field; field = field->next) {
713 /* If the column is not within the table, then skip it */
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);
716 continue;
717 }
718
719 ESCAPE_STRING(buf, field->value);
720 ast_str_append(&sql, 0, "%s `%s` = '%s'", first ? "" : ",", field->name, ast_str_buffer(buf));
721 first = 0;
722 }
723
725
726 ast_str_append(&sql, 0, " %s", ast_str_buffer(where));
727
728 ast_debug(1, "MySQL RealTime: Update SQL: %s\n", ast_str_buffer(sql));
729
730 /* Execution. */
731 if (mysql_real_query(&dbh->handle, ast_str_buffer(sql), ast_str_strlen(sql))) {
732 ast_log(LOG_WARNING, "MySQL RealTime: Failed to update database: %s\n", mysql_error(&dbh->handle));
734 release_database(dbh);
735 return -1;
736 }
737
738 numrows = mysql_affected_rows(&dbh->handle);
739 release_database(dbh);
740
741 ast_debug(1, "MySQL RealTime: Updated %" PRIu64 " rows on table: %s\n", numrows, tablename);
742
743 /* From http://dev.mysql.com/doc/mysql/en/mysql-affected-rows.html
744 * An integer greater than zero indicates the number of rows affected
745 * Zero indicates that no records were updated
746 * -1 indicates that the query returned an error (although, if the query failed, it should have been caught above.)
747 */
748
749 return (int)numrows;
750}
struct sla_ringing_trunk * first
Definition: app_sla.c:332
static struct columns * find_column(struct tables *table, const char *colname)

References ast_debug, ast_log, ast_str_append(), ast_str_buffer(), ast_str_set(), ast_str_strlen(), ast_str_thread_get(), buf, ESCAPE_STRING, find_column(), find_database(), find_table(), first, LOG_ERROR, LOG_WARNING, mysql_reconnect(), ast_variable::name, ast_variable::next, NULL, release_database, release_table(), scratch_buf, sql2_buf, sql_buf, table, and ast_variable::value.

◆ update_mysql()

static int update_mysql ( const char *  database,
const char *  tablename,
const char *  keyfield,
const char *  lookup,
const struct ast_variable rt_fields 
)
static

Definition at line 554 of file res_config_mysql.c.

555{
556 struct mysql_conn *dbh;
557 uint64_t numrows;
558 const struct ast_variable *field = rt_fields;
559 struct ast_str *sql = ast_str_thread_get(&sql_buf, 100), *buf = ast_str_thread_get(&scratch_buf, 100);
560 struct tables *table;
561 struct columns *column = NULL;
562
563 if (!(dbh = find_database(database, 1))) {
564 ast_log(LOG_WARNING, "MySQL RealTime: Invalid database specified: '%s' (check res_mysql.conf)\n", database);
565 return -1;
566 }
567
568 if (!tablename) {
569 ast_log(LOG_WARNING, "MySQL RealTime: No table specified.\n");
570 release_database(dbh);
571 return -1;
572 }
573
574 if (!(table = find_table(database, tablename))) {
575 ast_log(LOG_ERROR, "Table '%s' does not exist!!\n", tablename);
576 release_database(dbh);
577 return -1;
578 }
579
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);
583 release_database(dbh);
584 return -1;
585 }
586
587 /* Get the first parameter and first value in our list of passed paramater/value pairs */
588 if (!field) {
589 ast_log(LOG_WARNING, "MySQL RealTime: Realtime update requires at least 1 parameter and 1 value to update.\n");
591 release_database(dbh);
592 return -1;
593 }
594
595 /* Check that the column exists in the table */
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);
599 release_database(dbh);
600 return -1;
601 }
602
603 /* Must connect to the server before anything else, as the escape function requires the mysql handle. */
604 if (!mysql_reconnect(dbh)) {
606 release_database(dbh);
607 return -1;
608 }
609
610 /* Create the first part of the query using the first parameter/value pairs we just extracted
611 If there is only 1 set, then we have our query. Otherwise, loop thru the list and concat */
612
613 ESCAPE_STRING(buf, field->value);
614 ast_str_set(&sql, 0, "UPDATE %s SET `%s` = '%s'", tablename, field->name, ast_str_buffer(buf));
615
616 while ((field = field->next)) {
617 /* If the column is not within the table, then skip it */
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);
620 continue;
621 }
622
623 ESCAPE_STRING(buf, field->value);
624 ast_str_append(&sql, 0, ", `%s` = '%s'", field->name, ast_str_buffer(buf));
625 }
626
627 ESCAPE_STRING(buf, lookup);
628 ast_str_append(&sql, 0, " WHERE `%s` = '%s'", keyfield, ast_str_buffer(buf));
629
630 ast_debug(1, "MySQL RealTime: Update SQL: %s\n", ast_str_buffer(sql));
631
632 /* Execution. */
633 if (mysql_real_query(&dbh->handle, ast_str_buffer(sql), ast_str_strlen(sql))) {
634 ast_log(LOG_WARNING, "MySQL RealTime: Failed to update database: %s\n", mysql_error(&dbh->handle));
636 release_database(dbh);
637 return -1;
638 }
639
640 numrows = mysql_affected_rows(&dbh->handle);
642 release_database(dbh);
643
644 ast_debug(1, "MySQL RealTime: Updated %" PRIu64 " rows on table: %s\n", numrows, tablename);
645
646 /* From http://dev.mysql.com/doc/mysql/en/mysql-affected-rows.html
647 * An integer greater than zero indicates the number of rows affected
648 * Zero indicates that no records were updated
649 * -1 indicates that the query returned an error (although, if the query failed, it should have been caught above.)
650 */
651
652 return (int)numrows;
653}

References ast_debug, ast_log, ast_str_append(), ast_str_buffer(), ast_str_set(), ast_str_strlen(), ast_str_thread_get(), buf, ESCAPE_STRING, find_column(), find_database(), find_table(), LOG_ERROR, LOG_WARNING, mysql_reconnect(), ast_variable::name, ast_variable::next, NULL, release_database, release_table(), scratch_buf, sql_buf, table, and ast_variable::value.

Variable Documentation

◆ __mod_info

struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_LOAD_ORDER , .description = "MySQL RealTime Configuration Driver" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = AST_BUILDOPT_SUM, .support_level = AST_MODULE_SUPPORT_EXTENDED, .load = load_module, .unload = unload_module, .reload = reload, .load_pri = AST_MODPRI_REALTIME_DRIVER, .requires = "extconfig", }
static

Definition at line 1563 of file res_config_mysql.c.

◆ ast_module_info

const struct ast_module_info* ast_module_info = &__mod_info
static

Definition at line 1563 of file res_config_mysql.c.

◆ cli_realtime_mysql_status

struct ast_cli_entry cli_realtime_mysql_status[]
static
Initial value:
= {
{ .handler = handle_cli_realtime_mysql_status , .summary = "Shows connection information for the MySQL RealTime driver" ,},
{ .handler = handle_cli_realtime_mysql_cache , .summary = "Shows cached tables within the MySQL realtime driver" ,},
}
static char * handle_cli_realtime_mysql_cache(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
static char * handle_cli_realtime_mysql_status(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)

Definition at line 127 of file res_config_mysql.c.

Referenced by load_module(), and unload_module().

◆ databases

struct databases databases = { .first = NULL, .last = NULL, .lock = { PTHREAD_RWLOCK_INITIALIZER , NULL, {1, 0} } , }
static

◆ ESCAPE_CLAUSE

char* ESCAPE_CLAUSE = " ESCAPE '\\\\'"
static

Definition at line 308 of file res_config_mysql.c.

Referenced by realtime_multi_mysql(), and realtime_mysql().

◆ find_buf

struct ast_threadstorage find_buf = { .once = PTHREAD_ONCE_INIT , .key_init = __init_find_buf , .custom_init = NULL , }
static

Definition at line 74 of file res_config_mysql.c.

Referenced by find_table().

◆ modify2_buf

struct ast_threadstorage modify2_buf = { .once = PTHREAD_ONCE_INIT , .key_init = __init_modify2_buf , .custom_init = NULL , }
static

Definition at line 78 of file res_config_mysql.c.

◆ modify3_buf

struct ast_threadstorage modify3_buf = { .once = PTHREAD_ONCE_INIT , .key_init = __init_modify3_buf , .custom_init = NULL , }
static

Definition at line 79 of file res_config_mysql.c.

◆ modify_buf

struct ast_threadstorage modify_buf = { .once = PTHREAD_ONCE_INIT , .key_init = __init_modify_buf , .custom_init = NULL , }
static

Definition at line 77 of file res_config_mysql.c.

◆ mysql_engine

struct ast_config_engine mysql_engine
static

Definition at line 1136 of file res_config_mysql.c.

Referenced by load_module(), and unload_module().

◆ mysql_tables

struct mysql_tables mysql_tables = { .first = NULL, .last = NULL, .lock = { PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP , NULL, {1, 0} } , }
static

◆ scratch2_buf

struct ast_threadstorage scratch2_buf = { .once = PTHREAD_ONCE_INIT , .key_init = __init_scratch2_buf , .custom_init = NULL , }
static

Definition at line 76 of file res_config_mysql.c.

◆ scratch_buf

struct ast_threadstorage scratch_buf = { .once = PTHREAD_ONCE_INIT , .key_init = __init_scratch_buf , .custom_init = NULL , }
static

◆ sql2_buf

struct ast_threadstorage sql2_buf = { .once = PTHREAD_ONCE_INIT , .key_init = __init_sql2_buf , .custom_init = NULL , }
static

Definition at line 73 of file res_config_mysql.c.

Referenced by store_mysql(), and update2_mysql().

◆ sql_buf

struct ast_threadstorage sql_buf = { .once = PTHREAD_ONCE_INIT , .key_init = __init_sql_buf , .custom_init = NULL , }
static