YaLTeR / wl-clipboard-rs

A safe Rust crate for working with the Wayland clipboard.
Apache License 2.0
223 stars 16 forks source link

Error when pasting: `cat: write error: Broken pipe` #43

Closed AnyTimeTraveler closed 1 month ago

AnyTimeTraveler commented 3 months ago

I am executing this code:

fn main() {
    let x = "Hello world!".to_string().into_bytes().into();

    paste(x);
}

fn paste(x: Box<[u8]>) {
    let mut opts = Options::new();
    opts.foreground(true);
    opts.seat(All);
    opts.serve_requests(ServeRequests::Unlimited);
    let copy = opts.prepare_copy(Source::Bytes(x), MimeType::Autodetect).unwrap();

    copy.serve().unwrap();
}

With WAYLAND_DEBUG=1, I am getting this output:

/home/simon/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/bin/cargo run --color=always --package wl_clipboard_manager --bin wl_clipboard_manager
    Finished dev [unoptimized + debuginfo] target(s) in 0.03s
     Running `target/debug/wl_clipboard_manager`
[1947965.940][rs] -> wl_display@1.get_registry(wl_registry@2)
[1947965.963][rs] -> wl_display@1.sync(wl_callback@3)
[1947966.038][rs] <- wl_registry@2.global, (1, Some("wl_shm"), 1)
[1947966.075][rs] <- wl_registry@2.global, (2, Some("wl_drm"), 2)
[1947966.096][rs] <- wl_registry@2.global, (3, Some("zwp_linux_dmabuf_v1"), 4)
[1947966.113][rs] <- wl_registry@2.global, (4, Some("wl_compositor"), 5)
[1947966.130][rs] <- wl_registry@2.global, (5, Some("wl_subcompositor"), 1)
[1947966.147][rs] <- wl_registry@2.global, (6, Some("wl_data_device_manager"), 3)
[1947966.163][rs] <- wl_registry@2.global, (7, Some("zwlr_gamma_control_manager_v1"), 1)
[1947966.179][rs] <- wl_registry@2.global, (8, Some("zxdg_output_manager_v1"), 3)
[1947966.195][rs] <- wl_registry@2.global, (9, Some("org_kde_kwin_idle"), 1)
[1947966.211][rs] <- wl_registry@2.global, (10, Some("ext_idle_notifier_v1"), 1)
[1947966.228][rs] <- wl_registry@2.global, (11, Some("zwp_idle_inhibit_manager_v1"), 1)
[1947966.244][rs] <- wl_registry@2.global, (12, Some("zwlr_layer_shell_v1"), 4)
[1947966.260][rs] <- wl_registry@2.global, (13, Some("xdg_wm_base"), 2)
[1947966.276][rs] <- wl_registry@2.global, (14, Some("zwp_tablet_manager_v2"), 1)
[1947966.293][rs] <- wl_registry@2.global, (15, Some("org_kde_kwin_server_decoration_manager"), 1)
[1947966.309][rs] <- wl_registry@2.global, (16, Some("zxdg_decoration_manager_v1"), 1)
[1947966.325][rs] <- wl_registry@2.global, (17, Some("zwp_relative_pointer_manager_v1"), 1)
[1947966.342][rs] <- wl_registry@2.global, (18, Some("zwp_pointer_constraints_v1"), 1)
[1947966.358][rs] <- wl_registry@2.global, (19, Some("wp_presentation"), 1)
[1947966.374][rs] <- wl_registry@2.global, (20, Some("zwlr_output_manager_v1"), 4)
[1947966.399][rs] <- wl_registry@2.global, (21, Some("zwlr_output_power_manager_v1"), 1)
[1947966.416][rs] <- wl_registry@2.global, (22, Some("zwp_input_method_manager_v2"), 1)
[1947966.432][rs] <- wl_registry@2.global, (23, Some("zwp_text_input_manager_v3"), 1)
[1947966.448][rs] <- wl_registry@2.global, (24, Some("zwlr_foreign_toplevel_manager_v1"), 3)
[1947966.464][rs] <- wl_registry@2.global, (25, Some("ext_session_lock_manager_v1"), 1)
[1947966.480][rs] <- wl_registry@2.global, (26, Some("wp_drm_lease_device_v1"), 1)
[1947966.496][rs] <- wl_registry@2.global, (27, Some("zwlr_export_dmabuf_manager_v1"), 1)
[1947966.511][rs] <- wl_registry@2.global, (28, Some("zwlr_screencopy_manager_v1"), 3)
[1947966.525][rs] <- wl_registry@2.global, (29, Some("zwlr_data_control_manager_v1"), 2)
[1947966.540][rs] <- wl_registry@2.global, (30, Some("zwp_primary_selection_device_manager_v1"), 1)
[1947966.555][rs] <- wl_registry@2.global, (31, Some("wp_viewporter"), 1)
[1947966.569][rs] <- wl_registry@2.global, (32, Some("wp_single_pixel_buffer_manager_v1"), 1)
[1947966.584][rs] <- wl_registry@2.global, (33, Some("zxdg_exporter_v1"), 1)
[1947966.601][rs] <- wl_registry@2.global, (34, Some("zxdg_importer_v1"), 1)
[1947966.617][rs] <- wl_registry@2.global, (35, Some("zxdg_exporter_v2"), 1)
[1947966.632][rs] <- wl_registry@2.global, (36, Some("zxdg_importer_v2"), 1)
[1947966.646][rs] <- wl_registry@2.global, (37, Some("xdg_activation_v1"), 1)
[1947966.661][rs] <- wl_registry@2.global, (38, Some("zwp_virtual_keyboard_manager_v1"), 1)
[1947966.676][rs] <- wl_registry@2.global, (39, Some("zwlr_virtual_pointer_manager_v1"), 2)
[1947966.691][rs] <- wl_registry@2.global, (40, Some("zwlr_input_inhibit_manager_v1"), 1)
[1947966.706][rs] <- wl_registry@2.global, (41, Some("zwp_keyboard_shortcuts_inhibit_manager_v1"), 1)
[1947966.721][rs] <- wl_registry@2.global, (42, Some("wl_seat"), 8)
[1947966.736][rs] <- wl_registry@2.global, (43, Some("zwp_pointer_gestures_v1"), 3)
[1947966.750][rs] <- wl_registry@2.global, (44, Some("wl_output"), 4)
[1947966.765][rs] <- wl_registry@2.global, (46, Some("wl_output"), 4)
[1947966.779][rs] <- wl_callback@3.done, (101594)
[1947966.789][rs] <- wl_display@1.delete_id, (3)
[1947966.813][rs] -> wl_registry@2.bind(29, Some("zwlr_data_control_manager_v1"), 1, zwlr_data_control_manager_v1@3)
[1947966.838][rs] -> wl_registry@2.bind(42, Some("wl_seat"), 2, wl_seat@4)
[1947966.881][rs] -> zwlr_data_control_manager_v1@3.get_data_device(zwlr_data_control_device_v1@5, wl_seat@4)
[1947966.898][rs] -> wl_display@1.sync(wl_callback@6)
[1947966.946][rs] <- wl_seat@4.name, (Some("seat0"))
[1947966.961][rs] <- wl_seat@4.capabilities, (3)
[1947966.971][rs] <- zwlr_data_control_device_v1@5.data_offer, (zwlr_data_control_offer_v1@4278190080)
[1947966.982][rs] <- zwlr_data_control_offer_v1@4278190080.offer, (Some("application/glfw+clipboard-2039019"))
[1947966.994][rs] <- zwlr_data_control_offer_v1@4278190080.offer, (Some("TEXT"))
[1947967.008][rs] <- zwlr_data_control_offer_v1@4278190080.offer, (Some("STRING"))
[1947967.020][rs] <- zwlr_data_control_offer_v1@4278190080.offer, (Some("UTF8_STRING"))
[1947967.032][rs] <- zwlr_data_control_offer_v1@4278190080.offer, (Some("text/plain;charset=utf-8"))
[1947967.044][rs] <- zwlr_data_control_offer_v1@4278190080.offer, (Some("text/plain"))
[1947967.056][rs] <- zwlr_data_control_device_v1@5.selection, (zwlr_data_control_offer_v1@4278190080)
[1947967.067][rs] <- wl_callback@6.done, (101594)
[1947967.076][rs] <- wl_display@1.delete_id, (6)
[1947967.098][rs] -> zwlr_data_control_offer_v1@4278190080.destroy()
[1947967.283][rs] -> zwlr_data_control_manager_v1@3.create_data_source(zwlr_data_control_source_v1@6)
[1947967.300][rs] -> zwlr_data_control_source_v1@6.offer(Some("TEXT"))
[1947967.314][rs] -> zwlr_data_control_source_v1@6.offer(Some("UTF8_STRING"))
[1947967.326][rs] -> zwlr_data_control_source_v1@6.offer(Some("text/plain"))
[1947967.338][rs] -> zwlr_data_control_source_v1@6.offer(Some("STRING"))
[1947967.350][rs] -> zwlr_data_control_source_v1@6.offer(Some("text/plain;charset=utf-8"))
[1947967.362][rs] -> zwlr_data_control_device_v1@5.set_selection(zwlr_data_control_source_v1@6)
[1947967.442][rs] <- zwlr_data_control_device_v1@5.data_offer, (zwlr_data_control_offer_v1@4278190080)
[1947967.456][rs] <- zwlr_data_control_offer_v1@4278190080.offer, (Some("TEXT"))
[1947967.470][rs] <- zwlr_data_control_offer_v1@4278190080.offer, (Some("UTF8_STRING"))
[1947967.484][rs] <- zwlr_data_control_offer_v1@4278190080.offer, (Some("text/plain"))
[1947967.497][rs] <- zwlr_data_control_offer_v1@4278190080.offer, (Some("STRING"))
[1947967.511][rs] <- zwlr_data_control_offer_v1@4278190080.offer, (Some("text/plain;charset=utf-8"))
[1947967.526][rs] <- zwlr_data_control_device_v1@5.selection, (zwlr_data_control_offer_v1@4278190080)
[1947967.544][rs] <- zwlr_data_control_source_v1@6.send, (Some("text/plain;charset=utf-8"), 4)
[1947967.564][rs] -> zwlr_data_control_offer_v1@4278190080.destroy()
cat: write error: Broken pipe
[1947970.788][rs] -> zwlr_data_control_source_v1@6.destroy()
thread 'main' panicked at src/main.rs:73:18:
called `Result::unwrap()` on an `Err` value: Paste(Copy(ChildError(1)))
stack backtrace:
   0: rust_begin_unwind
             at /rustc/7cf61ebde7b22796c69757901dd346d0fe70bd97/library/std/src/panicking.rs:647:5
   1: core::panicking::panic_fmt
             at /rustc/7cf61ebde7b22796c69757901dd346d0fe70bd97/library/core/src/panicking.rs:72:14
   2: core::result::unwrap_failed
             at /rustc/7cf61ebde7b22796c69757901dd346d0fe70bd97/library/core/src/result.rs:1649:5
   3: core::result::Result<T,E>::unwrap
             at /rustc/7cf61ebde7b22796c69757901dd346d0fe70bd97/library/core/src/result.rs:1073:23
   4: wl_clipboard_manager::paste
             at ./src/main.rs:73:5
   5: wl_clipboard_manager::main
             at ./src/main.rs:63:5
   6: core::ops::function::FnOnce::call_once
             at /rustc/7cf61ebde7b22796c69757901dd346d0fe70bd97/library/core/src/ops/function.rs:250:5
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.

