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