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