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