1/*
2 * Copyright (c) 2000-2004 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28/*
29 * @OSF_COPYRIGHT@
30 */
31/*
32 * Mach Operating System
33 * Copyright (c) 1991,1990,1989 Carnegie Mellon University
34 * All Rights Reserved.
35 *
36 * Permission to use, copy, modify and distribute this software and its
37 * documentation is hereby granted, provided that both the copyright
38 * notice and this permission notice appear in all copies of the
39 * software, derivative works or modified versions, and any portions
40 * thereof, and that both notices appear in supporting documentation.
41 *
42 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
43 * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
44 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
45 *
46 * Carnegie Mellon requests users of this software to return to
47 *
48 *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
49 *  School of Computer Science
50 *  Carnegie Mellon University
51 *  Pittsburgh PA 15213-3890
52 *
53 * any improvements or extensions that they make and grant Carnegie Mellon
54 * the rights to redistribute these changes.
55 */
56/*
57 */
58/*
59 *	File:	ipc/ipc_pset.c
60 *	Author:	Rich Draves
61 *	Date:	1989
62 *
63 *	Functions to manipulate IPC port sets.
64 */
65
66#include <mach/port.h>
67#include <mach/kern_return.h>
68#include <mach/message.h>
69#include <ipc/ipc_mqueue.h>
70#include <ipc/ipc_object.h>
71#include <ipc/ipc_pset.h>
72#include <ipc/ipc_right.h>
73#include <ipc/ipc_space.h>
74#include <ipc/ipc_port.h>
75
76#include <kern/kern_types.h>
77#include <kern/spl.h>
78
79#include <vm/vm_map.h>
80
81/*
82 *	Routine:	ipc_pset_alloc
83 *	Purpose:
84 *		Allocate a port set.
85 *	Conditions:
86 *		Nothing locked.  If successful, the port set is returned
87 *		locked.  (The caller doesn't have a reference.)
88 *	Returns:
89 *		KERN_SUCCESS		The port set is allocated.
90 *		KERN_INVALID_TASK	The space is dead.
91 *		KERN_NO_SPACE		No room for an entry in the space.
92 *		KERN_RESOURCE_SHORTAGE	Couldn't allocate memory.
93 */
94
95kern_return_t
96ipc_pset_alloc(
97	ipc_space_t		space,
98	mach_port_name_t	*namep,
99	ipc_pset_t		*psetp)
100{
101	ipc_pset_t pset;
102	mach_port_name_t name;
103	kern_return_t kr;
104
105	kr = ipc_object_alloc(space, IOT_PORT_SET,
106			      MACH_PORT_TYPE_PORT_SET, 0,
107			      &name, (ipc_object_t *) &pset);
108	if (kr != KERN_SUCCESS)
109		return kr;
110	/* pset and space are locked */
111
112	pset->ips_local_name = name;
113	ipc_mqueue_init(&pset->ips_messages, TRUE /* set */);
114	is_write_unlock(space);
115
116	*namep = name;
117	*psetp = pset;
118	return KERN_SUCCESS;
119}
120
121/*
122 *	Routine:	ipc_pset_alloc_name
123 *	Purpose:
124 *		Allocate a port set, with a specific name.
125 *	Conditions:
126 *		Nothing locked.  If successful, the port set is returned
127 *		locked.  (The caller doesn't have a reference.)
128 *	Returns:
129 *		KERN_SUCCESS		The port set is allocated.
130 *		KERN_INVALID_TASK	The space is dead.
131 *		KERN_NAME_EXISTS	The name already denotes a right.
132 *		KERN_RESOURCE_SHORTAGE	Couldn't allocate memory.
133 */
134
135kern_return_t
136ipc_pset_alloc_name(
137	ipc_space_t		space,
138	mach_port_name_t	name,
139	ipc_pset_t		*psetp)
140{
141	ipc_pset_t pset;
142	kern_return_t kr;
143
144
145	kr = ipc_object_alloc_name(space, IOT_PORT_SET,
146				   MACH_PORT_TYPE_PORT_SET, 0,
147				   name, (ipc_object_t *) &pset);
148	if (kr != KERN_SUCCESS)
149		return kr;
150	/* pset is locked */
151
152	pset->ips_local_name = name;
153	ipc_mqueue_init(&pset->ips_messages, TRUE /* set */);
154
155	*psetp = pset;
156	return KERN_SUCCESS;
157}
158
159/*
160 *	Routine:	ipc_pset_member
161 *	Purpose:
162 *		Checks to see if a port is a member of a pset
163 *	Conditions:
164 *		Both port and port set are locked.
165 *		The port must be active.
166 */
167boolean_t
168ipc_pset_member(
169	ipc_pset_t	pset,
170	ipc_port_t	port)
171{
172	assert(ip_active(port));
173
174	return (ipc_mqueue_member(&port->ip_messages, &pset->ips_messages));
175}
176
177
178/*
179 *	Routine:	ipc_pset_add
180 *	Purpose:
181 *		Puts a port into a port set.
182 *	Conditions:
183 *		Both port and port set are locked and active.
184 *		The owner of the port set is also receiver for the port.
185 */
186
187kern_return_t
188ipc_pset_add(
189	ipc_pset_t	  pset,
190	ipc_port_t	  port,
191	wait_queue_link_t wql)
192{
193	kern_return_t kr;
194
195	assert(ips_active(pset));
196	assert(ip_active(port));
197
198	kr = ipc_mqueue_add(&port->ip_messages, &pset->ips_messages, wql);
199
200	if (kr == KERN_SUCCESS)
201		port->ip_pset_count++;
202
203	return kr;
204}
205
206
207
208/*
209 *	Routine:	ipc_pset_remove
210 *	Purpose:
211 *		Removes a port from a port set.
212 *		The port set loses a reference.
213 *	Conditions:
214 *		Both port and port set are locked.
215 *		The port must be active.
216 */
217
218kern_return_t
219ipc_pset_remove(
220	ipc_pset_t	  pset,
221	ipc_port_t	  port,
222	wait_queue_link_t *wqlp)
223{
224	kern_return_t kr;
225
226	assert(ip_active(port));
227
228	if (port->ip_pset_count == 0)
229		return KERN_NOT_IN_SET;
230
231	kr = ipc_mqueue_remove(&port->ip_messages, &pset->ips_messages, wqlp);
232
233	if (kr == KERN_SUCCESS)
234		port->ip_pset_count--;
235
236	return kr;
237}
238
239/*
240 *	Routine:	ipc_pset_remove_from_all
241 *	Purpose:
242 *		Removes a port from all it's port sets.
243 *	Conditions:
244 *		port is locked and active.
245 */
246
247kern_return_t
248ipc_pset_remove_from_all(
249	ipc_port_t	port,
250	queue_t		links)
251{
252	assert(ip_active(port));
253
254	if (port->ip_pset_count == 0)
255		return KERN_NOT_IN_SET;
256
257	/*
258	 * Remove the port's mqueue from all sets
259	 */
260	ipc_mqueue_remove_from_all(&port->ip_messages, links);
261	port->ip_pset_count = 0;
262	return KERN_SUCCESS;
263}
264
265
266/*
267 *	Routine:	ipc_pset_destroy
268 *	Purpose:
269 *		Destroys a port_set.
270 *	Conditions:
271 *		The port_set is locked and alive.
272 *		The caller has a reference, which is consumed.
273 *		Afterwards, the port_set is unlocked and dead.
274 */
275
276void
277ipc_pset_destroy(
278	ipc_pset_t	pset)
279{
280	spl_t		s;
281	queue_head_t link_data;
282	queue_t links = &link_data;
283	wait_queue_link_t wql;
284
285	queue_init(links);
286
287	assert(ips_active(pset));
288
289	pset->ips_object.io_bits &= ~IO_BITS_ACTIVE;
290
291	/*
292	 * remove all the member message queues
293	 */
294	ipc_mqueue_remove_all(&pset->ips_messages, links);
295
296	/*
297	 * Set all waiters on the portset running to
298	 * discover the change.
299	 */
300	s = splsched();
301	imq_lock(&pset->ips_messages);
302	ipc_mqueue_changed(&pset->ips_messages);
303	imq_unlock(&pset->ips_messages);
304	splx(s);
305
306	ips_unlock(pset);
307	ips_release(pset);       /* consume the ref our caller gave us */
308
309	while(!queue_empty(links)) {
310		wql = (wait_queue_link_t) dequeue(links);
311		wait_queue_link_free(wql);
312	}
313
314}
315
316/* Kqueue EVFILT_MACHPORT support */
317
318#include <sys/errno.h>
319
320static int      filt_machportattach(struct knote *kn);
321static void	filt_machportdetach(struct knote *kn);
322static int	filt_machport(struct knote *kn, long hint);
323static void     filt_machporttouch(struct knote *kn, struct kevent64_s *kev, long type);
324static unsigned filt_machportpeek(struct knote *kn);
325struct filterops machport_filtops = {
326        .f_attach = filt_machportattach,
327        .f_detach = filt_machportdetach,
328        .f_event = filt_machport,
329        .f_touch = filt_machporttouch,
330	.f_peek = filt_machportpeek,
331};
332
333static int
334filt_machportattach(
335        struct knote *kn)
336{
337        mach_port_name_t        name = (mach_port_name_t)kn->kn_kevent.ident;
338	wait_queue_link_t	wql = wait_queue_link_allocate();
339        ipc_pset_t              pset = IPS_NULL;
340        int                     result = ENOSYS;
341        kern_return_t           kr;
342
343        kr = ipc_object_translate(current_space(), name,
344                                  MACH_PORT_RIGHT_PORT_SET,
345                                  (ipc_object_t *)&pset);
346        if (kr != KERN_SUCCESS) {
347		wait_queue_link_free(wql);
348                return (kr == KERN_INVALID_NAME ? ENOENT : ENOTSUP);
349        }
350        /* We've got a lock on pset */
351
352	/*
353	 * Bind the portset wait queue directly to knote/kqueue.
354	 * This allows us to just use wait_queue foo to effect a wakeup,
355	 * rather than having to call knote() from the Mach code on each
356	 * message.
357	 */
358	result = knote_link_wait_queue(kn, &pset->ips_messages.imq_wait_queue, wql);
359	if (result == 0) {
360		/* keep a reference for the knote */
361		kn->kn_ptr.p_pset = pset;
362		ips_reference(pset);
363		ips_unlock(pset);
364		return 0;
365	}
366
367	ips_unlock(pset);
368	wait_queue_link_free(wql);
369	return result;
370}
371
372static void
373filt_machportdetach(
374        struct knote *kn)
375{
376        ipc_pset_t              pset = kn->kn_ptr.p_pset;
377	wait_queue_link_t	wql = WAIT_QUEUE_LINK_NULL;
378
379	/*
380	 * Unlink the portset wait queue from knote/kqueue,
381	 * and release our reference on the portset.
382	 */
383	ips_lock(pset);
384	(void)knote_unlink_wait_queue(kn, &pset->ips_messages.imq_wait_queue, &wql);
385	kn->kn_ptr.p_pset = IPS_NULL;
386	ips_unlock(pset);
387	ips_release(pset);
388	if (wql != WAIT_QUEUE_LINK_NULL)
389		wait_queue_link_free(wql);
390}
391
392static int
393filt_machport(
394        struct knote *kn,
395        __unused long hint)
396{
397        mach_port_name_t        name = (mach_port_name_t)kn->kn_kevent.ident;
398        ipc_pset_t              pset = IPS_NULL;
399	wait_result_t		wresult;
400	thread_t		self = current_thread();
401        kern_return_t           kr;
402	mach_msg_option_t	option;
403	mach_msg_size_t		size;
404
405	/* never called from below */
406	assert(hint == 0);
407
408	/*
409	 * called from user context. Have to validate the
410	 * name.  If it changed, we have an EOF situation.
411	 */
412	kr = ipc_object_translate(current_space(), name,
413				  MACH_PORT_RIGHT_PORT_SET,
414				  (ipc_object_t *)&pset);
415	if (kr != KERN_SUCCESS || pset != kn->kn_ptr.p_pset || !ips_active(pset)) {
416		kn->kn_data = 0;
417		kn->kn_flags |= (EV_EOF | EV_ONESHOT);
418		if (pset != IPS_NULL) {
419			ips_unlock(pset);
420		}
421		return(1);
422        }
423
424	/* just use the reference from here on out */
425	ips_reference(pset);
426	ips_unlock(pset);
427
428	/*
429	 * Only honor supported receive options. If no options are
430	 * provided, just force a MACH_RCV_TOO_LARGE to detect the
431	 * name of the port and sizeof the waiting message.
432	 */
433	option = kn->kn_sfflags & (MACH_RCV_MSG|MACH_RCV_LARGE|MACH_RCV_LARGE_IDENTITY|
434				   MACH_RCV_TRAILER_MASK|MACH_RCV_VOUCHER);
435	if (option & MACH_RCV_MSG) {
436		self->ith_msg_addr = (mach_vm_address_t) kn->kn_ext[0];
437		size = (mach_msg_size_t)kn->kn_ext[1];
438	} else {
439		option = MACH_RCV_LARGE;
440		self->ith_msg_addr = 0;
441		size = 0;
442	}
443
444	/*
445	 * Set up to receive a message or the notification of a
446	 * too large message.  But never allow this call to wait.
447	 * If the user provided aditional options, like trailer
448	 * options, pass those through here.  But we don't support
449	 * scatter lists through this interface.
450	 */
451	self->ith_object = (ipc_object_t)pset;
452	self->ith_msize = size;
453	self->ith_option = option;
454	self->ith_scatter_list_size = 0;
455	self->ith_receiver_name = MACH_PORT_NULL;
456	self->ith_continuation = NULL;
457	option |= MACH_RCV_TIMEOUT; // never wait
458	self->ith_state = MACH_RCV_IN_PROGRESS;
459
460	wresult = ipc_mqueue_receive_on_thread(
461			&pset->ips_messages,
462			option,
463			size, /* max_size */
464			0, /* immediate timeout */
465			THREAD_INTERRUPTIBLE,
466			self);
467	assert(wresult == THREAD_NOT_WAITING);
468	assert(self->ith_state != MACH_RCV_IN_PROGRESS);
469
470	/*
471	 * If we timed out, just release the reference on the
472	 * portset and return zero.
473	 */
474	if (self->ith_state == MACH_RCV_TIMED_OUT) {
475		ips_release(pset);
476		return 0;
477	}
478
479	/*
480	 * If we weren't attempting to receive a message
481	 * directly, we need to return the port name in
482	 * the kevent structure.
483	 */
484	if ((option & MACH_RCV_MSG) != MACH_RCV_MSG) {
485		assert(self->ith_state == MACH_RCV_TOO_LARGE);
486		assert(self->ith_kmsg == IKM_NULL);
487		kn->kn_data = self->ith_receiver_name;
488		ips_release(pset);
489		return 1;
490	}
491
492	/*
493	 * Attempt to receive the message directly, returning
494	 * the results in the fflags field.
495	 */
496	assert(option & MACH_RCV_MSG);
497	kn->kn_ext[1] = self->ith_msize;
498	kn->kn_data = MACH_PORT_NULL;
499	kn->kn_fflags = mach_msg_receive_results();
500	/* kmsg and pset reference consumed */
501
502	/*
503	 * if the user asked for the identity of ports containing a
504	 * a too-large message, return it in the data field (as we
505	 * do for messages we didn't try to receive).
506	 */
507        if ((kn->kn_fflags == MACH_RCV_TOO_LARGE) &&
508	    (option & MACH_RCV_LARGE_IDENTITY))
509	    kn->kn_data = self->ith_receiver_name;
510
511	return 1;
512}
513
514static void
515filt_machporttouch(struct knote *kn, struct kevent64_s *kev, long type)
516{
517        switch (type) {
518        case EVENT_REGISTER:
519                kn->kn_sfflags = kev->fflags;
520                kn->kn_sdata = kev->data;
521		kn->kn_ext[0] = kev->ext[0];
522		kn->kn_ext[1] = kev->ext[1];
523                break;
524        case EVENT_PROCESS:
525                *kev = kn->kn_kevent;
526                if (kn->kn_flags & EV_CLEAR) {
527			kn->kn_data = 0;
528			kn->kn_fflags = 0;
529		}
530                break;
531        default:
532                panic("filt_machporttouch() - invalid type (%ld)", type);
533                break;
534        }
535}
536
537/*
538 * Peek to see if the portset associated with the knote has any
539 * events. This pre-hook is called when a filter uses the stay-
540 * on-queue mechanism (as the knote_link_wait_queue mechanism
541 * does).
542 *
543 * This is called with the kqueue that the knote belongs to still
544 * locked (thus holding a reference on the knote, but restricting
545 * also restricting our ability to take other locks).
546 *
547 * Just peek at the pre-post status of the portset's wait queue
548 * to determine if it has anything interesting.  We can do it
549 * without holding the lock, as it is just a snapshot in time
550 * (if this is used as part of really waiting for events, we
551 * will catch changes in this status when the event gets posted
552 * up to the knote's kqueue).
553 */
554static unsigned
555filt_machportpeek(struct knote *kn)
556{
557        ipc_pset_t              pset = kn->kn_ptr.p_pset;
558	ipc_mqueue_t		set_mq = &pset->ips_messages;
559
560	return (ipc_mqueue_set_peek(set_mq));
561}
562