Skip to content

Commit

Permalink
Replace manifest IO Pipe with Log
Browse files Browse the repository at this point in the history
The `pipe` option for the container output is removed from the manifest.
The option `log` is renamed to `pipe`. A new option `discard` is added
for the output.
  • Loading branch information
flxo authored and Alfonso Ros committed Jan 31, 2022
1 parent ce615b3 commit 5411d42
Show file tree
Hide file tree
Showing 19 changed files with 191 additions and 123 deletions.
6 changes: 2 additions & 4 deletions examples/console/manifest.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,8 @@ console: true
uid: 1000
gid: 1000
io:
stdout:
log:
level: DEBUG
tag: console
stdout: pipe
stderr: pipe
mounts:
/dev:
type: dev
Expand Down
6 changes: 2 additions & 4 deletions examples/cpueater/manifest.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,5 @@ mounts:
type: bind
host: /system
io:
stdout:
log:
level: DEBUG
tag: cpueater
stdout: pipe
stderr: pipe
6 changes: 2 additions & 4 deletions examples/crashing/manifest.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,5 @@ mounts:
type: bind
host: /system
io:
stdout:
log:
level: DEBUG
tag: crashing
stdout: pipe
stderr: pipe
6 changes: 2 additions & 4 deletions examples/hello-ferris/manifest.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,5 @@ mounts:
dir: /
options: noexec,nodev,nosuid
io:
stdout:
log:
level: DEBUG
tag: ferris
stdout: pipe
stderr: pipe
6 changes: 2 additions & 4 deletions examples/hello-resource/manifest.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,5 @@ mounts:
type: bind
host: /system
io:
stdout:
log:
level: DEBUG
tag: hello
stdout: pipe
stderr: pipe
6 changes: 2 additions & 4 deletions examples/hello-world/manifest.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,8 @@ gid: 1000
env:
HELLO: northstar
io:
stdout:
log:
level: DEBUG
tag: hello
stdout: pipe
stderr: pipe
mounts:
/dev:
type: dev
Expand Down
12 changes: 3 additions & 9 deletions examples/inspect/manifest.yaml
Original file line number Diff line number Diff line change
@@ -1,17 +1,11 @@
name: inspect
name: inspect
version: 0.0.1
init: /inspect
uid: 1000
gid: 1000
io:
stdout:
log:
level: DEBUG
tag: inspect
stderr:
log:
level: WARN
tag: inspect
stdout: pipe
stderr: pipe
mounts:
/dev:
type: dev
Expand Down
6 changes: 2 additions & 4 deletions examples/memeater/manifest.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,5 @@ mounts:
type: bind
host: /system
io:
stdout:
log:
level: DEBUG
tag: memeater
stdout: pipe
stderr: pipe
6 changes: 2 additions & 4 deletions examples/persistence/manifest.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,5 @@ mounts:
type: bind
host: /system
io:
stdout:
log:
level: DEBUG
tag: persistence
stdout: pipe
stderr: pipe
8 changes: 3 additions & 5 deletions examples/seccomp/manifest.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,8 @@ mounts:
type: bind
host: /system
io:
stdout:
log:
level: DEBUG
tag: seccomp
stdout: pipe
stderr: pipe
seccomp:
profile:
default
default
11 changes: 3 additions & 8 deletions northstar-tests/test-container/manifest.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,9 @@ mounts:
dir: test
options: nosuid,nodev,noexec
io:
stdout:
log:
level: DEBUG
tag: test-container
stderr:
log:
level: DEBUG
tag: test-container
stdout: pipe
stderr: pipe

