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