/****************************************************************************** * file lib/management/cmd.c * author YuLiang * version 1.0.0 * date 09-Oct-2021 * brief This file provides all the command operation functions. * ****************************************************************************** * Attention * *

© COPYRIGHT(c) 2021 LandPower

* * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * 3. Neither the name of LandPower nor the names of its contributors may be used to * endorse or promote products derived from this software without specific * prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * ******************************************************************************/ /* Includes ------------------------------------------------------------------*/ #ifdef HAVE_CONFIG_H #include "config.h" #endif /* 标准C库头文件. */ #include #include #include #include #include #include #include #include #include //#include #include #include #include /* 外部程序库头文件. */ #include #include /* 用户代码头文件. */ #include "vty.h" #include "cmd.h" #include "sockunion.h" #include "pd_cpld.h" #include "list.h" /* Private define ------------------------------------------------------------*/ #define VTYSH_CONFIG_R_BUF 512 /* Private macro -------------------------------------------------------------*/ /* Private typedef -----------------------------------------------------------*/ /* Private variables ---------------------------------------------------------*/ /* 主机信息结构. */ host_t host; device_info_t device_info; int8_t is_system_init; /* 用于补齐回车,比如使用'?'或者'TAB'. */ static desc_t cmd_desc_enter; static char *cmd_enter = NULL; /* 多线程同时操作配置文件的互斥锁. */ static pthread_mutex_t m_config_mutex; /* 命令行所有节点列表. */ array_t *cmd_nodes = NULL; char *server_path = "server.socket"; char *client_path = "client.socket"; vtycmd_t *vtycmd; struct sockaddr_un cliun, serun; int socklen; #define MAXLINE 504 extern cmd_node_t pd_port_node; static cmd_node_t common_node = { COMMON_NODE, USERNAME_NODE, 1, "%s(common)# ", }; static cmd_node_t username_node = { USERNAME_NODE, USERNAME_NODE, 0, "Username: ", }; static cmd_node_t password_node = { PASSWORD_NODE, USERNAME_NODE, 0, "Password: ", }; static cmd_node_t enable_node = { ENABLE_NODE, USERNAME_NODE, 1, "%s# ", }; static cmd_node_t config_node = { CONFIG_NODE, ENABLE_NODE, 1, "%s(config)# ", }; /* shell vty结构体. */ vty_t *vtysh; /* 保存readline命令行内容的buf. */ static char *line_read; /* 保存readline命令行历史的文件. */ const char history_file[] = "./command_line_history"; /* cli线程pid. */ static pthread_t _cmd_pid; extern int32_t vtysh_config_save_bak(int idx); /* 根据传入的字符串,返回相应log模块. */ uint32_t _mode_match(const char *s) { if (0 == strncmp(s, "f", 1)) return LOG_MODE_FILE; else if(0 == strncmp(s, "s", 1)) return LOG_MODE_STDOUT; else if(0 == strncmp(s, "m", 1)) return LOG_MODE_MONITOR; return LOG_MODE_MAX; } void vtycmd_send() { if (vtysh->buf == NULL) return; vtycmd->buf_write.node = vtysh->node; memset(&vtycmd->buf_write, 0, sizeof(vtycmd->buf_write)); strcpy(vtycmd->buf_write.cmd, vtysh->buf); sendto(vtycmd->socket, (char*)(&vtycmd->buf_write), strlen(vtycmd->buf_write.cmd) + 8, 0, (struct sockaddr *)&serun, socklen); return; } /* 设置设备名称. */ CMD(mac_set, mac_set_cmd, "mac eth0 WORD", "Set mac\n" "HH:HH:HH:HH:HH:HH\n") { return CMD_SUCCESS; uint8_t mac[MAC_ADDR_LEN]; if (str_to_mac((char*)argv[0], mac) != E_NONE) { vty_out(vty, "Mac addr is not valid!%s", VTY_NEWLINE); return CMD_ERR_INCOMPLETE; } memcpy(device_info.mac, mac, MAC_ADDR_LEN); vtysh_device_save(); vtysh_eth0_save(); return CMD_SUCCESS; } /* 设置设备名称. */ CMD(id_set_cli, id_set_cmd, "id WORD", "Set id\n" "DDDD.DDDD\n") { return CMD_SUCCESS; uint32_t id[2] = {0}; if (str_to_id((char*)argv[0], id) != E_NONE) { vty_out(vty, "ID is not valid!%s", VTY_NEWLINE); return CMD_ERR_INCOMPLETE; } device_info.id_major = id[0]; device_info.id_minor = id[1]; vtysh_device_save(); return CMD_SUCCESS; } /* 设置设备名称. */ CMD(hostname_set, hostname_set_cmd, "hostname WORD", "Set hostname\n" "hostname\n") { return CMD_SUCCESS; snprintf(host.name, FILE_NAME_LEN, "%s", argv[0]); return CMD_SUCCESS; } /* 基本命令: 进入enable模式. */ CMD(config_enable, config_enable_cmd, "enable", "Turn on privileged mode command\n") { vty->node = ENABLE_NODE; return CMD_SUCCESS; } /* 基本命令:进入shell模式. */ CMD(bash_open, bash_open_cmd, "bash", "Open a shell terminal\n") { return CMD_SUCCESS; if (system("bash") <= 0) { vty_out(vty, "Entry bash error!%s", VTY_NEWLINE); return CMD_ERR_INCOMPLETE; } return CMD_SUCCESS; } /* 设置终端的显示模式. */ CMD(terminal_monitor, terminal_monitor_cmd, "terminal monitor", "Set terminal line parameters\n" "Copy debug output to the current terminal line\n") { vty->monitor = 1; return CMD_SUCCESS; } /* 设置终端的显示模式. */ CMD(no_terminal_monitor, no_terminal_monitor_cmd, "no terminal monitor", NO_STR "Set terminal line parameters\n" "Copy debug output to the current terminal line\n") { vty->monitor = 0; return CMD_SUCCESS; } /* 基本命令:进入config模式. */ CMD(config_terminal, config_terminal_cmd, "configure", "Configuration from vty interface\n") { return CMD_SUCCESS; if (vty_config_lock(vty)) vty->node = CONFIG_NODE; else { vty_out(vty, "VTY configuration is locked by other VTY%s", VTY_NEWLINE); return CMD_WARNING; } return CMD_SUCCESS; } /* 基本命令:显示版本信息. */ CMD(show_version, show_version_cmd, "show version", SHOW_STR "Displays zebra version\n") { return CMD_SUCCESS; vty_version_print(vty); vty_out(vty, "%s", VTY_NEWLINE); return CMD_SUCCESS; } /* 基本命令:退出当前模式. */ CMD(exit_node, exit_node_cmd, "exit", "Exit current mode\n") { return CMD_SUCCESS; cmd_node_t *node = cmd_node_get(vty->node); if (NULL == node) return CMD_WARNING; vty->node = node->up_node; if (CONFIG_NODE == node->node) vty_config_unlock(vty); return CMD_SUCCESS; } ALIAS(exit_node, quit_node_cmd, "quit", "Exit current mode\n") CMD(logoff_cli, logoff_cmd, "logoff", "Exit app\n") { return CMD_SUCCESS; exit(-1); return CMD_SUCCESS; } CMD(reboot_node, reboot_cmd, "reboot", "reset system\n") { return CMD_SUCCESS; reboot_system(DBG_M_CLI, BOOT_SYSTEM_RESET); return CMD_SUCCESS; } /* 配置log模块的输出级别. */ CMD(config_log_level, config_log_level_cmd, "log (stdout|file|monitor) "LOG_LEVELS, "Logging control\n" "Set stdout logging level\n" "Set file logging level\n" "Set terminal line (monitor) logging level\n" LOG_LEVEL_DESC) { return CMD_SUCCESS; LOG_MODE_E log_mode = _mode_match(argv[0]); LOG_LVL_E level = log_level_get_by_name(argv[1]); if (LOG_LVL_MAX == level) return CMD_ERR_NO_MATCH; if (log_mode >= LOG_MODE_MAX) return CMD_ERR_NO_MATCH; log_set_level(log_mode, level); return CMD_SUCCESS; } /* 配置log模块的输出级别. */ CMD(no_config_log_level, no_config_log_level_cmd, "no log (stdout|file|monitor) "LOG_LEVELS, NO_STR "Logging control\n" "Set stdout logging level\n" "Set file logging level\n" "Set terminal line (monitor) logging level\n" LOG_LEVEL_DESC) { return CMD_SUCCESS; uint32_t log_mode = _mode_match(argv[0]); int32_t level = log_level_get_by_name(argv[1]); if (LOG_LVL_MAX == level) return CMD_ERR_NO_MATCH; if (log_mode >= LOG_MODE_MAX) return CMD_ERR_NO_MATCH; log_unset_level(log_mode, level); return CMD_SUCCESS; } /* 默认串口输出日志 */ CMD(show_log_default, show_log_default_cmd, "show log", SHOW_STR LOG_STR) { return CMD_SUCCESS; log_show( LOG_DEFAULT_SHOW_CNT, LOG_LVL_MAX, NULL ); return CMD_SUCCESS; } CMD(show_log_level, show_log_level_cmd, "show log "LOG_LEVELS, SHOW_STR LOG_STR LOG_LEVEL_DESC) { return CMD_SUCCESS; int32_t level = log_level_get_by_name( argv[0] ); log_show( LOG_DEFAULT_SHOW_CNT, level, NULL ); return CMD_SUCCESS; } CMD(show_log_cnt, show_log_cnt_cmd, "show log <0-5000>", SHOW_STR LOG_STR "Count of logs, will show all as it == 0, can enter the keyword of logs afterword\n") { return CMD_SUCCESS; int32_t cnt = atoi( argv[0] ); log_show( cnt, LOG_LVL_MAX, NULL ); return CMD_SUCCESS; } CMD(show_log, show_log_cmd, "show log <0-5000> WORD", SHOW_STR LOG_STR "Count of logs, will show all as it == 0, can enter the keyword of logs afterword\n" "Keyword of logs\n") { return CMD_SUCCESS; int32_t cnt = atoi( argv[0] ); log_show( cnt, LOG_LVL_MAX, argv[1] ); return CMD_SUCCESS; } CMD(host_ip_set, host_ip_set_cmd, "host ip A.B.C.D A.B.C.D", "Host ip for produce\n" "IPv4 type address\n" "Ip address of host\n" "Ip mask of host\n") { return CMD_SUCCESS; vtysh_host_addr_set((char*)argv[0], (char*)argv[1]); return CMD_SUCCESS; } CMD(default_route_set, default_route_set_cmd, "default route A.B.C.D", "Default route set\n" "Default route\n" "Address of default route\n") { return CMD_SUCCESS; vtysh_gateway_set((char*)argv[0]); return CMD_SUCCESS; } CMD(factory_date_set, factory_date_set_cmd, "factory date WORD WORD", "factory\n" "factory date\n" "date\n" "time\n") { return CMD_SUCCESS; uint32_t t = 0; if (time_str_to_long((char*)argv[0], (char*)argv[1], &t) != E_NONE) { vty_out(vty, "Factory date set error!%s", VTY_NEWLINE); return CMD_SUCCESS; } device_info.factory_date = t; vtysh_device_save(); return CMD_SUCCESS; } CMD(deployment_date_set, deployment_date_set_cmd, "deployment date WORD WORD", "deployment\n" "deployment date\n" "date dddd-dd-dd\n" "time dd:dd:dd\n") { return CMD_SUCCESS; uint32_t t = 0; if (time_str_to_long((char*)argv[0], (char*)argv[1], &t) != E_NONE) { vty_out(vty, "Factory date set error!%s", VTY_NEWLINE); return CMD_SUCCESS; } device_info.deployment_date = t; vtysh_device_save(); return CMD_SUCCESS; } /* 把当前配置写入配置文件. */ CMD(config_write_file, config_write_file_cmd, "write file", "Write running configuration to memory, network, or terminal\n" "Write to configuration file\n") { return CMD_SUCCESS; if (vtysh_config_save() != E_NONE) { return CMD_WARNING; } return CMD_SUCCESS; } /* 在终端上显示当前配置信息. */ CMD(config_write_terminal, show_running_config_cmd, "show running-config", SHOW_STR "running configuration\n") { return CMD_SUCCESS; uint32_t i = 0; cmd_node_t *node = NULL; vty_out(vty, "Current configuration:%s", VTY_NEWLINE); vty_out(vty, "!%s", VTY_NEWLINE); for(i = 0; i < array_active(cmd_nodes); i++) if ((node = array_get(cmd_nodes, i)) && node->func) { if ((*node->func)(vty)) vty_out(vty, "!%s", VTY_NEWLINE); } vty_out(vty, "end%s", VTY_NEWLINE); return CMD_SUCCESS; } /* 在终端上显示当前配置信息. */ CMD(enable_test, enable_test_cmd, "test <0-3>", ROUTE_STR ROUTE_STR) { return CMD_SUCCESS; uint16_t utc = 0; uint16_t temp = 0; temp = strtol(argv[0], NULL, 10); //utc = 0; //cpld_write(0xc, 1, &utc); //sleep(1); cpld_write(0xb, 1, &temp); //utc = 3; //cpld_write(0xc, 1, &utc); //sleep(1); return CMD_SUCCESS; } /* Private function prototypes -----------------------------------------------*/ /* Internal functions --------------------------------------------------------*/ /* 用于自动补齐命令行关键字. */ void _vtysh_append_word(array_t *cmd_line, char *word, int32_t status) { uint32_t index = array_active(cmd_line) - 1; uint32_t len = 0; uint32_t start = 0; if (NULL == array_get(cmd_line, index)) rl_insert_text(word); else { len = strlen(array_get(cmd_line, index)); start = rl_end - len; rl_begin_undo_group(); rl_delete_text(start, rl_end); rl_point = start; rl_insert_text(word); rl_end_undo_group(); } if (CMD_COMPLETE_FULL_MATCH == status) rl_insert_text(" "); } /* config模式配置保存函数: vty -- 相应的终端 */ int32_t _config_write_host(vty_t *vty) { array_t *configs = config_node.configs; cmd_save_config_f *func = NULL; uint16_t i = 0; if (host.name) vty_out(vty, "hostname %s%s", host.name, VTY_NEWLINE); vty_out(vty, "!%s", VTY_NEWLINE); /* 其他配置保存 */ for(i = 0; i < array_active(configs); i++) { func = array_lookup(configs, i); if (!func) { continue; } if (func(vty)) { vty_out(vty, "!%s", VTY_NEWLINE); } } return E_NONE; } /* 获取命令串的描述: string -- 描述字符串(OUT) return: 命令行描述字符串,失败返回NULL */ static char *_cmd_desc_str(const char **string) { const char *cp = NULL; const char *start = NULL; char *str = NULL; int32_t strlen = 0; cp = *string; if (NULL == cp) { return NULL; } /* 跳过空格 */ while (isspace ((int) *cp) && *cp != '\0') { cp++; } if ('\0' == *cp) { return NULL; } /* 获取字符串 */ start = cp; while (!(*cp == '\r' || *cp == '\n') && *cp != '\0') { cp++; } strlen = cp - start; str = XMALLOC(MTYPE_CLI ,strlen + 1); memcpy(str, start, strlen); *(str + strlen) = '\0'; /* 更新描述字符串的指针. */ *string = cp; return str; } /* 解析命令行字符串,将其每个单词拆分存入数组中: string -- 命令行字符串 descstr -- 命令行帮助字符串 return: 命令行字符串结构数组,失败返回NULL */ array_t *_cmd_strs_create(const char *string, const char *descstr) { uint32_t multiple = 0; const char *sp = NULL; char *word = NULL; uint32_t len = 0; const char *cp = NULL; const char *dp = NULL; array_t *strs = NULL; array_t *descs = NULL; desc_t *desc = NULL; cp = string; dp = descstr; if (NULL == cp) { return NULL; } strs = array_init(ARRAY_MIN_SIZE, MTYPE_CLI); while (1) { /* 跳过多余空格 */ while (isspace((int)(*cp)) && *cp != '\0') { cp++; } if ('(' == *cp) { multiple = 1; cp++; } if (')' == *cp) { multiple = 0; cp++; } if ('|' == *cp) { if (!multiple) { log_err(LOG_CLI, "Command parse error!: %s", string); exit(1); } cp++; } while (isspace((int)(*cp)) && *cp != '\0') { cp++; } if ('(' == *cp) { multiple = 1; cp++; } if ('\0' == *cp) { return strs; } sp = cp; while (!(isspace((int) *cp) || '\r' == *cp || '\n' == *cp || ')' == *cp || '|' == *cp ) && *cp != '\0') { cp++; } /* 获取字符串 */ len = cp - sp; word = XMALLOC(MTYPE_CLI, len + 1); memcpy(word, sp, len); *(word + len) = '\0'; /* 创建命令行当个单词结构 */ desc = XMALLOC(MTYPE_CLI, sizeof(desc_t)); desc->cmd = word; desc->str = _cmd_desc_str(&dp); if (multiple) { /* 多选的命令放在同一个数组下 */ if (1 == multiple) { descs = array_init(ARRAY_MIN_SIZE, MTYPE_CLI); array_append(strs, descs, MTYPE_CLI); } multiple++; } else { /* 单个命令放在一个独立的数组下 */ descs = array_init(ARRAY_MIN_SIZE, MTYPE_CLI); array_append(strs, descs, MTYPE_CLI); } array_append(descs, desc, MTYPE_CLI); } } /* 统计命令有效单词个数: strs -- 命令行字符串数组 return: 命令行包含的字符串的个数 */ static uint32_t _cmd_descs_size(array_t *strs) { uint32_t i = 0; uint32_t size = 0; array_t *descs = NULL; desc_t *desc = NULL; /* 遍历所有字符串 */ for (i = 0; i < array_active(strs); i++) { if (NULL == (descs = array_get(strs, i))) { continue; } /* 多选项直接++, 单选项如果不是NULL或者可选项直接++ */ if (1 == (array_active(descs)) && (desc = array_get (descs, 0)) != NULL) { if (NULL == desc->cmd || CMD_OPTION(desc->cmd)) { return size; } else { size++; } } else { size++; } } return size; } /* 命令行字符串比较函数, 用于cmd_sort_node(): p -- 命令行节点1 q -- 命令行节点2 return: 等于返回0,大于返回正数,小于返回负数 */ static int _cmp_node (const void *p, const void *q) { const cmd_element_t *a = *(cmd_element_t * const *)p; const cmd_element_t *b = *(cmd_element_t * const *)q; return strcmp(a->string, b->string); } /* 命令字符串排序: p -- 命令字符串1 q -- 命令字符串2 return: 等于返回0,大于返回正数,小于返回负数 */ static int _cmp_desc (const void *p, const void *q) { const desc_t *a = *(desc_t * const *)p; const desc_t *b = *(desc_t * const *)q; return strcmp(a->cmd, b->cmd); } /* 获取命令行节点下所有命令数组: ntype -- 命令节点 return: 节点上所有命令行数组 */ static array_t *_cmd_node_cmds_get(NODE_TYPE_E ntype) { cmd_node_t *node = array_get(cmd_nodes, ntype); return node->cmds; } /* 检查字符串str是否符合range: range -- 范围字符串 str -- 需要检测字符串 return: 匹配返回1 */ static int _cmd_range_match(const char *range, const char *str) { char *p = NULL; char buf[DECIMAL_STRLEN_MAX + 1] = {0}; char *endptr = NULL; unsigned long min = 0; unsigned long max = 0; unsigned long val = 0; if (NULL == str) return 1; /* 将字符串转为十进制数. */ val = strtoul(str, &endptr, 10); if (*endptr != '\0') return 0; /* 是否符合格式 */ range++; p = strchr(range, '-'); if (p == NULL) return 0; if (p - range > DECIMAL_STRLEN_MAX) return 0; strncpy(buf, range, p - range); buf[p - range] = '\0'; min = strtoul(buf, &endptr, 10); if (*endptr != '\0') return 0; /* 是否符合格式 */ range = p + 1; p = strchr (range, '>'); if (p == NULL) return 0; if (p - range > DECIMAL_STRLEN_MAX) return 0; strncpy (buf, range, p - range); buf[p - range] = '\0'; max = strtoul (buf, &endptr, 10); if (*endptr != '\0') return 0; if (val < min || val > max) return 0; return 1; } /* 检查字符串是不是ipv6地址: str -- ipv6字符串 return: 部分匹配, 完全匹配, 不匹配 */ static MATCH_TYPE_E _cmd_ipv6_match(const char *str) { uint32_t state = STATE_START; uint32_t colons = 0; uint32_t nums = 0; uint32_t double_colon = 0; const char *sp = NULL; struct sockaddr_in6 sin6_dummy; int ret; if (NULL == str) return partly_match; if (strspn(str, IPV6_ADDR_STR) != strlen (str)) return no_match; /* use inet_pton that has a better support, * for example inet_pton can support the automatic addresses: * ::1.2.3.4 */ ret = inet_pton(AF_INET6, str, &sin6_dummy.sin6_addr); if (1 == ret) return exact_match; while(*str != '\0') { switch (state) { case STATE_START: if (*str == ':') { if (*(str + 1) != ':' && *(str + 1) != '\0') return no_match; colons--; state = STATE_COLON; } else { sp = str; state = STATE_ADDR; } continue; case STATE_COLON: colons++; if (*(str + 1) == ':') state = STATE_DOUBLE; else { sp = str + 1; state = STATE_ADDR; } break; case STATE_DOUBLE: if (double_colon) return no_match; if (*(str + 1) == ':') return no_match; else { if (*(str + 1) != '\0') colons++; sp = str + 1; state = STATE_ADDR; } double_colon++; nums++; break; case STATE_ADDR: if (*(str + 1) == ':' || *(str + 1) == '\0') { if (str - sp > 3) return no_match; nums++; state = STATE_COLON; } if (*(str + 1) == '.') state = STATE_DOT; break; case STATE_DOT: state = STATE_ADDR; break; default: break; } if (nums > 8) return no_match; if (colons > 7) return no_match; str++; } return exact_match; } /* 检查字符串是不是带掩码的ipv6地址: str -- ipv6字符串 return: 部分匹配, 完全匹配, 不匹配 */ static MATCH_TYPE_E _cmd_ipv6_prefix_match(const char *str) { uint32_t state = STATE_START; int colons = 0; uint32_t nums = 0; uint32_t double_colon = 0; uint32_t mask; const char *sp = NULL; char *endptr = NULL; if (NULL == str) return partly_match; if (strspn(str, IPV6_PREFIX_STR) != strlen(str)) return no_match; while (*str != '\0' && state != STATE_MASK) { switch (state) { case STATE_START: if (':' == *str) { if (*(str + 1) != ':' && *(str + 1) != '\0') return no_match; colons--; state = STATE_COLON; } else { sp = str; state = STATE_ADDR; } continue; case STATE_COLON: colons++; if (*(str + 1) == '/') return no_match; else if (*(str + 1) == ':') state = STATE_DOUBLE; else { sp = str + 1; state = STATE_ADDR; } break; case STATE_DOUBLE: if (double_colon) return no_match; if (*(str + 1) == ':') return no_match; else { if (*(str + 1) != '\0' && *(str + 1) != '/') colons++; sp = str + 1; if (*(str + 1) == '/') state = STATE_SLASH; else state = STATE_ADDR; } double_colon++; nums += 1; break; case STATE_ADDR: if (*(str + 1) == ':' || *(str + 1) == '.' || *(str + 1) == '\0' || *(str + 1) == '/') { if (str - sp > 3) return no_match; for (; sp <= str; sp++) if (*sp == '/') return no_match; nums++; if (*(str + 1) == ':') state = STATE_COLON; else if (*(str + 1) == '.') state = STATE_DOT; else if (*(str + 1) == '/') state = STATE_SLASH; } break; case STATE_DOT: state = STATE_ADDR; break; case STATE_SLASH: if (*(str + 1) == '\0') return partly_match; state = STATE_MASK; break; default: break; } if (nums > 11) return no_match; if (colons > 7) return no_match; str++; } if (state < STATE_MASK) return partly_match; mask = strtol (str, &endptr, 10); if (*endptr != '\0') return no_match; if (mask < 0 || mask > 128) return no_match; return exact_match; } /* 检查字符串是不是ipv4地址: str -- ipv4字符串 return: 部分匹配, 完全匹配, 不匹配 */ static MATCH_TYPE_E _cmd_ipv4_match(const char *str) { const char *sp; uint32_t dots = 0; uint32_t nums = 0; char buf[4] = {0}; if (NULL == str) return partly_match; while(1) { memset(buf, 0, sizeof (buf)); sp = str; while(*str != '\0') { if (*str == '.') { if (dots >= 3) return no_match; if (*(str + 1) == '.') return no_match; if (*(str + 1) == '\0') return partly_match; dots++; break; } if (!isdigit((int)*str)) return no_match; str++; } if (str - sp > 3) return no_match; strncpy(buf, sp, str - sp); if (atoi(buf) > 255) return no_match; nums++; if (*str == '\0') break; str++; } if (nums < 4) return partly_match; return exact_match; } /* 检查字符串是不是带掩码的ipv4地址: str -- ipv4字符串 return: 部分匹配, 完全匹配, 不匹配 */ static MATCH_TYPE_E _cmd_ipv4_prefix_match(const char *str) { const char *sp = NULL; int dots = 0; char buf[4] = {0}; if (NULL == str) return partly_match; while(1) { memset(buf, 0, sizeof (buf)); sp = str; while (*str != '\0' && *str != '/') { if (*str == '.') { if (dots == 3) return no_match; if (*(str + 1) == '.' || *(str + 1) == '/') return no_match; if (*(str + 1) == '\0') return partly_match; dots++; break; } if (!isdigit((int)(*str))) return no_match; str++; } if (str - sp > 3) return no_match; strncpy (buf, sp, str - sp); if (atoi(buf) > 255) return no_match; if (3 == dots) { if ('/' == *str) { if (*(str + 1) == '\0') return partly_match; str++; break; } else if (*str == '\0') return partly_match; } if (*str == '\0') return partly_match; str++; } sp = str; while(*str != '\0') { if (!isdigit((int)(*str))) return no_match; str++; } if (atoi(sp) > 32) return no_match; return exact_match; } /* cl_str与所有命令匹配,将没有匹配的cmd在cmds中置为NULL: cl_str -- 实际输入的命令 cmds -- 所有命令, index -- cl_str在命令行中的位置 return: 返回匹配结果 */ static MATCH_TYPE_E _cmd_filter_by_completion(char *cl_str,array_t *cmds,uint32_t index) { uint32_t i = 0, j = 0; int matched = 0; const char *str = NULL; cmd_element_t *cmd = NULL; MATCH_TYPE_E match_type = no_match; array_t *strs = NULL; desc_t *desc = NULL; /* 如果cmds中的命令在index位置上不能配置cl_str,在cmds中将会被设置为NULL. */ for (i = 0; i < array_active(cmds); i++) { /* 跳过空位. */ if (NULL == (cmd = array_get(cmds, i))) { continue; } /* 长度不够的命令直接排除. */ if (index >= array_active(cmd->strs)) { array_get(cmds, i) = NULL; continue; } matched = 0; /* 如果strs是命令中的参数,有可能有多个可选项,所以要做循环. */ strs = array_get(cmd->strs, index); for (j = 0; j < array_active(strs); j++) { if (NULL == (desc = array_get(strs, j))) { continue; } /* 比较是有优先级的,如果在index位置有多种类型的匹配, 这里只会返回优先级最高的类型. */ str = desc->cmd; if (CMD_VARARG(str)) { if (match_type < vararg_match) match_type = vararg_match; matched++; } else if (CMD_RANGE(str)) { if (_cmd_range_match(str, cl_str)) { if (match_type < range_match) { match_type = range_match; } matched++; } } else if (CMD_IPV6(str)) { if (_cmd_ipv6_match(cl_str)) { if (match_type < ipv6_match) { match_type = ipv6_match; } matched++; } } else if (CMD_IPV6_PREFIX(str)) { if (_cmd_ipv6_prefix_match(cl_str)) { if (match_type < ipv6_prefix_match) { match_type = ipv6_prefix_match; } matched++; } } else if (CMD_IPV4(str)) { if (_cmd_ipv4_match(cl_str)) { if (match_type < ipv4_match) { match_type = ipv4_match; } matched++; } } else if (CMD_IPV4_PREFIX(str)) { if (_cmd_ipv4_prefix_match(cl_str)) { if (match_type < ipv4_prefix_match) { match_type = ipv4_prefix_match; } matched++; } } else { /* Check is this point's argument optional ? */ if (CMD_OPTION(str) || CMD_VARIABLE(str)) { if (match_type < extend_match) { match_type = extend_match; } matched++; } else if (0 == strncmp(cl_str, str, strlen(cl_str))) { if (0 == strcmp(cl_str, str)) { match_type = exact_match; } else { if (match_type < partly_match) { match_type = partly_match; } } matched++; } } } /* 将不匹配的命令排除. */ if (!matched) { array_get(cmds, i) = NULL; } } return match_type; } /* 根据配置类型type,进一步剔除cmds中不符合的cmd: cl_str -- 命令行字符串 cmds -- 所有可用命令数组 index -- 命令行字符串索引 type -- 上一次匹配类型 return: 有多个模糊匹配返回1, ipv4 or ipv6匹配不完整返回2, 成功返回0 */ static int32_t _cmd_is_ambiguous(char *cl_str, array_t *cmds, uint32_t index, MATCH_TYPE_E type) { uint32_t i = 0, j = 0; int32_t match = 0; const char *str = NULL; cmd_element_t *cmd = NULL; const char *matched = NULL; array_t *strs = NULL; desc_t *desc = NULL; MATCH_TYPE_E ret = no_match; /* 遍历所有命令 */ for (i = 0; i < array_active(cmds); i++) { if (NULL == (cmd = array_get(cmds, i))) { continue; } match = 0; /* 获取命令行第index字符串 */ strs = array_get(cmd->strs, index); /* 遍历该字符串下所有可能的关键字 */ for (j = 0; j < array_active(strs); j++) { if (NULL == (desc = array_get(strs, j))) { continue; } ret = no_match; str = desc->cmd; switch (type) { case exact_match: if (!(CMD_OPTION(str) || CMD_VARIABLE(str)) && 0 == strcmp(cl_str, str)) { match++; } break; case partly_match: if (!(CMD_OPTION(str) || CMD_VARIABLE(str)) && 0 == strncmp(cl_str, str, strlen(cl_str))) { if (matched && strcmp(matched, str) != 0) { return 1; /* There is ambiguous match. */ } else { matched = str; } match++; } break; case range_match: if (_cmd_range_match(str, cl_str)) { if (matched && strcmp(matched, str) != 0) { return 1; } else { matched = str; } match++; } break; case ipv6_match: if (CMD_IPV6(str)) { match++; } break; case ipv6_prefix_match: if ((ret = _cmd_ipv6_prefix_match(cl_str)) != no_match) { if (ret == partly_match) { return 2; /* There is incomplete match. */ } match++; } break; case ipv4_match: if (CMD_IPV4(str)) { match++; } break; case ipv4_prefix_match: if ((ret = _cmd_ipv4_prefix_match(cl_str)) != no_match) { if (ret == partly_match) { return 2; /* There is incomplete match. */ } match++; } break; case extend_match: if (CMD_OPTION(str) || CMD_VARIABLE(str)) { match++; } break; case no_match: default: break; } } /* 将类型不匹配的命令排除. */ if (!match) { array_get(cmds, i) = NULL; } } return 0; } /* 获取命令节点下所有命令数组和公共命令节点上的所有命令数组合并: ntype -- 命令节点 return: 返回复制的命令行数组 node: 在非VIEW_NODE模式下总会复制公共节点上的命令行 */ static array_t *_cmd_cmds_create(NODE_TYPE_E ntype) { array_t *cmds = array_copy(_cmd_node_cmds_get(ntype), MTYPE_CLI); if (ntype != PASSWORD_NODE && ntype != USERNAME_NODE) { array_merge(cmds, _cmd_node_cmds_get(COMMON_NODE), MTYPE_CLI); } return cmds; } /* 从vty执行命令cmd_line. */ static int32_t _cmd_execute_command_real(array_t *cmd_line, vty_t *vty, cmd_element_t **cmd) { uint32_t i = 0; uint32_t index = 0; array_t *cmds = NULL; cmd_element_t *cmd_element = NULL; cmd_element_t *matched_element = NULL; uint32_t matched_count = 0; uint32_t incomplete_count = 0; int32_t argc = 0; const char *argv[CMD_ARGC_MAX] = {NULL}; MATCH_TYPE_E match = no_match; int32_t varflag = 0; char *cl_str = NULL; /* 将该节点下的所有命令复制一份. */ cmds = _cmd_cmds_create(vty->node); /* 查找符合的cmd. */ for(index = 0; index < array_active(cmd_line); index++) { if (NULL == (cl_str = array_get(cmd_line, index))) { continue; } int ret = 0; /* 匹配cl_str和cmds中index位置字符串并返回配置类型. */ match = _cmd_filter_by_completion(cl_str, cmds, index); if (vararg_match == match) { break; } /* 根据配置类型type,进一步剔除cmds中不符合的cmd. */ ret = _cmd_is_ambiguous(cl_str, cmds, index, match); if (1 == ret) { array_free(cmds, MTYPE_CLI); return CMD_ERR_AMBIGUOUS; } else if (2 == ret) { array_free(cmds, MTYPE_CLI); return CMD_ERR_NO_MATCH; } } /* 检查是否只有唯一匹配并取出该命令成员. */ matched_element = NULL; matched_count = 0; incomplete_count = 0; for (i = 0; i < array_active(cmds); i++) { if (NULL == (cmd_element = array_get(cmds, i))) { continue; } if (vararg_match == match || index >= cmd_element->str_size) { matched_element = cmd_element; matched_count++; } else { incomplete_count++; } } array_free(cmds, MTYPE_CLI); if (0 == matched_count) { if (incomplete_count) { return CMD_ERR_INCOMPLETE; } else { return CMD_ERR_NO_MATCH; } } if (matched_count > 1) { return CMD_ERR_AMBIGUOUS; } /* 将命令的参数解析出来. */ varflag = 0; argc = 0; for (i = 0; i < array_active(cmd_line); i++) { if (varflag) { argv[argc++] = array_get(cmd_line, i); } else { array_t *strs = array_get(matched_element->strs, i); if (1 == array_active(strs)) { desc_t *desc = array_get(strs, 0); if (CMD_VARARG(desc->cmd)) { varflag = 1; } if (varflag || CMD_VARIABLE(desc->cmd) || CMD_OPTION(desc->cmd)) { argv[argc++] = array_get(cmd_line, i); } } else { argv[argc++] = array_get(cmd_line, i); } } if (argc >= CMD_ARGC_MAX) { return CMD_ERR_EXEED_ARGC_MAX; } } /* 执行命令. */ if (cmd) { *cmd = matched_element; } if (matched_element->daemon) { return CMD_SUCCESS_DAEMON; } return (*matched_element->func)(matched_element, vty, argc, argv); } /* 字符串str与数组v中的cmd一一比较,用于保证数组v中只有1个字符串str: v -- 字符串数组 str -- 参与比较的字符串 return: 相同返回1,其他返回0.*/ static int32_t _cmd_desc_unique_string(array_t *v, const char *str) { uint32_t i = 0; desc_t *desc = NULL; for(i = 0; i < array_active(v); i++) { if (NULL == (desc = array_get(v, i))) { continue; } if (0 == strcmp(desc->cmd, str)) { return 1; } } return 0; } /* 比较src是否符合dst的格式,只要部分匹配即可: src -- 需要检测的字符串 dst -- 命令中的字符串 return: src匹配dst返回dst,否则返回NULL */ static const char *_cmd_entry_function_desc(const char *src, const char *dst) { if (CMD_VARARG(dst)) { return dst; } if (CMD_RANGE(dst)) { if (_cmd_range_match (dst, src)) { return dst; } else { return NULL; } } if (CMD_IPV6(dst)) { if (_cmd_ipv6_match(src)) { return dst; } else { return NULL; } } if (CMD_IPV6_PREFIX(dst)) { if (_cmd_ipv6_prefix_match(src)) { return dst; } else { return NULL; } } if (CMD_IPV4(dst)) { if (_cmd_ipv4_match(src)) { return dst; } else { return NULL; } } if (CMD_IPV4_PREFIX(dst)) { if (_cmd_ipv4_prefix_match(src)) { return dst; } else { return NULL; } } /* Optional or variable commands always match on '?' */ if (CMD_OPTION(dst) || CMD_VARIABLE(dst)) { return dst; } /* In case of 'command \t', given src is NULL string. */ if (src == NULL) { return dst; } if (0 == strncmp(src, dst, strlen(src))) { return dst; } else { return NULL; } } /* 返回可供补齐的命令字数组: cmd_line -- 用户输入的命令 vty -- 输入命令的vty status -- 匹配结果 return: 匹配的字符串数组 */ static array_t* _cmd_describe_command_real(array_t *cmd_line, vty_t *vty, int32_t *status) { uint32_t i = 0, j = 0; array_t *cmds = NULL; array_t *matchs = NULL; array_t *strs = NULL; array_t *descs = NULL; desc_t *desc = NULL; cmd_element_t *cmd = NULL; uint32_t index = array_active(cmd_line) - 1; int32_t ret = 0; MATCH_TYPE_E match = no_match; char *cl_str = NULL; const char *string = NULL; /* 将该节点下的所有命令复制一份. */ cmds = _cmd_cmds_create(vty->node); /* 初始化匹配数组. */ matchs = array_init(INIT_MATCHARR_SIZE, MTYPE_CLI); /* 先对倒数第2个关键字之前的命令进行匹配. */ for (i = 0; i < index; i++) { if (NULL == (cl_str = array_get(cmd_line, i))) { continue; } /* 比较所有cmds中第i个字符串,不匹配会将cmd从cmds中删除 */ match = _cmd_filter_by_completion(cl_str, cmds, i); /* 这里基本进不去,不用深究 */ if (vararg_match == match) { array_t *descs = NULL; uint32_t j = 0; uint32_t k = 0; for (j = 0; j < array_active(cmds); j++) { if ((cmd = array_get(cmds, j)) != NULL && (array_active(cmd->strs))) { descs = array_get(cmd->strs, array_active(cmd->strs) - 1); for (k = 0; k < array_active(descs); k++) { desc_t *desc = array_get(descs, k); array_append(matchs, desc, MTYPE_CLI); } } } array_append(matchs, &cmd_desc_enter, MTYPE_CLI); array_free(cmds, MTYPE_CLI); qsort(matchs->index, array_active(matchs), sizeof (void *), _cmp_desc); *status = CMD_SUCCESS; return matchs; } /* 根据前一轮的比较,把cmds中不是match匹配类型的cmd去掉. */ ret = _cmd_is_ambiguous(cl_str, cmds, i, match); if (1 == ret) { array_free(cmds, MTYPE_CLI); array_free(matchs, MTYPE_CLI); *status = CMD_ERR_AMBIGUOUS; return NULL; } else if(2 == ret) { array_free(cmds, MTYPE_CLI); array_free(matchs, MTYPE_CLI); *status = CMD_ERR_NO_MATCH; return NULL; } } /* 检查最后一个关键字 */ cl_str = array_get(cmd_line, index); if (cl_str) { match = _cmd_filter_by_completion(cl_str, cmds, index); } /* 得到匹配的关键字列表. */ for (i = 0; i < array_active(cmds); i++) { if (NULL == (cmd = array_get(cmds, i))) { continue; } strs = cmd->strs; if (cl_str && index >= array_active(strs)) { array_get(cmds, i) = NULL; continue; } /* 如果命令正好相同,直接加入desc_enter字符串. */ if (NULL == cl_str && index == array_active(strs)) { if (!_cmd_desc_unique_string(matchs, cmd_enter)) { array_append(matchs, &cmd_desc_enter, MTYPE_CLI); } continue; } descs = array_get(strs, index); desc = NULL; for (j = 0; j < array_active(descs); j++) { if (NULL == (desc = array_get(descs, j))) { continue; } if (NULL == (string = _cmd_entry_function_desc(cl_str, desc->cmd))) { continue; } if (!_cmd_desc_unique_string(matchs, string)) { array_append(matchs, desc, MTYPE_CLI); } } } array_free(cmds, MTYPE_CLI); if (NULL == array_get(matchs, 0)) { array_free(matchs, MTYPE_CLI); *status = CMD_ERR_NO_MATCH; return NULL; } /* 将得到的数组按字符串排序. */ qsort(matchs->index, array_active(matchs), sizeof(void*), _cmp_desc); *status = CMD_SUCCESS; return matchs; } /* 计算公共字符串的长度: matchs -- 字符串数组 return: 公共字符串长度 */ static uint32_t _cmd_common_str(array_t *matchs) { uint32_t i = 0; uint32_t j = 0; desc_t *desc = NULL; uint32_t common_len = 0xffffffff; char *s1, *s2; if (NULL == array_get(matchs, 0) || NULL == array_get(matchs, 1)) { return 0; } for (i = 1; i < array_active(matchs); i++) { desc = array_get(matchs, i - 1); s1 = desc->cmd; desc = array_get(matchs, i); s2 = desc->cmd; for (j = 0; s1[j] && s2[j]; j++) { if (s1[j] != s2[j]) { break; } } if (j < common_len) { common_len = j; } if (0 == common_len) { break; } } return common_len; } /* Interface functions -------------------------------------------------------*/ /* 命令行相应'?'输入: cmd_line -- 当前命令行字符串数组 vty -- 当前终端结构体 status -- 补齐状态*/ array_t *cmd_describe_command(array_t *cmd_line, vty_t *vty, int32_t *status) { return _cmd_describe_command_real(cmd_line, vty, status); } /* 命令行相应'TAB'输入: cmd_line -- 命令行字符串数组 vty -- 终端结构体 status -- 补齐状态 return: */ char **cmd_complete_command(array_t *cmd_line, vty_t *vty, int32_t *status) { uint32_t i = 0; array_t *matchs = NULL; array_t *temps = NULL; desc_t *desc = NULL; bool is_include_arg = FALSE; char **match_str = NULL; char buf[COMPLETE_BUF_SIZE] = {0}; uint32_t common_len = 0; uint32_t width = 0; uint32_t index = array_active(cmd_line) - 1; /* 获取可用关键字队列. */ matchs = _cmd_describe_command_real(cmd_line, vty, status); switch(*status) { case CMD_ERR_AMBIGUOUS: return NULL; case CMD_ERR_NO_MATCH: return NULL; } /* 判断匹配是不是有参数 */ for (i = 0; i < array_active(matchs); i++) { if (NULL == (desc = array_get(matchs, i))) { continue; } if (CMD_OPTION(desc->cmd) || CMD_VARIABLE(desc->cmd) || CMD_VARARG(desc->cmd) || CMD_RANGE(desc->cmd) || CMD_IPV4(desc->cmd) || CMD_IPV4_PREFIX(desc->cmd) || CMD_IPV6(desc->cmd) || CMD_IPV6_PREFIX(desc->cmd)) { is_include_arg = TRUE; } } /* 只有1个匹配,且不是参数的情况下,直接返回完整字符串. */ if (NULL == array_get(matchs, 1) && FALSE == is_include_arg) { /* Make new matchs. */ desc = array_get(matchs, 0); array_free(matchs, MTYPE_CLI); matchs = array_init(INIT_MATCHARR_SIZE, MTYPE_CLI); array_append(matchs, XSTRDUP(MTYPE_CLI, desc->cmd), MTYPE_CLI); match_str = (char **)matchs->index; array_free_wrapper(matchs, MTYPE_CLI); *status = CMD_COMPLETE_FULL_MATCH; return match_str; } /* 有多个匹配,且不是参数,且存在公共字符串的情况下,返回公共字符串. */ if (FALSE == is_include_arg && (common_len = _cmd_common_str(matchs))) { uint32_t len = 0; if (NULL == array_get(cmd_line, index)) { len = 0; } else { len = strlen(array_get(cmd_line, index)); } if (len < common_len) { char *common_str = NULL; desc = array_get(matchs, 0); common_str = XMALLOC(MTYPE_CLI, common_len + 1); memcpy(common_str, desc->cmd, common_len); common_str[common_len] = '\0'; array_free(matchs, MTYPE_CLI); matchs = array_init(INIT_MATCHARR_SIZE, MTYPE_CLI); array_append(matchs, common_str, MTYPE_CLI); match_str = (char **)matchs->index; array_free_wrapper(matchs, MTYPE_CLI); *status = CMD_COMPLETE_MATCH; return match_str; } } /* 计算所有命令字中最长的长度用于打印对其. */ for (i = 0; i < array_active(matchs); i++) { if (NULL == (desc = array_get(matchs, i))) { continue; } if (desc->cmd[0] == '\0') { continue; } uint32_t len = 0; len = strlen(desc->cmd); if (desc->cmd[0] == '.') { len--; } if (width < len) { width = len; } } /* 打印可用命令字. */ temps = array_init(INIT_MATCHARR_SIZE, MTYPE_CLI); for (i = 0; i < array_active(matchs); i++) { if (NULL == (desc = array_get(matchs, i))) { continue; } if (desc->cmd[0] == '\0') { continue; } if (!desc->str) { snprintf(buf, COMPLETE_BUF_SIZE, " %-s%s", desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd, VTY_NEWLINE); } else { snprintf(buf, COMPLETE_BUF_SIZE, " %-*s %s%s", width, desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd, desc->str, VTY_NEWLINE); } array_append(temps, XSTRDUP(MTYPE_CLI, buf), MTYPE_CLI); } array_append(temps, NULL, MTYPE_CLI); match_str = (char **)temps->index; array_free_wrapper(temps, MTYPE_CLI); array_free(matchs, MTYPE_CLI); *status = CMD_COMPLETE_LIST_MATCH; return match_str; } /* 将命令节点中的命令按字符串排序: return: void */ void cmd_sort_node(void) { uint32_t i = 0; cmd_node_t *node = NULL; for (i = 0; i < array_active(cmd_nodes); i++) { if (NULL == (node = array_get(cmd_nodes, i))) { continue; } array_t *cmds = node->cmds; qsort(cmds->index, array_active(cmds), sizeof (void *), _cmp_node); } } /* 将命令行模式加入总模式节点中: node -- 命令行节点结构体 func -- 该节点下的保存配置函数 return: void */ void cmd_install_node(cmd_node_t *node, cmd_save_config_f *func) { array_set(cmd_nodes, node->node, node, MTYPE_CLI); node->func = func; node->cmds = array_init(ARRAY_MIN_SIZE, MTYPE_CLI); } /* 安装命令到相应的命令模式节点: ntype -- 命令行节点 cmd -- 命令. return: void node: 命令没有检查入参数的有效性,请调用者一定保证入参的正确性.*/ void cmd_install_element(NODE_TYPE_E ntype, cmd_element_t *cmd) { cmd_node_t *cnode = NULL; if (!cmd_nodes) { log_err(LOG_CLI, "Command isn't initialied, please check it."); return; } cnode = array_get(cmd_nodes, ntype); if (NULL == cnode) { log_err(LOG_CLI, "Command node %d doesn't exist, please check it.", ntype); return; } array_append(cnode->cmds, cmd, MTYPE_CLI); if (NULL == cmd->strs) { cmd->strs = _cmd_strs_create(cmd->string, cmd->doc); } cmd->str_size = _cmd_descs_size(cmd->strs); } /* 命令行核心初始化: return: void */ void cmd_init(void) { cmd_enter = strdup(""); cmd_desc_enter.cmd = cmd_enter; cmd_desc_enter.str = strdup(""); /* 初始化所有命令的总节点. */ cmd_nodes = array_init(ARRAY_MIN_SIZE, MTYPE_CLI); /* 默认主机信息. */ strncpy(host.name, "50100110", FILE_NAME_LEN); strncpy(host.configfile, "startup-config", FILE_NAME_LEN); host.version = version_get(); host.compile = version_date_get(); host.lines = -1; /* 安装命令行模式节点. */ cmd_install_node(&common_node, NULL); cmd_install_node(&username_node, NULL); cmd_install_node(&password_node, NULL); cmd_install_node(&enable_node, NULL); cmd_install_node(&config_node, _config_write_host); /* config node是全局配置,需要注册其他的配置保存函数 */ config_node.configs = array_init(CONFIG_PRI_COUNT, MTYPE_CLI); /* 安转基本命令. */ cmd_install_element(COMMON_NODE, &show_version_cmd); cmd_install_element(COMMON_NODE, &exit_node_cmd); cmd_install_element(COMMON_NODE, &quit_node_cmd); cmd_install_element(COMMON_NODE, &show_running_config_cmd); cmd_install_element(COMMON_NODE, &show_log_default_cmd); cmd_install_element(COMMON_NODE, &show_log_level_cmd); cmd_install_element(COMMON_NODE, &show_log_cnt_cmd); cmd_install_element(COMMON_NODE, &show_log_cmd); cmd_install_element(ENABLE_NODE, &config_enable_cmd); cmd_install_element(ENABLE_NODE, &config_terminal_cmd); cmd_install_element(ENABLE_NODE, &bash_open_cmd); cmd_install_element(ENABLE_NODE, &terminal_monitor_cmd); cmd_install_element(ENABLE_NODE, &no_terminal_monitor_cmd); cmd_install_element(ENABLE_NODE, &config_write_file_cmd); cmd_install_element(ENABLE_NODE, &id_set_cmd); cmd_install_element(ENABLE_NODE, &host_ip_set_cmd); cmd_install_element(ENABLE_NODE, &default_route_set_cmd); cmd_install_element(ENABLE_NODE, &factory_date_set_cmd); cmd_install_element(ENABLE_NODE, &deployment_date_set_cmd); cmd_install_element(ENABLE_NODE, &logoff_cmd); cmd_install_element(ENABLE_NODE, &enable_test_cmd); cmd_install_element(ENABLE_NODE, &reboot_cmd); cmd_install_element(CONFIG_NODE, &hostname_set_cmd); cmd_install_element(CONFIG_NODE, &config_log_level_cmd); cmd_install_element(CONFIG_NODE, &no_config_log_level_cmd); } /* 分解命令每个单词并填入列表,便于于系统命令进行比较: string -- 命令行字符串 return: 命令行每个单词组成的数组,失败返回NULL */ array_t *cmd_strs_create(const char *string) { const char *cp = NULL; const char *start = NULL; char *word = NULL; int32_t strlen = 0; array_t *strs = NULL; if (NULL == string) { return NULL; } cp = string; /* 跳过空格. */ while (isspace((int)*cp) && *cp != '\0') { cp++; } /* 如果只有空格则直接返回. */ if ('\0' == *cp || '!' == *cp || '#' == *cp) { return NULL; } strs = array_init(ARRAY_MIN_SIZE, MTYPE_CLI); /* 产生单词列表. */ while (1) { start = cp; /* 找到word的结束位置. */ while (!(isspace((int)*cp) || '\r' == *cp || '\n' == *cp) && *cp != '\0') { cp++; } /* 产生word. */ strlen = cp - start; word = XMALLOC(MTYPE_CLI, strlen + 1); memcpy(word, start, strlen); *(word + strlen) = '\0'; array_append(strs, word, MTYPE_CLI); /* 跳过空格. */ while ((isspace((int)*cp) || '\r' == *cp || '\n' == *cp) && *cp != '\0') { cp++; } /* 遇到结束符返回. */ if ('\0' == *cp) { return strs; } } } /* 于函数cmd_strs_create对应,释放其分配的空间: a -- 命令行每个字符串组成的数组 return: void */ void cmd_strs_free(array_t *a) { uint32_t i = 0; char *cp = NULL; if (!a) { return; } for (i = 0; i < array_active(a); i++) { if ((cp = array_get(a, i)) != NULL) { XFREE(MTYPE_CLI, cp); } } array_free(a, MTYPE_CLI); } /* 从vty执行命令cmd_line: cmd_line -- 命令字符串 vty -- 终端结构体 cmd -- 被执行命令行结构体(OUT) return: 执行结果(CMD_xxxx) */ int32_t cmd_execute_command(array_t *cmd_line, vty_t *vty, cmd_element_t **cmd) { return _cmd_execute_command_real(cmd_line, vty, cmd); } /* 从vty执行命令cmd_line: vty -- 终端结构体 return: 执行结果(CMD_xxxx) */ int32_t cmd_execute(vty_t *vty) { int ret = CMD_ERR_NO_MATCH; array_t *cmd_line = NULL; /* 分解命令的每个单词到队列. */ cmd_line = cmd_strs_create(vty->buf); if (NULL == cmd_line) { return CMD_SUCCESS; } ret = cmd_execute_command(cmd_line, vty, NULL); if (ret != CMD_SUCCESS) { switch (ret) { case CMD_WARNING: if (vty->type == VTY_FILE) vty_out(vty, "Warning...%s", VTY_NEWLINE); break; case CMD_ERR_AMBIGUOUS: vty_out(vty, "%% Ambiguous command.%s", VTY_NEWLINE); break; case CMD_ERR_NO_MATCH: vty_out(vty, "%% Unknown command: %s %s", vty->buf, VTY_NEWLINE); break; case CMD_ERR_INCOMPLETE: vty_out(vty, "%% Command incomplete.%s", VTY_NEWLINE); break; } } cmd_strs_free(cmd_line); return ret; } /* 注册命令行配置保存函数: ntype -- 命令行节点类型 pri -- 优先级 func -- 配置保存回调函数 return: E_xxx */ int32_t cmd_config_node_config_register(int32_t pri, cmd_save_config_f *func) { cmd_node_t *node = &config_node; /* 参数检查 */ if (pri >= CONFIG_PRI_COUNT || !func) { return E_BAD_PARAM; } /* 加入列表 */ array_set(node->configs, pri, func, MTYPE_CLI); return 0; } /* 从shell终端读取一行命. */ char *_vtysh_gets(void) { HIST_ENTRY *last; struct termios new_setting,init_setting; /* readline要求自己释放其返回的buf. */ if (line_read) { free(line_read); line_read = NULL; } /* 输入密码时不要回显 */ if (PASSWORD_NODE == vtysh->node) { tcgetattr(0, &init_setting); new_setting = init_setting; new_setting.c_lflag &= ~ECHO; tcsetattr(0, TCSANOW, &new_setting); } /* 获取一行命令. */ //line_read = readline(vtysh_prompt()); line_read = readline(NULL); /* 打开回显 */ if (PASSWORD_NODE == vtysh->node) { tcsetattr(0,TCSANOW, &init_setting); printf("\r\n"); } /* 没有字符直接返回 */ if (NULL == line_read) { return NULL; } /* 如果命令有效记录历史. */ if (*line_read && vtysh->node != PASSWORD_NODE && vtysh->node != USERNAME_NODE) { using_history(); last = previous_history(); if (!last || strcmp (last->line, line_read) != 0) { add_history(line_read); append_history(1, history_file); } } return line_read; } void *_vtysh_handle(void *arg) { /* CLI主循环 */ while(1) { if (NULL == _vtysh_gets()) { continue; } if (strlen(line_read) >= vtysh->max) { printh("ERROR: The command is too long\n"); continue; } strncpy(vtysh->buf, line_read, vtysh->max - 1); //printf("Terminal Input:%s\n", vtysh->buf); vtycmd_send(); vty_execute(vtysh); } return NULL; } /* commandLine '?'响应函数. */ int vtysh_rl_question(void) { array_t *cmd_line = NULL; cmd_line = cmd_strs_create(rl_line_buffer); if (NULL == cmd_line) { cmd_line = array_init(1, MTYPE_CLI); array_append(cmd_line, '\0', MTYPE_CLI); } else if (rl_end && isspace((int)rl_line_buffer[rl_end - 1])) array_append(cmd_line, '\0', MTYPE_CLI); vty_question(vtysh, cmd_line); cmd_strs_free(cmd_line); rl_on_new_line(); return 0; } /* commandLine 'TAB'响应函数. */ int vtysh_rl_completion(void) { array_t *cmd_line = NULL; char **match_strs = NULL; int32_t complete_status = CMD_ERR_NO_MATCH; cmd_line = cmd_strs_create(rl_line_buffer); if (NULL == cmd_line) { cmd_line = array_init(1, MTYPE_CLI); array_append(cmd_line, '\0', MTYPE_CLI); } else if (rl_end && isspace ((int) rl_line_buffer[rl_end - 1])) array_append(cmd_line, '\0', MTYPE_CLI); match_strs = cmd_complete_command(cmd_line, vtysh, &complete_status); if (NULL == match_strs) { cmd_strs_free(cmd_line); return 0; } if (CMD_COMPLETE_MATCH == complete_status || CMD_COMPLETE_FULL_MATCH == complete_status) _vtysh_append_word(cmd_line, match_strs[0], complete_status); else { vty_print_word(vtysh, match_strs); rl_on_new_line(); } vty_free_match_strs(match_strs); cmd_strs_free(cmd_line); return 0; } /* shell前缀 */ char *vtysh_prompt(void) { static char buf[100] = {0}; cmd_node_t *node = NULL; node = cmd_node_get(vtysh->node); /* 用户密码输入 */ if (USERNAME_NODE == node->node || PASSWORD_NODE == node->node) { snprintf(buf, sizeof(buf), "%s", node->prompt); } else { snprintf(buf, sizeof(buf), node->prompt, host.name); } return buf; } /* 初始化readline. */ void vtysh_readline_init(void) { /* 修改自己的按键操作. */ rl_bind_key('?', (rl_command_func_t *)vtysh_rl_question); rl_bind_key('\t', (rl_command_func_t *)vtysh_rl_completion); //printf("2 %d\r\n", rl_bind_key(CONTROL('D'), (rl_command_func_t *)vtysh_rl_question)); //printf("2 %d\r\n", rl_bind_key(CONTROL('Z'), (rl_command_func_t *)vtysh_rl_question)); /* do not append space after completion. It will be appended * in new_completion() function explicitly. */ rl_completion_append_character = '\0'; read_history(history_file); clear_history(); } void vtysh_device_save(void) { int32_t fd = 0; int32_t rv = 0; /* 获取文件名. */ fd = open("device-config", O_RDWR | O_CREAT | O_TRUNC, 0777); if (-1 == fd) { DBG(DBG_M_CLI, "Can't open config file device-config\r\n"); return; } chmod("device-config", 0666); rv = write(fd, &device_info, sizeof(device_info)); if (rv != sizeof(device_info)) { DBG(DBG_M_CLI, "Can't write config file device-config\r\n"); } /* 回收资源 */ close(fd); vtysh_config_save_bak(SAVE_DEV_CFG_BAK_FILE); return; } void vtysh_eth0_save(void) { int32_t fd = 0; int32_t rv = 0; uint16_t len = 0; char *str = NULL; /* 获取文件名. */ fd = open("/etc/network/Eth0Setting", O_RDWR | O_CREAT | O_TRUNC, 0777); if (-1 == fd) { DBG(DBG_M_CLI, "Can't open config file Eth0Setting\r\n"); return; } chmod("/etc/network/Eth0Setting", 0666); str = XMALLOC(MTYPE_VTY_TMP, 512); len += snprintf(str, 512, "ip=%s\n", device_info.host_ip); len += snprintf(str + len, 512 - len, "netmask=%s\n", device_info.host_mask); len += snprintf(str + len, 512 - len, "mac=%02x:%02x:%02x:%02x:%02x:%02x\n", device_info.mac[0], device_info.mac[1], device_info.mac[2], device_info.mac[3], device_info.mac[4], device_info.mac[5]); len += snprintf(str + len, 512 - len, "gateway=%s\n", device_info.host_gw); rv = write(fd, str, len); if (rv != sizeof(device_info)) { DBG(DBG_M_CLI, "Can't write config file Eth0Setting\r\n"); } /* 回收资源 */ XFREE(MTYPE_VTY_TMP, str); close(fd); return; } /* 设备基本信息初始化 */ void vtysh_device_init(void) { int32_t fd = 0; int32_t rv = 0; //uint32_t addr = 0; //struct sockaddr_in server; /* 获取文件名. */ fd = open("device-config", O_RDONLY); if (fd != -1) { /* 有文件的情况下直接读取配置 */ rv = read(fd, &device_info, sizeof(device_info)); if (rv <= 0) { log_err(LOG_DEFAULT, "Can't read config file device-config."); } close(fd); } else { device_info.id_major = 5010; device_info.id_minor = 1; device_info.mac[0] = 0x68;device_info.mac[1] = 0x70;device_info.mac[2] = 0xDC; device_info.mac[3] = 0x89;device_info.mac[4] = 0x97;device_info.mac[5] = 0x6E; snprintf(device_info.host_ip, INET_ADDRSTRLEN, DEV_INFO_DEFAULT_IP); snprintf(device_info.host_mask, INET_ADDRSTRLEN, DEV_INFO_DEFAULT_MASK); snprintf(device_info.host_gw, INET_ADDRSTRLEN, DEV_INFO_DEFAULT_GW); device_info.factory_date = 1685808000; device_info.deployment_date = 1685808000; snprintf(device_info.dev_type, FILE_NAME_LEN, "GOM%04d", device_info.id_major); vtysh_device_save(); vtysh_eth0_save(); #if 0 /* 没有配置文件的时候,使用默认配置设置网卡 */ if (inet_aton(device_info.host_ip, &server.sin_addr) < 0) { log_err(LOG_DEFAULT, "ip inet_aton host_ip is error!\r\n"); } addr = server.sin_addr.s_addr; sockunion_ip_set("eth0", addr); if (inet_aton(device_info.host_mask, &server.sin_addr) < 0) { log_err(LOG_DEFAULT, "mask inet_aton host_mask is error!\r\n"); } addr = server.sin_addr.s_addr; sockunion_mask_set("eth0", addr); if (inet_aton(device_info.host_gw, &server.sin_addr) < 0) { log_err(LOG_DEFAULT, "gateway inet_aton host_gw is error!\r\n"); } addr = server.sin_addr.s_addr; sockunion_gw_set("eth0", addr, addr); #endif } return; } /* 初始化shell. */ void vtysh_init(void) { vtysh_readline_init(); vty_init(); vtysh_device_init(); /* Make vty structure. */ vtysh = vty_create(); vtysh->type = VTY_SHELL; //vtysh->node = CONFIG_NODE; vtysh->node = USERNAME_NODE; if (pthread_mutex_init(&m_config_mutex, NULL) != 0) { log_err(LOG_DEFAULT, "ERROR at mutex init return %s!", safe_strerror(errno)); return; } } /* 配置恢复并开启shell线程 */ void vtysh_config_recovery(void) { char *config_file = NULL; FILE *file = NULL; char *buf = NULL; /* 获取文件名. */ config_file = host.configfile; file = fopen(config_file, "r"); if (!file) { log_err(LOG_DEFAULT, "Can't open config file %s!", host.configfile); vtysh->node = USERNAME_NODE; return; } /* 配置恢复 */ buf = XMALLOC(MTYPE_CLI, VTYSH_CONFIG_R_BUF); while (!feof(file)) { if (NULL == fgets(buf, VTYSH_CONFIG_R_BUF, file)) { break; } strncpy(vtysh->buf, buf, vtysh->max - 1); vty_execute(vtysh); } XFREE(MTYPE_CLI, buf); /* 回收资源 */ fclose(file); vtysh->node = USERNAME_NODE; } int32_t vtysh_config_save_bak(int idx) { char src_file[128] = {0}; char bak_file[128] = {0}; char cmd[128] = {0}; snprintf(bak_file, 127, "%s/bak", DEVICE_RUN_PATH); if (idx == SAVE_DEV_CFG_BAK_FILE) { snprintf(src_file, 127, "%s/device-config", DEVICE_RUN_PATH); snprintf(cmd, 127, "cp -rf %s %s", src_file, bak_file); system(cmd); system("sync"); } else if (idx == SAVE_STA_CFG_BAK_FILE) { snprintf(src_file, 127, "%s/startup-config", DEVICE_RUN_PATH); snprintf(cmd, 127, "cp -rf %s %s", src_file, bak_file); system(cmd); system("sync"); } return 0; } int32_t vtysh_config_save(void) { uint32_t i = 0; int fd; cmd_node_t *node = NULL; char *config_file = NULL; char *config_file_tmp = NULL; char *config_file_sav = NULL; char buf[TIME_STR_LEN] = {0}; vty_t *file_vty = NULL; /* 获取文件名. */ config_file = host.configfile; pthread_mutex_lock(&m_config_mutex); config_file_sav = XMALLOC(MTYPE_CLI, strlen(config_file) + strlen(CONF_BACKUP_EXT) + 1); strcpy(config_file_sav, config_file); strcat(config_file_sav, CONF_BACKUP_EXT); config_file_tmp = XMALLOC(MTYPE_CLI, strlen(config_file) + 8); sprintf(config_file_tmp, "%s.XXXXXX", config_file); /* 打开临时文件写入配置. */ fd = open(config_file_tmp, O_RDWR | O_CREAT ,0777); if (fd < 0) { printh("Can't open configuration file %s.\r\n", config_file_tmp); XFREE(MTYPE_CLI, config_file_tmp); XFREE(MTYPE_CLI, config_file_sav); pthread_mutex_unlock(&m_config_mutex); return E_ERROR; } /* Make vty for configuration file. */ file_vty = vty_create(); file_vty->fd = fd; file_vty->type = VTY_FILE; /* Config file header print. */ vty_out(file_vty, "!\n! Zebra configuration saved from vty\n"); if (time_string(0, buf, sizeof(buf)) != 0) vty_out(file_vty, "! %s\n", buf); vty_out(file_vty, "!\n"); for (i = 0; i < array_active(cmd_nodes); i++) if ((node = array_get(cmd_nodes, i)) && node->func) { if ((*node->func)(file_vty)) vty_out(file_vty, "!\n"); } vty_close(file_vty); if (rename(config_file, config_file_sav) != 0) { printh("Can't backup configuration file %s: %s (%d).\r\n", config_file_sav, safe_strerror(errno), errno); } if (rename(config_file_tmp, config_file) != 0) { printh("Can't save configuration file %s: %s (%d).\r\n", config_file, safe_strerror(errno), errno); } if (chmod(config_file, 0666) != 0) { printh("Can't chmod configuration file %s: %s (%d).\r\n", config_file, safe_strerror(errno), errno); } if (chmod(config_file_sav, 0666) != 0) { printh("Can't chmod backup configuration file %s: %s (%d).\r\n", config_file, safe_strerror(errno), errno); } printh("Configuration saved to %s.\r\n", config_file); XFREE(MTYPE_CLI, config_file_tmp); XFREE(MTYPE_CLI, config_file_sav); pthread_mutex_unlock(&m_config_mutex); vtysh_config_save_bak(SAVE_STA_CFG_BAK_FILE); return E_NONE; } int32_t vtysh_host_addr_set(char *addr, char *mask) { uint32_t ip = 0; uint32_t ip_set = 0; uint8_t mac[MAC_ADDR_LEN] = {0}; struct sockaddr_in server; memcpy(mac, device_info.mac, MAC_ADDR_LEN); /* 转换ip地址 */ inet_aton(device_info.host_ip, &server.sin_addr); ip = server.sin_addr.s_addr; if (inet_aton(addr, &server.sin_addr) < 0) { DBG(DBG_M_CLI, "inet_aton ip is error!\r\n"); return E_ERROR; } ip_set = server.sin_addr.s_addr; /* 比较配置 */ if (ip != ip_set) { mac_generate_from_ip(addr, mac); vty_reset(); sockunion_ip_set("eth0", ip_set); vty_serv_sock_family(NULL, 11000, AF_INET); } inet_aton(device_info.host_mask, &server.sin_addr); ip = server.sin_addr.s_addr; if (inet_aton(mask, &server.sin_addr) < 0) { DBG(DBG_M_CLI, "inet_aton mask is error!\r\n"); return E_ERROR; } ip_set = server.sin_addr.s_addr; if (ip != ip_set) { sockunion_mask_set("eth0", ip_set); } memcpy(device_info.mac, mac, MAC_ADDR_LEN); strncpy(device_info.host_ip, addr, INET_ADDRSTRLEN - 1); strncpy(device_info.host_mask, mask, INET_ADDRSTRLEN - 1); vtysh_device_save(); vtysh_eth0_save(); return E_NONE; } int32_t vtysh_gateway_set(char *gateway) { uint32_t ip = 0; uint32_t ip_set = 0; struct sockaddr_in server; inet_aton(device_info.host_gw, &server.sin_addr); ip = server.sin_addr.s_addr; if (inet_aton(gateway, &server.sin_addr) < 0) { DBG(DBG_M_CLI, "inet_aton gateway is error!\r\n"); return E_ERROR; } ip_set = server.sin_addr.s_addr; if (ip != ip_set) { sockunion_gw_set("eth0", ip_set, ip); } strncpy(device_info.host_gw, gateway, INET_ADDRSTRLEN - 1); vtysh_device_save(); vtysh_eth0_save(); return E_NONE; } void vtysh_shell_init(void) { struct sched_param param; pthread_attr_t attr; int32_t rv = 0; /* 配置线程RR调度,优先级50 */ pthread_attr_init(&attr); param.sched_priority = 50; pthread_attr_setschedpolicy(&attr, SCHED_RR); pthread_attr_setschedparam(&attr, ¶m); pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); rv = pthread_create(&_cmd_pid, &attr, _vtysh_handle, NULL); if (rv != 0) { log_err(LOG_DEFAULT, "vtysh_shell_init can't create pthread %d!", rv); } else { thread_m_add("CLI", _cmd_pid); } pthread_attr_destroy(&attr); /* YL_TEST */ //vty_serv_sock_family(device_info.host_ip, 11000, AF_INET); //vty_serv_sock_family(NULL, 11000, AF_INET); } int create_client_socket() { int sockfd; if ((sockfd = socket(AF_UNIX, SOCK_DGRAM, 0)) == -1) { perror("client socket error"); return -1; } // 一般显式调用bind函数,以便服务器区分不同客户端 memset(&cliun, 0, sizeof(cliun)); cliun.sun_family = AF_UNIX; strcpy(cliun.sun_path, client_path); socklen = offsetof(struct sockaddr_un, sun_path) + strlen(cliun.sun_path); unlink(cliun.sun_path); if (bind(sockfd, (struct sockaddr *)&cliun, socklen) < 0) { perror("bind error"); exit(1); } memset(&serun, 0, sizeof(serun)); serun.sun_family = AF_UNIX; strcpy(serun.sun_path, server_path); socklen = offsetof(struct sockaddr_un, sun_path) + strlen(serun.sun_path); //if (connect(sockfd, (struct sockaddr *)&serun, sizeof(serun)) < 0) //{ // perror("connect error"); // exit(1); //} printf("[%s %d] Connect pass! %d\n",__FILE__,__LINE__,sockfd); return sockfd; } void *_vtycmd_recv_handle(void *arg) { char *buf = (char*)&vtycmd->buf_read; int n; while(1) { memset(vtycmd->buf_read.cmd, 0, 504); n = read(vtycmd->socket, buf, MAXLINE); if ( n <= 0 ) { printf("the other side has been closed.\n"); break; } //printf("\nsocket READ %d bytes[%s].node=%d param_num=%d\n", strlen(vtycmd->buf_read.cmd), vtycmd->buf_read.cmd, vtycmd->buf_read.node, vtycmd->buf_read.param_num); //printf("xxx%s\n", vtycmd->buf_read.cmd); vty_out(vtysh, "%s\n", vtycmd->buf_read.cmd); vtysh->node = vtycmd->buf_read.node; pd_port_node.param_num = vtycmd->buf_read.param_num; } return NULL; } void vtycmd_data_init() { vtycmd = XMALLOC(MTYPE_CLI, sizeof(vtycmd_t)); if (vtycmd == NULL) { log_err(LOG_DEFAULT, "ERROR at XMALLOC return %s!", safe_strerror(errno)); return; } vtycmd->socket = -1; memset(&vtycmd->buf_read, 0, sizeof(vtycmd_head_t)); memset(&vtycmd->buf_write, 0, sizeof(vtycmd_head_t)); if (pthread_mutex_init(&vtycmd->wr_mutex, NULL) != 0) { log_err(LOG_DEFAULT, "ERROR at mutex init return %s!", safe_strerror(errno)); return; } if (pthread_mutex_init(&vtycmd->rd_mutex, NULL) != 0) { log_err(LOG_DEFAULT, "ERROR at mutex init return %s!", safe_strerror(errno)); return; } } void vtycmd_init() { struct sched_param param; pthread_attr_t attr; pthread_t pid; int32_t rv = 0; vtycmd_data_init(); vtycmd->socket = create_client_socket(); /* 配置线程RR调度,优先级50 */ pthread_attr_init(&attr); param.sched_priority = 50; pthread_attr_setschedpolicy(&attr, SCHED_RR); pthread_attr_setschedparam(&attr, ¶m); pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); rv = pthread_create(&pid, &attr, _vtycmd_recv_handle, NULL); if (rv != 0) { log_err(LOG_DEFAULT, "vtysh_shell_init can't create pthread %d!", rv); } else { thread_m_add("CLI", pid); } pthread_attr_destroy(&attr); } /************************ (C) COPYRIGHT LandPower ***** END OF FILE ****************/