From 044b4a2f9ce4c9ab80d74ae60bded6f33ec426c4 Mon Sep 17 00:00:00 2001 From: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com> Date: Mon, 6 Oct 2025 20:53:18 +0800 Subject: [PATCH] manager: Simplify kpm management by migrating to the ksud side. --- .github/workflows/build-manager.yml | 25 -- .github/workflows/kpmmgr.yml | 40 -- .../java/com/sukisu/ultra/ui/util/KsuCli.kt | 131 +++--- .../io/sukisu/ultra/UltraToolInstall.java | 6 - userspace/kpmmgr/.gitignore | 2 - userspace/kpmmgr/jni/Android.mk | 6 - userspace/kpmmgr/jni/Application.mk | 3 - userspace/kpmmgr/jni/kpmmgr.c | 118 ------ userspace/ksud/src/cli.rs | 52 +++ userspace/ksud/src/init_event.rs | 6 +- userspace/ksud/src/kpm.rs | 401 ++++++++---------- userspace/ksud/src/main.rs | 3 +- 12 files changed, 306 insertions(+), 487 deletions(-) delete mode 100644 .github/workflows/kpmmgr.yml delete mode 100644 userspace/kpmmgr/.gitignore delete mode 100644 userspace/kpmmgr/jni/Android.mk delete mode 100644 userspace/kpmmgr/jni/Application.mk delete mode 100644 userspace/kpmmgr/jni/kpmmgr.c diff --git a/.github/workflows/build-manager.yml b/.github/workflows/build-manager.yml index 16d900ca..4f45a0df 100644 --- a/.github/workflows/build-manager.yml +++ b/.github/workflows/build-manager.yml @@ -9,7 +9,6 @@ on: - 'kernel/**' - 'userspace/ksud/**' - 'userspace/susfs/**' - - 'userspace/kpmmgr/**' - 'userspace/user_scanner/**' pull_request: branches: [ "main" ] @@ -96,19 +95,6 @@ jobs: with: target: ${{ matrix.target }} os: ${{ matrix.os }} - - build-kpmmgr: - if: ${{ always() }} - needs: [ check-build-lkm, build-lkm ] - strategy: - matrix: - include: - - target: aarch64-linux-android - os: ubuntu-latest - uses: ./.github/workflows/kpmmgr.yml - with: - target: ${{ matrix.target }} - os: ${{ matrix.os }} build-user_scanner: if: ${{ always() }} @@ -202,12 +188,6 @@ jobs: name: susfs-aarch64-linux-android path: . - - name: Download arm64 kpmmgr - uses: actions/download-artifact@v4 - with: - name: kpmmgr-aarch64-linux-android - path: . - - name: Download arm64 ksud uses: actions/download-artifact@v4 with: @@ -234,11 +214,6 @@ jobs: cp -f ../aarch64-linux-android/release/zakozako ../manager/app/src/main/jniLibs/arm64-v8a/libzakozako.so cp -f ../x86_64-linux-android/release/zakozako ../manager/app/src/main/jniLibs/x86_64/libzakozako.so cp -f ../armv7-linux-androideabi/release/zakozako ../manager/app/src/main/jniLibs/armeabi-v7a/libzakozako.so - - - name: Copy kpmmgr to app jniLibs - run: | - mkdir -p app/src/main/jniLibs/arm64-v8a - cp -f ../arm64-v8a/kpmmgr ../manager/app/src/main/jniLibs/arm64-v8a/libkpmmgr.so - name: Copy susfs to app jniLibs run: | diff --git a/.github/workflows/kpmmgr.yml b/.github/workflows/kpmmgr.yml deleted file mode 100644 index 18ba4513..00000000 --- a/.github/workflows/kpmmgr.yml +++ /dev/null @@ -1,40 +0,0 @@ -name: Build kpmmgr - -on: - push: - branches: [ "mian" ] - paths: - - '.github/workflows/kpmmgr.yml' - - 'userspace/kpmmgr/**' - workflow_dispatch: - workflow_call: - inputs: - target: - required: true - type: string - os: - required: false - type: string - default: self-hosted - -jobs: - build-susfs: - name: Build userspace kpmmgr - runs-on: ubuntu-latest - - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Build kpmmgr - working-directory: ./userspace/kpmmgr - run: | - $ANDROID_NDK_HOME/ndk-build - - - name: Upload a Build Artifact - uses: actions/upload-artifact@v4 - with: - name: kpmmgr-aarch64-linux-android - path: ./userspace/kpmmgr/libs diff --git a/manager/app/src/main/java/com/sukisu/ultra/ui/util/KsuCli.kt b/manager/app/src/main/java/com/sukisu/ultra/ui/util/KsuCli.kt index 119e4f49..2e05656d 100644 --- a/manager/app/src/main/java/com/sukisu/ultra/ui/util/KsuCli.kt +++ b/manager/app/src/main/java/com/sukisu/ultra/ui/util/KsuCli.kt @@ -419,6 +419,69 @@ fun deleteAppProfileTemplate(id: String): Boolean { return shell.newJob().add("${getKsuDaemonPath()} profile delete-template '${id}'") .to(ArrayList(), null).exec().isSuccess } +// KPM控制 +fun loadKpmModule(path: String, args: String? = null): String { + val shell = getRootShell() + val cmd = "${getKsuDaemonPath()} kpm load $path ${args ?: ""}" + return ShellUtils.fastCmd(shell, cmd) +} + +fun unloadKpmModule(name: String): String { + val shell = getRootShell() + val cmd = "${getKsuDaemonPath()} kpm unload $name" + return ShellUtils.fastCmd(shell, cmd) +} + +fun getKpmModuleCount(): Int { + val shell = getRootShell() + val cmd = "${getKsuDaemonPath()} kpm num" + val result = ShellUtils.fastCmd(shell, cmd) + return result.trim().toIntOrNull() ?: 0 +} + +fun runCmd(shell: Shell, cmd: String): String { + return shell.newJob() + .add(cmd) + .to(mutableListOf(), null) + .exec().out + .joinToString("\n") +} + +fun listKpmModules(): String { + val shell = getRootShell() + val cmd = "${getKsuDaemonPath()} kpm list" + return try { + runCmd(shell, cmd).trim() + } catch (e: Exception) { + Log.e(TAG, "Failed to list KPM modules", e) + "" + } +} + +fun getKpmModuleInfo(name: String): String { + val shell = getRootShell() + val cmd = "${getKsuDaemonPath()} kpm info $name" + return try { + runCmd(shell, cmd).trim() + } catch (e: Exception) { + Log.e(TAG, "Failed to get KPM module info: $name", e) + "" + } +} + +fun controlKpmModule(name: String, args: String? = null): Int { + val shell = getRootShell() + val cmd = """${getKsuDaemonPath()} kpm control $name "${args ?: ""}"""" + val result = runCmd(shell, cmd) + return result.trim().toIntOrNull() ?: -1 +} + +fun getKpmVersion(): String { + val shell = getRootShell() + val cmd = "${getKsuDaemonPath()} kpm version" + val result = ShellUtils.fastCmd(shell, cmd) + return result.trim() +} fun forceStopApp(packageName: String) { val shell = getRootShell() @@ -487,74 +550,6 @@ fun susfsSUS_SU_Mode(): String { return result } -fun getKpmmgrPath(): String { - return ksuApp.applicationInfo.nativeLibraryDir + File.separator + "libkpmmgr.so" -} - - -fun loadKpmModule(path: String, args: String? = null): String { - val shell = getRootShell() - val cmd = "${getKpmmgrPath()} load $path ${args ?: ""}" - return ShellUtils.fastCmd(shell, cmd) -} - -fun unloadKpmModule(name: String): String { - val shell = getRootShell() - val cmd = "${getKpmmgrPath()} unload $name" - return ShellUtils.fastCmd(shell, cmd) -} - -fun getKpmModuleCount(): Int { - val shell = getRootShell() - val cmd = "${getKpmmgrPath()} num" - val result = ShellUtils.fastCmd(shell, cmd) - return result.trim().toIntOrNull() ?: 0 -} - -fun runCmd(shell: Shell, cmd: String): String { - return shell.newJob() - .add(cmd) - .to(mutableListOf(), null) - .exec().out - .joinToString("\n") -} - -fun listKpmModules(): String { - val shell = getRootShell() - val cmd = "${getKpmmgrPath()} list" - return try { - runCmd(shell, cmd).trim() - } catch (e: Exception) { - Log.e(TAG, "Failed to list KPM modules", e) - "" - } -} - -fun getKpmModuleInfo(name: String): String { - val shell = getRootShell() - val cmd = "${getKpmmgrPath()} info $name" - return try { - runCmd(shell, cmd).trim() - } catch (e: Exception) { - Log.e(TAG, "Failed to get KPM module info: $name", e) - "" - } -} - -fun controlKpmModule(name: String, args: String? = null): Int { - val shell = getRootShell() - val cmd = """${getKpmmgrPath()} control $name "${args ?: ""}"""" - val result = runCmd(shell, cmd) - return result.trim().toIntOrNull() ?: -1 -} - -fun getKpmVersion(): String { - val shell = getRootShell() - val cmd = "${getKpmmgrPath()} version" - val result = ShellUtils.fastCmd(shell, cmd) - return result.trim() -} - fun getZygiskImplement(): String { val shell = getRootShell() diff --git a/manager/app/src/main/java/io/sukisu/ultra/UltraToolInstall.java b/manager/app/src/main/java/io/sukisu/ultra/UltraToolInstall.java index 84fdddae..0dd0a8b1 100644 --- a/manager/app/src/main/java/io/sukisu/ultra/UltraToolInstall.java +++ b/manager/app/src/main/java/io/sukisu/ultra/UltraToolInstall.java @@ -4,16 +4,10 @@ import static com.sukisu.ultra.ui.util.KsuCliKt.*; import android.annotation.SuppressLint; public class UltraToolInstall { - private static final String OUTSIDE_KPMMGR_PATH = "/data/adb/ksu/bin/kpmmgr"; private static final String OUTSIDE_SUSFSD_PATH = "/data/adb/ksu/bin/susfsd"; @SuppressLint("SetWorldReadable") public static void tryToInstall() { - String kpmmgrPath = getKpmmgrPath(); - if (UltraShellHelper.isPathExists(OUTSIDE_KPMMGR_PATH)) { - UltraShellHelper.CopyFileTo(kpmmgrPath, OUTSIDE_KPMMGR_PATH); - UltraShellHelper.runCmd("chmod a+rx " + OUTSIDE_KPMMGR_PATH); - } String SuSFSDaemonPath = getSuSFSDaemonPath(); if (UltraShellHelper.isPathExists(OUTSIDE_SUSFSD_PATH)) { UltraShellHelper.CopyFileTo(SuSFSDaemonPath, OUTSIDE_SUSFSD_PATH); diff --git a/userspace/kpmmgr/.gitignore b/userspace/kpmmgr/.gitignore deleted file mode 100644 index 4b4ce0f2..00000000 --- a/userspace/kpmmgr/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/obj -/libs \ No newline at end of file diff --git a/userspace/kpmmgr/jni/Android.mk b/userspace/kpmmgr/jni/Android.mk deleted file mode 100644 index a278158f..00000000 --- a/userspace/kpmmgr/jni/Android.mk +++ /dev/null @@ -1,6 +0,0 @@ -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) -LOCAL_MODULE := kpmmgr -LOCAL_SRC_FILES := kpmmgr.c -include $(BUILD_EXECUTABLE) diff --git a/userspace/kpmmgr/jni/Application.mk b/userspace/kpmmgr/jni/Application.mk deleted file mode 100644 index 61d31236..00000000 --- a/userspace/kpmmgr/jni/Application.mk +++ /dev/null @@ -1,3 +0,0 @@ -APP_ABI := arm64-v8a -APP_PLATFORM := android-24 -APP_STL := none diff --git a/userspace/kpmmgr/jni/kpmmgr.c b/userspace/kpmmgr/jni/kpmmgr.c deleted file mode 100644 index 7c4d9a7f..00000000 --- a/userspace/kpmmgr/jni/kpmmgr.c +++ /dev/null @@ -1,118 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#define KERNEL_SU_OPTION 0xDEADBEEF -#define KSU_OPTIONS 0xdeadbeef - -// KPM控制代码 -#define CMD_KPM_CONTROL 28 -#define CMD_KPM_CONTROL_MAX 7 - -// 控制代码 -// prctl(xxx, 28, "PATH", "ARGS") -// success return 0, error return -N -#define SUKISU_KPM_LOAD 28 - -// prctl(xxx, 29, "NAME") -// success return 0, error return -N -#define SUKISU_KPM_UNLOAD 29 - -// num = prctl(xxx, 30) -// error return -N -// success return +num or 0 -#define SUKISU_KPM_NUM 30 - -// prctl(xxx, 31, Buffer, BufferSize) -// success return +out, error return -N -#define SUKISU_KPM_LIST 31 - -// prctl(xxx, 32, "NAME", Buffer[256]) -// success return +out, error return -N -#define SUKISU_KPM_INFO 32 - -// prctl(xxx, 33, "NAME", "ARGS") -// success return KPM's result value -// error return -N -#define SUKISU_KPM_CONTROL 33 - -// prctl(xxx, 34, buffer, bufferSize) -// success return KPM's result value -// error return -N -#define SUKISU_KPM_VERSION 34 - -#define CONTROL_CODE(n) (n) - -void print_usage(const char *prog) { - printf("Usage: %s [args]\n", prog); - printf("Commands:\n"); - printf(" load Load a KPM module\n"); - printf(" unload Unload a KPM module\n"); - printf(" num Get number of loaded modules\n"); - printf(" list List loaded KPM modules\n"); - printf(" info Get info of a KPM module\n"); - printf(" control Send control command to a KPM module\n"); - printf(" version Print KPM Loader version\n"); -} - -int main(int argc, char *argv[]) { - if (argc < 2) { - print_usage(argv[0]); - return 1; - } - - int ret = -1; - int out = -1; // 存储返回值 - - if (strcmp(argv[1], "load") == 0 && argc >= 3) { - // 加载 KPM 模块 - ret = prctl(KSU_OPTIONS, CONTROL_CODE(SUKISU_KPM_LOAD), argv[2], (argc > 3 ? argv[3] : NULL), &out); - if(out > 0) { - printf("Success"); - } - } else if (strcmp(argv[1], "unload") == 0 && argc >= 3) { - // 卸载 KPM 模块 - ret = prctl(KSU_OPTIONS, CONTROL_CODE(SUKISU_KPM_UNLOAD), argv[2], NULL, &out); - } else if (strcmp(argv[1], "num") == 0) { - // 获取加载的 KPM 数量 - ret = prctl(KSU_OPTIONS, CONTROL_CODE(SUKISU_KPM_NUM), NULL, NULL, &out); - printf("%d", out); - return 0; - } else if (strcmp(argv[1], "list") == 0) { - // 获取模块列表 - char buffer[1024] = {0}; - ret = prctl(KSU_OPTIONS, CONTROL_CODE(SUKISU_KPM_LIST), buffer, sizeof(buffer), &out); - if (out >= 0) { - printf("%s", buffer); - } - } else if (strcmp(argv[1], "info") == 0 && argc >= 3) { - // 获取指定模块信息 - char buffer[256] = {0}; - ret = prctl(KSU_OPTIONS, CONTROL_CODE(SUKISU_KPM_INFO), argv[2], buffer, &out); - if (out >= 0) { - printf("%s\n", buffer); - } - } else if (strcmp(argv[1], "control") == 0 && argc >= 4) { - // 控制 KPM 模块 - ret = prctl(KSU_OPTIONS, CONTROL_CODE(SUKISU_KPM_CONTROL), argv[2], argv[3], &out); - } else if (strcmp(argv[1], "version") == 0) { - char buffer[1024] = {0}; - ret = prctl(KSU_OPTIONS, CONTROL_CODE(SUKISU_KPM_VERSION), buffer, sizeof(buffer), &out); - if (out >= 0) { - printf("%s", buffer); - } - } else { - print_usage(argv[0]); - return 1; - } - - if (out < 0) { - printf("Error: %s\n", strerror(-out)); - return -1; - } - - return 0; -} diff --git a/userspace/ksud/src/cli.rs b/userspace/ksud/src/cli.rs index bf4835f3..f1a0bce1 100644 --- a/userspace/ksud/src/cli.rs +++ b/userspace/ksud/src/cli.rs @@ -122,6 +122,14 @@ enum Commands { #[command(subcommand)] command: BootInfo, }, + + /// KPM module manager + #[cfg(target_arch = "aarch64")] + Kpm { + #[command(subcommand)] + command: kpm_cmd::Kpm, + }, + /// For developers Debug { #[command(subcommand)] @@ -272,6 +280,33 @@ enum Profile { ListTemplates, } +#[cfg(target_arch = "aarch64")] +mod kpm_cmd { + use clap::Subcommand; + use std::path::PathBuf; + + #[derive(Subcommand, Debug)] + pub enum Kpm { + /// Load a KPM module: load [args] + Load { + path: PathBuf, + args: Option, + }, + /// Unload a KPM module: unload + Unload { name: String }, + /// Get number of loaded modules + Num, + /// List loaded KPM modules + List, + /// Get info of a KPM module: info + Info { name: String }, + /// Send control command to a KPM module: control + Control { name: String, args: String }, + /// Print KPM Loader version + Version, + } +} + pub fn run() -> Result<()> { #[cfg(target_os = "android")] android_logger::init_once( @@ -381,6 +416,23 @@ pub fn run() -> Result<()> { magiskboot, flash, } => crate::boot_patch::restore(boot, magiskboot, flash), + #[cfg(target_arch = "aarch64")] + Commands::Kpm { command } => { + use crate::cli::kpm_cmd::Kpm; + match command { + Kpm::Load { path, args } => crate::kpm::kpm_load(path.to_str().unwrap(), args.as_deref()), + Kpm::Unload { name } => crate::kpm::kpm_unload(&name), + Kpm::Num => crate::kpm::kpm_num().map(|_| ()), + Kpm::List => crate::kpm::kpm_list(), + Kpm::Info { name } => crate::kpm::kpm_info(&name), + Kpm::Control { name, args } => { + let ret = crate::kpm::kpm_control(&name, &args)?; + println!("{}", ret); + Ok(()) + } + Kpm::Version => crate::kpm::kpm_version_loader(), + } + } }; if let Err(e) = &result { diff --git a/userspace/ksud/src/init_event.rs b/userspace/ksud/src/init_event.rs index 26a278dd..0c8bc7b4 100644 --- a/userspace/ksud/src/init_event.rs +++ b/userspace/ksud/src/init_event.rs @@ -1,10 +1,12 @@ use crate::defs::{KSU_MOUNT_SOURCE, NO_MOUNT_PATH, NO_TMPFS_PATH}; use crate::module::{handle_updated_modules, prune_modules}; -use crate::{assets, defs, ksucalls, restorecon, utils, kpm, uid_scanner}; +use crate::{assets, defs, ksucalls, restorecon, utils, uid_scanner}; use anyhow::{Context, Result}; use log::{info, warn}; use rustix::fs::{MountFlags, mount}; use std::path::Path; +#[cfg(target_arch = "aarch64")] +use crate::kpm; pub fn on_post_data_fs() -> Result<()> { ksucalls::report_post_fs_data(); @@ -70,10 +72,12 @@ pub fn on_post_data_fs() -> Result<()> { warn!("apply root profile sepolicy failed: {e}"); } + #[cfg(target_arch = "aarch64")] if let Err(e) = kpm::start_kpm_watcher() { warn!("KPM: Failed to start KPM watcher: {}", e); } + #[cfg(target_arch = "aarch64")] if let Err(e) = kpm::load_kpm_modules() { warn!("KPM: Failed to load KPM modules: {}", e); } diff --git a/userspace/ksud/src/kpm.rs b/userspace/ksud/src/kpm.rs index fba0b188..b8959de1 100644 --- a/userspace/ksud/src/kpm.rs +++ b/userspace/ksud/src/kpm.rs @@ -1,242 +1,236 @@ -use anyhow::{anyhow, Result}; -use libc::{prctl, c_char, c_void, c_int}; +use anyhow::{anyhow, bail, Result}; +use libc::{c_char, c_int, c_void, prctl}; use notify::{RecursiveMode, Watcher}; use std::ffi::{CStr, CString, OsStr}; use std::fs; -use std::path::Path; -use std::ptr; use std::os::unix::fs::PermissionsExt; +use std::path::{Path, PathBuf}; +use std::ptr; pub const KPM_DIR: &str = "/data/adb/kpm"; const KSU_OPTIONS: u32 = 0xdeadbeef; -const SUKISU_KPM_LOAD: i32 = 28; +const SUKISU_KPM_LOAD: i32 = 28; const SUKISU_KPM_UNLOAD: i32 = 29; -const SUKISU_KPM_VERSION: i32 = 34; +const SUKISU_KPM_NUM: i32 = 30; +const SUKISU_KPM_LIST: i32 = 31; +const SUKISU_KPM_INFO: i32 = 32; +const SUKISU_KPM_CONTROL:i32 = 33; +const SUKISU_KPM_VERSION:i32 = 34; -pub fn check_kpm_version() -> Result { - let mut buffer: [u8; 1024] = [0; 1024]; +#[inline(always)] +unsafe fn kpm_prctl( + cmd: i32, + arg1: *const c_void, + arg2: *const c_void, +) -> Result { let mut out: c_int = -1; - - let _ret = unsafe { + let ret = unsafe { prctl( KSU_OPTIONS as c_int, - SUKISU_KPM_VERSION, - buffer.as_mut_ptr() as *mut c_void, - buffer.len() as *mut c_void, + cmd as c_int, + arg1, + arg2, &mut out as *mut c_int as *mut c_void, ) }; - - if out < 0 { - return Err(anyhow!("KPM: prctl returned error: {}", out)); + if ret != 0 || out < 0 { + bail!("KPM prctl error: {}", std::io::Error::from_raw_os_error(-out)); } - - let version_str = unsafe { - CStr::from_ptr(buffer.as_ptr() as *const c_char) - }.to_string_lossy().to_string(); - - log::info!("KPM: Version check result: {}", version_str); - - // 检查版本是否有效(不为空且不以Error开头) - if version_str.is_empty() || version_str.starts_with("Error") { - return Err(anyhow!("KPM: Invalid version response: {}", version_str)); - } - - Ok(version_str) + Ok(out) +} + +fn str_to_cstr R>(s: &str, f: F) -> Result { + let cs = CString::new(s)?; + Ok(f(cs.as_ptr())) +} + +fn cbuf_to_string(buf: &[u8]) -> String { + unsafe { CStr::from_ptr(buf.as_ptr() as *const c_char) } + .to_string_lossy() + .into_owned() +} + +pub fn kpm_load(path: &str, args: Option<&str>) -> Result<()> { + str_to_cstr(path, |p_path| { + let _args_cstring; + let p_args = match args { + Some(a) => { + _args_cstring = CString::new(a)?; + _args_cstring.as_ptr() + } + None => ptr::null(), + }; + unsafe { kpm_prctl(SUKISU_KPM_LOAD, p_path as _, p_args as _) }?; + println!("Success"); + Ok(()) + })? +} + +pub fn kpm_unload(name: &str) -> Result<()> { + let _ = str_to_cstr(name, |p| unsafe { + kpm_prctl(SUKISU_KPM_UNLOAD, p as _, ptr::null()) + })?; + Ok(()) +} + +pub fn kpm_num() -> Result { + let n = unsafe { kpm_prctl(SUKISU_KPM_NUM, ptr::null(), ptr::null())? }; + println!("{}", n); + Ok(n) +} + +pub fn kpm_list() -> Result<()> { + let mut buf = vec![0u8; 1024]; + unsafe { + kpm_prctl( + SUKISU_KPM_LIST, + buf.as_mut_ptr() as _, + buf.len() as *const c_void, + )?; + } + print!("{}", cbuf_to_string(&buf)); + Ok(()) +} + +pub fn kpm_info(name: &str) -> Result<()> { + let mut buf = vec![0u8; 256]; + let _ = str_to_cstr(name, |p| unsafe { + kpm_prctl(SUKISU_KPM_INFO, p as _, buf.as_mut_ptr() as _) + })?; + println!("{}", cbuf_to_string(&buf)); + Ok(()) +} + +pub fn kpm_control(name: &str, args: &str) -> Result { + str_to_cstr(name, |p_name| { + str_to_cstr(args, |p_args| unsafe { + kpm_prctl(SUKISU_KPM_CONTROL, p_name as _, p_args as _) + })? + })? +} + +pub fn kpm_version_loader() -> Result<()> { + let mut buf = vec![0u8; 1024]; + unsafe { + kpm_prctl( + SUKISU_KPM_VERSION, + buf.as_mut_ptr() as _, + buf.len() as *const c_void, + )?; + } + print!("{}", cbuf_to_string(&buf)); + Ok(()) +} + +pub fn check_kpm_version() -> Result { + let mut buf = vec![0u8; 1024]; + unsafe { + kpm_prctl( + SUKISU_KPM_VERSION, + buf.as_mut_ptr() as _, + buf.len() as *const c_void, + )?; + } + let ver = cbuf_to_string(&buf); + if ver.is_empty() || ver.starts_with("Error") { + bail!("KPM: Invalid version response: {}", ver); + } + log::info!("KPM: Version check result: {}", ver); + Ok(ver) } -// 确保 KPM 目录存在,并设置777权限 pub fn ensure_kpm_dir() -> Result<()> { let path = Path::new(KPM_DIR); - - if path.exists() { - let meta = fs::metadata(path)?; - let current = meta.permissions().mode() & 0o777; - if current != 0o777 { - log::info!("KPM: Fixing permissions to 777 for {}", KPM_DIR); - fs::set_permissions(path, fs::Permissions::from_mode(0o777))?; - } + if !path.exists() { + fs::create_dir_all(path)?; + } + let meta = fs::metadata(path)?; + if meta.permissions().mode() & 0o777 != 0o777 { + fs::set_permissions(path, fs::Permissions::from_mode(0o777))?; } Ok(()) } pub fn start_kpm_watcher() -> Result<()> { - match check_kpm_version() { - Ok(version) => { - log::info!("KPM: Version check passed, version: {}", version); - } - Err(e) => { - log::warn!("KPM: Version check failed, skipping KPM functionality: {}", e); - return Ok(()) - } - } - + check_kpm_version()?; ensure_kpm_dir()?; - - // 检查是否处于安全模式 if crate::utils::is_safe_mode() { - log::warn!("KPM: System is in safe mode, removing all KPM modules"); - if let Err(e) = remove_all_kpms() { - log::error!("KPM: Error removing all KPM modules: {}", e); - } + log::warn!("KPM: Safe mode – removing all KPM modules"); + remove_all_kpms()?; return Ok(()); } let mut watcher = notify::recommended_watcher(|res| match res { Ok(event) => handle_kpm_event(event), - Err(e) => log::error!("KPM: File monitoring error: {:?}", e), + Err(e) => log::error!("KPM: File monitor error: {:?}", e), })?; - watcher.watch(Path::new(KPM_DIR), RecursiveMode::NonRecursive)?; - log::info!("KPM: Started file watcher for directory: {}", KPM_DIR); + log::info!("KPM: File watcher started on {}", KPM_DIR); Ok(()) } -// 处理 KPM 事件 -pub fn handle_kpm_event(event: notify::Event) { +fn handle_kpm_event(event: notify::Event) { match event.kind { - notify::EventKind::Create(_) => handle_create_event(event.paths), - notify::EventKind::Remove(_) => handle_remove_event(event.paths), - notify::EventKind::Modify(_) => handle_modify_event(event.paths), + notify::EventKind::Create(_) => { + for p in event.paths { + if p.extension() == Some(OsStr::new("kpm")) { + if let Err(e) = load_kpm(&p) { + log::warn!("KPM: Failed to load {}: {}", p.display(), e); + } + } + } + } + notify::EventKind::Modify(_) => { + for p in event.paths { + log::info!("KPM: Modified file: {}", p.display()); + } + } _ => {} } } -fn handle_create_event(paths: Vec) { - for path in paths { - if path.extension() == Some(OsStr::new("kpm")) { - log::info!("KPM: Detected new KPM file: {}", path.display()); - if let Err(e) = load_kpm(&path) { - log::warn!("KPM: Failed to load {}: {}", path.display(), e); - } - } - } -} - -fn handle_remove_event(paths: Vec) { - for path in paths { - if let Some(name) = path.file_stem().and_then(|s| s.to_str()) { - log::info!("KPM: Detected KPM file removal: {}", name); - if let Err(e) = unload_kpm(name) { - log::warn!("KPM: Failed to unload {}: {}", name, e); - } - } - } -} - -fn handle_modify_event(paths: Vec) { - for path in paths { - log::info!("KPM: Modified file detected: {}", path.display()); - } -} - -// 加载 KPM 模块 pub fn load_kpm(path: &Path) -> Result<()> { - let path_str = path - .to_str() - .ok_or_else(|| anyhow!("KPM: Invalid path: {}", path.display()))?; - - let path_cstring = CString::new(path_str) - .map_err(|e| anyhow!("KPM: Failed to convert path to CString: {}", e))?; - - let mut out: c_int = -1; - - let _ret = unsafe { - prctl( - KSU_OPTIONS as c_int, - SUKISU_KPM_LOAD, - path_cstring.as_ptr() as *mut c_void, - ptr::null_mut::(), - &mut out as *mut c_int as *mut c_void, - ) - }; - - if out < 0 { - return Err(anyhow!("KPM: prctl returned error: {}", out)); - } - - if out > 0 { - log::info!("KPM: Successfully loaded module: {}", path.display()); - } - - Ok(()) + let path_str = path.to_str().ok_or_else(|| anyhow!("Invalid path"))?; + kpm_load(path_str, None) } -// 卸载 KPM 模块 pub fn unload_kpm(name: &str) -> Result<()> { - let name_cstring = CString::new(name) - .map_err(|e| anyhow!("KPM: Failed to convert name to CString: {}", e))?; - - let mut out: c_int = -1; - - let _ret = unsafe { - prctl( - KSU_OPTIONS as c_int, - SUKISU_KPM_UNLOAD, - name_cstring.as_ptr() as *mut c_void, - ptr::null_mut::(), - &mut out as *mut c_int as *mut c_void, - ) - }; - - if out < 0 { - log::warn!("KPM: prctl returned error for unload: {}", out); - return Err(anyhow!("KPM: prctl returned error: {}", out)); + kpm_unload(name)?; + if let Some(p) = find_kpm_file(name)? { + fs::remove_file(&p).ok(); + log::info!("KPM: Deleted file {}", p.display()); } - - // 尝试删除对应的KPM文件 - if let Ok(Some(path)) = find_kpm_file(name) { - if let Err(e) = fs::remove_file(&path) { - log::warn!("KPM: Failed to delete KPM file {}: {}", path.display(), e); - } else { - log::info!("KPM: Deleted KPM file: {}", path.display()); - } - } - - log::info!("KPM: Successfully unloaded module: {}", name); Ok(()) } -// 通过名称查找 KPM 文件 -fn find_kpm_file(name: &str) -> Result> { - let kpm_dir = Path::new(KPM_DIR); - if !kpm_dir.exists() { +fn find_kpm_file(name: &str) -> Result> { + let dir = Path::new(KPM_DIR); + if !dir.exists() { return Ok(None); } - - for entry in fs::read_dir(kpm_dir)? { - let path = entry?.path(); - if let Some(file_name) = path.file_stem() { - if let Some(file_name_str) = file_name.to_str() { - if file_name_str == name && path.extension() == Some(OsStr::new("kpm")) { - return Ok(Some(path)); - } - } + for entry in fs::read_dir(dir)? { + let p = entry?.path(); + if p.extension() == Some(OsStr::new("kpm")) + && p.file_stem().map_or(false, |s| s == name) + { + return Ok(Some(p)); } } Ok(None) } -// 安全模式下删除所有 KPM 模块 pub fn remove_all_kpms() -> Result<()> { - let kpm_dir = Path::new(KPM_DIR); - if !kpm_dir.exists() { - log::info!("KPM: KPM directory does not exist, nothing to remove"); + let dir = Path::new(KPM_DIR); + if !dir.exists() { return Ok(()); } - - for entry in fs::read_dir(KPM_DIR)? { - let path = entry?.path(); - if path.extension().is_some_and(|ext| ext == "kpm") { - if let Some(name) = path.file_stem() { - let name_str = name.to_string_lossy(); - log::info!("KPM: Removing module in safe mode: {}", name_str); - if let Err(e) = unload_kpm(&name_str) { - log::error!("KPM: Failed to remove module {}: {}", name_str, e); - } - if let Err(e) = fs::remove_file(&path) { - log::error!("KPM: Failed to delete file {}: {}", path.display(), e); + for entry in fs::read_dir(dir)? { + let p = entry?.path(); + if p.extension() == Some(OsStr::new("kpm")) { + if let Some(name) = p.file_stem().and_then(|s| s.to_str()) { + if let Err(e) = unload_kpm(name) { + log::error!("KPM: Failed to unload {}: {}", name, e); } } } @@ -244,53 +238,26 @@ pub fn remove_all_kpms() -> Result<()> { Ok(()) } -// 加载所有 KPM 模块 pub fn load_kpm_modules() -> Result<()> { - match check_kpm_version() { - Ok(version) => { - log::info!("KPM: Version check passed before loading modules, version: {}", version); - } - Err(e) => { - log::warn!("KPM: Version check failed, skipping module loading: {}", e); - return Ok(()); - } - } - + check_kpm_version()?; ensure_kpm_dir()?; - - let kpm_dir = Path::new(KPM_DIR); - if !kpm_dir.exists() { - log::info!("KPM: KPM directory does not exist, no modules to load"); + let dir = Path::new(KPM_DIR); + if !dir.exists() { return Ok(()); } - - let mut loaded_count = 0; - let mut failed_count = 0; - - for entry in std::fs::read_dir(KPM_DIR)? { - let path = entry?.path(); - if let Some(file_name) = path.file_stem() { - if let Some(file_name_str) = file_name.to_str() { - if file_name_str.is_empty() { - log::warn!("KPM: Invalid KPM file name: {}", path.display()); - continue; - } - } - } - if path.extension().is_some_and(|ext| ext == "kpm") { - match load_kpm(&path) { - Ok(()) => { - log::info!("KPM: Successfully loaded module: {}", path.display()); - loaded_count += 1; - } + let (mut ok, mut ng) = (0, 0); + for entry in fs::read_dir(dir)? { + let p = entry?.path(); + if p.extension() == Some(OsStr::new("kpm")) { + match load_kpm(&p) { + Ok(_) => ok += 1, Err(e) => { - log::warn!("KPM: Failed to load module {}: {}", path.display(), e); - failed_count += 1; + log::warn!("KPM: Failed to load {}: {}", p.display(), e); + ng += 1; } } } } - - log::info!("KPM: Module loading completed - loaded: {}, failed: {}", loaded_count, failed_count); + log::info!("KPM: Load done – ok: {}, failed: {}", ok, ng); Ok(()) } diff --git a/userspace/ksud/src/main.rs b/userspace/ksud/src/main.rs index 9e2f35ce..bb20c67a 100644 --- a/userspace/ksud/src/main.rs +++ b/userspace/ksud/src/main.rs @@ -5,7 +5,6 @@ mod cli; mod debug; mod defs; mod init_event; -mod kpm; mod ksucalls; #[cfg(target_os = "android")] mod magic_mount; @@ -16,6 +15,8 @@ mod sepolicy; mod su; mod utils; mod uid_scanner; +#[cfg(target_arch = "aarch64")] +mod kpm; fn main() -> anyhow::Result<()> { cli::run()