个人理解
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查找.
