notify_client.c   [plain text]


/*
 * Copyright (c) 2003-2012 Apple Inc. All rights reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 *
 * This file contains Original Code and/or Modifications of Original Code
 * as defined in and that are subject to the Apple Public Source License
 * Version 2.0 (the 'License'). You may not use this file except in
 * compliance with the License. Please obtain a copy of the License at
 * http://www.opensource.apple.com/apsl/ and read it before using this
 * file.
 *
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 * Please see the License for the specific language governing rights and
 * limitations under the License.
 *
 * @APPLE_LICENSE_HEADER_END@
 */

#include <assert.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/un.h>
#include <sys/ipc.h>
#include <sys/signal.h>
#include <sys/syslimits.h>
#include <mach/mach.h>
#include <mach/mach_time.h>
#include <sys/mman.h>
#include <sys/fcntl.h>
#include <sys/time.h>
#include <bootstrap_priv.h>
#include <errno.h>
#include <pthread.h>
#include <TargetConditionals.h>
#include <AvailabilityMacros.h>
#include <Block.h>
#include <dispatch/dispatch.h>
#include <dispatch/private.h>
#include <_simple.h>

#include "libnotify.h"

#include "notify.h"
#include "notify_internal.h"
#include "notify_ipc.h"
#include "notify_private.h"

#ifdef DEBUG
#define DEBUG_REGISTRATION		0x00000001
#define DEBUG_NOTIFICATION		0x00000002
#define DEBUG_RETAIN_RELEASE	0x00000004
#define DEBUG_CANCEL			0x00000008
#define DEBUG_GET_STATE			0x00000010
#define DEBUG_SEND_NO_BLOCK		0x00000020
#define DEBUG_NODES				0x00000040
#define DEBUG_API				0x00000080
#define DEBUG_USER				0x80000000
#define DEBUG_ALL				0xffffffff
static uint32_t _libnotify_debug = DEBUG_ALL;
#endif /* DEBUG */

#define INITIAL_TOKEN_ID 0

// <rdar://problem/10385540>
WEAK_IMPORT_ATTRIBUTE bool _dispatch_is_multithreaded(void);

#define EVENT_INIT       0
#define EVENT_REGEN      1

#define SELF_PREFIX "self."
#define SELF_PREFIX_LEN 5

#define COMMON_SELF_PORT_KEY "self.com.apple.system.notify.common"

#define MULTIPLE_REGISTRATION_WARNING_TRIGGER 20

extern uint32_t _notify_lib_peek(notify_state_t *ns, pid_t pid, int token, int *val);
extern int *_notify_lib_check_addr(notify_state_t *ns, pid_t pid, int token);

#define CLIENT_NAME_TABLE_SIZE 256

#define NID_UNSET 0xffffffffffffffffL
#define NID_CALLED_ONCE 0xfffffffffffffffeL

/*
 * Details about registrations, tokens, dispatch (NOTIFY_OPT_DISPATCH), IPC versions, and etc.
 *
 * In the first versions of the client/server protocol (ipc versions 0 and 1), the integer
 * token representing a registration was generated by notifyd and returned to the client
 * as an out parameter in the MIG registration call.
 *
 * If a client uses the old protocols, then each registration in the client goes straight to
 * the server.  If the client registers for signals, file descriptor writes, or whatever,
 * that's all handled by the server.
 *
 * The current version (ipc version 2) has better performance.  The client now generates
 * a unique token (simply incremented by 1 for each new registration) and sends it to
 * notifyd in a MIG simpleroutine.  notifyd internally uses a 64 bit ID for each registration
 * that's formed from the client's PID (high-order 32 bits) and the client-provided integer
 * (low-order 32 bits).
 *
 * With the advent of libdispatch, there was an opportunity to further improve performance
 * and reduce the load on notifyd.  The client library (this source file) checks if the
 * client process is multithreaded, or at least can be multithreaded, with a call to the
 * _dispatch_is_multithreaded() routine.  This tells us if we can use dispatch in the client.
 *
 * If the client can use dispatch (NOTIFY_OPT_DISPATCH gets set in globals->client_opts),
 * then all registrations use a single shared mach port (globals->notify_common_port).
 * The client creates a dispatch source (globals->notify_dispatch_source) that handles all
 * notifications from the server.  The handler routine looks up the client's registration
 * for the received notification and sends a signal, writes on a file descriptor, or sends
 * a mach message as required.  It's all done locally in the client process.
 *
 * Many clients register several times for the same notification name.  That's sometimes
 * due to bad code, but it may be legitimate as well.  For example, different libraries or
 * frameworks may register independently, or different threads in a client process may
 * each require a registration for the same name.  When dispatch is available, client
 * registrations for the same name are coalesced.  The library still generates a new
 * token (and an underlying token data structure) for each registration, but only the
 * first registration is actually sent to notifyd.  Subsequent registrations are simply
 * chained to the first in a linked list.  When the globals->notify_dispatch_source
 * handler processes a notification from the server, it traverses the linked list and
 * forwards the notification to each.
 *
 * Note that it is possible for the client to still have multiple registrations with
 * notifyd, even when coalescing.  Polled registrations (memory or plain) must be handled
 * by notifyd, so these registration types are not coalesced.  Also, a client might start
 * out single threaded and appear not to be dispatch-safe, but them become multi-threaded
 * later on.  Early registrations would go individually to notifyd, while later
 * registrations would be coalesced.
 *
 * The library uses both locking and refcounting of data structures.  A mutex in the
 * library globals (globals->notify_lock) is used when accessing mutable values, hash
 * tables, and lists that are found in the global data (returned by _notify_globals()).
 * Data structure instances (name_node_t and registration_node_t types) are refcounted.
 * name_node_t instances have locks to protect their data.  registration_node_t
 * instances do not, mostly to save memory.  Most operations on them can either be done
 * atomically, or are within the scope of some other lock.
 */

typedef struct
{
	pthread_mutex_t lock;
	int32_t refcount;
	uint32_t coalesce_base_token;
	char *name;
	size_t name_len;
	uint64_t name_id;
	list_t *coalesced;
	struct __registration_node_s *coalesce_base;
} name_node_t;

/*
 * Data structure behind a client's token.
 * The library exports tokens (integers) to users of the library, so
 * notify_register_...() gives the client an int value that represents
 * a registration.
 *
 * If the client registers multiple times for the same name and we can
 * use dispatch in the library, then the duplicate registration creates
 * a new client-side registration, but not a new registration with the
 * server.  Multiple registrations are chained in a linked list.  The
 * base registration is retained for each coalesced registration.
 */
typedef struct __registration_node_s
{
	int32_t refcount;
	uint32_t token;
	uint32_t flags;

	/* shared memory slot and value at that location when we last did notify_check() */
	uint32_t slot;
	uint32_t val;

	/* client-facing parts of a notification */
	int fd;
	int signal;
	mach_port_t mp;
	dispatch_queue_t queue;
	notify_handler_t block;

	/* client_id is the value returned from notifyd for registrations using IPC version 0 */
	uint32_t client_id;

	/* state value and timestamp when we set it - used to regenerate if notifyd restarts */
	uint64_t set_state_val;
	uint64_t set_state_time;

	/* path monitoring */
	char *path;
	int path_flags;

	/* name table node for this registration */
	name_node_t *name_node;
} registration_node_t;


/* FORWARD */
static void _notify_lib_regenerate(int src);
static void notify_retain_mach_port(mach_port_t mp, int mine);
static void _notify_dispatch_handle(mach_port_t port);
static notify_state_t *_notify_lib_self_state();
static void registration_node_release(registration_node_t *r);
static void notify_release_file_descriptor(int fd);
static void notify_release_mach_port(mach_port_t mp, uint32_t flags);
static registration_node_t *registration_node_retain(registration_node_t *r);
uint32_t notify_register_plain(const char *name, int *out_token);

#ifdef DEBUG_MUTEX
#define mutex_lock(s,x,f,l) \
_notify_client_log(ASL_LEVEL_NOTICE, "attempting mutex lock %s %p from %s:%u", s, x, f, l); \
pthread_mutex_lock(x); \
_notify_client_log(ASL_LEVEL_NOTICE, "acquired mutex lock %s %p from %s:%u", s, x, f, l);
#define mutex_unlock(s,x,f,l) \
_notify_client_log(ASL_LEVEL_NOTICE, "dropping mutex lock %s %p from %s:%u", s, x, f, l); \
pthread_mutex_unlock(x);
#else
#define mutex_lock(s,x,f,l) pthread_mutex_lock(x)
#define mutex_unlock(s,x,f,l) pthread_mutex_unlock(x)
#endif

static void
_notify_client_log(int level, const char *fmt, ...)
{
	va_list ap;
	char *msg = NULL;

	va_start(ap, fmt);
	vasprintf(&msg, fmt, ap);
	va_end(ap);

	if (msg != NULL)
	{
		_simple_asl_log(level, "com.apple.notify", msg);
		//		fprintf(stderr, "%s\n", msg);
	}

	free(msg);
}

#pragma mark -
#pragma mark name_node_t

#ifdef DEBUG
static void
name_node_dump(int level, name_node_t *n)
{
	if (n == NULL)
	{
		_notify_client_log(level, "name_node_t NULL\n");
		return;
	}

	_notify_client_log(level, "name_node_t %p name=%s name_id=%llu refcount=%d coalesce_base_token=%u coalesce_base=%p coalesce_list_count=%u\n", (n->name == NULL) ? "NULL" : n->name, n->name_id, n->refcount, n->coalesce_base_token, n->coalesce_base, _nc_list_count(n->coalesced));
}
#endif

static name_node_t *
name_node_for_name(const char *name, uint64_t nid, bool create, bool glock)
{
	if (name == NULL) return NULL;

	notify_globals_t globals = _notify_globals();

	if (glock)
	{
		mutex_lock("global", &globals->notify_lock, __func__, __LINE__);
	}

	name_node_t *n = (name_node_t *)_nc_table_find(globals->name_table, name);
	if (n != NULL)
	{
		OSAtomicIncrement32(&n->refcount);
	}
	else if (create)
	{
		n = (name_node_t *)calloc(1, sizeof(name_node_t));
		if (n == NULL)
		{
#ifdef DEBUG
			_notify_client_log(ASL_LEVEL_ERR, "name_node_for_name name %s calloc failed errno %d [%s]\n", name, errno, strerror(errno));
#endif
			goto unlock_and_return;
		}

		n->name = strdup(name);
		if (n->name == NULL)
		{
			free(n);
			n = NULL;
			goto unlock_and_return;
		}

		n->refcount = 1;
		n->name_len = strlen(name);
		n->name_id = nid;
		n->coalesce_base_token = NOTIFY_TOKEN_INVALID;
		pthread_mutex_init(&n->lock, NULL);

		_nc_table_insert_no_copy(globals->name_table, n->name, n);
	}

unlock_and_return:

	if (glock)
	{
		mutex_unlock("global", &globals->notify_lock, __func__, __LINE__);
	}

#ifdef DEBUG
	if (_libnotify_debug & DEBUG_NODES)
	{
		if (n == NULL) _notify_client_log(ASL_LEVEL_NOTICE, "name_node_for_name name %s returning NULL\n", name);
		else _notify_client_log(ASL_LEVEL_NOTICE, "name_node_for_name name %s refcount %d %p\n", n->name, n->refcount, n);
	}
#endif

	return n;
}

/* unused */
#ifdef NOTDEF
static name_node_t *
name_node_retain(name_node_t *n)
{
	if (n != NULL) OSAtomicIncrement32(&n->refcount);
	return n;
}
#endif

static void
name_node_release(name_node_t *n)
{
	if (n == NULL) return;

	/* global lock required if we free the name node */
	notify_globals_t globals = _notify_globals();
	mutex_lock("global", &globals->notify_lock, __func__, __LINE__);

	mutex_lock(n->name, &n->lock, __func__, __LINE__);

	if (OSAtomicDecrement32(&n->refcount) > 0)
	{
#ifdef DEBUG
		if (_libnotify_debug & DEBUG_NODES) _notify_client_log(ASL_LEVEL_NOTICE, "name_node_release name %s refcount %d %p", n->name, n->refcount, n);
#endif
		mutex_unlock(n->name, &n->lock, __func__, __LINE__);
		mutex_unlock("global", &globals->notify_lock, __func__, __LINE__);
		return;
	}

	/* refcount is zero, free the node */
#ifdef DEBUG
	if (_libnotify_debug & DEBUG_NODES) _notify_client_log(ASL_LEVEL_NOTICE, "name_node_release name %s refcount %d %p FREE", n->name, n->refcount, n);
#endif

	_nc_table_delete(globals->name_table, n->name);

	mutex_unlock(n->name, &n->lock, __func__, __LINE__);
	mutex_unlock("global", &globals->notify_lock, __func__, __LINE__);

	free(n->name);
	n->name = NULL;
	free(n);
}

static void
name_node_add_coalesced_registration(name_node_t *n, registration_node_t *r)
{
	if (n == NULL) return;
	if (r == NULL) return;

	mutex_lock(n->name, &n->lock, __func__, __LINE__);
	registration_node_retain(n->coalesce_base);
	list_t *l = _nc_list_new(r);
	n->coalesced = _nc_list_prepend(n->coalesced, l);
	mutex_unlock(n->name, &n->lock, __func__, __LINE__);
}

static void
name_node_remove_coalesced_registration(name_node_t *n, registration_node_t *r)
{
	if (n == NULL) return;
	if (r == NULL) return;

	mutex_lock(n->name, &n->lock, __func__, __LINE__);
	n->coalesced = _nc_list_delete(n->coalesced, r);
	mutex_unlock(n->name, &n->lock, __func__, __LINE__);

	registration_node_release(n->coalesce_base);
}

static void
name_node_set_nid(name_node_t *n, uint64_t nid)
{
	if (n == NULL) return;

	mutex_lock(n->name, &n->lock, __func__, __LINE__);
	n->name_id = nid;
	mutex_unlock(n->name, &n->lock, __func__, __LINE__);
}

#pragma mark -
#pragma mark registration_node_t

static registration_node_t *
registration_node_find(uint32_t token)
{
	notify_globals_t globals = _notify_globals();

	mutex_lock("global", &globals->notify_lock, __func__, __LINE__);
	registration_node_t *r = (registration_node_t *)_nc_table_find_n(globals->registration_table, token);
	if (r != NULL) OSAtomicIncrement32(&r->refcount);
	mutex_unlock("global", &globals->notify_lock, __func__, __LINE__);

#ifdef DEBUG
	if (_libnotify_debug & DEBUG_NODES) _notify_client_log(ASL_LEVEL_NOTICE, "registration_node_find token %u refcount %d -> %p", token, r->refcount, r);
#endif

	return r;
}

/*
 * Tells the caller if a value is a valid token number.
 * Internal coalesce_base registration tokens are reported as invalid,
 * since clients should not be mucking around with them.
 */
bool
notify_is_valid_token(int val)
{
#ifdef DEBUG
	if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "-> %s\n", __func__);
#endif
	bool valid = true;
	notify_globals_t globals = _notify_globals();

	mutex_lock("global", &globals->notify_lock, __func__, __LINE__);
	registration_node_t *r = (registration_node_t *)_nc_table_find_n(globals->registration_table, val);
	if (r == NULL) valid = false;
	else if (r->flags & NOTIFY_TYPE_COALESCE_BASE) valid = false;
	mutex_unlock("global", &globals->notify_lock, __func__, __LINE__);

