sysv_msg.c revision 17971
117971Sbde/*	$Id: sysv_msg.c,v 1.12 1996/01/05 16:37:56 wollman Exp $ */
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
2213255Swollman#include "opt_sysvipc.h"
2313255Swollman
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>
3011626Sbde#include <sys/sysent.h>
312729Sdfr
3210653Sdgstatic void msginit __P((void *));
3310358SjulianSYSINIT(sysv_msg, SI_SUB_SYSV_MSG, SI_ORDER_FIRST, msginit, NULL)
3410358Sjulian
352729Sdfr#define MSG_DEBUG
362729Sdfr#undef MSG_DEBUG_OK
372729Sdfr
3812866Speter#ifndef _SYS_SYSPROTO_H_
3911626Sbdestruct msgctl_args;
4012866Speterint msgctl __P((struct proc *p, struct msgctl_args *uap, int *retval));
4111626Sbdestruct msgget_args;
4212866Speterint msgget __P((struct proc *p, struct msgget_args *uap, int *retval));
4311626Sbdestruct msgsnd_args;
4412866Speterint msgsnd __P((struct proc *p, struct msgsnd_args *uap, int *retval));
4511626Sbdestruct msgrcv_args;
4612866Speterint msgrcv __P((struct proc *p, struct msgrcv_args *uap, int *retval));
4712866Speter#endif
4811626Sbdestatic void msg_freehdr __P((struct msg *msghdr));
492729Sdfr
5011626Sbde/* XXX casting to (sy_call_t *) is bogus, as usual. */
5112819Sphkstatic sy_call_t *msgcalls[] = {
5211626Sbde	(sy_call_t *)msgctl, (sy_call_t *)msgget,
5311626Sbde	(sy_call_t *)msgsnd, (sy_call_t *)msgrcv
5411626Sbde};
552729Sdfr
5612819Sphkstatic int nfree_msgmaps;	/* # of free map entries */
5712819Sphkstatic short free_msgmaps;	/* head of linked list of free map entries */
5812819Sphkstatic struct msg *free_msghdrs;	/* list of free msg headers */
599759Sbdechar *msgpool;			/* MSGMAX byte long msg buffer pool */
609759Sbdestruct msgmap *msgmaps;		/* MSGSEG msgmap structures */
619759Sbdestruct msg *msghdrs;		/* MSGTQL msg headers */
629759Sbdestruct msqid_ds *msqids;	/* MSGMNI msqid_ds struct's */
632729Sdfr
642836Sdgvoid
6511626Sbdemsginit(dummy)
6611626Sbde	void *dummy;
672729Sdfr{
682729Sdfr	register int i;
692729Sdfr
702729Sdfr	/*
712729Sdfr	 * msginfo.msgssz should be a power of two for efficiency reasons.
722729Sdfr	 * It is also pretty silly if msginfo.msgssz is less than 8
732729Sdfr	 * or greater than about 256 so ...
742729Sdfr	 */
752729Sdfr
762729Sdfr	i = 8;
772729Sdfr	while (i < 1024 && i != msginfo.msgssz)
782729Sdfr		i <<= 1;
792729Sdfr    	if (i != msginfo.msgssz) {
802729Sdfr		printf("msginfo.msgssz=%d (0x%x)\n", msginfo.msgssz,
812729Sdfr		    msginfo.msgssz);
822729Sdfr		panic("msginfo.msgssz not a small power of 2");
832729Sdfr	}
842729Sdfr
852729Sdfr	if (msginfo.msgseg > 32767) {
862729Sdfr		printf("msginfo.msgseg=%d\n", msginfo.msgseg);
872729Sdfr		panic("msginfo.msgseg > 32767");
882729Sdfr	}
892729Sdfr
902729Sdfr	if (msgmaps == NULL)
912729Sdfr		panic("msgmaps is NULL");
922729Sdfr
932729Sdfr	for (i = 0; i < msginfo.msgseg; i++) {
942729Sdfr		if (i > 0)
952729Sdfr			msgmaps[i-1].next = i;
962729Sdfr		msgmaps[i].next = -1;	/* implies entry is available */
972729Sdfr	}
982729Sdfr	free_msgmaps = 0;
992729Sdfr	nfree_msgmaps = msginfo.msgseg;
1002729Sdfr
1012729Sdfr	if (msghdrs == NULL)
1022729Sdfr		panic("msghdrs is NULL");
1032729Sdfr
1042729Sdfr	for (i = 0; i < msginfo.msgtql; i++) {
1052729Sdfr		msghdrs[i].msg_type = 0;
1062729Sdfr		if (i > 0)
1072729Sdfr			msghdrs[i-1].msg_next = &msghdrs[i];
1082729Sdfr		msghdrs[i].msg_next = NULL;
1092729Sdfr    	}
1102729Sdfr	free_msghdrs = &msghdrs[0];
1112729Sdfr
1122729Sdfr	if (msqids == NULL)
1132729Sdfr		panic("msqids is NULL");
1142729Sdfr
1152729Sdfr	for (i = 0; i < msginfo.msgmni; i++) {
1162729Sdfr		msqids[i].msg_qbytes = 0;	/* implies entry is available */
1172729Sdfr		msqids[i].msg_perm.seq = 0;	/* reset to a known value */
1182729Sdfr	}
1192729Sdfr}
1202729Sdfr
1212729Sdfr/*
1222729Sdfr * Entry point for all MSG calls
1232729Sdfr */
1242729Sdfrint
1252729Sdfrmsgsys(p, uap, retval)
12611626Sbde	struct proc *p;
12711626Sbde	/* XXX actually varargs. */
12811626Sbde	struct msgsys_args /* {
12911626Sbde		u_int	which;
13011626Sbde		int	a2;
13111626Sbde		int	a3;
13211626Sbde		int	a4;
13311626Sbde		int	a5;
13411626Sbde		int	a6;
13511626Sbde	} */ *uap;
1362729Sdfr	int *retval;
1372729Sdfr{
1382729Sdfr
1392729Sdfr	if (uap->which >= sizeof(msgcalls)/sizeof(msgcalls[0]))
1402729Sdfr		return (EINVAL);
14111626Sbde	return ((*msgcalls[uap->which])(p, &uap->a2, retval));
1422729Sdfr}
1432729Sdfr
1442729Sdfrstatic void
1452729Sdfrmsg_freehdr(msghdr)
1462729Sdfr	struct msg *msghdr;
1472729Sdfr{
1482729Sdfr	while (msghdr->msg_ts > 0) {
1492729Sdfr		short next;
1502729Sdfr		if (msghdr->msg_spot < 0 || msghdr->msg_spot >= msginfo.msgseg)
1512729Sdfr			panic("msghdr->msg_spot out of range");
1522729Sdfr		next = msgmaps[msghdr->msg_spot].next;
1532729Sdfr		msgmaps[msghdr->msg_spot].next = free_msgmaps;
1542729Sdfr		free_msgmaps = msghdr->msg_spot;
1552729Sdfr		nfree_msgmaps++;
1562729Sdfr		msghdr->msg_spot = next;
1572729Sdfr		if (msghdr->msg_ts >= msginfo.msgssz)
1582729Sdfr			msghdr->msg_ts -= msginfo.msgssz;
1592729Sdfr		else
1602729Sdfr			msghdr->msg_ts = 0;
1612729Sdfr	}
1622729Sdfr	if (msghdr->msg_spot != -1)
1632729Sdfr		panic("msghdr->msg_spot != -1");
1642729Sdfr	msghdr->msg_next = free_msghdrs;
1652729Sdfr	free_msghdrs = msghdr;
1662729Sdfr}
1672729Sdfr
16812866Speter#ifndef _SYS_SYSPROTO_H_
1692729Sdfrstruct msgctl_args {
1702729Sdfr	int	msqid;
1712729Sdfr	int	cmd;
17212866Speter	struct	msqid_ds *buf;
1732729Sdfr};
17412866Speter#endif
1752729Sdfr
17612866Speterint
1772729Sdfrmsgctl(p, uap, retval)
1782729Sdfr	struct proc *p;
1792729Sdfr	register struct msgctl_args *uap;
1802729Sdfr	int *retval;
1812729Sdfr{
1822729Sdfr	int msqid = uap->msqid;
1832729Sdfr	int cmd = uap->cmd;
18412866Speter	struct msqid_ds *user_msqptr = uap->buf;
1852729Sdfr	struct ucred *cred = p->p_ucred;
1863308Sphk	int rval, eval;
1872729Sdfr	struct msqid_ds msqbuf;
1882729Sdfr	register struct msqid_ds *msqptr;
1892729Sdfr
1902729Sdfr#ifdef MSG_DEBUG_OK
1912729Sdfr	printf("call to msgctl(%d, %d, 0x%x)\n", msqid, cmd, user_msqptr);
1922729Sdfr#endif
1932729Sdfr
1942729Sdfr	msqid = IPCID_TO_IX(msqid);
1952729Sdfr
1962729Sdfr	if (msqid < 0 || msqid >= msginfo.msgmni) {
1972729Sdfr#ifdef MSG_DEBUG_OK
1982729Sdfr		printf("msqid (%d) out of range (0<=msqid<%d)\n", msqid,
1992729Sdfr		    msginfo.msgmni);
2002729Sdfr#endif
2012729Sdfr		return(EINVAL);
2022729Sdfr	}
2032729Sdfr
2042729Sdfr	msqptr = &msqids[msqid];
2052729Sdfr
2062729Sdfr	if (msqptr->msg_qbytes == 0) {
2072729Sdfr#ifdef MSG_DEBUG_OK
2082729Sdfr		printf("no such msqid\n");
2092729Sdfr#endif
2102729Sdfr		return(EINVAL);
2112729Sdfr	}
2122729Sdfr	if (msqptr->msg_perm.seq != IPCID_TO_SEQ(uap->msqid)) {
2132729Sdfr#ifdef MSG_DEBUG_OK
2142729Sdfr		printf("wrong sequence number\n");
2152729Sdfr#endif
2162729Sdfr		return(EINVAL);
2172729Sdfr	}
2182729Sdfr
2192729Sdfr	eval = 0;
2202729Sdfr	rval = 0;
2212729Sdfr
2222729Sdfr	switch (cmd) {
2232729Sdfr
2242729Sdfr	case IPC_RMID:
2252729Sdfr	{
2262729Sdfr		struct msg *msghdr;
2272729Sdfr		if ((eval = ipcperm(cred, &msqptr->msg_perm, IPC_M)))
2282729Sdfr			return(eval);
2292729Sdfr		/* Free the message headers */
2302729Sdfr		msghdr = msqptr->msg_first;
2312729Sdfr		while (msghdr != NULL) {
2322729Sdfr			struct msg *msghdr_tmp;
2332729Sdfr
2342729Sdfr			/* Free the segments of each message */
2352729Sdfr			msqptr->msg_cbytes -= msghdr->msg_ts;
2362729Sdfr			msqptr->msg_qnum--;
2372729Sdfr			msghdr_tmp = msghdr;
2382729Sdfr			msghdr = msghdr->msg_next;
2392729Sdfr			msg_freehdr(msghdr_tmp);
2402729Sdfr		}
2412729Sdfr
2422729Sdfr		if (msqptr->msg_cbytes != 0)
2432729Sdfr			panic("msg_cbytes is screwed up");
2442729Sdfr		if (msqptr->msg_qnum != 0)
2452729Sdfr			panic("msg_qnum is screwed up");
2462729Sdfr
2472729Sdfr		msqptr->msg_qbytes = 0;	/* Mark it as free */
2482729Sdfr
2492729Sdfr		wakeup((caddr_t)msqptr);
2502729Sdfr	}
2512729Sdfr
2522729Sdfr		break;
2532729Sdfr
2542729Sdfr	case IPC_SET:
2552729Sdfr		if ((eval = ipcperm(cred, &msqptr->msg_perm, IPC_M)))
2562729Sdfr			return(eval);
2572729Sdfr		if ((eval = copyin(user_msqptr, &msqbuf, sizeof(msqbuf))) != 0)
2582729Sdfr			return(eval);
2592729Sdfr		if (msqbuf.msg_qbytes > msqptr->msg_qbytes && cred->cr_uid != 0)
2602729Sdfr			return(EPERM);
2612729Sdfr		if (msqbuf.msg_qbytes > msginfo.msgmnb) {
2622729Sdfr#ifdef MSG_DEBUG_OK
2632729Sdfr			printf("can't increase msg_qbytes beyond %d (truncating)\n",
2642729Sdfr			    msginfo.msgmnb);
2652729Sdfr#endif
2662729Sdfr			msqbuf.msg_qbytes = msginfo.msgmnb;	/* silently restrict qbytes to system limit */
2672729Sdfr		}
2682729Sdfr		if (msqbuf.msg_qbytes == 0) {
2692729Sdfr#ifdef MSG_DEBUG_OK
2702729Sdfr			printf("can't reduce msg_qbytes to 0\n");
2712729Sdfr#endif
2722729Sdfr			return(EINVAL);		/* non-standard errno! */
2732729Sdfr		}
2742729Sdfr		msqptr->msg_perm.uid = msqbuf.msg_perm.uid;	/* change the owner */
2752729Sdfr		msqptr->msg_perm.gid = msqbuf.msg_perm.gid;	/* change the owner */
2762729Sdfr		msqptr->msg_perm.mode = (msqptr->msg_perm.mode & ~0777) |
2772729Sdfr		    (msqbuf.msg_perm.mode & 0777);
2782729Sdfr		msqptr->msg_qbytes = msqbuf.msg_qbytes;
2792729Sdfr		msqptr->msg_ctime = time.tv_sec;
2802729Sdfr		break;
2812729Sdfr
2822729Sdfr	case IPC_STAT:
2832729Sdfr		if ((eval = ipcperm(cred, &msqptr->msg_perm, IPC_R))) {
2842729Sdfr#ifdef MSG_DEBUG_OK
2852729Sdfr			printf("requester doesn't have read access\n");
2862729Sdfr#endif
2872729Sdfr			return(eval);
2882729Sdfr		}
2892729Sdfr		eval = copyout((caddr_t)msqptr, user_msqptr,
2902729Sdfr		    sizeof(struct msqid_ds));
2912729Sdfr		break;
2922729Sdfr
2932729Sdfr	default:
2942729Sdfr#ifdef MSG_DEBUG_OK
2952729Sdfr		printf("invalid command %d\n", cmd);
2962729Sdfr#endif
2972729Sdfr		return(EINVAL);
2982729Sdfr	}
2992729Sdfr
3002729Sdfr	if (eval == 0)
3012729Sdfr		*retval = rval;
3022729Sdfr	return(eval);
3032729Sdfr}
3042729Sdfr
30512866Speter#ifndef _SYS_SYSPROTO_H_
3062729Sdfrstruct msgget_args {
3072729Sdfr	key_t	key;
3082729Sdfr	int	msgflg;
3092729Sdfr};
31012866Speter#endif
3112729Sdfr
31212866Speterint
3132729Sdfrmsgget(p, uap, retval)
3142729Sdfr	struct proc *p;
3152729Sdfr	register struct msgget_args *uap;
3162729Sdfr	int *retval;
3172729Sdfr{
3182729Sdfr	int msqid, eval;
3192729Sdfr	int key = uap->key;
3202729Sdfr	int msgflg = uap->msgflg;
3212729Sdfr	struct ucred *cred = p->p_ucred;
3222836Sdg	register struct msqid_ds *msqptr = NULL;
3232729Sdfr
3242729Sdfr#ifdef MSG_DEBUG_OK
3252729Sdfr	printf("msgget(0x%x, 0%o)\n", key, msgflg);
3262729Sdfr#endif
3272729Sdfr
3282729Sdfr	if (key != IPC_PRIVATE) {
3292729Sdfr		for (msqid = 0; msqid < msginfo.msgmni; msqid++) {
3302729Sdfr			msqptr = &msqids[msqid];
3312729Sdfr			if (msqptr->msg_qbytes != 0 &&
3322729Sdfr			    msqptr->msg_perm.key == key)
3332729Sdfr				break;
3342729Sdfr		}
3352729Sdfr		if (msqid < msginfo.msgmni) {
3362729Sdfr#ifdef MSG_DEBUG_OK
3372729Sdfr			printf("found public key\n");
3382729Sdfr#endif
3392729Sdfr			if ((msgflg & IPC_CREAT) && (msgflg & IPC_EXCL)) {
3402729Sdfr#ifdef MSG_DEBUG_OK
3412729Sdfr				printf("not exclusive\n");
3422729Sdfr#endif
3432729Sdfr				return(EEXIST);
3442729Sdfr			}
3452729Sdfr			if ((eval = ipcperm(cred, &msqptr->msg_perm, msgflg & 0700 ))) {
3462729Sdfr#ifdef MSG_DEBUG_OK
3472729Sdfr				printf("requester doesn't have 0%o access\n",
3482729Sdfr				    msgflg & 0700);
3492729Sdfr#endif
3502729Sdfr				return(eval);
3512729Sdfr			}
3522729Sdfr			goto found;
3532729Sdfr		}
3542729Sdfr	}
3552729Sdfr
3562729Sdfr#ifdef MSG_DEBUG_OK
3572729Sdfr	printf("need to allocate the msqid_ds\n");
3582729Sdfr#endif
3592729Sdfr	if (key == IPC_PRIVATE || (msgflg & IPC_CREAT)) {
3602729Sdfr		for (msqid = 0; msqid < msginfo.msgmni; msqid++) {
3612729Sdfr			/*
3622729Sdfr			 * Look for an unallocated and unlocked msqid_ds.
3632729Sdfr			 * msqid_ds's can be locked by msgsnd or msgrcv while
3642729Sdfr			 * they are copying the message in/out.  We can't
3652729Sdfr			 * re-use the entry until they release it.
3662729Sdfr			 */
3672729Sdfr			msqptr = &msqids[msqid];
3682729Sdfr			if (msqptr->msg_qbytes == 0 &&
3692729Sdfr			    (msqptr->msg_perm.mode & MSG_LOCKED) == 0)
3702729Sdfr				break;
3712729Sdfr		}
3722729Sdfr		if (msqid == msginfo.msgmni) {
3732729Sdfr#ifdef MSG_DEBUG_OK
3742729Sdfr			printf("no more msqid_ds's available\n");
3752729Sdfr#endif
3768876Srgrimes			return(ENOSPC);
3772729Sdfr		}
3782729Sdfr#ifdef MSG_DEBUG_OK
3792729Sdfr		printf("msqid %d is available\n", msqid);
3802729Sdfr#endif
3812729Sdfr		msqptr->msg_perm.key = key;
3822729Sdfr		msqptr->msg_perm.cuid = cred->cr_uid;
3832729Sdfr		msqptr->msg_perm.uid = cred->cr_uid;
3842729Sdfr		msqptr->msg_perm.cgid = cred->cr_gid;
3852729Sdfr		msqptr->msg_perm.gid = cred->cr_gid;
3862729Sdfr		msqptr->msg_perm.mode = (msgflg & 0777);
3872729Sdfr		/* Make sure that the returned msqid is unique */
3882729Sdfr		msqptr->msg_perm.seq++;
3892729Sdfr		msqptr->msg_first = NULL;
3902729Sdfr		msqptr->msg_last = NULL;
3912729Sdfr		msqptr->msg_cbytes = 0;
3922729Sdfr		msqptr->msg_qnum = 0;
3932729Sdfr		msqptr->msg_qbytes = msginfo.msgmnb;
3942729Sdfr		msqptr->msg_lspid = 0;
3952729Sdfr		msqptr->msg_lrpid = 0;
3962729Sdfr		msqptr->msg_stime = 0;
3972729Sdfr		msqptr->msg_rtime = 0;
3982729Sdfr		msqptr->msg_ctime = time.tv_sec;
3992729Sdfr	} else {
4002729Sdfr#ifdef MSG_DEBUG_OK
4012729Sdfr		printf("didn't find it and wasn't asked to create it\n");
4022729Sdfr#endif
4032729Sdfr		return(ENOENT);
4042729Sdfr	}
4052729Sdfr
4062729Sdfrfound:
4072729Sdfr	/* Construct the unique msqid */
4082729Sdfr	*retval = IXSEQ_TO_IPCID(msqid, msqptr->msg_perm);
4092729Sdfr	return(0);
4102729Sdfr}
4112729Sdfr
41212866Speter#ifndef _SYS_SYSPROTO_H_
4132729Sdfrstruct msgsnd_args {
4142729Sdfr	int	msqid;
41512866Speter	void	*msgp;
4162729Sdfr	size_t	msgsz;
4172729Sdfr	int	msgflg;
4182729Sdfr};
41912866Speter#endif
4202729Sdfr
42112866Speterint
4222729Sdfrmsgsnd(p, uap, retval)
4232729Sdfr	struct proc *p;
4242729Sdfr	register struct msgsnd_args *uap;
4252729Sdfr	int *retval;
4262729Sdfr{
4272729Sdfr	int msqid = uap->msqid;
42812866Speter	void *user_msgp = uap->msgp;
4292729Sdfr	size_t msgsz = uap->msgsz;
4302729Sdfr	int msgflg = uap->msgflg;
4312729Sdfr	int segs_needed, eval;
4322729Sdfr	struct ucred *cred = p->p_ucred;
4332729Sdfr	register struct msqid_ds *msqptr;
4342729Sdfr	register struct msg *msghdr;
4352729Sdfr	short next;
4362729Sdfr
4372729Sdfr#ifdef MSG_DEBUG_OK
4382729Sdfr	printf("call to msgsnd(%d, 0x%x, %d, %d)\n", msqid, user_msgp, msgsz,
4392729Sdfr	    msgflg);
4402729Sdfr#endif
4412729Sdfr
4422729Sdfr	msqid = IPCID_TO_IX(msqid);
4432729Sdfr
4442729Sdfr	if (msqid < 0 || msqid >= msginfo.msgmni) {
4452729Sdfr#ifdef MSG_DEBUG_OK
4462729Sdfr		printf("msqid (%d) out of range (0<=msqid<%d)\n", msqid,
4472729Sdfr		    msginfo.msgmni);
4482729Sdfr#endif
4492729Sdfr		return(EINVAL);
4502729Sdfr	}
4512729Sdfr
4522729Sdfr	msqptr = &msqids[msqid];
4532729Sdfr	if (msqptr->msg_qbytes == 0) {
4542729Sdfr#ifdef MSG_DEBUG_OK
4552729Sdfr		printf("no such message queue id\n");
4562729Sdfr#endif
4572729Sdfr		return(EINVAL);
4582729Sdfr	}
4592729Sdfr	if (msqptr->msg_perm.seq != IPCID_TO_SEQ(uap->msqid)) {
4602729Sdfr#ifdef MSG_DEBUG_OK
4612729Sdfr		printf("wrong sequence number\n");
4622729Sdfr#endif
4632729Sdfr		return(EINVAL);
4642729Sdfr	}
4652729Sdfr
4662729Sdfr	if ((eval = ipcperm(cred, &msqptr->msg_perm, IPC_W))) {
4672729Sdfr#ifdef MSG_DEBUG_OK
4682729Sdfr		printf("requester doesn't have write access\n");
4692729Sdfr#endif
4702729Sdfr		return(eval);
4712729Sdfr	}
4722729Sdfr
4732729Sdfr	segs_needed = (msgsz + msginfo.msgssz - 1) / msginfo.msgssz;
4742729Sdfr#ifdef MSG_DEBUG_OK
4752729Sdfr	printf("msgsz=%d, msgssz=%d, segs_needed=%d\n", msgsz, msginfo.msgssz,
4762729Sdfr	    segs_needed);
4772729Sdfr#endif
4782729Sdfr	for (;;) {
4792729Sdfr		int need_more_resources = 0;
4802729Sdfr
4812729Sdfr		/*
4822729Sdfr		 * check msgsz
4832729Sdfr		 * (inside this loop in case msg_qbytes changes while we sleep)
4842729Sdfr		 */
4852729Sdfr
4862836Sdg		if (msgsz > msqptr->msg_qbytes) {
4872729Sdfr#ifdef MSG_DEBUG_OK
4882729Sdfr			printf("msgsz > msqptr->msg_qbytes\n");
4892729Sdfr#endif
4902729Sdfr			return(EINVAL);
4912729Sdfr		}
4922729Sdfr
4932729Sdfr		if (msqptr->msg_perm.mode & MSG_LOCKED) {
4942729Sdfr#ifdef MSG_DEBUG_OK
4952729Sdfr			printf("msqid is locked\n");
4962729Sdfr#endif
4972729Sdfr			need_more_resources = 1;
4982729Sdfr		}
4992729Sdfr		if (msgsz + msqptr->msg_cbytes > msqptr->msg_qbytes) {
5002729Sdfr#ifdef MSG_DEBUG_OK
5012729Sdfr			printf("msgsz + msg_cbytes > msg_qbytes\n");
5022729Sdfr#endif
5032729Sdfr			need_more_resources = 1;
5042729Sdfr		}
5052729Sdfr		if (segs_needed > nfree_msgmaps) {
5062729Sdfr#ifdef MSG_DEBUG_OK
5072729Sdfr			printf("segs_needed > nfree_msgmaps\n");
5082729Sdfr#endif
5092729Sdfr			need_more_resources = 1;
5102729Sdfr		}
5112729Sdfr		if (free_msghdrs == NULL) {
5122729Sdfr#ifdef MSG_DEBUG_OK
5132729Sdfr			printf("no more msghdrs\n");
5142729Sdfr#endif
5152729Sdfr			need_more_resources = 1;
5162729Sdfr		}
5172729Sdfr
5182729Sdfr		if (need_more_resources) {
5192729Sdfr			int we_own_it;
5202729Sdfr
5212729Sdfr			if ((msgflg & IPC_NOWAIT) != 0) {
5222729Sdfr#ifdef MSG_DEBUG_OK
5232729Sdfr				printf("need more resources but caller doesn't want to wait\n");
5242729Sdfr#endif
5252729Sdfr				return(EAGAIN);
5262729Sdfr			}
5272729Sdfr
5282729Sdfr			if ((msqptr->msg_perm.mode & MSG_LOCKED) != 0) {
5292729Sdfr#ifdef MSG_DEBUG_OK
5302729Sdfr				printf("we don't own the msqid_ds\n");
5312729Sdfr#endif
5322729Sdfr				we_own_it = 0;
5332729Sdfr			} else {
5342729Sdfr				/* Force later arrivals to wait for our
5352729Sdfr				   request */
5362729Sdfr#ifdef MSG_DEBUG_OK
5372729Sdfr				printf("we own the msqid_ds\n");
5382729Sdfr#endif
5392729Sdfr				msqptr->msg_perm.mode |= MSG_LOCKED;
5402729Sdfr				we_own_it = 1;
5412729Sdfr			}
5422729Sdfr#ifdef MSG_DEBUG_OK
5432729Sdfr			printf("goodnight\n");
5442729Sdfr#endif
5452729Sdfr			eval = tsleep((caddr_t)msqptr, (PZERO - 4) | PCATCH,
5462729Sdfr			    "msgwait", 0);
5472729Sdfr#ifdef MSG_DEBUG_OK
5482729Sdfr			printf("good morning, eval=%d\n", eval);
5492729Sdfr#endif
5502729Sdfr			if (we_own_it)
5512729Sdfr				msqptr->msg_perm.mode &= ~MSG_LOCKED;
5522729Sdfr			if (eval != 0) {
5532729Sdfr#ifdef MSG_DEBUG_OK
5542729Sdfr				printf("msgsnd:  interrupted system call\n");
5552729Sdfr#endif
5562729Sdfr				return(EINTR);
5572729Sdfr			}
5582729Sdfr
5592729Sdfr			/*
5602729Sdfr			 * Make sure that the msq queue still exists
5612729Sdfr			 */
5622729Sdfr
5632729Sdfr			if (msqptr->msg_qbytes == 0) {
5642729Sdfr#ifdef MSG_DEBUG_OK
5652729Sdfr				printf("msqid deleted\n");
5662729Sdfr#endif
5672729Sdfr				/* The SVID says to return EIDRM. */
5682729Sdfr#ifdef EIDRM
5692729Sdfr				return(EIDRM);
5702729Sdfr#else
5712729Sdfr				/* Unfortunately, BSD doesn't define that code
5722729Sdfr				   yet! */
5732729Sdfr				return(EINVAL);
5742729Sdfr#endif
5752729Sdfr			}
5762729Sdfr
5772729Sdfr		} else {
5782729Sdfr#ifdef MSG_DEBUG_OK
5792729Sdfr			printf("got all the resources that we need\n");
5802729Sdfr#endif
5812729Sdfr			break;
5822729Sdfr		}
5832729Sdfr	}
5842729Sdfr
5852729Sdfr	/*
5862729Sdfr	 * We have the resources that we need.
5872729Sdfr	 * Make sure!
5882729Sdfr	 */
5892729Sdfr
5902729Sdfr	if (msqptr->msg_perm.mode & MSG_LOCKED)
5912729Sdfr		panic("msg_perm.mode & MSG_LOCKED");
5922729Sdfr	if (segs_needed > nfree_msgmaps)
5932729Sdfr		panic("segs_needed > nfree_msgmaps");
5942729Sdfr	if (msgsz + msqptr->msg_cbytes > msqptr->msg_qbytes)
5952729Sdfr		panic("msgsz + msg_cbytes > msg_qbytes");
5962729Sdfr	if (free_msghdrs == NULL)
5972729Sdfr		panic("no more msghdrs");
5982729Sdfr
5992729Sdfr	/*
6002729Sdfr	 * Re-lock the msqid_ds in case we page-fault when copying in the
6012729Sdfr	 * message
6022729Sdfr	 */
6032729Sdfr
6042729Sdfr	if ((msqptr->msg_perm.mode & MSG_LOCKED) != 0)
6052729Sdfr		panic("msqid_ds is already locked");
6062729Sdfr	msqptr->msg_perm.mode |= MSG_LOCKED;
6072729Sdfr
6082729Sdfr	/*
6092729Sdfr	 * Allocate a message header
6102729Sdfr	 */
6112729Sdfr
6122729Sdfr	msghdr = free_msghdrs;
6132729Sdfr	free_msghdrs = msghdr->msg_next;
6142729Sdfr	msghdr->msg_spot = -1;
6152729Sdfr	msghdr->msg_ts = msgsz;
6162729Sdfr
6172729Sdfr	/*
6182729Sdfr	 * Allocate space for the message
6192729Sdfr	 */
6202729Sdfr
6212729Sdfr	while (segs_needed > 0) {
6222729Sdfr		if (nfree_msgmaps <= 0)
6232729Sdfr			panic("not enough msgmaps");
6242729Sdfr		if (free_msgmaps == -1)
6252729Sdfr			panic("nil free_msgmaps");
6262729Sdfr		next = free_msgmaps;
6272729Sdfr		if (next <= -1)
6282729Sdfr			panic("next too low #1");
6292729Sdfr		if (next >= msginfo.msgseg)
6302729Sdfr			panic("next out of range #1");
6312729Sdfr#ifdef MSG_DEBUG_OK
6322729Sdfr		printf("allocating segment %d to message\n", next);
6332729Sdfr#endif
6342729Sdfr		free_msgmaps = msgmaps[next].next;
6352729Sdfr		nfree_msgmaps--;
6362729Sdfr		msgmaps[next].next = msghdr->msg_spot;
6372729Sdfr		msghdr->msg_spot = next;
6382729Sdfr		segs_needed--;
6392729Sdfr	}
6402729Sdfr
6412729Sdfr	/*
6422729Sdfr	 * Copy in the message type
6432729Sdfr	 */
6442729Sdfr
6452729Sdfr	if ((eval = copyin(user_msgp, &msghdr->msg_type,
6462729Sdfr	    sizeof(msghdr->msg_type))) != 0) {
6472729Sdfr#ifdef MSG_DEBUG_OK
6482729Sdfr		printf("error %d copying the message type\n", eval);
6492729Sdfr#endif
6502729Sdfr		msg_freehdr(msghdr);
6512729Sdfr		msqptr->msg_perm.mode &= ~MSG_LOCKED;
6522729Sdfr		wakeup((caddr_t)msqptr);
6532729Sdfr		return(eval);
6542729Sdfr	}
65517971Sbde	user_msgp = (char *)user_msgp + sizeof(msghdr->msg_type);
6562729Sdfr
6572729Sdfr	/*
6582729Sdfr	 * Validate the message type
6592729Sdfr	 */
6602729Sdfr
6612729Sdfr	if (msghdr->msg_type < 1) {
6622729Sdfr		msg_freehdr(msghdr);
6632729Sdfr		msqptr->msg_perm.mode &= ~MSG_LOCKED;
6642729Sdfr		wakeup((caddr_t)msqptr);
6652729Sdfr#ifdef MSG_DEBUG_OK
6662729Sdfr		printf("mtype (%d) < 1\n", msghdr->msg_type);
6672729Sdfr#endif
6682729Sdfr		return(EINVAL);
6692729Sdfr	}
6702729Sdfr
6712729Sdfr	/*
6722729Sdfr	 * Copy in the message body
6732729Sdfr	 */
6742729Sdfr
6752729Sdfr	next = msghdr->msg_spot;
6762729Sdfr	while (msgsz > 0) {
6772729Sdfr		size_t tlen;
6782729Sdfr		if (msgsz > msginfo.msgssz)
6792729Sdfr			tlen = msginfo.msgssz;
6802729Sdfr		else
6812729Sdfr			tlen = msgsz;
6822729Sdfr		if (next <= -1)
6832729Sdfr			panic("next too low #2");
6842729Sdfr		if (next >= msginfo.msgseg)
6852729Sdfr			panic("next out of range #2");
6862729Sdfr		if ((eval = copyin(user_msgp, &msgpool[next * msginfo.msgssz],
6872729Sdfr		    tlen)) != 0) {
6882729Sdfr#ifdef MSG_DEBUG_OK
6892729Sdfr			printf("error %d copying in message segment\n", eval);
6902729Sdfr#endif
6912729Sdfr			msg_freehdr(msghdr);
6922729Sdfr			msqptr->msg_perm.mode &= ~MSG_LOCKED;
6932729Sdfr			wakeup((caddr_t)msqptr);
6942729Sdfr			return(eval);
6952729Sdfr		}
6962729Sdfr		msgsz -= tlen;
69717971Sbde		user_msgp = (char *)user_msgp + tlen;
6982729Sdfr		next = msgmaps[next].next;
6992729Sdfr	}
7002729Sdfr	if (next != -1)
7012729Sdfr		panic("didn't use all the msg segments");
7022729Sdfr
7032729Sdfr	/*
7042729Sdfr	 * We've got the message.  Unlock the msqid_ds.
7052729Sdfr	 */
7062729Sdfr
7072729Sdfr	msqptr->msg_perm.mode &= ~MSG_LOCKED;
7082729Sdfr
7092729Sdfr	/*
7102729Sdfr	 * Make sure that the msqid_ds is still allocated.
7112729Sdfr	 */
7122729Sdfr
7132729Sdfr	if (msqptr->msg_qbytes == 0) {
7142729Sdfr		msg_freehdr(msghdr);
7152729Sdfr		wakeup((caddr_t)msqptr);
7162729Sdfr		/* The SVID says to return EIDRM. */
7172729Sdfr#ifdef EIDRM
7182729Sdfr		return(EIDRM);
7192729Sdfr#else
7202729Sdfr		/* Unfortunately, BSD doesn't define that code yet! */
7212729Sdfr		return(EINVAL);
7222729Sdfr#endif
7232729Sdfr	}
7242729Sdfr
7252729Sdfr	/*
7262729Sdfr	 * Put the message into the queue
7272729Sdfr	 */
7282729Sdfr
7292729Sdfr	if (msqptr->msg_first == NULL) {
7302729Sdfr		msqptr->msg_first = msghdr;
7312729Sdfr		msqptr->msg_last = msghdr;
7322729Sdfr	} else {
7332729Sdfr		msqptr->msg_last->msg_next = msghdr;
7342729Sdfr		msqptr->msg_last = msghdr;
7352729Sdfr	}
7362729Sdfr	msqptr->msg_last->msg_next = NULL;
7372729Sdfr
7382729Sdfr	msqptr->msg_cbytes += msghdr->msg_ts;
7392729Sdfr	msqptr->msg_qnum++;
7402729Sdfr	msqptr->msg_lspid = p->p_pid;
7412729Sdfr	msqptr->msg_stime = time.tv_sec;
7422729Sdfr
7432729Sdfr	wakeup((caddr_t)msqptr);
7442729Sdfr	*retval = 0;
7452729Sdfr	return(0);
7462729Sdfr}
7472729Sdfr
74812866Speter#ifndef _SYS_SYSPROTO_H_
7492729Sdfrstruct msgrcv_args {
7502729Sdfr	int	msqid;
7512729Sdfr	void	*msgp;
7522729Sdfr	size_t	msgsz;
7532729Sdfr	long	msgtyp;
7542729Sdfr	int	msgflg;
7552729Sdfr};
75612866Speter#endif
7572729Sdfr
75812866Speterint
7592729Sdfrmsgrcv(p, uap, retval)
7602729Sdfr	struct proc *p;
7612729Sdfr	register struct msgrcv_args *uap;
7622729Sdfr	int *retval;
7632729Sdfr{
7642729Sdfr	int msqid = uap->msqid;
7652729Sdfr	void *user_msgp = uap->msgp;
7662729Sdfr	size_t msgsz = uap->msgsz;
7672729Sdfr	long msgtyp = uap->msgtyp;
7682729Sdfr	int msgflg = uap->msgflg;
7692729Sdfr	size_t len;
7702729Sdfr	struct ucred *cred = p->p_ucred;
7712729Sdfr	register struct msqid_ds *msqptr;
7722729Sdfr	register struct msg *msghdr;
7732729Sdfr	int eval;
7742729Sdfr	short next;
7752729Sdfr
7762729Sdfr#ifdef MSG_DEBUG_OK
7772729Sdfr	printf("call to msgrcv(%d, 0x%x, %d, %ld, %d)\n", msqid, user_msgp,
7782729Sdfr	    msgsz, msgtyp, msgflg);
7792729Sdfr#endif
7802729Sdfr
7812729Sdfr	msqid = IPCID_TO_IX(msqid);
7822729Sdfr
7832729Sdfr	if (msqid < 0 || msqid >= msginfo.msgmni) {
7842729Sdfr#ifdef MSG_DEBUG_OK
7852729Sdfr		printf("msqid (%d) out of range (0<=msqid<%d)\n", msqid,
7862729Sdfr		    msginfo.msgmni);
7872729Sdfr#endif
7882729Sdfr		return(EINVAL);
7892729Sdfr	}
7902729Sdfr
7912729Sdfr	msqptr = &msqids[msqid];
7922729Sdfr	if (msqptr->msg_qbytes == 0) {
7932729Sdfr#ifdef MSG_DEBUG_OK
7942729Sdfr		printf("no such message queue id\n");
7952729Sdfr#endif
7962729Sdfr		return(EINVAL);
7972729Sdfr	}
7982729Sdfr	if (msqptr->msg_perm.seq != IPCID_TO_SEQ(uap->msqid)) {
7992729Sdfr#ifdef MSG_DEBUG_OK
8002729Sdfr		printf("wrong sequence number\n");
8012729Sdfr#endif
8022729Sdfr		return(EINVAL);
8032729Sdfr	}
8042729Sdfr
8052729Sdfr	if ((eval = ipcperm(cred, &msqptr->msg_perm, IPC_R))) {
8062729Sdfr#ifdef MSG_DEBUG_OK
8072729Sdfr		printf("requester doesn't have read access\n");
8082729Sdfr#endif
8092729Sdfr		return(eval);
8102729Sdfr	}
8112729Sdfr
8122729Sdfr	msghdr = NULL;
8132729Sdfr	while (msghdr == NULL) {
8142729Sdfr		if (msgtyp == 0) {
8152729Sdfr			msghdr = msqptr->msg_first;
8162729Sdfr			if (msghdr != NULL) {
8172729Sdfr				if (msgsz < msghdr->msg_ts &&
8182729Sdfr				    (msgflg & MSG_NOERROR) == 0) {
8192729Sdfr#ifdef MSG_DEBUG_OK
8202729Sdfr					printf("first message on the queue is too big (want %d, got %d)\n",
8212729Sdfr					    msgsz, msghdr->msg_ts);
8222729Sdfr#endif
8232729Sdfr					return(E2BIG);
8242729Sdfr				}
8252729Sdfr				if (msqptr->msg_first == msqptr->msg_last) {
8262729Sdfr					msqptr->msg_first = NULL;
8272729Sdfr					msqptr->msg_last = NULL;
8282729Sdfr				} else {
8292729Sdfr					msqptr->msg_first = msghdr->msg_next;
8302729Sdfr					if (msqptr->msg_first == NULL)
8312729Sdfr						panic("msg_first/last screwed up #1");
8322729Sdfr				}
8332729Sdfr			}
8342729Sdfr		} else {
8352729Sdfr			struct msg *previous;
8362729Sdfr			struct msg **prev;
8372729Sdfr
8382729Sdfr			previous = NULL;
8392729Sdfr			prev = &(msqptr->msg_first);
8402729Sdfr			while ((msghdr = *prev) != NULL) {
8412729Sdfr				/*
8422729Sdfr				 * Is this message's type an exact match or is
8432729Sdfr				 * this message's type less than or equal to
8442729Sdfr				 * the absolute value of a negative msgtyp?
8452729Sdfr				 * Note that the second half of this test can
8462729Sdfr				 * NEVER be true if msgtyp is positive since
8472729Sdfr				 * msg_type is always positive!
8482729Sdfr				 */
8492729Sdfr
8502729Sdfr				if (msgtyp == msghdr->msg_type ||
8512729Sdfr				    msghdr->msg_type <= -msgtyp) {
8522729Sdfr#ifdef MSG_DEBUG_OK
8532729Sdfr					printf("found message type %d, requested %d\n",
8542729Sdfr					    msghdr->msg_type, msgtyp);
8552729Sdfr#endif
8562729Sdfr					if (msgsz < msghdr->msg_ts &&
8572729Sdfr					    (msgflg & MSG_NOERROR) == 0) {
8582729Sdfr#ifdef MSG_DEBUG_OK
8592729Sdfr						printf("requested message on the queue is too big (want %d, got %d)\n",
8602729Sdfr						    msgsz, msghdr->msg_ts);
8612729Sdfr#endif
8622729Sdfr						return(E2BIG);
8632729Sdfr					}
8642729Sdfr					*prev = msghdr->msg_next;
8652729Sdfr					if (msghdr == msqptr->msg_last) {
8662729Sdfr						if (previous == NULL) {
8672729Sdfr							if (prev !=
8682729Sdfr							    &msqptr->msg_first)
8692729Sdfr								panic("msg_first/last screwed up #2");
8702729Sdfr							msqptr->msg_first =
8712729Sdfr							    NULL;
8722729Sdfr							msqptr->msg_last =
8732729Sdfr							    NULL;
8742729Sdfr						} else {
8752729Sdfr							if (prev ==
8762729Sdfr							    &msqptr->msg_first)
8772729Sdfr								panic("msg_first/last screwed up #3");
8782729Sdfr							msqptr->msg_last =
8792729Sdfr							    previous;
8802729Sdfr						}
8812729Sdfr					}
8822729Sdfr					break;
8832729Sdfr				}
8842729Sdfr				previous = msghdr;
8852729Sdfr				prev = &(msghdr->msg_next);
8862729Sdfr			}
8872729Sdfr		}
8882729Sdfr
8892729Sdfr		/*
8902729Sdfr		 * We've either extracted the msghdr for the appropriate
8912729Sdfr		 * message or there isn't one.
8922729Sdfr		 * If there is one then bail out of this loop.
8932729Sdfr		 */
8942729Sdfr
8952729Sdfr		if (msghdr != NULL)
8962729Sdfr			break;
8972729Sdfr
8982729Sdfr		/*
8992729Sdfr		 * Hmph!  No message found.  Does the user want to wait?
9002729Sdfr		 */
9012729Sdfr
9022729Sdfr		if ((msgflg & IPC_NOWAIT) != 0) {
9032729Sdfr#ifdef MSG_DEBUG_OK
9042729Sdfr			printf("no appropriate message found (msgtyp=%d)\n",
9052729Sdfr			    msgtyp);
9062729Sdfr#endif
9072729Sdfr			/* The SVID says to return ENOMSG. */
9082729Sdfr#ifdef ENOMSG
9092729Sdfr			return(ENOMSG);
9102729Sdfr#else
9112729Sdfr			/* Unfortunately, BSD doesn't define that code yet! */
9122729Sdfr			return(EAGAIN);
9132729Sdfr#endif
9142729Sdfr		}
9152729Sdfr
9162729Sdfr		/*
9172729Sdfr		 * Wait for something to happen
9182729Sdfr		 */
9192729Sdfr
9202729Sdfr#ifdef MSG_DEBUG_OK
9212729Sdfr		printf("msgrcv:  goodnight\n");
9222729Sdfr#endif
9232729Sdfr		eval = tsleep((caddr_t)msqptr, (PZERO - 4) | PCATCH, "msgwait",
9242729Sdfr		    0);
9252729Sdfr#ifdef MSG_DEBUG_OK
9262729Sdfr		printf("msgrcv:  good morning (eval=%d)\n", eval);
9272729Sdfr#endif
9282729Sdfr
9292729Sdfr		if (eval != 0) {
9302729Sdfr#ifdef MSG_DEBUG_OK
9312729Sdfr			printf("msgsnd:  interrupted system call\n");
9322729Sdfr#endif
9332729Sdfr			return(EINTR);
9342729Sdfr		}
9352729Sdfr
9362729Sdfr		/*
9372729Sdfr		 * Make sure that the msq queue still exists
9382729Sdfr		 */
9392729Sdfr
9402729Sdfr		if (msqptr->msg_qbytes == 0 ||
9412729Sdfr		    msqptr->msg_perm.seq != IPCID_TO_SEQ(uap->msqid)) {
9422729Sdfr#ifdef MSG_DEBUG_OK
9432729Sdfr			printf("msqid deleted\n");
9442729Sdfr#endif
9452729Sdfr			/* The SVID says to return EIDRM. */
9462729Sdfr#ifdef EIDRM
9472729Sdfr			return(EIDRM);
9482729Sdfr#else
9492729Sdfr			/* Unfortunately, BSD doesn't define that code yet! */
9502729Sdfr			return(EINVAL);
9512729Sdfr#endif
9522729Sdfr		}
9532729Sdfr	}
9542729Sdfr
9552729Sdfr	/*
9562729Sdfr	 * Return the message to the user.
9572729Sdfr	 *
9582729Sdfr	 * First, do the bookkeeping (before we risk being interrupted).
9592729Sdfr	 */
9602729Sdfr
9612729Sdfr	msqptr->msg_cbytes -= msghdr->msg_ts;
9622729Sdfr	msqptr->msg_qnum--;
9632729Sdfr	msqptr->msg_lrpid = p->p_pid;
9642729Sdfr	msqptr->msg_rtime = time.tv_sec;
9652729Sdfr
9662729Sdfr	/*
9672729Sdfr	 * Make msgsz the actual amount that we'll be returning.
9682729Sdfr	 * Note that this effectively truncates the message if it is too long
9692729Sdfr	 * (since msgsz is never increased).
9702729Sdfr	 */
9712729Sdfr
9722729Sdfr#ifdef MSG_DEBUG_OK
9732729Sdfr	printf("found a message, msgsz=%d, msg_ts=%d\n", msgsz,
9742729Sdfr	    msghdr->msg_ts);
9752729Sdfr#endif
9762729Sdfr	if (msgsz > msghdr->msg_ts)
9772729Sdfr		msgsz = msghdr->msg_ts;
9782729Sdfr
9792729Sdfr	/*
9802729Sdfr	 * Return the type to the user.
9812729Sdfr	 */
9822729Sdfr
9832729Sdfr	eval = copyout((caddr_t)&(msghdr->msg_type), user_msgp,
9842729Sdfr	    sizeof(msghdr->msg_type));
9852729Sdfr	if (eval != 0) {
9862729Sdfr#ifdef MSG_DEBUG_OK
9872729Sdfr		printf("error (%d) copying out message type\n", eval);
9882729Sdfr#endif
9892729Sdfr		msg_freehdr(msghdr);
9902729Sdfr		wakeup((caddr_t)msqptr);
9912729Sdfr		return(eval);
9922729Sdfr	}
99317971Sbde	user_msgp = (char *)user_msgp + sizeof(msghdr->msg_type);
9942729Sdfr
9952729Sdfr	/*
9962729Sdfr	 * Return the segments to the user
9972729Sdfr	 */
9982729Sdfr
9992729Sdfr	next = msghdr->msg_spot;
10002729Sdfr	for (len = 0; len < msgsz; len += msginfo.msgssz) {
10012729Sdfr		size_t tlen;
10022729Sdfr
10032729Sdfr		if (msgsz > msginfo.msgssz)
10042729Sdfr			tlen = msginfo.msgssz;
10052729Sdfr		else
10062729Sdfr			tlen = msgsz;
10072729Sdfr		if (next <= -1)
10082729Sdfr			panic("next too low #3");
10092729Sdfr		if (next >= msginfo.msgseg)
10102729Sdfr			panic("next out of range #3");
10112729Sdfr		eval = copyout((caddr_t)&msgpool[next * msginfo.msgssz],
10122729Sdfr		    user_msgp, tlen);
10132729Sdfr		if (eval != 0) {
10142729Sdfr#ifdef MSG_DEBUG_OK
10152729Sdfr			printf("error (%d) copying out message segment\n",
10162729Sdfr			    eval);
10172729Sdfr#endif
10182729Sdfr			msg_freehdr(msghdr);
10192729Sdfr			wakeup((caddr_t)msqptr);
10202729Sdfr			return(eval);
10212729Sdfr		}
102217971Sbde		user_msgp = (char *)user_msgp + tlen;
10232729Sdfr		next = msgmaps[next].next;
10242729Sdfr	}
10252729Sdfr
10262729Sdfr	/*
10272729Sdfr	 * Done, return the actual number of bytes copied out.
10282729Sdfr	 */
10292729Sdfr
10302729Sdfr	msg_freehdr(msghdr);
10312729Sdfr	wakeup((caddr_t)msqptr);
10322729Sdfr	*retval = msgsz;
10332729Sdfr	return(0);
10342729Sdfr}
1035