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