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