diff --git a/src/disk.rs b/src/disk.rs index b266304..264742e 100644 --- a/src/disk.rs +++ b/src/disk.rs @@ -59,16 +59,19 @@ pub fn get_gupax_data_path() -> Result<PathBuf, TomlError> { Some(mut path) => { path.push(DIRECTORY); info!("OS | Data path ... OK ... {}", path.display()); + create_gupax_dir(&path)?; Ok(path) }, None => { error!("OS | Data path ... FAIL"); Err(TomlError::Path(PATH_ERROR.to_string())) }, } } -pub fn create_gupax_dir(path: PathBuf) -> Result<(), TomlError> { +pub fn create_gupax_dir(path: &PathBuf) -> Result<(), TomlError> { // Create directory - fs::create_dir_all(&path)?; - Ok(()) + match fs::create_dir_all(path) { + Ok(_) => { info!("OS | Create data path ... OK"); Ok(()) }, + Err(e) => { error!("OS | Create data path ... FAIL ... {}", e); Err(TomlError::Io(e)) }, + } } // Convert a [File] path to a [String] diff --git a/src/gupax.rs b/src/gupax.rs index 779e2cb..22513bb 100644 --- a/src/gupax.rs +++ b/src/gupax.rs @@ -71,34 +71,7 @@ impl Gupax { ui.vertical(|ui| { ui.set_enabled(!updating); if ui.add_sized([width, height], egui::Button::new("Check for updates")).on_hover_text(GUPAX_UPDATE).clicked() { - update.lock().unwrap().path_p2pool = og.lock().unwrap().gupax.absolute_p2pool_path.display().to_string(); - update.lock().unwrap().path_xmrig = og.lock().unwrap().gupax.absolute_xmrig_path.display().to_string(); - update.lock().unwrap().tor = og.lock().unwrap().gupax.update_via_tor; - let og = Arc::clone(&og); - let state_ver = Arc::clone(&state_ver); - let update = Arc::clone(&update); - let update_thread = Arc::clone(&update); - let state_path = state_path.clone(); - thread::spawn(move|| { - info!("Spawning update thread..."); - match Update::start(update_thread, og.clone(), state_ver.clone()) { - Err(e) => { - info!("Update ... FAIL ... {}", e); - *update.lock().unwrap().msg.lock().unwrap() = format!("{} | {}", MSG_FAILED, e); - }, - _ => { - info!("Update | Saving state..."); - match State::save(&mut og.lock().unwrap(), &state_path) { - Ok(_) => info!("Update ... OK"), - Err(e) => { - warn!("Update | Saving state ... FAIL ... {}", e); - *update.lock().unwrap().msg.lock().unwrap() = format!("Saving new versions into state failed"); - }, - }; - } - }; - *update.lock().unwrap().updating.lock().unwrap() = false; - }); + Update::spawn_thread(og, update, state_ver, state_path); } }); ui.vertical(|ui| { diff --git a/src/main.rs b/src/main.rs index 7243b91..463832e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -440,6 +440,7 @@ fn init_options() -> NativeOptions { } fn init_auto(app: &App) { + // Return early if [--no-startup] was not passed if app.no_startup { info!("[--no-startup] flag passed, skipping init_auto()..."); return @@ -449,39 +450,10 @@ fn init_auto(app: &App) { } else { info!("Starting init_auto()..."); } + // [Auto-Update] if app.state.gupax.auto_update { - let path_p2pool = app.og.lock().unwrap().gupax.absolute_p2pool_path.display().to_string(); - let path_xmrig = app.og.lock().unwrap().gupax.absolute_xmrig_path.display().to_string(); - let tor = app.og.lock().unwrap().gupax.update_via_tor; - app.update.lock().unwrap().path_p2pool = path_p2pool; - app.update.lock().unwrap().path_xmrig = path_xmrig; - app.update.lock().unwrap().tor = tor; - let og = Arc::clone(&app.og); - let state_ver = Arc::clone(&app.state.version); - let update = Arc::clone(&app.update); - let update_thread = Arc::clone(&app.update); - let state_path = app.state_path.clone(); - thread::spawn(move|| { - info!("Spawning update thread..."); - match Update::start(update_thread, og.clone(), state_ver.clone()) { - Err(e) => { - info!("Update ... FAIL ... {}", e); - *update.lock().unwrap().msg.lock().unwrap() = format!("{} | {}", MSG_FAILED, e); - }, - _ => { - info!("Update | Saving state..."); - match State::save(&mut og.lock().unwrap(), &state_path) { - Ok(_) => info!("Update ... OK"), - Err(e) => { - warn!("Update | Saving state ... FAIL ... {}", e); - *update.lock().unwrap().msg.lock().unwrap() = format!("Saving new versions into state failed"); - }, - }; - } - }; - *update.lock().unwrap().updating.lock().unwrap() = false; - }); + Update::spawn_thread(&app.og, &app.update, &app.state.version, &app.state_path); } else { info!("Skipping auto-update..."); } @@ -490,12 +462,7 @@ fn init_auto(app: &App) { let auto_node = app.og.lock().unwrap().p2pool.auto_node; let simple = app.og.lock().unwrap().p2pool.simple; if auto_node && simple { - let ping = Arc::clone(&app.ping); - let og = Arc::clone(&app.og); - thread::spawn(move|| { - info!("Spawning ping thread..."); - crate::node::ping(ping, og); - }); + Ping::spawn_thread(&app.ping, &app.og) } else { info!("Skipping auto-ping..."); } diff --git a/src/node.rs b/src/node.rs index bf447e0..0ab2132 100644 --- a/src/node.rs +++ b/src/node.rs @@ -90,32 +90,6 @@ impl NodeData { } } -//---------------------------------------------------------------------------------------------------- Ping data -#[derive(Debug)] -pub struct Ping { - pub nodes: Vec<NodeData>, - pub fastest: NodeEnum, - pub pinging: bool, - pub msg: String, - pub prog: f32, - pub pinged: bool, - pub auto_selected: bool, -} - -impl Ping { - pub fn new() -> Self { - Self { - nodes: NodeData::new_vec(), - fastest: NodeEnum::C3pool, - pinging: false, - msg: "No ping in progress".to_string(), - prog: 0.0, - pinged: false, - auto_selected: true, - } - } -} - //---------------------------------------------------------------------------------------------------- IP <-> Enum functions use crate::NodeEnum::*; // Function for returning IP/Enum @@ -191,110 +165,154 @@ pub fn format_enum(id: NodeEnum) -> String { } } -//---------------------------------------------------------------------------------------------------- Main Ping function -// This is for pinging the community nodes to -// find the fastest/slowest one for the user. -// The process: -// - Send [get_info] JSON-RPC request over HTTP to all IPs -// - Measure each request in milliseconds -// - Timeout on requests over 5 seconds -// - Add data to appropriate struct -// - Sorting fastest to lowest is automatic (fastest nodes return ... the fastest) -// -// This used to be done 3x linearly but after testing, sending a single -// JSON-RPC call to all IPs asynchronously resulted in the same data. -// -// <300ms = GREEN -// <1000ms = YELLOW -// >1000ms = RED -// timeout = BLACK -// default = GRAY -#[tokio::main] -pub async fn ping(ping: Arc<Mutex<Ping>>, og: Arc<Mutex<State>>) -> Result<(), anyhow::Error> { - // Timer - let now = Instant::now(); +//---------------------------------------------------------------------------------------------------- Ping data +#[derive(Debug)] +pub struct Ping { + pub nodes: Vec<NodeData>, + pub fastest: NodeEnum, + pub pinging: bool, + pub msg: String, + pub prog: f32, + pub pinged: bool, + pub auto_selected: bool, +} - // Start ping - ping.lock().unwrap().pinging = true; - ping.lock().unwrap().prog = 0.0; - let percent = (100.0 / ((NODE_IPS.len()) as f32)).floor(); +impl Ping { + pub fn new() -> Self { + Self { + nodes: NodeData::new_vec(), + fastest: NodeEnum::C3pool, + pinging: false, + msg: "No ping in progress".to_string(), + prog: 0.0, + pinged: false, + auto_selected: true, + } + } - // Create HTTP client - let info = format!("{}", "Creating HTTP Client"); - ping.lock().unwrap().msg = info; - let client: hyper::client::Client<hyper::client::HttpConnector> = hyper::Client::builder() - .build(hyper::client::HttpConnector::new()); - - // Random User Agent - let rand_user_agent = crate::Pkg::get_user_agent(); - // Handle vector - let mut handles = vec![]; - let node_vec = Arc::new(Mutex::new(Vec::new())); - - for ip in NODE_IPS { - let client = client.clone(); + //---------------------------------------------------------------------------------------------------- Main Ping function + // Intermediate thread for spawning thread + pub fn spawn_thread(ping: &Arc<Mutex<Self>>, og: &Arc<Mutex<State>>) { let ping = Arc::clone(&ping); - let node_vec = Arc::clone(&node_vec); - let request = hyper::Request::builder() - .method("POST") - .uri("http://".to_string() + ip + "/json_rpc") - .header("User-Agent", rand_user_agent) - .body(hyper::Body::from(r#"{"jsonrpc":"2.0","id":"0","method":"get_info"}"#)) - .unwrap(); - let handle = tokio::spawn(async move { response(client, request, ip, ping, percent, node_vec).await }); - handles.push(handle); + let og = Arc::clone(og); + std::thread::spawn(move|| { + info!("Spawning ping thread..."); + match Self::ping(ping.clone(), og) { + Ok(_) => info!("Ping ... OK"), + Err(err) => { + error!("Ping ... FAIL ... {}", err); + ping.lock().unwrap().pinged = false; + ping.lock().unwrap().msg = err.to_string(); + }, + }; + ping.lock().unwrap().pinging = false; + }); } - for handle in handles { - handle.await?; - } - let node_vec = node_vec.lock().unwrap().clone(); + // This is for pinging the community nodes to + // find the fastest/slowest one for the user. + // The process: + // - Send [get_info] JSON-RPC request over HTTP to all IPs + // - Measure each request in milliseconds + // - Timeout on requests over 5 seconds + // - Add data to appropriate struct + // - Sorting fastest to lowest is automatic (fastest nodes return ... the fastest) + // + // This used to be done 3x linearly but after testing, sending a single + // JSON-RPC call to all IPs asynchronously resulted in the same data. + // + // <300ms = GREEN + // <1000ms = YELLOW + // >1000ms = RED + // timeout = BLACK + // default = GRAY + #[tokio::main] + pub async fn ping(ping: Arc<Mutex<Self>>, og: Arc<Mutex<State>>) -> Result<(), anyhow::Error> { + // Timer + let now = Instant::now(); - let info = format!("Fastest node: {}ms ... {} @ {}", node_vec[0].ms, node_vec[0].id, node_vec[0].ip); - info!("Ping | {}", info); - info!("Ping | Took [{}] seconds...", now.elapsed().as_secs_f32()); - let mut ping = ping.lock().unwrap(); - ping.fastest = node_vec[0].id; - ping.nodes = node_vec; - ping.prog = 100.0; + // Start ping + ping.lock().unwrap().pinging = true; + ping.lock().unwrap().prog = 0.0; + let percent = (100.0 / ((NODE_IPS.len()) as f32)).floor(); + + // Create HTTP client + let info = format!("{}", "Creating HTTP Client"); + ping.lock().unwrap().msg = info; + let client: hyper::client::Client<hyper::client::HttpConnector> = hyper::Client::builder() + .build(hyper::client::HttpConnector::new()); + + // Random User Agent + let rand_user_agent = crate::Pkg::get_user_agent(); + // Handle vector + let mut handles = vec![]; + let node_vec = Arc::new(Mutex::new(Vec::new())); + + for ip in NODE_IPS { + let client = client.clone(); + let ping = Arc::clone(&ping); + let node_vec = Arc::clone(&node_vec); + let request = hyper::Request::builder() + .method("POST") + .uri("http://".to_string() + ip + "/json_rpc") + .header("User-Agent", rand_user_agent) + .body(hyper::Body::from(r#"{"jsonrpc":"2.0","id":"0","method":"get_info"}"#)) + .unwrap(); + let handle = tokio::spawn(async move { Self::response(client, request, ip, ping, percent, node_vec).await }); + handles.push(handle); + } + + for handle in handles { + handle.await?; + } + let node_vec = node_vec.lock().unwrap().clone(); + + let info = format!("Fastest node: {}ms ... {} @ {}", node_vec[0].ms, node_vec[0].id, node_vec[0].ip); + info!("Ping | {}", info); + info!("Ping | Took [{}] seconds...", now.elapsed().as_secs_f32()); + let mut ping = ping.lock().unwrap(); + ping.fastest = node_vec[0].id; + ping.nodes = node_vec; + ping.prog = 100.0; + ping.msg = info; + ping.pinging = false; + ping.pinged = true; + ping.auto_selected = false; + drop(ping); + Ok(()) + } + + async fn response(client: hyper::client::Client<hyper::client::HttpConnector>, request: hyper::Request<hyper::Body>, ip: &'static str, ping: Arc<Mutex<Self>>, percent: f32, node_vec: Arc<Mutex<Vec<NodeData>>>) { + let id = ip_to_enum(ip); + let now = Instant::now(); + let ms; + let info; + match tokio::time::timeout(Duration::from_secs(5), client.request(request)).await { + Ok(_) => { + ms = now.elapsed().as_millis(); + info = format!("{}ms ... {}: {}", ms, id, ip); + info!("Ping | {}", info) + }, + Err(e) => { + ms = 5000; + info = format!("{}ms ... {}: {}", ms, id, ip); + warn!("Ping | {}", info) + }, + }; + let color; + if ms < 300 { + color = Color32::from_rgb(100, 230, 100); // GREEN + } else if ms < 1000 { + color = Color32::from_rgb(230, 230, 100); // YELLOW + } else if ms < 5000 { + color = Color32::from_rgb(230, 50, 50); // RED + } else { + color = Color32::BLACK; + } + let mut ping = ping.lock().unwrap(); ping.msg = info; - ping.pinging = false; - ping.pinged = true; - ping.auto_selected = false; + ping.prog += percent; drop(ping); - Ok(()) -} - -async fn response(client: hyper::client::Client<hyper::client::HttpConnector>, request: hyper::Request<hyper::Body>, ip: &'static str, ping: Arc<Mutex<Ping>>, percent: f32, node_vec: Arc<Mutex<Vec<NodeData>>>) { - let id = ip_to_enum(ip); - let now = Instant::now(); - let ms; - let info; - match tokio::time::timeout(Duration::from_secs(5), client.request(request)).await { - Ok(_) => { - ms = now.elapsed().as_millis(); - info = format!("{}ms ... {}: {}", ms, id, ip); - info!("Ping | {}", info) - }, - Err(e) => { - ms = 5000; - info = format!("{}ms ... {}: {}", ms, id, ip); - warn!("Ping | {}", info) - }, - }; - let color; - if ms < 300 { - color = Color32::from_rgb(100, 230, 100); // GREEN - } else if ms < 1000 { - color = Color32::from_rgb(230, 230, 100); // YELLOW - } else if ms < 5000 { - color = Color32::from_rgb(230, 50, 50); // RED - } else { - color = Color32::BLACK; + node_vec.lock().unwrap().push(NodeData { id: ip_to_enum(ip), ip, ms, color, }); } - let mut ping = ping.lock().unwrap(); - ping.msg = info; - ping.prog += percent; - drop(ping); - node_vec.lock().unwrap().push(NodeData { id: ip_to_enum(ip), ip, ms, color, }); } diff --git a/src/p2pool.rs b/src/p2pool.rs index 3134156..d122bce 100644 --- a/src/p2pool.rs +++ b/src/p2pool.rs @@ -141,21 +141,7 @@ impl P2pool { // [Ping Button] ui.set_enabled(!ping.lock().unwrap().pinging); if ui.add_sized([width, height], Button::new("Ping community nodes")).on_hover_text(P2POOL_PING).clicked() { - let ping1 = Arc::clone(&ping); - let ping2 = Arc::clone(&ping); - let og = Arc::clone(og); - thread::spawn(move|| { - info!("Spawning ping thread..."); - match crate::node::ping(ping1, og) { - Ok(_) => info!("Ping ... OK"), - Err(err) => { - error!("Ping ... FAIL ... {}", err); - ping2.lock().unwrap().pinged = false; - ping2.lock().unwrap().msg = err.to_string(); - }, - }; - ping2.lock().unwrap().pinging = false; - }); + Ping::spawn_thread(&ping, &og); }}); ui.vertical(|ui| { diff --git a/src/update.rs b/src/update.rs index b005fc5..4c70c5a 100644 --- a/src/update.rs +++ b/src/update.rs @@ -288,6 +288,41 @@ impl Update { } } + // Intermediate function that spawns a new thread + // which starts the async [start()] function that + // actually contains the code. This is so that everytime + // an update needs to happen (Gupax tab, auto-update), the + // code only needs to be edited once, here. + pub fn spawn_thread(og: &Arc<Mutex<State>>, update: &Arc<Mutex<Update>>, state_ver: &Arc<Mutex<Version>>, state_path: &PathBuf) { + update.lock().unwrap().path_p2pool = og.lock().unwrap().gupax.absolute_p2pool_path.display().to_string(); + update.lock().unwrap().path_xmrig = og.lock().unwrap().gupax.absolute_xmrig_path.display().to_string(); + update.lock().unwrap().tor = og.lock().unwrap().gupax.update_via_tor; + let og = Arc::clone(&og); + let state_ver = Arc::clone(&state_ver); + let update = Arc::clone(&update); + let state_path = state_path.clone(); + std::thread::spawn(move|| { + info!("Spawning update thread..."); + match Update::start(update.clone(), og.clone(), state_ver.clone()) { + Err(e) => { + info!("Update ... FAIL ... {}", e); + *update.lock().unwrap().msg.lock().unwrap() = format!("{} | {}", MSG_FAILED, e); + }, + _ => { + info!("Update | Saving state..."); + match State::save(&mut og.lock().unwrap(), &state_path) { + Ok(_) => info!("Update ... OK"), + Err(e) => { + warn!("Update | Saving state ... FAIL ... {}", e); + *update.lock().unwrap().msg.lock().unwrap() = format!("Saving new versions into state failed"); + }, + }; + } + }; + *update.lock().unwrap().updating.lock().unwrap() = false; + }); + } + // Download process: // 0. setup tor, client, http, etc // 1. fill vector with all enums @@ -295,7 +330,6 @@ impl Update { // 3. if current == version, remove from vec // 4. loop over vec, download links // 5. extract, upgrade - #[tokio::main] pub async fn start(update: Arc<Mutex<Self>>, og: Arc<Mutex<State>>, state_ver: Arc<Mutex<Version>>) -> Result<(), anyhow::Error> { //---------------------------------------------------------------------------------------------------- Init