vsock: support log_vport and debug_console_vport

Fixes: #61, #64

Signed-off-by: Yang Bo <bo@hyper.sh>
This commit is contained in:
Yang Bo 2020-02-04 20:28:48 +08:00
parent b5e741ba8b
commit 3c1252ea79
7 changed files with 97 additions and 16 deletions

View File

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

View File

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

View File

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

View File

@ -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<CString> = args
let sa: Vec<CString> = 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 {

View File

@ -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<i32> {
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::<i32>()?)
}
// Map logrus (https://godoc.org/github.com/sirupsen/logrus)
// log level to the equivalent slog log levels.
//

View File

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

View File

@ -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<String>) -> Result<()> {
fn setup_debug_console(shells: Vec<String>, 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!(