manager: Simplify kpm management by migrating to the ksud side.
This commit is contained in:
25
.github/workflows/build-manager.yml
vendored
25
.github/workflows/build-manager.yml
vendored
@@ -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
|
||||
|
||||
40
.github/workflows/kpmmgr.yml
vendored
40
.github/workflows/kpmmgr.yml
vendored
@@ -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
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
2
userspace/kpmmgr/.gitignore
vendored
2
userspace/kpmmgr/.gitignore
vendored
@@ -1,2 +0,0 @@
|
||||
/obj
|
||||
/libs
|
||||
@@ -1,6 +0,0 @@
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := kpmmgr
|
||||
LOCAL_SRC_FILES := kpmmgr.c
|
||||
include $(BUILD_EXECUTABLE)
|
||||
@@ -1,3 +0,0 @@
|
||||
APP_ABI := arm64-v8a
|
||||
APP_PLATFORM := android-24
|
||||
APP_STL := none
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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_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<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));
|
||||
}
|
||||
|
||||
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, F: FnOnce(*const c_char) -> R>(s: &str, f: F) -> Result<R> {
|
||||
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<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));
|
||||
}
|
||||
|
||||
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::<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(())
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user