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