Asterisk cli模块分析

本文介绍 Asterisk 中 CLI 动态注册机制,分析了 cli 模块的关键结构体 ast_cli_entry 和相关函数 ast_cli_register 的实现细节。此外,还提供了简化实现 CLI 的方法,以满足特定需求。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

http://blog.youkuaiyun.com/ren911/category/683751.aspx

最近写一些工具库,需要远程命令行调试(cli)功能,原有的一个cli模块是将 接收处理的命令具体实现在cli模块中,其他模块需要修改添加自己的cli命令都需要去修改cli模块代码,觉得模块间耦合度太高,在看asterisk 源码时记得它的cli模块是一种注册机制,cli模块主要对外提供注册和反注册接口,其他模块实现一组特定的cli entry,再调用注册和反注册函数进行操作。可以动态的控制远程可操作的cli命令,觉得比较好,分析了一下。并参照它的思想简化的实现了一个满足自己 需求的cli模块。

    以下文章原是写在trac的wiki与代码结合使用的,所以部分超链接在此网页中无法使用。

 

  Asterisk cli 模块分析

 

 

asterisk cli结构分析图
   

整体流程分析图

关键结构体 ast_cli_entry :include/asterisk/cli.h

 

  1. /*! /brief A command line entry */   
  2.   
  3. struct  ast_cli_entry {  
  4.   
  5.         char  *  const  cmda[AST_MAX_CMD_LEN]; //命令的格式,显示字符串   
  6.   
  7.         /*! Handler for the command (fd for output, # of args, argument list).  
  8.  
  9.           Returns RESULT_SHOWUSAGE for improper arguments.  
  10.  
  11.           argv[] has argc 'useful' entries, and an additional NULL entry  
  12.  
  13.           at the end so that clients requiring NULL terminated arrays  
  14.  
  15.           can use it without need for copies.  
  16.  
  17.           You can overwrite argv or the strings it points to, but remember  
  18.  
  19.           that this memory is deallocated after the handler returns.  
  20.  
  21.          */   
  22.   
  23.         int  (*handler)( int  fd,  int  argc,  char  *argv[]); //命令的具体处理函数,传入参数不对时返回RESULT_SHOWUSAGE   
  24.   
  25.         /*! Summary of the command (< 60 characters) */   
  26.   
  27.         const   char  *summary; //命令的概述   
  28.   
  29.         /*! Detailed usage information */   
  30.   
  31.         const   char  *usage; //命令的详细使用范围   
  32.   
  33.         /*! Generate the n-th (starting from 0) possible completion  
  34.  
  35.           for a given 'word' following 'line' in position 'pos'.  
  36.  
  37.           'line' and 'word' must not be modified.  
  38.  
  39.           Must return a malloc'ed string with the n-th value when available,  
  40.  
  41.           or NULL if the n-th completion does not exist.  
  42.  
  43.           Typically, the function is called with increasing values for n  
  44.  
  45.           until a NULL is returned.  
  46.  
  47.          */   
  48.   
  49.         char  *(*generator)( const   char  *line,  const   char  *word,  int  pos,  int  n); //自动完成命令的函数,尽可能补充长的命令,按tab时。   
  50.   
  51.         struct  ast_cli_entry *deprecate_cmd; //和此命令有冲突的命令   
  52.   
  53.         /*! For keeping track of usage */   
  54.   
  55.         int  inuse; // 函数现在的使用范围,用于追踪。   
  56.   
  57.         struct  module *module;   /*! module this belongs to */   //命令属于的模块   
  58.   
  59.         char  *_full_cmd;         /* built at load time from cmda[] */   //完整命令,由cmda[]连接而成   
  60.   
  61.         /* This gets set in ast_cli_register()  
  62.  
  63.           It then gets set to something different when the deprecated command  
  64.  
  65.           is run for the first time (ie; after we warn the user that it's deprecated)  
  66.  
  67.          */   
  68.   
  69.         int   deprecated ;  
  70.   
  71.         char  *_deprecated_by;    /* copied from the "parent" _full_cmd, on deprecated commands */   
  72.   
  73.         /*! For linking */   
  74.   
  75.         AST_LIST_ENTRY(ast_cli_entry) list;  
  76.   
  77. };  
  78.   
  79.   
  80. /*! /brief A command line entry */   
  81.   
  82. struct  ast_cli_entry {  
  83.   
  84.         char  *  const  cmda[AST_MAX_CMD_LEN]; //命令的格式,显示字符串   
  85.   
  86.         /*! Handler for the command (fd for output, # of args, argument list).  
  87.  
  88.           Returns RESULT_SHOWUSAGE for improper arguments.  
  89.  
  90.           argv[] has argc 'useful' entries, and an additional NULL entry  
  91.  
  92.           at the end so that clients requiring NULL terminated arrays  
  93.  
  94.           can use it without need for copies.  
  95.  
  96.           You can overwrite argv or the strings it points to, but remember  
  97.  
  98.           that this memory is deallocated after the handler returns.  
  99.  
  100.          */   
  101.   
  102.         int  (*handler)( int  fd,  int  argc,  char  *argv[]); //命令的具体处理函数,传入参数不对时返回RESULT_SHOWUSAGE   
  103.   
  104.         /*! Summary of the command (< 60 characters) */   
  105.   
  106.         const   char  *summary; //命令的概述   
  107.   
  108.         /*! Detailed usage information */   
  109.   
  110.         const   char  *usage; //命令的详细使用范围   
  111.   
  112.         /*! Generate the n-th (starting from 0) possible completion  
  113.  
  114.           for a given 'word' following 'line' in position 'pos'.  
  115.  
  116.           'line' and 'word' must not be modified.  
  117.  
  118.           Must return a malloc'ed string with the n-th value when available,  
  119.  
  120.           or NULL if the n-th completion does not exist.  
  121.  
  122.           Typically, the function is called with increasing values for n  
  123.  
  124.           until a NULL is returned.  
  125.  
  126.          */   
  127.   
  128.         char  *(*generator)( const   char  *line,  const   char  *word,  int  pos,  int  n); //自动完成命令的函数,尽可能补充长的命令,按tab时。   
  129.   
  130.         struct  ast_cli_entry *deprecate_cmd; //和此命令有冲突的命令   
  131.   
  132.         /*! For keeping track of usage */   
  133.   
  134.         int  inuse; // 函数现在的使用范围,用于追踪。   
  135.   
  136.         struct  module *module;   /*! module this belongs to */   //命令属于的模块   
  137.   
  138.         char  *_full_cmd;         /* built at load time from cmda[] */   //完整命令,由cmda[]连接而成   
  139.   
  140.         /* This gets set in ast_cli_register()  
  141.  
  142.           It then gets set to something different when the deprecated command  
  143.  
  144.           is run for the first time (ie; after we warn the user that it's deprecated)  
  145.  
  146.          */   
  147.   
  148.         int   deprecated ;  
  149.   
  150.         char  *_deprecated_by;    /* copied from the "parent" _full_cmd, on deprecated commands */   
  151.   
  152.         /*! For linking */   
  153.   
  154.         AST_LIST_ENTRY(ast_cli_entry) list;  
  155.   
  156. };  

