You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

689 lines
20 KiB
Plaintext

/******************************************************************************
* 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
*
* <h2><center>&copy; COPYRIGHT(c) 2021 LandPower</center></h2>
*
* 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 <termios.h>
#include <pthread.h>
/* 外部程序库头文件. */
#include <readline/readline.h>
#include <readline/history.h>
/* 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, &param);
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 ****************/