Skip to content

Commit

Permalink
Use weakcall for eventfd on FreeBSD. (#717)
Browse files Browse the repository at this point in the history
* Use `weakcall` for `eventfd` on FreeBSD.

`eventfd` was introduced in FreeBSD 13, so it isn't in FreeBSD 12. Use
`weakcall` to call it on FreeBSD so that we don't have a link-time
dependency on it.

Fixes #716.

* Re-introduce FreeBSD 12 in the Cirrus CI config.

FreeBSD 12 was origally removed in #517 due to problems with `curl`,
however those were fixed by removing the `pkg install -y curl` in #643,
so we can now re-enable FreeBSD 12 testing.

* Use `weakcall!` for `ptsname_r` on FreeBSD too.

FreeBSD 12 lacks `ptsname_r`.

* Relax a test to accept `NOSYS` from `eventfd` on FreeBSD.

* Relax some `procctl` tests on FreeBSD too.

* Fix the `openpty` tests for FreeBSD 12.
  • Loading branch information
sunfishcode committed Jul 2, 2023
1 parent 164e8b4 commit 56dc42e
Show file tree
Hide file tree
Showing 6 changed files with 82 additions and 23 deletions.
13 changes: 13 additions & 0 deletions .cirrus.yml
Expand Up @@ -13,3 +13,16 @@ task:
test_script:
- . $HOME/.cargo/env
- cargo test --workspace --features=all-apis

task:
name: stable x86_64-unknown-freebsd-12
freebsd_instance:
image_family: freebsd-12-1
setup_script:
- curl https://sh.rustup.rs -sSf --output rustup.sh
- sh rustup.sh --default-toolchain stable -y --profile=minimal
- . $HOME/.cargo/env
- rustup default stable
test_script:
- . $HOME/.cargo/env
- cargo test --workspace --features=all-apis
14 changes: 13 additions & 1 deletion src/backend/libc/event/syscalls.rs
Expand Up @@ -33,7 +33,19 @@ pub(crate) fn eventfd(initval: u32, flags: EventfdFlags) -> io::Result<OwnedFd>
ret_owned_fd(eventfd2(initval, bitflags_bits!(flags)))
}

#[cfg(any(target_os = "freebsd", target_os = "illumos"))]
// `eventfd` was added in FreeBSD 13, so it isn't available on FreeBSD 12.
#[cfg(target_os = "freebsd")]
unsafe {
weakcall! {
fn eventfd(
initval: c::c_uint,
flags: c::c_int
) -> c::c_int
}
ret_owned_fd(eventfd(initval, bitflags_bits!(flags)))
}

#[cfg(target_os = "illumos")]
unsafe {
ret_owned_fd(c::eventfd(initval, bitflags_bits!(flags)))
}
Expand Down
19 changes: 18 additions & 1 deletion src/backend/libc/pty/syscalls.rs
Expand Up @@ -31,9 +31,26 @@ pub(crate) fn ptsname(fd: BorrowedFd, mut buffer: Vec<u8>) -> io::Result<CString

