1/*
2 * Copyright (c) 2003-2010 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 <stdio.h>
25#include <stdlib.h>
26#include <string.h>
27#include <asl.h>
28#include <bsm/libbsm.h>
29#include <mach/mach.h>
30#include <mach/mach_error.h>
31#include <mach/mach_traps.h>
32#include <sys/sysctl.h>
33#include <pthread.h>
34#include <sys/fcntl.h>
35#include <dispatch/private.h>
36#include "notify.h"
37#include "notifyd.h"
38#include "service.h"
39#include "notify_ipc.h"
40#include "notify_ipcServer.h"
41
42static void
43cancel_subscription(client_t *c)
44{
45	name_info_t *n;
46	int token;
47
48	if (global.notify_state == NULL) return;
49	if (c == NULL) return;
50
51	call_statistics.cleanup++;
52
53	token = c->client_id;
54
55	if (c->private != NULL)
56	{
57		service_close(c->private);
58		c->private = NULL;
59	}
60
61	n = c->name_info;
62	if ((n != NULL) && (n->refcount == 1) && (n->private != NULL))
63	{
64		service_close(n->private);
65		n->private = NULL;
66	}
67
68	_notify_lib_port_proc_release(global.notify_state, MACH_PORT_NULL, c->pid);
69
70	if (c->notify_type == NOTIFY_TYPE_MEMORY)
71	{
72		global.shared_memory_refcount[n->slot]--;
73	}
74    else if (c->notify_type == NOTIFY_TYPE_PORT)
75	{
76		_notify_lib_port_proc_release(global.notify_state, c->port, 0);
77	}
78
79	_notify_lib_cancel(global.notify_state, c->pid, token);
80}
81
82static void
83cancel_proc(void *px)
84{
85	void *tt;
86	long lpid = (long)px;
87	pid_t pid;
88	client_t *c;
89	list_t *l, *x;
90
91	if (global.notify_state == NULL) return;
92
93	pid = lpid;
94	x = NULL;
95
96	tt = _nc_table_traverse_start(global.notify_state->client_table);
97	while (tt != NULL)
98	{
99		c = _nc_table_traverse(global.notify_state->client_table, tt);
100		if (c == NULL) break;
101
102		if (c->pid == pid) x = _nc_list_prepend(x, _nc_list_new(c));
103	}
104	_nc_table_traverse_end(global.notify_state->client_table, tt);
105
106	for (l = x; l != NULL; l = _nc_list_next(l))
107	{
108		c = _nc_list_data(l);
109		cancel_subscription(c);
110	}
111
112	_nc_list_release_list(x);
113}
114
115static void
116cancel_port(mach_port_t port, dispatch_source_t src)
117{
118	void *tt;
119	client_t *c;
120	list_t *l, *x;
121
122	if (global.notify_state == NULL) return;
123
124	x = NULL;
125
126	tt = _nc_table_traverse_start(global.notify_state->client_table);
127	while (tt != NULL)
128	{
129		c = _nc_table_traverse(global.notify_state->client_table, tt);
130		if (c == NULL) break;
131
132		if (c->port == port) x = _nc_list_prepend(x, _nc_list_new(c));
133	}
134	_nc_table_traverse_end(global.notify_state->client_table, tt);
135
136	for (l = x; l != NULL; l = _nc_list_next(l))
137	{
138		c = _nc_list_data(l);
139		cancel_subscription(c);
140	}
141
142	_nc_list_release_list(x);
143}
144
145static void
146port_event(void *px)
147{
148	long lport = (long)px;
149	mach_port_t port;
150	unsigned long data;
151	portproc_data_t *pp;
152
153	if (global.notify_state == NULL) return;
154
155	port = (mach_port_t)lport;
156	if (port == MACH_PORT_NULL) return;
157
158	pp = _notify_lib_port_proc_find(global.notify_state, port, 0);
159	if (pp == NULL)
160	{
161		log_message(ASL_LEVEL_DEBUG, "can't find port source for %u\n", port);
162		return;
163	}
164
165	data = dispatch_source_get_data(pp->src);
166
167	if (data & DISPATCH_MACH_SEND_DEAD)
168	{
169		cancel_port(port, pp->src);
170	}
171	else if (data & DISPATCH_MACH_SEND_POSSIBLE)
172	{
173		_notify_lib_resume_port(global.notify_state, port);
174	}
175	else
176	{
177		log_message(ASL_LEVEL_DEBUG, "unknown data 0x%lx for %u\n", data, port);
178	}
179
180	_notify_lib_port_proc_release(global.notify_state, port, 0);
181}
182
183static void
184port_dealloc(void *px)
185{
186	long lport = (long)px;
187	mach_port_t port = (mach_port_t)lport;
188
189	if (port == MACH_PORT_NULL) return;
190	mach_port_deallocate(mach_task_self(), port);
191}
192
193static void
194port_registration_complete(void *px)
195{
196	long lport = (long)px;
197	mach_port_t port;
198
199	if (global.notify_state == NULL) return;
200
201	port = (mach_port_t)lport;
202	_notify_lib_resume_port(global.notify_state, port);
203}
204
205static void
206register_pid(pid_t pid)
207{
208	dispatch_source_t src;
209	long lpid;
210	portproc_data_t *pp;
211
212	if (global.notify_state == NULL) return;
213	if (pid <= 0) return;
214
215	/*
216	 * Check if this pid has already registered.
217	 * N.B. This call retains the portproc_data_t.  We want that.
218	 */
219	pp = _notify_lib_port_proc_find(global.notify_state, MACH_PORT_NULL, pid);
220	if (pp != NULL) return;
221
222	src = dispatch_source_create(DISPATCH_SOURCE_TYPE_PROC, pid, DISPATCH_PROC_EXIT, global.work_q);
223	dispatch_source_set_event_handler_f(src, (dispatch_function_t)cancel_proc);
224
225	lpid = pid;
226	dispatch_set_context(src, (void *)lpid);
227
228	_notify_lib_port_proc_new(global.notify_state, MACH_PORT_NULL, pid, 0, src);
229
230	dispatch_resume(src);
231}
232
233static void
234register_port(client_t *c)
235{
236	dispatch_source_t src;
237	long lport;
238	portproc_data_t *pp;
239
240	if (c == NULL) return;
241
242	/* ignore MACH_PORT_DEAD */
243	if (c->port == MACH_PORT_DEAD) return;
244
245	/*
246	 * Check if this port has already registered.
247	 * N.B. This call retains the portproc_data_t.  We want that.
248	 */
249	pp = _notify_lib_port_proc_find(global.notify_state, c->port, 0);
250	if (pp != NULL) return;
251
252	src = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_SEND, c->port, DISPATCH_MACH_SEND_DEAD | DISPATCH_MACH_SEND_POSSIBLE, global.work_q);
253
254	dispatch_source_set_event_handler_f(src, (dispatch_function_t)port_event);
255
256	/* retain send right for port - port_dealloc() will release when the source goes away */
257	mach_port_mod_refs(mach_task_self(), c->port, MACH_PORT_RIGHT_SEND, +1);
258	dispatch_source_set_cancel_handler_f(src, (dispatch_function_t)port_dealloc);
259
260	lport = c->port;
261	dispatch_set_context(src, (void *)lport);
262
263	dispatch_source_set_registration_handler_f(src, (dispatch_function_t)port_registration_complete);
264
265	_notify_lib_port_proc_new(global.notify_state, c->port, 0, NOTIFY_PORT_PROC_STATE_SUSPENDED, src);
266
267	dispatch_resume(src);
268}
269
270static uint32_t
271server_preflight(caddr_t name, mach_msg_type_number_t nameCnt, audit_token_t audit, int token, uid_t *uid, gid_t *gid, pid_t *pid, uint64_t *cid)
272{
273	pid_t xpid;
274
275	if ((name == NULL) && (nameCnt != 0)) return NOTIFY_STATUS_INVALID_NAME;
276
277	if (global.notify_state == NULL)
278	{
279		if (name != NULL) vm_deallocate(mach_task_self(), (vm_address_t)name, nameCnt);
280		return NOTIFY_STATUS_FAILED;
281	}
282
283	if ((name != NULL) && (name[nameCnt] != '\0'))
284	{
285		vm_deallocate(mach_task_self(), (vm_address_t)name, nameCnt);
286		return NOTIFY_STATUS_INVALID_NAME;
287	}
288
289	audit_token_to_au32(audit, NULL, uid, gid, NULL, NULL, &xpid, NULL, NULL);
290	if (pid != NULL) *pid = xpid;
291
292	if (token > 0)
293	{
294		client_t *c;
295		uint64_t xcid = make_client_id(xpid, token);
296		if (cid != NULL) *cid = xcid;
297
298		c = _nc_table_find_64(global.notify_state->client_table, xcid);
299		if (c != NULL)
300		{
301			/* duplicate tokens can occur if a process exec()s */
302			log_message(ASL_LEVEL_DEBUG, "duplicate token %d sent from PID %d\n", token, xpid);
303			cancel_subscription(c);
304		}
305	}
306
307	return NOTIFY_STATUS_OK;
308}
309
310kern_return_t __notify_server_post_3
311(
312	mach_port_t server,
313	uint64_t name_id,
314	audit_token_t audit
315)
316{
317	uid_t uid = (uid_t)-1;
318	gid_t gid = (gid_t)-1;
319
320	if (global.notify_state == NULL) return KERN_SUCCESS;
321
322	call_statistics.post++;
323	call_statistics.post_by_id++;
324
325	log_message(ASL_LEVEL_DEBUG, "__notify_server_post name_id %llu\n", name_id);
326
327	audit_token_to_au32(audit, NULL, &uid, &gid, NULL, NULL, NULL, NULL, NULL);
328
329	daemon_post_nid(name_id, uid, gid);
330	return KERN_SUCCESS;
331}
332
333kern_return_t __notify_server_post_2
334(
335	mach_port_t server,
336	caddr_t name,
337	mach_msg_type_number_t nameCnt,
338	uint64_t *name_id,
339	int *status,
340	audit_token_t audit
341)
342{
343	uid_t uid = (uid_t)-1;
344	gid_t gid = (gid_t)-1;
345	name_info_t *n;
346
347	*name_id = 0;
348
349	*status = server_preflight(name, nameCnt, audit, -1, &uid, &gid, NULL, NULL);
350	if (*status != NOTIFY_STATUS_OK) return KERN_SUCCESS;
351
352	call_statistics.post++;
353	call_statistics.post_by_name_and_fetch_id++;
354
355	*status = _notify_lib_check_controlled_access(global.notify_state, name, uid, gid, NOTIFY_ACCESS_WRITE);
356	if (*status != NOTIFY_STATUS_OK)
357	{
358		vm_deallocate(mach_task_self(), (vm_address_t)name, nameCnt);
359		return KERN_SUCCESS;
360	}
361
362	n = NULL;
363	*status = daemon_post(name, uid, gid);
364	if (*status == NOTIFY_STATUS_OK)
365	{
366		n = (name_info_t *)_nc_table_find(global.notify_state->name_table, name);
367	}
368
369	if (n == NULL)
370	{
371		*status = NOTIFY_STATUS_INVALID_NAME;
372		*name_id = UINT64_MAX;
373		call_statistics.post_no_op++;
374	}
375	else
376	{
377		*name_id = n->name_id;
378	}
379
380	if (*name_id == UINT64_MAX) log_message(ASL_LEVEL_DEBUG, "__notify_server_post %s\n", name);
381	else log_message(ASL_LEVEL_DEBUG, "__notify_server_post %s [%llu]\n", name, *name_id);
382
383	vm_deallocate(mach_task_self(), (vm_address_t)name, nameCnt);
384
385	return KERN_SUCCESS;
386}
387
388kern_return_t __notify_server_post_4
389(
390	mach_port_t server,
391	caddr_t name,
392	mach_msg_type_number_t nameCnt,
393	audit_token_t audit
394)
395{
396	uint64_t ignored_name_id;
397	int ignored_status;
398	kern_return_t kstatus;
399
400	kstatus = __notify_server_post_2(server, name, nameCnt, &ignored_name_id, &ignored_status, audit);
401
402	call_statistics.post_by_name_and_fetch_id--;
403	call_statistics.post_by_name++;
404
405	return kstatus;
406}
407
408kern_return_t __notify_server_post
409(
410	mach_port_t server,
411	caddr_t name,
412	mach_msg_type_number_t nameCnt,
413	int *status,
414	audit_token_t audit
415)
416{
417	uint64_t ignored_name_id;
418	kern_return_t kstatus;
419
420	*status = NOTIFY_STATUS_OK;
421
422	kstatus = __notify_server_post_2(server, name, nameCnt, &ignored_name_id, status, audit);
423
424	call_statistics.post_by_name_and_fetch_id--;
425	call_statistics.post_by_name++;
426
427	if (*status == NOTIFY_STATUS_INVALID_NAME) *status = NOTIFY_STATUS_OK;
428
429	return kstatus;
430}
431
432kern_return_t __notify_server_register_plain_2
433(
434	mach_port_t server,
435	caddr_t name,
436	mach_msg_type_number_t nameCnt,
437	int token,
438	audit_token_t audit
439)
440{
441	client_t *c;
442	uint64_t nid, cid;
443	uint32_t status;
444	uid_t uid = (uid_t)-1;
445	gid_t gid = (gid_t)-1;
446	pid_t pid = (pid_t)-1;
447
448	call_statistics.reg++;
449	call_statistics.reg_plain++;
450
451	status = server_preflight(name, nameCnt, audit, token, &uid, &gid, &pid, &cid);
452	if (status != NOTIFY_STATUS_OK) return KERN_SUCCESS;
453
454	log_message(ASL_LEVEL_DEBUG, "__notify_server_register_plain %s %d %d\n", name, pid, token);
455
456	status = _notify_lib_register_plain(global.notify_state, name, pid, token, -1, uid, gid, &nid);
457	if (status != NOTIFY_STATUS_OK)
458	{
459		vm_deallocate(mach_task_self(), (vm_address_t)name, nameCnt);
460		return KERN_SUCCESS;
461	}
462
463	c = _nc_table_find_64(global.notify_state->client_table, cid);
464
465	if (!strncmp(name, SERVICE_PREFIX, SERVICE_PREFIX_LEN)) service_open(name, c, uid, gid);
466	vm_deallocate(mach_task_self(), (vm_address_t)name, nameCnt);
467
468	register_pid(pid);
469
470	return KERN_SUCCESS;
471}
472
473kern_return_t __notify_server_register_check_2
474(
475	mach_port_t server,
476	caddr_t name,
477	mach_msg_type_number_t nameCnt,
478	int token,
479	int *size,
480	int *slot,
481	uint64_t *name_id,
482	int *status,
483	audit_token_t audit
484)
485{
486	name_info_t *n;
487	uint32_t i, j, x, new_slot;
488	uint64_t cid;
489	client_t *c;
490	uid_t uid = (uid_t)-1;
491	gid_t gid = (gid_t)-1;
492	pid_t pid = (pid_t)-1;
493
494	*size = 0;
495	*slot = 0;
496	*name_id = 0;
497	*status = NOTIFY_STATUS_OK;
498
499	*status = server_preflight(name, nameCnt, audit, token, &uid, &gid, &pid, &cid);
500	if (*status != NOTIFY_STATUS_OK) return KERN_SUCCESS;
501
502	call_statistics.reg++;
503	call_statistics.reg_check++;
504
505	if (global.nslots == 0)
506	{
507		*size = -1;
508		*slot = -1;
509		return __notify_server_register_plain_2(server, name, nameCnt, token, audit);
510	}
511
512	x = (uint32_t)-1;
513
514	n = (name_info_t *)_nc_table_find(global.notify_state->name_table, name);
515	if (n != NULL) x = n->slot;
516
517	new_slot = 0;
518	if (x == (uint32_t)-1)
519	{
520		/* find a slot */
521		new_slot = 1;
522
523		/*
524		 * Check slots beginning at the current slot_id + 1, since it's likely that the
525		 * next slot will be available.  Keep looking until we have examined all the
526		 * slots (skipping slot 0, which is reserved for notifyd). Stop if we find
527		 * an unused (refcount == 0) slot.
528		 */
529		for (i = 1, j = global.slot_id + 1; i < global.nslots; i++, j++)
530		{
531			if (j >= global.nslots) j = 1;
532			if (global.shared_memory_refcount[j] == 0)
533			{
534				x = j;
535				break;
536			}
537		}
538
539		if (x == (uint32_t)-1)
540		{
541			/*
542			 * We did not find an unused slot.  At this point, the shared
543			 * memory table is full, so we start re-using slots, beginning at
544			 * global.slot_id + 1.
545			 */
546			global.slot_id++;
547
548			/* wrap around to slot 1 (slot 0 is reserved for notifyd) */
549			if (global.slot_id >= global.nslots) global.slot_id = 1;
550			log_message(ASL_LEVEL_DEBUG, "reused shared memory slot %u\n", global.slot_id);
551			x = global.slot_id;
552		}
553		else
554		{
555			/* found a free slot */
556			global.slot_id = x;
557		}
558	}
559
560	if (new_slot == 1) global.shared_memory_base[x] = 1;
561	global.shared_memory_refcount[x]++;
562
563	log_message(ASL_LEVEL_DEBUG, "__notify_server_register_check %s %d %d\n", name, pid, token);
564
565	*size = global.nslots * sizeof(uint32_t);
566	*slot = x;
567	*status = _notify_lib_register_plain(global.notify_state, name, pid, token, x, uid, gid, name_id);
568	if (*status != NOTIFY_STATUS_OK)
569	{
570		vm_deallocate(mach_task_self(), (vm_address_t)name, nameCnt);
571		return KERN_SUCCESS;
572	}
573
574	c = _nc_table_find_64(global.notify_state->client_table, cid);
575
576	if (!strncmp(name, SERVICE_PREFIX, SERVICE_PREFIX_LEN)) service_open(name, c, uid, gid);
577	vm_deallocate(mach_task_self(), (vm_address_t)name, nameCnt);
578
579	register_pid(pid);
580
581	return KERN_SUCCESS;
582}
583
584kern_return_t __notify_server_register_signal_2
585(
586	mach_port_t server,
587	caddr_t name,
588	mach_msg_type_number_t nameCnt,
589	int token,
590	int sig,
591	audit_token_t audit
592)
593{
594	client_t *c;
595	uint64_t name_id, cid;
596	uint32_t status;
597	uid_t uid = (uid_t)-1;
598	gid_t gid = (gid_t)-1;
599	pid_t pid = (pid_t)-1;
600
601	status = server_preflight(name, nameCnt, audit, token, &uid, &gid, &pid, &cid);
602	if (status != NOTIFY_STATUS_OK) return KERN_SUCCESS;
603
604	call_statistics.reg++;
605	call_statistics.reg_signal++;
606
607	log_message(ASL_LEVEL_DEBUG, "__notify_server_register_signal %s %d %d %d\n", name, pid, token, sig);
608
609	status = _notify_lib_register_signal(global.notify_state, name, pid, token, sig, uid, gid, &name_id);
610	if (status != NOTIFY_STATUS_OK)
611	{
612		vm_deallocate(mach_task_self(), (vm_address_t)name, nameCnt);
613		return KERN_SUCCESS;
614	}
615
616	c = _nc_table_find_64(global.notify_state->client_table, cid);
617
618	if (!strncmp(name, SERVICE_PREFIX, SERVICE_PREFIX_LEN)) service_open(name, c, uid, gid);
619	vm_deallocate(mach_task_self(), (vm_address_t)name, nameCnt);
620
621	register_pid(pid);
622
623	return KERN_SUCCESS;
624}
625
626kern_return_t __notify_server_register_file_descriptor_2
627(
628	mach_port_t server,
629	caddr_t name,
630	mach_msg_type_number_t nameCnt,
631	int token,
632	fileport_t fileport,
633	audit_token_t audit
634)
635{
636	client_t *c;
637	int fd, flags;
638	uint32_t status;
639	uint64_t name_id, cid;
640	uid_t uid = (uid_t)-1;
641	gid_t gid = (gid_t)-1;
642	pid_t pid = (pid_t)-1;
643
644	status = server_preflight(name, nameCnt, audit, token, &uid, &gid, &pid, &cid);
645	if (status != NOTIFY_STATUS_OK) return KERN_SUCCESS;
646
647	call_statistics.reg++;
648	call_statistics.reg_file++;
649
650	log_message(ASL_LEVEL_DEBUG, "__notify_server_register_file_descriptor %s %d %d\n", name, pid, token);
651
652	fd = fileport_makefd(fileport);
653	mach_port_deallocate(mach_task_self(), fileport);
654	if (fd < 0)
655	{
656		vm_deallocate(mach_task_self(), (vm_address_t)name, nameCnt);
657		return KERN_SUCCESS;
658	}
659
660	flags = fcntl(fd, F_GETFL, 0);
661	if (flags < 0)
662	{
663		vm_deallocate(mach_task_self(), (vm_address_t)name, nameCnt);
664		return KERN_SUCCESS;
665	}
666
667	flags |= O_NONBLOCK;
668	if (fcntl(fd, F_SETFL, flags) < 0)
669	{
670		vm_deallocate(mach_task_self(), (vm_address_t)name, nameCnt);
671		return KERN_SUCCESS;
672	}
673
674	status = _notify_lib_register_file_descriptor(global.notify_state, name, pid, token, fd, uid, gid, &name_id);
675	if (status != NOTIFY_STATUS_OK)
676	{
677		vm_deallocate(mach_task_self(), (vm_address_t)name, nameCnt);
678		return KERN_SUCCESS;
679	}
680
681	c = _nc_table_find_64(global.notify_state->client_table, cid);
682
683	if (!strncmp(name, SERVICE_PREFIX, SERVICE_PREFIX_LEN)) service_open(name, c, uid, gid);
684	vm_deallocate(mach_task_self(), (vm_address_t)name, nameCnt);
685
686	register_pid(pid);
687
688	return KERN_SUCCESS;
689}
690
691kern_return_t __notify_server_register_mach_port_2
692(
693	mach_port_t server,
694	caddr_t name,
695	mach_msg_type_number_t nameCnt,
696	int token,
697	mach_port_t port,
698	audit_token_t audit
699)
700{
701	client_t *c;
702	uint64_t name_id, cid;
703	uint32_t status;
704	uid_t uid = (uid_t)-1;
705	gid_t gid = (gid_t)-1;
706	pid_t pid = (pid_t)-1;
707
708	if (port == MACH_PORT_DEAD) return KERN_SUCCESS;
709
710	status = server_preflight(name, nameCnt, audit, token, &uid, &gid, &pid, &cid);
711	if (status != NOTIFY_STATUS_OK)
712	{
713		mach_port_deallocate(mach_task_self(), port);
714		return KERN_SUCCESS;
715	}
716
717	call_statistics.reg++;
718	call_statistics.reg_port++;
719
720	log_message(ASL_LEVEL_DEBUG, "__notify_server_register_mach_port %s %d %d\n", name, pid, token);
721
722	status = _notify_lib_register_mach_port(global.notify_state, name, pid, token, port, uid, gid, &name_id);
723	if (status != NOTIFY_STATUS_OK)
724	{
725		vm_deallocate(mach_task_self(), (vm_address_t)name, nameCnt);
726		mach_port_deallocate(mach_task_self(), port);
727		return KERN_SUCCESS;
728	}
729
730	c = _nc_table_find_64(global.notify_state->client_table,cid);
731
732	if (!strncmp(name, SERVICE_PREFIX, SERVICE_PREFIX_LEN)) service_open(name, c, uid, gid);
733	vm_deallocate(mach_task_self(), (vm_address_t)name, nameCnt);
734
735	register_pid(pid);
736	register_port(c);
737
738	return KERN_SUCCESS;
739}
740
741kern_return_t __notify_server_cancel
742(
743	mach_port_t server,
744	int token,
745	int *status,
746	audit_token_t audit
747)
748{
749	client_t *c;
750	uid_t uid = (uid_t)-1;
751	pid_t pid = (pid_t)-1;
752
753	*status = server_preflight(NULL, 0, audit, -1, &uid, NULL, &pid, NULL);
754	if (*status != NOTIFY_STATUS_OK) return KERN_SUCCESS;
755
756	call_statistics.cancel++;
757
758	log_message(ASL_LEVEL_DEBUG, "__notify_server_cancel %d %d\n", pid, token);
759
760	*status = NOTIFY_STATUS_OK;
761
762	c = _nc_table_find_64(global.notify_state->client_table, make_client_id(pid, token));
763	if (c == NULL) *status = NOTIFY_STATUS_FAILED;
764	else cancel_subscription(c);
765
766	return KERN_SUCCESS;
767}
768
769kern_return_t __notify_server_cancel_2
770(
771	mach_port_t server,
772	int token,
773	audit_token_t audit
774)
775{
776	int ignored;
777	return __notify_server_cancel(server, token, &ignored, audit);
778}
779
780kern_return_t __notify_server_suspend
781(
782	mach_port_t server,
783	int token,
784	int *status,
785	audit_token_t audit
786)
787{
788	pid_t pid = (pid_t)-1;
789
790	*status = server_preflight(NULL, 0, audit, -1, NULL, NULL, &pid, NULL);
791	if (*status != NOTIFY_STATUS_OK) return KERN_SUCCESS;
792
793	call_statistics.suspend++;
794
795	log_message(ASL_LEVEL_DEBUG, "__notify_server_suspend %d %d\n", pid, token);
796
797	_notify_lib_suspend(global.notify_state, pid, token);
798	*status = NOTIFY_STATUS_OK;
799
800	return KERN_SUCCESS;
801}
802
803kern_return_t __notify_server_resume
804(
805	mach_port_t server,
806	int token,
807	int *status,
808	audit_token_t audit
809)
810{
811	pid_t pid = (pid_t)-1;
812
813	*status = server_preflight(NULL, 0, audit, -1, NULL, NULL, &pid, NULL);
814	if (*status != NOTIFY_STATUS_OK) return KERN_SUCCESS;
815
816	call_statistics.resume++;
817
818	log_message(ASL_LEVEL_DEBUG, "__notify_server_resume %d %d\n", pid, token);
819
820	_notify_lib_resume(global.notify_state, pid, token);
821	*status = NOTIFY_STATUS_OK;
822
823	return KERN_SUCCESS;
824}
825
826static uid_t
827uid_for_pid(pid_t pid)
828{
829	int mib[4];
830	struct kinfo_proc info;
831	size_t size = sizeof(struct kinfo_proc);
832
833	mib[0] = CTL_KERN;
834	mib[1] = KERN_PROC;
835	mib[2] = KERN_PROC_PID;
836	mib[3] = pid;
837
838	sysctl(mib, 4, &info, &size, 0, 0);
839
840	return (uid_t)info.kp_eproc.e_ucred.cr_uid;
841}
842
843kern_return_t __notify_server_suspend_pid
844(
845	mach_port_t server,
846	int pid,
847	int *status,
848	audit_token_t audit
849)
850{
851	uid_t uid, target_uid;
852
853	uid = (uid_t)-1;
854
855	*status = server_preflight(NULL, 0, audit, -1, &uid, NULL, NULL, NULL);
856	if (*status != NOTIFY_STATUS_OK) return KERN_SUCCESS;
857
858	call_statistics.suspend_pid++;
859
860	log_message(ASL_LEVEL_DEBUG, "__notify_server_suspend_pid %d\n", pid);
861
862	target_uid = uid_for_pid(pid);
863
864	if ((uid != 0) && (target_uid != uid))
865	{
866		*status = NOTIFY_STATUS_NOT_AUTHORIZED;
867		return KERN_SUCCESS;
868	}
869
870	*status = NOTIFY_STATUS_OK;
871
872	_notify_lib_suspend_proc(global.notify_state, pid);
873
874	return KERN_SUCCESS;
875}
876
877kern_return_t __notify_server_resume_pid
878(
879	mach_port_t server,
880	int pid,
881	int *status,
882	audit_token_t audit
883)
884{
885	uid_t uid, target_uid;
886
887	uid = (uid_t)-1;
888
889	*status = server_preflight(NULL, 0, audit, -1, &uid, NULL, NULL, NULL);
890	if (*status != NOTIFY_STATUS_OK) return KERN_SUCCESS;
891
892	call_statistics.resume_pid++;
893
894	log_message(ASL_LEVEL_DEBUG, "__notify_server_resume_pid %d\n", pid);
895
896	target_uid = uid_for_pid(pid);
897
898	if ((uid != 0) && (target_uid != uid))
899	{
900		*status = NOTIFY_STATUS_NOT_AUTHORIZED;
901		return KERN_SUCCESS;
902	}
903
904	*status = NOTIFY_STATUS_OK;
905
906	_notify_lib_resume_proc(global.notify_state, pid);
907
908	return KERN_SUCCESS;
909}
910
911kern_return_t __notify_server_check
912(
913	mach_port_t server,
914	int token,
915	int *check,
916	int *status,
917	audit_token_t audit
918)
919{
920	pid_t pid = (gid_t)-1;
921
922	*check = 0;
923
924	call_statistics.check++;
925
926	audit_token_to_au32(audit, NULL, NULL, NULL, NULL, NULL, &pid, NULL, NULL);
927
928	log_message(ASL_LEVEL_DEBUG, "__notify_server_check %d %d\n", pid, token);
929
930	*status =  _notify_lib_check(global.notify_state, pid, token, check);
931	return KERN_SUCCESS;
932}
933
934kern_return_t __notify_server_get_state
935(
936	mach_port_t server,
937	int token,
938	uint64_t *state,
939	int *status,
940	audit_token_t audit
941)
942{
943	uid_t uid = (uid_t)-1;
944	gid_t gid = (gid_t)-1;
945	pid_t pid = (pid_t)-1;
946	client_t *c;
947
948	*state = 0;
949
950	*status = server_preflight(NULL, 0, audit, -1, &uid, &gid, &pid, NULL);
951	if (*status != NOTIFY_STATUS_OK) return KERN_SUCCESS;
952
953	call_statistics.get_state++;
954	call_statistics.get_state_by_client++;
955
956	log_message(ASL_LEVEL_DEBUG, "__notify_server_get_state %d %d\n", pid, token);
957
958	c = _nc_table_find_64(global.notify_state->client_table, make_client_id(pid, token));
959	if ((c == NULL) || (c->name_info == NULL))
960	{
961		*status = NOTIFY_STATUS_FAILED;
962	}
963	else
964	{
965		*status = _notify_lib_get_state(global.notify_state, c->name_info->name_id, state, uid, gid);
966	}
967
968	return KERN_SUCCESS;
969}
970
971kern_return_t __notify_server_get_state_2
972(
973	mach_port_t server,
974	uint64_t name_id,
975	uint64_t *state,
976	int *status,
977	audit_token_t audit
978)
979{
980	uid_t uid = (uid_t)-1;
981	gid_t gid = (gid_t)-1;
982
983	*state = 0;
984
985	*status = server_preflight(NULL, 0, audit, -1, &uid, &gid, NULL, NULL);
986	if (*status != NOTIFY_STATUS_OK) return KERN_SUCCESS;
987
988	log_message(ASL_LEVEL_DEBUG, "__notify_server_get_state_2 %llu\n", name_id);
989
990	*status = _notify_lib_get_state(global.notify_state, name_id, state, uid, gid);
991	return KERN_SUCCESS;
992}
993
994kern_return_t __notify_server_get_state_3
995(
996	mach_port_t server,
997	int token,
998	uint64_t *state,
999	uint64_t *name_id,
1000	int *status,
1001	audit_token_t audit
1002)
1003{
1004	uid_t uid = (uid_t)-1;
1005	gid_t gid = (gid_t)-1;
1006	pid_t pid = (pid_t)-1;
1007	client_t *c;
1008
1009	*state = 0;
1010	*name_id = 0;
1011
1012	*status = server_preflight(NULL, 0, audit, -1, &uid, &gid, &pid, NULL);
1013	if (*status != NOTIFY_STATUS_OK) return KERN_SUCCESS;
1014
1015	call_statistics.get_state++;
1016	call_statistics.get_state_by_client_and_fetch_id++;
1017
1018	c = _nc_table_find_64(global.notify_state->client_table, make_client_id(pid, token));
1019	if ((c == NULL) || (c->name_info == NULL))
1020	{
1021		*status = NOTIFY_STATUS_FAILED;
1022		*name_id = UINT64_MAX;
1023	}
1024	else
1025	{
1026		*status = _notify_lib_get_state(global.notify_state, c->name_info->name_id, state, uid, gid);
1027		*name_id = c->name_info->name_id;
1028	}
1029
1030	if (*name_id == UINT64_MAX) log_message(ASL_LEVEL_DEBUG, "__notify_server_get_state_3 %d %d\n", pid, token);
1031	else log_message(ASL_LEVEL_DEBUG, "__notify_server_get_state_3 %d %d [%llu]\n", pid, token, *name_id);
1032
1033	return KERN_SUCCESS;
1034}
1035
1036kern_return_t __notify_server_set_state_3
1037(
1038	mach_port_t server,
1039	int token,
1040	uint64_t state,
1041	uint64_t *name_id,
1042	int *status,
1043	audit_token_t audit
1044)
1045{
1046	client_t *c;
1047	uid_t uid = (uid_t)-1;
1048	gid_t gid = (gid_t)-1;
1049	pid_t pid = (pid_t)-1;
1050
1051	*name_id = 0;
1052
1053	*status = server_preflight(NULL, 0, audit, -1, &uid, &gid, &pid, NULL);
1054	if (*status != NOTIFY_STATUS_OK) return KERN_SUCCESS;
1055
1056	call_statistics.set_state++;
1057	call_statistics.set_state_by_client_and_fetch_id++;
1058
1059	c = _nc_table_find_64(global.notify_state->client_table, make_client_id(pid, token));
1060	if ((c == NULL) || (c->name_info == NULL))
1061	{
1062		*status = NOTIFY_STATUS_FAILED;
1063		*name_id = UINT64_MAX;
1064	}
1065	else
1066	{
1067		*status = _notify_lib_set_state(global.notify_state, c->name_info->name_id, state, uid, gid);
1068		*name_id = c->name_info->name_id;
1069	}
1070
1071	if (*name_id == UINT64_MAX) log_message(ASL_LEVEL_DEBUG, "__notify_server_set_state_3 %d %d %llu\n", pid, token, state);
1072	else log_message(ASL_LEVEL_DEBUG, "__notify_server_set_state_3 %d %d %llu [%llu]\n", pid, token, state, *name_id);
1073
1074	return KERN_SUCCESS;
1075}
1076
1077kern_return_t __notify_server_set_state
1078(
1079	mach_port_t server,
1080	int token,
1081	uint64_t state,
1082	int *status,
1083	audit_token_t audit
1084)
1085{
1086	uint64_t ignored;
1087	kern_return_t kstatus;
1088
1089	*status = NOTIFY_STATUS_OK;
1090
1091	kstatus = __notify_server_set_state_3(server, token, state, &ignored, status, audit);
1092
1093	call_statistics.set_state_by_client_and_fetch_id--;
1094	call_statistics.set_state_by_client++;
1095
1096	return kstatus;
1097}
1098
1099kern_return_t __notify_server_set_state_2
1100(
1101	mach_port_t server,
1102	uint64_t name_id,
1103	uint64_t state,
1104	audit_token_t audit
1105)
1106{
1107	uint32_t status;
1108	uid_t uid = (uid_t)-1;
1109	gid_t gid = (gid_t)-1;
1110
1111	if (global.notify_state == NULL) return KERN_SUCCESS;
1112
1113	call_statistics.set_state++;
1114	call_statistics.set_state_by_id++;
1115
1116	audit_token_to_au32(audit, NULL, &uid, &gid, NULL, NULL, NULL, NULL, NULL);
1117
1118	log_message(ASL_LEVEL_DEBUG, "__notify_server_set_state_2 %llu %llu\n", name_id, state);
1119
1120	status = _notify_lib_set_state(global.notify_state, name_id, state, uid, gid);
1121	return KERN_SUCCESS;
1122}
1123
1124kern_return_t __notify_server_set_owner
1125(
1126	mach_port_t server,
1127	caddr_t name,
1128	mach_msg_type_number_t nameCnt,
1129	int uid,
1130	int gid,
1131	int *status,
1132	audit_token_t audit
1133)
1134{
1135	uid_t auid = (uid_t)-1;
1136
1137	*status = server_preflight(name, nameCnt, audit, -1, &auid, NULL, NULL, NULL);
1138	if (*status != NOTIFY_STATUS_OK) return KERN_SUCCESS;
1139
1140	call_statistics.set_owner++;
1141
1142	log_message(ASL_LEVEL_DEBUG, "__notify_server_set_owner %s %d %d\n", name, uid, gid);
1143
1144	/* only root may set owner for names */
1145	if (auid != 0)
1146	{
1147		vm_deallocate(mach_task_self(), (vm_address_t)name, nameCnt);
1148		*status = NOTIFY_STATUS_NOT_AUTHORIZED;
1149		return KERN_SUCCESS;
1150	}
1151
1152	*status = _notify_lib_set_owner(global.notify_state, name, uid, gid);
1153	vm_deallocate(mach_task_self(), (vm_address_t)name, nameCnt);
1154	return KERN_SUCCESS;
1155}
1156
1157kern_return_t __notify_server_get_owner
1158(
1159	mach_port_t server,
1160	caddr_t name,
1161	mach_msg_type_number_t nameCnt,
1162	int *uid,
1163	int *gid,
1164	int *status,
1165	audit_token_t audit
1166)
1167{
1168	*uid = 0;
1169	*gid = 0;
1170
1171	*status = server_preflight(name, nameCnt, audit, -1, NULL, NULL, NULL, NULL);
1172	if (*status != NOTIFY_STATUS_OK) return KERN_SUCCESS;
1173
1174	call_statistics.get_owner++;
1175
1176	log_message(ASL_LEVEL_DEBUG, "__notify_server_get_owner %s\n", name);
1177
1178	*status = _notify_lib_get_owner(global.notify_state, name, (uint32_t *)uid, (uint32_t *)gid);
1179	vm_deallocate(mach_task_self(), (vm_address_t)name, nameCnt);
1180	return KERN_SUCCESS;
1181}
1182
1183kern_return_t __notify_server_set_access
1184(
1185	mach_port_t server,
1186	caddr_t name,
1187	mach_msg_type_number_t nameCnt,
1188	int mode,
1189	int *status,
1190	audit_token_t audit
1191)
1192{
1193	uint32_t u, g;
1194	uid_t uid = (uid_t)-1;
1195	gid_t gid = (gid_t)-1;
1196
1197	*status = server_preflight(name, nameCnt, audit, -1, &uid, &gid, NULL, NULL);
1198	if (*status != NOTIFY_STATUS_OK) return KERN_SUCCESS;
1199
1200	call_statistics.set_access++;
1201
1202	log_message(ASL_LEVEL_DEBUG, "__notify_server_set_access %s 0x%03x\n", name, mode);
1203
1204	_notify_lib_get_owner(global.notify_state, name, &u, &g);
1205
1206	/* only root and owner may set access for names */
1207	if ((uid != 0) && (uid != u))
1208	{
1209		*status = NOTIFY_STATUS_NOT_AUTHORIZED;
1210		vm_deallocate(mach_task_self(), (vm_address_t)name, nameCnt);
1211		return KERN_SUCCESS;
1212	}
1213
1214	*status = _notify_lib_set_access(global.notify_state, name, mode);
1215	if ((u != 0) || (g != 0)) *status = _notify_lib_set_owner(global.notify_state, name, u, g);
1216	vm_deallocate(mach_task_self(), (vm_address_t)name, nameCnt);
1217	return KERN_SUCCESS;
1218}
1219
1220kern_return_t __notify_server_get_access
1221(
1222	mach_port_t server,
1223	caddr_t name,
1224	mach_msg_type_number_t nameCnt,
1225	int *mode,
1226	int *status,
1227	audit_token_t audit
1228)
1229{
1230	*mode = 0;
1231
1232	*status = server_preflight(name, nameCnt, audit, -1, NULL, NULL, NULL, NULL);
1233	if (*status != NOTIFY_STATUS_OK) return KERN_SUCCESS;
1234
1235	call_statistics.get_access++;
1236
1237	log_message(ASL_LEVEL_DEBUG, "__notify_server_get_access %s\n", name);
1238
1239	*status = _notify_lib_get_access(global.notify_state, name, (uint32_t *)mode);
1240	vm_deallocate(mach_task_self(), (vm_address_t)name, nameCnt);
1241	return KERN_SUCCESS;
1242}
1243
1244/* Unsupported because it makes no sense */
1245kern_return_t __notify_server_release_name
1246(
1247	mach_port_t server,
1248	caddr_t name,
1249	mach_msg_type_number_t nameCnt,
1250	int *status,
1251	audit_token_t audit
1252)
1253{
1254	*status = NOTIFY_STATUS_FAILED;
1255	return KERN_SUCCESS;
1256}
1257
1258kern_return_t __notify_server_monitor_file
1259(
1260	mach_port_t server,
1261	int token,
1262	caddr_t path,
1263	mach_msg_type_number_t pathCnt,
1264	int flags,
1265	int *status,
1266	audit_token_t audit
1267)
1268{
1269	client_t *c;
1270	name_info_t *n;
1271	uid_t uid = (uid_t)-1;
1272	gid_t gid = (gid_t)-1;
1273	pid_t pid = (pid_t)-1;
1274	uint32_t ubits = (uint32_t)flags;
1275
1276	*status = server_preflight(path, pathCnt, audit, -1, &uid, &gid, &pid, NULL);
1277	if (*status != NOTIFY_STATUS_OK) return KERN_SUCCESS;
1278
1279	call_statistics.monitor_file++;
1280
1281	log_message(ASL_LEVEL_DEBUG, "__notify_server_monitor_file %d %d %s 0x%08x\n", pid, token, path, ubits);
1282
1283	c = _nc_table_find_64(global.notify_state->client_table, make_client_id(pid, token));
1284	if (c == NULL)
1285	{
1286		vm_deallocate(mach_task_self(), (vm_address_t)path, pathCnt);
1287		*status = NOTIFY_STATUS_INVALID_REQUEST;
1288		return KERN_SUCCESS;
1289	}
1290
1291	n = c->name_info;
1292	if (n == NULL)
1293	{
1294		vm_deallocate(mach_task_self(), (vm_address_t)path, pathCnt);
1295		*status = NOTIFY_STATUS_INVALID_REQUEST;
1296		return KERN_SUCCESS;
1297	}
1298
1299	*status = service_open_path_private(n->name, c, path, uid, gid, ubits);
1300	vm_deallocate(mach_task_self(), (vm_address_t)path, pathCnt);
1301
1302	return KERN_SUCCESS;
1303}
1304
1305kern_return_t __notify_server_monitor_file_2
1306(
1307	mach_port_t server,
1308	int token,
1309	caddr_t path,
1310	mach_msg_type_number_t pathCnt,
1311	int flags,
1312	audit_token_t audit
1313)
1314{
1315	int ignored;
1316	return __notify_server_monitor_file(server, token, path, pathCnt, flags, &ignored, audit);
1317}
1318
1319/*
1320 * Original routines provide compatibility for legacy clients.
1321 * iOS simulator uses them.
1322 */
1323
1324/*
1325 * Generates a integer "token" for legacy client registrations.
1326 */
1327static int
1328generate_token(audit_token_t audit)
1329{
1330	static int legacy_id = 0;
1331
1332	if (++legacy_id == -1) legacy_id = 1;
1333	return legacy_id;
1334}
1335
1336kern_return_t __notify_server_register_plain
1337(
1338	mach_port_t server,
1339	caddr_t name,
1340	mach_msg_type_number_t nameCnt,
1341	int *client_id,
1342	int *status,
1343	audit_token_t audit
1344)
1345{
1346	int token = generate_token(audit);
1347
1348	*client_id = token;
1349	*status = NOTIFY_STATUS_OK;
1350
1351	return __notify_server_register_plain_2(server, name, nameCnt, token, audit);
1352}
1353
1354kern_return_t __notify_server_register_check
1355(
1356	mach_port_t server,
1357	caddr_t name,
1358	mach_msg_type_number_t nameCnt,
1359	int *size,
1360	int *slot,
1361	int *client_id,
1362	int *status,
1363	audit_token_t audit
1364)
1365{
1366	*size = 0;
1367	*slot = 0;
1368	*status = NOTIFY_STATUS_OK;
1369
1370	uint64_t nid;
1371	int token = generate_token(audit);
1372
1373	*client_id = token;
1374
1375	return __notify_server_register_check_2(server, name, nameCnt, token, size, slot, &nid, status, audit);
1376}
1377
1378kern_return_t __notify_server_register_signal
1379(
1380	mach_port_t server,
1381	caddr_t name,
1382	mach_msg_type_number_t nameCnt,
1383	int sig,
1384	int *client_id,
1385	int *status,
1386	audit_token_t audit
1387)
1388{
1389	int token = generate_token(audit);
1390
1391	*client_id = token;
1392	*status = NOTIFY_STATUS_OK;
1393
1394	return __notify_server_register_signal_2(server, name, nameCnt, token, sig, audit);
1395}
1396
1397kern_return_t __notify_server_register_file_descriptor
1398(
1399	mach_port_t server,
1400	caddr_t name,
1401	mach_msg_type_number_t nameCnt,
1402	fileport_t fileport,
1403	int ntoken,
1404	int *client_id,
1405	int *status,
1406	audit_token_t audit
1407)
1408{
1409	kern_return_t kstatus;
1410	client_t *c;
1411	pid_t pid = (pid_t)-1;
1412	int token = generate_token(audit);
1413
1414	*client_id = token;
1415	*status = NOTIFY_STATUS_OK;
1416
1417	kstatus = __notify_server_register_file_descriptor_2(server, name, nameCnt, token, fileport, audit);
1418	if (kstatus == KERN_SUCCESS)
1419	{
1420		audit_token_to_au32(audit, NULL, NULL, NULL, NULL, NULL, &pid, NULL, NULL);
1421		c = _nc_table_find_64(global.notify_state->client_table, make_client_id(pid, token));
1422		if (c == NULL) *status = NOTIFY_STATUS_FAILED;
1423		else c->send_val = ntoken;
1424	}
1425
1426	return kstatus;
1427}
1428
1429kern_return_t __notify_server_register_mach_port
1430(
1431	mach_port_t server,
1432	caddr_t name,
1433	mach_msg_type_number_t nameCnt,
1434	mach_port_t port,
1435	int ntoken,
1436	int *client_id,
1437	int *status,
1438	audit_token_t audit
1439)
1440{
1441	kern_return_t kstatus;
1442	client_t *c;
1443	pid_t pid = (pid_t)-1;
1444	int token;
1445
1446	*client_id = 0;
1447	*status = NOTIFY_STATUS_OK;
1448
1449	if (port == MACH_PORT_DEAD)
1450	{
1451		if ((name != NULL) && (nameCnt > 0)) vm_deallocate(mach_task_self(), (vm_address_t)name, nameCnt);
1452		*status = NOTIFY_STATUS_INVALID_REQUEST;
1453		return KERN_SUCCESS;
1454	}
1455
1456	token = generate_token(audit);
1457
1458	*client_id = token;
1459	*status = NOTIFY_STATUS_OK;
1460
1461	kstatus = __notify_server_register_mach_port_2(server, name, nameCnt, token, port, audit);
1462	if (kstatus == KERN_SUCCESS)
1463	{
1464		audit_token_to_au32(audit, NULL, NULL, NULL, NULL, NULL, &pid, NULL, NULL);
1465		c = _nc_table_find_64(global.notify_state->client_table, make_client_id(pid, token));
1466		if (c == NULL) *status = NOTIFY_STATUS_FAILED;
1467		else c->send_val = ntoken;
1468	}
1469
1470	return kstatus;
1471}
1472
1473kern_return_t __notify_server_simple_post
1474(
1475	mach_port_t server,
1476	caddr_t name,
1477	mach_msg_type_number_t nameCnt,
1478	audit_token_t audit
1479)
1480{
1481	return __notify_server_post_4(server, name, nameCnt, audit);
1482}
1483
1484kern_return_t __notify_server_regenerate
1485(
1486	mach_port_t server,
1487	caddr_t name,
1488	mach_msg_type_number_t nameCnt,
1489	int token,
1490	uint32_t reg_type,
1491	mach_port_t port,
1492	int sig,
1493	int prev_slot,
1494	uint64_t prev_state,
1495	uint64_t prev_time,
1496	caddr_t path,
1497	mach_msg_type_number_t pathCnt,
1498	int path_flags,
1499	int *new_slot,
1500	uint64_t *new_nid,
1501	int *status,
1502	audit_token_t audit
1503)
1504{
1505	kern_return_t kstatus;
1506	pid_t pid = (pid_t)-1;
1507	int size;
1508	name_info_t *n;
1509	client_t *c;
1510	uint64_t cid;
1511
1512	*new_slot = 0;
1513	*new_nid = 0;
1514	*status = NOTIFY_STATUS_OK;
1515
1516	if (name == NULL)
1517	{
1518		if (path != NULL) vm_deallocate(mach_task_self(), (vm_address_t)path, pathCnt);
1519		*status = NOTIFY_STATUS_INVALID_NAME;
1520		return KERN_SUCCESS;
1521	}
1522
1523	if (name[nameCnt] != '\0')
1524	{
1525		if (path != NULL) vm_deallocate(mach_task_self(), (vm_address_t)path, pathCnt);
1526		vm_deallocate(mach_task_self(), (vm_address_t)name, nameCnt);
1527		*status = NOTIFY_STATUS_INVALID_NAME;
1528		return KERN_SUCCESS;
1529	}
1530
1531	if ((path != NULL) && (path[pathCnt] != '\0'))
1532	{
1533		vm_deallocate(mach_task_self(), (vm_address_t)name, nameCnt);
1534		vm_deallocate(mach_task_self(), (vm_address_t)path, pathCnt);
1535		*status = NOTIFY_STATUS_INVALID_REQUEST;
1536		return KERN_SUCCESS;
1537	}
1538
1539	call_statistics.regenerate++;
1540
1541	audit_token_to_au32(audit, NULL, NULL, NULL, NULL, NULL, &pid, NULL, NULL);
1542
1543	log_message(ASL_LEVEL_DEBUG, "__notify_server_regenerate %s %d %d %d %u %d %d %llu %s %d\n", name, pid, token, reg_type, port, sig, prev_slot, prev_state, path, path_flags);
1544
1545	cid = make_client_id(pid, token);
1546	c = (client_t *)_nc_table_find_64(global.notify_state->client_table, cid);
1547	if (c != NULL)
1548	{
1549		/* duplicate client - this should never happen */
1550		vm_deallocate(mach_task_self(), (vm_address_t)name, nameCnt);
1551		if ((path != NULL) && (pathCnt > 0)) vm_deallocate(mach_task_self(), (vm_address_t)path, pathCnt);
1552		*status = NOTIFY_STATUS_FAILED;
1553		return KERN_SUCCESS;
1554	}
1555
1556	switch (reg_type)
1557	{
1558		case NOTIFY_TYPE_MEMORY:
1559		{
1560			/* prev_slot must be between 0 and global.nslots */
1561			if ((prev_slot < 0) || (prev_slot >= global.nslots))
1562			{
1563				*status = NOTIFY_STATUS_INVALID_REQUEST;
1564				return KERN_SUCCESS;
1565			}
1566
1567			kstatus = __notify_server_register_check_2(server, name, nameCnt, token, &size, new_slot, new_nid, status, audit);
1568			if (*status == NOTIFY_STATUS_OK)
1569			{
1570				if ((*new_slot != UINT32_MAX) && (global.last_shm_base != NULL))
1571				{
1572					global.shared_memory_base[*new_slot] = global.shared_memory_base[*new_slot] + global.last_shm_base[prev_slot] - 1;
1573					global.last_shm_base[prev_slot] = 0;
1574				}
1575			}
1576			break;
1577		}
1578		case NOTIFY_TYPE_PLAIN:
1579		{
1580			kstatus = __notify_server_register_plain_2(server, name, nameCnt, token, audit);
1581			break;
1582		}
1583		case NOTIFY_TYPE_PORT:
1584		{
1585			kstatus = __notify_server_register_mach_port_2(server, name, nameCnt, token, port, audit);
1586			break;
1587		}
1588		case NOTIFY_TYPE_SIGNAL:
1589		{
1590			kstatus = __notify_server_register_signal_2(server, name, nameCnt, token, sig, audit);
1591			break;
1592		}
1593		case NOTIFY_TYPE_FILE: /* fall through */
1594		default:
1595		{
1596			/* can not regenerate this type */
1597			vm_deallocate(mach_task_self(), (vm_address_t)name, nameCnt);
1598			if ((path != NULL) && (pathCnt > 0)) vm_deallocate(mach_task_self(), (vm_address_t)path, pathCnt);
1599			*status = NOTIFY_STATUS_FAILED;
1600			return KERN_SUCCESS;
1601		}
1602	}
1603
1604	if (path != NULL)
1605	{
1606		__notify_server_monitor_file_2(server, token, path, pathCnt, path_flags, audit);
1607	}
1608
1609	c = (client_t *)_nc_table_find_64(global.notify_state->client_table, cid);
1610	if (c == NULL)
1611	{
1612		*status = NOTIFY_STATUS_FAILED;
1613	}
1614	else
1615	{
1616		*status = NOTIFY_STATUS_OK;
1617		n = c->name_info;
1618		*new_nid = n->name_id;
1619		if (prev_time > n->state_time) n->state = prev_state;
1620	}
1621
1622	return KERN_SUCCESS;
1623}
1624