sysv_msg.c revision 2729
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 <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
40int
41msginit()
42{
43	register int i;
44	vm_offset_t whocares1, whocares2;
45
46	/*
47	 * msginfo.msgssz should be a power of two for efficiency reasons.
48	 * It is also pretty silly if msginfo.msgssz is less than 8
49	 * or greater than about 256 so ...
50	 */
51
52	i = 8;
53	while (i < 1024 && i != msginfo.msgssz)
54		i <<= 1;
55    	if (i != msginfo.msgssz) {
56		printf("msginfo.msgssz=%d (0x%x)\n", msginfo.msgssz,
57		    msginfo.msgssz);
58		panic("msginfo.msgssz not a small power of 2");
59	}
60
61	if (msginfo.msgseg > 32767) {
62		printf("msginfo.msgseg=%d\n", msginfo.msgseg);
63		panic("msginfo.msgseg > 32767");
64	}
65
66	if (msgmaps == NULL)
67		panic("msgmaps is NULL");
68
69	for (i = 0; i < msginfo.msgseg; i++) {
70		if (i > 0)
71			msgmaps[i-1].next = i;
72		msgmaps[i].next = -1;	/* implies entry is available */
73	}
74	free_msgmaps = 0;
75	nfree_msgmaps = msginfo.msgseg;
76
77	if (msghdrs == NULL)
78		panic("msghdrs is NULL");
79
80	for (i = 0; i < msginfo.msgtql; i++) {
81		msghdrs[i].msg_type = 0;
82		if (i > 0)
83			msghdrs[i-1].msg_next = &msghdrs[i];
84		msghdrs[i].msg_next = NULL;
85    	}
86	free_msghdrs = &msghdrs[0];
87
88	if (msqids == NULL)
89		panic("msqids is NULL");
90
91	for (i = 0; i < msginfo.msgmni; i++) {
92		msqids[i].msg_qbytes = 0;	/* implies entry is available */
93		msqids[i].msg_perm.seq = 0;	/* reset to a known value */
94	}
95}
96
97/*
98 * Entry point for all MSG calls
99 */
100
101struct msgsys_args {
102	u_int	which;
103};
104
105int
106msgsys(p, uap, retval)
107	struct caller *p;
108	struct msgsys_args *uap;
109	int *retval;
110{
111
112	if (uap->which >= sizeof(msgcalls)/sizeof(msgcalls[0]))
113		return (EINVAL);
114	return ((*msgcalls[uap->which])(p, &uap[1], retval));
115}
116
117static void
118msg_freehdr(msghdr)
119	struct msg *msghdr;
120{
121	while (msghdr->msg_ts > 0) {
122		short next;
123		if (msghdr->msg_spot < 0 || msghdr->msg_spot >= msginfo.msgseg)
124			panic("msghdr->msg_spot out of range");
125		next = msgmaps[msghdr->msg_spot].next;
126		msgmaps[msghdr->msg_spot].next = free_msgmaps;
127		free_msgmaps = msghdr->msg_spot;
128		nfree_msgmaps++;
129		msghdr->msg_spot = next;
130		if (msghdr->msg_ts >= msginfo.msgssz)
131			msghdr->msg_ts -= msginfo.msgssz;
132		else
133			msghdr->msg_ts = 0;
134	}
135	if (msghdr->msg_spot != -1)
136		panic("msghdr->msg_spot != -1");
137	msghdr->msg_next = free_msghdrs;
138	free_msghdrs = msghdr;
139}
140
141struct msgctl_args {
142	int	msqid;
143	int	cmd;
144	struct	msqid_ds *user_msqptr;
145};
146
147int
148msgctl(p, uap, retval)
149	struct proc *p;
150	register struct msgctl_args *uap;
151	int *retval;
152{
153	int msqid = uap->msqid;
154	int cmd = uap->cmd;
155	struct msqid_ds *user_msqptr = uap->user_msqptr;
156	struct ucred *cred = p->p_ucred;
157	int i, rval, eval;
158	struct msqid_ds msqbuf;
159	register struct msqid_ds *msqptr;
160
161#ifdef MSG_DEBUG_OK
162	printf("call to msgctl(%d, %d, 0x%x)\n", msqid, cmd, user_msqptr);
163#endif
164
165	msqid = IPCID_TO_IX(msqid);
166
167	if (msqid < 0 || msqid >= msginfo.msgmni) {
168#ifdef MSG_DEBUG_OK
169		printf("msqid (%d) out of range (0<=msqid<%d)\n", msqid,
170		    msginfo.msgmni);
171#endif
172		return(EINVAL);
173	}
174
175	msqptr = &msqids[msqid];
176
177	if (msqptr->msg_qbytes == 0) {
178#ifdef MSG_DEBUG_OK
179		printf("no such msqid\n");
180#endif
181		return(EINVAL);
182	}
183	if (msqptr->msg_perm.seq != IPCID_TO_SEQ(uap->msqid)) {
184#ifdef MSG_DEBUG_OK
185		printf("wrong sequence number\n");
186#endif
187		return(EINVAL);
188	}
189
190	eval = 0;
191	rval = 0;
192
193	switch (cmd) {
194
195	case IPC_RMID:
196	{
197		struct msg *msghdr;
198		if ((eval = ipcperm(cred, &msqptr->msg_perm, IPC_M)))
199			return(eval);
200		/* Free the message headers */
201		msghdr = msqptr->msg_first;
202		while (msghdr != NULL) {
203			struct msg *msghdr_tmp;
204
205			/* Free the segments of each message */
206			msqptr->msg_cbytes -= msghdr->msg_ts;
207			msqptr->msg_qnum--;
208			msghdr_tmp = msghdr;
209			msghdr = msghdr->msg_next;
210			msg_freehdr(msghdr_tmp);
211		}
212
213		if (msqptr->msg_cbytes != 0)
214			panic("msg_cbytes is screwed up");
215		if (msqptr->msg_qnum != 0)
216			panic("msg_qnum is screwed up");
217
218		msqptr->msg_qbytes = 0;	/* Mark it as free */
219
220		wakeup((caddr_t)msqptr);
221	}
222
223		break;
224
225	case IPC_SET:
226		if ((eval = ipcperm(cred, &msqptr->msg_perm, IPC_M)))
227			return(eval);
228		if ((eval = copyin(user_msqptr, &msqbuf, sizeof(msqbuf))) != 0)
229			return(eval);
230		if (msqbuf.msg_qbytes > msqptr->msg_qbytes && cred->cr_uid != 0)
231			return(EPERM);
232		if (msqbuf.msg_qbytes > msginfo.msgmnb) {
233#ifdef MSG_DEBUG_OK
234			printf("can't increase msg_qbytes beyond %d (truncating)\n",
235			    msginfo.msgmnb);
236#endif
237			msqbuf.msg_qbytes = msginfo.msgmnb;	/* silently restrict qbytes to system limit */
238		}
239		if (msqbuf.msg_qbytes == 0) {
240#ifdef MSG_DEBUG_OK
241			printf("can't reduce msg_qbytes to 0\n");
242#endif
243			return(EINVAL);		/* non-standard errno! */
244		}
245		msqptr->msg_perm.uid = msqbuf.msg_perm.uid;	/* change the owner */
246		msqptr->msg_perm.gid = msqbuf.msg_perm.gid;	/* change the owner */
247		msqptr->msg_perm.mode = (msqptr->msg_perm.mode & ~0777) |
248		    (msqbuf.msg_perm.mode & 0777);
249		msqptr->msg_qbytes = msqbuf.msg_qbytes;
250		msqptr->msg_ctime = time.tv_sec;
251		break;
252
253	case IPC_STAT:
254		if ((eval = ipcperm(cred, &msqptr->msg_perm, IPC_R))) {
255#ifdef MSG_DEBUG_OK
256			printf("requester doesn't have read access\n");
257#endif
258			return(eval);
259		}
260		eval = copyout((caddr_t)msqptr, user_msqptr,
261		    sizeof(struct msqid_ds));
262		break;
263
264	default:
265#ifdef MSG_DEBUG_OK
266		printf("invalid command %d\n", cmd);
267#endif
268		return(EINVAL);
269	}
270
271	if (eval == 0)
272		*retval = rval;
273	return(eval);
274}
275
276struct msgget_args {
277	key_t	key;
278	int	msgflg;
279};
280
281int
282msgget(p, uap, retval)
283	struct proc *p;
284	register struct msgget_args *uap;
285	int *retval;
286{
287	int msqid, eval;
288	int key = uap->key;
289	int msgflg = uap->msgflg;
290	struct ucred *cred = p->p_ucred;
291	register struct msqid_ds *msqptr;
292
293#ifdef MSG_DEBUG_OK
294	printf("msgget(0x%x, 0%o)\n", key, msgflg);
295#endif
296
297	if (key != IPC_PRIVATE) {
298		for (msqid = 0; msqid < msginfo.msgmni; msqid++) {
299			msqptr = &msqids[msqid];
300			if (msqptr->msg_qbytes != 0 &&
301			    msqptr->msg_perm.key == key)
302				break;
303		}
304		if (msqid < msginfo.msgmni) {
305#ifdef MSG_DEBUG_OK
306			printf("found public key\n");
307#endif
308			if ((msgflg & IPC_CREAT) && (msgflg & IPC_EXCL)) {
309#ifdef MSG_DEBUG_OK
310				printf("not exclusive\n");
311#endif
312				return(EEXIST);
313			}
314			if ((eval = ipcperm(cred, &msqptr->msg_perm, msgflg & 0700 ))) {
315#ifdef MSG_DEBUG_OK
316				printf("requester doesn't have 0%o access\n",
317				    msgflg & 0700);
318#endif
319				return(eval);
320			}
321			goto found;
322		}
323	}
324
325#ifdef MSG_DEBUG_OK
326	printf("need to allocate the msqid_ds\n");
327#endif
328	if (key == IPC_PRIVATE || (msgflg & IPC_CREAT)) {
329		for (msqid = 0; msqid < msginfo.msgmni; msqid++) {
330			/*
331			 * Look for an unallocated and unlocked msqid_ds.
332			 * msqid_ds's can be locked by msgsnd or msgrcv while
333			 * they are copying the message in/out.  We can't
334			 * re-use the entry until they release it.
335			 */
336			msqptr = &msqids[msqid];
337			if (msqptr->msg_qbytes == 0 &&
338			    (msqptr->msg_perm.mode & MSG_LOCKED) == 0)
339				break;
340		}
341		if (msqid == msginfo.msgmni) {
342#ifdef MSG_DEBUG_OK
343			printf("no more msqid_ds's available\n");
344#endif
345			return(ENOSPC);
346		}
347#ifdef MSG_DEBUG_OK
348		printf("msqid %d is available\n", msqid);
349#endif
350		msqptr->msg_perm.key = key;
351		msqptr->msg_perm.cuid = cred->cr_uid;
352		msqptr->msg_perm.uid = cred->cr_uid;
353		msqptr->msg_perm.cgid = cred->cr_gid;
354		msqptr->msg_perm.gid = cred->cr_gid;
355		msqptr->msg_perm.mode = (msgflg & 0777);
356		/* Make sure that the returned msqid is unique */
357		msqptr->msg_perm.seq++;
358		msqptr->msg_first = NULL;
359		msqptr->msg_last = NULL;
360		msqptr->msg_cbytes = 0;
361		msqptr->msg_qnum = 0;
362		msqptr->msg_qbytes = msginfo.msgmnb;
363		msqptr->msg_lspid = 0;
364		msqptr->msg_lrpid = 0;
365		msqptr->msg_stime = 0;
366		msqptr->msg_rtime = 0;
367		msqptr->msg_ctime = time.tv_sec;
368	} else {
369#ifdef MSG_DEBUG_OK
370		printf("didn't find it and wasn't asked to create it\n");
371#endif
372		return(ENOENT);
373	}
374
375found:
376	/* Construct the unique msqid */
377	*retval = IXSEQ_TO_IPCID(msqid, msqptr->msg_perm);
378	return(0);
379}
380
381struct msgsnd_args {
382	int	msqid;
383	void	*user_msgp;
384	size_t	msgsz;
385	int	msgflg;
386};
387
388int
389msgsnd(p, uap, retval)
390	struct proc *p;
391	register struct msgsnd_args *uap;
392	int *retval;
393{
394	int msqid = uap->msqid;
395	void *user_msgp = uap->user_msgp;
396	size_t msgsz = uap->msgsz;
397	int msgflg = uap->msgflg;
398	int segs_needed, eval;
399	struct ucred *cred = p->p_ucred;
400	register struct msqid_ds *msqptr;
401	register struct msg *msghdr;
402	short next;
403
404#ifdef MSG_DEBUG_OK
405	printf("call to msgsnd(%d, 0x%x, %d, %d)\n", msqid, user_msgp, msgsz,
406	    msgflg);
407#endif
408
409	msqid = IPCID_TO_IX(msqid);
410
411	if (msqid < 0 || msqid >= msginfo.msgmni) {
412#ifdef MSG_DEBUG_OK
413		printf("msqid (%d) out of range (0<=msqid<%d)\n", msqid,
414		    msginfo.msgmni);
415#endif
416		return(EINVAL);
417	}
418
419	msqptr = &msqids[msqid];
420	if (msqptr->msg_qbytes == 0) {
421#ifdef MSG_DEBUG_OK
422		printf("no such message queue id\n");
423#endif
424		return(EINVAL);
425	}
426	if (msqptr->msg_perm.seq != IPCID_TO_SEQ(uap->msqid)) {
427#ifdef MSG_DEBUG_OK
428		printf("wrong sequence number\n");
429#endif
430		return(EINVAL);
431	}
432
433	if ((eval = ipcperm(cred, &msqptr->msg_perm, IPC_W))) {
434#ifdef MSG_DEBUG_OK
435		printf("requester doesn't have write access\n");
436#endif
437		return(eval);
438	}
439
440	segs_needed = (msgsz + msginfo.msgssz - 1) / msginfo.msgssz;
441#ifdef MSG_DEBUG_OK
442	printf("msgsz=%d, msgssz=%d, segs_needed=%d\n", msgsz, msginfo.msgssz,
443	    segs_needed);
444#endif
445	for (;;) {
446		int need_more_resources = 0;
447
448		/*
449		 * check msgsz
450		 * (inside this loop in case msg_qbytes changes while we sleep)
451		 */
452
453		if (msgsz < 0 || msgsz > msqptr->msg_qbytes) {
454#ifdef MSG_DEBUG_OK
455			printf("msgsz > msqptr->msg_qbytes\n");
456#endif
457			return(EINVAL);
458		}
459
460		if (msqptr->msg_perm.mode & MSG_LOCKED) {
461#ifdef MSG_DEBUG_OK
462			printf("msqid is locked\n");
463#endif
464			need_more_resources = 1;
465		}
466		if (msgsz + msqptr->msg_cbytes > msqptr->msg_qbytes) {
467#ifdef MSG_DEBUG_OK
468			printf("msgsz + msg_cbytes > msg_qbytes\n");
469#endif
470			need_more_resources = 1;
471		}
472		if (segs_needed > nfree_msgmaps) {
473#ifdef MSG_DEBUG_OK
474			printf("segs_needed > nfree_msgmaps\n");
475#endif
476			need_more_resources = 1;
477		}
478		if (free_msghdrs == NULL) {
479#ifdef MSG_DEBUG_OK
480			printf("no more msghdrs\n");
481#endif
482			need_more_resources = 1;
483		}
484
485		if (need_more_resources) {
486			int we_own_it;
487
488			if ((msgflg & IPC_NOWAIT) != 0) {
489#ifdef MSG_DEBUG_OK
490				printf("need more resources but caller doesn't want to wait\n");
491#endif
492				return(EAGAIN);
493			}
494
495			if ((msqptr->msg_perm.mode & MSG_LOCKED) != 0) {
496#ifdef MSG_DEBUG_OK
497				printf("we don't own the msqid_ds\n");
498#endif
499				we_own_it = 0;
500			} else {
501				/* Force later arrivals to wait for our
502				   request */
503#ifdef MSG_DEBUG_OK
504				printf("we own the msqid_ds\n");
505#endif
506				msqptr->msg_perm.mode |= MSG_LOCKED;
507				we_own_it = 1;
508			}
509#ifdef MSG_DEBUG_OK
510			printf("goodnight\n");
511#endif
512			eval = tsleep((caddr_t)msqptr, (PZERO - 4) | PCATCH,
513			    "msgwait", 0);
514#ifdef MSG_DEBUG_OK
515			printf("good morning, eval=%d\n", eval);
516#endif
517			if (we_own_it)
518				msqptr->msg_perm.mode &= ~MSG_LOCKED;
519			if (eval != 0) {
520#ifdef MSG_DEBUG_OK
521				printf("msgsnd:  interrupted system call\n");
522#endif
523				return(EINTR);
524			}
525
526			/*
527			 * Make sure that the msq queue still exists
528			 */
529
530			if (msqptr->msg_qbytes == 0) {
531#ifdef MSG_DEBUG_OK
532				printf("msqid deleted\n");
533#endif
534				/* The SVID says to return EIDRM. */
535#ifdef EIDRM
536				return(EIDRM);
537#else
538				/* Unfortunately, BSD doesn't define that code
539				   yet! */
540				return(EINVAL);
541#endif
542			}
543
544		} else {
545#ifdef MSG_DEBUG_OK
546			printf("got all the resources that we need\n");
547#endif
548			break;
549		}
550	}
551
552	/*
553	 * We have the resources that we need.
554	 * Make sure!
555	 */
556
557	if (msqptr->msg_perm.mode & MSG_LOCKED)
558		panic("msg_perm.mode & MSG_LOCKED");
559	if (segs_needed > nfree_msgmaps)
560		panic("segs_needed > nfree_msgmaps");
561	if (msgsz + msqptr->msg_cbytes > msqptr->msg_qbytes)
562		panic("msgsz + msg_cbytes > msg_qbytes");
563	if (free_msghdrs == NULL)
564		panic("no more msghdrs");
565
566	/*
567	 * Re-lock the msqid_ds in case we page-fault when copying in the
568	 * message
569	 */
570
571	if ((msqptr->msg_perm.mode & MSG_LOCKED) != 0)
572		panic("msqid_ds is already locked");
573	msqptr->msg_perm.mode |= MSG_LOCKED;
574
575	/*
576	 * Allocate a message header
577	 */
578
579	msghdr = free_msghdrs;
580	free_msghdrs = msghdr->msg_next;
581	msghdr->msg_spot = -1;
582	msghdr->msg_ts = msgsz;
583
584	/*
585	 * Allocate space for the message
586	 */
587
588	while (segs_needed > 0) {
589		if (nfree_msgmaps <= 0)
590			panic("not enough msgmaps");
591		if (free_msgmaps == -1)
592			panic("nil free_msgmaps");
593		next = free_msgmaps;
594		if (next <= -1)
595			panic("next too low #1");
596		if (next >= msginfo.msgseg)
597			panic("next out of range #1");
598#ifdef MSG_DEBUG_OK
599		printf("allocating segment %d to message\n", next);
600#endif
601		free_msgmaps = msgmaps[next].next;
602		nfree_msgmaps--;
603		msgmaps[next].next = msghdr->msg_spot;
604		msghdr->msg_spot = next;
605		segs_needed--;
606	}
607
608	/*
609	 * Copy in the message type
610	 */
611
612	if ((eval = copyin(user_msgp, &msghdr->msg_type,
613	    sizeof(msghdr->msg_type))) != 0) {
614#ifdef MSG_DEBUG_OK
615		printf("error %d copying the message type\n", eval);
616#endif
617		msg_freehdr(msghdr);
618		msqptr->msg_perm.mode &= ~MSG_LOCKED;
619		wakeup((caddr_t)msqptr);
620		return(eval);
621	}
622	user_msgp += sizeof(msghdr->msg_type);
623
624	/*
625	 * Validate the message type
626	 */
627
628	if (msghdr->msg_type < 1) {
629		msg_freehdr(msghdr);
630		msqptr->msg_perm.mode &= ~MSG_LOCKED;
631		wakeup((caddr_t)msqptr);
632#ifdef MSG_DEBUG_OK
633		printf("mtype (%d) < 1\n", msghdr->msg_type);
634#endif
635		return(EINVAL);
636	}
637
638	/*
639	 * Copy in the message body
640	 */
641
642	next = msghdr->msg_spot;
643	while (msgsz > 0) {
644		size_t tlen;
645		if (msgsz > msginfo.msgssz)
646			tlen = msginfo.msgssz;
647		else
648			tlen = msgsz;
649		if (next <= -1)
650			panic("next too low #2");
651		if (next >= msginfo.msgseg)
652			panic("next out of range #2");
653		if ((eval = copyin(user_msgp, &msgpool[next * msginfo.msgssz],
654		    tlen)) != 0) {
655#ifdef MSG_DEBUG_OK
656			printf("error %d copying in message segment\n", eval);
657#endif
658			msg_freehdr(msghdr);
659			msqptr->msg_perm.mode &= ~MSG_LOCKED;
660			wakeup((caddr_t)msqptr);
661			return(eval);
662		}
663		msgsz -= tlen;
664		user_msgp += tlen;
665		next = msgmaps[next].next;
666	}
667	if (next != -1)
668		panic("didn't use all the msg segments");
669
670	/*
671	 * We've got the message.  Unlock the msqid_ds.
672	 */
673
674	msqptr->msg_perm.mode &= ~MSG_LOCKED;
675
676	/*
677	 * Make sure that the msqid_ds is still allocated.
678	 */
679
680	if (msqptr->msg_qbytes == 0) {
681		msg_freehdr(msghdr);
682		wakeup((caddr_t)msqptr);
683		/* The SVID says to return EIDRM. */
684#ifdef EIDRM
685		return(EIDRM);
686#else
687		/* Unfortunately, BSD doesn't define that code yet! */
688		return(EINVAL);
689#endif
690	}
691
692	/*
693	 * Put the message into the queue
694	 */
695
696	if (msqptr->msg_first == NULL) {
697		msqptr->msg_first = msghdr;
698		msqptr->msg_last = msghdr;
699	} else {
700		msqptr->msg_last->msg_next = msghdr;
701		msqptr->msg_last = msghdr;
702	}
703	msqptr->msg_last->msg_next = NULL;
704
705	msqptr->msg_cbytes += msghdr->msg_ts;
706	msqptr->msg_qnum++;
707	msqptr->msg_lspid = p->p_pid;
708	msqptr->msg_stime = time.tv_sec;
709
710	wakeup((caddr_t)msqptr);
711	*retval = 0;
712	return(0);
713}
714
715struct msgrcv_args {
716	int	msqid;
717	void	*msgp;
718	size_t	msgsz;
719	long	msgtyp;
720	int	msgflg;
721};
722
723int
724msgrcv(p, uap, retval)
725	struct proc *p;
726	register struct msgrcv_args *uap;
727	int *retval;
728{
729	int msqid = uap->msqid;
730	void *user_msgp = uap->msgp;
731	size_t msgsz = uap->msgsz;
732	long msgtyp = uap->msgtyp;
733	int msgflg = uap->msgflg;
734	size_t len;
735	struct ucred *cred = p->p_ucred;
736	register struct msqid_ds *msqptr;
737	register struct msg *msghdr;
738	int eval;
739	short next;
740
741#ifdef MSG_DEBUG_OK
742	printf("call to msgrcv(%d, 0x%x, %d, %ld, %d)\n", msqid, user_msgp,
743	    msgsz, msgtyp, msgflg);
744#endif
745
746	msqid = IPCID_TO_IX(msqid);
747
748	if (msqid < 0 || msqid >= msginfo.msgmni) {
749#ifdef MSG_DEBUG_OK
750		printf("msqid (%d) out of range (0<=msqid<%d)\n", msqid,
751		    msginfo.msgmni);
752#endif
753		return(EINVAL);
754	}
755
756	msqptr = &msqids[msqid];
757	if (msqptr->msg_qbytes == 0) {
758#ifdef MSG_DEBUG_OK
759		printf("no such message queue id\n");
760#endif
761		return(EINVAL);
762	}
763	if (msqptr->msg_perm.seq != IPCID_TO_SEQ(uap->msqid)) {
764#ifdef MSG_DEBUG_OK
765		printf("wrong sequence number\n");
766#endif
767		return(EINVAL);
768	}
769
770	if ((eval = ipcperm(cred, &msqptr->msg_perm, IPC_R))) {
771#ifdef MSG_DEBUG_OK
772		printf("requester doesn't have read access\n");
773#endif
774		return(eval);
775	}
776
777	if (msgsz < 0) {
778#ifdef MSG_DEBUG_OK
779		printf("msgsz < 0\n");
780#endif
781		return(EINVAL);
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