#ifdef DEBUG
	if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
	return valid;
}

static registration_node_t *
registration_node_retain(registration_node_t *r)
{
	if (r != NULL) OSAtomicIncrement32(&r->refcount);
	return r;
}

static void
registration_node_free(registration_node_t *r)
{
	notify_globals_t globals = _notify_globals();
	name_node_t *n = r->name_node;

	if (r->flags & NOTIFY_TYPE_COALESCED)
	{
		name_node_remove_coalesced_registration(n, r);
	}
	else if (r->flags & NOTIFY_TYPE_COALESCE_BASE)
	{
		mutex_lock(n->name, &n->lock, __func__, __LINE__);
		n->coalesce_base_token = NOTIFY_TOKEN_INVALID;
		n->coalesce_base = NULL;
		mutex_unlock(n->name, &n->lock, __func__, __LINE__);

		/* cancel the registration with notifyd */
#ifdef DEBUG
		if (_libnotify_debug & DEBUG_CANCEL) _notify_client_log(ASL_LEVEL_NOTICE, "_notify_server_cancel_2 token %u", r->token);
#endif
		(void)_notify_server_cancel_2(globals->notify_server_port, r->token);
	}

	notify_release_file_descriptor(r->fd);
	notify_release_mach_port(r->mp, r->flags);
	free(r->path);

	if (r->block != NULL) dispatch_async_f(r->queue, r->block, (dispatch_function_t)_Block_release);
	r->block = NULL;

	if (r->queue != NULL) dispatch_release(r->queue);
	r->queue = NULL;

	free(r);

	name_node_release(n);
}

static void
registration_node_release(registration_node_t *r)
{
	if (r == NULL) return;

	notify_globals_t globals = _notify_globals();
	mutex_lock("global", &globals->notify_lock, __func__, __LINE__);

	if (OSAtomicDecrement32(&r->refcount) > 0)
	{
#ifdef DEBUG
		if (_libnotify_debug & DEBUG_NODES) _notify_client_log(ASL_LEVEL_NOTICE, "registration_node_release token %u refcount %d flags 0x%08x %p", r->token, r->refcount, r->flags, r);
#endif

		mutex_unlock("global", &globals->notify_lock, __func__, __LINE__);
		return;
	}

	/* refcount is zero, free the node */

#ifdef DEBUG
	if (_libnotify_debug & DEBUG_NODES) _notify_client_log(ASL_LEVEL_NOTICE, "registration_node_release token %u refcount %d flags 0x%08x %p FREE", r->token, r->refcount, r->flags, r);
#endif

	_nc_table_delete_n(globals->registration_table, r->token);

	mutex_unlock("global", &globals->notify_lock, __func__, __LINE__);

	registration_node_free(r);
}

#pragma mark -
#if TARGET_IPHONE_SIMULATOR
const char *
_notify_shm_id()
{
	static dispatch_once_t once;
	static char *shm_id;

	dispatch_once(&once, ^{
		/*
		 * According to documentation, our shm_id must be no more than 31 characters long
		 * but in practice, even 31 characters is too long (<rdar://problem/16860882>),
		 * so we jump through some hoops to make a smaller string based on our UDID.
		 */
		const char *udid = getenv("SIMULATOR_UDID");
		if (udid && strlen(udid) == 36) {
			char scratch[34]; // 32 characters, 2 NUL

			/* 01234567890123456789012345678901234567890 */
			/* UUUUUUUU-UUUU-UUUU-LLLL-LLLLLLLLLLLL */
			memcpy(scratch, udid, 8);
			memcpy(scratch+8, udid+9, 4);
			memcpy(scratch+12, udid+14, 4);
			scratch[16] = '\0';

			memcpy(scratch+17, udid+19, 4);
			memcpy(scratch+21, udid+24, 12);
			scratch[33] = '\0';

			/*
			 * If the input is invalid, these will end up being undefined
			 * values, but they'll still be values we can use.
			 */
			uint64_t upper = strtoull(scratch, NULL, 16);
			uint64_t lower = strtoull(scratch + 17, NULL, 16);

			const char *c64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
			scratch[0]  = c64[(upper >> 57) & 0xf];
			scratch[1]  = c64[(upper >> 50) & 0xf];
			scratch[2]  = c64[(upper >> 43) & 0xf];
			scratch[3]  = c64[(upper >> 36) & 0xf];
			scratch[4]  = c64[(upper >> 29) & 0xf];
			scratch[5]  = c64[(upper >> 22) & 0xf];
			scratch[6]  = c64[(upper >> 15) & 0xf];
			scratch[7]  = c64[(upper >>  8) & 0xf];
			scratch[8]  = c64[(upper >>  1) & 0xf];
			/* Drop a bit on the floor, but that probably doesn't matter.  It does not need to be reversible */

			scratch[10] = c64[(lower >> 57) & 0xf];
			scratch[11] = c64[(lower >> 50) & 0xf];
			scratch[12] = c64[(lower >> 43) & 0xf];
			scratch[13] = c64[(lower >> 36) & 0xf];
			scratch[14] = c64[(lower >> 29) & 0xf];
			scratch[15] = c64[(lower >> 22) & 0xf];
			scratch[16] = c64[(lower >> 15) & 0xf];
			scratch[17] = c64[(lower >>  8) & 0xf];
			scratch[18] = c64[(lower >>  1) & 0xf];
			/* Drop a bit on the floor, but that probably doesn't matter.  It does not need to be reversible */

			scratch[19] = '\0';

			asprintf(&shm_id, "sim.not.%s", scratch);
			assert(shm_id);
		}

		if (shm_id == NULL)
		{
			shm_id = "apple.shm.notification_center";
		}
	});

	return shm_id;
}
#endif

static int
shm_attach(uint32_t size)
{
	int32_t shmfd;
	notify_globals_t globals = _notify_globals();

	shmfd = shm_open(SHM_ID, O_RDONLY, 0);
	if (shmfd == -1) return -1;

	globals->shm_base = mmap(NULL, size, PROT_READ, MAP_SHARED, shmfd, 0);
	close(shmfd);

	if (globals->shm_base == (uint32_t *)-1) globals->shm_base = NULL;
	if (globals->shm_base == NULL) return -1;

	return 0;
}

#ifdef NOTDEF
static void
shm_detach(void)
{
	if (shm_base != NULL)
	{
		shmdt(shm_base);
		shm_base = NULL;
	}
}
#endif

/*
 * Initialization of global variables. Called once per process.
 */
void
_notify_init_globals(void * /* notify_globals_t */ _globals)
{
	notify_globals_t globals = _globals;

	pthread_mutex_init(&globals->notify_lock, NULL);
	globals->token_id = INITIAL_TOKEN_ID;
	globals->notify_common_token = -1;
	globals->name_table = _nc_table_new(CLIENT_NAME_TABLE_SIZE);
	globals->registration_table = _nc_table_new(CLIENT_NAME_TABLE_SIZE);
	globals->checkLock = OS_SPINLOCK_INIT;
}

#if !_NOTIFY_HAS_ALLOC_ONCE
notify_globals_t
_notify_globals_impl(void)
{
	static dispatch_once_t once;
	static notify_globals_t globals;
	dispatch_once(&once, ^{
		globals = calloc(1, sizeof(struct notify_globals_s));
		_notify_init_globals(globals);
	});
	return globals;
}
#endif

/*
 * _notify_lib_init is called for each new registration (event = EVENT_INIT).
 * It is also called to re-initialize when the library has detected that
 * notifyd has restarted (event = EVENT_REGEN).
 */
static uint32_t
_notify_lib_init(uint32_t event)
{
	__block kern_return_t kstatus;
	__block bool first = false;
	int status, cid;
	uint64_t state;

	notify_globals_t globals = _notify_globals();

	/* notifyd sets NOTIFY_OPT_DISABLE to avoid re-entrancy issues */
	if (globals->client_opts & NOTIFY_OPT_DISABLE) return NOTIFY_STATUS_FAILED;

	/* Look up the notifyd server port just once. */
	kstatus = KERN_SUCCESS;
	dispatch_once(&globals->notify_server_port_once, ^{
		first = true;
		kstatus = bootstrap_look_up2(bootstrap_port, NOTIFY_SERVICE_NAME, &globals->notify_server_port, 0, BOOTSTRAP_PRIVILEGED_SERVER);
	});

	if (kstatus != KERN_SUCCESS) return NOTIFY_STATUS_FAILED;

	mutex_lock("global", &globals->notify_lock, __func__, __LINE__);

	/*
	 * _dispatch_is_multithreaded() tells us if it is safe to use dispatch queues for
	 * a shared port for all registrations (NOTIFY_OPT_DISPATCH), and to watch for notifyd
	 * exiting / restarting (NOTIFY_OPT_REGEN).
	 *
	 * Note that _dispatch_is_multithreaded is weak imported, <rdar://problem/10385540>
	 */
	if (_dispatch_is_multithreaded)
	{
		if (_dispatch_is_multithreaded()) globals->client_opts |= (NOTIFY_OPT_DISPATCH | NOTIFY_OPT_REGEN);
	}

	/*
	 * Look up the server's PID and supported IPC version on the first call,
	 * and on a regeneration event (when the server has restarted).
	 */
	if (first || (event == EVENT_REGEN))
	{
		pid_t last_pid = globals->notify_server_pid;

		globals->notify_ipc_version = 0;
		globals->notify_server_pid = 0;

		kstatus = _notify_server_register_plain(globals->notify_server_port, NOTIFY_IPC_VERSION_NAME, NOTIFY_IPC_VERSION_NAME_LEN, &cid, &status);
		if ((kstatus == KERN_SUCCESS) && (status == NOTIFY_STATUS_OK))
		{
			kstatus = _notify_server_get_state(globals->notify_server_port, cid, &state, &status);
			if ((kstatus == KERN_SUCCESS) && (status == NOTIFY_STATUS_OK))
			{
				globals->notify_ipc_version = (int32_t)state;
				state >>= 32;
				globals->notify_server_pid = (int32_t)state;
			}

			_notify_server_cancel(globals->notify_server_port, cid, &status);

			if ((last_pid == globals->notify_server_pid) && (event == EVENT_REGEN))
			{
				mutex_unlock("global", &globals->notify_lock, __func__, __LINE__);
				return NOTIFY_STATUS_INVALID_REQUEST;
			}
		}

		if (globals->server_proc_source != NULL)
		{
			dispatch_source_cancel(globals->server_proc_source);
			dispatch_release(globals->server_proc_source);
			globals->server_proc_source = NULL;
		}
	}

	if (globals->notify_ipc_version < 2)
	{
		/* regen is not supported below version 2 */
		globals->client_opts &= ~NOTIFY_OPT_REGEN;
	}

	/*
	 * Create a source (DISPATCH_SOURCE_TYPE_PROC) to invoke _notify_lib_regenerate if notifyd restarts.
	 * Available in IPC version 2.
	 */
	if ((globals->server_proc_source == NULL) && (globals->client_opts & NOTIFY_OPT_REGEN) && (globals->notify_server_pid != 0))
	{
		globals->server_proc_source = dispatch_source_create(DISPATCH_SOURCE_TYPE_PROC, (uintptr_t)globals->notify_server_pid, DISPATCH_PROC_EXIT, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0));
		dispatch_source_set_event_handler(globals->server_proc_source, ^{ _notify_lib_regenerate(1); });
		dispatch_resume(globals->server_proc_source);
	}

	/*
	 * Create the shared multiplex port if NOTIFY_OPT_DISPATCH is set.
	 */
	if ((globals->client_opts & NOTIFY_OPT_DISPATCH) && (globals->notify_common_port == MACH_PORT_NULL))
	{
		kern_return_t kr;
		task_t task = mach_task_self();

		kr = mach_port_allocate(task, MACH_PORT_RIGHT_RECEIVE, &globals->notify_common_port);
		if (kr == KERN_SUCCESS)
		{
			globals->notify_dispatch_source = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV, globals->notify_common_port, 0, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0));
			dispatch_source_set_event_handler(globals->notify_dispatch_source, ^{
				notify_globals_t globals = _notify_globals();
				_notify_dispatch_handle(globals->notify_common_port);
			});
			dispatch_source_set_cancel_handler(globals->notify_dispatch_source, ^{
				task_t task = mach_task_self();
				notify_globals_t globals = _notify_globals();
				mach_port_mod_refs(task, globals->notify_common_port, MACH_PORT_RIGHT_RECEIVE, -1);
			});
			dispatch_resume(globals->notify_dispatch_source);
		}
	}

	mutex_unlock("global", &globals->notify_lock, __func__, __LINE__);

	if (globals->notify_common_port != MACH_PORT_NULL && (first || event == EVENT_REGEN))
	{
		/* register the common port with notifyd */
		status = notify_register_mach_port(COMMON_PORT_KEY, &globals->notify_common_port, NOTIFY_REUSE | NOTIFY_NO_DISPATCH, &globals->notify_common_token);
	}

	return NOTIFY_STATUS_OK;
}

/* Reset all internal state at fork */
void
_notify_fork_child(void)
{
	notify_globals_t globals = _notify_globals();

	_notify_init_globals(globals);

	/*
	 * Expressly disable notify in the child side of a fork if it had
	 * been initialized in the parent. Using notify in the child process
	 * can lead to deadlock (see <rdar://problem/11498014>).
	 *
	 * Also disable notify in the forked child of a multi-threaded parent that
	 * used dispatch, since notify will use dispatch, and that will blow up.
	 * It's OK to make that check here by calling _dispatch_is_multithreaded(),
	 * since we will actually be looking at the parent's state.
	 */
	if (globals->notify_server_port != MACH_PORT_NULL) globals->client_opts = NOTIFY_OPT_DISABLE;
	if (_dispatch_is_multithreaded) // weak imported symbol
	{
		if (_dispatch_is_multithreaded()) globals->client_opts = NOTIFY_OPT_DISABLE;
	}

	globals->self_state = NULL;
	globals->notify_server_port = MACH_PORT_NULL;
	globals->notify_ipc_version = 0;
	globals->notify_server_pid = 0;

	globals->name_table = NULL;
	globals->registration_table = NULL;

	globals->fd_count = 0;
	globals->fd_clnt = NULL;
	globals->fd_srv = NULL;
	globals->fd_refcount = NULL;

	globals->mp_count = 0;
	globals->mp_list = NULL;
	globals->mp_refcount = NULL;
	globals->mp_mine = NULL;

	globals->shm_base = NULL;
}

/*
 * Create the registration structure associated with a client's token.
 * Will create a name_node_t structure for the name if one does not exist.
 * Note that this routine is NOT used to create the "base" for non-polled
 * coalesced registrations.  That's done by base_registration_create().
 */
