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