• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 use anyhow::Context;
19 use std::os::raw::c_char;
20 use std::ptr::null;
21 use std::{
22     ffi::{c_uint, c_void, CStr, CString},
23     str::Utf8Error,
24 };
25 use system_properties_bindgen::prop_info as PropInfo;
26 use thiserror::Error;
27 
28 /// Errors this crate can generate
29 #[derive(Error, Debug)]
30 pub enum PropertyWatcherError {
31     /// We can't watch for a property whose name contains a NUL character.
32     #[error("Cannot convert name to C string")]
33     BadNameError(#[from] std::ffi::NulError),
34     /// We can only watch for properties that exist when the watcher is created.
35     #[error("System property is absent")]
36     SystemPropertyAbsent,
37     /// System properties are not initialized
38     #[error("System properties are not initialized.")]
39     Uninitialized,
40     /// __system_property_wait timed out despite being given no timeout.
41     #[error("Wait failed")]
42     WaitFailed,
43     /// read callback was not called
44     #[error("__system_property_read_callback did not call callback")]
45     ReadCallbackNotCalled,
46     /// read callback gave us a NULL pointer
47     #[error("__system_property_read_callback gave us a NULL pointer instead of a string")]
48     MissingCString,
49     /// read callback gave us a bad C string
50     #[error("__system_property_read_callback gave us a non-UTF8 C string")]
51     BadCString(#[from] Utf8Error),
52     /// read callback returned an error
53     #[error("Callback failed")]
54     CallbackError(#[from] anyhow::Error),
55     /// Failure in setting the system property
56     #[error("__system_property_set failed.")]
57     SetPropertyFailed,
58 }
59 
60 /// Result type specific for this crate.
61 pub type Result<T> = std::result::Result<T, PropertyWatcherError>;
62 
63 /// PropertyWatcher takes the name of an Android system property such
64 /// as `keystore.boot_level`; it can report the current value of this
65 /// property, or wait for it to change.
66 pub struct PropertyWatcher {
67     prop_name: CString,
68     prop_info: *const PropInfo,
69     serial: c_uint,
70 }
71 
72 impl PropertyWatcher {
73     /// Create a PropertyWatcher for the named system property.
new(name: &str) -> Result<Self>74     pub fn new(name: &str) -> Result<Self> {
75         Ok(Self {
76             prop_name: CString::new(name)?,
77             prop_info: null(),
78             serial: 0,
79         })
80     }
81 
82     // Lazy-initializing accessor for self.prop_info.
get_prop_info(&mut self) -> Option<*const PropInfo>83     fn get_prop_info(&mut self) -> Option<*const PropInfo> {
84         if self.prop_info.is_null() {
85             // Unsafe required for FFI call. Input and output are both const.
86             // The returned pointer is valid for the lifetime of the program.
87             self.prop_info = unsafe {
88                 system_properties_bindgen::__system_property_find(self.prop_name.as_ptr())
89             };
90         }
91         if self.prop_info.is_null() {
92             None
93         } else {
94             Some(self.prop_info)
95         }
96     }
97 
read_raw(prop_info: *const PropInfo, mut f: impl FnMut(Option<&CStr>, Option<&CStr>))98     fn read_raw(prop_info: *const PropInfo, mut f: impl FnMut(Option<&CStr>, Option<&CStr>)) {
99         // Unsafe function converts values passed to us by
100         // __system_property_read_callback to Rust form
101         // and pass them to inner callback.
102         unsafe extern "C" fn callback(
103             res_p: *mut c_void,
104             name: *const c_char,
105             value: *const c_char,
106             _: c_uint,
107         ) {
108             let name = if name.is_null() {
109                 None
110             } else {
111                 Some(CStr::from_ptr(name))
112             };
113             let value = if value.is_null() {
114                 None
115             } else {
116                 Some(CStr::from_ptr(value))
117             };
118             let f = &mut *res_p.cast::<&mut dyn FnMut(Option<&CStr>, Option<&CStr>)>();
119             f(name, value);
120         }
121 
122         let mut f: &mut dyn FnMut(Option<&CStr>, Option<&CStr>) = &mut f;
123 
124         // Unsafe block for FFI call. We convert the FnMut
125         // to a void pointer, and unwrap it in our callback.
126         unsafe {
127             system_properties_bindgen::__system_property_read_callback(
128                 prop_info,
129                 Some(callback),
130                 &mut f as *mut _ as *mut c_void,
131             )
132         }
133     }
134 
135     /// Call the passed function, passing it the name and current value
136     /// of this system property. See documentation for
137     /// `__system_property_read_callback` for details.
138     /// 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>,139     pub fn read<T, F>(&mut self, mut f: F) -> Result<T>
140     where
141         F: FnMut(&str, &str) -> anyhow::Result<T>,
142     {
143         let prop_info = self
144             .get_prop_info()
145             .ok_or(PropertyWatcherError::SystemPropertyAbsent)?;
146         let mut result = Err(PropertyWatcherError::ReadCallbackNotCalled);
147         Self::read_raw(prop_info, |name, value| {
148             // use a wrapping closure as an erzatz try block.
149             result = (|| {
150                 let name = name.ok_or(PropertyWatcherError::MissingCString)?.to_str()?;
151                 let value = value
152                     .ok_or(PropertyWatcherError::MissingCString)?
153                     .to_str()?;
154                 f(name, value).map_err(PropertyWatcherError::CallbackError)
155             })()
156         });
157         result
158     }
159 
160     // Waits for the property that self is watching to be created. Returns immediately if the
161     // property already exists.
wait_for_property_creation(&mut self) -> Result<()>162     fn wait_for_property_creation(&mut self) -> Result<()> {
163         let mut global_serial = 0;
164         loop {
165             match self.get_prop_info() {
166                 Some(_) => return Ok(()),
167                 None => {
168                     // Unsafe call for FFI. The function modifies only global_serial, and has
169                     // no side-effects.
170                     if !unsafe {
171                         // Wait for a global serial number change, then try again. On success,
172                         // the function will update global_serial with the last version seen.
173                         system_properties_bindgen::__system_property_wait(
174                             null(),
175                             global_serial,
176                             &mut global_serial,
177                             null(),
178                         )
179                     } {
180                         return Err(PropertyWatcherError::WaitFailed);
181                     }
182                 }
183             }
184         }
185     }
186 
187     /// Wait for the system property to change. This
188     /// records the serial number of the last change, so
189     /// race conditions are avoided.
wait(&mut self) -> Result<()>190     pub fn wait(&mut self) -> Result<()> {
191         // If the property is null, then wait for it to be created. Subsequent waits will
192         // skip this step and wait for our specific property to change.
193         if self.prop_info.is_null() {
194             return self.wait_for_property_creation();
195         }
196 
197         let mut new_serial = self.serial;
198         // Unsafe block to call __system_property_wait.
199         // All arguments are private to PropertyWatcher so we
200         // can be confident they are valid.
201         if !unsafe {
202             system_properties_bindgen::__system_property_wait(
203                 self.prop_info,
204                 self.serial,
205                 &mut new_serial,
206                 null(),
207             )
208         } {
209             return Err(PropertyWatcherError::WaitFailed);
210         }
211         self.serial = new_serial;
212         Ok(())
213     }
214 }
215 
216 /// Reads a system property.
217 ///
218 /// Returns `Ok(None)` if the property doesn't exist.
read(name: &str) -> Result<Option<String>>219 pub fn read(name: &str) -> Result<Option<String>> {
220     match PropertyWatcher::new(name)?.read(|_name, value| Ok(value.to_owned())) {
221         Ok(value) => Ok(Some(value)),
222         Err(PropertyWatcherError::SystemPropertyAbsent) => Ok(None),
223         Err(e) => Err(e),
224     }
225 }
226 
parse_bool(value: &str) -> Option<bool>227 fn parse_bool(value: &str) -> Option<bool> {
228     if ["1", "y", "yes", "on", "true"].contains(&value) {
229         Some(true)
230     } else if ["0", "n", "no", "off", "false"].contains(&value) {
231         Some(false)
232     } else {
233         None
234     }
235 }
236 
237 /// Returns true if the system property `name` has the value "1", "y", "yes", "on", or "true",
238 /// false for "0", "n", "no", "off", or "false", or `default_value` otherwise.
read_bool(name: &str, default_value: bool) -> Result<bool>239 pub fn read_bool(name: &str, default_value: bool) -> Result<bool> {
240     Ok(read(name)?
241         .as_deref()
242         .and_then(parse_bool)
243         .unwrap_or(default_value))
244 }
245 
246 /// Writes a system property.
write(name: &str, value: &str) -> Result<()>247 pub fn write(name: &str, value: &str) -> Result<()> {
248     if
249     // Unsafe required for FFI call. Input and output are both const and valid strings.
250     unsafe {
251         // If successful, __system_property_set returns 0, otherwise, returns -1.
252         system_properties_bindgen::__system_property_set(
253             CString::new(name)
254                 .context("Failed to construct CString from name.")?
255                 .as_ptr(),
256             CString::new(value)
257                 .context("Failed to construct CString from value.")?
258                 .as_ptr(),
259         )
260     } == 0
261     {
262         Ok(())
263     } else {
264         Err(PropertyWatcherError::SetPropertyFailed)
265     }
266 }
267 
268 /// Iterates through the properties (that the current process is allowed to access).
foreach<F>(mut f: F) -> Result<()> where F: FnMut(&str, &str),269 pub fn foreach<F>(mut f: F) -> Result<()>
270 where
271     F: FnMut(&str, &str),
272 {
273     extern "C" fn read_callback<F: FnMut(&str, &str)>(
274         res_p: *mut c_void,
275         name: *const c_char,
276         value: *const c_char,
277         _: c_uint,
278     ) {
279         // SAFETY: system properties are null-terminated C string in UTF-8. See IsLegalPropertyName
280         // and IsLegalPropertyValue in system/core/init/util.cpp.
281         let name = unsafe { CStr::from_ptr(name) }.to_str().unwrap();
282         let value = unsafe { CStr::from_ptr(value) }.to_str().unwrap();
283 
284         let ptr = res_p as *mut F;
285         // SAFETY: ptr points to the API user's callback, which was cast to `*mut c_void` below.
286         // Here we're casting it back.
287         let f = unsafe { ptr.as_mut() }.unwrap();
288         f(name, value);
289     }
290 
291     extern "C" fn foreach_callback<F: FnMut(&str, &str)>(
292         prop_info: *const PropInfo,
293         res_p: *mut c_void,
294     ) {
295         // SAFETY: FFI call with an internal callback function in Rust, with other parameters
296         // passed through.
297         unsafe {
298             system_properties_bindgen::__system_property_read_callback(
299                 prop_info,
300                 Some(read_callback::<F>),
301                 res_p,
302             )
303         }
304     }
305 
306     // SAFETY: FFI call with an internal callback function in Rust, and another client's callback
307     // that's cast only for our own use right above.
308     let retval = unsafe {
309         system_properties_bindgen::__system_property_foreach(
310             Some(foreach_callback::<F>),
311             &mut f as *mut _ as *mut c_void,
312         )
313     };
314     if retval < 0 {
315         Err(PropertyWatcherError::Uninitialized)
316     } else {
317         Ok(())
318     }
319 }
320 
321 #[cfg(test)]
322 mod test {
323     use super::*;
324 
325     #[test]
parse_bool_test()326     fn parse_bool_test() {
327         for s in ["1", "y", "yes", "on", "true"] {
328             assert_eq!(parse_bool(s), Some(true), "testing with {}", s);
329         }
330         for s in ["0", "n", "no", "off", "false"] {
331             assert_eq!(parse_bool(s), Some(false), "testing with {}", s);
332         }
333         for s in ["random", "00", "of course", "no way", "YES", "Off"] {
334             assert_eq!(parse_bool(s), None, "testing with {}", s);
335         }
336     }
337 
338     #[test]
read_absent_bool_test()339     fn read_absent_bool_test() {
340         let prop = "certainly.does.not.exist";
341         assert!(matches!(read(prop), Ok(None)));
342         assert!(read_bool(prop, true).unwrap_or(false));
343         assert!(!read_bool(prop, false).unwrap_or(true));
344     }
345 
346     #[test]
foreach_test()347     fn foreach_test() {
348         let mut properties = Vec::new();
349         assert!(foreach(|name, value| {
350             properties.push((name.to_owned(), value.to_owned()));
351         })
352         .is_ok());
353         // Assuming the test runs on Android, any process can at least see some system properties.
354         assert!(!properties.is_empty());
355     }
356 }
357