一、项目介绍
本次任务使用MCXA346开发板并利用AI辅助实现一个带缓冲区的shell,核心是实现一个能读取、解析并执行用户输入命令的简易命令行解释器,并管理好输入缓冲区。基本要求:程序需能显示命令提示符,并循环接收用户输入(\n换行符为断句符号)。管理好输入缓冲区,妥善处理字符,并能进行基本的命令解析。
1.1、硬件介绍
NXP FRDM-MCXA346 开发板简介:

FRDM-MCXA346是一款紧凑且可扩展的开发板,可以快速基于MCX A346 MCU开展原型设计。它们提供行业标准的接口,可轻松访问MCU的I/O,配备集成的开放标准串行接口、外部闪存和板载MCU-Link调试器。
关键参数:
- MCX A346 Arm® Cortex®-M33内核,运行频率高达180MHz,1MB闪存,256KB RAM,带8KB的纠错码(ECC)
- 拥有双FlexPWM、4组16位ADC、专用MAU数学加速器以及SmartDMA
- 高速通用串行总线(HS USB)Type-C连接器(板载MCU-Link调试器),支持CAN/I3C/SPI/I²C/UART连接器(Arduino、PMOD/mikroBUS、DNP)
- 带有CMSIS-DAP的板载MCU-Link调试器 ,JTAG/SWD连接器
- 兼容Arduino、mikroBUS、Pmod多种生态
- 适合:工业自动化、电机控制、IoT边缘计算
1.2、任务功能设计目标思路
本次任务是实现一个串口shell。需要实现串口的发送和接收。然后解析串口接收的字符实现命令解析。计划支持上下左右按键,tab按钮等控制字符解析。然后就是上下翻历史记录功能,最后就是shell用户控制权限命令功能。
二、任务实现
2.1、软件流程

2.2、实现过程
首先新建基于MCXA346的串口的工程模板,实现串口发送接收。

然后利用AI实现shell代码核心功能。下面是使用deepseek的实现shell过程。





