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