From b4f1749b176fa29a5523d2b3bf5665b5f46d96be Mon Sep 17 00:00:00 2001 From: Marcin Mielniczuk Date: Thu, 20 Jul 2017 10:32:33 +0200 Subject: [PATCH 01/41] Rename the public ptrace_* functions. Unlike in C, we have namespacing in Rust. Renaming the functions allows us to avoid a `use nix::sys::ptrace::*` in favor of `use nix::sys::ptrace` and then calling, for example, `ptrace::traceme()` --- src/sys/ptrace.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sys/ptrace.rs b/src/sys/ptrace.rs index 73ca916087..877bfcb063 100644 --- a/src/sys/ptrace.rs +++ b/src/sys/ptrace.rs @@ -108,7 +108,7 @@ fn ptrace_other(request: ptrace::PtraceRequest, pid: Pid, addr: *mut c_void, dat } /// Set options, as with `ptrace(PTRACE_SETOPTIONS,...)`. -pub fn ptrace_setoptions(pid: Pid, options: ptrace::PtraceOptions) -> Result<()> { +pub fn setoptions(pid: Pid, options: ptrace::PtraceOptions) -> Result<()> { use self::ptrace::*; use std::ptr; @@ -117,19 +117,19 @@ pub fn ptrace_setoptions(pid: Pid, options: ptrace::PtraceOptions) -> Result<()> } /// Gets a ptrace event as described by `ptrace(PTRACE_GETEVENTMSG,...)` -pub fn ptrace_getevent(pid: Pid) -> Result { +pub fn getevent(pid: Pid) -> Result { use self::ptrace::*; ptrace_get_data::(PTRACE_GETEVENTMSG, pid) } /// Get siginfo as with `ptrace(PTRACE_GETSIGINFO,...)` -pub fn ptrace_getsiginfo(pid: Pid) -> Result { +pub fn getsiginfo(pid: Pid) -> Result { use self::ptrace::*; ptrace_get_data::(PTRACE_GETSIGINFO, pid) } /// Set siginfo as with `ptrace(PTRACE_SETSIGINFO,...)` -pub fn ptrace_setsiginfo(pid: Pid, sig: &siginfo_t) -> Result<()> { +pub fn setsiginfo(pid: Pid, sig: &siginfo_t) -> Result<()> { use self::ptrace::*; let ret = unsafe{ Errno::clear(); From 3192df4df60a781c0936092ed02a9c25b8b36caa Mon Sep 17 00:00:00 2001 From: Marcin Mielniczuk Date: Thu, 20 Jul 2017 16:00:40 +0200 Subject: [PATCH 02/41] Fix tests --- test/sys/test_ptrace.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/test/sys/test_ptrace.rs b/test/sys/test_ptrace.rs index 57b452cbd0..fc5a503cfb 100644 --- a/test/sys/test_ptrace.rs +++ b/test/sys/test_ptrace.rs @@ -1,6 +1,7 @@ use nix::Error; use nix::errno::*; use nix::unistd::*; +use nix::sys::ptrace; use nix::sys::ptrace::*; use nix::sys::ptrace::ptrace::*; @@ -17,21 +18,21 @@ fn test_ptrace() { // Just make sure ptrace_setoptions can be called at all, for now. #[test] fn test_ptrace_setoptions() { - let err = ptrace_setoptions(getpid(), PTRACE_O_TRACESYSGOOD).unwrap_err(); + let err = ptrace::setoptions(getpid(), PTRACE_O_TRACESYSGOOD).unwrap_err(); assert!(err != Error::UnsupportedOperation); } // Just make sure ptrace_getevent can be called at all, for now. #[test] fn test_ptrace_getevent() { - let err = ptrace_getevent(getpid()).unwrap_err(); + let err = ptrace::getevent(getpid()).unwrap_err(); assert!(err != Error::UnsupportedOperation); } // Just make sure ptrace_getsiginfo can be called at all, for now. #[test] fn test_ptrace_getsiginfo() { - match ptrace_getsiginfo(getpid()) { + match ptrace::getsiginfo(getpid()) { Err(Error::UnsupportedOperation) => panic!("ptrace_getsiginfo returns Error::UnsupportedOperation!"), _ => (), } @@ -41,7 +42,7 @@ fn test_ptrace_getsiginfo() { #[test] fn test_ptrace_setsiginfo() { let siginfo = unsafe { mem::uninitialized() }; - match ptrace_setsiginfo(getpid(), &siginfo) { + match ptrace::setsiginfo(getpid(), &siginfo) { Err(Error::UnsupportedOperation) => panic!("ptrace_setsiginfo returns Error::UnsupportedOperation!"), _ => (), } From d5c4d0f45b05f5af25ec4f7fb182185f921f34e0 Mon Sep 17 00:00:00 2001 From: Marcin Mielniczuk Date: Thu, 20 Jul 2017 16:24:41 +0200 Subject: [PATCH 03/41] Tidy up imports in test_ptrace.rs --- test/sys/test_ptrace.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/sys/test_ptrace.rs b/test/sys/test_ptrace.rs index fc5a503cfb..01f676c996 100644 --- a/test/sys/test_ptrace.rs +++ b/test/sys/test_ptrace.rs @@ -2,22 +2,22 @@ use nix::Error; use nix::errno::*; use nix::unistd::*; use nix::sys::ptrace; -use nix::sys::ptrace::*; -use nix::sys::ptrace::ptrace::*; use std::{mem, ptr}; #[test] fn test_ptrace() { + use nix::sys::ptrace::ptrace::*; // Just make sure ptrace can be called at all, for now. // FIXME: qemu-user doesn't implement ptrace on all arches, so permit ENOSYS - let err = ptrace(PTRACE_ATTACH, getpid(), ptr::null_mut(), ptr::null_mut()).unwrap_err(); + let err = ptrace::ptrace(PTRACE_ATTACH, getpid(), ptr::null_mut(), ptr::null_mut()).unwrap_err(); assert!(err == Error::Sys(Errno::EPERM) || err == Error::Sys(Errno::ENOSYS)); } // Just make sure ptrace_setoptions can be called at all, for now. #[test] fn test_ptrace_setoptions() { + use nix::sys::ptrace::ptrace::*; // for PTRACE_O_TRACESYSGOOD let err = ptrace::setoptions(getpid(), PTRACE_O_TRACESYSGOOD).unwrap_err(); assert!(err != Error::UnsupportedOperation); } From 18b2bc1f2087bb07a45916c603b262bbbb7fac37 Mon Sep 17 00:00:00 2001 From: Marcin Mielniczuk Date: Fri, 21 Jul 2017 10:03:41 +0200 Subject: [PATCH 04/41] Get rid of the bulk imports --- test/sys/test_ptrace.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/sys/test_ptrace.rs b/test/sys/test_ptrace.rs index 01f676c996..0614c13fd2 100644 --- a/test/sys/test_ptrace.rs +++ b/test/sys/test_ptrace.rs @@ -1,13 +1,13 @@ use nix::Error; -use nix::errno::*; -use nix::unistd::*; +use nix::errno::Errno; +use nix::unistd::getpid; use nix::sys::ptrace; use std::{mem, ptr}; #[test] fn test_ptrace() { - use nix::sys::ptrace::ptrace::*; + use nix::sys::ptrace::ptrace::PTRACE_ATTACH; // Just make sure ptrace can be called at all, for now. // FIXME: qemu-user doesn't implement ptrace on all arches, so permit ENOSYS let err = ptrace::ptrace(PTRACE_ATTACH, getpid(), ptr::null_mut(), ptr::null_mut()).unwrap_err(); @@ -17,7 +17,7 @@ fn test_ptrace() { // Just make sure ptrace_setoptions can be called at all, for now. #[test] fn test_ptrace_setoptions() { - use nix::sys::ptrace::ptrace::*; // for PTRACE_O_TRACESYSGOOD + use nix::sys::ptrace::ptrace::PTRACE_O_TRACESYSGOOD; let err = ptrace::setoptions(getpid(), PTRACE_O_TRACESYSGOOD).unwrap_err(); assert!(err != Error::UnsupportedOperation); } From 8928a7d1374bdd12bd52d04f4afc0ba2704f1285 Mon Sep 17 00:00:00 2001 From: Bryant Mairs Date: Tue, 11 Jul 2017 09:47:47 -0700 Subject: [PATCH 05/41] Remove old workaround --- src/sys/ioctl/mod.rs | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/src/sys/ioctl/mod.rs b/src/sys/ioctl/mod.rs index 48b3827bb8..b5e1709da6 100644 --- a/src/sys/ioctl/mod.rs +++ b/src/sys/ioctl/mod.rs @@ -114,12 +114,6 @@ mod platform; pub use self::platform::*; -// liblibc has the wrong decl for linux :| hack until #26809 lands. -extern "C" { - #[doc(hidden)] - pub fn ioctl(fd: libc::c_int, req: libc::c_ulong, ...) -> libc::c_int; -} - /// A hack to get the macros to work nicely. #[doc(hidden)] pub use ::libc as libc; @@ -140,34 +134,34 @@ macro_rules! ioctl { pub unsafe fn $name(fd: $crate::sys::ioctl::libc::c_int, data: *mut u8) -> $crate::Result<$crate::sys::ioctl::libc::c_int> { - convert_ioctl_res!($crate::sys::ioctl::ioctl(fd, $nr as $crate::sys::ioctl::libc::c_ulong, data)) + convert_ioctl_res!($crate::sys::ioctl::libc::ioctl(fd, $nr as $crate::sys::ioctl::libc::c_ulong, data)) } ); (none $name:ident with $ioty:expr, $nr:expr) => ( pub unsafe fn $name(fd: $crate::sys::ioctl::libc::c_int) -> $crate::Result<$crate::sys::ioctl::libc::c_int> { - convert_ioctl_res!($crate::sys::ioctl::ioctl(fd, io!($ioty, $nr) as $crate::sys::ioctl::libc::c_ulong)) + convert_ioctl_res!($crate::sys::ioctl::libc::ioctl(fd, io!($ioty, $nr) as $crate::sys::ioctl::libc::c_ulong)) } ); (read $name:ident with $ioty:expr, $nr:expr; $ty:ty) => ( pub unsafe fn $name(fd: $crate::sys::ioctl::libc::c_int, val: *mut $ty) -> $crate::Result<$crate::sys::ioctl::libc::c_int> { - convert_ioctl_res!($crate::sys::ioctl::ioctl(fd, ior!($ioty, $nr, ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::libc::c_ulong, val)) + convert_ioctl_res!($crate::sys::ioctl::libc::ioctl(fd, ior!($ioty, $nr, ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::libc::c_ulong, val)) } ); (write $name:ident with $ioty:expr, $nr:expr; $ty:ty) => ( pub unsafe fn $name(fd: $crate::sys::ioctl::libc::c_int, val: $ty) -> $crate::Result<$crate::sys::ioctl::libc::c_int> { - convert_ioctl_res!($crate::sys::ioctl::ioctl(fd, iow!($ioty, $nr, ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::libc::c_ulong, val)) + convert_ioctl_res!($crate::sys::ioctl::libc::ioctl(fd, iow!($ioty, $nr, ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::libc::c_ulong, val)) } ); (readwrite $name:ident with $ioty:expr, $nr:expr; $ty:ty) => ( pub unsafe fn $name(fd: $crate::sys::ioctl::libc::c_int, val: *mut $ty) -> $crate::Result<$crate::sys::ioctl::libc::c_int> { - convert_ioctl_res!($crate::sys::ioctl::ioctl(fd, iorw!($ioty, $nr, ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::libc::c_ulong, val)) + convert_ioctl_res!($crate::sys::ioctl::libc::ioctl(fd, iorw!($ioty, $nr, ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::libc::c_ulong, val)) } ); (read buf $name:ident with $ioty:expr, $nr:expr; $ty:ty) => ( @@ -175,14 +169,14 @@ macro_rules! ioctl { val: *mut $ty, len: usize) -> $crate::Result<$crate::sys::ioctl::libc::c_int> { - convert_ioctl_res!($crate::sys::ioctl::ioctl(fd, ior!($ioty, $nr, len) as $crate::sys::ioctl::libc::c_ulong, val)) + convert_ioctl_res!($crate::sys::ioctl::libc::ioctl(fd, ior!($ioty, $nr, len) as $crate::sys::ioctl::libc::c_ulong, val)) } ); (write buf $name:ident with $ioty:expr, $nr:expr; $ty:ty) => ( pub unsafe fn $name(fd: $crate::sys::ioctl::libc::c_int, val: *const $ty, len: usize) -> $crate::Result<$crate::sys::ioctl::libc::c_int> { - convert_ioctl_res!($crate::sys::ioctl::ioctl(fd, iow!($ioty, $nr, len) as $crate::sys::ioctl::libc::c_ulong, val)) + convert_ioctl_res!($crate::sys::ioctl::libc::ioctl(fd, iow!($ioty, $nr, len) as $crate::sys::ioctl::libc::c_ulong, val)) } ); (readwrite buf $name:ident with $ioty:expr, $nr:expr; $ty:ty) => ( @@ -190,7 +184,7 @@ macro_rules! ioctl { val: *mut $ty, len: usize) -> $crate::Result<$crate::sys::ioctl::libc::c_int> { - convert_ioctl_res!($crate::sys::ioctl::ioctl(fd, iorw!($ioty, $nr, len) as $crate::sys::ioctl::libc::c_ulong, val)) + convert_ioctl_res!($crate::sys::ioctl::libc::ioctl(fd, iorw!($ioty, $nr, len) as $crate::sys::ioctl::libc::c_ulong, val)) } ); } From 1b9a779333df43e33c3c77f286aed64682001ffe Mon Sep 17 00:00:00 2001 From: Bryant Mairs Date: Tue, 11 Jul 2017 11:36:15 -0700 Subject: [PATCH 06/41] Re-add bad variant of ioctl! --- src/sys/ioctl/mod.rs | 2 +- test/sys/test_ioctl.rs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sys/ioctl/mod.rs b/src/sys/ioctl/mod.rs index b5e1709da6..b3388c2804 100644 --- a/src/sys/ioctl/mod.rs +++ b/src/sys/ioctl/mod.rs @@ -130,7 +130,7 @@ macro_rules! convert_ioctl_res { #[macro_export] macro_rules! ioctl { - ($name:ident with $nr:expr) => ( + (bad $name:ident with $nr:expr) => ( pub unsafe fn $name(fd: $crate::sys::ioctl::libc::c_int, data: *mut u8) -> $crate::Result<$crate::sys::ioctl::libc::c_int> { diff --git a/test/sys/test_ioctl.rs b/test/sys/test_ioctl.rs index 55b61fd7b3..8a5b8a1233 100644 --- a/test/sys/test_ioctl.rs +++ b/test/sys/test_ioctl.rs @@ -1,7 +1,7 @@ #![allow(dead_code)] // Simple tests to ensure macro generated fns compile -ioctl!(do_bad with 0x1234); +ioctl!(bad do_bad with 0x1234); ioctl!(none do_none with 0, 0); ioctl!(read read_test with 0, 0; u32); ioctl!(write write_test with 0, 0; u64); @@ -82,7 +82,7 @@ mod linux { #[test] fn test_op_read_write_64() { assert_eq!(iorw!(b'z', 10, (1 as u64) << 32), 0xC0007A0A); - } + } } #[cfg(any(target_os = "macos", @@ -132,5 +132,5 @@ mod bsd { #[test] fn test_op_read_write_64() { assert_eq!(iorw!(b'z', 10, (1 as u64) << 32), 0xC0007A0A); - } + } } From a5b01c007586f3cbda72cd5c17c36e02ff72122f Mon Sep 17 00:00:00 2001 From: Bryant Mairs Date: Tue, 11 Jul 2017 09:55:47 -0700 Subject: [PATCH 07/41] Add a "bad none" variant to the ioctl macro --- src/sys/ioctl/mod.rs | 6 ++++++ test/sys/test_ioctl.rs | 1 + 2 files changed, 7 insertions(+) diff --git a/src/sys/ioctl/mod.rs b/src/sys/ioctl/mod.rs index b3388c2804..ac8ff14910 100644 --- a/src/sys/ioctl/mod.rs +++ b/src/sys/ioctl/mod.rs @@ -137,6 +137,12 @@ macro_rules! ioctl { convert_ioctl_res!($crate::sys::ioctl::libc::ioctl(fd, $nr as $crate::sys::ioctl::libc::c_ulong, data)) } ); + (bad none $name:ident with $nr:expr) => ( + pub unsafe fn $name(fd: $crate::sys::ioctl::libc::c_int) + -> $crate::Result<$crate::sys::ioctl::libc::c_int> { + convert_ioctl_res!($crate::sys::ioctl::libc::ioctl(fd, $nr as $crate::sys::ioctl::libc::c_ulong)) + } + ); (none $name:ident with $ioty:expr, $nr:expr) => ( pub unsafe fn $name(fd: $crate::sys::ioctl::libc::c_int) -> $crate::Result<$crate::sys::ioctl::libc::c_int> { diff --git a/test/sys/test_ioctl.rs b/test/sys/test_ioctl.rs index 8a5b8a1233..c0ae078a44 100644 --- a/test/sys/test_ioctl.rs +++ b/test/sys/test_ioctl.rs @@ -2,6 +2,7 @@ // Simple tests to ensure macro generated fns compile ioctl!(bad do_bad with 0x1234); +ioctl!(bad none do_bad_none with 0x1234); ioctl!(none do_none with 0, 0); ioctl!(read read_test with 0, 0; u32); ioctl!(write write_test with 0, 0; u64); From 2fe5c2b4c6c87e3ffebfbe2facdd324412468f1f Mon Sep 17 00:00:00 2001 From: Bryant Mairs Date: Tue, 11 Jul 2017 11:38:14 -0700 Subject: [PATCH 08/41] Hide internal macros/types within ioctl --- src/sys/ioctl/mod.rs | 1 + src/sys/ioctl/platform/bsd.rs | 10 ++++++++++ src/sys/ioctl/platform/linux.rs | 33 +++++++++++++++++++++++++++++++++ 3 files changed, 44 insertions(+) diff --git a/src/sys/ioctl/mod.rs b/src/sys/ioctl/mod.rs index ac8ff14910..6a79987669 100644 --- a/src/sys/ioctl/mod.rs +++ b/src/sys/ioctl/mod.rs @@ -120,6 +120,7 @@ pub use ::libc as libc; /// Convert raw ioctl return value to a Nix result #[macro_export] +#[doc(hidden)] macro_rules! convert_ioctl_res { ($w:expr) => ( { diff --git a/src/sys/ioctl/platform/bsd.rs b/src/sys/ioctl/platform/bsd.rs index 57b4d637ca..df3716b952 100644 --- a/src/sys/ioctl/platform/bsd.rs +++ b/src/sys/ioctl/platform/bsd.rs @@ -1,14 +1,20 @@ mod consts { + #[doc(hidden)] pub const VOID: u32 = 0x20000000; + #[doc(hidden)] pub const OUT: u32 = 0x40000000; + #[doc(hidden)] pub const IN: u32 = 0x80000000; + #[doc(hidden)] pub const INOUT: u32 = (IN|OUT); + #[doc(hidden)] pub const IOCPARM_MASK: u32 = 0x1fff; } pub use self::consts::*; #[macro_export] +#[doc(hidden)] macro_rules! ioc { ($inout:expr, $group:expr, $num:expr, $len:expr) => ( $inout | (($len as u32 & $crate::sys::ioctl::IOCPARM_MASK) << 16) | (($group as u32) << 8) | ($num as u32) @@ -16,21 +22,25 @@ macro_rules! ioc { } #[macro_export] +#[doc(hidden)] macro_rules! io { ($g:expr, $n:expr) => (ioc!($crate::sys::ioctl::VOID, $g, $n, 0)) } #[macro_export] +#[doc(hidden)] macro_rules! ior { ($g:expr, $n:expr, $len:expr) => (ioc!($crate::sys::ioctl::OUT, $g, $n, $len)) } #[macro_export] +#[doc(hidden)] macro_rules! iow { ($g:expr, $n:expr, $len:expr) => (ioc!($crate::sys::ioctl::IN, $g, $n, $len)) } #[macro_export] +#[doc(hidden)] macro_rules! iorw { ($g:expr, $n:expr, $len:expr) => (ioc!($crate::sys::ioctl::INOUT, $g, $n, $len)) } diff --git a/src/sys/ioctl/platform/linux.rs b/src/sys/ioctl/platform/linux.rs index cdf5b20ab8..00a25e8883 100644 --- a/src/sys/ioctl/platform/linux.rs +++ b/src/sys/ioctl/platform/linux.rs @@ -1,12 +1,19 @@ +#[doc(hidden)] pub const NRBITS: u32 = 8; +#[doc(hidden)] pub const TYPEBITS: u32 = 8; #[cfg(any(target_arch = "mips", target_arch = "mips64", target_arch = "powerpc", target_arch = "powerpc64"))] 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; } @@ -28,27 +35,41 @@ use this_arch_not_supported; target_arch = "x86_64", target_arch = "aarch64"))] mod consts { + #[doc(hidden)] pub const NONE: u8 = 0; + #[doc(hidden)] pub const READ: u8 = 2; + #[doc(hidden)] pub const WRITE: u8 = 1; + #[doc(hidden)] pub const SIZEBITS: u8 = 14; + #[doc(hidden)] pub const DIRBITS: u8 = 2; } pub use self::consts::*; +#[doc(hidden)] pub const NRSHIFT: u32 = 0; +#[doc(hidden)] pub const TYPESHIFT: u32 = NRSHIFT + NRBITS as u32; +#[doc(hidden)] pub const SIZESHIFT: u32 = TYPESHIFT + TYPEBITS as u32; +#[doc(hidden)] pub const DIRSHIFT: u32 = SIZESHIFT + SIZEBITS as u32; +#[doc(hidden)] pub const NRMASK: u32 = (1 << NRBITS) - 1; +#[doc(hidden)] pub const TYPEMASK: u32 = (1 << TYPEBITS) - 1; +#[doc(hidden)] pub const SIZEMASK: u32 = (1 << SIZEBITS) - 1; +#[doc(hidden)] pub const DIRMASK: u32 = (1 << DIRBITS) - 1; /// Encode an ioctl command. #[macro_export] +#[doc(hidden)] macro_rules! ioc { ($dir:expr, $ty:expr, $nr:expr, $sz:expr) => ( (($dir as u32) << $crate::sys::ioctl::DIRSHIFT) | @@ -59,53 +80,65 @@ macro_rules! ioc { /// Encode an ioctl command that has no associated data. #[macro_export] +#[doc(hidden)] macro_rules! io { ($ty:expr, $nr:expr) => (ioc!($crate::sys::ioctl::NONE, $ty, $nr, 0)) } /// Encode an ioctl command that reads. #[macro_export] +#[doc(hidden)] macro_rules! ior { ($ty:expr, $nr:expr, $sz:expr) => (ioc!($crate::sys::ioctl::READ, $ty, $nr, $sz)) } /// Encode an ioctl command that writes. #[macro_export] +#[doc(hidden)] macro_rules! iow { ($ty:expr, $nr:expr, $sz:expr) => (ioc!($crate::sys::ioctl::WRITE, $ty, $nr, $sz)) } /// Encode an ioctl command that both reads and writes. #[macro_export] +#[doc(hidden)] macro_rules! iorw { ($ty:expr, $nr:expr, $sz:expr) => (ioc!($crate::sys::ioctl::READ | $crate::sys::ioctl::WRITE, $ty, $nr, $sz)) } /// Extracts the "direction" (read/write/none) from an encoded ioctl command. #[inline(always)] +#[doc(hidden)] pub fn ioc_dir(nr: u32) -> u8 { ((nr >> DIRSHIFT) & DIRMASK) as u8 } /// Extracts the type from an encoded ioctl command. #[inline(always)] +#[doc(hidden)] pub fn ioc_type(nr: u32) -> u32 { (nr >> TYPESHIFT) & TYPEMASK } /// Extracts the ioctl number from an encoded ioctl command. #[inline(always)] +#[doc(hidden)] pub fn ioc_nr(nr: u32) -> u32 { (nr >> NRSHIFT) & NRMASK } /// Extracts the size from an encoded ioctl command. #[inline(always)] +#[doc(hidden)] pub fn ioc_size(nr: u32) -> u32 { ((nr >> SIZESHIFT) as u32) & SIZEMASK } +#[doc(hidden)] pub const IN: u32 = (WRITE as u32) << DIRSHIFT; +#[doc(hidden)] pub const OUT: u32 = (READ as u32) << DIRSHIFT; +#[doc(hidden)] pub const INOUT: u32 = ((READ|WRITE) as u32) << DIRSHIFT; +#[doc(hidden)] pub const SIZE_MASK: u32 = SIZEMASK << SIZESHIFT; From 233a6784fd8c660a85a9ac8b9d88a5245cb4c232 Mon Sep 17 00:00:00 2001 From: Bryant Mairs Date: Tue, 11 Jul 2017 11:39:44 -0700 Subject: [PATCH 09/41] Remove unnecessary path aliasing --- src/sys/ioctl/mod.rs | 59 +++++++++++++++++++++----------------------- 1 file changed, 28 insertions(+), 31 deletions(-) diff --git a/src/sys/ioctl/mod.rs b/src/sys/ioctl/mod.rs index 6a79987669..b54995b774 100644 --- a/src/sys/ioctl/mod.rs +++ b/src/sys/ioctl/mod.rs @@ -114,10 +114,6 @@ mod platform; pub use self::platform::*; -/// A hack to get the macros to work nicely. -#[doc(hidden)] -pub use ::libc as libc; - /// Convert raw ioctl return value to a Nix result #[macro_export] #[doc(hidden)] @@ -129,69 +125,70 @@ macro_rules! convert_ioctl_res { ); } +/// Generates ioctl functions. See [::sys::ioctl](sys/ioctl/index.html). #[macro_export] macro_rules! ioctl { (bad $name:ident with $nr:expr) => ( - pub unsafe fn $name(fd: $crate::sys::ioctl::libc::c_int, + pub unsafe fn $name(fd: $crate::libc::c_int, data: *mut u8) - -> $crate::Result<$crate::sys::ioctl::libc::c_int> { - convert_ioctl_res!($crate::sys::ioctl::libc::ioctl(fd, $nr as $crate::sys::ioctl::libc::c_ulong, data)) + -> $crate::Result<$crate::libc::c_int> { + convert_ioctl_res!($crate::libc::ioctl(fd, $nr as $crate::libc::c_ulong, data)) } ); (bad none $name:ident with $nr:expr) => ( - pub unsafe fn $name(fd: $crate::sys::ioctl::libc::c_int) - -> $crate::Result<$crate::sys::ioctl::libc::c_int> { - convert_ioctl_res!($crate::sys::ioctl::libc::ioctl(fd, $nr as $crate::sys::ioctl::libc::c_ulong)) + pub unsafe fn $name(fd: $crate::libc::c_int) + -> $crate::Result<$crate::libc::c_int> { + convert_ioctl_res!($crate::libc::ioctl(fd, $nr as $crate::libc::c_ulong)) } ); (none $name:ident with $ioty:expr, $nr:expr) => ( - pub unsafe fn $name(fd: $crate::sys::ioctl::libc::c_int) - -> $crate::Result<$crate::sys::ioctl::libc::c_int> { - convert_ioctl_res!($crate::sys::ioctl::libc::ioctl(fd, io!($ioty, $nr) as $crate::sys::ioctl::libc::c_ulong)) + pub unsafe fn $name(fd: $crate::libc::c_int) + -> $crate::Result<$crate::libc::c_int> { + convert_ioctl_res!($crate::libc::ioctl(fd, io!($ioty, $nr) as $crate::libc::c_ulong)) } ); (read $name:ident with $ioty:expr, $nr:expr; $ty:ty) => ( - pub unsafe fn $name(fd: $crate::sys::ioctl::libc::c_int, + pub unsafe fn $name(fd: $crate::libc::c_int, val: *mut $ty) - -> $crate::Result<$crate::sys::ioctl::libc::c_int> { - convert_ioctl_res!($crate::sys::ioctl::libc::ioctl(fd, ior!($ioty, $nr, ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::libc::c_ulong, val)) + -> $crate::Result<$crate::libc::c_int> { + convert_ioctl_res!($crate::libc::ioctl(fd, ior!($ioty, $nr, ::std::mem::size_of::<$ty>()) as $crate::libc::c_ulong, val)) } ); (write $name:ident with $ioty:expr, $nr:expr; $ty:ty) => ( - pub unsafe fn $name(fd: $crate::sys::ioctl::libc::c_int, + pub unsafe fn $name(fd: $crate::libc::c_int, val: $ty) - -> $crate::Result<$crate::sys::ioctl::libc::c_int> { - convert_ioctl_res!($crate::sys::ioctl::libc::ioctl(fd, iow!($ioty, $nr, ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::libc::c_ulong, val)) + -> $crate::Result<$crate::libc::c_int> { + convert_ioctl_res!($crate::libc::ioctl(fd, iow!($ioty, $nr, ::std::mem::size_of::<$ty>()) as $crate::libc::c_ulong, val)) } ); (readwrite $name:ident with $ioty:expr, $nr:expr; $ty:ty) => ( - pub unsafe fn $name(fd: $crate::sys::ioctl::libc::c_int, + pub unsafe fn $name(fd: $crate::libc::c_int, val: *mut $ty) - -> $crate::Result<$crate::sys::ioctl::libc::c_int> { - convert_ioctl_res!($crate::sys::ioctl::libc::ioctl(fd, iorw!($ioty, $nr, ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::libc::c_ulong, val)) + -> $crate::Result<$crate::libc::c_int> { + convert_ioctl_res!($crate::libc::ioctl(fd, iorw!($ioty, $nr, ::std::mem::size_of::<$ty>()) as $crate::libc::c_ulong, val)) } ); (read buf $name:ident with $ioty:expr, $nr:expr; $ty:ty) => ( - pub unsafe fn $name(fd: $crate::sys::ioctl::libc::c_int, + pub unsafe fn $name(fd: $crate::libc::c_int, val: *mut $ty, len: usize) - -> $crate::Result<$crate::sys::ioctl::libc::c_int> { - convert_ioctl_res!($crate::sys::ioctl::libc::ioctl(fd, ior!($ioty, $nr, len) as $crate::sys::ioctl::libc::c_ulong, val)) + -> $crate::Result<$crate::libc::c_int> { + convert_ioctl_res!($crate::libc::ioctl(fd, ior!($ioty, $nr, len) as $crate::libc::c_ulong, val)) } ); (write buf $name:ident with $ioty:expr, $nr:expr; $ty:ty) => ( - pub unsafe fn $name(fd: $crate::sys::ioctl::libc::c_int, + pub unsafe fn $name(fd: $crate::libc::c_int, val: *const $ty, - len: usize) -> $crate::Result<$crate::sys::ioctl::libc::c_int> { - convert_ioctl_res!($crate::sys::ioctl::libc::ioctl(fd, iow!($ioty, $nr, len) as $crate::sys::ioctl::libc::c_ulong, val)) + len: usize) -> $crate::Result<$crate::libc::c_int> { + convert_ioctl_res!($crate::libc::ioctl(fd, iow!($ioty, $nr, len) as $crate::libc::c_ulong, val)) } ); (readwrite buf $name:ident with $ioty:expr, $nr:expr; $ty:ty) => ( - pub unsafe fn $name(fd: $crate::sys::ioctl::libc::c_int, + pub unsafe fn $name(fd: $crate::libc::c_int, val: *mut $ty, len: usize) - -> $crate::Result<$crate::sys::ioctl::libc::c_int> { - convert_ioctl_res!($crate::sys::ioctl::libc::ioctl(fd, iorw!($ioty, $nr, len) as $crate::sys::ioctl::libc::c_ulong, val)) + -> $crate::Result<$crate::libc::c_int> { + convert_ioctl_res!($crate::libc::ioctl(fd, iorw!($ioty, $nr, len) as $crate::libc::c_ulong, val)) } ); } From 55a7d4b96d608b0f4b66dfa279e8906fca4e0ab2 Mon Sep 17 00:00:00 2001 From: Bryant Mairs Date: Tue, 11 Jul 2017 11:39:56 -0700 Subject: [PATCH 10/41] Revise ioctl module documentation This refactors the examples to more directly address the exact use cases for the `ioctl!` macro that `nix` provides. Additionally other macros that should not be used by end users are no longer discussed. --- src/sys/ioctl/mod.rs | 196 ++++++++++++++++++++++++++----------------- 1 file changed, 119 insertions(+), 77 deletions(-) diff --git a/src/sys/ioctl/mod.rs b/src/sys/ioctl/mod.rs index b54995b774..330ff8cddc 100644 --- a/src/sys/ioctl/mod.rs +++ b/src/sys/ioctl/mod.rs @@ -1,102 +1,144 @@ -//! Provide helpers for making ioctl system calls -//! -//! Currently supports Linux on all architectures. Other platforms welcome! +//! Provide helpers for making ioctl system calls. //! //! This library is pretty low-level and messy. `ioctl` is not fun. //! //! What is an `ioctl`? //! =================== //! -//! The `ioctl` syscall is the grab-bag syscall on POSIX systems. Don't want -//! to add a new syscall? Make it an `ioctl`! `ioctl` refers to both the syscall, -//! and the commands that can be send with it. `ioctl` stands for "IO control", -//! and the commands are always sent to a file descriptor. +//! The `ioctl` syscall is the grab-bag syscall on POSIX systems. Don't want to add a new +//! syscall? Make it an `ioctl`! `ioctl` refers to both the syscall, and the commands that can be +//! sent with it. `ioctl` stands for "IO control", and the commands are always sent to a file +//! descriptor. //! //! It is common to see `ioctl`s used for the following purposes: //! -//! * Provide read/write access to out-of-band data related -//! to a device such as configuration (for instance, setting -//! serial port options) -//! * Provide a mechanism for performing full-duplex data -//! transfers (for instance, xfer on SPI devices). -//! * Provide access to control functions on a device (for example, -//! on Linux you can send commands like pause, resume, and eject -//! to the CDROM device. -//! * Do whatever else the device driver creator thought made most sense. -//! -//! `ioctl`s are synchronous system calls and are similar to read and -//! write calls in that regard. +//! * Provide read/write access to out-of-band data related to a device such as configuration +//! (for instance, setting serial port options) +//! * Provide a mechanism for performing full-duplex data transfers (for instance, xfer on SPI +//! devices). +//! * Provide access to control functions on a device (for example, on Linux you can send +//! commands like pause, resume, and eject to the CDROM device. +//! * Do whatever else the device driver creator thought made most sense. +//! +//! `ioctl`s are synchronous system calls and are similar to read and write calls in that regard. +//! They operate on file descriptors and have an identifier that specifies what the ioctl is. +//! Additionally they may read or write data and therefore need to pass along a data pointer. +//! Besides the semantics of the ioctls being confusing, the generation of this identifer can also +//! be difficult. +//! +//! Historically `ioctl` numbers were arbitrary hard-coded values. In Linux (before 2.6) and some +//! unices this has changed to a more-ordered system where the ioctl numbers are partitioned into +//! subcomponents (For linux this is documented in +//! [`Documentation/ioctl/ioctl-number.txt`](http://elixir.free-electrons.com/linux/latest/source/Documentation/ioctl/ioctl-number.txt)): +//! +//! * Number: The actual ioctl ID +//! * Type: A grouping of ioctls for a common purpose or driver +//! * Size: The size in bytes of the data that will be transferred +//! * Direction: Whether there is any data and if it's read, write, or both +//! +//! Newer drivers should not generate complete integer identifiers for their `ioctl`s instead +//! preferring to use the 4 components above to generate the final ioctl identifier. Because of +//! how old `ioctl`s are, however, there are many hard-coded `ioctl` identifiers. These are +//! commonly referred to as "bad" in `ioctl` documentation. +//! +//! Defining ioctls +//! =============== +//! +//! This library provides the `ioctl!` macro, for binding `ioctl`s. This macro generates public +//! unsafe functions that can then be used for calling the ioctl. This macro has a few different +//! ways it can be used depending on the specific ioctl you're working with. +//! +//! A simple `ioctl` is `SPI_IOC_RD_MODE`. This ioctl works with the SPI interface on Linux. This +//! specific `ioctl` reads the mode of the SPI device as a `u8`. It's declared in +//! `/include/uapi/linux/spi/spidev.h` as `_IOR(SPI_IOC_MAGIC, 1, __u8)`. Since it uses the `_IOR` +//! macro, we know it's a `read` ioctl and can use the `ioctl!` macro as follows: //! -//! What does this module support? -//! =============================== +//! ``` +//! # #[macro_use] extern crate nix; +//! const SPI_IOC_MAGIC: u8 = b'k'; // Defined in linux/spi/spidev.h +//! const SPI_IOC_TYPE_MODE: u8 = 1; +//! ioctl!(read spi_read_mode with SPI_IOC_MAGIC, SPI_IOC_TYPE_MODE; u8); +//! # fn main() {} +//! ``` //! -//! This library provides the `ioctl!` macro, for binding `ioctl`s. -//! Here's a few examples of how that can work for SPI under Linux -//! from [rust-spidev](https://github.com/posborne/rust-spidev). +//! This generates the function: //! //! ``` -//! #[allow(non_camel_case_types)] -//! pub struct spi_ioc_transfer { -//! pub tx_buf: u64, -//! pub rx_buf: u64, -//! pub len: u32, -//! -//! // optional overrides -//! pub speed_hz: u32, -//! pub delay_usecs: u16, -//! pub bits_per_word: u8, -//! pub cs_change: u8, -//! pub pad: u32, +//! # #[macro_use] extern crate nix; +//! # use std::mem; +//! # use nix::{Errno, libc, Result}; +//! # use nix::libc::c_int as c_int; +//! # const SPI_IOC_MAGIC: u8 = b'k'; // Defined in linux/spi/spidev.h +//! # const SPI_IOC_TYPE_MODE: u8 = 1; +//! pub unsafe fn spi_read_mode(fd: c_int, data: *mut u8) -> Result { +//! let res = libc::ioctl(fd, ior!(SPI_IOC_MAGIC, SPI_IOC_TYPE_MODE, mem::size_of::()), data); +//! Errno::result(res) //! } +//! # fn main() {} +//! ``` //! -//! #[cfg(linux)] -//! mod ioctl { -//! use super::*; -//! -//! const SPI_IOC_MAGIC: u8 = 'k' as u8; -//! const SPI_IOC_NR_TRANSFER: u8 = 0; -//! const SPI_IOC_NR_MODE: u8 = 1; -//! const SPI_IOC_NR_LSB_FIRST: u8 = 2; -//! const SPI_IOC_NR_BITS_PER_WORD: u8 = 3; -//! const SPI_IOC_NR_MAX_SPEED_HZ: u8 = 4; -//! const SPI_IOC_NR_MODE32: u8 = 5; -//! -//! ioctl!(read get_mode_u8 with SPI_IOC_MAGIC, SPI_IOC_NR_MODE; u8); -//! ioctl!(read get_mode_u32 with SPI_IOC_MAGIC, SPI_IOC_NR_MODE; u32); -//! ioctl!(write set_mode_u8 with SPI_IOC_MAGIC, SPI_IOC_NR_MODE; u8); -//! ioctl!(write set_mode_u32 with SPI_IOC_MAGIC, SPI_IOC_NR_MODE32; u32); -//! ioctl!(read get_lsb_first with SPI_IOC_MAGIC, SPI_IOC_NR_LSB_FIRST; u8); -//! ioctl!(write set_lsb_first with SPI_IOC_MAGIC, SPI_IOC_NR_LSB_FIRST; u8); -//! ioctl!(read get_bits_per_word with SPI_IOC_MAGIC, SPI_IOC_NR_BITS_PER_WORD; u8); -//! ioctl!(write set_bits_per_word with SPI_IOC_MAGIC, SPI_IOC_NR_BITS_PER_WORD; u8); -//! ioctl!(read get_max_speed_hz with SPI_IOC_MAGIC, SPI_IOC_NR_MAX_SPEED_HZ; u32); -//! ioctl!(write set_max_speed_hz with SPI_IOC_MAGIC, SPI_IOC_NR_MAX_SPEED_HZ; u32); -//! ioctl!(write spidev_transfer with SPI_IOC_MAGIC, SPI_IOC_NR_TRANSFER; spi_ioc_transfer); -//! ioctl!(write buf spidev_transfer_buf with SPI_IOC_MAGIC, SPI_IOC_NR_TRANSFER; spi_ioc_transfer); -//! } +//! The return value for `ioctl` functions generated by the `ioctl!` macro are `nix::Error`s. +//! These are generated by assuming the return value of the ioctl is `-1` on error and everything +//! else is a valid return value. If this is not the case, `Result::map` can be used to map some +//! of the range of "good" values (-2..-Inf, 0..Inf) into a smaller range in a helper function. +//! +//! The mode for a given `ioctl` should be clear from the documentation if it has good +//! documentation. Otherwise it will be clear based on the macro used to generate the `ioctl` +//! number where `_IO`, `_IOR`, `_IOW`, and `_IORW` map to "none", "read", "write_*", and "readwrite" +//! respectively. To determine the specific `write_` variant to use you'll need to find +//! what the argument type is supposed to be. If it's an `int`, then `write_int` should be used, +//! otherwise it should be a pointer and `write_ptr` should be used. On Linux the +//! [`ioctl_list` man page](http://man7.org/linux/man-pages/man2/ioctl_list.2.html) describes a +//! large number of `ioctl`s and describes their argument data type. +//! +//! More examples on using `ioctl!` can be found in the [rust-spidev crate](https://github.com/rust-embedded/rust-spidev). //! -//! // doctest workaround -//! fn main() {} +//! ```text +//! pub unsafe fn $NAME(fd: c_int, val: *mut u8, len: usize) -> Result; //! ``` //! -//! Spidev uses the `_IOC` macros that are encouraged (as far as -//! `ioctl` can be encouraged at all) for newer drivers. Many -//! drivers, however, just use magic numbers with no attached -//! semantics. For those, the `ioctl!(bad ...)` variant should be -//! used (the "bad" terminology is from the Linux kernel). +//! As mentioned earlier, there are many old `ioctl`s that do not use the newer method of +//! generating `ioctl` numbers and instead use hardcoded values. These can be used with the `bad` +//! form of the `ioctl!` macro (there is no data transfer direction used with `bad`). The naming of +//! this comes from the Linux kernel which refers to these `ioctl`s as "bad". //! -//! How do I get the magic numbers? -//! =============================== +//! For example the `TCGETS` `ioctl` reads a `termios` data structure for a given file descriptor. +//! It can be implemented as: //! -//! For Linux, look at your system's headers. For example, `/usr/include/linux/input.h` has a lot -//! of lines defining macros which use `_IOR`, `_IOW`, `_IOC`, and `_IORW`. These macros -//! correspond to the `ior!`, `iow!`, `ioc!`, and `iorw!` macros defined in this crate. -//! Additionally, there is the `ioctl!` macro for creating a wrapper around `ioctl` that is -//! somewhat more type-safe. +//! ``` +//! # #[macro_use] extern crate nix; +//! # #[cfg(any(target_os = "android", target_os = "linux"))] +//! # use nix::libc::TCGETS as TCGETS; +//! # #[cfg(any(target_os = "android", target_os = "linux"))] +//! ioctl!(bad tcgets with TCGETS); +//! # fn main() {} +//! ``` +//! +//! The generated function has the same form as that generated by `read`: +//! +//! ```text +//! pub unsafe fn tcgets(fd: c_int, val: *mut u8) -> Result; +//! ``` +//! +//! There is also a `bad none` form for use with hard-coded `ioctl`s that do not transfer data. +//! The `TIOCEXCL` `ioctl` that's part of the termios API can be implemented as: //! -//! Most `ioctl`s have no or little documentation. You'll need to scrounge through -//! the source to figure out what they do and how they should be used. +//! ``` +//! # #[macro_use] extern crate nix; +//! # use nix::libc::TIOCEXCL as TIOCEXCL; +//! ioctl!(bad none tiocexcl with TIOCEXCL); +//! # fn main() {} +//! ``` +//! +//! More examples on using `ioctl!` can be found in the [rust-spidev crate](https://github.com/rust-embedded/rust-spidev). //! +//! Finding ioctl documentation +//! --------------------------- +//! +//! For Linux, look at your system's headers. For example, `/usr/include/linux/input.h` has a lot +//! of lines defining macros which use `_IO`, `_IOR`, `_IOW`, `_IOC`, and `_IORW`. Some `ioctl`s are +//! documented directly in the headers defining their constants, but others have more extensive +//! documentation in man pages (like termios' `ioctl`s which are in `tty_ioctl(4)`). #[cfg(any(target_os = "linux", target_os = "android"))] #[path = "platform/linux.rs"] #[macro_use] From caaffb883d78e09140b6a2fa77cc9fdb74d411fb Mon Sep 17 00:00:00 2001 From: Bryant Mairs Date: Tue, 11 Jul 2017 11:55:25 -0700 Subject: [PATCH 11/41] Remove ioc_* functions These are low-level functions that shouldn't be exposed --- src/sys/ioctl/platform/linux.rs | 28 ---------------------------- 1 file changed, 28 deletions(-) diff --git a/src/sys/ioctl/platform/linux.rs b/src/sys/ioctl/platform/linux.rs index 00a25e8883..e3a130a926 100644 --- a/src/sys/ioctl/platform/linux.rs +++ b/src/sys/ioctl/platform/linux.rs @@ -106,34 +106,6 @@ macro_rules! iorw { ($ty:expr, $nr:expr, $sz:expr) => (ioc!($crate::sys::ioctl::READ | $crate::sys::ioctl::WRITE, $ty, $nr, $sz)) } -/// Extracts the "direction" (read/write/none) from an encoded ioctl command. -#[inline(always)] -#[doc(hidden)] -pub fn ioc_dir(nr: u32) -> u8 { - ((nr >> DIRSHIFT) & DIRMASK) as u8 -} - -/// Extracts the type from an encoded ioctl command. -#[inline(always)] -#[doc(hidden)] -pub fn ioc_type(nr: u32) -> u32 { - (nr >> TYPESHIFT) & TYPEMASK -} - -/// Extracts the ioctl number from an encoded ioctl command. -#[inline(always)] -#[doc(hidden)] -pub fn ioc_nr(nr: u32) -> u32 { - (nr >> NRSHIFT) & NRMASK -} - -/// Extracts the size from an encoded ioctl command. -#[inline(always)] -#[doc(hidden)] -pub fn ioc_size(nr: u32) -> u32 { - ((nr >> SIZESHIFT) as u32) & SIZEMASK -} - #[doc(hidden)] pub const IN: u32 = (WRITE as u32) << DIRSHIFT; #[doc(hidden)] From 82e0139bedfd6f13dd8cd501148ea09619b1d91b Mon Sep 17 00:00:00 2001 From: Bryant Mairs Date: Tue, 11 Jul 2017 16:46:17 -0700 Subject: [PATCH 12/41] Use the proper ioctl number type depending on target This also means that we need to properly mask off bits to the valid range of ioctl numbers. --- src/sys/ioctl/mod.rs | 18 ++++++++--------- src/sys/ioctl/platform/bsd.rs | 17 ++++++++++------ src/sys/ioctl/platform/linux.rs | 36 ++++++++++++++++++++------------- 3 files changed, 42 insertions(+), 29 deletions(-) diff --git a/src/sys/ioctl/mod.rs b/src/sys/ioctl/mod.rs index 330ff8cddc..a2eb79b543 100644 --- a/src/sys/ioctl/mod.rs +++ b/src/sys/ioctl/mod.rs @@ -174,40 +174,40 @@ macro_rules! ioctl { pub unsafe fn $name(fd: $crate::libc::c_int, data: *mut u8) -> $crate::Result<$crate::libc::c_int> { - convert_ioctl_res!($crate::libc::ioctl(fd, $nr as $crate::libc::c_ulong, data)) + convert_ioctl_res!($crate::libc::ioctl(fd, $nr as $crate::sys::ioctl::ioctl_num_type, data)) } ); (bad none $name:ident with $nr:expr) => ( pub unsafe fn $name(fd: $crate::libc::c_int) -> $crate::Result<$crate::libc::c_int> { - convert_ioctl_res!($crate::libc::ioctl(fd, $nr as $crate::libc::c_ulong)) + convert_ioctl_res!($crate::libc::ioctl(fd, $nr as $crate::sys::ioctl::ioctl_num_type)) } ); (none $name:ident with $ioty:expr, $nr:expr) => ( pub unsafe fn $name(fd: $crate::libc::c_int) -> $crate::Result<$crate::libc::c_int> { - convert_ioctl_res!($crate::libc::ioctl(fd, io!($ioty, $nr) as $crate::libc::c_ulong)) + convert_ioctl_res!($crate::libc::ioctl(fd, io!($ioty, $nr) as $crate::sys::ioctl::ioctl_num_type)) } ); (read $name:ident with $ioty:expr, $nr:expr; $ty:ty) => ( pub unsafe fn $name(fd: $crate::libc::c_int, val: *mut $ty) -> $crate::Result<$crate::libc::c_int> { - convert_ioctl_res!($crate::libc::ioctl(fd, ior!($ioty, $nr, ::std::mem::size_of::<$ty>()) as $crate::libc::c_ulong, val)) + convert_ioctl_res!($crate::libc::ioctl(fd, ior!($ioty, $nr, ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::ioctl_num_type, val)) } ); (write $name:ident with $ioty:expr, $nr:expr; $ty:ty) => ( pub unsafe fn $name(fd: $crate::libc::c_int, val: $ty) -> $crate::Result<$crate::libc::c_int> { - convert_ioctl_res!($crate::libc::ioctl(fd, iow!($ioty, $nr, ::std::mem::size_of::<$ty>()) as $crate::libc::c_ulong, val)) + convert_ioctl_res!($crate::libc::ioctl(fd, iow!($ioty, $nr, ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::ioctl_num_type, val)) } ); (readwrite $name:ident with $ioty:expr, $nr:expr; $ty:ty) => ( pub unsafe fn $name(fd: $crate::libc::c_int, val: *mut $ty) -> $crate::Result<$crate::libc::c_int> { - convert_ioctl_res!($crate::libc::ioctl(fd, iorw!($ioty, $nr, ::std::mem::size_of::<$ty>()) as $crate::libc::c_ulong, val)) + convert_ioctl_res!($crate::libc::ioctl(fd, iorw!($ioty, $nr, ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::ioctl_num_type, val)) } ); (read buf $name:ident with $ioty:expr, $nr:expr; $ty:ty) => ( @@ -215,14 +215,14 @@ macro_rules! ioctl { val: *mut $ty, len: usize) -> $crate::Result<$crate::libc::c_int> { - convert_ioctl_res!($crate::libc::ioctl(fd, ior!($ioty, $nr, len) as $crate::libc::c_ulong, val)) + convert_ioctl_res!($crate::libc::ioctl(fd, ior!($ioty, $nr, len) as $crate::sys::ioctl::ioctl_num_type, val)) } ); (write buf $name:ident with $ioty:expr, $nr:expr; $ty:ty) => ( pub unsafe fn $name(fd: $crate::libc::c_int, val: *const $ty, len: usize) -> $crate::Result<$crate::libc::c_int> { - convert_ioctl_res!($crate::libc::ioctl(fd, iow!($ioty, $nr, len) as $crate::libc::c_ulong, val)) + convert_ioctl_res!($crate::libc::ioctl(fd, iow!($ioty, $nr, len) as $crate::sys::ioctl::ioctl_num_type, val)) } ); (readwrite buf $name:ident with $ioty:expr, $nr:expr; $ty:ty) => ( @@ -230,7 +230,7 @@ macro_rules! ioctl { val: *mut $ty, len: usize) -> $crate::Result<$crate::libc::c_int> { - convert_ioctl_res!($crate::libc::ioctl(fd, iorw!($ioty, $nr, len) as $crate::libc::c_ulong, val)) + convert_ioctl_res!($crate::libc::ioctl(fd, iorw!($ioty, $nr, len) as $crate::sys::ioctl::ioctl_num_type, val)) } ); } diff --git a/src/sys/ioctl/platform/bsd.rs b/src/sys/ioctl/platform/bsd.rs index df3716b952..ea39da3de6 100644 --- a/src/sys/ioctl/platform/bsd.rs +++ b/src/sys/ioctl/platform/bsd.rs @@ -1,14 +1,19 @@ +/// The datatype used for the ioctl number +#[doc(hidden)] +pub type ioctl_num_type = ::libc::c_ulong; + mod consts { + use ::sys::ioctl::platform::ioctl_num_type; #[doc(hidden)] - pub const VOID: u32 = 0x20000000; + pub const VOID: ioctl_num_type = 0x20000000; #[doc(hidden)] - pub const OUT: u32 = 0x40000000; + pub const OUT: ioctl_num_type = 0x40000000; #[doc(hidden)] - pub const IN: u32 = 0x80000000; + pub const IN: ioctl_num_type = 0x80000000; #[doc(hidden)] - pub const INOUT: u32 = (IN|OUT); + pub const INOUT: ioctl_num_type = (IN|OUT); #[doc(hidden)] - pub const IOCPARM_MASK: u32 = 0x1fff; + pub const IOCPARM_MASK: ioctl_num_type = 0x1fff; } pub use self::consts::*; @@ -17,7 +22,7 @@ pub use self::consts::*; #[doc(hidden)] macro_rules! ioc { ($inout:expr, $group:expr, $num:expr, $len:expr) => ( - $inout | (($len as u32 & $crate::sys::ioctl::IOCPARM_MASK) << 16) | (($group as u32) << 8) | ($num as u32) + $inout | (($len as $crate::sys::ioctl::ioctl_num_type & $crate::sys::ioctl::IOCPARM_MASK) << 16) | (($group as $crate::sys::ioctl::ioctl_num_type) << 8) | ($num as $crate::sys::ioctl::ioctl_num_type) ) } diff --git a/src/sys/ioctl/platform/linux.rs b/src/sys/ioctl/platform/linux.rs index e3a130a926..6daa925762 100644 --- a/src/sys/ioctl/platform/linux.rs +++ b/src/sys/ioctl/platform/linux.rs @@ -1,7 +1,15 @@ +/// The datatype used for the ioctl number +#[cfg(any(target_os = "android", target_env = "musl"))] #[doc(hidden)] -pub const NRBITS: u32 = 8; +pub type ioctl_num_type = ::libc::c_int; +#[cfg(not(any(target_os = "android", target_env = "musl")))] #[doc(hidden)] -pub const TYPEBITS: u32 = 8; +pub type ioctl_num_type = ::libc::c_ulong; + +#[doc(hidden)] +pub const NRBITS: ioctl_num_type = 8; +#[doc(hidden)] +pub const TYPEBITS: ioctl_num_type = 8; #[cfg(any(target_arch = "mips", target_arch = "mips64", target_arch = "powerpc", target_arch = "powerpc64"))] mod consts { @@ -50,32 +58,32 @@ mod consts { pub use self::consts::*; #[doc(hidden)] -pub const NRSHIFT: u32 = 0; +pub const NRSHIFT: ioctl_num_type = 0; #[doc(hidden)] -pub const TYPESHIFT: u32 = NRSHIFT + NRBITS as u32; +pub const TYPESHIFT: ioctl_num_type = NRSHIFT + NRBITS as ioctl_num_type; #[doc(hidden)] -pub const SIZESHIFT: u32 = TYPESHIFT + TYPEBITS as u32; +pub const SIZESHIFT: ioctl_num_type = TYPESHIFT + TYPEBITS as ioctl_num_type; #[doc(hidden)] -pub const DIRSHIFT: u32 = SIZESHIFT + SIZEBITS as u32; +pub const DIRSHIFT: ioctl_num_type = SIZESHIFT + SIZEBITS as ioctl_num_type; #[doc(hidden)] -pub const NRMASK: u32 = (1 << NRBITS) - 1; +pub const NRMASK: ioctl_num_type = (1 << NRBITS) - 1; #[doc(hidden)] -pub const TYPEMASK: u32 = (1 << TYPEBITS) - 1; +pub const TYPEMASK: ioctl_num_type = (1 << TYPEBITS) - 1; #[doc(hidden)] -pub const SIZEMASK: u32 = (1 << SIZEBITS) - 1; +pub const SIZEMASK: ioctl_num_type = (1 << SIZEBITS) - 1; #[doc(hidden)] -pub const DIRMASK: u32 = (1 << DIRBITS) - 1; +pub const DIRMASK: ioctl_num_type = (1 << DIRBITS) - 1; /// Encode an ioctl command. #[macro_export] #[doc(hidden)] macro_rules! ioc { ($dir:expr, $ty:expr, $nr:expr, $sz:expr) => ( - (($dir as u32) << $crate::sys::ioctl::DIRSHIFT) | - (($ty as u32) << $crate::sys::ioctl::TYPESHIFT) | - (($nr as u32) << $crate::sys::ioctl::NRSHIFT) | - (($sz as u32) << $crate::sys::ioctl::SIZESHIFT)) + (($dir as $crate::sys::ioctl::ioctl_num_type & $crate::sys::ioctl::DIRMASK) << $crate::sys::ioctl::DIRSHIFT) | + (($ty as $crate::sys::ioctl::ioctl_num_type & $crate::sys::ioctl::TYPEMASK) << $crate::sys::ioctl::TYPESHIFT) | + (($nr as $crate::sys::ioctl::ioctl_num_type & $crate::sys::ioctl::NRMASK) << $crate::sys::ioctl::NRSHIFT) | + (($sz as $crate::sys::ioctl::ioctl_num_type & $crate::sys::ioctl::SIZEMASK) << $crate::sys::ioctl::SIZESHIFT)) } /// Encode an ioctl command that has no associated data. From e4a1851ad9aae66246dfceb43e3f20127322879d Mon Sep 17 00:00:00 2001 From: Bryant Mairs Date: Tue, 11 Jul 2017 22:09:26 -0700 Subject: [PATCH 13/41] Remove c_int and c_void from root These were exported for some weird reason and then left in for documentation. Also some parts of certain modules used them and others used the libc:: prefix. This was removed to improve the docs and also code consistency --- src/lib.rs | 3 +-- src/sys/aio.rs | 18 +++++++++--------- src/sys/event.rs | 4 ++-- src/sys/signal.rs | 10 +++++----- 4 files changed, 17 insertions(+), 18 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index f0cdb581ee..b53510ba6c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -31,7 +31,6 @@ pub mod libc { pub use self::libc::*; } -pub use libc::{c_int, c_void}; pub use errno::Errno; pub mod errno; @@ -96,7 +95,7 @@ pub enum Error { /// The operation involved a conversion to Rust's native String type, which failed because the /// string did not contain all valid UTF-8. InvalidUtf8, - /// The operation is not supported by Nix, in this instance either use the libc bindings or + /// The operation is not supported by Nix, in this instance either use the libc bindings or /// consult the module documentation to see if there is a more appropriate interface available. UnsupportedOperation, } diff --git a/src/sys/aio.rs b/src/sys/aio.rs index 64aee7ca36..abb742f3bd 100644 --- a/src/sys/aio.rs +++ b/src/sys/aio.rs @@ -94,7 +94,7 @@ impl<'a> AioCb<'a> { /// be prioritized at the process's priority level minus `prio` /// * `sigev_notify` Determines how you will be notified of event /// completion. - pub fn from_fd(fd: RawFd, prio: ::c_int, + pub fn from_fd(fd: RawFd, prio: libc::c_int, sigev_notify: SigevNotify) -> AioCb<'a> { let mut a = AioCb::common_init(fd, prio, sigev_notify); a.aio_offset = 0; @@ -118,13 +118,13 @@ impl<'a> AioCb<'a> { /// * `opcode` This field is only used for `lio_listio`. It determines /// which operation to use for this individual aiocb pub fn from_mut_slice(fd: RawFd, offs: off_t, buf: &'a mut [u8], - prio: ::c_int, sigev_notify: SigevNotify, + prio: libc::c_int, sigev_notify: SigevNotify, opcode: LioOpcode) -> AioCb<'a> { let mut a = AioCb::common_init(fd, prio, sigev_notify); a.aio_offset = offs; a.aio_nbytes = buf.len() as size_t; a.aio_buf = buf.as_ptr() as *mut c_void; - a.aio_lio_opcode = opcode as ::c_int; + a.aio_lio_opcode = opcode as libc::c_int; let aiocb = AioCb { aiocb: a, mutable: true, in_progress: false, keeper: Keeper::phantom(PhantomData)}; @@ -146,13 +146,13 @@ impl<'a> AioCb<'a> { /// * `opcode` This field is only used for `lio_listio`. It determines /// which operation to use for this individual aiocb pub fn from_boxed_slice(fd: RawFd, offs: off_t, buf: Rc>, - prio: ::c_int, sigev_notify: SigevNotify, + prio: libc::c_int, sigev_notify: SigevNotify, opcode: LioOpcode) -> AioCb<'a> { let mut a = AioCb::common_init(fd, prio, sigev_notify); a.aio_offset = offs; a.aio_nbytes = buf.len() as size_t; a.aio_buf = buf.as_ptr() as *mut c_void; - a.aio_lio_opcode = opcode as ::c_int; + a.aio_lio_opcode = opcode as libc::c_int; let aiocb = AioCb{ aiocb: a, mutable: true, in_progress: false, keeper: Keeper::boxed(buf)}; @@ -173,7 +173,7 @@ impl<'a> AioCb<'a> { // AioCb, and they must all be the same type. We're basically stuck with // using an unsafe function, since aio (as designed in C) is an unsafe API. pub fn from_slice(fd: RawFd, offs: off_t, buf: &'a [u8], - prio: ::c_int, sigev_notify: SigevNotify, + prio: libc::c_int, sigev_notify: SigevNotify, opcode: LioOpcode) -> AioCb { let mut a = AioCb::common_init(fd, prio, sigev_notify); a.aio_offset = offs; @@ -183,14 +183,14 @@ impl<'a> AioCb<'a> { // it. a.aio_buf = buf.as_ptr() as *mut c_void; assert!(opcode != LioOpcode::LIO_READ, "Can't read into an immutable buffer"); - a.aio_lio_opcode = opcode as ::c_int; + a.aio_lio_opcode = opcode as libc::c_int; let aiocb = AioCb { aiocb: a, mutable: false, in_progress: false, keeper: Keeper::none}; aiocb } - fn common_init(fd: RawFd, prio: ::c_int, + fn common_init(fd: RawFd, prio: libc::c_int, sigev_notify: SigevNotify) -> libc::aiocb { // Use mem::zeroed instead of explicitly zeroing each field, because the // number and name of reserved fields is OS-dependent. On some OSes, @@ -235,7 +235,7 @@ impl<'a> AioCb<'a> { pub fn fsync(&mut self, mode: AioFsyncMode) -> Result<()> { let p: *mut libc::aiocb = &mut self.aiocb; self.in_progress = true; - Errno::result(unsafe { libc::aio_fsync(mode as ::c_int, p) }).map(drop) + Errno::result(unsafe { libc::aio_fsync(mode as libc::c_int, p) }).map(drop) } /// Asynchronously reads from a file descriptor into a buffer diff --git a/src/sys/event.rs b/src/sys/event.rs index 494c12dd47..95b7619eca 100644 --- a/src/sys/event.rs +++ b/src/sys/event.rs @@ -21,7 +21,7 @@ pub struct KEvent { #[cfg(any(target_os = "openbsd", target_os = "freebsd", target_os = "dragonfly", target_os = "macos", target_os = "ios"))] -type type_of_udata = *mut ::c_void; +type type_of_udata = *mut libc::c_void; #[cfg(any(target_os = "openbsd", target_os = "freebsd", target_os = "dragonfly", target_os = "macos", target_os = "ios"))] @@ -127,7 +127,7 @@ libc_bitflags!( #[cfg(any(target_os = "macos", target_os = "ios"))] NOTE_EXITSTATUS, NOTE_EXTEND, - #[cfg(any(target_os = "macos", + #[cfg(any(target_os = "macos", target_os = "ios", target_os = "freebsd", target_os = "dragonfly"))] diff --git a/src/sys/signal.rs b/src/sys/signal.rs index 309919b7eb..f885af9238 100644 --- a/src/sys/signal.rs +++ b/src/sys/signal.rs @@ -523,20 +523,20 @@ impl SigEvent { SigevNotify::SigevThreadId{..} => 4 // No SIGEV_THREAD_ID defined }; sev.sigev_signo = match sigev_notify { - SigevNotify::SigevSignal{ signal, .. } => signal as ::c_int, + SigevNotify::SigevSignal{ signal, .. } => signal as libc::c_int, #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))] SigevNotify::SigevKevent{ kq, ..} => kq, #[cfg(any(target_os = "linux", target_os = "freebsd"))] - SigevNotify::SigevThreadId{ signal, .. } => signal as ::c_int, + SigevNotify::SigevThreadId{ signal, .. } => signal as libc::c_int, _ => 0 }; sev.sigev_value.sival_ptr = match sigev_notify { SigevNotify::SigevNone => ptr::null_mut::(), - SigevNotify::SigevSignal{ si_value, .. } => si_value as *mut ::c_void, + SigevNotify::SigevSignal{ si_value, .. } => si_value as *mut libc::c_void, #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))] - SigevNotify::SigevKevent{ udata, .. } => udata as *mut ::c_void, + SigevNotify::SigevKevent{ udata, .. } => udata as *mut libc::c_void, #[cfg(any(target_os = "linux", target_os = "freebsd"))] - SigevNotify::SigevThreadId{ si_value, .. } => si_value as *mut ::c_void, + SigevNotify::SigevThreadId{ si_value, .. } => si_value as *mut libc::c_void, }; SigEvent::set_tid(&mut sev, &sigev_notify); SigEvent{sigevent: sev} From 88dc19b9291f502db40da745df23fed65e0f3518 Mon Sep 17 00:00:00 2001 From: Bryant Mairs Date: Tue, 11 Jul 2017 22:35:52 -0700 Subject: [PATCH 14/41] Remove unnecessary constants --- src/sys/ioctl/platform/linux.rs | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/sys/ioctl/platform/linux.rs b/src/sys/ioctl/platform/linux.rs index 6daa925762..117085d20b 100644 --- a/src/sys/ioctl/platform/linux.rs +++ b/src/sys/ioctl/platform/linux.rs @@ -113,12 +113,3 @@ macro_rules! iow { macro_rules! iorw { ($ty:expr, $nr:expr, $sz:expr) => (ioc!($crate::sys::ioctl::READ | $crate::sys::ioctl::WRITE, $ty, $nr, $sz)) } - -#[doc(hidden)] -pub const IN: u32 = (WRITE as u32) << DIRSHIFT; -#[doc(hidden)] -pub const OUT: u32 = (READ as u32) << DIRSHIFT; -#[doc(hidden)] -pub const INOUT: u32 = ((READ|WRITE) as u32) << DIRSHIFT; -#[doc(hidden)] -pub const SIZE_MASK: u32 = SIZEMASK << SIZESHIFT; From 7b078473b8ab52742c7ce46e338fc1ad98355dc8 Mon Sep 17 00:00:00 2001 From: Bryant Mairs Date: Thu, 13 Jul 2017 11:30:52 -0700 Subject: [PATCH 15/41] Unify argument names to generated ioctl functions --- src/sys/ioctl/mod.rs | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/src/sys/ioctl/mod.rs b/src/sys/ioctl/mod.rs index a2eb79b543..43114a5cce 100644 --- a/src/sys/ioctl/mod.rs +++ b/src/sys/ioctl/mod.rs @@ -94,7 +94,7 @@ //! More examples on using `ioctl!` can be found in the [rust-spidev crate](https://github.com/rust-embedded/rust-spidev). //! //! ```text -//! pub unsafe fn $NAME(fd: c_int, val: *mut u8, len: usize) -> Result; +//! pub unsafe fn $NAME(fd: c_int, data: *mut u8, len: usize) -> Result; //! ``` //! //! As mentioned earlier, there are many old `ioctl`s that do not use the newer method of @@ -117,7 +117,7 @@ //! The generated function has the same form as that generated by `read`: //! //! ```text -//! pub unsafe fn tcgets(fd: c_int, val: *mut u8) -> Result; +//! pub unsafe fn tcgets(fd: c_int, data: *mut u8) -> Result; //! ``` //! //! There is also a `bad none` form for use with hard-coded `ioctl`s that do not transfer data. @@ -191,46 +191,47 @@ macro_rules! ioctl { ); (read $name:ident with $ioty:expr, $nr:expr; $ty:ty) => ( pub unsafe fn $name(fd: $crate::libc::c_int, - val: *mut $ty) + data: *mut $ty) -> $crate::Result<$crate::libc::c_int> { - convert_ioctl_res!($crate::libc::ioctl(fd, ior!($ioty, $nr, ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::ioctl_num_type, val)) + convert_ioctl_res!($crate::libc::ioctl(fd, ior!($ioty, $nr, ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::ioctl_num_type, data)) } ); (write $name:ident with $ioty:expr, $nr:expr; $ty:ty) => ( pub unsafe fn $name(fd: $crate::libc::c_int, - val: $ty) + data: $ty) -> $crate::Result<$crate::libc::c_int> { - convert_ioctl_res!($crate::libc::ioctl(fd, iow!($ioty, $nr, ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::ioctl_num_type, val)) + convert_ioctl_res!($crate::libc::ioctl(fd, iow!($ioty, $nr, ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::ioctl_num_type, data)) } ); (readwrite $name:ident with $ioty:expr, $nr:expr; $ty:ty) => ( pub unsafe fn $name(fd: $crate::libc::c_int, - val: *mut $ty) + data: *mut $ty) -> $crate::Result<$crate::libc::c_int> { - convert_ioctl_res!($crate::libc::ioctl(fd, iorw!($ioty, $nr, ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::ioctl_num_type, val)) + convert_ioctl_res!($crate::libc::ioctl(fd, iorw!($ioty, $nr, ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::ioctl_num_type, data)) } ); (read buf $name:ident with $ioty:expr, $nr:expr; $ty:ty) => ( pub unsafe fn $name(fd: $crate::libc::c_int, - val: *mut $ty, + data: *mut $ty, len: usize) -> $crate::Result<$crate::libc::c_int> { - convert_ioctl_res!($crate::libc::ioctl(fd, ior!($ioty, $nr, len) as $crate::sys::ioctl::ioctl_num_type, val)) + convert_ioctl_res!($crate::libc::ioctl(fd, ior!($ioty, $nr, len) as $crate::sys::ioctl::ioctl_num_type, data)) } ); (write buf $name:ident with $ioty:expr, $nr:expr; $ty:ty) => ( pub unsafe fn $name(fd: $crate::libc::c_int, - val: *const $ty, - len: usize) -> $crate::Result<$crate::libc::c_int> { - convert_ioctl_res!($crate::libc::ioctl(fd, iow!($ioty, $nr, len) as $crate::sys::ioctl::ioctl_num_type, val)) + data: *const $ty, + len: usize) + -> $crate::Result<$crate::libc::c_int> { + convert_ioctl_res!($crate::libc::ioctl(fd, iow!($ioty, $nr, len) as $crate::sys::ioctl::ioctl_num_type, data)) } ); (readwrite buf $name:ident with $ioty:expr, $nr:expr; $ty:ty) => ( pub unsafe fn $name(fd: $crate::libc::c_int, - val: *mut $ty, + data: *mut $ty, len: usize) -> $crate::Result<$crate::libc::c_int> { - convert_ioctl_res!($crate::libc::ioctl(fd, iorw!($ioty, $nr, len) as $crate::sys::ioctl::ioctl_num_type, val)) + convert_ioctl_res!($crate::libc::ioctl(fd, iorw!($ioty, $nr, len) as $crate::sys::ioctl::ioctl_num_type, data)) } ); } From 3cf4cc68174ebe126a537ba8f29c29243201d917 Mon Sep 17 00:00:00 2001 From: Bryant Mairs Date: Tue, 18 Jul 2017 09:36:49 -0700 Subject: [PATCH 16/41] Split ioctl!(write ...) into write_ptr and write_int There two different write semantics used with ioctls: one involves passing a pointer the other involves passing an int. Previously the ioctl! macro did not distinguish between these cases and left it up to the user to set the proper datatype. This previous version was not type safe and prone to errors. The solution here is to split the "write" variant into a "write_ptr" and "write_int" variant that makes the semantics more explicit and improves type safety by specifying arguments better. --- src/sys/ioctl/mod.rs | 28 +++++++++++++++++++++++++--- test/sys/test_ioctl.rs | 9 +++++++-- 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/src/sys/ioctl/mod.rs b/src/sys/ioctl/mod.rs index 43114a5cce..058fc60a90 100644 --- a/src/sys/ioctl/mod.rs +++ b/src/sys/ioctl/mod.rs @@ -80,7 +80,22 @@ //! The return value for `ioctl` functions generated by the `ioctl!` macro are `nix::Error`s. //! These are generated by assuming the return value of the ioctl is `-1` on error and everything //! else is a valid return value. If this is not the case, `Result::map` can be used to map some -//! of the range of "good" values (-2..-Inf, 0..Inf) into a smaller range in a helper function. +//! of the range of "good" values (-Inf..-2, 0..Inf) into a smaller range in a helper function. +//! +//! Writing `ioctl`s generally use pointers as their data source and these should use the +//! `write_ptr` variant. But in some cases an `int` is passed directly. For these `ioctl`s use the +//! `write_int` variant of the `ioctl!` macro. This variant does not take a type as the last argument: +//! +//! ``` +//! # #[macro_use] extern crate nix; +//! const HCI_IOC_MAGIC: u8 = b'k'; +//! const HCI_IOC_HCIDEVUP: u8 = 1; +//! ioctl!(write_int hci_dev_up with HCI_IOC_MAGIC, HCI_IOC_HCIDEVUP); +//! # fn main() {} +//! ``` +//! +//! Some `ioctl`s don't transfer any data, and those should use the `none` variant. This variant +//! doesn't take a type and so it is declared similar to the `write_int` variant shown above. //! //! The mode for a given `ioctl` should be clear from the documentation if it has good //! documentation. Otherwise it will be clear based on the macro used to generate the `ioctl` @@ -196,13 +211,20 @@ macro_rules! ioctl { convert_ioctl_res!($crate::libc::ioctl(fd, ior!($ioty, $nr, ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::ioctl_num_type, data)) } ); - (write $name:ident with $ioty:expr, $nr:expr; $ty:ty) => ( + (write_ptr $name:ident with $ioty:expr, $nr:expr; $ty:ty) => ( pub unsafe fn $name(fd: $crate::libc::c_int, - data: $ty) + data: *const $ty) -> $crate::Result<$crate::libc::c_int> { convert_ioctl_res!($crate::libc::ioctl(fd, iow!($ioty, $nr, ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::ioctl_num_type, data)) } ); + (write_int $name:ident with $ioty:expr, $nr:expr) => ( + pub unsafe fn $name(fd: $crate::libc::c_int, + data: $crate::libc::c_int) + -> $crate::Result<$crate::libc::c_int> { + convert_ioctl_res!($crate::libc::ioctl(fd, iow!($ioty, $nr, ::std::mem::size_of::<$crate::libc::c_int>()) as $crate::sys::ioctl::ioctl_num_type, data)) + } + ); (readwrite $name:ident with $ioty:expr, $nr:expr; $ty:ty) => ( pub unsafe fn $name(fd: $crate::libc::c_int, data: *mut $ty) diff --git a/test/sys/test_ioctl.rs b/test/sys/test_ioctl.rs index c0ae078a44..7f0018d332 100644 --- a/test/sys/test_ioctl.rs +++ b/test/sys/test_ioctl.rs @@ -5,10 +5,15 @@ ioctl!(bad do_bad with 0x1234); ioctl!(bad none do_bad_none with 0x1234); ioctl!(none do_none with 0, 0); ioctl!(read read_test with 0, 0; u32); -ioctl!(write write_test with 0, 0; u64); +ioctl!(write_int write_ptr_int with 0, 0); +ioctl!(write_ptr write_ptr_u8 with 0, 0; u8); +ioctl!(write_ptr write_ptr_u32 with 0, 0; u32); +ioctl!(write_ptr write_ptr_u64 with 0, 0; u64); ioctl!(readwrite readwrite_test with 0, 0; u64); ioctl!(read buf readbuf_test with 0, 0; u32); -ioctl!(write buf writebuf_test with 0, 0; u32); +ioctl!(write buf writebuf_test_u8 with 0, 0; u8); +ioctl!(write buf writebuf_test_u32 with 0, 0; u32); +ioctl!(write buf writebuf_test_u64 with 0, 0; u64); ioctl!(readwrite buf readwritebuf_test with 0, 0; u32); // See C code for source of values for op calculations (does NOT work for mips/powerpc): From 571386b812c3914a39faed449939e629a1c5b58d Mon Sep 17 00:00:00 2001 From: Bryant Mairs Date: Thu, 13 Jul 2017 11:53:31 -0700 Subject: [PATCH 17/41] Add 'bad' prefixes for read, write_*, and readwrite ioctls --- src/sys/ioctl/mod.rs | 55 ++++++++++++++++++++++++++++++------------ test/sys/test_ioctl.rs | 7 ++++-- 2 files changed, 44 insertions(+), 18 deletions(-) diff --git a/src/sys/ioctl/mod.rs b/src/sys/ioctl/mod.rs index 058fc60a90..f54384e61f 100644 --- a/src/sys/ioctl/mod.rs +++ b/src/sys/ioctl/mod.rs @@ -108,35 +108,37 @@ //! //! More examples on using `ioctl!` can be found in the [rust-spidev crate](https://github.com/rust-embedded/rust-spidev). //! -//! ```text -//! pub unsafe fn $NAME(fd: c_int, data: *mut u8, len: usize) -> Result; -//! ``` +//! Using hard-coded ioctl numbers +//! ------------------------------ //! //! As mentioned earlier, there are many old `ioctl`s that do not use the newer method of -//! generating `ioctl` numbers and instead use hardcoded values. These can be used with the `bad` -//! form of the `ioctl!` macro (there is no data transfer direction used with `bad`). The naming of -//! this comes from the Linux kernel which refers to these `ioctl`s as "bad". +//! generating `ioctl` numbers and instead use hardcoded values. These can be used with the `bad *` +//! variants of the `ioctl!` macro. This naming comes from the Linux kernel which refers to these +//! `ioctl`s as "bad". These are a different variant as they bypass calling the macro that generates +//! the ioctl number and instead use the defined value directly. //! //! For example the `TCGETS` `ioctl` reads a `termios` data structure for a given file descriptor. -//! It can be implemented as: +//! It's defined as `0x5401` in `ioctls.h` on Linux and can be implemented as: //! //! ``` //! # #[macro_use] extern crate nix; //! # #[cfg(any(target_os = "android", target_os = "linux"))] //! # use nix::libc::TCGETS as TCGETS; //! # #[cfg(any(target_os = "android", target_os = "linux"))] -//! ioctl!(bad tcgets with TCGETS); +//! # use nix::libc::termios as termios; +//! # #[cfg(any(target_os = "android", target_os = "linux"))] +//! ioctl!(bad read tcgets with TCGETS; termios); //! # fn main() {} //! ``` //! //! The generated function has the same form as that generated by `read`: //! //! ```text -//! pub unsafe fn tcgets(fd: c_int, data: *mut u8) -> Result; +//! pub unsafe fn tcgets(fd: c_int, data: *mut termios) -> Result; //! ``` //! -//! There is also a `bad none` form for use with hard-coded `ioctl`s that do not transfer data. -//! The `TIOCEXCL` `ioctl` that's part of the termios API can be implemented as: +//! There is also a `bad none`, `bad write_int`/`bad write_ptr`, and `bad readwrite` variant that work +//! similar to the standard `none`, `write_int`/`write_ptr`, and `readwrite` variants. //! //! ``` //! # #[macro_use] extern crate nix; @@ -185,17 +187,38 @@ macro_rules! convert_ioctl_res { /// Generates ioctl functions. See [::sys::ioctl](sys/ioctl/index.html). #[macro_export] macro_rules! ioctl { - (bad $name:ident with $nr:expr) => ( + (bad none $name:ident with $nr:expr) => ( + pub unsafe fn $name(fd: $crate::libc::c_int) + -> $crate::Result<$crate::libc::c_int> { + convert_ioctl_res!($crate::libc::ioctl(fd, $nr as $crate::sys::ioctl::ioctl_num_type)) + } + ); + (bad read $name:ident with $nr:expr; $ty:ty) => ( pub unsafe fn $name(fd: $crate::libc::c_int, - data: *mut u8) + data: *mut $ty) -> $crate::Result<$crate::libc::c_int> { convert_ioctl_res!($crate::libc::ioctl(fd, $nr as $crate::sys::ioctl::ioctl_num_type, data)) } ); - (bad none $name:ident with $nr:expr) => ( - pub unsafe fn $name(fd: $crate::libc::c_int) + (bad write_ptr $name:ident with $nr:expr; $ty:ty) => ( + pub unsafe fn $name(fd: $crate::libc::c_int, + data: *const $ty) -> $crate::Result<$crate::libc::c_int> { - convert_ioctl_res!($crate::libc::ioctl(fd, $nr as $crate::sys::ioctl::ioctl_num_type)) + convert_ioctl_res!($crate::libc::ioctl(fd, $nr as $crate::sys::ioctl::ioctl_num_type, data)) + } + ); + (bad write_int $name:ident with $nr:expr) => ( + pub unsafe fn $name(fd: $crate::libc::c_int, + data: $crate::libc::c_int) + -> $crate::Result<$crate::libc::c_int> { + convert_ioctl_res!($crate::libc::ioctl(fd, $nr as $crate::sys::ioctl::ioctl_num_type, data)) + } + ); + (bad readwrite $name:ident with $nr:expr; $ty:ty) => ( + pub unsafe fn $name(fd: $crate::libc::c_int, + data: *mut $ty) + -> $crate::Result<$crate::libc::c_int> { + convert_ioctl_res!($crate::libc::ioctl(fd, $nr as $crate::sys::ioctl::ioctl_num_type, data)) } ); (none $name:ident with $ioty:expr, $nr:expr) => ( diff --git a/test/sys/test_ioctl.rs b/test/sys/test_ioctl.rs index 7f0018d332..184322667a 100644 --- a/test/sys/test_ioctl.rs +++ b/test/sys/test_ioctl.rs @@ -1,8 +1,11 @@ #![allow(dead_code)] // Simple tests to ensure macro generated fns compile -ioctl!(bad do_bad with 0x1234); -ioctl!(bad none do_bad_none with 0x1234); +ioctl!(bad none do_bad with 0x1234); +ioctl!(bad read do_bad_read with 0x1234; u16); +ioctl!(bad write_int do_bad_write_int with 0x1234); +ioctl!(bad write_ptr do_bad_write_ptr with 0x1234; u8); +ioctl!(bad readwrite do_bad_readwrite with 0x1234; u32); ioctl!(none do_none with 0, 0); ioctl!(read read_test with 0, 0; u32); ioctl!(write_int write_ptr_int with 0, 0); From d63c616244a523061cde77c19c6c9e92bbd15d49 Mon Sep 17 00:00:00 2001 From: Bryant Mairs Date: Thu, 13 Jul 2017 11:41:27 -0700 Subject: [PATCH 18/41] Refactor ioctl! for buffers Instead of relying on the macro user to calculate the length in bytes do that within the macro itself --- src/sys/ioctl/mod.rs | 68 ++++++++++++++++++++++++++++++++---------- test/sys/test_ioctl.rs | 13 ++++---- 2 files changed, 61 insertions(+), 20 deletions(-) diff --git a/src/sys/ioctl/mod.rs b/src/sys/ioctl/mod.rs index f54384e61f..4db3fe760b 100644 --- a/src/sys/ioctl/mod.rs +++ b/src/sys/ioctl/mod.rs @@ -140,14 +140,55 @@ //! There is also a `bad none`, `bad write_int`/`bad write_ptr`, and `bad readwrite` variant that work //! similar to the standard `none`, `write_int`/`write_ptr`, and `readwrite` variants. //! +//! Working with arrays +//! -------------------- +//! +//! Some `ioctl`s work with entire arrays of elements. These are supported by the `*_buf` variants in +//! the `ioctl!` macro which can be used by specifying `read_buf`, `write_buf`, and +//! `readwrite_buf`. Note that there are no "bad" versions for working with buffers. The generated +//! functions include a `len` argument to specify the number of elements (where the type of each +//! element is specified in the macro). +//! +//! Again looking to the SPI `ioctl`s on Linux for an example, there is a `SPI_IOC_MESSAGE` `ioctl` +//! that queues up multiple SPI messages by writing an entire array of `spi_ioc_transfer` structs. +//! `linux/spi/spidev.h` defines a macro to calculate the `ioctl` number like: +//! +//! ```C +//! #define SPI_IOC_MAGIC 'k' +//! #define SPI_MSGSIZE(N) ... +//! #define SPI_IOC_MESSAGE(N) _IOW(SPI_IOC_MAGIC, 0, char[SPI_MSGSIZE(N)]) +//! ``` +//! +//! The `SPI_MSGSIZE(N)` calculation is already handled by the `ioctl!` macro, so all that's +//! needed to define this `ioctl` is: +//! //! ``` //! # #[macro_use] extern crate nix; -//! # use nix::libc::TIOCEXCL as TIOCEXCL; -//! ioctl!(bad none tiocexcl with TIOCEXCL); +//! const SPI_IOC_MAGIC: u8 = b'k'; // Defined in linux/spi/spidev.h +//! const SPI_IOC_TYPE_MESSAGE: u8 = 0; +//! # pub struct spi_ioc_transfer(u64); +//! ioctl!(write_buf spi_transfer with SPI_IOC_MAGIC, SPI_IOC_TYPE_MESSAGE; spi_ioc_transfer); //! # fn main() {} //! ``` //! -//! More examples on using `ioctl!` can be found in the [rust-spidev crate](https://github.com/rust-embedded/rust-spidev). +//! This generates a function like: +//! +//! ``` +//! # #[macro_use] extern crate nix; +//! # use std::mem; +//! # use nix::{Errno, libc, Result}; +//! # use nix::libc::c_int as c_int; +//! # const SPI_IOC_MAGIC: u8 = b'k'; +//! # const SPI_IOC_TYPE_MESSAGE: u8 = 0; +//! # pub struct spi_ioc_transfer(u64); +//! pub unsafe fn spi_message(fd: c_int, data: &mut [spi_ioc_transfer]) -> Result { +//! let res = libc::ioctl(fd, +//! iow!(SPI_IOC_MAGIC, SPI_IOC_TYPE_MESSAGE, data.len() * mem::size_of::()), +//! data); +//! Errno::result(res) +//! } +//! # fn main() {} +//! ``` //! //! Finding ioctl documentation //! --------------------------- @@ -255,28 +296,25 @@ macro_rules! ioctl { convert_ioctl_res!($crate::libc::ioctl(fd, iorw!($ioty, $nr, ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::ioctl_num_type, data)) } ); - (read buf $name:ident with $ioty:expr, $nr:expr; $ty:ty) => ( + (read_buf $name:ident with $ioty:expr, $nr:expr; $ty:ty) => ( pub unsafe fn $name(fd: $crate::libc::c_int, - data: *mut $ty, - len: usize) + data: &mut [$ty]) -> $crate::Result<$crate::libc::c_int> { - convert_ioctl_res!($crate::libc::ioctl(fd, ior!($ioty, $nr, len) as $crate::sys::ioctl::ioctl_num_type, data)) + convert_ioctl_res!($crate::libc::ioctl(fd, ior!($ioty, $nr, data.len() * ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::ioctl_num_type, data)) } ); - (write buf $name:ident with $ioty:expr, $nr:expr; $ty:ty) => ( + (write_buf $name:ident with $ioty:expr, $nr:expr; $ty:ty) => ( pub unsafe fn $name(fd: $crate::libc::c_int, - data: *const $ty, - len: usize) + data: &[$ty]) -> $crate::Result<$crate::libc::c_int> { - convert_ioctl_res!($crate::libc::ioctl(fd, iow!($ioty, $nr, len) as $crate::sys::ioctl::ioctl_num_type, data)) + convert_ioctl_res!($crate::libc::ioctl(fd, iow!($ioty, $nr, data.len() * ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::ioctl_num_type, data)) } ); - (readwrite buf $name:ident with $ioty:expr, $nr:expr; $ty:ty) => ( + (readwrite_buf $name:ident with $ioty:expr, $nr:expr; $ty:ty) => ( pub unsafe fn $name(fd: $crate::libc::c_int, - data: *mut $ty, - len: usize) + data: &mut [$ty]) -> $crate::Result<$crate::libc::c_int> { - convert_ioctl_res!($crate::libc::ioctl(fd, iorw!($ioty, $nr, len) as $crate::sys::ioctl::ioctl_num_type, data)) + convert_ioctl_res!($crate::libc::ioctl(fd, iorw!($ioty, $nr, data.len() * ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::ioctl_num_type, data)) } ); } diff --git a/test/sys/test_ioctl.rs b/test/sys/test_ioctl.rs index 184322667a..742103a41b 100644 --- a/test/sys/test_ioctl.rs +++ b/test/sys/test_ioctl.rs @@ -13,11 +13,14 @@ ioctl!(write_ptr write_ptr_u8 with 0, 0; u8); ioctl!(write_ptr write_ptr_u32 with 0, 0; u32); ioctl!(write_ptr write_ptr_u64 with 0, 0; u64); ioctl!(readwrite readwrite_test with 0, 0; u64); -ioctl!(read buf readbuf_test with 0, 0; u32); -ioctl!(write buf writebuf_test_u8 with 0, 0; u8); -ioctl!(write buf writebuf_test_u32 with 0, 0; u32); -ioctl!(write buf writebuf_test_u64 with 0, 0; u64); -ioctl!(readwrite buf readwritebuf_test with 0, 0; u32); +ioctl!(read_buf readbuf_test with 0, 0; u32); +const SPI_IOC_MAGIC: u8 = b'k'; +const SPI_IOC_MESSAGE: u8 = 0; +ioctl!(write_buf writebuf_test_consts with SPI_IOC_MAGIC, SPI_IOC_MESSAGE; u8); +ioctl!(write_buf writebuf_test_u8 with 0, 0; u8); +ioctl!(write_buf writebuf_test_u32 with 0, 0; u32); +ioctl!(write_buf writebuf_test_u64 with 0, 0; u64); +ioctl!(readwrite_buf readwritebuf_test with 0, 0; u32); // See C code for source of values for op calculations (does NOT work for mips/powerpc): // https://gist.github.com/posborne/83ea6880770a1aef332e From 93b29290e8adf2181c012bd9b76aa05b99212682 Mon Sep 17 00:00:00 2001 From: Bryant Mairs Date: Mon, 17 Jul 2017 11:15:42 -0700 Subject: [PATCH 19/41] Update changelog --- CHANGELOG.md | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 350fc117c2..817a89f45e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,11 +24,12 @@ This project adheres to [Semantic Versioning](http://semver.org/). and nix::Error::UnsupportedOperation}` ([#614](https://github.com/nix-rust/nix/pull/614)) - Added `cfmakeraw`, `cfsetspeed`, and `tcgetsid`. ([#527](https://github.com/nix-rust/nix/pull/527)) +- Added "bad none", "bad write_ptr", "bad write_int", and "bad readwrite" variants to the `ioctl!` + macro. ([#670](https://github.com/nix-rust/nix/pull/670)) ### Changed -- Changed `ioctl!(write ...)` to take argument by value instead as pointer. - If you need a pointer as argument, use `ioctl!(write buf ...)`. - ([#626](https://github.com/nix-rust/nix/pull/626)) +- Changed `ioctl!(write ...)` into `ioctl!(write_ptr ...)` and `ioctl!(write_int ..)` variants + to more clearly separate those use cases. ([#670](https://github.com/nix-rust/nix/pull/670)) - Marked `sys::mman::{ mmap, munmap, madvise, munlock, msync }` as unsafe. ([#559](https://github.com/nix-rust/nix/pull/559)) - Minimum supported Rust version is now 1.13. @@ -48,6 +49,11 @@ This project adheres to [Semantic Versioning](http://semver.org/). - Revised the termios API including additional tests and documentation and exposed it on iOS. ([#527](https://github.com/nix-rust/nix/pull/527)) - `eventfd`, `signalfd`, and `pwritev`/`preadv` functionality is now included by default for all supported platforms. ([#681](https://github.com/nix-rust/nix/pull/561)) +- The `ioctl!` macro's plain variants has been replaced with "bad read" to be consistent with + other variants. The generated functions also have more strict types for their arguments. The + "*_buf" variants also now calculate total array size and take slice references for improved type + safety. The documentation has also been dramatically improved. + ([#670](https://github.com/nix-rust/nix/pull/670)) ### Removed - Removed `io::Error` from `nix::Error` and the conversion from `nix::Error` to `Errno` @@ -55,6 +61,9 @@ This project adheres to [Semantic Versioning](http://semver.org/). - All feature flags have been removed in favor of conditional compilation on supported platforms. `execvpe` is no longer supported, but this was already broken and will be added back in the next release. ([#681](https://github.com/nix-rust/nix/pull/561)) +- Removed `ioc_*` functions and many helper constants and macros within the `ioctl` module. These + should always have been private and only the `ioctl!` should be used in public code. + ([#670](https://github.com/nix-rust/nix/pull/670)) ### Fixed - Fixed multiple issues compiling under different archetectures and OSes. @@ -73,6 +82,8 @@ This project adheres to [Semantic Versioning](http://semver.org/). ([#623](https://github.com/nix-rust/nix/pull/623)) - Multiple constants related to the termios API have now been properly defined for all supported platforms. ([#527](https://github.com/nix-rust/nix/pull/527)) +- `ioctl!` macro now supports working with non-int datatypes and properly supports all platforms. + ([#670](https://github.com/nix-rust/nix/pull/670)) ## [0.8.1] 2017-04-16 From 085f47c1afa6fbde6ee484767b892666a2cbb567 Mon Sep 17 00:00:00 2001 From: Alan Somers Date: Wed, 19 Jul 2017 22:59:47 -0600 Subject: [PATCH 20/41] Fix thread safety issues in pty and termios tests ptsname(3) returns a pointer to a global variable, so it isn't thread-safe. Protect it with a mutex. --- test/sys/test_termios.rs | 22 +++++++++++++++++----- test/test.rs | 2 ++ test/test_pty.rs | 28 ++++++++++++++++++++++++---- 3 files changed, 43 insertions(+), 9 deletions(-) diff --git a/test/sys/test_termios.rs b/test/sys/test_termios.rs index c65720520d..2455a4e5af 100644 --- a/test/sys/test_termios.rs +++ b/test/sys/test_termios.rs @@ -18,10 +18,14 @@ fn write_all(f: RawFd, buf: &[u8]) { // Test tcgetattr on a terminal #[test] fn test_tcgetattr_pty() { - let pty = openpty(None, None).unwrap(); + // openpty uses ptname(3) internally + #[allow(unused_variables)] + let m = ::PTSNAME_MTX.lock().expect("Mutex got poisoned by another test"); + + let pty = openpty(None, None).expect("openpty failed"); assert!(termios::tcgetattr(pty.master).is_ok()); - close(pty.master).unwrap(); - close(pty.slave).unwrap(); + close(pty.master).expect("closing the master failed"); + close(pty.slave).expect("closing the slave failed"); } // Test tcgetattr on something that isn't a terminal #[test] @@ -41,12 +45,16 @@ fn test_tcgetattr_ebadf() { // Test modifying output flags #[test] fn test_output_flags() { + // openpty uses ptname(3) internally + #[allow(unused_variables)] + let m = ::PTSNAME_MTX.lock().expect("Mutex got poisoned by another test"); + // Open one pty to get attributes for the second one let mut termios = { - let pty = openpty(None, None).unwrap(); + let pty = openpty(None, None).expect("openpty failed"); assert!(pty.master > 0); assert!(pty.slave > 0); - let termios = tcgetattr(pty.master).unwrap(); + let termios = tcgetattr(pty.master).expect("tcgetattr failed"); close(pty.master).unwrap(); close(pty.slave).unwrap(); termios @@ -80,6 +88,10 @@ fn test_output_flags() { // Test modifying local flags #[test] fn test_local_flags() { + // openpty uses ptname(3) internally + #[allow(unused_variables)] + let m = ::PTSNAME_MTX.lock().expect("Mutex got poisoned by another test"); + // Open one pty to get attributes for the second one let mut termios = { let pty = openpty(None, None).unwrap(); diff --git a/test/test.rs b/test/test.rs index eb4556fa9f..fab499ce49 100644 --- a/test/test.rs +++ b/test/test.rs @@ -45,6 +45,8 @@ lazy_static! { /// Any test that creates child processes must grab this mutex, regardless /// of what it does with those children. pub static ref FORK_MTX: Mutex<()> = Mutex::new(()); + /// Any test that calls ptsname(3) must grab this mutex. + pub static ref PTSNAME_MTX: Mutex<()> = Mutex::new(()); /// Any test that alters signal handling must grab this mutex. pub static ref SIGNAL_MTX: Mutex<()> = Mutex::new(()); } diff --git a/test/test_pty.rs b/test/test_pty.rs index 55316ab4f6..75ef4923af 100644 --- a/test/test_pty.rs +++ b/test/test_pty.rs @@ -27,6 +27,9 @@ fn test_explicit_close() { #[test] #[cfg(any(target_os = "android", target_os = "linux"))] fn test_ptsname_equivalence() { + #[allow(unused_variables)] + let m = ::PTSNAME_MTX.lock().expect("Mutex got poisoned by another test"); + // Open a new PTTY master let master_fd = posix_openpt(O_RDWR).unwrap(); assert!(master_fd.as_raw_fd() > 0); @@ -42,6 +45,9 @@ fn test_ptsname_equivalence() { #[test] #[cfg(any(target_os = "android", target_os = "linux"))] fn test_ptsname_copy() { + #[allow(unused_variables)] + let m = ::PTSNAME_MTX.lock().expect("Mutex got poisoned by another test"); + // Open a new PTTY master let master_fd = posix_openpt(O_RDWR).unwrap(); assert!(master_fd.as_raw_fd() > 0); @@ -74,6 +80,9 @@ fn test_ptsname_r_copy() { #[test] #[cfg(any(target_os = "android", target_os = "linux"))] fn test_ptsname_unique() { + #[allow(unused_variables)] + let m = ::PTSNAME_MTX.lock().expect("Mutex got poisoned by another test"); + // Open a new PTTY master let master1_fd = posix_openpt(O_RDWR).unwrap(); assert!(master1_fd.as_raw_fd() > 0); @@ -95,16 +104,19 @@ fn test_ptsname_unique() { /// pair. #[test] fn test_open_ptty_pair() { + #[allow(unused_variables)] + let m = ::PTSNAME_MTX.lock().expect("Mutex got poisoned by another test"); + // Open a new PTTY master - let master_fd = posix_openpt(O_RDWR).unwrap(); + let master_fd = posix_openpt(O_RDWR).expect("posix_openpt failed"); assert!(master_fd.as_raw_fd() > 0); // Allow a slave to be generated for it - grantpt(&master_fd).unwrap(); - unlockpt(&master_fd).unwrap(); + grantpt(&master_fd).expect("grantpt failed"); + unlockpt(&master_fd).expect("unlockpt failed"); // Get the name of the slave - let slave_name = ptsname(&master_fd).unwrap(); + let slave_name = ptsname(&master_fd).expect("ptsname failed"); // Open the slave device let slave_fd = open(Path::new(&slave_name), O_RDWR, stat::Mode::empty()).unwrap(); @@ -113,6 +125,10 @@ fn test_open_ptty_pair() { #[test] fn test_openpty() { + // openpty uses ptname(3) internally + #[allow(unused_variables)] + let m = ::PTSNAME_MTX.lock().expect("Mutex got poisoned by another test"); + let pty = openpty(None, None).unwrap(); assert!(pty.master > 0); assert!(pty.slave > 0); @@ -145,6 +161,10 @@ fn test_openpty() { #[test] fn test_openpty_with_termios() { + // openpty uses ptname(3) internally + #[allow(unused_variables)] + let m = ::PTSNAME_MTX.lock().expect("Mutex got poisoned by another test"); + // Open one pty to get attributes for the second one let mut termios = { let pty = openpty(None, None).unwrap(); From 1c9e0ca620e70b7be1cc77a80b05ff1f36422eef Mon Sep 17 00:00:00 2001 From: Nicolas Dusart Date: Thu, 20 Jul 2017 14:03:27 +0200 Subject: [PATCH 21/41] fix some tests for Android --- test/sys/test_pthread.rs | 2 +- test/test_net.rs | 4 ++-- test/test_unistd.rs | 9 +++++++-- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/test/sys/test_pthread.rs b/test/sys/test_pthread.rs index 092a95aaf2..a4f02772c3 100644 --- a/test/sys/test_pthread.rs +++ b/test/sys/test_pthread.rs @@ -3,5 +3,5 @@ use nix::sys::pthread::*; #[test] fn test_pthread_self() { let tid = pthread_self(); - assert!(tid > 0); + assert!(tid != 0); } diff --git a/test/test_net.rs b/test/test_net.rs index acdaf47a86..24bd411ab2 100644 --- a/test/test_net.rs +++ b/test/test_net.rs @@ -1,9 +1,9 @@ use nix::net::if_::*; -#[cfg(target_os = "linux")] +#[cfg(any(target_os = "android", target_os = "linux"))] const LOOPBACK: &'static [u8] = b"lo"; -#[cfg(not(target_os = "linux"))] +#[cfg(not(any(target_os = "android", target_os = "linux")))] const LOOPBACK: &'static [u8] = b"lo0"; #[test] diff --git a/test/test_unistd.rs b/test/test_unistd.rs index fd4e327b56..7ae01febc5 100644 --- a/test/test_unistd.rs +++ b/test/test_unistd.rs @@ -64,7 +64,12 @@ fn test_wait() { #[test] fn test_mkstemp() { - let result = mkstemp("/tmp/nix_tempfile.XXXXXX"); + #[cfg(target_os = "android")] + let tmp = "/data/local/tmp/"; + #[cfg(not(target_os = "android"))] + let tmp = "/tmp/"; + + let result = mkstemp((tmp.to_owned() + "nix_tempfile.XXXXXX").as_str()); match result { Ok((fd, path)) => { close(fd).unwrap(); @@ -73,7 +78,7 @@ fn test_mkstemp() { Err(e) => panic!("mkstemp failed: {}", e) } - let result = mkstemp("/tmp/"); + let result = mkstemp(tmp); match result { Ok(_) => { panic!("mkstemp succeeded even though it should fail (provided a directory)"); From 2a5b86db44082a3e78520a50966cae865faf17d5 Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Thu, 20 Jul 2017 17:57:20 +0200 Subject: [PATCH 22/41] Remove workaround for `pub extern crate` On semi-recent Rust versions (I think 1.8+) this now works properly. --- src/lib.rs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index b53510ba6c..3518efacf7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -23,13 +23,7 @@ extern crate nix_test as nixtest; #[macro_use] mod macros; -// In rust 1.8+ this should be `pub extern crate libc` but prior -// to https://github.com/rust-lang/rust/issues/26775 being resolved -// it is necessary to get a little creative. -pub mod libc { - extern crate libc; - pub use self::libc::*; -} +pub extern crate libc; pub use errno::Errno; From b6ad298b773e370ce7d7907f5b11201a9b283017 Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Thu, 20 Jul 2017 23:09:35 +0200 Subject: [PATCH 23/41] Remove `homepage` from Cargo.toml It's just a (redundant) link to the repo, not a real homepage. According to the [API guidelines](https://github.com/brson/rust-api-guidelines#cargotoml-includes-all-common-metadata-c-metadata), this shouldn't be set in this case. --- Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index c47dfaf69c..15213b4d32 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,6 @@ name = "nix" description = "Rust friendly bindings to *nix APIs" version = "0.9.0-pre" authors = ["The nix-rust Project Developers"] -homepage = "https://github.com/nix-rust/nix" repository = "https://github.com/nix-rust/nix" license = "MIT" categories = ["os::unix-apis"] From 845453b3d2be1a760782b0c7eca02a1d9360f947 Mon Sep 17 00:00:00 2001 From: Bryant Mairs Date: Thu, 20 Jul 2017 14:54:26 -0700 Subject: [PATCH 24/41] Add tests of actual ioctl usage --- test/sys/test_ioctl.rs | 179 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 179 insertions(+) diff --git a/test/sys/test_ioctl.rs b/test/sys/test_ioctl.rs index 742103a41b..38ecdc99af 100644 --- a/test/sys/test_ioctl.rs +++ b/test/sys/test_ioctl.rs @@ -146,3 +146,182 @@ mod bsd { assert_eq!(iorw!(b'z', 10, (1 as u64) << 32), 0xC0007A0A); } } + +#[cfg(any(target_os = "android", target_os = "linux"))] +mod linux_ioctls { + use std::mem; + use std::os::unix::io::AsRawFd; + + use tempfile::tempfile; + use libc::{TCGETS, TCSBRK, TCSETS, TIOCNXCL, termios}; + + use nix::Error::Sys; + use nix::errno::{ENOTTY, ENOSYS}; + + ioctl!(bad none tiocnxcl with TIOCNXCL); + #[test] + fn test_ioctl_bad_none() { + let file = tempfile().unwrap(); + let res = unsafe { tiocnxcl(file.as_raw_fd()) }; + assert_eq!(res, Err(Sys(ENOTTY))); + } + + ioctl!(bad read tcgets with TCGETS; termios); + #[test] + fn test_ioctl_bad_read() { + let file = tempfile().unwrap(); + let mut termios = unsafe { mem::uninitialized() }; + let res = unsafe { tcgets(file.as_raw_fd(), &mut termios) }; + assert_eq!(res, Err(Sys(ENOTTY))); + } + + ioctl!(bad write_int tcsbrk with TCSBRK); + #[test] + fn test_ioctl_bad_write_int() { + let file = tempfile().unwrap(); + let res = unsafe { tcsbrk(file.as_raw_fd(), 0) }; + assert_eq!(res, Err(Sys(ENOTTY))); + } + + ioctl!(bad write_ptr tcsets with TCSETS; termios); + #[test] + fn test_ioctl_bad_write_ptr() { + let file = tempfile().unwrap(); + let termios: termios = unsafe { mem::uninitialized() }; + let res = unsafe { tcsets(file.as_raw_fd(), &termios) }; + assert_eq!(res, Err(Sys(ENOTTY))); + } + + // FIXME: Find a suitable example for "bad readwrite". + + // From linux/videodev2.h + ioctl!(none log_status with b'V', 70); + #[test] + fn test_ioctl_none() { + let file = tempfile().unwrap(); + let res = unsafe { log_status(file.as_raw_fd()) }; + assert!(res == Err(Sys(ENOTTY)) || res == Err(Sys(ENOSYS))); + } + + #[repr(C)] + pub struct v4l2_audio { + index: u32, + name: [u8; 32], + capability: u32, + mode: u32, + reserved: [u32; 2], + } + + // From linux/videodev2.h + ioctl!(write_ptr s_audio with b'V', 34; v4l2_audio); + #[test] + fn test_ioctl_read() { + let file = tempfile().unwrap(); + let data: v4l2_audio = unsafe { mem::uninitialized() }; + let res = unsafe { g_audio(file.as_raw_fd(), &data) }; + assert!(res == Err(Sys(ENOTTY)) || res == Err(Sys(ENOSYS))); + } + + // From linux/net/bluetooth/hci_sock.h + const HCI_IOC_MAGIC: u8 = b'H'; + const HCI_IOC_HCIDEVUP: u8 = 201; + ioctl!(write_int hcidevup with HCI_IOC_MAGIC, HCI_IOC_HCIDEVUP); + #[test] + fn test_ioctl_write_int() { + let file = tempfile().unwrap(); + let res = unsafe { hcidevup(file.as_raw_fd(), 0) }; + assert!(res == Err(Sys(ENOTTY)) || res == Err(Sys(ENOSYS))); + } + + // From linux/videodev2.h + ioctl!(write_ptr g_audio with b'V', 33; v4l2_audio); + #[test] + fn test_ioctl_write_ptr() { + let file = tempfile().unwrap(); + let mut data: v4l2_audio = unsafe { mem::uninitialized() }; + let res = unsafe { g_audio(file.as_raw_fd(), &mut data) }; + assert!(res == Err(Sys(ENOTTY)) || res == Err(Sys(ENOSYS))); + } + + // From linux/videodev2.h + ioctl!(readwrite enum_audio with b'V', 65; v4l2_audio); + #[test] + fn test_ioctl_readwrite() { + let file = tempfile().unwrap(); + let mut data: v4l2_audio = unsafe { mem::uninitialized() }; + let res = unsafe { enum_audio(file.as_raw_fd(), &mut data) }; + assert!(res == Err(Sys(ENOTTY)) || res == Err(Sys(ENOSYS))); + } + + // FIXME: Find a suitable example for read_buf. + + #[repr(C)] + pub struct spi_ioc_transfer { + tx_buf: u64, + rx_buf: u64, + len: u32, + speed_hz: u32, + delay_usecs: u16, + bits_per_word: u8, + cs_change: u8, + tx_nbits: u8, + rx_nbits: u8, + pad: u16, + } + + // From linux/spi/spidev.h + ioctl!(write_buf spi_ioc_message with super::SPI_IOC_MAGIC, super::SPI_IOC_MESSAGE; spi_ioc_transfer); + #[test] + fn test_ioctl_write_buf() { + let file = tempfile().unwrap(); + let mut data: [spi_ioc_transfer; 4] = unsafe { mem::uninitialized() }; + let res = unsafe { spi_ioc_message(file.as_raw_fd(), &mut data[..]) }; + assert!(res == Err(Sys(ENOTTY)) || res == Err(Sys(ENOSYS))); + } + + // FIXME: Find a suitable example for readwrite_buf. +} + +#[cfg(target_os = "freebsd")] +mod freebsd_ioctls { + use std::mem; + use std::os::unix::io::AsRawFd; + + use tempfile::tempfile; + use libc::termios; + + use nix::Error::Sys; + use nix::errno::ENOTTY; + + // From sys/sys/ttycom.h + const TTY_IOC_MAGIC: u8 = b't'; + const TTY_IOC_TYPE_NXCL: u8 = 14; + const TTY_IOC_TYPE_GETA: u8 = 19; + const TTY_IOC_TYPE_SETA: u8 = 20; + + ioctl!(none tiocnxcl with TTY_IOC_MAGIC, TTY_IOC_TYPE_NXCL); + #[test] + fn test_ioctl_none() { + let file = tempfile().unwrap(); + let res = unsafe { tiocnxcl(file.as_raw_fd()) }; + assert_eq!(res, Err(Sys(ENOTTY))); + } + + ioctl!(read tiocgeta with TTY_IOC_MAGIC, TTY_IOC_TYPE_GETA; termios); + #[test] + fn test_ioctl_read() { + let file = tempfile().unwrap(); + let mut termios = unsafe { mem::uninitialized() }; + let res = unsafe { tiocgeta(file.as_raw_fd(), &mut termios) }; + assert_eq!(res, Err(Sys(ENOTTY))); + } + + ioctl!(write_ptr tiocseta with TTY_IOC_MAGIC, TTY_IOC_TYPE_SETA; termios); + #[test] + fn test_ioctl_write_ptr() { + let file = tempfile().unwrap(); + let termios: termios = unsafe { mem::uninitialized() }; + let res = unsafe { tiocseta(file.as_raw_fd(), &termios) }; + assert_eq!(res, Err(Sys(ENOTTY))); + } +} From 903a52f602054dff1258b5ca04591f0c5edbe08b Mon Sep 17 00:00:00 2001 From: Geoffrey Thomas Date: Tue, 28 Mar 2017 16:55:36 -0400 Subject: [PATCH 25/41] Add WaitStatus::PtraceSyscall for use with PTRACE_O_TRACESYSGOOD The recommended way to trace syscalls with ptrace is to set the PTRACE_O_TRACESYSGOOD option, to distinguish syscall stops from receiving an actual SIGTRAP. In C, this would cause WSTOPSIG to return SIGTRAP | 0x80, but nix wants to parse that as an actual signal. Add another wait status type for syscall stops (in the language of the ptrace(2) manpage, "PTRACE_EVENT stops" and "Syscall-stops" are different things), and mask out bit 0x80 from signals before trying to parse it. Closes #550 --- CHANGELOG.md | 3 +++ src/sys/wait.rs | 19 ++++++++++++++-- test/sys/test_wait.rs | 50 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 70 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 817a89f45e..b7edf991c6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,9 @@ This project adheres to [Semantic Versioning](http://semver.org/). - Added `cfmakeraw`, `cfsetspeed`, and `tcgetsid`. ([#527](https://github.com/nix-rust/nix/pull/527)) - Added "bad none", "bad write_ptr", "bad write_int", and "bad readwrite" variants to the `ioctl!` macro. ([#670](https://github.com/nix-rust/nix/pull/670)) +- On Linux and Android, added support for receiving `PTRACE_O_TRACESYSGOOD` + events from `wait` and `waitpid` using `WaitStatus::PtraceSyscall` + ([#566](https://github.com/nix-rust/nix/pull/566)). ### Changed - Changed `ioctl!(write ...)` into `ioctl!(write_ptr ...)` and `ioctl!(write_int ..)` variants diff --git a/src/sys/wait.rs b/src/sys/wait.rs index ee0beade24..7033422da2 100644 --- a/src/sys/wait.rs +++ b/src/sys/wait.rs @@ -47,6 +47,8 @@ pub enum WaitStatus { Stopped(Pid, Signal), #[cfg(any(target_os = "linux", target_os = "android"))] PtraceEvent(Pid, Signal, c_int), + #[cfg(any(target_os = "linux", target_os = "android"))] + PtraceSyscall(Pid), Continued(Pid), StillAlive } @@ -56,6 +58,7 @@ pub enum WaitStatus { mod status { use sys::signal::Signal; use libc::c_int; + use libc::SIGTRAP; pub fn exited(status: i32) -> bool { (status & 0x7F) == 0 @@ -82,7 +85,17 @@ mod status { } pub fn stop_signal(status: i32) -> Signal { - Signal::from_c_int((status & 0xFF00) >> 8).unwrap() + // Keep only 7 bits of the signal: the high bit + // is used to indicate syscall stops, below. + Signal::from_c_int((status & 0x7F00) >> 8).unwrap() + } + + pub fn syscall_stop(status: i32) -> bool { + // From ptrace(2), setting PTRACE_O_TRACESYSGOOD has the effect + // of delivering SIGTRAP | 0x80 as the signal number for syscall + // stops. This allows easily distinguishing syscall stops from + // genuine SIGTRAP signals. + ((status & 0xFF00) >> 8) == SIGTRAP | 0x80 } pub fn stop_additional(status: i32) -> c_int { @@ -196,7 +209,9 @@ fn decode(pid : Pid, status: i32) -> WaitStatus { if #[cfg(any(target_os = "linux", target_os = "android"))] { fn decode_stopped(pid: Pid, status: i32) -> WaitStatus { let status_additional = status::stop_additional(status); - if status_additional == 0 { + if status::syscall_stop(status) { + WaitStatus::PtraceSyscall(pid) + } else if status_additional == 0 { WaitStatus::Stopped(pid, status::stop_signal(status)) } else { WaitStatus::PtraceEvent(pid, status::stop_signal(status), status::stop_additional(status)) diff --git a/test/sys/test_wait.rs b/test/sys/test_wait.rs index 2e28d9e78f..5f6c923184 100644 --- a/test/sys/test_wait.rs +++ b/test/sys/test_wait.rs @@ -34,3 +34,53 @@ fn test_wait_exit() { Err(_) => panic!("Error: Fork Failed") } } + +#[cfg(any(target_os = "linux", target_os = "android"))] +// FIXME: qemu-user doesn't implement ptrace on most arches +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +mod ptrace { + use nix::sys::ptrace::*; + use nix::sys::ptrace::ptrace::*; + use nix::sys::signal::*; + use nix::sys::wait::*; + use nix::unistd::*; + use nix::unistd::ForkResult::*; + use std::{ptr, process}; + + fn ptrace_child() -> ! { + let _ = ptrace(PTRACE_TRACEME, Pid::from_raw(0), ptr::null_mut(), ptr::null_mut()); + // As recommended by ptrace(2), raise SIGTRAP to pause the child + // until the parent is ready to continue + let _ = raise(SIGTRAP); + process::exit(0) + } + + fn ptrace_parent(child: Pid) { + // Wait for the raised SIGTRAP + assert_eq!(waitpid(child, None), Ok(WaitStatus::Stopped(child, SIGTRAP))); + // We want to test a syscall stop and a PTRACE_EVENT stop + assert!(ptrace_setoptions(child, PTRACE_O_TRACESYSGOOD | PTRACE_O_TRACEEXIT).is_ok()); + + // First, stop on the next system call, which will be exit() + assert!(ptrace(PTRACE_SYSCALL, child, ptr::null_mut(), ptr::null_mut()).is_ok()); + assert_eq!(waitpid(child, None), Ok(WaitStatus::PtraceSyscall(child))); + // Then get the ptrace event for the process exiting + assert!(ptrace(PTRACE_CONT, child, ptr::null_mut(), ptr::null_mut()).is_ok()); + assert_eq!(waitpid(child, None), Ok(WaitStatus::PtraceEvent(child, SIGTRAP, PTRACE_EVENT_EXIT))); + // Finally get the normal wait() result, now that the process has exited + assert!(ptrace(PTRACE_CONT, child, ptr::null_mut(), ptr::null_mut()).is_ok()); + assert_eq!(waitpid(child, None), Ok(WaitStatus::Exited(child, 0))); + } + + #[test] + fn test_wait_ptrace() { + #[allow(unused_variables)] + let m = ::FORK_MTX.lock().expect("Mutex got poisoned by another test"); + + match fork() { + Ok(Child) => ptrace_child(), + Ok(Parent { child }) => ptrace_parent(child), + Err(_) => panic!("Error: Fork Failed") + } + } +} From 228820224a05605977f68d90fe6d86c066a3e282 Mon Sep 17 00:00:00 2001 From: Geoffrey Thomas Date: Mon, 19 Jun 2017 23:51:25 -0400 Subject: [PATCH 26/41] Document WaitStatus and its variants --- src/sys/wait.rs | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/src/sys/wait.rs b/src/sys/wait.rs index 7033422da2..f31f666d6d 100644 --- a/src/sys/wait.rs +++ b/src/sys/wait.rs @@ -40,16 +40,56 @@ libc_bitflags!( target_os = "android"))] const WSTOPPED: WaitPidFlag = WUNTRACED; +/// Possible return values from `wait()` or `waitpid()`. +/// +/// Each status (other than `StillAlive`) describes a state transition +/// in a child process `Pid`, such as the process exiting or stopping, +/// plus additional data about the transition if any. +/// +/// Note that there are two Linux-specific enum variants, `PtraceEvent` +/// and `PtraceSyscall`. Portable code should avoid exhaustively +/// matching on `WaitStatus`. #[derive(Eq, PartialEq, Clone, Copy, Debug)] pub enum WaitStatus { + /// The process exited normally (as with `exit()` or returning from + /// `main`) with the given exit code. This case matches the C macro + /// `WIFEXITED(status)`; the second field is `WEXITSTATUS(status)`. Exited(Pid, i8), + /// The process was killed by the given signal. The third field + /// indicates whether the signal generated a core dump. This case + /// matches the C macro `WIFSIGNALED(status)`; the last two fields + /// correspond to `WTERMSIG(status)` and `WCOREDUMP(status)`. Signaled(Pid, Signal, bool), + /// The process is alive, but was stopped by the given signal. This + /// is only reported if `WaitPidFlag::WUNTRACED` was passed. This + /// case matches the C macro `WIFSTOPPED(status)`; the second field + /// is `WSTOPSIG(status)`. Stopped(Pid, Signal), + /// The traced process was stopped by a `PTRACE_EVENT_*` event. See + /// [`nix::sys::ptrace`] and [`ptrace`(2)] for more information. All + /// currently-defined events use `SIGTRAP` as the signal; the third + /// field is the `PTRACE_EVENT_*` value of the event. + /// + /// [`nix::sys::ptrace`]: ../ptrace/index.html + /// [`ptrace`(2)]: http://man7.org/linux/man-pages/man2/ptrace.2.html #[cfg(any(target_os = "linux", target_os = "android"))] PtraceEvent(Pid, Signal, c_int), + /// The traced process was stopped by execution of a system call, + /// and `PTRACE_O_TRACESYSGOOD` is in effect. See [`ptrace`(2)] for + /// more information. + /// + /// [`ptrace`(2)]: http://man7.org/linux/man-pages/man2/ptrace.2.html #[cfg(any(target_os = "linux", target_os = "android"))] PtraceSyscall(Pid), + /// The process was previously stopped but has resumed execution + /// after receiving a `SIGCONT` signal. This is only reported if + /// `WaitPidFlag::WCONTINUED` was passed. This case matches the C + /// macro `WIFCONTINUED(status)`. Continued(Pid), + /// There are currently no state changes to report in any awaited + /// child process. This is only returned if `WaitPidFlag::WNOHANG` + /// was used (otherwise `wait()` or `waitpid()` would block until + /// there was something to report). StillAlive } From 907bb9811370bccae55582b45c6e0b93dbd7b8ad Mon Sep 17 00:00:00 2001 From: Nicolas Dusart Date: Thu, 20 Jul 2017 16:41:13 +0200 Subject: [PATCH 27/41] use std::env::temp_dir() to retrieve the temp directory in test_mkstemp --- test/test_unistd.rs | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/test/test_unistd.rs b/test/test_unistd.rs index 7ae01febc5..035af52cdc 100644 --- a/test/test_unistd.rs +++ b/test/test_unistd.rs @@ -4,7 +4,7 @@ use nix::unistd::*; use nix::unistd::ForkResult::*; use nix::sys::wait::*; use nix::sys::stat; -use std::iter; +use std::{env, iter}; use std::ffi::CString; use std::fs::File; use std::io::Write; @@ -64,12 +64,10 @@ fn test_wait() { #[test] fn test_mkstemp() { - #[cfg(target_os = "android")] - let tmp = "/data/local/tmp/"; - #[cfg(not(target_os = "android"))] - let tmp = "/tmp/"; + let mut path = env::temp_dir(); + path.push("nix_tempfile.XXXXXX"); - let result = mkstemp((tmp.to_owned() + "nix_tempfile.XXXXXX").as_str()); + let result = mkstemp(&path); match result { Ok((fd, path)) => { close(fd).unwrap(); @@ -77,14 +75,12 @@ fn test_mkstemp() { }, Err(e) => panic!("mkstemp failed: {}", e) } +} - let result = mkstemp(tmp); - match result { - Ok(_) => { - panic!("mkstemp succeeded even though it should fail (provided a directory)"); - }, - Err(_) => {} - } +#[test] +fn test_mkstemp_directory() { + // mkstemp should fail if a directory is given + assert!(mkstemp(&env::temp_dir()).is_err()); } #[test] From a162ea27e593631fbc39274301f7dcba6eed06cb Mon Sep 17 00:00:00 2001 From: roblabla Date: Sun, 9 Jul 2017 00:52:20 +0200 Subject: [PATCH 28/41] Allow doc attributes in ioctl macro --- src/sys/ioctl/mod.rs | 67 ++++++++++++++++++++++++++++++++++-------- test/sys/test_ioctl.rs | 54 ++++++++++++++++++++++++++++++++++ 2 files changed, 108 insertions(+), 13 deletions(-) diff --git a/src/sys/ioctl/mod.rs b/src/sys/ioctl/mod.rs index 4db3fe760b..b29c8b422b 100644 --- a/src/sys/ioctl/mod.rs +++ b/src/sys/ioctl/mod.rs @@ -197,6 +197,34 @@ //! of lines defining macros which use `_IO`, `_IOR`, `_IOW`, `_IOC`, and `_IORW`. Some `ioctl`s are //! documented directly in the headers defining their constants, but others have more extensive //! documentation in man pages (like termios' `ioctl`s which are in `tty_ioctl(4)`). +//! +//! Documenting the generated functions +//! =================================== +//! +//! In many cases, users will wish for the functions generated by the `ioctl` +//! macro to be public and documented. For this reason, the generated functions +//! are public by default. If you wish to hide the ioctl, you will need to put +//! them in a private module. +//! +//! For documentation, it is possible to use doc comments inside the `ioctl!` +//! macro. Here is an example : +//! +//! ``` +//! # #[macro_use] extern crate nix; +//! # use nix::libc::c_int; +//! ioctl! { +//! /// Make the given terminal the controlling terminal of the calling process. The calling +//! /// process must be a session leader and not have a controlling terminal already. If the +//! /// terminal is already the controlling terminal of a different session group then the +//! /// ioctl will fail with **EPERM**, unless the caller is root (more precisely: has the +//! /// **CAP_SYS_ADMIN** capability) and arg equals 1, in which case the terminal is stolen +//! /// and all processes that had it as controlling terminal lose it. +//! read tiocsctty with b't', 19; c_int +//! } +//! +//! # fn main() {} +//! ``` +//! #[cfg(any(target_os = "linux", target_os = "android"))] #[path = "platform/linux.rs"] #[macro_use] @@ -228,89 +256,102 @@ macro_rules! convert_ioctl_res { /// Generates ioctl functions. See [::sys::ioctl](sys/ioctl/index.html). #[macro_export] macro_rules! ioctl { - (bad none $name:ident with $nr:expr) => ( + ($(#[$attr:meta])* bad none $name:ident with $nr:expr) => ( + $(#[$attr])* pub unsafe fn $name(fd: $crate::libc::c_int) -> $crate::Result<$crate::libc::c_int> { convert_ioctl_res!($crate::libc::ioctl(fd, $nr as $crate::sys::ioctl::ioctl_num_type)) } ); - (bad read $name:ident with $nr:expr; $ty:ty) => ( + ($(#[$attr:meta])* bad read $name:ident with $nr:expr; $ty:ty) => ( + $(#[$attr])* pub unsafe fn $name(fd: $crate::libc::c_int, data: *mut $ty) -> $crate::Result<$crate::libc::c_int> { convert_ioctl_res!($crate::libc::ioctl(fd, $nr as $crate::sys::ioctl::ioctl_num_type, data)) } ); - (bad write_ptr $name:ident with $nr:expr; $ty:ty) => ( + ($(#[$attr:meta])* bad write_ptr $name:ident with $nr:expr; $ty:ty) => ( + $(#[$attr])* pub unsafe fn $name(fd: $crate::libc::c_int, data: *const $ty) -> $crate::Result<$crate::libc::c_int> { convert_ioctl_res!($crate::libc::ioctl(fd, $nr as $crate::sys::ioctl::ioctl_num_type, data)) } ); - (bad write_int $name:ident with $nr:expr) => ( + ($(#[$attr:meta])* bad write_int $name:ident with $nr:expr) => ( + $(#[$attr])* pub unsafe fn $name(fd: $crate::libc::c_int, data: $crate::libc::c_int) -> $crate::Result<$crate::libc::c_int> { convert_ioctl_res!($crate::libc::ioctl(fd, $nr as $crate::sys::ioctl::ioctl_num_type, data)) } ); - (bad readwrite $name:ident with $nr:expr; $ty:ty) => ( + ($(#[$attr:meta])* bad readwrite $name:ident with $nr:expr; $ty:ty) => ( + $(#[$attr])* pub unsafe fn $name(fd: $crate::libc::c_int, data: *mut $ty) -> $crate::Result<$crate::libc::c_int> { convert_ioctl_res!($crate::libc::ioctl(fd, $nr as $crate::sys::ioctl::ioctl_num_type, data)) } ); - (none $name:ident with $ioty:expr, $nr:expr) => ( + ($(#[$attr:meta])* none $name:ident with $ioty:expr, $nr:expr) => ( + $(#[$attr])* pub unsafe fn $name(fd: $crate::libc::c_int) -> $crate::Result<$crate::libc::c_int> { convert_ioctl_res!($crate::libc::ioctl(fd, io!($ioty, $nr) as $crate::sys::ioctl::ioctl_num_type)) } ); - (read $name:ident with $ioty:expr, $nr:expr; $ty:ty) => ( + ($(#[$attr:meta])* read $name:ident with $ioty:expr, $nr:expr; $ty:ty) => ( + $(#[$attr])* pub unsafe fn $name(fd: $crate::libc::c_int, data: *mut $ty) -> $crate::Result<$crate::libc::c_int> { convert_ioctl_res!($crate::libc::ioctl(fd, ior!($ioty, $nr, ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::ioctl_num_type, data)) } ); - (write_ptr $name:ident with $ioty:expr, $nr:expr; $ty:ty) => ( + ($(#[$attr:meta])* write_ptr $name:ident with $ioty:expr, $nr:expr; $ty:ty) => ( + $(#[$attr])* pub unsafe fn $name(fd: $crate::libc::c_int, data: *const $ty) -> $crate::Result<$crate::libc::c_int> { convert_ioctl_res!($crate::libc::ioctl(fd, iow!($ioty, $nr, ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::ioctl_num_type, data)) } ); - (write_int $name:ident with $ioty:expr, $nr:expr) => ( + ($(#[$attr:meta])* write_int $name:ident with $ioty:expr, $nr:expr) => ( + $(#[$attr])* pub unsafe fn $name(fd: $crate::libc::c_int, data: $crate::libc::c_int) -> $crate::Result<$crate::libc::c_int> { convert_ioctl_res!($crate::libc::ioctl(fd, iow!($ioty, $nr, ::std::mem::size_of::<$crate::libc::c_int>()) as $crate::sys::ioctl::ioctl_num_type, data)) } ); - (readwrite $name:ident with $ioty:expr, $nr:expr; $ty:ty) => ( + ($(#[$attr:meta])* readwrite $name:ident with $ioty:expr, $nr:expr; $ty:ty) => ( + $(#[$attr])* pub unsafe fn $name(fd: $crate::libc::c_int, data: *mut $ty) -> $crate::Result<$crate::libc::c_int> { convert_ioctl_res!($crate::libc::ioctl(fd, iorw!($ioty, $nr, ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::ioctl_num_type, data)) } ); - (read_buf $name:ident with $ioty:expr, $nr:expr; $ty:ty) => ( + ($(#[$attr:meta])* read_buf $name:ident with $ioty:expr, $nr:expr; $ty:ty) => ( + $(#[$attr])* pub unsafe fn $name(fd: $crate::libc::c_int, data: &mut [$ty]) -> $crate::Result<$crate::libc::c_int> { convert_ioctl_res!($crate::libc::ioctl(fd, ior!($ioty, $nr, data.len() * ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::ioctl_num_type, data)) } ); - (write_buf $name:ident with $ioty:expr, $nr:expr; $ty:ty) => ( + ($(#[$attr:meta])* write_buf $name:ident with $ioty:expr, $nr:expr; $ty:ty) => ( + $(#[$attr])* pub unsafe fn $name(fd: $crate::libc::c_int, data: &[$ty]) -> $crate::Result<$crate::libc::c_int> { convert_ioctl_res!($crate::libc::ioctl(fd, iow!($ioty, $nr, data.len() * ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::ioctl_num_type, data)) } ); - (readwrite_buf $name:ident with $ioty:expr, $nr:expr; $ty:ty) => ( + ($(#[$attr:meta])* readwrite_buf $name:ident with $ioty:expr, $nr:expr; $ty:ty) => ( + $(#[$attr])* pub unsafe fn $name(fd: $crate::libc::c_int, data: &mut [$ty]) -> $crate::Result<$crate::libc::c_int> { diff --git a/test/sys/test_ioctl.rs b/test/sys/test_ioctl.rs index 38ecdc99af..bd275a97d3 100644 --- a/test/sys/test_ioctl.rs +++ b/test/sys/test_ioctl.rs @@ -22,6 +22,60 @@ ioctl!(write_buf writebuf_test_u32 with 0, 0; u32); ioctl!(write_buf writebuf_test_u64 with 0, 0; u64); ioctl!(readwrite_buf readwritebuf_test with 0, 0; u32); +// Make sure documentation works +ioctl! { + /// This documents the ioctl function + bad none do_bad_docs with 0x1234 +} +ioctl! { + /// This documents the ioctl function + bad read do_bad_read_docs with 0x1234; u16 +} +ioctl! { + /// This documents the ioctl function + bad write_int do_bad_write_int_docs with 0x1234 +} +ioctl! { + /// This documents the ioctl function + bad write_ptr do_bad_write_ptr_docs with 0x1234; u8 +} +ioctl! { + /// This documents the ioctl function + bad readwrite do_bad_readwrite_docs with 0x1234; u32 +} +ioctl! { + /// This documents the ioctl function + none do_none_docs with 0, 0 +} +ioctl! { + /// This documents the ioctl function + read do_read_docs with 0, 0; u32 +} +ioctl! { + /// This documents the ioctl function + write_int do_write_int_docs with 0, 0 +} +ioctl! { + /// This documents the ioctl function + write_ptr do_write_ptr_docs with 0, 0; u32 +} +ioctl! { + /// This documents the ioctl function + readwrite do_readwrite_docs with 0, 0; u32 +} +ioctl! { + /// This documents the ioctl function + read_buf do_read_buf_docs with 0, 0; u32 +} +ioctl! { + /// This documents the ioctl function + write_buf do_write_buf_docs with 0, 0; u32 +} +ioctl! { + /// This documents the ioctl function + readwrite_buf do_readwrite_buf_docs with 0, 0; u32 +} + // See C code for source of values for op calculations (does NOT work for mips/powerpc): // https://gist.github.com/posborne/83ea6880770a1aef332e // From 745a4abbfd5779c38b09b98d6b91c098221d5b90 Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Thu, 20 Jul 2017 19:55:13 +0200 Subject: [PATCH 29/41] Document invariants of fork and fix tests Some tests were invoking non-async-signal-safe functions from the child process after a `fork`. Since they might be invoked in parallel, this could lead to problems. --- src/unistd.rs | 16 ++++++++++++++-- test/sys/test_wait.rs | 8 +++++--- test/test_unistd.rs | 14 ++++++++++---- 3 files changed, 29 insertions(+), 9 deletions(-) diff --git a/src/unistd.rs b/src/unistd.rs index 25237a07ef..19f82ef6db 100644 --- a/src/unistd.rs +++ b/src/unistd.rs @@ -191,6 +191,18 @@ impl ForkResult { /// Continuing execution in parent process, new child has pid: 1234 /// I'm a new child process /// ``` +/// +/// # Safety +/// +/// In a multithreaded program, only [async-signal-safe] functions like `pause` +/// and `_exit` may be called by the child (the parent isn't restricted). Note +/// that memory allocation may **not** be async-signal-safe and thus must be +/// prevented. +/// +/// Those functions are only a small subset of your operating system's API, so +/// special care must be taken to only invoke code you can control and audit. +/// +/// [async-signal-safe]: http://man7.org/linux/man-pages/man7/signal-safety.7.html #[inline] pub fn fork() -> Result { use self::ForkResult::*; @@ -687,7 +699,7 @@ pub fn gethostname<'a>(buffer: &'a mut [u8]) -> Result<&'a CStr> { /// use nix::unistd::close; /// /// fn main() { -/// let mut f = tempfile::tempfile().unwrap(); +/// let f = tempfile::tempfile().unwrap(); /// close(f.as_raw_fd()).unwrap(); // Bad! f will also close on drop! /// } /// ``` @@ -700,7 +712,7 @@ pub fn gethostname<'a>(buffer: &'a mut [u8]) -> Result<&'a CStr> { /// use nix::unistd::close; /// /// fn main() { -/// let mut f = tempfile::tempfile().unwrap(); +/// let f = tempfile::tempfile().unwrap(); /// close(f.into_raw_fd()).unwrap(); // Good. into_raw_fd consumes f /// } /// ``` diff --git a/test/sys/test_wait.rs b/test/sys/test_wait.rs index 5f6c923184..e1d3464b95 100644 --- a/test/sys/test_wait.rs +++ b/test/sys/test_wait.rs @@ -2,15 +2,16 @@ use nix::unistd::*; use nix::unistd::ForkResult::*; use nix::sys::signal::*; use nix::sys::wait::*; -use libc::exit; +use libc::_exit; #[test] fn test_wait_signal() { #[allow(unused_variables)] let m = ::FORK_MTX.lock().expect("Mutex got poisoned by another test"); + // Safe: The child only calls `pause` and/or `_exit`, which are async-signal-safe. match fork() { - Ok(Child) => pause().unwrap_or(()), + Ok(Child) => pause().unwrap_or_else(|_| unsafe { _exit(123) }), Ok(Parent { child }) => { kill(child, Some(SIGKILL)).ok().expect("Error: Kill Failed"); assert_eq!(waitpid(child, None), Ok(WaitStatus::Signaled(child, SIGKILL, false))); @@ -25,8 +26,9 @@ fn test_wait_exit() { #[allow(unused_variables)] let m = ::FORK_MTX.lock().expect("Mutex got poisoned by another test"); + // Safe: Child only calls `_exit`, which is async-signal-safe. match fork() { - Ok(Child) => unsafe { exit(12); }, + Ok(Child) => unsafe { _exit(12); }, Ok(Parent { child }) => { assert_eq!(waitpid(child, None), Ok(WaitStatus::Exited(child, 12))); }, diff --git a/test/test_unistd.rs b/test/test_unistd.rs index 035af52cdc..4de56826b3 100644 --- a/test/test_unistd.rs +++ b/test/test_unistd.rs @@ -11,16 +11,17 @@ use std::io::Write; use std::os::unix::prelude::*; use tempfile::tempfile; use tempdir::TempDir; -use libc::off_t; -use std::process::exit; +use libc::{_exit, off_t}; #[test] fn test_fork_and_waitpid() { #[allow(unused_variables)] let m = ::FORK_MTX.lock().expect("Mutex got poisoned by another test"); + + // Safe: Child only calls `_exit`, which is signal-safe let pid = fork(); match pid { - Ok(Child) => exit(0), + Ok(Child) => unsafe { _exit(0) }, Ok(Parent { child }) => { // assert that child was created and pid > 0 let child_raw: ::libc::pid_t = child.into(); @@ -48,9 +49,11 @@ fn test_wait() { // Grab FORK_MTX so wait doesn't reap a different test's child process #[allow(unused_variables)] let m = ::FORK_MTX.lock().expect("Mutex got poisoned by another test"); + + // Safe: Child only calls `_exit`, which is signal-safe let pid = fork(); match pid { - Ok(Child) => exit(0), + Ok(Child) => unsafe { _exit(0) }, Ok(Parent { child }) => { let wait_status = wait(); @@ -112,6 +115,9 @@ macro_rules! execve_test_factory( // data from `reader`. let (reader, writer) = pipe().unwrap(); + // Safe: Child calls `exit`, `dup`, `close` and the provided `exec*` family function. + // NOTE: Technically, this makes the macro unsafe to use because you could pass anything. + // The tests make sure not to do that, though. match fork().unwrap() { Child => { #[cfg(not(target_os = "android"))] From 4cefd538497e4dc8cfa51bad286541e2336b774c Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Sat, 22 Jul 2017 18:03:10 +0200 Subject: [PATCH 30/41] Remove unneeded local --- test/test_unistd.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/test_unistd.rs b/test/test_unistd.rs index 4de56826b3..944232caaf 100644 --- a/test/test_unistd.rs +++ b/test/test_unistd.rs @@ -19,8 +19,7 @@ fn test_fork_and_waitpid() { let m = ::FORK_MTX.lock().expect("Mutex got poisoned by another test"); // Safe: Child only calls `_exit`, which is signal-safe - let pid = fork(); - match pid { + match fork() { Ok(Child) => unsafe { _exit(0) }, Ok(Parent { child }) => { // assert that child was created and pid > 0 From 536787e37d86f692b915ab1aa62c6ad793531eba Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Sat, 22 Jul 2017 18:07:08 +0200 Subject: [PATCH 31/41] Replace remaining process::exit with libc::_exit Note that ptrace isn't documented as signal-safe, but it's supposed to just be a light syscall wrapper, so it should be fine. --- test/sys/test_wait.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/sys/test_wait.rs b/test/sys/test_wait.rs index e1d3464b95..210e6bc8ce 100644 --- a/test/sys/test_wait.rs +++ b/test/sys/test_wait.rs @@ -48,13 +48,14 @@ mod ptrace { use nix::unistd::*; use nix::unistd::ForkResult::*; use std::{ptr, process}; + use libc::_exit; fn ptrace_child() -> ! { let _ = ptrace(PTRACE_TRACEME, Pid::from_raw(0), ptr::null_mut(), ptr::null_mut()); // As recommended by ptrace(2), raise SIGTRAP to pause the child // until the parent is ready to continue let _ = raise(SIGTRAP); - process::exit(0) + unsafe { _exit(0) } } fn ptrace_parent(child: Pid) { From 283fb1c722a1ccf120ba851da40f36f58f049d12 Mon Sep 17 00:00:00 2001 From: Alan Somers Date: Sun, 23 Jul 2017 19:05:49 -0600 Subject: [PATCH 32/41] Add CHANGELOG entry for PR #661 --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b7edf991c6..0bc162c3da 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,6 +31,8 @@ This project adheres to [Semantic Versioning](http://semver.org/). ([#566](https://github.com/nix-rust/nix/pull/566)). ### Changed +- The `ioctl!` macro and its variants now allow the generated functions to have + doccomments. ([#661](https://github.com/nix-rust/nix/pull/661)) - Changed `ioctl!(write ...)` into `ioctl!(write_ptr ...)` and `ioctl!(write_int ..)` variants to more clearly separate those use cases. ([#670](https://github.com/nix-rust/nix/pull/670)) - Marked `sys::mman::{ mmap, munmap, madvise, munlock, msync }` as unsafe. From f3167db18b03cb820d35b9cca46c51db0f8480b3 Mon Sep 17 00:00:00 2001 From: Alan Somers Date: Sun, 23 Jul 2017 21:11:17 -0600 Subject: [PATCH 33/41] Release 0.9.0 --- CHANGELOG.md | 2 +- Cargo.toml | 2 +- README.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0bc162c3da..949b98f2a3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). -## [Unreleased] +## [0.9.0] 2017-07-23 ### Added - Added `sysconf`, `pathconf`, and `fpathconf` diff --git a/Cargo.toml b/Cargo.toml index 15213b4d32..1df77c53ca 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ name = "nix" description = "Rust friendly bindings to *nix APIs" -version = "0.9.0-pre" +version = "0.9.0" authors = ["The nix-rust Project Developers"] repository = "https://github.com/nix-rust/nix" license = "MIT" diff --git a/README.md b/README.md index 2a72e0a825..c25040d9f5 100644 --- a/README.md +++ b/README.md @@ -92,7 +92,7 @@ To use `nix`, first add this to your `Cargo.toml`: ```toml [dependencies] -nix = "0.8.0" +nix = "0.9.0" ``` Then, add this to your crate root: From 021e851084e57c5a9b521a1bb2f7e8c6ca92c0cb Mon Sep 17 00:00:00 2001 From: Alan Somers Date: Sun, 23 Jul 2017 22:33:21 -0600 Subject: [PATCH 34/41] Bump version to 0.10.0-pre --- CHANGELOG.md | 2 ++ Cargo.toml | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 949b98f2a3..97d8350464 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,8 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). +## [Unreleased] + ## [0.9.0] 2017-07-23 ### Added diff --git a/Cargo.toml b/Cargo.toml index 1df77c53ca..23b1345587 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ name = "nix" description = "Rust friendly bindings to *nix APIs" -version = "0.9.0" +version = "0.10.0-pre" authors = ["The nix-rust Project Developers"] repository = "https://github.com/nix-rust/nix" license = "MIT" @@ -15,7 +15,7 @@ exclude = [ ] [dependencies] -libc = "0.2.25" +libc = { git = "https://github.com/rust-lang/libc" } bitflags = "0.9" cfg-if = "0.1.0" void = "1.0.2" From 8beaea26326e75ed1b0e5160566be4bb952e777e Mon Sep 17 00:00:00 2001 From: Bryant Mairs Date: Sun, 9 Jul 2017 12:13:32 -0700 Subject: [PATCH 35/41] Move Tier 3s to Tier 2 --- .travis.yml | 10 ---------- README.md | 10 ++++------ 2 files changed, 4 insertions(+), 16 deletions(-) diff --git a/.travis.yml b/.travis.yml index 935cb1ae2c..5595a23786 100644 --- a/.travis.yml +++ b/.travis.yml @@ -102,16 +102,6 @@ matrix: - env: TARGET=x86_64-unknown-linux-gnu rust: beta - # Planning to add these targets, but they can fail for now - - env: TARGET=mips64-unknown-linux-gnuabi64 - rust: 1.13.0 - - env: TARGET=mips64el-unknown-linux-gnuabi64 - rust: 1.13.0 - - env: TARGET=arm-unknown-linux-musleabi - rust: 1.14.0 - - env: TARGET=s390x-unknown-linux-gnu - rust: 1.13.0 - before_install: set -e install: diff --git a/README.md b/README.md index c25040d9f5..e83e6ffa41 100644 --- a/README.md +++ b/README.md @@ -69,23 +69,21 @@ Tier 2: * aarch64-apple-ios * aarch64-linux-android * arm-linux-androideabi + * arm-unknown-linux-musleabi (requires Rust >= 1.14) * armv7-apple-ios * armv7-linux-androideabi * armv7s-apple-ios * i386-apple-ios * i686-linux-android (requires Rust >= 1.18) * i686-unknown-freebsd + * mips64-unknown-linux-gnuabi64 + * mips64el-unknown-linux-gnuabi64 * powerpc-unknown-linux-gnu + * s390x-unknown-linux-gnu * x86_64-apple-ios * x86_64-linux-android (requires Rust >= 1.18) * x86_64-unknown-netbsd -Tier 3: - * arm-unknown-linux-musleabi (requires Rust >= 1.14) - * mips64-unknown-linux-gnuabi64 - * mips64el-unknown-linux-gnuabi64 - * s390x-unknown-linux-gnu - ## Usage To use `nix`, first add this to your `Cargo.toml`: From 46191fdbd8c26009dbd8889a6a3495643e5379a4 Mon Sep 17 00:00:00 2001 From: Bryant Mairs Date: Sun, 9 Jul 2017 13:19:29 -0700 Subject: [PATCH 36/41] Disable tests on s390x When tests are run in QEMU the job just times out --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 5595a23786..21924ae617 100644 --- a/.travis.yml +++ b/.travis.yml @@ -70,7 +70,7 @@ matrix: rust: 1.13.0 - env: TARGET=powerpc64le-unknown-linux-gnu rust: 1.13.0 - - env: TARGET=s390x-unknown-linux-gnu + - env: TARGET=s390x-unknown-linux-gnu DISABLE_TESTS=1 rust: 1.13.0 - env: TARGET=x86_64-unknown-linux-gnu rust: 1.13.0 From a7170033961fc27721f242ef27c2c2b76d4d943e Mon Sep 17 00:00:00 2001 From: Bryant Mairs Date: Sun, 9 Jul 2017 13:19:41 -0700 Subject: [PATCH 37/41] Disable failing tests on mips64 These are assumed to be QEMU issues, as they also fail on mips. --- README.md | 4 ++-- test/sys/test_aio.rs | 6 +++--- test/sys/test_ioctl.rs | 10 +++++----- test/test_mq.rs | 4 ++-- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index e83e6ffa41..3fd19a9293 100644 --- a/README.md +++ b/README.md @@ -57,6 +57,8 @@ Tier 1: * i686-unknown-linux-gnu * i686-unknown-linux-musl * mips-unknown-linux-gnu + * mips64-unknown-linux-gnuabi64 + * mips64el-unknown-linux-gnuabi64 * mipsel-unknown-linux-gnu * powerpc64-unknown-linux-gnu * powerpc64le-unknown-linux-gnu @@ -76,8 +78,6 @@ Tier 2: * i386-apple-ios * i686-linux-android (requires Rust >= 1.18) * i686-unknown-freebsd - * mips64-unknown-linux-gnuabi64 - * mips64el-unknown-linux-gnuabi64 * powerpc-unknown-linux-gnu * s390x-unknown-linux-gnu * x86_64-apple-ios diff --git a/test/sys/test_aio.rs b/test/sys/test_aio.rs index 54ee5a96a8..e4b48b8dec 100644 --- a/test/sys/test_aio.rs +++ b/test/sys/test_aio.rs @@ -241,7 +241,7 @@ extern fn sigfunc(_: c_int) { // Test an aio operation with completion delivered by a signal // FIXME: This test is ignored on mips because of failures in qemu in CI #[test] -#[cfg_attr(any(all(target_env = "musl", target_arch = "x86_64"), target_arch = "mips"), ignore)] +#[cfg_attr(any(all(target_env = "musl", target_arch = "x86_64"), target_arch = "mips", target_arch = "mips64"), ignore)] fn test_write_sigev_signal() { #[allow(unused_variables)] let m = ::SIGNAL_MTX.lock().expect("Mutex got poisoned by another test"); @@ -369,10 +369,10 @@ fn test_lio_listio_nowait() { // Test lio_listio with LIO_NOWAIT and a SigEvent to indicate when all AioCb's // are complete. -// FIXME: This test is ignored on mips because of failures in qemu in CI. +// FIXME: This test is ignored on mips/mips64 because of failures in qemu in CI. #[test] #[cfg(not(any(target_os = "ios", target_os = "macos")))] -#[cfg_attr(any(target_arch = "mips", target_env = "musl"), ignore)] +#[cfg_attr(any(target_arch = "mips", target_arch = "mips64", target_env = "musl"), ignore)] fn test_lio_listio_signal() { #[allow(unused_variables)] let m = ::SIGNAL_MTX.lock().expect("Mutex got poisoned by another test"); diff --git a/test/sys/test_ioctl.rs b/test/sys/test_ioctl.rs index bd275a97d3..1b9e464b97 100644 --- a/test/sys/test_ioctl.rs +++ b/test/sys/test_ioctl.rs @@ -86,7 +86,7 @@ ioctl! { mod linux { #[test] fn test_op_none() { - if cfg!(any(target_arch = "mips", target_arch="powerpc", target_arch="powerpc64")){ + if cfg!(any(target_arch = "mips", target_arch = "mips64", target_arch="powerpc", target_arch="powerpc64")){ assert_eq!(io!(b'q', 10), 0x2000710A); assert_eq!(io!(b'a', 255), 0x200061FF); } else { @@ -97,7 +97,7 @@ mod linux { #[test] fn test_op_write() { - if cfg!(any(target_arch = "mips", target_arch="powerpc", target_arch="powerpc64")){ + if cfg!(any(target_arch = "mips", target_arch = "mips64", target_arch="powerpc", target_arch="powerpc64")){ assert_eq!(iow!(b'z', 10, 1), 0x80017A0A); assert_eq!(iow!(b'z', 10, 512), 0x82007A0A); } else { @@ -109,7 +109,7 @@ mod linux { #[cfg(target_pointer_width = "64")] #[test] fn test_op_write_64() { - if cfg!(any(target_arch="powerpc64")){ + if cfg!(any(target_arch = "mips64", target_arch="powerpc64")){ assert_eq!(iow!(b'z', 10, (1 as u64) << 32), 0x80007A0A); } else { assert_eq!(iow!(b'z', 10, (1 as u64) << 32), 0x40007A0A); @@ -119,7 +119,7 @@ mod linux { #[test] fn test_op_read() { - if cfg!(any(target_arch = "mips", target_arch="powerpc", target_arch="powerpc64")){ + if cfg!(any(target_arch = "mips", target_arch = "mips64", target_arch="powerpc", target_arch="powerpc64")){ assert_eq!(ior!(b'z', 10, 1), 0x40017A0A); assert_eq!(ior!(b'z', 10, 512), 0x42007A0A); } else { @@ -131,7 +131,7 @@ mod linux { #[cfg(target_pointer_width = "64")] #[test] fn test_op_read_64() { - if cfg!(any(target_arch="powerpc64")){ + if cfg!(any(target_arch = "mips64", target_arch="powerpc64")){ assert_eq!(ior!(b'z', 10, (1 as u64) << 32), 0x40007A0A); } else { assert_eq!(ior!(b'z', 10, (1 as u64) << 32), 0x80007A0A); diff --git a/test/test_mq.rs b/test/test_mq.rs index 08edef78d7..39e6b62ac4 100644 --- a/test/test_mq.rs +++ b/test/test_mq.rs @@ -50,7 +50,7 @@ fn test_mq_getattr() { // FIXME: Fix failures for mips in QEMU #[test] -#[cfg_attr(target_arch = "mips", ignore)] +#[cfg_attr(any(target_arch = "mips", target_arch = "mips64"), ignore)] fn test_mq_setattr() { const MSG_SIZE: c_long = 32; let initial_attr = MqAttr::new(0, 10, MSG_SIZE, 0); @@ -78,7 +78,7 @@ fn test_mq_setattr() { // FIXME: Fix failures for mips in QEMU #[test] -#[cfg_attr(target_arch = "mips", ignore)] +#[cfg_attr(any(target_arch = "mips", target_arch = "mips64"), ignore)] fn test_mq_set_nonblocking() { const MSG_SIZE: c_long = 32; let initial_attr = MqAttr::new(0, 10, MSG_SIZE, 0); From ce60aa5ad81a0e1b4cc638592492bf72ec830db6 Mon Sep 17 00:00:00 2001 From: Bryant Mairs Date: Sun, 9 Jul 2017 16:22:15 -0700 Subject: [PATCH 38/41] Disable testing because of musl #include issues --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 21924ae617..6f069c7f46 100644 --- a/.travis.yml +++ b/.travis.yml @@ -48,7 +48,7 @@ matrix: rust: 1.13.0 - env: TARGET=arm-unknown-linux-gnueabi rust: 1.13.0 - - env: TARGET=arm-unknown-linux-musleabi + - env: TARGET=arm-unknown-linux-musleabi DISABLE_TESTS=1 rust: 1.14.0 - env: TARGET=armv7-unknown-linux-gnueabihf rust: 1.13.0 From 1844378250ea9a4aacf438ea8eef586f8b8d8951 Mon Sep 17 00:00:00 2001 From: Marcin Mielniczuk Date: Tue, 25 Jul 2017 09:12:29 +0200 Subject: [PATCH 39/41] Add a changelog entry --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 97d8350464..d5064c9c8c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,9 @@ This project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +### Changed +- Renamed existing `ptrace` wrappers to encourage namespacing ([#692](https://github.com/nix-rust/nix/pull/692)) + ## [0.9.0] 2017-07-23 ### Added From 9cf824810768734ea0187bf9e52a20ae332910b8 Mon Sep 17 00:00:00 2001 From: Marcin Mielniczuk Date: Tue, 25 Jul 2017 09:19:23 +0200 Subject: [PATCH 40/41] Fix the tests compiling --- test/sys/test_wait.rs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/test/sys/test_wait.rs b/test/sys/test_wait.rs index 210e6bc8ce..9300d6bf44 100644 --- a/test/sys/test_wait.rs +++ b/test/sys/test_wait.rs @@ -41,7 +41,7 @@ fn test_wait_exit() { // FIXME: qemu-user doesn't implement ptrace on most arches #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] mod ptrace { - use nix::sys::ptrace::*; + use nix::sys::ptrace; use nix::sys::ptrace::ptrace::*; use nix::sys::signal::*; use nix::sys::wait::*; @@ -51,7 +51,8 @@ mod ptrace { use libc::_exit; fn ptrace_child() -> ! { - let _ = ptrace(PTRACE_TRACEME, Pid::from_raw(0), ptr::null_mut(), ptr::null_mut()); + // TODO ptrace::traceme will be added in #666 + let _ = ptrace::ptrace(PTRACE_TRACEME, Pid::from_raw(0), ptr::null_mut(), ptr::null_mut()); // As recommended by ptrace(2), raise SIGTRAP to pause the child // until the parent is ready to continue let _ = raise(SIGTRAP); @@ -62,16 +63,18 @@ mod ptrace { // Wait for the raised SIGTRAP assert_eq!(waitpid(child, None), Ok(WaitStatus::Stopped(child, SIGTRAP))); // We want to test a syscall stop and a PTRACE_EVENT stop - assert!(ptrace_setoptions(child, PTRACE_O_TRACESYSGOOD | PTRACE_O_TRACEEXIT).is_ok()); + assert!(ptrace::setoptions(child, PTRACE_O_TRACESYSGOOD | PTRACE_O_TRACEEXIT).is_ok()); // First, stop on the next system call, which will be exit() - assert!(ptrace(PTRACE_SYSCALL, child, ptr::null_mut(), ptr::null_mut()).is_ok()); + // TODO ptrace::syscall will be added in #666 + assert!(ptrace::ptrace(PTRACE_SYSCALL, child, ptr::null_mut(), ptr::null_mut()).is_ok()); assert_eq!(waitpid(child, None), Ok(WaitStatus::PtraceSyscall(child))); // Then get the ptrace event for the process exiting - assert!(ptrace(PTRACE_CONT, child, ptr::null_mut(), ptr::null_mut()).is_ok()); + // TODO ptrace::cont will be added in #666 + assert!(ptrace::ptrace(PTRACE_CONT, child, ptr::null_mut(), ptr::null_mut()).is_ok()); assert_eq!(waitpid(child, None), Ok(WaitStatus::PtraceEvent(child, SIGTRAP, PTRACE_EVENT_EXIT))); // Finally get the normal wait() result, now that the process has exited - assert!(ptrace(PTRACE_CONT, child, ptr::null_mut(), ptr::null_mut()).is_ok()); + assert!(ptrace::ptrace(PTRACE_CONT, child, ptr::null_mut(), ptr::null_mut()).is_ok()); assert_eq!(waitpid(child, None), Ok(WaitStatus::Exited(child, 0))); } From bf93180babacbe0d82543f9c5d487cbffdb47546 Mon Sep 17 00:00:00 2001 From: Marcin Mielniczuk Date: Tue, 25 Jul 2017 17:52:52 +0200 Subject: [PATCH 41/41] Remove the TODO comments, add unwrap() for ptrace. --- test/sys/test_wait.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/test/sys/test_wait.rs b/test/sys/test_wait.rs index 9300d6bf44..8d2ca85fda 100644 --- a/test/sys/test_wait.rs +++ b/test/sys/test_wait.rs @@ -51,8 +51,7 @@ mod ptrace { use libc::_exit; fn ptrace_child() -> ! { - // TODO ptrace::traceme will be added in #666 - let _ = ptrace::ptrace(PTRACE_TRACEME, Pid::from_raw(0), ptr::null_mut(), ptr::null_mut()); + ptrace::ptrace(PTRACE_TRACEME, Pid::from_raw(0), ptr::null_mut(), ptr::null_mut()).unwrap(); // As recommended by ptrace(2), raise SIGTRAP to pause the child // until the parent is ready to continue let _ = raise(SIGTRAP); @@ -66,11 +65,9 @@ mod ptrace { assert!(ptrace::setoptions(child, PTRACE_O_TRACESYSGOOD | PTRACE_O_TRACEEXIT).is_ok()); // First, stop on the next system call, which will be exit() - // TODO ptrace::syscall will be added in #666 assert!(ptrace::ptrace(PTRACE_SYSCALL, child, ptr::null_mut(), ptr::null_mut()).is_ok()); assert_eq!(waitpid(child, None), Ok(WaitStatus::PtraceSyscall(child))); // Then get the ptrace event for the process exiting - // TODO ptrace::cont will be added in #666 assert!(ptrace::ptrace(PTRACE_CONT, child, ptr::null_mut(), ptr::null_mut()).is_ok()); assert_eq!(waitpid(child, None), Ok(WaitStatus::PtraceEvent(child, SIGTRAP, PTRACE_EVENT_EXIT))); // Finally get the normal wait() result, now that the process has exited