diff --git a/freebsd/src/event.rs b/freebsd/src/event.rs index 604bc7d..bbf6e37 100644 --- a/freebsd/src/event.rs +++ b/freebsd/src/event.rs @@ -73,8 +73,12 @@ impl EventFdNotify { EventFdNotify { fd } } + pub fn notify_waiters_with_value(&self, value: u64) { + unsafe { eventfd_write(self.fd, value) }; + } + pub fn notify_waiters(&self) { - unsafe { eventfd_write(self.fd, 1) }; + self.notify_waiters_with_value(1); } pub fn as_async_fd(&self) -> Result, std::io::Error> { @@ -82,6 +86,18 @@ impl EventFdNotify { AsyncFd::new(new_fd) } + pub async fn notified_take_value(&self) -> std::io::Result { + _ = self.as_async_fd()?.readable().await?; + unsafe { + let mut v = 0u64; + if eventfd_read(self.fd, &mut v) != 0 { + Err(std::io::Error::last_os_error()) + } else { + Ok(v) + } + } + } + pub async fn notified(&self) -> std::io::Result<()> { _ = self.as_async_fd()?.readable().await?; Ok(()) @@ -94,6 +110,21 @@ impl EventFdNotify { _ = nix::sys::event::kevent_ts(kq, &[kevent], &mut [out], None); } + pub fn notified_sync_take_value(&self) -> std::io::Result { + let kevent = KEvent::from_read(self.fd); + let kq = nix::sys::event::kqueue().unwrap(); + let out = KEvent::zero(); + _ = nix::sys::event::kevent_ts(kq, &[kevent], &mut [out], None); + unsafe { + let mut v = 0u64; + if eventfd_read(self.fd, &mut v) != 0 { + Err(std::io::Error::last_os_error()) + } else { + Ok(v) + } + } + } + pub fn new() -> EventFdNotify { let fd = unsafe { eventfd(0, EFD_NONBLOCK) }; EventFdNotify { fd } diff --git a/xc-bin/src/main.rs b/xc-bin/src/main.rs index 4a089e6..7195ecc 100644 --- a/xc-bin/src/main.rs +++ b/xc-bin/src/main.rs @@ -929,11 +929,13 @@ fn main() -> Result<(), ActionError> { notify: Maybe::Some(ipc::packet::codec::Fd(n.as_raw_fd())), use_tty: terminal, }; + if let Ok(response) = do_exec(&mut conn, request)? { if let Some(socket) = response.terminal_socket { _ = attach::run(socket); } - n.notified_sync(); + let exit = n.notified_sync_take_value(); + std::process::exit(exit.unwrap_or(1) as i32) } } }; diff --git a/xc/src/config.rs b/xc/src/config.rs index 1310136..11c6a61 100644 --- a/xc/src/config.rs +++ b/xc/src/config.rs @@ -24,14 +24,31 @@ use crate::res::network::Network; use serde::{Deserialize, Serialize}; use std::collections::HashMap; -/* -#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] -pub struct IfBind { - pub network: String, - pub bridge_if: String, - pub alias_if: String, + +fn default_socket_path() -> String { + "/var/run/xc.sock".to_string() } -*/ + +fn default_database_store() -> String { + "/var/db/xc.sqlite".to_string() +} + +fn default_registries() -> String { + "/var/db/xc.registries.json".to_string() +} + +fn default_layers_dir() -> String { + "/var/cache".to_string() +} + +fn default_logs_dir() -> String { + "/var/log/xc".to_string() +} + +fn default_devfs_offset() -> u16 { + 1000 +} + #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] pub struct XcConfig { /// Network interfaces should "xc" consider external @@ -40,11 +57,20 @@ pub struct XcConfig { pub image_dataset: String, /// Dataset for running containers pub container_dataset: String, - pub layers_dir: String, - pub devfs_id_offset: u16, + #[serde(default = "default_database_store")] pub image_database_store: String, + #[serde(default = "default_layers_dir")] + pub layers_dir: String, + #[serde(default = "default_logs_dir")] + pub logs_dir: String, + #[serde(default = "default_devfs_offset")] + pub devfs_id_offset: u16, + #[serde(default = "default_database_store")] pub database_store: String, + #[serde(default = "default_socket_path")] pub socket_path: String, + #[serde(default)] pub networks: HashMap, + #[serde(default = "default_registries")] pub registries: String, } diff --git a/xc/src/container/mod.rs b/xc/src/container/mod.rs index 9a1cfec..e4761fc 100644 --- a/xc/src/container/mod.rs +++ b/xc/src/container/mod.rs @@ -86,6 +86,8 @@ pub struct CreateContainer { pub allowing: Vec, pub image_reference: Option, pub default_router: Option, + + pub log_directory: Option, } impl CreateContainer { @@ -342,6 +344,7 @@ impl CreateContainer { allowing: self.allowing.clone(), image_reference: self.image_reference.clone(), default_router: self.default_router, + log_directory: self.log_directory.clone(), }) } } diff --git a/xc/src/container/process.rs b/xc/src/container/process.rs index 7f6f174..32ff7e8 100644 --- a/xc/src/container/process.rs +++ b/xc/src/container/process.rs @@ -147,15 +147,16 @@ pub(super) fn spawn_process_forward( pub(super) fn spawn_process_pty( cmd: std::process::Command, - log_path: &str, - socket_path: &str, + log_path: impl AsRef, + socket_path: impl AsRef, ) -> Result { + let log_path_str = log_path.as_ref().to_string_lossy().to_string(); let file = File::options() .create(true) .write(true) .open(log_path) - .map_err(|err| ExecError::CannotOpenLogFile(log_path.to_string(), err))?; - let listener = std::os::unix::net::UnixListener::bind(socket_path) + .map_err(|err| ExecError::CannotOpenLogFile(log_path_str.to_string(), err))?; + let listener = std::os::unix::net::UnixListener::bind(socket_path.as_ref()) .map_err(ExecError::CannotBindUnixSocket)?; let forwarder = PtyForwarder::from_command(listener, cmd, file); let pid = forwarder.pid(); @@ -166,9 +167,9 @@ pub(super) fn spawn_process_pty( }); Ok(SpawnInfo { pid, - stdout_log_file: Some(log_path.to_string()), - stderr_log_file: Some(log_path.to_string()), - terminal_socket: Some(socket_path.to_string()), + stdout_log_file: Some(log_path_str.to_string()), + stderr_log_file: Some(log_path_str), + terminal_socket: Some(socket_path.as_ref().to_string_lossy().to_string()), }) } diff --git a/xc/src/container/runner/mod.rs b/xc/src/container/runner/mod.rs index 19b87e2..5b27b52 100644 --- a/xc/src/container/runner/mod.rs +++ b/xc/src/container/runner/mod.rs @@ -54,6 +54,7 @@ pub struct ProcessRunnerStat { pub(super) id: String, pub(super) pid: u32, pub(super) process_stat: Sender, + pub(super) exit_notify: Option>, pub(super) notify: Option>, } @@ -64,19 +65,29 @@ impl ProcessRunnerStat { pub(super) fn id(&self) -> &str { self.id.as_str() } + pub(super) fn set_exited(&mut self, exit_code: i32) { self.process_stat.send_if_modified(|status| { status.set_exited(exit_code); true }); + if let Some(notify) = &self.exit_notify { + notify.clone().notify_waiters_with_value(exit_code as u64); + } } + pub(super) fn set_tree_exited(&mut self) { self.process_stat.send_if_modified(|status| { status.set_tree_exited(); true }); if let Some(notify) = &self.notify { - notify.clone().notify_waiters(); + let exit_code = self + .process_stat + .borrow() + .exit_code + .expect("The entire tree exited but not the process itself?!"); + notify.clone().notify_waiters_with_value(exit_code as u64); } } } @@ -192,6 +203,7 @@ impl ProcessRunner { id: &str, pid: u32, stat: Sender, + exit_notify: Option>, notify: Option>, ) { debug!("trace process id: {id}, pid: {pid}"); @@ -199,6 +211,7 @@ impl ProcessRunner { pid, id: id.to_string(), process_stat: stat, + exit_notify, notify, }; self.named_process.push(rstat); @@ -247,6 +260,7 @@ impl ProcessRunner { &mut self, id: &str, exec: &Jexec, + exit_notify: Option>, notify: Option>, ) -> Result { debug!("spawn: {exec:#?}"); @@ -279,17 +293,36 @@ impl ProcessRunner { if let Some(work_dir) = &exec.work_dir { cmd.jwork_dir(work_dir); } - + let devnull = std::path::PathBuf::from("/dev/null"); let spawn_info_result = match &exec.output_mode { StdioMode::Terminal => { let socket_path = format!("/var/run/xc.{}.{}", self.container.id, id); - let log_path = format!("/var/log/xc.{}.{}.log", self.container.id, id); + let log_path = self + .container + .log_directory + .clone() + .map(|path| { + let mut path = path; + path.push(format!("xc.{}.{id}.log", self.container.id)); + path + }) + .unwrap_or_else(|| devnull.clone()); spawn_process_pty(cmd, &log_path, &socket_path) } StdioMode::Files { stdout, stderr } => spawn_process_files(&mut cmd, stdout, stderr), StdioMode::Inherit => { - let out_path = format!("/var/log/xc.{}.{}.out.log", self.container.id, id); - let err_path = format!("/var/log/xc.{}.{}.err.log", self.container.id, id); + let (out_path, err_path) = self + .container + .log_directory + .clone() + .map(|path| { + let mut path = path; + let mut path2 = path.clone(); + path.push(format!("xc.{}.{id}.out.log", self.container.id)); + path2.push(format!("xc.{}.{id}.err.log", self.container.id)); + (path, path2) + }) + .unwrap_or_else(|| (devnull.clone(), devnull)); spawn_process_files(&mut cmd, &Some(out_path), &Some(err_path)) } StdioMode::Forward { @@ -317,6 +350,7 @@ impl ProcessRunner { pid, id: id.to_string(), process_stat: tx, + exit_notify, notify, }; @@ -382,7 +416,7 @@ impl ProcessRunner { if method == "exec" { let jexec: Jexec = serde_json::from_value(request.data).unwrap(); let notify = Arc::new(EventFdNotify::from_fd(jexec.notify.unwrap())); - let result = self.spawn_process(&crate::util::gen_id(), &jexec, Some(notify)); + let result = self.spawn_process(&crate::util::gen_id(), &jexec, Some(notify), None); match result { Ok(spawn_info) => { @@ -444,7 +478,7 @@ impl ProcessRunner { self.run_main(); } else if let Some((id, jexec)) = self.inits.pop_front() { self.inits.activate(); - _ = self.spawn_process(&id, &jexec, None); + _ = self.spawn_process(&id, &jexec, None, None); } } else { error!("self.start() is called but the container has already started!") @@ -557,7 +591,7 @@ impl ProcessRunner { 'kq: loop { while let Some((id, process)) = self.spawn_queue.pop_front() { - match self.spawn_process(&id, &process, None) { + match self.spawn_process(&id, &process, None, None) { Ok(spawn_info) => { debug!("{id} spawn: {spawn_info:#?}"); if id == "main" { diff --git a/xc/src/container/running.rs b/xc/src/container/running.rs index 5f2aa8f..ce9ac9a 100644 --- a/xc/src/container/running.rs +++ b/xc/src/container/running.rs @@ -36,6 +36,7 @@ use oci_util::image_reference::ImageReference; use std::collections::HashMap; use std::net::IpAddr; use std::os::fd::{AsRawFd, FromRawFd}; +use std::path::PathBuf; use std::sync::Arc; use tokio::sync::watch::Receiver; @@ -78,6 +79,8 @@ pub struct RunningContainer { pub default_router: Option, pub main_started_notify: Arc, + + pub log_directory: Option, } impl RunningContainer { diff --git a/xcd/src/bin/xcd.rs b/xcd/src/bin/xcd.rs index 8708ff4..390e7f0 100644 --- a/xcd/src/bin/xcd.rs +++ b/xcd/src/bin/xcd.rs @@ -47,5 +47,7 @@ fn main() { .enable_io() .build() .unwrap(); - _ = rt.block_on(xmain()); + if let Err(error) = rt.block_on(xmain()) { + eprintln!("{error:#?}") + } } diff --git a/xcd/src/lib.rs b/xcd/src/lib.rs index 76479fe..912fe87 100644 --- a/xcd/src/lib.rs +++ b/xcd/src/lib.rs @@ -35,6 +35,8 @@ mod task; mod util; pub mod ipc; + +use anyhow::{bail, Context}; use config_manager::ConfigManager; use context::ServerContext; use std::sync::Arc; @@ -51,6 +53,26 @@ pub async fn xmain() -> Result<(), anyhow::Error> { } Ok(config_manager) => { let xc_config = config_manager.config(); + + let log_path = std::path::Path::new(&xc_config.logs_dir); + let layers_path = std::path::Path::new(&xc_config.layers_dir); + + if !log_path.exists() || !log_path.is_dir() { + if !log_path.is_dir() { + bail!("logs_dir is not a directory!") + } else { + std::fs::create_dir_all(log_path).context("cannot create log directory")?; + } + } + + if !layers_path.exists() || !layers_path.is_dir() { + if !layers_path.is_dir() { + anyhow::bail!("layers_dir is not a directory!") + } else { + std::fs::create_dir_all(log_path).context("cannot create layer directory")?; + } + } + let path = xc_config.socket_path.to_string(); info!("config: {xc_config:#?}"); let context = Arc::new(RwLock::new(ServerContext::new(config_manager))); diff --git a/xcd/src/site.rs b/xcd/src/site.rs index ce0a527..1af11c9 100644 --- a/xcd/src/site.rs +++ b/xcd/src/site.rs @@ -129,9 +129,6 @@ impl Site { } pub fn update_host_file(&mut self, network: &str, hosts: &Vec<(String, IpAddr)>) { - use ipc::packet::codec::FromPacket; - use ipc::proto::ipc_err; - self.hosts_cache.insert(network.to_string(), hosts.clone()); let mut host_entries = Vec::new(); @@ -403,6 +400,7 @@ impl Site { allowing: blueprint.allowing, image_reference: blueprint.image_reference, default_router: blueprint.default_router, + log_directory: Some(std::path::PathBuf::from(&self.config.borrow().logs_dir)), }; let running_container = container