os: fix platform specific issues

This commit is contained in:
hinto-janaiyo 2022-11-02 18:18:41 -04:00
parent b64e1e3a46
commit 22a03a6034
No known key found for this signature in database
GPG key ID: D7483F6CA27D1B1D
9 changed files with 152 additions and 154 deletions

98
Cargo.lock generated
View file

@ -2,6 +2,48 @@
# It is not intended for manual editing. # It is not intended for manual editing.
version = 3 version = 3
[[package]]
name = "Gupax"
version = "0.1.0"
dependencies = [
"anyhow",
"arti-client",
"arti-hyper",
"bytes",
"chrono",
"dirs",
"eframe",
"egui",
"egui_extras",
"env_logger 0.9.1",
"figment",
"flate2",
"hex-literal",
"hyper",
"hyper-tls",
"image",
"log",
"monero",
"num-format",
"num_cpus",
"rand 0.8.5",
"regex",
"reqwest",
"rusqlite",
"serde",
"serde_json",
"sha2 0.10.6",
"tar",
"tls-api",
"tls-api-native-tls",
"tokio",
"toml",
"tor-rtcompat",
"walkdir",
"winres",
"zip",
]
[[package]] [[package]]
name = "ab_glyph" name = "ab_glyph"
version = "0.2.18" version = "0.2.18"
@ -1158,6 +1200,8 @@ dependencies = [
[[package]] [[package]]
name = "eframe" name = "eframe"
version = "0.19.0" version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0d49426c3e72a6728b0c790d22db8bf7bbcff10d83b8b6f3a01295be982302e"
dependencies = [ dependencies = [
"bytemuck", "bytemuck",
"egui", "egui",
@ -1178,6 +1222,8 @@ dependencies = [
[[package]] [[package]]
name = "egui" name = "egui"
version = "0.19.0" version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc9fcd393c3daaaf5909008a1d948319d538b79c51871e4df0993260260a94e4"
dependencies = [ dependencies = [
"ahash 0.8.0", "ahash 0.8.0",
"epaint", "epaint",
@ -1188,6 +1234,8 @@ dependencies = [
[[package]] [[package]]
name = "egui-winit" name = "egui-winit"
version = "0.19.0" version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07ddc525334c416e11580123e147b970f738507f427c9fb1cd09ea2dd7416a3a"
dependencies = [ dependencies = [
"arboard", "arboard",
"egui", "egui",
@ -1201,6 +1249,8 @@ dependencies = [
[[package]] [[package]]
name = "egui_extras" name = "egui_extras"
version = "0.19.0" version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f698f685bb0ad39e87109e2f695ded0bccde77d5d40bbf7590cb5561c1e3039d"
dependencies = [ dependencies = [
"egui", "egui",
"image", "image",
@ -1209,6 +1259,8 @@ dependencies = [
[[package]] [[package]]
name = "egui_glow" name = "egui_glow"
version = "0.19.0" version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad77d4a00402bae9658ee64be148f4b2a0b38e4fc7874970575ca01ed1c5b75d"
dependencies = [ dependencies = [
"bytemuck", "bytemuck",
"egui", "egui",
@ -1228,6 +1280,8 @@ checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797"
[[package]] [[package]]
name = "emath" name = "emath"
version = "0.19.0" version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9542a40106fdba943a055f418d1746a050e1a903a049b030c2b097d4686a33cf"
dependencies = [ dependencies = [
"bytemuck", "bytemuck",
] ]
@ -1284,6 +1338,8 @@ dependencies = [
[[package]] [[package]]
name = "epaint" name = "epaint"
version = "0.19.0" version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ba04741be7f6602b1a1b28f1082cce45948a7032961c52814f8946b28493300"
dependencies = [ dependencies = [
"ab_glyph", "ab_glyph",
"ahash 0.8.0", "ahash 0.8.0",
@ -1766,48 +1822,6 @@ dependencies = [
"gl_generator", "gl_generator",
] ]
[[package]]
name = "gupax"
version = "0.1.0"
dependencies = [
"anyhow",
"arti-client",
"arti-hyper",
"bytes",
"chrono",
"dirs",
"eframe",
"egui",
"egui_extras",
"env_logger 0.9.1",
"figment",
"flate2",
"hex-literal",
"hyper",
"hyper-tls",
"image",
"log",
"monero",
"num-format",
"num_cpus",
"rand 0.8.5",
"regex",
"reqwest",
"rusqlite",
"serde",
"serde_json",
"sha2 0.10.6",
"tar",
"tls-api",
"tls-api-native-tls",
"tokio",
"toml",
"tor-rtcompat",
"walkdir",
"winres",
"zip",
]
[[package]] [[package]]
name = "h2" name = "h2"
version = "0.3.15" version = "0.3.15"

View file

@ -1,6 +1,9 @@
[package] [package]
name = "gupax" name = "Gupax"
version = "0.1.0" version = "0.1.0"
authors = ["hinto-janaiyo <hinto-janaiyo@protonmail.com>"]
description = "GUI for P2Pool+XMRig"
documentation = "https://github.com/hinto-janaiyo/gupax"
edition = "2021" edition = "2021"
[dependencies] [dependencies]
@ -10,17 +13,18 @@ arti-hyper = "0.7.0"
bytes = "1.2.1" bytes = "1.2.1"
chrono = "0.4.22" chrono = "0.4.22"
dirs = "4.0.0" dirs = "4.0.0"
#eframe = "0.19.0" eframe = "0.19.0"
#egui = "0.19.0" egui = "0.19.0"
#egui_extras = { version = "0.19.0", features = ["image"] } egui_extras = { version = "0.19.0", features = ["image"] }
## [external/egui/crates/eframe/src/native/run.rs] line 41: [.with_srgb(true)] ## [external/egui/crates/eframe/src/native/run.rs] line 41: [.with_srgb(true)]
## This line causes a [panic!] inside a Windows VM, from a Linux host. ## This line causes a [panic!] inside a Windows VM, from a Linux host.
## There are many issue threads and PRs to fix it but for now, ## There are many issue threads and PRs to fix it but for now,
## this is here for convenience sake when I'm testing. ## this is here for convenience sake when I'm testing.
## The only change is [.with_srgb()] is set to [false]. ## The only change is [.with_srgb()] is set to [false].
eframe = { path = "external/egui/crates/eframe" } #eframe = { path = "external/egui/crates/eframe" }
egui = { path = "external/egui/crates/egui" } #egui = { path = "external/egui/crates/egui" }
egui_extras = { path = "external/egui/crates/egui_extras", features = ["image"] } #egui_glow = { path = "external/egui/crates/egui_glow"}
#egui_extras = { path = "external/egui/crates/egui_extras", features = ["image"] }
env_logger = "0.9.1" env_logger = "0.9.1"
figment = { version = "0.10.8", features = ["toml"] } figment = { version = "0.10.8", features = ["toml"] }
flate2 = "1.0" flate2 = "1.0"
@ -61,6 +65,5 @@ winres = "0.1.12"
# For macOS build (cargo-bundle) # For macOS build (cargo-bundle)
[package.metadata.bundle] [package.metadata.bundle]
identifier = "io.github.hinto-janaiyo.gupax" identifier = "com.github.hinto-janaiyo.gupax"
icon = ["images/png/icon@2x.png"] icon = ["images/icons/icon@2x.png"]
short_description = "GUI for P2Pool+XMRig"

View file

@ -92,7 +92,7 @@ Windows/macOS/Linux:
``` ```
cargo build --release cargo build --release
``` ```
On macOS, if you want the binary to have an icon in `Finder`, you must install [`cargo-bundle`](https://github.com/burtonageo/cargo-bundle) and compile with: On macOS, if you want the binary to have an icon in `Finder`, you must install [`cargo-bundle`](https://github.com/burtonageo/cargo-bundle) and compile uwith:
``` ```
cargo bundle --release cargo bundle --release
``` ```

View file

@ -57,6 +57,6 @@ Every frame, the max available `[width, height]` are calculated, and those are u
Main [App] outer frame (default: [1280.0, 720.0]) Main [App] outer frame (default: [1280.0, 720.0])
├─ Inner frame (1264.0, 704.0) ├─ Inner frame (1264.0, 704.0)
├─ TopPanel = [width: (max-90.0)/5.0, height: max/10.0] ├─ TopPanel = [width: (max-90.0)/5.0, height: max/10.0]
├─ BottomPanel = [width: max, height: max/15.0] ├─ BottomPanel = [width: max, height: max/18.0]
├─ CentralPanel = [width: (max/8.0), height: the rest ├─ CentralPanel = [width: (max/8.0), height: the rest
``` ```

View file

@ -96,8 +96,6 @@ r#"USAGE: gupax [--flags]
-r | --reset Reset all Gupax configuration/state -r | --reset Reset all Gupax configuration/state
-f | --ferris Print an extremely cute crab"#; -f | --ferris Print an extremely cute crab"#;
pub const ARG_COPYRIGHT: &'static str = pub const ARG_COPYRIGHT: &'static str =
r#"Gupax, P2Pool, and XMRig are licensed under GPLv3. r#"Gupax is licensed under GPLv3.
For more information, see here: For more information, see link below:
- https://github.com/hinto-janaiyo/gupax <https://github.com/hinto-janaiyo/gupax>"#;
- https://github.com/SChernykh/p2pool
- https://github.com/xmrig/xmrig"#;

View file

@ -51,7 +51,7 @@ impl Gupax {
info!("Spawning update thread..."); info!("Spawning update thread...");
match Update::start(update_thread, og_ver.clone(), state_ver.clone()) { match Update::start(update_thread, og_ver.clone(), state_ver.clone()) {
Err(e) => { Err(e) => {
info!("Update ... {} ... FAIL", e); info!("Update ... FAIL ... {}", e);
*update.lock().unwrap().msg.lock().unwrap() = format!("{} | {}", MSG_FAILED, e); *update.lock().unwrap().msg.lock().unwrap() = format!("{} | {}", MSG_FAILED, e);
}, },
_ => { _ => {
@ -87,13 +87,13 @@ impl Gupax {
ui.horizontal(|ui| { ui.horizontal(|ui| {
ui.group(|ui| { ui.group(|ui| {
let width = (width - SPACE*9.8)/5.0; let width = (width - SPACE*9.8)/5.0;
let height = height/2.0; let height = height/2.5;
let mut style = (*ctx.style()).clone(); let mut style = (*ctx.style()).clone();
style.spacing.icon_width_inner = width / 6.0; style.spacing.icon_width_inner = width / 6.0;
style.spacing.icon_width = width / 4.0; style.spacing.icon_width = width / 4.0;
style.spacing.icon_spacing = 20.0; style.spacing.icon_spacing = 20.0;
ctx.set_style(style); ctx.set_style(style);
let height = height/2.0; let height = height/2.5;
ui.add_sized([width, height], egui::Checkbox::new(&mut state.auto_update, "Auto-update")).on_hover_text(GUPAX_AUTO_UPDATE); ui.add_sized([width, height], egui::Checkbox::new(&mut state.auto_update, "Auto-update")).on_hover_text(GUPAX_AUTO_UPDATE);
ui.separator(); ui.separator();
ui.add_sized([width, height], egui::Checkbox::new(&mut state.auto_node, "Auto-node")).on_hover_text(GUPAX_AUTO_NODE); ui.add_sized([width, height], egui::Checkbox::new(&mut state.auto_node, "Auto-node")).on_hover_text(GUPAX_AUTO_NODE);

View file

@ -85,7 +85,7 @@ pub struct App {
// Static stuff // Static stuff
now: Instant, // Internal timer now: Instant, // Internal timer
exe: String, // Path for [Gupax] binary exe: String, // Path for [Gupax] binary
tmp: String, // Tmp folder for updates, random every update dir: String, // Directory [Gupax] binary is in
resolution: Vec2, // Frame resolution resolution: Vec2, // Frame resolution
os: &'static str, // OS os: &'static str, // OS
version: String, // Gupax version version: String, // Gupax version
@ -115,7 +115,7 @@ impl App {
node: Arc::new(Mutex::new(NodeStruct::default())), node: Arc::new(Mutex::new(NodeStruct::default())),
og: Arc::new(Mutex::new(State::default())), og: Arc::new(Mutex::new(State::default())),
state: State::default(), state: State::default(),
update: Arc::new(Mutex::new(Update::new(PathBuf::new(), PathBuf::new(), true))), update: Arc::new(Mutex::new(Update::new(String::new(), PathBuf::new(), PathBuf::new(), true))),
diff: false, diff: false,
p2pool: false, p2pool: false,
xmrig: false, xmrig: false,
@ -123,7 +123,7 @@ impl App {
reset: false, reset: false,
now: Instant::now(), now: Instant::now(),
exe: "".to_string(), exe: "".to_string(),
tmp: "".to_string(), dir: "".to_string(),
resolution: Vec2::new(1280.0, 720.0), resolution: Vec2::new(1280.0, 720.0),
os: OS, os: OS,
version: format!("{}", GUPAX_VERSION), version: format!("{}", GUPAX_VERSION),
@ -132,12 +132,16 @@ impl App {
}; };
// Apply arg state // Apply arg state
let mut app = parse_args(app); let mut app = parse_args(app);
// Get exe path + random tmp folder // Get exe path
app.exe = match get_exe_dir() { app.exe = match get_exe() {
Ok(exe) => exe, Ok(exe) => exe,
Err(err) => { panic_main(err.to_string()); exit(1); }, Err(err) => { panic_main(err.to_string()); exit(1); },
}; };
app.tmp = get_rand_tmp(&app.exe); // Get exe directory path
app.dir = match get_exe_dir() {
Ok(dir) => dir,
Err(err) => { panic_main(err.to_string()); exit(1); },
};
// Read disk state if no [--reset] arg // Read disk state if no [--reset] arg
if app.reset == false { if app.reset == false {
app.og = match State::get() { app.og = match State::get() {
@ -157,7 +161,7 @@ impl App {
let p2pool_path = app.og.lock().unwrap().gupax.absolute_p2pool_path.clone(); let p2pool_path = app.og.lock().unwrap().gupax.absolute_p2pool_path.clone();
let xmrig_path = app.og.lock().unwrap().gupax.absolute_xmrig_path.clone(); let xmrig_path = app.og.lock().unwrap().gupax.absolute_xmrig_path.clone();
let tor = app.og.lock().unwrap().gupax.update_via_tor; let tor = app.og.lock().unwrap().gupax.update_via_tor;
app.update = Arc::new(Mutex::new(Update::new(p2pool_path, xmrig_path, tor))); app.update = Arc::new(Mutex::new(Update::new(app.exe.clone(), p2pool_path, xmrig_path, tor)));
app app
} }
} }
@ -264,7 +268,7 @@ fn parse_args(mut app: App) -> App {
match arg.as_str() { match arg.as_str() {
"-h"|"--help" => { println!("{}", ARG_HELP); exit(0); }, "-h"|"--help" => { println!("{}", ARG_HELP); exit(0); },
"-v"|"--version" => { "-v"|"--version" => {
println!("Gupax | {}\nP2Pool | {}\nXMRig | {}\n\nOS: [{}], Commit: [{}]\n\n{}", GUPAX_VERSION, P2POOL_VERSION, XMRIG_VERSION, OS_NAME, &COMMIT[..40], ARG_COPYRIGHT); println!("Gupax {} (OS: {}, Commit: {})\n\n{}", GUPAX_VERSION, OS_NAME, &COMMIT[..40], ARG_COPYRIGHT);
exit(0); exit(0);
}, },
"-f"|"--ferris" => { println!("{}", FERRIS); exit(0); }, "-f"|"--ferris" => { println!("{}", FERRIS); exit(0); },
@ -298,25 +302,12 @@ pub fn get_exe_dir() -> Result<String, std::io::Error> {
} }
} }
pub fn get_rand_tmp(path: &String) -> String {
use rand::{thread_rng, Rng};
use rand::distributions::Alphanumeric;
let rand: String = thread_rng()
.sample_iter(&Alphanumeric)
.take(10)
.map(char::from)
.collect();
let path = path.to_string() + "/gupax_tmp_" + &rand;
info!("Generated rand_tmp ... {}", path);
path
}
// Clean any [gupax_tmp.*] directories // Clean any [gupax_tmp.*] directories
pub fn clean_dir() -> Result<(), anyhow::Error> { pub fn clean_dir() -> Result<(), anyhow::Error> {
for entry in std::fs::read_dir(get_exe_dir()?)? { for entry in std::fs::read_dir(get_exe_dir()?)? {
let entry = entry?; let entry = entry?;
entry.path().is_dir() && continue; if ! entry.path().is_dir() { continue }
if entry.file_name().to_str().ok_or(anyhow::Error::msg("Basename failed"))?.starts_with("gupax_tmp_") { if entry.file_name().to_str().ok_or(anyhow::Error::msg("Basename failed"))?.starts_with("gupax_update_") {
let path = entry.path(); let path = entry.path();
match std::fs::remove_dir_all(&path) { match std::fs::remove_dir_all(&path) {
Ok(_) => info!("Remove [{}] ... OK", path.display()), Ok(_) => info!("Remove [{}] ... OK", path.display()),
@ -431,13 +422,13 @@ impl eframe::App for App {
// Close confirmation. // Close confirmation.
if self.quit { if self.quit {
// If [ask_before_quit == true] // If [ask_before_quit == true]
if self.state.gupax.ask_before_quit { if self.og.lock().unwrap().gupax.ask_before_quit {
egui::TopBottomPanel::bottom("quit").show(ctx, |ui| { egui::TopBottomPanel::bottom("quit").show(ctx, |ui| {
let width = self.width; let width = self.width;
let height = self.height/8.0; let height = self.height/8.0;
ui.group(|ui| { ui.group(|ui| {
if ui.add_sized([width, height], egui::Button::new("Yes")).clicked() { if ui.add_sized([width, height], egui::Button::new("Yes")).clicked() {
if self.state.gupax.save_before_quit { if self.og.lock().unwrap().gupax.save_before_quit {
if self.diff { if self.diff {
info!("Saving before quit..."); info!("Saving before quit...");
match self.state.save() { match self.state.save() {
@ -461,15 +452,14 @@ impl eframe::App for App {
let ten = height/10.0; let ten = height/10.0;
// Detect processes or update // Detect processes or update
ui.add_space(ten); ui.add_space(ten);
// || self.update.updating if *self.update.lock().unwrap().updating.lock().unwrap() || self.p2pool || self.xmrig {
if self.p2pool || self.xmrig {
ui.add_sized([width, height/4.0], Label::new("Are you sure you want to quit?")); ui.add_sized([width, height/4.0], Label::new("Are you sure you want to quit?"));
// if self.update.updating { ui.add_sized([width, ten], Label::new("Update is in progress...!")); } if *self.update.lock().unwrap().updating.lock().unwrap() { ui.add_sized([width, ten], Label::new("Update is in progress...!")); }
if self.p2pool { ui.add_sized([width, ten], Label::new("P2Pool is online...!")); } if self.p2pool { ui.add_sized([width, ten], Label::new("P2Pool is online...!")); }
if self.xmrig { ui.add_sized([width, ten], Label::new("XMRig is online...!")); } if self.xmrig { ui.add_sized([width, ten], Label::new("XMRig is online...!")); }
// Else, just quit // Else, just quit
} else { } else {
if self.state.gupax.save_before_quit { if self.og.lock().unwrap().gupax.save_before_quit {
if self.diff { if self.diff {
info!("Saving before quit..."); info!("Saving before quit...");
match self.state.save() { match self.state.save() {
@ -486,7 +476,7 @@ impl eframe::App for App {
}); });
// Else, quit (save if [save_before_quit == true] // Else, quit (save if [save_before_quit == true]
} else { } else {
if self.state.gupax.save_before_quit { if self.og.lock().unwrap().gupax.save_before_quit {
if self.diff { if self.diff {
info!("Saving before quit..."); info!("Saving before quit...");
match self.state.save() { match self.state.save() {
@ -534,7 +524,7 @@ impl eframe::App for App {
// Bottom: app info + state/process buttons // Bottom: app info + state/process buttons
egui::TopBottomPanel::bottom("bottom").show(ctx, |ui| { egui::TopBottomPanel::bottom("bottom").show(ctx, |ui| {
let width = self.width/8.0; let width = self.width/8.0;
let height = self.height/15.0; let height = self.height/18.0;
ui.style_mut().override_text_style = Some(Name("Bottom".into())); ui.style_mut().override_text_style = Some(Name("Bottom".into()));
ui.horizontal(|ui| { ui.horizontal(|ui| {
ui.group(|ui| { ui.group(|ui| {

View file

@ -96,7 +96,7 @@ impl State {
// Windows | {FOLDERID_RoamingAppData} | C:\Users\Alice\AppData\Roaming // Windows | {FOLDERID_RoamingAppData} | C:\Users\Alice\AppData\Roaming
let mut path = match dirs::data_dir() { let mut path = match dirs::data_dir() {
Some(mut path) => { Some(mut path) => {
path.push(DIRECTORY); path.push(STATE_DIRECTORY);
info!("OS data path ... OK"); info!("OS data path ... OK");
path path
}, },
@ -104,7 +104,7 @@ impl State {
}; };
// Create directory // Create directory
fs::create_dir_all(&path)?; fs::create_dir_all(&path)?;
path.push(FILENAME); path.push(STATE_FILE);
info!("TOML path ... {}", path.display()); info!("TOML path ... {}", path.display());
Ok(path) Ok(path)
} }
@ -255,27 +255,24 @@ impl From<std::io::Error> for TomlError {
} }
//---------------------------------------------------------------------------------------------------- Const //---------------------------------------------------------------------------------------------------- Const
const FILENAME: &'static str = "gupax.toml"; // State file
const STATE_FILE: &'static str = "gupax.toml";
const ERROR: &'static str = "TOML Error"; const ERROR: &'static str = "TOML Error";
const PATH_ERROR: &'static str = "PATH for state directory could not be not found"; const PATH_ERROR: &'static str = "PATH for state directory could not be not found";
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
const DIRECTORY: &'static str = "Gupax"; const STATE_DIRECTORY: &'static str = "Gupax";
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
const DIRECTORY: &'static str = "Gupax"; const STATE_DIRECTORY: &'static str = "com.github.hinto-janaiyo.gupax";
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
const DIRECTORY: &'static str = "gupax"; const STATE_DIRECTORY: &'static str = "gupax";
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
pub const DEFAULT_P2POOL_PATH: &'static str = r"P2Pool\p2pool.exe"; pub const DEFAULT_P2POOL_PATH: &'static str = r"P2Pool\p2pool.exe";
#[cfg(target_os = "macos")] #[cfg(target_family = "unix")]
pub const DEFAULT_P2POOL_PATH: &'static str = "P2Pool/P2Pool";
#[cfg(target_os = "linux")]
pub const DEFAULT_P2POOL_PATH: &'static str = "p2pool/p2pool"; pub const DEFAULT_P2POOL_PATH: &'static str = "p2pool/p2pool";
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
pub const DEFAULT_XMRIG_PATH: &'static str = r"XMRig\xmrig.exe"; pub const DEFAULT_XMRIG_PATH: &'static str = r"XMRig\xmrig.exe";
#[cfg(target_os = "macos")] #[cfg(target_family = "unix")]
pub const DEFAULT_XMRIG_PATH: &'static str = "XMRig/XMRig";
#[cfg(target_os = "linux")]
pub const DEFAULT_XMRIG_PATH: &'static str = "xmrig/xmrig"; pub const DEFAULT_XMRIG_PATH: &'static str = "xmrig/xmrig";
//---------------------------------------------------------------------------------------------------- Error Enum //---------------------------------------------------------------------------------------------------- Error Enum

View file

@ -35,6 +35,7 @@ use crate::update::Name::*;
use hyper::{Client,Body,Request}; use hyper::{Client,Body,Request};
use hyper::header::HeaderValue; use hyper::header::HeaderValue;
use hyper_tls::HttpsConnector; use hyper_tls::HttpsConnector;
use hyper::header::LOCATION;
use log::*; use log::*;
use rand::distributions::Alphanumeric; use rand::distributions::Alphanumeric;
use rand::{thread_rng, Rng}; use rand::{thread_rng, Rng};
@ -55,18 +56,17 @@ use std::os::unix::fs::OpenOptionsExt;
//---------------------------------------------------------------------------------------------------- Constants //---------------------------------------------------------------------------------------------------- Constants
// Package naming schemes: // Package naming schemes:
// gupax | gupax-vX.X.X-(windows|macos|linux)-x64.(zip|tar.gz) // gupax | gupax-vX.X.X-(windows|macos|linux)-x64(standalone|bundle).(zip|tar.gz)
// p2pool | p2pool-vX.X.X-(windows|macos|linux)-x64.(zip|tar.gz) // p2pool | p2pool-vX.X.X-(windows|macos|linux)-x64.(zip|tar.gz)
// xmrig | xmrig-X.X.X-(msvc-win64|macos-x64|linux-static-x64).(zip|tar.gz) // xmrig | xmrig-X.X.X-(msvc-win64|macos-x64|linux-static-x64).(zip|tar.gz)
// //
// Download link = PREFIX + Version (found at runtime) + SUFFIX + Version + EXT // Download link = PREFIX + Version (found at runtime) + SUFFIX + Version + EXT
// Example: https://github.com/hinto-janaiyo/gupax/releases/download/v0.0.1/gupax-v0.0.1-linux-standalone-x64 // Example: https://github.com/hinto-janaiyo/gupax/releases/download/v0.0.1/gupax-v0.0.1-linux-standalone-x64.tar.gz
// //
// Exceptions (there are always exceptions...): // Exceptions (there are always exceptions...):
// - XMRig doesn't have a [v], so it is [xmrig-6.18.0-...] // - XMRig doesn't have a [v], so it is [xmrig-6.18.0-...]
// - XMRig separates the hash and signature // - XMRig separates the hash and signature
// - P2Pool hashes are in UPPERCASE // - P2Pool hashes are in UPPERCASE
// - Gupax will be downloaded as a standalone binary (no decompression/extraction needed)
const GUPAX_METADATA: &'static str = "https://api.github.com/repos/hinto-janaiyo/gupax/releases/latest"; const GUPAX_METADATA: &'static str = "https://api.github.com/repos/hinto-janaiyo/gupax/releases/latest";
const P2POOL_METADATA: &'static str = "https://api.github.com/repos/SChernykh/p2pool/releases/latest"; const P2POOL_METADATA: &'static str = "https://api.github.com/repos/SChernykh/p2pool/releases/latest";
@ -85,21 +85,21 @@ const P2POOL_HASH: &'static str = "sha256sums.txt.asc";
const XMRIG_HASH: &'static str = "SHA256SUMS"; const XMRIG_HASH: &'static str = "SHA256SUMS";
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
const GUPAX_EXTENSION: &'static str = "-windows-x64-standalone.exe"; const GUPAX_EXTENSION: &'static str = "-windows-x64-standalone.zip";
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
const P2POOL_EXTENSION: &'static str = "-windows-x64.zip"; const P2POOL_EXTENSION: &'static str = "-windows-x64.zip";
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
const XMRIG_EXTENSION: &'static str = "-msvc-win64.zip"; const XMRIG_EXTENSION: &'static str = "-msvc-win64.zip";
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
const GUPAX_EXTENSION: &'static str = "-macos-x64-standalone"; const GUPAX_EXTENSION: &'static str = "-macos-x64-standalone.tar.gz";
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
const P2POOL_EXTENSION: &'static str = "-macos-x64.tar.gz"; const P2POOL_EXTENSION: &'static str = "-macos-x64.tar.gz";
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
const XMRIG_EXTENSION: &'static str = "-macos-x64.tar.gz"; const XMRIG_EXTENSION: &'static str = "-macos-x64.tar.gz";
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
const GUPAX_EXTENSION: &'static str = "-linux-x64-standalone"; const GUPAX_EXTENSION: &'static str = "-linux-x64-standalone.tar.gz";
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
const P2POOL_EXTENSION: &'static str = "-linux-x64.tar.gz"; const P2POOL_EXTENSION: &'static str = "-linux-x64.tar.gz";
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
@ -107,23 +107,17 @@ const XMRIG_EXTENSION: &'static str = "-linux-static-x64.tar.gz";
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
const GUPAX_BINARY: &'static str = "gupax.exe"; const GUPAX_BINARY: &'static str = "gupax.exe";
#[cfg(target_os = "macos")] #[cfg(target_family = "unix")]
const GUPAX_BINARY: &'static str = "Gupax";
#[cfg(target_os = "linux")]
const GUPAX_BINARY: &'static str = "gupax"; const GUPAX_BINARY: &'static str = "gupax";
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
const P2POOL_BINARY: &'static str = "p2pool.exe"; const P2POOL_BINARY: &'static str = "p2pool.exe";
#[cfg(target_os = "macos")] #[cfg(target_family = "unix")]
const P2POOL_BINARY: &'static str = "P2Pool";
#[cfg(target_os = "linux")]
const P2POOL_BINARY: &'static str = "p2pool"; const P2POOL_BINARY: &'static str = "p2pool";
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
const XMRIG_BINARY: &'static str = "xmrig.exe"; const XMRIG_BINARY: &'static str = "xmrig.exe";
#[cfg(target_os = "macos")] #[cfg(target_family = "unix")]
const XMRIG_BINARY: &'static str = "XMRig";
#[cfg(target_os = "linux")]
const XMRIG_BINARY: &'static str = "xmrig"; const XMRIG_BINARY: &'static str = "xmrig";
// Some fake Curl/Wget user-agents because GitHub API requires one and a Tor browser // Some fake Curl/Wget user-agents because GitHub API requires one and a Tor browser
@ -235,9 +229,9 @@ pub struct Update {
impl Update { impl Update {
// Takes in current paths from [State] // Takes in current paths from [State]
pub fn new(path_p2pool: PathBuf, path_xmrig: PathBuf, tor: bool) -> Self { pub fn new(path_gupax: String, path_p2pool: PathBuf, path_xmrig: PathBuf, tor: bool) -> Self {
Self { Self {
path_gupax: crate::get_exe().unwrap(), path_gupax,
path_p2pool: path_p2pool.display().to_string(), path_p2pool: path_p2pool.display().to_string(),
path_xmrig: path_xmrig.display().to_string(), path_xmrig: path_xmrig.display().to_string(),
tmp_dir: "".to_string(), tmp_dir: "".to_string(),
@ -260,9 +254,9 @@ impl Update {
.collect(); .collect();
let base = crate::get_exe_dir()?; let base = crate::get_exe_dir()?;
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
let tmp_dir = format!("{}{}{}{}", base, r"\gupax_tmp_", rand_string, r"\"); let tmp_dir = format!("{}{}{}{}", base, r"\gupax_update_", rand_string, r"\");
#[cfg(target_family = "unix")] #[cfg(target_family = "unix")]
let tmp_dir = format!("{}{}{}{}", base, "/gupax_tmp_", rand_string, "/"); let tmp_dir = format!("{}{}{}{}", base, "/gupax_update_", rand_string, "/");
info!("Update | Temporary directory ... {}", tmp_dir); info!("Update | Temporary directory ... {}", tmp_dir);
Ok(tmp_dir) Ok(tmp_dir)
} }
@ -346,7 +340,8 @@ impl Update {
} }
let prog = *update.lock().unwrap().prog.lock().unwrap(); let prog = *update.lock().unwrap().prog.lock().unwrap();
info!("Update | {}", update.lock().unwrap().msg.lock().unwrap()); info!("Update | {}", update.lock().unwrap().msg.lock().unwrap());
let client = Self::get_client(update.lock().unwrap().tor).await?; let tor = update.lock().unwrap().tor;
let client = Self::get_client(tor).await?;
*update.lock().unwrap().prog.lock().unwrap() += 5.0; *update.lock().unwrap().prog.lock().unwrap() += 5.0;
info!("Update | Init ... OK ... {}%", prog); info!("Update | Init ... OK ... {}%", prog);
@ -490,14 +485,10 @@ impl Update {
let version = pkg.new_ver.lock().unwrap(); let version = pkg.new_ver.lock().unwrap();
let link; let link;
// Download link = PREFIX + Version (found at runtime) + SUFFIX + Version + EXT // Download link = PREFIX + Version (found at runtime) + SUFFIX + Version + EXT
// Example: https://github.com/hinto-janaiyo/gupax/releases/download/v0.0.1/gupax-v0.0.1-linux-standalone-x64 // Example: https://github.com/hinto-janaiyo/gupax/releases/download/v0.0.1/gupax-v0.0.1-linux-x64-standalone
// XMRig doesn't have a [v], so slice it out // XMRig doesn't have a [v], so slice it out
if pkg.name == Name::Xmrig { if pkg.name == Name::Xmrig {
link = pkg.link_prefix.to_string() + &version + &pkg.link_suffix + &version[1..] + &pkg.link_extension; link = pkg.link_prefix.to_string() + &version + &pkg.link_suffix + &version[1..] + &pkg.link_extension;
// TODO FIX ME
// This is temp link for v0.0.1 of [Gupax]
} else if pkg.name == Name::Gupax {
link = "https://github.com/hinto-janaiyo/gupax/releases/download/v0.0.1/gupax-v0.0.1-linux-x64".to_string()
} else { } else {
link = pkg.link_prefix.to_string() + &version + &pkg.link_suffix + &version + &pkg.link_extension; link = pkg.link_prefix.to_string() + &version + &pkg.link_suffix + &version + &pkg.link_extension;
} }
@ -549,18 +540,15 @@ impl Update {
*update.lock().unwrap().msg.lock().unwrap() = format!("{}{}", MSG_EXTRACT, new_pkgs); *update.lock().unwrap().msg.lock().unwrap() = format!("{}{}", MSG_EXTRACT, new_pkgs);
info!("Update | {}", EXTRACT); info!("Update | {}", EXTRACT);
for pkg in vec4.iter() { for pkg in vec4.iter() {
if pkg.name == Name::Gupax { let tmp;
let tmp = tmp_dir.to_owned() + GUPAX_BINARY; match pkg.name {
#[cfg(target_family = "unix")] Name::Gupax => tmp = tmp_dir.to_owned() + GUPAX_BINARY,
std::fs::OpenOptions::new().create(true).write(true).mode(0o750).open(&tmp)?; _ => tmp = tmp_dir.to_owned() + &pkg.name.to_string(),
std::fs::write(tmp, pkg.bytes.lock().unwrap().as_ref())?;
} else {
let tmp = tmp_dir.to_owned() + &pkg.name.to_string();
#[cfg(target_os = "windows")]
ZipArchive::extract(&mut ZipArchive::new(std::io::Cursor::new(pkg.bytes.lock().unwrap().as_ref()))?, tmp)?;
#[cfg(target_family = "unix")]
tar::Archive::new(flate2::read::GzDecoder::new(pkg.bytes.lock().unwrap().as_ref())).unpack(tmp)?;
} }
#[cfg(target_os = "windows")]
ZipArchive::extract(&mut ZipArchive::new(std::io::Cursor::new(pkg.bytes.lock().unwrap().as_ref()))?, tmp)?;
#[cfg(target_family = "unix")]
tar::Archive::new(flate2::read::GzDecoder::new(pkg.bytes.lock().unwrap().as_ref())).unpack(tmp)?;
*update.lock().unwrap().prog.lock().unwrap() += (5.0 / pkg_amount).round(); *update.lock().unwrap().prog.lock().unwrap() += (5.0 / pkg_amount).round();
info!("Update | {} ... OK", pkg.name); info!("Update | {} ... OK", pkg.name);
} }
@ -577,18 +565,24 @@ impl Update {
// Just in case, create all folders // Just in case, create all folders
for entry in WalkDir::new(tmp_dir.clone()) { for entry in WalkDir::new(tmp_dir.clone()) {
let entry = entry?.clone(); let entry = entry?.clone();
// If not a file, continue
if ! entry.file_type().is_file() { continue }
let basename = entry.file_name().to_str().ok_or(anyhow::Error::msg("WalkDir basename failed"))?; let basename = entry.file_name().to_str().ok_or(anyhow::Error::msg("WalkDir basename failed"))?;
match basename { match basename {
GUPAX_BINARY => { GUPAX_BINARY => {
let path = update.lock().unwrap().path_gupax.clone();
info!("Update | Moving [{}] -> [{}]", entry.path().display(), path);
// Unix can replace running binaries no problem (they're loading into memory) // Unix can replace running binaries no problem (they're loading into memory)
// Windows locks binaries in place, so we must move (rename) current binary // Windows locks binaries in place, so we must move (rename) current binary
// into the temp folder, then move the new binary into the old ones spot. // into the temp folder, then move the new binary into the old ones spot.
// Clearing the temp folder is now moved at startup instead at the end // Clearing the temp folder is now moved at startup instead at the end
// of this function due to this behavior, thanks Windows. // of this function due to this behavior, thanks Windows.
let path = update.lock().unwrap().path_gupax.clone();
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
std::fs::rename(&path, tmp_dir.clone() + "gupax_old.exe")?; let tmp_windows = tmp_dir.clone() + "gupax_old.exe";
#[cfg(target_os = "windows")]
info!("Update | WINDOWS ONLY ... Moving [{}] -> [{}]", &path, tmp_windows);
#[cfg(target_os = "windows")]
std::fs::rename(&path, tmp_windows)?;
info!("Update | Moving [{}] -> [{}]", entry.path().display(), path);
std::fs::rename(entry.path(), path)?; std::fs::rename(entry.path(), path)?;
*update.lock().unwrap().prog.lock().unwrap() += (5.0 / pkg_amount).round(); *update.lock().unwrap().prog.lock().unwrap() += (5.0 / pkg_amount).round();
}, },
@ -693,7 +687,7 @@ impl Pkg {
//---------------------------------------------------------------------------------------------------- Pkg functions //---------------------------------------------------------------------------------------------------- Pkg functions
// Generate fake [User-Agent] HTTP header // Generate fake [User-Agent] HTTP header
pub fn get_user_agent() -> &'static str { fn get_user_agent() -> &'static str {
let rand = thread_rng().gen_range(0..50); let rand = thread_rng().gen_range(0..50);
let user_agent = FAKE_USER_AGENT[rand]; let user_agent = FAKE_USER_AGENT[rand];
info!("Update | Randomly selecting User-Agent ({}/50) ... {}", rand, user_agent); info!("Update | Randomly selecting User-Agent ({}/50) ... {}", rand, user_agent);
@ -701,7 +695,7 @@ impl Pkg {
} }
// Generate GET request based off input URI + fake user agent // Generate GET request based off input URI + fake user agent
pub fn get_request(link: String, user_agent: &'static str) -> Result<Request<Body>, anyhow::Error> { fn get_request(link: String, user_agent: &'static str) -> Result<Request<Body>, anyhow::Error> {
let request = Request::builder() let request = Request::builder()
.method("GET") .method("GET")
.uri(link) .uri(link)
@ -712,8 +706,8 @@ impl Pkg {
// Get metadata using [Generic hyper::client<C>] & [Request] // Get metadata using [Generic hyper::client<C>] & [Request]
// and change [version, prog] under an Arc<Mutex> // and change [version, prog] under an Arc<Mutex>
pub async fn get_metadata<C>(name: Name, new_ver: Arc<Mutex<String>>, client: Client<C>, link: String, user_agent: &'static str) -> Result<(), Error> async fn get_metadata<C>(name: Name, new_ver: Arc<Mutex<String>>, client: Client<C>, link: String, user_agent: &'static str) -> Result<(), Error>
where C: hyper::client::connect::Connect + Clone + Send + Sync + 'static, { where C: hyper::client::connect::Connect + Clone + Send + Sync + 'static, {
let request = Pkg::get_request(link.clone(), user_agent)?; let request = Pkg::get_request(link.clone(), user_agent)?;
let mut response = client.request(request).await?; let mut response = client.request(request).await?;
let body = hyper::body::to_bytes(response.body_mut()).await?; let body = hyper::body::to_bytes(response.body_mut()).await?;
@ -724,15 +718,17 @@ impl Pkg {
// Takes a [Request], fills the appropriate [Pkg] // Takes a [Request], fills the appropriate [Pkg]
// [bytes] field with the [Archive/Standalone] // [bytes] field with the [Archive/Standalone]
pub async fn get_bytes<C>(name: Name, bytes: Arc<Mutex<bytes::Bytes>>, client: Client<C>, link: String, user_agent: &'static str) -> Result<(), anyhow::Error> async fn get_bytes<C>(name: Name, bytes: Arc<Mutex<bytes::Bytes>>, client: Client<C>, link: String, user_agent: &'static str) -> Result<(), anyhow::Error>
where C: hyper::client::connect::Connect + Clone + Send + Sync + 'static, { where C: hyper::client::connect::Connect + Clone + Send + Sync + 'static, {
let request = Self::get_request(link.clone(), user_agent)?;
let mut response = client.request(request).await?;
// GitHub sends a 302 redirect, so we must follow // GitHub sends a 302 redirect, so we must follow
// the [Location] header... only if Reqwest had custom // the [Location] header... only if Reqwest had custom
// connectors so I didn't have to manually do this... // connectors so I didn't have to manually do this...
let request = Self::get_request(link.clone(), user_agent)?; if response.headers().contains_key(LOCATION) {
let response = client.request(request).await?; let request = Self::get_request(response.headers().get(LOCATION).unwrap().to_str()?.to_string(), user_agent)?;
let request = Self::get_request(response.headers().get(hyper::header::LOCATION).unwrap().to_str()?.to_string(), user_agent)?; response = client.request(request).await?;
let response = client.request(request).await?; }
let body = hyper::body::to_bytes(response.into_body()).await?; let body = hyper::body::to_bytes(response.into_body()).await?;
*bytes.lock().unwrap() = body; *bytes.lock().unwrap() = body;
Ok(()) Ok(())