Merge pull request #5191 from fufesou/refact/linux_headless_allow_option
Refact/linux headless allow option
This commit is contained in:
commit
69f1969e60
@ -372,7 +372,7 @@ class _DesktopHomePageState extends State<DesktopHomePage>
|
||||
} else if (Platform.isLinux) {
|
||||
if (bind.mainCurrentIsWayland()) {
|
||||
return buildInstallCard(
|
||||
"Warning", translate("wayland_experiment_tip"), "", () async {},
|
||||
"Warning", "wayland_experiment_tip", "", () async {},
|
||||
help: 'Help',
|
||||
link: 'https://rustdesk.com/docs/en/manual/linux/#x11-required');
|
||||
} else if (bind.mainIsLoginWayland()) {
|
||||
|
@ -312,20 +312,26 @@ class _GeneralState extends State<_General> {
|
||||
}
|
||||
|
||||
Widget other() {
|
||||
return _Card(title: 'Other', children: [
|
||||
final children = [
|
||||
_OptionCheckBox(context, 'Confirm before closing multiple tabs',
|
||||
'enable-confirm-closing-tabs'),
|
||||
_OptionCheckBox(context, 'Adaptive Bitrate', 'enable-abr'),
|
||||
if (Platform.isLinux)
|
||||
Tooltip(
|
||||
message: translate('software_render_tip'),
|
||||
child: _OptionCheckBox(
|
||||
context,
|
||||
"Always use software rendering",
|
||||
'allow-always-software-render',
|
||||
),
|
||||
)
|
||||
]);
|
||||
_OptionCheckBox(context, 'Adaptive Bitrate', 'enable-abr')
|
||||
];
|
||||
if (Platform.isLinux) {
|
||||
children.add(Tooltip(
|
||||
message: translate('software_render_tip'),
|
||||
child: _OptionCheckBox(
|
||||
context,
|
||||
"Always use software rendering",
|
||||
'allow-always-software-render',
|
||||
),
|
||||
));
|
||||
}
|
||||
if (bind.mainShowOption(key: 'allow-linux-headless')) {
|
||||
children.add(_OptionCheckBox(
|
||||
context, 'Allow linux headless', 'allow-linux-headless'));
|
||||
}
|
||||
return _Card(title: 'Other', children: children);
|
||||
}
|
||||
|
||||
Widget hwcodec() {
|
||||
|
@ -32,6 +32,10 @@ pub const COMPRESS_LEVEL: i32 = 3;
|
||||
const SERIAL: i32 = 3;
|
||||
const PASSWORD_ENC_VERSION: &str = "00";
|
||||
|
||||
// config2 options
|
||||
#[cfg(target_os = "linux")]
|
||||
pub const CONFIG_OPTION_ALLOW_LINUX_HEADLESS: &str = "allow-linux-headless";
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
lazy_static::lazy_static! {
|
||||
pub static ref ORG: Arc<RwLock<String>> = Arc::new(RwLock::new("com.carriez".to_owned()));
|
||||
|
@ -90,6 +90,7 @@ pub const LOGIN_MSG_PASSWORD_EMPTY: &str = "Empty Password";
|
||||
pub const LOGIN_MSG_PASSWORD_WRONG: &str = "Wrong Password";
|
||||
pub const LOGIN_MSG_NO_PASSWORD_ACCESS: &str = "No Password Access";
|
||||
pub const LOGIN_MSG_OFFLINE: &str = "Offline";
|
||||
pub const LOGIN_SCREEN_WAYLAND: &str = "Wayland login screen is not supported";
|
||||
#[cfg(target_os = "linux")]
|
||||
pub const SCRAP_UBUNTU_HIGHER_REQUIRED: &str = "Wayland requires Ubuntu 21.04 or higher version.";
|
||||
#[cfg(target_os = "linux")]
|
||||
@ -2100,7 +2101,13 @@ struct LoginErrorMsgBox {
|
||||
lazy_static::lazy_static! {
|
||||
static ref LOGIN_ERROR_MAP: Arc<HashMap<&'static str, LoginErrorMsgBox>> = {
|
||||
use hbb_common::config::LINK_HEADLESS_LINUX_SUPPORT;
|
||||
let map = HashMap::from([(LOGIN_MSG_DESKTOP_SESSION_NOT_READY, LoginErrorMsgBox{
|
||||
let map = HashMap::from([(LOGIN_SCREEN_WAYLAND, LoginErrorMsgBox{
|
||||
msgtype: "error",
|
||||
title: "Login Error",
|
||||
text: "Login screen using Wayland is not supported",
|
||||
link: "https://rustdesk.com/docs/en/manual/linux/#login-screen",
|
||||
try_again: true,
|
||||
}), (LOGIN_MSG_DESKTOP_SESSION_NOT_READY, LoginErrorMsgBox{
|
||||
msgtype: "session-login",
|
||||
title: "",
|
||||
text: "",
|
||||
|
@ -614,6 +614,15 @@ pub fn main_get_error() -> String {
|
||||
get_error()
|
||||
}
|
||||
|
||||
pub fn main_show_option(_key: String) -> SyncReturn<bool> {
|
||||
#[cfg(all(target_os = "linux", feature = "linux_headless"))]
|
||||
#[cfg(not(any(feature = "flatpak", feature = "appimage")))]
|
||||
if _key.eq(config::CONFIG_OPTION_ALLOW_LINUX_HEADLESS) {
|
||||
return SyncReturn(true)
|
||||
}
|
||||
SyncReturn(false)
|
||||
}
|
||||
|
||||
pub fn main_set_option(key: String, value: String) {
|
||||
if key.eq("custom-rendezvous-server") {
|
||||
set_option(key, value);
|
||||
|
@ -1,5 +1,8 @@
|
||||
use super::{CursorData, ResultType};
|
||||
use desktop::Desktop;
|
||||
#[cfg(all(feature = "linux_headless"))]
|
||||
#[cfg(not(any(feature = "flatpak", feature = "appimage")))]
|
||||
use hbb_common::config::CONFIG_OPTION_ALLOW_LINUX_HEADLESS;
|
||||
pub use hbb_common::platform::linux::*;
|
||||
use hbb_common::{
|
||||
allow_err, bail,
|
||||
@ -69,6 +72,19 @@ pub struct xcb_xfixes_get_cursor_image {
|
||||
pub pixels: *const c_long,
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[cfg(feature = "linux_headless")]
|
||||
#[cfg(not(any(feature = "flatpak", feature = "appimage")))]
|
||||
pub fn is_headless_allowed() -> bool {
|
||||
Config::get_option(CONFIG_OPTION_ALLOW_LINUX_HEADLESS) == "Y"
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_login_screen_wayland() -> bool {
|
||||
let values = get_values_of_seat0_with_gdm_wayland(&[0, 2]);
|
||||
is_gdm_user(&values[1]) && get_display_server_of_session(&values[0]) == DISPLAY_SERVER_WAYLAND
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn sleep_millis(millis: u64) {
|
||||
std::thread::sleep(Duration::from_millis(millis));
|
||||
@ -429,13 +445,21 @@ fn get_cm() -> bool {
|
||||
}
|
||||
|
||||
pub fn is_login_wayland() -> bool {
|
||||
if let Ok(contents) = std::fs::read_to_string("/etc/gdm3/custom.conf") {
|
||||
contents.contains("#WaylandEnable=false") || contents.contains("WaylandEnable=true")
|
||||
} else if let Ok(contents) = std::fs::read_to_string("/etc/gdm/custom.conf") {
|
||||
contents.contains("#WaylandEnable=false") || contents.contains("WaylandEnable=true")
|
||||
} else {
|
||||
false
|
||||
let files = ["/etc/gdm3/custom.conf", "/etc/gdm/custom.conf"];
|
||||
match (
|
||||
Regex::new(r"# *WaylandEnable *= *false"),
|
||||
Regex::new(r"WaylandEnable *= *true"),
|
||||
) {
|
||||
(Ok(pat1), Ok(pat2)) => {
|
||||
for file in files {
|
||||
if let Ok(contents) = std::fs::read_to_string(file) {
|
||||
return pat1.is_match(&contents) || pat2.is_match(&contents);
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@ -109,6 +109,10 @@ pub fn try_start_desktop(_username: &str, _passsword: &str) -> String {
|
||||
// No need to verify password here.
|
||||
return "".to_owned();
|
||||
}
|
||||
if !username.is_empty() {
|
||||
// Another user is logged in. No need to start a new xsession.
|
||||
return "".to_owned();
|
||||
}
|
||||
|
||||
if let Some(msg) = detect_headless() {
|
||||
return msg.to_owned();
|
||||
|
@ -73,6 +73,7 @@ impl RendezvousMediator {
|
||||
allow_err!(super::lan::start_listening());
|
||||
});
|
||||
}
|
||||
// It is ok to run xdesktop manager when the headless function is not allowed.
|
||||
#[cfg(all(target_os = "linux", feature = "linux_headless"))]
|
||||
#[cfg(not(any(feature = "flatpak", feature = "appimage")))]
|
||||
crate::platform::linux_desktop_manager::start_xdesktop();
|
||||
|
@ -191,10 +191,7 @@ pub struct Connection {
|
||||
pressed_modifiers: HashSet<rdev::Key>,
|
||||
#[cfg(all(target_os = "linux", feature = "linux_headless"))]
|
||||
#[cfg(not(any(feature = "flatpak", feature = "appimage")))]
|
||||
rx_cm_stream_ready: mpsc::Receiver<()>,
|
||||
#[cfg(all(target_os = "linux", feature = "linux_headless"))]
|
||||
#[cfg(not(any(feature = "flatpak", feature = "appimage")))]
|
||||
tx_desktop_ready: mpsc::Sender<()>,
|
||||
linux_headless_handle: LinuxHeadlessHandle,
|
||||
closed: bool,
|
||||
delay_response_instant: Instant,
|
||||
}
|
||||
@ -266,6 +263,10 @@ impl Connection {
|
||||
let (tx_cm_stream_ready, _rx_cm_stream_ready) = mpsc::channel(1);
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
let (_tx_desktop_ready, rx_desktop_ready) = mpsc::channel(1);
|
||||
#[cfg(all(target_os = "linux", feature = "linux_headless"))]
|
||||
#[cfg(not(any(feature = "flatpak", feature = "appimage")))]
|
||||
let linux_headless_handle =
|
||||
LinuxHeadlessHandle::new(_rx_cm_stream_ready, _tx_desktop_ready);
|
||||
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
let tx_cloned = tx.clone();
|
||||
@ -322,10 +323,7 @@ impl Connection {
|
||||
pressed_modifiers: Default::default(),
|
||||
#[cfg(all(target_os = "linux", feature = "linux_headless"))]
|
||||
#[cfg(not(any(feature = "flatpak", feature = "appimage")))]
|
||||
rx_cm_stream_ready: _rx_cm_stream_ready,
|
||||
#[cfg(all(target_os = "linux", feature = "linux_headless"))]
|
||||
#[cfg(not(any(feature = "flatpak", feature = "appimage")))]
|
||||
tx_desktop_ready: _tx_desktop_ready,
|
||||
linux_headless_handle,
|
||||
closed: false,
|
||||
delay_response_instant: Instant::now(),
|
||||
};
|
||||
@ -985,8 +983,10 @@ impl Connection {
|
||||
}
|
||||
#[cfg(feature = "linux_headless")]
|
||||
#[cfg(not(any(feature = "flatpak", feature = "appimage")))]
|
||||
if linux_desktop_manager::is_headless() {
|
||||
platform_additions.insert("headless".into(), json!(true));
|
||||
if crate::platform::is_headless_allowed() {
|
||||
if linux_desktop_manager::is_headless() {
|
||||
platform_additions.insert("headless".into(), json!(true));
|
||||
}
|
||||
}
|
||||
if !platform_additions.is_empty() {
|
||||
pi.platform_additions =
|
||||
@ -1009,10 +1009,15 @@ impl Connection {
|
||||
if dtype != crate::platform::linux::DISPLAY_SERVER_X11
|
||||
&& dtype != crate::platform::linux::DISPLAY_SERVER_WAYLAND
|
||||
{
|
||||
res.set_error(format!(
|
||||
"Unsupported display server type \"{}\", x11 or wayland expected",
|
||||
dtype
|
||||
));
|
||||
let msg = if crate::platform::linux::is_login_screen_wayland() {
|
||||
crate::client::LOGIN_SCREEN_WAYLAND.to_owned()
|
||||
} else {
|
||||
format!(
|
||||
"Unsupported display server type \"{}\", x11 or wayland expected",
|
||||
dtype
|
||||
)
|
||||
};
|
||||
res.set_error(msg);
|
||||
let mut msg_out = Message::new();
|
||||
msg_out.set_login_response(res);
|
||||
self.send(msg_out).await;
|
||||
@ -1373,28 +1378,22 @@ impl Connection {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(
|
||||
feature = "flatpak",
|
||||
feature = "appimage",
|
||||
not(all(target_os = "linux", feature = "linux_headless"))
|
||||
))]
|
||||
let err_msg = "".to_owned();
|
||||
#[cfg(all(target_os = "linux", feature = "linux_headless"))]
|
||||
#[cfg(not(any(feature = "flatpak", feature = "appimage")))]
|
||||
let desktop_err = match lr.os_login.as_ref() {
|
||||
Some(os_login) => {
|
||||
linux_desktop_manager::try_start_desktop(&os_login.username, &os_login.password)
|
||||
}
|
||||
None => linux_desktop_manager::try_start_desktop("", ""),
|
||||
};
|
||||
#[cfg(all(target_os = "linux", feature = "linux_headless"))]
|
||||
#[cfg(not(any(feature = "flatpak", feature = "appimage")))]
|
||||
let is_headless = linux_desktop_manager::is_headless();
|
||||
#[cfg(all(target_os = "linux", feature = "linux_headless"))]
|
||||
#[cfg(not(any(feature = "flatpak", feature = "appimage")))]
|
||||
let wait_ipc_timeout = 10_000;
|
||||
let err_msg = self
|
||||
.linux_headless_handle
|
||||
.try_start_desktop(lr.os_login.as_ref());
|
||||
|
||||
// If err is LOGIN_MSG_DESKTOP_SESSION_NOT_READY, just keep this msg and go on checking password.
|
||||
#[cfg(all(target_os = "linux", feature = "linux_headless"))]
|
||||
#[cfg(not(any(feature = "flatpak", feature = "appimage")))]
|
||||
if !desktop_err.is_empty()
|
||||
&& desktop_err != crate::client::LOGIN_MSG_DESKTOP_SESSION_NOT_READY
|
||||
if !err_msg.is_empty() && err_msg != crate::client::LOGIN_MSG_DESKTOP_SESSION_NOT_READY
|
||||
{
|
||||
self.send_login_error(desktop_err).await;
|
||||
self.send_login_error(err_msg).await;
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1422,34 +1421,20 @@ impl Connection {
|
||||
self.send_login_error("Connection not allowed").await;
|
||||
return false;
|
||||
} else if self.is_recent_session() {
|
||||
#[cfg(all(target_os = "linux", feature = "linux_headless"))]
|
||||
#[cfg(not(any(feature = "flatpak", feature = "appimage")))]
|
||||
if desktop_err.is_empty() {
|
||||
#[cfg(target_os = "linux")]
|
||||
if is_headless {
|
||||
self.tx_desktop_ready.send(()).await.ok();
|
||||
let _res = timeout(wait_ipc_timeout, self.rx_cm_stream_ready.recv()).await;
|
||||
}
|
||||
self.try_start_cm(lr.my_id, lr.my_name, true);
|
||||
if err_msg.is_empty() {
|
||||
#[cfg(all(target_os = "linux", feature = "linux_headless"))]
|
||||
#[cfg(not(any(feature = "flatpak", feature = "appimage")))]
|
||||
self.linux_headless_handle.wait_desktop_cm_ready().await;
|
||||
self.try_start_cm(lr.my_id.clone(), lr.my_name.clone(), true);
|
||||
self.send_logon_response().await;
|
||||
if self.port_forward_socket.is_some() {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
self.send_login_error(desktop_err).await;
|
||||
}
|
||||
#[cfg(not(all(target_os = "linux", feature = "linux_headless")))]
|
||||
{
|
||||
self.try_start_cm(lr.my_id, lr.my_name, true);
|
||||
self.send_logon_response().await;
|
||||
if self.port_forward_socket.is_some() {
|
||||
return false;
|
||||
}
|
||||
self.send_login_error(err_msg).await;
|
||||
}
|
||||
} else if lr.password.is_empty() {
|
||||
#[cfg(all(target_os = "linux", feature = "linux_headless"))]
|
||||
#[cfg(not(any(feature = "flatpak", feature = "appimage")))]
|
||||
if desktop_err.is_empty() {
|
||||
if err_msg.is_empty() {
|
||||
self.try_start_cm(lr.my_id, lr.my_name, false);
|
||||
} else {
|
||||
self.send_login_error(
|
||||
@ -1457,8 +1442,6 @@ impl Connection {
|
||||
)
|
||||
.await;
|
||||
}
|
||||
#[cfg(not(all(target_os = "linux", feature = "linux_headless")))]
|
||||
self.try_start_cm(lr.my_id, lr.my_name, false);
|
||||
} else {
|
||||
let mut failure = LOGIN_FAILURES
|
||||
.lock()
|
||||
@ -1497,9 +1480,7 @@ impl Connection {
|
||||
.lock()
|
||||
.unwrap()
|
||||
.insert(self.ip.clone(), failure);
|
||||
#[cfg(all(target_os = "linux", feature = "linux_headless"))]
|
||||
#[cfg(not(any(feature = "flatpak", feature = "appimage")))]
|
||||
if desktop_err.is_empty() {
|
||||
if err_msg.is_empty() {
|
||||
self.send_login_error(crate::client::LOGIN_MSG_PASSWORD_WRONG)
|
||||
.await;
|
||||
self.try_start_cm(lr.my_id, lr.my_name, false);
|
||||
@ -1509,40 +1490,21 @@ impl Connection {
|
||||
)
|
||||
.await;
|
||||
}
|
||||
#[cfg(not(all(target_os = "linux", feature = "linux_headless")))]
|
||||
{
|
||||
self.send_login_error(crate::client::LOGIN_MSG_PASSWORD_WRONG)
|
||||
.await;
|
||||
self.try_start_cm(lr.my_id, lr.my_name, false);
|
||||
}
|
||||
} else {
|
||||
if failure.0 != 0 {
|
||||
LOGIN_FAILURES.lock().unwrap().remove(&self.ip);
|
||||
}
|
||||
#[cfg(all(target_os = "linux", feature = "linux_headless"))]
|
||||
#[cfg(not(any(feature = "flatpak", feature = "appimage")))]
|
||||
if desktop_err.is_empty() {
|
||||
#[cfg(target_os = "linux")]
|
||||
if is_headless {
|
||||
self.tx_desktop_ready.send(()).await.ok();
|
||||
let _res =
|
||||
timeout(wait_ipc_timeout, self.rx_cm_stream_ready.recv()).await;
|
||||
}
|
||||
if err_msg.is_empty() {
|
||||
#[cfg(all(target_os = "linux", feature = "linux_headless"))]
|
||||
#[cfg(not(any(feature = "flatpak", feature = "appimage")))]
|
||||
self.linux_headless_handle.wait_desktop_cm_ready().await;
|
||||
self.send_logon_response().await;
|
||||
self.try_start_cm(lr.my_id, lr.my_name, true);
|
||||
if self.port_forward_socket.is_some() {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
self.send_login_error(desktop_err).await;
|
||||
}
|
||||
#[cfg(not(all(target_os = "linux", feature = "linux_headless")))]
|
||||
{
|
||||
self.send_logon_response().await;
|
||||
self.try_start_cm(lr.my_id, lr.my_name, true);
|
||||
if self.port_forward_socket.is_some() {
|
||||
return false;
|
||||
}
|
||||
self.send_login_error(err_msg).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2361,19 +2323,14 @@ async fn start_ipc(
|
||||
args.push("--hide");
|
||||
};
|
||||
|
||||
#[allow(unused_mut)]
|
||||
#[cfg(target_os = "linux")]
|
||||
#[cfg(not(feature = "linux_headless"))]
|
||||
let user = None;
|
||||
#[cfg(all(target_os = "linux", feature = "linux_headless"))]
|
||||
#[cfg(any(feature = "flatpak", feature = "appimage"))]
|
||||
let user = None;
|
||||
#[cfg(all(target_os = "linux", feature = "linux_headless"))]
|
||||
#[cfg(not(any(feature = "flatpak", feature = "appimage")))]
|
||||
let mut user = None;
|
||||
|
||||
// Cm run as user, wait until desktop session is ready.
|
||||
#[cfg(all(target_os = "linux", feature = "linux_headless"))]
|
||||
#[cfg(not(any(feature = "flatpak", feature = "appimage")))]
|
||||
if linux_desktop_manager::is_headless() {
|
||||
if crate::platform::is_headless_allowed() && linux_desktop_manager::is_headless() {
|
||||
let mut username = linux_desktop_manager::get_username();
|
||||
loop {
|
||||
if !username.is_empty() {
|
||||
@ -2572,6 +2529,52 @@ impl Drop for Connection {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(target_os = "linux", feature = "linux_headless"))]
|
||||
#[cfg(not(any(feature = "flatpak", feature = "appimage")))]
|
||||
struct LinuxHeadlessHandle {
|
||||
pub is_headless_allowed: bool,
|
||||
pub is_headless: bool,
|
||||
pub wait_ipc_timeout: u64,
|
||||
pub rx_cm_stream_ready: mpsc::Receiver<()>,
|
||||
pub tx_desktop_ready: mpsc::Sender<()>,
|
||||
}
|
||||
|
||||
#[cfg(all(target_os = "linux", feature = "linux_headless"))]
|
||||
#[cfg(not(any(feature = "flatpak", feature = "appimage")))]
|
||||
impl LinuxHeadlessHandle {
|
||||
pub fn new(rx_cm_stream_ready: mpsc::Receiver<()>, tx_desktop_ready: mpsc::Sender<()>) -> Self {
|
||||
let is_headless_allowed = crate::platform::is_headless_allowed();
|
||||
let is_headless = is_headless_allowed && linux_desktop_manager::is_headless();
|
||||
Self {
|
||||
is_headless_allowed,
|
||||
is_headless,
|
||||
wait_ipc_timeout: 10_000,
|
||||
rx_cm_stream_ready,
|
||||
tx_desktop_ready,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn try_start_desktop(&mut self, os_login: Option<&OSLogin>) -> String {
|
||||
if self.is_headless_allowed {
|
||||
match os_login {
|
||||
Some(os_login) => {
|
||||
linux_desktop_manager::try_start_desktop(&os_login.username, &os_login.password)
|
||||
}
|
||||
None => linux_desktop_manager::try_start_desktop("", ""),
|
||||
}
|
||||
} else {
|
||||
"".to_string()
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn wait_desktop_cm_ready(&mut self) {
|
||||
if self.is_headless {
|
||||
self.tx_desktop_ready.send(()).await.ok();
|
||||
let _res = timeout(self.wait_ipc_timeout, self.rx_cm_stream_ready.recv()).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mod raii {
|
||||
use super::*;
|
||||
pub struct ConnectionID(i32);
|
||||
|
Loading…
x
Reference in New Issue
Block a user