server.rs 23 KB

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