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