mirror of
				https://github.com/asterisk/asterisk.git
				synced 2025-10-31 02:37:10 +00:00 
			
		
		
		
	Merge the realtime failover branch
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@278957 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
		
							
								
								
									
										1
									
								
								CHANGES
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								CHANGES
									
									
									
									
									
								
							| @@ -524,6 +524,7 @@ Miscellaneous | ||||
|    during device configuration. | ||||
|  * The UNISTIM channel driver (chan_unistim) has been updated to support devices that | ||||
|    have less than 3 lines on the LCD. | ||||
|  * Realtime now supports database failover.  See the sample extconfig.conf for details. | ||||
|  | ||||
| CLI Changes | ||||
| ----------- | ||||
|   | ||||
| @@ -9,7 +9,7 @@ | ||||
| ; | ||||
| ; Static configuration files: | ||||
| ; | ||||
| ; file.conf => driver,database[,table] | ||||
| ; file.conf => driver,database[,table[,priority]] | ||||
| ; | ||||
| ; maps a particular configuration file to the given | ||||
| ; database driver, database and table (or uses the | ||||
| @@ -40,14 +40,26 @@ | ||||
| ; database and table (or uses the name of | ||||
| ; the family if the table is not specified | ||||
| ; | ||||
| ;example => odbc,asterisk,alttable | ||||
| ;example => odbc,asterisk,alttable,1 | ||||
| ;example => mysql,asterisk,alttable,2 | ||||
| ;example2 => ldap,"dc=oxymium,dc=net",example2 | ||||
| ; | ||||
| ; Additionally, priorities are now supported for use as failover methods | ||||
| ; for retrieving realtime data.  If one connection fails to retrieve any | ||||
| ; information, the next sequential priority will be tried next.  This | ||||
| ; especially works well with ODBC connections, since res_odbc now caches | ||||
| ; when connection failures occur and prevents immediately retrying those | ||||
| ; connections until after a specified timeout.  Note:  priorities must | ||||
| ; start at 1 and be sequential (i.e. if you have only priorities 1, 2, | ||||
| ; and 4, then 4 will be ignored, because there is no 3). | ||||
| ; | ||||
| ; "odbc" is shown in the examples below, but is not the only valid realtime | ||||
| ; engine.  There is: | ||||
| ;    odbc ... res_config_odbc | ||||
| ;    sqlite ... res_config_sqlite | ||||
| ;    pgsql ... res_config_pgsql | ||||
| ;    curl ... res_config_curl | ||||
| ;    ldap ... res_config_ldap | ||||
| ; | ||||
| ;iaxusers => odbc,asterisk | ||||
| ;iaxpeers => odbc,asterisk | ||||
|   | ||||
| @@ -10,18 +10,69 @@ | ||||
|  | ||||
| ; All other sections are arbitrary names for database connections. | ||||
|  | ||||
| ; | ||||
| ; The context name is what will be used in other configuration files, such | ||||
| ; as extconfig.conf and func_odbc.conf, to reference this connection. | ||||
| [asterisk] | ||||
| ; | ||||
| ; Permit disabling sections without needing to comment them out. | ||||
| ; If not specified, it is assumed the section is enabled. | ||||
| enabled => no | ||||
| ; | ||||
| ; This value should match an entry in /etc/odbc.ini | ||||
| ; (or /usr/local/etc/odbc.ini, on FreeBSD and similar systems). | ||||
| dsn => asterisk | ||||
| ; | ||||
| ; Username for connecting to the database.  The default user is "root". | ||||
| ;username => myuser | ||||
| ; | ||||
| ; Password for authenticating the user to the database.  The default | ||||
| ; password is blank. | ||||
| ;password => mypass | ||||
| ; | ||||
| ; Build a connection at startup? | ||||
| pre-connect => yes | ||||
| ; | ||||
| ; What should we execute to ensure that our connection is still alive?  The | ||||
| ; statement should return a non-zero value in the first field of its first | ||||
| ; record.  The default is "select 1". | ||||
| ;sanitysql => select 1 | ||||
|  | ||||
| ; | ||||
| ; On some databases, the connection times out and a reconnection will be | ||||
| ; necessary.  This setting configures the amount of time a connection | ||||
| ; may sit idle (in seconds) before a reconnection will be attempted. | ||||
| ;idlecheck => 3600 | ||||
| ; | ||||
| ; Should we use a single connection for all queries?  Most databases will | ||||
| ; allow sharing the connection, though Sybase and MS SQL Server will not. | ||||
| ;share_connections => yes | ||||
| ; | ||||
| ; If we aren't sharing connections, what is the maximum number of connections | ||||
| ; that we should attempt? | ||||
| ;limit => 5 | ||||
| ; | ||||
| ; When the channel is destroyed, should any uncommitted open transactions | ||||
| ; automatically be committed? | ||||
| ;forcecommit => no | ||||
| ; | ||||
| ; How should we perceive data in other transactions within the database? | ||||
| ; Possible values are read_uncommitted, read_committed, repeatable_read, | ||||
| ; and serializable.  The default is read_committed. | ||||
| ;isolation => repeatable_read | ||||
| ; | ||||
| ; Is the backslash a native escape character?  The default is yes, but for | ||||
| ; MS SQL Server, the answer is no. | ||||
| ;backslash_is_escape => yes | ||||
| ; | ||||
| ; How long (in seconds) should we attempt to connect before considering the | ||||
| ; connection dead?  The default is 10 seconds, but you may wish to reduce it, | ||||
| ; to increase responsiveness. | ||||
| ;connect_timeout => 10 | ||||
| ; | ||||
| ; When a connection fails, how long (in seconds) should we cache that | ||||
| ; information before we attempt another connection?  This increases | ||||
| ; responsiveness, when a database resource is not working. | ||||
| ;negative_connection_cache => 300 | ||||
|  | ||||
| [mysql2] | ||||
| enabled => no | ||||
| @@ -29,11 +80,6 @@ dsn => MySQL-asterisk | ||||
| username => myuser | ||||
| password => mypass | ||||
| pre-connect => yes | ||||
| ; | ||||
| ; On some databases, the connection times out and a reconnection will be | ||||
| ; necessary.  This setting configures the amount of time a connection | ||||
| ; may sit idle (in seconds) before a reconnection will be attempted. | ||||
| ;idlecheck => 3600 | ||||
|  | ||||
| ; Certain servers, such as MS SQL Server and Sybase use the TDS protocol, which | ||||
| ; limits the number of active queries per connection to 1.  By telling res_odbc | ||||
| @@ -64,5 +110,12 @@ sanitysql => select count(*) from systables | ||||
| ; Server does not. | ||||
| backslash_is_escape => no | ||||
|  | ||||
|  | ||||
|  | ||||
| ; | ||||
| ; If you are having problems with concurrency, please read this note from the | ||||
| ; mailing lists, regarding UnixODBC: | ||||
| ; | ||||
| ; http://lists.digium.com/pipermail/asterisk-dev/2009-February/036539.html | ||||
| ; | ||||
| ; In summary, try setting "Threading=2" in the relevant section within your | ||||
| ; odbcinst.ini. | ||||
| ; | ||||
|   | ||||
| @@ -39,6 +39,7 @@ typedef enum { ODBC_SUCCESS=0, ODBC_FAIL=-1} odbc_status; | ||||
| enum { | ||||
| 	RES_ODBC_SANITY_CHECK = (1 << 0), | ||||
| 	RES_ODBC_INDEPENDENT_CONNECTION = (1 << 1), | ||||
| 	RES_ODBC_CONNECTED = (1 << 2), | ||||
| }; | ||||
|  | ||||
| /*! \brief ODBC container */ | ||||
| @@ -59,6 +60,7 @@ struct odbc_obj { | ||||
| 	AST_LIST_ENTRY(odbc_obj) list; | ||||
| }; | ||||
|  | ||||
| /*!\brief These structures are used for adaptive capabilities */ | ||||
| struct odbc_cache_columns { | ||||
| 	char *name; | ||||
| 	SQLSMALLINT type; | ||||
| @@ -99,17 +101,20 @@ struct odbc_cache_tables { | ||||
|  */ | ||||
| int ast_odbc_smart_execute(struct odbc_obj *obj, SQLHSTMT stmt) __attribute__((deprecated)); | ||||
|  | ||||
| /*!  | ||||
| /*! | ||||
|  * \brief Retrieves a connected ODBC object | ||||
|  * \param name The name of the ODBC class for which a connection is needed. | ||||
|  * \param flags Set of flags used to control which connection is returned. | ||||
|  * \retval ODBC object  | ||||
|  * \retval  NULL if there is no connection available with the requested name. | ||||
|  * \param flags One or more of the following flags: | ||||
|  * \li RES_ODBC_SANITY_CHECK Whether to ensure that a connection is valid before returning the handle.  Usually unnecessary. | ||||
|  * \li RES_ODBC_INDEPENDENT_CONNECTION Return a handle which is independent from all others.  Usually used when starting a transaction. | ||||
|  * \li RES_ODBC_CONNECTED Only return a connected handle.  Intended for use with peers which use idlecheck, which are checked periodically for reachability. | ||||
|  * \return ODBC object | ||||
|  * \retval NULL if there is no connection available with the requested name. | ||||
|  * | ||||
|  * Connection classes may, in fact, contain multiple connection handles.  If | ||||
|  * the connection is pooled, then each connection will be dedicated to the | ||||
|  * thread which requests it.  Note that all connections should be released | ||||
|  * when the thread is done by calling odbc_release_obj(), below. | ||||
|  * when the thread is done by calling ast_odbc_release_obj(), below. | ||||
|  */ | ||||
| struct odbc_obj *_ast_odbc_request_obj2(const char *name, struct ast_flags flags, const char *file, const char *function, int lineno); | ||||
| struct odbc_obj *_ast_odbc_request_obj(const char *name, int check, const char *file, const char *function, int lineno); | ||||
| @@ -130,7 +135,7 @@ struct odbc_obj *_ast_odbc_request_obj(const char *name, int check, const char * | ||||
| struct odbc_obj *ast_odbc_retrieve_transaction_obj(struct ast_channel *chan, const char *objname); | ||||
|  | ||||
| /*!  | ||||
|  * \brief Releases an ODBC object previously allocated by odbc_request_obj() | ||||
|  * \brief Releases an ODBC object previously allocated by ast_odbc_request_obj() | ||||
|  * \param obj The ODBC object | ||||
|  */ | ||||
| void ast_odbc_release_obj(struct odbc_obj *obj); | ||||
| @@ -175,7 +180,9 @@ SQLHSTMT ast_odbc_prepare_and_execute(struct odbc_obj *obj, SQLHSTMT (*prepare_c | ||||
|  * \param tablename Tablename to describe | ||||
|  * \retval A structure describing the table layout, or NULL, if the table is not found or another error occurs. | ||||
|  * When a structure is returned, the contained columns list will be | ||||
|  * rdlock'ed, to ensure that it will be retained in memory. | ||||
|  * rdlock'ed, to ensure that it will be retained in memory.  The information | ||||
|  * will be cached until a reload event or when ast_odbc_clear_cache() is called | ||||
|  * with the relevant parameters. | ||||
|  * \since 1.6.1 | ||||
|  */ | ||||
| struct odbc_cache_tables *ast_odbc_find_table(const char *database, const char *tablename); | ||||
| @@ -191,6 +198,8 @@ struct odbc_cache_columns *ast_odbc_find_column(struct odbc_cache_tables *table, | ||||
|  | ||||
| /*! | ||||
|  * \brief Remove a cache entry from memory | ||||
|  * This function may be called to clear entries created and cached by the | ||||
|  * ast_odbc_find_table() API call. | ||||
|  * \param database Name of an ODBC class (used to ensure like-named tables in different databases are not confused) | ||||
|  * \param tablename Tablename for which a cached record should be removed | ||||
|  * \retval 0 if the cache entry was removed, or -1 if no matching entry was found. | ||||
|   | ||||
							
								
								
									
										143
									
								
								main/config.c
									
									
									
									
									
								
							
							
						
						
									
										143
									
								
								main/config.c
									
									
									
									
									
								
							| @@ -163,6 +163,7 @@ static int hashtab_compare_strings(void *a, void *b, int flags) | ||||
|  | ||||
| static struct ast_config_map { | ||||
| 	struct ast_config_map *next; | ||||
| 	int priority; | ||||
| 	char *name; | ||||
| 	char *driver; | ||||
| 	char *database; | ||||
| @@ -1858,7 +1859,7 @@ static void clear_config_maps(void) | ||||
| 	ast_mutex_unlock(&config_lock); | ||||
| } | ||||
|  | ||||
| static int append_mapping(const char *name, const char *driver, const char *database, const char *table) | ||||
| static int append_mapping(const char *name, const char *driver, const char *database, const char *table, int priority) | ||||
| { | ||||
| 	struct ast_config_map *map; | ||||
| 	int length; | ||||
| @@ -1883,6 +1884,7 @@ static int append_mapping(const char *name, const char *driver, const char *data | ||||
| 		map->table = map->database + strlen(map->database) + 1; | ||||
| 		strcpy(map->table, table); | ||||
| 	} | ||||
| 	map->priority = priority; | ||||
| 	map->next = config_maps; | ||||
|  | ||||
| 	ast_verb(2, "Binding %s to %s/%s/%s\n", map->name, map->driver, map->database, map->table ? map->table : map->name); | ||||
| @@ -1895,8 +1897,9 @@ int read_config_maps(void) | ||||
| { | ||||
| 	struct ast_config *config, *configtmp; | ||||
| 	struct ast_variable *v; | ||||
| 	char *driver, *table, *database, *stringp, *tmp; | ||||
| 	char *driver, *table, *database, *textpri, *stringp, *tmp; | ||||
| 	struct ast_flags flags = { CONFIG_FLAG_NOREALTIME }; | ||||
| 	int pri; | ||||
|  | ||||
| 	clear_config_maps(); | ||||
|  | ||||
| @@ -1930,6 +1933,10 @@ int read_config_maps(void) | ||||
| 		} | ||||
|  | ||||
| 		table = strsep(&stringp, ","); | ||||
| 		textpri = strsep(&stringp, ","); | ||||
| 		if (!textpri || !(pri = atoi(textpri))) { | ||||
| 			pri = 1; | ||||
| 		} | ||||
|  | ||||
| 		if (!strcmp(v->name, extconfig_conf)) { | ||||
| 			ast_log(LOG_WARNING, "Cannot bind '%s'!\n", extconfig_conf); | ||||
| @@ -1950,14 +1957,14 @@ int read_config_maps(void) | ||||
| 			continue; | ||||
| 		if (!strcasecmp(v->name, "sipfriends")) { | ||||
| 			ast_log(LOG_WARNING, "The 'sipfriends' table is obsolete, update your config to use sipusers and sippeers, though they can point to the same table.\n"); | ||||
| 			append_mapping("sipusers", driver, database, table ? table : "sipfriends"); | ||||
| 			append_mapping("sippeers", driver, database, table ? table : "sipfriends"); | ||||
| 			append_mapping("sipusers", driver, database, table ? table : "sipfriends", pri); | ||||
| 			append_mapping("sippeers", driver, database, table ? table : "sipfriends", pri); | ||||
| 		} else if (!strcasecmp(v->name, "iaxfriends")) { | ||||
| 			ast_log(LOG_WARNING, "The 'iaxfriends' table is obsolete, update your config to use iaxusers and iaxpeers, though they can point to the same table.\n"); | ||||
| 			append_mapping("iaxusers", driver, database, table ? table : "iaxfriends"); | ||||
| 			append_mapping("iaxpeers", driver, database, table ? table : "iaxfriends"); | ||||
| 			append_mapping("iaxusers", driver, database, table ? table : "iaxfriends", pri); | ||||
| 			append_mapping("iaxpeers", driver, database, table ? table : "iaxfriends", pri); | ||||
| 		} else  | ||||
| 			append_mapping(v->name, driver, database, table); | ||||
| 			append_mapping(v->name, driver, database, table, pri); | ||||
| 	} | ||||
| 		 | ||||
| 	ast_config_destroy(config); | ||||
| @@ -2006,7 +2013,7 @@ int ast_config_engine_deregister(struct ast_config_engine *del) | ||||
| } | ||||
|  | ||||
| /*! \brief Find realtime engine for realtime family */ | ||||
| static struct ast_config_engine *find_engine(const char *family, char *database, int dbsiz, char *table, int tabsiz)  | ||||
| static struct ast_config_engine *find_engine(const char *family, int priority, char *database, int dbsiz, char *table, int tabsiz)  | ||||
| { | ||||
| 	struct ast_config_engine *eng, *ret = NULL; | ||||
| 	struct ast_config_map *map; | ||||
| @@ -2014,7 +2021,7 @@ static struct ast_config_engine *find_engine(const char *family, char *database, | ||||
| 	ast_mutex_lock(&config_lock); | ||||
|  | ||||
| 	for (map = config_maps; map; map = map->next) { | ||||
| 		if (!strcasecmp(family, map->name)) { | ||||
| 		if (!strcasecmp(family, map->name) && (priority == map->priority)) { | ||||
| 			if (database) | ||||
| 				ast_copy_string(database, map->database, dbsiz); | ||||
| 			if (table) | ||||
| @@ -2063,13 +2070,13 @@ struct ast_config *ast_config_internal_load(const char *filename, struct ast_con | ||||
| 	if (!ast_test_flag(&flags, CONFIG_FLAG_NOREALTIME) && config_engine_list) { | ||||
| 		struct ast_config_engine *eng; | ||||
|  | ||||
| 		eng = find_engine(filename, db, sizeof(db), table, sizeof(table)); | ||||
| 		eng = find_engine(filename, 1, db, sizeof(db), table, sizeof(table)); | ||||
|  | ||||
|  | ||||
| 		if (eng && eng->load_func) { | ||||
| 			loader = eng; | ||||
| 		} else { | ||||
| 			eng = find_engine("global", db, sizeof(db), table, sizeof(table)); | ||||
| 			eng = find_engine("global", 1, db, sizeof(db), table, sizeof(table)); | ||||
| 			if (eng && eng->load_func) | ||||
| 				loader = eng; | ||||
| 		} | ||||
| @@ -2107,10 +2114,17 @@ static struct ast_variable *ast_load_realtime_helper(const char *family, va_list | ||||
| 	char db[256]; | ||||
| 	char table[256]; | ||||
| 	struct ast_variable *res=NULL; | ||||
| 	int i; | ||||
|  | ||||
| 	eng = find_engine(family, db, sizeof(db), table, sizeof(table)); | ||||
| 	if (eng && eng->realtime_func)  | ||||
| 		res = eng->realtime_func(db, table, ap); | ||||
| 	for (i = 1; ; i++) { | ||||
| 		if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) { | ||||
| 			if (eng->realtime_func && (res = eng->realtime_func(db, table, ap))) { | ||||
| 				return res; | ||||
| 			} | ||||
| 		} else { | ||||
| 			return NULL; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return res; | ||||
| } | ||||
| @@ -2168,7 +2182,7 @@ int ast_check_realtime(const char *family) | ||||
| 		return 0;	/* There are no engines at all so fail early */ | ||||
| 	} | ||||
|  | ||||
| 	eng = find_engine(family, NULL, 0, NULL, 0); | ||||
| 	eng = find_engine(family, 1, NULL, 0, NULL, 0); | ||||
| 	if (eng) | ||||
| 		return 1; | ||||
| 	return 0; | ||||
| @@ -2186,12 +2200,18 @@ int ast_realtime_require_field(const char *family, ...) | ||||
| 	char db[256]; | ||||
| 	char table[256]; | ||||
| 	va_list ap; | ||||
| 	int res = -1; | ||||
| 	int res = -1, i; | ||||
|  | ||||
| 	va_start(ap, family); | ||||
| 	eng = find_engine(family, db, sizeof(db), table, sizeof(table)); | ||||
| 	if (eng && eng->require_func) { | ||||
| 		res = eng->require_func(db, table, ap); | ||||
| 	for (i = 1; ; i++) { | ||||
| 		if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) { | ||||
| 			/* If the require succeeds, it returns 0. */ | ||||
| 			if (eng->require_func && !(res = eng->require_func(db, table, ap))) { | ||||
| 				break; | ||||
| 			} | ||||
| 		} else { | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
| 	va_end(ap); | ||||
|  | ||||
| @@ -2203,11 +2223,17 @@ int ast_unload_realtime(const char *family) | ||||
| 	struct ast_config_engine *eng; | ||||
| 	char db[256]; | ||||
| 	char table[256]; | ||||
| 	int res = -1; | ||||
| 	int res = -1, i; | ||||
|  | ||||
| 	eng = find_engine(family, db, sizeof(db), table, sizeof(table)); | ||||
| 	if (eng && eng->unload_func) { | ||||
| 		res = eng->unload_func(db, table); | ||||
| 	for (i = 1; ; i++) { | ||||
| 		if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) { | ||||
| 			if (eng->unload_func) { | ||||
| 				/* Do this for ALL engines */ | ||||
| 				res = eng->unload_func(db, table); | ||||
| 			} | ||||
| 		} else { | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
| 	return res; | ||||
| } | ||||
| @@ -2219,11 +2245,18 @@ struct ast_config *ast_load_realtime_multientry(const char *family, ...) | ||||
| 	char table[256]; | ||||
| 	struct ast_config *res = NULL; | ||||
| 	va_list ap; | ||||
| 	int i; | ||||
|  | ||||
| 	va_start(ap, family); | ||||
| 	eng = find_engine(family, db, sizeof(db), table, sizeof(table)); | ||||
| 	if (eng && eng->realtime_multi_func)  | ||||
| 		res = eng->realtime_multi_func(db, table, ap); | ||||
| 	for (i = 1; ; i++) { | ||||
| 		if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) { | ||||
| 			if (eng->realtime_multi_func && (res = eng->realtime_multi_func(db, table, ap))) { | ||||
| 				break; | ||||
| 			} | ||||
| 		} else { | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
| 	va_end(ap); | ||||
|  | ||||
| 	return res; | ||||
| @@ -2232,15 +2265,22 @@ struct ast_config *ast_load_realtime_multientry(const char *family, ...) | ||||
| int ast_update_realtime(const char *family, const char *keyfield, const char *lookup, ...) | ||||
| { | ||||
| 	struct ast_config_engine *eng; | ||||
| 	int res = -1; | ||||
| 	int res = -1, i; | ||||
| 	char db[256]; | ||||
| 	char table[256]; | ||||
| 	va_list ap; | ||||
|  | ||||
| 	va_start(ap, lookup); | ||||
| 	eng = find_engine(family, db, sizeof(db), table, sizeof(table)); | ||||
| 	if (eng && eng->update_func)  | ||||
| 		res = eng->update_func(db, table, keyfield, lookup, ap); | ||||
| 	for (i = 1; ; i++) { | ||||
| 		if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) { | ||||
| 			/* If the update succeeds, it returns 0. */ | ||||
| 			if (eng->update_func && !(res = eng->update_func(db, table, keyfield, lookup, ap))) { | ||||
| 				break; | ||||
| 			} | ||||
| 		} else { | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
| 	va_end(ap); | ||||
|  | ||||
| 	return res; | ||||
| @@ -2249,15 +2289,21 @@ int ast_update_realtime(const char *family, const char *keyfield, const char *lo | ||||
| int ast_update2_realtime(const char *family, ...) | ||||
| { | ||||
| 	struct ast_config_engine *eng; | ||||
| 	int res = -1; | ||||
| 	int res = -1, i; | ||||
| 	char db[256]; | ||||
| 	char table[256]; | ||||
| 	va_list ap; | ||||
|  | ||||
| 	va_start(ap, family); | ||||
| 	eng = find_engine(family, db, sizeof(db), table, sizeof(table)); | ||||
| 	if (eng && eng->update2_func)  | ||||
| 		res = eng->update2_func(db, table, ap); | ||||
| 	for (i = 1; ; i++) { | ||||
| 		if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) { | ||||
| 			if (eng->update2_func && !(res = eng->update2_func(db, table, ap))) { | ||||
| 				break; | ||||
| 			} | ||||
| 		} else { | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
| 	va_end(ap); | ||||
|  | ||||
| 	return res; | ||||
| @@ -2266,15 +2312,22 @@ int ast_update2_realtime(const char *family, ...) | ||||
| int ast_store_realtime(const char *family, ...) | ||||
| { | ||||
| 	struct ast_config_engine *eng; | ||||
| 	int res = -1; | ||||
| 	int res = -1, i; | ||||
| 	char db[256]; | ||||
| 	char table[256]; | ||||
| 	va_list ap; | ||||
|  | ||||
| 	va_start(ap, family); | ||||
| 	eng = find_engine(family, db, sizeof(db), table, sizeof(table)); | ||||
| 	if (eng && eng->store_func)  | ||||
| 		res = eng->store_func(db, table, ap); | ||||
| 	for (i = 1; ; i++) { | ||||
| 		if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) { | ||||
| 			/* If the store succeeds, it returns 0. */ | ||||
| 			if (eng->store_func && !(res = eng->store_func(db, table, ap))) { | ||||
| 				break; | ||||
| 			} | ||||
| 		} else { | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
| 	va_end(ap); | ||||
|  | ||||
| 	return res; | ||||
| @@ -2283,15 +2336,21 @@ int ast_store_realtime(const char *family, ...) | ||||
| int ast_destroy_realtime(const char *family, const char *keyfield, const char *lookup, ...) | ||||
| { | ||||
| 	struct ast_config_engine *eng; | ||||
| 	int res = -1; | ||||
| 	int res = -1, i; | ||||
| 	char db[256]; | ||||
| 	char table[256]; | ||||
| 	va_list ap; | ||||
|  | ||||
| 	va_start(ap, lookup); | ||||
| 	eng = find_engine(family, db, sizeof(db), table, sizeof(table)); | ||||
| 	if (eng && eng->destroy_func)  | ||||
| 		res = eng->destroy_func(db, table, keyfield, lookup, ap); | ||||
| 	for (i = 1; ; i++) { | ||||
| 		if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) { | ||||
| 			if (eng->destroy_func && !(res = eng->destroy_func(db, table, keyfield, lookup, ap))) { | ||||
| 				break; | ||||
| 			} | ||||
| 		} else { | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
| 	va_end(ap); | ||||
|  | ||||
| 	return res; | ||||
|   | ||||
| @@ -167,6 +167,7 @@ static struct ast_variable *realtime_odbc(const char *database, const char *tabl | ||||
| 	SQLLEN indicator; | ||||
| 	va_list aq; | ||||
| 	struct custom_prepare_struct cps = { .sql = sql }; | ||||
| 	struct ast_flags connected_flag = { RES_ODBC_CONNECTED }; | ||||
|  | ||||
| 	if (ast_string_field_init(&cps, 256)) { | ||||
| 		return NULL; | ||||
| @@ -179,7 +180,7 @@ static struct ast_variable *realtime_odbc(const char *database, const char *tabl | ||||
| 		return NULL; | ||||
| 	} | ||||
|  | ||||
| 	obj = ast_odbc_request_obj(database, 0); | ||||
| 	obj = ast_odbc_request_obj2(database, connected_flag); | ||||
|  | ||||
| 	if (!obj) { | ||||
| 		ast_log(LOG_ERROR, "No database handle available with the name of '%s' (check res_odbc.conf)\n", database); | ||||
| @@ -325,6 +326,7 @@ static struct ast_config *realtime_multi_odbc(const char *database, const char * | ||||
| 	struct ast_variable *var=NULL; | ||||
| 	struct ast_config *cfg=NULL; | ||||
| 	struct ast_category *cat=NULL; | ||||
| 	struct ast_flags connected_flag = { RES_ODBC_CONNECTED }; | ||||
| 	SQLULEN colsize; | ||||
| 	SQLSMALLINT colcount=0; | ||||
| 	SQLSMALLINT datatype; | ||||
| @@ -341,7 +343,7 @@ static struct ast_config *realtime_multi_odbc(const char *database, const char * | ||||
| 	va_copy(aq, ap); | ||||
|  | ||||
|  | ||||
| 	obj = ast_odbc_request_obj(database, 0); | ||||
| 	obj = ast_odbc_request_obj2(database, connected_flag); | ||||
| 	if (!obj) { | ||||
| 		ast_string_field_free_memory(&cps); | ||||
| 		return NULL; | ||||
| @@ -479,6 +481,7 @@ static int update_odbc(const char *database, const char *table, const char *keyf | ||||
| 	struct custom_prepare_struct cps = { .sql = sql, .extra = lookup }; | ||||
| 	struct odbc_cache_tables *tableptr; | ||||
| 	struct odbc_cache_columns *column; | ||||
| 	struct ast_flags connected_flag = { RES_ODBC_CONNECTED }; | ||||
|  | ||||
| 	if (!table) { | ||||
| 		return -1; | ||||
| @@ -492,7 +495,7 @@ static int update_odbc(const char *database, const char *table, const char *keyf | ||||
| 	} | ||||
|  | ||||
| 	tableptr = ast_odbc_find_table(database, table); | ||||
| 	if (!(obj = ast_odbc_request_obj(database, 0))) { | ||||
| 	if (!(obj = ast_odbc_request_obj2(database, connected_flag))) { | ||||
| 		ast_odbc_release_table(tableptr); | ||||
| 		ast_string_field_free_memory(&cps); | ||||
| 		return -1; | ||||
| @@ -716,6 +719,7 @@ static int store_odbc(const char *database, const char *table, va_list ap) | ||||
| 	int res; | ||||
| 	va_list aq; | ||||
| 	struct custom_prepare_struct cps = { .sql = sql, .extra = NULL }; | ||||
| 	struct ast_flags connected_flag = { RES_ODBC_CONNECTED }; | ||||
|  | ||||
| 	va_copy(cps.ap, ap); | ||||
| 	va_copy(aq, ap); | ||||
| @@ -723,7 +727,7 @@ static int store_odbc(const char *database, const char *table, va_list ap) | ||||
| 	if (!table) | ||||
| 		return -1; | ||||
|  | ||||
| 	obj = ast_odbc_request_obj(database, 0); | ||||
| 	obj = ast_odbc_request_obj2(database, connected_flag); | ||||
| 	if (!obj) | ||||
| 		return -1; | ||||
|  | ||||
| @@ -790,6 +794,7 @@ static int destroy_odbc(const char *database, const char *table, const char *key | ||||
| 	int res; | ||||
| 	va_list aq; | ||||
| 	struct custom_prepare_struct cps = { .sql = sql, .extra = lookup }; | ||||
| 	struct ast_flags connected_flag = { RES_ODBC_CONNECTED }; | ||||
|  | ||||
| 	va_copy(cps.ap, ap); | ||||
| 	va_copy(aq, ap); | ||||
| @@ -797,7 +802,7 @@ static int destroy_odbc(const char *database, const char *table, const char *key | ||||
| 	if (!table) | ||||
| 		return -1; | ||||
|  | ||||
| 	obj = ast_odbc_request_obj(database, 0); | ||||
| 	obj = ast_odbc_request_obj2(database, connected_flag); | ||||
| 	if (!obj) | ||||
| 		return -1; | ||||
|  | ||||
| @@ -883,13 +888,14 @@ static struct ast_config *config_odbc(const char *database, const char *table, c | ||||
| 	char last[128] = ""; | ||||
| 	struct config_odbc_obj q; | ||||
| 	struct ast_flags loader_flags = { 0 }; | ||||
| 	struct ast_flags connected_flag = { RES_ODBC_CONNECTED }; | ||||
|  | ||||
| 	memset(&q, 0, sizeof(q)); | ||||
|  | ||||
| 	if (!file || !strcmp (file, "res_config_odbc.conf")) | ||||
| 		return NULL;		/* cant configure myself with myself ! */ | ||||
|  | ||||
| 	obj = ast_odbc_request_obj(database, 0); | ||||
| 	obj = ast_odbc_request_obj2(database, connected_flag); | ||||
| 	if (!obj) | ||||
| 		return NULL; | ||||
|  | ||||
|   | ||||
| @@ -131,6 +131,12 @@ struct odbc_class | ||||
| 	unsigned int limit;                  /*!< Maximum number of database handles we will allow */ | ||||
| 	int count;                           /*!< Running count of pooled connections */ | ||||
| 	unsigned int idlecheck;              /*!< Recheck the connection if it is idle for this long (in seconds) */ | ||||
| 	unsigned int conntimeout;            /*!< Maximum time the connection process should take */ | ||||
| 	/*! When a connection fails, cache that failure for how long? */ | ||||
| 	struct timeval negative_connection_cache; | ||||
| 	/*! When a connection fails, when did that last occur? */ | ||||
| 	struct timeval last_negative_connect; | ||||
| 	/*! List of handles associated with this class */ | ||||
| 	struct ao2_container *obj_container; | ||||
| }; | ||||
|  | ||||
| @@ -661,7 +667,7 @@ SQLHSTMT ast_odbc_prepare_and_execute(struct odbc_obj *obj, SQLHSTMT (*prepare_c | ||||
| 	return stmt; | ||||
| } | ||||
|  | ||||
| int ast_odbc_smart_execute(struct odbc_obj *obj, SQLHSTMT stmt)  | ||||
| int ast_odbc_smart_execute(struct odbc_obj *obj, SQLHSTMT stmt) | ||||
| { | ||||
| 	int res = 0, i; | ||||
| 	SQLINTEGER nativeerror=0, numfields=0; | ||||
| @@ -681,9 +687,10 @@ int ast_odbc_smart_execute(struct odbc_obj *obj, SQLHSTMT stmt) | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} else | ||||
| 	} else { | ||||
| 		obj->last_used = ast_tvnow(); | ||||
| 	 | ||||
| 	} | ||||
|  | ||||
| 	return res; | ||||
| } | ||||
|  | ||||
| @@ -746,7 +753,8 @@ static int load_odbc_config(void) | ||||
| 	struct ast_variable *v; | ||||
| 	char *cat; | ||||
| 	const char *dsn, *username, *password, *sanitysql; | ||||
| 	int enabled, pooling, limit, bse, forcecommit, isolation; | ||||
| 	int enabled, pooling, limit, bse, conntimeout, forcecommit, isolation; | ||||
| 	struct timeval ncache = { 0, 0 }; | ||||
| 	unsigned int idlecheck; | ||||
| 	int preconnect = 0, res = 0; | ||||
| 	struct ast_flags config_flags = { 0 }; | ||||
| @@ -808,6 +816,22 @@ static int load_odbc_config(void) | ||||
| 					sanitysql = v->value; | ||||
| 				} else if (!strcasecmp(v->name, "backslash_is_escape")) { | ||||
| 					bse = ast_true(v->value); | ||||
| 				} else if (!strcasecmp(v->name, "connect_timeout")) { | ||||
| 					if (sscanf(v->value, "%d", &conntimeout) != 1 || conntimeout < 1) { | ||||
| 						ast_log(LOG_WARNING, "connect_timeout must be a positive integer\n"); | ||||
| 						conntimeout = 10; | ||||
| 					} | ||||
| 				} else if (!strcasecmp(v->name, "negative_connection_cache")) { | ||||
| 					double dncache; | ||||
| 					if (sscanf(v->value, "%lf", &dncache) != 1 || dncache < 0) { | ||||
| 						ast_log(LOG_WARNING, "negative_connection_cache must be a non-negative integer\n"); | ||||
| 						/* 5 minutes sounds like a reasonable default */ | ||||
| 						ncache.tv_sec = 300; | ||||
| 						ncache.tv_usec = 0; | ||||
| 					} else { | ||||
| 						ncache.tv_sec = (int)dncache; | ||||
| 						ncache.tv_usec = (dncache - ncache.tv_sec) * 1000000; | ||||
| 					} | ||||
| 				} else if (!strcasecmp(v->name, "forcecommit")) { | ||||
| 					forcecommit = ast_true(v->value); | ||||
| 				} else if (!strcasecmp(v->name, "isolation")) { | ||||
| @@ -851,6 +875,8 @@ static int load_odbc_config(void) | ||||
| 				new->forcecommit = forcecommit ? 1 : 0; | ||||
| 				new->isolation = isolation; | ||||
| 				new->idlecheck = idlecheck; | ||||
| 				new->conntimeout = conntimeout; | ||||
| 				new->negative_connection_cache = ncache; | ||||
|  | ||||
| 				if (cat) | ||||
| 					ast_copy_string(new->name, cat, sizeof(new->name)); | ||||
| @@ -923,7 +949,13 @@ static char *handle_cli_odbc_show(struct ast_cli_entry *e, int cmd, struct ast_c | ||||
| 	while ((class = ao2_iterator_next(&aoi))) { | ||||
| 		if ((a->argc == 2) || (a->argc == 3 && !strcmp(a->argv[2], "all")) || (!strcmp(a->argv[2], class->name))) { | ||||
| 			int count = 0; | ||||
| 			char timestr[80]; | ||||
| 			struct ast_tm tm; | ||||
|  | ||||
| 			ast_localtime(&class->last_negative_connect, &tm, NULL); | ||||
| 			ast_strftime(timestr, sizeof(timestr), "%Y-%m-%d %T", &tm); | ||||
| 			ast_cli(a->fd, "  Name:   %s\n  DSN:    %s\n", class->name, class->dsn); | ||||
| 			ast_cli(a->fd, "    Last connection attempt: %s\n", timestr); | ||||
|  | ||||
| 			if (class->haspool) { | ||||
| 				struct ao2_iterator aoi2 = ao2_iterator_init(class->obj_container, 0); | ||||
| @@ -1171,6 +1203,7 @@ struct odbc_obj *_ast_odbc_request_obj2(const char *name, struct ast_flags flags | ||||
| 	unsigned char state[10], diagnostic[256]; | ||||
|  | ||||
| 	if (!(class = ao2_callback(class_container, 0, aoro2_class_cb, (char *) name))) { | ||||
| 		ast_debug(1, "Class not found!\n"); | ||||
| 		return NULL; | ||||
| 	} | ||||
|  | ||||
| @@ -1183,11 +1216,13 @@ struct odbc_obj *_ast_odbc_request_obj2(const char *name, struct ast_flags flags | ||||
| 		if (obj) { | ||||
| 			ast_assert(ao2_ref(obj, 0) > 1); | ||||
| 		} | ||||
|  | ||||
| 		if (!obj && (class->count < class->limit)) { | ||||
| 		if (!obj && (class->count < class->limit) && | ||||
| 				(time(NULL) > class->last_negative_connect.tv_sec + class->negative_connection_cache.tv_sec)) { | ||||
| 			obj = ao2_alloc(sizeof(*obj), odbc_obj_destructor); | ||||
| 			if (!obj) { | ||||
| 				class->count--; | ||||
| 				ao2_ref(class, -1); | ||||
| 				ast_debug(3, "Unable to allocate object\n"); | ||||
| 				return NULL; | ||||
| 			} | ||||
| 			ast_assert(ao2_ref(obj, 0) == 1); | ||||
| @@ -1228,9 +1263,11 @@ struct odbc_obj *_ast_odbc_request_obj2(const char *name, struct ast_flags flags | ||||
| 	} else if (ast_test_flag(&flags, RES_ODBC_INDEPENDENT_CONNECTION)) { | ||||
| 		/* Non-pooled connections -- but must use a separate connection handle */ | ||||
| 		if (!(obj = ao2_callback(class->obj_container, 0, aoro2_obj_cb, USE_TX))) { | ||||
| 			ast_debug(1, "Object not found\n"); | ||||
| 			obj = ao2_alloc(sizeof(*obj), odbc_obj_destructor); | ||||
| 			if (!obj) { | ||||
| 				ao2_ref(class, -1); | ||||
| 				ast_debug(3, "Unable to allocate object\n"); | ||||
| 				return NULL; | ||||
| 			} | ||||
| 			ast_mutex_init(&obj->lock); | ||||
| @@ -1271,6 +1308,7 @@ struct odbc_obj *_ast_odbc_request_obj2(const char *name, struct ast_flags flags | ||||
| 			if (!(obj = ao2_alloc(sizeof(*obj), odbc_obj_destructor))) { | ||||
| 				ast_assert(ao2_ref(class, 0) > 1); | ||||
| 				ao2_ref(class, -1); | ||||
| 				ast_debug(3, "Unable to allocate object\n"); | ||||
| 				return NULL; | ||||
| 			} | ||||
| 			ast_mutex_init(&obj->lock); | ||||
| @@ -1313,10 +1351,16 @@ struct odbc_obj *_ast_odbc_request_obj2(const char *name, struct ast_flags flags | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if (obj && ast_test_flag(&flags, RES_ODBC_SANITY_CHECK)) { | ||||
| 	if (obj && ast_test_flag(&flags, RES_ODBC_CONNECTED) && !obj->up) { | ||||
| 		/* Check if this connection qualifies for reconnection, with negative connection cache time */ | ||||
| 		if (time(NULL) > class->last_negative_connect.tv_sec + class->negative_connection_cache.tv_sec) { | ||||
| 			odbc_obj_connect(obj); | ||||
| 		} | ||||
| 	} else if (obj && ast_test_flag(&flags, RES_ODBC_SANITY_CHECK)) { | ||||
| 		ast_odbc_sanity_check(obj); | ||||
| 	} else if (obj && obj->parent->idlecheck > 0 && ast_tvdiff_sec(ast_tvnow(), obj->last_used) > obj->parent->idlecheck) | ||||
| 	} else if (obj && obj->parent->idlecheck > 0 && ast_tvdiff_sec(ast_tvnow(), obj->last_used) > obj->parent->idlecheck) { | ||||
| 		odbc_obj_connect(obj); | ||||
| 	} | ||||
|  | ||||
| #ifdef DEBUG_THREADS | ||||
| 	if (obj) { | ||||
| @@ -1431,11 +1475,12 @@ static odbc_status odbc_obj_connect(struct odbc_obj *obj) | ||||
|  | ||||
| 	if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { | ||||
| 		ast_log(LOG_WARNING, "res_odbc: Error AllocHDB %d\n", res); | ||||
| 		obj->parent->last_negative_connect = ast_tvnow(); | ||||
| 		ast_mutex_unlock(&obj->lock); | ||||
| 		return ODBC_FAIL; | ||||
| 	} | ||||
| 	SQLSetConnectAttr(obj->con, SQL_LOGIN_TIMEOUT, (SQLPOINTER *) 10, 0); | ||||
| 	SQLSetConnectAttr(obj->con, SQL_ATTR_CONNECTION_TIMEOUT, (SQLPOINTER *) 10, 0); | ||||
| 	SQLSetConnectAttr(obj->con, SQL_LOGIN_TIMEOUT, (SQLPOINTER *) obj->parent->conntimeout, 0); | ||||
| 	SQLSetConnectAttr(obj->con, SQL_ATTR_CONNECTION_TIMEOUT, (SQLPOINTER *) obj->parent->conntimeout, 0); | ||||
| #ifdef NEEDTRACE | ||||
| 	SQLSetConnectAttr(obj->con, SQL_ATTR_TRACE, &enable, SQL_IS_INTEGER); | ||||
| 	SQLSetConnectAttr(obj->con, SQL_ATTR_TRACEFILE, tracefile, strlen(tracefile)); | ||||
| @@ -1448,6 +1493,7 @@ static odbc_status odbc_obj_connect(struct odbc_obj *obj) | ||||
|  | ||||
| 	if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { | ||||
| 		SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, 1, state, &err, msg, 100, &mlen); | ||||
| 		obj->parent->last_negative_connect = ast_tvnow(); | ||||
| 		ast_mutex_unlock(&obj->lock); | ||||
| 		ast_log(LOG_WARNING, "res_odbc: Error SQLConnect=%d errno=%d %s\n", res, (int)err, msg); | ||||
| 		return ODBC_FAIL; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user