new mac service and local config

This commit is contained in:
rustdesk
2022-04-26 11:19:45 +08:00
parent 05ac26e5fe
commit 3566b0ee7a
15 changed files with 308 additions and 419 deletions

View File

@@ -465,3 +465,8 @@ pub fn is_ip(id: &str) -> bool {
.unwrap()
.is_match(id)
}
#[inline]
pub fn get_app_name() -> &'static str {
hbb_common::config::APP_NAME
}

View File

@@ -3,7 +3,7 @@ pub use clipboard::ClipbaordFile;
use hbb_common::{
allow_err, bail, bytes,
bytes_codec::BytesCodec,
config::{self, Config},
config::{self, Config, Config2},
futures::StreamExt as _,
futures_util::sink::SinkExt,
log, timeout, tokio,
@@ -96,15 +96,8 @@ pub enum Data {
Socks(Option<config::Socks5Server>),
FS(FS),
Test,
SyncConfigToRootReq {
from: String,
},
SyncConfigToRootResp(bool),
SyncConfigToUserReq {
username: String,
to: String,
},
SyncConfigToUserResp(bool),
SyncConfig(Option<(Config, Config2)>),
#[cfg(not(any(target_os = "android", target_os = "ios")))]
ClipbaordFile(ClipbaordFile),
ClipboardFileEnabled(bool),
}
@@ -173,6 +166,30 @@ pub async fn new_listener(postfix: &str) -> ResultType<Incoming> {
}
}
pub struct CheckIfRestart(String, Vec<String>, String);
impl CheckIfRestart {
pub fn new() -> CheckIfRestart {
CheckIfRestart(
Config::get_option("stop-service"),
Config::get_rendezvous_servers(),
Config::get_option("audio-input"),
)
}
}
impl Drop for CheckIfRestart {
fn drop(&mut self) {
if self.0 != Config::get_option("stop-service")
|| self.1 != Config::get_rendezvous_servers()
{
RendezvousMediator::restart();
}
if self.2 != Config::get_option("audio-input") {
crate::audio_service::restart();
}
}
}
async fn handle(data: Data, stream: &mut Connection) {
match data {
Data::SystemInfo(_) => {
@@ -280,21 +297,15 @@ async fn handle(data: Data, stream: &mut Connection) {
let t = Config::get_nat_type();
allow_err!(stream.send(&Data::NatType(Some(t))).await);
}
Data::SyncConfigToRootReq { from } => {
allow_err!(
stream
.send(&Data::SyncConfigToRootResp(Config::sync_config_to_root(
from
)))
.await
);
Data::SyncConfig(Some((config, config2))) => {
Config::set(config);
Config2::set(config2);
allow_err!(stream.send(&Data::SyncConfig(None)).await);
}
Data::SyncConfigToUserReq { username, to } => {
Data::SyncConfig(None) => {
allow_err!(
stream
.send(&Data::SyncConfigToUserResp(Config::sync_config_to_user(
username, to
)))
.send(&Data::SyncConfig(Some((Config::get(), Config2::get()))))
.await
);
}

View File

@@ -1,4 +1,4 @@
use hbb_common::{config::Config, log};
use hbb_common::{config::LocalConfig, log};
use std::ops::Deref;
mod cn;
@@ -19,7 +19,7 @@ pub fn translate(name: String) -> String {
}
pub fn translate_locale(name: String, locale: &str) -> String {
let mut lang = Config::get_option("lang");
let mut lang = LocalConfig::get_option("lang");
if lang.is_empty() {
lang = locale
.split("-")

View File

@@ -104,11 +104,12 @@ pub fn is_can_screen_recording(prompt: bool) -> bool {
pub fn is_installed_daemon(prompt: bool) -> bool {
let daemon = format!("{}_service.plist", crate::get_full_name());
let agent = format!("{}_server.plist", crate::get_full_name());
let agent_plist_file = format!("/Library/LaunchAgents/{}", agent);
if !prompt {
if !std::path::Path::new(&format!("/Library/LaunchDaemons/{}", daemon)).exists() {
return false;
}
if !std::path::Path::new(&format!("/Library/LaunchAgents/{}", agent)).exists() {
if !std::path::Path::new(&agent_plist_file).exists() {
return false;
}
return true;
@@ -123,43 +124,92 @@ pub fn is_installed_daemon(prompt: bool) -> bool {
let agent_plist = PRIVILEGES_SCRIPTS_DIR.get_file(&agent).unwrap();
let agent_plist_body = agent_plist.contents_utf8().unwrap();
match std::process::Command::new("osascript")
.arg("-e")
.arg(install_script_body)
.arg(daemon_plist_body)
.arg(agent_plist_body)
.arg(&get_active_username())
.spawn()
{
Ok(_) => {
std::process::exit(0);
std::thread::spawn(move || {
match std::process::Command::new("osascript")
.arg("-e")
.arg(install_script_body)
.arg(daemon_plist_body)
.arg(agent_plist_body)
.arg(&get_active_username())
.output()
{
Err(e) => {
log::error!("run osascript failed: {}", e);
}
_ => {
if std::path::Path::new(&agent_plist_file).exists() {
std::process::Command::new("osascript")
.arg("-e")
.arg(
PRIVILEGES_SCRIPTS_DIR
.get_file("run.scpt")
.unwrap()
.contents_utf8()
.unwrap(),
)
.arg(&format!(
"sleep 0.5; launchctl load -w {}; sleep 0.5; open /Applications/{}.app",
agent_plist_file,
crate::get_app_name()
))
.spawn()
.ok();
std::thread::sleep(std::time::Duration::from_millis(100)); // avoid exit crash
std::process::exit(0);
}
}
}
Err(e) => {
log::error!("run osascript failed: {}", e);
false
}
}
});
false
}
pub fn launch(load: bool) {
pub fn uninstall() -> bool {
// to-do: do together with win/linux about refactory start/stop service
if !is_installed() || !is_installed_daemon(false) {
return;
}
let mut script_filename = "load.scpt";
if !load {
script_filename = "unload.scpt";
if !is_installed_daemon(false) {
return false;
}
let script_file = PRIVILEGES_SCRIPTS_DIR.get_file(script_filename).unwrap();
let script_file = PRIVILEGES_SCRIPTS_DIR.get_file("uninstall.scpt").unwrap();
let script_body = script_file.contents_utf8().unwrap();
std::process::Command::new("osascript")
.arg("-e")
.arg(script_body)
.arg(&get_active_username())
.spawn()
.ok();
std::thread::spawn(move || {
match std::process::Command::new("osascript")
.arg("-e")
.arg(script_body)
.arg(&get_active_username())
.output()
{
Err(e) => {
log::error!("run osascript failed: {}", e);
}
_ => {
let agent = format!("{}_server.plist", crate::get_full_name());
let agent_plist_file = format!("/Library/LaunchAgents/{}", agent);
if !std::path::Path::new(&agent_plist_file).exists() {
crate::ipc::set_option("stop-service", "Y");
std::process::Command::new("osascript")
.arg("-e")
.arg(
PRIVILEGES_SCRIPTS_DIR
.get_file("run.scpt")
.unwrap()
.contents_utf8()
.unwrap(),
)
.arg(&format!(
"sleep 0.5; launchctl remove {}_server; sleep 0.5; open /Applications/{}.app",
crate::get_full_name(),
crate::get_app_name()
))
.spawn()
.ok();
std::thread::sleep(std::time::Duration::from_millis(100)); // avoid exit crash
std::process::exit(0);
}
}
}
});
true
}
pub fn get_cursor_pos() -> Option<(i32, i32)> {
@@ -352,6 +402,24 @@ pub fn lock_screen() {
pub fn start_os_service() {
log::info!("{}", crate::username());
std::thread::spawn(move || loop {
let exe = std::env::current_exe().unwrap_or_default();
let tm0 = hbb_common::get_modified_time(&exe);
loop {
std::thread::sleep(std::time::Duration::from_millis(300));
if hbb_common::get_modified_time(&exe) != tm0 {
log::info!("{:?} updated, will restart", exe);
std::process::Command::new("pkill")
.args(&["-f", exe.to_str().unwrap_or("")])
.output()
.ok();
std::process::exit(0); // self not killed by above pkill
}
}
});
if let Err(err) = crate::ipc::start("_service") {
log::error!("Failed to start ipc_service: {}", err);
}
@@ -425,7 +493,7 @@ pub fn is_installed() -> bool {
if let Ok(p) = std::env::current_exe() {
return p.to_str().unwrap_or_default().contains(&format!(
"/Applications/{}.app",
hbb_common::config::APP_NAME
crate::get_app_name(),
));
}
false

View File

@@ -1,19 +1,16 @@
on run {daemon_file, agent_file, user}
set sh1 to "echo " & quoted form of daemon_file & " > /Library/LaunchDaemons/com.carriez.RustDesk_service.plist && chown root:wheel /Library/LaunchDaemons/com.carriez.RustDesk_service.plist;"
set sh1 to "echo " & quoted form of daemon_file & " > /Library/LaunchDaemons/com.carriez.RustDesk_service.plist && chown root:wheel /Library/LaunchDaemons/com.carriez.RustDesk_service.plist;"
set sh2 to "echo " & quoted form of agent_file & " > /Library/LaunchAgents/com.carriez.RustDesk_server.plist && chown root:wheel /Library/LaunchAgents/com.carriez.RustDesk_server.plist;"
set sh2 to "echo " & quoted form of agent_file & " > /Library/LaunchAgents/com.carriez.RustDesk_server.plist && chown root:wheel /Library/LaunchAgents/com.carriez.RustDesk_server.plist;"
set sh3 to "cp -rf /Users/" & user & "/Library/Preferences/com.carriez.RustDesk/RustDesk.toml /var/root/Library/Preferences/com.carriez.RustDesk/;"
set sh4 to "cp -rf /Users/" & user & "/Library/Preferences/com.carriez.RustDesk/RustDesk2.toml /var/root/Library/Preferences/com.carriez.RustDesk/;"
set sh5 to "launchctl unload -w /Library/LaunchAgents/com.carriez.RustDesk_server.plist; launchctl load -w /Library/LaunchDaemons/com.carriez.RustDesk_service.plist;"
set sh6 to "pkill -f rustdesk; launchctl unload -w /Library/LaunchAgents/com.carriez.RustDesk_server.plist; launchctl load -w /Library/LaunchAgents/com.carriez.RustDesk_server.plist; open /Applications/RustDesk.app"
set sh5 to "launchctl load -w /Library/LaunchDaemons/com.carriez.RustDesk_service.plist;"
set sh to sh1 & sh2 & sh3 & sh4 & sh5
set sh to sh1 & sh2 & sh3 & sh4 & sh5
do shell script sh with prompt "RustDesk want to install daemon and agent" with administrator privileges
do shell script sh6
do shell script sh with prompt "RustDesk want to install daemon and agent" with administrator privileges
end run

View File

@@ -1,16 +0,0 @@
on run {user}
set sh1 to "cp -rf /Users/" & user & "/Library/Preferences/com.carriez.RustDesk/RustDesk.toml /var/root/Library/Preferences/com.carriez.RustDesk/;"
set sh2 to "cp -rf /Users/" & user & "/Library/Preferences/com.carriez.RustDesk/RustDesk2.toml /var/root/Library/Preferences/com.carriez.RustDesk/;"
set sh3 to "launchctl load -w /Library/LaunchDaemons/com.carriez.rustdesk_service.plist;"
set sh4 to "launchctl load -w /Library/LaunchAgents/com.carriez.rustdesk_server.plist;"
set sh to sh1 & sh2 & sh3
do shell script sh with prompt "RustDesk want to launch daemon" with administrator privileges
do shell script sh4
end run

View File

@@ -1,6 +0,0 @@
set sh1 to "launchctl unload -w /Library/LaunchDaemons/com.carriez.rustdesk_service.plist;"
set sh2 to "launchctl unload -w /Library/LaunchAgents/com.carriez.rustdesk_server.plist;"
do shell script sh1 with prompt "RustDesk want to unload daemon" with administrator privileges
do shell script sh2

View File

@@ -1,12 +1,10 @@
#[cfg(target_os = "macos")]
use crate::ipc::ConnectionTmpl;
use crate::ipc::Data;
use connection::{ConnInner, Connection};
pub use connection::*;
use hbb_common::{
allow_err,
anyhow::{anyhow, Context},
bail,
config::{Config, CONNECT_TIMEOUT, RELAY_PORT},
config::{Config, Config2, CONNECT_TIMEOUT, RELAY_PORT},
log,
message_proto::*,
protobuf::{Message as _, ProtobufEnum},
@@ -15,10 +13,6 @@ use hbb_common::{
sodiumoxide::crypto::{box_, secretbox, sign},
timeout, tokio, ResultType, Stream,
};
#[cfg(target_os = "macos")]
use notify::{watcher, RecursiveMode, Watcher};
#[cfg(target_os = "macos")]
use parity_tokio_ipc::ConnectionClient;
use service::{GenericService, Service, ServiceTmpl, Subscriber};
use std::{
collections::HashMap,
@@ -339,101 +333,46 @@ async fn sync_and_watch_config_dir() {
return;
}
match crate::ipc::connect(1000, "_service").await {
Ok(mut conn) => {
match sync_config_to_user(&mut conn).await {
Err(e) => log::error!("sync config to user failed:{}", e),
_ => {}
}
tokio::spawn(async move {
log::info!(
"watching config dir: {}",
Config::path("").to_str().unwrap().to_string()
);
let (tx, rx) = std::sync::mpsc::channel();
let mut watcher = watcher(tx, Duration::from_secs(2)).unwrap();
watcher
.watch(Config::path("").as_path(), RecursiveMode::Recursive)
.unwrap();
for i in 1..=6 {
sleep(i as f32 * 0.3).await;
match crate::ipc::connect(1000, "_service").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 {
Data::SyncConfig(Some((config, config2))) => {
let _chk = crate::ipc::CheckIfRestart::new();
Config::set(config);
Config2::set(config2);
log::info!("sync config from root");
}
_ => {}
};
};
}
let mut cfg0 = (Config::get(), Config2::get());
loop {
let ev = rx.recv();
match ev {
Ok(event) => match event {
notify::DebouncedEvent::Write(path) => {
log::info!(
"config file changed, call ipc_service to sync: {}",
path.to_str().unwrap().to_string()
);
match sync_config_to_root(&mut conn, path).await {
Err(e) => log::error!("sync config to root failed: {}", e),
_ => {}
}
sleep(0.3).await;
let cfg = (Config::get(), Config2::get());
if cfg != cfg0 {
cfg0 = cfg;
log::info!("config updated, sync to root");
match conn.send(&Data::SyncConfig(Some(cfg0.clone()))).await {
Err(e) => {
log::error!("sync config to root failed: {}", e);
}
x => {
log::debug!("another {:?}", x)
_ => {
conn.next_timeout(1000).await.ok();
}
},
Err(e) => println!("watch error: {:?}", e),
}
}
}
});
}
Err(_) => {
log::info!("connect ipc_service failed, skip config sync");
return;
}
Err(_) => {
log::info!("#{} try: failed to connect to ipc_service", i);
}
}
}
}
#[cfg(target_os = "macos")]
async fn sync_config_to_user(conn: &mut ConnectionTmpl<ConnectionClient>) -> ResultType<()> {
allow_err!(
conn.send(&Data::SyncConfigToUserReq {
username: crate::username(),
to: Config::path("").to_str().unwrap().to_string(),
})
.await
);
if let Some(data) = conn.next_timeout(2000).await? {
match data {
Data::SyncConfigToUserResp(success) => {
log::info!("copy and reload config dir success: {:?}", success);
}
_ => {}
};
};
Ok(())
}
#[cfg(target_os = "macos")]
async fn sync_config_to_root(
conn: &mut ConnectionTmpl<ConnectionClient>,
from: std::path::PathBuf,
) -> ResultType<()> {
allow_err!(
conn.send(&Data::SyncConfigToRootReq {
from: from.to_str().unwrap().to_string()
})
.await
);
// todo: this code will block outer loop, resolve it later.
// if let Some(data) = conn.next_timeout(2000).await? {
// match data {
// Data::SyncConfigToRootResp(success) => {
// log::info!("copy config to root dir success: {:?}", success);
// }
// x => {
// log::info!("receive another {:?}", x)
// }
// };
// };
Ok(())
log::error!("skipped config sync");
}

View File

@@ -8,7 +8,7 @@ use crate::common::SOFTWARE_UPDATE_URL;
use crate::ipc;
use hbb_common::{
allow_err,
config::{self, Config, Fav, PeerConfig, APP_NAME, ICON},
config::{self, Config, LocalConfig, PeerConfig, APP_NAME, ICON},
log, sleep,
tokio::{self, time},
};
@@ -221,11 +221,11 @@ impl UI {
}
fn get_remote_id(&mut self) -> String {
Config::get_remote_id()
LocalConfig::get_remote_id()
}
fn set_remote_id(&mut self, id: String) {
Config::set_remote_id(&id);
LocalConfig::set_remote_id(&id);
}
fn goto_install(&mut self) {
@@ -275,7 +275,11 @@ impl UI {
}
fn get_local_option(&self, key: String) -> String {
Config::get_option(&key)
LocalConfig::get_option(&key)
}
fn set_local_option(&self, key: String, value: String) {
LocalConfig::set_option(key, value);
}
fn peer_has_password(&self, id: String) -> bool {
@@ -353,6 +357,13 @@ impl UI {
}
fn set_option(&self, key: String, value: String) {
#[cfg(target_os = "macos")]
if &key == "stop-service" {
let is_stop = value == "Y";
if is_stop && crate::platform::macos::uninstall() {
return;
}
}
let mut options = self.2.lock().unwrap();
if value.is_empty() {
options.remove(&key);
@@ -360,11 +371,6 @@ impl UI {
options.insert(key.clone(), value.clone());
}
ipc::set_options(options.clone()).ok();
#[cfg(target_os = "macos")]
if &key == "stop-service" {
crate::platform::macos::launch(value != "Y");
}
}
// TODO: ui prompt
@@ -431,11 +437,11 @@ impl UI {
fn save_size(&mut self, x: i32, y: i32, w: i32, h: i32) {
crate::server::input_service::fix_key_down_timeout_at_exit();
Config::set_size(x, y, w, h);
LocalConfig::set_size(x, y, w, h);
}
fn get_size(&mut self) -> Value {
let s = Config::get_size();
let s = LocalConfig::get_size();
let mut v = Value::array(0);
v.push(s.0);
v.push(s.1);
@@ -470,7 +476,7 @@ impl UI {
}
fn get_fav(&self) -> Value {
Value::from_iter(Fav::load().peers)
Value::from_iter(LocalConfig::get_fav())
}
fn store_fav(&self, fav: Value) {
@@ -482,10 +488,11 @@ impl UI {
}
}
});
Fav::store(tmp);
LocalConfig::set_fav(tmp);
}
fn get_recent_sessions(&mut self) -> Value {
// to-do: limit number of recent sessions, and remove old peer file
let peers: Vec<Value> = PeerConfig::peers()
.drain(..)
.map(|p| Self::get_peer_value(p.0, p.2))
@@ -701,6 +708,7 @@ impl sciter::EventHandler for UI {
fn get_options();
fn get_option(String);
fn get_local_option(String);
fn set_local_option(String, String);
fn get_peer_option(String, String);
fn peer_has_password(String);
fn forget_password(String);
@@ -830,7 +838,7 @@ fn check_connect_status(
reconnect: bool,
) -> (Arc<Mutex<(i32, bool)>>, Arc<Mutex<HashMap<String, String>>>) {
let status = Arc::new(Mutex::new((0, false)));
let options = Arc::new(Mutex::new(HashMap::new()));
let options = Arc::new(Mutex::new(Config::get_options()));
let cloned = status.clone();
let cloned_options = options.clone();
std::thread::spawn(move || check_connect_status_(reconnect, cloned, cloned_options));

View File

@@ -66,7 +66,7 @@ class SessionStyle: Reactor.Component {
event click $(span.inactive) {
var option = getSessionsStyleOption(this.type);
var sessionsStyle = getSessionsStyle(this.type);
handler.set_option(option, sessionsStyle == "tile" ? "list" : "tile");
handler.set_local_option(option, sessionsStyle == "tile" ? "list" : "tile");
app.multipleSessions.update();
}
}
@@ -294,7 +294,7 @@ class MultipleSessions: Reactor.Component {
if (el.id == "lan") {
discover();
}
handler.set_option('show-sessions-type', el.id || "");
handler.set_local_option('show-sessions-type', el.id || "");
this.stupidUpdate();
}

View File

@@ -7,7 +7,7 @@ view.windowMinSize = (500, 300);
var app;
var tmp = handler.get_connect_status();
var connect_status = tmp[0];
var service_stopped = false;
var service_stopped = handler.get_option("stop-service") == "Y";
var software_update_url = "";
var key_confirmed = tmp[1];
var system_error = "";
@@ -368,7 +368,7 @@ class App: Reactor.Component
{is_win && handler.is_installed() && !software_update_url && handler.is_installed_lower_version() ? <UpgradeMe /> : ""}
{is_can_screen_recording ? "": <CanScreenRecording />}
{is_can_screen_recording && !handler.is_process_trusted(false) ? <TrustMe /> : ""}
{is_can_screen_recording && handler.is_process_trusted(false) && handler.is_installed() && !handler.is_installed_daemon(false) ? <InstallDaemon /> : ""}
{!service_stopped && is_can_screen_recording && handler.is_process_trusted(false) && handler.is_installed() && !handler.is_installed_daemon(false) ? <InstallDaemon /> : ""}
{system_error ? <SystemError /> : ""}
{!system_error && handler.is_login_wayland() && !handler.current_is_wayland() ? <FixWayland /> : ""}
{!system_error && handler.current_is_wayland() ? <ModifyDefaultLogin /> : ""}
@@ -772,8 +772,7 @@ function checkConnectStatus() {
var tmp = !!handler.get_option("stop-service");
if (tmp != service_stopped) {
service_stopped = tmp;
app.connect_status.update();
myIdMenu.update();
app.update();
}
tmp = handler.get_connect_status();
if (tmp[0] != connect_status) {