2023-02-04 11:23:36 +08:00
|
|
|
use std::{
|
|
|
|
collections::HashMap,
|
|
|
|
net::SocketAddr,
|
|
|
|
sync::{Arc, Mutex, RwLock, Weak},
|
|
|
|
time::Duration,
|
|
|
|
};
|
|
|
|
|
2022-10-22 22:19:14 +08:00
|
|
|
use bytes::Bytes;
|
2023-02-04 11:23:36 +08:00
|
|
|
|
2022-05-12 17:35:25 +08:00
|
|
|
pub use connection::*;
|
2023-06-07 20:42:07 +08:00
|
|
|
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
|
|
|
use hbb_common::config::Config2;
|
2023-05-14 18:17:02 +08:00
|
|
|
use hbb_common::tcp::{self, new_listener};
|
2022-05-12 17:35:25 +08:00
|
|
|
use hbb_common::{
|
|
|
|
allow_err,
|
2023-05-19 09:51:01 +08:00
|
|
|
anyhow::Context,
|
2022-05-12 17:35:25 +08:00
|
|
|
bail,
|
2022-11-13 18:11:13 +08:00
|
|
|
config::{Config, CONNECT_TIMEOUT, RELAY_PORT},
|
2022-05-12 17:35:25 +08:00
|
|
|
log,
|
|
|
|
message_proto::*,
|
2022-07-20 09:50:08 -07:00
|
|
|
protobuf::{Enum, Message as _},
|
2022-05-12 17:35:25 +08:00
|
|
|
rendezvous_proto::*,
|
|
|
|
socket_client,
|
2023-05-14 18:17:02 +08:00
|
|
|
sodiumoxide::crypto::{box_, sign},
|
2023-02-08 19:17:59 +08:00
|
|
|
timeout, tokio, ResultType, Stream,
|
2022-05-12 17:35:25 +08:00
|
|
|
};
|
2022-11-13 18:11:13 +08:00
|
|
|
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
2023-02-04 11:23:36 +08:00
|
|
|
use service::ServiceTmpl;
|
2023-10-08 21:44:54 +08:00
|
|
|
use service::{EmptyExtraFieldService, GenericService, Service, Subscriber};
|
2023-02-04 11:23:36 +08:00
|
|
|
|
2023-02-08 19:17:59 +08:00
|
|
|
use crate::ipc::Data;
|
2022-07-16 00:45:23 +08:00
|
|
|
|
2022-05-12 17:35:25 +08:00
|
|
|
pub mod audio_service;
|
|
|
|
cfg_if::cfg_if! {
|
|
|
|
if #[cfg(not(any(target_os = "android", target_os = "ios")))] {
|
|
|
|
mod clipboard_service;
|
2022-07-20 09:50:08 -07:00
|
|
|
#[cfg(target_os = "linux")]
|
2022-10-11 20:36:19 -07:00
|
|
|
pub(crate) mod wayland;
|
2022-07-20 09:50:08 -07:00
|
|
|
#[cfg(target_os = "linux")]
|
|
|
|
pub mod uinput;
|
2022-10-11 19:52:03 +08:00
|
|
|
#[cfg(target_os = "linux")]
|
2023-12-19 08:14:58 +05:30
|
|
|
pub mod rdp_input;
|
|
|
|
#[cfg(target_os = "linux")]
|
2022-10-11 19:52:03 +08:00
|
|
|
pub mod dbus;
|
2022-05-12 17:35:25 +08:00
|
|
|
pub mod input_service;
|
|
|
|
} else {
|
|
|
|
mod clipboard_service {
|
|
|
|
pub const NAME: &'static str = "";
|
|
|
|
}
|
|
|
|
pub mod input_service {
|
|
|
|
pub const NAME_CURSOR: &'static str = "";
|
|
|
|
pub const NAME_POS: &'static str = "";
|
2024-04-25 10:56:02 +05:30
|
|
|
pub const NAME_WINDOW_FOCUS: &'static str = "";
|
2022-05-12 17:35:25 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
mod connection;
|
2023-10-08 21:44:54 +08:00
|
|
|
pub mod display_service;
|
2022-11-10 10:27:13 +08:00
|
|
|
#[cfg(windows)]
|
|
|
|
pub mod portable_service;
|
2022-05-12 17:35:25 +08:00
|
|
|
mod service;
|
2022-07-05 22:31:08 +08:00
|
|
|
mod video_qos;
|
2022-05-12 17:35:25 +08:00
|
|
|
pub mod video_service;
|
|
|
|
|
|
|
|
pub type Childs = Arc<Mutex<Vec<std::process::Child>>>;
|
|
|
|
type ConnMap = HashMap<i32, ConnInner>;
|
|
|
|
|
2023-06-09 13:35:03 +08:00
|
|
|
#[cfg(any(target_os = "macos", target_os = "linux"))]
|
|
|
|
const CONFIG_SYNC_INTERVAL_SECS: f32 = 0.3;
|
|
|
|
|
2022-05-12 17:35:25 +08:00
|
|
|
lazy_static::lazy_static! {
|
|
|
|
pub static ref CHILD_PROCESS: Childs = Default::default();
|
2022-11-10 10:27:13 +08:00
|
|
|
pub static ref CONN_COUNT: Arc<Mutex<usize>> = Default::default();
|
2023-02-08 19:17:59 +08:00
|
|
|
// A client server used to provide local services(audio, video, clipboard, etc.)
|
2022-11-03 21:09:37 +08:00
|
|
|
// for all initiative connections.
|
|
|
|
//
|
|
|
|
// [Note]
|
2023-07-02 01:27:34 +08:00
|
|
|
// ugly
|
2022-11-03 21:09:37 +08:00
|
|
|
// Now we use this [`CLIENT_SERVER`] to do following operations:
|
|
|
|
// - record local audio, and send to remote
|
|
|
|
pub static ref CLIENT_SERVER: ServerPtr = new();
|
2022-05-12 17:35:25 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
pub struct Server {
|
|
|
|
connections: ConnMap,
|
2023-10-08 21:44:54 +08:00
|
|
|
services: HashMap<String, Box<dyn Service>>,
|
2022-05-12 17:35:25 +08:00
|
|
|
id_count: i32,
|
|
|
|
}
|
|
|
|
|
|
|
|
pub type ServerPtr = Arc<RwLock<Server>>;
|
|
|
|
pub type ServerPtrWeak = Weak<RwLock<Server>>;
|
|
|
|
|
|
|
|
pub fn new() -> ServerPtr {
|
|
|
|
let mut server = Server {
|
|
|
|
connections: HashMap::new(),
|
|
|
|
services: HashMap::new(),
|
2023-07-02 01:27:34 +08:00
|
|
|
id_count: hbb_common::rand::random::<i32>() % 1000 + 1000, // ensure positive
|
2022-05-12 17:35:25 +08:00
|
|
|
};
|
|
|
|
server.add_service(Box::new(audio_service::new()));
|
2023-11-03 07:17:40 +08:00
|
|
|
#[cfg(not(target_os = "ios"))]
|
2023-10-08 21:44:54 +08:00
|
|
|
server.add_service(Box::new(display_service::new()));
|
2022-05-12 17:35:25 +08:00
|
|
|
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
|
|
|
{
|
|
|
|
server.add_service(Box::new(clipboard_service::new()));
|
2023-10-08 21:44:54 +08:00
|
|
|
if !display_service::capture_cursor_embedded() {
|
2022-11-29 16:36:35 +08:00
|
|
|
server.add_service(Box::new(input_service::new_cursor()));
|
|
|
|
server.add_service(Box::new(input_service::new_pos()));
|
2024-04-25 10:56:02 +05:30
|
|
|
server.add_service(Box::new(input_service::new_window_focus()));
|
2022-11-29 16:36:35 +08:00
|
|
|
}
|
2022-05-12 17:35:25 +08:00
|
|
|
}
|
|
|
|
Arc::new(RwLock::new(server))
|
|
|
|
}
|
|
|
|
|
|
|
|
async fn accept_connection_(server: ServerPtr, socket: Stream, secure: bool) -> ResultType<()> {
|
|
|
|
let local_addr = socket.local_addr();
|
|
|
|
drop(socket);
|
|
|
|
// even we drop socket, below still may fail if not use reuse_addr,
|
|
|
|
// there is TIME_WAIT before socket really released, so sometimes we
|
|
|
|
// see “Only one usage of each socket address is normally permitted” on windows sometimes,
|
|
|
|
let listener = new_listener(local_addr, true).await?;
|
|
|
|
log::info!("Server listening on: {}", &listener.local_addr()?);
|
|
|
|
if let Ok((stream, addr)) = timeout(CONNECT_TIMEOUT, listener.accept()).await? {
|
|
|
|
stream.set_nodelay(true).ok();
|
|
|
|
let stream_addr = stream.local_addr()?;
|
|
|
|
create_tcp_connection(server, Stream::from(stream, stream_addr), addr, secure).await?;
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub async fn create_tcp_connection(
|
|
|
|
server: ServerPtr,
|
|
|
|
stream: Stream,
|
|
|
|
addr: SocketAddr,
|
|
|
|
secure: bool,
|
|
|
|
) -> ResultType<()> {
|
|
|
|
let mut stream = stream;
|
2023-07-02 01:27:34 +08:00
|
|
|
let id = server.write().unwrap().get_new_id();
|
2022-05-12 17:35:25 +08:00
|
|
|
let (sk, pk) = Config::get_key_pair();
|
|
|
|
if secure && pk.len() == sign::PUBLICKEYBYTES && sk.len() == sign::SECRETKEYBYTES {
|
|
|
|
let mut sk_ = [0u8; sign::SECRETKEYBYTES];
|
|
|
|
sk_[..].copy_from_slice(&sk);
|
|
|
|
let sk = sign::SecretKey(sk_);
|
|
|
|
let mut msg_out = Message::new();
|
|
|
|
let (our_pk_b, our_sk_b) = box_::gen_keypair();
|
|
|
|
msg_out.set_signed_id(SignedId {
|
|
|
|
id: sign::sign(
|
|
|
|
&IdPk {
|
|
|
|
id: Config::get_id(),
|
2022-07-16 00:45:23 +08:00
|
|
|
pk: Bytes::from(our_pk_b.0.to_vec()),
|
2022-05-12 17:35:25 +08:00
|
|
|
..Default::default()
|
|
|
|
}
|
|
|
|
.write_to_bytes()
|
|
|
|
.unwrap_or_default(),
|
|
|
|
&sk,
|
2022-10-22 22:19:14 +08:00
|
|
|
)
|
|
|
|
.into(),
|
2022-05-12 17:35:25 +08:00
|
|
|
..Default::default()
|
|
|
|
});
|
|
|
|
timeout(CONNECT_TIMEOUT, stream.send(&msg_out)).await??;
|
|
|
|
match timeout(CONNECT_TIMEOUT, stream.next()).await? {
|
|
|
|
Some(res) => {
|
|
|
|
let bytes = res?;
|
|
|
|
if let Ok(msg_in) = Message::parse_from_bytes(&bytes) {
|
2022-07-14 17:20:01 +08:00
|
|
|
if let Some(message::Union::PublicKey(pk)) = msg_in.union {
|
2022-05-12 17:35:25 +08:00
|
|
|
if pk.asymmetric_value.len() == box_::PUBLICKEYBYTES {
|
2023-05-14 18:17:02 +08:00
|
|
|
stream.set_key(tcp::Encrypt::decode(
|
|
|
|
&pk.symmetric_value,
|
|
|
|
&pk.asymmetric_value,
|
|
|
|
&our_sk_b,
|
|
|
|
)?);
|
2022-05-12 17:35:25 +08:00
|
|
|
} else if pk.asymmetric_value.is_empty() {
|
|
|
|
Config::set_key_confirmed(false);
|
|
|
|
log::info!("Force to update pk");
|
|
|
|
} else {
|
|
|
|
bail!("Handshake failed: invalid public sign key length from peer");
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
log::error!("Handshake failed: invalid message type");
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
bail!("Handshake failed: invalid message format");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
None => {
|
|
|
|
bail!("Failed to receive public key");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-12-28 11:01:09 +08:00
|
|
|
#[cfg(target_os = "macos")]
|
|
|
|
{
|
2022-12-29 22:22:16 +02:00
|
|
|
use std::process::Command;
|
2022-12-28 11:01:09 +08:00
|
|
|
Command::new("/usr/bin/caffeinate")
|
|
|
|
.arg("-u")
|
|
|
|
.arg("-t 5")
|
|
|
|
.spawn()
|
|
|
|
.ok();
|
2022-12-29 22:22:16 +02:00
|
|
|
log::info!("wake up macos");
|
|
|
|
}
|
2022-05-12 17:35:25 +08:00
|
|
|
Connection::start(addr, stream, id, Arc::downgrade(&server)).await;
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub async fn accept_connection(
|
|
|
|
server: ServerPtr,
|
|
|
|
socket: Stream,
|
|
|
|
peer_addr: SocketAddr,
|
|
|
|
secure: bool,
|
|
|
|
) {
|
|
|
|
if let Err(err) = accept_connection_(server, socket, secure).await {
|
|
|
|
log::error!("Failed to accept connection from {}: {}", peer_addr, err);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub async fn create_relay_connection(
|
|
|
|
server: ServerPtr,
|
|
|
|
relay_server: String,
|
|
|
|
uuid: String,
|
|
|
|
peer_addr: SocketAddr,
|
|
|
|
secure: bool,
|
2022-12-28 13:52:13 +08:00
|
|
|
ipv4: bool,
|
2022-05-12 17:35:25 +08:00
|
|
|
) {
|
|
|
|
if let Err(err) =
|
2022-12-28 13:52:13 +08:00
|
|
|
create_relay_connection_(server, relay_server, uuid.clone(), peer_addr, secure, ipv4).await
|
2022-05-12 17:35:25 +08:00
|
|
|
{
|
|
|
|
log::error!(
|
|
|
|
"Failed to create relay connection for {} with uuid {}: {}",
|
|
|
|
peer_addr,
|
|
|
|
uuid,
|
|
|
|
err
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
async fn create_relay_connection_(
|
|
|
|
server: ServerPtr,
|
|
|
|
relay_server: String,
|
|
|
|
uuid: String,
|
|
|
|
peer_addr: SocketAddr,
|
|
|
|
secure: bool,
|
2022-12-28 13:52:13 +08:00
|
|
|
ipv4: bool,
|
2022-05-12 17:35:25 +08:00
|
|
|
) -> ResultType<()> {
|
|
|
|
let mut stream = socket_client::connect_tcp(
|
2022-12-28 13:52:13 +08:00
|
|
|
socket_client::ipv4_to_ipv6(crate::check_port(relay_server, RELAY_PORT), ipv4),
|
2022-05-12 17:35:25 +08:00
|
|
|
CONNECT_TIMEOUT,
|
|
|
|
)
|
|
|
|
.await?;
|
|
|
|
let mut msg_out = RendezvousMessage::new();
|
2023-03-20 00:56:17 +08:00
|
|
|
let licence_key = crate::get_key(true).await;
|
2022-05-12 17:35:25 +08:00
|
|
|
msg_out.set_request_relay(RequestRelay {
|
|
|
|
licence_key,
|
|
|
|
uuid,
|
|
|
|
..Default::default()
|
|
|
|
});
|
|
|
|
stream.send(&msg_out).await?;
|
|
|
|
create_tcp_connection(server, stream, peer_addr, secure).await?;
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Server {
|
2023-10-08 21:44:54 +08:00
|
|
|
fn is_video_service_name(name: &str) -> bool {
|
|
|
|
name.starts_with(video_service::NAME)
|
|
|
|
}
|
|
|
|
|
2023-11-17 23:38:27 +08:00
|
|
|
pub fn try_add_primay_video_service(&mut self) {
|
2023-11-17 17:11:25 +08:00
|
|
|
let primary_video_service_name =
|
|
|
|
video_service::get_service_name(*display_service::PRIMARY_DISPLAY_IDX);
|
|
|
|
if !self.contains(&primary_video_service_name) {
|
|
|
|
self.add_service(Box::new(video_service::new(
|
|
|
|
*display_service::PRIMARY_DISPLAY_IDX,
|
|
|
|
)));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-12 17:35:25 +08:00
|
|
|
pub fn add_connection(&mut self, conn: ConnInner, noperms: &Vec<&'static str>) {
|
2023-10-08 21:44:54 +08:00
|
|
|
let primary_video_service_name =
|
|
|
|
video_service::get_service_name(*display_service::PRIMARY_DISPLAY_IDX);
|
2022-05-12 17:35:25 +08:00
|
|
|
for s in self.services.values() {
|
2023-10-08 21:44:54 +08:00
|
|
|
let name = s.name();
|
|
|
|
if Self::is_video_service_name(&name) && name != primary_video_service_name {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if !noperms.contains(&(&name as _)) {
|
2022-05-12 17:35:25 +08:00
|
|
|
s.on_subscribe(conn.clone());
|
|
|
|
}
|
|
|
|
}
|
2024-02-27 22:28:23 +08:00
|
|
|
#[cfg(target_os = "macos")]
|
|
|
|
self.update_enable_retina();
|
2022-05-12 17:35:25 +08:00
|
|
|
self.connections.insert(conn.id(), conn);
|
2022-11-10 10:27:13 +08:00
|
|
|
*CONN_COUNT.lock().unwrap() = self.connections.len();
|
2022-05-12 17:35:25 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn remove_connection(&mut self, conn: &ConnInner) {
|
|
|
|
for s in self.services.values() {
|
|
|
|
s.on_unsubscribe(conn.id());
|
|
|
|
}
|
|
|
|
self.connections.remove(&conn.id());
|
2022-11-10 10:27:13 +08:00
|
|
|
*CONN_COUNT.lock().unwrap() = self.connections.len();
|
2024-02-27 22:28:23 +08:00
|
|
|
#[cfg(target_os = "macos")]
|
|
|
|
self.update_enable_retina();
|
2022-05-12 17:35:25 +08:00
|
|
|
}
|
|
|
|
|
2022-11-09 17:28:47 +08:00
|
|
|
pub fn close_connections(&mut self) {
|
|
|
|
let conn_inners: Vec<_> = self.connections.values_mut().collect();
|
|
|
|
for c in conn_inners {
|
|
|
|
let mut misc = Misc::new();
|
|
|
|
misc.set_stop_service(true);
|
|
|
|
let mut msg = Message::new();
|
|
|
|
msg.set_misc(misc);
|
|
|
|
c.send(Arc::new(msg));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-12 17:35:25 +08:00
|
|
|
fn add_service(&mut self, service: Box<dyn Service>) {
|
|
|
|
let name = service.name();
|
|
|
|
self.services.insert(name, service);
|
|
|
|
}
|
|
|
|
|
2023-10-08 21:44:54 +08:00
|
|
|
pub fn contains(&self, name: &str) -> bool {
|
|
|
|
self.services.contains_key(name)
|
|
|
|
}
|
|
|
|
|
2022-05-12 17:35:25 +08:00
|
|
|
pub fn subscribe(&mut self, name: &str, conn: ConnInner, sub: bool) {
|
2023-10-08 21:44:54 +08:00
|
|
|
if let Some(s) = self.services.get(name) {
|
2022-05-12 17:35:25 +08:00
|
|
|
if s.is_subed(conn.id()) == sub {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if sub {
|
|
|
|
s.on_subscribe(conn.clone());
|
|
|
|
} else {
|
|
|
|
s.on_unsubscribe(conn.id());
|
|
|
|
}
|
2024-02-27 22:28:23 +08:00
|
|
|
#[cfg(target_os = "macos")]
|
|
|
|
self.update_enable_retina();
|
2022-05-12 17:35:25 +08:00
|
|
|
}
|
|
|
|
}
|
2022-11-03 21:09:37 +08:00
|
|
|
|
|
|
|
// get a new unique id
|
|
|
|
pub fn get_new_id(&mut self) -> i32 {
|
|
|
|
self.id_count += 1;
|
2023-07-08 15:26:24 +08:00
|
|
|
self.id_count
|
2022-11-03 21:09:37 +08:00
|
|
|
}
|
2023-10-08 21:44:54 +08:00
|
|
|
|
|
|
|
pub fn set_video_service_opt(&self, display: Option<usize>, opt: &str, value: &str) {
|
|
|
|
for (k, v) in self.services.iter() {
|
|
|
|
if let Some(display) = display {
|
|
|
|
if k != &video_service::get_service_name(display) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if Self::is_video_service_name(k) {
|
|
|
|
v.set_option(opt, value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-04-25 10:56:02 +05:30
|
|
|
fn get_subbed_displays_count(&self, conn_id: i32) -> usize {
|
|
|
|
self.services
|
|
|
|
.keys()
|
|
|
|
.filter(|k| {
|
2024-07-04 20:18:53 +08:00
|
|
|
Self::is_video_service_name(k)
|
|
|
|
&& self
|
|
|
|
.services
|
|
|
|
.get(*k)
|
|
|
|
.map(|s| s.is_subed(conn_id))
|
|
|
|
.unwrap_or(false)
|
2024-04-25 10:56:02 +05:30
|
|
|
})
|
|
|
|
.count()
|
|
|
|
}
|
|
|
|
|
2023-10-08 21:44:54 +08:00
|
|
|
fn capture_displays(
|
|
|
|
&mut self,
|
|
|
|
conn: ConnInner,
|
|
|
|
displays: &[usize],
|
|
|
|
include: bool,
|
|
|
|
exclude: bool,
|
|
|
|
) {
|
|
|
|
let displays = displays
|
|
|
|
.iter()
|
|
|
|
.map(|d| video_service::get_service_name(*d))
|
|
|
|
.collect::<Vec<_>>();
|
|
|
|
let keys = self.services.keys().cloned().collect::<Vec<_>>();
|
|
|
|
for name in keys.iter() {
|
|
|
|
if Self::is_video_service_name(&name) {
|
|
|
|
if displays.contains(&name) {
|
|
|
|
if include {
|
|
|
|
self.subscribe(&name, conn.clone(), true);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if exclude {
|
|
|
|
self.subscribe(&name, conn.clone(), false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2024-02-27 22:28:23 +08:00
|
|
|
|
|
|
|
#[cfg(target_os = "macos")]
|
|
|
|
fn update_enable_retina(&self) {
|
|
|
|
let mut video_service_count = 0;
|
|
|
|
for (name, service) in self.services.iter() {
|
|
|
|
if Self::is_video_service_name(&name) && service.ok() {
|
|
|
|
video_service_count += 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
*scrap::quartz::ENABLE_RETINA.lock().unwrap() = video_service_count < 2;
|
|
|
|
}
|
2022-05-12 17:35:25 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Drop for Server {
|
|
|
|
fn drop(&mut self) {
|
|
|
|
for s in self.services.values() {
|
|
|
|
s.join();
|
|
|
|
}
|
2022-07-07 01:27:21 +08:00
|
|
|
#[cfg(target_os = "linux")]
|
2022-07-20 09:50:08 -07:00
|
|
|
wayland::clear();
|
2022-05-12 17:35:25 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn check_zombie() {
|
|
|
|
std::thread::spawn(|| loop {
|
|
|
|
let mut lock = CHILD_PROCESS.lock().unwrap();
|
|
|
|
let mut i = 0;
|
|
|
|
while i != lock.len() {
|
|
|
|
let c = &mut (*lock)[i];
|
|
|
|
if let Ok(Some(_)) = c.try_wait() {
|
|
|
|
lock.remove(i);
|
|
|
|
} else {
|
|
|
|
i += 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
drop(lock);
|
|
|
|
std::thread::sleep(Duration::from_millis(100));
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2022-05-29 04:39:12 +08:00
|
|
|
/// Start the host server that allows the remote peer to control the current machine.
|
2022-10-22 22:19:14 +08:00
|
|
|
///
|
2022-05-29 04:39:12 +08:00
|
|
|
/// # Arguments
|
2022-10-22 22:19:14 +08:00
|
|
|
///
|
2022-05-29 04:39:12 +08:00
|
|
|
/// * `is_server` - Whether the current client is definitely the server.
|
|
|
|
/// If true, the server will be started.
|
|
|
|
/// Otherwise, client will check if there's already a server and start one if not.
|
2022-05-12 17:35:25 +08:00
|
|
|
#[cfg(any(target_os = "android", target_os = "ios"))]
|
|
|
|
#[tokio::main]
|
2023-03-27 19:13:29 +08:00
|
|
|
pub async fn start_server(_is_server: bool) {
|
2022-05-12 17:35:25 +08:00
|
|
|
crate::RendezvousMediator::start_all().await;
|
|
|
|
}
|
|
|
|
|
2022-05-29 04:39:12 +08:00
|
|
|
/// Start the host server that allows the remote peer to control the current machine.
|
2022-10-22 22:19:14 +08:00
|
|
|
///
|
2022-05-29 04:39:12 +08:00
|
|
|
/// # Arguments
|
2022-10-22 22:19:14 +08:00
|
|
|
///
|
2022-05-29 04:39:12 +08:00
|
|
|
/// * `is_server` - Whether the current client is definitely the server.
|
|
|
|
/// If true, the server will be started.
|
|
|
|
/// Otherwise, client will check if there's already a server and start one if not.
|
2022-05-12 17:35:25 +08:00
|
|
|
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
|
|
|
#[tokio::main]
|
|
|
|
pub async fn start_server(is_server: bool) {
|
|
|
|
#[cfg(target_os = "linux")]
|
|
|
|
{
|
|
|
|
log::info!("DISPLAY={:?}", std::env::var("DISPLAY"));
|
|
|
|
log::info!("XAUTHORITY={:?}", std::env::var("XAUTHORITY"));
|
|
|
|
}
|
2023-07-24 14:17:09 +08:00
|
|
|
#[cfg(windows)]
|
|
|
|
hbb_common::platform::windows::start_cpu_performance_monitor();
|
2022-05-12 17:35:25 +08:00
|
|
|
|
|
|
|
if is_server {
|
2023-05-10 23:57:46 +08:00
|
|
|
crate::common::set_server_running(true);
|
2022-05-12 17:35:25 +08:00
|
|
|
std::thread::spawn(move || {
|
|
|
|
if let Err(err) = crate::ipc::start("") {
|
|
|
|
log::error!("Failed to start ipc: {}", err);
|
2024-06-18 22:04:34 +08:00
|
|
|
if crate::is_server() {
|
2024-05-18 23:14:42 +08:00
|
|
|
log::error!("ipc is occupied by another process, try kill it");
|
2024-06-18 22:04:34 +08:00
|
|
|
std::thread::spawn(stop_main_window_process).join().ok();
|
2024-05-18 23:14:42 +08:00
|
|
|
}
|
2022-05-12 17:35:25 +08:00
|
|
|
std::process::exit(-1);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
input_service::fix_key_down_timeout_loop();
|
2022-11-30 10:28:03 +08:00
|
|
|
#[cfg(target_os = "linux")]
|
|
|
|
if crate::platform::current_is_wayland() {
|
|
|
|
allow_err!(input_service::setup_uinput(0, 1920, 0, 1080).await);
|
|
|
|
}
|
2023-06-07 23:08:50 +08:00
|
|
|
#[cfg(any(target_os = "macos", target_os = "linux"))]
|
2022-05-12 17:35:25 +08:00
|
|
|
tokio::spawn(async { sync_and_watch_config_dir().await });
|
2023-07-08 15:26:24 +08:00
|
|
|
#[cfg(target_os = "windows")]
|
|
|
|
crate::platform::try_kill_broker();
|
hwcodec, only process that start ipc server start check process (#8325)
check process send config to ipc server, other process get config from ipc server. Process will save config to toml, and the toml will be used if the config is none.
when start check process: ipc server process start or option changed
from disable to enable
when get config: main window start or option changed from disable to
enable, start_video_audio_threads.
Only windows implements signature, which is used to mark whether the gpu software and hardware information changes. After reboot, the signature doesn't change. https://asawicki.info/news_1773_how_to_programmatically_check_graphics_driver_version, use dxgi way to get software version, it's not consistent with the visible driver version, after updating intel driver with small version change, the signature doesn't change. Linux doesn't use toml file.
Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-06-12 20:40:35 +08:00
|
|
|
#[cfg(feature = "hwcodec")]
|
2024-07-12 11:08:51 +08:00
|
|
|
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
hwcodec, only process that start ipc server start check process (#8325)
check process send config to ipc server, other process get config from ipc server. Process will save config to toml, and the toml will be used if the config is none.
when start check process: ipc server process start or option changed
from disable to enable
when get config: main window start or option changed from disable to
enable, start_video_audio_threads.
Only windows implements signature, which is used to mark whether the gpu software and hardware information changes. After reboot, the signature doesn't change. https://asawicki.info/news_1773_how_to_programmatically_check_graphics_driver_version, use dxgi way to get software version, it's not consistent with the visible driver version, after updating intel driver with small version change, the signature doesn't change. Linux doesn't use toml file.
Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-06-12 20:40:35 +08:00
|
|
|
scrap::hwcodec::start_check_process();
|
2022-05-12 17:35:25 +08:00
|
|
|
crate::RendezvousMediator::start_all().await;
|
|
|
|
} else {
|
|
|
|
match crate::ipc::connect(1000, "").await {
|
|
|
|
Ok(mut conn) => {
|
|
|
|
if conn.send(&Data::SyncConfig(None)).await.is_ok() {
|
|
|
|
if let Ok(Some(data)) = conn.next_timeout(1000).await {
|
|
|
|
match data {
|
2023-02-08 19:17:59 +08:00
|
|
|
Data::SyncConfig(Some(configs)) => {
|
|
|
|
let (config, config2) = *configs;
|
2022-05-12 17:35:25 +08:00
|
|
|
if Config::set(config) {
|
|
|
|
log::info!("config synced");
|
|
|
|
}
|
|
|
|
if Config2::set(config2) {
|
|
|
|
log::info!("config2 synced");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
hwcodec, only process that start ipc server start check process (#8325)
check process send config to ipc server, other process get config from ipc server. Process will save config to toml, and the toml will be used if the config is none.
when start check process: ipc server process start or option changed
from disable to enable
when get config: main window start or option changed from disable to
enable, start_video_audio_threads.
Only windows implements signature, which is used to mark whether the gpu software and hardware information changes. After reboot, the signature doesn't change. https://asawicki.info/news_1773_how_to_programmatically_check_graphics_driver_version, use dxgi way to get software version, it's not consistent with the visible driver version, after updating intel driver with small version change, the signature doesn't change. Linux doesn't use toml file.
Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-06-12 20:40:35 +08:00
|
|
|
#[cfg(feature = "hwcodec")]
|
|
|
|
#[cfg(any(target_os = "windows", target_os = "linux"))]
|
|
|
|
crate::ipc::client_get_hwcodec_config_thread(0);
|
2022-05-12 17:35:25 +08:00
|
|
|
}
|
|
|
|
Err(err) => {
|
|
|
|
log::info!("server not started (will try to start): {}", err);
|
|
|
|
std::thread::spawn(|| start_server(true));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-04 11:23:36 +08:00
|
|
|
#[cfg(target_os = "macos")]
|
|
|
|
#[tokio::main(flavor = "current_thread")]
|
|
|
|
pub async fn start_ipc_url_server() {
|
|
|
|
log::debug!("Start an ipc server for listening to url schemes");
|
|
|
|
match crate::ipc::new_listener("_url").await {
|
|
|
|
Ok(mut incoming) => {
|
|
|
|
while let Some(Ok(conn)) = incoming.next().await {
|
|
|
|
let mut conn = crate::ipc::Connection::new(conn);
|
|
|
|
match conn.next_timeout(1000).await {
|
2023-02-08 19:17:59 +08:00
|
|
|
Ok(Some(data)) => match data {
|
|
|
|
#[cfg(feature = "flutter")]
|
|
|
|
Data::UrlLink(url) => {
|
2023-04-18 23:02:37 +08:00
|
|
|
let mut m = HashMap::new();
|
|
|
|
m.insert("name", "on_url_scheme_received");
|
|
|
|
m.insert("url", url.as_str());
|
2023-05-02 12:52:27 +08:00
|
|
|
let event = serde_json::to_string(&m).unwrap_or("".to_owned());
|
2023-05-14 18:17:02 +08:00
|
|
|
match crate::flutter::push_global_event(
|
|
|
|
crate::flutter::APP_TYPE_MAIN,
|
|
|
|
event,
|
|
|
|
) {
|
2023-04-18 23:02:37 +08:00
|
|
|
None => log::warn!("No main window app found!"),
|
|
|
|
Some(..) => {}
|
2023-02-04 11:23:36 +08:00
|
|
|
}
|
|
|
|
}
|
2023-02-08 19:17:59 +08:00
|
|
|
_ => {
|
|
|
|
log::warn!("An unexpected data was sent to the ipc url server.")
|
|
|
|
}
|
|
|
|
},
|
2023-02-04 11:23:36 +08:00
|
|
|
Err(err) => {
|
|
|
|
log::error!("{}", err);
|
|
|
|
}
|
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Err(err) => {
|
|
|
|
log::error!("{}", err);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-06-07 23:08:50 +08:00
|
|
|
#[cfg(any(target_os = "macos", target_os = "linux"))]
|
2022-05-12 17:35:25 +08:00
|
|
|
async fn sync_and_watch_config_dir() {
|
|
|
|
if crate::platform::is_root() {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
let mut cfg0 = (Config::get(), Config2::get());
|
|
|
|
let mut synced = false;
|
2024-05-02 13:23:32 +08:00
|
|
|
let tries = if crate::is_server() { 30 } else { 3 };
|
2022-05-12 17:35:25 +08:00
|
|
|
log::debug!("#tries of ipc service connection: {}", tries);
|
|
|
|
use hbb_common::sleep;
|
|
|
|
for i in 1..=tries {
|
2023-06-09 13:35:03 +08:00
|
|
|
sleep(i as f32 * CONFIG_SYNC_INTERVAL_SECS).await;
|
2022-05-12 17:35:25 +08:00
|
|
|
match crate::ipc::connect(1000, "_service").await {
|
|
|
|
Ok(mut conn) => {
|
|
|
|
if !synced {
|
|
|
|
if conn.send(&Data::SyncConfig(None)).await.is_ok() {
|
|
|
|
if let Ok(Some(data)) = conn.next_timeout(1000).await {
|
|
|
|
match data {
|
2023-02-08 19:17:59 +08:00
|
|
|
Data::SyncConfig(Some(configs)) => {
|
|
|
|
let (config, config2) = *configs;
|
2022-05-12 17:35:25 +08:00
|
|
|
let _chk = crate::ipc::CheckIfRestart::new();
|
2023-06-04 16:48:24 +08:00
|
|
|
if !config.is_empty() {
|
|
|
|
if cfg0.0 != config {
|
|
|
|
cfg0.0 = config.clone();
|
|
|
|
Config::set(config);
|
|
|
|
log::info!("sync config from root");
|
|
|
|
}
|
|
|
|
if cfg0.1 != config2 {
|
|
|
|
cfg0.1 = config2.clone();
|
|
|
|
Config2::set(config2);
|
|
|
|
log::info!("sync config2 from root");
|
|
|
|
}
|
2022-05-12 17:35:25 +08:00
|
|
|
}
|
|
|
|
synced = true;
|
|
|
|
}
|
|
|
|
_ => {}
|
|
|
|
};
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
loop {
|
2023-06-09 13:35:03 +08:00
|
|
|
sleep(CONFIG_SYNC_INTERVAL_SECS).await;
|
2022-05-12 17:35:25 +08:00
|
|
|
let cfg = (Config::get(), Config2::get());
|
|
|
|
if cfg != cfg0 {
|
|
|
|
log::info!("config updated, sync to root");
|
2023-02-08 19:17:59 +08:00
|
|
|
match conn.send(&Data::SyncConfig(Some(cfg.clone().into()))).await {
|
2022-05-12 17:35:25 +08:00
|
|
|
Err(e) => {
|
|
|
|
log::error!("sync config to root failed: {}", e);
|
2024-03-26 22:31:58 +08:00
|
|
|
match crate::ipc::connect(1000, "_service").await {
|
|
|
|
Ok(mut _conn) => {
|
|
|
|
conn = _conn;
|
|
|
|
log::info!("reconnected to ipc_service");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
_ => {}
|
|
|
|
}
|
2022-05-12 17:35:25 +08:00
|
|
|
}
|
|
|
|
_ => {
|
|
|
|
cfg0 = cfg;
|
|
|
|
conn.next_timeout(1000).await.ok();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Err(_) => {
|
|
|
|
log::info!("#{} try: failed to connect to ipc_service", i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-06-05 18:02:54 +08:00
|
|
|
log::warn!("skipped config sync");
|
2022-05-12 17:35:25 +08:00
|
|
|
}
|
2024-06-18 22:04:34 +08:00
|
|
|
|
|
|
|
#[tokio::main(flavor = "current_thread")]
|
|
|
|
pub async fn stop_main_window_process() {
|
|
|
|
// this may also kill another --server process,
|
|
|
|
// but --server usually can be auto restarted by --service, so it is ok
|
|
|
|
if let Ok(mut conn) = crate::ipc::connect(1000, "").await {
|
|
|
|
conn.send(&crate::ipc::Data::Close).await.ok();
|
|
|
|
}
|
|
|
|
#[cfg(windows)]
|
|
|
|
{
|
|
|
|
// in case above failure, e.g. zombie process
|
|
|
|
if let Err(e) = crate::platform::try_kill_rustdesk_main_window_process() {
|
|
|
|
log::error!("kill failed: {}", e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|