quagga 命令定义的代码分析--从定义到实现

本文大多数内容转自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);

还需要看看怎么扑捉命令的。

你可能感兴趣的:(quagga,路由软件,开源软件,linux)