xref: /NextBSD/usr.sbin/asl/bsd_out.c (revision 33da5adc555b3bc29986eeadca03829e4ad06b1e)
1 /*
2  * Copyright (c) 2004-2011 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 <sys/un.h>
34 #include <sys/uio.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <unistd.h>
39 #include <ctype.h>
40 #include <fcntl.h>
41 #include <errno.h>
42 #include <netdb.h>
43 #include <pthread.h>
44 #include <notify.h>
45 #include "daemon.h"
46 
47 #define MY_ID "bsd_out"
48 
49 #define _PATH_WALL "/usr/bin/wall"
50 #define ASL_KEY_FACILITY "Facility"
51 #define FACILITY_KERNEL "kern"
52 #define _PATH_CONSOLE "/dev/console"
53 
54 #define DST_TYPE_NONE 0
55 #define DST_TYPE_FILE 1
56 #define DST_TYPE_CONS 2
57 #define DST_TYPE_SOCK 3
58 #define DST_TYPE_WALL 4
59 #define DST_TYPE_NOTE 5
60 
61 #define CLOSE_ON_IDLE_SEC 300
62 
63 static dispatch_queue_t bsd_out_queue;
64 static dispatch_source_t bsd_idle_timer;
65 
66 struct config_rule
67 {
68 	uint32_t count;
69 	char *dst;
70 	int fd;
71 	int type;
72 	struct sockaddr *addr;
73 	char **facility;
74 	uint32_t *fac_prefix_len;
75 	int *pri;
76 	uint32_t last_hash;
77 	uint32_t last_count;
78 	time_t last_time;
79 	dispatch_source_t dup_timer;
80 	char *last_msg;
81 	TAILQ_ENTRY(config_rule) entries;
82 };
83 
84 static TAILQ_HEAD(cr, config_rule) bsd_out_rule;
85 
86 extern uint32_t asl_core_string_hash(const char *s, uint32_t inlen);
87 
88 static int
_level_for_name(const char * name)89 _level_for_name(const char *name)
90 {
91 	if (name == NULL) return -1;
92 
93 	if (!strcasecmp(name, "emerg")) return ASL_LEVEL_EMERG;
94 	if (!strcasecmp(name, "panic")) return ASL_LEVEL_EMERG;
95 	if (!strcasecmp(name, "alert")) return ASL_LEVEL_ALERT;
96 	if (!strcasecmp(name, "crit")) return ASL_LEVEL_CRIT;
97 	if (!strcasecmp(name, "err")) return ASL_LEVEL_ERR;
98 	if (!strcasecmp(name, "error")) return ASL_LEVEL_ERR;
99 	if (!strcasecmp(name, "warn")) return ASL_LEVEL_WARNING;
100 	if (!strcasecmp(name, "warning")) return ASL_LEVEL_WARNING;
101 	if (!strcasecmp(name, "notice")) return ASL_LEVEL_NOTICE;
102 	if (!strcasecmp(name, "info")) return ASL_LEVEL_INFO;
103 	if (!strcasecmp(name, "debug")) return ASL_LEVEL_DEBUG;
104 	if (!strcmp(name, "*")) return ASL_LEVEL_DEBUG;
105 
106 	/* special case */
107 	if (!strcasecmp(name, "none")) return -2;
108 
109 	return -1;
110 }
111 
112 static int
_syslog_dst_open(struct config_rule * r)113 _syslog_dst_open(struct config_rule *r)
114 {
115 	int i;
116 	char *node, *serv;
117 	struct addrinfo hints, *gai, *ai;
118 
119 	if (r == NULL) return -1;
120 	if (r->fd != -1) return 0;
121 
122 	if (r->dst[0] == '/')
123 	{
124 		r->fd = open(r->dst, O_WRONLY | O_APPEND | O_CREAT | O_NOCTTY, 0644);
125 		if (r->fd < 0)
126 		{
127 			asldebug("%s: open failed for file: %s (%s)\n", MY_ID, r->dst, strerror(errno));
128 			return -1;
129 		}
130 
131 		r->type = DST_TYPE_FILE;
132 		if (!strcmp(r->dst, _PATH_CONSOLE)) r->type = DST_TYPE_CONS;
133 
134 		return 0;
135 	}
136 
137 	if (r->dst[0] == '!')
138 	{
139 		r->type = DST_TYPE_NOTE;
140 		r->fd = -1;
141 		return 0;
142 	}
143 
144 	if (r->dst[0] == '@')
145 	{
146 		node = strdup(r->dst + 1);
147 		if (node == NULL) return -1;
148 
149 		serv = NULL;
150 		serv = strrchr(node, ':');
151 		if (serv != NULL) *serv++ = '\0';
152 		else serv = "syslog";
153 
154 		memset(&hints, 0, sizeof(hints));
155 		hints.ai_family = PF_UNSPEC;
156 		hints.ai_socktype = SOCK_DGRAM;
157 		i = getaddrinfo(node, serv, &hints, &gai);
158 		free(node);
159 		if (i != 0)
160 		{
161 			asldebug("%s: getaddrinfo failed for node %s service %s: (%s)\n", MY_ID, node, serv, gai_strerror(i));
162 			return -1;
163 		}
164 
165 		for (ai = gai; ai != NULL; ai = ai->ai_next)
166 		{
167 			r->fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
168 			if (r->fd < 0) continue;
169 
170 			r->addr = (struct sockaddr *)malloc(ai->ai_addrlen);
171 			if (r->addr == NULL) return -1;
172 
173 			memcpy(r->addr, ai->ai_addr, ai->ai_addrlen);
174 
175 			break;
176 		}
177 
178 		freeaddrinfo(gai);
179 
180 		if (r->fd < 0)
181 		{
182 			asldebug("%s: connection failed for %s\n", MY_ID, (r->dst) + 1);
183 			free(r->addr);
184 			r->addr = NULL;
185 			return -1;
186 		}
187 
188 		if (fcntl(r->fd, F_SETFL, O_NONBLOCK) < 0)
189 		{
190 			close(r->fd);
191 			r->fd = -1;
192 			asldebug("%s: couldn't set O_NONBLOCK for fd %d: %s\n", MY_ID, r->fd, strerror(errno));
193 			free(r->addr);
194 			r->addr = NULL;
195 			return -1;
196 		}
197 
198 		r->type = DST_TYPE_SOCK;
199 		return 0;
200 
201 	}
202 
203 	if (strcmp(r->dst, "*") == 0)
204 	{
205 		r->type = DST_TYPE_WALL;
206 		r->fd = -1;
207 		return 0;
208 	}
209 
210 	/* Can't deal with dst! */
211 	asldebug("%s: unsupported / unknown output name: %s\n", MY_ID, r->dst);
212 	return -1;
213 }
214 
215 static void
_syslog_dst_close(struct config_rule * r)216 _syslog_dst_close(struct config_rule *r)
217 {
218 	if (r == NULL) return;
219 
220 	if (r->addr != NULL)
221 	{
222 		free(r->addr);
223 		r->addr = NULL;
224 	}
225 
226 	switch (r->type)
227 	{
228 		case DST_TYPE_FILE:
229 		case DST_TYPE_CONS:
230 		{
231 			if (r->fd >= 0) close(r->fd);
232 			r->fd = -1;
233 			break;
234 		}
235 
236 		case DST_TYPE_SOCK:
237 		{
238 			if (r->fd >= 0) close(r->fd);
239 			r->fd = -1;
240 			break;
241 		}
242 
243 		case DST_TYPE_NONE:
244 		case DST_TYPE_WALL:
245 		case DST_TYPE_NOTE:
246 		default:
247 		{
248 			/* do nothing */
249 			return;
250 		}
251 	}
252 }
253 
254 static char *
_clean_facility_name(char * s)255 _clean_facility_name(char *s)
256 {
257 	uint32_t len;
258 	char *p, *out;
259 
260 	if (s == NULL) return NULL;
261 	len = strlen(s);
262 	if (len == 0) return NULL;
263 
264 	p = s;
265 
266 	if ((*s == '\'') || (*s == '"'))
267 	{
268 		len--;
269 		p++;
270 		if (p[len - 1] == *s) len --;
271 	}
272 
273 	out = calloc(1, len + 1);
274 	if (out == NULL) return NULL;
275 
276 	memcpy(out, p, len);
277 	return out;
278 }
279 
280 static int
_parse_line(char * s)281 _parse_line(char *s)
282 {
283 	char **semi, **comma, *star;
284 	int i, j, n, lasts, lastc, pri;
285 	struct config_rule *out;
286 
287 	if (s == NULL) return -1;
288 	while ((*s == ' ') || (*s == '\t')) s++;
289 	if (*s == '#') return -1;
290 
291 	semi = explode(s, "; \t");
292 
293 	if (semi == NULL) return -1;
294 	out = (struct config_rule *)calloc(1, sizeof(struct config_rule));
295 	if (out == NULL) return -1;
296 	out->fd = -1;
297 
298 	n = 0;
299 	lasts = -1;
300 	for (i = 0; semi[i] != NULL; i++)
301 	{
302 		if (semi[i][0] == '\0') continue;
303 		n++;
304 		lasts = i;
305 	}
306 
307 	out->dst = strdup(semi[lasts]);
308 	if (out->dst == NULL) return -1;
309 
310 	for (i = 0; i < lasts; i++)
311 	{
312 		if (semi[i][0] == '\0') continue;
313 		comma = explode(semi[i], ",.");
314 		lastc = -1;
315 		for (j = 0; comma[j] != NULL; j++)
316 		{
317 			if (comma[j][0] == '\0') continue;
318 			lastc = j;
319 		}
320 
321 		for (j = 0; j < lastc; j++)
322 		{
323 			if (comma[j][0] == '\0') continue;
324 			pri = _level_for_name(comma[lastc]);
325 			if (pri == -1) continue;
326 
327 			if (out->count == 0)
328 			{
329 				out->facility = (char **)calloc(1, sizeof(char *));
330 				out->fac_prefix_len = (uint32_t *)calloc(1, sizeof(uint32_t));
331 				out->pri = (int *)calloc(1, sizeof(int));
332 			}
333 			else
334 			{
335 				out->facility = (char **)reallocf(out->facility, (out->count + 1) * sizeof(char *));
336 				out->fac_prefix_len = (uint32_t *)reallocf(out->fac_prefix_len, (out->count + 1) * sizeof(uint32_t));
337 				out->pri = (int *)reallocf(out->pri, (out->count + 1) * sizeof(int));
338 			}
339 
340 			if (out->facility == NULL) return -1;
341 			if (out->fac_prefix_len == NULL) return -1;
342 			if (out->pri == NULL) return -1;
343 
344 			out->facility[out->count] = _clean_facility_name(comma[j]);
345 			if (out->facility[out->count] == NULL) return -1;
346 
347 			out->fac_prefix_len[out->count] = 0;
348 			star = strchr(out->facility[out->count], '*');
349 			if (star != NULL) out->fac_prefix_len[out->count] = (uint32_t)(star - out->facility[out->count]);
350 
351 			out->pri[out->count] = pri;
352 			out->count++;
353 		}
354 
355 		free_string_list(comma);
356 	}
357 
358 	free_string_list(semi);
359 
360 	TAILQ_INSERT_TAIL(&bsd_out_rule, out, entries);
361 
362 	return 0;
363 }
364 
365 static int
_bsd_send_repeat_msg(struct config_rule * r)366 _bsd_send_repeat_msg(struct config_rule *r)
367 {
368 	char vt[32], *msg;
369 	time_t tick;
370 	int len, status;
371 
372 	if (r == NULL) return -1;
373 	if (r->type != DST_TYPE_FILE) return 0;
374 	if (r->last_count == 0) return 0;
375 
376 	/* stop the timer */
377 	dispatch_suspend(r->dup_timer);
378 
379 	tick = time(NULL);
380 	memset(vt, 0, sizeof(vt));
381 	ctime_r(&tick, vt);
382 	vt[19] = '\0';
383 
384 	msg = NULL;
385 	asprintf(&msg, "%s: --- last message repeated %u time%s ---\n", vt + 4, r->last_count, (r->last_count == 1) ? "" : "s");
386 	r->last_count = 0;
387 	if (msg == NULL) return -1;
388 
389 	len = strlen(msg);
390 	status = write(r->fd, msg, len);
391 	if ((status < 0) || (status < len))
392 	{
393 		asldebug("%s: error writing repeat message (%s): %s\n", MY_ID, r->dst, strerror(errno));
394 
395 		/* Try re-opening the file (once) and write again */
396 		close(r->fd);
397 		r->fd = open(r->dst, O_WRONLY | O_APPEND | O_CREAT | O_NOCTTY, 0644);
398 		if (r->fd < 0)
399 		{
400 			asldebug("%s: re-open failed for file: %s (%s)\n", MY_ID, r->dst, strerror(errno));
401 			free(msg);
402 			return -1;
403 		}
404 
405 		status = write(r->fd, msg, len);
406 		if ((status < 0) || (status < len))
407 		{
408 			asldebug("%s: error re-writing message (%s): %s\n", MY_ID, r->dst, strerror(errno));
409 			free(msg);
410 			return -1;
411 		}
412 	}
413 
414 	free(msg);
415 	return 0;
416 }
417 
418 static int
_bsd_send(asl_msg_t * msg,struct config_rule * r,char ** out,char ** fwd,time_t now)419 _bsd_send(asl_msg_t *msg, struct config_rule *r, char **out, char **fwd, time_t now)
420 {
421 	char *sf, *outmsg;
422 	const char *vlevel, *vfacility;
423 	size_t outlen;
424 	int pf, fc, status, is_dup, do_write;
425 	uint32_t msg_hash, n;
426 
427 	if (out == NULL) return -1;
428 	if (fwd == NULL) return -1;
429 	if (r == NULL) return -1;
430 
431 	_syslog_dst_open(r);
432 
433 	if (r->type == DST_TYPE_NOTE)
434 	{
435 		notify_post(r->dst+1);
436 		return 0;
437 	}
438 
439 	msg_hash = 0;
440 	outmsg = NULL;
441 
442 	/* Build output string if it hasn't been built by a previous rule-match */
443 	if (*out == NULL)
444 	{
445 		*out = asl_format_message((asl_msg_t *)msg, ASL_MSG_FMT_BSD, ASL_TIME_FMT_LCL, ASL_ENCODE_SAFE, &n);
446 		if (*out == NULL) return -1;
447 	}
448 
449 	/* check if message is a duplicate of the last message, and inside the dup time window */
450 	is_dup = 0;
451 	if ((global.bsd_max_dup_time > 0) && (*out != NULL) && (r->last_msg != NULL))
452 	{
453 		msg_hash = asl_core_string_hash(*out + 16, strlen(*out + 16));
454 		if ((r->last_hash == msg_hash) && (!strcmp(r->last_msg, *out + 16)))
455 		{
456 			if ((now - r->last_time) < global.bsd_max_dup_time) is_dup = 1;
457 		}
458 	}
459 
460 	if ((*fwd == NULL) && (r->type == DST_TYPE_SOCK))
461 	{
462 		pf = 7;
463 		vlevel = asl_msg_get_val_for_key(msg, ASL_KEY_LEVEL);
464 		if (vlevel != NULL) pf = atoi(vlevel);
465 
466 		fc = asl_syslog_faciliy_name_to_num(asl_msg_get_val_for_key(msg, ASL_KEY_FACILITY));
467 		if (fc > 0) pf |= fc;
468 
469 		sf = NULL;
470 		asprintf(&sf, "<%d>%s", pf, *out);
471 		if (sf == NULL) return -1;
472 
473 		*fwd = sf;
474 	}
475 
476 	if (r->type == DST_TYPE_SOCK) outlen = strlen(*fwd);
477 	else outlen = strlen(*out);
478 
479 	if ((r->type == DST_TYPE_FILE) || (r->type == DST_TYPE_CONS))
480 	{
481 		/*
482 		 * If current message is NOT a duplicate and r->last_count > 0
483 		 * we need to write a "last message was repeated N times" log entry
484 		 */
485 		if ((r->type == DST_TYPE_FILE) && (is_dup == 0) && (r->last_count > 0)) _bsd_send_repeat_msg(r);
486 
487 		do_write = 1;
488 
489 		/*
490 		 * Special case for kernel messages.
491 		 * Don't write kernel messages to /dev/console.
492 		 * The kernel printf routine already sends them to /dev/console
493 		 * so writing them here would cause duplicates.
494 		 */
495 		vfacility = asl_msg_get_val_for_key(msg, ASL_KEY_FACILITY);
496 		if ((vfacility != NULL) && (!strcmp(vfacility, FACILITY_KERNEL)) && (r->type == DST_TYPE_CONS)) do_write = 0;
497 		if ((do_write == 1) && (r->type == DST_TYPE_FILE) && (is_dup == 1))
498 		{
499 			do_write = 0;
500 
501 			if (r->dup_timer == NULL)
502 			{
503 				/* create a timer to flush dups on this file */
504 				r->dup_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, bsd_out_queue);
505 				dispatch_source_set_event_handler(r->dup_timer, ^{ _bsd_send_repeat_msg(r); });
506 			}
507 
508 			if (r->last_count == 0)
509 			{
510 				/* start the timer */
511 				dispatch_source_set_timer(r->dup_timer, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * global.bsd_max_dup_time), DISPATCH_TIME_FOREVER, 0);
512 				dispatch_resume(r->dup_timer);
513 			}
514 		}
515 
516 		if (do_write == 0) status = outlen;
517 		else status = write(r->fd, *out, outlen);
518 
519 		if ((status < 0) || (status < outlen))
520 		{
521 			asldebug("%s: error writing message (%s): %s\n", MY_ID, r->dst, strerror(errno));
522 
523 			/* Try re-opening the file (once) and write again */
524 			close(r->fd);
525 			r->fd = open(r->dst, O_WRONLY | O_APPEND | O_CREAT | O_NOCTTY, 0644);
526 			if (r->fd < 0)
527 			{
528 				asldebug("%s: re-open failed for file: %s (%s)\n", MY_ID, r->dst, strerror(errno));
529 				return -1;
530 			}
531 
532 			status = write(r->fd, *out, outlen);
533 			if ((status < 0) || (status < outlen))
534 			{
535 				asldebug("%s: error re-writing message (%s): %s\n", MY_ID, r->dst, strerror(errno));
536 			}
537 		}
538 	}
539 	else if ((r->type == DST_TYPE_SOCK) && (r->addr != NULL))
540 	{
541 		status = sendto(r->fd, *fwd, outlen, 0, r->addr, r->addr->sa_len);
542 		if (status < 0) asldebug("%s: error sending message (%s): %s\n", MY_ID, r->dst, strerror(errno));
543 	}
544 	else if (r->type == DST_TYPE_WALL)
545 	{
546 #if !TARGET_OS_EMBEDDED
547 		FILE *pw = popen(_PATH_WALL, "w");
548 		if (pw < 0)
549 		{
550 			asldebug("%s: error sending wall message: %s\n", MY_ID, strerror(errno));
551 			return -1;
552 		}
553 
554 		fprintf(pw, "%s", *out);
555 		pclose(pw);
556 #endif
557 	}
558 
559 	if (is_dup == 1)
560 	{
561 		r->last_count++;
562 	}
563 	else
564 	{
565 		free(r->last_msg);
566 		r->last_msg = NULL;
567 
568 		if (*out != NULL) r->last_msg = strdup(*out + 16);
569 
570 		r->last_hash = msg_hash;
571 		r->last_count = 0;
572 		r->last_time = now;
573 	}
574 
575 	return 0;
576 }
577 
578 static int
_bsd_rule_match(asl_msg_t * msg,struct config_rule * r)579 _bsd_rule_match(asl_msg_t *msg, struct config_rule *r)
580 {
581 	uint32_t i, test, f;
582 	int32_t pri;
583 	const char *val;
584 
585 	if (msg == NULL) return 0;
586 	if (r == NULL) return 0;
587 	if (r->count == 0) return 0;
588 
589 	test = 0;
590 
591 	for (i = 0; i < r->count; i++)
592 	{
593 		if (r->pri[i] == -1) continue;
594 
595 		if ((test == 1) && (r->pri[i] >= 0)) continue;
596 		if ((test == 0) && (r->pri[i] == -2)) continue;
597 
598 		f = 0;
599 		val = asl_msg_get_val_for_key(msg, ASL_KEY_FACILITY);
600 
601 		if (strcmp(r->facility[i], "*") == 0)
602 		{
603 			f = 1;
604 		}
605 		else if ((r->fac_prefix_len[i] > 0) && (strncasecmp(r->facility[i], val, r->fac_prefix_len[i]) == 0))
606 		{
607 			f = 1;
608 		}
609 		else if ((val != NULL) && (strcasecmp(r->facility[i], val) == 0))
610 		{
611 			f = 1;
612 		}
613 
614 		if (f == 0) continue;
615 
616 		/* Turn off matching facility with priority "none" */
617 		if (r->pri[i] == -2)
618 		{
619 			test = 0;
620 			continue;
621 		}
622 
623 		val = asl_msg_get_val_for_key(msg, ASL_KEY_LEVEL);
624 		if (val == NULL) continue;
625 
626 		pri = atoi(val);
627 		if (pri < 0) continue;
628 
629 		if (pri <= r->pri[i]) test = 1;
630 	}
631 
632 	return test;
633 }
634 
635 static int
_bsd_match_and_send(asl_msg_t * msg)636 _bsd_match_and_send(asl_msg_t *msg)
637 {
638 	struct config_rule *r;
639 	char *out, *fwd;
640 	time_t now;
641 
642 	if (msg == NULL) return -1;
643 
644 	out = NULL;
645 	fwd = NULL;
646 
647 	now = time(NULL);
648 
649 	for (r = bsd_out_rule.tqh_first; r != NULL; r = r->entries.tqe_next)
650 	{
651 		if (_bsd_rule_match(msg, r) == 1) _bsd_send(msg, r, &out, &fwd, now);
652 	}
653 
654 	free(out);
655 	free(fwd);
656 
657 	return 0;
658 }
659 
660 void
bsd_out_message(asl_msg_t * msg)661 bsd_out_message(asl_msg_t *msg)
662 {
663 	if (msg == NULL) return;
664 
665 	OSAtomicIncrement32(&global.bsd_queue_count);
666 	asl_msg_retain((asl_msg_t *)msg);
667 
668 	dispatch_async(bsd_out_queue, ^{
669 		_bsd_match_and_send(msg);
670 		asl_msg_release((asl_msg_t *)msg);
671 		OSAtomicDecrement32(&global.bsd_queue_count);
672 	});
673 }
674 
675 static void
_bsd_close_idle_files()676 _bsd_close_idle_files()
677 {
678 	time_t now;
679 	struct config_rule *r;
680 	uint64_t delta;
681 
682 	now = time(NULL);
683 
684 	for (r = bsd_out_rule.tqh_first; r != NULL; r = r->entries.tqe_next)
685 	{
686 		/* only applies to files */
687 		if (r->type != DST_TYPE_FILE) continue;
688 
689 		/*
690 		 * If the last message repeat count is non-zero, a _bsd_flush_duplicates()
691 		 * call will occur within 30 seconds.  Don't bother closing the file.
692 		 */
693 		if (r->last_count > 0) continue;
694 
695 		delta = now - r->last_time;
696 		if (delta > CLOSE_ON_IDLE_SEC) _syslog_dst_close(r);
697 	}
698 }
699 
700 static int
_parse_config_file(const char * confname)701 _parse_config_file(const char *confname)
702 {
703 	FILE *cf;
704 	char *line;
705 
706 	cf = fopen(confname, "r");
707 	if (cf == NULL) return 1;
708 
709 	while (NULL != (line = get_line_from_file(cf)))
710 	{
711 		_parse_line(line);
712 		free(line);
713 	}
714 
715 	fclose(cf);
716 
717 	return 0;
718 }
719 
720 int
bsd_out_init(void)721 bsd_out_init(void)
722 {
723 	static dispatch_once_t once;
724 
725 	asldebug("%s: init\n", MY_ID);
726 
727 	TAILQ_INIT(&bsd_out_rule);
728 	_parse_config_file(_PATH_SYSLOG_CONF);
729 
730 	dispatch_once(&once, ^{
731 		bsd_out_queue = dispatch_queue_create("BSD Out Queue", NULL);
732 
733 		/* start a timer to close idle files */
734 		bsd_idle_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, bsd_out_queue);
735 		dispatch_source_set_event_handler(bsd_idle_timer, ^{ _bsd_close_idle_files(); });
736 		dispatch_source_set_timer(bsd_idle_timer, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * CLOSE_ON_IDLE_SEC), NSEC_PER_SEC * CLOSE_ON_IDLE_SEC, 0);
737 		dispatch_resume(bsd_idle_timer);
738 	});
739 
740 	return 0;
741 }
742 
743 static int
_bsd_out_close_internal(void)744 _bsd_out_close_internal(void)
745 {
746 	struct config_rule *r, *n;
747 	int i;
748 
749 	n = NULL;
750 	for (r = bsd_out_rule.tqh_first; r != NULL; r = n)
751 	{
752 		n = r->entries.tqe_next;
753 
754  		if (r->dup_timer != NULL)
755 		{
756 			if (r->last_count > 0) _bsd_send_repeat_msg(r);
757 			dispatch_source_cancel(r->dup_timer);
758 			dispatch_resume(r->dup_timer);
759 			dispatch_release(r->dup_timer);
760 		}
761 
762 		free(r->dst);
763 		free(r->addr);
764 		free(r->last_msg);
765 		free(r->fac_prefix_len);
766 		free(r->pri);
767 
768 		if (r->fd >= 0) close(r->fd);
769 
770 		if (r->facility != NULL)
771 		{
772 			for (i = 0; i < r->count; i++)
773                 free(r->facility[i]);
774 
775 			free(r->facility);
776 		}
777 
778 
779 		TAILQ_REMOVE(&bsd_out_rule, r, entries);
780 		free(r);
781 	}
782 
783 	return 0;
784 }
785 
786 int
bsd_out_close(void)787 bsd_out_close(void)
788 {
789 	dispatch_async(bsd_out_queue, ^{
790 		_bsd_out_close_internal();
791 	});
792 
793 	return 0;
794 }
795 
796 int
bsd_out_reset(void)797 bsd_out_reset(void)
798 {
799 	dispatch_async(bsd_out_queue, ^{
800 		_bsd_out_close_internal();
801 		bsd_out_init();
802 	});
803 
804 	return 0;
805 }
806 
807 #endif /* !TARGET_IPHONE_SIMULATOR */
808