1 use cfg_if::cfg_if;
2 use std::ffi::CString;
3 use std::str;
4
5 use nix::errno::Errno;
6 use nix::mqueue::{mq_attr_member_t, mq_close, mq_open, mq_receive, mq_send};
7 use nix::mqueue::{MQ_OFlag, MqAttr};
8 use nix::sys::stat::Mode;
9
10 // Defined as a macro such that the error source is reported as the caller's location.
11 macro_rules! assert_attr_eq {
12 ($read_attr:ident, $initial_attr:ident) => {
13 cfg_if! {
14 if #[cfg(any(target_os = "dragonfly", target_os = "netbsd"))] {
15 // NetBSD (and others which inherit its implementation) include other flags
16 // in read_attr, such as those specified by oflag. Just make sure at least
17 // the correct bits are set.
18 assert_eq!($read_attr.flags() & $initial_attr.flags(), $initial_attr.flags());
19 assert_eq!($read_attr.maxmsg(), $initial_attr.maxmsg());
20 assert_eq!($read_attr.msgsize(), $initial_attr.msgsize());
21 assert_eq!($read_attr.curmsgs(), $initial_attr.curmsgs());
22 } else {
23 assert_eq!($read_attr, $initial_attr);
24 }
25 }
26 }
27 }
28
29 #[test]
test_mq_send_and_receive()30 fn test_mq_send_and_receive() {
31 const MSG_SIZE: mq_attr_member_t = 32;
32 let attr = MqAttr::new(0, 10, MSG_SIZE, 0);
33 let mq_name = &CString::new(b"/a_nix_test_queue".as_ref()).unwrap();
34
35 let oflag0 = MQ_OFlag::O_CREAT | MQ_OFlag::O_WRONLY;
36 let mode = Mode::S_IWUSR | Mode::S_IRUSR | Mode::S_IRGRP | Mode::S_IROTH;
37 let r0 = mq_open(mq_name, oflag0, mode, Some(&attr));
38 if let Err(Errno::ENOSYS) = r0 {
39 println!("message queues not supported or module not loaded?");
40 return;
41 };
42 let mqd0 = r0.unwrap();
43 let msg_to_send = "msg_1";
44 mq_send(&mqd0, msg_to_send.as_bytes(), 1).unwrap();
45
46 let oflag1 = MQ_OFlag::O_CREAT | MQ_OFlag::O_RDONLY;
47 let mqd1 = mq_open(mq_name, oflag1, mode, Some(&attr)).unwrap();
48 let mut buf = [0u8; 32];
49 let mut prio = 0u32;
50 let len = mq_receive(&mqd1, &mut buf, &mut prio).unwrap();
51 assert_eq!(prio, 1);
52
53 mq_close(mqd1).unwrap();
54 mq_close(mqd0).unwrap();
55 assert_eq!(msg_to_send, str::from_utf8(&buf[0..len]).unwrap());
56 }
57
58 #[test]
test_mq_getattr()59 fn test_mq_getattr() {
60 use nix::mqueue::mq_getattr;
61 const MSG_SIZE: mq_attr_member_t = 32;
62 let initial_attr = MqAttr::new(0, 10, MSG_SIZE, 0);
63 let mq_name = &CString::new(b"/attr_test_get_attr".as_ref()).unwrap();
64 let oflag = MQ_OFlag::O_CREAT | MQ_OFlag::O_WRONLY;
65 let mode = Mode::S_IWUSR | Mode::S_IRUSR | Mode::S_IRGRP | Mode::S_IROTH;
66 let r = mq_open(mq_name, oflag, mode, Some(&initial_attr));
67 if let Err(Errno::ENOSYS) = r {
68 println!("message queues not supported or module not loaded?");
69 return;
70 };
71 let mqd = r.unwrap();
72
73 let read_attr = mq_getattr(&mqd).unwrap();
74 assert_attr_eq!(read_attr, initial_attr);
75 mq_close(mqd).unwrap();
76 }
77
78 // FIXME: Fix failures for mips in QEMU
79 #[test]
80 #[cfg_attr(
81 all(qemu, any(target_arch = "mips", target_arch = "mips64")),
82 ignore
83 )]
test_mq_setattr()84 fn test_mq_setattr() {
85 use nix::mqueue::{mq_getattr, mq_setattr};
86 const MSG_SIZE: mq_attr_member_t = 32;
87 let initial_attr = MqAttr::new(0, 10, MSG_SIZE, 0);
88 let mq_name = &CString::new(b"/attr_test_get_attr".as_ref()).unwrap();
89 let oflag = MQ_OFlag::O_CREAT | MQ_OFlag::O_WRONLY;
90 let mode = Mode::S_IWUSR | Mode::S_IRUSR | Mode::S_IRGRP | Mode::S_IROTH;
91 let r = mq_open(mq_name, oflag, mode, Some(&initial_attr));
92 if let Err(Errno::ENOSYS) = r {
93 println!("message queues not supported or module not loaded?");
94 return;
95 };
96 let mqd = r.unwrap();
97
98 let new_attr = MqAttr::new(0, 20, MSG_SIZE * 2, 100);
99 let old_attr = mq_setattr(&mqd, &new_attr).unwrap();
100 assert_attr_eq!(old_attr, initial_attr);
101
102 // No changes here because according to the Linux man page only
103 // O_NONBLOCK can be set (see tests below)
104 #[cfg(not(any(target_os = "dragonfly", target_os = "netbsd")))]
105 {
106 let new_attr_get = mq_getattr(&mqd).unwrap();
107 assert_ne!(new_attr_get, new_attr);
108 }
109
110 let new_attr_non_blocking = MqAttr::new(
111 MQ_OFlag::O_NONBLOCK.bits() as mq_attr_member_t,
112 10,
113 MSG_SIZE,
114 0,
115 );
116 mq_setattr(&mqd, &new_attr_non_blocking).unwrap();
117 let new_attr_get = mq_getattr(&mqd).unwrap();
118
119 // now the O_NONBLOCK flag has been set
120 #[cfg(not(any(target_os = "dragonfly", target_os = "netbsd")))]
121 {
122 assert_ne!(new_attr_get, initial_attr);
123 }
124 assert_attr_eq!(new_attr_get, new_attr_non_blocking);
125 mq_close(mqd).unwrap();
126 }
127
128 // FIXME: Fix failures for mips in QEMU
129 #[test]
130 #[cfg_attr(
131 all(qemu, any(target_arch = "mips", target_arch = "mips64")),
132 ignore
133 )]
test_mq_set_nonblocking()134 fn test_mq_set_nonblocking() {
135 use nix::mqueue::{mq_getattr, mq_remove_nonblock, mq_set_nonblock};
136 const MSG_SIZE: mq_attr_member_t = 32;
137 let initial_attr = MqAttr::new(0, 10, MSG_SIZE, 0);
138 let mq_name = &CString::new(b"/attr_test_get_attr".as_ref()).unwrap();
139 let oflag = MQ_OFlag::O_CREAT | MQ_OFlag::O_WRONLY;
140 let mode = Mode::S_IWUSR | Mode::S_IRUSR | Mode::S_IRGRP | Mode::S_IROTH;
141 let r = mq_open(mq_name, oflag, mode, Some(&initial_attr));
142 if let Err(Errno::ENOSYS) = r {
143 println!("message queues not supported or module not loaded?");
144 return;
145 };
146 let mqd = r.unwrap();
147 mq_set_nonblock(&mqd).unwrap();
148 let new_attr = mq_getattr(&mqd);
149 let o_nonblock_bits = MQ_OFlag::O_NONBLOCK.bits() as mq_attr_member_t;
150 assert_eq!(new_attr.unwrap().flags() & o_nonblock_bits, o_nonblock_bits);
151 mq_remove_nonblock(&mqd).unwrap();
152 let new_attr = mq_getattr(&mqd);
153 assert_eq!(new_attr.unwrap().flags() & o_nonblock_bits, 0);
154 mq_close(mqd).unwrap();
155 }
156
157 #[test]
test_mq_unlink()158 fn test_mq_unlink() {
159 use nix::mqueue::mq_unlink;
160 const MSG_SIZE: mq_attr_member_t = 32;
161 let initial_attr = MqAttr::new(0, 10, MSG_SIZE, 0);
162 let mq_name_opened = &CString::new(b"/mq_unlink_test".as_ref()).unwrap();
163 #[cfg(not(any(target_os = "dragonfly", target_os = "netbsd")))]
164 let mq_name_not_opened =
165 &CString::new(b"/mq_unlink_test".as_ref()).unwrap();
166 let oflag = MQ_OFlag::O_CREAT | MQ_OFlag::O_WRONLY;
167 let mode = Mode::S_IWUSR | Mode::S_IRUSR | Mode::S_IRGRP | Mode::S_IROTH;
168 let r = mq_open(mq_name_opened, oflag, mode, Some(&initial_attr));
169 if let Err(Errno::ENOSYS) = r {
170 println!("message queues not supported or module not loaded?");
171 return;
172 };
173 let mqd = r.unwrap();
174
175 let res_unlink = mq_unlink(mq_name_opened);
176 assert_eq!(res_unlink, Ok(()));
177
178 // NetBSD (and others which inherit its implementation) defer removing the message
179 // queue name until all references are closed, whereas Linux and others remove the
180 // message queue name immediately.
181 #[cfg(not(any(target_os = "dragonfly", target_os = "netbsd")))]
182 {
183 let res_unlink_not_opened = mq_unlink(mq_name_not_opened);
184 assert_eq!(res_unlink_not_opened, Err(Errno::ENOENT));
185 }
186
187 mq_close(mqd).unwrap();
188 let res_unlink_after_close = mq_unlink(mq_name_opened);
189 assert_eq!(res_unlink_after_close, Err(Errno::ENOENT));
190 }
191