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