1/*
2 * Copyright (c) 2003-2012 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24#include <sys/types.h>
25#include <stdio.h>
26#include <fcntl.h>
27#include <stdlib.h>
28#include <unistd.h>
29#include <sys/socket.h>
30#include <netinet/in.h>
31#include <sys/un.h>
32#include <sys/ipc.h>
33#include <signal.h>
34#include <mach/mach.h>
35#include <mach/mach_time.h>
36#include <errno.h>
37#include <pthread.h>
38
39#include "libnotify.h"
40#include "notify.h"
41#include "notify_internal.h"
42
43#define USER_PROTECTED_UID_PREFIX "user.uid."
44#define USER_PROTECTED_UID_PREFIX_LEN 9
45
46uint64_t
47make_client_id(pid_t pid, int token)
48{
49	uint64_t cid;
50
51	cid = pid;
52	cid <<= 32;
53	cid |= token;
54
55	return cid;
56}
57
58notify_state_t *
59_notify_lib_notify_state_new(uint32_t flags, uint32_t table_size)
60{
61	notify_state_t *ns;
62
63	ns = (notify_state_t *)calloc(1, sizeof(notify_state_t));
64	if (ns == NULL) return NULL;
65
66	ns->flags = flags;
67	ns->sock = -1;
68
69	if (ns->flags & NOTIFY_STATE_USE_LOCKS)
70	{
71		ns->lock = (pthread_mutex_t *)malloc(sizeof(pthread_mutex_t));
72		if (ns->lock == NULL)
73		{
74			free(ns);
75			return NULL;
76		}
77
78		pthread_mutex_init(ns->lock, NULL);
79	}
80
81	ns->name_table = _nc_table_new(table_size);
82	ns->name_id_table = _nc_table_new(table_size);
83	ns->client_table = _nc_table_new(table_size);
84	ns->port_table = _nc_table_new(table_size);
85	ns->proc_table = _nc_table_new(table_size);
86
87	if ((ns->name_table == NULL) || (ns->name_id_table == NULL) || (ns->client_table == NULL) || (ns->port_table == NULL) || (ns->proc_table == NULL))
88	{
89		free(ns->lock);
90		_nc_table_free(ns->name_table);
91		_nc_table_free(ns->name_id_table);
92		_nc_table_free(ns->client_table);
93		_nc_table_free(ns->port_table);
94		_nc_table_free(ns->proc_table);
95		free(ns);
96		return NULL;
97	}
98
99	return ns;
100}
101
102void
103_notify_lib_notify_state_free(notify_state_t *ns)
104{
105	if (ns == NULL) return;
106
107	_nc_table_free(ns->name_table);
108	_nc_table_free(ns->name_id_table);
109	_nc_table_free(ns->client_table);
110	_nc_table_free(ns->port_table);
111	_nc_table_free(ns->proc_table);
112
113	if (ns->lock != NULL)
114	{
115		pthread_mutex_destroy(ns->lock);
116		free(ns->lock);
117	}
118
119	if (ns->sock != -1)
120	{
121		shutdown(ns->sock, 2);
122		close(ns->sock);
123	}
124
125	if (ns->controlled_name != NULL) free(ns->controlled_name);
126}
127
128static client_t *
129_internal_client_new(notify_state_t *ns, pid_t pid, int token)
130{
131	client_t *c;
132	uint64_t cid = make_client_id(pid, token);
133
134	if (ns == NULL) return NULL;
135
136	/* detect duplicates - should never happen, but it would be bad */
137	c = _nc_table_find_64(ns->client_table, cid);
138	if (c != NULL) return NULL;
139
140	c = calloc(1, sizeof(client_t));
141	if (c == NULL) return NULL;
142
143	ns->stat_client_alloc++;
144
145	c->client_id = cid;
146	c->pid = pid;
147	c->send_val = token;
148
149	_nc_table_insert_64(ns->client_table, cid, c);
150
151	return c;
152}
153
154static void
155_internal_client_release(notify_state_t *ns, client_t *c)
156{
157	uint64_t cid;
158
159	if (ns == NULL) return;
160	if (c == NULL) return;
161
162	cid = c->client_id;
163	_nc_table_delete_64(ns->client_table, cid);
164
165	switch (c->notify_type)
166	{
167		case NOTIFY_TYPE_SIGNAL:
168		{
169			break;
170		}
171		case NOTIFY_TYPE_FILE:
172		{
173			if (c->fd > 0) close(c->fd);
174			c->fd = -1;
175			break;
176		}
177		case NOTIFY_TYPE_PORT:
178		{
179			if (c->port != MACH_PORT_NULL)
180			{
181				/* release my send right to the port */
182				mach_port_deallocate(mach_task_self(), c->port);
183			}
184			break;
185		}
186		default:
187		{
188			break;
189		}
190	}
191
192	free(c);
193	ns->stat_client_free++;
194}
195
196static name_info_t *
197_internal_new_name(notify_state_t *ns, const char *name)
198{
199	name_info_t *n;
200	size_t namelen;
201
202	if (ns == NULL) return NULL;
203	if (name == NULL) return NULL;
204
205	namelen = strlen(name) + 1;
206
207	n = (name_info_t *)calloc(1, sizeof(name_info_t) + namelen);
208	if (n == NULL) return NULL;
209
210	ns->stat_name_alloc++;
211
212	n->name = (char *)n + sizeof(name_info_t);
213	memcpy(n->name, name, namelen);
214
215	notify_globals_t globals = _notify_globals();
216	n->name_id = globals->name_id++;
217
218	n->access = NOTIFY_ACCESS_DEFAULT;
219	n->slot = (uint32_t)-1;
220	n->val = 1;
221
222	_nc_table_insert_no_copy(ns->name_table, n->name, n);
223	_nc_table_insert_64(ns->name_id_table, n->name_id, n);
224
225	return n;
226}
227
228static void
229_internal_insert_controlled_name(notify_state_t *ns, name_info_t *n)
230{
231	int i, j;
232
233	if (ns == NULL) return;
234	if (n == NULL) return;
235
236	if (ns->controlled_name == NULL) ns->controlled_name_count = 0;
237
238	for (i = 0; i < ns->controlled_name_count; i++)
239	{
240		if (ns->controlled_name[i] == n) return;
241	}
242
243	ns->controlled_name = (name_info_t **)reallocf(ns->controlled_name, (ns->controlled_name_count + 1) * sizeof(name_info_t *));
244
245	/*
246	 * Insert name in reverse sorted order (longer names preceed shorter names).
247	 * this means that in _internal_check_access, we check subspaces from the bottom up
248	 * i.e. we check access for the "deepest" controlled subspace.
249	 */
250
251	for (i = 0; i < ns->controlled_name_count; i++)
252	{
253		if (strcmp(n->name, ns->controlled_name[i]->name) > 0) break;
254	}
255
256	for (j = ns->controlled_name_count; j > i; j--)
257	{
258		ns->controlled_name[j] = ns->controlled_name[j-1];
259	}
260
261	ns->controlled_name[i] = n;
262	ns->controlled_name_count++;
263}
264
265static void
266_internal_remove_controlled_name(notify_state_t *ns, name_info_t *n)
267{
268	uint32_t i, j;
269
270	for (i = 0; i < ns->controlled_name_count; i++)
271	{
272		if (ns->controlled_name[i] == n)
273		{
274			for (j = i + 1; j < ns->controlled_name_count; j++)
275			{
276				ns->controlled_name[j-1] = ns->controlled_name[j];
277			}
278
279			ns->controlled_name_count--;
280			if (ns->controlled_name_count == 0)
281			{
282				free(ns->controlled_name);
283				ns->controlled_name = NULL;
284			}
285			else
286			{
287				ns->controlled_name = (name_info_t **)reallocf(ns->controlled_name, ns->controlled_name_count * sizeof(name_info_t *));
288			}
289
290			return;
291		}
292	}
293}
294
295static uint32_t
296_internal_check_access(notify_state_t *ns, const char *name, uid_t uid, gid_t gid, int req)
297{
298	uint32_t i, len, plen;
299	name_info_t *p;
300	char str[64];
301
302	if (ns == NULL) return NOTIFY_STATUS_FAILED;
303	if (name == NULL) return NOTIFY_STATUS_INVALID_NAME;
304
305	/* root may do anything */
306	if (uid == 0) return NOTIFY_STATUS_OK;
307
308	/* if name has "user.uid." as a prefix, it is a user-protected namespace */
309	if (!strncmp(name, USER_PROTECTED_UID_PREFIX, USER_PROTECTED_UID_PREFIX_LEN))
310    {
311        snprintf(str, sizeof(str) - 1, "%s%d", USER_PROTECTED_UID_PREFIX, uid);
312        len = strlen(str);
313
314        /* user <uid> may access user.uid.<uid> or a subtree name */
315        if ((!strncmp(name, str, len)) && ((name[len] == '\0') || (name[len] == '.'))) return NOTIFY_STATUS_OK;
316        return NOTIFY_STATUS_NOT_AUTHORIZED;
317    }
318
319    len = strlen(name);
320
321	if (ns->controlled_name == NULL) ns->controlled_name_count = 0;
322	for (i = 0; i < ns->controlled_name_count; i++)
323	{
324		p = ns->controlled_name[i];
325		if (p == NULL) break;
326		if (p->name == NULL) continue;
327
328		plen = strlen(p->name);
329		if (plen > len) continue;
330		if (strncmp(p->name, name, plen)) continue;
331
332		/* Found a match or a prefix, check if restrictions apply to this uid/gid */
333		if ((p->uid == uid) && (p->access & (req << NOTIFY_ACCESS_USER_SHIFT))) break;
334		if ((p->gid == gid) && (p->access & (req << NOTIFY_ACCESS_GROUP_SHIFT))) break;
335		if (p->access & (req << NOTIFY_ACCESS_OTHER_SHIFT)) break;
336
337		return NOTIFY_STATUS_NOT_AUTHORIZED;
338	}
339
340	return NOTIFY_STATUS_OK;
341}
342
343uint32_t
344_notify_lib_check_controlled_access(notify_state_t *ns, char *name, uid_t uid, gid_t gid, int req)
345{
346	uint32_t status;
347
348	if (ns == NULL) return NOTIFY_STATUS_FAILED;
349
350	if (ns->lock != NULL) pthread_mutex_lock(ns->lock);
351	status = _internal_check_access(ns, name, uid, gid, req);
352	if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
353
354	return status;
355}
356
357uint32_t
358_notify_lib_port_proc_new(notify_state_t *ns, mach_port_t port, pid_t proc, uint32_t state, dispatch_source_t src)
359{
360	portproc_data_t *pdata;
361
362	if (ns == NULL) return NOTIFY_STATUS_FAILED;
363	if ((proc == 0) && (port == MACH_PORT_NULL)) return NOTIFY_STATUS_FAILED;
364
365	pdata = (portproc_data_t *)calloc(1, sizeof(portproc_data_t));
366	if (pdata == NULL) return NOTIFY_STATUS_FAILED;
367
368	ns->stat_portproc_alloc++;
369
370	pdata->refcount = 1;
371	pdata->flags = state;
372	pdata->src = src;
373
374	if (ns->lock != NULL) pthread_mutex_lock(ns->lock);
375	if (proc == 0) _nc_table_insert_n(ns->port_table, port, pdata);
376	else _nc_table_insert_n(ns->proc_table, proc, pdata);
377	if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
378
379	return NOTIFY_STATUS_OK;
380}
381
382portproc_data_t *
383_notify_lib_port_proc_find(notify_state_t *ns, mach_port_t port, pid_t proc)
384{
385	portproc_data_t *pdata = NULL;
386
387	if (ns == NULL) return NULL;
388	if ((proc == 0) && (port == MACH_PORT_NULL)) return NULL;
389
390	if (ns->lock != NULL) pthread_mutex_lock(ns->lock);
391
392	if (proc == 0) pdata = _nc_table_find_n(ns->port_table, port);
393	else pdata = _nc_table_find_n(ns->proc_table, proc);
394
395	if (pdata != NULL) pdata->refcount++;
396
397	if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
398
399	return pdata;
400}
401
402void
403_notify_lib_port_proc_release(notify_state_t *ns, mach_port_t port, pid_t proc)
404{
405	portproc_data_t *pdata = NULL;
406
407	if (ns == NULL) return;
408	if ((proc == 0) && (port == MACH_PORT_NULL)) return;
409
410	if (ns->lock != NULL) pthread_mutex_lock(ns->lock);
411
412	if (proc == 0) pdata = _nc_table_find_n(ns->port_table, port);
413	else pdata = _nc_table_find_n(ns->proc_table, proc);
414
415	if (pdata != NULL)
416	{
417		if (pdata->refcount > 0) pdata->refcount--;
418		if (pdata->refcount == 0)
419		{
420			if (proc == 0) _nc_table_delete_n(ns->port_table, port);
421			else _nc_table_delete_n(ns->proc_table, proc);
422
423			dispatch_source_cancel(pdata->src);
424			dispatch_release(pdata->src);
425
426			free(pdata);
427			ns->stat_portproc_free++;
428		}
429	}
430
431	if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
432}
433
434/*
435 * Send notification to a subscriber
436 */
437static uint32_t
438_internal_send(notify_state_t *ns, client_t *c)
439{
440	uint32_t send;
441	portproc_data_t *pdata;
442
443	if (ns == NULL) return NOTIFY_STATUS_FAILED;
444	if (c == NULL) return NOTIFY_STATUS_FAILED;
445
446	if (c->state & NOTIFY_CLIENT_STATE_SUSPENDED)
447	{
448		c->state |= NOTIFY_CLIENT_STATE_PENDING;
449		return NOTIFY_STATUS_OK;
450	}
451
452	pdata = _nc_table_find_n(ns->proc_table, c->pid);
453	if ((pdata != NULL) && (pdata->flags & NOTIFY_PORT_PROC_STATE_SUSPENDED))
454	{
455		c->suspend_count++;
456		c->state |= NOTIFY_CLIENT_STATE_SUSPENDED;
457		c->state |= NOTIFY_CLIENT_STATE_PENDING;
458		return NOTIFY_STATUS_OK;
459	}
460
461	send = c->send_val;
462
463	switch (c->notify_type)
464	{
465		case NOTIFY_TYPE_SIGNAL:
466		{
467			int rc = 0;
468
469			if (c->pid == NOTIFY_CLIENT_SELF) rc = kill(getpid(), c->sig);
470			else rc = kill(c->pid, c->sig);
471
472			if (rc != 0) return NOTIFY_STATUS_FAILED;
473
474			c->state &= ~NOTIFY_CLIENT_STATE_PENDING;
475			c->state &= ~NOTIFY_CLIENT_STATE_TIMEOUT;
476
477			return NOTIFY_STATUS_OK;
478		}
479
480		case NOTIFY_TYPE_FILE:
481		{
482			ssize_t len;
483
484			if (c->fd >= 0)
485			{
486				send = htonl(send);
487				len = write(c->fd, &send, sizeof(uint32_t));
488				if (len != sizeof(uint32_t))
489				{
490					close(c->fd);
491					c->fd = -1;
492					return NOTIFY_STATUS_FAILED;
493				}
494			}
495
496			c->state &= ~NOTIFY_CLIENT_STATE_PENDING;
497			c->state &= ~NOTIFY_CLIENT_STATE_TIMEOUT;
498
499			return NOTIFY_STATUS_OK;
500		}
501
502		case NOTIFY_TYPE_PORT:
503		{
504			kern_return_t kstatus;
505			mach_msg_empty_send_t msg;
506			mach_msg_option_t opts = MACH_SEND_MSG | MACH_SEND_TIMEOUT;
507
508			pdata = _nc_table_find_n(ns->port_table, c->port);
509			if ((pdata != NULL) && (pdata->flags & NOTIFY_PORT_PROC_STATE_SUSPENDED))
510			{
511				c->suspend_count++;
512				c->state |= NOTIFY_CLIENT_STATE_SUSPENDED;
513				c->state |= NOTIFY_CLIENT_STATE_PENDING;
514				return NOTIFY_STATUS_OK;
515			}
516
517			if (ns->flags & NOTIFY_STATE_ENABLE_RESEND) opts |= MACH_SEND_NOTIFY;
518
519			memset(&msg, 0, sizeof(mach_msg_empty_send_t));
520			msg.header.msgh_size = sizeof(mach_msg_empty_send_t);
521			msg.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSGH_BITS_ZERO);
522			msg.header.msgh_local_port = MACH_PORT_NULL;
523			msg.header.msgh_remote_port = c->port;
524			msg.header.msgh_id = (mach_msg_id_t)send;
525
526			kstatus = mach_msg(&msg.header, opts, msg.header.msgh_size, 0, MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
527
528			if (kstatus == MACH_SEND_TIMED_OUT)
529			{
530				/* deallocate port rights obtained via pseudo-receive after failed mach_msg() send */
531				mach_msg_destroy(&msg.header);
532				if (ns->flags & NOTIFY_STATE_ENABLE_RESEND)
533				{
534					/*
535					 * Suspend on timeout.
536					 * notifyd will get a MACH_NOTIFY_SEND_POSSIBLE and trigger a retry.
537					 * c->suspend_count must be zero, or we would not be trying to send.
538					 */
539					c->suspend_count++;
540					c->state |= NOTIFY_CLIENT_STATE_SUSPENDED;
541					c->state |= NOTIFY_CLIENT_STATE_PENDING;
542					c->state |= NOTIFY_CLIENT_STATE_TIMEOUT;
543
544					return NOTIFY_STATUS_OK;
545				}
546
547				return NOTIFY_STATUS_FAILED;
548			}
549			else if (kstatus != KERN_SUCCESS) return NOTIFY_STATUS_FAILED;
550
551			c->state &= ~NOTIFY_CLIENT_STATE_PENDING;
552			c->state &= ~NOTIFY_CLIENT_STATE_TIMEOUT;
553
554			return NOTIFY_STATUS_OK;
555		}
556
557		default:
558		{
559			break;
560		}
561	}
562
563	c->state &= ~NOTIFY_CLIENT_STATE_PENDING;
564	c->state &= ~NOTIFY_CLIENT_STATE_TIMEOUT;
565
566	return NOTIFY_STATUS_OK;
567}
568
569uint32_t
570_notify_lib_post_client(notify_state_t *ns, client_t *c)
571{
572	uint32_t status;
573
574	if (ns == NULL) return NOTIFY_STATUS_FAILED;
575	if (c == NULL) return NOTIFY_STATUS_FAILED;
576
577	if (ns->lock != NULL) pthread_mutex_lock(ns->lock);
578	status = _internal_send(ns, c);
579	if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
580
581	return status;
582}
583
584static uint32_t
585_internal_post_name(notify_state_t *ns, name_info_t *n, uid_t uid, gid_t gid)
586{
587	int auth;
588	list_t *l;
589	client_t *c;
590
591	if (n == NULL) return NOTIFY_STATUS_INVALID_NAME;
592
593	auth = _internal_check_access(ns, n->name, uid, gid, NOTIFY_ACCESS_WRITE);
594	if (auth != 0) return NOTIFY_STATUS_NOT_AUTHORIZED;
595
596	n->val++;
597
598	for (l = n->subscriptions; l != NULL; l = _nc_list_next(l))
599	{
600		c = _nc_list_data(l);
601		if (c != NULL) _internal_send(ns, c);
602	}
603
604	return NOTIFY_STATUS_OK;
605}
606
607/*
608 * Notify subscribers of this name.
609 */
610uint32_t
611_notify_lib_post(notify_state_t *ns, const char *name, uid_t uid, gid_t gid)
612{
613	name_info_t *n;
614	uint32_t status;
615
616	if (ns == NULL) return NOTIFY_STATUS_FAILED;
617
618	if (ns->lock != NULL) pthread_mutex_lock(ns->lock);
619
620	n = (name_info_t *)_nc_table_find(ns->name_table, name);
621	if (n == NULL)
622	{
623		if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
624		return NOTIFY_STATUS_INVALID_NAME;
625	}
626
627	status = _internal_post_name(ns, n, uid, gid);
628
629	if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
630	return status;
631}
632
633uint32_t
634_notify_lib_post_nid(notify_state_t *ns, uint64_t nid, uid_t uid, gid_t gid)
635{
636	name_info_t *n;
637	uint32_t status;
638
639	if (ns == NULL) return NOTIFY_STATUS_FAILED;
640
641	if (ns->lock != NULL) pthread_mutex_lock(ns->lock);
642
643	n = (name_info_t *)_nc_table_find_64(ns->name_id_table, nid);
644	if (n == NULL)
645	{
646		if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
647		return NOTIFY_STATUS_INVALID_NAME;
648	}
649
650	status = _internal_post_name(ns, n, uid, gid);
651
652	if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
653	return status;
654}
655
656static void
657_internal_release_name_info(notify_state_t *ns, name_info_t *n)
658{
659	if (ns == NULL) return;
660	if (n == NULL) return;
661
662	if (n->refcount > 0) n->refcount--;
663	if (n->refcount == 0)
664	{
665		_internal_remove_controlled_name(ns, n);
666		_nc_table_delete(ns->name_table, n->name);
667		_nc_table_delete_64(ns->name_id_table, n->name_id);
668		_nc_list_release_list(n->subscriptions);
669		free(n);
670		ns->stat_name_free++;
671	}
672}
673
674/*
675 * Cancel (delete) a client
676 */
677static void
678_internal_cancel(notify_state_t *ns, uint64_t cid)
679{
680	client_t *c;
681	name_info_t *n;
682
683	if (ns == NULL) return;
684
685	c = NULL;
686	n = NULL;
687
688	c = _nc_table_find_64(ns->client_table, cid);
689	if (c == NULL) return;
690
691	n = c->name_info;
692	if (n == NULL) return;
693
694	n->subscriptions =_nc_list_find_release(n->subscriptions, c);
695	_internal_client_release(ns, c);
696	_internal_release_name_info(ns, n);
697}
698
699void
700_notify_lib_cancel(notify_state_t *ns, pid_t pid, int token)
701{
702	uint64_t cid;
703
704	if (ns == NULL) return;
705
706	cid = make_client_id(pid, token);
707
708	if (ns->lock != NULL) pthread_mutex_lock(ns->lock);
709	_internal_cancel(ns, cid);
710	if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
711}
712
713void
714_notify_lib_suspend(notify_state_t *ns, pid_t pid, int token)
715{
716	client_t *c;
717	uint64_t cid;
718
719	if (ns == NULL) return;
720
721	cid = make_client_id(pid, token);
722
723	if (ns->lock != NULL) pthread_mutex_lock(ns->lock);
724
725	c = _nc_table_find_64(ns->client_table, cid);
726	if (c != NULL)
727	{
728		c->state |= NOTIFY_CLIENT_STATE_SUSPENDED;
729		if (c->suspend_count < UINT32_MAX) c->suspend_count++;
730	}
731
732	if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
733}
734
735uint32_t
736_notify_lib_resume(notify_state_t *ns, pid_t pid, int token)
737{
738	client_t *c;
739	uint64_t cid;
740	uint32_t status = NOTIFY_STATUS_OK;
741
742	if (ns == NULL) return NOTIFY_STATUS_FAILED;
743
744	cid = make_client_id(pid, token);
745
746	if (ns->lock != NULL) pthread_mutex_lock(ns->lock);
747
748	c = _nc_table_find_64(ns->client_table, cid);
749	if (c != NULL)
750	{
751		if (c->suspend_count > 0) c->suspend_count--;
752		if (c->suspend_count == 0)
753		{
754			c->state &= ~NOTIFY_CLIENT_STATE_SUSPENDED;
755			c->state &= ~NOTIFY_CLIENT_STATE_TIMEOUT;
756
757			if (c->state & NOTIFY_CLIENT_STATE_PENDING)
758			{
759				status = _internal_send(ns, c);
760			}
761		}
762	}
763
764	if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
765
766	return status;
767}
768
769void
770_notify_lib_suspend_proc(notify_state_t *ns, pid_t pid)
771{
772	portproc_data_t *pdata;
773
774	if (ns == NULL) return;
775
776	if (ns->lock != NULL) pthread_mutex_lock(ns->lock);
777
778	pdata = _nc_table_find_n(ns->proc_table, pid);
779	if (pdata != NULL) pdata->flags |= NOTIFY_PORT_PROC_STATE_SUSPENDED;
780
781	if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
782}
783
784void
785_notify_lib_resume_proc(notify_state_t *ns, pid_t pid)
786{
787	client_t *c;
788	void *tt;
789	portproc_data_t *pdata;
790
791	if (ns == NULL) return;
792
793	if (ns->lock != NULL) pthread_mutex_lock(ns->lock);
794
795	/* Resume all subscriptions for this process */
796	pdata = _nc_table_find_n(ns->proc_table, pid);
797	if (pdata != NULL) pdata->flags &= ~NOTIFY_PORT_PROC_STATE_SUSPENDED;
798
799	tt = _nc_table_traverse_start(ns->client_table);
800	while (tt != NULL)
801	{
802		c = _nc_table_traverse(ns->client_table, tt);
803		if (c == NULL) break;
804
805		if (c->pid == pid)
806		{
807			if (c->suspend_count > 0) c->suspend_count--;
808			if (c->suspend_count == 0)
809			{
810				c->state &= ~NOTIFY_CLIENT_STATE_SUSPENDED;
811				c->state &= ~NOTIFY_CLIENT_STATE_TIMEOUT;
812
813				if (c->state & NOTIFY_CLIENT_STATE_PENDING)
814				{
815					_internal_send(ns, c);
816				}
817			}
818		}
819	}
820	_nc_table_traverse_end(ns->client_table, tt);
821
822	if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
823}
824
825void
826_notify_lib_suspend_port(notify_state_t *ns, mach_port_t port)
827{
828	portproc_data_t *pdata;
829
830	if (ns == NULL) return;
831
832	if (ns->lock != NULL) pthread_mutex_lock(ns->lock);
833
834	pdata = _nc_table_find_n(ns->port_table, port);
835	if (pdata != NULL) pdata->flags |= NOTIFY_PORT_PROC_STATE_SUSPENDED;
836
837	if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
838}
839
840void
841_notify_lib_resume_port(notify_state_t *ns, mach_port_t port)
842{
843	client_t *c;
844	void *tt;
845	portproc_data_t *pdata;
846
847	if (ns == NULL) return;
848
849	if (ns->lock != NULL) pthread_mutex_lock(ns->lock);
850
851	/* Resume all subscriptions with this port */
852	pdata = _nc_table_find_n(ns->port_table, port);
853	if (pdata != NULL) pdata->flags &= ~NOTIFY_PORT_PROC_STATE_SUSPENDED;
854
855	tt = _nc_table_traverse_start(ns->client_table);
856	while (tt != NULL)
857	{
858		c = _nc_table_traverse(ns->client_table, tt);
859		if (c == NULL) break;
860
861		if (c->port == port)
862		{
863			if (c->suspend_count > 0) c->suspend_count--;
864			if (c->suspend_count == 0)
865			{
866				c->state &= ~NOTIFY_CLIENT_STATE_SUSPENDED;
867				c->state &= ~NOTIFY_CLIENT_STATE_TIMEOUT;
868
869				if (c->state & NOTIFY_CLIENT_STATE_PENDING)
870				{
871					_internal_send(ns, c);
872				}
873			}
874		}
875	}
876	_nc_table_traverse_end(ns->client_table, tt);
877
878	if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
879}
880
881/*
882 * Delete all clients for a process
883 * N.B. notifyd does not use this routine.
884 */
885void
886_notify_lib_cancel_proc(notify_state_t *ns, pid_t pid)
887{
888	client_t *c;
889	void *tt;
890	list_t *l, *x;
891
892	if (ns == NULL) return;
893
894	x = NULL;
895
896	if (ns->lock != NULL) pthread_mutex_lock(ns->lock);
897
898	tt = _nc_table_traverse_start(ns->client_table);
899	while (tt != NULL)
900	{
901		c = _nc_table_traverse(ns->client_table, tt);
902		if (c == NULL) break;
903
904		if (c->pid == pid) x = _nc_list_prepend(x, _nc_list_new(c));
905	}
906	_nc_table_traverse_end(ns->client_table, tt);
907
908	for (l = x; l != NULL; l = _nc_list_next(l))
909	{
910		c = _nc_list_data(l);
911		_internal_cancel(ns, c->client_id);
912	}
913
914	_nc_list_release_list(x);
915
916	if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
917}
918
919/*
920 * Delete all clients for a port
921 * N.B. notifyd does not use this routine.
922 */
923void
924_notify_lib_cancel_port(notify_state_t *ns, mach_port_t port)
925{
926	client_t *c;
927	void *tt;
928	list_t *l, *x;
929
930	if (ns == NULL) return;
931
932	x = NULL;
933
934	if (ns->lock != NULL) pthread_mutex_lock(ns->lock);
935
936	tt = _nc_table_traverse_start(ns->client_table);
937	while (tt != NULL)
938	{
939		c = _nc_table_traverse(ns->client_table, tt);
940		if (c == NULL) break;
941
942		if (c->port == port) x = _nc_list_prepend(x, _nc_list_new(c));
943	}
944	_nc_table_traverse_end(ns->client_table, tt);
945
946	for (l = x; l != NULL; l = _nc_list_next(l))
947	{
948		c = _nc_list_data(l);
949		_internal_cancel(ns, c->client_id);
950	}
951
952	_nc_list_release_list(x);
953
954	if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
955}
956
957/*
958 * Check if a name has changed since the last time this client checked.
959 * Returns true, false, or error.
960 */
961uint32_t
962_notify_lib_check(notify_state_t *ns, pid_t pid, int token, int *check)
963{
964	client_t *c;
965	uint64_t cid;
966
967	if (ns == NULL) return NOTIFY_STATUS_FAILED;
968	if (check == NULL) return NOTIFY_STATUS_FAILED;
969
970	cid = make_client_id(pid, token);
971
972	if (ns->lock != NULL) pthread_mutex_lock(ns->lock);
973
974	c = _nc_table_find_64(ns->client_table, cid);
975
976	if (c == NULL)
977	{
978		if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
979		return NOTIFY_STATUS_INVALID_TOKEN;
980	}
981
982	if (c->name_info == NULL)
983	{
984		if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
985		return NOTIFY_STATUS_INVALID_TOKEN;
986	}
987
988	if (c->name_info->val == c->lastval)
989	{
990		*check = 0;
991		if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
992		return NOTIFY_STATUS_OK;
993	}
994
995	c->lastval = c->name_info->val;
996	*check = 1;
997
998	if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
999	return NOTIFY_STATUS_OK;
1000}
1001
1002/*
1003 * SPI: get value for a name.
1004 */
1005uint32_t
1006_notify_lib_peek(notify_state_t *ns, pid_t pid, int token, int *val)
1007{
1008	client_t *c;
1009	uint64_t cid;
1010
1011	if (ns == NULL) return NOTIFY_STATUS_FAILED;
1012	if (val == NULL) return NOTIFY_STATUS_FAILED;
1013
1014	cid = make_client_id(pid, token);
1015
1016	if (ns->lock != NULL) pthread_mutex_lock(ns->lock);
1017
1018	c = _nc_table_find_64(ns->client_table, cid);
1019
1020	if (c == NULL)
1021	{
1022		if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
1023		return NOTIFY_STATUS_INVALID_TOKEN;
1024	}
1025
1026	if (c->name_info == NULL)
1027	{
1028		if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
1029		return NOTIFY_STATUS_INVALID_TOKEN;
1030	}
1031
1032	*val = c->name_info->val;
1033
1034	if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
1035	return NOTIFY_STATUS_OK;
1036}
1037
1038int *
1039_notify_lib_check_addr(notify_state_t *ns, pid_t pid, int token)
1040{
1041	client_t *c;
1042	int *addr;
1043	uint64_t cid;
1044
1045	if (ns == NULL) return NULL;
1046
1047	cid = make_client_id(pid, token);
1048
1049	if (ns->lock != NULL) pthread_mutex_lock(ns->lock);
1050
1051	c = _nc_table_find_64(ns->client_table, cid);
1052
1053	if (c == NULL)
1054	{
1055		if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
1056		return NULL;
1057	}
1058
1059	if (c->name_info == NULL)
1060	{
1061		if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
1062		return NULL;
1063	}
1064
1065	addr = (int *)&(c->name_info->val);
1066
1067	if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
1068	return addr;
1069}
1070
1071/*
1072 * Get state value for a name.
1073 */
1074uint32_t
1075_notify_lib_get_state(notify_state_t *ns, uint64_t nid, uint64_t *state, uid_t uid, gid_t gid)
1076{
1077	name_info_t *n;
1078
1079	if (ns == NULL) return NOTIFY_STATUS_FAILED;
1080	if (state == NULL) return NOTIFY_STATUS_FAILED;
1081
1082	*state = 0;
1083
1084	if (ns->lock != NULL) pthread_mutex_lock(ns->lock);
1085
1086	n = (name_info_t *)_nc_table_find_64(ns->name_id_table, nid);
1087
1088	if (n == NULL)
1089	{
1090		if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
1091		return NOTIFY_STATUS_INVALID_NAME;
1092	}
1093
1094#ifdef GET_STATE_AUTH_CHECK
1095	int auth = _internal_check_access(ns, n->name, uid, gid, NOTIFY_ACCESS_READ);
1096	if (auth != 0)
1097	{
1098		if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
1099		return NOTIFY_STATUS_NOT_AUTHORIZED;
1100	}
1101#endif
1102
1103	*state = n->state;
1104
1105	if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
1106	return NOTIFY_STATUS_OK;
1107}
1108
1109/*
1110 * Set state value for a name.
1111 */
1112uint32_t
1113_notify_lib_set_state(notify_state_t *ns, uint64_t nid, uint64_t state, uid_t uid, gid_t gid)
1114{
1115	name_info_t *n;
1116	int auth;
1117
1118	if (ns == NULL) return NOTIFY_STATUS_FAILED;
1119
1120	if (ns->lock != NULL) pthread_mutex_lock(ns->lock);
1121
1122	n = (name_info_t *)_nc_table_find_64(ns->name_id_table, nid);
1123
1124	if (n == NULL)
1125	{
1126		if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
1127		return NOTIFY_STATUS_INVALID_NAME;
1128	}
1129
1130	auth = _internal_check_access(ns, n->name, uid, gid, NOTIFY_ACCESS_WRITE);
1131	if (auth != 0)
1132	{
1133		if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
1134		return NOTIFY_STATUS_NOT_AUTHORIZED;
1135	}
1136
1137	n->state = state;
1138	n->state_time = mach_absolute_time();
1139
1140	if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
1141	return NOTIFY_STATUS_OK;
1142}
1143
1144static uint32_t
1145_internal_register_common(notify_state_t *ns, const char *name, pid_t pid, int token, uid_t uid, gid_t gid, client_t **outc)
1146{
1147	client_t *c;
1148	name_info_t *n;
1149	int is_new_name;
1150	uint32_t status;
1151
1152	if (ns == NULL) return NOTIFY_STATUS_FAILED;
1153	if (name == NULL) return NOTIFY_STATUS_INVALID_NAME;
1154	if (outc == NULL) return NOTIFY_STATUS_OK;
1155
1156	status = _internal_check_access(ns, name, uid, gid, NOTIFY_ACCESS_READ);
1157	if (status != NOTIFY_STATUS_OK) return NOTIFY_STATUS_NOT_AUTHORIZED;
1158
1159	*outc = NULL;
1160	is_new_name = 0;
1161
1162	n = (name_info_t *)_nc_table_find(ns->name_table, name);
1163	if (n == NULL)
1164	{
1165		is_new_name = 1;
1166
1167		n = _internal_new_name(ns, name);
1168		if (n == NULL) return NOTIFY_STATUS_FAILED;
1169	}
1170
1171	c = _internal_client_new(ns, pid, token);
1172	if (c == NULL)
1173	{
1174		if (is_new_name == 1)
1175		{
1176			_nc_table_delete(ns->name_table, n->name);
1177			_nc_list_release_list(n->subscriptions);
1178			free(n);
1179			ns->stat_name_free++;
1180		}
1181
1182		return NOTIFY_STATUS_FAILED;
1183	}
1184
1185	n->refcount++;
1186
1187	c->name_info = n;
1188	n->subscriptions = _nc_list_prepend(n->subscriptions, _nc_list_new(c));
1189
1190	*outc = c;
1191
1192	return NOTIFY_STATUS_OK;
1193}
1194
1195/*
1196 * Register for signal.
1197 * Returns the client_id;
1198 */
1199uint32_t
1200_notify_lib_register_signal(notify_state_t *ns, const char *name, pid_t pid, int token, uint32_t sig, uid_t uid, gid_t gid, uint64_t *out_nid)
1201{
1202	client_t *c;
1203	uint32_t status;
1204
1205	if (ns == NULL) return NOTIFY_STATUS_FAILED;
1206	if (name == NULL) return NOTIFY_STATUS_INVALID_NAME;
1207
1208	c = NULL;
1209
1210	if (ns->lock != NULL) pthread_mutex_lock(ns->lock);
1211
1212	status = _internal_register_common(ns, name, pid, token, uid, gid, &c);
1213	if (status != NOTIFY_STATUS_OK)
1214	{
1215		if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
1216		return status;
1217	}
1218
1219	c->notify_type = NOTIFY_TYPE_SIGNAL;
1220	c->pid = pid;
1221	c->sig = sig;
1222	*out_nid = c->name_info->name_id;
1223
1224	if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
1225	return NOTIFY_STATUS_OK;
1226}
1227
1228/*
1229 * Register for notification on a file descriptor.
1230 * Returns the client_id;
1231 */
1232uint32_t
1233_notify_lib_register_file_descriptor(notify_state_t *ns, const char *name, pid_t pid, int token, int fd, uid_t uid, gid_t gid, uint64_t *out_nid)
1234{
1235	client_t *c;
1236	uint32_t status;
1237
1238	if (ns == NULL) return NOTIFY_STATUS_FAILED;
1239	if (name == NULL) return NOTIFY_STATUS_INVALID_NAME;
1240
1241	c = NULL;
1242
1243	if (ns->lock != NULL) pthread_mutex_lock(ns->lock);
1244
1245	status = _internal_register_common(ns, name, pid, token, uid, gid, &c);
1246	if (status != NOTIFY_STATUS_OK)
1247	{
1248		if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
1249		return status;
1250	}
1251
1252	c->notify_type = NOTIFY_TYPE_FILE;
1253	c->fd = fd;
1254	*out_nid = c->name_info->name_id;
1255
1256	if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
1257	return NOTIFY_STATUS_OK;
1258}
1259
1260/*
1261 * Register for notification on a mach port.
1262 * Returns the client_id;
1263 */
1264uint32_t
1265_notify_lib_register_mach_port(notify_state_t *ns, const char *name, pid_t pid, int token, mach_port_t port, uid_t uid, gid_t gid, uint64_t *out_nid)
1266{
1267	client_t *c;
1268	uint32_t status;
1269
1270	if (ns == NULL) return NOTIFY_STATUS_FAILED;
1271	if (name == NULL) return NOTIFY_STATUS_INVALID_NAME;
1272
1273	c = NULL;
1274
1275	if (ns->lock != NULL) pthread_mutex_lock(ns->lock);
1276
1277	status = _internal_register_common(ns, name, pid, token, uid, gid, &c);
1278	if (status != NOTIFY_STATUS_OK)
1279	{
1280		if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
1281		return status;
1282	}
1283
1284	c->notify_type = NOTIFY_TYPE_PORT;
1285	c->port = port;
1286	*out_nid = c->name_info->name_id;
1287
1288	if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
1289	return NOTIFY_STATUS_OK;
1290}
1291
1292/*
1293 * Plain registration - only for notify_check()
1294 * Returns the client_id.
1295 */
1296uint32_t
1297_notify_lib_register_plain(notify_state_t *ns, const char *name, pid_t pid, int token, uint32_t slot, uint32_t uid, uint32_t gid, uint64_t *out_nid)
1298{
1299	client_t *c;
1300	uint32_t status;
1301
1302	if (ns == NULL) return NOTIFY_STATUS_FAILED;
1303	if (name == NULL) return NOTIFY_STATUS_INVALID_NAME;
1304
1305	c = NULL;
1306
1307	if (ns->lock != NULL) pthread_mutex_lock(ns->lock);
1308
1309	status = _internal_register_common(ns, name, pid, token, uid, gid, &c);
1310	if (status != NOTIFY_STATUS_OK)
1311	{
1312		if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
1313		return status;
1314	}
1315
1316	if (slot == SLOT_NONE)
1317	{
1318		c->notify_type = NOTIFY_TYPE_PLAIN;
1319	}
1320	else
1321	{
1322		c->notify_type = NOTIFY_TYPE_MEMORY;
1323		c->name_info->slot = slot;
1324	}
1325
1326	*out_nid = c->name_info->name_id;
1327
1328	if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
1329	return NOTIFY_STATUS_OK;
1330}
1331
1332uint32_t
1333_notify_lib_set_owner(notify_state_t *ns, const char *name, uid_t uid, gid_t gid)
1334{
1335	name_info_t *n;
1336
1337	if (ns == NULL) return NOTIFY_STATUS_FAILED;
1338	if (name == NULL) return NOTIFY_STATUS_INVALID_NAME;
1339
1340	if (ns->lock != NULL) pthread_mutex_lock(ns->lock);
1341
1342	n = (name_info_t *)_nc_table_find(ns->name_table, name);
1343	if (n == NULL)
1344	{
1345		/* create new name */
1346		n = _internal_new_name(ns, name);
1347		if (n == NULL)
1348		{
1349			if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
1350			return NOTIFY_STATUS_FAILED;
1351		}
1352
1353		/*
1354		 * Setting the refcount here allows the namespace to be "pre-populated"
1355		 * with controlled names.  notifyd does this for reserved names in
1356		 * its configuration file.
1357		 */
1358		n->refcount++;
1359	}
1360
1361	n->uid = uid;
1362	n->gid = gid;
1363
1364	_internal_insert_controlled_name(ns, n);
1365
1366	if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
1367	return NOTIFY_STATUS_OK;
1368}
1369
1370uint32_t
1371_notify_lib_get_owner(notify_state_t *ns, const char *name, uint32_t *uid, uint32_t *gid)
1372{
1373	name_info_t *n;
1374	int i, nlen, len;
1375
1376	if (ns == NULL) return NOTIFY_STATUS_FAILED;
1377	if (name == NULL) return NOTIFY_STATUS_INVALID_NAME;
1378
1379	if (ns->lock != NULL) pthread_mutex_lock(ns->lock);
1380
1381	n = (name_info_t *)_nc_table_find(ns->name_table, name);
1382	if (n != NULL)
1383	{
1384		*uid = n->uid;
1385		*gid = n->gid;
1386		if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
1387		return NOTIFY_STATUS_OK;
1388	}
1389
1390	len = strlen(name);
1391
1392	for (i = 0; i < ns->controlled_name_count; i++)
1393	{
1394		n = ns->controlled_name[i];
1395		if (n == NULL) break;
1396
1397		nlen = strlen(n->name);
1398
1399		if (!strcmp(n->name, name))
1400		{
1401			*uid = n->uid;
1402			*gid = n->gid;
1403			if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
1404			return NOTIFY_STATUS_OK;
1405		}
1406
1407		/* check if this key is a prefix */
1408		if (nlen >= len) continue;
1409		if (strncmp(n->name, name, nlen)) continue;
1410
1411		*uid = n->uid;
1412		*gid = n->gid;
1413
1414		if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
1415		return NOTIFY_STATUS_OK;
1416	}
1417
1418	*uid = 0;
1419	*gid = 0;
1420
1421	if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
1422	return NOTIFY_STATUS_OK;
1423}
1424
1425uint32_t
1426_notify_lib_set_access(notify_state_t *ns, const char *name, uint32_t mode)
1427{
1428	name_info_t *n;
1429
1430	if (ns == NULL) return NOTIFY_STATUS_FAILED;
1431	if (name == NULL) return NOTIFY_STATUS_INVALID_NAME;
1432
1433	if (ns->lock != NULL) pthread_mutex_lock(ns->lock);
1434
1435	n = (name_info_t *)_nc_table_find(ns->name_table, name);
1436	if (n == NULL)
1437	{
1438		/* create new name */
1439		n = _internal_new_name(ns, name);
1440		if (n == NULL)
1441		{
1442			if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
1443			return NOTIFY_STATUS_FAILED;
1444		}
1445
1446		/*
1447		 * Setting the refcount here allows the namespace to be "pre-populated"
1448		 * with controlled names.  notifyd does this for reserved names in
1449		 * its configuration file.
1450		 */
1451		n->refcount++;
1452	}
1453
1454	n->access = mode;
1455
1456	_internal_insert_controlled_name(ns, n);
1457
1458	if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
1459	return NOTIFY_STATUS_OK;
1460}
1461
1462uint32_t
1463_notify_lib_get_access(notify_state_t *ns, const char *name, uint32_t *mode)
1464{
1465	name_info_t *n;
1466	int i, nlen, len;
1467
1468	if (ns == NULL) return NOTIFY_STATUS_FAILED;
1469	if (name == NULL) return NOTIFY_STATUS_INVALID_NAME;
1470
1471	if (ns->lock != NULL) pthread_mutex_lock(ns->lock);
1472
1473	n = (name_info_t *)_nc_table_find(ns->name_table, name);
1474	if (n != NULL)
1475	{
1476		*mode = n->access;
1477
1478		if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
1479		return NOTIFY_STATUS_OK;
1480	}
1481
1482	len = strlen(name);
1483
1484	for (i = 0; i < ns->controlled_name_count; i++)
1485	{
1486		n = ns->controlled_name[i];
1487		if (n == NULL) break;
1488		if (n->name == NULL) continue;
1489
1490		nlen = strlen(n->name);
1491
1492		if (!strcmp(n->name, name))
1493		{
1494			*mode = n->access;
1495
1496			if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
1497			return NOTIFY_STATUS_OK;
1498		}
1499
1500		/* check if this key is a prefix */
1501		if (nlen >= len) continue;
1502		if (strncmp(n->name, name, nlen)) continue;
1503
1504		*mode = n->access;
1505
1506		if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
1507		return NOTIFY_STATUS_OK;
1508	}
1509
1510	*mode = NOTIFY_ACCESS_DEFAULT;
1511
1512	if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
1513	return NOTIFY_STATUS_OK;
1514}
1515
1516uint32_t
1517_notify_lib_release_name(notify_state_t *ns, const char *name, uid_t uid, gid_t gid)
1518{
1519	name_info_t *n;
1520
1521	if (ns == NULL) return NOTIFY_STATUS_FAILED;
1522	if (name == NULL) return NOTIFY_STATUS_INVALID_NAME;
1523
1524	if (ns->lock != NULL) pthread_mutex_lock(ns->lock);
1525
1526	n = (name_info_t *)_nc_table_find(ns->name_table, name);
1527	if (n == NULL)
1528	{
1529		if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
1530		return NOTIFY_STATUS_INVALID_NAME;
1531	}
1532
1533	/* Owner and root may release */
1534	if ((n->uid != uid) && (uid != 0))
1535	{
1536		if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
1537		return NOTIFY_STATUS_NOT_AUTHORIZED;
1538	}
1539
1540	_internal_release_name_info(ns, n);
1541
1542	if (ns->lock != NULL) pthread_mutex_unlock(ns->lock);
1543	return NOTIFY_STATUS_OK;
1544}
1545