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