use std::{
env,
io::{self, Read, Write},
os::unix::io::AsRawFd,
sync::{atomic::AtomicBool, Arc},
};
use termios::{Termios, ECHO, ICANON, ISIG, TCSANOW};
use virt::{
connect::Connect,
domain::Domain,
event::{event_add_handle, event_register_default_impl, event_run_default_impl},
stream::Stream,
sys::{
virStreamEventType, VIR_DOMAIN_CONSOLE_FORCE, VIR_EVENT_HANDLE_READABLE,
VIR_STREAM_EVENT_READABLE, VIR_STREAM_NONBLOCK,
},
};
pub struct Console {
pub st: Stream,
pub cond: Arc<AtomicBool>,
}
impl Console {
pub fn new(st: Stream) -> Self {
Console {
st,
cond: Arc::new(AtomicBool::new(true)),
}
}
}
fn read_callback(stream: &Stream, event_type: virStreamEventType) {
if event_type == VIR_STREAM_EVENT_READABLE {
let mut buf = vec![0; 1024];
let stdout = io::stdout();
let mut stdout = stdout.lock();
match stream.recv(buf.as_mut_slice()) {
Ok(_) => {
stdout.write_all(&buf).unwrap();
}
Err(e) => {
println!("{e}");
}
}
}
}
fn stdin_callback(
_watch: libc::c_int,
_fd: libc::c_int,
events: libc::c_int,
console_ptr: *mut libc::c_void,
) {
if events == VIR_EVENT_HANDLE_READABLE as libc::c_int {
let stdin = io::stdin();
let mut stdin = stdin.lock();
let con = unsafe { &mut *(console_ptr as *mut Console) };
let mut buf = [0; 1];
if stdin.read(&mut buf).is_ok() {
if buf[0] == 29 {
con.cond.store(false, std::sync::atomic::Ordering::SeqCst);
}
let _ = con.st.send(&buf);
}
}
}
#[allow(dead_code)]
fn timer_task(_timer: libc::c_int, _opaque: *mut libc::c_void) {
println!("timer!");
}
fn set_raw_mode() -> Termios {
let stdin = io::stdin();
let fd = stdin.as_raw_fd();
let mut termios = Termios::from_fd(fd).unwrap();
let orig_termios = termios;
termios.c_lflag &= !(ICANON | ECHO | ISIG);
termios::tcsetattr(fd, TCSANOW, &termios).unwrap();
orig_termios
}
fn reset_mode(orig_termios: Termios) {
let stdin = io::stdin();
let fd = stdin.as_raw_fd();
termios::tcsetattr(fd, TCSANOW, &orig_termios).unwrap();
println!();
}
fn main() {
println!("Try to connect via console");
println!("Escape character is ^] (Ctrl + ])");
let uri = env::args().nth(1);
let name = env::args().nth(2).unwrap();
let dev_name = env::args().nth(3);
let _ = event_register_default_impl();
let conn = Connect::open(uri.as_deref()).unwrap();
let dom = Domain::lookup_by_name(&conn, &name).unwrap();
let st = Stream::new(&conn, VIR_STREAM_NONBLOCK).unwrap();
dom.open_console(dev_name.as_deref(), &st, VIR_DOMAIN_CONSOLE_FORCE)
.unwrap();
let mut console = Console::new(st);
console
.st
.event_add_callback(VIR_STREAM_EVENT_READABLE, |st, event_type| {
read_callback(st, event_type)
})
.unwrap();
let console_ptr = &mut console as *mut Console as *mut libc::c_void;
let _ehw = event_add_handle(
0,
VIR_EVENT_HANDLE_READABLE,
|watch, fd, events, opaque| stdin_callback(watch, fd, events as libc::c_int, opaque),
console_ptr,
)
.unwrap();
let orig_termios = set_raw_mode();
while console.cond.load(std::sync::atomic::Ordering::SeqCst) {
let _ = event_run_default_impl();
}
reset_mode(orig_termios);
}