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

Implementation of ps commmand #172

Merged
merged 1 commit into from
Jul 31, 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
40 changes: 38 additions & 2 deletions src/cgroups/common.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use std::{
env,
fmt::{Debug, Display},
fs,
io::Write,
fs::{self, File},
io::{BufRead, BufReader, Write},
path::{Path, PathBuf},
};

Expand Down Expand Up @@ -36,6 +36,8 @@ pub trait CgroupManager {
fn freeze(&self, state: FreezerState) -> Result<()>;
/// Retrieve statistics for the cgroup
fn stats(&self) -> Result<Stats>;
// Gets the PIDs inside the cgroup
fn get_all_pids(&self) -> Result<Vec<Pid>>;
zidoshare marked this conversation as resolved.
Show resolved Hide resolved
}

#[derive(Debug)]
Expand Down Expand Up @@ -177,3 +179,37 @@ pub fn create_cgroup_manager<P: Into<PathBuf>>(
_ => bail!("could not find cgroup filesystem"),
}
}

pub fn get_all_pids(path: &Path) -> Result<Vec<Pid>> {
log::debug!("scan pids in folder: {:?}", path);
let mut result = vec![];
walk_dir(&path, &mut |p| {
let file_path = p.join(CGROUP_PROCS);
if file_path.exists() {
let file = File::open(file_path)?;
for line in BufReader::new(file).lines() {
if let Ok(line) = line {
result.push(Pid::from_raw(line.parse::<i32>()?))
}
}
}
Ok(())
})?;
Ok(result)
}

fn walk_dir<F>(path: &Path, c: &mut F) -> Result<()>
where
F: FnMut(&Path) -> Result<()>,
{
c(&path)?;
for entry in fs::read_dir(path)? {
let entry = entry?;
let path = entry.path();

if path.is_dir() {
walk_dir(&path, c)?;
}
}
Ok(())
}
10 changes: 9 additions & 1 deletion src/cgroups/v1/manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use super::{
perf_event::PerfEvent, pids::Pids, util, Controller,
};

use crate::cgroups::common::CGROUP_PROCS;
use crate::cgroups::common::{self, CGROUP_PROCS};
use crate::cgroups::stats::{Stats, StatsProvider};
use crate::utils;
use crate::{cgroups::common::CgroupManager, utils::PathBufExt};
Expand Down Expand Up @@ -101,6 +101,14 @@ impl Manager {
}

impl CgroupManager for Manager {
fn get_all_pids(&self) -> Result<Vec<Pid>> {
let devices = self.subsystems.get(&CtrlType::Devices);
if let Some(p) = devices {
common::get_all_pids(p)
} else {
bail!("subsystem does not exist")
}
}
fn add_task(&self, pid: Pid) -> Result<()> {
for subsys in &self.subsystems {
match subsys.0 {
Expand Down
4 changes: 4 additions & 0 deletions src/cgroups/v2/manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,4 +159,8 @@ impl CgroupManager for Manager {
fn stats(&self) -> Result<Stats> {
Ok(Stats::default())
}

fn get_all_pids(&self) -> Result<Vec<Pid>> {
common::get_all_pids(&self.full_path)
}
}
4 changes: 4 additions & 0 deletions src/cgroups/v2/systemd_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,10 @@ impl CgroupManager for SystemDCGroupManager {
fn stats(&self) -> Result<Stats> {
Ok(Stats::default())
}

fn get_all_pids(&self) -> Result<Vec<Pid>> {
common::get_all_pids(&self.full_path)
}
}

#[cfg(test)]
Expand Down
1 change: 1 addition & 0 deletions src/commands/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ pub mod info;
pub mod kill;
pub mod list;
pub mod pause;
pub mod ps;
pub mod resume;
pub mod run;
pub mod spec_json;
Expand Down
86 changes: 86 additions & 0 deletions src/commands/ps.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
use crate::{cgroups, container::Container, utils};
use anyhow::{bail, Context, Result};
use clap::{self, Clap};
use std::{path::PathBuf, process::Command};

/// display the processes inside a container
#[derive(Clap, Debug)]
pub struct Ps {
/// format to display processes: table or json (default: "table")
#[clap(short, long, default_value = "table")]
format: String,
pub container_id: String,
/// options will be passed to the ps utility
#[clap(setting = clap::ArgSettings::Last)]
ps_options: Vec<String>,
}
impl Ps {
pub fn exec(&self, root_path: PathBuf) -> Result<()> {
let container_root = root_path.join(&self.container_id);
if !container_root.exists() {
bail!("{} doesn't exist.", self.container_id)
}
let container = Container::load(container_root)?.refresh_status()?;
if container.root.exists() {
let config_absolute_path = container.root.join("config.json");
log::debug!("load spec from {:?}", config_absolute_path);
let spec = oci_spec::Spec::load(config_absolute_path)?;
log::debug!("spec: {:?}", spec);
let cgroups_path = utils::get_cgroup_path(
&spec.linux.context("no linux in spec")?.cgroups_path,
container.id(),
);
let systemd_cgroup = container
.systemd()
.context("could not determine cgroup manager")?;
let cmanager = cgroups::common::create_cgroup_manager(cgroups_path, systemd_cgroup)?;
zidoshare marked this conversation as resolved.
Show resolved Hide resolved
let pids: Vec<i32> = cmanager
.get_all_pids()?
.iter()
.map(|pid| pid.as_raw())
.collect();

if self.format == "json" {
println!("{}", serde_json::to_string(&pids)?);
} else if self.format == "table" {
let default_ps_options = vec![String::from("-ef")];
let ps_options = if self.ps_options.is_empty() {
&default_ps_options
} else {
&self.ps_options
};
let output = Command::new("ps").args(ps_options).output()?;
if !output.status.success() {
println!("{}", std::str::from_utf8(&output.stderr)?);
} else {
let lines = std::str::from_utf8(&output.stdout)?;
let lines: Vec<&str> = lines.split("\n").collect();
let pid_index = get_pid_index(lines[0])?;
println!("{}", &lines[0]);
for line in &lines[1..] {
if line.is_empty() {
continue;
}
let fields: Vec<&str> = line.split_whitespace().collect();
let pid: i32 = fields[pid_index].parse()?;
if pids.contains(&pid) {
println!("{}", line);
}
}
}
}
}
Ok(())
}
}

fn get_pid_index(title: &str) -> Result<usize> {
let titles = title.split_whitespace();

for (index, name) in titles.enumerate() {
if name == "PID" {
return Ok(index);
}
}
bail!("could't find PID field in ps output");
}
4 changes: 4 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ use youki::commands::info;
use youki::commands::kill;
use youki::commands::list;
use youki::commands::pause;
use youki::commands::ps;
use youki::commands::resume;
use youki::commands::run;
use youki::commands::spec_json;
Expand Down Expand Up @@ -74,6 +75,8 @@ enum SubCommand {
Resume(resume::Resume),
#[clap(version = "0.0.1", author = "utam0k <[email protected]>")]
Events(events::Events),
#[clap(version = "0.0.1", author = "utam0k <[email protected]>", setting=clap::AppSettings::AllowLeadingHyphen)]
Ps(ps::Ps),
}

/// This is the entry point in the container runtime. The binary is run by a high-level container runtime,
Expand Down Expand Up @@ -108,5 +111,6 @@ fn main() -> Result<()> {
SubCommand::Pause(pause) => pause.exec(root_path, systemd_cgroup),
SubCommand::Resume(resume) => resume.exec(root_path, systemd_cgroup),
SubCommand::Events(events) => events.exec(root_path),
SubCommand::Ps(ps) => ps.exec(root_path),
}
}