1 //! Posix Message Queue functions
2 //!
3 //! [Further reading and details on the C API](http://man7.org/linux/man-pages/man7/mq_overview.7.html)
4 
5 use crate::Result;
6 use crate::errno::Errno;
7 
8 use libc::{self, c_char, mqd_t, size_t};
9 use std::ffi::CString;
10 use crate::sys::stat::Mode;
11 use std::mem;
12 
13 libc_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 
25 libc_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)]
33 pub 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"))]
40 pub type mq_attr_member_t = i64;
41 #[cfg(not(all(target_arch = "x86_64", target_pointer_width = "32")))]
42 pub type mq_attr_member_t = libc::c_long;
43 
44 impl MqAttr {
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) -> MqAttr45     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 
flags(&self) -> mq_attr_member_t62     pub 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)`](http://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)]
mq_open(name: &CString, oflag: MQ_OFlag, mode: Mode, attr: Option<&MqAttr>) -> Result<mqd_t>73 pub 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)`](http://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_unlink.html)
mq_unlink(name: &CString) -> Result<()>93 pub 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)`](http://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_close.html)
mq_close(mqdes: mqd_t) -> Result<()>101 pub 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)`](http://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_receive.html)
mq_receive(mqdes: mqd_t, message: &mut [u8], msg_prio: &mut u32) -> Result<usize>109 pub 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)`](http://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_send.html)
mq_send(mqdes: mqd_t, message: &[u8], msq_prio: u32) -> Result<()>123 pub 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)`](http://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_getattr.html)
mq_getattr(mqd: mqd_t) -> Result<MqAttr>136 pub 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](http://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_setattr.html)
mq_setattr(mqd: mqd_t, newattr: &MqAttr) -> Result<MqAttr>147 pub 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
mq_set_nonblock(mqd: mqd_t) -> Result<MqAttr>158 pub fn mq_set_nonblock(mqd: mqd_t) -> Result<MqAttr> {
159     let oldattr = mq_getattr(mqd)?;
160     let newattr = MqAttr::new(mq_attr_member_t::from(MQ_OFlag::O_NONBLOCK.bits()),
161                               oldattr.mq_attr.mq_maxmsg,
162                               oldattr.mq_attr.mq_msgsize,
163                               oldattr.mq_attr.mq_curmsgs);
164     mq_setattr(mqd, &newattr)
165 }
166 
167 /// Convenience function.
168 /// Removes `O_NONBLOCK` attribute for a given message queue descriptor
169 /// Returns the old attributes
mq_remove_nonblock(mqd: mqd_t) -> Result<MqAttr>170 pub fn mq_remove_nonblock(mqd: mqd_t) -> Result<MqAttr> {
171     let oldattr = mq_getattr(mqd)?;
172     let newattr = MqAttr::new(0,
173                               oldattr.mq_attr.mq_maxmsg,
174                               oldattr.mq_attr.mq_msgsize,
175                               oldattr.mq_attr.mq_curmsgs);
176     mq_setattr(mqd, &newattr)
177 }
178