network_manager/
service.rs

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