manager: Simplify kpm management by migrating to the ksud side.

This commit is contained in:
ShirkNeko
2025-10-06 20:53:18 +08:00
parent 59cd8d1c3b
commit 044b4a2f9c
12 changed files with 306 additions and 487 deletions

View File

@@ -9,7 +9,6 @@ on:
- 'kernel/**'
- 'userspace/ksud/**'
- 'userspace/susfs/**'
- 'userspace/kpmmgr/**'
- 'userspace/user_scanner/**'
pull_request:
branches: [ "main" ]
@@ -97,19 +96,6 @@ jobs:
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() }}
needs: [ check-build-lkm, build-lkm ]
@@ -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:
@@ -235,11 +215,6 @@ jobs:
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: |
mkdir -p app/src/main/jniLibs/arm64-v8a

View File

@@ -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

View File

@@ -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<String>(), 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<String>(), 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()

View File

@@ -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);

View File

@@ -1,2 +0,0 @@
/obj
/libs

View File

@@ -1,6 +0,0 @@
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := kpmmgr
LOCAL_SRC_FILES := kpmmgr.c
include $(BUILD_EXECUTABLE)

View File

@@ -1,3 +0,0 @@
APP_ABI := arm64-v8a
APP_PLATFORM := android-24
APP_STL := none

View File

@@ -1,118 +0,0 @@
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/prctl.h>
#include <string.h>
#include <errno.h>
#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 <command> [args]\n", prog);
printf("Commands:\n");
printf(" load <path> <args> Load a KPM module\n");
printf(" unload <name> Unload a KPM module\n");
printf(" num Get number of loaded modules\n");
printf(" list List loaded KPM modules\n");
printf(" info <name> Get info of a KPM module\n");
printf(" control <name> <args> 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;
}

View File

@@ -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 <path> [args]
Load {
path: PathBuf,
args: Option<String>,
},
/// Unload a KPM module: unload <name>
Unload { name: String },
/// Get number of loaded modules
Num,
/// List loaded KPM modules
List,
/// Get info of a KPM module: info <name>
Info { name: String },
/// Send control command to a KPM module: control <name> <args>
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 {

View File

@@ -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);
}

View File

@@ -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_UNLOAD: i32 = 29;
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<String> {
let mut buffer: [u8; 1024] = [0; 1024];
#[inline(always)]
unsafe fn kpm_prctl(
cmd: i32,
arg1: *const c_void,
arg2: *const c_void,
) -> Result<i32> {
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));
}
Ok(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));
fn str_to_cstr<R, F: FnOnce(*const c_char) -> R>(s: &str, f: F) -> Result<R> {
let cs = CString::new(s)?;
Ok(f(cs.as_ptr()))
}
Ok(version_str)
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<i32> {
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<i32> {
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<String> {
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<std::path::PathBuf>) {
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<std::path::PathBuf>) {
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<std::path::PathBuf>) {
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::<c_void>(),
&mut out as *mut c_int as *mut c_void,
)
};
if out < 0 {
return Err(anyhow!("KPM: prctl returned error: {}", out));
let path_str = path.to_str().ok_or_else(|| anyhow!("Invalid path"))?;
kpm_load(path_str, None)
}
if out > 0 {
log::info!("KPM: Successfully loaded module: {}", path.display());
}
Ok(())
}
// 卸载 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::<c_void>(),
&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<Option<std::path::PathBuf>> {
let kpm_dir = Path::new(KPM_DIR);
if !kpm_dir.exists() {
fn find_kpm_file(name: &str) -> Result<Option<PathBuf>> {
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(())
}

View File

@@ -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()