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