]> git.saurik.com Git - apple/syslog.git/blame - syslogd.tproj/bsd_out.c
syslog-356.200.4.tar.gz
[apple/syslog.git] / syslogd.tproj / bsd_out.c
CommitLineData
b16a592a 1/*
a83ff38a 2 * Copyright (c) 2004-2011 Apple Inc. All rights reserved.
b16a592a
A
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
5dd30d76
A
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.
b16a592a
A
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,
5dd30d76
A
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.
b16a592a
A
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
81582353
A
24#include <TargetConditionals.h>
25
26#if TARGET_IPHONE_SIMULATOR
27struct _not_empty;
28#else
29
b16a592a
A
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>
02b408bf 39#include <ctype.h>
b16a592a
A
40#include <fcntl.h>
41#include <errno.h>
42#include <netdb.h>
c4fdb7d1 43#include <pthread.h>
b16a592a
A
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"
b16a592a
A
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
db78b1bd 61#define CLOSE_ON_IDLE_SEC 300
c4fdb7d1 62
db78b1bd
A
63static dispatch_queue_t bsd_out_queue;
64static dispatch_source_t bsd_idle_timer;
b16a592a
A
65
66struct config_rule
67{
68 uint32_t count;
69 char *dst;
70 int fd;
71 int type;
72 struct sockaddr *addr;
73 char **facility;
a83ff38a 74 uint32_t *fac_prefix_len;
b16a592a 75 int *pri;
5dd30d76
A
76 uint32_t last_hash;
77 uint32_t last_count;
78 time_t last_time;
db78b1bd 79 dispatch_source_t dup_timer;
5dd30d76 80 char *last_msg;
b16a592a
A
81 TAILQ_ENTRY(config_rule) entries;
82};
83
84static TAILQ_HEAD(cr, config_rule) bsd_out_rule;
85
57b0aad2 86extern uint32_t asl_core_string_hash(const char *s, uint32_t inlen);
5dd30d76 87
b16a592a
A
88static int
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
112static int
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;
5dd30d76 120 if (r->fd != -1) return 0;
b16a592a
A
121
122 if (r->dst[0] == '/')
123 {
5dd30d76 124 r->fd = open(r->dst, O_WRONLY | O_APPEND | O_CREAT | O_NOCTTY, 0644);
b16a592a
A
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;
c4fdb7d1 140 r->fd = -1;
b16a592a
A
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
c4fdb7d1 170 r->addr = (struct sockaddr *)malloc(ai->ai_addrlen);
02b408bf
A
171 if (r->addr == NULL) return -1;
172
b16a592a
A
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);
c4fdb7d1
A
183 free(r->addr);
184 r->addr = NULL;
b16a592a
A
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));
c4fdb7d1
A
193 free(r->addr);
194 r->addr = NULL;
b16a592a
A
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;
c4fdb7d1 206 r->fd = -1;
b16a592a
A
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
5dd30d76
A
215static void
216_syslog_dst_close(struct config_rule *r)
217{
218 if (r == NULL) return;
219
c4fdb7d1
A
220 if (r->addr != NULL)
221 {
222 free(r->addr);
223 r->addr = NULL;
224 }
225
5dd30d76
A
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;
5dd30d76
A
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
c4fdb7d1
A
254static char *
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
b16a592a
A
280static int
281_parse_line(char *s)
282{
a83ff38a 283 char **semi, **comma, *star;
b16a592a
A
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
c4fdb7d1 291 semi = explode(s, "; \t");
b16a592a
A
292
293 if (semi == NULL) return -1;
294 out = (struct config_rule *)calloc(1, sizeof(struct config_rule));
295 if (out == NULL) return -1;
5dd30d76 296 out->fd = -1;
b16a592a
A
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]);
02b408bf
A
308 if (out->dst == NULL) return -1;
309
b16a592a
A
310 for (i = 0; i < lasts; i++)
311 {
312 if (semi[i][0] == '\0') continue;
c4fdb7d1 313 comma = explode(semi[i], ",.");
b16a592a
A
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 *));
a83ff38a 330 out->fac_prefix_len = (uint32_t *)calloc(1, sizeof(uint32_t));
b16a592a
A
331 out->pri = (int *)calloc(1, sizeof(int));
332 }
333 else
334 {
02b408bf 335 out->facility = (char **)reallocf(out->facility, (out->count + 1) * sizeof(char *));
a83ff38a 336 out->fac_prefix_len = (uint32_t *)reallocf(out->fac_prefix_len, (out->count + 1) * sizeof(uint32_t));
02b408bf 337 out->pri = (int *)reallocf(out->pri, (out->count + 1) * sizeof(int));
b16a592a 338 }
02b408bf
A
339
340 if (out->facility == NULL) return -1;
a83ff38a 341 if (out->fac_prefix_len == NULL) return -1;
02b408bf 342 if (out->pri == NULL) return -1;
5dd30d76 343
c4fdb7d1 344 out->facility[out->count] = _clean_facility_name(comma[j]);
02b408bf
A
345 if (out->facility[out->count] == NULL) return -1;
346
a83ff38a
A
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
b16a592a
A
351 out->pri[out->count] = pri;
352 out->count++;
353 }
354
81582353 355 free_string_list(comma);
b16a592a
A
356 }
357
81582353 358 free_string_list(semi);
b16a592a
A
359
360 TAILQ_INSERT_TAIL(&bsd_out_rule, out, entries);
361
362 return 0;
363}
364
365static int
db78b1bd 366_bsd_send_repeat_msg(struct config_rule *r)
5dd30d76 367{
1496e7d1 368 char vt[32], *msg;
5dd30d76
A
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
db78b1bd
A
376 /* stop the timer */
377 dispatch_suspend(r->dup_timer);
378
5dd30d76 379 tick = time(NULL);
1496e7d1
A
380 memset(vt, 0, sizeof(vt));
381 ctime_r(&tick, vt);
382 vt[19] = '\0';
5dd30d76
A
383
384 msg = NULL;
1496e7d1 385 asprintf(&msg, "%s: --- last message repeated %u time%s ---\n", vt + 4, r->last_count, (r->last_count == 1) ? "" : "s");
db78b1bd 386 r->last_count = 0;
5dd30d76
A
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
418static int
f3df4c03 419_bsd_send(asl_msg_t *msg, struct config_rule *r, char **out, char **fwd, time_t now)
b16a592a 420{
a83ff38a
A
421 char *sf, *outmsg;
422 const char *vlevel, *vfacility;
423 size_t outlen;
5dd30d76 424 int pf, fc, status, is_dup, do_write;
a83ff38a 425 uint32_t msg_hash, n;
b16a592a
A
426
427 if (out == NULL) return -1;
428 if (fwd == NULL) return -1;
5dd30d76 429 if (r == NULL) return -1;
b16a592a 430
a83ff38a
A
431 _syslog_dst_open(r);
432
b16a592a
A
433 if (r->type == DST_TYPE_NOTE)
434 {
435 notify_post(r->dst+1);
436 return 0;
437 }
438
5dd30d76 439 msg_hash = 0;
02b408bf 440 outmsg = NULL;
b16a592a
A
441
442 /* Build output string if it hasn't been built by a previous rule-match */
443 if (*out == NULL)
444 {
a83ff38a
A
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;
b16a592a
A
447 }
448
5dd30d76
A
449 /* check if message is a duplicate of the last message, and inside the dup time window */
450 is_dup = 0;
57b0aad2 451 if ((global.bsd_max_dup_time > 0) && (*out != NULL) && (r->last_msg != NULL))
5dd30d76 452 {
57b0aad2 453 msg_hash = asl_core_string_hash(*out + 16, strlen(*out + 16));
5dd30d76
A
454 if ((r->last_hash == msg_hash) && (!strcmp(r->last_msg, *out + 16)))
455 {
57b0aad2 456 if ((now - r->last_time) < global.bsd_max_dup_time) is_dup = 1;
5dd30d76
A
457 }
458 }
459
b16a592a
A
460 if ((*fwd == NULL) && (r->type == DST_TYPE_SOCK))
461 {
462 pf = 7;
f3df4c03 463 vlevel = asl_msg_get_val_for_key(msg, ASL_KEY_LEVEL);
b16a592a
A
464 if (vlevel != NULL) pf = atoi(vlevel);
465
f3df4c03 466 fc = asl_syslog_faciliy_name_to_num(asl_msg_get_val_for_key(msg, ASL_KEY_FACILITY));
b16a592a
A
467 if (fc > 0) pf |= fc;
468
469 sf = NULL;
470 asprintf(&sf, "<%d>%s", pf, *out);
471 if (sf == NULL) return -1;
5dd30d76 472
b16a592a
A
473 *fwd = sf;
474 }
475
b16a592a
A
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 {
5dd30d76
A
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 */
db78b1bd 485 if ((r->type == DST_TYPE_FILE) && (is_dup == 0) && (r->last_count > 0)) _bsd_send_repeat_msg(r);
5dd30d76
A
486
487 do_write = 1;
488
b16a592a
A
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 */
f3df4c03 495 vfacility = asl_msg_get_val_for_key(msg, ASL_KEY_FACILITY);
5dd30d76 496 if ((vfacility != NULL) && (!strcmp(vfacility, FACILITY_KERNEL)) && (r->type == DST_TYPE_CONS)) do_write = 0;
db78b1bd
A
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 }
5dd30d76
A
515
516 if (do_write == 0) status = outlen;
517 else status = write(r->fd, *out, outlen);
b16a592a 518
b16a592a
A
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);
5dd30d76 525 r->fd = open(r->dst, O_WRONLY | O_APPEND | O_CREAT | O_NOCTTY, 0644);
b16a592a
A
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 }
5dd30d76 539 else if ((r->type == DST_TYPE_SOCK) && (r->addr != NULL))
b16a592a
A
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 {
81582353 546#if !TARGET_OS_EMBEDDED
a83ff38a 547 FILE *pw = popen(_PATH_WALL, "w");
b16a592a
A
548 if (pw < 0)
549 {
550 asldebug("%s: error sending wall message: %s\n", MY_ID, strerror(errno));
551 return -1;
552 }
553
c4fdb7d1 554 fprintf(pw, "%s", *out);
b16a592a 555 pclose(pw);
a83ff38a 556#endif
b16a592a
A
557 }
558
5dd30d76
A
559 if (is_dup == 1)
560 {
561 r->last_count++;
562 }
563 else
564 {
a83ff38a 565 free(r->last_msg);
5dd30d76
A
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
b16a592a
A
575 return 0;
576}
577
578static int
f3df4c03 579_bsd_rule_match(asl_msg_t *msg, struct config_rule *r)
b16a592a 580{
db78b1bd
A
581 uint32_t i, test, f;
582 int32_t pri;
b16a592a
A
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;
f3df4c03 599 val = asl_msg_get_val_for_key(msg, ASL_KEY_FACILITY);
a83ff38a
A
600
601 if (strcmp(r->facility[i], "*") == 0)
b16a592a 602 {
a83ff38a
A
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;
b16a592a
A
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
f3df4c03 623 val = asl_msg_get_val_for_key(msg, ASL_KEY_LEVEL);
b16a592a
A
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
db78b1bd 635static int
f3df4c03 636_bsd_match_and_send(asl_msg_t *msg)
b16a592a
A
637{
638 struct config_rule *r;
639 char *out, *fwd;
db78b1bd 640 time_t now;
b16a592a
A
641
642 if (msg == NULL) return -1;
643
644 out = NULL;
645 fwd = NULL;
646
db78b1bd 647 now = time(NULL);
5dd30d76 648
b16a592a
A
649 for (r = bsd_out_rule.tqh_first; r != NULL; r = r->entries.tqe_next)
650 {
db78b1bd 651 if (_bsd_rule_match(msg, r) == 1) _bsd_send(msg, r, &out, &fwd, now);
b16a592a
A
652 }
653
a83ff38a
A
654 free(out);
655 free(fwd);
b16a592a
A
656
657 return 0;
658}
659
c4fdb7d1 660void
5222c21d 661bsd_out_message(asl_msg_t *msg, int64_t msize)
c4fdb7d1 662{
db78b1bd
A
663 if (msg == NULL) return;
664
81582353 665 OSAtomicIncrement32(&global.bsd_queue_count);
db78b1bd
A
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);
5222c21d
A
671
672 /* end of the output module chain (after asl) - decrement global memory stats */
673 OSAtomicAdd64(-1ll * msize, &global.memory_size);
674
81582353 675 OSAtomicDecrement32(&global.bsd_queue_count);
db78b1bd
A
676 });
677}
678
679static void
680_bsd_close_idle_files()
681{
682 time_t now;
c4fdb7d1
A
683 struct config_rule *r;
684 uint64_t delta;
685
db78b1bd
A
686 now = time(NULL);
687
c4fdb7d1
A
688 for (r = bsd_out_rule.tqh_first; r != NULL; r = r->entries.tqe_next)
689 {
690 /* only applies to files */
691 if (r->type != DST_TYPE_FILE) continue;
692
693 /*
db78b1bd 694 * If the last message repeat count is non-zero, a _bsd_flush_duplicates()
c4fdb7d1
A
695 * call will occur within 30 seconds. Don't bother closing the file.
696 */
697 if (r->last_count > 0) continue;
698
699 delta = now - r->last_time;
700 if (delta > CLOSE_ON_IDLE_SEC) _syslog_dst_close(r);
701 }
702}
703
b16a592a
A
704static int
705_parse_config_file(const char *confname)
706{
707 FILE *cf;
708 char *line;
709
710 cf = fopen(confname, "r");
711 if (cf == NULL) return 1;
712
713 while (NULL != (line = get_line_from_file(cf)))
714 {
715 _parse_line(line);
716 free(line);
717 }
718
719 fclose(cf);
720
721 return 0;
722}
723
724int
725bsd_out_init(void)
726{
db78b1bd
A
727 static dispatch_once_t once;
728
b16a592a
A
729 asldebug("%s: init\n", MY_ID);
730
731 TAILQ_INIT(&bsd_out_rule);
db78b1bd 732 _parse_config_file(_PATH_SYSLOG_CONF);
b16a592a 733
db78b1bd
A
734 dispatch_once(&once, ^{
735 bsd_out_queue = dispatch_queue_create("BSD Out Queue", NULL);
b16a592a 736
db78b1bd
A
737 /* start a timer to close idle files */
738 bsd_idle_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, bsd_out_queue);
f3df4c03 739 dispatch_source_set_event_handler(bsd_idle_timer, ^{ _bsd_close_idle_files(); });
db78b1bd
A
740 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);
741 dispatch_resume(bsd_idle_timer);
742 });
b16a592a 743
b16a592a
A
744 return 0;
745}
746
db78b1bd
A
747static int
748_bsd_out_close_internal(void)
b16a592a
A
749{
750 struct config_rule *r, *n;
751 int i;
752
753 n = NULL;
754 for (r = bsd_out_rule.tqh_first; r != NULL; r = n)
755 {
756 n = r->entries.tqe_next;
5dd30d76 757
db78b1bd
A
758 if (r->dup_timer != NULL)
759 {
760 if (r->last_count > 0) _bsd_send_repeat_msg(r);
761 dispatch_source_cancel(r->dup_timer);
762 dispatch_resume(r->dup_timer);
763 dispatch_release(r->dup_timer);
764 }
765
766 free(r->dst);
a83ff38a
A
767 free(r->addr);
768 free(r->last_msg);
769 free(r->fac_prefix_len);
770 free(r->pri);
771
772 if (r->fd >= 0) close(r->fd);
773
b16a592a
A
774 if (r->facility != NULL)
775 {
776 for (i = 0; i < r->count; i++)
a83ff38a
A
777 free(r->facility[i]);
778
b16a592a
A
779 free(r->facility);
780 }
b16a592a 781
f3df4c03 782
b16a592a
A
783 TAILQ_REMOVE(&bsd_out_rule, r, entries);
784 free(r);
785 }
786
787 return 0;
788}
c4fdb7d1
A
789
790int
db78b1bd 791bsd_out_close(void)
c4fdb7d1 792{
db78b1bd
A
793 dispatch_async(bsd_out_queue, ^{
794 _bsd_out_close_internal();
795 });
1496e7d1 796
db78b1bd
A
797 return 0;
798}
799
800int
801bsd_out_reset(void)
802{
803 dispatch_async(bsd_out_queue, ^{
804 _bsd_out_close_internal();
805 bsd_out_init();
806 });
1496e7d1 807
c4fdb7d1
A
808 return 0;
809}
81582353
A
810
811#endif /* !TARGET_IPHONE_SIMULATOR */