use super::error;
use crate::error::KnotError;
use crate::util::MakeFallible;
use crate::{common, KnotSync};
use fallible_iterator::FallibleIterator;
use libknot_sys as sys;
use std::ffi::{CStr, CString};
use std::{fmt, i32, mem, ptr, time};
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum MessageField {
Command,
Flags,
Error,
Section,
Item,
Id,
Zone,
Owner,
TTL,
Type,
Data,
Filter,
}
const ALL_MESSAGE_FIELDS: [MessageField; 12] = [
MessageField::Command,
MessageField::Flags,
MessageField::Error,
MessageField::Section,
MessageField::Item,
MessageField::Id,
MessageField::Zone,
MessageField::Owner,
MessageField::TTL,
MessageField::Type,
MessageField::Data,
MessageField::Filter,
];
impl MessageField {
#[allow(dead_code)]
pub(crate) fn to_sys(self) -> sys::knot_ctl_idx_t {
match self {
MessageField::Command => sys::knot_ctl_idx_t::KNOT_CTL_IDX_CMD,
MessageField::Flags => sys::knot_ctl_idx_t::KNOT_CTL_IDX_FLAGS,
MessageField::Error => sys::knot_ctl_idx_t::KNOT_CTL_IDX_ERROR,
MessageField::Section => sys::knot_ctl_idx_t::KNOT_CTL_IDX_SECTION,
MessageField::Item => sys::knot_ctl_idx_t::KNOT_CTL_IDX_ITEM,
MessageField::Id => sys::knot_ctl_idx_t::KNOT_CTL_IDX_ID,
MessageField::Zone => sys::knot_ctl_idx_t::KNOT_CTL_IDX_ZONE,
MessageField::Owner => sys::knot_ctl_idx_t::KNOT_CTL_IDX_OWNER,
MessageField::TTL => sys::knot_ctl_idx_t::KNOT_CTL_IDX_TTL,
MessageField::Type => sys::knot_ctl_idx_t::KNOT_CTL_IDX_TYPE,
MessageField::Data => sys::knot_ctl_idx_t::KNOT_CTL_IDX_DATA,
MessageField::Filter => sys::knot_ctl_idx_t::KNOT_CTL_IDX_FILTERS,
}
}
#[allow(dead_code)]
pub(crate) fn from_sys(s: sys::knot_ctl_idx_t) -> MessageField {
match s {
sys::knot_ctl_idx_t::KNOT_CTL_IDX_CMD => MessageField::Command,
sys::knot_ctl_idx_t::KNOT_CTL_IDX_FLAGS => MessageField::Flags,
sys::knot_ctl_idx_t::KNOT_CTL_IDX_ERROR => MessageField::Error,
sys::knot_ctl_idx_t::KNOT_CTL_IDX_SECTION => MessageField::Section,
sys::knot_ctl_idx_t::KNOT_CTL_IDX_ITEM => MessageField::Item,
sys::knot_ctl_idx_t::KNOT_CTL_IDX_ID => MessageField::Id,
sys::knot_ctl_idx_t::KNOT_CTL_IDX_ZONE => MessageField::Zone,
sys::knot_ctl_idx_t::KNOT_CTL_IDX_OWNER => MessageField::Owner,
sys::knot_ctl_idx_t::KNOT_CTL_IDX_TTL => MessageField::TTL,
sys::knot_ctl_idx_t::KNOT_CTL_IDX_TYPE => MessageField::Type,
sys::knot_ctl_idx_t::KNOT_CTL_IDX_DATA => MessageField::Data,
sys::knot_ctl_idx_t::KNOT_CTL_IDX_FILTERS => MessageField::Filter,
sys::knot_ctl_idx_t::KNOT_CTL_IDX__COUNT => unreachable!("INVALID INDEX __COUNT"),
}
}
}
pub struct Control {
ctl: *mut sys::knot_ctl,
}
impl Control {
pub fn new(path: &str) -> error::Result<Control> {
let ptr = unsafe { sys::knot_ctl_alloc() };
if ptr.is_null() {
return Err(error::KnotError::OutOfMemory);
}
if let Ok(c_path) = CString::new(path) {
error::auto_err(unsafe { sys::knot_ctl_connect(ptr, c_path.as_ptr()) })?;
} else {
return Err(error::KnotError::NulError);
}
Ok(Control {
ctl: ptr,
})
}
pub fn send_message(&self, msg: &ControlMessage) -> error::Result<()> {
let ptr = (&msg.data) as *const sys::knot_ctl_data_t;
error::auto_err(unsafe {
sys::knot_ctl_send(
self.ctl,
sys::knot_ctl_type_t_KNOT_CTL_TYPE_DATA,
ptr as *mut [*const i8; 12],
) })?;
error::auto_err(unsafe {
sys::knot_ctl_send(
self.ctl,
sys::knot_ctl_type_t_KNOT_CTL_TYPE_BLOCK,
ptr::null::<sys::knot_ctl_data_t>() as *mut [*const i8; 12],
)
})?;
Ok(())
}
pub fn recv_single_message(&self) -> error::Result<(sys::knot_ctl_type_t, ControlMessage)> {
let mut ty = sys::knot_ctl_type_t_KNOT_CTL_TYPE_BLOCK;
let mut msg = [ptr::null(); 12];
error::auto_err(unsafe {
sys::knot_ctl_receive(
self.ctl,
(&mut ty) as *mut sys::knot_ctl_type_t,
(&mut msg) as *mut sys::knot_ctl_data_t,
)
})?;
Ok((ty, unsafe { ControlMessage::from_raw(msg)? }))
}
pub fn set_timeout(&mut self, timeout: Option<time::Duration>) {
if let Some(t) = timeout {
let ms = t.as_secs() * 1000 + (t.subsec_millis() as u64);
if ms > i32::MAX as u64 {
panic!("Size must be between 1 and i32::MAX ms");
}
unsafe { sys::knot_ctl_set_timeout(self.ctl, ms as i32) };
} else {
unsafe { sys::knot_ctl_set_timeout(self.ctl, 0) };
}
}
pub fn send_request(&mut self, msg: &ControlMessage) -> error::Result<MessageIterator> {
self.send_message(msg)?;
Ok(MessageIterator {
con: self,
burnt: false,
})
}
}
impl KnotSync for Control {
fn in_transaction(&self) -> bool {
true
}
fn conf_transaction<F, T, C>(&mut self, cb: C) -> Result<T, F>
where
C: FnOnce(&mut Self) -> Result<T, F>,
F: From<KnotError>,
{
log::trace!("Beginning Config Transaction");
let next = self
.send_request(&MessageBuilder::new().cmd("conf-begin").build())?
.next();
if let Some(n) = next {
n?.make_successful()?;
}
match cb(self) {
Ok(thing) => {
log::debug!("Committing Config Transaction");
let next = self
.send_request(&MessageBuilder::new().cmd("conf-commit").build())?
.next();
if let Some(n) = next {
match n?.make_successful() {
Ok(_) => Ok(thing),
Err(e) => {
log::debug!("Commit failed, falling back to aborting it");
self.send_request(&MessageBuilder::new().cmd("conf-abort").build())?;
Err(e.into())
}
}
} else {
Ok(thing)
}
}
Err(e) => {
log::debug!("Aborting Config Transaction");
self.send_request(&MessageBuilder::new().cmd("conf-abort").build())?;
Err(e)
}
}
}
fn conf_set(
&mut self,
section: &str,
id: Option<&str>,
item: Option<&str>,
value: Option<&str>,
) -> error::Result<()> {
log::trace!(
"conf-set: {section}[{id:?}].{item:?} = {data:?}",
section = section,
item = item,
id = id,
data = value
);
let mut msg = MessageBuilder::new()
.cmd("conf-set")
.section(section)
.build();
msg.set(MessageField::Item, item)?;
msg.set(MessageField::Id, id)?;
msg.set(MessageField::Data, value)?;
for el in self.send_request(&msg)? {
el?.make_successful()?;
}
Ok(())
}
fn conf_unset(
&mut self,
section: &str,
id: Option<&str>,
item: Option<&str>,
value: Option<&str>,
) -> error::Result<()> {
log::trace!(
"conf-unset: {section}[{id:?}].{item:?}",
section = section,
item = item,
id = id
);
let mut msg = MessageBuilder::new()
.cmd("conf-unset")
.section(section)
.build();
msg.set(MessageField::Item, item)?;
msg.set(MessageField::Id, id)?;
msg.set(MessageField::Data, value)?;
for el in self.send_request(&msg)? {
el?.make_successful()?;
}
Ok(())
}
fn zone_transaction<F, T, C>(&mut self, zone: &str, cb: C) -> Result<T, F>
where
C: FnOnce(&mut Self) -> Result<T, F>,
F: From<KnotError>,
{
let next = self
.send_request(&MessageBuilder::new().cmd("zone-begin").zone(zone).build())?
.next();
if let Some(n) = next {
n?.make_successful()?;
}
match cb(self) {
Ok(thing) => {
let next = self
.send_request(&MessageBuilder::new().cmd("zone-commit").zone(zone).build())?
.next();
if let Some(n) = next {
n?.make_successful()?;
}
Ok(thing)
}
Err(e) => {
self.send_request(&MessageBuilder::new().cmd("zone-abort").zone(zone).build())?;
Err(e)
}
}
}
fn conf_get<'a>(
&'a mut self,
section: Option<&str>,
id: Option<&str>,
item: Option<&str>,
) -> Result<Vec<common::ControlMessage>, KnotError> {
log::trace!(
"conf-get: {section:?}[{id:?}].{item:?}",
section = section,
item = item,
id = id
);
let msg = MessageBuilder::new().cmd("conf-get").build();
fallible_iterator::convert(self.send_request(&msg)?)
.map(|el| el.into_successful())
.map(|el| {
let msg = ALL_MESSAGE_FIELDS
.iter()
.enumerate()
.make_fallible()
.flat_map(|(id, field)| {
Ok(fallible_iterator::convert(
el.get(*field)?
.map(|val| Result::<_, KnotError>::Ok((id as u8, val.to_owned())))
.into_iter(),
))
})
.collect()?;
common::build_message(msg)
})
.collect::<Vec<_>>()
}
}
impl Drop for Control {
fn drop(&mut self) {
unsafe {
sys::knot_ctl_free(self.ctl);
self.ctl = ptr::null_mut::<sys::knot_ctl>();
}
}
}
pub struct ControlMessage {
pub(crate) data: sys::knot_ctl_data_t,
data_storage: [Option<CString>; 12],
}
impl ControlMessage {
pub fn new() -> ControlMessage {
ControlMessage {
data: [ptr::null(); 12],
data_storage: Default::default(),
}
}
pub fn set(&mut self, field: MessageField, value: Option<&str>) -> error::Result<()> {
match value {
Some(value) => {
if let Ok(c_value) = CString::new(value) {
self.data[field as usize] = c_value.as_ptr();
self.data_storage[field as usize] = Some(c_value);
} else {
return Err(error::KnotError::NulError);
}
}
None => {
self.data[field as usize] = ptr::null();
self.data_storage[field as usize] = None;
}
}
Ok(())
}
pub fn get(&self, field: MessageField) -> error::Result<Option<&str>> {
if let Some(ref c_value) = self.data_storage[field as usize] {
match c_value.as_c_str().to_str() {
Ok(val) => Ok(Some(val)),
Err(_) => Err(error::KnotError::UTF8Error),
}
} else {
Ok(None)
}
}
pub unsafe fn from_raw(data: sys::knot_ctl_data_t) -> error::Result<ControlMessage> {
let mut ds: Vec<Option<CString>> = data
.iter()
.map(|e| {
if e.is_null() {
None
} else {
Some(CStr::from_ptr(*e).to_owned())
}
})
.collect();
let mut d: Vec<_> = ds
.iter()
.map(|s| {
if let Some(s) = s {
s.as_c_str().as_ptr()
} else {
ptr::null()
}
})
.collect();
let mut msg = ControlMessage {
data: [ptr::null(); 12],
data_storage: Default::default(),
};
swap(&mut msg.data, &mut d);
swap(&mut msg.data_storage, &mut ds);
Ok(msg)
}
pub fn make_successful(&self) -> error::Result<&ControlMessage> {
if let Some(e) = self.get(MessageField::Error)? {
Err(error::KnotError::KnotErrorMsg { msg: e.to_string() })
} else {
Ok(self)
}
}
pub fn into_successful(self) -> error::Result<ControlMessage> {
if let Some(e) = self.get(MessageField::Error)? {
Err(error::KnotError::KnotErrorMsg { msg: e.to_string() })
} else {
Ok(self)
}
}
}
impl Default for ControlMessage {
fn default() -> Self {
Self::new()
}
}
impl fmt::Debug for ControlMessage {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("ControlMessage")
.field(
"KNOT_CTL_IDX_CMD",
&self.get(MessageField::Command).unwrap(),
)
.field(
"KNOT_CTL_IDX_FLAGS",
&self.get(MessageField::Flags).unwrap(),
)
.field(
"KNOT_CTL_IDX_ERROR",
&self.get(MessageField::Error).unwrap(),
)
.field(
"KNOT_CTL_IDX_SECTION",
&self.get(MessageField::Section).unwrap(),
)
.field("KNOT_CTL_IDX_ITEM", &self.get(MessageField::Item).unwrap())
.field("KNOT_CTL_IDX_ID", &self.get(MessageField::Id).unwrap())
.field("KNOT_CTL_IDX_ZONE", &self.get(MessageField::Zone).unwrap())
.field(
"KNOT_CTL_IDX_OWNER",
&self.get(MessageField::Owner).unwrap(),
)
.field("KNOT_CTL_IDX_TTL", &self.get(MessageField::TTL).unwrap())
.field("KNOT_CTL_IDX_TYPE", &self.get(MessageField::Type).unwrap())
.field("KNOT_CTL_IDX_DATA", &self.get(MessageField::Data).unwrap())
.field(
"KNOT_CTL_IDX_FILTER",
&self.get(MessageField::Filter).unwrap(),
)
.finish()
}
}
#[derive(Debug)]
pub struct MessageBuilder {
pub(crate) message: ControlMessage,
}
impl MessageBuilder {
pub fn new() -> MessageBuilder {
MessageBuilder {
message: ControlMessage::new(),
}
}
pub fn cmd(mut self, cmd: &str) -> Self {
self.message.set(MessageField::Command, Some(cmd)).unwrap();
self
}
pub fn section(mut self, section: &str) -> Self {
self.message
.set(MessageField::Section, Some(section))
.unwrap();
self
}
pub fn item(mut self, item: &str) -> Self {
self.message.set(MessageField::Item, Some(item)).unwrap();
self
}
pub fn zone(mut self, zone: &str) -> Self {
self.message.set(MessageField::Zone, Some(zone)).unwrap();
self
}
pub fn owner(mut self, owner: &str) -> Self {
self.message.set(MessageField::Owner, Some(owner)).unwrap();
self
}
pub fn ttl(mut self, ttl: u32) -> Self {
self.message
.set(MessageField::TTL, Some(&format!("{}", ttl)))
.unwrap();
self
}
pub fn record_type(mut self, ty: &str) -> Self {
self.message.set(MessageField::Type, Some(ty)).unwrap();
self
}
pub fn data(mut self, data: &str) -> Self {
self.message.set(MessageField::Data, Some(data)).unwrap();
self
}
pub fn build(self) -> ControlMessage {
self.message
}
}
impl Default for MessageBuilder {
fn default() -> Self {
Self::new()
}
}
impl From<MessageBuilder> for ControlMessage {
fn from(thing: MessageBuilder) -> ControlMessage {
thing.message
}
}
pub struct MessageIterator<'a> {
pub(crate) con: &'a mut Control,
pub(crate) burnt: bool,
}
impl<'a> Iterator for MessageIterator<'a> {
type Item = error::Result<ControlMessage>;
fn next(&mut self) -> Option<Self::Item> {
if self.burnt {
None
} else {
let (id, m) = match self.con.recv_single_message() {
Ok(thing) => thing,
Err(e) => return Some(Err(e)),
};
match id {
sys::knot_ctl_type_t_KNOT_CTL_TYPE_DATA
| sys::knot_ctl_type_t_KNOT_CTL_TYPE_EXTRA => Some(Ok(m)),
sys::knot_ctl_type_t_KNOT_CTL_TYPE_BLOCK
| sys::knot_ctl_type_t_KNOT_CTL_TYPE_END => {
self.burnt = true;
None
}
_ => panic!("Unexpected frame type {}", id),
}
}
}
}
impl<'a> Drop for MessageIterator<'a> {
fn drop(&mut self) {
for _ in self {}
}
}
fn swap<T>(s1: &mut [T], s2: &mut [T]) {
for i in 0..s1.len() {
mem::swap(&mut s1[i], &mut s2[i]);
}
}