sysv_msg.c revision 205323
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 * Copyright (c) 2003-2005 McAfee, Inc.
21 * All rights reserved.
22 *
23 * This software was developed for the FreeBSD Project in part by McAfee
24 * Research, the Security Research Division of McAfee, Inc under DARPA/SPAWAR
25 * contract N66001-01-C-8035 ("CBOSS"), as part of the DARPA CHATS research
26 * program.
27 *
28 * Redistribution and use in source and binary forms, with or without
29 * modification, are permitted provided that the following conditions
30 * are met:
31 * 1. Redistributions of source code must retain the above copyright
32 *    notice, this list of conditions and the following disclaimer.
33 * 2. Redistributions in binary form must reproduce the above copyright
34 *    notice, this list of conditions and the following disclaimer in the
35 *    documentation and/or other materials provided with the distribution.
36 *
37 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
38 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
39 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
40 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
41 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
42 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
43 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
44 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
45 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
46 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
47 * SUCH DAMAGE.
48 */
49
50#include <sys/cdefs.h>
51__FBSDID("$FreeBSD: head/sys/kern/sysv_msg.c 205323 2010-03-19 11:04:42Z kib $");
52
53#include "opt_compat.h"
54#include "opt_sysvipc.h"
55
56#include <sys/param.h>
57#include <sys/systm.h>
58#include <sys/sysproto.h>
59#include <sys/kernel.h>
60#include <sys/priv.h>
61#include <sys/proc.h>
62#include <sys/lock.h>
63#include <sys/mutex.h>
64#include <sys/module.h>
65#include <sys/msg.h>
66#include <sys/syscall.h>
67#include <sys/syscallsubr.h>
68#include <sys/sysent.h>
69#include <sys/sysctl.h>
70#include <sys/malloc.h>
71#include <sys/jail.h>
72
73#include <security/mac/mac_framework.h>
74
75static MALLOC_DEFINE(M_MSG, "msg", "SVID compatible message queues");
76
77static int msginit(void);
78static int msgunload(void);
79static int sysvmsg_modload(struct module *, int, void *);
80
81#ifdef MSG_DEBUG
82#define DPRINTF(a)	printf a
83#else
84#define DPRINTF(a)	(void)0
85#endif
86
87static void msg_freehdr(struct msg *msghdr);
88
89#ifndef MSGSSZ
90#define MSGSSZ	8		/* Each segment must be 2^N long */
91#endif
92#ifndef MSGSEG
93#define MSGSEG	2048		/* must be less than 32767 */
94#endif
95#define MSGMAX	(MSGSSZ*MSGSEG)
96#ifndef MSGMNB
97#define MSGMNB	2048		/* max # of bytes in a queue */
98#endif
99#ifndef MSGMNI
100#define MSGMNI	40
101#endif
102#ifndef MSGTQL
103#define MSGTQL	40
104#endif
105
106/*
107 * Based on the configuration parameters described in an SVR2 (yes, two)
108 * config(1m) man page.
109 *
110 * Each message is broken up and stored in segments that are msgssz bytes
111 * long.  For efficiency reasons, this should be a power of two.  Also,
112 * it doesn't make sense if it is less than 8 or greater than about 256.
113 * Consequently, msginit in kern/sysv_msg.c checks that msgssz is a power of
114 * two between 8 and 1024 inclusive (and panic's if it isn't).
115 */
116struct msginfo msginfo = {
117                MSGMAX,         /* max chars in a message */
118                MSGMNI,         /* # of message queue identifiers */
119                MSGMNB,         /* max chars in a queue */
120                MSGTQL,         /* max messages in system */
121                MSGSSZ,         /* size of a message segment */
122                		/* (must be small power of 2 greater than 4) */
123                MSGSEG          /* number of message segments */
124};
125
126/*
127 * macros to convert between msqid_ds's and msqid's.
128 * (specific to this implementation)
129 */
130#define MSQID(ix,ds)	((ix) & 0xffff | (((ds).msg_perm.seq << 16) & 0xffff0000))
131#define MSQID_IX(id)	((id) & 0xffff)
132#define MSQID_SEQ(id)	(((id) >> 16) & 0xffff)
133
134/*
135 * The rest of this file is specific to this particular implementation.
136 */
137
138struct msgmap {
139	short	next;		/* next segment in buffer */
140    				/* -1 -> available */
141    				/* 0..(MSGSEG-1) -> index of next segment */
142};
143
144#define MSG_LOCKED	01000	/* Is this msqid_ds locked? */
145
146static int nfree_msgmaps;	/* # of free map entries */
147static short free_msgmaps;	/* head of linked list of free map entries */
148static struct msg *free_msghdrs;/* list of free msg headers */
149static char *msgpool;		/* MSGMAX byte long msg buffer pool */
150static struct msgmap *msgmaps;	/* MSGSEG msgmap structures */
151static struct msg *msghdrs;	/* MSGTQL msg headers */
152static struct msqid_kernel *msqids;	/* MSGMNI msqid_kernel struct's */
153static struct mtx msq_mtx;	/* global mutex for message queues. */
154
155static struct syscall_helper_data msg_syscalls[] = {
156	SYSCALL_INIT_HELPER(msgctl),
157	SYSCALL_INIT_HELPER(msgget),
158	SYSCALL_INIT_HELPER(msgsnd),
159	SYSCALL_INIT_HELPER(msgrcv),
160#if defined(COMPAT_FREEBSD4) || defined(COMPAT_FREEBSD5) || \
161    defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD7)
162	SYSCALL_INIT_HELPER(msgsys),
163	SYSCALL_INIT_HELPER(freebsd7_msgctl),
164#endif
165	SYSCALL_INIT_LAST
166};
167
168#ifdef COMPAT_FREEBSD32
169#include <compat/freebsd32/freebsd32.h>
170#include <compat/freebsd32/freebsd32_ipc.h>
171#include <compat/freebsd32/freebsd32_proto.h>
172#include <compat/freebsd32/freebsd32_signal.h>
173#include <compat/freebsd32/freebsd32_syscall.h>
174#include <compat/freebsd32/freebsd32_util.h>
175
176static struct syscall_helper_data msg32_syscalls[] = {
177	SYSCALL32_INIT_HELPER(freebsd32_msgctl),
178	SYSCALL32_INIT_HELPER(freebsd32_msgsnd),
179	SYSCALL32_INIT_HELPER(freebsd32_msgrcv),
180	SYSCALL32_INIT_HELPER(msgget),
181	SYSCALL32_INIT_HELPER(freebsd32_msgsys),
182#if defined(COMPAT_FREEBSD4) || defined(COMPAT_FREEBSD5) || \
183    defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD7)
184	SYSCALL32_INIT_HELPER(freebsd7_freebsd32_msgctl),
185#endif
186	SYSCALL_INIT_LAST
187};
188#endif
189
190static int
191msginit()
192{
193	int i, error;
194
195	TUNABLE_INT_FETCH("kern.ipc.msgseg", &msginfo.msgseg);
196	TUNABLE_INT_FETCH("kern.ipc.msgssz", &msginfo.msgssz);
197	msginfo.msgmax = msginfo.msgseg * msginfo.msgssz;
198	TUNABLE_INT_FETCH("kern.ipc.msgmni", &msginfo.msgmni);
199	TUNABLE_INT_FETCH("kern.ipc.msgmnb", &msginfo.msgmnb);
200	TUNABLE_INT_FETCH("kern.ipc.msgtql", &msginfo.msgtql);
201
202	msgpool = malloc(msginfo.msgmax, M_MSG, M_WAITOK);
203	if (msgpool == NULL)
204		panic("msgpool is NULL");
205	msgmaps = malloc(sizeof(struct msgmap) * msginfo.msgseg, M_MSG, M_WAITOK);
206	if (msgmaps == NULL)
207		panic("msgmaps is NULL");
208	msghdrs = malloc(sizeof(struct msg) * msginfo.msgtql, M_MSG, M_WAITOK);
209	if (msghdrs == NULL)
210		panic("msghdrs is NULL");
211	msqids = malloc(sizeof(struct msqid_kernel) * msginfo.msgmni, M_MSG,
212	    M_WAITOK);
213	if (msqids == NULL)
214		panic("msqids is NULL");
215
216	/*
217	 * msginfo.msgssz should be a power of two for efficiency reasons.
218	 * It is also pretty silly if msginfo.msgssz is less than 8
219	 * or greater than about 256 so ...
220	 */
221
222	i = 8;
223	while (i < 1024 && i != msginfo.msgssz)
224		i <<= 1;
225    	if (i != msginfo.msgssz) {
226		DPRINTF(("msginfo.msgssz=%d (0x%x)\n", msginfo.msgssz,
227		    msginfo.msgssz));
228		panic("msginfo.msgssz not a small power of 2");
229	}
230
231	if (msginfo.msgseg > 32767) {
232		DPRINTF(("msginfo.msgseg=%d\n", msginfo.msgseg));
233		panic("msginfo.msgseg > 32767");
234	}
235
236	if (msgmaps == NULL)
237		panic("msgmaps is NULL");
238
239	for (i = 0; i < msginfo.msgseg; i++) {
240		if (i > 0)
241			msgmaps[i-1].next = i;
242		msgmaps[i].next = -1;	/* implies entry is available */
243	}
244	free_msgmaps = 0;
245	nfree_msgmaps = msginfo.msgseg;
246
247	if (msghdrs == NULL)
248		panic("msghdrs is NULL");
249
250	for (i = 0; i < msginfo.msgtql; i++) {
251		msghdrs[i].msg_type = 0;
252		if (i > 0)
253			msghdrs[i-1].msg_next = &msghdrs[i];
254		msghdrs[i].msg_next = NULL;
255#ifdef MAC
256		mac_sysvmsg_init(&msghdrs[i]);
257#endif
258    	}
259	free_msghdrs = &msghdrs[0];
260
261	if (msqids == NULL)
262		panic("msqids is NULL");
263
264	for (i = 0; i < msginfo.msgmni; i++) {
265		msqids[i].u.msg_qbytes = 0;	/* implies entry is available */
266		msqids[i].u.msg_perm.seq = 0;	/* reset to a known value */
267		msqids[i].u.msg_perm.mode = 0;
268#ifdef MAC
269		mac_sysvmsq_init(&msqids[i]);
270#endif
271	}
272	mtx_init(&msq_mtx, "msq", NULL, MTX_DEF);
273
274	error = syscall_helper_register(msg_syscalls);
275	if (error != 0)
276		return (error);
277#ifdef COMPAT_FREEBSD32
278	error = syscall32_helper_register(msg32_syscalls);
279	if (error != 0)
280		return (error);
281#endif
282	return (0);
283}
284
285static int
286msgunload()
287{
288	struct msqid_kernel *msqkptr;
289	int msqid;
290#ifdef MAC
291	int i;
292#endif
293
294	syscall_helper_unregister(msg_syscalls);
295#ifdef COMPAT_FREEBSD32
296	syscall32_helper_unregister(msg32_syscalls);
297#endif
298
299	for (msqid = 0; msqid < msginfo.msgmni; msqid++) {
300		/*
301		 * Look for an unallocated and unlocked msqid_ds.
302		 * msqid_ds's can be locked by msgsnd or msgrcv while
303		 * they are copying the message in/out.  We can't
304		 * re-use the entry until they release it.
305		 */
306		msqkptr = &msqids[msqid];
307		if (msqkptr->u.msg_qbytes != 0 ||
308		    (msqkptr->u.msg_perm.mode & MSG_LOCKED) != 0)
309			break;
310	}
311	if (msqid != msginfo.msgmni)
312		return (EBUSY);
313
314#ifdef MAC
315	for (i = 0; i < msginfo.msgtql; i++)
316		mac_sysvmsg_destroy(&msghdrs[i]);
317	for (msqid = 0; msqid < msginfo.msgmni; msqid++)
318		mac_sysvmsq_destroy(&msqids[msqid]);
319#endif
320	free(msgpool, M_MSG);
321	free(msgmaps, M_MSG);
322	free(msghdrs, M_MSG);
323	free(msqids, M_MSG);
324	mtx_destroy(&msq_mtx);
325	return (0);
326}
327
328
329static int
330sysvmsg_modload(struct module *module, int cmd, void *arg)
331{
332	int error = 0;
333
334	switch (cmd) {
335	case MOD_LOAD:
336		error = msginit();
337		if (error != 0)
338			msgunload();
339		break;
340	case MOD_UNLOAD:
341		error = msgunload();
342		break;
343	case MOD_SHUTDOWN:
344		break;
345	default:
346		error = EINVAL;
347		break;
348	}
349	return (error);
350}
351
352static moduledata_t sysvmsg_mod = {
353	"sysvmsg",
354	&sysvmsg_modload,
355	NULL
356};
357
358DECLARE_MODULE(sysvmsg, sysvmsg_mod, SI_SUB_SYSV_MSG, SI_ORDER_FIRST);
359MODULE_VERSION(sysvmsg, 1);
360
361static void
362msg_freehdr(msghdr)
363	struct msg *msghdr;
364{
365	while (msghdr->msg_ts > 0) {
366		short next;
367		if (msghdr->msg_spot < 0 || msghdr->msg_spot >= msginfo.msgseg)
368			panic("msghdr->msg_spot out of range");
369		next = msgmaps[msghdr->msg_spot].next;
370		msgmaps[msghdr->msg_spot].next = free_msgmaps;
371		free_msgmaps = msghdr->msg_spot;
372		nfree_msgmaps++;
373		msghdr->msg_spot = next;
374		if (msghdr->msg_ts >= msginfo.msgssz)
375			msghdr->msg_ts -= msginfo.msgssz;
376		else
377			msghdr->msg_ts = 0;
378	}
379	if (msghdr->msg_spot != -1)
380		panic("msghdr->msg_spot != -1");
381	msghdr->msg_next = free_msghdrs;
382	free_msghdrs = msghdr;
383#ifdef MAC
384	mac_sysvmsg_cleanup(msghdr);
385#endif
386}
387
388#ifndef _SYS_SYSPROTO_H_
389struct msgctl_args {
390	int	msqid;
391	int	cmd;
392	struct	msqid_ds *buf;
393};
394#endif
395int
396msgctl(td, uap)
397	struct thread *td;
398	register struct msgctl_args *uap;
399{
400	int msqid = uap->msqid;
401	int cmd = uap->cmd;
402	struct msqid_ds msqbuf;
403	int error;
404
405	DPRINTF(("call to msgctl(%d, %d, %p)\n", msqid, cmd, uap->buf));
406	if (cmd == IPC_SET &&
407	    (error = copyin(uap->buf, &msqbuf, sizeof(msqbuf))) != 0)
408		return (error);
409	error = kern_msgctl(td, msqid, cmd, &msqbuf);
410	if (cmd == IPC_STAT && error == 0)
411		error = copyout(&msqbuf, uap->buf, sizeof(struct msqid_ds));
412	return (error);
413}
414
415int
416kern_msgctl(td, msqid, cmd, msqbuf)
417	struct thread *td;
418	int msqid;
419	int cmd;
420	struct msqid_ds *msqbuf;
421{
422	int rval, error, msqix;
423	register struct msqid_kernel *msqkptr;
424
425	if (!prison_allow(td->td_ucred, PR_ALLOW_SYSVIPC))
426		return (ENOSYS);
427
428	msqix = IPCID_TO_IX(msqid);
429
430	if (msqix < 0 || msqix >= msginfo.msgmni) {
431		DPRINTF(("msqid (%d) out of range (0<=msqid<%d)\n", msqix,
432		    msginfo.msgmni));
433		return (EINVAL);
434	}
435
436	msqkptr = &msqids[msqix];
437
438	mtx_lock(&msq_mtx);
439	if (msqkptr->u.msg_qbytes == 0) {
440		DPRINTF(("no such msqid\n"));
441		error = EINVAL;
442		goto done2;
443	}
444	if (msqkptr->u.msg_perm.seq != IPCID_TO_SEQ(msqid)) {
445		DPRINTF(("wrong sequence number\n"));
446		error = EINVAL;
447		goto done2;
448	}
449#ifdef MAC
450	error = mac_sysvmsq_check_msqctl(td->td_ucred, msqkptr, cmd);
451	if (error != 0)
452		goto done2;
453#endif
454
455	error = 0;
456	rval = 0;
457
458	switch (cmd) {
459
460	case IPC_RMID:
461	{
462		struct msg *msghdr;
463		if ((error = ipcperm(td, &msqkptr->u.msg_perm, IPC_M)))
464			goto done2;
465
466#ifdef MAC
467		/*
468		 * Check that the thread has MAC access permissions to
469		 * individual msghdrs.  Note: We need to do this in a
470		 * separate loop because the actual loop alters the
471		 * msq/msghdr info as it progresses, and there is no going
472		 * back if half the way through we discover that the
473		 * thread cannot free a certain msghdr.  The msq will get
474		 * into an inconsistent state.
475		 */
476		for (msghdr = msqkptr->u.msg_first; msghdr != NULL;
477		    msghdr = msghdr->msg_next) {
478			error = mac_sysvmsq_check_msgrmid(td->td_ucred, msghdr);
479			if (error != 0)
480				goto done2;
481		}
482#endif
483
484		/* Free the message headers */
485		msghdr = msqkptr->u.msg_first;
486		while (msghdr != NULL) {
487			struct msg *msghdr_tmp;
488
489			/* Free the segments of each message */
490			msqkptr->u.msg_cbytes -= msghdr->msg_ts;
491			msqkptr->u.msg_qnum--;
492			msghdr_tmp = msghdr;
493			msghdr = msghdr->msg_next;
494			msg_freehdr(msghdr_tmp);
495		}
496
497		if (msqkptr->u.msg_cbytes != 0)
498			panic("msg_cbytes is screwed up");
499		if (msqkptr->u.msg_qnum != 0)
500			panic("msg_qnum is screwed up");
501
502		msqkptr->u.msg_qbytes = 0;	/* Mark it as free */
503
504#ifdef MAC
505		mac_sysvmsq_cleanup(msqkptr);
506#endif
507
508		wakeup(msqkptr);
509	}
510
511		break;
512
513	case IPC_SET:
514		if ((error = ipcperm(td, &msqkptr->u.msg_perm, IPC_M)))
515			goto done2;
516		if (msqbuf->msg_qbytes > msqkptr->u.msg_qbytes) {
517			error = priv_check(td, PRIV_IPC_MSGSIZE);
518			if (error)
519				goto done2;
520		}
521		if (msqbuf->msg_qbytes > msginfo.msgmnb) {
522			DPRINTF(("can't increase msg_qbytes beyond %d"
523			    "(truncating)\n", msginfo.msgmnb));
524			msqbuf->msg_qbytes = msginfo.msgmnb;	/* silently restrict qbytes to system limit */
525		}
526		if (msqbuf->msg_qbytes == 0) {
527			DPRINTF(("can't reduce msg_qbytes to 0\n"));
528			error = EINVAL;		/* non-standard errno! */
529			goto done2;
530		}
531		msqkptr->u.msg_perm.uid = msqbuf->msg_perm.uid;	/* change the owner */
532		msqkptr->u.msg_perm.gid = msqbuf->msg_perm.gid;	/* change the owner */
533		msqkptr->u.msg_perm.mode = (msqkptr->u.msg_perm.mode & ~0777) |
534		    (msqbuf->msg_perm.mode & 0777);
535		msqkptr->u.msg_qbytes = msqbuf->msg_qbytes;
536		msqkptr->u.msg_ctime = time_second;
537		break;
538
539	case IPC_STAT:
540		if ((error = ipcperm(td, &msqkptr->u.msg_perm, IPC_R))) {
541			DPRINTF(("requester doesn't have read access\n"));
542			goto done2;
543		}
544		*msqbuf = msqkptr->u;
545		break;
546
547	default:
548		DPRINTF(("invalid command %d\n", cmd));
549		error = EINVAL;
550		goto done2;
551	}
552
553	if (error == 0)
554		td->td_retval[0] = rval;
555done2:
556	mtx_unlock(&msq_mtx);
557	return (error);
558}
559
560#ifndef _SYS_SYSPROTO_H_
561struct msgget_args {
562	key_t	key;
563	int	msgflg;
564};
565#endif
566int
567msgget(td, uap)
568	struct thread *td;
569	register struct msgget_args *uap;
570{
571	int msqid, error = 0;
572	int key = uap->key;
573	int msgflg = uap->msgflg;
574	struct ucred *cred = td->td_ucred;
575	register struct msqid_kernel *msqkptr = NULL;
576
577	DPRINTF(("msgget(0x%x, 0%o)\n", key, msgflg));
578
579	if (!prison_allow(td->td_ucred, PR_ALLOW_SYSVIPC))
580		return (ENOSYS);
581
582	mtx_lock(&msq_mtx);
583	if (key != IPC_PRIVATE) {
584		for (msqid = 0; msqid < msginfo.msgmni; msqid++) {
585			msqkptr = &msqids[msqid];
586			if (msqkptr->u.msg_qbytes != 0 &&
587			    msqkptr->u.msg_perm.key == key)
588				break;
589		}
590		if (msqid < msginfo.msgmni) {
591			DPRINTF(("found public key\n"));
592			if ((msgflg & IPC_CREAT) && (msgflg & IPC_EXCL)) {
593				DPRINTF(("not exclusive\n"));
594				error = EEXIST;
595				goto done2;
596			}
597			if ((error = ipcperm(td, &msqkptr->u.msg_perm,
598			    msgflg & 0700))) {
599				DPRINTF(("requester doesn't have 0%o access\n",
600				    msgflg & 0700));
601				goto done2;
602			}
603#ifdef MAC
604			error = mac_sysvmsq_check_msqget(cred, msqkptr);
605			if (error != 0)
606				goto done2;
607#endif
608			goto found;
609		}
610	}
611
612	DPRINTF(("need to allocate the msqid_ds\n"));
613	if (key == IPC_PRIVATE || (msgflg & IPC_CREAT)) {
614		for (msqid = 0; msqid < msginfo.msgmni; msqid++) {
615			/*
616			 * Look for an unallocated and unlocked msqid_ds.
617			 * msqid_ds's can be locked by msgsnd or msgrcv while
618			 * they are copying the message in/out.  We can't
619			 * re-use the entry until they release it.
620			 */
621			msqkptr = &msqids[msqid];
622			if (msqkptr->u.msg_qbytes == 0 &&
623			    (msqkptr->u.msg_perm.mode & MSG_LOCKED) == 0)
624				break;
625		}
626		if (msqid == msginfo.msgmni) {
627			DPRINTF(("no more msqid_ds's available\n"));
628			error = ENOSPC;
629			goto done2;
630		}
631		DPRINTF(("msqid %d is available\n", msqid));
632		msqkptr->u.msg_perm.key = key;
633		msqkptr->u.msg_perm.cuid = cred->cr_uid;
634		msqkptr->u.msg_perm.uid = cred->cr_uid;
635		msqkptr->u.msg_perm.cgid = cred->cr_gid;
636		msqkptr->u.msg_perm.gid = cred->cr_gid;
637		msqkptr->u.msg_perm.mode = (msgflg & 0777);
638		/* Make sure that the returned msqid is unique */
639		msqkptr->u.msg_perm.seq = (msqkptr->u.msg_perm.seq + 1) & 0x7fff;
640		msqkptr->u.msg_first = NULL;
641		msqkptr->u.msg_last = NULL;
642		msqkptr->u.msg_cbytes = 0;
643		msqkptr->u.msg_qnum = 0;
644		msqkptr->u.msg_qbytes = msginfo.msgmnb;
645		msqkptr->u.msg_lspid = 0;
646		msqkptr->u.msg_lrpid = 0;
647		msqkptr->u.msg_stime = 0;
648		msqkptr->u.msg_rtime = 0;
649		msqkptr->u.msg_ctime = time_second;
650#ifdef MAC
651		mac_sysvmsq_create(cred, msqkptr);
652#endif
653	} else {
654		DPRINTF(("didn't find it and wasn't asked to create it\n"));
655		error = ENOENT;
656		goto done2;
657	}
658
659found:
660	/* Construct the unique msqid */
661	td->td_retval[0] = IXSEQ_TO_IPCID(msqid, msqkptr->u.msg_perm);
662done2:
663	mtx_unlock(&msq_mtx);
664	return (error);
665}
666
667#ifndef _SYS_SYSPROTO_H_
668struct msgsnd_args {
669	int	msqid;
670	const void	*msgp;
671	size_t	msgsz;
672	int	msgflg;
673};
674#endif
675int
676kern_msgsnd(td, msqid, msgp, msgsz, msgflg, mtype)
677	struct thread *td;
678	int msqid;
679	const void *msgp;	/* XXX msgp is actually mtext. */
680	size_t msgsz;
681	int msgflg;
682	long mtype;
683{
684	int msqix, segs_needed, error = 0;
685	register struct msqid_kernel *msqkptr;
686	register struct msg *msghdr;
687	short next;
688
689	if (!prison_allow(td->td_ucred, PR_ALLOW_SYSVIPC))
690		return (ENOSYS);
691
692	mtx_lock(&msq_mtx);
693	msqix = IPCID_TO_IX(msqid);
694
695	if (msqix < 0 || msqix >= msginfo.msgmni) {
696		DPRINTF(("msqid (%d) out of range (0<=msqid<%d)\n", msqix,
697		    msginfo.msgmni));
698		error = EINVAL;
699		goto done2;
700	}
701
702	msqkptr = &msqids[msqix];
703	if (msqkptr->u.msg_qbytes == 0) {
704		DPRINTF(("no such message queue id\n"));
705		error = EINVAL;
706		goto done2;
707	}
708	if (msqkptr->u.msg_perm.seq != IPCID_TO_SEQ(msqid)) {
709		DPRINTF(("wrong sequence number\n"));
710		error = EINVAL;
711		goto done2;
712	}
713
714	if ((error = ipcperm(td, &msqkptr->u.msg_perm, IPC_W))) {
715		DPRINTF(("requester doesn't have write access\n"));
716		goto done2;
717	}
718
719#ifdef MAC
720	error = mac_sysvmsq_check_msqsnd(td->td_ucred, msqkptr);
721	if (error != 0)
722		goto done2;
723#endif
724
725	segs_needed = (msgsz + msginfo.msgssz - 1) / msginfo.msgssz;
726	DPRINTF(("msgsz=%zu, msgssz=%d, segs_needed=%d\n", msgsz,
727	    msginfo.msgssz, segs_needed));
728	for (;;) {
729		int need_more_resources = 0;
730
731		/*
732		 * check msgsz
733		 * (inside this loop in case msg_qbytes changes while we sleep)
734		 */
735
736		if (msgsz > msqkptr->u.msg_qbytes) {
737			DPRINTF(("msgsz > msqkptr->u.msg_qbytes\n"));
738			error = EINVAL;
739			goto done2;
740		}
741
742		if (msqkptr->u.msg_perm.mode & MSG_LOCKED) {
743			DPRINTF(("msqid is locked\n"));
744			need_more_resources = 1;
745		}
746		if (msgsz + msqkptr->u.msg_cbytes > msqkptr->u.msg_qbytes) {
747			DPRINTF(("msgsz + msg_cbytes > msg_qbytes\n"));
748			need_more_resources = 1;
749		}
750		if (segs_needed > nfree_msgmaps) {
751			DPRINTF(("segs_needed > nfree_msgmaps\n"));
752			need_more_resources = 1;
753		}
754		if (free_msghdrs == NULL) {
755			DPRINTF(("no more msghdrs\n"));
756			need_more_resources = 1;
757		}
758
759		if (need_more_resources) {
760			int we_own_it;
761
762			if ((msgflg & IPC_NOWAIT) != 0) {
763				DPRINTF(("need more resources but caller "
764				    "doesn't want to wait\n"));
765				error = EAGAIN;
766				goto done2;
767			}
768
769			if ((msqkptr->u.msg_perm.mode & MSG_LOCKED) != 0) {
770				DPRINTF(("we don't own the msqid_ds\n"));
771				we_own_it = 0;
772			} else {
773				/* Force later arrivals to wait for our
774				   request */
775				DPRINTF(("we own the msqid_ds\n"));
776				msqkptr->u.msg_perm.mode |= MSG_LOCKED;
777				we_own_it = 1;
778			}
779			DPRINTF(("msgsnd:  goodnight\n"));
780			error = msleep(msqkptr, &msq_mtx, (PZERO - 4) | PCATCH,
781			    "msgsnd", hz);
782			DPRINTF(("msgsnd:  good morning, error=%d\n", error));
783			if (we_own_it)
784				msqkptr->u.msg_perm.mode &= ~MSG_LOCKED;
785			if (error == EWOULDBLOCK) {
786				DPRINTF(("msgsnd:  timed out\n"));
787				continue;
788			}
789			if (error != 0) {
790				DPRINTF(("msgsnd:  interrupted system call\n"));
791				error = EINTR;
792				goto done2;
793			}
794
795			/*
796			 * Make sure that the msq queue still exists
797			 */
798
799			if (msqkptr->u.msg_qbytes == 0) {
800				DPRINTF(("msqid deleted\n"));
801				error = EIDRM;
802				goto done2;
803			}
804
805		} else {
806			DPRINTF(("got all the resources that we need\n"));
807			break;
808		}
809	}
810
811	/*
812	 * We have the resources that we need.
813	 * Make sure!
814	 */
815
816	if (msqkptr->u.msg_perm.mode & MSG_LOCKED)
817		panic("msg_perm.mode & MSG_LOCKED");
818	if (segs_needed > nfree_msgmaps)
819		panic("segs_needed > nfree_msgmaps");
820	if (msgsz + msqkptr->u.msg_cbytes > msqkptr->u.msg_qbytes)
821		panic("msgsz + msg_cbytes > msg_qbytes");
822	if (free_msghdrs == NULL)
823		panic("no more msghdrs");
824
825	/*
826	 * Re-lock the msqid_ds in case we page-fault when copying in the
827	 * message
828	 */
829
830	if ((msqkptr->u.msg_perm.mode & MSG_LOCKED) != 0)
831		panic("msqid_ds is already locked");
832	msqkptr->u.msg_perm.mode |= MSG_LOCKED;
833
834	/*
835	 * Allocate a message header
836	 */
837
838	msghdr = free_msghdrs;
839	free_msghdrs = msghdr->msg_next;
840	msghdr->msg_spot = -1;
841	msghdr->msg_ts = msgsz;
842	msghdr->msg_type = mtype;
843#ifdef MAC
844	/*
845	 * XXXMAC: Should the mac_sysvmsq_check_msgmsq check follow here
846	 * immediately?  Or, should it be checked just before the msg is
847	 * enqueued in the msgq (as it is done now)?
848	 */
849	mac_sysvmsg_create(td->td_ucred, msqkptr, msghdr);
850#endif
851
852	/*
853	 * Allocate space for the message
854	 */
855
856	while (segs_needed > 0) {
857		if (nfree_msgmaps <= 0)
858			panic("not enough msgmaps");
859		if (free_msgmaps == -1)
860			panic("nil free_msgmaps");
861		next = free_msgmaps;
862		if (next <= -1)
863			panic("next too low #1");
864		if (next >= msginfo.msgseg)
865			panic("next out of range #1");
866		DPRINTF(("allocating segment %d to message\n", next));
867		free_msgmaps = msgmaps[next].next;
868		nfree_msgmaps--;
869		msgmaps[next].next = msghdr->msg_spot;
870		msghdr->msg_spot = next;
871		segs_needed--;
872	}
873
874	/*
875	 * Validate the message type
876	 */
877
878	if (msghdr->msg_type < 1) {
879		msg_freehdr(msghdr);
880		msqkptr->u.msg_perm.mode &= ~MSG_LOCKED;
881		wakeup(msqkptr);
882		DPRINTF(("mtype (%ld) < 1\n", msghdr->msg_type));
883		error = EINVAL;
884		goto done2;
885	}
886
887	/*
888	 * Copy in the message body
889	 */
890
891	next = msghdr->msg_spot;
892	while (msgsz > 0) {
893		size_t tlen;
894		if (msgsz > msginfo.msgssz)
895			tlen = msginfo.msgssz;
896		else
897			tlen = msgsz;
898		if (next <= -1)
899			panic("next too low #2");
900		if (next >= msginfo.msgseg)
901			panic("next out of range #2");
902		mtx_unlock(&msq_mtx);
903		if ((error = copyin(msgp, &msgpool[next * msginfo.msgssz],
904		    tlen)) != 0) {
905			mtx_lock(&msq_mtx);
906			DPRINTF(("error %d copying in message segment\n",
907			    error));
908			msg_freehdr(msghdr);
909			msqkptr->u.msg_perm.mode &= ~MSG_LOCKED;
910			wakeup(msqkptr);
911			goto done2;
912		}
913		mtx_lock(&msq_mtx);
914		msgsz -= tlen;
915		msgp = (const char *)msgp + tlen;
916		next = msgmaps[next].next;
917	}
918	if (next != -1)
919		panic("didn't use all the msg segments");
920
921	/*
922	 * We've got the message.  Unlock the msqid_ds.
923	 */
924
925	msqkptr->u.msg_perm.mode &= ~MSG_LOCKED;
926
927	/*
928	 * Make sure that the msqid_ds is still allocated.
929	 */
930
931	if (msqkptr->u.msg_qbytes == 0) {
932		msg_freehdr(msghdr);
933		wakeup(msqkptr);
934		error = EIDRM;
935		goto done2;
936	}
937
938#ifdef MAC
939	/*
940	 * Note: Since the task/thread allocates the msghdr and usually
941	 * primes it with its own MAC label, for a majority of policies, it
942	 * won't be necessary to check whether the msghdr has access
943	 * permissions to the msgq.  The mac_sysvmsq_check_msqsnd check would
944	 * suffice in that case.  However, this hook may be required where
945	 * individual policies derive a non-identical label for the msghdr
946	 * from the current thread label and may want to check the msghdr
947	 * enqueue permissions, along with read/write permissions to the
948	 * msgq.
949	 */
950	error = mac_sysvmsq_check_msgmsq(td->td_ucred, msghdr, msqkptr);
951	if (error != 0) {
952		msg_freehdr(msghdr);
953		wakeup(msqkptr);
954		goto done2;
955	}
956#endif
957
958	/*
959	 * Put the message into the queue
960	 */
961	if (msqkptr->u.msg_first == NULL) {
962		msqkptr->u.msg_first = msghdr;
963		msqkptr->u.msg_last = msghdr;
964	} else {
965		msqkptr->u.msg_last->msg_next = msghdr;
966		msqkptr->u.msg_last = msghdr;
967	}
968	msqkptr->u.msg_last->msg_next = NULL;
969
970	msqkptr->u.msg_cbytes += msghdr->msg_ts;
971	msqkptr->u.msg_qnum++;
972	msqkptr->u.msg_lspid = td->td_proc->p_pid;
973	msqkptr->u.msg_stime = time_second;
974
975	wakeup(msqkptr);
976	td->td_retval[0] = 0;
977done2:
978	mtx_unlock(&msq_mtx);
979	return (error);
980}
981
982int
983msgsnd(td, uap)
984	struct thread *td;
985	register struct msgsnd_args *uap;
986{
987	int error;
988	long mtype;
989
990	DPRINTF(("call to msgsnd(%d, %p, %zu, %d)\n", uap->msqid, uap->msgp,
991	    uap->msgsz, uap->msgflg));
992
993	if ((error = copyin(uap->msgp, &mtype, sizeof(mtype))) != 0) {
994		DPRINTF(("error %d copying the message type\n", error));
995		return (error);
996	}
997	return (kern_msgsnd(td, uap->msqid,
998	    (const char *)uap->msgp + sizeof(mtype),
999	    uap->msgsz, uap->msgflg, mtype));
1000}
1001
1002#ifndef _SYS_SYSPROTO_H_
1003struct msgrcv_args {
1004	int	msqid;
1005	void	*msgp;
1006	size_t	msgsz;
1007	long	msgtyp;
1008	int	msgflg;
1009};
1010#endif
1011int
1012kern_msgrcv(td, msqid, msgp, msgsz, msgtyp, msgflg, mtype)
1013	struct thread *td;
1014	int msqid;
1015	void *msgp;	/* XXX msgp is actually mtext. */
1016	size_t msgsz;
1017	long msgtyp;
1018	int msgflg;
1019	long *mtype;
1020{
1021	size_t len;
1022	register struct msqid_kernel *msqkptr;
1023	register struct msg *msghdr;
1024	int msqix, error = 0;
1025	short next;
1026
1027	if (!prison_allow(td->td_ucred, PR_ALLOW_SYSVIPC))
1028		return (ENOSYS);
1029
1030	msqix = IPCID_TO_IX(msqid);
1031
1032	if (msqix < 0 || msqix >= msginfo.msgmni) {
1033		DPRINTF(("msqid (%d) out of range (0<=msqid<%d)\n", msqix,
1034		    msginfo.msgmni));
1035		return (EINVAL);
1036	}
1037
1038	msqkptr = &msqids[msqix];
1039	mtx_lock(&msq_mtx);
1040	if (msqkptr->u.msg_qbytes == 0) {
1041		DPRINTF(("no such message queue id\n"));
1042		error = EINVAL;
1043		goto done2;
1044	}
1045	if (msqkptr->u.msg_perm.seq != IPCID_TO_SEQ(msqid)) {
1046		DPRINTF(("wrong sequence number\n"));
1047		error = EINVAL;
1048		goto done2;
1049	}
1050
1051	if ((error = ipcperm(td, &msqkptr->u.msg_perm, IPC_R))) {
1052		DPRINTF(("requester doesn't have read access\n"));
1053		goto done2;
1054	}
1055
1056#ifdef MAC
1057	error = mac_sysvmsq_check_msqrcv(td->td_ucred, msqkptr);
1058	if (error != 0)
1059		goto done2;
1060#endif
1061
1062	msghdr = NULL;
1063	while (msghdr == NULL) {
1064		if (msgtyp == 0) {
1065			msghdr = msqkptr->u.msg_first;
1066			if (msghdr != NULL) {
1067				if (msgsz < msghdr->msg_ts &&
1068				    (msgflg & MSG_NOERROR) == 0) {
1069					DPRINTF(("first message on the queue "
1070					    "is too big (want %zu, got %d)\n",
1071					    msgsz, msghdr->msg_ts));
1072					error = E2BIG;
1073					goto done2;
1074				}
1075#ifdef MAC
1076				error = mac_sysvmsq_check_msgrcv(td->td_ucred,
1077				    msghdr);
1078				if (error != 0)
1079					goto done2;
1080#endif
1081				if (msqkptr->u.msg_first == msqkptr->u.msg_last) {
1082					msqkptr->u.msg_first = NULL;
1083					msqkptr->u.msg_last = NULL;
1084				} else {
1085					msqkptr->u.msg_first = msghdr->msg_next;
1086					if (msqkptr->u.msg_first == NULL)
1087						panic("msg_first/last screwed up #1");
1088				}
1089			}
1090		} else {
1091			struct msg *previous;
1092			struct msg **prev;
1093
1094			previous = NULL;
1095			prev = &(msqkptr->u.msg_first);
1096			while ((msghdr = *prev) != NULL) {
1097				/*
1098				 * Is this message's type an exact match or is
1099				 * this message's type less than or equal to
1100				 * the absolute value of a negative msgtyp?
1101				 * Note that the second half of this test can
1102				 * NEVER be true if msgtyp is positive since
1103				 * msg_type is always positive!
1104				 */
1105
1106				if (msgtyp == msghdr->msg_type ||
1107				    msghdr->msg_type <= -msgtyp) {
1108					DPRINTF(("found message type %ld, "
1109					    "requested %ld\n",
1110					    msghdr->msg_type, msgtyp));
1111					if (msgsz < msghdr->msg_ts &&
1112					    (msgflg & MSG_NOERROR) == 0) {
1113						DPRINTF(("requested message "
1114						    "on the queue is too big "
1115						    "(want %zu, got %hu)\n",
1116						    msgsz, msghdr->msg_ts));
1117						error = E2BIG;
1118						goto done2;
1119					}
1120#ifdef MAC
1121					error = mac_sysvmsq_check_msgrcv(
1122					    td->td_ucred, msghdr);
1123					if (error != 0)
1124						goto done2;
1125#endif
1126					*prev = msghdr->msg_next;
1127					if (msghdr == msqkptr->u.msg_last) {
1128						if (previous == NULL) {
1129							if (prev !=
1130							    &msqkptr->u.msg_first)
1131								panic("msg_first/last screwed up #2");
1132							msqkptr->u.msg_first =
1133							    NULL;
1134							msqkptr->u.msg_last =
1135							    NULL;
1136						} else {
1137							if (prev ==
1138							    &msqkptr->u.msg_first)
1139								panic("msg_first/last screwed up #3");
1140							msqkptr->u.msg_last =
1141							    previous;
1142						}
1143					}
1144					break;
1145				}
1146				previous = msghdr;
1147				prev = &(msghdr->msg_next);
1148			}
1149		}
1150
1151		/*
1152		 * We've either extracted the msghdr for the appropriate
1153		 * message or there isn't one.
1154		 * If there is one then bail out of this loop.
1155		 */
1156
1157		if (msghdr != NULL)
1158			break;
1159
1160		/*
1161		 * Hmph!  No message found.  Does the user want to wait?
1162		 */
1163
1164		if ((msgflg & IPC_NOWAIT) != 0) {
1165			DPRINTF(("no appropriate message found (msgtyp=%ld)\n",
1166			    msgtyp));
1167			/* The SVID says to return ENOMSG. */
1168			error = ENOMSG;
1169			goto done2;
1170		}
1171
1172		/*
1173		 * Wait for something to happen
1174		 */
1175
1176		DPRINTF(("msgrcv:  goodnight\n"));
1177		error = msleep(msqkptr, &msq_mtx, (PZERO - 4) | PCATCH,
1178		    "msgrcv", 0);
1179		DPRINTF(("msgrcv:  good morning (error=%d)\n", error));
1180
1181		if (error != 0) {
1182			DPRINTF(("msgrcv:  interrupted system call\n"));
1183			error = EINTR;
1184			goto done2;
1185		}
1186
1187		/*
1188		 * Make sure that the msq queue still exists
1189		 */
1190
1191		if (msqkptr->u.msg_qbytes == 0 ||
1192		    msqkptr->u.msg_perm.seq != IPCID_TO_SEQ(msqid)) {
1193			DPRINTF(("msqid deleted\n"));
1194			error = EIDRM;
1195			goto done2;
1196		}
1197	}
1198
1199	/*
1200	 * Return the message to the user.
1201	 *
1202	 * First, do the bookkeeping (before we risk being interrupted).
1203	 */
1204
1205	msqkptr->u.msg_cbytes -= msghdr->msg_ts;
1206	msqkptr->u.msg_qnum--;
1207	msqkptr->u.msg_lrpid = td->td_proc->p_pid;
1208	msqkptr->u.msg_rtime = time_second;
1209
1210	/*
1211	 * Make msgsz the actual amount that we'll be returning.
1212	 * Note that this effectively truncates the message if it is too long
1213	 * (since msgsz is never increased).
1214	 */
1215
1216	DPRINTF(("found a message, msgsz=%zu, msg_ts=%hu\n", msgsz,
1217	    msghdr->msg_ts));
1218	if (msgsz > msghdr->msg_ts)
1219		msgsz = msghdr->msg_ts;
1220	*mtype = msghdr->msg_type;
1221
1222	/*
1223	 * Return the segments to the user
1224	 */
1225
1226	next = msghdr->msg_spot;
1227	for (len = 0; len < msgsz; len += msginfo.msgssz) {
1228		size_t tlen;
1229
1230		if (msgsz - len > msginfo.msgssz)
1231			tlen = msginfo.msgssz;
1232		else
1233			tlen = msgsz - len;
1234		if (next <= -1)
1235			panic("next too low #3");
1236		if (next >= msginfo.msgseg)
1237			panic("next out of range #3");
1238		mtx_unlock(&msq_mtx);
1239		error = copyout(&msgpool[next * msginfo.msgssz], msgp, tlen);
1240		mtx_lock(&msq_mtx);
1241		if (error != 0) {
1242			DPRINTF(("error (%d) copying out message segment\n",
1243			    error));
1244			msg_freehdr(msghdr);
1245			wakeup(msqkptr);
1246			goto done2;
1247		}
1248		msgp = (char *)msgp + tlen;
1249		next = msgmaps[next].next;
1250	}
1251
1252	/*
1253	 * Done, return the actual number of bytes copied out.
1254	 */
1255
1256	msg_freehdr(msghdr);
1257	wakeup(msqkptr);
1258	td->td_retval[0] = msgsz;
1259done2:
1260	mtx_unlock(&msq_mtx);
1261	return (error);
1262}
1263
1264int
1265msgrcv(td, uap)
1266	struct thread *td;
1267	register struct msgrcv_args *uap;
1268{
1269	int error;
1270	long mtype;
1271
1272	DPRINTF(("call to msgrcv(%d, %p, %zu, %ld, %d)\n", uap->msqid,
1273	    uap->msgp, uap->msgsz, uap->msgtyp, uap->msgflg));
1274
1275	if ((error = kern_msgrcv(td, uap->msqid,
1276	    (char *)uap->msgp + sizeof(mtype), uap->msgsz,
1277	    uap->msgtyp, uap->msgflg, &mtype)) != 0)
1278		return (error);
1279	if ((error = copyout(&mtype, uap->msgp, sizeof(mtype))) != 0)
1280		DPRINTF(("error %d copying the message type\n", error));
1281	return (error);
1282}
1283
1284static int
1285sysctl_msqids(SYSCTL_HANDLER_ARGS)
1286{
1287
1288	return (SYSCTL_OUT(req, msqids,
1289	    sizeof(struct msqid_kernel) * msginfo.msgmni));
1290}
1291
1292SYSCTL_INT(_kern_ipc, OID_AUTO, msgmax, CTLFLAG_RD, &msginfo.msgmax, 0,
1293    "Maximum message size");
1294SYSCTL_INT(_kern_ipc, OID_AUTO, msgmni, CTLFLAG_RDTUN, &msginfo.msgmni, 0,
1295    "Number of message queue identifiers");
1296SYSCTL_INT(_kern_ipc, OID_AUTO, msgmnb, CTLFLAG_RDTUN, &msginfo.msgmnb, 0,
1297    "Maximum number of bytes in a queue");
1298SYSCTL_INT(_kern_ipc, OID_AUTO, msgtql, CTLFLAG_RDTUN, &msginfo.msgtql, 0,
1299    "Maximum number of messages in the system");
1300SYSCTL_INT(_kern_ipc, OID_AUTO, msgssz, CTLFLAG_RDTUN, &msginfo.msgssz, 0,
1301    "Size of a message segment");
1302SYSCTL_INT(_kern_ipc, OID_AUTO, msgseg, CTLFLAG_RDTUN, &msginfo.msgseg, 0,
1303    "Number of message segments");
1304SYSCTL_PROC(_kern_ipc, OID_AUTO, msqids, CTLFLAG_RD,
1305    NULL, 0, sysctl_msqids, "", "Message queue IDs");
1306
1307#ifdef COMPAT_FREEBSD32
1308int
1309freebsd32_msgsys(struct thread *td, struct freebsd32_msgsys_args *uap)
1310{
1311
1312#if defined(COMPAT_FREEBSD4) || defined(COMPAT_FREEBSD5) || \
1313    defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD7)
1314	switch (uap->which) {
1315	case 0:
1316		return (freebsd7_freebsd32_msgctl(td,
1317		    (struct freebsd7_freebsd32_msgctl_args *)&uap->a2));
1318	case 2:
1319		return (freebsd32_msgsnd(td,
1320		    (struct freebsd32_msgsnd_args *)&uap->a2));
1321	case 3:
1322		return (freebsd32_msgrcv(td,
1323		    (struct freebsd32_msgrcv_args *)&uap->a2));
1324	default:
1325		return (msgsys(td, (struct msgsys_args *)uap));
1326	}
1327#else
1328	return (nosys(td, NULL));
1329#endif
1330}
1331
1332#if defined(COMPAT_FREEBSD4) || defined(COMPAT_FREEBSD5) || \
1333    defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD7)
1334int
1335freebsd7_freebsd32_msgctl(struct thread *td,
1336    struct freebsd7_freebsd32_msgctl_args *uap)
1337{
1338	struct msqid_ds msqbuf;
1339	struct msqid_ds32_old msqbuf32;
1340	int error;
1341
1342	if (uap->cmd == IPC_SET) {
1343		error = copyin(uap->buf, &msqbuf32, sizeof(msqbuf32));
1344		if (error)
1345			return (error);
1346		freebsd32_ipcperm_old_in(&msqbuf32.msg_perm, &msqbuf.msg_perm);
1347		PTRIN_CP(msqbuf32, msqbuf, msg_first);
1348		PTRIN_CP(msqbuf32, msqbuf, msg_last);
1349		CP(msqbuf32, msqbuf, msg_cbytes);
1350		CP(msqbuf32, msqbuf, msg_qnum);
1351		CP(msqbuf32, msqbuf, msg_qbytes);
1352		CP(msqbuf32, msqbuf, msg_lspid);
1353		CP(msqbuf32, msqbuf, msg_lrpid);
1354		CP(msqbuf32, msqbuf, msg_stime);
1355		CP(msqbuf32, msqbuf, msg_rtime);
1356		CP(msqbuf32, msqbuf, msg_ctime);
1357	}
1358	error = kern_msgctl(td, uap->msqid, uap->cmd, &msqbuf);
1359	if (error)
1360		return (error);
1361	if (uap->cmd == IPC_STAT) {
1362		bzero(&msqbuf32, sizeof(msqbuf32));
1363		freebsd32_ipcperm_old_out(&msqbuf.msg_perm, &msqbuf32.msg_perm);
1364		PTROUT_CP(msqbuf, msqbuf32, msg_first);
1365		PTROUT_CP(msqbuf, msqbuf32, msg_last);
1366		CP(msqbuf, msqbuf32, msg_cbytes);
1367		CP(msqbuf, msqbuf32, msg_qnum);
1368		CP(msqbuf, msqbuf32, msg_qbytes);
1369		CP(msqbuf, msqbuf32, msg_lspid);
1370		CP(msqbuf, msqbuf32, msg_lrpid);
1371		CP(msqbuf, msqbuf32, msg_stime);
1372		CP(msqbuf, msqbuf32, msg_rtime);
1373		CP(msqbuf, msqbuf32, msg_ctime);
1374		error = copyout(&msqbuf32, uap->buf, sizeof(struct msqid_ds32));
1375	}
1376	return (error);
1377}
1378#endif
1379
1380int
1381freebsd32_msgctl(struct thread *td, struct freebsd32_msgctl_args *uap)
1382{
1383	struct msqid_ds msqbuf;
1384	struct msqid_ds32 msqbuf32;
1385	int error;
1386
1387	if (uap->cmd == IPC_SET) {
1388		error = copyin(uap->buf, &msqbuf32, sizeof(msqbuf32));
1389		if (error)
1390			return (error);
1391		freebsd32_ipcperm_in(&msqbuf32.msg_perm, &msqbuf.msg_perm);
1392		PTRIN_CP(msqbuf32, msqbuf, msg_first);
1393		PTRIN_CP(msqbuf32, msqbuf, msg_last);
1394		CP(msqbuf32, msqbuf, msg_cbytes);
1395		CP(msqbuf32, msqbuf, msg_qnum);
1396		CP(msqbuf32, msqbuf, msg_qbytes);
1397		CP(msqbuf32, msqbuf, msg_lspid);
1398		CP(msqbuf32, msqbuf, msg_lrpid);
1399		CP(msqbuf32, msqbuf, msg_stime);
1400		CP(msqbuf32, msqbuf, msg_rtime);
1401		CP(msqbuf32, msqbuf, msg_ctime);
1402	}
1403	error = kern_msgctl(td, uap->msqid, uap->cmd, &msqbuf);
1404	if (error)
1405		return (error);
1406	if (uap->cmd == IPC_STAT) {
1407		freebsd32_ipcperm_out(&msqbuf.msg_perm, &msqbuf32.msg_perm);
1408		PTROUT_CP(msqbuf, msqbuf32, msg_first);
1409		PTROUT_CP(msqbuf, msqbuf32, msg_last);
1410		CP(msqbuf, msqbuf32, msg_cbytes);
1411		CP(msqbuf, msqbuf32, msg_qnum);
1412		CP(msqbuf, msqbuf32, msg_qbytes);
1413		CP(msqbuf, msqbuf32, msg_lspid);
1414		CP(msqbuf, msqbuf32, msg_lrpid);
1415		CP(msqbuf, msqbuf32, msg_stime);
1416		CP(msqbuf, msqbuf32, msg_rtime);
1417		CP(msqbuf, msqbuf32, msg_ctime);
1418		error = copyout(&msqbuf32, uap->buf, sizeof(struct msqid_ds32));
1419	}
1420	return (error);
1421}
1422
1423int
1424freebsd32_msgsnd(struct thread *td, struct freebsd32_msgsnd_args *uap)
1425{
1426	const void *msgp;
1427	long mtype;
1428	int32_t mtype32;
1429	int error;
1430
1431	msgp = PTRIN(uap->msgp);
1432	if ((error = copyin(msgp, &mtype32, sizeof(mtype32))) != 0)
1433		return (error);
1434	mtype = mtype32;
1435	return (kern_msgsnd(td, uap->msqid,
1436	    (const char *)msgp + sizeof(mtype32),
1437	    uap->msgsz, uap->msgflg, mtype));
1438}
1439
1440int
1441freebsd32_msgrcv(struct thread *td, struct freebsd32_msgrcv_args *uap)
1442{
1443	void *msgp;
1444	long mtype;
1445	int32_t mtype32;
1446	int error;
1447
1448	msgp = PTRIN(uap->msgp);
1449	if ((error = kern_msgrcv(td, uap->msqid,
1450	    (char *)msgp + sizeof(mtype32), uap->msgsz,
1451	    uap->msgtyp, uap->msgflg, &mtype)) != 0)
1452		return (error);
1453	mtype32 = (int32_t)mtype;
1454	return (copyout(&mtype32, msgp, sizeof(mtype32)));
1455}
1456#endif
1457
1458#if defined(COMPAT_FREEBSD4) || defined(COMPAT_FREEBSD5) || \
1459    defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD7)
1460
1461/* XXX casting to (sy_call_t *) is bogus, as usual. */
1462static sy_call_t *msgcalls[] = {
1463	(sy_call_t *)freebsd7_msgctl, (sy_call_t *)msgget,
1464	(sy_call_t *)msgsnd, (sy_call_t *)msgrcv
1465};
1466
1467/*
1468 * Entry point for all MSG calls.
1469 */
1470int
1471msgsys(td, uap)
1472	struct thread *td;
1473	/* XXX actually varargs. */
1474	struct msgsys_args /* {
1475		int	which;
1476		int	a2;
1477		int	a3;
1478		int	a4;
1479		int	a5;
1480		int	a6;
1481	} */ *uap;
1482{
1483	int error;
1484
1485	if (!prison_allow(td->td_ucred, PR_ALLOW_SYSVIPC))
1486		return (ENOSYS);
1487	if (uap->which < 0 ||
1488	    uap->which >= sizeof(msgcalls)/sizeof(msgcalls[0]))
1489		return (EINVAL);
1490	error = (*msgcalls[uap->which])(td, &uap->a2);
1491	return (error);
1492}
1493
1494#ifndef CP
1495#define CP(src, dst, fld)	do { (dst).fld = (src).fld; } while (0)
1496#endif
1497
1498#ifndef _SYS_SYSPROTO_H_
1499struct freebsd7_msgctl_args {
1500	int	msqid;
1501	int	cmd;
1502	struct	msqid_ds_old *buf;
1503};
1504#endif
1505int
1506freebsd7_msgctl(td, uap)
1507	struct thread *td;
1508	struct freebsd7_msgctl_args *uap;
1509{
1510	struct msqid_ds_old msqold;
1511	struct msqid_ds msqbuf;
1512	int error;
1513
1514	DPRINTF(("call to freebsd7_msgctl(%d, %d, %p)\n", uap->msqid, uap->cmd,
1515	    uap->buf));
1516	if (uap->cmd == IPC_SET) {
1517		error = copyin(uap->buf, &msqold, sizeof(msqold));
1518		if (error)
1519			return (error);
1520		ipcperm_old2new(&msqold.msg_perm, &msqbuf.msg_perm);
1521		CP(msqold, msqbuf, msg_first);
1522		CP(msqold, msqbuf, msg_last);
1523		CP(msqold, msqbuf, msg_cbytes);
1524		CP(msqold, msqbuf, msg_qnum);
1525		CP(msqold, msqbuf, msg_qbytes);
1526		CP(msqold, msqbuf, msg_lspid);
1527		CP(msqold, msqbuf, msg_lrpid);
1528		CP(msqold, msqbuf, msg_stime);
1529		CP(msqold, msqbuf, msg_rtime);
1530		CP(msqold, msqbuf, msg_ctime);
1531	}
1532	error = kern_msgctl(td, uap->msqid, uap->cmd, &msqbuf);
1533	if (error)
1534		return (error);
1535	if (uap->cmd == IPC_STAT) {
1536		bzero(&msqold, sizeof(msqold));
1537		ipcperm_new2old(&msqbuf.msg_perm, &msqold.msg_perm);
1538		CP(msqbuf, msqold, msg_first);
1539		CP(msqbuf, msqold, msg_last);
1540		CP(msqbuf, msqold, msg_cbytes);
1541		CP(msqbuf, msqold, msg_qnum);
1542		CP(msqbuf, msqold, msg_qbytes);
1543		CP(msqbuf, msqold, msg_lspid);
1544		CP(msqbuf, msqold, msg_lrpid);
1545		CP(msqbuf, msqold, msg_stime);
1546		CP(msqbuf, msqold, msg_rtime);
1547		CP(msqbuf, msqold, msg_ctime);
1548		error = copyout(&msqold, uap->buf, sizeof(struct msqid_ds_old));
1549	}
1550	return (error);
1551}
1552
1553#undef CP
1554
1555#endif	/* COMPAT_FREEBSD4 || COMPAT_FREEBSD5 || COMPAT_FREEBSD6 ||
1556	   COMPAT_FREEBSD7 */
1557