Process finished with exit code 101

I tracked the cat: write error: Broken pipe down to this function:

pub fn copy_data(from_fd: Option<RawFd>, to_fd: RawFd, wait: bool) -> Result<(), CopyDataError>

in utils.rs:100.

I am using NixOS, which might have some influence on the environment that this code is running in.

I read the explaination for the usage of cat at the top of the function and it doesn't really make sense to me:

// We use the cat utility for data copying. It's easier (no need to implement any complex // buffering logic), surprisingly safer (a Rust implementation would likely require usage of // from_raw_fd() which is unsafe) and ideally faster (cat's been around for a while and is // probably pretty optimized).

While I can understand that using cat might be easier, the following function does not look easier than what I would expect this function to do. The argument that the cat implementation is safer also makes no sense, since this function uses the unsafe fork(). Lastly, I can't possibly imagine that spawning not just another instance of the current program with fork, but also then waiting for it to become cat and for that to finish, could in any way be faster than just doing a file-write.

I hope that I am just missing something in my thinking and that the implementation of this function makes sense with that additional knowledge. I would be very glad for an explanation and help for resolving my original problem, since I can't see what's happening in the forked program with my debugger.

Thank you for the good work that is this project and for any answers I might receive Simon

AnyTimeTraveler commented 3 months ago

