Add UID scanner functionality and related infrastructure
- Introduced a new module `uid_scanner` in userspace for managing UID scanning. - Created a new GitHub Actions workflow for building the `user_scanner`. - Implemented kernel communication in `throne_comm.c` and `throne_comm.h` to handle user space updates and rescan requests. - Developed the `uid_scanner` daemon in C to scan user directories and manage UID whitelists. - Added configuration management for the UID scanner with support for multiple users and auto-scanning. - Implemented logging and error handling throughout the UID scanning process. - Created necessary build files for the `user_scanner` JNI integration. - Added a `.gitignore` file to exclude build artifacts.
This commit is contained in:
29
.github/workflows/build-manager.yml
vendored
29
.github/workflows/build-manager.yml
vendored
@@ -10,6 +10,7 @@ on:
|
|||||||
- 'userspace/ksud/**'
|
- 'userspace/ksud/**'
|
||||||
- 'userspace/susfs/**'
|
- 'userspace/susfs/**'
|
||||||
- 'userspace/kpmmgr/**'
|
- 'userspace/kpmmgr/**'
|
||||||
|
- 'userspace/user_scanner/**'
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [ "main" ]
|
branches: [ "main" ]
|
||||||
paths:
|
paths:
|
||||||
@@ -109,6 +110,19 @@ jobs:
|
|||||||
target: ${{ matrix.target }}
|
target: ${{ matrix.target }}
|
||||||
os: ${{ matrix.os }}
|
os: ${{ matrix.os }}
|
||||||
|
|
||||||
|
build-user_scanner:
|
||||||
|
if: ${{ always() }}
|
||||||
|
needs: [ check-build-lkm, build-lkm ]
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- target: All-linux-android
|
||||||
|
os: ubuntu-latest
|
||||||
|
uses: ./.github/workflows/user_scanner.yml
|
||||||
|
with:
|
||||||
|
target: ${{ matrix.target }}
|
||||||
|
os: ${{ matrix.os }}
|
||||||
|
|
||||||
build-ksud:
|
build-ksud:
|
||||||
if: ${{ always() }}
|
if: ${{ always() }}
|
||||||
needs: [ check-build-lkm, build-lkm ]
|
needs: [ check-build-lkm, build-lkm ]
|
||||||
@@ -176,6 +190,12 @@ jobs:
|
|||||||
- name: Setup Android SDK
|
- name: Setup Android SDK
|
||||||
uses: android-actions/setup-android@v3
|
uses: android-actions/setup-android@v3
|
||||||
|
|
||||||
|
- name: Download all userscanner artifacts
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: userscanner-all-linux-android
|
||||||
|
path: .
|
||||||
|
|
||||||
- name: Download arm64 susfs
|
- name: Download arm64 susfs
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v4
|
||||||
with:
|
with:
|
||||||
@@ -225,6 +245,15 @@ jobs:
|
|||||||
mkdir -p app/src/main/jniLibs/arm64-v8a
|
mkdir -p app/src/main/jniLibs/arm64-v8a
|
||||||
cp -f ../arm64-v8a/zakozakozako ../manager/app/src/main/jniLibs/arm64-v8a/libzakozakozako.so
|
cp -f ../arm64-v8a/zakozakozako ../manager/app/src/main/jniLibs/arm64-v8a/libzakozakozako.so
|
||||||
|
|
||||||
|
- name: Copy user_scanner to app jniLibs
|
||||||
|
run: |
|
||||||
|
mkdir -p app/src/main/jniLibs/arm64-v8a
|
||||||
|
mkdir -p app/src/main/jniLibs/x86_64
|
||||||
|
mkdir -p app/src/main/jniLibs/armeabi-v7a
|
||||||
|
cp -f ../arm64-v8a/uid_scanner ../manager/app/src/main/jniLibs/arm64-v8a/uid_scanner.so
|
||||||
|
cp -f ../x86_64/uid_scanner ../manager/app/src/main/jniLibs/x86_64/uid_scanner.so
|
||||||
|
cp -f ../armeabi-v7a/uid_scanner ../manager/app/src/main/jniLibs/armeabi-v7a/uid_scanner.so
|
||||||
|
|
||||||
- name: Build with Gradle
|
- name: Build with Gradle
|
||||||
run: ./gradlew clean assembleRelease
|
run: ./gradlew clean assembleRelease
|
||||||
|
|
||||||
|
|||||||
40
.github/workflows/user_scanner.yml
vendored
Normal file
40
.github/workflows/user_scanner.yml
vendored
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
name: Build user_scanner
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ "mian" ]
|
||||||
|
paths:
|
||||||
|
- '.github/workflows/user_scanner.yml'
|
||||||
|
- 'userspace/user_scanner/**'
|
||||||
|
workflow_dispatch:
|
||||||
|
workflow_call:
|
||||||
|
inputs:
|
||||||
|
target:
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
os:
|
||||||
|
required: false
|
||||||
|
type: string
|
||||||
|
default: self-hosted
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-user_scanner:
|
||||||
|
name: Build userspace user_scanner
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Build user_scanner
|
||||||
|
working-directory: ./userspace/user_scanner
|
||||||
|
run: |
|
||||||
|
$ANDROID_NDK_HOME/ndk-build
|
||||||
|
|
||||||
|
- name: Upload a Build Artifact
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: userscanner-all-linux-android
|
||||||
|
path: ./userspace/user_scanner/libs
|
||||||
@@ -8,6 +8,7 @@ kernelsu-objs += core_hook.o
|
|||||||
kernelsu-objs += ksud.o
|
kernelsu-objs += ksud.o
|
||||||
kernelsu-objs += embed_ksud.o
|
kernelsu-objs += embed_ksud.o
|
||||||
kernelsu-objs += kernel_compat.o
|
kernelsu-objs += kernel_compat.o
|
||||||
|
kernelsu-objs += throne_comm.o
|
||||||
|
|
||||||
ifeq ($(CONFIG_KSU_TRACEPOINT_HOOK), y)
|
ifeq ($(CONFIG_KSU_TRACEPOINT_HOOK), y)
|
||||||
kernelsu-objs += ksu_trace.o
|
kernelsu-objs += ksu_trace.o
|
||||||
|
|||||||
@@ -42,6 +42,7 @@
|
|||||||
#include "manager.h"
|
#include "manager.h"
|
||||||
#include "selinux/selinux.h"
|
#include "selinux/selinux.h"
|
||||||
#include "throne_tracker.h"
|
#include "throne_tracker.h"
|
||||||
|
#include "throne_comm.h"
|
||||||
#include "kernel_compat.h"
|
#include "kernel_compat.h"
|
||||||
|
|
||||||
#include "kpm/kpm.h"
|
#include "kpm/kpm.h"
|
||||||
@@ -223,6 +224,9 @@ int ksu_handle_rename(struct dentry *old_dentry, struct dentry *new_dentry)
|
|||||||
new_dentry->d_iname, buf);
|
new_dentry->d_iname, buf);
|
||||||
|
|
||||||
track_throne();
|
track_throne();
|
||||||
|
|
||||||
|
// Also request userspace scan for next time
|
||||||
|
ksu_request_userspace_scan();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -407,6 +411,8 @@ int ksu_handle_prctl(int option, unsigned long arg2, unsigned long arg3,
|
|||||||
post_fs_data_lock = true;
|
post_fs_data_lock = true;
|
||||||
pr_info("post-fs-data triggered\n");
|
pr_info("post-fs-data triggered\n");
|
||||||
on_post_fs_data();
|
on_post_fs_data();
|
||||||
|
// Initialize throne communication
|
||||||
|
ksu_throne_comm_init();
|
||||||
// Initializing Dynamic Signatures
|
// Initializing Dynamic Signatures
|
||||||
ksu_dynamic_manager_init();
|
ksu_dynamic_manager_init();
|
||||||
pr_info("Dynamic sign config loaded during post-fs-data\n");
|
pr_info("Dynamic sign config loaded during post-fs-data\n");
|
||||||
@@ -1027,6 +1033,7 @@ void __init ksu_core_init(void)
|
|||||||
|
|
||||||
void ksu_core_exit(void)
|
void ksu_core_exit(void)
|
||||||
{
|
{
|
||||||
|
ksu_throne_comm_exit();
|
||||||
#ifdef CONFIG_KPROBE
|
#ifdef CONFIG_KPROBE
|
||||||
pr_info("ksu_core_kprobe_exit\n");
|
pr_info("ksu_core_kprobe_exit\n");
|
||||||
// we dont use this now
|
// we dont use this now
|
||||||
|
|||||||
@@ -5,36 +5,6 @@
|
|||||||
#include <linux/version.h>
|
#include <linux/version.h>
|
||||||
#include "ss/policydb.h"
|
#include "ss/policydb.h"
|
||||||
#include "linux/key.h"
|
#include "linux/key.h"
|
||||||
#include <linux/list.h>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* list_count_nodes - count the number of nodes in a list
|
|
||||||
* @head: the head of the list
|
|
||||||
*
|
|
||||||
* This function iterates over the list starting from @head and counts
|
|
||||||
* the number of nodes in the list. It does not modify the list.
|
|
||||||
*
|
|
||||||
* Context: Any context. The function is safe to call in any context,
|
|
||||||
* including interrupt context, as it does not sleep or allocate
|
|
||||||
* memory.
|
|
||||||
*
|
|
||||||
* Return: the number of nodes in the list (excluding the head)
|
|
||||||
*/
|
|
||||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0)
|
|
||||||
static inline __maybe_unused size_t list_count_nodes(const struct list_head *head)
|
|
||||||
{
|
|
||||||
const struct list_head *pos;
|
|
||||||
size_t count = 0;
|
|
||||||
|
|
||||||
if (!head)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
list_for_each(pos, head)
|
|
||||||
count++;
|
|
||||||
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Adapt to Huawei HISI kernel without affecting other kernels ,
|
* Adapt to Huawei HISI kernel without affecting other kernels ,
|
||||||
|
|||||||
125
kernel/throne_comm.c
Normal file
125
kernel/throne_comm.c
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
#include <linux/fs.h>
|
||||||
|
#include <linux/proc_fs.h>
|
||||||
|
#include <linux/seq_file.h>
|
||||||
|
#include <linux/uaccess.h>
|
||||||
|
#include <linux/workqueue.h>
|
||||||
|
#include <linux/version.h>
|
||||||
|
|
||||||
|
#include "klog.h"
|
||||||
|
#include "throne_comm.h"
|
||||||
|
|
||||||
|
#define PROC_UID_SCANNER "ksu_uid_scanner"
|
||||||
|
|
||||||
|
static struct proc_dir_entry *proc_entry = NULL;
|
||||||
|
static struct workqueue_struct *scanner_wq = NULL;
|
||||||
|
static struct work_struct scan_work;
|
||||||
|
|
||||||
|
// Signal userspace to rescan
|
||||||
|
static bool need_rescan = false;
|
||||||
|
|
||||||
|
static void rescan_work_fn(struct work_struct *work)
|
||||||
|
{
|
||||||
|
// Signal userspace through proc interface
|
||||||
|
need_rescan = true;
|
||||||
|
pr_info("requested userspace uid rescan\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void ksu_request_userspace_scan(void)
|
||||||
|
{
|
||||||
|
if (scanner_wq) {
|
||||||
|
queue_work(scanner_wq, &scan_work);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ksu_handle_userspace_update(void)
|
||||||
|
{
|
||||||
|
// Called when userspace notifies update complete
|
||||||
|
need_rescan = false;
|
||||||
|
pr_info("userspace uid list updated\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static int uid_scanner_show(struct seq_file *m, void *v)
|
||||||
|
{
|
||||||
|
if (need_rescan) {
|
||||||
|
seq_puts(m, "RESCAN\n");
|
||||||
|
} else {
|
||||||
|
seq_puts(m, "OK\n");
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int uid_scanner_open(struct inode *inode, struct file *file)
|
||||||
|
{
|
||||||
|
return single_open(file, uid_scanner_show, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t uid_scanner_write(struct file *file, const char __user *buffer,
|
||||||
|
size_t count, loff_t *pos)
|
||||||
|
{
|
||||||
|
char cmd[16];
|
||||||
|
|
||||||
|
if (count >= sizeof(cmd))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (copy_from_user(cmd, buffer, count))
|
||||||
|
return -EFAULT;
|
||||||
|
|
||||||
|
cmd[count] = '\0';
|
||||||
|
|
||||||
|
// Remove newline if present
|
||||||
|
if (count > 0 && cmd[count-1] == '\n')
|
||||||
|
cmd[count-1] = '\0';
|
||||||
|
|
||||||
|
if (strcmp(cmd, "UPDATED") == 0) {
|
||||||
|
ksu_handle_userspace_update();
|
||||||
|
pr_info("received userspace update notification\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct proc_ops uid_scanner_proc_ops = {
|
||||||
|
.proc_open = uid_scanner_open,
|
||||||
|
.proc_read = seq_read,
|
||||||
|
.proc_write = uid_scanner_write,
|
||||||
|
.proc_lseek = seq_lseek,
|
||||||
|
.proc_release = single_release,
|
||||||
|
};
|
||||||
|
|
||||||
|
int ksu_throne_comm_init(void)
|
||||||
|
{
|
||||||
|
// Create workqueue
|
||||||
|
scanner_wq = alloc_workqueue("ksu_scanner", WQ_UNBOUND, 1);
|
||||||
|
if (!scanner_wq) {
|
||||||
|
pr_err("failed to create scanner workqueue\n");
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
INIT_WORK(&scan_work, rescan_work_fn);
|
||||||
|
|
||||||
|
// Create proc entry
|
||||||
|
proc_entry = proc_create(PROC_UID_SCANNER, 0600, NULL, &uid_scanner_proc_ops);
|
||||||
|
if (!proc_entry) {
|
||||||
|
pr_err("failed to create proc entry\n");
|
||||||
|
destroy_workqueue(scanner_wq);
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
pr_info("throne communication initialized\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ksu_throne_comm_exit(void)
|
||||||
|
{
|
||||||
|
if (proc_entry) {
|
||||||
|
proc_remove(proc_entry);
|
||||||
|
proc_entry = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (scanner_wq) {
|
||||||
|
destroy_workqueue(scanner_wq);
|
||||||
|
scanner_wq = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
pr_info("throne communication cleaned up\n");
|
||||||
|
}
|
||||||
12
kernel/throne_comm.h
Normal file
12
kernel/throne_comm.h
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
#ifndef __KSU_H_THRONE_COMM
|
||||||
|
#define __KSU_H_THRONE_COMM
|
||||||
|
|
||||||
|
void ksu_request_userspace_scan(void);
|
||||||
|
|
||||||
|
void ksu_handle_userspace_update(void);
|
||||||
|
|
||||||
|
int ksu_throne_comm_init(void);
|
||||||
|
|
||||||
|
void ksu_throne_comm_exit(void);
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -5,8 +5,6 @@
|
|||||||
#include <linux/string.h>
|
#include <linux/string.h>
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
#include <linux/version.h>
|
#include <linux/version.h>
|
||||||
#include <linux/stat.h>
|
|
||||||
#include <linux/namei.h>
|
|
||||||
|
|
||||||
#include "allowlist.h"
|
#include "allowlist.h"
|
||||||
#include "klog.h" // IWYU pragma: keep
|
#include "klog.h" // IWYU pragma: keep
|
||||||
@@ -15,12 +13,12 @@
|
|||||||
#include "throne_tracker.h"
|
#include "throne_tracker.h"
|
||||||
#include "kernel_compat.h"
|
#include "kernel_compat.h"
|
||||||
#include "dynamic_manager.h"
|
#include "dynamic_manager.h"
|
||||||
|
#include "throne_comm.h"
|
||||||
|
|
||||||
uid_t ksu_manager_uid = KSU_INVALID_UID;
|
uid_t ksu_manager_uid = KSU_INVALID_UID;
|
||||||
|
|
||||||
#define SYSTEM_PACKAGES_LIST_PATH "/data/system/packages.list.tmp"
|
#define SYSTEM_PACKAGES_LIST_PATH "/data/system/packages.list.tmp"
|
||||||
#define USER_DATA_PATH "/data/user_de/0"
|
#define KSU_UID_LIST_PATH "/data/misc/user_uid/uid_list"
|
||||||
#define USER_DATA_PATH_LEN 256
|
|
||||||
|
|
||||||
struct uid_data {
|
struct uid_data {
|
||||||
struct list_head list;
|
struct list_head list;
|
||||||
@@ -28,6 +26,111 @@ struct uid_data {
|
|||||||
char package[KSU_MAX_PACKAGE_NAME];
|
char package[KSU_MAX_PACKAGE_NAME];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Try read whitelist first, fallback if failed
|
||||||
|
static int read_uid_whitelist(struct list_head *uid_list)
|
||||||
|
{
|
||||||
|
struct file *fp;
|
||||||
|
char *file_content = NULL;
|
||||||
|
char *line, *next_line;
|
||||||
|
loff_t file_size;
|
||||||
|
loff_t pos = 0;
|
||||||
|
int count = 0;
|
||||||
|
ssize_t bytes_read;
|
||||||
|
|
||||||
|
fp = ksu_filp_open_compat(KSU_UID_LIST_PATH, O_RDONLY, 0);
|
||||||
|
if (IS_ERR(fp)) {
|
||||||
|
pr_info("whitelist not found, fallback needed\n");
|
||||||
|
return -ENOENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
file_size = fp->f_inode->i_size;
|
||||||
|
if (file_size <= 0) {
|
||||||
|
pr_info("whitelist file is empty\n");
|
||||||
|
filp_close(fp, NULL);
|
||||||
|
return -ENODATA;
|
||||||
|
}
|
||||||
|
|
||||||
|
file_content = kzalloc(file_size + 1, GFP_ATOMIC);
|
||||||
|
if (!file_content) {
|
||||||
|
pr_err("failed to allocate memory for whitelist file (%lld bytes)\n", file_size);
|
||||||
|
filp_close(fp, NULL);
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes_read = ksu_kernel_read_compat(fp, file_content, file_size, &pos);
|
||||||
|
if (bytes_read != file_size) {
|
||||||
|
pr_err("failed to read whitelist file: read %zd bytes, expected %lld bytes\n",
|
||||||
|
bytes_read, file_size);
|
||||||
|
kfree(file_content);
|
||||||
|
filp_close(fp, NULL);
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
file_content[file_size] = '\0';
|
||||||
|
filp_close(fp, NULL);
|
||||||
|
|
||||||
|
pr_info("successfully read whitelist file (%lld bytes), parsing lines...\n", file_size);
|
||||||
|
|
||||||
|
line = file_content;
|
||||||
|
while (line && *line) {
|
||||||
|
next_line = strchr(line, '\n');
|
||||||
|
if (next_line) {
|
||||||
|
*next_line = '\0';
|
||||||
|
next_line++;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *trimmed_line = line;
|
||||||
|
while (*trimmed_line == ' ' || *trimmed_line == '\t' || *trimmed_line == '\r') {
|
||||||
|
trimmed_line++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strlen(trimmed_line) > 0) {
|
||||||
|
char *line_copy = trimmed_line;
|
||||||
|
char *uid_str = strsep(&line_copy, " \t");
|
||||||
|
char *package_name = line_copy;
|
||||||
|
|
||||||
|
if (package_name) {
|
||||||
|
while (*package_name == ' ' || *package_name == '\t') {
|
||||||
|
package_name++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (uid_str && package_name && strlen(package_name) > 0) {
|
||||||
|
u32 uid;
|
||||||
|
if (!kstrtou32(uid_str, 10, &uid)) {
|
||||||
|
struct uid_data *data = kzalloc(sizeof(struct uid_data), GFP_ATOMIC);
|
||||||
|
if (data) {
|
||||||
|
data->uid = uid;
|
||||||
|
size_t pkg_len = strlen(package_name);
|
||||||
|
size_t copy_len = min(pkg_len, (size_t)(KSU_MAX_PACKAGE_NAME - 1));
|
||||||
|
strncpy(data->package, package_name, copy_len);
|
||||||
|
data->package[copy_len] = '\0';
|
||||||
|
|
||||||
|
list_add_tail(&data->list, uid_list);
|
||||||
|
count++;
|
||||||
|
|
||||||
|
if (count % 100 == 0) {
|
||||||
|
pr_info("parsed %d packages so far...\n", count);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
pr_err("failed to allocate memory for uid_data\n");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
pr_warn("invalid uid format in line: %s\n", trimmed_line);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
pr_warn("invalid line format: %s\n", trimmed_line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
line = next_line;
|
||||||
|
}
|
||||||
|
|
||||||
|
kfree(file_content);
|
||||||
|
pr_info("successfully loaded %d uids from whitelist\n", count);
|
||||||
|
return count > 0 ? 0 : -ENODATA;
|
||||||
|
}
|
||||||
|
|
||||||
static int get_pkg_from_apk_path(char *pkg, const char *path)
|
static int get_pkg_from_apk_path(char *pkg, const char *path)
|
||||||
{
|
{
|
||||||
int len = strlen(path);
|
int len = strlen(path);
|
||||||
@@ -142,138 +245,6 @@ struct my_dir_context {
|
|||||||
#define FILLDIR_ACTOR_CONTINUE 0
|
#define FILLDIR_ACTOR_CONTINUE 0
|
||||||
#define FILLDIR_ACTOR_STOP -EINVAL
|
#define FILLDIR_ACTOR_STOP -EINVAL
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
struct uid_scan_stats {
|
|
||||||
size_t total_found;
|
|
||||||
size_t errors_encountered;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct user_data_context {
|
|
||||||
struct dir_context ctx;
|
|
||||||
struct list_head *uid_list;
|
|
||||||
struct uid_scan_stats *stats;
|
|
||||||
};
|
|
||||||
|
|
||||||
FILLDIR_RETURN_TYPE user_data_actor(struct dir_context *ctx, const char *name,
|
|
||||||
int namelen, loff_t off, u64 ino,
|
|
||||||
unsigned int d_type)
|
|
||||||
{
|
|
||||||
struct user_data_context *my_ctx =
|
|
||||||
container_of(ctx, struct user_data_context, ctx);
|
|
||||||
|
|
||||||
if (!my_ctx || !my_ctx->uid_list) {
|
|
||||||
return FILLDIR_ACTOR_STOP;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!strncmp(name, "..", namelen) || !strncmp(name, ".", namelen))
|
|
||||||
return FILLDIR_ACTOR_CONTINUE;
|
|
||||||
|
|
||||||
if (d_type != DT_DIR)
|
|
||||||
return FILLDIR_ACTOR_CONTINUE;
|
|
||||||
|
|
||||||
if (namelen >= KSU_MAX_PACKAGE_NAME) {
|
|
||||||
pr_warn("Package name too long: %.*s\n", namelen, name);
|
|
||||||
if (my_ctx->stats)
|
|
||||||
my_ctx->stats->errors_encountered++;
|
|
||||||
return FILLDIR_ACTOR_CONTINUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
char package_path[USER_DATA_PATH_LEN];
|
|
||||||
if (snprintf(package_path, sizeof(package_path), "%s/%.*s",
|
|
||||||
USER_DATA_PATH, namelen, name) >= sizeof(package_path)) {
|
|
||||||
pr_err("Path too long for package: %.*s\n", namelen, name);
|
|
||||||
if (my_ctx->stats)
|
|
||||||
my_ctx->stats->errors_encountered++;
|
|
||||||
return FILLDIR_ACTOR_CONTINUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct path path;
|
|
||||||
int err = kern_path(package_path, LOOKUP_FOLLOW, &path);
|
|
||||||
if (err) {
|
|
||||||
pr_debug("Package path lookup failed: %s (err: %d)\n", package_path, err);
|
|
||||||
if (my_ctx->stats)
|
|
||||||
my_ctx->stats->errors_encountered++;
|
|
||||||
return FILLDIR_ACTOR_CONTINUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct kstat stat;
|
|
||||||
err = vfs_getattr(&path, &stat, STATX_UID, AT_STATX_SYNC_AS_STAT);
|
|
||||||
path_put(&path);
|
|
||||||
|
|
||||||
if (err) {
|
|
||||||
pr_debug("Failed to get attributes for: %s (err: %d)\n", package_path, err);
|
|
||||||
if (my_ctx->stats)
|
|
||||||
my_ctx->stats->errors_encountered++;
|
|
||||||
return FILLDIR_ACTOR_CONTINUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
uid_t uid = from_kuid(&init_user_ns, stat.uid);
|
|
||||||
if (uid == (uid_t)-1) {
|
|
||||||
pr_warn("Invalid UID for package: %.*s\n", namelen, name);
|
|
||||||
if (my_ctx->stats)
|
|
||||||
my_ctx->stats->errors_encountered++;
|
|
||||||
return FILLDIR_ACTOR_CONTINUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct uid_data *data = kzalloc(sizeof(struct uid_data), GFP_ATOMIC);
|
|
||||||
if (!data) {
|
|
||||||
pr_err("Failed to allocate memory for package: %.*s\n", namelen, name);
|
|
||||||
if (my_ctx->stats)
|
|
||||||
my_ctx->stats->errors_encountered++;
|
|
||||||
return FILLDIR_ACTOR_CONTINUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
data->uid = uid;
|
|
||||||
size_t copy_len = min(namelen, KSU_MAX_PACKAGE_NAME - 1);
|
|
||||||
strncpy(data->package, name, copy_len);
|
|
||||||
data->package[copy_len] = '\0';
|
|
||||||
|
|
||||||
list_add_tail(&data->list, my_ctx->uid_list);
|
|
||||||
|
|
||||||
if (my_ctx->stats)
|
|
||||||
my_ctx->stats->total_found++;
|
|
||||||
|
|
||||||
pr_info("UserDE UID: Found package: %s, uid: %u\n", data->package, data->uid);
|
|
||||||
|
|
||||||
return FILLDIR_ACTOR_CONTINUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
int scan_user_data_for_uids(struct list_head *uid_list)
|
|
||||||
{
|
|
||||||
struct file *dir_file;
|
|
||||||
struct uid_scan_stats stats = {0};
|
|
||||||
int ret = 0;
|
|
||||||
|
|
||||||
if (!uid_list) {
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
dir_file = ksu_filp_open_compat(USER_DATA_PATH, O_RDONLY, 0);
|
|
||||||
if (IS_ERR(dir_file)) {
|
|
||||||
pr_err("UserDE UID: Failed to open %s: %ld\n", USER_DATA_PATH, PTR_ERR(dir_file));
|
|
||||||
return PTR_ERR(dir_file);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct user_data_context ctx = {
|
|
||||||
.ctx.actor = user_data_actor,
|
|
||||||
.uid_list = uid_list,
|
|
||||||
.stats = &stats
|
|
||||||
};
|
|
||||||
|
|
||||||
ret = iterate_dir(dir_file, &ctx.ctx);
|
|
||||||
filp_close(dir_file, NULL);
|
|
||||||
|
|
||||||
if (stats.errors_encountered > 0) {
|
|
||||||
pr_warn("Encountered %zu errors while scanning user data directory\n",
|
|
||||||
stats.errors_encountered);
|
|
||||||
}
|
|
||||||
|
|
||||||
pr_info("UserDE UID: Scanned user data directory, found %zu packages with %zu errors\n",
|
|
||||||
stats.total_found, stats.errors_encountered);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
FILLDIR_RETURN_TYPE my_actor(struct dir_context *ctx, const char *name,
|
FILLDIR_RETURN_TYPE my_actor(struct dir_context *ctx, const char *name,
|
||||||
int namelen, loff_t off, u64 ino,
|
int namelen, loff_t off, u64 ino,
|
||||||
unsigned int d_type)
|
unsigned int d_type)
|
||||||
@@ -468,16 +439,22 @@ void track_throne()
|
|||||||
struct list_head uid_list;
|
struct list_head uid_list;
|
||||||
INIT_LIST_HEAD(&uid_list);
|
INIT_LIST_HEAD(&uid_list);
|
||||||
|
|
||||||
pr_info("Starting UID scan from user data directory\n");
|
pr_info("track_throne triggered, attempting whitelist read\n");
|
||||||
int ret = scan_user_data_for_uids(&uid_list);
|
|
||||||
|
// Try read whitelist first
|
||||||
|
int ret = read_uid_whitelist(&uid_list);
|
||||||
|
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
pr_warn("Failed to scan user data directory (%d), falling back to packages.list\n", ret);
|
pr_info("whitelist read failed (%d), request userspace scan\n", ret);
|
||||||
|
|
||||||
|
// Request userspace to rescan
|
||||||
|
ksu_request_userspace_scan();
|
||||||
|
|
||||||
// fallback to packages.list method
|
// fallback to packages.list method
|
||||||
struct file *fp = ksu_filp_open_compat(SYSTEM_PACKAGES_LIST_PATH, O_RDONLY, 0);
|
struct file *fp = ksu_filp_open_compat(SYSTEM_PACKAGES_LIST_PATH, O_RDONLY, 0);
|
||||||
if (IS_ERR(fp)) {
|
if (IS_ERR(fp)) {
|
||||||
pr_err("Both user data scan and packages.list failed: %ld\n", PTR_ERR(fp));
|
pr_err("%s: open " SYSTEM_PACKAGES_LIST_PATH " failed: %ld\n",
|
||||||
|
__func__, PTR_ERR(fp));
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -532,7 +509,7 @@ void track_throne()
|
|||||||
filp_close(fp, 0);
|
filp_close(fp, 0);
|
||||||
pr_info("Loaded %zu packages from packages.list fallback\n", fallback_count);
|
pr_info("Loaded %zu packages from packages.list fallback\n", fallback_count);
|
||||||
} else {
|
} else {
|
||||||
pr_info("UserDE UID: Successfully loaded %zu packages from user data directory\n", list_count_nodes(&uid_list));
|
pr_info("loaded uids from whitelist successfully\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
// now update uid list
|
// now update uid list
|
||||||
|
|||||||
@@ -31,6 +31,8 @@ object Natives {
|
|||||||
|
|
||||||
const val MINIMAL_SUPPORTED_DYNAMIC_MANAGER = 13215
|
const val MINIMAL_SUPPORTED_DYNAMIC_MANAGER = 13215
|
||||||
|
|
||||||
|
const val MINIMAL_SUPPORTED_UID_SCANNER = 13347
|
||||||
|
|
||||||
const val ROOT_UID = 0
|
const val ROOT_UID = 0
|
||||||
const val ROOT_GID = 0
|
const val ROOT_GID = 0
|
||||||
|
|
||||||
|
|||||||
@@ -48,6 +48,8 @@ import com.sukisu.ultra.ui.theme.getCardColors
|
|||||||
import com.sukisu.ultra.ui.theme.getCardElevation
|
import com.sukisu.ultra.ui.theme.getCardElevation
|
||||||
import com.sukisu.ultra.ui.util.LocalSnackbarHost
|
import com.sukisu.ultra.ui.util.LocalSnackbarHost
|
||||||
import com.sukisu.ultra.ui.util.getBugreportFile
|
import com.sukisu.ultra.ui.util.getBugreportFile
|
||||||
|
import com.sukisu.ultra.ui.util.setUidAutoScan
|
||||||
|
import com.sukisu.ultra.ui.util.setUidMultiUserScan
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
@@ -127,7 +129,7 @@ fun SettingScreen(navigator: DestinationsNavigator) {
|
|||||||
navigator.navigate(AppProfileTemplateScreenDestination)
|
navigator.navigate(AppProfileTemplateScreenDestination)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
// 卸载模块开关
|
// 卸载模块开关
|
||||||
var umountChecked by rememberSaveable {
|
var umountChecked by rememberSaveable {
|
||||||
mutableStateOf(Natives.isDefaultUmountModules())
|
mutableStateOf(Natives.isDefaultUmountModules())
|
||||||
@@ -177,6 +179,80 @@ fun SettingScreen(navigator: DestinationsNavigator) {
|
|||||||
forceSignatureVerification = enabled
|
forceSignatureVerification = enabled
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
if (Natives.version >= Natives.MINIMAL_SUPPORTED_UID_SCANNER) {
|
||||||
|
var uidAutoScanEnabled by rememberSaveable {
|
||||||
|
mutableStateOf(prefs.getBoolean("uid_auto_scan", false))
|
||||||
|
}
|
||||||
|
|
||||||
|
var uidMultiUserScanEnabled by rememberSaveable {
|
||||||
|
mutableStateOf(prefs.getBoolean("uid_multi_user_scan", false))
|
||||||
|
}
|
||||||
|
// 用户态扫描应用列表开关
|
||||||
|
SwitchItem(
|
||||||
|
icon = Icons.Filled.Scanner,
|
||||||
|
title = stringResource(R.string.uid_auto_scan_title),
|
||||||
|
summary = stringResource(R.string.uid_auto_scan_summary),
|
||||||
|
checked = uidAutoScanEnabled,
|
||||||
|
onCheckedChange = { enabled ->
|
||||||
|
scope.launch {
|
||||||
|
try {
|
||||||
|
if (setUidAutoScan(enabled)) {
|
||||||
|
uidAutoScanEnabled = enabled
|
||||||
|
prefs.edit { putBoolean("uid_auto_scan", enabled) }
|
||||||
|
|
||||||
|
// 如果关闭了用户态扫描,则同时关闭多用户扫描
|
||||||
|
if (!enabled) {
|
||||||
|
uidMultiUserScanEnabled = false
|
||||||
|
prefs.edit { putBoolean("uid_multi_user_scan", false) }
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
snackBarHost.showSnackbar(context.getString(R.string.uid_scanner_setting_failed))
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
snackBarHost.showSnackbar(
|
||||||
|
context.getString(
|
||||||
|
R.string.uid_scanner_setting_error,
|
||||||
|
e.message ?: ""
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// 多用户应用扫描开关 - 仅在启用用户态扫描时显示
|
||||||
|
AnimatedVisibility(
|
||||||
|
visible = uidAutoScanEnabled,
|
||||||
|
enter = fadeIn() + expandVertically(),
|
||||||
|
exit = fadeOut() + shrinkVertically()
|
||||||
|
) {
|
||||||
|
SwitchItem(
|
||||||
|
icon = Icons.Filled.Groups,
|
||||||
|
title = stringResource(R.string.uid_multi_user_scan_title),
|
||||||
|
summary = stringResource(R.string.uid_multi_user_scan_summary),
|
||||||
|
checked = uidMultiUserScanEnabled,
|
||||||
|
onCheckedChange = { enabled ->
|
||||||
|
scope.launch {
|
||||||
|
try {
|
||||||
|
if (setUidMultiUserScan(enabled)) {
|
||||||
|
uidMultiUserScanEnabled = enabled
|
||||||
|
prefs.edit { putBoolean("uid_multi_user_scan", enabled) }
|
||||||
|
} else {
|
||||||
|
snackBarHost.showSnackbar(context.getString(R.string.uid_scanner_setting_failed))
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
snackBarHost.showSnackbar(
|
||||||
|
context.getString(
|
||||||
|
R.string.uid_scanner_setting_error,
|
||||||
|
e.message ?: ""
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -807,4 +883,4 @@ private fun TopBar(
|
|||||||
windowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal),
|
windowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal),
|
||||||
scrollBehavior = scrollBehavior
|
scrollBehavior = scrollBehavior
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -569,3 +569,47 @@ fun getZygiskImplement(): String {
|
|||||||
Log.i(TAG, "Zygisk implement: $result")
|
Log.i(TAG, "Zygisk implement: $result")
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getUidScannerDaemonPath(): String {
|
||||||
|
return ksuApp.applicationInfo.nativeLibraryDir + File.separator + "libuid_scanner.so"
|
||||||
|
}
|
||||||
|
|
||||||
|
fun ensureUidScannerExecutable(): Boolean {
|
||||||
|
val shell = getRootShell()
|
||||||
|
val uidScannerPath = getUidScannerDaemonPath()
|
||||||
|
val targetPath = "/data/adb/uid_scanner"
|
||||||
|
|
||||||
|
if (!ShellUtils.fastCmdResult(shell, "test -f $targetPath")) {
|
||||||
|
val copyResult = ShellUtils.fastCmdResult(shell, "cp $uidScannerPath $targetPath")
|
||||||
|
if (!copyResult) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val result = ShellUtils.fastCmdResult(shell, "chmod 755 $targetPath")
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setUidAutoScan(enabled: Boolean): Boolean {
|
||||||
|
val shell = getRootShell()
|
||||||
|
if (!ensureUidScannerExecutable()) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
val enableValue = if (enabled) 1 else 0
|
||||||
|
val cmd = "${getUidScannerDaemonPath()} --auto-scan $enableValue && ${getUidScannerDaemonPath()} reload"
|
||||||
|
val result = ShellUtils.fastCmdResult(shell, cmd)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setUidMultiUserScan(enabled: Boolean): Boolean {
|
||||||
|
val shell = getRootShell()
|
||||||
|
if (!ensureUidScannerExecutable()) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
val enableValue = if (enabled) 1 else 0
|
||||||
|
val cmd = "${getUidScannerDaemonPath()} --multi-user $enableValue && ${getUidScannerDaemonPath()} reload"
|
||||||
|
val result = ShellUtils.fastCmdResult(shell, cmd)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,15 +1,13 @@
|
|||||||
package io.sukisu.ultra;
|
package io.sukisu.ultra;
|
||||||
|
|
||||||
import static com.sukisu.ultra.ui.util.KsuCliKt.getKpmmgrPath;
|
import static com.sukisu.ultra.ui.util.KsuCliKt.*;
|
||||||
import static com.sukisu.ultra.ui.util.KsuCliKt.getSuSFSDaemonPath;
|
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
|
|
||||||
public class UltraToolInstall {
|
public class UltraToolInstall {
|
||||||
private static final String OUTSIDE_KPMMGR_PATH = "/data/adb/ksu/bin/kpmmgr";
|
private static final String OUTSIDE_KPMMGR_PATH = "/data/adb/ksu/bin/kpmmgr";
|
||||||
private static final String OUTSIDE_SUSFSD_PATH = "/data/adb/ksu/bin/susfsd";
|
private static final String OUTSIDE_SUSFSD_PATH = "/data/adb/ksu/bin/susfsd";
|
||||||
|
|
||||||
@SuppressLint("SetWorldReadable")
|
@SuppressLint("SetWorldReadable")
|
||||||
@SuppressWarnings("ResultOfMethodCallIgnored")
|
|
||||||
public static void tryToInstall() {
|
public static void tryToInstall() {
|
||||||
String kpmmgrPath = getKpmmgrPath();
|
String kpmmgrPath = getKpmmgrPath();
|
||||||
if (UltraShellHelper.isPathExists(OUTSIDE_KPMMGR_PATH)) {
|
if (UltraShellHelper.isPathExists(OUTSIDE_KPMMGR_PATH)) {
|
||||||
|
|||||||
@@ -653,4 +653,11 @@
|
|||||||
<!-- KPM选项单选按钮组字符串 -->
|
<!-- KPM选项单选按钮组字符串 -->
|
||||||
<string name="kpm_follow_kernel_file">跟随内核</string>
|
<string name="kpm_follow_kernel_file">跟随内核</string>
|
||||||
<string name="kpm_follow_kernel_description">原样使用内核,不进行任何 KPM 修改</string>
|
<string name="kpm_follow_kernel_description">原样使用内核,不进行任何 KPM 修改</string>
|
||||||
|
<!-- UID Scanner Settings -->
|
||||||
|
<string name="uid_auto_scan_title">用户态扫描应用列表</string>
|
||||||
|
<string name="uid_auto_scan_summary">开启后将使用用户态扫描应用列表,提高稳定性 (因内核扫描应用列表出现卡死等问题可以尝试打开此选项) </string>
|
||||||
|
<string name="uid_multi_user_scan_title">多用户应用扫描</string>
|
||||||
|
<string name="uid_multi_user_scan_summary">开启后将扫描所有用户的应用,包括工作资料等</string>
|
||||||
|
<string name="uid_scanner_setting_failed">设置失败,请检查权限</string>
|
||||||
|
<string name="uid_scanner_setting_error">设置失败: %s</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -661,4 +661,11 @@ Important Note:\n
|
|||||||
<!-- KPM option radio group strings -->
|
<!-- KPM option radio group strings -->
|
||||||
<string name="kpm_follow_kernel_file">Follow Kernel</string>
|
<string name="kpm_follow_kernel_file">Follow Kernel</string>
|
||||||
<string name="kpm_follow_kernel_description">Use kernel as-is without any KPM modifications</string>
|
<string name="kpm_follow_kernel_description">Use kernel as-is without any KPM modifications</string>
|
||||||
|
<!-- UID Scanner Settings -->
|
||||||
|
<string name="uid_auto_scan_title">User-mode scanning application list</string>
|
||||||
|
<string name="uid_auto_scan_summary">Enabling this option will use user-mode scanning for the application list, improving stability. (If you encounter issues such as freezing during kernel scanning of the application list, you may try enabling this option.)</string>
|
||||||
|
<string name="uid_multi_user_scan_title">Multi-User Application Scanning</string>
|
||||||
|
<string name="uid_multi_user_scan_summary">When enabled, scans applications for all users, including work profiles</string>
|
||||||
|
<string name="uid_scanner_setting_failed">Setting failed, please check permissions</string>
|
||||||
|
<string name="uid_scanner_setting_error">Setting failed: %s</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
use crate::defs::{KSU_MOUNT_SOURCE, NO_MOUNT_PATH, NO_TMPFS_PATH};
|
use crate::defs::{KSU_MOUNT_SOURCE, NO_MOUNT_PATH, NO_TMPFS_PATH};
|
||||||
use crate::kpm;
|
|
||||||
use crate::module::{handle_updated_modules, prune_modules};
|
use crate::module::{handle_updated_modules, prune_modules};
|
||||||
use crate::{assets, defs, ksucalls, restorecon, utils};
|
use crate::{assets, defs, ksucalls, restorecon, utils, kpm, uid_scanner};
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
use log::{info, warn};
|
use log::{info, warn};
|
||||||
use rustix::fs::{MountFlags, mount};
|
use rustix::fs::{MountFlags, mount};
|
||||||
@@ -35,6 +34,9 @@ pub fn on_post_data_fs() -> Result<()> {
|
|||||||
|
|
||||||
assets::ensure_binaries(true).with_context(|| "Failed to extract bin assets")?;
|
assets::ensure_binaries(true).with_context(|| "Failed to extract bin assets")?;
|
||||||
|
|
||||||
|
// Start UID scanner daemon with highest priority
|
||||||
|
uid_scanner::start_uid_scanner_daemon()?;
|
||||||
|
|
||||||
// tell kernel that we've mount the module, so that it can do some optimization
|
// tell kernel that we've mount the module, so that it can do some optimization
|
||||||
ksucalls::report_module_mounted();
|
ksucalls::report_module_mounted();
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ mod restorecon;
|
|||||||
mod sepolicy;
|
mod sepolicy;
|
||||||
mod su;
|
mod su;
|
||||||
mod utils;
|
mod utils;
|
||||||
|
mod uid_scanner;
|
||||||
|
|
||||||
fn main() -> anyhow::Result<()> {
|
fn main() -> anyhow::Result<()> {
|
||||||
cli::run()
|
cli::run()
|
||||||
|
|||||||
91
userspace/ksud/src/uid_scanner.rs
Normal file
91
userspace/ksud/src/uid_scanner.rs
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
use crate::utils;
|
||||||
|
use anyhow::{Context, Result};
|
||||||
|
use log::{info, warn};
|
||||||
|
use std::{
|
||||||
|
fs,
|
||||||
|
io::Write,
|
||||||
|
os::unix::{
|
||||||
|
fs::{symlink, PermissionsExt},
|
||||||
|
process::CommandExt,
|
||||||
|
},
|
||||||
|
path::Path,
|
||||||
|
process::{Command, Stdio},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn start_uid_scanner_daemon() -> Result<()> {
|
||||||
|
const SCANNER_PATH: &str = "/data/adb/uid_scanner";
|
||||||
|
const LINK_DIR: &str = "/data/adb/ksu/bin";
|
||||||
|
const LINK_PATH: &str = "/data/adb/ksu/bin/uid_scanner";
|
||||||
|
const SERVICE_DIR: &str = "/data/adb/service.d";
|
||||||
|
const SERVICE_PATH: &str = "/data/adb/service.d/uid_scanner.sh";
|
||||||
|
|
||||||
|
if !Path::new(SCANNER_PATH).exists() {
|
||||||
|
warn!("uid scanner binary not found at {}", SCANNER_PATH);
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Err(e) = fs::set_permissions(SCANNER_PATH, fs::Permissions::from_mode(0o755)) {
|
||||||
|
warn!("failed to set permissions for {}: {}", SCANNER_PATH, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
{
|
||||||
|
if let Err(e) = fs::create_dir_all(LINK_DIR) {
|
||||||
|
warn!("failed to create {}: {}", LINK_DIR, e);
|
||||||
|
} else if !Path::new(LINK_PATH).exists() {
|
||||||
|
match symlink(SCANNER_PATH, LINK_PATH) {
|
||||||
|
Ok(_) => info!("created symlink {} -> {}", SCANNER_PATH, LINK_PATH),
|
||||||
|
Err(e) => warn!("failed to create symlink: {}", e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Err(e) = fs::create_dir_all(SERVICE_DIR) {
|
||||||
|
warn!("failed to create {}: {}", SERVICE_DIR, e);
|
||||||
|
} else if !Path::new(SERVICE_PATH).exists() {
|
||||||
|
let content = r#"#!/system/bin/sh
|
||||||
|
# KSU uid_scanner auto-restart script
|
||||||
|
until [ -d "/sdcard/Android" ]; do sleep 1; done
|
||||||
|
sleep 10
|
||||||
|
/data/adb/uid_scanner restart
|
||||||
|
"#;
|
||||||
|
match fs::OpenOptions::new()
|
||||||
|
.write(true)
|
||||||
|
.create_new(true)
|
||||||
|
.open(SERVICE_PATH)
|
||||||
|
.and_then(|mut f| {
|
||||||
|
f.write_all(content.as_bytes())?;
|
||||||
|
f.sync_all()?;
|
||||||
|
fs::set_permissions(SERVICE_PATH, fs::Permissions::from_mode(0o755))
|
||||||
|
}) {
|
||||||
|
Ok(_) => info!("created service script {}", SERVICE_PATH),
|
||||||
|
Err(e) => warn!("failed to write {}: {}", SERVICE_PATH, e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
info!("starting uid scanner daemon with highest priority");
|
||||||
|
let mut cmd = Command::new(SCANNER_PATH);
|
||||||
|
cmd.arg("start")
|
||||||
|
.stdin(Stdio::null())
|
||||||
|
.stdout(Stdio::null())
|
||||||
|
.stderr(Stdio::null())
|
||||||
|
.current_dir("/");
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
cmd.pre_exec(|| {
|
||||||
|
libc::nice(-20);
|
||||||
|
libc::setsid();
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
match cmd.spawn() {
|
||||||
|
Ok(child) => {
|
||||||
|
info!("uid scanner daemon started with pid: {}", child.id());
|
||||||
|
std::mem::drop(child);
|
||||||
|
}
|
||||||
|
Err(e) => warn!("failed to start uid scanner daemon: {}", e),
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
2
userspace/user_scanner/.gitignore
vendored
Normal file
2
userspace/user_scanner/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
/obj
|
||||||
|
/libs
|
||||||
8
userspace/user_scanner/jni/Android.mk
Normal file
8
userspace/user_scanner/jni/Android.mk
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
LOCAL_PATH := $(call my-dir)
|
||||||
|
|
||||||
|
include $(CLEAR_VARS)
|
||||||
|
LOCAL_MODULE := uid_scanner
|
||||||
|
LOCAL_SRC_FILES := uid_scanner.c
|
||||||
|
LOCAL_LDLIBS := -llog
|
||||||
|
LOCAL_CFLAGS := -Wall -Wextra -std=c99
|
||||||
|
include $(BUILD_EXECUTABLE)
|
||||||
3
userspace/user_scanner/jni/Application.mk
Normal file
3
userspace/user_scanner/jni/Application.mk
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
APP_ABI := arm64-v8a x86_64 armeabi-v7a
|
||||||
|
APP_PLATFORM := android-35
|
||||||
|
APP_STL := none
|
||||||
984
userspace/user_scanner/jni/uid_scanner.c
Normal file
984
userspace/user_scanner/jni/uid_scanner.c
Normal file
@@ -0,0 +1,984 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <android/log.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
#define LOG_TAG "User_UID_Scanner"
|
||||||
|
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
|
||||||
|
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
|
||||||
|
|
||||||
|
// Paths and constants
|
||||||
|
#define USER_DATA_BASE_PATH "/data/user_de"
|
||||||
|
#define KSU_UID_LIST_PATH "/data/misc/user_uid/uid_list"
|
||||||
|
#define PROC_COMM_PATH "/proc/ksu_uid_scanner"
|
||||||
|
#define PID_FILE_PATH "/data/misc/user_uid/uid_scanner.pid"
|
||||||
|
#define LOG_FILE_PATH "/data/misc/user_uid/uid_scanner.log"
|
||||||
|
#define CONFIG_FILE_PATH "/data/misc/user_uid/uid_scanner.conf"
|
||||||
|
|
||||||
|
#define MAX_PACKAGE_NAME 256
|
||||||
|
#define MAX_PATH_LEN 512
|
||||||
|
#define MAX_LOG_SIZE (1024 * 1024) // 1MB
|
||||||
|
#define MAX_USERS 8
|
||||||
|
#define MAX_RETRIES 3
|
||||||
|
#define RETRY_DELAY 60
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
LANG_EN = 0,
|
||||||
|
LANG_ZH = 1
|
||||||
|
} language_t;
|
||||||
|
|
||||||
|
struct scanner_config {
|
||||||
|
language_t language;
|
||||||
|
int multi_user_scan;
|
||||||
|
int scan_interval;
|
||||||
|
int log_level;
|
||||||
|
int auto_scan;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct uid_data {
|
||||||
|
int uid;
|
||||||
|
char package[MAX_PACKAGE_NAME];
|
||||||
|
struct uid_data *next;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
const char *en;
|
||||||
|
const char *zh;
|
||||||
|
} message_t;
|
||||||
|
|
||||||
|
// Global variables
|
||||||
|
static volatile int manual_scan_flag = 0;
|
||||||
|
static volatile int should_exit = 0;
|
||||||
|
static volatile int should_reload = 0;
|
||||||
|
static struct uid_data *uid_list_head = NULL;
|
||||||
|
static int log_fd = -1;
|
||||||
|
|
||||||
|
static struct scanner_config config = {
|
||||||
|
.language = LANG_EN,
|
||||||
|
.multi_user_scan = 0,
|
||||||
|
.scan_interval = 5,
|
||||||
|
.log_level = 1,
|
||||||
|
.auto_scan = 0
|
||||||
|
};
|
||||||
|
|
||||||
|
int save_config(void);
|
||||||
|
|
||||||
|
// message dictionary
|
||||||
|
static const message_t messages[] = {
|
||||||
|
{"Signal %d received", "收到信号 %d"},
|
||||||
|
{"Reload signal", "重载信号"},
|
||||||
|
{"User signal", "用户信号"},
|
||||||
|
{"Log rotated", "日志轮转"},
|
||||||
|
{"Fork failed: %s", "Fork失败: %s"},
|
||||||
|
{"setsid failed: %s", "setsid失败: %s"},
|
||||||
|
{"Second fork failed: %s", "第二次fork失败: %s"},
|
||||||
|
{"chdir failed: %s", "目录切换失败: %s"},
|
||||||
|
{"PID file create failed %s: %s", "PID文件创建失败 %s: %s"},
|
||||||
|
{"PID file created: %d", "PID文件已创建: %d"},
|
||||||
|
{"Daemon not running", "守护进程未运行"},
|
||||||
|
{"Stopping daemon (PID: %d)", "停止守护进程 (PID: %d)"},
|
||||||
|
{"Kill signal failed: %s", "终止信号失败: %s"},
|
||||||
|
{"Daemon stopped", "守护进程已停止"},
|
||||||
|
{"Force terminating", "强制终止中"},
|
||||||
|
{"Daemon killed", "守护进程已杀死"},
|
||||||
|
{"Cannot stop daemon", "无法停止守护进程"},
|
||||||
|
{"Restarting daemon", "重启守护进程"},
|
||||||
|
{"Cannot stop old daemon", "无法停止旧守护进程"},
|
||||||
|
{"Starting new daemon", "启动新守护进程"},
|
||||||
|
{"Status: Not running", "状态: 未运行"},
|
||||||
|
{"Status: Running (PID: %d)", "状态: 运行中 (PID: %d)"},
|
||||||
|
{"Recent logs:", "最近日志:"},
|
||||||
|
{"Status: Stopped (stale PID)", "状态: 已停止 (陈旧PID)"},
|
||||||
|
{"Sending reload signal (PID: %d)", "发送重载信号 (PID: %d)"},
|
||||||
|
{"Reload signal sent", "重载信号已发送"},
|
||||||
|
{"Reload signal failed: %s", "重载信号失败: %s"},
|
||||||
|
{"Directory open failed %s: %s", "目录打开失败 %s: %s"},
|
||||||
|
{"Scan started", "扫描开始"},
|
||||||
|
{"Package name too long: %s", "包名过长: %s"},
|
||||||
|
{"File stat failed %s: %s", "文件状态获取失败 %s: %s"},
|
||||||
|
{"Memory allocation failed", "内存分配失败"},
|
||||||
|
{"Scan complete, found %d packages", "扫描完成,发现 %d 个包"},
|
||||||
|
{"Whitelist file open failed %s: %s", "白名单文件打开失败 %s: %s"},
|
||||||
|
{"Whitelist written %d entries", "白名单写入 %d 个条目"},
|
||||||
|
{"Kernel comm file open failed %s: %s", "内核通信文件打开失败 %s: %s"},
|
||||||
|
{"Kernel comm write failed %s: %s", "内核通信写入失败 %s: %s"},
|
||||||
|
{"Kernel notified", "内核已通知"},
|
||||||
|
{"Performing scan and update", "执行扫描和更新"},
|
||||||
|
{"Scan failed", "扫描失败"},
|
||||||
|
{"Whitelist write failed", "白名单写入失败"},
|
||||||
|
{"Scan completed successfully", "扫描成功完成"},
|
||||||
|
{"Whitelist not found: %s", "白名单未找到: %s"},
|
||||||
|
{"Current whitelist:", "当前白名单:"},
|
||||||
|
{"One-time scan", "一次性扫描"},
|
||||||
|
{"Invalid argument: %s", "无效参数: %s"},
|
||||||
|
{"Daemon already running", "守护进程已运行"},
|
||||||
|
{"Starting daemon", "启动守护进程"},
|
||||||
|
{"Daemon startup failed", "守护进程启动失败"},
|
||||||
|
{"Daemon started", "守护进程已启动"},
|
||||||
|
{"Reload request received", "收到重载请求"},
|
||||||
|
{"Kernel rescan request", "内核重扫描请求"},
|
||||||
|
{"Daemon exiting", "守护进程退出中"},
|
||||||
|
{"Daemon exited", "守护进程已退出"},
|
||||||
|
{"Config loaded", "配置已加载"},
|
||||||
|
{"Config saved", "配置已保存"},
|
||||||
|
{"Config load failed: %s", "配置加载失败: %s"},
|
||||||
|
{"Config save failed: %s", "配置保存失败: %s"},
|
||||||
|
{"Language switched to English", "语言切换到英文"},
|
||||||
|
{"Language switched to Chinese", "语言切换到中文"},
|
||||||
|
{"Multi-user scan enabled", "多用户扫描启用"},
|
||||||
|
{"Multi-user scan disabled", "多用户扫描禁用"},
|
||||||
|
{"Scanning directory: %s", "扫描目录: %s"},
|
||||||
|
{"Found %d users", "发现 %d 个用户"},
|
||||||
|
{"Using fallback user detection", "使用备用用户检测"},
|
||||||
|
{"Auto scan enabled", "自动扫描启用"},
|
||||||
|
{"Auto scan disabled", "自动扫描禁用"},
|
||||||
|
{"Auto scan disabled, daemon loaded", "自动扫描禁用,守护进程已加载"},
|
||||||
|
{"Auto scan disabled, skipping", "自动扫描禁用,跳过"},
|
||||||
|
{"Auto scan disabled, ignoring kernel request", "自动扫描禁用,忽略内核请求"},
|
||||||
|
{"Retry attempt %d/%d", "重试 %d/%d"},
|
||||||
|
{"Max retries reached, waiting %d seconds", "达到最大重试次数,等待 %d 秒"},
|
||||||
|
{"Operation failed after retries", "重试后操作失败"},
|
||||||
|
{"Auto scan disabled, operation not allowed", "自动扫描禁用,操作不被允许"},
|
||||||
|
{"Manual scan requested, ignoring auto_scan setting", "手动扫描请求,忽略自动扫描设置"}
|
||||||
|
};
|
||||||
|
|
||||||
|
#define MSG_COUNT (sizeof(messages) / sizeof(messages[0]))
|
||||||
|
|
||||||
|
const char* get_message(int msg_id) {
|
||||||
|
if (msg_id < 0 || msg_id >= (int)MSG_COUNT) {
|
||||||
|
return "Unknown message";
|
||||||
|
}
|
||||||
|
return (config.language == LANG_ZH) ? messages[msg_id].zh : messages[msg_id].en;
|
||||||
|
}
|
||||||
|
|
||||||
|
void write_log(const char *level, int msg_id, ...) {
|
||||||
|
char buffer[1024];
|
||||||
|
char formatted_msg[1024];
|
||||||
|
time_t now = time(NULL);
|
||||||
|
struct tm *tm_info = localtime(&now);
|
||||||
|
va_list args;
|
||||||
|
|
||||||
|
va_start(args, msg_id);
|
||||||
|
vsnprintf(formatted_msg, sizeof(formatted_msg), get_message(msg_id), args);
|
||||||
|
va_end(args);
|
||||||
|
|
||||||
|
strftime(buffer, 64, "[%H:%M:%S]", tm_info);
|
||||||
|
snprintf(buffer + strlen(buffer), sizeof(buffer) - strlen(buffer), " %s: %s", level, formatted_msg);
|
||||||
|
|
||||||
|
if (log_fd != -1) {
|
||||||
|
dprintf(log_fd, "%s\n", buffer);
|
||||||
|
fsync(log_fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(level, "ERROR") == 0) {
|
||||||
|
LOGE("%s", formatted_msg);
|
||||||
|
} else {
|
||||||
|
LOGI("%s", formatted_msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retry wrapper for operations
|
||||||
|
int retry_operation(int (*operation)(void), const char *op_name) {
|
||||||
|
(void)op_name;
|
||||||
|
for (int attempt = 1; attempt <= MAX_RETRIES; attempt++) {
|
||||||
|
int result = operation();
|
||||||
|
if (result == 0) {
|
||||||
|
return 0; // Success
|
||||||
|
}
|
||||||
|
|
||||||
|
if (attempt < MAX_RETRIES) {
|
||||||
|
write_log("WARN", 69, attempt, MAX_RETRIES); // Retry attempt X/Y
|
||||||
|
sleep(1);
|
||||||
|
} else {
|
||||||
|
write_log("ERROR", 70, RETRY_DELAY); // Max retries reached
|
||||||
|
sleep(RETRY_DELAY);
|
||||||
|
write_log("ERROR", 71); // Operation failed after retries
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ensure_directory_exists(void) {
|
||||||
|
system("mkdir -p /data/misc/user_uid");
|
||||||
|
}
|
||||||
|
|
||||||
|
void parse_config_line(const char *key, const char *value) {
|
||||||
|
if (strcmp(key, "language") == 0) {
|
||||||
|
config.language = (strcmp(value, "zh") == 0) ? LANG_ZH : LANG_EN;
|
||||||
|
} else if (strcmp(key, "multi_user_scan") == 0) {
|
||||||
|
config.multi_user_scan = atoi(value);
|
||||||
|
} else if (strcmp(key, "scan_interval") == 0) {
|
||||||
|
config.scan_interval = atoi(value);
|
||||||
|
if (config.scan_interval < 1) config.scan_interval = 5;
|
||||||
|
} else if (strcmp(key, "log_level") == 0) {
|
||||||
|
config.log_level = atoi(value);
|
||||||
|
} else if (strcmp(key, "auto_scan") == 0) {
|
||||||
|
config.auto_scan = atoi(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int load_config(void) {
|
||||||
|
FILE *fp = fopen(CONFIG_FILE_PATH, "r");
|
||||||
|
if (!fp) {
|
||||||
|
write_log("WARN", 56, "配置文件不存在,使用默认配置");
|
||||||
|
return save_config();
|
||||||
|
}
|
||||||
|
|
||||||
|
char line[256];
|
||||||
|
while (fgets(line, sizeof(line), fp)) {
|
||||||
|
line[strcspn(line, "\n")] = 0;
|
||||||
|
|
||||||
|
if (line[0] == '#' || line[0] == '\0') continue;
|
||||||
|
|
||||||
|
char key[64], value[64];
|
||||||
|
if (sscanf(line, "%63[^=]=%63s", key, value) == 2) {
|
||||||
|
parse_config_line(key, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(fp);
|
||||||
|
write_log("INFO", 54); // Config loaded
|
||||||
|
write_log("INFO", config.auto_scan ? 64 : 65); // 记录当前自动扫描状态
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int save_config(void) {
|
||||||
|
ensure_directory_exists();
|
||||||
|
|
||||||
|
FILE *fp = fopen(CONFIG_FILE_PATH, "w");
|
||||||
|
if (!fp) {
|
||||||
|
write_log("ERROR", 57, strerror(errno)); // Config save failed
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(fp, "# UID Scanner Configuration\n");
|
||||||
|
fprintf(fp, "# Language: en (English) or zh (Chinese)\n");
|
||||||
|
fprintf(fp, "language=%s\n", (config.language == LANG_ZH) ? "zh" : "en");
|
||||||
|
fprintf(fp, "# Multi-user scanning: 0=disabled, 1=enabled\n");
|
||||||
|
fprintf(fp, "multi_user_scan=%d\n", config.multi_user_scan);
|
||||||
|
fprintf(fp, "# Scan interval in seconds\n");
|
||||||
|
fprintf(fp, "scan_interval=%d\n", config.scan_interval);
|
||||||
|
fprintf(fp, "# Log level: 0=minimal, 1=normal, 2=verbose\n");
|
||||||
|
fprintf(fp, "log_level=%d\n", config.log_level);
|
||||||
|
fprintf(fp, "# Auto scan: 0=disabled, 1=enabled\n");
|
||||||
|
fprintf(fp, "auto_scan=%d\n", config.auto_scan);
|
||||||
|
|
||||||
|
fclose(fp);
|
||||||
|
write_log("INFO", 55); // Config saved
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_language(language_t lang) {
|
||||||
|
config.language = lang;
|
||||||
|
save_config();
|
||||||
|
write_log("INFO", (lang == LANG_ZH) ? 59 : 58);
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_multi_user_scan(int enabled) {
|
||||||
|
config.multi_user_scan = enabled;
|
||||||
|
save_config();
|
||||||
|
write_log("INFO", enabled ? 60 : 61);
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_auto_scan(int enabled) {
|
||||||
|
config.auto_scan = enabled;
|
||||||
|
save_config();
|
||||||
|
write_log("INFO", enabled ? 64 : 65);
|
||||||
|
}
|
||||||
|
|
||||||
|
void signal_handler(int sig) {
|
||||||
|
switch (sig) {
|
||||||
|
case SIGTERM:
|
||||||
|
case SIGINT:
|
||||||
|
should_exit = 1;
|
||||||
|
write_log("INFO", 0, sig);
|
||||||
|
break;
|
||||||
|
case SIGHUP:
|
||||||
|
should_reload = 1;
|
||||||
|
write_log("INFO", 1);
|
||||||
|
break;
|
||||||
|
case SIGUSR1:
|
||||||
|
should_reload = 1;
|
||||||
|
write_log("INFO", 2);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void manage_log_file(void) {
|
||||||
|
struct stat st;
|
||||||
|
if (log_fd == -1 || fstat(log_fd, &st) != 0) return;
|
||||||
|
|
||||||
|
if (st.st_size > MAX_LOG_SIZE) {
|
||||||
|
close(log_fd);
|
||||||
|
char backup_path[MAX_PATH_LEN];
|
||||||
|
snprintf(backup_path, sizeof(backup_path), "%s.old", LOG_FILE_PATH);
|
||||||
|
rename(LOG_FILE_PATH, backup_path);
|
||||||
|
log_fd = open(LOG_FILE_PATH, O_WRONLY | O_CREAT | O_APPEND, 0644);
|
||||||
|
if (log_fd != -1) {
|
||||||
|
write_log("INFO", 3); // Log rotated
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void setup_daemon_stdio(void) {
|
||||||
|
close(STDIN_FILENO);
|
||||||
|
close(STDOUT_FILENO);
|
||||||
|
close(STDERR_FILENO);
|
||||||
|
open("/dev/null", O_RDONLY);
|
||||||
|
open("/dev/null", O_WRONLY);
|
||||||
|
open("/dev/null", O_WRONLY);
|
||||||
|
}
|
||||||
|
|
||||||
|
int daemonize(void) {
|
||||||
|
pid_t pid = fork();
|
||||||
|
if (pid < 0) {
|
||||||
|
LOGE(get_message(4), strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (pid > 0) exit(0);
|
||||||
|
|
||||||
|
if (setsid() < 0) {
|
||||||
|
LOGE(get_message(5), strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
signal(SIGHUP, SIG_IGN);
|
||||||
|
pid = fork();
|
||||||
|
if (pid < 0) {
|
||||||
|
LOGE(get_message(6), strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (pid > 0) exit(0);
|
||||||
|
|
||||||
|
umask(0);
|
||||||
|
if (chdir("/") < 0) {
|
||||||
|
LOGE(get_message(7), strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
setup_daemon_stdio();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int write_pid_file(void) {
|
||||||
|
ensure_directory_exists();
|
||||||
|
FILE *fp = fopen(PID_FILE_PATH, "w");
|
||||||
|
if (!fp) {
|
||||||
|
write_log("ERROR", 8, PID_FILE_PATH, strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
fprintf(fp, "%d\n", getpid());
|
||||||
|
fclose(fp);
|
||||||
|
write_log("INFO", 9, getpid());
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
pid_t read_pid_file(void) {
|
||||||
|
FILE *fp = fopen(PID_FILE_PATH, "r");
|
||||||
|
if (!fp) return 0;
|
||||||
|
|
||||||
|
pid_t pid = 0;
|
||||||
|
if (fscanf(fp, "%d", &pid) != 1) pid = 0;
|
||||||
|
fclose(fp);
|
||||||
|
return pid;
|
||||||
|
}
|
||||||
|
|
||||||
|
int is_daemon_running(void) {
|
||||||
|
pid_t pid = read_pid_file();
|
||||||
|
if (pid <= 0) return 0;
|
||||||
|
|
||||||
|
if (kill(pid, 0) == 0) {
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
unlink(PID_FILE_PATH);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int stop_daemon(void) {
|
||||||
|
pid_t pid = read_pid_file();
|
||||||
|
if (pid <= 0) {
|
||||||
|
printf("%s\n", get_message(10));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf(get_message(11), pid);
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
if (kill(pid, SIGTERM) != 0) {
|
||||||
|
printf(get_message(12), strerror(errno));
|
||||||
|
printf("\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait up to 30 seconds
|
||||||
|
for (int i = 0; i < 30; i++) {
|
||||||
|
if (kill(pid, 0) != 0) {
|
||||||
|
printf("%s\n", get_message(13));
|
||||||
|
unlink(PID_FILE_PATH);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
sleep(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("%s\n", get_message(14));
|
||||||
|
if (kill(pid, SIGKILL) == 0) {
|
||||||
|
printf("%s\n", get_message(15));
|
||||||
|
unlink(PID_FILE_PATH);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("%s\n", get_message(16));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int restart_daemon(void) {
|
||||||
|
printf("%s\n", get_message(17));
|
||||||
|
stop_daemon();
|
||||||
|
sleep(2);
|
||||||
|
|
||||||
|
if (is_daemon_running()) {
|
||||||
|
printf("%s\n", get_message(18));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("%s\n", get_message(19));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void show_status(void) {
|
||||||
|
pid_t pid = read_pid_file();
|
||||||
|
if (pid <= 0) {
|
||||||
|
printf("%s\n", get_message(20));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (kill(pid, 0) == 0) {
|
||||||
|
printf(get_message(21), pid);
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
if (access(LOG_FILE_PATH, R_OK) == 0) {
|
||||||
|
printf("\n%s\n", get_message(22));
|
||||||
|
char cmd[512];
|
||||||
|
snprintf(cmd, sizeof(cmd), "tail -n 10 %s", LOG_FILE_PATH);
|
||||||
|
system(cmd);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
printf("%s\n", get_message(23));
|
||||||
|
unlink(PID_FILE_PATH);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void reload_daemon(void) {
|
||||||
|
pid_t pid = read_pid_file();
|
||||||
|
if (pid <= 0 || kill(pid, 0) != 0) {
|
||||||
|
printf("%s\n", get_message(10));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf(get_message(24), pid);
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
if (kill(pid, SIGUSR1) == 0) {
|
||||||
|
printf("%s\n", get_message(25));
|
||||||
|
} else {
|
||||||
|
printf(get_message(26), strerror(errno));
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int get_users_from_pm(char user_dirs[][MAX_PATH_LEN], int max_users) {
|
||||||
|
FILE *fp = popen("pm list users 2>/dev/null | grep 'UserInfo{' | sed 's/.*UserInfo{\\([0-9]*\\):.*/\\1/'", "r");
|
||||||
|
if (!fp) return 0;
|
||||||
|
|
||||||
|
int user_count = 0;
|
||||||
|
char line[64];
|
||||||
|
while (fgets(line, sizeof(line), fp) && user_count < max_users) {
|
||||||
|
int user_id = atoi(line);
|
||||||
|
if (user_id >= 0) {
|
||||||
|
snprintf(user_dirs[user_count], MAX_PATH_LEN, "%s/%d", USER_DATA_BASE_PATH, user_id);
|
||||||
|
if (access(user_dirs[user_count], F_OK) == 0) {
|
||||||
|
user_count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pclose(fp);
|
||||||
|
return user_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
int get_users_from_directory_scan(char user_dirs[][MAX_PATH_LEN], int max_users) {
|
||||||
|
DIR *dir = opendir(USER_DATA_BASE_PATH);
|
||||||
|
if (!dir) {
|
||||||
|
write_log("ERROR", 27, USER_DATA_BASE_PATH, strerror(errno));
|
||||||
|
snprintf(user_dirs[0], MAX_PATH_LEN, "%s/0", USER_DATA_BASE_PATH);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int user_count = 0;
|
||||||
|
struct dirent *entry;
|
||||||
|
while ((entry = readdir(dir)) != NULL && user_count < max_users) {
|
||||||
|
if (entry->d_type == DT_DIR) {
|
||||||
|
char *endptr;
|
||||||
|
long user_id = strtol(entry->d_name, &endptr, 10);
|
||||||
|
if (*endptr == '\0' && strlen(entry->d_name) > 0 && user_id >= 0) {
|
||||||
|
snprintf(user_dirs[user_count], MAX_PATH_LEN, "%s/%s", USER_DATA_BASE_PATH, entry->d_name);
|
||||||
|
user_count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
closedir(dir);
|
||||||
|
|
||||||
|
if (user_count == 0) {
|
||||||
|
snprintf(user_dirs[0], MAX_PATH_LEN, "%s/0", USER_DATA_BASE_PATH);
|
||||||
|
user_count = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return user_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
int get_user_directories(char user_dirs[][MAX_PATH_LEN], int max_users) {
|
||||||
|
if (!config.multi_user_scan) {
|
||||||
|
snprintf(user_dirs[0], MAX_PATH_LEN, "%s/0", USER_DATA_BASE_PATH);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int user_count = get_users_from_pm(user_dirs, max_users);
|
||||||
|
if (user_count > 0) return user_count;
|
||||||
|
|
||||||
|
return get_users_from_directory_scan(user_dirs, max_users);
|
||||||
|
}
|
||||||
|
|
||||||
|
void free_uid_list(void) {
|
||||||
|
struct uid_data *current = uid_list_head;
|
||||||
|
while (current) {
|
||||||
|
struct uid_data *next = current->next;
|
||||||
|
free(current);
|
||||||
|
current = next;
|
||||||
|
}
|
||||||
|
uid_list_head = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct uid_data* create_uid_entry(int uid, const char *package_name) {
|
||||||
|
struct uid_data *data = malloc(sizeof(struct uid_data));
|
||||||
|
if (!data) {
|
||||||
|
write_log("ERROR", 31);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
data->uid = uid;
|
||||||
|
strncpy(data->package, package_name, MAX_PACKAGE_NAME - 1);
|
||||||
|
data->package[MAX_PACKAGE_NAME - 1] = '\0';
|
||||||
|
data->next = uid_list_head;
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
int scan_single_directory(const char *dir_path) {
|
||||||
|
DIR *dir = opendir(dir_path);
|
||||||
|
if (!dir) {
|
||||||
|
write_log("ERROR", 27, dir_path, strerror(errno));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int count = 0;
|
||||||
|
struct dirent *entry;
|
||||||
|
while ((entry = readdir(dir)) != NULL) {
|
||||||
|
if (should_exit) break;
|
||||||
|
|
||||||
|
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) continue;
|
||||||
|
if (entry->d_type != DT_DIR) continue;
|
||||||
|
|
||||||
|
if (strlen(entry->d_name) >= MAX_PACKAGE_NAME) {
|
||||||
|
write_log("WARN", 29, entry->d_name);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
char path[MAX_PATH_LEN];
|
||||||
|
snprintf(path, sizeof(path), "%s/%s", dir_path, entry->d_name);
|
||||||
|
|
||||||
|
struct stat st;
|
||||||
|
if (stat(path, &st) != 0) {
|
||||||
|
write_log("ERROR", 30, path, strerror(errno));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct uid_data *data = create_uid_entry(st.st_uid, entry->d_name);
|
||||||
|
if (data) {
|
||||||
|
uid_list_head = data;
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
closedir(dir);
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
int perform_uid_scan(void) {
|
||||||
|
char user_dirs[MAX_USERS][MAX_PATH_LEN];
|
||||||
|
int total_count = 0;
|
||||||
|
|
||||||
|
free_uid_list();
|
||||||
|
|
||||||
|
int user_count = get_user_directories(user_dirs, MAX_USERS);
|
||||||
|
if (user_count <= 0) return -1;
|
||||||
|
|
||||||
|
write_log("INFO", 28);
|
||||||
|
write_log("INFO", 63, user_count);
|
||||||
|
|
||||||
|
for (int i = 0; i < user_count && !should_exit; i++) {
|
||||||
|
write_log("INFO", 62, user_dirs[i]);
|
||||||
|
total_count += scan_single_directory(user_dirs[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
write_log("INFO", 32, total_count);
|
||||||
|
return total_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
int write_uid_whitelist(void) {
|
||||||
|
ensure_directory_exists();
|
||||||
|
|
||||||
|
FILE *fp = fopen(KSU_UID_LIST_PATH, "w");
|
||||||
|
if (!fp) {
|
||||||
|
write_log("ERROR", 33, KSU_UID_LIST_PATH, strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int count = 0;
|
||||||
|
struct uid_data *current = uid_list_head;
|
||||||
|
while (current) {
|
||||||
|
fprintf(fp, "%d %s\n", current->uid, current->package);
|
||||||
|
current = current->next;
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(fp);
|
||||||
|
write_log("INFO", 34, count);
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
void notify_kernel_update(void) {
|
||||||
|
int fd = open(PROC_COMM_PATH, O_WRONLY);
|
||||||
|
if (fd < 0) {
|
||||||
|
write_log("ERROR", 35, PROC_COMM_PATH, strerror(errno));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (write(fd, "UPDATED", 7) != 7) {
|
||||||
|
write_log("ERROR", 36, PROC_COMM_PATH, strerror(errno));
|
||||||
|
} else {
|
||||||
|
write_log("INFO", 37);
|
||||||
|
}
|
||||||
|
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
int check_kernel_request(void) {
|
||||||
|
FILE *fp = fopen(PROC_COMM_PATH, "r");
|
||||||
|
if (!fp) return 0;
|
||||||
|
|
||||||
|
char status[16];
|
||||||
|
int result = 0;
|
||||||
|
if (fgets(status, sizeof(status), fp) != NULL) {
|
||||||
|
result = (strncmp(status, "RESCAN", 6) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(fp);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retry wrapper functions
|
||||||
|
int scan_operation(void) {
|
||||||
|
return perform_uid_scan() < 0 ? -1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int write_operation(void) {
|
||||||
|
return write_uid_whitelist() < 0 ? -1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void perform_scan_update(void) {
|
||||||
|
if (!config.auto_scan && !manual_scan_flag) {
|
||||||
|
write_log("WARN", 72); // Auto scan disabled, operation not allowed
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
write_log("INFO", 38);
|
||||||
|
|
||||||
|
if (retry_operation(scan_operation, "scan") != 0) {
|
||||||
|
write_log("ERROR", 39);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (retry_operation(write_operation, "write") != 0) {
|
||||||
|
write_log("ERROR", 40);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
notify_kernel_update();
|
||||||
|
write_log("INFO", 41);
|
||||||
|
}
|
||||||
|
|
||||||
|
void perform_manual_scan_update(void) {
|
||||||
|
manual_scan_flag = 1;
|
||||||
|
write_log("INFO", 73); // Manual scan requested, ignoring auto_scan setting
|
||||||
|
write_log("INFO", 38);
|
||||||
|
|
||||||
|
if (retry_operation(scan_operation, "scan") != 0) {
|
||||||
|
write_log("ERROR", 39);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (retry_operation(write_operation, "write") != 0) {
|
||||||
|
write_log("ERROR", 40);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
notify_kernel_update();
|
||||||
|
write_log("INFO", 41);
|
||||||
|
}
|
||||||
|
|
||||||
|
void print_usage(const char *prog) {
|
||||||
|
if (config.language == LANG_ZH) {
|
||||||
|
printf("用法: %s [选项]\n", prog);
|
||||||
|
printf("KSU UID 扫描器 - 管理UID白名单\n\n");
|
||||||
|
printf("选项:\n");
|
||||||
|
printf(" start 启动守护进程\n");
|
||||||
|
printf(" stop 停止守护进程\n");
|
||||||
|
printf(" restart 重启守护进程\n");
|
||||||
|
printf(" status 显示守护进程状态\n");
|
||||||
|
printf(" reload 重新加载守护进程配置\n");
|
||||||
|
printf(" -s, --scan 执行一次扫描并退出 (忽略auto_scan设置)\n");
|
||||||
|
printf(" -l, --list 列出当前UID白名单\n");
|
||||||
|
printf(" --lang <en|zh> 设置语言 (英文|中文)\n");
|
||||||
|
printf(" --multi-user <0|1> 设置多用户扫描 (0=禁用, 1=启用)\n");
|
||||||
|
printf(" --auto-scan <0|1> 设置自动扫描 (0=禁用, 1=启用)\n");
|
||||||
|
printf(" --config 显示当前配置\n");
|
||||||
|
printf(" -h, --help 显示此帮助信息\n");
|
||||||
|
} else {
|
||||||
|
printf("Usage: %s [options]\n", prog);
|
||||||
|
printf("KSU UID Scanner - Manage UID whitelist\n\n");
|
||||||
|
printf("Options:\n");
|
||||||
|
printf(" start Start daemon\n");
|
||||||
|
printf(" stop Stop daemon\n");
|
||||||
|
printf(" restart Restart daemon\n");
|
||||||
|
printf(" status Show daemon status\n");
|
||||||
|
printf(" reload Reload daemon config\n");
|
||||||
|
printf(" -s, --scan Perform one scan and exit (ignore auto_scan setting)\n");
|
||||||
|
printf(" -l, --list List current UID whitelist\n");
|
||||||
|
printf(" --lang <en|zh> Set language\n");
|
||||||
|
printf(" --multi-user <0|1> Set multi-user scanning\n");
|
||||||
|
printf(" --auto-scan <0|1> Set auto scanning\n");
|
||||||
|
printf(" --config Show current config\n");
|
||||||
|
printf(" -h, --help Show this help\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void list_whitelist(void) {
|
||||||
|
FILE *fp = fopen(KSU_UID_LIST_PATH, "r");
|
||||||
|
if (!fp) {
|
||||||
|
printf(get_message(42), strerror(errno));
|
||||||
|
printf("\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("%s\n", get_message(43));
|
||||||
|
printf("%-8s %-40s\n", "UID", (config.language == LANG_ZH) ? "包名" : "Package");
|
||||||
|
printf("%-8s %-40s\n", "--------", "----------------------------------------");
|
||||||
|
|
||||||
|
char line[512];
|
||||||
|
while (fgets(line, sizeof(line), fp)) {
|
||||||
|
int uid;
|
||||||
|
char package[256];
|
||||||
|
if (sscanf(line, "%d %255s", &uid, package) == 2) {
|
||||||
|
printf("%-8d %-40s\n", uid, package);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fclose(fp);
|
||||||
|
}
|
||||||
|
|
||||||
|
void show_config(void) {
|
||||||
|
if (config.language == LANG_ZH) {
|
||||||
|
printf("当前配置:\n");
|
||||||
|
printf(" 语言: %s\n", (config.language == LANG_ZH) ? "中文" : "英文");
|
||||||
|
printf(" 多用户扫描: %s\n", config.multi_user_scan ? "启用" : "禁用");
|
||||||
|
printf(" 自动扫描: %s\n", config.auto_scan ? "启用" : "禁用");
|
||||||
|
printf(" 扫描间隔: %d 秒\n", config.scan_interval);
|
||||||
|
printf(" 日志级别: %d\n", config.log_level);
|
||||||
|
} else {
|
||||||
|
printf("Current Configuration:\n");
|
||||||
|
printf(" Language: %s\n", (config.language == LANG_ZH) ? "Chinese" : "English");
|
||||||
|
printf(" Multi-user scan: %s\n", config.multi_user_scan ? "Enabled" : "Disabled");
|
||||||
|
printf(" Auto scan: %s\n", config.auto_scan ? "Enabled" : "Disabled");
|
||||||
|
printf(" Scan interval: %d seconds\n", config.scan_interval);
|
||||||
|
printf(" Log level: %d\n", config.log_level);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int handle_config_command(int argc, char *argv[]) {
|
||||||
|
if (strcmp(argv[1], "--lang") == 0) {
|
||||||
|
if (argc < 3) return 1;
|
||||||
|
if (strcmp(argv[2], "zh") == 0) {
|
||||||
|
set_language(LANG_ZH);
|
||||||
|
} else if (strcmp(argv[2], "en") == 0) {
|
||||||
|
set_language(LANG_EN);
|
||||||
|
} else {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
} else if (strcmp(argv[1], "--multi-user") == 0) {
|
||||||
|
if (argc < 3) return 1;
|
||||||
|
int value = atoi(argv[2]);
|
||||||
|
if (value != 0 && value != 1) return 1;
|
||||||
|
set_multi_user_scan(value);
|
||||||
|
return 0;
|
||||||
|
} else if (strcmp(argv[1], "--auto-scan") == 0) {
|
||||||
|
if (argc < 3) return 1;
|
||||||
|
int value = atoi(argv[2]);
|
||||||
|
if (value != 0 && value != 1) return 1;
|
||||||
|
set_auto_scan(value);
|
||||||
|
return 0;
|
||||||
|
} else if (strcmp(argv[1], "--config") == 0) {
|
||||||
|
show_config();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int handle_single_command(int argc, char *argv[]) {
|
||||||
|
(void)argc;
|
||||||
|
if (strcmp(argv[1], "-s") == 0 || strcmp(argv[1], "--scan") == 0) {
|
||||||
|
printf("%s\n", get_message(44));
|
||||||
|
manual_scan_flag = 1;
|
||||||
|
perform_manual_scan_update();
|
||||||
|
return 0;
|
||||||
|
} else if (strcmp(argv[1], "-l") == 0 || strcmp(argv[1], "--list") == 0) {
|
||||||
|
list_whitelist();
|
||||||
|
return 0;
|
||||||
|
} else if (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0) {
|
||||||
|
print_usage(argv[0]);
|
||||||
|
return 0;
|
||||||
|
} else if (strcmp(argv[1], "status") == 0) {
|
||||||
|
show_status();
|
||||||
|
return 0;
|
||||||
|
} else if (strcmp(argv[1], "stop") == 0) {
|
||||||
|
return stop_daemon();
|
||||||
|
} else if (strcmp(argv[1], "reload") == 0) {
|
||||||
|
reload_daemon();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setup_signal_handlers(void) {
|
||||||
|
signal(SIGTERM, signal_handler);
|
||||||
|
signal(SIGINT, signal_handler);
|
||||||
|
signal(SIGHUP, signal_handler);
|
||||||
|
signal(SIGUSR1, signal_handler);
|
||||||
|
signal(SIGPIPE, SIG_IGN);
|
||||||
|
}
|
||||||
|
|
||||||
|
void init_daemon_logging(void) {
|
||||||
|
ensure_directory_exists();
|
||||||
|
log_fd = open(LOG_FILE_PATH, O_WRONLY | O_CREAT | O_APPEND, 0644);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cleanup_daemon_resources(void) {
|
||||||
|
write_log("INFO", 52);
|
||||||
|
free_uid_list();
|
||||||
|
unlink(PID_FILE_PATH);
|
||||||
|
if (log_fd != -1) close(log_fd);
|
||||||
|
write_log("INFO", 53);
|
||||||
|
}
|
||||||
|
|
||||||
|
void run_daemon_loop(void) {
|
||||||
|
load_config();
|
||||||
|
|
||||||
|
write_log("INFO", 49);
|
||||||
|
|
||||||
|
if (!config.auto_scan) {
|
||||||
|
write_log("INFO", 66);
|
||||||
|
} else {
|
||||||
|
perform_scan_update();
|
||||||
|
}
|
||||||
|
|
||||||
|
while (!should_exit) {
|
||||||
|
if (should_reload) {
|
||||||
|
load_config();
|
||||||
|
|
||||||
|
if (!config.auto_scan) {
|
||||||
|
write_log("INFO", 67);
|
||||||
|
} else {
|
||||||
|
write_log("INFO", 50);
|
||||||
|
perform_scan_update();
|
||||||
|
}
|
||||||
|
should_reload = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (check_kernel_request()) {
|
||||||
|
if (!config.auto_scan) {
|
||||||
|
write_log("INFO", 68);
|
||||||
|
} else {
|
||||||
|
write_log("INFO", 51);
|
||||||
|
perform_scan_update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
manage_log_file();
|
||||||
|
|
||||||
|
int sleep_iterations = config.scan_interval * 10;
|
||||||
|
for (int i = 0; i < sleep_iterations && !should_exit && !should_reload; i++) {
|
||||||
|
usleep(100000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
load_config();
|
||||||
|
|
||||||
|
if (argc < 2) {
|
||||||
|
print_usage(argv[0]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int result = handle_config_command(argc, argv);
|
||||||
|
if (result >= 0) return result;
|
||||||
|
|
||||||
|
result = handle_single_command(argc, argv);
|
||||||
|
if (result >= 0) return result;
|
||||||
|
|
||||||
|
if (strcmp(argv[1], "restart") == 0) {
|
||||||
|
if (restart_daemon() != 0) return 1;
|
||||||
|
} else if (strcmp(argv[1], "start") != 0) {
|
||||||
|
printf(get_message(45), argv[1]);
|
||||||
|
printf("\n");
|
||||||
|
print_usage(argv[0]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_daemon_running()) {
|
||||||
|
printf("%s\n", get_message(46));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("%s\n", get_message(47));
|
||||||
|
if (daemonize() != 0) {
|
||||||
|
printf("%s\n", get_message(48));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
init_daemon_logging();
|
||||||
|
if (write_pid_file() != 0) exit(1);
|
||||||
|
setup_signal_handlers();
|
||||||
|
run_daemon_loop();
|
||||||
|
cleanup_daemon_resources();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user