1/*
2 * Copyright (c) 2004-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 <TargetConditionals.h>
25
26#if TARGET_IPHONE_SIMULATOR
27struct _not_empty;
28#else
29
30#include <sys/types.h>
31#include <sys/stat.h>
32#include <sys/socket.h>
33#include <netinet/in.h>
34#include <netinet/tcp.h>
35#include <arpa/inet.h>
36#include <sys/un.h>
37#include <sys/uio.h>
38#include <stdio.h>
39#include <stdlib.h>
40#include <string.h>
41#include <fcntl.h>
42#include <errno.h>
43#include <unistd.h>
44#include <netdb.h>
45#include <pthread.h>
46#include <notify.h>
47#include "daemon.h"
48
49#define forever for(;;)
50
51#define MY_ID "remote"
52#define MAXLINE 4096
53#define LOCKDOWN_PATH "/var/run/lockdown"
54#define SYSLOG_SOCK_PATH "/var/run/lockdown/syslog.sock"
55#define ASL_REMOTE_PORT 203
56
57#define PRINT_STD 0
58#define PRINT_RAW 1
59
60#define WATCH_OFF 0
61#define WATCH_LOCKDOWN_START 1
62#define WATCH_RUN 2
63
64#define SESSION_FLAGS_LOCKDOWN 0x00000001
65
66#define MAXSOCK 1
67
68static int rfd4 = -1;
69static int rfd6 = -1;
70static int rfdl = -1;
71
72static dispatch_source_t in_src_local;
73static dispatch_source_t in_src_tcp;
74static dispatch_queue_t in_queue;
75
76#ifdef NSS32
77typedef uint32_t notify_state_t;
78extern int notify_set_state(int, notify_state_t);
79#else
80typedef uint64_t notify_state_t;
81#endif
82
83extern char *asl_list_to_string(asl_search_result_t *list, uint32_t *outlen);
84extern size_t asl_memory_size(asl_memory_t *s);
85extern uint32_t db_query(aslresponse query, aslresponse *res, uint64_t startid, int count, int flags, uint64_t *lastid, int32_t ruid, int32_t rgid, int raccess);
86
87extern void add_lockdown_session(int fd);
88extern void remove_lockdown_session(int fd);
89
90#define SESSION_WRITE(f,x) if (write(f, x, strlen(x)) < 0) goto exit_session
91
92typedef struct
93{
94	int sock;
95	uint32_t flags;
96} session_args_t;
97
98uint32_t
99remote_db_size(uint32_t sel)
100{
101	if (sel == DB_TYPE_FILE) return global.db_file_max;
102	if (sel == DB_TYPE_MEMORY) return global.db_memory_max;
103	if (sel == DB_TYPE_MINI) return global.db_mini_max;
104	return 0;
105}
106
107uint32_t
108remote_db_set_size(uint32_t sel, uint32_t size)
109{
110	if (sel == DB_TYPE_FILE) global.db_file_max = size;
111	if (sel == DB_TYPE_MEMORY) global.db_memory_max = size;
112	if (sel == DB_TYPE_MINI) global.db_mini_max = size;
113	return 0;
114}
115
116aslmsg
117remote_db_stats(uint32_t sel)
118{
119	aslmsg m;
120	m = NULL;
121
122	if (sel == DB_TYPE_FILE) asl_store_statistics(global.file_db, &m);
123	if (sel == DB_TYPE_MEMORY) asl_memory_statistics(global.memory_db, &m);
124	if (sel == DB_TYPE_MINI) asl_mini_memory_statistics(global.mini_db, &m);
125	return m;
126}
127
128void
129session(void *x)
130{
131	int i, s, wfd, status, pfmt, watch, wtoken, nfd, do_prompt;
132	aslresponse res;
133	asl_search_result_t ql;
134	uint32_t outlen;
135	aslmsg stats;
136	asl_msg_t *query;
137	asl_msg_t *qlq[1];
138	char str[1024], *p, *qs, *out;
139	ssize_t len;
140	fd_set readfds, errfds;
141	uint64_t low_id, high_id;
142	uint32_t dbselect, flags;
143	session_args_t *sp;
144
145	if (x == NULL) pthread_exit(NULL);
146
147	sp = (session_args_t *)x;
148	s = sp->sock;
149	flags = sp->flags;
150	free(x);
151
152	asldebug("%s %d: starting interactive session for %ssocket %d\n", MY_ID, s, (flags & SESSION_FLAGS_LOCKDOWN) ? "lockdown " : "", s);
153
154	do_prompt = 1;
155	watch = WATCH_OFF;
156	wfd = -1;
157	wtoken = -1;
158
159	dbselect = 0;
160	if (global.dbtype & DB_TYPE_MEMORY) dbselect = DB_TYPE_MEMORY;
161	else if (global.dbtype & DB_TYPE_MINI) dbselect = DB_TYPE_MINI;
162	else if (global.dbtype & DB_TYPE_FILE) dbselect = DB_TYPE_FILE;
163
164	low_id = 0;
165	high_id = 0;
166
167	pfmt = PRINT_STD;
168	query = NULL;
169	memset(&ql, 0, sizeof(asl_search_result_t));
170
171	if (flags & SESSION_FLAGS_LOCKDOWN) sleep(1);
172
173	snprintf(str, sizeof(str), "\n========================\nASL is here to serve you\n");
174	if (write(s, str, strlen(str)) < 0)
175	{
176		close(s);
177		pthread_exit(NULL);
178		return;
179	}
180
181	if (flags & SESSION_FLAGS_LOCKDOWN)
182	{
183		snprintf(str, sizeof(str), "> ");
184		SESSION_WRITE(s, str);
185	}
186
187	forever
188	{
189		if (((flags & SESSION_FLAGS_LOCKDOWN) == 0) && (do_prompt > 0))
190		{
191			snprintf(str, sizeof(str), "> ");
192			SESSION_WRITE(s, str);
193		}
194
195		do_prompt = 1;
196
197		memset(str, 0, sizeof(str));
198
199		FD_ZERO(&readfds);
200		FD_SET(s, &readfds);
201		FD_ZERO(&errfds);
202		FD_SET(s, &errfds);
203		nfd = s;
204
205		if (wfd != -1)
206		{
207			FD_SET(wfd, &readfds);
208			if (wfd > nfd) nfd = wfd;
209		}
210
211		status = select(nfd + 1, &readfds, NULL, &errfds, NULL);
212		if (status == 0) continue;
213		if (status < 0)
214		{
215			asldebug("%s %d: select %d %s\n", MY_ID, s, errno, strerror(errno));
216			goto exit_session;
217		}
218
219		if (FD_ISSET(s, &errfds))
220		{
221			asldebug("%s %d: error on socket %d\n", MY_ID, s, s);
222			goto exit_session;
223		}
224
225		if ((wfd != -1) && (FD_ISSET(wfd, &readfds)))
226		{
227			(void)read(wfd, &i, sizeof(int));
228		}
229
230		if (FD_ISSET(s, &errfds))
231		{
232			asldebug("%s %d: socket %d reported error\n", MY_ID, s, s);
233			goto exit_session;
234		}
235
236		if (FD_ISSET(s, &readfds))
237		{
238			len = read(s, str, sizeof(str) - 1);
239			if (len <= 0)
240			{
241				asldebug("%s %d: read error on socket %d: %d %s\n", MY_ID, s, s, errno, strerror(errno));
242				goto exit_session;
243			}
244
245			while ((len > 1) && ((str[len - 1] == '\n') || (str[len - 1] == '\r')))
246			{
247				str[len - 1] = '\0';
248				len--;
249			}
250
251			if ((!strcmp(str, "q")) || (!strcmp(str, "quit")) || (!strcmp(str, "exit")))
252			{
253				snprintf(str, sizeof(str), "Goodbye\n");
254				write(s, str, strlen(str));
255				close(s);
256				s = -1;
257				break;
258			}
259
260			if ((!strcmp(str, "?")) || (!strcmp(str, "help")))
261			{
262				snprintf(str, sizeof(str), "Commands\n");
263				SESSION_WRITE(s, str);
264				snprintf(str, sizeof(str), "    quit                 exit session\n");
265				SESSION_WRITE(s, str);
266				snprintf(str, sizeof(str), "    select [val]         get [set] current database\n");
267				SESSION_WRITE(s, str);
268				snprintf(str, sizeof(str), "                         val must be \"file\", \"mem\", or \"mini\"\n");
269				SESSION_WRITE(s, str);
270				snprintf(str, sizeof(str), "    file [on/off]        enable / disable file store\n");
271				SESSION_WRITE(s, str);
272				snprintf(str, sizeof(str), "    memory [on/off]      enable / disable memory store\n");
273				SESSION_WRITE(s, str);
274				snprintf(str, sizeof(str), "    mini [on/off]        enable / disable mini memory store\n");
275				SESSION_WRITE(s, str);
276				snprintf(str, sizeof(str), "    stats                database statistics\n");
277				SESSION_WRITE(s, str);
278				snprintf(str, sizeof(str), "    flush                flush database\n");
279				SESSION_WRITE(s, str);
280				snprintf(str, sizeof(str), "    dbsize [val]         get [set] database size (# of records)\n");
281				SESSION_WRITE(s, str);
282				snprintf(str, sizeof(str), "    watch                print new messages as they arrive\n");
283				SESSION_WRITE(s, str);
284				snprintf(str, sizeof(str), "    stop                 stop watching for new messages\n");
285				SESSION_WRITE(s, str);
286				snprintf(str, sizeof(str), "    raw                  use raw format for printing messages\n");
287				SESSION_WRITE(s, str);
288				snprintf(str, sizeof(str), "    std                  use standard format for printing messages\n");
289				SESSION_WRITE(s, str);
290				snprintf(str, sizeof(str), "    *                    show all log messages\n");
291				SESSION_WRITE(s, str);
292				snprintf(str, sizeof(str), "    * key val            equality search for messages (single key/value pair)\n");
293				SESSION_WRITE(s, str);
294				snprintf(str, sizeof(str), "    * op key val         search for matching messages (single key/value pair)\n");
295				SESSION_WRITE(s, str);
296				snprintf(str, sizeof(str), "    * [op key val] ...   search for matching messages (multiple key/value pairs)\n");
297				SESSION_WRITE(s, str);
298				snprintf(str, sizeof(str), "                         operators:  =  <  >  ! (not equal)  T (key exists)  R (regex)\n");
299				SESSION_WRITE(s, str);
300				snprintf(str, sizeof(str), "                         modifiers (must follow operator):\n");
301				SESSION_WRITE(s, str);
302				snprintf(str, sizeof(str), "                                 C=casefold  N=numeric  S=substring  A=prefix  Z=suffix\n");
303				SESSION_WRITE(s, str);
304				snprintf(str, sizeof(str), "\n");
305				SESSION_WRITE(s, str);
306				continue;
307			}
308			else if (!strcmp(str, "stats"))
309			{
310				stats = remote_db_stats(dbselect);
311				out = asl_format_message((asl_msg_t *)stats, ASL_MSG_FMT_RAW, ASL_TIME_FMT_SEC, ASL_ENCODE_NONE, &outlen);
312				write(s, out, outlen);
313				free(out);
314				asl_free(stats);
315				continue;
316			}
317			else if (!strcmp(str, "flush"))
318			{}
319			else if (!strncmp(str, "select", 6))
320			{
321				p = str + 6;
322				while ((*p == ' ') || (*p == '\t')) p++;
323				if (*p == '\0')
324				{
325					if (dbselect == 0) snprintf(str, sizeof(str), "no store\n");
326					else if (dbselect == DB_TYPE_FILE) snprintf(str, sizeof(str), "file store\n");
327					else if (dbselect == DB_TYPE_MEMORY) snprintf(str, sizeof(str), "memory store\n");
328					else if (dbselect == DB_TYPE_MINI) snprintf(str, sizeof(str), "mini memory store\n");
329					SESSION_WRITE(s, str);
330					continue;
331				}
332
333				if (!strncmp(p, "file", 4))
334				{
335					if ((global.dbtype & DB_TYPE_FILE) == 0)
336					{
337						snprintf(str, sizeof(str), "file database is not enabled\n");
338						SESSION_WRITE(s, str);
339						continue;
340					}
341
342					dbselect = DB_TYPE_FILE;
343				}
344				else if (!strncmp(p, "mem", 3))
345				{
346					if ((global.dbtype & DB_TYPE_MEMORY) == 0)
347					{
348						snprintf(str, sizeof(str), "memory database is not enabled\n");
349						SESSION_WRITE(s, str);
350						continue;
351					}
352
353					dbselect = DB_TYPE_MEMORY;
354				}
355				else if (!strncmp(p, "mini", 4))
356				{
357					if ((global.dbtype & DB_TYPE_MINI) == 0)
358					{
359						if (global.mini_db != NULL)
360						{
361							snprintf(str, sizeof(str), "mini memory database is enabled for disaster messages\n");
362							SESSION_WRITE(s, str);
363						}
364						else
365						{
366							snprintf(str, sizeof(str), "mini memory database is not enabled\n");
367							SESSION_WRITE(s, str);
368							continue;
369						}
370					}
371
372					dbselect = DB_TYPE_MINI;
373				}
374				else
375				{
376					snprintf(str, sizeof(str), "unknown database type\n");
377					SESSION_WRITE(s, str);
378					continue;
379				}
380
381				snprintf(str, sizeof(str), "OK\n");
382				SESSION_WRITE(s, str);
383				continue;
384			}
385			else if (!strncmp(str, "file", 4))
386			{
387				p = str + 4;
388				while ((*p == ' ') || (*p == '\t')) p++;
389				if (*p == '\0')
390				{
391					snprintf(str, sizeof(str), "file database is %senabled\n", (global.dbtype & DB_TYPE_FILE) ? "" : "not ");
392					SESSION_WRITE(s, str);
393					if ((global.dbtype & DB_TYPE_FILE) != 0) dbselect = DB_TYPE_FILE;
394					continue;
395				}
396
397				if (!strcmp(p, "on")) global.dbtype |= DB_TYPE_FILE;
398				else if (!strcmp(p, "off")) global.dbtype &= ~ DB_TYPE_FILE;
399
400				snprintf(str, sizeof(str), "OK\n");
401				SESSION_WRITE(s, str);
402				continue;
403			}
404			else if (!strncmp(str, "memory", 6))
405			{
406				p = str + 6;
407				while ((*p == ' ') || (*p == '\t')) p++;
408				if (*p == '\0')
409				{
410					snprintf(str, sizeof(str), "memory database is %senabled\n", (global.dbtype & DB_TYPE_MEMORY) ? "" : "not ");
411					SESSION_WRITE(s, str);
412					if ((global.dbtype & DB_TYPE_MEMORY) != 0) dbselect = DB_TYPE_MEMORY;
413					continue;
414				}
415
416				if (!strcmp(p, "on")) global.dbtype |= DB_TYPE_MEMORY;
417				else if (!strcmp(p, "off")) global.dbtype &= ~ DB_TYPE_MEMORY;
418
419				snprintf(str, sizeof(str), "OK\n");
420				SESSION_WRITE(s, str);
421				continue;
422			}
423			else if (!strncmp(str, "mini", 4))
424			{
425				p = str + 4;
426				while ((*p == ' ') || (*p == '\t')) p++;
427				if (*p == '\0')
428				{
429					snprintf(str, sizeof(str), "mini database is %senabled\n", (global.dbtype & DB_TYPE_MINI) ? "" : "not ");
430					SESSION_WRITE(s, str);
431					if ((global.dbtype & DB_TYPE_MINI) != 0) dbselect = DB_TYPE_MINI;
432					continue;
433				}
434
435				if (!strcmp(p, "on")) global.dbtype |= DB_TYPE_MINI;
436				else if (!strcmp(p, "off")) global.dbtype &= ~ DB_TYPE_MINI;
437
438				snprintf(str, sizeof(str), "OK\n");
439				SESSION_WRITE(s, str);
440				continue;
441			}
442			else if (!strncmp(str, "dbsize", 6))
443			{
444				if (dbselect == 0)
445				{
446					snprintf(str, sizeof(str), "no store\n");
447					SESSION_WRITE(s, str);
448					continue;
449				}
450
451				p = str + 6;
452				while ((*p == ' ') || (*p == '\t')) p++;
453				if (*p == '\0')
454				{
455					snprintf(str, sizeof(str), "DB size %u\n", remote_db_size(dbselect));
456					SESSION_WRITE(s, str);
457					continue;
458				}
459
460				i = atoi(p);
461				remote_db_set_size(dbselect, i);
462
463				snprintf(str, sizeof(str), "OK\n");
464				SESSION_WRITE(s, str);
465				continue;
466			}
467			else if (!strcmp(str, "stop"))
468			{
469				if (watch != WATCH_OFF)
470				{
471					watch = WATCH_OFF;
472					notify_cancel(wtoken);
473					wfd = -1;
474					wtoken = -1;
475
476					low_id = 0;
477					high_id = 0;
478
479					if (query != NULL) free(query);
480					query = NULL;
481
482					snprintf(str, sizeof(str), "OK\n");
483					SESSION_WRITE(s, str);
484					continue;
485				}
486
487				snprintf(str, sizeof(str), "not watching!\n");
488				SESSION_WRITE(s, str);
489				continue;
490			}
491			else if (!strcmp(str, "raw"))
492			{
493				pfmt = PRINT_RAW;
494				continue;
495			}
496			else if (!strcmp(str, "std"))
497			{
498				pfmt = PRINT_STD;
499				continue;
500			}
501			else if (!strcmp(str, "watch"))
502			{
503				if (((flags & SESSION_FLAGS_LOCKDOWN) == 0) && (watch != WATCH_OFF))
504				{
505					snprintf(str, sizeof(str), "already watching!\n");
506					SESSION_WRITE(s, str);
507					continue;
508				}
509
510				if (flags & SESSION_FLAGS_LOCKDOWN)
511				{
512					/*
513					 * If this session is PurpleConsole or Xcode watching for log messages,
514					 * we pass through the bottom of the loop (below) once to pick up
515					 * existing messages already in memory.  After that, dbserver will
516					 * send new messages in send_to_direct_watchers().  We wait until
517					 * the initial messages are sent before adding the connection to
518					 * global.lockdown_session_fds to allow this query to complete before
519					 * dbserver starts sending.  To prevent a race between this query and
520					 * when messages are sent by send_to_direct_watchers, we suspend the
521					 * work queue and resume it when lockdown_session_fds has been updated.
522					 */
523					watch = WATCH_LOCKDOWN_START;
524					dispatch_suspend(global.work_queue);
525				}
526				else
527				{
528					status = notify_register_file_descriptor(kNotifyASLDBUpdate, &wfd, 0, &wtoken);
529					if (status != 0)
530					{
531						snprintf(str, sizeof(str), "notify_register_file_descriptor failed: %d\n", status);
532						SESSION_WRITE(s, str);
533						continue;
534					}
535
536					watch = WATCH_RUN;
537				}
538
539				snprintf(str, sizeof(str), "OK\n");
540				SESSION_WRITE(s, str);
541				do_prompt = 2;
542			}
543			else if ((str[0] == '*') || (str[0] == 'T') || (str[0] == '=') || (str[0] == '!') || (str[0] == '<') || (str[0] == '>'))
544			{
545				memset(&ql, 0, sizeof(asl_search_result_t));
546				if (query != NULL) free(query);
547				query = NULL;
548
549				p = str;
550				if (*p == '*') p++;
551				while ((*p == ' ') || (*p == '\t')) p++;
552
553				if (*p == '\0')
554				{
555					/* NULL query */
556				}
557				else if (*p == '[')
558				{
559					qs = NULL;
560					asprintf(&qs, "Q %s", p);
561					query = asl_msg_from_string(qs);
562					free(qs);
563				}
564				else if ((*p == 'T') || (*p == '=') || (*p == '!') || (*p == '<') || (*p == '>') || (*p == 'R'))
565				{
566					qs = NULL;
567					asprintf(&qs, "Q [%s]", p);
568					query = asl_msg_from_string(qs);
569					free(qs);
570				}
571				else
572				{
573					qs = NULL;
574					asprintf(&qs, "Q [= %s]", p);
575					query = asl_msg_from_string(qs);
576					free(qs);
577				}
578			}
579			else
580			{
581				snprintf(str, sizeof(str), "unrecognized command\n");
582				SESSION_WRITE(s, str);
583				snprintf(str, sizeof(str), "enter \"help\" for help\n");
584				SESSION_WRITE(s, str);
585				continue;
586			}
587		}
588
589		if ((flags & SESSION_FLAGS_LOCKDOWN) && (watch == WATCH_RUN)) continue;
590
591		/* Bottom of the loop: do a database query and print the results */
592
593		if (query != NULL)
594		{
595			ql.count = 1;
596			qlq[0] = query;
597			ql.msg = qlq;
598		}
599
600		if (watch == WATCH_OFF) low_id = 0;
601
602		memset(&res, 0, sizeof(aslresponse));
603		high_id = 0;
604		(void)db_query(&ql, (aslresponse *)&res, low_id, 0, 0, &high_id, 0, 0, 0);
605
606		if ((watch == WATCH_RUN) && (high_id >= low_id)) low_id = high_id + 1;
607
608		if (res == NULL)
609		{
610			if (watch == WATCH_OFF)
611			{
612				snprintf(str, sizeof(str), "-nil-\n");
613				SESSION_WRITE(s, str);
614			}
615			else
616			{
617				if (do_prompt != 2) do_prompt = 0;
618			}
619		}
620		else if (pfmt == PRINT_RAW)
621		{
622			if (watch == WATCH_RUN)
623			{
624				snprintf(str, sizeof(str), "\n");
625				SESSION_WRITE(s, str);
626			}
627
628			outlen = 0;
629			out = asl_list_to_string((asl_search_result_t *)res, &outlen);
630			write(s, out, outlen);
631			free(out);
632
633			snprintf(str, sizeof(str), "\n");
634			SESSION_WRITE(s, str);
635		}
636		else
637		{
638			if ((watch == WATCH_RUN) || (watch == WATCH_LOCKDOWN_START))
639			{
640				snprintf(str, sizeof(str), "\n");
641				SESSION_WRITE(s, str);
642			}
643
644			snprintf(str, sizeof(str), "\n");
645			for (i = 0; i < res->count; i++)
646			{
647				int wstatus;
648
649				out = asl_format_message(res->msg[i], ASL_MSG_FMT_STD, ASL_TIME_FMT_LCL, ASL_ENCODE_SAFE, &outlen);
650
651				do
652				{
653					int n = 0;
654
655					errno = 0;
656					wstatus = write(s, out, outlen);
657					if (wstatus < 0)
658					{
659						asldebug("%s %d: %d/%d write data failed: %d %s\n", MY_ID, s, i, res->count, errno, strerror(errno));
660						if (errno == EAGAIN)
661						{
662							n++;
663							if (n > 10) break;
664							usleep(10000);
665						}
666						else
667						{
668							goto exit_session;
669						}
670					}
671				} while (errno == EAGAIN);
672
673				free(out);
674				if (global.remote_delay_time > 0) usleep(global.remote_delay_time);
675			}
676		}
677
678		aslresponse_free(res);
679
680		if (watch == WATCH_LOCKDOWN_START)
681		{
682			add_lockdown_session(s);
683			watch = WATCH_RUN;
684			dispatch_resume(global.work_queue);
685		}
686	}
687
688exit_session:
689
690	asldebug("%s %d: terminating session for %ssocket %d\n", MY_ID, s, (flags & SESSION_FLAGS_LOCKDOWN) ? "lockdown " : "", s);
691
692	if (s >= 0)
693	{
694		if (flags & SESSION_FLAGS_LOCKDOWN) remove_lockdown_session(s);
695		close(s);
696	}
697
698	if (watch == WATCH_LOCKDOWN_START) dispatch_resume(global.work_queue);
699	if (wtoken >= 0) notify_cancel(wtoken);
700	if (query != NULL) asl_msg_release(query);
701	pthread_exit(NULL);
702}
703
704aslmsg
705remote_acceptmsg(int fd, int tcp)
706{
707	socklen_t fromlen;
708	int s, flags, status, v;
709	pthread_attr_t attr;
710	pthread_t t;
711	struct sockaddr_storage from;
712	session_args_t *sp;
713
714	fromlen = sizeof(struct sockaddr_un);
715	if (tcp == 1) fromlen = sizeof(struct sockaddr_storage);
716
717	memset(&from, 0, sizeof(from));
718
719	s = accept(fd, (struct sockaddr *)&from, &fromlen);
720	if (s == -1)
721	{
722		asldebug("%s: accept: %s\n", MY_ID, strerror(errno));
723		return NULL;
724	}
725
726	flags = fcntl(s, F_GETFL, 0);
727	flags &= ~ O_NONBLOCK;
728	status = fcntl(s, F_SETFL, flags);
729	if (status < 0)
730	{
731		asldebug("%s: fcntl: %s\n", MY_ID, strerror(errno));
732		close(s);
733		return NULL;
734	}
735
736	v = 1;
737	setsockopt(s, SOL_SOCKET, SO_NOSIGPIPE, &v, sizeof(v));
738
739	if (tcp == 1)
740	{
741		flags = 1;
742		setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &flags, sizeof(int));
743	}
744
745	sp = (session_args_t *)calloc(1, sizeof(session_args_t));
746	if (sp == NULL)
747	{
748		asldebug("%s: malloc: %s\n", MY_ID, strerror(errno));
749		close(s);
750		return NULL;
751	}
752
753	sp->sock = s;
754	if (tcp == 0) sp->flags |= SESSION_FLAGS_LOCKDOWN;
755
756	pthread_attr_init(&attr);
757	pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
758	pthread_create(&t, &attr, (void *(*)(void *))session, (void *)sp);
759	pthread_attr_destroy(&attr);
760
761	return NULL;
762}
763
764aslmsg
765remote_acceptmsg_local(int fd)
766{
767	return remote_acceptmsg(fd, 0);
768}
769
770aslmsg
771remote_acceptmsg_tcp(int fd)
772{
773	return remote_acceptmsg(fd, 1);
774}
775
776int
777remote_init_lockdown(void)
778{
779	int status, reuse, fd;
780	struct sockaddr_un local;
781
782	fd = socket(AF_UNIX, SOCK_STREAM, 0);
783	if (fd < 0)
784	{
785		asldebug("%s: socket: %s\n", MY_ID, strerror(errno));
786		return -1;
787	}
788
789	reuse = 1;
790	status = setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &reuse, sizeof(int));
791	if (status < 0)
792	{
793		asldebug("%s: setsockopt: %s\n", MY_ID, strerror(errno));
794		close(fd);
795		return -1;
796	}
797
798	/* make sure the lockdown directory exists */
799	mkdir(LOCKDOWN_PATH, 0777);
800
801	memset(&local, 0, sizeof(local));
802	local.sun_family = AF_UNIX;
803	strlcpy(local.sun_path, SYSLOG_SOCK_PATH, sizeof(local.sun_path));
804	unlink(local.sun_path);
805
806	status = bind(fd, (struct sockaddr *)&local, sizeof(local.sun_family) + sizeof(local.sun_path));
807
808	if (status < 0)
809	{
810		asldebug("%s: bind: %s\n", MY_ID, strerror(errno));
811		close(fd);
812		return -1;
813	}
814
815	status = fcntl(fd, F_SETFL, O_NONBLOCK);
816	if (status < 0)
817	{
818		asldebug("%s: fcntl: %s\n", MY_ID, strerror(errno));
819		close(fd);
820		return -1;
821	}
822
823	status = listen(fd, 5);
824	if (status < 0)
825	{
826		asldebug("%s: listen: %s\n", MY_ID, strerror(errno));
827		close(fd);
828		return -1;
829	}
830
831	chmod(SYSLOG_SOCK_PATH, 0666);
832
833	in_src_local = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, (uintptr_t)fd, 0, in_queue);
834	dispatch_source_set_event_handler(in_src_local, ^{ remote_acceptmsg_local(fd); });
835	dispatch_resume(in_src_local);
836
837	return fd;
838}
839
840int
841remote_init_tcp(int family)
842{
843	int status, reuse, fd;
844	struct sockaddr_in a4;
845	struct sockaddr_in6 a6;
846	struct sockaddr *s;
847	socklen_t len;
848
849	fd = socket(family, SOCK_STREAM, 0);
850	if (fd < 0)
851	{
852		asldebug("%s: socket: %s\n", MY_ID, strerror(errno));
853		return -1;
854	}
855
856	reuse = 1;
857	status = setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &reuse, sizeof(int));
858	if (status < 0)
859	{
860		asldebug("%s: setsockopt: %s\n", MY_ID, strerror(errno));
861		close(fd);
862		return -1;
863	}
864
865	memset(&(a4.sin_addr), 0, sizeof(struct in_addr));
866	a4.sin_family = AF_INET;
867	a4.sin_port = htons(ASL_REMOTE_PORT);
868
869	memset(&(a6.sin6_addr), 0, sizeof(struct in6_addr));
870	a6.sin6_family = AF_INET6;
871	a6.sin6_port = htons(ASL_REMOTE_PORT);
872
873	s = (struct sockaddr *)&a4;
874	len = sizeof(struct sockaddr_in);
875
876	if (family == AF_INET6)
877	{
878		s = (struct sockaddr *)&a6;
879		len = sizeof(struct sockaddr_in6);
880	}
881
882	status = bind(fd, s, len);
883	if (status < 0)
884	{
885		asldebug("%s: bind: %s\n", MY_ID, strerror(errno));
886		close(fd);
887		return -1;
888	}
889
890	status = fcntl(fd, F_SETFL, O_NONBLOCK);
891	if (status < 0)
892	{
893		asldebug("%s: fcntl: %s\n", MY_ID, strerror(errno));
894		close(fd);
895		return -1;
896	}
897
898	status = listen(fd, 5);
899	if (status < 0)
900	{
901		asldebug("%s: listen: %s\n", MY_ID, strerror(errno));
902		close(fd);
903		return -1;
904	}
905
906	in_src_tcp = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, (uintptr_t)fd, 0, in_queue);
907	dispatch_source_set_event_handler(in_src_tcp, ^{ remote_acceptmsg_tcp(fd); });
908	dispatch_resume(in_src_tcp);
909
910	return fd;
911}
912
913int
914remote_init(void)
915{
916	static dispatch_once_t once;
917
918	dispatch_once(&once, ^{
919		in_queue = dispatch_queue_create(MY_ID, NULL);
920	});
921
922	asldebug("%s: init\n", MY_ID);
923
924#ifdef LOCKDOWN
925	rfdl = remote_init_lockdown();
926#endif
927
928#ifdef REMOTE_IPV4
929	rfd4 = remote_init_tcp(AF_INET);
930#endif
931
932#ifdef REMOTE_IPV6
933	rfd6 = remote_init_tcp(AF_INET6);
934#endif
935
936	return 0;
937}
938
939int
940remote_close(void)
941{
942	if (rfdl >= 0)
943	{
944		close(rfdl);
945	}
946
947	rfdl = -1;
948
949	if (rfd4 >= 0)
950	{
951		close(rfd4);
952	}
953
954	rfd4 = -1;
955
956	if (rfd6 >= 0)
957	{
958		close(rfd6);
959	}
960
961	rfd6 = -1;
962
963	return 0;
964}
965
966int
967remote_reset(void)
968{
969	return 0;
970
971	remote_close();
972	return remote_init();
973}
974
975#endif /* !TARGET_IPHONE_SIMULATOR */
976