2024-02-23 03:25:06 +00:00
|
|
|
use anyhow::Result;
|
2024-03-06 12:05:01 +00:00
|
|
|
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,
|
|
|
|
};
|
2024-03-27 02:54:39 +00:00
|
|
|
use krata::{
|
|
|
|
events::EventStream,
|
|
|
|
v1::{
|
|
|
|
common::GuestStatus,
|
|
|
|
control::{watch_events_reply::Event, ConsoleDataReply, ConsoleDataRequest},
|
|
|
|
},
|
2024-03-13 11:34:52 +00:00
|
|
|
};
|
2024-03-15 17:36:26 +00:00
|
|
|
use log::debug;
|
2024-02-23 03:25:06 +00:00
|
|
|
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,
|
2024-02-23 03:25:06 +00:00
|
|
|
};
|
2024-03-06 12:05:01 +00:00
|
|
|
use tokio_stream::{Stream, StreamExt};
|
|
|
|
use tonic::Streaming;
|
2024-02-23 03:25:06 +00:00
|
|
|
|
2024-03-06 12:05:01 +00:00
|
|
|
pub struct StdioConsoleStream;
|
2024-02-23 03:25:06 +00:00
|
|
|
|
2024-03-06 12:05:01 +00:00
|
|
|
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![] };
|
2024-03-05 11:35:25 +00:00
|
|
|
|
2024-03-06 12:05:01 +00:00
|
|
|
let mut buffer = vec![0u8; 60];
|
|
|
|
loop {
|
|
|
|
let size = match stdin.read(&mut buffer).await {
|
|
|
|
Ok(size) => size,
|
2024-03-05 11:35:25 +00:00
|
|
|
Err(error) => {
|
2024-03-06 12:05:01 +00:00
|
|
|
debug!("failed to read stdin: {}", error);
|
|
|
|
break;
|
2024-03-05 11:35:25 +00:00
|
|
|
}
|
2024-03-06 12:05:01 +00:00
|
|
|
};
|
|
|
|
let data = buffer[0..size].to_vec();
|
|
|
|
if size == 1 && buffer[0] == 0x1d {
|
|
|
|
break;
|
2024-03-05 11:35:25 +00:00
|
|
|
}
|
2024-03-06 15:57:56 +00:00
|
|
|
yield ConsoleDataRequest { guest_id: String::default(), data };
|
2024-03-06 12:05:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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();
|
2024-03-06 12:05:01 +00:00
|
|
|
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?;
|
2024-02-23 03:25:06 +00:00
|
|
|
}
|
2024-03-05 11:35:25 +00:00
|
|
|
Ok(())
|
2024-02-23 03:25:06 +00:00
|
|
|
}
|
2024-03-13 11:34:52 +00:00
|
|
|
|
2024-03-15 17:36:26 +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 {
|
2024-03-14 23:29:07 +00:00
|
|
|
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;
|
|
|
|
};
|
|
|
|
|
2024-03-14 23:29:07 +00:00
|
|
|
let Some(state) = guest.state else {
|
|
|
|
continue;
|
|
|
|
};
|
2024-03-14 14:03:11 +00:00
|
|
|
|
2024-03-14 23:29:07 +00:00
|
|
|
if guest.id != id {
|
|
|
|
continue;
|
|
|
|
}
|
2024-03-13 11:34:52 +00:00
|
|
|
|
2024-03-14 23:29:07 +00:00
|
|
|
if let Some(exit_info) = state.exit_info {
|
2024-03-15 17:36:26 +00:00
|
|
|
return Some(exit_info.code);
|
2024-03-14 23:29:07 +00:00
|
|
|
}
|
2024-03-13 11:34:52 +00:00
|
|
|
|
2024-03-15 17:36:26 +00:00
|
|
|
let status = state.status();
|
2024-03-23 07:00:12 +00:00
|
|
|
if status == GuestStatus::Destroying || status == GuestStatus::Destroyed {
|
2024-03-15 17:36:26 +00:00
|
|
|
return Some(10);
|
2024-03-13 11:34:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2024-03-15 17:36:26 +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();
|
|
|
|
}
|
|
|
|
}
|
2024-02-23 03:25:06 +00:00
|
|
|
}
|