1use crate::errors::{Context, SdError};
2use libc::pid_t;
3use nix::sys::socket;
4use nix::unistd;
5use std::io::{self, IoSlice};
6use std::os::unix::io::RawFd;
7use std::os::unix::net::UnixDatagram;
8use std::os::unix::prelude::AsRawFd;
9use std::{env, fmt, fs, time};
10
11pub fn booted() -> bool {
17 fs::symlink_metadata("/run/systemd/system")
18 .map(|p| p.is_dir())
19 .unwrap_or(false)
20}
21
22pub fn watchdog_enabled(unset_env: bool) -> Option<time::Duration> {
28 let env_usec = env::var("WATCHDOG_USEC").ok();
29 let env_pid = env::var("WATCHDOG_PID").ok();
30
31 if unset_env {
32 env::remove_var("WATCHDOG_USEC");
33 env::remove_var("WATCHDOG_PID");
34 };
35
36 let timeout = {
37 if let Some(usec) = env_usec.and_then(|usec_str| usec_str.parse::<u64>().ok()) {
38 time::Duration::from_millis(usec / 1_000)
39 } else {
40 return None;
41 }
42 };
43
44 let pid = {
45 if let Some(pid_str) = env_pid {
46 if let Ok(p) = pid_str.parse::<pid_t>() {
47 unistd::Pid::from_raw(p)
48 } else {
49 return None;
50 }
51 } else {
52 return Some(timeout);
53 }
54 };
55
56 if unistd::getpid() == pid {
57 Some(timeout)
58 } else {
59 None
60 }
61}
62
63pub fn notify(unset_env: bool, state: &[NotifyState]) -> Result<bool, SdError> {
72 notify_with_fds(unset_env, state, &[])
73}
74
75pub fn notify_with_fds(
79 unset_env: bool,
80 state: &[NotifyState],
81 fds: &[RawFd],
82) -> Result<bool, SdError> {
83 let env_sock = match env::var("NOTIFY_SOCKET").ok() {
84 None => return Ok(false),
85 Some(v) => v,
86 };
87
88 if unset_env {
89 env::remove_var("NOTIFY_SOCKET");
90 };
91
92 sanity_check_state_entries(state)?;
93
94 let socket_addr = match env_sock.strip_prefix('@') {
97 Some(stripped_addr) => socket::UnixAddr::new_abstract(stripped_addr.as_bytes())
98 .with_context(|| format!("invalid Unix socket abstract address {}", env_sock))?,
99 None => socket::UnixAddr::new(env_sock.as_str())
100 .with_context(|| format!("invalid Unix socket path address {}", env_sock))?,
101 };
102
103 let socket = UnixDatagram::unbound().context("failed to open Unix datagram socket")?;
104 let msg = state
105 .iter()
106 .fold(String::new(), |res, s| res + &format!("{}\n", s))
107 .into_bytes();
108 let msg_len = msg.len();
109 let msg_iov = IoSlice::new(&msg);
110
111 let ancillary = if !fds.is_empty() {
112 vec![socket::ControlMessage::ScmRights(fds)]
113 } else {
114 vec![]
115 };
116
117 let sent_len = socket::sendmsg(
118 socket.as_raw_fd(),
119 &[msg_iov],
120 &ancillary,
121 socket::MsgFlags::empty(),
122 Some(&socket_addr),
123 )
124 .map_err(|e| io::Error::from_raw_os_error(e as i32))
125 .context("failed to send notify datagram")?;
126
127 if sent_len != msg_len {
128 return Err(format!(
129 "incomplete notify sendmsg, sent {} out of {}",
130 sent_len, msg_len
131 )
132 .into());
133 }
134
135 Ok(true)
136}
137
138#[derive(Clone, Debug, PartialEq, Eq, Hash)]
139pub enum NotifyState {
141 Buserror(String),
143 Errno(u8),
145 Fdname(String),
147 Fdstore,
149 FdstoreRemove,
151 FdpollDisable,
155 Mainpid(unistd::Pid),
157 Other(String),
159 Ready,
161 Reloading,
163 Status(String),
165 Stopping,
167 Watchdog,
169 WatchdogUsec(u64),
171}
172
173impl fmt::Display for NotifyState {
174 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
175 match *self {
176 NotifyState::Buserror(ref s) => write!(f, "BUSERROR={}", s),
177 NotifyState::Errno(e) => write!(f, "ERRNO={}", e),
178 NotifyState::Fdname(ref s) => write!(f, "FDNAME={}", s),
179 NotifyState::Fdstore => write!(f, "FDSTORE=1"),
180 NotifyState::FdstoreRemove => write!(f, "FDSTOREREMOVE=1"),
181 NotifyState::FdpollDisable => write!(f, "FDPOLL=0"),
182 NotifyState::Mainpid(ref p) => write!(f, "MAINPID={}", p),
183 NotifyState::Other(ref s) => write!(f, "{}", s),
184 NotifyState::Ready => write!(f, "READY=1"),
185 NotifyState::Reloading => write!(f, "RELOADING=1"),
186 NotifyState::Status(ref s) => write!(f, "STATUS={}", s),
187 NotifyState::Stopping => write!(f, "STOPPING=1"),
188 NotifyState::Watchdog => write!(f, "WATCHDOG=1"),
189 NotifyState::WatchdogUsec(u) => write!(f, "WATCHDOG_USEC={}", u),
190 }
191 }
192}
193
194fn sanity_check_state_entries(state: &[NotifyState]) -> Result<(), SdError> {
196 for (index, entry) in state.iter().enumerate() {
197 match entry {
198 NotifyState::Fdname(ref name) => validate_fdname(name),
199 _ => Ok(()),
200 }
201 .with_context(|| format!("invalid notify state entry #{}", index))?;
202 }
203
204 Ok(())
205}
206
207fn validate_fdname(fdname: &str) -> Result<(), SdError> {
212 if fdname.len() > 255 {
213 return Err(format!("fdname '{}' longer than 255 characters", fdname).into());
214 }
215
216 for c in fdname.chars() {
217 if !c.is_ascii() || c == ':' || c.is_ascii_control() {
218 return Err(format!("invalid character '{}' in fdname '{}'", c, fdname).into());
219 }
220 }
221
222 Ok(())
223}