static int
client_registration_create(const char *name, size_t namelen, uint64_t nid, uint32_t token, uint32_t cid, uint32_t slot, uint32_t flags, int sig, int fd, mach_port_t mp)
{
	name_node_t *name_node = NULL;
	registration_node_t *reg_node;
	uint32_t warn_count = 0;
	notify_globals_t globals = _notify_globals();

	if (globals->name_table == NULL) return -1;
	if (globals->registration_table == NULL) return -1;
	if (name == NULL) return -1;

	mutex_lock("global", &globals->notify_lock, __func__, __LINE__);

	/* should never happen, but check if the registration exists */
	reg_node = (registration_node_t *)_nc_table_find_n(globals->registration_table, token);
	if (reg_node != NULL) goto client_registration_create_fail;

	name_node = name_node_for_name(name, nid, true, false);
	if (name_node == NULL) goto client_registration_create_fail;

	reg_node = (registration_node_t *)calloc(1, sizeof(registration_node_t));
	if (reg_node == NULL)
	{
#ifdef DEBUG
		_notify_client_log(ASL_LEVEL_ERR, "client_registration_create name %s calloc failed errno %d [%s]\n", name, errno, strerror(errno));
#endif
		name_node_release(name_node);
		goto client_registration_create_fail;
	}

	reg_node->refcount = 1;
	reg_node->token = token;
	_nc_table_insert_n(globals->registration_table, token, reg_node);

#ifdef DEBUG
	if (_libnotify_debug & DEBUG_NODES) _notify_client_log(ASL_LEVEL_NOTICE, "client_registration_create token %u refcount %d -> %p", token, reg_node->refcount, reg_node);
#endif

#ifdef DEBUG
	if (_libnotify_debug & DEBUG_REGISTRATION)
	{
		if (nid == NID_UNSET) _notify_client_log(ASL_LEVEL_NOTICE, "client_registration_create reg_node %p name %s name_node %p nid NID_UNSET token %u flags 0x%08x", reg_node, name, name_node, token, flags);
		else if (nid == NID_CALLED_ONCE) _notify_client_log(ASL_LEVEL_NOTICE, "client_registration_create reg_node %p name %s name_node %p nid NID_CALLED_ONCE token %u flags 0x%08x", reg_node, name, name_node, token, flags);
		else _notify_client_log(ASL_LEVEL_NOTICE, "client_registration_create reg_node %p name %s name_node %p nid %llu token %u flags 0x%08x", reg_node, name, name_node, nid, token, flags);
	}
#endif

	/* no need to regenerate a coalesced registration */
	if (flags & NOTIFY_TYPE_COALESCED) flags &= ~NOTIFY_FLAG_REGEN;

	reg_node->flags = flags;
	reg_node->slot = slot;
	reg_node->val = 0;
	reg_node->fd = fd;
	reg_node->mp = mp;
	reg_node->signal = sig;
	reg_node->client_id = cid;

	/*
	 * Registration nodes retain their name node, so in theory we should
	 * retain name_node here.  However, name_node_for_name() retains
	 * the name node, so we just make the assignment here and skip
	 * calling name_node_release() before returning from this routine.
	 */
	reg_node->name_node = name_node;

	/*
	 * If dispatch is available, add this registration to the name node's dispatch list.
	 * Doesn't apply to polled types (memory and plain).
	 *
	 * Note that if NOTIFY_OPT_DISPATCH is called and this is a delivered (non-polled)
	 * notificaton type, then the NOTIFY_TYPE_COALESCED flag will be true.  The if
	 * statement below could include "&& (flags & NOTIFY_TYPE_COALESCED)", but it's
	 * not required.
	 */
	if ((globals->client_opts & NOTIFY_OPT_DISPATCH) && (flags & NOTIFY_TYPE_DELIVERED))
	{
#ifdef DEBUG
		if (_libnotify_debug & DEBUG_REGISTRATION) _notify_client_log(ASL_LEVEL_NOTICE, "Add coalesced registration %p to name %s name node %p base %p", reg_node, name, name_node, name_node->coalesce_base);
#endif
		name_node_add_coalesced_registration(name_node, reg_node);
	}

	if ((name_node->refcount % MULTIPLE_REGISTRATION_WARNING_TRIGGER) == 0)
	{
		warn_count = name_node->refcount;
	}

	if (warn_count > 0)
	{
		_notify_client_log(ASL_LEVEL_WARNING, "notify name \"%s\" has been registered %d times - this may be a leak", name, warn_count);
	}

	mutex_unlock("global", &globals->notify_lock, __func__, __LINE__);
	return 0;

client_registration_create_fail:
	mutex_unlock("global", &globals->notify_lock, __func__, __LINE__);
	return -1;
}

/*
 * N.B. base_registration_create is called from notify_register_mach_port(), which holds the global lock
 */
static int
base_registration_create(const char *name, size_t namelen, uint64_t nid, uint32_t token, uint32_t flags, mach_port_t mp)
{
	name_node_t *name_node = NULL;
	registration_node_t *reg_node;
	notify_globals_t globals = _notify_globals();

	if (globals->name_table == NULL) return -1;
	if (globals->registration_table == NULL) return -1;
	if (name == NULL) return -1;

	/* should never happen, but check if the registration exists */
	reg_node = (registration_node_t *)_nc_table_find_n(globals->registration_table, token);
	if (reg_node != NULL) return -1;

	reg_node = (registration_node_t *)calloc(1, sizeof(registration_node_t));
	if (reg_node == NULL)
	{
#ifdef DEBUG
		_notify_client_log(ASL_LEVEL_ERR, "base_registration_create name %s calloc failed errno %d [%s]\n", name, errno, strerror(errno));
#endif
		return -1;
	}

	name_node = name_node_for_name(name, nid, true, false);
	if (name_node == NULL)
	{
		free(reg_node);
		return -1;
	}

#ifdef DEBUG
	if (_libnotify_debug & DEBUG_REGISTRATION)
	{
		if (nid == NID_UNSET) _notify_client_log(ASL_LEVEL_NOTICE, "base_registration_create reg_node %p name %s name_node %p nid NID_UNSET token %u flags 0x%08x", reg_node, name, name_node, token, flags);
		else if (nid == NID_CALLED_ONCE) _notify_client_log(ASL_LEVEL_NOTICE, "base_registration_create reg_node %p name %s name_node %p nid NID_CALLED_ONCE token %u flags 0x%08x", reg_node, name, name_node, token, flags);
		else _notify_client_log(ASL_LEVEL_NOTICE, "base_registration_create reg_node %p name %s name_node %p nid %llu token %u flags 0x%08x", reg_node, name, name_node, nid, token, flags);
	}
#endif

	reg_node->refcount = 1;
	reg_node->token = token;
	reg_node->client_id = token;
	reg_node->flags = flags | NOTIFY_TYPE_COALESCE_BASE;
	reg_node->mp = mp;
	reg_node->val = 0;
	reg_node->slot = SLOT_NONE;
	reg_node->fd = FD_NONE;
	reg_node->signal = SIGNAL_NONE;
	reg_node->name_node = name_node;

	/*
	 * Registration nodes retain their name node, so in theory we should
	 * retain name_node here.  However, name_node_for_name() retains
	 * the name node, so we just make the assignment here and skip
	 * calling name_node_release() before returning from this routine.
	 */
	name_node->coalesce_base = reg_node;
	name_node->coalesce_base_token = token;

	_nc_table_insert_n(globals->registration_table, token, reg_node);
	return 0;
}

static void
_notify_lib_regenerate_registration(registration_node_t *r)
{
	uint32_t type;
	int status, new_slot;
	kern_return_t kstatus;
	mach_port_t port;
	uint64_t new_nid;
	size_t pathlen;

	notify_globals_t globals = _notify_globals();

	if (r == NULL) return;
	if (r->flags & NOTIFY_FLAG_SELF) return;
	if ((r->flags & NOTIFY_FLAG_REGEN) == 0) return;
	if (r->token == globals->notify_common_token) return;

	port = MACH_PORT_NULL;
	if (r->flags & NOTIFY_TYPE_PORT)
	{
		port = globals->notify_common_port;
	}

	pathlen = 0;
	if (r->path != NULL) pathlen = strlen(r->path);
	type = r->flags & 0x000000ff;

	kstatus = _notify_server_regenerate(globals->notify_server_port, (caddr_t)r->name_node->name, (mach_msg_type_number_t)r->name_node->name_len, r->token, type, port, r->signal, r->slot, r->set_state_val, r->set_state_time, r->path, (mach_msg_type_number_t)pathlen, r->path_flags, &new_slot, &new_nid, &status);

	if (kstatus != KERN_SUCCESS) status = NOTIFY_STATUS_FAILED;
	if (status != NOTIFY_STATUS_OK) return;

	r->slot = new_slot;
	r->name_node->name_id = new_nid;
}

/*
 * Invoked when server has died.
 * Regenerates all registrations and state.
 */
static void
_notify_lib_regenerate(int src)
{
	void *tt;
	registration_node_t *reg;
	notify_globals_t globals = _notify_globals();

	if ((globals->client_opts & NOTIFY_OPT_REGEN) == 0) return;

	/* _notify_lib_init returns an error if regeneration is unnecessary */
	if (_notify_lib_init(EVENT_REGEN) == NOTIFY_STATUS_OK)
	{
		mutex_lock("global", &globals->notify_lock, __func__, __LINE__);

		tt = _nc_table_traverse_start(globals->registration_table);
		while (tt != NULL)
		{
			reg = _nc_table_traverse(globals->registration_table, tt);
			if (reg == NULL) break;
			_notify_lib_regenerate_registration(reg);
		}

		_nc_table_traverse_end(globals->name_table, tt);

		mutex_unlock("global", &globals->notify_lock, __func__, __LINE__);
	}
}

/*
 * Regenerate if the server PID (shared memory slot 0) has changed.
 */
static inline void
regenerate_check()
{
	notify_globals_t globals = _notify_globals();

	if ((globals->client_opts & NOTIFY_OPT_REGEN) == 0) return;

	if ((globals->shm_base != NULL) && (globals->shm_base[0] != globals->notify_server_pid)) _notify_lib_regenerate(0);
}

/* notify_lock is required in notify_retain_file_descriptor */
static void
notify_retain_file_descriptor(int clnt, int srv)
{
	int x, i;
	notify_globals_t globals = _notify_globals();

	if (clnt < 0) return;
	if (srv < 0) return;

	mutex_lock("global", &globals->notify_lock, __func__, __LINE__);

	x = -1;
	for (i = 0; (i < globals->fd_count) && (x < 0); i++)
	{
		if (globals->fd_clnt[i] == clnt) x = i;
	}

	if (x >= 0)
	{
		globals->fd_refcount[x]++;
		mutex_unlock("global", &globals->notify_lock, __func__, __LINE__);
		return;
	}

	x = globals->fd_count;
	globals->fd_count++;

	globals->fd_clnt = (int *)reallocf(globals->fd_clnt, globals->fd_count * sizeof(int));
	globals->fd_srv = (int *)reallocf(globals->fd_srv, globals->fd_count * sizeof(int));
	globals->fd_refcount = (int *)reallocf(globals->fd_refcount, globals->fd_count * sizeof(int));

	if ((globals->fd_clnt == NULL) || (globals->fd_srv == NULL) || (globals->fd_refcount == NULL))
	{
#ifdef DEBUG
		_notify_client_log(ASL_LEVEL_ERR, "notify_retain_file_descriptor reallocf failed errno %d [%s]\n", errno, strerror(errno));
#endif
		free(globals->fd_clnt);
		globals->fd_clnt = NULL;
		free(globals->fd_srv);
		globals->fd_srv = NULL;
		free(globals->fd_refcount);
		globals->fd_refcount = NULL;
		globals->fd_count = 0;
	}
	else
	{
		globals->fd_clnt[x] = clnt;
		globals->fd_srv[x] = srv;
		globals->fd_refcount[x] = 1;
	}

	mutex_unlock("global", &globals->notify_lock, __func__, __LINE__);
}

static void
notify_release_file_descriptor(int fd)
{
	int x, i, j;
	notify_globals_t globals = _notify_globals();

	if (fd < 0) return;

	mutex_lock("global", &globals->notify_lock, __func__, __LINE__);

	x = -1;
	for (i = 0; (i < globals->fd_count) && (x < 0); i++)
	{
		if (globals->fd_clnt[i] == fd) x = i;
	}

	if (x < 0)
	{
		mutex_unlock("global", &globals->notify_lock, __func__, __LINE__);
		return;
	}

	if (globals->fd_refcount[x] > 0) globals->fd_refcount[x]--;
	if (globals->fd_refcount[x] > 0)
	{
		mutex_unlock("global", &globals->notify_lock, __func__, __LINE__);
		return;
	}

	close(globals->fd_clnt[x]);
	close(globals->fd_srv[x]);

	if (globals->fd_count == 1)
	{
		free(globals->fd_clnt);
		free(globals->fd_srv);
		free(globals->fd_refcount);
		globals->fd_count = 0;
		mutex_unlock("global", &globals->notify_lock, __func__, __LINE__);
		return;
	}

	for (i = x + 1, j = x; i < globals->fd_count; i++, j++)
	{
		globals->fd_clnt[j] = globals->fd_clnt[i];
		globals->fd_srv[j] = globals->fd_srv[i];
		globals->fd_refcount[j] = globals->fd_refcount[i];
	}

	globals->fd_count--;

	globals->fd_clnt = (int *)reallocf(globals->fd_clnt, globals->fd_count * sizeof(int));
	globals->fd_srv = (int *)reallocf(globals->fd_srv, globals->fd_count * sizeof(int));
	globals->fd_refcount = (int *)reallocf(globals->fd_refcount, globals->fd_count * sizeof(int));

	if ((globals->fd_clnt == NULL) || (globals->fd_srv == NULL) || (globals->fd_refcount == NULL))
	{
#ifdef DEBUG
		_notify_client_log(ASL_LEVEL_ERR, "notify_release_file_descriptor reallocf failed errno %d [%s]\n", errno, strerror(errno));
#endif
		free(globals->fd_clnt);
		free(globals->fd_srv);
		free(globals->fd_refcount);
		globals->fd_count = 0;
	}

	mutex_unlock("global", &globals->notify_lock, __func__, __LINE__);
}

/* notify_lock is required in notify_retain_mach_port */
static void
notify_retain_mach_port(mach_port_t mp, int mine)
{
	int x, i;
	notify_globals_t globals = _notify_globals();

	if (mp == MACH_PORT_NULL) return;

	mutex_lock("global", &globals->notify_lock, __func__, __LINE__);

	x = -1;
	for (i = 0; (i < globals->mp_count) && (x < 0); i++)
	{
		if (globals->mp_list[i] == mp) x = i;
	}

	if (x >= 0)
	{
		globals->mp_refcount[x]++;
		mutex_unlock("global", &globals->notify_lock, __func__, __LINE__);
		return;
	}

	x = globals->mp_count;
	globals->mp_count++;

	globals->mp_list = (mach_port_t *)reallocf(globals->mp_list, globals->mp_count * sizeof(mach_port_t));
	globals->mp_refcount = (int *)reallocf(globals->mp_refcount, globals->mp_count * sizeof(int));
	globals->mp_mine = (int *)reallocf(globals->mp_mine, globals->mp_count * sizeof(int));

	if ((globals->mp_list == NULL) || (globals->mp_refcount == NULL) || (globals->mp_mine == NULL))
	{
#ifdef DEBUG
		_notify_client_log(ASL_LEVEL_ERR, "notify_retain_mach_port reallocf failed errno %d [%s]\n", errno, strerror(errno));
#endif
		free(globals->mp_list);
		globals->mp_list = NULL;
		free(globals->mp_refcount);
		globals->mp_refcount = NULL;
		free(globals->mp_mine);
		globals->mp_mine = NULL;
		globals->mp_count = 0;
	}
	else
	{
		globals->mp_list[x] = mp;
		globals->mp_refcount[x] = 1;
		globals->mp_mine[x] = mine;
	}

	mutex_unlock("global", &globals->notify_lock, __func__, __LINE__);
}

