17c478bdstevel@tonic-gate/*
27c478bdstevel@tonic-gate * CDDL HEADER START
37c478bdstevel@tonic-gate *
47c478bdstevel@tonic-gate * The contents of this file are subject to the terms of the
53470957raf * Common Development and Distribution License (the "License").
63470957raf * You may not use this file except in compliance with the License.
77c478bdstevel@tonic-gate *
87c478bdstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bdstevel@tonic-gate * or http://www.opensolaris.org/os/licensing.
107c478bdstevel@tonic-gate * See the License for the specific language governing permissions
117c478bdstevel@tonic-gate * and limitations under the License.
127c478bdstevel@tonic-gate *
137c478bdstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each
147c478bdstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bdstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the
167c478bdstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying
177c478bdstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bdstevel@tonic-gate *
197c478bdstevel@tonic-gate * CDDL HEADER END
207c478bdstevel@tonic-gate */
21e8031f0raf
227c478bdstevel@tonic-gate/*
23a574db8raf * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
247c478bdstevel@tonic-gate * Use is subject to license terms.
257c478bdstevel@tonic-gate */
267c478bdstevel@tonic-gate
277257d1braf#include "lint.h"
28f841f6araf#include "mtlib.h"
297c478bdstevel@tonic-gate#define	_KMEMUSER
307c478bdstevel@tonic-gate#include <sys/param.h>		/* _MQ_OPEN_MAX, _MQ_PRIO_MAX, _SEM_VALUE_MAX */
317c478bdstevel@tonic-gate#undef	_KMEMUSER
327c478bdstevel@tonic-gate#include <mqueue.h>
337c478bdstevel@tonic-gate#include <sys/types.h>
347c478bdstevel@tonic-gate#include <sys/file.h>
357c478bdstevel@tonic-gate#include <sys/mman.h>
367c478bdstevel@tonic-gate#include <errno.h>
377c478bdstevel@tonic-gate#include <stdarg.h>
387c478bdstevel@tonic-gate#include <limits.h>
397c478bdstevel@tonic-gate#include <pthread.h>
407c478bdstevel@tonic-gate#include <assert.h>
417c478bdstevel@tonic-gate#include <string.h>
427c478bdstevel@tonic-gate#include <unistd.h>
437c478bdstevel@tonic-gate#include <stdlib.h>
447c478bdstevel@tonic-gate#include <sys/stat.h>
457c478bdstevel@tonic-gate#include <inttypes.h>
46f841f6araf#include "sigev_thread.h"
477c478bdstevel@tonic-gate#include "pos4obj.h"
48f841f6araf
49f841f6araf/*
50f841f6araf * Default values per message queue
51f841f6araf */
52f841f6araf#define	MQ_MAXMSG	128
53f841f6araf#define	MQ_MAXSIZE	1024
54f841f6araf
55f841f6araf#define	MQ_MAGIC	0x4d534751		/* "MSGQ" */
56f841f6araf
57f841f6araf/*
58f841f6araf * Message header which is part of messages in link list
59f841f6araf */
60f841f6araftypedef struct {
61f841f6araf	uint64_t 	msg_next;	/* offset of next message in the link */
62f841f6araf	uint64_t	msg_len;	/* length of the message */
63f841f6araf} msghdr_t;
64f841f6araf
65f841f6araf/*
66f841f6araf * message queue description
67f841f6araf */
68f841f6arafstruct mq_dn {
69f841f6araf	size_t		mqdn_flags;	/* open description flags */
70f841f6araf};
71f841f6araf
72f841f6araf/*
73f841f6araf * message queue descriptor structure
74f841f6araf */
75f841f6araftypedef struct mq_des {
76f841f6araf	struct mq_des	*mqd_next;	/* list of all open mq descriptors, */
77f841f6araf	struct mq_des	*mqd_prev;	/* needed for fork-safety */
78f841f6araf	int		mqd_magic;	/* magic # to identify mq_des */
79f841f6araf	int		mqd_flags;	/* operation flag per open */
80f841f6araf	struct mq_header *mqd_mq;	/* address pointer of message Q */
81f841f6araf	struct mq_dn	*mqd_mqdn;	/* open	description */
82f841f6araf	thread_communication_data_t *mqd_tcd;	/* SIGEV_THREAD notification */
8334537abraf	int		mqd_ownerdead;	/* mq_exclusive is inconsistent */
84f841f6araf} mqdes_t;
85f841f6araf
86f841f6araf/*
87f841f6araf * message queue common header, part of the mmap()ed file.
88f841f6araf * Since message queues may be shared between 32- and 64-bit processes,
89f841f6araf * care must be taken to make sure that the elements of this structure
90f841f6araf * are identical for both _LP64 and _ILP32 cases.
91f841f6araf */
92f841f6araftypedef struct mq_header {
93f841f6araf	/* first field must be mq_totsize, DO NOT insert before this	*/
94f841f6araf	int64_t		mq_totsize;	/* total size of the Queue */
95f841f6araf	int64_t		mq_maxsz;	/* max size of each message */
96f841f6araf	uint32_t	mq_maxmsg;	/* max messages in the queue */
97f841f6araf	uint32_t	mq_maxprio;	/* maximum mqueue priority */
98f841f6araf	uint32_t	mq_curmaxprio;	/* current maximum MQ priority */
99f841f6araf	uint32_t	mq_mask;	/* priority bitmask */
100f841f6araf	uint64_t	mq_freep;	/* free message's head pointer */
101f841f6araf	uint64_t	mq_headpp;	/* pointer to head pointers */
102f841f6araf	uint64_t	mq_tailpp;	/* pointer to tail pointers */
103f841f6araf	signotify_id_t	mq_sigid;	/* notification id (3 int's) */
104f841f6araf	uint32_t	mq_ntype;	/* notification type (SIGEV_*) */
105f841f6araf	uint64_t	mq_des;		/* pointer to msg Q descriptor */
106f841f6araf	mutex_t		mq_exclusive;	/* acquire for exclusive access */
107f841f6araf	sem_t		mq_rblocked;	/* number of processes rblocked */
108f841f6araf	sem_t		mq_notfull;	/* mq_send()'s block on this */
109f841f6araf	sem_t		mq_notempty;	/* mq_receive()'s block on this */
110f841f6araf	sem_t		mq_spawner;	/* spawner thread blocks on this */
111f841f6araf} mqhdr_t;
1127c478bdstevel@tonic-gate
11306a502ecasper/*
11406a502ecasper * The code assumes that _MQ_OPEN_MAX == -1 or "no fixed implementation limit".
11506a502ecasper * If this assumption is somehow invalidated, mq_open() needs to be changed
11606a502ecasper * back to the old version which kept a count and enforced a limit.
11706a502ecasper * We make sure that this is pointed out to those changing <sys/param.h>
11806a502ecasper * by checking _MQ_OPEN_MAX at compile time.
11906a502ecasper */
12006a502ecasper#if _MQ_OPEN_MAX != -1
121f841f6araf#error "mq_open() no longer enforces _MQ_OPEN_MAX and needs fixing."
12206a502ecasper#endif
12306a502ecasper
1247c478bdstevel@tonic-gate#define	MQ_ALIGNSIZE	8	/* 64-bit alignment */
1257c478bdstevel@tonic-gate
1267c478bdstevel@tonic-gate#ifdef DEBUG
127f841f6araf#define	MQ_ASSERT(x)	assert(x);
1287c478bdstevel@tonic-gate
1297c478bdstevel@tonic-gate#define	MQ_ASSERT_PTR(_m, _p) \
1307c478bdstevel@tonic-gate	assert((_p) != NULL && !((uintptr_t)(_p) & (MQ_ALIGNSIZE -1)) && \
1317c478bdstevel@tonic-gate	    !((uintptr_t)_m + (uintptr_t)(_p) >= (uintptr_t)_m + \
1327c478bdstevel@tonic-gate	    _m->mq_totsize));
1337c478bdstevel@tonic-gate
1347c478bdstevel@tonic-gate#define	MQ_ASSERT_SEMVAL_LEQ(sem, val) { \
1357c478bdstevel@tonic-gate	int _val; \
1367c478bdstevel@tonic-gate	(void) sem_getvalue((sem), &_val); \
1377c478bdstevel@tonic-gate	assert((_val) <= val); }
1387c478bdstevel@tonic-gate#else
1397c478bdstevel@tonic-gate#define	MQ_ASSERT(x)
1407c478bdstevel@tonic-gate#define	MQ_ASSERT_PTR(_m, _p)
1417c478bdstevel@tonic-gate#define	MQ_ASSERT_SEMVAL_LEQ(sem, val)
1427c478bdstevel@tonic-gate#endif
1437c478bdstevel@tonic-gate
1447c478bdstevel@tonic-gate#define	MQ_PTR(m, n)	((msghdr_t *)((uintptr_t)m + (uintptr_t)n))
1457c478bdstevel@tonic-gate#define	HEAD_PTR(m, n)	((uint64_t *)((uintptr_t)m + \
1467c478bdstevel@tonic-gate			(uintptr_t)m->mq_headpp + n * sizeof (uint64_t)))
1477c478bdstevel@tonic-gate#define	TAIL_PTR(m, n)	((uint64_t *)((uintptr_t)m + \
1487c478bdstevel@tonic-gate			(uintptr_t)m->mq_tailpp + n * sizeof (uint64_t)))
1497c478bdstevel@tonic-gate
1507c478bdstevel@tonic-gate#define	MQ_RESERVED	((mqdes_t *)-1)
1517c478bdstevel@tonic-gate
1527c478bdstevel@tonic-gate#define	ABS_TIME	0
1537c478bdstevel@tonic-gate#define	REL_TIME	1
1547c478bdstevel@tonic-gate
155f841f6arafstatic mutex_t mq_list_lock = DEFAULTMUTEX;
156f841f6arafstatic mqdes_t *mq_list = NULL;
157f841f6araf
158f841f6arafextern int __signotify(int cmd, siginfo_t *sigonfo, signotify_id_t *sn_id);
1593470957raf
1607c478bdstevel@tonic-gatestatic int
1617c478bdstevel@tonic-gatemq_is_valid(mqdes_t *mqdp)
1627c478bdstevel@tonic-gate{
1637c478bdstevel@tonic-gate	/*
16406a502ecasper	 * Any use of a message queue after it was closed is
16506a502ecasper	 * undefined.  But the standard strongly favours EBADF
16606a502ecasper	 * returns.  Before we dereference which could be fatal,
16706a502ecasper	 * we first do some pointer sanity checks.
1687c478bdstevel@tonic-gate	 */
16906a502ecasper	if (mqdp != NULL && mqdp != MQ_RESERVED &&
17006a502ecasper	    ((uintptr_t)mqdp & 0x7) == 0) {
17106a502ecasper		return (mqdp->mqd_magic == MQ_MAGIC);
1727c478bdstevel@tonic-gate	}
17306a502ecasper
1747c478bdstevel@tonic-gate	return (0);
1757c478bdstevel@tonic-gate}
1767c478bdstevel@tonic-gate
1777c478bdstevel@tonic-gatestatic void
1787c478bdstevel@tonic-gatemq_init(mqhdr_t *mqhp, size_t msgsize, ssize_t maxmsg)
1797c478bdstevel@tonic-gate{
1807c478bdstevel@tonic-gate	int		i;
1817c478bdstevel@tonic-gate	uint64_t	temp;
1827c478bdstevel@tonic-gate	uint64_t	currentp;
1837c478bdstevel@tonic-gate	uint64_t	nextp;
1847c478bdstevel@tonic-gate
1857c478bdstevel@tonic-gate	/*
1867c478bdstevel@tonic-gate	 * We only need to initialize the non-zero fields.  The use of
1877c478bdstevel@tonic-gate	 * ftruncate() on the message queue file assures that the
18834537abraf	 * pages will be zero-filled.
1897c478bdstevel@tonic-gate	 */
19034537abraf	(void) mutex_init(&mqhp->mq_exclusive,
19134537abraf	    USYNC_PROCESS | LOCK_ROBUST, NULL);
1927c478bdstevel@tonic-gate	(void) sem_init(&mqhp->mq_rblocked, 1, 0);
1937c478bdstevel@tonic-gate	(void) sem_init(&mqhp->mq_notempty, 1, 0);
1943470957raf	(void) sem_init(&mqhp->mq_spawner, 1, 0);
1957c478bdstevel@tonic-gate	(void) sem_init(&mqhp->mq_notfull, 1, (uint_t)maxmsg);
1967c478bdstevel@tonic-gate
1977c478bdstevel@tonic-gate	mqhp->mq_maxsz = msgsize;
1987c478bdstevel@tonic-gate	mqhp->mq_maxmsg = maxmsg;
1997c478bdstevel@tonic-gate
2007c478bdstevel@tonic-gate	/*
2017c478bdstevel@tonic-gate	 * As of this writing (1997), there are 32 message queue priorities.
2023470957raf	 * If this is to change, then the size of the mq_mask will
2033470957raf	 * also have to change.  If DEBUG is defined, assert that
2047c478bdstevel@tonic-gate	 * _MQ_PRIO_MAX hasn't changed.
2057c478bdstevel@tonic-gate	 */
2067c478bdstevel@tonic-gate	mqhp->mq_maxprio = _MQ_PRIO_MAX;
2073470957raf#if defined(DEBUG)
2083470957raf	/* LINTED always true */
2097c478bdstevel@tonic-gate	MQ_ASSERT(sizeof (mqhp->mq_mask) * 8 >= _MQ_PRIO_MAX);
2103470957raf#endif
2117c478bdstevel@tonic-gate
2127c478bdstevel@tonic-gate	/*
2137c478bdstevel@tonic-gate	 * Since the message queue can be mapped into different
2147c478bdstevel@tonic-gate	 * virtual address ranges by different processes, we don't
2157c478bdstevel@tonic-gate	 * keep track of pointers, only offsets into the shared region.
2167c478bdstevel@tonic-gate	 */
2177c478bdstevel@tonic-gate	mqhp->mq_headpp = sizeof (mqhdr_t);
2187c478bdstevel@tonic-gate	mqhp->mq_tailpp = mqhp->mq_headpp +
219a574db8raf	    mqhp->mq_maxprio * sizeof (uint64_t);
2207c478bdstevel@tonic-gate	mqhp->mq_freep = mqhp->mq_tailpp +
221a574db8raf	    mqhp->mq_maxprio * sizeof (uint64_t);
2227c478bdstevel@tonic-gate
2237c478bdstevel@tonic-gate	currentp = mqhp->mq_freep;
2247c478bdstevel@tonic-gate	MQ_PTR(mqhp, currentp)->msg_next = 0;
2257c478bdstevel@tonic-gate
2267c478bdstevel@tonic-gate	temp = (mqhp->mq_maxsz + MQ_ALIGNSIZE - 1) & ~(MQ_ALIGNSIZE - 1);
2277c478bdstevel@tonic-gate	for (i = 1; i < mqhp->mq_maxmsg; i++) {
2287c478bdstevel@tonic-gate		nextp = currentp + sizeof (msghdr_t) + temp;
2297c478bdstevel@tonic-gate		MQ_PTR(mqhp, currentp)->msg_next = nextp;
2307c478bdstevel@tonic-gate		MQ_PTR(mqhp, nextp)->msg_next = 0;
2317c478bdstevel@tonic-gate		currentp = nextp;
2327c478bdstevel@tonic-gate	}
2337c478bdstevel@tonic-gate}
2347c478bdstevel@tonic-gate
2357c478bdstevel@tonic-gatestatic size_t
2367c478bdstevel@tonic-gatemq_getmsg(mqhdr_t *mqhp, char *msgp, uint_t *msg_prio)
2377c478bdstevel@tonic-gate{
2387c478bdstevel@tonic-gate	uint64_t currentp;
2397c478bdstevel@tonic-gate	msghdr_t *curbuf;
2407c478bdstevel@tonic-gate	uint64_t *headpp;
2417c478bdstevel@tonic-gate	uint64_t *tailpp;
2427c478bdstevel@tonic-gate
2433470957raf	MQ_ASSERT(MUTEX_HELD(&mqhp->mq_exclusive));
2447c478bdstevel@tonic-gate
2457c478bdstevel@tonic-gate	/*
2467c478bdstevel@tonic-gate	 * Get the head and tail pointers for the queue of maximum
2477c478bdstevel@tonic-gate	 * priority.  We shouldn't be here unless there is a message for
2487c478bdstevel@tonic-gate	 * us, so it's fair to assert that both the head and tail
2497c478bdstevel@tonic-gate	 * pointers are non-NULL.
2507c478bdstevel@tonic-gate	 */
2517c478bdstevel@tonic-gate	headpp = HEAD_PTR(mqhp, mqhp->mq_curmaxprio);
2527c478bdstevel@tonic-gate	tailpp = TAIL_PTR(mqhp, mqhp->mq_curmaxprio);
2537c478bdstevel@tonic-gate
2547c478bdstevel@tonic-gate	if (msg_prio != NULL)
2557c478bdstevel@tonic-gate		*msg_prio = mqhp->mq_curmaxprio;
2567c478bdstevel@tonic-gate
2577c478bdstevel@tonic-gate	currentp = *headpp;
2587c478bdstevel@tonic-gate	MQ_ASSERT_PTR(mqhp, currentp);
2597c478bdstevel@tonic-gate	curbuf = MQ_PTR(mqhp, currentp);
2607c478bdstevel@tonic-gate
261e86c3f0Toomas Soome	if ((*headpp = curbuf->msg_next) == 0) {
2627c478bdstevel@tonic-gate		/*
2637c478bdstevel@tonic-gate		 * We just nuked the last message in this priority's queue.
2647c478bdstevel@tonic-gate		 * Twiddle this priority's bit, and then find the next bit
2657c478bdstevel@tonic-gate		 * tipped.
2667c478bdstevel@tonic-gate		 */
2677c478bdstevel@tonic-gate		uint_t prio = mqhp->mq_curmaxprio;
2687c478bdstevel@tonic-gate
2697c478bdstevel@tonic-gate		mqhp->mq_mask &= ~(1u << prio);
2707c478bdstevel@tonic-gate
2717c478bdstevel@tonic-gate		for (; prio != 0; prio--)
2727c478bdstevel@tonic-gate			if (mqhp->mq_mask & (1u << prio))
2737c478bdstevel@tonic-gate				break;
2747c478bdstevel@tonic-gate		mqhp->mq_curmaxprio = prio;
2757c478bdstevel@tonic-gate
276e86c3f0Toomas Soome		*tailpp = 0;
2777c478bdstevel@tonic-gate	}
2787c478bdstevel@tonic-gate
2797c478bdstevel@tonic-gate	/*
2807c478bdstevel@tonic-gate	 * Copy the message, and put the buffer back on the free list.
2817c478bdstevel@tonic-gate	 */
2827c478bdstevel@tonic-gate	(void) memcpy(msgp, (char *)&curbuf[1], curbuf->msg_len);
2837c478bdstevel@tonic-gate	curbuf->msg_next = mqhp->mq_freep;
2847c478bdstevel@tonic-gate	mqhp->mq_freep = currentp;
2857c478bdstevel@tonic-gate
2867c478bdstevel@tonic-gate	return (curbuf->msg_len);
2877c478bdstevel@tonic-gate}
2887c478bdstevel@tonic-gate
2897c478bdstevel@tonic-gate
2907c478bdstevel@tonic-gatestatic void
2917c478bdstevel@tonic-gatemq_putmsg(mqhdr_t *mqhp, const char *msgp, ssize_t len, uint_t prio)
2927c478bdstevel@tonic-gate{
2937c478bdstevel@tonic-gate	uint64_t currentp;
2947c478bdstevel@tonic-gate	msghdr_t *curbuf;
2957c478bdstevel@tonic-gate	uint64_t *headpp;
2967c478bdstevel@tonic-gate	uint64_t *tailpp;
2977c478bdstevel@tonic-gate
2983470957raf	MQ_ASSERT(MUTEX_HELD(&mqhp->mq_exclusive));
2997c478bdstevel@tonic-gate
3007c478bdstevel@tonic-gate	/*
3017c478bdstevel@tonic-gate	 * Grab a free message block, and link it in.  We shouldn't
3027c478bdstevel@tonic-gate	 * be here unless there is room in the queue for us;  it's
3037c478bdstevel@tonic-gate	 * fair to assert that the free pointer is non-NULL.
3047c478bdstevel@tonic-gate	 */
3057c478bdstevel@tonic-gate	currentp = mqhp->mq_freep;
3067c478bdstevel@tonic-gate	MQ_ASSERT_PTR(mqhp, currentp);
3077c478bdstevel@tonic-gate	curbuf = MQ_PTR(mqhp, currentp);
3087c478bdstevel@tonic-gate
3097c478bdstevel@tonic-gate	/*
3107c478bdstevel@tonic-gate	 * Remove a message from the free list, and copy in the new contents.
3117c478bdstevel@tonic-gate	 */
3127c478bdstevel@tonic-gate	mqhp->mq_freep = curbuf->msg_next;
313e86c3f0Toomas Soome	curbuf->msg_next = 0;
3147c478bdstevel@tonic-gate	(void) memcpy((char *)&curbuf[1], msgp, len);
3157c478bdstevel@tonic-gate	curbuf->msg_len = len;
3167c478bdstevel@tonic-gate
3177c478bdstevel@tonic-gate	headpp = HEAD_PTR(mqhp, prio);
3187c478bdstevel@tonic-gate	tailpp = TAIL_PTR(mqhp, prio);
3197c478bdstevel@tonic-gate
3207c478bdstevel@tonic-gate	if (*tailpp == 0) {
3217c478bdstevel@tonic-gate		/*
3227c478bdstevel@tonic-gate		 * This is the first message on this queue.  Set the
3237c478bdstevel@tonic-gate		 * head and tail pointers, and tip the appropriate bit
3247c478bdstevel@tonic-gate		 * in the priority mask.
3257c478bdstevel@tonic-gate		 */
3267c478bdstevel@tonic-gate		*headpp = currentp;
3277c478bdstevel@tonic-gate		*tailpp = currentp;
3287c478bdstevel@tonic-gate		mqhp->mq_mask |= (1u << prio);
3297c478bdstevel@tonic-gate		if (prio > mqhp->mq_curmaxprio)
3307c478bdstevel@tonic-gate			mqhp->mq_curmaxprio = prio;
3317c478bdstevel@tonic-gate	} else {
3327c478bdstevel@tonic-gate		MQ_ASSERT_PTR(mqhp, *tailpp);
3337c478bdstevel@tonic-gate		MQ_PTR(mqhp, *tailpp)->msg_next = currentp;
3347c478bdstevel@tonic-gate		*tailpp = currentp;
3357c478bdstevel@tonic-gate	}
3367c478bdstevel@tonic-gate}
3377c478bdstevel@tonic-gate
33834537abraf/*
33934537abraf * Send a notification and also delete the registration.
34034537abraf */
34134537abrafstatic void
34234537abrafdo_notify(mqhdr_t *mqhp)
34334537abraf{
34434537abraf	(void) __signotify(SN_SEND, NULL, &mqhp->mq_sigid);
34534537abraf	if (mqhp->mq_ntype == SIGEV_THREAD ||
34634537abraf	    mqhp->mq_ntype == SIGEV_PORT)
34734537abraf		(void) sem_post(&mqhp->mq_spawner);
34834537abraf	mqhp->mq_ntype = 0;
34934537abraf	mqhp->mq_des = 0;
35034537abraf}
35134537abraf
35234537abraf/*
35334537abraf * Called when the mq_exclusive lock draws EOWNERDEAD or ENOTRECOVERABLE.
35434537abraf * Wake up anyone waiting on mq_*send() or mq_*receive() and ensure that
35534537abraf * they fail with errno == EBADMSG.  Trigger any registered notification.
35634537abraf */
35734537abrafstatic void
35834537abrafowner_dead(mqdes_t *mqdp, int error)
35934537abraf{
36034537abraf	mqhdr_t *mqhp = mqdp->mqd_mq;
36134537abraf
36234537abraf	mqdp->mqd_ownerdead = 1;
36334537abraf	(void) sem_post(&mqhp->mq_notfull);
36434537abraf	(void) sem_post(&mqhp->mq_notempty);
36534537abraf	if (error == EOWNERDEAD) {
36634537abraf		if (mqhp->mq_sigid.sn_pid != 0)
36734537abraf			do_notify(mqhp);
36834537abraf		(void) mutex_unlock(&mqhp->mq_exclusive);
36934537abraf	}
37034537abraf	errno = EBADMSG;
37134537abraf}
37234537abraf
3737c478bdstevel@tonic-gatemqd_t
3747257d1brafmq_open(const char *path, int oflag, /* mode_t mode, mq_attr *attr */ ...)
3757c478bdstevel@tonic-gate{
3767c478bdstevel@tonic-gate	va_list		ap;
37734537abraf	mode_t		mode = 0;
37834537abraf	struct mq_attr	*attr = NULL;
3797c478bdstevel@tonic-gate	int		fd;
3807c478bdstevel@tonic-gate	int		err;
3817c478bdstevel@tonic-gate	int		cr_flag = 0;
3827c478bdstevel@tonic-gate	int		locked = 0;
3837c478bdstevel@tonic-gate	uint64_t	total_size;
3847c478bdstevel@tonic-gate	size_t		msgsize;
3857c478bdstevel@tonic-gate	ssize_t		maxmsg;
3867c478bdstevel@tonic-gate	uint64_t	temp;
3877c478bdstevel@tonic-gate	void		*ptr;
3887c478bdstevel@tonic-gate	mqdes_t		*mqdp;
3897c478bdstevel@tonic-gate	mqhdr_t		*mqhp;
3907c478bdstevel@tonic-gate	struct mq_dn	*mqdnp;
3917c478bdstevel@tonic-gate
3927c478bdstevel@tonic-gate	if (__pos4obj_check(path) == -1)
3937c478bdstevel@tonic-gate		return ((mqd_t)-1);
3947c478bdstevel@tonic-gate
3957c478bdstevel@tonic-gate	/* acquire MSGQ lock to have atomic operation */
3967c478bdstevel@tonic-gate	if (__pos4obj_lock(path, MQ_LOCK_TYPE) < 0)
3977c478bdstevel@tonic-gate		goto out;
3987c478bdstevel@tonic-gate	locked = 1;
3997c478bdstevel@tonic-gate
4007c478bdstevel@tonic-gate	va_start(ap, oflag);
4017c478bdstevel@tonic-gate	/* filter oflag to have READ/WRITE/CREATE modes only */
4027c478bdstevel@tonic-gate	oflag = oflag & (O_RDONLY|O_WRONLY|O_RDWR|O_CREAT|O_EXCL|O_NONBLOCK);
4037c478bdstevel@tonic-gate	if ((oflag & O_CREAT) != 0) {
4047c478bdstevel@tonic-gate		mode = va_arg(ap, mode_t);
4057c478bdstevel@tonic-gate		attr = va_arg(ap, struct mq_attr *);
4067c478bdstevel@tonic-gate	}
4077c478bdstevel@tonic-gate	va_end(ap);
4087c478bdstevel@tonic-gate
4097c478bdstevel@tonic-gate	if ((fd = __pos4obj_open(path, MQ_PERM_TYPE, oflag,
4107c478bdstevel@tonic-gate	    mode, &cr_flag)) < 0)
4117c478bdstevel@tonic-gate		goto out;
4127c478bdstevel@tonic-gate
4137c478bdstevel@tonic-gate	/* closing permission file */
4147c478bdstevel@tonic-gate	(void) __close_nc(fd);
4157c478bdstevel@tonic-gate
4167c478bdstevel@tonic-gate	/* Try to open/create data file */
4177c478bdstevel@tonic-gate	if (cr_flag) {
4187c478bdstevel@tonic-gate		cr_flag = PFILE_CREATE;
4197c478bdstevel@tonic-gate		if (attr == NULL) {
4207c478bdstevel@tonic-gate			maxmsg = MQ_MAXMSG;
4217c478bdstevel@tonic-gate			msgsize = MQ_MAXSIZE;
4227c478bdstevel@tonic-gate		} else if (attr->mq_maxmsg <= 0 || attr->mq_msgsize <= 0) {
4237c478bdstevel@tonic-gate			errno = EINVAL;
4247c478bdstevel@tonic-gate			goto out;
4257c478bdstevel@tonic-gate		} else if (attr->mq_maxmsg > _SEM_VALUE_MAX) {
4267c478bdstevel@tonic-gate			errno = ENOSPC;
4277c478bdstevel@tonic-gate			goto out;
4287c478bdstevel@tonic-gate		} else {
4297c478bdstevel@tonic-gate			maxmsg = attr->mq_maxmsg;
4307c478bdstevel@tonic-gate			msgsize = attr->mq_msgsize;
4317c478bdstevel@tonic-gate		}
4327c478bdstevel@tonic-gate
4337c478bdstevel@tonic-gate		/* adjust for message size at word boundary */
4347c478bdstevel@tonic-gate		temp = (msgsize + MQ_ALIGNSIZE - 1) & ~(MQ_ALIGNSIZE - 1);
4357c478bdstevel@tonic-gate
4367c478bdstevel@tonic-gate		total_size = sizeof (mqhdr_t) +
437a574db8raf		    maxmsg * (temp + sizeof (msghdr_t)) +
438a574db8raf		    2 * _MQ_PRIO_MAX * sizeof (uint64_t);
4397c478bdstevel@tonic-gate
4407c478bdstevel@tonic-gate		if (total_size > SSIZE_MAX) {
4417c478bdstevel@tonic-gate			errno = ENOSPC;
4427c478bdstevel@tonic-gate			goto out;
4437c478bdstevel@tonic-gate		}
4447c478bdstevel@tonic-gate
4457c478bdstevel@tonic-gate		/*
4467c478bdstevel@tonic-gate		 * data file is opened with read/write to those
4477c478bdstevel@tonic-gate		 * who have read or write permission
4487c478bdstevel@tonic-gate		 */
4497c478bdstevel@tonic-gate		mode = mode | (mode & 0444) >> 1 | (mode & 0222) << 1;
4507c478bdstevel@tonic-gate		if ((fd = __pos4obj_open(path, MQ_DATA_TYPE,
4517c478bdstevel@tonic-gate		    (O_RDWR|O_CREAT|O_EXCL), mode, &err)) < 0)
4527c478bdstevel@tonic-gate			goto out;
4537c478bdstevel@tonic-gate
4547c478bdstevel@tonic-gate		cr_flag |= DFILE_CREATE | DFILE_OPEN;
4557c478bdstevel@tonic-gate
4567c478bdstevel@tonic-gate		/* force permissions to avoid umask effect */
4577c478bdstevel@tonic-gate		if (fchmod(fd, mode) < 0)
4587c478bdstevel@tonic-gate			goto out;
4597c478bdstevel@tonic-gate
4607c478bdstevel@tonic-gate		if (ftruncate64(fd, (off64_t)total_size) < 0)
4617c478bdstevel@tonic-gate			goto out;
4627c478bdstevel@tonic-gate	} else {
4637c478bdstevel@tonic-gate		if ((fd = __pos4obj_open(path, MQ_DATA_TYPE,
4647c478bdstevel@tonic-gate		    O_RDWR, 0666, &err)) < 0)
4657c478bdstevel@tonic-gate			goto out;
4667c478bdstevel@tonic-gate		cr_flag = DFILE_OPEN;
4677c478bdstevel@tonic-gate
4687c478bdstevel@tonic-gate		/* Message queue has not been initialized yet */
4697c478bdstevel@tonic-gate		if (read(fd, &total_size, sizeof (total_size)) !=
4707c478bdstevel@tonic-gate		    sizeof (total_size) || total_size == 0) {
4717c478bdstevel@tonic-gate			errno = ENOENT;
4727c478bdstevel@tonic-gate			goto out;
4737c478bdstevel@tonic-gate		}
4747c478bdstevel@tonic-gate
4757c478bdstevel@tonic-gate		/* Message queue too big for this process to handle */
4767c478bdstevel@tonic-gate		if (total_size > SSIZE_MAX) {
4777c478bdstevel@tonic-gate			errno = EFBIG;
4787c478bdstevel@tonic-gate			goto out;
4797c478bdstevel@tonic-gate		}
4807c478bdstevel@tonic-gate	}
4817c478bdstevel@tonic-gate
4827c478bdstevel@tonic-gate	if ((mqdp = (mqdes_t *)malloc(sizeof (mqdes_t))) == NULL) {
4837c478bdstevel@tonic-gate		errno = ENOMEM;
4847c478bdstevel@tonic-gate		goto out;
4857c478bdstevel@tonic-gate	}
4867c478bdstevel@tonic-gate	cr_flag |= ALLOC_MEM;
4877c478bdstevel@tonic-gate
4887c478bdstevel@tonic-gate	if ((ptr = mmap64(NULL, total_size, PROT_READ|PROT_WRITE,
4897c478bdstevel@tonic-gate	    MAP_SHARED, fd, (off64_t)0)) == MAP_FAILED)
4907c478bdstevel@tonic-gate		goto out;
4917c478bdstevel@tonic-gate	mqhp = ptr;
4927c478bdstevel@tonic-gate	cr_flag |= DFILE_MMAP;
4937c478bdstevel@tonic-gate
4947c478bdstevel@tonic-gate	/* closing data file */
4957c478bdstevel@tonic-gate	(void) __close_nc(fd);
4967c478bdstevel@tonic-gate	cr_flag &= ~DFILE_OPEN;
4977c478bdstevel@tonic-gate
4987c478bdstevel@tonic-gate	/*
4997c478bdstevel@tonic-gate	 * create, unlink, size, mmap, and close description file
5007c478bdstevel@tonic-gate	 * all for a flag word in anonymous shared memory
5017c478bdstevel@tonic-gate	 */
5027c478bdstevel@tonic-gate	if ((fd = __pos4obj_open(path, MQ_DSCN_TYPE, O_RDWR | O_CREAT,
5037c478bdstevel@tonic-gate	    0666, &err)) < 0)
5047c478bdstevel@tonic-gate		goto out;
5057c478bdstevel@tonic-gate	cr_flag |= DFILE_OPEN;
5067c478bdstevel@tonic-gate	(void) __pos4obj_unlink(path, MQ_DSCN_TYPE);
5077c478bdstevel@tonic-gate	if (ftruncate64(fd, (off64_t)sizeof (struct mq_dn)) < 0)
5087c478bdstevel@tonic-gate		goto out;
5097c478bdstevel@tonic-gate
5107c478bdstevel@tonic-gate	if ((ptr = mmap64(NULL, sizeof (struct mq_dn),
5117c478bdstevel@tonic-gate	    PROT_READ | PROT_WRITE, MAP_SHARED, fd, (off64_t)0)) == MAP_FAILED)
5127c478bdstevel@tonic-gate		goto out;
5137c478bdstevel@tonic-gate	mqdnp = ptr;
5147c478bdstevel@tonic-gate	cr_flag |= MQDNP_MMAP;
5157c478bdstevel@tonic-gate
5167c478bdstevel@tonic-gate	(void) __close_nc(fd);
5177c478bdstevel@tonic-gate	cr_flag &= ~DFILE_OPEN;
5187c478bdstevel@tonic-gate
5197c478bdstevel@tonic-gate	/*
5207c478bdstevel@tonic-gate	 * we follow the same strategy as filesystem open() routine,
5217c478bdstevel@tonic-gate	 * where fcntl.h flags are changed to flags defined in file.h.
5227c478bdstevel@tonic-gate	 */
5237c478bdstevel@tonic-gate	mqdp->mqd_flags = (oflag - FOPEN) & (FREAD|FWRITE);
5247c478bdstevel@tonic-gate	mqdnp->mqdn_flags = (oflag - FOPEN) & (FNONBLOCK);
5257c478bdstevel@tonic-gate
5267c478bdstevel@tonic-gate	/* new message queue requires initialization */
5277c478bdstevel@tonic-gate	if ((cr_flag & DFILE_CREATE) != 0) {
5287c478bdstevel@tonic-gate		/* message queue header has to be initialized */
5297c478bdstevel@tonic-gate		mq_init(mqhp, msgsize, maxmsg);
5307c478bdstevel@tonic-gate		mqhp->mq_totsize = total_size;
5317c478bdstevel@tonic-gate	}
5327c478bdstevel@tonic-gate	mqdp->mqd_mq = mqhp;
5337c478bdstevel@tonic-gate	mqdp->mqd_mqdn = mqdnp;
5347c478bdstevel@tonic-gate	mqdp->mqd_magic = MQ_MAGIC;
5353470957raf	mqdp->mqd_tcd = NULL;
53634537abraf	mqdp->mqd_ownerdead = 0;
5373470957raf	if (__pos4obj_unlock(path, MQ_LOCK_TYPE) == 0) {
538f841f6araf		lmutex_lock(&mq_list_lock);
5393470957raf		mqdp->mqd_next = mq_list;
5403470957raf		mqdp->mqd_prev = NULL;
5413470957raf		if (mq_list)
5423470957raf			mq_list->mqd_prev = mqdp;
5433470957raf		mq_list = mqdp;
544f841f6araf		lmutex_unlock(&mq_list_lock);
5457c478bdstevel@tonic-gate		return ((mqd_t)mqdp);
5463470957raf	}
54706a502ecasper
5487c478bdstevel@tonic-gate	locked = 0;	/* fall into the error case */
5497c478bdstevel@tonic-gateout:
5507c478bdstevel@tonic-gate	err = errno;
5517c478bdstevel@tonic-gate	if ((cr_flag & DFILE_OPEN) != 0)
5527c478bdstevel@tonic-gate		(void) __close_nc(fd);
5537c478bdstevel@tonic-gate	if ((cr_flag & DFILE_CREATE) != 0)
5547c478bdstevel@tonic-gate		(void) __pos4obj_unlink(path, MQ_DATA_TYPE);
5557c478bdstevel@tonic-gate	if ((cr_flag & PFILE_CREATE) != 0)
5567c478bdstevel@tonic-gate		(void) __pos4obj_unlink(path, MQ_PERM_TYPE);
5577c478bdstevel@tonic-gate	if ((cr_flag & ALLOC_MEM) != 0)
5587c478bdstevel@tonic-gate		free((void *)mqdp);
5597c478bdstevel@tonic-gate	if ((cr_flag & DFILE_MMAP) != 0)
5607c478bdstevel@tonic-gate		(void) munmap((caddr_t)mqhp, (size_t)total_size);
5617c478bdstevel@tonic-gate	if ((cr_flag & MQDNP_MMAP) != 0)
5627c478bdstevel@tonic-gate		(void) munmap((caddr_t)mqdnp, sizeof (struct mq_dn));
5637c478bdstevel@tonic-gate	if (locked)
5647c478bdstevel@tonic-gate		(void) __pos4obj_unlock(path, MQ_LOCK_TYPE);
5657c478bdstevel@tonic-gate	errno = err;
5667c478bdstevel@tonic-gate	return ((mqd_t)-1);
5677c478bdstevel@tonic-gate}
5687c478bdstevel@tonic-gate
569f841f6arafstatic void
570f841f6arafmq_close_cleanup(mqdes_t *mqdp)
571f841f6araf{
572f841f6araf	mqhdr_t *mqhp = mqdp->mqd_mq;
573f841f6araf	struct mq_dn *mqdnp = mqdp->mqd_mqdn;
574f841f6araf
575f841f6araf	/* invalidate the descriptor before freeing it */
576f841f6araf	mqdp->mqd_magic = 0;
57734537abraf	if (!mqdp->mqd_ownerdead)
57834537abraf		(void) mutex_unlock(&mqhp->mq_exclusive);
579f841f6araf
580f841f6araf	lmutex_lock(&mq_list_lock);
581f841f6araf	if (mqdp->mqd_next)
582f841f6araf		mqdp->mqd_next->mqd_prev = mqdp->mqd_prev;
583f841f6araf	if (mqdp->mqd_prev)
584f841f6araf		mqdp->mqd_prev->mqd_next = mqdp->mqd_next;
585f841f6araf	if (mq_list == mqdp)
586f841f6araf		mq_list = mqdp->mqd_next;
587f841f6araf	lmutex_unlock(&mq_list_lock);
588f841f6araf
589f841f6araf	free(mqdp);
590f841f6araf	(void) munmap((caddr_t)mqdnp, sizeof (struct mq_dn));
591f841f6araf	(void) munmap((caddr_t)mqhp, (size_t)mqhp->mq_totsize);
592f841f6araf}
593f841f6araf
5947c478bdstevel@tonic-gateint
5957257d1brafmq_close(mqd_t mqdes)
5967c478bdstevel@tonic-gate{
5977c478bdstevel@tonic-gate	mqdes_t *mqdp = (mqdes_t *)mqdes;
5987c478bdstevel@tonic-gate	mqhdr_t *mqhp;
5993470957raf	thread_communication_data_t *tcdp;
60034537abraf	int error;
6017c478bdstevel@tonic-gate
6027c478bdstevel@tonic-gate	if (!mq_is_valid(mqdp)) {
6037c478bdstevel@tonic-gate		errno = EBADF;
6047c478bdstevel@tonic-gate		return (-1);
6057c478bdstevel@tonic-gate	}
6067c478bdstevel@tonic-gate
6077c478bdstevel@tonic-gate	mqhp = mqdp->mqd_mq;
60834537abraf	if ((error = mutex_lock(&mqhp->mq_exclusive)) != 0) {
60934537abraf		mqdp->mqd_ownerdead = 1;
61034537abraf		if (error == EOWNERDEAD)
61134537abraf			(void) mutex_unlock(&mqhp->mq_exclusive);
61234537abraf		/* carry on regardless, without holding mq_exclusive */
61334537abraf	}
614f841f6araf
6157c478bdstevel@tonic-gate	if (mqhp->mq_des == (uintptr_t)mqdp &&
6167c478bdstevel@tonic-gate	    mqhp->mq_sigid.sn_pid == getpid()) {
6173470957raf		/* notification is set for this descriptor, remove it */
6187c478bdstevel@tonic-gate		(void) __signotify(SN_CANCEL, NULL, &mqhp->mq_sigid);
6193470957raf		mqhp->mq_ntype = 0;
6207c478bdstevel@tonic-gate		mqhp->mq_des = 0;
6217c478bdstevel@tonic-gate	}
622f841f6araf
623f841f6araf	pthread_cleanup_push(mq_close_cleanup, mqdp);
6243470957raf	if ((tcdp = mqdp->mqd_tcd) != NULL) {
6253470957raf		mqdp->mqd_tcd = NULL;
626f841f6araf		del_sigev_mq(tcdp);	/* possible cancellation point */
6273470957raf	}
628f841f6araf	pthread_cleanup_pop(1);		/* finish in the cleanup handler */
6297c478bdstevel@tonic-gate
630f841f6araf	return (0);
6317c478bdstevel@tonic-gate}
6327c478bdstevel@tonic-gate
6337c478bdstevel@tonic-gateint
6347257d1brafmq_unlink(const char *path)
6357c478bdstevel@tonic-gate{
6367c478bdstevel@tonic-gate	int err;
6377c478bdstevel@tonic-gate
6387c478bdstevel@tonic-gate	if (__pos4obj_check(path) < 0)
6397c478bdstevel@tonic-gate		return (-1);
6407c478bdstevel@tonic-gate
6417c478bdstevel@tonic-gate	if (__pos4obj_lock(path, MQ_LOCK_TYPE) < 0) {
642