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

2 篇文章 0 订阅
2 篇文章 0 订阅

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

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

  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。
Quagga和Zebra是两个开源的网络路由软件套件。Zebra是一个基于BSD授权的路由软件套件,提供了OSPF、BGP、RIP、RIPng等协议的实现。而Quagga则是Zebra的一个分支,目前由社区维护,支持更多的协议,包括IS-IS、OSPFv3、PIM、RIPng等。 下面我们来简要分析一下Quagga和Zebra的源码结构: 1. Quagga Quagga的源码主要分为以下几个部分: - bgpd:BGP协议的实现。 - ospfd:OSPF协议的实现。 - ospf6d:OSPFv3协议的实现。 - ripd:RIP协议的实现。 - ripngd:RIPng协议的实现。 - isisd:ISIS协议的实现。 - pimd:PIM协议的实现。 - vtysh:用户交互界面。 其中,每个协议的实现都在对应的目录下,包括配置文件的解析、协议控制块的管理、路由表的维护等。而vtysh则是一个基于GNU Readline的交互式命令行界面,用于用户与Quagga的交互。 2. Zebra Zebra的源码主要分为以下几个部分: - zebra:路由守护进程,负责各个协议之间的协调和管理。 - lib:公共库,包括路由表、控制块、事件机制等。 - ospf:OSPF协议的实现。 - ospf6:OSPFv3协议的实现。 - bgp:BGP协议的实现。 - ripd:RIP协议的实现。 - ripng:RIPng协议的实现。 在Zebra中,路由表、控制块等公共部分的实现都在lib目录下,而各个协议的实现则在对应的目录下。 总体来说,Quagga和Zebra的源码结构比较清晰,各个协议之间的代码实现相对独立,方便开发者进行二次开发和定制。同时,它们的协议实现也比较完善,可以满足不同场景下的路由需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值