#include <mach/port.h>
#include <mach/kern_return.h>
#include <mach/message.h>
#include <ipc/ipc_mqueue.h>
#include <ipc/ipc_object.h>
#include <ipc/ipc_pset.h>
#include <ipc/ipc_right.h>
#include <ipc/ipc_space.h>
#include <ipc/ipc_port.h>
#include <kern/kern_types.h>
#include <kern/spl.h>
#include <vm/vm_map.h>
kern_return_t
ipc_pset_alloc(
ipc_space_t space,
mach_port_name_t *namep,
ipc_pset_t *psetp)
{
ipc_pset_t pset;
mach_port_name_t name;
kern_return_t kr;
kr = ipc_object_alloc(space, IOT_PORT_SET,
MACH_PORT_TYPE_PORT_SET, 0,
&name, (ipc_object_t *) &pset);
if (kr != KERN_SUCCESS)
return kr;
pset->ips_local_name = name;
ipc_mqueue_init(&pset->ips_messages, TRUE );
is_write_unlock(space);
*namep = name;
*psetp = pset;
return KERN_SUCCESS;
}
kern_return_t
ipc_pset_alloc_name(
ipc_space_t space,
mach_port_name_t name,
ipc_pset_t *psetp)
{
ipc_pset_t pset;
kern_return_t kr;
kr = ipc_object_alloc_name(space, IOT_PORT_SET,
MACH_PORT_TYPE_PORT_SET, 0,
name, (ipc_object_t *) &pset);
if (kr != KERN_SUCCESS)
return kr;
pset->ips_local_name = name;
ipc_mqueue_init(&pset->ips_messages, TRUE );
*psetp = pset;
return KERN_SUCCESS;
}
boolean_t
ipc_pset_member(
ipc_pset_t pset,
ipc_port_t port)
{
assert(ip_active(port));
return (ipc_mqueue_member(&port->ip_messages, &pset->ips_messages));
}
kern_return_t
ipc_pset_add(
ipc_pset_t pset,
ipc_port_t port,
wait_queue_link_t wql)
{
kern_return_t kr;
assert(ips_active(pset));
assert(ip_active(port));
kr = ipc_mqueue_add(&port->ip_messages, &pset->ips_messages, wql);
if (kr == KERN_SUCCESS)
port->ip_pset_count++;
return kr;
}
kern_return_t
ipc_pset_remove(
ipc_pset_t pset,
ipc_port_t port,
wait_queue_link_t *wqlp)
{
kern_return_t kr;
assert(ip_active(port));
if (port->ip_pset_count == 0)
return KERN_NOT_IN_SET;
kr = ipc_mqueue_remove(&port->ip_messages, &pset->ips_messages, wqlp);
if (kr == KERN_SUCCESS)
port->ip_pset_count--;
return kr;
}
kern_return_t
ipc_pset_remove_from_all(
ipc_port_t port,
queue_t links)
{
assert(ip_active(port));
if (port->ip_pset_count == 0)
return KERN_NOT_IN_SET;
ipc_mqueue_remove_from_all(&port->ip_messages, links);
port->ip_pset_count = 0;
return KERN_SUCCESS;
}
void
ipc_pset_destroy(
ipc_pset_t pset)
{
spl_t s;
queue_head_t link_data;
queue_t links = &link_data;
wait_queue_link_t wql;
queue_init(links);
assert(ips_active(pset));
pset->ips_object.io_bits &= ~IO_BITS_ACTIVE;
ipc_mqueue_remove_all(&pset->ips_messages, links);
s = splsched();
imq_lock(&pset->ips_messages);
ipc_mqueue_changed(&pset->ips_messages);
imq_unlock(&pset->ips_messages);
splx(s);
ips_unlock(pset);
ips_release(pset);
while(!queue_empty(links)) {
wql = (wait_queue_link_t) dequeue(links);
wait_queue_link_free(wql);
}
}
#include <sys/errno.h>
static int filt_machportattach(struct knote *kn);
static void filt_machportdetach(struct knote *kn);
static int filt_machport(struct knote *kn, long hint);
static void filt_machporttouch(struct knote *kn, struct kevent64_s *kev, long type);
static unsigned filt_machportpeek(struct knote *kn);
struct filterops machport_filtops = {
.f_attach = filt_machportattach,
.f_detach = filt_machportdetach,
.f_event = filt_machport,
.f_touch = filt_machporttouch,
.f_peek = filt_machportpeek,
};
static int
filt_machportattach(
struct knote *kn)
{
mach_port_name_t name = (mach_port_name_t)kn->kn_kevent.ident;
wait_queue_link_t wql = wait_queue_link_allocate();
ipc_pset_t pset = IPS_NULL;
int result = ENOSYS;
kern_return_t kr;
kr = ipc_object_translate(current_space(), name,
MACH_PORT_RIGHT_PORT_SET,
(ipc_object_t *)&pset);
if (kr != KERN_SUCCESS) {
wait_queue_link_free(wql);
return (kr == KERN_INVALID_NAME ? ENOENT : ENOTSUP);
}
result = knote_link_wait_queue(kn, &pset->ips_messages.imq_wait_queue, wql);
if (result == 0) {
kn->kn_ptr.p_pset = pset;
ips_reference(pset);
ips_unlock(pset);
return 0;
}
ips_unlock(pset);
wait_queue_link_free(wql);
return result;
}
static void
filt_machportdetach(
struct knote *kn)
{
ipc_pset_t pset = kn->kn_ptr.p_pset;
wait_queue_link_t wql = WAIT_QUEUE_LINK_NULL;
ips_lock(pset);
(void)knote_unlink_wait_queue(kn, &pset->ips_messages.imq_wait_queue, &wql);
kn->kn_ptr.p_pset = IPS_NULL;
ips_unlock(pset);
ips_release(pset);
if (wql != WAIT_QUEUE_LINK_NULL)
wait_queue_link_free(wql);
}
static int
filt_machport(
struct knote *kn,
__unused long hint)
{
mach_port_name_t name = (mach_port_name_t)kn->kn_kevent.ident;
ipc_pset_t pset = IPS_NULL;
wait_result_t wresult;
thread_t self = current_thread();
kern_return_t kr;
mach_msg_option_t option;
mach_msg_size_t size;
assert(hint == 0);
kr = ipc_object_translate(current_space(), name,
MACH_PORT_RIGHT_PORT_SET,
(ipc_object_t *)&pset);
if (kr != KERN_SUCCESS || pset != kn->kn_ptr.p_pset || !ips_active(pset)) {
kn->kn_data = 0;
kn->kn_flags |= (EV_EOF | EV_ONESHOT);
if (pset != IPS_NULL) {
ips_unlock(pset);
}
return(1);
}
ips_reference(pset);
ips_unlock(pset);
option = kn->kn_sfflags & (MACH_RCV_MSG|MACH_RCV_LARGE|MACH_RCV_LARGE_IDENTITY|
MACH_RCV_TRAILER_MASK|MACH_RCV_VOUCHER);
if (option & MACH_RCV_MSG) {
self->ith_msg_addr = (mach_vm_address_t) kn->kn_ext[0];
size = (mach_msg_size_t)kn->kn_ext[1];
} else {
option = MACH_RCV_LARGE;
self->ith_msg_addr = 0;
size = 0;
}
self->ith_object = (ipc_object_t)pset;
self->ith_msize = size;
self->ith_option = option;
self->ith_scatter_list_size = 0;
self->ith_receiver_name = MACH_PORT_NULL;
self->ith_continuation = NULL;
option |= MACH_RCV_TIMEOUT; self->ith_state = MACH_RCV_IN_PROGRESS;
wresult = ipc_mqueue_receive_on_thread(
&pset->ips_messages,
option,
size,
0,
THREAD_INTERRUPTIBLE,
self);
assert(wresult == THREAD_NOT_WAITING);
assert(self->ith_state != MACH_RCV_IN_PROGRESS);
if (self->ith_state == MACH_RCV_TIMED_OUT) {
ips_release(pset);
return 0;
}
if ((option & MACH_RCV_MSG) != MACH_RCV_MSG) {
assert(self->ith_state == MACH_RCV_TOO_LARGE);
assert(self->ith_kmsg == IKM_NULL);
kn->kn_data = self->ith_receiver_name;
ips_release(pset);
return 1;
}
assert(option & MACH_RCV_MSG);
kn->kn_ext[1] = self->ith_msize;
kn->kn_data = MACH_PORT_NULL;
kn->kn_fflags = mach_msg_receive_results();
if ((kn->kn_fflags == MACH_RCV_TOO_LARGE) &&
(option & MACH_RCV_LARGE_IDENTITY))
kn->kn_data = self->ith_receiver_name;
return 1;
}
static void
filt_machporttouch(struct knote *kn, struct kevent64_s *kev, long type)
{
switch (type) {
case EVENT_REGISTER:
kn->kn_sfflags = kev->fflags;
kn->kn_sdata = kev->data;
kn->kn_ext[0] = kev->ext[0];
kn->kn_ext[1] = kev->ext[1];
break;
case EVENT_PROCESS:
*kev = kn->kn_kevent;
if (kn->kn_flags & EV_CLEAR) {
kn->kn_data = 0;
kn->kn_fflags = 0;
}
break;
default:
panic("filt_machporttouch() - invalid type (%ld)", type);
break;
}
}
static unsigned
filt_machportpeek(struct knote *kn)
{
ipc_pset_t pset = kn->kn_ptr.p_pset;
ipc_mqueue_t set_mq = &pset->ips_messages;
return (ipc_mqueue_set_peek(set_mq));
}