1 // Copyright (C) 2021 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 //! This crate provides the PropertyWatcher type, which watches for changes
16 //! in Android system properties.
17
18 // Temporary public re-export to avoid breaking dependents.
19 pub use self::error::{PropertyWatcherError, Result};
20 use anyhow::Context;
21 use libc::timespec;
22 use std::os::raw::c_char;
23 use std::ptr::null;
24 use std::{
25 ffi::{c_uint, c_void, CStr, CString},
26 time::{Duration, Instant},
27 };
28 use system_properties_bindgen::prop_info as PropInfo;
29
30 pub mod error;
31 #[doc(hidden)]
32 pub mod parsers_formatters;
33
34 /// PropertyWatcher takes the name of an Android system property such
35 /// as `keystore.boot_level`; it can report the current value of this
36 /// property, or wait for it to change.
37 pub struct PropertyWatcher {
38 prop_name: CString,
39 prop_info: *const PropInfo,
40 serial: c_uint,
41 }
42
43 impl PropertyWatcher {
44 /// Create a PropertyWatcher for the named system property.
new(name: &str) -> Result<Self>45 pub fn new(name: &str) -> Result<Self> {
46 Ok(Self { prop_name: CString::new(name)?, prop_info: null(), serial: 0 })
47 }
48
49 // Lazy-initializing accessor for self.prop_info.
get_prop_info(&mut self) -> Option<*const PropInfo>50 fn get_prop_info(&mut self) -> Option<*const PropInfo> {
51 if self.prop_info.is_null() {
52 // SAFETY: Input and output are both const. The returned pointer is valid for the
53 // lifetime of the program.
54 self.prop_info = unsafe {
55 system_properties_bindgen::__system_property_find(self.prop_name.as_ptr())
56 };
57 }
58 if self.prop_info.is_null() {
59 None
60 } else {
61 Some(self.prop_info)
62 }
63 }
64
read_raw(prop_info: *const PropInfo, mut f: impl FnMut(Option<&CStr>, Option<&CStr>))65 fn read_raw(prop_info: *const PropInfo, mut f: impl FnMut(Option<&CStr>, Option<&CStr>)) {
66 // Unsafe function converts values passed to us by
67 // __system_property_read_callback to Rust form
68 // and pass them to inner callback.
69 unsafe extern "C" fn callback(
70 res_p: *mut c_void,
71 name: *const c_char,
72 value: *const c_char,
73 _: c_uint,
74 ) {
75 let name = if name.is_null() {
76 None
77 } else {
78 // SAFETY: system property names are null-terminated C strings in UTF-8. See
79 // IsLegalPropertyName in system/core/init/util.cpp.
80 Some(unsafe { CStr::from_ptr(name) })
81 };
82 let value = if value.is_null() {
83 None
84 } else {
85 // SAFETY: system property values are null-terminated C strings in UTF-8. See
86 // IsLegalPropertyValue in system/core/init/util.cpp.
87 Some(unsafe { CStr::from_ptr(value) })
88 };
89 // SAFETY: We converted the FnMut from `f` to a void pointer below, now we convert it
90 // back.
91 let f = unsafe { &mut *res_p.cast::<&mut dyn FnMut(Option<&CStr>, Option<&CStr>)>() };
92 f(name, value);
93 }
94
95 let mut f: &mut dyn FnMut(Option<&CStr>, Option<&CStr>) = &mut f;
96
97 // SAFETY: We convert the FnMut to a void pointer, and unwrap it in our callback.
98 unsafe {
99 system_properties_bindgen::__system_property_read_callback(
100 prop_info,
101 Some(callback),
102 &mut f as *mut _ as *mut c_void,
103 )
104 }
105 }
106
107 /// Call the passed function, passing it the name and current value
108 /// of this system property. See documentation for
109 /// `__system_property_read_callback` for details.
110 /// Returns an error if the property is empty or doesn't exist.
read<T, F>(&mut self, mut f: F) -> Result<T> where F: FnMut(&str, &str) -> anyhow::Result<T>,111 pub fn read<T, F>(&mut self, mut f: F) -> Result<T>
112 where
113 F: FnMut(&str, &str) -> anyhow::Result<T>,
114 {
115 let prop_info = self.get_prop_info().ok_or(PropertyWatcherError::SystemPropertyAbsent)?;
116 let mut result = Err(PropertyWatcherError::ReadCallbackNotCalled);
117 Self::read_raw(prop_info, |name, value| {
118 // use a wrapping closure as an erzatz try block.
119 result = (|| {
120 let name = name.ok_or(PropertyWatcherError::MissingCString)?.to_str()?;
121 let value = value.ok_or(PropertyWatcherError::MissingCString)?.to_str()?;
122 f(name, value).map_err(PropertyWatcherError::CallbackError)
123 })()
124 });
125 result
126 }
127
128 // Waits for the property that self is watching to be created. Returns immediately if the
129 // property already exists.
wait_for_property_creation_until(&mut self, until: Option<Instant>) -> Result<()>130 fn wait_for_property_creation_until(&mut self, until: Option<Instant>) -> Result<()> {
131 let mut global_serial = 0;
132 loop {
133 match self.get_prop_info() {
134 Some(_) => return Ok(()),
135 None => {
136 let remaining_timeout = remaining_time_until(until);
137 // SAFETY: The function modifies only global_serial, and has no side-effects.
138 if !unsafe {
139 // Wait for a global serial number change, then try again. On success,
140 // the function will update global_serial with the last version seen.
141 system_properties_bindgen::__system_property_wait(
142 null(),
143 global_serial,
144 &mut global_serial,
145 if let Some(remaining_timeout) = &remaining_timeout {
146 remaining_timeout
147 } else {
148 null()
149 },
150 )
151 } {
152 return Err(PropertyWatcherError::WaitFailed);
153 }
154 }
155 }
156 }
157 }
158
159 /// Waits until the system property changes, or `until` is reached.
160 ///
161 /// This records the serial number of the last change, so race conditions are avoided.
wait_for_property_change_until(&mut self, until: Option<Instant>) -> Result<()>162 fn wait_for_property_change_until(&mut self, until: Option<Instant>) -> Result<()> {
163 // If the property is null, then wait for it to be created. Subsequent waits will
164 // skip this step and wait for our specific property to change.
165 if self.prop_info.is_null() {
166 return self.wait_for_property_creation_until(None);
167 }
168
169 let remaining_timeout = remaining_time_until(until);
170 let mut new_serial = self.serial;
171 // SAFETY: All arguments are private to PropertyWatcher so we can be confident they are
172 // valid.
173 if !unsafe {
174 system_properties_bindgen::__system_property_wait(
175 self.prop_info,
176 self.serial,
177 &mut new_serial,
178 if let Some(remaining_timeout) = &remaining_timeout {
179 remaining_timeout
180 } else {
181 null()
182 },
183 )
184 } {
185 return Err(PropertyWatcherError::WaitFailed);
186 }
187 self.serial = new_serial;
188 Ok(())
189 }
190
191 /// Waits for the system property to change, or the timeout to elapse.
192 ///
193 /// This records the serial number of the last change, so race conditions are avoided.
wait(&mut self, timeout: Option<Duration>) -> Result<()>194 pub fn wait(&mut self, timeout: Option<Duration>) -> Result<()> {
195 let until = timeout.map(|timeout| Instant::now() + timeout);
196 self.wait_for_property_change_until(until)
197 }
198
199 /// Waits until the property exists and has the given value.
wait_for_value( &mut self, expected_value: &str, timeout: Option<Duration>, ) -> Result<()>200 pub fn wait_for_value(
201 &mut self,
202 expected_value: &str,
203 timeout: Option<Duration>,
204 ) -> Result<()> {
205 let until = timeout.map(|timeout| Instant::now() + timeout);
206
207 self.wait_for_property_creation_until(until)?;
208
209 while self.read(|_, value| Ok(value != expected_value))? {
210 self.wait_for_property_change_until(until)?;
211 }
212
213 Ok(())
214 }
215 }
216
217 /// Reads a system property.
218 ///
219 /// Returns `Ok(None)` if the property doesn't exist.
read(name: &str) -> Result<Option<String>>220 pub fn read(name: &str) -> Result<Option<String>> {
221 match PropertyWatcher::new(name)?.read(|_name, value| Ok(value.to_owned())) {
222 Ok(value) => Ok(Some(value)),
223 Err(PropertyWatcherError::SystemPropertyAbsent) => Ok(None),
224 Err(e) => Err(e),
225 }
226 }
227
parse_bool(value: &str) -> Option<bool>228 fn parse_bool(value: &str) -> Option<bool> {
229 match value {
230 "1" | "y" | "yes" | "on" | "true" => Some(true),
231 "0" | "n" | "no" | "off" | "false" => Some(false),
232 _ => None,
233 }
234 }
235
236 /// Returns the duration remaining until the given instant.
237 ///
238 /// Returns `None` if `None` is passed in, or `Some(0)` if `until` is in the past.
remaining_time_until(until: Option<Instant>) -> Option<timespec>239 fn remaining_time_until(until: Option<Instant>) -> Option<timespec> {
240 until.map(|until| {
241 duration_to_timespec(until.checked_duration_since(Instant::now()).unwrap_or_default())
242 })
243 }
244
245 /// Converts the given `Duration` to a C `timespec`.
duration_to_timespec(duration: Duration) -> timespec246 fn duration_to_timespec(duration: Duration) -> timespec {
247 timespec {
248 tv_sec: duration.as_secs().try_into().unwrap(),
249 tv_nsec: duration.subsec_nanos() as _,
250 }
251 }
252
253 /// Returns true if the system property `name` has the value "1", "y", "yes", "on", or "true",
254 /// false for "0", "n", "no", "off", or "false", or `default_value` otherwise.
read_bool(name: &str, default_value: bool) -> Result<bool>255 pub fn read_bool(name: &str, default_value: bool) -> Result<bool> {
256 Ok(read(name)?.as_deref().and_then(parse_bool).unwrap_or(default_value))
257 }
258
259 /// Writes a system property.
write(name: &str, value: &str) -> Result<()>260 pub fn write(name: &str, value: &str) -> Result<()> {
261 if
262 // SAFETY: Input and output are both const and valid strings.
263 unsafe {
264 // If successful, __system_property_set returns 0, otherwise, returns -1.
265 system_properties_bindgen::__system_property_set(
266 CString::new(name).context("Failed to construct CString from name.")?.as_ptr(),
267 CString::new(value).context("Failed to construct CString from value.")?.as_ptr(),
268 )
269 } == 0
270 {
271 Ok(())
272 } else {
273 Err(PropertyWatcherError::SetPropertyFailed)
274 }
275 }
276
277 /// Iterates through the properties (that the current process is allowed to access).
foreach<F>(mut f: F) -> Result<()> where F: FnMut(&str, &str),278 pub fn foreach<F>(mut f: F) -> Result<()>
279 where
280 F: FnMut(&str, &str),
281 {
282 extern "C" fn read_callback<F: FnMut(&str, &str)>(
283 res_p: *mut c_void,
284 name: *const c_char,
285 value: *const c_char,
286 _: c_uint,
287 ) {
288 // SAFETY: system property names are null-terminated C strings in UTF-8. See
289 // IsLegalPropertyName in system/core/init/util.cpp.
290 let name = unsafe { CStr::from_ptr(name) }.to_str().unwrap();
291 // SAFETY: system property values are null-terminated C strings in UTF-8. See
292 // IsLegalPropertyValue in system/core/init/util.cpp.
293 let value = unsafe { CStr::from_ptr(value) }.to_str().unwrap();
294
295 let ptr = res_p as *mut F;
296 // SAFETY: ptr points to the API user's callback, which was cast to `*mut c_void` below.
297 // Here we're casting it back.
298 let f = unsafe { ptr.as_mut() }.unwrap();
299 f(name, value);
300 }
301
302 extern "C" fn foreach_callback<F: FnMut(&str, &str)>(
303 prop_info: *const PropInfo,
304 res_p: *mut c_void,
305 ) {
306 // SAFETY: FFI call with an internal callback function in Rust, with other parameters
307 // passed through.
308 unsafe {
309 system_properties_bindgen::__system_property_read_callback(
310 prop_info,
311 Some(read_callback::<F>),
312 res_p,
313 )
314 }
315 }
316
317 // SAFETY: FFI call with an internal callback function in Rust, and another client's callback
318 // that's cast only for our own use right above.
319 let retval = unsafe {
320 system_properties_bindgen::__system_property_foreach(
321 Some(foreach_callback::<F>),
322 &mut f as *mut _ as *mut c_void,
323 )
324 };
325 if retval < 0 {
326 Err(PropertyWatcherError::Uninitialized)
327 } else {
328 Ok(())
329 }
330 }
331
332 #[cfg(test)]
333 mod test {
334 use super::*;
335
336 #[test]
parse_bool_test()337 fn parse_bool_test() {
338 for s in ["1", "y", "yes", "on", "true"] {
339 assert_eq!(parse_bool(s), Some(true), "testing with {}", s);
340 }
341 for s in ["0", "n", "no", "off", "false"] {
342 assert_eq!(parse_bool(s), Some(false), "testing with {}", s);
343 }
344 for s in ["random", "00", "of course", "no way", "YES", "Off"] {
345 assert_eq!(parse_bool(s), None, "testing with {}", s);
346 }
347 }
348
349 #[test]
read_absent_bool_test()350 fn read_absent_bool_test() {
351 let prop = "certainly.does.not.exist";
352 assert!(matches!(read(prop), Ok(None)));
353 assert!(read_bool(prop, true).unwrap_or(false));
354 assert!(!read_bool(prop, false).unwrap_or(true));
355 }
356
357 #[test]
foreach_test()358 fn foreach_test() {
359 let mut properties = Vec::new();
360 assert!(foreach(|name, value| {
361 properties.push((name.to_owned(), value.to_owned()));
362 })
363 .is_ok());
364 // Assuming the test runs on Android, any process can at least see some system properties.
365 assert!(!properties.is_empty());
366 }
367 }
368