nix/
mqueue.rs

1//! Posix Message Queue functions
2//!
3//! [Further reading and details on the C API](https://man7.org/linux/man-pages/man7/mq_overview.7.html)
4
5use crate::Result;
6use crate::errno::Errno;
7
8use libc::{self, c_char, mqd_t, size_t};
9use std::ffi::CString;
10use crate::sys::stat::Mode;
11use std::mem;
12
13libc_bitflags!{
14    pub struct MQ_OFlag: libc::c_int {
15        O_RDONLY;
16        O_WRONLY;
17        O_RDWR;
18        O_CREAT;
19        O_EXCL;
20        O_NONBLOCK;
21        O_CLOEXEC;
22    }
23}
24
25libc_bitflags!{
26    pub struct FdFlag: libc::c_int {
27        FD_CLOEXEC;
28    }
29}
30
31#[repr(C)]
32#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
33pub struct MqAttr {
34    mq_attr: libc::mq_attr,
35}
36
37// x32 compatibility
38// See https://sourceware.org/bugzilla/show_bug.cgi?id=21279
39#[cfg(all(target_arch = "x86_64", target_pointer_width = "32"))]
40pub type mq_attr_member_t = i64;
41#[cfg(not(all(target_arch = "x86_64", target_pointer_width = "32")))]
42pub type mq_attr_member_t = libc::c_long;
43
44impl MqAttr {
45    pub fn new(mq_flags: mq_attr_member_t,
46               mq_maxmsg: mq_attr_member_t,
47               mq_msgsize: mq_attr_member_t,
48               mq_curmsgs: mq_attr_member_t)
49               -> MqAttr
50    {
51        let mut attr = mem::MaybeUninit::<libc::mq_attr>::uninit();
52        unsafe {
53            let p = attr.as_mut_ptr();
54            (*p).mq_flags = mq_flags;
55            (*p).mq_maxmsg = mq_maxmsg;
56            (*p).mq_msgsize = mq_msgsize;
57            (*p).mq_curmsgs = mq_curmsgs;
58            MqAttr { mq_attr: attr.assume_init() }
59        }
60    }
61
62    pub const fn flags(&self) -> mq_attr_member_t {
63        self.mq_attr.mq_flags
64    }
65}
66
67
68/// Open a message queue
69///
70/// See also [`mq_open(2)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_open.html)
71// The mode.bits cast is only lossless on some OSes
72#[allow(clippy::cast_lossless)]
73pub fn mq_open(name: &CString,
74               oflag: MQ_OFlag,
75               mode: Mode,
76               attr: Option<&MqAttr>)
77               -> Result<mqd_t> {
78    let res = match attr {
79        Some(mq_attr) => unsafe {
80            libc::mq_open(name.as_ptr(),
81                          oflag.bits(),
82                          mode.bits() as libc::c_int,
83                          &mq_attr.mq_attr as *const libc::mq_attr)
84        },
85        None => unsafe { libc::mq_open(name.as_ptr(), oflag.bits()) },
86    };
87    Errno::result(res)
88}
89
90/// Remove a message queue
91///
92/// See also [`mq_unlink(2)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_unlink.html)
93pub fn mq_unlink(name: &CString) -> Result<()> {
94    let res = unsafe { libc::mq_unlink(name.as_ptr()) };
95    Errno::result(res).map(drop)
96}
97
98/// Close a message queue
99///
100/// See also [`mq_close(2)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_close.html)
101pub fn mq_close(mqdes: mqd_t) -> Result<()> {
102    let res = unsafe { libc::mq_close(mqdes) };
103    Errno::result(res).map(drop)
104}
105
106/// Receive a message from a message queue
107///
108/// See also [`mq_receive(2)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_receive.html)
109pub fn mq_receive(mqdes: mqd_t, message: &mut [u8], msg_prio: &mut u32) -> Result<usize> {
110    let len = message.len() as size_t;
111    let res = unsafe {
112        libc::mq_receive(mqdes,
113                         message.as_mut_ptr() as *mut c_char,
114                         len,
115                         msg_prio as *mut u32)
116    };
117    Errno::result(res).map(|r| r as usize)
118}
119
120/// Send a message to a message queue
121///
122/// See also [`mq_send(2)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_send.html)
123pub fn mq_send(mqdes: mqd_t, message: &[u8], msq_prio: u32) -> Result<()> {
124    let res = unsafe {
125        libc::mq_send(mqdes,
126                      message.as_ptr() as *const c_char,
127                      message.len(),
128                      msq_prio)
129    };
130    Errno::result(res).map(drop)
131}
132
133/// Get message queue attributes
134///
135/// See also [`mq_getattr(2)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_getattr.html)
136pub fn mq_getattr(mqd: mqd_t) -> Result<MqAttr> {
137    let mut attr = mem::MaybeUninit::<libc::mq_attr>::uninit();
138    let res = unsafe { libc::mq_getattr(mqd, attr.as_mut_ptr()) };
139    Errno::result(res).map(|_| unsafe{MqAttr { mq_attr: attr.assume_init() }})
140}
141
142/// Set the attributes of the message queue. Only `O_NONBLOCK` can be set, everything else will be ignored
143/// Returns the old attributes
144/// It is recommend to use the `mq_set_nonblock()` and `mq_remove_nonblock()` convenience functions as they are easier to use
145///
146/// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_setattr.html)
147pub fn mq_setattr(mqd: mqd_t, newattr: &MqAttr) -> Result<MqAttr> {
148    let mut attr = mem::MaybeUninit::<libc::mq_attr>::uninit();
149    let res = unsafe {
150        libc::mq_setattr(mqd, &newattr.mq_attr as *const libc::mq_attr, attr.as_mut_ptr())
151    };
152    Errno::result(res).map(|_| unsafe{ MqAttr { mq_attr: attr.assume_init() }})
153}
154
155/// Convenience function.
156/// Sets the `O_NONBLOCK` attribute for a given message queue descriptor
157/// Returns the old attributes
158#[allow(clippy::useless_conversion)]    // Not useless on all OSes
159pub fn mq_set_nonblock(mqd: mqd_t) -> Result<MqAttr> {
160    let oldattr = mq_getattr(mqd)?;
161    let newattr = MqAttr::new(mq_attr_member_t::from(MQ_OFlag::O_NONBLOCK.bits()),
162                              oldattr.mq_attr.mq_maxmsg,
163                              oldattr.mq_attr.mq_msgsize,
164                              oldattr.mq_attr.mq_curmsgs);
165    mq_setattr(mqd, &newattr)
166}
167
168/// Convenience function.
169/// Removes `O_NONBLOCK` attribute for a given message queue descriptor
170/// Returns the old attributes
171pub fn mq_remove_nonblock(mqd: mqd_t) -> Result<MqAttr> {
172    let oldattr = mq_getattr(mqd)?;
173    let newattr = MqAttr::new(0,
174                              oldattr.mq_attr.mq_maxmsg,
175                              oldattr.mq_attr.mq_msgsize,
176                              oldattr.mq_attr.mq_curmsgs);
177    mq_setattr(mqd, &newattr)
178}