diff --git a/freebsd/src/net/pf.rs b/freebsd/src/net/pf.rs index 599548c..42cfcc1 100644 --- a/freebsd/src/net/pf.rs +++ b/freebsd/src/net/pf.rs @@ -25,12 +25,14 @@ use ipcidr::IpCidr; use std::io::Write; use std::net::IpAddr; -use std::process::Command; +use std::process::{Command, Stdio}; pub const PFCTL_CMD: &str = crate::env_or_default!("XC_PFCTL_CMD", "/sbin/pfctl"); pub fn is_pf_enabled() -> Result { Command::new(PFCTL_CMD) + .stdout(Stdio::null()) + .stderr(Stdio::null()) .arg("-s") .arg("Running") .status() diff --git a/xc/src/container/mod.rs b/xc/src/container/mod.rs index 2e10a00..c97198f 100644 --- a/xc/src/container/mod.rs +++ b/xc/src/container/mod.rs @@ -189,7 +189,7 @@ impl Container { } else { proto = proto.param("vnet", Value::Int(1)); for alloc in self.ip_alloc.iter() { - if let Some(network) = &alloc.network { + if let Some(_network) = &alloc.network { let (epair_a, epair_b) = undo.create_epair()?; undo.iface_up(epair_a.to_owned())?; undo.iface_up(epair_b.to_owned())?; diff --git a/xcd/src/context/instantiate.rs b/xcd/src/context/instantiate.rs index 5c3e1cb..131b8c4 100644 --- a/xcd/src/context/instantiate.rs +++ b/xcd/src/context/instantiate.rs @@ -101,6 +101,20 @@ impl InstantiateBlueprint { ipc::packet::codec::Maybe::Some(x) => Some(EventFdNotify::from_fd(x.as_raw_fd())), }; + for (name, env_spec) in config.envs.iter() { + if env_spec.required && !request.envs.contains_key(&name.to_string()) { + let extra_info = env_spec + .description + .as_ref() + .map(|d| format!(" - {d}")) + .unwrap_or_default(); + precondition_failure!( + ENOENT, + "missing required environment variable: {name}{extra_info}" + ); + } + } + let Some(entry_point) = config.entry_points.get(&request.entry_point) else { precondition_failure!(ENOENT, "requested entry point not found: {}", request.entry_point); }; diff --git a/xcd/src/ipc.rs b/xcd/src/ipc.rs index 0c7ab57..8650d2c 100644 --- a/xcd/src/ipc.rs +++ b/xcd/src/ipc.rs @@ -213,6 +213,15 @@ async fn instantiate( // let id = Uuid::new_v4().to_string(); let id = gen_id(); + if request + .name + .as_ref() + .and_then(|n| n.parse::().ok()) + .is_some() + { + return ipc_err(EINVAL, "container name cannot be integer literal"); + } + let row = { let ctx = context.read().await; let dlctx = ctx.image_manager.read().await; @@ -526,27 +535,28 @@ async fn show_container( } #[derive(Serialize, Deserialize, Debug)] -pub struct DestroyContainerRequest { +pub struct KillContainerRequest { pub name: String, } #[derive(Serialize, Deserialize, Debug)] -pub struct DestroyContainerResponse {} +pub struct KillContainerResponse {} -#[ipc_method(method = "destroy_container")] -async fn destroy_container( +#[ipc_method(method = "kill_container")] +async fn kill_container( context: Arc>, local_context: &mut ConnectionContext, - request: DestroyContainerRequest, -) -> GenericResult { + request: KillContainerRequest, +) -> GenericResult { let mut context = context.write().await; if let Some(id) = context.alias_map.get(&request.name).cloned() { _ = context.terminate(&id).await; - Ok(DestroyContainerResponse {}) + Ok(KillContainerResponse {}) } else { enoent(format!("no such container: {}", request.name).as_str()) } } + #[derive(Serialize, Deserialize, Debug)] pub struct DoRdr { pub name: String, @@ -564,6 +574,25 @@ async fn rdr_container( Ok(request) } +#[derive(Serialize, Deserialize, Debug)] +pub struct ContainerRdrList { + pub name: String, +} + +#[ipc_method(method = "list_site_rdr")] +async fn list_site_rdr( + context: Arc>, + local_context: &mut ConnectionContext, + request: ContainerRdrList, +) -> GenericResult> { + let context = context.read().await; + if let Some(id) = context.alias_map.get(&request.name) { + Ok(context.port_forward_table.all_rules_with_id(id)) + } else { + enoent(format!("no such container: {}", request.name).as_str()) + } +} + #[derive(FromPacket)] pub struct FdImport { pub fd: Fd, @@ -695,17 +724,17 @@ async fn commit_container( } #[derive(Serialize, Deserialize, Debug)] -pub struct ReplaceMetaRequest { +pub struct SetConfigRequest { pub name: String, pub tag: String, - pub meta: xc::models::jail_image::JailConfig, + pub config: xc::models::jail_image::JailConfig, } #[ipc_method(method = "replace_meta")] async fn replace_meta( context: Arc>, local_context: &mut ConnectionContext, - request: ReplaceMetaRequest, + request: SetConfigRequest, ) -> GenericResult { let ctx = context.read().await; let dlctx = ctx.image_manager.read().await; @@ -714,7 +743,7 @@ async fn replace_meta( .await .unwrap(); let mut manifest = record.manifest; - manifest.set_config(&request.meta); + manifest.set_config(&request.config); dlctx .register_and_tag_manifest(&request.name, &request.tag, &manifest) .await @@ -860,7 +889,7 @@ pub(crate) async fn register_to_service( service.register(create_network).await; service.register(list_networks).await; service.register(show_container).await; - service.register(destroy_container).await; + service.register(kill_container).await; service.register(list_containers).await; service.register(login_registry).await; service.register(commit_container).await; diff --git a/xcd/src/lib.rs b/xcd/src/lib.rs index be50254..f3afa8a 100644 --- a/xcd/src/lib.rs +++ b/xcd/src/lib.rs @@ -31,6 +31,7 @@ mod port; mod registry; mod site; mod task; +mod util; pub mod ipc; use config_manager::ConfigManager; diff --git a/xcd/src/site.rs b/xcd/src/site.rs index bc33ca3..f53a793 100644 --- a/xcd/src/site.rs +++ b/xcd/src/site.rs @@ -29,6 +29,7 @@ use std::ffi::OsString; use std::os::fd::RawFd; use std::sync::Arc; use tokio::sync::watch::Receiver; +use tracing::info; use xc::config::XcConfig; use xc::container::effect::UndoStack; use xc::container::{Container, ContainerManifest}; @@ -110,7 +111,6 @@ impl Site { pub fn kill_conatiner(&mut self) -> anyhow::Result<()> { use freebsd::nix::sys::event::{kevent_ts, EventFilter, EventFlag, FilterFlag, KEvent}; - use tracing::error; let event = KEvent::new( 2, EventFilter::EVFILT_USER, @@ -119,7 +119,7 @@ impl Site { 0 as freebsd::libc::intptr_t, 0 as freebsd::libc::intptr_t, ); - error!("killing container"); + info!("killing container"); _ = kevent_ts(self.ctl_channel.unwrap(), &[event], &mut [], None); Ok(()) } diff --git a/xcd/src/util.rs b/xcd/src/util.rs new file mode 100644 index 0000000..7881e78 --- /dev/null +++ b/xcd/src/util.rs @@ -0,0 +1,104 @@ +// Copyright (c) 2023 Yan Ka, Chiu. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions, and the following disclaimer, +// without modification, immediately at the beginning of the file. +// 2. The name of the author may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR +// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +// SUCH DAMAGE. +use std::borrow::Borrow; +use std::collections::{HashMap, HashSet}; +use std::hash::Hash; + +pub struct TwoWayMap { + reverse_map: HashMap>, + main_map: HashMap, +} + +impl Default for TwoWayMap { + fn default() -> TwoWayMap { + TwoWayMap { + reverse_map: HashMap::new(), + main_map: HashMap::new(), + } + } +} + +impl TwoWayMap { + pub fn new() -> TwoWayMap { + TwoWayMap { + reverse_map: HashMap::new(), + main_map: HashMap::new(), + } + } + + pub fn insert(&mut self, key: K, value: V) { + self.main_map.insert(key.clone(), value.clone()); + match self.reverse_map.get_mut(&value) { + Some(vec) => { + vec.insert(key); + } + None => { + self.reverse_map + .insert(value.clone(), HashSet::from_iter([key])); + } + } + } + + pub fn get(&self, key: &Q) -> Option<&V> + where + K: Borrow, + { + self.main_map.get(key) + } + + #[allow(unused)] + pub fn contains_key(&self, key: &Q) -> Option<&V> + where + K: Borrow, + { + self.main_map.get(key) + } + + pub fn remove_all_referenced(&mut self, value: &Q) -> Option> + where + V: Borrow, + Q: Eq + Hash + ?Sized, + { + let keys = self.reverse_map.remove(value)?; + for key in keys.iter() { + self.main_map.remove(key); + } + Some(keys) + } + + #[allow(unused)] + pub fn remove(&mut self, key: &Q) -> Option + where + K: Borrow, + Q: Eq + Hash + ?Sized, + { + let value = self.main_map.remove(key); + if let Some(value) = &value { + if let Some(keys) = self.reverse_map.get_mut(value) { + keys.remove(key); + } + } + value + } +}