Page MenuHomePhorge

No OneTemporary

Size
33 KB
Referenced Files
None
Subscribers
None
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

Mime Type
text/x-diff
Expires
Sun, Feb 1, 6:51 PM (1 d, 21 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
494888
Default Alt Text
(33 KB)

Event Timeline