From 3ba2b9d2c0353f15ade82bc268b1fb2f5acf1172 Mon Sep 17 00:00:00 2001 From: Nicolas Luck Date: Mon, 28 Apr 2025 22:02:09 +0200 Subject: [PATCH 1/8] Move portpicker from UI to rust-executor --- Cargo.lock | 2 +- rust-executor/Cargo.toml | 1 + rust-executor/src/lib.rs | 2 +- rust-executor/src/utils.rs | 14 ++++++++++++++ ui/src-tauri/Cargo.toml | 1 - ui/src-tauri/src/lib.rs | 2 +- ui/src-tauri/src/util.rs | 13 ------------- 7 files changed, 18 insertions(+), 17 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6cbebee28..ceca37b8c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -117,6 +117,7 @@ dependencies = [ "multihash", "once_cell", "os_info", + "portpicker", "rand 0.8.5", "regex", "reqwest 0.11.20", @@ -161,7 +162,6 @@ dependencies = [ "nix 0.23.2", "open 2.1.3", "opener", - "portpicker", "remove_dir_all", "reqwest 0.11.20", "serde", diff --git a/rust-executor/Cargo.toml b/rust-executor/Cargo.toml index 6d7543459..c8e79ced3 100644 --- a/rust-executor/Cargo.toml +++ b/rust-executor/Cargo.toml @@ -116,6 +116,7 @@ rodio = "*" libc = "0.2" chat-gpt-lib-rs = { version = "0.5.1", git = "https://github.com/coasys/chat-gpt-lib-rs" } anyhow = "1.0.95" +portpicker = "0.1.1" [dev-dependencies] maplit = "1.0.2" diff --git a/rust-executor/src/lib.rs b/rust-executor/src/lib.rs index 35b2a8ab2..bdc837635 100644 --- a/rust-executor/src/lib.rs +++ b/rust-executor/src/lib.rs @@ -9,7 +9,7 @@ mod holochain_service; mod js_core; mod prolog_service; mod runtime_service; -mod utils; +pub mod utils; mod wallet; pub mod agent; diff --git a/rust-executor/src/utils.rs b/rust-executor/src/utils.rs index 66ec01c43..568ab9ea3 100644 --- a/rust-executor/src/utils.rs +++ b/rust-executor/src/utils.rs @@ -1,6 +1,20 @@ use dirs::home_dir; use std::path::PathBuf; +use portpicker; pub(crate) fn ad4m_data_directory() -> PathBuf { home_dir().unwrap().join(".ad4m") } + +pub fn find_port(start_port: u16, end_port: u16) -> u16 { + for x in start_port..end_port { + if portpicker::is_free(x) { + return x; + } + } + + panic!( + "No open port found between: [{:?}, {:?}]", + start_port, end_port + ); +} diff --git a/ui/src-tauri/Cargo.toml b/ui/src-tauri/Cargo.toml index f3b4e9034..eb1c949f7 100644 --- a/ui/src-tauri/Cargo.toml +++ b/ui/src-tauri/Cargo.toml @@ -23,7 +23,6 @@ serde = { version = "1.0", features = ["derive"] } log = "0.4" log4rs = "1.0.0" nix = "0.23.1" -portpicker = "0.1.1" libc = "0.2" directories = "4.0.1" opener = "0.5.0" diff --git a/ui/src-tauri/src/lib.rs b/ui/src-tauri/src/lib.rs index 91f735d24..9b276ac9a 100644 --- a/ui/src-tauri/src/lib.rs +++ b/ui/src-tauri/src/lib.rs @@ -11,6 +11,7 @@ use libc::{rlimit, setrlimit, RLIMIT_NOFILE}; use log::LevelFilter; use log::{debug, error, info}; use rust_executor::Ad4mConfig; +use rust_executor::utils::find_port; use std::env; use std::fs; use std::fs::File; @@ -49,7 +50,6 @@ use crate::commands::state::{get_port, request_credential}; use crate::config::log_path; use crate::menu::reveal_log_file; -use crate::util::find_port; use crate::util::{create_main_window, save_executor_port}; use tauri::Manager; diff --git a/ui/src-tauri/src/util.rs b/ui/src-tauri/src/util.rs index 7819f8ea8..d12373236 100644 --- a/ui/src-tauri/src/util.rs +++ b/ui/src-tauri/src/util.rs @@ -12,19 +12,6 @@ use tauri::{AppHandle, Manager, WebviewUrl, WebviewWindowBuilder, Wry}; use tauri_plugin_positioner::Position; use tauri_plugin_positioner::WindowExt; -pub fn find_port(start_port: u16, end_port: u16) -> u16 { - for x in start_port..end_port { - if portpicker::is_free(x) { - return x; - } - } - - panic!( - "No open port found between: [{:?}, {:?}]", - start_port, end_port - ); -} - pub fn _has_processes_running(name: &str) -> usize { let processes = System::new_all(); let processes_by_name: Vec<&Process> = processes.processes_by_exact_name(name).collect(); From 6ac6decf6b1b088331a1e97ee8f0d9339f7d3315 Mon Sep 17 00:00:00 2001 From: Nicolas Luck Date: Mon, 28 Apr 2025 22:12:39 +0200 Subject: [PATCH 2/8] Find free ports in lib.rs --- rust-executor/src/lib.rs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/rust-executor/src/lib.rs b/rust-executor/src/lib.rs index bdc837635..c4b0823f6 100644 --- a/rust-executor/src/lib.rs +++ b/rust-executor/src/lib.rs @@ -34,13 +34,14 @@ use js_core::JsCore; use crate::{ agent::AgentService, ai_service::AIService, dapp_server::serve_dapp, db::Ad4mDb, languages::LanguageController, prolog_service::init_prolog_service, - runtime_service::RuntimeService, + runtime_service::RuntimeService, utils::find_port, }; pub use config::Ad4mConfig; pub use holochain_service::run_local_hc_services; use libc::{sigaction, sigemptyset, sighandler_t, SA_ONSTACK, SIGURG}; use std::ptr; + extern "C" fn handle_sigurg(_: libc::c_int) { //println!("Received SIGURG signal, but ignoring it."); } @@ -124,6 +125,18 @@ pub async fn run(mut config: Ad4mConfig) -> JoinHandle<()> { info!("Initializing Prolog service..."); init_prolog_service().await; + if config.gql_port.is_none() { + config.gql_port = Some(find_port(4000, 40000)); + } + + if config.hc_admin_port.is_none() { + config.hc_admin_port = Some(find_port(2000, 40000)); + } + + if config.hc_app_port.is_none() { + config.hc_app_port = Some(find_port(1337, 40000)); + } + info!("Starting js_core..."); let mut js_core_handle = JsCore::start(config.clone()).await; js_core_handle.initialized().await; From 5f0b2405bccb0296ad5f2bcb0a903c7f7a5b6019 Mon Sep 17 00:00:00 2001 From: Nicolas Luck Date: Mon, 28 Apr 2025 22:58:13 +0200 Subject: [PATCH 3/8] fmt --- rust-executor/src/lib.rs | 1 - rust-executor/src/utils.rs | 2 +- ui/src-tauri/src/lib.rs | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/rust-executor/src/lib.rs b/rust-executor/src/lib.rs index c4b0823f6..d5b355761 100644 --- a/rust-executor/src/lib.rs +++ b/rust-executor/src/lib.rs @@ -41,7 +41,6 @@ pub use holochain_service::run_local_hc_services; use libc::{sigaction, sigemptyset, sighandler_t, SA_ONSTACK, SIGURG}; use std::ptr; - extern "C" fn handle_sigurg(_: libc::c_int) { //println!("Received SIGURG signal, but ignoring it."); } diff --git a/rust-executor/src/utils.rs b/rust-executor/src/utils.rs index 568ab9ea3..60ad78d1b 100644 --- a/rust-executor/src/utils.rs +++ b/rust-executor/src/utils.rs @@ -1,6 +1,6 @@ use dirs::home_dir; -use std::path::PathBuf; use portpicker; +use std::path::PathBuf; pub(crate) fn ad4m_data_directory() -> PathBuf { home_dir().unwrap().join(".ad4m") diff --git a/ui/src-tauri/src/lib.rs b/ui/src-tauri/src/lib.rs index 9b276ac9a..4716f07e0 100644 --- a/ui/src-tauri/src/lib.rs +++ b/ui/src-tauri/src/lib.rs @@ -10,8 +10,8 @@ use colored::Colorize; use libc::{rlimit, setrlimit, RLIMIT_NOFILE}; use log::LevelFilter; use log::{debug, error, info}; -use rust_executor::Ad4mConfig; use rust_executor::utils::find_port; +use rust_executor::Ad4mConfig; use std::env; use std::fs; use std::fs::File; From 5b651817853b960de2a5dc7b8001d9899c0708e8 Mon Sep 17 00:00:00 2001 From: Nicolas Luck Date: Tue, 29 Apr 2025 14:49:03 +0200 Subject: [PATCH 4/8] Remove port scanning code from JS but keep an idempotent try to avoid problems with our (to be refactored) JS event loop --- executor/src/main.ts | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/executor/src/main.ts b/executor/src/main.ts index 411afdd46..8862105f5 100644 --- a/executor/src/main.ts +++ b/executor/src/main.ts @@ -83,14 +83,23 @@ export interface SeedFileSchema { export async function init(config: OuterConfig): Promise { let { appDataPath, networkBootstrapSeed, appLangAliases, bootstrapFixtures, languageLanguageOnly, - mocks, gqlPort, adminCredential, runDappServer, - dAppPort, logHolochainMetrics + adminCredential, logHolochainMetrics } = config - if(!gqlPort) gqlPort = 4000 - // Check to see if PORT 2000 & 1337 are available if not returns a random PORT - if(!config.hcPortAdmin) config.hcPortAdmin = await getPort({ port: 2000 }); - if(!config.hcPortApp) config.hcPortApp = await getPort({ port: 1337 }); - if(!dAppPort) dAppPort = await getPort({port: 4200}) + + // Moved the port check to Rust + // BUT: we have weird problem with our JS runtime + // (which will be refactored soon, when we move over the last remaining JS code to Rust) + // when this function doesn't actually some async I/O operations, the JS event loop doesn't + // seem to work for future JS calls. + // So this here is a hack that works for now. + // Putting it in a try/catch block to avoid the process from crashing if the port is already in use. + try { + await getPort({ port: 50000 }) + } catch (error) { + //ignore + } + + //await new Promise(resolve => setTimeout(resolve, 1000)); if(config.hcUseMdns === undefined) config.hcUseMdns = false if(config.hcUseProxy === undefined) config.hcUseProxy = true if(config.hcUseBootstrap === undefined) config.hcUseBootstrap = true From d3927d9f4aa062caccb9156be20cd67acf4c75f6 Mon Sep 17 00:00:00 2001 From: Nicolas Luck Date: Tue, 29 Apr 2025 14:50:16 +0200 Subject: [PATCH 5/8] whitespace --- rust-executor/src/js_core/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-executor/src/js_core/mod.rs b/rust-executor/src/js_core/mod.rs index 84a261501..ac063dff2 100644 --- a/rust-executor/src/js_core/mod.rs +++ b/rust-executor/src/js_core/mod.rs @@ -282,7 +282,7 @@ impl JsCore { .execute_async_smart(format!("initCore({})", config.get_json())) .await .expect("to be able to create js execution future") - .await ; + .await; match result { Ok(res) => { From 4a934e7a0ed2e2ec097d23e16807c8de6dd5877e Mon Sep 17 00:00:00 2001 From: Nicolas Luck Date: Tue, 29 Apr 2025 14:51:26 +0200 Subject: [PATCH 6/8] changelog --- CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG b/CHANGELOG index d5368de38..fac96b6b5 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -32,6 +32,7 @@ This project _loosely_ adheres to [Semantic Versioning](https://semver.org/spec/ - Fix handling of wrong passphrase during unlock (was frozen) [PR#596](https://github.com/coasys/ad4m/pull/596) - Fix open buttons not working on Linux by using Tauri's opener plugin [PR#599](https://github.com/coasys/ad4m/pull/599) - Harden setup of query subscriptions with delayed init handshakes in all cases and resubscribing after timeout [PR#601](https://github.com/coasys/ad4m/pull/601) +- Fix crash when default ports are used by other processes [PR#602](https://github.com/coasys/ad4m/pull/602) ### Added - Prolog predicates needed in new Flux mention notification trigger: From e9d380fc20b5cc9ff587901daa80150ec0ea75a5 Mon Sep 17 00:00:00 2001 From: Nicolas Luck Date: Tue, 29 Apr 2025 15:02:20 +0200 Subject: [PATCH 7/8] Handle no-free port in calling context with error log --- rust-executor/src/lib.rs | 27 ++++++++++++++++----------- rust-executor/src/utils.rs | 8 ++++---- ui/src-tauri/src/lib.rs | 6 +++++- 3 files changed, 25 insertions(+), 16 deletions(-) diff --git a/rust-executor/src/lib.rs b/rust-executor/src/lib.rs index d5b355761..4afb046f3 100644 --- a/rust-executor/src/lib.rs +++ b/rust-executor/src/lib.rs @@ -45,6 +45,19 @@ extern "C" fn handle_sigurg(_: libc::c_int) { //println!("Received SIGURG signal, but ignoring it."); } +fn find_and_set_port(config_port: &mut Option, start_port: u16, service_name: &str) { + if config_port.is_none() { + match find_port(start_port, 40000) { + Ok(port) => *config_port = Some(port), + Err(e) => { + let error_string = format!("Failed to find port for {}: {}", service_name, e); + error!("{}", error_string); + panic!("{}", error_string); + } + } + } +} + /// Runs the GraphQL server and the deno core runtime pub async fn run(mut config: Ad4mConfig) -> JoinHandle<()> { unsafe { @@ -124,17 +137,9 @@ pub async fn run(mut config: Ad4mConfig) -> JoinHandle<()> { info!("Initializing Prolog service..."); init_prolog_service().await; - if config.gql_port.is_none() { - config.gql_port = Some(find_port(4000, 40000)); - } - - if config.hc_admin_port.is_none() { - config.hc_admin_port = Some(find_port(2000, 40000)); - } - - if config.hc_app_port.is_none() { - config.hc_app_port = Some(find_port(1337, 40000)); - } + find_and_set_port(&mut config.gql_port, 4000, "GraphQL"); + find_and_set_port(&mut config.hc_admin_port, 2000, "Holochain admin"); + find_and_set_port(&mut config.hc_app_port, 1337, "Holochain app"); info!("Starting js_core..."); let mut js_core_handle = JsCore::start(config.clone()).await; diff --git a/rust-executor/src/utils.rs b/rust-executor/src/utils.rs index 60ad78d1b..7ab3d4f97 100644 --- a/rust-executor/src/utils.rs +++ b/rust-executor/src/utils.rs @@ -6,15 +6,15 @@ pub(crate) fn ad4m_data_directory() -> PathBuf { home_dir().unwrap().join(".ad4m") } -pub fn find_port(start_port: u16, end_port: u16) -> u16 { +pub fn find_port(start_port: u16, end_port: u16) -> Result { for x in start_port..end_port { if portpicker::is_free(x) { - return x; + return Ok(x); } } - panic!( + Err(format!( "No open port found between: [{:?}, {:?}]", start_port, end_port - ); + )) } diff --git a/ui/src-tauri/src/lib.rs b/ui/src-tauri/src/lib.rs index 4716f07e0..1d80aa13d 100644 --- a/ui/src-tauri/src/lib.rs +++ b/ui/src-tauri/src/lib.rs @@ -187,7 +187,11 @@ pub fn run() { tracing::subscriber::set_global_default(subscriber).expect("Failed to set tracing subscriber"); - let free_port = find_port(12000, 13000); + let free_port = find_port(12000, 13000).unwrap_or_else(|e| { + let error_string = format!("Failed to find free main executor interface port: {}", e); + error!("{}", error_string); + panic!("{}", error_string); + }); info!("Free port: {:?}", free_port); From a135d8bba047df86e7e27a99aecc784d292a81ba Mon Sep 17 00:00:00 2001 From: Nicolas Luck Date: Tue, 29 Apr 2025 15:14:47 +0200 Subject: [PATCH 8/8] Fix typo in comment Co-authored-by: James <34165044+jhweir@users.noreply.github.com> --- executor/src/main.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/executor/src/main.ts b/executor/src/main.ts index 8862105f5..7cdfe05bf 100644 --- a/executor/src/main.ts +++ b/executor/src/main.ts @@ -89,7 +89,7 @@ export async function init(config: OuterConfig): Promise { // Moved the port check to Rust // BUT: we have weird problem with our JS runtime // (which will be refactored soon, when we move over the last remaining JS code to Rust) - // when this function doesn't actually some async I/O operations, the JS event loop doesn't + // when this function doesn't actually do some async I/O operations, the JS event loop doesn't // seem to work for future JS calls. // So this here is a hack that works for now. // Putting it in a try/catch block to avoid the process from crashing if the port is already in use.