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