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