blob: b993c879def88fa0b5ca7714f27103c5eddca8b4 [file] [log] [blame]
Paul Crowley7c57bf12021-02-02 16:26:57 -08001// 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
Hasini Gunasinghe365ce372021-07-02 23:13:11 +000018use anyhow::{anyhow, Context, Result as AnyhowResult};
Seth Moore71940352021-04-14 23:03:56 +000019use keystore2_system_property_bindgen::prop_info as PropInfo;
Paul Crowley7c57bf12021-02-02 16:26:57 -080020use std::os::raw::c_char;
Seth Moore71940352021-04-14 23:03:56 +000021use std::ptr::null;
Paul Crowley7c57bf12021-02-02 16:26:57 -080022use 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),
Hasini Gunasinghe365ce372021-07-02 23:13:11 +000052 /// Failure in setting the system property
53 #[error("__system_property_set failed.")]
54 SetPropertyFailed,
Paul Crowley7c57bf12021-02-02 16:26:57 -080055}
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 {
Seth Moore71940352021-04-14 23:03:56 +000064 prop_name: CString,
65 prop_info: *const PropInfo,
Paul Crowley7c57bf12021-02-02 16:26:57 -080066 serial: keystore2_system_property_bindgen::__uint32_t,
67}
68
69impl PropertyWatcher {
70 /// Create a PropertyWatcher for the named system property.
71 pub fn new(name: &str) -> Result<Self> {
Seth Moore71940352021-04-14 23:03:56 +000072 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 keystore2_system_property_bindgen::__system_property_find(self.prop_name.as_ptr())
82 };
83 }
84 if self.prop_info.is_null() {
85 None
Paul Crowley7c57bf12021-02-02 16:26:57 -080086 } else {
Seth Moore71940352021-04-14 23:03:56 +000087 Some(self.prop_info)
Paul Crowley7c57bf12021-02-02 16:26:57 -080088 }
89 }
90
Seth Moore71940352021-04-14 23:03:56 +000091 fn read_raw(prop_info: *const PropInfo, mut f: impl FnOnce(Option<&CStr>, Option<&CStr>)) {
Paul Crowley7c57bf12021-02-02 16:26:57 -080092 // 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(
Seth Moore71940352021-04-14 23:03:56 +0000113 prop_info,
Paul Crowley7c57bf12021-02-02 16:26:57 -0800114 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.
Seth Moore71940352021-04-14 23:03:56 +0000123 /// Returns an error if the property is empty or doesn't exist.
124 pub fn read<T, F>(&mut self, f: F) -> Result<T>
Paul Crowley7c57bf12021-02-02 16:26:57 -0800125 where
126 F: FnOnce(&str, &str) -> anyhow::Result<T>,
127 {
Seth Moore71940352021-04-14 23:03:56 +0000128 let prop_info = self.get_prop_info().ok_or(PropertyWatcherError::SystemPropertyAbsent)?;
Paul Crowley7c57bf12021-02-02 16:26:57 -0800129 let mut result = Err(PropertyWatcherError::ReadCallbackNotCalled);
Seth Moore71940352021-04-14 23:03:56 +0000130 Self::read_raw(prop_info, |name, value| {
Paul Crowley7c57bf12021-02-02 16:26:57 -0800131 // 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
Seth Moore71940352021-04-14 23:03:56 +0000141 // 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 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
Paul Crowley7c57bf12021-02-02 16:26:57 -0800168 /// 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<()> {
Seth Moore71940352021-04-14 23:03:56 +0000172 // 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
Paul Crowley7c57bf12021-02-02 16:26:57 -0800178 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,
Seth Moore71940352021-04-14 23:03:56 +0000187 null(),
Paul Crowley7c57bf12021-02-02 16:26:57 -0800188 )
189 } {
190 return Err(PropertyWatcherError::WaitFailed);
191 }
192 self.serial = new_serial;
193 Ok(())
194 }
195}
Hasini Gunasinghe365ce372021-07-02 23:13:11 +0000196
197/// Writes a system property.
198pub 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}