mirror of
				https://github.com/edera-dev/krata.git
				synced 2025-11-04 07:39:39 +00:00 
			
		
		
		
	utilize async processing for console and child exit events
This commit is contained in:
		
							
								
								
									
										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