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