use hbb_common::log; /// shared by flutter and sciter main function /// /// [Note] /// If it returns [`None`], then the process will terminate, and flutter gui will not be started. /// If it returns [`Some`], then the process will continue, and flutter gui will be started. pub fn core_main() -> Option> { // https://docs.rs/flexi_logger/latest/flexi_logger/error_info/index.html#write // though async logger more efficient, but it also causes more problems, disable it for now // let mut _async_logger_holder: Option = None; let mut args = Vec::new(); let mut flutter_args = Vec::new(); let mut i = 0; let mut _is_elevate = false; let mut _is_run_as_system = false; let mut _is_quick_support = false; let mut _is_flutter_connect = false; let mut arg_exe = Default::default(); for arg in std::env::args() { // to-do: how to pass to flutter? if i == 0 { arg_exe = arg; } else if i > 0 { #[cfg(feature = "flutter")] if arg == "--connect" { _is_flutter_connect = true; } if arg == "--elevate" { _is_elevate = true; } else if arg == "--run-as-system" { _is_run_as_system = true; } else if arg == "--quick_support" { _is_quick_support = true; } else { args.push(arg); } } i += 1; } #[cfg(feature = "flutter")] if _is_flutter_connect { return core_main_invoke_new_connection(std::env::args()); } let click_setup = cfg!(windows) && args.is_empty() && crate::common::is_setup(&arg_exe); #[cfg(not(feature = "flutter"))] { _is_quick_support = cfg!(windows) && args.is_empty() && arg_exe.to_lowercase().ends_with("qs.exe"); } if click_setup { args.push("--install".to_owned()); flutter_args.push("--install".to_string()); } if args.contains(&"--noinstall".to_string()) { args.clear(); } if args.len() > 0 && args[0] == "--version" { println!("{}", crate::VERSION); return None; } #[cfg(debug_assertions)] { use hbb_common::env_logger::*; init_from_env(Env::default().filter_or(DEFAULT_FILTER_ENV, "info")); } #[cfg(not(debug_assertions))] { let mut path = hbb_common::config::Config::log_path(); if args.len() > 0 && args[0].starts_with("--") { let name = args[0].replace("--", ""); if !name.is_empty() { path.push(name); } } use flexi_logger::*; if let Ok(x) = Logger::try_with_env_or_str("debug") { // _async_logger_holder = x.log_to_file(FileSpec::default().directory(path)) //.write_mode(WriteMode::Async) .format(opt_format) .rotate( Criterion::Age(Age::Day), Naming::Timestamps, Cleanup::KeepLogFiles(6), ) .start() .ok(); } } #[cfg(windows)] if !crate::platform::is_installed() && args.is_empty() && _is_quick_support && !_is_elevate && !_is_run_as_system { if let Err(e) = crate::portable_service::client::start_portable_service() { log::error!("Failed to start portable service:{:?}", e); } } #[cfg(windows)] if !crate::platform::is_installed() && (_is_elevate || _is_run_as_system) { crate::platform::elevate_or_run_as_system(click_setup, _is_elevate, _is_run_as_system); return None; } if args.is_empty() { std::thread::spawn(move || crate::start_server(false)); } else { #[cfg(windows)] { use crate::platform; if args[0] == "--uninstall" { if let Err(err) = platform::uninstall_me() { log::error!("Failed to uninstall: {}", err); } return None; } else if args[0] == "--after-install" { if let Err(err) = platform::run_after_install() { log::error!("Failed to after-install: {}", err); } return None; } else if args[0] == "--before-uninstall" { if let Err(err) = platform::run_before_uninstall() { log::error!("Failed to before-uninstall: {}", err); } return None; } else if args[0] == "--update" { hbb_common::allow_err!(platform::update_me()); return None; } else if args[0] == "--reinstall" { hbb_common::allow_err!(platform::uninstall_me()); hbb_common::allow_err!(platform::install_me( "desktopicon startmenu", "".to_owned(), false, false, )); return None; } else if args[0] == "--silent-install" { hbb_common::allow_err!(platform::install_me( "desktopicon startmenu", "".to_owned(), true, args.len() > 1, )); return None; } else if args[0] == "--extract" { #[cfg(feature = "with_rc")] hbb_common::allow_err!(crate::rc::extract_resources(&args[1])); return None; } else if args[0] == "--tray" { crate::tray::start_tray(); return None; } else if args[0] == "--portable-service" { crate::platform::elevate_or_run_as_system( click_setup, _is_elevate, _is_run_as_system, ); return None; } } if args[0] == "--remove" { if args.len() == 2 { // sleep a while so that process of removed exe exit std::thread::sleep(std::time::Duration::from_secs(1)); std::fs::remove_file(&args[1]).ok(); return None; } } else if args[0] == "--service" { log::info!("start --service"); crate::start_os_service(); return None; } else if args[0] == "--server" { log::info!("start --server"); #[cfg(target_os = "windows")] { crate::start_server(true); return None; } #[cfg(target_os = "macos")] { std::thread::spawn(move || crate::start_server(true)); crate::tray::make_tray(); return None; } #[cfg(target_os = "linux")] { let handler = std::thread::spawn(move || crate::start_server(true)); // Show the tray in linux only when current user is a normal user // [Note] // As for GNOME, the tray cannot be shown in user's status bar. // As for KDE, the tray can be shown without user's theme. if !crate::platform::is_root() { crate::tray::start_tray(); } // prevent server exit when encountering errors from tray hbb_common::allow_err!(handler.join()); } } else if args[0] == "--import-config" { if args.len() == 2 { let filepath; let path = std::path::Path::new(&args[1]); if !path.is_absolute() { let mut cur = std::env::current_dir().unwrap(); cur.push(path); filepath = cur.to_str().unwrap().to_string(); } else { filepath = path.to_str().unwrap().to_string(); } import_config(&filepath); } return None; } else if args[0] == "--password" { if args.len() == 2 { crate::ipc::set_permanent_password(args[1].to_owned()).unwrap(); } return None; } else if args[0] == "--check-hwcodec-config" { #[cfg(feature = "hwcodec")] scrap::hwcodec::check_config(); return None; } else if args[0] == "--cm" { // call connection manager to establish connections // meanwhile, return true to call flutter window to show control panel #[cfg(feature = "flutter")] crate::flutter::connection_manager::start_listen_ipc_thread(); } } //_async_logger_holder.map(|x| x.flush()); #[cfg(feature = "flutter")] return Some(flutter_args); #[cfg(not(feature = "flutter"))] return Some(args); } fn import_config(path: &str) { use hbb_common::{config::*, get_exe_time, get_modified_time}; let path2 = path.replace(".toml", "2.toml"); let path2 = std::path::Path::new(&path2); let path = std::path::Path::new(path); log::info!("import config from {:?} and {:?}", path, path2); let config: Config = load_path(path.into()); if config.is_empty() { log::info!("Empty source config, skipped"); return; } if get_modified_time(&path) > get_modified_time(&Config::file()) && get_modified_time(&path) < get_exe_time() { if store_path(Config::file(), config).is_err() { log::info!("config written"); } } let config2: Config2 = load_path(path2.into()); if get_modified_time(&path2) > get_modified_time(&Config2::file()) { if store_path(Config2::file(), config2).is_err() { log::info!("config2 written"); } } } /// invoke a new connection /// /// [Note] /// this is for invoke new connection from dbus. /// If it returns [`None`], then the process will terminate, and flutter gui will not be started. /// If it returns [`Some`], then the process will continue, and flutter gui will be started. #[cfg(feature = "flutter")] fn core_main_invoke_new_connection(mut args: std::env::Args) -> Option> { args.position(|element| { return element == "--connect"; }) .unwrap(); let peer_id = args.next().unwrap_or("".to_string()); if peer_id.is_empty() { eprintln!("please provide a valid peer id"); return None; } #[cfg(target_os = "linux")] { use crate::dbus::invoke_new_connection; match invoke_new_connection(peer_id) { Ok(()) => { return None; } Err(err) => { log::error!("{}", err.as_ref()); // return Some to invoke this new connection by self return Some(Vec::new()); } } } #[cfg(windows)] { use winapi::um::winuser::WM_USER; let uni_links = format!("rustdesk://connection/new/{}", peer_id); let res = crate::platform::send_message_to_hnwd( "FLUTTER_RUNNER_WIN32_WINDOW", "RustDesk", (WM_USER + 2) as _, // refered from unilinks desktop pub uni_links.as_str(), true, ); return if res { None } else { Some(Vec::new()) }; } #[cfg(target_os = "macos")] return Some(Vec::new()); }