diff --git a/native/src/base/misc.hpp b/native/src/base/misc.hpp index 038dd1bd5..1c084afa8 100644 --- a/native/src/base/misc.hpp +++ b/native/src/base/misc.hpp @@ -325,3 +325,9 @@ void exec_command_async(Args &&...args) { }; exec_command(exec); } + +template +constexpr auto operator+(T e) noexcept -> + std::enable_if_t::value, std::underlying_type_t> { + return static_cast>(e); +} diff --git a/native/src/core/bootstages.cpp b/native/src/core/bootstages.cpp index aca62aace..e876de0cf 100644 --- a/native/src/core/bootstages.cpp +++ b/native/src/core/bootstages.cpp @@ -14,17 +14,6 @@ using namespace std; -// Boot stage state -enum : int { - FLAG_NONE = 0, - FLAG_POST_FS_DATA_DONE = (1 << 0), - FLAG_LATE_START_DONE = (1 << 1), - FLAG_BOOT_COMPLETE = (1 << 2), - FLAG_SAFE_MODE = (1 << 3), -}; - -static int boot_state = FLAG_NONE; - bool zygisk_enabled = false; /********* @@ -243,31 +232,6 @@ static bool magisk_env() { return true; } -static bool check_data() { - bool mnt = false; - file_readline("/proc/mounts", [&](string_view s) { - if (str_contains(s, " /data ") && !str_contains(s, "tmpfs")) { - mnt = true; - return false; - } - return true; - }); - if (!mnt) - return false; - auto crypto = get_prop("ro.crypto.state"); - if (!crypto.empty()) { - if (crypto != "encrypted") { - // Unencrypted, we can directly access data - return true; - } else { - // Encrypted, check whether vold is started - return !get_prop("init.svc.vold").empty(); - } - } - // ro.crypto.state is not set, assume it's unencrypted - return true; -} - void unlock_blocks() { int fd, dev, OFF = 0; @@ -341,10 +305,7 @@ static bool check_key_combo() { extern int disable_deny(); void MagiskD::post_fs_data() const { - if (!check_data()) - return; - - setup_logfile(); + as_rust().setup_logfile(); LOGI("** post-fs-data mode running\n"); @@ -364,7 +325,7 @@ void MagiskD::post_fs_data() const { if (get_prop("persist.sys.safemode", true) == "1" || get_prop("ro.sys.safemode") == "1" || check_key_combo()) { - boot_state |= FLAG_SAFE_MODE; + as_rust().enable_safe_mode(); // Disable all modules and denylist so next boot will be clean disable_modules(); disable_deny(); @@ -380,23 +341,19 @@ void MagiskD::post_fs_data() const { early_abort: // We still do magic mount because root itself might need it load_modules(); - boot_state |= FLAG_POST_FS_DATA_DONE; } void MagiskD::late_start() const { - setup_logfile(); + as_rust().setup_logfile(); LOGI("** late_start service mode running\n"); exec_common_scripts("service"); exec_module_scripts("service"); - - boot_state |= FLAG_LATE_START_DONE; } void MagiskD::boot_complete() const { - boot_state |= FLAG_BOOT_COMPLETE; - setup_logfile(); + as_rust().setup_logfile(); LOGI("** boot-complete triggered\n"); @@ -410,30 +367,3 @@ void MagiskD::boot_complete() const { reset_zygisk(true); } - -void boot_stage_handler(int client, int code) { - // Make sure boot stage execution is always serialized - static pthread_mutex_t stage_lock = PTHREAD_MUTEX_INITIALIZER; - mutex_guard lock(stage_lock); - MagiskD daemon; - - switch (code) { - case MainRequest::POST_FS_DATA: - if ((boot_state & FLAG_POST_FS_DATA_DONE) == 0) - daemon.post_fs_data(); - close(client); - break; - case MainRequest::LATE_START: - close(client); - if ((boot_state & FLAG_POST_FS_DATA_DONE) && (boot_state & FLAG_SAFE_MODE) == 0) - daemon.late_start(); - break; - case MainRequest::BOOT_COMPLETE: - close(client); - if ((boot_state & FLAG_SAFE_MODE) == 0) - daemon.boot_complete(); - break; - default: - __builtin_unreachable(); - } -} diff --git a/native/src/core/daemon.cpp b/native/src/core/daemon.cpp index a37413c4a..432f43c9e 100644 --- a/native/src/core/daemon.cpp +++ b/native/src/core/daemon.cpp @@ -128,8 +128,20 @@ static void poll_ctrl_handler(pollfd *pfd) { } } +const MagiskD &MagiskD::get() { + return *reinterpret_cast(&rust::get_magiskd()); +} + +const rust::MagiskD &MagiskD::as_rust() const { + return *reinterpret_cast(this); +} + +void MagiskD::boot_stage_handler(int client, int code) const { + as_rust().boot_stage_handler(client, code); +} + void MagiskD::reboot() const { - if (is_recovery()) + if (as_rust().is_recovery()) exec_command_sync("/system/bin/reboot", "recovery"); else exec_command_sync("/system/bin/reboot"); @@ -137,34 +149,33 @@ void MagiskD::reboot() const { static void handle_request_async(int client, int code, const sock_cred &cred) { switch (code) { - case MainRequest::DENYLIST: + case +RequestCode::DENYLIST: denylist_handler(client, &cred); break; - case MainRequest::SUPERUSER: + case +RequestCode::SUPERUSER: su_daemon_handler(client, &cred); break; - case MainRequest::ZYGOTE_RESTART: + case +RequestCode::ZYGOTE_RESTART: LOGI("** zygote restarted\n"); pkg_xml_ino = 0; prune_su_access(); reset_zygisk(false); close(client); break; - case MainRequest::SQLITE_CMD: + case +RequestCode::SQLITE_CMD: exec_sql(client); break; - case MainRequest::REMOVE_MODULES: { + case +RequestCode::REMOVE_MODULES: { int do_reboot = read_int(client); remove_modules(); write_int(client, 0); close(client); if (do_reboot) { - MagiskD daemon; - daemon.reboot(); + MagiskD::get().reboot(); } break; } - case MainRequest::ZYGISK: + case +RequestCode::ZYGISK: zygisk_handler(client, &cred); break; default: @@ -174,20 +185,20 @@ static void handle_request_async(int client, int code, const sock_cred &cred) { static void handle_request_sync(int client, int code) { switch (code) { - case MainRequest::CHECK_VERSION: + case +RequestCode::CHECK_VERSION: #if MAGISK_DEBUG write_string(client, MAGISK_VERSION ":MAGISK:D"); #else write_string(client, MAGISK_VERSION ":MAGISK:R"); #endif break; - case MainRequest::CHECK_VERSION_CODE: + case +RequestCode::CHECK_VERSION_CODE: write_int(client, MAGISK_VER_CODE); break; - case MainRequest::START_DAEMON: + case +RequestCode::START_DAEMON: rust::get_magiskd().setup_logfile(); break; - case MainRequest::STOP_DAEMON: { + case +RequestCode::STOP_DAEMON: { // Unmount all overlays denylist_handler(-1, nullptr); @@ -236,42 +247,42 @@ static void handle_request(pollfd *pfd) { if (!is_root && !is_zygote && !is_client(cred.pid)) { // Unsupported client state - write_int(client, MainResponse::ACCESS_DENIED); + write_int(client, +RespondCode::ACCESS_DENIED); return; } code = read_int(client); - if (code < 0 || code >= MainRequest::END || - code == MainRequest::_SYNC_BARRIER_ || - code == MainRequest::_STAGE_BARRIER_) { + if (code < 0 || code >= +RequestCode::END || + code == +RequestCode::_SYNC_BARRIER_ || + code == +RequestCode::_STAGE_BARRIER_) { // Unknown request code return; } // Check client permissions switch (code) { - case MainRequest::POST_FS_DATA: - case MainRequest::LATE_START: - case MainRequest::BOOT_COMPLETE: - case MainRequest::ZYGOTE_RESTART: - case MainRequest::SQLITE_CMD: - case MainRequest::DENYLIST: - case MainRequest::STOP_DAEMON: + case +RequestCode::POST_FS_DATA: + case +RequestCode::LATE_START: + case +RequestCode::BOOT_COMPLETE: + case +RequestCode::ZYGOTE_RESTART: + case +RequestCode::SQLITE_CMD: + case +RequestCode::DENYLIST: + case +RequestCode::STOP_DAEMON: if (!is_root) { - write_int(client, MainResponse::ROOT_REQUIRED); + write_int(client, +RespondCode::ROOT_REQUIRED); return; } break; - case MainRequest::REMOVE_MODULES: + case +RequestCode::REMOVE_MODULES: if (!is_root && cred.uid != AID_SHELL) { - write_int(client, MainResponse::ACCESS_DENIED); + write_int(client, +RespondCode::ACCESS_DENIED); return; } break; - case MainRequest::ZYGISK: + case +RequestCode::ZYGISK: if (!is_zygote) { // Invalid client context - write_int(client, MainResponse::ACCESS_DENIED); + write_int(client, +RespondCode::ACCESS_DENIED); return; } break; @@ -279,14 +290,16 @@ static void handle_request(pollfd *pfd) { break; } - write_int(client, MainResponse::OK); + write_int(client, +RespondCode::OK); - if (code < MainRequest::_SYNC_BARRIER_) { + if (code < +RequestCode::_SYNC_BARRIER_) { handle_request_sync(client, code); - } else if (code < MainRequest::_STAGE_BARRIER_) { + } else if (code < +RequestCode::_STAGE_BARRIER_) { exec_task([=, fd = client.release()] { handle_request_async(fd, code, cred); }); } else { - exec_task([=, fd = client.release()] { boot_stage_handler(fd, code); }); + exec_task([=, fd = client.release()] { + MagiskD::get().boot_stage_handler(fd, code); + }); } } @@ -460,20 +473,20 @@ int connect_daemon(int req, bool create) { } write_int(fd, req); int res = read_int(fd); - if (res < MainResponse::ERROR || res >= MainResponse::END) - res = MainResponse::ERROR; + if (res < +RespondCode::ERROR || res >= +RespondCode::END) + res = +RespondCode::ERROR; switch (res) { - case MainResponse::OK: + case +RespondCode::OK: break; - case MainResponse::ERROR: + case +RespondCode::ERROR: LOGE("Daemon error\n"); close(fd); return -1; - case MainResponse::ROOT_REQUIRED: + case +RespondCode::ROOT_REQUIRED: LOGE("Root is required for this operation\n"); close(fd); return -1; - case MainResponse::ACCESS_DENIED: + case +RespondCode::ACCESS_DENIED: LOGE("Access denied\n"); close(fd); return -1; diff --git a/native/src/core/daemon.rs b/native/src/core/daemon.rs index 412d85f05..1986203aa 100644 --- a/native/src/core/daemon.rs +++ b/native/src/core/daemon.rs @@ -1,23 +1,51 @@ use std::fs::File; -use std::io; use std::io::BufReader; +use std::sync::atomic::{AtomicU32, Ordering}; use std::sync::{Mutex, OnceLock}; +use std::{io, mem}; use base::libc::{O_CLOEXEC, O_RDONLY}; use base::{ - cstr, BufReadExt, Directory, FsPathBuf, ResultExt, Utf8CStr, Utf8CStrBuf, Utf8CStrBufArr, - Utf8CStrBufRef, WalkResult, + cstr, libc, open_fd, BufReadExt, Directory, FsPathBuf, ResultExt, Utf8CStr, Utf8CStrBuf, + Utf8CStrBufArr, Utf8CStrBufRef, WalkResult, }; +use crate::ffi::{CxxMagiskD, RequestCode}; use crate::logging::magisk_logging; use crate::{get_prop, MAIN_CONFIG}; // Global magiskd singleton pub static MAGISKD: OnceLock = OnceLock::new(); +#[repr(u32)] +enum BootState { + PostFsDataDone = (1 << 0), + LateStartDone = (1 << 1), + BootComplete = (1 << 2), + SafeMode = (1 << 3), +} + +#[derive(Default)] +#[repr(transparent)] +struct BootStateFlags(AtomicU32); + +impl BootStateFlags { + fn contains(&self, stage: BootState) -> bool { + let v = self.0.load(Ordering::Relaxed); + (v & stage as u32) != 0 + } + + fn set(&self, stage: BootState) { + let v = self.0.load(Ordering::Relaxed); + self.0.store(v | stage as u32, Ordering::Relaxed); + } +} + #[derive(Default)] pub struct MagiskD { pub logd: Mutex>, + boot_stage_lock: Mutex<()>, + boot_state_flags: BootStateFlags, is_emulator: bool, is_recovery: bool, } @@ -30,6 +58,50 @@ impl MagiskD { pub fn is_recovery(&self) -> bool { self.is_recovery } + + pub fn enable_safe_mode(&self) { + self.boot_state_flags.set(BootState::SafeMode) + } + + pub fn boot_stage_handler(&self, client: i32, code: i32) { + // Make sure boot stage execution is always serialized + let _guard = self.boot_stage_lock.lock().unwrap(); + + let code = RequestCode { repr: code }; + match code { + RequestCode::POST_FS_DATA => { + if check_data() && !self.boot_state_flags.contains(BootState::PostFsDataDone) { + self.as_cxx().post_fs_data(); + self.boot_state_flags.set(BootState::PostFsDataDone); + } + unsafe { libc::close(client) }; + } + RequestCode::LATE_START => { + unsafe { libc::close(client) }; + if self.boot_state_flags.contains(BootState::PostFsDataDone) + && !self.boot_state_flags.contains(BootState::SafeMode) + { + self.as_cxx().late_start(); + self.boot_state_flags.set(BootState::LateStartDone); + } + } + RequestCode::BOOT_COMPLETE => { + unsafe { libc::close(client) }; + if !self.boot_state_flags.contains(BootState::SafeMode) { + self.boot_state_flags.set(BootState::BootComplete); + self.as_cxx().boot_complete() + } + } + _ => { + unsafe { libc::close(client) }; + } + } + } + + #[inline(always)] + fn as_cxx(&self) -> &CxxMagiskD { + unsafe { mem::transmute(self) } + } } mod cxx_extern { @@ -69,15 +141,46 @@ pub fn daemon_entry() { } let magiskd = MagiskD { - logd: Default::default(), is_emulator, is_recovery, + ..Default::default() }; magiskd.start_log_daemon(); MAGISKD.set(magiskd).ok(); magisk_logging(); } +fn check_data() -> bool { + if let Ok(fd) = open_fd!(cstr!("/proc/mounts"), O_RDONLY | O_CLOEXEC) { + let file = File::from(fd); + let mut mnt = false; + BufReader::new(file).foreach_lines(|line| { + if line.contains(" /data ") && !line.contains("tmpfs") { + mnt = true; + return false; + } + true + }); + if !mnt { + return false; + } + let crypto = get_prop(cstr!("ro.crypto.state"), false); + return if !crypto.is_empty() { + if crypto != "encrypted" { + // Unencrypted, we can directly access data + true + } else { + // Encrypted, check whether vold is started + !get_prop(cstr!("init.svc.vold"), false).is_empty() + } + } else { + // ro.crypto.state is not set, assume it's unencrypted + true + }; + } + false +} + pub fn get_magiskd() -> &'static MagiskD { unsafe { MAGISKD.get().unwrap_unchecked() } } diff --git a/native/src/core/deny/cli.cpp b/native/src/core/deny/cli.cpp index 33d380cc8..f07db3a99 100644 --- a/native/src/core/deny/cli.cpp +++ b/native/src/core/deny/cli.cpp @@ -92,7 +92,7 @@ int denylist_cli(int argc, char **argv) { } // Send request - int fd = connect_daemon(MainRequest::DENYLIST); + int fd = connect_daemon(+RequestCode::DENYLIST); write_int(fd, req); if (req == DenyRequest::ADD || req == DenyRequest::REMOVE) { write_string(fd, argv[2]); diff --git a/native/src/core/include/core.hpp b/native/src/core/include/core.hpp index 52c18359f..c098fcd16 100644 --- a/native/src/core/include/core.hpp +++ b/native/src/core/include/core.hpp @@ -8,7 +8,6 @@ #include #include "socket.hpp" -#include "../core-rs.hpp" #define AID_ROOT 0 #define AID_SHELL 2000 @@ -19,11 +18,17 @@ #define to_app_id(uid) (uid % AID_USER_OFFSET) #define to_user_id(uid) (uid / AID_USER_OFFSET) +namespace rust { +struct MagiskD; +} + struct MagiskD { + // Make sure only references can exist + ~MagiskD() = delete; + // Binding to Rust - void setup_logfile() const noexcept { impl.setup_logfile(); } - bool is_emulator() const noexcept { return impl.is_emulator(); } - bool is_recovery() const noexcept { return impl.is_recovery(); } + static const MagiskD &get(); + void boot_stage_handler(int client, int code) const; // C++ implementation void reboot() const; @@ -32,46 +37,17 @@ struct MagiskD { void boot_complete() const; private: - const rust::MagiskD &impl = rust::get_magiskd(); + const rust::MagiskD &as_rust() const; }; -// Daemon command codes -namespace MainRequest { -enum : int { - START_DAEMON, - CHECK_VERSION, - CHECK_VERSION_CODE, - STOP_DAEMON, - - _SYNC_BARRIER_, - - SUPERUSER, - ZYGOTE_RESTART, - DENYLIST, - SQLITE_CMD, - REMOVE_MODULES, - ZYGISK, - - _STAGE_BARRIER_, - - POST_FS_DATA, - LATE_START, - BOOT_COMPLETE, - - END, -}; -} - // Return codes for daemon -namespace MainResponse { -enum : int { +enum class RespondCode : int { ERROR = -1, OK = 0, ROOT_REQUIRED, ACCESS_DENIED, END }; -} struct module_info { std::string name; @@ -139,3 +115,6 @@ int denylist_cli(int argc, char **argv); void initialize_denylist(); bool is_deny_target(int uid, std::string_view process); void revert_unmount(); + +// Include last to prevent recursive include issues +#include "../core-rs.hpp" diff --git a/native/src/core/lib.rs b/native/src/core/lib.rs index 177eb51b3..3cdb96941 100644 --- a/native/src/core/lib.rs +++ b/native/src/core/lib.rs @@ -18,6 +18,31 @@ mod resetprop; #[cxx::bridge] pub mod ffi { + #[repr(i32)] + enum RequestCode { + START_DAEMON, + CHECK_VERSION, + CHECK_VERSION_CODE, + STOP_DAEMON, + + _SYNC_BARRIER_, + + SUPERUSER, + ZYGOTE_RESTART, + DENYLIST, + SQLITE_CMD, + REMOVE_MODULES, + ZYGISK, + + _STAGE_BARRIER_, + + POST_FS_DATA, + LATE_START, + BOOT_COMPLETE, + + END, + } + extern "C++" { include!("include/resetprop.hpp"); @@ -27,6 +52,16 @@ pub mod ffi { unsafe fn prop_cb_exec(cb: Pin<&mut PropCb>, name: *const c_char, value: *const c_char); } + unsafe extern "C++" { + include!("include/core.hpp"); + + #[cxx_name = "MagiskD"] + type CxxMagiskD; + fn post_fs_data(self: &CxxMagiskD); + fn late_start(self: &CxxMagiskD); + fn boot_complete(self: &CxxMagiskD); + } + extern "Rust" { fn rust_test_entry(); fn android_logging(); @@ -48,11 +83,11 @@ pub mod ffi { type MagiskD; fn get_magiskd() -> &'static MagiskD; - fn get_log_pipe(self: &MagiskD) -> i32; - fn close_log_pipe(self: &MagiskD); fn setup_logfile(self: &MagiskD); fn is_emulator(self: &MagiskD) -> bool; fn is_recovery(self: &MagiskD) -> bool; + fn boot_stage_handler(self: &MagiskD, client: i32, code: i32); + fn enable_safe_mode(self: &MagiskD); } } diff --git a/native/src/core/logging.rs b/native/src/core/logging.rs index bca550afa..75991105d 100644 --- a/native/src/core/logging.rs +++ b/native/src/core/logging.rs @@ -3,7 +3,7 @@ use std::ffi::{c_char, c_void}; use std::fmt::Write as FmtWrite; use std::fs::File; use std::io::{IoSlice, Read, Write}; -use std::os::fd::{AsRawFd, FromRawFd, RawFd}; +use std::os::fd::{FromRawFd, RawFd}; use std::ptr::null_mut; use std::sync::atomic::{AtomicI32, Ordering}; use std::{fs, io}; @@ -382,14 +382,6 @@ impl MagiskD { } } - pub fn get_log_pipe(&self) -> RawFd { - self.logd - .lock() - .unwrap() - .as_ref() - .map_or(-1, |s| s.as_raw_fd()) - } - pub fn close_log_pipe(&self) { *self.logd.lock().unwrap() = None; } diff --git a/native/src/core/magisk.cpp b/native/src/core/magisk.cpp index 3d4f264b4..9381bca86 100644 --- a/native/src/core/magisk.cpp +++ b/native/src/core/magisk.cpp @@ -58,12 +58,12 @@ int magisk_main(int argc, char *argv[]) { #endif return 0; } else if (argv[1] == "-v"sv) { - int fd = connect_daemon(MainRequest::CHECK_VERSION); + int fd = connect_daemon(+RequestCode::CHECK_VERSION); string v = read_string(fd); printf("%s\n", v.data()); return 0; } else if (argv[1] == "-V"sv) { - int fd = connect_daemon(MainRequest::CHECK_VERSION_CODE); + int fd = connect_daemon(+RequestCode::CHECK_VERSION_CODE); printf("%d\n", read_int(fd)); return 0; } else if (argv[1] == "--list"sv) { @@ -83,29 +83,29 @@ int magisk_main(int argc, char *argv[]) { cp_afc(argv[2], argv[3]); return 0; } else if (argv[1] == "--daemon"sv) { - close(connect_daemon(MainRequest::START_DAEMON, true)); + close(connect_daemon(+RequestCode::START_DAEMON, true)); return 0; } else if (argv[1] == "--stop"sv) { - int fd = connect_daemon(MainRequest::STOP_DAEMON); + int fd = connect_daemon(+RequestCode::STOP_DAEMON); return read_int(fd); } else if (argv[1] == "--post-fs-data"sv) { - int fd = connect_daemon(MainRequest::POST_FS_DATA, true); + int fd = connect_daemon(+RequestCode::POST_FS_DATA, true); struct pollfd pfd = { fd, POLLIN, 0 }; poll(&pfd, 1, 1000 * POST_FS_DATA_WAIT_TIME); return 0; } else if (argv[1] == "--service"sv) { - close(connect_daemon(MainRequest::LATE_START, true)); + close(connect_daemon(+RequestCode::LATE_START, true)); return 0; } else if (argv[1] == "--boot-complete"sv) { - close(connect_daemon(MainRequest::BOOT_COMPLETE)); + close(connect_daemon(+RequestCode::BOOT_COMPLETE)); return 0; } else if (argv[1] == "--zygote-restart"sv) { - close(connect_daemon(MainRequest::ZYGOTE_RESTART)); + close(connect_daemon(+RequestCode::ZYGOTE_RESTART)); return 0; } else if (argv[1] == "--denylist"sv) { return denylist_cli(argc - 1, argv + 1); } else if (argc >= 3 && argv[1] == "--sqlite"sv) { - int fd = connect_daemon(MainRequest::SQLITE_CMD); + int fd = connect_daemon(+RequestCode::SQLITE_CMD); write_string(fd, argv[2]); string res; for (;;) { @@ -123,7 +123,7 @@ int magisk_main(int argc, char *argv[]) { } else { usage(); } - int fd = connect_daemon(MainRequest::REMOVE_MODULES); + int fd = connect_daemon(+RequestCode::REMOVE_MODULES); write_int(fd, do_reboot); return read_int(fd); } else if (argv[1] == "--path"sv) { diff --git a/native/src/core/su/su.cpp b/native/src/core/su/su.cpp index ba1783954..995d368f0 100644 --- a/native/src/core/su/su.cpp +++ b/native/src/core/su/su.cpp @@ -189,7 +189,7 @@ int su_client_main(int argc, char *argv[]) { int ptmx, fd; // Connect to client - fd = connect_daemon(MainRequest::SUPERUSER); + fd = connect_daemon(+RequestCode::SUPERUSER); // Send su_request xwrite(fd, &su_req, sizeof(su_req_base)); diff --git a/native/src/core/zygisk/zygisk.hpp b/native/src/core/zygisk/zygisk.hpp index 702cb2d77..49ce762f1 100644 --- a/native/src/core/zygisk/zygisk.hpp +++ b/native/src/core/zygisk/zygisk.hpp @@ -39,7 +39,7 @@ void hookJniNativeMethods(JNIEnv *env, const char *clz, JNINativeMethod *methods int remote_get_info(int uid, const char *process, uint32_t *flags, std::vector &fds); inline int zygisk_request(int req) { - int fd = connect_daemon(MainRequest::ZYGISK); + int fd = connect_daemon(+RequestCode::ZYGISK); if (fd < 0) return fd; write_int(fd, req); return fd;