blob: a1f48352fd66df5e284a1bdfcf8e042daa45312a [file] [log] [blame]
Joel Galensone1f1e792021-07-20 12:49:59 -07001/*
2 * Copyright (C) 2021 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17//! This crate provides the PropertyWatcher type, which watches for changes
18//! in Android system properties.
19
20use anyhow::{anyhow, Context, Result as AnyhowResult};
21use system_properties_bindgen::prop_info as PropInfo;
22use std::os::raw::c_char;
23use std::ptr::null;
24use std::{
25 ffi::{c_void, CStr, CString},
26 str::Utf8Error,
27};
28use thiserror::Error;
29
30/// Errors this crate can generate
31#[derive(Error, Debug)]
32pub enum PropertyWatcherError {
33 /// We can't watch for a property whose name contains a NUL character.
34 #[error("Cannot convert name to C string")]
35 BadNameError(#[from] std::ffi::NulError),
36 /// We can only watch for properties that exist when the watcher is created.
37 #[error("System property is absent")]
38 SystemPropertyAbsent,
39 /// __system_property_wait timed out despite being given no timeout.
40 #[error("Wait failed")]
41 WaitFailed,
42 /// read callback was not called
43 #[error("__system_property_read_callback did not call callback")]
44 ReadCallbackNotCalled,
45 /// read callback gave us a NULL pointer
46 #[error("__system_property_read_callback gave us a NULL pointer instead of a string")]
47 MissingCString,
48 /// read callback gave us a bad C string
49 #[error("__system_property_read_callback gave us a non-UTF8 C string")]
50 BadCString(#[from] Utf8Error),
51 /// read callback returned an error
52 #[error("Callback failed")]
53 CallbackError(#[from] anyhow::Error),
54 /// Failure in setting the system property
55 #[error("__system_property_set failed.")]
56 SetPropertyFailed,
57}
58
59/// Result type specific for this crate.
60pub type Result<T> = std::result::Result<T, PropertyWatcherError>;
61
62/// PropertyWatcher takes the name of an Android system property such
63/// as `keystore.boot_level`; it can report the current value of this
64/// property, or wait for it to change.
65pub struct PropertyWatcher {
66 prop_name: CString,
67 prop_info: *const PropInfo,
68 serial: system_properties_bindgen::__uint32_t,
69}
70
71impl PropertyWatcher {
72 /// Create a PropertyWatcher for the named system property.
73 pub fn new(name: &str) -> Result<Self> {
74 Ok(Self { prop_name: CString::new(name)?, prop_info: null(), serial: 0 })
75 }
76
77 // Lazy-initializing accessor for self.prop_info.
78 fn get_prop_info(&mut self) -> Option<*const PropInfo> {
79 if self.prop_info.is_null() {
80 // Unsafe required for FFI call. Input and output are both const.
81 // The returned pointer is valid for the lifetime of the program.
82 self.prop_info = unsafe {
83 system_properties_bindgen::__system_property_find(self.prop_name.as_ptr())
84 };
85 }
86 if self.prop_info.is_null() {
87 None
88 } else {
89 Some(self.prop_info)
90 }
91 }
92
93 fn read_raw(prop_info: *const PropInfo, mut f: impl FnOnce(Option<&CStr>, Option<&CStr>)) {
94 // Unsafe function converts values passed to us by
95 // __system_property_read_callback to Rust form
96 // and pass them to inner callback.
97 unsafe extern "C" fn callback(
98 res_p: *mut c_void,
99 name: *const c_char,
100 value: *const c_char,
101 _: system_properties_bindgen::__uint32_t,
102 ) {
103 let name = if name.is_null() { None } else { Some(CStr::from_ptr(name)) };
104 let value = if value.is_null() { None } else { Some(CStr::from_ptr(value)) };
105 let f = &mut *res_p.cast::<&mut dyn FnMut(Option<&CStr>, Option<&CStr>)>();
106 f(name, value);
107 }
108
109 let mut f: &mut dyn FnOnce(Option<&CStr>, Option<&CStr>) = &mut f;
110
111 // Unsafe block for FFI call. We convert the FnOnce
112 // to a void pointer, and unwrap it in our callback.
113 unsafe {
114 system_properties_bindgen::__system_property_read_callback(
115 prop_info,
116 Some(callback),
117 &mut f as *mut _ as *mut c_void,
118 )
119 }
120 }
121
122 /// Call the passed function, passing it the name and current value
123 /// of this system property. See documentation for
124 /// `__system_property_read_callback` for details.
125 /// Returns an error if the property is empty or doesn't exist.
126 pub fn read<T, F>(&mut self, f: F) -> Result<T>
127 where
128 F: FnOnce(&str, &str) -> anyhow::Result<T>,
129 {
130 let prop_info = self.get_prop_info().ok_or(PropertyWatcherError::SystemPropertyAbsent)?;
131 let mut result = Err(PropertyWatcherError::ReadCallbackNotCalled);
132 Self::read_raw(prop_info, |name, value| {
133 // use a wrapping closure as an erzatz try block.
134 result = (|| {
135 let name = name.ok_or(PropertyWatcherError::MissingCString)?.to_str()?;
136 let value = value.ok_or(PropertyWatcherError::MissingCString)?.to_str()?;
137 f(name, value).map_err(PropertyWatcherError::CallbackError)
138 })()
139 });
140 result
141 }
142
143 // Waits for the property that self is watching to be created. Returns immediately if the
144 // property already exists.
145 fn wait_for_property_creation(&mut self) -> Result<()> {
146 let mut global_serial = 0;
147 loop {
148 match self.get_prop_info() {
149 Some(_) => return Ok(()),
150 None => {
151 // Unsafe call for FFI. The function modifies only global_serial, and has
152 // no side-effects.
153 if !unsafe {
154 // Wait for a global serial number change, then try again. On success,
155 // the function will update global_serial with the last version seen.
156 system_properties_bindgen::__system_property_wait(
157 null(),
158 global_serial,
159 &mut global_serial,
160 null(),
161 )
162 } {
163 return Err(PropertyWatcherError::WaitFailed);
164 }
165 }
166 }
167 }
168 }
169
170 /// Wait for the system property to change. This
171 /// records the serial number of the last change, so
172 /// race conditions are avoided.
173 pub fn wait(&mut self) -> Result<()> {
174 // If the property is null, then wait for it to be created. Subsequent waits will
175 // skip this step and wait for our specific property to change.
176 if self.prop_info.is_null() {
177 return self.wait_for_property_creation();
178 }
179
180 let mut new_serial = self.serial;
181 // Unsafe block to call __system_property_wait.
182 // All arguments are private to PropertyWatcher so we
183 // can be confident they are valid.
184 if !unsafe {
185 system_properties_bindgen::__system_property_wait(
186 self.prop_info,
187 self.serial,
188 &mut new_serial,
189 null(),
190 )
191 } {
192 return Err(PropertyWatcherError::WaitFailed);
193 }
194 self.serial = new_serial;
195 Ok(())
196 }
197}
198
199/// Writes a system property.
200pub fn write(name: &str, value: &str) -> AnyhowResult<()> {
201 if
202 // Unsafe required for FFI call. Input and output are both const and valid strings.
203 unsafe {
204 // If successful, __system_property_set returns 0, otherwise, returns -1.
205 system_properties_bindgen::__system_property_set(
206 CString::new(name)
207 .context("In keystore2::system_property::write: Construction CString from name.")?
208 .as_ptr(),
209 CString::new(value)
210 .context("In keystore2::system_property::write: Constructing CString from value.")?
211 .as_ptr(),
212 )
213 } == 0
214 {
215 Ok(())
216 } else {
217 Err(anyhow!(PropertyWatcherError::SetPropertyFailed))
218 }
219}