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