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