Files
krata/crates/ctl/src/console.rs

116 lines
3.4 KiB
Rust
Raw Normal View History

use anyhow::Result;
use async_stream::stream;
2024-03-21 22:49:37 -07:00
use crossterm::{
terminal::{disable_raw_mode, enable_raw_mode, is_raw_mode_enabled},
tty::IsTty,
};
use krata::{
events::EventStream,
v1::{
common::GuestStatus,
control::{watch_events_reply::Event, ConsoleDataReply, ConsoleDataRequest},
},
2024-03-13 11:34:52 +00:00
};
use log::debug;
use tokio::{
2024-03-21 22:49:37 -07:00
io::{stdin, stdout, AsyncReadExt, AsyncWriteExt},
2024-03-13 11:34:52 +00:00
task::JoinHandle,
};
use tokio_stream::{Stream, StreamExt};
use tonic::Streaming;
pub struct StdioConsoleStream;
impl StdioConsoleStream {
pub async fn stdin_stream(guest: String) -> impl Stream<Item = ConsoleDataRequest> {
let mut stdin = stdin();
stream! {
2024-03-06 15:57:56 +00:00
yield ConsoleDataRequest { guest_id: guest, data: vec![] };
let mut buffer = vec![0u8; 60];
loop {
let size = match stdin.read(&mut buffer).await {
Ok(size) => size,
Err(error) => {
debug!("failed to read stdin: {}", error);
break;
}
};
let data = buffer[0..size].to_vec();
if size == 1 && buffer[0] == 0x1d {
break;
}
2024-03-06 15:57:56 +00:00
yield ConsoleDataRequest { guest_id: String::default(), data };
}
}
}
pub async fn stdout(mut stream: Streaming<ConsoleDataReply>) -> Result<()> {
2024-03-21 22:49:37 -07:00
if stdin().is_tty() {
enable_raw_mode()?;
StdioConsoleStream::register_terminal_restore_hook()?;
}
let mut stdout = stdout();
while let Some(reply) = stream.next().await {
let reply = reply?;
if reply.data.is_empty() {
continue;
}
stdout.write_all(&reply.data).await?;
stdout.flush().await?;
}
Ok(())
}
2024-03-13 11:34:52 +00:00
pub async fn guest_exit_hook(
id: String,
events: EventStream,
) -> Result<JoinHandle<Option<i32>>> {
2024-03-13 11:34:52 +00:00
Ok(tokio::task::spawn(async move {
let mut stream = events.subscribe();
while let Ok(event) = stream.recv().await {
match event {
Event::GuestChanged(changed) => {
let Some(guest) = changed.guest else {
2024-03-13 11:34:52 +00:00
continue;
};
let Some(state) = guest.state else {
continue;
};
2024-03-14 14:03:11 +00:00
if guest.id != id {
continue;
}
2024-03-13 11:34:52 +00:00
if let Some(exit_info) = state.exit_info {
return Some(exit_info.code);
}
2024-03-13 11:34:52 +00:00
let status = state.status();
if status == GuestStatus::Destroying || status == GuestStatus::Destroyed {
return Some(10);
2024-03-13 11:34:52 +00:00
}
}
}
}
None
2024-03-13 11:34:52 +00:00
}))
}
2024-03-21 22:49:37 -07:00
fn register_terminal_restore_hook() -> Result<()> {
if stdin().is_tty() {
ctrlc::set_handler(move || {
StdioConsoleStream::restore_terminal_mode();
})?;
}
Ok(())
}
pub fn restore_terminal_mode() {
if is_raw_mode_enabled().unwrap_or(false) {
let _ = disable_raw_mode();
}
}
}