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