orb_network_manager/
service.rs1extern 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}