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