diff --git a/Cargo.lock b/Cargo.lock index 2956672..7e4e0e0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1757,7 +1757,7 @@ dependencies = [ "reqwest", "rhai", "sha2", - "sysinfo", + "sysinfo 0.34.2", "talaria", "tokio", ] @@ -2552,13 +2552,23 @@ dependencies = [ [[package]] name = "objc2-core-foundation" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daeaf60f25471d26948a1c2f840e3f7d86f4109e3af4e8e4b5cd70c39690d925" +checksum = "1c10c2894a6fed806ade6027bcd50662746363a9589d3ec9d9bef30a4e4bc166" dependencies = [ "bitflags", ] +[[package]] +name = "objc2-io-kit" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71c1c64d6120e51cd86033f67176b1cb66780c2efe34dec55176f77befd93c0a" +dependencies = [ + "libc", + "objc2-core-foundation", +] + [[package]] name = "object" version = "0.36.7" @@ -3811,6 +3821,20 @@ dependencies = [ "windows 0.57.0", ] +[[package]] +name = "sysinfo" +version = "0.35.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79251336d17c72d9762b8b54be4befe38d2db56fbbc0241396d70f173c39d47a" +dependencies = [ + "libc", + "memchr", + "ntapi", + "objc2-core-foundation", + "objc2-io-kit", + "windows 0.61.1", +] + [[package]] name = "system-configuration" version = "0.6.1" @@ -3845,6 +3869,7 @@ dependencies = [ "serde_json", "strum", "strum_macros", + "sysinfo 0.35.1", "thiserror 2.0.12", "toml", "whoami", @@ -3865,7 +3890,7 @@ dependencies = [ "serde_json", "structz-macros", "strum", - "sysinfo", + "sysinfo 0.34.2", "talaria", "tokio", "toml", @@ -3995,9 +4020,9 @@ dependencies = [ [[package]] name = "tokio" -version = "1.44.1" +version = "1.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f382da615b842244d4b8738c82ed1275e6c5dd90c459a30941cd07080b06c91a" +checksum = "2513ca694ef9ede0fb23fe71a4ee4107cb102b9dc1930f6d0fd77aae068ae165" dependencies = [ "backtrace", "bytes", @@ -4586,6 +4611,28 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows" +version = "0.61.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5ee8f3d025738cb02bad7868bbb5f8a6327501e870bf51f1b455b0a2454a419" +dependencies = [ + "windows-collections", + "windows-core 0.61.1", + "windows-future", + "windows-link", + "windows-numerics", +] + +[[package]] +name = "windows-collections" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" +dependencies = [ + "windows-core 0.61.1", +] + [[package]] name = "windows-core" version = "0.52.0" @@ -4601,12 +4648,36 @@ version = "0.57.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2ed2439a290666cd67ecce2b0ffaad89c2a56b976b736e6ece670297897832d" dependencies = [ - "windows-implement", - "windows-interface", + "windows-implement 0.57.0", + "windows-interface 0.57.0", "windows-result 0.1.2", "windows-targets 0.52.6", ] +[[package]] +name = "windows-core" +version = "0.61.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46ec44dc15085cea82cf9c78f85a9114c463a369786585ad2882d1ff0b0acf40" +dependencies = [ + "windows-implement 0.60.0", + "windows-interface 0.59.1", + "windows-link", + "windows-result 0.3.3", + "windows-strings 0.4.1", +] + +[[package]] +name = "windows-future" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" +dependencies = [ + "windows-core 0.61.1", + "windows-link", + "windows-threading", +] + [[package]] name = "windows-implement" version = "0.57.0" @@ -4618,6 +4689,17 @@ dependencies = [ "syn 2.0.100", ] +[[package]] +name = "windows-implement" +version = "0.60.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.100", +] + [[package]] name = "windows-interface" version = "0.57.0" @@ -4629,20 +4711,41 @@ dependencies = [ "syn 2.0.100", ] +[[package]] +name = "windows-interface" +version = "0.59.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.100", +] + [[package]] name = "windows-link" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" +[[package]] +name = "windows-numerics" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" +dependencies = [ + "windows-core 0.61.1", + "windows-link", +] + [[package]] name = "windows-registry" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4286ad90ddb45071efd1a66dfa43eb02dd0dfbae1545ad6cc3c51cf34d7e8ba3" dependencies = [ - "windows-result 0.3.2", - "windows-strings", + "windows-result 0.3.3", + "windows-strings 0.3.1", "windows-targets 0.53.0", ] @@ -4657,9 +4760,9 @@ dependencies = [ [[package]] name = "windows-result" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252" +checksum = "4b895b5356fc36103d0f64dd1e94dfa7ac5633f1c9dd6e80fe9ec4adef69e09d" dependencies = [ "windows-link", ] @@ -4673,6 +4776,15 @@ dependencies = [ "windows-link", ] +[[package]] +name = "windows-strings" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a7ab927b2637c19b3dbe0965e75d8f2d30bdd697a1516191cad2ec4df8fb28a" +dependencies = [ + "windows-link", +] + [[package]] name = "windows-sys" version = "0.52.0" @@ -4738,6 +4850,15 @@ dependencies = [ "windows_x86_64_msvc 0.53.0", ] +[[package]] +name = "windows-threading" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6" +dependencies = [ + "windows-link", +] + [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" diff --git a/Cargo.toml b/Cargo.toml index 45aa20e..d30bc45 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,8 +8,6 @@ members = [ ] resolver = "2" -[profile] - [profile.wasm-dev] inherits = "dev" opt-level = 1 diff --git a/talaria/Cargo.toml b/talaria/Cargo.toml index 9db1b9a..cfbd5f4 100644 --- a/talaria/Cargo.toml +++ b/talaria/Cargo.toml @@ -19,3 +19,4 @@ rhai = { version = "1.21", featues = ["sync"] } whoami = "1.6.0" elevated-command = "1.1.2" toml = "0.8.22" +sysinfo = "0.35.1" diff --git a/talaria/src/stdlib/error.rs b/talaria/src/stdlib/error.rs index eff786e..75e551f 100644 --- a/talaria/src/stdlib/error.rs +++ b/talaria/src/stdlib/error.rs @@ -81,6 +81,45 @@ pub mod error { #[strum(props(class = "sys"))] SysUnsupportedError(String), + + #[strum( + props(class = "proc", name = "ProcessDoesNotExist"), + to_string = "process [{pid}] does not exists" + )] + ProcProcessDoesNotExist { pid: usize }, + + #[strum( + props(class = "proc", name = "FailedToKill"), + to_string = "process [{pid}] could not be killed" + )] + ProcFailedToKill { pid: usize }, + + #[strum( + props(class = "proc", name = "FailedToSendSignal"), + to_string = "process [{pid}] did not recieve kill signal" + )] + ProcFailedToSendSignal { pid: usize }, + + #[strum( + props(class = "proc", name = "TimeOut"), + to_string = "process [{pid}] timedout when when attempting to kill" + )] + ProcTimeOut { pid: usize }, + + #[strum( + props(class = "proc", name = "BadArguments"), + to_string = "could not parse arguments" + )] + ProcBadArguments, + + #[strum( + props(class = "proc", name = "FailedToStartProcess"), + to_string = "failed to start process: {command}" + )] + ProcFailedToStartProcess { command: String }, + + #[strum(props(class = "proc", name = "BadPid"), to_string = "bad pid: {pid}")] + ProcBadPid { pid: i64 }, } impl Into>> for ScriptError { diff --git a/talaria/src/stdlib/proc.rs b/talaria/src/stdlib/proc.rs index 7e41d83..e31bb03 100644 --- a/talaria/src/stdlib/proc.rs +++ b/talaria/src/stdlib/proc.rs @@ -1,5 +1,164 @@ +use rhai::plugin::*; + use rhai::export_module; use rhai::Module; #[export_module] -pub mod proc {} +pub mod proc { + + use crate::stdlib::{error::error::ScriptError, CastArray}; + use rhai::Array; + use sysinfo::{Pid, System}; + + /// Returns an Array of all process names and their `pid` + /// + /// # Example + /// ```terminal + /// run rhai `let res = proc::list; for i in res { print(i); }` + /// ``` + pub fn list() -> Array { + /* rhai Arrays do not supprot tuples, a structure that makes sense here: (name, pid). + * I do not love how the name and pid are one, but unsure how to solve this at the moment. + */ + let sys = System::new_all(); + let mut proc_list: Array = Array::new(); + + sys.processes().into_iter().for_each(|x| { + let format = format!( + "{} = {}", + x.1.name().to_string_lossy(), + x.0.as_u32().to_string() + ); + proc_list.push(format.into()); + }); + + proc_list + } + + /// Kills a process given a `pid` + /// + /// > [!CAUTION] + /// > Can throw exception if `pid` does not exist, if `pid` did not recieve signal, + /// > the pid given is bad (negative, for instance), or if `pid` failed to be kill + /// + ///
+ /// Exceptions + /// + /// - `ProcProcessDoesNotExist` + /// - `ProcFailedToSendSignal` + /// - `ProcFailedToKill` + /// - `ProcBadPid` + ///
+ /// + /// # Example + /// ```terminal + /// run rhai `try { proc::kill(123456); } catch (err) { print(err.msg); }` + /// ``` + #[rhai_fn(return_raw)] + pub fn kill(pid: i64) -> Result<(), Box> { + // Explaining => pid: i64 + // take this command attempting to kill the pid 123456: + // run rhai `try { proc::kill(123456); } catch (err) { print(err.msg); }` + // 123456 has the type i64 by default, where we need usize for all pids. + // this allows us to avoid casting in rhai directly. + if pid <= 0 || pid >= usize::MAX.try_into().unwrap() { + return ScriptError::ProcBadPid { pid }.into(); + } + + let pid = pid as usize; + + // Attempt to kill process + if let Err(error) = kill_attempt(pid) { + return error.into(); + }; + + // Interval to check if pid if process is still alive. + // Max 1.890 Seconds + let mut check_interval_ms = vec![5, 10, 25, 50, 100, 200, 500, 1000].into_iter(); + + let success = check_interval_ms.any(|interval| { + let sys = System::new_all(); + // Check if process still exists + if let None = sys.process(Pid::from(pid)) { + return true; + } + + // if process still exists, sleep and return false + std::thread::sleep(std::time::Duration::from_millis(interval)); + return false; + }); + + // if any returned true, process was successfully killed + match success { + true => Ok(()), + false => ScriptError::ProcFailedToKill { pid }.into(), + } + } + + // Helper function for killing a process + // Simply sends the kill signal. + // + // Returns true => successfully send kill sig + // Returns false => failed to send kill sig (example: bad perms) + fn kill_attempt(pid: usize) -> Result<(), ScriptError> { + let sys = System::new_all(); + let proc = sys.process(Pid::from(pid)); + let res = match proc { + Some(process) => process.kill(), + None => return Err(ScriptError::ProcProcessDoesNotExist { pid }), + }; + + match res { + true => Ok(()), + false => Err(ScriptError::ProcFailedToSendSignal { pid }), + } + } + + /// Starts a command with arguments + /// + /// > [CAUTION] + /// > Can throw exceptions if `args` cannot be casted, or if the process failed to start + /// + ///
+ /// Exceptions + /// + /// - `ProcBadArguments` + /// - `ProcFailedToStartProcess` + ///
+ /// + /// # Example + /// ```terminal + /// run rhai `try { proc::start("yes", ["pantheon is the best"]); } catch (err) { print(err.msg); }` + /// ``` + #[rhai_fn(return_raw)] + pub fn start(command: &str, args: Array) -> Result> { + let args = args.try_cast::(); + let child = match args { + Ok(vec_of_args) => std::process::Command::new(command) + .args(vec_of_args) + .spawn(), + Err(_) => return ScriptError::ProcBadArguments.into(), + }; + + match child { + Ok(child_p) => Ok(child_p.id()), + Err(_) => ScriptError::ProcFailedToStartProcess { + command: command.into(), + } + .into(), + } + } + + /// Returns the current `pid` of Hermes + /// + /// # Example + /// ```terminal + /// run rhai `print(proc::current_pid());` + /// ``` + pub fn current_pid() -> u32 { + std::process::id() + } +} + +// TODO: +// tests