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