From b97bdbcf7107275e48cca19f4cef37f66a682c8d Mon Sep 17 00:00:00 2001 From: "Ruan E. Formigoni" Date: Wed, 10 Apr 2024 17:17:03 -0300 Subject: [PATCH 1/6] path_dir_self for backend --- src/std/filesystem.hpp | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/std/filesystem.hpp b/src/std/filesystem.hpp index 652e05e..c5a8da1 100644 --- a/src/std/filesystem.hpp +++ b/src/std/filesystem.hpp @@ -114,19 +114,26 @@ Ret canonical(fs::path const& path) return Ret(ret, true, ""); } // function: canonical }}} -// dir_self() {{{ +// file_self() {{{ template -Ret dir_self() +Ret file_self() { boost::dll::fs::error_code err; - fs::path path_dir_self = boost::dll::program_location(err).parent_path().c_str(); + fs::path path_file_self = boost::dll::program_location(err).c_str(); if ( err ) { return should_throw<_throw, fs::path>("Failed to fetch location of self"); } // if - return Ret(canonical<_throw>(path_dir_self)._ret, true, ""); + return Ret(canonical<_throw>(path_file_self)._ret, true, ""); +} // file_self() }}} + +// dir_self() {{{ +template +Ret dir_self() +{ + return file_self<_throw>(); } // dir_self() }}} // file_exists() {{{ From 42751892766a12b113fe8538ddd344f1f1ce2b80 Mon Sep 17 00:00:00 2001 From: "Ruan E. Formigoni" Date: Wed, 10 Apr 2024 17:23:17 -0300 Subject: [PATCH 2/6] Use Ipc for file list/url fetch --- gui/wizard/src/db/fetch.rs | 50 ----------------------- gui/wizard/src/db/mod.rs | 1 - gui/wizard/src/frame/fetch.rs | 22 +++++++--- gui/wizard/src/frame/platform.rs | 58 +-------------------------- gui/wizard/src/main.rs | 1 + src/cmd/fetch.hpp | 69 +++++++++++++++++++------------- src/lib/ipc.hpp | 1 + src/lib/parser.hpp | 10 ++--- src/main.cpp | 8 +--- 9 files changed, 67 insertions(+), 153 deletions(-) delete mode 100644 gui/wizard/src/db/fetch.rs diff --git a/gui/wizard/src/db/fetch.rs b/gui/wizard/src/db/fetch.rs deleted file mode 100644 index 16e653f..0000000 --- a/gui/wizard/src/db/fetch.rs +++ /dev/null @@ -1,50 +0,0 @@ -use std::iter; -use std::env; -use std::fs::File; -use std::path::PathBuf; -use std::collections::HashMap; -use serde::{Deserialize, Serialize}; - -// struct Entry {{{ -#[derive(Serialize, Deserialize)] -struct Entry -{ - pub paths: Vec, - pub urls: Vec, -} // Entry }}} - -// from_file() {{{ -fn from_file() -> anyhow::Result -{ - let mut path_file : PathBuf = env::var("GIMG_DIR")?.into(); - path_file.push("gameimage.fetch.json"); - - Ok(path_file) -} // from_file() }}} - -// read() {{{ -pub fn read() -> anyhow::Result> -{ - let path_file = from_file()?; - - let file = File::open(path_file)?; - - let entry : Entry = serde_json::from_reader(file)?; - - let mut map : HashMap = HashMap::new(); - - for (path, url) in iter::zip(entry.paths, entry.urls) - { - map.insert(path, url); - } // for - - Ok(map) -} // fn: read }}} - -// get() {{{ -pub fn get() -> anyhow::Result> -{ - read() -} // get() }}} - -// vim: set expandtab fdm=marker ts=2 sw=2 tw=100 et : diff --git a/gui/wizard/src/db/mod.rs b/gui/wizard/src/db/mod.rs index 7951261..ddee47c 100644 --- a/gui/wizard/src/db/mod.rs +++ b/gui/wizard/src/db/mod.rs @@ -1,4 +1,3 @@ -pub mod fetch; pub mod search; pub mod project; pub mod global; diff --git a/gui/wizard/src/frame/fetch.rs b/gui/wizard/src/frame/fetch.rs index 255ce5e..d5fe02a 100644 --- a/gui/wizard/src/frame/fetch.rs +++ b/gui/wizard/src/frame/fetch.rs @@ -18,6 +18,7 @@ use fltk::{ use url as Url; use anyhow::anyhow as ah; +use crate::gameimage; use crate::dimm; use crate::frame; use crate::lib; @@ -28,6 +29,11 @@ use crate::common::PathBufExt; use crate::log; use crate::db; +macro_rules! log_return +{ + ($($arg:tt)*) => { { log!($($arg)*); return; } } +} // log_return + // fn url_basename() {{{ fn url_basename(url : Url::Url) -> anyhow::Result { @@ -139,15 +145,19 @@ pub fn fetch(tx: Sender, title: &str) // Populate 'vec_fetch' with links and download paths let mut base = frame_content.as_base_widget(); - let result_pairs_values = db::fetch::get(); + let vec_files = match gameimage::fetch::query_files() + { + Ok(vec_files) => vec_files, + Err(e) => log_return!("Could not fetch url list from backend: {}", e), + }; - if result_pairs_values.is_err() + let vec_urls = match gameimage::fetch::query_urls() { - log!("Could not fetch file list, '{}'", result_pairs_values.unwrap_err().to_string()); - return; - } // if + Ok(vec_urls) => vec_urls, + Err(e) => log_return!("Could not fetch file list from backend: {}", e), + }; - for (entry_path, entry_url) in result_pairs_values.unwrap() + for (entry_path, entry_url) in std::iter::zip(vec_files, vec_urls) { // Get full path to save the file into let file_dest = std::path::Path::new(&entry_path).to_path_buf(); diff --git a/gui/wizard/src/frame/platform.rs b/gui/wizard/src/frame/platform.rs index 2c825af..c3aa289 100644 --- a/gui/wizard/src/frame/platform.rs +++ b/gui/wizard/src/frame/platform.rs @@ -207,63 +207,7 @@ pub fn platform(tx: Sender, title: &str) ret_frame_footer.btn_prev.clone().emit(tx, common::Msg::DrawWelcome); // Set callback for next - let mut clone_btn_next = ret_frame_footer.btn_next.clone(); - let mut clone_output_status = ret_frame_footer.output_status.clone(); - let clone_tx = tx.clone(); - clone_btn_next.set_callback(move |_| - { - // Get selected platform - let str_platform = if let Ok(guard) = PLATFORM.lock() && let Some(platform) = guard.clone() - { - platform.as_str() - } // if - else - { - clone_output_status.set_value("Could not determine chosen platform"); - return; - }; // else - - // Fetch files - clone_output_status.set_value("Fetching list of files to download"); - - // Disable window - clone_tx.send_awake(common::Msg::WindDeactivate); - - // Fetch files - std::thread::spawn(move || - { - // Args for gameimage_sync - let arg_platform = format!("--platform={}", str_platform); - let arg_url_dwarfs; - let mut args = vec![ - "fetch" - , arg_platform.as_str() - , "--json=gameimage.fetch.json" - ]; - - if let Ok(guard) = URL.lock() && let Some(url) = guard.clone() - { - arg_url_dwarfs = format!("--url-dwarfs={}", url); - args.push(arg_url_dwarfs.as_str()); - env::set_var("GIMG_FETCH_URL_DWARFS", url); - } // match - else - { - log!("Failed to append --url-dwarfs for custom url"); - } // else - - // Ask back-end for the files to download for the selected platform - if common::gameimage_sync(args) != 0 - { - log!("Failed to fetch"); - clone_tx.send_awake(common::Msg::WindActivate); - return; - } // if - - // Draw next frame - clone_tx.send_awake(common::Msg::DrawFetch); - }); - }); + ret_frame_footer.btn_next.clone().emit(tx, common::Msg::DrawFetch); } // }}} diff --git a/gui/wizard/src/main.rs b/gui/wizard/src/main.rs index 9ee69e2..ab992ef 100644 --- a/gui/wizard/src/main.rs +++ b/gui/wizard/src/main.rs @@ -18,6 +18,7 @@ mod frame; mod lib; mod db; mod wizard; +mod gameimage; // }}} use common::Msg; diff --git a/src/cmd/fetch.hpp b/src/cmd/fetch.hpp index 54da977..6388833 100644 --- a/src/cmd/fetch.hpp +++ b/src/cmd/fetch.hpp @@ -30,6 +30,13 @@ namespace fs = std::filesystem; namespace { +// enum class IpcQuery {{{ +enum class IpcQuery +{ + FILES, + URLS, +}; // }}} + // get_path_file_image() {{{ decltype(auto) get_path_file_image(ns_enum::Platform const& platform) { @@ -597,44 +604,50 @@ inline void sha(ns_enum::Platform platform check_file(path_and_url_dwarfs.path, path_and_url_dwarfs.url); } // sha() }}} -// json() {{{ -inline void json(ns_enum::Platform platform - , fs::path path_json - , std::optional const& url_base = std::nullopt - , std::optional const& url_dwarfs = std::nullopt) +// ipc() {{{ +inline void ipc(ns_enum::Platform platform , std::optional query) { - // Remove if exists - fs::remove(path_json); + // Use self as IPC reference + fs::path path_file_ipc = ns_fs::ns_path::file_self()._ret; // Create image path fs::path path_file_image = get_path_file_image(platform); fs::path path_dir_image = path_file_image.parent_path(); - // Log - ns_log::write('i', "platform: ", ns_enum::to_string_lower(platform)); - ns_log::write('i', "image: ", path_file_image); - - // Get url and save path to base - ns_log::write('i', "Writting json for base"); - ns_db::from_file(path_json, [&](auto&& db) + if ( not query.has_value() ) { - auto path_and_url_base = url_resolve_base(platform, url_base, path_dir_image); - db("paths") |= path_and_url_base.path.c_str(); - db("urls") |= path_and_url_base.url.c_str(); - }, ns_db::Mode::CREATE); + "No query provided for IPC"_throw(); + } // if - if ( platform == ns_enum::Platform::LINUX ) { return; } + // Get query + IpcQuery ipc_query = ns_enum::from_string(ns_string::to_upper(*query)); - // Get url and save path to dwarfs - ns_log::write('i', "Writting json for dwarfs"); - ns_db::from_file(path_json, [&](auto&& db) - { - auto path_and_url_dwarfs = url_resolve_dwarfs(platform, url_dwarfs, path_dir_image); - db("paths") |= path_and_url_dwarfs.path.c_str(); - db("urls") |= path_and_url_dwarfs.url.c_str(); - }, ns_db::Mode::UPDATE); + // Start IPC + ns_ipc::Ipc ipc(path_file_ipc); + ns_log::write('i', "Path to ipc reference file: '", path_file_ipc, "'"); -} // json() }}} + switch (ipc_query) + { + case IpcQuery::FILES: + { + fetchlist_base_ret_t path_and_url_base = url_resolve_base(platform, std::nullopt, path_dir_image); + ipc.send(path_and_url_base.path); + if ( platform == ns_enum::Platform::LINUX ) { return; } + fetchlist_dwarfs_ret_t path_and_url_dwarfs = url_resolve_dwarfs(platform, std::nullopt, path_dir_image); + ipc.send(path_and_url_dwarfs.path); + } // case + break; + case IpcQuery::URLS: + { + fetchlist_base_ret_t path_and_url_base = url_resolve_base(platform, std::nullopt, path_dir_image); + ipc.send(path_and_url_base.url); + if ( platform == ns_enum::Platform::LINUX ) { return; } + fetchlist_dwarfs_ret_t path_and_url_dwarfs = url_resolve_dwarfs(platform, std::nullopt, path_dir_image); + ipc.send(path_and_url_dwarfs.url); + } // case + break; + } // switch +} // ipc() }}} } // namespace ns_fetch diff --git a/src/lib/ipc.hpp b/src/lib/ipc.hpp index d2b35d9..40ca05a 100644 --- a/src/lib/ipc.hpp +++ b/src/lib/ipc.hpp @@ -43,6 +43,7 @@ class Ipc inline Ipc::Ipc(fs::path path_file) { std::string identifier = ns_string::to_string(path_file); + ns_log::write('i', "key identifier: ", identifier); // Use a unique key for the message queue. if(m_key = ftok(identifier.c_str(), 65); m_key == -1 ) diff --git a/src/lib/parser.hpp b/src/lib/parser.hpp index b2e8390..35b38a2 100644 --- a/src/lib/parser.hpp +++ b/src/lib/parser.hpp @@ -140,7 +140,7 @@ class Fetch final : public Parser m_parser->add_argument("--platform") .action([&](std::string const& s){ m_map_option_value["--platform"]=s; }) .help("Specity the platform to download the flatimage"); - // Only download base file + // Only download provided file m_parser->add_argument("--only-file") .action([&](std::string const& s){ m_map_option_value["--only-file"]=s; }) .help("Only downloads the specified file"); @@ -150,10 +150,10 @@ class Fetch final : public Parser .implicit_value(true) .action([&](std::string const& s){ m_map_option_value["--sha"]=s; }) .help("Do not download, only check SHA"); - // Only write json, do not download - m_parser->add_argument("--json") - .action([&](std::string const& s){ m_map_option_value["--json"]=s; }) - .help("Do not download, save fetch list to json instead"); + // Query data with ipc + m_parser->add_argument("--ipc") + .action([&](std::string const& s){ m_map_option_value["--ipc"]=s; }) + .help("Query information through ipc (message queues)"); } // Fetch }; // class: Fetch }}} diff --git a/src/main.cpp b/src/main.cpp index df27397..88127a8 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -46,13 +46,9 @@ void fetch(ns_parser::Parser const& parser) return; } // if - if ( parser.optional("--json") ) + if ( parser.optional("--ipc") ) { - ns_fetch::json(platform - , *parser.optional("--json") - , parser.optional("--url-base") - , parser.optional("--url-dwarfs") - ); + ns_fetch::ipc(platform , parser.optional("--ipc")); return; } // if From 545ea7019f68550db658cd4fd308d1af7b257f92 Mon Sep 17 00:00:00 2001 From: "Ruan E. Formigoni" Date: Wed, 10 Apr 2024 17:29:53 -0300 Subject: [PATCH 3/6] include gameimage backend interface --- gui/wizard/src/frame/fetch.rs | 1 - gui/wizard/src/gameimage/fetch.rs | 53 ++++++++++++ gui/wizard/src/gameimage/gameimage.rs | 116 ++++++++++++++++++++++++++ gui/wizard/src/gameimage/mod.rs | 2 + src/lib/ipc.hpp | 12 ++- 5 files changed, 179 insertions(+), 5 deletions(-) create mode 100644 gui/wizard/src/gameimage/fetch.rs create mode 100644 gui/wizard/src/gameimage/gameimage.rs create mode 100644 gui/wizard/src/gameimage/mod.rs diff --git a/gui/wizard/src/frame/fetch.rs b/gui/wizard/src/frame/fetch.rs index d5fe02a..af01c25 100644 --- a/gui/wizard/src/frame/fetch.rs +++ b/gui/wizard/src/frame/fetch.rs @@ -27,7 +27,6 @@ use crate::common::FltkSenderExt; use crate::common::WidgetExtExtra; use crate::common::PathBufExt; use crate::log; -use crate::db; macro_rules! log_return { diff --git a/gui/wizard/src/gameimage/fetch.rs b/gui/wizard/src/gameimage/fetch.rs new file mode 100644 index 0000000..debcc39 --- /dev/null +++ b/gui/wizard/src/gameimage/fetch.rs @@ -0,0 +1,53 @@ +use anyhow::anyhow as ah; + +use crate::log; +use crate::common; +use crate::lib; +use crate::gameimage; + +macro_rules! log_return +{ + ($($arg:tt)*) => { { log!($($arg)*); return Err(ah!($($arg)*)); } } +} // log_return + +// query() {{{ +fn query(str_query : &str) -> anyhow::Result> +{ + let binary = gameimage::gameimage::binary()?; + let platform = gameimage::gameimage::platform()?; + + let _ = gameimage::gameimage::gameimage_async(vec! + [ + "fetch" + , "--platform", &platform + , "--ipc", &str_query + ]); + + let ipc = match lib::ipc::Ipc::new(binary, || {}) + { + Ok(ipc) => ipc, + Err(e) => { log_return!("Could not create ipc instance: {}", e); }, + }; // match + + let mut vec = vec![]; + while let Ok(msg) = ipc.recv() + { + vec.push(msg); + } // while + + Ok(vec) +} // query() }}} + +// query_urls() {{{ +pub fn query_urls() -> anyhow::Result> +{ + Ok(query("urls")?) +} // query_urls() }}} + +// query_files() {{{ +pub fn query_files() -> anyhow::Result> +{ + Ok(query("files")?) +} // query_files() }}} + +// vim: set expandtab fdm=marker ts=2 sw=2 tw=100 et : diff --git a/gui/wizard/src/gameimage/gameimage.rs b/gui/wizard/src/gameimage/gameimage.rs new file mode 100644 index 0000000..300e4e4 --- /dev/null +++ b/gui/wizard/src/gameimage/gameimage.rs @@ -0,0 +1,116 @@ +use std:: +{ + io::prelude::*, + env, + sync::{Arc,Mutex,mpsc}, +}; + +use crate::common; +use crate::log; + +// fn platform() {{{ +pub fn platform() -> anyhow::Result +{ + Ok(env::var("GIMG_PLATFORM")?.to_lowercase()) +} // }}} + +// fn binary() {{{ +pub fn binary() -> anyhow::Result +{ + Ok(std::path::PathBuf::from(env::var("GIMG_BACKEND")?)) +} // }}} + +// pub fn dir_build() {{{ +pub fn dir_build() -> anyhow::Result<()> +{ + Ok(env::set_current_dir(std::path::PathBuf::from(env::var("GIMG_DIR")?))?) +} // fn: dir_build }}} + +// pub fn gameimage_async() {{{ +pub fn gameimage_async(args : Vec<&str>) -> anyhow::Result> +{ + dir_build()?; + + let path_binary_gameimage = std::path::PathBuf::from(env::var("GIMG_BACKEND")?); + + let handle = std::process::Command::new(path_binary_gameimage) + .env_remove("LD_PRELOAD") + .env("FIM_FIFO", "0") + .stderr(std::process::Stdio::inherit()) + .stdout(std::process::Stdio::piped()) + .args(args) + .spawn()?; + + + // Create arc reader for stdout + let arc_handle = Arc::new(Mutex::new(handle)); + + // Clone process handle + let clone_arc_handle = arc_handle.clone(); + + // Create t/r + let (tx, rx) = mpsc::channel(); + std::thread::spawn(move || + { + // Acquire stdout + let mut lock = + if let Ok(lock) = clone_arc_handle.lock() && lock.stdout.is_some() + { + lock + } + else + { + log!("Could not acquire lock"); + let _ = tx.send(1); + return; + }; // else + + // Create buf + let mut buf = vec![0; 4096]; + + // Use buf to write buf to stdout & stderr + loop + { + std::thread::sleep(std::time::Duration::from_millis(50)); + + let bytes_read = match lock.stdout.as_mut().unwrap().read(&mut buf) + { + Ok(bytes_read) => bytes_read, + Err(_) => break, + }; + + if bytes_read == 0 { break; } + let output = String::from_utf8_lossy(&buf[..bytes_read]); + log!("{}", &output); + } + + if let Ok(status) = lock.wait() && let Some(code) = status.code() + { + let _ = tx.send(code); + } + else + { + let _ = tx.send(1); + } // else + + fltk::app::awake(); + }); + + Ok(rx) +} // fn: gameimage_async }}} + +// pub fn gameimage_sync() {{{ +pub fn gameimage_sync(args : Vec<&str>) -> i32 +{ + if let Ok(rx) = gameimage_async(args) + && let Ok(code) = rx.recv() + { + return code; + } // if + + log!("Could not retrieve exit code from backend"); + + 1 +} // fn: gameimage_sync }}} + +// vim: set expandtab fdm=marker ts=2 sw=2 tw=100 et : diff --git a/gui/wizard/src/gameimage/mod.rs b/gui/wizard/src/gameimage/mod.rs new file mode 100644 index 0000000..f112fe4 --- /dev/null +++ b/gui/wizard/src/gameimage/mod.rs @@ -0,0 +1,2 @@ +pub mod gameimage; +pub mod fetch; diff --git a/src/lib/ipc.hpp b/src/lib/ipc.hpp index 40ca05a..d9e6025 100644 --- a/src/lib/ipc.hpp +++ b/src/lib/ipc.hpp @@ -26,6 +26,7 @@ struct message_buffer char message_text[1024]; }; +// class Ipc {{{ class Ipc { private: @@ -38,8 +39,9 @@ class Ipc template void send(T&& t); -}; // class +}; // class Ipc }}} +// Ipc::Ipc() {{{ inline Ipc::Ipc(fs::path path_file) { std::string identifier = ns_string::to_string(path_file); @@ -62,8 +64,9 @@ inline Ipc::Ipc(fs::path path_file) ns_log::write('i', "Message queue id: ", m_message_queue_id); m_buffer.message_type = 1; -} // Ipc::Ipc +} // Ipc::Ipc() }}} +// Ipc::~Ipc() {{{ inline Ipc::~Ipc() { if ( msgctl(m_message_queue_id, IPC_RMID, NULL) == -1 ) @@ -71,8 +74,9 @@ inline Ipc::~Ipc() ns_log::write('i', "Could not remove the message queue"); perror("Could not remove message queue"); } // if -} // Ipc::~Ipc +} // Ipc::~Ipc() }}} +// Ipc::send() {{{ template void Ipc::send(T&& t) { @@ -86,7 +90,7 @@ void Ipc::send(T&& t) { perror("Failure to send message"); } // if -} // Ipc::send +} // Ipc::send() }}} } // namespace ns_ipc From cac7d9e4229a98987c1fdcaf22178eb43e65c019 Mon Sep 17 00:00:00 2001 From: "Ruan E. Formigoni" Date: Wed, 10 Apr 2024 17:37:55 -0300 Subject: [PATCH 4/6] Move validation to backend interface --- gui/wizard/src/frame/fetch.rs | 43 +++++-------------------------- gui/wizard/src/gameimage/fetch.rs | 33 ++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 36 deletions(-) diff --git a/gui/wizard/src/frame/fetch.rs b/gui/wizard/src/frame/fetch.rs index af01c25..fd2c135 100644 --- a/gui/wizard/src/frame/fetch.rs +++ b/gui/wizard/src/frame/fetch.rs @@ -59,45 +59,16 @@ struct Data btn_fetch : Button, } // struct }}} -// fn verify_and_configure() {{{ -fn verify_and_configure(mut output : Output) -> anyhow::Result<()> +// fn backend_validate_and_configure() {{{ +fn backend_validate_and_configure(mut output : Output) -> anyhow::Result<()> { - // Get platform - let str_platform = env::var("GIMG_PLATFORM")?.to_lowercase(); - - // Run backend to merge files output.set_value("Validating and extracting..."); - let arg_url_dwarfs; - let mut args = vec![ - "fetch" - , "--platform" - , &str_platform - ]; - - if let Ok(url) = env::var("GIMG_FETCH_URL_DWARFS") - { - arg_url_dwarfs = format!("--url-dwarfs={}", url); - args.push(&arg_url_dwarfs); - } // if - - args.push("--sha"); - - // Verify sha - if common::gameimage_sync(args.clone()) != 0 - { - log!("Failed to verify files with backend"); - return Err(ah!("Failed to verify files with backend")); - } // if - - let _ = args.pop(); + // Run backend to merge files + gameimage::fetch::validate()?; - // Process downloaded files - if common::gameimage_sync(args.clone()) != 0 - { - log!("Failed to configure downloaded files"); - return Err(ah!("Failed to configure downloaded files")); - } // if + // Use backend to configure downloaded files + gameimage::fetch::configure()?; Ok(()) } @@ -336,7 +307,7 @@ pub fn fetch(tx: Sender, title: &str) std::thread::spawn(move || { // Draw package creator - if verify_and_configure(clone_output_status.clone()).is_err() + if backend_validate_and_configure(clone_output_status.clone()).is_err() { log!("Failed to verify and configure downloaded files"); clone_output_status.set_value("Download the required files to proceed"); diff --git a/gui/wizard/src/gameimage/fetch.rs b/gui/wizard/src/gameimage/fetch.rs index debcc39..2922d7e 100644 --- a/gui/wizard/src/gameimage/fetch.rs +++ b/gui/wizard/src/gameimage/fetch.rs @@ -50,4 +50,37 @@ pub fn query_files() -> anyhow::Result> Ok(query("files")?) } // query_files() }}} +// validate() {{{ +pub fn validate() -> anyhow::Result +{ + let platform = gameimage::gameimage::platform()?; + + let rc = gameimage::gameimage::gameimage_sync(vec! + [ + "fetch" + , "--platform", &platform + , "--sha" + ]); + + if rc == 0 { return Ok(rc); } + + Err(ah!("Exit with error code {}", rc)) +} // validate() }}} + +// configure() {{{ +pub fn configure() -> anyhow::Result +{ + let platform = gameimage::gameimage::platform()?; + + let rc = gameimage::gameimage::gameimage_sync(vec! + [ + "fetch" + , "--platform", &platform + ]); + + if rc == 0 { return Ok(rc); } + + Err(ah!("Exit with error code {}", rc)) +} // configure() }}} + // vim: set expandtab fdm=marker ts=2 sw=2 tw=100 et : From 7031b07f9678dc251f77483589c4363e6f936879 Mon Sep 17 00:00:00 2001 From: "Ruan E. Formigoni" Date: Wed, 10 Apr 2024 19:24:33 -0300 Subject: [PATCH 5/6] Custom url file fetch handled by backend --- gui/wizard/src/frame/platform.rs | 34 +++++- gui/wizard/src/gameimage/fetch.rs | 46 ++++++++ gui/wizard/src/gameimage/gameimage.rs | 2 +- src/cmd/fetch.hpp | 159 +++++++++++++++++--------- src/common.hpp | 17 +++ src/lib/parser.hpp | 10 +- src/main.cpp | 29 +++-- 7 files changed, 227 insertions(+), 70 deletions(-) diff --git a/gui/wizard/src/frame/platform.rs b/gui/wizard/src/frame/platform.rs index c3aa289..2f52691 100644 --- a/gui/wizard/src/frame/platform.rs +++ b/gui/wizard/src/frame/platform.rs @@ -16,6 +16,7 @@ use crate::common; use crate::common::WidgetExtExtra; use crate::common::FltkSenderExt; use crate::log; +use crate::gameimage; // enum Platform {{{ #[derive(PartialEq, Clone)] @@ -166,7 +167,10 @@ pub fn platform(tx: Sender, title: &str) if *lock != Some(Platform::WineUrl) && let Ok(mut guard) = URL.lock() { *guard = None; - env::remove_var("GIMG_FETCH_URL_DWARFS"); + if let Err(e) = gameimage::fetch::url_clear() + { + log!("Could not clear custom url: {}", e); + } // if } // if if let Some(platform) = lock.clone() @@ -207,7 +211,33 @@ pub fn platform(tx: Sender, title: &str) ret_frame_footer.btn_prev.clone().emit(tx, common::Msg::DrawWelcome); // Set callback for next - ret_frame_footer.btn_next.clone().emit(tx, common::Msg::DrawFetch); + let mut clone_btn_next = ret_frame_footer.btn_next.clone(); + let mut clone_output_status = ret_frame_footer.output_status.clone(); + let clone_tx = tx.clone(); + clone_btn_next.set_callback(move |_| + { + // Fetch files + clone_output_status.set_value("Fetching list of files to download"); + + // Disable window + clone_tx.send_awake(common::Msg::WindDeactivate); + + // Set custom url if it was passed + if let Ok(guard) = URL.lock() && let Some(url) = guard.clone() + { + if let Err(e) = gameimage::fetch::set_url_dwarfs(&url) + { + log!("Exit backend with error: {}", e); + } // if + } // match + else + { + log!("Could not set custom url"); + } // else + + // Disable window + clone_tx.send_awake(common::Msg::DrawFetch); + }); } // }}} diff --git a/gui/wizard/src/gameimage/fetch.rs b/gui/wizard/src/gameimage/fetch.rs index 2922d7e..868f2fd 100644 --- a/gui/wizard/src/gameimage/fetch.rs +++ b/gui/wizard/src/gameimage/fetch.rs @@ -50,6 +50,52 @@ pub fn query_files() -> anyhow::Result> Ok(query("files")?) } // query_files() }}} +// url_clear() {{{ +pub fn url_clear() -> anyhow::Result +{ + let platform = gameimage::gameimage::platform()?; + + let rc = gameimage::gameimage::gameimage_sync(vec! + [ + "fetch" + , "--platform", &platform + , "--url-clear" + ]); + + if rc != 0 { return Err(ah!("backend exited with non-zero code: {}", rc)); } // if + + Ok(rc) +} // url_clear() }}} + +// set_url() {{{ +fn set_url(str_type : &str, str_url : &str) -> anyhow::Result +{ + let platform = gameimage::gameimage::platform()?; + + let rc = gameimage::gameimage::gameimage_sync(vec! + [ + "fetch" + , "--platform", &platform + , &str_type, &str_url + ]); + + if rc != 0 { return Err(ah!("backend exited with non-zero code: {}", rc)); } // if + + Ok(rc) +} // set_url() }}} + +// set_url_base() {{{ +pub fn set_url_base(str_url : &str) -> anyhow::Result +{ + Ok(set_url("--url-base", str_url)?) +} // set_url_base() }}} + +// set_url_dwarfs() {{{ +pub fn set_url_dwarfs(str_url : &str) -> anyhow::Result +{ + Ok(set_url("--url-dwarfs", str_url)?) +} // set_url_dwarfs() }}} + // validate() {{{ pub fn validate() -> anyhow::Result { diff --git a/gui/wizard/src/gameimage/gameimage.rs b/gui/wizard/src/gameimage/gameimage.rs index 300e4e4..130fc0d 100644 --- a/gui/wizard/src/gameimage/gameimage.rs +++ b/gui/wizard/src/gameimage/gameimage.rs @@ -31,7 +31,7 @@ pub fn gameimage_async(args : Vec<&str>) -> anyhow::Result> { dir_build()?; - let path_binary_gameimage = std::path::PathBuf::from(env::var("GIMG_BACKEND")?); + let path_binary_gameimage = binary()?; let handle = std::process::Command::new(path_binary_gameimage) .env_remove("LD_PRELOAD") diff --git a/src/cmd/fetch.hpp b/src/cmd/fetch.hpp index 6388833..f761aa2 100644 --- a/src/cmd/fetch.hpp +++ b/src/cmd/fetch.hpp @@ -26,6 +26,13 @@ namespace ns_fetch namespace fs = std::filesystem; +// enum class UrlType {{{ +enum class UrlType +{ + DWARFS, + BASE, +}; // }}} + // anonymous namespace namespace { @@ -347,15 +354,29 @@ inline void merge_base_and_dwarfs(std::string str_platform ns_subprocess::sync(path_file_image, "fim-dwarfs-add", path_file_dwarfs, "/fim/mount/{}"_fmt(str_platform)); } // merge_base_and_dwarfs() }}} +// url_get() {{{ +inline std::optional url_get(ns_enum::Platform platform, UrlType url_type) +{ + // Create image path + fs::path path_file_image = get_path_file_image(platform); + fs::path path_dir_image = path_file_image.parent_path(); + fs::path path_file_json = path_dir_image / "gameimage.fetch.json"; + + // Log + ns_log::write('i', "platform: ", ns_enum::to_string_lower(platform)); + ns_log::write('i', "image: ", path_file_image); + + // Get url and save path to base + return ns_common::catch_to_optional([&]{ return ns_db::query(path_file_json, ns_enum::to_string_lower(url_type)); }); +} // url_get() }}} + // url_resolve_base() {{{ -decltype(auto) url_resolve_base(ns_enum::Platform platform - , std::optional const& url - , fs::path path_dir_dst) +decltype(auto) url_resolve_base(ns_enum::Platform platform, fs::path path_dir_dst) { fetchlist_base_ret_t path_and_url_base; // Fetch from custom url - if ( url.has_value() ) + if ( auto url = url_get(platform, UrlType::BASE); url.has_value() ) { ns_log::write('i', "Download url: '", url->c_str()); path_and_url_base = { path_dir_dst / "{}.tar.xz"_fmt(ns_enum::to_string_lower(platform)), *url }; @@ -368,21 +389,17 @@ decltype(auto) url_resolve_base(ns_enum::Platform platform } // else return path_and_url_base; -} // function: url_resolve_base - -// url_resolve_base() }}} +} // url_resolve_base() }}} // url_resolve_dwarfs() {{{ -decltype(auto) url_resolve_dwarfs(ns_enum::Platform platform - , std::optional const& url - , fs::path path_dir_dst) +decltype(auto) url_resolve_dwarfs(ns_enum::Platform platform, fs::path path_dir_dst) { std::string str_platform = ns_enum::to_string_lower(platform); fetchlist_dwarfs_ret_t path_and_url_dwarfs; // Custom URL - if ( url.has_value() ) + if ( auto url = url_get(platform, UrlType::DWARFS); url.has_value() ) { if ( std::string{url->c_str()}.ends_with(".tar.xz") ) { @@ -396,10 +413,12 @@ decltype(auto) url_resolve_dwarfs(ns_enum::Platform platform { throw std::runtime_error("Unsupported file type for download"); } // else + ns_log::write('i', "Download url (custom): '", url->c_str()); } // if else { path_and_url_dwarfs = fetchlist_dwarfs(platform, path_dir_dst); + ns_log::write('i', "Download url (fetchlist): '", path_and_url_dwarfs.url.c_str()); } // else return path_and_url_dwarfs; @@ -413,12 +432,36 @@ void fetch_base(fetchlist_base_ret_t path_and_url) fetch_file_from_url_on_failed_check(path_and_url.path, path_and_url.url); } // fetch_base() }}} +// fetch_base() {{{ +inline fetchlist_base_ret_t fetch_base(ns_enum::Platform platform, fs::path path_dir_image) +{ + // Resolve custom url + fetchlist_base_ret_t path_and_url_base = url_resolve_base(platform, path_dir_image); + + // Fetch base + fetch_base(path_and_url_base); + + return path_and_url_base; +} // fetch() }}} + // fetch_dwarfs() {{{ void fetch_dwarfs(fetchlist_dwarfs_ret_t path_and_url) { fetch_file_from_url_on_failed_check(path_and_url.path, path_and_url.url); } // fetch_dwarfs() }}} +// fetch_dwarfs() {{{ +inline fetchlist_dwarfs_ret_t fetch_dwarfs(ns_enum::Platform platform, fs::path path_dir_image) +{ + // Resolve custom url + fetchlist_dwarfs_ret_t path_and_url_dwarfs = url_resolve_dwarfs(platform, path_dir_image); + + // Fetch dwarfs + fetch_dwarfs(path_and_url_dwarfs); + + return path_and_url_dwarfs; +} // fetch() }}} + // build_dwarfs() {{{ decltype(auto) build_dwarfs(ns_enum::Platform platform , fetchlist_dwarfs_ret_t path_and_url @@ -501,39 +544,8 @@ inline decltype(auto) cores_list(fs::path const& path_dir_fetch) return vector_cores; } // get_files_by_platform() }}} -// fetch_base() {{{ -inline fetchlist_base_ret_t fetch_base(ns_enum::Platform platform - , fs::path path_dir_image - , std::optional const& url_base = std::nullopt) -{ - // Resolve custom url - fetchlist_base_ret_t path_and_url_base = url_resolve_base(platform, url_base, path_dir_image); - - // Fetch base - fetch_base(path_and_url_base); - - return path_and_url_base; -} // fetch() }}} - -// fetch_dwarfs() {{{ -inline fetchlist_dwarfs_ret_t fetch_dwarfs(ns_enum::Platform platform - , fs::path path_dir_image - , std::optional const& url_dwarfs = std::nullopt) -{ - // Resolve custom url - fetchlist_dwarfs_ret_t path_and_url_dwarfs = url_resolve_dwarfs(platform, url_dwarfs, path_dir_image); - - // Fetch dwarfs - fetch_dwarfs(path_and_url_dwarfs); - - return path_and_url_dwarfs; -} // fetch() }}} - // fetch() {{{ -inline void fetch(ns_enum::Platform platform - , std::optional const& url_base = std::nullopt - , std::optional const& url_dwarfs = std::nullopt - , std::optional const& only_file = std::nullopt) +inline void fetch(ns_enum::Platform platform, std::optional const& only_file = std::nullopt) { // Create image path fs::path path_file_image = get_path_file_image(platform); @@ -541,25 +553,25 @@ inline void fetch(ns_enum::Platform platform if ( only_file and (only_file->string().ends_with(".dwarfs") or only_file->string().ends_with(".dwarfs.tar.xz"))) { - fetch_dwarfs(platform, path_dir_image, url_dwarfs); + fetch_dwarfs(platform, path_dir_image); return; } // else if if ( only_file and only_file->string().ends_with(".tar.xz")) { - fetch_base(platform, path_dir_image, url_base); + fetch_base(platform, path_dir_image); return; } // if // Verify & configure base - fetchlist_base_ret_t path_and_url_base = fetch_base(platform, path_dir_image, url_base); + fetchlist_base_ret_t path_and_url_base = fetch_base(platform, path_dir_image); tarball_extract_flatimage(path_and_url_base.path, path_file_image); // No dwarfs for linux if ( platform == ns_enum::Platform::LINUX ) { return; } // Verify & configure dwarfs - fetchlist_dwarfs_ret_t path_and_url_dwarfs = fetch_dwarfs(platform, path_dir_image, url_dwarfs); + fetchlist_dwarfs_ret_t path_and_url_dwarfs = fetch_dwarfs(platform, path_dir_image); build_dwarfs(platform, path_and_url_dwarfs, path_file_image); // Remove .dwarfs.tar.xz to get built dwarfs file path @@ -575,9 +587,7 @@ inline void fetch(ns_enum::Platform platform } // fetch() }}} // sha() {{{ -inline void sha(ns_enum::Platform platform - , std::optional const& url_base = std::nullopt - , std::optional const& url_dwarfs = std::nullopt) +inline void sha(ns_enum::Platform platform) { // Create image path fs::path path_file_image = get_path_file_image(platform); @@ -589,7 +599,7 @@ inline void sha(ns_enum::Platform platform ns_log::write('i', "Only checking SHA"); // Get base - auto path_and_url_base = url_resolve_base(platform, url_base, path_dir_image); + auto path_and_url_base = url_resolve_base(platform, path_dir_image); // Check sha for base check_file(path_and_url_base.path, path_and_url_base.url); @@ -598,7 +608,7 @@ inline void sha(ns_enum::Platform platform if ( platform == ns_enum::Platform::LINUX ) { return; } // Get dwarfs - auto path_and_url_dwarfs = url_resolve_dwarfs(platform, url_dwarfs, path_dir_image); + auto path_and_url_dwarfs = url_resolve_dwarfs(platform, path_dir_image); // Check sha for dwarfs check_file(path_and_url_dwarfs.path, path_and_url_dwarfs.url); @@ -630,25 +640,62 @@ inline void ipc(ns_enum::Platform platform , std::optional query) { case IpcQuery::FILES: { - fetchlist_base_ret_t path_and_url_base = url_resolve_base(platform, std::nullopt, path_dir_image); + fetchlist_base_ret_t path_and_url_base = url_resolve_base(platform, path_dir_image); ipc.send(path_and_url_base.path); if ( platform == ns_enum::Platform::LINUX ) { return; } - fetchlist_dwarfs_ret_t path_and_url_dwarfs = url_resolve_dwarfs(platform, std::nullopt, path_dir_image); + fetchlist_dwarfs_ret_t path_and_url_dwarfs = url_resolve_dwarfs(platform, path_dir_image); ipc.send(path_and_url_dwarfs.path); } // case break; case IpcQuery::URLS: { - fetchlist_base_ret_t path_and_url_base = url_resolve_base(platform, std::nullopt, path_dir_image); + fetchlist_base_ret_t path_and_url_base = url_resolve_base(platform, path_dir_image); ipc.send(path_and_url_base.url); if ( platform == ns_enum::Platform::LINUX ) { return; } - fetchlist_dwarfs_ret_t path_and_url_dwarfs = url_resolve_dwarfs(platform, std::nullopt, path_dir_image); + fetchlist_dwarfs_ret_t path_and_url_dwarfs = url_resolve_dwarfs(platform, path_dir_image); ipc.send(path_and_url_dwarfs.url); } // case break; } // switch } // ipc() }}} +// url_set() {{{ +inline void url_set(ns_enum::Platform platform + , std::optional opt_str_url + , UrlType url_type +) +{ + if ( not opt_str_url.has_value() ) + { + "No url was provided"_throw(); + } // if + + // Create image path + fs::path path_file_image = get_path_file_image(platform); + fs::path path_dir_image = path_file_image.parent_path(); + fs::path path_file_json = path_dir_image / "gameimage.fetch.json"; + + // Log + ns_log::write('i', "platform: ", ns_enum::to_string_lower(platform)); + ns_log::write('i', "image: ", path_file_image); + + // Get url and save path to base + ns_db::from_file(path_file_json, [&](auto&& db) + { + db(ns_enum::to_string_lower(url_type)) = *opt_str_url; + }, ns_db::Mode::CREATE); +} // url_set() }}} + +// url_clear() {{{ +inline void url_clear(ns_enum::Platform platform) +{ + // Create image path + fs::path path_file_image = get_path_file_image(platform); + fs::path path_dir_image = path_file_image.parent_path(); + fs::path path_file_json = path_dir_image / "gameimage.fetch.json"; + fs::remove(path_file_json); +} // url_clear() }}} + } // namespace ns_fetch /* vim: set expandtab fdm=marker ts=2 sw=2 tw=100 et :*/ diff --git a/src/common.hpp b/src/common.hpp index d0c939a..ff6afdb 100644 --- a/src/common.hpp +++ b/src/common.hpp @@ -115,6 +115,23 @@ constexpr bool check_and(T&& t, Args&&... flags) return static_cast(t) & ( static_cast(flags) & ... ); } // check_and() }}} +// catch_to_optional() {{{ +template +auto catch_to_optional(F&& f, Args&&... args) -> std::optional +{ + try + { + auto val = make_optional(f(std::forward(args)...)); + ns_log::write('i', "Optional: ", *val); + return val; + } // try + catch(std::exception const& e) + { + ns_log::write('i', "Optional (caught): ", e.what()); + return std::nullopt; + } // catch +} // catch_to_optional() }}} + } // namespace ns_common}}} diff --git a/src/lib/parser.hpp b/src/lib/parser.hpp index 35b38a2..82c34ee 100644 --- a/src/lib/parser.hpp +++ b/src/lib/parser.hpp @@ -131,11 +131,17 @@ class Fetch final : public Parser // Set custom base url m_parser->add_argument("--url-base") .action([&](std::string const& s){ m_map_option_value["--url-base"]=s; }) - .help("Custom url for the base"); + .help("Set custom url for the base"); // Set custom dwarfs url m_parser->add_argument("--url-dwarfs") .action([&](std::string const& s){ m_map_option_value["--url-dwarfs"]=s; }) - .help("Custom url to for dwarfs"); + .help("Set custom url to for dwarfs"); + // Clear custom urls url + m_parser->add_argument("--url-clear") + .default_value(false) + .implicit_value(true) + .action([&](std::string const& s){ m_map_option_value["--url-clear"]=s; }) + .help("Clear custom urls"); // Set platform m_parser->add_argument("--platform") .action([&](std::string const& s){ m_map_option_value["--platform"]=s; }) diff --git a/src/main.cpp b/src/main.cpp index 88127a8..2ea847a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -37,12 +37,27 @@ void fetch(ns_parser::Parser const& parser) { ns_enum::Platform platform = ns_enum::from_string(parser["--platform"]); + if ( parser.optional("--url-base") ) + { + ns_fetch::url_set(platform, parser.optional("--url-base"), ns_fetch::UrlType::BASE); + return; + } // if + + if ( parser.optional("--url-dwarfs") ) + { + ns_fetch::url_set(platform, parser.optional("--url-dwarfs"), ns_fetch::UrlType::DWARFS); + return; + } // if + + if ( parser.contains("--url-clear") ) + { + ns_fetch::url_clear(platform); + return; + } // if + if ( parser.contains("--sha") ) { - ns_fetch::sha(platform - , parser.optional("--url-base") - , parser.optional("--url-dwarfs") - ); + ns_fetch::sha(platform); return; } // if @@ -52,11 +67,7 @@ void fetch(ns_parser::Parser const& parser) return; } // if - ns_fetch::fetch(platform - , parser.optional("--url-base") - , parser.optional("--url-dwarfs") - , parser.optional("--only-file") - ); + ns_fetch::fetch(platform, parser.optional("--only-file")); } // fetch() }}} // init() {{{ From 9628ae60ab19a0de19be71a1867091f44e77194f Mon Sep 17 00:00:00 2001 From: "Ruan E. Formigoni" Date: Wed, 10 Apr 2024 20:06:31 -0300 Subject: [PATCH 6/6] Send stderr to logger in gameimage_(a)sync --- gui/wizard/src/frame/fetch.rs | 2 - gui/wizard/src/gameimage/gameimage.rs | 89 ++++++++++++++++++--------- 2 files changed, 59 insertions(+), 32 deletions(-) diff --git a/gui/wizard/src/frame/fetch.rs b/gui/wizard/src/frame/fetch.rs index fd2c135..d4127dc 100644 --- a/gui/wizard/src/frame/fetch.rs +++ b/gui/wizard/src/frame/fetch.rs @@ -322,8 +322,6 @@ pub fn fetch(tx: Sender, title: &str) clone_tx.send_awake(common::Msg::DrawCreator); }); - - // Export name for expected image path }); } // }}} diff --git a/gui/wizard/src/gameimage/gameimage.rs b/gui/wizard/src/gameimage/gameimage.rs index 130fc0d..dadf364 100644 --- a/gui/wizard/src/gameimage/gameimage.rs +++ b/gui/wizard/src/gameimage/gameimage.rs @@ -33,58 +33,87 @@ pub fn gameimage_async(args : Vec<&str>) -> anyhow::Result> let path_binary_gameimage = binary()?; - let handle = std::process::Command::new(path_binary_gameimage) + let mut handle = std::process::Command::new(path_binary_gameimage) .env_remove("LD_PRELOAD") .env("FIM_FIFO", "0") - .stderr(std::process::Stdio::inherit()) + .stderr(std::process::Stdio::piped()) .stdout(std::process::Stdio::piped()) .args(args) .spawn()?; // Create arc reader for stdout + let arc_stdout = Arc::new(Mutex::new(handle.stdout.take())); + let arc_stderr = Arc::new(Mutex::new(handle.stderr.take())); let arc_handle = Arc::new(Mutex::new(handle)); - // Clone process handle - let clone_arc_handle = arc_handle.clone(); - // Create t/r let (tx, rx) = mpsc::channel(); std::thread::spawn(move || { - // Acquire stdout - let mut lock = - if let Ok(lock) = clone_arc_handle.lock() && lock.stdout.is_some() - { - lock - } - else + let clone_arc_stdout = arc_stdout.clone(); + let clone_tx = tx.clone(); + std::thread::spawn(move || + { + // Acquire stdout + let mut lock_stdout = match clone_arc_stdout.lock() { - log!("Could not acquire lock"); - let _ = tx.send(1); - return; - }; // else + Ok(lock) => lock, + Err(e) => { log!("Could not acquire lock (stdout): {}", e); let _ = clone_tx.send(1); return; }, + }; - // Create buf - let mut buf = vec![0; 4096]; + // Create buf + let mut buf = vec![0; 4096]; - // Use buf to write buf to stdout & stderr - loop - { - std::thread::sleep(std::time::Duration::from_millis(50)); + // Use buf to write buf to stdout + loop + { + std::thread::sleep(std::time::Duration::from_millis(50)); + let bytes_read = match lock_stdout.as_mut().unwrap().read(&mut buf) + { + Ok(bytes_read) => bytes_read, + Err(_) => break, + }; + if bytes_read == 0 { break; } + let mut output = String::from_utf8_lossy(&buf[..bytes_read]).to_string(); + output = output.trim().to_string(); + log!("{}", &output); + } + }); - let bytes_read = match lock.stdout.as_mut().unwrap().read(&mut buf) + let clone_arc_stderr = arc_stderr.clone(); + let clone_tx = tx.clone(); + std::thread::spawn(move || + { + // Acquire stderr + let mut lock_stderr = match clone_arc_stderr.lock() { - Ok(bytes_read) => bytes_read, - Err(_) => break, + Ok(lock) => lock, + Err(e) => { log!("Could not acquire lock (stderr): {}", e); let _ = clone_tx.send(1); return; }, }; - if bytes_read == 0 { break; } - let output = String::from_utf8_lossy(&buf[..bytes_read]); - log!("{}", &output); - } + // Create buf + let mut buf = vec![0; 4096]; + + // Use buf to write buf to stderr + loop + { + std::thread::sleep(std::time::Duration::from_millis(50)); + let bytes_read = match lock_stderr.as_mut().unwrap().read(&mut buf) + { + Ok(bytes_read) => bytes_read, + Err(_) => break, + }; + if bytes_read == 0 { break; } + let mut output = String::from_utf8_lossy(&buf[..bytes_read]).to_string(); + output = output.trim().to_string(); + log!("{}", &output); + } + }); - if let Ok(status) = lock.wait() && let Some(code) = status.code() + if let Ok(mut guard) = arc_handle.lock() + && let Ok(status) = guard.wait() + && let Some(code) = status.code() { let _ = tx.send(code); }