mirror of
https://github.com/darlinghq/darling-Libnotify.git
synced 2024-11-26 21:50:29 +00:00
4405 lines
133 KiB
C
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(®->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(®_node->refcount, 1, relaxed);
|
|
reg_node->token = token;
|
|
_nc_table_insert_n(&globals->registration_table, ®_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(®_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, ®_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;
|
|
}
|
|
|
|
|