Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F2707987
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
33 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/src/cli/import.rs b/src/cli/import.rs
index 3f8822a..928a98d 100644
--- a/src/cli/import.rs
+++ b/src/cli/import.rs
@@ -1,82 +1,150 @@
+use super::Item;
use colored::Colorize;
use macros_rs::{crashln, string};
-use serde::Deserialize;
-use std::{collections::HashMap, fs};
+use serde::{Deserialize, Serialize};
+
+use std::{
+ collections::HashMap,
+ fs::{self, OpenOptions},
+ io::prelude::*,
+};
use pmc::{
+ file::Exists,
helpers,
process::{Env, Runner},
};
#[derive(Deserialize, Debug)]
struct ProcessWrapper {
#[serde(alias = "process")]
list: HashMap<String, Process>,
}
-#[derive(Deserialize, Debug)]
+#[derive(Serialize, Deserialize, Debug)]
struct Process {
script: String,
server: Option<String>,
watch: Option<Watch>,
#[serde(default)]
env: Env,
}
-#[derive(Deserialize, Debug)]
+#[derive(Serialize, Deserialize, Debug)]
struct Watch {
path: String,
}
impl Process {
fn get_watch_path(&self) -> Option<String> { self.watch.as_ref().and_then(|w| Some(w.path.clone())) }
}
pub fn read_hcl(path: &String) {
let mut servers: Vec<String> = vec![];
println!("{} Applying action importProcess", *helpers::SUCCESS);
let contents = match fs::read_to_string(path) {
Ok(contents) => contents,
Err(err) => crashln!("{} Cannot read file to import.\n{}", *helpers::FAIL, string!(err).white()),
};
let hcl_parsed: ProcessWrapper = match hcl::from_str(&contents) {
Ok(hcl) => hcl,
Err(err) => crashln!("{} Cannot parse imported file.\n{}", *helpers::FAIL, string!(err).white()),
};
for (name, item) in hcl_parsed.list {
let mut runner = Runner::new();
let server_name = &item.server.clone().unwrap_or("local".into());
let (kind, list_name) = super::format(server_name);
runner = super::Internal {
id: 0,
server_name,
kind: kind.clone(),
runner: runner.clone(),
}
.create(&item.script, &Some(name.clone()), &item.get_watch_path(), true);
println!("{} Imported {kind}process {name}", *helpers::SUCCESS);
match runner.find(&name, server_name) {
Some(id) => {
let mut p = runner.get(id);
p.stop();
p.set_env(item.env);
p.restart();
}
None => crashln!("{} Failed to write to ({name})", *helpers::FAIL),
}
if !servers.contains(&list_name) {
servers.push(list_name);
}
}
servers.iter().for_each(|server| super::Internal::list(&string!("default"), &server));
println!("{} Applied startProcess to imported items", *helpers::SUCCESS);
}
+
+pub fn export_hcl(item: &Item, path: &Option<String>) {
+ println!("{} Applying action exportProcess", *helpers::SUCCESS);
+
+ let runner = Runner::new();
+
+ let fetch_process = |id: usize| {
+ let process = runner.try_info(id);
+ let mut watch_parsed = None;
+ let mut env_parsed = HashMap::new();
+
+ let current_env: HashMap<String, String> = std::env::vars().collect();
+ let path = path.clone().unwrap_or(format!("{}.hcl", process.name.clone()));
+
+ if process.watch.enabled {
+ watch_parsed = Some(Watch { path: process.watch.path.clone() })
+ }
+
+ for (key, value) in process.env.clone() {
+ if let Some(current_value) = current_env.get(&key) {
+ if current_value != &value {
+ env_parsed.insert(key, value);
+ }
+ } else {
+ env_parsed.insert(key, value);
+ }
+ }
+
+ let data = hcl::block! {
+ process (process.name.clone()) {
+ script = (process.script.clone())
+ server = ("")
+ watch = (watch_parsed)
+ env = (env_parsed)
+ }
+ };
+
+ let serialized = hcl::to_string(&data).unwrap();
+
+ if Exists::check(&path).file() {
+ let mut file = OpenOptions::new().write(true).append(true).open(path.clone()).unwrap();
+ if let Err(err) = writeln!(file, "{}", serialized) {
+ crashln!("{} Error writing to file.\n{}", *helpers::FAIL, string!(err).white())
+ }
+ } else {
+ if let Err(err) = fs::write(path.clone(), serialized) {
+ crashln!("{} Error writing file.\n{}", *helpers::FAIL, string!(err).white())
+ }
+ }
+
+ println!("{} Exported process {id} to {path}", *helpers::SUCCESS);
+ };
+
+ match item {
+ Item::Id(id) => fetch_process(*id),
+ Item::Name(name) => match runner.find(&name, &string!("internal")) {
+ Some(id) => fetch_process(id),
+ None => crashln!("{} Process ({name}) not found", *helpers::FAIL),
+ },
+ }
+}
diff --git a/src/main.rs b/src/main.rs
index 134d280..5eb3aed 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,256 +1,266 @@
mod cli;
mod daemon;
mod globals;
mod webui;
use clap::{Parser, Subcommand};
use clap_verbosity_flag::{LogLevel, Verbosity};
use macros_rs::{str, string, then};
use update_informer::{registry, Check};
use crate::{
cli::{internal::Internal, Args, Item},
globals::defaults,
};
#[derive(Copy, Clone, Debug, Default)]
struct NoneLevel;
impl LogLevel for NoneLevel {
fn default() -> Option<log::Level> { None }
}
#[derive(Parser)]
#[command(version = str!(cli::get_version(false)))]
struct Cli {
#[command(subcommand)]
command: Commands,
#[clap(flatten)]
verbose: Verbosity<NoneLevel>,
}
#[derive(Subcommand)]
enum Daemon {
/// Reset process index
#[command(visible_alias = "reset_position")]
Reset,
/// Stop daemon
#[command(visible_alias = "kill")]
Stop,
/// Restart daemon
#[command(visible_alias = "restart", visible_alias = "start")]
Restore {
/// Daemon api
#[arg(long)]
api: bool,
/// WebUI using api
#[arg(long)]
webui: bool,
},
/// Check daemon health
#[command(visible_alias = "info", visible_alias = "status")]
Health {
/// Format output
#[arg(long, default_value_t = string!("default"))]
format: String,
},
}
#[derive(Subcommand)]
enum Server {
/// Add new server
#[command(visible_alias = "add")]
New,
/// List servers
#[command(visible_alias = "ls")]
List {
/// Format output
#[arg(long, default_value_t = string!("default"))]
format: String,
},
/// Remove server
#[command(visible_alias = "rm", visible_alias = "delete")]
Remove {
/// Server name
name: String,
},
/// Set default server
#[command(visible_alias = "set")]
Default {
/// Server name
name: Option<String>,
},
}
// add pmc restore command
#[derive(Subcommand)]
enum Commands {
/// Import process from environment file
#[command(visible_alias = "add")]
Import {
/// Path of file to import
path: String,
},
+ /// Export environment file from process
+ #[command(visible_alias = "get")]
+ Export {
+ #[clap(value_parser = cli::validate::<Item>)]
+ item: Item,
+ /// Path to export file
+ path: Option<String>,
+ },
/// Start/Restart a process
#[command(visible_alias = "restart")]
Start {
/// Process name
#[arg(long)]
name: Option<String>,
#[clap(value_parser = cli::validate::<Args>)]
args: Args,
/// Watch to reload path
#[arg(long)]
watch: Option<String>,
/// Server
#[arg(short, long)]
server: Option<String>,
/// Reset environment values
#[arg(short, long)]
reset_env: bool,
},
/// Stop/Kill a process
#[command(visible_alias = "kill")]
Stop {
#[clap(value_parser = cli::validate::<Item>)]
item: Item,
/// Server
#[arg(short, long)]
server: Option<String>,
},
/// Stop then remove a process
#[command(visible_alias = "rm", visible_alias = "delete")]
Remove {
#[clap(value_parser = cli::validate::<Item>)]
item: Item,
/// Server
#[arg(short, long)]
server: Option<String>,
},
/// Get env of a process
#[command(visible_alias = "cmdline")]
Env {
#[clap(value_parser = cli::validate::<Item>)]
item: Item,
/// Server
#[arg(short, long)]
server: Option<String>,
},
/// Get information of a process
#[command(visible_alias = "info")]
Details {
#[clap(value_parser = cli::validate::<Item>)]
item: Item,
/// Format output
#[arg(long, default_value_t = string!("default"))]
format: String,
/// Server
#[arg(short, long)]
server: Option<String>,
},
/// List all processes
#[command(visible_alias = "ls")]
List {
/// Format output
#[arg(long, default_value_t = string!("default"))]
format: String,
/// Server
#[arg(short, long)]
server: Option<String>,
},
/// Restore all processes
#[command(visible_alias = "resurrect")]
Restore {
/// Server
#[arg(short, long)]
server: Option<String>,
},
/// Save all processes to dumpfile
#[command(visible_alias = "store")]
Save {
/// Server
#[arg(short, long)]
server: Option<String>,
},
/// Get logs from a process
Logs {
#[clap(value_parser = cli::validate::<Item>)]
item: Item,
#[arg(long, default_value_t = 15, help = "")]
lines: usize,
/// Server
#[arg(short, long)]
server: Option<String>,
},
/// Flush a process log
#[command(visible_alias = "clean", visible_alias = "log_rotate")]
Flush {
#[clap(value_parser = cli::validate::<Item>)]
item: Item,
/// Server
#[arg(short, long)]
server: Option<String>,
},
/// Daemon management
#[command(visible_alias = "agent", visible_alias = "bgd")]
Daemon {
#[command(subcommand)]
command: Daemon,
},
/// Server management
#[command(visible_alias = "remote", visible_alias = "srv")]
Server {
#[command(subcommand)]
command: Server,
},
}
fn main() {
let cli = Cli::parse();
let mut env = env_logger::Builder::new();
let level = cli.verbose.log_level_filter();
let informer = update_informer::new(registry::Crates, "pmc", env!("CARGO_PKG_VERSION"));
if let Some(version) = informer.check_version().ok().flatten() {
println!("{} New version is available: {version}", *pmc::helpers::WARN);
}
globals::init();
env.filter_level(level).init();
match &cli.command {
Commands::Import { path } => cli::import::read_hcl(path),
+ Commands::Export { item, path } => cli::import::export_hcl(item, path),
Commands::Start { name, args, watch, server, reset_env } => cli::start(name, args, watch, reset_env, &defaults(server)),
Commands::Stop { item, server } => cli::stop(item, &defaults(server)),
Commands::Remove { item, server } => cli::remove(item, &defaults(server)),
Commands::Restore { server } => Internal::restore(&defaults(server)),
Commands::Save { server } => Internal::save(&defaults(server)),
Commands::Env { item, server } => cli::env(item, &defaults(server)),
Commands::Details { item, format, server } => cli::info(item, format, &defaults(server)),
Commands::List { format, server } => Internal::list(format, &defaults(server)),
Commands::Logs { item, lines, server } => cli::logs(item, lines, &defaults(server)),
Commands::Flush { item, server } => cli::flush(item, &defaults(server)),
Commands::Daemon { command } => match command {
Daemon::Stop => daemon::stop(),
Daemon::Reset => daemon::reset(),
Daemon::Health { format } => daemon::health(format),
Daemon::Restore { api, webui } => daemon::restart(api, webui, level.as_str() != "OFF"),
},
Commands::Server { command } => match command {
Server::New => cli::server::new(),
Server::Remove { name } => cli::server::remove(name),
Server::Default { name } => cli::server::default(name),
Server::List { format } => cli::server::list(format, cli.verbose.log_level()),
},
};
if !matches!(&cli.command, Commands::Daemon { .. })
&& !matches!(&cli.command, Commands::Server { .. })
&& !matches!(&cli.command, Commands::Save { .. })
&& !matches!(&cli.command, Commands::Env { .. })
+ && !matches!(&cli.command, Commands::Export { .. })
{
then!(!daemon::pid::exists(), daemon::restart(&false, &false, false));
}
}
diff --git a/src/process/mod.rs b/src/process/mod.rs
index 23e8e46..632e223 100644
--- a/src/process/mod.rs
+++ b/src/process/mod.rs
@@ -1,658 +1,660 @@
mod unix;
use crate::{
config,
config::structs::Server,
file, helpers,
service::{run, stop, ProcessMetadata},
};
use std::{
env,
fs::File,
path::PathBuf,
sync::{Arc, Mutex},
};
use nix::{
sys::signal::{kill, Signal},
unistd::Pid,
};
use chrono::serde::ts_milliseconds;
use chrono::{DateTime, Utc};
use global_placeholders::global;
use macros_rs::{crashln, string, ternary, then};
use psutil::process;
use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;
use utoipa::ToSchema;
#[derive(Serialize, Deserialize, ToSchema)]
pub struct ItemSingle {
pub info: Info,
pub stats: Stats,
pub watch: Watch,
pub log: Log,
pub raw: Raw,
}
#[derive(Serialize, Deserialize, ToSchema)]
pub struct Info {
pub id: usize,
pub pid: i64,
pub name: String,
pub status: String,
#[schema(value_type = String, example = "/path")]
pub path: PathBuf,
pub uptime: String,
pub command: String,
pub children: Vec<i64>,
}
#[derive(Serialize, Deserialize, ToSchema)]
pub struct Stats {
pub restarts: u64,
pub start_time: i64,
pub cpu_percent: Option<f64>,
pub memory_usage: Option<MemoryInfo>,
}
#[derive(Serialize, Deserialize, ToSchema)]
pub struct MemoryInfo {
pub rss: u64,
pub vms: u64,
}
#[derive(Serialize, Deserialize, ToSchema)]
pub struct Log {
pub out: String,
pub error: String,
}
#[derive(Serialize, Deserialize, ToSchema)]
pub struct Raw {
pub running: bool,
pub crashed: bool,
pub crashes: u64,
}
#[derive(Clone)]
pub struct LogInfo {
pub out: String,
pub error: String,
}
#[derive(Serialize, Deserialize, ToSchema)]
pub struct ProcessItem {
pid: i64,
id: usize,
cpu: String,
mem: String,
name: String,
restarts: u64,
status: String,
uptime: String,
#[schema(example = "/path")]
watch_path: String,
#[schema(value_type = String, example = "2000-01-01T01:00:00.000Z")]
start_time: DateTime<Utc>,
}
#[derive(Clone)]
pub struct ProcessWrapper {
pub id: usize,
pub runner: Arc<Mutex<Runner>>,
}
pub type Env = BTreeMap<String, String>;
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Process {
pub id: usize,
pub pid: i64,
pub env: Env,
pub name: String,
pub path: PathBuf,
pub script: String,
pub restarts: u64,
pub running: bool,
pub crash: Crash,
pub watch: Watch,
pub children: Vec<i64>,
#[serde(with = "ts_milliseconds")]
pub started: DateTime<Utc>,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Crash {
pub crashed: bool,
pub value: u64,
}
#[derive(Clone, Debug, Deserialize, Serialize, ToSchema)]
pub struct Watch {
pub enabled: bool,
#[schema(example = "/path")]
pub path: String,
pub hash: String,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Runner {
pub id: id::Id,
#[serde(skip)]
pub remote: Option<Remote>,
pub list: BTreeMap<usize, Process>,
}
#[derive(Clone, Debug)]
pub struct Remote {
address: String,
token: Option<String>,
pub config: RemoteConfig,
}
#[derive(Clone, Debug, Deserialize)]
pub struct RemoteConfig {
pub shell: String,
pub args: Vec<String>,
pub log_path: String,
}
pub enum Status {
Offline,
Running,
}
impl Status {
pub fn to_bool(&self) -> bool {
match self {
Status::Offline => false,
Status::Running => true,
}
}
}
macro_rules! lock {
($runner:expr) => {{
match $runner.lock() {
Ok(runner) => runner,
Err(err) => crashln!("Unable to lock mutex: {err}"),
}
}};
}
fn kill_children(children: Vec<i64>) {
for pid in children {
if let Err(err) = kill(Pid::from_raw(pid as i32), Signal::SIGTERM) {
log::error!("Failed to stop pid {pid}: {err:?}");
};
}
}
impl Runner {
pub fn new() -> Self { dump::read() }
pub fn refresh(&self) -> Self { Runner::new() }
pub fn connect(name: String, Server { address, token }: Server, verbose: bool) -> Option<Self> {
let remote_config = match config::from(&address, token.as_deref()) {
Ok(config) => config,
Err(err) => {
log::error!("{err}");
return None;
}
};
if let Ok(dump) = dump::from(&address, token.as_deref()) {
then!(verbose, println!("{} Fetched remote (name={name}, address={address})", *helpers::SUCCESS));
Some(Runner {
remote: Some(Remote {
token,
address: string!(address),
config: remote_config,
}),
..dump
})
} else {
None
}
}
pub fn start(&mut self, name: &String, command: &String, path: PathBuf, watch: &Option<String>) -> &mut Self {
if let Some(remote) = &self.remote {
if let Err(err) = http::create(remote, name, command, path, watch) {
crashln!("{} Failed to start create {name}\nError: {:#?}", *helpers::FAIL, err);
};
} else {
let id = self.id.next();
let config = config::read().runner;
let crash = Crash { crashed: false, value: 0 };
let watch = match watch {
Some(watch) => Watch {
enabled: true,
path: string!(watch),
hash: hash::create(file::cwd().join(watch)),
},
None => Watch {
enabled: false,
path: string!(""),
hash: string!(""),
},
};
let pid = run(ProcessMetadata {
args: config.args,
name: name.clone(),
shell: config.shell,
command: command.clone(),
log_path: config.log_path,
env: unix::env(),
});
self.list.insert(
id,
Process {
id,
pid,
path,
watch,
crash,
restarts: 0,
running: true,
children: vec![],
name: name.clone(),
started: Utc::now(),
script: command.clone(),
env: env::vars().collect(),
},
);
}
return self;
}
pub fn restart(&mut self, id: usize, dead: bool) -> &mut Self {
if let Some(remote) = &self.remote {
if let Err(err) = http::restart(remote, id) {
crashln!("{} Failed to start process {id}\nError: {:#?}", *helpers::FAIL, err);
};
} else {
let process = self.process(id);
let config = config::read().runner;
let Process { path, script, name, .. } = process.clone();
kill_children(process.children.clone());
stop(process.pid);
if let Err(err) = std::env::set_current_dir(&path) {
process.running = false;
process.children = vec![];
process.crash.crashed = true;
println!("{} Failed to set working directory {:?}\nError: {:#?}", *helpers::FAIL, path, err);
} else {
let mut temp_env = process.env.iter().map(|(key, value)| format!("{}={}", key, value)).collect::<Vec<String>>();
temp_env.extend(unix::env());
process.pid = run(ProcessMetadata {
args: config.args,
name: name.clone(),
shell: config.shell,
log_path: config.log_path,
command: script.to_string(),
env: temp_env,
});
process.running = true;
process.children = vec![];
process.started = Utc::now();
process.crash.crashed = false;
process.env.extend(env::vars().collect::<Env>());
then!(dead, process.restarts += 1);
then!(dead, process.crash.value += 1);
then!(!dead, process.crash.value = 0);
}
}
return self;
}
pub fn remove(&mut self, id: usize) {
if let Some(remote) = &self.remote {
if let Err(err) = http::remove(remote, id) {
crashln!("{} Failed to stop remove {id}\nError: {:#?}", *helpers::FAIL, err);
};
} else {
self.stop(id);
self.list.remove(&id);
self.save();
}
}
pub fn set_id(&mut self, id: id::Id) {
self.id = id;
self.id.next();
self.save();
}
pub fn set_status(&mut self, id: usize, status: Status) {
self.process(id).running = status.to_bool();
self.save();
}
pub fn items(&self) -> BTreeMap<usize, Process> { self.list.clone() }
pub fn items_mut(&mut self) -> &mut BTreeMap<usize, Process> { &mut self.list }
pub fn save(&self) { then!(self.remote.is_none(), dump::write(&self)) }
pub fn count(&mut self) -> usize { self.list().count() }
pub fn is_empty(&self) -> bool { self.list.is_empty() }
pub fn exists(&self, id: usize) -> bool { self.list.contains_key(&id) }
pub fn info(&self, id: usize) -> Option<&Process> { self.list.get(&id) }
+ pub fn try_info(&self, id: usize) -> &Process { self.list.get(&id).unwrap_or_else(|| crashln!("{} Process ({id}) not found", *helpers::FAIL)) }
+
pub fn size(&self) -> Option<&usize> { self.list.iter().map(|(k, _)| k).max() }
pub fn list<'l>(&'l mut self) -> impl Iterator<Item = (&'l usize, &'l mut Process)> { self.list.iter_mut().map(|(k, v)| (k, v)) }
pub fn process(&mut self, id: usize) -> &mut Process { self.list.get_mut(&id).unwrap_or_else(|| crashln!("{} Process ({id}) not found", *helpers::FAIL)) }
pub fn pid(&self, id: usize) -> i64 { self.list.get(&id).unwrap_or_else(|| crashln!("{} Process ({id}) not found", *helpers::FAIL)).pid }
pub fn get(self, id: usize) -> ProcessWrapper {
ProcessWrapper {
id,
runner: Arc::new(Mutex::new(self)),
}
}
pub fn set_crashed(&mut self, id: usize) -> &mut Self {
self.process(id).crash.crashed = true;
return self;
}
pub fn set_env(&mut self, id: usize, env: Env) -> &mut Self {
self.process(id).env.extend(env);
return self;
}
pub fn clear_env(&mut self, id: usize) -> &mut Self {
if let Some(remote) = &self.remote {
if let Err(err) = http::clear_env(remote, id) {
crashln!("{} Failed to clear environment on {id}\nError: {:#?}", *helpers::FAIL, err);
};
} else {
self.process(id).env = BTreeMap::new();
}
return self;
}
pub fn set_children(&mut self, id: usize, children: Vec<i64>) -> &mut Self {
self.process(id).children = children;
return self;
}
pub fn new_crash(&mut self, id: usize) -> &mut Self {
self.process(id).crash.value += 1;
return self;
}
pub fn stop(&mut self, id: usize) -> &mut Self {
if let Some(remote) = &self.remote {
if let Err(err) = http::stop(remote, id) {
crashln!("{} Failed to stop process {id}\nError: {:#?}", *helpers::FAIL, err);
};
} else {
let process = self.process(id);
kill_children(process.children.clone());
stop(process.pid);
process.running = false;
process.crash.crashed = false;
process.crash.value = 0;
process.children = vec![];
}
return self;
}
pub fn flush(&mut self, id: usize) -> &mut Self {
if let Some(remote) = &self.remote {
if let Err(err) = http::flush(remote, id) {
crashln!("{} Failed to flush process {id}\nError: {:#?}", *helpers::FAIL, err);
};
} else {
self.process(id).logs().flush();
}
return self;
}
pub fn rename(&mut self, id: usize, name: String) -> &mut Self {
if let Some(remote) = &self.remote {
if let Err(err) = http::rename(remote, id, name) {
crashln!("{} Failed to rename process {id}\nError: {:#?}", *helpers::FAIL, err);
};
} else {
self.process(id).name = name;
}
return self;
}
pub fn watch(&mut self, id: usize, path: &str, enabled: bool) -> &mut Self {
let process = self.process(id);
process.watch = Watch {
enabled,
path: string!(path),
hash: ternary!(enabled, hash::create(process.path.join(path)), string!("")),
};
return self;
}
pub fn find(&self, name: &str, server_name: &String) -> Option<usize> {
let mut runner = self.clone();
if !matches!(&**server_name, "internal" | "local") {
let Some(servers) = config::servers().servers else {
crashln!("{} Failed to read servers", *helpers::FAIL)
};
if let Some(server) = servers.get(server_name) {
runner = match Runner::connect(server_name.clone(), server.get(), false) {
Some(remote) => remote,
None => crashln!("{} Failed to connect (name={server_name}, address={})", *helpers::FAIL, server.address),
};
} else {
crashln!("{} Server '{server_name}' does not exist", *helpers::FAIL)
};
}
runner.list.iter().find(|(_, p)| p.name == name).map(|(id, _)| *id)
}
pub fn fetch(&self) -> Vec<ProcessItem> {
let mut processes: Vec<ProcessItem> = Vec::new();
for (id, item) in self.items() {
let mut memory_usage: Option<MemoryInfo> = None;
let mut cpu_percent: Option<f64> = None;
if let Ok(process) = process::Process::new(item.pid as u32) {
let mem_info_psutil = process.memory_info().ok();
cpu_percent = Some(super::service::get_process_cpu_usage_percentage(item.pid as i64));
memory_usage = Some(MemoryInfo {
rss: mem_info_psutil.as_ref().unwrap().rss(),
vms: mem_info_psutil.as_ref().unwrap().vms(),
});
}
let cpu_percent = match cpu_percent {
Some(percent) => format!("{:.2}%", percent),
None => string!("0.00%"),
};
let memory_usage = match memory_usage {
Some(usage) => helpers::format_memory(usage.rss),
None => string!("0b"),
};
let status = if item.running {
string!("online")
} else {
match item.crash.crashed {
true => string!("crashed"),
false => string!("stopped"),
}
};
processes.push(ProcessItem {
id,
status,
pid: item.pid,
cpu: cpu_percent,
mem: memory_usage,
restarts: item.restarts,
name: item.name.clone(),
start_time: item.started,
watch_path: item.watch.path.clone(),
uptime: helpers::format_duration(item.started),
});
}
return processes;
}
}
impl LogInfo {
pub fn flush(&self) {
if let Err(err) = File::create(&self.out) {
log::debug!("{err}");
crashln!("{} Failed to purge logs (path={})", *helpers::FAIL, self.error);
}
if let Err(err) = File::create(&self.error) {
log::debug!("{err}");
crashln!("{} Failed to purge logs (path={})", *helpers::FAIL, self.error);
}
}
}
impl Process {
/// Get a log paths of the process item
pub fn logs(&self) -> LogInfo {
let name = self.name.replace(" ", "_");
LogInfo {
out: global!("pmc.logs.out", name.as_str()),
error: global!("pmc.logs.error", name.as_str()),
}
}
}
impl ProcessWrapper {
/// Stop the process item
pub fn stop(&mut self) { lock!(self.runner).stop(self.id).save(); }
/// Restart the process item
pub fn restart(&mut self) { lock!(self.runner).restart(self.id, false).save(); }
/// Rename the process item
pub fn rename(&mut self, name: String) { lock!(self.runner).rename(self.id, name).save(); }
/// Enable watching a path on the process item
pub fn watch(&mut self, path: &str) { lock!(self.runner).watch(self.id, path, true).save(); }
/// Disable watching on the process item
pub fn disable_watch(&mut self) { lock!(self.runner).watch(self.id, "", false).save(); }
/// Set the process item as crashed
pub fn crashed(&mut self) { lock!(self.runner).restart(self.id, true).save(); }
/// Get the borrowed runner reference (lives till program end)
pub fn get_runner(&mut self) -> &Runner { Box::leak(Box::new(lock!(self.runner))) }
/// Append new environment values to the process item
pub fn set_env(&mut self, env: Env) { lock!(self.runner).set_env(self.id, env).save(); }
/// Clear environment values of the process item
pub fn clear_env(&mut self) { lock!(self.runner).clear_env(self.id).save(); }
/// Get a json dump of the process item
pub fn fetch(&self) -> ItemSingle {
let mut runner = lock!(self.runner);
let item = runner.process(self.id);
let config = config::read().runner;
let mut memory_usage: Option<MemoryInfo> = None;
let mut cpu_percent: Option<f64> = None;
if let Ok(process) = process::Process::new(item.pid as u32) {
let mem_info_psutil = process.memory_info().ok();
cpu_percent = Some(super::service::get_process_cpu_usage_percentage(item.pid as i64));
memory_usage = Some(MemoryInfo {
rss: mem_info_psutil.as_ref().unwrap().rss(),
vms: mem_info_psutil.as_ref().unwrap().vms(),
});
}
let status = if item.running {
string!("online")
} else {
match item.crash.crashed {
true => string!("crashed"),
false => string!("stopped"),
}
};
ItemSingle {
info: Info {
status,
id: item.id,
pid: item.pid,
name: item.name.clone(),
path: item.path.clone(),
children: item.children.clone(),
uptime: helpers::format_duration(item.started),
command: format!("{} {} '{}'", config.shell, config.args.join(" "), item.script.clone()),
},
stats: Stats {
cpu_percent,
memory_usage,
restarts: item.restarts,
start_time: item.started.timestamp_millis(),
},
watch: Watch {
enabled: item.watch.enabled,
hash: item.watch.hash.clone(),
path: item.watch.path.clone(),
},
log: Log {
out: item.logs().out,
error: item.logs().error,
},
raw: Raw {
running: item.running,
crashed: item.crash.crashed,
crashes: item.crash.value,
},
}
}
}
pub mod dump;
pub mod hash;
pub mod http;
pub mod id;
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sun, Feb 1, 6:51 PM (1 d, 19 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
494888
Default Alt Text
(33 KB)
Attached To
Mode
rPMC Process Management Controller
Attached
Detach File
Event Timeline
Log In to Comment