diff --git a/Cargo.lock b/Cargo.lock index 80b4e6e..1378150 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,6 +17,15 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + [[package]] name = "autocfg" version = "1.3.0" @@ -38,6 +47,29 @@ dependencies = [ "rustc-demangle", ] +[[package]] +name = "bindgen" +version = "0.69.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a00dc851838a2120612785d195287475a3ac45514741da670b735818822129a0" +dependencies = [ + "bitflags", + "cexpr", + "clang-sys", + "itertools", + "lazy_static", + "lazycell", + "log", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn", + "which", +] + [[package]] name = "bitflags" version = "2.6.0" @@ -56,18 +88,69 @@ version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "324c74f2155653c90b04f25b2a47a8a631360cb908f92a772695f430c7e31052" +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "clang-sys" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading", +] + [[package]] name = "constcat" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f2e5af989b1955b092db01462980c0a286217f86817e12b2c09aea46bd03651" +[[package]] +name = "core-foundation-sys" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" + +[[package]] +name = "crossbeam-deque" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" + [[package]] name = "darling" version = "0.20.10" @@ -162,9 +245,15 @@ dependencies = [ "dirs-next", "thiserror", "tracing", - "windows", + "windows 0.58.0", ] +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + [[package]] name = "encoding_rs" version = "0.8.34" @@ -174,6 +263,16 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "errno" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + [[package]] name = "fnv" version = "1.0.7" @@ -197,24 +296,83 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + [[package]] name = "hermit-abi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" +[[package]] +name = "home" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +dependencies = [ + "windows-sys 0.52.0", +] + [[package]] name = "ident_case" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + +[[package]] +name = "kill_tree" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3879339076ac4da142cc852d91693462927cbc99773b5ea422e4834e68c4ff2" +dependencies = [ + "bindgen", + "nix", + "tokio", + "tracing", + "windows 0.52.0", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + [[package]] name = "libc" version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" +[[package]] +name = "libloading" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" +dependencies = [ + "cfg-if", + "windows-targets 0.52.6", +] + [[package]] name = "libredox" version = "0.1.3" @@ -225,6 +383,12 @@ dependencies = [ "libc", ] +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + [[package]] name = "lock_api" version = "0.4.12" @@ -235,12 +399,24 @@ dependencies = [ "scopeguard", ] +[[package]] +name = "log" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + [[package]] name = "memchr" version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + [[package]] name = "miniz_oxide" version = "0.7.4" @@ -261,6 +437,36 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "nix" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" +dependencies = [ + "bitflags", + "cfg-if", + "libc", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "ntapi" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4" +dependencies = [ + "winapi", +] + [[package]] name = "num_cpus" version = "1.16.0" @@ -279,11 +485,13 @@ dependencies = [ "derive_builder", "dirs-utils", "encoding_rs", + "kill_tree", "memchr", "os_pipe", "parking_lot", "serde", "shared_child", + "sysinfo", "thiserror", "tokio", "tracing", @@ -344,6 +552,16 @@ version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" +[[package]] +name = "prettyplease" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e" +dependencies = [ + "proc-macro2", + "syn", +] + [[package]] name = "proc-macro2" version = "1.0.86" @@ -362,6 +580,26 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rayon" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + [[package]] name = "redox_syscall" version = "0.5.3" @@ -382,12 +620,60 @@ dependencies = [ "thiserror", ] +[[package]] +name = "regex" +version = "1.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" + [[package]] name = "rustc-demangle" version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustix" +version = "0.38.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + [[package]] name = "scopeguard" version = "1.2.0" @@ -424,6 +710,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + [[package]] name = "signal-hook-registry" version = "1.4.2" @@ -466,6 +758,21 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "sysinfo" +version = "0.30.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a5b4ddaee55fb2bea2bf0e5000747e5f5c0de765e5a5ff87f4cd106439f4bb3" +dependencies = [ + "cfg-if", + "core-foundation-sys", + "libc", + "ntapi", + "once_cell", + "rayon", + "windows 0.52.0", +] + [[package]] name = "thiserror" version = "1.0.62" @@ -559,6 +866,18 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "which" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +dependencies = [ + "either", + "home", + "once_cell", + "rustix", +] + [[package]] name = "winapi" version = "0.3.9" @@ -581,13 +900,32 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" +dependencies = [ + "windows-core 0.52.0", + "windows-targets 0.52.6", +] + [[package]] name = "windows" version = "0.58.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6" dependencies = [ - "windows-core", + "windows-core 0.58.0", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ "windows-targets 0.52.6", ] diff --git a/nyanpasu-utils/Cargo.toml b/nyanpasu-utils/Cargo.toml index 0baffad..506a2d5 100644 --- a/nyanpasu-utils/Cargo.toml +++ b/nyanpasu-utils/Cargo.toml @@ -7,11 +7,11 @@ authors.workspace = true repository.workspace = true [features] -default = ["core_manager", "dirs", "serde", "os"] -core_manager = ["dep:os_pipe", "dep:encoding_rs", "dep:shared_child"] +default = ["core_manager", "dirs", "serde"] +core_manager = ["os", "dep:os_pipe", "dep:encoding_rs", "dep:shared_child"] dirs = ["dep:dirs-utils"] serde = ["dep:serde"] -os = [] +os = ["dep:kill_tree", "dep:sysinfo"] [dependencies] tracing = { workspace = true } @@ -26,4 +26,6 @@ memchr = "2.7" shared_child = { version = "1", optional = true } dirs-utils = { path = "../dirs-utils", optional = true } serde = { version = "1", features = ["derive"], optional = true } -constcat = "0.5.0" \ No newline at end of file +constcat = "0.5.0" +kill_tree = { version = "0.2.4", features = ["tokio"], optional = true } +sysinfo = { version = "0.30", optional = true } diff --git a/nyanpasu-utils/src/core/instance.rs b/nyanpasu-utils/src/core/instance.rs index 5252ba8..6b34bee 100644 --- a/nyanpasu-utils/src/core/instance.rs +++ b/nyanpasu-utils/src/core/instance.rs @@ -8,6 +8,7 @@ use shared_child::SharedChild; use std::os::windows::process::CommandExt; use std::{ffi::OsStr, path::PathBuf, process::Command as StdCommand, sync::Arc, time::Duration}; use tokio::{process::Command as TokioCommand, sync::mpsc::Receiver}; +use tracing_attributes::instrument; // TODO: migrate to https://github.com/tauri-apps/tauri-plugin-shell/blob/v2/src/commands.rs @@ -18,6 +19,8 @@ pub struct CoreInstance { pub binary_path: PathBuf, pub app_dir: PathBuf, pub config_path: PathBuf, + /// A pid hold the instance, should check it running or not while start instance + pid_path: PathBuf, #[builder(default = "self.default_instance()", setter(skip))] instance: Mutex>>, #[builder(default = "self.default_state()", setter(skip))] @@ -128,6 +131,18 @@ impl CoreInstance { Ok(()) } + #[instrument(skip(self))] + async fn kill_instance_by_pid_file(&self) -> Result<(), std::io::Error> { + tracing::debug!("kill instance by pid file: {:?}", self.pid_path); + crate::os::kill_by_pid_file(&self.pid_path).await + } + + #[instrument(skip(self))] + async fn write_pid_file(&self, pid: u32) -> Result<(), std::io::Error> { + crate::os::create_pid_file(&self.pid_path, pid).await + } + + #[instrument(skip(self))] pub fn run(&self) -> Result<(Arc, Receiver), CoreInstanceError> { { let state = self.state.read(); @@ -135,6 +150,14 @@ impl CoreInstance { return Err(CoreInstanceError::StateCheckFailed); } } + + // kill instance by pid file if exists + block_on(async { + if let Err(err) = self.kill_instance_by_pid_file().await { + tracing::error!("Failed to kill instance by pid file: {:?}", err); + } + }); + let args = match self.core_type { CoreType::Clash(ref core_type) => { core_type.get_run_args(&self.app_dir, &self.config_path) @@ -224,6 +247,11 @@ impl CoreInstance { spawn(async move { tx.send(CommandEvent::DelayCheckpointPass).await }); } }); + block_on(async { + if let Err(err) = self.write_pid_file(child.id()).await { + tracing::error!("Failed to write pid file: {:?}", err); + } + }); { let mut instance = self.instance.lock(); *instance = Some(child.clone()); diff --git a/nyanpasu-utils/src/os/mod.rs b/nyanpasu-utils/src/os/mod.rs index 53564ef..79a21e3 100644 --- a/nyanpasu-utils/src/os/mod.rs +++ b/nyanpasu-utils/src/os/mod.rs @@ -1,3 +1,79 @@ +#![allow(unused_imports)] +#![allow(dead_code)] + mod os_impl; pub use os_impl::*; +use sysinfo::{Pid, ProcessRefreshKind, RefreshKind, System}; +use tracing_attributes::instrument; + +use std::{fmt::Display, io::Error as IoError, path::Path}; +use tokio::{fs::OpenOptions, io::AsyncWriteExt}; + +#[instrument] +pub async fn create_pid_file(path: T, pid: u32) -> Result<(), std::io::Error> +where + T: AsRef + std::fmt::Debug, +{ + let path = path.as_ref(); + let mut file = OpenOptions::new() + .write(true) + .create(true) + .truncate(true) + .open(path) + .await?; + file.write_all(pid.to_string().as_bytes()).await?; + Ok(()) +} + +#[instrument] +pub async fn get_pid_from_file(path: T) -> Option +where + T: AsRef + std::fmt::Debug, +{ + let path = path.as_ref(); + let content = tokio::fs::read_to_string(path).await.unwrap_or_default(); + let pid = content.trim().parse().ok()?; + Some(pid) +} + +#[instrument] +pub fn pid_exists(pid: u32) -> bool { + let kind = RefreshKind::new().with_processes(ProcessRefreshKind::new()); + let mut system = System::new_with_specifics(kind); + system.refresh_specifics(kind); + system.process(Pid::from_u32(pid)).is_some() +} + +#[instrument] +pub async fn kill_pid(pid: u32) -> Result<(), std::io::Error> { + tracing::debug!("kill pid: {}", pid); + if pid_exists(pid) { + tracing::debug!("pid exists, kill it"); + let list = kill_tree::tokio::kill_tree(pid as u32) + .await + .map_err(|e| IoError::new(std::io::ErrorKind::Other, format!("kill error: {:?}", e)))?; + for p in list { + if matches!(p, kill_tree::Output::Killed { .. }) { + tracing::info!("process is killed: {:?}", p); + } + } + } + Ok(()) +} + +#[instrument] +pub async fn kill_by_pid_file(path: T) -> Result<(), std::io::Error> +where + T: AsRef + std::fmt::Debug, +{ + let pid = match get_pid_from_file(&path).await { + Some(pid) => pid, + None => { + tracing::debug!("pid file not found or parsing error, skip"); + return Ok(()); + } + }; + kill_pid(pid).await?; + tokio::fs::remove_file(path).await +} diff --git a/nyanpasu-utils/src/os/os_impl/windows.rs b/nyanpasu-utils/src/os/os_impl/windows.rs index 525731e..ffa5e63 100644 --- a/nyanpasu-utils/src/os/os_impl/windows.rs +++ b/nyanpasu-utils/src/os/os_impl/windows.rs @@ -3,7 +3,7 @@ use tokio::process::Command; pub async fn get_current_user_sid() -> IoResult { let output = Command::new("cmd") - .args(&["/C", "wmic useraccount where name='%username%' get sid"]) + .args(["/C", "wmic useraccount where name='%username%' get sid"]) .output() .await .map_err(|e| {