Skip to content

Commit

Permalink
Get rid of binder-rs & Refine watchdog
Browse files Browse the repository at this point in the history
  • Loading branch information
5ec1cff committed Nov 3, 2023
1 parent 47e515e commit f958e57
Show file tree
Hide file tree
Showing 5 changed files with 131 additions and 49 deletions.
7 changes: 7 additions & 0 deletions module/src/service.sh
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,13 @@ fi

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
for file in ../*; do
if [ -d "$file" ] && [ -d "$file/zygisk" ] && ! [ -f "$file/disable" ]; then
Expand Down
1 change: 0 additions & 1 deletion zygiskd/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ proc-maps = "0.3"
rustix = { version = "0.38", features = [ "fs", "process", "mount", "net", "thread"] }
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 }
ptrace-do = { git = "https://github.com/5ec1cff/ptrace-do" }

Expand Down
3 changes: 3 additions & 0 deletions zygiskd/src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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_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)]
#[repr(u8)]
pub enum DaemonSocketAction {
Expand Down
27 changes: 26 additions & 1 deletion zygiskd/src/utils.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use anyhow::Result;
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::unix::net::UnixListener;
use std::process::Command;
Expand Down Expand Up @@ -96,6 +96,28 @@ pub fn set_property(name: &str, value: &str) -> Result<()> {
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<()> {
let cwd = std::env::current_dir()?;
let mnt = fs::File::open(format!("/proc/{}/ns/mnt", pid))?;
Expand Down Expand Up @@ -177,4 +199,7 @@ extern "C" {
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_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;
}
142 changes: 95 additions & 47 deletions zygiskd/src/watchdog.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
use crate::{constants, root_impl, utils};
use anyhow::{bail, Result};
use std::fs;
use std::future::Future;
use std::io::{BufRead, BufReader, Write};
use std::pin::Pin;
use std::time::Duration;
use binder::IBinder;
use futures::stream::FuturesUnordered;
use futures::StreamExt;
use futures::{FutureExt, join, pin_mut};
use futures::future::{Fuse, FusedFuture};
use log::{debug, error, info};
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 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();

Expand Down Expand Up @@ -108,62 +107,111 @@ fn check_and_set_hint() -> Result<bool> {
}

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 {
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 daemon64 = Command::new(constants::PATH_CP_BIN64).arg("daemon").spawn();
async fn spawn_daemon(mut daemon: Child) -> Result<()> {
let result = daemon.wait().await?;
log::error!("Daemon process {} died: {}", daemon.id().unwrap(), result);
Ok(())
}
if let Ok(it) = daemon32 {
child_ids.push(it.id().unwrap());
futures.push(Box::pin(spawn_daemon(it)));
}
if let Ok(it) = daemon64 {
child_ids.push(it.id().unwrap());
futures.push(Box::pin(spawn_daemon(it)));
async fn spawn_daemon(mut daemon: Child, mut killer: tokio::sync::watch::Receiver<()>) {
let id = daemon.id().unwrap();
select! {
result = daemon.wait() => {
log::error!("Daemon process {} died: {}", id, result
.expect("failed to get daemon process exit reason")
);
},
_ = killer.changed() => {
log::warn!("Kill daemon process {}", id);
daemon.kill().await.expect("failed to kill");
}
}
}
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()
}
};

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;
}
};
};
pin_mut!(wait32, wait64);

info!("System server ready");
let mut restart_zygote = true;

loop {
if binder.ping_binder().is_err() { break; }
tokio::time::sleep(Duration::from_secs(1)).await;
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;
}
bail!("System server died");
}
futures.push(Box::pin(binder_listener()));

if let Err(e) = futures.next().await.unwrap() {
error!("{}", e);
}
// kill all remain daemons
tx.send(())?;

for child in child_ids {
debug!("Killing child process {}", child);
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 {
bail!("Too many crashes, abort");
}
last_restart_time = current;

error!("Restarting zygote...");
utils::set_property(constants::PROP_CTL_RESTART, "zygote")?;
error!("Daemons are going to restart ...");

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")?;
}
}
}

0 comments on commit f958e57

Please sign in to comment.