Home | History | Annotate | Line # | Download | only in kern
sysv_msg.c revision 1.4
      1 /*
      2  * Implementation of SVID messages
      3  *
      4  * Author:  Daniel Boulet
      5  *
      6  * Copyright 1993 Daniel Boulet and RTMX Inc.
      7  *
      8  * This system call was implemented by Daniel Boulet under contract from RTMX.
      9  *
     10  * Redistribution and use in source forms, with and without modification,
     11  * are permitted provided that this entire comment appears intact.
     12  *
     13  * Redistribution in binary form may occur without any restrictions.
     14  * Obviously, it would be nice if you gave credit where credit is due
     15  * but requiring it would be too onerous.
     16  *
     17  * This software is provided ``AS IS'' without any warranties of any kind.
     18  *
     19  *	$Id: sysv_msg.c,v 1.4 1994/02/13 11:32:46 mycroft Exp $
     20  */
     21 
     22 #include <sys/param.h>
     23 #include <sys/systm.h>
     24 #include <sys/kernel.h>
     25 #include <sys/proc.h>
     26 #include <sys/msg.h>
     27 #include <sys/malloc.h>
     28 
     29 #define MSG_DEBUG
     30 #undef MSG_DEBUG_OK
     31 
     32 static int	msgctl(), msgget(), msgsnd(), msgrcv();
     33 
     34 int	(*msgcalls[])() = { msgctl, msgget, msgsnd, msgrcv };
     35 
     36 int nfree_msgmaps;		/* # of free map entries */
     37 short free_msgmaps;		/* head of linked list of free map entries */
     38 struct msg *free_msghdrs;	/* list of free msg headers */
     39 
     40 int
     41 msginit()
     42 {
     43 	register int i;
     44 	vm_offset_t whocares1, whocares2;
     45 
     46 	/*
     47 	 * msginfo.msgssz should be a power of two for efficiency reasons.
     48 	 * It is also pretty silly if msginfo.msgssz is less than 8
     49 	 * or greater than about 256 so ...
     50 	 */
     51 
     52 	i = 8;
     53 	while (i < 1024 && i != msginfo.msgssz)
     54 		i <<= 1;
     55     	if (i != msginfo.msgssz) {
     56 		printf("msginfo.msgssz=%d (0x%x)\n", msginfo.msgssz,
     57 		    msginfo.msgssz);
     58 		panic("msginfo.msgssz not a small power of 2");
     59 	}
     60 
     61 	if (msginfo.msgseg > 32767) {
     62 		printf("msginfo.msgseg=%d\n", msginfo.msgseg);
     63 		panic("msginfo.msgseg > 32767");
     64 	}
     65 
     66 	if (msgmaps == NULL)
     67 		panic("msgmaps is NULL");
     68 
     69 	for (i = 0; i < msginfo.msgseg; i++) {
     70 		if (i > 0)
     71 			msgmaps[i-1].next = i;
     72 		msgmaps[i].next = -1;	/* implies entry is available */
     73 	}
     74 	free_msgmaps = 0;
     75 	nfree_msgmaps = msginfo.msgseg;
     76 
     77 	if (msghdrs == NULL)
     78 		panic("msghdrs is NULL");
     79 
     80 	for (i = 0; i < msginfo.msgtql; i++) {
     81 		msghdrs[i].msg_type = 0;
     82 		if (i > 0)
     83 			msghdrs[i-1].msg_next = &msghdrs[i];
     84 		msghdrs[i].msg_next = NULL;
     85     	}
     86 	free_msghdrs = &msghdrs[0];
     87 
     88 	if (msqids == NULL)
     89 		panic("msqids is NULL");
     90 
     91 	for (i = 0; i < msginfo.msgmni; i++) {
     92 		msqids[i].msg_qbytes = 0;	/* implies entry is available */
     93 		msqids[i].msg_perm.seq = 0;	/* reset to a known value */
     94 	}
     95 }
     96 
     97 /*
     98  * Entry point for all MSG calls
     99  */
    100 
    101 struct msgsys_args {
    102 	u_int	which;
    103 };
    104 
    105 int
    106 msgsys(p, uap, retval)
    107 	struct caller *p;
    108 	struct msgsys_args *uap;
    109 	int *retval;
    110 {
    111 
    112 	if (uap->which >= sizeof(msgcalls)/sizeof(msgcalls[0]))
    113 		return (EINVAL);
    114 	return ((*msgcalls[uap->which])(p, &uap[1], retval));
    115 }
    116 
    117 static void
    118 msg_freehdr(msghdr)
    119 	struct msg *msghdr;
    120 {
    121 	while (msghdr->msg_ts > 0) {
    122 		short next;
    123 		if (msghdr->msg_spot < 0 || msghdr->msg_spot >= msginfo.msgseg)
    124 			panic("msghdr->msg_spot out of range");
    125 		next = msgmaps[msghdr->msg_spot].next;
    126 		msgmaps[msghdr->msg_spot].next = free_msgmaps;
    127 		free_msgmaps = msghdr->msg_spot;
    128 		nfree_msgmaps += 1;
    129 		msghdr->msg_spot = next;
    130 		if (msghdr->msg_ts >= msginfo.msgssz)
    131 			msghdr->msg_ts -= msginfo.msgssz;
    132 		else
    133 			msghdr->msg_ts = 0;
    134 	}
    135 	if (msghdr->msg_spot != -1)
    136 		panic("msghdr->msg_spot != -1");
    137 	msghdr->msg_next = free_msghdrs;
    138 	free_msghdrs = msghdr;
    139 }
    140 
    141 struct msgctl_args {
    142 	int	msqid;
    143 	int	cmd;
    144 	struct	msqid_ds *user_msqptr;
    145 };
    146 
    147 int
    148 msgctl(p, uap, retval)
    149 	struct proc *p;
    150 	register struct msgctl_args *uap;
    151 	int *retval;
    152 {
    153 	int msqid = uap->msqid;
    154 	int cmd = uap->cmd;
    155 	struct msqid_ds *user_msqptr = uap->user_msqptr;
    156 	struct ucred *cred = p->p_ucred;
    157 	int i, rval, eval;
    158 	struct msqid_ds msqbuf;
    159 	register struct msqid_ds *msqptr;
    160 
    161 #ifdef MSG_DEBUG_OK
    162 	printf("call to msgctl(%d, %d, 0x%x)\n", msqid, cmd, user_msqptr);
    163 #endif
    164 
    165 	msqid = IPCID_TO_IX(msqid);
    166 
    167 	if (msqid < 0 || msqid >= msginfo.msgmni) {
    168 #ifdef MSG_DEBUG_OK
    169 		printf("msqid (%d) out of range (0<=msqid<%d)\n", msqid,
    170 		    msginfo.msgmni);
    171 #endif
    172 		return(EINVAL);
    173 	}
    174 
    175 	msqptr = &msqids[msqid];
    176 
    177 	if (msqptr->msg_qbytes == 0) {
    178 #ifdef MSG_DEBUG_OK
    179 		printf("no such msqid\n");
    180 #endif
    181 		return(EINVAL);
    182 	}
    183 	if (msqptr->msg_perm.seq != IPCID_TO_SEQ(uap->msqid)) {
    184 #ifdef MSG_DEBUG_OK
    185 		printf("wrong sequence number\n");
    186 #endif
    187 		return(EINVAL);
    188 	}
    189 
    190 	eval = 0;
    191 	rval = 0;
    192 
    193 	switch (cmd) {
    194 
    195 	case IPC_RMID:
    196 #ifdef MSG_DEBUG_OK
    197 		printf("IPC_RMID\n");
    198 #endif
    199 	{
    200 		struct msg *msghdr;
    201 
    202 		if (cred->cr_uid != 0 &&
    203 		    msqptr->msg_perm.cuid != cred->cr_uid &&
    204 		    msqptr->msg_perm.uid != cred->cr_uid)
    205 			return(EPERM);
    206 
    207 		/* Free the message headers */
    208 		msghdr = msqptr->msg_first;
    209 		while (msghdr != NULL) {
    210 			struct msg *msghdr_tmp;
    211 
    212 			/* Free the segments of each message */
    213 			msqptr->msg_cbytes -= msghdr->msg_ts;
    214 			msqptr->msg_qnum -= 1;
    215 			msghdr_tmp = msghdr;
    216 			msghdr = msghdr->msg_next;
    217 			msg_freehdr(msghdr_tmp);
    218 		}
    219 
    220 		if (msqptr->msg_cbytes != 0)
    221 			panic("msg_cbytes is screwed up");
    222 		if (msqptr->msg_qnum != 0)
    223 			panic("msg_qnum is screwed up");
    224 
    225 		msqptr->msg_qbytes = 0;	/* Mark it as free */
    226 
    227 		wakeup((caddr_t)msqptr);
    228 	}
    229 
    230 		break;
    231 
    232 	case IPC_SET:
    233 #ifdef MSG_DEBUG_OK
    234 		printf("IPC_SET\n");
    235 #endif
    236 		if (cred->cr_uid != 0 &&
    237 		    msqptr->msg_perm.cuid != cred->cr_uid &&
    238 		    msqptr->msg_perm.uid != cred->cr_uid)
    239 			return(EPERM);
    240 		if ((eval = copyin(user_msqptr, &msqbuf, sizeof(msqbuf))) != 0)
    241 			return(eval);
    242 		if (msqbuf.msg_qbytes > msqptr->msg_qbytes && cred->cr_uid != 0)
    243 			return(EPERM);
    244 		if (msqbuf.msg_qbytes > msginfo.msgmnb) {
    245 #ifdef MSG_DEBUG_OK
    246 			printf("can't increase msg_qbytes beyond %d (truncating)\n",
    247 			    msginfo.msgmnb);
    248 #endif
    249 			msqbuf.msg_qbytes = msginfo.msgmnb;	/* silently restrict qbytes to system limit */
    250 		}
    251 		if (msqbuf.msg_qbytes == 0) {
    252 #ifdef MSG_DEBUG_OK
    253 			printf("can't reduce msg_qbytes to 0\n");
    254 #endif
    255 			return(EINVAL);		/* non-standard errno! */
    256 		}
    257 		msqptr->msg_perm.uid = msqbuf.msg_perm.uid;	/* change the owner */
    258 		msqptr->msg_perm.gid = msqbuf.msg_perm.gid;	/* change the owner */
    259 		msqptr->msg_perm.mode = (msqptr->msg_perm.mode & ~0777) |
    260 		    (msqbuf.msg_perm.mode & 0777);
    261 		msqptr->msg_qbytes = msqbuf.msg_qbytes;
    262 		msqptr->msg_ctime = time.tv_sec;
    263 		break;
    264 
    265 	case IPC_STAT:
    266 #ifdef MSG_DEBUG_OK
    267 		printf("IPC_STAT\n");
    268 #endif
    269 		if ((eval = ipcaccess(&msqptr->msg_perm, IPC_R, cred))) {
    270 #ifdef MSG_DEBUG_OK
    271 			printf("requester doesn't have read access\n");
    272 #endif
    273 			return(eval);
    274 		}
    275 		rval = 0;
    276 		eval = copyout((caddr_t)msqptr, user_msqptr,
    277 		    sizeof(struct msqid_ds));
    278 		break;
    279 
    280 	default:
    281 #ifdef MSG_DEBUG_OK
    282 		printf("invalid command %d\n", cmd);
    283 #endif
    284 		return(EINVAL);
    285 	}
    286 
    287 	if (eval == 0)
    288 		*retval = rval;
    289 
    290 	return(eval);
    291 }
    292 
    293 struct msgget_args {
    294 	key_t	key;
    295 	int	msgflg;
    296 };
    297 
    298 int
    299 msgget(p, uap, retval)
    300 	struct proc *p;
    301 	register struct msgget_args *uap;
    302 	int *retval;
    303 {
    304 	int msqid, eval;
    305 	int key = uap->key;
    306 	int msgflg = uap->msgflg;
    307 	struct ucred *cred = p->p_ucred;
    308 	register struct msqid_ds *msqptr;
    309 
    310 #ifdef MSG_DEBUG_OK
    311 	printf("msgget(0x%x, 0%o)\n", key, msgflg);
    312 #endif
    313 
    314 	if (key == IPC_PRIVATE) {
    315 #ifdef MSG_DEBUG_OK
    316 		printf("private key\n");
    317 #endif
    318 		msqid = msginfo.msgmni;
    319 	} else {
    320 		for (msqid = 0; msqid < msginfo.msgmni; msqid += 1) {
    321 			msqptr = &msqids[msqid];
    322 			if (msqptr->msg_qbytes != 0 &&
    323 			    msqptr->msg_perm.key == key)
    324 				break;
    325 		}
    326 		if (msqid < msginfo.msgmni) {
    327 #ifdef MSG_DEBUG_OK
    328 			printf("found public key\n");
    329 #endif
    330 			if ((msgflg & IPC_CREAT) && (msgflg & IPC_EXCL)) {
    331 #ifdef MSG_DEBUG_OK
    332 				printf("not exclusive\n");
    333 #endif
    334 				return(EEXIST);
    335 			}
    336 			if ((eval = ipcaccess(&msqptr->msg_perm, msgflg & 0700,
    337 			    cred))) {
    338 #ifdef MSG_DEBUG_OK
    339 				printf("requester doesn't have 0%o access\n",
    340 				    msgflg & 0700);
    341 #endif
    342 				return(eval);
    343 			}
    344 		} else {
    345 #ifdef MSG_DEBUG_OK
    346 			printf("didn't find public key\n");
    347 #endif
    348 		}
    349 	}
    350 
    351 	if (msqid == msginfo.msgmni) {
    352 #ifdef MSG_DEBUG_OK
    353 		printf("need to allocate the msqid_ds\n");
    354 #endif
    355 		if (key == IPC_PRIVATE || (msgflg & IPC_CREAT)) {
    356 			for (msqid = 0; msqid < msginfo.msgmni; msqid += 1) {
    357 				/*
    358 				 * Look for an unallocated and unlocked
    359 				 * msqid_ds.  msqid_ds's can be locked by
    360 				 * msgsnd or msgrcv while they are copying the
    361 				 * message in/out.  We can't re-use the entry
    362 				 * until they release it.
    363 				 */
    364 
    365 				msqptr = &msqids[msqid];
    366 				if (msqptr->msg_qbytes == 0 &&
    367 				    (msqptr->msg_perm.mode & MSG_LOCKED) == 0)
    368 					break;
    369 			}
    370 			if (msqid == msginfo.msgmni) {
    371 #ifdef MSG_DEBUG_OK
    372 				printf("no more msqid_ds's available\n");
    373 #endif
    374 				return(ENOSPC);
    375 			}
    376 #ifdef MSG_DEBUG_OK
    377 			printf("msqid %d is available\n", msqid+1);
    378 #endif
    379 			msqptr->msg_perm.key = key;
    380 			msqptr->msg_perm.cuid = cred->cr_uid;
    381 			msqptr->msg_perm.uid = cred->cr_uid;
    382 			msqptr->msg_perm.cgid = cred->cr_gid;
    383 			msqptr->msg_perm.gid = cred->cr_gid;
    384 			msqptr->msg_perm.mode = (msgflg & 0777);
    385 			/* Make sure that the returned msqid is unique */
    386 			msqptr->msg_perm.seq += 1;
    387 			msqptr->msg_first = NULL;
    388 			msqptr->msg_last = NULL;
    389 			msqptr->msg_cbytes = 0;
    390 			msqptr->msg_qnum = 0;
    391 			msqptr->msg_qbytes = msginfo.msgmnb;
    392 			msqptr->msg_lspid = 0;
    393 			msqptr->msg_lrpid = 0;
    394 			msqptr->msg_stime = 0;
    395 			msqptr->msg_rtime = 0;
    396 			msqptr->msg_ctime = time.tv_sec;
    397 		} else {
    398 #ifdef MSG_DEBUG_OK
    399 			printf("didn't find it and wasn't asked to create it\n");
    400 #endif
    401 			return(ENOENT);
    402 		}
    403 	}
    404 
    405 	/* Construct the unique msqid */
    406 	*retval = IXSEQ_TO_IPCID(msqid, msqptr->msg_perm);
    407 	return(0);
    408 }
    409 
    410 struct msgsnd_args {
    411 	int	msqid;
    412 	void	*user_msgp;
    413 	size_t	msgsz;
    414 	int	msgflg;
    415 };
    416 
    417 int
    418 msgsnd(p, uap, retval)
    419 	struct proc *p;
    420 	register struct msgsnd_args *uap;
    421 	int *retval;
    422 {
    423 	int msqid = uap->msqid;
    424 	void *user_msgp = uap->user_msgp;
    425 	size_t msgsz = uap->msgsz;
    426 	int msgflg = uap->msgflg;
    427 	int segs_needed, eval;
    428 	struct ucred *cred = p->p_ucred;
    429 	register struct msqid_ds *msqptr;
    430 	register struct msg *msghdr;
    431 	short next;
    432 
    433 #ifdef MSG_DEBUG_OK
    434 	printf("call to msgsnd(%d, 0x%x, %d, %d)\n", msqid, user_msgp, msgsz,
    435 	    msgflg);
    436 #endif
    437 
    438 	msqid = IPCID_TO_IX(msqid);
    439 
    440 	if (msqid < 0 || msqid >= msginfo.msgmni) {
    441 #ifdef MSG_DEBUG_OK
    442 		printf("msqid (%d) out of range (0<=msqid<%d)\n", msqid,
    443 		    msginfo.msgmni);
    444 #endif
    445 		return(EINVAL);
    446 	}
    447 
    448 	msqptr = &msqids[msqid];
    449 	if (msqptr->msg_qbytes == 0) {
    450 #ifdef MSG_DEBUG_OK
    451 		printf("no such message queue id\n");
    452 #endif
    453 		return(EINVAL);
    454 	}
    455 	if (msqptr->msg_perm.seq != IPCID_TO_SEQ(uap->msqid)) {
    456 #ifdef MSG_DEBUG_OK
    457 		printf("wrong sequence number\n");
    458 #endif
    459 		return(EINVAL);
    460 	}
    461 
    462 	if ((eval = ipcaccess(&msqptr->msg_perm, IPC_W, cred))) {
    463 #ifdef MSG_DEBUG_OK
    464 		printf("requester doesn't have write access\n");
    465 #endif
    466 		return(eval);
    467 	}
    468 
    469 	segs_needed = (msgsz + msginfo.msgssz - 1) / msginfo.msgssz;
    470 #ifdef MSG_DEBUG_OK
    471 	printf("msgsz=%d, msgssz=%d, segs_needed=%d\n", msgsz, msginfo.msgssz,
    472 	    segs_needed);
    473 #endif
    474 	for (;;) {
    475 		int need_more_resources = 0;
    476 
    477 		/*
    478 		 * check msgsz
    479 		 * (inside this loop in case msg_qbytes changes while we sleep)
    480 		 */
    481 
    482 		if (msgsz < 0 || msgsz > msqptr->msg_qbytes) {
    483 #ifdef MSG_DEBUG_OK
    484 			printf("msgsz > msqptr->msg_qbytes\n");
    485 #endif
    486 			return(EINVAL);
    487 		}
    488 
    489 		if (msqptr->msg_perm.mode & MSG_LOCKED) {
    490 #ifdef MSG_DEBUG_OK
    491 			printf("msqid is locked\n");
    492 #endif
    493 			need_more_resources = 1;
    494 		}
    495 		if (msgsz + msqptr->msg_cbytes > msqptr->msg_qbytes) {
    496 #ifdef MSG_DEBUG_OK
    497 			printf("msgsz + msg_cbytes > msg_qbytes\n");
    498 #endif
    499 			need_more_resources = 1;
    500 		}
    501 		if (segs_needed > nfree_msgmaps) {
    502 #ifdef MSG_DEBUG_OK
    503 			printf("segs_needed > nfree_msgmaps\n");
    504 #endif
    505 			need_more_resources = 1;
    506 		}
    507 		if (free_msghdrs == NULL) {
    508 #ifdef MSG_DEBUG_OK
    509 			printf("no more msghdrs\n");
    510 #endif
    511 			need_more_resources = 1;
    512 		}
    513 
    514 		if (need_more_resources) {
    515 			int we_own_it;
    516 
    517 			if ((msgflg & IPC_NOWAIT) != 0) {
    518 #ifdef MSG_DEBUG_OK
    519 				printf("need more resources but caller doesn't want to wait\n");
    520 #endif
    521 				return(EAGAIN);
    522 			}
    523 
    524 			if ((msqptr->msg_perm.mode & MSG_LOCKED) != 0) {
    525 #ifdef MSG_DEBUG_OK
    526 				printf("we don't own the msqid_ds\n");
    527 #endif
    528 				we_own_it = 0;
    529 			} else {
    530 				/* Force later arrivals to wait for our
    531 				   request */
    532 #ifdef MSG_DEBUG_OK
    533 				printf("we own the msqid_ds\n");
    534 #endif
    535 				msqptr->msg_perm.mode |= MSG_LOCKED;
    536 				we_own_it = 1;
    537 			}
    538 #ifdef MSG_DEBUG_OK
    539 			printf("goodnight\n");
    540 #endif
    541 			eval = tsleep((caddr_t)msqptr, (PZERO - 4) | PCATCH,
    542 			    "msgwait", 0);
    543 #ifdef MSG_DEBUG_OK
    544 			printf("good morning, eval=%d\n", eval);
    545 #endif
    546 			if (we_own_it)
    547 				msqptr->msg_perm.mode &= ~MSG_LOCKED;
    548 			if (eval != 0) {
    549 #ifdef MSG_DEBUG_OK
    550 				printf("msgsnd:  interrupted system call\n");
    551 #endif
    552 				return(EINTR);
    553 			}
    554 
    555 			/*
    556 			 * Make sure that the msq queue still exists
    557 			 */
    558 
    559 			if (msqptr->msg_qbytes == 0) {
    560 #ifdef MSG_DEBUG_OK
    561 				printf("msqid deleted\n");
    562 #endif
    563 				/* The SVID says to return EIDRM. */
    564 #ifdef EIDRM
    565 				return(EIDRM);
    566 #else
    567 				/* Unfortunately, BSD doesn't define that code
    568 				   yet! */
    569 				return(EINVAL);
    570 #endif
    571 			}
    572 
    573 		} else {
    574 #ifdef MSG_DEBUG_OK
    575 			printf("got all the resources that we need\n");
    576 #endif
    577 			break;
    578 		}
    579 	}
    580 
    581 	/*
    582 	 * We have the resources that we need.
    583 	 * Make sure!
    584 	 */
    585 
    586 	if (msqptr->msg_perm.mode & MSG_LOCKED)
    587 		panic("msg_perm.mode & MSG_LOCKED");
    588 	if (segs_needed > nfree_msgmaps)
    589 		panic("segs_needed > nfree_msgmaps");
    590 	if (msgsz + msqptr->msg_cbytes > msqptr->msg_qbytes)
    591 		panic("msgsz + msg_cbytes > msg_qbytes");
    592 	if (free_msghdrs == NULL)
    593 		panic("no more msghdrs");
    594 
    595 	/*
    596 	 * Re-lock the msqid_ds in case we page-fault when copying in the
    597 	 * message
    598 	 */
    599 
    600 	if ((msqptr->msg_perm.mode & MSG_LOCKED) != 0)
    601 		panic("msqid_ds is already locked");
    602 	msqptr->msg_perm.mode |= MSG_LOCKED;
    603 
    604 	/*
    605 	 * Allocate a message header
    606 	 */
    607 
    608 	msghdr = free_msghdrs;
    609 	free_msghdrs = msghdr->msg_next;
    610 	msghdr->msg_spot = -1;
    611 	msghdr->msg_ts = msgsz;
    612 
    613 	/*
    614 	 * Allocate space for the message
    615 	 */
    616 
    617 	while (segs_needed > 0) {
    618 		if (nfree_msgmaps <= 0)
    619 			panic("not enough msgmaps");
    620 		if (free_msgmaps == -1)
    621 			panic("nil free_msgmaps");
    622 		next = free_msgmaps;
    623 		if (next <= -1)
    624 			panic("next too low #1");
    625 		if (next >= msginfo.msgseg)
    626 			panic("next out of range #1");
    627 #ifdef MSG_DEBUG_OK
    628 		printf("allocating segment %d to message\n", next);
    629 #endif
    630 		free_msgmaps = msgmaps[next].next;
    631 		nfree_msgmaps -= 1;
    632 		msgmaps[next].next = msghdr->msg_spot;
    633 		msghdr->msg_spot = next;
    634 		segs_needed -= 1;
    635 	}
    636 
    637 	/*
    638 	 * Copy in the message type
    639 	 */
    640 
    641 	if ((eval = copyin(user_msgp, &msghdr->msg_type,
    642 	    sizeof(msghdr->msg_type))) != 0) {
    643 #ifdef MSG_DEBUG_OK
    644 		printf("error %d copying the message type\n", eval);
    645 #endif
    646 		msg_freehdr(msghdr);
    647 		msqptr->msg_perm.mode &= ~MSG_LOCKED;
    648 		wakeup((caddr_t)msqptr);
    649 		return(eval);
    650 	}
    651 	user_msgp += sizeof(msghdr->msg_type);
    652 
    653 	/*
    654 	 * Validate the message type
    655 	 */
    656 
    657 	if (msghdr->msg_type < 1) {
    658 		msg_freehdr(msghdr);
    659 		msqptr->msg_perm.mode &= ~MSG_LOCKED;
    660 		wakeup((caddr_t)msqptr);
    661 #ifdef MSG_DEBUG_OK
    662 		printf("mtype (%d) < 1\n", msghdr->msg_type);
    663 #endif
    664 		return(EINVAL);
    665 	}
    666 
    667 	/*
    668 	 * Copy in the message body
    669 	 */
    670 
    671 	next = msghdr->msg_spot;
    672 	while (msgsz > 0) {
    673 		size_t tlen;
    674 		if (msgsz > msginfo.msgssz)
    675 			tlen = msginfo.msgssz;
    676 		else
    677 			tlen = msgsz;
    678 		if (next <= -1)
    679 			panic("next too low #2");
    680 		if (next >= msginfo.msgseg)
    681 			panic("next out of range #2");
    682 		if ((eval = copyin(user_msgp, &msgpool[next * msginfo.msgssz],
    683 		    tlen)) != 0) {
    684 #ifdef MSG_DEBUG_OK
    685 			printf("error %d copying in message segment\n", eval);
    686 #endif
    687 			msg_freehdr(msghdr);
    688 			msqptr->msg_perm.mode &= ~MSG_LOCKED;
    689 			wakeup((caddr_t)msqptr);
    690 			return(eval);
    691 		}
    692 		msgsz -= tlen;
    693 		user_msgp += tlen;
    694 		next = msgmaps[next].next;
    695 	}
    696 	if (next != -1)
    697 		panic("didn't use all the msg segments");
    698 
    699 	/*
    700 	 * We've got the message.  Unlock the msqid_ds.
    701 	 */
    702 
    703 	msqptr->msg_perm.mode &= ~MSG_LOCKED;
    704 
    705 	/*
    706 	 * Make sure that the msqid_ds is still allocated.
    707 	 */
    708 
    709 	if (msqptr->msg_qbytes == 0) {
    710 		msg_freehdr(msghdr);
    711 		wakeup((caddr_t)msqptr);
    712 		/* The SVID says to return EIDRM. */
    713 #ifdef EIDRM
    714 		return(EIDRM);
    715 #else
    716 		/* Unfortunately, BSD doesn't define that code yet! */
    717 		return(EINVAL);
    718 #endif
    719 	}
    720 
    721 	/*
    722 	 * Put the message into the queue
    723 	 */
    724 
    725 	if (msqptr->msg_first == NULL) {
    726 		msqptr->msg_first = msghdr;
    727 		msqptr->msg_last = msghdr;
    728 	} else {
    729 		msqptr->msg_last->msg_next = msghdr;
    730 		msqptr->msg_last = msghdr;
    731 	}
    732 	msqptr->msg_last->msg_next = NULL;
    733 
    734 	msqptr->msg_cbytes += msghdr->msg_ts;
    735 	msqptr->msg_qnum += 1;
    736 	msqptr->msg_lspid = p->p_pid;
    737 	msqptr->msg_stime = time.tv_sec;
    738 
    739 	wakeup((caddr_t)msqptr);
    740 	*retval = 0;
    741 	return(0);
    742 }
    743 
    744 struct msgrcv_args {
    745 	int	msqid;
    746 	void	*msgp;
    747 	size_t	msgsz;
    748 	long	msgtyp;
    749 	int	msgflg;
    750 };
    751 
    752 int
    753 msgrcv(p, uap, retval)
    754 	struct proc *p;
    755 	register struct msgrcv_args *uap;
    756 	int *retval;
    757 {
    758 	int msqid = uap->msqid;
    759 	void *user_msgp = uap->msgp;
    760 	size_t msgsz = uap->msgsz;
    761 	long msgtyp = uap->msgtyp;
    762 	int msgflg = uap->msgflg;
    763 	size_t len;
    764 	struct ucred *cred = p->p_ucred;
    765 	register struct msqid_ds *msqptr;
    766 	register struct msg *msghdr;
    767 	int eval;
    768 	short next;
    769 
    770 #ifdef MSG_DEBUG_OK
    771 	printf("call to msgrcv(%d, 0x%x, %d, %ld, %d)\n", msqid, user_msgp,
    772 	    msgsz, msgtyp, msgflg);
    773 #endif
    774 
    775 	msqid = IPCID_TO_IX(msqid);
    776 
    777 	if (msqid < 0 || msqid >= msginfo.msgmni) {
    778 #ifdef MSG_DEBUG_OK
    779 		printf("msqid (%d) out of range (0<=msqid<%d)\n", msqid,
    780 		    msginfo.msgmni);
    781 #endif
    782 		return(EINVAL);
    783 	}
    784 
    785 	msqptr = &msqids[msqid];
    786 	if (msqptr->msg_qbytes == 0) {
    787 #ifdef MSG_DEBUG_OK
    788 		printf("no such message queue id\n");
    789 #endif
    790 		return(EINVAL);
    791 	}
    792 	if (msqptr->msg_perm.seq != IPCID_TO_SEQ(uap->msqid)) {
    793 #ifdef MSG_DEBUG_OK
    794 		printf("wrong sequence number\n");
    795 #endif
    796 		return(EINVAL);
    797 	}
    798 
    799 	if ((eval = ipcaccess(&msqptr->msg_perm, IPC_R, cred))) {
    800 #ifdef MSG_DEBUG_OK
    801 		printf("requester doesn't have read access\n");
    802 #endif
    803 		return(eval);
    804 	}
    805 
    806 	if (msgsz < 0) {
    807 #ifdef MSG_DEBUG_OK
    808 		printf("msgsz < 0\n");
    809 #endif
    810 		return(EINVAL);
    811 	}
    812 
    813 	msghdr = NULL;
    814 	while (msghdr == NULL) {
    815 		if (msgtyp == 0) {
    816 			msghdr = msqptr->msg_first;
    817 			if (msghdr != NULL) {
    818 				if (msgsz < msghdr->msg_ts &&
    819 				    (msgflg & MSG_NOERROR) == 0) {
    820 #ifdef MSG_DEBUG_OK
    821 					printf("first message on the queue is too big (want %d, got %d)\n",
    822 					    msgsz, msghdr->msg_ts);
    823 #endif
    824 					return(E2BIG);
    825 				}
    826 				if (msqptr->msg_first == msqptr->msg_last) {
    827 					msqptr->msg_first = NULL;
    828 					msqptr->msg_last = NULL;
    829 				} else {
    830 					msqptr->msg_first = msghdr->msg_next;
    831 					if (msqptr->msg_first == NULL)
    832 						panic("msg_first/last screwed up #1");
    833 				}
    834 			}
    835 		} else {
    836 			struct msg *previous;
    837 			struct msg **prev;
    838 
    839 			previous = NULL;
    840 			prev = &(msqptr->msg_first);
    841 			while ((msghdr = *prev) != NULL) {
    842 				/*
    843 				 * Is this message's type an exact match or is
    844 				 * this message's type less than or equal to
    845 				 * the absolute value of a negative msgtyp?
    846 				 * Note that the second half of this test can
    847 				 * NEVER be true if msgtyp is positive since
    848 				 * msg_type is always positive!
    849 				 */
    850 
    851 				if (msgtyp == msghdr->msg_type ||
    852 				    msghdr->msg_type <= -msgtyp) {
    853 #ifdef MSG_DEBUG_OK
    854 					printf("found message type %d, requested %d\n",
    855 					    msghdr->msg_type, msgtyp);
    856 #endif
    857 					if (msgsz < msghdr->msg_ts &&
    858 					    (msgflg & MSG_NOERROR) == 0) {
    859 #ifdef MSG_DEBUG_OK
    860 						printf("requested message on the queue is too big (want %d, got %d)\n",
    861 						    msgsz, msghdr->msg_ts);
    862 #endif
    863 						return(E2BIG);
    864 					}
    865 					*prev = msghdr->msg_next;
    866 					if (msghdr == msqptr->msg_last) {
    867 						if (previous == NULL) {
    868 							if (prev !=
    869 							    &msqptr->msg_first)
    870 								panic("msg_first/last screwed up #2");
    871 							msqptr->msg_first =
    872 							    NULL;
    873 							msqptr->msg_last =
    874 							    NULL;
    875 						} else {
    876 							if (prev ==
    877 							    &msqptr->msg_first)
    878 								panic("msg_first/last screwed up #3");
    879 							msqptr->msg_last =
    880 							    previous;
    881 						}
    882 					}
    883 					break;
    884 				}
    885 				previous = msghdr;
    886 				prev = &(msghdr->msg_next);
    887 			}
    888 		}
    889 
    890 		/*
    891 		 * We've either extracted the msghdr for the appropriate
    892 		 * message or there isn't one.
    893 		 * If there is one then bail out of this loop.
    894 		 */
    895 
    896 		if (msghdr != NULL)
    897 			break;
    898 
    899 		/*
    900 		 * Hmph!  No message found.  Does the user want to wait?
    901 		 */
    902 
    903 		if ((msgflg & IPC_NOWAIT) != 0) {
    904 #ifdef MSG_DEBUG_OK
    905 			printf("no appropriate message found (msgtyp=%d)\n",
    906 			    msgtyp);
    907 #endif
    908 			/* The SVID says to return ENOMSG. */
    909 #ifdef ENOMSG
    910 			return(ENOMSG);
    911 #else
    912 			/* Unfortunately, BSD doesn't define that code yet! */
    913 			return(EAGAIN);
    914 #endif
    915 		}
    916 
    917 		/*
    918 		 * Wait for something to happen
    919 		 */
    920 
    921 #ifdef MSG_DEBUG_OK
    922 		printf("msgrcv:  goodnight\n");
    923 #endif
    924 		eval = tsleep((caddr_t)msqptr, (PZERO - 4) | PCATCH, "msgwait",
    925 		    0);
    926 #ifdef MSG_DEBUG_OK
    927 		printf("msgrcv:  good morning (eval=%d)\n", eval);
    928 #endif
    929 
    930 		if (eval != 0) {
    931 #ifdef MSG_DEBUG_OK
    932 			printf("msgsnd:  interrupted system call\n");
    933 #endif
    934 			return(EINTR);
    935 		}
    936 
    937 		/*
    938 		 * Make sure that the msq queue still exists
    939 		 */
    940 
    941 		if (msqptr->msg_qbytes == 0 ||
    942 		    msqptr->msg_perm.seq != IPCID_TO_SEQ(uap->msqid)) {
    943 #ifdef MSG_DEBUG_OK
    944 			printf("msqid deleted\n");
    945 #endif
    946 			/* The SVID says to return EIDRM. */
    947 #ifdef EIDRM
    948 			return(EIDRM);
    949 #else
    950 			/* Unfortunately, BSD doesn't define that code yet! */
    951 			return(EINVAL);
    952 #endif
    953 		}
    954 	}
    955 
    956 	/*
    957 	 * Return the message to the user.
    958 	 *
    959 	 * First, do the bookkeeping (before we risk being interrupted).
    960 	 */
    961 
    962 	msqptr->msg_cbytes -= msghdr->msg_ts;
    963 	msqptr->msg_qnum -= 1;
    964 	msqptr->msg_lrpid = p->p_pid;
    965 	msqptr->msg_rtime = time.tv_sec;
    966 
    967 	/*
    968 	 * Make msgsz the actual amount that we'll be returning.
    969 	 * Note that this effectively truncates the message if it is too long
    970 	 * (since msgsz is never increased).
    971 	 */
    972 
    973 #ifdef MSG_DEBUG_OK
    974 	printf("found a message, msgsz=%d, msg_ts=%d\n", msgsz,
    975 	    msghdr->msg_ts);
    976 #endif
    977 	if (msgsz > msghdr->msg_ts)
    978 		msgsz = msghdr->msg_ts;
    979 
    980 	/*
    981 	 * Return the type to the user.
    982 	 */
    983 
    984 	eval = copyout((caddr_t)&(msghdr->msg_type), user_msgp,
    985 	    sizeof(msghdr->msg_type));
    986 	if (eval != 0) {
    987 #ifdef MSG_DEBUG_OK
    988 		printf("error (%d) copying out message type\n", eval);
    989 #endif
    990 		msg_freehdr(msghdr);
    991 		wakeup((caddr_t)msqptr);
    992 		return(eval);
    993 	}
    994 	user_msgp += sizeof(msghdr->msg_type);
    995 
    996 	/*
    997 	 * Return the segments to the user
    998 	 */
    999 
   1000 	next = msghdr->msg_spot;
   1001 	for (len = 0; len < msgsz; len += msginfo.msgssz) {
   1002 		size_t tlen;
   1003 
   1004 		if (msgsz > msginfo.msgssz)
   1005 			tlen = msginfo.msgssz;
   1006 		else
   1007 			tlen = msgsz;
   1008 		if (next <= -1)
   1009 			panic("next too low #3");
   1010 		if (next >= msginfo.msgseg)
   1011 			panic("next out of range #3");
   1012 		eval = copyout((caddr_t)&msgpool[next * msginfo.msgssz],
   1013 		    user_msgp, tlen);
   1014 		if (eval != 0) {
   1015 #ifdef MSG_DEBUG_OK
   1016 			printf("error (%d) copying out message segment\n",
   1017 			    eval);
   1018 #endif
   1019 			msg_freehdr(msghdr);
   1020 			wakeup((caddr_t)msqptr);
   1021 			return(eval);
   1022 		}
   1023 		user_msgp += tlen;
   1024 		next = msgmaps[next].next;
   1025 	}
   1026 
   1027 	/*
   1028 	 * Done, return the actual number of bytes copied out.
   1029 	 */
   1030 
   1031 	msg_freehdr(msghdr);
   1032 	wakeup((caddr_t)msqptr);
   1033 	*retval = msgsz;
   1034 	return(0);
   1035 }
   1036