• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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