Merge pull request #3855 from fufesou/refact/linux_run_server_on_gdm_wayland

start --server on gmd wayland
This commit is contained in:
RustDesk 2023-03-31 16:58:58 +08:00 committed by GitHub
commit 02561c3fff
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 403 additions and 189 deletions

View File

@ -115,7 +115,7 @@ impl Enigo {
impl Default for Enigo { impl Default for Enigo {
fn default() -> Self { fn default() -> Self {
let is_x11 = "x11" == hbb_common::platform::linux::get_display_server(); let is_x11 = hbb_common::platform::linux::is_x11_or_headless();
Self { Self {
is_x11, is_x11,
tfc: if is_x11 { tfc: if is_x11 {

View File

@ -915,15 +915,12 @@ impl PeerConfig {
decrypt_vec_or_original(&config.password, PASSWORD_ENC_VERSION); decrypt_vec_or_original(&config.password, PASSWORD_ENC_VERSION);
config.password = password; config.password = password;
store = store || store2; store = store || store2;
if let Some(v) = config.options.get_mut("rdp_password") { for opt in ["rdp_password", "os-password"] {
let (password, _, store2) = decrypt_str_or_original(v, PASSWORD_ENC_VERSION); if let Some(v) = config.options.get_mut(opt) {
*v = password; let (encrypted, _, store2) = decrypt_str_or_original(v, PASSWORD_ENC_VERSION);
*v = encrypted;
store = store || store2; store = store || store2;
} }
if let Some(v) = config.options.get_mut("os-password") {
let (password, _, store2) = decrypt_str_or_original(v, PASSWORD_ENC_VERSION);
*v = password;
store = store || store2;
} }
if store { if store {
config.store(id); config.store(id);
@ -941,12 +938,11 @@ impl PeerConfig {
let _lock = CONFIG.read().unwrap(); let _lock = CONFIG.read().unwrap();
let mut config = self.clone(); let mut config = self.clone();
config.password = encrypt_vec_or_original(&config.password, PASSWORD_ENC_VERSION); config.password = encrypt_vec_or_original(&config.password, PASSWORD_ENC_VERSION);
if let Some(v) = config.options.get_mut("rdp_password") { for opt in ["rdp_password", "os-password"] {
if let Some(v) = config.options.get_mut(opt) {
*v = encrypt_str_or_original(v, PASSWORD_ENC_VERSION) *v = encrypt_str_or_original(v, PASSWORD_ENC_VERSION)
} }
if let Some(v) = config.options.get_mut("os-password") { }
*v = encrypt_str_or_original(v, PASSWORD_ENC_VERSION)
};
if let Err(err) = store_path(Self::path(id), config) { if let Err(err) = store_path(Self::path(id), config) {
log::error!("Failed to store config: {}", err); log::error!("Failed to store config: {}", err);
} }

View File

@ -5,6 +5,9 @@ lazy_static::lazy_static! {
pub static ref DISTRO: Distro = Distro::new(); pub static ref DISTRO: Distro = Distro::new();
} }
pub const DISPLAY_SERVER_WAYLAND: &str = "wayland";
pub const DISPLAY_SERVER_X11: &str = "x11";
pub struct Distro { pub struct Distro {
pub name: String, pub name: String,
pub version_id: String, pub version_id: String,
@ -12,13 +15,12 @@ pub struct Distro {
impl Distro { impl Distro {
fn new() -> Self { fn new() -> Self {
let name = run_cmds("awk -F'=' '/^NAME=/ {print $2}' /etc/os-release".to_owned()) let name = run_cmds("awk -F'=' '/^NAME=/ {print $2}' /etc/os-release")
.unwrap_or_default() .unwrap_or_default()
.trim() .trim()
.trim_matches('"') .trim_matches('"')
.to_string(); .to_string();
let version_id = let version_id = run_cmds("awk -F'=' '/^VERSION_ID=/ {print $2}' /etc/os-release")
run_cmds("awk -F'=' '/^VERSION_ID=/ {print $2}' /etc/os-release".to_owned())
.unwrap_or_default() .unwrap_or_default()
.trim() .trim()
.trim_matches('"') .trim_matches('"')
@ -27,8 +29,27 @@ impl Distro {
} }
} }
#[inline]
pub fn is_gdm_user(username: &str) -> bool {
username == "gdm"
// || username == "lightgdm"
}
#[inline]
pub fn is_desktop_wayland() -> bool {
get_display_server() == DISPLAY_SERVER_WAYLAND
}
#[inline]
pub fn is_x11_or_headless() -> bool {
!is_desktop_wayland()
}
// -1
const INVALID_SESSION: &str = "4294967295";
pub fn get_display_server() -> String { pub fn get_display_server() -> String {
let mut session = get_values_of_seat0([0].to_vec())[0].clone(); let mut session = get_values_of_seat0(&[0])[0].clone();
if session.is_empty() { if session.is_empty() {
// loginctl has not given the expected output. try something else. // loginctl has not given the expected output. try something else.
if let Ok(sid) = std::env::var("XDG_SESSION_ID") { if let Ok(sid) = std::env::var("XDG_SESSION_ID") {
@ -36,14 +57,20 @@ pub fn get_display_server() -> String {
session = sid; session = sid;
} }
if session.is_empty() { if session.is_empty() {
session = run_cmds("cat /proc/self/sessionid".to_owned()).unwrap_or_default(); session = run_cmds("cat /proc/self/sessionid").unwrap_or_default();
if session == INVALID_SESSION {
session = "".to_owned();
} }
} }
}
if session.is_empty() {
"".to_owned()
} else {
get_display_server_of_session(&session) get_display_server_of_session(&session)
}
} }
fn get_display_server_of_session(session: &str) -> String { pub fn get_display_server_of_session(session: &str) -> String {
let mut display_server = if let Ok(output) = let mut display_server = if let Ok(output) =
run_loginctl(Some(vec!["show-session", "-p", "Type", session])) run_loginctl(Some(vec!["show-session", "-p", "Type", session]))
// Check session type of the session // Check session type of the session
@ -61,7 +88,7 @@ fn get_display_server_of_session(session: &str) -> String {
.replace("TTY=", "") .replace("TTY=", "")
.trim_end() .trim_end()
.into(); .into();
if let Ok(xorg_results) = run_cmds(format!("ps -e | grep \"{tty}.\\\\+Xorg\"")) if let Ok(xorg_results) = run_cmds(&format!("ps -e | grep \"{tty}.\\\\+Xorg\""))
// And check if Xorg is running on that tty // And check if Xorg is running on that tty
{ {
if xorg_results.trim_end() != "" { if xorg_results.trim_end() != "" {
@ -87,44 +114,68 @@ fn get_display_server_of_session(session: &str) -> String {
display_server.to_lowercase() display_server.to_lowercase()
} }
pub fn get_values_of_seat0(indices: Vec<usize>) -> Vec<String> { #[inline]
fn line_values(indices: &[usize], line: &str) -> Vec<String> {
indices
.into_iter()
.map(|idx| line.split_whitespace().nth(*idx).unwrap_or("").to_owned())
.collect::<Vec<String>>()
}
#[inline]
pub fn get_values_of_seat0(indices: &[usize]) -> Vec<String> {
_get_values_of_seat0(indices, true)
}
#[inline]
pub fn get_values_of_seat0_with_gdm_wayland(indices: &[usize]) -> Vec<String> {
_get_values_of_seat0(indices, false)
}
fn _get_values_of_seat0(indices: &[usize], ignore_gdm_wayland: bool) -> Vec<String> {
if let Ok(output) = run_loginctl(None) { if let Ok(output) = run_loginctl(None) {
for line in String::from_utf8_lossy(&output.stdout).lines() { for line in String::from_utf8_lossy(&output.stdout).lines() {
if line.contains("seat0") { if line.contains("seat0") {
if let Some(sid) = line.split_whitespace().next() { if let Some(sid) = line.split_whitespace().next() {
if is_active(sid) { if is_active(sid) {
return indices if ignore_gdm_wayland {
.into_iter() if is_gdm_user(line.split_whitespace().nth(2).unwrap_or(""))
.map(|idx| line.split_whitespace().nth(idx).unwrap_or("").to_owned()) && get_display_server_of_session(sid) == DISPLAY_SERVER_WAYLAND
.collect::<Vec<String>>(); {
continue;
} }
} }
return line_values(indices, line);
}
} }
} }
} }
// some case, there is no seat0 https://github.com/rustdesk/rustdesk/issues/73 // some case, there is no seat0 https://github.com/rustdesk/rustdesk/issues/73
if let Ok(output) = run_loginctl(None) {
for line in String::from_utf8_lossy(&output.stdout).lines() { for line in String::from_utf8_lossy(&output.stdout).lines() {
if let Some(sid) = line.split_whitespace().next() { if let Some(sid) = line.split_whitespace().next() {
if is_active(sid) {
let d = get_display_server_of_session(sid); let d = get_display_server_of_session(sid);
if is_active(sid) && d != "tty" { if ignore_gdm_wayland {
return indices if is_gdm_user(line.split_whitespace().nth(2).unwrap_or(""))
.into_iter() && d == DISPLAY_SERVER_WAYLAND
.map(|idx| line.split_whitespace().nth(idx).unwrap_or("").to_owned()) {
.collect::<Vec<String>>(); continue;
}
}
if d == "tty" {
continue;
}
return line_values(indices, line);
} }
} }
} }
} }
return indices line_values(indices, "")
.iter()
.map(|_x| "".to_owned())
.collect::<Vec<String>>();
} }
fn is_active(sid: &str) -> bool { pub fn is_active(sid: &str) -> bool {
if let Ok(output) = run_loginctl(Some(vec!["show-session", "-p", "State", sid])) { if let Ok(output) = run_loginctl(Some(vec!["show-session", "-p", "State", sid])) {
String::from_utf8_lossy(&output.stdout).contains("active") String::from_utf8_lossy(&output.stdout).contains("active")
} else { } else {
@ -132,9 +183,9 @@ fn is_active(sid: &str) -> bool {
} }
} }
pub fn run_cmds(cmds: String) -> ResultType<String> { pub fn run_cmds(cmds: &str) -> ResultType<String> {
let output = std::process::Command::new("sh") let output = std::process::Command::new("sh")
.args(vec!["-c", &cmds]) .args(vec!["-c", cmds])
.output()?; .output()?;
Ok(String::from_utf8_lossy(&output.stdout).to_string()) Ok(String::from_utf8_lossy(&output.stdout).to_string())
} }

View File

@ -74,7 +74,7 @@ pub trait TraitCapturer {
#[cfg(x11)] #[cfg(x11)]
#[inline] #[inline]
pub fn is_x11() -> bool { pub fn is_x11() -> bool {
"x11" == hbb_common::platform::linux::get_display_server() hbb_common::platform::linux::is_x11_or_headless()
} }
#[cfg(x11)] #[cfg(x11)]

View File

@ -755,7 +755,7 @@ lazy_static::lazy_static! {
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
lazy_static::lazy_static! { lazy_static::lazy_static! {
pub static ref IS_X11: bool = "x11" == hbb_common::platform::linux::get_display_server(); pub static ref IS_X11: bool = hbb_common::platform::linux::is_x11_or_headless();
} }
pub fn make_fd_to_json(id: i32, path: String, entries: &Vec<FileEntry>) -> String { pub fn make_fd_to_json(id: i32, path: String, entries: &Vec<FileEntry>) -> String {

View File

@ -1,4 +1,5 @@
use super::{CursorData, ResultType}; use super::{CursorData, ResultType};
use desktop::Desktop;
pub use hbb_common::platform::linux::*; pub use hbb_common::platform::linux::*;
use hbb_common::{ use hbb_common::{
allow_err, bail, allow_err, bail,
@ -64,6 +65,11 @@ pub struct xcb_xfixes_get_cursor_image {
pub pixels: *const c_long, pub pixels: *const c_long,
} }
#[inline]
fn sleep_millis(millis: u64) {
std::thread::sleep(Duration::from_millis(millis));
}
pub fn get_cursor_pos() -> Option<(i32, i32)> { pub fn get_cursor_pos() -> Option<(i32, i32)> {
let mut res = None; let mut res = None;
XDO.with(|xdo| { XDO.with(|xdo| {
@ -190,7 +196,7 @@ fn start_server(user: Option<(String, String)>, server: &mut Option<Child>) {
fn stop_server(server: &mut Option<Child>) { fn stop_server(server: &mut Option<Child>) {
if let Some(mut ps) = server.take() { if let Some(mut ps) = server.take() {
allow_err!(ps.kill()); allow_err!(ps.kill());
std::thread::sleep(Duration::from_millis(30)); sleep_millis(30);
match ps.try_wait() { match ps.try_wait() {
Ok(Some(_status)) => {} Ok(Some(_status)) => {}
Ok(None) => { Ok(None) => {
@ -201,44 +207,20 @@ fn stop_server(server: &mut Option<Child>) {
} }
} }
fn set_x11_env(uid: &str) { fn set_x11_env(desktop: &Desktop) {
log::info!("uid of seat0: {}", uid); log::info!("DISPLAY: {}", desktop.display);
let gdm = format!("/run/user/{}/gdm/Xauthority", uid); log::info!("XAUTHORITY: {}", desktop.xauth);
let mut auth = get_env_tries("XAUTHORITY", uid, 10); if !desktop.display.is_empty() {
// auth is another user's when uid = 0, https://github.com/rustdesk/rustdesk/issues/2468 std::env::set_var("DISPLAY", &desktop.display);
if auth.is_empty() || uid == "0" {
auth = if Path::new(&gdm).exists() {
gdm
} else {
let username = get_active_username();
if username == "root" {
format!("/{}/.Xauthority", username)
} else {
let tmp = format!("/home/{}/.Xauthority", username);
if Path::new(&tmp).exists() {
tmp
} else {
format!("/var/lib/{}/.Xauthority", username)
} }
if !desktop.xauth.is_empty() {
std::env::set_var("XAUTHORITY", &desktop.xauth);
} }
};
}
let mut d = get_env("DISPLAY", uid);
if d.is_empty() {
d = get_display();
}
if d.is_empty() {
d = ":0".to_owned();
}
d = d.replace(&whoami::hostname(), "").replace("localhost", "");
log::info!("DISPLAY: {}", d);
log::info!("XAUTHORITY: {}", auth);
std::env::set_var("XAUTHORITY", auth);
std::env::set_var("DISPLAY", d);
} }
#[inline]
fn stop_rustdesk_servers() { fn stop_rustdesk_servers() {
let _ = run_cmds(format!( let _ = run_cmds(&format!(
r##"ps -ef | grep -E 'rustdesk +--server' | awk '{{printf("kill -9 %d\n", $2)}}' | bash"##, r##"ps -ef | grep -E 'rustdesk +--server' | awk '{{printf("kill -9 %d\n", $2)}}' | bash"##,
)); ));
} }
@ -246,37 +228,49 @@ fn stop_rustdesk_servers() {
fn should_start_server( fn should_start_server(
try_x11: bool, try_x11: bool,
uid: &mut String, uid: &mut String,
cur_uid: String, desktop: &Desktop,
cm0: &mut bool, cm0: &mut bool,
last_restart: &mut Instant, last_restart: &mut Instant,
server: &mut Option<Child>, server: &mut Option<Child>,
) -> bool { ) -> bool {
let cm = get_cm(); let cm = get_cm();
let mut start_new = false; let mut start_new = false;
if cur_uid != *uid && !cur_uid.is_empty() { let mut should_kill = false;
*uid = cur_uid;
if desktop.is_headless() {
if !uid.is_empty() {
// From having a monitor to not having a monitor.
*uid = "".to_owned();
should_kill = true;
}
} else if desktop.uid != *uid && !desktop.uid.is_empty() {
*uid = desktop.uid.clone();
if try_x11 { if try_x11 {
set_x11_env(&uid); set_x11_env(&desktop);
} }
if let Some(ps) = server.as_mut() { should_kill = true;
allow_err!(ps.kill());
std::thread::sleep(Duration::from_millis(30));
*last_restart = Instant::now();
} }
} else if !cm
if !should_kill
&& !cm
&& ((*cm0 && last_restart.elapsed().as_secs() > 60) && ((*cm0 && last_restart.elapsed().as_secs() > 60)
|| last_restart.elapsed().as_secs() > 3600) || last_restart.elapsed().as_secs() > 3600)
{ {
// restart server if new connections all closed, or every one hour, // restart server if new connections all closed, or every one hour,
// as a workaround to resolve "SpotUdp" (dns resolve) // as a workaround to resolve "SpotUdp" (dns resolve)
// and x server get displays failure issue // and x server get displays failure issue
if let Some(ps) = server.as_mut() { should_kill = true;
allow_err!(ps.kill());
std::thread::sleep(Duration::from_millis(30));
*last_restart = Instant::now();
log::info!("restart server"); log::info!("restart server");
} }
if should_kill {
if let Some(ps) = server.as_mut() {
allow_err!(ps.kill());
sleep_millis(30);
*last_restart = Instant::now();
} }
}
if let Some(ps) = server.as_mut() { if let Some(ps) = server.as_mut() {
match ps.try_wait() { match ps.try_wait() {
Ok(Some(_)) => { Ok(Some(_)) => {
@ -296,7 +290,7 @@ fn should_start_server(
// stop_rustdesk_servers() is just a temp solution here. // stop_rustdesk_servers() is just a temp solution here.
fn force_stop_server() { fn force_stop_server() {
stop_rustdesk_servers(); stop_rustdesk_servers();
std::thread::sleep(Duration::from_millis(super::SERVICE_INTERVAL)); sleep_millis(super::SERVICE_INTERVAL);
} }
pub fn start_os_service() { pub fn start_os_service() {
@ -305,6 +299,8 @@ pub fn start_os_service() {
let running = Arc::new(AtomicBool::new(true)); let running = Arc::new(AtomicBool::new(true));
let r = running.clone(); let r = running.clone();
let mut desktop = Desktop::default();
let mut sid = "".to_owned();
let mut uid = "".to_owned(); let mut uid = "".to_owned();
let mut server: Option<Child> = None; let mut server: Option<Child> = None;
let mut user_server: Option<Child> = None; let mut user_server: Option<Child> = None;
@ -317,31 +313,18 @@ pub fn start_os_service() {
let mut cm0 = false; let mut cm0 = false;
let mut last_restart = Instant::now(); let mut last_restart = Instant::now();
while running.load(Ordering::SeqCst) { while running.load(Ordering::SeqCst) {
let (cur_uid, cur_user) = get_active_user_id_name(); desktop.refresh();
// for fixing https://github.com/rustdesk/rustdesk/issues/3129 to avoid too much dbus calling, // Duplicate logic here with should_start_server
// though duplicate logic here with should_start_server // Login wayland will try to start a headless --server.
if !(cur_uid != *uid && !cur_uid.is_empty()) { if desktop.username == "root" || !desktop.is_wayland() || desktop.is_login_wayland() {
let cm = get_cm();
if !(!cm
&& ((cm0 && last_restart.elapsed().as_secs() > 60)
|| last_restart.elapsed().as_secs() > 3600))
{
std::thread::sleep(Duration::from_millis(500));
continue;
}
}
let is_wayland = current_is_wayland();
if cur_user == "root" || !is_wayland {
// try kill subprocess "--server" // try kill subprocess "--server"
stop_server(&mut user_server); stop_server(&mut user_server);
// try start subprocess "--server" // try start subprocess "--server"
if should_start_server( if should_start_server(
true, true,
&mut uid, &mut uid,
cur_uid, &desktop,
&mut cm0, &mut cm0,
&mut last_restart, &mut last_restart,
&mut server, &mut server,
@ -349,8 +332,7 @@ pub fn start_os_service() {
force_stop_server(); force_stop_server();
start_server(None, &mut server); start_server(None, &mut server);
} }
} else if cur_user != "" { } else if desktop.username != "" {
if cur_user != "gdm" {
// try kill subprocess "--server" // try kill subprocess "--server"
stop_server(&mut server); stop_server(&mut server);
@ -358,21 +340,34 @@ pub fn start_os_service() {
if should_start_server( if should_start_server(
false, false,
&mut uid, &mut uid,
cur_uid.clone(), &desktop,
&mut cm0, &mut cm0,
&mut last_restart, &mut last_restart,
&mut user_server, &mut user_server,
) { ) {
force_stop_server(); force_stop_server();
start_server(Some((cur_uid, cur_user)), &mut user_server); start_server(
} Some((desktop.uid.clone(), desktop.username.clone())),
&mut user_server,
);
} }
} else { } else {
force_stop_server(); force_stop_server();
stop_server(&mut user_server); stop_server(&mut user_server);
stop_server(&mut server); stop_server(&mut server);
} }
std::thread::sleep(Duration::from_millis(super::SERVICE_INTERVAL));
let keeps_headless = sid.is_empty() && desktop.is_headless();
let keeps_session = sid == desktop.sid;
if keeps_headless || keeps_session {
// for fixing https://github.com/rustdesk/rustdesk/issues/3129 to avoid too much dbus calling,
sleep_millis(500);
} else {
sleep_millis(super::SERVICE_INTERVAL);
}
if !desktop.is_headless() {
sid = desktop.sid.clone();
}
} }
if let Some(ps) = user_server.take().as_mut() { if let Some(ps) = user_server.take().as_mut() {
@ -384,13 +379,15 @@ pub fn start_os_service() {
log::info!("Exit"); log::info!("Exit");
} }
#[inline]
pub fn get_active_user_id_name() -> (String, String) { pub fn get_active_user_id_name() -> (String, String) {
let vec_id_name = get_values_of_seat0([1, 2].to_vec()); let vec_id_name = get_values_of_seat0(&[1, 2]);
(vec_id_name[0].clone(), vec_id_name[1].clone()) (vec_id_name[0].clone(), vec_id_name[1].clone())
} }
#[inline]
pub fn get_active_userid() -> String { pub fn get_active_userid() -> String {
get_values_of_seat0([1].to_vec())[0].clone() get_values_of_seat0(&[1])[0].clone()
} }
fn get_cm() -> bool { fn get_cm() -> bool {
@ -409,45 +406,6 @@ fn get_cm() -> bool {
false false
} }
fn get_display() -> String {
let user = get_active_username();
log::debug!("w {}", &user);
if let Ok(output) = Command::new("w").arg(&user).output() {
for line in String::from_utf8_lossy(&output.stdout).lines() {
log::debug!(" {}", line);
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) = Command::new("ls")
.args(vec!["-l", "/tmp/.X11-unix/"])
.output()
{
for line in String::from_utf8_lossy(&output.stdout).lines() {
log::debug!(" {}", line);
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
}
pub fn is_login_wayland() -> bool { pub fn is_login_wayland() -> bool {
if let Ok(contents) = std::fs::read_to_string("/etc/gdm3/custom.conf") { if let Ok(contents) = std::fs::read_to_string("/etc/gdm3/custom.conf") {
contents.contains("#WaylandEnable=false") || contents.contains("WaylandEnable=true") contents.contains("#WaylandEnable=false") || contents.contains("WaylandEnable=true")
@ -458,9 +416,9 @@ pub fn is_login_wayland() -> bool {
} }
} }
#[inline]
pub fn current_is_wayland() -> bool { pub fn current_is_wayland() -> bool {
let dtype = get_display_server(); return is_desktop_wayland() && unsafe { UNMODIFIED };
return "wayland" == dtype && unsafe { UNMODIFIED };
} }
// to-do: test the other display manager // to-do: test the other display manager
@ -473,8 +431,9 @@ fn _get_display_manager() -> String {
"gdm3".to_owned() "gdm3".to_owned()
} }
#[inline]
pub fn get_active_username() -> String { pub fn get_active_username() -> String {
get_values_of_seat0([2].to_vec())[0].clone() get_values_of_seat0(&[2])[0].clone()
} }
pub fn get_active_user_home() -> Option<PathBuf> { pub fn get_active_user_home() -> Option<PathBuf> {
@ -488,9 +447,16 @@ pub fn get_active_user_home() -> Option<PathBuf> {
None None
} }
pub fn get_env_var(k: &str) -> String {
match std::env::var(k) {
Ok(v) => v,
Err(_e) => "".to_owned(),
}
}
pub fn is_prelogin() -> bool { pub fn is_prelogin() -> bool {
let n = get_active_userid().len(); let (uid, uname) = get_active_user_id_name();
n < 4 && n > 1 uid.len() >= 4 || uname == "root"
} }
pub fn is_root() -> bool { pub fn is_root() -> bool {
@ -498,7 +464,7 @@ pub fn is_root() -> bool {
} }
fn is_opensuse() -> bool { fn is_opensuse() -> bool {
if let Ok(res) = run_cmds("cat /etc/os-release | grep opensuse".to_owned()) { if let Ok(res) = run_cmds("cat /etc/os-release | grep opensuse") {
if !res.is_empty() { if !res.is_empty() {
return true; return true;
} }
@ -512,6 +478,9 @@ pub fn run_as_user(arg: Vec<&str>, user: Option<(String, String)>) -> ResultType
None => get_active_user_id_name(), None => get_active_user_id_name(),
}; };
let cmd = std::env::current_exe()?; let cmd = std::env::current_exe()?;
if uid.is_empty() {
bail!("No valid uid");
}
let xdg = &format!("XDG_RUNTIME_DIR=/run/user/{}", uid) as &str; let xdg = &format!("XDG_RUNTIME_DIR=/run/user/{}", uid) as &str;
let mut args = vec![xdg, "-u", &username, cmd.to_str().unwrap_or("")]; let mut args = vec![xdg, "-u", &username, cmd.to_str().unwrap_or("")];
args.append(&mut arg.clone()); args.append(&mut arg.clone());
@ -597,21 +566,31 @@ pub fn is_installed() -> bool {
true true
} }
fn get_env_tries(name: &str, uid: &str, n: usize) -> String { pub(super) fn get_env_tries(name: &str, uid: &str, process: &str, n: usize) -> String {
for _ in 0..n { for _ in 0..n {
let x = get_env(name, uid); let x = get_env(name, uid, process);
if !x.is_empty() { if !x.is_empty() {
return x; return x;
} }
std::thread::sleep(Duration::from_millis(300)); sleep_millis(300);
} }
"".to_owned() "".to_owned()
} }
fn get_env(name: &str, uid: &str) -> String { #[inline]
let cmd = format!("ps -u {} -o pid= | xargs -I__ cat /proc/__/environ 2>/dev/null | tr '\\0' '\\n' | grep '^{}=' | tail -1 | sed 's/{}=//g'", uid, name, name); fn get_env(name: &str, uid: &str, process: &str) -> String {
log::debug!("Run: {}", &cmd); let cmd = format!("ps -u {} -f | grep '{}' | grep -v 'grep' | tail -1 | awk '{{print $2}}' | xargs -I__ cat /proc/__/environ 2>/dev/null | tr '\\0' '\\n' | grep '^{}=' | tail -1 | sed 's/{}=//g'", uid, process, name, name);
if let Ok(x) = run_cmds(cmd) { if let Ok(x) = run_cmds(&cmd) {
x.trim_end().to_string()
} else {
"".to_owned()
}
}
#[inline]
fn get_env_from_pid(name: &str, pid: &str) -> String {
let cmd = format!("cat /proc/{}/environ 2>/dev/null | tr '\\0' '\\n' | grep '^{}=' | tail -1 | sed 's/{}=//g'", pid, name, name);
if let Ok(x) = run_cmds(&cmd) {
x.trim_end().to_string() x.trim_end().to_string()
} else { } else {
"".to_owned() "".to_owned()
@ -701,7 +680,7 @@ pub fn resolutions(name: &str) -> Vec<Resolution> {
let connected_pat = get_xrandr_conn_pat(name); let connected_pat = get_xrandr_conn_pat(name);
let mut v = vec![]; let mut v = vec![];
if let Ok(re) = Regex::new(&format!("{}{}", connected_pat, resolutions_pat)) { if let Ok(re) = Regex::new(&format!("{}{}", connected_pat, resolutions_pat)) {
match run_cmds("xrandr --query | tr -s ' '".to_owned()) { match run_cmds("xrandr --query | tr -s ' '") {
Ok(xrandr_output) => { Ok(xrandr_output) => {
// There'are different kinds of xrandr output. // There'are different kinds of xrandr output.
/* /*
@ -750,7 +729,7 @@ pub fn resolutions(name: &str) -> Vec<Resolution> {
} }
pub fn current_resolution(name: &str) -> ResultType<Resolution> { pub fn current_resolution(name: &str) -> ResultType<Resolution> {
let xrandr_output = run_cmds("xrandr --query | tr -s ' '".to_owned())?; let xrandr_output = run_cmds("xrandr --query | tr -s ' '")?;
let re = Regex::new(&get_xrandr_conn_pat(name))?; let re = Regex::new(&get_xrandr_conn_pat(name))?;
if let Some(caps) = re.captures(&xrandr_output) { if let Some(caps) = re.captures(&xrandr_output) {
if let Some((width, height)) = get_width_height_from_captures(&caps) { if let Some((width, height)) = get_width_height_from_captures(&caps) {
@ -775,3 +754,176 @@ pub fn change_resolution(name: &str, width: usize, height: usize) -> ResultType<
.spawn()?; .spawn()?;
Ok(()) Ok(())
} }
mod desktop {
use super::*;
pub const XFCE4_PANEL: &str = "xfce4-panel";
pub const GNOME_SESSION_BINARY: &str = "gnome-session-binary";
#[derive(Debug, Clone, Default)]
pub struct Desktop {
pub sid: String,
pub username: String,
pub uid: String,
pub protocal: String,
pub display: String,
pub xauth: String,
}
impl Desktop {
#[inline]
pub fn is_wayland(&self) -> bool {
self.protocal == DISPLAY_SERVER_WAYLAND
}
#[inline]
pub fn is_login_wayland(&self) -> bool {
super::is_gdm_user(&self.username) && self.protocal == DISPLAY_SERVER_WAYLAND
}
#[inline]
pub fn is_headless(&self) -> bool {
self.sid.is_empty()
}
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_from_xorg(&mut self) {
if let Ok(output) = run_cmds(&format!(
"ps -u {} -f | grep 'Xorg' | grep -v 'grep'",
&self.uid
)) {
for line in output.lines() {
let mut auth_found = false;
for v in line.split_whitespace() {
if v == "-auth" {
auth_found = true;
} else if auth_found {
if std::path::Path::new(v).is_absolute() {
self.xauth = v.to_string();
} else {
if let Some(pid) = line.split_whitespace().nth(1) {
let home_dir = get_env_from_pid("HOME", pid);
if home_dir.is_empty() {
self.xauth = format!("/home/{}/{}", self.username, v);
} else {
self.xauth = format!("{}/{}", home_dir, v);
}
} else {
// unreachable!
}
}
return;
}
}
}
}
}
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);
}
if self.xauth.is_empty() {
self.get_xauth_from_xorg();
}
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)
}
}
};
}
}
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
}
pub fn refresh(&mut self) {
if !self.sid.is_empty() && is_active(&self.sid) {
return;
}
let seat0_values = get_values_of_seat0(&[0, 1, 2]);
if seat0_values[0].is_empty() {
*self = Self::default();
return;
}
self.sid = seat0_values[0].clone();
self.uid = seat0_values[1].clone();
self.username = seat0_values[2].clone();
self.protocal = get_display_server_of_session(&self.sid).into();
if self.is_login_wayland() {
self.display = "".to_owned();
self.xauth = "".to_owned();
return;
}
self.get_display();
self.get_xauth();
}
}
}

View File

@ -883,7 +883,7 @@ impl Connection {
let dtype = crate::platform::linux::get_display_server(); let dtype = crate::platform::linux::get_display_server();
if dtype != "x11" && dtype != "wayland" { if dtype != "x11" && dtype != "wayland" {
res.set_error(format!( res.set_error(format!(
"Unsupported display server type {}, x11 or wayland expected", "Unsupported display server type \"{}\", x11 or wayland expected",
dtype dtype
)); ));
let mut msg_out = Message::new(); let mut msg_out = Message::new();

View File

@ -189,6 +189,17 @@ impl<T: Subscriber + From<ConnInner>> ServiceTmpl<T> {
} }
} }
#[inline]
fn wait_prelogin(&self) {
#[cfg(target_os = "linux")]
while self.active() {
if crate::platform::linux::is_prelogin() {
break;
}
thread::sleep(time::Duration::from_millis(300));
}
}
pub fn repeat<S, F>(&self, interval_ms: u64, callback: F) pub fn repeat<S, F>(&self, interval_ms: u64, callback: F)
where where
F: 'static + FnMut(Self, &mut S) -> ResultType<()> + Send, F: 'static + FnMut(Self, &mut S) -> ResultType<()> + Send,
@ -198,6 +209,8 @@ impl<T: Subscriber + From<ConnInner>> ServiceTmpl<T> {
let mut callback = callback; let mut callback = callback;
let sp = self.clone(); let sp = self.clone();
let thread = thread::spawn(move || { let thread = thread::spawn(move || {
sp.wait_prelogin();
let mut state = S::default(); let mut state = S::default();
let mut may_reset = false; let mut may_reset = false;
while sp.active() { while sp.active() {
@ -232,6 +245,8 @@ impl<T: Subscriber + From<ConnInner>> ServiceTmpl<T> {
let sp = self.clone(); let sp = self.clone();
let mut callback = callback; let mut callback = callback;
let thread = thread::spawn(move || { let thread = thread::spawn(move || {
sp.wait_prelogin();
let mut error_timeout = HIBERNATE_TIMEOUT; let mut error_timeout = HIBERNATE_TIMEOUT;
while sp.active() { while sp.active() {
if sp.has_subscribes() { if sp.has_subscribes() {