loop {
// On platforms with `ptsname_r`, use it.
#[cfg(any(target_os = "freebsd", linux_like, target_os = "fuchsia"))]
#[cfg(any(linux_like, target_os = "fuchsia"))]
let r = unsafe { c::ptsname_r(borrowed_fd(fd), buffer.as_mut_ptr().cast(), buffer.len()) };

// FreeBSD 12 doesn't have `ptsname_r`.
#[cfg(target_os = "freebsd")]
let r = unsafe {
weak! {
fn ptsname_r(
c::c_int,
*mut c::c_char,
c::size_t
) -> c::c_int
}
if let Some(func) = ptsname_r.get() {
func(borrowed_fd(fd), buffer.as_mut_ptr().cast(), buffer.len())
} else {
libc::ENOSYS
}
};

// MacOS 10.13.4 has `ptsname_r`; use it if we have it, otherwise fall
// back to calling the underlying ioctl directly.
#[cfg(apple)]
Expand Down
7 changes: 6 additions & 1 deletion tests/event/eventfd.rs
Expand Up @@ -6,7 +6,12 @@ fn test_eventfd() {
use std::mem::size_of;
use std::thread;

let efd = eventfd(0, EventfdFlags::CLOEXEC).unwrap();
let efd = match eventfd(0, EventfdFlags::CLOEXEC) {
Ok(efd) => efd,
#[cfg(target_os = "freebsd")]
Err(rustix::io::Errno::NOSYS) => return, // FreeBSD 12 lacks `eventfd`
Err(e) => Err(e).unwrap(),
};

let child = thread::spawn(move || {
for u in [1_u64, 3, 6, 11, 5000] {
Expand Down
12 changes: 10 additions & 2 deletions tests/process/procctl.rs
Expand Up @@ -13,7 +13,11 @@ fn test_trace_status() {

#[test]
fn test_reaper_status() {
assert_eq!(set_reaper_status(false), Err(io::Errno::INVAL));
match set_reaper_status(false).unwrap_err() {
io::Errno::INVAL => (),
io::Errno::PERM => return, // FreeBSD 12 doesn't support this
err => Err(err).unwrap(),
};
set_reaper_status(true).unwrap();
let status_while_acq = dbg!(get_reaper_status(None).unwrap());
set_reaper_status(false).unwrap();
Expand All @@ -38,7 +42,11 @@ fn test_trapcap() {

#[test]
fn test_no_new_privs() {
assert!(!no_new_privs(None).unwrap());
match no_new_privs(None) {
Ok(flag) => assert!(!flag),
Err(io::Errno::INVAL) => return, // FreeBSD 12 doesn't support this
Err(err) => Err(err).unwrap(),
};
set_no_new_privs(None).unwrap();
assert!(no_new_privs(None).unwrap());
// No going back but, well, we're not gonna execute SUID binaries from the
Expand Down
40 changes: 22 additions & 18 deletions tests/pty/openpty.rs
@@ -1,72 +1,76 @@
use rustix::fs::{openat, Mode, OFlags, CWD};
use rustix::pty::*;
use std::fs::File;
use std::io;
use std::io::{Read, Write};

#[test]
fn openpty_basic() -> io::Result<()> {
fn openpty_basic() {
// Use `CLOEXEC` if we can.
#[cfg(any(linux_kernel, target_os = "freebsd", target_os = "netbsd"))]
let flags = OpenptFlags::RDWR | OpenptFlags::NOCTTY | OpenptFlags::CLOEXEC;
#[cfg(not(any(linux_kernel, target_os = "freebsd", target_os = "netbsd")))]
let flags = OpenptFlags::RDWR | OpenptFlags::NOCTTY;

let controller = openpt(flags)?;
let controller = openpt(flags).unwrap();

grantpt(&controller)?;
unlockpt(&controller)?;
grantpt(&controller).unwrap();
unlockpt(&controller).unwrap();

let name = ptsname(&controller, Vec::new())?;
let name = match ptsname(&controller, Vec::new()) {
Ok(name) => name,
#[cfg(target_os = "freebsd")]
Err(rustix::io::Errno::NOSYS) => return, // FreeBSD 12 doesn't support this
Err(err) => Err(err).unwrap(),
};
let user = openat(
CWD,
name,
OFlags::RDWR | OFlags::NOCTTY | OFlags::CLOEXEC,
Mode::empty(),
)?;
)
.unwrap();

let mut controller = File::from(controller);
let mut user = File::from(user);

// The '\x04' is Ctrl-D, the default EOF control code.
controller.write_all(b"Hello, world!\n\x04")?;
controller.write_all(b"Hello, world!\n\x04").unwrap();

let mut s = String::new();
user.read_to_string(&mut s)?;
user.read_to_string(&mut s).unwrap();

assert_eq!(s, "Hello, world!\n");
Ok(())
}

// Like `openpty_basic` but use `ioctl_tiocgptpeer` instead of `ptsname`.
#[cfg(target_os = "linux")]
#[test]
fn openpty_get_peer() -> io::Result<()> {
fn openpty_get_peer() {
// Use `CLOEXEC` if we can.
#[cfg(any(linux_kernel, target_os = "freebsd", target_os = "netbsd"))]
let flags = OpenptFlags::RDWR | OpenptFlags::NOCTTY | OpenptFlags::CLOEXEC;
#[cfg(not(any(linux_kernel, target_os = "freebsd", target_os = "netbsd")))]
let flags = OpenptFlags::RDWR | OpenptFlags::NOCTTY;

let controller = openpt(flags)?;
let controller = openpt(flags).unwrap();

grantpt(&controller)?;
unlockpt(&controller)?;
grantpt(&controller).unwrap();
unlockpt(&controller).unwrap();

let user = ioctl_tiocgptpeer(
&controller,
OpenptFlags::RDWR | OpenptFlags::NOCTTY | OpenptFlags::CLOEXEC,
)?;
)
.unwrap();

let mut controller = File::from(controller);
let mut user = File::from(user);

// The '\x04' is Ctrl-D, the default EOF control code.
controller.write_all(b"Hello, world!\n\x04")?;
controller.write_all(b"Hello, world!\n\x04").unwrap();

let mut s = String::new();
user.read_to_string(&mut s)?;
user.read_to_string(&mut s).unwrap();

assert_eq!(s, "Hello, world!\n");
Ok(())
}

0 comments on commit 56dc42e

Please sign in to comment.