mirror of
https://github.com/michael-yuji/xc.git
synced 2026-03-22 08:44:54 +01:00
Allow configure log directory
This commit is contained in:
@@ -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 }
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()),
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -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" {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -47,5 +47,7 @@ fn main() {
|
||||
.enable_io()
|
||||
.build()
|
||||
.unwrap();
|
||||
_ = rt.block_on(xmain());
|
||||
if let Err(error) = rt.block_on(xmain()) {
|
||||
eprintln!("{error:#?}")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)));
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user