diff --git a/xc-bin/src/jailfile/directives/from.rs b/xc-bin/src/jailfile/directives/from.rs index e011a0e..47a9682 100644 --- a/xc-bin/src/jailfile/directives/from.rs +++ b/xc-bin/src/jailfile/directives/from.rs @@ -22,20 +22,25 @@ // OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF // SUCH DAMAGE. + use crate::jailfile::parse::Action; use crate::jailfile::JailContext; +use super::Directive; use anyhow::{bail, Result}; +use ipc::packet::codec::{List, Maybe}; use oci_util::image_reference::ImageReference; -use xc::util::gen_id; +use std::collections::HashMap; +use xc::{util::gen_id, models::network::DnsSetting}; +use xcd::ipc::*; pub(crate) struct FromDirective { image_reference: ImageReference, alias: Option, } -impl FromDirective { - pub(crate) fn from_action(action: &Action) -> Result { +impl Directive for FromDirective { + fn from_action(action: &Action) -> Result { if action.directive_name != "FROM" { bail!("directive_name is not FROM"); } @@ -56,7 +61,7 @@ impl FromDirective { } } - pub(crate) fn run_in_context(&self, context: &mut JailContext) -> Result<()> { + fn run_in_context(&self, context: &mut JailContext) -> Result<()> { if let Some(container_id) = &context.container_id { let tagged_containers = context.containers.values().collect::>(); if !tagged_containers.contains(&container_id) { @@ -65,12 +70,42 @@ impl FromDirective { } let name = format!("build-{}", gen_id()); /* create container */ - if let Some(alias) = &self.alias { - context - .containers - .insert(alias.to_string(), name.to_string()); + let req = InstantiateRequest { + alt_root: None, + name: Some(name.to_string()), + hostname: None, + copies: List::new(), + dns: DnsSetting::Inherit, + image_reference: self.image_reference.clone(), + no_clean: false, + main_norun: true, + init_norun: true, + deinit_norun: true, + persist: true, + ips: Vec::new(), + main_started_notify: Maybe::None, + entry_point: "main".to_string(), + entry_point_args: Vec::new(), + envs: HashMap::new(), + vnet: false, + mount_req: Vec::new(), + ipreq: Vec::new() + }; + eprintln!("before instantiate"); + match do_instantiate(&mut context.conn, req)? { + Ok(response) => { + eprintln!("instantiate resspones: {response:?}"); + if let Some(alias) = &self.alias { + context + .containers + .insert(alias.to_string(), name.to_string()); + } + context.container_id = Some(name); + Ok(()) + }, + Err(err) => { + bail!("instantiation failure: {err:?}") + } } - context.container_id = Some(name); - Ok(()) } } diff --git a/xc-bin/src/jailfile/directives/mod.rs b/xc-bin/src/jailfile/directives/mod.rs index a8bb44f..10588ba 100644 --- a/xc-bin/src/jailfile/directives/mod.rs +++ b/xc-bin/src/jailfile/directives/mod.rs @@ -23,3 +23,11 @@ // SUCH DAMAGE. pub mod from; pub mod run; + +use crate::jailfile::parse::Action; +use super::JailContext; + +pub(crate) trait Directive: Sized { + fn from_action(action: &Action) -> Result; + fn run_in_context(&self, context: &mut JailContext) -> Result<(), anyhow::Error>; +} diff --git a/xc-bin/src/jailfile/directives/run.rs b/xc-bin/src/jailfile/directives/run.rs index 8b13789..c25b718 100644 --- a/xc-bin/src/jailfile/directives/run.rs +++ b/xc-bin/src/jailfile/directives/run.rs @@ -1 +1,116 @@ +// 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 crate::jailfile::JailContext; +use crate::jailfile::parse::Action; +use super::Directive; + +use anyhow::{bail, Result}; +use freebsd::event::EventFdNotify; +use ipc::packet::codec::{Maybe, Fd}; +use nix::unistd::pipe; +use std::collections::HashMap; +use std::fs::File; +use std::os::fd::AsRawFd; +use xcd::ipc::*; + +enum Input { + File(File), + Content(String), + None +} + +pub(crate) struct RunDirective { + arg0: String, + args: Vec, + envs: HashMap, + input: Input +} + +impl Directive for RunDirective { + fn from_action(action: &Action) -> Result { + let mut args_iter = action.args.iter(); + let mut envs = HashMap::new(); + let mut args = Vec::new(); + + let mut curr = args_iter.next(); + + while let Some((key, value)) = curr.and_then(|c| c.split_once('=')) { + envs.insert(key.to_string(), value.to_string()); + curr = args_iter.next(); + } + + let Some(arg0) = curr else { + bail!("cannot determine arg0"); + }; + + for arg in args_iter { + args.push(arg.to_string()); + } + + let input = match &action.heredoc { + Some(value) => Input::Content(value.to_string()), + None => Input::None + }; + + Ok(RunDirective { + arg0: arg0.to_string(), + args, + envs, + input + }) + } + + fn run_in_context(&self, context: &mut JailContext) -> Result<()> { + let notify = EventFdNotify::new(); + /* + let (stdout_a, stdout_b) = pipe()?; + let (stderr_a, stderr_b) = pipe()?; + let (stdin_a, stdin_b) = pipe()?; + */ + eprintln!("arg0: {}, args: {:?}", self.arg0, self.args); + let request = ExecCommandRequest { + name: context.container_id.clone().expect("container not set"), + arg0: self.arg0.clone(), + args: self.args.clone(), + envs: self.envs.clone(), + stdin: Maybe::None,//Maybe::Some(Fd(stdin_b)), + stdout:Maybe::None,// Maybe::Some(Fd(stdout_b)), + stderr:Maybe::None,// Maybe::Some(Fd(stderr_b)), + uid: 0, + notify: Maybe::Some(Fd(notify.as_raw_fd())) + }; + eprintln!("before do_exec"); + match do_exec(&mut context.conn, request)? { + Ok(_) => { + eprintln!("before wait sync"); + notify.notified_sync(); + eprintln!("after wait sync"); + Ok(()) + }, + Err(err) => bail!("exec failure: {err:?}") + } + } +} diff --git a/xc-bin/src/jailfile/mod.rs b/xc-bin/src/jailfile/mod.rs index 560a8d1..215f0ea 100644 --- a/xc-bin/src/jailfile/mod.rs +++ b/xc-bin/src/jailfile/mod.rs @@ -24,8 +24,10 @@ pub mod directives; pub mod parse; -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; use std::os::unix::net::UnixStream; +use tracing::error; +use xcd::ipc::*; pub(crate) struct JailContext { /// The current container we are operating in @@ -33,5 +35,33 @@ pub(crate) struct JailContext { /// Mapping to different containers for multi-stage build pub(crate) containers: HashMap, - conn: UnixStream, + pub(crate) conn: UnixStream, +} + +impl JailContext { + pub(crate) fn new(conn: UnixStream) -> JailContext { + JailContext { + conn, + container_id: None, + containers: HashMap::new() + } + } + + pub(crate) fn release(mut self) -> anyhow::Result<()> { + let mut containers = HashSet::new(); + if let Some(container) = self.container_id { + containers.insert(container); + } + for (_, container) in self.containers.into_iter() { + containers.insert(container); + } + for name in containers.into_iter() { + let kill = KillContainerRequest { name: name.to_string() }; + match do_kill_container(&mut self.conn, kill)? { + Ok(_) => {}, + Err(error) => { error!("cannot kill container {name}: {error:?}"); } + } + } + Ok(()) + } } diff --git a/xc-bin/src/main.rs b/xc-bin/src/main.rs index 1101cfb..8f71bc4 100644 --- a/xc-bin/src/main.rs +++ b/xc-bin/src/main.rs @@ -82,6 +82,9 @@ enum Action { Attach { name: String, }, + Build { + image_reference: ImageReference + }, #[clap(subcommand)] Channel(ChannelAction), Commit { @@ -230,6 +233,51 @@ fn main() -> Result<(), ActionError> { } } } + Action::Build { image_reference } => { + use crate::jailfile::*; + use crate::jailfile::parse::*; + use crate::jailfile::directives::*; + use crate::jailfile::directives::run::*; + use crate::jailfile::directives::from::*; + let file = std::fs::read_to_string("Jailfile")?; + + let is_image_existed = do_describe_image(&mut conn, DescribeImageRequest { + image_name: image_reference.name.to_string(), + tag: image_reference.tag.to_string() + })?; + + if is_image_existed.is_ok() { + Err(anyhow::anyhow!("image already exist"))?; + } + + let actions = parse_jailfile(&file)?; + let mut context = JailContext::new(conn); + + eprintln!("actions: {actions:#?}"); + + for action in actions.iter() { + if action.directive_name == "RUN" { + let directive = RunDirective::from_action(action)?; + directive.run_in_context(&mut context)?; + } else if action.directive_name == "FROM" { + let directive = FromDirective::from_action(action)?; + directive.run_in_context(&mut context)?; + std::thread::sleep_ms(500); + } + } + + let req = CommitRequest { + name: image_reference.name, + tag: image_reference.tag.to_string(), + container_name: context.container_id.clone().unwrap(), + }; + let response = do_commit_container(&mut context.conn, req)?.unwrap(); + eprintln!("{response:#?}"); + + context.release()?; + // let response: CommitResponse = request(&mut conn, "commit", req)?; + + } Action::Channel(action) => { use_channel_action(&mut conn, action)?; }