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