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