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