use std::{ptr, env};
use std::os::unix::io::RawFd as Fd;
use libc::{c_char, c_uint};
use super::ffi::{c_int, size_t, pid_t};
use libc::{SOCK_STREAM, SOCK_DGRAM, SOCK_RAW};
use std::net::TcpListener;
use ffi::daemon as ffi;
use super::{Result, Error};
use std::io::ErrorKind;
use std::os::unix::io::FromRawFd;
use cstr_argument::CStrArgument;
use std::ptr::null;
pub enum SocketType {
Stream,
Datagram,
Raw,
}
pub enum Listening {
IsListening,
IsNotListening,
NoListeningCheck,
}
pub const LISTEN_FDS_START: Fd = 3;
pub const STATE_READY: &'static str = "READY";
pub const STATE_RELOADING: &'static str = "RELOADING";
pub const STATE_STOPPING: &'static str = "STOPPING";
pub const STATE_STATUS: &'static str = "STATUS";
pub const STATE_ERRNO: &'static str = "ERRNO";
pub const STATE_BUSERROR: &'static str = "BUSERROR";
pub const STATE_MAINPID: &'static str = "MAINPID";
pub const STATE_WATCHDOG: &'static str = "WATCHDOG";
pub const STATE_WATCHDOG_USEC: &'static str = "WATCHDOG_USEC";
pub const STATE_EXTEND_TIMEOUT_USEC: &'static str = "EXTEND_TIMEOUT_USEC";
pub const STATE_FDSTORE: &'static str = "FDSTORE";
pub const STATE_FDSTOREREMOVE: &'static str = "FDSTOREREMOVE";
pub const STATE_FDNAME: &'static 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 !try!(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)
}