darling-Libnotify/notify_client.c
2022-11-20 18:42:59 -08:00

4405 lines
133 KiB
C

/*
* 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 <stdatomic.h>
#include <os/alloc_once_private.h>
#include <os/lock_private.h>
#include <os/reason_private.h>
#include <os/variant_private.h>
#include <TargetConditionals.h>
#include <AvailabilityMacros.h>
#include <Block.h>
#include <dispatch/dispatch.h>
#include <dispatch/private.h>
#include <xpc/private.h>
#include <_simple.h>
#include <mach-o/dyld_priv.h> // _dyld_is_memory_immutable
#include "libnotify.h"
#include "notify.h"
#include "notify_internal.h"
#include "notify_ipc.h"
#include "notify_private.h"
#include "notify_probes.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 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 500
#define NID_UNSET 0xffffffffffffffffL
#define NID_CALLED_ONCE 0xfffffffffffffffeL
// If connection to notifyd tries, we retry a total of
// NOTIFY_SERVER_RETRY_NUMBER times, waiting in between each attempt for
// NOTIFY_SERVER_RETRY_WAIT_US microseconds.
#define NOTIFY_SERVER_RETRY_WAIT_US 100000
#define NOTIFY_SERVER_RETRY_NUMBER 50
/*
* 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
{
uint64_t name_id;
TAILQ_HEAD(, __registration_node_s) coalesced;
struct __registration_node_s *coalesce_base;
char *name;
os_unfair_lock lock;
atomic_uint_fast32_t refcount;
uint32_t coalesce_base_token;
bool has_been_warned;
bool needs_free;
} 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
{
TAILQ_ENTRY(__registration_node_s) registration_coalesced_entry;
atomic_uint_fast32_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_or_xtra_mp;
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_server_restart_handler(void *ctxt);
static void notify_retain_mach_port(notify_globals_t globals, mach_port_t mp, int flags);
static void _notify_dispatch_handle(void *context);
static void registration_node_release(registration_node_t *r);
static void registration_node_release_locked(notify_globals_t globals, registration_node_t *r);
static void notify_release_file_descriptor_locked(notify_globals_t globals, int fd);
static void notify_release_mach_port_locked(notify_globals_t globals, mach_port_t mp, uint32_t flags);
static uint32_t notify_register_coalesced_registration(const char *name, int flags, int *out_token, notify_globals_t globals, mach_port_t extra_mp);
// TSAN doesn't know about os_unfair_lock_with_options
#if defined(__has_feature)
#if __has_feature(thread_sanitizer)
#define TSAN_SAFE_LOCK(x) os_unfair_lock_lock(x)
#else
#define TSAN_SAFE_LOCK(x) os_unfair_lock_lock_with_options(x, OS_UNFAIR_LOCK_DATA_SYNCHRONIZATION)
#endif // __has_feature(thread_sanitizer)
#else
#define TSAN_SAFE_LOCK(x) os_unfair_lock_lock(x)
#endif // defined(__clang__)
#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); \
TSAN_SAFE_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); \
os_unfair_lock_unlock(x);
#else
#define mutex_lock(s,x,f,l) TSAN_SAFE_LOCK(x)
#define mutex_unlock(s,x,f,l) os_unfair_lock_unlock(x)
#endif
// returns the result after the decrement
inline static int32_t
atomic_refcount_release(atomic_uint_fast32_t *val)
{
int32_t result = os_atomic_dec(val, release);
// Crash right away if we underrun our refcounts
// as this indicates a bug in our logic
assert(result >= 0);
// result == 0 means that this was the last reference so any changes
// made to the refcounted object need to be visible to all threads
// at this point. c.f. "Release-Acquire ordering" at
// http://en.cppreference.com/w/cpp/atomic/memory_order
if (result == 0) os_atomic_thread_fence(acquire);
return result;
}
// returns the result after the increment
inline static int32_t
atomic_increment32(atomic_uint_fast32_t *val)
{
return os_atomic_inc(val, relaxed);
}
inline static void
name_node_retain(name_node_t *node)
{
atomic_increment32(&node->refcount);
}
inline static void
registration_node_retain(registration_node_t *reg)
{
atomic_increment32(&reg->refcount);
}
inline static uint32_t
client_opts(notify_globals_t globals)
{
return os_atomic_load(&globals->client_opts, relaxed);
}
__printflike(2, 3)
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);
#ifdef DEBUG_VERBOSE
fprintf(stderr, "thread %p: %s\n", (void *)pthread_self(), msg);
#endif
}
free(msg);
}
#if !TARGET_OS_SIMULATOR && !TARGET_OS_OSX
static const uint32_t LAUNCHD_PID = 1;
// do this so we don't have to link against libtrace
__printflike(1, 2)
static void
simulate_crash(char *fmt, ...)
{
char *desc = NULL;
va_list ap;
int len;
if (getpid() == LAUNCHD_PID) {
return;
}
va_start(ap, fmt);
len = vasprintf(&desc, fmt, ap);
va_end(ap);
if (desc == NULL) {
return;
}
os_fault_with_payload(OS_REASON_LIBSYSTEM, OS_REASON_LIBSYSTEM_CODE_FAULT,
desc, len + 1, desc, 0);
free(desc);
}
#define REPORT_BAD_BEHAVIOR(...) \
if(os_variant_has_internal_diagnostics("libnotify.simulate_crash")) \
{ \
simulate_crash(__VA_ARGS__); \
} else { \
_notify_client_log(ASL_LEVEL_ERR, __VA_ARGS__); \
} \
(void)0 // This allows ; after the macro and the compiler will optimize it out
#else /* !TARGET_OS_SIMULATOR && !TARGET_OS_OSX */
#define REPORT_BAD_BEHAVIOR(...) _notify_client_log(ASL_LEVEL_ERR, __VA_ARGS__)
#endif /* !TARGET_OS_SIMULATOR && !TARGET_OS_OSX */
static char *
_notify_strdup_if_mutable(const char *str, bool *needs_free)
{
size_t size = strlen(str) + 1;
if (!_dyld_is_memory_immutable(str, size)) {
char *clone = (char *)malloc(size);
if (clone) {
memcpy(clone, str, size);
if (needs_free) *needs_free = true;
}
return clone;
}
if (needs_free) *needs_free = false;
return (char *)str;
}
#pragma mark -
#pragma mark globals
#define INITIAL_TOKEN_ID 1
#define NUM_LOGGED_WRONG_CANARY 3
#if 64 < CANARY_COUNT
#error CANARY_COUNT too large to be represented in bitvector in _check_canary()
#endif
#if CANARY_COUNT < NUM_LOGGED_WRONG_CANARY
#error NUM_LOGGED_WRONG_CANARY too large; set equal to or lower than CANARY_COUNT
#endif
static const uint64_t canary_const = 0xAAAAaaaaAAAAaaaaULL;
/*
* Initialization of global variables. Called once per process.
*/
static void
_notify_init_globals(void * /* notify_globals_t */ _globals)
{
notify_globals_t globals = _globals;
for (uint64_t idx = 0; idx < CANARY_COUNT; idx += 1) {
globals->canary[idx] = canary_const;
}
globals->notify_lock = OS_UNFAIR_LOCK_INIT;
os_atomic_store(&globals->token_id, INITIAL_TOKEN_ID, relaxed);
globals->notify_common_token = -1;
globals->check_lock = OS_UNFAIR_LOCK_INIT;
_nc_table_init(&globals->name_node_table, offsetof(name_node_t, name));
_nc_table_init_n(&globals->registration_table, offsetof(registration_node_t, token));
_notify_lib_notify_state_init(&globals->self_state, NOTIFY_STATE_USE_LOCKS);
}
static inline void
_check_canary(notify_globals_t globals) {
uint64_t ne_bits = 0;
size_t cnt = 0;
uint64_t wrong[NUM_LOGGED_WRONG_CANARY];
for (size_t idx = 0; idx < CANARY_COUNT; idx += 1) {
uint64_t canary = globals->canary[idx];
if (os_unlikely(canary_const != canary)) {
ne_bits |= (1ULL << idx);
if (cnt == 0) {
memset(wrong, 0, sizeof(wrong));
}
if (cnt < NUM_LOGGED_WRONG_CANARY) {
wrong[cnt] = canary;
cnt += 1;
}
}
}
if (ne_bits != 0) {
REPORT_BAD_BEHAVIOR("BUG IN LIBNOTIFY CLIENT: internal data structure corrupted [0x%04llx, 0x%llx, 0x%llx, 0x%llx]]", ne_bits, wrong[0], wrong[1], wrong[2]);
// Reset canary to (1) avoid further reports of this corruption, and (2) detect the next corruption.
for (uint64_t idx = 0; idx < CANARY_COUNT; idx += 1) {
globals->canary[idx] = canary_const;
}
}
}
__attribute__((__pure__))
static inline notify_globals_t
_notify_globals(void)
{
notify_globals_t globals = (notify_globals_t)os_alloc_once(OS_ALLOC_ONCE_KEY_LIBSYSTEM_NOTIFY,
sizeof(struct notify_globals_s), &_notify_init_globals);
static uintptr_t _globals_lookup_counter = 0;
_globals_lookup_counter += 1;
if ((_globals_lookup_counter & 0x3) == 0) {
_check_canary(globals);
}
return globals;
}
#pragma mark -
#pragma mark name_node_t
#ifdef NOTDEF
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\n", (n->name == NULL) ? "NULL" : n->name, n->name_id, n->refcount, n->coalesce_base_token, n->coalesce_base);
}
#endif
// must be called with the global lock held
static name_node_t *
name_node_for_name_locked(notify_globals_t globals, const char *name, uint64_t nid, bool create)
{
os_unfair_lock_assert_owner(&globals->notify_lock);
if (name == NULL) return NULL;
name_node_t *n = _nc_table_find(&globals->name_node_table, name);
if (n != NULL)
{
name_node_retain(n);
}
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 done;
}
n->name = _notify_strdup_if_mutable(name, &n->needs_free);
if (n->name == NULL)
{
free(n);
n = NULL;
goto done;
}
os_atomic_store(&n->refcount, 1, relaxed);
n->name_id = nid;
TAILQ_INIT(&n->coalesced);
n->coalesce_base_token = NOTIFY_TOKEN_INVALID;
n->lock = OS_UNFAIR_LOCK_INIT;
n->has_been_warned = false;
_nc_table_insert(&globals->name_node_table, &n->name);
}
done:
#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;
}
static name_node_t *
name_node_for_name(const char *name, uint64_t nid, bool create)
{
notify_globals_t globals = _notify_globals();
mutex_lock("global", &globals->notify_lock, __func__, __LINE__);
name_node_t *node = name_node_for_name_locked(globals, name, nid, create);
mutex_unlock("global", &globals->notify_lock, __func__, __LINE__);
return node;
}
// must be called with the global lock held
static void
name_node_delete_locked(notify_globals_t globals, name_node_t *n)
{
os_unfair_lock_assert_owner(&globals->notify_lock);
/* 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_node_table, n->name);
if (n->needs_free) {
free(n->name);
}
n->name = NULL;
free(n);
}
// must be called with the global lock held
static void
name_node_release_locked(notify_globals_t globals, name_node_t *n)
{
os_unfair_lock_assert_owner(&globals->notify_lock);
if (n == NULL) return;
if (atomic_refcount_release(&n->refcount) > 0)
{
#ifdef DEBUG
if (_libnotify_debug & DEBUG_NODES) _notify_client_log(ASL_LEVEL_NOTICE, "%s name %s refcount %d %p", __func__, n->name, n->refcount, n);
#endif
return;
}
name_node_delete_locked(globals, n);
}
static void
name_node_unlock_and_release(name_node_t *n)
{
if (n == NULL) return;
mutex_unlock(n->name, &n->lock, __func__, __LINE__);
notify_globals_t globals = _notify_globals();
mutex_lock("global", &globals->notify_lock, __func__, __LINE__);
if (atomic_refcount_release(&n->refcount) > 0)
{
#ifdef DEBUG
if (_libnotify_debug & DEBUG_NODES) _notify_client_log(ASL_LEVEL_NOTICE, "%s name %s refcount %d %p", __func__, n->name, n->refcount, n);
#endif
mutex_unlock("global", &globals->notify_lock, __func__, __LINE__);
return;
}
name_node_delete_locked(globals, n);
mutex_unlock("global", &globals->notify_lock, __func__, __LINE__);
}
// We avoid an extra retain here if the base registration was just created
static void
name_node_add_coalesced_registration_locked(name_node_t *n, registration_node_t *r, bool skip_retain)
{
if (n == NULL) return;
if (r == NULL) return;
os_unfair_lock_assert_owner(&n->lock);
if (!skip_retain && n->coalesce_base) registration_node_retain(n->coalesce_base);
TAILQ_INSERT_TAIL(&n->coalesced, r, registration_coalesced_entry);
}
// must be called with the global lock held
static void
name_node_remove_coalesced_registration_locked(notify_globals_t globals, name_node_t *n, registration_node_t *r)
{
os_unfair_lock_assert_owner(&globals->notify_lock);
if (n == NULL) return;
if (r == NULL) return;
mutex_lock(n->name, &n->lock, __func__, __LINE__);
TAILQ_REMOVE(&n->coalesced, r, registration_coalesced_entry);
mutex_unlock(n->name, &n->lock, __func__, __LINE__);
registration_node_release_locked(globals, n->coalesce_base);
}
static void
name_node_set_nid_locked(name_node_t *n, uint64_t nid)
{
os_unfair_lock_assert_owner(&n->lock);
n->name_id = nid;
}
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__);
name_node_set_nid_locked(n, 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 = _nc_table_find_n(&globals->registration_table, token);
if (r != NULL) registration_node_retain(r);
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 ? r->refcount : -1, 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 = _nc_table_find_n(&globals->registration_table, val);
if (r == NULL) valid = false;
else if (r->flags & NOTIFY_FLAG_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;
}
// must be called with the global lock held
static void
registration_node_free_locked(notify_globals_t globals, registration_node_t *r)
{
os_unfair_lock_assert_owner(&globals->notify_lock);
name_node_t *n = r->name_node;
if (r->flags & NOTIFY_FLAG_COALESCED)
{
name_node_remove_coalesced_registration_locked(globals, n, r);
}
else if (r->flags & NOTIFY_FLAG_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
// hold lock across async server call
(void)_notify_server_cancel_2(globals->notify_server_port, r->token);
}
notify_release_file_descriptor_locked(globals, r->fd);
notify_release_mach_port_locked(globals, r->mp, r->flags);
if(((r->flags & NOTIFY_TYPE_MASK) == NOTIFY_TYPE_COMMON_PORT) && (r->signal_or_xtra_mp != MACH_PORT_NULL)) {
notify_release_mach_port_locked(globals, (mach_port_t)r->signal_or_xtra_mp, r->flags | NOTIFY_FLAG_RELEASE_SEND);
}
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_locked(globals, n);
}
// must be called with the global lock held
static void
registration_node_delete_locked(notify_globals_t globals, registration_node_t *r)
{
os_unfair_lock_assert_owner(&globals->notify_lock);
/* refcount is zero, free the node */
#ifdef DEBUG
if (_libnotify_debug & DEBUG_NODES) _notify_client_log(ASL_LEVEL_NOTICE, "%s token %u refcount %d flags 0x%08x %p FREE", __func__, r->token, r->refcount, r->flags, r);
#endif
_nc_table_delete_n(&globals->registration_table, r->token);
uint32_t reg_token = r->token;
uint32_t reg_flags = r->flags;
registration_node_free_locked(globals, r);
if (reg_flags & NOTIFY_FLAG_SELF)
{
/*
* _notify_lib_cancel fails quietly if self_state is NULL
* We let it fail quietly.
*/
_notify_lib_cancel(&globals->self_state, NOTIFY_CLIENT_SELF, reg_token);
#ifdef DEBUG
if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
return;
}
if (reg_flags & NOTIFY_FLAG_COALESCE_BASE)
{
#ifdef DEBUG
if (_libnotify_debug & DEBUG_CANCEL) _notify_client_log(ASL_LEVEL_NOTICE, "notify_cancel token %d NOTIFY_FLAG_COALESCE_BASE", reg_token);
#endif
#ifdef DEBUG
if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
return;
}
kern_return_t kstatus = KERN_SUCCESS;
if ((reg_flags & NOTIFY_FLAG_COALESCED) == 0)
{
// hold lock across async server call
kstatus = _notify_server_cancel_2(globals->notify_server_port, reg_token);
}
#ifdef DEBUG
if (_libnotify_debug & DEBUG_CANCEL) _notify_client_log(ASL_LEVEL_NOTICE, "notify_cancel token %d reg_node %p has been cancelled", reg_token, r);
#endif
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;
}
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
_notify_client_log(ASL_LEVEL_ERR, "<- %s [%d] _notify_server_cancel_2 failed: 0x%08x\n", __func__, __LINE__ + 2, kstatus);
return;
}
#ifdef DEBUG
if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
}
static void
registration_node_release(registration_node_t *r)
{
notify_globals_t globals = _notify_globals();
mutex_lock("global", &globals->notify_lock, __func__, __LINE__);
registration_node_release_locked(globals, r);
mutex_unlock("global", &globals->notify_lock, __func__, __LINE__);
}
static void
registration_node_release_locked(notify_globals_t globals, registration_node_t *r)
{
os_unfair_lock_assert_owner(&globals->notify_lock);
if (r == NULL) return;
if (atomic_refcount_release(&r->refcount) > 0)
{
#ifdef DEBUG
if (_libnotify_debug & DEBUG_NODES) _notify_client_log(ASL_LEVEL_NOTICE, "%s token %u refcount %d flags 0x%08x %p", __func__, r->token, r->refcount, r->flags, r);
#endif
return;
}
registration_node_delete_locked(globals, r);
}
#if !TARGET_OS_SIMULATOR
static bool
shm_attach(uint32_t size)
{
int32_t shmfd;
notify_globals_t globals = _notify_globals();
void *shm_base = NULL;
bool result = true;
shmfd = shm_open(SHM_ID, O_RDONLY, 0);
if (shmfd == -1)
{
REPORT_BAD_BEHAVIOR("Libnotify: %s failed on line %d with errno %d", __func__, __LINE__, errno);
return false;
}
shm_base = mmap(NULL, size, PROT_READ, MAP_SHARED, shmfd, 0);
if (shm_base == MAP_FAILED) {
REPORT_BAD_BEHAVIOR("Libnotify: %s failed on line %d with errno %d", __func__, __LINE__, errno);
result = false;
} else {
globals->shm_base = shm_base;
result = true;
}
close(shmfd);
return result;
}
#endif /* TARGET_OS_SIMULATOR */
#ifdef NOTDEF
static void
shm_detach(void)
{
if (shm_base != NULL)
{
shmdt(shm_base);
shm_base = NULL;
}
}
#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_locked(notify_globals_t globals, uint32_t event)
{
__block kern_return_t kstatus;
uint32_t status;
/* notifyd sets NOTIFY_OPT_DISABLE to avoid re-entrancy issues */
if (client_opts(globals) & NOTIFY_OPT_DISABLE) return NOTIFY_STATUS_OPT_DISABLE;
/* We need to hold the global lock here otherwise "first" is racy */
/* But then we don't need dispatch_once -- clean this up in 39829810 */
os_unfair_lock_assert_owner(&globals->notify_lock);
/* Look up the notifyd server port just once. */
kstatus = KERN_SUCCESS;
dispatch_once(&globals->notify_server_port_once, ^{
kstatus = bootstrap_look_up2(bootstrap_port, NOTIFY_SERVICE_NAME, &globals->notify_server_port, 0, BOOTSTRAP_PRIVILEGED_SERVER);
});
if ((kstatus != KERN_SUCCESS) || !(MACH_PORT_VALID(globals->notify_server_port)))
{
return NOTIFY_STATUS_SERVER_NOT_FOUND;
}
/*
* _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).
*/
if (_dispatch_is_multithreaded()) {
os_atomic_or(&globals->client_opts, NOTIFY_OPT_DISPATCH | NOTIFY_OPT_REGEN, relaxed);
}
/*
* Look up the server's PID and supported IPC version on the first call,
* and on a regeneration event (when the server has restarted).
*/
pid_t last_pid = globals->notify_server_pid;
if ((last_pid == 0) || (event == EVENT_REGEN))
{
uint32_t version;
pid_t new_pid;
globals->notify_server_pid = 0;
kstatus = _notify_server_checkin(globals->notify_server_port, &version, (uint32_t *)&new_pid, (int *)&status);
if (kstatus != KERN_SUCCESS)
{
if (kstatus == MACH_SEND_INVALID_DEST) {
status = NOTIFY_STATUS_SERVER_NOT_FOUND;
} else {
status = NOTIFY_STATUS_SERVER_CHECKIN_FAILED;
}
}
if (status != NOTIFY_STATUS_OK)
{
return status;
}
/*
* protocol versions below 3 aren't supported anymore
*/
if (version < NOTIFY_IPC_VERSION_MIN_SUPPORTED) {
NOTIFY_INTERNAL_CRASH(version, "Unsupported protocol version");
}
globals->notify_server_pid = new_pid;
if ((new_pid == last_pid) && (event == EVENT_REGEN))
{
return NOTIFY_STATUS_NO_REGEN_NEEDED;
}
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 (MACH_PORT_VALID(globals->notify_common_port)) {
if (globals->notify_dispatch_source) {
dispatch_source_cancel(globals->notify_dispatch_source);
dispatch_release(globals->notify_dispatch_source);
globals->notify_dispatch_source = NULL;
}
globals->notify_common_port = MACH_PORT_NULL;
}
}
/*
* 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) && (client_opts(globals) & 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_f(globals->server_proc_source, _notify_lib_server_restart_handler);
dispatch_resume(globals->server_proc_source);
}
/*
* Create the shared multiplex port if NOTIFY_OPT_DISPATCH is set.
*/
if ((client_opts(globals) & NOTIFY_OPT_DISPATCH) &&
((globals->notify_common_port == MACH_PORT_NULL) || (globals->notify_common_port == MACH_PORT_DEAD)))
{
mach_port_t mp;
// kstatus is the status of the mig call and status is the status of the server-side routine
kstatus = _notify_generate_common_port(globals->notify_server_port, &status, &mp);
if (kstatus != KERN_SUCCESS)
{
if (kstatus == MACH_SEND_INVALID_DEST) {
status = NOTIFY_STATUS_SERVER_NOT_FOUND;
} else {
status = NOTIFY_STATUS_SERVER_CHECKIN_FAILED;
}
}
if (status != NOTIFY_STATUS_OK)
{
return status;
}
globals->notify_common_port = mp;
globals->notify_dispatch_source = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV, mp, 0, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0));
dispatch_set_context(globals->notify_dispatch_source, globals);
dispatch_source_set_event_handler_f(globals->notify_dispatch_source, _notify_dispatch_handle);
dispatch_source_set_cancel_handler(globals->notify_dispatch_source, ^{
mach_port_destruct(mach_task_self(), mp, 0, 0);
});
dispatch_resume(globals->notify_dispatch_source);
}
return NOTIFY_STATUS_OK;
}
static uint32_t
_notify_lib_init(notify_globals_t globals, uint32_t event) {
mutex_lock("global", &globals->notify_lock, __func__, __LINE__);
uint32_t ret = _notify_lib_init_locked(globals, event);
mutex_unlock("global", &globals->notify_lock, __func__, __LINE__);
return ret;
}
/* Reset all internal state at fork */
/* Used in Libsystem without header declaration */
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.
*/
if (globals->notify_server_port != MACH_PORT_NULL ||
_dispatch_is_fork_of_multithreaded_parent())
{
os_atomic_store(&globals->client_opts, NOTIFY_OPT_DISABLE, relaxed);
}
_notify_lib_notify_state_init(&globals->self_state, NOTIFY_STATE_USE_LOCKS);
globals->notify_server_port = MACH_PORT_NULL;
globals->notify_server_pid = 0;
globals->fd_count = 0;
globals->fd_clnt = NULL;
globals->fd_srv = NULL;
globals->fd_refcount = NULL;
globals->mp_size = 0;
globals->mp_count = 0;
globals->mp_list = 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 uint32_t
client_registration_create_base( __attribute__((nonnull)) const char * name, uint64_t nid, uint32_t token, uint32_t cid, uint32_t slot, uint32_t flags, int sig, int fd, mach_port_t mp, bool create_base)
{
name_node_t *name_node = NULL;
registration_node_t *reg_node;
uint32_t warn_count = 0;
notify_globals_t globals = _notify_globals();
mutex_lock("global", &globals->notify_lock, __func__, __LINE__);
/* should never happen, but check if the registration exists */
reg_node = _nc_table_find_n(&globals->registration_table, token);
if (reg_node != NULL) goto client_registration_create_fail;
name_node = name_node_for_name_locked(globals, name, nid, true);
if (name_node == NULL) goto client_registration_create_fail;
mutex_lock(name_node->name, &name_node->lock, __func__, __LINE__);
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_unlock_and_release(name_node);
goto client_registration_create_fail;
}
os_atomic_store(&reg_node->refcount, 1, relaxed);
reg_node->token = token;
_nc_table_insert_n(&globals->registration_table, &reg_node->token);
#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_FLAG_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_or_xtra_mp = 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).
*/
if ((client_opts(globals) & NOTIFY_OPT_DISPATCH) && (flags & NOTIFY_FLAG_COALESCED))
{
bool delivered = notify_is_type(flags, NOTIFY_TYPE_PORT) ||
notify_is_type(flags, NOTIFY_TYPE_FILE) ||
notify_is_type(flags, NOTIFY_TYPE_SIGNAL) ||
notify_is_type(flags, NOTIFY_TYPE_XPC_EVENT) ||
notify_is_type(flags, NOTIFY_TYPE_COMMON_PORT);
if (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_locked(name_node, reg_node, create_base);
}
}
warn_count = os_atomic_load(&name_node->refcount, relaxed);
if ((!name_node->has_been_warned) && (warn_count == MULTIPLE_REGISTRATION_WARNING_TRIGGER))
{
name_node->has_been_warned = true;
REPORT_BAD_BEHAVIOR("notify name \"%s\" has been registered %d times - this may be a leak", name, warn_count);
}
mutex_unlock(name_node->name, &name_node->lock, __func__, __LINE__);
mutex_unlock("global", &globals->notify_lock, __func__, __LINE__);
return NOTIFY_STATUS_OK;
client_registration_create_fail:
mutex_unlock("global", &globals->notify_lock, __func__, __LINE__);
return NOTIFY_STATUS_CLIENT_REG_FAILED;
}
static uint32_t
client_registration_create(const char *name, uint64_t nid, uint32_t token, uint32_t cid, uint32_t slot, uint32_t flags, int sig, int fd, mach_port_t mp)
{
return client_registration_create_base(name, nid, token, cid, slot, flags, sig, fd, mp, false);
}
/*
* N.B. base_registration_create is called from notify_register_mach_port(), which holds the global lock
*/
static uint32_t
base_registration_create_locked(notify_globals_t globals, __attribute__((nonnull)) const char *name, uint64_t nid, uint32_t token, uint32_t flags, mach_port_t mp)
{
os_unfair_lock_assert_owner(&globals->notify_lock);
name_node_t *name_node = NULL;
registration_node_t *reg_node;
/* should never happen, but check if the registration exists */
reg_node = _nc_table_find_n(&globals->registration_table, token);
if (reg_node != NULL) return NOTIFY_STATUS_DOUBLE_REG;
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 NOTIFY_STATUS_ALLOC_FAILED;
}
name_node = name_node_for_name_locked(globals, name, nid, true);
if (name_node == NULL)
{
free(reg_node);
return NOTIFY_STATUS_NEW_NAME_FAILED;
}
#ifdef DEBUG
if (_libnotify_debug & DEBUG_REGISTRATION)
{
if (nid == NID_UNSET) _notify_client_log(ASL_LEVEL_NOTICE, "%s reg_node %p name %s name_node %p nid NID_UNSET token %u flags 0x%08x", __func__, reg_node, name, name_node, token, flags);
else if (nid == NID_CALLED_ONCE) _notify_client_log(ASL_LEVEL_NOTICE, "%s reg_node %p name %s name_node %p nid NID_CALLED_ONCE token %u flags 0x%08x", __func__, reg_node, name, name_node, token, flags);
else _notify_client_log(ASL_LEVEL_NOTICE, "%s reg_node %p name %s name_node %p nid %llu token %u flags 0x%08x", __func__, reg_node, name, name_node, nid, token, flags);
}
#endif
os_atomic_store(&reg_node->refcount, 1, relaxed);
reg_node->token = token;
reg_node->client_id = token;
reg_node->flags = flags | NOTIFY_FLAG_COALESCE_BASE;
reg_node->mp = mp;
reg_node->val = 0;
reg_node->slot = SLOT_NONE;
reg_node->fd = FD_NONE;
reg_node->signal_or_xtra_mp = 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, &reg_node->token);
return NOTIFY_STATUS_OK;
}
static bool
check_name_access(char *name, uid_t uid)
{
char str[64];
size_t len;
/* root may do anything */
if (uid == 0) return true;
/* if name does not have "user.uid." as a prefix, it is not a user-protected namespace */
if (strncmp(name, USER_PROTECTED_UID_PREFIX, USER_PROTECTED_UID_PREFIX_LEN))
{
return true;
}
snprintf(str, sizeof(str) - 1, "%s%d", USER_PROTECTED_UID_PREFIX, uid);
len = strlen(str);
/* user <uid> may access user.uid.<uid> or a subtree name */
return ((!strncmp(name, str, len)) && ((name[len] == '\0') || (name[len] == '.')));
}
static void
_notify_lib_regenerate_registration(registration_node_t *r)
{
uint32_t type;
int status;
int new_slot = SLOT_NONE;
kern_return_t kstatus;
uint64_t new_nid = NID_UNSET;
size_t pathlen;
char *name = r->name_node->name;
notify_globals_t globals = _notify_globals();
if (r->flags & NOTIFY_FLAG_SELF) return;
if ((r->flags & NOTIFY_FLAG_REGEN) == 0) return;
if(!check_name_access(name, geteuid()))
{
REPORT_BAD_BEHAVIOR("BUG IN LIBNOTIFY CLIENT: registration held for restricted name %s with process uid %d",
name, geteuid());
}
pathlen = 0;
if (r->path != NULL) pathlen = strlen(r->path) + 1;
type = r->flags & NOTIFY_TYPE_MASK;
int remaining_retries = NOTIFY_SERVER_RETRY_NUMBER;
do {
kstatus = _notify_server_regenerate(globals->notify_server_port, (caddr_t)name, r->token, type, MACH_PORT_NULL, (type == NOTIFY_TYPE_SIGNAL) ? r->signal_or_xtra_mp : 0,
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) {
break;
}
usleep(NOTIFY_SERVER_RETRY_WAIT_US);
} while (remaining_retries-- > 0);
assert(kstatus == KERN_SUCCESS);
if(status != NOTIFY_STATUS_OK && status != NOTIFY_STATUS_DUP_CLIENT &&
status != NOTIFY_STATUS_NO_REGEN_NEEDED)
{
REPORT_BAD_BEHAVIOR("Libnotify: _notify_server_regnerate failed for name %s with status %d", name, status);
}
r->slot = new_slot;
r->name_node->name_id = new_nid;
}
/*
* Invoked when server has died.
* Regenerates all registrations and state.
*/
static uint32_t
_notify_lib_regenerate_locked(notify_globals_t globals)
{
uint32_t result;
if ((client_opts(globals) & NOTIFY_OPT_REGEN) == 0) return NOTIFY_STATUS_OK;
/* if _notify_lib_init_lockec returns an error, regeneration is unnecessary */
result = _notify_lib_init_locked(globals, EVENT_REGEN);
if (result == NOTIFY_STATUS_NO_REGEN_NEEDED) {
return NOTIFY_STATUS_OK;
}
if (result == NOTIFY_STATUS_OK) {
_nc_table_foreach_n(&globals->registration_table, ^bool(void *reg) {
_notify_lib_regenerate_registration(reg);
return true;
});
}
return result;
}
/*
* Invoked when server has died.
* Regenerates all registrations and state.
*/
static void
_notify_lib_server_restart_handler(void *ctxt __unused)
{
notify_globals_t globals = _notify_globals();
mutex_lock("global", &globals->notify_lock, __func__, __LINE__);
(void)_notify_lib_regenerate_locked(globals);
mutex_unlock("global", &globals->notify_lock, __func__, __LINE__);
return;
}
/*
* Get the up to date shm_base if the server PID (shared memory slot 0)
* has changed.
*/
static inline uint32_t
regenerate_check(notify_globals_t globals)
{
uint32_t result = NOTIFY_STATUS_OK;
if ((client_opts(globals) & NOTIFY_OPT_REGEN) == 0) return NOTIFY_STATUS_OK;
mutex_lock("global", &globals->notify_lock, __func__, __LINE__);
if ((globals->shm_base != NULL) && ((pid_t)globals->shm_base[0] != globals->notify_server_pid))
{
result = _notify_lib_regenerate_locked(globals);
}
mutex_unlock("global", &globals->notify_lock, __func__, __LINE__);
return result;
}
/* notify_lock is required in notify_retain_file_descriptor */
static void
notify_retain_file_descriptor(int clnt, int srv)
{
uint32_t 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 = SLOT_NONE;
for (i = 0; (i < globals->fd_count) && (x == SLOT_NONE); i++)
{
if (globals->fd_clnt[i] == clnt) x = i;
}
if (x != SLOT_NONE)
{
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__);
}
// must be called with the global lock held
static void
notify_release_file_descriptor_locked(notify_globals_t globals, int fd)
{
os_unfair_lock_assert_owner(&globals->notify_lock);
uint32_t x, i, j;
if (fd < 0) return;
x = SLOT_NONE;
for (i = 0; (i < globals->fd_count) && (x == SLOT_NONE); i++)
{
if (globals->fd_clnt[i] == fd) x = i;
}
if (x == SLOT_NONE)
{
return;
}
if (globals->fd_refcount[x] > 0) globals->fd_refcount[x]--;
if (globals->fd_refcount[x] > 0)
{
return;
}
close(globals->fd_clnt[x]);
close(globals->fd_srv[x]);
if (globals->fd_count == 1)
{
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;
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);
globals->fd_clnt = NULL;
free(globals->fd_srv);
globals->fd_srv = NULL;
free(globals->fd_refcount);
globals->fd_refcount = NULL;
globals->fd_count = 0;
}
}
/* notify_lock is required in notify_retain_mach_port */
static void
notify_retain_mach_port(notify_globals_t globals, mach_port_t mp, int flags)
{
if (mp == MACH_PORT_NULL) return;
if (flags & _NOTIFY_COMMON_PORT) return;
mutex_lock("global", &globals->notify_lock, __func__, __LINE__);
struct mp_entry *entries = globals->mp_list;
for (uint32_t i = 0; i < globals->mp_count; i++)
{
if (entries[i].mpl_port_name == mp) {
entries[i].mpl_refs++;
mutex_unlock("global", &globals->notify_lock, __func__, __LINE__);
return;
}
}
uint32_t x = globals->mp_count;
globals->mp_count++;
if (x >= globals->mp_size) {
if (globals->mp_size < 4) {
globals->mp_size = 4;
} else {
globals->mp_size *= 2;
}
entries = reallocf(entries, globals->mp_size * sizeof(struct mp_entry));
globals->mp_list = entries;
if (os_unlikely(entries == NULL)) {
NOTIFY_CLIENT_CRASH(0, "Unable to allocate port array: "
"possible notification registration leak");
}
}
entries[x].mpl_port_name = mp;
entries[x].mpl_refs = 1;
entries[x].mpl_mine = ((flags & NOTIFY_REUSE) == 0);
mutex_unlock("global", &globals->notify_lock, __func__, __LINE__);
}
// must be called with the global lock held
static void
notify_release_mach_port_locked(notify_globals_t globals, mach_port_t mp, uint32_t flags)
{
struct mp_entry *entries = globals->mp_list;
uint32_t x = SLOT_NONE;
os_unfair_lock_assert_owner(&globals->notify_lock);
if (mp == MACH_PORT_NULL) return;
if (mp == globals->notify_common_port) return;
for (uint32_t i = 0; i < globals->mp_count; i++)
{
if (entries[i].mpl_port_name == mp) {
x = i;
break;
}
}
if (x == SLOT_NONE)
{
return;
}
if (entries[x].mpl_refs > 1) {
entries[x].mpl_refs--;
return;
}
if (entries[x].mpl_mine)
{
int uref_mod = 0;
if (flags & NOTIFY_FLAG_RELEASE_SEND) uref_mod = -1;
mach_port_destruct(mach_task_self(), mp, uref_mod, 0);
}
else if (flags & NOTIFY_FLAG_RELEASE_SEND)
{
/* multiplexed registration holds a send right in Libnotify */
mach_port_deallocate(mach_task_self(), mp);
}
// swap removed element with the last one in the array
globals->mp_count--;
if (x != globals->mp_count) {
entries[x] = entries[globals->mp_count];
}
if (globals->mp_count == 0)
{
free(entries);
globals->mp_count = globals->mp_size = 0;
globals->mp_list = NULL;
}
else if (globals->mp_size > 4 && globals->mp_count <= globals->mp_size / 4)
{
entries = realloc(entries, sizeof(struct mp_entry) * globals->mp_size / 2);
if (entries) {
globals->mp_list = entries;
globals->mp_size /= 2;
}
}
}
/* 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 (client_opts(globals) & NOTIFY_OPT_DISABLE)
{
if (!(opts & NOTIFY_OPT_ENABLE))
{
globals->saved_opts |= opts;
return;
}
/* re-enable by swapping in the saved server port and saved opts*/
mutex_lock("global", &globals->notify_lock, __func__, __LINE__);
os_atomic_store(&globals->client_opts, globals->saved_opts, relaxed);
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 = os_atomic_xchg(&globals->client_opts, NOTIFY_OPT_DISABLE, relaxed);
globals->saved_server_port = globals->notify_server_port;
globals->notify_server_port = MACH_PORT_NULL;
mutex_unlock("global", &globals->notify_lock, __func__, __LINE__);
return;
}
os_atomic_or(&globals->client_opts, opts, relaxed);
/* call _notify_lib_init to create ports / dispatch sources as required */
_notify_lib_init(globals, EVENT_INIT);
}
/**
* Check if process has root access entitlement and should claim root access
* from notifyd.
*/
static bool
should_claim_root_access(void)
{
static dispatch_once_t once;
static bool has_root_entitlement;
dispatch_once(&once, ^{
xpc_object_t entitlement = xpc_copy_entitlement_for_token(ROOT_ENTITLEMENT_KEY, NULL);
if (entitlement == XPC_BOOL_TRUE) has_root_entitlement = true;
if (entitlement) xpc_release(entitlement);
});
return has_root_entitlement;
}
/*
* 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)
{
NOTIFY_POST(name);
#ifdef DEBUG
if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "-> %s\n", __func__);
#endif
kern_return_t kstatus;
uint32_t status;
name_node_t *n;
uint64_t nid = UINT64_MAX;
notify_globals_t globals = _notify_globals();
status = regenerate_check(globals);
if (status != NOTIFY_STATUS_OK)
{
if(IS_INTERNAL_ERROR(status))
{
REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__);
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;
}
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))
{
_notify_lib_post(&globals->self_state, 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(globals, EVENT_INIT);
if (status != NOTIFY_STATUS_OK)
{
if(IS_INTERNAL_ERROR(status))
{
REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__);
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;
}
}
/* See if we have a name ID for this name. */
n = name_node_for_name(name, NID_UNSET, false);
if (n != NULL)
{
mutex_lock(n->name, &n->lock, __func__, __LINE__);
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, should_claim_root_access());
if (kstatus != KERN_SUCCESS)
{
name_node_unlock_and_release(n);
#ifdef DEBUG
if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d (%d) on line %d", __func__,
NOTIFY_STATUS_SERVER_POST_4_FAILED, kstatus, __LINE__);
return NOTIFY_STATUS_FAILED;
}
name_node_set_nid_locked(n, NID_CALLED_ONCE);
}
else if (n->name_id == NID_CALLED_ONCE)
{
status = NOTIFY_STATUS_SERVER_POST_2_FAILED;
/* 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, &nid, (int32_t *)&status, should_claim_root_access());
if (kstatus != KERN_SUCCESS || (status != NOTIFY_STATUS_OK && status != NOTIFY_STATUS_NO_NID))
{
name_node_unlock_and_release(n);
#ifdef DEBUG
if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d (%d) on line %d", __func__,
status, kstatus, __LINE__);
return NOTIFY_STATUS_FAILED;
}
if (status == NOTIFY_STATUS_NO_NID) {
// This means that notifyd doesn't have a nid for
// this name. By setting this to NID_UNSET, the
// next post will be by name, and then the one
// after that will attempt to query the nid again.
name_node_set_nid_locked(n, NID_UNSET);
} else {
name_node_set_nid_locked(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, should_claim_root_access());
if (kstatus != KERN_SUCCESS)
{
name_node_unlock_and_release(n);
#ifdef DEBUG
if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d (%d) on line %d", __func__,
NOTIFY_STATUS_SERVER_POST_3_FAILED, kstatus, __LINE__);
return NOTIFY_STATUS_FAILED;
}
}
name_node_unlock_and_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, should_claim_root_access());
if (kstatus != KERN_SUCCESS)
{
#ifdef DEBUG
if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d (%d) on line %d", __func__,
NOTIFY_STATUS_SERVER_POST_4_FAILED, kstatus, __LINE__);
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;
}
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_FLAG_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_FLAG_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 (r->flags & NOTIFY_FLAG_SUSPENDED)
{
r->flags |= NOTIFY_FLAG_DEFERRED_POST;
return;
}
/*
* 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);
/*
* If DTrace probes are currently active that deal with the delivery of a
* notification, then the name needs to live until the end of the delivery.
* It is freed after delivery.
*/
char *name = NULL;
if ((NOTIFY_DELIVER_START_ENABLED() || NOTIFY_DELIVER_END_ENABLED()) && r && r->name_node && r->name_node->name)
{
name = strdup(r->name_node->name);
}
// notify_dispatch_source runs on a DISPATCH_QUEUE_PRIORITY_HIGH queue (IN
// QoS). Dispatching directly from that queue to the client queue taints the
// block with IN qos. Instead we create a detached wrapper and let the block
// run with the priority of the target queue hierachy. This is the same
// behaviour as with dispatch sources.
// <rdar://problem/38438633>
dispatch_block_t detached_block = dispatch_block_create(DISPATCH_BLOCK_DETACHED, ^{
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 (name) NOTIFY_DELIVER_START(name);
if (valid) theblock(token);
if (name) NOTIFY_DELIVER_END(name);
_Block_release(theblock);
dispatch_release(thequeue);
if (name) free(name);
#ifdef DEBUG
if (_libnotify_debug & DEBUG_NOTIFICATION) _notify_client_log(ASL_LEVEL_NOTICE, "<- dispatch_async token %d", token);
#endif
});
dispatch_async(thequeue, detached_block);
Block_release(detached_block);
}
static void
_notify_dispatch_handle(void *context)
{
notify_globals_t globals = context;
mach_port_t port = globals->notify_common_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;
for (int retries = 5; retries-- > 0; ) {
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 | MACH_RCV_TIMEOUT, 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) continue;
n = r->name_node;
if (n == NULL)
{
/* should not happen */
registration_node_release(r);
continue;
}
mutex_lock(n->name, &n->lock, __func__, __LINE__);
if (r->flags & NOTIFY_FLAG_COALESCE_BASE)
{
registration_node_t *x;
TAILQ_FOREACH(x, &n->coalesced, registration_coalesced_entry)
{
if (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);
}
}
#pragma mark -
#pragma mark registration
static uint32_t
_notify_register_dispatch_with_extra_mp(const char *name, int *out_token, dispatch_queue_t queue, notify_handler_t handler, mach_port_t extra_mp)
{
#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();
status = regenerate_check(globals);
if (status != NOTIFY_STATUS_OK)
{
if(IS_INTERNAL_ERROR(status))
{
REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__);
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;
}
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_NULL_INPUT;
}
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_NULL_INPUT;
}
/* client is using dispatch: enable local demux / dispatch and regeneration */
notify_set_options(NOTIFY_OPT_DISPATCH | NOTIFY_OPT_REGEN);
status = notify_register_coalesced_registration(name, NOTIFY_REUSE | _NOTIFY_COMMON_PORT, out_token, globals, extra_mp);
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
REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, NOTIFY_STATUS_TOKEN_NOT_FOUND, __LINE__);
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;
}
uint32_t
notify_register_dispatch(const char *name, int *out_token, dispatch_queue_t queue, notify_handler_t handler)
{
return _notify_register_dispatch_with_extra_mp(name, out_token, queue, handler, MACH_PORT_NULL);
}
/* 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();
if (globals->notify_common_port == MACH_PORT_NULL) return NOTIFY_STATUS_COMMON_PORT_NULL;
status = notify_register_coalesced_registration(name, NOTIFY_REUSE | _NOTIFY_COMMON_PORT, out_token, globals, MACH_PORT_NULL);
if (status != NOTIFY_STATUS_OK)
{
return status;
}
r = registration_node_find(*out_token);
if (r == NULL)
{
return NOTIFY_STATUS_TOKEN_NOT_FOUND;
}
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;
}
uint32_t
notify_register_check(const char *name, int *out_token)
{
#if TARGET_OS_SIMULATOR
return notify_register_plain(name, out_token);
#else
#ifdef DEBUG
if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "-> %s\n", __func__);
#endif
kern_return_t kstatus;
uint32_t status, token;
uint64_t nid;
int slot;
int32_t shmsize;
uint32_t cid;
notify_globals_t globals = _notify_globals();
status = regenerate_check(globals);
if (status != NOTIFY_STATUS_OK)
{
if(IS_INTERNAL_ERROR(status))
{
REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__);
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;
}
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_NULL_INPUT;
}
*out_token = -1;
if (!strncmp(name, SELF_PREFIX, SELF_PREFIX_LEN))
{
token = atomic_increment32(&globals->token_id);
status = _notify_lib_register_plain(&globals->self_state, name, NOTIFY_CLIENT_SELF, token, SLOT_NONE, 0, 0, &nid);
if (status != NOTIFY_STATUS_OK)
{
if(IS_INTERNAL_ERROR(status))
{
REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__);
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;
}
cid = token;
status = client_registration_create(name, nid, token, cid, SLOT_NONE,
NOTIFY_FLAG_SELF | NOTIFY_TYPE_PLAIN, SIGNAL_NONE, FD_NONE, MACH_PORT_NULL);
if (status != NOTIFY_STATUS_OK)
{
if(IS_INTERNAL_ERROR(status))
{
REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__);
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;
}
*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(globals, EVENT_INIT);
if (status != NOTIFY_STATUS_OK)
{
if(IS_INTERNAL_ERROR(status))
{
REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__);
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;
}
}
token = atomic_increment32(&globals->token_id);
cid = token;
kstatus = _notify_server_register_check_2(globals->notify_server_port, (caddr_t)name, 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
REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d (%d) on line %d", __func__,
NOTIFY_STATUS_REG_CHECK_2_FAILED, kstatus, __LINE__);
return NOTIFY_STATUS_FAILED;
}
if (status != NOTIFY_STATUS_OK)
{
if(IS_INTERNAL_ERROR(status))
{
REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__);
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;
}
if (shmsize != -1)
{
mutex_lock("global", &globals->notify_lock, __func__, __LINE__);
if (globals->shm_base == NULL)
{
if (!shm_attach(shmsize))
{
#ifdef DEBUG
if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
mutex_unlock("global", &globals->notify_lock, __func__, __LINE__);
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
mutex_unlock("global", &globals->notify_lock, __func__, __LINE__);
REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__,
NOTIFY_STATUS_SHM_BASE_REMAINS_NULL, __LINE__);
return NOTIFY_STATUS_FAILED;
}
}
mutex_unlock("global", &globals->notify_lock, __func__, __LINE__);
status = client_registration_create(name, nid, token, cid, slot,
NOTIFY_TYPE_MEMORY | NOTIFY_FLAG_REGEN, SIGNAL_NONE, FD_NONE, MACH_PORT_NULL);
}
else
{
status = client_registration_create(name, nid, token, cid, SLOT_NONE,
NOTIFY_TYPE_PLAIN | NOTIFY_FLAG_REGEN, SIGNAL_NONE, FD_NONE, MACH_PORT_NULL);
}
if (status != NOTIFY_STATUS_OK)
{
if(IS_INTERNAL_ERROR(status))
{
REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__);
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;
}
*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;
#endif /* TARGET_OS_SIMULATOR */
}
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
kern_return_t kstatus;
uint32_t status;
uint64_t nid;
int token;
uint32_t cid;
notify_globals_t globals = _notify_globals();
status = regenerate_check(globals);
if (status != NOTIFY_STATUS_OK)
{
if(IS_INTERNAL_ERROR(status))
{
REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__);
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;
}
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))
{
token = atomic_increment32(&globals->token_id);
status = _notify_lib_register_plain(&globals->self_state, name, NOTIFY_CLIENT_SELF, token, SLOT_NONE, 0, 0, &nid);
if (status != NOTIFY_STATUS_OK)
{
if(IS_INTERNAL_ERROR(status))
{
REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__);
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;
}
cid = token;
status = client_registration_create(name, nid, token, cid, SLOT_NONE,
NOTIFY_FLAG_SELF | NOTIFY_TYPE_PLAIN, SIGNAL_NONE, FD_NONE, MACH_PORT_NULL);
if (status != NOTIFY_STATUS_OK)
{
if(IS_INTERNAL_ERROR(status))
{
REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__);
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;
}
*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(globals, EVENT_INIT);
if (status != NOTIFY_STATUS_OK)
{
if(IS_INTERNAL_ERROR(status))
{
REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__);
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;
}
}
token = atomic_increment32(&globals->token_id);
cid = token;
kstatus = _notify_server_register_plain_2(globals->notify_server_port, (caddr_t)name, 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
REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d (%d) on line %d", __func__, NOTIFY_STATUS_REG_PLAIN_2_FAILED, kstatus, __LINE__);
return NOTIFY_STATUS_FAILED;
}
status = client_registration_create(name, NID_UNSET, token, cid, SLOT_NONE,
NOTIFY_TYPE_PLAIN | NOTIFY_FLAG_REGEN, SIGNAL_NONE, FD_NONE, MACH_PORT_NULL);
if (status != NOTIFY_STATUS_OK)
{
if(IS_INTERNAL_ERROR(status))
{
REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__);
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;
}
*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
kern_return_t kstatus;
uint32_t status;
uint64_t nid;
int token;
uint32_t cid;
notify_globals_t globals = _notify_globals();
status = regenerate_check(globals);
if (status != NOTIFY_STATUS_OK)
{
if(IS_INTERNAL_ERROR(status))
{
REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__);
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;
}
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))
{
token = atomic_increment32(&globals->token_id);
status = _notify_lib_register_signal(&globals->self_state, name, NOTIFY_CLIENT_SELF, token, sig, 0, 0, &nid);
if (status != NOTIFY_STATUS_OK)
{
if(IS_INTERNAL_ERROR(status))
{
REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__);
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;
}
cid = token;
status = client_registration_create(name, nid, token, cid, SLOT_NONE,
NOTIFY_FLAG_SELF | NOTIFY_TYPE_SIGNAL, sig, FD_NONE, MACH_PORT_NULL);
if (status != NOTIFY_STATUS_OK)
{
if(IS_INTERNAL_ERROR(status))
{
REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__);
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;
}
*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 (client_opts(globals) & NOTIFY_OPT_DISPATCH)
{
status = notify_register_dispatch(name, out_token, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^(int unused){ kill(getpid(), sig); });
if(IS_INTERNAL_ERROR(status))
{
REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__);
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;
}
if (globals->notify_server_port == MACH_PORT_NULL)
{
status = _notify_lib_init(globals, EVENT_INIT);
if (status != NOTIFY_STATUS_OK)
{
if(IS_INTERNAL_ERROR(status))
{
REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__);
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;
}
}
token = atomic_increment32(&globals->token_id);
cid = token;
kstatus = _notify_server_register_signal_2(globals->notify_server_port, (caddr_t)name, 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
REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d (%d) on line %d", __func__, NOTIFY_STATUS_REG_SIGNAL_2_FAILED, kstatus, __LINE__);
return NOTIFY_STATUS_FAILED;
}
status = client_registration_create(name, NID_UNSET, token, cid, SLOT_NONE,
NOTIFY_TYPE_SIGNAL | NOTIFY_FLAG_REGEN, sig, FD_NONE, MACH_PORT_NULL);
if (status != NOTIFY_STATUS_OK)
{
if(IS_INTERNAL_ERROR(status))
{
REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__);
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;
}
*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;
}
// Assumes notify_port already exists
static uint32_t
notify_register_mach_port_self(const char *name, mach_port_name_t *notify_port, int flags, int *out_token, notify_globals_t globals)
{
#ifdef DEBUG
if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "-> %s\n", __func__);
#endif
assert(globals);
assert(name);
kern_return_t kstatus;
uint32_t token;
uint64_t nid;
uint32_t status;
uint32_t cid;
bool mine = false;
if ((flags & NOTIFY_REUSE) == 0)
{
/* caller wants a new port: create a new port with a receive right */
mine = true;
kstatus = mach_port_allocate(mach_task_self(), 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
REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d (%d) on line %d", __func__,
NOTIFY_STATUS_MACH_PORT_ALLOC_FAILED, kstatus, __LINE__);
return NOTIFY_STATUS_FAILED;
}
}
else if (!MACH_PORT_VALID(*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_INVALID_PORT;
}
/* we need to donate a Send right to the client */
kstatus = mach_port_insert_right(mach_task_self(), *notify_port, *notify_port, MACH_MSG_TYPE_MAKE_SEND);
if (kstatus != KERN_SUCCESS)
{
if (mine) mach_port_destruct(mach_task_self(), *notify_port, 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_INVALID_PORT;
}
token = atomic_increment32(&globals->token_id);
status = _notify_lib_register_mach_port(&globals->self_state, name, NOTIFY_CLIENT_SELF, token, *notify_port, 0, 0, &nid);
if (status != NOTIFY_STATUS_OK)
{
if (mine) mach_port_destruct(mach_task_self(), *notify_port, 0, 0);
if (IS_INTERNAL_ERROR(status))
{
REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__);
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;
}
cid = token;
status = client_registration_create(name, nid, token, cid, SLOT_NONE,
NOTIFY_FLAG_SELF | NOTIFY_TYPE_PORT, SIGNAL_NONE, FD_NONE, *notify_port);
if (status != NOTIFY_STATUS_OK)
{
if (mine) mach_port_destruct(mach_task_self(), *notify_port, 0, 0);
_notify_lib_cancel(&globals->self_state, NOTIFY_CLIENT_SELF, token);
if (IS_INTERNAL_ERROR(status))
{
REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__);
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;
}
*out_token = token;
notify_retain_mach_port(globals, *notify_port, flags);
NOTIFY_REGISTER_MACH_PORT(name, *notify_port, flags, 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;
}
// Assumes no dispatch
// Assumes notify_port already exists
// Assumes not self
static uint32_t
notify_register_mach_port_no_dispatch(const char *name, mach_port_name_t *notify_port, int flags, int *out_token, notify_globals_t globals)
{
#ifdef DEBUG
if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "-> %s\n", __func__);
#endif
assert(globals);
assert(name);
assert(strncmp(name, SELF_PREFIX, SELF_PREFIX_LEN));
uint32_t token;
uint32_t cid;
kern_return_t kstatus;
uint32_t status;
bool mine = false;
if (((flags & NOTIFY_REUSE) != 0) && !MACH_PORT_VALID(*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_INVALID_PORT;
}
if (globals->notify_server_port == MACH_PORT_NULL)
{
status = _notify_lib_init(globals, EVENT_INIT);
if (status != NOTIFY_STATUS_OK)
{
if(IS_INTERNAL_ERROR(status))
{
REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__);
status = NOTIFY_STATUS_FAILED;
}
return status;
}
}
token = atomic_increment32(&globals->token_id);
cid = token;
if ((flags & NOTIFY_REUSE) == 0) {
mine = true;
uint32_t status = NOTIFY_STATUS_OK;
*notify_port = MACH_PORT_NULL;
kstatus = _notify_server_register_mach_port_3(globals->notify_server_port, (caddr_t)name, token, &status, notify_port);
if (status != NOTIFY_STATUS_OK) {
assert(*notify_port == MACH_PORT_NULL);
if (IS_INTERNAL_ERROR(status))
{
REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__);
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;
}
} else {
kstatus = _notify_server_register_mach_port_2(globals->notify_server_port, (caddr_t)name, token, *notify_port);
}
if (kstatus != KERN_SUCCESS)
{
if (mine) mach_port_destruct(mach_task_self(), *notify_port, 0, 0);
#ifdef DEBUG
if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d (%d) on line %d", __func__,
NOTIFY_STATUS_REG_MACH_PORT_2_FAILED, kstatus, __LINE__);
return NOTIFY_STATUS_FAILED;
}
status = client_registration_create_base(name, NID_UNSET, token, cid, SLOT_NONE,
NOTIFY_TYPE_PORT, SIGNAL_NONE, FD_NONE, *notify_port, false);
if(status != NOTIFY_STATUS_OK)
{
if (mine) mach_port_destruct(mach_task_self(), *notify_port, 0, 0);
if (IS_INTERNAL_ERROR(status))
{
REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__);
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;
}
notify_retain_mach_port(globals, *notify_port, flags);
NOTIFY_REGISTER_MACH_PORT(name, *notify_port, flags, token);
if(out_token) {
*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;
}
static uint32_t
notify_register_coalesced_registration(const char *name, int flags, int *out_token, notify_globals_t globals, mach_port_t extra_mp)
{
#ifdef DEBUG
if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "-> %s\n", __func__);
#endif
assert(globals);
assert(name);
if(!strncmp(name, SELF_PREFIX, SELF_PREFIX_LEN)) {
return notify_register_mach_port_self(name, &globals->notify_common_port, flags, out_token, globals);
}
uint32_t status;
name_node_t *n;
bool create_base = false;
uint32_t token;
uint32_t cid;
kern_return_t kstatus;
uint32_t tflags = NOTIFY_TYPE_COMMON_PORT | NOTIFY_FLAG_REGEN;
/* initialize if necessary */
if ((globals->notify_server_port == MACH_PORT_NULL) ||
!MACH_PORT_VALID(globals->notify_common_port))
{
status = _notify_lib_init(globals, EVENT_INIT);
if (status != NOTIFY_STATUS_OK)
{
if (status != NOTIFY_STATUS_OPT_DISABLE) {
REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__);
}
#ifdef DEBUG
if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
return status;
}
}
/*
* 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 = _nc_table_find(&globals->name_node_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 = atomic_increment32(&globals->token_id);
kstatus = _notify_server_register_common_port(globals->notify_server_port, (caddr_t)name, token);
if (kstatus != KERN_SUCCESS)
{
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
REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d (%d) on line %d", __func__,
NOTIFY_STATUS_REG_MACH_PORT_2_FAILED, kstatus, __LINE__);
return NOTIFY_STATUS_FAILED;
}
status = base_registration_create_locked(globals, name, NID_UNSET, token, tflags,
globals->notify_common_port);
if (status != NOTIFY_STATUS_OK)
{
mutex_unlock("global", &globals->notify_lock, __func__, __LINE__);
if (IS_INTERNAL_ERROR(status))
{
REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__);
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;
}
}
/* we will need a pointer to the name node below - look it up while we still have the global lock */
n = _nc_table_find(&globals->name_node_table, name);
if(!create_base){
registration_node_retain(n->coalesce_base);
}
/* release globals->notify_lock here */
mutex_unlock("global", &globals->notify_lock, __func__, __LINE__);
/* caller's token */
token = atomic_increment32(&globals->token_id);
cid = token;
tflags |= NOTIFY_FLAG_COALESCED;
/*
* If this call to notify_register_mach_port created the coalesce_base registration,
* we avoid an extra retain here. It is created with a refcount of 1, and it would get retained
* by the coalesced registration created in client_registration_create_base below. We
* want the refcount to be equal to the number of registrations in the coalesce list.
* We pass create_base down to client_registration_create_base to indicate that it should avoid
* the extra retain -- see name_node_add_coalesced_registration()
*/
status = client_registration_create_base(name, NID_UNSET, token, cid, SLOT_NONE,
tflags, MACH_PORT_VALID(extra_mp) ? extra_mp : MACH_PORT_NULL, FD_NONE, globals->notify_server_port, create_base);
if(!create_base){
registration_node_release(n->coalesce_base);
}
if(status != NOTIFY_STATUS_OK){
if (IS_INTERNAL_ERROR(status))
{
REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__);
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;
}
notify_retain_mach_port(globals, globals->notify_server_port, flags);
if (out_token) {
*out_token = token;
}
return NOTIFY_STATUS_OK;
}
static void notify_mach_port(int token, mach_port_t port, notify_globals_t globals) {
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;
/* 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;
mach_msg(&msg.header, MACH_SEND_MSG, msg.header.msgh_size, 0, MACH_PORT_NULL, 0, MACH_PORT_NULL);
});
}
}
static uint32_t
notify_register_mach_port_via_dispatch(const char *name, mach_port_name_t *notify_port, int flags, int *out_token, notify_globals_t globals)
{
#ifdef DEBUG
if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "-> %s\n", __func__);
#endif
bool mine = false;
uint32_t return_status;
kern_return_t kstatus;
mach_port_t port;
if ((flags & NOTIFY_REUSE) == 0)
{
/* caller wants a new port: create a new port with a receive right */
mine = true;
kstatus = mach_port_allocate(mach_task_self(), 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
REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d (%d) on line %d", __func__,
NOTIFY_STATUS_MACH_PORT_ALLOC_FAILED, kstatus, __LINE__);
return NOTIFY_STATUS_FAILED;
}
}
port = *notify_port;
if (!MACH_PORT_VALID(port))
{
return NOTIFY_STATUS_INVALID_PORT;
}
/*
* If we use dispatch to forward notifications to this port, the the library needs the send right.
*/
kstatus = mach_port_insert_right(mach_task_self(), port, port, MACH_MSG_TYPE_MAKE_SEND);
if (kstatus != KERN_SUCCESS)
{
if (mine) mach_port_destruct(mach_task_self(), port, 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_INVALID_PORT;
}
return_status = _notify_register_dispatch_with_extra_mp(name, out_token, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0),
^(int token){
notify_mach_port(token, port, globals);
}, *notify_port);
if(return_status != NOTIFY_STATUS_OK && mine) {
mach_port_destruct(mach_task_self(), port, 0, 0);
}
if (return_status == NOTIFY_STATUS_OK) {
notify_retain_mach_port(globals, *notify_port, flags);
NOTIFY_REGISTER_MACH_PORT(name, *notify_port, flags, *out_token);
}
#ifdef DEBUG
if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
return return_status;
}
/*
* Port ownership rules:
* - notify common port is a pure receive right, with no send rights in our space
*
* - if the client doesn't pass NOTIFY_REUSE then we make a receive right that
* we track in the mach port references (notify_retain_mach_port, mpl_mine).
*
* - if we forward notifications to a local port, the library owns a send right
* tracked in the the registration with NOTIFY_FLAG_RELEASE_SEND
*
* - self notifications own a send right, tracked by the client_t structure
*/
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
uint32_t status;
notify_globals_t globals = _notify_globals();
status = regenerate_check(globals);
if (status != NOTIFY_STATUS_OK)
{
if(IS_INTERNAL_ERROR(status))
{
REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__);
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;
}
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;
}
if (!strncmp(name, SELF_PREFIX, SELF_PREFIX_LEN))
{
#ifdef DEBUG
if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
return notify_register_mach_port_self(name, notify_port, flags, out_token, globals);
}
else if ((client_opts(globals) & NOTIFY_OPT_DISPATCH) && ((flags & NOTIFY_NO_DISPATCH) == 0))
{
#ifdef DEBUG
if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
return notify_register_mach_port_via_dispatch(name, notify_port, flags, out_token, globals);
}
else
{
#ifdef DEBUG
if (_libnotify_debug & DEBUG_API) _notify_client_log(ASL_LEVEL_NOTICE, "<- %s [%d]\n", __func__, __LINE__ + 2);
#endif
return notify_register_mach_port_no_dispatch(name, notify_port, flags, out_token, globals);
}
}
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
uint32_t i, status;
uint64_t nid;
int token, mine, fdpair[2];
fileport_t fileport;
kern_return_t kstatus;
uint32_t cid = -1;
notify_globals_t globals = _notify_globals();
status = regenerate_check(globals);
if (status != NOTIFY_STATUS_OK)
{
if(IS_INTERNAL_ERROR(status))
{
REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__);
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;
}
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;
}
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
REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__,
NOTIFY_STATUS_PIPE_FAILED, __LINE__);
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))
{
token = atomic_increment32(&globals->token_id);
status = _notify_lib_register_file_descriptor(&globals->self_state, name, NOTIFY_CLIENT_SELF, token, fdpair[1], 0, 0, &nid);
if (status != NOTIFY_STATUS_OK)
{
if (mine == 1)
{
close(fdpair[0]);
close(fdpair[1]);
}
if(IS_INTERNAL_ERROR(status))
{
REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__);
status = NOTIFY_STATUS_FAILED;
}
#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;
status = client_registration_create(name, nid, token, cid, SLOT_NONE,
NOTIFY_FLAG_SELF | NOTIFY_TYPE_FILE, SIGNAL_NONE, *notify_fd, MACH_PORT_NULL);
if (status != NOTIFY_STATUS_OK)
{
if(IS_INTERNAL_ERROR(status))
{
REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__);
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;
}
*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 (client_opts(globals) & 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]);
}
if(IS_INTERNAL_ERROR(status))
{
REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__);
status = NOTIFY_STATUS_FAILED;
}
#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(globals, EVENT_INIT);
if (status != NOTIFY_STATUS_OK)
{
if (mine == 1)
{
close(fdpair[0]);
close(fdpair[1]);
}
if(IS_INTERNAL_ERROR(status))
{
REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__);
status = NOTIFY_STATUS_FAILED;
}
#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 status;
}
}
/* 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
REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__,
NOTIFY_STATUS_FILEPORT_MAKEPORT_FAILED, __LINE__);
return NOTIFY_STATUS_FAILED;
}
token = atomic_increment32(&globals->token_id);
kstatus = _notify_server_register_file_descriptor_2(globals->notify_server_port, (caddr_t)name, 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
REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d (%d) on line %d", __func__,
NOTIFY_STATUS_REG_FD_2_FAILED, kstatus, __LINE__);
return NOTIFY_STATUS_FAILED;
}
status = client_registration_create(name, NID_UNSET, token, cid, SLOT_NONE, NOTIFY_TYPE_FILE,
SIGNAL_NONE, *notify_fd, MACH_PORT_NULL);
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;
}
*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();
status = regenerate_check(globals);
if (status != NOTIFY_STATUS_OK)
{
if(IS_INTERNAL_ERROR(status))
{
REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__);
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;
}
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_NULL_INPUT;
}
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_NULL_INPUT 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_NULL_INPUT)) _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 (notify_is_type(r->flags, NOTIFY_TYPE_MEMORY))
{
if (globals->shm_base == NULL)
{
status = NOTIFY_STATUS_SHM_BASE_NULL;
#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;
mutex_lock("check_lock", &globals->check_lock, __func__, __LINE__);
if (r->val != globals->shm_base[r->slot])
{
*check = 1;
r->val = globals->shm_base[r->slot];
}
mutex_unlock("check_lock", &globals->check_lock, __func__, __LINE__);
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(globals, 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 (r->flags & NOTIFY_FLAG_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_SERVER_CHECK_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:
NOTIFY_CHECK(token, *check);
registration_node_release(r);
if(IS_INTERNAL_ERROR(status))
{
REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__);
status = NOTIFY_STATUS_FAILED;
}
#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;
}
/* Used in Libinfo without header declaration */
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();
status = regenerate_check(globals);
if (status != NOTIFY_STATUS_OK)
{
if(IS_INTERNAL_ERROR(status))
{
REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__);
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;
}
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_NULL_INPUT 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 (notify_is_type(r->flags, NOTIFY_TYPE_MEMORY))
{
if (globals->shm_base == NULL)
{
status = NOTIFY_STATUS_SHM_BASE_NULL;
goto release_and_return;
}
*val = globals->shm_base[r->slot];
status = NOTIFY_STATUS_OK;
}
release_and_return:
registration_node_release(r);
if(IS_INTERNAL_ERROR(status))
{
REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__);
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;
}
/* Used by Libc, cctools, configd, kext_tools, Libnotify, and libresolv without header declaration */
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;
registration_node_t *r;
char *dup;
notify_globals_t globals = _notify_globals();
status = regenerate_check(globals);
if (status != NOTIFY_STATUS_OK)
{
if(IS_INTERNAL_ERROR(status))
{
REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__);
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;
}
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(globals, EVENT_INIT);
if (status != NOTIFY_STATUS_OK)
{
registration_node_release(r);
if(IS_INTERNAL_ERROR(status))
{
REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__);
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;
}
}
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
REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__,
NOTIFY_STATUS_STRDUP_FAILED, __LINE__);
return NOTIFY_STATUS_FAILED;
}
{
int xtoken = token;
int len = (uint32_t)(strlen(path) + 1);
if (r->flags & NOTIFY_FLAG_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
REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d (%d) on line %d", __func__,
NOTIFY_STATUS_STRDUP_FAILED, kstatus, __LINE__);
return NOTIFY_STATUS_SERVER_MONITOR_FILE_2_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;
}
/* Used by kext_tools without header declaration */
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
static bool report_deprecated_once = true;
if(report_deprecated_once)
{
REPORT_BAD_BEHAVIOR("Libnotify client using deprecated function notify_get_event; this function does nothing");
report_deprecated_once = false;
}
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();
status = regenerate_check(globals);
if (status != NOTIFY_STATUS_OK)
{
if(IS_INTERNAL_ERROR(status))
{
REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__);
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;
}
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_NULL_INPUT 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);
if(IS_INTERNAL_ERROR(status))
{
REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__);
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;
}
if (globals->notify_server_port == MACH_PORT_NULL)
{
status = _notify_lib_init(globals, EVENT_INIT);
if (status != NOTIFY_STATUS_OK)
{
registration_node_release(r);
if(IS_INTERNAL_ERROR(status))
{
REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__);
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;
}
}
{
name_node_t *name_node = r->name_node;
int xtoken = token;
if (r->flags & NOTIFY_FLAG_COALESCED) xtoken = name_node->coalesce_base_token;
// This is a race, but it is safe. The worst case is that nid is looked-up and set twice.
mutex_lock(name_node->name, &name_node->lock, __func__, __LINE__);
nid = name_node->name_id;
mutex_unlock(name_node->name, &name_node->lock, __func__, __LINE__);
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(name_node, nid);
}
else
{
kstatus = _notify_server_get_state_2(globals->notify_server_port, nid, state, (int32_t *)&status);
}
}
registration_node_release(r);
if (kstatus != KERN_SUCCESS)
{
status = NOTIFY_STATUS_SERVER_GET_STATE_2_FAILED;
}
if(IS_INTERNAL_ERROR(status))
{
REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d (%d) on line %d", __func__, status, kstatus, __LINE__);
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_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();
status = regenerate_check(globals);
if (status != NOTIFY_STATUS_OK)
{
if(IS_INTERNAL_ERROR(status))
{
REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__);
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;
}
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_NULL_INPUT 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);
if(IS_INTERNAL_ERROR(status))
{
REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__);
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;
}
if (globals->notify_server_port == MACH_PORT_NULL)
{
status = _notify_lib_init(globals, EVENT_INIT);
if (status != NOTIFY_STATUS_OK)
{
registration_node_release(r);
if(IS_INTERNAL_ERROR(status))
{
REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__);
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;
}
}
status = NOTIFY_STATUS_OK;
{
name_node_t *name_node = r->name_node;
int xtoken = token;
if (r->flags & NOTIFY_FLAG_COALESCED) xtoken = name_node->coalesce_base_token;
// This is a race, but it is safe. The worst case is that nid is looked-up and set twice.
mutex_lock(name_node->name, &name_node->lock, __func__, __LINE__);
nid = name_node->name_id;
mutex_unlock(name_node->name, &name_node->lock, __func__, __LINE__);
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, should_claim_root_access());
if ((kstatus == KERN_SUCCESS) && (status == NOTIFY_STATUS_OK)) name_node_set_nid(name_node, nid);
}
else
{
status = NOTIFY_STATUS_OK;
kstatus = _notify_server_set_state_2(globals->notify_server_port, nid, state, should_claim_root_access());
}
}
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
REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d (%d) on line %d", __func__,
NOTIFY_STATUS_SERVER_SET_STATE_2_FAILED, kstatus, __LINE__);
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;
notify_globals_t globals = _notify_globals();
uint32_t status;
status = regenerate_check(globals);
if (status != NOTIFY_STATUS_OK)
{
if(IS_INTERNAL_ERROR(status))
{
REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__);
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;
}
mutex_lock("global", &globals->notify_lock, __func__, __LINE__);
r = _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 (!(r->flags & NOTIFY_FLAG_CANCELED)) {
r->flags |= NOTIFY_FLAG_CANCELED;
registration_node_release_locked(globals, r);
}
mutex_unlock("global", &globals->notify_lock, __func__, __LINE__);
// Always return OK here as there's nothing a client can do if the server call failed
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;
kern_return_t kstatus = KERN_SUCCESS;
notify_globals_t globals = _notify_globals();
status = regenerate_check(globals);
if (status != NOTIFY_STATUS_OK)
{
if(IS_INTERNAL_ERROR(status))
{
REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__);
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;
}
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(globals, EVENT_INIT);
if (status != NOTIFY_STATUS_OK)
{
registration_node_release(r);
if(IS_INTERNAL_ERROR(status))
{
REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__);
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;
}
}
mutex_lock(r->name_node->name, &r->name_node->lock, __func__, __LINE__);
if (r->flags & NOTIFY_FLAG_COALESCED)
{
r->flags |= NOTIFY_FLAG_SUSPENDED;
mutex_unlock(r->name_node->name, &r->name_node->lock, __func__, __LINE__);
} else {
mutex_unlock(r->name_node->name, &r->name_node->lock, __func__, __LINE__);
kstatus = _notify_server_suspend(globals->notify_server_port, token, (int32_t *)&status);
}
registration_node_release(r);
if (kstatus != KERN_SUCCESS) status = NOTIFY_STATUS_SERVER_SUSPEND_FAILED;
if(IS_INTERNAL_ERROR(status))
{
REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d (%d) on line %d", __func__, status, kstatus, __LINE__);
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;
kern_return_t kstatus = KERN_SUCCESS;
notify_globals_t globals = _notify_globals();
status = regenerate_check(globals);
if (status != NOTIFY_STATUS_OK)
{
if(IS_INTERNAL_ERROR(status))
{
REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__);
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;
}
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(globals, EVENT_INIT);
if (status != NOTIFY_STATUS_OK)
{
registration_node_release(r);
if(IS_INTERNAL_ERROR(status))
{
REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__);
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;
}
}
mutex_lock(r->name_node->name, &r->name_node->lock, __func__, __LINE__);
if (r->flags & NOTIFY_FLAG_COALESCED)
{
bool deferred_post = r->flags & NOTIFY_FLAG_DEFERRED_POST;
r->flags &= ~(NOTIFY_FLAG_SUSPENDED | NOTIFY_FLAG_DEFERRED_POST);
mutex_unlock(r->name_node->name, &r->name_node->lock, __func__, __LINE__);
if (deferred_post)
{
_notify_dispatch_local_notification(r);
}
} else {
mutex_unlock(r->name_node->name, &r->name_node->lock, __func__, __LINE__);
kstatus = _notify_server_resume(globals->notify_server_port, token, (int32_t *)&status);
}
registration_node_release(r);
if (kstatus != KERN_SUCCESS) status = NOTIFY_STATUS_SERVER_RESUME_FAILED;
if(IS_INTERNAL_ERROR(status))
{
REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d (%d) on line %d", __func__, status, kstatus, __LINE__);
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 = NOTIFY_STATUS_OK;
kern_return_t kstatus;
notify_globals_t globals = _notify_globals();
if (globals->notify_server_port == MACH_PORT_NULL)
{
status = _notify_lib_init(globals, EVENT_INIT);
if (status != NOTIFY_STATUS_OK)
{
if(IS_INTERNAL_ERROR(status))
{
REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__);
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;
}
}
kstatus = _notify_server_suspend_pid(globals->notify_server_port, pid);
if(kstatus != KERN_SUCCESS)
{
REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, kstatus, __LINE__);
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 = NOTIFY_STATUS_OK;
kern_return_t kstatus;
notify_globals_t globals = _notify_globals();
if (globals->notify_server_port == MACH_PORT_NULL)
{
status = _notify_lib_init(globals, EVENT_INIT);
if (status != NOTIFY_STATUS_OK)
{
if(IS_INTERNAL_ERROR(status))
{
REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__);
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;
}
}
kstatus = _notify_server_resume_pid(globals->notify_server_port, pid);
if(kstatus != KERN_SUCCESS)
{
REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, kstatus, __LINE__);
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
static bool report_deprecated_once = true;
if(report_deprecated_once)
{
REPORT_BAD_BEHAVIOR("Libnotify client using deprecated function notify_simple_post, use notify_post instead");
report_deprecated_once = false;
}
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;
}
uint32_t
notify_dump_status(const char *filepath)
{
notify_globals_t globals;
uint32_t status;
uint32_t kstatus;
int fileport_status;
int file_descriptor;
fileport_t fileport;
globals = _notify_globals();
if(globals->notify_server_port == MACH_PORT_NULL)
{
status = _notify_lib_init(globals, EVENT_INIT);
if (status != NOTIFY_STATUS_OK)
{
if(IS_INTERNAL_ERROR(status))
{
REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d", __func__, status, __LINE__);
status = NOTIFY_STATUS_FAILED;
}
return status;
}
}
file_descriptor = creat(filepath, S_IWUSR);
if(file_descriptor < 0)
{
REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d (errno: %d)", __func__, NOTIFY_STATUS_FAILED, __LINE__, errno);
return NOTIFY_STATUS_FAILED;
}
fileport = FILEPORT_NULL;
fileport_status = fileport_makeport(file_descriptor, &fileport);
if(fileport_status < 0)
{
close(file_descriptor);
REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d (fileport_status: %d)", __func__, NOTIFY_STATUS_FAILED, __LINE__, fileport_status);
return NOTIFY_STATUS_FAILED;
}
kstatus = _notify_server_dump(globals->notify_server_port, (mach_port_t)fileport);
close(file_descriptor);
if(kstatus != KERN_SUCCESS)
{
REPORT_BAD_BEHAVIOR("Libnotify: %s failed with code %d on line %d (kstatus: %d)", __func__, NOTIFY_STATUS_FAILED, __LINE__, kstatus);
return NOTIFY_STATUS_FAILED;
}
return NOTIFY_STATUS_OK;
}