This commit is contained in:
2026-04-11 14:18:59 +08:00
parent 0f34f8e094
commit c7fee12291
12 changed files with 920 additions and 233 deletions

View File

@@ -1,11 +1,11 @@
#include <cleonos_rust_bridge.h>
#include <cleonos_syscall.h>
#define SHELL_CMD_MAX 24ULL
#define SHELL_ARG_MAX 160ULL
#define SHELL_LINE_MAX 320ULL
#define SHELL_CAT_MAX 224ULL
#define SHELL_SCRIPT_MAX 1024ULL
#define SHELL_CMD_MAX 32ULL
#define SHELL_ARG_MAX 160ULL
#define SHELL_LINE_MAX 192ULL
#define SHELL_CAT_MAX 512ULL
#define SHELL_SCRIPT_MAX 1024ULL
#define SHELL_CLEAR_LINES 56ULL
static u64 shell_strlen(const char *str) {
u64 len = 0ULL;
@@ -42,66 +42,52 @@ static int shell_is_space(char ch) {
return (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n') ? 1 : 0;
}
static void shell_append_char(char *dst, u64 *cursor, u64 dst_size, char ch) {
if (*cursor + 1ULL >= dst_size) {
static int shell_is_printable(char ch) {
return (ch >= 32 && ch <= 126) ? 1 : 0;
}
static void shell_copy(char *dst, u64 dst_size, const char *src) {
u64 i = 0ULL;
if (dst == (char *)0 || src == (const char *)0 || dst_size == 0ULL) {
return;
}
dst[*cursor] = ch;
(*cursor)++;
while (src[i] != '\0' && i + 1ULL < dst_size) {
dst[i] = src[i];
i++;
}
dst[i] = '\0';
}
static void shell_append_text(char *dst, u64 *cursor, u64 dst_size, const char *text) {
u64 i = 0ULL;
static void shell_write(const char *text) {
u64 len;
if (text == (const char *)0) {
return;
}
while (text[i] != '\0') {
shell_append_char(dst, cursor, dst_size, text[i]);
i++;
len = shell_strlen(text);
if (len == 0ULL) {
return;
}
(void)cleonos_sys_tty_write(text, len);
}
static void shell_append_hex_u64(char *dst, u64 *cursor, u64 dst_size, u64 value) {
int nibble;
shell_append_text(dst, cursor, dst_size, "0X");
for (nibble = 15; nibble >= 0; nibble--) {
u64 shift = (u64)nibble * 4ULL;
u64 current = (value >> shift) & 0x0FULL;
char out = (current < 10ULL) ? (char)('0' + (char)current) : (char)('A' + (char)(current - 10ULL));
shell_append_char(dst, cursor, dst_size, out);
}
static void shell_write_char(char ch) {
(void)cleonos_sys_tty_write_char(ch);
}
static void shell_log_text(const char *text) {
u64 len = cleonos_rust_guarded_len((const unsigned char *)text, (usize)511U);
cleonos_sys_log_write(text, len);
static void shell_writeln(const char *text) {
shell_write(text);
shell_write_char('\n');
}
static void shell_log_prefixed(const char *prefix, const char *value) {
char line[SHELL_LINE_MAX];
u64 cursor = 0ULL;
shell_append_text(line, &cursor, (u64)sizeof(line), prefix);
shell_append_text(line, &cursor, (u64)sizeof(line), value);
line[cursor] = '\0';
shell_log_text(line);
}
static void shell_log_hex_prefixed(const char *prefix, u64 value) {
char line[SHELL_LINE_MAX];
u64 cursor = 0ULL;
shell_append_text(line, &cursor, (u64)sizeof(line), prefix);
shell_append_hex_u64(line, &cursor, (u64)sizeof(line), value);
line[cursor] = '\0';
shell_log_text(line);
static void shell_prompt(void) {
shell_write("cleonos(user)> ");
}
static void shell_trim_line(char *line) {
@@ -142,6 +128,9 @@ static void shell_parse_line(const char *line, char *out_cmd, u64 cmd_size, char
return;
}
out_cmd[0] = '\0';
out_arg[0] = '\0';
while (line[i] != '\0' && shell_is_space(line[i]) != 0) {
i++;
}
@@ -169,8 +158,98 @@ static void shell_parse_line(const char *line, char *out_cmd, u64 cmd_size, char
out_arg[arg_pos] = '\0';
}
static int shell_has_suffix(const char *name, const char *suffix) {
u64 name_len;
u64 suffix_len;
u64 i;
if (name == (const char *)0 || suffix == (const char *)0) {
return 0;
}
name_len = shell_strlen(name);
suffix_len = shell_strlen(suffix);
if (suffix_len > name_len) {
return 0;
}
for (i = 0ULL; i < suffix_len; i++) {
if (name[name_len - suffix_len + i] != suffix[i]) {
return 0;
}
}
return 1;
}
static int shell_resolve_exec_path(const char *arg, char *out_path, u64 out_size) {
u64 cursor = 0ULL;
u64 i;
if (arg == (const char *)0 || out_path == (char *)0 || out_size == 0ULL) {
return 0;
}
if (arg[0] == '\0') {
return 0;
}
out_path[0] = '\0';
if (arg[0] == '/') {
shell_copy(out_path, out_size, arg);
return 1;
}
{
static const char prefix[] = "/shell/";
u64 prefix_len = (u64)(sizeof(prefix) - 1U);
if (prefix_len + 1ULL >= out_size) {
return 0;
}
for (i = 0ULL; i < prefix_len; i++) {
out_path[cursor++] = prefix[i];
}
}
i = 0ULL;
while (arg[i] != '\0' && cursor + 1ULL < out_size) {
out_path[cursor++] = arg[i++];
}
out_path[cursor] = '\0';
if (arg[i] != '\0') {
return 0;
}
if (shell_has_suffix(out_path, ".elf") == 0) {
static const char suffix[] = ".elf";
for (i = 0ULL; suffix[i] != '\0'; i++) {
if (cursor + 1ULL >= out_size) {
return 0;
}
out_path[cursor++] = suffix[i];
}
out_path[cursor] = '\0';
}
return 1;
}
static void shell_cmd_help(void) {
shell_log_text("[USER][SHELL] commands: help ls <dir> cat <file> run <elf> stats");
shell_writeln("commands:");
shell_writeln(" help");
shell_writeln(" ls [dir]");
shell_writeln(" cat <file>");
shell_writeln(" exec <path|name>");
shell_writeln(" clear");
shell_writeln(" stats");
}
static void shell_cmd_ls(const char *arg) {
@@ -185,12 +264,14 @@ static void shell_cmd_ls(const char *arg) {
count = cleonos_sys_fs_child_count(path);
if (count == (u64)-1) {
shell_log_text("[USER][SHELL] ls failed");
shell_writeln("ls: directory not found");
return;
}
shell_log_prefixed("[USER][SHELL] ls ", path);
shell_log_hex_prefixed("[USER][SHELL] ls count: ", count);
if (count == 0ULL) {
shell_writeln("(empty)");
return;
}
for (i = 0ULL; i < count; i++) {
char name[CLEONOS_FS_NAME_MAX];
@@ -201,7 +282,7 @@ static void shell_cmd_ls(const char *arg) {
continue;
}
shell_log_prefixed("[USER][SHELL] - ", name);
shell_writeln(name);
}
}
@@ -210,14 +291,14 @@ static void shell_cmd_cat(const char *arg) {
u64 got;
if (arg == (const char *)0 || arg[0] == '\0') {
shell_log_text("[USER][SHELL] cat requires path");
shell_writeln("cat: file path required");
return;
}
got = cleonos_sys_fs_read(arg, cat_buf, SHELL_CAT_MAX);
if (got == 0ULL) {
shell_log_text("[USER][SHELL] cat failed");
shell_writeln("cat: file not found");
return;
}
@@ -226,43 +307,38 @@ static void shell_cmd_cat(const char *arg) {
}
cat_buf[got] = '\0';
shell_log_prefixed("[USER][SHELL] cat ", arg);
shell_log_text(cat_buf);
shell_writeln(cat_buf);
}
static void shell_cmd_run(const char *arg) {
static void shell_cmd_exec(const char *arg) {
char path[SHELL_LINE_MAX];
u64 status;
if (arg == (const char *)0 || arg[0] == '\0') {
shell_log_text("[USER][SHELL] run requires path");
if (shell_resolve_exec_path(arg, path, (u64)sizeof(path)) == 0) {
shell_writeln("exec: invalid target");
return;
}
status = cleonos_sys_exec_path(arg);
status = cleonos_sys_exec_path(path);
if (status == 0ULL) {
shell_log_prefixed("[USER][SHELL] run ok ", arg);
shell_writeln("exec: request accepted");
} else {
shell_log_prefixed("[USER][SHELL] run failed ", arg);
shell_writeln("exec: request failed");
}
}
static void shell_cmd_clear(void) {
u64 i;
for (i = 0ULL; i < SHELL_CLEAR_LINES; i++) {
shell_write_char('\n');
}
}
static void shell_cmd_stats(void) {
shell_log_hex_prefixed("[USER][SHELL] fs nodes: ", cleonos_sys_fs_node_count());
shell_log_hex_prefixed("[USER][SHELL] task count: ", cleonos_sys_task_count());
shell_log_hex_prefixed("[USER][SHELL] service count: ", cleonos_sys_service_count());
shell_log_hex_prefixed("[USER][SHELL] service ready: ", cleonos_sys_service_ready_count());
shell_log_hex_prefixed("[USER][SHELL] context switches: ", cleonos_sys_context_switches());
shell_log_hex_prefixed("[USER][SHELL] kelf count: ", cleonos_sys_kelf_count());
shell_log_hex_prefixed("[USER][SHELL] kelf runs: ", cleonos_sys_kelf_runs());
shell_log_hex_prefixed("[USER][SHELL] user shell ready: ", cleonos_sys_user_shell_ready());
shell_log_hex_prefixed("[USER][SHELL] user exec requested: ", cleonos_sys_user_exec_requested());
shell_log_hex_prefixed("[USER][SHELL] user launch tries: ", cleonos_sys_user_launch_tries());
shell_log_hex_prefixed("[USER][SHELL] user launch ok: ", cleonos_sys_user_launch_ok());
shell_log_hex_prefixed("[USER][SHELL] user launch fail: ", cleonos_sys_user_launch_fail());
shell_log_hex_prefixed("[USER][SHELL] exec requests: ", cleonos_sys_exec_request_count());
shell_log_hex_prefixed("[USER][SHELL] exec success: ", cleonos_sys_exec_success_count());
(void)cleonos_sys_task_count();
shell_writeln("stats: use kernel log channel for full counters");
}
static void shell_execute_line(const char *line) {
@@ -271,9 +347,6 @@ static void shell_execute_line(const char *line) {
char line_buf[SHELL_LINE_MAX];
u64 i = 0ULL;
cmd[0] = '\0';
arg[0] = '\0';
if (line == (const char *)0) {
return;
}
@@ -290,7 +363,6 @@ static void shell_execute_line(const char *line) {
return;
}
shell_log_prefixed("[USER][SHELL]$ ", line_buf);
shell_parse_line(line_buf, cmd, (u64)sizeof(cmd), arg, (u64)sizeof(arg));
if (shell_streq(cmd, "help") != 0) {
@@ -308,8 +380,13 @@ static void shell_execute_line(const char *line) {
return;
}
if (shell_streq(cmd, "run") != 0) {
shell_cmd_run(arg);
if (shell_streq(cmd, "exec") != 0 || shell_streq(cmd, "run") != 0) {
shell_cmd_exec(arg);
return;
}
if (shell_streq(cmd, "clear") != 0) {
shell_cmd_clear();
return;
}
@@ -318,24 +395,7 @@ static void shell_execute_line(const char *line) {
return;
}
shell_log_prefixed("[USER][SHELL] unknown command: ", cmd);
}
static void shell_run_default_script(void) {
static const char *script[] = {
"help",
"stats",
"ls /",
"ls /system",
"cat /README.txt",
"run /system/elfrunner.elf",
"run /system/memc.elf"
};
u64 i;
for (i = 0ULL; i < (u64)(sizeof(script) / sizeof(script[0])); i++) {
shell_execute_line(script[i]);
}
shell_writeln("unknown command; type 'help'");
}
static int shell_run_script_file(const char *path) {
@@ -360,7 +420,6 @@ static int shell_run_script_file(const char *path) {
}
script[got] = '\0';
shell_log_prefixed("[USER][SHELL] script ", path);
for (i = 0ULL; i <= got; i++) {
char ch = script[i];
@@ -384,14 +443,78 @@ static int shell_run_script_file(const char *path) {
return 1;
}
int cleonos_app_main(void) {
shell_log_text("[USER][SHELL] shell.elf command framework online");
static char shell_read_char_blocking(void) {
for (;;) {
u64 ch = cleonos_sys_kbd_get_char();
if (shell_run_script_file("/shell/init.cmd") == 0) {
shell_log_text("[USER][SHELL] /shell/init.cmd missing, using default script");
shell_run_default_script();
if (ch != (u64)-1) {
return (char)(ch & 0xFFULL);
}
__asm__ volatile("pause");
}
}
static void shell_read_line(char *out_line, u64 out_size) {
u64 cursor = 0ULL;
if (out_line == (char *)0 || out_size == 0ULL) {
return;
}
shell_log_text("[USER][SHELL] script done");
return 0;
out_line[0] = '\0';
for (;;) {
char ch = shell_read_char_blocking();
if (ch == '\r') {
continue;
}
if (ch == '\n') {
shell_write_char('\n');
break;
}
if (ch == '\b' || ch == 127) {
if (cursor > 0ULL) {
cursor--;
out_line[cursor] = '\0';
shell_write_char('\b');
}
continue;
}
if (ch == '\t') {
ch = ' ';
}
if (shell_is_printable(ch) == 0) {
continue;
}
if (cursor + 1ULL >= out_size) {
continue;
}
out_line[cursor++] = ch;
out_line[cursor] = '\0';
shell_write_char(ch);
}
}
int cleonos_app_main(void) {
char line[SHELL_LINE_MAX];
shell_writeln("[USER][SHELL] interactive framework online");
if (shell_run_script_file("/shell/init.cmd") == 0) {
shell_writeln("[USER][SHELL] /shell/init.cmd missing");
}
for (;;) {
shell_prompt();
shell_read_line(line, (u64)sizeof(line));
shell_execute_line(line);
}
}