1/*
2 * Copyright (c) 2003-2012 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24#include <sys/types.h>
25#include <sys/stat.h>
26#include <signal.h>
27#include <stdio.h>
28#include <stdlib.h>
29#include <errno.h>
30#include <sys/un.h>
31#include <sys/ipc.h>
32#include <sys/mman.h>
33#include <sys/fcntl.h>
34#include <sys/syslimits.h>
35#include <sys/param.h>
36#include <sys/resource.h>
37#include <asl.h>
38#include <assert.h>
39#include <inttypes.h>
40#include <TargetConditionals.h>
41#include "pathwatch.h"
42#include "notifyd.h"
43#include "service.h"
44#include "pathwatch.h"
45#include "timer.h"
46
47#include "notify_ipc.h"
48#include "notify_private.h"
49
50#define forever for(;;)
51#define IndexNull -1
52
53/* Compile flags */
54#define RUN_TIME_CHECKS
55
56#define CONFIG_FILE_PATH "/etc/notify.conf"
57#define DEBUG_LOG_PATH "/var/log/notifyd.log"
58
59#define STATUS_REQUEST_SHORT 0
60#define STATUS_REQUEST_LONG 1
61
62#define N_NOTIFY_TYPES 6
63
64static int notifyd_token;
65
66static char *status_file = NULL;
67
68typedef union
69{
70	mach_msg_header_t head;
71	union __RequestUnion__notify_ipc_subsystem request;
72} notify_request_msg;
73
74typedef union
75{
76	mach_msg_header_t head;
77	union __ReplyUnion__notify_ipc_subsystem reply;
78} notify_reply_msg;
79
80extern boolean_t notify_ipc_server(mach_msg_header_t *InHeadP, mach_msg_header_t *OutHeadP);
81
82static const char *
83notify_type_name(uint32_t t)
84{
85	switch (t)
86	{
87		case NOTIFY_TYPE_NONE:   return "none  ";
88		case NOTIFY_TYPE_MEMORY: return "memory";
89		case NOTIFY_TYPE_PLAIN:  return "plain ";
90		case NOTIFY_TYPE_PORT:   return "port  ";
91		case NOTIFY_TYPE_FILE:   return "file  ";
92		case NOTIFY_TYPE_SIGNAL: return "signal";
93		default: return "unknown";
94	}
95
96	return "unknown";
97}
98
99static void
100fprint_client(FILE *f, client_t *c)
101{
102	int token;
103
104	if (c == NULL)
105	{
106		fprintf(f, "NULL client\n");
107		return;
108	}
109
110	token = c->client_id;
111
112	fprintf(f, "client_id: %llu\n", c->client_id);
113	fprintf(f, "pid: %d\n", c->pid);
114	fprintf(f, "token: %d\n", token);
115	fprintf(f, "lastval: %u\n", c->lastval);
116	fprintf(f, "suspend_count: %u\n", c->suspend_count);
117	fprintf(f, "type: %s\n", notify_type_name(c->notify_type));
118	switch(c->notify_type)
119	{
120		case NOTIFY_TYPE_NONE:
121			break;
122
123		case NOTIFY_TYPE_PLAIN:
124			break;
125
126		case NOTIFY_TYPE_MEMORY:
127			break;
128
129		case NOTIFY_TYPE_PORT:
130			fprintf(f, "mach port: 0x%08x\n", c->port);
131			break;
132
133		case NOTIFY_TYPE_FILE:
134			fprintf(f, "fd: %d\n", c->fd);
135			break;
136
137		case NOTIFY_TYPE_SIGNAL:
138			fprintf(f, "signal: %d\n", c->sig);
139			break;
140
141		default: break;
142	}
143}
144
145static void
146fprint_quick_client(FILE *f, client_t *c, int pname)
147{
148	int token;
149	if (c == NULL) return;
150
151	token = c->client_id;
152
153	if (pname == 1) fprintf(f, " [%s]", c->name_info->name);
154	fprintf(f, " %u %u", c->pid, token);
155	if (c->suspend_count > 0) fprintf(f, " suspend %d", c->suspend_count);
156	if (c->state & NOTIFY_CLIENT_STATE_PENDING) fprintf(f, " pending");
157	if (c->state & NOTIFY_CLIENT_STATE_TIMEOUT) fprintf(f, " timeout");
158	fprintf(f, " %s", notify_type_name(c->notify_type));
159	if (c->notify_type == NOTIFY_TYPE_SIGNAL) fprintf(f, " %d", c->sig);
160	fprintf(f, "\n");
161}
162
163static void
164fprint_quick_name_info(FILE *f, name_info_t *n)
165{
166	list_t *sl;
167	client_t *c;
168
169	if (n == NULL) return;
170
171	fprintf(f, "\"%s\" uid=%u gid=%u %03x", n->name, n->uid, n->gid, n->access);
172	if (n->slot != -1)
173	{
174		fprintf(f, " slot %u", n->slot);
175		if (global.shared_memory_refcount[n->slot] != -1) fprintf(f, " = %u", global.shared_memory_base[n->slot]);
176	}
177
178	fprintf(f, "\n");
179
180	for (sl = n->subscriptions; sl != NULL; sl = _nc_list_next(sl))
181	{
182		c = _nc_list_data(sl);
183		if (c == NULL) break;
184
185		fprint_quick_client(f, c, 0);
186	}
187
188	fprintf(f, "\n");
189}
190
191static void
192fprint_name_info(FILE *f, const char *name, name_info_t *n, table_t *pid_table, pid_t *max_pid)
193{
194	list_t *sl;
195	client_t *c;
196	uint32_t i, reg[N_NOTIFY_TYPES];
197
198	if (n == NULL)
199	{
200		fprintf(f, "%s unknown\n", name);
201		return;
202	}
203
204	fprintf(f, "name: %s\n", n->name);
205	fprintf(f, "id: %llu\n", n->name_id);
206	fprintf(f, "uid: %u\n", n->uid);
207	fprintf(f, "gid: %u\n", n->gid);
208	fprintf(f, "access: %03x\n", n->access);
209	fprintf(f, "refcount: %u\n", n->refcount);
210	if (n->slot == -1) fprintf(f, "slot: -unassigned-");
211	else
212	{
213		fprintf(f, "slot: %u", n->slot);
214		if (global.shared_memory_refcount[n->slot] != -1)
215			fprintf(f, " = %u (%u)", global.shared_memory_base[n->slot], global.shared_memory_refcount[n->slot]);
216	}
217	fprintf(f, "\n");
218	fprintf(f, "val: %u\n", n->val);
219	fprintf(f, "state: %llu\n", n->state);
220
221	for (i = 0; i < N_NOTIFY_TYPES; i++) reg[i] = 0;
222
223	for (sl = n->subscriptions; sl != NULL; sl = _nc_list_next(sl))
224	{
225		list_t *l;
226
227		c = _nc_list_data(sl);
228		if (c == NULL) break;
229
230		if ((c->pid != (pid_t)-1) && (c->pid > *max_pid)) *max_pid = c->pid;
231
232		l = _nc_table_find_n(pid_table, c->pid);
233		if (l == NULL)
234		{
235			_nc_table_insert_n(pid_table, (uint32_t)c->pid, _nc_list_new(c));
236		}
237		else
238		{
239			_nc_list_concat(l, _nc_list_new(c));
240		}
241
242		switch (c->notify_type)
243		{
244			case NOTIFY_TYPE_MEMORY: reg[1]++; break;
245			case NOTIFY_TYPE_PLAIN:  reg[2]++; break;
246			case NOTIFY_TYPE_PORT:   reg[3]++; break;
247			case NOTIFY_TYPE_FILE:   reg[4]++; break;
248			case NOTIFY_TYPE_SIGNAL: reg[5]++; break;
249			default: reg[0]++;
250		}
251	}
252
253	fprintf(f, "types: none %u   memory %u   plain %u   port %u   file %u   signal %u\n", reg[0], reg[1], reg[2], reg[3], reg[4], reg[5]);
254
255	for (sl = n->subscriptions; sl != NULL; sl = _nc_list_next(sl))
256	{
257		c = _nc_list_data(sl);
258		if (c == NULL) break;
259
260		fprintf(f, "\n");
261		fprint_client(f, c);
262	}
263}
264
265static void
266fprint_quick_status(FILE *f)
267{
268	void *tt;
269	name_info_t *n;
270
271	tt = _nc_table_traverse_start(global.notify_state->name_table);
272
273	while (tt != NULL)
274	{
275		n = _nc_table_traverse(global.notify_state->name_table, tt);
276		if (n == NULL) break;
277		fprint_quick_name_info(f, n);
278	}
279
280	_nc_table_traverse_end(global.notify_state->name_table, tt);
281	fprintf(f, "\n");
282}
283
284static void
285fprint_status(FILE *f)
286{
287	void *tt;
288	name_info_t *n;
289	int32_t i;
290	client_t *c;
291	svc_info_t *info;
292	path_node_t *node;
293	timer_t *timer;
294	table_t *pid_table;
295	pid_t pid, max_pid;
296	uint32_t count;
297	portproc_data_t *pdata;
298
299	pid_table = _nc_table_new(0);
300	max_pid = 0;
301
302	fprintf(f, "--- GLOBALS ---\n");
303	fprintf(f, "%u slots (current id %u)\n", global.nslots, global.slot_id);
304	fprintf(f, "%u log_cutoff (default %u)\n", global.log_cutoff, global.log_default);
305	fprintf(f, "\n");
306
307	fprintf(f, "--- STATISTICS ---\n");
308	fprintf(f, "post         %llu\n", call_statistics.post);
309	fprintf(f, "    id       %llu\n", call_statistics.post_by_id);
310	fprintf(f, "    name     %llu\n", call_statistics.post_by_name);
311	fprintf(f, "    fetch    %llu\n", call_statistics.post_by_name_and_fetch_id);
312	fprintf(f, "    no_op    %llu\n", call_statistics.post_no_op);
313	fprintf(f, "\n");
314	fprintf(f, "register     %llu\n", call_statistics.reg);
315	fprintf(f, "    plain    %llu\n", call_statistics.reg_plain);
316	fprintf(f, "    check    %llu\n", call_statistics.reg_check);
317	fprintf(f, "    signal   %llu\n", call_statistics.reg_signal);
318	fprintf(f, "    file     %llu\n", call_statistics.reg_file);
319	fprintf(f, "    port     %llu\n", call_statistics.reg_port);
320	fprintf(f, "\n");
321	fprintf(f, "check        %llu\n", call_statistics.check);
322	fprintf(f, "cancel       %llu\n", call_statistics.cancel);
323	fprintf(f, "cleanup      %llu\n", call_statistics.cleanup);
324	fprintf(f, "regenerate   %llu\n", call_statistics.regenerate);
325	fprintf(f, "\n");
326	fprintf(f, "suspend      %llu\n", call_statistics.suspend);
327	fprintf(f, "resume       %llu\n", call_statistics.resume);
328	fprintf(f, "suspend_pid  %llu\n", call_statistics.suspend_pid);
329	fprintf(f, "resume_pid   %llu\n", call_statistics.resume_pid);
330	fprintf(f, "\n");
331	fprintf(f, "get_state    %llu\n", call_statistics.get_state);
332	fprintf(f, "    id       %llu\n", call_statistics.get_state_by_id);
333	fprintf(f, "    client   %llu\n", call_statistics.get_state_by_client);
334	fprintf(f, "    fetch    %llu\n", call_statistics.get_state_by_client_and_fetch_id);
335	fprintf(f, "\n");
336	fprintf(f, "set_state    %llu\n", call_statistics.set_state);
337	fprintf(f, "    id       %llu\n", call_statistics.set_state_by_id);
338	fprintf(f, "    client   %llu\n", call_statistics.set_state_by_client);
339	fprintf(f, "    fetch    %llu\n", call_statistics.set_state_by_client_and_fetch_id);
340	fprintf(f, "\n");
341	fprintf(f, "get_owner    %llu\n", call_statistics.get_owner);
342	fprintf(f, "set_owner    %llu\n", call_statistics.set_owner);
343	fprintf(f, "\n");
344	fprintf(f, "get_access   %llu\n", call_statistics.get_access);
345	fprintf(f, "set_access   %llu\n", call_statistics.set_access);
346	fprintf(f, "\n");
347	fprintf(f, "monitor      %llu\n", call_statistics.monitor_file);
348	fprintf(f, "svc_path     %llu\n", call_statistics.service_path);
349	fprintf(f, "svc_timer    %llu\n", call_statistics.service_timer);
350
351	fprintf(f, "\n");
352	fprintf(f, "name         alloc %9u   free %9u   extant %9u\n", global.notify_state->stat_name_alloc , global.notify_state->stat_name_free, global.notify_state->stat_name_alloc - global.notify_state->stat_name_free);
353	fprintf(f, "subscription alloc %9u   free %9u   extant %9u\n", global.notify_state->stat_client_alloc , global.notify_state->stat_client_free, global.notify_state->stat_client_alloc - global.notify_state->stat_client_free);
354	fprintf(f, "portproc     alloc %9u   free %9u   extant %9u\n", global.notify_state->stat_portproc_alloc , global.notify_state->stat_portproc_free, global.notify_state->stat_portproc_alloc - global.notify_state->stat_portproc_free);
355	fprintf(f, "\n");
356
357	count = 0;
358	tt = _nc_table_traverse_start(global.notify_state->port_table);
359	while (tt != NULL)
360	{
361		pdata = _nc_table_traverse(global.notify_state->port_table, tt);
362		if (pdata == NULL) break;
363		count++;
364	}
365	_nc_table_traverse_end(global.notify_state->port_table, tt);
366	fprintf(f, "port count   %u\n", count);
367
368	count = 0;
369	tt = _nc_table_traverse_start(global.notify_state->proc_table);
370	while (tt != NULL)
371	{
372		pdata = _nc_table_traverse(global.notify_state->proc_table, tt);
373		if (pdata == NULL) break;
374		count++;
375	}
376	_nc_table_traverse_end(global.notify_state->proc_table, tt);
377	fprintf(f, "proc count   %u\n", count);
378	fprintf(f, "\n");
379
380	fprintf(f, "--- NAME TABLE ---\n");
381	count = 0;
382	tt = _nc_table_traverse_start(global.notify_state->name_table);
383
384	while (tt != NULL)
385	{
386		n = _nc_table_traverse(global.notify_state->name_table, tt);
387		if (n == NULL) break;
388		fprint_name_info(f, n->name, n, pid_table, &max_pid);
389		fprintf(f, "\n");
390		count++;
391	}
392
393	fprintf(f, "--- NAME COUNT %u ---\n", count);
394	_nc_table_traverse_end(global.notify_state->name_table, tt);
395	fprintf(f, "\n");
396
397	fprintf(f, "--- SUBSCRIPTION TABLE ---\n");
398	count = 0;
399	tt = _nc_table_traverse_start(global.notify_state->client_table);
400
401	while (tt != NULL)
402	{
403		c = _nc_table_traverse(global.notify_state->client_table, tt);
404		if (c == NULL) break;
405		fprint_quick_client(f, c, 1);
406		count++;
407	}
408
409	fprintf(f, "--- SUBSCRIPTION COUNT %u ---\n", count);
410	_nc_table_traverse_end(global.notify_state->client_table, tt);
411	fprintf(f, "\n");
412
413	fprintf(f, "--- CONTROLLED NAME ---\n");
414	for (i = 0; i < global.notify_state->controlled_name_count; i++)
415	{
416		fprintf(f, "%s %u %u %03x\n", global.notify_state->controlled_name[i]->name, global.notify_state->controlled_name[i]->uid, global.notify_state->controlled_name[i]->gid, global.notify_state->controlled_name[i]->access);
417	}
418	fprintf(f, "--- CONTROLLED NAME COUNT %u ---\n", global.notify_state->controlled_name_count);
419	fprintf(f, "\n");
420
421	fprintf(f, "--- PUBLIC SERVICE ---\n");
422	count = 0;
423	tt = _nc_table_traverse_start(global.notify_state->name_table);
424	while (tt != NULL)
425	{
426		n = _nc_table_traverse(global.notify_state->name_table, tt);
427		if (n == NULL) break;
428		if (n->private == NULL) continue;
429
430		count++;
431		info = (svc_info_t *)n->private;
432
433		if (info->type == 0)
434		{
435			fprintf(f, "Null service: %s\n", n->name);
436		}
437		if (info->type == SERVICE_TYPE_PATH_PUBLIC)
438		{
439			node = (path_node_t *)info->private;
440			fprintf(f, "Path Service: %s <- %s\n", n->name, node->path);
441		}
442		else if (info->type == SERVICE_TYPE_TIMER_PUBLIC)
443		{
444			timer = (timer_t *)info->private;
445			switch (timer->type)
446			{
447				case TIME_EVENT_ONESHOT:
448				{
449					fprintf(f, "Time Service: %s <- Oneshot %llu\n", n->name, timer->start);
450					break;
451				}
452				case TIME_EVENT_CLOCK:
453				{
454					fprintf(f, "Time Service: %s <- Clock start %lld freq %u end %lld\n", n->name, timer->start, timer->freq, timer->end);
455					break;
456				}
457				case TIME_EVENT_CAL:
458				{
459					fprintf(f, "Time Service: %s <- Calendar start %lld freq %u end %lld day %d\n", n->name, timer->start, timer->freq, timer->end, timer->day);
460					break;
461				}
462			}
463		}
464		else
465		{
466			fprintf(f, "Unknown service: %s (%u)\n", n->name, info->type);
467		}
468	}
469
470	fprintf(f, "--- PUBLIC SERVICE COUNT %u ---\n", count);
471	_nc_table_traverse_end(global.notify_state->name_table, tt);
472	fprintf(f, "\n");
473
474	fprintf(f, "--- PRIVATE SERVICE ---\n");
475	count = 0;
476	tt = _nc_table_traverse_start(global.notify_state->client_table);
477	while (tt != NULL)
478	{
479		c = _nc_table_traverse(global.notify_state->client_table, tt);
480		if (c == NULL) break;
481		if (c->private == NULL) continue;
482
483		count++;
484		info = (svc_info_t *)c->private;
485		n = c->name_info;
486
487		if (info->type == 0)
488		{
489			fprintf(f, "PID %u Null service: %s\n", c->pid, n->name);
490		}
491		if (info->type == SERVICE_TYPE_PATH_PRIVATE)
492		{
493			node = (path_node_t *)info->private;
494			fprintf(f, "PID %u Path Service: %s <- %s (UID %d GID %d)\n", c->pid, n->name, node->path, node->uid, node->gid);
495		}
496		else if (info->type == SERVICE_TYPE_TIMER_PRIVATE)
497		{
498			timer = (timer_t *)info->private;
499			switch (timer->type)
500			{
501				case TIME_EVENT_ONESHOT:
502				{
503					fprintf(f, "PID %u Time Service: %s <- Oneshot %"PRId64"\n", c->pid, n->name, timer->start);
504					break;
505				}
506				case TIME_EVENT_CLOCK:
507				{
508					fprintf(f, "PID %u Time Service: %s <- Clock start %"PRId64" freq %"PRIu32" end %"PRId64"\n", c->pid, n->name, timer->start, timer->freq, timer->end);
509					break;
510				}
511				case TIME_EVENT_CAL:
512				{
513					fprintf(f, "PID %u Time Service: %s <- Calendar start %"PRId64" freq %"PRIu32" end %"PRId64" day %"PRId32"\n", c->pid, n->name, timer->start, timer->freq, timer->end, timer->day);
514					break;
515				}
516			}
517		}
518	}
519
520	fprintf(f, "--- PRIVATE SERVICE COUNT %u ---\n", count);
521	_nc_table_traverse_end(global.notify_state->client_table, tt);
522	fprintf(f, "\n");
523
524	fprintf(f, "--- PROCESSES ---\n");
525	for (pid = 0; pid <= max_pid; pid++)
526	{
527		int mem_count, plain_count, file_count, port_count, sig_count, com_port_count;
528		mach_port_t common_port = MACH_PORT_NULL;
529
530		list_t *x;
531		list_t *l = _nc_table_find_n(pid_table, pid);
532		if (l == NULL) continue;
533
534		mem_count = 0;
535		plain_count = 0;
536		file_count = 0;
537		port_count = 0;
538		sig_count = 0;
539		com_port_count = 0;
540
541		for (x = l; x != NULL; x = _nc_list_next(x))
542		{
543			c = _nc_list_data(x);
544			if (c != NULL)
545			{
546				if ((c->notify_type == NOTIFY_TYPE_PORT) && (!strcmp(c->name_info->name, COMMON_PORT_KEY))) common_port = c->port;
547			}
548		}
549
550		for (x = l; x != NULL; x = _nc_list_next(x))
551		{
552			c = _nc_list_data(x);
553			if (c != NULL)
554			{
555				switch(c->notify_type)
556				{
557					case NOTIFY_TYPE_NONE:
558						break;
559
560					case NOTIFY_TYPE_PLAIN:
561						plain_count++;
562						break;
563
564					case NOTIFY_TYPE_MEMORY:
565						mem_count++;
566						break;
567
568					case NOTIFY_TYPE_PORT:
569						port_count++;
570						if (c->port == common_port) com_port_count++;
571						break;
572
573					case NOTIFY_TYPE_FILE:
574						file_count++;
575						break;
576
577					case NOTIFY_TYPE_SIGNAL:
578						sig_count++;
579						break;
580
581					default: break;
582				}
583			}
584		}
585
586		fprintf(f, "pid: %u   ", pid);
587		if (file_count + sig_count == 0)
588		{
589			if (port_count == 0) fprintf(f, "regenerable / polling\n");
590			else if (port_count == com_port_count) fprintf(f, "regenerable\n");
591			else if (com_port_count > 0) fprintf(f, "partially regenerable\n");
592			else fprintf(f, "non-regenerable\n");
593		}
594		else
595		{
596			if (com_port_count == 0) fprintf(f, "non-regenerable\n");
597			else fprintf(f, "partially regenerable\n");
598		}
599
600		fprintf(f, "memory %u   plain %u   port %u   file %u   signal %u   common port %u\n", mem_count, plain_count, port_count, file_count, sig_count, com_port_count);
601		for (x = l; x != NULL; x = _nc_list_next(x))
602		{
603			c = _nc_list_data(x);
604			if (c != NULL)
605			{
606				fprintf(f, "  %s: %s\n", notify_type_name(c->notify_type), c->name_info->name);
607			}
608		}
609
610		fprintf(f, "\n");
611		_nc_list_release_list(l);
612	}
613	fprintf(f, "\n");
614	_nc_table_free(pid_table);
615}
616
617void
618dump_status(uint32_t level)
619{
620	FILE *f;
621
622	if (status_file == NULL)
623	{
624		asprintf(&status_file, "/var/run/notifyd_%u.status", getpid());
625		if (status_file == NULL) return;
626	}
627
628	unlink(status_file);
629	f = fopen(status_file, "w");
630	if (f == NULL) return;
631
632	if (level == STATUS_REQUEST_SHORT) fprint_quick_status(f);
633	else if (level == STATUS_REQUEST_LONG) fprint_status(f);
634
635	fclose(f);
636}
637
638void
639log_message(int priority, const char *str, ...)
640{
641	time_t t;
642	char now[32];
643	va_list ap;
644
645	if (priority > global.log_cutoff) return;
646
647	va_start(ap, str);
648
649	if (global.log_file != NULL)
650	{
651		t = time(NULL);
652		memset(now, 0, 32);
653		strftime(now, 32, "%b %e %T", localtime(&t));
654
655		fprintf(global.log_file, "%s: ", now);
656		vfprintf(global.log_file, str, ap);
657		fflush(global.log_file);
658	}
659	else
660	{
661		vfprintf(stderr, str, ap);
662	}
663
664	va_end(ap);
665}
666
667uint32_t
668daemon_post(const char *name, uint32_t u, uint32_t g)
669{
670	name_info_t *n;
671	uint32_t status;
672
673	if (name == NULL) return 0;
674
675	n = (name_info_t *)_nc_table_find(global.notify_state->name_table, name);
676	if (n == NULL) return 0;
677
678	if (n->slot != (uint32_t)-1) global.shared_memory_base[n->slot]++;
679
680	status = _notify_lib_post(global.notify_state, name, u, g);
681	return status;
682}
683
684uint32_t
685daemon_post_nid(uint64_t nid, uint32_t u, uint32_t g)
686{
687	name_info_t *n;
688	uint32_t status;
689
690	n = (name_info_t *)_nc_table_find_64(global.notify_state->name_id_table, nid);
691	if (n == NULL) return 0;
692
693	if (n->slot != (uint32_t)-1) global.shared_memory_base[n->slot]++;
694
695	status = _notify_lib_post_nid(global.notify_state, nid, u, g);
696	return status;
697}
698
699void
700daemon_post_client(uint64_t cid)
701{
702	client_t *c;
703
704	c = _nc_table_find_64(global.notify_state->client_table, cid);
705	if (c == NULL) return;
706
707	if ((c->notify_type == NOTIFY_TYPE_MEMORY) && (c->name_info != NULL) && (c->name_info->slot != (uint32_t)-1))
708	{
709		global.shared_memory_base[c->name_info->slot]++;
710	}
711
712	_notify_lib_post_client(global.notify_state, c);
713}
714
715void
716daemon_set_state(const char *name, uint64_t val)
717{
718	name_info_t *n;
719
720	if (name == NULL) return;
721
722	n = (name_info_t *)_nc_table_find(global.notify_state->name_table, name);
723	if (n == NULL) return;
724
725	n->state = val;
726}
727
728static void
729init_launch_config(const char *name)
730{
731	launch_data_t tmp, pdict;
732
733	tmp = launch_data_new_string(LAUNCH_KEY_CHECKIN);
734	global.launch_dict = launch_msg(tmp);
735	launch_data_free(tmp);
736
737	if (global.launch_dict == NULL)
738	{
739		fprintf(stderr, "%d launchd checkin failed\n", getpid());
740		exit(1);
741	}
742
743	tmp = launch_data_dict_lookup(global.launch_dict, LAUNCH_JOBKEY_MACHSERVICES);
744	if (tmp == NULL)
745	{
746		fprintf(stderr, "%d launchd lookup of LAUNCH_JOBKEY_MACHSERVICES failed\n", getpid());
747		exit(1);
748	}
749
750	pdict = launch_data_dict_lookup(tmp, name);
751	if (pdict == NULL)
752	{
753		fprintf(stderr, "%d launchd lookup of name %s failed\n", getpid(), name);
754		exit(1);
755	}
756
757	global.server_port = launch_data_get_machport(pdict);
758	if (global.server_port == MACH_PORT_NULL)
759	{
760		fprintf(stderr, "%d launchd lookup of server port for name %s failed\n", getpid(), name);
761		exit(1);
762	}
763}
764
765static void
766string_list_free(char **l)
767{
768	int i;
769
770	if (l == NULL) return;
771	for (i = 0; l[i] != NULL; i++)
772	{
773		if (l[i] != NULL) free(l[i]);
774		l[i] = NULL;
775	}
776	free(l);
777}
778
779static char **
780string_insert(char *s, char **l, unsigned int x)
781{
782	int i, len;
783
784	if (s == NULL) return l;
785	if (l == NULL)
786	{
787		l = (char **)malloc(2 * sizeof(char *));
788		l[0] = strdup(s);
789		l[1] = NULL;
790		return l;
791	}
792
793	for (i = 0; l[i] != NULL; i++);
794	len = i + 1; /* count the NULL on the end of the list too! */
795
796	l = (char **)realloc(l, (len + 1) * sizeof(char *));
797
798	if ((x >= (len - 1)) || (x == IndexNull))
799	{
800		l[len - 1] = strdup(s);
801		l[len] = NULL;
802		return l;
803	}
804
805	for (i = len; i > x; i--) l[i] = l[i - 1];
806	l[x] = strdup(s);
807	return l;
808}
809
810static char **
811string_append(char *s, char **l)
812{
813	return string_insert(s, l, IndexNull);
814}
815
816static unsigned int
817string_list_length(char **l)
818{
819	int i;
820
821	if (l == NULL) return 0;
822	for (i = 0; l[i] != NULL; i++);
823	return i;
824}
825
826static unsigned int
827string_index(char c, char *s)
828{
829	int i;
830	char *p;
831
832	if (s == NULL) return IndexNull;
833
834	for (i = 0, p = s; p[0] != '\0'; p++, i++)
835	{
836		if (p[0] == c) return i;
837	}
838
839	return IndexNull;
840}
841
842static char **
843explode(char *s, char *delim)
844{
845	char **l = NULL;
846	char *p, *t;
847	int i, n;
848
849	if (s == NULL) return NULL;
850
851	p = s;
852	while (p[0] != '\0')
853	{
854		for (i = 0; ((p[i] != '\0') && (string_index(p[i], delim) == IndexNull)); i++);
855		n = i;
856		t = malloc(n + 1);
857		for (i = 0; i < n; i++) t[i] = p[i];
858		t[n] = '\0';
859		l = string_append(t, l);
860		free(t);
861		t = NULL;
862		if (p[i] == '\0') return l;
863		if (p[i + 1] == '\0') l = string_append("", l);
864		p = p + i + 1;
865	}
866	return l;
867}
868
869static uint32_t
870atoaccess(char *s)
871{
872	uint32_t a;
873
874	if (s == NULL) return 0;
875	if (strlen(s) != 6) return 0;
876
877	a = 0;
878	if (s[0] == 'r') a |= (NOTIFY_ACCESS_READ << NOTIFY_ACCESS_USER_SHIFT);
879	if (s[1] == 'w') a |= (NOTIFY_ACCESS_WRITE << NOTIFY_ACCESS_USER_SHIFT);
880
881	if (s[2] == 'r') a |= (NOTIFY_ACCESS_READ << NOTIFY_ACCESS_GROUP_SHIFT);
882	if (s[3] == 'w') a |= (NOTIFY_ACCESS_WRITE << NOTIFY_ACCESS_GROUP_SHIFT);
883
884	if (s[4] == 'r') a |= (NOTIFY_ACCESS_READ << NOTIFY_ACCESS_OTHER_SHIFT);
885	if (s[5] == 'w') a |= (NOTIFY_ACCESS_WRITE << NOTIFY_ACCESS_OTHER_SHIFT);
886
887	return a;
888}
889
890static void
891init_config()
892{
893	FILE *f;
894	struct stat sb;
895	char line[1024];
896	char **args;
897	uint32_t status, argslen;
898	uint32_t uid, gid, access;
899	uint64_t nid, val64;
900
901	/*
902	 * Set IPC Version Number & PID
903	 */
904	val64 = getpid();
905	val64 <<= 32;
906	val64 |= NOTIFY_IPC_VERSION;
907
908	_notify_lib_register_plain(global.notify_state, NOTIFY_IPC_VERSION_NAME, -1, notifyd_token++, -1, 0, 0, &nid);
909	_notify_lib_set_state(global.notify_state, nid, val64, 0, 0);
910
911	/* Check config file */
912	if (stat(CONFIG_FILE_PATH, &sb) != 0) return;
913
914	if (sb.st_uid != 0)
915	{
916		log_message(ASL_LEVEL_ERR, "config file %s not owned by root: ignored\n", CONFIG_FILE_PATH);
917		return;
918	}
919
920	if (sb.st_mode & 02)
921	{
922		log_message(ASL_LEVEL_ERR, "config file %s is world-writable: ignored\n", CONFIG_FILE_PATH);
923		return;
924	}
925
926	/* Read config file */
927	f = fopen(CONFIG_FILE_PATH, "r");
928	if (f == NULL) return;
929
930	forever
931	{
932		if (fgets(line, 1024, f) == NULL) break;
933		if (line[0] == '\0') continue;
934		if (line[0] == '#') continue;
935
936		line[strlen(line) - 1] = '\0';
937		args = explode(line, "\t ");
938		argslen = string_list_length(args);
939		if (argslen == 0) continue;
940
941		if (!strcasecmp(args[0], "monitor"))
942		{
943			if (argslen < 3)
944			{
945				string_list_free(args);
946				continue;
947			}
948			_notify_lib_register_plain(global.notify_state, args[1], -1, notifyd_token++, -1, 0, 0, &nid);
949			service_open_path(args[1], args[2], 0, 0);
950		}
951
952		if (!strcasecmp(args[0], "timer"))
953		{
954			if (argslen < 3)
955			{
956				string_list_free(args);
957				continue;
958			}
959			_notify_lib_register_plain(global.notify_state, args[1], -1, notifyd_token++, -1, 0, 0, &nid);
960			status = service_open_timer(args[1], args[2]);
961		}
962
963		else if (!strcasecmp(args[0], "set"))
964		{
965			if (argslen < 3)
966			{
967				string_list_free(args);
968				continue;
969			}
970
971			val64 = atoll(args[2]);
972
973			_notify_lib_register_plain(global.notify_state, args[1], -1, notifyd_token++, -1, 0, 0, &nid);
974			_notify_lib_set_state(global.notify_state, nid, val64, 0, 0);
975		}
976
977		else if (!strcasecmp(args[0], "reserve"))
978		{
979			if (argslen == 1)
980			{
981				string_list_free(args);
982				continue;
983			}
984
985			uid = 0;
986			gid = 0;
987			access = NOTIFY_ACCESS_DEFAULT;
988
989			if (argslen > 2) uid = atoi(args[2]);
990			if (argslen > 3) gid = atoi(args[3]);
991			if (argslen > 4) access = atoaccess(args[4]);
992
993			if ((uid != 0) || (gid != 0)) _notify_lib_set_owner(global.notify_state, args[1], uid, gid);
994			if (access != NOTIFY_ACCESS_DEFAULT) _notify_lib_set_access(global.notify_state, args[1], access);
995		}
996		else if (!strcasecmp(args[0], "quit"))
997		{
998			string_list_free(args);
999			break;
1000		}
1001
1002		string_list_free(args);
1003	}
1004
1005	fclose(f);
1006}
1007
1008static void
1009service_mach_message(bool blocking)
1010{
1011	__block kern_return_t status;
1012	uint32_t rbits, sbits;
1013	notify_request_msg *request;
1014	notify_reply_msg *reply;
1015	char rbuf[sizeof(notify_request_msg) + MAX_TRAILER_SIZE];
1016	char sbuf[sizeof(notify_reply_msg) + MAX_TRAILER_SIZE];
1017
1018	forever
1019	{
1020		memset(rbuf, 0, sizeof(rbuf));
1021		memset(sbuf, 0, sizeof(sbuf));
1022
1023		request = (notify_request_msg *)rbuf;
1024		reply = (notify_reply_msg *)sbuf;
1025
1026		request->head.msgh_local_port = global.server_port;
1027		request->head.msgh_size = global.request_size;
1028
1029		rbits = MACH_RCV_MSG | (blocking ? 0 : MACH_RCV_TIMEOUT) | MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AUDIT) | MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0);
1030		sbits = MACH_SEND_MSG;
1031
1032		status = mach_msg(&(request->head), rbits, 0, global.request_size, global.server_port, 0, MACH_PORT_NULL);
1033		if (status != KERN_SUCCESS) return;
1034
1035#if TARGET_OS_EMBEDDED
1036		/* Synchronize with work_q since  on embedded main() calls this
1037		 * from the global concurrent queue. */
1038		dispatch_sync(global.work_q, ^{
1039			status = notify_ipc_server(&(request->head), &(reply->head));
1040		});
1041#else
1042		status = notify_ipc_server(&(request->head), &(reply->head));
1043#endif
1044		if (!status && (request->head.msgh_bits & MACH_MSGH_BITS_COMPLEX))
1045		{
1046			/* destroy the request - but not the reply port */
1047			request->head.msgh_remote_port = MACH_PORT_NULL;
1048			mach_msg_destroy(&(request->head));
1049		}
1050		if (reply->head.msgh_remote_port)
1051		{
1052			status = mach_msg(&(reply->head), sbits, reply->head.msgh_size, 0, MACH_PORT_NULL, 0, MACH_PORT_NULL);
1053			if (status == MACH_SEND_INVALID_DEST || status == MACH_SEND_TIMED_OUT)
1054			{
1055				/* deallocate reply port rights not consumed by failed mach_msg() send */
1056				mach_msg_destroy(&(reply->head));
1057			}
1058		}
1059	}
1060}
1061
1062static int32_t
1063open_shared_memory(const char *name)
1064{
1065	int32_t shmfd, isnew;
1066	uint32_t size;
1067
1068	size = global.nslots * sizeof(uint32_t);
1069
1070	isnew = 1;
1071	shmfd = shm_open(name, O_RDWR, 0644);
1072	if (shmfd != -1)
1073	{
1074		isnew = 0;
1075	}
1076	else
1077	{
1078		shmfd = shm_open(name, O_RDWR | O_CREAT, 0644);
1079	}
1080
1081	if (shmfd == -1)
1082	{
1083		fprintf(stderr, "shm_open %s failed: %s\n", name, strerror(errno));
1084		return -1;
1085	}
1086
1087	ftruncate(shmfd, size);
1088	global.shared_memory_base = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, shmfd, 0);
1089	close(shmfd);
1090
1091	if (isnew == 0)
1092	{
1093		global.last_shm_base = malloc(size);
1094		if (global.last_shm_base != NULL) memcpy(global.last_shm_base, global.shared_memory_base, size);
1095	}
1096
1097	memset(global.shared_memory_base, 0, size);
1098	global.shared_memory_refcount = (uint32_t *)malloc(size);
1099	if (global.shared_memory_refcount == NULL) return -1;
1100
1101	memset(global.shared_memory_refcount, 0, size);
1102
1103	/* slot 0 is notifyd's pid */
1104	global.shared_memory_base[0] = getpid();
1105	global.shared_memory_refcount[0] = 1;
1106	global.slot_id = 0;
1107
1108	return 0;
1109}
1110
1111int
1112main(int argc, const char *argv[])
1113{
1114	dispatch_queue_t main_q;
1115	const char *service_name;
1116	const char *shm_name;
1117	uint32_t i, status;
1118	struct rlimit rlim;
1119
1120	service_name = NOTIFY_SERVICE_NAME;
1121	shm_name = SHM_ID;
1122
1123#ifdef PORT_DEBUG
1124	debug_log_file = fopen("/var/log/notifyd.log", "a");
1125#endif
1126
1127	notify_set_options(NOTIFY_OPT_DISABLE);
1128
1129	/* remove limit of number of file descriptors */
1130	rlim.rlim_max = RLIM_INFINITY;
1131	rlim.rlim_cur = MIN(OPEN_MAX, rlim.rlim_max);
1132	setrlimit(RLIMIT_NOFILE, &rlim);
1133
1134	signal(SIGPIPE, SIG_IGN);
1135	signal(SIGHUP, SIG_IGN);
1136	signal(SIGUSR1, SIG_IGN);
1137	signal(SIGUSR2, SIG_IGN);
1138	signal(SIGWINCH, SIG_IGN);
1139
1140	memset(&call_statistics, 0, sizeof(struct call_statistics_s));
1141
1142	global.request_size = sizeof(notify_request_msg) + MAX_TRAILER_SIZE;
1143	global.reply_size = sizeof(notify_reply_msg) + MAX_TRAILER_SIZE;
1144	global.nslots = getpagesize() / sizeof(uint32_t);
1145	global.notify_state = _notify_lib_notify_state_new(NOTIFY_STATE_ENABLE_RESEND, 1024);
1146	global.log_cutoff = ASL_LEVEL_NOTICE;
1147	global.log_file = NULL;
1148	global.slot_id = (uint32_t)-1;
1149
1150	for (i = 1; i < argc; i++)
1151	{
1152		if (!strcmp(argv[i], "-d"))
1153		{
1154			global.log_cutoff = ASL_LEVEL_DEBUG;
1155		}
1156		else if (!strcmp(argv[i], "-log_cutoff"))
1157		{
1158			global.log_cutoff = atoi(argv[++i]);
1159		}
1160		else if (!strcmp(argv[i], "-log_file"))
1161		{
1162			if (global.log_file != NULL) fclose(global.log_file);
1163			global.log_file = fopen(argv[++i], "a");
1164		}
1165		else if (!strcmp(argv[i], "-service"))
1166		{
1167			service_name = argv[++i];
1168		}
1169		else if (!strcmp(argv[i], "-shm"))
1170		{
1171			shm_name = argv[++i];
1172		}
1173		else if (!strcmp(argv[i], "-shm_pages"))
1174		{
1175			global.nslots = atoi(argv[++i]) * (getpagesize() / sizeof(uint32_t));
1176		}
1177	}
1178
1179	global.log_default = global.log_cutoff;
1180
1181	if (global.log_file == NULL)
1182	{
1183		global.log_file = fopen(DEBUG_LOG_PATH, "a");
1184	}
1185
1186	log_message(ASL_LEVEL_DEBUG, "--------------------\n");
1187	log_message(ASL_LEVEL_DEBUG, "notifyd start PID %u\n", getpid());
1188
1189	init_launch_config(service_name);
1190
1191	if (global.nslots > 0)
1192	{
1193		status = open_shared_memory(shm_name);
1194		assert(status == 0);
1195	}
1196
1197	/* init from config file before starting the work queue */
1198	init_config();
1199
1200	main_q = dispatch_get_main_queue();
1201	assert(main_q != NULL);
1202
1203	global.work_q = dispatch_queue_create("WorkQ", NULL);
1204	assert(global.work_q != NULL);
1205
1206#if TARGET_OS_EMBEDDED
1207	/* Block a thread in mach_msg() to avoid the syscall overhead of frequent
1208	 * dispatch source wakeup, and synchronize with work_q after message
1209	 * reception in service_mach_message(). <rdar://problem/8785140> */
1210	dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
1211		forever service_mach_message(true);
1212	});
1213#else
1214	global.mach_src = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV, global.server_port, 0, global.work_q);
1215	assert(global.mach_src != NULL);
1216
1217	dispatch_source_set_event_handler(global.mach_src, ^{
1218		service_mach_message(false);
1219	});
1220	dispatch_resume(global.mach_src);
1221#endif
1222
1223	/* Set up SIGUSR1 */
1224	global.sig_usr1_src = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, (uintptr_t)SIGUSR1, 0, main_q);
1225	assert(global.sig_usr1_src != NULL);
1226	dispatch_source_set_event_handler(global.sig_usr1_src, ^{
1227		dispatch_async(global.work_q, ^{ dump_status(STATUS_REQUEST_SHORT); });
1228	});
1229	dispatch_resume(global.sig_usr1_src);
1230
1231	/* Set up SIGUSR2 */
1232	global.sig_usr2_src = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, (uintptr_t)SIGUSR2, 0, main_q);
1233	assert(global.sig_usr2_src != NULL);
1234	dispatch_source_set_event_handler(global.sig_usr2_src, ^{
1235		dispatch_async(global.work_q, ^{ dump_status(STATUS_REQUEST_LONG); });
1236	});
1237	dispatch_resume(global.sig_usr2_src);
1238
1239	/* Set up SIGWINCH */
1240	global.sig_winch_src = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, (uintptr_t)SIGWINCH, 0, main_q);
1241	assert(global.sig_winch_src != NULL);
1242	dispatch_source_set_event_handler(global.sig_winch_src, ^{
1243		if (global.log_cutoff == ASL_LEVEL_DEBUG) global.log_cutoff = global.log_default;
1244		else global.log_cutoff = ASL_LEVEL_DEBUG;
1245	});
1246	dispatch_resume(global.sig_winch_src);
1247
1248	dispatch_main();
1249
1250	/* NOTREACHED */
1251	return 0;
1252}
1253