diff --git a/CHANGELOG.md b/CHANGELOG.md index 0618953bb2..40b4bfa940 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ - **Breaking** Removed `utils::copy_data`. It forked into a `/usr/bin/env cat` for copying. All internal uses of the function have been changed to simply use `std::io::copy` instead. +- Replaced `nix` with `rustix`, following `wayland-rs`. ## v0.8.1 (7th Mar 2024) diff --git a/Cargo.lock b/Cargo.lock index 98bf6f0180..41d427d6a5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -100,12 +100,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" -[[package]] -name = "cfg_aliases" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" - [[package]] name = "chrono" version = "0.4.35" @@ -379,18 +373,6 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" -[[package]] -name = "nix" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4" -dependencies = [ - "bitflags 2.4.2", - "cfg-if", - "cfg_aliases", - "libc", -] - [[package]] name = "nom" version = "7.1.3" @@ -1088,10 +1070,10 @@ dependencies = [ "derive-new", "libc", "log", - "nix", "os_pipe", "proptest", "proptest-derive", + "rustix", "tempfile", "thiserror", "tree_magic_mini", @@ -1110,7 +1092,7 @@ dependencies = [ "libc", "log", "mime_guess", - "nix", + "rustix", "stderrlog", "structopt", "wl-clipboard-rs", diff --git a/Cargo.toml b/Cargo.toml index 6b9d595fa4..e7bd039447 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,7 @@ keywords = ["wayland", "clipboard"] [workspace.dependencies] libc = "0.2.153" log = "0.4.21" -nix = "0.28.0" +rustix = "0.38.31" [package] name = "wl-clipboard-rs" @@ -32,8 +32,8 @@ categories = ["os"] derive-new = "0.6.0" libc.workspace = true log.workspace = true -nix = { workspace = true, features = ["fs", "process", "event"] } os_pipe = { version = "1.1.5", features = ["io_safety"] } +rustix = { workspace = true, features = ["fs", "event"] } tempfile = "3.10.1" thiserror = "1" tree_magic_mini = "3.0.3" diff --git a/src/tests/mod.rs b/src/tests/mod.rs index 67e46cd954..13fee01c9f 100644 --- a/src/tests/mod.rs +++ b/src/tests/mod.rs @@ -1,10 +1,11 @@ use std::ffi::OsStr; +use std::os::fd::OwnedFd; use std::sync::atomic::AtomicU8; use std::sync::atomic::Ordering::SeqCst; use std::sync::{Arc, Mutex}; use std::thread; -use nix::sys::epoll::{Epoll, EpollCreateFlags, EpollEvent, EpollFlags, EpollTimeout}; +use rustix::event::epoll; use wayland_backend::server::ClientData; use wayland_server::{Display, ListeningSocket}; @@ -16,7 +17,7 @@ mod utils; pub struct TestServer { pub display: Display, pub socket: ListeningSocket, - pub epoll: Epoll, + pub epoll: OwnedFd, } struct ClientCounter(AtomicU8); @@ -36,17 +37,22 @@ impl TestServer { let mut display = Display::new().unwrap(); let socket = ListeningSocket::bind_auto("wl-clipboard-rs-test", 0..).unwrap(); - let epoll = Epoll::new(EpollCreateFlags::EPOLL_CLOEXEC).unwrap(); - - epoll - .add(&socket, EpollEvent::new(EpollFlags::EPOLLIN, 0)) - .unwrap(); - epoll - .add( - display.backend().poll_fd(), - EpollEvent::new(EpollFlags::EPOLLIN, 1), - ) - .unwrap(); + let epoll = epoll::create(epoll::CreateFlags::CLOEXEC).unwrap(); + + epoll::add( + &epoll, + &socket, + epoll::EventData::new_u64(0), + epoll::EventFlags::IN, + ) + .unwrap(); + epoll::add( + &epoll, + display.backend().poll_fd(), + epoll::EventData::new_u64(1), + epoll::EventFlags::IN, + ) + .unwrap(); TestServer { display, @@ -76,29 +82,30 @@ impl TestServer { while client_counter.0.load(SeqCst) > 0 || waiting_for_first_client { // Wait for requests from the client. - let mut events = [EpollEvent::empty(); 2]; - let nevents = self.epoll.wait(&mut events, EpollTimeout::NONE).unwrap(); - - let ready_socket = events.iter().take(nevents).any(|event| event.data() == 0); - let ready_clients = events.iter().take(nevents).any(|event| event.data() == 1); - - if ready_socket { - // Try to accept a new client. - if let Some(stream) = self.socket.accept().unwrap() { - waiting_for_first_client = false; - client_counter.0.fetch_add(1, SeqCst); - self.display - .handle() - .insert_client(stream, client_counter.clone()) - .unwrap(); + let mut events = epoll::EventVec::with_capacity(2); + epoll::wait(&self.epoll, &mut events, -1).unwrap(); + + for event in &events { + match event.data.u64() { + 0 => { + // Try to accept a new client. + if let Some(stream) = self.socket.accept().unwrap() { + waiting_for_first_client = false; + client_counter.0.fetch_add(1, SeqCst); + self.display + .handle() + .insert_client(stream, client_counter.clone()) + .unwrap(); + } + } + 1 => { + // Try to dispatch client messages. + self.display.dispatch_clients(state).unwrap(); + self.display.flush_clients().unwrap(); + } + x => panic!("unexpected epoll event: {x}"), } } - - if ready_clients { - // Try to dispatch client messages. - self.display.dispatch_clients(state).unwrap(); - self.display.flush_clients().unwrap(); - } } } } diff --git a/src/tests/state.rs b/src/tests/state.rs index 3c52f1bfff..44b6d00bc7 100644 --- a/src/tests/state.rs +++ b/src/tests/state.rs @@ -7,15 +7,15 @@ use std::collections::HashMap; use std::io::Write; -use std::os::fd::{AsFd, AsRawFd}; +use std::os::fd::AsFd; use std::sync::atomic::AtomicU8; use std::sync::atomic::Ordering::SeqCst; use std::sync::mpsc::Sender; -use nix::fcntl::{fcntl, FcntlArg, OFlag}; use os_pipe::PipeWriter; use proptest::prelude::*; use proptest_derive::Arbitrary; +use rustix::fs::{fcntl_setfl, OFlags}; use wayland_protocols_wlr::data_control::v1::server::zwlr_data_control_device_v1::{ self, ZwlrDataControlDeviceV1, }; @@ -261,7 +261,7 @@ impl Dispatch for State { } OfferInfo::Runtime { source } => { if state.set_nonblock_on_write_fd { - fcntl(fd.as_raw_fd(), FcntlArg::F_SETFL(OFlag::O_NONBLOCK)).unwrap(); + fcntl_setfl(&fd, OFlags::NONBLOCK).unwrap(); } source.send(mime_type, fd.as_fd()) diff --git a/wl-clipboard-rs-tools/Cargo.toml b/wl-clipboard-rs-tools/Cargo.toml index 2546e92793..d204caa915 100644 --- a/wl-clipboard-rs-tools/Cargo.toml +++ b/wl-clipboard-rs-tools/Cargo.toml @@ -16,7 +16,7 @@ anyhow = "1.0.80" libc.workspace = true log.workspace = true mime_guess = "2.0.4" -nix.workspace = true +rustix = { workspace = true, features = ["stdio"] } stderrlog = "0.6.0" structopt = { version = "0.3.26", features = ["wrap_help"] } wl-clipboard-rs = { path = "../", version = "0.8.1" } diff --git a/wl-clipboard-rs-tools/src/bin/wl-clip.rs b/wl-clipboard-rs-tools/src/bin/wl-clip.rs index 9c47eb664f..b166036216 100644 --- a/wl-clipboard-rs-tools/src/bin/wl-clip.rs +++ b/wl-clipboard-rs-tools/src/bin/wl-clip.rs @@ -1,13 +1,12 @@ use std::env::args_os; use std::ffi::OsString; -use std::fs::File; +use std::fs::{File, OpenOptions}; use std::io::{stdout, Read, Write}; use std::process; use anyhow::{Context, Error}; -use libc::{STDIN_FILENO, STDOUT_FILENO}; -use nix::fcntl::OFlag; -use nix::unistd::{close, dup2, fork, ForkResult}; +use libc::fork; +use rustix::stdio::{dup2_stdin, dup2_stdout}; use wl_clipboard_rs::copy::{self, ServeRequests, Source}; use wl_clipboard_rs::paste::{self, get_contents}; use wl_clipboard_rs::utils::is_text; @@ -290,20 +289,24 @@ fn main() -> Result<(), Error> { } else { // SAFETY: We don't spawn any threads, so doing things after forking is safe. // TODO: is there any way to verify that we don't spawn any threads? - if let ForkResult::Child = unsafe { fork() }.unwrap() { - // Replace STDIN and STDOUT with /dev/null. We won't be using them, and keeping them as - // is hangs a potential pipeline (i.e. wl-copy hello | cat). Also, simply closing the - // file descriptors is a bad idea because then they get reused by subsequent temp file - // opens, which breaks the dup2/close logic during data copying. - if let Ok(dev_null) = - nix::fcntl::open("/dev/null", OFlag::O_RDWR, nix::sys::stat::Mode::empty()) - { - let _ = dup2(dev_null, STDIN_FILENO); - let _ = dup2(dev_null, STDOUT_FILENO); - let _ = close(dev_null); - } + match unsafe { fork() } { + -1 => panic!("error forking: {:?}", std::io::Error::last_os_error()), + 0 => { + // Replace STDIN and STDOUT with /dev/null. We won't be using them, and keeping + // them as is hangs a potential pipeline (i.e. wl-copy hello | cat). Also, + // simply closing the file descriptors is a bad idea because then they get + // reused by subsequent temp file opens, which breaks the dup2/close logic + // during data copying. + if let Ok(dev_null) = + OpenOptions::new().read(true).write(true).open("/dev/null") + { + let _ = dup2_stdin(&dev_null); + let _ = dup2_stdout(&dev_null); + } - drop(prepared_copy.serve()); + drop(prepared_copy.serve()); + } + _ => (), } } } diff --git a/wl-clipboard-rs-tools/src/bin/wl-copy.rs b/wl-clipboard-rs-tools/src/bin/wl-copy.rs index 8b93459752..084aca0151 100644 --- a/wl-clipboard-rs-tools/src/bin/wl-copy.rs +++ b/wl-clipboard-rs-tools/src/bin/wl-copy.rs @@ -1,9 +1,9 @@ use std::ffi::OsString; +use std::fs::OpenOptions; use std::os::unix::ffi::OsStringExt; -use libc::{STDIN_FILENO, STDOUT_FILENO}; -use nix::fcntl::OFlag; -use nix::unistd::{close, dup2, fork, ForkResult}; +use libc::fork; +use rustix::stdio::{dup2_stdin, dup2_stdout}; use structopt::clap::AppSettings; use structopt::StructOpt; use wl_clipboard_rs::copy::{self, clear, ClipboardType, MimeType, Seat, ServeRequests, Source}; @@ -159,20 +159,22 @@ fn main() -> Result<(), anyhow::Error> { } else { // SAFETY: We don't spawn any threads, so doing things after forking is safe. // TODO: is there any way to verify that we don't spawn any threads? - if let ForkResult::Child = unsafe { fork() }.unwrap() { - // Replace STDIN and STDOUT with /dev/null. We won't be using them, and keeping them as - // is hangs a potential pipeline (i.e. wl-copy hello | cat). Also, simply closing the - // file descriptors is a bad idea because then they get reused by subsequent temp file - // opens, which breaks the dup2/close logic during data copying. - if let Ok(dev_null) = - nix::fcntl::open("/dev/null", OFlag::O_RDWR, nix::sys::stat::Mode::empty()) - { - let _ = dup2(dev_null, STDIN_FILENO); - let _ = dup2(dev_null, STDOUT_FILENO); - let _ = close(dev_null); + match unsafe { fork() } { + -1 => panic!("error forking: {:?}", std::io::Error::last_os_error()), + 0 => { + // Replace STDIN and STDOUT with /dev/null. We won't be using them, and keeping + // them as is hangs a potential pipeline (i.e. wl-copy hello | cat). Also, simply + // closing the file descriptors is a bad idea because then they get reused by + // subsequent temp file opens, which breaks the dup2/close logic during data + // copying. + if let Ok(dev_null) = OpenOptions::new().read(true).write(true).open("/dev/null") { + let _ = dup2_stdin(&dev_null); + let _ = dup2_stdout(&dev_null); + } + + drop(prepared_copy.serve()); } - - drop(prepared_copy.serve()); + _ => (), } }