1 // The manager binary (btmanagerd) is a fairly barebone bin file that depends on the manager_service
2 // library which implements most of the logic. The code is separated in this way so that we can
3 // apply certain linker flags (which is applied to the library but not the binary).
4 // Please keep main.rs logic light and write the heavy logic in the manager_service library instead.
5
6 use clap::{App, Arg};
7 use dbus::channel::MatchingReceiver;
8 use dbus::message::MatchRule;
9 use dbus_crossroads::Crossroads;
10 use dbus_projection::DisconnectWatcher;
11 use dbus_tokio::connection;
12 use log::LevelFilter;
13 use manager_service::bluetooth_manager::BluetoothManager;
14 use manager_service::powerd_suspend_manager::PowerdSuspendManager;
15 use manager_service::{bluetooth_experimental_dbus, iface_bluetooth_manager};
16 use manager_service::{bluetooth_manager_dbus, config_util, state_machine};
17 use std::sync::{Arc, Mutex};
18 use syslog::{BasicLogger, Facility, Formatter3164};
19
20 #[tokio::main]
main() -> Result<(), Box<dyn std::error::Error>>21 pub async fn main() -> Result<(), Box<dyn std::error::Error>> {
22 let matches = App::new("Bluetooth Manager")
23 .arg(Arg::with_name("systemd").long("systemd").help("If btadapterd uses systemd init"))
24 .arg(Arg::with_name("debug").long("debug").short("d").help("Enables debug level logs"))
25 .arg(
26 Arg::with_name("log-output")
27 .long("log-output")
28 .takes_value(true)
29 .possible_values(&["syslog", "stderr"])
30 .default_value("syslog")
31 .help("Select log output"),
32 )
33 .get_matches();
34
35 let is_debug = matches.is_present("debug");
36 let is_systemd = matches.is_present("systemd");
37
38 let level_filter = if is_debug { LevelFilter::Debug } else { LevelFilter::Info };
39
40 let log_output = matches.value_of("log-output").unwrap_or("syslog");
41
42 if log_output == "stderr" {
43 env_logger::Builder::new().filter(None, level_filter).init();
44 } else {
45 // syslog is the default log output.
46 let formatter = Formatter3164 {
47 facility: Facility::LOG_USER,
48 hostname: None,
49 process: "btmanagerd".into(),
50 pid: 0,
51 };
52
53 let logger = syslog::unix(formatter).expect("could not connect to syslog");
54 let _ = log::set_boxed_logger(Box::new(BasicLogger::new(logger)))
55 .map(|()| log::set_max_level(config_util::get_log_level().unwrap_or(level_filter)));
56 }
57
58 // Initialize config util
59 config_util::fix_config_file_format();
60
61 // Connect to the D-Bus system bus (this is blocking, unfortunately).
62 let (resource, conn) = connection::new_system_sync()?;
63
64 // There are multiple signal handlers. We need to set signal match mode to true to allow signal
65 // handlers process the signals independently. Otherwise only the first signal handler will get
66 // the chance to handle the same signal in case the match rule overlaps.
67 conn.set_signal_match_mode(true);
68
69 // Determine whether to use upstart or systemd
70 let invoker = if is_systemd {
71 state_machine::Invoker::SystemdInvoker
72 } else {
73 state_machine::Invoker::UpstartInvoker
74 };
75
76 let context = state_machine::create_new_state_machine_context(invoker);
77 let proxy = context.get_proxy();
78
79 // The resource is a task that should be spawned onto a tokio compatible
80 // reactor ASAP. If the resource ever finishes, you lost connection to D-Bus.
81 tokio::spawn(async {
82 let err = resource.await;
83 panic!("Lost connection to D-Bus: {}", err);
84 });
85
86 // Let's request a name on the bus, so that clients can find us.
87 conn.request_name("org.chromium.bluetooth.Manager", false, true, false).await?;
88 log::debug!("D-Bus name: {}", conn.unique_name());
89
90 // Create a new crossroads instance.
91 // The instance is configured so that introspection and properties interfaces
92 // are added by default on object path additions.
93 let cr = Arc::new(Mutex::new(Crossroads::new()));
94
95 // Enable async support for the crossroads instance.
96 cr.lock().unwrap().set_async_support(Some((
97 conn.clone(),
98 Box::new(|x| {
99 tokio::spawn(x);
100 }),
101 )));
102
103 // Object manager is necessary for clients (to inform them when Bluetooth is
104 // available). Create it at root (/) so subsequent additions generate
105 // InterfaceAdded and InterfaceRemoved signals.
106 cr.lock().unwrap().set_object_manager_support(Some(conn.clone()));
107 let om = cr.lock().unwrap().object_manager();
108 cr.lock().unwrap().insert("/", &[om], {});
109
110 let bluetooth_manager = Arc::new(Mutex::new(Box::new(BluetoothManager::new(proxy))));
111
112 // Set up the disconnect watcher to monitor client disconnects.
113 let disconnect_watcher = Arc::new(Mutex::new(DisconnectWatcher::new()));
114 disconnect_watcher.lock().unwrap().setup_watch(conn.clone()).await;
115
116 // We add the Crossroads instance to the connection so that incoming method calls will be
117 // handled.
118 // This must be done before exporting interfaces so that clients that rely on InterfacesAdded
119 // signal can rely on us being ready to handle methods on those exported interfaces.
120 let cr_clone = cr.clone();
121 conn.start_receive(
122 MatchRule::new_method_call(),
123 Box::new(move |msg, conn| {
124 cr_clone.lock().unwrap().handle_message(msg, conn).unwrap();
125 true
126 }),
127 );
128
129 // Let's add the "/org/chromium/bluetooth/Manager" path, which implements
130 // the org.chromium.bluetooth.Manager interface, to the crossroads instance.
131 let iface = bluetooth_manager_dbus::export_bluetooth_manager_dbus_intf(
132 conn.clone(),
133 &mut cr.lock().unwrap(),
134 disconnect_watcher.clone(),
135 );
136
137 // Let's add the "/org/chromium/bluetooth/Experimental" path, which implements
138 // the org.chromium.bluetooth.Experimental interface, to the crossroads instance
139 let iface_exp = bluetooth_experimental_dbus::export_bluetooth_experimental_dbus_intf(
140 conn.clone(),
141 &mut cr.lock().unwrap(),
142 disconnect_watcher.clone(),
143 );
144
145 // Create mixin object for Manager + Experimental interfaces.
146 let mixin = Box::new(iface_bluetooth_manager::BluetoothManagerMixin {
147 manager: bluetooth_manager.clone(),
148 experimental: bluetooth_manager.clone(),
149 });
150
151 cr.lock().unwrap().insert("/org/chromium/bluetooth/Manager", &[iface, iface_exp], mixin);
152
153 let mut powerd_suspend_manager = PowerdSuspendManager::new(conn.clone(), cr);
154
155 bluetooth_manager
156 .lock()
157 .unwrap()
158 .set_suspend_manager_context(powerd_suspend_manager.get_suspend_manager_context());
159
160 tokio::spawn(async move {
161 powerd_suspend_manager.init().await;
162 powerd_suspend_manager.mainloop().await;
163 });
164
165 tokio::spawn(async move {
166 state_machine::mainloop(context, bluetooth_manager).await;
167 });
168
169 std::future::pending::<()>().await;
170
171 // Run forever.
172 unreachable!()
173 }
174