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