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