From 9f73b89f217f20924a4767f822fb0c0e11973dd3 Mon Sep 17 00:00:00 2001 From: 21pages Date: Fri, 11 Nov 2022 11:40:23 +0800 Subject: [PATCH] portable-service: exchange ipc server/client Signed-off-by: 21pages --- src/server/connection.rs | 48 ++-- src/server/portable_service.rs | 426 +++++++++++++++------------------ src/server/video_service.rs | 38 +-- src/ui_cm_interface.rs | 8 +- 4 files changed, 239 insertions(+), 281 deletions(-) diff --git a/src/server/connection.rs b/src/server/connection.rs index c77f6ebd7..8674c6d9d 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -429,31 +429,31 @@ impl Connection { _ = second_timer.tick() => { #[cfg(windows)] { - use crate::portable_service::client::{PORTABLE_SERVICE_STATUS, PortableServiceStatus::*}; - let uac = crate::video_service::IS_UAC_RUNNING.lock().unwrap().clone(); - if last_uac != uac { - last_uac = uac; - if PORTABLE_SERVICE_STATUS.lock().unwrap().clone() == NotStarted { - let mut misc = Misc::new(); - misc.set_uac(uac); - let mut msg = Message::new(); - msg.set_misc(misc); - conn.inner.send(msg.into()); - } - } - let foreground_window_elevated = crate::video_service::IS_FOREGROUND_WINDOW_ELEVATED.lock().unwrap().clone(); - if last_foreground_window_elevated != foreground_window_elevated { - last_foreground_window_elevated = foreground_window_elevated; - if PORTABLE_SERVICE_STATUS.lock().unwrap().clone() == NotStarted { - let mut misc = Misc::new(); - misc.set_foreground_window_elevated(foreground_window_elevated); - let mut msg = Message::new(); - msg.set_misc(misc); - conn.inner.send(msg.into()); - } - } if !is_installed { - let show_elevation = PORTABLE_SERVICE_STATUS.lock().unwrap().clone() == NotStarted; + let portable_service_running = crate::portable_service::client::PORTABLE_SERVICE_RUNNING.lock().unwrap().clone(); + let uac = crate::video_service::IS_UAC_RUNNING.lock().unwrap().clone(); + if last_uac != uac { + last_uac = uac; + if !portable_service_running { + let mut misc = Misc::new(); + misc.set_uac(uac); + let mut msg = Message::new(); + msg.set_misc(misc); + conn.inner.send(msg.into()); + } + } + let foreground_window_elevated = crate::video_service::IS_FOREGROUND_WINDOW_ELEVATED.lock().unwrap().clone(); + if last_foreground_window_elevated != foreground_window_elevated { + last_foreground_window_elevated = foreground_window_elevated; + if !portable_service_running { + let mut misc = Misc::new(); + misc.set_foreground_window_elevated(foreground_window_elevated); + let mut msg = Message::new(); + msg.set_misc(misc); + conn.inner.send(msg.into()); + } + } + let show_elevation = !portable_service_running; conn.send_to_cm(ipc::Data::DataPortableService(ipc::DataPortableService::CmShowElevation(show_elevation))); } diff --git a/src/server/portable_service.rs b/src/server/portable_service.rs index 1de2a1c8b..a666b56d5 100644 --- a/src/server/portable_service.rs +++ b/src/server/portable_service.rs @@ -7,7 +7,6 @@ use hbb_common::{ log, message_proto::{KeyEvent, MouseEvent}, protobuf::Message, - sleep, tokio::{self, sync::mpsc}, ResultType, }; @@ -17,7 +16,7 @@ use std::{ mem::size_of, ops::{Deref, DerefMut}, sync::{Arc, Mutex}, - time::Duration, + time::{Duration, Instant}, }; use winapi::{ shared::minwindef::{BOOL, FALSE, TRUE}, @@ -48,18 +47,6 @@ const ADDR_CAPTURE_FRAME: usize = const IPC_PROFIX: &str = "_portable_service"; pub const SHMEM_NAME: &str = "_portable_service"; const MAX_NACK: usize = 3; -const IPC_CONN_TIMEOUT: Duration = Duration::from_secs(3); - -pub enum PortableServiceStatus { - NonStart, - Running, -} - -impl Default for PortableServiceStatus { - fn default() -> Self { - Self::NonStart - } -} pub struct SharedMemory { inner: Shmem, @@ -200,8 +187,6 @@ mod utils { // functions called in seperate SYSTEM user process. pub mod server { - use hbb_common::tokio::time::Instant; - use super::*; lazy_static::lazy_static! { @@ -220,7 +205,7 @@ pub mod server { run_capture(shmem2); })); threads.push(std::thread::spawn(|| { - run_ipc_server(); + run_ipc_client(); })); threads.push(std::thread::spawn(|| { run_exit_check(); @@ -270,7 +255,7 @@ pub mod server { if EXIT.lock().unwrap().clone() { break; } - let start = std::time::Instant::now(); + let start = Instant::now(); unsafe { let para_ptr = shmem.as_ptr().add(ADDR_CAPTURER_PARA); let para = para_ptr as *const CapturerPara; @@ -278,7 +263,6 @@ pub mod server { let use_yuv = (*para).use_yuv; let timeout_ms = (*para).timeout_ms; if c.is_none() { - let use_yuv = true; *crate::video_service::CURRENT_DISPLAY.lock().unwrap() = current_display; let (_, _current, display) = get_current_display().unwrap(); match Capturer::new(display, use_yuv) { @@ -348,114 +332,78 @@ pub mod server { } #[tokio::main(flavor = "current_thread")] - async fn run_ipc_server() { + async fn run_ipc_client() { use DataPortableService::*; let postfix = IPC_PROFIX; - let last_recv_time = Arc::new(Mutex::new(Instant::now())); - let mut interval = tokio::time::interval(Duration::from_secs(1)); - match new_listener(postfix).await { - Ok(mut incoming) => loop { - tokio::select! { - Some(result) = incoming.next() => { - match result { - Ok(stream) => { - log::info!("Got new connection"); - let last_recv_time_cloned = last_recv_time.clone(); - tokio::spawn(async move { - let mut stream = Connection::new(stream); - let postfix = postfix.to_owned(); - let mut timer = tokio::time::interval(Duration::from_secs(1)); - let mut nack = 0; - let mut old_conn_count = 0; - loop { - tokio::select! { - res = stream.next() => { - if res.is_ok() { - *last_recv_time_cloned.lock().unwrap() = Instant::now(); - } - match res { - Err(err) => { - log::error!( - "ipc{} connection closed: {}", - postfix, - err - ); - *EXIT.lock().unwrap() = true; - break; - } - Ok(Some(Data::DataPortableService(data))) => match data { - Ping => { - allow_err!( - stream - .send(&Data::DataPortableService(Pong)) - .await - ); - } - Pong => { - nack = 0; - } - ConnCount(Some(n)) => { - if old_conn_count != 0 && n == 0 { - log::info!("Connection count decrease to 0, exit"); - stream.send(&Data::DataPortableService(WillClose)).await.ok(); - *EXIT.lock().unwrap() = true; - break; - } - old_conn_count = n; - } - Mouse(v) => { - if let Ok(evt) = MouseEvent::parse_from_bytes(&v) { - crate::input_service::handle_mouse_(&evt); - } - } - Key(v) => { - if let Ok(evt) = KeyEvent::parse_from_bytes(&v) { - crate::input_service::handle_key_(&evt); - } - } - _ => {} - }, - _ => {} - } - } - _ = timer.tick() => { - nack+=1; - if nack > MAX_NACK { - log::info!("max ping nack, exit"); - *EXIT.lock().unwrap() = true; - break; - } - stream.send(&Data::DataPortableService(Ping)).await.ok(); - stream.send(&Data::DataPortableService(ConnCount(None))).await.ok(); - } + match ipc::connect(1000, postfix).await { + Ok(mut stream) => { + let mut timer = tokio::time::interval(Duration::from_secs(1)); + let mut nack = 0; + loop { + tokio::select! { + res = stream.next() => { + match res { + Err(err) => { + log::error!( + "ipc{} connection closed: {}", + postfix, + err + ); + break; + } + Ok(Some(Data::DataPortableService(data))) => match data { + Ping => { + allow_err!( + stream + .send(&Data::DataPortableService(Pong)) + .await + ); + } + Pong => { + nack = 0; + } + ConnCount(Some(n)) => { + if n == 0 { + log::info!("Connnection count equals 0, exit"); + stream.send(&Data::DataPortableService(WillClose)).await.ok(); + break; } } - }); - } - Err(err) => { - log::error!("Couldn't get portable client: {:?}", err); - *EXIT.lock().unwrap() = true; + Mouse(v) => { + if let Ok(evt) = MouseEvent::parse_from_bytes(&v) { + crate::input_service::handle_mouse_(&evt); + } + } + Key(v) => { + if let Ok(evt) = KeyEvent::parse_from_bytes(&v) { + crate::input_service::handle_key_(&evt); + } + } + _ => {} + }, + _ => {} } } - } - _ = interval.tick() => { - if last_recv_time.lock().unwrap().elapsed() > IPC_CONN_TIMEOUT { - log::error!("receive data timeout"); - *EXIT.lock().unwrap() = true; - } - if EXIT.lock().unwrap().clone() { - break; + _ = timer.tick() => { + nack+=1; + if nack > MAX_NACK { + log::info!("max ping nack, exit"); + break; + } + stream.send(&Data::DataPortableService(Ping)).await.ok(); + stream.send(&Data::DataPortableService(ConnCount(None))).await.ok(); } } } - }, - Err(err) => { - log::error!("Failed to start cm ipc server: {}", err); - *EXIT.lock().unwrap() = true; + } + Err(e) => { + log::error!("Failed to connect portable service ipc:{:?}", e); } } + + *EXIT.lock().unwrap() = true; } } @@ -466,54 +414,46 @@ pub mod client { use super::*; lazy_static::lazy_static! { - pub static ref SHMEM: Arc>> = Default::default(); - pub static ref PORTABLE_SERVICE_STATUS: Arc> = Default::default(); - static ref SENDER : Mutex> = Mutex::new(client::start_ipc_client()); - } - - #[derive(Debug, Clone, Copy, PartialEq, Eq)] - pub enum PortableServiceStatus { - NotStarted, - Starting, - Running, - } - - impl Default for PortableServiceStatus { - fn default() -> Self { - Self::NotStarted - } + pub static ref PORTABLE_SERVICE_RUNNING: Arc> = Default::default(); + static ref SHMEM: Arc>> = Default::default(); + static ref SENDER : Mutex> = Mutex::new(client::start_ipc_server()); } pub(crate) fn start_portable_service() -> ResultType<()> { - if PORTABLE_SERVICE_STATUS.lock().unwrap().clone() == PortableServiceStatus::NotStarted { - if SHMEM.lock().unwrap().is_none() { - let displays = scrap::Display::all()?; - if displays.is_empty() { - bail!("no display available!"); - } - let mut max_pixel = 0; - let align = 64; - for d in displays { - let pixel = utils::align(d.width(), align) * utils::align(d.height(), align); - if max_pixel < pixel { - max_pixel = pixel; - } - } - let shmem_size = utils::align(ADDR_CAPTURE_FRAME + max_pixel * 4, align); - // os error 112, no enough space - *SHMEM.lock().unwrap() = Some(crate::portable_service::SharedMemory::create( - crate::portable_service::SHMEM_NAME, - shmem_size, - )?); - shutdown_hooks::add_shutdown_hook(drop_shmem); - } - if crate::common::run_me(vec!["--portable-service"]).is_err() { - *SHMEM.lock().unwrap() = None; - bail!("Failed to run portable service process"); - } - *PORTABLE_SERVICE_STATUS.lock().unwrap() = PortableServiceStatus::Starting; - let _sender = SENDER.lock().unwrap(); + if PORTABLE_SERVICE_RUNNING.lock().unwrap().clone() { + bail!("already running"); } + if SHMEM.lock().unwrap().is_none() { + let displays = scrap::Display::all()?; + if displays.is_empty() { + bail!("no display available!"); + } + let mut max_pixel = 0; + let align = 64; + for d in displays { + let pixel = utils::align(d.width(), align) * utils::align(d.height(), align); + if max_pixel < pixel { + max_pixel = pixel; + } + } + let shmem_size = utils::align(ADDR_CAPTURE_FRAME + max_pixel * 4, align); + // os error 112, no enough space + *SHMEM.lock().unwrap() = Some(crate::portable_service::SharedMemory::create( + crate::portable_service::SHMEM_NAME, + shmem_size, + )?); + shutdown_hooks::add_shutdown_hook(drop_shmem); + } + let mut option = SHMEM.lock().unwrap(); + let shmem = option.as_mut().unwrap(); + unsafe { + libc::memset(shmem.as_ptr() as _, 0, shmem.len() as _); + } + if crate::common::run_me(vec!["--portable-service"]).is_err() { + *SHMEM.lock().unwrap() = None; + bail!("Failed to run portable service process"); + } + let _sender = SENDER.lock().unwrap(); Ok(()) } @@ -613,94 +553,98 @@ pub mod client { } } - pub(super) fn start_ipc_client() -> mpsc::UnboundedSender { + pub(super) fn start_ipc_server() -> mpsc::UnboundedSender { let (tx, rx) = mpsc::unbounded_channel::(); - std::thread::spawn(move || start_ipc_client_async(rx)); + std::thread::spawn(move || start_ipc_server_async(rx)); tx } #[tokio::main(flavor = "current_thread")] - async fn start_ipc_client_async(rx: mpsc::UnboundedReceiver) { + async fn start_ipc_server_async(rx: mpsc::UnboundedReceiver) { use DataPortableService::*; - let mut rx = rx; - let mut connect_failed = 0; - loop { - if PORTABLE_SERVICE_STATUS.lock().unwrap().clone() == PortableServiceStatus::NotStarted - { - sleep(1.).await; - continue; - } - if let Ok(mut c) = ipc::connect(1000, IPC_PROFIX).await { - let mut nack = 0; - let mut timer = tokio::time::interval(Duration::from_secs(1)); - loop { - tokio::select! { - res = c.next() => { - match res { - Err(err) => { - log::error!("ipc connection closed: {}", err); - break; - } - Ok(Some(Data::DataPortableService(data))) => { - match data { - Ping => { - c.send(&Data::DataPortableService(Pong)).await.ok(); - } - Pong => { - nack = 0; - *PORTABLE_SERVICE_STATUS.lock().unwrap() = PortableServiceStatus::Running; - }, - ConnCount(None) => { - let cnt = crate::server::CONN_COUNT.lock().unwrap().clone(); - c.send(&Data::DataPortableService(ConnCount(Some(cnt)))).await.ok(); - }, - WillClose => { - log::info!("portable service will close, set status to not started"); - *PORTABLE_SERVICE_STATUS.lock().unwrap() = PortableServiceStatus::NotStarted; - break; - } - _=>{} - } - } - _ => {} - } - } - _ = timer.tick() => { - nack+=1; - if nack > MAX_NACK { - // In fact, this will not happen, ipc will be closed before max nack. - log::error!("max ipc nack, set status to not started"); - *PORTABLE_SERVICE_STATUS.lock().unwrap() = PortableServiceStatus::NotStarted; - break; - } - c.send(&Data::DataPortableService(Ping)).await.ok(); - } - Some(data) = rx.recv() => { - allow_err!(c.send(&data).await); - } + let rx = Arc::new(tokio::sync::Mutex::new(rx)); + let postfix = IPC_PROFIX; + match new_listener(postfix).await { + Ok(mut incoming) => loop { + { + tokio::select! { + Some(result) = incoming.next() => { + match result { + Ok(stream) => { + log::info!("Got portable service ipc connection"); + let rx_clone = rx.clone(); + tokio::spawn(async move { + let mut stream = Connection::new(stream); + let postfix = postfix.to_owned(); + let mut timer = tokio::time::interval(Duration::from_secs(1)); + let mut nack = 0; + let mut rx = rx_clone.lock().await; + loop { + tokio::select! { + res = stream.next() => { + match res { + Err(err) => { + log::info!( + "ipc{} connection closed: {}", + postfix, + err + ); + break; + } + Ok(Some(Data::DataPortableService(data))) => match data { + Ping => { + stream.send(&Data::DataPortableService(Pong)).await.ok(); + } + Pong => { + nack = 0; + *PORTABLE_SERVICE_RUNNING.lock().unwrap() = true; + }, + ConnCount(None) => { + let cnt = crate::server::CONN_COUNT.lock().unwrap().clone(); + stream.send(&Data::DataPortableService(ConnCount(Some(cnt)))).await.ok(); + }, + WillClose => { + log::info!("portable service will close"); + break; + } + _=>{} + } + _=>{} + } + } + _ = timer.tick() => { + nack+=1; + if nack > MAX_NACK { + // In fact, this will not happen, ipc will be closed before max nack. + log::error!("max ipc nack"); + break; + } + stream.send(&Data::DataPortableService(Ping)).await.ok(); + } + Some(data) = rx.recv() => { + allow_err!(stream.send(&data).await); + } + } + } + *PORTABLE_SERVICE_RUNNING.lock().unwrap() = false; + }); + } + Err(err) => { + log::error!("Couldn't get portable client: {:?}", err); + } + } + } } } - } else { - connect_failed += 1; - if connect_failed > IPC_CONN_TIMEOUT.as_secs() { - connect_failed = 0; - *PORTABLE_SERVICE_STATUS.lock().unwrap() = PortableServiceStatus::NotStarted; - log::info!( - "connect failed {} times, set status to not started", - connect_failed - ); - } - log::info!( - "client ip connect failed, status:{:?}", - PORTABLE_SERVICE_STATUS.lock().unwrap().clone(), - ); + }, + Err(err) => { + log::error!("Failed to start portable service ipc server: {}", err); } - sleep(1.).await; } } - fn client_ipc_send(data: Data) -> ResultType<()> { + fn ipc_send(data: Data) -> ResultType<()> { let sender = SENDER.lock().unwrap(); sender .send(data) @@ -721,21 +665,25 @@ pub mod client { fn handle_mouse_(evt: &MouseEvent) -> ResultType<()> { let mut v = vec![]; evt.write_to_vec(&mut v)?; - client_ipc_send(Data::DataPortableService(DataPortableService::Mouse(v))) + ipc_send(Data::DataPortableService(DataPortableService::Mouse(v))) } fn handle_key_(evt: &KeyEvent) -> ResultType<()> { let mut v = vec![]; evt.write_to_vec(&mut v)?; - client_ipc_send(Data::DataPortableService(DataPortableService::Key(v))) + ipc_send(Data::DataPortableService(DataPortableService::Key(v))) } pub fn create_capturer( current_display: usize, display: scrap::Display, use_yuv: bool, + portable_service_running: bool, ) -> ResultType> { - if PORTABLE_SERVICE_STATUS.lock().unwrap().clone() == PortableServiceStatus::Running { + if portable_service_running != PORTABLE_SERVICE_RUNNING.lock().unwrap().clone() { + log::info!("portable service status mismatch"); + } + if portable_service_running { log::info!("Create shared memeory capturer"); return Ok(Box::new(CapturerPortable::new(current_display, use_yuv))); } else { @@ -747,7 +695,7 @@ pub mod client { } pub fn get_cursor_info(pci: PCURSORINFO) -> BOOL { - if PORTABLE_SERVICE_STATUS.lock().unwrap().clone() == PortableServiceStatus::Running { + if PORTABLE_SERVICE_RUNNING.lock().unwrap().clone() { get_cursor_info_(&mut SHMEM.lock().unwrap().as_mut().unwrap(), pci) } else { unsafe { winuser::GetCursorInfo(pci) } @@ -755,7 +703,7 @@ pub mod client { } pub fn handle_mouse(evt: &MouseEvent) { - if PORTABLE_SERVICE_STATUS.lock().unwrap().clone() == PortableServiceStatus::Running { + if PORTABLE_SERVICE_RUNNING.lock().unwrap().clone() { handle_mouse_(evt).ok(); } else { crate::input_service::handle_mouse_(evt); @@ -763,7 +711,7 @@ pub mod client { } pub fn handle_key(evt: &KeyEvent) { - if PORTABLE_SERVICE_STATUS.lock().unwrap().clone() == PortableServiceStatus::Running { + if PORTABLE_SERVICE_RUNNING.lock().unwrap().clone() { handle_key_(evt).ok(); } else { crate::input_service::handle_key_(evt); diff --git a/src/server/video_service.rs b/src/server/video_service.rs index f48fefeec..43ce013f5 100644 --- a/src/server/video_service.rs +++ b/src/server/video_service.rs @@ -20,7 +20,7 @@ use super::{video_qos::VideoQoS, *}; #[cfg(windows)] -use crate::portable_service::client::{PortableServiceStatus, PORTABLE_SERVICE_STATUS}; +use crate::portable_service::client::PORTABLE_SERVICE_RUNNING; use hbb_common::tokio::sync::{ mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender}, Mutex as TokioMutex, @@ -191,6 +191,7 @@ fn create_capturer( display: Display, use_yuv: bool, current: usize, + _portable_service_running: bool, ) -> ResultType> { #[cfg(not(windows))] let c: Option> = None; @@ -252,7 +253,12 @@ fn create_capturer( None => { log::debug!("Create capturer dxgi|gdi"); #[cfg(windows)] - return crate::portable_service::client::create_capturer(current, display, use_yuv); + return crate::portable_service::client::create_capturer( + current, + display, + use_yuv, + _portable_service_running, + ); #[cfg(not(windows))] return Ok(Box::new( Capturer::new(display, use_yuv).with_context(|| "Failed to create capturer")?, @@ -282,7 +288,7 @@ pub fn test_create_capturer(privacy_mode_id: i32, timeout_millis: u64) -> bool { let test_begin = Instant::now(); while test_begin.elapsed().as_millis() < timeout_millis as _ { if let Ok((_, current, display)) = get_current_display() { - if let Ok(_) = create_capturer(privacy_mode_id, display, true, current) { + if let Ok(_) = create_capturer(privacy_mode_id, display, true, current, false) { return true; } } @@ -331,7 +337,7 @@ impl DerefMut for CapturerInfo { } } -fn get_capturer(use_yuv: bool) -> ResultType { +fn get_capturer(use_yuv: bool, portable_service_running: bool) -> ResultType { #[cfg(target_os = "linux")] { if !scrap::is_x11() { @@ -373,7 +379,13 @@ fn get_capturer(use_yuv: bool) -> ResultType { } else { log::info!("In privacy mode, the peer side cannot watch the screen"); } - let capturer = create_capturer(captuerer_privacy_mode_id, display, use_yuv, current)?; + let capturer = create_capturer( + captuerer_privacy_mode_id, + display, + use_yuv, + current, + portable_service_running, + )?; Ok(CapturerInfo { origin, width, @@ -393,8 +405,12 @@ fn run(sp: GenericService) -> ResultType<()> { // ensure_inited() is needed because release_resouce() may be called. #[cfg(target_os = "linux")] super::wayland::ensure_inited()?; + #[cfg(windows)] + let last_portable_service_running = PORTABLE_SERVICE_RUNNING.lock().unwrap().clone(); + #[cfg(not(windows))] + let last_portable_service_running = false; - let mut c = get_capturer(true)?; + let mut c = get_capturer(true, last_portable_service_running)?; let mut video_qos = VIDEO_QOS.lock().unwrap(); video_qos.set_size(c.width as _, c.height as _); @@ -472,11 +488,6 @@ fn run(sp: GenericService) -> ResultType<()> { let recorder: Arc>> = Default::default(); #[cfg(windows)] start_uac_elevation_check(); - #[cfg(windows)] - let portable_service_status = crate::portable_service::client::PORTABLE_SERVICE_STATUS - .lock() - .unwrap() - .clone(); #[cfg(target_os = "linux")] let mut would_block_count = 0u32; @@ -508,15 +519,14 @@ fn run(sp: GenericService) -> ResultType<()> { bail!("SWITCH"); } #[cfg(windows)] - if portable_service_status != PORTABLE_SERVICE_STATUS.lock().unwrap().clone() { + if last_portable_service_running != PORTABLE_SERVICE_RUNNING.lock().unwrap().clone() { bail!("SWITCH"); } check_privacy_mode_changed(&sp, c.privacy_mode_id)?; #[cfg(windows)] { if crate::platform::windows::desktop_changed() - && PORTABLE_SERVICE_STATUS.lock().unwrap().clone() - == PortableServiceStatus::NotStarted + && !PORTABLE_SERVICE_RUNNING.lock().unwrap().clone() { bail!("Desktop changed"); } diff --git a/src/ui_cm_interface.rs b/src/ui_cm_interface.rs index b1e4db7f8..26b26bf92 100644 --- a/src/ui_cm_interface.rs +++ b/src/ui_cm_interface.rs @@ -770,11 +770,11 @@ fn cm_inner_send(id: i32, data: Data) { pub fn can_elevate() -> bool { #[cfg(windows)] { - use crate::portable_service::client::{ - PortableServiceStatus::NotStarted, PORTABLE_SERVICE_STATUS, - }; return !crate::platform::is_installed() - && PORTABLE_SERVICE_STATUS.lock().unwrap().clone() == NotStarted; + && !crate::portable_service::client::PORTABLE_SERVICE_RUNNING + .lock() + .unwrap() + .clone(); } #[cfg(not(windows))] return false;