mirror of
				https://github.com/edera-dev/krata.git
				synced 2025-11-03 07:19:37 +00:00 
			
		
		
		
	hypha: implement console support
This commit is contained in:
		@ -24,6 +24,7 @@ sha256 = "1.5.0"
 | 
				
			|||||||
url = "2.5.0"
 | 
					url = "2.5.0"
 | 
				
			||||||
ureq = "2.9.1"
 | 
					ureq = "2.9.1"
 | 
				
			||||||
path-clean = "1.0.1"
 | 
					path-clean = "1.0.1"
 | 
				
			||||||
 | 
					termion = "3.0.0"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[dependencies.xenstore]
 | 
					[dependencies.xenstore]
 | 
				
			||||||
path = "../xenstore"
 | 
					path = "../xenstore"
 | 
				
			||||||
 | 
				
			|||||||
@ -6,12 +6,6 @@ use std::path::PathBuf;
 | 
				
			|||||||
#[derive(Parser, Debug)]
 | 
					#[derive(Parser, Debug)]
 | 
				
			||||||
#[command(version, about)]
 | 
					#[command(version, about)]
 | 
				
			||||||
struct ControllerArgs {
 | 
					struct ControllerArgs {
 | 
				
			||||||
    #[arg(short, long)]
 | 
					 | 
				
			||||||
    kernel: String,
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    #[arg(short = 'r', long)]
 | 
					 | 
				
			||||||
    initrd: String,
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    #[arg(short, long, default_value = "auto")]
 | 
					    #[arg(short, long, default_value = "auto")]
 | 
				
