mirror of
https://github.com/Leonmmcoset/cleonos.git
synced 2026-04-21 18:44:01 +00:00
USC安全系统
This commit is contained in:
@@ -181,6 +181,19 @@ cl_set_bool_cache(CLEONOS_CLKS_ENABLE_TTY_READY_LOG ON "Print TTY ready logs")
|
|||||||
cl_set_bool_cache(CLEONOS_CLKS_ENABLE_IDLE_DEBUG_LOG ON "Print idle loop debug log")
|
cl_set_bool_cache(CLEONOS_CLKS_ENABLE_IDLE_DEBUG_LOG ON "Print idle loop debug log")
|
||||||
cl_set_bool_cache(CLEONOS_CLKS_ENABLE_PROCFS ON "Enable virtual /proc procfs syscall layer")
|
cl_set_bool_cache(CLEONOS_CLKS_ENABLE_PROCFS ON "Enable virtual /proc procfs syscall layer")
|
||||||
cl_set_bool_cache(CLEONOS_CLKS_ENABLE_EXEC_SERIAL_LOG ON "Enable EXEC info logs on serial output")
|
cl_set_bool_cache(CLEONOS_CLKS_ENABLE_EXEC_SERIAL_LOG ON "Enable EXEC info logs on serial output")
|
||||||
|
cl_set_bool_cache(CLEONOS_CLKS_ENABLE_USC ON "Enable UserSafeController dangerous syscall confirmations")
|
||||||
|
cl_set_bool_cache(CLEONOS_CLKS_ENABLE_USC_SC_FS_MKDIR ON "USC intercept policy: FS_MKDIR")
|
||||||
|
cl_set_bool_cache(CLEONOS_CLKS_ENABLE_USC_SC_FS_WRITE ON "USC intercept policy: FS_WRITE")
|
||||||
|
cl_set_bool_cache(CLEONOS_CLKS_ENABLE_USC_SC_FS_APPEND ON "USC intercept policy: FS_APPEND")
|
||||||
|
cl_set_bool_cache(CLEONOS_CLKS_ENABLE_USC_SC_FS_REMOVE ON "USC intercept policy: FS_REMOVE")
|
||||||
|
cl_set_bool_cache(CLEONOS_CLKS_ENABLE_USC_SC_EXEC_PATH ON "USC intercept policy: EXEC_PATH")
|
||||||
|
cl_set_bool_cache(CLEONOS_CLKS_ENABLE_USC_SC_EXEC_PATHV ON "USC intercept policy: EXEC_PATHV")
|
||||||
|
cl_set_bool_cache(CLEONOS_CLKS_ENABLE_USC_SC_EXEC_PATHV_IO ON "USC intercept policy: EXEC_PATHV_IO")
|
||||||
|
cl_set_bool_cache(CLEONOS_CLKS_ENABLE_USC_SC_SPAWN_PATH ON "USC intercept policy: SPAWN_PATH")
|
||||||
|
cl_set_bool_cache(CLEONOS_CLKS_ENABLE_USC_SC_SPAWN_PATHV ON "USC intercept policy: SPAWN_PATHV")
|
||||||
|
cl_set_bool_cache(CLEONOS_CLKS_ENABLE_USC_SC_PROC_KILL ON "USC intercept policy: PROC_KILL")
|
||||||
|
cl_set_bool_cache(CLEONOS_CLKS_ENABLE_USC_SC_SHUTDOWN ON "USC intercept policy: SHUTDOWN")
|
||||||
|
cl_set_bool_cache(CLEONOS_CLKS_ENABLE_USC_SC_RESTART ON "USC intercept policy: RESTART")
|
||||||
cl_set_bool_cache(CLEONOS_CLKS_ENABLE_KBD_TTY_SWITCH_HOTKEY ON "Enable ALT+F1..F4 TTY switch hotkey")
|
cl_set_bool_cache(CLEONOS_CLKS_ENABLE_KBD_TTY_SWITCH_HOTKEY ON "Enable ALT+F1..F4 TTY switch hotkey")
|
||||||
cl_set_bool_cache(CLEONOS_CLKS_ENABLE_KBD_CTRL_SHORTCUTS ON "Enable Ctrl+A/C/V keyboard shortcuts")
|
cl_set_bool_cache(CLEONOS_CLKS_ENABLE_KBD_CTRL_SHORTCUTS ON "Enable Ctrl+A/C/V keyboard shortcuts")
|
||||||
cl_set_bool_cache(CLEONOS_CLKS_ENABLE_KBD_FORCE_STOP_HOTKEY ON "Enable Ctrl+Alt+C force-stop hotkey")
|
cl_set_bool_cache(CLEONOS_CLKS_ENABLE_KBD_FORCE_STOP_HOTKEY ON "Enable Ctrl+Alt+C force-stop hotkey")
|
||||||
@@ -214,6 +227,19 @@ cl_bool_to_int(CLEONOS_CLKS_ENABLE_TTY_READY_LOG CLKS_CFG_TTY_READY_LOG_INT)
|
|||||||
cl_bool_to_int(CLEONOS_CLKS_ENABLE_IDLE_DEBUG_LOG CLKS_CFG_IDLE_DEBUG_LOG_INT)
|
cl_bool_to_int(CLEONOS_CLKS_ENABLE_IDLE_DEBUG_LOG CLKS_CFG_IDLE_DEBUG_LOG_INT)
|
||||||
cl_bool_to_int(CLEONOS_CLKS_ENABLE_PROCFS CLKS_CFG_PROCFS_INT)
|
cl_bool_to_int(CLEONOS_CLKS_ENABLE_PROCFS CLKS_CFG_PROCFS_INT)
|
||||||
cl_bool_to_int(CLEONOS_CLKS_ENABLE_EXEC_SERIAL_LOG CLKS_CFG_EXEC_SERIAL_LOG_INT)
|
cl_bool_to_int(CLEONOS_CLKS_ENABLE_EXEC_SERIAL_LOG CLKS_CFG_EXEC_SERIAL_LOG_INT)
|
||||||
|
cl_bool_to_int(CLEONOS_CLKS_ENABLE_USC CLKS_CFG_USC_INT)
|
||||||
|
cl_bool_to_int(CLEONOS_CLKS_ENABLE_USC_SC_FS_MKDIR CLKS_CFG_USC_SC_FS_MKDIR_INT)
|
||||||
|
cl_bool_to_int(CLEONOS_CLKS_ENABLE_USC_SC_FS_WRITE CLKS_CFG_USC_SC_FS_WRITE_INT)
|
||||||
|
cl_bool_to_int(CLEONOS_CLKS_ENABLE_USC_SC_FS_APPEND CLKS_CFG_USC_SC_FS_APPEND_INT)
|
||||||
|
cl_bool_to_int(CLEONOS_CLKS_ENABLE_USC_SC_FS_REMOVE CLKS_CFG_USC_SC_FS_REMOVE_INT)
|
||||||
|
cl_bool_to_int(CLEONOS_CLKS_ENABLE_USC_SC_EXEC_PATH CLKS_CFG_USC_SC_EXEC_PATH_INT)
|
||||||
|
cl_bool_to_int(CLEONOS_CLKS_ENABLE_USC_SC_EXEC_PATHV CLKS_CFG_USC_SC_EXEC_PATHV_INT)
|
||||||
|
cl_bool_to_int(CLEONOS_CLKS_ENABLE_USC_SC_EXEC_PATHV_IO CLKS_CFG_USC_SC_EXEC_PATHV_IO_INT)
|
||||||
|
cl_bool_to_int(CLEONOS_CLKS_ENABLE_USC_SC_SPAWN_PATH CLKS_CFG_USC_SC_SPAWN_PATH_INT)
|
||||||
|
cl_bool_to_int(CLEONOS_CLKS_ENABLE_USC_SC_SPAWN_PATHV CLKS_CFG_USC_SC_SPAWN_PATHV_INT)
|
||||||
|
cl_bool_to_int(CLEONOS_CLKS_ENABLE_USC_SC_PROC_KILL CLKS_CFG_USC_SC_PROC_KILL_INT)
|
||||||
|
cl_bool_to_int(CLEONOS_CLKS_ENABLE_USC_SC_SHUTDOWN CLKS_CFG_USC_SC_SHUTDOWN_INT)
|
||||||
|
cl_bool_to_int(CLEONOS_CLKS_ENABLE_USC_SC_RESTART CLKS_CFG_USC_SC_RESTART_INT)
|
||||||
cl_bool_to_int(CLEONOS_CLKS_ENABLE_KBD_TTY_SWITCH_HOTKEY CLKS_CFG_KBD_TTY_SWITCH_HOTKEY_INT)
|
cl_bool_to_int(CLEONOS_CLKS_ENABLE_KBD_TTY_SWITCH_HOTKEY CLKS_CFG_KBD_TTY_SWITCH_HOTKEY_INT)
|
||||||
cl_bool_to_int(CLEONOS_CLKS_ENABLE_KBD_CTRL_SHORTCUTS CLKS_CFG_KBD_CTRL_SHORTCUTS_INT)
|
cl_bool_to_int(CLEONOS_CLKS_ENABLE_KBD_CTRL_SHORTCUTS CLKS_CFG_KBD_CTRL_SHORTCUTS_INT)
|
||||||
cl_bool_to_int(CLEONOS_CLKS_ENABLE_KBD_FORCE_STOP_HOTKEY CLKS_CFG_KBD_FORCE_STOP_HOTKEY_INT)
|
cl_bool_to_int(CLEONOS_CLKS_ENABLE_KBD_FORCE_STOP_HOTKEY CLKS_CFG_KBD_FORCE_STOP_HOTKEY_INT)
|
||||||
@@ -261,6 +287,19 @@ set(ARCH_CFLAGS
|
|||||||
"-DCLKS_CFG_IDLE_DEBUG_LOG=${CLKS_CFG_IDLE_DEBUG_LOG_INT}"
|
"-DCLKS_CFG_IDLE_DEBUG_LOG=${CLKS_CFG_IDLE_DEBUG_LOG_INT}"
|
||||||
"-DCLKS_CFG_PROCFS=${CLKS_CFG_PROCFS_INT}"
|
"-DCLKS_CFG_PROCFS=${CLKS_CFG_PROCFS_INT}"
|
||||||
"-DCLKS_CFG_EXEC_SERIAL_LOG=${CLKS_CFG_EXEC_SERIAL_LOG_INT}"
|
"-DCLKS_CFG_EXEC_SERIAL_LOG=${CLKS_CFG_EXEC_SERIAL_LOG_INT}"
|
||||||
|
"-DCLKS_CFG_USC=${CLKS_CFG_USC_INT}"
|
||||||
|
"-DCLKS_CFG_USC_SC_FS_MKDIR=${CLKS_CFG_USC_SC_FS_MKDIR_INT}"
|
||||||
|
"-DCLKS_CFG_USC_SC_FS_WRITE=${CLKS_CFG_USC_SC_FS_WRITE_INT}"
|
||||||
|
"-DCLKS_CFG_USC_SC_FS_APPEND=${CLKS_CFG_USC_SC_FS_APPEND_INT}"
|
||||||
|
"-DCLKS_CFG_USC_SC_FS_REMOVE=${CLKS_CFG_USC_SC_FS_REMOVE_INT}"
|
||||||
|
"-DCLKS_CFG_USC_SC_EXEC_PATH=${CLKS_CFG_USC_SC_EXEC_PATH_INT}"
|
||||||
|
"-DCLKS_CFG_USC_SC_EXEC_PATHV=${CLKS_CFG_USC_SC_EXEC_PATHV_INT}"
|
||||||
|
"-DCLKS_CFG_USC_SC_EXEC_PATHV_IO=${CLKS_CFG_USC_SC_EXEC_PATHV_IO_INT}"
|
||||||
|
"-DCLKS_CFG_USC_SC_SPAWN_PATH=${CLKS_CFG_USC_SC_SPAWN_PATH_INT}"
|
||||||
|
"-DCLKS_CFG_USC_SC_SPAWN_PATHV=${CLKS_CFG_USC_SC_SPAWN_PATHV_INT}"
|
||||||
|
"-DCLKS_CFG_USC_SC_PROC_KILL=${CLKS_CFG_USC_SC_PROC_KILL_INT}"
|
||||||
|
"-DCLKS_CFG_USC_SC_SHUTDOWN=${CLKS_CFG_USC_SC_SHUTDOWN_INT}"
|
||||||
|
"-DCLKS_CFG_USC_SC_RESTART=${CLKS_CFG_USC_SC_RESTART_INT}"
|
||||||
"-DCLKS_CFG_KBD_TTY_SWITCH_HOTKEY=${CLKS_CFG_KBD_TTY_SWITCH_HOTKEY_INT}"
|
"-DCLKS_CFG_KBD_TTY_SWITCH_HOTKEY=${CLKS_CFG_KBD_TTY_SWITCH_HOTKEY_INT}"
|
||||||
"-DCLKS_CFG_KBD_CTRL_SHORTCUTS=${CLKS_CFG_KBD_CTRL_SHORTCUTS_INT}"
|
"-DCLKS_CFG_KBD_CTRL_SHORTCUTS=${CLKS_CFG_KBD_CTRL_SHORTCUTS_INT}"
|
||||||
"-DCLKS_CFG_KBD_FORCE_STOP_HOTKEY=${CLKS_CFG_KBD_FORCE_STOP_HOTKEY_INT}"
|
"-DCLKS_CFG_KBD_FORCE_STOP_HOTKEY=${CLKS_CFG_KBD_FORCE_STOP_HOTKEY_INT}"
|
||||||
@@ -344,6 +383,15 @@ list(REMOVE_DUPLICATES ASM_SOURCES)
|
|||||||
list(SORT C_SOURCES)
|
list(SORT C_SOURCES)
|
||||||
list(SORT ASM_SOURCES)
|
list(SORT ASM_SOURCES)
|
||||||
|
|
||||||
|
set(CLKS_BOOT_LIMINE_SOURCE "clks/kernel/boot/limine/limine_requests.c")
|
||||||
|
if(EXISTS "${CMAKE_SOURCE_DIR}/${CLKS_BOOT_LIMINE_SOURCE}")
|
||||||
|
list(APPEND C_SOURCES "${CLKS_BOOT_LIMINE_SOURCE}")
|
||||||
|
list(REMOVE_DUPLICATES C_SOURCES)
|
||||||
|
list(SORT C_SOURCES)
|
||||||
|
else()
|
||||||
|
cl_log_error("missing required boot source: ${CLKS_BOOT_LIMINE_SOURCE}")
|
||||||
|
endif()
|
||||||
|
|
||||||
file(GLOB_RECURSE KERNEL_INC_SOURCES_ABS CONFIGURE_DEPENDS
|
file(GLOB_RECURSE KERNEL_INC_SOURCES_ABS CONFIGURE_DEPENDS
|
||||||
"${CMAKE_SOURCE_DIR}/clks/**/*.inc"
|
"${CMAKE_SOURCE_DIR}/clks/**/*.inc"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -34,11 +34,64 @@
|
|||||||
#define CLKS_SYSCALL_KERNEL_ADDR_BASE 0xFFFF800000000000ULL
|
#define CLKS_SYSCALL_KERNEL_ADDR_BASE 0xFFFF800000000000ULL
|
||||||
#define CLKS_SYSCALL_STATS_MAX_ID CLKS_SYSCALL_EXEC_PATHV_IO
|
#define CLKS_SYSCALL_STATS_MAX_ID CLKS_SYSCALL_EXEC_PATHV_IO
|
||||||
#define CLKS_SYSCALL_STATS_RING_SIZE 256U
|
#define CLKS_SYSCALL_STATS_RING_SIZE 256U
|
||||||
|
#define CLKS_SYSCALL_USC_MAX_ALLOWED_APPS 64U
|
||||||
|
|
||||||
#ifndef CLKS_CFG_PROCFS
|
#ifndef CLKS_CFG_PROCFS
|
||||||
#define CLKS_CFG_PROCFS 1
|
#define CLKS_CFG_PROCFS 1
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef CLKS_CFG_USC
|
||||||
|
#define CLKS_CFG_USC 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef CLKS_CFG_USC_SC_FS_MKDIR
|
||||||
|
#define CLKS_CFG_USC_SC_FS_MKDIR 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef CLKS_CFG_USC_SC_FS_WRITE
|
||||||
|
#define CLKS_CFG_USC_SC_FS_WRITE 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef CLKS_CFG_USC_SC_FS_APPEND
|
||||||
|
#define CLKS_CFG_USC_SC_FS_APPEND 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef CLKS_CFG_USC_SC_FS_REMOVE
|
||||||
|
#define CLKS_CFG_USC_SC_FS_REMOVE 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef CLKS_CFG_USC_SC_EXEC_PATH
|
||||||
|
#define CLKS_CFG_USC_SC_EXEC_PATH 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef CLKS_CFG_USC_SC_EXEC_PATHV
|
||||||
|
#define CLKS_CFG_USC_SC_EXEC_PATHV 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef CLKS_CFG_USC_SC_EXEC_PATHV_IO
|
||||||
|
#define CLKS_CFG_USC_SC_EXEC_PATHV_IO 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef CLKS_CFG_USC_SC_SPAWN_PATH
|
||||||
|
#define CLKS_CFG_USC_SC_SPAWN_PATH 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef CLKS_CFG_USC_SC_SPAWN_PATHV
|
||||||
|
#define CLKS_CFG_USC_SC_SPAWN_PATHV 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef CLKS_CFG_USC_SC_PROC_KILL
|
||||||
|
#define CLKS_CFG_USC_SC_PROC_KILL 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef CLKS_CFG_USC_SC_SHUTDOWN
|
||||||
|
#define CLKS_CFG_USC_SC_SHUTDOWN 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef CLKS_CFG_USC_SC_RESTART
|
||||||
|
#define CLKS_CFG_USC_SC_RESTART 1
|
||||||
|
#endif
|
||||||
|
|
||||||
struct clks_syscall_frame {
|
struct clks_syscall_frame {
|
||||||
u64 rax;
|
u64 rax;
|
||||||
u64 rbx;
|
u64 rbx;
|
||||||
@@ -92,6 +145,10 @@ static u64 clks_syscall_stats_recent_id_count[CLKS_SYSCALL_STATS_MAX_ID + 1ULL];
|
|||||||
static u16 clks_syscall_stats_recent_ring[CLKS_SYSCALL_STATS_RING_SIZE];
|
static u16 clks_syscall_stats_recent_ring[CLKS_SYSCALL_STATS_RING_SIZE];
|
||||||
static u32 clks_syscall_stats_recent_head = 0U;
|
static u32 clks_syscall_stats_recent_head = 0U;
|
||||||
static u32 clks_syscall_stats_recent_size = 0U;
|
static u32 clks_syscall_stats_recent_size = 0U;
|
||||||
|
#if CLKS_CFG_USC != 0
|
||||||
|
static clks_bool clks_syscall_usc_allowed_used[CLKS_SYSCALL_USC_MAX_ALLOWED_APPS];
|
||||||
|
static char clks_syscall_usc_allowed_path[CLKS_SYSCALL_USC_MAX_ALLOWED_APPS][CLKS_EXEC_PROC_PATH_MAX];
|
||||||
|
#endif
|
||||||
|
|
||||||
#if defined(CLKS_ARCH_X86_64)
|
#if defined(CLKS_ARCH_X86_64)
|
||||||
static inline void clks_syscall_outb(u16 port, u8 value) {
|
static inline void clks_syscall_outb(u16 port, u8 value) {
|
||||||
@@ -1641,6 +1698,264 @@ static void clks_syscall_serial_write_hex64(u64 value) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if CLKS_CFG_USC != 0
|
||||||
|
static void clks_syscall_usc_sleep_until_input(void) {
|
||||||
|
#if defined(CLKS_ARCH_X86_64)
|
||||||
|
u64 flags = 0ULL;
|
||||||
|
|
||||||
|
__asm__ volatile("pushfq; popq %0" : "=r"(flags) : : "memory");
|
||||||
|
|
||||||
|
if ((flags & (1ULL << 9)) != 0ULL) {
|
||||||
|
__asm__ volatile("hlt" : : : "memory");
|
||||||
|
} else {
|
||||||
|
__asm__ volatile("sti; hlt; cli" : : : "memory");
|
||||||
|
}
|
||||||
|
#elif defined(CLKS_ARCH_AARCH64)
|
||||||
|
clks_cpu_pause();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *clks_syscall_usc_syscall_name(u64 id) {
|
||||||
|
switch (id) {
|
||||||
|
case CLKS_SYSCALL_FS_MKDIR:
|
||||||
|
return "FS_MKDIR";
|
||||||
|
case CLKS_SYSCALL_FS_WRITE:
|
||||||
|
return "FS_WRITE";
|
||||||
|
case CLKS_SYSCALL_FS_APPEND:
|
||||||
|
return "FS_APPEND";
|
||||||
|
case CLKS_SYSCALL_FS_REMOVE:
|
||||||
|
return "FS_REMOVE";
|
||||||
|
case CLKS_SYSCALL_EXEC_PATH:
|
||||||
|
return "EXEC_PATH";
|
||||||
|
case CLKS_SYSCALL_EXEC_PATHV:
|
||||||
|
return "EXEC_PATHV";
|
||||||
|
case CLKS_SYSCALL_EXEC_PATHV_IO:
|
||||||
|
return "EXEC_PATHV_IO";
|
||||||
|
case CLKS_SYSCALL_SPAWN_PATH:
|
||||||
|
return "SPAWN_PATH";
|
||||||
|
case CLKS_SYSCALL_SPAWN_PATHV:
|
||||||
|
return "SPAWN_PATHV";
|
||||||
|
case CLKS_SYSCALL_PROC_KILL:
|
||||||
|
return "PROC_KILL";
|
||||||
|
case CLKS_SYSCALL_SHUTDOWN:
|
||||||
|
return "SHUTDOWN";
|
||||||
|
case CLKS_SYSCALL_RESTART:
|
||||||
|
return "RESTART";
|
||||||
|
default:
|
||||||
|
return "UNKNOWN";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static clks_bool clks_syscall_usc_is_dangerous(u64 id) {
|
||||||
|
switch (id) {
|
||||||
|
case CLKS_SYSCALL_FS_MKDIR:
|
||||||
|
return (CLKS_CFG_USC_SC_FS_MKDIR != 0) ? CLKS_TRUE : CLKS_FALSE;
|
||||||
|
case CLKS_SYSCALL_FS_WRITE:
|
||||||
|
return (CLKS_CFG_USC_SC_FS_WRITE != 0) ? CLKS_TRUE : CLKS_FALSE;
|
||||||
|
case CLKS_SYSCALL_FS_APPEND:
|
||||||
|
return (CLKS_CFG_USC_SC_FS_APPEND != 0) ? CLKS_TRUE : CLKS_FALSE;
|
||||||
|
case CLKS_SYSCALL_FS_REMOVE:
|
||||||
|
return (CLKS_CFG_USC_SC_FS_REMOVE != 0) ? CLKS_TRUE : CLKS_FALSE;
|
||||||
|
case CLKS_SYSCALL_EXEC_PATH:
|
||||||
|
return (CLKS_CFG_USC_SC_EXEC_PATH != 0) ? CLKS_TRUE : CLKS_FALSE;
|
||||||
|
case CLKS_SYSCALL_EXEC_PATHV:
|
||||||
|
return (CLKS_CFG_USC_SC_EXEC_PATHV != 0) ? CLKS_TRUE : CLKS_FALSE;
|
||||||
|
case CLKS_SYSCALL_EXEC_PATHV_IO:
|
||||||
|
return (CLKS_CFG_USC_SC_EXEC_PATHV_IO != 0) ? CLKS_TRUE : CLKS_FALSE;
|
||||||
|
case CLKS_SYSCALL_SPAWN_PATH:
|
||||||
|
return (CLKS_CFG_USC_SC_SPAWN_PATH != 0) ? CLKS_TRUE : CLKS_FALSE;
|
||||||
|
case CLKS_SYSCALL_SPAWN_PATHV:
|
||||||
|
return (CLKS_CFG_USC_SC_SPAWN_PATHV != 0) ? CLKS_TRUE : CLKS_FALSE;
|
||||||
|
case CLKS_SYSCALL_PROC_KILL:
|
||||||
|
return (CLKS_CFG_USC_SC_PROC_KILL != 0) ? CLKS_TRUE : CLKS_FALSE;
|
||||||
|
case CLKS_SYSCALL_SHUTDOWN:
|
||||||
|
return (CLKS_CFG_USC_SC_SHUTDOWN != 0) ? CLKS_TRUE : CLKS_FALSE;
|
||||||
|
case CLKS_SYSCALL_RESTART:
|
||||||
|
return (CLKS_CFG_USC_SC_RESTART != 0) ? CLKS_TRUE : CLKS_FALSE;
|
||||||
|
default:
|
||||||
|
return CLKS_FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void clks_syscall_usc_copy_path(char *dst, usize dst_size, const char *src) {
|
||||||
|
usize i = 0U;
|
||||||
|
|
||||||
|
if (dst == CLKS_NULL || dst_size == 0U) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (src == CLKS_NULL) {
|
||||||
|
dst[0] = '\0';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (src[i] != '\0' && i + 1U < dst_size) {
|
||||||
|
dst[i] = src[i];
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
dst[i] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
static clks_bool clks_syscall_usc_current_app_path(char *out_path, usize out_size) {
|
||||||
|
u64 pid;
|
||||||
|
struct clks_exec_proc_snapshot snap;
|
||||||
|
|
||||||
|
if (out_path == CLKS_NULL || out_size == 0U) {
|
||||||
|
return CLKS_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
out_path[0] = '\0';
|
||||||
|
pid = clks_exec_current_pid();
|
||||||
|
|
||||||
|
if (pid == 0ULL) {
|
||||||
|
return CLKS_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clks_exec_proc_snapshot(pid, &snap) == CLKS_FALSE || snap.path[0] == '\0') {
|
||||||
|
return CLKS_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
clks_syscall_usc_copy_path(out_path, out_size, snap.path);
|
||||||
|
return CLKS_TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static i32 clks_syscall_usc_find_allowed_path(const char *path) {
|
||||||
|
u32 i;
|
||||||
|
|
||||||
|
if (path == CLKS_NULL || path[0] == '\0') {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0U; i < CLKS_SYSCALL_USC_MAX_ALLOWED_APPS; i++) {
|
||||||
|
if (clks_syscall_usc_allowed_used[i] == CLKS_TRUE &&
|
||||||
|
clks_strcmp(clks_syscall_usc_allowed_path[i], path) == 0) {
|
||||||
|
return (i32)i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void clks_syscall_usc_remember_path(const char *path) {
|
||||||
|
u32 i;
|
||||||
|
|
||||||
|
if (path == CLKS_NULL || path[0] == '\0') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clks_syscall_usc_find_allowed_path(path) >= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0U; i < CLKS_SYSCALL_USC_MAX_ALLOWED_APPS; i++) {
|
||||||
|
if (clks_syscall_usc_allowed_used[i] == CLKS_FALSE) {
|
||||||
|
clks_syscall_usc_allowed_used[i] = CLKS_TRUE;
|
||||||
|
clks_syscall_usc_copy_path(clks_syscall_usc_allowed_path[i], sizeof(clks_syscall_usc_allowed_path[i]), path);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void clks_syscall_usc_emit_text_line(const char *label, const char *value) {
|
||||||
|
char message[320];
|
||||||
|
usize pos = 0U;
|
||||||
|
|
||||||
|
message[0] = '\0';
|
||||||
|
pos = clks_syscall_procfs_append_text(message, sizeof(message), pos, label);
|
||||||
|
pos = clks_syscall_procfs_append_text(message, sizeof(message), pos, ": ");
|
||||||
|
pos = clks_syscall_procfs_append_text(message, sizeof(message), pos, value);
|
||||||
|
(void)pos;
|
||||||
|
clks_log(CLKS_LOG_WARN, "USC", message);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void clks_syscall_usc_emit_hex_line(const char *label, u64 value) {
|
||||||
|
clks_log_hex(CLKS_LOG_WARN, "USC", label, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static clks_bool clks_syscall_usc_prompt_allow(const char *app_path, u64 id, u64 arg0, u64 arg1, u64 arg2) {
|
||||||
|
const char *name = clks_syscall_usc_syscall_name(id);
|
||||||
|
u32 tty_index = clks_exec_current_tty();
|
||||||
|
|
||||||
|
#if !defined(CLKS_CFG_KEYBOARD) || (CLKS_CFG_KEYBOARD == 0)
|
||||||
|
(void)tty_index;
|
||||||
|
clks_syscall_usc_emit_text_line("BLOCK", "keyboard disabled, cannot prompt");
|
||||||
|
return CLKS_FALSE;
|
||||||
|
#else
|
||||||
|
clks_syscall_usc_emit_text_line("DANGEROUS_SYSCALL", "REQUEST DETECTED");
|
||||||
|
clks_syscall_usc_emit_text_line("APP", app_path);
|
||||||
|
clks_syscall_usc_emit_hex_line("SYSCALL_ID", id);
|
||||||
|
clks_syscall_usc_emit_text_line("SYSCALL_NAME", name);
|
||||||
|
clks_syscall_usc_emit_hex_line("ARG0", arg0);
|
||||||
|
clks_syscall_usc_emit_hex_line("ARG1", arg1);
|
||||||
|
clks_syscall_usc_emit_hex_line("ARG2", arg2);
|
||||||
|
clks_log(CLKS_LOG_WARN, "USC", "CONFIRM: Allow this app permanently? [y/N]");
|
||||||
|
clks_tty_write("[WARN][USC] Allow this app permanently? [y/N]: ");
|
||||||
|
clks_serial_write("[WARN][USC] Allow this app permanently? [y/N]: ");
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
char ch = '\0';
|
||||||
|
|
||||||
|
if (clks_keyboard_pop_char_for_tty(tty_index, &ch) == CLKS_TRUE) {
|
||||||
|
if (ch == 'y' || ch == 'Y') {
|
||||||
|
clks_tty_write("y\n");
|
||||||
|
clks_serial_write("y\n");
|
||||||
|
return CLKS_TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ch == 'n' || ch == 'N' || ch == '\n' || ch == '\r' || ch == 27) {
|
||||||
|
clks_tty_write("n\n");
|
||||||
|
clks_serial_write("n\n");
|
||||||
|
return CLKS_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
clks_syscall_usc_sleep_until_input();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static clks_bool clks_syscall_usc_check(u64 id, u64 arg0, u64 arg1, u64 arg2) {
|
||||||
|
#if CLKS_CFG_USC == 0
|
||||||
|
(void)id;
|
||||||
|
(void)arg0;
|
||||||
|
(void)arg1;
|
||||||
|
(void)arg2;
|
||||||
|
return CLKS_TRUE;
|
||||||
|
#else
|
||||||
|
char app_path[CLKS_EXEC_PROC_PATH_MAX];
|
||||||
|
|
||||||
|
if (clks_syscall_usc_is_dangerous(id) == CLKS_FALSE) {
|
||||||
|
return CLKS_TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clks_exec_is_running() == CLKS_FALSE || clks_exec_current_path_is_user() == CLKS_FALSE) {
|
||||||
|
return CLKS_TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clks_syscall_usc_current_app_path(app_path, sizeof(app_path)) == CLKS_FALSE) {
|
||||||
|
clks_syscall_usc_emit_text_line("BLOCK", "cannot resolve current app path");
|
||||||
|
return CLKS_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clks_syscall_usc_find_allowed_path(app_path) >= 0) {
|
||||||
|
return CLKS_TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clks_syscall_usc_prompt_allow(app_path, id, arg0, arg1, arg2) == CLKS_TRUE) {
|
||||||
|
clks_syscall_usc_remember_path(app_path);
|
||||||
|
clks_syscall_usc_emit_text_line("ALLOW", app_path);
|
||||||
|
return CLKS_TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
clks_syscall_usc_emit_text_line("DENY", app_path);
|
||||||
|
return CLKS_FALSE;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
static void clks_syscall_stats_reset(void) {
|
static void clks_syscall_stats_reset(void) {
|
||||||
clks_syscall_stats_total = 0ULL;
|
clks_syscall_stats_total = 0ULL;
|
||||||
clks_memset(clks_syscall_stats_id_count, 0, sizeof(clks_syscall_stats_id_count));
|
clks_memset(clks_syscall_stats_id_count, 0, sizeof(clks_syscall_stats_id_count));
|
||||||
@@ -1754,6 +2069,10 @@ void clks_syscall_init(void) {
|
|||||||
clks_syscall_symbols_checked = CLKS_FALSE;
|
clks_syscall_symbols_checked = CLKS_FALSE;
|
||||||
clks_syscall_symbols_data = CLKS_NULL;
|
clks_syscall_symbols_data = CLKS_NULL;
|
||||||
clks_syscall_symbols_size = 0ULL;
|
clks_syscall_symbols_size = 0ULL;
|
||||||
|
#if CLKS_CFG_USC != 0
|
||||||
|
clks_memset(clks_syscall_usc_allowed_used, 0, sizeof(clks_syscall_usc_allowed_used));
|
||||||
|
clks_memset(clks_syscall_usc_allowed_path, 0, sizeof(clks_syscall_usc_allowed_path));
|
||||||
|
#endif
|
||||||
clks_syscall_stats_reset();
|
clks_syscall_stats_reset();
|
||||||
clks_log(CLKS_LOG_INFO, "SYSCALL", "INT80 FRAMEWORK ONLINE");
|
clks_log(CLKS_LOG_INFO, "SYSCALL", "INT80 FRAMEWORK ONLINE");
|
||||||
}
|
}
|
||||||
@@ -1773,6 +2092,10 @@ u64 clks_syscall_dispatch(void *frame_ptr) {
|
|||||||
clks_syscall_stats_record(id);
|
clks_syscall_stats_record(id);
|
||||||
clks_syscall_trace_user_program(id);
|
clks_syscall_trace_user_program(id);
|
||||||
|
|
||||||
|
if (clks_syscall_usc_check(id, frame->rbx, frame->rcx, frame->rdx) == CLKS_FALSE) {
|
||||||
|
return (u64)-1;
|
||||||
|
}
|
||||||
|
|
||||||
switch (id) {
|
switch (id) {
|
||||||
case CLKS_SYSCALL_LOG_WRITE:
|
case CLKS_SYSCALL_LOG_WRITE:
|
||||||
return clks_syscall_log_write(frame->rbx, frame->rcx);
|
return clks_syscall_log_write(frame->rbx, frame->rcx);
|
||||||
|
|||||||
@@ -190,6 +190,123 @@
|
|||||||
"type": "tristate",
|
"type": "tristate",
|
||||||
"default": "y"
|
"default": "y"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"key": "CLEONOS_CLKS_ENABLE_USC",
|
||||||
|
"title": "UserSafeController (USC)",
|
||||||
|
"description": "Prompt before dangerous user syscalls and remember per-app approval for current boot.",
|
||||||
|
"type": "bool",
|
||||||
|
"default": true,
|
||||||
|
"depends_on": "CLEONOS_CLKS_ENABLE_KEYBOARD",
|
||||||
|
"group": "USC Syscall Policy"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "CLEONOS_CLKS_ENABLE_USC_SC_FS_MKDIR",
|
||||||
|
"title": "Intercept FS_MKDIR",
|
||||||
|
"description": "USC prompt for syscall FS_MKDIR.",
|
||||||
|
"type": "bool",
|
||||||
|
"default": true,
|
||||||
|
"depends_on": "CLEONOS_CLKS_ENABLE_USC",
|
||||||
|
"group": "USC Syscall Policy"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "CLEONOS_CLKS_ENABLE_USC_SC_FS_WRITE",
|
||||||
|
"title": "Intercept FS_WRITE",
|
||||||
|
"description": "USC prompt for syscall FS_WRITE.",
|
||||||
|
"type": "bool",
|
||||||
|
"default": true,
|
||||||
|
"depends_on": "CLEONOS_CLKS_ENABLE_USC",
|
||||||
|
"group": "USC Syscall Policy"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "CLEONOS_CLKS_ENABLE_USC_SC_FS_APPEND",
|
||||||
|
"title": "Intercept FS_APPEND",
|
||||||
|
"description": "USC prompt for syscall FS_APPEND.",
|
||||||
|
"type": "bool",
|
||||||
|
"default": true,
|
||||||
|
"depends_on": "CLEONOS_CLKS_ENABLE_USC",
|
||||||
|
"group": "USC Syscall Policy"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "CLEONOS_CLKS_ENABLE_USC_SC_FS_REMOVE",
|
||||||
|
"title": "Intercept FS_REMOVE",
|
||||||
|
"description": "USC prompt for syscall FS_REMOVE.",
|
||||||
|
"type": "bool",
|
||||||
|
"default": true,
|
||||||
|
"depends_on": "CLEONOS_CLKS_ENABLE_USC",
|
||||||
|
"group": "USC Syscall Policy"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "CLEONOS_CLKS_ENABLE_USC_SC_EXEC_PATH",
|
||||||
|
"title": "Intercept EXEC_PATH",
|
||||||
|
"description": "USC prompt for syscall EXEC_PATH.",
|
||||||
|
"type": "bool",
|
||||||
|
"default": true,
|
||||||
|
"depends_on": "CLEONOS_CLKS_ENABLE_USC",
|
||||||
|
"group": "USC Syscall Policy"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "CLEONOS_CLKS_ENABLE_USC_SC_EXEC_PATHV",
|
||||||
|
"title": "Intercept EXEC_PATHV",
|
||||||
|
"description": "USC prompt for syscall EXEC_PATHV.",
|
||||||
|
"type": "bool",
|
||||||
|
"default": true,
|
||||||
|
"depends_on": "CLEONOS_CLKS_ENABLE_USC",
|
||||||
|
"group": "USC Syscall Policy"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "CLEONOS_CLKS_ENABLE_USC_SC_EXEC_PATHV_IO",
|
||||||
|
"title": "Intercept EXEC_PATHV_IO",
|
||||||
|
"description": "USC prompt for syscall EXEC_PATHV_IO.",
|
||||||
|
"type": "bool",
|
||||||
|
"default": true,
|
||||||
|
"depends_on": "CLEONOS_CLKS_ENABLE_USC",
|
||||||
|
"group": "USC Syscall Policy"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "CLEONOS_CLKS_ENABLE_USC_SC_SPAWN_PATH",
|
||||||
|
"title": "Intercept SPAWN_PATH",
|
||||||
|
"description": "USC prompt for syscall SPAWN_PATH.",
|
||||||
|
"type": "bool",
|
||||||
|
"default": true,
|
||||||
|
"depends_on": "CLEONOS_CLKS_ENABLE_USC",
|
||||||
|
"group": "USC Syscall Policy"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "CLEONOS_CLKS_ENABLE_USC_SC_SPAWN_PATHV",
|
||||||
|
"title": "Intercept SPAWN_PATHV",
|
||||||
|
"description": "USC prompt for syscall SPAWN_PATHV.",
|
||||||
|
"type": "bool",
|
||||||
|
"default": true,
|
||||||
|
"depends_on": "CLEONOS_CLKS_ENABLE_USC",
|
||||||
|
"group": "USC Syscall Policy"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "CLEONOS_CLKS_ENABLE_USC_SC_PROC_KILL",
|
||||||
|
"title": "Intercept PROC_KILL",
|
||||||
|
"description": "USC prompt for syscall PROC_KILL.",
|
||||||
|
"type": "bool",
|
||||||
|
"default": true,
|
||||||
|
"depends_on": "CLEONOS_CLKS_ENABLE_USC",
|
||||||
|
"group": "USC Syscall Policy"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "CLEONOS_CLKS_ENABLE_USC_SC_SHUTDOWN",
|
||||||
|
"title": "Intercept SHUTDOWN",
|
||||||
|
"description": "USC prompt for syscall SHUTDOWN.",
|
||||||
|
"type": "bool",
|
||||||
|
"default": true,
|
||||||
|
"depends_on": "CLEONOS_CLKS_ENABLE_USC",
|
||||||
|
"group": "USC Syscall Policy"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "CLEONOS_CLKS_ENABLE_USC_SC_RESTART",
|
||||||
|
"title": "Intercept RESTART",
|
||||||
|
"description": "USC prompt for syscall RESTART.",
|
||||||
|
"type": "bool",
|
||||||
|
"default": true,
|
||||||
|
"depends_on": "CLEONOS_CLKS_ENABLE_USC",
|
||||||
|
"group": "USC Syscall Policy"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"key": "CLEONOS_CLKS_ENABLE_KBD_TTY_SWITCH_HOTKEY",
|
"key": "CLEONOS_CLKS_ENABLE_KBD_TTY_SWITCH_HOTKEY",
|
||||||
"title": "Keyboard TTY Switch Hotkey",
|
"title": "Keyboard TTY Switch Hotkey",
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ class OptionItem:
|
|||||||
depends_on: str
|
depends_on: str
|
||||||
selects: Tuple[str, ...]
|
selects: Tuple[str, ...]
|
||||||
implies: Tuple[str, ...]
|
implies: Tuple[str, ...]
|
||||||
|
group: str
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
@@ -312,6 +313,7 @@ def load_clks_options() -> List[OptionItem]:
|
|||||||
depends_on = str(entry.get("depends_on", entry.get("depends", ""))).strip()
|
depends_on = str(entry.get("depends_on", entry.get("depends", ""))).strip()
|
||||||
selects = _normalize_key_list(entry.get("select", entry.get("selects", ())))
|
selects = _normalize_key_list(entry.get("select", entry.get("selects", ())))
|
||||||
implies = _normalize_key_list(entry.get("imply", entry.get("implies", ())))
|
implies = _normalize_key_list(entry.get("imply", entry.get("implies", ())))
|
||||||
|
group = str(entry.get("group", entry.get("menu", "General"))).strip() or "General"
|
||||||
if not key:
|
if not key:
|
||||||
continue
|
continue
|
||||||
options.append(
|
options.append(
|
||||||
@@ -324,6 +326,7 @@ def load_clks_options() -> List[OptionItem]:
|
|||||||
depends_on=depends_on,
|
depends_on=depends_on,
|
||||||
selects=selects,
|
selects=selects,
|
||||||
implies=implies,
|
implies=implies,
|
||||||
|
group=group,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -378,6 +381,7 @@ def discover_user_apps() -> List[OptionItem]:
|
|||||||
depends_on="",
|
depends_on="",
|
||||||
selects=(),
|
selects=(),
|
||||||
implies=(),
|
implies=(),
|
||||||
|
group=section,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -417,6 +421,27 @@ def _build_index(options: Iterable[OptionItem]) -> Dict[str, OptionItem]:
|
|||||||
return {item.key: item for item in options}
|
return {item.key: item for item in options}
|
||||||
|
|
||||||
|
|
||||||
|
def _grouped_options(options: List[OptionItem]) -> List[Tuple[str, List[OptionItem]]]:
|
||||||
|
groups: Dict[str, List[OptionItem]] = {}
|
||||||
|
ordered_names: List[str] = []
|
||||||
|
|
||||||
|
for item in options:
|
||||||
|
name = (item.group or "General").strip() or "General"
|
||||||
|
if name not in groups:
|
||||||
|
groups[name] = []
|
||||||
|
ordered_names.append(name)
|
||||||
|
groups[name].append(item)
|
||||||
|
|
||||||
|
out: List[Tuple[str, List[OptionItem]]] = []
|
||||||
|
for name in ordered_names:
|
||||||
|
out.append((name, groups[name]))
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
def _group_enabled_count(group_options: List[OptionItem], ev: EvalResult) -> int:
|
||||||
|
return sum(1 for item in group_options if ev.effective.get(item.key, item.default) > TRI_N)
|
||||||
|
|
||||||
|
|
||||||
def _set_option_if_exists(values: Dict[str, int], option_index: Dict[str, OptionItem], key: str, level: int) -> None:
|
def _set_option_if_exists(values: Dict[str, int], option_index: Dict[str, OptionItem], key: str, level: int) -> None:
|
||||||
if key in values:
|
if key in values:
|
||||||
item = option_index.get(key)
|
item = option_index.get(key)
|
||||||
@@ -829,6 +854,47 @@ def section_loop(title: str, section_options: List[OptionItem], all_options: Lis
|
|||||||
print("unknown command")
|
print("unknown command")
|
||||||
|
|
||||||
|
|
||||||
|
def grouped_section_loop(
|
||||||
|
title: str,
|
||||||
|
section_options: List[OptionItem],
|
||||||
|
all_options: List[OptionItem],
|
||||||
|
values: Dict[str, int],
|
||||||
|
) -> None:
|
||||||
|
groups = _grouped_options(section_options)
|
||||||
|
|
||||||
|
if len(groups) <= 1:
|
||||||
|
section_loop(title, section_options, all_options, values)
|
||||||
|
return
|
||||||
|
|
||||||
|
while True:
|
||||||
|
ev = evaluate_config(all_options, values)
|
||||||
|
print()
|
||||||
|
print(f"== {title} / Groups ==")
|
||||||
|
print(f" 0. All ({_group_enabled_count(section_options, ev)}/{len(section_options)} enabled)")
|
||||||
|
for idx, (name, opts) in enumerate(groups, start=1):
|
||||||
|
print(f"{idx:3d}. {name} ({_group_enabled_count(opts, ev)}/{len(opts)} enabled)")
|
||||||
|
print("Commands: <number> open, b back")
|
||||||
|
|
||||||
|
raw = input(f"{title}/groups> ").strip().lower()
|
||||||
|
if not raw:
|
||||||
|
continue
|
||||||
|
if raw in {"b", "back", "q", "quit"}:
|
||||||
|
return
|
||||||
|
if not raw.isdigit():
|
||||||
|
print("invalid selection")
|
||||||
|
continue
|
||||||
|
|
||||||
|
idx = int(raw)
|
||||||
|
if idx == 0:
|
||||||
|
section_loop(title, section_options, all_options, values)
|
||||||
|
continue
|
||||||
|
if 1 <= idx <= len(groups):
|
||||||
|
group_name, group_items = groups[idx - 1]
|
||||||
|
section_loop(f"{title}/{group_name}", group_items, all_options, values)
|
||||||
|
continue
|
||||||
|
print("invalid selection")
|
||||||
|
|
||||||
|
|
||||||
def _safe_addnstr(stdscr, y: int, x: int, text: str, attr: int = 0) -> None:
|
def _safe_addnstr(stdscr, y: int, x: int, text: str, attr: int = 0) -> None:
|
||||||
h, w = stdscr.getmaxyx()
|
h, w = stdscr.getmaxyx()
|
||||||
if y < 0 or y >= h or x >= w:
|
if y < 0 or y >= h or x >= w:
|
||||||
@@ -1213,6 +1279,75 @@ def _run_ncurses_section(
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
|
|
||||||
|
def _run_ncurses_grouped_section(
|
||||||
|
stdscr,
|
||||||
|
theme: Dict[str, int],
|
||||||
|
title: str,
|
||||||
|
section_options: List[OptionItem],
|
||||||
|
all_options: List[OptionItem],
|
||||||
|
values: Dict[str, int],
|
||||||
|
) -> None:
|
||||||
|
groups = _grouped_options(section_options)
|
||||||
|
if len(groups) <= 1:
|
||||||
|
_run_ncurses_section(stdscr, theme, title, section_options, all_options, values)
|
||||||
|
return
|
||||||
|
|
||||||
|
selected = 0
|
||||||
|
|
||||||
|
while True:
|
||||||
|
ev = evaluate_config(all_options, values)
|
||||||
|
stdscr.erase()
|
||||||
|
h, w = stdscr.getmaxyx()
|
||||||
|
items: List[Tuple[str, List[OptionItem]]] = [("All", section_options)] + groups
|
||||||
|
|
||||||
|
if h < 12 or w < 56:
|
||||||
|
_safe_addnstr(stdscr, 0, 0, "Terminal too small for grouped view (need >= 56x12).", theme["status_warn"])
|
||||||
|
_safe_addnstr(stdscr, 2, 0, "Resize terminal then press any key, or ESC to go back.")
|
||||||
|
key = stdscr.getch()
|
||||||
|
if key in (27,):
|
||||||
|
return
|
||||||
|
continue
|
||||||
|
|
||||||
|
_safe_addnstr(stdscr, 0, 0, f" CLeonOS menuconfig / {title} / Groups ", theme["header"])
|
||||||
|
_safe_addnstr(stdscr, 1, 0, " Enter: open group ESC: back ", theme["subtitle"])
|
||||||
|
|
||||||
|
_draw_box(stdscr, 2, 0, h - 4, w, "CLKS Groups", theme["panel_border"], theme["panel_title"])
|
||||||
|
|
||||||
|
if selected < 0:
|
||||||
|
selected = 0
|
||||||
|
if selected >= len(items):
|
||||||
|
selected = len(items) - 1
|
||||||
|
|
||||||
|
for i, (name, opts) in enumerate(items):
|
||||||
|
row = 4 + i
|
||||||
|
if row >= h - 2:
|
||||||
|
break
|
||||||
|
on_count = _group_enabled_count(opts, ev)
|
||||||
|
line = f"{'>' if i == selected else ' '} {i:02d} {name} ({on_count}/{len(opts)} enabled)"
|
||||||
|
attr = theme["selected"] if i == selected else theme["value_label"]
|
||||||
|
_safe_addnstr(stdscr, row, 2, line, attr)
|
||||||
|
|
||||||
|
_safe_addnstr(stdscr, h - 1, 0, " Arrows/jk move Enter open ESC back ", theme["help"])
|
||||||
|
stdscr.refresh()
|
||||||
|
key = stdscr.getch()
|
||||||
|
|
||||||
|
if key in (27, ord("q"), ord("Q"), curses.KEY_LEFT):
|
||||||
|
return
|
||||||
|
if key in (curses.KEY_UP, ord("k"), ord("K")):
|
||||||
|
selected = (selected - 1) % len(items)
|
||||||
|
continue
|
||||||
|
if key in (curses.KEY_DOWN, ord("j"), ord("J")):
|
||||||
|
selected = (selected + 1) % len(items)
|
||||||
|
continue
|
||||||
|
if key in (curses.KEY_ENTER, 10, 13):
|
||||||
|
name, opts = items[selected]
|
||||||
|
if name == "All":
|
||||||
|
_run_ncurses_section(stdscr, theme, title, opts, all_options, values)
|
||||||
|
else:
|
||||||
|
_run_ncurses_section(stdscr, theme, f"{title}/{name}", opts, all_options, values)
|
||||||
|
continue
|
||||||
|
|
||||||
|
|
||||||
def _run_ncurses_main(stdscr, clks_options: List[OptionItem], user_options: List[OptionItem], values: Dict[str, int]) -> bool:
|
def _run_ncurses_main(stdscr, clks_options: List[OptionItem], user_options: List[OptionItem], values: Dict[str, int]) -> bool:
|
||||||
theme = _curses_theme()
|
theme = _curses_theme()
|
||||||
all_options = clks_options + user_options
|
all_options = clks_options + user_options
|
||||||
@@ -1288,7 +1423,7 @@ def _run_ncurses_main(stdscr, clks_options: List[OptionItem], user_options: List
|
|||||||
continue
|
continue
|
||||||
if key in (curses.KEY_ENTER, 10, 13):
|
if key in (curses.KEY_ENTER, 10, 13):
|
||||||
if selected == 0:
|
if selected == 0:
|
||||||
_run_ncurses_section(stdscr, theme, "CLKS", clks_options, all_options, values)
|
_run_ncurses_grouped_section(stdscr, theme, "CLKS", clks_options, all_options, values)
|
||||||
elif selected == 1:
|
elif selected == 1:
|
||||||
_run_ncurses_section(stdscr, theme, "USER", user_options, all_options, values)
|
_run_ncurses_section(stdscr, theme, "USER", user_options, all_options, values)
|
||||||
elif selected == 2:
|
elif selected == 2:
|
||||||
@@ -1594,7 +1729,20 @@ def interactive_menu_gui(clks_options: List[OptionItem], user_options: List[Opti
|
|||||||
_set_option_value(values, item, TRI_N)
|
_set_option_value(values, item, TRI_N)
|
||||||
self.refresh(keep_selection=False)
|
self.refresh(keep_selection=False)
|
||||||
|
|
||||||
|
clks_groups = _grouped_options(clks_options)
|
||||||
|
if len(clks_groups) <= 1:
|
||||||
clks_panel = _SectionPanel("CLKS Features", clks_options)
|
clks_panel = _SectionPanel("CLKS Features", clks_options)
|
||||||
|
else:
|
||||||
|
clks_panel = QtWidgets.QWidget()
|
||||||
|
clks_layout = QtWidgets.QVBoxLayout(clks_panel)
|
||||||
|
clks_layout.setContentsMargins(0, 0, 0, 0)
|
||||||
|
clks_layout.setSpacing(6)
|
||||||
|
clks_tabs = QtWidgets.QTabWidget()
|
||||||
|
clks_layout.addWidget(clks_tabs, 1)
|
||||||
|
clks_tabs.addTab(_SectionPanel("CLKS Features / All", clks_options), "All")
|
||||||
|
for group_name, group_items in clks_groups:
|
||||||
|
clks_tabs.addTab(_SectionPanel(f"CLKS Features / {group_name}", group_items), group_name)
|
||||||
|
|
||||||
user_panel = _SectionPanel("User Apps", user_options)
|
user_panel = _SectionPanel("User Apps", user_options)
|
||||||
tabs.addTab(clks_panel, "CLKS")
|
tabs.addTab(clks_panel, "CLKS")
|
||||||
tabs.addTab(user_panel, "USER")
|
tabs.addTab(user_panel, "USER")
|
||||||
@@ -1689,7 +1837,7 @@ def interactive_menu(clks_options: List[OptionItem], user_options: List[OptionIt
|
|||||||
show_summary(clks_options, user_options, values)
|
show_summary(clks_options, user_options, values)
|
||||||
choice = input("Select> ").strip().lower()
|
choice = input("Select> ").strip().lower()
|
||||||
if choice == "1":
|
if choice == "1":
|
||||||
section_loop("CLKS", clks_options, all_options, values)
|
grouped_section_loop("CLKS", clks_options, all_options, values)
|
||||||
continue
|
continue
|
||||||
if choice == "2":
|
if choice == "2":
|
||||||
section_loop("USER", user_options, all_options, values)
|
section_loop("USER", user_options, all_options, values)
|
||||||
|
|||||||
Reference in New Issue
Block a user