xref: /freebsd/lib/librt/mq.c (revision aa0a1e58)
1 /*-
2  * Copyright (c) 2006 David Xu <davidxu@freebsd.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $FreeBSD$
27  */
28 
29 #include <sys/cdefs.h>
30 #include <sys/types.h>
31 #include <sys/syscall.h>
32 #include <sys/mqueue.h>
33 
34 #include "namespace.h"
35 #include <errno.h>
36 #include <pthread.h>
37 #include <stddef.h>
38 #include <stdlib.h>
39 #include <signal.h>
40 #include "sigev_thread.h"
41 #include "un-namespace.h"
42 #include "libc_private.h"
43 
44 extern int	__sys_kmq_notify(int, const struct sigevent *);
45 extern int	__sys_kmq_open(const char *, int, mode_t,
46 		    const struct mq_attr *);
47 extern int	__sys_kmq_setattr(int, const struct mq_attr *__restrict,
48 		    struct mq_attr *__restrict);
49 extern ssize_t	__sys_kmq_timedreceive(int, char *__restrict, size_t,
50 		    unsigned *__restrict, const struct timespec *__restrict);
51 extern int	__sys_kmq_timedsend(int, const char *, size_t, unsigned,
52 		    const struct timespec *);
53 extern int	__sys_kmq_unlink(const char *);
54 extern int	__sys_close(int fd);
55 
56 struct __mq {
57 	int oshandle;
58 	struct sigev_node *node;
59 };
60 
61 __weak_reference(__mq_open, mq_open);
62 __weak_reference(__mq_open, _mq_open);
63 __weak_reference(__mq_close, mq_close);
64 __weak_reference(__mq_close, _mq_close);
65 __weak_reference(__mq_notify, mq_notify);
66 __weak_reference(__mq_notify, _mq_notify);
67 __weak_reference(__mq_getattr, mq_getattr);
68 __weak_reference(__mq_getattr, _mq_getattr);
69 __weak_reference(__mq_setattr, mq_setattr);
70 __weak_reference(__mq_setattr, _mq_setattr);
71 __weak_reference(__mq_timedreceive_cancel, mq_timedreceive);
72 __weak_reference(__mq_timedreceive, _mq_timedreceive);
73 __weak_reference(__mq_timedsend_cancel, mq_timedsend);
74 __weak_reference(__mq_timedsend, _mq_timedsend);
75 __weak_reference(__mq_unlink, mq_unlink);
76 __weak_reference(__mq_unlink, _mq_unlink);
77 __weak_reference(__mq_send_cancel, mq_send);
78 __weak_reference(__mq_send, _mq_send);
79 __weak_reference(__mq_receive_cancel, mq_receive);
80 __weak_reference(__mq_receive, _mq_receive);
81 
82 mqd_t
83 __mq_open(const char *name, int oflag, mode_t mode,
84 	const struct mq_attr *attr)
85 {
86 	struct __mq *mq;
87 	int err;
88 
89 	mq = malloc(sizeof(struct __mq));
90 	if (mq == NULL)
91 		return (NULL);
92 
93 	mq->oshandle = __sys_kmq_open(name, oflag, mode, attr);
94 	if (mq->oshandle != -1) {
95 		mq->node = NULL;
96 		return (mq);
97 	}
98 	err = errno;
99 	free(mq);
100 	errno = err;
101 	return ((mqd_t)-1L);
102 }
103 
104 int
105 __mq_close(mqd_t mqd)
106 {
107 	int h;
108 
109 	if (mqd->node != NULL) {
110 		__sigev_list_lock();
111 		__sigev_delete_node(mqd->node);
112 		__sigev_list_unlock();
113 	}
114 	h = mqd->oshandle;
115 	free(mqd);
116 	return (__sys_close(h));
117 }
118 
119 typedef void (*mq_func)(union sigval val);
120 
121 static void
122 mq_dispatch(struct sigev_node *sn)
123 {
124 	mq_func f = sn->sn_func;
125 
126 	/*
127 	 * Check generation before calling user function,
128 	 * this should avoid expired notification.
129 	 */
130 	if (sn->sn_gen == sn->sn_info.si_value.sival_int)
131 		f(sn->sn_value);
132 }
133 
134 int
135 __mq_notify(mqd_t mqd, const struct sigevent *evp)
136 {
137 	struct sigevent ev;
138 	struct sigev_node *sn;
139 	int ret;
140 
141 	if (evp == NULL || evp->sigev_notify != SIGEV_THREAD) {
142 		if (mqd->node != NULL) {
143 			__sigev_list_lock();
144 			__sigev_delete_node(mqd->node);
145 			mqd->node = NULL;
146 			__sigev_list_unlock();
147 		}
148 		return __sys_kmq_notify(mqd->oshandle, evp);
149 	}
150 
151 	if (__sigev_check_init()) {
152 		/*
153 		 * Thread library is not enabled.
154 		 */
155 		errno = EINVAL;
156 		return (-1);
157 	}
158 
159 	sn = __sigev_alloc(SI_MESGQ, evp, mqd->node, 1);
160 	if (sn == NULL) {
161 		errno = EAGAIN;
162 		return (-1);
163 	}
164 
165 	sn->sn_id = mqd->oshandle;
166 	sn->sn_dispatch = mq_dispatch;
167 	__sigev_get_sigevent(sn, &ev, sn->sn_gen);
168 	__sigev_list_lock();
169 	if (mqd->node != NULL)
170 		__sigev_delete_node(mqd->node);
171 	mqd->node = sn;
172 	__sigev_register(sn);
173 	ret = __sys_kmq_notify(mqd->oshandle, &ev);
174 	__sigev_list_unlock();
175 	return (ret);
176 }
177 
178 int
179 __mq_getattr(mqd_t mqd, struct mq_attr *attr)
180 {
181 
182 	return __sys_kmq_setattr(mqd->oshandle, NULL, attr);
183 }
184 
185 int
186 __mq_setattr(mqd_t mqd, const struct mq_attr *newattr, struct mq_attr *oldattr)
187 {
188 
189 	return __sys_kmq_setattr(mqd->oshandle, newattr, oldattr);
190 }
191 
192 ssize_t
193 __mq_timedreceive(mqd_t mqd, char *buf, size_t len,
194 	unsigned *prio, const struct timespec *timeout)
195 {
196 
197 	return __sys_kmq_timedreceive(mqd->oshandle, buf, len, prio, timeout);
198 }
199 
200 ssize_t
201 __mq_timedreceive_cancel(mqd_t mqd, char *buf, size_t len,
202 	unsigned *prio, const struct timespec *timeout)
203 {
204 	int ret;
205 
206 	_pthread_cancel_enter(1);
207 	ret = __sys_kmq_timedreceive(mqd->oshandle, buf, len, prio, timeout);
208 	_pthread_cancel_leave(ret == -1);
209 	return (ret);
210 }
211 
212 ssize_t
213 __mq_receive(mqd_t mqd, char *buf, size_t len, unsigned *prio)
214 {
215 
216 	return __sys_kmq_timedreceive(mqd->oshandle, buf, len, prio, NULL);
217 }
218 
219 ssize_t
220 __mq_receive_cancel(mqd_t mqd, char *buf, size_t len, unsigned *prio)
221 {
222 	int ret;
223 
224 	_pthread_cancel_enter(1);
225 	ret = __sys_kmq_timedreceive(mqd->oshandle, buf, len, prio, NULL);
226 	_pthread_cancel_leave(ret == -1);
227 	return (ret);
228 }
229 ssize_t
230 __mq_timedsend(mqd_t mqd, char *buf, size_t len,
231 	unsigned prio, const struct timespec *timeout)
232 {
233 
234 	return __sys_kmq_timedsend(mqd->oshandle, buf, len, prio, timeout);
235 }
236 
237 ssize_t
238 __mq_timedsend_cancel(mqd_t mqd, char *buf, size_t len,
239 	unsigned prio, const struct timespec *timeout)
240 {
241 	int ret;
242 
243 	_pthread_cancel_enter(1);
244 	ret = __sys_kmq_timedsend(mqd->oshandle, buf, len, prio, timeout);
245 	_pthread_cancel_leave(ret == -1);
246 	return (ret);
247 }
248 
249 ssize_t
250 __mq_send(mqd_t mqd, char *buf, size_t len, unsigned prio)
251 {
252 
253 	return __sys_kmq_timedsend(mqd->oshandle, buf, len, prio, NULL);
254 }
255 
256 
257 ssize_t
258 __mq_send_cancel(mqd_t mqd, char *buf, size_t len, unsigned prio)
259 {
260 	int ret;
261 
262 	_pthread_cancel_enter(1);
263 	ret = __sys_kmq_timedsend(mqd->oshandle, buf, len, prio, NULL);
264 	_pthread_cancel_leave(ret == -1);
265 	return (ret);
266 }
267 
268 int
269 __mq_unlink(const char *path)
270 {
271 
272 	return __sys_kmq_unlink(path);
273 }
274 
275 int
276 __mq_oshandle(mqd_t mqd)
277 {
278 
279 	return (mqd->oshandle);
280 }
281