sysv_msg.c revision 2836
1/*	$Id: sysv_msg.c,v 1.1 1994/09/13 14:46:57 dfr 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	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 = NULL;
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 > 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	msghdr = NULL;
778	while (msghdr == NULL) {
779		if (msgtyp == 0) {
780			msghdr = msqptr->msg_first;
781			if (msghdr != NULL) {
782				if (msgsz < msghdr->msg_ts &&
783				    (msgflg & MSG_NOERROR) == 0) {
784#ifdef MSG_DEBUG_OK
785					printf("first message on the queue is too big (want %d, got %d)\n",
786					    msgsz, msghdr->msg_ts);
787#endif
788					return(E2BIG);
789				}
790				if (msqptr->msg_first == msqptr->msg_last) {
791					msqptr->msg_first = NULL;
792					msqptr->msg_last = NULL;
793				} else {
794					msqptr->msg_first = msghdr->msg_next;
795					if (msqptr->msg_first == NULL)
796						panic("msg_first/last screwed up #1");
797				}
798			}
799		} else {
800			struct msg *previous;
801			struct msg **prev;
802
803			previous = NULL;
804			prev = &(msqptr->msg_first);
805			while ((msghdr = *prev) != NULL) {
806				/*
807				 * Is this message's type an exact match or is
808				 * this message's type less than or equal to
809				 * the absolute value of a negative msgtyp?
810				 * Note that the second half of this test can
811				 * NEVER be true if msgtyp is positive since
812				 * msg_type is always positive!
813				 */
814
815				if (msgtyp == msghdr->msg_type ||
816				    msghdr->msg_type <= -msgtyp) {
817#ifdef MSG_DEBUG_OK
818					printf("found message type %d, requested %d\n",
819					    msghdr->msg_type, msgtyp);
820#endif
821					if (msgsz < msghdr->msg_ts &&
822					    (msgflg & MSG_NOERROR) == 0) {
823#ifdef MSG_DEBUG_OK
824						printf("requested message on the queue is too big (want %d, got %d)\n",
825						    msgsz, msghdr->msg_ts);
826#endif
827						return(E2BIG);
828					}
829					*prev = msghdr->msg_next;
830					if (msghdr == msqptr->msg_last) {
831						if (previous == NULL) {
832							if (prev !=
833							    &msqptr->msg_first)
834								panic("msg_first/last screwed up #2");
835							msqptr->msg_first =
836							    NULL;
837							msqptr->msg_last =
838							    NULL;
839						} else {
840							if (prev ==
841							    &msqptr->msg_first)
842								panic("msg_first/last screwed up #3");
843							msqptr->msg_last =
844							    previous;
845						}
846					}
847					break;
848				}
849				previous = msghdr;
850				prev = &(msghdr->msg_next);
851			}
852		}
853
854		/*
855		 * We've either extracted the msghdr for the appropriate
856		 * message or there isn't one.
857		 * If there is one then bail out of this loop.
858		 */
859
860		if (msghdr != NULL)
861			break;
862
863		/*
864		 * Hmph!  No message found.  Does the user want to wait?
865		 */
866
867		if ((msgflg & IPC_NOWAIT) != 0) {
868#ifdef MSG_DEBUG_OK
869			printf("no appropriate message found (msgtyp=%d)\n",
870			    msgtyp);
871#endif
872			/* The SVID says to return ENOMSG. */
873#ifdef ENOMSG
874			return(ENOMSG);
875#else
876			/* Unfortunately, BSD doesn't define that code yet! */
877			return(EAGAIN);
878#endif
879		}
880
881		/*
882		 * Wait for something to happen
883		 */
884
885#ifdef MSG_DEBUG_OK
886		printf("msgrcv:  goodnight\n");
887#endif
888		eval = tsleep((caddr_t)msqptr, (PZERO - 4) | PCATCH, "msgwait",
889		    0);
890#ifdef MSG_DEBUG_OK
891		printf("msgrcv:  good morning (eval=%d)\n", eval);
892#endif
893
894		if (eval != 0) {
895#ifdef MSG_DEBUG_OK
896			printf("msgsnd:  interrupted system call\n");
897#endif
898			return(EINTR);
899		}
900
901		/*
902		 * Make sure that the msq queue still exists
903		 */
904
905		if (msqptr->msg_qbytes == 0 ||
906		    msqptr->msg_perm.seq != IPCID_TO_SEQ(uap->msqid)) {
907#ifdef MSG_DEBUG_OK
908			printf("msqid deleted\n");
909#endif
910			/* The SVID says to return EIDRM. */
911#ifdef EIDRM
912			return(EIDRM);
913#else
914			/* Unfortunately, BSD doesn't define that code yet! */
915			return(EINVAL);
916#endif
917		}
918	}
919
920	/*
921	 * Return the message to the user.
922	 *
923	 * First, do the bookkeeping (before we risk being interrupted).
924	 */
925
926	msqptr->msg_cbytes -= msghdr->msg_ts;
927	msqptr->msg_qnum--;
928	msqptr->msg_lrpid = p->p_pid;
929	msqptr->msg_rtime = time.tv_sec;
930
931	/*
932	 * Make msgsz the actual amount that we'll be returning.
933	 * Note that this effectively truncates the message if it is too long
934	 * (since msgsz is never increased).
935	 */
936
937#ifdef MSG_DEBUG_OK
938	printf("found a message, msgsz=%d, msg_ts=%d\n", msgsz,
939	    msghdr->msg_ts);
940#endif
941	if (msgsz > msghdr->msg_ts)
942		msgsz = msghdr->msg_ts;
943
944	/*
945	 * Return the type to the user.
946	 */
947
948	eval = copyout((caddr_t)&(msghdr->msg_type), user_msgp,
949	    sizeof(msghdr->msg_type));
950	if (eval != 0) {
951#ifdef MSG_DEBUG_OK
952		printf("error (%d) copying out message type\n", eval);
953#endif
954		msg_freehdr(msghdr);
955		wakeup((caddr_t)msqptr);
956		return(eval);
957	}
958	user_msgp += sizeof(msghdr->msg_type);
959
960	/*
961	 * Return the segments to the user
962	 */
963
964	next = msghdr->msg_spot;
965	for (len = 0; len < msgsz; len += msginfo.msgssz) {
966		size_t tlen;
967
968		if (msgsz > msginfo.msgssz)
969			tlen = msginfo.msgssz;
970		else
971			tlen = msgsz;
972		if (next <= -1)
973			panic("next too low #3");
974		if (next >= msginfo.msgseg)
975			panic("next out of range #3");
976		eval = copyout((caddr_t)&msgpool[next * msginfo.msgssz],
977		    user_msgp, tlen);
978		if (eval != 0) {
979#ifdef MSG_DEBUG_OK
980			printf("error (%d) copying out message segment\n",
981			    eval);
982#endif
983			msg_freehdr(msghdr);
984			wakeup((caddr_t)msqptr);
985			return(eval);
986		}
987		user_msgp += tlen;
988		next = msgmaps[next].next;
989	}
990
991	/*
992	 * Done, return the actual number of bytes copied out.
993	 */
994
995	msg_freehdr(msghdr);
996	wakeup((caddr_t)msqptr);
997	*retval = msgsz;
998	return(0);
999}
1000