sysv_msg.c revision 72786
1/* $FreeBSD: head/sys/kern/sysv_msg.c 72786 2001-02-21 06:39:57Z rwatson $ */
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_mod = {
249	"sysvmsg",
250	&sysvmsg_modload,
251	NULL
252};
253
254SYSCALL_MODULE_HELPER(msgsys, 6);
255SYSCALL_MODULE_HELPER(msgctl, 3);
256SYSCALL_MODULE_HELPER(msgget, 2);
257SYSCALL_MODULE_HELPER(msgsnd, 4);
258SYSCALL_MODULE_HELPER(msgrcv, 5);
259
260DECLARE_MODULE(sysvmsg, sysvmsg_mod,
261	SI_SUB_SYSV_MSG, SI_ORDER_FIRST);
262MODULE_VERSION(sysvmsg, 1);
263
264/*
265 * Entry point for all MSG calls
266 */
267int
268msgsys(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
289static void
290msg_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_
314struct msgctl_args {
315	int	msqid;
316	int	cmd;
317	struct	msqid_ds *buf;
318};
319#endif
320
321int
322msgctl(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_
455struct msgget_args {
456	key_t	key;
457	int	msgflg;
458};
459#endif
460
461int
462msgget(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
557found:
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_
564struct msgsnd_args {
565	int	msqid;
566	void	*msgp;
567	size_t	msgsz;
568	int	msgflg;
569};
570#endif
571
572int
573msgsnd(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_
888struct msgrcv_args {
889	int	msqid;
890	void	*msgp;
891	size_t	msgsz;
892	long	msgtyp;
893	int	msgflg;
894};
895#endif
896
897int
898msgrcv(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