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