summaryrefslogtreecommitdiff
path: root/system/gd/rust/linux/mgmt/src/bin/btmanagerd/main.rs
blob: 9f12f2c90543e4d3479c2786093707dc838847af (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
mod bluetooth_manager;
mod bluetooth_manager_dbus;
mod config_util;
mod dbus_arg;
mod state_machine;

use crate::bluetooth_manager::BluetoothManager;
use dbus::channel::MatchingReceiver;
use dbus::message::MatchRule;
use dbus_crossroads::Crossroads;
use dbus_projection::DisconnectWatcher;
use dbus_tokio::connection;
use log::LevelFilter;
use std::sync::atomic::AtomicBool;
use std::sync::{Arc, Mutex};
use syslog::{BasicLogger, Facility, Formatter3164};

#[derive(Clone)]
struct ManagerContext {
    proxy: state_machine::StateMachineProxy,
    floss_enabled: Arc<AtomicBool>,
}

#[tokio::main]
pub async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let formatter = Formatter3164 {
        facility: Facility::LOG_USER,
        hostname: None,
        process: "btmanagerd".into(),
        pid: 0,
    };

    let logger = syslog::unix(formatter).expect("could not connect to syslog");
    let _ = log::set_boxed_logger(Box::new(BasicLogger::new(logger)))
        .map(|()| log::set_max_level(config_util::get_log_level().unwrap_or(LevelFilter::Info)));

    // Initialize config util
    config_util::fix_config_file_format();

    // Connect to the D-Bus system bus (this is blocking, unfortunately).
    let (resource, conn) = connection::new_system_sync()?;

    // Determine whether to use upstart or systemd
    let args: Vec<String> = std::env::args().collect();
    let invoker = if args.len() > 1 {
        match &args[1][0..] {
            "--systemd" | "-s" => state_machine::Invoker::SystemdInvoker,
            _ => state_machine::Invoker::UpstartInvoker,
        }
    } else {
        state_machine::Invoker::UpstartInvoker
    };

    let context = state_machine::start_new_state_machine_context(invoker);
    let proxy = context.get_proxy();
    let manager_context = ManagerContext {
        proxy: proxy,
        floss_enabled: Arc::new(AtomicBool::new(config_util::is_floss_enabled())),
    };

    // The resource is a task that should be spawned onto a tokio compatible
    // reactor ASAP. If the resource ever finishes, you lost connection to D-Bus.
    tokio::spawn(async {
        let err = resource.await;
        panic!("Lost connection to D-Bus: {}", err);
    });

    // Let's request a name on the bus, so that clients can find us.
    conn.request_name("org.chromium.bluetooth.Manager", false, true, false).await?;

    // Create a new crossroads instance.
    // The instance is configured so that introspection and properties interfaces
    // are added by default on object path additions.
    let mut cr = Crossroads::new();

    // Enable async support for the crossroads instance.
    cr.set_async_support(Some((
        conn.clone(),
        Box::new(|x| {
            tokio::spawn(x);
        }),
    )));

    // Object manager is necessary for clients (to inform them when Bluetooth is
    // available). Create it at root (/) so subsequent additions generate
    // InterfaceAdded and InterfaceRemoved signals.
    cr.set_object_manager_support(Some(conn.clone()));
    cr.insert("/", &[cr.object_manager()], {});

    let bluetooth_manager = Arc::new(Mutex::new(Box::new(BluetoothManager::new(manager_context))));

    // Set up the disconnect watcher to monitor client disconnects.
    let disconnect_watcher = Arc::new(Mutex::new(DisconnectWatcher::new()));
    disconnect_watcher.lock().unwrap().setup_watch(conn.clone()).await;

    // Let's add the "/org/chromium/bluetooth/Manager" path, which implements
    // the org.chromium.bluetooth.Manager interface, to the crossroads instance.
    bluetooth_manager_dbus::export_bluetooth_manager_dbus_obj(
        "/org/chromium/bluetooth/Manager",
        conn.clone(),
        &mut cr,
        bluetooth_manager.clone(),
        disconnect_watcher.clone(),
    );

    // We add the Crossroads instance to the connection so that incoming method calls will be handled.
    conn.start_receive(
        MatchRule::new_method_call(),
        Box::new(move |msg, conn| {
            cr.handle_message(msg, conn).unwrap();
            true
        }),
    );

    tokio::spawn(async move {
        state_machine::mainloop(context, bluetooth_manager).await;
    });

    std::future::pending::<()>().await;

    // Run forever.
    unreachable!()
}