Allow configure log directory

This commit is contained in:
(null)
2023-07-23 23:10:11 -04:00
parent 794e094934
commit 5d4e949e76
10 changed files with 152 additions and 30 deletions

View File

@@ -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<AsyncFd<RawFd>, std::io::Error> {
@@ -82,6 +86,18 @@ impl EventFdNotify {
AsyncFd::new(new_fd)
}
pub async fn notified_take_value(&self) -> std::io::Result<u64> {
_ = 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<u64> {
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 }

View File

@@ -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)
}
}
};

View File

@@ -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<String, Network>,
#[serde(default = "default_registries")]
pub registries: String,
}

View File

@@ -86,6 +86,8 @@ pub struct CreateContainer {
pub allowing: Vec<String>,
pub image_reference: Option<ImageReference>,
pub default_router: Option<IpAddr>,
pub log_directory: Option<PathBuf>,
}
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(),
})
}
}

View File

@@ -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<Path>,
socket_path: impl AsRef<Path>,
) -> Result<SpawnInfo, ExecError> {
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()),
})
}

View File

@@ -54,6 +54,7 @@ pub struct ProcessRunnerStat {
pub(super) id: String,
pub(super) pid: u32,
pub(super) process_stat: Sender<ProcessStat>,
pub(super) exit_notify: Option<Arc<EventFdNotify>>,
pub(super) notify: Option<Arc<EventFdNotify>>,
}
@@ -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<ProcessStat>,
exit_notify: Option<Arc<EventFdNotify>>,
notify: Option<Arc<EventFdNotify>>,
) {
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<Arc<EventFdNotify>>,
notify: Option<Arc<EventFdNotify>>,
) -> Result<SpawnInfo, ExecError> {
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" {

View File

@@ -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<IpAddr>,
pub main_started_notify: Arc<EventFdNotify>,
pub log_directory: Option<PathBuf>,
}
impl RunningContainer {

View File

@@ -47,5 +47,7 @@ fn main() {
.enable_io()
.build()
.unwrap();
_ = rt.block_on(xmain());
if let Err(error) = rt.block_on(xmain()) {
eprintln!("{error:#?}")
}
}

View File

@@ -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)));

View File

@@ -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