static void
notify_release_mach_port(mach_port_t mp, uint32_t flags)
{
	int x, i;
	notify_globals_t globals = _notify_globals();

	if (mp == MACH_PORT_NULL) return;

	mutex_lock("global", &globals->notify_lock, __func__, __LINE__);

	x = -1;
	for (i = 0; (i < globals->mp_count) && (x < 0); i++)
	{
		if (globals->mp_list[i] == mp) x = i;
	}

	if (x < 0)
	{
		mutex_unlock("global", &globals->notify_lock, __func__, __LINE__);
		return;
	}

	if (globals->mp_refcount[x] > 0) globals->mp_refcount[x]--;
	if (globals->mp_refcount[x] > 0)
	{
		mutex_unlock("global", &globals->notify_lock, __func__, __LINE__);
		return;
	}

	if (globals->mp_mine[x] == 1)
	{
		mach_port_mod_refs(mach_task_self(), mp, MACH_PORT_RIGHT_RECEIVE, -1);

		/* release send right if this is a self notification */
		if (flags & NOTIFY_FLAG_SELF) mach_port_deallocate(mach_task_self(), mp);
	}

	if (flags & NOTIFY_FLAG_RELEASE_SEND)
	{
		/* multiplexed registration holds a send right in Libnotify */
		mach_port_deallocate(mach_task_self(), mp);
	}

	if (globals->mp_count == 1)
	{
		free(globals->mp_list);
		globals->mp_list = NULL;
		free(globals->mp_refcount);
		globals->mp_refcount = NULL;
		free(globals->mp_mine);
		globals->mp_mine = NULL;
		globals->mp_count = 0;
		mutex_unlock("global", &globals->notify_lock, __func__, __LINE__);
		return;
	}

	for (i = x + 1; i < globals->mp_count; i++)
	{
		globals->mp_list[i - 1] = globals->mp_list[i];
		globals->mp_refcount[i - 1] = globals->mp_refcount[i];
		globals->mp_mine[i - 1] = globals->mp_mine[i];
	}

	globals->mp_count--;

	globals->mp_list = (mach_port_t *)reallocf(globals->mp_list, globals->mp_count * sizeof(mach_port_t));
	globals->mp_refcount = (int *)reallocf(globals->mp_refcount, globals->mp_count * sizeof(int));
	globals->mp_mine = (int *)reallocf(globals->mp_mine, globals->mp_count * sizeof(int));

	if ((globals->mp_list == NULL) || (globals->mp_refcount == NULL) || (globals->mp_mine == NULL))
	{
#ifdef DEBUG
		_notify_client_log(ASL_LEVEL_ERR, "notify_release_mach_port reallocf failed errno %d [%s]\n", errno, strerror(errno));
#endif
		if (globals->mp_list != NULL) free(globals->mp_list);
		if (globals->mp_refcount != NULL) free(globals->mp_refcount);
		if (globals->mp_mine != NULL) free(globals->mp_mine);
		globals->mp_count = 0;
	}

	mutex_unlock("global", &globals->notify_lock, __func__, __LINE__);
}

static notify_state_t *
_notify_lib_self_state()
{
	notify_globals_t globals = _notify_globals();

	dispatch_once(&globals->self_state_once, ^{
		globals->self_state = _notify_lib_notify_state_new(NOTIFY_STATE_USE_LOCKS, 0);
	});

	return globals->self_state;
}

/* SPI */
void
notify_set_options(uint32_t opts)
{
	notify_globals_t globals = _notify_globals();

	/* NOTIFY_OPT_DISABLE can be unset with NOTIFY_OPT_ENABLE */
	if (globals->client_opts & NOTIFY_OPT_DISABLE)
	{
		if ((opts & NOTIFY_OPT_ENABLE) == 0) return;

		/* re-enable by swapping in the saved server port and saved opts*/
		mutex_lock("global", &globals->notify_lock, __func__, __LINE__);

		globals->client_opts = globals->saved_opts;
		globals->notify_server_port = globals->saved_server_port;

		mutex_unlock("global", &globals->notify_lock, __func__, __LINE__);
		return;
	}

	/*
	 * A client can disable the library even if the server port has already been fetched.
	 * Note that this could race with another thread making a Libnotify call.
	 */
	if (opts & NOTIFY_OPT_DISABLE)
	{
		mutex_lock("global", &globals->notify_lock, __func__, __LINE__);

		globals->saved_opts = globals->client_opts;
		globals->client_opts = NOTIFY_OPT_DISABLE;

		globals->saved_server_port = globals->notify_server_port;
		globals->notify_server_port = MACH_PORT_NULL;

		mutex_unlock("global", &globals->notify_lock, __func__, __LINE__);
		return;
	}

	globals->client_opts = opts;

	/* call _notify_lib_init to create ports / dispatch sources as required */
	_notify_lib_init(EVENT_INIT);
}

/*
 * PUBLIC API
 */

/*
 * notify_post is a very simple API, but the implementation is
 * more complex to try to optimize the time it takes.
 *
 * The server - notifyd - keeps a unique ID number for each key
 * in the namespace.  Although it's reasonably fast to call
 * _notify_server_post_4 (a MIG simpleroutine), the MIG call
 * allocates VM and copies the name string.  It's much faster to
 * call using the ID number.  The problem is mapping from name to
 * ID number.  The name table keeps track of all registered names
 * (in the client), but the registration calls are simpleroutines,
 * except for notify_register_check.  notify_register_check saves
 * the name ID in the name table, but the other routines set it
 * to NID_UNSET.
 *
 * In notify_post, we check if the name is known.  If it is not,
 * then the client is doing a "cold call".  There may be no
 * clients for this name anywhere on the system.  In this case
 * we simply send the name.  We take the allocate/copy cost, but
 * the latency is still not too bad since we use a simpleroutine.
 *
 * If the name in registered and the ID number is known, we send
 * the ID using a simpleroutine.  This is very fast.
 *
 * If the name is registered but the ID number is NID_UNSET, we
 * send the name (as in a "cold call".  It *might* just be that
 * this client process just posts once, and we don't want to incur
 * any addition cost.  The ID number is reset to NID_CALLED_ONCE.
 *
 * If the client posts the same name again (the ID number is
 * NID_CALLED_ONCE) we do a synchronous call to notifyd, sending
 * the name string and getting back the name ID, whcih we save
 * in the name table.  This is simply a zero/one/many heuristic:
 * If the client posts the same name more than once, we make the
 * guess that it's going to do it more frequently, and it's worth
 * the time it takes to fetch the ID from notifyd.
 */
uint32_t
notify_post(const char *name)
{
#ifdef DEBUG
	if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "-> %s\n", __func__);
#endif

	notify_state_t *ns_self;
	kern_return_t kstatus;
	uint32_t status;
	size_t namelen = 0;
	name_node_t *n;
	uint64_t nid = UINT64_MAX;
	notify_globals_t globals = _notify_globals();

	regenerate_check();

	if (name == NULL)
	{
#ifdef DEBUG
		if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
		return NOTIFY_STATUS_INVALID_NAME;
	}

	if (!strncmp(name, SELF_PREFIX, SELF_PREFIX_LEN))
	{
		ns_self = _notify_lib_self_state();
		if (ns_self == NULL) return NOTIFY_STATUS_FAILED;
		_notify_lib_post(ns_self, name, 0, 0);
#ifdef DEBUG
		if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
		return NOTIFY_STATUS_OK;
	}

	if (globals->notify_server_port == MACH_PORT_NULL)
	{
		status = _notify_lib_init(EVENT_INIT);
		if (status != 0)
		{
#ifdef DEBUG
			if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
			return NOTIFY_STATUS_FAILED;
		}
	}

	if (globals->notify_ipc_version == 0)
	{
		namelen = strlen(name);
		kstatus = _notify_server_post(globals->notify_server_port, (caddr_t)name, (mach_msg_type_number_t)namelen, (int32_t *)&status);
		if (kstatus != KERN_SUCCESS)
		{
#ifdef DEBUG
			if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
			return NOTIFY_STATUS_FAILED;
		}

#ifdef DEBUG
		if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
		return status;
	}

	namelen = strlen(name);

	/* See if we have a name ID for this name. */
	n = name_node_for_name(name, NID_UNSET, false, true);
	if (n != NULL)
	{
		if (n->name_id == NID_UNSET)
		{
			/* First post goes using the name string. */
			kstatus = _notify_server_post_4(globals->notify_server_port, (caddr_t)name, (mach_msg_type_number_t)namelen);
			if (kstatus != KERN_SUCCESS)
			{
				name_node_release(n);
#ifdef DEBUG
				if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
				return NOTIFY_STATUS_FAILED;
			}

			name_node_set_nid(n, NID_CALLED_ONCE);
		}
		else if (n->name_id == NID_CALLED_ONCE)
		{
			/* Post and fetch the name ID.  Slow, but subsequent posts will be very fast. */
			kstatus = _notify_server_post_2(globals->notify_server_port, (caddr_t)name, (mach_msg_type_number_t)namelen, &nid, (int32_t *)&status);
			if (kstatus != KERN_SUCCESS)
			{
				name_node_release(n);
#ifdef DEBUG
				if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
				return NOTIFY_STATUS_FAILED;
			}

			name_node_set_nid(n, nid);
		}
		else
		{
			/* We have the name ID.  Do an async post using the name ID.  Very fast. */
			kstatus = _notify_server_post_3(globals->notify_server_port, n->name_id);
			if (kstatus != KERN_SUCCESS)
			{
				name_node_release(n);
#ifdef DEBUG
				if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
				return NOTIFY_STATUS_FAILED;
			}
		}

		name_node_release(n);
#ifdef DEBUG
		if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
		return NOTIFY_STATUS_OK;
	}

	/* Do an async post using the name string. Fast (but not as fast as using name ID). */
	kstatus = _notify_server_post_4(globals->notify_server_port, (caddr_t)name, (mach_msg_type_number_t)namelen);
	if (kstatus != KERN_SUCCESS)
	{
#ifdef DEBUG
		if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
		return NOTIFY_STATUS_FAILED;
	}

#ifdef DEBUG
	if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
	return NOTIFY_STATUS_OK;
}

uint32_t
notify_set_owner(const char *name, uint32_t uid, uint32_t gid)
{
#ifdef DEBUG
	if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "-> %s\n", __func__);
#endif

	notify_state_t *ns_self;
	kern_return_t kstatus;
	uint32_t status;
	notify_globals_t globals = _notify_globals();

	if (name == NULL)
	{
#ifdef DEBUG
		if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
		return NOTIFY_STATUS_INVALID_NAME;
	}

	if (!strncmp(name, SELF_PREFIX, SELF_PREFIX_LEN))
	{
		ns_self = _notify_lib_self_state();
		if (ns_self == NULL)
		{
#ifdef DEBUG
			if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
			return NOTIFY_STATUS_FAILED;
		}

		status = _notify_lib_set_owner(ns_self, name, uid, gid);
#ifdef DEBUG
		if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
		return status;
	}

	if (globals->notify_server_port == MACH_PORT_NULL)
	{
		status = _notify_lib_init(EVENT_INIT);
		if (status != 0)
		{
#ifdef DEBUG
			if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
			return NOTIFY_STATUS_FAILED;
		}
	}

	kstatus = _notify_server_set_owner(globals->notify_server_port, (caddr_t)name, (mach_msg_type_number_t)strlen(name), uid, gid, (int32_t *)&status);
	if (kstatus != KERN_SUCCESS)
	{
#ifdef DEBUG
		if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
		return NOTIFY_STATUS_FAILED;
	}

#ifdef DEBUG
	if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
	return status;
}

uint32_t
notify_get_owner(const char *name, uint32_t *uid, uint32_t *gid)
{
#ifdef DEBUG
	if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "-> %s\n", __func__);
#endif

	notify_state_t *ns_self;
	kern_return_t kstatus;
	uint32_t status;
	notify_globals_t globals = _notify_globals();

	if (name == NULL)
	{
#ifdef DEBUG
		if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
		return NOTIFY_STATUS_INVALID_NAME;
	}

	if (!strncmp(name, SELF_PREFIX, SELF_PREFIX_LEN))
	{
		ns_self = _notify_lib_self_state();
		if (ns_self == NULL)
		{
#ifdef DEBUG
			if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
			return NOTIFY_STATUS_FAILED;
		}

		status = _notify_lib_get_owner(ns_self, name, uid, gid);
#ifdef DEBUG
		if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
		return status;
	}

	if (globals->notify_server_port == MACH_PORT_NULL)
	{
		status = _notify_lib_init(EVENT_INIT);
		if (status != 0)
		{
#ifdef DEBUG
			if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
			return NOTIFY_STATUS_FAILED;
		}
	}

	kstatus = _notify_server_get_owner(globals->notify_server_port, (caddr_t)name, (mach_msg_type_number_t)strlen(name), (int32_t *)uid, (int32_t *)gid, (int32_t *)&status);
	if (kstatus != KERN_SUCCESS)
	{
#ifdef DEBUG
		if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
		return NOTIFY_STATUS_FAILED;
	}

#ifdef DEBUG
	if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
	return status;
}

uint32_t
notify_set_access(const char *name, uint32_t access)
{
#ifdef DEBUG
	if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "-> %s\n", __func__);
#endif

	notify_state_t *ns_self;
	kern_return_t kstatus;
	uint32_t status;
	notify_globals_t globals = _notify_globals();

	if (name == NULL)
	{
#ifdef DEBUG
		if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
		return NOTIFY_STATUS_INVALID_NAME;
	}

	if (!strncmp(name, SELF_PREFIX, SELF_PREFIX_LEN))
	{
		ns_self = _notify_lib_self_state();
		if (ns_self == NULL)
		{
#ifdef DEBUG
			if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
			return NOTIFY_STATUS_FAILED;
		}

		status = _notify_lib_set_access(ns_self, name, access);
#ifdef DEBUG
		if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
		return status;
	}

	if (globals->notify_server_port == MACH_PORT_NULL)
	{
		status = _notify_lib_init(EVENT_INIT);
		if (status != 0)
		{
#ifdef DEBUG
			if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
			return NOTIFY_STATUS_FAILED;
		}
	}

	kstatus = _notify_server_set_access(globals->notify_server_port, (caddr_t)name, (mach_msg_type_number_t)strlen(name), access, (int32_t *)&status);
	if (kstatus != KERN_SUCCESS)
	{
#ifdef DEBUG
		if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
		return NOTIFY_STATUS_FAILED;
	}

#ifdef DEBUG
	if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
	return status;
}

uint32_t
notify_get_access(const char *name, uint32_t *access)
{
#ifdef DEBUG
	if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "-> %s\n", __func__);
