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:
@ -25,6 +25,9 @@ features = ["process"]
|
|||||||
[dependencies.krata]
|
[dependencies.krata]
|
||||||
path = "../shared"
|
path = "../shared"
|
||||||
|
|
||||||
|
[dependencies.xenevtchn]
|
||||||
|
path = "../libs/xen/xenevtchn"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
path = "src/lib.rs"
|
path = "src/lib.rs"
|
||||||
|
|
||||||
|
49
container/src/background.rs
Normal file
49
container/src/background.rs
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use crate::childwait::{ChildEvent, ChildWait};
|
||||||
|
use anyhow::Result;
|
||||||
|
use nix::{libc::c_int, unistd::Pid};
|
||||||
|
use tokio::{select, time::sleep};
|
||||||
|
|
||||||
|
pub struct ContainerBackground {
|
||||||
|
child: Pid,
|
||||||
|
wait: ChildWait,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ContainerBackground {
|
||||||
|
pub async fn new(child: Pid) -> Result<ContainerBackground> {
|
||||||
|
Ok(ContainerBackground {
|
||||||
|
child,
|
||||||
|
wait: ChildWait::new()?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn run(&mut self) -> Result<()> {
|
||||||
|
loop {
|
||||||
|
select! {
|
||||||
|
event = self.wait.recv() => match event {
|
||||||
|
Some(event) => self.child_event(event).await?,
|
||||||
|
None => {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn child_event(&mut self, event: ChildEvent) -> Result<()> {
|
||||||
|
if event.pid == self.child {
|
||||||
|
self.death(event.status).await?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn death(&mut self, code: c_int) -> Result<()> {
|
||||||
|
println!("[krata] container process exited: status = {}", code);
|
||||||
|
println!("[krata] looping forever");
|
||||||
|
loop {
|
||||||
|
sleep(Duration::from_secs(1)).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
84
container/src/childwait.rs
Normal file
84
container/src/childwait.rs
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
use std::{
|
||||||
|
ptr::addr_of_mut,
|
||||||
|
sync::{
|
||||||
|
atomic::{AtomicBool, Ordering},
|
||||||
|
Arc,
|
||||||
|
},
|
||||||
|
thread::{self, JoinHandle},
|
||||||
|
};
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
|
use log::warn;
|
||||||
|
use nix::{
|
||||||
|
libc::{c_int, wait},
|
||||||
|
unistd::Pid,
|
||||||
|
};
|
||||||
|
use tokio::sync::mpsc::{channel, Receiver, Sender};
|
||||||
|
|
||||||
|
const CHILD_WAIT_QUEUE_LEN: usize = 10;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub struct ChildEvent {
|
||||||
|
pub pid: Pid,
|
||||||
|
pub status: c_int,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ChildWait {
|
||||||
|
receiver: Receiver<ChildEvent>,
|
||||||
|
signal: Arc<AtomicBool>,
|
||||||
|
_task: JoinHandle<()>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ChildWait {
|
||||||
|
pub fn new() -> Result<ChildWait> {
|
||||||
|
let (sender, receiver) = channel(CHILD_WAIT_QUEUE_LEN);
|
||||||
|
let signal = Arc::new(AtomicBool::new(false));
|
||||||
|
let mut processor = ChildWaitTask {
|
||||||
|
sender,
|
||||||
|
signal: signal.clone(),
|
||||||
|
};
|
||||||
|
let task = thread::spawn(move || {
|
||||||
|
if let Err(error) = processor.process() {
|
||||||
|
warn!("failed to process child updates: {}", error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Ok(ChildWait {
|
||||||
|
receiver,
|
||||||
|
signal,
|
||||||
|
_task: task,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn recv(&mut self) -> Option<ChildEvent> {
|
||||||
|
self.receiver.recv().await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ChildWaitTask {
|
||||||
|
sender: Sender<ChildEvent>,
|
||||||
|
signal: Arc<AtomicBool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ChildWaitTask {
|
||||||
|
fn process(&mut self) -> Result<()> {
|
||||||
|
loop {
|
||||||
|
let mut status: c_int = 0;
|
||||||
|
let pid = unsafe { wait(addr_of_mut!(status)) };
|
||||||
|
let event = ChildEvent {
|
||||||
|
pid: Pid::from_raw(pid),
|
||||||
|
status,
|
||||||
|
};
|
||||||
|
let _ = self.sender.try_send(event);
|
||||||
|
|
||||||
|
if self.signal.load(Ordering::Acquire) {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for ChildWait {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
self.signal.store(true, Ordering::Release);
|
||||||
|
}
|
||||||
|
}
|
@ -3,7 +3,7 @@ use futures::stream::TryStreamExt;
|
|||||||
use ipnetwork::IpNetwork;
|
use ipnetwork::IpNetwork;
|
||||||
use krata::{LaunchInfo, LaunchNetwork};
|
use krata::{LaunchInfo, LaunchNetwork};
|
||||||
use log::{trace, warn};
|
use log::{trace, warn};
|
||||||
use nix::libc::{c_int, dup2, ioctl, wait};
|
use nix::libc::{dup2, ioctl};
|
||||||
use nix::unistd::{execve, fork, ForkResult, Pid};
|
use nix::unistd::{execve, fork, ForkResult, Pid};
|
||||||
use oci_spec::image::{Config, ImageConfiguration};
|
use oci_spec::image::{Config, ImageConfiguration};
|
||||||
use std::ffi::{CStr, CString};
|
use std::ffi::{CStr, CString};
|
||||||
@ -13,14 +13,13 @@ use std::os::fd::AsRawFd;
|
|||||||
use std::os::linux::fs::MetadataExt;
|
use std::os::linux::fs::MetadataExt;
|
||||||
use std::os::unix::fs::{chroot, PermissionsExt};
|
use std::os::unix::fs::{chroot, PermissionsExt};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::ptr::addr_of_mut;
|
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::thread::sleep;
|
|
||||||
use std::time::Duration;
|
|
||||||
use std::{fs, io};
|
use std::{fs, io};
|
||||||
use sys_mount::{FilesystemType, Mount, MountFlags};
|
use sys_mount::{FilesystemType, Mount, MountFlags};
|
||||||
use walkdir::WalkDir;
|
use walkdir::WalkDir;
|
||||||
|
|
||||||
|
use crate::background::ContainerBackground;
|
||||||
|
|
||||||
const IMAGE_BLOCK_DEVICE_PATH: &str = "/dev/xvda";
|
const IMAGE_BLOCK_DEVICE_PATH: &str = "/dev/xvda";
|
||||||
const CONFIG_BLOCK_DEVICE_PATH: &str = "/dev/xvdb";
|
const CONFIG_BLOCK_DEVICE_PATH: &str = "/dev/xvdb";
|
||||||
|
|
||||||
@ -84,7 +83,7 @@ impl ContainerInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Some(cfg) = config.config() {
|
if let Some(cfg) = config.config() {
|
||||||
self.run(cfg, &launch)?;
|
self.run(cfg, &launch).await?;
|
||||||
} else {
|
} else {
|
||||||
return Err(anyhow!(
|
return Err(anyhow!(
|
||||||
"unable to determine what to execute, image config doesn't tell us"
|
"unable to determine what to execute, image config doesn't tell us"
|
||||||
@ -367,7 +366,7 @@ impl ContainerInit {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(&mut self, config: &Config, launch: &LaunchInfo) -> Result<()> {
|
async fn run(&mut self, config: &Config, launch: &LaunchInfo) -> Result<()> {
|
||||||
let mut cmd = match config.cmd() {
|
let mut cmd = match config.cmd() {
|
||||||
None => vec![],
|
None => vec![],
|
||||||
Some(value) => value.clone(),
|
Some(value) => value.clone(),
|
||||||
@ -408,7 +407,7 @@ impl ContainerInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::env::set_current_dir(&working_dir)?;
|
std::env::set_current_dir(&working_dir)?;
|
||||||
self.fork_and_exec(&path_cstr, cmd_cstr, env_cstr)?;
|
self.fork_and_exec(&path_cstr, cmd_cstr, env_cstr).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -420,9 +419,14 @@ impl ContainerInit {
|
|||||||
Ok(results)
|
Ok(results)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fork_and_exec(&mut self, path: &CStr, cmd: Vec<CString>, env: Vec<CString>) -> Result<()> {
|
async fn fork_and_exec(
|
||||||
|
&mut self,
|
||||||
|
path: &CStr,
|
||||||
|
cmd: Vec<CString>,
|
||||||
|
env: Vec<CString>,
|
||||||
|
) -> Result<()> {
|
||||||
match unsafe { fork()? } {
|
match unsafe { fork()? } {
|
||||||
ForkResult::Parent { child } => self.background(child),
|
ForkResult::Parent { child } => self.background(child).await,
|
||||||
ForkResult::Child => {
|
ForkResult::Child => {
|
||||||
unsafe { nix::libc::setsid() };
|
unsafe { nix::libc::setsid() };
|
||||||
let result = unsafe { ioctl(io::stdin().as_raw_fd(), nix::libc::TIOCSCTTY, 0) };
|
let result = unsafe { ioctl(io::stdin().as_raw_fd(), nix::libc::TIOCSCTTY, 0) };
|
||||||
@ -435,21 +439,9 @@ impl ContainerInit {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn background(&mut self, executed: Pid) -> Result<()> {
|
async fn background(&mut self, executed: Pid) -> Result<()> {
|
||||||
loop {
|
let mut background = ContainerBackground::new(executed).await?;
|
||||||
let mut status: c_int = 0;
|
background.run().await?;
|
||||||
let pid = unsafe { wait(addr_of_mut!(status)) };
|
Ok(())
|
||||||
if executed.as_raw() == pid {
|
|
||||||
return self.death(status);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn death(&mut self, code: c_int) -> Result<()> {
|
|
||||||
println!("[krata] container process exited: status = {}", code);
|
|
||||||
println!("[krata] looping forever");
|
|
||||||
loop {
|
|
||||||
sleep(Duration::from_secs(1));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1 +1,3 @@
|
|||||||
|
pub mod background;
|
||||||
|
pub mod childwait;
|
||||||
pub mod init;
|
pub mod init;
|
||||||
|
@ -26,6 +26,8 @@ oci-spec = { workspace = true }
|
|||||||
backhand = { workspace = true }
|
backhand = { workspace = true }
|
||||||
uuid = { workspace = true }
|
uuid = { workspace = true }
|
||||||
ipnetwork = { workspace = true }
|
ipnetwork = { workspace = true }
|
||||||
|
tokio = { workspace = true }
|
||||||
|
futures = { workspace = true }
|
||||||
|
|
||||||
[dependencies.krata]
|
[dependencies.krata]
|
||||||
path = "../shared"
|
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();
|
env_logger::Builder::from_env(Env::default().default_filter_or("warn")).init();
|
||||||
|
|
||||||
let args = ControllerArgs::parse();
|
let args = ControllerArgs::parse();
|
||||||
@ -99,7 +100,7 @@ fn main() -> Result<()> {
|
|||||||
println!("launched container: {}", uuid);
|
println!("launched container: {}", uuid);
|
||||||
if attach {
|
if attach {
|
||||||
let mut console = ControllerConsole::new(&mut context);
|
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 } => {
|
Commands::Console { container } => {
|
||||||
let mut console = ControllerConsole::new(&mut context);
|
let mut console = ControllerConsole::new(&mut context);
|
||||||
console.perform(&container)?;
|
console.perform(&container).await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Commands::List { .. } => {
|
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 anyhow::{anyhow, Result};
|
||||||
use termion::raw::IntoRawMode;
|
|
||||||
|
use crate::console::XenConsole;
|
||||||
|
|
||||||
use super::ControllerContext;
|
use super::ControllerContext;
|
||||||
|
|
||||||
@ -18,49 +13,15 @@ impl ControllerConsole<'_> {
|
|||||||
ControllerConsole { context }
|
ControllerConsole { context }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn perform(&mut self, id: &str) -> Result<()> {
|
pub async fn perform(&mut self, id: &str) -> Result<()> {
|
||||||
let info = self
|
let info = self
|
||||||
.context
|
.context
|
||||||
.resolve(id)?
|
.resolve(id)?
|
||||||
.ok_or_else(|| anyhow!("unable to resolve container: {}", id))?;
|
.ok_or_else(|| anyhow!("unable to resolve container: {}", id))?;
|
||||||
let domid = info.domid;
|
let domid = info.domid;
|
||||||
let (mut read, mut write) = self.context.xen.open_console(domid)?;
|
let tty = self.context.xen.get_console_path(domid)?;
|
||||||
let mut stdin = io::stdin();
|
let console = XenConsole::new(&tty).await?;
|
||||||
let is_tty = termion::is_tty(&stdin);
|
console.attach().await?;
|
||||||
let mut stdout_for_exit = io::stdout().into_raw_mode()?;
|
Ok(())
|
||||||
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()?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,10 +4,11 @@ use advmac::MacAddr6;
|
|||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use ipnetwork::Ipv4Network;
|
use ipnetwork::Ipv4Network;
|
||||||
use krata::{
|
use krata::{
|
||||||
LaunchInfo, LaunchNetwork, LaunchNetworkIpv4, LaunchNetworkIpv6, LaunchNetworkResolver,
|
LaunchChannels, LaunchInfo, LaunchNetwork, LaunchNetworkIpv4, LaunchNetworkIpv6,
|
||||||
|
LaunchNetworkResolver,
|
||||||
};
|
};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
use xenclient::{DomainConfig, DomainDisk, DomainNetworkInterface};
|
use xenclient::{DomainConfig, DomainDisk, DomainEventChannel, DomainNetworkInterface};
|
||||||
use xenstore::client::XsdInterface;
|
use xenstore::client::XsdInterface;
|
||||||
|
|
||||||
use crate::image::{name::ImageName, ImageCompiler, ImageInfo};
|
use crate::image::{name::ImageName, ImageCompiler, ImageInfo};
|
||||||
@ -75,6 +76,9 @@ impl ControllerLaunch<'_> {
|
|||||||
}),
|
}),
|
||||||
env: request.env,
|
env: request.env,
|
||||||
run: request.run,
|
run: request.run,
|
||||||
|
channels: LaunchChannels {
|
||||||
|
exit: "krata-exit".to_string(),
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
let cfgblk = ConfigBlock::new(&uuid, &image_info)?;
|
let cfgblk = ConfigBlock::new(&uuid, &image_info)?;
|
||||||
@ -133,6 +137,7 @@ impl ControllerLaunch<'_> {
|
|||||||
script: None,
|
script: None,
|
||||||
}],
|
}],
|
||||||
filesystems: vec![],
|
filesystems: vec![],
|
||||||
|
event_channels: vec![DomainEventChannel { name: "krata-exit" }],
|
||||||
extra_keys: vec![
|
extra_keys: vec![
|
||||||
("krata/uuid".to_string(), uuid.to_string()),
|
("krata/uuid".to_string(), uuid.to_string()),
|
||||||
(
|
(
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
pub mod autoloop;
|
pub mod autoloop;
|
||||||
|
pub mod console;
|
||||||
pub mod ctl;
|
pub mod ctl;
|
||||||
pub mod image;
|
pub mod image;
|
||||||
|
@ -26,6 +26,7 @@ fn main() -> Result<()> {
|
|||||||
vifs: vec![],
|
vifs: vec![],
|
||||||
filesystems: vec![],
|
filesystems: vec![],
|
||||||
extra_keys: vec![],
|
extra_keys: vec![],
|
||||||
|
event_channels: vec![],
|
||||||
};
|
};
|
||||||
let domid = client.create(&config)?;
|
let domid = client.create(&config)?;
|
||||||
println!("created domain {}", domid);
|
println!("created domain {}", domid);
|
||||||
|
@ -11,7 +11,7 @@ use crate::error::{Error, Result};
|
|||||||
use crate::x86::X86BootSetup;
|
use crate::x86::X86BootSetup;
|
||||||
use log::{trace, warn};
|
use log::{trace, warn};
|
||||||
|
|
||||||
use std::fs::{read, File, OpenOptions};
|
use std::fs::read;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::thread;
|
use std::thread;
|
||||||
@ -59,6 +59,11 @@ pub struct DomainNetworkInterface<'a> {
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct DomainConsole {}
|
pub struct DomainConsole {}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct DomainEventChannel<'a> {
|
||||||
|
pub name: &'a str,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct DomainConfig<'a> {
|
pub struct DomainConfig<'a> {
|
||||||
pub backend_domid: u32,
|
pub backend_domid: u32,
|
||||||
@ -72,6 +77,7 @@ pub struct DomainConfig<'a> {
|
|||||||
pub consoles: Vec<DomainConsole>,
|
pub consoles: Vec<DomainConsole>,
|
||||||
pub vifs: Vec<DomainNetworkInterface<'a>>,
|
pub vifs: Vec<DomainNetworkInterface<'a>>,
|
||||||
pub filesystems: Vec<DomainFilesystem<'a>>,
|
pub filesystems: Vec<DomainFilesystem<'a>>,
|
||||||
|
pub event_channels: Vec<DomainEventChannel<'a>>,
|
||||||
pub extra_keys: Vec<(String, String)>,
|
pub extra_keys: Vec<(String, String)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -99,90 +105,6 @@ impl XenClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn destroy(&mut self, domid: u32) -> Result<()> {
|
|
||||||
if let Err(err) = self.destroy_store(domid) {
|
|
||||||
warn!("failed to destroy store for domain {}: {}", domid, err);
|
|
||||||
}
|
|
||||||
self.call.destroy_domain(domid)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn destroy_store(&mut self, domid: u32) -> Result<()> {
|
|
||||||
let dom_path = self.store.get_domain_path(domid)?;
|
|
||||||
let vm_path = self.store.read_string(&format!("{}/vm", dom_path))?;
|
|
||||||
if vm_path.is_empty() {
|
|
||||||
return Err(Error::DomainNonExistent);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut backend_paths: Vec<String> = Vec::new();
|
|
||||||
let console_frontend_path = format!("{}/console", dom_path);
|
|
||||||
let console_backend_path = self
|
|
||||||
.store
|
|
||||||
.read_string_optional(format!("{}/backend", console_frontend_path).as_str())?;
|
|
||||||
|
|
||||||
for device_category in self
|
|
||||||
.store
|
|
||||||
.list_any(format!("{}/device", dom_path).as_str())?
|
|
||||||
{
|
|
||||||
for device_id in self
|
|
||||||
.store
|
|
||||||
.list_any(format!("{}/device/{}", dom_path, device_category).as_str())?
|
|
||||||
{
|
|
||||||
let device_path = format!("{}/device/{}/{}", dom_path, device_category, device_id);
|
|
||||||
let backend_path = self
|
|
||||||
.store
|
|
||||||
.read_string(format!("{}/backend", device_path).as_str())?;
|
|
||||||
backend_paths.push(backend_path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for backend in &backend_paths {
|
|
||||||
let state_path = format!("{}/state", backend);
|
|
||||||
let online_path = format!("{}/online", backend);
|
|
||||||
let mut tx = self.store.transaction()?;
|
|
||||||
let state = tx.read_string(&state_path)?;
|
|
||||||
if state.is_empty() {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
tx.write_string(&online_path, "0")?;
|
|
||||||
if !state.is_empty() && u32::from_str(&state).unwrap_or(0) != 6 {
|
|
||||||
tx.write_string(&state_path, "5")?;
|
|
||||||
}
|
|
||||||
tx.commit()?;
|
|
||||||
|
|
||||||
let mut count: u32 = 0;
|
|
||||||
loop {
|
|
||||||
if count >= 100 {
|
|
||||||
warn!("unable to safely destroy backend: {}", backend);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
let state = self.store.read_string(&state_path)?;
|
|
||||||
let state = i64::from_str(&state).unwrap_or(-1);
|
|
||||||
if state == 6 {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
thread::sleep(Duration::from_millis(100));
|
|
||||||
count += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut tx = self.store.transaction()?;
|
|
||||||
let mut backend_removals: Vec<String> = Vec::new();
|
|
||||||
backend_removals.extend_from_slice(backend_paths.as_slice());
|
|
||||||
if let Some(backend) = console_backend_path {
|
|
||||||
backend_removals.push(backend);
|
|
||||||
}
|
|
||||||
for path in &backend_removals {
|
|
||||||
let path = PathBuf::from(path);
|
|
||||||
let parent = path.parent().ok_or(Error::PathParentNotFound)?;
|
|
||||||
tx.rm(parent.to_str().ok_or(Error::PathStringConversion)?)?;
|
|
||||||
}
|
|
||||||
tx.rm(&vm_path)?;
|
|
||||||
tx.rm(&dom_path)?;
|
|
||||||
tx.commit()?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn init(&mut self, domid: u32, domain: &CreateDomain, config: &DomainConfig) -> Result<()> {
|
fn init(&mut self, domid: u32, domain: &CreateDomain, config: &DomainConfig) -> Result<()> {
|
||||||
trace!(
|
trace!(
|
||||||
"XenClient init domid={} domain={:?} config={:?}",
|
"XenClient init domid={} domain={:?} config={:?}",
|
||||||
@ -401,6 +323,18 @@ impl XenClient {
|
|||||||
vif,
|
vif,
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for channel in &config.event_channels {
|
||||||
|
let id = self
|
||||||
|
.call
|
||||||
|
.evtchn_alloc_unbound(domid, config.backend_domid)?;
|
||||||
|
let channel_path = format!("{}/evtchn/{}", dom_path, channel.name);
|
||||||
|
self.store
|
||||||
|
.write_string(&format!("{}/name", channel_path), channel.name)?;
|
||||||
|
self.store
|
||||||
|
.write_string(&format!("{}/channel", channel_path), &id.to_string())?;
|
||||||
|
}
|
||||||
|
|
||||||
self.call.unpause_domain(domid)?;
|
self.call.unpause_domain(domid)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -673,7 +607,91 @@ impl XenClient {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn open_console(&mut self, domid: u32) -> Result<(File, File)> {
|
pub fn destroy(&mut self, domid: u32) -> Result<()> {
|
||||||
|
if let Err(err) = self.destroy_store(domid) {
|
||||||
|
warn!("failed to destroy store for domain {}: {}", domid, err);
|
||||||
|
}
|
||||||
|
self.call.destroy_domain(domid)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn destroy_store(&mut self, domid: u32) -> Result<()> {
|
||||||
|
let dom_path = self.store.get_domain_path(domid)?;
|
||||||
|
let vm_path = self.store.read_string(&format!("{}/vm", dom_path))?;
|
||||||
|
if vm_path.is_empty() {
|
||||||
|
return Err(Error::DomainNonExistent);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut backend_paths: Vec<String> = Vec::new();
|
||||||
|
let console_frontend_path = format!("{}/console", dom_path);
|
||||||
|
let console_backend_path = self
|
||||||
|
.store
|
||||||
|
.read_string_optional(format!("{}/backend", console_frontend_path).as_str())?;
|
||||||
|
|
||||||
|
for device_category in self
|
||||||
|
.store
|
||||||
|
.list_any(format!("{}/device", dom_path).as_str())?
|
||||||
|
{
|
||||||
|
for device_id in self
|
||||||
|
.store
|
||||||
|
.list_any(format!("{}/device/{}", dom_path, device_category).as_str())?
|
||||||
|
{
|
||||||
|
let device_path = format!("{}/device/{}/{}", dom_path, device_category, device_id);
|
||||||
|
let backend_path = self
|
||||||
|
.store
|
||||||
|
.read_string(format!("{}/backend", device_path).as_str())?;
|
||||||
|
backend_paths.push(backend_path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for backend in &backend_paths {
|
||||||
|
let state_path = format!("{}/state", backend);
|
||||||
|
let online_path = format!("{}/online", backend);
|
||||||
|
let mut tx = self.store.transaction()?;
|
||||||
|
let state = tx.read_string(&state_path)?;
|
||||||
|
if state.is_empty() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
tx.write_string(&online_path, "0")?;
|
||||||
|
if !state.is_empty() && u32::from_str(&state).unwrap_or(0) != 6 {
|
||||||
|
tx.write_string(&state_path, "5")?;
|
||||||
|
}
|
||||||
|
tx.commit()?;
|
||||||
|
|
||||||
|
let mut count: u32 = 0;
|
||||||
|
loop {
|
||||||
|
if count >= 100 {
|
||||||
|
warn!("unable to safely destroy backend: {}", backend);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
let state = self.store.read_string(&state_path)?;
|
||||||
|
let state = i64::from_str(&state).unwrap_or(-1);
|
||||||
|
if state == 6 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
thread::sleep(Duration::from_millis(100));
|
||||||
|
count += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut tx = self.store.transaction()?;
|
||||||
|
let mut backend_removals: Vec<String> = Vec::new();
|
||||||
|
backend_removals.extend_from_slice(backend_paths.as_slice());
|
||||||
|
if let Some(backend) = console_backend_path {
|
||||||
|
backend_removals.push(backend);
|
||||||
|
}
|
||||||
|
for path in &backend_removals {
|
||||||
|
let path = PathBuf::from(path);
|
||||||
|
let parent = path.parent().ok_or(Error::PathParentNotFound)?;
|
||||||
|
tx.rm(parent.to_str().ok_or(Error::PathStringConversion)?)?;
|
||||||
|
}
|
||||||
|
tx.rm(&vm_path)?;
|
||||||
|
tx.rm(&dom_path)?;
|
||||||
|
tx.commit()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_console_path(&mut self, domid: u32) -> Result<String> {
|
||||||
let dom_path = self.store.get_domain_path(domid)?;
|
let dom_path = self.store.get_domain_path(domid)?;
|
||||||
let console_tty_path = format!("{}/console/tty", dom_path);
|
let console_tty_path = format!("{}/console/tty", dom_path);
|
||||||
let mut tty: Option<String> = None;
|
let mut tty: Option<String> = None;
|
||||||
@ -687,8 +705,6 @@ impl XenClient {
|
|||||||
let Some(tty) = tty else {
|
let Some(tty) = tty else {
|
||||||
return Err(Error::TtyNotFound);
|
return Err(Error::TtyNotFound);
|
||||||
};
|
};
|
||||||
let read = OpenOptions::new().read(true).write(false).open(&tty)?;
|
Ok(tty)
|
||||||
let write = OpenOptions::new().read(false).write(true).open(&tty)?;
|
|
||||||
Ok((read, write))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,5 +10,5 @@ REAL_SCRIPT="$(realpath "${0}")"
|
|||||||
cd "$(dirname "${REAL_SCRIPT}")/.."
|
cd "$(dirname "${REAL_SCRIPT}")/.."
|
||||||
./initrd/build.sh -q
|
./initrd/build.sh -q
|
||||||
sudo cp "target/initrd/initrd" "/var/lib/krata/default/initrd"
|
sudo cp "target/initrd/initrd" "/var/lib/krata/default/initrd"
|
||||||
cargo build -q --target x86_64-unknown-linux-gnu --bin kratactl
|
cargo build --target x86_64-unknown-linux-gnu --bin kratactl
|
||||||
exec sudo RUST_LOG="${RUST_LOG}" target/x86_64-unknown-linux-gnu/debug/kratactl "${@}"
|
exec sudo RUST_LOG="${RUST_LOG}" target/x86_64-unknown-linux-gnu/debug/kratactl "${@}"
|
||||||
|
@ -8,5 +8,5 @@ fi
|
|||||||
|
|
||||||
REAL_SCRIPT="$(realpath "${0}")"
|
REAL_SCRIPT="$(realpath "${0}")"
|
||||||
cd "$(dirname "${REAL_SCRIPT}")/.."
|
cd "$(dirname "${REAL_SCRIPT}")/.."
|
||||||
cargo build -q --target x86_64-unknown-linux-gnu --bin kratanet
|
cargo build --target x86_64-unknown-linux-gnu --bin kratanet
|
||||||
exec sudo RUST_LOG="${RUST_LOG}" target/x86_64-unknown-linux-gnu/debug/kratanet "${@}"
|
exec sudo RUST_LOG="${RUST_LOG}" target/x86_64-unknown-linux-gnu/debug/kratanet "${@}"
|
||||||
|
@ -25,9 +25,15 @@ pub struct LaunchNetwork {
|
|||||||
pub resolver: LaunchNetworkResolver,
|
pub resolver: LaunchNetworkResolver,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
pub struct LaunchChannels {
|
||||||
|
pub exit: String,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
pub struct LaunchInfo {
|
pub struct LaunchInfo {
|
||||||
pub network: Option<LaunchNetwork>,
|
pub network: Option<LaunchNetwork>,
|
||||||
pub env: Option<Vec<String>>,
|
pub env: Option<Vec<String>>,
|
||||||
pub run: Option<Vec<String>>,
|
pub run: Option<Vec<String>>,
|
||||||
|
pub channels: LaunchChannels,
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user