Get rid of binder-rs & Refine watchdog

This commit is contained in:
5ec1cff
2023-11-03 18:12:50 +08:00
parent 47e515e2fc
commit f958e57af6
5 changed files with 134 additions and 52 deletions

View File

@@ -9,6 +9,13 @@ fi
cd "$MODDIR" cd "$MODDIR"
# temporary fix AVD 11 magisk
if [ -f /dev/zygisk_service ];then
log -p i -t "zygisk-sh" "service called twice";
exit;
fi
touch /dev/zygisk_service
if [ "$(which magisk)" ]; then if [ "$(which magisk)" ]; then
for file in ../*; do for file in ../*; do
if [ -d "$file" ] && [ -d "$file/zygisk" ] && ! [ -f "$file/disable" ]; then if [ -d "$file" ] && [ -d "$file/zygisk" ] && ! [ -f "$file/disable" ]; then

View File

@@ -23,7 +23,6 @@ proc-maps = "0.3"
rustix = { version = "0.38", features = [ "fs", "process", "mount", "net", "thread"] } rustix = { version = "0.38", features = [ "fs", "process", "mount", "net", "thread"] }
tokio = { version = "1.28", features = ["full"] } tokio = { version = "1.28", features = ["full"] }
binder = { git = "https://github.com/Kernel-SU/binder_rs", rev = "c9f2b62d6a744fd2264056c638c1b061a6a2932d" }
fuser = { git = "https://github.com/Dr-TSNG/fuser", default-features = false } fuser = { git = "https://github.com/Dr-TSNG/fuser", default-features = false }
ptrace-do = { git = "https://github.com/5ec1cff/ptrace-do" } ptrace-do = { git = "https://github.com/5ec1cff/ptrace-do" }

View File

@@ -40,6 +40,9 @@ pub const STATUS_ROOT_IMPL_TOO_OLD: &str = "❌ Root implementation version too
pub const STATUS_ROOT_IMPL_ABNORMAL: &str = "❌ Abnormal root implementation version"; pub const STATUS_ROOT_IMPL_ABNORMAL: &str = "❌ Abnormal root implementation version";
pub const STATUS_ROOT_IMPL_MULTIPLE: &str = "❌ Multiple root implementations installed"; pub const STATUS_ROOT_IMPL_MULTIPLE: &str = "❌ Multiple root implementations installed";
pub const MAX_RESTART_COUNT: i32 = 5;
pub const ZYGOTE_SERVICE_PROP: &str = "init.svc.zygote";
#[derive(Debug, Eq, PartialEq, TryFromPrimitive)] #[derive(Debug, Eq, PartialEq, TryFromPrimitive)]
#[repr(u8)] #[repr(u8)]
pub enum DaemonSocketAction { pub enum DaemonSocketAction {

View File

@@ -1,6 +1,6 @@
use anyhow::Result; use anyhow::Result;
use std::{fs, io::{Read, Write}, os::unix::net::UnixStream}; use std::{fs, io::{Read, Write}, os::unix::net::UnixStream};
use std::ffi::{c_char, CStr, CString}; use std::ffi::{c_char, c_void, CStr, CString};
use std::os::fd::AsFd; use std::os::fd::AsFd;
use std::os::unix::net::UnixListener; use std::os::unix::net::UnixListener;
use std::process::Command; use std::process::Command;
@@ -96,6 +96,28 @@ pub fn set_property(name: &str, value: &str) -> Result<()> {
Ok(()) Ok(())
} }
pub fn wait_property(name: &str, serial: u32) -> Result<u32> {
let name = CString::new(name)?;
let info = unsafe {
__system_property_find(name.as_ptr())
};
let mut serial = serial;
unsafe {
__system_property_wait(info, serial, &mut serial, std::ptr::null());
}
Ok(serial)
}
pub fn get_property_serial(name: &str) -> Result<u32> {
let name = CString::new(name)?;
let info = unsafe {
__system_property_find(name.as_ptr())
};
Ok(unsafe {
__system_property_serial(info)
})
}
pub fn switch_mount_namespace(pid: i32) -> Result<()> { pub fn switch_mount_namespace(pid: i32) -> Result<()> {
let cwd = std::env::current_dir()?; let cwd = std::env::current_dir()?;
let mnt = fs::File::open(format!("/proc/{}/ns/mnt", pid))?; let mnt = fs::File::open(format!("/proc/{}/ns/mnt", pid))?;
@@ -177,4 +199,7 @@ extern "C" {
fn __android_log_print(prio: i32, tag: *const c_char, fmt: *const c_char, ...) -> i32; fn __android_log_print(prio: i32, tag: *const c_char, fmt: *const c_char, ...) -> i32;
fn __system_property_get(name: *const c_char, value: *mut c_char) -> u32; fn __system_property_get(name: *const c_char, value: *mut c_char) -> u32;
fn __system_property_set(name: *const c_char, value: *const c_char) -> u32; fn __system_property_set(name: *const c_char, value: *const c_char) -> u32;
fn __system_property_find(name: *const c_char) -> *const c_void;
fn __system_property_wait(info: *const c_void, old_serial: u32, new_serial: *mut u32, timeout: *const libc::timespec) -> bool;
fn __system_property_serial(info: *const c_void) -> u32;
} }

View File

@@ -1,18 +1,17 @@
use crate::{constants, root_impl, utils}; use crate::{constants, root_impl, utils};
use anyhow::{bail, Result}; use anyhow::{bail, Result};
use std::fs; use std::fs;
use std::future::Future;
use std::io::{BufRead, BufReader, Write}; use std::io::{BufRead, BufReader, Write};
use std::pin::Pin;
use std::time::Duration; use std::time::Duration;
use binder::IBinder; use futures::{FutureExt, join, pin_mut};
use futures::stream::FuturesUnordered; use futures::future::{Fuse, FusedFuture};
use futures::StreamExt;
use log::{debug, error, info}; use log::{debug, error, info};
use rustix::mount::mount_bind; use rustix::mount::mount_bind;
use rustix::process::{getgid, getuid, kill_process, Pid, Signal}; use rustix::process::{getgid, getuid};
use tokio::process::{Child, Command}; use tokio::process::{Child, Command};
use crate::utils::LateInit; use tokio::{select, task};
use tokio::time::Instant;
use crate::utils::{get_property, get_property_serial, LateInit, wait_property};
static PROP_SECTIONS: LateInit<[String; 2]> = LateInit::new(); static PROP_SECTIONS: LateInit<[String; 2]> = LateInit::new();
@@ -108,62 +107,111 @@ fn check_and_set_hint() -> Result<bool> {
} }
async fn spawn_daemon() -> Result<()> { async fn spawn_daemon() -> Result<()> {
let mut lives = 5; let mut lives = constants::MAX_RESTART_COUNT;
let mut last_restart_time = Instant::now();
let (sender, mut receiver) = tokio::sync::mpsc::channel(1);
task::spawn_blocking(move || {
let mut serial = 0u32;
let mut last_state = "running".to_string();
info!("zygote property monitor started");
loop {
let old_serial = serial;
serial = wait_property(constants::ZYGOTE_SERVICE_PROP, serial).expect("failed to wait on property");
let new_state = get_property(constants::ZYGOTE_SERVICE_PROP).expect("failed to get property");
if last_state == "running" && new_state != "running" {
info!("new zygote state: {} serial {} -> {}", new_state, old_serial, serial);
sender.blocking_send(old_serial).expect("failed to notify");
}
last_state = new_state
}
});
let mut restart_serial = 0u32;
loop { loop {
let mut futures = FuturesUnordered::<Pin<Box<dyn Future<Output=Result<()>>>>>::new();
let mut child_ids = vec![];
let daemon32 = Command::new(constants::PATH_CP_BIN32).arg("daemon").spawn(); let daemon32 = Command::new(constants::PATH_CP_BIN32).arg("daemon").spawn();
let daemon64 = Command::new(constants::PATH_CP_BIN64).arg("daemon").spawn(); let daemon64 = Command::new(constants::PATH_CP_BIN64).arg("daemon").spawn();
async fn spawn_daemon(mut daemon: Child) -> Result<()> { async fn spawn_daemon(mut daemon: Child, mut killer: tokio::sync::watch::Receiver<()>) {
let result = daemon.wait().await?; let id = daemon.id().unwrap();
log::error!("Daemon process {} died: {}", daemon.id().unwrap(), result); select! {
Ok(()) result = daemon.wait() => {
} log::error!("Daemon process {} died: {}", id, result
if let Ok(it) = daemon32 { .expect("failed to get daemon process exit reason")
child_ids.push(it.id().unwrap()); );
futures.push(Box::pin(spawn_daemon(it))); },
} _ = killer.changed() => {
if let Ok(it) = daemon64 { log::warn!("Kill daemon process {}", id);
child_ids.push(it.id().unwrap()); daemon.kill().await.expect("failed to kill");
futures.push(Box::pin(spawn_daemon(it))); }
}
async fn binder_listener() -> Result<()> {
let mut binder = loop {
match binder::get_service("activity") {
Some(binder) => break binder,
None => {
log::trace!("System server not ready, wait for 1s...");
tokio::time::sleep(Duration::from_secs(1)).await;
}
};
};
info!("System server ready");
loop {
if binder.ping_binder().is_err() { break; }
tokio::time::sleep(Duration::from_secs(1)).await;
} }
bail!("System server died");
} }
futures.push(Box::pin(binder_listener())); let (tx, rx) = tokio::sync::watch::channel(());
let wait32 = match daemon32 {
Ok(child) => {
spawn_daemon(child, rx.clone()).fuse()
}
Err(..) => {
Fuse::terminated()
}
};
let wait64 = match daemon64 {
Ok(child) => {
spawn_daemon(child, rx.clone()).fuse()
}
Err(..) => {
Fuse::terminated()
}
};
if let Err(e) = futures.next().await.unwrap() { pin_mut!(wait32, wait64);
error!("{}", e);
let mut restart_zygote = true;
select! {
_ = &mut wait32 => {},
_ = &mut wait64 => {},
_ = async {
// we expect a serial different from last restart
loop {
if restart_serial != receiver.recv().await.expect("no serial received") {
break;
}
}
} => {
restart_zygote = false;
}
} }
for child in child_ids { // kill all remain daemons
debug!("Killing child process {}", child); tx.send(())?;
let _ = kill_process(Pid::from_raw(child as i32).unwrap(), Signal::Kill);
// wait for all daemons
loop {
futures::select! {
_ = wait32 => {},
_ = wait64 => {},
complete => { break; }
}
} }
lives -= 1; let current = Instant::now();
if current - last_restart_time >= Duration::new(30, 0) {
lives = constants::MAX_RESTART_COUNT;
log::warn!("reset live count to {}", lives);
} else {
lives -= 1;
log::warn!("remain live count {}", lives);
}
if lives == 0 { if lives == 0 {
bail!("Too many crashes, abort"); bail!("Too many crashes, abort");
} }
last_restart_time = current;
error!("Restarting zygote..."); error!("Daemons are going to restart ...");
utils::set_property(constants::PROP_CTL_RESTART, "zygote")?;
if restart_zygote {
error!("Restarting zygote...");
restart_serial = get_property_serial(constants::ZYGOTE_SERVICE_PROP)?;
debug!("serial before restart {}", restart_serial);
utils::set_property(constants::PROP_CTL_RESTART, "zygote")?;
}
} }
} }