#endif

	notify_state_t *ns_self;
	kern_return_t kstatus;
	uint32_t status;
	notify_globals_t globals = _notify_globals();

	if (name == NULL)
	{
#ifdef DEBUG
		if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
		return NOTIFY_STATUS_INVALID_NAME;
	}

	if (!strncmp(name, SELF_PREFIX, SELF_PREFIX_LEN))
	{
		ns_self = _notify_lib_self_state();
		if (ns_self == NULL)
		{
#ifdef DEBUG
			if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
			return NOTIFY_STATUS_FAILED;
		}

		status = _notify_lib_get_access(ns_self, name, access);
#ifdef DEBUG
		if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
		return status;
	}

	if (globals->notify_server_port == MACH_PORT_NULL)
	{
		status = _notify_lib_init(EVENT_INIT);
		if (status != 0)
		{
#ifdef DEBUG
			if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
			return NOTIFY_STATUS_FAILED;
		}
	}

	kstatus = _notify_server_get_access(globals->notify_server_port, (caddr_t)name, (mach_msg_type_number_t)strlen(name), (int32_t *)access, (int32_t *)&status);
	if (kstatus != KERN_SUCCESS)
	{
#ifdef DEBUG
		if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
		return NOTIFY_STATUS_FAILED;
	}

#ifdef DEBUG
	if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
	return status;
}

/* notifyd retains and releases a name when clients register and cancel. */
uint32_t
notify_release_name(const char *name)
{
#ifdef DEBUG
	if (_libnotify_debug & DEBUG_API)
	{
		_notify_client_log(ASL_LEVEL_NOTICE, "-> %s\n", __func__);
		_notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 3);
	}
#endif
	return NOTIFY_STATUS_OK;
}

static void
_notify_dispatch_local_notification(registration_node_t *r)
{
	if (r == NULL) return;
	if (r->queue == NULL) return;
	if (r->block == NULL) return;

#ifdef DEBUG
	if (_libnotify_debug & DEBUG_NOTIFICATION)
	{
		if (r->flags & NOTIFY_TYPE_COALESCED) _notify_client_log(ASL_LEVEL_NOTICE, "coalesced notification for token %d (name %s)", r->token, r->name_node->name);
		else if (r->flags & NOTIFY_TYPE_COALESCE_BASE) _notify_client_log(ASL_LEVEL_NOTICE, "coalesced base notification for token %d (name %s)", r->token, r->name_node->name);
		else _notify_client_log(ASL_LEVEL_NOTICE, "dispatch notification for token %d (name %s)", r->token, r->name_node->name);
	}
#endif

	/*
	 * If the block calls notify_cancel, the node can get trashed, so
	 * we keep anything we need from the block (properly retained and released)
	 * in local variables.  Concurrent notify_cancel() calls in the block are safe.
	 */
	int token = r->token;
	notify_handler_t theblock = Block_copy(r->block);
	dispatch_queue_t thequeue = r->queue;
	dispatch_retain(thequeue);

	dispatch_async(thequeue, ^{
		bool valid = notify_is_valid_token(token);
#ifdef DEBUG
		if (_libnotify_debug & DEBUG_NOTIFICATION) _notify_client_log(ASL_LEVEL_NOTICE, "-> dispatch_async token %d (%svalid) registration node %p", token, valid ? "" : "in", r);
#endif
		/* check if the token is still valid: it may have been cancelled */
		if (valid) theblock(token);
		_Block_release(theblock);
		dispatch_release(thequeue);
#ifdef DEBUG
		if (_libnotify_debug & DEBUG_NOTIFICATION) _notify_client_log(ASL_LEVEL_NOTICE, "<- dispatch_async token %d", token);
#endif
	});
}

static void
_notify_dispatch_handle(mach_port_t port)
{
	name_node_t *n;
	registration_node_t *r;
	int token;
	mach_msg_empty_rcv_t msg;
	kern_return_t status;

	if (port == MACH_PORT_NULL) return;

	memset(&msg, 0, sizeof(msg));

	/*
	 * The dispatch source watching our multiplexed mach port has fired.
	 * Read the message that is waiting and get the token ID from the mach header.
	 */
	status = mach_msg(&msg.header, MACH_RCV_MSG, 0, sizeof(msg), port, 0, MACH_PORT_NULL);
	if (status != KERN_SUCCESS) return;

	token = msg.header.msgh_id;

#ifdef DEBUG
	if (_libnotify_debug & DEBUG_NOTIFICATION) _notify_client_log(ASL_LEVEL_NOTICE, "_notify_dispatch_handle token %d", token);
#endif

	r = registration_node_find(token);
	if (r == NULL) return;

	n = r->name_node;
	if (n == NULL)
	{
		/* should not happen */
		registration_node_release(r);
		return;
	}

	mutex_lock(n->name, &n->lock, __func__, __LINE__);

	if (r->flags & NOTIFY_TYPE_COALESCE_BASE)
	{
		list_t *l;
		for (l = _nc_list_tail(n->coalesced); l != NULL; l = _nc_list_prev(l))
		{
			registration_node_t *x = (registration_node_t *)_nc_list_data(l);
			if ((x != NULL) && (x != r)) _notify_dispatch_local_notification(x);
		}
	}
	else
	{
		_notify_dispatch_local_notification(r);
	}

	mutex_unlock(n->name, &n->lock, __func__, __LINE__);

	registration_node_release(r);
}

uint32_t
notify_register_dispatch(const char *name, int *out_token, dispatch_queue_t queue, notify_handler_t handler)
{
#ifdef DEBUG
	if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "-> %s\n", __func__);
#endif

	uint32_t status;
	registration_node_t *r;
	notify_globals_t globals = _notify_globals();

	regenerate_check();

	if (queue == NULL)
	{
#ifdef DEBUG
		if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
		return NOTIFY_STATUS_FAILED;
	}

	if (handler == NULL)
	{
#ifdef DEBUG
		if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
		return NOTIFY_STATUS_FAILED;
	}

	/* client is using dispatch: enable local demux / dispatch and regeneration */
	notify_set_options(NOTIFY_OPT_DISPATCH | NOTIFY_OPT_REGEN);

	status = notify_register_mach_port(name, &globals->notify_common_port, NOTIFY_REUSE, out_token);
	if (status != NOTIFY_STATUS_OK)
	{
#ifdef DEBUG
		if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
		return status;
	}

	r = registration_node_find(*out_token);
	if (r == NULL)
	{
#ifdef DEBUG
		if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
		return NOTIFY_STATUS_FAILED;
	}

	r->queue = queue;
	dispatch_retain(r->queue);
	r->block = Block_copy(handler);

	registration_node_release(r);

#ifdef DEBUG
	if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
	return NOTIFY_STATUS_OK;
}

/* note this does not get self names */
static uint32_t
notify_register_mux_fd(const char *name, int *out_token, int rfd, int wfd)
{
	uint32_t status;
	registration_node_t *r;
	int val;
	notify_globals_t globals = _notify_globals();

	status = NOTIFY_STATUS_OK;

	if (globals->notify_common_port == MACH_PORT_NULL) return NOTIFY_STATUS_FAILED;

	status = notify_register_mach_port(name, &globals->notify_common_port, NOTIFY_REUSE, out_token);

	r = registration_node_find(*out_token);
	if (r == NULL) return NOTIFY_STATUS_FAILED;

	r->token = *out_token;
	r->fd = rfd;
	r->queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
	dispatch_retain(r->queue);
	val = htonl(r->token);
	r->block = (notify_handler_t)Block_copy(^(int unused){ write(wfd, &val, sizeof(val)); });

	registration_node_release(r);

	return NOTIFY_STATUS_OK;
}

#pragma mark -
#pragma mark registration

uint32_t
notify_register_check(const char *name, int *out_token)
{
#if TARGET_IPHONE_SIMULATOR
	return notify_register_plain(name, out_token);
#endif
#ifdef DEBUG
	if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "-> %s\n", __func__);
#endif

	notify_state_t *ns_self;
	kern_return_t kstatus;
	uint32_t status, token;
	uint64_t nid;
	int32_t slot, shmsize;
	size_t namelen;
	uint32_t cid;
	notify_globals_t globals = _notify_globals();

	regenerate_check();

	if (name == NULL)
	{
#ifdef DEBUG
		if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
		return NOTIFY_STATUS_INVALID_NAME;
	}

	if (out_token == NULL)
	{
#ifdef DEBUG
		if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
		return NOTIFY_STATUS_FAILED;
	}

	*out_token = -1;
	namelen = strlen(name);

	if (!strncmp(name, SELF_PREFIX, SELF_PREFIX_LEN))
	{
		ns_self = _notify_lib_self_state();
		if (ns_self == NULL)
		{
#ifdef DEBUG
			if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
			return NOTIFY_STATUS_FAILED;
		}

		token = OSAtomicIncrement32((int32_t *)&globals->token_id);
		status = _notify_lib_register_plain(ns_self, name, NOTIFY_CLIENT_SELF, token, SLOT_NONE, 0, 0, &nid);
		if (status != NOTIFY_STATUS_OK)
		{
#ifdef DEBUG
			if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
			return status;
		}

		cid = token;
		client_registration_create(name, namelen, nid, token, cid, SLOT_NONE, NOTIFY_FLAG_SELF | NOTIFY_TYPE_PLAIN, SIGNAL_NONE, FD_NONE, MACH_PORT_NULL);

		*out_token = token;
#ifdef DEBUG
		if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
		return NOTIFY_STATUS_OK;
	}

	if (globals->notify_server_port == MACH_PORT_NULL)
	{
		status = _notify_lib_init(EVENT_INIT);
		if (status != 0)
		{
#ifdef DEBUG
			if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
			return NOTIFY_STATUS_FAILED;
		}
	}

	token = OSAtomicIncrement32((int32_t *)&globals->token_id);
	kstatus = KERN_SUCCESS;

	if (globals->notify_ipc_version == 0)
	{
		nid = NID_UNSET;
		kstatus = _notify_server_register_check(globals->notify_server_port, (caddr_t)name, (mach_msg_type_number_t)namelen, &shmsize, &slot, (int32_t *)&cid, (int32_t *)&status);
	}
	else
	{
		cid = token;
		kstatus = _notify_server_register_check_2(globals->notify_server_port, (caddr_t)name, (mach_msg_type_number_t)namelen, token, &shmsize, &slot, &nid, (int32_t *)&status);
	}

	if (kstatus != KERN_SUCCESS)
	{
#ifdef DEBUG
		if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
		return NOTIFY_STATUS_FAILED;
	}

	if (status != NOTIFY_STATUS_OK)
	{
#ifdef DEBUG
		if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
		return status;
	}

	if (shmsize != -1)
	{
		if (globals->shm_base == NULL)
		{
			if (shm_attach(shmsize) != 0)
			{
#ifdef DEBUG
				if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
				return NOTIFY_STATUS_FAILED;
			}

			if (globals->shm_base == NULL)
			{
#ifdef DEBUG
				if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
				return NOTIFY_STATUS_FAILED;
			}
		}

		client_registration_create(name, namelen, nid, token, cid, slot, NOTIFY_TYPE_MEMORY | NOTIFY_FLAG_REGEN, SIGNAL_NONE, FD_NONE, MACH_PORT_NULL);
	}
	else
	{
		client_registration_create(name, namelen, nid, token, cid, SLOT_NONE, NOTIFY_TYPE_PLAIN | NOTIFY_FLAG_REGEN, SIGNAL_NONE, FD_NONE, MACH_PORT_NULL);
	}

	*out_token = token;
#ifdef DEBUG
	if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
	return status;
}

uint32_t
notify_register_plain(const char *name, int *out_token)
{
#ifdef DEBUG
	if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "-> %s\n", __func__);
#endif

	notify_state_t *ns_self;
	kern_return_t kstatus;
	uint32_t status;
	uint64_t nid;
	size_t namelen;
	int token;
	uint32_t cid;
	notify_globals_t globals = _notify_globals();

	regenerate_check();

	if (name == NULL)
	{
#ifdef DEBUG
		if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
		return NOTIFY_STATUS_INVALID_NAME;
	}

	namelen = strlen(name);

	if (!strncmp(name, SELF_PREFIX, SELF_PREFIX_LEN))
	{
		ns_self = _notify_lib_self_state();
		if (ns_self == NULL)
		{
#ifdef DEBUG
			if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
			return NOTIFY_STATUS_FAILED;
		}

		token = OSAtomicIncrement32((int32_t *)&globals->token_id);
		status = _notify_lib_register_plain(ns_self, name, NOTIFY_CLIENT_SELF, token, SLOT_NONE, 0, 0, &nid);
		if (status != NOTIFY_STATUS_OK)
		{
#ifdef DEBUG
			if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
			return status;
		}

		cid = token;
		client_registration_create(name, namelen, nid, token, cid, SLOT_NONE, NOTIFY_FLAG_SELF | NOTIFY_TYPE_PLAIN, SIGNAL_NONE, FD_NONE, MACH_PORT_NULL);

		*out_token = token;
#ifdef DEBUG
		if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
		return NOTIFY_STATUS_OK;
	}

	if (globals->notify_server_port == MACH_PORT_NULL)
	{
		status = _notify_lib_init(EVENT_INIT);
		if (status != 0)
		{
#ifdef DEBUG
			if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
			return NOTIFY_STATUS_FAILED;
		}
	}

	token = OSAtomicIncrement32((int32_t *)&globals->token_id);

	if (globals->notify_ipc_version == 0)
	{
		kstatus = _notify_server_register_plain(globals->notify_server_port, (caddr_t)name, (mach_msg_type_number_t)namelen, (int32_t *)&cid, (int32_t *)&status);
		if (kstatus != KERN_SUCCESS)
		{
#ifdef DEBUG
			if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
			return NOTIFY_STATUS_FAILED;
		}

		if (status != NOTIFY_STATUS_OK)
		{
#ifdef DEBUG
			if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
			return status;
		}
	}
	else
	{
		cid = token;
		kstatus = _notify_server_register_plain_2(globals->notify_server_port, (caddr_t)name, (mach_msg_type_number_t)namelen, token);
		if (kstatus != KERN_SUCCESS)
		{
#ifdef DEBUG
			if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
			return NOTIFY_STATUS_FAILED;
		}
	}

	client_registration_create(name, namelen, NID_UNSET, token, cid, SLOT_NONE, NOTIFY_TYPE_PLAIN | NOTIFY_FLAG_REGEN, SIGNAL_NONE, FD_NONE, MACH_PORT_NULL);

	*out_token = token;
#ifdef DEBUG
	if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
	return NOTIFY_STATUS_OK;
}

uint32_t
notify_register_signal(const char *name, int sig, int *out_token)
{
#ifdef DEBUG
	if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "-> %s\n", __func__);
