Skip to content

Commit

Permalink
Merge pull request #108 from Furisto/list-cmd
Browse files Browse the repository at this point in the history
Add list command
  • Loading branch information
utam0k authored Jun 24, 2021
2 parents a105e11 + 9d785aa commit 7578bac
Show file tree
Hide file tree
Showing 13 changed files with 248 additions and 38 deletions.
11 changes: 11 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,14 @@ libc = "0.2.84"
log = "0.4"
anyhow = "1.0"
mio = { version = "0.7", features = ["os-ext", "os-poll"] }
chrono = "0.4"
chrono = { version="0.4", features = ["serde"] }
once_cell = "1.6.0"
futures = { version = "0.3", features = ["thread-pool"] }
regex = "1.5"
oci_spec = { version = "0.1.0", path = "./oci_spec" }
systemd = { version = "0.8", default-features = false }
dbus = "0.9.2"
tabwriter = "1"

[dev-dependencies]
oci_spec = { version = "0.1.0", path = "./oci_spec", features = ["proptests"] }
Expand Down
13 changes: 12 additions & 1 deletion src/command/command.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! 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 std::{any::Any, ffi::OsStr, path::Path, sync::Arc};

use anyhow::Result;
use caps::{errors::CapsError, CapSet, CapsHashSet};
Expand All @@ -12,6 +12,8 @@ use nix::{

use oci_spec::LinuxRlimit;

use crate::command::{linux::LinuxCommand, test::TestHelperCommand};

/// This specifies various kernel/other functionalities required for
/// container management
pub trait Command {
Expand All @@ -23,4 +25,13 @@ pub trait Command {
fn set_capability(&self, cset: CapSet, value: &CapsHashSet) -> Result<(), CapsError>;
fn set_hostname(&self, hostname: &str) -> Result<()>;
fn set_rlimit(&self, rlimit: &LinuxRlimit) -> Result<()>;
fn get_pwuid(&self, uid: u32) -> Option<Arc<OsStr>>;
}

pub fn create_command() -> Box<dyn Command> {
if cfg!(test) {
Box::new(TestHelperCommand::default())
} else {
Box::new(LinuxCommand)
}
}
55 changes: 54 additions & 1 deletion src/command/linux.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
//! Implements Command trait for Linux systems
use std::{any::Any, path::Path};
use std::ffi::{CStr, OsStr};
use std::os::unix::ffi::OsStrExt;
use std::sync::Arc;
use std::{any::Any, mem, path::Path, ptr};

use anyhow::{bail, Result};
use caps::{errors::CapsError, CapSet, CapsHashSet};
use libc::{c_char, uid_t};
use nix::{
errno::Errno,
unistd::{fchdir, pivot_root, sethostname},
Expand All @@ -27,6 +31,21 @@ use crate::capabilities;
#[derive(Clone)]
pub struct LinuxCommand;

impl LinuxCommand {
unsafe fn from_raw_buf<'a, T>(p: *const c_char) -> T
where
T: From<&'a OsStr>,
{
T::from(OsStr::from_bytes(CStr::from_ptr(p).to_bytes()))
}

/// Reads data from the `c_passwd` and returns it as a `User`.
unsafe fn passwd_to_user(passwd: libc::passwd) -> Arc<OsStr> {
let name: Arc<OsStr> = Self::from_raw_buf(passwd.pw_name);
name
}
}

impl Command for LinuxCommand {
/// To enable dynamic typing,
/// see https://doc.rust-lang.org/std/any/index.html for more information
Expand Down Expand Up @@ -118,4 +137,38 @@ impl Command for LinuxCommand {
}
Ok(())
}

// taken from https://crates.io/crates/users
fn get_pwuid(&self, uid: uid_t) -> Option<Arc<OsStr>> {
let mut passwd = unsafe { mem::zeroed::<libc::passwd>() };
let mut buf = vec![0; 2048];
let mut result = ptr::null_mut::<libc::passwd>();

loop {
let r = unsafe {
libc::getpwuid_r(uid, &mut passwd, buf.as_mut_ptr(), buf.len(), &mut result)
};

if r != libc::ERANGE {
break;
}

let newsize = buf.len().checked_mul(2)?;
buf.resize(newsize, 0);
}

if result.is_null() {
// There is no such user, or an error has occurred.
// errno gets set if there’s an error.
return None;
}

if result != &mut passwd {
// The result of getpwuid_r should be its input passwd.
return None;
}

let user = unsafe { Self::passwd_to_user(result.read()) };
Some(user)
}
}
2 changes: 1 addition & 1 deletion src/command/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
//! to call syscalls required for container management

#[allow(clippy::module_inception)]
mod command;
pub mod command;
pub mod linux;
pub mod test;

Expand Down
6 changes: 5 additions & 1 deletion src/command/test.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::{any::Any, cell::RefCell};
use std::{any::Any, cell::RefCell, ffi::OsStr, sync::Arc};

use caps::{errors::CapsError, CapSet, CapsHashSet};
use nix::sched::CloneFlags;
Expand Down Expand Up @@ -60,6 +60,10 @@ impl Command for TestHelperCommand {
fn set_rlimit(&self, _rlimit: &LinuxRlimit) -> anyhow::Result<()> {
todo!()
}

fn get_pwuid(&self, _: u32) -> Option<Arc<OsStr>> {
todo!()
}
}

impl TestHelperCommand {
Expand Down
86 changes: 65 additions & 21 deletions src/container/container.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
use std::ffi::OsString;
use std::fs;
use std::path::{Path, PathBuf};

use anyhow::Result;
use chrono::DateTime;
use nix::unistd::Pid;

use chrono::Utc;
use procfs::process::Process;

use crate::command::command::create_command;

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

/// Structure representing the container data
Expand Down Expand Up @@ -39,7 +45,7 @@ impl Container {
pub fn status(&self) -> ContainerStatus {
self.state.status
}
pub fn refresh_status(&self) -> Result<Self> {
pub fn refresh_status(&mut self) -> Result<Self> {
let new_status = match self.pid() {
Some(pid) => {
// Note that Process::new does not spawn a new process
Expand All @@ -60,11 +66,19 @@ impl Container {
}
None => ContainerStatus::Stopped,
};
self.update_status(new_status)
Ok(self.update_status(new_status))
}

pub fn refresh_state(&self) -> Result<Self> {
let state = State::load(&self.root)?;
Ok(Self {
state,
root: self.root.clone(),
})
}

pub fn save(&self) -> Result<()> {
log::debug!("Sava container status: {:?} in {:?}", self, self.root);
log::debug!("Save container status: {:?} in {:?}", self, self.root);
self.state.save(&self.root)
}

Expand All @@ -85,24 +99,50 @@ impl Container {
}

pub fn set_pid(&self, pid: i32) -> Self {
Self::new(
self.state.id.as_str(),
self.state.status,
Some(pid),
self.state.bundle.as_str(),
&self.root,
)
.expect("unexpected error")
}

pub fn update_status(&self, status: ContainerStatus) -> Result<Self> {
Self::new(
self.state.id.as_str(),
status,
self.state.pid,
self.state.bundle.as_str(),
&self.root,
)
let mut new_state = self.state.clone();
new_state.pid = Some(pid);

Self {
state: new_state,
root: self.root.clone(),
}
}

pub fn created(&self) -> Option<DateTime<Utc>> {
self.state.created
}

pub fn set_creator(mut self, uid: u32) -> Self {
self.state.creator = Some(uid);
self
}

pub fn creator(&self) -> Option<OsString> {
if let Some(uid) = self.state.creator {
let command = create_command();
let user_name = command.get_pwuid(uid);
if let Some(user_name) = user_name {
return Some((&*user_name).to_owned());
}
}

None
}

pub fn update_status(&self, status: ContainerStatus) -> Self {
let created = match (status, self.state.created) {
(ContainerStatus::Created, None) => Some(Utc::now()),
_ => self.state.created,
};

let mut new_state = self.state.clone();
new_state.created = created;
new_state.status = status;

Self {
state: new_state,
root: self.root.clone(),
}
}

pub fn load(container_root: PathBuf) -> Result<Self> {
Expand All @@ -112,4 +152,8 @@ impl Container {
root: container_root,
})
}

pub fn bundle(&self) -> String {
self.state.bundle.clone()
}
}
23 changes: 23 additions & 0 deletions src/container/state.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
//! Information about status and state of the container
use std::collections::HashMap;
use std::fmt::Display;
use std::fs;
use std::{fs::File, path::Path};

use anyhow::Result;
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};

const STATE_FILE_PATH: &str = "state.json";
Expand Down Expand Up @@ -40,6 +42,19 @@ impl ContainerStatus {
}
}

impl Display for ContainerStatus {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let print = match *self {
Self::Creating => "Creating",
Self::Created => "Created",
Self::Running => "Running",
Self::Stopped => "Stopped",
};

write!(f, "{}", print)
}
}

/// Stores the state information of the container
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
Expand All @@ -57,6 +72,12 @@ pub struct State {
pub bundle: String,
// Annotations are key values associated with the container.
pub annotations: HashMap<String, String>,
// Creation time of the container
#[serde(skip_serializing_if = "Option::is_none")]
pub created: Option<DateTime<Utc>>,
// User that created the container
#[serde(skip_serializing_if = "Option::is_none")]
pub creator: Option<u32>,
}

impl State {
Expand All @@ -73,6 +94,8 @@ impl State {
pid,
bundle: bundle.to_string(),
annotations: HashMap::default(),
created: None,
creator: None,
}
}

Expand Down
5 changes: 4 additions & 1 deletion src/create.rs
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,10 @@ fn run_container<P: AsRef<Path>>(
// actually run the command / program to be run in container
utils::do_exec(&spec_args[0], spec_args, envs)?;
// the command / program is done executing
container.update_status(ContainerStatus::Stopped)?.save()?;
container
.refresh_state()?
.update_status(ContainerStatus::Stopped)
.save()?;

Ok(Process::Init(init))
}
Expand Down
Loading

0 comments on commit 7578bac

Please sign in to comment.