This patch includes:
- Event callback handlers
- Tests related to events(410-430)
src/bin/event.rs and src/bin/event_leak.rs
are the PoCs that Boxes related to callbacks are
not leaked.
---
rust/src/bin/.gitkeep | 0
rust/src/bin/event.rs | 29 ++++++
rust/src/bin/event_leak.rs | 30 ++++++
rust/src/error.rs | 6 ++
rust/src/event.rs | 146 ++++++++++++++++++++++++++++
rust/tests/410_close_event.rs | 39 ++++++++
rust/tests/420_log_messages.rs | 60 ++++++++++++
rust/tests/430_progress_messages.rs | 59 +++++++++++
8 files changed, 369 insertions(+)
delete mode 100644 rust/src/bin/.gitkeep
create mode 100644 rust/src/bin/event.rs
create mode 100644 rust/src/bin/event_leak.rs
create mode 100644 rust/tests/410_close_event.rs
create mode 100644 rust/tests/420_log_messages.rs
create mode 100644 rust/tests/430_progress_messages.rs
diff --git a/rust/src/bin/.gitkeep b/rust/src/bin/.gitkeep
deleted file mode 100644
index e69de29bb..000000000
diff --git a/rust/src/bin/event.rs b/rust/src/bin/event.rs
new file mode 100644
index 000000000..3a3aa7d7c
--- /dev/null
+++ b/rust/src/bin/event.rs
@@ -0,0 +1,29 @@
+extern crate guestfs;
+use guestfs::*;
+
+fn main() {
+ for _ in 0..256 {
+ let mut g = match Handle::create() {
+ Ok(g) => g,
+ Err(e) => panic!(format!(" could not create handle {:?}", e)),
+ };
+ g.set_event_callback(
+ |e, _, _, _| match e {
+ Event::Close => print!("c"),
+ _ => print!("o"),
+ },
+ &EVENT_ALL,
+ )
+ .unwrap();
+ let eh = g
+ .set_event_callback(|_, _, _, _| print!("n"), &EVENT_ALL)
+ .unwrap();
+ g.set_trace(true).unwrap();
+ g.delete_event_callback(eh).unwrap();
+ g.set_trace(false).unwrap();
+ }
+ let _v = vec![0; 1024 * 1024];
+ // no leak
+ // mem::forget(v);
+ println!()
+}
diff --git a/rust/src/bin/event_leak.rs b/rust/src/bin/event_leak.rs
new file mode 100644
index 000000000..176de3c9a
--- /dev/null
+++ b/rust/src/bin/event_leak.rs
@@ -0,0 +1,30 @@
+extern crate guestfs;
+use guestfs::*;
+use std::mem;
+
+fn main() {
+ for _ in 0..256 {
+ let mut g = match Handle::create() {
+ Ok(g) => g,
+ Err(e) => panic!(format!(" could not create handle {:?}", e)),
+ };
+ g.set_event_callback(
+ |e, _, _, _| match e {
+ Event::Close => print!("c"),
+ _ => print!("o"),
+ },
+ &EVENT_ALL,
+ )
+ .unwrap();
+ let eh = g
+ .set_event_callback(|_, _, _, _| print!("n"), &EVENT_ALL)
+ .unwrap();
+ g.set_trace(true).unwrap();
+ g.delete_event_callback(eh).unwrap();
+ g.set_trace(false).unwrap();
+ }
+ let v = vec![0; 1024 * 1024];
+ // leak
+ mem::forget(v);
+ println!()
+}
diff --git a/rust/src/error.rs b/rust/src/error.rs
index e526121e8..ce444e199 100644
--- a/rust/src/error.rs
+++ b/rust/src/error.rs
@@ -20,6 +20,7 @@ use crate::base;
use crate::utils;
use std::convert;
use std::ffi;
+use std::io;
use std::os::raw::{c_char, c_int};
use std::str;
@@ -41,6 +42,7 @@ pub enum Error {
API(APIError),
IllegalString(ffi::NulError),
Utf8Error(str::Utf8Error),
+ UnixError(io::Error, &'static str),
Create,
}
@@ -56,6 +58,10 @@ impl convert::From<str::Utf8Error> for Error {
}
}
+pub(crate) fn unix_error(operation: &'static str) -> Error {
+ Error::UnixError(io::Error::last_os_error(), operation)
+}
+
impl<'a> base::Handle<'a> {
pub(crate) fn get_error_from_handle(&self, operation: &'static str) ->
Error {
let c_msg = unsafe { guestfs_last_error(self.g) };
diff --git a/rust/src/event.rs b/rust/src/event.rs
index c363e913a..752e73610 100644
--- a/rust/src/event.rs
+++ b/rust/src/event.rs
@@ -1,4 +1,150 @@
+/* libguestfs Rust bindings
+ * Copyright (C) 2019 Hiroyuki Katsura <hiroyuki.katsura.0513(a)gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+use crate::base;
+use crate::error;
+use crate::guestfs;
+use crate::utils;
+use std::os::raw::{c_char, c_void};
+use std::slice;
+
+type GuestfsEventCallback = extern "C" fn(
+ *const base::guestfs_h,
+ *const c_void,
+ u64,
+ i32,
+ i32,
+ *const i8,
+ usize,
+ *const u64,
+ usize,
+);
+
+#[link(name = "guestfs")]
+extern "C" {
+ fn guestfs_set_event_callback(
+ g: *const base::guestfs_h,
+ cb: GuestfsEventCallback,
+ event_bitmask: u64,
+ flags: i32,
+ opaque: *const c_void,
+ ) -> i32;
+ fn guestfs_delete_event_callback(g: *const base::guestfs_h, eh: i32);
+ fn guestfs_event_to_string(bitmask: u64) -> *const c_char;
+ fn free(buf: *const c_void);
+}
+
#[derive(Hash, PartialEq, Eq)]
pub struct EventHandle {
eh: i32,
}
+
+fn events_to_bitmask(v: &[guestfs::Event]) -> u64 {
+ let mut r = 0u64;
+ for x in v.iter() {
+ r |= x.to_u64();
+ }
+ r
+}
+
+pub fn event_to_string(events: &[guestfs::Event]) -> Result<String,
error::Error> {
+ let bitmask = events_to_bitmask(events);
+
+ let r = unsafe { guestfs_event_to_string(bitmask) };
+ if r.is_null() {
+ Err(error::unix_error("event_to_string"))
+ } else {
+ let s = unsafe { utils::char_ptr_to_string(r) };
+ unsafe { free(r as *const c_void) };
+ Ok(s?)
+ }
+}
+
+impl<'a> base::Handle<'a> {
+ pub fn set_event_callback<C: 'a>(
+ &mut self,
+ callback: C,
+ events: &[guestfs::Event],
+ ) -> Result<EventHandle, error::Error>
+ where
+ C: Fn(guestfs::Event, EventHandle, &[u8], &[u64]) + 'a,
+ {
+ extern "C" fn trampoline<C>(
+ _g: *const base::guestfs_h,
+ opaque: *const c_void,
+ event: u64,
+ event_handle: i32,
+ _flags: i32,
+ buf: *const c_char,
+ buf_len: usize,
+ array: *const u64,
+ array_len: usize,
+ ) where
+ C: Fn(guestfs::Event, EventHandle, &[u8], &[u64]),
+ {
+ // trampoline function
+ // c.f.
https://s3.amazonaws.com/temp.michaelfbryan.com/callbacks/index.html
+
+ let event = match guestfs::Event::from_bitmask(event) {
+ Some(x) => x,
+ None => panic!("Failed to parse bitmask: {}", event),
+ };
+ let eh = EventHandle { eh: event_handle };
+ let buf = unsafe { slice::from_raw_parts(buf as *const u8, buf_len) };
+ let array = unsafe { slice::from_raw_parts(array, array_len) };
+
+ let callback: &Box<dyn Fn(guestfs::Event, EventHandle, &[u8],
&[u64])> =
+ Box::leak(unsafe { Box::from_raw(opaque as *mut _) });
+ callback(event, eh, buf, array)
+ }
+
+ // Because trait pointer is fat pointer, in order to pass it to API,
+ // double Box is used.
+ let callback: Box<Box<dyn Fn(guestfs::Event, EventHandle, &[u8],
&[u64]) + 'a>> =
+ Box::new(Box::new(callback));
+ let ptr = Box::into_raw(callback);
+ let callback = unsafe { Box::from_raw(ptr) };
+ let event_bitmask = events_to_bitmask(events);
+
+ let eh = {
+ unsafe {
+ guestfs_set_event_callback(
+ self.g,
+ trampoline::<C>,
+ event_bitmask,
+ 0,
+ ptr as *const c_void,
+ )
+ }
+ };
+ if eh == -1 {
+ return Err(self.get_error_from_handle("set_event_callback"));
+ }
+ self.callbacks.insert(EventHandle { eh }, callback);
+
+ Ok(EventHandle { eh })
+ }
+
+ pub fn delete_event_callback(&mut self, eh: EventHandle) -> Result<(),
error::Error> {
+ unsafe {
+ guestfs_delete_event_callback(self.g, eh.eh);
+ }
+ self.callbacks.remove(&eh);
+ Ok(())
+ }
+}
diff --git a/rust/tests/410_close_event.rs b/rust/tests/410_close_event.rs
new file mode 100644
index 000000000..e22474d36
--- /dev/null
+++ b/rust/tests/410_close_event.rs
@@ -0,0 +1,39 @@
+/* libguestfs Rust bindings
+ * Copyright (C) 2019 Hiroyuki Katsura <hiroyuki.katsura.0513(a)gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+extern crate guestfs;
+
+use std::cell::RefCell;
+use std::rc::Rc;
+
+#[test]
+fn close_event() {
+ let close_invoked = Rc::new(RefCell::new(0));
+ {
+ let mut g = guestfs::Handle::create().expect("create");
+ g.set_event_callback(
+ |_, _, _, _| {
+ *close_invoked.borrow_mut() += 1;
+ },
+ &[guestfs::Event::Close],
+ )
+ .unwrap();
+ assert_eq!(*close_invoked.borrow(), 0);
+ }
+ assert_eq!(*close_invoked.borrow(), 1);
+}
diff --git a/rust/tests/420_log_messages.rs b/rust/tests/420_log_messages.rs
new file mode 100644
index 000000000..78365995a
--- /dev/null
+++ b/rust/tests/420_log_messages.rs
@@ -0,0 +1,60 @@
+/* libguestfs Rust bindings
+ * Copyright (C) 2019 Hiroyuki Katsura <hiroyuki.katsura.0513(a)gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+extern crate guestfs;
+
+use std::cell::RefCell;
+use std::rc::Rc;
+use std::str;
+
+#[test]
+fn log_messages() {
+ let callback_invoked = Rc::new(RefCell::new(0));
+ {
+ let mut g = guestfs::Handle::create().expect("create");
+ g.set_event_callback(
+ |ev, _, buf, array| {
+ *callback_invoked.borrow_mut() += 1;
+
+ let event = guestfs::event_to_string(&[ev]).unwrap();
+
+ let buf = str::from_utf8(buf).unwrap();
+ let array = array
+ .into_iter()
+ .map(|x| format!("{}", x))
+ .collect::<Vec<String>>()
+ .join(",");
+
+ eprintln!("event logged: event={} buf={} array={}", event, buf,
array)
+ },
+ &[
+ guestfs::Event::Appliance,
+ guestfs::Event::Library,
+ guestfs::Event::Warning,
+ guestfs::Event::Trace,
+ ],
+ )
+ .unwrap();
+
+ g.set_trace(true).unwrap();
+ g.set_verbose(true).unwrap();
+ g.add_drive_ro("/dev/null").unwrap();
+ g.set_autosync(true).unwrap();
+ }
+ assert!(*callback_invoked.borrow() > 0);
+}
diff --git a/rust/tests/430_progress_messages.rs b/rust/tests/430_progress_messages.rs
new file mode 100644
index 000000000..08d8b59e7
--- /dev/null
+++ b/rust/tests/430_progress_messages.rs
@@ -0,0 +1,59 @@
+/* libguestfs Rust bindings
+ * Copyright (C) 2019 Hiroyuki Katsura <hiroyuki.katsura.0513(a)gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+extern crate guestfs;
+
+use std::cell::RefCell;
+use std::default::Default;
+use std::rc::Rc;
+
+#[test]
+fn progress_messages() {
+ let callback_invoked = Rc::new(RefCell::new(0));
+ {
+ let mut g = guestfs::Handle::create().expect("create");
+ g.add_drive("/dev/null", Default::default()).unwrap();
+ g.launch().unwrap();
+
+ let eh = g
+ .set_event_callback(
+ |_, _, _, _| {
+ *callback_invoked.borrow_mut() += 1;
+ },
+ &[guestfs::Event::Progress],
+ )
+ .unwrap();
+ assert_eq!("ok", g.debug("progress",
&["5"]).unwrap());
+ assert!(*callback_invoked.borrow() > 0);
+
+ *callback_invoked.borrow_mut() = 0;
+ g.delete_event_callback(eh).unwrap();
+ assert_eq!("ok", g.debug("progress",
&["5"]).unwrap());
+ assert_eq!(*callback_invoked.borrow(), 0);
+
+ g.set_event_callback(
+ |_, _, _, _| {
+ *callback_invoked.borrow_mut() += 1;
+ },
+ &[guestfs::Event::Progress],
+ )
+ .unwrap();
+ assert_eq!("ok", g.debug("progress",
&["5"]).unwrap());
+ }
+ assert!(*callback_invoked.borrow() > 0);
+}
--
2.20.1 (Apple Git-117)