sysv_msg.c revision 69644
150477Speter/* $FreeBSD: head/sys/kern/sysv_msg.c 69644 2000-12-05 23:05:45Z 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
25469644SalfredSYSCALL_MODULE_HELPER(msgsys, 6);
25569449SalfredSYSCALL_MODULE_HELPER(msgctl, 3);
25669449SalfredSYSCALL_MODULE_HELPER(msgget, 2);
25769449SalfredSYSCALL_MODULE_HELPER(msgsnd, 4);
25869449SalfredSYSCALL_MODULE_HELPER(msgrcv, 5);
25969449Salfred
26069449SalfredDECLARE_MODULE(sysvmsg_mod, sysvmsg_moduledata,
26169449Salfred	SI_SUB_SYSV_MSG, SI_ORDER_FIRST);
26269449Salfred
2632729Sdfr/*
2642729Sdfr * Entry point for all MSG calls
2652729Sdfr */
2662729Sdfrint
26730994Sphkmsgsys(p, uap)
26811626Sbde	struct proc *p;
26911626Sbde	/* XXX actually varargs. */
27011626Sbde	struct msgsys_args /* {
27111626Sbde		u_int	which;
27211626Sbde		int	a2;
27311626Sbde		int	a3;
27411626Sbde		int	a4;
27511626Sbde		int	a5;
27611626Sbde		int	a6;
27711626Sbde	} */ *uap;
2782729Sdfr{
2792729Sdfr
28068024Srwatson	if (!jail_sysvipc_allowed && p->p_prison != NULL)
28168024Srwatson		return (ENOSYS);
28268024Srwatson
2832729Sdfr	if (uap->which >= sizeof(msgcalls)/sizeof(msgcalls[0]))
2842729Sdfr		return (EINVAL);
28530994Sphk	return ((*msgcalls[uap->which])(p, &uap->a2));
2862729Sdfr}
2872729Sdfr
2882729Sdfrstatic void
2892729Sdfrmsg_freehdr(msghdr)
2902729Sdfr	struct msg *msghdr;
2912729Sdfr{
2922729Sdfr	while (msghdr->msg_ts > 0) {
2932729Sdfr		short next;
2942729Sdfr		if (msghdr->msg_spot < 0 || msghdr->msg_spot >= msginfo.msgseg)
2952729Sdfr			panic("msghdr->msg_spot out of range");
2962729Sdfr		next = msgmaps[msghdr->msg_spot].next;
2972729Sdfr		msgmaps[msghdr->msg_spot].next = free_msgmaps;
2982729Sdfr		free_msgmaps = msghdr->msg_spot;
2992729Sdfr		nfree_msgmaps++;
3002729Sdfr		msghdr->msg_spot = next;
3012729Sdfr		if (msghdr->msg_ts >= msginfo.msgssz)
3022729Sdfr			msghdr->msg_ts -= msginfo.msgssz;
3032729Sdfr		else
3042729Sdfr			msghdr->msg_ts = 0;
3052729Sdfr	}
3062729Sdfr	if (msghdr->msg_spot != -1)
3072729Sdfr		panic("msghdr->msg_spot != -1");
3082729Sdfr	msghdr->msg_next = free_msghdrs;
3092729Sdfr	free_msghdrs = msghdr;
3102729Sdfr}
3112729Sdfr
31212866Speter#ifndef _SYS_SYSPROTO_H_
3132729Sdfrstruct msgctl_args {
3142729Sdfr	int	msqid;
3152729Sdfr	int	cmd;
31612866Speter	struct	msqid_ds *buf;
3172729Sdfr};
31812866Speter#endif
3192729Sdfr
32012866Speterint
32130994Sphkmsgctl(p, uap)
3222729Sdfr	struct proc *p;
3232729Sdfr	register struct msgctl_args *uap;
3242729Sdfr{
3252729Sdfr	int msqid = uap->msqid;
3262729Sdfr	int cmd = uap->cmd;
32712866Speter	struct msqid_ds *user_msqptr = uap->buf;
3283308Sphk	int rval, eval;
3292729Sdfr	struct msqid_ds msqbuf;
3302729Sdfr	register struct msqid_ds *msqptr;
3312729Sdfr
3322729Sdfr#ifdef MSG_DEBUG_OK
3332729Sdfr	printf("call to msgctl(%d, %d, 0x%x)\n", msqid, cmd, user_msqptr);
3342729Sdfr#endif
3352729Sdfr
33668024Srwatson	if (!jail_sysvipc_allowed && p->p_prison != NULL)
33768024Srwatson		return (ENOSYS);
33868024Srwatson
3392729Sdfr	msqid = IPCID_TO_IX(msqid);
3402729Sdfr
3412729Sdfr	if (msqid < 0 || msqid >= msginfo.msgmni) {
3422729Sdfr#ifdef MSG_DEBUG_OK
3432729Sdfr		printf("msqid (%d) out of range (0<=msqid<%d)\n", msqid,
3442729Sdfr		    msginfo.msgmni);
3452729Sdfr#endif
3462729Sdfr		return(EINVAL);
3472729Sdfr	}
3482729Sdfr
3492729Sdfr	msqptr = &msqids[msqid];
3502729Sdfr
3512729Sdfr	if (msqptr->msg_qbytes == 0) {
3522729Sdfr#ifdef MSG_DEBUG_OK
3532729Sdfr		printf("no such msqid\n");
3542729Sdfr#endif
3552729Sdfr		return(EINVAL);
3562729Sdfr	}
3572729Sdfr	if (msqptr->msg_perm.seq != IPCID_TO_SEQ(uap->msqid)) {
3582729Sdfr#ifdef MSG_DEBUG_OK
3592729Sdfr		printf("wrong sequence number\n");
3602729Sdfr#endif
3612729Sdfr		return(EINVAL);
3622729Sdfr	}
3632729Sdfr
3642729Sdfr	eval = 0;
3652729Sdfr	rval = 0;
3662729Sdfr
3672729Sdfr	switch (cmd) {
3682729Sdfr
3692729Sdfr	case IPC_RMID:
3702729Sdfr	{
3712729Sdfr		struct msg *msghdr;
37246116Sphk		if ((eval = ipcperm(p, &msqptr->msg_perm, IPC_M)))
3732729Sdfr			return(eval);
3742729Sdfr		/* Free the message headers */
3752729Sdfr		msghdr = msqptr->msg_first;
3762729Sdfr		while (msghdr != NULL) {
3772729Sdfr			struct msg *msghdr_tmp;
3782729Sdfr
3792729Sdfr			/* Free the segments of each message */
3802729Sdfr			msqptr->msg_cbytes -= msghdr->msg_ts;
3812729Sdfr			msqptr->msg_qnum--;
3822729Sdfr			msghdr_tmp = msghdr;
3832729Sdfr			msghdr = msghdr->msg_next;
3842729Sdfr			msg_freehdr(msghdr_tmp);
3852729Sdfr		}
3862729Sdfr
3872729Sdfr		if (msqptr->msg_cbytes != 0)
3882729Sdfr			panic("msg_cbytes is screwed up");
3892729Sdfr		if (msqptr->msg_qnum != 0)
3902729Sdfr			panic("msg_qnum is screwed up");
3912729Sdfr
3922729Sdfr		msqptr->msg_qbytes = 0;	/* Mark it as free */
3932729Sdfr
3942729Sdfr		wakeup((caddr_t)msqptr);
3952729Sdfr	}
3962729Sdfr
3972729Sdfr		break;
3982729Sdfr
3992729Sdfr	case IPC_SET:
40046116Sphk		if ((eval = ipcperm(p, &msqptr->msg_perm, IPC_M)))
4012729Sdfr			return(eval);
4022729Sdfr		if ((eval = copyin(user_msqptr, &msqbuf, sizeof(msqbuf))) != 0)
4032729Sdfr			return(eval);
40443426Sphk		if (msqbuf.msg_qbytes > msqptr->msg_qbytes) {
40546116Sphk			eval = suser(p);
40643426Sphk			if (eval)
40743426Sphk				return(eval);
40843426Sphk		}
4092729Sdfr		if (msqbuf.msg_qbytes > msginfo.msgmnb) {
4102729Sdfr#ifdef MSG_DEBUG_OK
4112729Sdfr			printf("can't increase msg_qbytes beyond %d (truncating)\n",
4122729Sdfr			    msginfo.msgmnb);
4132729Sdfr#endif
4142729Sdfr			msqbuf.msg_qbytes = msginfo.msgmnb;	/* silently restrict qbytes to system limit */
4152729Sdfr		}
4162729Sdfr		if (msqbuf.msg_qbytes == 0) {
4172729Sdfr#ifdef MSG_DEBUG_OK
4182729Sdfr			printf("can't reduce msg_qbytes to 0\n");
4192729Sdfr#endif
4202729Sdfr			return(EINVAL);		/* non-standard errno! */
4212729Sdfr		}
4222729Sdfr		msqptr->msg_perm.uid = msqbuf.msg_perm.uid;	/* change the owner */
4232729Sdfr		msqptr->msg_perm.gid = msqbuf.msg_perm.gid;	/* change the owner */
4242729Sdfr		msqptr->msg_perm.mode = (msqptr->msg_perm.mode & ~0777) |
4252729Sdfr		    (msqbuf.msg_perm.mode & 0777);
4262729Sdfr		msqptr->msg_qbytes = msqbuf.msg_qbytes;
42734961Sphk		msqptr->msg_ctime = time_second;
4282729Sdfr		break;
4292729Sdfr
4302729Sdfr	case IPC_STAT:
43146116Sphk		if ((eval = ipcperm(p, &msqptr->msg_perm, IPC_R))) {
4322729Sdfr#ifdef MSG_DEBUG_OK
4332729Sdfr			printf("requester doesn't have read access\n");
4342729Sdfr#endif
4352729Sdfr			return(eval);
4362729Sdfr		}
4372729Sdfr		eval = copyout((caddr_t)msqptr, user_msqptr,
4382729Sdfr		    sizeof(struct msqid_ds));
4392729Sdfr		break;
4402729Sdfr
4412729Sdfr	default:
4422729Sdfr#ifdef MSG_DEBUG_OK
4432729Sdfr		printf("invalid command %d\n", cmd);
4442729Sdfr#endif
4452729Sdfr		return(EINVAL);
4462729Sdfr	}
4472729Sdfr
4482729Sdfr	if (eval == 0)
44930994Sphk		p->p_retval[0] = rval;
4502729Sdfr	return(eval);
4512729Sdfr}
4522729Sdfr
45312866Speter#ifndef _SYS_SYSPROTO_H_
4542729Sdfrstruct msgget_args {
4552729Sdfr	key_t	key;
4562729Sdfr	int	msgflg;
4572729Sdfr};
45812866Speter#endif
4592729Sdfr
46012866Speterint
46130994Sphkmsgget(p, uap)
4622729Sdfr	struct proc *p;
4632729Sdfr	register struct msgget_args *uap;
4642729Sdfr{
4652729Sdfr	int msqid, eval;
4662729Sdfr	int key = uap->key;
4672729Sdfr	int msgflg = uap->msgflg;
4682729Sdfr	struct ucred *cred = p->p_ucred;
4692836Sdg	register struct msqid_ds *msqptr = NULL;
4702729Sdfr
4712729Sdfr#ifdef MSG_DEBUG_OK
4722729Sdfr	printf("msgget(0x%x, 0%o)\n", key, msgflg);
4732729Sdfr#endif
4742729Sdfr
47568024Srwatson	if (!jail_sysvipc_allowed && p->p_prison != NULL)
47668024Srwatson		return (ENOSYS);
47768024Srwatson
4782729Sdfr	if (key != IPC_PRIVATE) {
4792729Sdfr		for (msqid = 0; msqid < msginfo.msgmni; msqid++) {
4802729Sdfr			msqptr = &msqids[msqid];
4812729Sdfr			if (msqptr->msg_qbytes != 0 &&
4822729Sdfr			    msqptr->msg_perm.key == key)
4832729Sdfr				break;
4842729Sdfr		}
4852729Sdfr		if (msqid < msginfo.msgmni) {
4862729Sdfr#ifdef MSG_DEBUG_OK
4872729Sdfr			printf("found public key\n");
4882729Sdfr#endif
4892729Sdfr			if ((msgflg & IPC_CREAT) && (msgflg & IPC_EXCL)) {
4902729Sdfr#ifdef MSG_DEBUG_OK
4912729Sdfr				printf("not exclusive\n");
4922729Sdfr#endif
4932729Sdfr				return(EEXIST);
4942729Sdfr			}
49546116Sphk			if ((eval = ipcperm(p, &msqptr->msg_perm, msgflg & 0700 ))) {
4962729Sdfr#ifdef MSG_DEBUG_OK
4972729Sdfr				printf("requester doesn't have 0%o access\n",
4982729Sdfr				    msgflg & 0700);
4992729Sdfr#endif
5002729Sdfr				return(eval);
5012729Sdfr			}
5022729Sdfr			goto found;
5032729Sdfr		}
5042729Sdfr	}
5052729Sdfr
5062729Sdfr#ifdef MSG_DEBUG_OK
5072729Sdfr	printf("need to allocate the msqid_ds\n");
5082729Sdfr#endif
5092729Sdfr	if (key == IPC_PRIVATE || (msgflg & IPC_CREAT)) {
5102729Sdfr		for (msqid = 0; msqid < msginfo.msgmni; msqid++) {
5112729Sdfr			/*
5122729Sdfr			 * Look for an unallocated and unlocked msqid_ds.
5132729Sdfr			 * msqid_ds's can be locked by msgsnd or msgrcv while
5142729Sdfr			 * they are copying the message in/out.  We can't
5152729Sdfr			 * re-use the entry until they release it.
5162729Sdfr			 */
5172729Sdfr			msqptr = &msqids[msqid];
5182729Sdfr			if (msqptr->msg_qbytes == 0 &&
5192729Sdfr			    (msqptr->msg_perm.mode & MSG_LOCKED) == 0)
5202729Sdfr				break;
5212729Sdfr		}
5222729Sdfr		if (msqid == msginfo.msgmni) {
5232729Sdfr#ifdef MSG_DEBUG_OK
5242729Sdfr			printf("no more msqid_ds's available\n");
5252729Sdfr#endif
5268876Srgrimes			return(ENOSPC);
5272729Sdfr		}
5282729Sdfr#ifdef MSG_DEBUG_OK
5292729Sdfr		printf("msqid %d is available\n", msqid);
5302729Sdfr#endif
5312729Sdfr		msqptr->msg_perm.key = key;
5322729Sdfr		msqptr->msg_perm.cuid = cred->cr_uid;
5332729Sdfr		msqptr->msg_perm.uid = cred->cr_uid;
5342729Sdfr		msqptr->msg_perm.cgid = cred->cr_gid;
5352729Sdfr		msqptr->msg_perm.gid = cred->cr_gid;
5362729Sdfr		msqptr->msg_perm.mode = (msgflg & 0777);
5372729Sdfr		/* Make sure that the returned msqid is unique */
5382729Sdfr		msqptr->msg_perm.seq++;
5392729Sdfr		msqptr->msg_first = NULL;
5402729Sdfr		msqptr->msg_last = NULL;
5412729Sdfr		msqptr->msg_cbytes = 0;
5422729Sdfr		msqptr->msg_qnum = 0;
5432729Sdfr		msqptr->msg_qbytes = msginfo.msgmnb;
5442729Sdfr		msqptr->msg_lspid = 0;
5452729Sdfr		msqptr->msg_lrpid = 0;
5462729Sdfr		msqptr->msg_stime = 0;
5472729Sdfr		msqptr->msg_rtime = 0;
54834961Sphk		msqptr->msg_ctime = time_second;
5492729Sdfr	} else {
5502729Sdfr#ifdef MSG_DEBUG_OK
5512729Sdfr		printf("didn't find it and wasn't asked to create it\n");
5522729Sdfr#endif
5532729Sdfr		return(ENOENT);
5542729Sdfr	}
5552729Sdfr
5562729Sdfrfound:
5572729Sdfr	/* Construct the unique msqid */
55830994Sphk	p->p_retval[0] = IXSEQ_TO_IPCID(msqid, msqptr->msg_perm);
5592729Sdfr	return(0);
5602729Sdfr}
5612729Sdfr
56212866Speter#ifndef _SYS_SYSPROTO_H_
5632729Sdfrstruct msgsnd_args {
5642729Sdfr	int	msqid;
56512866Speter	void	*msgp;
5662729Sdfr	size_t	msgsz;
5672729Sdfr	int	msgflg;
5682729Sdfr};
56912866Speter#endif
5702729Sdfr
57112866Speterint
57230994Sphkmsgsnd(p, uap)
5732729Sdfr	struct proc *p;
5742729Sdfr	register struct msgsnd_args *uap;
5752729Sdfr{
5762729Sdfr	int msqid = uap->msqid;
57712866Speter	void *user_msgp = uap->msgp;
5782729Sdfr	size_t msgsz = uap->msgsz;
5792729Sdfr	int msgflg = uap->msgflg;
5802729Sdfr	int segs_needed, eval;
5812729Sdfr	register struct msqid_ds *msqptr;
5822729Sdfr	register struct msg *msghdr;
5832729Sdfr	short next;
5842729Sdfr
5852729Sdfr#ifdef MSG_DEBUG_OK
5862729Sdfr	printf("call to msgsnd(%d, 0x%x, %d, %d)\n", msqid, user_msgp, msgsz,
5872729Sdfr	    msgflg);
5882729Sdfr#endif
5892729Sdfr
59068024Srwatson	if (!jail_sysvipc_allowed && p->p_prison != NULL)
59168024Srwatson		return (ENOSYS);
59268024Srwatson
5932729Sdfr	msqid = IPCID_TO_IX(msqid);
5942729Sdfr
5952729Sdfr	if (msqid < 0 || msqid >= msginfo.msgmni) {
5962729Sdfr#ifdef MSG_DEBUG_OK
5972729Sdfr		printf("msqid (%d) out of range (0<=msqid<%d)\n", msqid,
5982729Sdfr		    msginfo.msgmni);
5992729Sdfr#endif
6002729Sdfr		return(EINVAL);
6012729Sdfr	}
6022729Sdfr
6032729Sdfr	msqptr = &msqids[msqid];
6042729Sdfr	if (msqptr->msg_qbytes == 0) {
6052729Sdfr#ifdef MSG_DEBUG_OK
6062729Sdfr		printf("no such message queue id\n");
6072729Sdfr#endif
6082729Sdfr		return(EINVAL);
6092729Sdfr	}
6102729Sdfr	if (msqptr->msg_perm.seq != IPCID_TO_SEQ(uap->msqid)) {
6112729Sdfr#ifdef MSG_DEBUG_OK
6122729Sdfr		printf("wrong sequence number\n");
6132729Sdfr#endif
6142729Sdfr		return(EINVAL);
6152729Sdfr	}
6162729Sdfr
61746116Sphk	if ((eval = ipcperm(p, &msqptr->msg_perm, IPC_W))) {
6182729Sdfr#ifdef MSG_DEBUG_OK
6192729Sdfr		printf("requester doesn't have write access\n");
6202729Sdfr#endif
6212729Sdfr		return(eval);
6222729Sdfr	}
6232729Sdfr
6242729Sdfr	segs_needed = (msgsz + msginfo.msgssz - 1) / msginfo.msgssz;
6252729Sdfr#ifdef MSG_DEBUG_OK
6262729Sdfr	printf("msgsz=%d, msgssz=%d, segs_needed=%d\n", msgsz, msginfo.msgssz,
6272729Sdfr	    segs_needed);
6282729Sdfr#endif
6292729Sdfr	for (;;) {
6302729Sdfr		int need_more_resources = 0;
6312729Sdfr
6322729Sdfr		/*
6332729Sdfr		 * check msgsz
6342729Sdfr		 * (inside this loop in case msg_qbytes changes while we sleep)
6352729Sdfr		 */
6362729Sdfr
6372836Sdg		if (msgsz > msqptr->msg_qbytes) {
6382729Sdfr#ifdef MSG_DEBUG_OK
6392729Sdfr			printf("msgsz > msqptr->msg_qbytes\n");
6402729Sdfr#endif
6412729Sdfr			return(EINVAL);
6422729Sdfr		}
6432729Sdfr
6442729Sdfr		if (msqptr->msg_perm.mode & MSG_LOCKED) {
6452729Sdfr#ifdef MSG_DEBUG_OK
6462729Sdfr			printf("msqid is locked\n");
6472729Sdfr#endif
6482729Sdfr			need_more_resources = 1;
6492729Sdfr		}
6502729Sdfr		if (msgsz + msqptr->msg_cbytes > msqptr->msg_qbytes) {
6512729Sdfr#ifdef MSG_DEBUG_OK
6522729Sdfr			printf("msgsz + msg_cbytes > msg_qbytes\n");
6532729Sdfr#endif
6542729Sdfr			need_more_resources = 1;
6552729Sdfr		}
6562729Sdfr		if (segs_needed > nfree_msgmaps) {
6572729Sdfr#ifdef MSG_DEBUG_OK
6582729Sdfr			printf("segs_needed > nfree_msgmaps\n");
6592729Sdfr#endif
6602729Sdfr			need_more_resources = 1;
6612729Sdfr		}
6622729Sdfr		if (free_msghdrs == NULL) {
6632729Sdfr#ifdef MSG_DEBUG_OK
6642729Sdfr			printf("no more msghdrs\n");
6652729Sdfr#endif
6662729Sdfr			need_more_resources = 1;
6672729Sdfr		}
6682729Sdfr
6692729Sdfr		if (need_more_resources) {
6702729Sdfr			int we_own_it;
6712729Sdfr
6722729Sdfr			if ((msgflg & IPC_NOWAIT) != 0) {
6732729Sdfr#ifdef MSG_DEBUG_OK
6742729Sdfr				printf("need more resources but caller doesn't want to wait\n");
6752729Sdfr#endif
6762729Sdfr				return(EAGAIN);
6772729Sdfr			}
6782729Sdfr
6792729Sdfr			if ((msqptr->msg_perm.mode & MSG_LOCKED) != 0) {
6802729Sdfr#ifdef MSG_DEBUG_OK
6812729Sdfr				printf("we don't own the msqid_ds\n");
6822729Sdfr#endif
6832729Sdfr				we_own_it = 0;
6842729Sdfr			} else {
6852729Sdfr				/* Force later arrivals to wait for our
6862729Sdfr				   request */
6872729Sdfr#ifdef MSG_DEBUG_OK
6882729Sdfr				printf("we own the msqid_ds\n");
6892729Sdfr#endif
6902729Sdfr				msqptr->msg_perm.mode |= MSG_LOCKED;
6912729Sdfr				we_own_it = 1;
6922729Sdfr			}
6932729Sdfr#ifdef MSG_DEBUG_OK
6942729Sdfr			printf("goodnight\n");
6952729Sdfr#endif
6962729Sdfr			eval = tsleep((caddr_t)msqptr, (PZERO - 4) | PCATCH,
6972729Sdfr			    "msgwait", 0);
6982729Sdfr#ifdef MSG_DEBUG_OK
6992729Sdfr			printf("good morning, eval=%d\n", eval);
7002729Sdfr#endif
7012729Sdfr			if (we_own_it)
7022729Sdfr				msqptr->msg_perm.mode &= ~MSG_LOCKED;
7032729Sdfr			if (eval != 0) {
7042729Sdfr#ifdef MSG_DEBUG_OK
7052729Sdfr				printf("msgsnd:  interrupted system call\n");
7062729Sdfr#endif
7072729Sdfr				return(EINTR);
7082729Sdfr			}
7092729Sdfr
7102729Sdfr			/*
7112729Sdfr			 * Make sure that the msq queue still exists
7122729Sdfr			 */
7132729Sdfr
7142729Sdfr			if (msqptr->msg_qbytes == 0) {
7152729Sdfr#ifdef MSG_DEBUG_OK
7162729Sdfr				printf("msqid deleted\n");
7172729Sdfr#endif
7182729Sdfr				return(EIDRM);
7192729Sdfr			}
7202729Sdfr
7212729Sdfr		} else {
7222729Sdfr#ifdef MSG_DEBUG_OK
7232729Sdfr			printf("got all the resources that we need\n");
7242729Sdfr#endif
7252729Sdfr			break;
7262729Sdfr		}
7272729Sdfr	}
7282729Sdfr
7292729Sdfr	/*
7302729Sdfr	 * We have the resources that we need.
7312729Sdfr	 * Make sure!
7322729Sdfr	 */
7332729Sdfr
7342729Sdfr	if (msqptr->msg_perm.mode & MSG_LOCKED)
7352729Sdfr		panic("msg_perm.mode & MSG_LOCKED");
7362729Sdfr	if (segs_needed > nfree_msgmaps)
7372729Sdfr		panic("segs_needed > nfree_msgmaps");
7382729Sdfr	if (msgsz + msqptr->msg_cbytes > msqptr->msg_qbytes)
7392729Sdfr		panic("msgsz + msg_cbytes > msg_qbytes");
7402729Sdfr	if (free_msghdrs == NULL)
7412729Sdfr		panic("no more msghdrs");
7422729Sdfr
7432729Sdfr	/*
7442729Sdfr	 * Re-lock the msqid_ds in case we page-fault when copying in the
7452729Sdfr	 * message
7462729Sdfr	 */
7472729Sdfr
7482729Sdfr	if ((msqptr->msg_perm.mode & MSG_LOCKED) != 0)
7492729Sdfr		panic("msqid_ds is already locked");
7502729Sdfr	msqptr->msg_perm.mode |= MSG_LOCKED;
7512729Sdfr
7522729Sdfr	/*
7532729Sdfr	 * Allocate a message header
7542729Sdfr	 */
7552729Sdfr
7562729Sdfr	msghdr = free_msghdrs;
7572729Sdfr	free_msghdrs = msghdr->msg_next;
7582729Sdfr	msghdr->msg_spot = -1;
7592729Sdfr	msghdr->msg_ts = msgsz;
7602729Sdfr
7612729Sdfr	/*
7622729Sdfr	 * Allocate space for the message
7632729Sdfr	 */
7642729Sdfr
7652729Sdfr	while (segs_needed > 0) {
7662729Sdfr		if (nfree_msgmaps <= 0)
7672729Sdfr			panic("not enough msgmaps");
7682729Sdfr		if (free_msgmaps == -1)
7692729Sdfr			panic("nil free_msgmaps");
7702729Sdfr		next = free_msgmaps;
7712729Sdfr		if (next <= -1)
7722729Sdfr			panic("next too low #1");
7732729Sdfr		if (next >= msginfo.msgseg)
7742729Sdfr			panic("next out of range #1");
7752729Sdfr#ifdef MSG_DEBUG_OK
7762729Sdfr		printf("allocating segment %d to message\n", next);
7772729Sdfr#endif
7782729Sdfr		free_msgmaps = msgmaps[next].next;
7792729Sdfr		nfree_msgmaps--;
7802729Sdfr		msgmaps[next].next = msghdr->msg_spot;
7812729Sdfr		msghdr->msg_spot = next;
7822729Sdfr		segs_needed--;
7832729Sdfr	}
7842729Sdfr
7852729Sdfr	/*
7862729Sdfr	 * Copy in the message type
7872729Sdfr	 */
7882729Sdfr
7892729Sdfr	if ((eval = copyin(user_msgp, &msghdr->msg_type,
7902729Sdfr	    sizeof(msghdr->msg_type))) != 0) {
7912729Sdfr#ifdef MSG_DEBUG_OK
7922729Sdfr		printf("error %d copying the message type\n", eval);
7932729Sdfr#endif
7942729Sdfr		msg_freehdr(msghdr);
7952729Sdfr		msqptr->msg_perm.mode &= ~MSG_LOCKED;
7962729Sdfr		wakeup((caddr_t)msqptr);
7972729Sdfr		return(eval);
7982729Sdfr	}
79917971Sbde	user_msgp = (char *)user_msgp + sizeof(msghdr->msg_type);
8002729Sdfr
8012729Sdfr	/*
8022729Sdfr	 * Validate the message type
8032729Sdfr	 */
8042729Sdfr
8052729Sdfr	if (msghdr->msg_type < 1) {
8062729Sdfr		msg_freehdr(msghdr);
8072729Sdfr		msqptr->msg_perm.mode &= ~MSG_LOCKED;
8082729Sdfr		wakeup((caddr_t)msqptr);
8092729Sdfr#ifdef MSG_DEBUG_OK
8102729Sdfr		printf("mtype (%d) < 1\n", msghdr->msg_type);
8112729Sdfr#endif
8122729Sdfr		return(EINVAL);
8132729Sdfr	}
8142729Sdfr
8152729Sdfr	/*
8162729Sdfr	 * Copy in the message body
8172729Sdfr	 */
8182729Sdfr
8192729Sdfr	next = msghdr->msg_spot;
8202729Sdfr	while (msgsz > 0) {
8212729Sdfr		size_t tlen;
8222729Sdfr		if (msgsz > msginfo.msgssz)
8232729Sdfr			tlen = msginfo.msgssz;
8242729Sdfr		else
8252729Sdfr			tlen = msgsz;
8262729Sdfr		if (next <= -1)
8272729Sdfr			panic("next too low #2");
8282729Sdfr		if (next >= msginfo.msgseg)
8292729Sdfr			panic("next out of range #2");
8302729Sdfr		if ((eval = copyin(user_msgp, &msgpool[next * msginfo.msgssz],
8312729Sdfr		    tlen)) != 0) {
8322729Sdfr#ifdef MSG_DEBUG_OK
8332729Sdfr			printf("error %d copying in message segment\n", eval);
8342729Sdfr#endif
8352729Sdfr			msg_freehdr(msghdr);
8362729Sdfr			msqptr->msg_perm.mode &= ~MSG_LOCKED;
8372729Sdfr			wakeup((caddr_t)msqptr);
8382729Sdfr			return(eval);
8392729Sdfr		}
8402729Sdfr		msgsz -= tlen;
84117971Sbde		user_msgp = (char *)user_msgp + tlen;
8422729Sdfr		next = msgmaps[next].next;
8432729Sdfr	}
8442729Sdfr	if (next != -1)
8452729Sdfr		panic("didn't use all the msg segments");
8462729Sdfr
8472729Sdfr	/*
8482729Sdfr	 * We've got the message.  Unlock the msqid_ds.
8492729Sdfr	 */
8502729Sdfr
8512729Sdfr	msqptr->msg_perm.mode &= ~MSG_LOCKED;
8522729Sdfr
8532729Sdfr	/*
8542729Sdfr	 * Make sure that the msqid_ds is still allocated.
8552729Sdfr	 */
8562729Sdfr
8572729Sdfr	if (msqptr->msg_qbytes == 0) {
8582729Sdfr		msg_freehdr(msghdr);
8592729Sdfr		wakeup((caddr_t)msqptr);
8602729Sdfr		return(EIDRM);
8612729Sdfr	}
8622729Sdfr
8632729Sdfr	/*
8642729Sdfr	 * Put the message into the queue
8652729Sdfr	 */
8662729Sdfr
8672729Sdfr	if (msqptr->msg_first == NULL) {
8682729Sdfr		msqptr->msg_first = msghdr;
8692729Sdfr		msqptr->msg_last = msghdr;
8702729Sdfr	} else {
8712729Sdfr		msqptr->msg_last->msg_next = msghdr;
8722729Sdfr		msqptr->msg_last = msghdr;
8732729Sdfr	}
8742729Sdfr	msqptr->msg_last->msg_next = NULL;
8752729Sdfr
8762729Sdfr	msqptr->msg_cbytes += msghdr->msg_ts;
8772729Sdfr	msqptr->msg_qnum++;
8782729Sdfr	msqptr->msg_lspid = p->p_pid;
87934961Sphk	msqptr->msg_stime = time_second;
8802729Sdfr
8812729Sdfr	wakeup((caddr_t)msqptr);
88230994Sphk	p->p_retval[0] = 0;
8832729Sdfr	return(0);
8842729Sdfr}
8852729Sdfr
88612866Speter#ifndef _SYS_SYSPROTO_H_
8872729Sdfrstruct msgrcv_args {
8882729Sdfr	int	msqid;
8892729Sdfr	void	*msgp;
8902729Sdfr	size_t	msgsz;
8912729Sdfr	long	msgtyp;
8922729Sdfr	int	msgflg;
8932729Sdfr};
89412866Speter#endif
8952729Sdfr
89612866Speterint
89730994Sphkmsgrcv(p, uap)
8982729Sdfr	struct proc *p;
8992729Sdfr	register struct msgrcv_args *uap;
9002729Sdfr{
9012729Sdfr	int msqid = uap->msqid;
9022729Sdfr	void *user_msgp = uap->msgp;
9032729Sdfr	size_t msgsz = uap->msgsz;
9042729Sdfr	long msgtyp = uap->msgtyp;
9052729Sdfr	int msgflg = uap->msgflg;
9062729Sdfr	size_t len;
9072729Sdfr	register struct msqid_ds *msqptr;
9082729Sdfr	register struct msg *msghdr;
9092729Sdfr	int eval;
9102729Sdfr	short next;
9112729Sdfr
9122729Sdfr#ifdef MSG_DEBUG_OK
9132729Sdfr	printf("call to msgrcv(%d, 0x%x, %d, %ld, %d)\n", msqid, user_msgp,
9142729Sdfr	    msgsz, msgtyp, msgflg);
9152729Sdfr#endif
9162729Sdfr
91768024Srwatson	if (!jail_sysvipc_allowed && p->p_prison != NULL)
91868024Srwatson		return (ENOSYS);
91968024Srwatson
9202729Sdfr	msqid = IPCID_TO_IX(msqid);
9212729Sdfr
9222729Sdfr	if (msqid < 0 || msqid >= msginfo.msgmni) {
9232729Sdfr#ifdef MSG_DEBUG_OK
9242729Sdfr		printf("msqid (%d) out of range (0<=msqid<%d)\n", msqid,
9252729Sdfr		    msginfo.msgmni);
9262729Sdfr#endif
9272729Sdfr		return(EINVAL);
9282729Sdfr	}
9292729Sdfr
9302729Sdfr	msqptr = &msqids[msqid];
9312729Sdfr	if (msqptr->msg_qbytes == 0) {
9322729Sdfr#ifdef MSG_DEBUG_OK
9332729Sdfr		printf("no such message queue id\n");
9342729Sdfr#endif
9352729Sdfr		return(EINVAL);
9362729Sdfr	}
9372729Sdfr	if (msqptr->msg_perm.seq != IPCID_TO_SEQ(uap->msqid)) {
9382729Sdfr#ifdef MSG_DEBUG_OK
9392729Sdfr		printf("wrong sequence number\n");
9402729Sdfr#endif
9412729Sdfr		return(EINVAL);
9422729Sdfr	}
9432729Sdfr
94446116Sphk	if ((eval = ipcperm(p, &msqptr->msg_perm, IPC_R))) {
9452729Sdfr#ifdef MSG_DEBUG_OK
9462729Sdfr		printf("requester doesn't have read access\n");
9472729Sdfr#endif
9482729Sdfr		return(eval);
9492729Sdfr	}
9502729Sdfr
9512729Sdfr	msghdr = NULL;
9522729Sdfr	while (msghdr == NULL) {
9532729Sdfr		if (msgtyp == 0) {
9542729Sdfr			msghdr = msqptr->msg_first;
9552729Sdfr			if (msghdr != NULL) {
9562729Sdfr				if (msgsz < msghdr->msg_ts &&
9572729Sdfr				    (msgflg & MSG_NOERROR) == 0) {
9582729Sdfr#ifdef MSG_DEBUG_OK
9592729Sdfr					printf("first message on the queue is too big (want %d, got %d)\n",
9602729Sdfr					    msgsz, msghdr->msg_ts);
9612729Sdfr#endif
9622729Sdfr					return(E2BIG);
9632729Sdfr				}
9642729Sdfr				if (msqptr->msg_first == msqptr->msg_last) {
9652729Sdfr					msqptr->msg_first = NULL;
9662729Sdfr					msqptr->msg_last = NULL;
9672729Sdfr				} else {
9682729Sdfr					msqptr->msg_first = msghdr->msg_next;
9692729Sdfr					if (msqptr->msg_first == NULL)
9702729Sdfr						panic("msg_first/last screwed up #1");
9712729Sdfr				}
9722729Sdfr			}
9732729Sdfr		} else {
9742729Sdfr			struct msg *previous;
9752729Sdfr			struct msg **prev;
9762729Sdfr
9772729Sdfr			previous = NULL;
9782729Sdfr			prev = &(msqptr->msg_first);
9792729Sdfr			while ((msghdr = *prev) != NULL) {
9802729Sdfr				/*
9812729Sdfr				 * Is this message's type an exact match or is
9822729Sdfr				 * this message's type less than or equal to
9832729Sdfr				 * the absolute value of a negative msgtyp?
9842729Sdfr				 * Note that the second half of this test can
9852729Sdfr				 * NEVER be true if msgtyp is positive since
9862729Sdfr				 * msg_type is always positive!
9872729Sdfr				 */
9882729Sdfr
9892729Sdfr				if (msgtyp == msghdr->msg_type ||
9902729Sdfr				    msghdr->msg_type <= -msgtyp) {
9912729Sdfr#ifdef MSG_DEBUG_OK
9922729Sdfr					printf("found message type %d, requested %d\n",
9932729Sdfr					    msghdr->msg_type, msgtyp);
9942729Sdfr#endif
9952729Sdfr					if (msgsz < msghdr->msg_ts &&
9962729Sdfr					    (msgflg & MSG_NOERROR) == 0) {
9972729Sdfr#ifdef MSG_DEBUG_OK
9982729Sdfr						printf("requested message on the queue is too big (want %d, got %d)\n",
9992729Sdfr						    msgsz, msghdr->msg_ts);
10002729Sdfr#endif
10012729Sdfr						return(E2BIG);
10022729Sdfr					}
10032729Sdfr					*prev = msghdr->msg_next;
10042729Sdfr					if (msghdr == msqptr->msg_last) {
10052729Sdfr						if (previous == NULL) {
10062729Sdfr							if (prev !=
10072729Sdfr							    &msqptr->msg_first)
10082729Sdfr								panic("msg_first/last screwed up #2");
10092729Sdfr							msqptr->msg_first =
10102729Sdfr							    NULL;
10112729Sdfr							msqptr->msg_last =
10122729Sdfr							    NULL;
10132729Sdfr						} else {
10142729Sdfr							if (prev ==
10152729Sdfr							    &msqptr->msg_first)
10162729Sdfr								panic("msg_first/last screwed up #3");
10172729Sdfr							msqptr->msg_last =
10182729Sdfr							    previous;
10192729Sdfr						}
10202729Sdfr					}
10212729Sdfr					break;
10222729Sdfr				}
10232729Sdfr				previous = msghdr;
10242729Sdfr				prev = &(msghdr->msg_next);
10252729Sdfr			}
10262729Sdfr		}
10272729Sdfr
10282729Sdfr		/*
10292729Sdfr		 * We've either extracted the msghdr for the appropriate
10302729Sdfr		 * message or there isn't one.
10312729Sdfr		 * If there is one then bail out of this loop.
10322729Sdfr		 */
10332729Sdfr
10342729Sdfr		if (msghdr != NULL)
10352729Sdfr			break;
10362729Sdfr
10372729Sdfr		/*
10382729Sdfr		 * Hmph!  No message found.  Does the user want to wait?
10392729Sdfr		 */
10402729Sdfr
10412729Sdfr		if ((msgflg & IPC_NOWAIT) != 0) {
10422729Sdfr#ifdef MSG_DEBUG_OK
10432729Sdfr			printf("no appropriate message found (msgtyp=%d)\n",
10442729Sdfr			    msgtyp);
10452729Sdfr#endif
10462729Sdfr			/* The SVID says to return ENOMSG. */
10472729Sdfr#ifdef ENOMSG
10482729Sdfr			return(ENOMSG);
10492729Sdfr#else
10502729Sdfr			/* Unfortunately, BSD doesn't define that code yet! */
10512729Sdfr			return(EAGAIN);
10522729Sdfr#endif
10532729Sdfr		}
10542729Sdfr
10552729Sdfr		/*
10562729Sdfr		 * Wait for something to happen
10572729Sdfr		 */
10582729Sdfr
10592729Sdfr#ifdef MSG_DEBUG_OK
10602729Sdfr		printf("msgrcv:  goodnight\n");
10612729Sdfr#endif
10622729Sdfr		eval = tsleep((caddr_t)msqptr, (PZERO - 4) | PCATCH, "msgwait",
10632729Sdfr		    0);
10642729Sdfr#ifdef MSG_DEBUG_OK
10652729Sdfr		printf("msgrcv:  good morning (eval=%d)\n", eval);
10662729Sdfr#endif
10672729Sdfr
10682729Sdfr		if (eval != 0) {
10692729Sdfr#ifdef MSG_DEBUG_OK
10702729Sdfr			printf("msgsnd:  interrupted system call\n");
10712729Sdfr#endif
10722729Sdfr			return(EINTR);
10732729Sdfr		}
10742729Sdfr
10752729Sdfr		/*
10762729Sdfr		 * Make sure that the msq queue still exists
10772729Sdfr		 */
10782729Sdfr
10792729Sdfr		if (msqptr->msg_qbytes == 0 ||
10802729Sdfr		    msqptr->msg_perm.seq != IPCID_TO_SEQ(uap->msqid)) {
10812729Sdfr#ifdef MSG_DEBUG_OK
10822729Sdfr			printf("msqid deleted\n");
10832729Sdfr#endif
10842729Sdfr			return(EIDRM);
10852729Sdfr		}
10862729Sdfr	}
10872729Sdfr
10882729Sdfr	/*
10892729Sdfr	 * Return the message to the user.
10902729Sdfr	 *
10912729Sdfr	 * First, do the bookkeeping (before we risk being interrupted).
10922729Sdfr	 */
10932729Sdfr
10942729Sdfr	msqptr->msg_cbytes -= msghdr->msg_ts;
10952729Sdfr	msqptr->msg_qnum--;
10962729Sdfr	msqptr->msg_lrpid = p->p_pid;
109734961Sphk	msqptr->msg_rtime = time_second;
10982729Sdfr
10992729Sdfr	/*
11002729Sdfr	 * Make msgsz the actual amount that we'll be returning.
11012729Sdfr	 * Note that this effectively truncates the message if it is too long
11022729Sdfr	 * (since msgsz is never increased).
11032729Sdfr	 */
11042729Sdfr
11052729Sdfr#ifdef MSG_DEBUG_OK
11062729Sdfr	printf("found a message, msgsz=%d, msg_ts=%d\n", msgsz,
11072729Sdfr	    msghdr->msg_ts);
11082729Sdfr#endif
11092729Sdfr	if (msgsz > msghdr->msg_ts)
11102729Sdfr		msgsz = msghdr->msg_ts;
11112729Sdfr
11122729Sdfr	/*
11132729Sdfr	 * Return the type to the user.
11142729Sdfr	 */
11152729Sdfr
11162729Sdfr	eval = copyout((caddr_t)&(msghdr->msg_type), user_msgp,
11172729Sdfr	    sizeof(msghdr->msg_type));
11182729Sdfr	if (eval != 0) {
11192729Sdfr#ifdef MSG_DEBUG_OK
11202729Sdfr		printf("error (%d) copying out message type\n", eval);
11212729Sdfr#endif
11222729Sdfr		msg_freehdr(msghdr);
11232729Sdfr		wakeup((caddr_t)msqptr);
11242729Sdfr		return(eval);
11252729Sdfr	}
112617971Sbde	user_msgp = (char *)user_msgp + sizeof(msghdr->msg_type);
11272729Sdfr
11282729Sdfr	/*
11292729Sdfr	 * Return the segments to the user
11302729Sdfr	 */
11312729Sdfr
11322729Sdfr	next = msghdr->msg_spot;
11332729Sdfr	for (len = 0; len < msgsz; len += msginfo.msgssz) {
11342729Sdfr		size_t tlen;
11352729Sdfr
113645921Ssada		if (msgsz - len > msginfo.msgssz)
11372729Sdfr			tlen = msginfo.msgssz;
11382729Sdfr		else
113945921Ssada			tlen = msgsz - len;
11402729Sdfr		if (next <= -1)
11412729Sdfr			panic("next too low #3");
11422729Sdfr		if (next >= msginfo.msgseg)
11432729Sdfr			panic("next out of range #3");
11442729Sdfr		eval = copyout((caddr_t)&msgpool[next * msginfo.msgssz],
11452729Sdfr		    user_msgp, tlen);
11462729Sdfr		if (eval != 0) {
11472729Sdfr#ifdef MSG_DEBUG_OK
11482729Sdfr			printf("error (%d) copying out message segment\n",
11492729Sdfr			    eval);
11502729Sdfr#endif
11512729Sdfr			msg_freehdr(msghdr);
11522729Sdfr			wakeup((caddr_t)msqptr);
11532729Sdfr			return(eval);
11542729Sdfr		}
115517971Sbde		user_msgp = (char *)user_msgp + tlen;
11562729Sdfr		next = msgmaps[next].next;
11572729Sdfr	}
11582729Sdfr
11592729Sdfr	/*
11602729Sdfr	 * Done, return the actual number of bytes copied out.
11612729Sdfr	 */
11622729Sdfr
11632729Sdfr	msg_freehdr(msghdr);
11642729Sdfr	wakeup((caddr_t)msqptr);
116530994Sphk	p->p_retval[0] = msgsz;
11662729Sdfr	return(0);
11672729Sdfr}
1168