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