/****************************************************************************** * file lib/management/cli.c * author YuLiang * version 1.0.0 * date 10-Sep-2021 * brief This file provides all the cli related 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 ------------------------------------------------------------------*/ /* Standard includes. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include /* 外部程序库头文件. */ #include #include /* User includes. */ #include "cli.h" #include "list.h" /* Private define ------------------------------------------------------------*/ /* Private macro -------------------------------------------------------------*/ /* Private typedef -----------------------------------------------------------*/ /* Private variables ---------------------------------------------------------*/ static int32_t _cli_help(const char *cmd_str); static int32_t _cli_version(const char *cmd_str); static int32_t _cli_exit(const char *cmd_str); /* The definition of the "help" command. This command is always at the front of the list of registered commands. */ static const cli_command_t _cli_cmd_help = { "help", "List all command.", _cli_help, 0 }; static const cli_command_t _cli_cmd_version = { "version", "Show device version information.", _cli_version, 0 }; static const cli_command_t _cli_cmd_exit = { "exit", "Returns the upper-level node.", _cli_exit, 0 }; /* The definition of the list of commands. Commands that are registered are added to this list. */ static cli_node_t _cli_node[CLI_NODE_COUNT] = { {"common", 0xffff, CLI_NODE_COMMON, {NULL, NULL}}, {"Username: ", 0, CLI_NODE_PASSWORD, {NULL, NULL}}, {"config", 0xffff, CLI_NODE_PASSWORD, {NULL, NULL}}, }; /* 保存readline命令行历史的文件. */ static const char _cli_history_file[128] = "/home/embed/.command_line_history"; /* 保存readline命令行内容的buf. */ static char *_cli_cmd_inpurt = NULL; /* 当前cli所在的节点. */ static CLI_NODE_E _cli_now = CLI_NODE_PASSWORD; /* 命令行用户链表. */ static struct list_head _cli_user_list; /* cli线程pid. */ static pthread_t _cli_pid; /* Private function prototypes -----------------------------------------------*/ extern char* version_get(); extern char* version_date_get(); static int8_t _cli_param_num_get(const char *cmd_str); static cli_list_item_t *_cli_cmd_search(const char *cmd_str, CLI_NODE_E node); /* Internal functions --------------------------------------------------------*/ /* 将node节点下的命令复制到cmd_list_new链表中并排序. */ static int32_t _cli_cmd_copy_for_node(struct list_head *cmd_list_new, CLI_NODE_E node) { struct list_head *cmd_list = NULL; cli_list_item_t *cmd_item = NULL; cli_list_item_t *cmd_item_new = NULL; cli_list_item_t *cmd_item_temp = NULL; cli_list_item_t *cmd_item_next = NULL; int32_t rv = E_NONE; cmd_list = &_cli_node[node].list; list_for_each_entry(cmd_item, cmd_list, list) { /* 复制命令 */ cmd_item_new = XMALLOC(MTYPE_CLI, sizeof(cli_list_item_t)); if (NULL == cmd_item_new) { goto CMD_COPY_FOR_NODE_ERROR; } memcpy(cmd_item_new, cmd_item, sizeof(cli_list_item_t)); /* 命令排序 */ list_for_each_entry_reverse(cmd_item_temp, cmd_list_new, list) { if (strcmp(cmd_item_new->cmd->cmd_str, cmd_item_temp->cmd->cmd_str) > 0) { list_add(&cmd_item_new->list, &cmd_item_temp->list); break; } } /* 最小的命令放在链表头 */ if (&cmd_item_temp->list == cmd_list_new) { list_add(&cmd_item_new->list, &cmd_item_temp->list); } } return rv; /* 错误回滚已经申请的内存 */ CMD_COPY_FOR_NODE_ERROR: list_for_each_entry_safe(cmd_item_temp, cmd_item_next, cmd_list_new, list) { list_del(&cmd_item_temp->list); XFREE(MTYPE_CLI, cmd_item_temp); } return rv; } /* The callback function tyhat is executed when "help" is entered. This is the only default command that is always present. */ static int32_t _cli_help(const char *cmd_str) { struct list_head cmd_list_new; cli_list_item_t *cmd_item_temp = NULL; cli_list_item_t *cmd_item_next = NULL; INIT_LIST_HEAD(&cmd_list_new); /* 复制命令 */ LD_E_RETURN(DBG_M_CLI, _cli_cmd_copy_for_node(&cmd_list_new, _cli_now)); LD_E_RETURN(DBG_M_CLI, _cli_cmd_copy_for_node(&cmd_list_new, CLI_NODE_COMMON)); /* 打印信息 */ list_for_each_entry(cmd_item_temp, &cmd_list_new, list) { printf("%-32s", cmd_item_temp->cmd->cmd_str); printf("%s\r\n", cmd_item_temp->cmd->help_str); } printf("\n"); /* 释放内存 */ list_for_each_entry_safe(cmd_item_temp, cmd_item_next, &cmd_list_new, list) { list_del(&cmd_item_temp->list); XFREE(MTYPE_CLI, cmd_item_temp); } return E_NONE; } static int32_t _cli_version(const char *cmd_str) { printf("Welcome To Power IoT Application, By LandPower Embed Application Team.\r\n"); printf("Copyright(c) 2021 LandPower. All rights reserved.\r\n"); printf("Software version %s, compile time is %s.\r\n\n", version_get(), version_date_get()); return E_NONE; } static int32_t _cli_exit(const char *cmd_str) { cli_quit_node(); return E_NONE; } /* Return the number of parameters that follow the command name. */ static int8_t _cli_param_num_get(const char *cmd_str) { int8_t num = 0; bool is_end = FALSE; /* Count the number of space delimited words in pcCommandString. */ while(*cmd_str != 0) { if (' ' == (*cmd_str)) { if (is_end != TRUE) { num++; is_end = TRUE; } } else { is_end = FALSE; } cmd_str++; } /* If the command string ended with spaces, then there will have been too many parameters counted. */ if( is_end == TRUE ) { num--; } /* The value returned is one less than the number of space delimited words, as the first word should be the command itself. */ return num; } /* 命令行查找 */ static cli_list_item_t *_cli_cmd_search(const char *cmd_str, CLI_NODE_E node) { struct list_head *cmd_list = &_cli_node[node].list; cli_list_item_t *cmd_item = NULL; const char *cmd_str_register = NULL; size_t cmd_str_len = 0; /* Search for the command string in the list of registered commands. */ list_for_each_entry(cmd_item, cmd_list, list) { cmd_str_register = cmd_item->cmd->cmd_str; cmd_str_len = strlen(cmd_str_register); /* To ensure the string lengths match exactly, so as not to pick up a sub-string of a longer command, check the byte after the expected end of the string is either the end of the string or a space before a parameter. */ if ((cmd_str[cmd_str_len] != ' ') && (cmd_str[cmd_str_len] != 0x00)) { continue; } if (0 == strncmp(cmd_str, cmd_str_register, cmd_str_len)) { return cmd_item; } } return NULL; } /* 获取命令行前缀 */ char *_cli_prefix_get(char *host) { static char buf[100] = {0}; if (0xffff == _cli_node[_cli_now].index) { snprintf(buf, sizeof(buf), "%s(%s)> ", host, _cli_node[_cli_now].name); } else { snprintf(buf, sizeof(buf), "%s(%s-%d)> ", host, _cli_node[_cli_now].name, _cli_node[_cli_now].index); } return buf; } /* readline初始化 */ void _cli_readline_init(void) { /* 修改自己的按键操作. */ //rl_bind_key('?', (rl_command_func_t *)vtysh_rl_question); //rl_bind_key('\t', (rl_command_func_t *)vtysh_rl_completion); /* do not append space after completion. It will be appended * in new_completion() function explicitly. */ rl_completion_append_character = '\0'; read_history(_cli_history_file); } /* 密码处理函数 */ static int32_t _cli_process_password(const char * const cmd_str) { static cli_user_t user_login; cli_user_t *user = NULL; /* 表示在输入username */ if (0 == _cli_node[_cli_now].index) { /* 保存username,并进入输入password模式 */ _cli_node[_cli_now].index = 1; snprintf(_cli_node[_cli_now].name, CLI_NODE_MAME_LEN, "Password: "); snprintf(user_login.username, CLI_USERNAME_LEN, "%s", cmd_str); return E_NONE; } else { _cli_node[_cli_now].index = 0; snprintf(_cli_node[_cli_now].name, CLI_NODE_MAME_LEN, "Username: "); /* 保存password,比较输入正确性 */ snprintf(user_login.password, CLI_USERNAME_LEN, "%s", cmd_str); list_for_each_entry(user, &_cli_user_list, list) { if (0 == strcmp(user_login.username, user->username) && 0 == strcmp(user_login.password, user->password)) { /* 找到匹配 */ printf("\r\n\n"); cli_entry_node(CLI_NODE_CONFIG, 0xffff); return E_NONE; } } } return E_NOT_FOUND; } /* 添加一个新用户 */ int32_t _cli_user_add(const cli_user_t* const user) { cli_user_t *user_new = NULL; user_new = XMALLOC(MTYPE_CLI, sizeof(cli_user_t)); if (NULL == user_new) { return E_MEM; } memcpy(user_new, user, sizeof(cli_user_t)); list_add(&user_new->list, &_cli_user_list); return E_NONE; } void *_cli_handle(void *arg) { char *cmd = NULL; /* 主循环 */ for(;;) { /* 输入命令. */ cmd = cli_shell_gets(); if (NULL == cmd) { continue; } /* 执行命令. */ cli_process_command(cmd); } } /* Interface functions -------------------------------------------------------*/ /* description: 命令行注册函数. param: cmd -- 要注册的命令行结构体 node -- 要注册到的节点 return: E_NONE -- OK 其他 -- ERROR */ int32_t cli_register_command(const cli_command_t* const cmd, CLI_NODE_E node) { struct list_head *cmd_list = &_cli_node[node].list; cli_list_item_t *cmd_item = NULL; /* Check the parameter is not NULL. */ if (NULL == cmd) { return E_NULL; } /* Create a new list item that will reference the command being registered. */ cmd_item = XMALLOC(MTYPE_CLI, sizeof(cli_list_item_t)); if (NULL == cmd_item) { return E_MEM; } /* Reference the command being registered from the newly created list item. */ cmd_item->cmd = cmd; /* The new list item will get added to the end of the list. */ list_add(&cmd_item->list, cmd_list); return E_NONE; } /* description: 命令行注册函数. param: cmd_str -- 要执行的命令行 return: E_NONE -- OK 其他 -- ERROR */ int32_t cli_process_command(const char * const cmd_str) { static const cli_list_item_t *cmd_item = NULL; int32_t rv = E_NONE; /* 输入密码的过程不同于执行命令的过程 */ if (CLI_NODE_PASSWORD == _cli_now) { if (_cli_process_password(cmd_str) != E_NONE) { printf("Username or password is not match!!!\r\n\n"); return E_NOT_FOUND; } return E_NONE; } cmd_item = _cli_cmd_search(cmd_str, (CLI_NODE_E)_cli_now); if (NULL == cmd_item) { cmd_item = _cli_cmd_search(cmd_str, CLI_NODE_COMMON); } /* The command has been found. Check it has the expected number of parameters. If cExpectedNumberOfParameters is -1, then there could be a variable number of parameters and no check is made. */ if (cmd_item != NULL) { if (cmd_item->cmd->param_num >= 0 && _cli_param_num_get(cmd_str) > cmd_item->cmd->param_num ) { rv = E_BAD_PARAM; } } if ((cmd_item != NULL) && (rv != E_NONE)) { /* The command was found, but the number of parameters with the command was incorrect. */ printf("%s", cli_param_erro); cmd_item = NULL; } else if(cmd_item != NULL) { /* Call the callback function that is registered to this command. */ rv = cmd_item->cmd->function(cmd_str); if (rv != E_NONE) { printf("Command(%s) return %d.\r\n\n", cmd_item->cmd->cmd_str, rv); } } else { /* pxCommand was NULL, the command was not found. */ printf("Command not recognised.\r\n\n"); rv = E_NOT_FOUND; } return rv; } /* description: 进入命令行节点. param: cmd_str -- 命令字符串 param_index -- 需要获取的参数索引 param_len -- 获取的参数长度 return: char* -- 获取的参数字符串 */ const char *cli_parameter_get(const char *cmd_str, int32_t param_index, OUT int32_t *param_len) { int32_t index = 0; const char *param_str = NULL; *param_len = 0; while(index < param_index) { /* Index the character pointer past the current word. If this is the start of the command string then the first word is the command itself. */ while(((*cmd_str) != 0x00 ) && ((*cmd_str) != ' ')) { cmd_str++; } /* Find the start of the next string. */ while(((*cmd_str) != 0x00) && (' ' == (*cmd_str))) { cmd_str++; } /* Was a string found? */ if (*cmd_str != 0x00) { /* Is this the start of the required parameter? */ index++; if (index == param_index) { /* How long is the parameter? */ param_str = cmd_str; while(((*cmd_str) != 0x00 ) && ((*cmd_str) != ' ')) { (*param_len)++; cmd_str++; } if (*param_len == 0 || *param_len >= CLI_PARAM_LEN) { param_str = NULL; } break; } } else { break; } } return param_str; } /* description: 进入命令行节点. param: node -- 节点号 index -- 节点内部索引 return: E_NONE -- OK 其他 -- ERROR */ int32_t cli_entry_node(CLI_NODE_E node, int16_t index) { if (node >= CLI_NODE_COUNT) { return E_BAD_PARAM; } _cli_now = node; _cli_node[_cli_now].index = index; return E_NONE; } /* description: 返回上一级节点. param: return: */ void cli_quit_node(void) { _cli_now = _cli_node[_cli_now].upper_node; } /* description: 命令行读取. param: return: char* -- 读取的一行命令 */ char *cli_shell_gets(void) { HIST_ENTRY *last = NULL; struct termios new_setting,init_setting; /* readline要求自己释放其返回的buf. */ if (_cli_cmd_inpurt) { free(_cli_cmd_inpurt); _cli_cmd_inpurt = NULL; } /* 获取一行命令. */ if (CLI_NODE_PASSWORD == _cli_now) { /* 如果是输入密码则关闭回显. */ if (1 == _cli_node[_cli_now].index) { tcgetattr(0, &init_setting); new_setting = init_setting; new_setting.c_lflag &= ~ECHO; tcsetattr(0,TCSANOW,&new_setting); } _cli_cmd_inpurt = readline(_cli_node[_cli_now].name); if (1 == _cli_node[_cli_now].index) { tcsetattr(0,TCSANOW,&init_setting); } } else { _cli_cmd_inpurt = readline(_cli_prefix_get("Power_IoT")); /* 如果命令有效记录历史. */ if (_cli_cmd_inpurt && *_cli_cmd_inpurt) { using_history(); last = previous_history(); if (!last || strcmp (last->line, _cli_cmd_inpurt) != 0) { add_history(_cli_cmd_inpurt); append_history(1, _cli_history_file); } } } //if (strlen(_cli_cmd_inpurt) > 0) //{ // log_warn(LOG_CLI, "\"%s\" is input.", _cli_cmd_inpurt); //} return _cli_cmd_inpurt; } /* description: 命令行初始函数. param: return: */ int32_t cli_init(void) { struct sched_param param; pthread_attr_t attr; cli_user_t user_default; int32_t i = 0; int32_t rv = 0; /* 初始化readline */ _cli_readline_init(); /* 初始化命令节点数据链表 */ for(i = 0; i < CLI_NODE_COUNT; i++) { INIT_LIST_HEAD(&_cli_node[i].list); } /* 注册公有命令 */ cli_register_command(&_cli_cmd_help, CLI_NODE_COMMON); cli_register_command(&_cli_cmd_version, CLI_NODE_COMMON); cli_register_command(&_cli_cmd_exit, CLI_NODE_COMMON); /* 初始化用户列表 */ INIT_LIST_HEAD(&_cli_user_list); /* 添加默认用户 */ memset(&user_default, 0, sizeof(cli_user_t)); snprintf(user_default.username, CLI_USERNAME_LEN, CLI_USERNAME_DEFAULT); snprintf(user_default.password, CLI_USERNAME_LEN, CLI_PASSWORD_DEFAULT); _cli_user_add(&user_default); /* 配置线程RR调度,优先级50 */ pthread_attr_init(&attr); param.sched_priority = 10; pthread_attr_setschedpolicy(&attr, SCHED_RR); pthread_attr_setschedparam(&attr, ¶m); pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); rv = pthread_create(&_cli_pid, &attr, _cli_handle, NULL); if (rv != 0) { DBG(DBG_M_CLI, "Can't create pthread %d\r\n", rv); } else { thread_m_add("CLI", _cli_pid); } pthread_attr_destroy(&attr); return E_NONE; } /************************ (C) COPYRIGHT LandPower ***** END OF FILE ****************/