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