sysv_msg.c revision 69449
150477Speter/* $FreeBSD: head/sys/kern/sysv_msg.c 69449 2000-12-01 08:57:47Z alfred $ */
22729Sdfr
32729Sdfr/*
42729Sdfr * Implementation of SVID messages
52729Sdfr *
62729Sdfr * Author:  Daniel Boulet
72729Sdfr *
82729Sdfr * Copyright 1993 Daniel Boulet and RTMX Inc.
92729Sdfr *
102729Sdfr * This system call was implemented by Daniel Boulet under contract from RTMX.
112729Sdfr *
122729Sdfr * Redistribution and use in source forms, with and without modification,
132729Sdfr * are permitted provided that this entire comment appears intact.
142729Sdfr *
152729Sdfr * Redistribution in binary form may occur without any restrictions.
162729Sdfr * Obviously, it would be nice if you gave credit where credit is due
172729Sdfr * but requiring it would be too onerous.
182729Sdfr *
192729Sdfr * This software is provided ``AS IS'' without any warranties of any kind.
202729Sdfr */
212729Sdfr
2259839Speter#include "opt_sysvipc.h"
2359839Speter
242729Sdfr#include <sys/param.h>
252729Sdfr#include <sys/systm.h>
2611626Sbde#include <sys/sysproto.h>
272729Sdfr#include <sys/kernel.h>
282729Sdfr#include <sys/proc.h>
292729Sdfr#include <sys/msg.h>
3069449Salfred#include <sys/syscall.h>
3111626Sbde#include <sys/sysent.h>
3259839Speter#include <sys/sysctl.h>
3359839Speter#include <sys/malloc.h>
3468024Srwatson#include <sys/jail.h>
352729Sdfr
3659839Speterstatic MALLOC_DEFINE(M_MSG, "msg", "SVID compatible message queues");
3759839Speter
3869449Salfredstatic void msginit __P((void));
3969449Salfredstatic int msgunload __P((void));
4069449Salfredstatic int sysvmsg_modload __P((struct module *, int, void *));
4110358Sjulian
422729Sdfr#define MSG_DEBUG
432729Sdfr#undef MSG_DEBUG_OK
442729Sdfr
4511626Sbdestatic void msg_freehdr __P((struct msg *msghdr));
462729Sdfr
4711626Sbde/* XXX casting to (sy_call_t *) is bogus, as usual. */
4812819Sphkstatic sy_call_t *msgcalls[] = {
4911626Sbde	(sy_call_t *)msgctl, (sy_call_t *)msgget,
5011626Sbde	(sy_call_t *)msgsnd, (sy_call_t *)msgrcv
5111626Sbde};
522729Sdfr
5359839Speterstruct msg {
5459839Speter	struct	msg *msg_next;	/* next msg in the chain */
5559839Speter	long	msg_type;	/* type of this message */
5659839Speter    				/* >0 -> type of this message */
5759839Speter    				/* 0 -> free header */
5859839Speter	u_short	msg_ts;		/* size of this message */
5959839Speter	short	msg_spot;	/* location of start of msg in buffer */
6059839Speter};
6159839Speter
6259839Speter
6359839Speter#ifndef MSGSSZ
6459839Speter#define MSGSSZ	8		/* Each segment must be 2^N long */
6559839Speter#endif
6659839Speter#ifndef MSGSEG
6759839Speter#define MSGSEG	2048		/* must be less than 32767 */
6859839Speter#endif
6959839Speter#define MSGMAX	(MSGSSZ*MSGSEG)
7059839Speter#ifndef MSGMNB
7159839Speter#define MSGMNB	2048		/* max # of bytes in a queue */
7259839Speter#endif
7359839Speter#ifndef MSGMNI
7459839Speter#define MSGMNI	40
7559839Speter#endif
7659839Speter#ifndef MSGTQL
7759839Speter#define MSGTQL	40
7859839Speter#endif
7959839Speter
8059839Speter/*
8159839Speter * Based on the configuration parameters described in an SVR2 (yes, two)
8259839Speter * config(1m) man page.
8359839Speter *
8459839Speter * Each message is broken up and stored in segments that are msgssz bytes
8559839Speter * long.  For efficiency reasons, this should be a power of two.  Also,
8659839Speter * it doesn't make sense if it is less than 8 or greater than about 256.
8759839Speter * Consequently, msginit in kern/sysv_msg.c checks that msgssz is a power of
8859839Speter * two between 8 and 1024 inclusive (and panic's if it isn't).
8959839Speter */
9059839Speterstruct msginfo msginfo = {
9159839Speter                MSGMAX,         /* max chars in a message */
9259839Speter                MSGMNI,         /* # of message queue identifiers */
9359839Speter                MSGMNB,         /* max chars in a queue */
9459839Speter                MSGTQL,         /* max messages in system */
9559839Speter                MSGSSZ,         /* size of a message segment */
9659839Speter                		/* (must be small power of 2 greater than 4) */
9759839Speter                MSGSEG          /* number of message segments */
9859839Speter};
9959839Speter
10059839Speter/*
10159839Speter * macros to convert between msqid_ds's and msqid's.
10259839Speter * (specific to this implementation)
10359839Speter */
10459839Speter#define MSQID(ix,ds)	((ix) & 0xffff | (((ds).msg_perm.seq << 16) & 0xffff0000))
10559839Speter#define MSQID_IX(id)	((id) & 0xffff)
10659839Speter#define MSQID_SEQ(id)	(((id) >> 16) & 0xffff)
10759839Speter
10859839Speter/*
10959839Speter * The rest of this file is specific to this particular implementation.
11059839Speter */
11159839Speter
11259839Speterstruct msgmap {
11359839Speter	short	next;		/* next segment in buffer */
11459839Speter    				/* -1 -> available */
11559839Speter    				/* 0..(MSGSEG-1) -> index of next segment */
11659839Speter};
11759839Speter
11859839Speter#define MSG_LOCKED	01000	/* Is this msqid_ds locked? */
11959839Speter
12012819Sphkstatic int nfree_msgmaps;	/* # of free map entries */
12112819Sphkstatic short free_msgmaps;	/* head of linked list of free map entries */
12259839Speterstatic struct msg *free_msghdrs;/* list of free msg headers */
12359839Speterstatic char *msgpool;		/* MSGMAX byte long msg buffer pool */
12459839Speterstatic struct msgmap *msgmaps;	/* MSGSEG msgmap structures */
12559839Speterstatic struct msg *msghdrs;	/* MSGTQL msg headers */
12659839Speterstatic struct msqid_ds *msqids;	/* MSGMNI msqid_ds struct's */
1272729Sdfr
12859839Speterstatic void
12969449Salfredmsginit()
1302729Sdfr{
1312729Sdfr	register int i;
1322729Sdfr
13359839Speter	msgpool = malloc(msginfo.msgmax, M_MSG, M_WAITOK);
13459839Speter	if (msgpool == NULL)
13559839Speter		panic("msgpool is NULL");
13659839Speter	msgmaps = malloc(sizeof(struct msgmap) * msginfo.msgseg, M_MSG, M_WAITOK);
13759839Speter	if (msgmaps == NULL)
13859839Speter		panic("msgmaps is NULL");
13959839Speter	msghdrs = malloc(sizeof(struct msg) * msginfo.msgtql, M_MSG, M_WAITOK);
14059839Speter	if (msghdrs == NULL)
14159839Speter		panic("msghdrs is NULL");
14259839Speter	msqids = malloc(sizeof(struct msqid_ds) * msginfo.msgmni, M_MSG, M_WAITOK);
14359839Speter	if (msqids == NULL)
14459839Speter		panic("msqids is NULL");
14559839Speter
1462729Sdfr	/*
1472729Sdfr	 * msginfo.msgssz should be a power of two for efficiency reasons.
1482729Sdfr	 * It is also pretty silly if msginfo.msgssz is less than 8
1492729Sdfr	 * or greater than about 256 so ...
1502729Sdfr	 */
1512729Sdfr
1522729Sdfr	i = 8;
1532729Sdfr	while (i < 1024 && i != msginfo.msgssz)
1542729Sdfr		i <<= 1;
1552729Sdfr    	if (i != msginfo.msgssz) {
1562729Sdfr		printf("msginfo.msgssz=%d (0x%x)\n", msginfo.msgssz,
1572729Sdfr		    msginfo.msgssz);
1582729Sdfr		panic("msginfo.msgssz not a small power of 2");
1592729Sdfr	}
1602729Sdfr
1612729Sdfr	if (msginfo.msgseg > 32767) {
1622729Sdfr		printf("msginfo.msgseg=%d\n", msginfo.msgseg);
1632729Sdfr		panic("msginfo.msgseg > 32767");
1642729Sdfr	}
1652729Sdfr
1662729Sdfr	if (msgmaps == NULL)
1672729Sdfr		panic("msgmaps is NULL");
1682729Sdfr
1692729Sdfr	for (i = 0; i < msginfo.msgseg; i++) {
1702729Sdfr		if (i > 0)
1712729Sdfr			msgmaps[i-1].next = i;
1722729Sdfr		msgmaps[i].next = -1;	/* implies entry is available */
1732729Sdfr	}
1742729Sdfr	free_msgmaps = 0;
1752729Sdfr	nfree_msgmaps = msginfo.msgseg;
1762729Sdfr
1772729Sdfr	if (msghdrs == NULL)
1782729Sdfr		panic("msghdrs is NULL");
1792729Sdfr
1802729Sdfr	for (i = 0; i < msginfo.msgtql; i++) {
1812729Sdfr		msghdrs[i].msg_type = 0;
1822729Sdfr		if (i > 0)
1832729Sdfr			msghdrs[i-1].msg_next = &msghdrs[i];
1842729Sdfr		msghdrs[i].msg_next = NULL;
1852729Sdfr    	}
1862729Sdfr	free_msghdrs = &msghdrs[0];
1872729Sdfr
1882729Sdfr	if (msqids == NULL)
1892729Sdfr		panic("msqids is NULL");
1902729Sdfr
1912729Sdfr	for (i = 0; i < msginfo.msgmni; i++) {
1922729Sdfr		msqids[i].msg_qbytes = 0;	/* implies entry is available */
1932729Sdfr		msqids[i].msg_perm.seq = 0;	/* reset to a known value */
19466085Speter		msqids[i].msg_perm.mode = 0;
1952729Sdfr	}
1962729Sdfr}
1972729Sdfr
19869449Salfredstatic int
19969449Salfredmsgunload()
20069449Salfred{
20169449Salfred	struct msqid_ds *msqptr;
20269449Salfred	int msqid;
20369449Salfred
20469449Salfred	for (msqid = 0; msqid < msginfo.msgmni; msqid++) {
20569449Salfred		/*
20669449Salfred		 * Look for an unallocated and unlocked msqid_ds.
20769449Salfred		 * msqid_ds's can be locked by msgsnd or msgrcv while
20869449Salfred		 * they are copying the message in/out.  We can't
20969449Salfred		 * re-use the entry until they release it.
21069449Salfred		 */
21169449Salfred		msqptr = &msqids[msqid];
21269449Salfred		if (msqptr->msg_qbytes != 0 ||
21369449Salfred		    (msqptr->msg_perm.mode & MSG_LOCKED) != 0)
21469449Salfred			break;
21569449Salfred	}
21669449Salfred	if (msqid != msginfo.msgmni)
21769449Salfred		return (EBUSY);
21869449Salfred
21969449Salfred	free(msgpool, M_MSG);
22069449Salfred	free(msgmaps, M_MSG);
22169449Salfred	free(msghdrs, M_MSG);
22269449Salfred	free(msqids, M_MSG);
22369449Salfred	return (0);
22469449Salfred}
22569449Salfred
22669449Salfred
22769449Salfredstatic int
22869449Salfredsysvmsg_modload(struct module *module, int cmd, void *arg)
22969449Salfred{
23069449Salfred	int error = 0;
23169449Salfred
23269449Salfred	switch (cmd) {
23369449Salfred	case MOD_LOAD:
23469449Salfred		msginit();
23569449Salfred		break;
23669449Salfred	case MOD_UNLOAD:
23769449Salfred		error = msgunload();
23869449Salfred		break;
23969449Salfred	case MOD_SHUTDOWN:
24069449Salfred		break;
24169449Salfred	default:
24269449Salfred		error = EINVAL;
24369449Salfred		break;
24469449Salfred	}
24569449Salfred	return (error);
24669449Salfred}
24769449Salfred
24869449Salfredstatic moduledata_t sysvmsg_moduledata = {
24969449Salfred	"sysvmsg_mod",
25069449Salfred	&sysvmsg_modload,
25169449Salfred	NULL
25269449Salfred};
25369449Salfred
25469449SalfredSYSCALL_MODULE_HELPER(msgctl, 3);
25569449SalfredSYSCALL_MODULE_HELPER(msgget, 2);
25669449SalfredSYSCALL_MODULE_HELPER(msgsnd, 4);
25769449SalfredSYSCALL_MODULE_HELPER(msgrcv, 5);
25869449Salfred
25969449SalfredDECLARE_MODULE(sysvmsg_mod, sysvmsg_moduledata,
26069449Salfred	SI_SUB_SYSV_MSG, SI_ORDER_FIRST);
26169449Salfred
2622729Sdfr/*
2632729Sdfr * Entry point for all MSG calls
2642729Sdfr */
2652729Sdfrint
26630994Sphkmsgsys(p, uap)
26711626Sbde	struct proc *p;
26811626Sbde	/* XXX actually varargs. */
26911626Sbde	struct msgsys_args /* {
27011626Sbde		u_int	which;
27111626Sbde		int	a2;
27211626Sbde		int	a3;
27311626Sbde		int	a4;
27411626Sbde		int	a5;
27511626Sbde		int	a6;
27611626Sbde	} */ *uap;
2772729Sdfr{
2782729Sdfr
27968024Srwatson	if (!jail_sysvipc_allowed && p->p_prison != NULL)
28068024Srwatson		return (ENOSYS);
28168024Srwatson
2822729Sdfr	if (uap->which >= sizeof(msgcalls)/sizeof(msgcalls[0]))
2832729Sdfr		return (EINVAL);
28430994Sphk	return ((*msgcalls[uap->which])(p, &uap->a2));
2852729Sdfr}
2862729Sdfr
2872729Sdfrstatic void
2882729Sdfrmsg_freehdr(msghdr)
2892729Sdfr	struct msg *msghdr;
2902729Sdfr{
2912729Sdfr	while (msghdr->msg_ts > 0) {
2922729Sdfr		short next;
2932729Sdfr		if (msghdr->msg_spot < 0 || msghdr->msg_spot >= msginfo.msgseg)
2942729Sdfr			panic("msghdr->msg_spot out of range");
2952729Sdfr		next = msgmaps[msghdr->msg_spot].next;
2962729Sdfr		msgmaps[msghdr->msg_spot].next = free_msgmaps;
2972729Sdfr		free_msgmaps = msghdr->msg_spot;
2982729Sdfr		nfree_msgmaps++;
2992729Sdfr		msghdr->msg_spot = next;
3002729Sdfr		if (msghdr->msg_ts >= msginfo.msgssz)
3012729Sdfr			msghdr->msg_ts -= msginfo.msgssz;
3022729Sdfr		else
3032729Sdfr			msghdr->msg_ts = 0;
3042729Sdfr	}
3052729Sdfr	if (msghdr->msg_spot != -1)
3062729Sdfr		panic("msghdr->msg_spot != -1");
3072729Sdfr	msghdr->msg_next = free_msghdrs;
3082729Sdfr	free_msghdrs = msghdr;
3092729Sdfr}
3102729Sdfr
31112866Speter#ifndef _SYS_SYSPROTO_H_
3122729Sdfrstruct msgctl_args {
3132729Sdfr	int	msqid;
3142729Sdfr	int	cmd;
31512866Speter	struct	msqid_ds *buf;
3162729Sdfr};
31712866Speter#endif
3182729Sdfr
31912866Speterint
32030994Sphkmsgctl(p, uap)
3212729Sdfr	struct proc *p;
3222729Sdfr	register struct msgctl_args *uap;
3232729Sdfr{
3242729Sdfr	int msqid = uap->msqid;
3252729Sdfr	int cmd = uap->cmd;
32612866Speter	struct msqid_ds *user_msqptr = uap->buf;
3273308Sphk	int rval, eval;
3282729Sdfr	struct msqid_ds msqbuf;
3292729Sdfr	register struct msqid_ds *msqptr;
3302729Sdfr
3312729Sdfr#ifdef MSG_DEBUG_OK
3322729Sdfr	printf("call to msgctl(%d, %d, 0x%x)\n", msqid, cmd, user_msqptr);
3332729Sdfr#endif
3342729Sdfr
33568024Srwatson	if (!jail_sysvipc_allowed && p->p_prison != NULL)
33668024Srwatson		return (ENOSYS);
33768024Srwatson
3382729Sdfr	msqid = IPCID_TO_IX(msqid);
3392729Sdfr
3402729Sdfr	if (msqid < 0 || msqid >= msginfo.msgmni) {
3412729Sdfr#ifdef MSG_DEBUG_OK
3422729Sdfr		printf("msqid (%d) out of range (0<=msqid<%d)\n", msqid,
3432729Sdfr		    msginfo.msgmni);
3442729Sdfr#endif
3452729Sdfr		return(EINVAL);
3462729Sdfr	}
3472729Sdfr
3482729Sdfr	msqptr = &msqids[msqid];
3492729Sdfr
3502729Sdfr	if (msqptr->msg_qbytes == 0) {
3512729Sdfr#ifdef MSG_DEBUG_OK
3522729Sdfr		printf("no such msqid\n");
3532729Sdfr#endif
3542729Sdfr		return(EINVAL);
3552729Sdfr	}
3562729Sdfr	if (msqptr->msg_perm.seq != IPCID_TO_SEQ(uap->msqid)) {
3572729Sdfr#ifdef MSG_DEBUG_OK
3582729Sdfr		printf("wrong sequence number\n");
3592729Sdfr#endif
3602729Sdfr		return(EINVAL);
3612729Sdfr	}
3622729Sdfr
3632729Sdfr	eval = 0;
3642729Sdfr	rval = 0;
3652729Sdfr
3662729Sdfr	switch (cmd) {
3672729Sdfr
3682729Sdfr	case IPC_RMID:
3692729Sdfr	{
3702729Sdfr		struct msg *msghdr;
37146116Sphk		if ((eval = ipcperm(p, &msqptr->msg_perm, IPC_M)))
3722729Sdfr			return(eval);
3732729Sdfr		/* Free the message headers */
3742729Sdfr		msghdr = msqptr->msg_first;
3752729Sdfr		while (msghdr != NULL) {
3762729Sdfr			struct msg *msghdr_tmp;
3772729Sdfr
3782729Sdfr			/* Free the segments of each message */
3792729Sdfr			msqptr->msg_cbytes -= msghdr->msg_ts;
3802729Sdfr			msqptr->msg_qnum--;
3812729Sdfr			msghdr_tmp = msghdr;
3822729Sdfr			msghdr = msghdr->msg_next;
3832729Sdfr			msg_freehdr(msghdr_tmp);
3842729Sdfr		}
3852729Sdfr
3862729Sdfr		if (msqptr->msg_cbytes != 0)
3872729Sdfr			panic("msg_cbytes is screwed up");
3882729Sdfr		if (msqptr->msg_qnum != 0)
3892729Sdfr			panic("msg_qnum is screwed up");
3902729Sdfr
3912729Sdfr		msqptr->msg_qbytes = 0;	/* Mark it as free */
3922729Sdfr
3932729Sdfr		wakeup((caddr_t)msqptr);
3942729Sdfr	}
3952729Sdfr
3962729Sdfr		break;
3972729Sdfr
3982729Sdfr	case IPC_SET:
39946116Sphk		if ((eval = ipcperm(p, &msqptr->msg_perm, IPC_M)))
4002729Sdfr			return(eval);
4012729Sdfr		if ((eval = copyin(user_msqptr, &msqbuf, sizeof(msqbuf))) != 0)
4022729Sdfr			return(eval);
40343426Sphk		if (msqbuf.msg_qbytes > msqptr->msg_qbytes) {
40446116Sphk			eval = suser(p);
40543426Sphk			if (eval)
40643426Sphk				return(eval);
40743426Sphk		}
4082729Sdfr		if (msqbuf.msg_qbytes > msginfo.msgmnb) {
4092729Sdfr#ifdef MSG_DEBUG_OK
4102729Sdfr			printf("can't increase msg_qbytes beyond %d (truncating)\n",
4112729Sdfr			    msginfo.msgmnb);
4122729Sdfr#endif
4132729Sdfr			msqbuf.msg_qbytes = msginfo.msgmnb;	/* silently restrict qbytes to system limit */
4142729Sdfr		}
4152729Sdfr		if (msqbuf.msg_qbytes == 0) {
4162729Sdfr#ifdef MSG_DEBUG_OK
4172729Sdfr			printf("can't reduce msg_qbytes to 0\n");
4182729Sdfr#endif
4192729Sdfr			return(EINVAL);		/* non-standard errno! */
4202729Sdfr		}
4212729Sdfr		msqptr->msg_perm.uid = msqbuf.msg_perm.uid;	/* change the owner */
4222729Sdfr		msqptr->msg_perm.gid = msqbuf.msg_perm.gid;	/* change the owner */
4232729Sdfr		msqptr->msg_perm.mode = (msqptr->msg_perm.mode & ~0777) |
4242729Sdfr		    (msqbuf.msg_perm.mode & 0777);
4252729Sdfr		msqptr->msg_qbytes = msqbuf.msg_qbytes;
42634961Sphk		msqptr->msg_ctime = time_second;
4272729Sdfr		break;
4282729Sdfr
4292729Sdfr	case IPC_STAT:
43046116Sphk		if ((eval = ipcperm(p, &msqptr->msg_perm, IPC_R))) {
4312729Sdfr#ifdef MSG_DEBUG_OK
4322729Sdfr			printf("requester doesn't have read access\n");
4332729Sdfr#endif
4342729Sdfr			return(eval);
4352729Sdfr		}
4362729Sdfr		eval = copyout((caddr_t)msqptr, user_msqptr,
4372729Sdfr		    sizeof(struct msqid_ds));
4382729Sdfr		break;
4392729Sdfr
4402729Sdfr	default:
4412729Sdfr#ifdef MSG_DEBUG_OK
4422729Sdfr		printf("invalid command %d\n", cmd);
4432729Sdfr#endif
4442729Sdfr		return(EINVAL);
4452729Sdfr	}
4462729Sdfr
4472729Sdfr	if (eval == 0)
44830994Sphk		p->p_retval[0] = rval;
4492729Sdfr	return(eval);
4502729Sdfr}
4512729Sdfr
45212866Speter#ifndef _SYS_SYSPROTO_H_
4532729Sdfrstruct msgget_args {
4542729Sdfr	key_t	key;
4552729Sdfr	int	msgflg;
4562729Sdfr};
45712866Speter#endif
4582729Sdfr
45912866Speterint
46030994Sphkmsgget(p, uap)
4612729Sdfr	struct proc *p;
4622729Sdfr	register struct msgget_args *uap;
4632729Sdfr{
4642729Sdfr	int msqid, eval;
4652729Sdfr	int key = uap->key;
4662729Sdfr	int msgflg = uap->msgflg;
4672729Sdfr	struct ucred *cred = p->p_ucred;
4682836Sdg	register struct msqid_ds *msqptr = NULL;
4692729Sdfr
4702729Sdfr#ifdef MSG_DEBUG_OK
4712729Sdfr	printf("msgget(0x%x, 0%o)\n", key, msgflg);
4722729Sdfr#endif
4732729Sdfr
47468024Srwatson	if (!jail_sysvipc_allowed && p->p_prison != NULL)
47568024Srwatson		return (ENOSYS);
47668024Srwatson
4772729Sdfr	if (key != IPC_PRIVATE) {
4782729Sdfr		for (msqid = 0; msqid < msginfo.msgmni; msqid++) {
4792729Sdfr			msqptr = &msqids[msqid];
4802729Sdfr			if (msqptr->msg_qbytes != 0 &&
4812729Sdfr			    msqptr->msg_perm.key == key)
4822729Sdfr				break;
4832729Sdfr		}
4842729Sdfr		if (msqid < msginfo.msgmni) {
4852729Sdfr#ifdef MSG_DEBUG_OK
4862729Sdfr			printf("found public key\n");
4872729Sdfr#endif
4882729Sdfr			if ((msgflg & IPC_CREAT) && (msgflg & IPC_EXCL)) {
4892729Sdfr#ifdef MSG_DEBUG_OK
4902729Sdfr				printf("not exclusive\n");
4912729Sdfr#endif
4922729Sdfr				return(EEXIST);
4932729Sdfr			}
49446116Sphk			if ((eval = ipcperm(p, &msqptr->msg_perm, msgflg & 0700 ))) {
4952729Sdfr#ifdef MSG_DEBUG_OK
4962729Sdfr				printf("requester doesn't have 0%o access\n",
4972729Sdfr				    msgflg & 0700);
4982729Sdfr#endif
4992729Sdfr				return(eval);
5002729Sdfr			}
5012729Sdfr			goto found;
5022729Sdfr		}
5032729Sdfr	}
5042729Sdfr
5052729Sdfr#ifdef MSG_DEBUG_OK
5062729Sdfr	printf("need to allocate the msqid_ds\n");
5072729Sdfr#endif
5082729Sdfr	if (key == IPC_PRIVATE || (msgflg & IPC_CREAT)) {
5092729Sdfr		for (msqid = 0; msqid < msginfo.msgmni; msqid++) {
5102729Sdfr			/*
5112729Sdfr			 * Look for an unallocated and unlocked msqid_ds.
5122729Sdfr			 * msqid_ds's can be locked by msgsnd or msgrcv while
5132729Sdfr			 * they are copying the message in/out.  We can't
5142729Sdfr			 * re-use the entry until they release it.
5152729Sdfr			 */
5162729Sdfr			msqptr = &msqids[msqid];
5172729Sdfr			if (msqptr->msg_qbytes == 0 &&
5182729Sdfr			    (msqptr->msg_perm.mode & MSG_LOCKED) == 0)
5192729Sdfr				break;
5202729Sdfr		}
5212729Sdfr		if (msqid == msginfo.msgmni) {
5222729Sdfr#ifdef MSG_DEBUG_OK
5232729Sdfr			printf("no more msqid_ds's available\n");
5242729Sdfr#endif
5258876Srgrimes			return(ENOSPC);
5262729Sdfr		}
5272729Sdfr#ifdef MSG_DEBUG_OK
5282729Sdfr		printf("msqid %d is available\n", msqid);
5292729Sdfr#endif
5302729Sdfr		msqptr->msg_perm.key = key;
5312729Sdfr		msqptr->msg_perm.cuid = cred->cr_uid;
5322729Sdfr		msqptr->msg_perm.uid = cred->cr_uid;
5332729Sdfr		msqptr->msg_perm.cgid = cred->cr_gid;
5342729Sdfr		msqptr->msg_perm.gid = cred->cr_gid;
5352729Sdfr		msqptr->msg_perm.mode = (msgflg & 0777);
5362729Sdfr		/* Make sure that the returned msqid is unique */
5372729Sdfr		msqptr->msg_perm.seq++;
5382729Sdfr		msqptr->msg_first = NULL;
5392729Sdfr		msqptr->msg_last = NULL;
5402729Sdfr		msqptr->msg_cbytes = 0;
5412729Sdfr		msqptr->msg_qnum = 0;
5422729Sdfr		msqptr->msg_qbytes = msginfo.msgmnb;
5432729Sdfr		msqptr->msg_lspid = 0;
5442729Sdfr		msqptr->msg_lrpid = 0;
5452729Sdfr		msqptr->msg_stime = 0;
5462729Sdfr		msqptr->msg_rtime = 0;
54734961Sphk		msqptr->msg_ctime = time_second;
5482729Sdfr	} else {
5492729Sdfr#ifdef MSG_DEBUG_OK
5502729Sdfr		printf("didn't find it and wasn't asked to create it\n");
5512729Sdfr#endif
5522729Sdfr		return(ENOENT);
5532729Sdfr	}
5542729Sdfr
5552729Sdfrfound:
5562729Sdfr	/* Construct the unique msqid */
55730994Sphk	p->p_retval[0] = IXSEQ_TO_IPCID(msqid, msqptr->msg_perm);
5582729Sdfr	return(0);
5592729Sdfr}
5602729Sdfr
56112866Speter#ifndef _SYS_SYSPROTO_H_
5622729Sdfrstruct msgsnd_args {
5632729Sdfr	int	msqid;
56412866Speter	void	*msgp;
5652729Sdfr	size_t	msgsz;
5662729Sdfr	int	msgflg;
5672729Sdfr};
56812866Speter#endif
5692729Sdfr
57012866Speterint
57130994Sphkmsgsnd(p, uap)
5722729Sdfr	struct proc *p;
5732729Sdfr	register struct msgsnd_args *uap;
5742729Sdfr{
5752729Sdfr	int msqid = uap->msqid;
57612866Speter	void *user_msgp = uap->msgp;
5772729Sdfr	size_t msgsz = uap->msgsz;
5782729Sdfr	int msgflg = uap->msgflg;
5792729Sdfr	int segs_needed, eval;
5802729Sdfr	register struct msqid_ds *msqptr;
5812729Sdfr	register struct msg *msghdr;
5822729Sdfr	short next;
5832729Sdfr
5842729Sdfr#ifdef MSG_DEBUG_OK
5852729Sdfr	printf("call to msgsnd(%d, 0x%x, %d, %d)\n", msqid, user_msgp, msgsz,
5862729Sdfr	    msgflg);
5872729Sdfr#endif
5882729Sdfr
58968024Srwatson	if (!jail_sysvipc_allowed && p->p_prison != NULL)
59068024Srwatson		return (ENOSYS);
59168024Srwatson
5922729Sdfr	msqid = IPCID_TO_IX(msqid);
5932729Sdfr
5942729Sdfr	if (msqid < 0 || msqid >= msginfo.msgmni) {
5952729Sdfr#ifdef MSG_DEBUG_OK
5962729Sdfr		printf("msqid (%d) out of range (0<=msqid<%d)\n", msqid,
5972729Sdfr		    msginfo.msgmni);
5982729Sdfr#endif
5992729Sdfr		return(EINVAL);
6002729Sdfr	}
6012729Sdfr
6022729Sdfr	msqptr = &msqids[msqid];
6032729Sdfr	if (msqptr->msg_qbytes == 0) {
6042729Sdfr#ifdef MSG_DEBUG_OK
6052729Sdfr		printf("no such message queue id\n");
6062729Sdfr#endif
6072729Sdfr		return(EINVAL);
6082729Sdfr	}
6092729Sdfr	if (msqptr->msg_perm.seq != IPCID_TO_SEQ(uap->msqid)) {
6102729Sdfr#ifdef MSG_DEBUG_OK
6112729Sdfr		printf("wrong sequence number\n");
6122729Sdfr#endif
6132729Sdfr		return(EINVAL);
6142729Sdfr	}
6152729Sdfr
61646116Sphk	if ((eval = ipcperm(p, &msqptr->msg_perm, IPC_W))) {
6172729Sdfr#ifdef MSG_DEBUG_OK
6182729Sdfr		printf("requester doesn't have write access\n");
6192729Sdfr#endif
6202729Sdfr		return(eval);
6212729Sdfr	}
6222729Sdfr
6232729Sdfr	segs_needed = (msgsz + msginfo.msgssz - 1) / msginfo.msgssz;
6242729Sdfr#ifdef MSG_DEBUG_OK
6252729Sdfr	printf("msgsz=%d, msgssz=%d, segs_needed=%d\n", msgsz, msginfo.msgssz,
6262729Sdfr	    segs_needed);
6272729Sdfr#endif
6282729Sdfr	for (;;) {
6292729Sdfr		int need_more_resources = 0;
6302729Sdfr
6312729Sdfr		/*
6322729Sdfr		 * check msgsz
6332729Sdfr		 * (inside this loop in case msg_qbytes changes while we sleep)
6342729Sdfr		 */
6352729Sdfr
6362836Sdg		if (msgsz > msqptr->msg_qbytes) {
6372729Sdfr#ifdef MSG_DEBUG_OK
6382729Sdfr			printf("msgsz > msqptr->msg_qbytes\n");
6392729Sdfr#endif
6402729Sdfr			return(EINVAL);
6412729Sdfr		}
6422729Sdfr
6432729Sdfr		if (msqptr->msg_perm.mode & MSG_LOCKED) {
6442729Sdfr#ifdef MSG_DEBUG_OK
6452729Sdfr			printf("msqid is locked\n");
6462729Sdfr#endif
6472729Sdfr			need_more_resources = 1;
6482729Sdfr		}
6492729Sdfr		if (msgsz + msqptr->msg_cbytes > msqptr->msg_qbytes) {
6502729Sdfr#ifdef MSG_DEBUG_OK
6512729Sdfr			printf("msgsz + msg_cbytes > msg_qbytes\n");
6522729Sdfr#endif
6532729Sdfr			need_more_resources = 1;
6542729Sdfr		}
6552729Sdfr		if (segs_needed > nfree_msgmaps) {
6562729Sdfr#ifdef MSG_DEBUG_OK
6572729Sdfr			printf("segs_needed > nfree_msgmaps\n");
6582729Sdfr#endif
6592729Sdfr			need_more_resources = 1;
6602729Sdfr		}
6612729Sdfr		if (free_msghdrs == NULL) {
6622729Sdfr#ifdef MSG_DEBUG_OK
6632729Sdfr			printf("no more msghdrs\n");
6642729Sdfr#endif
6652729Sdfr			need_more_resources = 1;
6662729Sdfr		}
6672729Sdfr
6682729Sdfr		if (need_more_resources) {
6692729Sdfr			int we_own_it;
6702729Sdfr
6712729Sdfr			if ((msgflg & IPC_NOWAIT) != 0) {
6722729Sdfr#ifdef MSG_DEBUG_OK
6732729Sdfr				printf("need more resources but caller doesn't want to wait\n");
6742729Sdfr#endif
6752729Sdfr				return(EAGAIN);
6762729Sdfr			}
6772729Sdfr
6782729Sdfr			if ((msqptr->msg_perm.mode & MSG_LOCKED) != 0) {
6792729Sdfr#ifdef MSG_DEBUG_OK
6802729Sdfr				printf("we don't own the msqid_ds\n");
6812729Sdfr#endif
6822729Sdfr				we_own_it = 0;
6832729Sdfr			} else {
6842729Sdfr				/* Force later arrivals to wait for our
6852729Sdfr				   request */
6862729Sdfr#ifdef MSG_DEBUG_OK
6872729Sdfr				printf("we own the msqid_ds\n");
6882729Sdfr#endif
6892729Sdfr				msqptr->msg_perm.mode |= MSG_LOCKED;
6902729Sdfr				we_own_it = 1;
6912729Sdfr			}
6922729Sdfr#ifdef MSG_DEBUG_OK
6932729Sdfr			printf("goodnight\n");
6942729Sdfr#endif
6952729Sdfr			eval = tsleep((caddr_t)msqptr, (PZERO - 4) | PCATCH,
6962729Sdfr			    "msgwait", 0);
6972729Sdfr#ifdef MSG_DEBUG_OK
6982729Sdfr			printf("good morning, eval=%d\n", eval);
6992729Sdfr#endif
7002729Sdfr			if (we_own_it)
7012729Sdfr				msqptr->msg_perm.mode &= ~MSG_LOCKED;
7022729Sdfr			if (eval != 0) {
7032729Sdfr#ifdef MSG_DEBUG_OK
7042729Sdfr				printf("msgsnd:  interrupted system call\n");
7052729Sdfr#endif
7062729Sdfr				return(EINTR);
7072729Sdfr			}
7082729Sdfr
7092729Sdfr			/*
7102729Sdfr			 * Make sure that the msq queue still exists
7112729Sdfr			 */
7122729Sdfr
7132729Sdfr			if (msqptr->msg_qbytes == 0) {
7142729Sdfr#ifdef MSG_DEBUG_OK
7152729Sdfr				printf("msqid deleted\n");
7162729Sdfr#endif
7172729Sdfr				return(EIDRM);
7182729Sdfr			}
7192729Sdfr
7202729Sdfr		} else {
7212729Sdfr#ifdef MSG_DEBUG_OK
7222729Sdfr			printf("got all the resources that we need\n");
7232729Sdfr#endif
7242729Sdfr			break;
7252729Sdfr		}
7262729Sdfr	}
7272729Sdfr
7282729Sdfr	/*
7292729Sdfr	 * We have the resources that we need.
7302729Sdfr	 * Make sure!
7312729Sdfr	 */
7322729Sdfr
7332729Sdfr	if (msqptr->msg_perm.mode & MSG_LOCKED)
7342729Sdfr		panic("msg_perm.mode & MSG_LOCKED");
7352729Sdfr	if (segs_needed > nfree_msgmaps)
7362729Sdfr		panic("segs_needed > nfree_msgmaps");
7372729Sdfr	if (msgsz + msqptr->msg_cbytes > msqptr->msg_qbytes)
7382729Sdfr		panic("msgsz + msg_cbytes > msg_qbytes");
7392729Sdfr	if (free_msghdrs == NULL)
7402729Sdfr		panic("no more msghdrs");
7412729Sdfr
7422729Sdfr	/*
7432729Sdfr	 * Re-lock the msqid_ds in case we page-fault when copying in the
7442729Sdfr	 * message
7452729Sdfr	 */
7462729Sdfr
7472729Sdfr	if ((msqptr->msg_perm.mode & MSG_LOCKED) != 0)
7482729Sdfr		panic("msqid_ds is already locked");
7492729Sdfr	msqptr->msg_perm.mode |= MSG_LOCKED;
7502729Sdfr
7512729Sdfr	/*
7522729Sdfr	 * Allocate a message header
7532729Sdfr	 */
7542729Sdfr
7552729Sdfr	msghdr = free_msghdrs;
7562729Sdfr	free_msghdrs = msghdr->msg_next;
7572729Sdfr	msghdr->msg_spot = -1;
7582729Sdfr	msghdr->msg_ts = msgsz;
7592729Sdfr
7602729Sdfr	/*
7612729Sdfr	 * Allocate space for the message
7622729Sdfr	 */
7632729Sdfr
7642729Sdfr	while (segs_needed > 0) {
7652729Sdfr		if (nfree_msgmaps <= 0)
7662729Sdfr			panic("not enough msgmaps");
7672729Sdfr		if (free_msgmaps == -1)
7682729Sdfr			panic("nil free_msgmaps");
7692729Sdfr		next = free_msgmaps;
7702729Sdfr		if (next <= -1)
7712729Sdfr			panic("next too low #1");
7722729Sdfr		if (next >= msginfo.msgseg)
7732729Sdfr			panic("next out of range #1");
7742729Sdfr#ifdef MSG_DEBUG_OK
7752729Sdfr		printf("allocating segment %d to message\n", next);
7762729Sdfr#endif
7772729Sdfr		free_msgmaps = msgmaps[next].next;
7782729Sdfr		nfree_msgmaps--;
7792729Sdfr		msgmaps[next].next = msghdr->msg_spot;
7802729Sdfr		msghdr->msg_spot = next;
7812729Sdfr		segs_needed--;
7822729Sdfr	}
7832729Sdfr
7842729Sdfr	/*
7852729Sdfr	 * Copy in the message type
7862729Sdfr	 */
7872729Sdfr
7882729Sdfr	if ((eval = copyin(user_msgp, &msghdr->msg_type,
7892729Sdfr	    sizeof(msghdr->msg_type))) != 0) {
7902729Sdfr#ifdef MSG_DEBUG_OK
7912729Sdfr		printf("error %d copying the message type\n", eval);
7922729Sdfr#endif
7932729Sdfr		msg_freehdr(msghdr);
7942729Sdfr		msqptr->msg_perm.mode &= ~MSG_LOCKED;
7952729Sdfr		wakeup((caddr_t)msqptr);
7962729Sdfr		return(eval);
7972729Sdfr	}
79817971Sbde	user_msgp = (char *)user_msgp + sizeof(msghdr->msg_type);
7992729Sdfr
8002729Sdfr	/*
8012729Sdfr	 * Validate the message type
8022729Sdfr	 */
8032729Sdfr
8042729Sdfr	if (msghdr->msg_type < 1) {
8052729Sdfr		msg_freehdr(msghdr);
8062729Sdfr		msqptr->msg_perm.mode &= ~MSG_LOCKED;
8072729Sdfr		wakeup((caddr_t)msqptr);
8082729Sdfr#ifdef MSG_DEBUG_OK
8092729Sdfr		printf("mtype (%d) < 1\n", msghdr->msg_type);
8102729Sdfr#endif
8112729Sdfr		return(EINVAL);
8122729Sdfr	}
8132729Sdfr
8142729Sdfr	/*
8152729Sdfr	 * Copy in the message body
8162729Sdfr	 */
8172729Sdfr
8182729Sdfr	next = msghdr->msg_spot;
8192729Sdfr	while (msgsz > 0) {
8202729Sdfr		size_t tlen;
8212729Sdfr		if (msgsz > msginfo.msgssz)
8222729Sdfr			tlen = msginfo.msgssz;
8232729Sdfr		else
8242729Sdfr			tlen = msgsz;
8252729Sdfr		if (next <= -1)
8262729Sdfr			panic("next too low #2");
8272729Sdfr		if (next >= msginfo.msgseg)
8282729Sdfr			panic("next out of range #2");
8292729Sdfr		if ((eval = copyin(user_msgp, &msgpool[next * msginfo.msgssz],
8302729Sdfr		    tlen)) != 0) {
8312729Sdfr#ifdef MSG_DEBUG_OK
8322729Sdfr			printf("error %d copying in message segment\n", eval);
8332729Sdfr#endif
8342729Sdfr			msg_freehdr(msghdr);
8352729Sdfr			msqptr->msg_perm.mode &= ~MSG_LOCKED;
8362729Sdfr			wakeup((caddr_t)msqptr);
8372729Sdfr			return(eval);
8382729Sdfr		}
8392729Sdfr		msgsz -= tlen;
84017971Sbde		user_msgp = (char *)user_msgp + tlen;
8412729Sdfr		next = msgmaps[next].next;
8422729Sdfr	}
8432729Sdfr	if (next != -1)
8442729Sdfr		panic("didn't use all the msg segments");
8452729Sdfr
8462729Sdfr	/*
8472729Sdfr	 * We've got the message.  Unlock the msqid_ds.
8482729Sdfr	 */
8492729Sdfr
8502729Sdfr	msqptr->msg_perm.mode &= ~MSG_LOCKED;
8512729Sdfr
8522729Sdfr	/*
8532729Sdfr	 * Make sure that the msqid_ds is still allocated.
8542729Sdfr	 */
8552729Sdfr
8562729Sdfr	if (msqptr->msg_qbytes == 0) {
8572729Sdfr		msg_freehdr(msghdr);
8582729Sdfr		wakeup((caddr_t)msqptr);
8592729Sdfr		return(EIDRM);
8602729Sdfr	}
8612729Sdfr
8622729Sdfr	/*
8632729Sdfr	 * Put the message into the queue
8642729Sdfr	 */
8652729Sdfr
8662729Sdfr	if (msqptr->msg_first == NULL) {
8672729Sdfr		msqptr->msg_first = msghdr;
8682729Sdfr		msqptr->msg_last = msghdr;
8692729Sdfr	} else {
8702729Sdfr		msqptr->msg_last->msg_next = msghdr;
8712729Sdfr		msqptr->msg_last = msghdr;
8722729Sdfr	}
8732729Sdfr	msqptr->msg_last->msg_next = NULL;
8742729Sdfr
8752729Sdfr	msqptr->msg_cbytes += msghdr->msg_ts;
8762729Sdfr	msqptr->msg_qnum++;
8772729Sdfr	msqptr->msg_lspid = p->p_pid;
87834961Sphk	msqptr->msg_stime = time_second;
8792729Sdfr
8802729Sdfr	wakeup((caddr_t)msqptr);
88130994Sphk	p->p_retval[0] = 0;
8822729Sdfr	return(0);
8832729Sdfr}
8842729Sdfr
88512866Speter#ifndef _SYS_SYSPROTO_H_
8862729Sdfrstruct msgrcv_args {
8872729Sdfr	int	msqid;
8882729Sdfr	void	*msgp;
8892729Sdfr	size_t	msgsz;
8902729Sdfr	long	msgtyp;
8912729Sdfr	int	msgflg;
8922729Sdfr};
89312866Speter#endif
8942729Sdfr
89512866Speterint
89630994Sphkmsgrcv(p, uap)
8972729Sdfr	struct proc *p;
8982729Sdfr	register struct msgrcv_args *uap;
8992729Sdfr{
9002729Sdfr	int msqid = uap->msqid;
9012729Sdfr	void *user_msgp = uap->msgp;
9022729Sdfr	size_t msgsz = uap->msgsz;
9032729Sdfr	long msgtyp = uap->msgtyp;
9042729Sdfr	int msgflg = uap->msgflg;
9052729Sdfr	size_t len;
9062729Sdfr	register struct msqid_ds *msqptr;
9072729Sdfr	register struct msg *msghdr;
9082729Sdfr	int eval;
9092729Sdfr	short next;
9102729Sdfr
9112729Sdfr#ifdef MSG_DEBUG_OK
9122729Sdfr	printf("call to msgrcv(%d, 0x%x, %d, %ld, %d)\n", msqid, user_msgp,
9132729Sdfr	    msgsz, msgtyp, msgflg);
9142729Sdfr#endif
9152729Sdfr
91668024Srwatson	if (!jail_sysvipc_allowed && p->p_prison != NULL)
91768024Srwatson		return (ENOSYS);
91868024Srwatson
9192729Sdfr	msqid = IPCID_TO_IX(msqid);
9202729Sdfr
9212729Sdfr	if (msqid < 0 || msqid >= msginfo.msgmni) {
9222729Sdfr#ifdef MSG_DEBUG_OK
9232729Sdfr		printf("msqid (%d) out of range (0<=msqid<%d)\n", msqid,
9242729Sdfr		    msginfo.msgmni);
9252729Sdfr#endif
9262729Sdfr		return(EINVAL);
9272729Sdfr	}
9282729Sdfr
9292729Sdfr	msqptr = &msqids[msqid];
9302729Sdfr	if (msqptr->msg_qbytes == 0) {
9312729Sdfr#ifdef MSG_DEBUG_OK
9322729Sdfr		printf("no such message queue id\n");
9332729Sdfr#endif
9342729Sdfr		return(EINVAL);
9352729Sdfr	}
9362729Sdfr	if (msqptr->msg_perm.seq != IPCID_TO_SEQ(uap->msqid)) {
9372729Sdfr#ifdef MSG_DEBUG_OK
9382729Sdfr		printf("wrong sequence number\n");
9392729Sdfr#endif
9402729Sdfr		return(EINVAL);
9412729Sdfr	}
9422729Sdfr
94346116Sphk	if ((eval = ipcperm(p, &msqptr->msg_perm, IPC_R))) {
9442729Sdfr#ifdef MSG_DEBUG_OK
9452729Sdfr		printf("requester doesn't have read access\n");
9462729Sdfr#endif
9472729Sdfr		return(eval);
9482729Sdfr	}
9492729Sdfr
9502729Sdfr	msghdr = NULL;
9512729Sdfr	while (msghdr == NULL) {
9522729Sdfr		if (msgtyp == 0) {
9532729Sdfr			msghdr = msqptr->msg_first;
9542729Sdfr			if (msghdr != NULL) {
9552729Sdfr				if (msgsz < msghdr->msg_ts &&
9562729Sdfr				    (msgflg & MSG_NOERROR) == 0) {
9572729Sdfr#ifdef MSG_DEBUG_OK
9582729Sdfr					printf("first message on the queue is too big (want %d, got %d)\n",
9592729Sdfr					    msgsz, msghdr->msg_ts);
9602729Sdfr#endif
9612729Sdfr					return(E2BIG);
9622729Sdfr				}
9632729Sdfr				if (msqptr->msg_first == msqptr->msg_last) {
9642729Sdfr					msqptr->msg_first = NULL;
9652729Sdfr					msqptr->msg_last = NULL;
9662729Sdfr				} else {
9672729Sdfr					msqptr->msg_first = msghdr->msg_next;
9682729Sdfr					if (msqptr->msg_first == NULL)
9692729Sdfr						panic("msg_first/last screwed up #1");
9702729Sdfr				}
9712729Sdfr			}
9722729Sdfr		} else {
9732729Sdfr			struct msg *previous;
9742729Sdfr			struct msg **prev;
9752729Sdfr
9762729Sdfr			previous = NULL;
9772729Sdfr			prev = &(msqptr->msg_first);
9782729Sdfr			while ((msghdr = *prev) != NULL) {
9792729Sdfr				/*
9802729Sdfr				 * Is this message's type an exact match or is
9812729Sdfr				 * this message's type less than or equal to
9822729Sdfr				 * the absolute value of a negative msgtyp?
9832729Sdfr				 * Note that the second half of this test can
9842729Sdfr				 * NEVER be true if msgtyp is positive since
9852729Sdfr				 * msg_type is always positive!
9862729Sdfr				 */
9872729Sdfr
9882729Sdfr				if (msgtyp == msghdr->msg_type ||
9892729Sdfr				    msghdr->msg_type <= -msgtyp) {
9902729Sdfr#ifdef MSG_DEBUG_OK
9912729Sdfr					printf("found message type %d, requested %d\n",
9922729Sdfr					    msghdr->msg_type, msgtyp);
9932729Sdfr#endif
9942729Sdfr					if (msgsz < msghdr->msg_ts &&
9952729Sdfr					    (msgflg & MSG_NOERROR) == 0) {
9962729Sdfr#ifdef MSG_DEBUG_OK
9972729Sdfr						printf("requested message on the queue is too big (want %d, got %d)\n",
9982729Sdfr						    msgsz, msghdr->msg_ts);
9992729Sdfr#endif
10002729Sdfr						return(E2BIG);
10012729Sdfr					}
10022729Sdfr					*prev = msghdr->msg_next;
10032729Sdfr					if (msghdr == msqptr->msg_last) {
10042729Sdfr						if (previous == NULL) {
10052729Sdfr							if (prev !=
10062729Sdfr							    &msqptr->msg_first)
10072729Sdfr								panic("msg_first/last screwed up #2");
10082729Sdfr							msqptr->msg_first =
10092729Sdfr							    NULL;
10102729Sdfr							msqptr->msg_last =
10112729Sdfr							    NULL;
10122729Sdfr						} else {
10132729Sdfr							if (prev ==
10142729Sdfr							    &msqptr->msg_first)
10152729Sdfr								panic("msg_first/last screwed up #3");
10162729Sdfr							msqptr->msg_last =
10172729Sdfr							    previous;
10182729Sdfr						}
10192729Sdfr					}
10202729Sdfr					break;
10212729Sdfr				}
10222729Sdfr				previous = msghdr;
10232729Sdfr				prev = &(msghdr->msg_next);
10242729Sdfr			}
10252729Sdfr		}
10262729Sdfr
10272729Sdfr		/*
10282729Sdfr		 * We've either extracted the msghdr for the appropriate
10292729Sdfr		 * message or there isn't one.
10302729Sdfr		 * If there is one then bail out of this loop.
10312729Sdfr		 */
10322729Sdfr
10332729Sdfr		if (msghdr != NULL)
10342729Sdfr			break;
10352729Sdfr
10362729Sdfr		/*
10372729Sdfr		 * Hmph!  No message found.  Does the user want to wait?
10382729Sdfr		 */
10392729Sdfr
10402729Sdfr		if ((msgflg & IPC_NOWAIT) != 0) {
10412729Sdfr#ifdef MSG_DEBUG_OK
10422729Sdfr			printf("no appropriate message found (msgtyp=%d)\n",
10432729Sdfr			    msgtyp);
10442729Sdfr#endif
10452729Sdfr			/* The SVID says to return ENOMSG. */
10462729Sdfr#ifdef ENOMSG
10472729Sdfr			return(ENOMSG);
10482729Sdfr#else
10492729Sdfr			/* Unfortunately, BSD doesn't define that code yet! */
10502729Sdfr			return(EAGAIN);
10512729Sdfr#endif
10522729Sdfr		}
10532729Sdfr
10542729Sdfr		/*
10552729Sdfr		 * Wait for something to happen
10562729Sdfr		 */
10572729Sdfr
10582729Sdfr#ifdef MSG_DEBUG_OK
10592729Sdfr		printf("msgrcv:  goodnight\n");
10602729Sdfr#endif
10612729Sdfr		eval = tsleep((caddr_t)msqptr, (PZERO - 4) | PCATCH, "msgwait",
10622729Sdfr		    0);
10632729Sdfr#ifdef MSG_DEBUG_OK
10642729Sdfr		printf("msgrcv:  good morning (eval=%d)\n", eval);
10652729Sdfr#endif
10662729Sdfr
10672729Sdfr		if (eval != 0) {
10682729Sdfr#ifdef MSG_DEBUG_OK
10692729Sdfr			printf("msgsnd:  interrupted system call\n");
10702729Sdfr#endif
10712729Sdfr			return(EINTR);
10722729Sdfr		}
10732729Sdfr
10742729Sdfr		/*
10752729Sdfr		 * Make sure that the msq queue still exists
10762729Sdfr		 */
10772729Sdfr
10782729Sdfr		if (msqptr->msg_qbytes == 0 ||
10792729Sdfr		    msqptr->msg_perm.seq != IPCID_TO_SEQ(uap->msqid)) {
10802729Sdfr#ifdef MSG_DEBUG_OK
10812729Sdfr			printf("msqid deleted\n");
10822729Sdfr#endif
10832729Sdfr			return(EIDRM);
10842729Sdfr		}
10852729Sdfr	}
10862729Sdfr
10872729Sdfr	/*
10882729Sdfr	 * Return the message to the user.
10892729Sdfr	 *
10902729Sdfr	 * First, do the bookkeeping (before we risk being interrupted).
10912729Sdfr	 */
10922729Sdfr
10932729Sdfr	msqptr->msg_cbytes -= msghdr->msg_ts;
10942729Sdfr	msqptr->msg_qnum--;
10952729Sdfr	msqptr->msg_lrpid = p->p_pid;
109634961Sphk	msqptr->msg_rtime = time_second;
10972729Sdfr
10982729Sdfr	/*
10992729Sdfr	 * Make msgsz the actual amount that we'll be returning.
11002729Sdfr	 * Note that this effectively truncates the message if it is too long
11012729Sdfr	 * (since msgsz is never increased).
11022729Sdfr	 */
11032729Sdfr
11042729Sdfr#ifdef MSG_DEBUG_OK
11052729Sdfr	printf("found a message, msgsz=%d, msg_ts=%d\n", msgsz,
11062729Sdfr	    msghdr->msg_ts);
11072729Sdfr#endif
11082729Sdfr	if (msgsz > msghdr->msg_ts)
11092729Sdfr		msgsz = msghdr->msg_ts;
11102729Sdfr
11112729Sdfr	/*
11122729Sdfr	 * Return the type to the user.
11132729Sdfr	 */
11142729Sdfr
11152729Sdfr	eval = copyout((caddr_t)&(msghdr->msg_type), user_msgp,
11162729Sdfr	    sizeof(msghdr->msg_type));
11172729Sdfr	if (eval != 0) {
11182729Sdfr#ifdef MSG_DEBUG_OK
11192729Sdfr		printf("error (%d) copying out message type\n", eval);
11202729Sdfr#endif
11212729Sdfr		msg_freehdr(msghdr);
11222729Sdfr		wakeup((caddr_t)msqptr);
11232729Sdfr		return(eval);
11242729Sdfr	}
112517971Sbde	user_msgp = (char *)user_msgp + sizeof(msghdr->msg_type);
11262729Sdfr
11272729Sdfr	/*
11282729Sdfr	 * Return the segments to the user
11292729Sdfr	 */
11302729Sdfr
11312729Sdfr	next = msghdr->msg_spot;
11322729Sdfr	for (len = 0; len < msgsz; len += msginfo.msgssz) {
11332729Sdfr		size_t tlen;
11342729Sdfr
113545921Ssada		if (msgsz - len > msginfo.msgssz)
11362729Sdfr			tlen = msginfo.msgssz;
11372729Sdfr		else
113845921Ssada			tlen = msgsz - len;
11392729Sdfr		if (next <= -1)
11402729Sdfr			panic("next too low #3");
11412729Sdfr		if (next >= msginfo.msgseg)
11422729Sdfr			panic("next out of range #3");
11432729Sdfr		eval = copyout((caddr_t)&msgpool[next * msginfo.msgssz],
11442729Sdfr		    user_msgp, tlen);
11452729Sdfr		if (eval != 0) {
11462729Sdfr#ifdef MSG_DEBUG_OK
11472729Sdfr			printf("error (%d) copying out message segment\n",
11482729Sdfr			    eval);
11492729Sdfr#endif
11502729Sdfr			msg_freehdr(msghdr);
11512729Sdfr			wakeup((caddr_t)msqptr);
11522729Sdfr			return(eval);
11532729Sdfr		}
115417971Sbde		user_msgp = (char *)user_msgp + tlen;
11552729Sdfr		next = msgmaps[next].next;
11562729Sdfr	}
11572729Sdfr
11582729Sdfr	/*
11592729Sdfr	 * Done, return the actual number of bytes copied out.
11602729Sdfr	 */
11612729Sdfr
11622729Sdfr	msg_freehdr(msghdr);
11632729Sdfr	wakeup((caddr_t)msqptr);
116430994Sphk	p->p_retval[0] = msgsz;
11652729Sdfr	return(0);
11662729Sdfr}
1167