mirror of
https://github.com/edera-dev/krata.git
synced 2025-08-03 13:11:31 +00:00
utilize async processing for console and child exit events
This commit is contained in:
@ -26,6 +26,8 @@ oci-spec = { workspace = true }
|
||||
backhand = { workspace = true }
|
||||
uuid = { workspace = true }
|
||||
ipnetwork = { workspace = true }
|
||||
tokio = { workspace = true }
|
||||
futures = { workspace = true }
|
||||
|
||||
[dependencies.krata]
|
||||
path = "../shared"
|
||||
|
@ -53,7 +53,8 @@ enum Commands {
|
||||
},
|
||||
}
|
||||
|
||||
fn main() -> Result<()> {
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<()> {
|
||||
env_logger::Builder::from_env(Env::default().default_filter_or("warn")).init();
|
||||
|
||||
let args = ControllerArgs::parse();
|
||||
@ -99,7 +100,7 @@ fn main() -> Result<()> {
|
||||
println!("launched container: {}", uuid);
|
||||
if attach {
|
||||
let mut console = ControllerConsole::new(&mut context);
|
||||
console.perform(&uuid.to_string())?;
|
||||
console.perform(&uuid.to_string()).await?;
|
||||
}
|
||||
}
|
||||
|
||||
@ -110,7 +111,7 @@ fn main() -> Result<()> {
|
||||
|
||||
Commands::Console { container } => {
|
||||
let mut console = ControllerConsole::new(&mut context);
|
||||
console.perform(&container)?;
|
||||
console.perform(&container).await?;
|
||||
}
|
||||
|
||||
Commands::List { .. } => {
|
||||
|
74
controller/src/console.rs
Normal file
74
controller/src/console.rs
Normal file
@ -0,0 +1,74 @@
|
||||
use std::{
|
||||
io::{stdin, stdout},
|
||||
os::fd::{AsRawFd, FromRawFd},
|
||||
};
|
||||
|
||||
use anyhow::Result;
|
||||
use futures::future::join_all;
|
||||
use log::warn;
|
||||
use std::process::exit;
|
||||
use termion::raw::IntoRawMode;
|
||||
use tokio::{
|
||||
fs::File,
|
||||
io::{AsyncReadExt, AsyncWriteExt},
|
||||
};
|
||||
|
||||
pub struct XenConsole {
|
||||
xen_read_handle: File,
|
||||
xen_write_handle: File,
|
||||
}
|
||||
|
||||
impl XenConsole {
|
||||
pub async fn new(tty: &str) -> Result<XenConsole> {
|
||||
let xen_read_handle = File::options().read(true).write(false).open(tty).await?;
|
||||
let xen_write_handle = File::options().read(false).write(true).open(tty).await?;
|
||||
Ok(XenConsole {
|
||||
xen_read_handle,
|
||||
xen_write_handle,
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn attach(self) -> Result<()> {
|
||||
let stdin = stdin();
|
||||
let terminal = stdout().into_raw_mode()?;
|
||||
let stdout = unsafe { File::from_raw_fd(terminal.as_raw_fd()) };
|
||||
let reader_task = tokio::task::spawn(async move {
|
||||
if let Err(error) = XenConsole::copy_stdout(stdout, self.xen_read_handle).await {
|
||||
warn!("failed to copy console output: {}", error);
|
||||
}
|
||||
});
|
||||
let writer_task = tokio::task::spawn(async move {
|
||||
if let Err(error) = XenConsole::intercept_stdin(
|
||||
unsafe { File::from_raw_fd(stdin.as_raw_fd()) },
|
||||
self.xen_write_handle,
|
||||
)
|
||||
.await
|
||||
{
|
||||
warn!("failed to intercept stdin: {}", error);
|
||||
}
|
||||
});
|
||||
|
||||
join_all(vec![reader_task, writer_task]).await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn copy_stdout(mut stdout: File, mut console: File) -> Result<()> {
|
||||
let mut buffer = vec![0u8; 256];
|
||||
loop {
|
||||
let size = console.read(&mut buffer).await?;
|
||||
stdout.write_all(&buffer[0..size]).await?;
|
||||
stdout.flush().await?;
|
||||
}
|
||||
}
|
||||
|
||||
async fn intercept_stdin(mut stdin: File, mut console: File) -> Result<()> {
|
||||
let mut buffer = vec![0u8; 60];
|
||||
loop {
|
||||
let size = stdin.read(&mut buffer).await?;
|
||||
if size == 1 && buffer[0] == 0x1d {
|
||||
exit(0);
|
||||
}
|
||||
console.write_all(&buffer[0..size]).await?;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,11 +1,6 @@
|
||||
use std::{
|
||||
io::{self, Read, Write},
|
||||
process::exit,
|
||||
thread,
|
||||
};
|
||||
|
||||
use anyhow::{anyhow, Result};
|
||||
use termion::raw::IntoRawMode;
|
||||
|
||||
use crate::console::XenConsole;
|
||||
|
||||
use super::ControllerContext;
|
||||
|
||||
@ -18,49 +13,15 @@ impl ControllerConsole<'_> {
|
||||
ControllerConsole { context }
|
||||
}
|
||||
|
||||
pub fn perform(&mut self, id: &str) -> Result<()> {
|
||||
pub async fn perform(&mut self, id: &str) -> Result<()> {
|
||||
let info = self
|
||||
.context
|
||||
.resolve(id)?
|
||||
.ok_or_else(|| anyhow!("unable to resolve container: {}", id))?;
|
||||
let domid = info.domid;
|
||||
let (mut read, mut write) = self.context.xen.open_console(domid)?;
|
||||
let mut stdin = io::stdin();
|
||||
let is_tty = termion::is_tty(&stdin);
|
||||
let mut stdout_for_exit = io::stdout().into_raw_mode()?;
|
||||
thread::spawn(move || {
|
||||
let mut buffer = vec![0u8; 60];
|
||||
loop {
|
||||
let size = stdin.read(&mut buffer).expect("failed to read stdin");
|
||||
if is_tty && size == 1 && buffer[0] == 0x1d {
|
||||
stdout_for_exit
|
||||
.suspend_raw_mode()
|
||||
.expect("failed to disable raw mode");
|
||||
stdout_for_exit.flush().expect("failed to flush stdout");
|
||||
exit(0);
|
||||
}
|
||||
write
|
||||
.write_all(&buffer[0..size])
|
||||
.expect("failed to write to domain console");
|
||||
write.flush().expect("failed to flush domain console");
|
||||
}
|
||||
});
|
||||
|
||||
let mut buffer = vec![0u8; 256];
|
||||
if is_tty {
|
||||
let mut stdout = io::stdout().into_raw_mode()?;
|
||||
loop {
|
||||
let size = read.read(&mut buffer)?;
|
||||
stdout.write_all(&buffer[0..size])?;
|
||||
stdout.flush()?;
|
||||
}
|
||||
} else {
|
||||
let mut stdout = io::stdout();
|
||||
loop {
|
||||
let size = read.read(&mut buffer)?;
|
||||
stdout.write_all(&buffer[0..size])?;
|
||||
stdout.flush()?;
|
||||
}
|
||||
}
|
||||
let tty = self.context.xen.get_console_path(domid)?;
|
||||
let console = XenConsole::new(&tty).await?;
|
||||
console.attach().await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -4,10 +4,11 @@ use advmac::MacAddr6;
|
||||
use anyhow::{anyhow, Result};
|
||||
use ipnetwork::Ipv4Network;
|
||||
use krata::{
|
||||
LaunchInfo, LaunchNetwork, LaunchNetworkIpv4, LaunchNetworkIpv6, LaunchNetworkResolver,
|
||||
LaunchChannels, LaunchInfo, LaunchNetwork, LaunchNetworkIpv4, LaunchNetworkIpv6,
|
||||
LaunchNetworkResolver,
|
||||
};
|
||||
use uuid::Uuid;
|
||||
use xenclient::{DomainConfig, DomainDisk, DomainNetworkInterface};
|
||||
use xenclient::{DomainConfig, DomainDisk, DomainEventChannel, DomainNetworkInterface};
|
||||
use xenstore::client::XsdInterface;
|
||||
|
||||
use crate::image::{name::ImageName, ImageCompiler, ImageInfo};
|
||||
@ -75,6 +76,9 @@ impl ControllerLaunch<'_> {
|
||||
}),
|
||||
env: request.env,
|
||||
run: request.run,
|
||||
channels: LaunchChannels {
|
||||
exit: "krata-exit".to_string(),
|
||||
},
|
||||
};
|
||||
|
||||
let cfgblk = ConfigBlock::new(&uuid, &image_info)?;
|
||||
@ -133,6 +137,7 @@ impl ControllerLaunch<'_> {
|
||||
script: None,
|
||||
}],
|
||||
filesystems: vec![],
|
||||
event_channels: vec![DomainEventChannel { name: "krata-exit" }],
|
||||
extra_keys: vec![
|
||||
("krata/uuid".to_string(), uuid.to_string()),
|
||||
(
|
||||
|
@ -1,3 +1,4 @@
|
||||
pub mod autoloop;
|
||||
pub mod console;
|
||||
pub mod ctl;
|
||||
pub mod image;
|
||||
|
Reference in New Issue
Block a user