use std::ffi::CString;
use std::io;
use std::marker::PhantomData;
use std::ptr::NonNull;
#[cfg(has_hostid_from_file)]
use libnvme_sys::nvmf_hostid_from_file;
#[cfg(has_hostid_generate)]
use libnvme_sys::nvmf_hostid_generate;
use libnvme_sys::{
nvme_default_host, nvme_free_tree, nvme_lookup_host, nvme_root, nvme_scan,
nvmf_hostnqn_from_file, nvmf_hostnqn_generate,
};
use crate::host::{Host, Hosts};
use crate::{Error, Result};
pub struct Root {
inner: NonNull<nvme_root>,
_not_send_sync: PhantomData<*const ()>,
}
impl Root {
pub fn scan() -> Result<Self> {
let raw = unsafe { nvme_scan(std::ptr::null()) };
let inner = NonNull::new(raw).ok_or_else(io::Error::last_os_error)?;
Ok(Root {
inner,
_not_send_sync: PhantomData,
})
}
pub fn hosts(&self) -> Hosts<'_> {
Hosts::new(self.inner.as_ptr())
}
pub fn default_host(&self) -> Result<Host<'_>> {
let raw = unsafe { nvme_default_host(self.inner.as_ptr()) };
if raw.is_null() {
return Err(Error::Os(io::Error::last_os_error()));
}
Ok(Host::from_raw(raw, self.inner.as_ptr()))
}
pub fn lookup_host(&self, hostnqn: &str, hostid: Option<&str>) -> Result<Host<'_>> {
let hostnqn_c = CString::new(hostnqn).map_err(|_| {
Error::Os(io::Error::new(
io::ErrorKind::InvalidInput,
"interior NUL byte in hostnqn",
))
})?;
let hostid_c = match hostid {
Some(h) => Some(CString::new(h).map_err(|_| {
Error::Os(io::Error::new(
io::ErrorKind::InvalidInput,
"interior NUL byte in hostid",
))
})?),
None => None,
};
let hostid_ptr = match &hostid_c {
Some(c) => c.as_ptr(),
None => std::ptr::null(),
};
let raw = unsafe { nvme_lookup_host(self.inner.as_ptr(), hostnqn_c.as_ptr(), hostid_ptr) };
if raw.is_null() {
return Err(Error::Os(io::Error::last_os_error()));
}
Ok(Host::from_raw(raw, self.inner.as_ptr()))
}
}
pub fn generate_hostnqn() -> Result<String> {
let raw = unsafe { nvmf_hostnqn_generate() };
take_owned_cstr(raw)
}
#[cfg(has_hostid_generate)]
pub fn generate_hostid() -> Result<String> {
let raw = unsafe { nvmf_hostid_generate() };
take_owned_cstr(raw)
}
pub fn hostnqn_from_file() -> Result<String> {
let raw = unsafe { nvmf_hostnqn_from_file() };
take_owned_cstr(raw)
}
#[cfg(has_hostid_from_file)]
pub fn hostid_from_file() -> Result<String> {
let raw = unsafe { nvmf_hostid_from_file() };
take_owned_cstr(raw)
}
fn take_owned_cstr(ptr: *mut std::os::raw::c_char) -> Result<String> {
if ptr.is_null() {
return Err(Error::NotAvailable);
}
let owned = unsafe { std::ffi::CStr::from_ptr(ptr) }
.to_str()
.map_err(Error::Utf8)?
.to_owned();
unsafe { libc_free(ptr as *mut _) };
Ok(owned)
}
unsafe extern "C" {
#[link_name = "free"]
fn libc_free(ptr: *mut std::ffi::c_void);
}
impl Drop for Root {
fn drop(&mut self) {
unsafe { nvme_free_tree(self.inner.as_ptr()) };
}
}
impl std::fmt::Debug for Root {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Root")
.field("inner", &self.inner.as_ptr())
.finish()
}
}