use mumu::{
Interpreter,
Value,
FunctionValue,
apply_one_function_value,
};
use std::{
env,
process,
sync::{
Arc,
Mutex,
mpsc::{channel, Sender, Receiver},
},
thread,
collections::HashMap,
};
use lazy_static::lazy_static;
use sysinfo::{System, SystemExt, ProcessExt, Pid, PidExt};
use whoami;
use indexmap::IndexMap;
use crate::ACTIVE_TASKS;
use std::sync::atomic::Ordering;
#[cfg(unix)]
fn get_uid() -> i32 {
use nix::unistd::getuid;
getuid().as_raw() as i32
}
#[cfg(not(unix))]
fn get_uid() -> i32 {
0
}
enum ProcessMessage {
Ok(usize, Value),
Err(usize, String),
}
struct ProcessTask {
callback: Box<FunctionValue>,
done: bool,
}
struct ProcessManager {
next_id: usize,
tasks: HashMap<usize, ProcessTask>,
tx: Sender<ProcessMessage>,
rx: Receiver<ProcessMessage>,
}
lazy_static! {
static ref PROCESS_MANAGER: Mutex<ProcessManager> = Mutex::new(ProcessManager::new());
}
impl ProcessManager {
fn new() -> Self {
let (tx, rx) = channel();
ProcessManager {
next_id: 0,
tasks: HashMap::new(),
tx,
rx,
}
}
fn add_process_info_task(&mut self, callback: Box<FunctionValue>, verbose: bool) -> usize {
let task_id = self.next_id;
self.next_id += 1;
if verbose {
eprintln!("[process plugin] add_process_info_task => Creating new task with id={}.", task_id);
}
self.tasks.insert(task_id, ProcessTask {
callback,
done: false,
});
ACTIVE_TASKS.fetch_add(1, Ordering::SeqCst);
if verbose {
let total = ACTIVE_TASKS.load(Ordering::SeqCst);
eprintln!("[process plugin] add_process_info_task => ACTIVE_TASKS => now total={}.", total);
}
let txc = self.tx.clone();
thread::spawn(move || {
if verbose {
eprintln!("[process plugin] (thread) => Starting background gather for task_id={}.", task_id);
}
let mut sys = System::new();
sys.refresh_processes();
let pid_u32 = process::id();
let pid_sys: Pid = Pid::from_u32(pid_u32);
let mem_kb = match sys.process(pid_sys) {
Some(proc_info) => {
if verbose {
eprintln!(
"[process plugin] (thread) => Found process info in sysinfo. Memory = {} KB",
proc_info.memory()
);
}
proc_info.memory()
}
None => {
if verbose {
eprintln!("[process plugin] (thread) => No process info for pid={}, using 0KB", pid_u32);
}
0
}
};
let uid_val = get_uid();
let username_val = whoami::username();
let exe_full = match env::current_exe() {
Ok(p) => p,
Err(_e) => std::path::PathBuf::from("<unknown>"),
};
let self_str = exe_full.display().to_string();
let binary_name = exe_full
.file_name()
.map(|os| os.to_string_lossy().to_string())
.unwrap_or_else(|| "<no_binary_name>".to_string());
let cwd_str = match env::current_dir() {
Ok(p) => p.display().to_string(),
Err(_e) => "<unknown>".to_string(),
};
let launch_args: Vec<String> = env::args().collect();
let platform_str = env::consts::OS;
let arch_str = env::consts::ARCH;
let event_loop_len = 42;
let mut map = IndexMap::new();
map.insert("pid".to_string(), Value::Int(pid_u32 as i32));
map.insert("uid".to_string(), Value::Int(uid_val));
map.insert("username".to_string(), Value::SingleString(username_val));
map.insert("self".to_string(), Value::SingleString(self_str));
map.insert("binary_name".to_string(), Value::SingleString(binary_name));
map.insert("cwd".to_string(), Value::SingleString(cwd_str));
map.insert("launch_args".to_string(), Value::StrArray(launch_args));
map.insert("platform".to_string(), Value::SingleString(platform_str.to_string()));
map.insert("architecture".to_string(), Value::SingleString(arch_str.to_string()));
map.insert("memory_usage_kb".to_string(), Value::Int(mem_kb as i32));
map.insert("event_loop_len".to_string(), Value::Int(event_loop_len));
let result_val = Value::KeyedArray(map);
if verbose {
eprintln!(
"[process plugin] (thread) => Gathering done => sending Ok(task_id={}).",
task_id
);
}
if txc.send(ProcessMessage::Ok(task_id, result_val)).is_err() {
eprintln!("[process plugin] (thread) => Could not send Ok(...) for task_id={}", task_id);
}
});
task_id
}
fn poll_events(&mut self, interp: &mut Interpreter) {
let verbose = interp.is_verbose();
if verbose {
eprintln!("[process plugin] poll_events => about to check channel for messages...");
}
while let Ok(msg) = self.rx.try_recv() {
if verbose {
eprintln!("[process plugin] poll_events => got a message => {:?}", describe_message(&msg));
}
match msg {
ProcessMessage::Ok(id, data) => {
if let Some(task) = self.tasks.get_mut(&id) {
if !task.done {
task.done = true;
if verbose {
eprintln!("[process plugin] poll_events => calling user callback => task_id={}.", id);
}
let _ = apply_one_function_value(interp, task.callback.clone(), data);
}
}
}
ProcessMessage::Err(id, errmsg) => {
if let Some(task) = self.tasks.get_mut(&id) {
if !task.done {
task.done = true;
if verbose {
eprintln!(
"[process plugin] poll_events => callback with error => task_id={}, '{}'",
id, errmsg
);
}
let val = Value::StrArray(vec![errmsg]);
let _ = apply_one_function_value(interp, task.callback.clone(), val);
}
}
}
}
}
let before = self.tasks.len();
self.tasks.retain(|_, t| !t.done);
let after = self.tasks.len();
let removed = before.saturating_sub(after);
if removed > 0 {
ACTIVE_TASKS.fetch_sub(removed, Ordering::SeqCst);
if verbose {
eprintln!(
"[process plugin] poll_events => removed {} tasks, ACTIVE_TASKS now={}",
removed,
ACTIVE_TASKS.load(Ordering::SeqCst)
);
}
}
}
fn count_active_tasks(&self) -> usize {
ACTIVE_TASKS.load(Ordering::SeqCst)
}
}
fn describe_message(msg: &ProcessMessage) -> String {
match msg {
ProcessMessage::Ok(id, _) => format!("Ok(task_id={})", id),
ProcessMessage::Err(id, e) => format!("Err(task_id={}, '{}')", id, e),
}
}