xref: /netbsd-src/sys/kern/sysv_msg.c (revision 81b108b45f75f89f1e3ffad9fb6f074e771c0935)
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