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