blob: a8f2f29447edd9bfa83d2d723aace229ddb60eae [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
18use anyhow::{anyhow, Context, Result as AnyhowResult};
19use system_properties_bindgen::prop_info as PropInfo;
20use std::os::raw::c_char;
21use std::ptr::null;
22use std::{
23 ffi::{c_void, CStr, CString},
24 str::Utf8Error,
25};
26use 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,
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.
58pub 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.
63pub struct PropertyWatcher {
64 prop_name: CString,
65 prop_info: *const PropInfo,
66 serial: system_properties_bindgen::__uint32_t,
67}
68
69impl PropertyWatcher {
70 /// Create a PropertyWatcher for the named system property.
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.
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 system_properties_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
Matthew Maurer6b25cb22021-11-09 13:48:16 -080091 fn read_raw(prop_info: *const PropInfo, mut f: impl FnMut(Option<&CStr>, Option<&CStr>)) {
Joel Galensona11c55f2021-07-30 07:36:06 -070092 // 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 _: system_properties_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
Matthew Maurer6b25cb22021-11-09 13:48:16 -0800107 let mut f: &mut dyn FnMut(Option<&CStr>, Option<&CStr>) = &mut f;
Joel Galensona11c55f2021-07-30 07:36:06 -0700108
Matthew Maurer6b25cb22021-11-09 13:48:16 -0800109 // Unsafe block for FFI call. We convert the FnMut
Joel Galensona11c55f2021-07-30 07:36:06 -0700110 // to a void pointer, and unwrap it in our callback.
111 unsafe {
112 system_properties_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.
Matthew Maurer6b25cb22021-11-09 13:48:16 -0800124 pub fn read<T, F>(&mut self, mut f: F) -> Result<T>
Joel Galensona11c55f2021-07-30 07:36:06 -0700125 where
Matthew Maurer6b25cb22021-11-09 13:48:16 -0800126 F: FnMut(&str, &str) -> anyhow::Result<T>,
Joel Galensona11c55f2021-07-30 07:36:06 -0700127 {
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.
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 system_properties_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.
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 system_properties_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/// Reads a system property.
198pub fn read(name: &str) -> AnyhowResult<String> {
199 PropertyWatcher::new(name)
200 .context("Failed to create a PropertyWatcher.")?
201 .read(|_name, value| Ok(value.to_owned()))
202 .with_context(|| format!("Failed to read the system property {}.", name))
203}
204
205/// Writes a system property.
206pub fn write(name: &str, value: &str) -> AnyhowResult<()> {
207 if
208 // Unsafe required for FFI call. Input and output are both const and valid strings.
209 unsafe {
210 // If successful, __system_property_set returns 0, otherwise, returns -1.
211 system_properties_bindgen::__system_property_set(
212 CString::new(name)
213 .context("Failed to construct CString from name.")?
214 .as_ptr(),
215 CString::new(value)
216 .context("Failed to construct CString from value.")?
217 .as_ptr(),
218 )
219 } == 0
220 {
221 Ok(())
222 } else {
223 Err(anyhow!(PropertyWatcherError::SetPropertyFailed))
224 }
225}