#endif

	notify_state_t *ns_self;
	kern_return_t kstatus;
	uint32_t status;
	uint64_t nid;
	size_t namelen;
	int token;
	uint32_t cid;
	notify_globals_t globals = _notify_globals();

	regenerate_check();

	if (name == NULL)
	{
#ifdef DEBUG
		if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
		return NOTIFY_STATUS_INVALID_NAME;
	}

	namelen = strlen(name);

	if (!strncmp(name, SELF_PREFIX, SELF_PREFIX_LEN))
	{
		ns_self = _notify_lib_self_state();
		if (ns_self == NULL)
		{
#ifdef DEBUG
			if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
			return NOTIFY_STATUS_FAILED;
		}

		token = OSAtomicIncrement32((int32_t *)&globals->token_id);
		status = _notify_lib_register_signal(ns_self, name, NOTIFY_CLIENT_SELF, token, sig, 0, 0, &nid);
		if (status != NOTIFY_STATUS_OK)
		{
#ifdef DEBUG
			if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
			return status;
		}

		cid = token;
		client_registration_create(name, namelen, nid, token, cid, SLOT_NONE, NOTIFY_FLAG_SELF | NOTIFY_TYPE_SIGNAL, sig, FD_NONE, MACH_PORT_NULL);

		*out_token = token;
#ifdef DEBUG
		if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
		return NOTIFY_STATUS_OK;
	}

	if (globals->client_opts & NOTIFY_OPT_DISPATCH)
	{
		status = notify_register_dispatch(name, out_token, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^(int unused){ kill(getpid(), sig); });
#ifdef DEBUG
		if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
		return status;
	}

	if (globals->notify_server_port == MACH_PORT_NULL)
	{
		status = _notify_lib_init(EVENT_INIT);
		if (status != 0)
		{
#ifdef DEBUG
			if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
			return NOTIFY_STATUS_FAILED;
		}
	}

	token = OSAtomicIncrement32((int32_t *)&globals->token_id);

	if (globals->notify_ipc_version == 0)
	{
		kstatus = _notify_server_register_signal(globals->notify_server_port, (caddr_t)name, (mach_msg_type_number_t)namelen, sig, (int32_t *)&cid, (int32_t *)&status);
		if (kstatus != KERN_SUCCESS)
		{
#ifdef DEBUG
			if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
			return NOTIFY_STATUS_FAILED;
		}

		if (status != NOTIFY_STATUS_OK)
		{
#ifdef DEBUG
			if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
			return status;
		}
	}
	else
	{
		cid = token;
		kstatus = _notify_server_register_signal_2(globals->notify_server_port, (caddr_t)name, (mach_msg_type_number_t)namelen, token, sig);
		if (kstatus != KERN_SUCCESS)
		{
#ifdef DEBUG
			if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
			return NOTIFY_STATUS_FAILED;
		}
	}

	client_registration_create(name, namelen, NID_UNSET, token, cid, SLOT_NONE, NOTIFY_TYPE_SIGNAL | NOTIFY_FLAG_REGEN, sig, FD_NONE, MACH_PORT_NULL);

	*out_token = token;
#ifdef DEBUG
	if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
	return NOTIFY_STATUS_OK;
}

uint32_t
notify_register_mach_port(const char *name, mach_port_name_t *notify_port, int flags, int *out_token)
{
#ifdef DEBUG
	if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "-> %s\n", __func__);
#endif

	notify_state_t *ns_self;
	kern_return_t kstatus;
	uint32_t status;
	uint64_t nid;
	task_t task;
	int token, mine;
	size_t namelen;
	uint32_t cid, tflags;
	registration_node_t *r;
	name_node_t *n = NULL;
	mach_port_name_t port;
	notify_globals_t globals = _notify_globals();
	bool create_base = false;

	regenerate_check();

	if (name == NULL)
	{
#ifdef DEBUG
		if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
		return NOTIFY_STATUS_INVALID_NAME;
	}

	if (notify_port == NULL)
	{
#ifdef DEBUG
		if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
		return NOTIFY_STATUS_INVALID_PORT;
	}

	mine = 0;
	namelen = strlen(name);

	task = mach_task_self();

	if ((flags & NOTIFY_REUSE) == 0)
	{
		/* caller wants a new port: create a new port with a receive right */
		mine = 1;
		kstatus = mach_port_allocate(task, MACH_PORT_RIGHT_RECEIVE, notify_port);
		if (kstatus != KERN_SUCCESS)
		{
#ifdef DEBUG
			if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
			return NOTIFY_STATUS_FAILED;
		}
	}

	/*
	 * Add a send right to the port.
	 * If we register directly with notifyd, then notifyd will need the send right.
	 * If we use dispatch to forward notifications to this port, the the library needs the send right.
	 */
	kstatus = mach_port_insert_right(task, *notify_port, *notify_port, MACH_MSG_TYPE_MAKE_SEND);
	if (kstatus != KERN_SUCCESS)
	{
		if (mine == 1) mach_port_mod_refs(task, *notify_port, MACH_PORT_RIGHT_RECEIVE, -1);
#ifdef DEBUG
		if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
		return NOTIFY_STATUS_FAILED;
	}

	if (!strncmp(name, SELF_PREFIX, SELF_PREFIX_LEN))
	{
		ns_self = _notify_lib_self_state();
		if (ns_self == NULL)
		{
			if (mine == 1)
			{
				mach_port_mod_refs(task, *notify_port, MACH_PORT_RIGHT_RECEIVE, -1);
			}

			mach_port_deallocate(task, *notify_port);
#ifdef DEBUG
			if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
			return NOTIFY_STATUS_FAILED;
		}

		token = OSAtomicIncrement32((int32_t *)&globals->token_id);
		status = _notify_lib_register_mach_port(ns_self, name, NOTIFY_CLIENT_SELF, token, *notify_port, 0, 0, &nid);
		if (status != NOTIFY_STATUS_OK)
		{
			if (mine == 1)
			{
				mach_port_mod_refs(task, *notify_port, MACH_PORT_RIGHT_RECEIVE, -1);
			}

			mach_port_deallocate(task, *notify_port);
#ifdef DEBUG
			if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
			return status;
		}

		cid = token;
		client_registration_create(name, namelen, nid, token, cid, SLOT_NONE, NOTIFY_FLAG_SELF | NOTIFY_TYPE_PORT, SIGNAL_NONE, FD_NONE, *notify_port);

		*out_token = token;
		notify_retain_mach_port(*notify_port, mine);

#ifdef DEBUG
		if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
		return NOTIFY_STATUS_OK;
	}

	/* initialize if necessary */
	if (globals->notify_server_port == MACH_PORT_NULL)
	{
		status = _notify_lib_init(EVENT_INIT);
		if (status != 0)
		{
			/* error occurred: release receive right (if a new one was allocated) and the send right we added */
			if (mine == 1) mach_port_mod_refs(task, *notify_port, MACH_PORT_RIGHT_RECEIVE, -1);
			mach_port_deallocate(task, *notify_port);
#ifdef DEBUG
			if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
			return NOTIFY_STATUS_FAILED;
		}
	}

	if ((globals->client_opts & NOTIFY_OPT_DISPATCH) && (*notify_port != globals->notify_common_port))
	{
		/*
		 * We'll receive the notification on notify_common_port and forward it
		 * to the caller's port, so we add a send right to the common port.
		 */
		port = globals->notify_common_port;
		kstatus = mach_port_insert_right(task, globals->notify_common_port, globals->notify_common_port, MACH_MSG_TYPE_MAKE_SEND);
		if (kstatus != KERN_SUCCESS)
		{
			if (mine == 1) mach_port_mod_refs(task, *notify_port, MACH_PORT_RIGHT_RECEIVE, -1);
			mach_port_deallocate(task, *notify_port);
#ifdef DEBUG
			if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
			return NOTIFY_STATUS_FAILED;
		}
	}
	else
	{
		/* we'll register to receive the notification on the caller's port */
		port = *notify_port;
	}

	tflags = NOTIFY_TYPE_PORT;
	if (port == globals->notify_common_port) tflags |= NOTIFY_FLAG_REGEN;

	token = -1;
	cid = -1;

	if ((globals->client_opts & NOTIFY_OPT_DISPATCH) && ((flags & NOTIFY_NO_DISPATCH) == 0))
	{
		/*
		 * Dance to protect n->coalesce_base.
		 *
		 * We take globals->notify_lock here.  That prevents any other thread
		 * from looking up and/or creating a name node.  We hold the lock
		 * until the name_node (n) is created if necessary, and n->coalesce_base
		 * has been set if it was NULL. The code below is the only code that
		 * assigns a value to n->coalesce_base, so even if some other thread has
		 * the name node, they won't clobber the value.
		 */
		mutex_lock("global", &globals->notify_lock, __func__, __LINE__);

		n = (name_node_t *)_nc_table_find(globals->name_table, name);
		if (n == NULL)
		{
#ifdef DEBUG
			if (_libnotify_debug & DEBUG_REGISTRATION) _notify_client_log(ASL_LEVEL_NOTICE, "%s %d: name table find %s -> NULL", __func__, __LINE__, name);
#endif
			create_base = true;
		}
		else if (n->coalesce_base == NULL)
		{
#ifdef DEBUG
			if (_libnotify_debug & DEBUG_REGISTRATION) _notify_client_log(ASL_LEVEL_NOTICE, "%s %d: name table find %s -> %p, coalesce_base = NULL", __func__, __LINE__, name, n);
#endif
			create_base = true;
		}
#ifdef DEBUG
		else
		{
			if (_libnotify_debug & DEBUG_REGISTRATION) _notify_client_log(ASL_LEVEL_NOTICE, "%s %d: name table find %s -> %p, coalesce_base = %p", __func__, __LINE__, name, n, n->coalesce_base);
		}
#endif

		if (create_base)
		{
			/* base of coalesced registrations gets a private token */
			token = OSAtomicIncrement32((int32_t *)&globals->token_id);
			cid = token;

			kstatus = _notify_server_register_mach_port_2(globals->notify_server_port, (caddr_t)name, (mach_msg_type_number_t)namelen, token, globals->notify_common_port);
			if (kstatus != KERN_SUCCESS)
			{
				if (mine == 1) mach_port_mod_refs(task, *notify_port, MACH_PORT_RIGHT_RECEIVE, -1);
				mutex_unlock("global", &globals->notify_lock, __func__, __LINE__);
				mach_port_deallocate(task, *notify_port);
#ifdef DEBUG
				if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
				return NOTIFY_STATUS_FAILED;
			}

			base_registration_create(name, namelen, NID_UNSET, token, tflags, globals->notify_common_port);
		}

		/* we will need a pointer to the name node below - look it up while we still have the global lock */
		n = (name_node_t *)_nc_table_find(globals->name_table, name);

		/* release globals->notify_lock here */
		mutex_unlock("global", &globals->notify_lock, __func__, __LINE__);

		/* caller's token */
		token = OSAtomicIncrement32((int32_t *)&globals->token_id);
		cid = token;
		tflags |= NOTIFY_TYPE_COALESCED;
	}
	else
	{
		if (globals->notify_ipc_version == 0)
		{
			kstatus = _notify_server_register_mach_port(globals->notify_server_port, (caddr_t)name, (mach_msg_type_number_t)namelen, port, token, (int32_t *)&cid, (int32_t *)&status);
			if ((kstatus == KERN_SUCCESS) && (status != NOTIFY_STATUS_OK)) kstatus = KERN_FAILURE;
		}
		else
		{
			token = OSAtomicIncrement32((int32_t *)&globals->token_id);
			cid = token;

			kstatus = _notify_server_register_mach_port_2(globals->notify_server_port, (caddr_t)name, (mach_msg_type_number_t)namelen, token, port);
		}

		if (kstatus != KERN_SUCCESS)
		{
			if (mine == 1) mach_port_mod_refs(task, *notify_port, MACH_PORT_RIGHT_RECEIVE, -1);
			mach_port_deallocate(task, *notify_port);
#ifdef DEBUG
			if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
			return NOTIFY_STATUS_FAILED;
		}
	}

	client_registration_create(name, namelen, NID_UNSET, token, cid, SLOT_NONE, tflags, SIGNAL_NONE, FD_NONE, *notify_port);

	/*
	 * If this call to notify_register_mach_port created the coalesce_base registration,
	 * we can now release it.  It is created with a refcount of 1, and it gets retained
	 * by the coalesced registration created in client_registration_create above.  We
	 * want the refcount to be equal to the number of registrations in the coalesce list.
	 */
	if (create_base && (n != NULL)) registration_node_release(n->coalesce_base);

	if ((globals->client_opts & NOTIFY_OPT_DISPATCH) && (*notify_port != globals->notify_common_port))
	{
		r = registration_node_find(token);
		if (r == NULL)
		{
#ifdef DEBUG
			if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
			return NOTIFY_STATUS_FAILED;
		}

		/* remember to release the send right when this gets cancelled */
		r->flags |= NOTIFY_FLAG_RELEASE_SEND;

		port = *notify_port;
		r->queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
		dispatch_retain(r->queue);
		r->block = (notify_handler_t)Block_copy(^(int unused){
			mach_msg_empty_send_t msg;
			kern_return_t kstatus;


			/* send empty message to the port with msgh_id = token; */
			memset(&msg, 0, sizeof(msg));
			msg.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSGH_BITS_ZERO);
			msg.header.msgh_remote_port = port;
			msg.header.msgh_local_port = MACH_PORT_NULL;
			msg.header.msgh_size = sizeof(mach_msg_empty_send_t);
			msg.header.msgh_id = token;

			kstatus = mach_msg(&msg.header, MACH_SEND_MSG | MACH_SEND_TIMEOUT, msg.header.msgh_size, 0, MACH_PORT_NULL, 0, MACH_PORT_NULL);
			if (kstatus == MACH_SEND_TIMED_OUT)
			{
				dispatch_once(&globals->make_background_send_queue_once, ^{
					globals->background_send_queue = dispatch_queue_create("com.apple.notify.background.local.notification", NULL);
				});

				if (globals->background_send_queue != NULL) dispatch_async(globals->background_send_queue, ^{
					mach_msg_empty_send_t msg;
					kern_return_t kstatus;

					/* send empty message to the port with msgh_id = token; */
					memset(&msg, 0, sizeof(msg));
					msg.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSGH_BITS_ZERO);
					msg.header.msgh_remote_port = port;
					msg.header.msgh_local_port = MACH_PORT_NULL;
					msg.header.msgh_size = sizeof(mach_msg_empty_send_t);
					msg.header.msgh_id = token;

					kstatus = mach_msg(&msg.header, MACH_SEND_MSG, msg.header.msgh_size, 0, MACH_PORT_NULL, 0, MACH_PORT_NULL);
				});
			}
		});

		registration_node_release(r);
	}

	*out_token = token;
	notify_retain_mach_port(*notify_port, mine);

#ifdef DEBUG
	if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
	return NOTIFY_STATUS_OK;
}

static char *
_notify_mk_tmp_path(int tid)
{
#if TARGET_OS_EMBEDDED
	int freetmp = 0;
	char *path, *tmp = getenv("TMPDIR");

	if (tmp == NULL)
	{
		asprintf(&tmp, "/tmp/com.apple.notify.%d", geteuid());
		mkdir(tmp, 0755);
		freetmp = 1;
	}

	if (tmp == NULL) return NULL;

	asprintf(&path, "%s/com.apple.notify.%d.%d", tmp, getpid(), tid);
	if (freetmp) free(tmp);
	return path;
#else
	char tmp[PATH_MAX], *path;

	if (confstr(_CS_DARWIN_USER_TEMP_DIR, tmp, sizeof(tmp)) <= 0) return NULL;
#endif

	path = NULL;
	asprintf(&path, "%s/com.apple.notify.%d.%d", tmp, getpid(), tid);
	return path;
}

uint32_t
notify_register_file_descriptor(const char *name, int *notify_fd, int flags, int *out_token)
{
#ifdef DEBUG
	if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "-> %s\n", __func__);
