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