Nevermind, I decided to try replace that function in my own fork of this repo, only to find it already gone and my problem resolved, when I tried the same code with the master branch. I was using version 0.8.1 of the library when the error occured.

May I humbly request another bugfix-release, so I can use the fix in what will be version 0.8.2

Thank you again for fixing my problem :)

YaLTeR commented 3 months ago

Hey, yeah, the cat was always a bit iffy (I copied that from wl-clipboard where it perhaps makes more sense).

The argument that the cat implementation is safer also makes no sense, since this function uses the unsafe fork().

Written in the good old times when fork() was marked as safe in nix for whatever reason :p

Lastly, I can't possibly imagine that spawning not just another instance of the current program with fork, but also then waiting for it to become cat and for that to finish, could in any way be faster than just doing a file-write.

Not sure @bugaevc did any benchmarks here, but there could be a difference when copying very large amounts of data (admittedly, a pretty rare case).

May I humbly request another bugfix-release, so I can use the fix in what will be version 0.8.2

Should it be a 0.8.2 or a 0.9.0? Because I kinda replaced the entire nix with rustix, not sure I'm comfortable releasing that as a patch bump.

bugaevc commented 3 months ago

Not sure @bugaevc did any benchmarks here

Nope :D

AnyTimeTraveler commented 3 months ago

Should it be a 0.8.2 or a 0.9.0? Because I kinda replaced the entire nix with rustix, not sure I'm comfortable releasing that as a patch bump.

If you're swapping out an entire library, I would suggest making it 0.9.0, as there might be as yet unknown issues which one might not expect from a patch release.

AnyTimeTraveler commented 3 months ago

Also thank you very much for this nice explanation! In light of that knowledge, the initial implementation makes a lot more sense to me.

YaLTeR commented 1 month ago

Sorry for taking so long. I published v0.9.0 now. I've been running it myself since and haven't found any issues.