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