			||||||
    store: String,
 | 
					    store: String,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -22,6 +16,10 @@ struct ControllerArgs {
 | 
				
			|||||||
#[derive(Subcommand, Debug)]
 | 
					#[derive(Subcommand, Debug)]
 | 
				
			||||||
enum Commands {
 | 
					enum Commands {
 | 
				
			||||||
    Launch {
 | 
					    Launch {
 | 
				
			||||||
 | 
					        #[arg(short, long)]
 | 
				
			||||||
 | 
					        kernel: String,
 | 
				
			||||||
 | 
					        #[arg(short = 'r', long)]
 | 
				
			||||||
 | 
					        initrd: String,
 | 
				
			||||||
        #[arg(short, long)]
 | 
					        #[arg(short, long)]
 | 
				
			||||||
        image: String,
 | 
					        image: String,
 | 
				
			||||||
        #[arg(short, long, default_value_t = 1)]
 | 
					        #[arg(short, long, default_value_t = 1)]
 | 
				
			||||||
@ -33,6 +31,10 @@ enum Commands {
 | 
				
			|||||||
        #[arg(short, long)]
 | 
					        #[arg(short, long)]
 | 
				
			||||||
        domain: u32,
 | 
					        domain: u32,
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    Console {
 | 
				
			||||||
 | 
					        #[arg(short, long)]
 | 
				
			||||||
 | 
					        domain: u32,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fn main() -> Result<()> {
 | 
					fn main() -> Result<()> {
 | 
				
			||||||
@ -51,16 +53,27 @@ fn main() -> Result<()> {
 | 
				
			|||||||
        .map(|x| x.to_string())
 | 
					        .map(|x| x.to_string())
 | 
				
			||||||
        .ok_or_else(|| HyphaError::new("unable to convert store path to string"))?;
 | 
					        .ok_or_else(|| HyphaError::new("unable to convert store path to string"))?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let mut controller = Controller::new(store_path, args.kernel, args.initrd)?;
 | 
					    let mut controller = Controller::new(store_path)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    match args.command {
 | 
					    match args.command {
 | 
				
			||||||
        Commands::Launch { image, cpus, mem } => {
 | 
					        Commands::Launch {
 | 
				
			||||||
            let domid = controller.launch(image.as_str(), cpus, mem)?;
 | 
					            kernel,
 | 
				
			||||||
 | 
					            initrd,
 | 
				
			||||||
 | 
					            image,
 | 
				
			||||||
 | 
					            cpus,
 | 
				
			||||||
 | 
					            mem,
 | 
				
			||||||
 | 
					        } => {
 | 
				
			||||||
 | 
					            let domid = controller.launch(&kernel, &initrd, &image, cpus, mem)?;
 | 
				
			||||||
            println!("launched domain: {}", domid);
 | 
					            println!("launched domain: {}", domid);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Commands::Destroy { domain } => {
 | 
					        Commands::Destroy { domain } => {
 | 
				
			||||||
            controller.destroy(domain)?;
 | 
					            controller.destroy(domain)?;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Commands::Console { domain } => {
 | 
				
			||||||
 | 
					            controller.console(domain)?;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    Ok(())
 | 
					    Ok(())
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -7,8 +7,11 @@ use crate::image::cache::ImageCache;
 | 
				
			|||||||
use crate::image::name::ImageName;
 | 
					use crate::image::name::ImageName;
 | 
				
			||||||
use crate::image::{ImageCompiler, ImageInfo};
 | 
					use crate::image::{ImageCompiler, ImageInfo};
 | 
				
			||||||
use loopdev::LoopControl;
 | 
					use loopdev::LoopControl;
 | 
				
			||||||
use std::fs;
 | 
					use std::io::{Read, Write};
 | 
				
			||||||
use std::path::PathBuf;
 | 
					use std::path::PathBuf;
 | 
				
			||||||
 | 
					use std::process::exit;
 | 
				
			||||||
 | 
					use std::{fs, io, thread};
 | 
				
			||||||
 | 
					use termion::raw::IntoRawMode;
 | 
				
			||||||
use uuid::Uuid;
 | 
					use uuid::Uuid;
 | 
				
			||||||
use xenclient::{DomainConfig, DomainDisk, XenClient};
 | 
					use xenclient::{DomainConfig, DomainDisk, XenClient};
 | 
				
			||||||
use xenstore::client::{XsdClient, XsdInterface};
 | 
					use xenstore::client::{XsdClient, XsdInterface};
 | 
				
			||||||
@ -17,12 +20,10 @@ pub struct Controller {
 | 
				
			|||||||
    image_cache: ImageCache,
 | 
					    image_cache: ImageCache,
 | 
				
			||||||
    autoloop: AutoLoop,
 | 
					    autoloop: AutoLoop,
 | 
				
			||||||
    client: XenClient,
 | 
					    client: XenClient,
 | 
				
			||||||
    kernel_path: String,
 | 
					 | 
				
			||||||
    initrd_path: String,
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl Controller {
 | 
					impl Controller {
 | 
				
			||||||
    pub fn new(store_path: String, kernel_path: String, initrd_path: String) -> Result<Controller> {
 | 
					    pub fn new(store_path: String) -> Result<Controller> {
 | 
				
			||||||
        let mut image_cache_path = PathBuf::from(store_path);
 | 
					        let mut image_cache_path = PathBuf::from(store_path);
 | 
				
			||||||
        image_cache_path.push("cache");
 | 
					        image_cache_path.push("cache");
 | 
				
			||||||
        fs::create_dir_all(&image_cache_path)?;
 | 
					        fs::create_dir_all(&image_cache_path)?;
 | 
				
			||||||
@ -35,8 +36,6 @@ impl Controller {
 | 
				
			|||||||
            image_cache,
 | 
					            image_cache,
 | 
				
			||||||
            autoloop: AutoLoop::new(LoopControl::open()?),
 | 
					            autoloop: AutoLoop::new(LoopControl::open()?),
 | 
				
			||||||
            client,
 | 
					            client,
 | 
				
			||||||
            kernel_path,
 | 
					 | 
				
			||||||
            initrd_path,
 | 
					 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -46,7 +45,14 @@ impl Controller {
 | 
				
			|||||||
        compiler.compile(&image)
 | 
					        compiler.compile(&image)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub fn launch(&mut self, image: &str, vcpus: u32, mem: u64) -> Result<u32> {
 | 
					    pub fn launch(
 | 
				
			||||||
 | 
					        &mut self,
 | 
				
			||||||
 | 
					        kernel_path: &str,
 | 
				
			||||||
 | 
					        initrd_path: &str,
 | 
				
			||||||
 | 
					        image: &str,
 | 
				
			||||||
 | 
					        vcpus: u32,
 | 
				
			||||||
 | 
					        mem: u64,
 | 
				
			||||||
 | 
					    ) -> Result<u32> {
 | 
				
			||||||
        let uuid = Uuid::new_v4();
 | 
					        let uuid = Uuid::new_v4();
 | 
				
			||||||
        let name = format!("hypha-{uuid}");
 | 
					        let name = format!("hypha-{uuid}");
 | 
				
			||||||
        let image_info = self.compile(image)?;
 | 
					        let image_info = self.compile(image)?;
 | 
				
			||||||
@ -64,8 +70,8 @@ impl Controller {
 | 
				
			|||||||
            name: &name,
 | 
					            name: &name,
 | 
				
			||||||
            max_vcpus: vcpus,
 | 
					            max_vcpus: vcpus,
 | 
				
			||||||
            mem_mb: mem,
 | 
					            mem_mb: mem,
 | 
				
			||||||
            kernel_path: self.kernel_path.as_str(),
 | 
					            kernel_path,
 | 
				
			||||||
            initrd_path: self.initrd_path.as_str(),
 | 
					            initrd_path,
 | 
				
			||||||
            cmdline: "quiet elevator=noop",
 | 
					            cmdline: "quiet elevator=noop",
 | 
				
			||||||
            disks: vec![
 | 
					            disks: vec![
 | 
				
			||||||
                DomainDisk {
 | 
					                DomainDisk {
 | 
				
			||||||
@ -130,4 +136,45 @@ impl Controller {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
        Ok(uuid)
 | 
					        Ok(uuid)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn console(&mut self, domid: u32) -> Result<()> {
 | 
				
			||||||
 | 
					        let (mut read, mut write) = self.client.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()?;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -52,16 +52,19 @@ impl RegistryClient {
 | 
				
			|||||||
            .url
 | 
					            .url
 | 
				
			||||||
            .join(&format!("/v2/{}/manifests/{}", name, reference))?;
 | 
					            .join(&format!("/v2/{}/manifests/{}", name, reference))?;
 | 
				
			||||||
        let accept = format!(
 | 
					        let accept = format!(
 | 
				
			||||||
            "{}, {}, {}",
 | 
					            "{}, {}, {}, {}",
 | 
				
			||||||
            MediaType::ImageManifest.to_docker_v2s2()?,
 | 
					            MediaType::ImageManifest.to_docker_v2s2()?,
 | 
				
			||||||
            MediaType::ImageManifest,
 | 
					            MediaType::ImageManifest,
 | 
				
			||||||
            MediaType::ImageIndex,
 | 
					            MediaType::ImageIndex,
 | 
				
			||||||
 | 
					            MediaType::ImageIndex.to_docker_v2s2()?,
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
        let response = self.call(self.agent.get(url.as_str()).set("Accept", &accept))?;
 | 
					        let response = self.call(self.agent.get(url.as_str()).set("Accept", &accept))?;
 | 
				
			||||||
        let content_type = response.header("Content-Type").ok_or_else(|| {
 | 
					        let content_type = response.header("Content-Type").ok_or_else(|| {
 | 
				
			||||||
            HyphaError::new("registry response did not have a Content-Type header")
 | 
					            HyphaError::new("registry response did not have a Content-Type header")
 | 
				
			||||||
        })?;
 | 
					        })?;
 | 
				
			||||||
        if content_type == MediaType::ImageIndex.to_string() {
 | 
					        if content_type == MediaType::ImageIndex.to_string()
 | 
				
			||||||
 | 
					            || content_type == MediaType::ImageIndex.to_docker_v2s2()?
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
            let index = ImageIndex::from_reader(response.into_reader())?;
 | 
					            let index = ImageIndex::from_reader(response.into_reader())?;
 | 
				
			||||||
            let descriptor = self
 | 
					            let descriptor = self
 | 
				
			||||||
                .pick_manifest(index)
 | 
					                .pick_manifest(index)
 | 
				
			||||||
 | 
				
			|||||||
@ -10,7 +10,7 @@ use crate::x86::X86BootSetup;
 | 
				
			|||||||
use log::warn;
 | 
					use log::warn;
 | 
				
			||||||
use std::error::Error;
 | 
					use std::error::Error;
 | 
				
			||||||
use std::fmt::{Display, Formatter};
 | 
					use std::fmt::{Display, Formatter};
 | 
				
			||||||
use std::fs::read;
 | 
					use std::fs::{read, File, OpenOptions};
 | 
				
			||||||
use std::path::PathBuf;
 | 
					use std::path::PathBuf;
 | 
				
			||||||
use std::str::FromStr;
 | 
					use std::str::FromStr;
 | 
				
			||||||
use std::string::FromUtf8Error;
 | 
					use std::string::FromUtf8Error;
 | 
				
			||||||
@ -561,4 +561,22 @@ impl XenClient {
 | 
				
			|||||||
        tx.commit()?;
 | 
					        tx.commit()?;
 | 
				
			||||||
        Ok(())
 | 
					        Ok(())
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn open_console(&mut self, domid: u32) -> Result<(File, File), XenClientError> {
 | 
				
			||||||
 | 
					        let dom_path = self.store.get_domain_path(domid)?;
 | 
				
			||||||
 | 
					        let console_tty_path = format!("{}/console/tty", dom_path);
 | 
				
			||||||
 | 
					        let tty = self
 | 
				
			||||||
 | 
					            .store
 | 
				
			||||||
 | 
					            .read_string_optional(&console_tty_path)?
 | 
				
			||||||
 | 
					            .unwrap_or("".to_string());
 | 
				
			||||||
 | 
					        if tty.is_empty() {
 | 
				
			||||||
 | 
					            return Err(XenClientError::new(&format!(
 | 
				
			||||||
 | 
					                "domain {} does not have a tty",
 | 
				
			||||||
 | 
					                domid
 | 
				
			||||||
 | 
					            )));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        let read = OpenOptions::new().read(true).write(false).open(&tty)?;
 | 
				
			||||||
 | 
					        let write = OpenOptions::new().read(false).write(true).open(&tty)?;
 | 
				
			||||||
 | 
					        Ok((read, write))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user