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将模式切换过去的。所以大家不要将这个结构想的复杂化。