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
43struct __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
69mqd_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
91int
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
106typedef void (*mq_func)(union sigval val);
107
108static void
109mq_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
121int
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
165int
166__mq_getattr(mqd_t mqd, struct mq_attr *attr)
167{
168
169	return __sys_kmq_setattr(mqd->oshandle, NULL, attr);
170}
171
172int
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
179ssize_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
187ssize_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
199ssize_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
206ssize_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}
216ssize_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
224ssize_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
236ssize_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
244ssize_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
255int
256__mq_unlink(const char *path)
257{
258
259	return __sys_kmq_unlink(path);
260}
261
262#pragma weak mq_getfd_np
263int
264mq_getfd_np(mqd_t mqd)
265{
266
267	return (mqd->oshandle);
268}
269