#endif

	notify_state_t *ns_self;
	uint32_t i, status;
	uint64_t nid;
	int token, mine, fdpair[2];
	size_t namelen;
	fileport_t fileport;
	kern_return_t kstatus;
	uint32_t cid;
	notify_globals_t globals = _notify_globals();

	regenerate_check();

	mine = 0;

	if (name == NULL)
	{
#ifdef DEBUG
		if (_libnotify_debug & DEBUG_USER) _notify_client_log(ASL_LEVEL_ERR, "notify_register_file_descriptor name=NULL\n");
		if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
		return NOTIFY_STATUS_INVALID_NAME;
	}

	if (notify_fd == NULL)
	{
#ifdef DEBUG
		if (_libnotify_debug & DEBUG_USER) _notify_client_log(ASL_LEVEL_ERR, "notify_register_file_descriptor notify_fd=NULL\n");
		if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
		return NOTIFY_STATUS_INVALID_FILE;
	}

	namelen = strlen(name);

	if ((flags & NOTIFY_REUSE) == 0)
	{
		if (pipe(fdpair) < 0)
		{
#ifdef DEBUG
			if (_libnotify_debug & DEBUG_USER) _notify_client_log(ASL_LEVEL_ERR, "notify_register_file_descriptor %s [non-reused[ pipe failed errno=%d [%s]\n, name, errno, strerror(errno)");
			if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
			return NOTIFY_STATUS_FAILED;
		}

		mine = 1;
		*notify_fd = fdpair[0];
	}
	else
	{
		/* check the file descriptor - it must be one of "ours" */
		for (i = 0; i < globals->fd_count; i++)
		{
			if (globals->fd_clnt[i] == *notify_fd) break;
		}

		if (i >= globals->fd_count)
		{
#ifdef DEBUG
			if (_libnotify_debug & DEBUG_USER) _notify_client_log(ASL_LEVEL_ERR, "notify_register_file_descriptor %s [reused] file not found\n", name);
			if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
			return NOTIFY_STATUS_INVALID_FILE;
		}

		fdpair[0] = globals->fd_clnt[i];
		fdpair[1] = globals->fd_srv[i];
	}

	if (!strncmp(name, SELF_PREFIX, SELF_PREFIX_LEN))
	{
		ns_self = _notify_lib_self_state();
		if (ns_self == NULL)
		{
			if (mine == 1)
			{
				close(fdpair[0]);
				close(fdpair[1]);
			}

#ifdef DEBUG
			if (_libnotify_debug & DEBUG_USER) _notify_client_log(ASL_LEVEL_ERR, "notify_register_file_descriptor %s [self] _notify_lib_self_state failed\n", name);
			if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
			return NOTIFY_STATUS_FAILED;
		}

		token = OSAtomicIncrement32((int32_t *)&globals->token_id);
		status = _notify_lib_register_file_descriptor(ns_self, name, NOTIFY_CLIENT_SELF, token, fdpair[1], 0, 0, &nid);
		if (status != NOTIFY_STATUS_OK)
		{
			if (mine == 1)
			{
				close(fdpair[0]);
				close(fdpair[1]);
			}

#ifdef DEBUG
			if (_libnotify_debug & DEBUG_USER) _notify_client_log(ASL_LEVEL_ERR, "notify_register_file_descriptor %s [self] _notify_lib_register_file_descriptor failed status=%u\n", name, status);
			if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
			return status;
		}

		cid = token;
		client_registration_create(name, namelen, nid, token, cid, SLOT_NONE, NOTIFY_FLAG_SELF | NOTIFY_TYPE_FILE, SIGNAL_NONE, *notify_fd, MACH_PORT_NULL);

		*out_token = token;
		notify_retain_file_descriptor(fdpair[0], fdpair[1]);

#ifdef DEBUG
		if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
		return NOTIFY_STATUS_OK;
	}

	if (globals->client_opts & NOTIFY_OPT_DISPATCH)
	{
		/*
		 * Use dispatch to do a write() on fdpair[1] when notified.
		 */
		status = notify_register_mux_fd(name, out_token, fdpair[0], fdpair[1]);
		if (status != NOTIFY_STATUS_OK)
		{
			if (mine == 1)
			{
				close(fdpair[0]);
				close(fdpair[1]);
			}

#ifdef DEBUG
			if (_libnotify_debug & DEBUG_USER) _notify_client_log(ASL_LEVEL_ERR, "notify_register_file_descriptor %s notify_register_mux_fd failed status=%u\n", name, status);
			if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
			return status;
		}

		notify_retain_file_descriptor(fdpair[0], fdpair[1]);
#ifdef DEBUG
		if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
		return NOTIFY_STATUS_OK;
	}

	if (globals->notify_server_port == MACH_PORT_NULL)
	{
		status = _notify_lib_init(EVENT_INIT);
		if (status != 0)
		{
			if (mine == 1)
			{
				close(fdpair[0]);
				close(fdpair[1]);
			}

#ifdef DEBUG
			if (_libnotify_debug & DEBUG_USER) _notify_client_log(ASL_LEVEL_ERR, "notify_register_file_descriptor %s _notify_lib_init failed status=%u\n", name, status);
			if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
			return NOTIFY_STATUS_FAILED;
		}
	}

	/* send fdpair[1] (the sender's fd) to notifyd using a fileport */
	fileport = MACH_PORT_NULL;
	if (fileport_makeport(fdpair[1], (fileport_t *)&fileport) < 0)
	{
		if (mine == 1)
		{
			close(fdpair[0]);
			close(fdpair[1]);
		}

#ifdef DEBUG
		if (_libnotify_debug & DEBUG_USER) _notify_client_log(ASL_LEVEL_ERR, "notify_register_file_descriptor %s fileport_makeport failed errno=%d [%s]\n", name, errno, strerror(errno));
		if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
		return NOTIFY_STATUS_FAILED;
	}

	token = OSAtomicIncrement32((int32_t *)&globals->token_id);

	if (globals->notify_ipc_version == 0)
	{
		kstatus = _notify_server_register_file_descriptor(globals->notify_server_port, (caddr_t)name, (mach_msg_type_number_t)namelen, (mach_port_t)fileport, token, (int32_t *)&cid, (int32_t *)&status);
		if ((kstatus == KERN_SUCCESS) && (status != NOTIFY_STATUS_OK))
		{
#ifdef DEBUG
			if (_libnotify_debug & DEBUG_USER) _notify_client_log(ASL_LEVEL_ERR, "notify_register_file_descriptor %s [IPC version 0] _notify_server_register_file_descriptor returned status=%u\n", name, status);
			if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
			kstatus = KERN_FAILURE;
		}
	}
	else
	{
		kstatus = _notify_server_register_file_descriptor_2(globals->notify_server_port, (caddr_t)name, (mach_msg_type_number_t)namelen, token, (mach_port_t)fileport);
	}

	if (kstatus != KERN_SUCCESS)
	{
		if (mine == 1)
		{
			close(fdpair[0]);
			close(fdpair[1]);
		}

#ifdef DEBUG
		if (_libnotify_debug & DEBUG_USER) _notify_client_log(ASL_LEVEL_ERR, "notify_register_file_descriptor %s  _notify_server_register_file_descriptor[_2] failed status=0x%08xu\n", name, kstatus);
		if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
		return NOTIFY_STATUS_FAILED;
	}

	client_registration_create(name, namelen, NID_UNSET, token, cid, SLOT_NONE, NOTIFY_TYPE_FILE, SIGNAL_NONE, *notify_fd, MACH_PORT_NULL);

	*out_token = token;
	notify_retain_file_descriptor(fdpair[0], fdpair[1]);

#ifdef DEBUG
	if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
	return NOTIFY_STATUS_OK;
}

uint32_t
notify_check(int token, int *check)
{
#ifdef DEBUG
	if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "-> %s\n", __func__);
#endif

	kern_return_t kstatus;
	uint32_t status = NOTIFY_STATUS_OK;
	registration_node_t *r;
	uint32_t tid;
	notify_globals_t globals = _notify_globals();

	regenerate_check();

	if (check == NULL)
	{
#ifdef DEBUG
		if (_libnotify_debug & DEBUG_USER) _notify_client_log(ASL_LEVEL_ERR, "notify_check check=NULL\n", token);
		if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
		return NOTIFY_STATUS_FAILED;
	}

	r = registration_node_find(token);
	if (r == NULL)
	{
#ifdef DEBUG
		if (_libnotify_debug & DEBUG_USER) _notify_client_log(ASL_LEVEL_ERR, "notify_check token %d registration node not found\n", token);
		if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
		return NOTIFY_STATUS_INVALID_TOKEN;
	}

	if (r->flags & NOTIFY_FLAG_SELF)
	{
		/* _notify_lib_check returns NOTIFY_STATUS_FAILED if self_state is NULL */
		status = _notify_lib_check(globals->self_state, NOTIFY_CLIENT_SELF, token, check);
#ifdef DEBUG
		if (_libnotify_debug & DEBUG_USER) _notify_client_log(ASL_LEVEL_ERR, "notify_check token %d self registration _notify_lib_check status %u check %d\n", token, status, *check);
		if ((_libnotify_debug & DEBUG_USER) && (status != NOTIFY_STATUS_FAILED)) _notify_client_log(ASL_LEVEL_ERR, "notify_check token %d [self] _notify_lib_check failed status=%u\n", token, status);
#endif
		goto release_and_return;
	}

	if (r->flags & NOTIFY_TYPE_MEMORY)
	{
		if (globals->shm_base == NULL)
		{
			status = NOTIFY_STATUS_FAILED;
#ifdef DEBUG
			if (_libnotify_debug & DEBUG_USER) _notify_client_log(ASL_LEVEL_ERR, "notify_check token %d [memory] globals->shm_base is NULL\n", token);
#endif
			goto release_and_return;
		}

		*check = 0;

		OSSpinLockLock(&(globals->checkLock));
		if (r->val != globals->shm_base[r->slot])
		{
			*check = 1;
			r->val = globals->shm_base[r->slot];
		}
		OSSpinLockUnlock(&(globals->checkLock));

		status = NOTIFY_STATUS_OK;
#ifdef DEBUG
		if (_libnotify_debug & DEBUG_USER) _notify_client_log(ASL_LEVEL_ERR, "notify_check token %d shared memory check status %u check %d\n", token, status, *check);
#endif
		goto release_and_return;
	}

#ifdef DEBUG
	if (_libnotify_debug & DEBUG_USER) _notify_client_log(ASL_LEVEL_ERR, "notify_check token %d in not a shared memory registration\n", token);
#endif

	if (globals->notify_server_port == MACH_PORT_NULL)
	{
		status = _notify_lib_init(EVENT_INIT);
		if (status != 0)
		{
#ifdef DEBUG
			if (_libnotify_debug & DEBUG_USER) _notify_client_log(ASL_LEVEL_ERR, "notify_check token %d [non-memory] _notify_lib_init failed status=%u\n", token, status);
#endif
			goto release_and_return;
		}
	}

	tid = token;
	if (globals->notify_ipc_version == 0) tid = r->client_id;
	if (r->flags & NOTIFY_TYPE_COALESCED) tid = r->name_node->coalesce_base_token;

	kstatus = _notify_server_check(globals->notify_server_port, tid, check, (int32_t *)&status);
#ifdef DEBUG
	if (_libnotify_debug & DEBUG_USER) _notify_client_log(ASL_LEVEL_ERR, "notify_check token %d [%d] _notify_server_check kstatus 0x%08x status %u check %d\n", token, tid, kstatus, status, *check);
#endif
	if (kstatus != KERN_SUCCESS)
	{
		status = NOTIFY_STATUS_FAILED;
#ifdef DEBUG
		if (_libnotify_debug & DEBUG_USER) _notify_client_log(ASL_LEVEL_ERR, "notify_check token %d [non-memory] _notify_server_check failed kstatus=0x%08x status=%u\n", token, kstatus, status);
#endif
	}

release_and_return:

	registration_node_release(r);

#ifdef DEBUG
	if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d] status %u check %d\n", __func__, __LINE__ + 2, status, *check);
#endif
	return status;
}

uint32_t
notify_peek(int token, uint32_t *val)
{
#ifdef DEBUG
	if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "-> %s\n", __func__);
#endif

	registration_node_t *r;
	uint32_t status;
	notify_globals_t globals = _notify_globals();

	regenerate_check();

	r = registration_node_find(token);
	if (r == NULL)
	{
#ifdef DEBUG
		if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
		return NOTIFY_STATUS_INVALID_TOKEN;
	}

	if (r->flags & NOTIFY_FLAG_SELF)
	{
		/* _notify_lib_peek returns NOTIFY_STATUS_FAILED if self_state is NULL */
		status = _notify_lib_peek(globals->self_state, NOTIFY_CLIENT_SELF, token, (int *)val);
		goto release_and_return;
	}

	status = NOTIFY_STATUS_INVALID_REQUEST;

	if (r->flags & NOTIFY_TYPE_MEMORY)
	{
		if (globals->shm_base == NULL)
		{
			status = NOTIFY_STATUS_FAILED;
			goto release_and_return;
		}

		*val = globals->shm_base[r->slot];
		status = NOTIFY_STATUS_OK;
	}

release_and_return:

	registration_node_release(r);

#ifdef DEBUG
	if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
	return status;
}

int *
notify_check_addr(int token)
{
#ifdef DEBUG
	if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "-> %s\n", __func__);
#endif

	registration_node_t *r;
	int *val = NULL;
	notify_globals_t globals = _notify_globals();

	regenerate_check();

	r = registration_node_find(token);
	if (r == NULL)
	{
#ifdef DEBUG
		if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
		return NULL;
	}

	if (r->flags & NOTIFY_FLAG_SELF)
	{
		/* _notify_lib_check_addr returns NOTIFY_STATUS_FAILED if self_state is NULL */
		val = _notify_lib_check_addr(globals->self_state, NOTIFY_CLIENT_SELF, token);
		goto release_and_return;
	}

	if ((r->flags & NOTIFY_TYPE_MEMORY) && (globals->shm_base != NULL)) val = (int *)&(globals->shm_base[r->slot]);

release_and_return:

	registration_node_release(r);
#ifdef DEBUG
	if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
	return val;
}

uint32_t
notify_monitor_file(int token, char *path, int flags)
{
#ifdef DEBUG
	if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "-> %s\n", __func__);
#endif

	kern_return_t kstatus;
	uint32_t status, len;
	registration_node_t *r;
	char *dup;
	notify_globals_t globals = _notify_globals();

	regenerate_check();

	if (path == NULL)
	{
#ifdef DEBUG
		if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
		return NOTIFY_STATUS_INVALID_REQUEST;
	}

	r = registration_node_find(token);
	if (r == NULL)
	{
#ifdef DEBUG
		if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
		return NOTIFY_STATUS_INVALID_TOKEN;
	}

	if (r->flags & NOTIFY_FLAG_SELF)
	{
		registration_node_release(r);
#ifdef DEBUG
		if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
		return NOTIFY_STATUS_INVALID_REQUEST;
	}

	/* can only monitor one path with a token */
	if (r->path != NULL)
	{
		registration_node_release(r);
#ifdef DEBUG
		if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
		return NOTIFY_STATUS_INVALID_REQUEST;
	}

	if (globals->notify_server_port == MACH_PORT_NULL)
	{
		status = _notify_lib_init(EVENT_INIT);
		if (status != 0)
		{
			registration_node_release(r);
#ifdef DEBUG
			if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
			return NOTIFY_STATUS_FAILED;
		}
	}

	len = (uint32_t)strlen(path);
	dup = strdup(path);
	if (dup == NULL)
	{
#ifdef DEBUG
		if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
		return NOTIFY_STATUS_FAILED;
	}

	if (globals->notify_ipc_version == 0)
	{
		kstatus = _notify_server_monitor_file(globals->notify_server_port, r->client_id, path, len, flags, (int32_t *)&status);
		if ((kstatus == KERN_SUCCESS) && (status != NOTIFY_STATUS_OK)) kstatus = KERN_FAILURE;
	}
	else
	{
		int xtoken = token;
		if (r->flags & NOTIFY_TYPE_COALESCED) xtoken = r->name_node->coalesce_base_token;

		kstatus = _notify_server_monitor_file_2(globals->notify_server_port, xtoken, path, len, flags);
	}

	r->path = dup;
	r->path_flags = flags;

	registration_node_release(r);

	if (kstatus != KERN_SUCCESS)
	{
#ifdef DEBUG
		if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
		return NOTIFY_STATUS_FAILED;
	}

