From 3222693c357f6ea15911a74efa9c5a6c51edfbef Mon Sep 17 00:00:00 2001 From: hinto-janaiyo <hinto.janaiyo@protonmail.com> Date: Mon, 28 Nov 2022 12:05:09 -0500 Subject: [PATCH] command: implement basic data structures, functions This adds the basic wireframe of how processes will be handled. The data/funcs in [command.rs] will be the API the main GUI thread uses to talk to child processes. The process thread will loop every 1 second to read/write the necessary data (stdout, stdin), and handle signals from the GUI thread (kill, restart, etc). --- src/command.rs | 111 +++++++++++++++++++++++++++++++++++++++++++++++ src/constants.rs | 3 ++ src/main.rs | 1 + src/p2pool.rs | 3 +- 4 files changed, 117 insertions(+), 1 deletion(-) diff --git a/src/command.rs b/src/command.rs index 7e46dde..ca271a9 100644 --- a/src/command.rs +++ b/src/command.rs @@ -14,3 +14,114 @@ // // You should have received a copy of the GNU General Public License // along with this program. If not, see <https://www.gnu.org/licenses/>. + +// This file handles all things related to child processes (P2Pool/XMRig). +// The main GUI thread will interface with the [Arc<Mutex<...>>] data found +// here, e.g: User clicks [Start P2Pool] -> Init p2pool thread here. + +//---------------------------------------------------------------------------------------------------- Import +use std::{ + sync::{Arc,Mutex}, + path::PathBuf, + process::Command, + thread, +}; +use crate::constants::*; +use log::*; + +//---------------------------------------------------------------------------------------------------- [Process] Struct +// This holds all the state of a (child) process. +// The actual process thread runs in a 1 second loop, reading/writing to this struct. +// The main GUI thread will use this to display console text, online state, etc. +pub struct Process { + name: ProcessName, // P2Pool or XMRig? + online: bool, // Is the process alive? + args: String, // A single [String] containing the arguments + path: PathBuf, // The absolute path to the process binary + signal: ProcessSignal, // Did the user click [Stop/Restart]? + output: String, // This is the process's stdout + stderr + // STDIN Problem: + // - User can input many many commands in 1 second + // - The process loop only processes every 1 second + // - If there is only 1 [String] holding the user input, + // the user could overwrite their last input before + // the loop even has a chance to process their last command + // STDIN Solution: + // - When the user inputs something, push it to a [Vec] + // - In the process loop, loop over every [Vec] element and + // send each one individually to the process stdin + input: Vec<String>, +} + +//---------------------------------------------------------------------------------------------------- [Process] Impl +impl Process { + pub fn new(name: ProcessName, args: String, path: PathBuf) -> Self { + Self { + name, + online: false, + args, + path, + signal: ProcessSignal::None, + output: String::new(), + input: vec![String::new()], + } + } + + // Borrow a [&str], return an owned split collection + pub fn parse_args(args: &str) -> Vec<String> { + args.split_whitespace().map(|s| s.to_owned()).collect() + } + + pub fn spawn(process: &Arc<Mutex<Self>>, name: ProcessName) { + // Setup + let process = Arc::clone(process); + let args = Self::parse_args(&process.lock().unwrap().args); + info!("{} | Spawning initial thread", name); + info!("{} | Arguments: {:?}", name, args); + + // Spawn thread + thread::spawn(move || { + // Create & spawn child + let mut child = Command::new(&process.lock().unwrap().path) + .args(args) + .stdout(std::process::Stdio::piped()) + .spawn().unwrap(); + + // 1-second loop, reading and writing data to relevent struct + loop { + let process = process.lock().unwrap(); // Get lock + // If user sent a signal, handle it + match process.signal { + ProcessSignal::None => {}, + _ => { child.kill(); break; }, + }; +// println!("{:?}", String::from_utf8(child.wait_with_output().unwrap().stdout).unwrap()); + thread::sleep(SECOND); + } + + // End of thread, must mean process is offline + process.lock().unwrap().online = false; + }); + } +} + +//---------------------------------------------------------------------------------------------------- [ProcessSignal] Enum +#[derive(Copy,Clone,Eq,PartialEq,Debug)] +pub enum ProcessSignal { + None, + Stop, + Restart, +} + +//---------------------------------------------------------------------------------------------------- [ProcessName] Enum +#[derive(Copy,Clone,Eq,PartialEq,Debug)] +pub enum ProcessName { + P2Pool, + XMRig, +} + +impl std::fmt::Display for ProcessName { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{:#?}", self) + } +} diff --git a/src/constants.rs b/src/constants.rs index 62c4c26..3b41597 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -52,6 +52,9 @@ pub const YELLOW: egui::Color32 = egui::Color32::from_rgb(230, 230, 100); pub const LIGHT_GRAY: egui::Color32 = egui::Color32::LIGHT_GRAY; pub const BLACK: egui::Color32 = egui::Color32::BLACK; +// [Duration] constants +pub const SECOND: std::time::Duration = std::time::Duration::from_secs(1); + // OS specific #[cfg(target_os = "windows")] pub const OS: &'static str = " Windows"; diff --git a/src/main.rs b/src/main.rs index ec28943..45864d5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -60,6 +60,7 @@ mod gupax; mod p2pool; mod xmrig; mod update; +mod command; use {ferris::*,constants::*,node::*,disk::*,status::*,update::*,gupax::*}; //---------------------------------------------------------------------------------------------------- Struct + Impl diff --git a/src/p2pool.rs b/src/p2pool.rs index cdc758c..ab7f5e9 100644 --- a/src/p2pool.rs +++ b/src/p2pool.rs @@ -19,7 +19,8 @@ use crate::{ Regexes, constants::*, disk::*, - node::* + node::*, + command::*, }; use egui::{ TextEdit,SelectableLabel,ComboBox,Label,Button,