Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace ffi module by libc functions in mqueue.rs #392

Merged
merged 3 commits into from
Sep 1, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,18 @@ This project adheres to [Semantic Versioning](http://semver.org/).
`::nix::sched` to `Result<bool>` and `Result<()>`, respectively. They now
return `EINVAL`, if an invalid argument for the `field` parameter is passed.
([#402](https://github.com/nix-rust/nix/pull/402))
- `MqAttr` in `::nix::mqueue` is now an opaque proxy for `::libc::mq_attr`,
which has the same structure as the old `MqAttr`. The field `mq_flags` of
`::libc::mq_attr` is readable using the new method `flags()` of `MqAttr`.
`MqAttr` also no longer implements `Debug`.
([#0](https://github.com/nix-rust/nix/pull/0))
- The parameter `msq_prio` of `mq_receive` with type `u32` in `::nix::mqueue`
was replaced by a parameter named `msg_prio` with type `&mut u32`, so that
the message priority can be obtained by the caller.
([#0](https://github.com/nix-rust/nix/pull/0))
- The type alias `MQd` in `::nix::queue` was replaced by the type alias
`libc::mqd_t`, both of which are aliases for the same type.
([#0](https://github.com/nix-rust/nix/pull/0))

### Removed
- Type alias `SigNum` from `::nix::sys::signal`.
Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ pub mod fcntl;
#[cfg(any(target_os = "linux", target_os = "android"))]
pub mod mount;

#[cfg(any(target_os = "linux"))]
#[cfg(target_os = "linux")]
pub mod mqueue;

#[cfg(any(target_os = "linux", target_os = "macos"))]
Expand Down
178 changes: 93 additions & 85 deletions src/mqueue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,142 +4,150 @@

use {Errno, Result};

use libc::{c_int, c_long, c_char, size_t, mode_t};
use libc::{self, c_char, c_long, mode_t, mqd_t, size_t};
use std::ffi::CString;
use sys::stat::Mode;
use std::ptr;

pub use self::consts::*;

pub type MQd = c_int;

#[cfg(target_os = "linux")]
mod consts {
use libc::c_int;

bitflags!(
flags MQ_OFlag: c_int {
const O_RDONLY = 0o00000000,
const O_WRONLY = 0o00000001,
const O_RDWR = 0o00000002,
const O_CREAT = 0o00000100,
const O_EXCL = 0o00000200,
const O_NONBLOCK = 0o00004000,
const O_CLOEXEC = 0o02000000,
}
);

bitflags!(
flags FdFlag: c_int {
const FD_CLOEXEC = 1
}
);
use std::mem;

libc_bitflags!{
flags MQ_OFlag: libc::c_int {
O_RDONLY,
O_WRONLY,
O_RDWR,
O_CREAT,
O_EXCL,
O_NONBLOCK,
O_CLOEXEC,
}
}

mod ffi {
use libc::{c_char, size_t, ssize_t, c_uint, c_int};
use super::MQd;
use super::MqAttr;

#[allow(improper_ctypes)]
extern "C" {
pub fn mq_open(name: *const c_char, oflag: c_int, ...) -> MQd;

pub fn mq_close (mqd: MQd) -> c_int;

pub fn mq_unlink(name: *const c_char) -> c_int;

pub fn mq_receive (mqd: MQd, msg_ptr: *const c_char, msg_len: size_t, msq_prio: *const c_uint) -> ssize_t;

pub fn mq_send (mqd: MQd, msg_ptr: *const c_char, msg_len: size_t, msq_prio: c_uint) -> c_int;

pub fn mq_getattr(mqd: MQd, attr: *mut MqAttr) -> c_int;

pub fn mq_setattr(mqd: MQd, newattr: *const MqAttr, oldattr: *mut MqAttr) -> c_int;
libc_bitflags!{
flags FdFlag: libc::c_int {
FD_CLOEXEC,
}
}

#[repr(C)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[derive(Clone, Copy)]
pub struct MqAttr {
pub mq_flags: c_long,
pub mq_maxmsg: c_long,
pub mq_msgsize: c_long,
pub mq_curmsgs: c_long,
pad: [c_long; 4]
mq_attr: libc::mq_attr,
}

impl MqAttr {
pub fn new(mq_flags: c_long, mq_maxmsg: c_long, mq_msgsize: c_long, mq_curmsgs: c_long) -> MqAttr {
MqAttr { mq_flags: mq_flags, mq_maxmsg: mq_maxmsg, mq_msgsize: mq_msgsize, mq_curmsgs: mq_curmsgs, pad: [0; 4] }
}
impl PartialEq<MqAttr> for MqAttr {
fn eq(&self, other: &MqAttr) -> bool {
let self_attr = self.mq_attr;
let other_attr = other.mq_attr;
self_attr.mq_flags == other_attr.mq_flags && self_attr.mq_maxmsg == other_attr.mq_maxmsg &&
self_attr.mq_msgsize == other_attr.mq_msgsize &&
self_attr.mq_curmsgs == other_attr.mq_curmsgs
}
}

impl MqAttr {
pub fn new(mq_flags: c_long,
mq_maxmsg: c_long,
mq_msgsize: c_long,
mq_curmsgs: c_long)
-> MqAttr {
let mut attr = unsafe { mem::uninitialized::<libc::mq_attr>() };
attr.mq_flags = mq_flags;
attr.mq_maxmsg = mq_maxmsg;
attr.mq_msgsize = mq_msgsize;
attr.mq_curmsgs = mq_curmsgs;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any reason to not use the struct initializer syntax in this case rather than creating a mutable mq_attr and setting every field?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a private field for padding.

MqAttr { mq_attr: attr }
}

pub fn mq_open(name: &CString, oflag: MQ_OFlag, mode: Mode, attr: Option<&MqAttr>) -> Result<MQd> {
let attr_p = attr.map(|attr| attr as *const MqAttr).unwrap_or(ptr::null());
let res = unsafe { ffi::mq_open(name.as_ptr(), oflag.bits(), mode.bits() as mode_t, attr_p) };
pub fn flags(&self) -> c_long {
self.mq_attr.mq_flags
}
}


pub fn mq_open(name: &CString,
oflag: MQ_OFlag,
mode: Mode,
attr: Option<&MqAttr>)
-> Result<mqd_t> {
let res = match attr {
Some(mq_attr) => unsafe {
libc::mq_open(name.as_ptr(),
oflag.bits(),
mode.bits() as mode_t,
&mq_attr.mq_attr as *const libc::mq_attr)
},
None => unsafe { libc::mq_open(name.as_ptr(), oflag.bits()) },
};
Errno::result(res)
}

pub fn mq_unlink(name: &CString) -> Result<()> {
let res = unsafe { ffi::mq_unlink(name.as_ptr()) };
let res = unsafe { libc::mq_unlink(name.as_ptr()) };
Errno::result(res).map(drop)
}

pub fn mq_close(mqdes: MQd) -> Result<()> {
let res = unsafe { ffi::mq_close(mqdes) };
pub fn mq_close(mqdes: mqd_t) -> Result<()> {
let res = unsafe { libc::mq_close(mqdes) };
Errno::result(res).map(drop)
}


pub fn mq_receive(mqdes: MQd, message: &mut [u8], msq_prio: u32) -> Result<usize> {
pub fn mq_receive(mqdes: mqd_t, message: &mut [u8], msg_prio: &mut u32) -> Result<usize> {
let len = message.len() as size_t;
let res = unsafe { ffi::mq_receive(mqdes, message.as_mut_ptr() as *mut c_char, len, &msq_prio) };

let res = unsafe {
libc::mq_receive(mqdes,
message.as_mut_ptr() as *mut c_char,
len,
msg_prio as *mut u32)
};
Errno::result(res).map(|r| r as usize)
}

pub fn mq_send(mqdes: MQd, message: &[u8], msq_prio: u32) -> Result<()> {
let res = unsafe { ffi::mq_send(mqdes, message.as_ptr() as *const c_char, message.len(), msq_prio) };

pub fn mq_send(mqdes: mqd_t, message: &[u8], msq_prio: u32) -> Result<()> {
let res = unsafe {
libc::mq_send(mqdes,
message.as_ptr() as *const c_char,
message.len(),
msq_prio)
};
Errno::result(res).map(drop)
}

pub fn mq_getattr(mqd: MQd) -> Result<MqAttr> {
let mut attr = MqAttr::new(0, 0, 0, 0);
let res = unsafe { ffi::mq_getattr(mqd, &mut attr) };
try!(Errno::result(res));
Ok(attr)
pub fn mq_getattr(mqd: mqd_t) -> Result<MqAttr> {
let mut attr = unsafe { mem::uninitialized::<libc::mq_attr>() };
let res = unsafe { libc::mq_getattr(mqd, &mut attr) };
Errno::result(res).map(|_| MqAttr { mq_attr: attr })
}

/// Set the attributes of the message queue. Only `O_NONBLOCK` can be set, everything else will be ignored
/// Returns the old attributes
/// It is recommend to use the `mq_set_nonblock()` and `mq_remove_nonblock()` convenience functions as they are easier to use
///
/// [Further reading](http://man7.org/linux/man-pages/man3/mq_setattr.3.html)
pub fn mq_setattr(mqd: MQd, newattr: &MqAttr) -> Result<MqAttr> {
let mut attr = MqAttr::new(0, 0, 0, 0);
let res = unsafe { ffi::mq_setattr(mqd, newattr as *const MqAttr, &mut attr) };
try!(Errno::result(res));
Ok(attr)
pub fn mq_setattr(mqd: mqd_t, newattr: &MqAttr) -> Result<MqAttr> {
let mut attr = unsafe { mem::uninitialized::<libc::mq_attr>() };
let res = unsafe { libc::mq_setattr(mqd, &newattr.mq_attr as *const libc::mq_attr, &mut attr) };
Errno::result(res).map(|_| MqAttr { mq_attr: attr })
}

/// Convenience function.
/// Sets the `O_NONBLOCK` attribute for a given message queue descriptor
/// Returns the old attributes
pub fn mq_set_nonblock(mqd: MQd) -> Result<(MqAttr)> {
pub fn mq_set_nonblock(mqd: mqd_t) -> Result<(MqAttr)> {
let oldattr = try!(mq_getattr(mqd));
let newattr = MqAttr::new(O_NONBLOCK.bits() as c_long, oldattr.mq_maxmsg, oldattr.mq_msgsize, oldattr.mq_curmsgs);
let newattr = MqAttr::new(O_NONBLOCK.bits() as c_long,
oldattr.mq_attr.mq_maxmsg,
oldattr.mq_attr.mq_msgsize,
oldattr.mq_attr.mq_curmsgs);
mq_setattr(mqd, &newattr)
}

/// Convenience function.
/// Removes `O_NONBLOCK` attribute for a given message queue descriptor
/// Returns the old attributes
pub fn mq_remove_nonblock(mqd: MQd) -> Result<(MqAttr)> {
pub fn mq_remove_nonblock(mqd: mqd_t) -> Result<(MqAttr)> {
let oldattr = try!(mq_getattr(mqd));
let newattr = MqAttr::new(0, oldattr.mq_maxmsg, oldattr.mq_msgsize, oldattr.mq_curmsgs);
let newattr = MqAttr::new(0,
oldattr.mq_attr.mq_maxmsg,
oldattr.mq_attr.mq_msgsize,
oldattr.mq_attr.mq_curmsgs);
mq_setattr(mqd, &newattr)
}
8 changes: 5 additions & 3 deletions test/test_mq.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@ fn test_mq_send_and_receive() {
let mq_name_in_child = &CString::new(b"/a_nix_test_queue".as_ref()).unwrap();
let mqd_in_child = mq_open(mq_name_in_child, O_CREAT | O_RDONLY, S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH, Some(&attr)).unwrap();
let mut buf = [0u8; 32];
mq_receive(mqd_in_child, &mut buf, 1).unwrap();
let mut prio = 0u32;
mq_receive(mqd_in_child, &mut buf, &mut prio).unwrap();
assert!(prio == 1);
write(writer, &buf).unwrap(); // pipe result to parent process. Otherwise cargo does not report test failures correctly
mq_close(mqd_in_child).unwrap();
}
Expand Down Expand Up @@ -99,10 +101,10 @@ fn test_mq_set_nonblocking() {
let mqd = mq_open(mq_name, O_CREAT | O_WRONLY, S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH, Some(&initial_attr)).unwrap();
mq_set_nonblock(mqd).unwrap();
let new_attr = mq_getattr(mqd);
assert!(new_attr.unwrap().mq_flags == O_NONBLOCK.bits() as c_long);
assert!(new_attr.unwrap().flags() == O_NONBLOCK.bits() as c_long);
mq_remove_nonblock(mqd).unwrap();
let new_attr = mq_getattr(mqd);
assert!(new_attr.unwrap().mq_flags == 0);
assert!(new_attr.unwrap().flags() == 0);
mq_close(mqd).unwrap();
}

Expand Down