]> git.saurik.com Git - apple/syslog.git/blob - syslogd.tproj/bsd_out.c
6e7bd2affaffea576724b399120f5d2056a1e395
[apple/syslog.git] / syslogd.tproj / bsd_out.c
1 /*
2 * Copyright (c) 2004-2008 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 <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>
33 #include <ctype.h>
34 #include <fcntl.h>
35 #include <errno.h>
36 #include <netdb.h>
37 #include <notify.h>
38 #include "daemon.h"
39
40 #define MY_ID "bsd_out"
41
42 #define _PATH_WALL "/usr/bin/wall"
43 #define ASL_KEY_FACILITY "Facility"
44 #define FACILITY_KERNEL "kern"
45 #define _PATH_CONSOLE "/dev/console"
46 #define IndexNull ((uint32_t)-1)
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
55 static asl_msg_t *query = NULL;
56 static int reset = 0;
57
58 struct config_rule
59 {
60 uint32_t count;
61 char *dst;
62 int fd;
63 int type;
64 struct sockaddr *addr;
65 char **facility;
66 int *pri;
67 uint32_t last_hash;
68 uint32_t last_count;
69 time_t last_time;
70 char *last_msg;
71 TAILQ_ENTRY(config_rule) entries;
72 };
73
74 static TAILQ_HEAD(cr, config_rule) bsd_out_rule;
75
76 extern uint32_t asl_core_string_hash(const char *s, uint32_t inlen);
77
78 int bsd_out_close();
79 static int _parse_config_file(const char *);
80
81 static void
82 _do_reset(void)
83 {
84 bsd_out_close();
85 _parse_config_file(_PATH_SYSLOG_CONF);
86 }
87
88 static char **
89 _insertString(char *s, char **l, uint32_t x)
90 {
91 int i, len;
92
93 if (s == NULL) return l;
94 if (l == NULL)
95 {
96 l = (char **)malloc(2 * sizeof(char *));
97 if (l == NULL) return NULL;
98
99 l[0] = strdup(s);
100 if (l[0] == NULL)
101 {
102 free(l);
103 return NULL;
104 }
105
106 l[1] = NULL;
107 return l;
108 }
109
110 for (i = 0; l[i] != NULL; i++);
111 len = i + 1; /* count the NULL on the end of the list too! */
112
113 l = (char **)reallocf(l, (len + 1) * sizeof(char *));
114 if (l == NULL) return NULL;
115
116 if ((x >= (len - 1)) || (x == IndexNull))
117 {
118 l[len - 1] = strdup(s);
119 if (l[len - 1] == NULL)
120 {
121 free(l);
122 return NULL;
123 }
124
125 l[len] = NULL;
126 return l;
127 }
128
129 for (i = len; i > x; i--) l[i] = l[i - 1];
130 l[x] = strdup(s);
131 if (l[x] == NULL) return NULL;
132
133 return l;
134 }
135
136 static char **
137 _explode(char *s, char *delim)
138 {
139 char **l = NULL;
140 char *p, *t;
141 int i, n;
142
143 if (s == NULL) return NULL;
144
145 p = s;
146 while (p[0] != '\0')
147 {
148 for (i = 0; ((p[i] != '\0') && (strchr(delim, p[i]) == NULL)); i++);
149 n = i;
150 t = malloc(n + 1);
151 if (t == NULL) return NULL;
152
153 for (i = 0; i < n; i++) t[i] = p[i];
154 t[n] = '\0';
155 l = _insertString(t, l, IndexNull);
156 free(t);
157 t = NULL;
158 if (p[i] == '\0') return l;
159 if (p[i + 1] == '\0') l = _insertString("", l, IndexNull);
160 p = p + i + 1;
161 }
162
163 return l;
164 }
165
166 static void
167 _freeList(char **l)
168 {
169 int i;
170
171 if (l == NULL) return;
172 for (i = 0; l[i] != NULL; i++) free(l[i]);
173 free(l);
174 }
175
176 static int
177 _level_for_name(const char *name)
178 {
179 if (name == NULL) return -1;
180
181 if (!strcasecmp(name, "emerg")) return ASL_LEVEL_EMERG;
182 if (!strcasecmp(name, "panic")) return ASL_LEVEL_EMERG;
183 if (!strcasecmp(name, "alert")) return ASL_LEVEL_ALERT;
184 if (!strcasecmp(name, "crit")) return ASL_LEVEL_CRIT;
185 if (!strcasecmp(name, "err")) return ASL_LEVEL_ERR;
186 if (!strcasecmp(name, "error")) return ASL_LEVEL_ERR;
187 if (!strcasecmp(name, "warn")) return ASL_LEVEL_WARNING;
188 if (!strcasecmp(name, "warning")) return ASL_LEVEL_WARNING;
189 if (!strcasecmp(name, "notice")) return ASL_LEVEL_NOTICE;
190 if (!strcasecmp(name, "info")) return ASL_LEVEL_INFO;
191 if (!strcasecmp(name, "debug")) return ASL_LEVEL_DEBUG;
192 if (!strcmp(name, "*")) return ASL_LEVEL_DEBUG;
193
194 /* special case */
195 if (!strcasecmp(name, "none")) return -2;
196
197 return -1;
198 }
199
200 static int
201 _syslog_dst_open(struct config_rule *r)
202 {
203 int i;
204 char *node, *serv;
205 struct addrinfo hints, *gai, *ai;
206
207 if (r == NULL) return -1;
208 if (r->fd != -1) return 0;
209
210 if (r->dst[0] == '/')
211 {
212 r->fd = open(r->dst, O_WRONLY | O_APPEND | O_CREAT | O_NOCTTY, 0644);
213 if (r->fd < 0)
214 {
215 asldebug("%s: open failed for file: %s (%s)\n", MY_ID, r->dst, strerror(errno));
216 return -1;
217 }
218
219 r->type = DST_TYPE_FILE;
220 if (!strcmp(r->dst, _PATH_CONSOLE)) r->type = DST_TYPE_CONS;
221
222 return 0;
223 }
224
225 if (r->dst[0] == '!')
226 {
227 r->type = DST_TYPE_NOTE;
228 r->fd = 0;
229 return 0;
230 }
231
232 if (r->dst[0] == '@')
233 {
234 node = strdup(r->dst + 1);
235 if (node == NULL) return -1;
236
237 serv = NULL;
238 serv = strrchr(node, ':');
239 if (serv != NULL) *serv++ = '\0';
240 else serv = "syslog";
241
242 memset(&hints, 0, sizeof(hints));
243 hints.ai_family = PF_UNSPEC;
244 hints.ai_socktype = SOCK_DGRAM;
245 i = getaddrinfo(node, serv, &hints, &gai);
246 free(node);
247 if (i != 0)
248 {
249 asldebug("%s: getaddrinfo failed for node %s service %s: (%s)\n", MY_ID, node, serv, gai_strerror(i));
250 return -1;
251 }
252
253 for (ai = gai; ai != NULL; ai = ai->ai_next)
254 {
255 r->fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
256 if (r->fd < 0) continue;
257
258 r->addr = (struct sockaddr *)calloc(1, ai->ai_addrlen);
259 if (r->addr == NULL) return -1;
260
261 memcpy(r->addr, ai->ai_addr, ai->ai_addrlen);
262
263 break;
264 }
265
266 freeaddrinfo(gai);
267
268 if (r->fd < 0)
269 {
270 asldebug("%s: connection failed for %s\n", MY_ID, (r->dst) + 1);
271 return -1;
272 }
273
274 if (fcntl(r->fd, F_SETFL, O_NONBLOCK) < 0)
275 {
276 close(r->fd);
277 r->fd = -1;
278 asldebug("%s: couldn't set O_NONBLOCK for fd %d: %s\n", MY_ID, r->fd, strerror(errno));
279 return -1;
280 }
281
282 r->type = DST_TYPE_SOCK;
283 return 0;
284
285 }
286
287 if (strcmp(r->dst, "*") == 0)
288 {
289 r->type = DST_TYPE_WALL;
290 r->fd = 0;
291 return 0;
292 }
293
294 /* Can't deal with dst! */
295 asldebug("%s: unsupported / unknown output name: %s\n", MY_ID, r->dst);
296 return -1;
297 }
298
299 static void
300 _syslog_dst_close(struct config_rule *r)
301 {
302 if (r == NULL) return;
303
304 switch (r->type)
305 {
306 case DST_TYPE_FILE:
307 case DST_TYPE_CONS:
308 {
309 if (r->fd >= 0) close(r->fd);
310 r->fd = -1;
311 break;
312 }
313
314 case DST_TYPE_SOCK:
315 {
316 if (r->fd >= 0) close(r->fd);
317 r->fd = -1;
318 if (r->addr != NULL) free(r->addr);
319 r->addr = NULL;
320 break;
321 }
322
323 case DST_TYPE_NONE:
324 case DST_TYPE_WALL:
325 case DST_TYPE_NOTE:
326 default:
327 {
328 /* do nothing */
329 return;
330 }
331 }
332 }
333
334 static int
335 _parse_line(char *s)
336 {
337 char **semi, **comma;
338 int i, j, n, lasts, lastc, pri;
339 struct config_rule *out;
340
341 if (s == NULL) return -1;
342 while ((*s == ' ') || (*s == '\t')) s++;
343 if (*s == '#') return -1;
344
345 semi = _explode(s, "; \t");
346
347 if (semi == NULL) return -1;
348 out = (struct config_rule *)calloc(1, sizeof(struct config_rule));
349 if (out == NULL) return -1;
350 out->fd = -1;
351
352 n = 0;
353 lasts = -1;
354 for (i = 0; semi[i] != NULL; i++)
355 {
356 if (semi[i][0] == '\0') continue;
357 n++;
358 lasts = i;
359 }
360
361 out->dst = strdup(semi[lasts]);
362 if (out->dst == NULL) return -1;
363
364 for (i = 0; i < lasts; i++)
365 {
366 if (semi[i][0] == '\0') continue;
367 comma = _explode(semi[i], ",.");
368 lastc = -1;
369 for (j = 0; comma[j] != NULL; j++)
370 {
371 if (comma[j][0] == '\0') continue;
372 lastc = j;
373 }
374
375 for (j = 0; j < lastc; j++)
376 {
377 if (comma[j][0] == '\0') continue;
378 pri = _level_for_name(comma[lastc]);
379 if (pri == -1) continue;
380
381 if (out->count == 0)
382 {
383 out->facility = (char **)calloc(1, sizeof(char *));
384 out->pri = (int *)calloc(1, sizeof(int));
385 }
386 else
387 {
388 out->facility = (char **)reallocf(out->facility, (out->count + 1) * sizeof(char *));
389 out->pri = (int *)reallocf(out->pri, (out->count + 1) * sizeof(int));
390 }
391
392 if (out->facility == NULL) return -1;
393 if (out->pri == NULL) return -1;
394
395 out->facility[out->count] = strdup(comma[j]);
396 if (out->facility[out->count] == NULL) return -1;
397
398 out->pri[out->count] = pri;
399 out->count++;
400 }
401
402 _freeList(comma);
403 }
404
405 _freeList(semi);
406
407 TAILQ_INSERT_TAIL(&bsd_out_rule, out, entries);
408
409 return 0;
410 }
411
412 static char *
413 bsd_log_string(const char *msg)
414 {
415 uint32_t i, len, outlen;
416 char *out, *q;
417 uint8_t c;
418
419 if (msg == NULL) return NULL;
420
421 len = strlen(msg);
422 while ((len > 0) && (msg[len - 1] == '\n')) len--;
423
424 if (len == 0) return NULL;
425
426 outlen = len + 1;
427 for (i = 0; i < len; i++)
428 {
429 c = msg[i];
430 if (isascii(c) && iscntrl(c) && (c != '\t')) outlen++;
431 }
432
433 out = malloc(outlen);
434 if (out == NULL) return NULL;
435
436 q = out;
437
438 for (i = 0; i < len; i++)
439 {
440 c = msg[i];
441
442 if (isascii(c) && iscntrl(c))
443 {
444 if (c == '\n')
445 {
446 *q++ = '\\';
447 *q++ = 'n';
448 }
449 else if (c == '\t')
450 {
451 *q++ = c;
452 }
453 else
454 {
455 *q++ = '^';
456 *q++ = c ^ 0100;
457 }
458 }
459 else
460 {
461 *q++ = c;
462 }
463 }
464
465 *q = '\0';
466
467 return out;
468 }
469
470 static int
471 _syslog_send_repeat_msg(struct config_rule *r)
472 {
473 char vt[16], *p, *msg;
474 time_t tick;
475 int len, status;
476
477 if (r == NULL) return -1;
478 if (r->type != DST_TYPE_FILE) return 0;
479 if (r->last_count == 0) return 0;
480
481 tick = time(NULL);
482 p = ctime(&tick);
483 if (p == NULL) return -1;
484
485 memcpy(vt, p+4, 15);
486 vt[15] = '\0';
487
488 msg = NULL;
489 asprintf(&msg, "%s: --- last message repeated %u time%s ---\n", vt, r->last_count, (r->last_count == 1) ? "" : "s");
490 if (msg == NULL) return -1;
491
492 len = strlen(msg);
493 status = write(r->fd, msg, len);
494 if ((status < 0) || (status < len))
495 {
496 asldebug("%s: error writing repeat message (%s): %s\n", MY_ID, r->dst, strerror(errno));
497
498 /* Try re-opening the file (once) and write again */
499 close(r->fd);
500 r->fd = open(r->dst, O_WRONLY | O_APPEND | O_CREAT | O_NOCTTY, 0644);
501 if (r->fd < 0)
502 {
503 asldebug("%s: re-open failed for file: %s (%s)\n", MY_ID, r->dst, strerror(errno));
504 free(msg);
505 return -1;
506 }
507
508 status = write(r->fd, msg, len);
509 if ((status < 0) || (status < len))
510 {
511 asldebug("%s: error re-writing message (%s): %s\n", MY_ID, r->dst, strerror(errno));
512 free(msg);
513 return -1;
514 }
515 }
516
517 free(msg);
518 return 0;
519 }
520
521 static int
522 _syslog_send(asl_msg_t *msg, struct config_rule *r, char **out, char **fwd, time_t now)
523 {
524 char *so, *sf, *vt, *p, *outmsg;
525 const char *vtime, *vhost, *vident, *vpid, *vmsg, *vlevel, *vfacility, *vrefproc, *vrefpid;
526 size_t outlen, n;
527 time_t tick;
528 int pf, fc, status, is_dup, do_write;
529 FILE *pw;
530 uint32_t msg_hash;
531
532 if (out == NULL) return -1;
533 if (fwd == NULL) return -1;
534 if (r == NULL) return -1;
535
536 if (r->type == DST_TYPE_NOTE)
537 {
538 notify_post(r->dst+1);
539 return 0;
540 }
541
542 msg_hash = 0;
543 vt = NULL;
544 outmsg = NULL;
545
546 /* Build output string if it hasn't been built by a previous rule-match */
547 if (*out == NULL)
548 {
549 vtime = asl_get(msg, ASL_KEY_TIME);
550 if (vtime != NULL)
551 {
552 tick = asl_parse_time(vtime);
553 if (tick != (time_t)-1)
554 {
555 p = ctime(&tick);
556 vt = malloc(16);
557 if (vt == NULL) return -1;
558
559 memcpy(vt, p+4, 15);
560 vt[15] = '\0';
561 }
562 else if (strlen(vtime) < 24)
563 {
564 vt = strdup(vtime);
565 if (vt == NULL) return -1;
566 }
567 else
568 {
569 vt = malloc(16);
570 if (vt == NULL) return -1;
571
572 memcpy(vt, vtime+4, 15);
573 vt[15] = '\0';
574 }
575 }
576
577 if (vt == NULL)
578 {
579 tick = now;
580 p = ctime(&tick);
581 vt = malloc(16);
582 if (vt == NULL) return -1;
583
584 memcpy(vt, p+4, 15);
585 vt[15] = '\0';
586 }
587
588 vhost = asl_get(msg, ASL_KEY_HOST);
589 if (vhost == NULL) vhost = "localhost";
590
591 vident = asl_get(msg, ASL_KEY_SENDER);
592 if ((vident != NULL) && (!strcmp(vident, "Unknown"))) vident = NULL;
593
594 vpid = asl_get(msg, ASL_KEY_PID);
595 if ((vpid != NULL) && (!strcmp(vpid, "-1"))) vpid = NULL;
596
597 if ((vpid != NULL) && (vident == NULL)) vident = "Unknown";
598
599 vrefproc = asl_get(msg, ASL_KEY_REF_PROC);
600 vrefpid = asl_get(msg, ASL_KEY_REF_PID);
601
602 vmsg = asl_get(msg, ASL_KEY_MSG);
603 if (vmsg != NULL) outmsg = bsd_log_string(vmsg);
604
605 n = 0;
606 /* Time + " " */
607 if (vt != NULL) n += (strlen(vt) + 1);
608
609 /* Host + " " */
610 if (vhost != NULL) n += (strlen(vhost) + 1);
611
612 /* Sender */
613 if (vident != NULL) n += strlen(vident);
614
615 /* "[" PID "]" */
616 if (vpid != NULL) n += (strlen(vpid) + 2);
617
618 /* " (" */
619 if ((vrefproc != NULL) || (vrefpid != NULL)) n += 2;
620
621 /* RefProc */
622 if (vrefproc != NULL) n += strlen(vrefproc);
623
624 /* "[" RefPID "]" */
625 if (vrefpid != NULL) n += (strlen(vrefpid) + 2);
626
627 /* ")" */
628 if ((vrefproc != NULL) || (vrefpid != NULL)) n += 1;
629
630 /* ": " */
631 n += 2;
632
633 /* Message */
634 if (outmsg != NULL) n += strlen(outmsg);
635
636 if (n == 0) return -1;
637
638 /* "\n" + nul */
639 n += 2;
640
641 so = calloc(1, n);
642 if (so == NULL) return -1;
643
644 if (vt != NULL)
645 {
646 strcat(so, vt);
647 strcat(so, " ");
648 }
649
650 if (vhost != NULL)
651 {
652 strcat(so, vhost);
653 strcat(so, " ");
654 }
655
656 if (vident != NULL)
657 {
658 strcat(so, vident);
659 if (vpid != NULL)
660 {
661 strcat(so, "[");
662 strcat(so, vpid);
663 strcat(so, "]");
664 }
665 }
666
667 if ((vrefproc != NULL) || (vrefpid != NULL))
668 {
669 strcat(so, " (");
670
671 if (vrefproc != NULL) strcat(so, vrefproc);
672
673 if (vrefpid != NULL)
674 {
675 strcat(so, "[");
676 strcat(so, vrefpid);
677 strcat(so, "]");
678 }
679
680 strcat(so, ")");
681 }
682
683 strcat(so, ": ");
684
685 if (outmsg != NULL)
686 {
687 strcat(so, outmsg);
688 free(outmsg);
689 }
690
691 strcat(so, "\n");
692
693 free(vt);
694 *out = so;
695 }
696
697 /* check if message is a duplicate of the last message, and inside the dup time window */
698 is_dup = 0;
699 if ((global.bsd_max_dup_time > 0) && (*out != NULL) && (r->last_msg != NULL))
700 {
701 msg_hash = asl_core_string_hash(*out + 16, strlen(*out + 16));
702 if ((r->last_hash == msg_hash) && (!strcmp(r->last_msg, *out + 16)))
703 {
704 if ((now - r->last_time) < global.bsd_max_dup_time) is_dup = 1;
705 }
706 }
707
708 if ((*fwd == NULL) && (r->type == DST_TYPE_SOCK))
709 {
710 pf = 7;
711 vlevel = asl_get(msg, ASL_KEY_LEVEL);
712 if (vlevel != NULL) pf = atoi(vlevel);
713
714 fc = asl_syslog_faciliy_name_to_num(asl_get(msg, ASL_KEY_FACILITY));
715 if (fc > 0) pf |= fc;
716
717 sf = NULL;
718 asprintf(&sf, "<%d>%s", pf, *out);
719 if (sf == NULL) return -1;
720
721 *fwd = sf;
722 }
723
724 outlen = 0;
725 if (r->type == DST_TYPE_SOCK) outlen = strlen(*fwd);
726 else outlen = strlen(*out);
727
728 _syslog_dst_open(r);
729
730 if ((r->type == DST_TYPE_FILE) || (r->type == DST_TYPE_CONS))
731 {
732 /*
733 * If current message is NOT a duplicate and r->last_count > 0
734 * we need to write a "last message was repeated N times" log entry
735 */
736 if ((r->type == DST_TYPE_FILE) && (is_dup == 0) && (r->last_count > 0)) _syslog_send_repeat_msg(r);
737
738 do_write = 1;
739
740 /*
741 * Special case for kernel messages.
742 * Don't write kernel messages to /dev/console.
743 * The kernel printf routine already sends them to /dev/console
744 * so writing them here would cause duplicates.
745 */
746 vfacility = asl_get(msg, ASL_KEY_FACILITY);
747 if ((vfacility != NULL) && (!strcmp(vfacility, FACILITY_KERNEL)) && (r->type == DST_TYPE_CONS)) do_write = 0;
748 if ((do_write == 1) && (r->type == DST_TYPE_FILE) && (is_dup == 1)) do_write = 0;
749
750 if (do_write == 0) status = outlen;
751 else status = write(r->fd, *out, outlen);
752
753 if ((status < 0) || (status < outlen))
754 {
755 asldebug("%s: error writing message (%s): %s\n", MY_ID, r->dst, strerror(errno));
756
757 /* Try re-opening the file (once) and write again */
758 close(r->fd);
759 r->fd = open(r->dst, O_WRONLY | O_APPEND | O_CREAT | O_NOCTTY, 0644);
760 if (r->fd < 0)
761 {
762 asldebug("%s: re-open failed for file: %s (%s)\n", MY_ID, r->dst, strerror(errno));
763 return -1;
764 }
765
766 status = write(r->fd, *out, outlen);
767 if ((status < 0) || (status < outlen))
768 {
769 asldebug("%s: error re-writing message (%s): %s\n", MY_ID, r->dst, strerror(errno));
770 }
771 }
772 }
773 else if ((r->type == DST_TYPE_SOCK) && (r->addr != NULL))
774 {
775 status = sendto(r->fd, *fwd, outlen, 0, r->addr, r->addr->sa_len);
776 if (status < 0) asldebug("%s: error sending message (%s): %s\n", MY_ID, r->dst, strerror(errno));
777 }
778 else if (r->type == DST_TYPE_WALL)
779 {
780 pw = popen(_PATH_WALL, "w");
781 if (pw < 0)
782 {
783 asldebug("%s: error sending wall message: %s\n", MY_ID, strerror(errno));
784 return -1;
785 }
786
787 fprintf(pw, *out);
788 pclose(pw);
789 }
790
791 _syslog_dst_close(r);
792
793 if (is_dup == 1)
794 {
795 r->last_count++;
796 }
797 else
798 {
799 if (r->last_msg != NULL) free(r->last_msg);
800 r->last_msg = NULL;
801
802 if (*out != NULL) r->last_msg = strdup(*out + 16);
803
804 r->last_hash = msg_hash;
805 r->last_count = 0;
806 r->last_time = now;
807 }
808
809 return 0;
810 }
811
812 static int
813 _syslog_rule_match(asl_msg_t *msg, struct config_rule *r)
814 {
815 uint32_t i, test, f, pri;
816 const char *val;
817
818 if (msg == NULL) return 0;
819 if (r == NULL) return 0;
820 if (r->count == 0) return 0;
821
822 test = 0;
823
824 for (i = 0; i < r->count; i++)
825 {
826 if (r->pri[i] == -1) continue;
827
828 if ((test == 1) && (r->pri[i] >= 0)) continue;
829 if ((test == 0) && (r->pri[i] == -2)) continue;
830
831 f = 0;
832 if (strcmp(r->facility[i], "*") == 0) f = 1;
833 else
834 {
835 val = asl_get(msg, ASL_KEY_FACILITY);
836 if ((val != NULL) && (strcasecmp(r->facility[i], val) == 0)) f = 1;
837 }
838
839 if (f == 0) continue;
840
841 /* Turn off matching facility with priority "none" */
842 if (r->pri[i] == -2)
843 {
844 test = 0;
845 continue;
846 }
847
848 val = asl_get(msg, ASL_KEY_LEVEL);
849 if (val == NULL) continue;
850
851 pri = atoi(val);
852 if (pri < 0) continue;
853
854 if (pri <= r->pri[i]) test = 1;
855 }
856
857 return test;
858 }
859
860 int
861 bsd_out_sendmsg(asl_msg_t *msg, const char *outid)
862 {
863 struct config_rule *r;
864 char *out, *fwd;
865 time_t tick;
866 uint64_t delta;
867
868 if (reset != 0)
869 {
870 _do_reset();
871 reset = 0;
872 }
873
874 if (msg == NULL) return -1;
875
876 out = NULL;
877 fwd = NULL;
878
879 tick = time(NULL);
880 global.bsd_flush_time = 0;
881
882 for (r = bsd_out_rule.tqh_first; r != NULL; r = r->entries.tqe_next)
883 {
884 if (_syslog_rule_match(msg, r) == 1) _syslog_send(msg, r, &out, &fwd, tick);
885 if ((r->type == DST_TYPE_FILE) && (r->last_count > 0))
886 {
887 delta = tick - r->last_time;
888 if (delta < global.bsd_max_dup_time)
889 {
890 delta = global.bsd_max_dup_time - delta;
891 if (global.bsd_flush_time == 0) global.bsd_flush_time = delta;
892 else if (delta < global.bsd_flush_time) global.bsd_flush_time = delta;
893 }
894 }
895 }
896
897 if (out != NULL) free(out);
898 if (fwd != NULL) free(fwd);
899
900 return 0;
901 }
902
903 void
904 bsd_flush_duplicates(time_t now)
905 {
906 struct config_rule *r;
907 uint64_t delta;
908
909 global.bsd_flush_time = 0;
910
911 for (r = bsd_out_rule.tqh_first; r != NULL; r = r->entries.tqe_next)
912 {
913 if (r->type != DST_TYPE_FILE) continue;
914
915 if (r->last_count > 0)
916 {
917 delta = now - r->last_time;
918 if (delta < global.bsd_max_dup_time)
919 {
920 delta = global.bsd_max_dup_time - delta;
921 if (global.bsd_flush_time == 0) global.bsd_flush_time = delta;
922 else if (delta < global.bsd_flush_time) global.bsd_flush_time = delta;
923 }
924 else
925 {
926 _syslog_dst_open(r);
927 _syslog_send_repeat_msg(r);
928 _syslog_dst_close(r);
929
930 r->last_count = 0;
931 }
932 }
933 }
934 }
935
936 static int
937 _parse_config_file(const char *confname)
938 {
939 FILE *cf;
940 char *line;
941
942 cf = fopen(confname, "r");
943 if (cf == NULL) return 1;
944
945 while (NULL != (line = get_line_from_file(cf)))
946 {
947 _parse_line(line);
948 free(line);
949 }
950
951 fclose(cf);
952
953 return 0;
954 }
955
956 int
957 bsd_out_init(void)
958 {
959 asldebug("%s: init\n", MY_ID);
960
961 TAILQ_INIT(&bsd_out_rule);
962
963 query = asl_new(ASL_TYPE_QUERY);
964 aslevent_addmatch(query, MY_ID);
965 aslevent_addoutput(bsd_out_sendmsg, MY_ID);
966
967 _parse_config_file(_PATH_SYSLOG_CONF);
968 return 0;
969 }
970
971 int
972 bsd_out_reset(void)
973 {
974 reset = 1;
975 return 0;
976 }
977
978 int
979 bsd_out_close(void)
980 {
981 struct config_rule *r, *n;
982 int i;
983
984 n = NULL;
985 for (r = bsd_out_rule.tqh_first; r != NULL; r = n)
986 {
987 n = r->entries.tqe_next;
988
989 if (r->dst != NULL) free(r->dst);
990 if (r->fd > 0) close(r->fd);
991 if (r->addr != NULL) free(r->addr);
992 if (r->last_msg != NULL) free(r->last_msg);
993 if (r->facility != NULL)
994 {
995 for (i = 0; i < r->count; i++)
996 {
997 if (r->facility[i] != NULL) free(r->facility[i]);
998 }
999 free(r->facility);
1000 }
1001 if (r->pri != NULL) free(r->pri);
1002
1003 TAILQ_REMOVE(&bsd_out_rule, r, entries);
1004 free(r);
1005 }
1006
1007 return 0;
1008 }