Skip to content

Instantly share code, notes, and snippets.

@sighingnow
Created August 29, 2021 08:38
Show Gist options
  • Save sighingnow/4988a0100bc5030d301926f79254133a to your computer and use it in GitHub Desktop.
Save sighingnow/4988a0100bc5030d301926f79254133a to your computer and use it in GitHub Desktop.
Rust version of "Containers From Scratch" by Liz Rice, https://www.youtube.com/watch?v=8fi7uSYlOdc
use std::env;
use nix::sched;
use nix::sched::CloneFlags;
use nix::sys::signal::Signal;
use nix::sys::wait::waitpid;
use nix::unistd;
use cgroups_rs as cgroup;
use cgroups_rs::{Cgroup};
use cgroups_rs::cgroup_builder::{CgroupBuilder};
fn run(args: &[String]) {
println!("Running run {:?} as {}", args, unistd::getpid());
let clone_flags = CloneFlags::CLONE_NEWUTS | CloneFlags::CLONE_NEWPID | CloneFlags::CLONE_NEWNS;
const STACK_SIZE: usize = 1024 * 1024;
let stack: &mut [u8; STACK_SIZE] = &mut [0; STACK_SIZE];
let proc = sched::clone(
Box::new(|| {
child(args);
return 0;
}),
stack,
clone_flags,
Some(Signal::SIGCHLD as i32),
)
.expect("Failed to create container process");
waitpid(proc, None).unwrap();
println!("Finishes run ...");
}
fn attach_to_cgroup() {
let hierarchy = cgroup::hierarchies::auto();
let cg: Cgroup = CgroupBuilder::new("example")
.pid()
.maximum_number_of_processes(cgroup::MaxValue::Value(100))
.done()
.build(hierarchy);
// add current task to this cgroup
cg.add_task(cgroup::CgroupPid::from(unistd::getpid().as_raw() as u64)).unwrap();
}
fn child(args: &[String]) {
println!("Running child {:?} as {}", args, unistd::getpid());
let unshare_flags = CloneFlags::CLONE_NEWNS;
sched::unshare(unshare_flags).unwrap();
unistd::sethostname("container").unwrap();
attach_to_cgroup();
let exitcode = std::process::Command::new(args[0].as_str())
.args(args.get(1..).unwrap_or_default())
.spawn()
.expect("Failed to execute container command")
.wait()
.unwrap();
match exitcode.code() {
Some(code) => {
if code != 0 {
panic!("Exit with code {}", code);
}
}
None => ()
}
println!("Finishes child ...");
}
fn main() {
let args: Vec<String> = env::args().collect();
if args.len() < 2 {
panic!("Require command to run")
}
match args[1].as_str() {
"run" => run(args.get(2..).unwrap_or_default()),
"child" => child(args.get(2..).unwrap_or_default()),
cmd => {
panic!("Unsupported command: {}", cmd)
}
}
}
@sighingnow
Copy link
Author

Depends on

[dependencies]
cgroups-rs = "0.2.6"
nix = "0.22.1"

@sighingnow
Copy link
Author

The original Go version can be found at https://github.com/lizrice/containers-from-scratch

@sighingnow
Copy link
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment