xref: /freebsd/sys/kern/sysv_msg.c (revision 953a3198a35204535cc9d450f04da982a4fea59b)
1 /*	$Id: sysv_msg.c,v 1.7 1995/08/30 00:33:00 bde Exp $ */
2 
3 /*
4  * Implementation of SVID messages
5  *
6  * Author:  Daniel Boulet
7  *
8  * Copyright 1993 Daniel Boulet and RTMX Inc.
9  *
10  * This system call was implemented by Daniel Boulet under contract from RTMX.
11  *
12  * Redistribution and use in source forms, with and without modification,
13  * are permitted provided that this entire comment appears intact.
14  *
15  * Redistribution in binary form may occur without any restrictions.
16  * Obviously, it would be nice if you gave credit where credit is due
17  * but requiring it would be too onerous.
18  *
19  * This software is provided ``AS IS'' without any warranties of any kind.
20  */
21 
22 #include <sys/param.h>
23 #include <sys/systm.h>
24 #include <sys/kernel.h>
25 #include <sys/proc.h>
26 #include <sys/msg.h>
27 #include <sys/malloc.h>
28 
29 static void msginit __P((void *));
30 SYSINIT(sysv_msg, SI_SUB_SYSV_MSG, SI_ORDER_FIRST, msginit, NULL)
31 
32 #define MSG_DEBUG
33 #undef MSG_DEBUG_OK
34 
35 static int	msgctl(), msgget(), msgsnd(), msgrcv();
36 
37 int	(*msgcalls[])() = { msgctl, msgget, msgsnd, msgrcv };
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
48 msginit(udata)
49 	void *udata;
50 {
51 	register int i;
52 
53 	/*
54 	 * msginfo.msgssz should be a power of two for efficiency reasons.
55 	 * It is also pretty silly if msginfo.msgssz is less than 8
56 	 * or greater than about 256 so ...
57 	 */
58 
59 	i = 8;
60 	while (i < 1024 && i != msginfo.msgssz)
61 		i <<= 1;
62     	if (i != msginfo.msgssz) {
63 		printf("msginfo.msgssz=%d (0x%x)\n", msginfo.msgssz,
64 		    msginfo.msgssz);
65 		panic("msginfo.msgssz not a small power of 2");
66 	}
67 
68 	if (msginfo.msgseg > 32767) {
69 		printf("msginfo.msgseg=%d\n", msginfo.msgseg);
70 		panic("msginfo.msgseg > 32767");
71 	}
72 
73 	if (msgmaps == NULL)
74 		panic("msgmaps is NULL");
75 
76 	for (i = 0; i < msginfo.msgseg; i++) {
77 		if (i > 0)
78 			msgmaps[i-1].next = i;
79 		msgmaps[i].next = -1;	/* implies entry is available */
80 	}
81 	free_msgmaps = 0;
82 	nfree_msgmaps = msginfo.msgseg;
83 
84 	if (msghdrs == NULL)
85 		panic("msghdrs is NULL");
86 
87 	for (i = 0; i < msginfo.msgtql; i++) {
88 		msghdrs[i].msg_type = 0;
89 		if (i > 0)
90 			msghdrs[i-1].msg_next = &msghdrs[i];
91 		msghdrs[i].msg_next = NULL;
92     	}
93 	free_msghdrs = &msghdrs[0];
94 
95 	if (msqids == NULL)
96 		panic("msqids is NULL");
97 
98 	for (i = 0; i < msginfo.msgmni; i++) {
99 		msqids[i].msg_qbytes = 0;	/* implies entry is available */
100 		msqids[i].msg_perm.seq = 0;	/* reset to a known value */
101 	}
102 }
103 
104 /*
105  * Entry point for all MSG calls
106  */
107 
108 struct msgsys_args {
109 	u_int	which;
110 };
111 
112 int
113 msgsys(p, uap, retval)
114 	struct caller *p;
115 	struct msgsys_args *uap;
116 	int *retval;
117 {
118 
119 	if (uap->which >= sizeof(msgcalls)/sizeof(msgcalls[0]))
120 		return (EINVAL);
121 	return ((*msgcalls[uap->which])(p, &uap[1], retval));
122 }
123 
124 static void
125 msg_freehdr(msghdr)
126 	struct msg *msghdr;
127 {
128 	while (msghdr->msg_ts > 0) {
129 		short next;
130 		if (msghdr->msg_spot < 0 || msghdr->msg_spot >= msginfo.msgseg)
131 			panic("msghdr->msg_spot out of range");
132 		next = msgmaps[msghdr->msg_spot].next;
133 		msgmaps[msghdr->msg_spot].next = free_msgmaps;
134 		free_msgmaps = msghdr->msg_spot;
135 		nfree_msgmaps++;
136 		msghdr->msg_spot = next;
137 		if (msghdr->msg_ts >= msginfo.msgssz)
138 			msghdr->msg_ts -= msginfo.msgssz;
139 		else
140 			msghdr->msg_ts = 0;
141 	}
142 	if (msghdr->msg_spot != -1)
143 		panic("msghdr->msg_spot != -1");
144 	msghdr->msg_next = free_msghdrs;
145 	free_msghdrs = msghdr;
146 }
147 
148 struct msgctl_args {
149 	int	msqid;
150 	int	cmd;
151 	struct	msqid_ds *user_msqptr;
152 };
153 
154 int
155 msgctl(p, uap, retval)
156 	struct proc *p;
157 	register struct msgctl_args *uap;
158 	int *retval;
159 {
160 	int msqid = uap->msqid;
161 	int cmd = uap->cmd;
162 	struct msqid_ds *user_msqptr = uap->user_msqptr;
163 	struct ucred *cred = p->p_ucred;
164 	int rval, eval;
165 	struct msqid_ds msqbuf;
166 	register struct msqid_ds *msqptr;
167 
168 #ifdef MSG_DEBUG_OK
169 	printf("call to msgctl(%d, %d, 0x%x)\n", msqid, cmd, user_msqptr);
170 #endif
171 
172 	msqid = IPCID_TO_IX(msqid);
173 
174 	if (msqid < 0 || msqid >= msginfo.msgmni) {
175 #ifdef MSG_DEBUG_OK
176 		printf("msqid (%d) out of range (0<=msqid<%d)\n", msqid,
177 		    msginfo.msgmni);
178 #endif
179 		return(EINVAL);
180 	}
181 
182 	msqptr = &msqids[msqid];
183 
184 	if (msqptr->msg_qbytes == 0) {
185 #ifdef MSG_DEBUG_OK
186 		printf("no such msqid\n");
187 #endif
188 		return(EINVAL);
189 	}
190 	if (msqptr->msg_perm.seq != IPCID_TO_SEQ(uap->msqid)) {
191 #ifdef MSG_DEBUG_OK
192 		printf("wrong sequence number\n");
193 #endif
194 		return(EINVAL);
195 	}
196 
197 	eval = 0;
198 	rval = 0;
199 
200 	switch (cmd) {
201 
202 	case IPC_RMID:
203 	{
204 		struct msg *msghdr;
205 		if ((eval = ipcperm(cred, &msqptr->msg_perm, IPC_M)))
206 			return(eval);
207 		/* Free the message headers */
208 		msghdr = msqptr->msg_first;
209 		while (msghdr != NULL) {
210 			struct msg *msghdr_tmp;
211 
212 			/* Free the segments of each message */
213 			msqptr->msg_cbytes -= msghdr->msg_ts;
214 			msqptr->msg_qnum--;
215 			msghdr_tmp = msghdr;
216 			msghdr = msghdr->msg_next;
217 			msg_freehdr(msghdr_tmp);
218 		}
219 
220 		if (msqptr->msg_cbytes != 0)
221 			panic("msg_cbytes is screwed up");
222 		if (msqptr->msg_qnum != 0)
223 			panic("msg_qnum is screwed up");
224 
225 		msqptr->msg_qbytes = 0;	/* Mark it as free */
226 
227 		wakeup((caddr_t)msqptr);
228 	}
229 
230 		break;
231 
232 	case IPC_SET:
233 		if ((eval = ipcperm(cred, &msqptr->msg_perm, IPC_M)))
234 			return(eval);
235 		if ((eval = copyin(user_msqptr, &msqbuf, sizeof(msqbuf))) != 0)
236 			return(eval);
237 		if (msqbuf.msg_qbytes > msqptr->msg_qbytes && cred->cr_uid != 0)
238 			return(EPERM);
239 		if (msqbuf.msg_qbytes > msginfo.msgmnb) {
240 #ifdef MSG_DEBUG_OK
241 			printf("can't increase msg_qbytes beyond %d (truncating)\n",
242 			    msginfo.msgmnb);
243 #endif
244 			msqbuf.msg_qbytes = msginfo.msgmnb;	/* silently restrict qbytes to system limit */
245 		}
246 		if (msqbuf.msg_qbytes == 0) {
247 #ifdef MSG_DEBUG_OK
248 			printf("can't reduce msg_qbytes to 0\n");
249 #endif
250 			return(EINVAL);		/* non-standard errno! */
251 		}
252 		msqptr->msg_perm.uid = msqbuf.msg_perm.uid;	/* change the owner */
253 		msqptr->msg_perm.gid = msqbuf.msg_perm.gid;	/* change the owner */
254 		msqptr->msg_perm.mode = (msqptr->msg_perm.mode & ~0777) |
255 		    (msqbuf.msg_perm.mode & 0777);
256 		msqptr->msg_qbytes = msqbuf.msg_qbytes;
257 		msqptr->msg_ctime = time.tv_sec;
258 		break;
259 
260 	case IPC_STAT:
261 		if ((eval = ipcperm(cred, &msqptr->msg_perm, IPC_R))) {
262 #ifdef MSG_DEBUG_OK
263 			printf("requester doesn't have read access\n");
264 #endif
265 			return(eval);
266 		}
267 		eval = copyout((caddr_t)msqptr, user_msqptr,
268 		    sizeof(struct msqid_ds));
269 		break;
270 
271 	default:
272 #ifdef MSG_DEBUG_OK
273 		printf("invalid command %d\n", cmd);
274 #endif
275 		return(EINVAL);
276 	}
277 
278 	if (eval == 0)
279 		*retval = rval;
280 	return(eval);
281 }
282 
283 struct msgget_args {
284 	key_t	key;
285 	int	msgflg;
286 };
287 
288 int
289 msgget(p, uap, retval)
290 	struct proc *p;
291 	register struct msgget_args *uap;
292 	int *retval;
293 {
294 	int msqid, eval;
295 	int key = uap->key;
296 	int msgflg = 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 struct msgsnd_args {
389 	int	msqid;
390 	void	*user_msgp;
391 	size_t	msgsz;
392 	int	msgflg;
393 };
394 
395 int
396 msgsnd(p, uap, retval)
397 	struct proc *p;
398 	register struct msgsnd_args *uap;
399 	int *retval;
400 {
401 	int msqid = uap->msqid;
402 	void *user_msgp = uap->user_msgp;
403 	size_t msgsz = uap->msgsz;
404 	int msgflg = uap->msgflg;
405 	int segs_needed, eval;
406 	struct ucred *cred = p->p_ucred;
407 	register struct msqid_ds *msqptr;
408 	register struct msg *msghdr;
409 	short next;
410 
411 #ifdef MSG_DEBUG_OK
412 	printf("call to msgsnd(%d, 0x%x, %d, %d)\n", msqid, user_msgp, msgsz,
413 	    msgflg);
414 #endif
415 
416 	msqid = IPCID_TO_IX(msqid);
417 
418 	if (msqid < 0 || msqid >= msginfo.msgmni) {
419 #ifdef MSG_DEBUG_OK
420 		printf("msqid (%d) out of range (0<=msqid<%d)\n", msqid,
421 		    msginfo.msgmni);
422 #endif
423 		return(EINVAL);
424 	}
425 
426 	msqptr = &msqids[msqid];
427 	if (msqptr->msg_qbytes == 0) {
428 #ifdef MSG_DEBUG_OK
429 		printf("no such message queue id\n");
430 #endif
431 		return(EINVAL);
432 	}
433 	if (msqptr->msg_perm.seq != IPCID_TO_SEQ(uap->msqid)) {
434 #ifdef MSG_DEBUG_OK
435 		printf("wrong sequence number\n");
436 #endif
437 		return(EINVAL);
438 	}
439 
440 	if ((eval = ipcperm(cred, &msqptr->msg_perm, IPC_W))) {
441 #ifdef MSG_DEBUG_OK
442 		printf("requester doesn't have write access\n");
443 #endif
444 		return(eval);
445 	}
446 
447 	segs_needed = (msgsz + msginfo.msgssz - 1) / msginfo.msgssz;
448 #ifdef MSG_DEBUG_OK
449 	printf("msgsz=%d, msgssz=%d, segs_needed=%d\n", msgsz, msginfo.msgssz,
450 	    segs_needed);
451 #endif
452 	for (;;) {
453 		int need_more_resources = 0;
454 
455 		/*
456 		 * check msgsz
457 		 * (inside this loop in case msg_qbytes changes while we sleep)
458 		 */
459 
460 		if (msgsz > msqptr->msg_qbytes) {
461 #ifdef MSG_DEBUG_OK
462 			printf("msgsz > msqptr->msg_qbytes\n");
463 #endif
464 			return(EINVAL);
465 		}
466 
467 		if (msqptr->msg_perm.mode & MSG_LOCKED) {
468 #ifdef MSG_DEBUG_OK
469 			printf("msqid is locked\n");
470 #endif
471 			need_more_resources = 1;
472 		}
473 		if (msgsz + msqptr->msg_cbytes > msqptr->msg_qbytes) {
474 #ifdef MSG_DEBUG_OK
475 			printf("msgsz + msg_cbytes > msg_qbytes\n");
476 #endif
477 			need_more_resources = 1;
478 		}
479 		if (segs_needed > nfree_msgmaps) {
480 #ifdef MSG_DEBUG_OK
481 			printf("segs_needed > nfree_msgmaps\n");
482 #endif
483 			need_more_resources = 1;
484 		}
485 		if (free_msghdrs == NULL) {
486 #ifdef MSG_DEBUG_OK
487 			printf("no more msghdrs\n");
488 #endif
489 			need_more_resources = 1;
490 		}
491 
492 		if (need_more_resources) {
493 			int we_own_it;
494 
495 			if ((msgflg & IPC_NOWAIT) != 0) {
496 #ifdef MSG_DEBUG_OK
497 				printf("need more resources but caller doesn't want to wait\n");
498 #endif
499 				return(EAGAIN);
500 			}
501 
502 			if ((msqptr->msg_perm.mode & MSG_LOCKED) != 0) {
503 #ifdef MSG_DEBUG_OK
504 				printf("we don't own the msqid_ds\n");
505 #endif
506 				we_own_it = 0;
507 			} else {
508 				/* Force later arrivals to wait for our
509 				   request */
510 #ifdef MSG_DEBUG_OK
511 				printf("we own the msqid_ds\n");
512 #endif
513 				msqptr->msg_perm.mode |= MSG_LOCKED;
514 				we_own_it = 1;
515 			}
516 #ifdef MSG_DEBUG_OK
517 			printf("goodnight\n");
518 #endif
519 			eval = tsleep((caddr_t)msqptr, (PZERO - 4) | PCATCH,
520 			    "msgwait", 0);
521 #ifdef MSG_DEBUG_OK
522 			printf("good morning, eval=%d\n", eval);
523 #endif
524 			if (we_own_it)
525 				msqptr->msg_perm.mode &= ~MSG_LOCKED;
526 			if (eval != 0) {
527 #ifdef MSG_DEBUG_OK
528 				printf("msgsnd:  interrupted system call\n");
529 #endif
530 				return(EINTR);
531 			}
532 
533 			/*
534 			 * Make sure that the msq queue still exists
535 			 */
536 
537 			if (msqptr->msg_qbytes == 0) {
538 #ifdef MSG_DEBUG_OK
539 				printf("msqid deleted\n");
540 #endif
541 				/* The SVID says to return EIDRM. */
542 #ifdef EIDRM
543 				return(EIDRM);
544 #else
545 				/* Unfortunately, BSD doesn't define that code
546 				   yet! */
547 				return(EINVAL);
548 #endif
549 			}
550 
551 		} else {
552 #ifdef MSG_DEBUG_OK
553 			printf("got all the resources that we need\n");
554 #endif
555 			break;
556 		}
557 	}
558 
559 	/*
560 	 * We have the resources that we need.
561 	 * Make sure!
562 	 */
563 
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 
573 	/*
574 	 * Re-lock the msqid_ds in case we page-fault when copying in the
575 	 * message
576 	 */
577 
578 	if ((msqptr->msg_perm.mode & MSG_LOCKED) != 0)
579 		panic("msqid_ds is already locked");
580 	msqptr->msg_perm.mode |= MSG_LOCKED;
581 
582 	/*
583 	 * Allocate a message header
584 	 */
585 
586 	msghdr = free_msghdrs;
587 	free_msghdrs = msghdr->msg_next;
588 	msghdr->msg_spot = -1;
589 	msghdr->msg_ts = msgsz;
590 
591 	/*
592 	 * Allocate space for the message
593 	 */
594 
595 	while (segs_needed > 0) {
596 		if (nfree_msgmaps <= 0)
597 			panic("not enough msgmaps");
598 		if (free_msgmaps == -1)
599 			panic("nil free_msgmaps");
600 		next = free_msgmaps;
601 		if (next <= -1)
602 			panic("next too low #1");
603 		if (next >= msginfo.msgseg)
604 			panic("next out of range #1");
605 #ifdef MSG_DEBUG_OK
606 		printf("allocating segment %d to message\n", next);
607 #endif
608 		free_msgmaps = msgmaps[next].next;
609 		nfree_msgmaps--;
610 		msgmaps[next].next = msghdr->msg_spot;
611 		msghdr->msg_spot = next;
612 		segs_needed--;
613 	}
614 
615 	/*
616 	 * Copy in the message type
617 	 */
618 
619 	if ((eval = copyin(user_msgp, &msghdr->msg_type,
620 	    sizeof(msghdr->msg_type))) != 0) {
621 #ifdef MSG_DEBUG_OK
622 		printf("error %d copying the message type\n", eval);
623 #endif
624 		msg_freehdr(msghdr);
625 		msqptr->msg_perm.mode &= ~MSG_LOCKED;
626 		wakeup((caddr_t)msqptr);
627 		return(eval);
628 	}
629 	user_msgp += sizeof(msghdr->msg_type);
630 
631 	/*
632 	 * Validate the message type
633 	 */
634 
635 	if (msghdr->msg_type < 1) {
636 		msg_freehdr(msghdr);
637 		msqptr->msg_perm.mode &= ~MSG_LOCKED;
638 		wakeup((caddr_t)msqptr);
639 #ifdef MSG_DEBUG_OK
640 		printf("mtype (%d) < 1\n", msghdr->msg_type);
641 #endif
642 		return(EINVAL);
643 	}
644 
645 	/*
646 	 * Copy in the message body
647 	 */
648 
649 	next = msghdr->msg_spot;
650 	while (msgsz > 0) {
651 		size_t tlen;
652 		if (msgsz > msginfo.msgssz)
653 			tlen = msginfo.msgssz;
654 		else
655 			tlen = msgsz;
656 		if (next <= -1)
657 			panic("next too low #2");
658 		if (next >= msginfo.msgseg)
659 			panic("next out of range #2");
660 		if ((eval = copyin(user_msgp, &msgpool[next * msginfo.msgssz],
661 		    tlen)) != 0) {
662 #ifdef MSG_DEBUG_OK
663 			printf("error %d copying in message segment\n", eval);
664 #endif
665 			msg_freehdr(msghdr);
666 			msqptr->msg_perm.mode &= ~MSG_LOCKED;
667 			wakeup((caddr_t)msqptr);
668 			return(eval);
669 		}
670 		msgsz -= tlen;
671 		user_msgp += tlen;
672 		next = msgmaps[next].next;
673 	}
674 	if (next != -1)
675 		panic("didn't use all the msg segments");
676 
677 	/*
678 	 * We've got the message.  Unlock the msqid_ds.
679 	 */
680 
681 	msqptr->msg_perm.mode &= ~MSG_LOCKED;
682 
683 	/*
684 	 * Make sure that the msqid_ds is still allocated.
685 	 */
686 
687 	if (msqptr->msg_qbytes == 0) {
688 		msg_freehdr(msghdr);
689 		wakeup((caddr_t)msqptr);
690 		/* The SVID says to return EIDRM. */
691 #ifdef EIDRM
692 		return(EIDRM);
693 #else
694 		/* Unfortunately, BSD doesn't define that code yet! */
695 		return(EINVAL);
696 #endif
697 	}
698 
699 	/*
700 	 * Put the message into the queue
701 	 */
702 
703 	if (msqptr->msg_first == NULL) {
704 		msqptr->msg_first = msghdr;
705 		msqptr->msg_last = msghdr;
706 	} else {
707 		msqptr->msg_last->msg_next = msghdr;
708 		msqptr->msg_last = msghdr;
709 	}
710 	msqptr->msg_last->msg_next = NULL;
711 
712 	msqptr->msg_cbytes += msghdr->msg_ts;
713 	msqptr->msg_qnum++;
714 	msqptr->msg_lspid = p->p_pid;
715 	msqptr->msg_stime = time.tv_sec;
716 
717 	wakeup((caddr_t)msqptr);
718 	*retval = 0;
719 	return(0);
720 }
721 
722 struct msgrcv_args {
723 	int	msqid;
724 	void	*msgp;
725 	size_t	msgsz;
726 	long	msgtyp;
727 	int	msgflg;
728 };
729 
730 int
731 msgrcv(p, uap, retval)
732 	struct proc *p;
733 	register struct msgrcv_args *uap;
734 	int *retval;
735 {
736 	int msqid = uap->msqid;
737 	void *user_msgp = uap->msgp;
738 	size_t msgsz = uap->msgsz;
739 	long msgtyp = uap->msgtyp;
740 	int msgflg = uap->msgflg;
741 	size_t len;
742 	struct ucred *cred = p->p_ucred;
743 	register struct msqid_ds *msqptr;
744 	register struct msg *msghdr;
745 	int eval;
746 	short next;
747 
748 #ifdef MSG_DEBUG_OK
749 	printf("call to msgrcv(%d, 0x%x, %d, %ld, %d)\n", msqid, user_msgp,
750 	    msgsz, msgtyp, msgflg);
751 #endif
752 
753 	msqid = IPCID_TO_IX(msqid);
754 
755 	if (msqid < 0 || msqid >= msginfo.msgmni) {
756 #ifdef MSG_DEBUG_OK
757 		printf("msqid (%d) out of range (0<=msqid<%d)\n", msqid,
758 		    msginfo.msgmni);
759 #endif
760 		return(EINVAL);
761 	}
762 
763 	msqptr = &msqids[msqid];
764 	if (msqptr->msg_qbytes == 0) {
765 #ifdef MSG_DEBUG_OK
766 		printf("no such message queue id\n");
767 #endif
768 		return(EINVAL);
769 	}
770 	if (msqptr->msg_perm.seq != IPCID_TO_SEQ(uap->msqid)) {
771 #ifdef MSG_DEBUG_OK
772 		printf("wrong sequence number\n");
773 #endif
774 		return(EINVAL);
775 	}
776 
777 	if ((eval = ipcperm(cred, &msqptr->msg_perm, IPC_R))) {
778 #ifdef MSG_DEBUG_OK
779 		printf("requester doesn't have read access\n");
780 #endif
781 		return(eval);
782 	}
783 
784 	msghdr = NULL;
785 	while (msghdr == NULL) {
786 		if (msgtyp == 0) {
787 			msghdr = msqptr->msg_first;
788 			if (msghdr != NULL) {
789 				if (msgsz < msghdr->msg_ts &&
790 				    (msgflg & MSG_NOERROR) == 0) {
791 #ifdef MSG_DEBUG_OK
792 					printf("first message on the queue is too big (want %d, got %d)\n",
793 					    msgsz, msghdr->msg_ts);
794 #endif
795 					return(E2BIG);
796 				}
797 				if (msqptr->msg_first == msqptr->msg_last) {
798 					msqptr->msg_first = NULL;
799 					msqptr->msg_last = NULL;
800 				} else {
801 					msqptr->msg_first = msghdr->msg_next;
802 					if (msqptr->msg_first == NULL)
803 						panic("msg_first/last screwed up #1");
804 				}
805 			}
806 		} else {
807 			struct msg *previous;
808 			struct msg **prev;
809 
810 			previous = NULL;
811 			prev = &(msqptr->msg_first);
812 			while ((msghdr = *prev) != NULL) {
813 				/*
814 				 * Is this message's type an exact match or is
815 				 * this message's type less than or equal to
816 				 * the absolute value of a negative msgtyp?
817 				 * Note that the second half of this test can
818 				 * NEVER be true if msgtyp is positive since
819 				 * msg_type is always positive!
820 				 */
821 
822 				if (msgtyp == msghdr->msg_type ||
823 				    msghdr->msg_type <= -msgtyp) {
824 #ifdef MSG_DEBUG_OK
825 					printf("found message type %d, requested %d\n",
826 					    msghdr->msg_type, msgtyp);
827 #endif
828 					if (msgsz < msghdr->msg_ts &&
829 					    (msgflg & MSG_NOERROR) == 0) {
830 #ifdef MSG_DEBUG_OK
831 						printf("requested message on the queue is too big (want %d, got %d)\n",
832 						    msgsz, msghdr->msg_ts);
833 #endif
834 						return(E2BIG);
835 					}
836 					*prev = msghdr->msg_next;
837 					if (msghdr == msqptr->msg_last) {
838 						if (previous == NULL) {
839 							if (prev !=
840 							    &msqptr->msg_first)
841 								panic("msg_first/last screwed up #2");
842 							msqptr->msg_first =
843 							    NULL;
844 							msqptr->msg_last =
845 							    NULL;
846 						} else {
847 							if (prev ==
848 							    &msqptr->msg_first)
849 								panic("msg_first/last screwed up #3");
850 							msqptr->msg_last =
851 							    previous;
852 						}
853 					}
854 					break;
855 				}
856 				previous = msghdr;
857 				prev = &(msghdr->msg_next);
858 			}
859 		}
860 
861 		/*
862 		 * We've either extracted the msghdr for the appropriate
863 		 * message or there isn't one.
864 		 * If there is one then bail out of this loop.
865 		 */
866 
867 		if (msghdr != NULL)
868 			break;
869 
870 		/*
871 		 * Hmph!  No message found.  Does the user want to wait?
872 		 */
873 
874 		if ((msgflg & IPC_NOWAIT) != 0) {
875 #ifdef MSG_DEBUG_OK
876 			printf("no appropriate message found (msgtyp=%d)\n",
877 			    msgtyp);
878 #endif
879 			/* The SVID says to return ENOMSG. */
880 #ifdef ENOMSG
881 			return(ENOMSG);
882 #else
883 			/* Unfortunately, BSD doesn't define that code yet! */
884 			return(EAGAIN);
885 #endif
886 		}
887 
888 		/*
889 		 * Wait for something to happen
890 		 */
891 
892 #ifdef MSG_DEBUG_OK
893 		printf("msgrcv:  goodnight\n");
894 #endif
895 		eval = tsleep((caddr_t)msqptr, (PZERO - 4) | PCATCH, "msgwait",
896 		    0);
897 #ifdef MSG_DEBUG_OK
898 		printf("msgrcv:  good morning (eval=%d)\n", eval);
899 #endif
900 
901 		if (eval != 0) {
902 #ifdef MSG_DEBUG_OK
903 			printf("msgsnd:  interrupted system call\n");
904 #endif
905 			return(EINTR);
906 		}
907 
908 		/*
909 		 * Make sure that the msq queue still exists
910 		 */
911 
912 		if (msqptr->msg_qbytes == 0 ||
913 		    msqptr->msg_perm.seq != IPCID_TO_SEQ(uap->msqid)) {
914 #ifdef MSG_DEBUG_OK
915 			printf("msqid deleted\n");
916 #endif
917 			/* The SVID says to return EIDRM. */
918 #ifdef EIDRM
919 			return(EIDRM);
920 #else
921 			/* Unfortunately, BSD doesn't define that code yet! */
922 			return(EINVAL);
923 #endif
924 		}
925 	}
926 
927 	/*
928 	 * Return the message to the user.
929 	 *
930 	 * First, do the bookkeeping (before we risk being interrupted).
931 	 */
932 
933 	msqptr->msg_cbytes -= msghdr->msg_ts;
934 	msqptr->msg_qnum--;
935 	msqptr->msg_lrpid = p->p_pid;
936 	msqptr->msg_rtime = time.tv_sec;
937 
938 	/*
939 	 * Make msgsz the actual amount that we'll be returning.
940 	 * Note that this effectively truncates the message if it is too long
941 	 * (since msgsz is never increased).
942 	 */
943 
944 #ifdef MSG_DEBUG_OK
945 	printf("found a message, msgsz=%d, msg_ts=%d\n", msgsz,
946 	    msghdr->msg_ts);
947 #endif
948 	if (msgsz > msghdr->msg_ts)
949 		msgsz = msghdr->msg_ts;
950 
951 	/*
952 	 * Return the type to the user.
953 	 */
954 
955 	eval = copyout((caddr_t)&(msghdr->msg_type), user_msgp,
956 	    sizeof(msghdr->msg_type));
957 	if (eval != 0) {
958 #ifdef MSG_DEBUG_OK
959 		printf("error (%d) copying out message type\n", eval);
960 #endif
961 		msg_freehdr(msghdr);
962 		wakeup((caddr_t)msqptr);
963 		return(eval);
964 	}
965 	user_msgp += sizeof(msghdr->msg_type);
966 
967 	/*
968 	 * Return the segments to the user
969 	 */
970 
971 	next = msghdr->msg_spot;
972 	for (len = 0; len < msgsz; len += msginfo.msgssz) {
973 		size_t tlen;
974 
975 		if (msgsz > msginfo.msgssz)
976 			tlen = msginfo.msgssz;
977 		else
978 			tlen = msgsz;
979 		if (next <= -1)
980 			panic("next too low #3");
981 		if (next >= msginfo.msgseg)
982 			panic("next out of range #3");
983 		eval = copyout((caddr_t)&msgpool[next * msginfo.msgssz],
984 		    user_msgp, tlen);
985 		if (eval != 0) {
986 #ifdef MSG_DEBUG_OK
987 			printf("error (%d) copying out message segment\n",
988 			    eval);
989 #endif
990 			msg_freehdr(msghdr);
991 			wakeup((caddr_t)msqptr);
992 			return(eval);
993 		}
994 		user_msgp += tlen;
995 		next = msgmaps[next].next;
996 	}
997 
998 	/*
999 	 * Done, return the actual number of bytes copied out.
1000 	 */
1001 
1002 	msg_freehdr(msghdr);
1003 	wakeup((caddr_t)msqptr);
1004 	*retval = msgsz;
1005 	return(0);
1006 }
1007