个人理解
api是freeswitch中一个很重要的组成部分,可以通过esl或者控制台输入api命令来控制freeswitch或者其上的session。
那它的基本实现原理是什么呢?
既然各个api都是可以动态加载的,那基本应该有下面几个操作:
- freeswitch核心有一个存储结构,用于动态的存储所有的api
- 调用load mod_xxx的时候,模块内部初始化的时候,需要注册自己的api到核心存储结构中
- 我们在控制台调用show api的时候,就从核心存储结构中读取api llist,然后显示出来
简陋的UML图
基本数据结构
模块
文件: switch_loadable_module.c
struct switch_loadable_module {
char *key;
char *filename;
int perm;
switch_loadable_module_interface_t *module_interface;
switch_dso_lib_t lib;
switch_module_load_t switch_module_load;
switch_module_runtime_t switch_module_runtime;
switch_module_shutdown_t switch_module_shutdown;
switch_memory_pool_t *pool;
switch_status_t status;
switch_thread_t *thread;
switch_bool_t shutting_down;
};
模块接口
文件: switch_loadable_module.h
该结构体中存储中模块能实现的所有接口:
struct switch_loadable_module_interface {
/*! the name of the module */
const char *module_name;
/*! the table of endpoints the module has implemented */
switch_endpoint_interface_t *endpoint_interface;
/*! the table of timers the module has implemented */
switch_timer_interface_t *timer_interface;
/*! the table of dialplans the module has implemented */
switch_dialplan_interface_t *dialplan_interface;
/*! the table of codecs the module has implemented */
switch_codec_interface_t *codec_interface;
/*! the table of applications the module has implemented */
switch_application_interface_t *application_interface;
/*! the table of chat applications the module has implemented */
switch_chat_application_interface_t *chat_application_interface;
/*! the table of api functions the module has implemented */
switch_api_interface_t *api_interface;
/*! the table of json api functions the module has implemented */
switch_json_api_interface_t *json_api_interface;
/*! the table of file formats the module has implemented */
switch_file_interface_t *file_interface;
/*! the table of speech interfaces the module has implemented */
switch_speech_interface_t *speech_interface;
/*! the table of directory interfaces the module has implemented */
switch_directory_interface_t *directory_interface;
/*! the table of chat interfaces the module has implemented */
switch_chat_interface_t *chat_interface;
/*! the table of say interfaces the module has implemented */
switch_say_interface_t *say_interface;
/*! the table of asr interfaces the module has implemented */
switch_asr_interface_t *asr_interface;
/*! the table of management interfaces the module has implemented */
switch_management_interface_t *management_interface;
/*! the table of limit interfaces the module has implemented */
switch_limit_interface_t *limit_interface;
switch_thread_rwlock_t *rwlock;
int refs;
switch_memory_pool_t *pool;
};
API接口
switch_api_interface
/*! \brief A module interface to implement an api function */
struct switch_api_interface {
/*! api名称 */
const char *interface_name;
/*! api功能描述 */
const char *desc;
/*! api调用函数 */
switch_api_function_t function;
/*! api语法调用示例 */
const char *syntax;
switch_loadable_module_interface_t *parent;
...
};
处理流程
加载模块(load mod_xxx)
文件:switch_loadable_module.c
static switch_status_t switch_loadable_module_load_file(char *path, char *filename, switch_bool_t global, switch_loadable_module_t **new_module)
{
...
switch_loadable_module_t *module = NULL;
switch_loadable_module_interface_t *module_interface = NULL;
switch_module_load_t load_func_ptr = NULL;
...
//找到模块中的load函数
if (interface_struct_handle) {
mod_interface_functions = interface_struct_handle;
load_func_ptr = mod_interface_functions->load;
}
//调用load函数,传入空的module_interface对象,让其在模块内部初始化
status = load_func_ptr(&module_interface, pool);
if (status != SWITCH_STATUS_SUCCESS && status != SWITCH_STATUS_NOUNLOAD) {
err = "Module load routine returned an error";
module_interface = NULL;
break;
}
//模块初始化调用完毕后,组装该模块的各种属性
module->pool = pool;
module->filename = switch_core_strdup(module->pool, path);
module->module_interface = module_interface;
module->switch_module_load = load_func_ptr;
if (mod_interface_functions) {
module->switch_module_shutdown = mod_interface_functions->shutdown;
module->switch_module_runtime = mod_interface_functions->runtime;
}
...
}
从代码中可以看出, 在加载模块so文件的时候, 传递一个空的module_interface和内存池给模块的init函数。
模块初始化
0.初始化代码
SWITCH_MODULE_LOAD_FUNCTION(mod_commands_load)
{
switch_api_interface_t *commands_api_interface;
*module_interface = switch_loadable_module_create_module_interface(pool, modname);
...
switch_thread_rwlock_create(&bgapi_rwlock, pool);
switch_mutex_init(&reload_mutex, SWITCH_MUTEX_NESTED, pool);
...
SWITCH_ADD_API(commands_api_interface, "acl", "Compare an ip to an acl list", acl_function, "<ip> <list_name>");
...
}
1.通过入口函数,获取模块接口(module_interface)
文件:mod_commands.c
入口函数:SWITCH_MODULE_LOAD_FUNCTION(mod_commands_load)
该函数是一个宏,具体定义如下:
#define SWITCH_MODULE_LOAD_ARGS (switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool)
#define SWITCH_MODULE_LOAD_FUNCTION(name) switch_status_t name SWITCH_MODULE_LOAD_ARGS
所以,将宏SWITCH_MODULE_LOAD_FUNCTION全部展开之后,如下:
switch_status_t mod_commands_load (switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool)
结合上一步的“加载模块”部分,可以看到,参数传递进来的module_interface,本身是一个空结构。
本地需要注册到全局的api或者其他功能接口,都需要保存到该module_interface结构中。
module_interface里面具体的结构,可以参考module_interface。
2.初始化模块接口(module_interface)
*module_interface = switch_loadable_module_create_module_interface(pool, modname);
module_interfacec传递进来的时候是二级指针,*module_interface则代表将二级指针剥离,给里面露出来的一级指针(传递进来前的原始指针)赋值。
也就是说, 给传递进来的空指针赋值,让调用者能获取到该值。
switch_loadable_module_create_module_interface函数的功能也比较简单,在pool上面分配一块空间来存储模块接口,同时指明模块的名称。
具体代码如下:
SWITCH_DECLARE(switch_loadable_module_interface_t *) switch_loadable_module_create_module_interface(switch_memory_pool_t *pool, const char *name)
{
switch_loadable_module_interface_t *mod;
mod = switch_core_alloc(pool, sizeof(switch_loadable_module_interface_t));
switch_assert(mod != NULL);
mod->pool = pool;
mod->module_name = switch_core_strdup(mod->pool, name);
switch_thread_rwlock_create(&mod->rwlock, mod->pool);
return mod;
}
3.添加新的api到module_interface
SWITCH_ADD_API(commands_api_interface, "acl", "Compare an ip to an acl list", acl_function, "<ip> <list_name>");
SWITCH_ADD_API本身是一个宏,定义如下:
文件:switch_loadable_module.h
#define SWITCH_ADD_API(api_int, int_name, descript, funcptr, syntax_string) \
for (;;) { \
//创建api对象,并保存到module_interface的api链表中
api_int = (switch_api_interface_t *)switch_loadable_module_create_interface(*module_interface, SWITCH_API_INTERFACE); \
api_int->interface_name = int_name; \
api_int->desc = descript; \
api_int->function = funcptr; \
api_int->syntax = syntax_string; \
break; \
}
上面的代码是初始化api的各种参数。
模块加载后处理
读取模块初始化过的module_interface, 从中读取出来模块实现的各种接口,然后存储到全局的hash表中。
这里我们主要关注的是api接口,代码如下:
文件:switch_loadable_module.c
static switch_status_t switch_loadable_module_process(char *key, switch_loadable_module_t *new_module)
{
...
if (new_module->module_interface->api_interface) {
const switch_api_interface_t *ptr;
for (ptr = new_module->module_interface->api_interface; ptr; ptr = ptr->next) {
if (!ptr->interface_name) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Failed to load api interface from %s due to no interface name.\n", key);
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Adding API Function '%s'\n", ptr->interface_name);
if (switch_event_create(&event, SWITCH_EVENT_MODULE_LOAD) == SWITCH_STATUS_SUCCESS) {
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "type", "api");
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "name", ptr->interface_name);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "description", switch_str_nil(ptr->desc));
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "syntax", switch_str_nil(ptr->syntax));
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "key", new_module->key);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "filename", new_module->filename);
switch_event_fire(&event);
added++;
}
switch_core_hash_insert(loadable_modules.api_hash, ptr->interface_name, (const void *) ptr);
}
}
}
...
}
全局hash目前主要存储在sqlite中,我们读取下core.db内容,如下:
下面是系统启动时的模块加载日志:
先加载模块, 在加载模块上面的api函数
show api代码解析
我们在控制台输入show api,显示结果如下:
那具体的实现原理是什么?
文件: mod_commands.c
SWITCH_STANDARD_API(show_function)
{
...
if (cmd && *cmd && (mydata = strdup(cmd))) {
switch_separate_string(mydata, ' ', argv, (sizeof(argv) / sizeof(argv[0])));
command = argv[0];
if (argv[2] && !strcasecmp(argv[1], "as")) {
as = argv[2];
}
}
...
} else if (!strcasecmp(command, "application") || !strcasecmp(command, "api")) {
if (argv[1] && strcasecmp(argv[1], "as")) {
sprintf(sql,
"select name, description, syntax, ikey from interfaces where hostname='%s' and type = '%s' and description != '' and name = '%s' order by type,name",
switch_core_get_hostname(), command, argv[1]);
} else {
sprintf(sql, "select name, description, syntax, ikey from interfaces where hostname='%s' and type = '%s' and description != '' order by type,name", switch_core_get_hostname(), command);
}
if (!as) {
as = "delim";
holder.delim = ",";
}
if (!strcasecmp(as, "delim") || !strcasecmp(as, "csv")) {
if (zstr(holder.delim)) {
if (!(holder.delim = argv[3])) {
holder.delim = ",";
}
}
switch_cache_db_execute_sql_callback(db, sql, show_callback, &holder, &errmsg);
if (html) {
holder.stream->write_function(holder.stream, "</table>");
}
...
}
从上面的代码可以看出,
- 在收到show api请求之后, 判断是否有as json/as xml等格式要求
- 没有格式要求, 默认为delim
- 如果命令为show api, 则构建查询sql, 从interfaces表中查找
- 使用switch_cache_db_execute_sql_callback, 执行sql查找.