#ifdef DEBUG
	if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
	return NOTIFY_STATUS_OK;
}

uint32_t
notify_get_event(int token, int *ev, char *buf, int *len)
{
#ifdef DEBUG
	if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "-> %s\n", __func__);
#endif

	if (ev != NULL) *ev = 0;
	if (len != NULL) *len = 0;

#ifdef DEBUG
	if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
	return NOTIFY_STATUS_OK;
}

uint32_t
notify_get_state(int token, uint64_t *state)
{
#ifdef DEBUG
	if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "-> %s\n", __func__);
#endif

	kern_return_t kstatus;
	uint32_t status;
	registration_node_t *r;
	uint64_t nid;
	notify_globals_t globals = _notify_globals();

	regenerate_check();

	r = registration_node_find(token);
	if (r == NULL)
	{
#ifdef DEBUG
		if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
		return NOTIFY_STATUS_INVALID_TOKEN;
	}

	if (r->name_node == NULL)
	{
		registration_node_release(r);
#ifdef DEBUG
		if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
		return NOTIFY_STATUS_INVALID_TOKEN;
	}

	if (r->flags & NOTIFY_FLAG_SELF)
	{
		/* _notify_lib_get_state returns NOTIFY_STATUS_FAILED if self_state is NULL */
		status = _notify_lib_get_state(globals->self_state, r->name_node->name_id, state, 0, 0);
		registration_node_release(r);
#ifdef DEBUG
		if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
		return status;
	}

	if (globals->notify_server_port == MACH_PORT_NULL)
	{
		status = _notify_lib_init(EVENT_INIT);
		if (status != 0)
		{
			registration_node_release(r);
#ifdef DEBUG
			if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
			return NOTIFY_STATUS_FAILED;
		}
	}

	if (globals->notify_ipc_version == 0)
	{
		kstatus = _notify_server_get_state(globals->notify_server_port, r->client_id, state, (int32_t *)&status);
		if ((kstatus == KERN_SUCCESS) && (status != NOTIFY_STATUS_OK)) kstatus = KERN_FAILURE;
	}
	else
	{
		int xtoken = token;
		if (r->flags & NOTIFY_TYPE_COALESCED) xtoken = r->name_node->coalesce_base_token;

		nid = r->name_node->name_id;
		if ((nid == NID_UNSET) || (nid == NID_CALLED_ONCE))
		{
			kstatus = _notify_server_get_state_3(globals->notify_server_port, xtoken, state, (uint64_t *)&nid, (int32_t *)&status);
			if ((kstatus == KERN_SUCCESS) && (status == NOTIFY_STATUS_OK)) name_node_set_nid(r->name_node, nid);
		}
		else
		{
			kstatus = _notify_server_get_state_2(globals->notify_server_port, r->name_node->name_id, state, (int32_t *)&status);
		}
	}

	registration_node_release(r);
	if (kstatus != KERN_SUCCESS)
	{
#ifdef DEBUG
		if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
		return NOTIFY_STATUS_FAILED;
	}

#ifdef DEBUG
	if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
	return status;
}

uint32_t
notify_set_state(int token, uint64_t state)
{
#ifdef DEBUG
	if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "-> %s\n", __func__);
#endif

	kern_return_t kstatus;
	uint32_t status;
	registration_node_t *r;
	uint64_t nid;
	notify_globals_t globals = _notify_globals();

	regenerate_check();

	r = registration_node_find(token);
	if (r == NULL)
	{
#ifdef DEBUG
		if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
		return NOTIFY_STATUS_INVALID_TOKEN;
	}

	if (r->name_node == NULL)
	{
		registration_node_release(r);
#ifdef DEBUG
		if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
		return NOTIFY_STATUS_INVALID_TOKEN;
	}

	if (r->flags & NOTIFY_FLAG_SELF)
	{
		/* _notify_lib_set_state returns NOTIFY_STATUS_FAILED if self_state is NULL */
		status = _notify_lib_set_state(globals->self_state, r->name_node->name_id, state, 0, 0);
		registration_node_release(r);
#ifdef DEBUG
		if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
		return status;
	}

	if (globals->notify_server_port == MACH_PORT_NULL)
	{
		status = _notify_lib_init(EVENT_INIT);
		if (status != 0)
		{
			registration_node_release(r);
#ifdef DEBUG
			if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
			return NOTIFY_STATUS_FAILED;
		}
	}

	status = NOTIFY_STATUS_OK;

	if (globals->notify_ipc_version == 0)
	{
		kstatus = _notify_server_set_state(globals->notify_server_port, r->client_id, state, (int32_t *)&status);
		if ((kstatus == KERN_SUCCESS) && (status != NOTIFY_STATUS_OK)) kstatus = KERN_FAILURE;
	}
	else
	{
		int xtoken = token;
		if (r->flags & NOTIFY_TYPE_COALESCED) xtoken = r->name_node->coalesce_base_token;

		nid = r->name_node->name_id;
		if ((nid == NID_UNSET) || (nid == NID_CALLED_ONCE))
		{
			kstatus = _notify_server_set_state_3(globals->notify_server_port, xtoken, state, (uint64_t *)&nid, (int32_t *)&status);
			if ((kstatus == KERN_SUCCESS) && (status == NOTIFY_STATUS_OK)) name_node_set_nid(r->name_node, nid);
		}
		else
		{
			status = NOTIFY_STATUS_OK;
			kstatus = _notify_server_set_state_2(globals->notify_server_port, r->name_node->name_id, state);
		}
	}

	if ((kstatus == KERN_SUCCESS) && (status == NOTIFY_STATUS_OK))
	{
		r->set_state_time = mach_absolute_time();
		r->set_state_val = state;
	}

	registration_node_release(r);
	if (kstatus != KERN_SUCCESS)
	{
#ifdef DEBUG
		if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
		return NOTIFY_STATUS_FAILED;
	}

#ifdef DEBUG
	if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
	return NOTIFY_STATUS_OK;
}

uint32_t
notify_cancel(int token)
{
#ifdef DEBUG
	if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "-> %s\n", __func__);
#endif

	registration_node_t *r;
	uint32_t status;
	kern_return_t kstatus = KERN_SUCCESS;
	notify_globals_t globals = _notify_globals();

	regenerate_check();

	mutex_lock("global", &globals->notify_lock, __func__, __LINE__);

	r = (registration_node_t *)_nc_table_find_n(globals->registration_table, token);
	if (r == NULL)
	{
		mutex_unlock("global", &globals->notify_lock, __func__, __LINE__);
#ifdef DEBUG
		if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
		return NOTIFY_STATUS_INVALID_TOKEN;
	}

	if (OSAtomicDecrement32(&r->refcount) > 0)
	{
#ifdef DEBUG
		if (_libnotify_debug & (DEBUG_NODES | DEBUG_CANCEL)) _notify_client_log(ASL_LEVEL_NOTICE, "notify_cancel token %u refcount %d flags 0x%08x %p", r->token, r->refcount, r->flags, r);
#endif

		mutex_unlock("global", &globals->notify_lock, __func__, __LINE__);
#ifdef DEBUG
		if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
		return NOTIFY_STATUS_OK;
	}

	/* refcount is zero */

	_nc_table_delete_n(globals->registration_table, r->token);
	mutex_unlock("global", &globals->notify_lock, __func__, __LINE__);

	if (r->flags & NOTIFY_FLAG_SELF)
	{
		/*
		 * _notify_lib_cancel returns NOTIFY_STATUS_FAILED if self_state is NULL
		 * We let it fail quietly.
		 */
		_notify_lib_cancel(globals->self_state, NOTIFY_CLIENT_SELF, r->token);
		registration_node_free(r);
#ifdef DEBUG
		if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
		return NOTIFY_STATUS_OK;
	}

	if (r->flags & NOTIFY_TYPE_COALESCE_BASE)
	{
#ifdef DEBUG
		if (_libnotify_debug & DEBUG_CANCEL) _notify_client_log(ASL_LEVEL_NOTICE, "notify_cancel token %d NOTIFY_TYPE_COALESCE_BASE", token);
#endif
		registration_node_free(r);
#ifdef DEBUG
		if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
		return NOTIFY_STATUS_OK;
	}

	if (globals->notify_ipc_version == 0)
	{
		kstatus = _notify_server_cancel(globals->notify_server_port, r->client_id, (int32_t *)&status);
		if ((kstatus == KERN_SUCCESS) && (status != NOTIFY_STATUS_OK)) kstatus = KERN_FAILURE;
	}
	else if ((r->flags & NOTIFY_TYPE_COALESCED) == 0)
	{
		kstatus = _notify_server_cancel_2(globals->notify_server_port, token);
	}

#ifdef DEBUG
	if (_libnotify_debug & DEBUG_CANCEL) _notify_client_log(ASL_LEVEL_NOTICE, "notify_cancel token %d reg_node %p has been cancelled", token, r);
#endif

	registration_node_free(r);

	if ((kstatus == MIG_SERVER_DIED) || (kstatus == MACH_SEND_INVALID_DEST))
	{
#ifdef DEBUG
		if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
		return NOTIFY_STATUS_OK;
	}
	else if (kstatus != KERN_SUCCESS)
	{
#ifdef DEBUG
		if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
		return NOTIFY_STATUS_FAILED;
	}

#ifdef DEBUG
	if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
	return NOTIFY_STATUS_OK;
}

uint32_t
notify_suspend(int token)
{
#ifdef DEBUG
	if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "-> %s\n", __func__);
#endif

	registration_node_t *r;
	uint32_t status, tid;
	kern_return_t kstatus;
	notify_globals_t globals = _notify_globals();

	regenerate_check();

	r = registration_node_find(token);
	if (r == NULL)
	{
#ifdef DEBUG
		if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
		return NOTIFY_STATUS_INVALID_TOKEN;
	}

	if (r->flags & NOTIFY_FLAG_SELF)
	{
		_notify_lib_suspend(globals->self_state, NOTIFY_CLIENT_SELF, r->token);
		registration_node_release(r);
#ifdef DEBUG
		if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
		return NOTIFY_STATUS_OK;
	}

	if (globals->notify_server_port == MACH_PORT_NULL)
	{
		status = _notify_lib_init(EVENT_INIT);
		if (status != 0)
		{
			registration_node_release(r);
#ifdef DEBUG
			if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
			return NOTIFY_STATUS_FAILED;
		}
	}

	int xtoken = token;
	if (r->flags & NOTIFY_TYPE_COALESCED) xtoken = r->name_node->coalesce_base_token;

	tid = xtoken;
	if (globals->notify_ipc_version == 0) tid = r->client_id;
	if (r->flags & NOTIFY_TYPE_COALESCED) tid = r->name_node->coalesce_base_token;

	kstatus = _notify_server_suspend(globals->notify_server_port, tid, (int32_t *)&status);

	registration_node_release(r);
	if (kstatus != KERN_SUCCESS) status = NOTIFY_STATUS_FAILED;
#ifdef DEBUG
	if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
	return status;
}

uint32_t
notify_resume(int token)
{
#ifdef DEBUG
	if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "-> %s\n", __func__);
#endif

	registration_node_t *r;
	uint32_t status, tid;
	kern_return_t kstatus;
	notify_globals_t globals = _notify_globals();

	regenerate_check();

	r = registration_node_find(token);
	if (r == NULL)
	{
#ifdef DEBUG
		if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
		return NOTIFY_STATUS_INVALID_TOKEN;
	}

	if (r->flags & NOTIFY_FLAG_SELF)
	{
		_notify_lib_resume(globals->self_state, NOTIFY_CLIENT_SELF, r->token);
		registration_node_release(r);
#ifdef DEBUG
		if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
		return NOTIFY_STATUS_OK;
	}

	if (globals->notify_server_port == MACH_PORT_NULL)
	{
		status = _notify_lib_init(EVENT_INIT);
		if (status != 0)
		{
			registration_node_release(r);
#ifdef DEBUG
			if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
			return NOTIFY_STATUS_FAILED;
		}
	}

	int xtoken = token;
	if (r->flags & NOTIFY_TYPE_COALESCED) xtoken = r->name_node->coalesce_base_token;

	tid = xtoken;
	if (globals->notify_ipc_version == 0) tid = r->client_id;
	if (r->flags & NOTIFY_TYPE_COALESCED) tid = r->name_node->coalesce_base_token;

	kstatus = _notify_server_resume(globals->notify_server_port, tid, (int32_t *)&status);

	registration_node_release(r);
	if (kstatus != KERN_SUCCESS) status = NOTIFY_STATUS_FAILED;
#ifdef DEBUG
	if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
	return status;
}

uint32_t
notify_suspend_pid(pid_t pid)
{
#ifdef DEBUG
	if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "-> %s\n", __func__);
#endif

	uint32_t status;
	kern_return_t kstatus;
	notify_globals_t globals = _notify_globals();

	if (globals->notify_server_port == MACH_PORT_NULL)
	{
		status = _notify_lib_init(EVENT_INIT);
		if (status != 0)
		{
#ifdef DEBUG
			if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
			return NOTIFY_STATUS_FAILED;
		}
	}

	kstatus = _notify_server_suspend_pid(globals->notify_server_port, pid, (int32_t *)&status);

	if (kstatus != KERN_SUCCESS) status = NOTIFY_STATUS_FAILED;
#ifdef DEBUG
	if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
	return status;
}

uint32_t
notify_resume_pid(pid_t pid)
{
#ifdef DEBUG
	if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "-> %s\n", __func__);
#endif

	uint32_t status;
	kern_return_t kstatus;
	notify_globals_t globals = _notify_globals();

	if (globals->notify_server_port == MACH_PORT_NULL)
	{
		status = _notify_lib_init(EVENT_INIT);
		if (status != 0)
		{
#ifdef DEBUG
			if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
			return NOTIFY_STATUS_FAILED;
		}
	}

	kstatus = _notify_server_resume_pid(globals->notify_server_port, pid, (int32_t *)&status);

	if (kstatus != KERN_SUCCESS) status = NOTIFY_STATUS_FAILED;
#ifdef DEBUG
	if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
	return status;
}

/* Deprecated SPI */
uint32_t
notify_simple_post(const char *name)
{
#ifdef DEBUG
	if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "-> %s\n", __func__);
#endif

	uint32_t status = notify_post(name);
#ifdef DEBUG
	if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
	return status;
}