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