server.rs 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686
  1. use std::{
  2. collections::HashMap,
  3. net::SocketAddr,
  4. sync::{Arc, Mutex, RwLock, Weak},
  5. time::Duration,
  6. };
  7. use bytes::Bytes;
  8. pub use connection::*;
  9. #[cfg(not(any(target_os = "android", target_os = "ios")))]
  10. use hbb_common::config::Config2;
  11. use hbb_common::tcp::{self, new_listener};
  12. use hbb_common::{
  13. allow_err,
  14. anyhow::Context,
  15. bail,
  16. config::{Config, CONNECT_TIMEOUT, RELAY_PORT},
  17. log,
  18. message_proto::*,
  19. protobuf::{Enum, Message as _},
  20. rendezvous_proto::*,
  21. socket_client,
  22. sodiumoxide::crypto::{box_, sign},
  23. timeout, tokio, ResultType, Stream,
  24. };
  25. #[cfg(not(any(target_os = "android", target_os = "ios")))]
  26. use service::ServiceTmpl;
  27. use service::{EmptyExtraFieldService, GenericService, Service, Subscriber};
  28. use crate::ipc::Data;
  29. pub mod audio_service;
  30. cfg_if::cfg_if! {
  31. if #[cfg(not(target_os = "ios"))] {
  32. mod clipboard_service;
  33. #[cfg(target_os = "android")]
  34. pub use clipboard_service::is_clipboard_service_ok;
  35. #[cfg(target_os = "linux")]
  36. pub(crate) mod wayland;
  37. #[cfg(target_os = "linux")]
  38. pub mod uinput;
  39. #[cfg(target_os = "linux")]
  40. pub mod rdp_input;
  41. #[cfg(target_os = "linux")]
  42. pub mod dbus;
  43. #[cfg(not(target_os = "android"))]
  44. pub mod input_service;
  45. } else {
  46. mod clipboard_service {
  47. pub const NAME: &'static str = "";
  48. }
  49. }
  50. }
  51. #[cfg(any(target_os = "android", target_os = "ios"))]
  52. pub mod input_service {
  53. pub const NAME_CURSOR: &'static str = "";
  54. pub const NAME_POS: &'static str = "";
  55. pub const NAME_WINDOW_FOCUS: &'static str = "";
  56. }
  57. mod connection;
  58. pub mod display_service;
  59. #[cfg(windows)]
  60. pub mod portable_service;
  61. mod service;
  62. mod video_qos;
  63. pub mod video_service;
  64. pub type Childs = Arc<Mutex<Vec<std::process::Child>>>;
  65. type ConnMap = HashMap<i32, ConnInner>;
  66. #[cfg(any(target_os = "macos", target_os = "linux"))]
  67. const CONFIG_SYNC_INTERVAL_SECS: f32 = 0.3;
  68. lazy_static::lazy_static! {
  69. pub static ref CHILD_PROCESS: Childs = Default::default();
  70. pub static ref CONN_COUNT: Arc<Mutex<usize>> = Default::default();
  71. // A client server used to provide local services(audio, video, clipboard, etc.)
  72. // for all initiative connections.
  73. //
  74. // [Note]
  75. // ugly
  76. // Now we use this [`CLIENT_SERVER`] to do following operations:
  77. // - record local audio, and send to remote
  78. pub static ref CLIENT_SERVER: ServerPtr = new();
  79. }
  80. pub struct Server {
  81. connections: ConnMap,
  82. services: HashMap<String, Box<dyn Service>>,
  83. id_count: i32,
  84. }
  85. pub type ServerPtr = Arc<RwLock<Server>>;
  86. pub type ServerPtrWeak = Weak<RwLock<Server>>;
  87. pub fn new() -> ServerPtr {
  88. let mut server = Server {
  89. connections: HashMap::new(),
  90. services: HashMap::new(),
  91. id_count: hbb_common::rand::random::<i32>() % 1000 + 1000, // ensure positive
  92. };
  93. server.add_service(Box::new(audio_service::new()));
  94. #[cfg(not(target_os = "ios"))]
  95. {
  96. server.add_service(Box::new(display_service::new()));
  97. server.add_service(Box::new(clipboard_service::new(
  98. clipboard_service::NAME.to_owned(),
  99. )));
  100. #[cfg(feature = "unix-file-copy-paste")]
  101. server.add_service(Box::new(clipboard_service::new(
  102. clipboard_service::FILE_NAME.to_owned(),
  103. )));
  104. }
  105. #[cfg(not(any(target_os = "android", target_os = "ios")))]
  106. {
  107. if !display_service::capture_cursor_embedded() {
  108. server.add_service(Box::new(input_service::new_cursor()));
  109. server.add_service(Box::new(input_service::new_pos()));
  110. #[cfg(target_os = "linux")]
  111. if scrap::is_x11() {
  112. // wayland does not support multiple displays currently
  113. server.add_service(Box::new(input_service::new_window_focus()));
  114. }
  115. #[cfg(not(target_os = "linux"))]
  116. server.add_service(Box::new(input_service::new_window_focus()));
  117. }
  118. }
  119. Arc::new(RwLock::new(server))
  120. }
  121. async fn accept_connection_(server: ServerPtr, socket: Stream, secure: bool) -> ResultType<()> {
  122. let local_addr = socket.local_addr();
  123. drop(socket);
  124. // even we drop socket, below still may fail if not use reuse_addr,
  125. // there is TIME_WAIT before socket really released, so sometimes we
  126. // see “Only one usage of each socket address is normally permitted” on windows sometimes,
  127. let listener = new_listener(local_addr, true).await?;
  128. log::info!("Server listening on: {}", &listener.local_addr()?);
  129. if let Ok((stream, addr)) = timeout(CONNECT_TIMEOUT, listener.accept()).await? {
  130. stream.set_nodelay(true).ok();
  131. let stream_addr = stream.local_addr()?;
  132. create_tcp_connection(server, Stream::from(stream, stream_addr), addr, secure).await?;
  133. }
  134. Ok(())
  135. }
  136. pub async fn create_tcp_connection(
  137. server: ServerPtr,
  138. stream: Stream,
  139. addr: SocketAddr,
  140. secure: bool,
  141. ) -> ResultType<()> {
  142. let mut stream = stream;
  143. let id = server.write().unwrap().get_new_id();
  144. let (sk, pk) = Config::get_key_pair();
  145. if secure && pk.len() == sign::PUBLICKEYBYTES && sk.len() == sign::SECRETKEYBYTES {
  146. let mut sk_ = [0u8; sign::SECRETKEYBYTES];
  147. sk_[..].copy_from_slice(&sk);
  148. let sk = sign::SecretKey(sk_);
  149. let mut msg_out = Message::new();
  150. let (our_pk_b, our_sk_b) = box_::gen_keypair();
  151. msg_out.set_signed_id(SignedId {
  152. id: sign::sign(
  153. &IdPk {
  154. id: Config::get_id(),
  155. pk: Bytes::from(our_pk_b.0.to_vec()),
  156. ..Default::default()
  157. }
  158. .write_to_bytes()
  159. .unwrap_or_default(),
  160. &sk,
  161. )
  162. .into(),
  163. ..Default::default()
  164. });
  165. timeout(CONNECT_TIMEOUT, stream.send(&msg_out)).await??;
  166. match timeout(CONNECT_TIMEOUT, stream.next()).await? {
  167. Some(res) => {
  168. let bytes = res?;
  169. if let Ok(msg_in) = Message::parse_from_bytes(&bytes) {
  170. if let Some(message::Union::PublicKey(pk)) = msg_in.union {
  171. if pk.asymmetric_value.len() == box_::PUBLICKEYBYTES {
  172. stream.set_key(tcp::Encrypt::decode(
  173. &pk.symmetric_value,
  174. &pk.asymmetric_value,
  175. &our_sk_b,
  176. )?);
  177. } else if pk.asymmetric_value.is_empty() {
  178. Config::set_key_confirmed(false);
  179. log::info!("Force to update pk");
  180. } else {
  181. bail!("Handshake failed: invalid public sign key length from peer");
  182. }
  183. } else {
  184. log::error!("Handshake failed: invalid message type");
  185. }
  186. } else {
  187. bail!("Handshake failed: invalid message format");
  188. }
  189. }
  190. None => {
  191. bail!("Failed to receive public key");
  192. }
  193. }
  194. }
  195. #[cfg(target_os = "macos")]
  196. {
  197. use std::process::Command;
  198. Command::new("/usr/bin/caffeinate")
  199. .arg("-u")
  200. .arg("-t 5")
  201. .spawn()
  202. .ok();
  203. log::info!("wake up macos");
  204. }
  205. Connection::start(addr, stream, id, Arc::downgrade(&server)).await;
  206. Ok(())
  207. }
  208. pub async fn accept_connection(
  209. server: ServerPtr,
  210. socket: Stream,
  211. peer_addr: SocketAddr,
  212. secure: bool,
  213. ) {
  214. if let Err(err) = accept_connection_(server, socket, secure).await {
  215. log::error!("Failed to accept connection from {}: {}", peer_addr, err);
  216. }
  217. }
  218. pub async fn create_relay_connection(
  219. server: ServerPtr,
  220. relay_server: String,
  221. uuid: String,
  222. peer_addr: SocketAddr,
  223. secure: bool,
  224. ipv4: bool,
  225. ) {
  226. if let Err(err) =
  227. create_relay_connection_(server, relay_server, uuid.clone(), peer_addr, secure, ipv4).await
  228. {
  229. log::error!(
  230. "Failed to create relay connection for {} with uuid {}: {}",
  231. peer_addr,
  232. uuid,
  233. err
  234. );
  235. }
  236. }
  237. async fn create_relay_connection_(
  238. server: ServerPtr,
  239. relay_server: String,
  240. uuid: String,
  241. peer_addr: SocketAddr,
  242. secure: bool,
  243. ipv4: bool,
  244. ) -> ResultType<()> {
  245. let mut stream = socket_client::connect_tcp(
  246. socket_client::ipv4_to_ipv6(crate::check_port(relay_server, RELAY_PORT), ipv4),
  247. CONNECT_TIMEOUT,
  248. )
  249. .await?;
  250. let mut msg_out = RendezvousMessage::new();
  251. let licence_key = crate::get_key(true).await;
  252. msg_out.set_request_relay(RequestRelay {
  253. licence_key,
  254. uuid,
  255. ..Default::default()
  256. });
  257. stream.send(&msg_out).await?;
  258. create_tcp_connection(server, stream, peer_addr, secure).await?;
  259. Ok(())
  260. }
  261. impl Server {
  262. fn is_video_service_name(name: &str) -> bool {
  263. name.starts_with(video_service::NAME)
  264. }
  265. pub fn try_add_primay_video_service(&mut self) {
  266. let primary_video_service_name =
  267. video_service::get_service_name(*display_service::PRIMARY_DISPLAY_IDX);
  268. if !self.contains(&primary_video_service_name) {
  269. self.add_service(Box::new(video_service::new(
  270. *display_service::PRIMARY_DISPLAY_IDX,
  271. )));
  272. }
  273. }
  274. pub fn add_connection(&mut self, conn: ConnInner, noperms: &Vec<&'static str>) {
  275. let primary_video_service_name =
  276. video_service::get_service_name(*display_service::PRIMARY_DISPLAY_IDX);
  277. for s in self.services.values() {
  278. let name = s.name();
  279. if Self::is_video_service_name(&name) && name != primary_video_service_name {
  280. continue;
  281. }
  282. if !noperms.contains(&(&name as _)) {
  283. s.on_subscribe(conn.clone());
  284. }
  285. }
  286. #[cfg(target_os = "macos")]
  287. self.update_enable_retina();
  288. self.connections.insert(conn.id(), conn);
  289. *CONN_COUNT.lock().unwrap() = self.connections.len();
  290. }
  291. pub fn remove_connection(&mut self, conn: &ConnInner) {
  292. for s in self.services.values() {
  293. s.on_unsubscribe(conn.id());
  294. }
  295. self.connections.remove(&conn.id());
  296. *CONN_COUNT.lock().unwrap() = self.connections.len();
  297. #[cfg(target_os = "macos")]
  298. self.update_enable_retina();
  299. }
  300. pub fn close_connections(&mut self) {
  301. let conn_inners: Vec<_> = self.connections.values_mut().collect();
  302. for c in conn_inners {
  303. let mut misc = Misc::new();
  304. misc.set_stop_service(true);
  305. let mut msg = Message::new();
  306. msg.set_misc(misc);
  307. c.send(Arc::new(msg));
  308. }
  309. }
  310. fn add_service(&mut self, service: Box<dyn Service>) {
  311. let name = service.name();
  312. self.services.insert(name, service);
  313. }
  314. pub fn contains(&self, name: &str) -> bool {
  315. self.services.contains_key(name)
  316. }
  317. pub fn subscribe(&mut self, name: &str, conn: ConnInner, sub: bool) {
  318. if let Some(s) = self.services.get(name) {
  319. if s.is_subed(conn.id()) == sub {
  320. return;
  321. }
  322. if sub {
  323. s.on_subscribe(conn.clone());
  324. } else {
  325. s.on_unsubscribe(conn.id());
  326. }
  327. #[cfg(target_os = "macos")]
  328. self.update_enable_retina();
  329. }
  330. }
  331. // get a new unique id
  332. pub fn get_new_id(&mut self) -> i32 {
  333. self.id_count += 1;
  334. self.id_count
  335. }
  336. pub fn set_video_service_opt(&self, display: Option<usize>, opt: &str, value: &str) {
  337. for (k, v) in self.services.iter() {
  338. if let Some(display) = display {
  339. if k != &video_service::get_service_name(display) {
  340. continue;
  341. }
  342. }
  343. if Self::is_video_service_name(k) {
  344. v.set_option(opt, value);
  345. }
  346. }
  347. }
  348. fn get_subbed_displays_count(&self, conn_id: i32) -> usize {
  349. self.services
  350. .keys()
  351. .filter(|k| {
  352. Self::is_video_service_name(k)
  353. && self
  354. .services
  355. .get(*k)
  356. .map(|s| s.is_subed(conn_id))
  357. .unwrap_or(false)
  358. })
  359. .count()
  360. }
  361. fn capture_displays(
  362. &mut self,
  363. conn: ConnInner,
  364. displays: &[usize],
  365. include: bool,
  366. exclude: bool,
  367. ) {
  368. let displays = displays
  369. .iter()
  370. .map(|d| video_service::get_service_name(*d))
  371. .collect::<Vec<_>>();
  372. let keys = self.services.keys().cloned().collect::<Vec<_>>();
  373. for name in keys.iter() {
  374. if Self::is_video_service_name(&name) {
  375. if displays.contains(&name) {
  376. if include {
  377. self.subscribe(&name, conn.clone(), true);
  378. }
  379. } else {
  380. if exclude {
  381. self.subscribe(&name, conn.clone(), false);
  382. }
  383. }
  384. }
  385. }
  386. }
  387. #[cfg(target_os = "macos")]
  388. fn update_enable_retina(&self) {
  389. let mut video_service_count = 0;
  390. for (name, service) in self.services.iter() {
  391. if Self::is_video_service_name(&name) && service.ok() {
  392. video_service_count += 1;
  393. }
  394. }
  395. *scrap::quartz::ENABLE_RETINA.lock().unwrap() = video_service_count < 2;
  396. }
  397. }
  398. impl Drop for Server {
  399. fn drop(&mut self) {
  400. for s in self.services.values() {
  401. s.join();
  402. }
  403. #[cfg(target_os = "linux")]
  404. wayland::clear();
  405. }
  406. }
  407. pub fn check_zombie() {
  408. std::thread::spawn(|| loop {
  409. let mut lock = CHILD_PROCESS.lock().unwrap();
  410. let mut i = 0;
  411. while i != lock.len() {
  412. let c = &mut (*lock)[i];
  413. if let Ok(Some(_)) = c.try_wait() {
  414. lock.remove(i);
  415. } else {
  416. i += 1;
  417. }
  418. }
  419. drop(lock);
  420. std::thread::sleep(Duration::from_millis(100));
  421. });
  422. }
  423. /// Start the host server that allows the remote peer to control the current machine.
  424. ///
  425. /// # Arguments
  426. ///
  427. /// * `is_server` - Whether the current client is definitely the server.
  428. /// If true, the server will be started.
  429. /// Otherwise, client will check if there's already a server and start one if not.
  430. #[cfg(any(target_os = "android", target_os = "ios"))]
  431. #[tokio::main]
  432. pub async fn start_server(_is_server: bool) {
  433. crate::RendezvousMediator::start_all().await;
  434. }
  435. /// Start the host server that allows the remote peer to control the current machine.
  436. ///
  437. /// # Arguments
  438. ///
  439. /// * `is_server` - Whether the current client is definitely the server.
  440. /// If true, the server will be started.
  441. /// Otherwise, client will check if there's already a server and start one if not.
  442. /// * `no_server` - If `is_server` is false, whether to start a server if not found.
  443. #[cfg(not(any(target_os = "android", target_os = "ios")))]
  444. #[tokio::main]
  445. pub async fn start_server(is_server: bool, no_server: bool) {
  446. use std::sync::Once;
  447. static ONCE: Once = Once::new();
  448. ONCE.call_once(|| {
  449. #[cfg(target_os = "linux")]
  450. {
  451. log::info!("DISPLAY={:?}", std::env::var("DISPLAY"));
  452. log::info!("XAUTHORITY={:?}", std::env::var("XAUTHORITY"));
  453. }
  454. #[cfg(windows)]
  455. hbb_common::platform::windows::start_cpu_performance_monitor();
  456. });
  457. if is_server {
  458. crate::common::set_server_running(true);
  459. std::thread::spawn(move || {
  460. if let Err(err) = crate::ipc::start("") {
  461. log::error!("Failed to start ipc: {}", err);
  462. if crate::is_server() {
  463. log::error!("ipc is occupied by another process, try kill it");
  464. std::thread::spawn(stop_main_window_process).join().ok();
  465. }
  466. std::process::exit(-1);
  467. }
  468. });
  469. input_service::fix_key_down_timeout_loop();
  470. #[cfg(target_os = "linux")]
  471. if input_service::wayland_use_uinput() {
  472. allow_err!(input_service::setup_uinput(0, 1920, 0, 1080).await);
  473. }
  474. #[cfg(any(target_os = "macos", target_os = "linux"))]
  475. tokio::spawn(async { sync_and_watch_config_dir().await });
  476. #[cfg(target_os = "windows")]
  477. crate::platform::try_kill_broker();
  478. #[cfg(feature = "hwcodec")]
  479. scrap::hwcodec::start_check_process();
  480. crate::RendezvousMediator::start_all().await;
  481. } else {
  482. match crate::ipc::connect(1000, "").await {
  483. Ok(mut conn) => {
  484. if conn.send(&Data::SyncConfig(None)).await.is_ok() {
  485. if let Ok(Some(data)) = conn.next_timeout(1000).await {
  486. match data {
  487. Data::SyncConfig(Some(configs)) => {
  488. let (config, config2) = *configs;
  489. if Config::set(config) {
  490. log::info!("config synced");
  491. }
  492. if Config2::set(config2) {
  493. log::info!("config2 synced");
  494. }
  495. }
  496. _ => {}
  497. }
  498. }
  499. }
  500. #[cfg(feature = "hwcodec")]
  501. #[cfg(any(target_os = "windows", target_os = "linux"))]
  502. crate::ipc::client_get_hwcodec_config_thread(0);
  503. }
  504. Err(err) => {
  505. log::info!("server not started: {err:?}, no_server: {no_server}");
  506. if no_server {
  507. hbb_common::sleep(1.0).await;
  508. std::thread::spawn(|| start_server(false, true));
  509. } else {
  510. log::info!("try start server");
  511. std::thread::spawn(|| start_server(true, false));
  512. }
  513. }
  514. }
  515. }
  516. }
  517. #[cfg(target_os = "macos")]
  518. #[tokio::main(flavor = "current_thread")]
  519. pub async fn start_ipc_url_server() {
  520. log::debug!("Start an ipc server for listening to url schemes");
  521. match crate::ipc::new_listener("_url").await {
  522. Ok(mut incoming) => {
  523. while let Some(Ok(conn)) = incoming.next().await {
  524. let mut conn = crate::ipc::Connection::new(conn);
  525. match conn.next_timeout(1000).await {
  526. Ok(Some(data)) => match data {
  527. #[cfg(feature = "flutter")]
  528. Data::UrlLink(url) => {
  529. let mut m = HashMap::new();
  530. m.insert("name", "on_url_scheme_received");
  531. m.insert("url", url.as_str());
  532. let event = serde_json::to_string(&m).unwrap_or("".to_owned());
  533. match crate::flutter::push_global_event(
  534. crate::flutter::APP_TYPE_MAIN,
  535. event,
  536. ) {
  537. None => log::warn!("No main window app found!"),
  538. Some(..) => {}
  539. }
  540. }
  541. _ => {
  542. log::warn!("An unexpected data was sent to the ipc url server.")
  543. }
  544. },
  545. Err(err) => {
  546. log::error!("{}", err);
  547. }
  548. _ => {}
  549. }
  550. }
  551. }
  552. Err(err) => {
  553. log::error!("{}", err);
  554. }
  555. }
  556. }
  557. #[cfg(any(target_os = "macos", target_os = "linux"))]
  558. async fn sync_and_watch_config_dir() {
  559. if crate::platform::is_root() {
  560. return;
  561. }
  562. let mut cfg0 = (Config::get(), Config2::get());
  563. let mut synced = false;
  564. let tries = if crate::is_server() { 30 } else { 3 };
  565. log::debug!("#tries of ipc service connection: {}", tries);
  566. use hbb_common::sleep;
  567. for i in 1..=tries {
  568. sleep(i as f32 * CONFIG_SYNC_INTERVAL_SECS).await;
  569. match crate::ipc::connect(1000, "_service").await {
  570. Ok(mut conn) => {
  571. if !synced {
  572. if conn.send(&Data::SyncConfig(None)).await.is_ok() {
  573. if let Ok(Some(data)) = conn.next_timeout(1000).await {
  574. match data {
  575. Data::SyncConfig(Some(configs)) => {
  576. let (config, config2) = *configs;
  577. let _chk = crate::ipc::CheckIfRestart::new();
  578. if !config.is_empty() {
  579. if cfg0.0 != config {
  580. cfg0.0 = config.clone();
  581. Config::set(config);
  582. log::info!("sync config from root");
  583. }
  584. if cfg0.1 != config2 {
  585. cfg0.1 = config2.clone();
  586. Config2::set(config2);
  587. log::info!("sync config2 from root");
  588. }
  589. }
  590. synced = true;
  591. }
  592. _ => {}
  593. };
  594. };
  595. }
  596. }
  597. loop {
  598. sleep(CONFIG_SYNC_INTERVAL_SECS).await;
  599. let cfg = (Config::get(), Config2::get());
  600. if cfg != cfg0 {
  601. log::info!("config updated, sync to root");
  602. match conn.send(&Data::SyncConfig(Some(cfg.clone().into()))).await {
  603. Err(e) => {
  604. log::error!("sync config to root failed: {}", e);
  605. match crate::ipc::connect(1000, "_service").await {
  606. Ok(mut _conn) => {
  607. conn = _conn;
  608. log::info!("reconnected to ipc_service");
  609. break;
  610. }
  611. _ => {}
  612. }
  613. }
  614. _ => {
  615. cfg0 = cfg;
  616. conn.next_timeout(1000).await.ok();
  617. }
  618. }
  619. }
  620. }
  621. }
  622. Err(_) => {
  623. log::info!("#{} try: failed to connect to ipc_service", i);
  624. }
  625. }
  626. }
  627. log::warn!("skipped config sync");
  628. }
  629. #[tokio::main(flavor = "current_thread")]
  630. pub async fn stop_main_window_process() {
  631. // this may also kill another --server process,
  632. // but --server usually can be auto restarted by --service, so it is ok
  633. if let Ok(mut conn) = crate::ipc::connect(1000, "").await {
  634. conn.send(&crate::ipc::Data::Close).await.ok();
  635. }
  636. #[cfg(windows)]
  637. {
  638. // in case above failure, e.g. zombie process
  639. if let Err(e) = crate::platform::try_kill_rustdesk_main_window_process() {
  640. log::error!("kill failed: {}", e);
  641. }
  642. }
  643. }