use std::{mem, ptr, str};
use crate::domain::{Domain, DomainStatsRecord};
use crate::error::Error;
use crate::interface::Interface;
use crate::network::Network;
use crate::nodedev::NodeDevice;
use crate::nwfilter::NWFilter;
use crate::secret::Secret;
use crate::storage_pool::StoragePool;
extern "C" fn connect_callback(
ccreds: sys::virConnectCredentialPtr,
ncred: libc::c_uint,
cbdata: *mut libc::c_void,
) -> libc::c_int {
let callback: ConnectAuthCallback = unsafe {
mem::transmute(cbdata)
};
let mut rcreds: Vec<ConnectCredential> = Vec::new();
for i in 0..ncred as isize {
unsafe {
let c = ConnectCredential::from_ptr(ccreds.offset(i));
rcreds.push(c);
}
}
callback(&mut rcreds);
for i in 0..ncred as isize {
if rcreds[i as usize].result.is_some() {
if let Some(ref result) = rcreds[i as usize].result {
unsafe {
(*ccreds.offset(i)).resultlen = result.len() as libc::c_uint;
(*ccreds.offset(i)).result = string_to_mut_c_chars!(result.clone());
}
}
}
}
0
}
#[derive(Clone, Debug)]
pub struct NodeInfo {
pub model: String,
pub memory: u64,
pub cpus: u32,
pub mhz: u32,
pub nodes: u32,
pub sockets: u32,
pub cores: u32,
pub threads: u32,
}
pub type ConnectAuthCallback = fn(creds: &mut Vec<ConnectCredential>);
#[derive(Clone, Debug)]
pub struct ConnectCredential {
pub typed: i32,
pub prompt: String,
pub challenge: String,
pub def_result: String,
pub result: Option<String>,
}
impl ConnectCredential {
pub unsafe fn from_ptr(cred: sys::virConnectCredentialPtr) -> ConnectCredential {
let mut default: String = String::from("");
if !(*cred).defresult.is_null() {
default = c_chars_to_string!((*cred).defresult, nofree);
}
ConnectCredential {
typed: (*cred).type_,
prompt: c_chars_to_string!((*cred).prompt, nofree),
challenge: c_chars_to_string!((*cred).challenge, nofree),
def_result: default,
result: None,
}
}
}
pub struct ConnectAuth {
creds: Vec<sys::virConnectCredentialType>,
callback: ConnectAuthCallback,
}
impl ConnectAuth {
pub fn new(
creds: Vec<sys::virConnectCredentialType>,
callback: ConnectAuthCallback,
) -> ConnectAuth {
ConnectAuth { creds, callback }
}
}
#[derive(Debug)]
pub struct Connect {
ptr: Option<sys::virConnectPtr>,
}
impl Connect {
pub fn as_ptr(&self) -> sys::virConnectPtr {
self.ptr.unwrap()
}
pub fn new(ptr: sys::virConnectPtr) -> Connect {
Connect { ptr: Some(ptr) }
}
pub fn get_version() -> Result<u32, Error> {
unsafe {
let mut ver: libc::c_ulong = 0;
if sys::virGetVersion(&mut ver, ptr::null(), ptr::null_mut()) == -1 {
return Err(Error::last_error());
}
Ok(ver as u32)
}
}
pub fn open(uri: &str) -> Result<Connect, Error> {
unsafe {
let c = sys::virConnectOpen(string_to_c_chars!(uri));
if c.is_null() {
return Err(Error::last_error());
}
Ok(Connect::new(c))
}
}
pub fn open_read_only(uri: &str) -> Result<Connect, Error> {
unsafe {
let c = sys::virConnectOpenReadOnly(string_to_c_chars!(uri));
if c.is_null() {
return Err(Error::last_error());
}
Ok(Connect::new(c))
}
}
pub fn open_auth(
uri: &str,
auth: &mut ConnectAuth,
flags: sys::virConnectFlags,
) -> Result<Connect, Error> {
let mut cauth =
sys::virConnectAuth {
credtype: auth.creds.as_mut_ptr() as *mut libc::c_int,
ncredtype: auth.creds.len() as libc::c_uint,
cb: Some(connect_callback),
cbdata: auth.callback as *mut _,
};
let c = unsafe {
sys::virConnectOpenAuth(string_to_c_chars!(uri), &mut cauth, flags as libc::c_uint)
};
if c.is_null() {
return Err(Error::last_error());
}
Ok(Connect::new(c))
}
pub fn close(&mut self) -> Result<i32, Error> {
unsafe {
let ret = sys::virConnectClose(self.as_ptr());
if ret == -1 {
return Err(Error::last_error());
}
if ret == 0 {
self.ptr = None;
}
Ok(ret)
}
}
pub fn get_hostname(&self) -> Result<String, Error> {
unsafe {
let n = sys::virConnectGetHostname(self.as_ptr());
if n.is_null() {
return Err(Error::last_error());
}
Ok(c_chars_to_string!(n))
}
}
pub fn get_capabilities(&self) -> Result<String, Error> {
unsafe {
let n = sys::virConnectGetCapabilities(self.as_ptr());
if n.is_null() {
return Err(Error::last_error());
}
Ok(c_chars_to_string!(n))
}
}
pub fn get_lib_version(&self) -> Result<u32, Error> {
unsafe {
let mut ver: libc::c_ulong = 0;
if sys::virConnectGetLibVersion(self.as_ptr(), &mut ver) == -1 {
return Err(Error::last_error());
}
Ok(ver as u32)
}
}
pub fn get_type(&self) -> Result<String, Error> {
unsafe {
let t = sys::virConnectGetType(self.as_ptr());
if t.is_null() {
return Err(Error::last_error());
}
Ok(c_chars_to_string!(t, nofree))
}
}
pub fn get_uri(&self) -> Result<String, Error> {
unsafe {
let t = sys::virConnectGetURI(self.as_ptr());
if t.is_null() {
return Err(Error::last_error());
}
Ok(c_chars_to_string!(t))
}
}
pub fn get_sys_info(&self, flags: u32) -> Result<String, Error> {
unsafe {
let sys = sys::virConnectGetSysinfo(self.as_ptr(), flags as libc::c_uint);
if sys.is_null() {
return Err(Error::last_error());
}
Ok(c_chars_to_string!(sys))
}
}
pub fn get_max_vcpus(&self, attr: &str) -> Result<u32, Error> {
unsafe {
let max = sys::virConnectGetMaxVcpus(self.as_ptr(), string_to_c_chars!(attr));
if max == -1 {
return Err(Error::last_error());
}
Ok(max as u32)
}
}
pub fn get_cpu_models_names(&self, arch: &str, flags: u32) -> Result<Vec<String>, Error> {
unsafe {
let mut names: *mut *mut libc::c_char = ptr::null_mut();
let size = sys::virConnectGetCPUModelNames(
self.as_ptr(),
string_to_c_chars!(arch),
&mut names,
flags as libc::c_uint,
);
if size == -1 {
return Err(Error::last_error());
}
let mut array: Vec<String> = Vec::new();
for x in 0..size as isize {
array.push(c_chars_to_string!(*names.offset(x)));
}
libc::free(names as *mut libc::c_void);
Ok(array)
}
}
pub fn is_alive(&self) -> Result<bool, Error> {
unsafe {
let t = sys::virConnectIsAlive(self.as_ptr());
if t == -1 {
return Err(Error::last_error());
}
Ok(t == 1)
}
}
pub fn is_encrypted(&self) -> Result<bool, Error> {
unsafe {
let t = sys::virConnectIsEncrypted(self.as_ptr());
if t == -1 {
return Err(Error::last_error());
}
Ok(t == 1)
}
}
pub fn is_secure(&self) -> Result<bool, Error> {
unsafe {
let t = sys::virConnectIsSecure(self.as_ptr());
if t == -1 {
return Err(Error::last_error());
}
Ok(t == 1)
}
}
#[allow(clippy::needless_range_loop)]
pub fn list_domains(&self) -> Result<Vec<u32>, Error> {
unsafe {
let mut ids: [libc::c_int; 512] = [0; 512];
let size = sys::virConnectListDomains(self.as_ptr(), ids.as_mut_ptr(), 512);
if size == -1 {
return Err(Error::last_error());
}
let mut array: Vec<u32> = Vec::new();
for x in 0..size as usize {
array.push(ids[x] as u32);
}
Ok(array)
}
}
#[allow(clippy::needless_range_loop)]
pub fn list_interfaces(&self) -> Result<Vec<String>, Error> {
unsafe {
let mut names: [*mut libc::c_char; 1024] = [ptr::null_mut(); 1024];
let size = sys::virConnectListInterfaces(self.as_ptr(), names.as_mut_ptr(), 1024);
if size == -1 {
return Err(Error::last_error());
}
let mut array: Vec<String> = Vec::new();
for x in 0..size as usize {
array.push(c_chars_to_string!(names[x]));
}
Ok(array)
}
}
#[allow(clippy::needless_range_loop)]
pub fn list_networks(&self) -> Result<Vec<String>, Error> {
unsafe {
let mut names: [*mut libc::c_char; 1024] = [ptr::null_mut(); 1024];
let size = sys::virConnectListNetworks(self.as_ptr(), names.as_mut_ptr(), 1024);
if size == -1 {
return Err(Error::last_error());
}
let mut array: Vec<String> = Vec::new();
for x in 0..size as usize {
array.push(c_chars_to_string!(names[x]));
}
Ok(array)
}
}
#[allow(clippy::needless_range_loop)]
pub fn list_nw_filters(&self) -> Result<Vec<String>, Error> {
unsafe {
let mut names: [*mut libc::c_char; 1024] = [ptr::null_mut(); 1024];
let size = sys::virConnectListNWFilters(self.as_ptr(), names.as_mut_ptr(), 1024);
if size == -1 {
return Err(Error::last_error());
}
let mut array: Vec<String> = Vec::new();
for x in 0..size as usize {
array.push(c_chars_to_string!(names[x]));
}
Ok(array)
}
}
#[allow(clippy::needless_range_loop)]
pub fn list_secrets(&self) -> Result<Vec<String>, Error> {
unsafe {
let mut names: [*mut libc::c_char; 1024] = [ptr::null_mut(); 1024];
let size = sys::virConnectListSecrets(self.as_ptr(), names.as_mut_ptr(), 1024);
if size == -1 {
return Err(Error::last_error());
}
let mut array: Vec<String> = Vec::new();
for x in 0..size as usize {
array.push(c_chars_to_string!(names[x]));
}
Ok(array)
}
}
#[allow(clippy::needless_range_loop)]
pub fn list_storage_pools(&self) -> Result<Vec<String>, Error> {
unsafe {
let mut names: [*mut libc::c_char; 1024] = [ptr::null_mut(); 1024];
let size = sys::virConnectListStoragePools(self.as_ptr(), names.as_mut_ptr(), 1024);
if size == -1 {
return Err(Error::last_error());
}
let mut array: Vec<String> = Vec::new();
for x in 0..size as usize {
array.push(c_chars_to_string!(names[x]));
}
Ok(array)
}
}
pub fn list_all_domains(
&self,
flags: sys::virConnectListAllDomainsFlags,
) -> Result<Vec<Domain>, Error> {
unsafe {
let mut domains: *mut sys::virDomainPtr = ptr::null_mut();
let size =
sys::virConnectListAllDomains(self.as_ptr(), &mut domains, flags as libc::c_uint);
if size == -1 {
return Err(Error::last_error());
}
let mut array: Vec<Domain> = Vec::new();
for x in 0..size as isize {
array.push(Domain::new(*domains.offset(x)));
}
libc::free(domains as *mut libc::c_void);
Ok(array)
}
}
pub fn list_all_networks(
&self,
flags: sys::virConnectListAllNetworksFlags,
) -> Result<Vec<Network>, Error> {
unsafe {
let mut networks: *mut sys::virNetworkPtr = ptr::null_mut();
let size =
sys::virConnectListAllNetworks(self.as_ptr(), &mut networks, flags as libc::c_uint);
if size == -1 {
return Err(Error::last_error());
}
let mut array: Vec<Network> = Vec::new();
for x in 0..size as isize {
array.push(Network::new(*networks.offset(x)));
}
libc::free(networks as *mut libc::c_void);
Ok(array)
}
}
pub fn list_all_interfaces(
&self,
flags: sys::virConnectListAllInterfacesFlags,
) -> Result<Vec<Interface>, Error> {
unsafe {
let mut interfaces: *mut sys::virInterfacePtr = ptr::null_mut();
let size = sys::virConnectListAllInterfaces(
self.as_ptr(),
&mut interfaces,
flags as libc::c_uint,
);
if size == -1 {
return Err(Error::last_error());
}
let mut array: Vec<Interface> = Vec::new();
for x in 0..size as isize {
array.push(Interface::new(*interfaces.offset(x)));
}
libc::free(interfaces as *mut libc::c_void);
Ok(array)
}
}
pub fn list_all_node_devices(
&self,
flags: sys::virConnectListAllNodeDeviceFlags,
) -> Result<Vec<NodeDevice>, Error> {
unsafe {
let mut nodedevs: *mut sys::virNodeDevicePtr = ptr::null_mut();
let size = sys::virConnectListAllNodeDevices(
self.as_ptr(),
&mut nodedevs,
flags as libc::c_uint,
);
if size == -1 {
return Err(Error::last_error());
}
let mut array: Vec<NodeDevice> = Vec::new();
for x in 0..size as isize {
array.push(NodeDevice::new(*nodedevs.offset(x)));
}
libc::free(nodedevs as *mut libc::c_void);
Ok(array)
}
}
pub fn list_all_secrets(
&self,
flags: sys::virConnectListAllSecretsFlags,
) -> Result<Vec<Secret>, Error> {
unsafe {
let mut secrets: *mut sys::virSecretPtr = ptr::null_mut();
let size =
sys::virConnectListAllSecrets(self.as_ptr(), &mut secrets, flags as libc::c_uint);
if size == -1 {
return Err(Error::last_error());
}
let mut array: Vec<Secret> = Vec::new();
for x in 0..size as isize {
array.push(Secret::new(*secrets.offset(x)));
}
libc::free(secrets as *mut libc::c_void);
Ok(array)
}
}
pub fn list_all_storage_pools(
&self,
flags: sys::virConnectListAllStoragePoolsFlags,
) -> Result<Vec<StoragePool>, Error> {
unsafe {
let mut storages: *mut sys::virStoragePoolPtr = ptr::null_mut();
let size = sys::virConnectListAllStoragePools(
self.as_ptr(),
&mut storages,
flags as libc::c_uint,
);
if size == -1 {
return Err(Error::last_error());
}
let mut array: Vec<StoragePool> = Vec::new();
for x in 0..size as isize {
array.push(StoragePool::new(*storages.offset(x)));
}
libc::free(storages as *mut libc::c_void);
Ok(array)
}
}
pub fn list_all_nw_filters(&self, flags: u32) -> Result<Vec<NWFilter>, Error> {
unsafe {
let mut filters: *mut sys::virNWFilterPtr = ptr::null_mut();
let size =
sys::virConnectListAllNWFilters(self.as_ptr(), &mut filters, flags as libc::c_uint);
if size == -1 {
return Err(Error::last_error());
}
let mut array: Vec<NWFilter> = Vec::new();
for x in 0..size as isize {
array.push(NWFilter::new(*filters.offset(x)));
}
libc::free(filters as *mut libc::c_void);
Ok(array)
}
}
#[allow(clippy::needless_range_loop)]
pub fn list_defined_domains(&self) -> Result<Vec<String>, Error> {
unsafe {
let mut names: [*mut libc::c_char; 1024] = [ptr::null_mut(); 1024];
let size = sys::virConnectListDefinedDomains(self.as_ptr(), names.as_mut_ptr(), 1024);
if size == -1 {
return Err(Error::last_error());
}
let mut array: Vec<String> = Vec::new();
for x in 0..size as usize {
array.push(c_chars_to_string!(names[x]));
}
Ok(array)
}
}
#[allow(clippy::needless_range_loop)]
pub fn list_defined_interfaces(&self) -> Result<Vec<String>, Error> {
unsafe {
let mut names: [*mut libc::c_char; 1024] = [ptr::null_mut(); 1024];
let size =
sys::virConnectListDefinedInterfaces(self.as_ptr(), names.as_mut_ptr(), 1024);
if size == -1 {
return Err(Error::last_error());
}
let mut array: Vec<String> = Vec::new();
for x in 0..size as usize {
array.push(c_chars_to_string!(names[x]));
}
Ok(array)
}
}
#[allow(clippy::needless_range_loop)]
pub fn list_defined_storage_pools(&self) -> Result<Vec<String>, Error> {
unsafe {
let mut names: [*mut libc::c_char; 1024] = [ptr::null_mut(); 1024];
let size =
sys::virConnectListDefinedStoragePools(self.as_ptr(), names.as_mut_ptr(), 1024);
if size == -1 {
return Err(Error::last_error());
}
let mut array: Vec<String> = Vec::new();
for x in 0..size as usize {
array.push(c_chars_to_string!(names[x]));
}
Ok(array)
}
}
#[allow(clippy::needless_range_loop)]
pub fn list_defined_networks(&self) -> Result<Vec<String>, Error> {
unsafe {
let mut names: [*mut libc::c_char; 1024] = [ptr::null_mut(); 1024];
let size = sys::virConnectListDefinedNetworks(self.as_ptr(), names.as_mut_ptr(), 1024);
if size == -1 {
return Err(Error::last_error());
}
let mut array: Vec<String> = Vec::new();
for x in 0..size as usize {
array.push(c_chars_to_string!(names[x]));
}
Ok(array)
}
}
pub fn num_of_domains(&self) -> Result<u32, Error> {
unsafe {
let num = sys::virConnectNumOfDomains(self.as_ptr());
if num == -1 {
return Err(Error::last_error());
}
Ok(num as u32)
}
}
pub fn num_of_interfaces(&self) -> Result<u32, Error> {
unsafe {
let num = sys::virConnectNumOfInterfaces(self.as_ptr());
if num == -1 {
return Err(Error::last_error());
}
Ok(num as u32)
}
}
pub fn num_of_networks(&self) -> Result<u32, Error> {
unsafe {
let num = sys::virConnectNumOfNetworks(self.as_ptr());
if num == -1 {
return Err(Error::last_error());
}
Ok(num as u32)
}
}
pub fn num_of_storage_pools(&self) -> Result<u32, Error> {
unsafe {
let num = sys::virConnectNumOfStoragePools(self.as_ptr());
if num == -1 {
return Err(Error::last_error());
}
Ok(num as u32)
}
}
pub fn num_of_nw_filters(&self) -> Result<u32, Error> {
unsafe {
let num = sys::virConnectNumOfNWFilters(self.as_ptr());
if num == -1 {
return Err(Error::last_error());
}
Ok(num as u32)
}
}
pub fn num_of_secrets(&self) -> Result<u32, Error> {
unsafe {
let num = sys::virConnectNumOfSecrets(self.as_ptr());
if num == -1 {
return Err(Error::last_error());
}
Ok(num as u32)
}
}
pub fn num_of_defined_domains(&self) -> Result<u32, Error> {
unsafe {
let num = sys::virConnectNumOfDefinedDomains(self.as_ptr());
if num == -1 {
return Err(Error::last_error());
}
Ok(num as u32)
}
}
pub fn num_of_defined_interfaces(&self) -> Result<u32, Error> {
unsafe {
let num = sys::virConnectNumOfDefinedInterfaces(self.as_ptr());
if num == -1 {
return Err(Error::last_error());
}
Ok(num as u32)
}
}
pub fn num_of_defined_networks(&self) -> Result<u32, Error> {
unsafe {
let num = sys::virConnectNumOfDefinedNetworks(self.as_ptr());
if num == -1 {
return Err(Error::last_error());
}
Ok(num as u32)
}
}
pub fn num_of_defined_storage_pools(&self) -> Result<u32, Error> {
unsafe {
let num = sys::virConnectNumOfDefinedStoragePools(self.as_ptr());
if num == -1 {
return Err(Error::last_error());
}
Ok(num as u32)
}
}
pub fn get_hyp_version(&self) -> Result<u32, Error> {
unsafe {
let mut hyver: libc::c_ulong = 0;
if sys::virConnectGetVersion(self.as_ptr(), &mut hyver) == -1 {
return Err(Error::last_error());
}
Ok(hyver as u32)
}
}
pub fn compare_cpu(
&self,
xml: &str,
flags: sys::virConnectCompareCPUFlags,
) -> Result<sys::virCPUCompareResult, Error> {
unsafe {
let res = sys::virConnectCompareCPU(
self.as_ptr(),
string_to_c_chars!(xml),
flags as libc::c_uint,
);
if res == sys::VIR_CPU_COMPARE_ERROR {
return Err(Error::last_error());
}
Ok(res as sys::virCPUCompareResult)
}
}
pub fn get_free_memory(&self) -> Result<u64, Error> {
unsafe {
let res = sys::virNodeGetFreeMemory(self.as_ptr());
if res == 0 {
return Err(Error::last_error());
}
Ok(res as u64)
}
}
pub fn get_node_info(&self) -> Result<NodeInfo, Error> {
unsafe {
let mut pinfo = mem::MaybeUninit::uninit();
let res = sys::virNodeGetInfo(self.as_ptr(), pinfo.as_mut_ptr());
if res == -1 {
return Err(Error::last_error());
}
let pinfo = pinfo.assume_init();
Ok(NodeInfo {
model: c_chars_to_string!(pinfo.model.as_ptr(), nofree),
memory: pinfo.memory as u64,
cpus: pinfo.cpus as u32,
mhz: pinfo.mhz as u32,
nodes: pinfo.nodes as u32,
sockets: pinfo.sockets as u32,
cores: pinfo.cores as u32,
threads: pinfo.threads as u32,
})
}
}
pub fn set_keep_alive(&self, interval: i32, count: u32) -> Result<i32, Error> {
unsafe {
let ret = sys::virConnectSetKeepAlive(
self.as_ptr(),
interval as libc::c_int,
count as libc::c_uint,
);
if ret == -1 {
return Err(Error::last_error());
}
Ok(ret as i32)
}
}
pub fn domain_xml_from_native(
&self,
nformat: &str,
nconfig: &str,
flags: u32,
) -> Result<String, Error> {
unsafe {
let ret = sys::virConnectDomainXMLFromNative(
self.as_ptr(),
string_to_c_chars!(nformat),
string_to_c_chars!(nconfig),
flags as libc::c_uint,
);
if ret.is_null() {
return Err(Error::last_error());
}
Ok(c_chars_to_string!(ret))
}
}
pub fn domain_xml_to_native(
&self,
nformat: &str,
dxml: &str,
flags: u32,
) -> Result<String, Error> {
unsafe {
let ret = sys::virConnectDomainXMLToNative(
self.as_ptr(),
string_to_c_chars!(nformat),
string_to_c_chars!(dxml),
flags as libc::c_uint,
);
if ret.is_null() {
return Err(Error::last_error());
}
Ok(c_chars_to_string!(ret))
}
}
pub fn get_domain_capabilities(
&self,
emulatorbin: &str,
arch: &str,
machine: &str,
virttype: &str,
flags: u32,
) -> Result<String, Error> {
unsafe {
let ret = sys::virConnectGetDomainCapabilities(
self.as_ptr(),
string_to_c_chars!(emulatorbin),
string_to_c_chars!(arch),
string_to_c_chars!(machine),
string_to_c_chars!(virttype),
flags as libc::c_uint,
);
if ret.is_null() {
return Err(Error::last_error());
}
Ok(c_chars_to_string!(ret))
}
}
pub fn get_all_domain_stats(
&self,
stats: u32,
flags: u32,
) -> Result<Vec<DomainStatsRecord>, Error> {
unsafe {
let mut record: *mut sys::virDomainStatsRecordPtr = ptr::null_mut();
let size = sys::virConnectGetAllDomainStats(
self.as_ptr(),
stats as libc::c_uint,
&mut record,
flags as libc::c_uint,
);
if size == -1 {
return Err(Error::last_error());
}
let mut array: Vec<DomainStatsRecord> = Vec::new();
for x in 0..size as isize {
array.push(DomainStatsRecord {
ptr: *record.offset(x),
});
}
libc::free(record as *mut libc::c_void);
Ok(array)
}
}
pub fn baseline_cpu(
&self,
xmlcpus: &[&str],
flags: sys::virConnectBaselineCPUFlags,
) -> Result<String, Error> {
unsafe {
let mut xcpus: [*const libc::c_char; 512] = [ptr::null_mut(); 512];
for x in 0..xmlcpus.len() {
xcpus[x] = string_to_c_chars!(xmlcpus[x]);
}
let ret = sys::virConnectBaselineCPU(
self.as_ptr(),
xcpus.as_mut_ptr(),
xmlcpus.len() as libc::c_uint,
flags as libc::c_uint,
);
if ret.is_null() {
return Err(Error::last_error());
}
Ok(c_chars_to_string!(ret))
}
}
pub fn find_storage_pool_sources(
&self,
kind: &str,
spec: &str,
flags: u32,
) -> Result<String, Error> {
unsafe {
let n = sys::virConnectFindStoragePoolSources(
self.as_ptr(),
string_to_c_chars!(kind),
string_to_c_chars!(spec),
flags as libc::c_uint,
);
if n.is_null() {
return Err(Error::last_error());
}
Ok(c_chars_to_string!(n))
}
}
}