mirror of
https://github.com/rustdesk/rustdesk.git
synced 2025-06-11 04:33:08 +08:00
patch: linux fuse unmount
todo: grosely exit Signed-off-by: ClSlaid <cailue@bupt.edu.cn>
This commit is contained in:
parent
e5bcfeaad5
commit
3a21efbaae
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -937,6 +937,7 @@ dependencies = [
|
|||||||
"hbb_common",
|
"hbb_common",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"libc",
|
"libc",
|
||||||
|
"once_cell",
|
||||||
"parking_lot",
|
"parking_lot",
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
"rand 0.8.5",
|
"rand 0.8.5",
|
||||||
|
@ -434,9 +434,7 @@ Future<List<TToggleMenu>> toolbarDisplayToggle(
|
|||||||
child: Text(translate('Mute'))));
|
child: Text(translate('Mute'))));
|
||||||
}
|
}
|
||||||
// file copy and paste
|
// file copy and paste
|
||||||
if (Platform.isWindows &&
|
if (perms['file'] != false) {
|
||||||
pi.platform == kPeerPlatformWindows &&
|
|
||||||
perms['file'] != false) {
|
|
||||||
final option = 'enable-file-transfer';
|
final option = 'enable-file-transfer';
|
||||||
final value =
|
final value =
|
||||||
bind.sessionGetToggleOptionSync(sessionId: sessionId, arg: option);
|
bind.sessionGetToggleOptionSync(sessionId: sessionId, arg: option);
|
||||||
|
@ -18,6 +18,7 @@ hbb_common = { path = "../hbb_common" }
|
|||||||
parking_lot = {version = "0.12"}
|
parking_lot = {version = "0.12"}
|
||||||
|
|
||||||
[target.'cfg(any(target_os = "linux", target_os = "macos"))'.dependencies]
|
[target.'cfg(any(target_os = "linux", target_os = "macos"))'.dependencies]
|
||||||
|
once_cell = "1.18"
|
||||||
x11rb = {version = "0.12", features = ["all-extensions"]}
|
x11rb = {version = "0.12", features = ["all-extensions"]}
|
||||||
rand = {version = "0.8"}
|
rand = {version = "0.8"}
|
||||||
fuser = {version = "0.13"}
|
fuser = {version = "0.13"}
|
||||||
|
@ -558,9 +558,10 @@ impl FuseServer {
|
|||||||
// get files and directory path right in root of FUSE fs
|
// get files and directory path right in root of FUSE fs
|
||||||
pub fn list_root(&self) -> Vec<PathBuf> {
|
pub fn list_root(&self) -> Vec<PathBuf> {
|
||||||
let files = self.files.read();
|
let files = self.files.read();
|
||||||
let mut paths = Vec::new();
|
let children = &files[0].children;
|
||||||
for file in files.iter().filter(|f| f.parent == Some(FUSE_ROOT_ID)) {
|
let mut paths = Vec::with_capacity(children.len());
|
||||||
paths.push(PathBuf::from(&file.name));
|
for idx in children.iter().copied() {
|
||||||
|
paths.push(PathBuf::from(&files[idx as usize].name));
|
||||||
}
|
}
|
||||||
paths
|
paths
|
||||||
}
|
}
|
||||||
@ -1083,19 +1084,6 @@ struct FuseNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl FuseNode {
|
impl FuseNode {
|
||||||
pub fn new(name: &str, inode: Inode, attributes: InodeAttributes, conn_id: i32) -> Self {
|
|
||||||
Self {
|
|
||||||
conn_id,
|
|
||||||
stream_id: rand::random(),
|
|
||||||
inode,
|
|
||||||
name: name.to_owned(),
|
|
||||||
parent: None,
|
|
||||||
attributes,
|
|
||||||
children: Vec::new(),
|
|
||||||
file_handlers: FileHandles::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn from_description(inode: Inode, desc: FileDescription) -> Self {
|
pub fn from_description(inode: Inode, desc: FileDescription) -> Self {
|
||||||
Self {
|
Self {
|
||||||
conn_id: desc.conn_id,
|
conn_id: desc.conn_id,
|
||||||
@ -1281,10 +1269,15 @@ impl InodeAttributes {
|
|||||||
|
|
||||||
impl From<&InodeAttributes> for fuser::FileAttr {
|
impl From<&InodeAttributes> for fuser::FileAttr {
|
||||||
fn from(value: &InodeAttributes) -> Self {
|
fn from(value: &InodeAttributes) -> Self {
|
||||||
|
let blocks = if value.size % BLOCK_SIZE as u64 == 0 {
|
||||||
|
value.size / BLOCK_SIZE as u64
|
||||||
|
} else {
|
||||||
|
value.size / BLOCK_SIZE as u64 + 1
|
||||||
|
};
|
||||||
Self {
|
Self {
|
||||||
ino: value.inode,
|
ino: value.inode,
|
||||||
size: value.size,
|
size: value.size,
|
||||||
blocks: value.size.div_ceil(BLOCK_SIZE as u64),
|
blocks,
|
||||||
atime: value.last_accessed,
|
atime: value.last_accessed,
|
||||||
mtime: value.last_modified,
|
mtime: value.last_modified,
|
||||||
ctime: value.last_metadata_changed,
|
ctime: value.last_metadata_changed,
|
||||||
|
@ -3,7 +3,10 @@ use std::{
|
|||||||
fs::File,
|
fs::File,
|
||||||
os::unix::prelude::FileExt,
|
os::unix::prelude::FileExt,
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
sync::{atomic::AtomicBool, Arc},
|
sync::{
|
||||||
|
atomic::{AtomicUsize, Ordering},
|
||||||
|
Arc,
|
||||||
|
},
|
||||||
time::{Duration, SystemTime},
|
time::{Duration, SystemTime},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -14,7 +17,7 @@ use hbb_common::{
|
|||||||
log,
|
log,
|
||||||
};
|
};
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use parking_lot::RwLock;
|
use parking_lot::{Mutex, RwLock};
|
||||||
use utf16string::WString;
|
use utf16string::WString;
|
||||||
|
|
||||||
use crate::{send_data, ClipboardFile, CliprdrError, CliprdrServiceContext};
|
use crate::{send_data, ClipboardFile, CliprdrError, CliprdrServiceContext};
|
||||||
@ -44,8 +47,10 @@ fn add_remote_format(local_name: &str, remote_id: i32) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
trait SysClipboard: Send + Sync {
|
trait SysClipboard: Send + Sync {
|
||||||
fn wait_file_list(&self) -> Result<Vec<PathBuf>, CliprdrError>;
|
fn wait_file_list(&self) -> Result<Option<Vec<PathBuf>>, CliprdrError>;
|
||||||
fn set_file_list(&self, paths: &[PathBuf]) -> Result<(), CliprdrError>;
|
fn set_file_list(&self, paths: &[PathBuf]) -> Result<(), CliprdrError>;
|
||||||
|
fn stop(&self);
|
||||||
|
fn start(&self);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_sys_clipboard() -> Result<Box<dyn SysClipboard>, CliprdrError> {
|
fn get_sys_clipboard() -> Result<Box<dyn SysClipboard>, CliprdrError> {
|
||||||
@ -300,6 +305,12 @@ pub struct CliprdrClient {
|
|||||||
pub context: Arc<ClipboardContext>,
|
pub context: Arc<ClipboardContext>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Drop for CliprdrClient {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
self.context.ref_decrease();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl CliprdrServiceContext for CliprdrClient {
|
impl CliprdrServiceContext for CliprdrClient {
|
||||||
fn set_is_stopped(&mut self) -> Result<(), CliprdrError> {
|
fn set_is_stopped(&mut self) -> Result<(), CliprdrError> {
|
||||||
self.context.set_is_stopped()
|
self.context.set_is_stopped()
|
||||||
@ -315,11 +326,12 @@ impl CliprdrServiceContext for CliprdrClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct ClipboardContext {
|
pub struct ClipboardContext {
|
||||||
pub stop: AtomicBool,
|
pub client_count: AtomicUsize,
|
||||||
pub fuse_mount_point: PathBuf,
|
pub fuse_mount_point: PathBuf,
|
||||||
fuse_server: Arc<FuseServer>,
|
fuse_server: Arc<FuseServer>,
|
||||||
file_list: RwLock<Vec<LocalFile>>,
|
file_list: RwLock<Vec<LocalFile>>,
|
||||||
clipboard: Arc<dyn SysClipboard>,
|
clipboard: Arc<dyn SysClipboard>,
|
||||||
|
fuse_handle: Mutex<Option<fuser::BackgroundSession>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ClipboardContext {
|
impl ClipboardContext {
|
||||||
@ -331,53 +343,97 @@ impl ClipboardContext {
|
|||||||
})?;
|
})?;
|
||||||
|
|
||||||
let fuse_server = Arc::new(FuseServer::new(timeout));
|
let fuse_server = Arc::new(FuseServer::new(timeout));
|
||||||
|
|
||||||
let clipboard = get_sys_clipboard()?;
|
let clipboard = get_sys_clipboard()?;
|
||||||
let clipboard = Arc::from(clipboard);
|
let clipboard = Arc::from(clipboard);
|
||||||
let file_list = RwLock::new(vec![]);
|
let file_list = RwLock::new(vec![]);
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
stop: AtomicBool::new(false),
|
client_count: AtomicUsize::new(0),
|
||||||
fuse_mount_point,
|
fuse_mount_point,
|
||||||
fuse_server,
|
fuse_server,
|
||||||
|
fuse_handle: Mutex::new(None),
|
||||||
file_list,
|
file_list,
|
||||||
clipboard,
|
clipboard,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn client(self: Arc<Self>) -> CliprdrClient {
|
pub fn client(self: Arc<Self>) -> Result<CliprdrClient, CliprdrError> {
|
||||||
CliprdrClient { context: self }
|
if self.client_count.fetch_add(1, Ordering::Relaxed) == 0 {
|
||||||
|
let mut fuse_handle = self.fuse_handle.lock();
|
||||||
|
|
||||||
|
if fuse_handle.is_none() {
|
||||||
|
let mount_path = &self.fuse_mount_point;
|
||||||
|
create_if_not_exists(mount_path);
|
||||||
|
|
||||||
|
let mnt_opts = [
|
||||||
|
MountOption::FSName("rustdesk-cliprdr-fs".to_string()),
|
||||||
|
MountOption::RO,
|
||||||
|
MountOption::NoAtime,
|
||||||
|
];
|
||||||
|
log::info!(
|
||||||
|
"mounting clipboard FUSE to {}",
|
||||||
|
self.fuse_mount_point.display()
|
||||||
|
);
|
||||||
|
|
||||||
|
let new_handle =
|
||||||
|
fuser::spawn_mount2(self.fuse_server.client(), mount_path, &mnt_opts).map_err(
|
||||||
|
|e| {
|
||||||
|
log::error!("failed to mount cliprdr fuse: {:?}", e);
|
||||||
|
CliprdrError::CliprdrInit
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
*fuse_handle = Some(new_handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.clipboard.start();
|
||||||
|
let clip = self.clone();
|
||||||
|
std::thread::spawn(move || {
|
||||||
|
let res = clip.listen_clipboard();
|
||||||
|
if let Err(e) = res {
|
||||||
|
log::error!("failed to listen clipboard: {:?}", e);
|
||||||
|
}
|
||||||
|
log::info!("stopped listening clipboard");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(CliprdrClient { context: self })
|
||||||
}
|
}
|
||||||
|
|
||||||
// mount and run fuse server, blocking
|
/// set context to be inactive
|
||||||
pub fn mount(&self) -> Result<(), CliprdrError> {
|
pub fn ref_decrease(&self) {
|
||||||
let mount_opts = [
|
if self.client_count.fetch_sub(1, Ordering::Relaxed) > 1 {
|
||||||
MountOption::FSName("rustdesk-cliprdr-fs".to_string()),
|
return;
|
||||||
MountOption::RO,
|
}
|
||||||
MountOption::NoAtime,
|
|
||||||
];
|
let mut fuse_handle = self.fuse_handle.lock();
|
||||||
let fuse_client = self.fuse_server.client();
|
if let Some(fuse_handle) = fuse_handle.take() {
|
||||||
fuser::mount2(fuse_client, self.fuse_mount_point.clone(), &mount_opts).map_err(|e| {
|
fuse_handle.join();
|
||||||
log::error!("failed to mount fuse: {:?}", e);
|
}
|
||||||
CliprdrError::CliprdrInit
|
self.clipboard.stop();
|
||||||
})
|
std::fs::remove_dir(&self.fuse_mount_point).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// set clipboard data from file list
|
/// set clipboard data from file list
|
||||||
pub fn set_clipboard(&self, paths: &[PathBuf]) -> Result<(), CliprdrError> {
|
pub fn set_clipboard(&self, paths: &[PathBuf]) -> Result<(), CliprdrError> {
|
||||||
let prefix = self.fuse_mount_point.clone();
|
let prefix = self.fuse_mount_point.clone();
|
||||||
let paths: Vec<PathBuf> = paths.iter().cloned().map(|p| prefix.join(p)).collect();
|
let paths: Vec<PathBuf> = paths.iter().cloned().map(|p| prefix.join(p)).collect();
|
||||||
|
log::debug!("setting clipboard with paths: {:?}", paths);
|
||||||
self.clipboard.set_file_list(&paths)
|
self.clipboard.set_file_list(&paths)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn listen_clipboard(&self) -> Result<(), CliprdrError> {
|
pub fn listen_clipboard(&self) -> Result<(), CliprdrError> {
|
||||||
while let Ok(v) = self.clipboard.wait_file_list() {
|
log::debug!("start listening clipboard");
|
||||||
|
while let Some(v) = self.clipboard.wait_file_list()? {
|
||||||
let filtered: Vec<_> = v
|
let filtered: Vec<_> = v
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter(|pb| !pb.starts_with(&self.fuse_mount_point))
|
.filter(|pb| !pb.starts_with(&self.fuse_mount_point))
|
||||||
.collect();
|
.collect();
|
||||||
|
log::debug!("clipboard file list update (filtered): {:?}", filtered);
|
||||||
if filtered.is_empty() {
|
if filtered.is_empty() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
log::debug!("send file list update to remote");
|
||||||
|
|
||||||
// construct format list update and send
|
// construct format list update and send
|
||||||
let data = ClipboardFile::FormatList {
|
let data = ClipboardFile::FormatList {
|
||||||
@ -390,18 +446,27 @@ impl ClipboardContext {
|
|||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
send_data(0, data)
|
send_data(0, data);
|
||||||
|
log::debug!("format list update sent");
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn send_format_list(&self, conn_id: i32) -> Result<(), CliprdrError> {
|
fn send_format_list(&self, conn_id: i32) -> Result<(), CliprdrError> {
|
||||||
|
log::debug!("send format list to remote, conn={}", conn_id);
|
||||||
let data = self.clipboard.wait_file_list()?;
|
let data = self.clipboard.wait_file_list()?;
|
||||||
|
if data.is_none() {
|
||||||
|
log::debug!("clipboard disabled, skip sending");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
let data = data.unwrap();
|
||||||
|
|
||||||
let filtered: Vec<_> = data
|
let filtered: Vec<_> = data
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter(|pb| !pb.starts_with(&self.fuse_mount_point))
|
.filter(|pb| !pb.starts_with(&self.fuse_mount_point))
|
||||||
.collect();
|
.collect();
|
||||||
if filtered.is_empty() {
|
if filtered.is_empty() {
|
||||||
|
log::debug!("no files in format list, skip sending");
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -416,11 +481,19 @@ impl ClipboardContext {
|
|||||||
};
|
};
|
||||||
|
|
||||||
send_data(conn_id, format_list);
|
send_data(conn_id, format_list);
|
||||||
|
log::debug!("format list to remote dispatched, conn={}", conn_id);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn send_file_list(&self, conn_id: i32) -> Result<(), CliprdrError> {
|
fn send_file_list(&self, conn_id: i32) -> Result<(), CliprdrError> {
|
||||||
|
log::debug!("send file list to remote, conn={}", conn_id);
|
||||||
let data = self.clipboard.wait_file_list()?;
|
let data = self.clipboard.wait_file_list()?;
|
||||||
|
if data.is_none() {
|
||||||
|
log::debug!("clipboard disabled, skip sending");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let data = data.unwrap();
|
||||||
let filtered: Vec<_> = data
|
let filtered: Vec<_> = data
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter(|pb| !pb.starts_with(&self.fuse_mount_point))
|
.filter(|pb| !pb.starts_with(&self.fuse_mount_point))
|
||||||
@ -576,6 +649,13 @@ fn resp_file_contents_fail(conn_id: i32, stream_id: i32) {
|
|||||||
send_data(conn_id, resp)
|
send_data(conn_id, resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn create_if_not_exists(path: &PathBuf) {
|
||||||
|
if std::fs::metadata(path).is_ok() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
std::fs::create_dir(path).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
impl ClipboardContext {
|
impl ClipboardContext {
|
||||||
pub fn set_is_stopped(&self) -> Result<(), CliprdrError> {
|
pub fn set_is_stopped(&self) -> Result<(), CliprdrError> {
|
||||||
// do nothing
|
// do nothing
|
||||||
|
@ -1,5 +1,9 @@
|
|||||||
use std::path::PathBuf;
|
use std::{
|
||||||
|
path::PathBuf,
|
||||||
|
sync::atomic::{AtomicBool, Ordering},
|
||||||
|
};
|
||||||
|
|
||||||
|
use once_cell::sync::OnceCell;
|
||||||
use x11_clipboard::Clipboard;
|
use x11_clipboard::Clipboard;
|
||||||
use x11rb::protocol::xproto::Atom;
|
use x11rb::protocol::xproto::Atom;
|
||||||
|
|
||||||
@ -7,15 +11,21 @@ use crate::CliprdrError;
|
|||||||
|
|
||||||
use super::{encode_path_to_uri, parse_plain_uri_list, SysClipboard};
|
use super::{encode_path_to_uri, parse_plain_uri_list, SysClipboard};
|
||||||
|
|
||||||
|
static X11_CLIPBOARD: OnceCell<Clipboard> = OnceCell::new();
|
||||||
|
|
||||||
|
fn get_clip() -> Result<&'static Clipboard, CliprdrError> {
|
||||||
|
X11_CLIPBOARD.get_or_try_init(|| Clipboard::new().map_err(|_| CliprdrError::CliprdrInit))
|
||||||
|
}
|
||||||
|
|
||||||
pub struct X11Clipboard {
|
pub struct X11Clipboard {
|
||||||
|
stop: AtomicBool,
|
||||||
text_uri_list: Atom,
|
text_uri_list: Atom,
|
||||||
gnome_copied_files: Atom,
|
gnome_copied_files: Atom,
|
||||||
clipboard: Clipboard,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl X11Clipboard {
|
impl X11Clipboard {
|
||||||
pub fn new() -> Result<Self, CliprdrError> {
|
pub fn new() -> Result<Self, CliprdrError> {
|
||||||
let clipboard = Clipboard::new().map_err(|_| CliprdrError::CliprdrInit)?;
|
let clipboard = get_clip()?;
|
||||||
let text_uri_list = clipboard
|
let text_uri_list = clipboard
|
||||||
.setter
|
.setter
|
||||||
.get_atom("text/uri-list")
|
.get_atom("text/uri-list")
|
||||||
@ -25,33 +35,37 @@ impl X11Clipboard {
|
|||||||
.get_atom("x-special/gnome-copied-files")
|
.get_atom("x-special/gnome-copied-files")
|
||||||
.map_err(|_| CliprdrError::CliprdrInit)?;
|
.map_err(|_| CliprdrError::CliprdrInit)?;
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
|
stop: AtomicBool::new(false),
|
||||||
text_uri_list,
|
text_uri_list,
|
||||||
gnome_copied_files,
|
gnome_copied_files,
|
||||||
clipboard,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load(&self, target: Atom) -> Result<Vec<u8>, CliprdrError> {
|
fn load(&self, target: Atom) -> Result<Vec<u8>, CliprdrError> {
|
||||||
let clip = self.clipboard.setter.atoms.clipboard;
|
let clip = get_clip()?.setter.atoms.clipboard;
|
||||||
let prop = self.clipboard.setter.atoms.property;
|
let prop = get_clip()?.setter.atoms.property;
|
||||||
self.clipboard
|
get_clip()?
|
||||||
.load_wait(clip, target, prop)
|
.load_wait(clip, target, prop)
|
||||||
.map_err(|_| CliprdrError::ConversionFailure)
|
.map_err(|_| CliprdrError::ConversionFailure)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn store_batch(&self, batch: Vec<(Atom, Vec<u8>)>) -> Result<(), CliprdrError> {
|
fn store_batch(&self, batch: Vec<(Atom, Vec<u8>)>) -> Result<(), CliprdrError> {
|
||||||
let clip = self.clipboard.setter.atoms.clipboard;
|
let clip = get_clip()?.setter.atoms.clipboard;
|
||||||
self.clipboard
|
get_clip()?
|
||||||
.store_batch(clip, batch)
|
.store_batch(clip, batch)
|
||||||
.map_err(|_| CliprdrError::ClipboardInternalError)
|
.map_err(|_| CliprdrError::ClipboardInternalError)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SysClipboard for X11Clipboard {
|
impl SysClipboard for X11Clipboard {
|
||||||
fn wait_file_list(&self) -> Result<Vec<PathBuf>, CliprdrError> {
|
fn wait_file_list(&self) -> Result<Option<Vec<PathBuf>>, CliprdrError> {
|
||||||
|
if self.stop.load(Ordering::Relaxed) {
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
let v = self.load(self.text_uri_list)?;
|
let v = self.load(self.text_uri_list)?;
|
||||||
// loading 'text/uri-list' should be enough?
|
// loading 'text/uri-list' should be enough?
|
||||||
parse_plain_uri_list(v)
|
let p = parse_plain_uri_list(v)?;
|
||||||
|
Ok(Some(p))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_file_list(&self, paths: &[PathBuf]) -> Result<(), CliprdrError> {
|
fn set_file_list(&self, paths: &[PathBuf]) -> Result<(), CliprdrError> {
|
||||||
@ -66,4 +80,12 @@ impl SysClipboard for X11Clipboard {
|
|||||||
self.store_batch(batch)
|
self.store_batch(batch)
|
||||||
.map_err(|_| CliprdrError::ClipboardInternalError)
|
.map_err(|_| CliprdrError::ClipboardInternalError)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn stop(&self) {
|
||||||
|
self.stop.store(true, Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn start(&self) {
|
||||||
|
self.stop.store(false, Ordering::Relaxed);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,8 @@ pub fn create_cliprdr_context(
|
|||||||
) -> crate::ResultType<Box<dyn crate::CliprdrServiceContext>> {
|
) -> crate::ResultType<Box<dyn crate::CliprdrServiceContext>> {
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use hbb_common::{anyhow, log};
|
||||||
|
|
||||||
if !enable_files {
|
if !enable_files {
|
||||||
return Ok(Box::new(DummyCliprdrContext {}) as Box<_>);
|
return Ok(Box::new(DummyCliprdrContext {}) as Box<_>);
|
||||||
}
|
}
|
||||||
@ -34,16 +36,26 @@ pub fn create_cliprdr_context(
|
|||||||
let timeout = std::time::Duration::from_secs(response_wait_timeout_secs as u64);
|
let timeout = std::time::Duration::from_secs(response_wait_timeout_secs as u64);
|
||||||
let mut tmp_path = std::env::temp_dir();
|
let mut tmp_path = std::env::temp_dir();
|
||||||
tmp_path.push("rustdesk-cliprdr");
|
tmp_path.push("rustdesk-cliprdr");
|
||||||
let rd_mnt = tmp_path;
|
|
||||||
std::fs::create_dir(rd_mnt.clone())?;
|
log::info!("check mount point existence");
|
||||||
|
let rd_mnt = if !tmp_path.exists() {
|
||||||
|
log::info!("create mount point: {}", tmp_path.display());
|
||||||
|
std::fs::create_dir_all(tmp_path.clone())?;
|
||||||
|
tmp_path
|
||||||
|
} else if !tmp_path.is_dir() {
|
||||||
|
log::error!("{} is occupied and is not a directory", tmp_path.display());
|
||||||
|
return Err(CliprdrError::CliprdrInit.into());
|
||||||
|
} else {
|
||||||
|
tmp_path
|
||||||
|
};
|
||||||
|
|
||||||
let linux_ctx = Arc::new(linux::ClipboardContext::new(timeout, rd_mnt)?);
|
let linux_ctx = Arc::new(linux::ClipboardContext::new(timeout, rd_mnt)?);
|
||||||
|
let client = linux_ctx.client().map_err(|e| {
|
||||||
|
log::error!("create clipboard client: {:?}", e);
|
||||||
|
anyhow::anyhow!("create clipboard client: {:?}", e)
|
||||||
|
})?;
|
||||||
|
|
||||||
let fuse_ctx = linux_ctx.clone();
|
Ok(Box::new(client) as Box<_>)
|
||||||
std::thread::spawn(move || fuse_ctx.mount());
|
|
||||||
let clipboard_listen_ctx = linux_ctx.clone();
|
|
||||||
std::thread::spawn(move || clipboard_listen_ctx.listen_clipboard());
|
|
||||||
|
|
||||||
Ok(Box::new(linux_ctx.client()) as Box<_>)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct DummyCliprdrContext {}
|
struct DummyCliprdrContext {}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use super::{input_service::*, *};
|
use super::{input_service::*, *};
|
||||||
#[cfg(windows)]
|
#[cfg(any(target_os = "windows", target_os = "linux"))]
|
||||||
use crate::clipboard_file::*;
|
use crate::clipboard_file::*;
|
||||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||||
use crate::common::update_clipboard;
|
use crate::common::update_clipboard;
|
||||||
@ -175,7 +175,7 @@ pub struct Connection {
|
|||||||
// by peer
|
// by peer
|
||||||
disable_audio: bool,
|
disable_audio: bool,
|
||||||
// by peer
|
// by peer
|
||||||
#[cfg(windows)]
|
#[cfg(any(target_os = "windows", target_os = "linux"))]
|
||||||
enable_file_transfer: bool,
|
enable_file_transfer: bool,
|
||||||
// by peer
|
// by peer
|
||||||
audio_sender: Option<MediaSender>,
|
audio_sender: Option<MediaSender>,
|
||||||
@ -310,7 +310,7 @@ impl Connection {
|
|||||||
show_remote_cursor: false,
|
show_remote_cursor: false,
|
||||||
ip: "".to_owned(),
|
ip: "".to_owned(),
|
||||||
disable_audio: false,
|
disable_audio: false,
|
||||||
#[cfg(windows)]
|
#[cfg(any(target_os = "windows", target_os = "linux"))]
|
||||||
enable_file_transfer: false,
|
enable_file_transfer: false,
|
||||||
disable_clipboard: false,
|
disable_clipboard: false,
|
||||||
disable_keyboard: false,
|
disable_keyboard: false,
|
||||||
@ -457,7 +457,7 @@ impl Connection {
|
|||||||
ipc::Data::RawMessage(bytes) => {
|
ipc::Data::RawMessage(bytes) => {
|
||||||
allow_err!(conn.stream.send_raw(bytes).await);
|
allow_err!(conn.stream.send_raw(bytes).await);
|
||||||
}
|
}
|
||||||
#[cfg(windows)]
|
#[cfg(any(target_os="windows", target_os="linux"))]
|
||||||
ipc::Data::ClipboardFile(clip) => {
|
ipc::Data::ClipboardFile(clip) => {
|
||||||
allow_err!(conn.stream.send(&clip_2_msg(clip)).await);
|
allow_err!(conn.stream.send(&clip_2_msg(clip)).await);
|
||||||
}
|
}
|
||||||
@ -1156,7 +1156,7 @@ impl Connection {
|
|||||||
self.audio && !self.disable_audio
|
self.audio && !self.disable_audio
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(any(target_os = "windows", target_os = "linux"))]
|
||||||
fn file_transfer_enabled(&self) -> bool {
|
fn file_transfer_enabled(&self) -> bool {
|
||||||
self.file && self.enable_file_transfer
|
self.file && self.enable_file_transfer
|
||||||
}
|
}
|
||||||
@ -1706,7 +1706,7 @@ impl Connection {
|
|||||||
}
|
}
|
||||||
Some(message::Union::Cliprdr(_clip)) =>
|
Some(message::Union::Cliprdr(_clip)) =>
|
||||||
{
|
{
|
||||||
#[cfg(windows)]
|
#[cfg(any(target_os = "windows", target_os = "linux"))]
|
||||||
if let Some(clip) = msg_2_clip(_clip) {
|
if let Some(clip) = msg_2_clip(_clip) {
|
||||||
self.send_to_cm(ipc::Data::ClipboardFile(clip))
|
self.send_to_cm(ipc::Data::ClipboardFile(clip))
|
||||||
}
|
}
|
||||||
@ -2156,7 +2156,7 @@ impl Connection {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[cfg(windows)]
|
#[cfg(any(target_os = "windows", target_os = "linux"))]
|
||||||
if let Ok(q) = o.enable_file_transfer.enum_value() {
|
if let Ok(q) = o.enable_file_transfer.enum_value() {
|
||||||
if q != BoolOption::NotSet {
|
if q != BoolOption::NotSet {
|
||||||
self.enable_file_transfer = q == BoolOption::Yes;
|
self.enable_file_transfer = q == BoolOption::Yes;
|
||||||
|
@ -570,7 +570,7 @@ pub async fn start_ipc<T: InvokeUiCM>(cm: ConnectionManager<T>) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(any(target_os = "windows", target_os = "linux"))]
|
||||||
ContextSend::enable(Config::get_option("enable-file-transfer").is_empty());
|
ContextSend::enable(Config::get_option("enable-file-transfer").is_empty());
|
||||||
|
|
||||||
match ipc::new_listener("_cm").await {
|
match ipc::new_listener("_cm").await {
|
||||||
|
@ -594,7 +594,13 @@ pub fn current_is_wayland() -> bool {
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_new_version() -> String {
|
pub fn get_new_version() -> String {
|
||||||
(*SOFTWARE_UPDATE_URL.lock().unwrap().rsplit('/').next().unwrap_or("")).to_string()
|
(*SOFTWARE_UPDATE_URL
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.rsplit('/')
|
||||||
|
.next()
|
||||||
|
.unwrap_or(""))
|
||||||
|
.to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
@ -999,7 +1005,7 @@ async fn check_connect_status_(reconnect: bool, rx: mpsc::UnboundedReceiver<ipc:
|
|||||||
let mut mouse_time = 0;
|
let mut mouse_time = 0;
|
||||||
#[cfg(not(feature = "flutter"))]
|
#[cfg(not(feature = "flutter"))]
|
||||||
let mut id = "".to_owned();
|
let mut id = "".to_owned();
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(any(target_os = "windows", target_os = "linux"))]
|
||||||
let mut enable_file_transfer = "".to_owned();
|
let mut enable_file_transfer = "".to_owned();
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
@ -1022,7 +1028,7 @@ async fn check_connect_status_(reconnect: bool, rx: mpsc::UnboundedReceiver<ipc:
|
|||||||
*OPTIONS.lock().unwrap() = v;
|
*OPTIONS.lock().unwrap() = v;
|
||||||
*OPTION_SYNCED.lock().unwrap() = true;
|
*OPTION_SYNCED.lock().unwrap() = true;
|
||||||
|
|
||||||
#[cfg(target_os="windows")]
|
#[cfg(any(target_os="windows", target_os="linux"))]
|
||||||
{
|
{
|
||||||
let b = OPTIONS.lock().unwrap().get("enable-file-transfer").map(|x| x.to_string()).unwrap_or_default();
|
let b = OPTIONS.lock().unwrap().get("enable-file-transfer").map(|x| x.to_string()).unwrap_or_default();
|
||||||
if b != enable_file_transfer {
|
if b != enable_file_transfer {
|
||||||
|
@ -1254,7 +1254,7 @@ impl<T: InvokeUiSession> Session<T> {
|
|||||||
#[tokio::main(flavor = "current_thread")]
|
#[tokio::main(flavor = "current_thread")]
|
||||||
pub async fn io_loop<T: InvokeUiSession>(handler: Session<T>) {
|
pub async fn io_loop<T: InvokeUiSession>(handler: Session<T>) {
|
||||||
// It is ok to call this function multiple times.
|
// It is ok to call this function multiple times.
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(any(target_os = "windows", target_os = "linux"))]
|
||||||
if !handler.is_file_transfer() && !handler.is_port_forward() {
|
if !handler.is_file_transfer() && !handler.is_port_forward() {
|
||||||
clipboard::ContextSend::enable(true);
|
clipboard::ContextSend::enable(true);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user