1
0
mirror of https://github.com/containers/youki synced 2024-11-23 01:11:58 +01:00

Merge pull request #79 from YJDoc2/main

Document Container and Command modules
This commit is contained in:
utam0k 2021-06-11 00:03:56 +09:00 committed by GitHub
commit 10912ff178
Signed by: GitHub
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 64 additions and 6 deletions

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

@ -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;
@ -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<()>;

@ -1,3 +1,4 @@
//! Implements Command trait for Linux systems
use std::{any::Any, path::Path};
use anyhow::{bail, Result};
@ -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
// 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)?;
}
@ -61,15 +86,19 @@ 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)
@ -77,6 +106,7 @@ impl Command for LinuxCommand {
Ok(())
}
/// Sets resource limit for process
fn set_rlimit(&self, rlimit: &LinuxRlimit) -> Result<()> {
let rlim = &libc::rlimit {
rlim_cur: rlimit.soft,

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

@ -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,
}
@ -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
if let Ok(proc) = Process::new(pid.as_raw()) {
use procfs::process::ProcState;
match proc.stat.state().unwrap() {

@ -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};
@ -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,
}
@ -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 {