个人理解

api是freeswitch中一个很重要的组成部分,可以通过esl或者控制台输入api命令来控制freeswitch或者其上的session。
那它的基本实现原理是什么呢?

既然各个api都是可以动态加载的,那基本应该有下面几个操作:

  1. freeswitch核心有一个存储结构,用于动态的存储所有的api
  2. 调用load mod_xxx的时候,模块内部初始化的时候,需要注册自己的api到核心存储结构中
  3. 我们在控制台调用show api的时候,就从核心存储结构中读取api llist,然后显示出来

简陋的UML图

用uml图简单的来理解下: api的加载与调用 - 图1

基本数据结构

模块

文件: switch_loadable_module.c

  1. struct switch_loadable_module {
  2. char *key;
  3. char *filename;
  4. int perm;
  5. switch_loadable_module_interface_t *module_interface;
  6. switch_dso_lib_t lib;
  7. switch_module_load_t switch_module_load;
  8. switch_module_runtime_t switch_module_runtime;
  9. switch_module_shutdown_t switch_module_shutdown;
  10. switch_memory_pool_t *pool;
  11. switch_status_t status;
  12. switch_thread_t *thread;
  13. switch_bool_t shutting_down;
  14. };

模块接口

文件: switch_loadable_module.h
该结构体中存储中模块能实现的所有接口:

  1. struct switch_loadable_module_interface {
  2. /*! the name of the module */
  3. const char *module_name;
  4. /*! the table of endpoints the module has implemented */
  5. switch_endpoint_interface_t *endpoint_interface;
  6. /*! the table of timers the module has implemented */
  7. switch_timer_interface_t *timer_interface;
  8. /*! the table of dialplans the module has implemented */
  9. switch_dialplan_interface_t *dialplan_interface;
  10. /*! the table of codecs the module has implemented */
  11. switch_codec_interface_t *codec_interface;
  12. /*! the table of applications the module has implemented */
  13. switch_application_interface_t *application_interface;
  14. /*! the table of chat applications the module has implemented */
  15. switch_chat_application_interface_t *chat_application_interface;
  16. /*! the table of api functions the module has implemented */
  17. switch_api_interface_t *api_interface;
  18. /*! the table of json api functions the module has implemented */
  19. switch_json_api_interface_t *json_api_interface;
  20. /*! the table of file formats the module has implemented */
  21. switch_file_interface_t *file_interface;
  22. /*! the table of speech interfaces the module has implemented */
  23. switch_speech_interface_t *speech_interface;
  24. /*! the table of directory interfaces the module has implemented */
  25. switch_directory_interface_t *directory_interface;
  26. /*! the table of chat interfaces the module has implemented */
  27. switch_chat_interface_t *chat_interface;
  28. /*! the table of say interfaces the module has implemented */
  29. switch_say_interface_t *say_interface;
  30. /*! the table of asr interfaces the module has implemented */
  31. switch_asr_interface_t *asr_interface;
  32. /*! the table of management interfaces the module has implemented */
  33. switch_management_interface_t *management_interface;
  34. /*! the table of limit interfaces the module has implemented */
  35. switch_limit_interface_t *limit_interface;
  36. switch_thread_rwlock_t *rwlock;
  37. int refs;
  38. switch_memory_pool_t *pool;
  39. };

API接口

switch_api_interface

  1. /*! \brief A module interface to implement an api function */
  2. struct switch_api_interface {
  3. /*! api名称 */
  4. const char *interface_name;
  5. /*! api功能描述 */
  6. const char *desc;
  7. /*! api调用函数 */
  8. switch_api_function_t function;
  9. /*! api语法调用示例 */
  10. const char *syntax;
  11. switch_loadable_module_interface_t *parent;
  12. ...
  13. };

处理流程

加载模块(load mod_xxx)

