mirror of
https://github.com/rustdesk/rustdesk.git
synced 2024-11-23 19:49:05 +08:00
linux virtual display, init commit
Signed-off-by: fufesou <shuanglongchen@yeah.net>
This commit is contained in:
parent
ddd0d6eafc
commit
10eddc139c
54
Cargo.lock
generated
54
Cargo.lock
generated
@ -4159,6 +4159,38 @@ version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
|
||||
|
||||
[[package]]
|
||||
name = "pam"
|
||||
version = "0.7.0"
|
||||
source = "git+https://github.com/fufesou/pam#10da2cbbabe32cbc9de22a66abe44738e7ec0ea0"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"pam-macros",
|
||||
"pam-sys",
|
||||
"users 0.10.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pam-macros"
|
||||
version = "0.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c94f3b9b97df3c6d4e51a14916639b24e02c7d15d1dba686ce9b1118277cb811"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.54",
|
||||
"quote 1.0.26",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pam-sys"
|
||||
version = "1.0.0-alpha4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e9dfd42858f6a6bb1081079fd9dc259ca3e2aaece6cb689fd36b1058046c969"
|
||||
dependencies = [
|
||||
"bindgen 0.59.2",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pango"
|
||||
version = "0.16.5"
|
||||
@ -5124,6 +5156,7 @@ dependencies = [
|
||||
"objc",
|
||||
"objc_id",
|
||||
"os-version",
|
||||
"pam",
|
||||
"parity-tokio-ipc",
|
||||
"rdev",
|
||||
"repng",
|
||||
@ -5149,6 +5182,7 @@ dependencies = [
|
||||
"tray-icon",
|
||||
"trayicon",
|
||||
"url",
|
||||
"users 0.11.0",
|
||||
"uuid",
|
||||
"virtual_display",
|
||||
"whoami",
|
||||
@ -6443,6 +6477,26 @@ dependencies = [
|
||||
"serde 1.0.159",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "users"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aa4227e95324a443c9fcb06e03d4d85e91aabe9a5a02aa818688b6918b6af486"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "users"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24cc0f6d6f267b73e5a2cadf007ba8f9bc39c6a6f9666f8cf25ea809a153b032"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "utf8parse"
|
||||
version = "0.2.1"
|
||||
|
@ -122,6 +122,8 @@ mouce = { git="https://github.com/fufesou/mouce.git" }
|
||||
evdev = { git="https://github.com/fufesou/evdev" }
|
||||
dbus = "0.9"
|
||||
dbus-crossroads = "0.5"
|
||||
pam = { git="https://github.com/fufesou/pam" }
|
||||
users = "0.11.0"
|
||||
|
||||
[target.'cfg(target_os = "android")'.dependencies]
|
||||
android_logger = "0.11"
|
||||
|
@ -52,6 +52,11 @@ message FileTransfer {
|
||||
bool show_hidden = 2;
|
||||
}
|
||||
|
||||
message OSLogin {
|
||||
string username = 1;
|
||||
string password = 2;
|
||||
}
|
||||
|
||||
message LoginRequest {
|
||||
string username = 1;
|
||||
bytes password = 2;
|
||||
@ -65,6 +70,7 @@ message LoginRequest {
|
||||
bool video_ack_required = 9;
|
||||
uint64 session_id = 10;
|
||||
string version = 11;
|
||||
OSLogin os_login = 12;
|
||||
}
|
||||
|
||||
message ChatMessage { string text = 1; }
|
||||
|
@ -191,7 +191,7 @@ pub fn run_cmds(cmds: &str) -> ResultType<String> {
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "flatpak"))]
|
||||
fn run_loginctl(args: Option<Vec<&str>>) -> std::io::Result<std::process::Output> {
|
||||
pub(super) fn run_loginctl(args: Option<Vec<&str>>) -> std::io::Result<std::process::Output> {
|
||||
let mut cmd = std::process::Command::new("loginctl");
|
||||
if let Some(a) = args {
|
||||
return cmd.args(a).output();
|
||||
@ -200,7 +200,7 @@ fn run_loginctl(args: Option<Vec<&str>>) -> std::io::Result<std::process::Output
|
||||
}
|
||||
|
||||
#[cfg(feature = "flatpak")]
|
||||
fn run_loginctl(args: Option<Vec<&str>>) -> std::io::Result<std::process::Output> {
|
||||
pub(super) fn run_loginctl(args: Option<Vec<&str>>) -> std::io::Result<std::process::Output> {
|
||||
let mut l_args = String::from("loginctl");
|
||||
if let Some(a) = args {
|
||||
l_args = format!("{} {}", l_args, a.join(" "));
|
||||
|
5
res/pam.d/rustdesk.debian
Normal file
5
res/pam.d/rustdesk.debian
Normal file
@ -0,0 +1,5 @@
|
||||
#%PAM-1.0
|
||||
@include common-auth
|
||||
@include common-account
|
||||
@include common-session
|
||||
@include common-password
|
5
res/pam.d/rustdesk.suse
Normal file
5
res/pam.d/rustdesk.suse
Normal file
@ -0,0 +1,5 @@
|
||||
#%PAM-1.0
|
||||
auth include common-auth
|
||||
account include common-account
|
||||
session include common-session
|
||||
password include common-password
|
130
res/startwm.sh
Executable file
130
res/startwm.sh
Executable file
@ -0,0 +1,130 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# This script is derived from https://github.com/neutrinolabs/xrdp/sesman/startwm.sh.
|
||||
|
||||
#
|
||||
# This script is an example. You might need to edit this script
|
||||
# depending on your distro if it doesn't work for you.
|
||||
#
|
||||
# Uncomment the following line for debug:
|
||||
# exec xterm
|
||||
|
||||
|
||||
# Execution sequence for interactive login shell - pseudocode
|
||||
#
|
||||
# IF /etc/profile is readable THEN
|
||||
# execute ~/.bash_profile
|
||||
# END IF
|
||||
# IF ~/.bash_profile is readable THEN
|
||||
# execute ~/.bash_profile
|
||||
# ELSE
|
||||
# IF ~/.bash_login is readable THEN
|
||||
# execute ~/.bash_login
|
||||
# ELSE
|
||||
# IF ~/.profile is readable THEN
|
||||
# execute ~/.profile
|
||||
# END IF
|
||||
# END IF
|
||||
# END IF
|
||||
pre_start()
|
||||
{
|
||||
if [ -r /etc/profile ]; then
|
||||
. /etc/profile
|
||||
fi
|
||||
if [ -r ~/.bash_profile ]; then
|
||||
. ~/.bash_profile
|
||||
else
|
||||
if [ -r ~/.bash_login ]; then
|
||||
. ~/.bash_login
|
||||
else
|
||||
if [ -r ~/.profile ]; then
|
||||
. ~/.profile
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
# When loging out from the interactive shell, the execution sequence is:
|
||||
#
|
||||
# IF ~/.bash_logout exists THEN
|
||||
# execute ~/.bash_logout
|
||||
# END IF
|
||||
post_start()
|
||||
{
|
||||
if [ -r ~/.bash_logout ]; then
|
||||
. ~/.bash_logout
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
#start the window manager
|
||||
wm_start()
|
||||
{
|
||||
if [ -r /etc/default/locale ]; then
|
||||
. /etc/default/locale
|
||||
export LANG LANGUAGE
|
||||
fi
|
||||
|
||||
# debian
|
||||
if [ -r /etc/X11/Xsession ]; then
|
||||
pre_start
|
||||
. /etc/X11/Xsession
|
||||
post_start
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# alpine
|
||||
# Don't use /etc/X11/xinit/Xsession - it doesn't work
|
||||
if [ -f /etc/alpine-release ]; then
|
||||
if [ -f /etc/X11/xinit/xinitrc ]; then
|
||||
pre_start
|
||||
/etc/X11/xinit/xinitrc
|
||||
post_start
|
||||
else
|
||||
echo "** xinit package isn't installed" >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# el
|
||||
if [ -r /etc/X11/xinit/Xsession ]; then
|
||||
pre_start
|
||||
. /etc/X11/xinit/Xsession
|
||||
post_start
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# suse
|
||||
if [ -r /etc/X11/xdm/Xsession ]; then
|
||||
# since the following script run a user login shell,
|
||||
# do not execute the pseudo login shell scripts
|
||||
. /etc/X11/xdm/Xsession
|
||||
exit 0
|
||||
elif [ -r /usr/etc/X11/xdm/Xsession ]; then
|
||||
. /usr/etc/X11/xdm/Xsession
|
||||
exit 0
|
||||
fi
|
||||
|
||||
pre_start
|
||||
xterm
|
||||
post_start
|
||||
}
|
||||
|
||||
#. /etc/environment
|
||||
#export PATH=$PATH
|
||||
#export LANG=$LANG
|
||||
|
||||
# change PATH to be what your environment needs usually what is in
|
||||
# /etc/environment
|
||||
#PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games"
|
||||
#export PATH=$PATH
|
||||
|
||||
# for PATH and LANG from /etc/environment
|
||||
# pam will auto process the environment file if /etc/pam.d/xrdp-sesman
|
||||
# includes
|
||||
# auth required pam_env.so readenv=1
|
||||
|
||||
wm_start
|
||||
|
||||
exit 1
|
30
res/xorg.conf
Normal file
30
res/xorg.conf
Normal file
@ -0,0 +1,30 @@
|
||||
Section "Monitor"
|
||||
Identifier "Dummy Monitor"
|
||||
|
||||
# Default HorizSync 31.50 - 48.00 kHz
|
||||
HorizSync 5.0 - 150.0
|
||||
# Default VertRefresh 50.00 - 70.00 Hz
|
||||
VertRefresh 5.0 - 100.0
|
||||
|
||||
# Taken from https://www.xpra.org/xorg.conf
|
||||
Modeline "1920x1080" 23.53 1920 1952 2040 2072 1080 1106 1108 1135
|
||||
Modeline "1280x720" 27.41 1280 1312 1416 1448 720 737 740 757
|
||||
EndSection
|
||||
|
||||
Section "Device"
|
||||
Identifier "Dummy VideoCard"
|
||||
Driver "dummy"
|
||||
# Default VideoRam 4096
|
||||
# (1920 * 1080 * 4) / 1024 = 8100
|
||||
VideoRam 8100
|
||||
EndSection
|
||||
|
||||
Section "Screen"
|
||||
Identifier "Dummy Screen"
|
||||
Device "Dummy VideoCard"
|
||||
Monitor "Dummy Monitor"
|
||||
SubSectionSub "Display"
|
||||
Depth 24
|
||||
Modes "1920x1080" "1280x720"
|
||||
EndSubSection
|
||||
EndSection
|
@ -85,8 +85,8 @@ impl Interface for Session {
|
||||
handle_hash(self.lc.clone(), &pass, hash, self, peer).await;
|
||||
}
|
||||
|
||||
async fn handle_login_from_ui(&mut self, password: String, remember: bool, peer: &mut Stream) {
|
||||
handle_login_from_ui(self.lc.clone(), password, remember, peer).await;
|
||||
async fn handle_login_from_ui(&mut self, os_username: String, os_password: String, password: String, remember: bool, peer: &mut Stream) {
|
||||
handle_login_from_ui(self.lc.clone(), os_username, os_password, password, remember, peer).await;
|
||||
}
|
||||
|
||||
async fn handle_test_delay(&mut self, t: TestDelay, peer: &mut Stream) {
|
||||
|
124
src/client.rs
124
src/client.rs
@ -1591,7 +1591,12 @@ impl LoginConfigHandler {
|
||||
}
|
||||
|
||||
/// Create a [`Message`] for login.
|
||||
fn create_login_msg(&self, password: Vec<u8>) -> Message {
|
||||
fn create_login_msg(
|
||||
&self,
|
||||
os_username: String,
|
||||
os_password: String,
|
||||
password: Vec<u8>,
|
||||
) -> Message {
|
||||
#[cfg(any(target_os = "android", target_os = "ios"))]
|
||||
let my_id = Config::get_id_or(crate::common::DEVICE_ID.lock().unwrap().clone());
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
@ -1604,6 +1609,12 @@ impl LoginConfigHandler {
|
||||
option: self.get_option_message(true).into(),
|
||||
session_id: self.session_id,
|
||||
version: crate::VERSION.to_string(),
|
||||
os_login: Some(OSLogin {
|
||||
username: os_username,
|
||||
password: os_password,
|
||||
..Default::default()
|
||||
})
|
||||
.into(),
|
||||
..Default::default()
|
||||
};
|
||||
match self.conn_type {
|
||||
@ -1908,10 +1919,46 @@ pub fn handle_login_error(
|
||||
err: &str,
|
||||
interface: &impl Interface,
|
||||
) -> bool {
|
||||
if err == "Wrong Password" {
|
||||
if err == crate::server::LOGIN_MSG_PASSWORD_EMPTY {
|
||||
lc.write().unwrap().password = Default::default();
|
||||
interface.msgbox("input-password", "Password Required", "", "");
|
||||
true
|
||||
} else if err == crate::server::LOGIN_MSG_PASSWORD_WRONG {
|
||||
lc.write().unwrap().password = Default::default();
|
||||
interface.msgbox("re-input-password", err, "Do you want to enter again?", "");
|
||||
true
|
||||
} else if err == crate::server::LOGIN_MSG_XSESSION_NOT_READY {
|
||||
interface.msgbox(
|
||||
"xsession-login",
|
||||
"xsession is unready",
|
||||
"Input linux user/password",
|
||||
"",
|
||||
);
|
||||
true
|
||||
} else if err == crate::server::LOGIN_MSG_XSESSION_FAILED {
|
||||
interface.msgbox(
|
||||
"xsession-re-login",
|
||||
"xsession username/password is wrong",
|
||||
"Do you want to enter again?",
|
||||
"",
|
||||
);
|
||||
true
|
||||
} else if err == crate::server::LOGIN_MSG_XSESSION_NOT_READY_PASSWORD_EMPTY {
|
||||
interface.msgbox(
|
||||
"xsession-login-password",
|
||||
"xsession is unready",
|
||||
"Input connection password and linux user/password",
|
||||
"",
|
||||
);
|
||||
true
|
||||
} else if err == crate::server::LOGIN_MSG_XSESSION_NOT_READY_PASSWORD_WRONG {
|
||||
interface.msgbox(
|
||||
"xsession-login-re-password",
|
||||
"xsession is unready and password is wrong",
|
||||
"Do you want to enter again?",
|
||||
"",
|
||||
);
|
||||
true
|
||||
} else if err == "No Password Access" {
|
||||
lc.write().unwrap().password = Default::default();
|
||||
interface.msgbox(
|
||||
@ -1944,7 +1991,7 @@ pub async fn handle_hash(
|
||||
lc: Arc<RwLock<LoginConfigHandler>>,
|
||||
password_preset: &str,
|
||||
hash: Hash,
|
||||
interface: &impl Interface,
|
||||
_interface: &impl Interface,
|
||||
peer: &mut Stream,
|
||||
) {
|
||||
lc.write().unwrap().hash = hash.clone();
|
||||
@ -1970,13 +2017,19 @@ pub async fn handle_hash(
|
||||
}
|
||||
if password.is_empty() {
|
||||
// login without password, the remote side can click accept
|
||||
send_login(lc.clone(), Vec::new(), peer).await;
|
||||
interface.msgbox("input-password", "Password Required", "", "");
|
||||
send_login(lc.clone(), "".to_owned(), "".to_owned(), Vec::new(), peer).await;
|
||||
} else {
|
||||
let mut hasher = Sha256::new();
|
||||
hasher.update(&password);
|
||||
hasher.update(&hash.challenge);
|
||||
send_login(lc.clone(), hasher.finalize()[..].into(), peer).await;
|
||||
send_login(
|
||||
lc.clone(),
|
||||
"".to_owned(),
|
||||
"".to_owned(),
|
||||
hasher.finalize()[..].into(),
|
||||
peer,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
lc.write().unwrap().hash = hash;
|
||||
}
|
||||
@ -1986,10 +2039,21 @@ pub async fn handle_hash(
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `lc` - Login config.
|
||||
/// * `os_username` - OS username.
|
||||
/// * `os_password` - OS password.
|
||||
/// * `password` - Password.
|
||||
/// * `peer` - [`Stream`] for communicating with peer.
|
||||
async fn send_login(lc: Arc<RwLock<LoginConfigHandler>>, password: Vec<u8>, peer: &mut Stream) {
|
||||
let msg_out = lc.read().unwrap().create_login_msg(password);
|
||||
async fn send_login(
|
||||
lc: Arc<RwLock<LoginConfigHandler>>,
|
||||
os_username: String,
|
||||
os_password: String,
|
||||
password: Vec<u8>,
|
||||
peer: &mut Stream,
|
||||
) {
|
||||
let msg_out = lc
|
||||
.read()
|
||||
.unwrap()
|
||||
.create_login_msg(os_username, os_password, password);
|
||||
allow_err!(peer.send(&msg_out).await);
|
||||
}
|
||||
|
||||
@ -1998,25 +2062,40 @@ async fn send_login(lc: Arc<RwLock<LoginConfigHandler>>, password: Vec<u8>, peer
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `lc` - Login config.
|
||||
/// * `os_username` - OS username.
|
||||
/// * `os_password` - OS password.
|
||||
/// * `password` - Password.
|
||||
/// * `remember` - Whether to remember password.
|
||||
/// * `peer` - [`Stream`] for communicating with peer.
|
||||
pub async fn handle_login_from_ui(
|
||||
lc: Arc<RwLock<LoginConfigHandler>>,
|
||||
os_username: String,
|
||||
os_password: String,
|
||||
password: String,
|
||||
remember: bool,
|
||||
peer: &mut Stream,
|
||||
) {
|
||||
let mut hasher = Sha256::new();
|
||||
hasher.update(password);
|
||||
hasher.update(&lc.read().unwrap().hash.salt);
|
||||
let res = hasher.finalize();
|
||||
lc.write().unwrap().remember = remember;
|
||||
lc.write().unwrap().password = res[..].into();
|
||||
let mut hash_password = if password.is_empty() {
|
||||
let mut password2 = lc.read().unwrap().password.clone();
|
||||
if password2.is_empty() {
|
||||
password2 = lc.read().unwrap().config.password.clone();
|
||||
}
|
||||
password2
|
||||
} else {
|
||||
let mut hasher = Sha256::new();
|
||||
hasher.update(password);
|
||||
hasher.update(&lc.read().unwrap().hash.salt);
|
||||
let res = hasher.finalize();
|
||||
lc.write().unwrap().remember = remember;
|
||||
lc.write().unwrap().password = res[..].into();
|
||||
res[..].into()
|
||||
};
|
||||
let mut hasher2 = Sha256::new();
|
||||
hasher2.update(&res[..]);
|
||||
hasher2.update(&hash_password[..]);
|
||||
hasher2.update(&lc.read().unwrap().hash.challenge);
|
||||
send_login(lc.clone(), hasher2.finalize()[..].into(), peer).await;
|
||||
hash_password = hasher2.finalize()[..].to_vec();
|
||||
|
||||
send_login(lc.clone(), os_username, os_password, hash_password, peer).await;
|
||||
}
|
||||
|
||||
async fn send_switch_login_request(
|
||||
@ -2030,7 +2109,7 @@ async fn send_switch_login_request(
|
||||
lr: hbb_common::protobuf::MessageField::some(
|
||||
lc.read()
|
||||
.unwrap()
|
||||
.create_login_msg(vec![])
|
||||
.create_login_msg("".to_owned(), "".to_owned(), vec![])
|
||||
.login_request()
|
||||
.to_owned(),
|
||||
),
|
||||
@ -2051,7 +2130,14 @@ pub trait Interface: Send + Clone + 'static + Sized {
|
||||
self.msgbox("error", "Error", err, "");
|
||||
}
|
||||
async fn handle_hash(&mut self, pass: &str, hash: Hash, peer: &mut Stream);
|
||||
async fn handle_login_from_ui(&mut self, password: String, remember: bool, peer: &mut Stream);
|
||||
async fn handle_login_from_ui(
|
||||
&mut self,
|
||||
os_username: String,
|
||||
os_password: String,
|
||||
password: String,
|
||||
remember: bool,
|
||||
peer: &mut Stream,
|
||||
);
|
||||
async fn handle_test_delay(&mut self, t: TestDelay, peer: &mut Stream);
|
||||
|
||||
fn get_login_config_handler(&self) -> Arc<RwLock<LoginConfigHandler>>;
|
||||
@ -2071,7 +2157,7 @@ pub trait Interface: Send + Clone + 'static + Sized {
|
||||
#[derive(Clone)]
|
||||
pub enum Data {
|
||||
Close,
|
||||
Login((String, bool)),
|
||||
Login((String, String, String, bool)),
|
||||
Message(Message),
|
||||
SendFiles((i32, String, String, i32, bool, bool)),
|
||||
RemoveDirAll((i32, String, bool, bool)),
|
||||
|
@ -360,9 +360,9 @@ impl<T: InvokeUiSession> Remote<T> {
|
||||
allow_err!(peer.send(&msg).await);
|
||||
return false;
|
||||
}
|
||||
Data::Login((password, remember)) => {
|
||||
Data::Login((os_username, os_password, password, remember)) => {
|
||||
self.handler
|
||||
.handle_login_from_ui(password, remember, peer)
|
||||
.handle_login_from_ui(os_username, os_password, password, remember, peer)
|
||||
.await;
|
||||
}
|
||||
Data::ToggleClipboardFile => {
|
||||
|
@ -1,3 +1,4 @@
|
||||
use super::linux_desktop::GNOME_SESSION_BINARY;
|
||||
use super::{CursorData, ResultType};
|
||||
use desktop::Desktop;
|
||||
pub use hbb_common::platform::linux::*;
|
||||
|
951
src/platform/linux_desktop.rs
Normal file
951
src/platform/linux_desktop.rs
Normal file
@ -0,0 +1,951 @@
|
||||
use super::{linux::*, ResultType};
|
||||
use hbb_common::{allow_err, bail, log, rand::prelude::*, tokio::time};
|
||||
use pam;
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
os::unix::process::CommandExt,
|
||||
path::Path,
|
||||
process::{Child, Command},
|
||||
sync::{
|
||||
atomic::{AtomicBool, Ordering},
|
||||
mpsc::{sync_channel, SyncSender},
|
||||
Arc, Mutex,
|
||||
},
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
use users::{get_user_by_name, os::unix::UserExt, User};
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
static ref DESKTOP_RUNNING: Arc<AtomicBool> = Arc::new(AtomicBool::new(false));
|
||||
static ref DESKTOP_INST: Arc<Mutex<Option<Desktop>>> = Arc::new(Mutex::new(None));
|
||||
}
|
||||
|
||||
pub const VIRTUAL_X11_DESKTOP: &str = "xfce4";
|
||||
pub const VIRTUAL_X11_DESKTOP_START: &str = "startxfce4";
|
||||
pub const XFCE4_PANEL: &str = "xfce4-panel";
|
||||
pub const GNOME_SESSION_BINARY: &str = "gnome-session-binary";
|
||||
pub const ENV_DESKTOP_PROTOCAL: &str = "RUSTDESK_PROTOCAL";
|
||||
pub const ENV_DESKTOP_PROTOCAL_WAYLAND: &str = "wayland";
|
||||
pub const ENV_DESKTOP_PROTOCAL__X11: &str = "x11";
|
||||
pub const ENV_DESKTOP_PROTOCAL_UNKNOWN: &str = "unknown";
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Debug)]
|
||||
pub enum Protocal {
|
||||
Wayland,
|
||||
X11, // Xorg
|
||||
Unknown,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DesktopEnv {
|
||||
pub protocal: Protocal,
|
||||
pub username: String,
|
||||
pub uid: String,
|
||||
pub display: String,
|
||||
pub xauth: String,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Desktop {
|
||||
env: DesktopEnv,
|
||||
child_exit: Arc<AtomicBool>,
|
||||
is_child_running: Arc<AtomicBool>,
|
||||
}
|
||||
|
||||
fn check_update_env() {
|
||||
let mut inst = DESKTOP_INST.lock().unwrap();
|
||||
if let Some(inst) = &mut (*inst) {
|
||||
if !inst.is_child_running.load(Ordering::SeqCst) {
|
||||
inst.child_exit.store(true, Ordering::SeqCst);
|
||||
let old_env = inst.env.clone();
|
||||
allow_err!(inst.env.refresh());
|
||||
if !inst.env.is_same_env(&old_env) {
|
||||
inst.env.update_env();
|
||||
log::debug!("desktop env changed, {:?}", &inst.env);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn start_xdesktop() {
|
||||
std::thread::spawn(|| {
|
||||
if wait_xdesktop(20) {
|
||||
log::info!("Wait desktop: default");
|
||||
} else {
|
||||
log::info!("Wait desktop: none");
|
||||
}
|
||||
*DESKTOP_INST.lock().unwrap() = Some(Desktop::new());
|
||||
|
||||
let interval = time::Duration::from_millis(super::SERVICE_INTERVAL);
|
||||
DESKTOP_RUNNING.store(true, Ordering::SeqCst);
|
||||
while DESKTOP_RUNNING.load(Ordering::SeqCst) {
|
||||
check_update_env();
|
||||
std::thread::sleep(interval);
|
||||
}
|
||||
log::info!("xdesktop update thread exit");
|
||||
});
|
||||
}
|
||||
|
||||
pub fn stop_xdesktop() {
|
||||
DESKTOP_RUNNING.store(false, Ordering::SeqCst);
|
||||
}
|
||||
|
||||
pub fn get_desktop_env() -> Option<DesktopEnv> {
|
||||
match &*DESKTOP_INST.lock().unwrap() {
|
||||
Some(inst) => Some(inst.env.clone()),
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn try_start_x_session(username: &str, password: &str) -> ResultType<DesktopEnv> {
|
||||
let mut inst = DESKTOP_INST.lock().unwrap();
|
||||
if let Some(inst) = &mut (*inst) {
|
||||
let _ = inst.try_start_x_session(username, password)?;
|
||||
log::debug!("try_start_x_session, username: {}, {:?}", &username, &inst);
|
||||
Ok(inst.env.clone())
|
||||
} else {
|
||||
bail!(crate::server::LOGIN_MSG_XDESKTOP_NOT_INITED);
|
||||
}
|
||||
}
|
||||
|
||||
fn wait_xdesktop(timeout_secs: u64) -> bool {
|
||||
let wait_begin = Instant::now();
|
||||
while wait_begin.elapsed().as_secs() < timeout_secs {
|
||||
let seat0 = get_values_of_seat0(&[0]);
|
||||
if !seat0[0].is_empty() {
|
||||
return true;
|
||||
}
|
||||
|
||||
if let Ok(output) = run_cmds(format!(
|
||||
"ps -ef | grep -v 'grep' | grep -E 'gnome-session-binary|{}'",
|
||||
XFCE4_PANEL
|
||||
)) {
|
||||
if !output.is_empty() {
|
||||
log::info!("wait xdesktop: find xclient {}", &output);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
std::thread::sleep(Duration::from_millis(super::SERVICE_INTERVAL));
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
impl DesktopEnv {
|
||||
pub fn new() -> Self {
|
||||
let xauth = get_env_var("XAUTHORITY");
|
||||
|
||||
Self {
|
||||
protocal: Protocal::Unknown,
|
||||
username: "".to_owned(),
|
||||
uid: "".to_owned(),
|
||||
display: "".to_owned(),
|
||||
xauth: if xauth.is_empty() {
|
||||
"/tmp/.Xauthority".to_owned()
|
||||
} else {
|
||||
xauth
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn update_env(&self) {
|
||||
if self.is_ready() {
|
||||
std::env::set_var("DISPLAY", &self.display);
|
||||
std::env::set_var("XAUTHORITY", &self.xauth);
|
||||
std::env::set_var(ENV_DESKTOP_PROTOCAL, &self.protocal.to_string());
|
||||
} else {
|
||||
std::env::set_var("DISPLAY", "");
|
||||
std::env::set_var("XAUTHORITY", "");
|
||||
std::env::set_var(ENV_DESKTOP_PROTOCAL, &Protocal::Unknown.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_same_env(&self, other: &Self) -> bool {
|
||||
self.protocal == other.protocal
|
||||
&& self.uid == other.uid
|
||||
&& self.display == other.display
|
||||
&& self.xauth == other.xauth
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn is_ready(&self) -> bool {
|
||||
self.protocal == Protocal::X11
|
||||
}
|
||||
|
||||
// The logic mainly fron https://github.com/neutrinolabs/xrdp/blob/34fe9b60ebaea59e8814bbc3ca5383cabaa1b869/sesman/session.c#L334.
|
||||
fn get_avail_display() -> ResultType<u32> {
|
||||
let display_range = 0..51;
|
||||
for i in display_range.clone() {
|
||||
if Self::is_x_server_running(i) {
|
||||
continue;
|
||||
}
|
||||
return Ok(i);
|
||||
}
|
||||
bail!("No avaliable display found in range {:?}", display_range)
|
||||
}
|
||||
|
||||
fn is_x_server_running(display: u32) -> bool {
|
||||
Path::new(&format!("/tmp/.X11-unix/X{}", display)).exists()
|
||||
|| Path::new(&format!("/tmp/.X{}-lock", display)).exists()
|
||||
}
|
||||
|
||||
fn get_display(&mut self) {
|
||||
self.display = get_env_tries("DISPLAY", &self.uid, GNOME_SESSION_BINARY, 10);
|
||||
if self.display.is_empty() {
|
||||
self.display = get_env_tries("DISPLAY", &self.uid, XFCE4_PANEL, 10);
|
||||
}
|
||||
if self.display.is_empty() {
|
||||
self.display = Self::get_display_by_user(&self.username);
|
||||
}
|
||||
if self.display.is_empty() {
|
||||
self.display = ":0".to_owned();
|
||||
}
|
||||
self.display = self
|
||||
.display
|
||||
.replace(&whoami::hostname(), "")
|
||||
.replace("localhost", "");
|
||||
}
|
||||
|
||||
fn get_xauth(&mut self) {
|
||||
self.xauth = get_env_tries("XAUTHORITY", &self.uid, GNOME_SESSION_BINARY, 10);
|
||||
if self.xauth.is_empty() {
|
||||
get_env_tries("XAUTHORITY", &self.uid, XFCE4_PANEL, 10);
|
||||
}
|
||||
|
||||
let gdm = format!("/run/user/{}/gdm/Xauthority", self.uid);
|
||||
if self.xauth.is_empty() {
|
||||
self.xauth = if std::path::Path::new(&gdm).exists() {
|
||||
gdm
|
||||
} else {
|
||||
let username = &self.username;
|
||||
if username == "root" {
|
||||
format!("/{}/.Xauthority", username)
|
||||
} else {
|
||||
let tmp = format!("/home/{}/.Xauthority", username);
|
||||
if std::path::Path::new(&tmp).exists() {
|
||||
tmp
|
||||
} else {
|
||||
format!("/var/lib/{}/.Xauthority", username)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// fixme: reduce loginctl
|
||||
fn get_env_seat0(&mut self) -> ResultType<bool> {
|
||||
let output = Command::new("loginctl").output()?;
|
||||
for line in String::from_utf8_lossy(&output.stdout).lines() {
|
||||
if line.contains("seat0") {
|
||||
if let Some(sid) = line.split_whitespace().nth(0) {
|
||||
if Self::is_active(sid)? {
|
||||
if let Some(uid) = line.split_whitespace().nth(1) {
|
||||
self.uid = uid.to_owned();
|
||||
}
|
||||
if let Some(u) = line.split_whitespace().nth(2) {
|
||||
self.username = u.to_owned();
|
||||
}
|
||||
|
||||
self.protocal = Protocal::Unknown;
|
||||
let type_output = Command::new("loginctl")
|
||||
.args(vec!["show-session", "-p", "Type", sid])
|
||||
.output()?;
|
||||
let type_stdout = String::from_utf8_lossy(&type_output.stdout);
|
||||
|
||||
if type_stdout.contains("x11") {
|
||||
self.protocal = Protocal::X11;
|
||||
break;
|
||||
} else if type_stdout.contains("wayland") {
|
||||
self.protocal = Protocal::Wayland;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(self.is_ready())
|
||||
}
|
||||
|
||||
// some case, there is no seat0 https://github.com/rustdesk/rustdesk/issues/73
|
||||
fn get_env_active(&mut self) -> ResultType<bool> {
|
||||
let output = Command::new("loginctl").output()?;
|
||||
|
||||
// set active Xorg session
|
||||
for line in String::from_utf8_lossy(&output.stdout).lines() {
|
||||
if line.contains("sessions listed.") {
|
||||
continue;
|
||||
}
|
||||
if let Some(sid) = line.split_whitespace().nth(0) {
|
||||
if Self::is_active(sid)? {
|
||||
if Self::get_display_server_of_session(sid) == ENV_DESKTOP_PROTOCAL__X11 {
|
||||
if let Some(uid) = line.split_whitespace().nth(1) {
|
||||
self.uid = uid.to_owned();
|
||||
}
|
||||
if let Some(u) = line.split_whitespace().nth(2) {
|
||||
self.username = u.to_owned();
|
||||
}
|
||||
|
||||
self.protocal = Protocal::X11;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// // set active xfce4 session
|
||||
// for line in String::from_utf8_lossy(&output.stdout).lines() {
|
||||
// if let Some(sid) = line.split_whitespace().nth(0) {
|
||||
// if Self::is_active(sid)? {
|
||||
// let tty_output = Command::new("loginctl")
|
||||
// .args(vec!["show-session", "-p", "TTY", sid])
|
||||
// .output()?;
|
||||
// let tty: String = String::from_utf8_lossy(&tty_output.stdout)
|
||||
// .replace("TTY=", "")
|
||||
// .trim_end()
|
||||
// .into();
|
||||
|
||||
// let xfce_panel_info =
|
||||
// run_cmds(format!("ps -e | grep \"{}.\\\\+{}\"", tty, XFCE4_PANEL))?;
|
||||
// if xfce_panel_info.trim_end().to_string() != "" {
|
||||
// if let Some(uid) = line.split_whitespace().nth(1) {
|
||||
// self.uid = uid.to_owned();
|
||||
// }
|
||||
// if let Some(u) = line.split_whitespace().nth(2) {
|
||||
// self.username = u.to_owned();
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
Ok(self.is_ready())
|
||||
}
|
||||
|
||||
// fixme: dup
|
||||
fn get_display_server_of_session(session: &str) -> String {
|
||||
if let Ok(output) = Command::new("loginctl")
|
||||
.args(vec!["show-session", "-p", "Type", session])
|
||||
.output()
|
||||
// Check session type of the session
|
||||
{
|
||||
let display_server = String::from_utf8_lossy(&output.stdout)
|
||||
.replace("Type=", "")
|
||||
.trim_end()
|
||||
.into();
|
||||
if display_server == "tty" {
|
||||
// If the type is tty...
|
||||
if let Ok(output) = Command::new("loginctl")
|
||||
.args(vec!["show-session", "-p", "TTY", session])
|
||||
.output()
|
||||
// Get the tty number
|
||||
{
|
||||
let tty: String = String::from_utf8_lossy(&output.stdout)
|
||||
.replace("TTY=", "")
|
||||
.trim_end()
|
||||
.into();
|
||||
if let Ok(xorg_results) =
|
||||
run_cmds(format!("ps -e | grep \"{}.\\\\+Xorg\"", tty))
|
||||
// And check if Xorg is running on that tty
|
||||
{
|
||||
if xorg_results.trim_end().to_string() != "" {
|
||||
// If it is, manually return "x11", otherwise return tty
|
||||
ENV_DESKTOP_PROTOCAL__X11.to_owned()
|
||||
} else {
|
||||
display_server
|
||||
}
|
||||
} else {
|
||||
// If any of these commands fail just fall back to the display server
|
||||
display_server
|
||||
}
|
||||
} else {
|
||||
display_server
|
||||
}
|
||||
} else {
|
||||
// If the session is not a tty, then just return the type as usual
|
||||
display_server
|
||||
}
|
||||
} else {
|
||||
"".to_owned()
|
||||
}
|
||||
}
|
||||
|
||||
// fixme: remove
|
||||
fn is_active(sid: &str) -> ResultType<bool> {
|
||||
let output = Command::new("loginctl")
|
||||
.args(vec!["show-session", "-p", "State", sid])
|
||||
.output()?;
|
||||
|
||||
Ok(String::from_utf8_lossy(&output.stdout).contains("active"))
|
||||
}
|
||||
|
||||
fn get_display_by_user(user: &str) -> String {
|
||||
// log::debug!("w {}", &user);
|
||||
if let Ok(output) = std::process::Command::new("w").arg(&user).output() {
|
||||
for line in String::from_utf8_lossy(&output.stdout).lines() {
|
||||
let mut iter = line.split_whitespace();
|
||||
let b = iter.nth(2);
|
||||
if let Some(b) = b {
|
||||
if b.starts_with(":") {
|
||||
return b.to_owned();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// above not work for gdm user
|
||||
//log::debug!("ls -l /tmp/.X11-unix/");
|
||||
let mut last = "".to_owned();
|
||||
if let Ok(output) = std::process::Command::new("ls")
|
||||
.args(vec!["-l", "/tmp/.X11-unix/"])
|
||||
.output()
|
||||
{
|
||||
for line in String::from_utf8_lossy(&output.stdout).lines() {
|
||||
let mut iter = line.split_whitespace();
|
||||
let user_field = iter.nth(2);
|
||||
if let Some(x) = iter.last() {
|
||||
if x.starts_with("X") {
|
||||
last = x.replace("X", ":").to_owned();
|
||||
if user_field == Some(&user) {
|
||||
return last;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
last
|
||||
}
|
||||
|
||||
fn add_xauth_cookie(
|
||||
file: &str,
|
||||
display: &str,
|
||||
uid: u32,
|
||||
gid: u32,
|
||||
envs: &HashMap<&str, String>,
|
||||
) -> ResultType<()> {
|
||||
let randstr = (0..16)
|
||||
.map(|_| format!("{:02x}", random::<u8>()))
|
||||
.collect::<String>();
|
||||
let output = Command::new("xauth")
|
||||
.uid(uid)
|
||||
.gid(gid)
|
||||
.envs(envs)
|
||||
.args(vec!["-q", "-f", file, "add", display, ".", &randstr])
|
||||
.output()?;
|
||||
// xauth run success, even the following error occurs.
|
||||
// Ok(Output { status: ExitStatus(unix_wait_status(0)), stdout: "", stderr: "xauth: file .Xauthority does not exist\n" })
|
||||
let errmsg = String::from_utf8_lossy(&output.stderr).to_string();
|
||||
if !errmsg.is_empty() {
|
||||
if !errmsg.contains("does not exist") {
|
||||
bail!("Failed to launch xauth, {}", errmsg)
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn wait_x_server_running(pid: u32, display_num: u32, max_wait_secs: u64) -> ResultType<()> {
|
||||
let wait_begin = Instant::now();
|
||||
loop {
|
||||
if run_cmds(format!("ls /proc/{}", pid))?.is_empty() {
|
||||
bail!("X server exit");
|
||||
}
|
||||
|
||||
if Self::is_x_server_running(display_num) {
|
||||
return Ok(());
|
||||
}
|
||||
if wait_begin.elapsed().as_secs() > max_wait_secs {
|
||||
bail!("Failed to wait xserver after {} seconds", max_wait_secs);
|
||||
}
|
||||
std::thread::sleep(Duration::from_millis(300));
|
||||
}
|
||||
}
|
||||
|
||||
fn refresh(&mut self) -> ResultType<bool> {
|
||||
*self = Self::new();
|
||||
if self.get_env_seat0()? || self.get_env_active()? {
|
||||
self.get_display();
|
||||
self.get_xauth();
|
||||
Ok(true)
|
||||
} else {
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Desktop {
|
||||
fn drop(&mut self) {
|
||||
self.stop_children();
|
||||
}
|
||||
}
|
||||
|
||||
impl Desktop {
|
||||
fn fatal_exit() {
|
||||
std::process::exit(0);
|
||||
}
|
||||
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
env: DesktopEnv::new(),
|
||||
child_exit: Arc::new(AtomicBool::new(true)),
|
||||
is_child_running: Arc::new(AtomicBool::new(false)),
|
||||
}
|
||||
}
|
||||
|
||||
fn try_start_x_session(&mut self, username: &str, password: &str) -> ResultType<()> {
|
||||
match get_user_by_name(username) {
|
||||
Some(userinfo) => {
|
||||
let mut client = pam::Client::with_password(pam_get_service_name())?;
|
||||
client
|
||||
.conversation_mut()
|
||||
.set_credentials(username, password);
|
||||
match client.authenticate() {
|
||||
Ok(_) => {
|
||||
if self.env.is_ready() && self.env.username == username {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
self.env.username = username.to_string();
|
||||
self.env.uid = userinfo.uid().to_string();
|
||||
self.env.protocal = Protocal::Unknown;
|
||||
match self.start_x_session(&userinfo, password) {
|
||||
Ok(_) => {
|
||||
log::info!("Succeeded to start x11, update env {:?}", &self.env);
|
||||
self.env.update_env();
|
||||
Ok(())
|
||||
}
|
||||
Err(e) => {
|
||||
self.env = DesktopEnv::new();
|
||||
self.env.update_env();
|
||||
bail!("failed to start x session, {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
bail!("failed to check user pass for {}, {}", username, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
None => {
|
||||
bail!("failed to get userinfo of {}", username);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn start_x_session(&mut self, userinfo: &User, password: &str) -> ResultType<()> {
|
||||
self.stop_children();
|
||||
|
||||
let display_num = DesktopEnv::get_avail_display()?;
|
||||
// "xServer_ip:display_num.screen_num"
|
||||
self.env.display = format!(":{}", display_num);
|
||||
|
||||
let uid = userinfo.uid();
|
||||
let gid = userinfo.primary_group_id();
|
||||
let envs = HashMap::from([
|
||||
("SHELL", userinfo.shell().to_string_lossy().to_string()),
|
||||
("PATH", "/sbin:/bin:/usr/bin:/usr/local/bin".to_owned()),
|
||||
("USER", self.env.username.clone()),
|
||||
("UID", userinfo.uid().to_string()),
|
||||
("HOME", userinfo.home_dir().to_string_lossy().to_string()),
|
||||
(
|
||||
"XDG_RUNTIME_DIR",
|
||||
format!("/run/user/{}", userinfo.uid().to_string()),
|
||||
),
|
||||
// ("DISPLAY", self.display.clone()),
|
||||
// ("XAUTHORITY", self.xauth.clone()),
|
||||
// (ENV_DESKTOP_PROTOCAL, XProtocal::X11.to_string()),
|
||||
]);
|
||||
let env = self.env.clone();
|
||||
self.child_exit.store(false, Ordering::SeqCst);
|
||||
let is_child_running = self.is_child_running.clone();
|
||||
|
||||
let (tx_res, rx_res) = sync_channel(1);
|
||||
let password = password.to_string();
|
||||
// start x11
|
||||
std::thread::spawn(move || {
|
||||
match Self::start_x_session_thread(
|
||||
tx_res.clone(),
|
||||
is_child_running,
|
||||
env,
|
||||
uid,
|
||||
gid,
|
||||
display_num,
|
||||
password,
|
||||
envs,
|
||||
) {
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
log::error!("Failed to start x session thread");
|
||||
allow_err!(tx_res.send(format!("Failed to start x session thread, {}", e)));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// wait x11
|
||||
match rx_res.recv_timeout(Duration::from_millis(10_000)) {
|
||||
Ok(res) => {
|
||||
if res == "" {
|
||||
self.env.protocal = Protocal::X11;
|
||||
Ok(())
|
||||
} else {
|
||||
bail!(res)
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
bail!("Failed to recv x11 result {}", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn start_x_session_thread(
|
||||
tx_res: SyncSender<String>,
|
||||
is_child_running: Arc<AtomicBool>,
|
||||
env: DesktopEnv,
|
||||
uid: u32,
|
||||
gid: u32,
|
||||
display_num: u32,
|
||||
password: String,
|
||||
envs: HashMap<&str, String>,
|
||||
) -> ResultType<()> {
|
||||
let mut client = pam::Client::with_password(pam_get_service_name())?;
|
||||
client
|
||||
.conversation_mut()
|
||||
.set_credentials(&env.username, &password);
|
||||
client.authenticate()?;
|
||||
|
||||
client.set_item(pam::PamItemType::TTY, &env.display)?;
|
||||
client.open_session()?;
|
||||
|
||||
// fixme: FreeBSD kernel needs to login here.
|
||||
// see: https://github.com/neutrinolabs/xrdp/blob/a64573b596b5fb07ca3a51590c5308d621f7214e/sesman/session.c#L556
|
||||
|
||||
let (child_xorg, child_wm) = Self::start_x11(&env, uid, gid, display_num, &envs)?;
|
||||
is_child_running.store(true, Ordering::SeqCst);
|
||||
|
||||
log::info!("Start xorg and wm done, notify and wait xtop x11");
|
||||
allow_err!(tx_res.send("".to_owned()));
|
||||
|
||||
Self::wait_stop_x11(child_xorg, child_wm);
|
||||
log::info!("Wait x11 stop done");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn wait_xorg_exit(child_xorg: &mut Child) -> ResultType<String> {
|
||||
if let Ok(_) = child_xorg.kill() {
|
||||
for _ in 0..3 {
|
||||
match child_xorg.try_wait() {
|
||||
Ok(Some(status)) => return Ok(format!("Xorg exit with {}", status)),
|
||||
Ok(None) => {}
|
||||
Err(e) => {
|
||||
// fatal error
|
||||
log::error!("Failed to wait xorg process, {}", e);
|
||||
bail!("Failed to wait xorg process, {}", e)
|
||||
}
|
||||
}
|
||||
std::thread::sleep(std::time::Duration::from_millis(1_000));
|
||||
}
|
||||
log::error!("Failed to wait xorg process, not exit");
|
||||
bail!("Failed to wait xorg process, not exit")
|
||||
} else {
|
||||
Ok("Xorg is already exited".to_owned())
|
||||
}
|
||||
}
|
||||
|
||||
fn start_x11(
|
||||
env: &DesktopEnv,
|
||||
uid: u32,
|
||||
gid: u32,
|
||||
display_num: u32,
|
||||
envs: &HashMap<&str, String>,
|
||||
) -> ResultType<(Child, Child)> {
|
||||
log::debug!("envs of user {}: {:?}", &env.username, &envs);
|
||||
|
||||
DesktopEnv::add_xauth_cookie(&env.xauth, &env.display, uid, gid, &envs)?;
|
||||
|
||||
// Start Xorg
|
||||
let mut child_xorg = Self::start_x_server(&env.xauth, &env.display, uid, gid, &envs)?;
|
||||
|
||||
log::info!("xorg started, wait 10 secs to ensuer x server is running");
|
||||
|
||||
let max_wait_secs = 10;
|
||||
// wait x server running
|
||||
if let Err(e) =
|
||||
DesktopEnv::wait_x_server_running(child_xorg.id(), display_num, max_wait_secs)
|
||||
{
|
||||
match Self::wait_xorg_exit(&mut child_xorg) {
|
||||
Ok(msg) => log::info!("{}", msg),
|
||||
Err(e) => {
|
||||
log::error!("{}", e);
|
||||
Self::fatal_exit();
|
||||
}
|
||||
}
|
||||
bail!(e)
|
||||
}
|
||||
|
||||
log::info!(
|
||||
"xorg is running, start x window manager with DISPLAY: {}, XAUTHORITY: {}",
|
||||
&env.display,
|
||||
&env.xauth
|
||||
);
|
||||
|
||||
std::env::set_var("DISPLAY", &env.display);
|
||||
std::env::set_var("XAUTHORITY", &env.xauth);
|
||||
// start window manager (startwm.sh)
|
||||
let child_wm = match Self::start_x_window_manager(uid, gid, &envs) {
|
||||
Ok(c) => c,
|
||||
Err(e) => {
|
||||
match Self::wait_xorg_exit(&mut child_xorg) {
|
||||
Ok(msg) => log::info!("{}", msg),
|
||||
Err(e) => {
|
||||
log::error!("{}", e);
|
||||
Self::fatal_exit();
|
||||
}
|
||||
}
|
||||
bail!(e)
|
||||
}
|
||||
};
|
||||
log::info!("x window manager is started");
|
||||
|
||||
Ok((child_xorg, child_wm))
|
||||
}
|
||||
|
||||
fn try_wait_x11_child_exit(child_xorg: &mut Child, child_wm: &mut Child) -> bool {
|
||||
match child_xorg.try_wait() {
|
||||
Ok(Some(status)) => {
|
||||
println!(
|
||||
"=============================MYDEBUG Xorg exit with {}",
|
||||
status
|
||||
);
|
||||
log::info!("Xorg exit with {}", status);
|
||||
return true;
|
||||
}
|
||||
Ok(None) => {}
|
||||
Err(e) => log::error!("Failed to wait xorg process, {}", e),
|
||||
}
|
||||
|
||||
match child_wm.try_wait() {
|
||||
Ok(Some(status)) => {
|
||||
println!(
|
||||
"=============================MYDEBUG: wm exit with {}",
|
||||
status
|
||||
);
|
||||
log::info!("wm exit with {}", status);
|
||||
return true;
|
||||
}
|
||||
Ok(None) => {}
|
||||
Err(e) => log::error!("Failed to wait xorg process, {}", e),
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn wait_x11_children_exit(child_xorg: &mut Child, child_wm: &mut Child) {
|
||||
log::debug!("Try kill child process xorg");
|
||||
if let Ok(_) = child_xorg.kill() {
|
||||
let mut exited = false;
|
||||
for _ in 0..2 {
|
||||
match child_xorg.try_wait() {
|
||||
Ok(Some(status)) => {
|
||||
println!(
|
||||
"=============================MYDEBUG Xorg exit with {}",
|
||||
status
|
||||
);
|
||||
log::info!("Xorg exit with {}", status);
|
||||
exited = true;
|
||||
break;
|
||||
}
|
||||
Ok(None) => {}
|
||||
Err(e) => {
|
||||
println!(
|
||||
"=============================MYDEBUG Failed to wait xorg process, {}",
|
||||
&e
|
||||
);
|
||||
log::error!("Failed to wait xorg process, {}", e);
|
||||
Self::fatal_exit();
|
||||
}
|
||||
}
|
||||
std::thread::sleep(std::time::Duration::from_millis(1_000));
|
||||
}
|
||||
if !exited {
|
||||
println!(
|
||||
"=============================MYDEBUG Failed to wait child xorg, after kill()"
|
||||
);
|
||||
log::error!("Failed to wait child xorg, after kill()");
|
||||
// try kill -9?
|
||||
}
|
||||
}
|
||||
log::debug!("Try kill child process wm");
|
||||
if let Ok(_) = child_wm.kill() {
|
||||
let mut exited = false;
|
||||
for _ in 0..2 {
|
||||
match child_wm.try_wait() {
|
||||
Ok(Some(status)) => {
|
||||
println!(
|
||||
"=============================MYDEBUG wm exit with {}",
|
||||
status
|
||||
);
|
||||
log::info!("wm exit with {}", status);
|
||||
exited = true;
|
||||
}
|
||||
Ok(None) => {}
|
||||
Err(e) => {
|
||||
println!(
|
||||
"=============================MYDEBUG Failed to wait wm process, {}",
|
||||
&e
|
||||
);
|
||||
log::error!("Failed to wait wm process, {}", e);
|
||||
Self::fatal_exit();
|
||||
}
|
||||
}
|
||||
std::thread::sleep(std::time::Duration::from_millis(1_000));
|
||||
}
|
||||
if !exited {
|
||||
println!(
|
||||
"=============================MYDEBUG Failed to wait child xorg, after kill()"
|
||||
);
|
||||
log::error!("Failed to wait child xorg, after kill()");
|
||||
// try kill -9?
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn try_wait_stop_x11(child_xorg: &mut Child, child_wm: &mut Child) -> bool {
|
||||
let mut inst = DESKTOP_INST.lock().unwrap();
|
||||
let mut exited = true;
|
||||
if let Some(inst) = &mut (*inst) {
|
||||
if inst.child_exit.load(Ordering::SeqCst) {
|
||||
exited = true;
|
||||
} else {
|
||||
exited = Self::try_wait_x11_child_exit(child_xorg, child_wm);
|
||||
}
|
||||
if exited {
|
||||
println!("=============================MYDEBUG begin to wait x11 children exit");
|
||||
Self::wait_x11_children_exit(child_xorg, child_wm);
|
||||
inst.is_child_running.store(false, Ordering::SeqCst);
|
||||
inst.child_exit.store(true, Ordering::SeqCst);
|
||||
}
|
||||
}
|
||||
exited
|
||||
}
|
||||
|
||||
fn wait_stop_x11(mut child_xorg: Child, mut child_wm: Child) {
|
||||
loop {
|
||||
if Self::try_wait_stop_x11(&mut child_xorg, &mut child_wm) {
|
||||
break;
|
||||
}
|
||||
std::thread::sleep(Duration::from_millis(super::SERVICE_INTERVAL));
|
||||
}
|
||||
}
|
||||
|
||||
fn get_xorg() -> ResultType<&'static str> {
|
||||
// Fedora 26 or later
|
||||
let xorg = "/usr/libexec/Xorg";
|
||||
if Path::new(xorg).is_file() {
|
||||
return Ok(xorg);
|
||||
}
|
||||
// Debian 9 or later
|
||||
let xorg = "/usr/lib/xorg/Xorg";
|
||||
if Path::new(xorg).is_file() {
|
||||
return Ok(xorg);
|
||||
}
|
||||
// Ubuntu 16.04 or later
|
||||
let xorg = "/usr/lib/xorg/Xorg";
|
||||
if Path::new(xorg).is_file() {
|
||||
return Ok(xorg);
|
||||
}
|
||||
// Arch Linux
|
||||
let xorg = "/usr/lib/xorg-server/Xorg";
|
||||
if Path::new(xorg).is_file() {
|
||||
return Ok(xorg);
|
||||
}
|
||||
// Arch Linux
|
||||
let xorg = "/usr/lib/Xorg";
|
||||
if Path::new(xorg).is_file() {
|
||||
return Ok(xorg);
|
||||
}
|
||||
// CentOS 7 /usr/bin/Xorg or param=Xorg
|
||||
|
||||
log::warn!("Failed to find xorg, use default Xorg.\n Please add \"allowed_users=anybody\" to \"/etc/X11/Xwrapper.config\".");
|
||||
Ok("Xorg")
|
||||
}
|
||||
|
||||
fn start_x_server(
|
||||
xauth: &str,
|
||||
display: &str,
|
||||
uid: u32,
|
||||
gid: u32,
|
||||
envs: &HashMap<&str, String>,
|
||||
) -> ResultType<Child> {
|
||||
let xorg = Self::get_xorg()?;
|
||||
log::info!("Use xorg: {}", &xorg);
|
||||
match Command::new(xorg)
|
||||
.envs(envs)
|
||||
.uid(uid)
|
||||
.gid(gid)
|
||||
.args(vec![
|
||||
"-noreset",
|
||||
"+extension",
|
||||
"GLX",
|
||||
"+extension",
|
||||
"RANDR",
|
||||
"+extension",
|
||||
"RENDER",
|
||||
//"-logfile",
|
||||
//"/tmp/RustDesk_xorg.log",
|
||||
"-config",
|
||||
"rustdesk/xorg.conf",
|
||||
"-auth",
|
||||
xauth,
|
||||
display,
|
||||
])
|
||||
.spawn()
|
||||
{
|
||||
Ok(c) => Ok(c),
|
||||
Err(e) => {
|
||||
bail!("Failed to start Xorg with display {}, {}", display, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn start_x_window_manager(
|
||||
uid: u32,
|
||||
gid: u32,
|
||||
envs: &HashMap<&str, String>,
|
||||
) -> ResultType<Child> {
|
||||
match Command::new("/etc/rustdesk/startwm.sh")
|
||||
.envs(envs)
|
||||
.uid(uid)
|
||||
.gid(gid)
|
||||
.spawn()
|
||||
{
|
||||
Ok(c) => Ok(c),
|
||||
Err(e) => {
|
||||
bail!("Failed to start window manager, {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn stop_children(&mut self) {
|
||||
self.child_exit.store(true, Ordering::SeqCst);
|
||||
for _i in 1..10 {
|
||||
if !self.is_child_running.load(Ordering::SeqCst) {
|
||||
break;
|
||||
}
|
||||
std::thread::sleep(Duration::from_millis(super::SERVICE_INTERVAL));
|
||||
}
|
||||
if self.is_child_running.load(Ordering::SeqCst) {
|
||||
log::warn!("xdesktop child is still running!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn pam_get_service_name() -> &'static str {
|
||||
if Path::new("/etc/pam.d/rustdesk").is_file() {
|
||||
"rustdesk"
|
||||
} else {
|
||||
"gdm"
|
||||
}
|
||||
}
|
||||
|
||||
impl ToString for Protocal {
|
||||
fn to_string(&self) -> String {
|
||||
match self {
|
||||
Protocal::X11 => ENV_DESKTOP_PROTOCAL__X11.to_owned(),
|
||||
Protocal::Wayland => ENV_DESKTOP_PROTOCAL_WAYLAND.to_owned(),
|
||||
Protocal::Unknown => ENV_DESKTOP_PROTOCAL_UNKNOWN.to_owned(),
|
||||
}
|
||||
}
|
||||
}
|
@ -17,6 +17,9 @@ pub mod delegate;
|
||||
#[cfg(target_os = "linux")]
|
||||
pub mod linux;
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
pub mod linux_desktop;
|
||||
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
use hbb_common::{message_proto::CursorData, ResultType};
|
||||
#[cfg(not(any(target_os = "macos", target_os = "android", target_os = "ios")))]
|
||||
|
@ -199,8 +199,8 @@ async fn connect_and_login_2(
|
||||
},
|
||||
d = ui_receiver.recv() => {
|
||||
match d {
|
||||
Some(Data::Login((password, remember))) => {
|
||||
interface.handle_login_from_ui(password, remember, &mut stream).await;
|
||||
Some(Data::Login((os_username, os_password, password, remember))) => {
|
||||
interface.handle_login_from_ui(os_username, os_password, password, remember, &mut stream).await;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
@ -54,6 +54,8 @@ impl RendezvousMediator {
|
||||
pub async fn start_all() {
|
||||
let mut nat_tested = false;
|
||||
check_zombie();
|
||||
#[cfg(target_os = "linux")]
|
||||
crate::server::check_xdesktop();
|
||||
let server = new_server();
|
||||
if Config::get_nat_type() == NatType::UNKNOWN_NAT as i32 {
|
||||
crate::test_nat_type();
|
||||
@ -72,7 +74,10 @@ impl RendezvousMediator {
|
||||
allow_err!(super::lan::start_listening());
|
||||
});
|
||||
}
|
||||
loop {
|
||||
#[cfg(target_os = "linux")]
|
||||
crate::platform::linux_desktop::start_xdesktop();
|
||||
SHOULD_EXIT.store(false, Ordering::SeqCst);
|
||||
while !SHOULD_EXIT.load(Ordering::SeqCst) {
|
||||
Config::reset_online();
|
||||
if Config::get_option("stop-service").is_empty() {
|
||||
if !nat_tested {
|
||||
@ -96,6 +101,8 @@ impl RendezvousMediator {
|
||||
}
|
||||
sleep(1.).await;
|
||||
}
|
||||
#[cfg(target_os = "linux")]
|
||||
crate::platform::linux_desktop::stop_xdesktop();
|
||||
}
|
||||
|
||||
pub async fn start(server: ServerPtr, host: String) -> ResultType<()> {
|
||||
|
@ -356,6 +356,40 @@ pub fn check_zombie() {
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
pub fn check_xdesktop() {
|
||||
std::thread::spawn(|| {
|
||||
use crate::platform::linux_desktop::{get_desktop_env, DesktopEnv};
|
||||
let mut desktop_env = DesktopEnv::new();
|
||||
loop {
|
||||
let new_env = match get_desktop_env() {
|
||||
Some(env) => env,
|
||||
None => DesktopEnv::new(),
|
||||
};
|
||||
|
||||
if new_env.is_ready() {
|
||||
if !desktop_env.is_ready() {
|
||||
desktop_env = new_env.clone();
|
||||
} else {
|
||||
if !desktop_env.is_same_env(&new_env) {
|
||||
log::info!("xdesktop env diff {:?} to {:?}", &new_env, desktop_env);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if desktop_env.is_ready() {
|
||||
log::info!("xdesktop env diff {:?} to {:?}", &new_env, desktop_env);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
std::thread::sleep(Duration::from_millis(300));
|
||||
}
|
||||
|
||||
crate::RendezvousMediator::restart();
|
||||
});
|
||||
}
|
||||
|
||||
/// Start the host server that allows the remote peer to control the current machine.
|
||||
///
|
||||
/// # Arguments
|
||||
|
@ -57,6 +57,16 @@ lazy_static::lazy_static! {
|
||||
pub static CLICK_TIME: AtomicI64 = AtomicI64::new(0);
|
||||
pub static MOUSE_MOVE_TIME: AtomicI64 = AtomicI64::new(0);
|
||||
|
||||
pub const LOGIN_MSG_XDESKTOP_NOT_INITED: &str = "xdesktop env is not inited";
|
||||
pub const LOGIN_MSG_XSESSION_NOT_READY: &str = "xsession unready";
|
||||
pub const LOGIN_MSG_XSESSION_FAILED: &str = "xsession failed";
|
||||
pub const LOGIN_MSG_XSESSION_ANOTHER_USER_READTY: &str = "xsession another user login";
|
||||
pub const LOGIN_MSG_XSESSION_NOT_READY_PASSWORD_EMPTY: &str = "xsession unready, password empty";
|
||||
pub const LOGIN_MSG_XSESSION_NOT_READY_PASSWORD_WRONG: &str = "xsession unready, password wrong";
|
||||
pub const LOGIN_MSG_PASSWORD_EMPTY: &str = "Empty Password";
|
||||
pub const LOGIN_MSG_PASSWORD_WRONG: &str = "Wrong Password";
|
||||
pub const LOGIN_MSG_OFFLINE: &str = "Offline";
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct ConnInner {
|
||||
id: i32,
|
||||
@ -902,7 +912,7 @@ impl Connection {
|
||||
}
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
if self.file_transfer.is_some() {
|
||||
if crate::platform::is_prelogin() || self.tx_to_cm.send(ipc::Data::Test).is_err() {
|
||||
if !crate::platform::is_prelogin() || self.tx_to_cm.send(ipc::Data::Test).is_err() {
|
||||
username = "".to_owned();
|
||||
}
|
||||
}
|
||||
@ -1061,6 +1071,47 @@ impl Connection {
|
||||
self.tx_input.send(MessageInput::Key((msg, press))).ok();
|
||||
}
|
||||
|
||||
fn try_start_desktop(_username: &str, _passsword: &str) -> String {
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
if _username.is_empty() {
|
||||
match crate::platform::linux_desktop::get_desktop_env() {
|
||||
Some(desktop_env) => {
|
||||
if desktop_env.is_ready() {
|
||||
""
|
||||
} else {
|
||||
LOGIN_MSG_XSESSION_NOT_READY
|
||||
}
|
||||
}
|
||||
None => LOGIN_MSG_XDESKTOP_NOT_INITED,
|
||||
}
|
||||
.to_owned()
|
||||
} else {
|
||||
match crate::platform::linux_desktop::try_start_x_session(_username, _passsword) {
|
||||
Ok(desktop_env) => {
|
||||
if desktop_env.is_ready() {
|
||||
if _username != desktop_env.username {
|
||||
LOGIN_MSG_XSESSION_ANOTHER_USER_READTY.to_owned()
|
||||
} else {
|
||||
"".to_owned()
|
||||
}
|
||||
} else {
|
||||
LOGIN_MSG_XSESSION_NOT_READY.to_owned()
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("Failed to start xsession {}", e);
|
||||
LOGIN_MSG_XSESSION_FAILED.to_owned()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
{
|
||||
"".to_owned()
|
||||
}
|
||||
}
|
||||
|
||||
fn validate_one_password(&self, password: String) -> bool {
|
||||
if password.len() == 0 {
|
||||
return false;
|
||||
@ -1225,16 +1276,31 @@ impl Connection {
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
let desktop_err = match lr.os_login.as_ref() {
|
||||
Some(os_login) => Self::try_start_desktop(&os_login.username, &os_login.password),
|
||||
None => Self::try_start_desktop("", ""),
|
||||
};
|
||||
if !desktop_err.is_empty() && desktop_err != LOGIN_MSG_XSESSION_NOT_READY {
|
||||
self.send_login_error(desktop_err).await;
|
||||
return true;
|
||||
}
|
||||
|
||||
if !hbb_common::is_ipv4_str(&lr.username) && lr.username != Config::get_id() {
|
||||
self.send_login_error("Offline").await;
|
||||
self.send_login_error(LOGIN_MSG_OFFLINE).await;
|
||||
} else if password::approve_mode() == ApproveMode::Click
|
||||
|| password::approve_mode() == ApproveMode::Both && !password::has_valid_password()
|
||||
{
|
||||
self.try_start_cm(lr.my_id, lr.my_name, false);
|
||||
if hbb_common::get_version_number(&lr.version)
|
||||
>= hbb_common::get_version_number("1.2.0")
|
||||
{
|
||||
self.send_login_error("No Password Access").await;
|
||||
if desktop_err.is_empty() {
|
||||
self.try_start_cm(lr.my_id, lr.my_name, false);
|
||||
if hbb_common::get_version_number(&lr.version)
|
||||
>= hbb_common::get_version_number("1.2.0")
|
||||
{
|
||||
self.send_login_error("No Password Access").await;
|
||||
}
|
||||
} else {
|
||||
self.send_login_error(LOGIN_MSG_XSESSION_NOT_READY_PASSWORD_EMPTY)
|
||||
.await;
|
||||
}
|
||||
return true;
|
||||
} else if password::approve_mode() == ApproveMode::Password
|
||||
@ -1290,16 +1356,25 @@ impl Connection {
|
||||
.lock()
|
||||
.unwrap()
|
||||
.insert(self.ip.clone(), failure);
|
||||
self.send_login_error("Wrong Password").await;
|
||||
self.try_start_cm(lr.my_id, lr.my_name, false);
|
||||
if desktop_err.is_empty() {
|
||||
self.send_login_error(LOGIN_MSG_PASSWORD_WRONG).await;
|
||||
self.try_start_cm(lr.my_id, lr.my_name, false);
|
||||
} else {
|
||||
self.send_login_error(LOGIN_MSG_XSESSION_NOT_READY_PASSWORD_WRONG)
|
||||
.await;
|
||||
}
|
||||
} else {
|
||||
if failure.0 != 0 {
|
||||
LOGIN_FAILURES.lock().unwrap().remove(&self.ip);
|
||||
}
|
||||
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;
|
||||
if desktop_err.is_empty() {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2072,7 +2147,7 @@ async fn start_ipc(
|
||||
tx_from_cm: mpsc::UnboundedSender<ipc::Data>,
|
||||
) -> ResultType<()> {
|
||||
loop {
|
||||
if !crate::platform::is_prelogin() {
|
||||
if crate::platform::is_prelogin() {
|
||||
break;
|
||||
}
|
||||
sleep(1.).await;
|
||||
|
@ -221,6 +221,7 @@ impl<T: Subscriber + From<ConnInner>> ServiceTmpl<T> {
|
||||
thread::sleep(interval - elapsed);
|
||||
}
|
||||
}
|
||||
log::info!("Service {} exit", sp.name());
|
||||
});
|
||||
self.0.write().unwrap().handle = Some(thread);
|
||||
}
|
||||
@ -256,6 +257,7 @@ impl<T: Subscriber + From<ConnInner>> ServiceTmpl<T> {
|
||||
}
|
||||
thread::sleep(time::Duration::from_millis(HIBERNATE_TIMEOUT));
|
||||
}
|
||||
log::info!("Service {} exit", sp.name());
|
||||
});
|
||||
self.0.write().unwrap().handle = Some(thread);
|
||||
}
|
||||
|
@ -142,6 +142,10 @@ div.password input {
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
div.username input {
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
svg {
|
||||
background: none;
|
||||
}
|
||||
|
@ -252,7 +252,7 @@ function msgbox(type, title, content, link="", callback=null, height=180, width=
|
||||
view.close();
|
||||
return;
|
||||
}
|
||||
handler.login(res.password, res.remember);
|
||||
handler.login("", "", res.password, res.remember);
|
||||
if (!is_port_forward) {
|
||||
// Specially handling file transfer for no permission hanging issue (including 60ms
|
||||
// timer in setPermission.
|
||||
@ -262,6 +262,30 @@ function msgbox(type, title, content, link="", callback=null, height=180, width=
|
||||
else msgbox("connecting", "Connecting...", "Logging in...");
|
||||
}
|
||||
};
|
||||
} else if (type == "xsession-login" || type == "xsession-re-login") {
|
||||
callback = function (res) {
|
||||
if (!res) {
|
||||
view.close();
|
||||
return;
|
||||
}
|
||||
handler.login(res.osusername, res.ospassword, "", false);
|
||||
if (!is_port_forward) {
|
||||
if (is_file_transfer) handler.msgbox("connecting", "Connecting...", "Logging in...");
|
||||
else msgbox("connecting", "Connecting...", "Logging in...");
|
||||
}
|
||||
};
|
||||
} else if (type.indexOf("xsession-login") >= 0) {
|
||||
callback = function (res) {
|
||||
if (!res) {
|
||||
view.close();
|
||||
return;
|
||||
}
|
||||
handler.login(res.osusername, res.ospassword, res.password, res.remember);
|
||||
if (!is_port_forward) {
|
||||
if (is_file_transfer) handler.msgbox("connecting", "Connecting...", "Logging in...");
|
||||
else msgbox("connecting", "Connecting...", "Logging in...");
|
||||
}
|
||||
};
|
||||
} else if (type.indexOf("custom") < 0 && !is_port_forward && !callback) {
|
||||
callback = function() { view.close(); }
|
||||
} else if (type == 'wait-remote-accept-nook') {
|
||||
|
@ -32,7 +32,7 @@ class MsgboxComponent: Reactor.Component {
|
||||
}
|
||||
|
||||
function getIcon(color) {
|
||||
if (this.type == "input-password") {
|
||||
if (this.type == "input-password" || this.type == "xsession-login" || this.type == "xsession-login-password") {
|
||||
return <svg viewBox="0 0 505 505"><circle cx="252.5" cy="252.5" r="252.5" fill={color}/><path d="M271.9 246.1c29.2 17.5 67.6 13.6 92.7-11.5 29.7-29.7 29.7-77.8 0-107.4s-77.8-29.7-107.4 0c-25.1 25.1-29 63.5-11.5 92.7L118.1 347.4l26.2 26.2 26.4 26.4 10.6-10.6-10.1-10.1 9.7-9.7 10.1 10.1 10.6-10.6-10.1-10 9.7-9.7 10.1 10.1 10.6-10.6-26.4-26.3 76.4-76.5z" fill="#fff"/><circle cx="337.4" cy="154.4" r="17.7" fill={color}/></svg>;
|
||||
}
|
||||
if (this.type == "connecting") {
|
||||
@ -41,7 +41,7 @@ class MsgboxComponent: Reactor.Component {
|
||||
if (this.type == "success") {
|
||||
return <svg viewBox="0 0 512 512"><circle cx="256" cy="256" r="256" fill={color} /><path fill="#fff" d="M235.472 392.08l-121.04-94.296 34.416-44.168 74.328 57.904 122.672-177.016 46.032 31.888z"/></svg>;
|
||||
}
|
||||
if (this.type.indexOf("error") >= 0 || this.type == "re-input-password") {
|
||||
if (this.type.indexOf("error") >= 0 || this.type == "re-input-password" || this.type == "xsession-re-login" || this.type == "xsession-login-re-password") {
|
||||
return <svg viewBox="0 0 512 512"><ellipse cx="256" cy="256" rx="256" ry="255.832" fill={color}/><g fill="#fff"><path d="M376.812 337.18l-39.592 39.593-201.998-201.999 39.592-39.592z"/><path d="M376.818 174.825L174.819 376.824l-39.592-39.592 201.999-201.999z"/></g></svg>;
|
||||
}
|
||||
return null;
|
||||
@ -56,11 +56,36 @@ class MsgboxComponent: Reactor.Component {
|
||||
</div>;
|
||||
}
|
||||
|
||||
function getInputUserPasswordContent() {
|
||||
return <div .form>
|
||||
<div>{translate("OS Username")}</div>
|
||||
<div .username><input name='osusername' type='text' .outline-focus /></div>
|
||||
<div>{translate("OS Password")}</div>
|
||||
<PasswordComponent name='ospassword' />
|
||||
<div></div>
|
||||
</div>;
|
||||
}
|
||||
|
||||
function getXsessionPasswordContent() {
|
||||
return <div .form>
|
||||
<div>{translate("OS Username")}</div>
|
||||
<div .username><input name='osusername' type='text' .outline-focus /></div>
|
||||
<div>{translate("OS Password")}</div>
|
||||
<PasswordComponent name='ospassword' />
|
||||
<div>{translate('Please enter your password')}</div>
|
||||
<PasswordComponent />
|
||||
<div><button|checkbox(remember) {ts}>{translate('Remember password')}</button></div>
|
||||
</div>;
|
||||
}
|
||||
|
||||
function getContent() {
|
||||
if (this.type == "input-password") {
|
||||
return this.getInputPasswordContent();
|
||||
}
|
||||
if (this.type == "custom-os-password") {
|
||||
} else if (this.type == "xsession-login") {
|
||||
return this.getInputUserPasswordContent();
|
||||
} else if (this.type == "xsession-login-password") {
|
||||
return this.getXsessionPasswordContent();
|
||||
} else if (this.type == "custom-os-password") {
|
||||
var ts = this.auto_login ? { checked: true } : {};
|
||||
return <div .form>
|
||||
<PasswordComponent value={this.content} />
|
||||
@ -71,13 +96,13 @@ class MsgboxComponent: Reactor.Component {
|
||||
}
|
||||
|
||||
function getColor() {
|
||||
if (this.type == "input-password" || this.type == "custom-os-password") {
|
||||
if (this.type == "input-password" || this.type == "custom-os-password" || this.type == "xsession-login" || this.type == "xsession-login-password") {
|
||||
return "#AD448E";
|
||||
}
|
||||
if (this.type == "success") {
|
||||
return "#32bea6";
|
||||
}
|
||||
if (this.type.indexOf("error") >= 0 || this.type == "re-input-password") {
|
||||
if (this.type.indexOf("error") >= 0 || this.type == "re-input-password" || this.type == "xsession-re-login" || this.type == "xsession-login-re-password") {
|
||||
return "#e04f5f";
|
||||
}
|
||||
return "#2C8CFF";
|
||||
@ -177,6 +202,16 @@ class MsgboxComponent: Reactor.Component {
|
||||
this.update();
|
||||
return;
|
||||
}
|
||||
if (this.type == "xsession-re-login") {
|
||||
this.type = "xsession-login";
|
||||
this.update();
|
||||
return;
|
||||
}
|
||||
if (this.type == "xsession-login-re-password") {
|
||||
this.type = "xsession-login-password";
|
||||
this.update();
|
||||
return;
|
||||
}
|
||||
var values = this.getValues();
|
||||
if (this.callback) {
|
||||
var self = this;
|
||||
@ -238,6 +273,21 @@ class MsgboxComponent: Reactor.Component {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (this.type == "xsession-login") {
|
||||
values.osusername = (values.osusername || "").trim();
|
||||
values.ospassword = (values.ospassword || "").trim();
|
||||
if (!values.osusername || !values.ospassword) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (this.type == "xsession-login-password") {
|
||||
values.password = (values.password || "").trim();
|
||||
values.osusername = (values.osusername || "").trim();
|
||||
values.ospassword = (values.ospassword || "").trim();
|
||||
if (!values.osusername || !values.ospassword || !values.password) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
|
@ -398,7 +398,7 @@ impl sciter::EventHandler for SciterSession {
|
||||
fn is_file_transfer();
|
||||
fn is_port_forward();
|
||||
fn is_rdp();
|
||||
fn login(String, bool);
|
||||
fn login(String, String, String, bool);
|
||||
fn new_rdp();
|
||||
fn send_mouse(i32, i32, i32, bool, bool, bool, bool);
|
||||
fn enter();
|
||||
|
@ -711,8 +711,14 @@ impl<T: InvokeUiSession> Session<T> {
|
||||
fs::get_string(&path)
|
||||
}
|
||||
|
||||
pub fn login(&self, password: String, remember: bool) {
|
||||
self.send(Data::Login((password, remember)));
|
||||
pub fn login(
|
||||
&mut self,
|
||||
os_username: String,
|
||||
os_password: String,
|
||||
password: String,
|
||||
remember: bool,
|
||||
) {
|
||||
self.send(Data::Login((os_username, os_password, password, remember)));
|
||||
}
|
||||
|
||||
pub fn new_rdp(&self) {
|
||||
@ -1005,8 +1011,23 @@ impl<T: InvokeUiSession> Interface for Session<T> {
|
||||
handle_hash(self.lc.clone(), pass, hash, self, peer).await;
|
||||
}
|
||||
|
||||
async fn handle_login_from_ui(&mut self, password: String, remember: bool, peer: &mut Stream) {
|
||||
handle_login_from_ui(self.lc.clone(), password, remember, peer).await;
|
||||
async fn handle_login_from_ui(
|
||||
&mut self,
|
||||
os_username: String,
|
||||
os_password: String,
|
||||
password: String,
|
||||
remember: bool,
|
||||
peer: &mut Stream,
|
||||
) {
|
||||
handle_login_from_ui(
|
||||
self.lc.clone(),
|
||||
os_username,
|
||||
os_password,
|
||||
password,
|
||||
remember,
|
||||
peer,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
async fn handle_test_delay(&mut self, t: TestDelay, peer: &mut Stream) {
|
||||
|
Loading…
Reference in New Issue
Block a user