最近写一些工具库,需要远程命令行调试(cli)功能,原有的一个cli模块是将接收处理的命令具体实现在cli模块中,其他模块需要修改添加自己的cli命令都需要去修改cli模块代码,觉得模块间耦合度太高,在看asterisk源码时记得它的cli模块是一种注册机制,cli模块主要对外提供注册和反注册接口,其他模块实现一组特定的cli entry,再调用注册和反注册函数进行操作。可以动态的控制远程可操作的cli命令,觉得比较好,分析了一下。并参照它的思想简化的实现了一个满足自己需求的cli模块。
以下文章原是写在trac的wiki与代码结合使用的,所以部分超链接在此网页中无法使用。
整体流程分析图
关键结构体ast_cli_entry:include/asterisk/cli.h
具体ast_cli_entry例子
具体例子: chan_sip.c中
模块中如何使用见 三.外围模块中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,显示使用范围。具体代码:
一.核心文件中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执行命令,源码
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。
· 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机制
· 1.首先声明一个struct ast_cli_entry cli_sip[],将自己具有的可cli调用的函数写在其中,包括相应的说明等,如#具体ast_cli_entry例子。
· 2.在load_module中调用ast_cli_register_multiple注册cli_sip[]到helpers。如:
在实现中根据自身的需求和时间决定暂时先简化实现一个cli,主要需要其动态注册功能,但是对于其中一些具体实现进行简化,如命令cmd暂时只接受一个字符串,只使用最快查找等。但对于开发接口都尽量保留,以便后期开发。对于其运用到其他运行支撑模块如lock.h,linkedlist.h 等进行实现,但对于io模块暂时未实现。
测试代码,功能达到要求。