具体 ast_cli_entry 例子

具体例子: chan_sip.c

  1. static   struct  ast_cli_entry cli_sip[] = {  
  2.   
  3.         { { "sip""show""channels" , NULL },  
  4.   
  5.         sip_show_channels, "List active SIP channels" ,  
  6.   
  7.         show_channels_usage },  
  8.   
  9.    
  10.   
  11.         { { "sip""show""domains" , NULL },  
  12.   
  13.         sip_show_domains, "List our local SIP domains." ,  
  14.   
  15.         show_domains_usage },  
  16.   
  17.         ...  
  18.   
  19.         { { "sip""show""users" , NULL },  
  20.   
  21.         sip_show_users, "List defined SIP users" ,  
  22.   
  23.         show_users_usage },  
  24.   
  25.         ...  

模块中如何使用见 .外围模块中 cli机制

cli_sip sip 通道模块注册的 cli 命令集合。其中每 一个是具体的一个命令。以 "sip show users" 为例, { "sip", "show", "users", NULL } cmda sip_show_users handler (函数), "List defined SIP users" 是命令概述, show_users_usage 是命令使用范围(一个字符串)。

sip_show_users 分析 handler 的结构 , 位于 : chan_sip.c ,成功返回 RESULT_SUCCESS ,失败返回 RESULT_SHOWUSAGE ,显示使用范围。具体代码:

  1. /*! /brief  CLI Command 'SIP Show Users' */   
  2.   
  3. static   int  sip_show_users( int  fd,  int  argc,  char  *argv[])  
  4.   
  5. {  
  6.   
  7.         regex_t regexbuf;  
  8.   
  9.         int  havepattern = FALSE;  
  10.   
  11.    
  12.   
  13. #define FORMAT  "%-25.25s  %-15.15s  %-15.15s  %-15.15s  %-5.5s%-10.10s/n"   
  14.   
  15.    
  16.   
  17.         switch  (argc) {  
  18.   
  19.         case  5:  
  20.   
  21.                 if  (!strcasecmp(argv[3],  "like" )) {  
  22.   
  23.                         if  (regcomp(®exbuf, argv[4], REG_EXTENDED | REG_NOSUB))  
  24.   
  25.                                 return  RESULT_SHOWUSAGE;  
  26.   
  27.                         havepattern = TRUE;  
  28.   
  29.                 } else   
  30.   
  31.                         return  RESULT_SHOWUSAGE;  
  32.   
  33.         case  3:  
  34.   
  35.                 break ;  
  36.   
  37.         default :  
  38.   
  39.                 return  RESULT_SHOWUSAGE;  
  40.   
  41.         }  
  42.   
  43.    
  44.   
  45.         ast_cli(fd, FORMAT, "Username""Secret""Accountcode""Def.Context""ACL""NAT" );  
  46.   
  47.         ASTOBJ_CONTAINER_TRAVERSE(&userl, 1, do  {  
  48.   
  49.                 ASTOBJ_RDLOCK(iterator);  
  50.   
  51.    
  52.   
  53.                 if  (havepattern && regexec(®exbuf, iterator->name, 0, NULL, 0)) {  
  54.   
  55.                         ASTOBJ_UNLOCK(iterator);  
  56.   
  57.                         continue ;  
  58.   
  59.                 }  
  60.   
  61.    
  62.   
  63.                 ast_cli(fd, FORMAT, iterator->name,   
  64.   
  65.                         iterator->secret,   
  66.   
  67.                         iterator->accountcode,  
  68.   
  69.                         iterator->context,  
  70.   
  71.                         iterator->ha ? "Yes"  :  "No" ,  
  72.   
  73.                         nat2str(ast_test_flag(&iterator->flags[0], SIP_NAT)));  
  74.   
  75.                 ASTOBJ_UNLOCK(iterator);  
  76.   
  77.         } while  (0)  
  78.   
  79.         );  
  80.   
  81.    
  82.   
  83.         if  (havepattern)  
  84.   
  85.                 regfree(®exbuf);  
  86.   
  87.    
  88.   
  89.         return  RESULT_SUCCESS;  
  90.   
  91. #undef FORMAT   
  92.   
  93. }  

. 核心文件中 cli 机制, main 函数调用 cli 流程

1.cli.c cli.h 分析

·       1. ast_cli: 用 于在 cli 界面上显示信息。 源 码

void ast_cli(int fd, char *fmt, ...)

fd 为文件句柄, fmt 和后面的参数是要 显示的信息。

·       2. ast_cli_command :用于具体处理 cli 上输入的命令,选择对应的 handler 处理。 源码
int ast_cli_command(int fd, const char *s)

fd 为文件句柄 ,s arg ,但是 s 是将所有 argv 拼起来的一个字 符串,在函数内部调用 parse_args 将其分离还原。

·       3. ast_cli_command_multiple 循 环调用 ast_cli_command 执行命令, 源 码

  1. int  ast_cli_command_multiple( int  fd,  size_t  size,  const   char  *s)  
  2.   
  3. {  
  4.   
  5.         char  cmd[512];  
  6.   
  7.         int  x, y = 0, count = 0;  
  8.   
  9.    
  10.   
  11.         for  (x = 0; x < size; x++) {  
  12.   
  13.                 cmd[y] = s[x];  
  14.   
  15.                 y++;  
  16.   
  17.                 if  (s[x] ==  '/0' ) {  
  18.   
  19.                         ast_cli_command(fd, cmd);  
  20.   
  21.                         y = 0;  
  22.   
  23.                         count++;  
  24.   
  25.                 }  
  26.   
  27.         }  
  28.   
  29.         return  count;  
  30.   
  31. }  

2.asterisk.c 分析

·       4. netconsole, main/asterisk.c 中,它为线程函数,调用了 ast_cli_command_multiple

static void *netconsole(void *vconsole)
vconsole
是一个 struct console 结构,是一个 console 的结构,
ast_cli_command_multiple(con->fd, res, tmp);
res
是要处理的命令的数目, tmp 是命令字符串(多条命令在一起,以 '/0' 分隔)。 它结构中的 fd 即为传递给最终 ast_cli fd

·       5. listener, 它调用了 netconsole ,在 main/asterisk.c 中,为线程函数,按照 consoles 的数目,启动多个 netconsole 的线程,传递参数为 console

  1. static   void  *listener( void  *unused)  
  2.   
  3. {  
  4.   
  5.   ...  
  6.   
  7.         for  (;;) {  
  8.   
  9.          ...  
  10.   
  11.                 s = poll(fds, 1, -1);  
  12.   
  13.                 if  (s < 0) {  
  14.   
  15.                         if  (errno != EINTR)  
  16.   
  17.                                 ast_log(LOG_WARNING, "Accept returned %d: %s/n" , s, strerror(errno));  
  18.   
  19.                 } else  {  
  20.   
  21.                         for  (x = 0; x < AST_MAX_CONNECTS; x++) {  
  22.   
  23.                                 if  (consoles[x].fd < 0) {  
  24.   
  25.                                         ...  
  26.   
  27.                                         if  (ast_pthread_create_background(&consoles[x].t, &attr, netconsole, &consoles[x])) {  
  28.   
  29.                                                 ast_log(LOG_ERROR, "Unable to spawn thread to handle connection: %s/n" , strerror(errno));  
  30.   
  31.                                                 close(consoles[x].p[0]);  
  32.   
  33.                                                 close(consoles[x].p[1]);  
  34.   
  35.                                                 consoles[x].fd = -1;  
  36.   
  37.                                                 fdprint(s, "Server failed to spawn thread/n" );  
  38.   
  39.                                                 close(s);  
  40.   
  41.                                         }  
  42.   
  43.                                  ...  
  44.   
  45.                                 }  
  46.   
  47.                           ...  
  48.   
  49.                           }  
  50.   
  51.                    }  
  52.   
  53.             }  
  54.   
  55.   ...  
  56.   
  57. }  

·       6.ast_makesocket ,在 main/asterisk.c , 它启动了 listener线程

它又被 main 函数 调 用

. 核心文件中 cli 机制,向外提供的 cli 注册接口

·       1.ast_cli_register __ast_cli_register !__ast_cli_register源码 ast_cli_register源码 不知为什么有 ast_cli_register 调用 __ast_cli_register 这一层,未看出有什么作用,而且在 unregister 中没 有这 样一层调用。 __ast_cli_register 具体处理将一个 struct ast_cli_entry *e 注册到 helpers 上,它调用 find_cli 寻找是否已经注册。没有注册使用 AST_LIST_INSERT_TAIL(&helpers, e, list) 或者 AST_LIST_INSERT_BEFORE_CURRENT(&helpers, e, list) 将其注册到 helpers 上。

·       2.ast_cli_unregister 。反注册一个 struct ast_cli_entry *e

·       3.find_cli, 在链表 buildins helpers 上寻找一个符合条件的 struct ast_cli_entry *e 。具体调用 cli_next 遍历这 两个链表。在这里还有匹配度的选择,可以进行最优匹配或者最先匹配。

. 外围模块中 cli 机制

    外围模块需要使用 cli 机制:

·       1. 首先声明一个 struct ast_cli_entry cli_sip[] , 将自己具有的可 cli 调用的函数写在其中,包括相应的说明等,如 #具 体 ast_cli_entry例子

·       2. load_module 中调用 ast_cli_register_multiple 注册 cli_sip[] helpers 。如 :

  1. static   int  load_module( void )  
  2.   
  3. {  
  4.   
  5.         ...   
  6.   
  7.         /* Register all CLI functions for SIP */   
  8.   
  9.         ast_cli_register_multiple(cli_sip, sizeof (cli_sip)/  sizeof ( struct  ast_cli_entry));  
  10.   
  11.         ...  
  12.   
  13. }  

  四.参照其思想简化实现

    在实现中根据自身的需求和时间决定暂时先简化实现一个cli,主要需要其动态注册功能,但是对于其中一些具体实现进行简化,如命令cmd暂时只接受一个字 符串,只使用最快查找等。但对于开发接口都尽量保留,以便后期开发。对于其运用到其他运行支撑模块如lock.h,linkedlist.h 等进行实现,但对于io模块暂时未实现。

    测试代码,功能达到要求。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值