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