diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 607bac72..5288c96b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,37 +1,85 @@ on: + workflow_dispatch: pull_request: push: + tags: ["v*.*.*"] branches-ignore: - '**.tmp' -name: ci +name: Continuous Integration jobs: - test: - runs-on: ubuntu-latest + test-sys: + name: Build and test the sys crate / ${{matrix.target}} + runs-on: ${{matrix.host_os}} strategy: + fail-fast: false matrix: - rust: - - stable - - beta - - nightly - - 1.47.0 + include: + - target: x86_64-unknown-linux-gnu + host_os: ubuntu-latest + - target: x86_64-apple-darwin + host_os: macos-latest + - target: x86_64-unknown-freebsd + host_os: ubuntu-latest + vm_action: vmactions/freebsd-vm@v1 + - target: x86_64-unknown-openbsd + host_os: ubuntu-latest + vm_action: vmactions/openbsd-vm@v1 steps: - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@master + - name: Rust Cache + uses: Swatinem/rust-cache@v2 with: - toolchain: ${{ matrix.rust }} - - uses: Swatinem/rust-cache@v2 - - run: cargo test --all + key: ${{matrix.host_os}}-${{matrix.target}}-ioctl-sys + - name: Build for target ${{matrix.target}} with a virtual machine + if: matrix.vm_action != null + uses: vmactions/openbsd-vm@v1 + with: + usesh: true + prepare: | + pkg_add rust + run: | + cargo test --package ioctl-sys + - name: Build for target ${{matrix.target}} + if: matrix.vm_action == null + run: cargo test --package ioctl-sys check: runs-on: ubuntu-latest - steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@beta with: components: rustfmt, clippy - uses: Swatinem/rust-cache@v2 - - run: cargo fmt --all -- --check + - run: cargo fmt --all #- run: cargo clippy --all --all-targets -- -D warnings + + test: + name: Build / ${{matrix.target}} + runs-on: ${{matrix.host_os}} + strategy: + fail-fast: false + matrix: + include: + - target: x86_64-unknown-linux-gnu + host_os: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Rust Cache + uses: Swatinem/rust-cache@v2 + with: + key: ${{matrix.host_os}}-${{matrix.target}}-ioctl-sys + - name: Test for target ${{matrix.target}} with a virtual machine + if: matrix.vm_action != null + uses: vmactions/openbsd-vm@v1 + with: + usesh: true + prepare: | + pkg_add rust + run: | + cargo test --all + - name: Test for target ${{matrix.target}} + if: matrix.vm_action == null + run: cargo test --all \ No newline at end of file diff --git a/ioctl-sys/Cargo.toml b/ioctl-sys/Cargo.toml index 12f5f039..6d4b6765 100644 --- a/ioctl-sys/Cargo.toml +++ b/ioctl-sys/Cargo.toml @@ -9,3 +9,7 @@ documentation = "https://docs.rs/ioctl-sys" include = ["Cargo.toml", "**/*.rs"] [dependencies] + +[dev-dependencies] +# Only for the ioctl test +libc = "0.2" diff --git a/ioctl-sys/src/lib.rs b/ioctl-sys/src/lib.rs index 73d7da75..c839f9a9 100644 --- a/ioctl-sys/src/lib.rs +++ b/ioctl-sys/src/lib.rs @@ -1,20 +1,18 @@ use std::os::raw::{c_int, c_ulong}; -#[cfg(any( +#[cfg(not(any( target_os = "linux", target_os = "macos", target_os = "openbsd", + target_os = "freebsd", + target_os = "netbsd", + target_os = "dragonfly", target_os = "android" -))] -#[macro_use] +)))] +compile_error!("This platform is not supported!"); + mod platform; -#[cfg(any( - target_os = "linux", - target_os = "macos", - target_os = "openbsd", - target_os = "android" -))] pub use platform::*; extern "C" { @@ -31,14 +29,6 @@ pub fn check_res(res: c_int) -> std::io::Result<()> { } } -#[cfg(not(any( - target_os = "linux", - target_os = "macos", - target_os = "openbsd", - target_os = "android" -)))] -use platform_not_supported; - #[cfg(doctest)] mod test_readme { macro_rules! external_doc_test { diff --git a/ioctl-sys/src/platform/macos.rs b/ioctl-sys/src/platform/bsd.rs similarity index 82% rename from ioctl-sys/src/platform/macos.rs rename to ioctl-sys/src/platform/bsd.rs index 63314409..cc0db8b1 100644 --- a/ioctl-sys/src/platform/macos.rs +++ b/ioctl-sys/src/platform/bsd.rs @@ -1,3 +1,4 @@ +/// Constants for BSD-derived operating systems. Confirmed to work on macOS, FreeBSD, OpenBSD. #[cfg(any(target_arch = "aarch64", target_arch = "x86_64"))] mod consts { #[doc(hidden)] diff --git a/ioctl-sys/src/platform/mod.rs b/ioctl-sys/src/platform/mod.rs index cc8838ee..43234834 100644 --- a/ioctl-sys/src/platform/mod.rs +++ b/ioctl-sys/src/platform/mod.rs @@ -7,12 +7,14 @@ pub const TYPEBITS: u32 = 8; #[path = "linux.rs"] mod consts; -#[cfg(target_os = "macos")] -#[path = "macos.rs"] -mod consts; - -#[cfg(target_os = "openbsd")] -#[path = "openbsd.rs"] +#[cfg(any( + target_os = "macos", + target_os = "openbsd", + target_os = "freebsd", + target_os = "netbsd", + target_os = "dragonfly", +))] +#[path = "bsd.rs"] mod consts; #[doc(hidden)] diff --git a/ioctl-sys/src/platform/openbsd.rs b/ioctl-sys/src/platform/openbsd.rs deleted file mode 100644 index 63314409..00000000 --- a/ioctl-sys/src/platform/openbsd.rs +++ /dev/null @@ -1,19 +0,0 @@ -#[cfg(any(target_arch = "aarch64", target_arch = "x86_64"))] -mod consts { - #[doc(hidden)] - pub const NONE: u8 = 1; - #[doc(hidden)] - pub const READ: u8 = 2; - #[doc(hidden)] - pub const WRITE: u8 = 4; - #[doc(hidden)] - pub const SIZEBITS: u8 = 13; - #[doc(hidden)] - pub const DIRBITS: u8 = 3; -} - -#[cfg(not(any(target_arch = "aarch64", target_arch = "x86_64")))] -use this_arch_not_supported; - -#[doc(hidden)] -pub use self::consts::*; diff --git a/ioctl-sys/tests/ioctl_test.rs b/ioctl-sys/tests/ioctl_test.rs new file mode 100644 index 00000000..9c9e4a93 --- /dev/null +++ b/ioctl-sys/tests/ioctl_test.rs @@ -0,0 +1,101 @@ +extern crate ioctl_sys; +extern crate libc; + +const TEMP_FILE_PATH: &str = concat!(env!("CARGO_TARGET_TMPDIR"), "/ioctl_test"); + +// BSD ioctl tests. Shamelessly stolen from the nix crate +#[cfg(any(target_os = "freebsd", target_os = "macos", target_os = "openbsd"))] +mod bsd_ioctls { + use std::fs::File; + use std::{io, mem}; + use std::os::fd::IntoRawFd; + use std::os::raw::c_int; + + use libc::termios; + + + // From: + // macOS: /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/sys/ttycom.h + // FreeBSD, OpenBSD: + use ioctl_sys::ioctl; + use TEMP_FILE_PATH; + ioctl!(none tiocnxcl with b't', 14); + ioctl!(read tiocgeta with b't', 19; termios); + ioctl!(write tiocseta with b't', 20; termios); + + // Common function + fn test_ioctl( + expected_err_code: Option<(c_int, &str)>, + f: fn(temp_file_fd: c_int, stdout_fd: c_int) -> c_int + ) { + let temp_file_fd = File::create(TEMP_FILE_PATH) + .expect("create temp file").into_raw_fd(); + let stdout_fd = 1; + let result = f(temp_file_fd, stdout_fd); + match expected_err_code { + Some((code, name)) => { + let fail_err_code = io::Error::last_os_error().raw_os_error() + .expect("ioctl error code"); + assert_eq!(result, -1, "expected fail code (-1)"); + assert_eq!(fail_err_code, code, "expected error code {} ({})", name, code); + }, + None => { + assert_eq!(result, 0, "expected success code (0)"); + } + } + } + + #[test] + fn test_ioctl_none_fail() { + test_ioctl(Some((25, "ERRNOTTY")), |file_fd, _stdout_fd| { + unsafe { tiocnxcl( file_fd ) } + }); + } + + #[test] + fn test_ioctl_read_fail() { + test_ioctl(Some((25, "ERRNOTTY")), |file_fd, _stdout_fd| { + let mut termios = unsafe { mem::zeroed() }; + unsafe { tiocgeta( file_fd, &mut termios ) } + }); + } + + #[test] + fn test_ioctl_write_fail() { + test_ioctl(Some((25, "ERRNOTTY")), |file_fd, _stdout_fd| { + let mut termios = unsafe { mem::zeroed() }; + unsafe { tiocseta( file_fd, &mut termios ) } + }); + } + + // Ignored because it need doesn't work on GitHub actions + #[ignore] + #[test] + fn test_ioctl_none_pass() { + test_ioctl(Some((25, "ERRNOTTY")), |_file_fd, stdout_fd| { + unsafe { tiocnxcl( stdout_fd ) } + }); + } + + // Ignored because it need doesn't work on GitHub actions + #[ignore] + #[test] + fn test_ioctl_read_pass() { + test_ioctl(Some((25, "ERRNOTTY")), |_file_fd, stdout_fd| { + let mut termios = unsafe { mem::zeroed() }; + unsafe { tiocgeta( stdout_fd, &mut termios ) } + }); + } + + // Ignored because it need doesn't work on GitHub actions + // Also ignored because TIOCSETA with zeroed termios will destroy your current terminal session + // If you decide to test it, just restart your terminal after + #[ignore] + #[test] + fn test_ioctl_write_pass() { + test_ioctl(Some((25, "ERRNOTTY")), |_file_fd, stdout_fd| { + let mut termios = unsafe { mem::zeroed() }; + unsafe { tiocseta( stdout_fd, &mut termios ) } + }); + } +} \ No newline at end of file