文件:switch_loadable_module.c

  1. static switch_status_t switch_loadable_module_load_file(char *path, char *filename, switch_bool_t global, switch_loadable_module_t **new_module)
  2. {
  3. ...
  4. switch_loadable_module_t *module = NULL;
  5. switch_loadable_module_interface_t *module_interface = NULL;
  6. switch_module_load_t load_func_ptr = NULL;
  7. ...
  8. //找到模块中的load函数
  9. if (interface_struct_handle) {
  10. mod_interface_functions = interface_struct_handle;
  11. load_func_ptr = mod_interface_functions->load;
  12. }
  13. //调用load函数,传入空的module_interface对象,让其在模块内部初始化
  14. status = load_func_ptr(&module_interface, pool);
  15. if (status != SWITCH_STATUS_SUCCESS && status != SWITCH_STATUS_NOUNLOAD) {
  16. err = "Module load routine returned an error";
  17. module_interface = NULL;
  18. break;
  19. }
  20. //模块初始化调用完毕后,组装该模块的各种属性
  21. module->pool = pool;
  22. module->filename = switch_core_strdup(module->pool, path);
  23. module->module_interface = module_interface;
  24. module->switch_module_load = load_func_ptr;
  25. if (mod_interface_functions) {
  26. module->switch_module_shutdown = mod_interface_functions->shutdown;
  27. module->switch_module_runtime = mod_interface_functions->runtime;
  28. }
  29. ...
  30. }

从代码中可以看出, 在加载模块so文件的时候, 传递一个空的module_interface和内存池给模块的init函数。

模块初始化

0.初始化代码

  1. SWITCH_MODULE_LOAD_FUNCTION(mod_commands_load)
  2. {
  3. switch_api_interface_t *commands_api_interface;
  4. *module_interface = switch_loadable_module_create_module_interface(pool, modname);
  5. ...
  6. switch_thread_rwlock_create(&bgapi_rwlock, pool);
  7. switch_mutex_init(&reload_mutex, SWITCH_MUTEX_NESTED, pool);
  8. ...
  9. SWITCH_ADD_API(commands_api_interface, "acl", "Compare an ip to an acl list", acl_function, "<ip> <list_name>");
  10. ...
  11. }

1.通过入口函数,获取模块接口(module_interface)

文件:mod_commands.c
入口函数:SWITCH_MODULE_LOAD_FUNCTION(mod_commands_load)
该函数是一个宏,具体定义如下:

  1. #define SWITCH_MODULE_LOAD_ARGS (switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool)
  2. #define SWITCH_MODULE_LOAD_FUNCTION(name) switch_status_t name SWITCH_MODULE_LOAD_ARGS

所以,将宏SWITCH_MODULE_LOAD_FUNCTION全部展开之后,如下:

  1. 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)

  1. *module_interface = switch_loadable_module_create_module_interface(pool, modname);

module_interfacec传递进来的时候是二级指针,*module_interface则代表将二级指针剥离,给里面露出来的一级指针(传递进来前的原始指针)赋值。
也就是说, 给传递进来的空指针赋值,让调用者能获取到该值。

switch_loadable_module_create_module_interface函数的功能也比较简单,在pool上面分配一块空间来存储模块接口,同时指明模块的名称。
具体代码如下:

  1. SWITCH_DECLARE(switch_loadable_module_interface_t *) switch_loadable_module_create_module_interface(switch_memory_pool_t *pool, const char *name)
  2. {
  3. switch_loadable_module_interface_t *mod;
  4. mod = switch_core_alloc(pool, sizeof(switch_loadable_module_interface_t));
  5. switch_assert(mod != NULL);
  6. mod->pool = pool;
  7. mod->module_name = switch_core_strdup(mod->pool, name);
  8. switch_thread_rwlock_create(&mod->rwlock, mod->pool);
  9. return mod;
  10. }

