sysv_msg.c revision 141471
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 141471 2005-02-07 18:44:55Z jhb $");
52116182Sobrien
5359839Speter#include "opt_sysvipc.h"
54140614Srwatson#include "opt_mac.h"
5559839Speter
562729Sdfr#include <sys/param.h>
572729Sdfr#include <sys/systm.h>
5811626Sbde#include <sys/sysproto.h>
592729Sdfr#include <sys/kernel.h>
602729Sdfr#include <sys/proc.h>
6182607Sdillon#include <sys/lock.h>
62140614Srwatson#include <sys/mac.h>
6382607Sdillon#include <sys/mutex.h>
64129882Sphk#include <sys/module.h>
652729Sdfr#include <sys/msg.h>
6669449Salfred#include <sys/syscall.h>
67140839Ssobomax#include <sys/syscallsubr.h>
6811626Sbde#include <sys/sysent.h>
6959839Speter#include <sys/sysctl.h>
7059839Speter#include <sys/malloc.h>
7168024Srwatson#include <sys/jail.h>
722729Sdfr
7359839Speterstatic MALLOC_DEFINE(M_MSG, "msg", "SVID compatible message queues");
7459839Speter
7592723Salfredstatic void msginit(void);
7692723Salfredstatic int msgunload(void);
7792723Salfredstatic int sysvmsg_modload(struct module *, int, void *);
7810358Sjulian
79100523Salfred#ifdef MSG_DEBUG
80100523Salfred#define DPRINTF(a)	printf a
81100523Salfred#else
82100523Salfred#define DPRINTF(a)
83100523Salfred#endif
84140614Srwatson#ifdef MAC_DEBUG
85140614Srwatson#define MPRINTF(a)	printf a
86140614Srwatson#else
87140614Srwatson#define MPRINTF(a)
88140614Srwatson#endif
892729Sdfr
9092723Salfredstatic void msg_freehdr(struct msg *msghdr);
912729Sdfr
9211626Sbde/* XXX casting to (sy_call_t *) is bogus, as usual. */
9312819Sphkstatic sy_call_t *msgcalls[] = {
9411626Sbde	(sy_call_t *)msgctl, (sy_call_t *)msgget,
9511626Sbde	(sy_call_t *)msgsnd, (sy_call_t *)msgrcv
9611626Sbde};
972729Sdfr
9859839Speter#ifndef MSGSSZ
9959839Speter#define MSGSSZ	8		/* Each segment must be 2^N long */
10059839Speter#endif
10159839Speter#ifndef MSGSEG
10259839Speter#define MSGSEG	2048		/* must be less than 32767 */
10359839Speter#endif
10459839Speter#define MSGMAX	(MSGSSZ*MSGSEG)
10559839Speter#ifndef MSGMNB
10659839Speter#define MSGMNB	2048		/* max # of bytes in a queue */
10759839Speter#endif
10859839Speter#ifndef MSGMNI
10959839Speter#define MSGMNI	40
11059839Speter#endif
11159839Speter#ifndef MSGTQL
11259839Speter#define MSGTQL	40
11359839Speter#endif
11459839Speter
11559839Speter/*
11659839Speter * Based on the configuration parameters described in an SVR2 (yes, two)
11759839Speter * config(1m) man page.
11859839Speter *
11959839Speter * Each message is broken up and stored in segments that are msgssz bytes
12059839Speter * long.  For efficiency reasons, this should be a power of two.  Also,
12159839Speter * it doesn't make sense if it is less than 8 or greater than about 256.
12259839Speter * Consequently, msginit in kern/sysv_msg.c checks that msgssz is a power of
12359839Speter * two between 8 and 1024 inclusive (and panic's if it isn't).
12459839Speter */
12559839Speterstruct msginfo msginfo = {
12659839Speter                MSGMAX,         /* max chars in a message */
12759839Speter                MSGMNI,         /* # of message queue identifiers */
12859839Speter                MSGMNB,         /* max chars in a queue */
12959839Speter                MSGTQL,         /* max messages in system */
13059839Speter                MSGSSZ,         /* size of a message segment */
13159839Speter                		/* (must be small power of 2 greater than 4) */
13259839Speter                MSGSEG          /* number of message segments */
13359839Speter};
13459839Speter
13559839Speter/*
13659839Speter * macros to convert between msqid_ds's and msqid's.
13759839Speter * (specific to this implementation)
13859839Speter */
13959839Speter#define MSQID(ix,ds)	((ix) & 0xffff | (((ds).msg_perm.seq << 16) & 0xffff0000))
14059839Speter#define MSQID_IX(id)	((id) & 0xffff)
14159839Speter#define MSQID_SEQ(id)	(((id) >> 16) & 0xffff)
14259839Speter
14359839Speter/*
14459839Speter * The rest of this file is specific to this particular implementation.
14559839Speter */
14659839Speter
14759839Speterstruct msgmap {
14859839Speter	short	next;		/* next segment in buffer */
14959839Speter    				/* -1 -> available */
15059839Speter    				/* 0..(MSGSEG-1) -> index of next segment */
15159839Speter};
15259839Speter
15359839Speter#define MSG_LOCKED	01000	/* Is this msqid_ds locked? */
15459839Speter
15512819Sphkstatic int nfree_msgmaps;	/* # of free map entries */
15612819Sphkstatic short free_msgmaps;	/* head of linked list of free map entries */
15759839Speterstatic struct msg *free_msghdrs;/* list of free msg headers */
15859839Speterstatic char *msgpool;		/* MSGMAX byte long msg buffer pool */
15959839Speterstatic struct msgmap *msgmaps;	/* MSGSEG msgmap structures */
16059839Speterstatic struct msg *msghdrs;	/* MSGTQL msg headers */
161137613Srwatsonstatic struct msqid_kernel *msqids;	/* MSGMNI msqid_kernel struct's */
162101772Salfredstatic struct mtx msq_mtx;	/* global mutex for message queues. */
1632729Sdfr
16459839Speterstatic void
16569449Salfredmsginit()
1662729Sdfr{
1672729Sdfr	register int i;
1682729Sdfr
16983765Smr	TUNABLE_INT_FETCH("kern.ipc.msgseg", &msginfo.msgseg);
17083765Smr	TUNABLE_INT_FETCH("kern.ipc.msgssz", &msginfo.msgssz);
17183765Smr	msginfo.msgmax = msginfo.msgseg * msginfo.msgssz;
17283765Smr	TUNABLE_INT_FETCH("kern.ipc.msgmni", &msginfo.msgmni);
173139436Srwatson	TUNABLE_INT_FETCH("kern.ipc.msgmnb", &msginfo.msgmnb);
174139436Srwatson	TUNABLE_INT_FETCH("kern.ipc.msgtql", &msginfo.msgtql);
17583765Smr
176111119Simp	msgpool = malloc(msginfo.msgmax, M_MSG, M_WAITOK);
17759839Speter	if (msgpool == NULL)
17859839Speter		panic("msgpool is NULL");
179111119Simp	msgmaps = malloc(sizeof(struct msgmap) * msginfo.msgseg, M_MSG, M_WAITOK);
18059839Speter	if (msgmaps == NULL)
18159839Speter		panic("msgmaps is NULL");
182111119Simp	msghdrs = malloc(sizeof(struct msg) * msginfo.msgtql, M_MSG, M_WAITOK);
18359839Speter	if (msghdrs == NULL)
18459839Speter		panic("msghdrs is NULL");
185137613Srwatson	msqids = malloc(sizeof(struct msqid_kernel) * msginfo.msgmni, M_MSG,
186137613Srwatson	    M_WAITOK);
18759839Speter	if (msqids == NULL)
18859839Speter		panic("msqids is NULL");
18959839Speter
1902729Sdfr	/*
1912729Sdfr	 * msginfo.msgssz should be a power of two for efficiency reasons.
1922729Sdfr	 * It is also pretty silly if msginfo.msgssz is less than 8
1932729Sdfr	 * or greater than about 256 so ...
1942729Sdfr	 */
1952729Sdfr
1962729Sdfr	i = 8;
1972729Sdfr	while (i < 1024 && i != msginfo.msgssz)
1982729Sdfr		i <<= 1;
1992729Sdfr    	if (i != msginfo.msgssz) {
200100523Salfred		DPRINTF(("msginfo.msgssz=%d (0x%x)\n", msginfo.msgssz,
201100523Salfred		    msginfo.msgssz));
2022729Sdfr		panic("msginfo.msgssz not a small power of 2");
2032729Sdfr	}
2042729Sdfr
2052729Sdfr	if (msginfo.msgseg > 32767) {
206100523Salfred		DPRINTF(("msginfo.msgseg=%d\n", msginfo.msgseg));
2072729Sdfr		panic("msginfo.msgseg > 32767");
2082729Sdfr	}
2092729Sdfr
2102729Sdfr	if (msgmaps == NULL)
2112729Sdfr		panic("msgmaps is NULL");
2122729Sdfr
2132729Sdfr	for (i = 0; i < msginfo.msgseg; i++) {
2142729Sdfr		if (i > 0)
2152729Sdfr			msgmaps[i-1].next = i;
2162729Sdfr		msgmaps[i].next = -1;	/* implies entry is available */
2172729Sdfr	}
2182729Sdfr	free_msgmaps = 0;
2192729Sdfr	nfree_msgmaps = msginfo.msgseg;
2202729Sdfr
2212729Sdfr	if (msghdrs == NULL)
2222729Sdfr		panic("msghdrs is NULL");
2232729Sdfr
2242729Sdfr	for (i = 0; i < msginfo.msgtql; i++) {
2252729Sdfr		msghdrs[i].msg_type = 0;
2262729Sdfr		if (i > 0)
2272729Sdfr			msghdrs[i-1].msg_next = &msghdrs[i];
2282729Sdfr		msghdrs[i].msg_next = NULL;
229140614Srwatson#ifdef MAC
230140614Srwatson		mac_init_sysv_msgmsg(&msghdrs[i]);
231140614Srwatson#endif
2322729Sdfr    	}
2332729Sdfr	free_msghdrs = &msghdrs[0];
2342729Sdfr
2352729Sdfr	if (msqids == NULL)
2362729Sdfr		panic("msqids is NULL");
2372729Sdfr
2382729Sdfr	for (i = 0; i < msginfo.msgmni; i++) {
239137613Srwatson		msqids[i].u.msg_qbytes = 0;	/* implies entry is available */
240137613Srwatson		msqids[i].u.msg_perm.seq = 0;	/* reset to a known value */
241137613Srwatson		msqids[i].u.msg_perm.mode = 0;
242140614Srwatson#ifdef MAC
243140614Srwatson		mac_init_sysv_msgqueue(&msqids[i]);
244140614Srwatson#endif
2452729Sdfr	}
246101772Salfred	mtx_init(&msq_mtx, "msq", NULL, MTX_DEF);
2472729Sdfr}
2482729Sdfr
24969449Salfredstatic int
25069449Salfredmsgunload()
25169449Salfred{
252137613Srwatson	struct msqid_kernel *msqkptr;
25369449Salfred	int msqid;
254140614Srwatson#ifdef MAC
255140614Srwatson	int i;
256140614Srwatson#endif
25769449Salfred
25869449Salfred	for (msqid = 0; msqid < msginfo.msgmni; msqid++) {
25969449Salfred		/*
26069449Salfred		 * Look for an unallocated and unlocked msqid_ds.
26169449Salfred		 * msqid_ds's can be locked by msgsnd or msgrcv while
26269449Salfred		 * they are copying the message in/out.  We can't
26369449Salfred		 * re-use the entry until they release it.
26469449Salfred		 */
265137613Srwatson		msqkptr = &msqids[msqid];
266137613Srwatson		if (msqkptr->u.msg_qbytes != 0 ||
267137613Srwatson		    (msqkptr->u.msg_perm.mode & MSG_LOCKED) != 0)
26869449Salfred			break;
26969449Salfred	}
27069449Salfred	if (msqid != msginfo.msgmni)
27169449Salfred		return (EBUSY);
27269449Salfred
273140614Srwatson#ifdef MAC
274140614Srwatson	for (i = 0; i < msginfo.msgtql; i++)
275140614Srwatson		mac_destroy_sysv_msgmsg(&msghdrs[i]);
276140614Srwatson	for (msqid = 0; msqid < msginfo.msgmni; msqid++)
277140614Srwatson		mac_destroy_sysv_msgqueue(&msqids[msqid]);
278140614Srwatson#endif
27969449Salfred	free(msgpool, M_MSG);
28069449Salfred	free(msgmaps, M_MSG);
28169449Salfred	free(msghdrs, M_MSG);
28269449Salfred	free(msqids, M_MSG);
283101772Salfred	mtx_destroy(&msq_mtx);
28469449Salfred	return (0);
28569449Salfred}
28669449Salfred
28769449Salfred
28869449Salfredstatic int
28969449Salfredsysvmsg_modload(struct module *module, int cmd, void *arg)
29069449Salfred{
29169449Salfred	int error = 0;
29269449Salfred
29369449Salfred	switch (cmd) {
29469449Salfred	case MOD_LOAD:
29569449Salfred		msginit();
29669449Salfred		break;
29769449Salfred	case MOD_UNLOAD:
29869449Salfred		error = msgunload();
29969449Salfred		break;
30069449Salfred	case MOD_SHUTDOWN:
30169449Salfred		break;
30269449Salfred	default:
30369449Salfred		error = EINVAL;
30469449Salfred		break;
30569449Salfred	}
30669449Salfred	return (error);
30769449Salfred}
30869449Salfred
30971038Sdesstatic moduledata_t sysvmsg_mod = {
31071038Sdes	"sysvmsg",
31169449Salfred	&sysvmsg_modload,
31269449Salfred	NULL
31369449Salfred};
31469449Salfred
31588633SalfredSYSCALL_MODULE_HELPER(msgsys);
31688633SalfredSYSCALL_MODULE_HELPER(msgctl);
31788633SalfredSYSCALL_MODULE_HELPER(msgget);
31888633SalfredSYSCALL_MODULE_HELPER(msgsnd);
31988633SalfredSYSCALL_MODULE_HELPER(msgrcv);
32069449Salfred
32171038SdesDECLARE_MODULE(sysvmsg, sysvmsg_mod,
32269449Salfred	SI_SUB_SYSV_MSG, SI_ORDER_FIRST);
32371038SdesMODULE_VERSION(sysvmsg, 1);
32469449Salfred
3252729Sdfr/*
3262729Sdfr * Entry point for all MSG calls
32782607Sdillon *
32882607Sdillon * MPSAFE
3292729Sdfr */
3302729Sdfrint
33183366Sjulianmsgsys(td, uap)
33283366Sjulian	struct thread *td;
33311626Sbde	/* XXX actually varargs. */
33411626Sbde	struct msgsys_args /* {
335118615Snectar		int	which;
33611626Sbde		int	a2;
33711626Sbde		int	a3;
33811626Sbde		int	a4;
33911626Sbde		int	a5;
34011626Sbde		int	a6;
34111626Sbde	} */ *uap;
3422729Sdfr{
34382607Sdillon	int error;
3442729Sdfr
34591703Sjhb	if (!jail_sysvipc_allowed && jailed(td->td_ucred))
34691703Sjhb		return (ENOSYS);
347118615Snectar	if (uap->which < 0 ||
348118615Snectar	    uap->which >= sizeof(msgcalls)/sizeof(msgcalls[0]))
34991703Sjhb		return (EINVAL);
35083366Sjulian	error = (*msgcalls[uap->which])(td, &uap->a2);
35182607Sdillon	return (error);
3522729Sdfr}
3532729Sdfr
3542729Sdfrstatic void
3552729Sdfrmsg_freehdr(msghdr)
3562729Sdfr	struct msg *msghdr;
3572729Sdfr{
3582729Sdfr	while (msghdr->msg_ts > 0) {
3592729Sdfr		short next;
3602729Sdfr		if (msghdr->msg_spot < 0 || msghdr->msg_spot >= msginfo.msgseg)
3612729Sdfr			panic("msghdr->msg_spot out of range");
3622729Sdfr		next = msgmaps[msghdr->msg_spot].next;
3632729Sdfr		msgmaps[msghdr->msg_spot].next = free_msgmaps;
3642729Sdfr		free_msgmaps = msghdr->msg_spot;
3652729Sdfr		nfree_msgmaps++;
3662729Sdfr		msghdr->msg_spot = next;
3672729Sdfr		if (msghdr->msg_ts >= msginfo.msgssz)
3682729Sdfr			msghdr->msg_ts -= msginfo.msgssz;
3692729Sdfr		else
3702729Sdfr			msghdr->msg_ts = 0;
3712729Sdfr	}
3722729Sdfr	if (msghdr->msg_spot != -1)
3732729Sdfr		panic("msghdr->msg_spot != -1");
3742729Sdfr	msghdr->msg_next = free_msghdrs;
3752729Sdfr	free_msghdrs = msghdr;
376140614Srwatson#ifdef MAC
377140614Srwatson	mac_cleanup_sysv_msgmsg(msghdr);
378140614Srwatson#endif
3792729Sdfr}
3802729Sdfr
38112866Speter#ifndef _SYS_SYSPROTO_H_
3822729Sdfrstruct msgctl_args {
3832729Sdfr	int	msqid;
3842729Sdfr	int	cmd;
38512866Speter	struct	msqid_ds *buf;
3862729Sdfr};
38712866Speter#endif
3882729Sdfr
38982607Sdillon/*
39082607Sdillon * MPSAFE
39182607Sdillon */
39212866Speterint
39383366Sjulianmsgctl(td, uap)
39483366Sjulian	struct thread *td;
3952729Sdfr	register struct msgctl_args *uap;
3962729Sdfr{
3972729Sdfr	int msqid = uap->msqid;
3982729Sdfr	int cmd = uap->cmd;
3992729Sdfr	struct msqid_ds msqbuf;
400140839Ssobomax	int error;
401140839Ssobomax
402140839Ssobomax	DPRINTF(("call to msgctl(%d, %d, 0x%x)\n", msqid, cmd, uap->buf));
403140839Ssobomax	if (cmd == IPC_SET &&
404140839Ssobomax	    (error = copyin(uap->buf, &msqbuf, sizeof(msqbuf))) != 0)
405140839Ssobomax		return (error);
406141471Sjhb	error = kern_msgctl(td, msqid, cmd, &msqbuf);
407140839Ssobomax	if (cmd == IPC_STAT && error == 0)
408141471Sjhb		error = copyout(&msqbuf, uap->buf, sizeof(struct msqid_ds));
409140839Ssobomax	return (error);
410140839Ssobomax}
411140839Ssobomax
412140839Ssobomaxint
413141471Sjhbkern_msgctl(td, msqid, cmd, msqbuf)
414140839Ssobomax	struct thread *td;
415140839Ssobomax	int msqid;
416140839Ssobomax	int cmd;
417140839Ssobomax	struct msqid_ds *msqbuf;
418140839Ssobomax{
419140839Ssobomax	int rval, error, msqix;
420137613Srwatson	register struct msqid_kernel *msqkptr;
4212729Sdfr
42291703Sjhb	if (!jail_sysvipc_allowed && jailed(td->td_ucred))
42391703Sjhb		return (ENOSYS);
42491703Sjhb
425140839Ssobomax	msqix = IPCID_TO_IX(msqid);
4262729Sdfr
427140839Ssobomax	if (msqix < 0 || msqix >= msginfo.msgmni) {
428140839Ssobomax		DPRINTF(("msqid (%d) out of range (0<=msqid<%d)\n", msqix,
429100523Salfred		    msginfo.msgmni));
430101772Salfred		return (EINVAL);
4312729Sdfr	}
4322729Sdfr
433140839Ssobomax	msqkptr = &msqids[msqix];
4342729Sdfr
435101772Salfred	mtx_lock(&msq_mtx);
436137613Srwatson	if (msqkptr->u.msg_qbytes == 0) {
437100523Salfred		DPRINTF(("no such msqid\n"));
43882607Sdillon		error = EINVAL;
43982607Sdillon		goto done2;
4402729Sdfr	}
441140839Ssobomax	if (msqkptr->u.msg_perm.seq != IPCID_TO_SEQ(msqid)) {
442100523Salfred		DPRINTF(("wrong sequence number\n"));
44382607Sdillon		error = EINVAL;
44482607Sdillon		goto done2;
4452729Sdfr	}
446140614Srwatson#ifdef MAC
447140614Srwatson	error = mac_check_sysv_msqctl(td->td_ucred, msqkptr, cmd);
448140614Srwatson	if (error != 0) {
449140614Srwatson		MPRINTF(("mac_check_sysv_msqctl returned %d\n", error));
450140614Srwatson		goto done2;
451140614Srwatson	}
452140614Srwatson#endif
4532729Sdfr
45482607Sdillon	error = 0;
4552729Sdfr	rval = 0;
4562729Sdfr
4572729Sdfr	switch (cmd) {
4582729Sdfr
4592729Sdfr	case IPC_RMID:
4602729Sdfr	{
4612729Sdfr		struct msg *msghdr;
462137613Srwatson		if ((error = ipcperm(td, &msqkptr->u.msg_perm, IPC_M)))
46382607Sdillon			goto done2;
464137613Srwatson
465140614Srwatson#ifdef MAC
466140614Srwatson		/*
467140614Srwatson		 * Check that the thread has MAC access permissions to
468140614Srwatson		 * individual msghdrs.  Note: We need to do this in a
469140614Srwatson		 * separate loop because the actual loop alters the
470140614Srwatson		 * msq/msghdr info as it progresses, and there is no going
471140614Srwatson		 * back if half the way through we discover that the
472140614Srwatson		 * thread cannot free a certain msghdr.  The msq will get
473140614Srwatson		 * into an inconsistent state.
474140614Srwatson		 */
475140614Srwatson		for (msghdr = msqkptr->u.msg_first; msghdr != NULL;
476140614Srwatson		    msghdr = msghdr->msg_next) {
477140614Srwatson			error = mac_check_sysv_msgrmid(td->td_ucred, msghdr);
478140614Srwatson			if (error != 0) {
479140614Srwatson				MPRINTF(("mac_check_sysv_msgrmid returned %d\n",
480140614Srwatson				    error));
481140614Srwatson				goto done2;
482140614Srwatson			}
483140614Srwatson		}
484140614Srwatson#endif
485140614Srwatson
4862729Sdfr		/* Free the message headers */
487137613Srwatson		msghdr = msqkptr->u.msg_first;
4882729Sdfr		while (msghdr != NULL) {
4892729Sdfr			struct msg *msghdr_tmp;
4902729Sdfr
4912729Sdfr			/* Free the segments of each message */
492137613Srwatson			msqkptr->u.msg_cbytes -= msghdr->msg_ts;
493137613Srwatson			msqkptr->u.msg_qnum--;
4942729Sdfr			msghdr_tmp = msghdr;
4952729Sdfr			msghdr = msghdr->msg_next;
4962729Sdfr			msg_freehdr(msghdr_tmp);
4972729Sdfr		}
4982729Sdfr
499137613Srwatson		if (msqkptr->u.msg_cbytes != 0)
5002729Sdfr			panic("msg_cbytes is screwed up");
501137613Srwatson		if (msqkptr->u.msg_qnum != 0)
5022729Sdfr			panic("msg_qnum is screwed up");
5032729Sdfr
504137613Srwatson		msqkptr->u.msg_qbytes = 0;	/* Mark it as free */
5052729Sdfr
506140614Srwatson#ifdef MAC
507140614Srwatson		mac_cleanup_sysv_msgqueue(msqkptr);
508140614Srwatson#endif
509140614Srwatson
510137613Srwatson		wakeup(msqkptr);
5112729Sdfr	}
5122729Sdfr
5132729Sdfr		break;
5142729Sdfr
5152729Sdfr	case IPC_SET:
516137613Srwatson		if ((error = ipcperm(td, &msqkptr->u.msg_perm, IPC_M)))
51782607Sdillon			goto done2;
518140839Ssobomax		if (msqbuf->msg_qbytes > msqkptr->u.msg_qbytes) {
51993593Sjhb			error = suser(td);
52082607Sdillon			if (error)
52182607Sdillon				goto done2;
52243426Sphk		}
523140839Ssobomax		if (msqbuf->msg_qbytes > msginfo.msgmnb) {
524100523Salfred			DPRINTF(("can't increase msg_qbytes beyond %d"
525100523Salfred			    "(truncating)\n", msginfo.msgmnb));
526140839Ssobomax			msqbuf->msg_qbytes = msginfo.msgmnb;	/* silently restrict qbytes to system limit */
5272729Sdfr		}
528140839Ssobomax		if (msqbuf->msg_qbytes == 0) {
529100523Salfred			DPRINTF(("can't reduce msg_qbytes to 0\n"));
53082607Sdillon			error = EINVAL;		/* non-standard errno! */
53182607Sdillon			goto done2;
5322729Sdfr		}
533140839Ssobomax		msqkptr->u.msg_perm.uid = msqbuf->msg_perm.uid;	/* change the owner */
534140839Ssobomax		msqkptr->u.msg_perm.gid = msqbuf->msg_perm.gid;	/* change the owner */
535137613Srwatson		msqkptr->u.msg_perm.mode = (msqkptr->u.msg_perm.mode & ~0777) |
536140839Ssobomax		    (msqbuf->msg_perm.mode & 0777);
537140839Ssobomax		msqkptr->u.msg_qbytes = msqbuf->msg_qbytes;
538137613Srwatson		msqkptr->u.msg_ctime = time_second;
5392729Sdfr		break;
5402729Sdfr
5412729Sdfr	case IPC_STAT:
542137613Srwatson		if ((error = ipcperm(td, &msqkptr->u.msg_perm, IPC_R))) {
543100523Salfred			DPRINTF(("requester doesn't have read access\n"));
54482607Sdillon			goto done2;
5452729Sdfr		}
546141471Sjhb		*msqbuf = msqkptr->u;
5472729Sdfr		break;
5482729Sdfr
5492729Sdfr	default:
550100523Salfred		DPRINTF(("invalid command %d\n", cmd));
55182607Sdillon		error = EINVAL;
55282607Sdillon		goto done2;
5532729Sdfr	}
5542729Sdfr
55582607Sdillon	if (error == 0)
55683366Sjulian		td->td_retval[0] = rval;
55782607Sdillondone2:
558101772Salfred	mtx_unlock(&msq_mtx);
559141471Sjhb	return (error);
5602729Sdfr}
5612729Sdfr
56212866Speter#ifndef _SYS_SYSPROTO_H_
5632729Sdfrstruct msgget_args {
5642729Sdfr	key_t	key;
5652729Sdfr	int	msgflg;
5662729Sdfr};
56712866Speter#endif
5682729Sdfr
56982607Sdillon/*
57082607Sdillon * MPSAFE
57182607Sdillon */
57212866Speterint
57383366Sjulianmsgget(td, uap)
57483366Sjulian	struct thread *td;
5752729Sdfr	register struct msgget_args *uap;
5762729Sdfr{
57782607Sdillon	int msqid, error = 0;
5782729Sdfr	int key = uap->key;
5792729Sdfr	int msgflg = uap->msgflg;
58091703Sjhb	struct ucred *cred = td->td_ucred;
581137613Srwatson	register struct msqid_kernel *msqkptr = NULL;
5822729Sdfr
583100523Salfred	DPRINTF(("msgget(0x%x, 0%o)\n", key, msgflg));
5842729Sdfr
58591703Sjhb	if (!jail_sysvipc_allowed && jailed(td->td_ucred))
58691703Sjhb		return (ENOSYS);
58791703Sjhb
588101772Salfred	mtx_lock(&msq_mtx);
5892729Sdfr	if (key != IPC_PRIVATE) {
5902729Sdfr		for (msqid = 0; msqid < msginfo.msgmni; msqid++) {
591137613Srwatson			msqkptr = &msqids[msqid];
592137613Srwatson			if (msqkptr->u.msg_qbytes != 0 &&
593137613Srwatson			    msqkptr->u.msg_perm.key == key)
5942729Sdfr				break;
5952729Sdfr		}
5962729Sdfr		if (msqid < msginfo.msgmni) {
597100523Salfred			DPRINTF(("found public key\n"));
5982729Sdfr			if ((msgflg & IPC_CREAT) && (msgflg & IPC_EXCL)) {
599100523Salfred				DPRINTF(("not exclusive\n"));
60082607Sdillon				error = EEXIST;
60182607Sdillon				goto done2;
6022729Sdfr			}
603137613Srwatson			if ((error = ipcperm(td, &msqkptr->u.msg_perm,
604137613Srwatson			    msgflg & 0700))) {
605100523Salfred				DPRINTF(("requester doesn't have 0%o access\n",
606100523Salfred				    msgflg & 0700));
60782607Sdillon				goto done2;
6082729Sdfr			}
609140614Srwatson#ifdef MAC
610140614Srwatson			error = mac_check_sysv_msqget(cred, msqkptr);
611140614Srwatson			if (error != 0) {
612140614Srwatson				MPRINTF(("mac_check_sysv_msqget returned %d\n",
613140614Srwatson				    error));
614140614Srwatson				goto done2;
615140614Srwatson			}
616140614Srwatson#endif
6172729Sdfr			goto found;
6182729Sdfr		}
6192729Sdfr	}
6202729Sdfr
621100523Salfred	DPRINTF(("need to allocate the msqid_ds\n"));
6222729Sdfr	if (key == IPC_PRIVATE || (msgflg & IPC_CREAT)) {
6232729Sdfr		for (msqid = 0; msqid < msginfo.msgmni; msqid++) {
6242729Sdfr			/*
6252729Sdfr			 * Look for an unallocated and unlocked msqid_ds.
6262729Sdfr			 * msqid_ds's can be locked by msgsnd or msgrcv while
6272729Sdfr			 * they are copying the message in/out.  We can't
6282729Sdfr			 * re-use the entry until they release it.
6292729Sdfr			 */
630137613Srwatson			msqkptr = &msqids[msqid];
631137613Srwatson			if (msqkptr->u.msg_qbytes == 0 &&
632137613Srwatson			    (msqkptr->u.msg_perm.mode & MSG_LOCKED) == 0)
6332729Sdfr				break;
6342729Sdfr		}
6352729Sdfr		if (msqid == msginfo.msgmni) {
636100523Salfred			DPRINTF(("no more msqid_ds's available\n"));
63782607Sdillon			error = ENOSPC;
63882607Sdillon			goto done2;
6392729Sdfr		}
640100523Salfred		DPRINTF(("msqid %d is available\n", msqid));
641137613Srwatson		msqkptr->u.msg_perm.key = key;
642137613Srwatson		msqkptr->u.msg_perm.cuid = cred->cr_uid;
643137613Srwatson		msqkptr->u.msg_perm.uid = cred->cr_uid;
644137613Srwatson		msqkptr->u.msg_perm.cgid = cred->cr_gid;
645137613Srwatson		msqkptr->u.msg_perm.gid = cred->cr_gid;
646137613Srwatson		msqkptr->u.msg_perm.mode = (msgflg & 0777);
6472729Sdfr		/* Make sure that the returned msqid is unique */
648137613Srwatson		msqkptr->u.msg_perm.seq = (msqkptr->u.msg_perm.seq + 1) & 0x7fff;
649137613Srwatson		msqkptr->u.msg_first = NULL;
650137613Srwatson		msqkptr->u.msg_last = NULL;
651137613Srwatson		msqkptr->u.msg_cbytes = 0;
652137613Srwatson		msqkptr->u.msg_qnum = 0;
653137613Srwatson		msqkptr->u.msg_qbytes = msginfo.msgmnb;
654137613Srwatson		msqkptr->u.msg_lspid = 0;
655137613Srwatson		msqkptr->u.msg_lrpid = 0;
656137613Srwatson		msqkptr->u.msg_stime = 0;
657137613Srwatson		msqkptr->u.msg_rtime = 0;
658137613Srwatson		msqkptr->u.msg_ctime = time_second;
659140614Srwatson#ifdef MAC
660140614Srwatson		mac_create_sysv_msgqueue(cred, msqkptr);
661140614Srwatson#endif
6622729Sdfr	} else {
663100523Salfred		DPRINTF(("didn't find it and wasn't asked to create it\n"));
66482607Sdillon		error = ENOENT;
66582607Sdillon		goto done2;
6662729Sdfr	}
6672729Sdfr
6682729Sdfrfound:
6692729Sdfr	/* Construct the unique msqid */
670137613Srwatson	td->td_retval[0] = IXSEQ_TO_IPCID(msqid, msqkptr->u.msg_perm);
67182607Sdillondone2:
672101772Salfred	mtx_unlock(&msq_mtx);
67382607Sdillon	return (error);
6742729Sdfr}
6752729Sdfr
67612866Speter#ifndef _SYS_SYSPROTO_H_
6772729Sdfrstruct msgsnd_args {
6782729Sdfr	int	msqid;
679109895Salfred	const void	*msgp;
6802729Sdfr	size_t	msgsz;
6812729Sdfr	int	msgflg;
6822729Sdfr};
68312866Speter#endif
6842729Sdfr
68582607Sdillon/*
68682607Sdillon * MPSAFE
68782607Sdillon */
68812866Speterint
68983366Sjulianmsgsnd(td, uap)
69083366Sjulian	struct thread *td;
6912729Sdfr	register struct msgsnd_args *uap;
6922729Sdfr{
6932729Sdfr	int msqid = uap->msqid;
694109895Salfred	const void *user_msgp = uap->msgp;
6952729Sdfr	size_t msgsz = uap->msgsz;
6962729Sdfr	int msgflg = uap->msgflg;
69782607Sdillon	int segs_needed, error = 0;
698137613Srwatson	register struct msqid_kernel *msqkptr;
6992729Sdfr	register struct msg *msghdr;
7002729Sdfr	short next;
7012729Sdfr
702100523Salfred	DPRINTF(("call to msgsnd(%d, 0x%x, %d, %d)\n", msqid, user_msgp, msgsz,
703100523Salfred	    msgflg));
70491703Sjhb	if (!jail_sysvipc_allowed && jailed(td->td_ucred))
70591703Sjhb		return (ENOSYS);
70691703Sjhb
707101772Salfred	mtx_lock(&msq_mtx);
7082729Sdfr	msqid = IPCID_TO_IX(msqid);
7092729Sdfr
7102729Sdfr	if (msqid < 0 || msqid >= msginfo.msgmni) {
711100523Salfred		DPRINTF(("msqid (%d) out of range (0<=msqid<%d)\n", msqid,
712100523Salfred		    msginfo.msgmni));
71382607Sdillon		error = EINVAL;
71482607Sdillon		goto done2;
7152729Sdfr	}
7162729Sdfr
717137613Srwatson	msqkptr = &msqids[msqid];
718137613Srwatson	if (msqkptr->u.msg_qbytes == 0) {
719100523Salfred		DPRINTF(("no such message queue id\n"));
72082607Sdillon		error = EINVAL;
72182607Sdillon		goto done2;
7222729Sdfr	}
723137613Srwatson	if (msqkptr->u.msg_perm.seq != IPCID_TO_SEQ(uap->msqid)) {
724100523Salfred		DPRINTF(("wrong sequence number\n"));
72582607Sdillon		error = EINVAL;
72682607Sdillon		goto done2;
7272729Sdfr	}
7282729Sdfr
729137613Srwatson	if ((error = ipcperm(td, &msqkptr->u.msg_perm, IPC_W))) {
730100523Salfred		DPRINTF(("requester doesn't have write access\n"));
73182607Sdillon		goto done2;
7322729Sdfr	}
7332729Sdfr
734140614Srwatson#ifdef MAC
735140614Srwatson	error = mac_check_sysv_msqsnd(td->td_ucred, msqkptr);
736140614Srwatson	if (error != 0) {
737140614Srwatson		MPRINTF(("mac_check_sysv_msqsnd returned %d\n", error));
738140614Srwatson		goto done2;
739140614Srwatson	}
740140614Srwatson#endif
741140614Srwatson
7422729Sdfr	segs_needed = (msgsz + msginfo.msgssz - 1) / msginfo.msgssz;
743100523Salfred	DPRINTF(("msgsz=%d, msgssz=%d, segs_needed=%d\n", msgsz, msginfo.msgssz,
744100523Salfred	    segs_needed));
7452729Sdfr	for (;;) {
7462729Sdfr		int need_more_resources = 0;
7472729Sdfr
7482729Sdfr		/*
7492729Sdfr		 * check msgsz
7502729Sdfr		 * (inside this loop in case msg_qbytes changes while we sleep)
7512729Sdfr		 */
7522729Sdfr
753137613Srwatson		if (msgsz > msqkptr->u.msg_qbytes) {
754137613Srwatson			DPRINTF(("msgsz > msqkptr->u.msg_qbytes\n"));
75582607Sdillon			error = EINVAL;
75682607Sdillon			goto done2;
7572729Sdfr		}
7582729Sdfr
759137613Srwatson		if (msqkptr->u.msg_perm.mode & MSG_LOCKED) {
760100523Salfred			DPRINTF(("msqid is locked\n"));
7612729Sdfr			need_more_resources = 1;
7622729Sdfr		}
763137613Srwatson		if (msgsz + msqkptr->u.msg_cbytes > msqkptr->u.msg_qbytes) {
764100523Salfred			DPRINTF(("msgsz + msg_cbytes > msg_qbytes\n"));
7652729Sdfr			need_more_resources = 1;
7662729Sdfr		}
7672729Sdfr		if (segs_needed > nfree_msgmaps) {
768100523Salfred			DPRINTF(("segs_needed > nfree_msgmaps\n"));
7692729Sdfr			need_more_resources = 1;
7702729Sdfr		}
7712729Sdfr		if (free_msghdrs == NULL) {
772100523Salfred			DPRINTF(("no more msghdrs\n"));
7732729Sdfr			need_more_resources = 1;
7742729Sdfr		}
7752729Sdfr
7762729Sdfr		if (need_more_resources) {
7772729Sdfr			int we_own_it;
7782729Sdfr
7792729Sdfr			if ((msgflg & IPC_NOWAIT) != 0) {
780100523Salfred				DPRINTF(("need more resources but caller "
781100523Salfred				    "doesn't want to wait\n"));
78282607Sdillon				error = EAGAIN;
78382607Sdillon				goto done2;
7842729Sdfr			}
7852729Sdfr
786137613Srwatson			if ((msqkptr->u.msg_perm.mode & MSG_LOCKED) != 0) {
787100523Salfred				DPRINTF(("we don't own the msqid_ds\n"));
7882729Sdfr				we_own_it = 0;
7892729Sdfr			} else {
7902729Sdfr				/* Force later arrivals to wait for our
7912729Sdfr				   request */
792100523Salfred				DPRINTF(("we own the msqid_ds\n"));
793137613Srwatson				msqkptr->u.msg_perm.mode |= MSG_LOCKED;
7942729Sdfr				we_own_it = 1;
7952729Sdfr			}
796100523Salfred			DPRINTF(("goodnight\n"));
797137613Srwatson			error = msleep(msqkptr, &msq_mtx, (PZERO - 4) | PCATCH,
7982729Sdfr			    "msgwait", 0);
799100523Salfred			DPRINTF(("good morning, error=%d\n", error));
8002729Sdfr			if (we_own_it)
801137613Srwatson				msqkptr->u.msg_perm.mode &= ~MSG_LOCKED;
80282607Sdillon			if (error != 0) {
803100523Salfred				DPRINTF(("msgsnd:  interrupted system call\n"));
80482607Sdillon				error = EINTR;
80582607Sdillon				goto done2;
8062729Sdfr			}
8072729Sdfr
8082729Sdfr			/*
8092729Sdfr			 * Make sure that the msq queue still exists
8102729Sdfr			 */
8112729Sdfr
812137613Srwatson			if (msqkptr->u.msg_qbytes == 0) {
813100523Salfred				DPRINTF(("msqid deleted\n"));
81482607Sdillon				error = EIDRM;
81582607Sdillon				goto done2;
8162729Sdfr			}
8172729Sdfr
8182729Sdfr		} else {
819100523Salfred			DPRINTF(("got all the resources that we need\n"));
8202729Sdfr			break;
8212729Sdfr		}
8222729Sdfr	}
8232729Sdfr
8242729Sdfr	/*
8252729Sdfr	 * We have the resources that we need.
8262729Sdfr	 * Make sure!
8272729Sdfr	 */
8282729Sdfr
829137613Srwatson	if (msqkptr->u.msg_perm.mode & MSG_LOCKED)
8302729Sdfr		panic("msg_perm.mode & MSG_LOCKED");
8312729Sdfr	if (segs_needed > nfree_msgmaps)
8322729Sdfr		panic("segs_needed > nfree_msgmaps");
833137613Srwatson	if (msgsz + msqkptr->u.msg_cbytes > msqkptr->u.msg_qbytes)
8342729Sdfr		panic("msgsz + msg_cbytes > msg_qbytes");
8352729Sdfr	if (free_msghdrs == NULL)
8362729Sdfr		panic("no more msghdrs");
8372729Sdfr
8382729Sdfr	/*
8392729Sdfr	 * Re-lock the msqid_ds in case we page-fault when copying in the
8402729Sdfr	 * message
8412729Sdfr	 */
8422729Sdfr
843137613Srwatson	if ((msqkptr->u.msg_perm.mode & MSG_LOCKED) != 0)
8442729Sdfr		panic("msqid_ds is already locked");
845137613Srwatson	msqkptr->u.msg_perm.mode |= MSG_LOCKED;
8462729Sdfr
8472729Sdfr	/*
8482729Sdfr	 * Allocate a message header
8492729Sdfr	 */
8502729Sdfr
8512729Sdfr	msghdr = free_msghdrs;
8522729Sdfr	free_msghdrs = msghdr->msg_next;
8532729Sdfr	msghdr->msg_spot = -1;
8542729Sdfr	msghdr->msg_ts = msgsz;
855140614Srwatson#ifdef MAC
856140614Srwatson	/*
857140614Srwatson	 * XXXMAC: Should the mac_check_sysv_msgmsq check follow here
858140614Srwatson	 * immediately?  Or, should it be checked just before the msg is
859140614Srwatson	 * enqueued in the msgq (as it is done now)?
860140614Srwatson	 */
861140614Srwatson	mac_create_sysv_msgmsg(td->td_ucred, msqkptr, msghdr);
862140614Srwatson#endif
8632729Sdfr
8642729Sdfr	/*
8652729Sdfr	 * Allocate space for the message
8662729Sdfr	 */
8672729Sdfr
8682729Sdfr	while (segs_needed > 0) {
8692729Sdfr		if (nfree_msgmaps <= 0)
8702729Sdfr			panic("not enough msgmaps");
8712729Sdfr		if (free_msgmaps == -1)
8722729Sdfr			panic("nil free_msgmaps");
8732729Sdfr		next = free_msgmaps;
8742729Sdfr		if (next <= -1)
8752729Sdfr			panic("next too low #1");
8762729Sdfr		if (next >= msginfo.msgseg)
8772729Sdfr			panic("next out of range #1");
878100523Salfred		DPRINTF(("allocating segment %d to message\n", next));
8792729Sdfr		free_msgmaps = msgmaps[next].next;
8802729Sdfr		nfree_msgmaps--;
8812729Sdfr		msgmaps[next].next = msghdr->msg_spot;
8822729Sdfr		msghdr->msg_spot = next;
8832729Sdfr		segs_needed--;
8842729Sdfr	}
8852729Sdfr
8862729Sdfr	/*
8872729Sdfr	 * Copy in the message type
8882729Sdfr	 */
8892729Sdfr
890101772Salfred	mtx_unlock(&msq_mtx);
89182607Sdillon	if ((error = copyin(user_msgp, &msghdr->msg_type,
8922729Sdfr	    sizeof(msghdr->msg_type))) != 0) {
893101772Salfred		mtx_lock(&msq_mtx);
894100523Salfred		DPRINTF(("error %d copying the message type\n", error));
8952729Sdfr		msg_freehdr(msghdr);
896137613Srwatson		msqkptr->u.msg_perm.mode &= ~MSG_LOCKED;
897137613Srwatson		wakeup(msqkptr);
89882607Sdillon		goto done2;
8992729Sdfr	}
900101772Salfred	mtx_lock(&msq_mtx);
901109906Salfred	user_msgp = (const char *)user_msgp + sizeof(msghdr->msg_type);
9022729Sdfr
9032729Sdfr	/*
9042729Sdfr	 * Validate the message type
9052729Sdfr	 */
9062729Sdfr
9072729Sdfr	if (msghdr->msg_type < 1) {
9082729Sdfr		msg_freehdr(msghdr);
909137613Srwatson		msqkptr->u.msg_perm.mode &= ~MSG_LOCKED;
910137613Srwatson		wakeup(msqkptr);
911100523Salfred		DPRINTF(("mtype (%d) < 1\n", msghdr->msg_type));
91282607Sdillon		error = EINVAL;
91382607Sdillon		goto done2;
9142729Sdfr	}
9152729Sdfr
9162729Sdfr	/*
9172729Sdfr	 * Copy in the message body
9182729Sdfr	 */
9192729Sdfr
9202729Sdfr	next = msghdr->msg_spot;
9212729Sdfr	while (msgsz > 0) {
9222729Sdfr		size_t tlen;
9232729Sdfr		if (msgsz > msginfo.msgssz)
9242729Sdfr			tlen = msginfo.msgssz;
9252729Sdfr		else
9262729Sdfr			tlen = msgsz;
9272729Sdfr		if (next <= -1)
9282729Sdfr			panic("next too low #2");
9292729Sdfr		if (next >= msginfo.msgseg)
9302729Sdfr			panic("next out of range #2");
931101772Salfred		mtx_unlock(&msq_mtx);
93282607Sdillon		if ((error = copyin(user_msgp, &msgpool[next * msginfo.msgssz],
9332729Sdfr		    tlen)) != 0) {
934101772Salfred			mtx_lock(&msq_mtx);
935100523Salfred			DPRINTF(("error %d copying in message segment\n",
936100523Salfred			    error));
9372729Sdfr			msg_freehdr(msghdr);
938137613Srwatson			msqkptr->u.msg_perm.mode &= ~MSG_LOCKED;
939137613Srwatson			wakeup(msqkptr);
94082607Sdillon			goto done2;
9412729Sdfr		}
942101772Salfred		mtx_lock(&msq_mtx);
9432729Sdfr		msgsz -= tlen;
944109906Salfred		user_msgp = (const char *)user_msgp + tlen;
9452729Sdfr		next = msgmaps[next].next;
9462729Sdfr	}
9472729Sdfr	if (next != -1)
9482729Sdfr		panic("didn't use all the msg segments");
9492729Sdfr
9502729Sdfr	/*
9512729Sdfr	 * We've got the message.  Unlock the msqid_ds.
9522729Sdfr	 */
9532729Sdfr
954137613Srwatson	msqkptr->u.msg_perm.mode &= ~MSG_LOCKED;
9552729Sdfr
9562729Sdfr	/*
9572729Sdfr	 * Make sure that the msqid_ds is still allocated.
9582729Sdfr	 */
9592729Sdfr
960137613Srwatson	if (msqkptr->u.msg_qbytes == 0) {
9612729Sdfr		msg_freehdr(msghdr);
962137613Srwatson		wakeup(msqkptr);
96382607Sdillon		error = EIDRM;
96482607Sdillon		goto done2;
9652729Sdfr	}
9662729Sdfr
967140614Srwatson#ifdef MAC
9682729Sdfr	/*
969140614Srwatson	 * Note: Since the task/thread allocates the msghdr and usually
970140614Srwatson	 * primes it with its own MAC label, for a majority of policies, it
971140614Srwatson	 * won't be necessary to check whether the msghdr has access
972140614Srwatson	 * permissions to the msgq.  The mac_check_sysv_msqsnd check would
973140614Srwatson	 * suffice in that case.  However, this hook may be required where
974140614Srwatson	 * individual policies derive a non-identical label for the msghdr
975140614Srwatson	 * from the current thread label and may want to check the msghdr
976140614Srwatson	 * enqueue permissions, along with read/write permissions to the
977140614Srwatson	 * msgq.
978140614Srwatson	 */
979140614Srwatson	error = mac_check_sysv_msgmsq(td->td_ucred, msghdr, msqkptr);
980140614Srwatson	if (error != 0) {
981140614Srwatson		MPRINTF(("mac_check_sysv_msqmsq returned %d\n", error));
982140614Srwatson		msg_freehdr(msghdr);
983140614Srwatson		wakeup(msqkptr);
984140614Srwatson		goto done2;
985140614Srwatson	}
986140614Srwatson#endif
987140614Srwatson
988140614Srwatson	/*
9892729Sdfr	 * Put the message into the queue
9902729Sdfr	 */
991137613Srwatson	if (msqkptr->u.msg_first == NULL) {
992137613Srwatson		msqkptr->u.msg_first = msghdr;
993137613Srwatson		msqkptr->u.msg_last = msghdr;
9942729Sdfr	} else {
995137613Srwatson		msqkptr->u.msg_last->msg_next = msghdr;
996137613Srwatson		msqkptr->u.msg_last = msghdr;
9972729Sdfr	}
998137613Srwatson	msqkptr->u.msg_last->msg_next = NULL;
9992729Sdfr
1000137613Srwatson	msqkptr->u.msg_cbytes += msghdr->msg_ts;
1001137613Srwatson	msqkptr->u.msg_qnum++;
1002137613Srwatson	msqkptr->u.msg_lspid = td->td_proc->p_pid;
1003137613Srwatson	msqkptr->u.msg_stime = time_second;
10042729Sdfr
1005137613Srwatson	wakeup(msqkptr);
100683366Sjulian	td->td_retval[0] = 0;
100782607Sdillondone2:
1008101772Salfred	mtx_unlock(&msq_mtx);
100982607Sdillon	return (error);
10102729Sdfr}
10112729Sdfr
101212866Speter#ifndef _SYS_SYSPROTO_H_
10132729Sdfrstruct msgrcv_args {
10142729Sdfr	int	msqid;
10152729Sdfr	void	*msgp;
10162729Sdfr	size_t	msgsz;
10172729Sdfr	long	msgtyp;
10182729Sdfr	int	msgflg;
10192729Sdfr};
102012866Speter#endif
10212729Sdfr
102282607Sdillon/*
102382607Sdillon * MPSAFE
102482607Sdillon */
102512866Speterint
102683366Sjulianmsgrcv(td, uap)
102783366Sjulian	struct thread *td;
10282729Sdfr	register struct msgrcv_args *uap;
10292729Sdfr{
10302729Sdfr	int msqid = uap->msqid;
10312729Sdfr	void *user_msgp = uap->msgp;
10322729Sdfr	size_t msgsz = uap->msgsz;
10332729Sdfr	long msgtyp = uap->msgtyp;
10342729Sdfr	int msgflg = uap->msgflg;
10352729Sdfr	size_t len;
1036137613Srwatson	register struct msqid_kernel *msqkptr;
10372729Sdfr	register struct msg *msghdr;
103882607Sdillon	int error = 0;
10392729Sdfr	short next;
10402729Sdfr
1041100523Salfred	DPRINTF(("call to msgrcv(%d, 0x%x, %d, %ld, %d)\n", msqid, user_msgp,
1042100523Salfred	    msgsz, msgtyp, msgflg));
10432729Sdfr
104491703Sjhb	if (!jail_sysvipc_allowed && jailed(td->td_ucred))
104591703Sjhb		return (ENOSYS);
104691703Sjhb
10472729Sdfr	msqid = IPCID_TO_IX(msqid);
10482729Sdfr
10492729Sdfr	if (msqid < 0 || msqid >= msginfo.msgmni) {
1050100523Salfred		DPRINTF(("msqid (%d) out of range (0<=msqid<%d)\n", msqid,
1051100523Salfred		    msginfo.msgmni));
1052101772Salfred		return (EINVAL);
10532729Sdfr	}
10542729Sdfr
1055137613Srwatson	msqkptr = &msqids[msqid];
1056101772Salfred	mtx_lock(&msq_mtx);
1057137613Srwatson	if (msqkptr->u.msg_qbytes == 0) {
1058100523Salfred		DPRINTF(("no such message queue id\n"));
105982607Sdillon		error = EINVAL;
106082607Sdillon		goto done2;
10612729Sdfr	}
1062137613Srwatson	if (msqkptr->u.msg_perm.seq != IPCID_TO_SEQ(uap->msqid)) {
1063100523Salfred		DPRINTF(("wrong sequence number\n"));
106482607Sdillon		error = EINVAL;
106582607Sdillon		goto done2;
10662729Sdfr	}
10672729Sdfr
1068137613Srwatson	if ((error = ipcperm(td, &msqkptr->u.msg_perm, IPC_R))) {
1069100523Salfred		DPRINTF(("requester doesn't have read access\n"));
107082607Sdillon		goto done2;
10712729Sdfr	}
10722729Sdfr
1073140614Srwatson#ifdef MAC
1074140614Srwatson	error = mac_check_sysv_msqrcv(td->td_ucred, msqkptr);
1075140614Srwatson	if (error != 0) {
1076140614Srwatson		MPRINTF(("mac_check_sysv_msqrcv returned %d\n", error));
1077140614Srwatson		goto done2;
1078140614Srwatson	}
1079140614Srwatson#endif
1080140614Srwatson
10812729Sdfr	msghdr = NULL;
10822729Sdfr	while (msghdr == NULL) {
10832729Sdfr		if (msgtyp == 0) {
1084137613Srwatson			msghdr = msqkptr->u.msg_first;
10852729Sdfr			if (msghdr != NULL) {
10862729Sdfr				if (msgsz < msghdr->msg_ts &&
10872729Sdfr				    (msgflg & MSG_NOERROR) == 0) {
1088100523Salfred					DPRINTF(("first message on the queue "
1089100523Salfred					    "is too big (want %d, got %d)\n",
1090100523Salfred					    msgsz, msghdr->msg_ts));
109182607Sdillon					error = E2BIG;
109282607Sdillon					goto done2;
10932729Sdfr				}
1094140614Srwatson#ifdef MAC
1095140614Srwatson				error = mac_check_sysv_msgrcv(td->td_ucred,
1096140614Srwatson				    msghdr);
1097140614Srwatson				if (error != 0) {
1098140614Srwatson					MPRINTF(("mac_check_sysv_msgrcv "
1099140614Srwatson					    "returned %d\n", error));
1100140614Srwatson					goto done2;
1101140614Srwatson				}
1102140614Srwatson#endif
1103137613Srwatson				if (msqkptr->u.msg_first == msqkptr->u.msg_last) {
1104137613Srwatson					msqkptr->u.msg_first = NULL;
1105137613Srwatson					msqkptr->u.msg_last = NULL;
11062729Sdfr				} else {
1107137613Srwatson					msqkptr->u.msg_first = msghdr->msg_next;
1108137613Srwatson					if (msqkptr->u.msg_first == NULL)
11092729Sdfr						panic("msg_first/last screwed up #1");
11102729Sdfr				}
11112729Sdfr			}
11122729Sdfr		} else {
11132729Sdfr			struct msg *previous;
11142729Sdfr			struct msg **prev;
11152729Sdfr
11162729Sdfr			previous = NULL;
1117137613Srwatson			prev = &(msqkptr->u.msg_first);
11182729Sdfr			while ((msghdr = *prev) != NULL) {
11192729Sdfr				/*
11202729Sdfr				 * Is this message's type an exact match or is
11212729Sdfr				 * this message's type less than or equal to
11222729Sdfr				 * the absolute value of a negative msgtyp?
11232729Sdfr				 * Note that the second half of this test can
11242729Sdfr				 * NEVER be true if msgtyp is positive since
11252729Sdfr				 * msg_type is always positive!
11262729Sdfr				 */
11272729Sdfr
11282729Sdfr				if (msgtyp == msghdr->msg_type ||
11292729Sdfr				    msghdr->msg_type <= -msgtyp) {
1130100523Salfred					DPRINTF(("found message type %d, "
1131100523Salfred					    "requested %d\n",
1132100523Salfred					    msghdr->msg_type, msgtyp));
11332729Sdfr					if (msgsz < msghdr->msg_ts &&
11342729Sdfr					    (msgflg & MSG_NOERROR) == 0) {
1135100523Salfred						DPRINTF(("requested message "
1136100523Salfred						    "on the queue is too big "
1137100523Salfred						    "(want %d, got %d)\n",
1138100523Salfred						    msgsz, msghdr->msg_ts));
113982607Sdillon						error = E2BIG;
114082607Sdillon						goto done2;
11412729Sdfr					}
1142140614Srwatson#ifdef MAC
1143140614Srwatson					error = mac_check_sysv_msgrcv(
1144140614Srwatson					    td->td_ucred, msghdr);
1145140614Srwatson					if (error != 0) {
1146140614Srwatson						MPRINTF(("mac_check_sysv_"
1147140614Srwatson						    "msgrcv returned %d\n",
1148140614Srwatson						    error));
1149140614Srwatson						goto done2;
1150140614Srwatson					}
1151140614Srwatson#endif
11522729Sdfr					*prev = msghdr->msg_next;
1153137613Srwatson					if (msghdr == msqkptr->u.msg_last) {
11542729Sdfr						if (previous == NULL) {
11552729Sdfr							if (prev !=
1156137613Srwatson							    &msqkptr->u.msg_first)
11572729Sdfr								panic("msg_first/last screwed up #2");
1158137613Srwatson							msqkptr->u.msg_first =
11592729Sdfr							    NULL;
1160137613Srwatson							msqkptr->u.msg_last =
11612729Sdfr							    NULL;
11622729Sdfr						} else {
11632729Sdfr							if (prev ==
1164137613Srwatson							    &msqkptr->u.msg_first)
11652729Sdfr								panic("msg_first/last screwed up #3");
1166137613Srwatson							msqkptr->u.msg_last =
11672729Sdfr							    previous;
11682729Sdfr						}
11692729Sdfr					}
11702729Sdfr					break;
11712729Sdfr				}
11722729Sdfr				previous = msghdr;
11732729Sdfr				prev = &(msghdr->msg_next);
11742729Sdfr			}
11752729Sdfr		}
11762729Sdfr
11772729Sdfr		/*
11782729Sdfr		 * We've either extracted the msghdr for the appropriate
11792729Sdfr		 * message or there isn't one.
11802729Sdfr		 * If there is one then bail out of this loop.
11812729Sdfr		 */
11822729Sdfr
11832729Sdfr		if (msghdr != NULL)
11842729Sdfr			break;
11852729Sdfr
11862729Sdfr		/*
11872729Sdfr		 * Hmph!  No message found.  Does the user want to wait?
11882729Sdfr		 */
11892729Sdfr
11902729Sdfr		if ((msgflg & IPC_NOWAIT) != 0) {
1191100523Salfred			DPRINTF(("no appropriate message found (msgtyp=%d)\n",
1192100523Salfred			    msgtyp));
11932729Sdfr			/* The SVID says to return ENOMSG. */
119482607Sdillon			error = ENOMSG;
119582607Sdillon			goto done2;
11962729Sdfr		}
11972729Sdfr
11982729Sdfr		/*
11992729Sdfr		 * Wait for something to happen
12002729Sdfr		 */
12012729Sdfr
1202100523Salfred		DPRINTF(("msgrcv:  goodnight\n"));
1203137613Srwatson		error = msleep(msqkptr, &msq_mtx, (PZERO - 4) | PCATCH,
1204101772Salfred		    "msgwait", 0);
1205100523Salfred		DPRINTF(("msgrcv:  good morning (error=%d)\n", error));
12062729Sdfr
120782607Sdillon		if (error != 0) {
1208100523Salfred			DPRINTF(("msgsnd:  interrupted system call\n"));
120982607Sdillon			error = EINTR;
121082607Sdillon			goto done2;
12112729Sdfr		}
12122729Sdfr
12132729Sdfr		/*
12142729Sdfr		 * Make sure that the msq queue still exists
12152729Sdfr		 */
12162729Sdfr
1217137613Srwatson		if (msqkptr->u.msg_qbytes == 0 ||
1218137613Srwatson		    msqkptr->u.msg_perm.seq != IPCID_TO_SEQ(uap->msqid)) {
1219100523Salfred			DPRINTF(("msqid deleted\n"));
122082607Sdillon			error = EIDRM;
122182607Sdillon			goto done2;
12222729Sdfr		}
12232729Sdfr	}
12242729Sdfr
12252729Sdfr	/*
12262729Sdfr	 * Return the message to the user.
12272729Sdfr	 *
12282729Sdfr	 * First, do the bookkeeping (before we risk being interrupted).
12292729Sdfr	 */
12302729Sdfr
1231137613Srwatson	msqkptr->u.msg_cbytes -= msghdr->msg_ts;
1232137613Srwatson	msqkptr->u.msg_qnum--;
1233137613Srwatson	msqkptr->u.msg_lrpid = td->td_proc->p_pid;
1234137613Srwatson	msqkptr->u.msg_rtime = time_second;
12352729Sdfr
12362729Sdfr	/*
12372729Sdfr	 * Make msgsz the actual amount that we'll be returning.
12382729Sdfr	 * Note that this effectively truncates the message if it is too long
12392729Sdfr	 * (since msgsz is never increased).
12402729Sdfr	 */
12412729Sdfr
1242100523Salfred	DPRINTF(("found a message, msgsz=%d, msg_ts=%d\n", msgsz,
1243100523Salfred	    msghdr->msg_ts));
12442729Sdfr	if (msgsz > msghdr->msg_ts)
12452729Sdfr		msgsz = msghdr->msg_ts;
12462729Sdfr
12472729Sdfr	/*
12482729Sdfr	 * Return the type to the user.
12492729Sdfr	 */
12502729Sdfr
1251101772Salfred	mtx_unlock(&msq_mtx);
1252100511Salfred	error = copyout(&(msghdr->msg_type), user_msgp,
12532729Sdfr	    sizeof(msghdr->msg_type));
1254101772Salfred	mtx_lock(&msq_mtx);
125582607Sdillon	if (error != 0) {
1256100523Salfred		DPRINTF(("error (%d) copying out message type\n", error));
12572729Sdfr		msg_freehdr(msghdr);
1258137613Srwatson		wakeup(msqkptr);
125982607Sdillon		goto done2;
12602729Sdfr	}
126117971Sbde	user_msgp = (char *)user_msgp + sizeof(msghdr->msg_type);
12622729Sdfr
12632729Sdfr	/*
12642729Sdfr	 * Return the segments to the user
12652729Sdfr	 */
12662729Sdfr
12672729Sdfr	next = msghdr->msg_spot;
12682729Sdfr	for (len = 0; len < msgsz; len += msginfo.msgssz) {
12692729Sdfr		size_t tlen;
12702729Sdfr
127145921Ssada		if (msgsz - len > msginfo.msgssz)
12722729Sdfr			tlen = msginfo.msgssz;
12732729Sdfr		else
127445921Ssada			tlen = msgsz - len;
12752729Sdfr		if (next <= -1)
12762729Sdfr			panic("next too low #3");
12772729Sdfr		if (next >= msginfo.msgseg)
12782729Sdfr			panic("next out of range #3");
1279101772Salfred		mtx_unlock(&msq_mtx);
1280100511Salfred		error = copyout(&msgpool[next * msginfo.msgssz],
12812729Sdfr		    user_msgp, tlen);
1282101772Salfred		mtx_lock(&msq_mtx);
128382607Sdillon		if (error != 0) {
1284100523Salfred			DPRINTF(("error (%d) copying out message segment\n",
1285100523Salfred			    error));
12862729Sdfr			msg_freehdr(msghdr);
1287137613Srwatson			wakeup(msqkptr);
128882607Sdillon			goto done2;
12892729Sdfr		}
129017971Sbde		user_msgp = (char *)user_msgp + tlen;
12912729Sdfr		next = msgmaps[next].next;
12922729Sdfr	}
12932729Sdfr
12942729Sdfr	/*
12952729Sdfr	 * Done, return the actual number of bytes copied out.
12962729Sdfr	 */
12972729Sdfr
12982729Sdfr	msg_freehdr(msghdr);
1299137613Srwatson	wakeup(msqkptr);
130083366Sjulian	td->td_retval[0] = msgsz;
130182607Sdillondone2:
1302101772Salfred	mtx_unlock(&msq_mtx);
130382607Sdillon	return (error);
13042729Sdfr}
130577461Sdd
130677461Sddstatic int
130777461Sddsysctl_msqids(SYSCTL_HANDLER_ARGS)
130877461Sdd{
130977461Sdd
131077461Sdd	return (SYSCTL_OUT(req, msqids,
1311137613Srwatson	    sizeof(struct msqid_kernel) * msginfo.msgmni));
131277461Sdd}
131377461Sdd
131477461SddSYSCTL_DECL(_kern_ipc);
131577461SddSYSCTL_INT(_kern_ipc, OID_AUTO, msgmax, CTLFLAG_RD, &msginfo.msgmax, 0, "");
1316121307SsilbySYSCTL_INT(_kern_ipc, OID_AUTO, msgmni, CTLFLAG_RDTUN, &msginfo.msgmni, 0, "");
1317139436SrwatsonSYSCTL_INT(_kern_ipc, OID_AUTO, msgmnb, CTLFLAG_RDTUN, &msginfo.msgmnb, 0, "");
1318139436SrwatsonSYSCTL_INT(_kern_ipc, OID_AUTO, msgtql, CTLFLAG_RDTUN, &msginfo.msgtql, 0, "");
1319121307SsilbySYSCTL_INT(_kern_ipc, OID_AUTO, msgssz, CTLFLAG_RDTUN, &msginfo.msgssz, 0, "");
1320121307SsilbySYSCTL_INT(_kern_ipc, OID_AUTO, msgseg, CTLFLAG_RDTUN, &msginfo.msgseg, 0, "");
132177461SddSYSCTL_PROC(_kern_ipc, OID_AUTO, msqids, CTLFLAG_RD,
132277461Sdd    NULL, 0, sysctl_msqids, "", "Message queue IDs");
1323