• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 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::{anyhow, Context, Result as AnyhowResult};
19 use keystore2_system_property_bindgen::prop_info as PropInfo;
20 use std::os::raw::c_char;
21 use std::ptr::null;
22 use std::{
23     ffi::{c_void, CStr, CString},
24     str::Utf8Error,
25 };
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_property_wait timed out despite being given no timeout.
38     #[error("Wait failed")]
39     WaitFailed,
40     /// read callback was not called
41     #[error("__system_property_read_callback did not call callback")]
42     ReadCallbackNotCalled,
43     /// read callback gave us a NULL pointer
44     #[error("__system_property_read_callback gave us a NULL pointer instead of a string")]
45     MissingCString,
46     /// read callback gave us a bad C string
47     #[error("__system_property_read_callback gave us a non-UTF8 C string")]
48     BadCString(#[from] Utf8Error),
49     /// read callback returned an error
50     #[error("Callback failed")]
51     CallbackError(#[from] anyhow::Error),
52     /// Failure in setting the system property
53     #[error("__system_property_set failed.")]
54     SetPropertyFailed,
55 }
56 
57 /// Result type specific for this crate.
58 pub type Result<T> = std::result::Result<T, PropertyWatcherError>;
59 
60 /// PropertyWatcher takes the name of an Android system property such
61 /// as `keystore.boot_level`; it can report the current value of this
62 /// property, or wait for it to change.
63 pub struct PropertyWatcher {
64     prop_name: CString,
65     prop_info: *const PropInfo,
66     serial: keystore2_system_property_bindgen::__uint32_t,
67 }
68 
69 impl PropertyWatcher {
70     /// Create a PropertyWatcher for the named system property.
new(name: &str) -> Result<Self>71     pub fn new(name: &str) -> Result<Self> {
72         Ok(Self { prop_name: CString::new(name)?, prop_info: null(), serial: 0 })
73     }
74 
75     // Lazy-initializing accessor for self.prop_info.
get_prop_info(&mut self) -> Option<*const PropInfo>76     fn get_prop_info(&mut self) -> Option<*const PropInfo> {
77         if self.prop_info.is_null() {
78             // Unsafe required for FFI call. Input and output are both const.
79             // The returned pointer is valid for the lifetime of the program.
80             self.prop_info = unsafe {
81                 keystore2_system_property_bindgen::__system_property_find(self.prop_name.as_ptr())
82             };
83         }
84         if self.prop_info.is_null() {
85             None
86         } else {
87             Some(self.prop_info)
88         }
89     }
90 
read_raw(prop_info: *const PropInfo, mut f: impl FnOnce(Option<&CStr>, Option<&CStr>))91     fn read_raw(prop_info: *const PropInfo, mut f: impl FnOnce(Option<&CStr>, Option<&CStr>)) {
92         // Unsafe function converts values passed to us by
93         // __system_property_read_callback to Rust form
94         // and pass them to inner callback.
95         unsafe extern "C" fn callback(
96             res_p: *mut c_void,
97             name: *const c_char,
98             value: *const c_char,
99             _: keystore2_system_property_bindgen::__uint32_t,
100         ) {
101             let name = if name.is_null() { None } else { Some(CStr::from_ptr(name)) };
102             let value = if value.is_null() { None } else { Some(CStr::from_ptr(value)) };
103             let f = &mut *res_p.cast::<&mut dyn FnMut(Option<&CStr>, Option<&CStr>)>();
104             f(name, value);
105         }
106 
107         let mut f: &mut dyn FnOnce(Option<&CStr>, Option<&CStr>) = &mut f;
108 
109         // Unsafe block for FFI call. We convert the FnOnce
110         // to a void pointer, and unwrap it in our callback.
111         unsafe {
112             keystore2_system_property_bindgen::__system_property_read_callback(
113                 prop_info,
114                 Some(callback),
115                 &mut f as *mut _ as *mut c_void,
116             )
117         }
118     }
119 
120     /// Call the passed function, passing it the name and current value
121     /// of this system property. See documentation for
122     /// `__system_property_read_callback` for details.
123     /// Returns an error if the property is empty or doesn't exist.
read<T, F>(&mut self, f: F) -> Result<T> where F: FnOnce(&str, &str) -> anyhow::Result<T>,124     pub fn read<T, F>(&mut self, f: F) -> Result<T>
125     where
126         F: FnOnce(&str, &str) -> anyhow::Result<T>,
127     {
128         let prop_info = self.get_prop_info().ok_or(PropertyWatcherError::SystemPropertyAbsent)?;
129         let mut result = Err(PropertyWatcherError::ReadCallbackNotCalled);
130         Self::read_raw(prop_info, |name, value| {
131             // use a wrapping closure as an erzatz try block.
132             result = (|| {
133                 let name = name.ok_or(PropertyWatcherError::MissingCString)?.to_str()?;
134                 let value = value.ok_or(PropertyWatcherError::MissingCString)?.to_str()?;
135                 f(name, value).map_err(PropertyWatcherError::CallbackError)
136             })()
137         });
138         result
139     }
140 
141     // Waits for the property that self is watching to be created. Returns immediately if the
142     // property already exists.
wait_for_property_creation(&mut self) -> Result<()>143     fn wait_for_property_creation(&mut self) -> Result<()> {
144         let mut global_serial = 0;
145         loop {
146             match self.get_prop_info() {
147                 Some(_) => return Ok(()),
148                 None => {
149                     // Unsafe call for FFI. The function modifies only global_serial, and has
150                     // no side-effects.
151                     if !unsafe {
152                         // Wait for a global serial number change, then try again. On success,
153                         // the function will update global_serial with the last version seen.
154                         keystore2_system_property_bindgen::__system_property_wait(
155                             null(),
156                             global_serial,
157                             &mut global_serial,
158                             null(),
159                         )
160                     } {
161                         return Err(PropertyWatcherError::WaitFailed);
162                     }
163                 }
164             }
165         }
166     }
167 
168     /// Wait for the system property to change. This
169     /// records the serial number of the last change, so
170     /// race conditions are avoided.
wait(&mut self) -> Result<()>171     pub fn wait(&mut self) -> Result<()> {
172         // If the property is null, then wait for it to be created. Subsequent waits will
173         // skip this step and wait for our specific property to change.
174         if self.prop_info.is_null() {
175             return self.wait_for_property_creation();
176         }
177 
178         let mut new_serial = self.serial;
179         // Unsafe block to call __system_property_wait.
180         // All arguments are private to PropertyWatcher so we
181         // can be confident they are valid.
182         if !unsafe {
183             keystore2_system_property_bindgen::__system_property_wait(
184                 self.prop_info,
185                 self.serial,
186                 &mut new_serial,
187                 null(),
188             )
189         } {
190             return Err(PropertyWatcherError::WaitFailed);
191         }
192         self.serial = new_serial;
193         Ok(())
194     }
195 }
196 
197 /// Writes a system property.
write(name: &str, value: &str) -> AnyhowResult<()>198 pub fn write(name: &str, value: &str) -> AnyhowResult<()> {
199     if
200     // Unsafe required for FFI call. Input and output are both const and valid strings.
201     unsafe {
202         // If successful, __system_property_set returns 0, otherwise, returns -1.
203         keystore2_system_property_bindgen::__system_property_set(
204             CString::new(name)
205                 .context("In keystore2::system_property::write: Construction CString from name.")?
206                 .as_ptr(),
207             CString::new(value)
208                 .context("In keystore2::system_property::write: Constructing CString from value.")?
209                 .as_ptr(),
210         )
211     } == 0
212     {
213         Ok(())
214     } else {
215         Err(anyhow!(PropertyWatcherError::SetPropertyFailed))
216     }
217 }
218