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