sysv_msg.c revision 165403
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 165403 2006-12-20 19:26:30Z jkim $");
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>
60164033Srwatson#include <sys/priv.h>
612729Sdfr#include <sys/proc.h>
6282607Sdillon#include <sys/lock.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
73163606Srwatson#include <security/mac/mac_framework.h>
74163606Srwatson
7559839Speterstatic MALLOC_DEFINE(M_MSG, "msg", "SVID compatible message queues");
7659839Speter
7792723Salfredstatic void msginit(void);
7892723Salfredstatic int msgunload(void);
7992723Salfredstatic int sysvmsg_modload(struct module *, int, void *);
8010358Sjulian
81100523Salfred#ifdef MSG_DEBUG
82100523Salfred#define DPRINTF(a)	printf a
83100523Salfred#else
84100523Salfred#define DPRINTF(a)
85100523Salfred#endif
862729Sdfr
8792723Salfredstatic void msg_freehdr(struct msg *msghdr);
882729Sdfr
8911626Sbde/* XXX casting to (sy_call_t *) is bogus, as usual. */
9012819Sphkstatic sy_call_t *msgcalls[] = {
9111626Sbde	(sy_call_t *)msgctl, (sy_call_t *)msgget,
9211626Sbde	(sy_call_t *)msgsnd, (sy_call_t *)msgrcv
9311626Sbde};
942729Sdfr
9559839Speter#ifndef MSGSSZ
9659839Speter#define MSGSSZ	8		/* Each segment must be 2^N long */
9759839Speter#endif
9859839Speter#ifndef MSGSEG
9959839Speter#define MSGSEG	2048		/* must be less than 32767 */
10059839Speter#endif
10159839Speter#define MSGMAX	(MSGSSZ*MSGSEG)
10259839Speter#ifndef MSGMNB
10359839Speter#define MSGMNB	2048		/* max # of bytes in a queue */
10459839Speter#endif
10559839Speter#ifndef MSGMNI
10659839Speter#define MSGMNI	40
10759839Speter#endif
10859839Speter#ifndef MSGTQL
10959839Speter#define MSGTQL	40
11059839Speter#endif
11159839Speter
11259839Speter/*
11359839Speter * Based on the configuration parameters described in an SVR2 (yes, two)
11459839Speter * config(1m) man page.
11559839Speter *
11659839Speter * Each message is broken up and stored in segments that are msgssz bytes
11759839Speter * long.  For efficiency reasons, this should be a power of two.  Also,
11859839Speter * it doesn't make sense if it is less than 8 or greater than about 256.
11959839Speter * Consequently, msginit in kern/sysv_msg.c checks that msgssz is a power of
12059839Speter * two between 8 and 1024 inclusive (and panic's if it isn't).
12159839Speter */
12259839Speterstruct msginfo msginfo = {
12359839Speter                MSGMAX,         /* max chars in a message */
12459839Speter                MSGMNI,         /* # of message queue identifiers */
12559839Speter                MSGMNB,         /* max chars in a queue */
12659839Speter                MSGTQL,         /* max messages in system */
12759839Speter                MSGSSZ,         /* size of a message segment */
12859839Speter                		/* (must be small power of 2 greater than 4) */
12959839Speter                MSGSEG          /* number of message segments */
13059839Speter};
13159839Speter
13259839Speter/*
13359839Speter * macros to convert between msqid_ds's and msqid's.
13459839Speter * (specific to this implementation)
13559839Speter */
13659839Speter#define MSQID(ix,ds)	((ix) & 0xffff | (((ds).msg_perm.seq << 16) & 0xffff0000))
13759839Speter#define MSQID_IX(id)	((id) & 0xffff)
13859839Speter#define MSQID_SEQ(id)	(((id) >> 16) & 0xffff)
13959839Speter
14059839Speter/*
14159839Speter * The rest of this file is specific to this particular implementation.
14259839Speter */
14359839Speter
14459839Speterstruct msgmap {
14559839Speter	short	next;		/* next segment in buffer */
14659839Speter    				/* -1 -> available */
14759839Speter    				/* 0..(MSGSEG-1) -> index of next segment */
14859839Speter};
14959839Speter
15059839Speter#define MSG_LOCKED	01000	/* Is this msqid_ds locked? */
15159839Speter
15212819Sphkstatic int nfree_msgmaps;	/* # of free map entries */
15312819Sphkstatic short free_msgmaps;	/* head of linked list of free map entries */
15459839Speterstatic struct msg *free_msghdrs;/* list of free msg headers */
15559839Speterstatic char *msgpool;		/* MSGMAX byte long msg buffer pool */
15659839Speterstatic struct msgmap *msgmaps;	/* MSGSEG msgmap structures */
15759839Speterstatic struct msg *msghdrs;	/* MSGTQL msg headers */
158137613Srwatsonstatic struct msqid_kernel *msqids;	/* MSGMNI msqid_kernel struct's */
159101772Salfredstatic struct mtx msq_mtx;	/* global mutex for message queues. */
1602729Sdfr
16159839Speterstatic void
16269449Salfredmsginit()
1632729Sdfr{
1642729Sdfr	register int i;
1652729Sdfr
16683765Smr	TUNABLE_INT_FETCH("kern.ipc.msgseg", &msginfo.msgseg);
16783765Smr	TUNABLE_INT_FETCH("kern.ipc.msgssz", &msginfo.msgssz);
16883765Smr	msginfo.msgmax = msginfo.msgseg * msginfo.msgssz;
16983765Smr	TUNABLE_INT_FETCH("kern.ipc.msgmni", &msginfo.msgmni);
170139436Srwatson	TUNABLE_INT_FETCH("kern.ipc.msgmnb", &msginfo.msgmnb);
171139436Srwatson	TUNABLE_INT_FETCH("kern.ipc.msgtql", &msginfo.msgtql);
17283765Smr
173111119Simp	msgpool = malloc(msginfo.msgmax, M_MSG, M_WAITOK);
17459839Speter	if (msgpool == NULL)
17559839Speter		panic("msgpool is NULL");
176111119Simp	msgmaps = malloc(sizeof(struct msgmap) * msginfo.msgseg, M_MSG, M_WAITOK);
17759839Speter	if (msgmaps == NULL)
17859839Speter		panic("msgmaps is NULL");
179111119Simp	msghdrs = malloc(sizeof(struct msg) * msginfo.msgtql, M_MSG, M_WAITOK);
18059839Speter	if (msghdrs == NULL)
18159839Speter		panic("msghdrs is NULL");
182137613Srwatson	msqids = malloc(sizeof(struct msqid_kernel) * msginfo.msgmni, M_MSG,
183137613Srwatson	    M_WAITOK);
18459839Speter	if (msqids == NULL)
18559839Speter		panic("msqids is NULL");
18659839Speter
1872729Sdfr	/*
1882729Sdfr	 * msginfo.msgssz should be a power of two for efficiency reasons.
1892729Sdfr	 * It is also pretty silly if msginfo.msgssz is less than 8
1902729Sdfr	 * or greater than about 256 so ...
1912729Sdfr	 */
1922729Sdfr
1932729Sdfr	i = 8;
1942729Sdfr	while (i < 1024 && i != msginfo.msgssz)
1952729Sdfr		i <<= 1;
1962729Sdfr    	if (i != msginfo.msgssz) {
197100523Salfred		DPRINTF(("msginfo.msgssz=%d (0x%x)\n", msginfo.msgssz,
198100523Salfred		    msginfo.msgssz));
1992729Sdfr		panic("msginfo.msgssz not a small power of 2");
2002729Sdfr	}
2012729Sdfr
2022729Sdfr	if (msginfo.msgseg > 32767) {
203100523Salfred		DPRINTF(("msginfo.msgseg=%d\n", msginfo.msgseg));
2042729Sdfr		panic("msginfo.msgseg > 32767");
2052729Sdfr	}
2062729Sdfr
2072729Sdfr	if (msgmaps == NULL)
2082729Sdfr		panic("msgmaps is NULL");
2092729Sdfr
2102729Sdfr	for (i = 0; i < msginfo.msgseg; i++) {
2112729Sdfr		if (i > 0)
2122729Sdfr			msgmaps[i-1].next = i;
2132729Sdfr		msgmaps[i].next = -1;	/* implies entry is available */
2142729Sdfr	}
2152729Sdfr	free_msgmaps = 0;
2162729Sdfr	nfree_msgmaps = msginfo.msgseg;
2172729Sdfr
2182729Sdfr	if (msghdrs == NULL)
2192729Sdfr		panic("msghdrs is NULL");
2202729Sdfr
2212729Sdfr	for (i = 0; i < msginfo.msgtql; i++) {
2222729Sdfr		msghdrs[i].msg_type = 0;
2232729Sdfr		if (i > 0)
2242729Sdfr			msghdrs[i-1].msg_next = &msghdrs[i];
2252729Sdfr		msghdrs[i].msg_next = NULL;
226140614Srwatson#ifdef MAC
227140614Srwatson		mac_init_sysv_msgmsg(&msghdrs[i]);
228140614Srwatson#endif
2292729Sdfr    	}
2302729Sdfr	free_msghdrs = &msghdrs[0];
2312729Sdfr
2322729Sdfr	if (msqids == NULL)
2332729Sdfr		panic("msqids is NULL");
2342729Sdfr
2352729Sdfr	for (i = 0; i < msginfo.msgmni; i++) {
236137613Srwatson		msqids[i].u.msg_qbytes = 0;	/* implies entry is available */
237137613Srwatson		msqids[i].u.msg_perm.seq = 0;	/* reset to a known value */
238137613Srwatson		msqids[i].u.msg_perm.mode = 0;
239140614Srwatson#ifdef MAC
240140614Srwatson		mac_init_sysv_msgqueue(&msqids[i]);
241140614Srwatson#endif
2422729Sdfr	}
243101772Salfred	mtx_init(&msq_mtx, "msq", NULL, MTX_DEF);
2442729Sdfr}
2452729Sdfr
24669449Salfredstatic int
24769449Salfredmsgunload()
24869449Salfred{
249137613Srwatson	struct msqid_kernel *msqkptr;
25069449Salfred	int msqid;
251140614Srwatson#ifdef MAC
252140614Srwatson	int i;
253140614Srwatson#endif
25469449Salfred
25569449Salfred	for (msqid = 0; msqid < msginfo.msgmni; msqid++) {
25669449Salfred		/*
25769449Salfred		 * Look for an unallocated and unlocked msqid_ds.
25869449Salfred		 * msqid_ds's can be locked by msgsnd or msgrcv while
25969449Salfred		 * they are copying the message in/out.  We can't
26069449Salfred		 * re-use the entry until they release it.
26169449Salfred		 */
262137613Srwatson		msqkptr = &msqids[msqid];
263137613Srwatson		if (msqkptr->u.msg_qbytes != 0 ||
264137613Srwatson		    (msqkptr->u.msg_perm.mode & MSG_LOCKED) != 0)
26569449Salfred			break;
26669449Salfred	}
26769449Salfred	if (msqid != msginfo.msgmni)
26869449Salfred		return (EBUSY);
26969449Salfred
270140614Srwatson#ifdef MAC
271140614Srwatson	for (i = 0; i < msginfo.msgtql; i++)
272140614Srwatson		mac_destroy_sysv_msgmsg(&msghdrs[i]);
273140614Srwatson	for (msqid = 0; msqid < msginfo.msgmni; msqid++)
274140614Srwatson		mac_destroy_sysv_msgqueue(&msqids[msqid]);
275140614Srwatson#endif
27669449Salfred	free(msgpool, M_MSG);
27769449Salfred	free(msgmaps, M_MSG);
27869449Salfred	free(msghdrs, M_MSG);
27969449Salfred	free(msqids, M_MSG);
280101772Salfred	mtx_destroy(&msq_mtx);
28169449Salfred	return (0);
28269449Salfred}
28369449Salfred
28469449Salfred
28569449Salfredstatic int
28669449Salfredsysvmsg_modload(struct module *module, int cmd, void *arg)
28769449Salfred{
28869449Salfred	int error = 0;
28969449Salfred
29069449Salfred	switch (cmd) {
29169449Salfred	case MOD_LOAD:
29269449Salfred		msginit();
29369449Salfred		break;
29469449Salfred	case MOD_UNLOAD:
29569449Salfred		error = msgunload();
29669449Salfred		break;
29769449Salfred	case MOD_SHUTDOWN:
29869449Salfred		break;
29969449Salfred	default:
30069449Salfred		error = EINVAL;
30169449Salfred		break;
30269449Salfred	}
30369449Salfred	return (error);
30469449Salfred}
30569449Salfred
30671038Sdesstatic moduledata_t sysvmsg_mod = {
30771038Sdes	"sysvmsg",
30869449Salfred	&sysvmsg_modload,
30969449Salfred	NULL
31069449Salfred};
31169449Salfred
31288633SalfredSYSCALL_MODULE_HELPER(msgsys);
31388633SalfredSYSCALL_MODULE_HELPER(msgctl);
31488633SalfredSYSCALL_MODULE_HELPER(msgget);
31588633SalfredSYSCALL_MODULE_HELPER(msgsnd);
31688633SalfredSYSCALL_MODULE_HELPER(msgrcv);
31769449Salfred
31871038SdesDECLARE_MODULE(sysvmsg, sysvmsg_mod,
31969449Salfred	SI_SUB_SYSV_MSG, SI_ORDER_FIRST);
32071038SdesMODULE_VERSION(sysvmsg, 1);
32169449Salfred
3222729Sdfr/*
3232729Sdfr * Entry point for all MSG calls
32482607Sdillon *
32582607Sdillon * MPSAFE
3262729Sdfr */
3272729Sdfrint
32883366Sjulianmsgsys(td, uap)
32983366Sjulian	struct thread *td;
33011626Sbde	/* XXX actually varargs. */
33111626Sbde	struct msgsys_args /* {
332118615Snectar		int	which;
33311626Sbde		int	a2;
33411626Sbde		int	a3;
33511626Sbde		int	a4;
33611626Sbde		int	a5;
33711626Sbde		int	a6;
33811626Sbde	} */ *uap;
3392729Sdfr{
34082607Sdillon	int error;
3412729Sdfr
34291703Sjhb	if (!jail_sysvipc_allowed && jailed(td->td_ucred))
34391703Sjhb		return (ENOSYS);
344118615Snectar	if (uap->which < 0 ||
345118615Snectar	    uap->which >= sizeof(msgcalls)/sizeof(msgcalls[0]))
34691703Sjhb		return (EINVAL);
34783366Sjulian	error = (*msgcalls[uap->which])(td, &uap->a2);
34882607Sdillon	return (error);
3492729Sdfr}
3502729Sdfr
3512729Sdfrstatic void
3522729Sdfrmsg_freehdr(msghdr)
3532729Sdfr	struct msg *msghdr;
3542729Sdfr{
3552729Sdfr	while (msghdr->msg_ts > 0) {
3562729Sdfr		short next;
3572729Sdfr		if (msghdr->msg_spot < 0 || msghdr->msg_spot >= msginfo.msgseg)
3582729Sdfr			panic("msghdr->msg_spot out of range");
3592729Sdfr		next = msgmaps[msghdr->msg_spot].next;
3602729Sdfr		msgmaps[msghdr->msg_spot].next = free_msgmaps;
3612729Sdfr		free_msgmaps = msghdr->msg_spot;
3622729Sdfr		nfree_msgmaps++;
3632729Sdfr		msghdr->msg_spot = next;
3642729Sdfr		if (msghdr->msg_ts >= msginfo.msgssz)
3652729Sdfr			msghdr->msg_ts -= msginfo.msgssz;
3662729Sdfr		else
3672729Sdfr			msghdr->msg_ts = 0;
3682729Sdfr	}
3692729Sdfr	if (msghdr->msg_spot != -1)
3702729Sdfr		panic("msghdr->msg_spot != -1");
3712729Sdfr	msghdr->msg_next = free_msghdrs;
3722729Sdfr	free_msghdrs = msghdr;
373140614Srwatson#ifdef MAC
374140614Srwatson	mac_cleanup_sysv_msgmsg(msghdr);
375140614Srwatson#endif
3762729Sdfr}
3772729Sdfr
37812866Speter#ifndef _SYS_SYSPROTO_H_
3792729Sdfrstruct msgctl_args {
3802729Sdfr	int	msqid;
3812729Sdfr	int	cmd;
38212866Speter	struct	msqid_ds *buf;
3832729Sdfr};
38412866Speter#endif
3852729Sdfr
38682607Sdillon/*
38782607Sdillon * MPSAFE
38882607Sdillon */
38912866Speterint
39083366Sjulianmsgctl(td, uap)
39183366Sjulian	struct thread *td;
3922729Sdfr	register struct msgctl_args *uap;
3932729Sdfr{
3942729Sdfr	int msqid = uap->msqid;
3952729Sdfr	int cmd = uap->cmd;
3962729Sdfr	struct msqid_ds msqbuf;
397140839Ssobomax	int error;
398140839Ssobomax
399165403Sjkim	DPRINTF(("call to msgctl(%d, %d, %p)\n", msqid, cmd, uap->buf));
400140839Ssobomax	if (cmd == IPC_SET &&
401140839Ssobomax	    (error = copyin(uap->buf, &msqbuf, sizeof(msqbuf))) != 0)
402140839Ssobomax		return (error);
403141471Sjhb	error = kern_msgctl(td, msqid, cmd, &msqbuf);
404140839Ssobomax	if (cmd == IPC_STAT && error == 0)
405141471Sjhb		error = copyout(&msqbuf, uap->buf, sizeof(struct msqid_ds));
406140839Ssobomax	return (error);
407140839Ssobomax}
408140839Ssobomax
409140839Ssobomaxint
410141471Sjhbkern_msgctl(td, msqid, cmd, msqbuf)
411140839Ssobomax	struct thread *td;
412140839Ssobomax	int msqid;
413140839Ssobomax	int cmd;
414140839Ssobomax	struct msqid_ds *msqbuf;
415140839Ssobomax{
416140839Ssobomax	int rval, error, msqix;
417137613Srwatson	register struct msqid_kernel *msqkptr;
4182729Sdfr
41991703Sjhb	if (!jail_sysvipc_allowed && jailed(td->td_ucred))
42091703Sjhb		return (ENOSYS);
42191703Sjhb
422140839Ssobomax	msqix = IPCID_TO_IX(msqid);
4232729Sdfr
424140839Ssobomax	if (msqix < 0 || msqix >= msginfo.msgmni) {
425140839Ssobomax		DPRINTF(("msqid (%d) out of range (0<=msqid<%d)\n", msqix,
426100523Salfred		    msginfo.msgmni));
427101772Salfred		return (EINVAL);
4282729Sdfr	}
4292729Sdfr
430140839Ssobomax	msqkptr = &msqids[msqix];
4312729Sdfr
432101772Salfred	mtx_lock(&msq_mtx);
433137613Srwatson	if (msqkptr->u.msg_qbytes == 0) {
434100523Salfred		DPRINTF(("no such msqid\n"));
43582607Sdillon		error = EINVAL;
43682607Sdillon		goto done2;
4372729Sdfr	}
438140839Ssobomax	if (msqkptr->u.msg_perm.seq != IPCID_TO_SEQ(msqid)) {
439100523Salfred		DPRINTF(("wrong sequence number\n"));
44082607Sdillon		error = EINVAL;
44182607Sdillon		goto done2;
4422729Sdfr	}
443140614Srwatson#ifdef MAC
444140614Srwatson	error = mac_check_sysv_msqctl(td->td_ucred, msqkptr, cmd);
445162468Srwatson	if (error != 0)
446140614Srwatson		goto done2;
447140614Srwatson#endif
4482729Sdfr
44982607Sdillon	error = 0;
4502729Sdfr	rval = 0;
4512729Sdfr
4522729Sdfr	switch (cmd) {
4532729Sdfr
4542729Sdfr	case IPC_RMID:
4552729Sdfr	{
4562729Sdfr		struct msg *msghdr;
457137613Srwatson		if ((error = ipcperm(td, &msqkptr->u.msg_perm, IPC_M)))
45882607Sdillon			goto done2;
459137613Srwatson
460140614Srwatson#ifdef MAC
461140614Srwatson		/*
462140614Srwatson		 * Check that the thread has MAC access permissions to
463140614Srwatson		 * individual msghdrs.  Note: We need to do this in a
464140614Srwatson		 * separate loop because the actual loop alters the
465140614Srwatson		 * msq/msghdr info as it progresses, and there is no going
466140614Srwatson		 * back if half the way through we discover that the
467140614Srwatson		 * thread cannot free a certain msghdr.  The msq will get
468140614Srwatson		 * into an inconsistent state.
469140614Srwatson		 */
470140614Srwatson		for (msghdr = msqkptr->u.msg_first; msghdr != NULL;
471140614Srwatson		    msghdr = msghdr->msg_next) {
472140614Srwatson			error = mac_check_sysv_msgrmid(td->td_ucred, msghdr);
473162468Srwatson			if (error != 0)
474140614Srwatson				goto done2;
475140614Srwatson		}
476140614Srwatson#endif
477140614Srwatson
4782729Sdfr		/* Free the message headers */
479137613Srwatson		msghdr = msqkptr->u.msg_first;
4802729Sdfr		while (msghdr != NULL) {
4812729Sdfr			struct msg *msghdr_tmp;
4822729Sdfr
4832729Sdfr			/* Free the segments of each message */
484137613Srwatson			msqkptr->u.msg_cbytes -= msghdr->msg_ts;
485137613Srwatson			msqkptr->u.msg_qnum--;
4862729Sdfr			msghdr_tmp = msghdr;
4872729Sdfr			msghdr = msghdr->msg_next;
4882729Sdfr			msg_freehdr(msghdr_tmp);
4892729Sdfr		}
4902729Sdfr
491137613Srwatson		if (msqkptr->u.msg_cbytes != 0)
4922729Sdfr			panic("msg_cbytes is screwed up");
493137613Srwatson		if (msqkptr->u.msg_qnum != 0)
4942729Sdfr			panic("msg_qnum is screwed up");
4952729Sdfr
496137613Srwatson		msqkptr->u.msg_qbytes = 0;	/* Mark it as free */
4972729Sdfr
498140614Srwatson#ifdef MAC
499140614Srwatson		mac_cleanup_sysv_msgqueue(msqkptr);
500140614Srwatson#endif
501140614Srwatson
502137613Srwatson		wakeup(msqkptr);
5032729Sdfr	}
5042729Sdfr
5052729Sdfr		break;
5062729Sdfr
5072729Sdfr	case IPC_SET:
508137613Srwatson		if ((error = ipcperm(td, &msqkptr->u.msg_perm, IPC_M)))
50982607Sdillon			goto done2;
510140839Ssobomax		if (msqbuf->msg_qbytes > msqkptr->u.msg_qbytes) {
511164033Srwatson			error = priv_check(td, PRIV_IPC_MSGSIZE);
51282607Sdillon			if (error)
51382607Sdillon				goto done2;
51443426Sphk		}
515140839Ssobomax		if (msqbuf->msg_qbytes > msginfo.msgmnb) {
516100523Salfred			DPRINTF(("can't increase msg_qbytes beyond %d"
517100523Salfred			    "(truncating)\n", msginfo.msgmnb));
518140839Ssobomax			msqbuf->msg_qbytes = msginfo.msgmnb;	/* silently restrict qbytes to system limit */
5192729Sdfr		}
520140839Ssobomax		if (msqbuf->msg_qbytes == 0) {
521100523Salfred			DPRINTF(("can't reduce msg_qbytes to 0\n"));
52282607Sdillon			error = EINVAL;		/* non-standard errno! */
52382607Sdillon			goto done2;
5242729Sdfr		}
525140839Ssobomax		msqkptr->u.msg_perm.uid = msqbuf->msg_perm.uid;	/* change the owner */
526140839Ssobomax		msqkptr->u.msg_perm.gid = msqbuf->msg_perm.gid;	/* change the owner */
527137613Srwatson		msqkptr->u.msg_perm.mode = (msqkptr->u.msg_perm.mode & ~0777) |
528140839Ssobomax		    (msqbuf->msg_perm.mode & 0777);
529140839Ssobomax		msqkptr->u.msg_qbytes = msqbuf->msg_qbytes;
530137613Srwatson		msqkptr->u.msg_ctime = time_second;
5312729Sdfr		break;
5322729Sdfr
5332729Sdfr	case IPC_STAT:
534137613Srwatson		if ((error = ipcperm(td, &msqkptr->u.msg_perm, IPC_R))) {
535100523Salfred			DPRINTF(("requester doesn't have read access\n"));
53682607Sdillon			goto done2;
5372729Sdfr		}
538141471Sjhb		*msqbuf = msqkptr->u;
5392729Sdfr		break;
5402729Sdfr
5412729Sdfr	default:
542100523Salfred		DPRINTF(("invalid command %d\n", cmd));
54382607Sdillon		error = EINVAL;
54482607Sdillon		goto done2;
5452729Sdfr	}
5462729Sdfr
54782607Sdillon	if (error == 0)
54883366Sjulian		td->td_retval[0] = rval;
54982607Sdillondone2:
550101772Salfred	mtx_unlock(&msq_mtx);
551141471Sjhb	return (error);
5522729Sdfr}
5532729Sdfr
55412866Speter#ifndef _SYS_SYSPROTO_H_
5552729Sdfrstruct msgget_args {
5562729Sdfr	key_t	key;
5572729Sdfr	int	msgflg;
5582729Sdfr};
55912866Speter#endif
5602729Sdfr
56182607Sdillon/*
56282607Sdillon * MPSAFE
56382607Sdillon */
56412866Speterint
56583366Sjulianmsgget(td, uap)
56683366Sjulian	struct thread *td;
5672729Sdfr	register struct msgget_args *uap;
5682729Sdfr{
56982607Sdillon	int msqid, error = 0;
5702729Sdfr	int key = uap->key;
5712729Sdfr	int msgflg = uap->msgflg;
57291703Sjhb	struct ucred *cred = td->td_ucred;
573137613Srwatson	register struct msqid_kernel *msqkptr = NULL;
5742729Sdfr
575100523Salfred	DPRINTF(("msgget(0x%x, 0%o)\n", key, msgflg));
5762729Sdfr
57791703Sjhb	if (!jail_sysvipc_allowed && jailed(td->td_ucred))
57891703Sjhb		return (ENOSYS);
57991703Sjhb
580101772Salfred	mtx_lock(&msq_mtx);
5812729Sdfr	if (key != IPC_PRIVATE) {
5822729Sdfr		for (msqid = 0; msqid < msginfo.msgmni; msqid++) {
583137613Srwatson			msqkptr = &msqids[msqid];
584137613Srwatson			if (msqkptr->u.msg_qbytes != 0 &&
585137613Srwatson			    msqkptr->u.msg_perm.key == key)
5862729Sdfr				break;
5872729Sdfr		}
5882729Sdfr		if (msqid < msginfo.msgmni) {
589100523Salfred			DPRINTF(("found public key\n"));
5902729Sdfr			if ((msgflg & IPC_CREAT) && (msgflg & IPC_EXCL)) {
591100523Salfred				DPRINTF(("not exclusive\n"));
59282607Sdillon				error = EEXIST;
59382607Sdillon				goto done2;
5942729Sdfr			}
595137613Srwatson			if ((error = ipcperm(td, &msqkptr->u.msg_perm,
596137613Srwatson			    msgflg & 0700))) {
597100523Salfred				DPRINTF(("requester doesn't have 0%o access\n",
598100523Salfred				    msgflg & 0700));
59982607Sdillon				goto done2;
6002729Sdfr			}
601140614Srwatson#ifdef MAC
602140614Srwatson			error = mac_check_sysv_msqget(cred, msqkptr);
603162468Srwatson			if (error != 0)
604140614Srwatson				goto done2;
605140614Srwatson#endif
6062729Sdfr			goto found;
6072729Sdfr		}
6082729Sdfr	}
6092729Sdfr
610100523Salfred	DPRINTF(("need to allocate the msqid_ds\n"));
6112729Sdfr	if (key == IPC_PRIVATE || (msgflg & IPC_CREAT)) {
6122729Sdfr		for (msqid = 0; msqid < msginfo.msgmni; msqid++) {
6132729Sdfr			/*
6142729Sdfr			 * Look for an unallocated and unlocked msqid_ds.
6152729Sdfr			 * msqid_ds's can be locked by msgsnd or msgrcv while
6162729Sdfr			 * they are copying the message in/out.  We can't
6172729Sdfr			 * re-use the entry until they release it.
6182729Sdfr			 */
619137613Srwatson			msqkptr = &msqids[msqid];
620137613Srwatson			if (msqkptr->u.msg_qbytes == 0 &&
621137613Srwatson			    (msqkptr->u.msg_perm.mode & MSG_LOCKED) == 0)
6222729Sdfr				break;
6232729Sdfr		}
6242729Sdfr		if (msqid == msginfo.msgmni) {
625100523Salfred			DPRINTF(("no more msqid_ds's available\n"));
62682607Sdillon			error = ENOSPC;
62782607Sdillon			goto done2;
6282729Sdfr		}
629100523Salfred		DPRINTF(("msqid %d is available\n", msqid));
630137613Srwatson		msqkptr->u.msg_perm.key = key;
631137613Srwatson		msqkptr->u.msg_perm.cuid = cred->cr_uid;
632137613Srwatson		msqkptr->u.msg_perm.uid = cred->cr_uid;
633137613Srwatson		msqkptr->u.msg_perm.cgid = cred->cr_gid;
634137613Srwatson		msqkptr->u.msg_perm.gid = cred->cr_gid;
635137613Srwatson		msqkptr->u.msg_perm.mode = (msgflg & 0777);
6362729Sdfr		/* Make sure that the returned msqid is unique */
637137613Srwatson		msqkptr->u.msg_perm.seq = (msqkptr->u.msg_perm.seq + 1) & 0x7fff;
638137613Srwatson		msqkptr->u.msg_first = NULL;
639137613Srwatson		msqkptr->u.msg_last = NULL;
640137613Srwatson		msqkptr->u.msg_cbytes = 0;
641137613Srwatson		msqkptr->u.msg_qnum = 0;
642137613Srwatson		msqkptr->u.msg_qbytes = msginfo.msgmnb;
643137613Srwatson		msqkptr->u.msg_lspid = 0;
644137613Srwatson		msqkptr->u.msg_lrpid = 0;
645137613Srwatson		msqkptr->u.msg_stime = 0;
646137613Srwatson		msqkptr->u.msg_rtime = 0;
647137613Srwatson		msqkptr->u.msg_ctime = time_second;
648140614Srwatson#ifdef MAC
649140614Srwatson		mac_create_sysv_msgqueue(cred, msqkptr);
650140614Srwatson#endif
6512729Sdfr	} else {
652100523Salfred		DPRINTF(("didn't find it and wasn't asked to create it\n"));
65382607Sdillon		error = ENOENT;
65482607Sdillon		goto done2;
6552729Sdfr	}
6562729Sdfr
6572729Sdfrfound:
6582729Sdfr	/* Construct the unique msqid */
659137613Srwatson	td->td_retval[0] = IXSEQ_TO_IPCID(msqid, msqkptr->u.msg_perm);
66082607Sdillondone2:
661101772Salfred	mtx_unlock(&msq_mtx);
66282607Sdillon	return (error);
6632729Sdfr}
6642729Sdfr
66512866Speter#ifndef _SYS_SYSPROTO_H_
6662729Sdfrstruct msgsnd_args {
6672729Sdfr	int	msqid;
668109895Salfred	const void	*msgp;
6692729Sdfr	size_t	msgsz;
6702729Sdfr	int	msgflg;
6712729Sdfr};
67212866Speter#endif
6732729Sdfr
67412866Speterint
675165403Sjkimkern_msgsnd(td, msqid, msgp, msgsz, msgflg, mtype)
67683366Sjulian	struct thread *td;
677165403Sjkim	int msqid;
678165403Sjkim	const void *msgp;	/* XXX msgp is actually mtext. */
679165403Sjkim	size_t msgsz;
680165403Sjkim	int msgflg;
681165403Sjkim	long mtype;
6822729Sdfr{
683165403Sjkim	int msqix, segs_needed, error = 0;
684137613Srwatson	register struct msqid_kernel *msqkptr;
6852729Sdfr	register struct msg *msghdr;
6862729Sdfr	short next;
6872729Sdfr
68891703Sjhb	if (!jail_sysvipc_allowed && jailed(td->td_ucred))
68991703Sjhb		return (ENOSYS);
69091703Sjhb
691101772Salfred	mtx_lock(&msq_mtx);
692165403Sjkim	msqix = IPCID_TO_IX(msqid);
6932729Sdfr
694165403Sjkim	if (msqix < 0 || msqix >= msginfo.msgmni) {
695165403Sjkim		DPRINTF(("msqid (%d) out of range (0<=msqid<%d)\n", msqix,
696100523Salfred		    msginfo.msgmni));
69782607Sdillon		error = EINVAL;
69882607Sdillon		goto done2;
6992729Sdfr	}
7002729Sdfr
701165403Sjkim	msqkptr = &msqids[msqix];
702137613Srwatson	if (msqkptr->u.msg_qbytes == 0) {
703100523Salfred		DPRINTF(("no such message queue id\n"));
70482607Sdillon		error = EINVAL;
70582607Sdillon		goto done2;
7062729Sdfr	}
707165403Sjkim	if (msqkptr->u.msg_perm.seq != IPCID_TO_SEQ(msqid)) {
708100523Salfred		DPRINTF(("wrong sequence number\n"));
70982607Sdillon		error = EINVAL;
71082607Sdillon		goto done2;
7112729Sdfr	}
7122729Sdfr
713137613Srwatson	if ((error = ipcperm(td, &msqkptr->u.msg_perm, IPC_W))) {
714100523Salfred		DPRINTF(("requester doesn't have write access\n"));
71582607Sdillon		goto done2;
7162729Sdfr	}
7172729Sdfr
718140614Srwatson#ifdef MAC
719140614Srwatson	error = mac_check_sysv_msqsnd(td->td_ucred, msqkptr);
720162468Srwatson	if (error != 0)
721140614Srwatson		goto done2;
722140614Srwatson#endif
723140614Srwatson
7242729Sdfr	segs_needed = (msgsz + msginfo.msgssz - 1) / msginfo.msgssz;
725165403Sjkim	DPRINTF(("msgsz=%zu, msgssz=%d, segs_needed=%d\n", msgsz,
726165403Sjkim	    msginfo.msgssz, segs_needed));
7272729Sdfr	for (;;) {
7282729Sdfr		int need_more_resources = 0;
7292729Sdfr
7302729Sdfr		/*
7312729Sdfr		 * check msgsz
7322729Sdfr		 * (inside this loop in case msg_qbytes changes while we sleep)
7332729Sdfr		 */
7342729Sdfr
735137613Srwatson		if (msgsz > msqkptr->u.msg_qbytes) {
736137613Srwatson			DPRINTF(("msgsz > msqkptr->u.msg_qbytes\n"));
73782607Sdillon			error = EINVAL;
73882607Sdillon			goto done2;
7392729Sdfr		}
7402729Sdfr
741137613Srwatson		if (msqkptr->u.msg_perm.mode & MSG_LOCKED) {
742100523Salfred			DPRINTF(("msqid is locked\n"));
7432729Sdfr			need_more_resources = 1;
7442729Sdfr		}
745137613Srwatson		if (msgsz + msqkptr->u.msg_cbytes > msqkptr->u.msg_qbytes) {
746100523Salfred			DPRINTF(("msgsz + msg_cbytes > msg_qbytes\n"));
7472729Sdfr			need_more_resources = 1;
7482729Sdfr		}
7492729Sdfr		if (segs_needed > nfree_msgmaps) {
750100523Salfred			DPRINTF(("segs_needed > nfree_msgmaps\n"));
7512729Sdfr			need_more_resources = 1;
7522729Sdfr		}
7532729Sdfr		if (free_msghdrs == NULL) {
754100523Salfred			DPRINTF(("no more msghdrs\n"));
7552729Sdfr			need_more_resources = 1;
7562729Sdfr		}
7572729Sdfr
7582729Sdfr		if (need_more_resources) {
7592729Sdfr			int we_own_it;
7602729Sdfr
7612729Sdfr			if ((msgflg & IPC_NOWAIT) != 0) {
762100523Salfred				DPRINTF(("need more resources but caller "
763100523Salfred				    "doesn't want to wait\n"));
76482607Sdillon				error = EAGAIN;
76582607Sdillon				goto done2;
7662729Sdfr			}
7672729Sdfr
768137613Srwatson			if ((msqkptr->u.msg_perm.mode & MSG_LOCKED) != 0) {
769100523Salfred				DPRINTF(("we don't own the msqid_ds\n"));
7702729Sdfr				we_own_it = 0;
7712729Sdfr			} else {
7722729Sdfr				/* Force later arrivals to wait for our
7732729Sdfr				   request */
774100523Salfred				DPRINTF(("we own the msqid_ds\n"));
775137613Srwatson				msqkptr->u.msg_perm.mode |= MSG_LOCKED;
7762729Sdfr				we_own_it = 1;
7772729Sdfr			}
778164368Sjkim			DPRINTF(("msgsnd:  goodnight\n"));
779137613Srwatson			error = msleep(msqkptr, &msq_mtx, (PZERO - 4) | PCATCH,
780164368Sjkim			    "msgsnd", hz);
781164368Sjkim			DPRINTF(("msgsnd:  good morning, error=%d\n", error));
7822729Sdfr			if (we_own_it)
783137613Srwatson				msqkptr->u.msg_perm.mode &= ~MSG_LOCKED;
784164368Sjkim			if (error == EWOULDBLOCK) {
785164368Sjkim				DPRINTF(("msgsnd:  timed out\n"));
786164368Sjkim				continue;
787164368Sjkim			}
78882607Sdillon			if (error != 0) {
789100523Salfred				DPRINTF(("msgsnd:  interrupted system call\n"));
79082607Sdillon				error = EINTR;
79182607Sdillon				goto done2;
7922729Sdfr			}
7932729Sdfr
7942729Sdfr			/*
7952729Sdfr			 * Make sure that the msq queue still exists
7962729Sdfr			 */
7972729Sdfr
798137613Srwatson			if (msqkptr->u.msg_qbytes == 0) {
799100523Salfred				DPRINTF(("msqid deleted\n"));
80082607Sdillon				error = EIDRM;
80182607Sdillon				goto done2;
8022729Sdfr			}
8032729Sdfr
8042729Sdfr		} else {
805100523Salfred			DPRINTF(("got all the resources that we need\n"));
8062729Sdfr			break;
8072729Sdfr		}
8082729Sdfr	}
8092729Sdfr
8102729Sdfr	/*
8112729Sdfr	 * We have the resources that we need.
8122729Sdfr	 * Make sure!
8132729Sdfr	 */
8142729Sdfr
815137613Srwatson	if (msqkptr->u.msg_perm.mode & MSG_LOCKED)
8162729Sdfr		panic("msg_perm.mode & MSG_LOCKED");
8172729Sdfr	if (segs_needed > nfree_msgmaps)
8182729Sdfr		panic("segs_needed > nfree_msgmaps");
819137613Srwatson	if (msgsz + msqkptr->u.msg_cbytes > msqkptr->u.msg_qbytes)
8202729Sdfr		panic("msgsz + msg_cbytes > msg_qbytes");
8212729Sdfr	if (free_msghdrs == NULL)
8222729Sdfr		panic("no more msghdrs");
8232729Sdfr
8242729Sdfr	/*
8252729Sdfr	 * Re-lock the msqid_ds in case we page-fault when copying in the
8262729Sdfr	 * message
8272729Sdfr	 */
8282729Sdfr
829137613Srwatson	if ((msqkptr->u.msg_perm.mode & MSG_LOCKED) != 0)
8302729Sdfr		panic("msqid_ds is already locked");
831137613Srwatson	msqkptr->u.msg_perm.mode |= MSG_LOCKED;
8322729Sdfr
8332729Sdfr	/*
8342729Sdfr	 * Allocate a message header
8352729Sdfr	 */
8362729Sdfr
8372729Sdfr	msghdr = free_msghdrs;
8382729Sdfr	free_msghdrs = msghdr->msg_next;
8392729Sdfr	msghdr->msg_spot = -1;
8402729Sdfr	msghdr->msg_ts = msgsz;
841165403Sjkim	msghdr->msg_type = mtype;
842140614Srwatson#ifdef MAC
843140614Srwatson	/*
844140614Srwatson	 * XXXMAC: Should the mac_check_sysv_msgmsq check follow here
845140614Srwatson	 * immediately?  Or, should it be checked just before the msg is
846140614Srwatson	 * enqueued in the msgq (as it is done now)?
847140614Srwatson	 */
848140614Srwatson	mac_create_sysv_msgmsg(td->td_ucred, msqkptr, msghdr);
849140614Srwatson#endif
8502729Sdfr
8512729Sdfr	/*
8522729Sdfr	 * Allocate space for the message
8532729Sdfr	 */
8542729Sdfr
8552729Sdfr	while (segs_needed > 0) {
8562729Sdfr		if (nfree_msgmaps <= 0)
8572729Sdfr			panic("not enough msgmaps");
8582729Sdfr		if (free_msgmaps == -1)
8592729Sdfr			panic("nil free_msgmaps");
8602729Sdfr		next = free_msgmaps;
8612729Sdfr		if (next <= -1)
8622729Sdfr			panic("next too low #1");
8632729Sdfr		if (next >= msginfo.msgseg)
8642729Sdfr			panic("next out of range #1");
865100523Salfred		DPRINTF(("allocating segment %d to message\n", next));
8662729Sdfr		free_msgmaps = msgmaps[next].next;
8672729Sdfr		nfree_msgmaps--;
8682729Sdfr		msgmaps[next].next = msghdr->msg_spot;
8692729Sdfr		msghdr->msg_spot = next;
8702729Sdfr		segs_needed--;
8712729Sdfr	}
8722729Sdfr
8732729Sdfr	/*
8742729Sdfr	 * Validate the message type
8752729Sdfr	 */
8762729Sdfr
8772729Sdfr	if (msghdr->msg_type < 1) {
8782729Sdfr		msg_freehdr(msghdr);
879137613Srwatson		msqkptr->u.msg_perm.mode &= ~MSG_LOCKED;
880137613Srwatson		wakeup(msqkptr);
881165403Sjkim		DPRINTF(("mtype (%ld) < 1\n", msghdr->msg_type));
88282607Sdillon		error = EINVAL;
88382607Sdillon		goto done2;
8842729Sdfr	}
8852729Sdfr
8862729Sdfr	/*
8872729Sdfr	 * Copy in the message body
8882729Sdfr	 */
8892729Sdfr
8902729Sdfr	next = msghdr->msg_spot;
8912729Sdfr	while (msgsz > 0) {
8922729Sdfr		size_t tlen;
8932729Sdfr		if (msgsz > msginfo.msgssz)
8942729Sdfr			tlen = msginfo.msgssz;
8952729Sdfr		else
8962729Sdfr			tlen = msgsz;
8972729Sdfr		if (next <= -1)
8982729Sdfr			panic("next too low #2");
8992729Sdfr		if (next >= msginfo.msgseg)
9002729Sdfr			panic("next out of range #2");
901101772Salfred		mtx_unlock(&msq_mtx);
902165403Sjkim		if ((error = copyin(msgp, &msgpool[next * msginfo.msgssz],
9032729Sdfr		    tlen)) != 0) {
904101772Salfred			mtx_lock(&msq_mtx);
905100523Salfred			DPRINTF(("error %d copying in message segment\n",
906100523Salfred			    error));
9072729Sdfr			msg_freehdr(msghdr);
908137613Srwatson			msqkptr->u.msg_perm.mode &= ~MSG_LOCKED;
909137613Srwatson			wakeup(msqkptr);
91082607Sdillon			goto done2;
9112729Sdfr		}
912101772Salfred		mtx_lock(&msq_mtx);
9132729Sdfr		msgsz -= tlen;
914165403Sjkim		msgp = (const char *)msgp + tlen;
9152729Sdfr		next = msgmaps[next].next;
9162729Sdfr	}
9172729Sdfr	if (next != -1)
9182729Sdfr		panic("didn't use all the msg segments");
9192729Sdfr
9202729Sdfr	/*
9212729Sdfr	 * We've got the message.  Unlock the msqid_ds.
9222729Sdfr	 */
9232729Sdfr
924137613Srwatson	msqkptr->u.msg_perm.mode &= ~MSG_LOCKED;
9252729Sdfr
9262729Sdfr	/*
9272729Sdfr	 * Make sure that the msqid_ds is still allocated.
9282729Sdfr	 */
9292729Sdfr
930137613Srwatson	if (msqkptr->u.msg_qbytes == 0) {
9312729Sdfr		msg_freehdr(msghdr);
932137613Srwatson		wakeup(msqkptr);
93382607Sdillon		error = EIDRM;
93482607Sdillon		goto done2;
9352729Sdfr	}
9362729Sdfr
937140614Srwatson#ifdef MAC
9382729Sdfr	/*
939140614Srwatson	 * Note: Since the task/thread allocates the msghdr and usually
940140614Srwatson	 * primes it with its own MAC label, for a majority of policies, it
941140614Srwatson	 * won't be necessary to check whether the msghdr has access
942140614Srwatson	 * permissions to the msgq.  The mac_check_sysv_msqsnd check would
943140614Srwatson	 * suffice in that case.  However, this hook may be required where
944140614Srwatson	 * individual policies derive a non-identical label for the msghdr
945140614Srwatson	 * from the current thread label and may want to check the msghdr
946140614Srwatson	 * enqueue permissions, along with read/write permissions to the
947140614Srwatson	 * msgq.
948140614Srwatson	 */
949140614Srwatson	error = mac_check_sysv_msgmsq(td->td_ucred, msghdr, msqkptr);
950140614Srwatson	if (error != 0) {
951140614Srwatson		msg_freehdr(msghdr);
952140614Srwatson		wakeup(msqkptr);
953140614Srwatson		goto done2;
954140614Srwatson	}
955140614Srwatson#endif
956140614Srwatson
957140614Srwatson	/*
9582729Sdfr	 * Put the message into the queue
9592729Sdfr	 */
960137613Srwatson	if (msqkptr->u.msg_first == NULL) {
961137613Srwatson		msqkptr->u.msg_first = msghdr;
962137613Srwatson		msqkptr->u.msg_last = msghdr;
9632729Sdfr	} else {
964137613Srwatson		msqkptr->u.msg_last->msg_next = msghdr;
965137613Srwatson		msqkptr->u.msg_last = msghdr;
9662729Sdfr	}
967137613Srwatson	msqkptr->u.msg_last->msg_next = NULL;
9682729Sdfr
969137613Srwatson	msqkptr->u.msg_cbytes += msghdr->msg_ts;
970137613Srwatson	msqkptr->u.msg_qnum++;
971137613Srwatson	msqkptr->u.msg_lspid = td->td_proc->p_pid;
972137613Srwatson	msqkptr->u.msg_stime = time_second;
9732729Sdfr
974137613Srwatson	wakeup(msqkptr);
97583366Sjulian	td->td_retval[0] = 0;
97682607Sdillondone2:
977101772Salfred	mtx_unlock(&msq_mtx);
97882607Sdillon	return (error);
9792729Sdfr}
9802729Sdfr
981165403Sjkim/*
982165403Sjkim * MPSAFE
983165403Sjkim */
984165403Sjkimint
985165403Sjkimmsgsnd(td, uap)
986165403Sjkim	struct thread *td;
987165403Sjkim	register struct msgsnd_args *uap;
988165403Sjkim{
989165403Sjkim	int error;
990165403Sjkim	long mtype;
991165403Sjkim
992165403Sjkim	DPRINTF(("call to msgsnd(%d, %p, %zu, %d)\n", uap->msqid, uap->msgp,
993165403Sjkim	    uap->msgsz, uap->msgflg));
994165403Sjkim
995165403Sjkim	if ((error = copyin(uap->msgp, &mtype, sizeof(mtype))) != 0) {
996165403Sjkim		DPRINTF(("error %d copying the message type\n", error));
997165403Sjkim		return (error);
998165403Sjkim	}
999165403Sjkim	return (kern_msgsnd(td, uap->msqid,
1000165403Sjkim	    (const char *)uap->msgp + sizeof(mtype),
1001165403Sjkim	    uap->msgsz, uap->msgflg, mtype));
1002165403Sjkim}
1003165403Sjkim
100412866Speter#ifndef _SYS_SYSPROTO_H_
10052729Sdfrstruct msgrcv_args {
10062729Sdfr	int	msqid;
10072729Sdfr	void	*msgp;
10082729Sdfr	size_t	msgsz;
10092729Sdfr	long	msgtyp;
10102729Sdfr	int	msgflg;
10112729Sdfr};
101212866Speter#endif
10132729Sdfr
101412866Speterint
1015165403Sjkimkern_msgrcv(td, msqid, msgp, msgsz, msgtyp, msgflg, mtype)
101683366Sjulian	struct thread *td;
1017165403Sjkim	int msqid;
1018165403Sjkim	void *msgp;	/* XXX msgp is actually mtext. */
1019165403Sjkim	size_t msgsz;
1020165403Sjkim	long msgtyp;
1021165403Sjkim	int msgflg;
1022165403Sjkim	long *mtype;
10232729Sdfr{
10242729Sdfr	size_t len;
1025137613Srwatson	register struct msqid_kernel *msqkptr;
10262729Sdfr	register struct msg *msghdr;
1027165403Sjkim	int msqix, error = 0;
10282729Sdfr	short next;
10292729Sdfr
103091703Sjhb	if (!jail_sysvipc_allowed && jailed(td->td_ucred))
103191703Sjhb		return (ENOSYS);
103291703Sjhb
1033165403Sjkim	msqix = IPCID_TO_IX(msqid);
10342729Sdfr
1035165403Sjkim	if (msqix < 0 || msqix >= msginfo.msgmni) {
1036165403Sjkim		DPRINTF(("msqid (%d) out of range (0<=msqid<%d)\n", msqix,
1037100523Salfred		    msginfo.msgmni));
1038101772Salfred		return (EINVAL);
10392729Sdfr	}
10402729Sdfr
1041165403Sjkim	msqkptr = &msqids[msqix];
1042101772Salfred	mtx_lock(&msq_mtx);
1043137613Srwatson	if (msqkptr->u.msg_qbytes == 0) {
1044100523Salfred		DPRINTF(("no such message queue id\n"));
104582607Sdillon		error = EINVAL;
104682607Sdillon		goto done2;
10472729Sdfr	}
1048165403Sjkim	if (msqkptr->u.msg_perm.seq != IPCID_TO_SEQ(msqid)) {
1049100523Salfred		DPRINTF(("wrong sequence number\n"));
105082607Sdillon		error = EINVAL;
105182607Sdillon		goto done2;
10522729Sdfr	}
10532729Sdfr
1054137613Srwatson	if ((error = ipcperm(td, &msqkptr->u.msg_perm, IPC_R))) {
1055100523Salfred		DPRINTF(("requester doesn't have read access\n"));
105682607Sdillon		goto done2;
10572729Sdfr	}
10582729Sdfr
1059140614Srwatson#ifdef MAC
1060140614Srwatson	error = mac_check_sysv_msqrcv(td->td_ucred, msqkptr);
1061162468Srwatson	if (error != 0)
1062140614Srwatson		goto done2;
1063140614Srwatson#endif
1064140614Srwatson
10652729Sdfr	msghdr = NULL;
10662729Sdfr	while (msghdr == NULL) {
10672729Sdfr		if (msgtyp == 0) {
1068137613Srwatson			msghdr = msqkptr->u.msg_first;
10692729Sdfr			if (msghdr != NULL) {
10702729Sdfr				if (msgsz < msghdr->msg_ts &&
10712729Sdfr				    (msgflg & MSG_NOERROR) == 0) {
1072100523Salfred					DPRINTF(("first message on the queue "
1073165403Sjkim					    "is too big (want %zu, got %d)\n",
1074100523Salfred					    msgsz, msghdr->msg_ts));
107582607Sdillon					error = E2BIG;
107682607Sdillon					goto done2;
10772729Sdfr				}
1078140614Srwatson#ifdef MAC
1079140614Srwatson				error = mac_check_sysv_msgrcv(td->td_ucred,
1080140614Srwatson				    msghdr);
1081162468Srwatson				if (error != 0)
1082140614Srwatson					goto done2;
1083140614Srwatson#endif
1084137613Srwatson				if (msqkptr->u.msg_first == msqkptr->u.msg_last) {
1085137613Srwatson					msqkptr->u.msg_first = NULL;
1086137613Srwatson					msqkptr->u.msg_last = NULL;
10872729Sdfr				} else {
1088137613Srwatson					msqkptr->u.msg_first = msghdr->msg_next;
1089137613Srwatson					if (msqkptr->u.msg_first == NULL)
10902729Sdfr						panic("msg_first/last screwed up #1");
10912729Sdfr				}
10922729Sdfr			}
10932729Sdfr		} else {
10942729Sdfr			struct msg *previous;
10952729Sdfr			struct msg **prev;
10962729Sdfr
10972729Sdfr			previous = NULL;
1098137613Srwatson			prev = &(msqkptr->u.msg_first);
10992729Sdfr			while ((msghdr = *prev) != NULL) {
11002729Sdfr				/*
11012729Sdfr				 * Is this message's type an exact match or is
11022729Sdfr				 * this message's type less than or equal to
11032729Sdfr				 * the absolute value of a negative msgtyp?
11042729Sdfr				 * Note that the second half of this test can
11052729Sdfr				 * NEVER be true if msgtyp is positive since
11062729Sdfr				 * msg_type is always positive!
11072729Sdfr				 */
11082729Sdfr
11092729Sdfr				if (msgtyp == msghdr->msg_type ||
11102729Sdfr				    msghdr->msg_type <= -msgtyp) {
1111165403Sjkim					DPRINTF(("found message type %ld, "
1112165403Sjkim					    "requested %ld\n",
1113100523Salfred					    msghdr->msg_type, msgtyp));
11142729Sdfr					if (msgsz < msghdr->msg_ts &&
11152729Sdfr					    (msgflg & MSG_NOERROR) == 0) {
1116100523Salfred						DPRINTF(("requested message "
1117100523Salfred						    "on the queue is too big "
1118165403Sjkim						    "(want %zu, got %hu)\n",
1119100523Salfred						    msgsz, msghdr->msg_ts));
112082607Sdillon						error = E2BIG;
112182607Sdillon						goto done2;
11222729Sdfr					}
1123140614Srwatson#ifdef MAC
1124140614Srwatson					error = mac_check_sysv_msgrcv(
1125140614Srwatson					    td->td_ucred, msghdr);
1126162468Srwatson					if (error != 0)
1127140614Srwatson						goto done2;
1128140614Srwatson#endif
11292729Sdfr					*prev = msghdr->msg_next;
1130137613Srwatson					if (msghdr == msqkptr->u.msg_last) {
11312729Sdfr						if (previous == NULL) {
11322729Sdfr							if (prev !=
1133137613Srwatson							    &msqkptr->u.msg_first)
11342729Sdfr								panic("msg_first/last screwed up #2");
1135137613Srwatson							msqkptr->u.msg_first =
11362729Sdfr							    NULL;
1137137613Srwatson							msqkptr->u.msg_last =
11382729Sdfr							    NULL;
11392729Sdfr						} else {
11402729Sdfr							if (prev ==
1141137613Srwatson							    &msqkptr->u.msg_first)
11422729Sdfr								panic("msg_first/last screwed up #3");
1143137613Srwatson							msqkptr->u.msg_last =
11442729Sdfr							    previous;
11452729Sdfr						}
11462729Sdfr					}
11472729Sdfr					break;
11482729Sdfr				}
11492729Sdfr				previous = msghdr;
11502729Sdfr				prev = &(msghdr->msg_next);
11512729Sdfr			}
11522729Sdfr		}
11532729Sdfr
11542729Sdfr		/*
11552729Sdfr		 * We've either extracted the msghdr for the appropriate
11562729Sdfr		 * message or there isn't one.
11572729Sdfr		 * If there is one then bail out of this loop.
11582729Sdfr		 */
11592729Sdfr
11602729Sdfr		if (msghdr != NULL)
11612729Sdfr			break;
11622729Sdfr
11632729Sdfr		/*
11642729Sdfr		 * Hmph!  No message found.  Does the user want to wait?
11652729Sdfr		 */
11662729Sdfr
11672729Sdfr		if ((msgflg & IPC_NOWAIT) != 0) {
1168165403Sjkim			DPRINTF(("no appropriate message found (msgtyp=%ld)\n",
1169100523Salfred			    msgtyp));
11702729Sdfr			/* The SVID says to return ENOMSG. */
117182607Sdillon			error = ENOMSG;
117282607Sdillon			goto done2;
11732729Sdfr		}
11742729Sdfr
11752729Sdfr		/*
11762729Sdfr		 * Wait for something to happen
11772729Sdfr		 */
11782729Sdfr
1179100523Salfred		DPRINTF(("msgrcv:  goodnight\n"));
1180137613Srwatson		error = msleep(msqkptr, &msq_mtx, (PZERO - 4) | PCATCH,
1181164368Sjkim		    "msgrcv", 0);
1182100523Salfred		DPRINTF(("msgrcv:  good morning (error=%d)\n", error));
11832729Sdfr
118482607Sdillon		if (error != 0) {
1185164368Sjkim			DPRINTF(("msgrcv:  interrupted system call\n"));
118682607Sdillon			error = EINTR;
118782607Sdillon			goto done2;
11882729Sdfr		}
11892729Sdfr
11902729Sdfr		/*
11912729Sdfr		 * Make sure that the msq queue still exists
11922729Sdfr		 */
11932729Sdfr
1194137613Srwatson		if (msqkptr->u.msg_qbytes == 0 ||
1195165403Sjkim		    msqkptr->u.msg_perm.seq != IPCID_TO_SEQ(msqid)) {
1196100523Salfred			DPRINTF(("msqid deleted\n"));
119782607Sdillon			error = EIDRM;
119882607Sdillon			goto done2;
11992729Sdfr		}
12002729Sdfr	}
12012729Sdfr
12022729Sdfr	/*
12032729Sdfr	 * Return the message to the user.
12042729Sdfr	 *
12052729Sdfr	 * First, do the bookkeeping (before we risk being interrupted).
12062729Sdfr	 */
12072729Sdfr
1208137613Srwatson	msqkptr->u.msg_cbytes -= msghdr->msg_ts;
1209137613Srwatson	msqkptr->u.msg_qnum--;
1210137613Srwatson	msqkptr->u.msg_lrpid = td->td_proc->p_pid;
1211137613Srwatson	msqkptr->u.msg_rtime = time_second;
12122729Sdfr
12132729Sdfr	/*
12142729Sdfr	 * Make msgsz the actual amount that we'll be returning.
12152729Sdfr	 * Note that this effectively truncates the message if it is too long
12162729Sdfr	 * (since msgsz is never increased).
12172729Sdfr	 */
12182729Sdfr
1219165403Sjkim	DPRINTF(("found a message, msgsz=%zu, msg_ts=%hu\n", msgsz,
1220100523Salfred	    msghdr->msg_ts));
12212729Sdfr	if (msgsz > msghdr->msg_ts)
12222729Sdfr		msgsz = msghdr->msg_ts;
1223165403Sjkim	*mtype = msghdr->msg_type;
12242729Sdfr
12252729Sdfr	/*
12262729Sdfr	 * Return the segments to the user
12272729Sdfr	 */
12282729Sdfr
12292729Sdfr	next = msghdr->msg_spot;
12302729Sdfr	for (len = 0; len < msgsz; len += msginfo.msgssz) {
12312729Sdfr		size_t tlen;
12322729Sdfr
123345921Ssada		if (msgsz - len > msginfo.msgssz)
12342729Sdfr			tlen = msginfo.msgssz;
12352729Sdfr		else
123645921Ssada			tlen = msgsz - len;
12372729Sdfr		if (next <= -1)
12382729Sdfr			panic("next too low #3");
12392729Sdfr		if (next >= msginfo.msgseg)
12402729Sdfr			panic("next out of range #3");
1241101772Salfred		mtx_unlock(&msq_mtx);
1242165403Sjkim		error = copyout(&msgpool[next * msginfo.msgssz], msgp, tlen);
1243101772Salfred		mtx_lock(&msq_mtx);
124482607Sdillon		if (error != 0) {
1245100523Salfred			DPRINTF(("error (%d) copying out message segment\n",
1246100523Salfred			    error));
12472729Sdfr			msg_freehdr(msghdr);
1248137613Srwatson			wakeup(msqkptr);
124982607Sdillon			goto done2;
12502729Sdfr		}
1251165403Sjkim		msgp = (char *)msgp + tlen;
12522729Sdfr		next = msgmaps[next].next;
12532729Sdfr	}
12542729Sdfr
12552729Sdfr	/*
12562729Sdfr	 * Done, return the actual number of bytes copied out.
12572729Sdfr	 */
12582729Sdfr
12592729Sdfr	msg_freehdr(msghdr);
1260137613Srwatson	wakeup(msqkptr);
126183366Sjulian	td->td_retval[0] = msgsz;
126282607Sdillondone2:
1263101772Salfred	mtx_unlock(&msq_mtx);
126482607Sdillon	return (error);
12652729Sdfr}
126677461Sdd
1267165403Sjkim/*
1268165403Sjkim * MPSAFE
1269165403Sjkim */
1270165403Sjkimint
1271165403Sjkimmsgrcv(td, uap)
1272165403Sjkim	struct thread *td;
1273165403Sjkim	register struct msgrcv_args *uap;
1274165403Sjkim{
1275165403Sjkim	int error;
1276165403Sjkim	long mtype;
1277165403Sjkim
1278165403Sjkim	DPRINTF(("call to msgrcv(%d, %p, %zu, %ld, %d)\n", uap->msqid,
1279165403Sjkim	    uap->msgp, uap->msgsz, uap->msgtyp, uap->msgflg));
1280165403Sjkim
1281165403Sjkim	if ((error = kern_msgrcv(td, uap->msqid,
1282165403Sjkim	    (char *)uap->msgp + sizeof(mtype), uap->msgsz,
1283165403Sjkim	    uap->msgtyp, uap->msgflg, &mtype)) != 0)
1284165403Sjkim		return (error);
1285165403Sjkim	if ((error = copyout(&mtype, uap->msgp, sizeof(mtype))) != 0)
1286165403Sjkim		DPRINTF(("error %d copying the message type\n", error));
1287165403Sjkim	return (error);
1288165403Sjkim}
1289165403Sjkim
129077461Sddstatic int
129177461Sddsysctl_msqids(SYSCTL_HANDLER_ARGS)
129277461Sdd{
129377461Sdd
129477461Sdd	return (SYSCTL_OUT(req, msqids,
1295137613Srwatson	    sizeof(struct msqid_kernel) * msginfo.msgmni));
129677461Sdd}
129777461Sdd
1298141710ScsjpSYSCTL_INT(_kern_ipc, OID_AUTO, msgmax, CTLFLAG_RD, &msginfo.msgmax, 0,
1299141710Scsjp    "Maximum message size");
1300141710ScsjpSYSCTL_INT(_kern_ipc, OID_AUTO, msgmni, CTLFLAG_RDTUN, &msginfo.msgmni, 0,
1301141710Scsjp    "Number of message queue identifiers");
1302141710ScsjpSYSCTL_INT(_kern_ipc, OID_AUTO, msgmnb, CTLFLAG_RDTUN, &msginfo.msgmnb, 0,
1303141710Scsjp    "Maximum number of bytes in a queue");
1304141710ScsjpSYSCTL_INT(_kern_ipc, OID_AUTO, msgtql, CTLFLAG_RDTUN, &msginfo.msgtql, 0,
1305141710Scsjp    "Maximum number of messages in the system");
1306141710ScsjpSYSCTL_INT(_kern_ipc, OID_AUTO, msgssz, CTLFLAG_RDTUN, &msginfo.msgssz, 0,
1307141710Scsjp    "Size of a message segment");
1308141710ScsjpSYSCTL_INT(_kern_ipc, OID_AUTO, msgseg, CTLFLAG_RDTUN, &msginfo.msgseg, 0,
1309141710Scsjp    "Number of message segments");
131077461SddSYSCTL_PROC(_kern_ipc, OID_AUTO, msqids, CTLFLAG_RD,
131177461Sdd    NULL, 0, sysctl_msqids, "", "Message queue IDs");
1312