helper: copy p2pool functions to xmrig (todo!)

This commit is contained in:
hinto-janaiyo 2022-12-07 20:50:14 -05:00
parent 82918d4106
commit 8281d97bc3
No known key found for this signature in database
GPG key ID: B1C5A64B80691E45
2 changed files with 264 additions and 35 deletions

View file

@ -506,47 +506,276 @@ impl Helper {
info!("P2Pool | Watchdog thread exiting... Goodbye!");
}
//---------------------------------------------------------------------------------------------------- XMRig specific
// Intermediate function that parses the arguments, and spawns the XMRig watchdog thread.
pub fn spawn_xmrig(helper: &Arc<Mutex<Self>>, state: &crate::disk::Xmrig, path: std::path::PathBuf) {
let mut args = Vec::with_capacity(500);
if state.simple {
let rig_name = if state.simple_rig.is_empty() { GUPAX_VERSION.to_string() } else { state.simple_rig.clone() }; // Rig name
args.push(format!("--threads {}", state.current_threads)); // Threads
args.push(format!("--user {}", state.simple_rig)); // Rig name
args.push(format!("--url 127.0.0.1:3333")); // Local P2Pool (the default)
args.push("--no-color".to_string()); // No color escape codes
if state.pause != 0 { args.push(format!("--pause-on-active {}", state.pause)); } // Pause on active
} else {
if !state.arguments.is_empty() {
for arg in state.arguments.split_whitespace() {
args.push(arg.to_string());
}
} else {
args.push(format!("--user {}", state.address.clone())); // Wallet
args.push(format!("--threads {}", state.current_threads)); // Threads
args.push(format!("--rig-id {}", state.selected_rig)); // Rig ID
args.push(format!("--url {}:{}", state.selected_ip.clone(), state.selected_port.clone())); // IP/Port
args.push(format!("--http-host {}", state.api_ip).to_string()); // HTTP API IP
args.push(format!("--http-port {}", state.api_port).to_string()); // HTTP API Port
args.push("--no-color".to_string()); // No color escape codes
if state.tls { args.push("--tls".to_string()); } // TLS
if state.keepalive { args.push("--keepalive".to_string()); } // Keepalive
if state.pause != 0 { args.push(format!("--pause-on-active {}", state.pause)); } // Pause on active
}
}
// Print arguments to console
crate::disk::print_dash(&format!("XMRig | Launch arguments ... {:#?}", args));
//---------------------------------------------------------------------------------------------------- XMRig specific, most functions are very similar to P2Pool's
// // Read P2Pool's API file.
// fn read_p2pool_api(path: &std::path::PathBuf) -> Result<String, std::io::Error> {
// match std::fs::read_to_string(path) {
// Ok(s) => Ok(s),
// Err(e) => { warn!("P2Pool API | [{}] read error: {}", path.display(), e); Err(e) },
// }
// }
//
// // Deserialize the above [String] into a [PrivP2poolApi]
// fn str_to_priv_p2pool_api(string: &str) -> Result<PrivP2poolApi, serde_json::Error> {
// match serde_json::from_str::<PrivP2poolApi>(string) {
// Ok(a) => Ok(a),
// Err(e) => { warn!("P2Pool API | Could not deserialize API data: {}", e); Err(e) },
// }
// }
//
// // Just sets some signals for the watchdog thread to pick up on.
// pub fn stop_p2pool(helper: &Arc<Mutex<Self>>) {
// info!("P2Pool | Attempting to stop...");
// helper.lock().unwrap().p2pool.lock().unwrap().signal = ProcessSignal::Stop;
// helper.lock().unwrap().p2pool.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<Mutex<Self>>, 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");
// }
pub fn start_xmrig(helper: &Arc<Mutex<Self>>, state: &crate::disk::Xmrig, path: &std::path::PathBuf) {
helper.lock().unwrap().xmrig.lock().unwrap().state = ProcessState::Middle;
let args = Self::build_xmrig_args_and_mutate_img(helper, state, path);
// Print arguments & user settings to console
crate::disk::print_dash(&format!("XMRig | Launch arguments: {:#?}", args));
// Spawn watchdog thread
let process = Arc::clone(&helper.lock().unwrap().xmrig);
let gui_api = Arc::clone(&helper.lock().unwrap().gui_api_xmrig);
let pub_api = Arc::clone(&helper.lock().unwrap().pub_api_xmrig);
let priv_api = Arc::clone(&helper.lock().unwrap().priv_api_xmrig);
let path = path.clone();
thread::spawn(move || {
Self::spawn_xmrig_watchdog(args);
Self::spawn_xmrig_watchdog(process, gui_api, pub_api, priv_api, args, path);
});
}
// The actual XMRig watchdog tokio runtime.
// Takes in some [State/P2pool] and parses it to build the actual command arguments.
// Returns the [Vec] of actual arguments, and mutates the [ImgP2pool] for the main GUI thread
// It returns a value... and mutates a deeply nested passed argument... this is some pretty bad code...
pub fn build_xmrig_args_and_mutate_img(helper: &Arc<Mutex<Self>>, state: &crate::disk::P2pool, path: &std::path::PathBuf) -> Vec<String> {
let mut args = Vec::with_capacity(500);
let path = path.clone();
let mut api_path = path.clone();
api_path.pop();
// [Simple]
if state.simple {
// Build the xmrig argument
let (ip, rpc, zmq) = crate::node::enum_to_ip_rpc_zmq_tuple(state.node); // Get: (IP, RPC, ZMQ)
args.push("--wallet".to_string()); args.push(state.address.clone()); // Wallet address
args.push("--host".to_string()); args.push(ip.to_string()); // IP Address
args.push("--rpc-port".to_string()); args.push(rpc.to_string()); // RPC Port
args.push("--zmq-port".to_string()); args.push(zmq.to_string()); // ZMQ Port
args.push("--data-api".to_string()); args.push(api_path.display().to_string()); // API Path
args.push("--local-api".to_string()); // Enable API
args.push("--no-color".to_string()); // Remove color escape sequences, Gupax terminal can't parse it :(
args.push("--mini".to_string()); // P2Pool Mini
*helper.lock().unwrap().img_xmrig.lock().unwrap() = ImgP2pool {
mini: true,
address: state.address.clone(),
host: ip.to_string(),
rpc: rpc.to_string(),
zmq: zmq.to_string(),
log_level: "3".to_string(),
out_peers: "10".to_string(),
in_peers: "10".to_string(),
};
// [Advanced]
} else {
// Overriding command arguments
if !state.arguments.is_empty() {
// This parses the input and attemps to fill out
// the [ImgP2pool]... This is pretty bad code...
let mut last = "";
let lock = helper.lock().unwrap();
let mut xmrig_image = lock.img_xmrig.lock().unwrap();
for arg in state.arguments.split_whitespace() {
match last {
"--mini" => xmrig_image.mini = true,
"--wallet" => xmrig_image.address = arg.to_string(),
"--host" => xmrig_image.host = arg.to_string(),
"--rpc-port" => xmrig_image.rpc = arg.to_string(),
"--zmq-port" => xmrig_image.zmq = arg.to_string(),
"--loglevel" => xmrig_image.log_level = arg.to_string(),
"--out-peers" => xmrig_image.out_peers = arg.to_string(),
"--in-peers" => xmrig_image.in_peers = arg.to_string(),
_ => (),
}
args.push(arg.to_string());
last = arg;
}
// Else, build the argument
} else {
args.push("--wallet".to_string()); args.push(state.address.clone()); // Wallet
args.push("--host".to_string()); args.push(state.selected_ip.to_string()); // IP
args.push("--rpc-port".to_string()); args.push(state.selected_rpc.to_string()); // RPC
args.push("--zmq-port".to_string()); args.push(state.selected_zmq.to_string()); // ZMQ
args.push("--loglevel".to_string()); args.push(state.log_level.to_string()); // Log Level
args.push("--out-peers".to_string()); args.push(state.out_peers.to_string()); // Out Peers
args.push("--in-peers".to_string()); args.push(state.in_peers.to_string()); // In Peers
args.push("--data-api".to_string()); args.push(api_path.display().to_string()); // API Path
args.push("--local-api".to_string()); // Enable API
args.push("--no-color".to_string()); // Remove color escape sequences
if state.mini { args.push("--mini".to_string()); }; // Mini
*helper.lock().unwrap().img_xmrig.lock().unwrap() = ImgP2pool {
mini: state.mini,
address: state.address.clone(),
host: state.selected_ip.to_string(),
rpc: state.selected_rpc.to_string(),
zmq: state.selected_zmq.to_string(),
log_level: state.log_level.to_string(),
out_peers: state.out_peers.to_string(),
in_peers: state.in_peers.to_string(),
}
}
}
args
}
// 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]
pub async fn spawn_xmrig_watchdog(args: Vec<String>) {
async fn spawn_xmrig_watchdog(process: Arc<Mutex<Process>>, gui_api: Arc<Mutex<PubXmrigApi>>, pub_api: Arc<Mutex<PubXmrigApi>>, priv_api: Arc<Mutex<PrivXmrigApi>>, args: Vec<String>, mut path: std::path::PathBuf) {
// 1a. Create PTY
let pty = portable_pty::native_pty_system();
let pair = pty.openpty(portable_pty::PtySize {
rows: 24,
cols: 80,
pixel_width: 0,
pixel_height: 0,
}).unwrap();
// 1b. Create command
let mut cmd = portable_pty::CommandBuilder::new(path.as_path());
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()));
// 2. Set process state
let mut lock = process.lock().unwrap();
lock.state = ProcessState::Alive;
lock.signal = ProcessSignal::None;
lock.start = Instant::now();
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);
drop(lock);
// 3. Spawn PTY read thread
let output_clone = Arc::clone(&process.lock().unwrap().output);
thread::spawn(move || {
Self::read_pty(output_clone, reader);
});
// path.pop();
// path.push(P2POOL_API_PATH);
// let regex = P2poolRegex::new();
let output = Arc::clone(&process.lock().unwrap().output);
let start = process.lock().unwrap().start;
// 4. Loop as watchdog
loop {
// Set timer
let now = Instant::now();
// Check SIGNAL
if process.lock().unwrap().signal == ProcessSignal::Stop {
child_pty.lock().unwrap().kill();
let exit_status = match child_pty.lock().unwrap().wait() {
Ok(e) => {
if e.success() {
process.lock().unwrap().state = ProcessState::Dead; "Successful"
} else {
process.lock().unwrap().state = ProcessState::Failed; "Failed"
}
},
_ => { process.lock().unwrap().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", HORI_CONSOLE, uptime, exit_status, HORI_CONSOLE);
process.lock().unwrap().signal = ProcessSignal::None;
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", HORI_CONSOLE, uptime, exit_status, HORI_CONSOLE);
process.lock().unwrap().state = ProcessState::Waiting;
break
// Check if the process is 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", HORI_CONSOLE, uptime, exit_status, HORI_CONSOLE);
process.lock().unwrap().signal = ProcessSignal::None;
break
}
// Check vector of user input
let mut lock = process.lock().unwrap();
if !lock.input.is_empty() {
let input = std::mem::take(&mut lock.input);
for line in input {
writeln!(lock.stdin.as_mut().unwrap(), "{}", line);
}
}
drop(lock);
// Always update from output
// PubP2poolApi::update_from_output(&pub_api, &output, start.elapsed(), &regex);
//
// // Read API file into string
// if let Ok(string) = Self::read_xmrig_api(&path) {
// // Deserialize
// if let Ok(s) = Self::str_to_priv_xmrig_api(&string) {
// // Update the structs.
// PubP2poolApi::update_from_priv(&pub_api, &priv_api);
// }
// }
// Check if logs need resetting
Self::check_reset_output(&output, ProcessName::Xmrig);
// Sleep (only if 900ms hasn't passed)
let elapsed = now.elapsed().as_millis();
// Since logic goes off if less than 1000, casting should be safe
if elapsed < 900 { std::thread::sleep(std::time::Duration::from_millis((900-elapsed) as u64)); }
}
// 5. If loop broke, we must be done here.
info!("XMRig | Watchdog thread exiting... Goodbye!");
}
//---------------------------------------------------------------------------------------------------- The "helper"

View file

@ -936,7 +936,7 @@ impl eframe::App for App {
if esc || ui.add_sized([width, height/2.0], Button::new("No")).clicked() { self.error_state.reset() }
},
ErrorButtons::Sudo => {
let sudo_width = (width/10.0);
let sudo_width = width/10.0;
let height = ui.available_height()/4.0;
let mut sudo = self.sudo.lock().unwrap();
let hide = sudo.hide.clone();