diff --git a/Cargo.lock b/Cargo.lock index 1ea2e7c9b..435c226dc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1115,6 +1115,25 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" +[[package]] +name = "fsevent" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ab7d1bd1bd33cc98b0889831b72da23c0aa4df9cec7e0702f46ecea04b35db6" +dependencies = [ + "bitflags", + "fsevent-sys", +] + +[[package]] +name = "fsevent-sys" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f41b048a94555da0f42f1d632e2e19510084fb8e303b0daa2816e733fb3644a0" +dependencies = [ + "libc", +] + [[package]] name = "fuchsia-cprng" version = "0.1.1" @@ -1698,6 +1717,26 @@ dependencies = [ "tiff", ] +[[package]] +name = "inotify" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4816c66d2c8ae673df83366c18341538f234a26d65a9ecea5c348b453ac1d02f" +dependencies = [ + "bitflags", + "inotify-sys", + "libc", +] + +[[package]] +name = "inotify-sys" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb" +dependencies = [ + "libc", +] + [[package]] name = "instant" version = "0.1.12" @@ -2060,6 +2099,18 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "mio-extras" +version = "2.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52403fe290012ce777c4626790c8951324a2b9e3316b3143779c72b029742f19" +dependencies = [ + "lazycell", + "log", + "mio 0.6.23", + "slab", +] + [[package]] name = "mio-named-pipes" version = "0.1.7" @@ -2224,6 +2275,24 @@ dependencies = [ "version_check", ] +[[package]] +name = "notify" +version = "4.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae03c8c853dba7bfd23e571ff0cff7bc9dceb40a4cd684cd1681824183f45257" +dependencies = [ + "bitflags", + "filetime", + "fsevent", + "fsevent-sys", + "inotify", + "libc", + "mio 0.6.23", + "mio-extras", + "walkdir", + "winapi 0.3.9", +] + [[package]] name = "ntapi" version = "0.3.6" @@ -3153,6 +3222,7 @@ dependencies = [ "mac_address", "machine-uid", "magnum-opus", + "notify", "objc", "parity-tokio-ipc", "psutil", diff --git a/Cargo.toml b/Cargo.toml index 6a9325e25..9a13bedaf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -69,6 +69,7 @@ cocoa = "0.24" dispatch = "0.2" core-foundation = "0.9" core-graphics = "0.22" +notify = "4.0.17" [target.'cfg(target_os = "linux")'.dependencies] libpulse-simple-binding = "2.24" diff --git a/libs/hbb_common/src/config.rs b/libs/hbb_common/src/config.rs index 21ca74a06..85dcfd984 100644 --- a/libs/hbb_common/src/config.rs +++ b/libs/hbb_common/src/config.rs @@ -190,6 +190,11 @@ impl Config2 { Config::load_::("2") } + fn reload(&mut self) { + let new_config = Config2::load(); + *self = new_config; + } + fn store(&self) { Config::store_(self, "2"); } @@ -214,6 +219,11 @@ impl Config { cfg } + fn reload(&mut self) { + let new_config = Config::load(); + *self = new_config; + } + fn store_(config: &T, suffix: &str) { let file = Self::file_(suffix); if let Err(err) = confy::store_path(file, config) { @@ -272,7 +282,7 @@ impl Config { } } - fn path>(p: P) -> PathBuf { + pub fn path>(p: P) -> PathBuf { #[cfg(any(target_os = "android", target_os = "ios"))] { let mut path: PathBuf = APP_DIR.read().unwrap().clone().into(); @@ -659,6 +669,93 @@ impl Config { Some(_) => NetworkType::ProxySocks, } } + + pub fn sync_config_to_user>(target_username: String, to_dir: P) -> bool { + let config1_root_file_path = Config::file_(""); + let config1_filename = config1_root_file_path.file_name(); + + let config2_root_file_path = Config::file_("2"); + let config2_filename = config2_root_file_path.file_name(); + + let config1_to_file_path = to_dir + .as_ref() + .join(PathBuf::from(&config1_filename.unwrap())); + let config2_to_file_path = to_dir + .as_ref() + .join(PathBuf::from(&config2_filename.unwrap())); + + log::info!( + "config1_root_path:{}", + &config1_root_file_path.as_path().to_str().unwrap() + ); + log::info!( + "config2_root_path:{}", + &config2_root_file_path.as_path().to_str().unwrap() + ); + log::info!( + "config1_to_path:{}", + &config1_to_file_path.as_path().to_str().unwrap() + ); + log::info!( + "config2_to_path:{}", + &config2_to_file_path.as_path().to_str().unwrap() + ); + + match std::fs::copy(&config1_root_file_path, &config1_to_file_path) { + Err(e) => log::error!( + "copy config {} to user failed: {}", + config1_filename.unwrap().to_str().unwrap(), + e + ), + _ => {} + } + + match std::fs::copy(&config2_root_file_path, &config2_to_file_path) { + Err(e) => log::error!( + "copy config {} to user failed: {}", + config2_filename.unwrap().to_str().unwrap(), + e + ), + _ => {} + } + + let success = std::process::Command::new("chown") + .arg(&target_username.to_string()) + .arg(&config1_to_file_path.to_str().unwrap().to_string()) + .arg(&config2_to_file_path.to_str().unwrap().to_string()) + .spawn() + .is_ok(); + + if success { + CONFIG.write().unwrap().reload(); + CONFIG2.write().unwrap().reload(); + } + + return success; + } + + pub fn sync_config_to_root>(from_file_path: P) -> bool { + if let Some(filename) = from_file_path.as_ref().file_name() { + let to = Config::path(filename); + return match std::fs::copy(from_file_path, &to) { + Ok(count) => { + if count > 0 { + return std::process::Command::new("chown") + .arg("root") + .arg(&to.to_str().unwrap().to_string()) + .spawn() + .is_ok(); + } + false + } + Err(e) => { + log::error!("sync_config_to_root failed: {}", e); + false + } + }; + } + false + } } const PEERS: &str = "peers"; diff --git a/src/ipc.rs b/src/ipc.rs index e197d804d..8bd206f67 100644 --- a/src/ipc.rs +++ b/src/ipc.rs @@ -92,6 +92,15 @@ pub enum Data { Socks(Option), FS(FS), Test, + SyncConfigToRootReq { + from: String, + }, + SyncConfigToRootResp(bool), + SyncConfigToUserReq { + username: String, + to: String, + }, + SyncConfigToUserResp(bool), } #[tokio::main(flavor = "current_thread")] @@ -252,6 +261,24 @@ 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::SyncConfigToUserReq { username, to } => { + allow_err!( + stream + .send(&Data::SyncConfigToUserResp(Config::sync_config_to_user( + username, to + ))) + .await + ); + } _ => {} } } diff --git a/src/main.rs b/src/main.rs index e2d00d0c5..39b52cb58 100644 --- a/src/main.rs +++ b/src/main.rs @@ -101,6 +101,10 @@ fn main() { ipc::set_password(args[1].to_owned()).unwrap(); } return; + } else if cfg!(target_os = "macos") && args[0] == "--daemon" { + log::info!("start --daemon"); + crate::platform::start_daemon(); + return; } } ui::start(&mut args[..]); diff --git a/src/platform/macos.rs b/src/platform/macos.rs index 5834fd024..f278fdf9c 100644 --- a/src/platform/macos.rs +++ b/src/platform/macos.rs @@ -333,3 +333,11 @@ pub fn block_input(_v: bool) { pub fn is_installed() -> bool { true } + +pub fn start_daemon(){ + log::info!("{}",crate::username()); + if let Err(err) = crate::ipc::start("_daemon") { + log::error!("Failed to start ipc_daemon: {}", err); + std::process::exit(-1); + } +} diff --git a/src/server.rs b/src/server.rs index 451c18e1b..13e3a678d 100644 --- a/src/server.rs +++ b/src/server.rs @@ -1,4 +1,4 @@ -use crate::ipc::Data; +use crate::ipc::{ConnectionTmpl, Data}; use connection::{ConnInner, Connection}; use hbb_common::{ allow_err, @@ -9,12 +9,15 @@ use hbb_common::{ message_proto::*, protobuf::{Message as _, ProtobufEnum}, rendezvous_proto::*, - sleep, + sleep, socket_client, sodiumoxide::crypto::{box_, secretbox, sign}, timeout, tokio, ResultType, Stream, - socket_client, }; +use notify::{watcher, RecursiveMode, Watcher}; +use parity_tokio_ipc::ConnectionClient; use service::{GenericService, Service, ServiceTmpl, Subscriber}; +use std::path::PathBuf; +use std::time::Duration; use std::{ collections::HashMap, net::SocketAddr, @@ -268,6 +271,9 @@ pub async fn start_server(is_server: bool, _tray: bool) { log::info!("DISPLAY={:?}", std::env::var("DISPLAY")); log::info!("XAUTHORITY={:?}", std::env::var("XAUTHORITY")); } + + sync_and_watch_config_dir().await; + if is_server { std::thread::spawn(move || { if let Err(err) = crate::ipc::start("") { @@ -317,3 +323,105 @@ pub async fn start_server(is_server: bool, _tray: bool) { } } } + +async fn sync_and_watch_config_dir() { + if crate::username() == "root" { + return; + } + + match crate::ipc::connect(1000, "_daemon").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(); + + loop { + let ev = rx.recv(); + match ev { + Ok(event) => match event { + notify::DebouncedEvent::Write(path) => { + log::info!( + "config file changed, call ipc_daemon 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), + _ => {} + } + } + x => { + log::debug!("another {:?}", x) + } + }, + Err(e) => println!("watch error: {:?}", e), + } + } + }); + } + Err(_) => { + log::info!("connect ipc_daemon failed, skip config sync"); + return; + } + } +} + +async fn sync_config_to_user(conn: &mut ConnectionTmpl) -> 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(()) +} + +async fn sync_config_to_root( + conn: &mut ConnectionTmpl, + from: 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(()) +}