1 use log::LevelFilter;
2 use serde_json::{Map, Value};
3 use std::convert::TryInto;
4 use std::path::Path;
5
6 // Directory for Bluetooth hci devices
7 pub const HCI_DEVICES_DIR: &str = "/sys/class/bluetooth";
8
9 // File to store the Bluetooth daemon to use (bluez or floss)
10 const BLUETOOTH_DAEMON_CURRENT: &str = "/var/lib/bluetooth/bluetooth-daemon.current";
11
12 // File to store the config for BluetoothManager
13 const BTMANAGERD_CONF: &str = "/var/lib/bluetooth/btmanagerd.json";
14
15 /// Folder to keep files which override floss configuration
16 const FLOSS_SYSPROPS_OVERRIDE_DIR: &str = "/var/lib/bluetooth/sysprops.conf.d";
17
18 /// File to store bluetooth devcoredump configuration. This file is used by the
19 /// udev rule to copy the devcoredump state to .../device/coredump_enabled sysfs
20 /// entry. It is also used by the user-space crash reporter to enable/disable
21 /// parsing of the devcoredump.
22 const FLOSS_COREDUMP_CONF_PATH: &str = "/var/run/bluetooth/coredump_disabled";
23
24 /// Key used for default adapter entry.
25 const DEFAULT_ADAPTER_KEY: &str = "default_adapter";
26
27 /// In the absence of other values, default to hci0.
28 const DEFAULT_ADAPTER_VALUE: i32 = 0;
29
is_floss_enabled() -> bool30 pub fn is_floss_enabled() -> bool {
31 match std::fs::read(BLUETOOTH_DAEMON_CURRENT) {
32 Ok(v) => {
33 let content = std::str::from_utf8(&v);
34 match content {
35 Ok(version) => version.contains("floss"),
36 Err(_) => false,
37 }
38 }
39 Err(_) => false,
40 }
41 }
42
write_floss_enabled(enabled: bool) -> bool43 pub fn write_floss_enabled(enabled: bool) -> bool {
44 std::fs::write(
45 BLUETOOTH_DAEMON_CURRENT,
46 match enabled {
47 true => "floss",
48 _ => "bluez",
49 },
50 )
51 .is_ok()
52 }
53
read_config() -> std::io::Result<String>54 pub fn read_config() -> std::io::Result<String> {
55 std::fs::read_to_string(BTMANAGERD_CONF)
56 }
57
get_log_level() -> Option<LevelFilter>58 pub fn get_log_level() -> Option<LevelFilter> {
59 get_log_level_internal(read_config().ok()?)
60 }
61
get_log_level_internal(config: String) -> Option<LevelFilter>62 fn get_log_level_internal(config: String) -> Option<LevelFilter> {
63 serde_json::from_str::<Value>(config.as_str())
64 .ok()?
65 .get("log_level")?
66 .as_str()?
67 .parse::<LevelFilter>()
68 .ok()
69 }
70
71 /// Returns whether hci N is enabled in config; defaults to true.
is_hci_n_enabled(n: i32) -> bool72 pub fn is_hci_n_enabled(n: i32) -> bool {
73 match read_config().ok().and_then(|config| is_hci_n_enabled_internal(config, n)) {
74 Some(v) => v,
75 _ => true,
76 }
77 }
78
is_hci_n_enabled_internal(config: String, n: i32) -> Option<bool>79 fn is_hci_n_enabled_internal(config: String, n: i32) -> Option<bool> {
80 serde_json::from_str::<Value>(config.as_str())
81 .ok()?
82 .get(format!("hci{}", n))?
83 .as_object()?
84 .get("enabled")?
85 .as_bool()
86 }
87
88 // When we initialize BluetoothManager, we need to make sure the file is a well-formatted json.
fix_config_file_format() -> bool89 pub fn fix_config_file_format() -> bool {
90 match read_config() {
91 Ok(s) => match serde_json::from_str::<Value>(s.as_str()) {
92 Ok(_) => true,
93 _ => std::fs::write(BTMANAGERD_CONF, "{}").is_ok(),
94 },
95 _ => std::fs::write(BTMANAGERD_CONF, "{}").is_ok(),
96 }
97 }
98
modify_hci_n_enabled(n: i32, enabled: bool) -> bool99 pub fn modify_hci_n_enabled(n: i32, enabled: bool) -> bool {
100 if !fix_config_file_format() {
101 false
102 } else {
103 match read_config()
104 .ok()
105 .and_then(|config| modify_hci_n_enabled_internal(config, n, enabled))
106 {
107 Some(s) => std::fs::write(BTMANAGERD_CONF, s).is_ok(),
108 _ => false,
109 }
110 }
111 }
112
modify_hci_n_enabled_internal(config: String, n: i32, enabled: bool) -> Option<String>113 fn modify_hci_n_enabled_internal(config: String, n: i32, enabled: bool) -> Option<String> {
114 let hci_interface = format!("hci{}", n);
115 let mut o = serde_json::from_str::<Value>(config.as_str()).ok()?;
116 match o.get_mut(hci_interface.clone()) {
117 Some(section) => {
118 section.as_object_mut()?.insert("enabled".to_string(), Value::Bool(enabled));
119 serde_json::ser::to_string_pretty(&o).ok()
120 }
121 _ => {
122 let mut entry_map = Map::new();
123 entry_map.insert("enabled".to_string(), Value::Bool(enabled));
124 o.as_object_mut()?.insert(hci_interface, Value::Object(entry_map));
125 serde_json::ser::to_string_pretty(&o).ok()
126 }
127 }
128 }
129
get_default_adapter() -> i32130 pub fn get_default_adapter() -> i32 {
131 match read_config().ok().and_then(|config| {
132 serde_json::from_str::<Value>(config.as_str()).ok()?.get(DEFAULT_ADAPTER_KEY)?.as_i64()
133 }) {
134 Some(v) => v.try_into().unwrap_or(DEFAULT_ADAPTER_VALUE),
135 None => DEFAULT_ADAPTER_VALUE,
136 }
137 }
138
set_default_adapter(adapter: i32) -> bool139 pub fn set_default_adapter(adapter: i32) -> bool {
140 match read_config().ok().and_then(|config| {
141 let mut cfg = serde_json::from_str::<Value>(config.as_str()).ok()?;
142 cfg[DEFAULT_ADAPTER_KEY] = serde_json::to_value(adapter).ok().unwrap();
143 serde_json::ser::to_string_pretty(&cfg).ok()
144 }) {
145 Some(s) => std::fs::write(BTMANAGERD_CONF, s).is_ok(),
146 None => false,
147 }
148 }
149
list_hci_devices() -> Vec<i32>150 pub fn list_hci_devices() -> Vec<i32> {
151 hci_devices_string_to_int(list_hci_devices_string())
152 }
153
list_hci_devices_string() -> Vec<String>154 fn list_hci_devices_string() -> Vec<String> {
155 match std::fs::read_dir(HCI_DEVICES_DIR) {
156 Ok(entries) => entries
157 .map(|e| e.unwrap().path().file_name().unwrap().to_str().unwrap().to_string())
158 .collect::<Vec<_>>(),
159 _ => Vec::new(),
160 }
161 }
162
163 /// Check whether a certain hci device exists in sysfs.
check_hci_device_exists(hci: i32) -> bool164 pub fn check_hci_device_exists(hci: i32) -> bool {
165 Path::new(format!("{}/hci{}", HCI_DEVICES_DIR, hci).as_str()).exists()
166 }
167
168 /// Get the devpath for a given hci index. This gives a stable path that can be
169 /// used to identify a device even as the hci index fluctuates.
get_devpath_for_hci(hci: i32) -> Option<String>170 pub fn get_devpath_for_hci(hci: i32) -> Option<String> {
171 match std::fs::canonicalize(format!("{}/hci{}/device", HCI_DEVICES_DIR, hci).as_str()) {
172 Ok(p) => Some(p.into_os_string().into_string().ok()?),
173 Err(e) => {
174 log::debug!("Failed to get devpath for hci{} with error: {}", hci, e);
175 None
176 }
177 }
178 }
179
hci_devices_string_to_int(devices: Vec<String>) -> Vec<i32>180 fn hci_devices_string_to_int(devices: Vec<String>) -> Vec<i32> {
181 devices
182 .into_iter()
183 .filter_map(|e| if e.starts_with("hci") { e[3..].parse::<i32>().ok() } else { None })
184 .collect()
185 }
186
list_pid_files(pid_dir: &str) -> Vec<String>187 pub fn list_pid_files(pid_dir: &str) -> Vec<String> {
188 match std::fs::read_dir(pid_dir) {
189 Ok(entries) => entries
190 .map(|e| e.unwrap().path().file_name().unwrap().to_str().unwrap().to_string())
191 .collect::<Vec<_>>(),
192 _ => Vec::new(),
193 }
194 }
195
196 /// Calls the reset sysfs entry for an hci device. Returns True if the write succeeds.
reset_hci_device(hci: i32) -> bool197 pub fn reset_hci_device(hci: i32) -> bool {
198 let path = format!("/sys/class/bluetooth/hci{}/reset", hci);
199 std::fs::write(path, "1").is_ok()
200 }
201
read_floss_ll_privacy_enabled() -> std::io::Result<bool>202 pub fn read_floss_ll_privacy_enabled() -> std::io::Result<bool> {
203 let parent = Path::new(FLOSS_SYSPROPS_OVERRIDE_DIR);
204 if !parent.is_dir() {
205 return Ok(false);
206 }
207
208 let data = std::fs::read_to_string(format!(
209 "{}/{}",
210 FLOSS_SYSPROPS_OVERRIDE_DIR, "privacy_override.conf"
211 ))?;
212
213 Ok(data == "[Sysprops]\nbluetooth.core.gap.le.privacy.enabled=true\n")
214 }
215
write_floss_ll_privacy_enabled(enabled: bool) -> std::io::Result<()>216 pub fn write_floss_ll_privacy_enabled(enabled: bool) -> std::io::Result<()> {
217 let parent = Path::new(FLOSS_SYSPROPS_OVERRIDE_DIR);
218
219 std::fs::create_dir_all(parent)?;
220
221 let data = format!(
222 "[Sysprops]\nbluetooth.core.gap.le.privacy.enabled={}",
223 if enabled { "true\n" } else { "false\n" }
224 );
225
226 std::fs::write(format!("{}/{}", FLOSS_SYSPROPS_OVERRIDE_DIR, "privacy_override.conf"), data)
227 }
228
set_adapter_coredump_state(enabled: bool) -> std::io::Result<()>229 pub fn set_adapter_coredump_state(enabled: bool) -> std::io::Result<()> {
230 let data = format!("{}\n", !enabled as i32);
231
232 for hci in list_hci_devices_string() {
233 let path = format!("{}/{}/device/coredump_disabled", HCI_DEVICES_DIR, hci);
234
235 std::fs::write(path, &data)?;
236 }
237
238 Ok(())
239 }
240
write_coredump_state_to_file(enabled: bool) -> bool241 pub fn write_coredump_state_to_file(enabled: bool) -> bool {
242 let data = format!("{}\n", !enabled as i32);
243
244 if let Err(e) = std::fs::write(FLOSS_COREDUMP_CONF_PATH, data) {
245 log::error!("Failed to write devcoredump state (error: {})", e);
246 return false;
247 }
248
249 if let Err(e) = set_adapter_coredump_state(enabled) {
250 log::error!("Failed to set devcoredump state (error: {})", e);
251 return false;
252 }
253
254 true
255 }
256
257 #[cfg(test)]
258 mod tests {
259 use super::*;
260
is_hci_n_enabled_internal_wrapper(config: String, n: i32) -> bool261 fn is_hci_n_enabled_internal_wrapper(config: String, n: i32) -> bool {
262 is_hci_n_enabled_internal(config, n).or(Some(true)).unwrap()
263 }
264
265 #[test]
parse_log_level()266 fn parse_log_level() {
267 assert_eq!(
268 get_log_level_internal("{\"log_level\": \"error\"}".to_string()).unwrap(),
269 LevelFilter::Error
270 );
271 assert_eq!(
272 get_log_level_internal("{\"log_level\": \"warn\"}".to_string()).unwrap(),
273 LevelFilter::Warn
274 );
275 assert_eq!(
276 get_log_level_internal("{\"log_level\": \"info\"}".to_string()).unwrap(),
277 LevelFilter::Info
278 );
279 assert_eq!(
280 get_log_level_internal("{\"log_level\": \"debug\"}".to_string()).unwrap(),
281 LevelFilter::Debug
282 );
283 assert_eq!(
284 get_log_level_internal("{\"log_level\": \"trace\"}".to_string()).unwrap(),
285 LevelFilter::Trace
286 );
287 assert_eq!(
288 get_log_level_internal("{\"log_level\": \"random\"}".to_string()).is_none(),
289 true
290 );
291 }
292
293 #[test]
parse_hci0_enabled()294 fn parse_hci0_enabled() {
295 assert_eq!(
296 is_hci_n_enabled_internal_wrapper("{\"hci0\":\n{\"enabled\": true}}".to_string(), 0),
297 true
298 );
299 }
300
301 #[test]
modify_hci0_enabled()302 fn modify_hci0_enabled() {
303 let modified_string =
304 modify_hci_n_enabled_internal("{\"hci0\":\n{\"enabled\": false}}".to_string(), 0, true)
305 .unwrap();
306 assert_eq!(is_hci_n_enabled_internal_wrapper(modified_string, 0), true);
307 }
308
309 #[test]
modify_hci0_enabled_from_empty()310 fn modify_hci0_enabled_from_empty() {
311 let modified_string = modify_hci_n_enabled_internal("{}".to_string(), 0, true).unwrap();
312 assert_eq!(is_hci_n_enabled_internal_wrapper(modified_string, 0), true);
313 }
314
315 #[test]
parse_hci0_not_enabled()316 fn parse_hci0_not_enabled() {
317 assert_eq!(
318 is_hci_n_enabled_internal_wrapper("{\"hci0\":\n{\"enabled\": false}}".to_string(), 0),
319 false
320 );
321 }
322
323 #[test]
parse_hci1_not_present()324 fn parse_hci1_not_present() {
325 assert_eq!(
326 is_hci_n_enabled_internal_wrapper("{\"hci0\":\n{\"enabled\": true}}".to_string(), 1),
327 true
328 );
329 }
330
331 #[test]
test_hci_devices_string_to_int_none()332 fn test_hci_devices_string_to_int_none() {
333 assert_eq!(hci_devices_string_to_int(vec!["somethingelse".to_string()]), Vec::<i32>::new());
334 }
335
336 #[test]
test_hci_devices_string_to_int_one()337 fn test_hci_devices_string_to_int_one() {
338 assert_eq!(hci_devices_string_to_int(vec!["hci0".to_string()]), vec![0]);
339 }
340
341 #[test]
test_hci_devices_string_to_int_two()342 fn test_hci_devices_string_to_int_two() {
343 assert_eq!(
344 hci_devices_string_to_int(vec!["hci0".to_string(), "hci1".to_string()]),
345 vec![0, 1]
346 );
347 }
348 }
349