nix/
mqueue.rs

1//! Posix Message Queue functions
2//!
3//! # Example
4//!
5// no_run because a kernel module may be required.
6//! ```no_run
7//! # use std::ffi::CString;
8//! # use nix::mqueue::*;
9//! use nix::sys::stat::Mode;
10//!
11//! const MSG_SIZE: mq_attr_member_t = 32;
12//! let mq_name= CString::new("/a_nix_test_queue").unwrap();
13//!
14//! let oflag0 = MQ_OFlag::O_CREAT | MQ_OFlag::O_WRONLY;
15//! let mode = Mode::S_IWUSR | Mode::S_IRUSR | Mode::S_IRGRP | Mode::S_IROTH;
16//! let mqd0 = mq_open(&mq_name, oflag0, mode, None).unwrap();
17//! let msg_to_send = b"msg_1";
18//! mq_send(&mqd0, msg_to_send, 1).unwrap();
19//!
20//! let oflag1 = MQ_OFlag::O_CREAT | MQ_OFlag::O_RDONLY;
21//! let mqd1 = mq_open(&mq_name, oflag1, mode, None).unwrap();
22//! let mut buf = [0u8; 32];
23//! let mut prio = 0u32;
24//! let len = mq_receive(&mqd1, &mut buf, &mut prio).unwrap();
25//! assert_eq!(prio, 1);
26//! assert_eq!(msg_to_send, &buf[0..len]);
27//!
28//! mq_close(mqd1).unwrap();
29//! mq_close(mqd0).unwrap();
30//! ```
31//! [Further reading and details on the C API](https://man7.org/linux/man-pages/man7/mq_overview.7.html)
32
33use crate::Result;
34use crate::errno::Errno;
35
36use libc::{self, c_char, mqd_t, size_t};
37use std::ffi::CStr;
38use crate::sys::stat::Mode;
39use std::mem;
40
41libc_bitflags!{
42    pub struct MQ_OFlag: libc::c_int {
43        O_RDONLY;
44        O_WRONLY;
45        O_RDWR;
46        O_CREAT;
47        O_EXCL;
48        O_NONBLOCK;
49        O_CLOEXEC;
50    }
51}
52
53libc_bitflags!{
54    pub struct FdFlag: libc::c_int {
55        FD_CLOEXEC;
56    }
57}
58
59#[repr(C)]
60#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
61pub struct MqAttr {
62    mq_attr: libc::mq_attr,
63}
64
65/// Identifies an open POSIX Message Queue
66// A safer wrapper around libc::mqd_t, which is a pointer on some platforms
67// Deliberately is not Clone to prevent use-after-close scenarios
68#[repr(transparent)]
69#[derive(Debug)]
70#[allow(missing_copy_implementations)]
71pub struct MqdT(mqd_t);
72
73// x32 compatibility
74// See https://sourceware.org/bugzilla/show_bug.cgi?id=21279
75#[cfg(all(target_arch = "x86_64", target_pointer_width = "32"))]
76#[cfg_attr(docsrs, doc(cfg(all())))]
77pub type mq_attr_member_t = i64;
78#[cfg(not(all(target_arch = "x86_64", target_pointer_width = "32")))]
79#[cfg_attr(docsrs, doc(cfg(all())))]
80pub type mq_attr_member_t = libc::c_long;
81
82impl MqAttr {
83    pub fn new(mq_flags: mq_attr_member_t,
84               mq_maxmsg: mq_attr_member_t,
85               mq_msgsize: mq_attr_member_t,
86               mq_curmsgs: mq_attr_member_t)
87               -> MqAttr
88    {
89        let mut attr = mem::MaybeUninit::<libc::mq_attr>::uninit();
90        unsafe {
91            let p = attr.as_mut_ptr();
92            (*p).mq_flags = mq_flags;
93            (*p).mq_maxmsg = mq_maxmsg;
94            (*p).mq_msgsize = mq_msgsize;
95            (*p).mq_curmsgs = mq_curmsgs;
96            MqAttr { mq_attr: attr.assume_init() }
97        }
98    }
99
100    pub const fn flags(&self) -> mq_attr_member_t {
101        self.mq_attr.mq_flags
102    }
103
104    /// The max number of messages that can be held by the queue
105    pub const fn maxmsg(&self) -> mq_attr_member_t {
106        self.mq_attr.mq_maxmsg
107    }
108
109    /// The maximum size of each message (in bytes)
110    pub const fn msgsize(&self) -> mq_attr_member_t {
111        self.mq_attr.mq_msgsize
112    }
113
114    /// The number of messages currently held in the queue
115    pub const fn curmsgs(&self) -> mq_attr_member_t {
116        self.mq_attr.mq_curmsgs
117    }
118}
119
120
121/// Open a message queue
122///
123/// See also [`mq_open(2)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_open.html)
124// The mode.bits cast is only lossless on some OSes
125#[allow(clippy::cast_lossless)]
126pub fn mq_open(name: &CStr,
127               oflag: MQ_OFlag,
128               mode: Mode,
129               attr: Option<&MqAttr>)
130               -> Result<MqdT> {
131    let res = match attr {
132        Some(mq_attr) => unsafe {
133            libc::mq_open(name.as_ptr(),
134                          oflag.bits(),
135                          mode.bits() as libc::c_int,
136                          &mq_attr.mq_attr as *const libc::mq_attr)
137        },
138        None => unsafe { libc::mq_open(name.as_ptr(), oflag.bits()) },
139    };
140    Errno::result(res).map(MqdT)
141}
142
143/// Remove a message queue
144///
145/// See also [`mq_unlink(2)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_unlink.html)
146pub fn mq_unlink(name: &CStr) -> Result<()> {
147    let res = unsafe { libc::mq_unlink(name.as_ptr()) };
148    Errno::result(res).map(drop)
149}
150
151/// Close a message queue
152///
153/// See also [`mq_close(2)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_close.html)
154pub fn mq_close(mqdes: MqdT) -> Result<()> {
155    let res = unsafe { libc::mq_close(mqdes.0) };
156    Errno::result(res).map(drop)
157}
158
159/// Receive a message from a message queue
160///
161/// See also [`mq_receive(2)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_receive.html)
162pub fn mq_receive(mqdes: &MqdT, message: &mut [u8], msg_prio: &mut u32) -> Result<usize> {
163    let len = message.len() as size_t;
164    let res = unsafe {
165        libc::mq_receive(mqdes.0,
166                         message.as_mut_ptr() as *mut c_char,
167                         len,
168                         msg_prio as *mut u32)
169    };
170    Errno::result(res).map(|r| r as usize)
171}
172
173/// Send a message to a message queue
174///
175/// See also [`mq_send(2)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_send.html)
176pub fn mq_send(mqdes: &MqdT, message: &[u8], msq_prio: u32) -> Result<()> {
177    let res = unsafe {
178        libc::mq_send(mqdes.0,
179                      message.as_ptr() as *const c_char,
180                      message.len(),
181                      msq_prio)
182    };
183    Errno::result(res).map(drop)
184}
185
186/// Get message queue attributes
187///
188/// See also [`mq_getattr(2)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_getattr.html)
189pub fn mq_getattr(mqd: &MqdT) -> Result<MqAttr> {
190    let mut attr = mem::MaybeUninit::<libc::mq_attr>::uninit();
191    let res = unsafe { libc::mq_getattr(mqd.0, attr.as_mut_ptr()) };
192    Errno::result(res).map(|_| unsafe{MqAttr { mq_attr: attr.assume_init() }})
193}
194
195/// Set the attributes of the message queue. Only `O_NONBLOCK` can be set, everything else will be ignored
196/// Returns the old attributes
197/// It is recommend to use the `mq_set_nonblock()` and `mq_remove_nonblock()` convenience functions as they are easier to use
198///
199/// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_setattr.html)
200pub fn mq_setattr(mqd: &MqdT, newattr: &MqAttr) -> Result<MqAttr> {
201    let mut attr = mem::MaybeUninit::<libc::mq_attr>::uninit();
202    let res = unsafe {
203        libc::mq_setattr(mqd.0, &newattr.mq_attr as *const libc::mq_attr, attr.as_mut_ptr())
204    };
205    Errno::result(res).map(|_| unsafe{ MqAttr { mq_attr: attr.assume_init() }})
206}
207
208/// Convenience function.
209/// Sets the `O_NONBLOCK` attribute for a given message queue descriptor
210/// Returns the old attributes
211#[allow(clippy::useless_conversion)]    // Not useless on all OSes
212pub fn mq_set_nonblock(mqd: &MqdT) -> Result<MqAttr> {
213    let oldattr = mq_getattr(mqd)?;
214    let newattr = MqAttr::new(mq_attr_member_t::from(MQ_OFlag::O_NONBLOCK.bits()),
215                              oldattr.mq_attr.mq_maxmsg,
216                              oldattr.mq_attr.mq_msgsize,
217                              oldattr.mq_attr.mq_curmsgs);
218    mq_setattr(mqd, &newattr)
219}
220
221/// Convenience function.
222/// Removes `O_NONBLOCK` attribute for a given message queue descriptor
223/// Returns the old attributes
224pub fn mq_remove_nonblock(mqd: &MqdT) -> Result<MqAttr> {
225    let oldattr = mq_getattr(mqd)?;
226    let newattr = MqAttr::new(0,
227                              oldattr.mq_attr.mq_maxmsg,
228                              oldattr.mq_attr.mq_msgsize,
229                              oldattr.mq_attr.mq_curmsgs);
230    mq_setattr(mqd, &newattr)
231}