Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Document Container and Command modules #79

Merged
merged 4 commits into from
Jun 10, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions docs/doc-draft.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,5 +76,19 @@ The main youki process sets up the pipe and forks the child process and waits on
- [user-namespace man page](https://man7.org/linux/man-pages/man7/user_namespaces.7.html)
- [wait man page](https://man7.org/linux/man-pages/man3/wait.3p.html)

### Container

This contains structure represent and functions related to container process and its state and status.

### Command

This contains a trait to wrap commonly required syscalls, so that they can be abstracted from implementation details for rest of Youki.
This also provides implementation for Linux syscalls for the trait.

- [pivot_root man page](https://man7.org/linux/man-pages/man2/pivot_root.2.html)
- [umount2 man page](https://man7.org/linux/man-pages/man2/umount2.2.html)
- [capabilities man page](https://man7.org/linux/man-pages/man7/capabilities.7.html)
- [unshare man page](https://man7.org/linux/man-pages/man2/unshare.2.html)

[oci runtime specification]: https://github.com/opencontainers/runtime-spec/blob/master/runtime.md
[runc man pages]: (https://github.com/opencontainers/runc/blob/master/man/runc.8.md)
5 changes: 5 additions & 0 deletions src/command/command.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
//! An interface trait so that rest of Youki can call
//! necessary functions without having to worry about their
//! implementation details
use std::{any::Any, path::Path};

use anyhow::Result;
Expand All @@ -9,6 +12,8 @@ use nix::{

use oci_spec::LinuxRlimit;

/// This specifies various kernel/other functionalities required for
/// container management
pub trait Command {
fn as_any(&self) -> &dyn Any;
fn pivot_rootfs(&self, path: &Path) -> Result<()>;
Expand Down
30 changes: 30 additions & 0 deletions src/command/linux.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
//! Implements Command trait for Linux systems
use std::{any::Any, path::Path};

use anyhow::{bail, Result};
Expand All @@ -22,36 +23,60 @@ use oci_spec::LinuxRlimit;
use super::Command;
use crate::capabilities;

/// Empty structure to implement Command trait for
#[derive(Clone)]
pub struct LinuxCommand;

impl Command for LinuxCommand {
/// To enable dynamic typing,
/// see https://doc.rust-lang.org/std/any/index.html for more information
fn as_any(&self) -> &dyn Any {
self
}

/// Function to set given path as root path inside process
fn pivot_rootfs(&self, path: &Path) -> Result<()> {
// open the path as directory and read only
let newroot = open(path, OFlag::O_DIRECTORY | OFlag::O_RDONLY, Mode::empty())?;

// make the given path as the root directory for the container
// see https://man7.org/linux/man-pages/man2/pivot_root.2.html, specially the notes
// pivot root usually changes the root directory to first argument, and then mounts the original root
// directory at second argument. Giving same path for both stacks mapping of the original root directory
// above the new directory at the same path, then the call to umount unmounts the original root directory from
// this path. This is done, as otherwise, we will need to create a separate temporary directory under the new root path
// so we can move the original root there, and then unmount that. This way saves the creation of the temporary
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

// directory to put original root directory.
pivot_root(path, path)?;

// Unmount the original root directory which was stacked on top of new root directory
// MNT_DETACH makes the mount point unavailable to new accesses, but waits till the original mount point
// to be free of activity to actually unmount
// see https://man7.org/linux/man-pages/man2/umount2.2.html for more information
umount2("/", MntFlags::MNT_DETACH)?;
// Change directory to root
fchdir(newroot)?;
Ok(())
}

/// Set namespace for process
fn set_ns(&self, rawfd: i32, nstype: CloneFlags) -> Result<()> {
nix::sched::setns(rawfd, nstype)?;
Ok(())
}

/// set uid and gid for process
fn set_id(&self, uid: Uid, gid: Gid) -> Result<()> {
if let Err(e) = prctl::set_keep_capabilities(true) {
bail!("set keep capabilities returned {}", e);
};
// args : real *id, effective *id, saved set *id respectively
unistd::setresgid(gid, gid, gid)?;
unistd::setresuid(uid, uid, uid)?;

// if not the root user, reset capabilities to effective capabilities,
// which are used by kernel to perform checks
// see https://man7.org/linux/man-pages/man7/capabilities.7.html for more information
if uid != Uid::from_raw(0) {
capabilities::reset_effective(self)?;
}
Expand All @@ -61,22 +86,27 @@ impl Command for LinuxCommand {
Ok(())
}

/// Disassociate parts of execution context
// see https://man7.org/linux/man-pages/man2/unshare.2.html for more information
fn unshare(&self, flags: CloneFlags) -> Result<()> {
unshare(flags)?;
Ok(())
}

/// Set capabilities for container process
fn set_capability(&self, cset: CapSet, value: &CapsHashSet) -> Result<(), CapsError> {
caps::set(None, cset, value)
}

/// Sets hostname for process
fn set_hostname(&self, hostname: &str) -> Result<()> {
if let Err(e) = sethostname(hostname) {
bail!("Failed to set {} as hostname. {:?}", hostname, e)
}
Ok(())
}

/// Sets resource limit for process
fn set_rlimit(&self, rlimit: &LinuxRlimit) -> Result<()> {
let rlim = &libc::rlimit {
rlim_cur: rlimit.soft,
Expand Down
2 changes: 2 additions & 0 deletions src/command/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
//! Contains a wrapper of syscalls for unit tests
//! This provides a uniform interface for rest of Youki
//! to call syscalls required for container management

#[allow(clippy::module_inception)]
mod command;
Expand Down
7 changes: 6 additions & 1 deletion src/container/container.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,12 @@ use procfs::process::Process;

use crate::container::{ContainerStatus, State};

/// Structure representing the container data
#[derive(Debug)]
pub struct Container {
// State of the container
pub state: State,
// indicated the directory for the root path in the container
pub root: PathBuf,
}

Expand All @@ -36,10 +39,12 @@ impl Container {
pub fn status(&self) -> ContainerStatus {
self.state.status
}

pub fn refresh_status(&self) -> Result<Self> {
let new_status = match self.pid() {
Some(pid) => {
// Note that Process::new does not spawn a new process
// but instead creates a new Process structure, and fill
// it with information about the process with given pid
Comment on lines +45 to +47
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a good comment. I, the author, didn't think anything of it, but it certainly could be misunderstood.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, the first time I was looking at this while commenting create.rs, I was wondering why we create a new process here, then I checked the docs of process and found out it fills new structure. So I kept that in mind when writing this :)

if let Ok(proc) = Process::new(pid.as_raw()) {
use procfs::process::ProcState;
match proc.stat.state().unwrap() {
Expand Down
12 changes: 7 additions & 5 deletions src/container/state.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
//! Information about status and state of the container
use std::collections::HashMap;
use std::fs;
use std::{fs::File, path::Path};
Expand All @@ -7,17 +8,17 @@ use serde::{Deserialize, Serialize};

const STATE_FILE_PATH: &str = "state.json";

/// Indicates status of the container
#[derive(Serialize, Deserialize, Debug, Copy, Clone)]
#[serde(rename_all = "camelCase")]
pub enum ContainerStatus {
// StateCreating indicates that the container is being created
// The container is being created
Creating,
// StateCreated indicates that the runtime has finished the create operation
// The runtime has finished the create operation
Created,
// StateRunning indicates that the container process has executed the
// user-specified program but has not exited
// The container process has executed the user-specified program but has not exited
Running,
// StateStopped indicates that the container process has exited
// The container process has exited
Stopped,
}

Expand All @@ -39,6 +40,7 @@ impl ContainerStatus {
}
}

/// Stores the state information of the container
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
pub struct State {
Expand Down