sysv_msg.c revision 137613
12729Sdfr/*
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 */
192729Sdfr
20116182Sobrien#include <sys/cdefs.h>
21116182Sobrien__FBSDID("$FreeBSD: head/sys/kern/sysv_msg.c 137613 2004-11-12 13:23:47Z rwatson $");
22116182Sobrien
2359839Speter#include "opt_sysvipc.h"
2459839Speter
252729Sdfr#include <sys/param.h>
262729Sdfr#include <sys/systm.h>
2711626Sbde#include <sys/sysproto.h>
282729Sdfr#include <sys/kernel.h>
292729Sdfr#include <sys/proc.h>
3082607Sdillon#include <sys/lock.h>
3182607Sdillon#include <sys/mutex.h>
32129882Sphk#include <sys/module.h>
332729Sdfr#include <sys/msg.h>
3469449Salfred#include <sys/syscall.h>
3511626Sbde#include <sys/sysent.h>
3659839Speter#include <sys/sysctl.h>
3759839Speter#include <sys/malloc.h>
3868024Srwatson#include <sys/jail.h>
392729Sdfr
4059839Speterstatic MALLOC_DEFINE(M_MSG, "msg", "SVID compatible message queues");
4159839Speter
4292723Salfredstatic void msginit(void);
4392723Salfredstatic int msgunload(void);
4492723Salfredstatic int sysvmsg_modload(struct module *, int, void *);
4510358Sjulian
46100523Salfred#ifdef MSG_DEBUG
47100523Salfred#define DPRINTF(a)	printf a
48100523Salfred#else
49100523Salfred#define DPRINTF(a)
50100523Salfred#endif
512729Sdfr
5292723Salfredstatic void msg_freehdr(struct msg *msghdr);
532729Sdfr
5411626Sbde/* XXX casting to (sy_call_t *) is bogus, as usual. */
5512819Sphkstatic sy_call_t *msgcalls[] = {
5611626Sbde	(sy_call_t *)msgctl, (sy_call_t *)msgget,
5711626Sbde	(sy_call_t *)msgsnd, (sy_call_t *)msgrcv
5811626Sbde};
592729Sdfr
6059839Speter#ifndef MSGSSZ
6159839Speter#define MSGSSZ	8		/* Each segment must be 2^N long */
6259839Speter#endif
6359839Speter#ifndef MSGSEG
6459839Speter#define MSGSEG	2048		/* must be less than 32767 */
6559839Speter#endif
6659839Speter#define MSGMAX	(MSGSSZ*MSGSEG)
6759839Speter#ifndef MSGMNB
6859839Speter#define MSGMNB	2048		/* max # of bytes in a queue */
6959839Speter#endif
7059839Speter#ifndef MSGMNI
7159839Speter#define MSGMNI	40
7259839Speter#endif
7359839Speter#ifndef MSGTQL
7459839Speter#define MSGTQL	40
7559839Speter#endif
7659839Speter
7759839Speter/*
7859839Speter * Based on the configuration parameters described in an SVR2 (yes, two)
7959839Speter * config(1m) man page.
8059839Speter *
8159839Speter * Each message is broken up and stored in segments that are msgssz bytes
8259839Speter * long.  For efficiency reasons, this should be a power of two.  Also,
8359839Speter * it doesn't make sense if it is less than 8 or greater than about 256.
8459839Speter * Consequently, msginit in kern/sysv_msg.c checks that msgssz is a power of
8559839Speter * two between 8 and 1024 inclusive (and panic's if it isn't).
8659839Speter */
8759839Speterstruct msginfo msginfo = {
8859839Speter                MSGMAX,         /* max chars in a message */
8959839Speter                MSGMNI,         /* # of message queue identifiers */
9059839Speter                MSGMNB,         /* max chars in a queue */
9159839Speter                MSGTQL,         /* max messages in system */
9259839Speter                MSGSSZ,         /* size of a message segment */
9359839Speter                		/* (must be small power of 2 greater than 4) */
9459839Speter                MSGSEG          /* number of message segments */
9559839Speter};
9659839Speter
9759839Speter/*
9859839Speter * macros to convert between msqid_ds's and msqid's.
9959839Speter * (specific to this implementation)
10059839Speter */
10159839Speter#define MSQID(ix,ds)	((ix) & 0xffff | (((ds).msg_perm.seq << 16) & 0xffff0000))
10259839Speter#define MSQID_IX(id)	((id) & 0xffff)
10359839Speter#define MSQID_SEQ(id)	(((id) >> 16) & 0xffff)
10459839Speter
10559839Speter/*
10659839Speter * The rest of this file is specific to this particular implementation.
10759839Speter */
10859839Speter
10959839Speterstruct msgmap {
11059839Speter	short	next;		/* next segment in buffer */
11159839Speter    				/* -1 -> available */
11259839Speter    				/* 0..(MSGSEG-1) -> index of next segment */
11359839Speter};
11459839Speter
11559839Speter#define MSG_LOCKED	01000	/* Is this msqid_ds locked? */
11659839Speter
11712819Sphkstatic int nfree_msgmaps;	/* # of free map entries */
11812819Sphkstatic short free_msgmaps;	/* head of linked list of free map entries */
11959839Speterstatic struct msg *free_msghdrs;/* list of free msg headers */
12059839Speterstatic char *msgpool;		/* MSGMAX byte long msg buffer pool */
12159839Speterstatic struct msgmap *msgmaps;	/* MSGSEG msgmap structures */
12259839Speterstatic struct msg *msghdrs;	/* MSGTQL msg headers */
123137613Srwatsonstatic struct msqid_kernel *msqids;	/* MSGMNI msqid_kernel struct's */
124101772Salfredstatic struct mtx msq_mtx;	/* global mutex for message queues. */
1252729Sdfr
12659839Speterstatic void
12769449Salfredmsginit()
1282729Sdfr{
1292729Sdfr	register int i;
1302729Sdfr
13183765Smr	TUNABLE_INT_FETCH("kern.ipc.msgseg", &msginfo.msgseg);
13283765Smr	TUNABLE_INT_FETCH("kern.ipc.msgssz", &msginfo.msgssz);
13383765Smr	msginfo.msgmax = msginfo.msgseg * msginfo.msgssz;
13483765Smr	TUNABLE_INT_FETCH("kern.ipc.msgmni", &msginfo.msgmni);
13583765Smr
136111119Simp	msgpool = malloc(msginfo.msgmax, M_MSG, M_WAITOK);
13759839Speter	if (msgpool == NULL)
13859839Speter		panic("msgpool is NULL");
139111119Simp	msgmaps = malloc(sizeof(struct msgmap) * msginfo.msgseg, M_MSG, M_WAITOK);
14059839Speter	if (msgmaps == NULL)
14159839Speter		panic("msgmaps is NULL");
142111119Simp	msghdrs = malloc(sizeof(struct msg) * msginfo.msgtql, M_MSG, M_WAITOK);
14359839Speter	if (msghdrs == NULL)
14459839Speter		panic("msghdrs is NULL");
145137613Srwatson	msqids = malloc(sizeof(struct msqid_kernel) * msginfo.msgmni, M_MSG,
146137613Srwatson	    M_WAITOK);
14759839Speter	if (msqids == NULL)
14859839Speter		panic("msqids is NULL");
14959839Speter
1502729Sdfr	/*
1512729Sdfr	 * msginfo.msgssz should be a power of two for efficiency reasons.
1522729Sdfr	 * It is also pretty silly if msginfo.msgssz is less than 8
1532729Sdfr	 * or greater than about 256 so ...
1542729Sdfr	 */
1552729Sdfr
1562729Sdfr	i = 8;
1572729Sdfr	while (i < 1024 && i != msginfo.msgssz)
1582729Sdfr		i <<= 1;
1592729Sdfr    	if (i != msginfo.msgssz) {
160100523Salfred		DPRINTF(("msginfo.msgssz=%d (0x%x)\n", msginfo.msgssz,
161100523Salfred		    msginfo.msgssz));
1622729Sdfr		panic("msginfo.msgssz not a small power of 2");
1632729Sdfr	}
1642729Sdfr
1652729Sdfr	if (msginfo.msgseg > 32767) {
166100523Salfred		DPRINTF(("msginfo.msgseg=%d\n", msginfo.msgseg));
1672729Sdfr		panic("msginfo.msgseg > 32767");
1682729Sdfr	}
1692729Sdfr
1702729Sdfr	if (msgmaps == NULL)
1712729Sdfr		panic("msgmaps is NULL");
1722729Sdfr
1732729Sdfr	for (i = 0; i < msginfo.msgseg; i++) {
1742729Sdfr		if (i > 0)
1752729Sdfr			msgmaps[i-1].next = i;
1762729Sdfr		msgmaps[i].next = -1;	/* implies entry is available */
1772729Sdfr	}
1782729Sdfr	free_msgmaps = 0;
1792729Sdfr	nfree_msgmaps = msginfo.msgseg;
1802729Sdfr
1812729Sdfr	if (msghdrs == NULL)
1822729Sdfr		panic("msghdrs is NULL");
1832729Sdfr
1842729Sdfr	for (i = 0; i < msginfo.msgtql; i++) {
1852729Sdfr		msghdrs[i].msg_type = 0;
1862729Sdfr		if (i > 0)
1872729Sdfr			msghdrs[i-1].msg_next = &msghdrs[i];
1882729Sdfr		msghdrs[i].msg_next = NULL;
1892729Sdfr    	}
1902729Sdfr	free_msghdrs = &msghdrs[0];
1912729Sdfr
1922729Sdfr	if (msqids == NULL)
1932729Sdfr		panic("msqids is NULL");
1942729Sdfr
1952729Sdfr	for (i = 0; i < msginfo.msgmni; i++) {
196137613Srwatson		msqids[i].u.msg_qbytes = 0;	/* implies entry is available */
197137613Srwatson		msqids[i].u.msg_perm.seq = 0;	/* reset to a known value */
198137613Srwatson		msqids[i].u.msg_perm.mode = 0;
1992729Sdfr	}
200101772Salfred	mtx_init(&msq_mtx, "msq", NULL, MTX_DEF);
2012729Sdfr}
2022729Sdfr
20369449Salfredstatic int
20469449Salfredmsgunload()
20569449Salfred{
206137613Srwatson	struct msqid_kernel *msqkptr;
20769449Salfred	int msqid;
20869449Salfred
20969449Salfred	for (msqid = 0; msqid < msginfo.msgmni; msqid++) {
21069449Salfred		/*
21169449Salfred		 * Look for an unallocated and unlocked msqid_ds.
21269449Salfred		 * msqid_ds's can be locked by msgsnd or msgrcv while
21369449Salfred		 * they are copying the message in/out.  We can't
21469449Salfred		 * re-use the entry until they release it.
21569449Salfred		 */
216137613Srwatson		msqkptr = &msqids[msqid];
217137613Srwatson		if (msqkptr->u.msg_qbytes != 0 ||
218137613Srwatson		    (msqkptr->u.msg_perm.mode & MSG_LOCKED) != 0)
21969449Salfred			break;
22069449Salfred	}
22169449Salfred	if (msqid != msginfo.msgmni)
22269449Salfred		return (EBUSY);
22369449Salfred
22469449Salfred	free(msgpool, M_MSG);
22569449Salfred	free(msgmaps, M_MSG);
22669449Salfred	free(msghdrs, M_MSG);
22769449Salfred	free(msqids, M_MSG);
228101772Salfred	mtx_destroy(&msq_mtx);
22969449Salfred	return (0);
23069449Salfred}
23169449Salfred
23269449Salfred
23369449Salfredstatic int
23469449Salfredsysvmsg_modload(struct module *module, int cmd, void *arg)
23569449Salfred{
23669449Salfred	int error = 0;
23769449Salfred
23869449Salfred	switch (cmd) {
23969449Salfred	case MOD_LOAD:
24069449Salfred		msginit();
24169449Salfred		break;
24269449Salfred	case MOD_UNLOAD:
24369449Salfred		error = msgunload();
24469449Salfred		break;
24569449Salfred	case MOD_SHUTDOWN:
24669449Salfred		break;
24769449Salfred	default:
24869449Salfred		error = EINVAL;
24969449Salfred		break;
25069449Salfred	}
25169449Salfred	return (error);
25269449Salfred}
25369449Salfred
25471038Sdesstatic moduledata_t sysvmsg_mod = {
25571038Sdes	"sysvmsg",
25669449Salfred	&sysvmsg_modload,
25769449Salfred	NULL
25869449Salfred};
25969449Salfred
26088633SalfredSYSCALL_MODULE_HELPER(msgsys);
26188633SalfredSYSCALL_MODULE_HELPER(msgctl);
26288633SalfredSYSCALL_MODULE_HELPER(msgget);
26388633SalfredSYSCALL_MODULE_HELPER(msgsnd);
26488633SalfredSYSCALL_MODULE_HELPER(msgrcv);
26569449Salfred
26671038SdesDECLARE_MODULE(sysvmsg, sysvmsg_mod,
26769449Salfred	SI_SUB_SYSV_MSG, SI_ORDER_FIRST);
26871038SdesMODULE_VERSION(sysvmsg, 1);
26969449Salfred
2702729Sdfr/*
2712729Sdfr * Entry point for all MSG calls
27282607Sdillon *
27382607Sdillon * MPSAFE
2742729Sdfr */
2752729Sdfrint
27683366Sjulianmsgsys(td, uap)
27783366Sjulian	struct thread *td;
27811626Sbde	/* XXX actually varargs. */
27911626Sbde	struct msgsys_args /* {
280118615Snectar		int	which;
28111626Sbde		int	a2;
28211626Sbde		int	a3;
28311626Sbde		int	a4;
28411626Sbde		int	a5;
28511626Sbde		int	a6;
28611626Sbde	} */ *uap;
2872729Sdfr{
28882607Sdillon	int error;
2892729Sdfr
29091703Sjhb	if (!jail_sysvipc_allowed && jailed(td->td_ucred))
29191703Sjhb		return (ENOSYS);
292118615Snectar	if (uap->which < 0 ||
293118615Snectar	    uap->which >= sizeof(msgcalls)/sizeof(msgcalls[0]))
29491703Sjhb		return (EINVAL);
29583366Sjulian	error = (*msgcalls[uap->which])(td, &uap->a2);
29682607Sdillon	return (error);
2972729Sdfr}
2982729Sdfr
2992729Sdfrstatic void
3002729Sdfrmsg_freehdr(msghdr)
3012729Sdfr	struct msg *msghdr;
3022729Sdfr{
3032729Sdfr	while (msghdr->msg_ts > 0) {
3042729Sdfr		short next;
3052729Sdfr		if (msghdr->msg_spot < 0 || msghdr->msg_spot >= msginfo.msgseg)
3062729Sdfr			panic("msghdr->msg_spot out of range");
3072729Sdfr		next = msgmaps[msghdr->msg_spot].next;
3082729Sdfr		msgmaps[msghdr->msg_spot].next = free_msgmaps;
3092729Sdfr		free_msgmaps = msghdr->msg_spot;
3102729Sdfr		nfree_msgmaps++;
3112729Sdfr		msghdr->msg_spot = next;
3122729Sdfr		if (msghdr->msg_ts >= msginfo.msgssz)
3132729Sdfr			msghdr->msg_ts -= msginfo.msgssz;
3142729Sdfr		else
3152729Sdfr			msghdr->msg_ts = 0;
3162729Sdfr	}
3172729Sdfr	if (msghdr->msg_spot != -1)
3182729Sdfr		panic("msghdr->msg_spot != -1");
3192729Sdfr	msghdr->msg_next = free_msghdrs;
3202729Sdfr	free_msghdrs = msghdr;
3212729Sdfr}
3222729Sdfr
32312866Speter#ifndef _SYS_SYSPROTO_H_
3242729Sdfrstruct msgctl_args {
3252729Sdfr	int	msqid;
3262729Sdfr	int	cmd;
32712866Speter	struct	msqid_ds *buf;
3282729Sdfr};
32912866Speter#endif
3302729Sdfr
33182607Sdillon/*
33282607Sdillon * MPSAFE
33382607Sdillon */
33412866Speterint
33583366Sjulianmsgctl(td, uap)
33683366Sjulian	struct thread *td;
3372729Sdfr	register struct msgctl_args *uap;
3382729Sdfr{
3392729Sdfr	int msqid = uap->msqid;
3402729Sdfr	int cmd = uap->cmd;
34112866Speter	struct msqid_ds *user_msqptr = uap->buf;
34282607Sdillon	int rval, error;
3432729Sdfr	struct msqid_ds msqbuf;
344137613Srwatson	register struct msqid_kernel *msqkptr;
3452729Sdfr
346100523Salfred	DPRINTF(("call to msgctl(%d, %d, 0x%x)\n", msqid, cmd, user_msqptr));
34791703Sjhb	if (!jail_sysvipc_allowed && jailed(td->td_ucred))
34891703Sjhb		return (ENOSYS);
34991703Sjhb
3502729Sdfr	msqid = IPCID_TO_IX(msqid);
3512729Sdfr
3522729Sdfr	if (msqid < 0 || msqid >= msginfo.msgmni) {
353100523Salfred		DPRINTF(("msqid (%d) out of range (0<=msqid<%d)\n", msqid,
354100523Salfred		    msginfo.msgmni));
355101772Salfred		return (EINVAL);
3562729Sdfr	}
357101772Salfred	if (cmd == IPC_SET &&
358101772Salfred	    (error = copyin(user_msqptr, &msqbuf, sizeof(msqbuf))) != 0)
359101772Salfred		return (error);
3602729Sdfr
361137613Srwatson	msqkptr = &msqids[msqid];
3622729Sdfr
363101772Salfred	mtx_lock(&msq_mtx);
364137613Srwatson	if (msqkptr->u.msg_qbytes == 0) {
365100523Salfred		DPRINTF(("no such msqid\n"));
36682607Sdillon		error = EINVAL;
36782607Sdillon		goto done2;
3682729Sdfr	}
369137613Srwatson	if (msqkptr->u.msg_perm.seq != IPCID_TO_SEQ(uap->msqid)) {
370100523Salfred		DPRINTF(("wrong sequence number\n"));
37182607Sdillon		error = EINVAL;
37282607Sdillon		goto done2;
3732729Sdfr	}
3742729Sdfr
37582607Sdillon	error = 0;
3762729Sdfr	rval = 0;
3772729Sdfr
3782729Sdfr	switch (cmd) {
3792729Sdfr
3802729Sdfr	case IPC_RMID:
3812729Sdfr	{
3822729Sdfr		struct msg *msghdr;
383137613Srwatson		if ((error = ipcperm(td, &msqkptr->u.msg_perm, IPC_M)))
38482607Sdillon			goto done2;
385137613Srwatson
3862729Sdfr		/* Free the message headers */
387137613Srwatson		msghdr = msqkptr->u.msg_first;
3882729Sdfr		while (msghdr != NULL) {
3892729Sdfr			struct msg *msghdr_tmp;
3902729Sdfr
3912729Sdfr			/* Free the segments of each message */
392137613Srwatson			msqkptr->u.msg_cbytes -= msghdr->msg_ts;
393137613Srwatson			msqkptr->u.msg_qnum--;
3942729Sdfr			msghdr_tmp = msghdr;
3952729Sdfr			msghdr = msghdr->msg_next;
3962729Sdfr			msg_freehdr(msghdr_tmp);
3972729Sdfr		}
3982729Sdfr
399137613Srwatson		if (msqkptr->u.msg_cbytes != 0)
4002729Sdfr			panic("msg_cbytes is screwed up");
401137613Srwatson		if (msqkptr->u.msg_qnum != 0)
4022729Sdfr			panic("msg_qnum is screwed up");
4032729Sdfr
404137613Srwatson		msqkptr->u.msg_qbytes = 0;	/* Mark it as free */
4052729Sdfr
406137613Srwatson		wakeup(msqkptr);
4072729Sdfr	}
4082729Sdfr
4092729Sdfr		break;
4102729Sdfr
4112729Sdfr	case IPC_SET:
412137613Srwatson		if ((error = ipcperm(td, &msqkptr->u.msg_perm, IPC_M)))
41382607Sdillon			goto done2;
414137613Srwatson		if (msqbuf.msg_qbytes > msqkptr->u.msg_qbytes) {
41593593Sjhb			error = suser(td);
41682607Sdillon			if (error)
41782607Sdillon				goto done2;
41843426Sphk		}
4192729Sdfr		if (msqbuf.msg_qbytes > msginfo.msgmnb) {
420100523Salfred			DPRINTF(("can't increase msg_qbytes beyond %d"
421100523Salfred			    "(truncating)\n", msginfo.msgmnb));
4222729Sdfr			msqbuf.msg_qbytes = msginfo.msgmnb;	/* silently restrict qbytes to system limit */
4232729Sdfr		}
4242729Sdfr		if (msqbuf.msg_qbytes == 0) {
425100523Salfred			DPRINTF(("can't reduce msg_qbytes to 0\n"));
42682607Sdillon			error = EINVAL;		/* non-standard errno! */
42782607Sdillon			goto done2;
4282729Sdfr		}
429137613Srwatson		msqkptr->u.msg_perm.uid = msqbuf.msg_perm.uid;	/* change the owner */
430137613Srwatson		msqkptr->u.msg_perm.gid = msqbuf.msg_perm.gid;	/* change the owner */
431137613Srwatson		msqkptr->u.msg_perm.mode = (msqkptr->u.msg_perm.mode & ~0777) |
4322729Sdfr		    (msqbuf.msg_perm.mode & 0777);
433137613Srwatson		msqkptr->u.msg_qbytes = msqbuf.msg_qbytes;
434137613Srwatson		msqkptr->u.msg_ctime = time_second;
4352729Sdfr		break;
4362729Sdfr
4372729Sdfr	case IPC_STAT:
438137613Srwatson		if ((error = ipcperm(td, &msqkptr->u.msg_perm, IPC_R))) {
439100523Salfred			DPRINTF(("requester doesn't have read access\n"));
44082607Sdillon			goto done2;
4412729Sdfr		}
4422729Sdfr		break;
4432729Sdfr
4442729Sdfr	default:
445100523Salfred		DPRINTF(("invalid command %d\n", cmd));
44682607Sdillon		error = EINVAL;
44782607Sdillon		goto done2;
4482729Sdfr	}
4492729Sdfr
45082607Sdillon	if (error == 0)
45183366Sjulian		td->td_retval[0] = rval;
45282607Sdillondone2:
453101772Salfred	mtx_unlock(&msq_mtx);
454101772Salfred	if (cmd == IPC_STAT && error == 0)
455137613Srwatson		error = copyout(&(msqkptr->u), user_msqptr, sizeof(struct msqid_ds));
45682607Sdillon	return(error);
4572729Sdfr}
4582729Sdfr
45912866Speter#ifndef _SYS_SYSPROTO_H_
4602729Sdfrstruct msgget_args {
4612729Sdfr	key_t	key;
4622729Sdfr	int	msgflg;
4632729Sdfr};
46412866Speter#endif
4652729Sdfr
46682607Sdillon/*
46782607Sdillon * MPSAFE
46882607Sdillon */
46912866Speterint
47083366Sjulianmsgget(td, uap)
47183366Sjulian	struct thread *td;
4722729Sdfr	register struct msgget_args *uap;
4732729Sdfr{
47482607Sdillon	int msqid, error = 0;
4752729Sdfr	int key = uap->key;
4762729Sdfr	int msgflg = uap->msgflg;
47791703Sjhb	struct ucred *cred = td->td_ucred;
478137613Srwatson	register struct msqid_kernel *msqkptr = NULL;
4792729Sdfr
480100523Salfred	DPRINTF(("msgget(0x%x, 0%o)\n", key, msgflg));
4812729Sdfr
48291703Sjhb	if (!jail_sysvipc_allowed && jailed(td->td_ucred))
48391703Sjhb		return (ENOSYS);
48491703Sjhb
485101772Salfred	mtx_lock(&msq_mtx);
4862729Sdfr	if (key != IPC_PRIVATE) {
4872729Sdfr		for (msqid = 0; msqid < msginfo.msgmni; msqid++) {
488137613Srwatson			msqkptr = &msqids[msqid];
489137613Srwatson			if (msqkptr->u.msg_qbytes != 0 &&
490137613Srwatson			    msqkptr->u.msg_perm.key == key)
4912729Sdfr				break;
4922729Sdfr		}
4932729Sdfr		if (msqid < msginfo.msgmni) {
494100523Salfred			DPRINTF(("found public key\n"));
4952729Sdfr			if ((msgflg & IPC_CREAT) && (msgflg & IPC_EXCL)) {
496100523Salfred				DPRINTF(("not exclusive\n"));
49782607Sdillon				error = EEXIST;
49882607Sdillon				goto done2;
4992729Sdfr			}
500137613Srwatson			if ((error = ipcperm(td, &msqkptr->u.msg_perm,
501137613Srwatson			    msgflg & 0700))) {
502100523Salfred				DPRINTF(("requester doesn't have 0%o access\n",
503100523Salfred				    msgflg & 0700));
50482607Sdillon				goto done2;
5052729Sdfr			}
5062729Sdfr			goto found;
5072729Sdfr		}
5082729Sdfr	}
5092729Sdfr
510100523Salfred	DPRINTF(("need to allocate the msqid_ds\n"));
5112729Sdfr	if (key == IPC_PRIVATE || (msgflg & IPC_CREAT)) {
5122729Sdfr		for (msqid = 0; msqid < msginfo.msgmni; msqid++) {
5132729Sdfr			/*
5142729Sdfr			 * Look for an unallocated and unlocked msqid_ds.
5152729Sdfr			 * msqid_ds's can be locked by msgsnd or msgrcv while
5162729Sdfr			 * they are copying the message in/out.  We can't
5172729Sdfr			 * re-use the entry until they release it.
5182729Sdfr			 */
519137613Srwatson			msqkptr = &msqids[msqid];
520137613Srwatson			if (msqkptr->u.msg_qbytes == 0 &&
521137613Srwatson			    (msqkptr->u.msg_perm.mode & MSG_LOCKED) == 0)
5222729Sdfr				break;
5232729Sdfr		}
5242729Sdfr		if (msqid == msginfo.msgmni) {
525100523Salfred			DPRINTF(("no more msqid_ds's available\n"));
52682607Sdillon			error = ENOSPC;
52782607Sdillon			goto done2;
5282729Sdfr		}
529100523Salfred		DPRINTF(("msqid %d is available\n", msqid));
530137613Srwatson		msqkptr->u.msg_perm.key = key;
531137613Srwatson		msqkptr->u.msg_perm.cuid = cred->cr_uid;
532137613Srwatson		msqkptr->u.msg_perm.uid = cred->cr_uid;
533137613Srwatson		msqkptr->u.msg_perm.cgid = cred->cr_gid;
534137613Srwatson		msqkptr->u.msg_perm.gid = cred->cr_gid;
535137613Srwatson		msqkptr->u.msg_perm.mode = (msgflg & 0777);
5362729Sdfr		/* Make sure that the returned msqid is unique */
537137613Srwatson		msqkptr->u.msg_perm.seq = (msqkptr->u.msg_perm.seq + 1) & 0x7fff;
538137613Srwatson		msqkptr->u.msg_first = NULL;
539137613Srwatson		msqkptr->u.msg_last = NULL;
540137613Srwatson		msqkptr->u.msg_cbytes = 0;
541137613Srwatson		msqkptr->u.msg_qnum = 0;
542137613Srwatson		msqkptr->u.msg_qbytes = msginfo.msgmnb;
543137613Srwatson		msqkptr->u.msg_lspid = 0;
544137613Srwatson		msqkptr->u.msg_lrpid = 0;
545137613Srwatson		msqkptr->u.msg_stime = 0;
546137613Srwatson		msqkptr->u.msg_rtime = 0;
547137613Srwatson		msqkptr->u.msg_ctime = time_second;
5482729Sdfr	} else {
549100523Salfred		DPRINTF(("didn't find it and wasn't asked to create it\n"));
55082607Sdillon		error = ENOENT;
55182607Sdillon		goto done2;
5522729Sdfr	}
5532729Sdfr
5542729Sdfrfound:
5552729Sdfr	/* Construct the unique msqid */
556137613Srwatson	td->td_retval[0] = IXSEQ_TO_IPCID(msqid, msqkptr->u.msg_perm);
55782607Sdillondone2:
558101772Salfred	mtx_unlock(&msq_mtx);
55982607Sdillon	return (error);
5602729Sdfr}
5612729Sdfr
56212866Speter#ifndef _SYS_SYSPROTO_H_
5632729Sdfrstruct msgsnd_args {
5642729Sdfr	int	msqid;
565109895Salfred	const void	*msgp;
5662729Sdfr	size_t	msgsz;
5672729Sdfr	int	msgflg;
5682729Sdfr};
56912866Speter#endif
5702729Sdfr
57182607Sdillon/*
57282607Sdillon * MPSAFE
57382607Sdillon */
57412866Speterint
57583366Sjulianmsgsnd(td, uap)
57683366Sjulian	struct thread *td;
5772729Sdfr	register struct msgsnd_args *uap;
5782729Sdfr{
5792729Sdfr	int msqid = uap->msqid;
580109895Salfred	const void *user_msgp = uap->msgp;
5812729Sdfr	size_t msgsz = uap->msgsz;
5822729Sdfr	int msgflg = uap->msgflg;
58382607Sdillon	int segs_needed, error = 0;
584137613Srwatson	register struct msqid_kernel *msqkptr;
5852729Sdfr	register struct msg *msghdr;
5862729Sdfr	short next;
5872729Sdfr
588100523Salfred	DPRINTF(("call to msgsnd(%d, 0x%x, %d, %d)\n", msqid, user_msgp, msgsz,
589100523Salfred	    msgflg));
59091703Sjhb	if (!jail_sysvipc_allowed && jailed(td->td_ucred))
59191703Sjhb		return (ENOSYS);
59291703Sjhb
593101772Salfred	mtx_lock(&msq_mtx);
5942729Sdfr	msqid = IPCID_TO_IX(msqid);
5952729Sdfr
5962729Sdfr	if (msqid < 0 || msqid >= msginfo.msgmni) {
597100523Salfred		DPRINTF(("msqid (%d) out of range (0<=msqid<%d)\n", msqid,
598100523Salfred		    msginfo.msgmni));
59982607Sdillon		error = EINVAL;
60082607Sdillon		goto done2;
6012729Sdfr	}
6022729Sdfr
603137613Srwatson	msqkptr = &msqids[msqid];
604137613Srwatson	if (msqkptr->u.msg_qbytes == 0) {
605100523Salfred		DPRINTF(("no such message queue id\n"));
60682607Sdillon		error = EINVAL;
60782607Sdillon		goto done2;
6082729Sdfr	}
609137613Srwatson	if (msqkptr->u.msg_perm.seq != IPCID_TO_SEQ(uap->msqid)) {
610100523Salfred		DPRINTF(("wrong sequence number\n"));
61182607Sdillon		error = EINVAL;
61282607Sdillon		goto done2;
6132729Sdfr	}
6142729Sdfr
615137613Srwatson	if ((error = ipcperm(td, &msqkptr->u.msg_perm, IPC_W))) {
616100523Salfred		DPRINTF(("requester doesn't have write access\n"));
61782607Sdillon		goto done2;
6182729Sdfr	}
6192729Sdfr
6202729Sdfr	segs_needed = (msgsz + msginfo.msgssz - 1) / msginfo.msgssz;
621100523Salfred	DPRINTF(("msgsz=%d, msgssz=%d, segs_needed=%d\n", msgsz, msginfo.msgssz,
622100523Salfred	    segs_needed));
6232729Sdfr	for (;;) {
6242729Sdfr		int need_more_resources = 0;
6252729Sdfr
6262729Sdfr		/*
6272729Sdfr		 * check msgsz
6282729Sdfr		 * (inside this loop in case msg_qbytes changes while we sleep)
6292729Sdfr		 */
6302729Sdfr
631137613Srwatson		if (msgsz > msqkptr->u.msg_qbytes) {
632137613Srwatson			DPRINTF(("msgsz > msqkptr->u.msg_qbytes\n"));
63382607Sdillon			error = EINVAL;
63482607Sdillon			goto done2;
6352729Sdfr		}
6362729Sdfr
637137613Srwatson		if (msqkptr->u.msg_perm.mode & MSG_LOCKED) {
638100523Salfred			DPRINTF(("msqid is locked\n"));
6392729Sdfr			need_more_resources = 1;
6402729Sdfr		}
641137613Srwatson		if (msgsz + msqkptr->u.msg_cbytes > msqkptr->u.msg_qbytes) {
642100523Salfred			DPRINTF(("msgsz + msg_cbytes > msg_qbytes\n"));
6432729Sdfr			need_more_resources = 1;
6442729Sdfr		}
6452729Sdfr		if (segs_needed > nfree_msgmaps) {
646100523Salfred			DPRINTF(("segs_needed > nfree_msgmaps\n"));
6472729Sdfr			need_more_resources = 1;
6482729Sdfr		}
6492729Sdfr		if (free_msghdrs == NULL) {
650100523Salfred			DPRINTF(("no more msghdrs\n"));
6512729Sdfr			need_more_resources = 1;
6522729Sdfr		}
6532729Sdfr
6542729Sdfr		if (need_more_resources) {
6552729Sdfr			int we_own_it;
6562729Sdfr
6572729Sdfr			if ((msgflg & IPC_NOWAIT) != 0) {
658100523Salfred				DPRINTF(("need more resources but caller "
659100523Salfred				    "doesn't want to wait\n"));
66082607Sdillon				error = EAGAIN;
66182607Sdillon				goto done2;
6622729Sdfr			}
6632729Sdfr
664137613Srwatson			if ((msqkptr->u.msg_perm.mode & MSG_LOCKED) != 0) {
665100523Salfred				DPRINTF(("we don't own the msqid_ds\n"));
6662729Sdfr				we_own_it = 0;
6672729Sdfr			} else {
6682729Sdfr				/* Force later arrivals to wait for our
6692729Sdfr				   request */
670100523Salfred				DPRINTF(("we own the msqid_ds\n"));
671137613Srwatson				msqkptr->u.msg_perm.mode |= MSG_LOCKED;
6722729Sdfr				we_own_it = 1;
6732729Sdfr			}
674100523Salfred			DPRINTF(("goodnight\n"));
675137613Srwatson			error = msleep(msqkptr, &msq_mtx, (PZERO - 4) | PCATCH,
6762729Sdfr			    "msgwait", 0);
677100523Salfred			DPRINTF(("good morning, error=%d\n", error));
6782729Sdfr			if (we_own_it)
679137613Srwatson				msqkptr->u.msg_perm.mode &= ~MSG_LOCKED;
68082607Sdillon			if (error != 0) {
681100523Salfred				DPRINTF(("msgsnd:  interrupted system call\n"));
68282607Sdillon				error = EINTR;
68382607Sdillon				goto done2;
6842729Sdfr			}
6852729Sdfr
6862729Sdfr			/*
6872729Sdfr			 * Make sure that the msq queue still exists
6882729Sdfr			 */
6892729Sdfr
690137613Srwatson			if (msqkptr->u.msg_qbytes == 0) {
691100523Salfred				DPRINTF(("msqid deleted\n"));
69282607Sdillon				error = EIDRM;
69382607Sdillon				goto done2;
6942729Sdfr			}
6952729Sdfr
6962729Sdfr		} else {
697100523Salfred			DPRINTF(("got all the resources that we need\n"));
6982729Sdfr			break;
6992729Sdfr		}
7002729Sdfr	}
7012729Sdfr
7022729Sdfr	/*
7032729Sdfr	 * We have the resources that we need.
7042729Sdfr	 * Make sure!
7052729Sdfr	 */
7062729Sdfr
707137613Srwatson	if (msqkptr->u.msg_perm.mode & MSG_LOCKED)
7082729Sdfr		panic("msg_perm.mode & MSG_LOCKED");
7092729Sdfr	if (segs_needed > nfree_msgmaps)
7102729Sdfr		panic("segs_needed > nfree_msgmaps");
711137613Srwatson	if (msgsz + msqkptr->u.msg_cbytes > msqkptr->u.msg_qbytes)
7122729Sdfr		panic("msgsz + msg_cbytes > msg_qbytes");
7132729Sdfr	if (free_msghdrs == NULL)
7142729Sdfr		panic("no more msghdrs");
7152729Sdfr
7162729Sdfr	/*
7172729Sdfr	 * Re-lock the msqid_ds in case we page-fault when copying in the
7182729Sdfr	 * message
7192729Sdfr	 */
7202729Sdfr
721137613Srwatson	if ((msqkptr->u.msg_perm.mode & MSG_LOCKED) != 0)
7222729Sdfr		panic("msqid_ds is already locked");
723137613Srwatson	msqkptr->u.msg_perm.mode |= MSG_LOCKED;
7242729Sdfr
7252729Sdfr	/*
7262729Sdfr	 * Allocate a message header
7272729Sdfr	 */
7282729Sdfr
7292729Sdfr	msghdr = free_msghdrs;
7302729Sdfr	free_msghdrs = msghdr->msg_next;
7312729Sdfr	msghdr->msg_spot = -1;
7322729Sdfr	msghdr->msg_ts = msgsz;
7332729Sdfr
7342729Sdfr	/*
7352729Sdfr	 * Allocate space for the message
7362729Sdfr	 */
7372729Sdfr
7382729Sdfr	while (segs_needed > 0) {
7392729Sdfr		if (nfree_msgmaps <= 0)
7402729Sdfr			panic("not enough msgmaps");
7412729Sdfr		if (free_msgmaps == -1)
7422729Sdfr			panic("nil free_msgmaps");
7432729Sdfr		next = free_msgmaps;
7442729Sdfr		if (next <= -1)
7452729Sdfr			panic("next too low #1");
7462729Sdfr		if (next >= msginfo.msgseg)
7472729Sdfr			panic("next out of range #1");
748100523Salfred		DPRINTF(("allocating segment %d to message\n", next));
7492729Sdfr		free_msgmaps = msgmaps[next].next;
7502729Sdfr		nfree_msgmaps--;
7512729Sdfr		msgmaps[next].next = msghdr->msg_spot;
7522729Sdfr		msghdr->msg_spot = next;
7532729Sdfr		segs_needed--;
7542729Sdfr	}
7552729Sdfr
7562729Sdfr	/*
7572729Sdfr	 * Copy in the message type
7582729Sdfr	 */
7592729Sdfr
760101772Salfred	mtx_unlock(&msq_mtx);
76182607Sdillon	if ((error = copyin(user_msgp, &msghdr->msg_type,
7622729Sdfr	    sizeof(msghdr->msg_type))) != 0) {
763101772Salfred		mtx_lock(&msq_mtx);
764100523Salfred		DPRINTF(("error %d copying the message type\n", error));
7652729Sdfr		msg_freehdr(msghdr);
766137613Srwatson		msqkptr->u.msg_perm.mode &= ~MSG_LOCKED;
767137613Srwatson		wakeup(msqkptr);
76882607Sdillon		goto done2;
7692729Sdfr	}
770101772Salfred	mtx_lock(&msq_mtx);
771109906Salfred	user_msgp = (const char *)user_msgp + sizeof(msghdr->msg_type);
7722729Sdfr
7732729Sdfr	/*
7742729Sdfr	 * Validate the message type
7752729Sdfr	 */
7762729Sdfr
7772729Sdfr	if (msghdr->msg_type < 1) {
7782729Sdfr		msg_freehdr(msghdr);
779137613Srwatson		msqkptr->u.msg_perm.mode &= ~MSG_LOCKED;
780137613Srwatson		wakeup(msqkptr);
781100523Salfred		DPRINTF(("mtype (%d) < 1\n", msghdr->msg_type));
78282607Sdillon		error = EINVAL;
78382607Sdillon		goto done2;
7842729Sdfr	}
7852729Sdfr
7862729Sdfr	/*
7872729Sdfr	 * Copy in the message body
7882729Sdfr	 */
7892729Sdfr
7902729Sdfr	next = msghdr->msg_spot;
7912729Sdfr	while (msgsz > 0) {
7922729Sdfr		size_t tlen;
7932729Sdfr		if (msgsz > msginfo.msgssz)
7942729Sdfr			tlen = msginfo.msgssz;
7952729Sdfr		else
7962729Sdfr			tlen = msgsz;
7972729Sdfr		if (next <= -1)
7982729Sdfr			panic("next too low #2");
7992729Sdfr		if (next >= msginfo.msgseg)
8002729Sdfr			panic("next out of range #2");
801101772Salfred		mtx_unlock(&msq_mtx);
80282607Sdillon		if ((error = copyin(user_msgp, &msgpool[next * msginfo.msgssz],
8032729Sdfr		    tlen)) != 0) {
804101772Salfred			mtx_lock(&msq_mtx);
805100523Salfred			DPRINTF(("error %d copying in message segment\n",
806100523Salfred			    error));
8072729Sdfr			msg_freehdr(msghdr);
808137613Srwatson			msqkptr->u.msg_perm.mode &= ~MSG_LOCKED;
809137613Srwatson			wakeup(msqkptr);
81082607Sdillon			goto done2;
8112729Sdfr		}
812101772Salfred		mtx_lock(&msq_mtx);
8132729Sdfr		msgsz -= tlen;
814109906Salfred		user_msgp = (const char *)user_msgp + tlen;
8152729Sdfr		next = msgmaps[next].next;
8162729Sdfr	}
8172729Sdfr	if (next != -1)
8182729Sdfr		panic("didn't use all the msg segments");
8192729Sdfr
8202729Sdfr	/*
8212729Sdfr	 * We've got the message.  Unlock the msqid_ds.
8222729Sdfr	 */
8232729Sdfr
824137613Srwatson	msqkptr->u.msg_perm.mode &= ~MSG_LOCKED;
8252729Sdfr
8262729Sdfr	/*
8272729Sdfr	 * Make sure that the msqid_ds is still allocated.
8282729Sdfr	 */
8292729Sdfr
830137613Srwatson	if (msqkptr->u.msg_qbytes == 0) {
8312729Sdfr		msg_freehdr(msghdr);
832137613Srwatson		wakeup(msqkptr);
83382607Sdillon		error = EIDRM;
83482607Sdillon		goto done2;
8352729Sdfr	}
8362729Sdfr
8372729Sdfr	/*
8382729Sdfr	 * Put the message into the queue
8392729Sdfr	 */
840137613Srwatson	if (msqkptr->u.msg_first == NULL) {
841137613Srwatson		msqkptr->u.msg_first = msghdr;
842137613Srwatson		msqkptr->u.msg_last = msghdr;
8432729Sdfr	} else {
844137613Srwatson		msqkptr->u.msg_last->msg_next = msghdr;
845137613Srwatson		msqkptr->u.msg_last = msghdr;
8462729Sdfr	}
847137613Srwatson	msqkptr->u.msg_last->msg_next = NULL;
8482729Sdfr
849137613Srwatson	msqkptr->u.msg_cbytes += msghdr->msg_ts;
850137613Srwatson	msqkptr->u.msg_qnum++;
851137613Srwatson	msqkptr->u.msg_lspid = td->td_proc->p_pid;
852137613Srwatson	msqkptr->u.msg_stime = time_second;
8532729Sdfr
854137613Srwatson	wakeup(msqkptr);
85583366Sjulian	td->td_retval[0] = 0;
85682607Sdillondone2:
857101772Salfred	mtx_unlock(&msq_mtx);
85882607Sdillon	return (error);
8592729Sdfr}
8602729Sdfr
86112866Speter#ifndef _SYS_SYSPROTO_H_
8622729Sdfrstruct msgrcv_args {
8632729Sdfr	int	msqid;
8642729Sdfr	void	*msgp;
8652729Sdfr	size_t	msgsz;
8662729Sdfr	long	msgtyp;
8672729Sdfr	int	msgflg;
8682729Sdfr};
86912866Speter#endif
8702729Sdfr
87182607Sdillon/*
87282607Sdillon * MPSAFE
87382607Sdillon */
87412866Speterint
87583366Sjulianmsgrcv(td, uap)
87683366Sjulian	struct thread *td;
8772729Sdfr	register struct msgrcv_args *uap;
8782729Sdfr{
8792729Sdfr	int msqid = uap->msqid;
8802729Sdfr	void *user_msgp = uap->msgp;
8812729Sdfr	size_t msgsz = uap->msgsz;
8822729Sdfr	long msgtyp = uap->msgtyp;
8832729Sdfr	int msgflg = uap->msgflg;
8842729Sdfr	size_t len;
885137613Srwatson	register struct msqid_kernel *msqkptr;
8862729Sdfr	register struct msg *msghdr;
88782607Sdillon	int error = 0;
8882729Sdfr	short next;
8892729Sdfr
890100523Salfred	DPRINTF(("call to msgrcv(%d, 0x%x, %d, %ld, %d)\n", msqid, user_msgp,
891100523Salfred	    msgsz, msgtyp, msgflg));
8922729Sdfr
89391703Sjhb	if (!jail_sysvipc_allowed && jailed(td->td_ucred))
89491703Sjhb		return (ENOSYS);
89591703Sjhb
8962729Sdfr	msqid = IPCID_TO_IX(msqid);
8972729Sdfr
8982729Sdfr	if (msqid < 0 || msqid >= msginfo.msgmni) {
899100523Salfred		DPRINTF(("msqid (%d) out of range (0<=msqid<%d)\n", msqid,
900100523Salfred		    msginfo.msgmni));
901101772Salfred		return (EINVAL);
9022729Sdfr	}
9032729Sdfr
904137613Srwatson	msqkptr = &msqids[msqid];
905101772Salfred	mtx_lock(&msq_mtx);
906137613Srwatson	if (msqkptr->u.msg_qbytes == 0) {
907100523Salfred		DPRINTF(("no such message queue id\n"));
90882607Sdillon		error = EINVAL;
90982607Sdillon		goto done2;
9102729Sdfr	}
911137613Srwatson	if (msqkptr->u.msg_perm.seq != IPCID_TO_SEQ(uap->msqid)) {
912100523Salfred		DPRINTF(("wrong sequence number\n"));
91382607Sdillon		error = EINVAL;
91482607Sdillon		goto done2;
9152729Sdfr	}
9162729Sdfr
917137613Srwatson	if ((error = ipcperm(td, &msqkptr->u.msg_perm, IPC_R))) {
918100523Salfred		DPRINTF(("requester doesn't have read access\n"));
91982607Sdillon		goto done2;
9202729Sdfr	}
9212729Sdfr
9222729Sdfr	msghdr = NULL;
9232729Sdfr	while (msghdr == NULL) {
9242729Sdfr		if (msgtyp == 0) {
925137613Srwatson			msghdr = msqkptr->u.msg_first;
9262729Sdfr			if (msghdr != NULL) {
9272729Sdfr				if (msgsz < msghdr->msg_ts &&
9282729Sdfr				    (msgflg & MSG_NOERROR) == 0) {
929100523Salfred					DPRINTF(("first message on the queue "
930100523Salfred					    "is too big (want %d, got %d)\n",
931100523Salfred					    msgsz, msghdr->msg_ts));
93282607Sdillon					error = E2BIG;
93382607Sdillon					goto done2;
9342729Sdfr				}
935137613Srwatson				if (msqkptr->u.msg_first == msqkptr->u.msg_last) {
936137613Srwatson					msqkptr->u.msg_first = NULL;
937137613Srwatson					msqkptr->u.msg_last = NULL;
9382729Sdfr				} else {
939137613Srwatson					msqkptr->u.msg_first = msghdr->msg_next;
940137613Srwatson					if (msqkptr->u.msg_first == NULL)
9412729Sdfr						panic("msg_first/last screwed up #1");
9422729Sdfr				}
9432729Sdfr			}
9442729Sdfr		} else {
9452729Sdfr			struct msg *previous;
9462729Sdfr			struct msg **prev;
9472729Sdfr
9482729Sdfr			previous = NULL;
949137613Srwatson			prev = &(msqkptr->u.msg_first);
9502729Sdfr			while ((msghdr = *prev) != NULL) {
9512729Sdfr				/*
9522729Sdfr				 * Is this message's type an exact match or is
9532729Sdfr				 * this message's type less than or equal to
9542729Sdfr				 * the absolute value of a negative msgtyp?
9552729Sdfr				 * Note that the second half of this test can
9562729Sdfr				 * NEVER be true if msgtyp is positive since
9572729Sdfr				 * msg_type is always positive!
9582729Sdfr				 */
9592729Sdfr
9602729Sdfr				if (msgtyp == msghdr->msg_type ||
9612729Sdfr				    msghdr->msg_type <= -msgtyp) {
962100523Salfred					DPRINTF(("found message type %d, "
963100523Salfred					    "requested %d\n",
964100523Salfred					    msghdr->msg_type, msgtyp));
9652729Sdfr					if (msgsz < msghdr->msg_ts &&
9662729Sdfr					    (msgflg & MSG_NOERROR) == 0) {
967100523Salfred						DPRINTF(("requested message "
968100523Salfred						    "on the queue is too big "
969100523Salfred						    "(want %d, got %d)\n",
970100523Salfred						    msgsz, msghdr->msg_ts));
97182607Sdillon						error = E2BIG;
97282607Sdillon						goto done2;
9732729Sdfr					}
9742729Sdfr					*prev = msghdr->msg_next;
975137613Srwatson					if (msghdr == msqkptr->u.msg_last) {
9762729Sdfr						if (previous == NULL) {
9772729Sdfr							if (prev !=
978137613Srwatson							    &msqkptr->u.msg_first)
9792729Sdfr								panic("msg_first/last screwed up #2");
980137613Srwatson							msqkptr->u.msg_first =
9812729Sdfr							    NULL;
982137613Srwatson							msqkptr->u.msg_last =
9832729Sdfr							    NULL;
9842729Sdfr						} else {
9852729Sdfr							if (prev ==
986137613Srwatson							    &msqkptr->u.msg_first)
9872729Sdfr								panic("msg_first/last screwed up #3");
988137613Srwatson							msqkptr->u.msg_last =
9892729Sdfr							    previous;
9902729Sdfr						}
9912729Sdfr					}
9922729Sdfr					break;
9932729Sdfr				}
9942729Sdfr				previous = msghdr;
9952729Sdfr				prev = &(msghdr->msg_next);
9962729Sdfr			}
9972729Sdfr		}
9982729Sdfr
9992729Sdfr		/*
10002729Sdfr		 * We've either extracted the msghdr for the appropriate
10012729Sdfr		 * message or there isn't one.
10022729Sdfr		 * If there is one then bail out of this loop.
10032729Sdfr		 */
10042729Sdfr
10052729Sdfr		if (msghdr != NULL)
10062729Sdfr			break;
10072729Sdfr
10082729Sdfr		/*
10092729Sdfr		 * Hmph!  No message found.  Does the user want to wait?
10102729Sdfr		 */
10112729Sdfr
10122729Sdfr		if ((msgflg & IPC_NOWAIT) != 0) {
1013100523Salfred			DPRINTF(("no appropriate message found (msgtyp=%d)\n",
1014100523Salfred			    msgtyp));
10152729Sdfr			/* The SVID says to return ENOMSG. */
101682607Sdillon			error = ENOMSG;
101782607Sdillon			goto done2;
10182729Sdfr		}
10192729Sdfr
10202729Sdfr		/*
10212729Sdfr		 * Wait for something to happen
10222729Sdfr		 */
10232729Sdfr
1024100523Salfred		DPRINTF(("msgrcv:  goodnight\n"));
1025137613Srwatson		error = msleep(msqkptr, &msq_mtx, (PZERO - 4) | PCATCH,
1026101772Salfred		    "msgwait", 0);
1027100523Salfred		DPRINTF(("msgrcv:  good morning (error=%d)\n", error));
10282729Sdfr
102982607Sdillon		if (error != 0) {
1030100523Salfred			DPRINTF(("msgsnd:  interrupted system call\n"));
103182607Sdillon			error = EINTR;
103282607Sdillon			goto done2;
10332729Sdfr		}
10342729Sdfr
10352729Sdfr		/*
10362729Sdfr		 * Make sure that the msq queue still exists
10372729Sdfr		 */
10382729Sdfr
1039137613Srwatson		if (msqkptr->u.msg_qbytes == 0 ||
1040137613Srwatson		    msqkptr->u.msg_perm.seq != IPCID_TO_SEQ(uap->msqid)) {
1041100523Salfred			DPRINTF(("msqid deleted\n"));
104282607Sdillon			error = EIDRM;
104382607Sdillon			goto done2;
10442729Sdfr		}
10452729Sdfr	}
10462729Sdfr
10472729Sdfr	/*
10482729Sdfr	 * Return the message to the user.
10492729Sdfr	 *
10502729Sdfr	 * First, do the bookkeeping (before we risk being interrupted).
10512729Sdfr	 */
10522729Sdfr
1053137613Srwatson	msqkptr->u.msg_cbytes -= msghdr->msg_ts;
1054137613Srwatson	msqkptr->u.msg_qnum--;
1055137613Srwatson	msqkptr->u.msg_lrpid = td->td_proc->p_pid;
1056137613Srwatson	msqkptr->u.msg_rtime = time_second;
10572729Sdfr
10582729Sdfr	/*
10592729Sdfr	 * Make msgsz the actual amount that we'll be returning.
10602729Sdfr	 * Note that this effectively truncates the message if it is too long
10612729Sdfr	 * (since msgsz is never increased).
10622729Sdfr	 */
10632729Sdfr
1064100523Salfred	DPRINTF(("found a message, msgsz=%d, msg_ts=%d\n", msgsz,
1065100523Salfred	    msghdr->msg_ts));
10662729Sdfr	if (msgsz > msghdr->msg_ts)
10672729Sdfr		msgsz = msghdr->msg_ts;
10682729Sdfr
10692729Sdfr	/*
10702729Sdfr	 * Return the type to the user.
10712729Sdfr	 */
10722729Sdfr
1073101772Salfred	mtx_unlock(&msq_mtx);
1074100511Salfred	error = copyout(&(msghdr->msg_type), user_msgp,
10752729Sdfr	    sizeof(msghdr->msg_type));
1076101772Salfred	mtx_lock(&msq_mtx);
107782607Sdillon	if (error != 0) {
1078100523Salfred		DPRINTF(("error (%d) copying out message type\n", error));
10792729Sdfr		msg_freehdr(msghdr);
1080137613Srwatson		wakeup(msqkptr);
108182607Sdillon		goto done2;
10822729Sdfr	}
108317971Sbde	user_msgp = (char *)user_msgp + sizeof(msghdr->msg_type);
10842729Sdfr
10852729Sdfr	/*
10862729Sdfr	 * Return the segments to the user
10872729Sdfr	 */
10882729Sdfr
10892729Sdfr	next = msghdr->msg_spot;
10902729Sdfr	for (len = 0; len < msgsz; len += msginfo.msgssz) {
10912729Sdfr		size_t tlen;
10922729Sdfr
109345921Ssada		if (msgsz - len > msginfo.msgssz)
10942729Sdfr			tlen = msginfo.msgssz;
10952729Sdfr		else
109645921Ssada			tlen = msgsz - len;
10972729Sdfr		if (next <= -1)
10982729Sdfr			panic("next too low #3");
10992729Sdfr		if (next >= msginfo.msgseg)
11002729Sdfr			panic("next out of range #3");
1101101772Salfred		mtx_unlock(&msq_mtx);
1102100511Salfred		error = copyout(&msgpool[next * msginfo.msgssz],
11032729Sdfr		    user_msgp, tlen);
1104101772Salfred		mtx_lock(&msq_mtx);
110582607Sdillon		if (error != 0) {
1106100523Salfred			DPRINTF(("error (%d) copying out message segment\n",
1107100523Salfred			    error));
11082729Sdfr			msg_freehdr(msghdr);
1109137613Srwatson			wakeup(msqkptr);
111082607Sdillon			goto done2;
11112729Sdfr		}
111217971Sbde		user_msgp = (char *)user_msgp + tlen;
11132729Sdfr		next = msgmaps[next].next;
11142729Sdfr	}
11152729Sdfr
11162729Sdfr	/*
11172729Sdfr	 * Done, return the actual number of bytes copied out.
11182729Sdfr	 */
11192729Sdfr
11202729Sdfr	msg_freehdr(msghdr);
1121137613Srwatson	wakeup(msqkptr);
112283366Sjulian	td->td_retval[0] = msgsz;
112382607Sdillondone2:
1124101772Salfred	mtx_unlock(&msq_mtx);
112582607Sdillon	return (error);
11262729Sdfr}
112777461Sdd
112877461Sddstatic int
112977461Sddsysctl_msqids(SYSCTL_HANDLER_ARGS)
113077461Sdd{
113177461Sdd
113277461Sdd	return (SYSCTL_OUT(req, msqids,
1133137613Srwatson	    sizeof(struct msqid_kernel) * msginfo.msgmni));
113477461Sdd}
113577461Sdd
113677461SddSYSCTL_DECL(_kern_ipc);
113777461SddSYSCTL_INT(_kern_ipc, OID_AUTO, msgmax, CTLFLAG_RD, &msginfo.msgmax, 0, "");
1138121307SsilbySYSCTL_INT(_kern_ipc, OID_AUTO, msgmni, CTLFLAG_RDTUN, &msginfo.msgmni, 0, "");
113977461SddSYSCTL_INT(_kern_ipc, OID_AUTO, msgmnb, CTLFLAG_RD, &msginfo.msgmnb, 0, "");
114077461SddSYSCTL_INT(_kern_ipc, OID_AUTO, msgtql, CTLFLAG_RD, &msginfo.msgtql, 0, "");
1141121307SsilbySYSCTL_INT(_kern_ipc, OID_AUTO, msgssz, CTLFLAG_RDTUN, &msginfo.msgssz, 0, "");
1142121307SsilbySYSCTL_INT(_kern_ipc, OID_AUTO, msgseg, CTLFLAG_RDTUN, &msginfo.msgseg, 0, "");
114377461SddSYSCTL_PROC(_kern_ipc, OID_AUTO, msqids, CTLFLAG_RD,
114477461Sdd    NULL, 0, sysctl_msqids, "", "Message queue IDs");
1145