use std::{ptr, collections};
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;
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_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 fn listen_fds(unset_environment: bool) -> Result<Fd> {
let fds = sd_try!(ffi::sd_listen_fds(unset_environment as c_int));
Ok(fds)
}
pub fn is_fifo(fd: Fd, path: Option<&str>) -> Result<bool> {
let c_path = char_or_null!(path);
let result = sd_try!(ffi::sd_is_fifo(fd, c_path));
Ok(result != 0)
}
pub fn is_special(fd: Fd, path: Option<&str>) -> Result<bool> {
let c_path = char_or_null!(path);
let result = sd_try!(ffi::sd_is_special(fd, c_path));
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(fd: Fd,
socktype: Option<SocketType>,
listening: Listening,
path: Option<&str>)
-> Result<bool> {
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 {
Some(p) => {
let path_cstr = ::std::ffi::CString::new(p.as_bytes()).unwrap();
c_length = path_cstr.as_bytes().len() as size_t;
c_path = path_cstr.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(fd: Fd, path: Option<&str>) -> Result<bool> {
let c_path = char_or_null!(path);
let result = sd_try!(ffi::sd_is_mq(fd, c_path));
Ok(result != 0)
}
fn state_to_c_string(state: collections::HashMap<&str, &str>) -> ::std::ffi::CString {
let mut state_vec = Vec::new();
for (key, value) in state.iter() {
state_vec.push(vec![*key, *value].join("="));
}
let state_str = state_vec.join("\n");
::std::ffi::CString::new(state_str.as_bytes()).unwrap()
}
pub fn notify(unset_environment: bool, state: collections::HashMap<&str, &str>) -> Result<bool> {
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(pid: pid_t,
unset_environment: bool,
state: collections::HashMap<&str, &str>)
-> Result<bool> {
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 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)
}