xref: /netbsd/sys/kern/sysv_msg.c (revision 9cb4ef14)
1 /*	$NetBSD: sysv_msg.c,v 1.76 2019/10/04 23:20:22 kamil Exp $	*/
2 
3 /*-
4  * Copyright (c) 1999, 2006, 2007 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
9  * NASA Ames Research Center, and by Andrew Doran.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30  * POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 /*
34  * Implementation of SVID messages
35  *
36  * Author: Daniel Boulet
37  *
38  * Copyright 1993 Daniel Boulet and RTMX Inc.
39  *
40  * This system call was implemented by Daniel Boulet under contract from RTMX.
41  *
42  * Redistribution and use in source forms, with and without modification,
43  * are permitted provided that this entire comment appears intact.
44  *
45  * Redistribution in binary form may occur without any restrictions.
46  * Obviously, it would be nice if you gave credit where credit is due
47  * but requiring it would be too onerous.
48  *
49  * This software is provided ``AS IS'' without any warranties of any kind.
50  */
51 
52 #include <sys/cdefs.h>
53 __KERNEL_RCSID(0, "$NetBSD: sysv_msg.c,v 1.76 2019/10/04 23:20:22 kamil Exp $");
54 
55 #ifdef _KERNEL_OPT
56 #include "opt_sysv.h"
57 #endif
58 
59 #include <sys/param.h>
60 #include <sys/kernel.h>
61 #include <sys/msg.h>
62 #include <sys/sysctl.h>
63 #include <sys/mount.h>		/* XXX for <sys/syscallargs.h> */
64 #include <sys/syscallargs.h>
65 #include <sys/kauth.h>
66 
67 #define MSG_DEBUG
68 #undef MSG_DEBUG_OK
69 
70 #ifdef MSG_DEBUG_OK
71 #define MSG_PRINTF(a)	printf a
72 #else
73 #define MSG_PRINTF(a)
74 #endif
75 
76 static int	nfree_msgmaps;		/* # of free map entries */
77 static short	free_msgmaps;	/* head of linked list of free map entries */
78 static struct	__msg *free_msghdrs;	/* list of free msg headers */
79 static char	*msgpool;		/* MSGMAX byte long msg buffer pool */
80 static struct	msgmap *msgmaps;	/* MSGSEG msgmap structures */
81 static struct __msg *msghdrs;		/* MSGTQL msg headers */
82 
83 kmsq_t	*msqs;				/* MSGMNI msqid_ds struct's */
84 kmutex_t msgmutex;			/* subsystem lock */
85 
86 static u_int	msg_waiters = 0;	/* total number of msgrcv waiters */
87 static bool	msg_realloc_state;
88 static kcondvar_t msg_realloc_cv;
89 
90 static void msg_freehdr(struct __msg *);
91 
92 extern int kern_has_sysvmsg;
93 
94 SYSCTL_SETUP_PROTO(sysctl_ipc_msg_setup);
95 
96 int
msginit(void)97 msginit(void)
98 {
99 	int i, sz;
100 	vaddr_t v;
101 
102 	/*
103 	 * msginfo.msgssz should be a power of two for efficiency reasons.
104 	 * It is also pretty silly if msginfo.msgssz is less than 8
105 	 * or greater than about 256 so ...
106 	 */
107 
108 	i = 8;
109 	while (i < 1024 && i != msginfo.msgssz)
110 		i <<= 1;
111 	if (i != msginfo.msgssz) {
112 		printf("msginfo.msgssz = %d, not a small power of 2",
113 		    msginfo.msgssz);
114 		return EINVAL;
115 	}
116 
117 	if (msginfo.msgseg > 32767) {
118 		printf("msginfo.msgseg = %d > 32767", msginfo.msgseg);
119 		return EINVAL;
120 	}
121 
122 	/* Allocate the wired memory for our structures */
123 	sz = ALIGN(msginfo.msgmax) +
124 	    ALIGN(msginfo.msgseg * sizeof(struct msgmap)) +
125 	    ALIGN(msginfo.msgtql * sizeof(struct __msg)) +
126 	    ALIGN(msginfo.msgmni * sizeof(kmsq_t));
127 	sz = round_page(sz);
128 	v = uvm_km_alloc(kernel_map, sz, 0, UVM_KMF_WIRED|UVM_KMF_ZERO);
129 	if (v == 0) {
130 		printf("sysv_msg: cannot allocate memory");
131 		return ENOMEM;
132 	}
133 	msgpool = (void *)v;
134 	msgmaps = (void *)((uintptr_t)msgpool + ALIGN(msginfo.msgmax));
135 	msghdrs = (void *)((uintptr_t)msgmaps +
136 	    ALIGN(msginfo.msgseg * sizeof(struct msgmap)));
137 	msqs = (void *)((uintptr_t)msghdrs +
138 	    ALIGN(msginfo.msgtql * sizeof(struct __msg)));
139 
140 	for (i = 0; i < (msginfo.msgseg - 1); i++)
141 		msgmaps[i].next = i + 1;
142 	msgmaps[msginfo.msgseg - 1].next = -1;
143 
144 	free_msgmaps = 0;
145 	nfree_msgmaps = msginfo.msgseg;
146 
147 	for (i = 0; i < (msginfo.msgtql - 1); i++) {
148 		msghdrs[i].msg_type = 0;
149 		msghdrs[i].msg_next = &msghdrs[i + 1];
150 	}
151 	i = msginfo.msgtql - 1;
152 	msghdrs[i].msg_type = 0;
153 	msghdrs[i].msg_next = NULL;
154 	free_msghdrs = &msghdrs[0];
155 
156 	for (i = 0; i < msginfo.msgmni; i++) {
157 		cv_init(&msqs[i].msq_cv, "msgwait");
158 		/* Implies entry is available */
159 		msqs[i].msq_u.msg_qbytes = 0;
160 		/* Reset to a known value */
161 		msqs[i].msq_u.msg_perm._seq = 0;
162 	}
163 
164 	mutex_init(&msgmutex, MUTEX_DEFAULT, IPL_NONE);
165 	cv_init(&msg_realloc_cv, "msgrealc");
166 	msg_realloc_state = false;
167 
168 	kern_has_sysvmsg = 1;
169 
170 	return 0;
171 }
172 
173 int
msgfini(void)174 msgfini(void)
175 {
176 	int i, sz;
177 	vaddr_t v = (vaddr_t)msgpool;
178 
179 	mutex_enter(&msgmutex);
180 	for (i = 0; i < msginfo.msgmni; i++) {
181 		if (msqs[i].msq_u.msg_qbytes != 0) {
182 			mutex_exit(&msgmutex);
183 			return 1; /* queue not available, prevent unload! */
184 		}
185 	}
186 /*
187  * Destroy all condvars and free the memory we're using
188  */
189 	for (i = 0; i < msginfo.msgmni; i++) {
190 		cv_destroy(&msqs[i].msq_cv);
191 	}
192 	sz = ALIGN(msginfo.msgmax) +
193 	    ALIGN(msginfo.msgseg * sizeof(struct msgmap)) +
194 	    ALIGN(msginfo.msgtql * sizeof(struct __msg)) +
195 	    ALIGN(msginfo.msgmni * sizeof(kmsq_t));
196 	sz = round_page(sz);
197 	uvm_km_free(kernel_map, v, sz, UVM_KMF_WIRED);
198 
199 	cv_destroy(&msg_realloc_cv);
200 	mutex_exit(&msgmutex);
201 	mutex_destroy(&msgmutex);
202 
203 	kern_has_sysvmsg = 0;
204 
205 	return 0;
206 }
207 
208 static int
msgrealloc(int newmsgmni,int newmsgseg)209 msgrealloc(int newmsgmni, int newmsgseg)
210 {
211 	struct msgmap *new_msgmaps;
212 	struct __msg *new_msghdrs, *new_free_msghdrs;
213 	char *old_msgpool, *new_msgpool;
214 	kmsq_t *new_msqs;
215 	vaddr_t v;
216 	int i, sz, msqid, newmsgmax, new_nfree_msgmaps;
217 	short new_free_msgmaps;
218 
219 	if (newmsgmni < 1 || newmsgseg < 1)
220 		return EINVAL;
221 
222 	/* Allocate the wired memory for our structures */
223 	newmsgmax = msginfo.msgssz * newmsgseg;
224 	sz = ALIGN(newmsgmax) +
225 	    ALIGN(newmsgseg * sizeof(struct msgmap)) +
226 	    ALIGN(msginfo.msgtql * sizeof(struct __msg)) +
227 	    ALIGN(newmsgmni * sizeof(kmsq_t));
228 	sz = round_page(sz);
229 	v = uvm_km_alloc(kernel_map, sz, 0, UVM_KMF_WIRED|UVM_KMF_ZERO);
230 	if (v == 0)
231 		return ENOMEM;
232 
233 	mutex_enter(&msgmutex);
234 	if (msg_realloc_state) {
235 		mutex_exit(&msgmutex);
236 		uvm_km_free(kernel_map, v, sz, UVM_KMF_WIRED);
237 		return EBUSY;
238 	}
239 	msg_realloc_state = true;
240 	if (msg_waiters) {
241 		/*
242 		 * Mark reallocation state, wake-up all waiters,
243 		 * and wait while they will all exit.
244 		 */
245 		for (i = 0; i < msginfo.msgmni; i++)
246 			cv_broadcast(&msqs[i].msq_cv);
247 		while (msg_waiters)
248 			cv_wait(&msg_realloc_cv, &msgmutex);
249 	}
250 	old_msgpool = msgpool;
251 
252 	/* We cannot reallocate less memory than we use */
253 	i = 0;
254 	for (msqid = 0; msqid < msginfo.msgmni; msqid++) {
255 		struct msqid_ds *mptr;
256 		kmsq_t *msq;
257 
258 		msq = &msqs[msqid];
259 		mptr = &msq->msq_u;
260 		if (mptr->msg_qbytes || (mptr->msg_perm.mode & MSG_LOCKED))
261 			i = msqid;
262 	}
263 	if (i >= newmsgmni || (msginfo.msgseg - nfree_msgmaps) > newmsgseg) {
264 		mutex_exit(&msgmutex);
265 		uvm_km_free(kernel_map, v, sz, UVM_KMF_WIRED);
266 		return EBUSY;
267 	}
268 
269 	new_msgpool = (void *)v;
270 	new_msgmaps = (void *)((uintptr_t)new_msgpool + ALIGN(newmsgmax));
271 	new_msghdrs = (void *)((uintptr_t)new_msgmaps +
272 	    ALIGN(newmsgseg * sizeof(struct msgmap)));
273 	new_msqs = (void *)((uintptr_t)new_msghdrs +
274 	    ALIGN(msginfo.msgtql * sizeof(struct __msg)));
275 
276 	/* Initialize the structures */
277 	for (i = 0; i < (newmsgseg - 1); i++)
278 		new_msgmaps[i].next = i + 1;
279 	new_msgmaps[newmsgseg - 1].next = -1;
280 	new_free_msgmaps = 0;
281 	new_nfree_msgmaps = newmsgseg;
282 
283 	for (i = 0; i < (msginfo.msgtql - 1); i++) {
284 		new_msghdrs[i].msg_type = 0;
285 		new_msghdrs[i].msg_next = &new_msghdrs[i + 1];
286 	}
287 	i = msginfo.msgtql - 1;
288 	new_msghdrs[i].msg_type = 0;
289 	new_msghdrs[i].msg_next = NULL;
290 	new_free_msghdrs = &new_msghdrs[0];
291 
292 	for (i = 0; i < newmsgmni; i++) {
293 		new_msqs[i].msq_u.msg_qbytes = 0;
294 		new_msqs[i].msq_u.msg_perm._seq = 0;
295 		cv_init(&new_msqs[i].msq_cv, "msgwait");
296 	}
297 
298 	/*
299 	 * Copy all message queue identifiers, message headers and buffer
300 	 * pools to the new memory location.
301 	 */
302 	for (msqid = 0; msqid < msginfo.msgmni; msqid++) {
303 		struct __msg *nmsghdr, *msghdr, *pmsghdr;
304 		struct msqid_ds *nmptr, *mptr;
305 		kmsq_t *nmsq, *msq;
306 
307 		msq = &msqs[msqid];
308 		mptr = &msq->msq_u;
309 
310 		if (mptr->msg_qbytes == 0 &&
311 		    (mptr->msg_perm.mode & MSG_LOCKED) == 0)
312 			continue;
313 
314 		nmsq = &new_msqs[msqid];
315 		nmptr = &nmsq->msq_u;
316 		memcpy(nmptr, mptr, sizeof(struct msqid_ds));
317 
318 		/*
319 		 * Go through the message headers, and copy each one
320 		 * by taking the new ones, and thus defragmenting.
321 		 */
322 		nmsghdr = pmsghdr = NULL;
323 		msghdr = mptr->_msg_first;
324 		while (msghdr) {
325 			short nnext = 0, next;
326 			u_short msgsz, segcnt;
327 
328 			/* Take an entry from the new list of free msghdrs */
329 			nmsghdr = new_free_msghdrs;
330 			KASSERT(nmsghdr != NULL);
331 			new_free_msghdrs = nmsghdr->msg_next;
332 
333 			nmsghdr->msg_next = NULL;
334 			if (pmsghdr) {
335 				pmsghdr->msg_next = nmsghdr;
336 			} else {
337 				nmptr->_msg_first = nmsghdr;
338 				pmsghdr = nmsghdr;
339 			}
340 			nmsghdr->msg_ts = msghdr->msg_ts;
341 			nmsghdr->msg_spot = -1;
342 
343 			/* Compute the amount of segments and reserve them */
344 			msgsz = msghdr->msg_ts;
345 			segcnt = (msgsz + msginfo.msgssz - 1) / msginfo.msgssz;
346 			if (segcnt == 0)
347 				continue;
348 			while (segcnt--) {
349 				nnext = new_free_msgmaps;
350 				new_free_msgmaps = new_msgmaps[nnext].next;
351 				new_nfree_msgmaps--;
352 				new_msgmaps[nnext].next = nmsghdr->msg_spot;
353 				nmsghdr->msg_spot = nnext;
354 			}
355 
356 			/* Copy all segments */
357 			KASSERT(nnext == nmsghdr->msg_spot);
358 			next = msghdr->msg_spot;
359 			while (msgsz > 0) {
360 				size_t tlen;
361 
362 				if (msgsz >= msginfo.msgssz) {
363 					tlen = msginfo.msgssz;
364 					msgsz -= msginfo.msgssz;
365 				} else {
366 					tlen = msgsz;
367 					msgsz = 0;
368 				}
369 
370 				/* Copy the message buffer */
371 				memcpy(&new_msgpool[nnext * msginfo.msgssz],
372 				    &msgpool[next * msginfo.msgssz], tlen);
373 
374 				/* Next entry of the map */
375 				nnext = msgmaps[nnext].next;
376 				next = msgmaps[next].next;
377 			}
378 
379 			/* Next message header */
380 			msghdr = msghdr->msg_next;
381 		}
382 		nmptr->_msg_last = nmsghdr;
383 	}
384 	KASSERT((msginfo.msgseg - nfree_msgmaps) ==
385 	    (newmsgseg - new_nfree_msgmaps));
386 
387 	sz = ALIGN(msginfo.msgmax) +
388 	    ALIGN(msginfo.msgseg * sizeof(struct msgmap)) +
389 	    ALIGN(msginfo.msgtql * sizeof(struct __msg)) +
390 	    ALIGN(msginfo.msgmni * sizeof(kmsq_t));
391 	sz = round_page(sz);
392 
393 	for (i = 0; i < msginfo.msgmni; i++)
394 		cv_destroy(&msqs[i].msq_cv);
395 
396 	/* Set the pointers and update the new values */
397 	msgpool = new_msgpool;
398 	msgmaps = new_msgmaps;
399 	msghdrs = new_msghdrs;
400 	msqs = new_msqs;
401 
402 	free_msghdrs = new_free_msghdrs;
403 	free_msgmaps = new_free_msgmaps;
404 	nfree_msgmaps = new_nfree_msgmaps;
405 	msginfo.msgmni = newmsgmni;
406 	msginfo.msgseg = newmsgseg;
407 	msginfo.msgmax = newmsgmax;
408 
409 	/* Reallocation completed - notify all waiters, if any */
410 	msg_realloc_state = false;
411 	cv_broadcast(&msg_realloc_cv);
412 	mutex_exit(&msgmutex);
413 
414 	uvm_km_free(kernel_map, (vaddr_t)old_msgpool, sz, UVM_KMF_WIRED);
415 	return 0;
416 }
417 
418 static void
msg_freehdr(struct __msg * msghdr)419 msg_freehdr(struct __msg *msghdr)
420 {
421 
422 	KASSERT(mutex_owned(&msgmutex));
423 
424 	while (msghdr->msg_ts > 0) {
425 		short next;
426 		KASSERT(msghdr->msg_spot >= 0);
427 		KASSERT(msghdr->msg_spot < msginfo.msgseg);
428 
429 		next = msgmaps[msghdr->msg_spot].next;
430 		msgmaps[msghdr->msg_spot].next = free_msgmaps;
431 		free_msgmaps = msghdr->msg_spot;
432 		nfree_msgmaps++;
433 		msghdr->msg_spot = next;
434 		if (msghdr->msg_ts >= msginfo.msgssz)
435 			msghdr->msg_ts -= msginfo.msgssz;
436 		else
437 			msghdr->msg_ts = 0;
438 	}
439 	KASSERT(msghdr->msg_spot == -1);
440 	msghdr->msg_next = free_msghdrs;
441 	free_msghdrs = msghdr;
442 }
443 
444 int
sys___msgctl50(struct lwp * l,const struct sys___msgctl50_args * uap,register_t * retval)445 sys___msgctl50(struct lwp *l, const struct sys___msgctl50_args *uap,
446     register_t *retval)
447 {
448 	/* {
449 		syscallarg(int) msqid;
450 		syscallarg(int) cmd;
451 		syscallarg(struct msqid_ds *) buf;
452 	} */
453 	struct msqid_ds msqbuf;
454 	int cmd, error;
455 
456 	cmd = SCARG(uap, cmd);
457 
458 	if (cmd == IPC_SET) {
459 		error = copyin(SCARG(uap, buf), &msqbuf, sizeof(msqbuf));
460 		if (error)
461 			return (error);
462 	}
463 
464 	error = msgctl1(l, SCARG(uap, msqid), cmd,
465 	    (cmd == IPC_SET || cmd == IPC_STAT) ? &msqbuf : NULL);
466 
467 	if (error == 0 && cmd == IPC_STAT)
468 		error = copyout(&msqbuf, SCARG(uap, buf), sizeof(msqbuf));
469 
470 	return (error);
471 }
472 
473 int
msgctl1(struct lwp * l,int msqid,int cmd,struct msqid_ds * msqbuf)474 msgctl1(struct lwp *l, int msqid, int cmd, struct msqid_ds *msqbuf)
475 {
476 	kauth_cred_t cred = l->l_cred;
477 	struct msqid_ds *msqptr;
478 	kmsq_t *msq;
479 	int error = 0, ix;
480 
481 	MSG_PRINTF(("call to msgctl1(%d, %d)\n", msqid, cmd));
482 
483 	ix = IPCID_TO_IX(msqid);
484 
485 	mutex_enter(&msgmutex);
486 
487 	if (ix < 0 || ix >= msginfo.msgmni) {
488 		MSG_PRINTF(("msqid (%d) out of range (0<=msqid<%d)\n", ix,
489 		    msginfo.msgmni));
490 		error = EINVAL;
491 		goto unlock;
492 	}
493 
494 	msq = &msqs[ix];
495 	msqptr = &msq->msq_u;
496 
497 	if (msqptr->msg_qbytes == 0) {
498 		MSG_PRINTF(("no such msqid\n"));
499 		error = EINVAL;
500 		goto unlock;
501 	}
502 	if (msqptr->msg_perm._seq != IPCID_TO_SEQ(msqid)) {
503 		MSG_PRINTF(("wrong sequence number\n"));
504 		error = EINVAL;
505 		goto unlock;
506 	}
507 
508 	switch (cmd) {
509 	case IPC_RMID:
510 	{
511 		struct __msg *msghdr;
512 		if ((error = ipcperm(cred, &msqptr->msg_perm, IPC_M)) != 0)
513 			break;
514 		/* Free the message headers */
515 		msghdr = msqptr->_msg_first;
516 		while (msghdr != NULL) {
517 			struct __msg *msghdr_tmp;
518 
519 			/* Free the segments of each message */
520 			msqptr->_msg_cbytes -= msghdr->msg_ts;
521 			msqptr->msg_qnum--;
522 			msghdr_tmp = msghdr;
523 			msghdr = msghdr->msg_next;
524 			msg_freehdr(msghdr_tmp);
525 		}
526 		KASSERT(msqptr->_msg_cbytes == 0);
527 		KASSERT(msqptr->msg_qnum == 0);
528 
529 		/* Mark it as free */
530 		msqptr->msg_qbytes = 0;
531 		cv_broadcast(&msq->msq_cv);
532 	}
533 		break;
534 
535 	case IPC_SET:
536 		if ((error = ipcperm(cred, &msqptr->msg_perm, IPC_M)))
537 			break;
538 		if (msqbuf->msg_qbytes > msqptr->msg_qbytes &&
539 		    kauth_authorize_system(cred, KAUTH_SYSTEM_SYSVIPC,
540 		    KAUTH_REQ_SYSTEM_SYSVIPC_MSGQ_OVERSIZE,
541 		    KAUTH_ARG(msqbuf->msg_qbytes),
542 		    KAUTH_ARG(msqptr->msg_qbytes), NULL) != 0) {
543 			error = EPERM;
544 			break;
545 		}
546 		if (msqbuf->msg_qbytes > msginfo.msgmnb) {
547 			MSG_PRINTF(("can't increase msg_qbytes beyond %d "
548 			    "(truncating)\n", msginfo.msgmnb));
549 			/* silently restrict qbytes to system limit */
550 			msqbuf->msg_qbytes = msginfo.msgmnb;
551 		}
552 		if (msqbuf->msg_qbytes == 0) {
553 			MSG_PRINTF(("can't reduce msg_qbytes to 0\n"));
554 			error = EINVAL;		/* XXX non-standard errno! */
555 			break;
556 		}
557 		msqptr->msg_perm.uid = msqbuf->msg_perm.uid;
558 		msqptr->msg_perm.gid = msqbuf->msg_perm.gid;
559 		msqptr->msg_perm.mode = (msqptr->msg_perm.mode & ~0777) |
560 		    (msqbuf->msg_perm.mode & 0777);
561 		msqptr->msg_qbytes = msqbuf->msg_qbytes;
562 		msqptr->msg_ctime = time_second;
563 		break;
564 
565 	case IPC_STAT:
566 		if ((error = ipcperm(cred, &msqptr->msg_perm, IPC_R))) {
567 			MSG_PRINTF(("requester doesn't have read access\n"));
568 			break;
569 		}
570 		memset(msqbuf, 0, sizeof *msqbuf);
571 		msqbuf->msg_perm = msqptr->msg_perm;
572 		msqbuf->msg_perm.mode &= 0777;
573 		msqbuf->msg_qnum = msqptr->msg_qnum;
574 		msqbuf->msg_qbytes = msqptr->msg_qbytes;
575 		msqbuf->msg_lspid = msqptr->msg_lspid;
576 		msqbuf->msg_lrpid = msqptr->msg_lrpid;
577 		msqbuf->msg_stime = msqptr->msg_stime;
578 		msqbuf->msg_rtime = msqptr->msg_rtime;
579 		msqbuf->msg_ctime = msqptr->msg_ctime;
580 		break;
581 
582 	default:
583 		MSG_PRINTF(("invalid command %d\n", cmd));
584 		error = EINVAL;
585 		break;
586 	}
587 
588 unlock:
589 	mutex_exit(&msgmutex);
590 	return (error);
591 }
592 
593 int
sys_msgget(struct lwp * l,const struct sys_msgget_args * uap,register_t * retval)594 sys_msgget(struct lwp *l, const struct sys_msgget_args *uap, register_t *retval)
595 {
596 	/* {
597 		syscallarg(key_t) key;
598 		syscallarg(int) msgflg;
599 	} */
600 	int msqid, error = 0;
601 	int key = SCARG(uap, key);
602 	int msgflg = SCARG(uap, msgflg);
603 	kauth_cred_t cred = l->l_cred;
604 	struct msqid_ds *msqptr = NULL;
605 	kmsq_t *msq;
606 
607 	mutex_enter(&msgmutex);
608 
609 	MSG_PRINTF(("msgget(0x%x, 0%o)\n", key, msgflg));
610 
611 	if (key != IPC_PRIVATE) {
612 		for (msqid = 0; msqid < msginfo.msgmni; msqid++) {
613 			msq = &msqs[msqid];
614 			msqptr = &msq->msq_u;
615 			if (msqptr->msg_qbytes != 0 &&
616 			    msqptr->msg_perm._key == key)
617 				break;
618 		}
619 		if (msqid < msginfo.msgmni) {
620 			MSG_PRINTF(("found public key\n"));
621 			if ((msgflg & IPC_CREAT) && (msgflg & IPC_EXCL)) {
622 				MSG_PRINTF(("not exclusive\n"));
623 				error = EEXIST;
624 				goto unlock;
625 			}
626 			if ((error = ipcperm(cred, &msqptr->msg_perm,
627 			    msgflg & 0700 ))) {
628 				MSG_PRINTF(("requester doesn't have 0%o access\n",
629 				    msgflg & 0700));
630 				goto unlock;
631 			}
632 			goto found;
633 		}
634 	}
635 
636 	MSG_PRINTF(("need to allocate the msqid_ds\n"));
637 	if (key == IPC_PRIVATE || (msgflg & IPC_CREAT)) {
638 		for (msqid = 0; msqid < msginfo.msgmni; msqid++) {
639 			/*
640 			 * Look for an unallocated and unlocked msqid_ds.
641 			 * msqid_ds's can be locked by msgsnd or msgrcv while
642 			 * they are copying the message in/out.  We can't
643 			 * re-use the entry until they release it.
644 			 */
645 			msq = &msqs[msqid];
646 			msqptr = &msq->msq_u;
647 			if (msqptr->msg_qbytes == 0 &&
648 			    (msqptr->msg_perm.mode & MSG_LOCKED) == 0)
649 				break;
650 		}
651 		if (msqid == msginfo.msgmni) {
652 			MSG_PRINTF(("no more msqid_ds's available\n"));
653 			error = ENOSPC;
654 			goto unlock;
655 		}
656 		MSG_PRINTF(("msqid %d is available\n", msqid));
657 		msqptr->msg_perm._key = key;
658 		msqptr->msg_perm.cuid = kauth_cred_geteuid(cred);
659 		msqptr->msg_perm.uid = kauth_cred_geteuid(cred);
660 		msqptr->msg_perm.cgid = kauth_cred_getegid(cred);
661 		msqptr->msg_perm.gid = kauth_cred_getegid(cred);
662 		msqptr->msg_perm.mode = (msgflg & 0777);
663 		/* Make sure that the returned msqid is unique */
664 		msqptr->msg_perm._seq++;
665 		msqptr->_msg_first = NULL;
666 		msqptr->_msg_last = NULL;
667 		msqptr->_msg_cbytes = 0;
668 		msqptr->msg_qnum = 0;
669 		msqptr->msg_qbytes = msginfo.msgmnb;
670 		msqptr->msg_lspid = 0;
671 		msqptr->msg_lrpid = 0;
672 		msqptr->msg_stime = 0;
673 		msqptr->msg_rtime = 0;
674 		msqptr->msg_ctime = time_second;
675 	} else {
676 		MSG_PRINTF(("didn't find it and wasn't asked to create it\n"));
677 		error = ENOENT;
678 		goto unlock;
679 	}
680 
681 found:
682 	/* Construct the unique msqid */
683 	*retval = IXSEQ_TO_IPCID(msqid, msqptr->msg_perm);
684 
685 unlock:
686 	mutex_exit(&msgmutex);
687 	return (error);
688 }
689 
690 int
sys_msgsnd(struct lwp * l,const struct sys_msgsnd_args * uap,register_t * retval)691 sys_msgsnd(struct lwp *l, const struct sys_msgsnd_args *uap, register_t *retval)
692 {
693 	/* {
694 		syscallarg(int) msqid;
695 		syscallarg(const void *) msgp;
696 		syscallarg(size_t) msgsz;
697 		syscallarg(int) msgflg;
698 	} */
699 
700 	return msgsnd1(l, SCARG(uap, msqid), SCARG(uap, msgp),
701 	    SCARG(uap, msgsz), SCARG(uap, msgflg), sizeof(long), copyin);
702 }
703 
704 int
msgsnd1(struct lwp * l,int msqidr,const char * user_msgp,size_t msgsz,int msgflg,size_t typesz,copyin_t fetch_type)705 msgsnd1(struct lwp *l, int msqidr, const char *user_msgp, size_t msgsz,
706     int msgflg, size_t typesz, copyin_t fetch_type)
707 {
708 	int segs_needed, error = 0, msqid;
709 	kauth_cred_t cred = l->l_cred;
710 	struct msqid_ds *msqptr;
711 	struct __msg *msghdr;
712 	kmsq_t *msq;
713 	short next;
714 
715 	MSG_PRINTF(("call to msgsnd(%d, %p, %lld, %d)\n", msqidr,
716 	     user_msgp, (long long)msgsz, msgflg));
717 
718 	if ((ssize_t)msgsz < 0)
719 		return EINVAL;
720 
721 restart:
722 	msqid = IPCID_TO_IX(msqidr);
723 
724 	mutex_enter(&msgmutex);
725 	/* In case of reallocation, we will wait for completion */
726 	while (__predict_false(msg_realloc_state))
727 		cv_wait(&msg_realloc_cv, &msgmutex);
728 
729 	if (msqid < 0 || msqid >= msginfo.msgmni) {
730 		MSG_PRINTF(("msqid (%d) out of range (0<=msqid<%d)\n", msqid,
731 		    msginfo.msgmni));
732 		error = EINVAL;
733 		goto unlock;
734 	}
735 
736 	msq = &msqs[msqid];
737 	msqptr = &msq->msq_u;
738 
739 	if (msqptr->msg_qbytes == 0) {
740 		MSG_PRINTF(("no such message queue id\n"));
741 		error = EINVAL;
742 		goto unlock;
743 	}
744 	if (msqptr->msg_perm._seq != IPCID_TO_SEQ(msqidr)) {
745 		MSG_PRINTF(("wrong sequence number\n"));
746 		error = EINVAL;
747 		goto unlock;
748 	}
749 
750 	if ((error = ipcperm(cred, &msqptr->msg_perm, IPC_W))) {
751 		MSG_PRINTF(("requester doesn't have write access\n"));
752 		goto unlock;
753 	}
754 
755 	segs_needed = (msgsz + msginfo.msgssz - 1) / msginfo.msgssz;
756 	MSG_PRINTF(("msgsz=%lld, msgssz=%d, segs_needed=%d\n",
757 	    (long long)msgsz, msginfo.msgssz, segs_needed));
758 	for (;;) {
759 		int need_more_resources = 0;
760 
761 		/*
762 		 * check msgsz [cannot be negative since it is unsigned]
763 		 * (inside this loop in case msg_qbytes changes while we sleep)
764 		 */
765 
766 		if (msgsz > msqptr->msg_qbytes) {
767 			MSG_PRINTF(("msgsz > msqptr->msg_qbytes\n"));
768 			error = EINVAL;
769 			goto unlock;
770 		}
771 
772 		if (msqptr->msg_perm.mode & MSG_LOCKED) {
773 			MSG_PRINTF(("msqid is locked\n"));
774 			need_more_resources = 1;
775 		}
776 		if (msgsz + msqptr->_msg_cbytes > msqptr->msg_qbytes) {
777 			MSG_PRINTF(("msgsz + msg_cbytes > msg_qbytes\n"));
778 			need_more_resources = 1;
779 		}
780 		if (segs_needed > nfree_msgmaps) {
781 			MSG_PRINTF(("segs_needed > nfree_msgmaps\n"));
782 			need_more_resources = 1;
783 		}
784 		if (free_msghdrs == NULL) {
785 			MSG_PRINTF(("no more msghdrs\n"));
786 			need_more_resources = 1;
787 		}
788 
789 		if (need_more_resources) {
790 			int we_own_it;
791 
792 			if ((msgflg & IPC_NOWAIT) != 0) {
793 				MSG_PRINTF(("need more resources but caller "
794 				    "doesn't want to wait\n"));
795 				error = EAGAIN;
796 				goto unlock;
797 			}
798 
799 			if ((msqptr->msg_perm.mode & MSG_LOCKED) != 0) {
800 				MSG_PRINTF(("we don't own the msqid_ds\n"));
801 				we_own_it = 0;
802 			} else {
803 				/* Force later arrivals to wait for our
804 				   request */
805 				MSG_PRINTF(("we own the msqid_ds\n"));
806 				msqptr->msg_perm.mode |= MSG_LOCKED;
807 				we_own_it = 1;
808 			}
809 
810 			msg_waiters++;
811 			MSG_PRINTF(("goodnight\n"));
812 			error = cv_wait_sig(&msq->msq_cv, &msgmutex);
813 			MSG_PRINTF(("good morning, error=%d\n", error));
814 			msg_waiters--;
815 
816 			if (we_own_it)
817 				msqptr->msg_perm.mode &= ~MSG_LOCKED;
818 
819 			/*
820 			 * In case of such state, notify reallocator and
821 			 * restart the call.
822 			 */
823 			if (msg_realloc_state) {
824 				cv_broadcast(&msg_realloc_cv);
825 				mutex_exit(&msgmutex);
826 				goto restart;
827 			}
828 
829 			if (error != 0) {
830 				MSG_PRINTF(("msgsnd: interrupted system "
831 				    "call\n"));
832 				error = EINTR;
833 				goto unlock;
834 			}
835 
836 			/*
837 			 * Make sure that the msq queue still exists
838 			 */
839 
840 			if (msqptr->msg_qbytes == 0) {
841 				MSG_PRINTF(("msqid deleted\n"));
842 				error = EIDRM;
843 				goto unlock;
844 			}
845 		} else {
846 			MSG_PRINTF(("got all the resources that we need\n"));
847 			break;
848 		}
849 	}
850 
851 	/*
852 	 * We have the resources that we need.
853 	 * Make sure!
854 	 */
855 
856 	KASSERT((msqptr->msg_perm.mode & MSG_LOCKED) == 0);
857 	KASSERT(segs_needed <= nfree_msgmaps);
858 	KASSERT(msgsz + msqptr->_msg_cbytes <= msqptr->msg_qbytes);
859 	KASSERT(free_msghdrs != NULL);
860 
861 	/*
862 	 * Re-lock the msqid_ds in case we page-fault when copying in the
863 	 * message
864 	 */
865 
866 	KASSERT((msqptr->msg_perm.mode & MSG_LOCKED) == 0);
867 	msqptr->msg_perm.mode |= MSG_LOCKED;
868 
869 	/*
870 	 * Allocate a message header
871 	 */
872 
873 	msghdr = free_msghdrs;
874 	free_msghdrs = msghdr->msg_next;
875 	msghdr->msg_spot = -1;
876 	msghdr->msg_ts = msgsz;
877 
878 	/*
879 	 * Allocate space for the message
880 	 */
881 
882 	while (segs_needed > 0) {
883 		KASSERT(nfree_msgmaps > 0);
884 		KASSERT(free_msgmaps != -1);
885 		KASSERT(free_msgmaps < msginfo.msgseg);
886 
887 		next = free_msgmaps;
888 		MSG_PRINTF(("allocating segment %d to message\n", next));
889 		free_msgmaps = msgmaps[next].next;
890 		nfree_msgmaps--;
891 		msgmaps[next].next = msghdr->msg_spot;
892 		msghdr->msg_spot = next;
893 		segs_needed--;
894 	}
895 
896 	/*
897 	 * Copy in the message type
898 	 */
899 	mutex_exit(&msgmutex);
900 	error = (*fetch_type)(user_msgp, &msghdr->msg_type, typesz);
901 	mutex_enter(&msgmutex);
902 	if (error != 0) {
903 		MSG_PRINTF(("error %d copying the message type\n", error));
904 		msg_freehdr(msghdr);
905 		msqptr->msg_perm.mode &= ~MSG_LOCKED;
906 		cv_broadcast(&msq->msq_cv);
907 		goto unlock;
908 	}
909 	user_msgp += typesz;
910 
911 	/*
912 	 * Validate the message type
913 	 */
914 
915 	if (msghdr->msg_type < 1) {
916 		msg_freehdr(msghdr);
917 		msqptr->msg_perm.mode &= ~MSG_LOCKED;
918 		cv_broadcast(&msq->msq_cv);
919 		MSG_PRINTF(("mtype (%ld) < 1\n", msghdr->msg_type));
920 		error = EINVAL;
921 		goto unlock;
922 	}
923 
924 	/*
925 	 * Copy in the message body
926 	 */
927 
928 	next = msghdr->msg_spot;
929 	while (msgsz > 0) {
930 		size_t tlen;
931 		KASSERT(next > -1);
932 		KASSERT(next < msginfo.msgseg);
933 
934 		if (msgsz > msginfo.msgssz)
935 			tlen = msginfo.msgssz;
936 		else
937 			tlen = msgsz;
938 		mutex_exit(&msgmutex);
939 		error = copyin(user_msgp, &msgpool[next * msginfo.msgssz], tlen);
940 		mutex_enter(&msgmutex);
941 		if (error != 0) {
942 			MSG_PRINTF(("error %d copying in message segment\n",
943 			    error));
944 			msg_freehdr(msghdr);
945 			msqptr->msg_perm.mode &= ~MSG_LOCKED;
946 			cv_broadcast(&msq->msq_cv);
947 			goto unlock;
948 		}
949 		msgsz -= tlen;
950 		user_msgp += tlen;
951 		next = msgmaps[next].next;
952 	}
953 	KASSERT(next == -1);
954 
955 	/*
956 	 * We've got the message.  Unlock the msqid_ds.
957 	 */
958 
959 	msqptr->msg_perm.mode &= ~MSG_LOCKED;
960 
961 	/*
962 	 * Make sure that the msqid_ds is still allocated.
963 	 */
964 
965 	if (msqptr->msg_qbytes == 0) {
966 		msg_freehdr(msghdr);
967 		cv_broadcast(&msq->msq_cv);
968 		error = EIDRM;
969 		goto unlock;
970 	}
971 
972 	/*
973 	 * Put the message into the queue
974 	 */
975 
976 	if (msqptr->_msg_first == NULL) {
977 		msqptr->_msg_first = msghdr;
978 		msqptr->_msg_last = msghdr;
979 	} else {
980 		msqptr->_msg_last->msg_next = msghdr;
981 		msqptr->_msg_last = msghdr;
982 	}
983 	msqptr->_msg_last->msg_next = NULL;
984 
985 	msqptr->_msg_cbytes += msghdr->msg_ts;
986 	msqptr->msg_qnum++;
987 	msqptr->msg_lspid = l->l_proc->p_pid;
988 	msqptr->msg_stime = time_second;
989 
990 	cv_broadcast(&msq->msq_cv);
991 
992 unlock:
993 	mutex_exit(&msgmutex);
994 	return error;
995 }
996 
997 int
sys_msgrcv(struct lwp * l,const struct sys_msgrcv_args * uap,register_t * retval)998 sys_msgrcv(struct lwp *l, const struct sys_msgrcv_args *uap, register_t *retval)
999 {
1000 	/* {
1001 		syscallarg(int) msqid;
1002 		syscallarg(void *) msgp;
1003 		syscallarg(size_t) msgsz;
1004 		syscallarg(long) msgtyp;
1005 		syscallarg(int) msgflg;
1006 	} */
1007 
1008 	return msgrcv1(l, SCARG(uap, msqid), SCARG(uap, msgp),
1009 	    SCARG(uap, msgsz), SCARG(uap, msgtyp), SCARG(uap, msgflg),
1010 	    sizeof(long), copyout, retval);
1011 }
1012 
1013 int
msgrcv1(struct lwp * l,int msqidr,char * user_msgp,size_t msgsz,long msgtyp,int msgflg,size_t typesz,copyout_t put_type,register_t * retval)1014 msgrcv1(struct lwp *l, int msqidr, char *user_msgp, size_t msgsz, long msgtyp,
1015     int msgflg, size_t typesz, copyout_t put_type, register_t *retval)
1016 {
1017 	size_t len;
1018 	kauth_cred_t cred = l->l_cred;
1019 	struct msqid_ds *msqptr;
1020 	struct __msg *msghdr;
1021 	int error = 0, msqid;
1022 	kmsq_t *msq;
1023 	short next;
1024 
1025 	MSG_PRINTF(("call to msgrcv(%d, %p, %lld, %ld, %d)\n", msqidr,
1026 	    user_msgp, (long long)msgsz, msgtyp, msgflg));
1027 
1028 	if ((ssize_t)msgsz < 0)
1029 		return EINVAL;
1030 
1031 restart:
1032 	msqid = IPCID_TO_IX(msqidr);
1033 
1034 	mutex_enter(&msgmutex);
1035 	/* In case of reallocation, we will wait for completion */
1036 	while (__predict_false(msg_realloc_state))
1037 		cv_wait(&msg_realloc_cv, &msgmutex);
1038 
1039 	if (msqid < 0 || msqid >= msginfo.msgmni) {
1040 		MSG_PRINTF(("msqid (%d) out of range (0<=msqid<%d)\n", msqid,
1041 		    msginfo.msgmni));
1042 		error = EINVAL;
1043 		goto unlock;
1044 	}
1045 
1046 	msq = &msqs[msqid];
1047 	msqptr = &msq->msq_u;
1048 
1049 	if (msqptr->msg_qbytes == 0) {
1050 		MSG_PRINTF(("no such message queue id\n"));
1051 		error = EINVAL;
1052 		goto unlock;
1053 	}
1054 	if (msqptr->msg_perm._seq != IPCID_TO_SEQ(msqidr)) {
1055 		MSG_PRINTF(("wrong sequence number\n"));
1056 		error = EINVAL;
1057 		goto unlock;
1058 	}
1059 
1060 	if ((error = ipcperm(cred, &msqptr->msg_perm, IPC_R))) {
1061 		MSG_PRINTF(("requester doesn't have read access\n"));
1062 		goto unlock;
1063 	}
1064 
1065 	msghdr = NULL;
1066 	while (msghdr == NULL) {
1067 		if (msgtyp == 0) {
1068 			msghdr = msqptr->_msg_first;
1069 			if (msghdr != NULL) {
1070 				if (msgsz < msghdr->msg_ts &&
1071 				    (msgflg & MSG_NOERROR) == 0) {
1072 					MSG_PRINTF(("first msg on the queue "
1073 					    "is too big (want %lld, got %d)\n",
1074 					    (long long)msgsz, msghdr->msg_ts));
1075 					error = E2BIG;
1076 					goto unlock;
1077 				}
1078 				if (msqptr->_msg_first == msqptr->_msg_last) {
1079 					msqptr->_msg_first = NULL;
1080 					msqptr->_msg_last = NULL;
1081 				} else {
1082 					msqptr->_msg_first = msghdr->msg_next;
1083 					KASSERT(msqptr->_msg_first != NULL);
1084 				}
1085 			}
1086 		} else {
1087 			struct __msg *previous;
1088 			struct __msg **prev;
1089 
1090 			for (previous = NULL, prev = &msqptr->_msg_first;
1091 			     (msghdr = *prev) != NULL;
1092 			     previous = msghdr, prev = &msghdr->msg_next) {
1093 				/*
1094 				 * Is this message's type an exact match or is
1095 				 * this message's type less than or equal to
1096 				 * the absolute value of a negative msgtyp?
1097 				 * Note that the second half of this test can
1098 				 * NEVER be true if msgtyp is positive since
1099 				 * msg_type is always positive!
1100 				 */
1101 
1102 				if (msgtyp != msghdr->msg_type &&
1103 				    msgtyp != LONG_MIN &&
1104 				    msghdr->msg_type > -msgtyp)
1105 					continue;
1106 
1107 				MSG_PRINTF(("found message type %ld, requested %ld\n",
1108 				    msghdr->msg_type, msgtyp));
1109 				if (msgsz < msghdr->msg_ts &&
1110 				     (msgflg & MSG_NOERROR) == 0) {
1111 					MSG_PRINTF(("requested message on the queue "
1112 					    "is too big (want %lld, got %d)\n",
1113 					    (long long)msgsz, msghdr->msg_ts));
1114 					error = E2BIG;
1115 					goto unlock;
1116 				}
1117 				*prev = msghdr->msg_next;
1118 				if (msghdr != msqptr->_msg_last)
1119 					break;
1120 				if (previous == NULL) {
1121 					KASSERT(prev == &msqptr->_msg_first);
1122 					msqptr->_msg_first = NULL;
1123 					msqptr->_msg_last = NULL;
1124 				} else {
1125 					KASSERT(prev != &msqptr->_msg_first);
1126 					msqptr->_msg_last = previous;
1127 				}
1128 				break;
1129 			}
1130 		}
1131 
1132 		/*
1133 		 * We've either extracted the msghdr for the appropriate
1134 		 * message or there isn't one.
1135 		 * If there is one then bail out of this loop.
1136 		 */
1137 		if (msghdr != NULL)
1138 			break;
1139 
1140 		/*
1141 		 * Hmph!  No message found.  Does the user want to wait?
1142 		 */
1143 
1144 		if ((msgflg & IPC_NOWAIT) != 0) {
1145 			MSG_PRINTF(("no appropriate message found (msgtyp=%ld)\n",
1146 			    msgtyp));
1147 			error = ENOMSG;
1148 			goto unlock;
1149 		}
1150 
1151 		/*
1152 		 * Wait for something to happen
1153 		 */
1154 
1155 		msg_waiters++;
1156 		MSG_PRINTF(("msgrcv:  goodnight\n"));
1157 		error = cv_wait_sig(&msq->msq_cv, &msgmutex);
1158 		MSG_PRINTF(("msgrcv: good morning (error=%d)\n", error));
1159 		msg_waiters--;
1160 
1161 		/*
1162 		 * In case of such state, notify reallocator and
1163 		 * restart the call.
1164 		 */
1165 		if (msg_realloc_state) {
1166 			cv_broadcast(&msg_realloc_cv);
1167 			mutex_exit(&msgmutex);
1168 			goto restart;
1169 		}
1170 
1171 		if (error != 0) {
1172 			MSG_PRINTF(("msgsnd: interrupted system call\n"));
1173 			error = EINTR;
1174 			goto unlock;
1175 		}
1176 
1177 		/*
1178 		 * Make sure that the msq queue still exists
1179 		 */
1180 
1181 		if (msqptr->msg_qbytes == 0 ||
1182 		    msqptr->msg_perm._seq != IPCID_TO_SEQ(msqidr)) {
1183 			MSG_PRINTF(("msqid deleted\n"));
1184 			error = EIDRM;
1185 			goto unlock;
1186 		}
1187 	}
1188 
1189 	/*
1190 	 * Return the message to the user.
1191 	 *
1192 	 * First, do the bookkeeping (before we risk being interrupted).
1193 	 */
1194 
1195 	msqptr->_msg_cbytes -= msghdr->msg_ts;
1196 	msqptr->msg_qnum--;
1197 	msqptr->msg_lrpid = l->l_proc->p_pid;
1198 	msqptr->msg_rtime = time_second;
1199 
1200 	/*
1201 	 * Make msgsz the actual amount that we'll be returning.
1202 	 * Note that this effectively truncates the message if it is too long
1203 	 * (since msgsz is never increased).
1204 	 */
1205 
1206 	MSG_PRINTF(("found a message, msgsz=%lld, msg_ts=%d\n",
1207 	    (long long)msgsz, msghdr->msg_ts));
1208 	if (msgsz > msghdr->msg_ts)
1209 		msgsz = msghdr->msg_ts;
1210 
1211 	/*
1212 	 * Return the type to the user.
1213 	 */
1214 	mutex_exit(&msgmutex);
1215 	error = (*put_type)(&msghdr->msg_type, user_msgp, typesz);
1216 	mutex_enter(&msgmutex);
1217 	if (error != 0) {
1218 		MSG_PRINTF(("error (%d) copying out message type\n", error));
1219 		msg_freehdr(msghdr);
1220 		cv_broadcast(&msq->msq_cv);
1221 		goto unlock;
1222 	}
1223 	user_msgp += typesz;
1224 
1225 	/*
1226 	 * Return the segments to the user
1227 	 */
1228 
1229 	next = msghdr->msg_spot;
1230 	for (len = 0; len < msgsz; len += msginfo.msgssz) {
1231 		size_t tlen;
1232 		KASSERT(next > -1);
1233 		KASSERT(next < msginfo.msgseg);
1234 
1235 		if (msgsz - len > msginfo.msgssz)
1236 			tlen = msginfo.msgssz;
1237 		else
1238 			tlen = msgsz - len;
1239 		mutex_exit(&msgmutex);
1240 		error = copyout(&msgpool[next * msginfo.msgssz],
1241 		    user_msgp, tlen);
1242 		mutex_enter(&msgmutex);
1243 		if (error != 0) {
1244 			MSG_PRINTF(("error (%d) copying out message segment\n",
1245 			    error));
1246 			msg_freehdr(msghdr);
1247 			cv_broadcast(&msq->msq_cv);
1248 			goto unlock;
1249 		}
1250 		user_msgp += tlen;
1251 		next = msgmaps[next].next;
1252 	}
1253 
1254 	/*
1255 	 * Done, return the actual number of bytes copied out.
1256 	 */
1257 
1258 	msg_freehdr(msghdr);
1259 	cv_broadcast(&msq->msq_cv);
1260 	*retval = msgsz;
1261 
1262 unlock:
1263 	mutex_exit(&msgmutex);
1264 	return error;
1265 }
1266 
1267 /*
1268  * Sysctl initialization and nodes.
1269  */
1270 
1271 static int
sysctl_ipc_msgmni(SYSCTLFN_ARGS)1272 sysctl_ipc_msgmni(SYSCTLFN_ARGS)
1273 {
1274 	int newsize, error;
1275 	struct sysctlnode node;
1276 	node = *rnode;
1277 	node.sysctl_data = &newsize;
1278 
1279 	newsize = msginfo.msgmni;
1280 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
1281 	if (error || newp == NULL)
1282 		return error;
1283 
1284 	sysctl_unlock();
1285 	error = msgrealloc(newsize, msginfo.msgseg);
1286 	sysctl_relock();
1287 	return error;
1288 }
1289 
1290 static int
sysctl_ipc_msgseg(SYSCTLFN_ARGS)1291 sysctl_ipc_msgseg(SYSCTLFN_ARGS)
1292 {
1293 	int newsize, error;
1294 	struct sysctlnode node;
1295 	node = *rnode;
1296 	node.sysctl_data = &newsize;
1297 
1298 	newsize = msginfo.msgseg;
1299 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
1300 	if (error || newp == NULL)
1301 		return error;
1302 
1303 	sysctl_unlock();
1304 	error = msgrealloc(msginfo.msgmni, newsize);
1305 	sysctl_relock();
1306 	return error;
1307 }
1308 
1309 SYSCTL_SETUP(sysctl_ipc_msg_setup, "sysctl kern.ipc subtree setup")
1310 {
1311 	const struct sysctlnode *node = NULL;
1312 
1313 	sysctl_createv(clog, 0, NULL, &node,
1314 		CTLFLAG_PERMANENT,
1315 		CTLTYPE_NODE, "ipc",
1316 		SYSCTL_DESCR("SysV IPC options"),
1317 		NULL, 0, NULL, 0,
1318 		CTL_KERN, KERN_SYSVIPC, CTL_EOL);
1319 
1320 	if (node == NULL)
1321 		return;
1322 
1323 	sysctl_createv(clog, 0, &node, NULL,
1324 		CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
1325 		CTLTYPE_INT, "msgmni",
1326 		SYSCTL_DESCR("Max number of message queue identifiers"),
1327 		sysctl_ipc_msgmni, 0, &msginfo.msgmni, 0,
1328 		CTL_CREATE, CTL_EOL);
1329 	sysctl_createv(clog, 0, &node, NULL,
1330 		CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
1331 		CTLTYPE_INT, "msgseg",
1332 		SYSCTL_DESCR("Max number of number of message segments"),
1333 		sysctl_ipc_msgseg, 0, &msginfo.msgseg, 0,
1334 		CTL_CREATE, CTL_EOL);
1335 }
1336