sysv_msg.c revision 194910
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 194910 2009-06-24 21:10:52Z jhb $");
52116182Sobrien
53194894Sjhb#include "opt_compat.h"
5459839Speter#include "opt_sysvipc.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
84194575Srdivacky#define DPRINTF(a)	(void)0
85100523Salfred#endif
862729Sdfr
8792723Salfredstatic void msg_freehdr(struct msg *msghdr);
882729Sdfr
8959839Speter#ifndef MSGSSZ
9059839Speter#define MSGSSZ	8		/* Each segment must be 2^N long */
9159839Speter#endif
9259839Speter#ifndef MSGSEG
9359839Speter#define MSGSEG	2048		/* must be less than 32767 */
9459839Speter#endif
9559839Speter#define MSGMAX	(MSGSSZ*MSGSEG)
9659839Speter#ifndef MSGMNB
9759839Speter#define MSGMNB	2048		/* max # of bytes in a queue */
9859839Speter#endif
9959839Speter#ifndef MSGMNI
10059839Speter#define MSGMNI	40
10159839Speter#endif
10259839Speter#ifndef MSGTQL
10359839Speter#define MSGTQL	40
10459839Speter#endif
10559839Speter
10659839Speter/*
10759839Speter * Based on the configuration parameters described in an SVR2 (yes, two)
10859839Speter * config(1m) man page.
10959839Speter *
11059839Speter * Each message is broken up and stored in segments that are msgssz bytes
11159839Speter * long.  For efficiency reasons, this should be a power of two.  Also,
11259839Speter * it doesn't make sense if it is less than 8 or greater than about 256.
11359839Speter * Consequently, msginit in kern/sysv_msg.c checks that msgssz is a power of
11459839Speter * two between 8 and 1024 inclusive (and panic's if it isn't).
11559839Speter */
11659839Speterstruct msginfo msginfo = {
11759839Speter                MSGMAX,         /* max chars in a message */
11859839Speter                MSGMNI,         /* # of message queue identifiers */
11959839Speter                MSGMNB,         /* max chars in a queue */
12059839Speter                MSGTQL,         /* max messages in system */
12159839Speter                MSGSSZ,         /* size of a message segment */
12259839Speter                		/* (must be small power of 2 greater than 4) */
12359839Speter                MSGSEG          /* number of message segments */
12459839Speter};
12559839Speter
12659839Speter/*
12759839Speter * macros to convert between msqid_ds's and msqid's.
12859839Speter * (specific to this implementation)
12959839Speter */
13059839Speter#define MSQID(ix,ds)	((ix) & 0xffff | (((ds).msg_perm.seq << 16) & 0xffff0000))
13159839Speter#define MSQID_IX(id)	((id) & 0xffff)
13259839Speter#define MSQID_SEQ(id)	(((id) >> 16) & 0xffff)
13359839Speter
13459839Speter/*
13559839Speter * The rest of this file is specific to this particular implementation.
13659839Speter */
13759839Speter
13859839Speterstruct msgmap {
13959839Speter	short	next;		/* next segment in buffer */
14059839Speter    				/* -1 -> available */
14159839Speter    				/* 0..(MSGSEG-1) -> index of next segment */
14259839Speter};
14359839Speter
14459839Speter#define MSG_LOCKED	01000	/* Is this msqid_ds locked? */
14559839Speter
14612819Sphkstatic int nfree_msgmaps;	/* # of free map entries */
14712819Sphkstatic short free_msgmaps;	/* head of linked list of free map entries */
14859839Speterstatic struct msg *free_msghdrs;/* list of free msg headers */
14959839Speterstatic char *msgpool;		/* MSGMAX byte long msg buffer pool */
15059839Speterstatic struct msgmap *msgmaps;	/* MSGSEG msgmap structures */
15159839Speterstatic struct msg *msghdrs;	/* MSGTQL msg headers */
152137613Srwatsonstatic struct msqid_kernel *msqids;	/* MSGMNI msqid_kernel struct's */
153101772Salfredstatic struct mtx msq_mtx;	/* global mutex for message queues. */
1542729Sdfr
15559839Speterstatic void
15669449Salfredmsginit()
1572729Sdfr{
1582729Sdfr	register int i;
1592729Sdfr
16083765Smr	TUNABLE_INT_FETCH("kern.ipc.msgseg", &msginfo.msgseg);
16183765Smr	TUNABLE_INT_FETCH("kern.ipc.msgssz", &msginfo.msgssz);
16283765Smr	msginfo.msgmax = msginfo.msgseg * msginfo.msgssz;
16383765Smr	TUNABLE_INT_FETCH("kern.ipc.msgmni", &msginfo.msgmni);
164139436Srwatson	TUNABLE_INT_FETCH("kern.ipc.msgmnb", &msginfo.msgmnb);
165139436Srwatson	TUNABLE_INT_FETCH("kern.ipc.msgtql", &msginfo.msgtql);
16683765Smr
167111119Simp	msgpool = malloc(msginfo.msgmax, M_MSG, M_WAITOK);
16859839Speter	if (msgpool == NULL)
16959839Speter		panic("msgpool is NULL");
170111119Simp	msgmaps = malloc(sizeof(struct msgmap) * msginfo.msgseg, M_MSG, M_WAITOK);
17159839Speter	if (msgmaps == NULL)
17259839Speter		panic("msgmaps is NULL");
173111119Simp	msghdrs = malloc(sizeof(struct msg) * msginfo.msgtql, M_MSG, M_WAITOK);
17459839Speter	if (msghdrs == NULL)
17559839Speter		panic("msghdrs is NULL");
176137613Srwatson	msqids = malloc(sizeof(struct msqid_kernel) * msginfo.msgmni, M_MSG,
177137613Srwatson	    M_WAITOK);
17859839Speter	if (msqids == NULL)
17959839Speter		panic("msqids is NULL");
18059839Speter
1812729Sdfr	/*
1822729Sdfr	 * msginfo.msgssz should be a power of two for efficiency reasons.
1832729Sdfr	 * It is also pretty silly if msginfo.msgssz is less than 8
1842729Sdfr	 * or greater than about 256 so ...
1852729Sdfr	 */
1862729Sdfr
1872729Sdfr	i = 8;
1882729Sdfr	while (i < 1024 && i != msginfo.msgssz)
1892729Sdfr		i <<= 1;
1902729Sdfr    	if (i != msginfo.msgssz) {
191100523Salfred		DPRINTF(("msginfo.msgssz=%d (0x%x)\n", msginfo.msgssz,
192100523Salfred		    msginfo.msgssz));
1932729Sdfr		panic("msginfo.msgssz not a small power of 2");
1942729Sdfr	}
1952729Sdfr
1962729Sdfr	if (msginfo.msgseg > 32767) {
197100523Salfred		DPRINTF(("msginfo.msgseg=%d\n", msginfo.msgseg));
1982729Sdfr		panic("msginfo.msgseg > 32767");
1992729Sdfr	}
2002729Sdfr
2012729Sdfr	if (msgmaps == NULL)
2022729Sdfr		panic("msgmaps is NULL");
2032729Sdfr
2042729Sdfr	for (i = 0; i < msginfo.msgseg; i++) {
2052729Sdfr		if (i > 0)
2062729Sdfr			msgmaps[i-1].next = i;
2072729Sdfr		msgmaps[i].next = -1;	/* implies entry is available */
2082729Sdfr	}
2092729Sdfr	free_msgmaps = 0;
2102729Sdfr	nfree_msgmaps = msginfo.msgseg;
2112729Sdfr
2122729Sdfr	if (msghdrs == NULL)
2132729Sdfr		panic("msghdrs is NULL");
2142729Sdfr
2152729Sdfr	for (i = 0; i < msginfo.msgtql; i++) {
2162729Sdfr		msghdrs[i].msg_type = 0;
2172729Sdfr		if (i > 0)
2182729Sdfr			msghdrs[i-1].msg_next = &msghdrs[i];
2192729Sdfr		msghdrs[i].msg_next = NULL;
220140614Srwatson#ifdef MAC
221172930Srwatson		mac_sysvmsg_init(&msghdrs[i]);
222140614Srwatson#endif
2232729Sdfr    	}
2242729Sdfr	free_msghdrs = &msghdrs[0];
2252729Sdfr
2262729Sdfr	if (msqids == NULL)
2272729Sdfr		panic("msqids is NULL");
2282729Sdfr
2292729Sdfr	for (i = 0; i < msginfo.msgmni; i++) {
230137613Srwatson		msqids[i].u.msg_qbytes = 0;	/* implies entry is available */
231137613Srwatson		msqids[i].u.msg_perm.seq = 0;	/* reset to a known value */
232137613Srwatson		msqids[i].u.msg_perm.mode = 0;
233140614Srwatson#ifdef MAC
234172930Srwatson		mac_sysvmsq_init(&msqids[i]);
235140614Srwatson#endif
2362729Sdfr	}
237101772Salfred	mtx_init(&msq_mtx, "msq", NULL, MTX_DEF);
2382729Sdfr}
2392729Sdfr
24069449Salfredstatic int
24169449Salfredmsgunload()
24269449Salfred{
243137613Srwatson	struct msqid_kernel *msqkptr;
24469449Salfred	int msqid;
245140614Srwatson#ifdef MAC
246140614Srwatson	int i;
247140614Srwatson#endif
24869449Salfred
24969449Salfred	for (msqid = 0; msqid < msginfo.msgmni; msqid++) {
25069449Salfred		/*
25169449Salfred		 * Look for an unallocated and unlocked msqid_ds.
25269449Salfred		 * msqid_ds's can be locked by msgsnd or msgrcv while
25369449Salfred		 * they are copying the message in/out.  We can't
25469449Salfred		 * re-use the entry until they release it.
25569449Salfred		 */
256137613Srwatson		msqkptr = &msqids[msqid];
257137613Srwatson		if (msqkptr->u.msg_qbytes != 0 ||
258137613Srwatson		    (msqkptr->u.msg_perm.mode & MSG_LOCKED) != 0)
25969449Salfred			break;
26069449Salfred	}
26169449Salfred	if (msqid != msginfo.msgmni)
26269449Salfred		return (EBUSY);
26369449Salfred
264140614Srwatson#ifdef MAC
265140614Srwatson	for (i = 0; i < msginfo.msgtql; i++)
266172930Srwatson		mac_sysvmsg_destroy(&msghdrs[i]);
267140614Srwatson	for (msqid = 0; msqid < msginfo.msgmni; msqid++)
268172930Srwatson		mac_sysvmsq_destroy(&msqids[msqid]);
269140614Srwatson#endif
27069449Salfred	free(msgpool, M_MSG);
27169449Salfred	free(msgmaps, M_MSG);
27269449Salfred	free(msghdrs, M_MSG);
27369449Salfred	free(msqids, M_MSG);
274101772Salfred	mtx_destroy(&msq_mtx);
27569449Salfred	return (0);
27669449Salfred}
27769449Salfred
27869449Salfred
27969449Salfredstatic int
28069449Salfredsysvmsg_modload(struct module *module, int cmd, void *arg)
28169449Salfred{
28269449Salfred	int error = 0;
28369449Salfred
28469449Salfred	switch (cmd) {
28569449Salfred	case MOD_LOAD:
28669449Salfred		msginit();
28769449Salfred		break;
28869449Salfred	case MOD_UNLOAD:
28969449Salfred		error = msgunload();
29069449Salfred		break;
29169449Salfred	case MOD_SHUTDOWN:
29269449Salfred		break;
29369449Salfred	default:
29469449Salfred		error = EINVAL;
29569449Salfred		break;
29669449Salfred	}
29769449Salfred	return (error);
29869449Salfred}
29969449Salfred
30071038Sdesstatic moduledata_t sysvmsg_mod = {
30171038Sdes	"sysvmsg",
30269449Salfred	&sysvmsg_modload,
30369449Salfred	NULL
30469449Salfred};
30569449Salfred
30688633SalfredSYSCALL_MODULE_HELPER(msgctl);
30788633SalfredSYSCALL_MODULE_HELPER(msgget);
30888633SalfredSYSCALL_MODULE_HELPER(msgsnd);
30988633SalfredSYSCALL_MODULE_HELPER(msgrcv);
31069449Salfred
311194832SjhbDECLARE_MODULE(sysvmsg, sysvmsg_mod, SI_SUB_SYSV_MSG, SI_ORDER_FIRST);
31271038SdesMODULE_VERSION(sysvmsg, 1);
31369449Salfred
3142729Sdfrstatic void
3152729Sdfrmsg_freehdr(msghdr)
3162729Sdfr	struct msg *msghdr;
3172729Sdfr{
3182729Sdfr	while (msghdr->msg_ts > 0) {
3192729Sdfr		short next;
3202729Sdfr		if (msghdr->msg_spot < 0 || msghdr->msg_spot >= msginfo.msgseg)
3212729Sdfr			panic("msghdr->msg_spot out of range");
3222729Sdfr		next = msgmaps[msghdr->msg_spot].next;
3232729Sdfr		msgmaps[msghdr->msg_spot].next = free_msgmaps;
3242729Sdfr		free_msgmaps = msghdr->msg_spot;
3252729Sdfr		nfree_msgmaps++;
3262729Sdfr		msghdr->msg_spot = next;
3272729Sdfr		if (msghdr->msg_ts >= msginfo.msgssz)
3282729Sdfr			msghdr->msg_ts -= msginfo.msgssz;
3292729Sdfr		else
3302729Sdfr			msghdr->msg_ts = 0;
3312729Sdfr	}
3322729Sdfr	if (msghdr->msg_spot != -1)
3332729Sdfr		panic("msghdr->msg_spot != -1");
3342729Sdfr	msghdr->msg_next = free_msghdrs;
3352729Sdfr	free_msghdrs = msghdr;
336140614Srwatson#ifdef MAC
337172930Srwatson	mac_sysvmsg_cleanup(msghdr);
338140614Srwatson#endif
3392729Sdfr}
3402729Sdfr
34112866Speter#ifndef _SYS_SYSPROTO_H_
3422729Sdfrstruct msgctl_args {
3432729Sdfr	int	msqid;
3442729Sdfr	int	cmd;
34512866Speter	struct	msqid_ds *buf;
3462729Sdfr};
34712866Speter#endif
34812866Speterint
34983366Sjulianmsgctl(td, uap)
35083366Sjulian	struct thread *td;
3512729Sdfr	register struct msgctl_args *uap;
3522729Sdfr{
3532729Sdfr	int msqid = uap->msqid;
3542729Sdfr	int cmd = uap->cmd;
3552729Sdfr	struct msqid_ds msqbuf;
356140839Ssobomax	int error;
357140839Ssobomax
358165403Sjkim	DPRINTF(("call to msgctl(%d, %d, %p)\n", msqid, cmd, uap->buf));
359140839Ssobomax	if (cmd == IPC_SET &&
360140839Ssobomax	    (error = copyin(uap->buf, &msqbuf, sizeof(msqbuf))) != 0)
361140839Ssobomax		return (error);
362141471Sjhb	error = kern_msgctl(td, msqid, cmd, &msqbuf);
363140839Ssobomax	if (cmd == IPC_STAT && error == 0)
364141471Sjhb		error = copyout(&msqbuf, uap->buf, sizeof(struct msqid_ds));
365140839Ssobomax	return (error);
366140839Ssobomax}
367140839Ssobomax
368140839Ssobomaxint
369141471Sjhbkern_msgctl(td, msqid, cmd, msqbuf)
370140839Ssobomax	struct thread *td;
371140839Ssobomax	int msqid;
372140839Ssobomax	int cmd;
373140839Ssobomax	struct msqid_ds *msqbuf;
374140839Ssobomax{
375140839Ssobomax	int rval, error, msqix;
376137613Srwatson	register struct msqid_kernel *msqkptr;
3772729Sdfr
378192895Sjamie	if (!prison_allow(td->td_ucred, PR_ALLOW_SYSVIPC))
37991703Sjhb		return (ENOSYS);
38091703Sjhb
381140839Ssobomax	msqix = IPCID_TO_IX(msqid);
3822729Sdfr
383140839Ssobomax	if (msqix < 0 || msqix >= msginfo.msgmni) {
384140839Ssobomax		DPRINTF(("msqid (%d) out of range (0<=msqid<%d)\n", msqix,
385100523Salfred		    msginfo.msgmni));
386101772Salfred		return (EINVAL);
3872729Sdfr	}
3882729Sdfr
389140839Ssobomax	msqkptr = &msqids[msqix];
3902729Sdfr
391101772Salfred	mtx_lock(&msq_mtx);
392137613Srwatson	if (msqkptr->u.msg_qbytes == 0) {
393100523Salfred		DPRINTF(("no such msqid\n"));
39482607Sdillon		error = EINVAL;
39582607Sdillon		goto done2;
3962729Sdfr	}
397140839Ssobomax	if (msqkptr->u.msg_perm.seq != IPCID_TO_SEQ(msqid)) {
398100523Salfred		DPRINTF(("wrong sequence number\n"));
39982607Sdillon		error = EINVAL;
40082607Sdillon		goto done2;
4012729Sdfr	}
402140614Srwatson#ifdef MAC
403172930Srwatson	error = mac_sysvmsq_check_msqctl(td->td_ucred, msqkptr, cmd);
404162468Srwatson	if (error != 0)
405140614Srwatson		goto done2;
406140614Srwatson#endif
4072729Sdfr
40882607Sdillon	error = 0;
4092729Sdfr	rval = 0;
4102729Sdfr
4112729Sdfr	switch (cmd) {
4122729Sdfr
4132729Sdfr	case IPC_RMID:
4142729Sdfr	{
4152729Sdfr		struct msg *msghdr;
416137613Srwatson		if ((error = ipcperm(td, &msqkptr->u.msg_perm, IPC_M)))
41782607Sdillon			goto done2;
418137613Srwatson
419140614Srwatson#ifdef MAC
420140614Srwatson		/*
421140614Srwatson		 * Check that the thread has MAC access permissions to
422140614Srwatson		 * individual msghdrs.  Note: We need to do this in a
423140614Srwatson		 * separate loop because the actual loop alters the
424140614Srwatson		 * msq/msghdr info as it progresses, and there is no going
425140614Srwatson		 * back if half the way through we discover that the
426140614Srwatson		 * thread cannot free a certain msghdr.  The msq will get
427140614Srwatson		 * into an inconsistent state.
428140614Srwatson		 */
429140614Srwatson		for (msghdr = msqkptr->u.msg_first; msghdr != NULL;
430140614Srwatson		    msghdr = msghdr->msg_next) {
431172930Srwatson			error = mac_sysvmsq_check_msgrmid(td->td_ucred, msghdr);
432162468Srwatson			if (error != 0)
433140614Srwatson				goto done2;
434140614Srwatson		}
435140614Srwatson#endif
436140614Srwatson
4372729Sdfr		/* Free the message headers */
438137613Srwatson		msghdr = msqkptr->u.msg_first;
4392729Sdfr		while (msghdr != NULL) {
4402729Sdfr			struct msg *msghdr_tmp;
4412729Sdfr
4422729Sdfr			/* Free the segments of each message */
443137613Srwatson			msqkptr->u.msg_cbytes -= msghdr->msg_ts;
444137613Srwatson			msqkptr->u.msg_qnum--;
4452729Sdfr			msghdr_tmp = msghdr;
4462729Sdfr			msghdr = msghdr->msg_next;
4472729Sdfr			msg_freehdr(msghdr_tmp);
4482729Sdfr		}
4492729Sdfr
450137613Srwatson		if (msqkptr->u.msg_cbytes != 0)
4512729Sdfr			panic("msg_cbytes is screwed up");
452137613Srwatson		if (msqkptr->u.msg_qnum != 0)
4532729Sdfr			panic("msg_qnum is screwed up");
4542729Sdfr
455137613Srwatson		msqkptr->u.msg_qbytes = 0;	/* Mark it as free */
4562729Sdfr
457140614Srwatson#ifdef MAC
458172930Srwatson		mac_sysvmsq_cleanup(msqkptr);
459140614Srwatson#endif
460140614Srwatson
461137613Srwatson		wakeup(msqkptr);
4622729Sdfr	}
4632729Sdfr
4642729Sdfr		break;
4652729Sdfr
4662729Sdfr	case IPC_SET:
467137613Srwatson		if ((error = ipcperm(td, &msqkptr->u.msg_perm, IPC_M)))
46882607Sdillon			goto done2;
469140839Ssobomax		if (msqbuf->msg_qbytes > msqkptr->u.msg_qbytes) {
470170587Srwatson			error = priv_check(td, PRIV_IPC_MSGSIZE);
47182607Sdillon			if (error)
47282607Sdillon				goto done2;
47343426Sphk		}
474140839Ssobomax		if (msqbuf->msg_qbytes > msginfo.msgmnb) {
475100523Salfred			DPRINTF(("can't increase msg_qbytes beyond %d"
476100523Salfred			    "(truncating)\n", msginfo.msgmnb));
477140839Ssobomax			msqbuf->msg_qbytes = msginfo.msgmnb;	/* silently restrict qbytes to system limit */
4782729Sdfr		}
479140839Ssobomax		if (msqbuf->msg_qbytes == 0) {
480100523Salfred			DPRINTF(("can't reduce msg_qbytes to 0\n"));
48182607Sdillon			error = EINVAL;		/* non-standard errno! */
48282607Sdillon			goto done2;
4832729Sdfr		}
484140839Ssobomax		msqkptr->u.msg_perm.uid = msqbuf->msg_perm.uid;	/* change the owner */
485140839Ssobomax		msqkptr->u.msg_perm.gid = msqbuf->msg_perm.gid;	/* change the owner */
486137613Srwatson		msqkptr->u.msg_perm.mode = (msqkptr->u.msg_perm.mode & ~0777) |
487140839Ssobomax		    (msqbuf->msg_perm.mode & 0777);
488140839Ssobomax		msqkptr->u.msg_qbytes = msqbuf->msg_qbytes;
489137613Srwatson		msqkptr->u.msg_ctime = time_second;
4902729Sdfr		break;
4912729Sdfr
4922729Sdfr	case IPC_STAT:
493137613Srwatson		if ((error = ipcperm(td, &msqkptr->u.msg_perm, IPC_R))) {
494100523Salfred			DPRINTF(("requester doesn't have read access\n"));
49582607Sdillon			goto done2;
4962729Sdfr		}
497141471Sjhb		*msqbuf = msqkptr->u;
4982729Sdfr		break;
4992729Sdfr
5002729Sdfr	default:
501100523Salfred		DPRINTF(("invalid command %d\n", cmd));
50282607Sdillon		error = EINVAL;
50382607Sdillon		goto done2;
5042729Sdfr	}
5052729Sdfr
50682607Sdillon	if (error == 0)
50783366Sjulian		td->td_retval[0] = rval;
50882607Sdillondone2:
509101772Salfred	mtx_unlock(&msq_mtx);
510141471Sjhb	return (error);
5112729Sdfr}
5122729Sdfr
51312866Speter#ifndef _SYS_SYSPROTO_H_
5142729Sdfrstruct msgget_args {
5152729Sdfr	key_t	key;
5162729Sdfr	int	msgflg;
5172729Sdfr};
51812866Speter#endif
51912866Speterint
52083366Sjulianmsgget(td, uap)
52183366Sjulian	struct thread *td;
5222729Sdfr	register struct msgget_args *uap;
5232729Sdfr{
52482607Sdillon	int msqid, error = 0;
5252729Sdfr	int key = uap->key;
5262729Sdfr	int msgflg = uap->msgflg;
52791703Sjhb	struct ucred *cred = td->td_ucred;
528137613Srwatson	register struct msqid_kernel *msqkptr = NULL;
5292729Sdfr
530100523Salfred	DPRINTF(("msgget(0x%x, 0%o)\n", key, msgflg));
5312729Sdfr
532192895Sjamie	if (!prison_allow(td->td_ucred, PR_ALLOW_SYSVIPC))
53391703Sjhb		return (ENOSYS);
53491703Sjhb
535101772Salfred	mtx_lock(&msq_mtx);
5362729Sdfr	if (key != IPC_PRIVATE) {
5372729Sdfr		for (msqid = 0; msqid < msginfo.msgmni; msqid++) {
538137613Srwatson			msqkptr = &msqids[msqid];
539137613Srwatson			if (msqkptr->u.msg_qbytes != 0 &&
540137613Srwatson			    msqkptr->u.msg_perm.key == key)
5412729Sdfr				break;
5422729Sdfr		}
5432729Sdfr		if (msqid < msginfo.msgmni) {
544100523Salfred			DPRINTF(("found public key\n"));
5452729Sdfr			if ((msgflg & IPC_CREAT) && (msgflg & IPC_EXCL)) {
546100523Salfred				DPRINTF(("not exclusive\n"));
54782607Sdillon				error = EEXIST;
54882607Sdillon				goto done2;
5492729Sdfr			}
550137613Srwatson			if ((error = ipcperm(td, &msqkptr->u.msg_perm,
551137613Srwatson			    msgflg & 0700))) {
552100523Salfred				DPRINTF(("requester doesn't have 0%o access\n",
553100523Salfred				    msgflg & 0700));
55482607Sdillon				goto done2;
5552729Sdfr			}
556140614Srwatson#ifdef MAC
557172930Srwatson			error = mac_sysvmsq_check_msqget(cred, msqkptr);
558162468Srwatson			if (error != 0)
559140614Srwatson				goto done2;
560140614Srwatson#endif
5612729Sdfr			goto found;
5622729Sdfr		}
5632729Sdfr	}
5642729Sdfr
565100523Salfred	DPRINTF(("need to allocate the msqid_ds\n"));
5662729Sdfr	if (key == IPC_PRIVATE || (msgflg & IPC_CREAT)) {
5672729Sdfr		for (msqid = 0; msqid < msginfo.msgmni; msqid++) {
5682729Sdfr			/*
5692729Sdfr			 * Look for an unallocated and unlocked msqid_ds.
5702729Sdfr			 * msqid_ds's can be locked by msgsnd or msgrcv while
5712729Sdfr			 * they are copying the message in/out.  We can't
5722729Sdfr			 * re-use the entry until they release it.
5732729Sdfr			 */
574137613Srwatson			msqkptr = &msqids[msqid];
575137613Srwatson			if (msqkptr->u.msg_qbytes == 0 &&
576137613Srwatson			    (msqkptr->u.msg_perm.mode & MSG_LOCKED) == 0)
5772729Sdfr				break;
5782729Sdfr		}
5792729Sdfr		if (msqid == msginfo.msgmni) {
580100523Salfred			DPRINTF(("no more msqid_ds's available\n"));
58182607Sdillon			error = ENOSPC;
58282607Sdillon			goto done2;
5832729Sdfr		}
584100523Salfred		DPRINTF(("msqid %d is available\n", msqid));
585137613Srwatson		msqkptr->u.msg_perm.key = key;
586137613Srwatson		msqkptr->u.msg_perm.cuid = cred->cr_uid;
587137613Srwatson		msqkptr->u.msg_perm.uid = cred->cr_uid;
588137613Srwatson		msqkptr->u.msg_perm.cgid = cred->cr_gid;
589137613Srwatson		msqkptr->u.msg_perm.gid = cred->cr_gid;
590137613Srwatson		msqkptr->u.msg_perm.mode = (msgflg & 0777);
5912729Sdfr		/* Make sure that the returned msqid is unique */
592137613Srwatson		msqkptr->u.msg_perm.seq = (msqkptr->u.msg_perm.seq + 1) & 0x7fff;
593137613Srwatson		msqkptr->u.msg_first = NULL;
594137613Srwatson		msqkptr->u.msg_last = NULL;
595137613Srwatson		msqkptr->u.msg_cbytes = 0;
596137613Srwatson		msqkptr->u.msg_qnum = 0;
597137613Srwatson		msqkptr->u.msg_qbytes = msginfo.msgmnb;
598137613Srwatson		msqkptr->u.msg_lspid = 0;
599137613Srwatson		msqkptr->u.msg_lrpid = 0;
600137613Srwatson		msqkptr->u.msg_stime = 0;
601137613Srwatson		msqkptr->u.msg_rtime = 0;
602137613Srwatson		msqkptr->u.msg_ctime = time_second;
603140614Srwatson#ifdef MAC
604172930Srwatson		mac_sysvmsq_create(cred, msqkptr);
605140614Srwatson#endif
6062729Sdfr	} else {
607100523Salfred		DPRINTF(("didn't find it and wasn't asked to create it\n"));
60882607Sdillon		error = ENOENT;
60982607Sdillon		goto done2;
6102729Sdfr	}
6112729Sdfr
6122729Sdfrfound:
6132729Sdfr	/* Construct the unique msqid */
614137613Srwatson	td->td_retval[0] = IXSEQ_TO_IPCID(msqid, msqkptr->u.msg_perm);
61582607Sdillondone2:
616101772Salfred	mtx_unlock(&msq_mtx);
61782607Sdillon	return (error);
6182729Sdfr}
6192729Sdfr
62012866Speter#ifndef _SYS_SYSPROTO_H_
6212729Sdfrstruct msgsnd_args {
6222729Sdfr	int	msqid;
623109895Salfred	const void	*msgp;
6242729Sdfr	size_t	msgsz;
6252729Sdfr	int	msgflg;
6262729Sdfr};
62712866Speter#endif
62812866Speterint
629165403Sjkimkern_msgsnd(td, msqid, msgp, msgsz, msgflg, mtype)
63083366Sjulian	struct thread *td;
631165403Sjkim	int msqid;
632165403Sjkim	const void *msgp;	/* XXX msgp is actually mtext. */
633165403Sjkim	size_t msgsz;
634165403Sjkim	int msgflg;
635165403Sjkim	long mtype;
6362729Sdfr{
637165403Sjkim	int msqix, segs_needed, error = 0;
638137613Srwatson	register struct msqid_kernel *msqkptr;
6392729Sdfr	register struct msg *msghdr;
6402729Sdfr	short next;
6412729Sdfr
642192895Sjamie	if (!prison_allow(td->td_ucred, PR_ALLOW_SYSVIPC))
64391703Sjhb		return (ENOSYS);
64491703Sjhb
645101772Salfred	mtx_lock(&msq_mtx);
646165403Sjkim	msqix = IPCID_TO_IX(msqid);
6472729Sdfr
648165403Sjkim	if (msqix < 0 || msqix >= msginfo.msgmni) {
649165403Sjkim		DPRINTF(("msqid (%d) out of range (0<=msqid<%d)\n", msqix,
650100523Salfred		    msginfo.msgmni));
65182607Sdillon		error = EINVAL;
65282607Sdillon		goto done2;
6532729Sdfr	}
6542729Sdfr
655165403Sjkim	msqkptr = &msqids[msqix];
656137613Srwatson	if (msqkptr->u.msg_qbytes == 0) {
657100523Salfred		DPRINTF(("no such message queue id\n"));
65882607Sdillon		error = EINVAL;
65982607Sdillon		goto done2;
6602729Sdfr	}
661165403Sjkim	if (msqkptr->u.msg_perm.seq != IPCID_TO_SEQ(msqid)) {
662100523Salfred		DPRINTF(("wrong sequence number\n"));
66382607Sdillon		error = EINVAL;
66482607Sdillon		goto done2;
6652729Sdfr	}
6662729Sdfr
667137613Srwatson	if ((error = ipcperm(td, &msqkptr->u.msg_perm, IPC_W))) {
668100523Salfred		DPRINTF(("requester doesn't have write access\n"));
66982607Sdillon		goto done2;
6702729Sdfr	}
6712729Sdfr
672140614Srwatson#ifdef MAC
673172930Srwatson	error = mac_sysvmsq_check_msqsnd(td->td_ucred, msqkptr);
674162468Srwatson	if (error != 0)
675140614Srwatson		goto done2;
676140614Srwatson#endif
677140614Srwatson
6782729Sdfr	segs_needed = (msgsz + msginfo.msgssz - 1) / msginfo.msgssz;
679165403Sjkim	DPRINTF(("msgsz=%zu, msgssz=%d, segs_needed=%d\n", msgsz,
680165403Sjkim	    msginfo.msgssz, segs_needed));
6812729Sdfr	for (;;) {
6822729Sdfr		int need_more_resources = 0;
6832729Sdfr
6842729Sdfr		/*
6852729Sdfr		 * check msgsz
6862729Sdfr		 * (inside this loop in case msg_qbytes changes while we sleep)
6872729Sdfr		 */
6882729Sdfr
689137613Srwatson		if (msgsz > msqkptr->u.msg_qbytes) {
690137613Srwatson			DPRINTF(("msgsz > msqkptr->u.msg_qbytes\n"));
69182607Sdillon			error = EINVAL;
69282607Sdillon			goto done2;
6932729Sdfr		}
6942729Sdfr
695137613Srwatson		if (msqkptr->u.msg_perm.mode & MSG_LOCKED) {
696100523Salfred			DPRINTF(("msqid is locked\n"));
6972729Sdfr			need_more_resources = 1;
6982729Sdfr		}
699137613Srwatson		if (msgsz + msqkptr->u.msg_cbytes > msqkptr->u.msg_qbytes) {
700100523Salfred			DPRINTF(("msgsz + msg_cbytes > msg_qbytes\n"));
7012729Sdfr			need_more_resources = 1;
7022729Sdfr		}
7032729Sdfr		if (segs_needed > nfree_msgmaps) {
704100523Salfred			DPRINTF(("segs_needed > nfree_msgmaps\n"));
7052729Sdfr			need_more_resources = 1;
7062729Sdfr		}
7072729Sdfr		if (free_msghdrs == NULL) {
708100523Salfred			DPRINTF(("no more msghdrs\n"));
7092729Sdfr			need_more_resources = 1;
7102729Sdfr		}
7112729Sdfr
7122729Sdfr		if (need_more_resources) {
7132729Sdfr			int we_own_it;
7142729Sdfr
7152729Sdfr			if ((msgflg & IPC_NOWAIT) != 0) {
716100523Salfred				DPRINTF(("need more resources but caller "
717100523Salfred				    "doesn't want to wait\n"));
71882607Sdillon				error = EAGAIN;
71982607Sdillon				goto done2;
7202729Sdfr			}
7212729Sdfr
722137613Srwatson			if ((msqkptr->u.msg_perm.mode & MSG_LOCKED) != 0) {
723100523Salfred				DPRINTF(("we don't own the msqid_ds\n"));
7242729Sdfr				we_own_it = 0;
7252729Sdfr			} else {
7262729Sdfr				/* Force later arrivals to wait for our
7272729Sdfr				   request */
728100523Salfred				DPRINTF(("we own the msqid_ds\n"));
729137613Srwatson				msqkptr->u.msg_perm.mode |= MSG_LOCKED;
7302729Sdfr				we_own_it = 1;
7312729Sdfr			}
732164368Sjkim			DPRINTF(("msgsnd:  goodnight\n"));
733137613Srwatson			error = msleep(msqkptr, &msq_mtx, (PZERO - 4) | PCATCH,
734164368Sjkim			    "msgsnd", hz);
735164368Sjkim			DPRINTF(("msgsnd:  good morning, error=%d\n", error));
7362729Sdfr			if (we_own_it)
737137613Srwatson				msqkptr->u.msg_perm.mode &= ~MSG_LOCKED;
738164368Sjkim			if (error == EWOULDBLOCK) {
739164368Sjkim				DPRINTF(("msgsnd:  timed out\n"));
740164368Sjkim				continue;
741164368Sjkim			}
74282607Sdillon			if (error != 0) {
743100523Salfred				DPRINTF(("msgsnd:  interrupted system call\n"));
74482607Sdillon				error = EINTR;
74582607Sdillon				goto done2;
7462729Sdfr			}
7472729Sdfr
7482729Sdfr			/*
7492729Sdfr			 * Make sure that the msq queue still exists
7502729Sdfr			 */
7512729Sdfr
752137613Srwatson			if (msqkptr->u.msg_qbytes == 0) {
753100523Salfred				DPRINTF(("msqid deleted\n"));
75482607Sdillon				error = EIDRM;
75582607Sdillon				goto done2;
7562729Sdfr			}
7572729Sdfr
7582729Sdfr		} else {
759100523Salfred			DPRINTF(("got all the resources that we need\n"));
7602729Sdfr			break;
7612729Sdfr		}
7622729Sdfr	}
7632729Sdfr
7642729Sdfr	/*
7652729Sdfr	 * We have the resources that we need.
7662729Sdfr	 * Make sure!
7672729Sdfr	 */
7682729Sdfr
769137613Srwatson	if (msqkptr->u.msg_perm.mode & MSG_LOCKED)
7702729Sdfr		panic("msg_perm.mode & MSG_LOCKED");
7712729Sdfr	if (segs_needed > nfree_msgmaps)
7722729Sdfr		panic("segs_needed > nfree_msgmaps");
773137613Srwatson	if (msgsz + msqkptr->u.msg_cbytes > msqkptr->u.msg_qbytes)
7742729Sdfr		panic("msgsz + msg_cbytes > msg_qbytes");
7752729Sdfr	if (free_msghdrs == NULL)
7762729Sdfr		panic("no more msghdrs");
7772729Sdfr
7782729Sdfr	/*
7792729Sdfr	 * Re-lock the msqid_ds in case we page-fault when copying in the
7802729Sdfr	 * message
7812729Sdfr	 */
7822729Sdfr
783137613Srwatson	if ((msqkptr->u.msg_perm.mode & MSG_LOCKED) != 0)
7842729Sdfr		panic("msqid_ds is already locked");
785137613Srwatson	msqkptr->u.msg_perm.mode |= MSG_LOCKED;
7862729Sdfr
7872729Sdfr	/*
7882729Sdfr	 * Allocate a message header
7892729Sdfr	 */
7902729Sdfr
7912729Sdfr	msghdr = free_msghdrs;
7922729Sdfr	free_msghdrs = msghdr->msg_next;
7932729Sdfr	msghdr->msg_spot = -1;
7942729Sdfr	msghdr->msg_ts = msgsz;
795165403Sjkim	msghdr->msg_type = mtype;
796140614Srwatson#ifdef MAC
797140614Srwatson	/*
798172930Srwatson	 * XXXMAC: Should the mac_sysvmsq_check_msgmsq check follow here
799140614Srwatson	 * immediately?  Or, should it be checked just before the msg is
800140614Srwatson	 * enqueued in the msgq (as it is done now)?
801140614Srwatson	 */
802172930Srwatson	mac_sysvmsg_create(td->td_ucred, msqkptr, msghdr);
803140614Srwatson#endif
8042729Sdfr
8052729Sdfr	/*
8062729Sdfr	 * Allocate space for the message
8072729Sdfr	 */
8082729Sdfr
8092729Sdfr	while (segs_needed > 0) {
8102729Sdfr		if (nfree_msgmaps <= 0)
8112729Sdfr			panic("not enough msgmaps");
8122729Sdfr		if (free_msgmaps == -1)
8132729Sdfr			panic("nil free_msgmaps");
8142729Sdfr		next = free_msgmaps;
8152729Sdfr		if (next <= -1)
8162729Sdfr			panic("next too low #1");
8172729Sdfr		if (next >= msginfo.msgseg)
8182729Sdfr			panic("next out of range #1");
819100523Salfred		DPRINTF(("allocating segment %d to message\n", next));
8202729Sdfr		free_msgmaps = msgmaps[next].next;
8212729Sdfr		nfree_msgmaps--;
8222729Sdfr		msgmaps[next].next = msghdr->msg_spot;
8232729Sdfr		msghdr->msg_spot = next;
8242729Sdfr		segs_needed--;
8252729Sdfr	}
8262729Sdfr
8272729Sdfr	/*
8282729Sdfr	 * Validate the message type
8292729Sdfr	 */
8302729Sdfr
8312729Sdfr	if (msghdr->msg_type < 1) {
8322729Sdfr		msg_freehdr(msghdr);
833137613Srwatson		msqkptr->u.msg_perm.mode &= ~MSG_LOCKED;
834137613Srwatson		wakeup(msqkptr);
835165403Sjkim		DPRINTF(("mtype (%ld) < 1\n", msghdr->msg_type));
83682607Sdillon		error = EINVAL;
83782607Sdillon		goto done2;
8382729Sdfr	}
8392729Sdfr
8402729Sdfr	/*
8412729Sdfr	 * Copy in the message body
8422729Sdfr	 */
8432729Sdfr
8442729Sdfr	next = msghdr->msg_spot;
8452729Sdfr	while (msgsz > 0) {
8462729Sdfr		size_t tlen;
8472729Sdfr		if (msgsz > msginfo.msgssz)
8482729Sdfr			tlen = msginfo.msgssz;
8492729Sdfr		else
8502729Sdfr			tlen = msgsz;
8512729Sdfr		if (next <= -1)
8522729Sdfr			panic("next too low #2");
8532729Sdfr		if (next >= msginfo.msgseg)
8542729Sdfr			panic("next out of range #2");
855101772Salfred		mtx_unlock(&msq_mtx);
856165403Sjkim		if ((error = copyin(msgp, &msgpool[next * msginfo.msgssz],
8572729Sdfr		    tlen)) != 0) {
858101772Salfred			mtx_lock(&msq_mtx);
859100523Salfred			DPRINTF(("error %d copying in message segment\n",
860100523Salfred			    error));
8612729Sdfr			msg_freehdr(msghdr);
862137613Srwatson			msqkptr->u.msg_perm.mode &= ~MSG_LOCKED;
863137613Srwatson			wakeup(msqkptr);
86482607Sdillon			goto done2;
8652729Sdfr		}
866101772Salfred		mtx_lock(&msq_mtx);
8672729Sdfr		msgsz -= tlen;
868165403Sjkim		msgp = (const char *)msgp + tlen;
8692729Sdfr		next = msgmaps[next].next;
8702729Sdfr	}
8712729Sdfr	if (next != -1)
8722729Sdfr		panic("didn't use all the msg segments");
8732729Sdfr
8742729Sdfr	/*
8752729Sdfr	 * We've got the message.  Unlock the msqid_ds.
8762729Sdfr	 */
8772729Sdfr
878137613Srwatson	msqkptr->u.msg_perm.mode &= ~MSG_LOCKED;
8792729Sdfr
8802729Sdfr	/*
8812729Sdfr	 * Make sure that the msqid_ds is still allocated.
8822729Sdfr	 */
8832729Sdfr
884137613Srwatson	if (msqkptr->u.msg_qbytes == 0) {
8852729Sdfr		msg_freehdr(msghdr);
886137613Srwatson		wakeup(msqkptr);
88782607Sdillon		error = EIDRM;
88882607Sdillon		goto done2;
8892729Sdfr	}
8902729Sdfr
891140614Srwatson#ifdef MAC
8922729Sdfr	/*
893140614Srwatson	 * Note: Since the task/thread allocates the msghdr and usually
894140614Srwatson	 * primes it with its own MAC label, for a majority of policies, it
895140614Srwatson	 * won't be necessary to check whether the msghdr has access
896172930Srwatson	 * permissions to the msgq.  The mac_sysvmsq_check_msqsnd check would
897140614Srwatson	 * suffice in that case.  However, this hook may be required where
898140614Srwatson	 * individual policies derive a non-identical label for the msghdr
899140614Srwatson	 * from the current thread label and may want to check the msghdr
900140614Srwatson	 * enqueue permissions, along with read/write permissions to the
901140614Srwatson	 * msgq.
902140614Srwatson	 */
903172930Srwatson	error = mac_sysvmsq_check_msgmsq(td->td_ucred, msghdr, msqkptr);
904140614Srwatson	if (error != 0) {
905140614Srwatson		msg_freehdr(msghdr);
906140614Srwatson		wakeup(msqkptr);
907140614Srwatson		goto done2;
908140614Srwatson	}
909140614Srwatson#endif
910140614Srwatson
911140614Srwatson	/*
9122729Sdfr	 * Put the message into the queue
9132729Sdfr	 */
914137613Srwatson	if (msqkptr->u.msg_first == NULL) {
915137613Srwatson		msqkptr->u.msg_first = msghdr;
916137613Srwatson		msqkptr->u.msg_last = msghdr;
9172729Sdfr	} else {
918137613Srwatson		msqkptr->u.msg_last->msg_next = msghdr;
919137613Srwatson		msqkptr->u.msg_last = msghdr;
9202729Sdfr	}
921137613Srwatson	msqkptr->u.msg_last->msg_next = NULL;
9222729Sdfr
923137613Srwatson	msqkptr->u.msg_cbytes += msghdr->msg_ts;
924137613Srwatson	msqkptr->u.msg_qnum++;
925137613Srwatson	msqkptr->u.msg_lspid = td->td_proc->p_pid;
926137613Srwatson	msqkptr->u.msg_stime = time_second;
9272729Sdfr
928137613Srwatson	wakeup(msqkptr);
92983366Sjulian	td->td_retval[0] = 0;
93082607Sdillondone2:
931101772Salfred	mtx_unlock(&msq_mtx);
93282607Sdillon	return (error);
9332729Sdfr}
9342729Sdfr
935165403Sjkimint
936165403Sjkimmsgsnd(td, uap)
937165403Sjkim	struct thread *td;
938165403Sjkim	register struct msgsnd_args *uap;
939165403Sjkim{
940165403Sjkim	int error;
941165403Sjkim	long mtype;
942165403Sjkim
943165403Sjkim	DPRINTF(("call to msgsnd(%d, %p, %zu, %d)\n", uap->msqid, uap->msgp,
944165403Sjkim	    uap->msgsz, uap->msgflg));
945165403Sjkim
946165403Sjkim	if ((error = copyin(uap->msgp, &mtype, sizeof(mtype))) != 0) {
947165403Sjkim		DPRINTF(("error %d copying the message type\n", error));
948165403Sjkim		return (error);
949165403Sjkim	}
950165403Sjkim	return (kern_msgsnd(td, uap->msqid,
951165403Sjkim	    (const char *)uap->msgp + sizeof(mtype),
952165403Sjkim	    uap->msgsz, uap->msgflg, mtype));
953165403Sjkim}
954165403Sjkim
95512866Speter#ifndef _SYS_SYSPROTO_H_
9562729Sdfrstruct msgrcv_args {
9572729Sdfr	int	msqid;
9582729Sdfr	void	*msgp;
9592729Sdfr	size_t	msgsz;
9602729Sdfr	long	msgtyp;
9612729Sdfr	int	msgflg;
9622729Sdfr};
96312866Speter#endif
96412866Speterint
965165403Sjkimkern_msgrcv(td, msqid, msgp, msgsz, msgtyp, msgflg, mtype)
96683366Sjulian	struct thread *td;
967165403Sjkim	int msqid;
968165403Sjkim	void *msgp;	/* XXX msgp is actually mtext. */
969165403Sjkim	size_t msgsz;
970165403Sjkim	long msgtyp;
971165403Sjkim	int msgflg;
972165403Sjkim	long *mtype;
9732729Sdfr{
9742729Sdfr	size_t len;
975137613Srwatson	register struct msqid_kernel *msqkptr;
9762729Sdfr	register struct msg *msghdr;
977165403Sjkim	int msqix, error = 0;
9782729Sdfr	short next;
9792729Sdfr
980192895Sjamie	if (!prison_allow(td->td_ucred, PR_ALLOW_SYSVIPC))
98191703Sjhb		return (ENOSYS);
98291703Sjhb
983165403Sjkim	msqix = IPCID_TO_IX(msqid);
9842729Sdfr
985165403Sjkim	if (msqix < 0 || msqix >= msginfo.msgmni) {
986165403Sjkim		DPRINTF(("msqid (%d) out of range (0<=msqid<%d)\n", msqix,
987100523Salfred		    msginfo.msgmni));
988101772Salfred		return (EINVAL);
9892729Sdfr	}
9902729Sdfr
991165403Sjkim	msqkptr = &msqids[msqix];
992101772Salfred	mtx_lock(&msq_mtx);
993137613Srwatson	if (msqkptr->u.msg_qbytes == 0) {
994100523Salfred		DPRINTF(("no such message queue id\n"));
99582607Sdillon		error = EINVAL;
99682607Sdillon		goto done2;
9972729Sdfr	}
998165403Sjkim	if (msqkptr->u.msg_perm.seq != IPCID_TO_SEQ(msqid)) {
999100523Salfred		DPRINTF(("wrong sequence number\n"));
100082607Sdillon		error = EINVAL;
100182607Sdillon		goto done2;
10022729Sdfr	}
10032729Sdfr
1004137613Srwatson	if ((error = ipcperm(td, &msqkptr->u.msg_perm, IPC_R))) {
1005100523Salfred		DPRINTF(("requester doesn't have read access\n"));
100682607Sdillon		goto done2;
10072729Sdfr	}
10082729Sdfr
1009140614Srwatson#ifdef MAC
1010172930Srwatson	error = mac_sysvmsq_check_msqrcv(td->td_ucred, msqkptr);
1011162468Srwatson	if (error != 0)
1012140614Srwatson		goto done2;
1013140614Srwatson#endif
1014140614Srwatson
10152729Sdfr	msghdr = NULL;
10162729Sdfr	while (msghdr == NULL) {
10172729Sdfr		if (msgtyp == 0) {
1018137613Srwatson			msghdr = msqkptr->u.msg_first;
10192729Sdfr			if (msghdr != NULL) {
10202729Sdfr				if (msgsz < msghdr->msg_ts &&
10212729Sdfr				    (msgflg & MSG_NOERROR) == 0) {
1022100523Salfred					DPRINTF(("first message on the queue "
1023165403Sjkim					    "is too big (want %zu, got %d)\n",
1024100523Salfred					    msgsz, msghdr->msg_ts));
102582607Sdillon					error = E2BIG;
102682607Sdillon					goto done2;
10272729Sdfr				}
1028140614Srwatson#ifdef MAC
1029172930Srwatson				error = mac_sysvmsq_check_msgrcv(td->td_ucred,
1030140614Srwatson				    msghdr);
1031162468Srwatson				if (error != 0)
1032140614Srwatson					goto done2;
1033140614Srwatson#endif
1034137613Srwatson				if (msqkptr->u.msg_first == msqkptr->u.msg_last) {
1035137613Srwatson					msqkptr->u.msg_first = NULL;
1036137613Srwatson					msqkptr->u.msg_last = NULL;
10372729Sdfr				} else {
1038137613Srwatson					msqkptr->u.msg_first = msghdr->msg_next;
1039137613Srwatson					if (msqkptr->u.msg_first == NULL)
10402729Sdfr						panic("msg_first/last screwed up #1");
10412729Sdfr				}
10422729Sdfr			}
10432729Sdfr		} else {
10442729Sdfr			struct msg *previous;
10452729Sdfr			struct msg **prev;
10462729Sdfr
10472729Sdfr			previous = NULL;
1048137613Srwatson			prev = &(msqkptr->u.msg_first);
10492729Sdfr			while ((msghdr = *prev) != NULL) {
10502729Sdfr				/*
10512729Sdfr				 * Is this message's type an exact match or is
10522729Sdfr				 * this message's type less than or equal to
10532729Sdfr				 * the absolute value of a negative msgtyp?
10542729Sdfr				 * Note that the second half of this test can
10552729Sdfr				 * NEVER be true if msgtyp is positive since
10562729Sdfr				 * msg_type is always positive!
10572729Sdfr				 */
10582729Sdfr
10592729Sdfr				if (msgtyp == msghdr->msg_type ||
10602729Sdfr				    msghdr->msg_type <= -msgtyp) {
1061165403Sjkim					DPRINTF(("found message type %ld, "
1062165403Sjkim					    "requested %ld\n",
1063100523Salfred					    msghdr->msg_type, msgtyp));
10642729Sdfr					if (msgsz < msghdr->msg_ts &&
10652729Sdfr					    (msgflg & MSG_NOERROR) == 0) {
1066100523Salfred						DPRINTF(("requested message "
1067100523Salfred						    "on the queue is too big "
1068165403Sjkim						    "(want %zu, got %hu)\n",
1069100523Salfred						    msgsz, msghdr->msg_ts));
107082607Sdillon						error = E2BIG;
107182607Sdillon						goto done2;
10722729Sdfr					}
1073140614Srwatson#ifdef MAC
1074172930Srwatson					error = mac_sysvmsq_check_msgrcv(
1075140614Srwatson					    td->td_ucred, msghdr);
1076162468Srwatson					if (error != 0)
1077140614Srwatson						goto done2;
1078140614Srwatson#endif
10792729Sdfr					*prev = msghdr->msg_next;
1080137613Srwatson					if (msghdr == msqkptr->u.msg_last) {
10812729Sdfr						if (previous == NULL) {
10822729Sdfr							if (prev !=
1083137613Srwatson							    &msqkptr->u.msg_first)
10842729Sdfr								panic("msg_first/last screwed up #2");
1085137613Srwatson							msqkptr->u.msg_first =
10862729Sdfr							    NULL;
1087137613Srwatson							msqkptr->u.msg_last =
10882729Sdfr							    NULL;
10892729Sdfr						} else {
10902729Sdfr							if (prev ==
1091137613Srwatson							    &msqkptr->u.msg_first)
10922729Sdfr								panic("msg_first/last screwed up #3");
1093137613Srwatson							msqkptr->u.msg_last =
10942729Sdfr							    previous;
10952729Sdfr						}
10962729Sdfr					}
10972729Sdfr					break;
10982729Sdfr				}
10992729Sdfr				previous = msghdr;
11002729Sdfr				prev = &(msghdr->msg_next);
11012729Sdfr			}
11022729Sdfr		}
11032729Sdfr
11042729Sdfr		/*
11052729Sdfr		 * We've either extracted the msghdr for the appropriate
11062729Sdfr		 * message or there isn't one.
11072729Sdfr		 * If there is one then bail out of this loop.
11082729Sdfr		 */
11092729Sdfr
11102729Sdfr		if (msghdr != NULL)
11112729Sdfr			break;
11122729Sdfr
11132729Sdfr		/*
11142729Sdfr		 * Hmph!  No message found.  Does the user want to wait?
11152729Sdfr		 */
11162729Sdfr
11172729Sdfr		if ((msgflg & IPC_NOWAIT) != 0) {
1118165403Sjkim			DPRINTF(("no appropriate message found (msgtyp=%ld)\n",
1119100523Salfred			    msgtyp));
11202729Sdfr			/* The SVID says to return ENOMSG. */
112182607Sdillon			error = ENOMSG;
112282607Sdillon			goto done2;
11232729Sdfr		}
11242729Sdfr
11252729Sdfr		/*
11262729Sdfr		 * Wait for something to happen
11272729Sdfr		 */
11282729Sdfr
1129100523Salfred		DPRINTF(("msgrcv:  goodnight\n"));
1130137613Srwatson		error = msleep(msqkptr, &msq_mtx, (PZERO - 4) | PCATCH,
1131164368Sjkim		    "msgrcv", 0);
1132100523Salfred		DPRINTF(("msgrcv:  good morning (error=%d)\n", error));
11332729Sdfr
113482607Sdillon		if (error != 0) {
1135164368Sjkim			DPRINTF(("msgrcv:  interrupted system call\n"));
113682607Sdillon			error = EINTR;
113782607Sdillon			goto done2;
11382729Sdfr		}
11392729Sdfr
11402729Sdfr		/*
11412729Sdfr		 * Make sure that the msq queue still exists
11422729Sdfr		 */
11432729Sdfr
1144137613Srwatson		if (msqkptr->u.msg_qbytes == 0 ||
1145165403Sjkim		    msqkptr->u.msg_perm.seq != IPCID_TO_SEQ(msqid)) {
1146100523Salfred			DPRINTF(("msqid deleted\n"));
114782607Sdillon			error = EIDRM;
114882607Sdillon			goto done2;
11492729Sdfr		}
11502729Sdfr	}
11512729Sdfr
11522729Sdfr	/*
11532729Sdfr	 * Return the message to the user.
11542729Sdfr	 *
11552729Sdfr	 * First, do the bookkeeping (before we risk being interrupted).
11562729Sdfr	 */
11572729Sdfr
1158137613Srwatson	msqkptr->u.msg_cbytes -= msghdr->msg_ts;
1159137613Srwatson	msqkptr->u.msg_qnum--;
1160137613Srwatson	msqkptr->u.msg_lrpid = td->td_proc->p_pid;
1161137613Srwatson	msqkptr->u.msg_rtime = time_second;
11622729Sdfr
11632729Sdfr	/*
11642729Sdfr	 * Make msgsz the actual amount that we'll be returning.
11652729Sdfr	 * Note that this effectively truncates the message if it is too long
11662729Sdfr	 * (since msgsz is never increased).
11672729Sdfr	 */
11682729Sdfr
1169165403Sjkim	DPRINTF(("found a message, msgsz=%zu, msg_ts=%hu\n", msgsz,
1170100523Salfred	    msghdr->msg_ts));
11712729Sdfr	if (msgsz > msghdr->msg_ts)
11722729Sdfr		msgsz = msghdr->msg_ts;
1173165403Sjkim	*mtype = msghdr->msg_type;
11742729Sdfr
11752729Sdfr	/*
11762729Sdfr	 * Return the segments to the user
11772729Sdfr	 */
11782729Sdfr
11792729Sdfr	next = msghdr->msg_spot;
11802729Sdfr	for (len = 0; len < msgsz; len += msginfo.msgssz) {
11812729Sdfr		size_t tlen;
11822729Sdfr
118345921Ssada		if (msgsz - len > msginfo.msgssz)
11842729Sdfr			tlen = msginfo.msgssz;
11852729Sdfr		else
118645921Ssada			tlen = msgsz - len;
11872729Sdfr		if (next <= -1)
11882729Sdfr			panic("next too low #3");
11892729Sdfr		if (next >= msginfo.msgseg)
11902729Sdfr			panic("next out of range #3");
1191101772Salfred		mtx_unlock(&msq_mtx);
1192165403Sjkim		error = copyout(&msgpool[next * msginfo.msgssz], msgp, tlen);
1193101772Salfred		mtx_lock(&msq_mtx);
119482607Sdillon		if (error != 0) {
1195100523Salfred			DPRINTF(("error (%d) copying out message segment\n",
1196100523Salfred			    error));
11972729Sdfr			msg_freehdr(msghdr);
1198137613Srwatson			wakeup(msqkptr);
119982607Sdillon			goto done2;
12002729Sdfr		}
1201165403Sjkim		msgp = (char *)msgp + tlen;
12022729Sdfr		next = msgmaps[next].next;
12032729Sdfr	}
12042729Sdfr
12052729Sdfr	/*
12062729Sdfr	 * Done, return the actual number of bytes copied out.
12072729Sdfr	 */
12082729Sdfr
12092729Sdfr	msg_freehdr(msghdr);
1210137613Srwatson	wakeup(msqkptr);
121183366Sjulian	td->td_retval[0] = msgsz;
121282607Sdillondone2:
1213101772Salfred	mtx_unlock(&msq_mtx);
121482607Sdillon	return (error);
12152729Sdfr}
121677461Sdd
1217165403Sjkimint
1218165403Sjkimmsgrcv(td, uap)
1219165403Sjkim	struct thread *td;
1220165403Sjkim	register struct msgrcv_args *uap;
1221165403Sjkim{
1222165403Sjkim	int error;
1223165403Sjkim	long mtype;
1224165403Sjkim
1225165403Sjkim	DPRINTF(("call to msgrcv(%d, %p, %zu, %ld, %d)\n", uap->msqid,
1226165403Sjkim	    uap->msgp, uap->msgsz, uap->msgtyp, uap->msgflg));
1227165403Sjkim
1228165403Sjkim	if ((error = kern_msgrcv(td, uap->msqid,
1229165403Sjkim	    (char *)uap->msgp + sizeof(mtype), uap->msgsz,
1230165403Sjkim	    uap->msgtyp, uap->msgflg, &mtype)) != 0)
1231165403Sjkim		return (error);
1232165403Sjkim	if ((error = copyout(&mtype, uap->msgp, sizeof(mtype))) != 0)
1233165403Sjkim		DPRINTF(("error %d copying the message type\n", error));
1234165403Sjkim	return (error);
1235165403Sjkim}
1236165403Sjkim
123777461Sddstatic int
123877461Sddsysctl_msqids(SYSCTL_HANDLER_ARGS)
123977461Sdd{
124077461Sdd
124177461Sdd	return (SYSCTL_OUT(req, msqids,
1242137613Srwatson	    sizeof(struct msqid_kernel) * msginfo.msgmni));
124377461Sdd}
124477461Sdd
1245141710ScsjpSYSCTL_INT(_kern_ipc, OID_AUTO, msgmax, CTLFLAG_RD, &msginfo.msgmax, 0,
1246141710Scsjp    "Maximum message size");
1247141710ScsjpSYSCTL_INT(_kern_ipc, OID_AUTO, msgmni, CTLFLAG_RDTUN, &msginfo.msgmni, 0,
1248141710Scsjp    "Number of message queue identifiers");
1249141710ScsjpSYSCTL_INT(_kern_ipc, OID_AUTO, msgmnb, CTLFLAG_RDTUN, &msginfo.msgmnb, 0,
1250141710Scsjp    "Maximum number of bytes in a queue");
1251141710ScsjpSYSCTL_INT(_kern_ipc, OID_AUTO, msgtql, CTLFLAG_RDTUN, &msginfo.msgtql, 0,
1252141710Scsjp    "Maximum number of messages in the system");
1253141710ScsjpSYSCTL_INT(_kern_ipc, OID_AUTO, msgssz, CTLFLAG_RDTUN, &msginfo.msgssz, 0,
1254141710Scsjp    "Size of a message segment");
1255141710ScsjpSYSCTL_INT(_kern_ipc, OID_AUTO, msgseg, CTLFLAG_RDTUN, &msginfo.msgseg, 0,
1256141710Scsjp    "Number of message segments");
125777461SddSYSCTL_PROC(_kern_ipc, OID_AUTO, msqids, CTLFLAG_RD,
125877461Sdd    NULL, 0, sysctl_msqids, "", "Message queue IDs");
1259194894Sjhb
1260194894Sjhb#if defined(COMPAT_FREEBSD4) || defined(COMPAT_FREEBSD5) || \
1261194894Sjhb    defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD7)
1262194894SjhbSYSCALL_MODULE_HELPER(msgsys);
1263194910SjhbSYSCALL_MODULE_HELPER(freebsd7_msgctl);
1264194894Sjhb
1265194894Sjhb/* XXX casting to (sy_call_t *) is bogus, as usual. */
1266194894Sjhbstatic sy_call_t *msgcalls[] = {
1267194910Sjhb	(sy_call_t *)freebsd7_msgctl, (sy_call_t *)msgget,
1268194894Sjhb	(sy_call_t *)msgsnd, (sy_call_t *)msgrcv
1269194894Sjhb};
1270194894Sjhb
1271194894Sjhb/*
1272194894Sjhb * Entry point for all MSG calls.
1273194894Sjhb */
1274194894Sjhbint
1275194894Sjhbmsgsys(td, uap)
1276194894Sjhb	struct thread *td;
1277194894Sjhb	/* XXX actually varargs. */
1278194894Sjhb	struct msgsys_args /* {
1279194894Sjhb		int	which;
1280194894Sjhb		int	a2;
1281194894Sjhb		int	a3;
1282194894Sjhb		int	a4;
1283194894Sjhb		int	a5;
1284194894Sjhb		int	a6;
1285194894Sjhb	} */ *uap;
1286194894Sjhb{
1287194894Sjhb	int error;
1288194894Sjhb
1289194894Sjhb	if (!prison_allow(td->td_ucred, PR_ALLOW_SYSVIPC))
1290194894Sjhb		return (ENOSYS);
1291194894Sjhb	if (uap->which < 0 ||
1292194894Sjhb	    uap->which >= sizeof(msgcalls)/sizeof(msgcalls[0]))
1293194894Sjhb		return (EINVAL);
1294194894Sjhb	error = (*msgcalls[uap->which])(td, &uap->a2);
1295194894Sjhb	return (error);
1296194894Sjhb}
1297194910Sjhb
1298194910Sjhb#define CP(src, dst, fld)	do { (dst).fld = (src).fld; } while (0)
1299194910Sjhb
1300194910Sjhb#ifndef _SYS_SYSPROTO_H_
1301194910Sjhbstruct freebsd7_msgctl_args {
1302194910Sjhb	int	msqid;
1303194910Sjhb	int	cmd;
1304194910Sjhb	struct	msqid_ds_old *buf;
1305194910Sjhb};
1306194910Sjhb#endif
1307194910Sjhbint
1308194910Sjhbfreebsd7_msgctl(td, uap)
1309194910Sjhb	struct thread *td;
1310194910Sjhb	struct freebsd7_msgctl_args *uap;
1311194910Sjhb{
1312194910Sjhb	struct msqid_ds_old msqold;
1313194910Sjhb	struct msqid_ds msqbuf;
1314194910Sjhb	int error;
1315194910Sjhb
1316194910Sjhb	DPRINTF(("call to freebsd7_msgctl(%d, %d, %p)\n", uap->msqid, uap->cmd,
1317194910Sjhb	    uap->buf));
1318194910Sjhb	if (uap->cmd == IPC_SET) {
1319194910Sjhb		error = copyin(uap->buf, &msqold, sizeof(msqold));
1320194910Sjhb		if (error)
1321194910Sjhb			return (error);
1322194910Sjhb		ipcperm_old2new(&msqold.msg_perm, &msqbuf.msg_perm);
1323194910Sjhb		CP(msqold, msqbuf, msg_first);
1324194910Sjhb		CP(msqold, msqbuf, msg_last);
1325194910Sjhb		CP(msqold, msqbuf, msg_cbytes);
1326194910Sjhb		CP(msqold, msqbuf, msg_qnum);
1327194910Sjhb		CP(msqold, msqbuf, msg_qbytes);
1328194910Sjhb		CP(msqold, msqbuf, msg_lspid);
1329194910Sjhb		CP(msqold, msqbuf, msg_lrpid);
1330194910Sjhb		CP(msqold, msqbuf, msg_stime);
1331194910Sjhb		CP(msqold, msqbuf, msg_rtime);
1332194910Sjhb		CP(msqold, msqbuf, msg_ctime);
1333194910Sjhb	}
1334194910Sjhb	error = kern_msgctl(td, uap->msqid, uap->cmd, &msqbuf);
1335194910Sjhb	if (error)
1336194910Sjhb		return (error);
1337194910Sjhb	if (uap->cmd == IPC_STAT) {
1338194910Sjhb		bzero(&msqold, sizeof(msqold));
1339194910Sjhb		ipcperm_new2old(&msqbuf.msg_perm, &msqold.msg_perm);
1340194910Sjhb		CP(msqbuf, msqold, msg_first);
1341194910Sjhb		CP(msqbuf, msqold, msg_last);
1342194910Sjhb		CP(msqbuf, msqold, msg_cbytes);
1343194910Sjhb		CP(msqbuf, msqold, msg_qnum);
1344194910Sjhb		CP(msqbuf, msqold, msg_qbytes);
1345194910Sjhb		CP(msqbuf, msqold, msg_lspid);
1346194910Sjhb		CP(msqbuf, msqold, msg_lrpid);
1347194910Sjhb		CP(msqbuf, msqold, msg_stime);
1348194910Sjhb		CP(msqbuf, msqold, msg_rtime);
1349194910Sjhb		CP(msqbuf, msqold, msg_ctime);
1350194910Sjhb		error = copyout(&msqold, uap->buf, sizeof(struct msqid_ds_old));
1351194910Sjhb	}
1352194910Sjhb	return (error);
1353194910Sjhb}
1354194910Sjhb
1355194910Sjhb#undef CP
1356194910Sjhb
1357194894Sjhb#endif	/* COMPAT_FREEBSD4 || COMPAT_FREEBSD5 || COMPAT_FREEBSD6 ||
1358194894Sjhb	   COMPAT_FREEBSD7 */
1359