1/*-
2 * Implementation of SVID messages
3 *
4 * Author:  Daniel Boulet
5 *
6 * Copyright 1993 Daniel Boulet and RTMX Inc.
7 *
8 * This system call was implemented by Daniel Boulet under contract from RTMX.
9 *
10 * Redistribution and use in source forms, with and without modification,
11 * are permitted provided that this entire comment appears intact.
12 *
13 * Redistribution in binary form may occur without any restrictions.
14 * Obviously, it would be nice if you gave credit where credit is due
15 * but requiring it would be too onerous.
16 *
17 * This software is provided ``AS IS'' without any warranties of any kind.
18 */
19/*-
20 * SPDX-License-Identifier: BSD-2-Clause
21 *
22 * Copyright (c) 2003-2005 McAfee, Inc.
23 * Copyright (c) 2016-2017 Robert N. M. Watson
24 * All rights reserved.
25 *
26 * This software was developed for the FreeBSD Project in part by McAfee
27 * Research, the Security Research Division of McAfee, Inc under DARPA/SPAWAR
28 * contract N66001-01-C-8035 ("CBOSS"), as part of the DARPA CHATS research
29 * program.
30 *
31 * Portions of this software were developed by BAE Systems, the University of
32 * Cambridge Computer Laboratory, and Memorial University under DARPA/AFRL
33 * contract FA8650-15-C-7558 ("CADETS"), as part of the DARPA Transparent
34 * Computing (TC) research program.
35 *
36 * Redistribution and use in source and binary forms, with or without
37 * modification, are permitted provided that the following conditions
38 * are met:
39 * 1. Redistributions of source code must retain the above copyright
40 *    notice, this list of conditions and the following disclaimer.
41 * 2. Redistributions in binary form must reproduce the above copyright
42 *    notice, this list of conditions and the following disclaimer in the
43 *    documentation and/or other materials provided with the distribution.
44 *
45 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
46 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
47 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
48 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
49 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
50 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
51 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
52 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
53 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
54 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
55 * SUCH DAMAGE.
56 */
57
58#include <sys/cdefs.h>
59#include "opt_sysvipc.h"
60
61#include <sys/param.h>
62#include <sys/systm.h>
63#include <sys/sysproto.h>
64#include <sys/kernel.h>
65#include <sys/priv.h>
66#include <sys/proc.h>
67#include <sys/lock.h>
68#include <sys/mutex.h>
69#include <sys/module.h>
70#include <sys/mount.h>
71#include <sys/msg.h>
72#include <sys/racct.h>
73#include <sys/sx.h>
74#include <sys/syscall.h>
75#include <sys/syscallsubr.h>
76#include <sys/sysent.h>
77#include <sys/sysctl.h>
78#include <sys/malloc.h>
79#include <sys/jail.h>
80
81#include <security/audit/audit.h>
82#include <security/mac/mac_framework.h>
83
84FEATURE(sysv_msg, "System V message queues support");
85
86static MALLOC_DEFINE(M_MSG, "msg", "SVID compatible message queues");
87
88static int msginit(void);
89static int msgunload(void);
90static int sysvmsg_modload(struct module *, int, void *);
91static void msq_remove(struct msqid_kernel *);
92static struct prison *msg_find_prison(struct ucred *);
93static int msq_prison_cansee(struct prison *, struct msqid_kernel *);
94static int msg_prison_check(void *, void *);
95static int msg_prison_set(void *, void *);
96static int msg_prison_get(void *, void *);
97static int msg_prison_remove(void *, void *);
98static void msg_prison_cleanup(struct prison *);
99
100#ifdef MSG_DEBUG
101#define DPRINTF(a)	printf a
102#else
103#define DPRINTF(a)	(void)0
104#endif
105
106static void msg_freehdr(struct msg *msghdr);
107
108#ifndef MSGSSZ
109#define MSGSSZ	8		/* Each segment must be 2^N long */
110#endif
111#ifndef MSGSEG
112#define MSGSEG	2048		/* must be less than 32767 */
113#endif
114#define MSGMAX	(MSGSSZ*MSGSEG)
115#ifndef MSGMNB
116#define MSGMNB	2048		/* max # of bytes in a queue */
117#endif
118#ifndef MSGMNI
119#define MSGMNI	40
120#endif
121#ifndef MSGTQL
122#define MSGTQL	40
123#endif
124
125/*
126 * Based on the configuration parameters described in an SVR2 (yes, two)
127 * config(1m) man page.
128 *
129 * Each message is broken up and stored in segments that are msgssz bytes
130 * long.  For efficiency reasons, this should be a power of two.  Also,
131 * it doesn't make sense if it is less than 8 or greater than about 256.
132 * Consequently, msginit in kern/sysv_msg.c checks that msgssz is a power of
133 * two between 8 and 1024 inclusive (and panic's if it isn't).
134 */
135struct msginfo msginfo = {
136                MSGMAX,         /* max chars in a message */
137                MSGMNI,         /* # of message queue identifiers */
138                MSGMNB,         /* max chars in a queue */
139                MSGTQL,         /* max messages in system */
140                MSGSSZ,         /* size of a message segment */
141                		/* (must be small power of 2 greater than 4) */
142                MSGSEG          /* number of message segments */
143};
144
145/*
146 * macros to convert between msqid_ds's and msqid's.
147 * (specific to this implementation)
148 */
149#define MSQID(ix,ds)	((ix) & 0xffff | (((ds).msg_perm.seq << 16) & 0xffff0000))
150#define MSQID_IX(id)	((id) & 0xffff)
151#define MSQID_SEQ(id)	(((id) >> 16) & 0xffff)
152
153/*
154 * The rest of this file is specific to this particular implementation.
155 */
156
157struct msgmap {
158	short	next;		/* next segment in buffer */
159    				/* -1 -> available */
160    				/* 0..(MSGSEG-1) -> index of next segment */
161};
162
163#define MSG_LOCKED	01000	/* Is this msqid_ds locked? */
164
165static int nfree_msgmaps;	/* # of free map entries */
166static short free_msgmaps;	/* head of linked list of free map entries */
167static struct msg *free_msghdrs;/* list of free msg headers */
168static char *msgpool;		/* MSGMAX byte long msg buffer pool */
169static struct msgmap *msgmaps;	/* MSGSEG msgmap structures */
170static struct msg *msghdrs;	/* MSGTQL msg headers */
171static struct msqid_kernel *msqids;	/* MSGMNI msqid_kernel struct's */
172static struct mtx msq_mtx;	/* global mutex for message queues. */
173static unsigned msg_prison_slot;/* prison OSD slot */
174
175static struct syscall_helper_data msg_syscalls[] = {
176	SYSCALL_INIT_HELPER(msgctl),
177	SYSCALL_INIT_HELPER(msgget),
178	SYSCALL_INIT_HELPER(msgsnd),
179	SYSCALL_INIT_HELPER(msgrcv),
180#if defined(COMPAT_FREEBSD4) || defined(COMPAT_FREEBSD5) || \
181    defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD7)
182	SYSCALL_INIT_HELPER(msgsys),
183	SYSCALL_INIT_HELPER_COMPAT(freebsd7_msgctl),
184#endif
185	SYSCALL_INIT_LAST
186};
187
188#ifdef COMPAT_FREEBSD32
189#include <compat/freebsd32/freebsd32.h>
190#include <compat/freebsd32/freebsd32_ipc.h>
191#include <compat/freebsd32/freebsd32_proto.h>
192#include <compat/freebsd32/freebsd32_signal.h>
193#include <compat/freebsd32/freebsd32_syscall.h>
194#include <compat/freebsd32/freebsd32_util.h>
195
196static struct syscall_helper_data msg32_syscalls[] = {
197	SYSCALL32_INIT_HELPER(freebsd32_msgctl),
198	SYSCALL32_INIT_HELPER(freebsd32_msgsnd),
199	SYSCALL32_INIT_HELPER(freebsd32_msgrcv),
200	SYSCALL32_INIT_HELPER_COMPAT(msgget),
201	SYSCALL32_INIT_HELPER(freebsd32_msgsys),
202#if defined(COMPAT_FREEBSD4) || defined(COMPAT_FREEBSD5) || \
203    defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD7)
204	SYSCALL32_INIT_HELPER(freebsd7_freebsd32_msgctl),
205#endif
206	SYSCALL_INIT_LAST
207};
208#endif
209
210static int
211msginit(void)
212{
213	struct prison *pr;
214	void **rsv;
215	int i, error;
216	osd_method_t methods[PR_MAXMETHOD] = {
217	    [PR_METHOD_CHECK] =		msg_prison_check,
218	    [PR_METHOD_SET] =		msg_prison_set,
219	    [PR_METHOD_GET] =		msg_prison_get,
220	    [PR_METHOD_REMOVE] =	msg_prison_remove,
221	};
222
223	msginfo.msgmax = msginfo.msgseg * msginfo.msgssz;
224	msgpool = malloc(msginfo.msgmax, M_MSG, M_WAITOK);
225	msgmaps = malloc(sizeof(struct msgmap) * msginfo.msgseg, M_MSG, M_WAITOK);
226	msghdrs = malloc(sizeof(struct msg) * msginfo.msgtql, M_MSG, M_WAITOK);
227	msqids = malloc(sizeof(struct msqid_kernel) * msginfo.msgmni, M_MSG,
228	    M_WAITOK | M_ZERO);
229
230	/*
231	 * msginfo.msgssz should be a power of two for efficiency reasons.
232	 * It is also pretty silly if msginfo.msgssz is less than 8
233	 * or greater than about 256 so ...
234	 */
235
236	i = 8;
237	while (i < 1024 && i != msginfo.msgssz)
238		i <<= 1;
239    	if (i != msginfo.msgssz) {
240		DPRINTF(("msginfo.msgssz=%d (0x%x)\n", msginfo.msgssz,
241		    msginfo.msgssz));
242		panic("msginfo.msgssz not a small power of 2");
243	}
244
245	if (msginfo.msgseg > 32767) {
246		DPRINTF(("msginfo.msgseg=%d\n", msginfo.msgseg));
247		panic("msginfo.msgseg > 32767");
248	}
249
250	for (i = 0; i < msginfo.msgseg; i++) {
251		if (i > 0)
252			msgmaps[i-1].next = i;
253		msgmaps[i].next = -1;	/* implies entry is available */
254	}
255	free_msgmaps = 0;
256	nfree_msgmaps = msginfo.msgseg;
257
258	for (i = 0; i < msginfo.msgtql; i++) {
259		msghdrs[i].msg_type = 0;
260		if (i > 0)
261			msghdrs[i-1].msg_next = &msghdrs[i];
262		msghdrs[i].msg_next = NULL;
263#ifdef MAC
264		mac_sysvmsg_init(&msghdrs[i]);
265#endif
266    	}
267	free_msghdrs = &msghdrs[0];
268
269	for (i = 0; i < msginfo.msgmni; i++) {
270		msqids[i].u.msg_qbytes = 0;	/* implies entry is available */
271		msqids[i].u.msg_perm.seq = 0;	/* reset to a known value */
272		msqids[i].u.msg_perm.mode = 0;
273#ifdef MAC
274		mac_sysvmsq_init(&msqids[i]);
275#endif
276	}
277	mtx_init(&msq_mtx, "msq", NULL, MTX_DEF);
278
279	/* Set current prisons according to their allow.sysvipc. */
280	msg_prison_slot = osd_jail_register(NULL, methods);
281	rsv = osd_reserve(msg_prison_slot);
282	prison_lock(&prison0);
283	(void)osd_jail_set_reserved(&prison0, msg_prison_slot, rsv, &prison0);
284	prison_unlock(&prison0);
285	rsv = NULL;
286	sx_slock(&allprison_lock);
287	TAILQ_FOREACH(pr, &allprison, pr_list) {
288		if (rsv == NULL)
289			rsv = osd_reserve(msg_prison_slot);
290		prison_lock(pr);
291		if (pr->pr_allow & PR_ALLOW_SYSVIPC) {
292			(void)osd_jail_set_reserved(pr, msg_prison_slot, rsv,
293			    &prison0);
294			rsv = NULL;
295		}
296		prison_unlock(pr);
297	}
298	if (rsv != NULL)
299		osd_free_reserved(rsv);
300	sx_sunlock(&allprison_lock);
301
302	error = syscall_helper_register(msg_syscalls, SY_THR_STATIC_KLD);
303	if (error != 0)
304		return (error);
305#ifdef COMPAT_FREEBSD32
306	error = syscall32_helper_register(msg32_syscalls, SY_THR_STATIC_KLD);
307	if (error != 0)
308		return (error);
309#endif
310	return (0);
311}
312
313static int
314msgunload(void)
315{
316	struct msqid_kernel *msqkptr;
317	int msqid;
318#ifdef MAC
319	int i;
320#endif
321
322	syscall_helper_unregister(msg_syscalls);
323#ifdef COMPAT_FREEBSD32
324	syscall32_helper_unregister(msg32_syscalls);
325#endif
326
327	for (msqid = 0; msqid < msginfo.msgmni; msqid++) {
328		msqkptr = &msqids[msqid];
329		if (msqkptr->u.msg_qbytes != 0 ||
330		    (msqkptr->u.msg_perm.mode & MSG_LOCKED) != 0)
331			break;
332	}
333	if (msqid != msginfo.msgmni)
334		return (EBUSY);
335
336	if (msg_prison_slot != 0)
337		osd_jail_deregister(msg_prison_slot);
338#ifdef MAC
339	for (i = 0; i < msginfo.msgtql; i++)
340		mac_sysvmsg_destroy(&msghdrs[i]);
341	for (msqid = 0; msqid < msginfo.msgmni; msqid++)
342		mac_sysvmsq_destroy(&msqids[msqid]);
343#endif
344	free(msgpool, M_MSG);
345	free(msgmaps, M_MSG);
346	free(msghdrs, M_MSG);
347	free(msqids, M_MSG);
348	mtx_destroy(&msq_mtx);
349	return (0);
350}
351
352static int
353sysvmsg_modload(struct module *module, int cmd, void *arg)
354{
355	int error = 0;
356
357	switch (cmd) {
358	case MOD_LOAD:
359		error = msginit();
360		if (error != 0)
361			msgunload();
362		break;
363	case MOD_UNLOAD:
364		error = msgunload();
365		break;
366	case MOD_SHUTDOWN:
367		break;
368	default:
369		error = EINVAL;
370		break;
371	}
372	return (error);
373}
374
375static moduledata_t sysvmsg_mod = {
376	"sysvmsg",
377	&sysvmsg_modload,
378	NULL
379};
380
381DECLARE_MODULE(sysvmsg, sysvmsg_mod, SI_SUB_SYSV_MSG, SI_ORDER_FIRST);
382MODULE_VERSION(sysvmsg, 1);
383
384static void
385msg_freehdr(struct msg *msghdr)
386{
387	while (msghdr->msg_ts > 0) {
388		short next;
389		if (msghdr->msg_spot < 0 || msghdr->msg_spot >= msginfo.msgseg)
390			panic("msghdr->msg_spot out of range");
391		next = msgmaps[msghdr->msg_spot].next;
392		msgmaps[msghdr->msg_spot].next = free_msgmaps;
393		free_msgmaps = msghdr->msg_spot;
394		nfree_msgmaps++;
395		msghdr->msg_spot = next;
396		if (msghdr->msg_ts >= msginfo.msgssz)
397			msghdr->msg_ts -= msginfo.msgssz;
398		else
399			msghdr->msg_ts = 0;
400	}
401	if (msghdr->msg_spot != -1)
402		panic("msghdr->msg_spot != -1");
403	msghdr->msg_next = free_msghdrs;
404	free_msghdrs = msghdr;
405#ifdef MAC
406	mac_sysvmsg_cleanup(msghdr);
407#endif
408}
409
410static void
411msq_remove(struct msqid_kernel *msqkptr)
412{
413	struct msg *msghdr;
414
415	racct_sub_cred(msqkptr->cred, RACCT_NMSGQ, 1);
416	racct_sub_cred(msqkptr->cred, RACCT_MSGQQUEUED, msqkptr->u.msg_qnum);
417	racct_sub_cred(msqkptr->cred, RACCT_MSGQSIZE, msqkptr->u.msg_cbytes);
418	crfree(msqkptr->cred);
419	msqkptr->cred = NULL;
420
421	/* Free the message headers */
422	msghdr = msqkptr->u.__msg_first;
423	while (msghdr != NULL) {
424		struct msg *msghdr_tmp;
425
426		/* Free the segments of each message */
427		msqkptr->u.msg_cbytes -= msghdr->msg_ts;
428		msqkptr->u.msg_qnum--;
429		msghdr_tmp = msghdr;
430		msghdr = msghdr->msg_next;
431		msg_freehdr(msghdr_tmp);
432	}
433
434	if (msqkptr->u.msg_cbytes != 0)
435		panic("msg_cbytes is screwed up");
436	if (msqkptr->u.msg_qnum != 0)
437		panic("msg_qnum is screwed up");
438
439	msqkptr->u.msg_qbytes = 0;	/* Mark it as free */
440
441#ifdef MAC
442	mac_sysvmsq_cleanup(msqkptr);
443#endif
444
445	wakeup(msqkptr);
446}
447
448static struct prison *
449msg_find_prison(struct ucred *cred)
450{
451	struct prison *pr, *rpr;
452
453	pr = cred->cr_prison;
454	prison_lock(pr);
455	rpr = osd_jail_get(pr, msg_prison_slot);
456	prison_unlock(pr);
457	return rpr;
458}
459
460static int
461msq_prison_cansee(struct prison *rpr, struct msqid_kernel *msqkptr)
462{
463
464	if (msqkptr->cred == NULL ||
465	    !(rpr == msqkptr->cred->cr_prison ||
466	      prison_ischild(rpr, msqkptr->cred->cr_prison)))
467		return (EINVAL);
468	return (0);
469}
470
471#ifndef _SYS_SYSPROTO_H_
472struct msgctl_args {
473	int	msqid;
474	int	cmd;
475	struct	msqid_ds *buf;
476};
477#endif
478int
479sys_msgctl(struct thread *td, struct msgctl_args *uap)
480{
481	int msqid = uap->msqid;
482	int cmd = uap->cmd;
483	struct msqid_ds msqbuf;
484	int error;
485
486	DPRINTF(("call to msgctl(%d, %d, %p)\n", msqid, cmd, uap->buf));
487	if (cmd == IPC_SET &&
488	    (error = copyin(uap->buf, &msqbuf, sizeof(msqbuf))) != 0)
489		return (error);
490	error = kern_msgctl(td, msqid, cmd, &msqbuf);
491	if (cmd == IPC_STAT && error == 0)
492		error = copyout(&msqbuf, uap->buf, sizeof(struct msqid_ds));
493	return (error);
494}
495
496int
497kern_msgctl(struct thread *td, int msqid, int cmd, struct msqid_ds *msqbuf)
498{
499	int rval, error, msqix;
500	struct msqid_kernel *msqkptr;
501	struct prison *rpr;
502
503	rpr = msg_find_prison(td->td_ucred);
504	if (rpr == NULL)
505		return (ENOSYS);
506
507	AUDIT_ARG_SVIPC_CMD(cmd);
508	AUDIT_ARG_SVIPC_ID(msqid);
509	msqix = IPCID_TO_IX(msqid);
510
511	if (msqix < 0 || msqix >= msginfo.msgmni) {
512		DPRINTF(("msqid (%d) out of range (0<=msqid<%d)\n", msqix,
513		    msginfo.msgmni));
514		return (EINVAL);
515	}
516
517	msqkptr = &msqids[msqix];
518
519	mtx_lock(&msq_mtx);
520	if (msqkptr->u.msg_qbytes == 0) {
521		DPRINTF(("no such msqid\n"));
522		error = EINVAL;
523		goto done2;
524	}
525	if (msqkptr->u.msg_perm.seq != IPCID_TO_SEQ(msqid)) {
526		DPRINTF(("wrong sequence number\n"));
527		error = EINVAL;
528		goto done2;
529	}
530
531	error = msq_prison_cansee(rpr, msqkptr);
532	if (error != 0) {
533		DPRINTF(("requester can't see prison\n"));
534		goto done2;
535	}
536
537#ifdef MAC
538	error = mac_sysvmsq_check_msqctl(td->td_ucred, msqkptr, cmd);
539	if (error != 0)
540		goto done2;
541#endif
542
543	error = 0;
544	rval = 0;
545
546	switch (cmd) {
547	case IPC_RMID:
548	{
549#ifdef MAC
550		struct msg *msghdr;
551#endif
552		if ((error = ipcperm(td, &msqkptr->u.msg_perm, IPC_M)))
553			goto done2;
554
555#ifdef MAC
556		/*
557		 * Check that the thread has MAC access permissions to
558		 * individual msghdrs.  Note: We need to do this in a
559		 * separate loop because the actual loop alters the
560		 * msq/msghdr info as it progresses, and there is no going
561		 * back if half the way through we discover that the
562		 * thread cannot free a certain msghdr.  The msq will get
563		 * into an inconsistent state.
564		 */
565		for (msghdr = msqkptr->u.__msg_first; msghdr != NULL;
566		    msghdr = msghdr->msg_next) {
567			error = mac_sysvmsq_check_msgrmid(td->td_ucred, msghdr);
568			if (error != 0)
569				goto done2;
570		}
571#endif
572
573		msq_remove(msqkptr);
574	}
575
576		break;
577
578	case IPC_SET:
579		AUDIT_ARG_SVIPC_PERM(&msqbuf->msg_perm);
580		if ((error = ipcperm(td, &msqkptr->u.msg_perm, IPC_M)))
581			goto done2;
582		if (msqbuf->msg_qbytes > msqkptr->u.msg_qbytes) {
583			error = priv_check(td, PRIV_IPC_MSGSIZE);
584			if (error)
585				goto done2;
586		}
587		if (msqbuf->msg_qbytes > msginfo.msgmnb) {
588			DPRINTF(("can't increase msg_qbytes beyond %d"
589			    "(truncating)\n", msginfo.msgmnb));
590			msqbuf->msg_qbytes = msginfo.msgmnb;	/* silently restrict qbytes to system limit */
591		}
592		if (msqbuf->msg_qbytes == 0) {
593			DPRINTF(("can't reduce msg_qbytes to 0\n"));
594			error = EINVAL;		/* non-standard errno! */
595			goto done2;
596		}
597		msqkptr->u.msg_perm.uid = msqbuf->msg_perm.uid;	/* change the owner */
598		msqkptr->u.msg_perm.gid = msqbuf->msg_perm.gid;	/* change the owner */
599		msqkptr->u.msg_perm.mode = (msqkptr->u.msg_perm.mode & ~0777) |
600		    (msqbuf->msg_perm.mode & 0777);
601		msqkptr->u.msg_qbytes = msqbuf->msg_qbytes;
602		msqkptr->u.msg_ctime = time_second;
603		break;
604
605	case IPC_STAT:
606		if ((error = ipcperm(td, &msqkptr->u.msg_perm, IPC_R))) {
607			DPRINTF(("requester doesn't have read access\n"));
608			goto done2;
609		}
610		*msqbuf = msqkptr->u;
611		if (td->td_ucred->cr_prison != msqkptr->cred->cr_prison)
612			msqbuf->msg_perm.key = IPC_PRIVATE;
613
614		/*
615		 * Try to hide the fact that the structure layout is shared by
616		 * both the kernel and userland.  These pointers are not useful
617		 * to userspace.
618		 */
619		msqbuf->__msg_first = msqbuf->__msg_last = NULL;
620		break;
621
622	default:
623		DPRINTF(("invalid command %d\n", cmd));
624		error = EINVAL;
625		goto done2;
626	}
627
628	if (error == 0)
629		td->td_retval[0] = rval;
630done2:
631	mtx_unlock(&msq_mtx);
632	return (error);
633}
634
635#ifndef _SYS_SYSPROTO_H_
636struct msgget_args {
637	key_t	key;
638	int	msgflg;
639};
640#endif
641
642int
643sys_msgget(struct thread *td, struct msgget_args *uap)
644{
645	int msqid, error = 0;
646	int key = uap->key;
647	int msgflg = uap->msgflg;
648	struct ucred *cred = td->td_ucred;
649	struct msqid_kernel *msqkptr = NULL;
650
651	DPRINTF(("msgget(0x%x, 0%o)\n", key, msgflg));
652
653	if (msg_find_prison(cred) == NULL)
654		return (ENOSYS);
655
656	mtx_lock(&msq_mtx);
657	if (key != IPC_PRIVATE) {
658		for (msqid = 0; msqid < msginfo.msgmni; msqid++) {
659			msqkptr = &msqids[msqid];
660			if (msqkptr->u.msg_qbytes != 0 &&
661			    msqkptr->cred != NULL &&
662			    msqkptr->cred->cr_prison == cred->cr_prison &&
663			    msqkptr->u.msg_perm.key == key)
664				break;
665		}
666		if (msqid < msginfo.msgmni) {
667			DPRINTF(("found public key\n"));
668			if ((msgflg & IPC_CREAT) && (msgflg & IPC_EXCL)) {
669				DPRINTF(("not exclusive\n"));
670				error = EEXIST;
671				goto done2;
672			}
673			AUDIT_ARG_SVIPC_ID(IXSEQ_TO_IPCID(msqid,
674			    msqkptr->u.msg_perm));
675			if ((error = ipcperm(td, &msqkptr->u.msg_perm,
676			    msgflg & 0700))) {
677				DPRINTF(("requester doesn't have 0%o access\n",
678				    msgflg & 0700));
679				goto done2;
680			}
681#ifdef MAC
682			error = mac_sysvmsq_check_msqget(cred, msqkptr);
683			if (error != 0)
684				goto done2;
685#endif
686			goto found;
687		}
688	}
689
690	DPRINTF(("need to allocate the msqid_ds\n"));
691	if (key == IPC_PRIVATE || (msgflg & IPC_CREAT)) {
692		for (msqid = 0; msqid < msginfo.msgmni; msqid++) {
693			/*
694			 * Look for an unallocated and unlocked msqid_ds.
695			 * msqid_ds's can be locked by msgsnd or msgrcv while
696			 * they are copying the message in/out.  We can't
697			 * re-use the entry until they release it.
698			 */
699			msqkptr = &msqids[msqid];
700			if (msqkptr->u.msg_qbytes == 0 &&
701			    (msqkptr->u.msg_perm.mode & MSG_LOCKED) == 0)
702				break;
703		}
704		if (msqid == msginfo.msgmni) {
705			DPRINTF(("no more msqid_ds's available\n"));
706			error = ENOSPC;
707			goto done2;
708		}
709#ifdef RACCT
710		if (racct_enable) {
711			PROC_LOCK(td->td_proc);
712			error = racct_add(td->td_proc, RACCT_NMSGQ, 1);
713			PROC_UNLOCK(td->td_proc);
714			if (error != 0) {
715				error = ENOSPC;
716				goto done2;
717			}
718		}
719#endif
720		DPRINTF(("msqid %d is available\n", msqid));
721		msqkptr->u.msg_perm.key = key;
722		msqkptr->u.msg_perm.cuid = cred->cr_uid;
723		msqkptr->u.msg_perm.uid = cred->cr_uid;
724		msqkptr->u.msg_perm.cgid = cred->cr_gid;
725		msqkptr->u.msg_perm.gid = cred->cr_gid;
726		msqkptr->u.msg_perm.mode = (msgflg & 0777);
727		msqkptr->cred = crhold(cred);
728		/* Make sure that the returned msqid is unique */
729		msqkptr->u.msg_perm.seq = (msqkptr->u.msg_perm.seq + 1) & 0x7fff;
730		msqkptr->u.__msg_first = NULL;
731		msqkptr->u.__msg_last = NULL;
732		msqkptr->u.msg_cbytes = 0;
733		msqkptr->u.msg_qnum = 0;
734		msqkptr->u.msg_qbytes = msginfo.msgmnb;
735		msqkptr->u.msg_lspid = 0;
736		msqkptr->u.msg_lrpid = 0;
737		msqkptr->u.msg_stime = 0;
738		msqkptr->u.msg_rtime = 0;
739		msqkptr->u.msg_ctime = time_second;
740#ifdef MAC
741		mac_sysvmsq_create(cred, msqkptr);
742#endif
743		AUDIT_ARG_SVIPC_PERM(&msqkptr->u.msg_perm);
744	} else {
745		DPRINTF(("didn't find it and wasn't asked to create it\n"));
746		error = ENOENT;
747		goto done2;
748	}
749
750found:
751	/* Construct the unique msqid */
752	td->td_retval[0] = IXSEQ_TO_IPCID(msqid, msqkptr->u.msg_perm);
753done2:
754	mtx_unlock(&msq_mtx);
755	return (error);
756}
757
758#ifndef _SYS_SYSPROTO_H_
759struct msgsnd_args {
760	int	msqid;
761	const void	*msgp;	/* XXX msgp is actually mtext. */
762	size_t	msgsz;
763	int	msgflg;
764};
765#endif
766int
767kern_msgsnd(struct thread *td, int msqid, const void *msgp,
768    size_t msgsz, int msgflg, long mtype)
769{
770	int msqix, segs_needed, error = 0;
771	struct msqid_kernel *msqkptr;
772	struct msg *msghdr;
773	struct prison *rpr;
774	short next;
775#ifdef RACCT
776	size_t saved_msgsz = 0;
777#endif
778
779	rpr = msg_find_prison(td->td_ucred);
780	if (rpr == NULL)
781		return (ENOSYS);
782
783	mtx_lock(&msq_mtx);
784	AUDIT_ARG_SVIPC_ID(msqid);
785	msqix = IPCID_TO_IX(msqid);
786
787	if (msqix < 0 || msqix >= msginfo.msgmni) {
788		DPRINTF(("msqid (%d) out of range (0<=msqid<%d)\n", msqix,
789		    msginfo.msgmni));
790		error = EINVAL;
791		goto done2;
792	}
793
794	msqkptr = &msqids[msqix];
795	AUDIT_ARG_SVIPC_PERM(&msqkptr->u.msg_perm);
796	if (msqkptr->u.msg_qbytes == 0) {
797		DPRINTF(("no such message queue id\n"));
798		error = EINVAL;
799		goto done2;
800	}
801	if (msqkptr->u.msg_perm.seq != IPCID_TO_SEQ(msqid)) {
802		DPRINTF(("wrong sequence number\n"));
803		error = EINVAL;
804		goto done2;
805	}
806
807	if ((error = msq_prison_cansee(rpr, msqkptr))) {
808		DPRINTF(("requester can't see prison\n"));
809		goto done2;
810	}
811
812	if ((error = ipcperm(td, &msqkptr->u.msg_perm, IPC_W))) {
813		DPRINTF(("requester doesn't have write access\n"));
814		goto done2;
815	}
816
817#ifdef MAC
818	error = mac_sysvmsq_check_msqsnd(td->td_ucred, msqkptr);
819	if (error != 0)
820		goto done2;
821#endif
822
823#ifdef RACCT
824	if (racct_enable) {
825		PROC_LOCK(td->td_proc);
826		if (racct_add(td->td_proc, RACCT_MSGQQUEUED, 1)) {
827			PROC_UNLOCK(td->td_proc);
828			error = EAGAIN;
829			goto done2;
830		}
831		saved_msgsz = msgsz;
832		if (racct_add(td->td_proc, RACCT_MSGQSIZE, msgsz)) {
833			racct_sub(td->td_proc, RACCT_MSGQQUEUED, 1);
834			PROC_UNLOCK(td->td_proc);
835			error = EAGAIN;
836			goto done2;
837		}
838		PROC_UNLOCK(td->td_proc);
839	}
840#endif
841
842	segs_needed = howmany(msgsz, msginfo.msgssz);
843	DPRINTF(("msgsz=%zu, msgssz=%d, segs_needed=%d\n", msgsz,
844	    msginfo.msgssz, segs_needed));
845	for (;;) {
846		int need_more_resources = 0;
847
848		/*
849		 * check msgsz
850		 * (inside this loop in case msg_qbytes changes while we sleep)
851		 */
852
853		if (msgsz > msqkptr->u.msg_qbytes) {
854			DPRINTF(("msgsz > msqkptr->u.msg_qbytes\n"));
855			error = EINVAL;
856			goto done3;
857		}
858
859		if (msqkptr->u.msg_perm.mode & MSG_LOCKED) {
860			DPRINTF(("msqid is locked\n"));
861			need_more_resources = 1;
862		}
863		if (msgsz + msqkptr->u.msg_cbytes > msqkptr->u.msg_qbytes) {
864			DPRINTF(("msgsz + msg_cbytes > msg_qbytes\n"));
865			need_more_resources = 1;
866		}
867		if (segs_needed > nfree_msgmaps) {
868			DPRINTF(("segs_needed > nfree_msgmaps\n"));
869			need_more_resources = 1;
870		}
871		if (free_msghdrs == NULL) {
872			DPRINTF(("no more msghdrs\n"));
873			need_more_resources = 1;
874		}
875
876		if (need_more_resources) {
877			int we_own_it;
878
879			if ((msgflg & IPC_NOWAIT) != 0) {
880				DPRINTF(("need more resources but caller "
881				    "doesn't want to wait\n"));
882				error = EAGAIN;
883				goto done3;
884			}
885
886			if ((msqkptr->u.msg_perm.mode & MSG_LOCKED) != 0) {
887				DPRINTF(("we don't own the msqid_ds\n"));
888				we_own_it = 0;
889			} else {
890				/* Force later arrivals to wait for our
891				   request */
892				DPRINTF(("we own the msqid_ds\n"));
893				msqkptr->u.msg_perm.mode |= MSG_LOCKED;
894				we_own_it = 1;
895			}
896			DPRINTF(("msgsnd:  goodnight\n"));
897			error = msleep(msqkptr, &msq_mtx, (PZERO - 4) | PCATCH,
898			    "msgsnd", hz);
899			DPRINTF(("msgsnd:  good morning, error=%d\n", error));
900			if (we_own_it)
901				msqkptr->u.msg_perm.mode &= ~MSG_LOCKED;
902			if (error == EWOULDBLOCK) {
903				DPRINTF(("msgsnd:  timed out\n"));
904				continue;
905			}
906			if (error != 0) {
907				DPRINTF(("msgsnd:  interrupted system call\n"));
908				error = EINTR;
909				goto done3;
910			}
911
912			/*
913			 * Make sure that the msq queue still exists
914			 */
915
916			if (msqkptr->u.msg_qbytes == 0) {
917				DPRINTF(("msqid deleted\n"));
918				error = EIDRM;
919				goto done3;
920			}
921
922		} else {
923			DPRINTF(("got all the resources that we need\n"));
924			break;
925		}
926	}
927
928	/*
929	 * We have the resources that we need.
930	 * Make sure!
931	 */
932
933	if (msqkptr->u.msg_perm.mode & MSG_LOCKED)
934		panic("msg_perm.mode & MSG_LOCKED");
935	if (segs_needed > nfree_msgmaps)
936		panic("segs_needed > nfree_msgmaps");
937	if (msgsz + msqkptr->u.msg_cbytes > msqkptr->u.msg_qbytes)
938		panic("msgsz + msg_cbytes > msg_qbytes");
939	if (free_msghdrs == NULL)
940		panic("no more msghdrs");
941
942	/*
943	 * Re-lock the msqid_ds in case we page-fault when copying in the
944	 * message
945	 */
946
947	if ((msqkptr->u.msg_perm.mode & MSG_LOCKED) != 0)
948		panic("msqid_ds is already locked");
949	msqkptr->u.msg_perm.mode |= MSG_LOCKED;
950
951	/*
952	 * Allocate a message header
953	 */
954
955	msghdr = free_msghdrs;
956	free_msghdrs = msghdr->msg_next;
957	msghdr->msg_spot = -1;
958	msghdr->msg_ts = msgsz;
959	msghdr->msg_type = mtype;
960#ifdef MAC
961	/*
962	 * XXXMAC: Should the mac_sysvmsq_check_msgmsq check follow here
963	 * immediately?  Or, should it be checked just before the msg is
964	 * enqueued in the msgq (as it is done now)?
965	 */
966	mac_sysvmsg_create(td->td_ucred, msqkptr, msghdr);
967#endif
968
969	/*
970	 * Allocate space for the message
971	 */
972
973	while (segs_needed > 0) {
974		if (nfree_msgmaps <= 0)
975			panic("not enough msgmaps");
976		if (free_msgmaps == -1)
977			panic("nil free_msgmaps");
978		next = free_msgmaps;
979		if (next <= -1)
980			panic("next too low #1");
981		if (next >= msginfo.msgseg)
982			panic("next out of range #1");
983		DPRINTF(("allocating segment %d to message\n", next));
984		free_msgmaps = msgmaps[next].next;
985		nfree_msgmaps--;
986		msgmaps[next].next = msghdr->msg_spot;
987		msghdr->msg_spot = next;
988		segs_needed--;
989	}
990
991	/*
992	 * Validate the message type
993	 */
994
995	if (msghdr->msg_type < 1) {
996		msg_freehdr(msghdr);
997		msqkptr->u.msg_perm.mode &= ~MSG_LOCKED;
998		wakeup(msqkptr);
999		DPRINTF(("mtype (%ld) < 1\n", msghdr->msg_type));
1000		error = EINVAL;
1001		goto done3;
1002	}
1003
1004	/*
1005	 * Copy in the message body
1006	 */
1007
1008	next = msghdr->msg_spot;
1009	while (msgsz > 0) {
1010		size_t tlen;
1011		if (msgsz > msginfo.msgssz)
1012			tlen = msginfo.msgssz;
1013		else
1014			tlen = msgsz;
1015		if (next <= -1)
1016			panic("next too low #2");
1017		if (next >= msginfo.msgseg)
1018			panic("next out of range #2");
1019		mtx_unlock(&msq_mtx);
1020		if ((error = copyin(msgp, &msgpool[next * msginfo.msgssz],
1021		    tlen)) != 0) {
1022			mtx_lock(&msq_mtx);
1023			DPRINTF(("error %d copying in message segment\n",
1024			    error));
1025			msg_freehdr(msghdr);
1026			msqkptr->u.msg_perm.mode &= ~MSG_LOCKED;
1027			wakeup(msqkptr);
1028			goto done3;
1029		}
1030		mtx_lock(&msq_mtx);
1031		msgsz -= tlen;
1032		msgp = (const char *)msgp + tlen;
1033		next = msgmaps[next].next;
1034	}
1035	if (next != -1)
1036		panic("didn't use all the msg segments");
1037
1038	/*
1039	 * We've got the message.  Unlock the msqid_ds.
1040	 */
1041
1042	msqkptr->u.msg_perm.mode &= ~MSG_LOCKED;
1043
1044	/*
1045	 * Make sure that the msqid_ds is still allocated.
1046	 */
1047
1048	if (msqkptr->u.msg_qbytes == 0) {
1049		msg_freehdr(msghdr);
1050		wakeup(msqkptr);
1051		error = EIDRM;
1052		goto done3;
1053	}
1054
1055#ifdef MAC
1056	/*
1057	 * Note: Since the task/thread allocates the msghdr and usually
1058	 * primes it with its own MAC label, for a majority of policies, it
1059	 * won't be necessary to check whether the msghdr has access
1060	 * permissions to the msgq.  The mac_sysvmsq_check_msqsnd check would
1061	 * suffice in that case.  However, this hook may be required where
1062	 * individual policies derive a non-identical label for the msghdr
1063	 * from the current thread label and may want to check the msghdr
1064	 * enqueue permissions, along with read/write permissions to the
1065	 * msgq.
1066	 */
1067	error = mac_sysvmsq_check_msgmsq(td->td_ucred, msghdr, msqkptr);
1068	if (error != 0) {
1069		msg_freehdr(msghdr);
1070		wakeup(msqkptr);
1071		goto done3;
1072	}
1073#endif
1074
1075	/*
1076	 * Put the message into the queue
1077	 */
1078	if (msqkptr->u.__msg_first == NULL) {
1079		msqkptr->u.__msg_first = msghdr;
1080		msqkptr->u.__msg_last = msghdr;
1081	} else {
1082		msqkptr->u.__msg_last->msg_next = msghdr;
1083		msqkptr->u.__msg_last = msghdr;
1084	}
1085	msqkptr->u.__msg_last->msg_next = NULL;
1086
1087	msqkptr->u.msg_cbytes += msghdr->msg_ts;
1088	msqkptr->u.msg_qnum++;
1089	msqkptr->u.msg_lspid = td->td_proc->p_pid;
1090	msqkptr->u.msg_stime = time_second;
1091
1092	wakeup(msqkptr);
1093	td->td_retval[0] = 0;
1094done3:
1095#ifdef RACCT
1096	if (racct_enable && error != 0) {
1097		PROC_LOCK(td->td_proc);
1098		racct_sub(td->td_proc, RACCT_MSGQQUEUED, 1);
1099		racct_sub(td->td_proc, RACCT_MSGQSIZE, saved_msgsz);
1100		PROC_UNLOCK(td->td_proc);
1101	}
1102#endif
1103done2:
1104	mtx_unlock(&msq_mtx);
1105	return (error);
1106}
1107
1108int
1109sys_msgsnd(struct thread *td, struct msgsnd_args *uap)
1110{
1111	int error;
1112	long mtype;
1113
1114	DPRINTF(("call to msgsnd(%d, %p, %zu, %d)\n", uap->msqid, uap->msgp,
1115	    uap->msgsz, uap->msgflg));
1116
1117	if ((error = copyin(uap->msgp, &mtype, sizeof(mtype))) != 0) {
1118		DPRINTF(("error %d copying the message type\n", error));
1119		return (error);
1120	}
1121	return (kern_msgsnd(td, uap->msqid,
1122	    (const char *)uap->msgp + sizeof(mtype),
1123	    uap->msgsz, uap->msgflg, mtype));
1124}
1125
1126#ifndef _SYS_SYSPROTO_H_
1127struct msgrcv_args {
1128	int	msqid;
1129	void	*msgp;
1130	size_t	msgsz;
1131	long	msgtyp;
1132	int	msgflg;
1133};
1134#endif
1135/* XXX msgp is actually mtext. */
1136int
1137kern_msgrcv(struct thread *td, int msqid, void *msgp, size_t msgsz, long msgtyp,
1138    int msgflg, long *mtype)
1139{
1140	size_t len;
1141	struct msqid_kernel *msqkptr;
1142	struct msg *msghdr;
1143	struct prison *rpr;
1144	int msqix, error = 0;
1145	short next;
1146
1147	rpr = msg_find_prison(td->td_ucred);
1148	if (rpr == NULL)
1149		return (ENOSYS);
1150
1151	AUDIT_ARG_SVIPC_ID(msqid);
1152	msqix = IPCID_TO_IX(msqid);
1153
1154	if (msqix < 0 || msqix >= msginfo.msgmni) {
1155		DPRINTF(("msqid (%d) out of range (0<=msqid<%d)\n", msqix,
1156		    msginfo.msgmni));
1157		return (EINVAL);
1158	}
1159
1160	msqkptr = &msqids[msqix];
1161	mtx_lock(&msq_mtx);
1162	AUDIT_ARG_SVIPC_PERM(&msqkptr->u.msg_perm);
1163	if (msqkptr->u.msg_qbytes == 0) {
1164		DPRINTF(("no such message queue id\n"));
1165		error = EINVAL;
1166		goto done2;
1167	}
1168	if (msqkptr->u.msg_perm.seq != IPCID_TO_SEQ(msqid)) {
1169		DPRINTF(("wrong sequence number\n"));
1170		error = EINVAL;
1171		goto done2;
1172	}
1173
1174	if ((error = msq_prison_cansee(rpr, msqkptr))) {
1175		DPRINTF(("requester can't see prison\n"));
1176		goto done2;
1177	}
1178
1179	if ((error = ipcperm(td, &msqkptr->u.msg_perm, IPC_R))) {
1180		DPRINTF(("requester doesn't have read access\n"));
1181		goto done2;
1182	}
1183
1184#ifdef MAC
1185	error = mac_sysvmsq_check_msqrcv(td->td_ucred, msqkptr);
1186	if (error != 0)
1187		goto done2;
1188#endif
1189
1190	msghdr = NULL;
1191	while (msghdr == NULL) {
1192		if (msgtyp == 0) {
1193			msghdr = msqkptr->u.__msg_first;
1194			if (msghdr != NULL) {
1195				if (msgsz < msghdr->msg_ts &&
1196				    (msgflg & MSG_NOERROR) == 0) {
1197					DPRINTF(("first message on the queue "
1198					    "is too big (want %zu, got %d)\n",
1199					    msgsz, msghdr->msg_ts));
1200					error = E2BIG;
1201					goto done2;
1202				}
1203#ifdef MAC
1204				error = mac_sysvmsq_check_msgrcv(td->td_ucred,
1205				    msghdr);
1206				if (error != 0)
1207					goto done2;
1208#endif
1209				if (msqkptr->u.__msg_first ==
1210				    msqkptr->u.__msg_last) {
1211					msqkptr->u.__msg_first = NULL;
1212					msqkptr->u.__msg_last = NULL;
1213				} else {
1214					msqkptr->u.__msg_first = msghdr->msg_next;
1215					if (msqkptr->u.__msg_first == NULL)
1216						panic("msg_first/last screwed up #1");
1217				}
1218			}
1219		} else {
1220			struct msg *previous;
1221			struct msg **prev;
1222
1223			previous = NULL;
1224			prev = &(msqkptr->u.__msg_first);
1225			while ((msghdr = *prev) != NULL) {
1226				/*
1227				 * Is this message's type an exact match or is
1228				 * this message's type less than or equal to
1229				 * the absolute value of a negative msgtyp?
1230				 * Note that the second half of this test can
1231				 * NEVER be true if msgtyp is positive since
1232				 * msg_type is always positive!
1233				 */
1234
1235				if (msgtyp == msghdr->msg_type ||
1236				    msghdr->msg_type <= -msgtyp) {
1237					DPRINTF(("found message type %ld, "
1238					    "requested %ld\n",
1239					    msghdr->msg_type, msgtyp));
1240					if (msgsz < msghdr->msg_ts &&
1241					    (msgflg & MSG_NOERROR) == 0) {
1242						DPRINTF(("requested message "
1243						    "on the queue is too big "
1244						    "(want %zu, got %hu)\n",
1245						    msgsz, msghdr->msg_ts));
1246						error = E2BIG;
1247						goto done2;
1248					}
1249#ifdef MAC
1250					error = mac_sysvmsq_check_msgrcv(
1251					    td->td_ucred, msghdr);
1252					if (error != 0)
1253						goto done2;
1254#endif
1255					*prev = msghdr->msg_next;
1256					if (msghdr == msqkptr->u.__msg_last) {
1257						if (previous == NULL) {
1258							if (prev !=
1259							    &msqkptr->u.__msg_first)
1260								panic("__msg_first/last screwed up #2");
1261							msqkptr->u.__msg_first =
1262							    NULL;
1263							msqkptr->u.__msg_last =
1264							    NULL;
1265						} else {
1266							if (prev ==
1267							    &msqkptr->u.__msg_first)
1268								panic("__msg_first/last screwed up #3");
1269							msqkptr->u.__msg_last =
1270							    previous;
1271						}
1272					}
1273					break;
1274				}
1275				previous = msghdr;
1276				prev = &(msghdr->msg_next);
1277			}
1278		}
1279
1280		/*
1281		 * We've either extracted the msghdr for the appropriate
1282		 * message or there isn't one.
1283		 * If there is one then bail out of this loop.
1284		 */
1285
1286		if (msghdr != NULL)
1287			break;
1288
1289		/*
1290		 * Hmph!  No message found.  Does the user want to wait?
1291		 */
1292
1293		if ((msgflg & IPC_NOWAIT) != 0) {
1294			DPRINTF(("no appropriate message found (msgtyp=%ld)\n",
1295			    msgtyp));
1296			/* The SVID says to return ENOMSG. */
1297			error = ENOMSG;
1298			goto done2;
1299		}
1300
1301		/*
1302		 * Wait for something to happen
1303		 */
1304
1305		DPRINTF(("msgrcv:  goodnight\n"));
1306		error = msleep(msqkptr, &msq_mtx, (PZERO - 4) | PCATCH,
1307		    "msgrcv", 0);
1308		DPRINTF(("msgrcv:  good morning (error=%d)\n", error));
1309
1310		if (error != 0) {
1311			DPRINTF(("msgrcv:  interrupted system call\n"));
1312			error = EINTR;
1313			goto done2;
1314		}
1315
1316		/*
1317		 * Make sure that the msq queue still exists
1318		 */
1319
1320		if (msqkptr->u.msg_qbytes == 0 ||
1321		    msqkptr->u.msg_perm.seq != IPCID_TO_SEQ(msqid)) {
1322			DPRINTF(("msqid deleted\n"));
1323			error = EIDRM;
1324			goto done2;
1325		}
1326	}
1327
1328	/*
1329	 * Return the message to the user.
1330	 *
1331	 * First, do the bookkeeping (before we risk being interrupted).
1332	 */
1333
1334	msqkptr->u.msg_cbytes -= msghdr->msg_ts;
1335	msqkptr->u.msg_qnum--;
1336	msqkptr->u.msg_lrpid = td->td_proc->p_pid;
1337	msqkptr->u.msg_rtime = time_second;
1338
1339	racct_sub_cred(msqkptr->cred, RACCT_MSGQQUEUED, 1);
1340	racct_sub_cred(msqkptr->cred, RACCT_MSGQSIZE, msghdr->msg_ts);
1341
1342	/*
1343	 * Make msgsz the actual amount that we'll be returning.
1344	 * Note that this effectively truncates the message if it is too long
1345	 * (since msgsz is never increased).
1346	 */
1347
1348	DPRINTF(("found a message, msgsz=%zu, msg_ts=%hu\n", msgsz,
1349	    msghdr->msg_ts));
1350	if (msgsz > msghdr->msg_ts)
1351		msgsz = msghdr->msg_ts;
1352	*mtype = msghdr->msg_type;
1353
1354	/*
1355	 * Return the segments to the user
1356	 */
1357
1358	next = msghdr->msg_spot;
1359	for (len = 0; len < msgsz; len += msginfo.msgssz) {
1360		size_t tlen;
1361
1362		if (msgsz - len > msginfo.msgssz)
1363			tlen = msginfo.msgssz;
1364		else
1365			tlen = msgsz - len;
1366		if (next <= -1)
1367			panic("next too low #3");
1368		if (next >= msginfo.msgseg)
1369			panic("next out of range #3");
1370		mtx_unlock(&msq_mtx);
1371		error = copyout(&msgpool[next * msginfo.msgssz], msgp, tlen);
1372		mtx_lock(&msq_mtx);
1373		if (error != 0) {
1374			DPRINTF(("error (%d) copying out message segment\n",
1375			    error));
1376			msg_freehdr(msghdr);
1377			wakeup(msqkptr);
1378			goto done2;
1379		}
1380		msgp = (char *)msgp + tlen;
1381		next = msgmaps[next].next;
1382	}
1383
1384	/*
1385	 * Done, return the actual number of bytes copied out.
1386	 */
1387
1388	msg_freehdr(msghdr);
1389	wakeup(msqkptr);
1390	td->td_retval[0] = msgsz;
1391done2:
1392	mtx_unlock(&msq_mtx);
1393	return (error);
1394}
1395
1396int
1397sys_msgrcv(struct thread *td, struct msgrcv_args *uap)
1398{
1399	int error;
1400	long mtype;
1401
1402	DPRINTF(("call to msgrcv(%d, %p, %zu, %ld, %d)\n", uap->msqid,
1403	    uap->msgp, uap->msgsz, uap->msgtyp, uap->msgflg));
1404
1405	if ((error = kern_msgrcv(td, uap->msqid,
1406	    (char *)uap->msgp + sizeof(mtype), uap->msgsz,
1407	    uap->msgtyp, uap->msgflg, &mtype)) != 0)
1408		return (error);
1409	if ((error = copyout(&mtype, uap->msgp, sizeof(mtype))) != 0)
1410		DPRINTF(("error %d copying the message type\n", error));
1411	return (error);
1412}
1413
1414static int
1415sysctl_msqids(SYSCTL_HANDLER_ARGS)
1416{
1417	struct msqid_kernel tmsqk;
1418#ifdef COMPAT_FREEBSD32
1419	struct msqid_kernel32 tmsqk32;
1420#endif
1421	struct prison *pr, *rpr;
1422	void *outaddr;
1423	size_t outsize;
1424	int error, i;
1425
1426	pr = req->td->td_ucred->cr_prison;
1427	rpr = msg_find_prison(req->td->td_ucred);
1428	error = 0;
1429	for (i = 0; i < msginfo.msgmni; i++) {
1430		mtx_lock(&msq_mtx);
1431		if (msqids[i].u.msg_qbytes == 0 || rpr == NULL ||
1432		    msq_prison_cansee(rpr, &msqids[i]) != 0)
1433			bzero(&tmsqk, sizeof(tmsqk));
1434		else {
1435			tmsqk = msqids[i];
1436			if (tmsqk.cred->cr_prison != pr)
1437				tmsqk.u.msg_perm.key = IPC_PRIVATE;
1438		}
1439		mtx_unlock(&msq_mtx);
1440#ifdef COMPAT_FREEBSD32
1441		if (SV_CURPROC_FLAG(SV_ILP32)) {
1442			bzero(&tmsqk32, sizeof(tmsqk32));
1443			freebsd32_ipcperm_out(&tmsqk.u.msg_perm,
1444			    &tmsqk32.u.msg_perm);
1445			/* Don't copy u.msg_first or u.msg_last */
1446			CP(tmsqk, tmsqk32, u.msg_cbytes);
1447			CP(tmsqk, tmsqk32, u.msg_qnum);
1448			CP(tmsqk, tmsqk32, u.msg_qbytes);
1449			CP(tmsqk, tmsqk32, u.msg_lspid);
1450			CP(tmsqk, tmsqk32, u.msg_lrpid);
1451			CP(tmsqk, tmsqk32, u.msg_stime);
1452			CP(tmsqk, tmsqk32, u.msg_rtime);
1453			CP(tmsqk, tmsqk32, u.msg_ctime);
1454			/* Don't copy label or cred */
1455			outaddr = &tmsqk32;
1456			outsize = sizeof(tmsqk32);
1457		} else
1458#endif
1459		{
1460			/* Don't leak kernel pointers */
1461			tmsqk.u.__msg_first = NULL;
1462			tmsqk.u.__msg_last = NULL;
1463			tmsqk.label = NULL;
1464			tmsqk.cred = NULL;
1465			/*
1466			 * XXX: some padding also exists, but we take care to
1467			 * allocate our pool of msqid_kernel structs with
1468			 * zeroed memory so this should be OK.
1469			 */
1470			outaddr = &tmsqk;
1471			outsize = sizeof(tmsqk);
1472		}
1473		error = SYSCTL_OUT(req, outaddr, outsize);
1474		if (error != 0)
1475			break;
1476	}
1477	return (error);
1478}
1479
1480int
1481kern_get_msqids(struct thread *td, struct msqid_kernel **res, size_t *sz)
1482{
1483	struct msqid_kernel *pmsqk;
1484	struct prison *pr, *rpr;
1485	int i, mi;
1486
1487	*sz = mi = msginfo.msgmni;
1488	if (res == NULL)
1489		return (0);
1490
1491	pr = td->td_ucred->cr_prison;
1492	rpr = msg_find_prison(td->td_ucred);
1493	*res = malloc(sizeof(struct msqid_kernel) * mi, M_TEMP, M_WAITOK);
1494	for (i = 0; i < mi; i++) {
1495		pmsqk = &(*res)[i];
1496		mtx_lock(&msq_mtx);
1497		if (msqids[i].u.msg_qbytes == 0 || rpr == NULL ||
1498		    msq_prison_cansee(rpr, &msqids[i]) != 0)
1499			bzero(pmsqk, sizeof(*pmsqk));
1500		else {
1501			*pmsqk = msqids[i];
1502			if (pmsqk->cred->cr_prison != pr)
1503				pmsqk->u.msg_perm.key = IPC_PRIVATE;
1504		}
1505		mtx_unlock(&msq_mtx);
1506		pmsqk->u.__msg_first = NULL;
1507		pmsqk->u.__msg_last = NULL;
1508		pmsqk->label = NULL;
1509		pmsqk->cred = NULL;
1510	}
1511	return (0);
1512}
1513
1514SYSCTL_INT(_kern_ipc, OID_AUTO, msgmax, CTLFLAG_RD, &msginfo.msgmax, 0,
1515    "Maximum message size");
1516SYSCTL_INT(_kern_ipc, OID_AUTO, msgmni, CTLFLAG_RDTUN, &msginfo.msgmni, 0,
1517    "Number of message queue identifiers");
1518SYSCTL_INT(_kern_ipc, OID_AUTO, msgmnb, CTLFLAG_RDTUN, &msginfo.msgmnb, 0,
1519    "Maximum number of bytes in a queue");
1520SYSCTL_INT(_kern_ipc, OID_AUTO, msgtql, CTLFLAG_RDTUN, &msginfo.msgtql, 0,
1521    "Maximum number of messages in the system");
1522SYSCTL_INT(_kern_ipc, OID_AUTO, msgssz, CTLFLAG_RDTUN, &msginfo.msgssz, 0,
1523    "Size of a message segment");
1524SYSCTL_INT(_kern_ipc, OID_AUTO, msgseg, CTLFLAG_RDTUN, &msginfo.msgseg, 0,
1525    "Number of message segments");
1526SYSCTL_PROC(_kern_ipc, OID_AUTO, msqids,
1527    CTLTYPE_OPAQUE | CTLFLAG_RD | CTLFLAG_MPSAFE,
1528    NULL, 0, sysctl_msqids, "",
1529    "Array of struct msqid_kernel for each potential message queue");
1530
1531static int
1532msg_prison_check(void *obj, void *data)
1533{
1534	struct prison *pr = obj;
1535	struct prison *prpr;
1536	struct vfsoptlist *opts = data;
1537	int error, jsys;
1538
1539	/*
1540	 * sysvmsg is a jailsys integer.
1541	 * It must be "disable" if the parent jail is disabled.
1542	 */
1543	error = vfs_copyopt(opts, "sysvmsg", &jsys, sizeof(jsys));
1544	if (error != ENOENT) {
1545		if (error != 0)
1546			return (error);
1547		switch (jsys) {
1548		case JAIL_SYS_DISABLE:
1549			break;
1550		case JAIL_SYS_NEW:
1551		case JAIL_SYS_INHERIT:
1552			prison_lock(pr->pr_parent);
1553			prpr = osd_jail_get(pr->pr_parent, msg_prison_slot);
1554			prison_unlock(pr->pr_parent);
1555			if (prpr == NULL)
1556				return (EPERM);
1557			break;
1558		default:
1559			return (EINVAL);
1560		}
1561	}
1562
1563	return (0);
1564}
1565
1566static int
1567msg_prison_set(void *obj, void *data)
1568{
1569	struct prison *pr = obj;
1570	struct prison *tpr, *orpr, *nrpr, *trpr;
1571	struct vfsoptlist *opts = data;
1572	void *rsv;
1573	int jsys, descend;
1574
1575	/*
1576	 * sysvmsg controls which jail is the root of the associated msgs (this
1577	 * jail or same as the parent), or if the feature is available at all.
1578	 */
1579	if (vfs_copyopt(opts, "sysvmsg", &jsys, sizeof(jsys)) == ENOENT)
1580		jsys = vfs_flagopt(opts, "allow.sysvipc", NULL, 0)
1581		    ? JAIL_SYS_INHERIT
1582		    : vfs_flagopt(opts, "allow.nosysvipc", NULL, 0)
1583		    ? JAIL_SYS_DISABLE
1584		    : -1;
1585	if (jsys == JAIL_SYS_DISABLE) {
1586		prison_lock(pr);
1587		orpr = osd_jail_get(pr, msg_prison_slot);
1588		if (orpr != NULL)
1589			osd_jail_del(pr, msg_prison_slot);
1590		prison_unlock(pr);
1591		if (orpr != NULL) {
1592			if (orpr == pr)
1593				msg_prison_cleanup(pr);
1594			/* Disable all child jails as well. */
1595			FOREACH_PRISON_DESCENDANT(pr, tpr, descend) {
1596				prison_lock(tpr);
1597				trpr = osd_jail_get(tpr, msg_prison_slot);
1598				if (trpr != NULL) {
1599					osd_jail_del(tpr, msg_prison_slot);
1600					prison_unlock(tpr);
1601					if (trpr == tpr)
1602						msg_prison_cleanup(tpr);
1603				} else {
1604					prison_unlock(tpr);
1605					descend = 0;
1606				}
1607			}
1608		}
1609	} else if (jsys != -1) {
1610		if (jsys == JAIL_SYS_NEW)
1611			nrpr = pr;
1612		else {
1613			prison_lock(pr->pr_parent);
1614			nrpr = osd_jail_get(pr->pr_parent, msg_prison_slot);
1615			prison_unlock(pr->pr_parent);
1616		}
1617		rsv = osd_reserve(msg_prison_slot);
1618		prison_lock(pr);
1619		orpr = osd_jail_get(pr, msg_prison_slot);
1620		if (orpr != nrpr)
1621			(void)osd_jail_set_reserved(pr, msg_prison_slot, rsv,
1622			    nrpr);
1623		else
1624			osd_free_reserved(rsv);
1625		prison_unlock(pr);
1626		if (orpr != nrpr) {
1627			if (orpr == pr)
1628				msg_prison_cleanup(pr);
1629			if (orpr != NULL) {
1630				/* Change child jails matching the old root, */
1631				FOREACH_PRISON_DESCENDANT(pr, tpr, descend) {
1632					prison_lock(tpr);
1633					trpr = osd_jail_get(tpr,
1634					    msg_prison_slot);
1635					if (trpr == orpr) {
1636						(void)osd_jail_set(tpr,
1637						    msg_prison_slot, nrpr);
1638						prison_unlock(tpr);
1639						if (trpr == tpr)
1640							msg_prison_cleanup(tpr);
1641					} else {
1642						prison_unlock(tpr);
1643						descend = 0;
1644					}
1645				}
1646			}
1647		}
1648	}
1649
1650	return (0);
1651}
1652
1653static int
1654msg_prison_get(void *obj, void *data)
1655{
1656	struct prison *pr = obj;
1657	struct prison *rpr;
1658	struct vfsoptlist *opts = data;
1659	int error, jsys;
1660
1661	/* Set sysvmsg based on the jail's root prison. */
1662	prison_lock(pr);
1663	rpr = osd_jail_get(pr, msg_prison_slot);
1664	prison_unlock(pr);
1665	jsys = rpr == NULL ? JAIL_SYS_DISABLE
1666	    : rpr == pr ? JAIL_SYS_NEW : JAIL_SYS_INHERIT;
1667	error = vfs_setopt(opts, "sysvmsg", &jsys, sizeof(jsys));
1668	if (error == ENOENT)
1669		error = 0;
1670	return (error);
1671}
1672
1673static int
1674msg_prison_remove(void *obj, void *data __unused)
1675{
1676	struct prison *pr = obj;
1677	struct prison *rpr;
1678
1679	prison_lock(pr);
1680	rpr = osd_jail_get(pr, msg_prison_slot);
1681	prison_unlock(pr);
1682	if (rpr == pr)
1683		msg_prison_cleanup(pr);
1684	return (0);
1685}
1686
1687static void
1688msg_prison_cleanup(struct prison *pr)
1689{
1690	struct msqid_kernel *msqkptr;
1691	int i;
1692
1693	/* Remove any msqs that belong to this jail. */
1694	mtx_lock(&msq_mtx);
1695	for (i = 0; i < msginfo.msgmni; i++) {
1696		msqkptr = &msqids[i];
1697		if (msqkptr->u.msg_qbytes != 0 &&
1698		    msqkptr->cred != NULL && msqkptr->cred->cr_prison == pr)
1699			msq_remove(msqkptr);
1700	}
1701	mtx_unlock(&msq_mtx);
1702}
1703
1704SYSCTL_JAIL_PARAM_SYS_NODE(sysvmsg, CTLFLAG_RW, "SYSV message queues");
1705
1706#ifdef COMPAT_FREEBSD32
1707int
1708freebsd32_msgsys(struct thread *td, struct freebsd32_msgsys_args *uap)
1709{
1710
1711#if defined(COMPAT_FREEBSD4) || defined(COMPAT_FREEBSD5) || \
1712    defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD7)
1713	AUDIT_ARG_SVIPC_WHICH(uap->which);
1714	switch (uap->which) {
1715	case 0:
1716		return (freebsd7_freebsd32_msgctl(td,
1717		    (struct freebsd7_freebsd32_msgctl_args *)&uap->a2));
1718	case 2:
1719		return (freebsd32_msgsnd(td,
1720		    (struct freebsd32_msgsnd_args *)&uap->a2));
1721	case 3:
1722		return (freebsd32_msgrcv(td,
1723		    (struct freebsd32_msgrcv_args *)&uap->a2));
1724	default:
1725		return (sys_msgsys(td, (struct msgsys_args *)uap));
1726	}
1727#else
1728	return (nosys(td, NULL));
1729#endif
1730}
1731
1732#if defined(COMPAT_FREEBSD4) || defined(COMPAT_FREEBSD5) || \
1733    defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD7)
1734int
1735freebsd7_freebsd32_msgctl(struct thread *td,
1736    struct freebsd7_freebsd32_msgctl_args *uap)
1737{
1738	struct msqid_ds msqbuf;
1739	struct msqid_ds_old32 msqbuf32;
1740	int error;
1741
1742	if (uap->cmd == IPC_SET) {
1743		error = copyin(uap->buf, &msqbuf32, sizeof(msqbuf32));
1744		if (error)
1745			return (error);
1746		freebsd32_ipcperm_old_in(&msqbuf32.msg_perm, &msqbuf.msg_perm);
1747		PTRIN_CP(msqbuf32, msqbuf, __msg_first);
1748		PTRIN_CP(msqbuf32, msqbuf, __msg_last);
1749		CP(msqbuf32, msqbuf, msg_cbytes);
1750		CP(msqbuf32, msqbuf, msg_qnum);
1751		CP(msqbuf32, msqbuf, msg_qbytes);
1752		CP(msqbuf32, msqbuf, msg_lspid);
1753		CP(msqbuf32, msqbuf, msg_lrpid);
1754		CP(msqbuf32, msqbuf, msg_stime);
1755		CP(msqbuf32, msqbuf, msg_rtime);
1756		CP(msqbuf32, msqbuf, msg_ctime);
1757	}
1758	error = kern_msgctl(td, uap->msqid, uap->cmd, &msqbuf);
1759	if (error)
1760		return (error);
1761	if (uap->cmd == IPC_STAT) {
1762		bzero(&msqbuf32, sizeof(msqbuf32));
1763		freebsd32_ipcperm_old_out(&msqbuf.msg_perm, &msqbuf32.msg_perm);
1764		PTROUT_CP(msqbuf, msqbuf32, __msg_first);
1765		PTROUT_CP(msqbuf, msqbuf32, __msg_last);
1766		CP(msqbuf, msqbuf32, msg_cbytes);
1767		CP(msqbuf, msqbuf32, msg_qnum);
1768		CP(msqbuf, msqbuf32, msg_qbytes);
1769		CP(msqbuf, msqbuf32, msg_lspid);
1770		CP(msqbuf, msqbuf32, msg_lrpid);
1771		CP(msqbuf, msqbuf32, msg_stime);
1772		CP(msqbuf, msqbuf32, msg_rtime);
1773		CP(msqbuf, msqbuf32, msg_ctime);
1774		error = copyout(&msqbuf32, uap->buf, sizeof(struct msqid_ds32));
1775	}
1776	return (error);
1777}
1778#endif
1779
1780int
1781freebsd32_msgctl(struct thread *td, struct freebsd32_msgctl_args *uap)
1782{
1783	struct msqid_ds msqbuf;
1784	struct msqid_ds32 msqbuf32;
1785	int error;
1786
1787	if (uap->cmd == IPC_SET) {
1788		error = copyin(uap->buf, &msqbuf32, sizeof(msqbuf32));
1789		if (error)
1790			return (error);
1791		freebsd32_ipcperm_in(&msqbuf32.msg_perm, &msqbuf.msg_perm);
1792		PTRIN_CP(msqbuf32, msqbuf, __msg_first);
1793		PTRIN_CP(msqbuf32, msqbuf, __msg_last);
1794		CP(msqbuf32, msqbuf, msg_cbytes);
1795		CP(msqbuf32, msqbuf, msg_qnum);
1796		CP(msqbuf32, msqbuf, msg_qbytes);
1797		CP(msqbuf32, msqbuf, msg_lspid);
1798		CP(msqbuf32, msqbuf, msg_lrpid);
1799		CP(msqbuf32, msqbuf, msg_stime);
1800		CP(msqbuf32, msqbuf, msg_rtime);
1801		CP(msqbuf32, msqbuf, msg_ctime);
1802	}
1803	error = kern_msgctl(td, uap->msqid, uap->cmd, &msqbuf);
1804	if (error)
1805		return (error);
1806	if (uap->cmd == IPC_STAT) {
1807		freebsd32_ipcperm_out(&msqbuf.msg_perm, &msqbuf32.msg_perm);
1808		PTROUT_CP(msqbuf, msqbuf32, __msg_first);
1809		PTROUT_CP(msqbuf, msqbuf32, __msg_last);
1810		CP(msqbuf, msqbuf32, msg_cbytes);
1811		CP(msqbuf, msqbuf32, msg_qnum);
1812		CP(msqbuf, msqbuf32, msg_qbytes);
1813		CP(msqbuf, msqbuf32, msg_lspid);
1814		CP(msqbuf, msqbuf32, msg_lrpid);
1815		CP(msqbuf, msqbuf32, msg_stime);
1816		CP(msqbuf, msqbuf32, msg_rtime);
1817		CP(msqbuf, msqbuf32, msg_ctime);
1818		error = copyout(&msqbuf32, uap->buf, sizeof(struct msqid_ds32));
1819	}
1820	return (error);
1821}
1822
1823int
1824freebsd32_msgsnd(struct thread *td, struct freebsd32_msgsnd_args *uap)
1825{
1826	const void *msgp;
1827	long mtype;
1828	int32_t mtype32;
1829	int error;
1830
1831	msgp = PTRIN(uap->msgp);
1832	if ((error = copyin(msgp, &mtype32, sizeof(mtype32))) != 0)
1833		return (error);
1834	mtype = mtype32;
1835	return (kern_msgsnd(td, uap->msqid,
1836	    (const char *)msgp + sizeof(mtype32),
1837	    uap->msgsz, uap->msgflg, mtype));
1838}
1839
1840int
1841freebsd32_msgrcv(struct thread *td, struct freebsd32_msgrcv_args *uap)
1842{
1843	void *msgp;
1844	long mtype;
1845	int32_t mtype32;
1846	int error;
1847
1848	msgp = PTRIN(uap->msgp);
1849	if ((error = kern_msgrcv(td, uap->msqid,
1850	    (char *)msgp + sizeof(mtype32), uap->msgsz,
1851	    uap->msgtyp, uap->msgflg, &mtype)) != 0)
1852		return (error);
1853	mtype32 = (int32_t)mtype;
1854	return (copyout(&mtype32, msgp, sizeof(mtype32)));
1855}
1856#endif
1857
1858#if defined(COMPAT_FREEBSD4) || defined(COMPAT_FREEBSD5) || \
1859    defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD7)
1860
1861/* XXX casting to (sy_call_t *) is bogus, as usual. */
1862static sy_call_t *msgcalls[] = {
1863	(sy_call_t *)freebsd7_msgctl, (sy_call_t *)sys_msgget,
1864	(sy_call_t *)sys_msgsnd, (sy_call_t *)sys_msgrcv
1865};
1866
1867/*
1868 * Entry point for all MSG calls.
1869 *
1870 * XXX actually varargs.
1871 * struct msgsys_args {
1872 *		int	which;
1873 *		int	a2;
1874 *		int	a3;
1875 *		int	a4;
1876 *		int	a5;
1877 *		int	a6;
1878 *	} *uap;
1879 */
1880int
1881sys_msgsys(struct thread *td, struct msgsys_args *uap)
1882{
1883	int error;
1884
1885	AUDIT_ARG_SVIPC_WHICH(uap->which);
1886	if (uap->which < 0 || uap->which >= nitems(msgcalls))
1887		return (EINVAL);
1888	error = (*msgcalls[uap->which])(td, &uap->a2);
1889	return (error);
1890}
1891
1892#ifndef CP
1893#define CP(src, dst, fld)	do { (dst).fld = (src).fld; } while (0)
1894#endif
1895
1896#ifndef _SYS_SYSPROTO_H_
1897struct freebsd7_msgctl_args {
1898	int	msqid;
1899	int	cmd;
1900	struct	msqid_ds_old *buf;
1901};
1902#endif
1903int
1904freebsd7_msgctl(struct thread *td, struct freebsd7_msgctl_args *uap)
1905{
1906	struct msqid_ds_old msqold;
1907	struct msqid_ds msqbuf;
1908	int error;
1909
1910	DPRINTF(("call to freebsd7_msgctl(%d, %d, %p)\n", uap->msqid, uap->cmd,
1911	    uap->buf));
1912	if (uap->cmd == IPC_SET) {
1913		error = copyin(uap->buf, &msqold, sizeof(msqold));
1914		if (error)
1915			return (error);
1916		ipcperm_old2new(&msqold.msg_perm, &msqbuf.msg_perm);
1917		CP(msqold, msqbuf, __msg_first);
1918		CP(msqold, msqbuf, __msg_last);
1919		CP(msqold, msqbuf, msg_cbytes);
1920		CP(msqold, msqbuf, msg_qnum);
1921		CP(msqold, msqbuf, msg_qbytes);
1922		CP(msqold, msqbuf, msg_lspid);
1923		CP(msqold, msqbuf, msg_lrpid);
1924		CP(msqold, msqbuf, msg_stime);
1925		CP(msqold, msqbuf, msg_rtime);
1926		CP(msqold, msqbuf, msg_ctime);
1927	}
1928	error = kern_msgctl(td, uap->msqid, uap->cmd, &msqbuf);
1929	if (error)
1930		return (error);
1931	if (uap->cmd == IPC_STAT) {
1932		bzero(&msqold, sizeof(msqold));
1933		ipcperm_new2old(&msqbuf.msg_perm, &msqold.msg_perm);
1934		CP(msqbuf, msqold, __msg_first);
1935		CP(msqbuf, msqold, __msg_last);
1936		CP(msqbuf, msqold, msg_cbytes);
1937		CP(msqbuf, msqold, msg_qnum);
1938		CP(msqbuf, msqold, msg_qbytes);
1939		CP(msqbuf, msqold, msg_lspid);
1940		CP(msqbuf, msqold, msg_lrpid);
1941		CP(msqbuf, msqold, msg_stime);
1942		CP(msqbuf, msqold, msg_rtime);
1943		CP(msqbuf, msqold, msg_ctime);
1944		error = copyout(&msqold, uap->buf, sizeof(struct msqid_ds_old));
1945	}
1946	return (error);
1947}
1948
1949#undef CP
1950
1951#endif	/* COMPAT_FREEBSD4 || COMPAT_FREEBSD5 || COMPAT_FREEBSD6 ||
1952	   COMPAT_FREEBSD7 */
1953