diff --git a/src/agent/Cargo.toml b/src/agent/Cargo.toml index 6f97b80428..e325193a9a 100644 --- a/src/agent/Cargo.toml +++ b/src/agent/Cargo.toml @@ -15,7 +15,7 @@ grpcio = { git="https://github.com/alipay/grpc-rs", branch="rust_agent" } protobuf = "2.6.1" futures = "0.1.27" libc = "0.2.58" -nix = "0.14.1" +nix = "0.17.0" prctl = "1.0.0" serde_json = "1.0.39" signal-hook = "0.1.9" diff --git a/src/agent/netlink/Cargo.toml b/src/agent/netlink/Cargo.toml index 2b2bf0af5f..53d50cad48 100644 --- a/src/agent/netlink/Cargo.toml +++ b/src/agent/netlink/Cargo.toml @@ -8,7 +8,7 @@ edition = "2018" [dependencies] libc = "0.2.58" -nix = "0.14.1" +nix = "0.17.0" protobuf = "2.6.1" rustjail = { path = "../rustjail" } protocols = { path = "../protocols" } diff --git a/src/agent/rustjail/Cargo.toml b/src/agent/rustjail/Cargo.toml index 6fdcde2fb7..f85798c65e 100644 --- a/src/agent/rustjail/Cargo.toml +++ b/src/agent/rustjail/Cargo.toml @@ -12,7 +12,7 @@ serde_derive = "1.0.91" oci = { path = "../oci" } protocols = { path ="../protocols" } caps = "0.3.0" -nix = "0.14.1" +nix = "0.17.0" scopeguard = "1.0.0" prctl = "1.0.0" lazy_static = "1.3.0" diff --git a/src/agent/rustjail/src/container.rs b/src/agent/rustjail/src/container.rs index 5e4c1c1dc5..81f9bdca89 100644 --- a/src/agent/rustjail/src/container.rs +++ b/src/agent/rustjail/src/container.rs @@ -6,7 +6,7 @@ use lazy_static; use protocols::oci::{Hook, Linux, LinuxNamespace, LinuxResources, POSIXRlimit, Spec}; use serde_json; -use std::ffi::CString; +use std::ffi::{CStr, CString}; use std::fs; use std::mem; use std::os::unix::io::RawFd; @@ -672,10 +672,11 @@ fn do_exec(logger: &Logger, path: &str, args: &[String], env: &[String]) -> Resu let logger = logger.new(o!("command" => "exec")); let p = CString::new(path.to_string()).unwrap(); - let a: Vec = args + let sa: Vec = args .iter() .map(|s| CString::new(s.to_string()).unwrap_or_default()) .collect(); + let a: Vec<&CStr> = sa.iter().map(|s| s.as_c_str()).collect(); for (key, _) in env::vars() { env::remove_var(key); @@ -696,7 +697,7 @@ fn do_exec(logger: &Logger, path: &str, args: &[String], env: &[String]) -> Resu */ // execvp doesn't use env for the search path, so we set env manually debug!(logger, "exec process right now!"); - if let Err(e) = unistd::execvp(&p, &a) { + if let Err(e) = unistd::execvp(p.as_c_str(), a.as_slice()) { info!(logger, "execve failed!!!"); info!(logger, "binary: {:?}, args: {:?}, envs: {:?}", p, a, env); match e { diff --git a/src/agent/src/config.rs b/src/agent/src/config.rs index 9bd191113c..a55934feef 100644 --- a/src/agent/src/config.rs +++ b/src/agent/src/config.rs @@ -10,6 +10,8 @@ const DEBUG_CONSOLE_FLAG: &str = "agent.debug_console"; const DEV_MODE_FLAG: &str = "agent.devmode"; const LOG_LEVEL_OPTION: &str = "agent.log"; const HOTPLUG_TIMOUT_OPTION: &str = "agent.hotplug_timeout"; +const DEBUG_CONSOLE_VPORT_OPTION: &str = "agent.debug_console_vport"; +const LOG_VPORT_OPTION: &str = "agent.log_vport"; const DEFAULT_LOG_LEVEL: slog::Level = slog::Level::Info; const DEFAULT_HOTPLUG_TIMEOUT: time::Duration = time::Duration::from_secs(3); @@ -24,6 +26,8 @@ pub struct agentConfig { pub dev_mode: bool, pub log_level: slog::Level, pub hotplug_timeout: time::Duration, + pub debug_console_vport: i32, + pub log_vport: i32, } impl agentConfig { @@ -33,6 +37,8 @@ impl agentConfig { dev_mode: false, log_level: DEFAULT_LOG_LEVEL, hotplug_timeout: DEFAULT_HOTPLUG_TIMEOUT, + debug_console_vport: 0, + log_vport: 0, } } @@ -60,12 +66,35 @@ impl agentConfig { self.hotplug_timeout = hotplugTimeout; } } + + if param.starts_with(format!("{}=", DEBUG_CONSOLE_VPORT_OPTION).as_str()) { + let port = get_vsock_port(param)?; + if port > 0 { + self.debug_console_vport = port; + } + } + + if param.starts_with(format!("{}=", LOG_VPORT_OPTION).as_str()) { + let port = get_vsock_port(param)?; + if port > 0 { + self.log_vport = port; + } + } } Ok(()) } } +fn get_vsock_port(p: &str) -> Result { + let fields: Vec<&str> = p.split("=").collect(); + if fields.len() != 2 { + return Err(ErrorKind::ErrorCode("invalid port parameter".to_string()).into()); + } + + Ok(fields[1].parse::()?) +} + // Map logrus (https://godoc.org/github.com/sirupsen/logrus) // log level to the equivalent slog log levels. // diff --git a/src/agent/src/grpc.rs b/src/agent/src/grpc.rs index 56a7c115dc..1946005657 100644 --- a/src/agent/src/grpc.rs +++ b/src/agent/src/grpc.rs @@ -40,6 +40,7 @@ use netlink::{RtnlHandle, NETLINK_ROUTE}; use libc::{self, c_ushort, pid_t, winsize, TIOCSWINSZ}; use serde_json; +use std::convert::TryFrom; use std::fs; use std::os::unix::io::RawFd; use std::os::unix::prelude::PermissionsExt; @@ -303,7 +304,7 @@ impl agentService { ); let p = find_process(&mut sandbox, cid.as_str(), eid.as_str(), true)?; - let mut signal = Signal::from_c_int(req.signal as i32).unwrap(); + let mut signal = Signal::try_from(req.signal as i32).unwrap(); // For container initProcess, if it hasn't installed handler for "SIGTERM" signal, // it will ignore the "SIGTERM" signal sent to it, thus send it "SIGKILL" signal diff --git a/src/agent/src/main.rs b/src/agent/src/main.rs index b8a54698f3..2ab806ab1c 100644 --- a/src/agent/src/main.rs +++ b/src/agent/src/main.rs @@ -31,6 +31,8 @@ extern crate slog_json; extern crate netlink; use futures::*; +use nix::fcntl::{self, OFlag}; +use nix::sys::socket::{self, AddressFamily, SockAddr, SockFlag, SockType}; use nix::sys::wait::{self, WaitStatus}; use nix::unistd; use prctl::set_child_subreaper; @@ -38,7 +40,7 @@ use rustjail::errors::*; use signal_hook::{iterator::Signals, SIGCHLD}; use std::collections::HashMap; use std::env; -use std::fs; +use std::fs::{self, File}; use std::os::unix::fs as unixfs; use std::os::unix::io::AsRawFd; use std::path::Path; @@ -108,13 +110,17 @@ fn main() -> Result<()> { lazy_static::initialize(&SHELLS); lazy_static::initialize(&AGENT_CONFIG); + + // support vsock log + let (rfd, wfd) = unistd::pipe2(OFlag::O_CLOEXEC)?; + let writer = unsafe { File::from_raw_fd(wfd) }; + let agentConfig = AGENT_CONFIG.clone(); if unistd::getpid() == Pid::from_raw(1) { // Init a temporary logger used by init agent as init process // since before do the base mount, it wouldn't access "/proc/cmdline" // to get the customzied debug level. - let writer = io::stdout(); let logger = logging::create_logger(NAME, "agent", slog::Level::Debug, writer); init_agent_as_init(&logger)?; } @@ -128,7 +134,32 @@ fn main() -> Result<()> { } let config = agentConfig.read().unwrap(); - let writer = io::stdout(); + let log_vport = config.log_vport as u32; + let log_handle = thread::spawn(move || -> Result<()> { + let mut reader = unsafe { File::from_raw_fd(rfd) }; + if log_vport > 0 { + let listenfd = socket::socket( + AddressFamily::Vsock, + SockType::Stream, + SockFlag::SOCK_CLOEXEC, + None, + )?; + let addr = SockAddr::new_vsock(libc::VMADDR_CID_ANY, log_vport); + socket::bind(listenfd, &addr)?; + socket::listen(listenfd, 1)?; + let datafd = socket::accept4(listenfd, SockFlag::SOCK_CLOEXEC)?; + let mut log_writer = unsafe { File::from_raw_fd(datafd) }; + let _ = io::copy(&mut reader, &mut log_writer)?; + let _ = unistd::close(listenfd); + let _ = unistd::close(datafd); + } + // copy log to stdout + let mut stdout_writer = io::stdout(); + let _ = io::copy(&mut reader, &mut stdout_writer)?; + Ok(()) + }); + + let writer = unsafe { File::from_raw_fd(wfd) }; // Recreate a logger with the log level get from "/proc/cmdline". let logger = logging::create_logger(NAME, "agent", config.log_level, writer); @@ -146,13 +177,14 @@ fn main() -> Result<()> { let _guard = slog_scope::set_global_logger(logger.new(o!("subsystem" => "grpc"))); let shells = SHELLS.clone(); + let debug_console_vport = config.debug_console_vport as u32; let shell_handle = if config.debug_console { let thread_logger = logger.clone(); thread::spawn(move || { let shells = shells.lock().unwrap(); - let result = setup_debug_console(shells.to_vec()); + let result = setup_debug_console(shells.to_vec(), debug_console_vport); if result.is_err() { // Report error, but don't fail warn!(thread_logger, "failed to setup debug console"; @@ -199,6 +231,7 @@ fn main() -> Result<()> { // let _ = rx.wait(); handle.join().unwrap(); + let _ = log_handle.join(); if config.debug_console { shell_handle.join().unwrap(); @@ -333,18 +366,35 @@ lazy_static! { // pub static mut TRACE_MODE: ; use crate::config::agentConfig; -use nix::fcntl::{self, OFlag}; use nix::sys::stat::Mode; use std::os::unix::io::{FromRawFd, RawFd}; use std::path::PathBuf; use std::process::{exit, Command, Stdio}; -fn setup_debug_console(shells: Vec) -> Result<()> { +fn setup_debug_console(shells: Vec, port: u32) -> Result<()> { for shell in shells.iter() { let binary = PathBuf::from(shell); if binary.exists() { - let f: RawFd = fcntl::open(CONSOLE_PATH, OFlag::O_RDWR, Mode::empty())?; + let f: RawFd = if port > 0 { + let listenfd = socket::socket( + AddressFamily::Vsock, + SockType::Stream, + SockFlag::SOCK_CLOEXEC, + None, + )?; + let addr = SockAddr::new_vsock(libc::VMADDR_CID_ANY, port); + socket::bind(listenfd, &addr)?; + socket::listen(listenfd, 1)?; + socket::accept4(listenfd, SockFlag::SOCK_CLOEXEC)? + } else { + let mut flags = OFlag::empty(); + flags.insert(OFlag::O_RDWR); + flags.insert(OFlag::O_CLOEXEC); + fcntl::open(CONSOLE_PATH, flags, Mode::empty())? + }; + let cmd = Command::new(shell) + .arg("-i") .stdin(unsafe { Stdio::from_raw_fd(f) }) .stdout(unsafe { Stdio::from_raw_fd(f) }) .stderr(unsafe { Stdio::from_raw_fd(f) }) @@ -384,7 +434,7 @@ mod tests { let mut shells = shells_ref.lock().unwrap(); shells.clear(); - let result = setup_debug_console(shells.to_vec()); + let result = setup_debug_console(shells.to_vec(), 0); assert!(result.is_err()); assert_eq!(result.unwrap_err().to_string(), "Error Code: 'no shell'"); @@ -407,7 +457,7 @@ mod tests { shells.push(shell); - let result = setup_debug_console(shells.to_vec()); + let result = setup_debug_console(shells.to_vec(), 0); assert!(result.is_err()); assert_eq!(