From e94d65b4b223b9aca3e0773262ffa091f13bfd2c Mon Sep 17 00:00:00 2001 From: LoveSy Date: Fri, 22 Dec 2023 01:49:25 +0800 Subject: [PATCH] Add `resetprop -w` for waiting property change It's very easy to wait for property change both in Java and C++, but it's not the case in shell script. With this patch, developers can now easily to wait for property change, just like what we have in `.rc` files, and to wait for boot complete. --- docs/guides.md | 2 +- native/src/core/include/resetprop.hpp | 8 ++--- native/src/core/lib.rs | 7 +++- native/src/core/resetprop/persist.rs | 2 +- native/src/core/resetprop/resetprop.cpp | 44 ++++++++++++++++++------- 5 files changed, 45 insertions(+), 18 deletions(-) diff --git a/docs/guides.md b/docs/guides.md index 6bd7e5c84..49c48c839 100644 --- a/docs/guides.md +++ b/docs/guides.md @@ -109,7 +109,7 @@ Update JSON format: #### Shell scripts (`*.sh`) -Please read the [Boot Scripts](#boot-scripts) section to understand the difference between `post-fs-data.sh` and `service.sh`. For most module developers, `service.sh` should be good enough if you just need to run a boot script. +Please read the [Boot Scripts](#boot-scripts) section to understand the difference between `post-fs-data.sh` and `service.sh`. For most module developers, `service.sh` should be good enough if you just need to run a boot script. If you need to wait for boot completed, you can use `resetprop -w sys.boot_completed 0`. In all scripts of your module, please use `MODDIR=${0%/*}` to get your module's base directory path; do **NOT** hardcode your module path in scripts. If Zygisk is enabled, the environment variable `ZYGISK_ENABLED` will be set to `1`. diff --git a/native/src/core/include/resetprop.hpp b/native/src/core/include/resetprop.hpp index 931f52500..88e1d3e24 100644 --- a/native/src/core/include/resetprop.hpp +++ b/native/src/core/include/resetprop.hpp @@ -5,14 +5,14 @@ #include struct prop_cb { - virtual void exec(const char *name, const char *value) = 0; + virtual void exec(const char *name, const char *value, uint32_t serial) = 0; }; using prop_list = std::map; struct prop_collector : prop_cb { explicit prop_collector(prop_list &list) : list(list) {} - void exec(const char *name, const char *value) override { + void exec(const char *name, const char *value, uint32_t) override { list.insert({name, value}); } private: @@ -26,6 +26,6 @@ int delete_prop(const char *name, bool persist = false); int set_prop(const char *name, const char *value, bool skip_svc = false); void load_prop_file(const char *filename, bool skip_svc = false); -static inline void prop_cb_exec(prop_cb &cb, const char *name, const char *value) { - cb.exec(name, value); +static inline void prop_cb_exec(prop_cb &cb, const char *name, const char *value, uint32_t serial) { + cb.exec(name, value, serial); } diff --git a/native/src/core/lib.rs b/native/src/core/lib.rs index 87dcf33d2..7876fdbfa 100644 --- a/native/src/core/lib.rs +++ b/native/src/core/lib.rs @@ -49,7 +49,12 @@ pub mod ffi { #[cxx_name = "prop_cb"] type PropCb; unsafe fn get_prop_rs(name: *const c_char, persist: bool) -> String; - unsafe fn prop_cb_exec(cb: Pin<&mut PropCb>, name: *const c_char, value: *const c_char); + unsafe fn prop_cb_exec( + cb: Pin<&mut PropCb>, + name: *const c_char, + value: *const c_char, + serial: u32, + ); } unsafe extern "C++" { diff --git a/native/src/core/resetprop/persist.rs b/native/src/core/resetprop/persist.rs index e2231bbf7..2aec56025 100644 --- a/native/src/core/resetprop/persist.rs +++ b/native/src/core/resetprop/persist.rs @@ -39,7 +39,7 @@ trait PropCbExec { impl PropCbExec for Pin<&mut PropCb> { fn exec(&mut self, name: &Utf8CStr, value: &Utf8CStr) { - unsafe { prop_cb_exec(self.as_mut(), name.as_ptr(), value.as_ptr()) } + unsafe { prop_cb_exec(self.as_mut(), name.as_ptr(), value.as_ptr(), u32::MAX) } } } diff --git a/native/src/core/resetprop/resetprop.cpp b/native/src/core/resetprop/resetprop.cpp index 786428056..297f60cd9 100644 --- a/native/src/core/resetprop/resetprop.cpp +++ b/native/src/core/resetprop/resetprop.cpp @@ -33,10 +33,12 @@ struct PropFlags { void setPersist() { flags |= (1 << 1); } void setContext() { flags |= (1 << 2); } void setPersistOnly() { flags |= (1 << 3); setPersist(); } + void setWait() { flags |= (1 << 4); } bool isSkipSvc() const { return flags & 1; } bool isPersist() const { return flags & (1 << 1); } bool isContext() const { return flags & (1 << 2); } bool isPersistOnly() const { return flags & (1 << 3); } + bool isWait() const { return flags & (1 << 4); } private: uint32_t flags = 0; }; @@ -49,7 +51,7 @@ Usage: %s [flags] [arguments...] Read mode arguments: (no arguments) print all properties - NAME get property + NAME [OLD_VALUE] get property of NAME, optionally with an OLD_VALUE for -w Write mode arguments: NAME VALUE set property NAME as VALUE @@ -64,6 +66,8 @@ Read mode flags: -p also read persistent props from storage -P only read persistent props from storage -Z get property context instead of value + -w wait for property change, and if OLD_VALUE is specified, wait for it to change to other value + return immediately if persistent Write mode flags: -n set properties bypassing property_service @@ -104,8 +108,8 @@ illegal: static void read_prop_with_cb(const prop_info *pi, void *cb) { if (system_property_read_callback) { - auto callback = [](void *cb, const char *name, const char *value, uint32_t) { - static_cast(cb)->exec(name, value); + auto callback = [](void *cb, const char *name, const char *value, uint32_t serial) { + static_cast(cb)->exec(name, value, serial); }; system_property_read_callback(pi, callback, cb); } else { @@ -114,19 +118,21 @@ static void read_prop_with_cb(const prop_info *pi, void *cb) { name[0] = '\0'; value[0] = '\0'; system_property_read(pi, name, value); - static_cast(cb)->exec(name, value); + static_cast(cb)->exec(name, value, pi->serial); } } template struct prop_to_string : prop_cb { - void exec(const char *, const char *value) override { + void exec(const char *, const char *value, uint32_t serial) override { val = value; + s = serial; } StringType val; + uint32_t s; }; -template<> void prop_to_string::exec(const char *, const char *value) { +template<> void prop_to_string::exec(const char *, const char *value, uint32_t) { // We do not want to crash when values are not UTF-8 val = rust::String::lossy(value); } @@ -181,7 +187,7 @@ static int set_prop(const char *name, const char *value, PropFlags flags) { } template -static StringType get_prop(const char *name, PropFlags flags) { +static StringType get_prop(const char *name, PropFlags flags, const char *wait_value = nullptr) { if (!check_legal_property_name(name)) return {}; @@ -190,15 +196,20 @@ static StringType get_prop(const char *name, PropFlags flags) { if (flags.isContext()) { auto context = __system_property_get_context(name) ?: ""; LOGD("resetprop: prop context [%s]: [%s]\n", name, context); - cb.exec(name, context); + cb.exec(name, context, -1); return cb.val; } if (!flags.isPersistOnly()) { - if (auto pi = system_property_find(name)) { + auto pi = system_property_find(name); + if (!pi) return {}; + read_prop_with_cb(pi, &cb); + if (flags.isWait() && (wait_value == nullptr || cb.val == wait_value)) { + uint32_t new_serial; + __system_property_wait(pi, cb.s, &new_serial, nullptr); read_prop_with_cb(pi, &cb); - LOGD("resetprop: get prop [%s]: [%s]\n", name, cb.val.c_str()); } + LOGD("resetprop: get prop [%s]: [%s]\n", name, cb.val.c_str()); } if (cb.val.empty() && flags.isPersist() && str_starts(name, "persist.")) @@ -319,6 +330,9 @@ int resetprop_main(int argc, char *argv[]) { case 'Z': flags.setContext(); continue; + case 'w': + flags.setWait(); + continue; case '\0': break; default: @@ -355,7 +369,15 @@ int resetprop_main(int argc, char *argv[]) { return 0; } case 2: - return set_prop(argv[0], argv[1], flags); + if (flags.isWait()) { + auto val = get_prop(argv[0], flags, argv[1]); + if (val.empty()) + return 1; + printf("%s\n", val.data()); + return 0; + } else { + return set_prop(argv[0], argv[1], flags); + } default: usage(argv0); }