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