mirror of
https://github.com/Leonmmcoset/cleonos.git
synced 2026-04-21 18:44:01 +00:00
重写Shell+增加更多Syscall
This commit is contained in:
@@ -364,12 +364,34 @@ foreach(SRC IN LISTS USER_APP_MAIN_SOURCES)
|
||||
list(APPEND USER_APP_NAMES "${_app_name}")
|
||||
|
||||
add_user_c_object("${SRC}" _user_obj)
|
||||
|
||||
set(_app_specific_objs)
|
||||
file(GLOB _app_specific_abs CONFIGURE_DEPENDS
|
||||
"${CMAKE_SOURCE_DIR}/cleonos/c/apps/${_app_name}_*.c"
|
||||
"${CMAKE_SOURCE_DIR}/cleonos/c/apps/${_app_name}/*.c"
|
||||
)
|
||||
foreach(_extra_abs IN LISTS _app_specific_abs)
|
||||
file(RELATIVE_PATH _extra_rel "${CMAKE_SOURCE_DIR}" "${_extra_abs}")
|
||||
|
||||
if(_extra_rel STREQUAL "${SRC}")
|
||||
continue()
|
||||
endif()
|
||||
|
||||
if(_extra_rel MATCHES "_kmain\\.c$")
|
||||
continue()
|
||||
endif()
|
||||
|
||||
add_user_c_object("${_extra_rel}" _extra_obj)
|
||||
list(APPEND _app_specific_objs "${_extra_obj}")
|
||||
endforeach()
|
||||
list(REMOVE_DUPLICATES _app_specific_objs)
|
||||
|
||||
set(_app_out "${USER_APP_DIR}/${_app_name}.elf")
|
||||
add_custom_command(
|
||||
OUTPUT "${_app_out}"
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory "${USER_APP_DIR}"
|
||||
COMMAND ${USER_LD} ${USER_LDFLAGS} -o "${_app_out}" ${USER_COMMON_OBJECTS} "${_user_obj}" "${USER_RUST_LIB}"
|
||||
DEPENDS ${USER_COMMON_OBJECTS} "${_user_obj}" "${USER_RUST_LIB}" "${USER_LINKER_SCRIPT}"
|
||||
COMMAND ${USER_LD} ${USER_LDFLAGS} -o "${_app_out}" ${USER_COMMON_OBJECTS} "${_user_obj}" ${_app_specific_objs} "${USER_RUST_LIB}"
|
||||
DEPENDS ${USER_COMMON_OBJECTS} "${_user_obj}" ${_app_specific_objs} "${USER_RUST_LIB}" "${USER_LINKER_SCRIPT}"
|
||||
VERBATIM
|
||||
)
|
||||
|
||||
|
||||
800
cleonos/c/apps/shell/shell_cmd.c
Normal file
800
cleonos/c/apps/shell/shell_cmd.c
Normal file
@@ -0,0 +1,800 @@
|
||||
#include "shell_internal.h"
|
||||
|
||||
#define USH_DMESG_DEFAULT 64ULL
|
||||
#define USH_DMESG_LINE_MAX 256ULL
|
||||
#define USH_COPY_MAX 65536U
|
||||
|
||||
static int ush_path_is_under_temp(const char *path) {
|
||||
if (path == (const char *)0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (path[0] != '/' || path[1] != 't' || path[2] != 'e' || path[3] != 'm' || path[4] != 'p') {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (path[5] == '\0' || path[5] == '/') ? 1 : 0;
|
||||
}
|
||||
|
||||
static int ush_split_first_and_rest(const char *arg, char *out_first, u64 out_first_size, const char **out_rest) {
|
||||
u64 i = 0ULL;
|
||||
u64 p = 0ULL;
|
||||
|
||||
if (arg == (const char *)0 || out_first == (char *)0 || out_first_size == 0ULL || out_rest == (const char **)0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
out_first[0] = '\0';
|
||||
*out_rest = "";
|
||||
|
||||
while (arg[i] != '\0' && ush_is_space(arg[i]) != 0) {
|
||||
i++;
|
||||
}
|
||||
|
||||
if (arg[i] == '\0') {
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (arg[i] != '\0' && ush_is_space(arg[i]) == 0) {
|
||||
if (p + 1ULL < out_first_size) {
|
||||
out_first[p++] = arg[i];
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
out_first[p] = '\0';
|
||||
|
||||
while (arg[i] != '\0' && ush_is_space(arg[i]) != 0) {
|
||||
i++;
|
||||
}
|
||||
|
||||
*out_rest = &arg[i];
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ush_split_two_args(const char *arg,
|
||||
char *out_first,
|
||||
u64 out_first_size,
|
||||
char *out_second,
|
||||
u64 out_second_size) {
|
||||
u64 i = 0ULL;
|
||||
u64 p = 0ULL;
|
||||
|
||||
if (arg == (const char *)0 ||
|
||||
out_first == (char *)0 || out_first_size == 0ULL ||
|
||||
out_second == (char *)0 || out_second_size == 0ULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
out_first[0] = '\0';
|
||||
out_second[0] = '\0';
|
||||
|
||||
while (arg[i] != '\0' && ush_is_space(arg[i]) != 0) {
|
||||
i++;
|
||||
}
|
||||
|
||||
if (arg[i] == '\0') {
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (arg[i] != '\0' && ush_is_space(arg[i]) == 0) {
|
||||
if (p + 1ULL < out_first_size) {
|
||||
out_first[p++] = arg[i];
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
out_first[p] = '\0';
|
||||
|
||||
while (arg[i] != '\0' && ush_is_space(arg[i]) != 0) {
|
||||
i++;
|
||||
}
|
||||
|
||||
if (arg[i] == '\0') {
|
||||
return 0;
|
||||
}
|
||||
|
||||
p = 0ULL;
|
||||
while (arg[i] != '\0' && ush_is_space(arg[i]) == 0) {
|
||||
if (p + 1ULL < out_second_size) {
|
||||
out_second[p++] = arg[i];
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
out_second[p] = '\0';
|
||||
|
||||
return (out_first[0] != '\0' && out_second[0] != '\0') ? 1 : 0;
|
||||
}
|
||||
|
||||
static int ush_cmd_help(void) {
|
||||
ush_writeln("commands:");
|
||||
ush_writeln(" help");
|
||||
ush_writeln(" ls [dir]");
|
||||
ush_writeln(" cat <file>");
|
||||
ush_writeln(" pwd");
|
||||
ush_writeln(" cd [dir]");
|
||||
ush_writeln(" exec|run <path|name>");
|
||||
ush_writeln(" clear");
|
||||
ush_writeln(" memstat / fsstat / taskstat / userstat / shstat / stats");
|
||||
ush_writeln(" tty [index]");
|
||||
ush_writeln(" dmesg [n]");
|
||||
ush_writeln(" kbdstat");
|
||||
ush_writeln(" mkdir <dir> (/temp only)");
|
||||
ush_writeln(" touch <file> (/temp only)");
|
||||
ush_writeln(" write <file> <text> (/temp only)");
|
||||
ush_writeln(" append <file> <text> (/temp only)");
|
||||
ush_writeln(" cp <src> <dst> (dst /temp only)");
|
||||
ush_writeln(" mv <src> <dst> (/temp only)");
|
||||
ush_writeln(" rm <path> (/temp only)");
|
||||
ush_writeln(" rusttest / panic / elfloader (kernel shell only)");
|
||||
ush_writeln("edit keys: Left/Right, Home/End, Up/Down history");
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ush_cmd_ls(const ush_state *sh, const char *arg) {
|
||||
const char *target = arg;
|
||||
char path[USH_PATH_MAX];
|
||||
u64 count;
|
||||
u64 i;
|
||||
|
||||
if (target == (const char *)0 || target[0] == '\0') {
|
||||
target = ".";
|
||||
}
|
||||
|
||||
if (ush_resolve_path(sh, target, path, (u64)sizeof(path)) == 0) {
|
||||
ush_writeln("ls: invalid path");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (cleonos_sys_fs_stat_type(path) != 2ULL) {
|
||||
ush_writeln("ls: directory not found");
|
||||
return 0;
|
||||
}
|
||||
|
||||
count = cleonos_sys_fs_child_count(path);
|
||||
|
||||
if (count == 0ULL) {
|
||||
ush_writeln("(empty)");
|
||||
return 1;
|
||||
}
|
||||
|
||||
for (i = 0ULL; i < count; i++) {
|
||||
char name[CLEONOS_FS_NAME_MAX];
|
||||
name[0] = '\0';
|
||||
|
||||
if (cleonos_sys_fs_get_child_name(path, i, name) != 0ULL) {
|
||||
ush_writeln(name);
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ush_cmd_cat(const ush_state *sh, const char *arg) {
|
||||
char path[USH_PATH_MAX];
|
||||
char buf[USH_CAT_MAX + 1ULL];
|
||||
u64 size;
|
||||
u64 req;
|
||||
u64 got;
|
||||
|
||||
if (arg == (const char *)0 || arg[0] == '\0') {
|
||||
ush_writeln("cat: file path required");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ush_resolve_path(sh, arg, path, (u64)sizeof(path)) == 0) {
|
||||
ush_writeln("cat: invalid path");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (cleonos_sys_fs_stat_type(path) != 1ULL) {
|
||||
ush_writeln("cat: file not found");
|
||||
return 0;
|
||||
}
|
||||
|
||||
size = cleonos_sys_fs_stat_size(path);
|
||||
|
||||
if (size == (u64)-1) {
|
||||
ush_writeln("cat: failed to stat file");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (size == 0ULL) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
req = (size < USH_CAT_MAX) ? size : USH_CAT_MAX;
|
||||
got = cleonos_sys_fs_read(path, buf, req);
|
||||
|
||||
if (got == 0ULL) {
|
||||
ush_writeln("cat: read failed");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (got > USH_CAT_MAX) {
|
||||
got = USH_CAT_MAX;
|
||||
}
|
||||
|
||||
buf[got] = '\0';
|
||||
ush_writeln(buf);
|
||||
|
||||
if (size > got) {
|
||||
ush_writeln("[cat] output truncated");
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ush_cmd_pwd(const ush_state *sh) {
|
||||
ush_writeln(sh->cwd);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ush_cmd_cd(ush_state *sh, const char *arg) {
|
||||
const char *target = arg;
|
||||
char path[USH_PATH_MAX];
|
||||
|
||||
if (target == (const char *)0 || target[0] == '\0') {
|
||||
target = "/";
|
||||
}
|
||||
|
||||
if (ush_resolve_path(sh, target, path, (u64)sizeof(path)) == 0) {
|
||||
ush_writeln("cd: invalid path");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (cleonos_sys_fs_stat_type(path) != 2ULL) {
|
||||
ush_writeln("cd: directory not found");
|
||||
return 0;
|
||||
}
|
||||
|
||||
ush_copy(sh->cwd, (u64)sizeof(sh->cwd), path);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ush_cmd_exec(const ush_state *sh, const char *arg) {
|
||||
char path[USH_PATH_MAX];
|
||||
u64 status;
|
||||
|
||||
if (ush_resolve_exec_path(sh, arg, path, (u64)sizeof(path)) == 0) {
|
||||
ush_writeln("exec: invalid target");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ush_path_is_under_system(path) != 0) {
|
||||
ush_writeln("exec: /system/*.elf is kernel-mode (KELF), not user-exec");
|
||||
return 0;
|
||||
}
|
||||
|
||||
status = cleonos_sys_exec_path(path);
|
||||
|
||||
if (status == (u64)-1) {
|
||||
ush_writeln("exec: request failed");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (status == 0ULL) {
|
||||
ush_writeln("exec: request accepted");
|
||||
return 1;
|
||||
}
|
||||
|
||||
ush_writeln("exec: returned non-zero status");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ush_cmd_clear(void) {
|
||||
u64 i;
|
||||
|
||||
for (i = 0ULL; i < USH_CLEAR_LINES; i++) {
|
||||
ush_write_char('\n');
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ush_cmd_kbdstat(void) {
|
||||
ush_writeln("kbdstat:");
|
||||
ush_print_kv_hex(" BUFFERED", cleonos_sys_kbd_buffered());
|
||||
ush_print_kv_hex(" PUSHED", cleonos_sys_kbd_pushed());
|
||||
ush_print_kv_hex(" POPPED", cleonos_sys_kbd_popped());
|
||||
ush_print_kv_hex(" DROPPED", cleonos_sys_kbd_dropped());
|
||||
ush_print_kv_hex(" HOTKEY_SWITCHES", cleonos_sys_kbd_hotkey_switches());
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ush_cmd_memstat(void) {
|
||||
ush_writeln("memstat (user ABI limited):");
|
||||
ush_print_kv_hex(" SERVICE_COUNT", cleonos_sys_service_count());
|
||||
ush_print_kv_hex(" SERVICE_READY_COUNT", cleonos_sys_service_ready_count());
|
||||
ush_print_kv_hex(" KELF_COUNT", cleonos_sys_kelf_count());
|
||||
ush_print_kv_hex(" KELF_RUNS", cleonos_sys_kelf_runs());
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ush_cmd_fsstat(void) {
|
||||
ush_writeln("fsstat:");
|
||||
ush_print_kv_hex(" NODE_COUNT", cleonos_sys_fs_node_count());
|
||||
ush_print_kv_hex(" ROOT_CHILDREN", cleonos_sys_fs_child_count("/"));
|
||||
ush_print_kv_hex(" SYSTEM_CHILDREN", cleonos_sys_fs_child_count("/system"));
|
||||
ush_print_kv_hex(" SHELL_CHILDREN", cleonos_sys_fs_child_count("/shell"));
|
||||
ush_print_kv_hex(" TEMP_CHILDREN", cleonos_sys_fs_child_count("/temp"));
|
||||
ush_print_kv_hex(" DRIVER_CHILDREN", cleonos_sys_fs_child_count("/driver"));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ush_cmd_taskstat(void) {
|
||||
ush_writeln("taskstat:");
|
||||
ush_print_kv_hex(" TASK_COUNT", cleonos_sys_task_count());
|
||||
ush_print_kv_hex(" CURRENT_TASK", cleonos_syscall(CLEONOS_SYSCALL_CUR_TASK, 0ULL, 0ULL, 0ULL));
|
||||
ush_print_kv_hex(" TIMER_TICKS", cleonos_sys_timer_ticks());
|
||||
ush_print_kv_hex(" CONTEXT_SWITCHES", cleonos_sys_context_switches());
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ush_cmd_userstat(void) {
|
||||
ush_writeln("userstat:");
|
||||
ush_print_kv_hex(" USER_SHELL_READY", cleonos_sys_user_shell_ready());
|
||||
ush_print_kv_hex(" USER_EXEC_REQUESTED", cleonos_sys_user_exec_requested());
|
||||
ush_print_kv_hex(" USER_LAUNCH_TRIES", cleonos_sys_user_launch_tries());
|
||||
ush_print_kv_hex(" USER_LAUNCH_OK", cleonos_sys_user_launch_ok());
|
||||
ush_print_kv_hex(" USER_LAUNCH_FAIL", cleonos_sys_user_launch_fail());
|
||||
ush_print_kv_hex(" EXEC_REQUESTS", cleonos_sys_exec_request_count());
|
||||
ush_print_kv_hex(" EXEC_SUCCESS", cleonos_sys_exec_success_count());
|
||||
ush_print_kv_hex(" TTY_COUNT", cleonos_sys_tty_count());
|
||||
ush_print_kv_hex(" TTY_ACTIVE", cleonos_sys_tty_active());
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ush_cmd_shstat(const ush_state *sh) {
|
||||
ush_writeln("shstat:");
|
||||
ush_print_kv_hex(" CMD_TOTAL", sh->cmd_total);
|
||||
ush_print_kv_hex(" CMD_OK", sh->cmd_ok);
|
||||
ush_print_kv_hex(" CMD_FAIL", sh->cmd_fail);
|
||||
ush_print_kv_hex(" CMD_UNKNOWN", sh->cmd_unknown);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ush_cmd_tty(const char *arg) {
|
||||
u64 tty_count = cleonos_sys_tty_count();
|
||||
u64 active = cleonos_sys_tty_active();
|
||||
|
||||
if (arg == (const char *)0 || arg[0] == '\0') {
|
||||
ush_print_kv_hex("TTY_COUNT", tty_count);
|
||||
ush_print_kv_hex("TTY_ACTIVE", active);
|
||||
return 1;
|
||||
}
|
||||
|
||||
{
|
||||
u64 idx;
|
||||
|
||||
if (ush_parse_u64_dec(arg, &idx) == 0) {
|
||||
ush_writeln("tty: usage tty [index]");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (idx >= tty_count) {
|
||||
ush_writeln("tty: index out of range");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (cleonos_sys_tty_switch(idx) == (u64)-1) {
|
||||
ush_writeln("tty: switch failed");
|
||||
return 0;
|
||||
}
|
||||
|
||||
ush_writeln("tty: switched");
|
||||
ush_print_kv_hex("TTY_ACTIVE", cleonos_sys_tty_active());
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
static int ush_cmd_dmesg(const char *arg) {
|
||||
u64 total = cleonos_sys_log_journal_count();
|
||||
u64 limit = USH_DMESG_DEFAULT;
|
||||
u64 start;
|
||||
u64 i;
|
||||
|
||||
if (arg != (const char *)0 && arg[0] != '\0') {
|
||||
if (ush_parse_u64_dec(arg, &limit) == 0 || limit == 0ULL) {
|
||||
ush_writeln("dmesg: usage dmesg [positive_count]");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (total == 0ULL) {
|
||||
ush_writeln("(journal empty)");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (limit > total) {
|
||||
limit = total;
|
||||
}
|
||||
|
||||
start = total - limit;
|
||||
|
||||
for (i = start; i < total; i++) {
|
||||
char line[USH_DMESG_LINE_MAX];
|
||||
|
||||
if (cleonos_sys_log_journal_read(i, line, (u64)sizeof(line)) != 0ULL) {
|
||||
ush_writeln(line);
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ush_cmd_mkdir(const ush_state *sh, const char *arg) {
|
||||
char path[USH_PATH_MAX];
|
||||
|
||||
if (arg == (const char *)0 || arg[0] == '\0') {
|
||||
ush_writeln("mkdir: directory path required");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ush_resolve_path(sh, arg, path, (u64)sizeof(path)) == 0) {
|
||||
ush_writeln("mkdir: invalid path");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ush_path_is_under_temp(path) == 0) {
|
||||
ush_writeln("mkdir: target must be under /temp");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (cleonos_sys_fs_mkdir(path) == 0ULL) {
|
||||
ush_writeln("mkdir: failed");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ush_cmd_touch(const ush_state *sh, const char *arg) {
|
||||
static const char empty_data[1] = {'\0'};
|
||||
char path[USH_PATH_MAX];
|
||||
|
||||
if (arg == (const char *)0 || arg[0] == '\0') {
|
||||
ush_writeln("touch: file path required");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ush_resolve_path(sh, arg, path, (u64)sizeof(path)) == 0) {
|
||||
ush_writeln("touch: invalid path");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ush_path_is_under_temp(path) == 0) {
|
||||
ush_writeln("touch: target must be under /temp");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (cleonos_sys_fs_write(path, empty_data, 0ULL) == 0ULL) {
|
||||
ush_writeln("touch: failed");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ush_cmd_write(const ush_state *sh, const char *arg) {
|
||||
char path_arg[USH_PATH_MAX];
|
||||
char abs_path[USH_PATH_MAX];
|
||||
const char *payload;
|
||||
u64 payload_len;
|
||||
|
||||
if (arg == (const char *)0 || arg[0] == '\0') {
|
||||
ush_writeln("write: usage write <file> <text>");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ush_split_first_and_rest(arg, path_arg, (u64)sizeof(path_arg), &payload) == 0) {
|
||||
ush_writeln("write: usage write <file> <text>");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ush_resolve_path(sh, path_arg, abs_path, (u64)sizeof(abs_path)) == 0) {
|
||||
ush_writeln("write: invalid path");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ush_path_is_under_temp(abs_path) == 0) {
|
||||
ush_writeln("write: target must be under /temp");
|
||||
return 0;
|
||||
}
|
||||
|
||||
payload_len = ush_strlen(payload);
|
||||
|
||||
if (cleonos_sys_fs_write(abs_path, payload, payload_len) == 0ULL) {
|
||||
ush_writeln("write: failed");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ush_cmd_append(const ush_state *sh, const char *arg) {
|
||||
char path_arg[USH_PATH_MAX];
|
||||
char abs_path[USH_PATH_MAX];
|
||||
const char *payload;
|
||||
u64 payload_len;
|
||||
|
||||
if (arg == (const char *)0 || arg[0] == '\0') {
|
||||
ush_writeln("append: usage append <file> <text>");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ush_split_first_and_rest(arg, path_arg, (u64)sizeof(path_arg), &payload) == 0) {
|
||||
ush_writeln("append: usage append <file> <text>");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ush_resolve_path(sh, path_arg, abs_path, (u64)sizeof(abs_path)) == 0) {
|
||||
ush_writeln("append: invalid path");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ush_path_is_under_temp(abs_path) == 0) {
|
||||
ush_writeln("append: target must be under /temp");
|
||||
return 0;
|
||||
}
|
||||
|
||||
payload_len = ush_strlen(payload);
|
||||
|
||||
if (cleonos_sys_fs_append(abs_path, payload, payload_len) == 0ULL) {
|
||||
ush_writeln("append: failed");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ush_copy_file(const char *src_path, const char *dst_path) {
|
||||
static char copy_buf[USH_COPY_MAX];
|
||||
u64 src_type;
|
||||
u64 src_size;
|
||||
u64 got;
|
||||
|
||||
src_type = cleonos_sys_fs_stat_type(src_path);
|
||||
|
||||
if (src_type != 1ULL) {
|
||||
ush_writeln("cp: source file not found");
|
||||
return 0;
|
||||
}
|
||||
|
||||
src_size = cleonos_sys_fs_stat_size(src_path);
|
||||
|
||||
if (src_size == (u64)-1) {
|
||||
ush_writeln("cp: failed to stat source");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (src_size > (u64)USH_COPY_MAX) {
|
||||
ush_writeln("cp: source too large for user shell buffer");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (src_size == 0ULL) {
|
||||
got = 0ULL;
|
||||
} else {
|
||||
got = cleonos_sys_fs_read(src_path, copy_buf, src_size);
|
||||
|
||||
if (got == 0ULL || got != src_size) {
|
||||
ush_writeln("cp: failed to read source");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (cleonos_sys_fs_write(dst_path, copy_buf, got) == 0ULL) {
|
||||
ush_writeln("cp: failed to write destination");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ush_cmd_cp(const ush_state *sh, const char *arg) {
|
||||
char src_arg[USH_PATH_MAX];
|
||||
char dst_arg[USH_PATH_MAX];
|
||||
char src_path[USH_PATH_MAX];
|
||||
char dst_path[USH_PATH_MAX];
|
||||
|
||||
if (arg == (const char *)0 || arg[0] == '\0') {
|
||||
ush_writeln("cp: usage cp <src> <dst>");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ush_split_two_args(arg, src_arg, (u64)sizeof(src_arg), dst_arg, (u64)sizeof(dst_arg)) == 0) {
|
||||
ush_writeln("cp: usage cp <src> <dst>");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ush_resolve_path(sh, src_arg, src_path, (u64)sizeof(src_path)) == 0 ||
|
||||
ush_resolve_path(sh, dst_arg, dst_path, (u64)sizeof(dst_path)) == 0) {
|
||||
ush_writeln("cp: invalid path");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ush_path_is_under_temp(dst_path) == 0) {
|
||||
ush_writeln("cp: destination must be under /temp");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return ush_copy_file(src_path, dst_path);
|
||||
}
|
||||
|
||||
static int ush_cmd_mv(const ush_state *sh, const char *arg) {
|
||||
char src_arg[USH_PATH_MAX];
|
||||
char dst_arg[USH_PATH_MAX];
|
||||
char src_path[USH_PATH_MAX];
|
||||
char dst_path[USH_PATH_MAX];
|
||||
|
||||
if (arg == (const char *)0 || arg[0] == '\0') {
|
||||
ush_writeln("mv: usage mv <src> <dst>");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ush_split_two_args(arg, src_arg, (u64)sizeof(src_arg), dst_arg, (u64)sizeof(dst_arg)) == 0) {
|
||||
ush_writeln("mv: usage mv <src> <dst>");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ush_resolve_path(sh, src_arg, src_path, (u64)sizeof(src_path)) == 0 ||
|
||||
ush_resolve_path(sh, dst_arg, dst_path, (u64)sizeof(dst_path)) == 0) {
|
||||
ush_writeln("mv: invalid path");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ush_path_is_under_temp(src_path) == 0 || ush_path_is_under_temp(dst_path) == 0) {
|
||||
ush_writeln("mv: source and destination must be under /temp");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ush_copy_file(src_path, dst_path) == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (cleonos_sys_fs_remove(src_path) == 0ULL) {
|
||||
ush_writeln("mv: source remove failed");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ush_cmd_rm(const ush_state *sh, const char *arg) {
|
||||
char path[USH_PATH_MAX];
|
||||
|
||||
if (arg == (const char *)0 || arg[0] == '\0') {
|
||||
ush_writeln("rm: path required");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ush_resolve_path(sh, arg, path, (u64)sizeof(path)) == 0) {
|
||||
ush_writeln("rm: invalid path");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ush_path_is_under_temp(path) == 0) {
|
||||
ush_writeln("rm: target must be under /temp");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (cleonos_sys_fs_remove(path) == 0ULL) {
|
||||
ush_writeln("rm: failed (directory must be empty)");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ush_cmd_stats(const ush_state *sh) {
|
||||
(void)ush_cmd_memstat();
|
||||
(void)ush_cmd_fsstat();
|
||||
(void)ush_cmd_taskstat();
|
||||
(void)ush_cmd_userstat();
|
||||
(void)ush_cmd_kbdstat();
|
||||
(void)ush_cmd_shstat(sh);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ush_cmd_not_supported(const char *name, const char *why) {
|
||||
ush_write(name);
|
||||
ush_write(": ");
|
||||
ush_writeln(why);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ush_execute_line(ush_state *sh, const char *line) {
|
||||
char line_buf[USH_LINE_MAX];
|
||||
char cmd[USH_CMD_MAX];
|
||||
char arg[USH_ARG_MAX];
|
||||
u64 i = 0ULL;
|
||||
int known = 1;
|
||||
int success = 0;
|
||||
|
||||
if (sh == (ush_state *)0 || line == (const char *)0) {
|
||||
return;
|
||||
}
|
||||
|
||||
while (line[i] != '\0' && i + 1ULL < (u64)sizeof(line_buf)) {
|
||||
line_buf[i] = line[i];
|
||||
i++;
|
||||
}
|
||||
|
||||
line_buf[i] = '\0';
|
||||
ush_trim_line(line_buf);
|
||||
|
||||
if (line_buf[0] == '\0' || line_buf[0] == '#') {
|
||||
return;
|
||||
}
|
||||
|
||||
ush_parse_line(line_buf, cmd, (u64)sizeof(cmd), arg, (u64)sizeof(arg));
|
||||
ush_trim_line(arg);
|
||||
|
||||
if (ush_streq(cmd, "help") != 0) {
|
||||
success = ush_cmd_help();
|
||||
} else if (ush_streq(cmd, "ls") != 0 || ush_streq(cmd, "dir") != 0) {
|
||||
success = ush_cmd_ls(sh, arg);
|
||||
} else if (ush_streq(cmd, "cat") != 0) {
|
||||
success = ush_cmd_cat(sh, arg);
|
||||
} else if (ush_streq(cmd, "pwd") != 0) {
|
||||
success = ush_cmd_pwd(sh);
|
||||
} else if (ush_streq(cmd, "cd") != 0) {
|
||||
success = ush_cmd_cd(sh, arg);
|
||||
} else if (ush_streq(cmd, "exec") != 0 || ush_streq(cmd, "run") != 0) {
|
||||
success = ush_cmd_exec(sh, arg);
|
||||
} else if (ush_streq(cmd, "clear") != 0 || ush_streq(cmd, "cls") != 0) {
|
||||
success = ush_cmd_clear();
|
||||
} else if (ush_streq(cmd, "memstat") != 0) {
|
||||
success = ush_cmd_memstat();
|
||||
} else if (ush_streq(cmd, "fsstat") != 0) {
|
||||
success = ush_cmd_fsstat();
|
||||
} else if (ush_streq(cmd, "taskstat") != 0) {
|
||||
success = ush_cmd_taskstat();
|
||||
} else if (ush_streq(cmd, "userstat") != 0) {
|
||||
success = ush_cmd_userstat();
|
||||
} else if (ush_streq(cmd, "shstat") != 0) {
|
||||
success = ush_cmd_shstat(sh);
|
||||
} else if (ush_streq(cmd, "stats") != 0) {
|
||||
success = ush_cmd_stats(sh);
|
||||
} else if (ush_streq(cmd, "tty") != 0) {
|
||||
success = ush_cmd_tty(arg);
|
||||
} else if (ush_streq(cmd, "dmesg") != 0) {
|
||||
success = ush_cmd_dmesg(arg);
|
||||
} else if (ush_streq(cmd, "kbdstat") != 0) {
|
||||
success = ush_cmd_kbdstat();
|
||||
} else if (ush_streq(cmd, "mkdir") != 0) {
|
||||
success = ush_cmd_mkdir(sh, arg);
|
||||
} else if (ush_streq(cmd, "touch") != 0) {
|
||||
success = ush_cmd_touch(sh, arg);
|
||||
} else if (ush_streq(cmd, "write") != 0) {
|
||||
success = ush_cmd_write(sh, arg);
|
||||
} else if (ush_streq(cmd, "append") != 0) {
|
||||
success = ush_cmd_append(sh, arg);
|
||||
} else if (ush_streq(cmd, "cp") != 0) {
|
||||
success = ush_cmd_cp(sh, arg);
|
||||
} else if (ush_streq(cmd, "mv") != 0) {
|
||||
success = ush_cmd_mv(sh, arg);
|
||||
} else if (ush_streq(cmd, "rm") != 0) {
|
||||
success = ush_cmd_rm(sh, arg);
|
||||
} else if (ush_streq(cmd, "rusttest") != 0 || ush_streq(cmd, "panic") != 0 || ush_streq(cmd, "elfloader") != 0) {
|
||||
success = ush_cmd_not_supported(cmd, "this command is kernel-shell only");
|
||||
} else {
|
||||
known = 0;
|
||||
success = 0;
|
||||
ush_writeln("unknown command; type 'help'");
|
||||
}
|
||||
|
||||
sh->cmd_total++;
|
||||
|
||||
if (success != 0) {
|
||||
sh->cmd_ok++;
|
||||
} else {
|
||||
sh->cmd_fail++;
|
||||
}
|
||||
|
||||
if (known == 0) {
|
||||
sh->cmd_unknown++;
|
||||
}
|
||||
}
|
||||
322
cleonos/c/apps/shell/shell_input.c
Normal file
322
cleonos/c/apps/shell/shell_input.c
Normal file
@@ -0,0 +1,322 @@
|
||||
#include "shell_internal.h"
|
||||
|
||||
static void ush_history_cancel_nav(ush_state *sh) {
|
||||
if (sh == (ush_state *)0) {
|
||||
return;
|
||||
}
|
||||
|
||||
sh->history_nav = -1;
|
||||
sh->nav_saved_len = 0ULL;
|
||||
sh->nav_saved_cursor = 0ULL;
|
||||
sh->nav_saved_line[0] = '\0';
|
||||
}
|
||||
|
||||
static void ush_reset_line(ush_state *sh) {
|
||||
if (sh == (ush_state *)0) {
|
||||
return;
|
||||
}
|
||||
|
||||
sh->line_len = 0ULL;
|
||||
sh->cursor = 0ULL;
|
||||
sh->rendered_len = 0ULL;
|
||||
sh->line[0] = '\0';
|
||||
}
|
||||
|
||||
static void ush_load_line(ush_state *sh, const char *line) {
|
||||
if (sh == (ush_state *)0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (line == (const char *)0) {
|
||||
ush_reset_line(sh);
|
||||
return;
|
||||
}
|
||||
|
||||
ush_copy(sh->line, (u64)sizeof(sh->line), line);
|
||||
sh->line_len = ush_strlen(sh->line);
|
||||
sh->cursor = sh->line_len;
|
||||
}
|
||||
|
||||
static void ush_render_line(ush_state *sh) {
|
||||
u64 i;
|
||||
|
||||
if (sh == (ush_state *)0) {
|
||||
return;
|
||||
}
|
||||
|
||||
ush_write_char('\r');
|
||||
ush_prompt(sh);
|
||||
|
||||
for (i = 0ULL; i < sh->line_len; i++) {
|
||||
ush_write_char(sh->line[i]);
|
||||
}
|
||||
|
||||
for (i = sh->line_len; i < sh->rendered_len; i++) {
|
||||
ush_write_char(' ');
|
||||
}
|
||||
|
||||
ush_write_char('\r');
|
||||
ush_prompt(sh);
|
||||
|
||||
for (i = 0ULL; i < sh->cursor; i++) {
|
||||
ush_write_char(sh->line[i]);
|
||||
}
|
||||
|
||||
sh->rendered_len = sh->line_len;
|
||||
}
|
||||
|
||||
static int ush_line_has_non_space(const char *line) {
|
||||
u64 i = 0ULL;
|
||||
|
||||
if (line == (const char *)0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (line[i] != '\0') {
|
||||
if (ush_is_space(line[i]) == 0) {
|
||||
return 1;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ush_history_push(ush_state *sh, const char *line) {
|
||||
if (sh == (ush_state *)0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ush_line_has_non_space(line) == 0) {
|
||||
ush_history_cancel_nav(sh);
|
||||
return;
|
||||
}
|
||||
|
||||
if (sh->history_count > 0ULL && ush_streq(sh->history[sh->history_count - 1ULL], line) != 0) {
|
||||
ush_history_cancel_nav(sh);
|
||||
return;
|
||||
}
|
||||
|
||||
if (sh->history_count < USH_HISTORY_MAX) {
|
||||
ush_copy(sh->history[sh->history_count], (u64)sizeof(sh->history[sh->history_count]), line);
|
||||
sh->history_count++;
|
||||
} else {
|
||||
u64 i;
|
||||
|
||||
for (i = 1ULL; i < USH_HISTORY_MAX; i++) {
|
||||
ush_copy(sh->history[i - 1ULL], (u64)sizeof(sh->history[i - 1ULL]), sh->history[i]);
|
||||
}
|
||||
|
||||
ush_copy(sh->history[USH_HISTORY_MAX - 1ULL], (u64)sizeof(sh->history[USH_HISTORY_MAX - 1ULL]), line);
|
||||
}
|
||||
|
||||
ush_history_cancel_nav(sh);
|
||||
}
|
||||
|
||||
static void ush_history_apply_current(ush_state *sh) {
|
||||
if (sh == (ush_state *)0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (sh->history_nav >= 0) {
|
||||
ush_load_line(sh, sh->history[(u64)sh->history_nav]);
|
||||
} else {
|
||||
ush_copy(sh->line, (u64)sizeof(sh->line), sh->nav_saved_line);
|
||||
sh->line_len = sh->nav_saved_len;
|
||||
if (sh->line_len > USH_LINE_MAX - 1ULL) {
|
||||
sh->line_len = USH_LINE_MAX - 1ULL;
|
||||
sh->line[sh->line_len] = '\0';
|
||||
}
|
||||
sh->cursor = sh->nav_saved_cursor;
|
||||
if (sh->cursor > sh->line_len) {
|
||||
sh->cursor = sh->line_len;
|
||||
}
|
||||
}
|
||||
|
||||
ush_render_line(sh);
|
||||
}
|
||||
|
||||
static void ush_history_up(ush_state *sh) {
|
||||
if (sh == (ush_state *)0 || sh->history_count == 0ULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (sh->history_nav < 0) {
|
||||
ush_copy(sh->nav_saved_line, (u64)sizeof(sh->nav_saved_line), sh->line);
|
||||
sh->nav_saved_len = sh->line_len;
|
||||
sh->nav_saved_cursor = sh->cursor;
|
||||
sh->history_nav = (i64)sh->history_count - 1;
|
||||
} else if (sh->history_nav > 0) {
|
||||
sh->history_nav--;
|
||||
}
|
||||
|
||||
ush_history_apply_current(sh);
|
||||
}
|
||||
|
||||
static void ush_history_down(ush_state *sh) {
|
||||
if (sh == (ush_state *)0 || sh->history_nav < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ((u64)sh->history_nav + 1ULL < sh->history_count) {
|
||||
sh->history_nav++;
|
||||
} else {
|
||||
sh->history_nav = -1;
|
||||
}
|
||||
|
||||
ush_history_apply_current(sh);
|
||||
}
|
||||
|
||||
static char ush_read_char_blocking(void) {
|
||||
for (;;) {
|
||||
u64 ch = cleonos_sys_kbd_get_char();
|
||||
|
||||
if (ch != (u64)-1) {
|
||||
return (char)(ch & 0xFFULL);
|
||||
}
|
||||
|
||||
__asm__ volatile("pause");
|
||||
}
|
||||
}
|
||||
|
||||
void ush_read_line(ush_state *sh, char *out_line, u64 out_size) {
|
||||
if (sh == (ush_state *)0 || out_line == (char *)0 || out_size == 0ULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
ush_reset_line(sh);
|
||||
ush_history_cancel_nav(sh);
|
||||
|
||||
out_line[0] = '\0';
|
||||
|
||||
ush_prompt(sh);
|
||||
|
||||
for (;;) {
|
||||
char ch = ush_read_char_blocking();
|
||||
|
||||
if (ch == '\r') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ch == '\n') {
|
||||
ush_write_char('\n');
|
||||
sh->line[sh->line_len] = '\0';
|
||||
ush_history_push(sh, sh->line);
|
||||
ush_copy(out_line, out_size, sh->line);
|
||||
ush_reset_line(sh);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ch == USH_KEY_UP) {
|
||||
ush_history_up(sh);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ch == USH_KEY_DOWN) {
|
||||
ush_history_down(sh);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ch == USH_KEY_LEFT) {
|
||||
if (sh->cursor > 0ULL) {
|
||||
sh->cursor--;
|
||||
ush_render_line(sh);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ch == USH_KEY_RIGHT) {
|
||||
if (sh->cursor < sh->line_len) {
|
||||
sh->cursor++;
|
||||
ush_render_line(sh);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ch == USH_KEY_HOME) {
|
||||
if (sh->cursor != 0ULL) {
|
||||
sh->cursor = 0ULL;
|
||||
ush_render_line(sh);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ch == USH_KEY_END) {
|
||||
if (sh->cursor != sh->line_len) {
|
||||
sh->cursor = sh->line_len;
|
||||
ush_render_line(sh);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ch == '\b' || ch == 127) {
|
||||
if (sh->cursor > 0ULL && sh->line_len > 0ULL) {
|
||||
u64 i;
|
||||
|
||||
ush_history_cancel_nav(sh);
|
||||
|
||||
for (i = sh->cursor - 1ULL; i < sh->line_len; i++) {
|
||||
sh->line[i] = sh->line[i + 1ULL];
|
||||
}
|
||||
|
||||
sh->line_len--;
|
||||
sh->cursor--;
|
||||
ush_render_line(sh);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ch == USH_KEY_DELETE) {
|
||||
if (sh->cursor < sh->line_len) {
|
||||
u64 i;
|
||||
|
||||
ush_history_cancel_nav(sh);
|
||||
|
||||
for (i = sh->cursor; i < sh->line_len; i++) {
|
||||
sh->line[i] = sh->line[i + 1ULL];
|
||||
}
|
||||
|
||||
sh->line_len--;
|
||||
ush_render_line(sh);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ch == '\t') {
|
||||
ch = ' ';
|
||||
}
|
||||
|
||||
if (ush_is_printable(ch) == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (sh->line_len + 1ULL >= USH_LINE_MAX) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ush_history_cancel_nav(sh);
|
||||
|
||||
if (sh->cursor == sh->line_len) {
|
||||
sh->line[sh->line_len++] = ch;
|
||||
sh->line[sh->line_len] = '\0';
|
||||
sh->cursor = sh->line_len;
|
||||
ush_write_char(ch);
|
||||
sh->rendered_len = sh->line_len;
|
||||
continue;
|
||||
}
|
||||
|
||||
{
|
||||
u64 i;
|
||||
|
||||
for (i = sh->line_len; i > sh->cursor; i--) {
|
||||
sh->line[i] = sh->line[i - 1ULL];
|
||||
}
|
||||
|
||||
sh->line[sh->cursor] = ch;
|
||||
sh->line_len++;
|
||||
sh->cursor++;
|
||||
sh->line[sh->line_len] = '\0';
|
||||
ush_render_line(sh);
|
||||
}
|
||||
}
|
||||
}
|
||||
74
cleonos/c/apps/shell/shell_internal.h
Normal file
74
cleonos/c/apps/shell/shell_internal.h
Normal file
@@ -0,0 +1,74 @@
|
||||
#ifndef CLEONOS_USER_SHELL_INTERNAL_H
|
||||
#define CLEONOS_USER_SHELL_INTERNAL_H
|
||||
|
||||
#include <cleonos_syscall.h>
|
||||
|
||||
typedef long long i64;
|
||||
|
||||
#define USH_CMD_MAX 32ULL
|
||||
#define USH_ARG_MAX 160ULL
|
||||
#define USH_LINE_MAX 192ULL
|
||||
#define USH_PATH_MAX 192ULL
|
||||
#define USH_CAT_MAX 512ULL
|
||||
#define USH_SCRIPT_MAX 1024ULL
|
||||
#define USH_CLEAR_LINES 56ULL
|
||||
#define USH_HISTORY_MAX 16ULL
|
||||
|
||||
#define USH_KEY_LEFT ((char)0x01)
|
||||
#define USH_KEY_RIGHT ((char)0x02)
|
||||
#define USH_KEY_UP ((char)0x03)
|
||||
#define USH_KEY_DOWN ((char)0x04)
|
||||
#define USH_KEY_HOME ((char)0x05)
|
||||
#define USH_KEY_END ((char)0x06)
|
||||
#define USH_KEY_DELETE ((char)0x07)
|
||||
|
||||
typedef struct ush_state {
|
||||
char line[USH_LINE_MAX];
|
||||
u64 line_len;
|
||||
u64 cursor;
|
||||
u64 rendered_len;
|
||||
|
||||
char cwd[USH_PATH_MAX];
|
||||
|
||||
char history[USH_HISTORY_MAX][USH_LINE_MAX];
|
||||
u64 history_count;
|
||||
i64 history_nav;
|
||||
char nav_saved_line[USH_LINE_MAX];
|
||||
u64 nav_saved_len;
|
||||
u64 nav_saved_cursor;
|
||||
|
||||
u64 cmd_total;
|
||||
u64 cmd_ok;
|
||||
u64 cmd_fail;
|
||||
u64 cmd_unknown;
|
||||
} ush_state;
|
||||
|
||||
void ush_init_state(ush_state *sh);
|
||||
|
||||
u64 ush_strlen(const char *str);
|
||||
int ush_streq(const char *left, const char *right);
|
||||
int ush_is_space(char ch);
|
||||
int ush_is_printable(char ch);
|
||||
int ush_has_suffix(const char *name, const char *suffix);
|
||||
int ush_contains_char(const char *text, char needle);
|
||||
int ush_parse_u64_dec(const char *text, u64 *out_value);
|
||||
void ush_copy(char *dst, u64 dst_size, const char *src);
|
||||
void ush_trim_line(char *line);
|
||||
void ush_parse_line(const char *line, char *out_cmd, u64 cmd_size, char *out_arg, u64 arg_size);
|
||||
|
||||
void ush_write(const char *text);
|
||||
void ush_write_char(char ch);
|
||||
void ush_writeln(const char *text);
|
||||
void ush_prompt(const ush_state *sh);
|
||||
void ush_write_hex_u64(u64 value);
|
||||
void ush_print_kv_hex(const char *label, u64 value);
|
||||
|
||||
int ush_resolve_path(const ush_state *sh, const char *arg, char *out_path, u64 out_size);
|
||||
int ush_resolve_exec_path(const ush_state *sh, const char *arg, char *out_path, u64 out_size);
|
||||
int ush_path_is_under_system(const char *path);
|
||||
|
||||
void ush_read_line(ush_state *sh, char *out_line, u64 out_size);
|
||||
int ush_run_script_file(ush_state *sh, const char *path);
|
||||
void ush_execute_line(ush_state *sh, const char *line);
|
||||
|
||||
#endif
|
||||
202
cleonos/c/apps/shell/shell_path.c
Normal file
202
cleonos/c/apps/shell/shell_path.c
Normal file
@@ -0,0 +1,202 @@
|
||||
#include "shell_internal.h"
|
||||
|
||||
static int ush_path_push_component(char *path, u64 path_size, u64 *io_len, const char *component, u64 comp_len) {
|
||||
u64 i;
|
||||
|
||||
if (path == (char *)0 || io_len == (u64 *)0 || component == (const char *)0 || comp_len == 0ULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (*io_len == 1ULL) {
|
||||
if (*io_len + comp_len >= path_size) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0ULL; i < comp_len; i++) {
|
||||
path[1ULL + i] = component[i];
|
||||
}
|
||||
|
||||
*io_len = 1ULL + comp_len;
|
||||
path[*io_len] = '\0';
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (*io_len + 1ULL + comp_len >= path_size) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
path[*io_len] = '/';
|
||||
for (i = 0ULL; i < comp_len; i++) {
|
||||
path[*io_len + 1ULL + i] = component[i];
|
||||
}
|
||||
|
||||
*io_len += (1ULL + comp_len);
|
||||
path[*io_len] = '\0';
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void ush_path_pop_component(char *path, u64 *io_len) {
|
||||
if (path == (char *)0 || io_len == (u64 *)0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (*io_len <= 1ULL) {
|
||||
path[0] = '/';
|
||||
path[1] = '\0';
|
||||
*io_len = 1ULL;
|
||||
return;
|
||||
}
|
||||
|
||||
while (*io_len > 1ULL && path[*io_len - 1ULL] != '/') {
|
||||
(*io_len)--;
|
||||
}
|
||||
|
||||
if (*io_len > 1ULL) {
|
||||
(*io_len)--;
|
||||
}
|
||||
|
||||
path[*io_len] = '\0';
|
||||
}
|
||||
|
||||
static int ush_path_parse_into(const char *src, char *out_path, u64 out_size, u64 *io_len) {
|
||||
u64 i = 0ULL;
|
||||
|
||||
if (src == (const char *)0 || out_path == (char *)0 || io_len == (u64 *)0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (src[0] == '/') {
|
||||
i = 1ULL;
|
||||
}
|
||||
|
||||
while (src[i] != '\0') {
|
||||
u64 start;
|
||||
u64 len;
|
||||
|
||||
while (src[i] == '/') {
|
||||
i++;
|
||||
}
|
||||
|
||||
if (src[i] == '\0') {
|
||||
break;
|
||||
}
|
||||
|
||||
start = i;
|
||||
|
||||
while (src[i] != '\0' && src[i] != '/') {
|
||||
i++;
|
||||
}
|
||||
|
||||
len = i - start;
|
||||
|
||||
if (len == 1ULL && src[start] == '.') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (len == 2ULL && src[start] == '.' && src[start + 1ULL] == '.') {
|
||||
ush_path_pop_component(out_path, io_len);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ush_path_push_component(out_path, out_size, io_len, src + start, len) == 0) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int ush_resolve_path(const ush_state *sh, const char *arg, char *out_path, u64 out_size) {
|
||||
u64 len = 1ULL;
|
||||
|
||||
if (sh == (const ush_state *)0 || out_path == (char *)0 || out_size < 2ULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
out_path[0] = '/';
|
||||
out_path[1] = '\0';
|
||||
|
||||
if (arg == (const char *)0 || arg[0] == '\0') {
|
||||
return ush_path_parse_into(sh->cwd, out_path, out_size, &len);
|
||||
}
|
||||
|
||||
if (arg[0] != '/') {
|
||||
if (ush_path_parse_into(sh->cwd, out_path, out_size, &len) == 0) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return ush_path_parse_into(arg, out_path, out_size, &len);
|
||||
}
|
||||
|
||||
int ush_resolve_exec_path(const ush_state *sh, const char *arg, char *out_path, u64 out_size) {
|
||||
u64 i;
|
||||
u64 cursor = 0ULL;
|
||||
|
||||
if (sh == (const ush_state *)0 || 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] == '/') {
|
||||
ush_copy(out_path, out_size, arg);
|
||||
} else if (ush_contains_char(arg, '/') != 0) {
|
||||
if (ush_resolve_path(sh, arg, out_path, out_size) == 0) {
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
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];
|
||||
}
|
||||
|
||||
for (i = 0ULL; arg[i] != '\0'; i++) {
|
||||
if (cursor + 1ULL >= out_size) {
|
||||
return 0;
|
||||
}
|
||||
out_path[cursor++] = arg[i];
|
||||
}
|
||||
|
||||
out_path[cursor] = '\0';
|
||||
}
|
||||
|
||||
if (ush_has_suffix(out_path, ".elf") == 0) {
|
||||
static const char suffix[] = ".elf";
|
||||
|
||||
cursor = ush_strlen(out_path);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
int ush_path_is_under_system(const char *path) {
|
||||
if (path == (const char *)0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (path[0] != '/' || path[1] != 's' || path[2] != 'y' || path[3] != 's' || path[4] != 't' || path[5] != 'e' || path[6] != 'm') {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (path[7] == '\0' || path[7] == '/') ? 1 : 0;
|
||||
}
|
||||
46
cleonos/c/apps/shell/shell_script.c
Normal file
46
cleonos/c/apps/shell/shell_script.c
Normal file
@@ -0,0 +1,46 @@
|
||||
#include "shell_internal.h"
|
||||
|
||||
int ush_run_script_file(ush_state *sh, const char *path) {
|
||||
char script[USH_SCRIPT_MAX + 1ULL];
|
||||
char line[USH_LINE_MAX];
|
||||
u64 got;
|
||||
u64 i;
|
||||
u64 line_pos = 0ULL;
|
||||
|
||||
if (sh == (ush_state *)0 || path == (const char *)0 || path[0] == '\0') {
|
||||
return 0;
|
||||
}
|
||||
|
||||
got = cleonos_sys_fs_read(path, script, USH_SCRIPT_MAX);
|
||||
|
||||
if (got == 0ULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (got > USH_SCRIPT_MAX) {
|
||||
got = USH_SCRIPT_MAX;
|
||||
}
|
||||
|
||||
script[got] = '\0';
|
||||
|
||||
for (i = 0ULL; i <= got; i++) {
|
||||
char ch = script[i];
|
||||
|
||||
if (ch == '\r') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ch == '\n' || ch == '\0') {
|
||||
line[line_pos] = '\0';
|
||||
ush_execute_line(sh, line);
|
||||
line_pos = 0ULL;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (line_pos + 1ULL < (u64)sizeof(line)) {
|
||||
line[line_pos++] = ch;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
273
cleonos/c/apps/shell/shell_util.c
Normal file
273
cleonos/c/apps/shell/shell_util.c
Normal file
@@ -0,0 +1,273 @@
|
||||
#include "shell_internal.h"
|
||||
|
||||
void ush_init_state(ush_state *sh) {
|
||||
if (sh == (ush_state *)0) {
|
||||
return;
|
||||
}
|
||||
|
||||
sh->line[0] = '\0';
|
||||
sh->line_len = 0ULL;
|
||||
sh->cursor = 0ULL;
|
||||
sh->rendered_len = 0ULL;
|
||||
|
||||
ush_copy(sh->cwd, (u64)sizeof(sh->cwd), "/");
|
||||
|
||||
sh->history_count = 0ULL;
|
||||
sh->history_nav = -1;
|
||||
sh->nav_saved_line[0] = '\0';
|
||||
sh->nav_saved_len = 0ULL;
|
||||
sh->nav_saved_cursor = 0ULL;
|
||||
|
||||
sh->cmd_total = 0ULL;
|
||||
sh->cmd_ok = 0ULL;
|
||||
sh->cmd_fail = 0ULL;
|
||||
sh->cmd_unknown = 0ULL;
|
||||
}
|
||||
|
||||
u64 ush_strlen(const char *str) {
|
||||
u64 len = 0ULL;
|
||||
|
||||
if (str == (const char *)0) {
|
||||
return 0ULL;
|
||||
}
|
||||
|
||||
while (str[len] != '\0') {
|
||||
len++;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
int ush_streq(const char *left, const char *right) {
|
||||
u64 i = 0ULL;
|
||||
|
||||
if (left == (const char *)0 || right == (const char *)0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (left[i] != '\0' && right[i] != '\0') {
|
||||
if (left[i] != right[i]) {
|
||||
return 0;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
return (left[i] == right[i]) ? 1 : 0;
|
||||
}
|
||||
|
||||
int ush_is_space(char ch) {
|
||||
return (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n') ? 1 : 0;
|
||||
}
|
||||
|
||||
int ush_is_printable(char ch) {
|
||||
return (ch >= 32 && ch <= 126) ? 1 : 0;
|
||||
}
|
||||
|
||||
int ush_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 = ush_strlen(name);
|
||||
suffix_len = ush_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;
|
||||
}
|
||||
|
||||
int ush_contains_char(const char *text, char needle) {
|
||||
u64 i = 0ULL;
|
||||
|
||||
if (text == (const char *)0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (text[i] != '\0') {
|
||||
if (text[i] == needle) {
|
||||
return 1;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ush_parse_u64_dec(const char *text, u64 *out_value) {
|
||||
u64 value = 0ULL;
|
||||
u64 i = 0ULL;
|
||||
|
||||
if (text == (const char *)0 || out_value == (u64 *)0 || text[0] == '\0') {
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (text[i] != '\0') {
|
||||
u64 digit;
|
||||
|
||||
if (text[i] < '0' || text[i] > '9') {
|
||||
return 0;
|
||||
}
|
||||
|
||||
digit = (u64)(text[i] - '0');
|
||||
|
||||
if (value > ((0xFFFFFFFFFFFFFFFFULL - digit) / 10ULL)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
value = (value * 10ULL) + digit;
|
||||
i++;
|
||||
}
|
||||
|
||||
*out_value = value;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void ush_copy(char *dst, u64 dst_size, const char *src) {
|
||||
u64 i = 0ULL;
|
||||
|
||||
if (dst == (char *)0 || src == (const char *)0 || dst_size == 0ULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
while (src[i] != '\0' && i + 1ULL < dst_size) {
|
||||
dst[i] = src[i];
|
||||
i++;
|
||||
}
|
||||
|
||||
dst[i] = '\0';
|
||||
}
|
||||
|
||||
void ush_trim_line(char *line) {
|
||||
u64 start = 0ULL;
|
||||
u64 i = 0ULL;
|
||||
u64 len;
|
||||
|
||||
if (line == (char *)0) {
|
||||
return;
|
||||
}
|
||||
|
||||
while (line[start] != '\0' && ush_is_space(line[start]) != 0) {
|
||||
start++;
|
||||
}
|
||||
|
||||
if (start > 0ULL) {
|
||||
while (line[start + i] != '\0') {
|
||||
line[i] = line[start + i];
|
||||
i++;
|
||||
}
|
||||
line[i] = '\0';
|
||||
}
|
||||
|
||||
len = ush_strlen(line);
|
||||
|
||||
while (len > 0ULL && ush_is_space(line[len - 1ULL]) != 0) {
|
||||
line[len - 1ULL] = '\0';
|
||||
len--;
|
||||
}
|
||||
}
|
||||
|
||||
void ush_parse_line(const char *line, char *out_cmd, u64 cmd_size, char *out_arg, u64 arg_size) {
|
||||
u64 i = 0ULL;
|
||||
u64 cmd_pos = 0ULL;
|
||||
u64 arg_pos = 0ULL;
|
||||
|
||||
if (line == (const char *)0 || out_cmd == (char *)0 || out_arg == (char *)0) {
|
||||
return;
|
||||
}
|
||||
|
||||
out_cmd[0] = '\0';
|
||||
out_arg[0] = '\0';
|
||||
|
||||
while (line[i] != '\0' && ush_is_space(line[i]) != 0) {
|
||||
i++;
|
||||
}
|
||||
|
||||
while (line[i] != '\0' && ush_is_space(line[i]) == 0) {
|
||||
if (cmd_pos + 1ULL < cmd_size) {
|
||||
out_cmd[cmd_pos++] = line[i];
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
out_cmd[cmd_pos] = '\0';
|
||||
|
||||
while (line[i] != '\0' && ush_is_space(line[i]) != 0) {
|
||||
i++;
|
||||
}
|
||||
|
||||
while (line[i] != '\0') {
|
||||
if (arg_pos + 1ULL < arg_size) {
|
||||
out_arg[arg_pos++] = line[i];
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
out_arg[arg_pos] = '\0';
|
||||
}
|
||||
|
||||
void ush_write(const char *text) {
|
||||
u64 len;
|
||||
|
||||
if (text == (const char *)0) {
|
||||
return;
|
||||
}
|
||||
|
||||
len = ush_strlen(text);
|
||||
|
||||
if (len == 0ULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
(void)cleonos_sys_tty_write(text, len);
|
||||
}
|
||||
|
||||
void ush_write_char(char ch) {
|
||||
(void)cleonos_sys_tty_write_char(ch);
|
||||
}
|
||||
|
||||
void ush_writeln(const char *text) {
|
||||
ush_write(text);
|
||||
ush_write_char('\n');
|
||||
}
|
||||
|
||||
void ush_prompt(const ush_state *sh) {
|
||||
if (sh == (const ush_state *)0) {
|
||||
ush_write("cleonos(user)> ");
|
||||
return;
|
||||
}
|
||||
|
||||
ush_write("cleonos(user:");
|
||||
ush_write(sh->cwd);
|
||||
ush_write(")> ");
|
||||
}
|
||||
|
||||
void ush_write_hex_u64(u64 value) {
|
||||
i64 nibble;
|
||||
|
||||
ush_write("0X");
|
||||
|
||||
for (nibble = 15; nibble >= 0; nibble--) {
|
||||
u64 current = (value >> (u64)(nibble * 4)) & 0x0FULL;
|
||||
char out = (current < 10ULL) ? (char)('0' + current) : (char)('A' + (current - 10ULL));
|
||||
ush_write_char(out);
|
||||
}
|
||||
}
|
||||
|
||||
void ush_print_kv_hex(const char *label, u64 value) {
|
||||
ush_write(label);
|
||||
ush_write(": ");
|
||||
ush_write_hex_u64(value);
|
||||
ush_write_char('\n');
|
||||
}
|
||||
@@ -1,539 +1,18 @@
|
||||
#include <cleonos_syscall.h>
|
||||
|
||||
#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;
|
||||
|
||||
if (str == (const char *)0) {
|
||||
return 0ULL;
|
||||
}
|
||||
|
||||
while (str[len] != '\0') {
|
||||
len++;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static int shell_streq(const char *left, const char *right) {
|
||||
u64 i = 0ULL;
|
||||
|
||||
if (left == (const char *)0 || right == (const char *)0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (left[i] != '\0' && right[i] != '\0') {
|
||||
if (left[i] != right[i]) {
|
||||
return 0;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
return (left[i] == right[i]) ? 1 : 0;
|
||||
}
|
||||
|
||||
static int shell_is_space(char ch) {
|
||||
return (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n') ? 1 : 0;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
while (src[i] != '\0' && i + 1ULL < dst_size) {
|
||||
dst[i] = src[i];
|
||||
i++;
|
||||
}
|
||||
|
||||
dst[i] = '\0';
|
||||
}
|
||||
|
||||
static void shell_write(const char *text) {
|
||||
u64 len;
|
||||
|
||||
if (text == (const char *)0) {
|
||||
return;
|
||||
}
|
||||
|
||||
len = shell_strlen(text);
|
||||
|
||||
if (len == 0ULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
(void)cleonos_sys_tty_write(text, len);
|
||||
}
|
||||
|
||||
static void shell_write_char(char ch) {
|
||||
(void)cleonos_sys_tty_write_char(ch);
|
||||
}
|
||||
|
||||
static void shell_writeln(const char *text) {
|
||||
shell_write(text);
|
||||
shell_write_char('\n');
|
||||
}
|
||||
|
||||
static void shell_prompt(void) {
|
||||
shell_write("cleonos(user)> ");
|
||||
}
|
||||
|
||||
static void shell_trim_line(char *line) {
|
||||
u64 start = 0ULL;
|
||||
u64 i = 0ULL;
|
||||
u64 len;
|
||||
|
||||
if (line == (char *)0) {
|
||||
return;
|
||||
}
|
||||
|
||||
while (line[start] != '\0' && shell_is_space(line[start]) != 0) {
|
||||
start++;
|
||||
}
|
||||
|
||||
if (start > 0ULL) {
|
||||
while (line[start + i] != '\0') {
|
||||
line[i] = line[start + i];
|
||||
i++;
|
||||
}
|
||||
line[i] = '\0';
|
||||
}
|
||||
|
||||
len = shell_strlen(line);
|
||||
|
||||
while (len > 0ULL && shell_is_space(line[len - 1ULL]) != 0) {
|
||||
line[len - 1ULL] = '\0';
|
||||
len--;
|
||||
}
|
||||
}
|
||||
|
||||
static void shell_parse_line(const char *line, char *out_cmd, u64 cmd_size, char *out_arg, u64 arg_size) {
|
||||
u64 i = 0ULL;
|
||||
u64 cmd_pos = 0ULL;
|
||||
u64 arg_pos = 0ULL;
|
||||
|
||||
if (line == (const char *)0 || out_cmd == (char *)0 || out_arg == (char *)0) {
|
||||
return;
|
||||
}
|
||||
|
||||
out_cmd[0] = '\0';
|
||||
out_arg[0] = '\0';
|
||||
|
||||
while (line[i] != '\0' && shell_is_space(line[i]) != 0) {
|
||||
i++;
|
||||
}
|
||||
|
||||
while (line[i] != '\0' && shell_is_space(line[i]) == 0) {
|
||||
if (cmd_pos + 1ULL < cmd_size) {
|
||||
out_cmd[cmd_pos++] = line[i];
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
out_cmd[cmd_pos] = '\0';
|
||||
|
||||
while (line[i] != '\0' && shell_is_space(line[i]) != 0) {
|
||||
i++;
|
||||
}
|
||||
|
||||
while (line[i] != '\0') {
|
||||
if (arg_pos + 1ULL < arg_size) {
|
||||
out_arg[arg_pos++] = line[i];
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
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 int shell_path_is_under_system(const char *path) {
|
||||
if (path == (const char *)0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (path[0] != '/' || path[1] != 's' || path[2] != 'y' || path[3] != 's' || path[4] != 't' || path[5] != 'e' || path[6] != 'm') {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (path[7] == '\0' || path[7] == '/') ? 1 : 0;
|
||||
}
|
||||
|
||||
static void shell_cmd_help(void) {
|
||||
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) {
|
||||
const char *path = arg;
|
||||
u64 count;
|
||||
u64 i;
|
||||
|
||||
if (path == (const char *)0 || path[0] == '\0') {
|
||||
path = "/";
|
||||
}
|
||||
|
||||
count = cleonos_sys_fs_child_count(path);
|
||||
|
||||
if (count == (u64)-1) {
|
||||
shell_writeln("ls: directory not found");
|
||||
return;
|
||||
}
|
||||
|
||||
if (count == 0ULL) {
|
||||
shell_writeln("(empty)");
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0ULL; i < count; i++) {
|
||||
char name[CLEONOS_FS_NAME_MAX];
|
||||
|
||||
name[0] = '\0';
|
||||
|
||||
if (cleonos_sys_fs_get_child_name(path, i, name) == 0ULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
shell_writeln(name);
|
||||
}
|
||||
}
|
||||
|
||||
static void shell_cmd_cat(const char *arg) {
|
||||
char cat_buf[SHELL_CAT_MAX + 1ULL];
|
||||
u64 got;
|
||||
|
||||
if (arg == (const char *)0 || arg[0] == '\0') {
|
||||
shell_writeln("cat: file path required");
|
||||
return;
|
||||
}
|
||||
|
||||
got = cleonos_sys_fs_read(arg, cat_buf, SHELL_CAT_MAX);
|
||||
|
||||
if (got == 0ULL) {
|
||||
shell_writeln("cat: file not found");
|
||||
return;
|
||||
}
|
||||
|
||||
if (got > SHELL_CAT_MAX) {
|
||||
got = SHELL_CAT_MAX;
|
||||
}
|
||||
|
||||
cat_buf[got] = '\0';
|
||||
shell_writeln(cat_buf);
|
||||
}
|
||||
|
||||
static void shell_cmd_exec(const char *arg) {
|
||||
char path[SHELL_LINE_MAX];
|
||||
u64 status;
|
||||
|
||||
if (shell_resolve_exec_path(arg, path, (u64)sizeof(path)) == 0) {
|
||||
shell_writeln("exec: invalid target");
|
||||
return;
|
||||
}
|
||||
|
||||
if (shell_path_is_under_system(path) != 0) {
|
||||
shell_writeln("exec: /system/*.elf is kernel-mode (KELF), not user-exec");
|
||||
return;
|
||||
}
|
||||
|
||||
status = cleonos_sys_exec_path(path);
|
||||
|
||||
if (status == (u64)-1) {
|
||||
shell_writeln("exec: request failed");
|
||||
} else if (status == 0ULL) {
|
||||
shell_writeln("exec: request accepted");
|
||||
} else {
|
||||
shell_writeln("exec: returned non-zero status");
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
(void)cleonos_sys_task_count();
|
||||
shell_writeln("stats: use kernel log channel for full counters");
|
||||
}
|
||||
|
||||
static void shell_execute_line(const char *line) {
|
||||
char cmd[SHELL_CMD_MAX];
|
||||
char arg[SHELL_ARG_MAX];
|
||||
char line_buf[SHELL_LINE_MAX];
|
||||
u64 i = 0ULL;
|
||||
|
||||
if (line == (const char *)0) {
|
||||
return;
|
||||
}
|
||||
|
||||
while (line[i] != '\0' && i + 1ULL < (u64)sizeof(line_buf)) {
|
||||
line_buf[i] = line[i];
|
||||
i++;
|
||||
}
|
||||
|
||||
line_buf[i] = '\0';
|
||||
shell_trim_line(line_buf);
|
||||
|
||||
if (line_buf[0] == '\0' || line_buf[0] == '#') {
|
||||
return;
|
||||
}
|
||||
|
||||
shell_parse_line(line_buf, cmd, (u64)sizeof(cmd), arg, (u64)sizeof(arg));
|
||||
|
||||
if (shell_streq(cmd, "help") != 0) {
|
||||
shell_cmd_help();
|
||||
return;
|
||||
}
|
||||
|
||||
if (shell_streq(cmd, "ls") != 0) {
|
||||
shell_cmd_ls(arg);
|
||||
return;
|
||||
}
|
||||
|
||||
if (shell_streq(cmd, "cat") != 0) {
|
||||
shell_cmd_cat(arg);
|
||||
return;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if (shell_streq(cmd, "stats") != 0) {
|
||||
shell_cmd_stats();
|
||||
return;
|
||||
}
|
||||
|
||||
shell_writeln("unknown command; type 'help'");
|
||||
}
|
||||
|
||||
static int shell_run_script_file(const char *path) {
|
||||
char script[SHELL_SCRIPT_MAX + 1ULL];
|
||||
char line[SHELL_LINE_MAX];
|
||||
u64 got;
|
||||
u64 i;
|
||||
u64 line_pos = 0ULL;
|
||||
|
||||
if (path == (const char *)0 || path[0] == '\0') {
|
||||
return 0;
|
||||
}
|
||||
|
||||
got = cleonos_sys_fs_read(path, script, SHELL_SCRIPT_MAX);
|
||||
|
||||
if (got == 0ULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (got > SHELL_SCRIPT_MAX) {
|
||||
got = SHELL_SCRIPT_MAX;
|
||||
}
|
||||
|
||||
script[got] = '\0';
|
||||
|
||||
for (i = 0ULL; i <= got; i++) {
|
||||
char ch = script[i];
|
||||
|
||||
if (ch == '\r') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ch == '\n' || ch == '\0') {
|
||||
line[line_pos] = '\0';
|
||||
shell_execute_line(line);
|
||||
line_pos = 0ULL;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (line_pos + 1ULL < (u64)sizeof(line)) {
|
||||
line[line_pos++] = ch;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char shell_read_char_blocking(void) {
|
||||
for (;;) {
|
||||
u64 ch = cleonos_sys_kbd_get_char();
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
#include "shell/shell_internal.h"
|
||||
|
||||
int cleonos_app_main(void) {
|
||||
char line[SHELL_LINE_MAX];
|
||||
ush_state sh;
|
||||
char line[USH_LINE_MAX];
|
||||
|
||||
shell_writeln("[USER][SHELL] interactive framework online");
|
||||
ush_init_state(&sh);
|
||||
ush_writeln("[USER][SHELL] interactive framework online");
|
||||
|
||||
if (shell_run_script_file("/shell/init.cmd") == 0) {
|
||||
shell_writeln("[USER][SHELL] /shell/init.cmd missing");
|
||||
if (ush_run_script_file(&sh, "/shell/init.cmd") == 0) {
|
||||
ush_writeln("[USER][SHELL] /shell/init.cmd missing");
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
shell_prompt();
|
||||
shell_read_line(line, (u64)sizeof(line));
|
||||
shell_execute_line(line);
|
||||
ush_read_line(&sh, line, (u64)sizeof(line));
|
||||
ush_execute_line(&sh, line);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,6 +33,19 @@ typedef unsigned long long usize;
|
||||
#define CLEONOS_SYSCALL_TTY_WRITE 24ULL
|
||||
#define CLEONOS_SYSCALL_TTY_WRITE_CHAR 25ULL
|
||||
#define CLEONOS_SYSCALL_KBD_GET_CHAR 26ULL
|
||||
#define CLEONOS_SYSCALL_FS_STAT_TYPE 27ULL
|
||||
#define CLEONOS_SYSCALL_FS_STAT_SIZE 28ULL
|
||||
#define CLEONOS_SYSCALL_FS_MKDIR 29ULL
|
||||
#define CLEONOS_SYSCALL_FS_WRITE 30ULL
|
||||
#define CLEONOS_SYSCALL_FS_APPEND 31ULL
|
||||
#define CLEONOS_SYSCALL_FS_REMOVE 32ULL
|
||||
#define CLEONOS_SYSCALL_LOG_JOURNAL_COUNT 33ULL
|
||||
#define CLEONOS_SYSCALL_LOG_JOURNAL_READ 34ULL
|
||||
#define CLEONOS_SYSCALL_KBD_BUFFERED 35ULL
|
||||
#define CLEONOS_SYSCALL_KBD_PUSHED 36ULL
|
||||
#define CLEONOS_SYSCALL_KBD_POPPED 37ULL
|
||||
#define CLEONOS_SYSCALL_KBD_DROPPED 38ULL
|
||||
#define CLEONOS_SYSCALL_KBD_HOTKEY_SWITCHES 39ULL
|
||||
|
||||
u64 cleonos_syscall(u64 id, u64 arg0, u64 arg1, u64 arg2);
|
||||
u64 cleonos_sys_log_write(const char *message, u64 length);
|
||||
@@ -61,5 +74,18 @@ u64 cleonos_sys_tty_switch(u64 tty_index);
|
||||
u64 cleonos_sys_tty_write(const char *text, u64 length);
|
||||
u64 cleonos_sys_tty_write_char(char ch);
|
||||
u64 cleonos_sys_kbd_get_char(void);
|
||||
u64 cleonos_sys_fs_stat_type(const char *path);
|
||||
u64 cleonos_sys_fs_stat_size(const char *path);
|
||||
u64 cleonos_sys_fs_mkdir(const char *path);
|
||||
u64 cleonos_sys_fs_write(const char *path, const char *data, u64 size);
|
||||
u64 cleonos_sys_fs_append(const char *path, const char *data, u64 size);
|
||||
u64 cleonos_sys_fs_remove(const char *path);
|
||||
u64 cleonos_sys_log_journal_count(void);
|
||||
u64 cleonos_sys_log_journal_read(u64 index_from_oldest, char *out_line, u64 out_size);
|
||||
u64 cleonos_sys_kbd_buffered(void);
|
||||
u64 cleonos_sys_kbd_pushed(void);
|
||||
u64 cleonos_sys_kbd_popped(void);
|
||||
u64 cleonos_sys_kbd_dropped(void);
|
||||
u64 cleonos_sys_kbd_hotkey_switches(void);
|
||||
|
||||
#endif
|
||||
@@ -116,3 +116,55 @@ u64 cleonos_sys_tty_write_char(char ch) {
|
||||
u64 cleonos_sys_kbd_get_char(void) {
|
||||
return cleonos_syscall(CLEONOS_SYSCALL_KBD_GET_CHAR, 0ULL, 0ULL, 0ULL);
|
||||
}
|
||||
|
||||
u64 cleonos_sys_fs_stat_type(const char *path) {
|
||||
return cleonos_syscall(CLEONOS_SYSCALL_FS_STAT_TYPE, (u64)path, 0ULL, 0ULL);
|
||||
}
|
||||
|
||||
u64 cleonos_sys_fs_stat_size(const char *path) {
|
||||
return cleonos_syscall(CLEONOS_SYSCALL_FS_STAT_SIZE, (u64)path, 0ULL, 0ULL);
|
||||
}
|
||||
|
||||
u64 cleonos_sys_fs_mkdir(const char *path) {
|
||||
return cleonos_syscall(CLEONOS_SYSCALL_FS_MKDIR, (u64)path, 0ULL, 0ULL);
|
||||
}
|
||||
|
||||
u64 cleonos_sys_fs_write(const char *path, const char *data, u64 size) {
|
||||
return cleonos_syscall(CLEONOS_SYSCALL_FS_WRITE, (u64)path, (u64)data, size);
|
||||
}
|
||||
|
||||
u64 cleonos_sys_fs_append(const char *path, const char *data, u64 size) {
|
||||
return cleonos_syscall(CLEONOS_SYSCALL_FS_APPEND, (u64)path, (u64)data, size);
|
||||
}
|
||||
|
||||
u64 cleonos_sys_fs_remove(const char *path) {
|
||||
return cleonos_syscall(CLEONOS_SYSCALL_FS_REMOVE, (u64)path, 0ULL, 0ULL);
|
||||
}
|
||||
|
||||
u64 cleonos_sys_log_journal_count(void) {
|
||||
return cleonos_syscall(CLEONOS_SYSCALL_LOG_JOURNAL_COUNT, 0ULL, 0ULL, 0ULL);
|
||||
}
|
||||
|
||||
u64 cleonos_sys_log_journal_read(u64 index_from_oldest, char *out_line, u64 out_size) {
|
||||
return cleonos_syscall(CLEONOS_SYSCALL_LOG_JOURNAL_READ, index_from_oldest, (u64)out_line, out_size);
|
||||
}
|
||||
|
||||
u64 cleonos_sys_kbd_buffered(void) {
|
||||
return cleonos_syscall(CLEONOS_SYSCALL_KBD_BUFFERED, 0ULL, 0ULL, 0ULL);
|
||||
}
|
||||
|
||||
u64 cleonos_sys_kbd_pushed(void) {
|
||||
return cleonos_syscall(CLEONOS_SYSCALL_KBD_PUSHED, 0ULL, 0ULL, 0ULL);
|
||||
}
|
||||
|
||||
u64 cleonos_sys_kbd_popped(void) {
|
||||
return cleonos_syscall(CLEONOS_SYSCALL_KBD_POPPED, 0ULL, 0ULL, 0ULL);
|
||||
}
|
||||
|
||||
u64 cleonos_sys_kbd_dropped(void) {
|
||||
return cleonos_syscall(CLEONOS_SYSCALL_KBD_DROPPED, 0ULL, 0ULL, 0ULL);
|
||||
}
|
||||
|
||||
u64 cleonos_sys_kbd_hotkey_switches(void) {
|
||||
return cleonos_syscall(CLEONOS_SYSCALL_KBD_HOTKEY_SWITCHES, 0ULL, 0ULL, 0ULL);
|
||||
}
|
||||
@@ -30,6 +30,19 @@
|
||||
#define CLKS_SYSCALL_TTY_WRITE 24ULL
|
||||
#define CLKS_SYSCALL_TTY_WRITE_CHAR 25ULL
|
||||
#define CLKS_SYSCALL_KBD_GET_CHAR 26ULL
|
||||
#define CLKS_SYSCALL_FS_STAT_TYPE 27ULL
|
||||
#define CLKS_SYSCALL_FS_STAT_SIZE 28ULL
|
||||
#define CLKS_SYSCALL_FS_MKDIR 29ULL
|
||||
#define CLKS_SYSCALL_FS_WRITE 30ULL
|
||||
#define CLKS_SYSCALL_FS_APPEND 31ULL
|
||||
#define CLKS_SYSCALL_FS_REMOVE 32ULL
|
||||
#define CLKS_SYSCALL_LOG_JOURNAL_COUNT 33ULL
|
||||
#define CLKS_SYSCALL_LOG_JOURNAL_READ 34ULL
|
||||
#define CLKS_SYSCALL_KBD_BUFFERED 35ULL
|
||||
#define CLKS_SYSCALL_KBD_PUSHED 36ULL
|
||||
#define CLKS_SYSCALL_KBD_POPPED 37ULL
|
||||
#define CLKS_SYSCALL_KBD_DROPPED 38ULL
|
||||
#define CLKS_SYSCALL_KBD_HOTKEY_SWITCHES 39ULL
|
||||
|
||||
void clks_syscall_init(void);
|
||||
u64 clks_syscall_dispatch(void *frame_ptr);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include <clks/exec.h>
|
||||
#include <clks/fs.h>
|
||||
#include <clks/heap.h>
|
||||
#include <clks/interrupts.h>
|
||||
#include <clks/kelf.h>
|
||||
#include <clks/keyboard.h>
|
||||
@@ -15,7 +16,9 @@
|
||||
#define CLKS_SYSCALL_LOG_MAX_LEN 191U
|
||||
#define CLKS_SYSCALL_PATH_MAX 192U
|
||||
#define CLKS_SYSCALL_NAME_MAX 96U
|
||||
#define CLKS_SYSCALL_IO_MAX_LEN 512U
|
||||
#define CLKS_SYSCALL_TTY_MAX_LEN 512U
|
||||
#define CLKS_SYSCALL_FS_IO_MAX_LEN 65536U
|
||||
#define CLKS_SYSCALL_JOURNAL_MAX_LEN 256U
|
||||
|
||||
struct clks_syscall_frame {
|
||||
u64 rax;
|
||||
@@ -94,15 +97,15 @@ static u64 clks_syscall_log_write(u64 arg0, u64 arg1) {
|
||||
static u64 clks_syscall_tty_write(u64 arg0, u64 arg1) {
|
||||
const char *src = (const char *)arg0;
|
||||
u64 len = arg1;
|
||||
char buf[CLKS_SYSCALL_IO_MAX_LEN + 1U];
|
||||
char buf[CLKS_SYSCALL_TTY_MAX_LEN + 1U];
|
||||
u64 i;
|
||||
|
||||
if (src == CLKS_NULL || len == 0ULL) {
|
||||
return 0ULL;
|
||||
}
|
||||
|
||||
if (len > CLKS_SYSCALL_IO_MAX_LEN) {
|
||||
len = CLKS_SYSCALL_IO_MAX_LEN;
|
||||
if (len > CLKS_SYSCALL_TTY_MAX_LEN) {
|
||||
len = CLKS_SYSCALL_TTY_MAX_LEN;
|
||||
}
|
||||
|
||||
for (i = 0ULL; i < len; i++) {
|
||||
@@ -197,6 +200,139 @@ static u64 clks_syscall_exec_path(u64 arg0) {
|
||||
return status;
|
||||
}
|
||||
|
||||
static u64 clks_syscall_fs_stat_type(u64 arg0) {
|
||||
char path[CLKS_SYSCALL_PATH_MAX];
|
||||
struct clks_fs_node_info info;
|
||||
|
||||
if (clks_syscall_copy_user_string(arg0, path, sizeof(path)) == CLKS_FALSE) {
|
||||
return (u64)-1;
|
||||
}
|
||||
|
||||
if (clks_fs_stat(path, &info) == CLKS_FALSE) {
|
||||
return (u64)-1;
|
||||
}
|
||||
|
||||
return (u64)info.type;
|
||||
}
|
||||
|
||||
static u64 clks_syscall_fs_stat_size(u64 arg0) {
|
||||
char path[CLKS_SYSCALL_PATH_MAX];
|
||||
struct clks_fs_node_info info;
|
||||
|
||||
if (clks_syscall_copy_user_string(arg0, path, sizeof(path)) == CLKS_FALSE) {
|
||||
return (u64)-1;
|
||||
}
|
||||
|
||||
if (clks_fs_stat(path, &info) == CLKS_FALSE) {
|
||||
return (u64)-1;
|
||||
}
|
||||
|
||||
return info.size;
|
||||
}
|
||||
|
||||
static u64 clks_syscall_fs_mkdir(u64 arg0) {
|
||||
char path[CLKS_SYSCALL_PATH_MAX];
|
||||
|
||||
if (clks_syscall_copy_user_string(arg0, path, sizeof(path)) == CLKS_FALSE) {
|
||||
return 0ULL;
|
||||
}
|
||||
|
||||
return (clks_fs_mkdir(path) == CLKS_TRUE) ? 1ULL : 0ULL;
|
||||
}
|
||||
|
||||
static u64 clks_syscall_fs_write_common(u64 arg0, u64 arg1, u64 arg2, clks_bool append_mode) {
|
||||
char path[CLKS_SYSCALL_PATH_MAX];
|
||||
void *heap_copy = CLKS_NULL;
|
||||
const void *payload = CLKS_NULL;
|
||||
clks_bool ok;
|
||||
|
||||
if (clks_syscall_copy_user_string(arg0, path, sizeof(path)) == CLKS_FALSE) {
|
||||
return 0ULL;
|
||||
}
|
||||
|
||||
if (arg2 > CLKS_SYSCALL_FS_IO_MAX_LEN) {
|
||||
return 0ULL;
|
||||
}
|
||||
|
||||
if (arg2 > 0ULL) {
|
||||
if (arg1 == 0ULL) {
|
||||
return 0ULL;
|
||||
}
|
||||
|
||||
heap_copy = clks_kmalloc((usize)arg2);
|
||||
|
||||
if (heap_copy == CLKS_NULL) {
|
||||
return 0ULL;
|
||||
}
|
||||
|
||||
clks_memcpy(heap_copy, (const void *)arg1, (usize)arg2);
|
||||
payload = (const void *)heap_copy;
|
||||
}
|
||||
|
||||
if (append_mode == CLKS_TRUE) {
|
||||
ok = clks_fs_append(path, payload, arg2);
|
||||
} else {
|
||||
ok = clks_fs_write_all(path, payload, arg2);
|
||||
}
|
||||
|
||||
if (heap_copy != CLKS_NULL) {
|
||||
clks_kfree(heap_copy);
|
||||
}
|
||||
|
||||
return (ok == CLKS_TRUE) ? 1ULL : 0ULL;
|
||||
}
|
||||
|
||||
static u64 clks_syscall_fs_write(u64 arg0, u64 arg1, u64 arg2) {
|
||||
return clks_syscall_fs_write_common(arg0, arg1, arg2, CLKS_FALSE);
|
||||
}
|
||||
|
||||
static u64 clks_syscall_fs_append(u64 arg0, u64 arg1, u64 arg2) {
|
||||
return clks_syscall_fs_write_common(arg0, arg1, arg2, CLKS_TRUE);
|
||||
}
|
||||
|
||||
static u64 clks_syscall_fs_remove(u64 arg0) {
|
||||
char path[CLKS_SYSCALL_PATH_MAX];
|
||||
|
||||
if (clks_syscall_copy_user_string(arg0, path, sizeof(path)) == CLKS_FALSE) {
|
||||
return 0ULL;
|
||||
}
|
||||
|
||||
return (clks_fs_remove(path) == CLKS_TRUE) ? 1ULL : 0ULL;
|
||||
}
|
||||
|
||||
static u64 clks_syscall_log_journal_count(void) {
|
||||
return clks_log_journal_count();
|
||||
}
|
||||
|
||||
static u64 clks_syscall_log_journal_read(u64 arg0, u64 arg1, u64 arg2) {
|
||||
char line[CLKS_SYSCALL_JOURNAL_MAX_LEN];
|
||||
usize line_len;
|
||||
usize copy_len;
|
||||
|
||||
if (arg1 == 0ULL || arg2 == 0ULL) {
|
||||
return 0ULL;
|
||||
}
|
||||
|
||||
if (clks_log_journal_read(arg0, line, sizeof(line)) == CLKS_FALSE) {
|
||||
return 0ULL;
|
||||
}
|
||||
|
||||
line_len = clks_strlen(line) + 1U;
|
||||
copy_len = line_len;
|
||||
|
||||
if (copy_len > (usize)arg2) {
|
||||
copy_len = (usize)arg2;
|
||||
}
|
||||
|
||||
if (copy_len > sizeof(line)) {
|
||||
copy_len = sizeof(line);
|
||||
}
|
||||
|
||||
clks_memcpy((void *)arg1, line, copy_len);
|
||||
((char *)arg1)[copy_len - 1U] = '\0';
|
||||
return 1ULL;
|
||||
}
|
||||
|
||||
void clks_syscall_init(void) {
|
||||
clks_syscall_ready = CLKS_TRUE;
|
||||
clks_log(CLKS_LOG_INFO, "SYSCALL", "INT80 FRAMEWORK ONLINE");
|
||||
@@ -274,6 +410,32 @@ u64 clks_syscall_dispatch(void *frame_ptr) {
|
||||
return clks_syscall_tty_write_char(frame->rbx);
|
||||
case CLKS_SYSCALL_KBD_GET_CHAR:
|
||||
return clks_syscall_kbd_get_char();
|
||||
case CLKS_SYSCALL_FS_STAT_TYPE:
|
||||
return clks_syscall_fs_stat_type(frame->rbx);
|
||||
case CLKS_SYSCALL_FS_STAT_SIZE:
|
||||
return clks_syscall_fs_stat_size(frame->rbx);
|
||||
case CLKS_SYSCALL_FS_MKDIR:
|
||||
return clks_syscall_fs_mkdir(frame->rbx);
|
||||
case CLKS_SYSCALL_FS_WRITE:
|
||||
return clks_syscall_fs_write(frame->rbx, frame->rcx, frame->rdx);
|
||||
case CLKS_SYSCALL_FS_APPEND:
|
||||
return clks_syscall_fs_append(frame->rbx, frame->rcx, frame->rdx);
|
||||
case CLKS_SYSCALL_FS_REMOVE:
|
||||
return clks_syscall_fs_remove(frame->rbx);
|
||||
case CLKS_SYSCALL_LOG_JOURNAL_COUNT:
|
||||
return clks_syscall_log_journal_count();
|
||||
case CLKS_SYSCALL_LOG_JOURNAL_READ:
|
||||
return clks_syscall_log_journal_read(frame->rbx, frame->rcx, frame->rdx);
|
||||
case CLKS_SYSCALL_KBD_BUFFERED:
|
||||
return clks_keyboard_buffered_count();
|
||||
case CLKS_SYSCALL_KBD_PUSHED:
|
||||
return clks_keyboard_push_count();
|
||||
case CLKS_SYSCALL_KBD_POPPED:
|
||||
return clks_keyboard_pop_count();
|
||||
case CLKS_SYSCALL_KBD_DROPPED:
|
||||
return clks_keyboard_drop_count();
|
||||
case CLKS_SYSCALL_KBD_HOTKEY_SWITCHES:
|
||||
return clks_keyboard_hotkey_switch_count();
|
||||
default:
|
||||
return (u64)-1;
|
||||
}
|
||||
|
||||
@@ -24,6 +24,10 @@
|
||||
- `stage25.md`
|
||||
- `stage26.md`
|
||||
|
||||
- `syscall.md` (syscall ABI reference)
|
||||
|
||||
## Notes
|
||||
- Stage docs use a fixed template: goal, implementation, acceptance criteria, build targets, QEMU command, and debugging notes.
|
||||
- Stages 16~19 are currently not documented in this folder; add them later using the same template to keep history continuous.
|
||||
|
||||
|
||||
|
||||
316
docs/syscall.md
Normal file
316
docs/syscall.md
Normal file
@@ -0,0 +1,316 @@
|
||||
# CLeonOS Syscall 文档
|
||||
|
||||
本文档描述 CLeonOS 用户态通过 `int 0x80` 进入内核的 syscall ABI 与当前实现行为。
|
||||
|
||||
## 1. 调用约定(x86_64)
|
||||
|
||||
用户态统一通过:
|
||||
|
||||
```c
|
||||
u64 cleonos_syscall(u64 id, u64 arg0, u64 arg1, u64 arg2);
|
||||
```
|
||||
|
||||
寄存器约定:
|
||||
|
||||
- `rax` = syscall id
|
||||
- `rbx` = arg0
|
||||
- `rcx` = arg1
|
||||
- `rdx` = arg2
|
||||
- 返回值在 `rax`
|
||||
|
||||
中断向量:
|
||||
|
||||
- `int 0x80`
|
||||
|
||||
头文件位置:
|
||||
|
||||
- `cleonos/c/include/cleonos_syscall.h`
|
||||
|
||||
内核分发位置:
|
||||
|
||||
- `clks/kernel/syscall.c`
|
||||
|
||||
## 2. 全局返回规则
|
||||
|
||||
- 成功时通常返回非负值(如长度、计数、状态)。
|
||||
- 失败时多数接口返回 `0xFFFFFFFFFFFFFFFF`(即 `u64` 的 `-1`)。
|
||||
- 部分接口失败返回 `0`(例如 `FS_READ` / `FS_WRITE` / `FS_APPEND` / `FS_REMOVE` / `LOG_JOURNAL_READ`)。
|
||||
|
||||
## 3. 当前实现中的长度/路径限制
|
||||
|
||||
以下限制由内核 `clks/kernel/syscall.c` 当前实现决定:
|
||||
|
||||
- 日志写入 `LOG_WRITE`:最大拷贝 `191` 字节。
|
||||
- TTY 文本写入 `TTY_WRITE`:最大拷贝 `512` 字节。
|
||||
- 文件读取 `FS_READ`:最多读取 `min(file_size, buffer_size)` 字节。
|
||||
- 文件写入 `FS_WRITE` / `FS_APPEND`:单次最大 `65536` 字节。
|
||||
- log journal 行读取缓冲:`256` 字节。
|
||||
- 路径缓冲上限:`192` 字节(包含 `\0`)。
|
||||
- 文件名输出上限:`96` 字节(与 `CLEONOS_FS_NAME_MAX` 对齐)。
|
||||
|
||||
文件系统写入类 syscall 的权限限制:
|
||||
|
||||
- `FS_MKDIR` / `FS_WRITE` / `FS_APPEND` / `FS_REMOVE` 仅允许 `/temp` 树下路径。
|
||||
|
||||
## 4. Syscall 列表(0~39)
|
||||
|
||||
### 0 `CLEONOS_SYSCALL_LOG_WRITE`
|
||||
|
||||
- 参数:
|
||||
- `arg0`: `const char *message`
|
||||
- `arg1`: `u64 length`
|
||||
- 返回:实际写入长度
|
||||
- 说明:写入内核日志通道(tag 为 `SYSCALL`),长度会被截断到 191。
|
||||
|
||||
### 1 `CLEONOS_SYSCALL_TIMER_TICKS`
|
||||
|
||||
- 参数:无
|
||||
- 返回:系统 timer tick 计数
|
||||
|
||||
### 2 `CLEONOS_SYSCALL_TASK_COUNT`
|
||||
|
||||
- 参数:无
|
||||
- 返回:任务总数
|
||||
|
||||
### 3 `CLEONOS_SYSCALL_CUR_TASK`
|
||||
|
||||
- 参数:无
|
||||
- 返回:当前任务 ID
|
||||
|
||||
### 4 `CLEONOS_SYSCALL_SERVICE_COUNT`
|
||||
|
||||
- 参数:无
|
||||
- 返回:服务总数
|
||||
|
||||
### 5 `CLEONOS_SYSCALL_SERVICE_READY_COUNT`
|
||||
|
||||
- 参数:无
|
||||
- 返回:ready 服务数
|
||||
|
||||
### 6 `CLEONOS_SYSCALL_CONTEXT_SWITCHES`
|
||||
|
||||
- 参数:无
|
||||
- 返回:上下文切换计数
|
||||
|
||||
### 7 `CLEONOS_SYSCALL_KELF_COUNT`
|
||||
|
||||
- 参数:无
|
||||
- 返回:内核态 ELF 应用计数
|
||||
|
||||
### 8 `CLEONOS_SYSCALL_KELF_RUNS`
|
||||
|
||||
- 参数:无
|
||||
- 返回:内核态 ELF 累计运行次数
|
||||
|
||||
### 9 `CLEONOS_SYSCALL_FS_NODE_COUNT`
|
||||
|
||||
- 参数:无
|
||||
- 返回:VFS 节点总数
|
||||
|
||||
### 10 `CLEONOS_SYSCALL_FS_CHILD_COUNT`
|
||||
|
||||
- 参数:
|
||||
- `arg0`: `const char *dir_path`
|
||||
- 返回:子节点数量
|
||||
|
||||
### 11 `CLEONOS_SYSCALL_FS_GET_CHILD_NAME`
|
||||
|
||||
- 参数:
|
||||
- `arg0`: `const char *dir_path`
|
||||
- `arg1`: `u64 index`
|
||||
- `arg2`: `char *out_name`
|
||||
- 返回:成功 `1`,失败 `0`
|
||||
- 说明:最多写入 96 字节(含终止符)。
|
||||
|
||||
### 12 `CLEONOS_SYSCALL_FS_READ`
|
||||
|
||||
- 参数:
|
||||
- `arg0`: `const char *path`
|
||||
- `arg1`: `char *out_buffer`
|
||||
- `arg2`: `u64 buffer_size`
|
||||
- 返回:实际读取字节数,失败/文件空返回 `0`
|
||||
- 说明:不会自动追加 `\0`,调用方应自行处理文本终止。
|
||||
|
||||
### 13 `CLEONOS_SYSCALL_EXEC_PATH`
|
||||
|
||||
- 参数:
|
||||
- `arg0`: `const char *path`
|
||||
- 返回:
|
||||
- `-1`:请求失败
|
||||
- 其他:由内核执行器返回状态(通常 `0` 表示 accepted)
|
||||
|
||||
### 14 `CLEONOS_SYSCALL_EXEC_REQUESTS`
|
||||
|
||||
- 参数:无
|
||||
- 返回:执行请求累计数
|
||||
|
||||
### 15 `CLEONOS_SYSCALL_EXEC_SUCCESS`
|
||||
|
||||
- 参数:无
|
||||
- 返回:执行成功累计数
|
||||
|
||||
### 16 `CLEONOS_SYSCALL_USER_SHELL_READY`
|
||||
|
||||
- 参数:无
|
||||
- 返回:用户 shell ready(1/0)
|
||||
|
||||
### 17 `CLEONOS_SYSCALL_USER_EXEC_REQUESTED`
|
||||
|
||||
- 参数:无
|
||||
- 返回:是否发起过用户侧 exec 请求(1/0)
|
||||
|
||||
### 18 `CLEONOS_SYSCALL_USER_LAUNCH_TRIES`
|
||||
|
||||
- 参数:无
|
||||
- 返回:用户态启动尝试次数
|
||||
|
||||
### 19 `CLEONOS_SYSCALL_USER_LAUNCH_OK`
|
||||
|
||||
- 参数:无
|
||||
- 返回:用户态启动成功次数
|
||||
|
||||
### 20 `CLEONOS_SYSCALL_USER_LAUNCH_FAIL`
|
||||
|
||||
- 参数:无
|
||||
- 返回:用户态启动失败次数
|
||||
|
||||
### 21 `CLEONOS_SYSCALL_TTY_COUNT`
|
||||
|
||||
- 参数:无
|
||||
- 返回:TTY 总数
|
||||
|
||||
### 22 `CLEONOS_SYSCALL_TTY_ACTIVE`
|
||||
|
||||
- 参数:无
|
||||
- 返回:当前 active TTY 索引
|
||||
|
||||
### 23 `CLEONOS_SYSCALL_TTY_SWITCH`
|
||||
|
||||
- 参数:
|
||||
- `arg0`: `u64 tty_index`
|
||||
- 返回:切换后的 active TTY 索引
|
||||
|
||||
### 24 `CLEONOS_SYSCALL_TTY_WRITE`
|
||||
|
||||
- 参数:
|
||||
- `arg0`: `const char *text`
|
||||
- `arg1`: `u64 length`
|
||||
- 返回:实际写入长度
|
||||
- 说明:长度会被截断到 512。
|
||||
|
||||
### 25 `CLEONOS_SYSCALL_TTY_WRITE_CHAR`
|
||||
|
||||
- 参数:
|
||||
- `arg0`: `u64 ch`(低 8 位有效)
|
||||
- 返回:当前实现固定返回 `1`
|
||||
|
||||
### 26 `CLEONOS_SYSCALL_KBD_GET_CHAR`
|
||||
|
||||
- 参数:无
|
||||
- 返回:
|
||||
- 无输入时 `-1`
|
||||
- 有输入时返回字符值(低 8 位)
|
||||
|
||||
### 27 `CLEONOS_SYSCALL_FS_STAT_TYPE`
|
||||
|
||||
- 参数:
|
||||
- `arg0`: `const char *path`
|
||||
- 返回:`1=FILE`,`2=DIR`,失败 `-1`
|
||||
|
||||
### 28 `CLEONOS_SYSCALL_FS_STAT_SIZE`
|
||||
|
||||
- 参数:
|
||||
- `arg0`: `const char *path`
|
||||
- 返回:文件大小;目录通常为 `0`;失败 `-1`
|
||||
|
||||
### 29 `CLEONOS_SYSCALL_FS_MKDIR`
|
||||
|
||||
- 参数:
|
||||
- `arg0`: `const char *path`
|
||||
- 返回:成功 `1`,失败 `0`
|
||||
- 说明:仅允许 `/temp` 下创建目录。
|
||||
|
||||
### 30 `CLEONOS_SYSCALL_FS_WRITE`
|
||||
|
||||
- 参数:
|
||||
- `arg0`: `const char *path`
|
||||
- `arg1`: `const char *data`
|
||||
- `arg2`: `u64 size`
|
||||
- 返回:成功 `1`,失败 `0`
|
||||
- 说明:覆盖写;仅允许 `/temp` 下文件。
|
||||
|
||||
### 31 `CLEONOS_SYSCALL_FS_APPEND`
|
||||
|
||||
- 参数:
|
||||
- `arg0`: `const char *path`
|
||||
- `arg1`: `const char *data`
|
||||
- `arg2`: `u64 size`
|
||||
- 返回:成功 `1`,失败 `0`
|
||||
- 说明:追加写;仅允许 `/temp` 下文件。
|
||||
|
||||
### 32 `CLEONOS_SYSCALL_FS_REMOVE`
|
||||
|
||||
- 参数:
|
||||
- `arg0`: `const char *path`
|
||||
- 返回:成功 `1`,失败 `0`
|
||||
- 说明:仅允许 `/temp` 下删除;目录需为空。
|
||||
|
||||
### 33 `CLEONOS_SYSCALL_LOG_JOURNAL_COUNT`
|
||||
|
||||
- 参数:无
|
||||
- 返回:日志 journal 条目数量
|
||||
|
||||
### 34 `CLEONOS_SYSCALL_LOG_JOURNAL_READ`
|
||||
|
||||
- 参数:
|
||||
- `arg0`: `u64 index_from_oldest`
|
||||
- `arg1`: `char *out_line`
|
||||
- `arg2`: `u64 out_size`
|
||||
- 返回:成功 `1`,失败 `0`
|
||||
- 说明:内核会保证输出字符串有 `\0` 终止。
|
||||
|
||||
### 35 `CLEONOS_SYSCALL_KBD_BUFFERED`
|
||||
|
||||
- 参数:无
|
||||
- 返回:当前键盘队列中的字符数量
|
||||
|
||||
### 36 `CLEONOS_SYSCALL_KBD_PUSHED`
|
||||
|
||||
- 参数:无
|
||||
- 返回:键盘累计入队计数
|
||||
|
||||
### 37 `CLEONOS_SYSCALL_KBD_POPPED`
|
||||
|
||||
- 参数:无
|
||||
- 返回:键盘累计出队计数
|
||||
|
||||
### 38 `CLEONOS_SYSCALL_KBD_DROPPED`
|
||||
|
||||
- 参数:无
|
||||
- 返回:键盘队列溢出丢弃计数
|
||||
|
||||
### 39 `CLEONOS_SYSCALL_KBD_HOTKEY_SWITCHES`
|
||||
|
||||
- 参数:无
|
||||
- 返回:ALT+F1..F4 热键切换计数
|
||||
|
||||
## 5. 用户态封装函数
|
||||
|
||||
用户态封装位于:
|
||||
|
||||
- `cleonos/c/src/syscall.c`
|
||||
|
||||
常用封装示例:
|
||||
|
||||
- `cleonos_sys_fs_read()`
|
||||
- `cleonos_sys_fs_write()` / `cleonos_sys_fs_append()` / `cleonos_sys_fs_remove()`
|
||||
- `cleonos_sys_log_journal_count()` / `cleonos_sys_log_journal_read()`
|
||||
- `cleonos_sys_exec_path()`
|
||||
- `cleonos_sys_tty_write()`
|
||||
- `cleonos_sys_kbd_get_char()` / `cleonos_sys_kbd_buffered()`
|
||||
|
||||
## 6. 开发注意事项
|
||||
|
||||
- 传入的字符串/缓冲指针目前按“同地址空间可直接访问”模型处理,后续若引入严格用户态地址隔离,需要补充用户内存校验。
|
||||
- `FS_READ` 不保证文本终止符;读取文本请预留 1 字节并手动 `buf[n] = '\0'`。
|
||||
- `FS_WRITE`/`FS_APPEND` 仅允许 `/temp`,并有单次长度上限。
|
||||
@@ -3,9 +3,3 @@
|
||||
|
||||
help
|
||||
stats
|
||||
ls /
|
||||
ls /shell
|
||||
ls /system
|
||||
cat /README.txt
|
||||
run /hello.elf
|
||||
stats
|
||||
|
||||
@@ -73,6 +73,19 @@ SYS_TTY_SWITCH = 23
|
||||
SYS_TTY_WRITE = 24
|
||||
SYS_TTY_WRITE_CHAR = 25
|
||||
SYS_KBD_GET_CHAR = 26
|
||||
SYS_FS_STAT_TYPE = 27
|
||||
SYS_FS_STAT_SIZE = 28
|
||||
SYS_FS_MKDIR = 29
|
||||
SYS_FS_WRITE = 30
|
||||
SYS_FS_APPEND = 31
|
||||
SYS_FS_REMOVE = 32
|
||||
SYS_LOG_JOURNAL_COUNT = 33
|
||||
SYS_LOG_JOURNAL_READ = 34
|
||||
SYS_KBD_BUFFERED = 35
|
||||
SYS_KBD_PUSHED = 36
|
||||
SYS_KBD_POPPED = 37
|
||||
SYS_KBD_DROPPED = 38
|
||||
SYS_KBD_HOTKEY_SWITCHES = 39
|
||||
|
||||
|
||||
def u64(value: int) -> int:
|
||||
@@ -126,22 +139,57 @@ class SharedKernelState:
|
||||
tty_active: int = 0
|
||||
kbd_queue: Deque[int] = field(default_factory=collections.deque)
|
||||
kbd_lock: threading.Lock = field(default_factory=threading.Lock)
|
||||
kbd_queue_cap: int = 256
|
||||
kbd_drop_count: int = 0
|
||||
kbd_push_count: int = 0
|
||||
kbd_pop_count: int = 0
|
||||
kbd_hotkey_switches: int = 0
|
||||
log_journal_cap: int = 256
|
||||
log_journal: Deque[str] = field(default_factory=lambda: collections.deque(maxlen=256))
|
||||
fs_write_max: int = 65536
|
||||
|
||||
def timer_ticks(self) -> int:
|
||||
return (time.monotonic_ns() - self.start_ns) // 1_000_000
|
||||
|
||||
def push_key(self, key: int) -> None:
|
||||
with self.kbd_lock:
|
||||
if len(self.kbd_queue) >= 1024:
|
||||
if len(self.kbd_queue) >= self.kbd_queue_cap:
|
||||
self.kbd_queue.popleft()
|
||||
self.kbd_drop_count = u64(self.kbd_drop_count + 1)
|
||||
self.kbd_queue.append(key & 0xFF)
|
||||
self.kbd_push_count = u64(self.kbd_push_count + 1)
|
||||
|
||||
def pop_key(self) -> Optional[int]:
|
||||
with self.kbd_lock:
|
||||
if not self.kbd_queue:
|
||||
return None
|
||||
self.kbd_pop_count = u64(self.kbd_pop_count + 1)
|
||||
return self.kbd_queue.popleft()
|
||||
|
||||
def buffered_count(self) -> int:
|
||||
with self.kbd_lock:
|
||||
return len(self.kbd_queue)
|
||||
|
||||
def log_journal_push(self, text: str) -> None:
|
||||
if text is None:
|
||||
return
|
||||
|
||||
normalized = text.replace("\r", "")
|
||||
lines = normalized.split("\n")
|
||||
|
||||
for line in lines:
|
||||
if len(line) > 255:
|
||||
line = line[:255]
|
||||
self.log_journal.append(line)
|
||||
|
||||
def log_journal_count(self) -> int:
|
||||
return len(self.log_journal)
|
||||
|
||||
def log_journal_read(self, index_from_oldest: int) -> Optional[str]:
|
||||
if index_from_oldest < 0 or index_from_oldest >= len(self.log_journal):
|
||||
return None
|
||||
return list(self.log_journal)[index_from_oldest]
|
||||
|
||||
|
||||
class InputPump:
|
||||
def __init__(self, state: SharedKernelState) -> None:
|
||||
@@ -321,7 +369,9 @@ class CLeonOSWineNative:
|
||||
def _dispatch_syscall(self, uc: Uc, sid: int, arg0: int, arg1: int, arg2: int) -> int:
|
||||
if sid == SYS_LOG_WRITE:
|
||||
data = self._read_guest_bytes(uc, arg0, arg1)
|
||||
self._host_write(data.decode("utf-8", errors="replace"))
|
||||
text = data.decode("utf-8", errors="replace")
|
||||
self._host_write(text)
|
||||
self.state.log_journal_push(text)
|
||||
return len(data)
|
||||
if sid == SYS_TIMER_TICKS:
|
||||
return self.state.timer_ticks()
|
||||
@@ -371,7 +421,7 @@ class CLeonOSWineNative:
|
||||
if arg0 >= self.state.tty_count:
|
||||
return u64_neg1()
|
||||
self.state.tty_active = int(arg0)
|
||||
return 0
|
||||
return self.state.tty_active
|
||||
if sid == SYS_TTY_WRITE:
|
||||
data = self._read_guest_bytes(uc, arg0, arg1)
|
||||
self._host_write(data.decode("utf-8", errors="replace"))
|
||||
@@ -382,10 +432,36 @@ class CLeonOSWineNative:
|
||||
self._host_write("\b \b")
|
||||
else:
|
||||
self._host_write(ch)
|
||||
return 0
|
||||
return 1
|
||||
if sid == SYS_KBD_GET_CHAR:
|
||||
key = self.state.pop_key()
|
||||
return u64_neg1() if key is None else key
|
||||
if sid == SYS_FS_STAT_TYPE:
|
||||
return self._fs_stat_type(uc, arg0)
|
||||
if sid == SYS_FS_STAT_SIZE:
|
||||
return self._fs_stat_size(uc, arg0)
|
||||
if sid == SYS_FS_MKDIR:
|
||||
return self._fs_mkdir(uc, arg0)
|
||||
if sid == SYS_FS_WRITE:
|
||||
return self._fs_write(uc, arg0, arg1, arg2)
|
||||
if sid == SYS_FS_APPEND:
|
||||
return self._fs_append(uc, arg0, arg1, arg2)
|
||||
if sid == SYS_FS_REMOVE:
|
||||
return self._fs_remove(uc, arg0)
|
||||
if sid == SYS_LOG_JOURNAL_COUNT:
|
||||
return self.state.log_journal_count()
|
||||
if sid == SYS_LOG_JOURNAL_READ:
|
||||
return self._log_journal_read(uc, arg0, arg1, arg2)
|
||||
if sid == SYS_KBD_BUFFERED:
|
||||
return self.state.buffered_count()
|
||||
if sid == SYS_KBD_PUSHED:
|
||||
return self.state.kbd_push_count
|
||||
if sid == SYS_KBD_POPPED:
|
||||
return self.state.kbd_pop_count
|
||||
if sid == SYS_KBD_DROPPED:
|
||||
return self.state.kbd_drop_count
|
||||
if sid == SYS_KBD_HOTKEY_SWITCHES:
|
||||
return self.state.kbd_hotkey_switches
|
||||
|
||||
return u64_neg1()
|
||||
|
||||
@@ -603,6 +679,132 @@ class CLeonOSWineNative:
|
||||
return 0
|
||||
return len(data) if self._write_guest_bytes(uc, out_ptr, data) else 0
|
||||
|
||||
def _fs_stat_type(self, uc: Uc, path_ptr: int) -> int:
|
||||
path = self._read_guest_cstring(uc, path_ptr)
|
||||
host_path = self._guest_to_host(path, must_exist=True)
|
||||
if host_path is None:
|
||||
return u64_neg1()
|
||||
if host_path.is_dir():
|
||||
return 2
|
||||
if host_path.is_file():
|
||||
return 1
|
||||
return u64_neg1()
|
||||
|
||||
def _fs_stat_size(self, uc: Uc, path_ptr: int) -> int:
|
||||
path = self._read_guest_cstring(uc, path_ptr)
|
||||
host_path = self._guest_to_host(path, must_exist=True)
|
||||
if host_path is None:
|
||||
return u64_neg1()
|
||||
if host_path.is_dir():
|
||||
return 0
|
||||
if host_path.is_file():
|
||||
try:
|
||||
return host_path.stat().st_size
|
||||
except Exception:
|
||||
return u64_neg1()
|
||||
return u64_neg1()
|
||||
|
||||
@staticmethod
|
||||
def _guest_path_is_under_temp(path: str) -> bool:
|
||||
return path == "/temp" or path.startswith("/temp/")
|
||||
|
||||
def _fs_mkdir(self, uc: Uc, path_ptr: int) -> int:
|
||||
path = self._normalize_guest_path(self._read_guest_cstring(uc, path_ptr))
|
||||
if not self._guest_path_is_under_temp(path):
|
||||
return 0
|
||||
|
||||
host_path = self._guest_to_host(path, must_exist=False)
|
||||
if host_path is None:
|
||||
return 0
|
||||
|
||||
if host_path.exists() and host_path.is_file():
|
||||
return 0
|
||||
|
||||
try:
|
||||
host_path.mkdir(parents=True, exist_ok=True)
|
||||
return 1
|
||||
except Exception:
|
||||
return 0
|
||||
|
||||
def _fs_write_common(self, uc: Uc, path_ptr: int, data_ptr: int, size: int, append_mode: bool) -> int:
|
||||
path = self._normalize_guest_path(self._read_guest_cstring(uc, path_ptr))
|
||||
|
||||
if not self._guest_path_is_under_temp(path) or path == "/temp":
|
||||
return 0
|
||||
|
||||
if size < 0 or size > self.state.fs_write_max:
|
||||
return 0
|
||||
|
||||
host_path = self._guest_to_host(path, must_exist=False)
|
||||
if host_path is None:
|
||||
return 0
|
||||
|
||||
if host_path.exists() and host_path.is_dir():
|
||||
return 0
|
||||
|
||||
data = b""
|
||||
if size > 0:
|
||||
if data_ptr == 0:
|
||||
return 0
|
||||
data = self._read_guest_bytes(uc, data_ptr, size)
|
||||
if len(data) != int(size):
|
||||
return 0
|
||||
|
||||
try:
|
||||
host_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
mode = "ab" if append_mode else "wb"
|
||||
with host_path.open(mode) as fh:
|
||||
if data:
|
||||
fh.write(data)
|
||||
return 1
|
||||
except Exception:
|
||||
return 0
|
||||
|
||||
def _fs_write(self, uc: Uc, path_ptr: int, data_ptr: int, size: int) -> int:
|
||||
return self._fs_write_common(uc, path_ptr, data_ptr, size, append_mode=False)
|
||||
|
||||
def _fs_append(self, uc: Uc, path_ptr: int, data_ptr: int, size: int) -> int:
|
||||
return self._fs_write_common(uc, path_ptr, data_ptr, size, append_mode=True)
|
||||
|
||||
def _fs_remove(self, uc: Uc, path_ptr: int) -> int:
|
||||
path = self._normalize_guest_path(self._read_guest_cstring(uc, path_ptr))
|
||||
|
||||
if not self._guest_path_is_under_temp(path) or path == "/temp":
|
||||
return 0
|
||||
|
||||
host_path = self._guest_to_host(path, must_exist=True)
|
||||
if host_path is None:
|
||||
return 0
|
||||
|
||||
try:
|
||||
if host_path.is_dir():
|
||||
if any(host_path.iterdir()):
|
||||
return 0
|
||||
host_path.rmdir()
|
||||
else:
|
||||
host_path.unlink()
|
||||
return 1
|
||||
except Exception:
|
||||
return 0
|
||||
|
||||
def _log_journal_read(self, uc: Uc, index_from_oldest: int, out_ptr: int, out_size: int) -> int:
|
||||
if out_ptr == 0 or out_size == 0:
|
||||
return 0
|
||||
|
||||
line = self.state.log_journal_read(int(index_from_oldest))
|
||||
if line is None:
|
||||
return 0
|
||||
|
||||
encoded = line.encode("utf-8", errors="replace")
|
||||
max_payload = int(out_size) - 1
|
||||
if max_payload < 0:
|
||||
return 0
|
||||
|
||||
if len(encoded) > max_payload:
|
||||
encoded = encoded[:max_payload]
|
||||
|
||||
return 1 if self._write_guest_bytes(uc, out_ptr, encoded + b"\x00") else 0
|
||||
|
||||
def _exec_path(self, uc: Uc, path_ptr: int) -> int:
|
||||
path = self._read_guest_cstring(uc, path_ptr)
|
||||
guest_path = self._normalize_guest_path(path)
|
||||
|
||||
Reference in New Issue
Block a user