3.添加新的api到module_interface

  1. 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

  1. #define SWITCH_ADD_API(api_int, int_name, descript, funcptr, syntax_string) \
  2. for (;;) { \
  3. //创建api对象,并保存到module_interface的api链表中
  4. api_int = (switch_api_interface_t *)switch_loadable_module_create_interface(*module_interface, SWITCH_API_INTERFACE); \
  5. api_int->interface_name = int_name; \
  6. api_int->desc = descript; \
  7. api_int->function = funcptr; \
  8. api_int->syntax = syntax_string; \
  9. break; \
  10. }

上面的代码是初始化api的各种参数。

模块加载后处理

读取模块初始化过的module_interface, 从中读取出来模块实现的各种接口,然后存储到全局的hash表中。
这里我们主要关注的是api接口,代码如下:

文件:switch_loadable_module.c

  1. static switch_status_t switch_loadable_module_process(char *key, switch_loadable_module_t *new_module)
  2. {
  3. ...
  4. if (new_module->module_interface->api_interface) {
  5. const switch_api_interface_t *ptr;
  6. for (ptr = new_module->module_interface->api_interface; ptr; ptr = ptr->next) {
  7. if (!ptr->interface_name) {
  8. switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Failed to load api interface from %s due to no interface name.\n", key);
  9. } else {
  10. switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Adding API Function '%s'\n", ptr->interface_name);
  11. if (switch_event_create(&event, SWITCH_EVENT_MODULE_LOAD) == SWITCH_STATUS_SUCCESS) {
  12. switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "type", "api");
  13. switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "name", ptr->interface_name);
  14. switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "description", switch_str_nil(ptr->desc));
  15. switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "syntax", switch_str_nil(ptr->syntax));
  16. switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "key", new_module->key);
  17. switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "filename", new_module->filename);
  18. switch_event_fire(&event);
  19. added++;
  20. }
  21. switch_core_hash_insert(loadable_modules.api_hash, ptr->interface_name, (const void *) ptr);
  22. }
  23. }
  24. }
  25. ...
  26. }

全局hash目前主要存储在sqlite中,我们读取下core.db内容,如下:
image.png

下面是系统启动时的模块加载日志:
image.png
先加载模块, 在加载模块上面的api函数

show api代码解析

我们在控制台输入show api,显示结果如下:
image.png
那具体的实现原理是什么?

文件: mod_commands.c

  1. SWITCH_STANDARD_API(show_function)
  2. {
  3. ...
  4. if (cmd && *cmd && (mydata = strdup(cmd))) {
  5. switch_separate_string(mydata, ' ', argv, (sizeof(argv) / sizeof(argv[0])));
  6. command = argv[0];
  7. if (argv[2] && !strcasecmp(argv[1], "as")) {
  8. as = argv[2];
  9. }
  10. }
  11. ...
  12. } else if (!strcasecmp(command, "application") || !strcasecmp(command, "api")) {
  13. if (argv[1] && strcasecmp(argv[1], "as")) {
  14. sprintf(sql,
  15. "select name, description, syntax, ikey from interfaces where hostname='%s' and type = '%s' and description != '' and name = '%s' order by type,name",
  16. switch_core_get_hostname(), command, argv[1]);
  17. } else {
  18. 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);
  19. }
  20. if (!as) {
  21. as = "delim";
  22. holder.delim = ",";
  23. }
  24. if (!strcasecmp(as, "delim") || !strcasecmp(as, "csv")) {
  25. if (zstr(holder.delim)) {
  26. if (!(holder.delim = argv[3])) {
  27. holder.delim = ",";
  28. }
  29. }
  30. switch_cache_db_execute_sql_callback(db, sql, show_callback, &holder, &errmsg);
  31. if (html) {
  32. holder.stream->write_function(holder.stream, "</table>");
  33. }
  34. ...
  35. }

从上面的代码可以看出,

  1. 在收到show api请求之后, 判断是否有as json/as xml等格式要求
  2. 没有格式要求, 默认为delim
  3. 如果命令为show api, 则构建查询sql, 从interfaces表中查找
  4. 使用switch_cache_db_execute_sql_callback, 执行sql查找.