mirror of
https://github.com/rustdesk/rustdesk.git
synced 2026-03-07 12:20:03 +03:00
Fix some single device multiple ids scenarios on MacOS (#14196)
* fix(macos): sync config to root when root config is empty Signed-off-by: 21pages <sunboeasy@gmail.com> * fix(server): gate startup on initial config sync; document CheckIfResendPk limitation - wait up to 3s for initial root->local config sync before starting server services - continue startup when timeout is hit, while keeping sync/watch running in background - avoid blocking non-server process startup - clarify that CheckIfResendPk only re-registers PK for current ID and does not solve multi-ID when root uses a non-default mac-generated ID Signed-off-by: 21pages <sunboeasy@gmail.com> --------- Signed-off-by: 21pages <sunboeasy@gmail.com>
This commit is contained in:
@@ -40,6 +40,7 @@ lazy_static::lazy_static! {
|
|||||||
}
|
}
|
||||||
static SHOULD_EXIT: AtomicBool = AtomicBool::new(false);
|
static SHOULD_EXIT: AtomicBool = AtomicBool::new(false);
|
||||||
static MANUAL_RESTARTED: AtomicBool = AtomicBool::new(false);
|
static MANUAL_RESTARTED: AtomicBool = AtomicBool::new(false);
|
||||||
|
static SENT_REGISTER_PK: AtomicBool = AtomicBool::new(false);
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct RendezvousMediator {
|
pub struct RendezvousMediator {
|
||||||
@@ -689,6 +690,7 @@ impl RendezvousMediator {
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
});
|
});
|
||||||
socket.send(&msg_out).await?;
|
socket.send(&msg_out).await?;
|
||||||
|
SENT_REGISTER_PK.store(true, Ordering::SeqCst);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -904,3 +906,28 @@ async fn udp_nat_listen(
|
|||||||
})?;
|
})?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// When config is not yet synced from root, register_pk may have already been sent with a new generated pk.
|
||||||
|
// After config sync completes, the pk may change. This struct detects pk changes and triggers
|
||||||
|
// a re-registration by setting key_confirmed to false.
|
||||||
|
// NOTE:
|
||||||
|
// This only corrects PK registration for the current ID. If root uses a non-default mac-generated ID,
|
||||||
|
// this does not resolve the multi-ID issue by itself.
|
||||||
|
pub struct CheckIfResendPk {
|
||||||
|
pk: Option<Vec<u8>>,
|
||||||
|
}
|
||||||
|
impl CheckIfResendPk {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
pk: Config::get_cached_pk(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Drop for CheckIfResendPk {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
if SENT_REGISTER_PK.load(Ordering::SeqCst) && Config::get_cached_pk() != self.pk {
|
||||||
|
Config::set_key_confirmed(false);
|
||||||
|
log::info!("Set key_confirmed to false due to pk changed, will resend register_pk");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -82,6 +82,10 @@ type ConnMap = HashMap<i32, ConnInner>;
|
|||||||
|
|
||||||
#[cfg(any(target_os = "macos", target_os = "linux"))]
|
#[cfg(any(target_os = "macos", target_os = "linux"))]
|
||||||
const CONFIG_SYNC_INTERVAL_SECS: f32 = 0.3;
|
const CONFIG_SYNC_INTERVAL_SECS: f32 = 0.3;
|
||||||
|
#[cfg(any(target_os = "macos", target_os = "linux"))]
|
||||||
|
// 3s is enough for at least one initial sync attempt:
|
||||||
|
// 0.3s backoff + up to 1s connect timeout + up to 1s response timeout.
|
||||||
|
const CONFIG_SYNC_INITIAL_WAIT_SECS: u64 = 3;
|
||||||
|
|
||||||
lazy_static::lazy_static! {
|
lazy_static::lazy_static! {
|
||||||
pub static ref CHILD_PROCESS: Childs = Default::default();
|
pub static ref CHILD_PROCESS: Childs = Default::default();
|
||||||
@@ -600,7 +604,7 @@ pub async fn start_server(is_server: bool, no_server: bool) {
|
|||||||
allow_err!(input_service::setup_uinput(0, 1920, 0, 1080).await);
|
allow_err!(input_service::setup_uinput(0, 1920, 0, 1080).await);
|
||||||
}
|
}
|
||||||
#[cfg(any(target_os = "macos", target_os = "linux"))]
|
#[cfg(any(target_os = "macos", target_os = "linux"))]
|
||||||
tokio::spawn(async { sync_and_watch_config_dir().await });
|
wait_initial_config_sync().await;
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
crate::platform::try_kill_broker();
|
crate::platform::try_kill_broker();
|
||||||
#[cfg(feature = "hwcodec")]
|
#[cfg(feature = "hwcodec")]
|
||||||
@@ -685,13 +689,43 @@ pub async fn start_ipc_url_server() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(target_os = "macos", target_os = "linux"))]
|
#[cfg(any(target_os = "macos", target_os = "linux"))]
|
||||||
async fn sync_and_watch_config_dir() {
|
async fn wait_initial_config_sync() {
|
||||||
if crate::platform::is_root() {
|
if crate::platform::is_root() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Non-server process should not block startup, but still keeps background sync/watch alive.
|
||||||
|
if !crate::is_server() {
|
||||||
|
tokio::spawn(async move {
|
||||||
|
sync_and_watch_config_dir(None).await;
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let (sync_done_tx, mut sync_done_rx) = tokio::sync::oneshot::channel::<()>();
|
||||||
|
tokio::spawn(async move {
|
||||||
|
sync_and_watch_config_dir(Some(sync_done_tx)).await;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Server process waits up to N seconds for initial root->local sync to reduce stale-start window.
|
||||||
|
tokio::select! {
|
||||||
|
_ = &mut sync_done_rx => {
|
||||||
|
}
|
||||||
|
_ = tokio::time::sleep(Duration::from_secs(CONFIG_SYNC_INITIAL_WAIT_SECS)) => {
|
||||||
|
log::warn!(
|
||||||
|
"timed out waiting {}s for initial config sync, continue startup and keep syncing in background",
|
||||||
|
CONFIG_SYNC_INITIAL_WAIT_SECS
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(any(target_os = "macos", target_os = "linux"))]
|
||||||
|
async fn sync_and_watch_config_dir(sync_done_tx: Option<tokio::sync::oneshot::Sender<()>>) {
|
||||||
let mut cfg0 = (Config::get(), Config2::get());
|
let mut cfg0 = (Config::get(), Config2::get());
|
||||||
let mut synced = false;
|
let mut synced = false;
|
||||||
|
let mut is_root_config_empty = false;
|
||||||
|
let mut sync_done_tx = sync_done_tx;
|
||||||
let tries = if crate::is_server() { 30 } else { 3 };
|
let tries = if crate::is_server() { 30 } else { 3 };
|
||||||
log::debug!("#tries of ipc service connection: {}", tries);
|
log::debug!("#tries of ipc service connection: {}", tries);
|
||||||
use hbb_common::sleep;
|
use hbb_common::sleep;
|
||||||
@@ -706,6 +740,8 @@ async fn sync_and_watch_config_dir() {
|
|||||||
Data::SyncConfig(Some(configs)) => {
|
Data::SyncConfig(Some(configs)) => {
|
||||||
let (config, config2) = *configs;
|
let (config, config2) = *configs;
|
||||||
let _chk = crate::ipc::CheckIfRestart::new();
|
let _chk = crate::ipc::CheckIfRestart::new();
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
let _chk_pk = crate::CheckIfResendPk::new();
|
||||||
if !config.is_empty() {
|
if !config.is_empty() {
|
||||||
if cfg0.0 != config {
|
if cfg0.0 != config {
|
||||||
cfg0.0 = config.clone();
|
cfg0.0 = config.clone();
|
||||||
@@ -717,8 +753,20 @@ async fn sync_and_watch_config_dir() {
|
|||||||
Config2::set(config2);
|
Config2::set(config2);
|
||||||
log::info!("sync config2 from root");
|
log::info!("sync config2 from root");
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// only on macos, because this issue was only reproduced on macos
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
{
|
||||||
|
// root config is empty, mark for sync in watch loop
|
||||||
|
// to prevent root from generating a new config on login screen
|
||||||
|
is_root_config_empty = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
synced = true;
|
synced = true;
|
||||||
|
// Notify startup waiter once initial sync phase finishes successfully.
|
||||||
|
if let Some(tx) = sync_done_tx.take() {
|
||||||
|
let _ = tx.send(());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
};
|
};
|
||||||
@@ -729,8 +777,14 @@ async fn sync_and_watch_config_dir() {
|
|||||||
loop {
|
loop {
|
||||||
sleep(CONFIG_SYNC_INTERVAL_SECS).await;
|
sleep(CONFIG_SYNC_INTERVAL_SECS).await;
|
||||||
let cfg = (Config::get(), Config2::get());
|
let cfg = (Config::get(), Config2::get());
|
||||||
if cfg != cfg0 {
|
let should_sync =
|
||||||
log::info!("config updated, sync to root");
|
cfg != cfg0 || (is_root_config_empty && !cfg.0.is_empty());
|
||||||
|
if should_sync {
|
||||||
|
if is_root_config_empty {
|
||||||
|
log::info!("root config is empty, sync our config to root");
|
||||||
|
} else {
|
||||||
|
log::info!("config updated, sync to root");
|
||||||
|
}
|
||||||
match conn.send(&Data::SyncConfig(Some(cfg.clone().into()))).await {
|
match conn.send(&Data::SyncConfig(Some(cfg.clone().into()))).await {
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
log::error!("sync config to root failed: {}", e);
|
log::error!("sync config to root failed: {}", e);
|
||||||
@@ -745,6 +799,7 @@ async fn sync_and_watch_config_dir() {
|
|||||||
_ => {
|
_ => {
|
||||||
cfg0 = cfg;
|
cfg0 = cfg;
|
||||||
conn.next_timeout(1000).await.ok();
|
conn.next_timeout(1000).await.ok();
|
||||||
|
is_root_config_empty = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -755,6 +810,10 @@ async fn sync_and_watch_config_dir() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Notify startup waiter even when initial sync is skipped/failed, to avoid unnecessary waiting.
|
||||||
|
if let Some(tx) = sync_done_tx.take() {
|
||||||
|
let _ = tx.send(());
|
||||||
|
}
|
||||||
log::warn!("skipped config sync");
|
log::warn!("skipped config sync");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user