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