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