use hbb_common::log; #[cfg(not(any(target_os = "android", target_os = "ios")))] use hbb_common::platform::register_breakdown_handler; /// 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(not(any(target_os = "android", target_os = "ios")))] register_breakdown_handler(); #[cfg(target_os = "linux")] #[cfg(feature = "flutter")] { let (k, v) = ("LIBGL_ALWAYS_SOFTWARE", "true"); if !hbb_common::config::Config::get_option("allow-always-software-render").is_empty() { std::env::set_var(k, v); } else { std::env::remove_var(k); } } #[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); 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(windows)] { _is_quick_support |= !crate::platform::is_installed() && args.is_empty() && (arg_exe.to_lowercase().ends_with("qs.exe") || (!click_setup && crate::platform::is_elevated(None).unwrap_or(false))); crate::portable_service::client::set_quick_support(_is_quick_support); } #[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 { use crate::portable_service::client; if let Err(e) = client::start_portable_service(client::StartPara::Direct) { 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] == "--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] == "--tray" { crate::tray::start_tray(); 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 with user {}", crate::username()); #[cfg(any(target_os = "linux", target_os = "windows"))] { crate::start_server(true); return None; } #[cfg(target_os = "macos")] { let handler = std::thread::spawn(move || crate::start_server(true)); 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 { if crate::platform::is_root() { crate::ipc::set_permanent_password(args[1].to_owned()).unwrap(); } else { println!("Administrative privileges required!"); } } 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 crate::ui_interface::start_option_status_sync(); } } //_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"; })?; let peer_id = args.next().unwrap_or("".to_string()); if peer_id.is_empty() { eprintln!("please provide a valid peer id"); return None; } let mut switch_uuid = None; while let Some(item) = args.next() { if item == "--switch_uuid" { switch_uuid = args.next(); } } let mut param_array = vec![]; if switch_uuid.is_some() { let switch_uuid = switch_uuid.map_or("".to_string(), |p| format!("switch_uuid={}", p)); param_array.push(switch_uuid); } let params = param_array.join("&"); let params_flag = if params.is_empty() { "" } else { "?" }; #[allow(unused)] let uni_links = format!( "rustdesk://connection/new/{}{}{}", peer_id, params_flag, params ); #[cfg(target_os = "linux")] { use crate::dbus::invoke_new_connection; match invoke_new_connection(uni_links) { 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 res = crate::platform::send_message_to_hnwd( "FLUTTER_RUNNER_WIN32_WINDOW", "RustDesk", (WM_USER + 2) as _, // referred from unilinks desktop pub uni_links.as_str(), false, ); return if res { None } else { Some(Vec::new()) }; } #[cfg(target_os = "macos")] { return if let Err(_) = crate::ipc::send_url_scheme(uni_links) { Some(Vec::new()) } else { None }; } }