use super::ffi::{c_int, pid_t, size_t};
use super::{Error, Result};
use ::ffi::daemon as ffi;
use cstr_argument::CStrArgument;
use libc::{c_char, c_uint};
use libc::{SOCK_DGRAM, SOCK_RAW, SOCK_STREAM};
use std::io::ErrorKind;
use std::net::TcpListener;
use std::os::unix::io::FromRawFd;
use std::os::unix::io::RawFd as Fd;
use std::ptr::null;
use std::{env, ptr};
pub enum SocketType {
Stream,
Datagram,
Raw,
}
pub enum Listening {
IsListening,
IsNotListening,
NoListeningCheck,
}
pub const LISTEN_FDS_START: Fd = 3;
pub const STATE_READY: &str = "READY";
pub const STATE_RELOADING: &str = "RELOADING";
pub const STATE_STOPPING: &str = "STOPPING";
pub const STATE_STATUS: &str = "STATUS";
pub const STATE_ERRNO: &str = "ERRNO";
pub const STATE_BUSERROR: &str = "BUSERROR";
pub const STATE_MAINPID: &str = "MAINPID";
pub const STATE_WATCHDOG: &str = "WATCHDOG";
pub const STATE_WATCHDOG_USEC: &str = "WATCHDOG_USEC";
pub const STATE_EXTEND_TIMEOUT_USEC: &str = "EXTEND_TIMEOUT_USEC";
pub const STATE_FDSTORE: &str = "FDSTORE";
pub const STATE_FDSTOREREMOVE: &str = "FDSTOREREMOVE";
pub const STATE_FDNAME: &str = "FDNAME";
pub fn listen_fds(unset_environment: bool) -> Result<Fd> {
let fds = sd_try!(ffi::sd_listen_fds(0));
if unset_environment {
env::remove_var("LISTEN_FDS");
env::remove_var("LISTEN_PID");
env::remove_var("LISTEN_FDNAMES");
}
Ok(fds)
}
pub fn is_fifo<S: CStrArgument>(fd: Fd, path: Option<S>) -> Result<bool> {
let path = path.map(|x| x.into_cstr());
let result = sd_try!(ffi::sd_is_fifo(
fd,
path.map_or(null(), |x| x.as_ref().as_ptr())
));
Ok(result != 0)
}
pub fn is_special<S: CStrArgument>(fd: Fd, path: Option<S>) -> Result<bool> {
let path = path.map(|x| x.into_cstr());
let result = sd_try!(ffi::sd_is_special(
fd,
path.map_or(null(), |x| x.as_ref().as_ptr())
));
Ok(result != 0)
}
#[inline]
fn get_c_socktype(socktype: Option<SocketType>) -> c_int {
match socktype {
Some(SocketType::Stream) => SOCK_STREAM,
Some(SocketType::Datagram) => SOCK_DGRAM,
Some(SocketType::Raw) => SOCK_RAW,
None => 0,
}
}
#[inline]
fn get_c_listening(listening: Listening) -> c_int {
match listening {
Listening::IsListening => 1,
Listening::IsNotListening => 0,
Listening::NoListeningCheck => -1,
}
}
pub fn is_socket(
fd: Fd,
family: Option<c_uint>,
socktype: Option<SocketType>,
listening: Listening,
) -> Result<bool> {
let c_family = family.unwrap_or(0) as c_int;
let c_socktype = get_c_socktype(socktype);
let c_listening = get_c_listening(listening);
let result = sd_try!(ffi::sd_is_socket(fd, c_family, c_socktype, c_listening));
Ok(result != 0)
}
pub fn is_socket_inet(
fd: Fd,
family: Option<c_uint>,
socktype: Option<SocketType>,
listening: Listening,
port: Option<u16>,
) -> Result<bool> {
let c_family = family.unwrap_or(0) as c_int;
let c_socktype = get_c_socktype(socktype);
let c_listening = get_c_listening(listening);
let c_port = port.unwrap_or(0) as u16;
let result = sd_try!(ffi::sd_is_socket_inet(
fd,
c_family,
c_socktype,
c_listening,
c_port
));
Ok(result != 0)
}
pub fn tcp_listener(fd: Fd) -> Result<TcpListener> {
if !is_socket_inet(
fd,
None,
Some(SocketType::Stream),
Listening::IsListening,
None,
)? {
Err(Error::new(
ErrorKind::InvalidInput,
"Socket type was not as expected",
))
} else {
Ok(unsafe { TcpListener::from_raw_fd(fd) })
}
}
pub fn is_socket_unix<S: CStrArgument>(
fd: Fd,
socktype: Option<SocketType>,
listening: Listening,
path: Option<S>,
) -> Result<bool> {
let path_cstr = path.map(|p| p.into_cstr());
let c_socktype = get_c_socktype(socktype);
let c_listening = get_c_listening(listening);
let c_path: *const c_char;
let c_length: size_t;
match path_cstr.as_ref() {
Some(p) => {
let path_ref = p.as_ref();
c_length = path_ref.to_bytes().len() as size_t;
c_path = path_ref.as_ptr() as *const c_char;
}
None => {
c_path = ptr::null();
c_length = 0;
}
}
let result = sd_try!(ffi::sd_is_socket_unix(
fd,
c_socktype,
c_listening,
c_path,
c_length
));
Ok(result != 0)
}
pub fn is_mq<S: CStrArgument>(fd: Fd, path: Option<S>) -> Result<bool> {
let path = path.map(|x| x.into_cstr());
let result = sd_try!(ffi::sd_is_mq(
fd,
path.map_or(null(), |x| x.as_ref().as_ptr())
));
Ok(result != 0)
}
fn state_to_c_string<'a, I, K, V>(state: I) -> ::std::ffi::CString
where
I: Iterator<Item = &'a (K, V)>,
K: AsRef<str> + 'a,
V: AsRef<str> + 'a,
{
let mut state_vec = Vec::new();
for (key, value) in state {
state_vec.push(vec![key.as_ref(), value.as_ref()].join("="));
}
let state_str = state_vec.join("\n");
::std::ffi::CString::new(state_str.as_bytes()).unwrap()
}
pub fn notify<'a, I, K, V>(unset_environment: bool, state: I) -> Result<bool>
where
I: Iterator<Item = &'a (K, V)>,
K: AsRef<str> + 'a,
V: AsRef<str> + 'a,
{
let c_state = state_to_c_string(state);
let result = sd_try!(ffi::sd_notify(unset_environment as c_int, c_state.as_ptr()));
Ok(result != 0)
}
pub fn pid_notify<'a, I, K, V>(pid: pid_t, unset_environment: bool, state: I) -> Result<bool>
where
I: Iterator<Item = &'a (K, V)>,
K: AsRef<str> + 'a,
V: AsRef<str> + 'a,
{
let c_state = state_to_c_string(state);
let result = sd_try!(ffi::sd_pid_notify(
pid,
unset_environment as c_int,
c_state.as_ptr()
));
Ok(result != 0)
}
pub fn pid_notify_with_fds<'a, I, K, V>(
pid: pid_t,
unset_environment: bool,
state: I,
fds: &[Fd],
) -> Result<bool>
where
I: Iterator<Item = &'a (K, V)>,
K: AsRef<str> + 'a,
V: AsRef<str> + 'a,
{
let c_state = state_to_c_string(state);
let result = sd_try!(ffi::sd_pid_notify_with_fds(
pid,
unset_environment as c_int,
c_state.as_ptr(),
fds.as_ptr(),
fds.len() as c_uint
));
Ok(result != 0)
}
pub fn booted() -> Result<bool> {
let result = sd_try!(ffi::sd_booted());
Ok(result != 0)
}
pub fn watchdog_enabled(unset_environment: bool) -> Result<u64> {
let mut timeout: u64 = 0;
sd_try!(ffi::sd_watchdog_enabled(
unset_environment as c_int,
&mut timeout
));
Ok(timeout)
}