sysv_msg.c revision 194832
1139804Simp/*-
22729Sdfr * Implementation of SVID messages
32729Sdfr *
42729Sdfr * Author:  Daniel Boulet
52729Sdfr *
62729Sdfr * Copyright 1993 Daniel Boulet and RTMX Inc.
72729Sdfr *
82729Sdfr * This system call was implemented by Daniel Boulet under contract from RTMX.
92729Sdfr *
102729Sdfr * Redistribution and use in source forms, with and without modification,
112729Sdfr * are permitted provided that this entire comment appears intact.
122729Sdfr *
132729Sdfr * Redistribution in binary form may occur without any restrictions.
142729Sdfr * Obviously, it would be nice if you gave credit where credit is due
152729Sdfr * but requiring it would be too onerous.
162729Sdfr *
172729Sdfr * This software is provided ``AS IS'' without any warranties of any kind.
182729Sdfr */
19140614Srwatson/*-
20140614Srwatson * Copyright (c) 2003-2005 McAfee, Inc.
21140614Srwatson * All rights reserved.
22140614Srwatson *
23140614Srwatson * This software was developed for the FreeBSD Project in part by McAfee
24140614Srwatson * Research, the Security Research Division of McAfee, Inc under DARPA/SPAWAR
25140614Srwatson * contract N66001-01-C-8035 ("CBOSS"), as part of the DARPA CHATS research
26140614Srwatson * program.
27140614Srwatson *
28140614Srwatson * Redistribution and use in source and binary forms, with or without
29140614Srwatson * modification, are permitted provided that the following conditions
30140614Srwatson * are met:
31140614Srwatson * 1. Redistributions of source code must retain the above copyright
32140614Srwatson *    notice, this list of conditions and the following disclaimer.
33140614Srwatson * 2. Redistributions in binary form must reproduce the above copyright
34140614Srwatson *    notice, this list of conditions and the following disclaimer in the
35140614Srwatson *    documentation and/or other materials provided with the distribution.
36140614Srwatson *
37140614Srwatson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
38140614Srwatson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
39140614Srwatson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
40140614Srwatson * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
41140614Srwatson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
42140614Srwatson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
43140614Srwatson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
44140614Srwatson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
45140614Srwatson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
46140614Srwatson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
47140614Srwatson * SUCH DAMAGE.
48140614Srwatson */
492729Sdfr
50116182Sobrien#include <sys/cdefs.h>
51116182Sobrien__FBSDID("$FreeBSD: head/sys/kern/sysv_msg.c 194832 2009-06-24 13:35:38Z jhb $");
52116182Sobrien
5359839Speter#include "opt_sysvipc.h"
5459839Speter
552729Sdfr#include <sys/param.h>
562729Sdfr#include <sys/systm.h>
5711626Sbde#include <sys/sysproto.h>
582729Sdfr#include <sys/kernel.h>
59164033Srwatson#include <sys/priv.h>
602729Sdfr#include <sys/proc.h>
6182607Sdillon#include <sys/lock.h>
6282607Sdillon#include <sys/mutex.h>
63129882Sphk#include <sys/module.h>
642729Sdfr#include <sys/msg.h>
6569449Salfred#include <sys/syscall.h>
66140839Ssobomax#include <sys/syscallsubr.h>
6711626Sbde#include <sys/sysent.h>
6859839Speter#include <sys/sysctl.h>
6959839Speter#include <sys/malloc.h>
7068024Srwatson#include <sys/jail.h>
712729Sdfr
72163606Srwatson#include <security/mac/mac_framework.h>
73163606Srwatson
7459839Speterstatic MALLOC_DEFINE(M_MSG, "msg", "SVID compatible message queues");
7559839Speter
7692723Salfredstatic void msginit(void);
7792723Salfredstatic int msgunload(void);
7892723Salfredstatic int sysvmsg_modload(struct module *, int, void *);
7910358Sjulian
80100523Salfred#ifdef MSG_DEBUG
81100523Salfred#define DPRINTF(a)	printf a
82100523Salfred#else
83194575Srdivacky#define DPRINTF(a)	(void)0
84100523Salfred#endif
852729Sdfr
8692723Salfredstatic void msg_freehdr(struct msg *msghdr);
872729Sdfr
8811626Sbde/* XXX casting to (sy_call_t *) is bogus, as usual. */
8912819Sphkstatic sy_call_t *msgcalls[] = {
9011626Sbde	(sy_call_t *)msgctl, (sy_call_t *)msgget,
9111626Sbde	(sy_call_t *)msgsnd, (sy_call_t *)msgrcv
9211626Sbde};
932729Sdfr
9459839Speter#ifndef MSGSSZ
9559839Speter#define MSGSSZ	8		/* Each segment must be 2^N long */
9659839Speter#endif
9759839Speter#ifndef MSGSEG
9859839Speter#define MSGSEG	2048		/* must be less than 32767 */
9959839Speter#endif
10059839Speter#define MSGMAX	(MSGSSZ*MSGSEG)
10159839Speter#ifndef MSGMNB
10259839Speter#define MSGMNB	2048		/* max # of bytes in a queue */
10359839Speter#endif
10459839Speter#ifndef MSGMNI
10559839Speter#define MSGMNI	40
10659839Speter#endif
10759839Speter#ifndef MSGTQL
10859839Speter#define MSGTQL	40
10959839Speter#endif
11059839Speter
11159839Speter/*
11259839Speter * Based on the configuration parameters described in an SVR2 (yes, two)
11359839Speter * config(1m) man page.
11459839Speter *
11559839Speter * Each message is broken up and stored in segments that are msgssz bytes
11659839Speter * long.  For efficiency reasons, this should be a power of two.  Also,
11759839Speter * it doesn't make sense if it is less than 8 or greater than about 256.
11859839Speter * Consequently, msginit in kern/sysv_msg.c checks that msgssz is a power of
11959839Speter * two between 8 and 1024 inclusive (and panic's if it isn't).
12059839Speter */
12159839Speterstruct msginfo msginfo = {
12259839Speter                MSGMAX,         /* max chars in a message */
12359839Speter                MSGMNI,         /* # of message queue identifiers */
12459839Speter                MSGMNB,         /* max chars in a queue */
12559839Speter                MSGTQL,         /* max messages in system */
12659839Speter                MSGSSZ,         /* size of a message segment */
12759839Speter                		/* (must be small power of 2 greater than 4) */
12859839Speter                MSGSEG          /* number of message segments */
12959839Speter};
13059839Speter
13159839Speter/*
13259839Speter * macros to convert between msqid_ds's and msqid's.
13359839Speter * (specific to this implementation)
13459839Speter */
13559839Speter#define MSQID(ix,ds)	((ix) & 0xffff | (((ds).msg_perm.seq << 16) & 0xffff0000))
13659839Speter#define MSQID_IX(id)	((id) & 0xffff)
13759839Speter#define MSQID_SEQ(id)	(((id) >> 16) & 0xffff)
13859839Speter
13959839Speter/*
14059839Speter * The rest of this file is specific to this particular implementation.
14159839Speter */
14259839Speter
14359839Speterstruct msgmap {
14459839Speter	short	next;		/* next segment in buffer */
14559839Speter    				/* -1 -> available */
14659839Speter    				/* 0..(MSGSEG-1) -> index of next segment */
14759839Speter};
14859839Speter
14959839Speter#define MSG_LOCKED	01000	/* Is this msqid_ds locked? */
15059839Speter
15112819Sphkstatic int nfree_msgmaps;	/* # of free map entries */
15212819Sphkstatic short free_msgmaps;	/* head of linked list of free map entries */
15359839Speterstatic struct msg *free_msghdrs;/* list of free msg headers */
15459839Speterstatic char *msgpool;		/* MSGMAX byte long msg buffer pool */
15559839Speterstatic struct msgmap *msgmaps;	/* MSGSEG msgmap structures */
15659839Speterstatic struct msg *msghdrs;	/* MSGTQL msg headers */
157137613Srwatsonstatic struct msqid_kernel *msqids;	/* MSGMNI msqid_kernel struct's */
158101772Salfredstatic struct mtx msq_mtx;	/* global mutex for message queues. */
1592729Sdfr
16059839Speterstatic void
16169449Salfredmsginit()
1622729Sdfr{
1632729Sdfr	register int i;
1642729Sdfr
16583765Smr	TUNABLE_INT_FETCH("kern.ipc.msgseg", &msginfo.msgseg);
16683765Smr	TUNABLE_INT_FETCH("kern.ipc.msgssz", &msginfo.msgssz);
16783765Smr	msginfo.msgmax = msginfo.msgseg * msginfo.msgssz;
16883765Smr	TUNABLE_INT_FETCH("kern.ipc.msgmni", &msginfo.msgmni);
169139436Srwatson	TUNABLE_INT_FETCH("kern.ipc.msgmnb", &msginfo.msgmnb);
170139436Srwatson	TUNABLE_INT_FETCH("kern.ipc.msgtql", &msginfo.msgtql);
17183765Smr
172111119Simp	msgpool = malloc(msginfo.msgmax, M_MSG, M_WAITOK);
17359839Speter	if (msgpool == NULL)
17459839Speter		panic("msgpool is NULL");
175111119Simp	msgmaps = malloc(sizeof(struct msgmap) * msginfo.msgseg, M_MSG, M_WAITOK);
17659839Speter	if (msgmaps == NULL)
17759839Speter		panic("msgmaps is NULL");
178111119Simp	msghdrs = malloc(sizeof(struct msg) * msginfo.msgtql, M_MSG, M_WAITOK);
17959839Speter	if (msghdrs == NULL)
18059839Speter		panic("msghdrs is NULL");
181137613Srwatson	msqids = malloc(sizeof(struct msqid_kernel) * msginfo.msgmni, M_MSG,
182137613Srwatson	    M_WAITOK);
18359839Speter	if (msqids == NULL)
18459839Speter		panic("msqids is NULL");
18559839Speter
1862729Sdfr	/*
1872729Sdfr	 * msginfo.msgssz should be a power of two for efficiency reasons.
1882729Sdfr	 * It is also pretty silly if msginfo.msgssz is less than 8
1892729Sdfr	 * or greater than about 256 so ...
1902729Sdfr	 */
1912729Sdfr
1922729Sdfr	i = 8;
1932729Sdfr	while (i < 1024 && i != msginfo.msgssz)
1942729Sdfr		i <<= 1;
1952729Sdfr    	if (i != msginfo.msgssz) {
196100523Salfred		DPRINTF(("msginfo.msgssz=%d (0x%x)\n", msginfo.msgssz,
197100523Salfred		    msginfo.msgssz));
1982729Sdfr		panic("msginfo.msgssz not a small power of 2");
1992729Sdfr	}
2002729Sdfr
2012729Sdfr	if (msginfo.msgseg > 32767) {
202100523Salfred		DPRINTF(("msginfo.msgseg=%d\n", msginfo.msgseg));
2032729Sdfr		panic("msginfo.msgseg > 32767");
2042729Sdfr	}
2052729Sdfr
2062729Sdfr	if (msgmaps == NULL)
2072729Sdfr		panic("msgmaps is NULL");
2082729Sdfr
2092729Sdfr	for (i = 0; i < msginfo.msgseg; i++) {
2102729Sdfr		if (i > 0)
2112729Sdfr			msgmaps[i-1].next = i;
2122729Sdfr		msgmaps[i].next = -1;	/* implies entry is available */
2132729Sdfr	}
2142729Sdfr	free_msgmaps = 0;
2152729Sdfr	nfree_msgmaps = msginfo.msgseg;
2162729Sdfr
2172729Sdfr	if (msghdrs == NULL)
2182729Sdfr		panic("msghdrs is NULL");
2192729Sdfr
2202729Sdfr	for (i = 0; i < msginfo.msgtql; i++) {
2212729Sdfr		msghdrs[i].msg_type = 0;
2222729Sdfr		if (i > 0)
2232729Sdfr			msghdrs[i-1].msg_next = &msghdrs[i];
2242729Sdfr		msghdrs[i].msg_next = NULL;
225140614Srwatson#ifdef MAC
226172930Srwatson		mac_sysvmsg_init(&msghdrs[i]);
227140614Srwatson#endif
2282729Sdfr    	}
2292729Sdfr	free_msghdrs = &msghdrs[0];
2302729Sdfr
2312729Sdfr	if (msqids == NULL)
2322729Sdfr		panic("msqids is NULL");
2332729Sdfr
2342729Sdfr	for (i = 0; i < msginfo.msgmni; i++) {
235137613Srwatson		msqids[i].u.msg_qbytes = 0;	/* implies entry is available */
236137613Srwatson		msqids[i].u.msg_perm.seq = 0;	/* reset to a known value */
237137613Srwatson		msqids[i].u.msg_perm.mode = 0;
238140614Srwatson#ifdef MAC
239172930Srwatson		mac_sysvmsq_init(&msqids[i]);
240140614Srwatson#endif
2412729Sdfr	}
242101772Salfred	mtx_init(&msq_mtx, "msq", NULL, MTX_DEF);
2432729Sdfr}
2442729Sdfr
24569449Salfredstatic int
24669449Salfredmsgunload()
24769449Salfred{
248137613Srwatson	struct msqid_kernel *msqkptr;
24969449Salfred	int msqid;
250140614Srwatson#ifdef MAC
251140614Srwatson	int i;
252140614Srwatson#endif
25369449Salfred
25469449Salfred	for (msqid = 0; msqid < msginfo.msgmni; msqid++) {
25569449Salfred		/*
25669449Salfred		 * Look for an unallocated and unlocked msqid_ds.
25769449Salfred		 * msqid_ds's can be locked by msgsnd or msgrcv while
25869449Salfred		 * they are copying the message in/out.  We can't
25969449Salfred		 * re-use the entry until they release it.
26069449Salfred		 */
261137613Srwatson		msqkptr = &msqids[msqid];
262137613Srwatson		if (msqkptr->u.msg_qbytes != 0 ||
263137613Srwatson		    (msqkptr->u.msg_perm.mode & MSG_LOCKED) != 0)
26469449Salfred			break;
26569449Salfred	}
26669449Salfred	if (msqid != msginfo.msgmni)
26769449Salfred		return (EBUSY);
26869449Salfred
269140614Srwatson#ifdef MAC
270140614Srwatson	for (i = 0; i < msginfo.msgtql; i++)
271172930Srwatson		mac_sysvmsg_destroy(&msghdrs[i]);
272140614Srwatson	for (msqid = 0; msqid < msginfo.msgmni; msqid++)
273172930Srwatson		mac_sysvmsq_destroy(&msqids[msqid]);
274140614Srwatson#endif
27569449Salfred	free(msgpool, M_MSG);
27669449Salfred	free(msgmaps, M_MSG);
27769449Salfred	free(msghdrs, M_MSG);
27869449Salfred	free(msqids, M_MSG);
279101772Salfred	mtx_destroy(&msq_mtx);
28069449Salfred	return (0);
28169449Salfred}
28269449Salfred
28369449Salfred
28469449Salfredstatic int
28569449Salfredsysvmsg_modload(struct module *module, int cmd, void *arg)
28669449Salfred{
28769449Salfred	int error = 0;
28869449Salfred
28969449Salfred	switch (cmd) {
29069449Salfred	case MOD_LOAD:
29169449Salfred		msginit();
29269449Salfred		break;
29369449Salfred	case MOD_UNLOAD:
29469449Salfred		error = msgunload();
29569449Salfred		break;
29669449Salfred	case MOD_SHUTDOWN:
29769449Salfred		break;
29869449Salfred	default:
29969449Salfred		error = EINVAL;
30069449Salfred		break;
30169449Salfred	}
30269449Salfred	return (error);
30369449Salfred}
30469449Salfred
30571038Sdesstatic moduledata_t sysvmsg_mod = {
30671038Sdes	"sysvmsg",
30769449Salfred	&sysvmsg_modload,
30869449Salfred	NULL
30969449Salfred};
31069449Salfred
31188633SalfredSYSCALL_MODULE_HELPER(msgsys);
31288633SalfredSYSCALL_MODULE_HELPER(msgctl);
31388633SalfredSYSCALL_MODULE_HELPER(msgget);
31488633SalfredSYSCALL_MODULE_HELPER(msgsnd);
31588633SalfredSYSCALL_MODULE_HELPER(msgrcv);
31669449Salfred
317194832SjhbDECLARE_MODULE(sysvmsg, sysvmsg_mod, SI_SUB_SYSV_MSG, SI_ORDER_FIRST);
31871038SdesMODULE_VERSION(sysvmsg, 1);
31969449Salfred
3202729Sdfr/*
321167211Srwatson * Entry point for all MSG calls.
3222729Sdfr */
3232729Sdfrint
32483366Sjulianmsgsys(td, uap)
32583366Sjulian	struct thread *td;
32611626Sbde	/* XXX actually varargs. */
32711626Sbde	struct msgsys_args /* {
328118615Snectar		int	which;
32911626Sbde		int	a2;
33011626Sbde		int	a3;
33111626Sbde		int	a4;
33211626Sbde		int	a5;
33311626Sbde		int	a6;
33411626Sbde	} */ *uap;
3352729Sdfr{
33682607Sdillon	int error;
3372729Sdfr
338192895Sjamie	if (!prison_allow(td->td_ucred, PR_ALLOW_SYSVIPC))
33991703Sjhb		return (ENOSYS);
340118615Snectar	if (uap->which < 0 ||
341118615Snectar	    uap->which >= sizeof(msgcalls)/sizeof(msgcalls[0]))
34291703Sjhb		return (EINVAL);
34383366Sjulian	error = (*msgcalls[uap->which])(td, &uap->a2);
34482607Sdillon	return (error);
3452729Sdfr}
3462729Sdfr
3472729Sdfrstatic void
3482729Sdfrmsg_freehdr(msghdr)
3492729Sdfr	struct msg *msghdr;
3502729Sdfr{
3512729Sdfr	while (msghdr->msg_ts > 0) {
3522729Sdfr		short next;
3532729Sdfr		if (msghdr->msg_spot < 0 || msghdr->msg_spot >= msginfo.msgseg)
3542729Sdfr			panic("msghdr->msg_spot out of range");
3552729Sdfr		next = msgmaps[msghdr->msg_spot].next;
3562729Sdfr		msgmaps[msghdr->msg_spot].next = free_msgmaps;
3572729Sdfr		free_msgmaps = msghdr->msg_spot;
3582729Sdfr		nfree_msgmaps++;
3592729Sdfr		msghdr->msg_spot = next;
3602729Sdfr		if (msghdr->msg_ts >= msginfo.msgssz)
3612729Sdfr			msghdr->msg_ts -= msginfo.msgssz;
3622729Sdfr		else
3632729Sdfr			msghdr->msg_ts = 0;
3642729Sdfr	}
3652729Sdfr	if (msghdr->msg_spot != -1)
3662729Sdfr		panic("msghdr->msg_spot != -1");
3672729Sdfr	msghdr->msg_next = free_msghdrs;
3682729Sdfr	free_msghdrs = msghdr;
369140614Srwatson#ifdef MAC
370172930Srwatson	mac_sysvmsg_cleanup(msghdr);
371140614Srwatson#endif
3722729Sdfr}
3732729Sdfr
37412866Speter#ifndef _SYS_SYSPROTO_H_
3752729Sdfrstruct msgctl_args {
3762729Sdfr	int	msqid;
3772729Sdfr	int	cmd;
37812866Speter	struct	msqid_ds *buf;
3792729Sdfr};
38012866Speter#endif
38112866Speterint
38283366Sjulianmsgctl(td, uap)
38383366Sjulian	struct thread *td;
3842729Sdfr	register struct msgctl_args *uap;
3852729Sdfr{
3862729Sdfr	int msqid = uap->msqid;
3872729Sdfr	int cmd = uap->cmd;
3882729Sdfr	struct msqid_ds msqbuf;
389140839Ssobomax	int error;
390140839Ssobomax
391165403Sjkim	DPRINTF(("call to msgctl(%d, %d, %p)\n", msqid, cmd, uap->buf));
392140839Ssobomax	if (cmd == IPC_SET &&
393140839Ssobomax	    (error = copyin(uap->buf, &msqbuf, sizeof(msqbuf))) != 0)
394140839Ssobomax		return (error);
395141471Sjhb	error = kern_msgctl(td, msqid, cmd, &msqbuf);
396140839Ssobomax	if (cmd == IPC_STAT && error == 0)
397141471Sjhb		error = copyout(&msqbuf, uap->buf, sizeof(struct msqid_ds));
398140839Ssobomax	return (error);
399140839Ssobomax}
400140839Ssobomax
401140839Ssobomaxint
402141471Sjhbkern_msgctl(td, msqid, cmd, msqbuf)
403140839Ssobomax	struct thread *td;
404140839Ssobomax	int msqid;
405140839Ssobomax	int cmd;
406140839Ssobomax	struct msqid_ds *msqbuf;
407140839Ssobomax{
408140839Ssobomax	int rval, error, msqix;
409137613Srwatson	register struct msqid_kernel *msqkptr;
4102729Sdfr
411192895Sjamie	if (!prison_allow(td->td_ucred, PR_ALLOW_SYSVIPC))
41291703Sjhb		return (ENOSYS);
41391703Sjhb
414140839Ssobomax	msqix = IPCID_TO_IX(msqid);
4152729Sdfr
416140839Ssobomax	if (msqix < 0 || msqix >= msginfo.msgmni) {
417140839Ssobomax		DPRINTF(("msqid (%d) out of range (0<=msqid<%d)\n", msqix,
418100523Salfred		    msginfo.msgmni));
419101772Salfred		return (EINVAL);
4202729Sdfr	}
4212729Sdfr
422140839Ssobomax	msqkptr = &msqids[msqix];
4232729Sdfr
424101772Salfred	mtx_lock(&msq_mtx);
425137613Srwatson	if (msqkptr->u.msg_qbytes == 0) {
426100523Salfred		DPRINTF(("no such msqid\n"));
42782607Sdillon		error = EINVAL;
42882607Sdillon		goto done2;
4292729Sdfr	}
430140839Ssobomax	if (msqkptr->u.msg_perm.seq != IPCID_TO_SEQ(msqid)) {
431100523Salfred		DPRINTF(("wrong sequence number\n"));
43282607Sdillon		error = EINVAL;
43382607Sdillon		goto done2;
4342729Sdfr	}
435140614Srwatson#ifdef MAC
436172930Srwatson	error = mac_sysvmsq_check_msqctl(td->td_ucred, msqkptr, cmd);
437162468Srwatson	if (error != 0)
438140614Srwatson		goto done2;
439140614Srwatson#endif
4402729Sdfr
44182607Sdillon	error = 0;
4422729Sdfr	rval = 0;
4432729Sdfr
4442729Sdfr	switch (cmd) {
4452729Sdfr
4462729Sdfr	case IPC_RMID:
4472729Sdfr	{
4482729Sdfr		struct msg *msghdr;
449137613Srwatson		if ((error = ipcperm(td, &msqkptr->u.msg_perm, IPC_M)))
45082607Sdillon			goto done2;
451137613Srwatson
452140614Srwatson#ifdef MAC
453140614Srwatson		/*
454140614Srwatson		 * Check that the thread has MAC access permissions to
455140614Srwatson		 * individual msghdrs.  Note: We need to do this in a
456140614Srwatson		 * separate loop because the actual loop alters the
457140614Srwatson		 * msq/msghdr info as it progresses, and there is no going
458140614Srwatson		 * back if half the way through we discover that the
459140614Srwatson		 * thread cannot free a certain msghdr.  The msq will get
460140614Srwatson		 * into an inconsistent state.
461140614Srwatson		 */
462140614Srwatson		for (msghdr = msqkptr->u.msg_first; msghdr != NULL;
463140614Srwatson		    msghdr = msghdr->msg_next) {
464172930Srwatson			error = mac_sysvmsq_check_msgrmid(td->td_ucred, msghdr);
465162468Srwatson			if (error != 0)
466140614Srwatson				goto done2;
467140614Srwatson		}
468140614Srwatson#endif
469140614Srwatson
4702729Sdfr		/* Free the message headers */
471137613Srwatson		msghdr = msqkptr->u.msg_first;
4722729Sdfr		while (msghdr != NULL) {
4732729Sdfr			struct msg *msghdr_tmp;
4742729Sdfr
4752729Sdfr			/* Free the segments of each message */
476137613Srwatson			msqkptr->u.msg_cbytes -= msghdr->msg_ts;
477137613Srwatson			msqkptr->u.msg_qnum--;
4782729Sdfr			msghdr_tmp = msghdr;
4792729Sdfr			msghdr = msghdr->msg_next;
4802729Sdfr			msg_freehdr(msghdr_tmp);
4812729Sdfr		}
4822729Sdfr
483137613Srwatson		if (msqkptr->u.msg_cbytes != 0)
4842729Sdfr			panic("msg_cbytes is screwed up");
485137613Srwatson		if (msqkptr->u.msg_qnum != 0)
4862729Sdfr			panic("msg_qnum is screwed up");
4872729Sdfr
488137613Srwatson		msqkptr->u.msg_qbytes = 0;	/* Mark it as free */
4892729Sdfr
490140614Srwatson#ifdef MAC
491172930Srwatson		mac_sysvmsq_cleanup(msqkptr);
492140614Srwatson#endif
493140614Srwatson
494137613Srwatson		wakeup(msqkptr);
4952729Sdfr	}
4962729Sdfr
4972729Sdfr		break;
4982729Sdfr
4992729Sdfr	case IPC_SET:
500137613Srwatson		if ((error = ipcperm(td, &msqkptr->u.msg_perm, IPC_M)))
50182607Sdillon			goto done2;
502140839Ssobomax		if (msqbuf->msg_qbytes > msqkptr->u.msg_qbytes) {
503170587Srwatson			error = priv_check(td, PRIV_IPC_MSGSIZE);
50482607Sdillon			if (error)
50582607Sdillon				goto done2;
50643426Sphk		}
507140839Ssobomax		if (msqbuf->msg_qbytes > msginfo.msgmnb) {
508100523Salfred			DPRINTF(("can't increase msg_qbytes beyond %d"
509100523Salfred			    "(truncating)\n", msginfo.msgmnb));
510140839Ssobomax			msqbuf->msg_qbytes = msginfo.msgmnb;	/* silently restrict qbytes to system limit */
5112729Sdfr		}
512140839Ssobomax		if (msqbuf->msg_qbytes == 0) {
513100523Salfred			DPRINTF(("can't reduce msg_qbytes to 0\n"));
51482607Sdillon			error = EINVAL;		/* non-standard errno! */
51582607Sdillon			goto done2;
5162729Sdfr		}
517140839Ssobomax		msqkptr->u.msg_perm.uid = msqbuf->msg_perm.uid;	/* change the owner */
518140839Ssobomax		msqkptr->u.msg_perm.gid = msqbuf->msg_perm.gid;	/* change the owner */
519137613Srwatson		msqkptr->u.msg_perm.mode = (msqkptr->u.msg_perm.mode & ~0777) |
520140839Ssobomax		    (msqbuf->msg_perm.mode & 0777);
521140839Ssobomax		msqkptr->u.msg_qbytes = msqbuf->msg_qbytes;
522137613Srwatson		msqkptr->u.msg_ctime = time_second;
5232729Sdfr		break;
5242729Sdfr
5252729Sdfr	case IPC_STAT:
526137613Srwatson		if ((error = ipcperm(td, &msqkptr->u.msg_perm, IPC_R))) {
527100523Salfred			DPRINTF(("requester doesn't have read access\n"));
52882607Sdillon			goto done2;
5292729Sdfr		}
530141471Sjhb		*msqbuf = msqkptr->u;
5312729Sdfr		break;
5322729Sdfr
5332729Sdfr	default:
534100523Salfred		DPRINTF(("invalid command %d\n", cmd));
53582607Sdillon		error = EINVAL;
53682607Sdillon		goto done2;
5372729Sdfr	}
5382729Sdfr
53982607Sdillon	if (error == 0)
54083366Sjulian		td->td_retval[0] = rval;
54182607Sdillondone2:
542101772Salfred	mtx_unlock(&msq_mtx);
543141471Sjhb	return (error);
5442729Sdfr}
5452729Sdfr
54612866Speter#ifndef _SYS_SYSPROTO_H_
5472729Sdfrstruct msgget_args {
5482729Sdfr	key_t	key;
5492729Sdfr	int	msgflg;
5502729Sdfr};
55112866Speter#endif
55212866Speterint
55383366Sjulianmsgget(td, uap)
55483366Sjulian	struct thread *td;
5552729Sdfr	register struct msgget_args *uap;
5562729Sdfr{
55782607Sdillon	int msqid, error = 0;
5582729Sdfr	int key = uap->key;
5592729Sdfr	int msgflg = uap->msgflg;
56091703Sjhb	struct ucred *cred = td->td_ucred;
561137613Srwatson	register struct msqid_kernel *msqkptr = NULL;
5622729Sdfr
563100523Salfred	DPRINTF(("msgget(0x%x, 0%o)\n", key, msgflg));
5642729Sdfr
565192895Sjamie	if (!prison_allow(td->td_ucred, PR_ALLOW_SYSVIPC))
56691703Sjhb		return (ENOSYS);
56791703Sjhb
568101772Salfred	mtx_lock(&msq_mtx);
5692729Sdfr	if (key != IPC_PRIVATE) {
5702729Sdfr		for (msqid = 0; msqid < msginfo.msgmni; msqid++) {
571137613Srwatson			msqkptr = &msqids[msqid];
572137613Srwatson			if (msqkptr->u.msg_qbytes != 0 &&
573137613Srwatson			    msqkptr->u.msg_perm.key == key)
5742729Sdfr				break;
5752729Sdfr		}
5762729Sdfr		if (msqid < msginfo.msgmni) {
577100523Salfred			DPRINTF(("found public key\n"));
5782729Sdfr			if ((msgflg & IPC_CREAT) && (msgflg & IPC_EXCL)) {
579100523Salfred				DPRINTF(("not exclusive\n"));
58082607Sdillon				error = EEXIST;
58182607Sdillon				goto done2;
5822729Sdfr			}
583137613Srwatson			if ((error = ipcperm(td, &msqkptr->u.msg_perm,
584137613Srwatson			    msgflg & 0700))) {
585100523Salfred				DPRINTF(("requester doesn't have 0%o access\n",
586100523Salfred				    msgflg & 0700));
58782607Sdillon				goto done2;
5882729Sdfr			}
589140614Srwatson#ifdef MAC
590172930Srwatson			error = mac_sysvmsq_check_msqget(cred, msqkptr);
591162468Srwatson			if (error != 0)
592140614Srwatson				goto done2;
593140614Srwatson#endif
5942729Sdfr			goto found;
5952729Sdfr		}
5962729Sdfr	}
5972729Sdfr
598100523Salfred	DPRINTF(("need to allocate the msqid_ds\n"));
5992729Sdfr	if (key == IPC_PRIVATE || (msgflg & IPC_CREAT)) {
6002729Sdfr		for (msqid = 0; msqid < msginfo.msgmni; msqid++) {
6012729Sdfr			/*
6022729Sdfr			 * Look for an unallocated and unlocked msqid_ds.
6032729Sdfr			 * msqid_ds's can be locked by msgsnd or msgrcv while
6042729Sdfr			 * they are copying the message in/out.  We can't
6052729Sdfr			 * re-use the entry until they release it.
6062729Sdfr			 */
607137613Srwatson			msqkptr = &msqids[msqid];
608137613Srwatson			if (msqkptr->u.msg_qbytes == 0 &&
609137613Srwatson			    (msqkptr->u.msg_perm.mode & MSG_LOCKED) == 0)
6102729Sdfr				break;
6112729Sdfr		}
6122729Sdfr		if (msqid == msginfo.msgmni) {
613100523Salfred			DPRINTF(("no more msqid_ds's available\n"));
61482607Sdillon			error = ENOSPC;
61582607Sdillon			goto done2;
6162729Sdfr		}
617100523Salfred		DPRINTF(("msqid %d is available\n", msqid));
618137613Srwatson		msqkptr->u.msg_perm.key = key;
619137613Srwatson		msqkptr->u.msg_perm.cuid = cred->cr_uid;
620137613Srwatson		msqkptr->u.msg_perm.uid = cred->cr_uid;
621137613Srwatson		msqkptr->u.msg_perm.cgid = cred->cr_gid;
622137613Srwatson		msqkptr->u.msg_perm.gid = cred->cr_gid;
623137613Srwatson		msqkptr->u.msg_perm.mode = (msgflg & 0777);
6242729Sdfr		/* Make sure that the returned msqid is unique */
625137613Srwatson		msqkptr->u.msg_perm.seq = (msqkptr->u.msg_perm.seq + 1) & 0x7fff;
626137613Srwatson		msqkptr->u.msg_first = NULL;
627137613Srwatson		msqkptr->u.msg_last = NULL;
628137613Srwatson		msqkptr->u.msg_cbytes = 0;
629137613Srwatson		msqkptr->u.msg_qnum = 0;
630137613Srwatson		msqkptr->u.msg_qbytes = msginfo.msgmnb;
631137613Srwatson		msqkptr->u.msg_lspid = 0;
632137613Srwatson		msqkptr->u.msg_lrpid = 0;
633137613Srwatson		msqkptr->u.msg_stime = 0;
634137613Srwatson		msqkptr->u.msg_rtime = 0;
635137613Srwatson		msqkptr->u.msg_ctime = time_second;
636140614Srwatson#ifdef MAC
637172930Srwatson		mac_sysvmsq_create(cred, msqkptr);
638140614Srwatson#endif
6392729Sdfr	} else {
640100523Salfred		DPRINTF(("didn't find it and wasn't asked to create it\n"));
64182607Sdillon		error = ENOENT;
64282607Sdillon		goto done2;
6432729Sdfr	}
6442729Sdfr
6452729Sdfrfound:
6462729Sdfr	/* Construct the unique msqid */
647137613Srwatson	td->td_retval[0] = IXSEQ_TO_IPCID(msqid, msqkptr->u.msg_perm);
64882607Sdillondone2:
649101772Salfred	mtx_unlock(&msq_mtx);
65082607Sdillon	return (error);
6512729Sdfr}
6522729Sdfr
65312866Speter#ifndef _SYS_SYSPROTO_H_
6542729Sdfrstruct msgsnd_args {
6552729Sdfr	int	msqid;
656109895Salfred	const void	*msgp;
6572729Sdfr	size_t	msgsz;
6582729Sdfr	int	msgflg;
6592729Sdfr};
66012866Speter#endif
66112866Speterint
662165403Sjkimkern_msgsnd(td, msqid, msgp, msgsz, msgflg, mtype)
66383366Sjulian	struct thread *td;
664165403Sjkim	int msqid;
665165403Sjkim	const void *msgp;	/* XXX msgp is actually mtext. */
666165403Sjkim	size_t msgsz;
667165403Sjkim	int msgflg;
668165403Sjkim	long mtype;
6692729Sdfr{
670165403Sjkim	int msqix, segs_needed, error = 0;
671137613Srwatson	register struct msqid_kernel *msqkptr;
6722729Sdfr	register struct msg *msghdr;
6732729Sdfr	short next;
6742729Sdfr
675192895Sjamie	if (!prison_allow(td->td_ucred, PR_ALLOW_SYSVIPC))
67691703Sjhb		return (ENOSYS);
67791703Sjhb
678101772Salfred	mtx_lock(&msq_mtx);
679165403Sjkim	msqix = IPCID_TO_IX(msqid);
6802729Sdfr
681165403Sjkim	if (msqix < 0 || msqix >= msginfo.msgmni) {
682165403Sjkim		DPRINTF(("msqid (%d) out of range (0<=msqid<%d)\n", msqix,
683100523Salfred		    msginfo.msgmni));
68482607Sdillon		error = EINVAL;
68582607Sdillon		goto done2;
6862729Sdfr	}
6872729Sdfr
688165403Sjkim	msqkptr = &msqids[msqix];
689137613Srwatson	if (msqkptr->u.msg_qbytes == 0) {
690100523Salfred		DPRINTF(("no such message queue id\n"));
69182607Sdillon		error = EINVAL;
69282607Sdillon		goto done2;
6932729Sdfr	}
694165403Sjkim	if (msqkptr->u.msg_perm.seq != IPCID_TO_SEQ(msqid)) {
695100523Salfred		DPRINTF(("wrong sequence number\n"));
69682607Sdillon		error = EINVAL;
69782607Sdillon		goto done2;
6982729Sdfr	}
6992729Sdfr
700137613Srwatson	if ((error = ipcperm(td, &msqkptr->u.msg_perm, IPC_W))) {
701100523Salfred		DPRINTF(("requester doesn't have write access\n"));
70282607Sdillon		goto done2;
7032729Sdfr	}
7042729Sdfr
705140614Srwatson#ifdef MAC
706172930Srwatson	error = mac_sysvmsq_check_msqsnd(td->td_ucred, msqkptr);
707162468Srwatson	if (error != 0)
708140614Srwatson		goto done2;
709140614Srwatson#endif
710140614Srwatson
7112729Sdfr	segs_needed = (msgsz + msginfo.msgssz - 1) / msginfo.msgssz;
712165403Sjkim	DPRINTF(("msgsz=%zu, msgssz=%d, segs_needed=%d\n", msgsz,
713165403Sjkim	    msginfo.msgssz, segs_needed));
7142729Sdfr	for (;;) {
7152729Sdfr		int need_more_resources = 0;
7162729Sdfr
7172729Sdfr		/*
7182729Sdfr		 * check msgsz
7192729Sdfr		 * (inside this loop in case msg_qbytes changes while we sleep)
7202729Sdfr		 */
7212729Sdfr
722137613Srwatson		if (msgsz > msqkptr->u.msg_qbytes) {
723137613Srwatson			DPRINTF(("msgsz > msqkptr->u.msg_qbytes\n"));
72482607Sdillon			error = EINVAL;
72582607Sdillon			goto done2;
7262729Sdfr		}
7272729Sdfr
728137613Srwatson		if (msqkptr->u.msg_perm.mode & MSG_LOCKED) {
729100523Salfred			DPRINTF(("msqid is locked\n"));
7302729Sdfr			need_more_resources = 1;
7312729Sdfr		}
732137613Srwatson		if (msgsz + msqkptr->u.msg_cbytes > msqkptr->u.msg_qbytes) {
733100523Salfred			DPRINTF(("msgsz + msg_cbytes > msg_qbytes\n"));
7342729Sdfr			need_more_resources = 1;
7352729Sdfr		}
7362729Sdfr		if (segs_needed > nfree_msgmaps) {
737100523Salfred			DPRINTF(("segs_needed > nfree_msgmaps\n"));
7382729Sdfr			need_more_resources = 1;
7392729Sdfr		}
7402729Sdfr		if (free_msghdrs == NULL) {
741100523Salfred			DPRINTF(("no more msghdrs\n"));
7422729Sdfr			need_more_resources = 1;
7432729Sdfr		}
7442729Sdfr
7452729Sdfr		if (need_more_resources) {
7462729Sdfr			int we_own_it;
7472729Sdfr
7482729Sdfr			if ((msgflg & IPC_NOWAIT) != 0) {
749100523Salfred				DPRINTF(("need more resources but caller "
750100523Salfred				    "doesn't want to wait\n"));
75182607Sdillon				error = EAGAIN;
75282607Sdillon				goto done2;
7532729Sdfr			}
7542729Sdfr
755137613Srwatson			if ((msqkptr->u.msg_perm.mode & MSG_LOCKED) != 0) {
756100523Salfred				DPRINTF(("we don't own the msqid_ds\n"));
7572729Sdfr				we_own_it = 0;
7582729Sdfr			} else {
7592729Sdfr				/* Force later arrivals to wait for our
7602729Sdfr				   request */
761100523Salfred				DPRINTF(("we own the msqid_ds\n"));
762137613Srwatson				msqkptr->u.msg_perm.mode |= MSG_LOCKED;
7632729Sdfr				we_own_it = 1;
7642729Sdfr			}
765164368Sjkim			DPRINTF(("msgsnd:  goodnight\n"));
766137613Srwatson			error = msleep(msqkptr, &msq_mtx, (PZERO - 4) | PCATCH,
767164368Sjkim			    "msgsnd", hz);
768164368Sjkim			DPRINTF(("msgsnd:  good morning, error=%d\n", error));
7692729Sdfr			if (we_own_it)
770137613Srwatson				msqkptr->u.msg_perm.mode &= ~MSG_LOCKED;
771164368Sjkim			if (error == EWOULDBLOCK) {
772164368Sjkim				DPRINTF(("msgsnd:  timed out\n"));
773164368Sjkim				continue;
774164368Sjkim			}
77582607Sdillon			if (error != 0) {
776100523Salfred				DPRINTF(("msgsnd:  interrupted system call\n"));
77782607Sdillon				error = EINTR;
77882607Sdillon				goto done2;
7792729Sdfr			}
7802729Sdfr
7812729Sdfr			/*
7822729Sdfr			 * Make sure that the msq queue still exists
7832729Sdfr			 */
7842729Sdfr
785137613Srwatson			if (msqkptr->u.msg_qbytes == 0) {
786100523Salfred				DPRINTF(("msqid deleted\n"));
78782607Sdillon				error = EIDRM;
78882607Sdillon				goto done2;
7892729Sdfr			}
7902729Sdfr
7912729Sdfr		} else {
792100523Salfred			DPRINTF(("got all the resources that we need\n"));
7932729Sdfr			break;
7942729Sdfr		}
7952729Sdfr	}
7962729Sdfr
7972729Sdfr	/*
7982729Sdfr	 * We have the resources that we need.
7992729Sdfr	 * Make sure!
8002729Sdfr	 */
8012729Sdfr
802137613Srwatson	if (msqkptr->u.msg_perm.mode & MSG_LOCKED)
8032729Sdfr		panic("msg_perm.mode & MSG_LOCKED");
8042729Sdfr	if (segs_needed > nfree_msgmaps)
8052729Sdfr		panic("segs_needed > nfree_msgmaps");
806137613Srwatson	if (msgsz + msqkptr->u.msg_cbytes > msqkptr->u.msg_qbytes)
8072729Sdfr		panic("msgsz + msg_cbytes > msg_qbytes");
8082729Sdfr	if (free_msghdrs == NULL)
8092729Sdfr		panic("no more msghdrs");
8102729Sdfr
8112729Sdfr	/*
8122729Sdfr	 * Re-lock the msqid_ds in case we page-fault when copying in the
8132729Sdfr	 * message
8142729Sdfr	 */
8152729Sdfr
816137613Srwatson	if ((msqkptr->u.msg_perm.mode & MSG_LOCKED) != 0)
8172729Sdfr		panic("msqid_ds is already locked");
818137613Srwatson	msqkptr->u.msg_perm.mode |= MSG_LOCKED;
8192729Sdfr
8202729Sdfr	/*
8212729Sdfr	 * Allocate a message header
8222729Sdfr	 */
8232729Sdfr
8242729Sdfr	msghdr = free_msghdrs;
8252729Sdfr	free_msghdrs = msghdr->msg_next;
8262729Sdfr	msghdr->msg_spot = -1;
8272729Sdfr	msghdr->msg_ts = msgsz;
828165403Sjkim	msghdr->msg_type = mtype;
829140614Srwatson#ifdef MAC
830140614Srwatson	/*
831172930Srwatson	 * XXXMAC: Should the mac_sysvmsq_check_msgmsq check follow here
832140614Srwatson	 * immediately?  Or, should it be checked just before the msg is
833140614Srwatson	 * enqueued in the msgq (as it is done now)?
834140614Srwatson	 */
835172930Srwatson	mac_sysvmsg_create(td->td_ucred, msqkptr, msghdr);
836140614Srwatson#endif
8372729Sdfr
8382729Sdfr	/*
8392729Sdfr	 * Allocate space for the message
8402729Sdfr	 */
8412729Sdfr
8422729Sdfr	while (segs_needed > 0) {
8432729Sdfr		if (nfree_msgmaps <= 0)
8442729Sdfr			panic("not enough msgmaps");
8452729Sdfr		if (free_msgmaps == -1)
8462729Sdfr			panic("nil free_msgmaps");
8472729Sdfr		next = free_msgmaps;
8482729Sdfr		if (next <= -1)
8492729Sdfr			panic("next too low #1");
8502729Sdfr		if (next >= msginfo.msgseg)
8512729Sdfr			panic("next out of range #1");
852100523Salfred		DPRINTF(("allocating segment %d to message\n", next));
8532729Sdfr		free_msgmaps = msgmaps[next].next;
8542729Sdfr		nfree_msgmaps--;
8552729Sdfr		msgmaps[next].next = msghdr->msg_spot;
8562729Sdfr		msghdr->msg_spot = next;
8572729Sdfr		segs_needed--;
8582729Sdfr	}
8592729Sdfr
8602729Sdfr	/*
8612729Sdfr	 * Validate the message type
8622729Sdfr	 */
8632729Sdfr
8642729Sdfr	if (msghdr->msg_type < 1) {
8652729Sdfr		msg_freehdr(msghdr);
866137613Srwatson		msqkptr->u.msg_perm.mode &= ~MSG_LOCKED;
867137613Srwatson		wakeup(msqkptr);
868165403Sjkim		DPRINTF(("mtype (%ld) < 1\n", msghdr->msg_type));
86982607Sdillon		error = EINVAL;
87082607Sdillon		goto done2;
8712729Sdfr	}
8722729Sdfr
8732729Sdfr	/*
8742729Sdfr	 * Copy in the message body
8752729Sdfr	 */
8762729Sdfr
8772729Sdfr	next = msghdr->msg_spot;
8782729Sdfr	while (msgsz > 0) {
8792729Sdfr		size_t tlen;
8802729Sdfr		if (msgsz > msginfo.msgssz)
8812729Sdfr			tlen = msginfo.msgssz;
8822729Sdfr		else
8832729Sdfr			tlen = msgsz;
8842729Sdfr		if (next <= -1)
8852729Sdfr			panic("next too low #2");
8862729Sdfr		if (next >= msginfo.msgseg)
8872729Sdfr			panic("next out of range #2");
888101772Salfred		mtx_unlock(&msq_mtx);
889165403Sjkim		if ((error = copyin(msgp, &msgpool[next * msginfo.msgssz],
8902729Sdfr		    tlen)) != 0) {
891101772Salfred			mtx_lock(&msq_mtx);
892100523Salfred			DPRINTF(("error %d copying in message segment\n",
893100523Salfred			    error));
8942729Sdfr			msg_freehdr(msghdr);
895137613Srwatson			msqkptr->u.msg_perm.mode &= ~MSG_LOCKED;
896137613Srwatson			wakeup(msqkptr);
89782607Sdillon			goto done2;
8982729Sdfr		}
899101772Salfred		mtx_lock(&msq_mtx);
9002729Sdfr		msgsz -= tlen;
901165403Sjkim		msgp = (const char *)msgp + tlen;
9022729Sdfr		next = msgmaps[next].next;
9032729Sdfr	}
9042729Sdfr	if (next != -1)
9052729Sdfr		panic("didn't use all the msg segments");
9062729Sdfr
9072729Sdfr	/*
9082729Sdfr	 * We've got the message.  Unlock the msqid_ds.
9092729Sdfr	 */
9102729Sdfr
911137613Srwatson	msqkptr->u.msg_perm.mode &= ~MSG_LOCKED;
9122729Sdfr
9132729Sdfr	/*
9142729Sdfr	 * Make sure that the msqid_ds is still allocated.
9152729Sdfr	 */
9162729Sdfr
917137613Srwatson	if (msqkptr->u.msg_qbytes == 0) {
9182729Sdfr		msg_freehdr(msghdr);
919137613Srwatson		wakeup(msqkptr);
92082607Sdillon		error = EIDRM;
92182607Sdillon		goto done2;
9222729Sdfr	}
9232729Sdfr
924140614Srwatson#ifdef MAC
9252729Sdfr	/*
926140614Srwatson	 * Note: Since the task/thread allocates the msghdr and usually
927140614Srwatson	 * primes it with its own MAC label, for a majority of policies, it
928140614Srwatson	 * won't be necessary to check whether the msghdr has access
929172930Srwatson	 * permissions to the msgq.  The mac_sysvmsq_check_msqsnd check would
930140614Srwatson	 * suffice in that case.  However, this hook may be required where
931140614Srwatson	 * individual policies derive a non-identical label for the msghdr
932140614Srwatson	 * from the current thread label and may want to check the msghdr
933140614Srwatson	 * enqueue permissions, along with read/write permissions to the
934140614Srwatson	 * msgq.
935140614Srwatson	 */
936172930Srwatson	error = mac_sysvmsq_check_msgmsq(td->td_ucred, msghdr, msqkptr);
937140614Srwatson	if (error != 0) {
938140614Srwatson		msg_freehdr(msghdr);
939140614Srwatson		wakeup(msqkptr);
940140614Srwatson		goto done2;
941140614Srwatson	}
942140614Srwatson#endif
943140614Srwatson
944140614Srwatson	/*
9452729Sdfr	 * Put the message into the queue
9462729Sdfr	 */
947137613Srwatson	if (msqkptr->u.msg_first == NULL) {
948137613Srwatson		msqkptr->u.msg_first = msghdr;
949137613Srwatson		msqkptr->u.msg_last = msghdr;
9502729Sdfr	} else {
951137613Srwatson		msqkptr->u.msg_last->msg_next = msghdr;
952137613Srwatson		msqkptr->u.msg_last = msghdr;
9532729Sdfr	}
954137613Srwatson	msqkptr->u.msg_last->msg_next = NULL;
9552729Sdfr
956137613Srwatson	msqkptr->u.msg_cbytes += msghdr->msg_ts;
957137613Srwatson	msqkptr->u.msg_qnum++;
958137613Srwatson	msqkptr->u.msg_lspid = td->td_proc->p_pid;
959137613Srwatson	msqkptr->u.msg_stime = time_second;
9602729Sdfr
961137613Srwatson	wakeup(msqkptr);
96283366Sjulian	td->td_retval[0] = 0;
96382607Sdillondone2:
964101772Salfred	mtx_unlock(&msq_mtx);
96582607Sdillon	return (error);
9662729Sdfr}
9672729Sdfr
968165403Sjkimint
969165403Sjkimmsgsnd(td, uap)
970165403Sjkim	struct thread *td;
971165403Sjkim	register struct msgsnd_args *uap;
972165403Sjkim{
973165403Sjkim	int error;
974165403Sjkim	long mtype;
975165403Sjkim
976165403Sjkim	DPRINTF(("call to msgsnd(%d, %p, %zu, %d)\n", uap->msqid, uap->msgp,
977165403Sjkim	    uap->msgsz, uap->msgflg));
978165403Sjkim
979165403Sjkim	if ((error = copyin(uap->msgp, &mtype, sizeof(mtype))) != 0) {
980165403Sjkim		DPRINTF(("error %d copying the message type\n", error));
981165403Sjkim		return (error);
982165403Sjkim	}
983165403Sjkim	return (kern_msgsnd(td, uap->msqid,
984165403Sjkim	    (const char *)uap->msgp + sizeof(mtype),
985165403Sjkim	    uap->msgsz, uap->msgflg, mtype));
986165403Sjkim}
987165403Sjkim
98812866Speter#ifndef _SYS_SYSPROTO_H_
9892729Sdfrstruct msgrcv_args {
9902729Sdfr	int	msqid;
9912729Sdfr	void	*msgp;
9922729Sdfr	size_t	msgsz;
9932729Sdfr	long	msgtyp;
9942729Sdfr	int	msgflg;
9952729Sdfr};
99612866Speter#endif
99712866Speterint
998165403Sjkimkern_msgrcv(td, msqid, msgp, msgsz, msgtyp, msgflg, mtype)
99983366Sjulian	struct thread *td;
1000165403Sjkim	int msqid;
1001165403Sjkim	void *msgp;	/* XXX msgp is actually mtext. */
1002165403Sjkim	size_t msgsz;
1003165403Sjkim	long msgtyp;
1004165403Sjkim	int msgflg;
1005165403Sjkim	long *mtype;
10062729Sdfr{
10072729Sdfr	size_t len;
1008137613Srwatson	register struct msqid_kernel *msqkptr;
10092729Sdfr	register struct msg *msghdr;
1010165403Sjkim	int msqix, error = 0;
10112729Sdfr	short next;
10122729Sdfr
1013192895Sjamie	if (!prison_allow(td->td_ucred, PR_ALLOW_SYSVIPC))
101491703Sjhb		return (ENOSYS);
101591703Sjhb
1016165403Sjkim	msqix = IPCID_TO_IX(msqid);
10172729Sdfr
1018165403Sjkim	if (msqix < 0 || msqix >= msginfo.msgmni) {
1019165403Sjkim		DPRINTF(("msqid (%d) out of range (0<=msqid<%d)\n", msqix,
1020100523Salfred		    msginfo.msgmni));
1021101772Salfred		return (EINVAL);
10222729Sdfr	}
10232729Sdfr
1024165403Sjkim	msqkptr = &msqids[msqix];
1025101772Salfred	mtx_lock(&msq_mtx);
1026137613Srwatson	if (msqkptr->u.msg_qbytes == 0) {
1027100523Salfred		DPRINTF(("no such message queue id\n"));
102882607Sdillon		error = EINVAL;
102982607Sdillon		goto done2;
10302729Sdfr	}
1031165403Sjkim	if (msqkptr->u.msg_perm.seq != IPCID_TO_SEQ(msqid)) {
1032100523Salfred		DPRINTF(("wrong sequence number\n"));
103382607Sdillon		error = EINVAL;
103482607Sdillon		goto done2;
10352729Sdfr	}
10362729Sdfr
1037137613Srwatson	if ((error = ipcperm(td, &msqkptr->u.msg_perm, IPC_R))) {
1038100523Salfred		DPRINTF(("requester doesn't have read access\n"));
103982607Sdillon		goto done2;
10402729Sdfr	}
10412729Sdfr
1042140614Srwatson#ifdef MAC
1043172930Srwatson	error = mac_sysvmsq_check_msqrcv(td->td_ucred, msqkptr);
1044162468Srwatson	if (error != 0)
1045140614Srwatson		goto done2;
1046140614Srwatson#endif
1047140614Srwatson
10482729Sdfr	msghdr = NULL;
10492729Sdfr	while (msghdr == NULL) {
10502729Sdfr		if (msgtyp == 0) {
1051137613Srwatson			msghdr = msqkptr->u.msg_first;
10522729Sdfr			if (msghdr != NULL) {
10532729Sdfr				if (msgsz < msghdr->msg_ts &&
10542729Sdfr				    (msgflg & MSG_NOERROR) == 0) {
1055100523Salfred					DPRINTF(("first message on the queue "
1056165403Sjkim					    "is too big (want %zu, got %d)\n",
1057100523Salfred					    msgsz, msghdr->msg_ts));
105882607Sdillon					error = E2BIG;
105982607Sdillon					goto done2;
10602729Sdfr				}
1061140614Srwatson#ifdef MAC
1062172930Srwatson				error = mac_sysvmsq_check_msgrcv(td->td_ucred,
1063140614Srwatson				    msghdr);
1064162468Srwatson				if (error != 0)
1065140614Srwatson					goto done2;
1066140614Srwatson#endif
1067137613Srwatson				if (msqkptr->u.msg_first == msqkptr->u.msg_last) {
1068137613Srwatson					msqkptr->u.msg_first = NULL;
1069137613Srwatson					msqkptr->u.msg_last = NULL;
10702729Sdfr				} else {
1071137613Srwatson					msqkptr->u.msg_first = msghdr->msg_next;
1072137613Srwatson					if (msqkptr->u.msg_first == NULL)
10732729Sdfr						panic("msg_first/last screwed up #1");
10742729Sdfr				}
10752729Sdfr			}
10762729Sdfr		} else {
10772729Sdfr			struct msg *previous;
10782729Sdfr			struct msg **prev;
10792729Sdfr
10802729Sdfr			previous = NULL;
1081137613Srwatson			prev = &(msqkptr->u.msg_first);
10822729Sdfr			while ((msghdr = *prev) != NULL) {
10832729Sdfr				/*
10842729Sdfr				 * Is this message's type an exact match or is
10852729Sdfr				 * this message's type less than or equal to
10862729Sdfr				 * the absolute value of a negative msgtyp?
10872729Sdfr				 * Note that the second half of this test can
10882729Sdfr				 * NEVER be true if msgtyp is positive since
10892729Sdfr				 * msg_type is always positive!
10902729Sdfr				 */
10912729Sdfr
10922729Sdfr				if (msgtyp == msghdr->msg_type ||
10932729Sdfr				    msghdr->msg_type <= -msgtyp) {
1094165403Sjkim					DPRINTF(("found message type %ld, "
1095165403Sjkim					    "requested %ld\n",
1096100523Salfred					    msghdr->msg_type, msgtyp));
10972729Sdfr					if (msgsz < msghdr->msg_ts &&
10982729Sdfr					    (msgflg & MSG_NOERROR) == 0) {
1099100523Salfred						DPRINTF(("requested message "
1100100523Salfred						    "on the queue is too big "
1101165403Sjkim						    "(want %zu, got %hu)\n",
1102100523Salfred						    msgsz, msghdr->msg_ts));
110382607Sdillon						error = E2BIG;
110482607Sdillon						goto done2;
11052729Sdfr					}
1106140614Srwatson#ifdef MAC
1107172930Srwatson					error = mac_sysvmsq_check_msgrcv(
1108140614Srwatson					    td->td_ucred, msghdr);
1109162468Srwatson					if (error != 0)
1110140614Srwatson						goto done2;
1111140614Srwatson#endif
11122729Sdfr					*prev = msghdr->msg_next;
1113137613Srwatson					if (msghdr == msqkptr->u.msg_last) {
11142729Sdfr						if (previous == NULL) {
11152729Sdfr							if (prev !=
1116137613Srwatson							    &msqkptr->u.msg_first)
11172729Sdfr								panic("msg_first/last screwed up #2");
1118137613Srwatson							msqkptr->u.msg_first =
11192729Sdfr							    NULL;
1120137613Srwatson							msqkptr->u.msg_last =
11212729Sdfr							    NULL;
11222729Sdfr						} else {
11232729Sdfr							if (prev ==
1124137613Srwatson							    &msqkptr->u.msg_first)
11252729Sdfr								panic("msg_first/last screwed up #3");
1126137613Srwatson							msqkptr->u.msg_last =
11272729Sdfr							    previous;
11282729Sdfr						}
11292729Sdfr					}
11302729Sdfr					break;
11312729Sdfr				}
11322729Sdfr				previous = msghdr;
11332729Sdfr				prev = &(msghdr->msg_next);
11342729Sdfr			}
11352729Sdfr		}
11362729Sdfr
11372729Sdfr		/*
11382729Sdfr		 * We've either extracted the msghdr for the appropriate
11392729Sdfr		 * message or there isn't one.
11402729Sdfr		 * If there is one then bail out of this loop.
11412729Sdfr		 */
11422729Sdfr
11432729Sdfr		if (msghdr != NULL)
11442729Sdfr			break;
11452729Sdfr
11462729Sdfr		/*
11472729Sdfr		 * Hmph!  No message found.  Does the user want to wait?
11482729Sdfr		 */
11492729Sdfr
11502729Sdfr		if ((msgflg & IPC_NOWAIT) != 0) {
1151165403Sjkim			DPRINTF(("no appropriate message found (msgtyp=%ld)\n",
1152100523Salfred			    msgtyp));
11532729Sdfr			/* The SVID says to return ENOMSG. */
115482607Sdillon			error = ENOMSG;
115582607Sdillon			goto done2;
11562729Sdfr		}
11572729Sdfr
11582729Sdfr		/*
11592729Sdfr		 * Wait for something to happen
11602729Sdfr		 */
11612729Sdfr
1162100523Salfred		DPRINTF(("msgrcv:  goodnight\n"));
1163137613Srwatson		error = msleep(msqkptr, &msq_mtx, (PZERO - 4) | PCATCH,
1164164368Sjkim		    "msgrcv", 0);
1165100523Salfred		DPRINTF(("msgrcv:  good morning (error=%d)\n", error));
11662729Sdfr
116782607Sdillon		if (error != 0) {
1168164368Sjkim			DPRINTF(("msgrcv:  interrupted system call\n"));
116982607Sdillon			error = EINTR;
117082607Sdillon			goto done2;
11712729Sdfr		}
11722729Sdfr
11732729Sdfr		/*
11742729Sdfr		 * Make sure that the msq queue still exists
11752729Sdfr		 */
11762729Sdfr
1177137613Srwatson		if (msqkptr->u.msg_qbytes == 0 ||
1178165403Sjkim		    msqkptr->u.msg_perm.seq != IPCID_TO_SEQ(msqid)) {
1179100523Salfred			DPRINTF(("msqid deleted\n"));
118082607Sdillon			error = EIDRM;
118182607Sdillon			goto done2;
11822729Sdfr		}
11832729Sdfr	}
11842729Sdfr
11852729Sdfr	/*
11862729Sdfr	 * Return the message to the user.
11872729Sdfr	 *
11882729Sdfr	 * First, do the bookkeeping (before we risk being interrupted).
11892729Sdfr	 */
11902729Sdfr
1191137613Srwatson	msqkptr->u.msg_cbytes -= msghdr->msg_ts;
1192137613Srwatson	msqkptr->u.msg_qnum--;
1193137613Srwatson	msqkptr->u.msg_lrpid = td->td_proc->p_pid;
1194137613Srwatson	msqkptr->u.msg_rtime = time_second;
11952729Sdfr
11962729Sdfr	/*
11972729Sdfr	 * Make msgsz the actual amount that we'll be returning.
11982729Sdfr	 * Note that this effectively truncates the message if it is too long
11992729Sdfr	 * (since msgsz is never increased).
12002729Sdfr	 */
12012729Sdfr
1202165403Sjkim	DPRINTF(("found a message, msgsz=%zu, msg_ts=%hu\n", msgsz,
1203100523Salfred	    msghdr->msg_ts));
12042729Sdfr	if (msgsz > msghdr->msg_ts)
12052729Sdfr		msgsz = msghdr->msg_ts;
1206165403Sjkim	*mtype = msghdr->msg_type;
12072729Sdfr
12082729Sdfr	/*
12092729Sdfr	 * Return the segments to the user
12102729Sdfr	 */
12112729Sdfr
12122729Sdfr	next = msghdr->msg_spot;
12132729Sdfr	for (len = 0; len < msgsz; len += msginfo.msgssz) {
12142729Sdfr		size_t tlen;
12152729Sdfr
121645921Ssada		if (msgsz - len > msginfo.msgssz)
12172729Sdfr			tlen = msginfo.msgssz;
12182729Sdfr		else
121945921Ssada			tlen = msgsz - len;
12202729Sdfr		if (next <= -1)
12212729Sdfr			panic("next too low #3");
12222729Sdfr		if (next >= msginfo.msgseg)
12232729Sdfr			panic("next out of range #3");
1224101772Salfred		mtx_unlock(&msq_mtx);
1225165403Sjkim		error = copyout(&msgpool[next * msginfo.msgssz], msgp, tlen);
1226101772Salfred		mtx_lock(&msq_mtx);
122782607Sdillon		if (error != 0) {
1228100523Salfred			DPRINTF(("error (%d) copying out message segment\n",
1229100523Salfred			    error));
12302729Sdfr			msg_freehdr(msghdr);
1231137613Srwatson			wakeup(msqkptr);
123282607Sdillon			goto done2;
12332729Sdfr		}
1234165403Sjkim		msgp = (char *)msgp + tlen;
12352729Sdfr		next = msgmaps[next].next;
12362729Sdfr	}
12372729Sdfr
12382729Sdfr	/*
12392729Sdfr	 * Done, return the actual number of bytes copied out.
12402729Sdfr	 */
12412729Sdfr
12422729Sdfr	msg_freehdr(msghdr);
1243137613Srwatson	wakeup(msqkptr);
124483366Sjulian	td->td_retval[0] = msgsz;
124582607Sdillondone2:
1246101772Salfred	mtx_unlock(&msq_mtx);
124782607Sdillon	return (error);
12482729Sdfr}
124977461Sdd
1250165403Sjkimint
1251165403Sjkimmsgrcv(td, uap)
1252165403Sjkim	struct thread *td;
1253165403Sjkim	register struct msgrcv_args *uap;
1254165403Sjkim{
1255165403Sjkim	int error;
1256165403Sjkim	long mtype;
1257165403Sjkim
1258165403Sjkim	DPRINTF(("call to msgrcv(%d, %p, %zu, %ld, %d)\n", uap->msqid,
1259165403Sjkim	    uap->msgp, uap->msgsz, uap->msgtyp, uap->msgflg));
1260165403Sjkim
1261165403Sjkim	if ((error = kern_msgrcv(td, uap->msqid,
1262165403Sjkim	    (char *)uap->msgp + sizeof(mtype), uap->msgsz,
1263165403Sjkim	    uap->msgtyp, uap->msgflg, &mtype)) != 0)
1264165403Sjkim		return (error);
1265165403Sjkim	if ((error = copyout(&mtype, uap->msgp, sizeof(mtype))) != 0)
1266165403Sjkim		DPRINTF(("error %d copying the message type\n", error));
1267165403Sjkim	return (error);
1268165403Sjkim}
1269165403Sjkim
127077461Sddstatic int
127177461Sddsysctl_msqids(SYSCTL_HANDLER_ARGS)
127277461Sdd{
127377461Sdd
127477461Sdd	return (SYSCTL_OUT(req, msqids,
1275137613Srwatson	    sizeof(struct msqid_kernel) * msginfo.msgmni));
127677461Sdd}
127777461Sdd
1278141710ScsjpSYSCTL_INT(_kern_ipc, OID_AUTO, msgmax, CTLFLAG_RD, &msginfo.msgmax, 0,
1279141710Scsjp    "Maximum message size");
1280141710ScsjpSYSCTL_INT(_kern_ipc, OID_AUTO, msgmni, CTLFLAG_RDTUN, &msginfo.msgmni, 0,
1281141710Scsjp    "Number of message queue identifiers");
1282141710ScsjpSYSCTL_INT(_kern_ipc, OID_AUTO, msgmnb, CTLFLAG_RDTUN, &msginfo.msgmnb, 0,
1283141710Scsjp    "Maximum number of bytes in a queue");
1284141710ScsjpSYSCTL_INT(_kern_ipc, OID_AUTO, msgtql, CTLFLAG_RDTUN, &msginfo.msgtql, 0,
1285141710Scsjp    "Maximum number of messages in the system");
1286141710ScsjpSYSCTL_INT(_kern_ipc, OID_AUTO, msgssz, CTLFLAG_RDTUN, &msginfo.msgssz, 0,
1287141710Scsjp    "Size of a message segment");
1288141710ScsjpSYSCTL_INT(_kern_ipc, OID_AUTO, msgseg, CTLFLAG_RDTUN, &msginfo.msgseg, 0,
1289141710Scsjp    "Number of message segments");
129077461SddSYSCTL_PROC(_kern_ipc, OID_AUTO, msqids, CTLFLAG_RD,
129177461Sdd    NULL, 0, sysctl_msqids, "", "Message queue IDs");
1292