rlimits:
nproc:
soft: 10000
Expand Down
2 changes: 1 addition & 1 deletion northstar-tests/tests/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -350,7 +350,7 @@ test!(container_shall_only_have_configured_fds, {
client()
.start_with_args(TEST_CONTAINER, ["inspect"])
.await?;
assume("/proc/self/fd/0: /dev/pts", 5).await?;
assume("/proc/self/fd/0: /dev/null", 5).await?;
assume("/proc/self/fd/1: /dev/pts", 5).await?;
assume("/proc/self/fd/2: /dev/pts", 5).await?;
assume("total: 3", 5).await?;
Expand Down
45 changes: 24 additions & 21 deletions northstar/src/npk/manifest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,8 @@ pub struct Manifest {
/// Resource limits
pub rlimits: Option<HashMap<RLimitResource, RLimitValue>>,
/// IO configuration
pub io: Option<Io>,
#[serde(default, skip_serializing_if = "Io::is_default")]
pub io: Io,
/// Optional custom data. The runtime doesnt use this.
pub custom: Option<Value>,
}
Expand Down Expand Up @@ -97,7 +98,6 @@ impl Manifest {
|| self.seccomp.is_some()
|| self.capabilities.is_some()
|| self.suppl_groups.is_some()
|| self.io.is_some()
{
return Err(Error::Invalid(
"Resource containers must not define any of the following manifest entries:\
Expand Down Expand Up @@ -444,31 +444,37 @@ pub enum Mount {
}

/// IO configuration for stdin, stdout, stderr
#[derive(Clone, Eq, PartialEq, Debug, Serialize, Deserialize, JsonSchema)]
#[derive(Default, Clone, Eq, PartialEq, Debug, Serialize, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct Io {
/// stdout configuration
#[serde(skip_serializing_if = "Option::is_none")]
pub stdout: Option<Output>,
pub stdout: Output,
/// stderr configuration
#[serde(skip_serializing_if = "Option::is_none")]
pub stderr: Option<Output>,
pub stderr: Output,
}

impl Io {
/// Returns true if the io configuration is the default one
pub fn is_default(&self) -> bool {
self.stdout == Output::default() && self.stderr == Output::default()
}
}

/// Io redirection for stdout/stderr
#[derive(Clone, Eq, PartialEq, Debug, Serialize, Deserialize, JsonSchema)]
pub enum Output {
/// Inherit the runtimes stdout/stderr
/// Discard output
#[serde(rename = "discard")]
Discard,
/// Forward output to the logging system with level and optional tag
#[serde(rename = "pipe")]
Pipe,
/// Forward output to the logging system with level and optional tag
#[serde(rename = "log")]
Log {
/// Level
level: Level,
/// Tag
tag: String,
},
}

impl Default for Output {
fn default() -> Output {
Output::Discard
}
}

/// Log level
Expand Down Expand Up @@ -840,7 +846,7 @@ mounts:
options: noexec
autostart: critical
seccomp:
allow:
allow:
fork: any
waitpid: any
cgroups:
Expand Down Expand Up @@ -1099,10 +1105,7 @@ seccomp:
capabilities:
- CAP_NET_ADMIN
io:
stdout:
log:
level: DEBUG
tag: test
stdout: pipe
stderr: pipe
cgroups:
memory:
Expand Down
76 changes: 59 additions & 17 deletions northstar/src/runtime/fork/forker/impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,21 @@ use nix::{
};
use std::{
collections::HashMap,
os::unix::{net::UnixStream, prelude::RawFd},
os::unix::{
net::UnixStream,
prelude::{AsRawFd, RawFd},
},
thread::ThreadId,
};
use tokio::select;

type Inits = HashMap<Container, (Pid, AsyncMessage)>;
type Inits = HashMap<Container, InitComm>;

struct InitComm {
pid: Pid,
message: AsyncMessage,
share_fds: UnixStream,
}

/// Entry point of the forker process
pub async fn run(
Expand Down Expand Up @@ -84,8 +93,20 @@ pub async fn run(

create(&mut inits, init, console_fd)
}
Some(Message::ExecRequest { container, path, args, env }) => {
let (response, exit) = exec(&mut inits, container, path, args, env).await;
Some(Message::ExecRequest { container, path, args, env, stdout, stderr }) => {
// This file descriptor corresponds to either /dev/null or some pty
let stdout_fd = nix::fcntl::open(
&stdout,
nix::fcntl::OFlag::O_RDWR,
nix::sys::stat::Mode::empty(),
).expect("Failed to open pty");
let stderr_fd = nix::fcntl::open(
&stderr,
nix::fcntl::OFlag::O_RDWR,
nix::sys::stat::Mode::empty(),
).expect("Failed to open pty");

let (response, exit) = exec(&mut inits, container, path, args, env, stdout_fd, stderr_fd).await;

exits.push(exit);

Expand Down Expand Up @@ -113,15 +134,17 @@ fn create(inits: &mut Inits, init: Init, console_fd: Option<RawFd>) -> Message {
// TODO: Check if container is already there...

let container = init.container.clone();
let mut socket_pair = socket_pair().expect("Failed to create socket pair");
let mut sockets = socket_pair().expect("Failed to create socket pair");
let mut init_pid_channel = Channel::new();
let mut fd_share_pair = socket_pair().expect("Failed to create socket pair");

let trampoline_pid = fork(|| {
set_log_target("northstar::forker-trampoline".into());
util::set_parent_death_signal(Signal::SIGKILL);

let mut init_pid_tx = init_pid_channel.write_end();
let socket = socket_pair.snd();
let socket = sockets.snd();
let fd_share = fd_share_pair.snd();

// Create pid namespace
debug!("Creating pid namespace");
Expand All @@ -132,7 +155,7 @@ fn create(inits: &mut Inits, init: Init, console_fd: Option<RawFd>) -> Message {
debug!("Forking init of {}", init.container);
let init_pid = fork(|| {
// Dive into init and never return
init.run(socket, console_fd);
init.run(socket, console_fd, fd_share);
})
.expect("Failed to fork init");

Expand Down Expand Up @@ -167,10 +190,18 @@ fn create(inits: &mut Inits, init: Init, console_fd: Option<RawFd>) -> Message {

debug!("Created container {} with pid {}", container, pid);

let socket = socket_pair.fst();
let socket = sockets.fst();
let fd_share = fd_share_pair.fst();
let forker_socket: AsyncMessage = socket.try_into().expect("Failed to create AsyncMessage");

inits.insert(container, (pid, forker_socket));
inits.insert(
container,
InitComm {
pid,
message: forker_socket,
share_fds: fd_share,
},
);

Message::CreateResult { init: pid }
}
Expand All @@ -182,8 +213,10 @@ async fn exec(
path: NonNullString,
args: Vec<NonNullString>,
env: Vec<NonNullString>,
stdout_fd: RawFd,
stderr_fd: RawFd,
) -> (Message, impl Future<Output = (Container, ExitStatus)>) {
if let Some((init_pid, mut init_socket)) = inits.remove(&container) {
if let Some(mut init) = inits.remove(&container) {
debug!(
"Forwarding exec request for container {}: {}",
container,
Expand All @@ -193,31 +226,40 @@ async fn exec(
let path = path.into();
let args = args.iter().cloned().map(Into::into).collect();
let env = env.iter().cloned().map(Into::into).collect();

let message = init::Message::Exec { path, args, env };
init_socket.send(message).await.expect("Failed to send");
match init_socket.recv().await.expect("Failed to receive") {

init.message.send(message).await.expect("Failed to send");

// Sends the stdout and stderr FDs to the init process
share_fds::send_fd(init.share_fds.as_raw_fd(), stdout_fd).expect("Failed to send");
share_fds::send_fd(init.share_fds.as_raw_fd(), stderr_fd).expect("Failed to send");
unistd::close(stdout_fd).expect("Failed to close stdout_fd");
unistd::close(stderr_fd).expect("Failed to close stderr_fd");

match init.message.recv().await.expect("Failed to receive") {
Some(init::Message::Forked { .. }) => (),
_ => panic!("Unexpected init message"),
}

// Construct a future that waits to the init to signal a exit of it's child
// Afterwards reap the init process which should have exitted already
let exit = async move {
match init_socket.recv().await {
match init.message.recv().await {
Ok(Some(init::Message::Exit {
pid: _,
exit_status,
})) => {
// Reap init process
debug!("Reaping init process of {} ({})", container, init_pid);
waitpid(unistd::Pid::from_raw(init_pid as i32), None)
debug!("Reaping init process of {} ({})", container, init.pid);
waitpid(unistd::Pid::from_raw(init.pid as i32), None)
.expect("Failed to reap init process");
(container, exit_status)
}
Ok(None) | Err(_) => {
// Reap init process
debug!("Reaping init process of {} ({})", container, init_pid);
waitpid(unistd::Pid::from_raw(init_pid as i32), None)
debug!("Reaping init process of {} ({})", container, init.pid);
waitpid(unistd::Pid::from_raw(init.pid as i32), None)
.expect("Failed to reap init process");
(container, ExitStatus::Exit(-1))
}
Expand Down
Loading

0 comments on commit 5411d42

Please sign in to comment.