• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
33 use crate::errno::Errno;
34 use crate::Result;
35 
36 use crate::sys::stat::Mode;
37 use libc::{self, c_char, mqd_t, size_t};
38 use std::ffi::CStr;
39 use std::mem;
40 
41 libc_bitflags! {
42     /// Used with [`mq_open`].
43     pub struct MQ_OFlag: libc::c_int {
44         /// Open the message queue for receiving messages.
45         O_RDONLY;
46         /// Open the queue for sending messages.
47         O_WRONLY;
48         /// Open the queue for both receiving and sending messages
49         O_RDWR;
50         /// Create a message queue.
51         O_CREAT;
52         /// If set along with `O_CREAT`, `mq_open` will fail if the message
53         /// queue name exists.
54         O_EXCL;
55         /// `mq_send` and `mq_receive` should fail with `EAGAIN` rather than
56         /// wait for resources that are not currently available.
57         O_NONBLOCK;
58         /// Set the close-on-exec flag for the message queue descriptor.
59         O_CLOEXEC;
60     }
61 }
62 
63 /// A message-queue attribute, optionally used with [`mq_setattr`] and
64 /// [`mq_getattr`] and optionally [`mq_open`],
65 #[repr(C)]
66 #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
67 pub struct MqAttr {
68     mq_attr: libc::mq_attr,
69 }
70 
71 /// Identifies an open POSIX Message Queue
72 // A safer wrapper around libc::mqd_t, which is a pointer on some platforms
73 // Deliberately is not Clone to prevent use-after-close scenarios
74 #[repr(transparent)]
75 #[derive(Debug)]
76 #[allow(missing_copy_implementations)]
77 pub struct MqdT(mqd_t);
78 
79 // x32 compatibility
80 // See https://sourceware.org/bugzilla/show_bug.cgi?id=21279
81 /// Size of a message queue attribute member
82 #[cfg(all(target_arch = "x86_64", target_pointer_width = "32"))]
83 #[cfg_attr(docsrs, doc(cfg(all())))]
84 pub type mq_attr_member_t = i64;
85 /// Size of a message queue attribute member
86 #[cfg(not(all(target_arch = "x86_64", target_pointer_width = "32")))]
87 #[cfg_attr(docsrs, doc(cfg(all())))]
88 pub type mq_attr_member_t = libc::c_long;
89 
90 impl MqAttr {
91     /// Create a new message queue attribute
92     ///
93     /// # Arguments
94     ///
95     /// - `mq_flags`:   Either `0` or `O_NONBLOCK`.
96     /// - `mq_maxmsg`:  Maximum number of messages on the queue.
97     /// - `mq_msgsize`: Maximum message size in bytes.
98     /// - `mq_curmsgs`: Number of messages currently in the queue.
new( mq_flags: mq_attr_member_t, mq_maxmsg: mq_attr_member_t, mq_msgsize: mq_attr_member_t, mq_curmsgs: mq_attr_member_t, ) -> MqAttr99     pub fn new(
100         mq_flags: mq_attr_member_t,
101         mq_maxmsg: mq_attr_member_t,
102         mq_msgsize: mq_attr_member_t,
103         mq_curmsgs: mq_attr_member_t,
104     ) -> MqAttr {
105         let mut attr = mem::MaybeUninit::<libc::mq_attr>::uninit();
106         unsafe {
107             let p = attr.as_mut_ptr();
108             (*p).mq_flags = mq_flags;
109             (*p).mq_maxmsg = mq_maxmsg;
110             (*p).mq_msgsize = mq_msgsize;
111             (*p).mq_curmsgs = mq_curmsgs;
112             MqAttr {
113                 mq_attr: attr.assume_init(),
114             }
115         }
116     }
117 
118     /// The current flags, either `0` or `O_NONBLOCK`.
flags(&self) -> mq_attr_member_t119     pub const fn flags(&self) -> mq_attr_member_t {
120         self.mq_attr.mq_flags
121     }
122 
123     /// The max number of messages that can be held by the queue
maxmsg(&self) -> mq_attr_member_t124     pub const fn maxmsg(&self) -> mq_attr_member_t {
125         self.mq_attr.mq_maxmsg
126     }
127 
128     /// The maximum size of each message (in bytes)
msgsize(&self) -> mq_attr_member_t129     pub const fn msgsize(&self) -> mq_attr_member_t {
130         self.mq_attr.mq_msgsize
131     }
132 
133     /// The number of messages currently held in the queue
curmsgs(&self) -> mq_attr_member_t134     pub const fn curmsgs(&self) -> mq_attr_member_t {
135         self.mq_attr.mq_curmsgs
136     }
137 }
138 
139 /// Open a message queue
140 ///
141 /// See also [`mq_open(2)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_open.html)
142 // The mode.bits cast is only lossless on some OSes
143 #[allow(clippy::cast_lossless)]
mq_open( name: &CStr, oflag: MQ_OFlag, mode: Mode, attr: Option<&MqAttr>, ) -> Result<MqdT>144 pub fn mq_open(
145     name: &CStr,
146     oflag: MQ_OFlag,
147     mode: Mode,
148     attr: Option<&MqAttr>,
149 ) -> Result<MqdT> {
150     let res = match attr {
151         Some(mq_attr) => unsafe {
152             libc::mq_open(
153                 name.as_ptr(),
154                 oflag.bits(),
155                 mode.bits() as libc::c_int,
156                 &mq_attr.mq_attr as *const libc::mq_attr,
157             )
158         },
159         None => unsafe { libc::mq_open(name.as_ptr(), oflag.bits()) },
160     };
161     Errno::result(res).map(MqdT)
162 }
163 
164 /// Remove a message queue
165 ///
166 /// See also [`mq_unlink(2)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_unlink.html)
mq_unlink(name: &CStr) -> Result<()>167 pub fn mq_unlink(name: &CStr) -> Result<()> {
168     let res = unsafe { libc::mq_unlink(name.as_ptr()) };
169     Errno::result(res).map(drop)
170 }
171 
172 /// Close a message queue
173 ///
174 /// See also [`mq_close(2)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_close.html)
mq_close(mqdes: MqdT) -> Result<()>175 pub fn mq_close(mqdes: MqdT) -> Result<()> {
176     let res = unsafe { libc::mq_close(mqdes.0) };
177     Errno::result(res).map(drop)
178 }
179 
180 /// Receive a message from a message queue
181 ///
182 /// See also [`mq_receive(2)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_receive.html)
mq_receive( mqdes: &MqdT, message: &mut [u8], msg_prio: &mut u32, ) -> Result<usize>183 pub fn mq_receive(
184     mqdes: &MqdT,
185     message: &mut [u8],
186     msg_prio: &mut u32,
187 ) -> Result<usize> {
188     let len = message.len() as size_t;
189     let res = unsafe {
190         libc::mq_receive(
191             mqdes.0,
192             message.as_mut_ptr() as *mut c_char,
193             len,
194             msg_prio as *mut u32,
195         )
196     };
197     Errno::result(res).map(|r| r as usize)
198 }
199 
200 /// Send a message to a message queue
201 ///
202 /// See also [`mq_send(2)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_send.html)
mq_send(mqdes: &MqdT, message: &[u8], msq_prio: u32) -> Result<()>203 pub fn mq_send(mqdes: &MqdT, message: &[u8], msq_prio: u32) -> Result<()> {
204     let res = unsafe {
205         libc::mq_send(
206             mqdes.0,
207             message.as_ptr() as *const c_char,
208             message.len(),
209             msq_prio,
210         )
211     };
212     Errno::result(res).map(drop)
213 }
214 
215 /// Get message queue attributes
216 ///
217 /// See also [`mq_getattr(2)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_getattr.html)
mq_getattr(mqd: &MqdT) -> Result<MqAttr>218 pub fn mq_getattr(mqd: &MqdT) -> Result<MqAttr> {
219     let mut attr = mem::MaybeUninit::<libc::mq_attr>::uninit();
220     let res = unsafe { libc::mq_getattr(mqd.0, attr.as_mut_ptr()) };
221     Errno::result(res).map(|_| unsafe {
222         MqAttr {
223             mq_attr: attr.assume_init(),
224         }
225     })
226 }
227 
228 /// Set the attributes of the message queue. Only `O_NONBLOCK` can be set, everything else will be ignored
229 /// Returns the old attributes
230 /// It is recommend to use the `mq_set_nonblock()` and `mq_remove_nonblock()` convenience functions as they are easier to use
231 ///
232 /// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_setattr.html)
mq_setattr(mqd: &MqdT, newattr: &MqAttr) -> Result<MqAttr>233 pub fn mq_setattr(mqd: &MqdT, newattr: &MqAttr) -> Result<MqAttr> {
234     let mut attr = mem::MaybeUninit::<libc::mq_attr>::uninit();
235     let res = unsafe {
236         libc::mq_setattr(
237             mqd.0,
238             &newattr.mq_attr as *const libc::mq_attr,
239             attr.as_mut_ptr(),
240         )
241     };
242     Errno::result(res).map(|_| unsafe {
243         MqAttr {
244             mq_attr: attr.assume_init(),
245         }
246     })
247 }
248 
249 /// Convenience function.
250 /// Sets the `O_NONBLOCK` attribute for a given message queue descriptor
251 /// Returns the old attributes
252 #[allow(clippy::useless_conversion)] // Not useless on all OSes
mq_set_nonblock(mqd: &MqdT) -> Result<MqAttr>253 pub fn mq_set_nonblock(mqd: &MqdT) -> Result<MqAttr> {
254     let oldattr = mq_getattr(mqd)?;
255     let newattr = MqAttr::new(
256         mq_attr_member_t::from(MQ_OFlag::O_NONBLOCK.bits()),
257         oldattr.mq_attr.mq_maxmsg,
258         oldattr.mq_attr.mq_msgsize,
259         oldattr.mq_attr.mq_curmsgs,
260     );
261     mq_setattr(mqd, &newattr)
262 }
263 
264 /// Convenience function.
265 /// Removes `O_NONBLOCK` attribute for a given message queue descriptor
266 /// Returns the old attributes
mq_remove_nonblock(mqd: &MqdT) -> Result<MqAttr>267 pub fn mq_remove_nonblock(mqd: &MqdT) -> Result<MqAttr> {
268     let oldattr = mq_getattr(mqd)?;
269     let newattr = MqAttr::new(
270         0,
271         oldattr.mq_attr.mq_maxmsg,
272         oldattr.mq_attr.mq_msgsize,
273         oldattr.mq_attr.mq_curmsgs,
274     );
275     mq_setattr(mqd, &newattr)
276 }
277