orb_network_manager/
service.rs

1extern crate dbus;
2extern crate futures;
3extern crate futures_cpupool;
4extern crate tokio_timer;
5
6use self::dbus::arg::{Dict, Iter, Variant};
7use self::dbus::ffidisp::{BusType, Connection, ConnectionItem};
8use self::dbus::strings::{Interface, Member};
9use self::dbus::{arg::messageitem::Props, Message, Path};
10use self::futures::Future;
11use self::futures_cpupool::CpuPool;
12use self::tokio_timer::Timer;
13use std::str::FromStr;
14use std::time::Duration;
15
16use errors::*;
17
18pub const SD_SERVICE_MANAGER: &str = "org.freedesktop.systemd1";
19pub const SD_SERVICE_PATH: &str = "/org/freedesktop/systemd1";
20pub const SD_MANAGER_INTERFACE: &str = "org.freedesktop.systemd1.Manager";
21pub const SD_UNIT_INTERFACE: &str = "org.freedesktop.systemd1.Unit";
22
23pub fn start_service(timeout: u64) -> Result<ServiceState> {
24    let state = get_service_state()?;
25    match state {
26        ServiceState::Active => Ok(state),
27        ServiceState::Activating => handler(timeout, ServiceState::Active),
28        ServiceState::Failed => bail!(ErrorKind::Service),
29        _ => {
30            let message = Message::new_method_call(
31                SD_SERVICE_MANAGER,
32                SD_SERVICE_PATH,
33                SD_MANAGER_INTERFACE,
34                "StartUnit",
35            )
36                .map_err(|_| ErrorKind::Service)?
37                .append2("NetworkManager.service", "fail");
38
39            let connection =
40                Connection::get_private(BusType::System).map_err(|_| ErrorKind::Service)?;
41
42            connection
43                .send_with_reply_and_block(message, 2000)
44                .map_err(|_| ErrorKind::Service)?;
45
46            handler(timeout, ServiceState::Active)
47        }
48    }
49}
50
51pub fn stop_service(timeout: u64) -> Result<ServiceState> {
52    let state = get_service_state()?;
53    match state {
54        ServiceState::Inactive => Ok(state),
55        ServiceState::Deactivating => handler(timeout, ServiceState::Inactive),
56        ServiceState::Failed => bail!(ErrorKind::Service),
57        _ => {
58            let message = Message::new_method_call(
59                SD_SERVICE_MANAGER,
60                SD_SERVICE_PATH,
61                SD_MANAGER_INTERFACE,
62                "StopUnit",
63            )
64                .map_err(|_| ErrorKind::Service)?
65                .append2("NetworkManager.service", "fail");
66
67            let connection =
68                Connection::get_private(BusType::System).map_err(|_| ErrorKind::Service)?;
69
70            connection
71                .send_with_reply_and_block(message, 2000)
72                .map_err(|_| ErrorKind::Service)?;
73
74            handler(timeout, ServiceState::Inactive)
75        }
76    }
77}
78
79pub fn get_service_state() -> Result<ServiceState> {
80    let message = Message::new_method_call(
81        SD_SERVICE_MANAGER,
82        SD_SERVICE_PATH,
83        SD_MANAGER_INTERFACE,
84        "GetUnit",
85    )
86        .map_err(|_| ErrorKind::Service)?
87        .append1("NetworkManager.service");
88
89    let connection = Connection::get_private(BusType::System).map_err(|_| ErrorKind::Service)?;
90
91    let response = connection
92        .send_with_reply_and_block(message, 2000)
93        .map_err(|_| ErrorKind::Service)?;
94
95    let path = response.get1::<Path>().ok_or(ErrorKind::Service)?;
96
97    let response = Props::new(
98        &connection,
99        SD_SERVICE_MANAGER,
100        path,
101        SD_UNIT_INTERFACE,
102        2000,
103    )
104        .get("ActiveState")
105        .map_err(|_| ErrorKind::Service)?;
106
107    response
108        .inner::<&str>()
109        .ok()
110        .ok_or(ErrorKind::Service)?
111        .parse()
112}
113
114fn handler(timeout: u64, target_state: ServiceState) -> Result<ServiceState> {
115    if timeout == 0 {
116        return get_service_state();
117    }
118
119    let timer = Timer::default()
120        .sleep(Duration::from_secs(timeout))
121        .then(|_| bail!(ErrorKind::Service));
122
123    let process = CpuPool::new_num_cpus().spawn_fn(|| {
124        let connection =
125            Connection::get_private(BusType::System).map_err(|_| ErrorKind::Service)?;
126        connection
127            .add_match(
128                "type='signal', sender='org.freedesktop.systemd1', \
129                 interface='org.freedesktop.DBus.Properties', \
130                 member='PropertiesChanged', \
131                 path='/org/freedesktop/systemd1/unit/NetworkManager_2eservice'",
132            )
133            .map_err(|_| ErrorKind::Service)?;
134
135        if get_service_state()? == target_state {
136            return Ok(target_state);
137        }
138
139        for item in connection.iter(0) {
140            let response = if let ConnectionItem::Signal(ref signal) = item {
141                signal
142            } else {
143                continue;
144            };
145
146            if response.interface().ok_or(ErrorKind::Service)?
147                != Interface::from("org.freedesktop.DBus.Properties")
148                || response.member().ok_or(ErrorKind::Service)? != Member::from("PropertiesChanged")
149                || response.path().ok_or(ErrorKind::Service)?
150                != Path::from("/org/freedesktop/systemd1/unit/NetworkManager_2eservice")
151            {
152                continue;
153            }
154
155            let (interface, dictionary) = response.get2::<&str, Dict<&str, Variant<Iter>, _>>();
156
157            if interface.ok_or(ErrorKind::Service)? != "org.freedesktop.systemd1.Unit" {
158                continue;
159            }
160
161            for (k, mut v) in dictionary.ok_or(ErrorKind::Service)? {
162                if k == "ActiveState" {
163                    let response = v.0.get::<&str>().ok_or(ErrorKind::Service)?;
164                    let state: ServiceState = response.parse()?;
165                    if state == target_state {
166                        return Ok(target_state);
167                    }
168                }
169            }
170        }
171        bail!(ErrorKind::Service)
172    });
173
174    match timer.select(process).map(|(result, _)| result).wait() {
175        Ok(val) => Ok(val),
176        Err(val) => Err(val.0),
177    }
178}
179
180#[derive(Debug, Eq, PartialEq)]
181pub enum ServiceState {
182    Active,
183    Reloading,
184    Inactive,
185    Failed,
186    Activating,
187    Deactivating,
188}
189
190impl FromStr for ServiceState {
191    type Err = Error;
192    fn from_str(s: &str) -> Result<ServiceState> {
193        match s {
194            "active" => Ok(ServiceState::Active),
195            "reloading" => Ok(ServiceState::Reloading),
196            "inactive" => Ok(ServiceState::Inactive),
197            "failed" => Ok(ServiceState::Failed),
198            "activating" => Ok(ServiceState::Activating),
199            "deactivating" => Ok(ServiceState::Deactivating),
200            _ => bail!(ErrorKind::Service),
201        }
202    }
203}