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