Avoid panic on dispatch failure (#23)

Prevent crash when calling unwrap on either sync_roundtrip
or dispatch_pending, and handle such case by replying downstream
that clipboard is dead.
This commit is contained in:
Kirill Chibisov 2020-08-27 14:08:26 +03:00 committed by GitHub
parent 2aea170962
commit 8a81be7c0a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 43 additions and 10 deletions

View file

@ -4,6 +4,7 @@
- Fixed clipboard crashing, when seat has neither keyboard nor pointer focus
- Advertise UTF8_STRING mimetype
- Fixed crash when writing data to the server fails
- Fixed fd leaking from keymap updates
## 0.5.1 -- 2020-07-10

View file

@ -43,10 +43,17 @@ impl Clipboard {
/// Loads content from a clipboard on a last observed seat.
pub fn load(&self) -> Result<String> {
let _ = self.request_sender.send(worker::Command::Load);
self.request_receiver.recv().unwrap()
if let Ok(reply) = self.request_receiver.recv() {
reply
} else {
// The clipboard thread is dead, however we shouldn't crash downstream, so
// propogating an error.
Err(std::io::Error::new(std::io::ErrorKind::Other, "clipboard is dead."))
}
}
/// Store to a clipboard
/// Store to a clipboard.
///
/// Stores to a clipboard on a last observed seat.
pub fn store<T: Into<String>>(&self, text: T) {
@ -59,7 +66,14 @@ impl Clipboard {
/// Loads content from a primary clipboard on a last observed seat.
pub fn load_primary(&self) -> Result<String> {
let _ = self.request_sender.send(worker::Command::LoadPrimary);
self.request_receiver.recv().unwrap()
if let Ok(reply) = self.request_receiver.recv() {
reply
} else {
// The clipboard thread is dead, however we shouldn't crash downstream, so
// propogating an error.
Err(std::io::Error::new(std::io::ErrorKind::Other, "clipboard is dead."))
}
}
/// Store to a primary clipboard.
@ -74,7 +88,7 @@ impl Clipboard {
impl Drop for Clipboard {
fn drop(&mut self) {
// Shutdown smithay-clipboard.
self.request_sender.send(worker::Command::Exit).unwrap();
let _ = self.request_sender.send(worker::Command::Exit);
if let Some(clipboard_thread) = self.clipboard_thread.take() {
let _ = clipboard_thread.join();
}

View file

@ -41,7 +41,12 @@ macro_rules! handle_load {
}
};
$queue.sync_roundtrip(&mut (), |_, _, _| unreachable!()).unwrap();
// If we fail here, it means that we likely won't be able
// to read clipboard anyway, so return and reply to prevent block/crash.
if $queue.sync_roundtrip(&mut (), |_, _, _| unreachable!()).is_err() {
handlers::reply_error(&$tx, "failed to access clipboard.");
return;
};
let mut contents = String::new();
let result = reader.read_to_string(&mut contents).map(|_| {
@ -52,7 +57,7 @@ macro_rules! handle_load {
}
});
$tx.send(result).unwrap();
let _ = $tx.send(result);
});
// Send back that we've failed to load data from the clipboard.
@ -86,7 +91,7 @@ macro_rules! handle_store {
/// Reply an error to a clipboard master.
pub fn reply_error(tx: &Sender<Result<String>>, description: &str) {
tx.send(Err(std::io::Error::new(std::io::ErrorKind::Other, description))).unwrap();
let _ = tx.send(Err(std::io::Error::new(std::io::ErrorKind::Other, description)));
}
/// Update seat and serial on pointer events.

View file

@ -68,7 +68,12 @@ fn worker_impl(display: Display, request_rx: Receiver<Command>, reply_tx: Sender
let env = Environment::init(&display_proxy, SmithayClipboard::new());
let req = queue.sync_roundtrip(&mut (), |_, _, _| unreachable!());
let _ = req.and_then(|_| queue.sync_roundtrip(&mut (), |_, _, _| unreachable!())).unwrap();
let req = req.and_then(|_| queue.sync_roundtrip(&mut (), |_, _, _| unreachable!()));
// We shouldn't crash the application if we've failed to dispatch.
if req.is_err() {
return;
}
// Get data device manager.
let data_device_manager = env.get_global::<WlDataDeviceManager>();
@ -209,7 +214,12 @@ fn worker_impl(display: Display, request_rx: Receiver<Command>, reply_tx: Sender
// Reset the time we're sleeping.
sa_tracker.reset_sleep();
queue.sync_roundtrip(&mut dispatch_data, |_, _, _| unimplemented!()).unwrap();
if queue.sync_roundtrip(&mut dispatch_data, |_, _, _| unimplemented!()).is_err() {
if request == Command::LoadPrimary || request == Command::Load {
handlers::reply_error(&reply_tx, "primary clipboard is not available.");
break;
}
}
// Get latest observed seat and serial.
let (seat, serial) = match dispatch_data.last_seat() {
@ -270,7 +280,10 @@ fn worker_impl(display: Display, request_rx: Receiver<Command>, reply_tx: Sender
}
}
let pending_events = queue.dispatch_pending(&mut dispatch_data, |_, _, _| {}).unwrap();
let pending_events = match queue.dispatch_pending(&mut dispatch_data, |_, _, _| {}) {
Ok(pending_events) => pending_events,
Err(_) => break,
};
// If some application is trying to spam us when there're no seats, it's likely that
// someone is trying to paste from us.