#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/un.h>
#include <sys/ipc.h>
#include <sys/signal.h>
#include <sys/syslimits.h>
#include <mach/mach.h>
#include <mach/mach_time.h>
#include <sys/mman.h>
#include <sys/fcntl.h>
#include <sys/time.h>
#include <bootstrap_priv.h>
#include <errno.h>
#include <pthread.h>
#include <TargetConditionals.h>
#include <libkern/OSAtomic.h>
#include <Block.h>
#include <dispatch/dispatch.h>
#include <dispatch/private.h>
#include <_simple.h>
#include "libnotify.h"
#include "notify.h"
#include "notify_internal.h"
#include "notify_ipc.h"
#include "notify_private.h"
#if TARGET_IPHONE_SIMULATOR
#define INITIAL_TOKEN_ID (1 << 20)
#else
#define INITIAL_TOKEN_ID 0
#endif
WEAK_IMPORT_ATTRIBUTE bool _dispatch_is_multithreaded(void);
#define EVENT_INIT 0
#define EVENT_REGEN 1
#define SELF_PREFIX "self."
#define SELF_PREFIX_LEN 5
#define COMMON_SELF_PORT_KEY "self.com.apple.system.notify.common"
#define MULTIPLE_REGISTRATION_WARNING_TRIGGER 20
extern uint32_t _notify_lib_peek(notify_state_t *ns, pid_t pid, int token, int *val);
extern int *_notify_lib_check_addr(notify_state_t *ns, pid_t pid, int token);
#define CLIENT_TOKEN_TABLE_SIZE 256
#define NID_UNSET 0xffffffffffffffffL
#define NID_CALLED_ONCE 0xfffffffffffffffeL
#define NO_LOCK 1
typedef struct
{
uint32_t refcount;
uint64_t name_id;
} name_table_node_t;
typedef struct
{
uint32_t refcount;
const char *name;
size_t namelen;
name_table_node_t *name_node;
uint32_t token;
uint32_t slot;
uint32_t val;
uint32_t flags;
int fd;
int signal;
mach_port_t mp;
uint32_t client_id;
uint64_t set_state_time;
uint64_t set_state_val;
char * path;
int path_flags;
dispatch_queue_t queue;
notify_handler_t block;
} token_table_node_t;
static void _notify_lib_regenerate(int src);
static void notify_retain_mach_port(mach_port_t mp, int mine);
static void _notify_dispatch_handle(mach_port_t port);
static notify_state_t *_notify_lib_self_state();
static int
shm_attach(uint32_t size)
{
int32_t shmfd;
notify_globals_t globals = _notify_globals();
shmfd = shm_open(SHM_ID, O_RDONLY, 0);
if (shmfd == -1) return -1;
globals->shm_base = mmap(NULL, size, PROT_READ, MAP_SHARED, shmfd, 0);
close(shmfd);
if (globals->shm_base == (uint32_t *)-1) globals->shm_base = NULL;
if (globals->shm_base == NULL) return -1;
return 0;
}
#ifdef NOTDEF
static void
shm_detach(void)
{
if (shm_base != NULL)
{
shmdt(shm_base);
shm_base = NULL;
}
}
#endif
void
_notify_init_globals(void * _globals)
{
notify_globals_t globals = _globals;
pthread_mutex_init(&globals->notify_lock, NULL);
globals->token_id = INITIAL_TOKEN_ID;
globals->notify_common_token = -1;
}
#if !_NOTIFY_HAS_ALLOC_ONCE
notify_globals_t
_notify_globals_impl(void)
{
static dispatch_once_t once;
static notify_globals_t globals;
dispatch_once(&once, ^{
globals = calloc(1, sizeof(struct notify_globals_s));
_notify_init_globals(globals);
});
return globals;
}
#endif
static uint32_t
_notify_lib_init(uint32_t event)
{
__block kern_return_t kstatus;
__block bool first = false;
int status, cid;
uint64_t state;
notify_globals_t globals = _notify_globals();
if (globals->client_opts & NOTIFY_OPT_DISABLE) return NOTIFY_STATUS_FAILED;
kstatus = KERN_SUCCESS;
dispatch_once(&globals->notify_server_port_once, ^{
first = true;
kstatus = bootstrap_look_up2(bootstrap_port, NOTIFY_SERVICE_NAME, &globals->notify_server_port, 0, BOOTSTRAP_PRIVILEGED_SERVER);
});
if (kstatus != KERN_SUCCESS) return NOTIFY_STATUS_FAILED;
pthread_mutex_lock(&globals->notify_lock);
if (_dispatch_is_multithreaded)
{
if (_dispatch_is_multithreaded()) globals->client_opts |= (NOTIFY_OPT_DEMUX | NOTIFY_OPT_REGEN);
}
if (first || (event == EVENT_REGEN))
{
pid_t last_pid = globals->notify_server_pid;
globals->notify_ipc_version = 0;
globals->notify_server_pid = 0;
kstatus = _notify_server_register_plain(globals->notify_server_port, NOTIFY_IPC_VERSION_NAME, NOTIFY_IPC_VERSION_NAME_LEN, &cid, &status);
if ((kstatus == KERN_SUCCESS) && (status == NOTIFY_STATUS_OK))
{
kstatus = _notify_server_get_state(globals->notify_server_port, cid, &state, &status);
if ((kstatus == KERN_SUCCESS) && (status == NOTIFY_STATUS_OK))
{
globals->notify_ipc_version = state;
state >>= 32;
globals->notify_server_pid = state;
}
_notify_server_cancel(globals->notify_server_port, cid, &status);
if ((last_pid == globals->notify_server_pid) && (event == EVENT_REGEN))
{
pthread_mutex_unlock(&globals->notify_lock);
return NOTIFY_STATUS_INVALID_REQUEST;
}
}
if (globals->server_proc_source != NULL)
{
dispatch_source_cancel(globals->server_proc_source);
dispatch_release(globals->server_proc_source);
globals->server_proc_source = NULL;
}
}
if (globals->notify_ipc_version < 2)
{
globals->client_opts &= ~NOTIFY_OPT_REGEN;
}
if ((globals->server_proc_source == NULL) && (globals->client_opts & NOTIFY_OPT_REGEN) && (globals->notify_server_pid != 0))
{
globals->server_proc_source = dispatch_source_create(DISPATCH_SOURCE_TYPE_PROC, (uintptr_t)globals->notify_server_pid, DISPATCH_PROC_EXIT, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0));
dispatch_source_set_event_handler(globals->server_proc_source, ^{ _notify_lib_regenerate(1); });
dispatch_resume(globals->server_proc_source);
}
if ((globals->client_opts & NOTIFY_OPT_DEMUX) && (globals->notify_common_port == MACH_PORT_NULL))
{
kern_return_t kr;
task_t task = mach_task_self();
kr = mach_port_allocate(task, MACH_PORT_RIGHT_RECEIVE, &globals->notify_common_port);
if (kr == KERN_SUCCESS)
{
globals->notify_dispatch_source = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV, globals->notify_common_port, 0, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0));
dispatch_source_set_event_handler(globals->notify_dispatch_source, ^{
notify_globals_t globals = _notify_globals();
_notify_dispatch_handle(globals->notify_common_port);
});
dispatch_source_set_cancel_handler(globals->notify_dispatch_source, ^{
task_t task = mach_task_self();
notify_globals_t globals = _notify_globals();
mach_port_mod_refs(task, globals->notify_common_port, MACH_PORT_RIGHT_RECEIVE, -1);
});
dispatch_resume(globals->notify_dispatch_source);
}
}
pthread_mutex_unlock(&globals->notify_lock);
if (globals->notify_common_port != MACH_PORT_NULL && (first || event == EVENT_REGEN))
{
status = notify_register_mach_port(COMMON_PORT_KEY, &globals->notify_common_port, NOTIFY_REUSE, &globals->notify_common_token);
}
return NOTIFY_STATUS_OK;
}
void
_notify_fork_child(void)
{
notify_globals_t globals = _notify_globals();
_notify_init_globals(globals);
if (globals->notify_server_port != MACH_PORT_NULL) globals->client_opts = NOTIFY_OPT_DISABLE;
if (_dispatch_is_multithreaded) {
if (_dispatch_is_multithreaded()) globals->client_opts = NOTIFY_OPT_DISABLE;
}
globals->self_state = NULL;
globals->notify_server_port = MACH_PORT_NULL;
globals->notify_ipc_version = 0;
globals->notify_server_pid = 0;
globals->token_table = NULL;
globals->token_name_table = NULL;
globals->fd_count = 0;
globals->fd_clnt = NULL;
globals->fd_srv = NULL;
globals->fd_refcount = NULL;
globals->mp_count = 0;
globals->mp_list = NULL;
globals->mp_refcount = NULL;
globals->mp_mine = NULL;
globals->shm_base = NULL;
}
static uint32_t
token_table_add(const char *name, size_t namelen, uint64_t nid, uint32_t token, uint32_t cid, uint32_t slot, uint32_t flags, int sig, int fd, mach_port_t mp, int lock)
{
token_table_node_t *t;
name_table_node_t *n;
uint32_t warn_count = 0;
notify_globals_t globals = _notify_globals();
dispatch_once(&globals->token_table_once, ^{
globals->token_table = _nc_table_new(CLIENT_TOKEN_TABLE_SIZE);
globals->token_name_table = _nc_table_new(CLIENT_TOKEN_TABLE_SIZE);
});
if (globals->token_table == NULL) return -1;
if (globals->token_name_table == NULL) return -1;
if (name == NULL) return -1;
t = (token_table_node_t *)calloc(1, sizeof(token_table_node_t));
if (t == NULL) return -1;
t->refcount = 1;
t->name = NULL;
t->namelen = namelen;
t->token = token;
t->slot = slot;
t->val = 0;
t->flags = flags;
t->fd = fd;
t->mp = mp;
t->client_id = cid;
if (lock != NO_LOCK) pthread_mutex_lock(&globals->notify_lock);
_nc_table_insert_n(globals->token_table, t->token, t);
n = _nc_table_find_get_key(globals->token_name_table, name, &(t->name));
if (n == NULL)
{
char *copy_name = strdup(name);
if (copy_name == NULL)
{
free(t);
if (lock != NO_LOCK) pthread_mutex_unlock(&globals->notify_lock);
return -1;
}
t->name = (const char *)copy_name;
n = (name_table_node_t *)calloc(1, sizeof(name_table_node_t));
if (n != NULL)
{
n->refcount = 1;
n->name_id = nid;
_nc_table_insert_pass(globals->token_name_table, copy_name, n);
t->name_node = n;
}
}
else
{
t->name_node = n;
n->refcount++;
if ((n->refcount % MULTIPLE_REGISTRATION_WARNING_TRIGGER) == 0)
{
warn_count = n->refcount;
}
}
if (lock != NO_LOCK) pthread_mutex_unlock(&globals->notify_lock);
if (warn_count > 0)
{
char *msg;
asprintf(&msg, "notify name \"%s\" has been registered %d times - this may be a leak", name, warn_count);
if (msg)
_simple_asl_log(ASL_LEVEL_WARNING, "com.apple.notify", msg);
free(msg);
}
return 0;
}
static token_table_node_t *
token_table_find_retain(uint32_t token)
{
token_table_node_t *t;
notify_globals_t globals = _notify_globals();
pthread_mutex_lock(&globals->notify_lock);
t = (token_table_node_t *)_nc_table_find_n(globals->token_table, token);
if (t != NULL) t->refcount++;
pthread_mutex_unlock(&globals->notify_lock);
return t;
}
static token_table_node_t *
token_table_find_no_lock(uint32_t token)
{
notify_globals_t globals = _notify_globals();
return (token_table_node_t *)_nc_table_find_n(globals->token_table, token);
}
static name_table_node_t *
name_table_find_retain_no_lock(const char *name)
{
name_table_node_t *n;
notify_globals_t globals = _notify_globals();
n = (name_table_node_t *)_nc_table_find(globals->token_name_table, name);
if (n != NULL) n->refcount++;
return n;
}
static void
name_table_release_no_lock(const char *name)
{
name_table_node_t *n;
notify_globals_t globals = _notify_globals();
n = (name_table_node_t *)_nc_table_find(globals->token_name_table, name);
if (n != NULL)
{
if (n->refcount > 0) n->refcount--;
if (n->refcount == 0)
{
_nc_table_delete(globals->token_name_table, name);
free(n);
}
}
}
static void
name_table_set_nid(const char *name, uint64_t nid)
{
name_table_node_t *n;
notify_globals_t globals = _notify_globals();
pthread_mutex_lock(&globals->notify_lock);
n = (name_table_node_t *)_nc_table_find(globals->token_name_table, name);
if (n != NULL) n->name_id = nid;
pthread_mutex_unlock(&globals->notify_lock);
}
static void
_notify_lib_regenerate_token(token_table_node_t *t)
{
uint32_t type;
int status, new_slot;
kern_return_t kstatus;
mach_port_t port;
uint64_t new_nid;
size_t pathlen;
if (t == NULL) return;
if (t->name == NULL) return;
if (t->flags & NOTIFY_FLAG_SELF) return;
if ((t->flags & NOTIFY_FLAG_REGEN) == 0) return;
if (!strcmp(t->name, COMMON_PORT_KEY)) return;
notify_globals_t globals = _notify_globals();
port = MACH_PORT_NULL;
if (t->flags & NOTIFY_TYPE_PORT)
{
port = globals->notify_common_port;
}
pathlen = 0;
if (t->path != NULL) pathlen = strlen(t->path);
type = t->flags & 0x000000ff;
kstatus = _notify_server_regenerate(globals->notify_server_port, (caddr_t)t->name, t->namelen, t->token, type, port, t->signal, t->slot, t->set_state_val, t->set_state_time, t->path, pathlen, t->path_flags, &new_slot, &new_nid, &status);
if (kstatus != KERN_SUCCESS) status = NOTIFY_STATUS_FAILED;
if (status != NOTIFY_STATUS_OK) return;
t->slot = new_slot;
if (t->name_node != NULL) t->name_node->name_id = new_nid;
}
static void
_notify_lib_regenerate(int src)
{
void *tt;
token_table_node_t *t;
notify_globals_t globals = _notify_globals();
if ((globals->client_opts & NOTIFY_OPT_REGEN) == 0) return;
if (_notify_lib_init(EVENT_REGEN) == NOTIFY_STATUS_OK)
{
pthread_mutex_lock(&globals->notify_lock);
tt = _nc_table_traverse_start(globals->token_table);
while (tt != NULL)
{
t = _nc_table_traverse(globals->token_table, tt);
if (t == NULL) break;
_notify_lib_regenerate_token(t);
}
_nc_table_traverse_end(globals->token_table, tt);
pthread_mutex_unlock(&globals->notify_lock);
}
}
static inline void
regenerate_check()
{
notify_globals_t globals = _notify_globals();
if ((globals->client_opts & NOTIFY_OPT_REGEN) == 0) return;
if ((globals->shm_base != NULL) && (globals->shm_base[0] != globals->notify_server_pid)) _notify_lib_regenerate(0);
}
static void
notify_retain_file_descriptor(int clnt, int srv)
{
int x, i;
notify_globals_t globals = _notify_globals();
if (clnt < 0) return;
if (srv < 0) return;
pthread_mutex_lock(&globals->notify_lock);
x = -1;
for (i = 0; (i < globals->fd_count) && (x < 0); i++)
{
if (globals->fd_clnt[i] == clnt) x = i;
}
if (x >= 0)
{
globals->fd_refcount[x]++;
pthread_mutex_unlock(&globals->notify_lock);
return;
}
x = globals->fd_count;
globals->fd_count++;
if (x == 0)
{
globals->fd_clnt = (int *)calloc(1, sizeof(int));
globals->fd_srv = (int *)calloc(1, sizeof(int));
globals->fd_refcount = (int *)calloc(1, sizeof(int));
}
else
{
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))
{
free(globals->fd_clnt);
free(globals->fd_srv);
free(globals->fd_refcount);
globals->fd_count = 0;
}
else
{
globals->fd_clnt[x] = clnt;
globals->fd_srv[x] = srv;
globals->fd_refcount[x] = 1;
}
pthread_mutex_unlock(&globals->notify_lock);
}
static void
notify_release_file_descriptor(int fd)
{
int x, i, j;
notify_globals_t globals = _notify_globals();
if (fd < 0) return;
x = -1;
for (i = 0; (i < globals->fd_count) && (x < 0); i++)
{
if (globals->fd_clnt[i] == fd) x = i;
}
if (x < 0) 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);
free(globals->fd_srv);
free(globals->fd_refcount);
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))
{
free(globals->fd_clnt);
free(globals->fd_srv);
free(globals->fd_refcount);
globals->fd_count = 0;
}
}
static void
notify_retain_mach_port(mach_port_t mp, int mine)
{
int x, i;
notify_globals_t globals = _notify_globals();
if (mp == MACH_PORT_NULL) return;
pthread_mutex_lock(&globals->notify_lock);
x = -1;
for (i = 0; (i < globals->mp_count) && (x < 0); i++)
{
if (globals->mp_list[i] == mp) x = i;
}
if (x >= 0)
{
globals->mp_refcount[x]++;
pthread_mutex_unlock(&globals->notify_lock);
return;
}
x = globals->mp_count;
globals->mp_count++;
if (x == 0)
{
globals->mp_list = (mach_port_t *)calloc(1, sizeof(mach_port_t));
globals->mp_refcount = (int *)calloc(1, sizeof(int));
globals->mp_mine = (int *)calloc(1, sizeof(int));
}
else
{
globals->mp_list = (mach_port_t *)reallocf(globals->mp_list, globals->mp_count * sizeof(mach_port_t));
globals->mp_refcount = (int *)reallocf(globals->mp_refcount, globals->mp_count * sizeof(int));
globals->mp_mine = (int *)reallocf(globals->mp_mine, globals->mp_count * sizeof(int));
}
if ((globals->mp_list == NULL) || (globals->mp_refcount == NULL) || (globals->mp_mine == NULL))
{
if (globals->mp_list != NULL) free(globals->mp_list);
if (globals->mp_refcount != NULL) free(globals->mp_refcount);
if (globals->mp_mine != NULL) free(globals->mp_mine);
globals->mp_count = 0;
}
else
{
globals->mp_list[x] = mp;
globals->mp_refcount[x] = 1;
globals->mp_mine[x] = mine;
}
pthread_mutex_unlock(&globals->notify_lock);
}
static void
notify_release_mach_port(mach_port_t mp, uint32_t flags)
{
int x, i;
notify_globals_t globals = _notify_globals();
if (mp == MACH_PORT_NULL) return;
x = -1;
for (i = 0; (i < globals->mp_count) && (x < 0); i++)
{
if (globals->mp_list[i] == mp) x = i;
}
if (x < 0) return;
if (globals->mp_refcount[x] > 0) globals->mp_refcount[x]--;
if (globals->mp_refcount[x] > 0) return;
if (globals->mp_mine[x] == 1)
{
mach_port_mod_refs(mach_task_self(), mp, MACH_PORT_RIGHT_RECEIVE, -1);
if (flags & NOTIFY_FLAG_SELF) mach_port_deallocate(mach_task_self(), mp);
}
if (flags & NOTIFY_FLAG_RELEASE_SEND)
{
mach_port_deallocate(mach_task_self(), mp);
}
if (globals->mp_count == 1)
{
if (globals->mp_list != NULL) free(globals->mp_list);
if (globals->mp_refcount != NULL) free(globals->mp_refcount);
if (globals->mp_mine != NULL) free(globals->mp_mine);
globals->mp_count = 0;
return;
}
for (i = x + 1; i < globals->mp_count; i++)
{
globals->mp_list[i - 1] = globals->mp_list[i];
globals->mp_refcount[i - 1] = globals->mp_refcount[i];
globals->mp_mine[i - 1] = globals->mp_mine[i];
}
globals->mp_count--;
globals->mp_list = (mach_port_t *)reallocf(globals->mp_list, globals->mp_count * sizeof(mach_port_t));
globals->mp_refcount = (int *)reallocf(globals->mp_refcount, globals->mp_count * sizeof(int));
globals->mp_mine = (int *)reallocf(globals->mp_mine, globals->mp_count * sizeof(int));
if ((globals->mp_list == NULL) || (globals->mp_refcount == NULL) || (globals->mp_mine == NULL))
{
if (globals->mp_list != NULL) free(globals->mp_list);
if (globals->mp_refcount != NULL) free(globals->mp_refcount);
if (globals->mp_mine != NULL) free(globals->mp_mine);
globals->mp_count = 0;
}
}
static void
token_table_release_no_lock(token_table_node_t *t)
{
notify_globals_t globals = _notify_globals();
if (t == NULL) return;
if (t->refcount > 0) t->refcount--;
if (t->refcount > 0) return;
notify_release_file_descriptor(t->fd);
notify_release_mach_port(t->mp, t->flags);
if (t->block != NULL)
{
dispatch_async_f(t->queue, t->block, (dispatch_function_t)_Block_release);
}
t->block = NULL;
if (t->queue != NULL) dispatch_release(t->queue);
t->queue = NULL;
_nc_table_delete_n(globals->token_table, t->token);
name_table_release_no_lock(t->name);
free(t->path);
free(t);
}
static void
token_table_release(token_table_node_t *t)
{
notify_globals_t globals = _notify_globals();
pthread_mutex_lock(&globals->notify_lock);
token_table_release_no_lock(t);
pthread_mutex_unlock(&globals->notify_lock);
}
static notify_state_t *
_notify_lib_self_state()
{
notify_globals_t globals = _notify_globals();
dispatch_once(&globals->self_state_once, ^{
globals->self_state = _notify_lib_notify_state_new(NOTIFY_STATE_USE_LOCKS, 0);
});
return globals->self_state;
}
void
notify_set_options(uint32_t opts)
{
notify_globals_t globals = _notify_globals();
if (globals->client_opts & NOTIFY_OPT_DISABLE) return;
globals->client_opts = opts;
_notify_lib_init(EVENT_INIT);
}
uint32_t
notify_post(const char *name)
{
notify_state_t *ns_self;
kern_return_t kstatus;
uint32_t status;
size_t namelen = 0;
name_table_node_t *n;
uint64_t nid = UINT64_MAX;
notify_globals_t globals = _notify_globals();
regenerate_check();
if (name == NULL) return NOTIFY_STATUS_INVALID_NAME;
if (!strncmp(name, SELF_PREFIX, SELF_PREFIX_LEN))
{
ns_self = _notify_lib_self_state();
if (ns_self == NULL) return NOTIFY_STATUS_FAILED;
_notify_lib_post(ns_self, name, 0, 0);
return NOTIFY_STATUS_OK;
}
if (globals->notify_server_port == MACH_PORT_NULL)
{
status = _notify_lib_init(EVENT_INIT);
if (status != 0) return NOTIFY_STATUS_FAILED;
}
if (globals->notify_ipc_version == 0)
{
namelen = strlen(name);
kstatus = _notify_server_post(globals->notify_server_port, (caddr_t)name, namelen, (int32_t *)&status);
if (kstatus != KERN_SUCCESS) return NOTIFY_STATUS_FAILED;
return status;
}
namelen = strlen(name);
pthread_mutex_lock(&globals->notify_lock);
n = name_table_find_retain_no_lock(name);
if (n != NULL)
{
if (n->name_id == NID_UNSET)
{
kstatus = _notify_server_post_4(globals->notify_server_port, (caddr_t)name, namelen);
if (kstatus != KERN_SUCCESS)
{
name_table_release_no_lock(name);
pthread_mutex_unlock(&globals->notify_lock);
return NOTIFY_STATUS_FAILED;
}
n->name_id = NID_CALLED_ONCE;
name_table_release_no_lock(name);
pthread_mutex_unlock(&globals->notify_lock);
return NOTIFY_STATUS_OK;
}
else if (n->name_id == NID_CALLED_ONCE)
{
kstatus = _notify_server_post_2(globals->notify_server_port, (caddr_t)name, namelen, &nid, (int32_t *)&status);
if (kstatus != KERN_SUCCESS)
{
name_table_release_no_lock(name);
pthread_mutex_unlock(&globals->notify_lock);
return NOTIFY_STATUS_FAILED;
}
if (status == NOTIFY_STATUS_OK) n->name_id = nid;
name_table_release_no_lock(name);
pthread_mutex_unlock(&globals->notify_lock);
return status;
}
else
{
kstatus = _notify_server_post_3(globals->notify_server_port, n->name_id);
name_table_release_no_lock(name);
pthread_mutex_unlock(&globals->notify_lock);
if (kstatus != KERN_SUCCESS) return NOTIFY_STATUS_FAILED;
return NOTIFY_STATUS_OK;
}
}
pthread_mutex_unlock(&globals->notify_lock);
kstatus = _notify_server_post_4(globals->notify_server_port, (caddr_t)name, namelen);
if (kstatus != KERN_SUCCESS) return NOTIFY_STATUS_FAILED;
return NOTIFY_STATUS_OK;
}
uint32_t
notify_set_owner(const char *name, uint32_t uid, uint32_t gid)
{
notify_state_t *ns_self;
kern_return_t kstatus;
uint32_t status;
notify_globals_t globals = _notify_globals();
if (name == NULL) return NOTIFY_STATUS_INVALID_NAME;
if (!strncmp(name, SELF_PREFIX, SELF_PREFIX_LEN))
{
ns_self = _notify_lib_self_state();
if (ns_self == NULL) return NOTIFY_STATUS_FAILED;
status = _notify_lib_set_owner(ns_self, name, uid, gid);
return status;
}
if (globals->notify_server_port == MACH_PORT_NULL)
{
status = _notify_lib_init(EVENT_INIT);
if (status != 0) return NOTIFY_STATUS_FAILED;
}
kstatus = _notify_server_set_owner(globals->notify_server_port, (caddr_t)name, strlen(name), uid, gid, (int32_t *)&status);
if (kstatus != KERN_SUCCESS) return NOTIFY_STATUS_FAILED;
return status;
}
uint32_t
notify_get_owner(const char *name, uint32_t *uid, uint32_t *gid)
{
notify_state_t *ns_self;
kern_return_t kstatus;
uint32_t status;
notify_globals_t globals = _notify_globals();
if (name == NULL) return NOTIFY_STATUS_INVALID_NAME;
if (!strncmp(name, SELF_PREFIX, SELF_PREFIX_LEN))
{
ns_self = _notify_lib_self_state();
if (ns_self == NULL) return NOTIFY_STATUS_FAILED;
status = _notify_lib_get_owner(ns_self, name, uid, gid);
return status;
}
if (globals->notify_server_port == MACH_PORT_NULL)
{
status = _notify_lib_init(EVENT_INIT);
if (status != 0) return NOTIFY_STATUS_FAILED;
}
kstatus = _notify_server_get_owner(globals->notify_server_port, (caddr_t)name, strlen(name), (int32_t *)uid, (int32_t *)gid, (int32_t *)&status);
if (kstatus != KERN_SUCCESS) return NOTIFY_STATUS_FAILED;
return status;
}
uint32_t
notify_set_access(const char *name, uint32_t access)
{
notify_state_t *ns_self;
kern_return_t kstatus;
uint32_t status;
notify_globals_t globals = _notify_globals();
if (name == NULL) return NOTIFY_STATUS_INVALID_NAME;
if (!strncmp(name, SELF_PREFIX, SELF_PREFIX_LEN))
{
ns_self = _notify_lib_self_state();
if (ns_self == NULL) return NOTIFY_STATUS_FAILED;
status = _notify_lib_set_access(ns_self, name, access);
return status;
}
if (globals->notify_server_port == MACH_PORT_NULL)
{
status = _notify_lib_init(EVENT_INIT);
if (status != 0) return NOTIFY_STATUS_FAILED;
}
kstatus = _notify_server_set_access(globals->notify_server_port, (caddr_t)name, strlen(name), access, (int32_t *)&status);
if (kstatus != KERN_SUCCESS) return NOTIFY_STATUS_FAILED;
return status;
}
uint32_t
notify_get_access(const char *name, uint32_t *access)
{
notify_state_t *ns_self;
kern_return_t kstatus;
uint32_t status;
notify_globals_t globals = _notify_globals();
if (name == NULL) return NOTIFY_STATUS_INVALID_NAME;
if (!strncmp(name, SELF_PREFIX, SELF_PREFIX_LEN))
{
ns_self = _notify_lib_self_state();
if (ns_self == NULL) return NOTIFY_STATUS_FAILED;
status = _notify_lib_get_access(ns_self, name, access);
return status;
}
if (globals->notify_server_port == MACH_PORT_NULL)
{
status = _notify_lib_init(EVENT_INIT);
if (status != 0) return NOTIFY_STATUS_FAILED;
}
kstatus = _notify_server_get_access(globals->notify_server_port, (caddr_t)name, strlen(name), (int32_t *)access, (int32_t *)&status);
if (kstatus != KERN_SUCCESS) return NOTIFY_STATUS_FAILED;
return status;
}
uint32_t
notify_release_name(const char *name)
{
return NOTIFY_STATUS_OK;
}
static void
_notify_dispatch_handle(mach_port_t port)
{
token_table_node_t *t;
int token;
mach_msg_empty_rcv_t msg;
kern_return_t status;
if (port == MACH_PORT_NULL) return;
memset(&msg, 0, sizeof(msg));
status = mach_msg(&msg.header, MACH_RCV_MSG, 0, sizeof(msg), port, 0, MACH_PORT_NULL);
if (status != KERN_SUCCESS) return;
token = msg.header.msgh_id;
t = token_table_find_retain(token);
if ((t != NULL) && (t->queue != NULL) && (t->block != NULL))
{
notify_handler_t theblock = Block_copy(t->block);
dispatch_queue_t thequeue = t->queue;
dispatch_retain(thequeue);
token_table_release(t);
dispatch_async(thequeue, ^{ theblock(token); });
_Block_release(theblock);
dispatch_release(thequeue);
}
}
uint32_t
notify_register_dispatch(const char *name, int *out_token, dispatch_queue_t queue, notify_handler_t handler)
{
__block uint32_t status;
token_table_node_t *t;
notify_globals_t globals = _notify_globals();
regenerate_check();
if (queue == NULL) return NOTIFY_STATUS_FAILED;
if (handler == NULL) return NOTIFY_STATUS_FAILED;
notify_set_options(NOTIFY_OPT_DEMUX | NOTIFY_OPT_REGEN);
status = notify_register_mach_port(name, &globals->notify_common_port, NOTIFY_REUSE, out_token);
if (status != NOTIFY_STATUS_OK) return status;
t = token_table_find_retain(*out_token);
if (t == NULL) return NOTIFY_STATUS_FAILED;
t->queue = queue;
dispatch_retain(t->queue);
t->block = Block_copy(handler);
token_table_release(t);
return NOTIFY_STATUS_OK;
}
static uint32_t
notify_register_mux_fd(const char *name, int *out_token, int rfd, int wfd)
{
__block uint32_t status;
token_table_node_t *t;
int val;
notify_globals_t globals = _notify_globals();
status = NOTIFY_STATUS_OK;
if (globals->notify_common_port == MACH_PORT_NULL) return NOTIFY_STATUS_FAILED;
status = notify_register_mach_port(name, &globals->notify_common_port, NOTIFY_REUSE, out_token);
t = token_table_find_retain(*out_token);
if (t == NULL) return NOTIFY_STATUS_FAILED;
t->token = *out_token;
t->fd = rfd;
t->queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
dispatch_retain(t->queue);
val = htonl(t->token);
t->block = (notify_handler_t)Block_copy(^(int unused){ write(wfd, &val, sizeof(val)); });
token_table_release(t);
return NOTIFY_STATUS_OK;
}
uint32_t
notify_register_check(const char *name, int *out_token)
{
notify_state_t *ns_self;
kern_return_t kstatus;
uint32_t status, token;
uint64_t nid;
int32_t slot, shmsize;
size_t namelen;
uint32_t cid;
notify_globals_t globals = _notify_globals();
regenerate_check();
if (name == NULL) return NOTIFY_STATUS_INVALID_NAME;
if (out_token == NULL) return NOTIFY_STATUS_FAILED;
*out_token = -1;
namelen = strlen(name);
if (!strncmp(name, SELF_PREFIX, SELF_PREFIX_LEN))
{
ns_self = _notify_lib_self_state();
if (ns_self == NULL) return NOTIFY_STATUS_FAILED;
token = OSAtomicIncrement32((int32_t *)&globals->token_id);
status = _notify_lib_register_plain(ns_self, name, NOTIFY_CLIENT_SELF, token, SLOT_NONE, 0, 0, &nid);
if (status != NOTIFY_STATUS_OK) return status;
cid = token;
token_table_add(name, namelen, nid, token, cid, SLOT_NONE, NOTIFY_FLAG_SELF | NOTIFY_TYPE_PLAIN, SIGNAL_NONE, FD_NONE, MACH_PORT_NULL, 0);
*out_token = token;
return NOTIFY_STATUS_OK;
}
if (globals->notify_server_port == MACH_PORT_NULL)
{
status = _notify_lib_init(EVENT_INIT);
if (status != 0) return NOTIFY_STATUS_FAILED;
}
token = OSAtomicIncrement32((int32_t *)&globals->token_id);
kstatus = KERN_SUCCESS;
if (globals->notify_ipc_version == 0)
{
nid = NID_UNSET;
kstatus = _notify_server_register_check(globals->notify_server_port, (caddr_t)name, namelen, &shmsize, &slot, (int32_t *)&cid, (int32_t *)&status);
}
else
{
cid = token;
kstatus = _notify_server_register_check_2(globals->notify_server_port, (caddr_t)name, namelen, token, &shmsize, &slot, &nid, (int32_t *)&status);
}
if (kstatus != KERN_SUCCESS) return NOTIFY_STATUS_FAILED;
if (status != NOTIFY_STATUS_OK) return status;
if (shmsize != -1)
{
if (globals->shm_base == NULL)
{
if (shm_attach(shmsize) != 0) return NOTIFY_STATUS_FAILED;
if (globals->shm_base == NULL) return NOTIFY_STATUS_FAILED;
}
token_table_add(name, namelen, nid, token, cid, slot, NOTIFY_TYPE_MEMORY | NOTIFY_FLAG_REGEN, SIGNAL_NONE, FD_NONE, MACH_PORT_NULL, 0);
}
else
{
token_table_add(name, namelen, nid, token, cid, SLOT_NONE, NOTIFY_TYPE_PLAIN | NOTIFY_FLAG_REGEN, SIGNAL_NONE, FD_NONE, MACH_PORT_NULL, 0);
}
*out_token = token;
return status;
}
uint32_t
notify_register_plain(const char *name, int *out_token)
{
notify_state_t *ns_self;
kern_return_t kstatus;
uint32_t status;
uint64_t nid;
size_t namelen;
int token;
uint32_t cid;
notify_globals_t globals = _notify_globals();
regenerate_check();
if (name == NULL) return NOTIFY_STATUS_INVALID_NAME;
namelen = strlen(name);
if (!strncmp(name, SELF_PREFIX, SELF_PREFIX_LEN))
{
ns_self = _notify_lib_self_state();
if (ns_self == NULL) return NOTIFY_STATUS_FAILED;
token = OSAtomicIncrement32((int32_t *)&globals->token_id);
status = _notify_lib_register_plain(ns_self, name, NOTIFY_CLIENT_SELF, token, SLOT_NONE, 0, 0, &nid);
if (status != NOTIFY_STATUS_OK) return status;
cid = token;
token_table_add(name, namelen, nid, token, cid, SLOT_NONE, NOTIFY_FLAG_SELF | NOTIFY_TYPE_PLAIN, SIGNAL_NONE, FD_NONE, MACH_PORT_NULL, 0);
*out_token = token;
return NOTIFY_STATUS_OK;
}
if (globals->notify_server_port == MACH_PORT_NULL)
{
status = _notify_lib_init(EVENT_INIT);
if (status != 0) return NOTIFY_STATUS_FAILED;
}
token = OSAtomicIncrement32((int32_t *)&globals->token_id);
if (globals->notify_ipc_version == 0)
{
kstatus = _notify_server_register_plain(globals->notify_server_port, (caddr_t)name, namelen, (int32_t *)&cid, (int32_t *)&status);
if (kstatus != KERN_SUCCESS) return NOTIFY_STATUS_FAILED;
if (status != NOTIFY_STATUS_OK) return status;
}
else
{
cid = token;
kstatus = _notify_server_register_plain_2(globals->notify_server_port, (caddr_t)name, namelen, token);
if (kstatus != KERN_SUCCESS) return NOTIFY_STATUS_FAILED;
}
token_table_add(name, namelen, NID_UNSET, token, cid, SLOT_NONE, NOTIFY_TYPE_PLAIN | NOTIFY_FLAG_REGEN, SIGNAL_NONE, FD_NONE, MACH_PORT_NULL, 0);
*out_token = token;
return NOTIFY_STATUS_OK;
}
uint32_t
notify_register_signal(const char *name, int sig, int *out_token)
{
notify_state_t *ns_self;
kern_return_t kstatus;
uint32_t status;
uint64_t nid;
size_t namelen;
int token;
uint32_t cid;
notify_globals_t globals = _notify_globals();
regenerate_check();
if (name == NULL) return NOTIFY_STATUS_INVALID_NAME;
namelen = strlen(name);
if (!strncmp(name, SELF_PREFIX, SELF_PREFIX_LEN))
{
ns_self = _notify_lib_self_state();
if (ns_self == NULL) return NOTIFY_STATUS_FAILED;
token = OSAtomicIncrement32((int32_t *)&globals->token_id);
status = _notify_lib_register_signal(ns_self, name, NOTIFY_CLIENT_SELF, token, sig, 0, 0, &nid);
if (status != NOTIFY_STATUS_OK) return status;
cid = token;
token_table_add(name, namelen, nid, token, cid, SLOT_NONE, NOTIFY_FLAG_SELF | NOTIFY_TYPE_SIGNAL, sig, FD_NONE, MACH_PORT_NULL, 0);
*out_token = token;
return NOTIFY_STATUS_OK;
}
if (globals->client_opts & NOTIFY_OPT_DEMUX)
{
return notify_register_dispatch(name, out_token, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^(int unused){ kill(getpid(), sig); });
}
if (globals->notify_server_port == MACH_PORT_NULL)
{
status = _notify_lib_init(EVENT_INIT);
if (status != 0) return NOTIFY_STATUS_FAILED;
}
token = OSAtomicIncrement32((int32_t *)&globals->token_id);
if (globals->notify_ipc_version == 0)
{
kstatus = _notify_server_register_signal(globals->notify_server_port, (caddr_t)name, namelen, sig, (int32_t *)&cid, (int32_t *)&status);
if (kstatus != KERN_SUCCESS) return NOTIFY_STATUS_FAILED;
if (status != NOTIFY_STATUS_OK) return status;
}
else
{
cid = token;
kstatus = _notify_server_register_signal_2(globals->notify_server_port, (caddr_t)name, namelen, token, sig);
if (kstatus != KERN_SUCCESS) return NOTIFY_STATUS_FAILED;
}
token_table_add(name, namelen, NID_UNSET, token, cid, SLOT_NONE, NOTIFY_TYPE_SIGNAL | NOTIFY_FLAG_REGEN, sig, FD_NONE, MACH_PORT_NULL, 0);
*out_token = token;
return NOTIFY_STATUS_OK;
}
uint32_t
notify_register_mach_port(const char *name, mach_port_name_t *notify_port, int flags, int *out_token)
{
notify_state_t *ns_self;
kern_return_t kstatus;
uint32_t status;
uint64_t nid;
task_t task;
int token, mine;
size_t namelen;
uint32_t cid, tflags;
token_table_node_t *t;
mach_port_name_t port;
notify_globals_t globals = _notify_globals();
regenerate_check();
if (name == NULL) return NOTIFY_STATUS_INVALID_NAME;
if (notify_port == NULL) return NOTIFY_STATUS_INVALID_PORT;
mine = 0;
namelen = strlen(name);
task = mach_task_self();
if ((flags & NOTIFY_REUSE) == 0)
{
mine = 1;
kstatus = mach_port_allocate(task, MACH_PORT_RIGHT_RECEIVE, notify_port);
if (kstatus != KERN_SUCCESS) return NOTIFY_STATUS_FAILED;
}
kstatus = mach_port_insert_right(task, *notify_port, *notify_port, MACH_MSG_TYPE_MAKE_SEND);
if (kstatus != KERN_SUCCESS)
{
if (mine == 1) mach_port_mod_refs(task, *notify_port, MACH_PORT_RIGHT_RECEIVE, -1);
return NOTIFY_STATUS_FAILED;
}
if (!strncmp(name, SELF_PREFIX, SELF_PREFIX_LEN))
{
ns_self = _notify_lib_self_state();
if (ns_self == NULL)
{
if (mine == 1)
{
mach_port_mod_refs(task, *notify_port, MACH_PORT_RIGHT_RECEIVE, -1);
}
mach_port_deallocate(task, *notify_port);
return NOTIFY_STATUS_FAILED;
}
token = OSAtomicIncrement32((int32_t *)&globals->token_id);
status = _notify_lib_register_mach_port(ns_self, name, NOTIFY_CLIENT_SELF, token, *notify_port, 0, 0, &nid);
if (status != NOTIFY_STATUS_OK)
{
if (mine == 1)
{
mach_port_mod_refs(task, *notify_port, MACH_PORT_RIGHT_RECEIVE, -1);
}
mach_port_deallocate(task, *notify_port);
return status;
}
cid = token;
token_table_add(name, namelen, nid, token, cid, SLOT_NONE, NOTIFY_FLAG_SELF | NOTIFY_TYPE_PORT, SIGNAL_NONE, FD_NONE, *notify_port, 0);
*out_token = token;
notify_retain_mach_port(*notify_port, mine);
return NOTIFY_STATUS_OK;
}
if (globals->notify_server_port == MACH_PORT_NULL)
{
status = _notify_lib_init(EVENT_INIT);
if (status != 0)
{
if (mine == 1)
{
mach_port_mod_refs(task, *notify_port, MACH_PORT_RIGHT_RECEIVE, -1);
}
mach_port_deallocate(task, *notify_port);
return NOTIFY_STATUS_FAILED;
}
}
if ((globals->client_opts & NOTIFY_OPT_DEMUX) && (*notify_port != globals->notify_common_port))
{
port = globals->notify_common_port;
kstatus = mach_port_insert_right(task, globals->notify_common_port, globals->notify_common_port, MACH_MSG_TYPE_MAKE_SEND);
}
else
{
port = *notify_port;
kstatus = KERN_SUCCESS;
}
if (kstatus == KERN_SUCCESS)
{
token = OSAtomicIncrement32((int32_t *)&globals->token_id);
if (globals->notify_ipc_version == 0)
{
kstatus = _notify_server_register_mach_port(globals->notify_server_port, (caddr_t)name, namelen, port, token, (int32_t *)&cid, (int32_t *)&status);
if ((kstatus == KERN_SUCCESS) && (status != NOTIFY_STATUS_OK)) kstatus = KERN_FAILURE;
}
else
{
cid = token;
kstatus = _notify_server_register_mach_port_2(globals->notify_server_port, (caddr_t)name, namelen, token, port);
}
}
if (kstatus != KERN_SUCCESS)
{
if (mine == 1)
{
mach_port_mod_refs(task, *notify_port, MACH_PORT_RIGHT_RECEIVE, -1);
}
mach_port_deallocate(task, *notify_port);
return NOTIFY_STATUS_FAILED;
}
tflags = NOTIFY_TYPE_PORT;
if (port == globals->notify_common_port) tflags |= NOTIFY_FLAG_REGEN;
token_table_add(name, namelen, NID_UNSET, token, cid, SLOT_NONE, tflags, SIGNAL_NONE, FD_NONE, *notify_port, 0);
if ((globals->client_opts & NOTIFY_OPT_DEMUX) && (*notify_port != globals->notify_common_port))
{
t = token_table_find_retain(token);
if (t == NULL) return NOTIFY_STATUS_FAILED;
t->flags |= NOTIFY_FLAG_RELEASE_SEND;
port = *notify_port;
t->queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
dispatch_retain(t->queue);
t->block = (notify_handler_t)Block_copy(^(int unused){
mach_msg_empty_send_t msg;
kern_return_t kstatus;
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);
});
token_table_release(t);
}
*out_token = token;
notify_retain_mach_port(*notify_port, mine);
return NOTIFY_STATUS_OK;
}
static char *
_notify_mk_tmp_path(int tid)
{
#if TARGET_OS_EMBEDDED
int freetmp = 0;
char *path, *tmp = getenv("TMPDIR");
if (tmp == NULL)
{
asprintf(&tmp, "/tmp/com.apple.notify.%d", geteuid());
mkdir(tmp, 0755);
freetmp = 1;
}
if (tmp == NULL) return NULL;
asprintf(&path, "%s/com.apple.notify.%d.%d", tmp, getpid(), tid);
if (freetmp) free(tmp);
return path;
#else
char tmp[PATH_MAX], *path;
if (confstr(_CS_DARWIN_USER_TEMP_DIR, tmp, sizeof(tmp)) <= 0) return NULL;
#endif
path = NULL;
asprintf(&path, "%s/com.apple.notify.%d.%d", tmp, getpid(), tid);
return path;
}
uint32_t
notify_register_file_descriptor(const char *name, int *notify_fd, int flags, int *out_token)
{
notify_state_t *ns_self;
uint32_t i, status;
uint64_t nid;
int token, mine, fdpair[2];
size_t namelen;
fileport_t fileport;
kern_return_t kstatus;
uint32_t cid;
notify_globals_t globals = _notify_globals();
regenerate_check();
mine = 0;
if (name == NULL) return NOTIFY_STATUS_INVALID_NAME;
if (notify_fd == NULL) return NOTIFY_STATUS_INVALID_FILE;
namelen = strlen(name);
if ((flags & NOTIFY_REUSE) == 0)
{
if (pipe(fdpair) < 0) return NOTIFY_STATUS_FAILED;
mine = 1;
*notify_fd = fdpair[0];
}
else
{
for (i = 0; i < globals->fd_count; i++)
{
if (globals->fd_clnt[i] == *notify_fd) break;
}
if (i >= globals->fd_count) return NOTIFY_STATUS_INVALID_FILE;
fdpair[0] = globals->fd_clnt[i];
fdpair[1] = globals->fd_srv[i];
}
if (!strncmp(name, SELF_PREFIX, SELF_PREFIX_LEN))
{
ns_self = _notify_lib_self_state();
if (ns_self == NULL)
{
if (mine == 1)
{
close(fdpair[0]);
close(fdpair[1]);
}
return NOTIFY_STATUS_FAILED;
}
token = OSAtomicIncrement32((int32_t *)&globals->token_id);
status = _notify_lib_register_file_descriptor(ns_self, name, NOTIFY_CLIENT_SELF, token, fdpair[1], 0, 0, &nid);
if (status != NOTIFY_STATUS_OK)
{
if (mine == 1)
{
close(fdpair[0]);
close(fdpair[1]);
}
return status;
}
cid = token;
token_table_add(name, namelen, nid, token, cid, SLOT_NONE, NOTIFY_FLAG_SELF | NOTIFY_TYPE_FILE, SIGNAL_NONE, *notify_fd, MACH_PORT_NULL, 0);
*out_token = token;
notify_retain_file_descriptor(fdpair[0], fdpair[1]);
return NOTIFY_STATUS_OK;
}
if (globals->client_opts & NOTIFY_OPT_DEMUX)
{
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]);
}
return status;
}
notify_retain_file_descriptor(fdpair[0], fdpair[1]);
return NOTIFY_STATUS_OK;
}
if (globals->notify_server_port == MACH_PORT_NULL)
{
status = _notify_lib_init(EVENT_INIT);
if (status != 0)
{
if (mine == 1)
{
close(fdpair[0]);
close(fdpair[1]);
}
return NOTIFY_STATUS_FAILED;
}
}
fileport = MACH_PORT_NULL;
if (fileport_makeport(fdpair[1], (fileport_t *)&fileport) < 0)
{
if (mine == 1)
{
close(fdpair[0]);
close(fdpair[1]);
}
return NOTIFY_STATUS_FAILED;
}
token = OSAtomicIncrement32((int32_t *)&globals->token_id);
if (globals->notify_ipc_version == 0)
{
kstatus = _notify_server_register_file_descriptor(globals->notify_server_port, (caddr_t)name, namelen, (mach_port_t)fileport, token, (int32_t *)&cid, (int32_t *)&status);
if ((kstatus == KERN_SUCCESS) && (status != NOTIFY_STATUS_OK)) kstatus = KERN_FAILURE;
}
else
{
kstatus = _notify_server_register_file_descriptor_2(globals->notify_server_port, (caddr_t)name, namelen, token, (mach_port_t)fileport);
}
if (kstatus != KERN_SUCCESS)
{
if (mine == 1)
{
close(fdpair[0]);
close(fdpair[1]);
}
return NOTIFY_STATUS_FAILED;
}
token_table_add(name, namelen, NID_UNSET, token, cid, SLOT_NONE, NOTIFY_TYPE_FILE, SIGNAL_NONE, *notify_fd, MACH_PORT_NULL, 0);
*out_token = token;
notify_retain_file_descriptor(fdpair[0], fdpair[1]);
return NOTIFY_STATUS_OK;
}
uint32_t
notify_check(int token, int *check)
{
kern_return_t kstatus;
uint32_t status, val;
token_table_node_t *t;
uint32_t tid;
notify_globals_t globals = _notify_globals();
regenerate_check();
pthread_mutex_lock(&globals->notify_lock);
t = token_table_find_no_lock(token);
if (t == NULL)
{
pthread_mutex_unlock(&globals->notify_lock);
return NOTIFY_STATUS_INVALID_TOKEN;
}
if (t->flags & NOTIFY_FLAG_SELF)
{
status = _notify_lib_check(globals->self_state, NOTIFY_CLIENT_SELF, token, check);
pthread_mutex_unlock(&globals->notify_lock);
return status;
}
if (t->flags & NOTIFY_TYPE_MEMORY)
{
if (globals->shm_base == NULL)
{
pthread_mutex_unlock(&globals->notify_lock);
return NOTIFY_STATUS_FAILED;
}
*check = 0;
val = globals->shm_base[t->slot];
if (t->val != val)
{
*check = 1;
t->val = val;
}
pthread_mutex_unlock(&globals->notify_lock);
return NOTIFY_STATUS_OK;
}
tid = token;
if (globals->notify_ipc_version == 0) tid = t->client_id;
pthread_mutex_unlock(&globals->notify_lock);
if (globals->notify_server_port == MACH_PORT_NULL)
{
status = _notify_lib_init(EVENT_INIT);
if (status != 0) return NOTIFY_STATUS_FAILED;
}
kstatus = _notify_server_check(globals->notify_server_port, tid, check, (int32_t *)&status);
if (kstatus != KERN_SUCCESS) return NOTIFY_STATUS_FAILED;
return status;
}
uint32_t
notify_peek(int token, uint32_t *val)
{
token_table_node_t *t;
uint32_t status;
notify_globals_t globals = _notify_globals();
regenerate_check();
t = token_table_find_retain(token);
if (t == NULL) return NOTIFY_STATUS_INVALID_TOKEN;
if (t->flags & NOTIFY_FLAG_SELF)
{
status = _notify_lib_peek(globals->self_state, NOTIFY_CLIENT_SELF, token, (int *)val);
token_table_release(t);
return status;
}
if (t->flags & NOTIFY_TYPE_MEMORY)
{
if (globals->shm_base == NULL)
{
token_table_release(t);
return NOTIFY_STATUS_FAILED;
}
*val = globals->shm_base[t->slot];
token_table_release(t);
return NOTIFY_STATUS_OK;
}
token_table_release(t);
return NOTIFY_STATUS_INVALID_REQUEST;
}
int *
notify_check_addr(int token)
{
token_table_node_t *t;
uint32_t slot;
int *val;
notify_globals_t globals = _notify_globals();
regenerate_check();
t = token_table_find_retain(token);
if (t == NULL) return NULL;
if (t->flags & NOTIFY_FLAG_SELF)
{
val = _notify_lib_check_addr(globals->self_state, NOTIFY_CLIENT_SELF, token);
token_table_release(t);
return val;
}
if (t->flags & NOTIFY_TYPE_MEMORY)
{
slot = t->slot;
token_table_release(t);
if (globals->shm_base == NULL) return NULL;
return (int *)&(globals->shm_base[slot]);
}
token_table_release(t);
return NULL;
}
uint32_t
notify_monitor_file(int token, char *path, int flags)
{
kern_return_t kstatus;
uint32_t status, len;
token_table_node_t *t;
char *dup;
notify_globals_t globals = _notify_globals();
regenerate_check();
if (path == NULL) return NOTIFY_STATUS_INVALID_REQUEST;
t = token_table_find_retain(token);
if (t == NULL) return NOTIFY_STATUS_INVALID_TOKEN;
if (t->flags & NOTIFY_FLAG_SELF)
{
token_table_release(t);
return NOTIFY_STATUS_INVALID_REQUEST;
}
if (t->path != NULL)
{
token_table_release(t);
return NOTIFY_STATUS_INVALID_REQUEST;
}
if (globals->notify_server_port == MACH_PORT_NULL)
{
status = _notify_lib_init(EVENT_INIT);
if (status != 0)
{
token_table_release(t);
return NOTIFY_STATUS_FAILED;
}
}
len = strlen(path);
dup = strdup(path);
if (dup == NULL) return NOTIFY_STATUS_FAILED;
if (globals->notify_ipc_version == 0)
{
kstatus = _notify_server_monitor_file(globals->notify_server_port, t->client_id, path, len, flags, (int32_t *)&status);
if ((kstatus == KERN_SUCCESS) && (status != NOTIFY_STATUS_OK)) kstatus = KERN_FAILURE;
}
else
{
kstatus = _notify_server_monitor_file_2(globals->notify_server_port, token, path, len, flags);
}
t->path = dup;
t->path_flags = flags;
token_table_release(t);
if (kstatus != KERN_SUCCESS) return NOTIFY_STATUS_FAILED;
return NOTIFY_STATUS_OK;
}
uint32_t
notify_get_event(int token, int *ev, char *buf, int *len)
{
if (ev != NULL) *ev = 0;
if (len != NULL) *len = 0;
return NOTIFY_STATUS_OK;
}
uint32_t
notify_get_state(int token, uint64_t *state)
{
kern_return_t kstatus;
uint32_t status;
token_table_node_t *t;
uint64_t nid;
notify_globals_t globals = _notify_globals();
regenerate_check();
t = token_table_find_retain(token);
if (t == NULL) return NOTIFY_STATUS_INVALID_TOKEN;
if (t->name_node == NULL)
{
token_table_release(t);
return NOTIFY_STATUS_INVALID_TOKEN;
}
if (t->flags & NOTIFY_FLAG_SELF)
{
status = _notify_lib_get_state(globals->self_state, t->name_node->name_id, state, 0, 0);
token_table_release(t);
return status;
}
if (globals->notify_server_port == MACH_PORT_NULL)
{
status = _notify_lib_init(EVENT_INIT);
if (status != 0)
{
token_table_release(t);
return NOTIFY_STATUS_FAILED;
}
}
if (globals->notify_ipc_version == 0)
{
kstatus = _notify_server_get_state(globals->notify_server_port, t->client_id, state, (int32_t *)&status);
if ((kstatus == KERN_SUCCESS) && (status != NOTIFY_STATUS_OK)) kstatus = KERN_FAILURE;
}
else
{
if (t->name_node->name_id >= NID_CALLED_ONCE)
{
kstatus = _notify_server_get_state_3(globals->notify_server_port, t->token, state, (uint64_t *)&nid, (int32_t *)&status);
if ((kstatus == KERN_SUCCESS) && (status == NOTIFY_STATUS_OK)) name_table_set_nid(t->name, nid);
}
else
{
kstatus = _notify_server_get_state_2(globals->notify_server_port, t->name_node->name_id, state, (int32_t *)&status);
}
}
token_table_release(t);
if (kstatus != KERN_SUCCESS) return NOTIFY_STATUS_FAILED;
return status;
}
uint32_t
notify_set_state(int token, uint64_t state)
{
kern_return_t kstatus;
uint32_t status;
token_table_node_t *t;
uint64_t nid;
notify_globals_t globals = _notify_globals();
regenerate_check();
t = token_table_find_retain(token);
if (t == NULL) return NOTIFY_STATUS_INVALID_TOKEN;
if (t->name_node == NULL)
{
token_table_release(t);
return NOTIFY_STATUS_INVALID_TOKEN;
}
if (t->flags & NOTIFY_FLAG_SELF)
{
status = _notify_lib_set_state(globals->self_state, t->name_node->name_id, state, 0, 0);
token_table_release(t);
return status;
}
if (globals->notify_server_port == MACH_PORT_NULL)
{
status = _notify_lib_init(EVENT_INIT);
if (status != 0)
{
token_table_release(t);
return NOTIFY_STATUS_FAILED;
}
}
status = NOTIFY_STATUS_OK;
if (globals->notify_ipc_version == 0)
{
kstatus = _notify_server_set_state(globals->notify_server_port, t->client_id, state, (int32_t *)&status);
if ((kstatus == KERN_SUCCESS) && (status != NOTIFY_STATUS_OK)) kstatus = KERN_FAILURE;
}
else
{
if (t->name_node->name_id >= NID_CALLED_ONCE)
{
kstatus = _notify_server_set_state_3(globals->notify_server_port, t->token, state, (uint64_t *)&nid, (int32_t *)&status);
if ((kstatus == KERN_SUCCESS) && (status == NOTIFY_STATUS_OK)) name_table_set_nid(t->name, nid);
}
else
{
status = NOTIFY_STATUS_OK;
kstatus = _notify_server_set_state_2(globals->notify_server_port, t->name_node->name_id, state);
}
}
if ((kstatus == KERN_SUCCESS) && (status == NOTIFY_STATUS_OK))
{
t->set_state_time = mach_absolute_time();
t->set_state_val = state;
}
token_table_release(t);
if (kstatus != KERN_SUCCESS) return NOTIFY_STATUS_FAILED;
return NOTIFY_STATUS_OK;
}
uint32_t
notify_cancel(int token)
{
token_table_node_t *t;
uint32_t status;
kern_return_t kstatus;
notify_globals_t globals = _notify_globals();
regenerate_check();
pthread_mutex_lock(&globals->notify_lock);
t = token_table_find_no_lock(token);
if (t == NULL)
{
pthread_mutex_unlock(&globals->notify_lock);
return NOTIFY_STATUS_INVALID_TOKEN;
}
if (t->flags & NOTIFY_FLAG_SELF)
{
_notify_lib_cancel(globals->self_state, NOTIFY_CLIENT_SELF, t->token);
token_table_release_no_lock(t);
pthread_mutex_unlock(&globals->notify_lock);
return NOTIFY_STATUS_OK;
}
if (globals->notify_ipc_version == 0)
{
kstatus = _notify_server_cancel(globals->notify_server_port, t->client_id, (int32_t *)&status);
if ((kstatus == KERN_SUCCESS) && (status != NOTIFY_STATUS_OK)) kstatus = KERN_FAILURE;
}
else
{
kstatus = _notify_server_cancel_2(globals->notify_server_port, token);
}
token_table_release_no_lock(t);
pthread_mutex_unlock(&globals->notify_lock);
if ((kstatus == MIG_SERVER_DIED) || (kstatus == MACH_SEND_INVALID_DEST)) return NOTIFY_STATUS_OK;
else if (kstatus != KERN_SUCCESS) return NOTIFY_STATUS_FAILED;
return NOTIFY_STATUS_OK;
}
uint32_t
notify_suspend(int token)
{
token_table_node_t *t;
uint32_t status, tid;
kern_return_t kstatus;
notify_globals_t globals = _notify_globals();
regenerate_check();
t = token_table_find_retain(token);
if (t == NULL) return NOTIFY_STATUS_INVALID_TOKEN;
if (t->flags & NOTIFY_FLAG_SELF)
{
_notify_lib_suspend(globals->self_state, NOTIFY_CLIENT_SELF, t->token);
token_table_release(t);
return NOTIFY_STATUS_OK;
}
if (globals->notify_server_port == MACH_PORT_NULL)
{
status = _notify_lib_init(EVENT_INIT);
if (status != 0)
{
token_table_release(t);
return NOTIFY_STATUS_FAILED;
}
}
tid = token;
if (globals->notify_ipc_version == 0) tid = t->client_id;
kstatus = _notify_server_suspend(globals->notify_server_port, tid, (int32_t *)&status);
token_table_release(t);
if (kstatus != KERN_SUCCESS) status = NOTIFY_STATUS_FAILED;
return status;
}
uint32_t
notify_resume(int token)
{
token_table_node_t *t;
uint32_t status, tid;
kern_return_t kstatus;
notify_globals_t globals = _notify_globals();
regenerate_check();
t = token_table_find_retain(token);
if (t == NULL) return NOTIFY_STATUS_INVALID_TOKEN;
if (t->flags & NOTIFY_FLAG_SELF)
{
_notify_lib_resume(globals->self_state, NOTIFY_CLIENT_SELF, t->token);
token_table_release(t);
return NOTIFY_STATUS_OK;
}
if (globals->notify_server_port == MACH_PORT_NULL)
{
status = _notify_lib_init(EVENT_INIT);
if (status != 0)
{
token_table_release(t);
return NOTIFY_STATUS_FAILED;
}
}
tid = token;
if (globals->notify_ipc_version == 0) tid = t->client_id;
kstatus = _notify_server_resume(globals->notify_server_port, tid, (int32_t *)&status);
token_table_release(t);
if (kstatus != KERN_SUCCESS) status = NOTIFY_STATUS_FAILED;
return status;
}
uint32_t
notify_suspend_pid(pid_t pid)
{
uint32_t status;
kern_return_t kstatus;
notify_globals_t globals = _notify_globals();
if (globals->notify_server_port == MACH_PORT_NULL)
{
status = _notify_lib_init(EVENT_INIT);
if (status != 0)
{
return NOTIFY_STATUS_FAILED;
}
}
kstatus = _notify_server_suspend_pid(globals->notify_server_port, pid, (int32_t *)&status);
if (kstatus != KERN_SUCCESS) status = NOTIFY_STATUS_FAILED;
return status;
}
uint32_t
notify_resume_pid(pid_t pid)
{
uint32_t status;
kern_return_t kstatus;
notify_globals_t globals = _notify_globals();
if (globals->notify_server_port == MACH_PORT_NULL)
{
status = _notify_lib_init(EVENT_INIT);
if (status != 0)
{
return NOTIFY_STATUS_FAILED;
}
}
kstatus = _notify_server_resume_pid(globals->notify_server_port, pid, (int32_t *)&status);
if (kstatus != KERN_SUCCESS) status = NOTIFY_STATUS_FAILED;
return status;
}
uint32_t
notify_simple_post(const char *name)
{
return notify_post(name);
}