Something went wrong. Try again.
Cider Isn't Darwin Emulation, Really
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545/* * 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 <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"#include "notify_internal.h"
#define USER_PROTECTED_UID_PREFIX "user.uid."#define USER_PROTECTED_UID_PREFIX_LEN 9
uint64_tmake_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;
/* detect duplicates - should never happen, but it would be bad */ 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;
ns->stat_client_alloc++;
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) { /* release my send right to the port */ mach_port_deallocate(mach_task_self(), c->port); } break; } default: { break; } }
free(c); ns->stat_client_free++;}
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;
ns->stat_name_alloc++;
n->name = (char *)n + sizeof(name_info_t); memcpy(n->name, name, namelen);
notify_globals_t globals = _notify_globals(); n->name_id = globals->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 *));
/* * Insert name in reverse sorted order (longer names preceed shorter names). * this means that in _internal_check_access, we check subspaces from the bottom up * i.e. we check access for the "deepest" controlled subspace. */
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;
/* root may do anything */ if (uid == 0) return NOTIFY_STATUS_OK;
/* if name has "user.uid." as a prefix, it is a user-protected namespace */ 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);
/* user <uid> may access user.uid.<uid> or a subtree name */ 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;
/* Found a match or a prefix, check if restrictions apply to this uid/gid */ 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;
ns->stat_portproc_alloc++;
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); ns->stat_portproc_free++; } }
if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);}
/* * Send notification to a subscriber */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) { /* deallocate port rights obtained via pseudo-receive after failed mach_msg() send */ mach_msg_destroy(&msg.header); if (ns->flags & NOTIFY_STATE_ENABLE_RESEND) { /* * Suspend on timeout. * notifyd will get a MACH_NOTIFY_SEND_POSSIBLE and trigger a retry. * c->suspend_count must be zero, or we would not be trying to send. */ 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; list_t *l; 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++;
for (l = n->subscriptions; l != NULL; l = _nc_list_next(l)) { c = _nc_list_data(l); if (c != NULL) _internal_send(ns, c); }
return NOTIFY_STATUS_OK;}
/* * Notify subscribers of this name. */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_list_release_list(n->subscriptions); free(n); ns->stat_name_free++; }}
/* * Cancel (delete) a client */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;
n->subscriptions =_nc_list_find_release(n->subscriptions, c); _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);
/* Resume all subscriptions for this process */ 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);
/* Resume all subscriptions with this port */ 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);}
/* * Delete all clients for a process * N.B. notifyd does not use this routine. */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);}
/* * Delete all clients for a port * N.B. notifyd does not use this routine. */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);}
/* * Check if a name has changed since the last time this client checked. * Returns true, false, or error. */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;}
/* * SPI: get value for a name. */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;}
/* * Get state value for a name. */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;}
/* * Set state value for a name. */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;}
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_list_release_list(n->subscriptions); free(n); ns->stat_name_free++; }
return NOTIFY_STATUS_FAILED; }
n->refcount++;
c->name_info = n; n->subscriptions = _nc_list_prepend(n->subscriptions, _nc_list_new(c));
*outc = c;
return NOTIFY_STATUS_OK;}
/* * Register for signal. * Returns the client_id; */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;}
/* * Register for notification on a file descriptor. * Returns the client_id; */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;}
/* * Register for notification on a mach port. * Returns the client_id; */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;}
/* * Plain registration - only for notify_check() * Returns the client_id. */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) { /* create new name */ n = _internal_new_name(ns, name); if (n == NULL) { if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); return NOTIFY_STATUS_FAILED; }
/* * Setting the refcount here allows the namespace to be "pre-populated" * with controlled names. notifyd does this for reserved names in * its configuration file. */ 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; }
/* check if this key is a prefix */ 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) { /* create new name */ n = _internal_new_name(ns, name); if (n == NULL) { if (ns->lock != NULL) pthread_mutex_unlock(ns->lock); return NOTIFY_STATUS_FAILED; }
/* * Setting the refcount here allows the namespace to be "pre-populated" * with controlled names. notifyd does this for reserved names in * its configuration file. */ 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; }
/* check if this key is a prefix */ 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; }
/* Owner and root may release */ 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;}