From 230a229052c4619727a597738a82839cafb9ac61 Mon Sep 17 00:00:00 2001 From: Chen Yiyang Date: Thu, 28 Jul 2022 15:51:59 +0800 Subject: [PATCH] runk: add ps sub-command ps command supprot two formats, `json` and `table`. `json` format just outputs pids in the container. `table` format will use `ps` utilty in the host, search and output all processes in the container. Add a struct `container` to represent a spawned container. Move the `kill` implemention from kill.rs as a method of `container`. Fixes: #4361 Signed-off-by: Chen Yiyang --- src/tools/runk/libcontainer/src/builder.rs | 16 ++--- src/tools/runk/libcontainer/src/container.rs | 50 +++++++++++++++- src/tools/runk/src/commands/kill.rs | 32 ++-------- src/tools/runk/src/commands/mod.rs | 1 + src/tools/runk/src/commands/ps.rs | 63 ++++++++++++++++++++ src/tools/runk/src/main.rs | 1 + 6 files changed, 122 insertions(+), 41 deletions(-) create mode 100644 src/tools/runk/src/commands/ps.rs diff --git a/src/tools/runk/libcontainer/src/builder.rs b/src/tools/runk/libcontainer/src/builder.rs index 70bd2b3374..afb02b3565 100644 --- a/src/tools/runk/libcontainer/src/builder.rs +++ b/src/tools/runk/libcontainer/src/builder.rs @@ -503,11 +503,7 @@ mod tests { let root = tempdir().unwrap(); // let bundle = temp let id = "test".to_string(); - create_activated_dirs( - &root.path().to_path_buf(), - &id, - &bundle_dir.path().to_path_buf(), - ); + create_activated_dirs(root.path(), &id, bundle_dir.path()); let pid = getpid().as_raw(); let mut spec = create_dummy_spec(); @@ -517,7 +513,7 @@ mod tests { .to_string_lossy() .to_string(); - let status = create_dummy_status(&id, pid, &root.path().to_path_buf(), &spec); + let status = create_dummy_status(&id, pid, root.path(), &spec); status.save().unwrap(); let result = ActivatedContainerBuilder::default() @@ -600,13 +596,9 @@ mod tests { .join(TEST_ROOTFS_PATH) .to_string_lossy() .to_string(); - create_activated_dirs( - &root.path().to_path_buf(), - &id, - &bundle_dir.path().to_path_buf(), - ); + create_activated_dirs(root.path(), &id, bundle_dir.path()); - let status = create_dummy_status(&id, pid, &root.path().to_path_buf(), &spec); + let status = create_dummy_status(&id, pid, root.path(), &spec); status.save().unwrap(); let launcher = ActivatedContainerBuilder::default() .id(id) diff --git a/src/tools/runk/libcontainer/src/container.rs b/src/tools/runk/libcontainer/src/container.rs index ad6db7c817..abc40fbba3 100644 --- a/src/tools/runk/libcontainer/src/container.rs +++ b/src/tools/runk/libcontainer/src/container.rs @@ -3,9 +3,14 @@ // SPDX-License-Identifier: Apache-2.0 // -use crate::status::Status; +use crate::status::{self, get_all_pid, get_current_container_state, Status}; use anyhow::{anyhow, Result}; -use nix::unistd::{chdir, unlink}; +use nix::sys::signal::kill; +use nix::{ + sys::signal::Signal, + unistd::{chdir, unlink, Pid}, +}; +use oci::ContainerState; use rustjail::{ container::{BaseContainer, LinuxContainer, EXEC_FIFO_FILENAME}, process::{Process, ProcessOperations}, @@ -26,6 +31,47 @@ pub enum ContainerAction { Run, } +#[derive(Debug)] +pub struct Container { + pub status: Status, + pub state: ContainerState, +} + +impl Container { + pub fn load(state_root: &Path, id: &str) -> Result { + let status = Status::load(state_root, id)?; + let state = get_current_container_state(&status)?; + Ok(Self { status, state }) + } + + pub fn processes(&self) -> Result> { + get_all_pid(&self.status.cgroup_manager) + } + + pub fn kill(&self, signal: Signal, all: bool) -> Result<()> { + if all { + let pids = self.processes()?; + for pid in pids { + if !status::is_process_running(pid)? { + continue; + } + kill(pid, signal)?; + } + } else { + if self.state == ContainerState::Stopped { + return Err(anyhow!("container {} not running", self.status.id)); + } + let pid = Pid::from_raw(self.status.pid); + if status::is_process_running(pid)? { + kill(pid, signal)?; + } + } + Ok(()) + } + + // TODO: add pause and resume +} + /// Used to run a process. If init is set, it will create a container and run the process in it. /// If init is not set, it will run the process in an existing container. #[derive(Debug)] diff --git a/src/tools/runk/src/commands/kill.rs b/src/tools/runk/src/commands/kill.rs index 333f6b187a..0ae0d343dd 100644 --- a/src/tools/runk/src/commands/kill.rs +++ b/src/tools/runk/src/commands/kill.rs @@ -4,44 +4,22 @@ // use crate::Kill; -use anyhow::{anyhow, Result}; -use libcontainer::status::{self, get_current_container_state, Status}; -use nix::{ - sys::signal::{kill, Signal}, - unistd::Pid, -}; -use oci::ContainerState; +use anyhow::Result; +use libcontainer::container::Container; +use nix::sys::signal::Signal; use slog::{info, Logger}; use std::{convert::TryFrom, path::Path, str::FromStr}; pub fn run(opts: Kill, state_root: &Path, logger: &Logger) -> Result<()> { let container_id = &opts.container_id; - let status = Status::load(state_root, container_id)?; - let current_state = get_current_container_state(&status)?; + let container = Container::load(state_root, container_id)?; let sig = parse_signal(&opts.signal)?; // TODO: liboci-cli does not support --all option for kill command. // After liboci-cli supports the option, we will change the following code. // as a workaround we use a custom Kill command. let all = opts.all; - if all { - let pids = status::get_all_pid(&status.cgroup_manager)?; - for pid in pids { - if !status::is_process_running(pid)? { - continue; - } - kill(pid, sig)?; - } - } else { - if current_state == ContainerState::Stopped { - return Err(anyhow!("container {} not running", container_id)); - } - - let p = Pid::from_raw(status.pid); - if status::is_process_running(p)? { - kill(p, sig)?; - } - } + container.kill(sig, all)?; info!(&logger, "kill command finished successfully"); diff --git a/src/tools/runk/src/commands/mod.rs b/src/tools/runk/src/commands/mod.rs index e1e0810790..4243035214 100644 --- a/src/tools/runk/src/commands/mod.rs +++ b/src/tools/runk/src/commands/mod.rs @@ -8,6 +8,7 @@ pub mod delete; pub mod exec; pub mod kill; pub mod list; +pub mod ps; pub mod run; pub mod spec; pub mod start; diff --git a/src/tools/runk/src/commands/ps.rs b/src/tools/runk/src/commands/ps.rs new file mode 100644 index 0000000000..84bbc5ef2d --- /dev/null +++ b/src/tools/runk/src/commands/ps.rs @@ -0,0 +1,63 @@ +// Copyright 2021-2022 Kata Contributors +// +// SPDX-License-Identifier: Apache-2.0 +// + +use anyhow::anyhow; +use anyhow::Result; +use libcontainer::container::Container; +use liboci_cli::Ps; +use slog::{info, Logger}; +use std::path::Path; +use std::process::Command; +use std::str; + +pub fn run(opts: Ps, root: &Path, logger: &Logger) -> Result<()> { + let container = Container::load(root, opts.container_id.as_str())?; + let pids = container + .processes()? + .iter() + .map(|pid| pid.as_raw()) + .collect::>(); + + match opts.format.as_str() { + "json" => println!("{}", serde_json::to_string(&pids)?), + "table" => { + let ps_options = if opts.ps_options.is_empty() { + vec!["-ef".to_string()] + } else { + opts.ps_options + }; + let output = Command::new("ps").args(ps_options).output()?; + if !output.status.success() { + return Err(anyhow!("{}", std::str::from_utf8(&output.stderr)?)); + } + let lines = str::from_utf8(&output.stdout)?.lines().collect::>(); + if lines.is_empty() { + return Err(anyhow!("no processes found")); + } + let pid_index = lines[0] + .split_whitespace() + .position(|field| field == "PID") + .ok_or_else(|| anyhow!("could't find PID field in ps output"))?; + println!("{}", lines[0]); + for &line in &lines[1..] { + if line.is_empty() { + continue; + } + let fields = line.split_whitespace().collect::>(); + if pid_index >= fields.len() { + continue; + } + let pid: i32 = fields[pid_index].parse()?; + if pids.contains(&pid) { + println!("{}", line); + } + } + } + _ => return Err(anyhow!("unknown format: {}", opts.format)), + } + + info!(&logger, "ps command finished successfully"); + Ok(()) +} diff --git a/src/tools/runk/src/main.rs b/src/tools/runk/src/main.rs index 6e5b976999..4565e6a369 100644 --- a/src/tools/runk/src/main.rs +++ b/src/tools/runk/src/main.rs @@ -80,6 +80,7 @@ async fn cmd_run(subcmd: SubCommand, root_path: &Path, logger: &Logger) -> Resul CommonCmd::Spec(spec) => commands::spec::run(spec, logger), CommonCmd::List(list) => commands::list::run(list, root_path, logger), CommonCmd::Exec(exec) => commands::exec::run(exec, root_path, logger).await, + CommonCmd::Ps(ps) => commands::ps::run(ps, root_path, logger), _ => { return Err(anyhow!("command is not implemented yet")); }