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