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