From a3802ef4f71a8b9ac9a397304e280831bffd0eaa Mon Sep 17 00:00:00 2001 From: hinto-janaiyo Date: Thu, 8 Dec 2022 18:31:20 -0500 Subject: [PATCH] helper: start xmrig with sudo, implement [restart_xmrig()] This commit takes care of correctly starting [sudo] with XMRig as a command argument. The frontend [restart_*] function is also implemented. In XMRig's case, the threads will sleep [3/5 seconds] before [starting/restarting] so that [sudo] has time to open its STDIN. This prevents premature inputs and outputting the password to the STDOUT. --- src/gupax.rs | 2 +- src/helper.rs | 111 ++++++++++++++++++++++++++++++-------------------- src/main.rs | 8 ++-- src/sudo.rs | 21 ++++++---- 4 files changed, 84 insertions(+), 58 deletions(-) diff --git a/src/gupax.rs b/src/gupax.rs index 75b2b27..a20a4d5 100644 --- a/src/gupax.rs +++ b/src/gupax.rs @@ -232,7 +232,7 @@ impl Gupax { // Saved [Tab] ui.group(|ui| { let height = ui.available_height()/2.0; - let width = (width/5.0)-(SPACE*1.8); + let width = (width/5.0)-(SPACE*1.93); ui.horizontal(|ui| { if ui.add_sized([width, height], SelectableLabel::new(self.tab == Tab::About, "About")).on_hover_text(GUPAX_TAB_ABOUT).clicked() { self.tab = Tab::About; } ui.separator(); diff --git a/src/helper.rs b/src/helper.rs index 8fe76d1..7e5f83e 100644 --- a/src/helper.rs +++ b/src/helper.rs @@ -43,8 +43,11 @@ use std::{ time::*, thread, }; +use crate::{ + constants::*, + SudoState, +}; use serde::{Serialize,Deserialize}; -use crate::constants::*; use num_format::{Buffer,Locale}; use log::*; @@ -558,29 +561,32 @@ impl Helper { helper.lock().unwrap().xmrig.lock().unwrap().state = ProcessState::Middle; } -// // The "restart frontend" to a "frontend" function. -// // Basically calls to kill the current p2pool, waits a little, then starts the below function in a a new thread, then exit. -// pub fn restart_p2pool(helper: &Arc>, state: &crate::disk::P2pool, path: &std::path::PathBuf) { -// info!("P2Pool | Attempting to restart..."); -// helper.lock().unwrap().p2pool.lock().unwrap().signal = ProcessSignal::Restart; -// helper.lock().unwrap().p2pool.lock().unwrap().state = ProcessState::Middle; -// -// let helper = Arc::clone(&helper); -// let state = state.clone(); -// let path = path.clone(); -// // This thread lives to wait, start p2pool then die. -// thread::spawn(move || { -// while helper.lock().unwrap().p2pool.lock().unwrap().is_alive() { -// warn!("P2Pool | Want to restart but process is still alive, waiting..."); -// thread::sleep(SECOND); -// } -// // Ok, process is not alive, start the new one! -// Self::start_p2pool(&helper, &state, &path); -// }); -// info!("P2Pool | Restart ... OK"); -// } + // The "restart frontend" to a "frontend" function. + // Basically calls to kill the current xmrig, waits a little, then starts the below function in a a new thread, then exit. + pub fn restart_xmrig(helper: &Arc>, state: &crate::disk::Xmrig, path: &std::path::PathBuf, sudo: Arc>) { + info!("XMRig | Attempting to restart..."); + helper.lock().unwrap().xmrig.lock().unwrap().signal = ProcessSignal::Restart; + helper.lock().unwrap().xmrig.lock().unwrap().state = ProcessState::Middle; - pub fn start_xmrig(helper: &Arc>, state: &crate::disk::Xmrig, path: &std::path::PathBuf) { + let helper = Arc::clone(&helper); + let state = state.clone(); + let path = path.clone(); + // This thread lives to wait, start xmrig then die. + thread::spawn(move || { + while helper.lock().unwrap().xmrig.lock().unwrap().is_alive() { + warn!("XMRig | Want to restart but process is still alive, waiting..."); + 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! + Self::start_xmrig(&helper, &state, &path, sudo); + }); + info!("XMRig | Restart ... OK"); + } + + pub fn start_xmrig(helper: &Arc>, state: &crate::disk::Xmrig, path: &std::path::PathBuf, sudo: Arc>) { helper.lock().unwrap().xmrig.lock().unwrap().state = ProcessState::Middle; let args = Self::build_xmrig_args_and_mutate_img(helper, state, path); @@ -596,7 +602,7 @@ impl Helper { let priv_api = Arc::clone(&helper.lock().unwrap().priv_api_xmrig); let path = path.clone(); thread::spawn(move || { - Self::spawn_xmrig_watchdog(process, gui_api, pub_api, priv_api, args, path); + Self::spawn_xmrig_watchdog(process, gui_api, pub_api, priv_api, args, path, sudo); }); } @@ -606,6 +612,12 @@ impl Helper { pub fn build_xmrig_args_and_mutate_img(helper: &Arc>, state: &crate::disk::Xmrig, path: &std::path::PathBuf) -> Vec { let mut args = Vec::with_capacity(500); let path = path.clone(); + // The actual binary we're executing is [sudo], technically + // the XMRig path is just an argument to sudo, so add it. + // Before that though, add the ["--prompt"] flag and set it + // to emptyness so that it doesn't show up in the output. + args.push(r#"--prompt="#.to_string()); + args.push(path.display().to_string()); // [Simple] if state.simple { @@ -666,7 +678,7 @@ impl Helper { // The P2Pool watchdog. Spawns 1 OS thread for reading a PTY (STDOUT+STDERR), and combines the [Child] with a PTY so STDIN actually works. #[tokio::main] - async fn spawn_xmrig_watchdog(process: Arc>, gui_api: Arc>, pub_api: Arc>, priv_api: Arc>, args: Vec, mut path: std::path::PathBuf) { + async fn spawn_xmrig_watchdog(process: Arc>, gui_api: Arc>, pub_api: Arc>, priv_api: Arc>, args: Vec, mut path: std::path::PathBuf, sudo: Arc>) { // 1a. Create PTY let pty = portable_pty::native_pty_system(); let pair = pty.openpty(portable_pty::PtySize { @@ -676,12 +688,15 @@ impl Helper { pixel_height: 0, }).unwrap(); // 1b. Create command - let mut cmd = portable_pty::CommandBuilder::new(path.as_path()); + let mut cmd = portable_pty::CommandBuilder::new("sudo"); cmd.args(args); cmd.cwd(path.as_path().parent().unwrap()); // 1c. Create child let child_pty = Arc::new(Mutex::new(pair.slave.spawn_command(cmd).unwrap())); + // 1d. Wait a bit for [sudo]. + thread::sleep(std::time::Duration::from_secs(3)); + // 2. Set process state let mut lock = process.lock().unwrap(); lock.state = ProcessState::Alive; @@ -690,6 +705,11 @@ impl Helper { lock.child = Some(Arc::clone(&child_pty)); let reader = pair.master.try_clone_reader().unwrap(); // Get STDOUT/STDERR before moving the PTY lock.stdin = Some(pair.master); + + // 3. Input [sudo] pass, wipe, then drop. + writeln!(lock.stdin.as_mut().unwrap(), "{}", sudo.lock().unwrap().pass); + SudoState::wipe(&sudo); + drop(sudo); drop(lock); // 3. Spawn PTY read thread @@ -712,37 +732,38 @@ impl Helper { // Set timer let now = Instant::now(); - // Check SIGNAL - if process.lock().unwrap().signal == ProcessSignal::Stop { + // Stop on [Stop/Restart] SIGNAL + let signal = process.lock().unwrap().signal; + if signal == ProcessSignal::Stop || signal == ProcessSignal::Restart { child_pty.lock().unwrap().kill(); let exit_status = match child_pty.lock().unwrap().wait() { Ok(e) => { + let mut process = process.lock().unwrap(); if e.success() { - process.lock().unwrap().state = ProcessState::Dead; "Successful" + if process.signal == ProcessSignal::Stop { process.state = ProcessState::Dead; } + "Successful" } else { - process.lock().unwrap().state = ProcessState::Failed; "Failed" + if process.signal == ProcessSignal::Stop { process.state = ProcessState::Failed; } + "Failed" } }, - _ => { process.lock().unwrap().state = ProcessState::Failed; "Unknown Error" }, + _ => { + let mut process = process.lock().unwrap(); + if process.signal == ProcessSignal::Stop { process.state = ProcessState::Failed; } + "Unknown Error" + }, }; 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; + let mut process = process.lock().unwrap(); + match process.signal { + ProcessSignal::Stop => process.signal = ProcessSignal::None, + ProcessSignal::Restart => process.state = ProcessState::Waiting, + _ => (), + } break - // Check RESTART - } else if process.lock().unwrap().signal == ProcessSignal::Restart { - child_pty.lock().unwrap().kill(); - let exit_status = match child_pty.lock().unwrap().wait() { - Ok(e) => if e.success() { "Successful" } else { "Failed" }, - _ => "Unknown Error", - }; - 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().state = ProcessState::Waiting; - break - // Check if the process is secretly died without us knowing :) + // 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" }, diff --git a/src/main.rs b/src/main.rs index 3ebe5fd..1d9a9cb 100644 --- a/src/main.rs +++ b/src/main.rs @@ -965,7 +965,7 @@ impl eframe::App for App { if ui.add_sized([box_width, height], Button::new(RichText::new("👁").color(color))).on_hover_text(PASSWORD_HIDE).clicked() { sudo.hide = !sudo.hide; } }); if esc || ui.add_sized([width, height*4.0], Button::new("Leave")).clicked() { self.error_state.reset(); }; - // If [test_sudo()] finished, reset sudo state + error state. + // If [test_sudo()] finished, reset error state. if sudo.success { self.error_state.reset(); } @@ -1164,8 +1164,8 @@ impl eframe::App for App { }); } else if self.xmrig.lock().unwrap().is_alive() { if ui.add_sized([width, height], Button::new("⟲")).on_hover_text("Restart XMRig").clicked() { -// self.error_state.ask_sudo(&self.sudo); -// Helper::restart_xmrig(&self.helper, &self.state.xmrig, &self.state.gupax.absolute_xmrig_path); + 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() { Helper::stop_xmrig(&self.helper); @@ -1179,8 +1179,8 @@ impl eframe::App for App { ui.add_sized([width, height], Button::new("⏹")).on_hover_text("Stop XMRig"); }); if ui.add_sized([width, height], Button::new("⏺")).on_hover_text("Start XMRig").clicked() { + self.sudo.lock().unwrap().signal = ProcessSignal::Start; self.error_state.ask_sudo(&self.sudo); -// Helper::start_xmrig(&self.helper, &self.state.xmrig, &self.state.gupax.absolute_xmrig_path); } } }); diff --git a/src/sudo.rs b/src/sudo.rs index 5eb7f68..235a02c 100644 --- a/src/sudo.rs +++ b/src/sudo.rs @@ -42,6 +42,7 @@ pub struct SudoState { pub hide: bool, // Are we hiding the password? pub msg: String, // The message shown to the user if unsuccessful pub pass: String, // The actual password wrapped in a [SecretVec] + pub signal: ProcessSignal, // Main GUI will set this depending on if we want [Start] or [Restart] } impl SudoState { @@ -52,6 +53,7 @@ impl SudoState { hide: true, msg: "".to_string(), pass: String::with_capacity(256), + signal: ProcessSignal::None, } } @@ -61,15 +63,15 @@ impl SudoState { let mut state = state.lock().unwrap(); state.testing = false; state.success = false; + state.signal = ProcessSignal::None; } // Swaps the pass with another 256-capacity String, // zeroizes the old and drops it. pub fn wipe(state: &Arc>) { let mut new = String::with_capacity(256); - let mut state = state.lock().unwrap(); // new is now == old, and vice-versa. - std::mem::swap(&mut new, &mut state.pass); + std::mem::swap(&mut new, &mut state.lock().unwrap().pass); // we're wiping & dropping the old pass here. new.zeroize(); std::mem::drop(new); @@ -127,7 +129,6 @@ impl SudoState { Ok(Some(code)) => if code.success() { info!("Sudo | Password ... OK!"); state.lock().unwrap().success = true; - crate::helper::Helper::start_xmrig(&helper, &xmrig, &path); //----------- Start XMRig break }, Ok(None) => { @@ -143,12 +144,16 @@ impl SudoState { }, } } - let mut lock = state.lock().unwrap(); - if !lock.success { lock.msg = "Incorrect password!".to_string(); } - drop(lock); sudo.kill(); - Self::wipe(&state); - state.lock().unwrap().testing = false; + if state.lock().unwrap().success { + match state.lock().unwrap().signal { + ProcessSignal::Restart => crate::helper::Helper::restart_xmrig(&helper, &xmrig, &path, Arc::clone(&state)), + _ => crate::helper::Helper::start_xmrig(&helper, &xmrig, &path, Arc::clone(&state)), + } + } else { + state.lock().unwrap().msg = "Incorrect password!".to_string(); + Self::wipe(&state); + } }); } }