本文大多数内容转自http://blog.chinaunix.net/uid-20788636-id-1841428.html,部分内容自己读过代码之后觉得需要多加注意的,也记录下来
在quagga中有很多的命令,利用这些下面分析一下命令是怎么被读取和执行的。在quagga中定义的命令都是利用宏定义实现的,这个宏定义还是有点复杂,下面是用的比较多的一个命令的宏定义语句( 还有其他宏定义的命令实现,如DEFUN_ATTR、DEFSH、DEFUNSH,其实都是DEFUN_CMD_FUNC_DECL、DEFUN_CMD_ELEMENT、DEFUN_CMD_FUNC_TEXT三个的组合了)。
#define DEFUN(funcname, cmdname, cmdstr, helpstr) \
DEFUN_CMD_FUNC_DECL(funcname) \
DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, 0, 0) \
DEFUN_CMD_FUNC_TEXT(funcname)
第一个funcname是函数的名称,第二个是注册的命令的名字,第三个是在vtysh终端下输入的命令字符串,第四个是帮助信息,当输入“?”时,显示出来,如果多个字符串(例如hellow word),那么则在helpstr中用多个字符串表示,即helpstr是个字符串数组。
#define DEFUN_CMD_FUNC_DECL(funcname) \
static int funcname (struct cmd_element *, struct vty *, int, const char *[]); //注意,这个后面是有分号的,即只是定义一个函数没有实现
#define DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, attrs, dnum) \
struct cmd_element cmdname = \ //这个只是实现一个结构体实体,这个结构体会在实现每条命令用到
{ \
.string = cmdstr, \
.func = funcname, \
.doc = helpstr, \
.attr = attrs, \
.daemon = dnum, \
};
#define DEFUN_CMD_FUNC_TEXT(funcname) \
static int funcname \
(struct cmd_element *self __attribute__ ((unused)), \
struct vty *vty __attribute__ ((unused)), \
int argc __attribute__ ((unused)), \
const char *argv[] __attribute__ ((unused)) ) //注意,这个后面是没有分号的,也就是说可以加{...}实现这个函数!
//在这里我终于理解宏就相当于组合起来然后打印出来再执行一般...
假设我们这里有一个下面宏定义:
DEFUN (vtysh_show_hello, vtysh_show_hello_cmd,
"show hello",
" hello1\n"
" hello2\n"
)
{
printf("hello\n");
return CMD_SUCCESS;
}
看一下它是如何展开的:
首先看一下下面的这个结构体,在宏DEFUN_CMD_ELEMENT中使用到。
struct cmd_element
{
const char *string; /* Command specification by string. */
int (*func) (struct cmd_element *, struct vty *, int, const char *[]);
const char *doc; /* Documentation of this command. */
int daemon; /* Daemon to which this command belong. */
vector strvec; /* Pointing out each description vector. */
unsigned int cmdsize; /* Command index count. */
char *config; /* Configuration string */
vector subconfig; /* Sub configuration string */
u_char attr; /* Command attributes */
};
#define DEFUN(funcname, cmdname, cmdstr, helpstr) \
int funcname (struct cmd_element *, struct vty *, int, char **); \
struct cmd_element cmdname = \
{ \
cmdstr, \
funcname, \
helpstr \
}; \
int funcname \
(struct cmd_element *self, struct vty *vty, int argc, char **argv)
//上面的宏定义由于DEFUN_CMD_FUNC_TEXT是排最后面的(就是没有分号那个),因此后面加上{...}后,就能实现那样一个函数,十分巧妙的利用结构体定义函数,与类的概念、实体的概念就十分类似了
还有一个结构struct vty应定义在vty.h中。根据宏定义DEFUN,可展开如下:
int vtysh_show_hello (struct cmd_element *, struct vty *, int, char **);
struct cmd_element vtysh_show_hello_cmd =
{
"show hello",
vtysh_show_hello,
" hello1\n hello2\n"
};
int vtysh_show_hello (struct cmd_element *self, struct vty *vty, int argc, char **argv) //这是利用无分号实现的函数,但是为何不会重名重定义吗?
{
printf("hello\n");
return CMD_SUCCESS;
}
在vtysh_main.c中,包含有一个main()函数,
假设我们定义了一个函数的宏定义,这里摘自quagga里面。
DEFUN (show_version,
show_version_cmd,
"show version",
SHOW_STR
"Displays zebra version\n")
{
vty_out (vty, "Quagga %s (%s).%s", QUAGGA_VERSION, host.name?host.name:"",
VTY_NEWLINE);
vty_out (vty, "%s%s", QUAGGA_COPYRIGHT, VTY_NEWLINE);
return CMD_SUCCESS;
}
上面已经分析了这个函数是怎么一步一步展开的,我们在定义一个命令的时候,也必须要在某一个节点下安装这个命令。使用下面的语句。
在cmd_init(int terminal)函数中。
整个quagga中的命令节点是这样的:cmdver->cmd_node->cmd_element。
其中cmdver是一个vector结构体:
struct _vector
{
unsigned int active; /* number of active slots */
unsigned int alloced; /* number of allocated slot */
void **index; /* index to data */
};
利用空指针可以指向cmd_node的结构体,同理cmd_node里面也有一个vector的变量指向cmd_element,统统形成一个树形结构。
install_node (&view_node, NULL);
void
install_node (struct cmd_node *node,
int (*func) (struct vty *))
{
vector_set_index (cmdvec, node->node, node);
node->func = func;
node->cmd_vector = vector_init (VECTOR_MIN_SIZE);
}
Cmdvec变量在cmd_init函数一开始时进行了初始化,
cmdvec = vector_init (VECTOR_MIN_SIZE);
在vecto_init函数中分配了内存,并且返回vector结构。一个命令要在某个结点下安装。
install_element (VIEW_NODE, &show_version_cmd);
/* Install a command into a node. 安装一个命令到一个节点*/
void
install_element (enum node_type ntype, struct cmd_element *cmd)
{
struct cmd_node *cnode;
/* cmd_init hasn't been called */
if (!cmdvec)
return;
cnode = vector_slot (cmdvec, ntype);
if (cnode == NULL)
{
fprintf (stderr, "Command node %d doesn't exist, please check it\n",
ntype);
exit (1);
}
vector_set (cnode->cmd_vector, cmd);//在该节点下最小的空位置设置,cmd_element结构体的值,在这处理输入的命令时,调用相对应的处理命令
cmd->strvec = cmd_make_descvec (cmd->string, cmd->doc);
cmd->cmdsize = cmd_cmdsize (cmd->strvec);
}
这样加上上面用宏定义的命令和注册命令的流程,一个完整的命令就算完成了。
当我们在命令行中输入一个命令时,我们看一下它的流程是什么 ,也就是最后是怎么调用到我们定义的命令的处理函数。在vty_main.c函数中,有一个main()函数,在这个函数中对输入的命令行进行了处理。我们只看其中最主要的一个部分,也就是一个无限循环,在这个无限循环中我们可以看到vtysh_rl_gets()函数不断的读取命令行中的输入,如果有输入的话,就会调用vtysh_execute (line_read);函数对输入的命令行进行处理
* Main command loop. */
while (vtysh_rl_gets ())
vtysh_execute (line_read);
void
vtysh_execute (const char *line)
{
vtysh_execute_func (line, 1);
}
在static void
vtysh_execute_func (const char *line, int pager)
{
……………………
saved_ret = ret = cmd_execute_command (vline, vty, &cmd, 1);
…………………
}
int
cmd_execute_command (vector vline, struct vty *vty, struct cmd_element **cmd, int vtysh)
{
……………….
saved_ret = ret = cmd_execute_command_real (vline, vty, cmd);//真正的命令行处理函数
………………
}
在这个函数中,调用了我们定义的命令的处理函数
static int
cmd_execute_command_real (vector vline, struct vty *vty,
struct cmd_element **cmd)
{
…………………………..
//找到与已经安装的命令匹配的命令,
for (i = 0; i < vector_active (cmd_vector); i++)
if ((cmd_element = vector_slot (cmd_vector, i)))
{
if (match == vararg_match || index >= cmd_element->cmdsize)
{
matched_element = cmd_element;
#if 0
printf ("DEBUG: %s\n", cmd_element->string);
#endif
matched_count++;
}
else
{
incomplete_count++;
}
}
/* Argument treatment 对输入的参数进行处理*/
varflag = 0;
argc = 0;
for (i = 0; i < vector_active (vline); i++)
{
if (varflag)
argv[argc++] = vector_slot (vline, i);
else
{
vector descvec = vector_slot (matched_element->strvec, i);
if (vector_active (descvec) == 1)
{
struct desc *desc = vector_slot (descvec, 0);
if (CMD_VARARG (desc->cmd))
varflag = 1;
if (varflag || CMD_VARIABLE (desc->cmd) || CMD_OPTION (desc->cmd))
argv[argc++] = vector_slot (vline, i);
}
else
argv[argc++] = vector_slot (vline, i);
}
if (argc >= CMD_ARGC_MAX)
return CMD_ERR_EXEED_ARGC_MAX;
}
/* For vtysh execution. */
if (cmd)
*cmd = matched_element;
if (matched_element->daemon)
return CMD_SUCCESS_DAEMON;
return (*matched_element->func) (matched_element, vty, argc, argv);//最后调用处理函数,也就是我们使用DEFUN宏定义的命令
}
}
其实最后就是执行了之前宏定义的函数实现命令。但是发现其他各个模块,例如ripd、ospfd中不是使用cmd_execute_command实现执行命令的,而是这样一个循环:
/* Start finite state machine, here we go! */
while (thread_fetch (master, &thread))
thread_call (&thread);
还需要看看怎么扑捉命令的。