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