blob: eb46ec16742da66a185f1d49f0ac9964e1a9e534 [file] [log] [blame]
Joel Galensona11c55f2021-07-30 07:36:06 -07001// 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
Andrew Walbran512adf22022-02-03 17:40:50 +000018use anyhow::Context;
Joel Galensona11c55f2021-07-30 07:36:06 -070019use std::os::raw::c_char;
20use std::ptr::null;
21use std::{
22 ffi::{c_void, CStr, CString},
23 str::Utf8Error,
24};
Andrew Walbran686ca922022-02-04 15:50:13 +000025use system_properties_bindgen::prop_info as PropInfo;
Joel Galensona11c55f2021-07-30 07:36:06 -070026use thiserror::Error;
27
28/// Errors this crate can generate
29#[derive(Error, Debug)]
30pub 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,
Victor Hsiehdde93512022-05-10 15:22:54 -070037 /// System properties are not initialized
38 #[error("System properties are not initialized.")]
39 Uninitialized,
Joel Galensona11c55f2021-07-30 07:36:06 -070040 /// __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.
61pub 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.
66pub struct PropertyWatcher {
67 prop_name: CString,
68 prop_info: *const PropInfo,
69 serial: system_properties_bindgen::__uint32_t,
70}
71
72impl PropertyWatcher {
73 /// Create a PropertyWatcher for the named system property.
74 pub fn new(name: &str) -> Result<Self> {
Andrew Walbran686ca922022-02-04 15:50:13 +000075 Ok(Self {
76 prop_name: CString::new(name)?,
77 prop_info: null(),
78 serial: 0,
79 })
Joel Galensona11c55f2021-07-30 07:36:06 -070080 }
81
82 // Lazy-initializing accessor for self.prop_info.
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
Matthew Maurer6b25cb22021-11-09 13:48:16 -080098 fn read_raw(prop_info: *const PropInfo, mut f: impl FnMut(Option<&CStr>, Option<&CStr>)) {
Joel Galensona11c55f2021-07-30 07:36:06 -070099 // 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 _: system_properties_bindgen::__uint32_t,
107 ) {
Andrew Walbran686ca922022-02-04 15:50:13 +0000108 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 };
Joel Galensona11c55f2021-07-30 07:36:06 -0700118 let f = &mut *res_p.cast::<&mut dyn FnMut(Option<&CStr>, Option<&CStr>)>();
119 f(name, value);
120 }
121
Matthew Maurer6b25cb22021-11-09 13:48:16 -0800122 let mut f: &mut dyn FnMut(Option<&CStr>, Option<&CStr>) = &mut f;
Joel Galensona11c55f2021-07-30 07:36:06 -0700123
Matthew Maurer6b25cb22021-11-09 13:48:16 -0800124 // Unsafe block for FFI call. We convert the FnMut
Joel Galensona11c55f2021-07-30 07:36:06 -0700125 // 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.
Matthew Maurer6b25cb22021-11-09 13:48:16 -0800139 pub fn read<T, F>(&mut self, mut f: F) -> Result<T>
Joel Galensona11c55f2021-07-30 07:36:06 -0700140 where
Matthew Maurer6b25cb22021-11-09 13:48:16 -0800141 F: FnMut(&str, &str) -> anyhow::Result<T>,
Joel Galensona11c55f2021-07-30 07:36:06 -0700142 {
Andrew Walbran686ca922022-02-04 15:50:13 +0000143 let prop_info = self
144 .get_prop_info()
145 .ok_or(PropertyWatcherError::SystemPropertyAbsent)?;
Joel Galensona11c55f2021-07-30 07:36:06 -0700146 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()?;
Andrew Walbran686ca922022-02-04 15:50:13 +0000151 let value = value
152 .ok_or(PropertyWatcherError::MissingCString)?
153 .to_str()?;
Joel Galensona11c55f2021-07-30 07:36:06 -0700154 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.
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.
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.
Andrew Walbran512adf22022-02-03 17:40:50 +0000217///
218/// Returns `Ok(None)` if the property doesn't exist.
219pub fn read(name: &str) -> Result<Option<String>> {
Andrew Walbran686ca922022-02-04 15:50:13 +0000220 match PropertyWatcher::new(name)?.read(|_name, value| Ok(value.to_owned())) {
Andrew Walbran512adf22022-02-03 17:40:50 +0000221 Ok(value) => Ok(Some(value)),
222 Err(PropertyWatcherError::SystemPropertyAbsent) => Ok(None),
223 Err(e) => Err(e),
224 }
Joel Galensona11c55f2021-07-30 07:36:06 -0700225}
226
Andrew Scull5e5f7e12022-01-28 00:01:00 +0000227fn 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.
Andrew Walbran512adf22022-02-03 17:40:50 +0000239pub 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))
Andrew Scull5e5f7e12022-01-28 00:01:00 +0000244}
245
Joel Galensona11c55f2021-07-30 07:36:06 -0700246/// Writes a system property.
Andrew Walbran512adf22022-02-03 17:40:50 +0000247pub fn write(name: &str, value: &str) -> Result<()> {
Joel Galensona11c55f2021-07-30 07:36:06 -0700248 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 {
Andrew Walbran512adf22022-02-03 17:40:50 +0000264 Err(PropertyWatcherError::SetPropertyFailed)
Joel Galensona11c55f2021-07-30 07:36:06 -0700265 }
266}
Andrew Scull5e5f7e12022-01-28 00:01:00 +0000267
Victor Hsiehdde93512022-05-10 15:22:54 -0700268/// Iterates through the properties (that the current process is allowed to access).
269pub fn foreach<F>(mut f: F) -> Result<()>
270where
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 _: system_properties_bindgen::__uint32_t,
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
Andrew Scull5e5f7e12022-01-28 00:01:00 +0000321#[cfg(test)]
Andrew Walbran686ca922022-02-04 15:50:13 +0000322mod test {
323 use super::*;
Andrew Scull5e5f7e12022-01-28 00:01:00 +0000324
Andrew Walbran686ca922022-02-04 15:50:13 +0000325 #[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 }
Andrew Walbran512adf22022-02-03 17:40:50 +0000337
338 #[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 }
Victor Hsiehdde93512022-05-10 15:22:54 -0700345
346 #[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 }
Andrew Scull5e5f7e12022-01-28 00:01:00 +0000356}