最终经过多轮对话实现了完整的shell核心代码和实例。
#ifndef _LSY_SHELL_CORE_H_
#define _LSY_SHELL_CORE_H_
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include "lsy_shell_config.h"
// 错误码定义
typedef enum {
SHELL_OK = 0,
SHELL_ERR,
SHELL_ERR_CMD_NOT_FOUND,
SHELL_ERR_PARAM,
SHELL_ERR_BUFFER_FULL,
SHELL_ERR_CMD_EXISTS,
SHELL_ERR_CMD_TABLE_FULL,
SHELL_ERR_MEMORY,
// ==================== 新增:权限相关错误码 ========
SHELL_ERR_PERMISSION_DENIED, // 权限不足
SHELL_ERR_USER_NOT_FOUND, // 用户不存在
SHELL_ERR_INVALID_PASSWORD, // 密码错误
SHELL_ERR_USER_LOCKED, // 用户被锁定
SHELL_ERR_NOT_LOGGED_IN, // 未登录
SHELL_ERR_LOGIN_TIMEOUT, // 登录超时
// ==================== 新增结束 ====================
} shell_status_t;
// ==================== 新增:权限级别枚举 ====================
typedef enum {
SHELL_PERMISSION_NONE = 0, // 无权限
SHELL_PERMISSION_GUEST = 1, // 访客权限
SHELL_PERMISSION_USER = 2, // 普通用户权限
SHELL_PERMISSION_ADMIN = 3, // 管理员权限
SHELL_PERMISSION_ROOT = 8, // 超级用户权限
} shell_permission_level_t;
// 权限位掩码定义(用于命令权限控制)
#define SHELL_PERM_MASK_GUEST 0x01 // 访客位
#define SHELL_PERM_MASK_USER 0x02 // 普通用户位
#define SHELL_PERM_MASK_ADMIN 0x04 // 管理员位
#define SHELL_PERM_MASK_ROOT 0x80 // 超级用户位
#define SHELL_PERM_MASK_ALL (SHELL_PERM_MASK_USER | SHELL_PERM_MASK_ADMIN | \
SHELL_PERM_MASK_ROOT | SHELL_PERM_MASK_GUEST) // 所有权限
// 默认权限配置
#define SHELL_PERM_DEFAULT_GUEST SHELL_PERM_MASK_GUEST // 访客默认权限
#define SHELL_PERM_DEFAULT_USER SHELL_PERM_MASK_USER // 普通用户默认权限
#define SHELL_PERM_DEFAULT_ADMIN SHELL_PERM_MASK_ADMIN // 管理员默认权限
#define SHELL_PERM_DEFAULT_ROOT SHELL_PERM_MASK_ROOT // 超级用户默认权限
// ==================== 新增结束 ====================
// IO操作结构体
typedef struct {
int (*init)(void);
int (*send)(const char *data, size_t len);
int (*receive)(char *buffer, size_t max_len);
int (*ctrl)(uint32_t cmd,uint8_t *para);
} shell_io_ops_t;
// 命令处理函数类型
typedef shell_status_t (*shell_cmd_handler_t)(int argc, char **argv);
// 命令项结构
typedef struct shell_command {
const char *name; // 命令名
const char *description; // 命令描述
shell_cmd_handler_t handler; // 处理函数
void *user_data; // 用户数据
struct shell_command *next; // 链表指针
uint8_t is_static; // 静态命令标志
// ========== 新增:命令权限字段 ====================
uint32_t permission_mask; // 命令所需权限掩码(哪些权限级别的用户可以执行)
} shell_command_t;
// ==================== 新增:用户信息结构体 ====================
typedef struct shell_user {
char username[SHELL_USERNAME_MAX_LEN]; // 用户名
char password[SHELL_PASSWORD_MAX_LEN]; // 密码(实际使用中应存储哈希值)
shell_permission_level_t permission_level; // 用户权限级别
uint32_t permission_mask; // 用户权限掩码(可访问的权限位)
uint32_t last_login_time; // 最后登录时间
uint8_t is_locked; // 是否被锁定
uint8_t login_attempts; // 登录尝试次数
uint8_t is_logged_in; // 当前是否已登录
struct shell_user *next; // 链表指针
} shell_user_t;
// 登录状态枚举
typedef enum {
LOGIN_STATE_LOGGED_OUT = 0, // 未登录
LOGIN_STATE_LOGGED_IN, // 已登录
LOGIN_STATE_WAITING_PASSWORD, // 等待输入密码
LOGIN_STATE_CHANGING_PASSWORD // 正在修改密码
} shell_login_state_t;
// ==================== 新增结束 ====================
// Shell上下文结构
typedef struct {
shell_io_ops_t io; // IO操作
char input_buffer[SHELL_BUFFER_SIZE]; // 输入缓冲区
size_t buf_pos; // 缓冲区位置
shell_command_t *cur_cmd; // 记录当前的命令
shell_command_t *cmd_list; // 命令链表头
shell_command_t *static_cmds;// 静态命令表
size_t static_cmd_count; // 静态命令数量
char prompt[32]; // 提示符
void *user_context; // 用户上下文
uint8_t echo_enabled; // 回显使能
uint8_t initialized; // 初始化标志
#if SHELL_HISTORY_SIZE > 0
// 历史记录相关字段
char history_buffer[SHELL_HISTORY_BUFFER_SIZE]; // 历史记录存储缓冲区
char *history[SHELL_HISTORY_SIZE]; // 历史记录索引数组
size_t history_count; // 当前历史记录数量
size_t history_pos; // 当前浏览位置
int history_index; // 最新命令索引
#endif
#if SHELL_USERS_ENABLE == 1
// ==================== 新增:用户和权限管理相关字段 ====================
shell_user_t *user_list; // 用户链表头
shell_user_t *current_user; // 当前登录用户
shell_login_state_t login_state; // 登录状态
char login_username[SHELL_USERNAME_MAX_LEN]; // 登录中的用户名
uint32_t login_start_time; // 登录开始时间
uint8_t login_attempts; // 当前登录尝试次数
uint8_t require_login; // 是否需要登录才能使用shell
// ==================== 新增结束 ====================
#endif
} shell_context_t;
// 初始化Shell
shell_status_t shell_init(shell_context_t *ctx,
shell_io_ops_t *io_ops,
const char *prompt);
// 注册静态命令表
shell_status_t shell_register_static_table(shell_context_t *ctx,
shell_command_t *cmd_table,
size_t cmd_count);
// 动态注册命令
shell_status_t shell_register_user_cmd(shell_context_t *ctx,
const char *name,
const char *description,
shell_cmd_handler_t handler,
void *user_data,
uint32_t permission_mask // 新增:命令所需权限
);
#define shell_register_command(ctx,name,desc,handler,usr_data) \
shell_register_user_cmd(ctx,name,desc,handler,usr_data,SHELL_PERM_MASK_ALL)
// 动态删除命令
shell_status_t shell_unregister_command(shell_context_t *ctx,
const char *name);
// 获取当前命令
shell_command_t* shell_get_current_cmd(shell_context_t *ctx);
// 查找命令
shell_command_t* shell_find_command(shell_context_t *ctx,
const char *name);
// 处理输入字符
shell_status_t shell_process_char(shell_context_t *ctx, char ch);
// 处理字符串
shell_status_t shell_process_string(shell_context_t *ctx, const char *str);
// 打印帮助信息
void shell_print_help(shell_context_t *ctx);
// 列出所有命令
void shell_list_commands(shell_context_t *ctx);
// 输出字符串
void shell_print(shell_context_t *ctx, const char *str);
// 输出格式化字符串
void shell_printf(shell_context_t *ctx, const char *format, ...);
// 设置回显
void shell_set_echo(shell_context_t *ctx, uint8_t enable);
// 获取命令数量
size_t shell_get_command_count(shell_context_t *ctx);
// 清理所有动态命令
shell_status_t shell_cleanup_dynamic_commands(shell_context_t *ctx);
// 获取静态命令表
shell_command_t* get_static_commands(size_t *count);
// 初始化内存保护配置
void memory_view_init(shell_context_t *ctx);
// ==================== 新增:用户和权限管理API ====================
#if SHELL_USERS_ENABLE == 1
// 用户管理
shell_status_t shell_user_add(shell_context_t *ctx,
const char *username,
const char *password,
shell_permission_level_t level);
shell_status_t shell_user_remove(shell_context_t *ctx, const char *username);
shell_status_t shell_user_change_password(shell_context_t *ctx,
const char *username,
const char *old_password,
const char *new_password);
shell_status_t shell_user_set_permission(shell_context_t *ctx,
const char *username,
shell_permission_level_t level);
shell_status_t shell_user_lock(shell_context_t *ctx, const char *username);
shell_status_t shell_user_unlock(shell_context_t *ctx, const char *username);
shell_user_t* shell_user_find(shell_context_t *ctx, const char *username);
// 登录/登出
shell_status_t shell_login(shell_context_t *ctx,
const char *username,
const char *password);
shell_status_t shell_logout(shell_context_t *ctx);
uint8_t shell_is_logged_in(shell_context_t *ctx);
shell_permission_level_t shell_get_current_permission(shell_context_t *ctx);
// 权限检查
uint8_t shell_check_command_permission(shell_context_t *ctx,
shell_command_t *cmd);
// 初始化默认用户(管理员和访客)
shell_status_t shell_init_default_users(shell_context_t *ctx);
// 列出所有用户
void shell_list_users(shell_context_t *ctx);
#endif
// ==================== 新增结束 ====================
//ls shell port API
int lsy_shell_init(shell_io_ops_t * io_ops);
int lsy_shell_input(uint8_t ch);
extern shell_context_t g_lsy_shell;
#endif // _LSY_SHELL_CORE_H_
核心代码
#include "lsy_shell_core.h"
// 内部函数声明
static int shell_split_args(char *str, char *argv[], int max_args);
static void shell_execute_command(shell_context_t *ctx,
shell_command_t *cmd,
int argc, char **argv);
static void shell_redisplay_line(shell_context_t *ctx);
#if SHELL_HISTORY_SIZE > 0
// 历史记录功能函数声明
static void shell_history_add(shell_context_t *ctx, const char *cmd);
static void shell_history_prev(shell_context_t *ctx);
static void shell_history_next(shell_context_t *ctx);
static void shell_history_clear(shell_context_t *ctx); // 清除历史记录
static void shell_history_list(shell_context_t *ctx); // 显示历史记录
static void shell_history_compact(shell_context_t *ctx); // 整理历史记录缓冲区
static void shell_history_save(shell_context_t *ctx);
static void shell_history_load(shell_context_t *ctx);
#endif
// ==================== 新增:用户管理内部函数声明 ====================
#if SHELL_USERS_ENABLE == 1
static shell_user_t* shell_user_alloc(void);
static void shell_user_free(shell_user_t *user);
static uint32_t shell_permission_level_to_mask(shell_permission_level_t level);
static void shell_prompt_update(shell_context_t *ctx);
static shell_status_t shell_handle_login_sequence(shell_context_t *ctx, const char *input);
static uint8_t shell_verify_password(const char *input_pass, const char *stored_pass);
#endif
// ==================== 新增结束 ====================
// 分配新命令节点
static shell_command_t* shell_alloc_command(void)
{
shell_command_t *cmd = (shell_command_t*)malloc(sizeof(shell_command_t));
if (cmd) {
memset(cmd, 0, sizeof(shell_command_t));
}
return cmd;
}
// 释放命令节点
static void shell_free_command(shell_command_t *cmd)
{
if (cmd && !cmd->is_static) {
free(cmd);
}
}
// 初始化Shell
shell_status_t shell_init(shell_context_t *ctx,
shell_io_ops_t *io_ops,
const char *prompt)
{
if (!ctx || !io_ops) {
return SHELL_ERR;
}
memset(ctx, 0, sizeof(shell_context_t));
ctx->io = *io_ops;
ctx->buf_pos = 0;
ctx->cur_cmd = NULL;
ctx->cmd_list = NULL;
ctx->static_cmds = NULL;
ctx->static_cmd_count = 0;
ctx->echo_enabled = 1;
ctx->initialized = 1;
#if SHELL_HISTORY_SIZE > 0
// 初始化历史记录
ctx->history_count = 0;
ctx->history_pos = 0;
ctx->history_index = -1;
memset(ctx->history_buffer, 0, sizeof(ctx->history_buffer));
memset(ctx->history, 0, sizeof(ctx->history));
#endif
#if SHELL_USERS_ENABLE == 1
// ==================== 新增:初始化用户管理 ====================
ctx->user_list = NULL;
ctx->current_user = NULL;
ctx->login_state = LOGIN_STATE_LOGGED_OUT;
ctx->login_username[0] = '\0';
ctx->login_attempts = 0;
ctx->require_login = 1; // 默认需要登录
// 初始化默认用户
shell_init_default_users(ctx);
// 设置初始提示符
if (ctx->require_login) {
strcpy(ctx->prompt, "login: ");
} else
#endif
if (prompt) {
strncpy(ctx->prompt, prompt, sizeof(ctx->prompt) - 1);
ctx->prompt[sizeof(ctx->prompt) - 1] = '\0';
} else {
strcpy(ctx->prompt, "lsy:>");
}
// ==================== 新增结束 ====================
// 打印欢迎信息
shell_print(ctx, "\r\n=== Little Simple Dynamic Shell By LiuShuiyuan===\r\n");
shell_print(ctx, "Commands can be registered and unregistered dynamically\r\n");
#if SHELL_HISTORY_SIZE > 0
shell_printf(ctx, "History support: %d commands can be saved\r\n", SHELL_HISTORY_SIZE);
#endif
#if SHELL_USERS_ENABLE == 1
// ==================== 新增:显示登录提示 ====================
if (ctx->require_login) {
shell_print(ctx, "Please login with username and password\r\n");
shell_printf(ctx, "%s", ctx->prompt);
} else
#endif
{
shell_print(ctx, "Type 'help' or 'list' for available commands\r\n");
shell_printf(ctx, "%s", ctx->prompt);
}
return SHELL_OK;
}
// 注册静态命令表
shell_status_t shell_register_static_table(shell_context_t *ctx,
shell_command_t *cmd_table,
size_t cmd_count) {
if (!ctx || !cmd_table) {
return SHELL_ERR;
}
ctx->static_cmds = cmd_table;
ctx->static_cmd_count = cmd_count;
// 标记静态命令
for (size_t i = 0; i < cmd_count; i++) {
cmd_table[i].is_static = 1;
}
return SHELL_OK;
}
// 动态注册命令
shell_status_t shell_register_user_cmd(shell_context_t *ctx,
const char *name,
const char *description,
shell_cmd_handler_t handler,
void *user_data,
uint32_t permission_mask // 新增:命令所需权限
)
{
if (!ctx || !name || !handler) {
return SHELL_ERR;
}
// 检查命令是否已存在
shell_command_t *existing = shell_find_command(ctx, name);
if (existing) {
return SHELL_ERR_CMD_EXISTS;
}
// 创建新命令节点
shell_command_t *new_cmd = shell_alloc_command();
if (!new_cmd) {
return SHELL_ERR_MEMORY;
}
// 填充命令信息
new_cmd->name = name;
new_cmd->description = description ? description : "No description";
new_cmd->handler = handler;
new_cmd->user_data = user_data;
new_cmd->is_static = 0;
new_cmd->next = NULL;
// ==================== 新增:设置命令权限 ====================
new_cmd->permission_mask = permission_mask;
// ==================== 新增结束 ====================
// 添加到链表头部
new_cmd->next = ctx->cmd_list;
ctx->cmd_list = new_cmd;
return SHELL_OK;
}
// 动态删除命令
shell_status_t shell_unregister_command(shell_context_t *ctx,
const char *name)
{
if (!ctx || !name) {
return SHELL_ERR;
}
// 跳过内置命令
// if (strcmp(name, "help") == 0 || strcmp(name, "list") == 0 || strcmp(name, "echo") == 0) {
// shell_printf(ctx, "Cannot unregister built-in command '%s'\r\n", name);
// return SHELL_ERR;
// }
shell_command_t *prev = NULL;
shell_command_t *curr = ctx->cmd_list;
// 查找要删除的命令
while (curr) {
if (strcmp(curr->name, name) == 0) {
if (curr->is_static) {
shell_printf(ctx, "Cannot unregister static command '%s'\r\n", name);
return SHELL_ERR;
}
// 从链表中删除
if (prev) {
prev->next = curr->next;
} else {
ctx->cmd_list = curr->next;
}
// 释放内存
shell_free_command(curr);
shell_printf(ctx, "Command '%s' unregistered successfully\r\n", name);
return SHELL_OK;
}
prev = curr;
curr = curr->next;
}
// 检查静态命令表
if (ctx->static_cmds) {
for (size_t i = 0; i < ctx->static_cmd_count; i++) {
if (strcmp(ctx->static_cmds[i].name, name) == 0) {
shell_printf(ctx, "Cannot unregister static command '%s'\r\n", name);
return SHELL_ERR;
}
}
}
shell_printf(ctx, "Command '%s' not found\r\n", name);
return SHELL_ERR_CMD_NOT_FOUND;
}
// 获取当前命令
shell_command_t* shell_get_current_cmd(shell_context_t *ctx)
{
return ctx->cur_cmd;
}
// 查找命令
shell_command_t* shell_find_command(shell_context_t *ctx,
const char *name)
{
if (!ctx || !name) {
return NULL;
}
// 首先检查动态命令链表
shell_command_t *cmd = ctx->cmd_list;
while (cmd) {
if (strcmp(cmd->name, name) == 0) {
return cmd;
}
cmd = cmd->next;
}
// 然后检查静态命令表
if (ctx->static_cmds) {
for (size_t i = 0; i < ctx->static_cmd_count; i++) {
if (strcmp(ctx->static_cmds[i].name, name) == 0) {
return &ctx->static_cmds[i];
}
}
}
#if 0
// ==================== 修改:内置命令列表,添加用户管理命令 ====================
#if SHELL_USERS_ENABLE == 1
if (strcmp(name, "help") == 0) {
static shell_command_t cmd_help = {
.name = "help",
.handler = NULL,
.is_static = 1,
.permission_mask = SHELL_PERM_MASK_ALL // 所有用户可访问
};
return &cmd_help;
} else if (strcmp(name, "list") == 0) {
static shell_command_t cmd_list = {
.name = "list",
.handler = NULL,
.is_static = 1,
.permission_mask = SHELL_PERM_MASK_ALL
};
return &cmd_list;
} else if (strcmp(name, "echo") == 0) {
static shell_command_t cmd_echo = {
.name = "echo",
.handler = NULL,
.is_static = 1,
.permission_mask = SHELL_PERM_MASK_ALL
};
return &cmd_echo;
} else if (strcmp(name, "history") == 0) {
static shell_command_t cmd_history = {
.name = "history",
.handler = NULL,
.is_static = 1,
.permission_mask = SHELL_PERM_MASK_ALL
};
return &cmd_history;
} else if (strcmp(name, "login") == 0) {
static shell_command_t cmd_login = {
.name = "login",
.handler = NULL,
.is_static = 1,
.permission_mask = SHELL_PERM_MASK_ALL
};
return &cmd_login;
} else if (strcmp(name, "logout") == 0) {
static shell_command_t cmd_logout = {
.name = "logout",
.handler = NULL,
.is_static = 1,
.permission_mask = SHELL_PERM_MASK_ALL
};
return &cmd_logout;
} else if (strcmp(name, "passwd") == 0) {
static shell_command_t cmd_passwd = {
.name = "passwd",
.handler = NULL,
.is_static = 1,
.permission_mask = SHELL_PERM_MASK_ALL
};
return &cmd_passwd;
} else if (strcmp(name, "users") == 0) {
static shell_command_t cmd_users = {
.name = "users",
.handler = NULL,
.is_static = 1,
.permission_mask = SHELL_PERM_MASK_ADMIN | SHELL_PERM_MASK_ROOT
};
return &cmd_users;
} else if (strcmp(name, "useradd") == 0) {
static shell_command_t cmd_useradd = {
.name = "useradd",
.handler = NULL,
.is_static = 1,
.permission_mask = SHELL_PERM_MASK_ADMIN | SHELL_PERM_MASK_ROOT
};
return &cmd_useradd;
} else if (strcmp(name, "userdel") == 0) {
static shell_command_t cmd_userdel = {
.name = "userdel",
.handler = NULL,
.is_static = 1,
.permission_mask = SHELL_PERM_MASK_ADMIN | SHELL_PERM_MASK_ROOT
};
return &cmd_userdel;
} else if (strcmp(name, "userlock") == 0) {
static shell_command_t cmd_userlock = {
.name = "userlock",
.handler = NULL,
.is_static = 1,
.permission_mask = SHELL_PERM_MASK_ADMIN | SHELL_PERM_MASK_ROOT
};
return &cmd_userlock;
} else if (strcmp(name, "userunlock") == 0) {
static shell_command_t cmd_userunlock = {
.name = "userunlock",
.handler = NULL,
.is_static = 1,
.permission_mask = SHELL_PERM_MASK_ADMIN | SHELL_PERM_MASK_ROOT
};
return &cmd_userunlock;
}
// ==================== 修改结束 ====================
#else
// 检查内置命令
if (strcmp(name, "help") == 0) {
// 返回一个虚拟命令结构
static shell_command_t cmd_help = {
.name = "help",
.handler = NULL,
.is_static = 1
};
return &cmd_help;
}else if (strcmp(name, "list") == 0) {
// 返回一个虚拟命令结构
static shell_command_t cmd_list = {
.name = "list",
.handler = NULL,
.is_static = 1
};
return &cmd_list;
}else if (strcmp(name, "echo") == 0) {
// 返回一个虚拟命令结构
static shell_command_t cmd_echo = {
.name = "echo",
.handler = NULL,
.is_static = 1
};
return &cmd_echo;
}
#if SHELL_HISTORY_SIZE > 0
else if (strcmp(name, "history") == 0) {
static shell_command_t cmd_history = {
.name = "history",
.handler = NULL,
.is_static = 1
};
return &cmd_history;
}
#endif
#endif
#endif
return NULL;
}
/**
* @brief 字符串分割函数(增强版,支持转义字符)
* @param str 输入字符串(会被修改)
* @param argv 输出参数数组
* @param max_args 最大参数个数
* @return 解析出的参数个数
*
* 支持的特性:
* 1. 空格分隔参数
* 2. 双引号内的内容作为一个参数(支持转义)
* 3. 转义字符:\" 表示一个字面双引号,\\ 表示一个字面反斜杠
* 4. 支持转义空格(如:a\ b 作为一个参数 "a b")
*
*/
static int shell_split_args(char *str, char *argv[], int max_args)
{
int argc = 0;
int in_arg = 0;
int in_quote = 0;
int escape = 0; // 转义标志:1表示当前字符需要被转义
char *p = str; // 当前读取位置
char *q = str; // 当前写入位置
while (*p && argc < max_args)
{
if (escape) {
// 转义模式:直接复制字符,不进行特殊处理
if (in_arg) {
//已开始新参数
}else
{
//未开始新参数
argv[argc] = q; // 记录参数起始位置
argc++;
in_arg = 1;
}
*q++ = *p++;
escape = 0;
continue;
}
if (*p == '\\') {
// 遇到反斜杠,设置转义标志
escape = 1;
p++;
continue;
}
if (*p == '"') {
if (in_quote) {
// 结束引号
in_quote = 0;
*q = '\0'; // 添加字符串结束符
q++; // 跳过结束符位置(保持q和p的同步)
} else {
// 开始引号,不将引号写入目标缓冲区
in_quote = 1;
if (in_arg) {
//已开始新参数,双引号不在新参数最前面
}else
{
//未开始新参数,双引号在新参数最前面
}
}
p++;
continue;
}
// 处理空格(仅在非引号模式下作为分隔符)
if (!in_quote && (*p == ' ' || *p == '\t' || *p == '\r')) {
if (in_arg) {
// 结束当前参数
*q = '\0';
q++;
in_arg = 0;
}
p++;
continue;
}
// 开始一个新参数
if (!in_arg) {
argv[argc] = q; // 记录参数起始位置
argc++;
in_arg = 1;
}
// 复制普通字符
*q++ = *p++;
}
// 处理最后一个参数
if (in_arg) {
*q = '\0';
}
// 检查是否有未闭合的引号
if (in_quote) {
// 可以选择报错,这里简单处理为忽略最后一个不完整的参数
if (argc > 0) {
argc--; // 移除最后一个不完整的参数
}
}
argv[argc] = NULL;
return argc;
}
#if 0
// 字符串分割函数
static int shell_split_args(char *str, char *argv[], int max_args)
{
int argc = 0;
int in_arg = 0;
int in_quote = 0;
while (*str && argc < max_args) {
// 处理引号
if (*str == '"') {
in_quote = !in_quote;
*str = '\0';
str++;
continue;
}
// 跳过空白字符(不在引号内时)
if (!in_quote && (*str == ' ' || *str == '\t' || *str == '\r')) {
*str = '\0';
in_arg = 0;
str++;
continue;
}
// 开始一个新参数
if (!in_arg) {
argv[argc++] = str;
in_arg = 1;
}
str++;
}
argv[argc] = NULL;
return argc;
}
#endif
// 重新显示当前行
static void shell_redisplay_line(shell_context_t *ctx)
{
if (!ctx || !ctx->echo_enabled)
{
return;
}
// 清除当前行
shell_print(ctx, "\r\033[K");
// 显示提示符
shell_printf(ctx, "%s", ctx->prompt);
#if SHELL_USERS_ENABLE == 1
if(ctx->login_state != LOGIN_STATE_WAITING_PASSWORD){
#endif
// 显示当前输入缓冲区内容
shell_print(ctx, ctx->input_buffer);
#if SHELL_USERS_ENABLE == 1
}else
{
for (int i = 0; i < strlen(ctx->input_buffer); i++) {
shell_print(ctx, "*");
}
}
#endif
// 将光标移动到正确位置
int cursor_move = strlen(ctx->input_buffer) - ctx->buf_pos;
for (int i = 0; i < cursor_move; i++) {
shell_print(ctx, "\033[D");
}
}
// 终端控制字符状态机状态
typedef enum {
STATE_NORMAL, // 正常状态
STATE_ESC, // 收到ESC
STATE_CSI, // 收到'['
STATE_CSI_PARAM, // 解析CSI参数
STATE_SS3 // 收到'O' (SS3状态)
} shell_input_state_t;
// 处理输入字符 - 增强版,支持终端控制字符识别
shell_status_t shell_process_char(shell_context_t *ctx, char ch)
{
if (!ctx || !ctx->initialized) {
return SHELL_ERR;
}
static shell_input_state_t input_state = STATE_NORMAL;
static char csi_buffer[16]; // CSI参数缓冲区
static int csi_pos = 0;
static int esc_param1 = 0; // CSI参数1
static int esc_param2 = 0; // CSI参数2
// 状态机处理终端控制序列
switch (input_state) {
case STATE_NORMAL:
// 检查是否ESC字符
if (ch == '\x1b') {
input_state = STATE_ESC;
csi_pos = 0;
esc_param1 = 0;
esc_param2 = 0;
return SHELL_OK;
}
break;
case STATE_ESC:
// ESC后的字符
if (ch == '[') {
input_state = STATE_CSI;
csi_pos = 0;
esc_param1 = 0;
esc_param2 = 0;
return SHELL_OK;
} else if (ch == 'O') {
input_state = STATE_SS3;
return SHELL_OK;
} else {
// 未知ESC序列,重置状态
input_state = STATE_NORMAL;
return SHELL_OK;
}
case STATE_SS3:
// SS3状态:处理 F1-F4 等按键
input_state = STATE_NORMAL;
switch (ch) {
// 可以根据需要处理F1,F2,F3,F4
case 'P': // F1
return SHELL_OK;
case 'Q': // F2
return SHELL_OK;
case 'R': // F3
return SHELL_OK;
case 'S': // F4
return SHELL_OK;
case 'H': // Home
// 移动光标到行首
ctx->buf_pos = 0;
shell_redisplay_line(ctx);
return SHELL_OK;
case 'F': // End
// 移动光标到行尾
ctx->buf_pos = strlen(ctx->input_buffer);
shell_redisplay_line(ctx);
return SHELL_OK;
}
return SHELL_OK;
case STATE_CSI:
// CSI状态:处理 '[' 后的字符
if (ch >= '0' && ch <= '9') {
// 解析参数
if (csi_pos == 0) {
esc_param1 = esc_param1 * 10 + (ch - '0');
} else if (csi_pos == 1) {
esc_param2 = esc_param2 * 10 + (ch - '0');
}
csi_buffer[csi_pos++] = ch;
return SHELL_OK;
} else if (ch == ';') {
// 参数分隔符
csi_pos++;
csi_buffer[csi_pos] = '\0';
return SHELL_OK;
} else {
// 命令字符
input_state = STATE_NORMAL;
// 处理各种CSI命令
switch (ch) {
case 'A': // 上箭头 历史记录上一条
#if SHELL_HISTORY_SIZE > 0
shell_history_prev(ctx);
#endif
break;
case 'B': // 下箭头 历史记录下一条
#if SHELL_HISTORY_SIZE > 0
shell_history_next(ctx);
#endif
break;
case 'C': // 右箭头
if (ctx->buf_pos < strlen(ctx->input_buffer)) {
ctx->buf_pos++;
if (ctx->echo_enabled) {
// 发送光标右移序列
const char *move_right = "\033[C";
ctx->io.send(move_right, strlen(move_right));
}
}
break;
case 'D': // 左箭头
if (ctx->buf_pos > 0) {
ctx->buf_pos--;
if (ctx->echo_enabled) {
// 发送光标左移序列
const char *move_left = "\033[D";
ctx->io.send(move_left, strlen(move_left));
}
}
break;
case 'H': // Home键
ctx->buf_pos = 0;
shell_redisplay_line(ctx);
break;
case 'F': // End键
ctx->buf_pos = strlen(ctx->input_buffer);
shell_redisplay_line(ctx);
break;
case '~': // 带数字前缀的命令
if (esc_param1 == 3)
{
// Del键
if (ctx->buf_pos < strlen(ctx->input_buffer)) {
// 删除当前位置的字符
memmove(&ctx->input_buffer[ctx->buf_pos],
&ctx->input_buffer[ctx->buf_pos + 1],
strlen(ctx->input_buffer) - ctx->buf_pos);
shell_redisplay_line(ctx);
}
} else if (esc_param1 == 5) { // Page Up
// 可扩展功能
} else if (esc_param1 == 6) { // Page Down
// 可扩展功能
}
break;
case 'Z': // Shift+Tab 反向补全
break;
}
return SHELL_OK;
}
default:
input_state = STATE_NORMAL;
break;
}
// 处理普通字符
if (input_state == STATE_NORMAL) {
// 处理回车
if (ch == '\r' || ch == '\n')
{
if (strlen(ctx->input_buffer) > 0) {
//ctx->input_buffer[ctx->buf_pos] = '\0';
#if SHELL_HISTORY_SIZE > 0
#if SHELL_USERS_ENABLE == 1
if(ctx->login_state != LOGIN_STATE_WAITING_PASSWORD){
#endif
// 将命令添加到历史记录
shell_history_add(ctx, ctx->input_buffer);
#if SHELL_USERS_ENABLE == 1
}
#endif
#endif
// 回显输入
if (ctx->echo_enabled) {
shell_print(ctx, "\r\n");
}
// 处理命令
shell_process_string(ctx, ctx->input_buffer);
// 重置缓冲区
ctx->buf_pos = 0;
memset(ctx->input_buffer,0,SHELL_BUFFER_SIZE);
ctx->input_buffer[0] = '\0';
// 显示新提示符
if (ctx->echo_enabled) {
shell_printf(ctx, "%s", ctx->prompt);
}
} else {
// 空行,只显示提示符
if (ctx->echo_enabled) {
shell_printf(ctx, "\r\n%s", ctx->prompt);
}
}
return SHELL_OK;
}
// 处理退格
if (ch == '\b' || ch == 0x7F)
{
if (ctx->buf_pos > 0) {
// 删除当前位置的字符
memmove(&ctx->input_buffer[ctx->buf_pos - 1],
&ctx->input_buffer[ctx->buf_pos],
strlen(ctx->input_buffer) - ctx->buf_pos + 1);
ctx->buf_pos--;
shell_redisplay_line(ctx);
}
return SHELL_OK;
}
// 处理Tab键(命令补全)
if (ch == '\t') {
if(0 ==strlen(ctx->input_buffer))
{
shell_list_commands(ctx);
shell_printf(ctx, "%s", ctx->prompt);
}else {
// 这里可以实现命令补全功能
}
return SHELL_OK;
}
// 普通字符 - 插入到当前光标位置
if (ctx->buf_pos < sizeof(ctx->input_buffer) - 1) {
// 如果有字符在当前光标位置之后,需要移动它们
if (ctx->buf_pos < strlen(ctx->input_buffer)) {
// 将光标后的字符向后移动
memmove(&ctx->input_buffer[ctx->buf_pos + 1],
&ctx->input_buffer[ctx->buf_pos],
strlen(ctx->input_buffer) - ctx->buf_pos + 1);
}
// 插入新字符
ctx->input_buffer[ctx->buf_pos] = ch;
ctx->buf_pos++;
// 回显字符
if (ctx->echo_enabled && ctx->io.send)
{
if (ctx->buf_pos == strlen(ctx->input_buffer)) {
#if SHELL_USERS_ENABLE == 1
if(ctx->login_state != LOGIN_STATE_WAITING_PASSWORD){
#endif
// 在行尾,直接回显
char echo[2] = {ch, '\0'};
ctx->io.send(echo, 1);
#if SHELL_USERS_ENABLE == 1
}else{
// 在行尾,直接回显
char echo[2] = {'*', '\0'};
ctx->io.send(echo, 1);
}
#endif
} else {
// 在行中插入,需要重新显示整行
shell_redisplay_line(ctx);
}
}
}
}
return SHELL_OK;
}
// 处理字符串
shell_status_t shell_process_string(shell_context_t *ctx, const char *str)
{
char buffer[SHELL_BUFFER_SIZE];
char *argv[SHELL_MAX_ARGS];
int argc;
if (!ctx || !str) {
return SHELL_ERR;
}
// ==================== 新增:处理登录序列 ====================
#if SHELL_USERS_ENABLE == 1
if (ctx->require_login && !ctx->current_user) {
// 未登录状态,处理登录序列
return shell_handle_login_sequence(ctx, str);
}
#endif
// ==================== 新增结束 ====================
// 复制字符串以便修改
strncpy(buffer, str, sizeof(buffer) - 1);
buffer[sizeof(buffer) - 1] = '\0';
// 分割参数
argc = shell_split_args(buffer, argv, sizeof(argv)/sizeof(argv[0]));
if (argc == 0) {
return SHELL_OK;
}
// ==================== 新增:登出命令 ====================
#if SHELL_USERS_ENABLE == 1
if (strcmp(argv[0], "logout") == 0) {
shell_logout(ctx);
return SHELL_OK;
}
#endif
// ==================== 新增结束 ====================
// 处理内置命令
if (((strcmp(argv[0], "help") == 0)||(strcmp(argv[0], "?") == 0)) && (argc == 1)) {
shell_print_help(ctx);
return SHELL_OK;
}
if ((strcmp(argv[0], "list") == 0) && (argc == 1)) {
shell_list_commands(ctx);
return SHELL_OK;
}
if ((strcmp(argv[0], "clear") == 0) && (argc == 1)) {
// 发送清屏序列
shell_print(ctx, "\033[2J\033[H");
return SHELL_OK;
}
if ((strcmp(argv[0], "echo") == 0) && (argc > 2)) {
if (argc == 2) {
if (strcmp(argv[1], "on") == 0) {
ctx->echo_enabled = 1;
shell_print(ctx, "Echo enabled\r\n");
} else if (strcmp(argv[1], "off") == 0) {
ctx->echo_enabled = 0;
shell_print(ctx, "Echo disabled\r\n");
} else {
shell_print(ctx, "Usage: echo <on|off>\r\n");
}
} else {
shell_printf(ctx, "Echo is %s\r\n",
ctx->echo_enabled ? "enabled" : "disabled");
}
return SHELL_OK;
}
#if SHELL_HISTORY_SIZE > 0
// 添加历史记录相关命令
if ((strcmp(argv[0], "history") == 0) && (argc == 1)) {
// // ==================== 新增:权限检查 ====================
//#if SHELL_USERS_ENABLE == 1
// if (ctx->current_user && ctx->current_user->permission_level < SHELL_PERMISSION_USER) {
// shell_print(ctx, "Permission denied\r\n");
// shell_printf(ctx, "%s", ctx->prompt);
// return SHELL_ERR_PERMISSION_DENIED;
// }
//#endif
// // ==================== 新增结束 ====================
if (argc == 1) {
shell_history_list(ctx);
} else if (argc == 2) {
if (strcmp(argv[1], "clear") == 0) {
shell_history_clear(ctx);
} else {
shell_print(ctx, "Usage: history [clear]\r\n");
}
}
return SHELL_OK;
}
#endif
// ==================== 新增:用户管理命令 ====================
#if SHELL_USERS_ENABLE == 1
if (strcmp(argv[0], "users") == 0) {
shell_list_users(ctx);
return SHELL_OK;
}
if (strcmp(argv[0], "passwd") == 0) {
// 修改密码
// 修改当前用户密码
shell_print(ctx, "Changing password for current user\r\n");
if (argc == 1) {
// 简化处理,实际应该交互式输入
shell_print(ctx, "Usage: passwd <old_password> <new_password>\r\n");
} else if (argc == 3) {
shell_user_change_password(ctx,
ctx->current_user->username,
argv[1], argv[2]);
} else {
shell_print(ctx, "Usage: passwd <old_password> <new_password>\r\n");
}
return SHELL_OK;
}
if (strcmp(argv[0], "useradd") == 0) {
// 添加用户(需要管理员权限)
if (argc >= 3) {
shell_permission_level_t level = SHELL_PERMISSION_USER;
if (argc >= 4) {
level = (shell_permission_level_t)atoi(argv[3]);
//if(level > SHELL_PERMISSION_USER) level = SHELL_PERMISSION_USER;
}
//检查当前添加的用户权限不能超过当前用户权限
if(level > ctx->current_user->permission_level) level = ctx->current_user->permission_level;
shell_user_add(ctx, argv[1], argv[2], level);
} else {
shell_print(ctx, "Usage: useradd <username> <password> [permission_level:0/1/2/3]\r\n");
}
return SHELL_OK;
}
if (strcmp(argv[0], "userdel") == 0) {
// 删除用户(需要管理员权限)
if (argc == 2) {
shell_user_remove(ctx, argv[1]);
} else {
shell_print(ctx, "Usage: userdel <username>\r\n");
}
return SHELL_OK;
}
if (strcmp(argv[0], "userlock") == 0) {
// 锁定用户(需要管理员权限)
if (argc == 2) {
shell_user_lock(ctx, argv[1]);
} else {
shell_print(ctx, "Usage: userlock <username>\r\n");
}
return SHELL_OK;
}
if (strcmp(argv[0], "userunlock") == 0) {
// 解锁用户(需要管理员权限)
if (argc == 2) {
shell_user_unlock(ctx, argv[1]);
} else {
shell_print(ctx, "Usage: userunlock <username>\r\n");
}
return SHELL_OK;
}
#endif
// ==================== 新增结束 ====================
// 查找命令
shell_command_t *cmd = shell_find_command(ctx, argv[0]);
if (!cmd) {
shell_printf(ctx, "Error: Command '%s' not found\r\n", argv[0]);
return SHELL_ERR_CMD_NOT_FOUND;
}
// ==================== 新增:权限检查 ====================
#if SHELL_USERS_ENABLE == 1
if (!shell_check_command_permission(ctx, cmd)) {
shell_printf(ctx, "Error: Permission denied for command '%s'\r\n", argv[0]);
shell_printf(ctx, "%s", ctx->prompt);
return SHELL_ERR_PERMISSION_DENIED;
}
#endif
// ==================== 新增结束 ====================
ctx->cur_cmd = cmd;
// 执行命令
if (cmd->handler) {
shell_execute_command(ctx, ctx->cur_cmd, argc, argv);
}
ctx->cur_cmd = NULL;
return SHELL_OK;
}
// 执行命令
static void shell_execute_command(shell_context_t *ctx,
shell_command_t *cmd,
int argc, char **argv)
{
shell_status_t status = cmd->handler(argc, argv);
if (status != SHELL_OK) {
shell_printf(ctx, "Command returned error: %d\r\n", status);
}
}
// 打印帮助信息
void shell_print_help(shell_context_t *ctx)
{
if (!ctx) {
return;
}
shell_print(ctx, "\r\n===LSY Shell Help ===\r\n");
shell_print(ctx, "Built-in commands:\r\n");
shell_print(ctx, " help - Show this help message\r\n");
shell_print(ctx, " list - List all registered commands\r\n");
shell_print(ctx, " echo <on|off> - Enable/disable input echo\r\n");
shell_print(ctx, " clear - Clear display\r\n");
#if SHELL_HISTORY_SIZE > 0
shell_print(ctx, " history [clear] - Show or clear command history\r\n");
#endif
// ==================== 新增:用户管理命令帮助 ====================
#if SHELL_USERS_ENABLE == 1
shell_print(ctx, "\r\nUser management commands:\r\n");
shell_print(ctx, " login - Login to shell\r\n");
shell_print(ctx, " logout - Logout from shell\r\n");
shell_print(ctx, " passwd - Change password\r\n");
shell_print(ctx, " users - List all users (admin only)\r\n");
shell_print(ctx, " useradd - Add new user (admin only)\r\n");
shell_print(ctx, " userdel - Delete user (admin only)\r\n");
shell_print(ctx, " userlock - Lock user account (admin only)\r\n");
shell_print(ctx, " userunlock - Unlock user account (admin only)\r\n");
#endif
// ==================== 新增结束 ====================
shell_print(ctx, "\r\nNavigation:\r\n");
shell_print(ctx, " Up/Down - Browse command history\r\n");
shell_print(ctx, " Left/Right - Move cursor\r\n");
shell_print(ctx, " Home/End - Jump to start/end\r\n");
shell_print(ctx, " Backspace/Del - Delete character\r\n");
shell_print(ctx, "\r\n");
}
// 列出所有命令
void shell_list_commands(shell_context_t *ctx)
{
if (!ctx) {
return;
}
shell_print(ctx, "\r\n=== Registered Commands ===\r\n");
int count = 0;
// ==================== 修改:内置命令权限显示 ====================
#if SHELL_USERS_ENABLE == 1
shell_printf(ctx, "%-15s - %-30s [B] Perm: all\r\n", "help", "Show help information");
shell_printf(ctx, "%-15s - %-30s [B] Perm: all\r\n", "list", "List all commands");
shell_printf(ctx, "%-15s - %-30s [B] Perm: all\r\n", "echo", "Control input echo");
shell_printf(ctx, "%-15s - %-30s [B] Perm: all\r\n", "clear", "Clear display");
count += 4;
#if SHELL_HISTORY_SIZE > 0
shell_printf(ctx, "%-15s - %-30s [B] Perm: all\r\n", "history", "Show/clear command history");
count += 1;
#endif
shell_printf(ctx, "%-15s - %-30s [B] Perm: all\r\n", "login", "Login to shell");
shell_printf(ctx, "%-15s - %-30s [B] Perm: all\r\n", "logout", "Logout from shell");
shell_printf(ctx, "%-15s - %-30s [B] Perm: all\r\n", "passwd", "Change password");
shell_printf(ctx, "%-15s - %-30s [B] Perm: admin\r\n", "users", "List all users");
shell_printf(ctx, "%-15s - %-30s [B] Perm: admin\r\n", "useradd", "Add new user");
shell_printf(ctx, "%-15s - %-30s [B] Perm: admin\r\n", "userdel", "Delete user");
shell_printf(ctx, "%-15s - %-30s [B] Perm: admin\r\n", "userlock", "Lock user");
shell_printf(ctx, "%-15s - %-30s [B] Perm: admin\r\n", "userunlock", "Unlock user");
count += 8;
// ==================== 修改结束 ====================
#else
// 内置命令
shell_printf(ctx, "%-15s - Show help information [B]\r\n", "help");
shell_printf(ctx, "%-15s - List all commands [B]\r\n", "list");
shell_printf(ctx, "%-15s - Control input echo [B]\r\n", "echo");
shell_printf(ctx, "%-15s - Clear display [B]\r\n", "clear");
count += 4;
#if SHELL_HISTORY_SIZE > 0
shell_printf(ctx, "%-15s - Show/clear command history [B]\r\n", "history");
count += 1;
#endif
#endif
// 列出静态命令
if (ctx->static_cmds) {
for (size_t i = 0; i < ctx->static_cmd_count; i++) {
// ==================== 新增:显示静态命令权限 ====================
#if SHELL_USERS_ENABLE == 1
char perm_str[32] = "";
if (0 == ctx->static_cmds[i].permission_mask) {
strcat(perm_str, "all");
}else
{
if (ctx->static_cmds[i].permission_mask & SHELL_PERM_MASK_ROOT) {
strcat(perm_str, "root+");
}
if (ctx->static_cmds[i].permission_mask & SHELL_PERM_MASK_ADMIN) {
strcat(perm_str, "admin+");
}
if (ctx->static_cmds[i].permission_mask & SHELL_PERM_MASK_USER) {
strcat(perm_str, "user+");
}
if (ctx->static_cmds[i].permission_mask & SHELL_PERM_MASK_GUEST) {
strcat(perm_str, "guest+");
}
}
shell_printf(ctx, "%-15s - %-30s [S] Perm: %s\r\n",
ctx->static_cmds[i].name,
ctx->static_cmds[i].description,
perm_str);
// ==================== 新增结束 ====================
#else
shell_printf(ctx, "%-15s - %s [S]\r\n",
ctx->static_cmds[i].name,
ctx->static_cmds[i].description);
#endif
count++;
}
}
// 列出动态命令
shell_command_t *cmd = ctx->cmd_list;
while (cmd) {
#if SHELL_USERS_ENABLE == 1
// ==================== 新增:显示动态命令权限 ====================
char perm_str[32] = "";
if (0 == cmd->permission_mask) {
strcat(perm_str, "all");
}else
{
if (cmd->permission_mask & SHELL_PERM_MASK_ROOT) {
strcat(perm_str, "root+");
}
if (cmd->permission_mask & SHELL_PERM_MASK_ADMIN) {
strcat(perm_str, "admin+");
}
if (cmd->permission_mask & SHELL_PERM_MASK_USER) {
strcat(perm_str, "user+");
}
if (cmd->permission_mask & SHELL_PERM_MASK_GUEST) {
strcat(perm_str, "guest+");
}
}
shell_printf(ctx, "%-15s - %-30s [D] Perm: %s\r\n",
cmd->name, cmd->description, perm_str);
// ==================== 新增结束 ====================
#else
shell_printf(ctx, "%-15s - %s [D]\r\n",
cmd->name, cmd->description);
#endif
cmd = cmd->next;
count++;
}
shell_printf(ctx, "\r\nTotal: %d commands (D=Dynamic, S=Static, B=Built-in)\r\n", count);
}
// 输出字符串
void shell_print(shell_context_t *ctx, const char *str) {
if (ctx && ctx->io.send && str) {
ctx->io.send(str, strlen(str));
}
}
// 输出格式化字符串
void shell_printf(shell_context_t *ctx, const char *format, ...) {
char buffer[256];
va_list args;
if (!ctx || !ctx->io.send) {
va_start(args, format);
printf(format, args);
va_end(args);
return;
}
va_start(args, format);
int len = vsnprintf(buffer, sizeof(buffer), format, args);
va_end(args);
if (len > 0 && len < (int)sizeof(buffer)) {
ctx->io.send(buffer, len);
}
}
// 设置回显
void shell_set_echo(shell_context_t *ctx, uint8_t enable) {
if (ctx) {
ctx->echo_enabled = enable;
}
}
// 获取命令数量
size_t shell_get_command_count(shell_context_t *ctx) {
if (!ctx) {
return 0;
}
size_t count = 0;
// 动态命令
shell_command_t *cmd = ctx->cmd_list;
while (cmd) {
count++;
cmd = cmd->next;
}
// 静态命令
if (ctx->static_cmds) {
count += ctx->static_cmd_count;
}
// 内置命令
count += 4; // help, list, echo, clear
#if SHELL_HISTORY_SIZE > 0
count += 1; // history
#endif
#if SHELL_USERS_ENABLE == 1
count += 8;
#endif
return count;
}
// 清理所有动态命令
shell_status_t shell_cleanup_dynamic_commands(shell_context_t *ctx) {
if (!ctx) {
return SHELL_ERR;
}
shell_command_t *curr = ctx->cmd_list;
shell_command_t *next;
while (curr) {
next = curr->next;
if (!curr->is_static) {
shell_free_command(curr);
}
curr = next;
}
ctx->cmd_list = NULL;
return SHELL_OK;
}
////////////////////////////////////////////////////////////////////////////
#if SHELL_HISTORY_SIZE > 0
// 添加命令到历史记录
static void shell_history_add(shell_context_t *ctx, const char *cmd)
{
if (!ctx || !cmd || strlen(cmd) == 0) {
return;
}
// 检查是否与上一条历史记录重复
if (ctx->history_count > 0) {
int last_idx = (ctx->history_index + ctx->history_count - 1) % SHELL_HISTORY_SIZE;
if (ctx->history[last_idx] && strcmp(ctx->history[last_idx], cmd) == 0) {
return; // 重复命令,不添加
}
}
size_t cmd_len = strlen(cmd) + 1;
// 检查缓冲区空间
if (ctx->history_count >= SHELL_HISTORY_SIZE) {
// 历史已满,释放最旧的一条
char *oldest = ctx->history[ctx->history_index];
if (oldest) {
size_t oldest_len = strlen(oldest) + 1;
// 移动缓冲区数据(简化处理,实际可以使用环形缓冲区)
memmove(oldest, oldest + oldest_len,
ctx->history_buffer + sizeof(ctx->history_buffer) - (oldest + oldest_len));
// 调整所有指针
for (size_t i = 1; i < SHELL_HISTORY_SIZE; i++) {
if (ctx->history[i] && ctx->history[i] > oldest) {
ctx->history[i-1] = ctx->history[i] - oldest_len;
}
}
}
ctx->history_count--;
}
// 找到可用的存储位置
char *store_pos;
if (ctx->history_count == 0) {
store_pos = ctx->history_buffer;
ctx->history_index = 0;
} else {
// 计算下一个存储位置
int last_idx = (ctx->history_index + ctx->history_count - 1) % SHELL_HISTORY_SIZE;
char *last_cmd = ctx->history[last_idx];
store_pos = last_cmd + strlen(last_cmd) + 1;
// 检查是否超出缓冲区
if ((store_pos + cmd_len) > (ctx->history_buffer + sizeof(ctx->history_buffer))) {
// 缓冲区满,重新整理
shell_history_clear(ctx);
store_pos = ctx->history_buffer;
ctx->history_index = 0;
// shell_history_compact(ctx);
// // 重新计算存储位置
// if (ctx->history_count > 0) {
// last_idx = (ctx->history_index + ctx->history_count - 1) % SHELL_HISTORY_SIZE;
// last_cmd = ctx->history[last_idx];
// store_pos = last_cmd + strlen(last_cmd) + 1;
// } else {
// store_pos = ctx->history_buffer;
// }
}
}
// 存储命令
if ((store_pos + cmd_len) < (ctx->history_buffer + sizeof(ctx->history_buffer)))
{
strcpy(store_pos, cmd);
// 更新索引数组
int new_idx = (ctx->history_index + ctx->history_count) % SHELL_HISTORY_SIZE;
ctx->history[new_idx] = store_pos;
ctx->history_count++;
// 重置浏览位置
ctx->history_pos = ctx->history_count;
}
}
/*
// 整理历史记录缓冲区
static void shell_history_compact(shell_context_t *ctx)
{
if (ctx->history_count == 0) return;
char temp_buffer[SHELL_HISTORY_BUFFER_SIZE];
char *temp_ptr = temp_buffer;
char *new_history[SHELL_HISTORY_SIZE];
size_t new_count = 0;
// 按顺序整理历史记录
for (size_t i = 0; i < ctx->history_count; i++) {
int idx = (ctx->history_index + i) % SHELL_HISTORY_SIZE;
if (ctx->history[idx]) {
size_t len = strlen(ctx->history[idx]) + 1;
if (temp_ptr + len <= temp_buffer + sizeof(temp_buffer)) {
strcpy(temp_ptr, ctx->history[idx]);
new_history[new_count++] = temp_ptr;
temp_ptr += len;
}
}
}
// 复制回原缓冲区
memcpy(ctx->history_buffer, temp_buffer, temp_ptr - temp_buffer);
memcpy(ctx->history, new_history, new_count * sizeof(char*));
ctx->history_count = new_count;
ctx->history_index = 0;
}
*/
// 获取上一条历史命令
static void shell_history_prev(shell_context_t *ctx)
{
if (!ctx || ctx->history_count == 0) {
return;
}
if (ctx->history_pos > 0) {
ctx->history_pos--;
}
size_t idx = (ctx->history_index + ctx->history_pos) % SHELL_HISTORY_SIZE;
const char *history_cmd = ctx->history[idx];
if (history_cmd) {
// 保存当前输入(用于下箭头恢复)
if (ctx->history_pos == ctx->history_count - 1) {
// 可以保存当前编辑的命令
}
// 替换输入缓冲区
strncpy(ctx->input_buffer, history_cmd, sizeof(ctx->input_buffer) - 1);
ctx->input_buffer[sizeof(ctx->input_buffer) - 1] = '\0';
ctx->buf_pos = strlen(ctx->input_buffer);
// 重新显示
shell_redisplay_line(ctx);
}
}
// 获取下一条历史命令
static void shell_history_next(shell_context_t *ctx)
{
if (!ctx) {
return;
}
if (ctx->history_pos < ctx->history_count - 1) {
ctx->history_pos++;
size_t idx = (ctx->history_index + ctx->history_pos) % SHELL_HISTORY_SIZE;
const char *history_cmd = ctx->history[idx];
if (history_cmd) {
strncpy(ctx->input_buffer, history_cmd, sizeof(ctx->input_buffer) - 1);
ctx->input_buffer[sizeof(ctx->input_buffer) - 1] = '\0';
ctx->buf_pos = strlen(ctx->input_buffer);
}
} else {
// 已经到最后一条历史记录,清空输入
ctx->history_pos = ctx->history_count;
memset(ctx->input_buffer,0,SHELL_BUFFER_SIZE);
ctx->buf_pos = 0;
}
// 重新显示
shell_redisplay_line(ctx);
}
// 清除历史记录
static void shell_history_clear(shell_context_t *ctx)
{
if (!ctx) {
return;
}
memset(ctx->history_buffer, 0, sizeof(ctx->history_buffer));
memset(ctx->history, 0, sizeof(ctx->history));
ctx->history_count = 0;
ctx->history_pos = 0;
ctx->history_index = -1;
shell_print(ctx, "History cleared\r\n");
}
// 显示历史记录
static void shell_history_list(shell_context_t *ctx)
{
if (!ctx) {
return;
}
if (ctx->history_count == 0) {
shell_print(ctx, "No history commands\r\n");
return;
}
shell_print(ctx, "\r\n=== Command History ===\r\n");
shell_printf(ctx, "History Item:%d\r\n",ctx->history_count);
for (size_t i = 0; i < ctx->history_count; i++) {
int idx = (ctx->history_index + i) % SHELL_HISTORY_SIZE;
if (ctx->history[idx]) {
shell_printf(ctx, "%3zu: %s\r\n", i + 1, ctx->history[idx]);
}
}
}
// 添加保存历史记录到文件的功能(可选)
shell_status_t shell_history_save_to_file(shell_context_t *ctx, const char *filename)
{
if (!ctx || !filename) {
return SHELL_ERR;
}
FILE *fp = fopen(filename, "w");
if (!fp) {
return SHELL_ERR;
}
for (size_t i = 0; i < ctx->history_count; i++) {
int idx = (ctx->history_index + i) % SHELL_HISTORY_SIZE;
if (ctx->history[idx]) {
fprintf(fp, "%s\n", ctx->history[idx]);
}
}
fclose(fp);
return SHELL_OK;
}
// 从文件加载历史记录(可选)
shell_status_t shell_history_load_from_file(shell_context_t *ctx, const char *filename)
{
if (!ctx || !filename) {
return SHELL_ERR;
}
FILE *fp = fopen(filename, "r");
if (!fp) {
return SHELL_ERR;
}
char buffer[SHELL_BUFFER_SIZE];
while (fgets(buffer, sizeof(buffer), fp)) {
// 去除换行符
size_t len = strlen(buffer);
if (len > 0 && buffer[len - 1] == '\n') {
buffer[len - 1] = '\0';
}
shell_history_add(ctx, buffer);
}
fclose(fp);
return SHELL_OK;
}
#endif
// ==================== 新增:用户管理内部函数实现 ====================
#if SHELL_USERS_ENABLE == 1
/**
* @brief 分配新的用户节点
* @return 分配的用户节点指针,失败返回NULL
*/
static shell_user_t* shell_user_alloc(void)
{
shell_user_t *user = (shell_user_t*)malloc(sizeof(shell_user_t));
if (user) {
memset(user, 0, sizeof(shell_user_t));
}
return user;
}
/**
* @brief 释放用户节点
* @param user 要释放的用户节点
*/
static void shell_user_free(shell_user_t *user)
{
if (user) {
free(user);
}
}
/**
* @brief 将权限级别转换为权限掩码
* @param level 权限级别
* @return 权限掩码
*/
static uint32_t shell_permission_level_to_mask(shell_permission_level_t level)
{
switch (level) {
case SHELL_PERMISSION_USER:
return SHELL_PERM_MASK_USER;
case SHELL_PERMISSION_ADMIN:
return SHELL_PERM_MASK_ADMIN;
case SHELL_PERMISSION_ROOT:
return SHELL_PERM_MASK_ROOT;
case SHELL_PERMISSION_GUEST:
return SHELL_PERM_MASK_GUEST;
default:
return 0;
}
}
/**
* @brief 更新提示符,显示当前用户名
*/
static void shell_prompt_update(shell_context_t *ctx)
{
if (!ctx) return;
if (ctx->current_user) {
// 已登录,显示 [用户名@主机]$
snprintf(ctx->prompt, sizeof(ctx->prompt), "[%s@shell]$ ",
ctx->current_user->username);
} else if (ctx->login_state == LOGIN_STATE_WAITING_PASSWORD) {
// 等待输入密码,显示 Password:
strcpy(ctx->prompt, "Password: ");
} else {
// 未登录,显示 login:
strcpy(ctx->prompt, "login: ");
}
}
/**
* @brief 简单的密码验证(实际应用中应使用哈希比较)
* @param input_pass 输入的密码
* @param stored_pass 存储的密码
* @return 1表示验证通过,0表示失败
*/
static uint8_t shell_verify_password(const char *input_pass, const char *stored_pass)
{
// TODO: 实际应用中应使用安全的密码哈希比较,如bcrypt、scrypt等
// 这里仅作演示,直接比较字符串
return (strcmp(input_pass, stored_pass) == 0) ? 1 : 0;
}
/**
* @brief 处理登录序列(用户名/密码输入)
* @param ctx Shell上下文
* @param input 用户输入
* @return SHELL_OK表示登录处理完成,SHELL_ERR表示需要继续
*/
static shell_status_t shell_handle_login_sequence(shell_context_t *ctx, const char *input)
{
if (!ctx || !input) {
return SHELL_ERR;
}
switch (ctx->login_state) {
case LOGIN_STATE_LOGGED_OUT:
// 未登录状态,等待输入用户名
if (strlen(input) > 0) {
// 保存用户名
strncpy(ctx->login_username, input, sizeof(ctx->login_username) - 1);
ctx->login_username[sizeof(ctx->login_username) - 1] = '\0';
// 查找用户
shell_user_t *user = shell_user_find(ctx, ctx->login_username);
if (!user) {
shell_print(ctx, "\r\nUser not found\r\n");
ctx->login_attempts++;
if (ctx->login_attempts >= SHELL_MAX_LOGIN_ATTEMPTS) {
shell_print(ctx, "Too many login attempts\r\n");
// 可以在这里添加延时防止暴力破解
}
return SHELL_OK;
}
if (user->is_locked) {
shell_print(ctx, "\r\nUser account is locked\r\n");
return SHELL_OK;
}
// 切换到等待密码状态
ctx->login_state = LOGIN_STATE_WAITING_PASSWORD;
ctx->login_start_time = 0; // 实际应用中应获取系统时间
shell_prompt_update(ctx);
}
break;
case LOGIN_STATE_WAITING_PASSWORD:
// 等待密码状态,验证密码
{
shell_user_t *user = shell_user_find(ctx, ctx->login_username);
if (!user) {
// 用户不存在,返回登录状态
ctx->login_state = LOGIN_STATE_LOGGED_OUT;
ctx->login_username[0] = '\0';
shell_prompt_update(ctx);
return SHELL_OK;
}
// 验证密码
if (shell_verify_password(input, user->password)) {
// 登录成功
ctx->current_user = user;
user->is_logged_in = 1;
user->login_attempts = 0;
ctx->login_state = LOGIN_STATE_LOGGED_IN;
// 更新提示符
shell_prompt_update(ctx);
// 显示欢迎信息
shell_printf(ctx, "\r\nWelcome, %s!\r\n", user->username);
shell_printf(ctx, "Permission level: %d\r\n", user->permission_level);
} else {
// 密码错误
user->login_attempts++;
ctx->login_attempts++;
shell_print(ctx, "\r\nInvalid password\r\n");
if (user->login_attempts >= SHELL_MAX_LOGIN_ATTEMPTS) {
// 锁定用户
user->is_locked = 1;
shell_print(ctx, "User account locked due to too many failed attempts\r\n");
ctx->login_state = LOGIN_STATE_LOGGED_OUT;
ctx->login_username[0] = '\0';
} else {
// 继续等待密码
return SHELL_OK; // 保持等待密码状态,不显示提示符
}
// 返回登录状态
ctx->login_state = LOGIN_STATE_LOGGED_OUT;
shell_prompt_update(ctx);
}
}
break;
default:
break;
}
return SHELL_OK;
}
/**
* @brief 初始化默认用户(管理员和访客)
* @param ctx Shell上下文
* @return SHELL_OK表示成功
*/
shell_status_t shell_init_default_users(shell_context_t *ctx)
{
if (!ctx) {
return SHELL_ERR;
}
// 添加管理员用户(密码:admin)
shell_user_add(ctx, "admin", "pwd_admin", SHELL_PERMISSION_ADMIN);
// 添加普通用户(密码:user)
shell_user_add(ctx, "user", "pwd_user", SHELL_PERMISSION_USER);
// 添加普通用户(密码:lsy)
shell_user_add(ctx, "lsy", "pwd_lsy", SHELL_PERMISSION_USER);
// 添加访客用户(密码:guest,无密码)
shell_user_add(ctx, "guest", "", SHELL_PERMISSION_GUEST);
// 添加超级用户(密码:root)
shell_user_add(ctx, "root", "pwd_root", SHELL_PERMISSION_ROOT);
return SHELL_OK;
}
/**
* @brief 列出所有用户(需要管理员权限)
* @param ctx Shell上下文
*/
void shell_list_users(shell_context_t *ctx)
{
if (!ctx) {
return;
}
// 检查权限(只有管理员和root可以查看用户列表)
if (!ctx->current_user ||
(ctx->current_user->permission_level < SHELL_PERMISSION_ADMIN)) {
shell_print(ctx, "Permission denied\r\n");
return;
}
shell_print(ctx, "\r\n=== User List ===\r\n");
shell_printf(ctx, "%-20s %-10s %-8s %-8s\r\n",
"Username", "Level", "Locked", "Logged In");
shell_print(ctx, "------------------------------------------------\r\n");
shell_user_t *user = ctx->user_list;
while (user) {
const char *level_str;
switch (user->permission_level) {
case SHELL_PERMISSION_ROOT: level_str = "root"; break;
case SHELL_PERMISSION_ADMIN: level_str = "admin"; break;
case SHELL_PERMISSION_USER: level_str = "user"; break;
case SHELL_PERMISSION_GUEST: level_str = "guest"; break;
default: level_str = "none"; break;
}
shell_printf(ctx, "%-20s %-10s %-8s %-8s\r\n",
user->username,
level_str,
user->is_locked ? "Yes" : "No",
user->is_logged_in ? "Yes" : "No");
user = user->next;
}
shell_print(ctx, "\r\n");
}
// ==================== 新增:用户管理API实现 ====================
/**
* @brief 添加新用户
* @param ctx Shell上下文
* @param username 用户名
* @param password 密码
* @param level 权限级别
* @return SHELL_OK表示成功
*/
shell_status_t shell_user_add(shell_context_t *ctx,
const char *username,
const char *password,
shell_permission_level_t level)
{
if (!ctx || !username) {
return SHELL_ERR;
}
// 检查用户是否已存在
if (shell_user_find(ctx, username)) {
return SHELL_ERR_CMD_EXISTS; // 用户已存在
}
// 分配新用户节点
shell_user_t *new_user = shell_user_alloc();
if (!new_user) {
return SHELL_ERR_MEMORY;
}
// 填充用户信息
strncpy(new_user->username, username, sizeof(new_user->username) - 1);
new_user->username[sizeof(new_user->username) - 1] = '\0';
if (password) {
strncpy(new_user->password, password, sizeof(new_user->password) - 1);
new_user->password[sizeof(new_user->password) - 1] = '\0';
}
new_user->permission_level = level;
new_user->permission_mask = shell_permission_level_to_mask(level);
new_user->is_locked = 0;
new_user->login_attempts = 0;
new_user->last_login_time = 0;
new_user->is_logged_in = 0;
// 添加到用户链表头部
new_user->next = ctx->user_list;
ctx->user_list = new_user;
return SHELL_OK;
}
/**
* @brief 删除用户
* @param ctx Shell上下文
* @param username 要删除的用户名
* @return SHELL_OK表示成功
*/
shell_status_t shell_user_remove(shell_context_t *ctx, const char *username)
{
if (!ctx || !username) {
return SHELL_ERR;
}
// 不能删除当前登录用户
if (ctx->current_user && strcmp(ctx->current_user->username, username) == 0) {
return SHELL_ERR_PERMISSION_DENIED;
}
shell_user_t *prev = NULL;
shell_user_t *curr = ctx->user_list;
while (curr) {
if (strcmp(curr->username, username) == 0) {
//检查用户权限,不能删除大等于当前用户等级权限的用户
if(ctx->current_user && ctx->current_user->permission_level <= curr->permission_level){
return SHELL_ERR_PERMISSION_DENIED;
}
if (prev) {
prev->next = curr->next;
} else {
ctx->user_list = curr->next;
}
shell_user_free(curr);
return SHELL_OK;
}
prev = curr;
curr = curr->next;
}
return SHELL_ERR_USER_NOT_FOUND;
}
/**
* @brief 修改用户密码
* @param ctx Shell上下文
* @param username 用户名
* @param old_password 旧密码
* @param new_password 新密码
* @return SHELL_OK表示成功
*/
shell_status_t shell_user_change_password(shell_context_t *ctx,
const char *username,
const char *old_password,
const char *new_password)
{
if (!ctx || !username || !new_password) {
return SHELL_ERR;
}
shell_user_t *user = shell_user_find(ctx, username);
if (!user) {
return SHELL_ERR_USER_NOT_FOUND;
}
// 验证旧密码(如果是修改自己的密码)
if (ctx->current_user && strcmp(ctx->current_user->username, username) == 0) {
if (!shell_verify_password(old_password, user->password)) {
return SHELL_ERR_INVALID_PASSWORD;
}
} else {
// 修改其他用户密码需要管理员权限
if (!ctx->current_user ||
ctx->current_user->permission_level < SHELL_PERMISSION_ADMIN) {
return SHELL_ERR_PERMISSION_DENIED;
}
}
// 设置新密码
strncpy(user->password, new_password, sizeof(user->password) - 1);
user->password[sizeof(user->password) - 1] = '\0';
return SHELL_OK;
}
/**
* @brief 设置用户权限级别
* @param ctx Shell上下文
* @param username 用户名
* @param level 新的权限级别
* @return SHELL_OK表示成功
*/
shell_status_t shell_user_set_permission(shell_context_t *ctx,
const char *username,
shell_permission_level_t level)
{
if (!ctx || !username) {
return SHELL_ERR;
}
// 需要管理员权限
if (!ctx->current_user ||
ctx->current_user->permission_level < SHELL_PERMISSION_ADMIN) {
return SHELL_ERR_PERMISSION_DENIED;
}
shell_user_t *user = shell_user_find(ctx, username);
if (!user) {
return SHELL_ERR_USER_NOT_FOUND;
}
user->permission_level = level;
user->permission_mask = shell_permission_level_to_mask(level);
return SHELL_OK;
}
/**
* @brief 锁定用户账户
* @param ctx Shell上下文
* @param username 用户名
* @return SHELL_OK表示成功
*/
shell_status_t shell_user_lock(shell_context_t *ctx, const char *username)
{
if (!ctx || !username) {
return SHELL_ERR;
}
// 需要管理员权限
if (!ctx->current_user ||
ctx->current_user->permission_level < SHELL_PERMISSION_ADMIN) {
return SHELL_ERR_PERMISSION_DENIED;
}
shell_user_t *user = shell_user_find(ctx, username);
if (!user) {
return SHELL_ERR_USER_NOT_FOUND;
}
user->is_locked = 1;
return SHELL_OK;
}
/**
* @brief 解锁用户账户
* @param ctx Shell上下文
* @param username 用户名
* @return SHELL_OK表示成功
*/
shell_status_t shell_user_unlock(shell_context_t *ctx, const char *username)
{
if (!ctx || !username) {
return SHELL_ERR;
}
// 需要管理员权限
if (!ctx->current_user ||
ctx->current_user->permission_level < SHELL_PERMISSION_ADMIN) {
return SHELL_ERR_PERMISSION_DENIED;
}
shell_user_t *user = shell_user_find(ctx, username);
if (!user) {
return SHELL_ERR_USER_NOT_FOUND;
}
user->is_locked = 0;
user->login_attempts = 0;
return SHELL_OK;
}
/**
* @brief 查找用户
* @param ctx Shell上下文
* @param username 用户名
* @return 用户指针,未找到返回NULL
*/
shell_user_t* shell_user_find(shell_context_t *ctx, const char *username)
{
if (!ctx || !username) {
return NULL;
}
shell_user_t *user = ctx->user_list;
while (user) {
if (strcmp(user->username, username) == 0) {
return user;
}
user = user->next;
}
return NULL;
}
/**
* @brief 用户登录
* @param ctx Shell上下文
* @param username 用户名
* @param password 密码
* @return SHELL_OK表示成功
*/
shell_status_t shell_login(shell_context_t *ctx,
const char *username,
const char *password)
{
if (!ctx || !username || !password) {
return SHELL_ERR;
}
shell_user_t *user = shell_user_find(ctx, username);
if (!user) {
return SHELL_ERR_USER_NOT_FOUND;
}
if (user->is_locked) {
return SHELL_ERR_USER_LOCKED;
}
if (!shell_verify_password(password, user->password)) {
user->login_attempts++;
if (user->login_attempts >= SHELL_MAX_LOGIN_ATTEMPTS) {
user->is_locked = 1;
}
return SHELL_ERR_INVALID_PASSWORD;
}
// 登录成功
ctx->current_user = user;
user->is_logged_in = 1;
user->login_attempts = 0;
ctx->login_state = LOGIN_STATE_LOGGED_IN;
// 更新提示符
shell_prompt_update(ctx);
return SHELL_OK;
}
/**
* @brief 用户登出
* @param ctx Shell上下文
* @return SHELL_OK表示成功
*/
shell_status_t shell_logout(shell_context_t *ctx)
{
if (!ctx) {
return SHELL_ERR;
}
if (ctx->current_user) {
ctx->current_user->is_logged_in = 0;
ctx->current_user = NULL;
}
ctx->login_state = LOGIN_STATE_LOGGED_OUT;
ctx->login_username[0] = '\0';
ctx->login_attempts = 0;
// 更新提示符
shell_prompt_update(ctx);
return SHELL_OK;
}
/**
* @brief 检查是否已登录
* @param ctx Shell上下文
* @return 1表示已登录,0表示未登录
*/
uint8_t shell_is_logged_in(shell_context_t *ctx)
{
return (ctx && ctx->current_user) ? 1 : 0;
}
/**
* @brief 获取当前用户权限级别
* @param ctx Shell上下文
* @return 权限级别,未登录返回SHELL_PERMISSION_NONE
*/
shell_permission_level_t shell_get_current_permission(shell_context_t *ctx)
{
if (!ctx || !ctx->current_user) {
return SHELL_PERMISSION_NONE;
}
return ctx->current_user->permission_level;
}
/**
* @brief 检查用户是否有权限执行命令
* @param ctx Shell上下文
* @param cmd 命令指针
* @return 1表示有权限,0表示无权限
*/
uint8_t shell_check_command_permission(shell_context_t *ctx, shell_command_t *cmd)
{
if (!ctx || !cmd) {
return 0;
}
// 如果不需要登录,则所有命令都可执行
if (!ctx->require_login) {
return 1;
}
// 未登录状态只能执行登录相关命令
if (!ctx->current_user) {
// 允许登录和帮助命令
if (strcmp(cmd->name, "login") == 0 ||
strcmp(cmd->name, "help") == 0)
{
return 1;
}
return 0;
}
// 检查命令权限掩码
if (cmd->permission_mask == 0) {
// 权限掩码为0表示所有用户都可执行
return 1;
}
// 检查当前用户权限掩码是否包含命令所需的权限
return (ctx->current_user->permission_mask & cmd->permission_mask) ? 1 : 0;
}
#endif
// ==================== 新增结束 ====================
其中核心字符解析函数:
支持的按键识别
- 上下左右箭头:
- 上箭头(
\x1b[A) - 可扩展历史命令 - 下箭头(
\x1b[B) - 可扩展历史命令 - 右箭头(
\x1b[C) - 光标右移 - 左箭头(
\x1b[D) - 光标左移
- 上箭头(
- Del键(
\x1b[3~) - 删除光标所在位置的字符 - Backspace(
\b或0x7F) - 删除光标前的字符 - ESC键 - 作为控制序列的开始标记
- Home/End键:
- Home(
\x1b[H或\x1b[1~或\x1bOH) - End(
\x1b[F或\x1b[4~或\x1bOF)
- Home(
- F1-F4功能键 - 通过SS3序列识别
主要特性
- 光标移动:支持左右箭头在命令行中移动光标位置
- 字符插入:支持在光标位置插入字符
- 字符删除:支持Del和Backspace删除字符
- 状态机设计:使用有限状态机处理终端控制序列
- 回显适配:根据光标位置正确显示命令行内容
// 终端控制字符状态机状态
typedef enum {
STATE_NORMAL, // 正常状态
STATE_ESC, // 收到ESC
STATE_CSI, // 收到'['
STATE_CSI_PARAM, // 解析CSI参数
STATE_SS3 // 收到'O' (SS3状态)
} shell_input_state_t;
/**
* @brief 字符串分割函数(增强版,支持转义字符)
* @param str 输入字符串(会被修改)
* @param argv 输出参数数组
* @param max_args 最大参数个数
* @return 解析出的参数个数
*
* 支持的特性:
* 1. 空格分隔参数
* 2. 双引号内的内容作为一个参数(支持转义)
* 3. 转义字符:\" 表示一个字面双引号,\\ 表示一个字面反斜杠
* 4. 支持转义空格(如:a\ b 作为一个参数 "a b")
*
*/
static int shell_split_args(char *str, char *argv[], int max_args)
{
int argc = 0;
int in_arg = 0;
int in_quote = 0;
int escape = 0; // 转义标志:1表示当前字符需要被转义
char *p = str; // 当前读取位置
char *q = str; // 当前写入位置
while (*p && argc < max_args)
{
if (escape) {
// 转义模式:直接复制字符,不进行特殊处理
if (in_arg) {
//已开始新参数
}else
{
//未开始新参数
argv[argc] = q; // 记录参数起始位置
argc++;
in_arg = 1;
}
*q++ = *p++;
escape = 0;
continue;
}
if (*p == '\\') {
// 遇到反斜杠,设置转义标志
escape = 1;
p++;
continue;
}
if (*p == '"') {
if (in_quote) {
// 结束引号
in_quote = 0;
*q = '\0'; // 添加字符串结束符
q++; // 跳过结束符位置(保持q和p的同步)
} else {
// 开始引号,不将引号写入目标缓冲区
in_quote = 1;
if (in_arg) {
//已开始新参数,双引号不在新参数最前面
}else
{
//未开始新参数,双引号在新参数最前面
}
}
p++;
continue;
}
// 处理空格(仅在非引号模式下作为分隔符)
if (!in_quote && (*p == ' ' || *p == '\t' || *p == '\r')) {
if (in_arg) {
// 结束当前参数
*q = '\0';
q++;
in_arg = 0;
}
p++;
continue;
}
// 开始一个新参数
if (!in_arg) {
argv[argc] = q; // 记录参数起始位置
argc++;
in_arg = 1;
}
// 复制普通字符
*q++ = *p++;
}
// 处理最后一个参数
if (in_arg) {
*q = '\0';
}
// 检查是否有未闭合的引号
if (in_quote) {
// 可以选择报错,这里简单处理为忽略最后一个不完整的参数
if (argc > 0) {
argc--; // 移除最后一个不完整的参数
}
}
argv[argc] = NULL;
return argc;
}
// 重新显示当前行
static void shell_redisplay_line(shell_context_t *ctx)
{
if (!ctx || !ctx->echo_enabled)
{
return;
}
// 清除当前行
shell_print(ctx, "\r\033[K");
// 显示提示符
shell_printf(ctx, "%s", ctx->prompt);
#if SHELL_USERS_ENABLE == 1
if(ctx->login_state != LOGIN_STATE_WAITING_PASSWORD){
#endif
// 显示当前输入缓冲区内容
shell_print(ctx, ctx->input_buffer);
#if SHELL_USERS_ENABLE == 1
}else
{
for (int i = 0; i < strlen(ctx->input_buffer); i++) {
shell_print(ctx, "*");
}
}
#endif
// 将光标移动到正确位置
int cursor_move = strlen(ctx->input_buffer) - ctx->buf_pos;
for (int i = 0; i < cursor_move; i++) {
shell_print(ctx, "\033[D");
}
}
// 处理输入字符 - 增强版,支持终端控制字符识别
shell_status_t shell_process_char(shell_context_t *ctx, char ch)
{
if (!ctx || !ctx->initialized) {
return SHELL_ERR;
}
static shell_input_state_t input_state = STATE_NORMAL;
static char csi_buffer[16]; // CSI参数缓冲区
static int csi_pos = 0;
static int esc_param1 = 0; // CSI参数1
static int esc_param2 = 0; // CSI参数2
// 状态机处理终端控制序列
switch (input_state) {
case STATE_NORMAL:
// 检查是否ESC字符
if (ch == '\x1b') {
input_state = STATE_ESC;
csi_pos = 0;
esc_param1 = 0;
esc_param2 = 0;
return SHELL_OK;
}
break;
case STATE_ESC:
// ESC后的字符
if (ch == '[') {
input_state = STATE_CSI;
csi_pos = 0;
esc_param1 = 0;
esc_param2 = 0;
return SHELL_OK;
} else if (ch == 'O') {
input_state = STATE_SS3;
return SHELL_OK;
} else {
// 未知ESC序列,重置状态
input_state = STATE_NORMAL;
return SHELL_OK;
}
case STATE_SS3:
// SS3状态:处理 F1-F4 等按键
input_state = STATE_NORMAL;
switch (ch) {
// 可以根据需要处理F1,F2,F3,F4
case 'P': // F1
return SHELL_OK;
case 'Q': // F2
return SHELL_OK;
case 'R': // F3
return SHELL_OK;
case 'S': // F4
return SHELL_OK;
case 'H': // Home
// 移动光标到行首
ctx->buf_pos = 0;
shell_redisplay_line(ctx);
return SHELL_OK;
case 'F': // End
// 移动光标到行尾
ctx->buf_pos = strlen(ctx->input_buffer);
shell_redisplay_line(ctx);
return SHELL_OK;
}
return SHELL_OK;
case STATE_CSI:
// CSI状态:处理 '[' 后的字符
if (ch >= '0' && ch <= '9') {
// 解析参数
if (csi_pos == 0) {
esc_param1 = esc_param1 * 10 + (ch - '0');
} else if (csi_pos == 1) {
esc_param2 = esc_param2 * 10 + (ch - '0');
}
csi_buffer[csi_pos++] = ch;
return SHELL_OK;
} else if (ch == ';') {
// 参数分隔符
csi_pos++;
csi_buffer[csi_pos] = '\0';
return SHELL_OK;
} else {
// 命令字符
input_state = STATE_NORMAL;
// 处理各种CSI命令
switch (ch) {
case 'A': // 上箭头 历史记录上一条
#if SHELL_HISTORY_SIZE > 0
shell_history_prev(ctx);
#endif
break;
case 'B': // 下箭头 历史记录下一条
#if SHELL_HISTORY_SIZE > 0
shell_history_next(ctx);
#endif
break;
case 'C': // 右箭头
if (ctx->buf_pos < strlen(ctx->input_buffer)) {
ctx->buf_pos++;
if (ctx->echo_enabled) {
// 发送光标右移序列
const char *move_right = "\033[C";
ctx->io.send(move_right, strlen(move_right));
}
}
break;
case 'D': // 左箭头
if (ctx->buf_pos > 0) {
ctx->buf_pos--;
if (ctx->echo_enabled) {
// 发送光标左移序列
const char *move_left = "\033[D";
ctx->io.send(move_left, strlen(move_left));
}
}
break;
case 'H': // Home键
ctx->buf_pos = 0;
shell_redisplay_line(ctx);
break;
case 'F': // End键
ctx->buf_pos = strlen(ctx->input_buffer);
shell_redisplay_line(ctx);
break;
case '~': // 带数字前缀的命令
if (esc_param1 == 3)
{
// Del键
if (ctx->buf_pos < strlen(ctx->input_buffer)) {
// 删除当前位置的字符
memmove(&ctx->input_buffer[ctx->buf_pos],
&ctx->input_buffer[ctx->buf_pos + 1],
strlen(ctx->input_buffer) - ctx->buf_pos);
shell_redisplay_line(ctx);
}
} else if (esc_param1 == 5) { // Page Up
// 可扩展功能
} else if (esc_param1 == 6) { // Page Down
// 可扩展功能
}
break;
case 'Z': // Shift+Tab 反向补全
break;
}
return SHELL_OK;
}
default:
input_state = STATE_NORMAL;
break;
}
// 处理普通字符
if (input_state == STATE_NORMAL) {
// 处理回车
if (ch == '\r' || ch == '\n')
{
if (strlen(ctx->input_buffer) > 0) {
//ctx->input_buffer[ctx->buf_pos] = '\0';
#if SHELL_HISTORY_SIZE > 0
// 将命令添加到历史记录
shell_history_add(ctx, ctx->input_buffer);
#endif
// 回显输入
if (ctx->echo_enabled) {
shell_print(ctx, "\r\n");
}
// 处理命令
shell_process_string(ctx, ctx->input_buffer);
// 重置缓冲区
ctx->buf_pos = 0;
memset(ctx->input_buffer,0,SHELL_BUFFER_SIZE);
ctx->input_buffer[0] = '\0';
// 显示新提示符
if (ctx->echo_enabled) {
shell_printf(ctx, "%s", ctx->prompt);
}
} else {
// 空行,只显示提示符
if (ctx->echo_enabled) {
shell_printf(ctx, "\r\n%s", ctx->prompt);
}
}
return SHELL_OK;
}
// 处理退格
if (ch == '\b' || ch == 0x7F)
{
if (ctx->buf_pos > 0) {
// 删除当前位置的字符
memmove(&ctx->input_buffer[ctx->buf_pos - 1],
&ctx->input_buffer[ctx->buf_pos],
strlen(ctx->input_buffer) - ctx->buf_pos + 1);
ctx->buf_pos--;
shell_redisplay_line(ctx);
}
return SHELL_OK;
}
// 处理Tab键(命令补全)
if (ch == '\t') {
if(0 ==strlen(ctx->input_buffer))
{
shell_list_commands(ctx);
shell_printf(ctx, "%s", ctx->prompt);
}else {
// 这里可以实现命令补全功能
}
return SHELL_OK;
}
// 普通字符 - 插入到当前光标位置
if (ctx->buf_pos < sizeof(ctx->input_buffer) - 1) {
// 如果有字符在当前光标位置之后,需要移动它们
if (ctx->buf_pos < strlen(ctx->input_buffer)) {
// 将光标后的字符向后移动
memmove(&ctx->input_buffer[ctx->buf_pos + 1],
&ctx->input_buffer[ctx->buf_pos],
strlen(ctx->input_buffer) - ctx->buf_pos + 1);
}
// 插入新字符
ctx->input_buffer[ctx->buf_pos] = ch;
ctx->buf_pos++;
// 回显字符
if (ctx->echo_enabled && ctx->io.send) {
if (ctx->buf_pos == strlen(ctx->input_buffer)) {
#if SHELL_USERS_ENABLE == 1
if(ctx->login_state != LOGIN_STATE_WAITING_PASSWORD){
#endif
// 在行尾,直接回显
char echo[2] = {ch, '\0'};
ctx->io.send(echo, 1);
#if SHELL_USERS_ENABLE == 1
}else{
// 在行尾,直接回显
char echo[2] = {'*', '\0'};
ctx->io.send(echo, 1);
}
#endif
} else {
// 在行中插入,需要重新显示整行
shell_redisplay_line(ctx);
}
}
}
}
return SHELL_OK;
}
然后就是处理一帧命令字符串,解析并执行命令。
// 处理字符串
shell_status_t shell_process_string(shell_context_t *ctx, const char *str)
{
char buffer[SHELL_BUFFER_SIZE];
char *argv[SHELL_MAX_ARGS];
int argc;
if (!ctx || !str) {
return SHELL_ERR;
}
// ==================== 新增:处理登录序列 ====================
#if SHELL_USERS_ENABLE == 1
if (ctx->require_login && !ctx->current_user) {
// 未登录状态,处理登录序列
return shell_handle_login_sequence(ctx, str);
}
#endif
// ==================== 新增结束 ====================
// 复制字符串以便修改
strncpy(buffer, str, sizeof(buffer) - 1);
buffer[sizeof(buffer) - 1] = '\0';
// 分割参数
argc = shell_split_args(buffer, argv, sizeof(argv)/sizeof(argv[0]));
if (argc == 0) {
return SHELL_OK;
}
// ==================== 新增:登出命令 ====================
#if SHELL_USERS_ENABLE == 1
if (strcmp(argv[0], "logout") == 0) {
shell_logout(ctx);
return SHELL_OK;
}
#endif
// ==================== 新增结束 ====================
// 处理内置命令
if (strcmp(argv[0], "help") == 0) {
shell_print_help(ctx);
return SHELL_OK;
}
if (strcmp(argv[0], "list") == 0) {
shell_list_commands(ctx);
return SHELL_OK;
}
if (strcmp(argv[0], "echo") == 0) {
// ==================== 新增:权限检查 ====================
#if SHELL_USERS_ENABLE == 1
if (ctx->current_user && ctx->current_user->permission_level < SHELL_PERMISSION_USER) {
shell_print(ctx, "Permission denied\r\n");
shell_printf(ctx, "%s", ctx->prompt);
return SHELL_ERR_PERMISSION_DENIED;
}
#endif
// ==================== 新增结束 ====================
if (argc == 2) {
if (strcmp(argv[1], "on") == 0) {
ctx->echo_enabled = 1;
shell_print(ctx, "Echo enabled\r\n");
} else if (strcmp(argv[1], "off") == 0) {
ctx->echo_enabled = 0;
shell_print(ctx, "Echo disabled\r\n");
} else {
shell_print(ctx, "Usage: echo <on|off>\r\n");
}
} else {
shell_printf(ctx, "Echo is %s\r\n",
ctx->echo_enabled ? "enabled" : "disabled");
}
return SHELL_OK;
}
#if SHELL_HISTORY_SIZE > 0
// 添加历史记录相关命令
if (strcmp(argv[0], "history") == 0) {
// ==================== 新增:权限检查 ====================
#if SHELL_USERS_ENABLE == 1
if (ctx->current_user && ctx->current_user->permission_level < SHELL_PERMISSION_USER) {
shell_print(ctx, "Permission denied\r\n");
shell_printf(ctx, "%s", ctx->prompt);
return SHELL_ERR_PERMISSION_DENIED;
}
#endif
// ==================== 新增结束 ====================
if (argc == 1) {
shell_history_list(ctx);
} else if (argc == 2) {
if (strcmp(argv[1], "clear") == 0) {
shell_history_clear(ctx);
} else {
shell_print(ctx, "Usage: history [clear]\r\n");
}
}
return SHELL_OK;
}
#endif
// ==================== 新增:用户管理命令 ====================
#if SHELL_USERS_ENABLE == 1
if (strcmp(argv[0], "users") == 0) {
shell_list_users(ctx);
return SHELL_OK;
}
if (strcmp(argv[0], "passwd") == 0) {
// 修改密码
if (argc == 1) {
// 修改当前用户密码
shell_print(ctx, "Changing password for current user\r\n");
// 简化处理,实际应该交互式输入
shell_print(ctx, "Usage: passwd <old_password> <new_password>\r\n");
} else if (argc == 3) {
shell_user_change_password(ctx,
ctx->current_user->username,
argv[1], argv[2]);
} else {
shell_print(ctx, "Usage: passwd <old_password> <new_password>\r\n");
}
return SHELL_OK;
}
if (strcmp(argv[0], "useradd") == 0) {
// 添加用户(需要管理员权限)
if (argc >= 3) {
shell_permission_level_t level = SHELL_PERMISSION_USER;
if (argc >= 4) {
level = (shell_permission_level_t)atoi(argv[3]);
}
shell_user_add(ctx, argv[1], argv[2], level);
} else {
shell_print(ctx, "Usage: useradd <username> <password> [permission_level]\r\n");
}
return SHELL_OK;
}
if (strcmp(argv[0], "userdel") == 0) {
// 删除用户(需要管理员权限)
if (argc == 2) {
shell_user_remove(ctx, argv[1]);
} else {
shell_print(ctx, "Usage: userdel <username>\r\n");
}
return SHELL_OK;
}
if (strcmp(argv[0], "userlock") == 0) {
// 锁定用户(需要管理员权限)
if (argc == 2) {
shell_user_lock(ctx, argv[1]);
} else {
shell_print(ctx, "Usage: userlock <username>\r\n");
}
return SHELL_OK;
}
if (strcmp(argv[0], "userunlock") == 0) {
// 解锁用户(需要管理员权限)
if (argc == 2) {
shell_user_unlock(ctx, argv[1]);
} else {
shell_print(ctx, "Usage: userunlock <username>\r\n");
}
return SHELL_OK;
}
#endif
// ==================== 新增结束 ====================
// 查找命令
shell_command_t *cmd = shell_find_command(ctx, argv[0]);
if (!cmd) {
shell_printf(ctx, "Error: Command '%s' not found\r\n", argv[0]);
return SHELL_ERR_CMD_NOT_FOUND;
}
// ==================== 新增:权限检查 ====================
#if SHELL_USERS_ENABLE == 1
if (!shell_check_command_permission(ctx, cmd)) {
shell_printf(ctx, "Error: Permission denied for command '%s'\r\n", argv[0]);
shell_printf(ctx, "%s", ctx->prompt);
return SHELL_ERR_PERMISSION_DENIED;
}
#endif
// ==================== 新增结束 ====================
ctx->cur_cmd = cmd;
// 执行命令
if (cmd->handler) {
shell_execute_command(ctx, ctx->cur_cmd, argc, argv);
}
ctx->cur_cmd = NULL;
return SHELL_OK;
}
// 执行命令
static void shell_execute_command(shell_context_t *ctx,
shell_command_t *cmd,
int argc, char **argv)
{
shell_status_t status = cmd->handler(argc, argv);
if (status != SHELL_OK) {
shell_printf(ctx, "Command returned error: %d\r\n", status);
}
}
接着是增加历史记录功能和用户管理功能。
历史记录主要功能特点
- 历史记录存储:使用环形缓冲区存储最近执行的命令
- 历史浏览:通过上下箭头浏览历史命令
- 命令去重:自动过滤连续重复的命令
- 历史命令管理:支持
history命令显示历史记录,history clear清除历史 - 内存优化:自动整理历史记录缓冲区,避免内存碎片
- 状态保持:历史浏览时可以保持当前编辑状态
- 扩展性:预留了保存/加载历史记录到文件的功能
//历史记录配置
#define SHELL_HISTORY_SIZE 10 //=0时关闭该功能,历史记录最大条数
#define SHELL_HISTORY_BUFFER_SIZE (SHELL_BUFFER_SIZE * SHELL_HISTORY_SIZE) // 历史记录缓冲区总大小
#if SHELL_HISTORY_SIZE > 0
// 历史记录相关字段
char history_buffer[SHELL_HISTORY_BUFFER_SIZE]; // 历史记录存储缓冲区
char *history[SHELL_HISTORY_SIZE]; // 历史记录索引数组
size_t history_count; // 当前历史记录数量
size_t history_pos; // 当前浏览位置
int history_index; // 最新命令索引
#endif
#if SHELL_HISTORY_SIZE > 0
// 历史记录功能函数声明
static void shell_history_add(shell_context_t *ctx, const char *cmd);
static void shell_history_prev(shell_context_t *ctx);
static void shell_history_next(shell_context_t *ctx);
static void shell_history_clear(shell_context_t *ctx); // 清除历史记录
static void shell_history_list(shell_context_t *ctx); // 显示历史记录
static void shell_history_compact(shell_context_t *ctx); // 整理历史记录缓冲区
static void shell_history_save(shell_context_t *ctx);
static void shell_history_load(shell_context_t *ctx);
#endif
////////////////////////////////////////////////////////////////////////////
#if SHELL_HISTORY_SIZE > 0
// 添加命令到历史记录
static void shell_history_add(shell_context_t *ctx, const char *cmd)
{
if (!ctx || !cmd || strlen(cmd) == 0) {
return;
}
// 检查是否与上一条历史记录重复
if (ctx->history_count > 0) {
int last_idx = (ctx->history_index - 1 + SHELL_HISTORY_SIZE) % SHELL_HISTORY_SIZE;
if (ctx->history[last_idx] && strcmp(ctx->history[last_idx], cmd) == 0) {
return; // 重复命令,不添加
}
}
size_t cmd_len = strlen(cmd) + 1;
// 检查缓冲区空间
if (ctx->history_count >= SHELL_HISTORY_SIZE) {
// 历史已满,释放最旧的一条
char *oldest = ctx->history[ctx->history_index];
if (oldest) {
size_t oldest_len = strlen(oldest) + 1;
// 移动缓冲区数据(简化处理,实际可以使用环形缓冲区)
memmove(oldest, oldest + oldest_len,
ctx->history_buffer + sizeof(ctx->history_buffer) - (oldest + oldest_len));
// 调整所有指针
for (size_t i = 0; i < SHELL_HISTORY_SIZE; i++) {
if (ctx->history[i] && ctx->history[i] > oldest) {
ctx->history[i] -= oldest_len;
}
}
}
ctx->history_count--;
}
// 找到可用的存储位置
char *store_pos;
if (ctx->history_count == 0) {
store_pos = ctx->history_buffer;
ctx->history_index = 0;
} else {
// 计算下一个存储位置
int last_idx = (ctx->history_index + ctx->history_count - 1) % SHELL_HISTORY_SIZE;
char *last_cmd = ctx->history[last_idx];
store_pos = last_cmd + strlen(last_cmd) + 1;
// 检查是否超出缓冲区
if (store_pos + cmd_len > ctx->history_buffer + sizeof(ctx->history_buffer)) {
// 缓冲区满,重新整理
shell_history_compact(ctx);
// 重新计算存储位置
if (ctx->history_count > 0) {
last_idx = (ctx->history_index + ctx->history_count - 1) % SHELL_HISTORY_SIZE;
last_cmd = ctx->history[last_idx];
store_pos = last_cmd + strlen(last_cmd) + 1;
} else {
store_pos = ctx->history_buffer;
}
}
}
// 存储命令
strcpy(store_pos, cmd);
// 更新索引数组
int new_idx = (ctx->history_index + ctx->history_count) % SHELL_HISTORY_SIZE;
ctx->history[new_idx] = store_pos;
ctx->history_count++;
// 重置浏览位置
ctx->history_pos = ctx->history_count;
}
// 整理历史记录缓冲区
static void shell_history_compact(shell_context_t *ctx)
{
if (ctx->history_count == 0) return;
char temp_buffer[SHELL_HISTORY_BUFFER_SIZE];
char *temp_ptr = temp_buffer;
char *new_history[SHELL_HISTORY_SIZE];
size_t new_count = 0;
// 按顺序整理历史记录
for (size_t i = 0; i < ctx->history_count; i++) {
int idx = (ctx->history_index + i) % SHELL_HISTORY_SIZE;
if (ctx->history[idx]) {
size_t len = strlen(ctx->history[idx]) + 1;
if (temp_ptr + len <= temp_buffer + sizeof(temp_buffer)) {
strcpy(temp_ptr, ctx->history[idx]);
new_history[new_count++] = temp_ptr;
temp_ptr += len;
}
}
}
// 复制回原缓冲区
memcpy(ctx->history_buffer, temp_buffer, temp_ptr - temp_buffer);
memcpy(ctx->history, new_history, new_count * sizeof(char*));
ctx->history_count = new_count;
ctx->history_index = 0;
}
// 获取上一条历史命令
static void shell_history_prev(shell_context_t *ctx)
{
if (!ctx || ctx->history_count == 0) {
return;
}
if (ctx->history_pos > 0) {
ctx->history_pos--;
}
size_t idx = (ctx->history_index + ctx->history_pos) % SHELL_HISTORY_SIZE;
const char *history_cmd = ctx->history[idx];
if (history_cmd) {
// 保存当前输入(用于下箭头恢复)
if (ctx->history_pos == ctx->history_count - 1) {
// 可以保存当前编辑的命令
}
// 替换输入缓冲区
strncpy(ctx->input_buffer, history_cmd, sizeof(ctx->input_buffer) - 1);
ctx->input_buffer[sizeof(ctx->input_buffer) - 1] = '\0';
ctx->buf_pos = strlen(ctx->input_buffer);
// 重新显示
shell_redisplay_line(ctx);
}
}
// 获取下一条历史命令
static void shell_history_next(shell_context_t *ctx)
{
if (!ctx) {
return;
}
if (ctx->history_pos < ctx->history_count - 1) {
ctx->history_pos++;
size_t idx = (ctx->history_index + ctx->history_pos) % SHELL_HISTORY_SIZE;
const char *history_cmd = ctx->history[idx];
if (history_cmd) {
strncpy(ctx->input_buffer, history_cmd, sizeof(ctx->input_buffer) - 1);
ctx->input_buffer[sizeof(ctx->input_buffer) - 1] = '\0';
ctx->buf_pos = strlen(ctx->input_buffer);
}
} else {
// 已经到最后一条历史记录,清空输入
ctx->history_pos = ctx->history_count;
memset(ctx->input_buffer,0,SHELL_BUFFER_SIZE);
ctx->buf_pos = 0;
}
// 重新显示
shell_redisplay_line(ctx);
}
// 清除历史记录
static void shell_history_clear(shell_context_t *ctx)
{
if (!ctx) {
return;
}
memset(ctx->history_buffer, 0, sizeof(ctx->history_buffer));
memset(ctx->history, 0, sizeof(ctx->history));
ctx->history_count = 0;
ctx->history_pos = 0;
ctx->history_index = -1;
shell_print(ctx, "History cleared\r\n");
}
// 显示历史记录
static void shell_history_list(shell_context_t *ctx)
{
if (!ctx) {
return;
}
if (ctx->history_count == 0) {
shell_print(ctx, "No history commands\r\n");
return;
}
shell_print(ctx, "\r\n=== Command History ===\r\n");
for (size_t i = 0; i < ctx->history_count; i++) {
int idx = (ctx->history_index + i) % SHELL_HISTORY_SIZE;
if (ctx->history[idx]) {
shell_printf(ctx, "%3zu: %s\r\n", i + 1, ctx->history[idx]);
}
}
}
// 添加保存历史记录到文件的功能(可选)
shell_status_t shell_history_save_to_file(shell_context_t *ctx, const char *filename)
{
if (!ctx || !filename) {
return SHELL_ERR;
}
FILE *fp = fopen(filename, "w");
if (!fp) {
return SHELL_ERR;
}
for (size_t i = 0; i < ctx->history_count; i++) {
int idx = (ctx->history_index + i) % SHELL_HISTORY_SIZE;
if (ctx->history[idx]) {
fprintf(fp, "%s\n", ctx->history[idx]);
}
}
fclose(fp);
return SHELL_OK;
}
// 从文件加载历史记录(可选)
shell_status_t shell_history_load_from_file(shell_context_t *ctx, const char *filename)
{
if (!ctx || !filename) {
return SHELL_ERR;
}
FILE *fp = fopen(filename, "r");
if (!fp) {
return SHELL_ERR;
}
char buffer[SHELL_BUFFER_SIZE];
while (fgets(buffer, sizeof(buffer), fp)) {
// 去除换行符
size_t len = strlen(buffer);
if (len > 0 && buffer[len - 1] == '\n') {
buffer[len - 1] = '\0';
}
shell_history_add(ctx, buffer);
}
fclose(fp);
return SHELL_OK;
}
#endif
用户管理主要功能特点
- 用户管理:
- 支持多用户
- 用户增删改查
- 密码管理
- 账户锁定/解锁
- 权限级别:
- 超级用户(root):最高权限
- 管理员(admin):管理权限
- 普通用户(user):基本权限
- 访客(guest):受限权限
- 命令权限控制:
- 每个命令可以设置所需的权限掩码
- 自动检查用户权限
- 权限不足时拒绝执行
- 登录系统:
- 交互式登录
- 密码验证
- 登录尝试次数限制
- 账户自动锁定
- 安全特性:
- 密码不回显
- 登录超时
- 暴力破解防护
- 权限隔离
- 内置命令:
- login:登录
- logout:登出
- passwd:修改密码
- users:查看用户列表
- useradd:添加用户
- userdel:删除用户
- userlock:锁定用户
- userunlock:解锁用户
// ==================== 新增:权限管理配置 ====================
#define SHELL_USERS_ENABLE 1 // 用户权限功能开关
#define SHELL_MAX_USERS 10 // 最大用户数
#define SHELL_USERNAME_MAX_LEN 32 // 用户名最大长度
#define SHELL_PASSWORD_MAX_LEN 32 // 密码最大长度
#define SHELL_MAX_LOGIN_ATTEMPTS 3 // 最大登录尝试次数
#define SHELL_LOGIN_TIMEOUT 30000 // 登录超时时间(ms)
// ==================== 新增结束 ====================
// ==================== 新增:权限相关错误码 ========
SHELL_ERR_PERMISSION_DENIED, // 权限不足
SHELL_ERR_USER_NOT_FOUND, // 用户不存在
SHELL_ERR_INVALID_PASSWORD, // 密码错误
SHELL_ERR_USER_LOCKED, // 用户被锁定
SHELL_ERR_NOT_LOGGED_IN, // 未登录
SHELL_ERR_LOGIN_TIMEOUT, // 登录超时
// ==================== 新增结束 ====================
// ==================== 新增:权限级别枚举 ====================
typedef enum {
SHELL_PERMISSION_NONE = 0, // 无权限
SHELL_PERMISSION_GUEST = 1, // 访客权限
SHELL_PERMISSION_USER = 2, // 普通用户权限
SHELL_PERMISSION_ADMIN = 3, // 管理员权限
SHELL_PERMISSION_ROOT = 8, // 超级用户权限
} shell_permission_level_t;
// 权限位掩码定义(用于命令权限控制)
#define SHELL_PERM_MASK_GUEST 0x01 // 访客位
#define SHELL_PERM_MASK_USER 0x02 // 普通用户位
#define SHELL_PERM_MASK_ADMIN 0x04 // 管理员位
#define SHELL_PERM_MASK_ROOT 0x80 // 超级用户位
#define SHELL_PERM_MASK_ALL (SHELL_PERM_MASK_USER | SHELL_PERM_MASK_ADMIN | \
SHELL_PERM_MASK_ROOT | SHELL_PERM_MASK_GUEST) // 所有权限
// 默认权限配置
#define SHELL_PERM_DEFAULT_GUEST SHELL_PERM_MASK_GUEST // 访客默认权限
#define SHELL_PERM_DEFAULT_USER SHELL_PERM_MASK_USER // 普通用户默认权限
#define SHELL_PERM_DEFAULT_ADMIN SHELL_PERM_MASK_ADMIN // 管理员默认权限
#define SHELL_PERM_DEFAULT_ROOT SHELL_PERM_MASK_ROOT // 超级用户默认权限
// ==================== 新增结束 ====================
// ==================== 新增:用户信息结构体 ====================
typedef struct shell_user {
char username[SHELL_USERNAME_MAX_LEN]; // 用户名
char password[SHELL_PASSWORD_MAX_LEN]; // 密码(实际使用中应存储哈希值)
shell_permission_level_t permission_level; // 用户权限级别
uint32_t permission_mask; // 用户权限掩码(可访问的权限位)
uint32_t last_login_time; // 最后登录时间
uint8_t is_locked; // 是否被锁定
uint8_t login_attempts; // 登录尝试次数
uint8_t is_logged_in; // 当前是否已登录
struct shell_user *next; // 链表指针
} shell_user_t;
// 登录状态枚举
typedef enum {
LOGIN_STATE_LOGGED_OUT = 0, // 未登录
LOGIN_STATE_LOGGED_IN, // 已登录
LOGIN_STATE_WAITING_PASSWORD, // 等待输入密码
LOGIN_STATE_CHANGING_PASSWORD // 正在修改密码
} shell_login_state_t;
// ==================== 新增结束 ====================
#if SHELL_USERS_ENABLE == 1
// ==================== 新增:用户和权限管理相关字段 ====================
shell_user_t *user_list; // 用户链表头
shell_user_t *current_user; // 当前登录用户
shell_login_state_t login_state; // 登录状态
char login_username[SHELL_USERNAME_MAX_LEN]; // 登录中的用户名
uint32_t login_start_time; // 登录开始时间
uint8_t login_attempts; // 当前登录尝试次数
uint8_t require_login; // 是否需要登录才能使用shell
// ==================== 新增结束 ====================
#endif
// ==================== 新增:用户管理内部函数声明 ====================
#if SHELL_USERS_ENABLE == 1
static shell_user_t* shell_user_alloc(void);
static void shell_user_free(shell_user_t *user);
static uint32_t shell_permission_level_to_mask(shell_permission_level_t level);
static void shell_prompt_update(shell_context_t *ctx);
static shell_status_t shell_handle_login_sequence(shell_context_t *ctx, const char *input);
static uint8_t shell_verify_password(const char *input_pass, const char *stored_pass);
#endif
// ==================== 新增结束 ====================
// ==================== 新增:用户管理内部函数实现 ====================
#if SHELL_USERS_ENABLE == 1
/**
* @brief 分配新的用户节点
* @return 分配的用户节点指针,失败返回NULL
*/
static shell_user_t* shell_user_alloc(void)
{
shell_user_t *user = (shell_user_t*)malloc(sizeof(shell_user_t));
if (user) {
memset(user, 0, sizeof(shell_user_t));
}
return user;
}
/**
* @brief 释放用户节点
* @param user 要释放的用户节点
*/
static void shell_user_free(shell_user_t *user)
{
if (user) {
free(user);
}
}
/**
* @brief 将权限级别转换为权限掩码
* @param level 权限级别
* @return 权限掩码
*/
static uint32_t shell_permission_level_to_mask(shell_permission_level_t level)
{
switch (level) {
case SHELL_PERMISSION_USER:
return SHELL_PERM_MASK_USER;
case SHELL_PERMISSION_ADMIN:
return SHELL_PERM_MASK_ADMIN;
case SHELL_PERMISSION_ROOT:
return SHELL_PERM_MASK_ROOT;
case SHELL_PERMISSION_GUEST:
return SHELL_PERM_MASK_GUEST;
default:
return 0;
}
}
/**
* @brief 更新提示符,显示当前用户名
*/
static void shell_prompt_update(shell_context_t *ctx)
{
if (!ctx) return;
if (ctx->current_user) {
// 已登录,显示 [用户名@主机]$
snprintf(ctx->prompt, sizeof(ctx->prompt), "[%s@shell]$ ",
ctx->current_user->username);
} else if (ctx->login_state == LOGIN_STATE_WAITING_PASSWORD) {
// 等待输入密码,显示 Password:
strcpy(ctx->prompt, "Password: ");
} else {
// 未登录,显示 login:
strcpy(ctx->prompt, "login: ");
}
}
/**
* @brief 简单的密码验证(实际应用中应使用哈希比较)
* @param input_pass 输入的密码
* @param stored_pass 存储的密码
* @return 1表示验证通过,0表示失败
*/
static uint8_t shell_verify_password(const char *input_pass, const char *stored_pass)
{
// TODO: 实际应用中应使用安全的密码哈希比较,如bcrypt、scrypt等
// 这里仅作演示,直接比较字符串
return (strcmp(input_pass, stored_pass) == 0) ? 1 : 0;
}
/**
* @brief 处理登录序列(用户名/密码输入)
* @param ctx Shell上下文
* @param input 用户输入
* @return SHELL_OK表示登录处理完成,SHELL_ERR表示需要继续
*/
static shell_status_t shell_handle_login_sequence(shell_context_t *ctx, const char *input)
{
if (!ctx || !input) {
return SHELL_ERR;
}
switch (ctx->login_state) {
case LOGIN_STATE_LOGGED_OUT:
// 未登录状态,等待输入用户名
if (strlen(input) > 0) {
// 保存用户名
strncpy(ctx->login_username, input, sizeof(ctx->login_username) - 1);
ctx->login_username[sizeof(ctx->login_username) - 1] = '\0';
// 查找用户
shell_user_t *user = shell_user_find(ctx, ctx->login_username);
if (!user) {
shell_print(ctx, "\r\nUser not found\r\n");
ctx->login_attempts++;
if (ctx->login_attempts >= SHELL_MAX_LOGIN_ATTEMPTS) {
shell_print(ctx, "Too many login attempts\r\n");
// 可以在这里添加延时防止暴力破解
}
return SHELL_OK;
}
if (user->is_locked) {
shell_print(ctx, "\r\nUser account is locked\r\n");
return SHELL_OK;
}
// 切换到等待密码状态
ctx->login_state = LOGIN_STATE_WAITING_PASSWORD;
ctx->login_start_time = 0; // 实际应用中应获取系统时间
shell_prompt_update(ctx);
}
break;
case LOGIN_STATE_WAITING_PASSWORD:
// 等待密码状态,验证密码
{
shell_user_t *user = shell_user_find(ctx, ctx->login_username);
if (!user) {
// 用户不存在,返回登录状态
ctx->login_state = LOGIN_STATE_LOGGED_OUT;
ctx->login_username[0] = '\0';
shell_prompt_update(ctx);
return SHELL_OK;
}
// 验证密码
if (shell_verify_password(input, user->password)) {
// 登录成功
ctx->current_user = user;
user->is_logged_in = 1;
user->login_attempts = 0;
ctx->login_state = LOGIN_STATE_LOGGED_IN;
// 更新提示符
shell_prompt_update(ctx);
// 显示欢迎信息
shell_printf(ctx, "\r\nWelcome, %s!\r\n", user->username);
shell_printf(ctx, "Permission level: %d\r\n", user->permission_level);
} else {
// 密码错误
user->login_attempts++;
ctx->login_attempts++;
shell_print(ctx, "\r\nInvalid password\r\n");
if (user->login_attempts >= SHELL_MAX_LOGIN_ATTEMPTS) {
// 锁定用户
user->is_locked = 1;
shell_print(ctx, "User account locked due to too many failed attempts\r\n");
ctx->login_state = LOGIN_STATE_LOGGED_OUT;
ctx->login_username[0] = '\0';
} else {
// 继续等待密码
return SHELL_OK; // 保持等待密码状态,不显示提示符
}
// 返回登录状态
ctx->login_state = LOGIN_STATE_LOGGED_OUT;
shell_prompt_update(ctx);
}
}
break;
default:
break;
}
return SHELL_OK;
}
/**
* @brief 初始化默认用户(管理员和访客)
* @param ctx Shell上下文
* @return SHELL_OK表示成功
*/
shell_status_t shell_init_default_users(shell_context_t *ctx)
{
if (!ctx) {
return SHELL_ERR;
}
// 添加管理员用户(密码:admin)
shell_user_add(ctx, "admin", "admin", SHELL_PERMISSION_ADMIN);
// 添加普通用户(密码:user)
shell_user_add(ctx, "user", "user", SHELL_PERMISSION_USER);
// 添加访客用户(密码:guest,无密码)
shell_user_add(ctx, "guest", "", SHELL_PERMISSION_GUEST);
// 添加超级用户(密码:root)
shell_user_add(ctx, "root", "root", SHELL_PERMISSION_ROOT);
return SHELL_OK;
}
/**
* @brief 列出所有用户(需要管理员权限)
* @param ctx Shell上下文
*/
void shell_list_users(shell_context_t *ctx)
{
if (!ctx) {
return;
}
// 检查权限(只有管理员和root可以查看用户列表)
if (!ctx->current_user ||
(ctx->current_user->permission_level < SHELL_PERMISSION_ADMIN)) {
shell_print(ctx, "Permission denied\r\n");
return;
}
shell_print(ctx, "\r\n=== User List ===\r\n");
shell_printf(ctx, "%-20s %-10s %-8s %-8s\r\n",
"Username", "Level", "Locked", "Logged In");
shell_print(ctx, "------------------------------------------------\r\n");
shell_user_t *user = ctx->user_list;
while (user) {
const char *level_str;
switch (user->permission_level) {
case SHELL_PERMISSION_ROOT: level_str = "root"; break;
case SHELL_PERMISSION_ADMIN: level_str = "admin"; break;
case SHELL_PERMISSION_USER: level_str = "user"; break;
case SHELL_PERMISSION_GUEST: level_str = "guest"; break;
default: level_str = "none"; break;
}
shell_printf(ctx, "%-20s %-10s %-8s %-8s\r\n",
user->username,
level_str,
user->is_locked ? "Yes" : "No",
user->is_logged_in ? "Yes" : "No");
user = user->next;
}
shell_print(ctx, "\r\n");
}
// ==================== 新增:用户管理API实现 ====================
/**
* @brief 添加新用户
* @param ctx Shell上下文
* @param username 用户名
* @param password 密码
* @param level 权限级别
* @return SHELL_OK表示成功
*/
shell_status_t shell_user_add(shell_context_t *ctx,
const char *username,
const char *password,
shell_permission_level_t level)
{
if (!ctx || !username) {
return SHELL_ERR;
}
// 检查用户是否已存在
if (shell_user_find(ctx, username)) {
return SHELL_ERR_CMD_EXISTS; // 用户已存在
}
// 分配新用户节点
shell_user_t *new_user = shell_user_alloc();
if (!new_user) {
return SHELL_ERR_MEMORY;
}
// 填充用户信息
strncpy(new_user->username, username, sizeof(new_user->username) - 1);
new_user->username[sizeof(new_user->username) - 1] = '\0';
if (password) {
strncpy(new_user->password, password, sizeof(new_user->password) - 1);
new_user->password[sizeof(new_user->password) - 1] = '\0';
}
new_user->permission_level = level;
new_user->permission_mask = shell_permission_level_to_mask(level);
new_user->is_locked = 0;
new_user->login_attempts = 0;
new_user->last_login_time = 0;
new_user->is_logged_in = 0;
// 添加到用户链表头部
new_user->next = ctx->user_list;
ctx->user_list = new_user;
return SHELL_OK;
}
/**
* @brief 删除用户
* @param ctx Shell上下文
* @param username 要删除的用户名
* @return SHELL_OK表示成功
*/
shell_status_t shell_user_remove(shell_context_t *ctx, const char *username)
{
if (!ctx || !username) {
return SHELL_ERR;
}
// 不能删除当前登录用户
if (ctx->current_user && strcmp(ctx->current_user->username, username) == 0) {
return SHELL_ERR_PERMISSION_DENIED;
}
shell_user_t *prev = NULL;
shell_user_t *curr = ctx->user_list;
while (curr) {
if (strcmp(curr->username, username) == 0) {
if (prev) {
prev->next = curr->next;
} else {
ctx->user_list = curr->next;
}
shell_user_free(curr);
return SHELL_OK;
}
prev = curr;
curr = curr->next;
}
return SHELL_ERR_USER_NOT_FOUND;
}
/**
* @brief 修改用户密码
* @param ctx Shell上下文
* @param username 用户名
* @param old_password 旧密码
* @param new_password 新密码
* @return SHELL_OK表示成功
*/
shell_status_t shell_user_change_password(shell_context_t *ctx,
const char *username,
const char *old_password,
const char *new_password)
{
if (!ctx || !username || !new_password) {
return SHELL_ERR;
}
shell_user_t *user = shell_user_find(ctx, username);
if (!user) {
return SHELL_ERR_USER_NOT_FOUND;
}
// 验证旧密码(如果是修改自己的密码)
if (ctx->current_user && strcmp(ctx->current_user->username, username) == 0) {
if (!shell_verify_password(old_password, user->password)) {
return SHELL_ERR_INVALID_PASSWORD;
}
} else {
// 修改其他用户密码需要管理员权限
if (!ctx->current_user ||
ctx->current_user->permission_level < SHELL_PERMISSION_ADMIN) {
return SHELL_ERR_PERMISSION_DENIED;
}
}
// 设置新密码
strncpy(user->password, new_password, sizeof(user->password) - 1);
user->password[sizeof(user->password) - 1] = '\0';
return SHELL_OK;
}
/**
* @brief 设置用户权限级别
* @param ctx Shell上下文
* @param username 用户名
* @param level 新的权限级别
* @return SHELL_OK表示成功
*/
shell_status_t shell_user_set_permission(shell_context_t *ctx,
const char *username,
shell_permission_level_t level)
{
if (!ctx || !username) {
return SHELL_ERR;
}
// 需要管理员权限
if (!ctx->current_user ||
ctx->current_user->permission_level < SHELL_PERMISSION_ADMIN) {
return SHELL_ERR_PERMISSION_DENIED;
}
shell_user_t *user = shell_user_find(ctx, username);
if (!user) {
return SHELL_ERR_USER_NOT_FOUND;
}
user->permission_level = level;
user->permission_mask = shell_permission_level_to_mask(level);
return SHELL_OK;
}
/**
* @brief 锁定用户账户
* @param ctx Shell上下文
* @param username 用户名
* @return SHELL_OK表示成功
*/
shell_status_t shell_user_lock(shell_context_t *ctx, const char *username)
{
if (!ctx || !username) {
return SHELL_ERR;
}
// 需要管理员权限
if (!ctx->current_user ||
ctx->current_user->permission_level < SHELL_PERMISSION_ADMIN) {
return SHELL_ERR_PERMISSION_DENIED;
}
shell_user_t *user = shell_user_find(ctx, username);
if (!user) {
return SHELL_ERR_USER_NOT_FOUND;
}
user->is_locked = 1;
return SHELL_OK;
}
/**
* @brief 解锁用户账户
* @param ctx Shell上下文
* @param username 用户名
* @return SHELL_OK表示成功
*/
shell_status_t shell_user_unlock(shell_context_t *ctx, const char *username)
{
if (!ctx || !username) {
return SHELL_ERR;
}
// 需要管理员权限
if (!ctx->current_user ||
ctx->current_user->permission_level < SHELL_PERMISSION_ADMIN) {
return SHELL_ERR_PERMISSION_DENIED;
}
shell_user_t *user = shell_user_find(ctx, username);
if (!user) {
return SHELL_ERR_USER_NOT_FOUND;
}
user->is_locked = 0;
user->login_attempts = 0;
return SHELL_OK;
}
/**
* @brief 查找用户
* @param ctx Shell上下文
* @param username 用户名
* @return 用户指针,未找到返回NULL
*/
shell_user_t* shell_user_find(shell_context_t *ctx, const char *username)
{
if (!ctx || !username) {
return NULL;
}
shell_user_t *user = ctx->user_list;
while (user) {
if (strcmp(user->username, username) == 0) {
return user;
}
user = user->next;
}
return NULL;
}
/**
* @brief 用户登录
* @param ctx Shell上下文
* @param username 用户名
* @param password 密码
* @return SHELL_OK表示成功
*/
shell_status_t shell_login(shell_context_t *ctx,
const char *username,
const char *password)
{
if (!ctx || !username || !password) {
return SHELL_ERR;
}
shell_user_t *user = shell_user_find(ctx, username);
if (!user) {
return SHELL_ERR_USER_NOT_FOUND;
}
if (user->is_locked) {
return SHELL_ERR_USER_LOCKED;
}
if (!shell_verify_password(password, user->password)) {
user->login_attempts++;
if (user->login_attempts >= SHELL_MAX_LOGIN_ATTEMPTS) {
user->is_locked = 1;
}
return SHELL_ERR_INVALID_PASSWORD;
}
// 登录成功
ctx->current_user = user;
user->is_logged_in = 1;
user->login_attempts = 0;
ctx->login_state = LOGIN_STATE_LOGGED_IN;
// 更新提示符
shell_prompt_update(ctx);
return SHELL_OK;
}
/**
* @brief 用户登出
* @param ctx Shell上下文
* @return SHELL_OK表示成功
*/
shell_status_t shell_logout(shell_context_t *ctx)
{
if (!ctx) {
return SHELL_ERR;
}
if (ctx->current_user) {
ctx->current_user->is_logged_in = 0;
ctx->current_user = NULL;
}
ctx->login_state = LOGIN_STATE_LOGGED_OUT;
ctx->login_username[0] = '\0';
ctx->login_attempts = 0;
// 更新提示符
shell_prompt_update(ctx);
return SHELL_OK;
}
/**
* @brief 检查是否已登录
* @param ctx Shell上下文
* @return 1表示已登录,0表示未登录
*/
uint8_t shell_is_logged_in(shell_context_t *ctx)
{
return (ctx && ctx->current_user) ? 1 : 0;
}
/**
* @brief 获取当前用户权限级别
* @param ctx Shell上下文
* @return 权限级别,未登录返回SHELL_PERMISSION_NONE
*/
shell_permission_level_t shell_get_current_permission(shell_context_t *ctx)
{
if (!ctx || !ctx->current_user) {
return SHELL_PERMISSION_NONE;
}
return ctx->current_user->permission_level;
}
/**
* @brief 检查用户是否有权限执行命令
* @param ctx Shell上下文
* @param cmd 命令指针
* @return 1表示有权限,0表示无权限
*/
uint8_t shell_check_command_permission(shell_context_t *ctx, shell_command_t *cmd)
{
if (!ctx || !cmd) {
return 0;
}
// 如果不需要登录,则所有命令都可执行
if (!ctx->require_login) {
return 1;
}
// 未登录状态只能执行登录相关命令
if (!ctx->current_user) {
// 允许登录和帮助命令
if (strcmp(cmd->name, "login") == 0 ||
strcmp(cmd->name, "help") == 0) {
return 1;
}
return 0;
}
// 检查命令权限掩码
if (cmd->permission_mask == 0) {
// 权限掩码为0表示所有用户都可执行
return 1;
}
// 检查当前用户权限掩码是否包含命令所需的权限
return (ctx->current_user->permission_mask & cmd->permission_mask) ? 1 : 0;
}
#endif
// ==================== 新增结束 ====================
最后就是命令示例编写:
#include "lsy_shell_core.h"
// 静态命令处理函数示例
static shell_status_t cmd_reboot(int argc, char **argv) {
(void)argc;
(void)argv;
shell_print(&g_lsy_shell, "Rebooting...\r\n");
// 直接调用库函数
NVIC_SystemReset();
return SHELL_OK;
}
static shell_status_t cmd_version(int argc, char **argv) {
(void)argc;
(void)argv;
shell_printf(&g_lsy_shell, "\r\n=== Little Dynamic Shell ===\r\n");
shell_printf(&g_lsy_shell, "Shell Version: %s\r\n",LSY_SHELL_VERSION_STR);
shell_printf(&g_lsy_shell, "Build Date: %s %s\r\n", __DATE__, __TIME__);
return SHELL_OK;
}
static shell_status_t cmd_analysis(int argc, char **argv) {
int i;
shell_printf(&g_lsy_shell, "CMD Name: %s\r\n", argv[0]);
for(i=1;i<argc;i++)
{
shell_printf(&g_lsy_shell, "Para%d: %s\r\n", i, argv[i]);
}
return SHELL_OK;
}
// 静态命令表
shell_command_t static_commands[] = {
{"cmdtest", "Test cmd parameter", cmd_analysis,NULL, NULL, 1},
{"reboot", "Reboot the system", cmd_reboot, NULL, NULL, 1},
{"version", "Show version information", cmd_version, NULL, NULL, 1}
};
// 获取静态命令表
shell_command_t* get_static_commands(size_t *count) {
*count = sizeof(static_commands) / sizeof(static_commands[0]);
return static_commands;
}
动态命令:
#include <stdlib.h>
#include "main.h"
#include "lsy_shell_core.h"
// 示例命令处理函数
static shell_status_t cmd_led_control(int argc, char **argv)
{
uint32_t pinstate = 1;
if (argc != 3) {
shell_print(&g_lsy_shell, "Usage: led <r|g|b> <on|off>\r\n");
return SHELL_ERR_PARAM;
}
if(!strcmp(argv[2], "on"))
{
pinstate = 0;
}else if(!strcmp(argv[2],"off"))
{
pinstate = 1;
}else
{
shell_print(&g_lsy_shell,"cmd para2 ERROR!\n");
return SHELL_ERR_PARAM;
}
if(!strcmp(argv[1], "r"))
{
GPIO_PinWrite(BOARD_LED_RED_GPIO, BOARD_LED_RED_GPIO_PIN,pinstate);
}else if(!strcmp(argv[1],"g"))
{
GPIO_PinWrite(BOARD_LED_GREEN_GPIO, BOARD_LED_GREEN_GPIO_PIN,pinstate);
}else if(!strcmp(argv[1],"b"))
{
GPIO_PinWrite(BOARD_LED_BLUE_GPIO, BOARD_LED_BLUE_GPIO_PIN,pinstate);
}else
{
shell_print(&g_lsy_shell,"cmd para1 ERROR!\n");
return SHELL_ERR_PARAM;
}
shell_printf(&g_lsy_shell, "led %s\r\n",pinstate ? "off":"on");
return SHELL_OK;
}
// 调用下面函数注册动态注册简单命令
shell_register_command(shell_ctx, "led",
"Control LED: led <on|off>",
cmd_led_control, NULL);
用户调用接口:
#include "lsy_shell_core.h"
// 全局Shell上下文
shell_context_t g_lsy_shell;
extern void register_example_commands(shell_context_t *shell_ctx);
extern void register_batch_commands(shell_context_t *shell_ctx);
int lsy_shell_init(shell_io_ops_t * io_ops)
{
// 初始化Shell
shell_init(&g_lsy_shell, io_ops, SHELL_PROMPT);
// 注册静态命令表
size_t static_cmd_count;
shell_command_t *static_cmds = get_static_commands(&static_cmd_count);
shell_register_static_table(&g_lsy_shell, static_cmds, static_cmd_count);
// 注册内存控制命令
memory_view_init(&g_lsy_shell);
// 注册示例命令
register_example_commands(&g_lsy_shell);
return 0;
}
int lsy_shell_input(uint8_t ch)
{
shell_process_char(&g_lsy_shell, ch);
return 0;
}
///////////////////////////////////////////////////////////////////////////////
//
void shell_usart_init(void)
{
#if UART_SHELL != 4
serial_manager_status_t status = kStatus_SerialManager_Error;
g_write_handle = (serial_write_handle_t)&g_write_handle_buff[0];
status = SerialManager_OpenWriteHandle(g_serialHandle, g_write_handle);
assert(kStatus_SerialManager_Success == status);
g_read_handle = (serial_read_handle_t)&g_read_handle_buff[0];
status = SerialManager_OpenReadHandle(g_serialHandle, g_read_handle);
assert(kStatus_SerialManager_Success == status);
#endif
#ifdef UART_SHELL
#if UART_SHELL == 1
userShellInit(); //LETTER_SHELL
#elif UART_SHELL == 2
shell_init(); //NR_MICRO_SHELL
#elif UART_SHELL == 3
// 配置IO操作
shell_io_ops_t io_ops = {
.send = USART_PutStr,
};
lsy_shell_init(&io_ops); //LSY_SHELL
#elif UART_SHELL == 4
//初始化shell
SHELL_Init((shell_handle_t)s_shellHandle, (serial_handle_t)g_serialHandle, "nxp:>");
SHELL_RegisterCommand(s_shellHandle,SHELL_COMMAND(test)); //注册新的shell函数
SHELL_RegisterCommand(s_shellHandle,SHELL_COMMAND(led)); //注册新的shell函数
#endif
#endif
/* 启动非阻塞式读取接收数据 */
}
void shell_usart_loop(void)
{
#if UART_SHELL == 4
SHELL_Task(s_shellHandle);
#else
//阻塞式读取接收数据
serial_manager_status_t status = SerialManager_ReadBlocking(g_read_handle, uart_recv_buff, 1);
if(kStatus_Success == status)
{
if(((g_shell_uart.rx.write_i+1)&0x1ff) != g_shell_uart.rx.read_i)
{
g_shell_uart.rx.buff[g_shell_uart.rx.write_i++] = uart_recv_buff[0] & 0xff;
g_shell_uart.rx.write_i &= 0x1ff;//256Byte
}
}
//=============================================================================================
if(g_shell_uart.rx.read_i != g_shell_uart.rx.write_i)
{
#ifdef UART_SHELL
#if UART_SHELL == 1
shellHandler(&shell, g_shell_uart.rx.buff[g_shell_uart.rx.read_i++]); //letter shell
#elif UART_SHELL == 2
shell(g_shell_uart.rx.buff[g_shell_uart.rx.read_i++]);
#elif UART_SHELL == 3
lsy_shell_input(g_shell_uart.rx.buff[g_shell_uart.rx.read_i++]);
#else
USART_PutChar(g_shell_uart.rx.buff[g_shell_uart.rx.read_i++]);
#endif
#endif
g_shell_uart.rx.read_i &= 0x1ff; //256Byte
}
if(g_shell_uart.tx_cpl == 0)
{
uart_get_data_send();
}
#endif
}
最终实现了一个功能完善的嵌入式命令行外壳(Shell)系统,提供了动态命令管理、终端控制、历史记录、用户权限管理等核心功能。系统采用模块化设计,可灵活配置,适用于嵌入式设备、物联网终端、调试系统等场景。
2.3、Shell核心功能总结
2.3.1. 命令管理模块
静态命令
- 编译时确定的命令表
- 节省内存,执行效率高
- 适用于系统基础命令
动态命令
- 运行时动态注册/注销
- 链表结构存储
- 支持热插拔式命令管理
内置命令
help- 显示帮助信息list- 列出所有命令echo <on|off>- 控制输入回显history [clear]- 历史记录管理
2.3.2. 终端控制模块
按键识别
- 上下左右箭头:光标移动
- Home/End:行首/行尾跳转
- Backspace/Del:字符删除
- ESC序列:完整终端控制
行编辑功能
- 光标位置移动
- 字符插入/删除
- 行内编辑
- 自动换行处理
2.3.3. 历史记录模块
存储管理
- 环形缓冲区设计
- 自动去重(连续重复命令)
- 内存紧凑存储
浏览功能
- 上箭头:上一条历史
- 下箭头:下一条历史
- 命令快速复用
管理功能
history:显示历史列表history clear:清空历史
2.3.4. 用户权限管理模块
用户系统
text
权限级别:
├── 超级用户(root) - 最高权限
├── 管理员(admin) - 管理权限
├── 普通用户(user) - 基本权限
└── 访客(guest) - 受限权限
用户管理命令
login- 用户登录logout- 用户登出passwd- 修改密码users- 查看用户列表(管理员)useradd- 添加用户(管理员)userdel- 删除用户(管理员)userlock- 锁定用户(管理员)userunlock- 解锁用户(管理员)
安全特性
- 密码不回显
- 登录尝试次数限制
- 账户自动锁定
- 权限掩码控制
- 命令级权限检查
三、任务功能展示
下面是运行效果:



四、总结
本次活动通过与AI的多轮对话完成了任务目标,实现了一个基于串口的shell项目代码工程。总体来说还是很顺利的,核心代码都是AI给出,然后通过调试和与AI的提问进行优化和解决出现的所有问题。
最后感谢电子森林与得捷电子联合推出的Funpack系列活动,给了我们动手实验的机会。学习了更多的技能。