diff --git a/src/agent/rustjail/src/process.rs b/src/agent/rustjail/src/process.rs index 3d278b7ac3..8aac0c67d4 100644 --- a/src/agent/rustjail/src/process.rs +++ b/src/agent/rustjail/src/process.rs @@ -12,7 +12,7 @@ use std::os::unix::io::RawFd; // use crate::cgroups::Manager as CgroupManager; // use crate::intelrdt::Manager as RdtManager; -use nix::fcntl::OFlag; +use nix::fcntl::{fcntl, FcntlArg, OFlag}; use nix::sys::signal::{self, Signal}; use nix::sys::socket::{self, AddressFamily, SockFlag, SockType}; use nix::sys::wait::{self, WaitStatus}; @@ -72,7 +72,13 @@ impl ProcessOperations for Process { } impl Process { - pub fn new(logger: &Logger, ocip: &OCIProcess, id: &str, init: bool) -> Result { + pub fn new( + logger: &Logger, + ocip: &OCIProcess, + id: &str, + init: bool, + pipe_size: i32, + ) -> Result { let logger = logger.new(o!("subsystem" => "process")); let mut p = Process { @@ -128,14 +134,54 @@ impl Process { p.parent_stdin = Some(pstdin); p.stdin = Some(stdin); - let (pstdout, stdout) = unistd::pipe2(OFlag::O_CLOEXEC)?; + let (pstdout, stdout) = create_extended_pipe(OFlag::O_CLOEXEC, pipe_size)?; p.parent_stdout = Some(pstdout); p.stdout = Some(stdout); - let (pstderr, stderr) = unistd::pipe2(OFlag::O_CLOEXEC)?; + let (pstderr, stderr) = create_extended_pipe(OFlag::O_CLOEXEC, pipe_size)?; p.parent_stderr = Some(pstderr); p.stderr = Some(stderr); Ok(p) } } + +fn create_extended_pipe(flags: OFlag, pipe_size: i32) -> Result<(RawFd, RawFd)> { + let (r, w) = unistd::pipe2(flags)?; + if pipe_size > 0 { + fcntl(w, FcntlArg::F_SETPIPE_SZ(pipe_size))?; + } + Ok((r, w)) +} + +#[cfg(test)] +mod tests { + use crate::process::create_extended_pipe; + use nix::fcntl::{fcntl, FcntlArg, OFlag}; + use std::fs; + use std::os::unix::io::RawFd; + + fn get_pipe_max_size() -> i32 { + fs::read_to_string("/proc/sys/fs/pipe-max-size") + .unwrap() + .trim() + .parse::() + .unwrap() + } + + fn get_pipe_size(fd: RawFd) -> i32 { + fcntl(fd, FcntlArg::F_GETPIPE_SZ).unwrap() + } + + #[test] + fn test_create_extended_pipe() { + // Test the default + let (r, w) = create_extended_pipe(OFlag::O_CLOEXEC, 0).unwrap(); + + // Test setting to the max size + let max_size = get_pipe_max_size(); + let (r, w) = create_extended_pipe(OFlag::O_CLOEXEC, max_size).unwrap(); + let actual_size = get_pipe_size(w); + assert_eq!(max_size, actual_size); + } +} diff --git a/src/agent/src/config.rs b/src/agent/src/config.rs index a55934feef..0c67bc19d9 100644 --- a/src/agent/src/config.rs +++ b/src/agent/src/config.rs @@ -12,9 +12,11 @@ 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 CONTAINER_PIPE_SIZE_OPTION: &str = "agent.container_pipe_size"; const DEFAULT_LOG_LEVEL: slog::Level = slog::Level::Info; const DEFAULT_HOTPLUG_TIMEOUT: time::Duration = time::Duration::from_secs(3); +const DEFAULT_CONTAINER_PIPE_SIZE: i32 = 0; // FIXME: unused const TRACE_MODE_FLAG: &str = "agent.trace"; @@ -28,6 +30,7 @@ pub struct agentConfig { pub hotplug_timeout: time::Duration, pub debug_console_vport: i32, pub log_vport: i32, + pub container_pipe_size: i32, } impl agentConfig { @@ -39,6 +42,7 @@ impl agentConfig { hotplug_timeout: DEFAULT_HOTPLUG_TIMEOUT, debug_console_vport: 0, log_vport: 0, + container_pipe_size: DEFAULT_CONTAINER_PIPE_SIZE, } } @@ -80,6 +84,11 @@ impl agentConfig { self.log_vport = port; } } + + if param.starts_with(format!("{}=", CONTAINER_PIPE_SIZE_OPTION).as_str()) { + let container_pipe_size = get_container_pipe_size(param)?; + self.container_pipe_size = container_pipe_size + } } Ok(()) @@ -156,6 +165,40 @@ fn get_hotplug_timeout(param: &str) -> Result { Ok(time::Duration::from_secs(value.unwrap())) } +fn get_container_pipe_size(param: &str) -> Result { + let fields: Vec<&str> = param.split("=").collect(); + + if fields.len() != 2 { + return Err( + ErrorKind::ErrorCode(String::from("invalid container pipe size parameter")).into(), + ); + } + + let key = fields[0]; + if key != CONTAINER_PIPE_SIZE_OPTION { + return Err( + ErrorKind::ErrorCode(String::from("invalid container pipe size key name")).into(), + ); + } + + let res = fields[1].parse::(); + if res.is_err() { + return Err( + ErrorKind::ErrorCode(String::from("unable to parse container pipe size")).into(), + ); + } + + let value = res.unwrap(); + if value < 0 { + return Err(ErrorKind::ErrorCode(String::from( + "container pipe size should not be negative", + )) + .into()); + } + + Ok(value) +} + #[cfg(test)] mod tests { use super::*; @@ -172,6 +215,11 @@ mod tests { const ERR_INVALID_HOTPLUG_TIMEOUT_PARAM: &str = "unable to parse hotplug timeout"; const ERR_INVALID_HOTPLUG_TIMEOUT_KEY: &str = "invalid hotplug timeout key name"; + const ERR_INVALID_CONTAINER_PIPE_SIZE: &str = "invalid container pipe size parameter"; + const ERR_INVALID_CONTAINER_PIPE_SIZE_PARAM: &str = "unable to parse container pipe size"; + const ERR_INVALID_CONTAINER_PIPE_SIZE_KEY: &str = "invalid container pipe size key name"; + const ERR_INVALID_CONTAINER_PIPE_NEGATIVE: &str = "container pipe size should not be negative"; + // helper function to make errors less crazy-long fn make_err(desc: &str) -> Error { ErrorKind::ErrorCode(desc.to_string()).into() @@ -218,6 +266,7 @@ mod tests { dev_mode: bool, log_level: slog::Level, hotplug_timeout: time::Duration, + container_pipe_size: i32, } let tests = &[ @@ -227,6 +276,7 @@ mod tests { dev_mode: true, log_level: DEFAULT_LOG_LEVEL, hotplug_timeout: DEFAULT_HOTPLUG_TIMEOUT, + container_pipe_size: DEFAULT_CONTAINER_PIPE_SIZE, }, TestData { contents: "agent.debug_console agent.devmodex", @@ -234,6 +284,7 @@ mod tests { dev_mode: false, log_level: DEFAULT_LOG_LEVEL, hotplug_timeout: DEFAULT_HOTPLUG_TIMEOUT, + container_pipe_size: DEFAULT_CONTAINER_PIPE_SIZE, }, TestData { contents: "agent.logx=debug", @@ -241,6 +292,7 @@ mod tests { dev_mode: false, log_level: DEFAULT_LOG_LEVEL, hotplug_timeout: DEFAULT_HOTPLUG_TIMEOUT, + container_pipe_size: DEFAULT_CONTAINER_PIPE_SIZE, }, TestData { contents: "agent.log=debug", @@ -248,6 +300,7 @@ mod tests { dev_mode: false, log_level: slog::Level::Debug, hotplug_timeout: DEFAULT_HOTPLUG_TIMEOUT, + container_pipe_size: DEFAULT_CONTAINER_PIPE_SIZE, }, TestData { contents: "", @@ -255,6 +308,7 @@ mod tests { dev_mode: false, log_level: DEFAULT_LOG_LEVEL, hotplug_timeout: DEFAULT_HOTPLUG_TIMEOUT, + container_pipe_size: DEFAULT_CONTAINER_PIPE_SIZE, }, TestData { contents: "foo", @@ -262,6 +316,7 @@ mod tests { dev_mode: false, log_level: DEFAULT_LOG_LEVEL, hotplug_timeout: DEFAULT_HOTPLUG_TIMEOUT, + container_pipe_size: DEFAULT_CONTAINER_PIPE_SIZE, }, TestData { contents: "foo bar", @@ -269,6 +324,7 @@ mod tests { dev_mode: false, log_level: DEFAULT_LOG_LEVEL, hotplug_timeout: DEFAULT_HOTPLUG_TIMEOUT, + container_pipe_size: DEFAULT_CONTAINER_PIPE_SIZE, }, TestData { contents: "foo bar", @@ -276,6 +332,7 @@ mod tests { dev_mode: false, log_level: DEFAULT_LOG_LEVEL, hotplug_timeout: DEFAULT_HOTPLUG_TIMEOUT, + container_pipe_size: DEFAULT_CONTAINER_PIPE_SIZE, }, TestData { contents: "foo agent bar", @@ -283,6 +340,7 @@ mod tests { dev_mode: false, log_level: DEFAULT_LOG_LEVEL, hotplug_timeout: DEFAULT_HOTPLUG_TIMEOUT, + container_pipe_size: DEFAULT_CONTAINER_PIPE_SIZE, }, TestData { contents: "foo debug_console agent bar devmode", @@ -290,6 +348,7 @@ mod tests { dev_mode: false, log_level: DEFAULT_LOG_LEVEL, hotplug_timeout: DEFAULT_HOTPLUG_TIMEOUT, + container_pipe_size: DEFAULT_CONTAINER_PIPE_SIZE, }, TestData { contents: "agent.debug_console", @@ -297,6 +356,7 @@ mod tests { dev_mode: false, log_level: DEFAULT_LOG_LEVEL, hotplug_timeout: DEFAULT_HOTPLUG_TIMEOUT, + container_pipe_size: DEFAULT_CONTAINER_PIPE_SIZE, }, TestData { contents: " agent.debug_console ", @@ -304,6 +364,7 @@ mod tests { dev_mode: false, log_level: DEFAULT_LOG_LEVEL, hotplug_timeout: DEFAULT_HOTPLUG_TIMEOUT, + container_pipe_size: DEFAULT_CONTAINER_PIPE_SIZE, }, TestData { contents: "agent.debug_console foo", @@ -311,6 +372,7 @@ mod tests { dev_mode: false, log_level: DEFAULT_LOG_LEVEL, hotplug_timeout: DEFAULT_HOTPLUG_TIMEOUT, + container_pipe_size: DEFAULT_CONTAINER_PIPE_SIZE, }, TestData { contents: " agent.debug_console foo", @@ -318,6 +380,7 @@ mod tests { dev_mode: false, log_level: DEFAULT_LOG_LEVEL, hotplug_timeout: DEFAULT_HOTPLUG_TIMEOUT, + container_pipe_size: DEFAULT_CONTAINER_PIPE_SIZE, }, TestData { contents: "foo agent.debug_console bar", @@ -325,6 +388,7 @@ mod tests { dev_mode: false, log_level: DEFAULT_LOG_LEVEL, hotplug_timeout: DEFAULT_HOTPLUG_TIMEOUT, + container_pipe_size: DEFAULT_CONTAINER_PIPE_SIZE, }, TestData { contents: "foo agent.debug_console", @@ -332,6 +396,7 @@ mod tests { dev_mode: false, log_level: DEFAULT_LOG_LEVEL, hotplug_timeout: DEFAULT_HOTPLUG_TIMEOUT, + container_pipe_size: DEFAULT_CONTAINER_PIPE_SIZE, }, TestData { contents: "foo agent.debug_console ", @@ -339,6 +404,7 @@ mod tests { dev_mode: false, log_level: DEFAULT_LOG_LEVEL, hotplug_timeout: DEFAULT_HOTPLUG_TIMEOUT, + container_pipe_size: DEFAULT_CONTAINER_PIPE_SIZE, }, TestData { contents: "agent.devmode", @@ -346,6 +412,7 @@ mod tests { dev_mode: true, log_level: DEFAULT_LOG_LEVEL, hotplug_timeout: DEFAULT_HOTPLUG_TIMEOUT, + container_pipe_size: DEFAULT_CONTAINER_PIPE_SIZE, }, TestData { contents: " agent.devmode ", @@ -353,6 +420,7 @@ mod tests { dev_mode: true, log_level: DEFAULT_LOG_LEVEL, hotplug_timeout: DEFAULT_HOTPLUG_TIMEOUT, + container_pipe_size: DEFAULT_CONTAINER_PIPE_SIZE, }, TestData { contents: "agent.devmode foo", @@ -360,6 +428,7 @@ mod tests { dev_mode: true, log_level: DEFAULT_LOG_LEVEL, hotplug_timeout: DEFAULT_HOTPLUG_TIMEOUT, + container_pipe_size: DEFAULT_CONTAINER_PIPE_SIZE, }, TestData { contents: " agent.devmode foo", @@ -367,6 +436,7 @@ mod tests { dev_mode: true, log_level: DEFAULT_LOG_LEVEL, hotplug_timeout: DEFAULT_HOTPLUG_TIMEOUT, + container_pipe_size: DEFAULT_CONTAINER_PIPE_SIZE, }, TestData { contents: "foo agent.devmode bar", @@ -374,6 +444,7 @@ mod tests { dev_mode: true, log_level: DEFAULT_LOG_LEVEL, hotplug_timeout: DEFAULT_HOTPLUG_TIMEOUT, + container_pipe_size: DEFAULT_CONTAINER_PIPE_SIZE, }, TestData { contents: "foo agent.devmode", @@ -381,6 +452,7 @@ mod tests { dev_mode: true, log_level: DEFAULT_LOG_LEVEL, hotplug_timeout: DEFAULT_HOTPLUG_TIMEOUT, + container_pipe_size: DEFAULT_CONTAINER_PIPE_SIZE, }, TestData { contents: "foo agent.devmode ", @@ -388,6 +460,7 @@ mod tests { dev_mode: true, log_level: DEFAULT_LOG_LEVEL, hotplug_timeout: DEFAULT_HOTPLUG_TIMEOUT, + container_pipe_size: DEFAULT_CONTAINER_PIPE_SIZE, }, TestData { contents: "agent.devmode agent.debug_console", @@ -395,6 +468,7 @@ mod tests { dev_mode: true, log_level: DEFAULT_LOG_LEVEL, hotplug_timeout: DEFAULT_HOTPLUG_TIMEOUT, + container_pipe_size: DEFAULT_CONTAINER_PIPE_SIZE, }, TestData { contents: "agent.devmode agent.debug_console agent.hotplug_timeout=100", @@ -402,6 +476,7 @@ mod tests { dev_mode: true, log_level: DEFAULT_LOG_LEVEL, hotplug_timeout: time::Duration::from_secs(100), + container_pipe_size: DEFAULT_CONTAINER_PIPE_SIZE, }, TestData { contents: "agent.devmode agent.debug_console agent.hotplug_timeout=0", @@ -409,6 +484,39 @@ mod tests { dev_mode: true, log_level: DEFAULT_LOG_LEVEL, hotplug_timeout: DEFAULT_HOTPLUG_TIMEOUT, + container_pipe_size: DEFAULT_CONTAINER_PIPE_SIZE, + }, + TestData { + contents: "agent.devmode agent.debug_console agent.container_pipe_size=2097152", + debug_console: true, + dev_mode: true, + log_level: DEFAULT_LOG_LEVEL, + hotplug_timeout: DEFAULT_HOTPLUG_TIMEOUT, + container_pipe_size: 2097152, + }, + TestData { + contents: "agent.devmode agent.debug_console agent.container_pipe_size=100", + debug_console: true, + dev_mode: true, + log_level: DEFAULT_LOG_LEVEL, + hotplug_timeout: DEFAULT_HOTPLUG_TIMEOUT, + container_pipe_size: 100, + }, + TestData { + contents: "agent.devmode agent.debug_console agent.container_pipe_size=0", + debug_console: true, + dev_mode: true, + log_level: DEFAULT_LOG_LEVEL, + hotplug_timeout: DEFAULT_HOTPLUG_TIMEOUT, + container_pipe_size: DEFAULT_CONTAINER_PIPE_SIZE, + }, + TestData { + contents: "agent.devmode agent.debug_console agent.container_pip_siz=100", + debug_console: true, + dev_mode: true, + log_level: DEFAULT_LOG_LEVEL, + hotplug_timeout: DEFAULT_HOTPLUG_TIMEOUT, + container_pipe_size: DEFAULT_CONTAINER_PIPE_SIZE, }, ]; @@ -438,9 +546,15 @@ mod tests { .expect(&format!("{}: failed to write file contents", msg)); let mut config = agentConfig::new(); - assert!(config.debug_console == false, msg); - assert!(config.dev_mode == false, msg); - assert!(config.hotplug_timeout == time::Duration::from_secs(3), msg); + assert_eq!(config.debug_console, false, "{}", msg); + assert_eq!(config.dev_mode, false, "{}", msg); + assert_eq!( + config.hotplug_timeout, + time::Duration::from_secs(3), + "{}", + msg + ); + assert_eq!(config.container_pipe_size, 0, "{}", msg); let result = config.parse_cmdline(filename); assert!(result.is_ok(), "{}", msg); @@ -449,6 +563,7 @@ mod tests { assert_eq!(d.dev_mode, config.dev_mode, "{}", msg); assert_eq!(d.log_level, config.log_level, "{}", msg); assert_eq!(d.hotplug_timeout, config.hotplug_timeout, "{}", msg); + assert_eq!(d.container_pipe_size, config.container_pipe_size, "{}", msg); } } @@ -689,4 +804,78 @@ mod tests { assert_result!(d.result, result, format!("{}", msg)); } } + + #[test] + fn test_get_container_pipe_size() { + #[derive(Debug)] + struct TestData<'a> { + param: &'a str, + result: Result, + } + + let tests = &[ + TestData { + param: "", + result: Err(make_err(ERR_INVALID_CONTAINER_PIPE_SIZE)), + }, + TestData { + param: "agent.container_pipe_size", + result: Err(make_err(ERR_INVALID_CONTAINER_PIPE_SIZE)), + }, + TestData { + param: "foo=bar", + result: Err(make_err(ERR_INVALID_CONTAINER_PIPE_SIZE_KEY)), + }, + TestData { + param: "agent.container_pip_siz=1", + result: Err(make_err(ERR_INVALID_CONTAINER_PIPE_SIZE_KEY)), + }, + TestData { + param: "agent.container_pipe_size=1", + result: Ok(1), + }, + TestData { + param: "agent.container_pipe_size=3", + result: Ok(3), + }, + TestData { + param: "agent.container_pipe_size=2097152", + result: Ok(2097152), + }, + TestData { + param: "agent.container_pipe_size=0", + result: Ok(0), + }, + TestData { + param: "agent.container_pipe_size=-1", + result: Err(make_err(ERR_INVALID_CONTAINER_PIPE_NEGATIVE)), + }, + TestData { + param: "agent.container_pipe_size=foobar", + result: Err(make_err(ERR_INVALID_CONTAINER_PIPE_SIZE_PARAM)), + }, + TestData { + param: "agent.container_pipe_size=j", + result: Err(make_err(ERR_INVALID_CONTAINER_PIPE_SIZE_PARAM)), + }, + TestData { + param: "agent.container_pipe_size=4jbsdja", + result: Err(make_err(ERR_INVALID_CONTAINER_PIPE_SIZE_PARAM)), + }, + TestData { + param: "agent.container_pipe_size=4294967296", + result: Err(make_err(ERR_INVALID_CONTAINER_PIPE_SIZE_PARAM)), + }, + ]; + + for (i, d) in tests.iter().enumerate() { + let msg = format!("test[{}]: {:?}", i, d); + + let result = get_container_pipe_size(d.param); + + let msg = format!("{}: result: {:?}", msg, result); + + assert_result!(d.result, result, format!("{}", msg)); + } + } } diff --git a/src/agent/src/grpc.rs b/src/agent/src/grpc.rs index b6d3eeaacf..b336156435 100644 --- a/src/agent/src/grpc.rs +++ b/src/agent/src/grpc.rs @@ -36,6 +36,7 @@ use crate::namespace::{NSTYPEIPC, NSTYPEPID, NSTYPEUTS}; use crate::random; use crate::sandbox::Sandbox; use crate::version::{AGENT_VERSION, API_VERSION}; +use crate::AGENT_CONFIG; use netlink::{RtnlHandle, NETLINK_ROUTE}; use libc::{self, c_ushort, pid_t, winsize, TIOCSWINSZ}; @@ -139,8 +140,9 @@ impl agentService { let mut ctr: LinuxContainer = LinuxContainer::new(cid.as_str(), CONTAINER_BASE, opts, &sl!())?; + let pipe_size = AGENT_CONFIG.read().unwrap().container_pipe_size; let p = if oci.Process.is_some() { - let tp = Process::new(&sl!(), oci.get_Process(), eid.as_str(), true)?; + let tp = Process::new(&sl!(), oci.get_Process(), eid.as_str(), true, pipe_size)?; tp } else { info!(sl!(), "no process configurations!"); @@ -274,7 +276,8 @@ impl agentService { return Err(ErrorKind::Nix(nix::Error::from_errno(nix::errno::Errno::EINVAL)).into()); }; - let p = Process::new(&sl!(), ocip, exec_id.as_str(), false)?; + let pipe_size = AGENT_CONFIG.read().unwrap().container_pipe_size; + let p = Process::new(&sl!(), ocip, exec_id.as_str(), false, pipe_size)?; let ctr = match sandbox.get_container(cid.as_str()) { Some(v) => v,