#include <sys/types.h>
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/un.h>
#include <sys/ipc.h>
#include <signal.h>
#include <mach/mach.h>
#include <mach/mach_time.h>
#include <errno.h>
#include <pthread.h>
#include "libnotify.h"
#include "notify.h"
#define USER_PROTECTED_UID_PREFIX "user.uid."
#define USER_PROTECTED_UID_PREFIX_LEN 9
int __notify_78945668_info__ = 0;
static uint64_t name_id = 0;
uint64_t
make_client_id(pid_t pid, int token)
{
uint64_t cid;
cid = pid;
cid <<= 32;
cid |= token;
return cid;
}
notify_state_t *
_notify_lib_notify_state_new(uint32_t flags, uint32_t table_size)
{
notify_state_t *ns;
ns = (notify_state_t *)calloc(1, sizeof(notify_state_t));
if (ns == NULL) return NULL;
ns->flags = flags;
ns->sock = -1;
if (ns->flags & NOTIFY_STATE_USE_LOCKS)
{
ns->lock = (pthread_mutex_t *)malloc(sizeof(pthread_mutex_t));
if (ns->lock == NULL)
{
free(ns);
return NULL;
}
pthread_mutex_init(ns->lock, NULL);
}
ns->name_table = _nc_table_new(table_size);
ns->name_id_table = _nc_table_new(table_size);
ns->client_table = _nc_table_new(table_size);
ns->port_table = _nc_table_new(table_size);
ns->proc_table = _nc_table_new(table_size);
if ((ns->name_table == NULL) || (ns->name_id_table == NULL) || (ns->client_table == NULL) || (ns->port_table == NULL) || (ns->proc_table == NULL))
{
free(ns->lock);
_nc_table_free(ns->name_table);
_nc_table_free(ns->name_id_table);
_nc_table_free(ns->client_table);
_nc_table_free(ns->port_table);
_nc_table_free(ns->proc_table);
free(ns);
return NULL;
}
return ns;
}
void
_notify_lib_notify_state_free(notify_state_t *ns)
{
if (ns == NULL) return;
_nc_table_free(ns->name_table);
_nc_table_free(ns->name_id_table);
_nc_table_free(ns->client_table);
_nc_table_free(ns->port_table);
_nc_table_free(ns->proc_table);
if (ns->lock != NULL)
{
pthread_mutex_destroy(ns->lock);
free(ns->lock);
}
if (ns->sock != -1)
{
shutdown(ns->sock, 2);
close(ns->sock);
}
if (ns->controlled_name != NULL) free(ns->controlled_name);
}
static client_t *
_internal_client_new(notify_state_t *ns, pid_t pid, int token)
{
client_t *c;
uint64_t cid = make_client_id(pid, token);
if (ns == NULL) return NULL;
c = _nc_table_find_64(ns->client_table, cid);
if (c != NULL) return NULL;
c = calloc(1, sizeof(client_t));
if (c == NULL) return NULL;
c->client_id = cid;
c->pid = pid;
c->send_val = token;
_nc_table_insert_64(ns->client_table, cid, c);
return c;
}
static void
_internal_client_release(notify_state_t *ns, client_t *c)
{
uint64_t cid;
if (ns == NULL) return;
if (c == NULL) return;
cid = c->client_id;
_nc_table_delete_64(ns->client_table, cid);
switch (c->notify_type)
{
case NOTIFY_TYPE_SIGNAL:
{
break;
}
case NOTIFY_TYPE_FILE:
{
if (c->fd > 0) close(c->fd);
c->fd = -1;
break;
}
case NOTIFY_TYPE_PORT:
{
if (c->port != MACH_PORT_NULL)
{
mach_port_deallocate(mach_task_self(), c->port);
}
break;
}
default:
{
break;
}
}
free(c);
}
static name_info_t *
_internal_new_name(notify_state_t *ns, const char *name)
{
name_info_t *n;
size_t namelen;
if (ns == NULL) return NULL;
if (name == NULL) return NULL;
namelen = strlen(name) + 1;
n = (name_info_t *)calloc(1, sizeof(name_info_t) + namelen);
if (n == NULL) return NULL;
n->subscription_table = _nc_table_new(0);
if (n->subscription_table == NULL)
{
free(n);
return NULL;
}
n->name = (char *)n + sizeof(name_info_t);
memcpy(n->name, name, namelen);
n->name_id = name_id++;
n->access = NOTIFY_ACCESS_DEFAULT;
n->slot = (uint32_t)-1;
n->val = 1;
_nc_table_insert_no_copy(ns->name_table, n->name, n);
_nc_table_insert_64(ns->name_id_table, n->name_id, n);
return n;
}
static void
_internal_insert_controlled_name(notify_state_t *ns, name_info_t *n)
{
int i, j;
if (ns == NULL) return;
if (n == NULL) return;
if (ns->controlled_name == NULL) ns->controlled_name_count = 0;
for (i = 0; i < ns->controlled_name_count; i++)
{
if (ns->controlled_name[i] == n) return;
}
ns->controlled_name = (name_info_t **)reallocf(ns->controlled_name, (ns->controlled_name_count + 1) * sizeof(name_info_t *));
for (i = 0; i < ns->controlled_name_count; i++)
{
if (strcmp(n->name, ns->controlled_name[i]->name) > 0) break;
}
for (j = ns->controlled_name_count; j > i; j--)
{
ns->controlled_name[j] = ns->controlled_name[j-1];
}
ns->controlled_name[i] = n;
ns->controlled_name_count++;
}
static void
_internal_remove_controlled_name(notify_state_t *ns, name_info_t *n)
{
uint32_t i, j;
for (i = 0; i < ns->controlled_name_count; i++)
{
if (ns->controlled_name[i] == n)
{
for (j = i + 1; j < ns->controlled_name_count; j++)
{
ns->controlled_name[j-1] = ns->controlled_name[j];
}
ns->controlled_name_count--;
if (ns->controlled_name_count == 0)
{
free(ns->controlled_name);
ns->controlled_name = NULL;
}
else
{
ns->controlled_name = (name_info_t **)reallocf(ns->controlled_name, ns->controlled_name_count * sizeof(name_info_t *));
}
return;
}
}
}
static uint32_t
_internal_check_access(notify_state_t *ns, const char *name, uid_t uid, gid_t gid, int req)
{
uint32_t i, len, plen;
name_info_t *p;
char str[64];
if (ns == NULL) return NOTIFY_STATUS_FAILED;
if (name == NULL) return NOTIFY_STATUS_INVALID_NAME;
if (uid == 0) return NOTIFY_STATUS_OK;
if (!strncmp(name, USER_PROTECTED_UID_PREFIX, USER_PROTECTED_UID_PREFIX_LEN))
{
snprintf(str, sizeof(str) - 1, "%s%d", USER_PROTECTED_UID_PREFIX, uid);
len = strlen(str);
if ((!strncmp(name, str, len)) && ((name[len] == '\0') || (name[len] == '.'))) return NOTIFY_STATUS_OK;
return NOTIFY_STATUS_NOT_AUTHORIZED;
}
len = strlen(name);
if (ns->controlled_name == NULL) ns->controlled_name_count = 0;
for (i = 0; i < ns->controlled_name_count; i++)
{
p = ns->controlled_name[i];
if (p == NULL) break;
if (p->name == NULL) continue;
plen = strlen(p->name);
if (plen > len) continue;
if (strncmp(p->name, name, plen)) continue;
if ((p->uid == uid) && (p->access & (req << NOTIFY_ACCESS_USER_SHIFT))) break;
if ((p->gid == gid) && (p->access & (req << NOTIFY_ACCESS_GROUP_SHIFT))) break;
if (p->access & (req << NOTIFY_ACCESS_OTHER_SHIFT)) break;
return NOTIFY_STATUS_NOT_AUTHORIZED;
}
return NOTIFY_STATUS_OK;
}
uint32_t
_notify_lib_check_controlled_access(notify_state_t *ns, char *name, uid_t uid, gid_t gid, int req)
{
uint32_t status;
if (ns == NULL) return NOTIFY_STATUS_FAILED;
if (ns->lock != NULL) pthread_mutex_lock(ns->lock);
status = _internal_check_access(ns, name, uid, gid, req);
if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
return status;
}
uint32_t
_notify_lib_port_proc_new(notify_state_t *ns, mach_port_t port, pid_t proc, uint32_t state, dispatch_source_t src)
{
portproc_data_t *pdata;
if (ns == NULL) return NOTIFY_STATUS_FAILED;
if ((proc == 0) && (port == MACH_PORT_NULL)) return NOTIFY_STATUS_FAILED;
pdata = (portproc_data_t *)calloc(1, sizeof(portproc_data_t));
if (pdata == NULL) return NOTIFY_STATUS_FAILED;
pdata->refcount = 1;
pdata->flags = state;
pdata->src = src;
if (ns->lock != NULL) pthread_mutex_lock(ns->lock);
if (proc == 0) _nc_table_insert_n(ns->port_table, port, pdata);
else _nc_table_insert_n(ns->proc_table, proc, pdata);
if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
return NOTIFY_STATUS_OK;
}
portproc_data_t *
_notify_lib_port_proc_find(notify_state_t *ns, mach_port_t port, pid_t proc)
{
portproc_data_t *pdata = NULL;
if (ns == NULL) return NULL;
if ((proc == 0) && (port == MACH_PORT_NULL)) return NULL;
if (ns->lock != NULL) pthread_mutex_lock(ns->lock);
if (proc == 0) pdata = _nc_table_find_n(ns->port_table, port);
else pdata = _nc_table_find_n(ns->proc_table, proc);
if (pdata != NULL) pdata->refcount++;
if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
return pdata;
}
void
_notify_lib_port_proc_release(notify_state_t *ns, mach_port_t port, pid_t proc)
{
portproc_data_t *pdata = NULL;
if (ns == NULL) return;
if ((proc == 0) && (port == MACH_PORT_NULL)) return;
if (ns->lock != NULL) pthread_mutex_lock(ns->lock);
if (proc == 0) pdata = _nc_table_find_n(ns->port_table, port);
else pdata = _nc_table_find_n(ns->proc_table, proc);
if (pdata != NULL)
{
if (pdata->refcount > 0) pdata->refcount--;
if (pdata->refcount == 0)
{
if (proc == 0) _nc_table_delete_n(ns->port_table, port);
else _nc_table_delete_n(ns->proc_table, proc);
dispatch_source_cancel(pdata->src);
dispatch_release(pdata->src);
free(pdata);
}
}
if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
}
static uint32_t
_internal_send(notify_state_t *ns, client_t *c)
{
uint32_t send;
portproc_data_t *pdata;
if (ns == NULL) return NOTIFY_STATUS_FAILED;
if (c == NULL) return NOTIFY_STATUS_FAILED;
if (c->state & NOTIFY_CLIENT_STATE_SUSPENDED)
{
c->state |= NOTIFY_CLIENT_STATE_PENDING;
return NOTIFY_STATUS_OK;
}
pdata = _nc_table_find_n(ns->proc_table, c->pid);
if ((pdata != NULL) && (pdata->flags & NOTIFY_PORT_PROC_STATE_SUSPENDED))
{
c->suspend_count++;
c->state |= NOTIFY_CLIENT_STATE_SUSPENDED;
c->state |= NOTIFY_CLIENT_STATE_PENDING;
return NOTIFY_STATUS_OK;
}
send = c->send_val;
switch (c->notify_type)
{
case NOTIFY_TYPE_SIGNAL:
{
int rc = 0;
if (c->pid == NOTIFY_CLIENT_SELF) rc = kill(getpid(), c->sig);
else rc = kill(c->pid, c->sig);
if (rc != 0) return NOTIFY_STATUS_FAILED;
c->state &= ~NOTIFY_CLIENT_STATE_PENDING;
c->state &= ~NOTIFY_CLIENT_STATE_TIMEOUT;
return NOTIFY_STATUS_OK;
}
case NOTIFY_TYPE_FILE:
{
ssize_t len;
if (c->fd >= 0)
{
send = htonl(send);
len = write(c->fd, &send, sizeof(uint32_t));
if (len != sizeof(uint32_t))
{
close(c->fd);
c->fd = -1;
return NOTIFY_STATUS_FAILED;
}
}
c->state &= ~NOTIFY_CLIENT_STATE_PENDING;
c->state &= ~NOTIFY_CLIENT_STATE_TIMEOUT;
return NOTIFY_STATUS_OK;
}
case NOTIFY_TYPE_PORT:
{
kern_return_t kstatus;
mach_msg_empty_send_t msg;
mach_msg_option_t opts = MACH_SEND_MSG | MACH_SEND_TIMEOUT;
pdata = _nc_table_find_n(ns->port_table, c->port);
if ((pdata != NULL) && (pdata->flags & NOTIFY_PORT_PROC_STATE_SUSPENDED))
{
c->suspend_count++;
c->state |= NOTIFY_CLIENT_STATE_SUSPENDED;
c->state |= NOTIFY_CLIENT_STATE_PENDING;
return NOTIFY_STATUS_OK;
}
if (ns->flags & NOTIFY_STATE_ENABLE_RESEND) opts |= MACH_SEND_NOTIFY;
memset(&msg, 0, sizeof(mach_msg_empty_send_t));
msg.header.msgh_size = sizeof(mach_msg_empty_send_t);
msg.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSGH_BITS_ZERO);
msg.header.msgh_local_port = MACH_PORT_NULL;
msg.header.msgh_remote_port = c->port;
msg.header.msgh_id = (mach_msg_id_t)send;
kstatus = mach_msg(&msg.header, opts, msg.header.msgh_size, 0, MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
if (kstatus == MACH_SEND_TIMED_OUT)
{
mach_msg_destroy(&msg.header);
if (ns->flags & NOTIFY_STATE_ENABLE_RESEND)
{
c->suspend_count++;
c->state |= NOTIFY_CLIENT_STATE_SUSPENDED;
c->state |= NOTIFY_CLIENT_STATE_PENDING;
c->state |= NOTIFY_CLIENT_STATE_TIMEOUT;
return NOTIFY_STATUS_OK;
}
return NOTIFY_STATUS_FAILED;
}
else if (kstatus != KERN_SUCCESS) return NOTIFY_STATUS_FAILED;
c->state &= ~NOTIFY_CLIENT_STATE_PENDING;
c->state &= ~NOTIFY_CLIENT_STATE_TIMEOUT;
return NOTIFY_STATUS_OK;
}
default:
{
break;
}
}
c->state &= ~NOTIFY_CLIENT_STATE_PENDING;
c->state &= ~NOTIFY_CLIENT_STATE_TIMEOUT;
return NOTIFY_STATUS_OK;
}
uint32_t
_notify_lib_post_client(notify_state_t *ns, client_t *c)
{
uint32_t status;
if (ns == NULL) return NOTIFY_STATUS_FAILED;
if (c == NULL) return NOTIFY_STATUS_FAILED;
if (ns->lock != NULL) pthread_mutex_lock(ns->lock);
status = _internal_send(ns, c);
if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
return status;
}
static uint32_t
_internal_post_name(notify_state_t *ns, name_info_t *n, uid_t uid, gid_t gid)
{
int auth;
void *st;
client_t *c;
if (n == NULL) return NOTIFY_STATUS_INVALID_NAME;
auth = _internal_check_access(ns, n->name, uid, gid, NOTIFY_ACCESS_WRITE);
if (auth != 0) return NOTIFY_STATUS_NOT_AUTHORIZED;
n->val++;
st = _nc_table_traverse_start(n->subscription_table);
while (st != NULL)
{
c = _nc_table_traverse(n->subscription_table, st);
if (c == NULL) break;
_internal_send(ns, c);
}
_nc_table_traverse_end(n->subscription_table, st);
return NOTIFY_STATUS_OK;
}
uint32_t
_notify_lib_post(notify_state_t *ns, const char *name, uid_t uid, gid_t gid)
{
name_info_t *n;
uint32_t status;
if (ns == NULL) return NOTIFY_STATUS_FAILED;
if (ns->lock != NULL) pthread_mutex_lock(ns->lock);
n = (name_info_t *)_nc_table_find(ns->name_table, name);
if (n == NULL)
{
if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
return NOTIFY_STATUS_INVALID_NAME;
}
status = _internal_post_name(ns, n, uid, gid);
if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
return status;
}
uint32_t
_notify_lib_post_nid(notify_state_t *ns, uint64_t nid, uid_t uid, gid_t gid)
{
name_info_t *n;
uint32_t status;
if (ns == NULL) return NOTIFY_STATUS_FAILED;
if (ns->lock != NULL) pthread_mutex_lock(ns->lock);
n = (name_info_t *)_nc_table_find_64(ns->name_id_table, nid);
if (n == NULL)
{
if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
return NOTIFY_STATUS_INVALID_NAME;
}
status = _internal_post_name(ns, n, uid, gid);
if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
return status;
}
static void
_internal_release_name_info(notify_state_t *ns, name_info_t *n)
{
if (ns == NULL) return;
if (n == NULL) return;
if (n->refcount > 0) n->refcount--;
if (n->refcount == 0)
{
_internal_remove_controlled_name(ns, n);
_nc_table_delete(ns->name_table, n->name);
_nc_table_delete_64(ns->name_id_table, n->name_id);
_nc_table_free(n->subscription_table);
free(n);
}
}
static void
_internal_cancel(notify_state_t *ns, uint64_t cid)
{
client_t *c;
name_info_t *n;
if (ns == NULL) return;
c = NULL;
n = NULL;
c = _nc_table_find_64(ns->client_table, cid);
if (c == NULL) return;
n = c->name_info;
if (n == NULL) return;
_nc_table_delete_64(n->subscription_table, cid);
_internal_client_release(ns, c);
_internal_release_name_info(ns, n);
}
void
_notify_lib_cancel(notify_state_t *ns, pid_t pid, int token)
{
uint64_t cid;
if (ns == NULL) return;
cid = make_client_id(pid, token);
if (ns->lock != NULL) pthread_mutex_lock(ns->lock);
_internal_cancel(ns, cid);
if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
}
void
_notify_lib_suspend(notify_state_t *ns, pid_t pid, int token)
{
client_t *c;
uint64_t cid;
if (ns == NULL) return;
cid = make_client_id(pid, token);
if (ns->lock != NULL) pthread_mutex_lock(ns->lock);
c = _nc_table_find_64(ns->client_table, cid);
if (c != NULL)
{
c->state |= NOTIFY_CLIENT_STATE_SUSPENDED;
if (c->suspend_count < UINT32_MAX) c->suspend_count++;
}
if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
}
uint32_t
_notify_lib_resume(notify_state_t *ns, pid_t pid, int token)
{
client_t *c;
uint64_t cid;
uint32_t status = NOTIFY_STATUS_OK;
if (ns == NULL) return NOTIFY_STATUS_FAILED;
cid = make_client_id(pid, token);
if (ns->lock != NULL) pthread_mutex_lock(ns->lock);
c = _nc_table_find_64(ns->client_table, cid);
if (c != NULL)
{
if (c->suspend_count > 0) c->suspend_count--;
if (c->suspend_count == 0)
{
c->state &= ~NOTIFY_CLIENT_STATE_SUSPENDED;
c->state &= ~NOTIFY_CLIENT_STATE_TIMEOUT;
if (c->state & NOTIFY_CLIENT_STATE_PENDING)
{
status = _internal_send(ns, c);
}
}
}
if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
return status;
}
void
_notify_lib_suspend_proc(notify_state_t *ns, pid_t pid)
{
portproc_data_t *pdata;
if (ns == NULL) return;
if (ns->lock != NULL) pthread_mutex_lock(ns->lock);
pdata = _nc_table_find_n(ns->proc_table, pid);
if (pdata != NULL) pdata->flags |= NOTIFY_PORT_PROC_STATE_SUSPENDED;
if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
}
void
_notify_lib_resume_proc(notify_state_t *ns, pid_t pid)
{
client_t *c;
void *tt;
portproc_data_t *pdata;
if (ns == NULL) return;
if (ns->lock != NULL) pthread_mutex_lock(ns->lock);
pdata = _nc_table_find_n(ns->proc_table, pid);
if (pdata != NULL) pdata->flags &= ~NOTIFY_PORT_PROC_STATE_SUSPENDED;
tt = _nc_table_traverse_start(ns->client_table);
while (tt != NULL)
{
c = _nc_table_traverse(ns->client_table, tt);
if (c == NULL) break;
if (c->pid == pid)
{
if (c->suspend_count > 0) c->suspend_count--;
if (c->suspend_count == 0)
{
c->state &= ~NOTIFY_CLIENT_STATE_SUSPENDED;
c->state &= ~NOTIFY_CLIENT_STATE_TIMEOUT;
if (c->state & NOTIFY_CLIENT_STATE_PENDING)
{
_internal_send(ns, c);
}
}
}
}
_nc_table_traverse_end(ns->client_table, tt);
if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
}
void
_notify_lib_suspend_port(notify_state_t *ns, mach_port_t port)
{
portproc_data_t *pdata;
if (ns == NULL) return;
if (ns->lock != NULL) pthread_mutex_lock(ns->lock);
pdata = _nc_table_find_n(ns->port_table, port);
if (pdata != NULL) pdata->flags |= NOTIFY_PORT_PROC_STATE_SUSPENDED;
if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
}
void
_notify_lib_resume_port(notify_state_t *ns, mach_port_t port)
{
client_t *c;
void *tt;
portproc_data_t *pdata;
if (ns == NULL) return;
if (ns->lock != NULL) pthread_mutex_lock(ns->lock);
pdata = _nc_table_find_n(ns->port_table, port);
if (pdata != NULL) pdata->flags &= ~NOTIFY_PORT_PROC_STATE_SUSPENDED;
tt = _nc_table_traverse_start(ns->client_table);
while (tt != NULL)
{
c = _nc_table_traverse(ns->client_table, tt);
if (c == NULL) break;
if (c->port == port)
{
if (c->suspend_count > 0) c->suspend_count--;
if (c->suspend_count == 0)
{
c->state &= ~NOTIFY_CLIENT_STATE_SUSPENDED;
c->state &= ~NOTIFY_CLIENT_STATE_TIMEOUT;
if (c->state & NOTIFY_CLIENT_STATE_PENDING)
{
_internal_send(ns, c);
}
}
}
}
_nc_table_traverse_end(ns->client_table, tt);
if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
}
void
_notify_lib_cancel_proc(notify_state_t *ns, pid_t pid)
{
client_t *c;
void *tt;
list_t *l, *x;
if (ns == NULL) return;
x = NULL;
if (ns->lock != NULL) pthread_mutex_lock(ns->lock);
tt = _nc_table_traverse_start(ns->client_table);
while (tt != NULL)
{
c = _nc_table_traverse(ns->client_table, tt);
if (c == NULL) break;
if (c->pid == pid) x = _nc_list_prepend(x, _nc_list_new(c));
}
_nc_table_traverse_end(ns->client_table, tt);
for (l = x; l != NULL; l = _nc_list_next(l))
{
c = _nc_list_data(l);
_internal_cancel(ns, c->client_id);
}
_nc_list_release_list(x);
if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
}
void
_notify_lib_cancel_port(notify_state_t *ns, mach_port_t port)
{
client_t *c;
void *tt;
list_t *l, *x;
if (ns == NULL) return;
x = NULL;
if (ns->lock != NULL) pthread_mutex_lock(ns->lock);
tt = _nc_table_traverse_start(ns->client_table);
while (tt != NULL)
{
c = _nc_table_traverse(ns->client_table, tt);
if (c == NULL) break;
if (c->port == port) x = _nc_list_prepend(x, _nc_list_new(c));
}
_nc_table_traverse_end(ns->client_table, tt);
for (l = x; l != NULL; l = _nc_list_next(l))
{
c = _nc_list_data(l);
_internal_cancel(ns, c->client_id);
}
_nc_list_release_list(x);
if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
}
uint32_t
_notify_lib_check(notify_state_t *ns, pid_t pid, int token, int *check)
{
client_t *c;
uint64_t cid;
if (ns == NULL) return NOTIFY_STATUS_FAILED;
if (check == NULL) return NOTIFY_STATUS_FAILED;
cid = make_client_id(pid, token);
if (ns->lock != NULL) pthread_mutex_lock(ns->lock);
c = _nc_table_find_64(ns->client_table, cid);
if (c == NULL)
{
if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
return NOTIFY_STATUS_INVALID_TOKEN;
}
if (c->name_info == NULL)
{
if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
return NOTIFY_STATUS_INVALID_TOKEN;
}
if (c->name_info->val == c->lastval)
{
*check = 0;
if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
return NOTIFY_STATUS_OK;
}
c->lastval = c->name_info->val;
*check = 1;
if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
return NOTIFY_STATUS_OK;
}
uint32_t
_notify_lib_peek(notify_state_t *ns, pid_t pid, int token, int *val)
{
client_t *c;
uint64_t cid;
if (ns == NULL) return NOTIFY_STATUS_FAILED;
if (val == NULL) return NOTIFY_STATUS_FAILED;
cid = make_client_id(pid, token);
if (ns->lock != NULL) pthread_mutex_lock(ns->lock);
c = _nc_table_find_64(ns->client_table, cid);
if (c == NULL)
{
if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
return NOTIFY_STATUS_INVALID_TOKEN;
}
if (c->name_info == NULL)
{
if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
return NOTIFY_STATUS_INVALID_TOKEN;
}
*val = c->name_info->val;
if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
return NOTIFY_STATUS_OK;
}
int *
_notify_lib_check_addr(notify_state_t *ns, pid_t pid, int token)
{
client_t *c;
int *addr;
uint64_t cid;
if (ns == NULL) return NULL;
cid = make_client_id(pid, token);
if (ns->lock != NULL) pthread_mutex_lock(ns->lock);
c = _nc_table_find_64(ns->client_table, cid);
if (c == NULL)
{
if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
return NULL;
}
if (c->name_info == NULL)
{
if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
return NULL;
}
addr = (int *)&(c->name_info->val);
if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
return addr;
}
uint32_t
_notify_lib_get_state(notify_state_t *ns, uint64_t nid, uint64_t *state, uid_t uid, gid_t gid)
{
name_info_t *n;
if (ns == NULL) return NOTIFY_STATUS_FAILED;
if (state == NULL) return NOTIFY_STATUS_FAILED;
*state = 0;
if (ns->lock != NULL) pthread_mutex_lock(ns->lock);
n = (name_info_t *)_nc_table_find_64(ns->name_id_table, nid);
if (n == NULL)
{
if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
return NOTIFY_STATUS_INVALID_NAME;
}
#ifdef GET_STATE_AUTH_CHECK
int auth = _internal_check_access(ns, n->name, uid, gid, NOTIFY_ACCESS_READ);
if (auth != 0)
{
if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
return NOTIFY_STATUS_NOT_AUTHORIZED;
}
#endif
*state = n->state;
if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
return NOTIFY_STATUS_OK;
}
uint32_t
_notify_lib_set_state(notify_state_t *ns, uint64_t nid, uint64_t state, uid_t uid, gid_t gid)
{
name_info_t *n;
int auth;
if (ns == NULL) return NOTIFY_STATUS_FAILED;
if (ns->lock != NULL) pthread_mutex_lock(ns->lock);
n = (name_info_t *)_nc_table_find_64(ns->name_id_table, nid);
if (n == NULL)
{
if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
return NOTIFY_STATUS_INVALID_NAME;
}
auth = _internal_check_access(ns, n->name, uid, gid, NOTIFY_ACCESS_WRITE);
if (auth != 0)
{
if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
return NOTIFY_STATUS_NOT_AUTHORIZED;
}
n->state = state;
n->state_time = mach_absolute_time();
if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
return NOTIFY_STATUS_OK;
}
uint32_t
_notify_lib_get_val(notify_state_t *ns, pid_t pid, int token, int *val)
{
client_t *c;
uint64_t cid;
if (val == NULL) return NOTIFY_STATUS_FAILED;
*val = 0;
if (ns == NULL) return NOTIFY_STATUS_FAILED;
if (val == NULL) return NOTIFY_STATUS_FAILED;
cid = make_client_id(pid, token);
if (ns->lock != NULL) pthread_mutex_lock(ns->lock);
c = _nc_table_find_64(ns->client_table, cid);
if (c == NULL)
{
if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
return NOTIFY_STATUS_INVALID_TOKEN;
}
if (c->name_info == NULL)
{
if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
return NOTIFY_STATUS_INVALID_TOKEN;
}
*val = c->name_info->val;
if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
return NOTIFY_STATUS_OK;
}
uint32_t
_notify_lib_set_val(notify_state_t *ns, pid_t pid, int token, int val, uid_t uid, gid_t gid)
{
client_t *c;
int auth;
uint64_t cid;
if (ns == NULL) return NOTIFY_STATUS_FAILED;
cid = make_client_id(pid, token);
if (ns->lock != NULL) pthread_mutex_lock(ns->lock);
c = _nc_table_find_64(ns->client_table, cid);
if (c == NULL)
{
if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
return NOTIFY_STATUS_INVALID_TOKEN;
}
if (c->name_info == NULL)
{
if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
return NOTIFY_STATUS_INVALID_TOKEN;
}
auth = _internal_check_access(ns, c->name_info->name, uid, gid, NOTIFY_ACCESS_WRITE);
if (auth != 0)
{
if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
return NOTIFY_STATUS_NOT_AUTHORIZED;
}
c->name_info->val = val;
if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
return NOTIFY_STATUS_OK;
}
static uint32_t
_internal_register_common(notify_state_t *ns, const char *name, pid_t pid, int token, uid_t uid, gid_t gid, client_t **outc)
{
client_t *c;
name_info_t *n;
int is_new_name;
uint32_t status;
if (ns == NULL) return NOTIFY_STATUS_FAILED;
if (name == NULL) return NOTIFY_STATUS_INVALID_NAME;
if (outc == NULL) return NOTIFY_STATUS_OK;
status = _internal_check_access(ns, name, uid, gid, NOTIFY_ACCESS_READ);
if (status != NOTIFY_STATUS_OK) return NOTIFY_STATUS_NOT_AUTHORIZED;
*outc = NULL;
is_new_name = 0;
n = (name_info_t *)_nc_table_find(ns->name_table, name);
if (n == NULL)
{
is_new_name = 1;
n = _internal_new_name(ns, name);
if (n == NULL) return NOTIFY_STATUS_FAILED;
}
c = _internal_client_new(ns, pid, token);
if (c == NULL)
{
if (is_new_name == 1)
{
_nc_table_delete(ns->name_table, n->name);
_nc_table_free(n->subscription_table);
free(n);
}
return NOTIFY_STATUS_FAILED;
}
n->refcount++;
c->name_info = n;
_nc_table_insert_64(n->subscription_table, c->client_id, c);
*outc = c;
return NOTIFY_STATUS_OK;
}
uint32_t
_notify_lib_register_signal(notify_state_t *ns, const char *name, pid_t pid, int token, uint32_t sig, uid_t uid, gid_t gid, uint64_t *out_nid)
{
client_t *c;
uint32_t status;
if (ns == NULL) return NOTIFY_STATUS_FAILED;
if (name == NULL) return NOTIFY_STATUS_INVALID_NAME;
c = NULL;
if (ns->lock != NULL) pthread_mutex_lock(ns->lock);
status = _internal_register_common(ns, name, pid, token, uid, gid, &c);
if (status != NOTIFY_STATUS_OK)
{
if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
return status;
}
c->notify_type = NOTIFY_TYPE_SIGNAL;
c->pid = pid;
c->sig = sig;
*out_nid = c->name_info->name_id;
if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
return NOTIFY_STATUS_OK;
}
uint32_t
_notify_lib_register_file_descriptor(notify_state_t *ns, const char *name, pid_t pid, int token, int fd, uid_t uid, gid_t gid, uint64_t *out_nid)
{
client_t *c;
uint32_t status;
if (ns == NULL) return NOTIFY_STATUS_FAILED;
if (name == NULL) return NOTIFY_STATUS_INVALID_NAME;
c = NULL;
if (ns->lock != NULL) pthread_mutex_lock(ns->lock);
status = _internal_register_common(ns, name, pid, token, uid, gid, &c);
if (status != NOTIFY_STATUS_OK)
{
if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
return status;
}
c->notify_type = NOTIFY_TYPE_FILE;
c->fd = fd;
*out_nid = c->name_info->name_id;
if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
return NOTIFY_STATUS_OK;
}
uint32_t
_notify_lib_register_mach_port(notify_state_t *ns, const char *name, pid_t pid, int token, mach_port_t port, uid_t uid, gid_t gid, uint64_t *out_nid)
{
client_t *c;
uint32_t status;
if (ns == NULL) return NOTIFY_STATUS_FAILED;
if (name == NULL) return NOTIFY_STATUS_INVALID_NAME;
c = NULL;
if (ns->lock != NULL) pthread_mutex_lock(ns->lock);
status = _internal_register_common(ns, name, pid, token, uid, gid, &c);
if (status != NOTIFY_STATUS_OK)
{
if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
return status;
}
c->notify_type = NOTIFY_TYPE_PORT;
c->port = port;
*out_nid = c->name_info->name_id;
if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
return NOTIFY_STATUS_OK;
}
uint32_t
_notify_lib_register_plain(notify_state_t *ns, const char *name, pid_t pid, int token, uint32_t slot, uint32_t uid, uint32_t gid, uint64_t *out_nid)
{
client_t *c;
uint32_t status;
if (ns == NULL) return NOTIFY_STATUS_FAILED;
if (name == NULL) return NOTIFY_STATUS_INVALID_NAME;
c = NULL;
if (ns->lock != NULL) pthread_mutex_lock(ns->lock);
status = _internal_register_common(ns, name, pid, token, uid, gid, &c);
if (status != NOTIFY_STATUS_OK)
{
if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
return status;
}
if (slot == SLOT_NONE)
{
c->notify_type = NOTIFY_TYPE_PLAIN;
}
else
{
c->notify_type = NOTIFY_TYPE_MEMORY;
c->name_info->slot = slot;
}
*out_nid = c->name_info->name_id;
if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
return NOTIFY_STATUS_OK;
}
uint32_t
_notify_lib_set_owner(notify_state_t *ns, const char *name, uid_t uid, gid_t gid)
{
name_info_t *n;
if (ns == NULL) return NOTIFY_STATUS_FAILED;
if (name == NULL) return NOTIFY_STATUS_INVALID_NAME;
if (ns->lock != NULL) pthread_mutex_lock(ns->lock);
n = (name_info_t *)_nc_table_find(ns->name_table, name);
if (n == NULL)
{
n = _internal_new_name(ns, name);
if (n == NULL)
{
if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
return NOTIFY_STATUS_FAILED;
}
n->refcount++;
}
n->uid = uid;
n->gid = gid;
_internal_insert_controlled_name(ns, n);
if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
return NOTIFY_STATUS_OK;
}
uint32_t
_notify_lib_get_owner(notify_state_t *ns, const char *name, uint32_t *uid, uint32_t *gid)
{
name_info_t *n;
int i, nlen, len;
if (ns == NULL) return NOTIFY_STATUS_FAILED;
if (name == NULL) return NOTIFY_STATUS_INVALID_NAME;
if (ns->lock != NULL) pthread_mutex_lock(ns->lock);
n = (name_info_t *)_nc_table_find(ns->name_table, name);
if (n != NULL)
{
*uid = n->uid;
*gid = n->gid;
if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
return NOTIFY_STATUS_OK;
}
len = strlen(name);
for (i = 0; i < ns->controlled_name_count; i++)
{
n = ns->controlled_name[i];
if (n == NULL) break;
nlen = strlen(n->name);
if (!strcmp(n->name, name))
{
*uid = n->uid;
*gid = n->gid;
if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
return NOTIFY_STATUS_OK;
}
if (nlen >= len) continue;
if (strncmp(n->name, name, nlen)) continue;
*uid = n->uid;
*gid = n->gid;
if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
return NOTIFY_STATUS_OK;
}
*uid = 0;
*gid = 0;
if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
return NOTIFY_STATUS_OK;
}
uint32_t
_notify_lib_set_access(notify_state_t *ns, const char *name, uint32_t mode)
{
name_info_t *n;
if (ns == NULL) return NOTIFY_STATUS_FAILED;
if (name == NULL) return NOTIFY_STATUS_INVALID_NAME;
if (ns->lock != NULL) pthread_mutex_lock(ns->lock);
n = (name_info_t *)_nc_table_find(ns->name_table, name);
if (n == NULL)
{
n = _internal_new_name(ns, name);
if (n == NULL)
{
if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
return NOTIFY_STATUS_FAILED;
}
n->refcount++;
}
n->access = mode;
_internal_insert_controlled_name(ns, n);
if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
return NOTIFY_STATUS_OK;
}
uint32_t
_notify_lib_get_access(notify_state_t *ns, const char *name, uint32_t *mode)
{
name_info_t *n;
int i, nlen, len;
if (ns == NULL) return NOTIFY_STATUS_FAILED;
if (name == NULL) return NOTIFY_STATUS_INVALID_NAME;
if (ns->lock != NULL) pthread_mutex_lock(ns->lock);
n = (name_info_t *)_nc_table_find(ns->name_table, name);
if (n != NULL)
{
*mode = n->access;
if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
return NOTIFY_STATUS_OK;
}
len = strlen(name);
for (i = 0; i < ns->controlled_name_count; i++)
{
n = ns->controlled_name[i];
if (n == NULL) break;
if (n->name == NULL) continue;
nlen = strlen(n->name);
if (!strcmp(n->name, name))
{
*mode = n->access;
if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
return NOTIFY_STATUS_OK;
}
if (nlen >= len) continue;
if (strncmp(n->name, name, nlen)) continue;
*mode = n->access;
if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
return NOTIFY_STATUS_OK;
}
*mode = NOTIFY_ACCESS_DEFAULT;
if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
return NOTIFY_STATUS_OK;
}
uint32_t
_notify_lib_release_name(notify_state_t *ns, const char *name, uid_t uid, gid_t gid)
{
name_info_t *n;
if (ns == NULL) return NOTIFY_STATUS_FAILED;
if (name == NULL) return NOTIFY_STATUS_INVALID_NAME;
if (ns->lock != NULL) pthread_mutex_lock(ns->lock);
n = (name_info_t *)_nc_table_find(ns->name_table, name);
if (n == NULL)
{
if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
return NOTIFY_STATUS_INVALID_NAME;
}
if ((n->uid != uid) && (uid != 0))
{
if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
return NOTIFY_STATUS_NOT_AUTHORIZED;
}
_internal_release_name_info(ns, n);
if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
return NOTIFY_STATUS_OK;
}