mirror of
https://github.com/Cyrix126/gupaxx.git
synced 2025-01-08 23:19:26 +00:00
macOS: handle killing XMRig with [sudo]
Even with the parent-child relationship and direct process handle, Gupax can't kill an XMRig spawned with [sudo] on macOS, even though it can do it fine on Linux. So, on macOS, get the user's [sudo] pass on the [Stop] button and summon a [sudo kill] on XMRig's PID. This also complicates things since now we have to keep [SudoState] somehow between a [Stop] and a [Start]. So there is macOS specific code that now handles that.
This commit is contained in:
parent
4da775667b
commit
3fee0e5690
4 changed files with 83 additions and 41 deletions
11
Cargo.lock
generated
11
Cargo.lock
generated
|
@ -30,6 +30,7 @@ dependencies = [
|
||||||
"rfd",
|
"rfd",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
"static_vcruntime",
|
||||||
"sudo",
|
"sudo",
|
||||||
"tar",
|
"tar",
|
||||||
"tls-api",
|
"tls-api",
|
||||||
|
@ -3740,6 +3741,12 @@ version = "1.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "static_vcruntime"
|
||||||
|
version = "2.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "954e3e877803def9dc46075bf4060147c55cd70db97873077232eae0269dc89b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "str-buf"
|
name = "str-buf"
|
||||||
version = "1.0.6"
|
version = "1.0.6"
|
||||||
|
@ -3911,9 +3918,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tiff"
|
name = "tiff"
|
||||||
version = "0.8.0"
|
version = "0.8.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f17def29300a156c19ae30814710d9c63cd50288a49c6fd3a10ccfbe4cf886fd"
|
checksum = "7449334f9ff2baf290d55d73983a7d6fa15e01198faef72af07e2a8db851e471"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"flate2",
|
"flate2",
|
||||||
"jpeg-decoder",
|
"jpeg-decoder",
|
||||||
|
|
|
@ -23,12 +23,11 @@
|
||||||
// way down and (if possible) asynchronously executing them at the very end.
|
// way down and (if possible) asynchronously executing them at the very end.
|
||||||
//
|
//
|
||||||
// The main GUI thread will interface with this thread by mutating the Arc<Mutex>'s
|
// The main GUI thread will interface with this thread by mutating the Arc<Mutex>'s
|
||||||
// found here, e.g: User clicks [Start P2Pool] -> Arc<Mutex<ProcessSignal> is set
|
// found here, e.g: User clicks [Stop P2Pool] -> Arc<Mutex<ProcessSignal> is set
|
||||||
// indicating to this thread during its loop: "I should start P2Pool!", e.g:
|
// indicating to this thread during its loop: "I should stop P2Pool!", e.g:
|
||||||
//
|
//
|
||||||
// match p2pool.lock().unwrap().signal {
|
// if p2pool.lock().unwrap().signal == ProcessSignal::Stop {
|
||||||
// ProcessSignal::Start => start_p2pool(),
|
// stop_p2pool(),
|
||||||
// ...
|
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// This also includes all things related to handling the child processes (P2Pool/XMRig):
|
// This also includes all things related to handling the child processes (P2Pool/XMRig):
|
||||||
|
@ -60,7 +59,7 @@ const MAX_PROCESS_OUTPUT_BYTES: usize = 56_000_000;
|
||||||
// Just a little leeway so a reset will go off before the [String] allocates more memory.
|
// Just a little leeway so a reset will go off before the [String] allocates more memory.
|
||||||
const PROCESS_OUTPUT_LEEWAY: usize = MAX_PROCESS_OUTPUT_BYTES - 1000;
|
const PROCESS_OUTPUT_LEEWAY: usize = MAX_PROCESS_OUTPUT_BYTES - 1000;
|
||||||
// The max bytes the GUI thread should hold
|
// The max bytes the GUI thread should hold
|
||||||
const MAX_GUI_OUTPUT_BYTES: usize = 1_000_000;
|
const MAX_GUI_OUTPUT_BYTES: usize = 500_000;
|
||||||
const GUI_OUTPUT_LEEWAY: usize = MAX_GUI_OUTPUT_BYTES - 1000;
|
const GUI_OUTPUT_LEEWAY: usize = MAX_GUI_OUTPUT_BYTES - 1000;
|
||||||
|
|
||||||
|
|
||||||
|
@ -234,7 +233,7 @@ impl Helper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// For the GUI thread, the max is 1_000_000 bytes.
|
// For the GUI thread
|
||||||
fn check_reset_gui_p2pool_output(gui_api: &Arc<Mutex<PubP2poolApi>>) {
|
fn check_reset_gui_p2pool_output(gui_api: &Arc<Mutex<PubP2poolApi>>) {
|
||||||
let mut gui_api = gui_api.lock().unwrap();
|
let mut gui_api = gui_api.lock().unwrap();
|
||||||
if gui_api.output.len() > GUI_OUTPUT_LEEWAY {
|
if gui_api.output.len() > GUI_OUTPUT_LEEWAY {
|
||||||
|
@ -554,6 +553,25 @@ impl Helper {
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
|
// If processes are started with [sudo] on macOS, they must also
|
||||||
|
// be killed with [sudo] (even if I have a direct handle to it as the
|
||||||
|
// parent process...!). This is only needed on macOS, not Linux.
|
||||||
|
fn sudo_kill(pid: u32, sudo: &Arc<Mutex<SudoState>>) -> bool {
|
||||||
|
// Spawn [sudo] to execute [kill] on the given [pid]
|
||||||
|
let mut child = std::process::Command::new("sudo")
|
||||||
|
.args(["--stdin", "kill", "-9", &pid.to_string()])
|
||||||
|
.stdin(Stdio::piped())
|
||||||
|
.spawn().unwrap();
|
||||||
|
|
||||||
|
// Write the [sudo] password to STDIN.
|
||||||
|
let mut stdin = child.stdin.take().unwrap();
|
||||||
|
use std::io::Write;
|
||||||
|
writeln!(stdin, "{}\n", sudo.lock().unwrap().pass);
|
||||||
|
|
||||||
|
// Return exit code of [sudo/kill].
|
||||||
|
child.wait().unwrap().success()
|
||||||
|
}
|
||||||
|
|
||||||
// Just sets some signals for the watchdog thread to pick up on.
|
// Just sets some signals for the watchdog thread to pick up on.
|
||||||
pub fn stop_xmrig(helper: &Arc<Mutex<Self>>) {
|
pub fn stop_xmrig(helper: &Arc<Mutex<Self>>) {
|
||||||
info!("XMRig | Attempting to stop...");
|
info!("XMRig | Attempting to stop...");
|
||||||
|
@ -573,13 +591,10 @@ impl Helper {
|
||||||
let path = path.clone();
|
let path = path.clone();
|
||||||
// This thread lives to wait, start xmrig then die.
|
// This thread lives to wait, start xmrig then die.
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
while helper.lock().unwrap().xmrig.lock().unwrap().is_alive() {
|
while helper.lock().unwrap().xmrig.lock().unwrap().state != ProcessState::Waiting {
|
||||||
warn!("XMRig | Want to restart but process is still alive, waiting...");
|
warn!("XMRig | Want to restart but process is still alive, waiting...");
|
||||||
thread::sleep(SECOND);
|
thread::sleep(SECOND);
|
||||||
}
|
}
|
||||||
// We should reallllly sleep so all the components have a chance to catch up.
|
|
||||||
// Premature starting could miss the [sudo] STDIN timeframe and output it to STDOUT.
|
|
||||||
thread::sleep(std::time::Duration::from_secs(3));
|
|
||||||
// Ok, process is not alive, start the new one!
|
// Ok, process is not alive, start the new one!
|
||||||
Self::start_xmrig(&helper, &state, &path, sudo);
|
Self::start_xmrig(&helper, &state, &path, sudo);
|
||||||
});
|
});
|
||||||
|
@ -616,10 +631,11 @@ impl Helper {
|
||||||
// the XMRig path is just an argument to sudo, so add it.
|
// the XMRig path is just an argument to sudo, so add it.
|
||||||
// Before that though, add the ["--prompt"] flag and set it
|
// Before that though, add the ["--prompt"] flag and set it
|
||||||
// to emptyness so that it doesn't show up in the output.
|
// to emptyness so that it doesn't show up in the output.
|
||||||
#[cfg(target_family = "unix")] // Of course, only on Unix.
|
if cfg!(unix) {
|
||||||
args.push(r#"--prompt="#.to_string());
|
args.push("--stdin".to_string());
|
||||||
#[cfg(target_family = "unix")]
|
args.push(r#"--prompt="#.to_string());
|
||||||
args.push(path.display().to_string());
|
args.push(path.display().to_string());
|
||||||
|
}
|
||||||
|
|
||||||
// [Simple]
|
// [Simple]
|
||||||
if state.simple {
|
if state.simple {
|
||||||
|
@ -716,10 +732,6 @@ impl Helper {
|
||||||
// 1c. Create child
|
// 1c. Create child
|
||||||
let child_pty = Arc::new(Mutex::new(pair.slave.spawn_command(cmd).unwrap()));
|
let child_pty = Arc::new(Mutex::new(pair.slave.spawn_command(cmd).unwrap()));
|
||||||
|
|
||||||
// 1d. Wait a bit for [sudo].
|
|
||||||
#[cfg(target_family = "unix")]
|
|
||||||
thread::sleep(std::time::Duration::from_secs(3));
|
|
||||||
|
|
||||||
// 2. Set process state
|
// 2. Set process state
|
||||||
let mut lock = process.lock().unwrap();
|
let mut lock = process.lock().unwrap();
|
||||||
lock.state = ProcessState::Alive;
|
lock.state = ProcessState::Alive;
|
||||||
|
@ -733,7 +745,6 @@ impl Helper {
|
||||||
if cfg!(unix) {
|
if cfg!(unix) {
|
||||||
writeln!(lock.stdin.as_mut().unwrap(), "{}", sudo.lock().unwrap().pass);
|
writeln!(lock.stdin.as_mut().unwrap(), "{}", sudo.lock().unwrap().pass);
|
||||||
SudoState::wipe(&sudo);
|
SudoState::wipe(&sudo);
|
||||||
drop(sudo);
|
|
||||||
}
|
}
|
||||||
drop(lock);
|
drop(lock);
|
||||||
|
|
||||||
|
@ -757,10 +768,34 @@ impl Helper {
|
||||||
// Set timer
|
// Set timer
|
||||||
let now = Instant::now();
|
let now = Instant::now();
|
||||||
|
|
||||||
|
// Check if the process secretly died without us knowing :)
|
||||||
|
if let Ok(Some(code)) = child_pty.lock().unwrap().try_wait() {
|
||||||
|
let exit_status = match code.success() {
|
||||||
|
true => { process.lock().unwrap().state = ProcessState::Dead; "Successful" },
|
||||||
|
false => { process.lock().unwrap().state = ProcessState::Failed; "Failed" },
|
||||||
|
};
|
||||||
|
let uptime = HumanTime::into_human(start.elapsed());
|
||||||
|
info!("XMRig | Stopped ... Uptime was: [{}], Exit status: [{}]", uptime, exit_status);
|
||||||
|
writeln!(gui_api.lock().unwrap().output, "{}\nXMRig stopped | Uptime: [{}] | Exit status: [{}]\n{}\n\n\n\n", HORI_CONSOLE, uptime, exit_status, HORI_CONSOLE);
|
||||||
|
process.lock().unwrap().signal = ProcessSignal::None;
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
// Stop on [Stop/Restart] SIGNAL
|
// Stop on [Stop/Restart] SIGNAL
|
||||||
let signal = process.lock().unwrap().signal;
|
let signal = process.lock().unwrap().signal;
|
||||||
if signal == ProcessSignal::Stop || signal == ProcessSignal::Restart {
|
if signal == ProcessSignal::Stop || signal == ProcessSignal::Restart {
|
||||||
child_pty.lock().unwrap().kill();
|
// macOS requires [sudo] again to kill [XMRig]
|
||||||
|
if cfg!(target_os = "macos") {
|
||||||
|
// If we're at this point, that means the user has
|
||||||
|
// entered their [sudo] pass again, after we wiped it.
|
||||||
|
// So, we should be able to find it in our [Arc<Mutex<SudoState>>].
|
||||||
|
Self::sudo_kill(child_pty.lock().unwrap().process_id().unwrap(), &sudo);
|
||||||
|
// And... wipe it again (only if we're stopping full).
|
||||||
|
// If we're restarting, the next start will wipe it for us.
|
||||||
|
if signal != ProcessSignal::Restart { SudoState::wipe(&sudo); }
|
||||||
|
} else {
|
||||||
|
child_pty.lock().unwrap().kill();
|
||||||
|
}
|
||||||
let exit_status = match child_pty.lock().unwrap().wait() {
|
let exit_status = match child_pty.lock().unwrap().wait() {
|
||||||
Ok(e) => {
|
Ok(e) => {
|
||||||
let mut process = process.lock().unwrap();
|
let mut process = process.lock().unwrap();
|
||||||
|
@ -788,17 +823,6 @@ impl Helper {
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
// Check if the process secretly died without us knowing :)
|
|
||||||
} else if let Ok(Some(code)) = child_pty.lock().unwrap().try_wait() {
|
|
||||||
let exit_status = match code.success() {
|
|
||||||
true => { process.lock().unwrap().state = ProcessState::Dead; "Successful" },
|
|
||||||
false => { process.lock().unwrap().state = ProcessState::Failed; "Failed" },
|
|
||||||
};
|
|
||||||
let uptime = HumanTime::into_human(start.elapsed());
|
|
||||||
info!("XMRig | Stopped ... Uptime was: [{}], Exit status: [{}]", uptime, exit_status);
|
|
||||||
writeln!(gui_api.lock().unwrap().output, "{}\nXMRig stopped | Uptime: [{}] | Exit status: [{}]\n{}\n\n\n\n", HORI_CONSOLE, uptime, exit_status, HORI_CONSOLE);
|
|
||||||
process.lock().unwrap().signal = ProcessSignal::None;
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check vector of user input
|
// Check vector of user input
|
||||||
|
|
21
src/main.rs
21
src/main.rs
|
@ -1207,14 +1207,23 @@ impl eframe::App for App {
|
||||||
});
|
});
|
||||||
} else if self.xmrig.lock().unwrap().is_alive() {
|
} else if self.xmrig.lock().unwrap().is_alive() {
|
||||||
if ui.add_sized([width, height], Button::new("⟲")).on_hover_text("Restart XMRig").clicked() {
|
if ui.add_sized([width, height], Button::new("⟲")).on_hover_text("Restart XMRig").clicked() {
|
||||||
self.sudo.lock().unwrap().signal = ProcessSignal::Restart;
|
if cfg!(windows) {
|
||||||
#[cfg(target_family = "unix")]
|
Helper::restart_xmrig(&self.helper, &self.state.xmrig, &self.state.gupax.absolute_xmrig_path, Arc::clone(&self.sudo));
|
||||||
self.error_state.ask_sudo(&self.sudo);
|
} else if cfg!(target_os = "macos") {
|
||||||
#[cfg(target_os = "windows")]
|
self.sudo.lock().unwrap().signal = ProcessSignal::Restart;
|
||||||
Helper::restart_xmrig(&self.helper, &self.state.xmrig, &self.state.gupax.absolute_xmrig_path, Arc::clone(&self.sudo));
|
self.error_state.ask_sudo(&self.sudo);
|
||||||
|
} else {
|
||||||
|
self.sudo.lock().unwrap().signal = ProcessSignal::Restart;
|
||||||
|
self.error_state.ask_sudo(&self.sudo);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if ui.add_sized([width, height], Button::new("⏹")).on_hover_text("Stop XMRig").clicked() {
|
if ui.add_sized([width, height], Button::new("⏹")).on_hover_text("Stop XMRig").clicked() {
|
||||||
Helper::stop_xmrig(&self.helper);
|
if cfg!(target_os = "macos") {
|
||||||
|
self.sudo.lock().unwrap().signal = ProcessSignal::Stop;
|
||||||
|
self.error_state.ask_sudo(&self.sudo);
|
||||||
|
} else {
|
||||||
|
Helper::stop_xmrig(&self.helper);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ui.add_enabled_ui(false, |ui| {
|
ui.add_enabled_ui(false, |ui| {
|
||||||
ui.add_sized([width, height], Button::new("⏺")).on_hover_text("Start XMRig");
|
ui.add_sized([width, height], Button::new("⏺")).on_hover_text("Start XMRig");
|
||||||
|
|
|
@ -78,7 +78,7 @@ impl SudoState {
|
||||||
let mut state = state.lock().unwrap();
|
let mut state = state.lock().unwrap();
|
||||||
state.testing = false;
|
state.testing = false;
|
||||||
state.success = false;
|
state.success = false;
|
||||||
state.signal = ProcessSignal::None;
|
// state.signal = ProcessSignal::None;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Swaps the pass with another 256-capacity String,
|
// Swaps the pass with another 256-capacity String,
|
||||||
|
@ -163,12 +163,14 @@ impl SudoState {
|
||||||
if state.lock().unwrap().success {
|
if state.lock().unwrap().success {
|
||||||
match state.lock().unwrap().signal {
|
match state.lock().unwrap().signal {
|
||||||
ProcessSignal::Restart => crate::helper::Helper::restart_xmrig(&helper, &xmrig, &path, Arc::clone(&state)),
|
ProcessSignal::Restart => crate::helper::Helper::restart_xmrig(&helper, &xmrig, &path, Arc::clone(&state)),
|
||||||
|
ProcessSignal::Stop => crate::helper::Helper::stop_xmrig(&helper),
|
||||||
_ => crate::helper::Helper::start_xmrig(&helper, &xmrig, &path, Arc::clone(&state)),
|
_ => crate::helper::Helper::start_xmrig(&helper, &xmrig, &path, Arc::clone(&state)),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
state.lock().unwrap().msg = "Incorrect password!".to_string();
|
state.lock().unwrap().msg = "Incorrect password!".to_string();
|
||||||
Self::wipe(&state);
|
Self::wipe(&state);
|
||||||
}
|
}
|
||||||
|
state.lock().unwrap().signal = ProcessSignal::None;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue