use std::{
fs::read_link,
path::{Path, PathBuf},
};
use serde::Serialize;
use crate::{
error::{CollectResult, MetricError},
utils,
};
#[derive(Debug, Serialize)]
pub struct Process {
id: usize,
path: PathBuf,
}
impl Process {
fn new(id: usize, path: PathBuf) -> Self {
Self { id, path }
}
pub fn pid(&self) -> usize {
self.id
}
pub fn path(&self) -> PathBuf {
self.path.clone()
}
pub fn comm(&self) -> CollectResult<String> {
match utils::collect_info_string("comm", &self.path) {
Ok(c) => Ok(c.unwrap_or_default().trim().to_string()),
Err(err) => Err(err),
}
}
pub fn wchan(&self) -> CollectResult<String> {
match utils::collect_info_string("wchan", &self.path) {
Ok(c) => {
let mut wchan = String::new();
let wchan_data = c.unwrap_or_default().trim().to_string();
if wchan_data != "0" {
wchan = wchan_data;
}
Ok(wchan)
}
Err(err) => Err(err),
}
}
pub fn cmdline(&self) -> CollectResult<Vec<String>> {
match utils::collect_info_string("cmdline", &self.path) {
Ok(c) => {
let proc_cmdline = c
.unwrap_or_default()
.trim_end_matches("\x00")
.split("\x00")
.map(|v| v.to_string())
.collect::<Vec<String>>();
Ok(proc_cmdline)
}
Err(err) => Err(err),
}
}
pub fn environ(&self) -> CollectResult<Vec<String>> {
match utils::collect_info_string("environ", &self.path) {
Ok(c) => {
let proc_cmdline = c
.unwrap_or_default()
.trim_end_matches("\x00")
.split("\x00")
.map(|v| v.to_string())
.collect::<Vec<String>>();
Ok(proc_cmdline)
}
Err(err) => Err(err),
}
}
pub fn executable(&self) -> CollectResult<PathBuf> {
let mut proc_path = self.path.clone();
proc_path.push("exe");
match read_link(&proc_path) {
Ok(c) => {
if c.exists() {
return Ok(c);
}
Err(MetricError::PathNotFound(c))
}
Err(err) => Err(MetricError::IOError(proc_path, err)),
}
}
pub fn cwd(&self) -> CollectResult<PathBuf> {
let mut proc_path = self.path.clone();
proc_path.push("cwd");
match read_link(&proc_path) {
Ok(c) => {
if c.exists() {
return Ok(c);
}
Err(MetricError::PathNotFound(c))
}
Err(err) => Err(MetricError::IOError(proc_path, err)),
}
}
pub fn root_dir(&self) -> CollectResult<PathBuf> {
let mut proc_path = self.path.clone();
proc_path.push("root");
match read_link(&proc_path) {
Ok(c) => {
if c.exists() {
return Ok(c);
}
Err(MetricError::PathNotFound(c))
}
Err(err) => Err(MetricError::IOError(proc_path, err)),
}
}
}
pub fn collect_all() -> CollectResult<Vec<Process>> {
let proc_path = Path::new("/proc");
collect_all_from(proc_path)
}
pub fn collect(pid: usize) -> CollectResult<Process> {
let proc_path = Path::new("/proc");
collect_from(proc_path, pid)
}
fn collect_all_from(base_path: &Path) -> CollectResult<Vec<Process>> {
let mut sysprocs = Vec::new();
for file_info in utils::list_dir_content(base_path, "", "proc") {
if let Ok(pid) = file_info.parse::<usize>() {
let mut proc_dir_path = PathBuf::from(base_path);
proc_dir_path.push(format!("{}", pid));
if proc_dir_path.as_path().is_dir() {
sysprocs.push(Process::new(pid, proc_dir_path));
}
}
}
Ok(sysprocs)
}
pub fn collect_from(base_path: &Path, pid: usize) -> CollectResult<Process> {
let mut proc_dir_path = PathBuf::from(base_path);
proc_dir_path.push(format!("{}", pid));
if proc_dir_path.as_path().is_dir() {
return Ok(Process::new(pid, proc_dir_path));
}
Err(MetricError::ProcessNotFound(pid))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn proc_collect_all() {
let sysprocs =
collect_all_from(Path::new("test_data/fixtures/proc")).expect("running procs");
assert_eq!(sysprocs.len(), 7);
}
#[test]
fn proc_collect() {
let proc_path = Path::new("test_data/fixtures/proc");
let sys_single_proc = collect_from(proc_path, 2);
assert_eq!(sys_single_proc.is_err(), true);
let sys_single_proc = collect_from(proc_path, 26231).expect("running proc 26231");
assert_eq!(sys_single_proc.cwd().unwrap(), PathBuf::from("/usr/bin/"));
assert_eq!(sys_single_proc.comm().unwrap(), "vim");
assert_eq!(sys_single_proc.wchan().unwrap(), "poll_schedule_timeout");
assert_eq!(
sys_single_proc.executable().unwrap(),
PathBuf::from("/usr/bin/ls"),
);
assert_eq!(sys_single_proc.root_dir().unwrap(), PathBuf::from("/"));
assert_eq!(
sys_single_proc.cmdline().unwrap(),
["vim", "test.go", "+10"],
);
assert_eq!(
sys_single_proc.environ().unwrap(),
["PATH=/go/bin:/usr/local/go/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", "HOSTNAME=cd24e11f73a5", "TERM=xterm", "GOLANG_VERSION=1.12.5", "GOPATH=/go", "HOME=/root"],
);
let sys_single_proc = collect_from(proc_path, 26232).expect("running proc 26232");
assert_eq!(sys_single_proc.cwd().is_err(), true);
assert_eq!(sys_single_proc.root_dir().is_err(), true);
}
}