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