VTYSH源码解析(一)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/sinat_36544290/article/details/82348028

VTYSH源码解析

what is vtysh?

vtysh是一个命令行解析引擎。后来发展成了zebra项目,现在zebra已经不再被维护,现在主要维护它的分支项目quagga项目。具体项目历史比较久远,可能很多老码才知道吧。
本来是想学习zebra项目的,但是这个项目源码框架稍微有点复杂,所以从简单的命令行接口入手vtysh。之后再慢慢理解zebra的运行机制。

vtysh源码

这个代码是我从网上找的,但是忘记链接地址了,所以我将代码上传到了csdn上。当然github上也有很多,大部分都是从zebra或者quagga上摘出的框架,代码结构不太好。下载地址

源码详解

vtysh源码还是比较简单的,主要需要理解的有两部分:
1. vtysh的命令结构体
2. readline lib的使用
理解这两部分,我们自己也可以快速开发出一个简单的shell,当然我记得《unix环境高级编程》前几个例程,有一个就是shell的实现。

主函数解析

我们先看以下vtysh_main.c

/* VTY shell main routine. */
int main (int argc, char **argv, char **env)
{
    char *line;
    int opt;
    int eval_flag = 0;
    int boot_flag = 0;
    char *eval_line = NULL;
    char *config_file = CONFIG_DIR "/" CONFIG_FILE;

    if(getenv("VTYSH_CONFIG"))
        config_file = getenv("VTYSH_CONFIG");
    while (1) 
    {
        opt = getopt_long (argc, argv, "be:c:hv", longopts, 0);
        if (opt == EOF)
            break;
        switch (opt) 
        {
            case 0:
                break;
            case 'b':
                boot_flag = 1;
                break;
            case 'e':
                eval_flag = 1;
                eval_line = optarg;
                break;
            case 'h':
                usage (argv[0], 0);
                break;
            case 'c':
                config_file = optarg;
                break;
            case 'v':
                printf("Ver:%s %s\n", __DATE__, __TIME__);
                exit(0);
            default:
                usage (argv[0], 1);
                break;
        }
    }

    /* Signal and others. */
    signal_init ();

    /* Init config. */
    config_init();

    /* Init the cmd */
    cmd_init();

    /* Init the vtysh */
    vtysh_init_vty ();

    /* Install command and node view */
    cmd_parse_init();

    //TODO load the dynamic so

    /* sort the node */
    cmd_sort_node();

    /* If eval mode */
    if (eval_flag)
    {
        vtysh_execute("enable");
        vtysh_execute("config terminal");
        exit(vtysh_execute(eval_line));
    }

    /* Boot startup configuration file. */
    if (boot_flag)
        exit(vtysh_boot_config (config_file));

    in_show_welcome();
    host.config = config_file;
    vtysh_load_config(config_file);

    /* Main command loop. */
    while ((line = vtysh_readline()) != NULL)
        vtysh_execute (line);
    printf ("\n");

    exit (0);
}

逻辑很顺,一条线,甚至没有几个if else,解析命令行传参->初始化信号->初始化配置变量->初始化cmd节点()->vty初始化(模式初始化、readline初始化等)->自定义cmd挂载->命令排序->载入config文件->阻塞等待命令输入()。

命令结构解析

这里写图片描述

具体关键结构体如下:

/* struct for vector */
struct _vector 
{
  unsigned int max;     /* max number of used slot */
  unsigned int alloced;     /* number of allocated slot */
  void **index;         /* index to data */
};
typedef struct _vector *vector;
/* There are some command levels which called from command node. */
enum node_type 
{
    AUTH_NODE,          /* Authentication mode of vty interface. */
    VIEW_NODE,          /* View node. Default mode of vty interface. */
    AUTH_ENABLE_NODE,   /* Authentication mode for change enable. */
    ENABLE_NODE,        /* Enable node. */
    CONFIG_NODE,        /* Config node. Default mode of config file. */

    NAC_NODE,           /* for nac */
    USER_NODE,          /* for user */
    VTY_NODE            /* Vty node. */
};

/* Node which has some commands and prompt string and configuration
   function pointer . */
struct cmd_node 
{
  /* Node index. */
  enum node_type node;      

  /* Prompt character at vty interface. */
  char *prompt;         

  /* Is this node's configuration goes to vtysh ? */
  int vtysh;

  /* Node's configuration write function */
  int (*func) (struct vty *);

  /* Vector of this node's command list. */
  vector cmd_vector;    
};
/* Structure of command element. */
struct cmd_element 
{
  char *string;         /* Command specification by string. */
  int (*func) (struct cmd_element *, struct vty *, int, char **);
  char *doc;            /* Documentation of this command. */
  int daemon;           /* Daemon to which this command belong. */
  vector strvec;        /* Pointing out each description vector. */
  int cmdsize;          /* Command index count. */
  char *config;         /* Configuration string */
  vector subconfig;     /* Sub configuration string */
};

struct _vector index –> struct cmd_node * (enum node_type最大值)个数 vector cmd_vector -> struct cmd_element

这里有一个疑问,命令模式的多层嵌套是怎样实现的呢?
很多人看到cmd_vector以为是结构体不断嵌套,其实不是,vtysh的命令结构只有三层vector->node->cmd_vector。
命令模式之间的嵌套其实是node之间的切换,所有的node都挂在根vector下,比如说config模式是在enable模式下,这只是逻辑上的顺序,实际上config和enable都是在根vector下,只不过在切换到config模式时,是通过命令config将模式切换过去的。所以大家不要将这个结构想的复杂化。

猜你喜欢

转载自blog.csdn.net/sinat_36544290/article/details/82348028