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