1/* 2 * Copyright (c) 2003-2012 Apple Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24#include <assert.h> 25#include <sys/types.h> 26#include <sys/stat.h> 27#include <stdio.h> 28#include <stdlib.h> 29#include <unistd.h> 30#include <signal.h> 31#include <sys/socket.h> 32#include <netinet/in.h> 33#include <sys/un.h> 34#include <sys/ipc.h> 35#include <sys/signal.h> 36#include <sys/syslimits.h> 37#include <mach/mach.h> 38#include <mach/mach_time.h> 39#include <sys/mman.h> 40#include <sys/fcntl.h> 41#include <sys/time.h> 42#include <bootstrap_priv.h> 43#include <errno.h> 44#include <pthread.h> 45#include <TargetConditionals.h> 46#include <AvailabilityMacros.h> 47#include <libkern/OSAtomic.h> 48#include <Block.h> 49#include <dispatch/dispatch.h> 50#include <dispatch/private.h> 51#include <_simple.h> 52 53#include "libnotify.h" 54 55#include "notify.h" 56#include "notify_internal.h" 57#include "notify_ipc.h" 58#include "notify_private.h" 59 60#define INITIAL_TOKEN_ID 0 61 62// <rdar://problem/10385540> 63WEAK_IMPORT_ATTRIBUTE bool _dispatch_is_multithreaded(void); 64 65#define EVENT_INIT 0 66#define EVENT_REGEN 1 67 68#define SELF_PREFIX "self." 69#define SELF_PREFIX_LEN 5 70 71#define COMMON_SELF_PORT_KEY "self.com.apple.system.notify.common" 72 73#define MULTIPLE_REGISTRATION_WARNING_TRIGGER 20 74 75extern uint32_t _notify_lib_peek(notify_state_t *ns, pid_t pid, int token, int *val); 76extern int *_notify_lib_check_addr(notify_state_t *ns, pid_t pid, int token); 77 78#define CLIENT_TOKEN_TABLE_SIZE 256 79 80#define NID_UNSET 0xffffffffffffffffL 81#define NID_CALLED_ONCE 0xfffffffffffffffeL 82 83#define NO_LOCK 1 84 85typedef struct 86{ 87 uint32_t refcount; 88 uint64_t name_id; 89} name_table_node_t; 90 91typedef struct 92{ 93 uint32_t refcount; 94 const char *name; 95 size_t namelen; 96 name_table_node_t *name_node; 97 uint32_t token; 98 uint32_t slot; 99 uint32_t val; 100 uint32_t flags; 101 int fd; 102 int signal; 103 mach_port_t mp; 104 uint32_t client_id; 105 uint64_t set_state_time; 106 uint64_t set_state_val; 107 char * path; 108 int path_flags; 109 dispatch_queue_t queue; 110 notify_handler_t block; 111} token_table_node_t; 112 113/* FORWARD */ 114static void _notify_lib_regenerate(int src); 115static void notify_retain_mach_port(mach_port_t mp, int mine); 116static void _notify_dispatch_handle(mach_port_t port); 117static notify_state_t *_notify_lib_self_state(); 118 119#if TARGET_IPHONE_SIMULATOR 120const char * 121_notify_shm_id() 122{ 123 static dispatch_once_t once; 124 static char *shm_id; 125 126 dispatch_once(&once, ^{ 127 // According to documentation, our shm_id must be no more than 31 characters long 128 // but in practice, even 31 characters is too long (<rdar://problem/16860882>), 129 // so we jump through some hoops to make a smaller string based on our UDID. 130 const char *udid = getenv("SIMULATOR_UDID"); 131 if (udid && strlen(udid) == 36) { 132 char scratch[34]; // 32 characters, 2 NUL 133 134 // 01234567890123456789012345678901234567890 135 // UUUUUUUU-UUUU-UUUU-LLLL-LLLLLLLLLLLL 136 memcpy(scratch, udid, 8); 137 memcpy(scratch+8, udid+9, 4); 138 memcpy(scratch+12, udid+14, 4); 139 scratch[16] = '\0'; 140 141 memcpy(scratch+17, udid+19, 4); 142 memcpy(scratch+21, udid+24, 12); 143 scratch[33] = '\0'; 144 145 // If the input is invalid, these will end up being undefined 146 // values, but they'll still be values we can use. 147 uint64_t upper = strtoull(scratch, NULL, 16); 148 uint64_t lower = strtoull(scratch + 17, NULL, 16); 149 150 const char *c64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; 151 scratch[0] = c64[(upper >> 57) & 0xf]; 152 scratch[1] = c64[(upper >> 50) & 0xf]; 153 scratch[2] = c64[(upper >> 43) & 0xf]; 154 scratch[3] = c64[(upper >> 36) & 0xf]; 155 scratch[4] = c64[(upper >> 29) & 0xf]; 156 scratch[5] = c64[(upper >> 22) & 0xf]; 157 scratch[6] = c64[(upper >> 15) & 0xf]; 158 scratch[7] = c64[(upper >> 8) & 0xf]; 159 scratch[8] = c64[(upper >> 1) & 0xf]; 160 // Drop a bit on the floor, but that probably doesn't matter. It does not need to be reversible 161 162 scratch[10] = c64[(lower >> 57) & 0xf]; 163 scratch[11] = c64[(lower >> 50) & 0xf]; 164 scratch[12] = c64[(lower >> 43) & 0xf]; 165 scratch[13] = c64[(lower >> 36) & 0xf]; 166 scratch[14] = c64[(lower >> 29) & 0xf]; 167 scratch[15] = c64[(lower >> 22) & 0xf]; 168 scratch[16] = c64[(lower >> 15) & 0xf]; 169 scratch[17] = c64[(lower >> 8) & 0xf]; 170 scratch[18] = c64[(lower >> 1) & 0xf]; 171 // Drop a bit on the floor, but that probably doesn't matter. It does not need to be reversible 172 173 scratch[19] = '\0'; 174 175 asprintf(&shm_id, "sim.not.%s", scratch); 176 assert(shm_id); 177 } 178 179 if (!shm_id) { 180 shm_id = "apple.shm.notification_center"; 181 } 182 }); 183 184 return shm_id; 185} 186#endif 187 188static int 189shm_attach(uint32_t size) 190{ 191 int32_t shmfd; 192 notify_globals_t globals = _notify_globals(); 193 194 shmfd = shm_open(SHM_ID, O_RDONLY, 0); 195 if (shmfd == -1) return -1; 196 197 globals->shm_base = mmap(NULL, size, PROT_READ, MAP_SHARED, shmfd, 0); 198 close(shmfd); 199 200 if (globals->shm_base == (uint32_t *)-1) globals->shm_base = NULL; 201 if (globals->shm_base == NULL) return -1; 202 203 return 0; 204} 205 206#ifdef NOTDEF 207static void 208shm_detach(void) 209{ 210 if (shm_base != NULL) 211 { 212 shmdt(shm_base); 213 shm_base = NULL; 214 } 215} 216#endif 217 218/* 219 * Initialization of global variables. Called once per process. 220 */ 221void 222_notify_init_globals(void * /* notify_globals_t */ _globals) 223{ 224 notify_globals_t globals = _globals; 225 226 pthread_mutex_init(&globals->notify_lock, NULL); 227 globals->token_id = INITIAL_TOKEN_ID; 228 globals->notify_common_token = -1; 229} 230 231#if !_NOTIFY_HAS_ALLOC_ONCE 232notify_globals_t 233_notify_globals_impl(void) 234{ 235 static dispatch_once_t once; 236 static notify_globals_t globals; 237 dispatch_once(&once, ^{ 238 globals = calloc(1, sizeof(struct notify_globals_s)); 239 _notify_init_globals(globals); 240 }); 241 return globals; 242} 243#endif 244 245/* 246 * _notify_lib_init is called for each new registration (event = EVENT_INIT). 247 * It is also called to re-initialize when the library has detected that 248 * notifyd has restarted (event = EVENT_REGEN). 249 */ 250static uint32_t 251_notify_lib_init(uint32_t event) 252{ 253 __block kern_return_t kstatus; 254 __block bool first = false; 255 int status, cid; 256 uint64_t state; 257 258 notify_globals_t globals = _notify_globals(); 259 260 /* notifyd sets NOTIFY_OPT_DISABLE to avoid re-entrancy issues */ 261 if (globals->client_opts & NOTIFY_OPT_DISABLE) return NOTIFY_STATUS_FAILED; 262 263 /* Look up the notifyd server port just once. */ 264 kstatus = KERN_SUCCESS; 265 dispatch_once(&globals->notify_server_port_once, ^{ 266 first = true; 267 kstatus = bootstrap_look_up2(bootstrap_port, NOTIFY_SERVICE_NAME, &globals->notify_server_port, 0, BOOTSTRAP_PRIVILEGED_SERVER); 268 }); 269 270 if (kstatus != KERN_SUCCESS) return NOTIFY_STATUS_FAILED; 271 272 pthread_mutex_lock(&globals->notify_lock); 273 274 /* 275 * _dispatch_is_multithreaded() tells us if it is safe to use dispatch queues for 276 * a shared port for all registratios, and to watch for notifyd exiting / restarting. 277 * 278 * Note that _dispatch_is_multithreaded is weak imported, <rdar://problem/10385540> 279 */ 280 if (_dispatch_is_multithreaded) 281 { 282 if (_dispatch_is_multithreaded()) globals->client_opts |= (NOTIFY_OPT_DEMUX | NOTIFY_OPT_REGEN); 283 } 284 285 /* 286 * Look up the server's PID and supported IPC version on the first call, 287 * and on a regeneration event (when the server has restarted). 288 */ 289 if (first || (event == EVENT_REGEN)) 290 { 291 pid_t last_pid = globals->notify_server_pid; 292 293 globals->notify_ipc_version = 0; 294 globals->notify_server_pid = 0; 295 296 kstatus = _notify_server_register_plain(globals->notify_server_port, NOTIFY_IPC_VERSION_NAME, NOTIFY_IPC_VERSION_NAME_LEN, &cid, &status); 297 if ((kstatus == KERN_SUCCESS) && (status == NOTIFY_STATUS_OK)) 298 { 299 kstatus = _notify_server_get_state(globals->notify_server_port, cid, &state, &status); 300 if ((kstatus == KERN_SUCCESS) && (status == NOTIFY_STATUS_OK)) 301 { 302 globals->notify_ipc_version = state; 303 state >>= 32; 304 globals->notify_server_pid = state; 305 } 306 307 _notify_server_cancel(globals->notify_server_port, cid, &status); 308 309 if ((last_pid == globals->notify_server_pid) && (event == EVENT_REGEN)) 310 { 311 pthread_mutex_unlock(&globals->notify_lock); 312 return NOTIFY_STATUS_INVALID_REQUEST; 313 } 314 } 315 316 if (globals->server_proc_source != NULL) 317 { 318 dispatch_source_cancel(globals->server_proc_source); 319 dispatch_release(globals->server_proc_source); 320 globals->server_proc_source = NULL; 321 } 322 } 323 324 if (globals->notify_ipc_version < 2) 325 { 326 /* regen is not supported below version 2 */ 327 globals->client_opts &= ~NOTIFY_OPT_REGEN; 328 } 329 330 /* 331 * Create a source (DISPATCH_SOURCE_TYPE_PROC) to invoke _notify_lib_regenerate if notifyd restarts. 332 * Available in IPC version 2. 333 */ 334 if ((globals->server_proc_source == NULL) && (globals->client_opts & NOTIFY_OPT_REGEN) && (globals->notify_server_pid != 0)) 335 { 336 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)); 337 dispatch_source_set_event_handler(globals->server_proc_source, ^{ _notify_lib_regenerate(1); }); 338 dispatch_resume(globals->server_proc_source); 339 } 340 341 /* 342 * Create the shared multiplex ports if NOTIFY_OPT_DEMUX is set. 343 */ 344 if ((globals->client_opts & NOTIFY_OPT_DEMUX) && (globals->notify_common_port == MACH_PORT_NULL)) 345 { 346 kern_return_t kr; 347 task_t task = mach_task_self(); 348 349 kr = mach_port_allocate(task, MACH_PORT_RIGHT_RECEIVE, &globals->notify_common_port); 350 if (kr == KERN_SUCCESS) 351 { 352 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)); 353 dispatch_source_set_event_handler(globals->notify_dispatch_source, ^{ 354 notify_globals_t globals = _notify_globals(); 355 _notify_dispatch_handle(globals->notify_common_port); 356 }); 357 dispatch_source_set_cancel_handler(globals->notify_dispatch_source, ^{ 358 task_t task = mach_task_self(); 359 notify_globals_t globals = _notify_globals(); 360 mach_port_mod_refs(task, globals->notify_common_port, MACH_PORT_RIGHT_RECEIVE, -1); 361 }); 362 dispatch_resume(globals->notify_dispatch_source); 363 } 364 } 365 366 pthread_mutex_unlock(&globals->notify_lock); 367 368 if (globals->notify_common_port != MACH_PORT_NULL && (first || event == EVENT_REGEN)) 369 { 370 /* register the common port with notifyd */ 371 status = notify_register_mach_port(COMMON_PORT_KEY, &globals->notify_common_port, NOTIFY_REUSE, &globals->notify_common_token); 372 } 373 374 return NOTIFY_STATUS_OK; 375} 376 377/* Reset all internal state at fork */ 378void 379_notify_fork_child(void) 380{ 381 notify_globals_t globals = _notify_globals(); 382 383 _notify_init_globals(globals); 384 385 /* 386 * Expressly disable notify in the child side of a fork if it had 387 * been initialized in the parent. Using notify in the child process 388 * can lead to deadlock (see <rdar://problem/11498014>). 389 * 390 * Also disable notify in the forked child of a multi-threaded parent that 391 * used dispatch, since notify will use dispatch, and that will blow up. 392 * It's OK to make that check here by calling _dispatch_is_multithreaded(), 393 * since we will actually be looking at the parent's state. 394 */ 395 if (globals->notify_server_port != MACH_PORT_NULL) globals->client_opts = NOTIFY_OPT_DISABLE; 396 if (_dispatch_is_multithreaded) // weak imported symbol 397 { 398 if (_dispatch_is_multithreaded()) globals->client_opts = NOTIFY_OPT_DISABLE; 399 } 400 401 globals->self_state = NULL; 402 globals->notify_server_port = MACH_PORT_NULL; 403 globals->notify_ipc_version = 0; 404 globals->notify_server_pid = 0; 405 406 globals->token_table = NULL; 407 globals->token_name_table = NULL; 408 409 globals->fd_count = 0; 410 globals->fd_clnt = NULL; 411 globals->fd_srv = NULL; 412 globals->fd_refcount = NULL; 413 414 globals->mp_count = 0; 415 globals->mp_list = NULL; 416 globals->mp_refcount = NULL; 417 globals->mp_mine = NULL; 418 419 globals->shm_base = NULL; 420} 421 422static uint32_t 423token_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) 424{ 425 token_table_node_t *t; 426 name_table_node_t *n; 427 uint32_t warn_count = 0; 428 notify_globals_t globals = _notify_globals(); 429 430 dispatch_once(&globals->token_table_once, ^{ 431 globals->token_table = _nc_table_new(CLIENT_TOKEN_TABLE_SIZE); 432 globals->token_name_table = _nc_table_new(CLIENT_TOKEN_TABLE_SIZE); 433 }); 434 435 if (globals->token_table == NULL) return -1; 436 if (globals->token_name_table == NULL) return -1; 437 if (name == NULL) return -1; 438 439 t = (token_table_node_t *)calloc(1, sizeof(token_table_node_t)); 440 if (t == NULL) return -1; 441 442 t->refcount = 1; 443 444 /* we will get t->name from the token_name_table */ 445 t->name = NULL; 446 447 t->namelen = namelen; 448 t->token = token; 449 t->slot = slot; 450 t->val = 0; 451 t->flags = flags; 452 t->fd = fd; 453 t->mp = mp; 454 t->client_id = cid; 455 456 if (lock != NO_LOCK) pthread_mutex_lock(&globals->notify_lock); 457 _nc_table_insert_n(globals->token_table, t->token, t); 458 459 /* check if we have this name in the name table */ 460 n = _nc_table_find_get_key(globals->token_name_table, name, &(t->name)); 461 if (n == NULL) 462 { 463 char *copy_name = strdup(name); 464 if (copy_name == NULL) 465 { 466 free(t); 467 if (lock != NO_LOCK) pthread_mutex_unlock(&globals->notify_lock); 468 return -1; 469 } 470 471 t->name = (const char *)copy_name; 472 473 /* create a new name table node */ 474 n = (name_table_node_t *)calloc(1, sizeof(name_table_node_t)); 475 if (n != NULL) 476 { 477 n->refcount = 1; 478 n->name_id = nid; 479 480 /* the name table node "owns" the name */ 481 _nc_table_insert_pass(globals->token_name_table, copy_name, n); 482 t->name_node = n; 483 } 484 } 485 else 486 { 487 /* this token retains the name table node */ 488 t->name_node = n; 489 n->refcount++; 490 491 if ((n->refcount % MULTIPLE_REGISTRATION_WARNING_TRIGGER) == 0) 492 { 493 warn_count = n->refcount; 494 } 495 } 496 497 if (lock != NO_LOCK) pthread_mutex_unlock(&globals->notify_lock); 498 499 if (warn_count > 0) 500 { 501 char *msg; 502 asprintf(&msg, "notify name \"%s\" has been registered %d times - this may be a leak", name, warn_count); 503 if (msg) 504 _simple_asl_log(ASL_LEVEL_WARNING, "com.apple.notify", msg); 505 free(msg); 506 } 507 508 return 0; 509} 510 511static token_table_node_t * 512token_table_find_retain(uint32_t token) 513{ 514 token_table_node_t *t; 515 notify_globals_t globals = _notify_globals(); 516 517 pthread_mutex_lock(&globals->notify_lock); 518 519 t = (token_table_node_t *)_nc_table_find_n(globals->token_table, token); 520 if (t != NULL) t->refcount++; 521 522 pthread_mutex_unlock(&globals->notify_lock); 523 524 return t; 525} 526 527static token_table_node_t * 528token_table_find_no_lock(uint32_t token) 529{ 530 notify_globals_t globals = _notify_globals(); 531 return (token_table_node_t *)_nc_table_find_n(globals->token_table, token); 532} 533 534static name_table_node_t * 535name_table_find_retain_no_lock(const char *name) 536{ 537 name_table_node_t *n; 538 notify_globals_t globals = _notify_globals(); 539 540 n = (name_table_node_t *)_nc_table_find(globals->token_name_table, name); 541 if (n != NULL) n->refcount++; 542 543 return n; 544} 545 546static void 547name_table_release_no_lock(const char *name) 548{ 549 name_table_node_t *n; 550 notify_globals_t globals = _notify_globals(); 551 552 n = (name_table_node_t *)_nc_table_find(globals->token_name_table, name); 553 if (n != NULL) 554 { 555 if (n->refcount > 0) n->refcount--; 556 if (n->refcount == 0) 557 { 558 _nc_table_delete(globals->token_name_table, name); 559 free(n); 560 } 561 } 562} 563 564static void 565name_table_set_nid(const char *name, uint64_t nid) 566{ 567 name_table_node_t *n; 568 notify_globals_t globals = _notify_globals(); 569 570 pthread_mutex_lock(&globals->notify_lock); 571 572 n = (name_table_node_t *)_nc_table_find(globals->token_name_table, name); 573 if (n != NULL) n->name_id = nid; 574 575 pthread_mutex_unlock(&globals->notify_lock); 576} 577 578static void 579_notify_lib_regenerate_token(token_table_node_t *t) 580{ 581 uint32_t type; 582 int status, new_slot; 583 kern_return_t kstatus; 584 mach_port_t port; 585 uint64_t new_nid; 586 size_t pathlen; 587 588 if (t == NULL) return; 589 if (t->name == NULL) return; 590 if (t->flags & NOTIFY_FLAG_SELF) return; 591 if ((t->flags & NOTIFY_FLAG_REGEN) == 0) return; 592 if (!strcmp(t->name, COMMON_PORT_KEY)) return; 593 594 notify_globals_t globals = _notify_globals(); 595 596 port = MACH_PORT_NULL; 597 if (t->flags & NOTIFY_TYPE_PORT) 598 { 599 port = globals->notify_common_port; 600 } 601 602 pathlen = 0; 603 if (t->path != NULL) pathlen = strlen(t->path); 604 type = t->flags & 0x000000ff; 605 606 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); 607 608 if (kstatus != KERN_SUCCESS) status = NOTIFY_STATUS_FAILED; 609 if (status != NOTIFY_STATUS_OK) return; 610 611 t->slot = new_slot; 612 613 /* reset the name_id in the name table node */ 614 if (t->name_node != NULL) t->name_node->name_id = new_nid; 615} 616 617/* 618 * Invoked when server has died. 619 * Regenerates all registrations and state. 620 */ 621static void 622_notify_lib_regenerate(int src) 623{ 624 void *tt; 625 token_table_node_t *t; 626 notify_globals_t globals = _notify_globals(); 627 628 if ((globals->client_opts & NOTIFY_OPT_REGEN) == 0) return; 629 630 /* _notify_lib_init returns an error if regeneration is unnecessary */ 631 if (_notify_lib_init(EVENT_REGEN) == NOTIFY_STATUS_OK) 632 { 633 pthread_mutex_lock(&globals->notify_lock); 634 635 tt = _nc_table_traverse_start(globals->token_table); 636 while (tt != NULL) 637 { 638 t = _nc_table_traverse(globals->token_table, tt); 639 if (t == NULL) break; 640 _notify_lib_regenerate_token(t); 641 } 642 643 _nc_table_traverse_end(globals->token_table, tt); 644 645 pthread_mutex_unlock(&globals->notify_lock); 646 } 647} 648 649/* 650 * Regenerate if the server PID (shared memory slot 0) has changed. 651 */ 652static inline void 653regenerate_check() 654{ 655 notify_globals_t globals = _notify_globals(); 656 657 if ((globals->client_opts & NOTIFY_OPT_REGEN) == 0) return; 658 659 if ((globals->shm_base != NULL) && (globals->shm_base[0] != globals->notify_server_pid)) _notify_lib_regenerate(0); 660} 661 662/* notify_lock is required in notify_retain_file_descriptor */ 663static void 664notify_retain_file_descriptor(int clnt, int srv) 665{ 666 int x, i; 667 notify_globals_t globals = _notify_globals(); 668 669 if (clnt < 0) return; 670 if (srv < 0) return; 671 672 pthread_mutex_lock(&globals->notify_lock); 673 674 x = -1; 675 for (i = 0; (i < globals->fd_count) && (x < 0); i++) 676 { 677 if (globals->fd_clnt[i] == clnt) x = i; 678 } 679 680 if (x >= 0) 681 { 682 globals->fd_refcount[x]++; 683 pthread_mutex_unlock(&globals->notify_lock); 684 return; 685 } 686 687 x = globals->fd_count; 688 globals->fd_count++; 689 690 if (x == 0) 691 { 692 globals->fd_clnt = (int *)calloc(1, sizeof(int)); 693 globals->fd_srv = (int *)calloc(1, sizeof(int)); 694 globals->fd_refcount = (int *)calloc(1, sizeof(int)); 695 } 696 else 697 { 698 globals->fd_clnt = (int *)reallocf(globals->fd_clnt, globals->fd_count * sizeof(int)); 699 globals->fd_srv = (int *)reallocf(globals->fd_srv, globals->fd_count * sizeof(int)); 700 globals->fd_refcount = (int *)reallocf(globals->fd_refcount, globals->fd_count * sizeof(int)); 701 } 702 703 if ((globals->fd_clnt == NULL) || (globals->fd_srv == NULL) || (globals->fd_refcount == NULL)) 704 { 705 free(globals->fd_clnt); 706 free(globals->fd_srv); 707 free(globals->fd_refcount); 708 globals->fd_count = 0; 709 } 710 else 711 { 712 globals->fd_clnt[x] = clnt; 713 globals->fd_srv[x] = srv; 714 globals->fd_refcount[x] = 1; 715 } 716 717 pthread_mutex_unlock(&globals->notify_lock); 718} 719 720/* notify_lock is NOT required in notify_release_file_descriptor */ 721static void 722notify_release_file_descriptor(int fd) 723{ 724 int x, i, j; 725 notify_globals_t globals = _notify_globals(); 726 727 if (fd < 0) return; 728 729 x = -1; 730 for (i = 0; (i < globals->fd_count) && (x < 0); i++) 731 { 732 if (globals->fd_clnt[i] == fd) x = i; 733 } 734 735 if (x < 0) return; 736 737 if (globals->fd_refcount[x] > 0) globals->fd_refcount[x]--; 738 if (globals->fd_refcount[x] > 0) return; 739 740 close(globals->fd_clnt[x]); 741 close(globals->fd_srv[x]); 742 743 if (globals->fd_count == 1) 744 { 745 free(globals->fd_clnt); 746 free(globals->fd_srv); 747 free(globals->fd_refcount); 748 globals->fd_count = 0; 749 return; 750 } 751 752 for (i = x + 1, j = x; i < globals->fd_count; i++, j++) 753 { 754 globals->fd_clnt[j] = globals->fd_clnt[i]; 755 globals->fd_srv[j] = globals->fd_srv[i]; 756 globals->fd_refcount[j] = globals->fd_refcount[i]; 757 } 758 759 globals->fd_count--; 760 761 globals->fd_clnt = (int *)reallocf(globals->fd_clnt, globals->fd_count * sizeof(int)); 762 globals->fd_srv = (int *)reallocf(globals->fd_srv, globals->fd_count * sizeof(int)); 763 globals->fd_refcount = (int *)reallocf(globals->fd_refcount, globals->fd_count * sizeof(int)); 764 765 if ((globals->fd_clnt == NULL) || (globals->fd_srv == NULL) || (globals->fd_refcount == NULL)) 766 { 767 free(globals->fd_clnt); 768 free(globals->fd_srv); 769 free(globals->fd_refcount); 770 globals->fd_count = 0; 771 } 772} 773 774/* notify_lock is required in notify_retain_mach_port */ 775static void 776notify_retain_mach_port(mach_port_t mp, int mine) 777{ 778 int x, i; 779 notify_globals_t globals = _notify_globals(); 780 781 if (mp == MACH_PORT_NULL) return; 782 783 pthread_mutex_lock(&globals->notify_lock); 784 785 x = -1; 786 for (i = 0; (i < globals->mp_count) && (x < 0); i++) 787 { 788 if (globals->mp_list[i] == mp) x = i; 789 } 790 791 if (x >= 0) 792 { 793 globals->mp_refcount[x]++; 794 pthread_mutex_unlock(&globals->notify_lock); 795 return; 796 } 797 798 x = globals->mp_count; 799 globals->mp_count++; 800 801 if (x == 0) 802 { 803 globals->mp_list = (mach_port_t *)calloc(1, sizeof(mach_port_t)); 804 globals->mp_refcount = (int *)calloc(1, sizeof(int)); 805 globals->mp_mine = (int *)calloc(1, sizeof(int)); 806 } 807 else 808 { 809 globals->mp_list = (mach_port_t *)reallocf(globals->mp_list, globals->mp_count * sizeof(mach_port_t)); 810 globals->mp_refcount = (int *)reallocf(globals->mp_refcount, globals->mp_count * sizeof(int)); 811 globals->mp_mine = (int *)reallocf(globals->mp_mine, globals->mp_count * sizeof(int)); 812 } 813 814 if ((globals->mp_list == NULL) || (globals->mp_refcount == NULL) || (globals->mp_mine == NULL)) 815 { 816 if (globals->mp_list != NULL) free(globals->mp_list); 817 if (globals->mp_refcount != NULL) free(globals->mp_refcount); 818 if (globals->mp_mine != NULL) free(globals->mp_mine); 819 globals->mp_count = 0; 820 } 821 else 822 { 823 globals->mp_list[x] = mp; 824 globals->mp_refcount[x] = 1; 825 globals->mp_mine[x] = mine; 826 } 827 828 pthread_mutex_unlock(&globals->notify_lock); 829} 830 831/* notify_lock is NOT required in notify_release_mach_port */ 832static void 833notify_release_mach_port(mach_port_t mp, uint32_t flags) 834{ 835 int x, i; 836 notify_globals_t globals = _notify_globals(); 837 838 if (mp == MACH_PORT_NULL) return; 839 840 x = -1; 841 for (i = 0; (i < globals->mp_count) && (x < 0); i++) 842 { 843 if (globals->mp_list[i] == mp) x = i; 844 } 845 846 if (x < 0) return; 847 848 if (globals->mp_refcount[x] > 0) globals->mp_refcount[x]--; 849 if (globals->mp_refcount[x] > 0) return; 850 851 if (globals->mp_mine[x] == 1) 852 { 853 mach_port_mod_refs(mach_task_self(), mp, MACH_PORT_RIGHT_RECEIVE, -1); 854 855 /* release send right if this is a self notification */ 856 if (flags & NOTIFY_FLAG_SELF) mach_port_deallocate(mach_task_self(), mp); 857 } 858 859 if (flags & NOTIFY_FLAG_RELEASE_SEND) 860 { 861 /* multiplexed registration holds a send right in Libnotify */ 862 mach_port_deallocate(mach_task_self(), mp); 863 } 864 865 if (globals->mp_count == 1) 866 { 867 if (globals->mp_list != NULL) free(globals->mp_list); 868 if (globals->mp_refcount != NULL) free(globals->mp_refcount); 869 if (globals->mp_mine != NULL) free(globals->mp_mine); 870 globals->mp_count = 0; 871 return; 872 } 873 874 for (i = x + 1; i < globals->mp_count; i++) 875 { 876 globals->mp_list[i - 1] = globals->mp_list[i]; 877 globals->mp_refcount[i - 1] = globals->mp_refcount[i]; 878 globals->mp_mine[i - 1] = globals->mp_mine[i]; 879 } 880 881 globals->mp_count--; 882 883 globals->mp_list = (mach_port_t *)reallocf(globals->mp_list, globals->mp_count * sizeof(mach_port_t)); 884 globals->mp_refcount = (int *)reallocf(globals->mp_refcount, globals->mp_count * sizeof(int)); 885 globals->mp_mine = (int *)reallocf(globals->mp_mine, globals->mp_count * sizeof(int)); 886 887 if ((globals->mp_list == NULL) || (globals->mp_refcount == NULL) || (globals->mp_mine == NULL)) 888 { 889 if (globals->mp_list != NULL) free(globals->mp_list); 890 if (globals->mp_refcount != NULL) free(globals->mp_refcount); 891 if (globals->mp_mine != NULL) free(globals->mp_mine); 892 globals->mp_count = 0; 893 } 894} 895 896static void 897token_table_release_no_lock(token_table_node_t *t) 898{ 899 notify_globals_t globals = _notify_globals(); 900 901 if (t == NULL) return; 902 903 if (t->refcount > 0) t->refcount--; 904 if (t->refcount > 0) return; 905 906 notify_release_file_descriptor(t->fd); 907 notify_release_mach_port(t->mp, t->flags); 908 909 if (t->block != NULL) 910 { 911 dispatch_async_f(t->queue, t->block, (dispatch_function_t)_Block_release); 912 } 913 914 t->block = NULL; 915 916 if (t->queue != NULL) dispatch_release(t->queue); 917 t->queue = NULL; 918 919 _nc_table_delete_n(globals->token_table, t->token); 920 name_table_release_no_lock(t->name); 921 922 free(t->path); 923 free(t); 924} 925 926static void 927token_table_release(token_table_node_t *t) 928{ 929 notify_globals_t globals = _notify_globals(); 930 931 pthread_mutex_lock(&globals->notify_lock); 932 token_table_release_no_lock(t); 933 pthread_mutex_unlock(&globals->notify_lock); 934} 935 936static notify_state_t * 937_notify_lib_self_state() 938{ 939 notify_globals_t globals = _notify_globals(); 940 941 dispatch_once(&globals->self_state_once, ^{ 942 globals->self_state = _notify_lib_notify_state_new(NOTIFY_STATE_USE_LOCKS, 0); 943 }); 944 945 return globals->self_state; 946} 947 948/* SPI */ 949void 950notify_set_options(uint32_t opts) 951{ 952 notify_globals_t globals = _notify_globals(); 953 954 /* NOTIFY_OPT_DISABLE can be unset with NOTIFY_OPT_ENABLE */ 955 if (globals->client_opts & NOTIFY_OPT_DISABLE) 956 { 957 if ((opts & NOTIFY_OPT_ENABLE) == 0) return; 958 959 /* re-enable by swapping in the saved server port and saved opts*/ 960 pthread_mutex_lock(&globals->notify_lock); 961 962 globals->client_opts = globals->saved_opts; 963 globals->notify_server_port = globals->saved_server_port; 964 965 pthread_mutex_unlock(&globals->notify_lock); 966 return; 967 } 968 969 /* 970 * A client can disable the library even if the server port has already been fetched. 971 * Note that this could race with another thread making a Libnotify call. 972 */ 973 if (opts & NOTIFY_OPT_DISABLE) 974 { 975 pthread_mutex_lock(&globals->notify_lock); 976 977 globals->saved_opts = globals->client_opts; 978 globals->client_opts = NOTIFY_OPT_DISABLE; 979 980 globals->saved_server_port = globals->notify_server_port; 981 globals->notify_server_port = MACH_PORT_NULL; 982 983 pthread_mutex_unlock(&globals->notify_lock); 984 return; 985 } 986 987 globals->client_opts = opts; 988 989 /* call _notify_lib_init to create ports / dispatch sources as required */ 990 _notify_lib_init(EVENT_INIT); 991} 992 993/* 994 * PUBLIC API 995 */ 996 997/* 998 * notify_post is a very simple API, but the implementation is 999 * more complex to try to optimize the time it takes. 1000 * 1001 * The server - notifyd - keeps a unique ID number for each key 1002 * in the namespace. Although it's reasonably fast to call 1003 * _notify_server_post_4 (a MIG simpleroutine), the MIG call 1004 * allocates VM and copies the name string. It's much faster to 1005 * call using the ID number. The problem is mapping from name to 1006 * ID number. The token table keeps track of all registered names 1007 * (in the client), but the registration calls are simpleroutines, 1008 * except for notify_register_check. notify_register_check saves 1009 * the name ID in the token table, but the other routines set it 1010 * to NID_UNSET. 1011 * 1012 * In notify_post, we check if the name is known. If it is not, 1013 * then the client is doing a "cold call". There may be no 1014 * clients for this name anywhere on the system. In this case 1015 * we simply send the name. We take the allocate/copy cost, but 1016 * the latency is still not too bad since we use a simpleroutine. 1017 * 1018 * If the name in registered and the ID number is known, we send 1019 * the ID using a simpleroutine. This is very fast. 1020 * 1021 * If the name is registered but the ID number is NID_UNSET, we 1022 * send the name (as in a "cold call". It *might* just be that 1023 * this client process just posts once, and we don't want to incur 1024 * any addition cost. The ID number is reset to NID_CALLED_ONCE. 1025 * 1026 * If the client posts the same name again (the ID number is 1027 * NID_CALLED_ONCE, we do a synchronous call to notifyd, sending 1028 * the name string and getting back the name ID, whcih we save 1029 * in the token table. This is simply a zero/one/many heuristic: 1030 * If the client posts the same name more than once, we make the 1031 * guess that it's going to do it more frequently, and it's worth 1032 * the time it takes to fetch the ID from notifyd. 1033 */ 1034uint32_t 1035notify_post(const char *name) 1036{ 1037 notify_state_t *ns_self; 1038 kern_return_t kstatus; 1039 uint32_t status; 1040 size_t namelen = 0; 1041 name_table_node_t *n; 1042 uint64_t nid = UINT64_MAX; 1043 notify_globals_t globals = _notify_globals(); 1044 1045 regenerate_check(); 1046 1047 if (name == NULL) return NOTIFY_STATUS_INVALID_NAME; 1048 1049 if (!strncmp(name, SELF_PREFIX, SELF_PREFIX_LEN)) 1050 { 1051 ns_self = _notify_lib_self_state(); 1052 if (ns_self == NULL) return NOTIFY_STATUS_FAILED; 1053 _notify_lib_post(ns_self, name, 0, 0); 1054 return NOTIFY_STATUS_OK; 1055 } 1056 1057 if (globals->notify_server_port == MACH_PORT_NULL) 1058 { 1059 status = _notify_lib_init(EVENT_INIT); 1060 if (status != 0) return NOTIFY_STATUS_FAILED; 1061 } 1062 1063 if (globals->notify_ipc_version == 0) 1064 { 1065 namelen = strlen(name); 1066 kstatus = _notify_server_post(globals->notify_server_port, (caddr_t)name, namelen, (int32_t *)&status); 1067 if (kstatus != KERN_SUCCESS) return NOTIFY_STATUS_FAILED; 1068 return status; 1069 } 1070 1071 namelen = strlen(name); 1072 1073 /* Lock to prevent a race with notify cancel over the use of name IDs */ 1074 pthread_mutex_lock(&globals->notify_lock); 1075 1076 /* See if we have a name ID for this name. */ 1077 n = name_table_find_retain_no_lock(name); 1078 if (n != NULL) 1079 { 1080 if (n->name_id == NID_UNSET) 1081 { 1082 /* First post goes using the name string. */ 1083 kstatus = _notify_server_post_4(globals->notify_server_port, (caddr_t)name, namelen); 1084 if (kstatus != KERN_SUCCESS) 1085 { 1086 name_table_release_no_lock(name); 1087 pthread_mutex_unlock(&globals->notify_lock); 1088 return NOTIFY_STATUS_FAILED; 1089 } 1090 1091 n->name_id = NID_CALLED_ONCE; 1092 name_table_release_no_lock(name); 1093 pthread_mutex_unlock(&globals->notify_lock); 1094 return NOTIFY_STATUS_OK; 1095 } 1096 else if (n->name_id == NID_CALLED_ONCE) 1097 { 1098 /* Post and fetch the name ID. Slow, but subsequent posts will be very fast. */ 1099 kstatus = _notify_server_post_2(globals->notify_server_port, (caddr_t)name, namelen, &nid, (int32_t *)&status); 1100 if (kstatus != KERN_SUCCESS) 1101 { 1102 name_table_release_no_lock(name); 1103 pthread_mutex_unlock(&globals->notify_lock); 1104 return NOTIFY_STATUS_FAILED; 1105 } 1106 1107 if (status == NOTIFY_STATUS_OK) n->name_id = nid; 1108 name_table_release_no_lock(name); 1109 pthread_mutex_unlock(&globals->notify_lock); 1110 return status; 1111 } 1112 else 1113 { 1114 /* We have the name ID. Do an async post using the name ID. Very fast. */ 1115 kstatus = _notify_server_post_3(globals->notify_server_port, n->name_id); 1116 name_table_release_no_lock(name); 1117 pthread_mutex_unlock(&globals->notify_lock); 1118 if (kstatus != KERN_SUCCESS) return NOTIFY_STATUS_FAILED; 1119 return NOTIFY_STATUS_OK; 1120 } 1121 } 1122 1123 pthread_mutex_unlock(&globals->notify_lock); 1124 1125 /* Do an async post using the name string. Fast (but not as fast as using name ID). */ 1126 kstatus = _notify_server_post_4(globals->notify_server_port, (caddr_t)name, namelen); 1127 if (kstatus != KERN_SUCCESS) return NOTIFY_STATUS_FAILED; 1128 return NOTIFY_STATUS_OK; 1129} 1130 1131uint32_t 1132notify_set_owner(const char *name, uint32_t uid, uint32_t gid) 1133{ 1134 notify_state_t *ns_self; 1135 kern_return_t kstatus; 1136 uint32_t status; 1137 notify_globals_t globals = _notify_globals(); 1138 1139 if (name == NULL) return NOTIFY_STATUS_INVALID_NAME; 1140 1141 if (!strncmp(name, SELF_PREFIX, SELF_PREFIX_LEN)) 1142 { 1143 ns_self = _notify_lib_self_state(); 1144 if (ns_self == NULL) return NOTIFY_STATUS_FAILED; 1145 status = _notify_lib_set_owner(ns_self, name, uid, gid); 1146 return status; 1147 } 1148 1149 if (globals->notify_server_port == MACH_PORT_NULL) 1150 { 1151 status = _notify_lib_init(EVENT_INIT); 1152 if (status != 0) return NOTIFY_STATUS_FAILED; 1153 } 1154 1155 kstatus = _notify_server_set_owner(globals->notify_server_port, (caddr_t)name, strlen(name), uid, gid, (int32_t *)&status); 1156 if (kstatus != KERN_SUCCESS) return NOTIFY_STATUS_FAILED; 1157 return status; 1158} 1159 1160uint32_t 1161notify_get_owner(const char *name, uint32_t *uid, uint32_t *gid) 1162{ 1163 notify_state_t *ns_self; 1164 kern_return_t kstatus; 1165 uint32_t status; 1166 notify_globals_t globals = _notify_globals(); 1167 1168 if (name == NULL) return NOTIFY_STATUS_INVALID_NAME; 1169 1170 if (!strncmp(name, SELF_PREFIX, SELF_PREFIX_LEN)) 1171 { 1172 ns_self = _notify_lib_self_state(); 1173 if (ns_self == NULL) return NOTIFY_STATUS_FAILED; 1174 status = _notify_lib_get_owner(ns_self, name, uid, gid); 1175 return status; 1176 } 1177 1178 if (globals->notify_server_port == MACH_PORT_NULL) 1179 { 1180 status = _notify_lib_init(EVENT_INIT); 1181 if (status != 0) return NOTIFY_STATUS_FAILED; 1182 } 1183 1184 kstatus = _notify_server_get_owner(globals->notify_server_port, (caddr_t)name, strlen(name), (int32_t *)uid, (int32_t *)gid, (int32_t *)&status); 1185 if (kstatus != KERN_SUCCESS) return NOTIFY_STATUS_FAILED; 1186 return status; 1187} 1188 1189uint32_t 1190notify_set_access(const char *name, uint32_t access) 1191{ 1192 notify_state_t *ns_self; 1193 kern_return_t kstatus; 1194 uint32_t status; 1195 notify_globals_t globals = _notify_globals(); 1196 1197 if (name == NULL) return NOTIFY_STATUS_INVALID_NAME; 1198 1199 if (!strncmp(name, SELF_PREFIX, SELF_PREFIX_LEN)) 1200 { 1201 ns_self = _notify_lib_self_state(); 1202 if (ns_self == NULL) return NOTIFY_STATUS_FAILED; 1203 status = _notify_lib_set_access(ns_self, name, access); 1204 return status; 1205 } 1206 1207 if (globals->notify_server_port == MACH_PORT_NULL) 1208 { 1209 status = _notify_lib_init(EVENT_INIT); 1210 if (status != 0) return NOTIFY_STATUS_FAILED; 1211 } 1212 1213 kstatus = _notify_server_set_access(globals->notify_server_port, (caddr_t)name, strlen(name), access, (int32_t *)&status); 1214 if (kstatus != KERN_SUCCESS) return NOTIFY_STATUS_FAILED; 1215 return status; 1216} 1217 1218uint32_t 1219notify_get_access(const char *name, uint32_t *access) 1220{ 1221 notify_state_t *ns_self; 1222 kern_return_t kstatus; 1223 uint32_t status; 1224 notify_globals_t globals = _notify_globals(); 1225 1226 if (name == NULL) return NOTIFY_STATUS_INVALID_NAME; 1227 1228 if (!strncmp(name, SELF_PREFIX, SELF_PREFIX_LEN)) 1229 { 1230 ns_self = _notify_lib_self_state(); 1231 if (ns_self == NULL) return NOTIFY_STATUS_FAILED; 1232 status = _notify_lib_get_access(ns_self, name, access); 1233 return status; 1234 } 1235 1236 if (globals->notify_server_port == MACH_PORT_NULL) 1237 { 1238 status = _notify_lib_init(EVENT_INIT); 1239 if (status != 0) return NOTIFY_STATUS_FAILED; 1240 } 1241 1242 kstatus = _notify_server_get_access(globals->notify_server_port, (caddr_t)name, strlen(name), (int32_t *)access, (int32_t *)&status); 1243 if (kstatus != KERN_SUCCESS) return NOTIFY_STATUS_FAILED; 1244 return status; 1245} 1246 1247/* notifyd retains and releases a name when clients register and cancel. */ 1248uint32_t 1249notify_release_name(const char *name) 1250{ 1251 return NOTIFY_STATUS_OK; 1252} 1253 1254static void 1255_notify_dispatch_handle(mach_port_t port) 1256{ 1257 token_table_node_t *t; 1258 int token; 1259 mach_msg_empty_rcv_t msg; 1260 kern_return_t status; 1261 1262 if (port == MACH_PORT_NULL) return; 1263 1264 memset(&msg, 0, sizeof(msg)); 1265 1266 status = mach_msg(&msg.header, MACH_RCV_MSG, 0, sizeof(msg), port, 0, MACH_PORT_NULL); 1267 if (status != KERN_SUCCESS) return; 1268 1269 token = msg.header.msgh_id; 1270 1271 t = token_table_find_retain(token); 1272 1273 if (t != NULL) 1274 { 1275 if ((t->queue != NULL) && (t->block != NULL)) 1276 { 1277 /* 1278 * Don't reference into the token table node after token_table_release(). 1279 * If the block calls notify_cancel, the node can get trashed, so 1280 * we keep anything we need from the block (properly retained and released) 1281 * in local variables. Concurrent notify_cancel() calls in the block are safe. 1282 */ 1283 notify_handler_t theblock = Block_copy(t->block); 1284 dispatch_queue_t thequeue = t->queue; 1285 dispatch_retain(thequeue); 1286 1287 dispatch_async(thequeue, ^{ 1288 token_table_node_t *t = token_table_find_no_lock(token); 1289 if (t != NULL) theblock(token); 1290 }); 1291 1292 _Block_release(theblock); 1293 dispatch_release(thequeue); 1294 } 1295 1296 token_table_release(t); 1297 } 1298} 1299 1300uint32_t 1301notify_register_dispatch(const char *name, int *out_token, dispatch_queue_t queue, notify_handler_t handler) 1302{ 1303 __block uint32_t status; 1304 token_table_node_t *t; 1305 notify_globals_t globals = _notify_globals(); 1306 1307 regenerate_check(); 1308 1309 if (queue == NULL) return NOTIFY_STATUS_FAILED; 1310 if (handler == NULL) return NOTIFY_STATUS_FAILED; 1311 1312 /* client is using dispatch: enable local demux and regeneration */ 1313 notify_set_options(NOTIFY_OPT_DEMUX | NOTIFY_OPT_REGEN); 1314 1315 status = notify_register_mach_port(name, &globals->notify_common_port, NOTIFY_REUSE, out_token); 1316 if (status != NOTIFY_STATUS_OK) return status; 1317 1318 t = token_table_find_retain(*out_token); 1319 if (t == NULL) return NOTIFY_STATUS_FAILED; 1320 1321 t->queue = queue; 1322 dispatch_retain(t->queue); 1323 t->block = Block_copy(handler); 1324 token_table_release(t); 1325 1326 return NOTIFY_STATUS_OK; 1327} 1328 1329/* note this does not get self names */ 1330static uint32_t 1331notify_register_mux_fd(const char *name, int *out_token, int rfd, int wfd) 1332{ 1333 __block uint32_t status; 1334 token_table_node_t *t; 1335 int val; 1336 notify_globals_t globals = _notify_globals(); 1337 1338 status = NOTIFY_STATUS_OK; 1339 1340 if (globals->notify_common_port == MACH_PORT_NULL) return NOTIFY_STATUS_FAILED; 1341 1342 status = notify_register_mach_port(name, &globals->notify_common_port, NOTIFY_REUSE, out_token); 1343 1344 t = token_table_find_retain(*out_token); 1345 if (t == NULL) return NOTIFY_STATUS_FAILED; 1346 1347 t->token = *out_token; 1348 t->fd = rfd; 1349 t->queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0); 1350 dispatch_retain(t->queue); 1351 val = htonl(t->token); 1352 t->block = (notify_handler_t)Block_copy(^(int unused){ write(wfd, &val, sizeof(val)); }); 1353 1354 token_table_release(t); 1355 1356 return NOTIFY_STATUS_OK; 1357} 1358 1359uint32_t 1360notify_register_check(const char *name, int *out_token) 1361{ 1362 notify_state_t *ns_self; 1363 kern_return_t kstatus; 1364 uint32_t status, token; 1365 uint64_t nid; 1366 int32_t slot, shmsize; 1367 size_t namelen; 1368 uint32_t cid; 1369 notify_globals_t globals = _notify_globals(); 1370 1371 regenerate_check(); 1372 1373 if (name == NULL) return NOTIFY_STATUS_INVALID_NAME; 1374 if (out_token == NULL) return NOTIFY_STATUS_FAILED; 1375 1376 *out_token = -1; 1377 namelen = strlen(name); 1378 1379 if (!strncmp(name, SELF_PREFIX, SELF_PREFIX_LEN)) 1380 { 1381 ns_self = _notify_lib_self_state(); 1382 if (ns_self == NULL) return NOTIFY_STATUS_FAILED; 1383 1384 token = OSAtomicIncrement32((int32_t *)&globals->token_id); 1385 status = _notify_lib_register_plain(ns_self, name, NOTIFY_CLIENT_SELF, token, SLOT_NONE, 0, 0, &nid); 1386 if (status != NOTIFY_STATUS_OK) return status; 1387 1388 cid = token; 1389 token_table_add(name, namelen, nid, token, cid, SLOT_NONE, NOTIFY_FLAG_SELF | NOTIFY_TYPE_PLAIN, SIGNAL_NONE, FD_NONE, MACH_PORT_NULL, 0); 1390 1391 *out_token = token; 1392 return NOTIFY_STATUS_OK; 1393 } 1394 1395 if (globals->notify_server_port == MACH_PORT_NULL) 1396 { 1397 status = _notify_lib_init(EVENT_INIT); 1398 if (status != 0) return NOTIFY_STATUS_FAILED; 1399 } 1400 1401 token = OSAtomicIncrement32((int32_t *)&globals->token_id); 1402 kstatus = KERN_SUCCESS; 1403 1404 if (globals->notify_ipc_version == 0) 1405 { 1406 nid = NID_UNSET; 1407 kstatus = _notify_server_register_check(globals->notify_server_port, (caddr_t)name, namelen, &shmsize, &slot, (int32_t *)&cid, (int32_t *)&status); 1408 } 1409 else 1410 { 1411 cid = token; 1412 kstatus = _notify_server_register_check_2(globals->notify_server_port, (caddr_t)name, namelen, token, &shmsize, &slot, &nid, (int32_t *)&status); 1413 } 1414 1415 if (kstatus != KERN_SUCCESS) return NOTIFY_STATUS_FAILED; 1416 if (status != NOTIFY_STATUS_OK) return status; 1417 1418 if (shmsize != -1) 1419 { 1420 if (globals->shm_base == NULL) 1421 { 1422 if (shm_attach(shmsize) != 0) return NOTIFY_STATUS_FAILED; 1423 if (globals->shm_base == NULL) return NOTIFY_STATUS_FAILED; 1424 } 1425 1426 token_table_add(name, namelen, nid, token, cid, slot, NOTIFY_TYPE_MEMORY | NOTIFY_FLAG_REGEN, SIGNAL_NONE, FD_NONE, MACH_PORT_NULL, 0); 1427 } 1428 else 1429 { 1430 token_table_add(name, namelen, nid, token, cid, SLOT_NONE, NOTIFY_TYPE_PLAIN | NOTIFY_FLAG_REGEN, SIGNAL_NONE, FD_NONE, MACH_PORT_NULL, 0); 1431 } 1432 1433 *out_token = token; 1434 return status; 1435} 1436 1437uint32_t 1438notify_register_plain(const char *name, int *out_token) 1439{ 1440 notify_state_t *ns_self; 1441 kern_return_t kstatus; 1442 uint32_t status; 1443 uint64_t nid; 1444 size_t namelen; 1445 int token; 1446 uint32_t cid; 1447 notify_globals_t globals = _notify_globals(); 1448 1449 regenerate_check(); 1450 1451 if (name == NULL) return NOTIFY_STATUS_INVALID_NAME; 1452 1453 namelen = strlen(name); 1454 1455 if (!strncmp(name, SELF_PREFIX, SELF_PREFIX_LEN)) 1456 { 1457 ns_self = _notify_lib_self_state(); 1458 if (ns_self == NULL) return NOTIFY_STATUS_FAILED; 1459 1460 token = OSAtomicIncrement32((int32_t *)&globals->token_id); 1461 status = _notify_lib_register_plain(ns_self, name, NOTIFY_CLIENT_SELF, token, SLOT_NONE, 0, 0, &nid); 1462 if (status != NOTIFY_STATUS_OK) return status; 1463 1464 cid = token; 1465 token_table_add(name, namelen, nid, token, cid, SLOT_NONE, NOTIFY_FLAG_SELF | NOTIFY_TYPE_PLAIN, SIGNAL_NONE, FD_NONE, MACH_PORT_NULL, 0); 1466 1467 *out_token = token; 1468 return NOTIFY_STATUS_OK; 1469 } 1470 1471 if (globals->notify_server_port == MACH_PORT_NULL) 1472 { 1473 status = _notify_lib_init(EVENT_INIT); 1474 if (status != 0) return NOTIFY_STATUS_FAILED; 1475 } 1476 1477 token = OSAtomicIncrement32((int32_t *)&globals->token_id); 1478 1479 if (globals->notify_ipc_version == 0) 1480 { 1481 kstatus = _notify_server_register_plain(globals->notify_server_port, (caddr_t)name, namelen, (int32_t *)&cid, (int32_t *)&status); 1482 if (kstatus != KERN_SUCCESS) return NOTIFY_STATUS_FAILED; 1483 if (status != NOTIFY_STATUS_OK) return status; 1484 } 1485 else 1486 { 1487 cid = token; 1488 kstatus = _notify_server_register_plain_2(globals->notify_server_port, (caddr_t)name, namelen, token); 1489 if (kstatus != KERN_SUCCESS) return NOTIFY_STATUS_FAILED; 1490 } 1491 1492 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); 1493 1494 *out_token = token; 1495 return NOTIFY_STATUS_OK; 1496} 1497 1498uint32_t 1499notify_register_signal(const char *name, int sig, int *out_token) 1500{ 1501 notify_state_t *ns_self; 1502 kern_return_t kstatus; 1503 uint32_t status; 1504 uint64_t nid; 1505 size_t namelen; 1506 int token; 1507 uint32_t cid; 1508 notify_globals_t globals = _notify_globals(); 1509 1510 regenerate_check(); 1511 1512 if (name == NULL) return NOTIFY_STATUS_INVALID_NAME; 1513 1514 namelen = strlen(name); 1515 1516 if (!strncmp(name, SELF_PREFIX, SELF_PREFIX_LEN)) 1517 { 1518 ns_self = _notify_lib_self_state(); 1519 if (ns_self == NULL) return NOTIFY_STATUS_FAILED; 1520 1521 token = OSAtomicIncrement32((int32_t *)&globals->token_id); 1522 status = _notify_lib_register_signal(ns_self, name, NOTIFY_CLIENT_SELF, token, sig, 0, 0, &nid); 1523 if (status != NOTIFY_STATUS_OK) return status; 1524 1525 cid = token; 1526 token_table_add(name, namelen, nid, token, cid, SLOT_NONE, NOTIFY_FLAG_SELF | NOTIFY_TYPE_SIGNAL, sig, FD_NONE, MACH_PORT_NULL, 0); 1527 1528 *out_token = token; 1529 return NOTIFY_STATUS_OK; 1530 } 1531 1532 if (globals->client_opts & NOTIFY_OPT_DEMUX) 1533 { 1534 return notify_register_dispatch(name, out_token, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^(int unused){ kill(getpid(), sig); }); 1535 } 1536 1537 if (globals->notify_server_port == MACH_PORT_NULL) 1538 { 1539 status = _notify_lib_init(EVENT_INIT); 1540 if (status != 0) return NOTIFY_STATUS_FAILED; 1541 } 1542 1543 token = OSAtomicIncrement32((int32_t *)&globals->token_id); 1544 1545 if (globals->notify_ipc_version == 0) 1546 { 1547 kstatus = _notify_server_register_signal(globals->notify_server_port, (caddr_t)name, namelen, sig, (int32_t *)&cid, (int32_t *)&status); 1548 if (kstatus != KERN_SUCCESS) return NOTIFY_STATUS_FAILED; 1549 if (status != NOTIFY_STATUS_OK) return status; 1550 } 1551 else 1552 { 1553 cid = token; 1554 kstatus = _notify_server_register_signal_2(globals->notify_server_port, (caddr_t)name, namelen, token, sig); 1555 if (kstatus != KERN_SUCCESS) return NOTIFY_STATUS_FAILED; 1556 } 1557 1558 token_table_add(name, namelen, NID_UNSET, token, cid, SLOT_NONE, NOTIFY_TYPE_SIGNAL | NOTIFY_FLAG_REGEN, sig, FD_NONE, MACH_PORT_NULL, 0); 1559 1560 *out_token = token; 1561 return NOTIFY_STATUS_OK; 1562} 1563 1564uint32_t 1565notify_register_mach_port(const char *name, mach_port_name_t *notify_port, int flags, int *out_token) 1566{ 1567 notify_state_t *ns_self; 1568 kern_return_t kstatus; 1569 uint32_t status; 1570 uint64_t nid; 1571 task_t task; 1572 int token, mine; 1573 size_t namelen; 1574 uint32_t cid, tflags; 1575 token_table_node_t *t; 1576 mach_port_name_t port; 1577 notify_globals_t globals = _notify_globals(); 1578 1579 regenerate_check(); 1580 1581 if (name == NULL) return NOTIFY_STATUS_INVALID_NAME; 1582 if (notify_port == NULL) return NOTIFY_STATUS_INVALID_PORT; 1583 1584 mine = 0; 1585 namelen = strlen(name); 1586 1587 task = mach_task_self(); 1588 1589 if ((flags & NOTIFY_REUSE) == 0) 1590 { 1591 mine = 1; 1592 kstatus = mach_port_allocate(task, MACH_PORT_RIGHT_RECEIVE, notify_port); 1593 if (kstatus != KERN_SUCCESS) return NOTIFY_STATUS_FAILED; 1594 } 1595 1596 kstatus = mach_port_insert_right(task, *notify_port, *notify_port, MACH_MSG_TYPE_MAKE_SEND); 1597 if (kstatus != KERN_SUCCESS) 1598 { 1599 if (mine == 1) mach_port_mod_refs(task, *notify_port, MACH_PORT_RIGHT_RECEIVE, -1); 1600 return NOTIFY_STATUS_FAILED; 1601 } 1602 1603 if (!strncmp(name, SELF_PREFIX, SELF_PREFIX_LEN)) 1604 { 1605 ns_self = _notify_lib_self_state(); 1606 if (ns_self == NULL) 1607 { 1608 if (mine == 1) 1609 { 1610 mach_port_mod_refs(task, *notify_port, MACH_PORT_RIGHT_RECEIVE, -1); 1611 } 1612 1613 mach_port_deallocate(task, *notify_port); 1614 return NOTIFY_STATUS_FAILED; 1615 } 1616 1617 token = OSAtomicIncrement32((int32_t *)&globals->token_id); 1618 status = _notify_lib_register_mach_port(ns_self, name, NOTIFY_CLIENT_SELF, token, *notify_port, 0, 0, &nid); 1619 if (status != NOTIFY_STATUS_OK) 1620 { 1621 if (mine == 1) 1622 { 1623 mach_port_mod_refs(task, *notify_port, MACH_PORT_RIGHT_RECEIVE, -1); 1624 } 1625 1626 mach_port_deallocate(task, *notify_port); 1627 return status; 1628 } 1629 1630 cid = token; 1631 token_table_add(name, namelen, nid, token, cid, SLOT_NONE, NOTIFY_FLAG_SELF | NOTIFY_TYPE_PORT, SIGNAL_NONE, FD_NONE, *notify_port, 0); 1632 1633 *out_token = token; 1634 notify_retain_mach_port(*notify_port, mine); 1635 1636 return NOTIFY_STATUS_OK; 1637 } 1638 1639 if (globals->notify_server_port == MACH_PORT_NULL) 1640 { 1641 status = _notify_lib_init(EVENT_INIT); 1642 if (status != 0) 1643 { 1644 if (mine == 1) 1645 { 1646 mach_port_mod_refs(task, *notify_port, MACH_PORT_RIGHT_RECEIVE, -1); 1647 } 1648 1649 mach_port_deallocate(task, *notify_port); 1650 return NOTIFY_STATUS_FAILED; 1651 } 1652 } 1653 1654 if ((globals->client_opts & NOTIFY_OPT_DEMUX) && (*notify_port != globals->notify_common_port)) 1655 { 1656 port = globals->notify_common_port; 1657 kstatus = mach_port_insert_right(task, globals->notify_common_port, globals->notify_common_port, MACH_MSG_TYPE_MAKE_SEND); 1658 } 1659 else 1660 { 1661 port = *notify_port; 1662 kstatus = KERN_SUCCESS; 1663 } 1664 1665 if (kstatus == KERN_SUCCESS) 1666 { 1667 token = OSAtomicIncrement32((int32_t *)&globals->token_id); 1668 1669 if (globals->notify_ipc_version == 0) 1670 { 1671 kstatus = _notify_server_register_mach_port(globals->notify_server_port, (caddr_t)name, namelen, port, token, (int32_t *)&cid, (int32_t *)&status); 1672 if ((kstatus == KERN_SUCCESS) && (status != NOTIFY_STATUS_OK)) kstatus = KERN_FAILURE; 1673 } 1674 else 1675 { 1676 cid = token; 1677 kstatus = _notify_server_register_mach_port_2(globals->notify_server_port, (caddr_t)name, namelen, token, port); 1678 } 1679 } 1680 1681 if (kstatus != KERN_SUCCESS) 1682 { 1683 if (mine == 1) 1684 { 1685 mach_port_mod_refs(task, *notify_port, MACH_PORT_RIGHT_RECEIVE, -1); 1686 } 1687 1688 mach_port_deallocate(task, *notify_port); 1689 return NOTIFY_STATUS_FAILED; 1690 } 1691 1692 tflags = NOTIFY_TYPE_PORT; 1693 if (port == globals->notify_common_port) tflags |= NOTIFY_FLAG_REGEN; 1694 token_table_add(name, namelen, NID_UNSET, token, cid, SLOT_NONE, tflags, SIGNAL_NONE, FD_NONE, *notify_port, 0); 1695 1696 if ((globals->client_opts & NOTIFY_OPT_DEMUX) && (*notify_port != globals->notify_common_port)) 1697 { 1698 t = token_table_find_retain(token); 1699 if (t == NULL) return NOTIFY_STATUS_FAILED; 1700 1701 /* remember to release the send right when this gets cancelled */ 1702 t->flags |= NOTIFY_FLAG_RELEASE_SEND; 1703 1704 port = *notify_port; 1705 t->queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0); 1706 dispatch_retain(t->queue); 1707 t->block = (notify_handler_t)Block_copy(^(int unused){ 1708 mach_msg_empty_send_t msg; 1709 kern_return_t kstatus; 1710 1711 /* send empty message to the port with msgh_id = token; */ 1712 memset(&msg, 0, sizeof(msg)); 1713 msg.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSGH_BITS_ZERO); 1714 msg.header.msgh_remote_port = port; 1715 msg.header.msgh_local_port = MACH_PORT_NULL; 1716 msg.header.msgh_size = sizeof(mach_msg_empty_send_t); 1717 msg.header.msgh_id = token; 1718 1719 kstatus = mach_msg(&(msg.header), MACH_SEND_MSG | MACH_SEND_TIMEOUT, msg.header.msgh_size, 0, MACH_PORT_NULL, 0, MACH_PORT_NULL); 1720 }); 1721 1722 token_table_release(t); 1723 } 1724 1725 *out_token = token; 1726 notify_retain_mach_port(*notify_port, mine); 1727 1728 return NOTIFY_STATUS_OK; 1729} 1730 1731static char * 1732_notify_mk_tmp_path(int tid) 1733{ 1734#if TARGET_OS_EMBEDDED 1735 int freetmp = 0; 1736 char *path, *tmp = getenv("TMPDIR"); 1737 1738 if (tmp == NULL) 1739 { 1740 asprintf(&tmp, "/tmp/com.apple.notify.%d", geteuid()); 1741 mkdir(tmp, 0755); 1742 freetmp = 1; 1743 } 1744 1745 if (tmp == NULL) return NULL; 1746 1747 asprintf(&path, "%s/com.apple.notify.%d.%d", tmp, getpid(), tid); 1748 if (freetmp) free(tmp); 1749 return path; 1750#else 1751 char tmp[PATH_MAX], *path; 1752 1753 if (confstr(_CS_DARWIN_USER_TEMP_DIR, tmp, sizeof(tmp)) <= 0) return NULL; 1754#endif 1755 1756 path = NULL; 1757 asprintf(&path, "%s/com.apple.notify.%d.%d", tmp, getpid(), tid); 1758 return path; 1759} 1760 1761uint32_t 1762notify_register_file_descriptor(const char *name, int *notify_fd, int flags, int *out_token) 1763{ 1764 notify_state_t *ns_self; 1765 uint32_t i, status; 1766 uint64_t nid; 1767 int token, mine, fdpair[2]; 1768 size_t namelen; 1769 fileport_t fileport; 1770 kern_return_t kstatus; 1771 uint32_t cid; 1772 notify_globals_t globals = _notify_globals(); 1773 1774 regenerate_check(); 1775 1776 mine = 0; 1777 1778 if (name == NULL) return NOTIFY_STATUS_INVALID_NAME; 1779 if (notify_fd == NULL) return NOTIFY_STATUS_INVALID_FILE; 1780 1781 namelen = strlen(name); 1782 1783 if ((flags & NOTIFY_REUSE) == 0) 1784 { 1785 if (pipe(fdpair) < 0) return NOTIFY_STATUS_FAILED; 1786 1787 mine = 1; 1788 *notify_fd = fdpair[0]; 1789 } 1790 else 1791 { 1792 /* check the file descriptor - it must be one of "ours" */ 1793 for (i = 0; i < globals->fd_count; i++) 1794 { 1795 if (globals->fd_clnt[i] == *notify_fd) break; 1796 } 1797 1798 if (i >= globals->fd_count) return NOTIFY_STATUS_INVALID_FILE; 1799 1800 fdpair[0] = globals->fd_clnt[i]; 1801 fdpair[1] = globals->fd_srv[i]; 1802 } 1803 1804 if (!strncmp(name, SELF_PREFIX, SELF_PREFIX_LEN)) 1805 { 1806 ns_self = _notify_lib_self_state(); 1807 if (ns_self == NULL) 1808 { 1809 if (mine == 1) 1810 { 1811 close(fdpair[0]); 1812 close(fdpair[1]); 1813 } 1814 1815 return NOTIFY_STATUS_FAILED; 1816 } 1817 1818 token = OSAtomicIncrement32((int32_t *)&globals->token_id); 1819 status = _notify_lib_register_file_descriptor(ns_self, name, NOTIFY_CLIENT_SELF, token, fdpair[1], 0, 0, &nid); 1820 if (status != NOTIFY_STATUS_OK) 1821 { 1822 if (mine == 1) 1823 { 1824 close(fdpair[0]); 1825 close(fdpair[1]); 1826 } 1827 1828 return status; 1829 } 1830 1831 cid = token; 1832 token_table_add(name, namelen, nid, token, cid, SLOT_NONE, NOTIFY_FLAG_SELF | NOTIFY_TYPE_FILE, SIGNAL_NONE, *notify_fd, MACH_PORT_NULL, 0); 1833 1834 *out_token = token; 1835 notify_retain_file_descriptor(fdpair[0], fdpair[1]); 1836 1837 return NOTIFY_STATUS_OK; 1838 } 1839 1840 if (globals->client_opts & NOTIFY_OPT_DEMUX) 1841 { 1842 /* 1843 * Use dispatch to do a write() on fdpair[1] when notified. 1844 */ 1845 status = notify_register_mux_fd(name, out_token, fdpair[0], fdpair[1]); 1846 if (status != NOTIFY_STATUS_OK) 1847 { 1848 if (mine == 1) 1849 { 1850 close(fdpair[0]); 1851 close(fdpair[1]); 1852 } 1853 1854 return status; 1855 } 1856 1857 notify_retain_file_descriptor(fdpair[0], fdpair[1]); 1858 return NOTIFY_STATUS_OK; 1859 } 1860 1861 if (globals->notify_server_port == MACH_PORT_NULL) 1862 { 1863 status = _notify_lib_init(EVENT_INIT); 1864 if (status != 0) 1865 { 1866 if (mine == 1) 1867 { 1868 close(fdpair[0]); 1869 close(fdpair[1]); 1870 } 1871 1872 return NOTIFY_STATUS_FAILED; 1873 } 1874 } 1875 1876 /* send fdpair[1] (the sender's fd) to notifyd using a fileport */ 1877 fileport = MACH_PORT_NULL; 1878 if (fileport_makeport(fdpair[1], (fileport_t *)&fileport) < 0) 1879 { 1880 if (mine == 1) 1881 { 1882 close(fdpair[0]); 1883 close(fdpair[1]); 1884 } 1885 1886 return NOTIFY_STATUS_FAILED; 1887 } 1888 1889 token = OSAtomicIncrement32((int32_t *)&globals->token_id); 1890 1891 if (globals->notify_ipc_version == 0) 1892 { 1893 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); 1894 if ((kstatus == KERN_SUCCESS) && (status != NOTIFY_STATUS_OK)) kstatus = KERN_FAILURE; 1895 } 1896 else 1897 { 1898 kstatus = _notify_server_register_file_descriptor_2(globals->notify_server_port, (caddr_t)name, namelen, token, (mach_port_t)fileport); 1899 } 1900 1901 if (kstatus != KERN_SUCCESS) 1902 { 1903 if (mine == 1) 1904 { 1905 close(fdpair[0]); 1906 close(fdpair[1]); 1907 } 1908 1909 return NOTIFY_STATUS_FAILED; 1910 } 1911 1912 token_table_add(name, namelen, NID_UNSET, token, cid, SLOT_NONE, NOTIFY_TYPE_FILE, SIGNAL_NONE, *notify_fd, MACH_PORT_NULL, 0); 1913 1914 *out_token = token; 1915 notify_retain_file_descriptor(fdpair[0], fdpair[1]); 1916 1917 return NOTIFY_STATUS_OK; 1918} 1919 1920uint32_t 1921notify_check(int token, int *check) 1922{ 1923 kern_return_t kstatus; 1924 uint32_t status, val; 1925 token_table_node_t *t; 1926 uint32_t tid; 1927 notify_globals_t globals = _notify_globals(); 1928 1929 regenerate_check(); 1930 1931 pthread_mutex_lock(&globals->notify_lock); 1932 1933 t = token_table_find_no_lock(token); 1934 if (t == NULL) 1935 { 1936 pthread_mutex_unlock(&globals->notify_lock); 1937 return NOTIFY_STATUS_INVALID_TOKEN; 1938 } 1939 1940 if (t->flags & NOTIFY_FLAG_SELF) 1941 { 1942 /* _notify_lib_check returns NOTIFY_STATUS_FAILED if self_state is NULL */ 1943 status = _notify_lib_check(globals->self_state, NOTIFY_CLIENT_SELF, token, check); 1944 pthread_mutex_unlock(&globals->notify_lock); 1945 return status; 1946 } 1947 1948 if (t->flags & NOTIFY_TYPE_MEMORY) 1949 { 1950 if (globals->shm_base == NULL) 1951 { 1952 pthread_mutex_unlock(&globals->notify_lock); 1953 return NOTIFY_STATUS_FAILED; 1954 } 1955 1956 *check = 0; 1957 val = globals->shm_base[t->slot]; 1958 if (t->val != val) 1959 { 1960 *check = 1; 1961 t->val = val; 1962 } 1963 1964 pthread_mutex_unlock(&globals->notify_lock); 1965 return NOTIFY_STATUS_OK; 1966 } 1967 1968 tid = token; 1969 if (globals->notify_ipc_version == 0) tid = t->client_id; 1970 1971 pthread_mutex_unlock(&globals->notify_lock); 1972 1973 if (globals->notify_server_port == MACH_PORT_NULL) 1974 { 1975 status = _notify_lib_init(EVENT_INIT); 1976 if (status != 0) return NOTIFY_STATUS_FAILED; 1977 } 1978 1979 kstatus = _notify_server_check(globals->notify_server_port, tid, check, (int32_t *)&status); 1980 1981 if (kstatus != KERN_SUCCESS) return NOTIFY_STATUS_FAILED; 1982 return status; 1983} 1984 1985uint32_t 1986notify_peek(int token, uint32_t *val) 1987{ 1988 token_table_node_t *t; 1989 uint32_t status; 1990 notify_globals_t globals = _notify_globals(); 1991 1992 regenerate_check(); 1993 1994 t = token_table_find_retain(token); 1995 if (t == NULL) return NOTIFY_STATUS_INVALID_TOKEN; 1996 1997 if (t->flags & NOTIFY_FLAG_SELF) 1998 { 1999 /* _notify_lib_peek returns NOTIFY_STATUS_FAILED if self_state is NULL */ 2000 status = _notify_lib_peek(globals->self_state, NOTIFY_CLIENT_SELF, token, (int *)val); 2001 token_table_release(t); 2002 return status; 2003 } 2004 2005 if (t->flags & NOTIFY_TYPE_MEMORY) 2006 { 2007 if (globals->shm_base == NULL) 2008 { 2009 token_table_release(t); 2010 return NOTIFY_STATUS_FAILED; 2011 } 2012 2013 *val = globals->shm_base[t->slot]; 2014 token_table_release(t); 2015 return NOTIFY_STATUS_OK; 2016 } 2017 2018 token_table_release(t); 2019 return NOTIFY_STATUS_INVALID_REQUEST; 2020} 2021 2022int * 2023notify_check_addr(int token) 2024{ 2025 token_table_node_t *t; 2026 uint32_t slot; 2027 int *val; 2028 notify_globals_t globals = _notify_globals(); 2029 2030 regenerate_check(); 2031 2032 t = token_table_find_retain(token); 2033 if (t == NULL) return NULL; 2034 2035 if (t->flags & NOTIFY_FLAG_SELF) 2036 { 2037 /* _notify_lib_check_addr returns NOTIFY_STATUS_FAILED if self_state is NULL */ 2038 val = _notify_lib_check_addr(globals->self_state, NOTIFY_CLIENT_SELF, token); 2039 token_table_release(t); 2040 return val; 2041 } 2042 2043 if (t->flags & NOTIFY_TYPE_MEMORY) 2044 { 2045 slot = t->slot; 2046 token_table_release(t); 2047 2048 if (globals->shm_base == NULL) return NULL; 2049 return (int *)&(globals->shm_base[slot]); 2050 } 2051 2052 token_table_release(t); 2053 return NULL; 2054} 2055 2056uint32_t 2057notify_monitor_file(int token, char *path, int flags) 2058{ 2059 kern_return_t kstatus; 2060 uint32_t status, len; 2061 token_table_node_t *t; 2062 char *dup; 2063 notify_globals_t globals = _notify_globals(); 2064 2065 regenerate_check(); 2066 2067 if (path == NULL) return NOTIFY_STATUS_INVALID_REQUEST; 2068 2069 t = token_table_find_retain(token); 2070 if (t == NULL) return NOTIFY_STATUS_INVALID_TOKEN; 2071 2072 if (t->flags & NOTIFY_FLAG_SELF) 2073 { 2074 token_table_release(t); 2075 return NOTIFY_STATUS_INVALID_REQUEST; 2076 } 2077 2078 /* can only monitor one path with a token */ 2079 if (t->path != NULL) 2080 { 2081 token_table_release(t); 2082 return NOTIFY_STATUS_INVALID_REQUEST; 2083 } 2084 2085 if (globals->notify_server_port == MACH_PORT_NULL) 2086 { 2087 status = _notify_lib_init(EVENT_INIT); 2088 if (status != 0) 2089 { 2090 token_table_release(t); 2091 return NOTIFY_STATUS_FAILED; 2092 } 2093 } 2094 2095 len = strlen(path); 2096 dup = strdup(path); 2097 if (dup == NULL) return NOTIFY_STATUS_FAILED; 2098 2099 if (globals->notify_ipc_version == 0) 2100 { 2101 kstatus = _notify_server_monitor_file(globals->notify_server_port, t->client_id, path, len, flags, (int32_t *)&status); 2102 if ((kstatus == KERN_SUCCESS) && (status != NOTIFY_STATUS_OK)) kstatus = KERN_FAILURE; 2103 } 2104 else 2105 { 2106 kstatus = _notify_server_monitor_file_2(globals->notify_server_port, token, path, len, flags); 2107 } 2108 2109 t->path = dup; 2110 t->path_flags = flags; 2111 2112 token_table_release(t); 2113 if (kstatus != KERN_SUCCESS) return NOTIFY_STATUS_FAILED; 2114 return NOTIFY_STATUS_OK; 2115} 2116 2117uint32_t 2118notify_get_event(int token, int *ev, char *buf, int *len) 2119{ 2120 if (ev != NULL) *ev = 0; 2121 if (len != NULL) *len = 0; 2122 2123 return NOTIFY_STATUS_OK; 2124} 2125 2126uint32_t 2127notify_get_state(int token, uint64_t *state) 2128{ 2129 kern_return_t kstatus; 2130 uint32_t status; 2131 token_table_node_t *t; 2132 uint64_t nid; 2133 notify_globals_t globals = _notify_globals(); 2134 2135 regenerate_check(); 2136 2137 t = token_table_find_retain(token); 2138 if (t == NULL) return NOTIFY_STATUS_INVALID_TOKEN; 2139 if (t->name_node == NULL) 2140 { 2141 token_table_release(t); 2142 return NOTIFY_STATUS_INVALID_TOKEN; 2143 } 2144 2145 if (t->flags & NOTIFY_FLAG_SELF) 2146 { 2147 /* _notify_lib_get_state returns NOTIFY_STATUS_FAILED if self_state is NULL */ 2148 status = _notify_lib_get_state(globals->self_state, t->name_node->name_id, state, 0, 0); 2149 token_table_release(t); 2150 return status; 2151 } 2152 2153 if (globals->notify_server_port == MACH_PORT_NULL) 2154 { 2155 status = _notify_lib_init(EVENT_INIT); 2156 if (status != 0) 2157 { 2158 token_table_release(t); 2159 return NOTIFY_STATUS_FAILED; 2160 } 2161 } 2162 2163 if (globals->notify_ipc_version == 0) 2164 { 2165 kstatus = _notify_server_get_state(globals->notify_server_port, t->client_id, state, (int32_t *)&status); 2166 if ((kstatus == KERN_SUCCESS) && (status != NOTIFY_STATUS_OK)) kstatus = KERN_FAILURE; 2167 } 2168 else 2169 { 2170 if (t->name_node->name_id >= NID_CALLED_ONCE) 2171 { 2172 kstatus = _notify_server_get_state_3(globals->notify_server_port, t->token, state, (uint64_t *)&nid, (int32_t *)&status); 2173 if ((kstatus == KERN_SUCCESS) && (status == NOTIFY_STATUS_OK)) name_table_set_nid(t->name, nid); 2174 } 2175 else 2176 { 2177 kstatus = _notify_server_get_state_2(globals->notify_server_port, t->name_node->name_id, state, (int32_t *)&status); 2178 } 2179 } 2180 2181 token_table_release(t); 2182 if (kstatus != KERN_SUCCESS) return NOTIFY_STATUS_FAILED; 2183 return status; 2184} 2185 2186uint32_t 2187notify_set_state(int token, uint64_t state) 2188{ 2189 kern_return_t kstatus; 2190 uint32_t status; 2191 token_table_node_t *t; 2192 uint64_t nid; 2193 notify_globals_t globals = _notify_globals(); 2194 2195 regenerate_check(); 2196 2197 t = token_table_find_retain(token); 2198 if (t == NULL) return NOTIFY_STATUS_INVALID_TOKEN; 2199 if (t->name_node == NULL) 2200 { 2201 token_table_release(t); 2202 return NOTIFY_STATUS_INVALID_TOKEN; 2203 } 2204 2205 if (t->flags & NOTIFY_FLAG_SELF) 2206 { 2207 /* _notify_lib_set_state returns NOTIFY_STATUS_FAILED if self_state is NULL */ 2208 status = _notify_lib_set_state(globals->self_state, t->name_node->name_id, state, 0, 0); 2209 token_table_release(t); 2210 return status; 2211 } 2212 2213 if (globals->notify_server_port == MACH_PORT_NULL) 2214 { 2215 status = _notify_lib_init(EVENT_INIT); 2216 if (status != 0) 2217 { 2218 token_table_release(t); 2219 return NOTIFY_STATUS_FAILED; 2220 } 2221 } 2222 2223 status = NOTIFY_STATUS_OK; 2224 2225 if (globals->notify_ipc_version == 0) 2226 { 2227 kstatus = _notify_server_set_state(globals->notify_server_port, t->client_id, state, (int32_t *)&status); 2228 if ((kstatus == KERN_SUCCESS) && (status != NOTIFY_STATUS_OK)) kstatus = KERN_FAILURE; 2229 } 2230 else 2231 { 2232 if (t->name_node->name_id >= NID_CALLED_ONCE) 2233 { 2234 kstatus = _notify_server_set_state_3(globals->notify_server_port, t->token, state, (uint64_t *)&nid, (int32_t *)&status); 2235 if ((kstatus == KERN_SUCCESS) && (status == NOTIFY_STATUS_OK)) name_table_set_nid(t->name, nid); 2236 } 2237 else 2238 { 2239 status = NOTIFY_STATUS_OK; 2240 kstatus = _notify_server_set_state_2(globals->notify_server_port, t->name_node->name_id, state); 2241 } 2242 } 2243 2244 if ((kstatus == KERN_SUCCESS) && (status == NOTIFY_STATUS_OK)) 2245 { 2246 t->set_state_time = mach_absolute_time(); 2247 t->set_state_val = state; 2248 } 2249 2250 token_table_release(t); 2251 if (kstatus != KERN_SUCCESS) return NOTIFY_STATUS_FAILED; 2252 return NOTIFY_STATUS_OK; 2253} 2254 2255uint32_t 2256notify_cancel(int token) 2257{ 2258 token_table_node_t *t; 2259 uint32_t status; 2260 kern_return_t kstatus; 2261 notify_globals_t globals = _notify_globals(); 2262 2263 regenerate_check(); 2264 2265 /* 2266 * Lock to prevent a race with notify_post, which uses the name ID. 2267 * If we are cancelling the last registration for this name, then we need 2268 * to block those routines from getting the name ID from the name table. 2269 * Once notifyd gets the cancellation, the name may vanish, and the name ID 2270 * held in the name table would go stale. 2271 * 2272 * Uses token_table_find_no_lock() which does not retain, and 2273 * token_table_release_no_lock() which releases the token. 2274 */ 2275 pthread_mutex_lock(&globals->notify_lock); 2276 2277 t = token_table_find_no_lock(token); 2278 if (t == NULL) 2279 { 2280 pthread_mutex_unlock(&globals->notify_lock); 2281 return NOTIFY_STATUS_INVALID_TOKEN; 2282 } 2283 2284 if (t->flags & NOTIFY_FLAG_SELF) 2285 { 2286 /* 2287 * _notify_lib_cancel returns NOTIFY_STATUS_FAILED if self_state is NULL 2288 * We let it fail quietly. 2289 */ 2290 _notify_lib_cancel(globals->self_state, NOTIFY_CLIENT_SELF, t->token); 2291 2292 token_table_release_no_lock(t); 2293 pthread_mutex_unlock(&globals->notify_lock); 2294 return NOTIFY_STATUS_OK; 2295 } 2296 2297 if (globals->notify_ipc_version == 0) 2298 { 2299 kstatus = _notify_server_cancel(globals->notify_server_port, t->client_id, (int32_t *)&status); 2300 if ((kstatus == KERN_SUCCESS) && (status != NOTIFY_STATUS_OK)) kstatus = KERN_FAILURE; 2301 } 2302 else 2303 { 2304 kstatus = _notify_server_cancel_2(globals->notify_server_port, token); 2305 } 2306 2307 token_table_release_no_lock(t); 2308 pthread_mutex_unlock(&globals->notify_lock); 2309 2310 if ((kstatus == MIG_SERVER_DIED) || (kstatus == MACH_SEND_INVALID_DEST)) return NOTIFY_STATUS_OK; 2311 else if (kstatus != KERN_SUCCESS) return NOTIFY_STATUS_FAILED; 2312 2313 return NOTIFY_STATUS_OK; 2314} 2315 2316bool 2317notify_is_valid_token(int val) 2318{ 2319 token_table_node_t *t; 2320 bool valid = false; 2321 2322 if (val < 0) return false; 2323 2324 notify_globals_t globals = _notify_globals(); 2325 2326 pthread_mutex_lock(&globals->notify_lock); 2327 2328 t = (token_table_node_t *)_nc_table_find_n(globals->token_table, val); 2329 if (t != NULL) valid = true; 2330 2331 pthread_mutex_unlock(&globals->notify_lock); 2332 2333 return valid; 2334} 2335 2336uint32_t 2337notify_suspend(int token) 2338{ 2339 token_table_node_t *t; 2340 uint32_t status, tid; 2341 kern_return_t kstatus; 2342 notify_globals_t globals = _notify_globals(); 2343 2344 regenerate_check(); 2345 2346 t = token_table_find_retain(token); 2347 if (t == NULL) return NOTIFY_STATUS_INVALID_TOKEN; 2348 2349 if (t->flags & NOTIFY_FLAG_SELF) 2350 { 2351 _notify_lib_suspend(globals->self_state, NOTIFY_CLIENT_SELF, t->token); 2352 token_table_release(t); 2353 return NOTIFY_STATUS_OK; 2354 } 2355 2356 if (globals->notify_server_port == MACH_PORT_NULL) 2357 { 2358 status = _notify_lib_init(EVENT_INIT); 2359 if (status != 0) 2360 { 2361 token_table_release(t); 2362 return NOTIFY_STATUS_FAILED; 2363 } 2364 } 2365 2366 tid = token; 2367 if (globals->notify_ipc_version == 0) tid = t->client_id; 2368 2369 kstatus = _notify_server_suspend(globals->notify_server_port, tid, (int32_t *)&status); 2370 2371 token_table_release(t); 2372 if (kstatus != KERN_SUCCESS) status = NOTIFY_STATUS_FAILED; 2373 return status; 2374} 2375 2376uint32_t 2377notify_resume(int token) 2378{ 2379 token_table_node_t *t; 2380 uint32_t status, tid; 2381 kern_return_t kstatus; 2382 notify_globals_t globals = _notify_globals(); 2383 2384 regenerate_check(); 2385 2386 t = token_table_find_retain(token); 2387 if (t == NULL) return NOTIFY_STATUS_INVALID_TOKEN; 2388 2389 if (t->flags & NOTIFY_FLAG_SELF) 2390 { 2391 _notify_lib_resume(globals->self_state, NOTIFY_CLIENT_SELF, t->token); 2392 token_table_release(t); 2393 return NOTIFY_STATUS_OK; 2394 } 2395 2396 if (globals->notify_server_port == MACH_PORT_NULL) 2397 { 2398 status = _notify_lib_init(EVENT_INIT); 2399 if (status != 0) 2400 { 2401 token_table_release(t); 2402 return NOTIFY_STATUS_FAILED; 2403 } 2404 } 2405 2406 tid = token; 2407 if (globals->notify_ipc_version == 0) tid = t->client_id; 2408 2409 kstatus = _notify_server_resume(globals->notify_server_port, tid, (int32_t *)&status); 2410 2411 token_table_release(t); 2412 if (kstatus != KERN_SUCCESS) status = NOTIFY_STATUS_FAILED; 2413 return status; 2414} 2415 2416uint32_t 2417notify_suspend_pid(pid_t pid) 2418{ 2419 uint32_t status; 2420 kern_return_t kstatus; 2421 notify_globals_t globals = _notify_globals(); 2422 2423 if (globals->notify_server_port == MACH_PORT_NULL) 2424 { 2425 status = _notify_lib_init(EVENT_INIT); 2426 if (status != 0) 2427 { 2428 return NOTIFY_STATUS_FAILED; 2429 } 2430 } 2431 2432 kstatus = _notify_server_suspend_pid(globals->notify_server_port, pid, (int32_t *)&status); 2433 2434 if (kstatus != KERN_SUCCESS) status = NOTIFY_STATUS_FAILED; 2435 return status; 2436} 2437 2438uint32_t 2439notify_resume_pid(pid_t pid) 2440{ 2441 uint32_t status; 2442 kern_return_t kstatus; 2443 notify_globals_t globals = _notify_globals(); 2444 2445 if (globals->notify_server_port == MACH_PORT_NULL) 2446 { 2447 status = _notify_lib_init(EVENT_INIT); 2448 if (status != 0) 2449 { 2450 return NOTIFY_STATUS_FAILED; 2451 } 2452 } 2453 2454 kstatus = _notify_server_resume_pid(globals->notify_server_port, pid, (int32_t *)&status); 2455 2456 if (kstatus != KERN_SUCCESS) status = NOTIFY_STATUS_FAILED; 2457 return status; 2458} 2459 2460/* Deprecated SPI */ 2461uint32_t 2462notify_simple_post(const char *name) 2463{ 2464 return notify_post(name); 2465} 2466