]> git.saurik.com Git - apple/syslog.git/blame - syslogd.tproj/bsd_out.c
syslog-69.0.3.tar.gz
[apple/syslog.git] / syslogd.tproj / bsd_out.c
CommitLineData
b16a592a 1/*
57b0aad2 2 * Copyright (c) 2004-2008 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>
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
55static asl_msg_t *query = NULL;
56static int reset = 0;
57
58struct 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;
5dd30d76
A
67 uint32_t last_hash;
68 uint32_t last_count;
69 time_t last_time;
70 char *last_msg;
b16a592a
A
71 TAILQ_ENTRY(config_rule) entries;
72};
73
74static TAILQ_HEAD(cr, config_rule) bsd_out_rule;
75
57b0aad2 76extern uint32_t asl_core_string_hash(const char *s, uint32_t inlen);
5dd30d76 77
b16a592a
A
78int bsd_out_close();
79static int _parse_config_file(const char *);
80
81static void
82_do_reset(void)
83{
84 bsd_out_close();
85 _parse_config_file(_PATH_SYSLOG_CONF);
86}
87
88static 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 *));
02b408bf
A
97 if (l == NULL) return NULL;
98
b16a592a 99 l[0] = strdup(s);
02b408bf
A
100 if (l[0] == NULL)
101 {
102 free(l);
103 return NULL;
104 }
105
b16a592a
A
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
02b408bf
A
113 l = (char **)reallocf(l, (len + 1) * sizeof(char *));
114 if (l == NULL) return NULL;
b16a592a
A
115
116 if ((x >= (len - 1)) || (x == IndexNull))
117 {
118 l[len - 1] = strdup(s);
02b408bf
A
119 if (l[len - 1] == NULL)
120 {
121 free(l);
122 return NULL;
123 }
124
b16a592a
A
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);
02b408bf
A
131 if (l[x] == NULL) return NULL;
132
b16a592a
A
133 return l;
134}
135
136static 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);
02b408bf
A
151 if (t == NULL) return NULL;
152
b16a592a
A
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
166static 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
176static 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
200static 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;
5dd30d76 208 if (r->fd != -1) return 0;
b16a592a
A
209
210 if (r->dst[0] == '/')
211 {
5dd30d76 212 r->fd = open(r->dst, O_WRONLY | O_APPEND | O_CREAT | O_NOCTTY, 0644);
b16a592a
A
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;
5dd30d76 228 r->fd = 0;
b16a592a
A
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);
02b408bf
A
259 if (r->addr == NULL) return -1;
260
b16a592a
A
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;
5dd30d76 290 r->fd = 0;
b16a592a
A
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
5dd30d76
A
299static 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
b16a592a
A
334static 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;
5dd30d76 350 out->fd = -1;
b16a592a
A
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]);
02b408bf
A
362 if (out->dst == NULL) return -1;
363
b16a592a
A
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 {
02b408bf
A
388 out->facility = (char **)reallocf(out->facility, (out->count + 1) * sizeof(char *));
389 out->pri = (int *)reallocf(out->pri, (out->count + 1) * sizeof(int));
b16a592a 390 }
02b408bf
A
391
392 if (out->facility == NULL) return -1;
393 if (out->pri == NULL) return -1;
5dd30d76 394
b16a592a 395 out->facility[out->count] = strdup(comma[j]);
02b408bf
A
396 if (out->facility[out->count] == NULL) return -1;
397
b16a592a
A
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
02b408bf
A
412static char *
413bsd_log_string(const char *msg)
414{
415 uint32_t i, len, outlen;
416 char *out, *q;
417 uint8_t c;
5dd30d76 418
02b408bf 419 if (msg == NULL) return NULL;
5dd30d76 420
02b408bf 421 len = strlen(msg);
5dd30d76
A
422 while ((len > 0) && (msg[len - 1] == '\n')) len--;
423
424 if (len == 0) return NULL;
425
02b408bf
A
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 }
5dd30d76 432
02b408bf
A
433 out = malloc(outlen);
434 if (out == NULL) return NULL;
435
436 q = out;
5dd30d76 437
02b408bf
A
438 for (i = 0; i < len; i++)
439 {
440 c = msg[i];
5dd30d76 441
02b408bf
A
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 }
5dd30d76 464
02b408bf 465 *q = '\0';
5dd30d76 466
02b408bf
A
467 return out;
468}
469
b16a592a 470static int
5dd30d76
A
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
521static int
522_syslog_send(asl_msg_t *msg, struct config_rule *r, char **out, char **fwd, time_t now)
b16a592a 523{
02b408bf 524 char *so, *sf, *vt, *p, *outmsg;
5dd30d76 525 const char *vtime, *vhost, *vident, *vpid, *vmsg, *vlevel, *vfacility, *vrefproc, *vrefpid;
b16a592a 526 size_t outlen, n;
b16a592a 527 time_t tick;
5dd30d76
A
528 int pf, fc, status, is_dup, do_write;
529 FILE *pw;
530 uint32_t msg_hash;
b16a592a
A
531
532 if (out == NULL) return -1;
533 if (fwd == NULL) return -1;
5dd30d76 534 if (r == NULL) return -1;
b16a592a
A
535
536 if (r->type == DST_TYPE_NOTE)
537 {
538 notify_post(r->dst+1);
539 return 0;
540 }
541
5dd30d76 542 msg_hash = 0;
b16a592a 543 vt = NULL;
02b408bf 544 outmsg = NULL;
b16a592a
A
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;
02b408bf 558
b16a592a
A
559 memcpy(vt, p+4, 15);
560 vt[15] = '\0';
561 }
5dd30d76
A
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;
02b408bf 571
5dd30d76
A
572 memcpy(vt, vtime+4, 15);
573 vt[15] = '\0';
574 }
b16a592a
A
575 }
576
577 if (vt == NULL)
578 {
5dd30d76 579 tick = now;
b16a592a
A
580 p = ctime(&tick);
581 vt = malloc(16);
582 if (vt == NULL) return -1;
02b408bf 583
b16a592a
A
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
5dd30d76
A
599 vrefproc = asl_get(msg, ASL_KEY_REF_PROC);
600 vrefpid = asl_get(msg, ASL_KEY_REF_PID);
601
b16a592a 602 vmsg = asl_get(msg, ASL_KEY_MSG);
02b408bf 603 if (vmsg != NULL) outmsg = bsd_log_string(vmsg);
5dd30d76 604
b16a592a 605 n = 0;
5dd30d76 606 /* Time + " " */
b16a592a 607 if (vt != NULL) n += (strlen(vt) + 1);
5dd30d76
A
608
609 /* Host + " " */
b16a592a 610 if (vhost != NULL) n += (strlen(vhost) + 1);
5dd30d76
A
611
612 /* Sender */
b16a592a 613 if (vident != NULL) n += strlen(vident);
5dd30d76
A
614
615 /* "[" PID "]" */
b16a592a 616 if (vpid != NULL) n += (strlen(vpid) + 2);
5dd30d76
A
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 */
02b408bf 634 if (outmsg != NULL) n += strlen(outmsg);
b16a592a
A
635
636 if (n == 0) return -1;
5dd30d76
A
637
638 /* "\n" + nul */
b16a592a
A
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
5dd30d76
A
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
b16a592a
A
683 strcat(so, ": ");
684
02b408bf 685 if (outmsg != NULL)
b16a592a 686 {
02b408bf
A
687 strcat(so, outmsg);
688 free(outmsg);
b16a592a
A
689 }
690
5dd30d76
A
691 strcat(so, "\n");
692
b16a592a
A
693 free(vt);
694 *out = so;
695 }
696
5dd30d76
A
697 /* check if message is a duplicate of the last message, and inside the dup time window */
698 is_dup = 0;
57b0aad2 699 if ((global.bsd_max_dup_time > 0) && (*out != NULL) && (r->last_msg != NULL))
5dd30d76 700 {
57b0aad2 701 msg_hash = asl_core_string_hash(*out + 16, strlen(*out + 16));
5dd30d76
A
702 if ((r->last_hash == msg_hash) && (!strcmp(r->last_msg, *out + 16)))
703 {
57b0aad2 704 if ((now - r->last_time) < global.bsd_max_dup_time) is_dup = 1;
5dd30d76
A
705 }
706 }
707
b16a592a
A
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;
5dd30d76 720
b16a592a
A
721 *fwd = sf;
722 }
723
724 outlen = 0;
725 if (r->type == DST_TYPE_SOCK) outlen = strlen(*fwd);
726 else outlen = strlen(*out);
727
5dd30d76
A
728 _syslog_dst_open(r);
729
b16a592a
A
730 if ((r->type == DST_TYPE_FILE) || (r->type == DST_TYPE_CONS))
731 {
5dd30d76
A
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
b16a592a
A
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);
5dd30d76
A
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);
b16a592a 752
b16a592a
A
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);
5dd30d76 759 r->fd = open(r->dst, O_WRONLY | O_APPEND | O_CREAT | O_NOCTTY, 0644);
b16a592a
A
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 }
5dd30d76 773 else if ((r->type == DST_TYPE_SOCK) && (r->addr != NULL))
b16a592a
A
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
5dd30d76
A
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
b16a592a
A
809 return 0;
810}
811
812static 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
860int
861bsd_out_sendmsg(asl_msg_t *msg, const char *outid)
862{
863 struct config_rule *r;
864 char *out, *fwd;
5dd30d76
A
865 time_t tick;
866 uint64_t delta;
b16a592a
A
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
5dd30d76 879 tick = time(NULL);
57b0aad2 880 global.bsd_flush_time = 0;
5dd30d76 881
b16a592a
A
882 for (r = bsd_out_rule.tqh_first; r != NULL; r = r->entries.tqe_next)
883 {
5dd30d76
A
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;
57b0aad2 888 if (delta < global.bsd_max_dup_time)
5dd30d76 889 {
57b0aad2
A
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;
5dd30d76
A
893 }
894 }
b16a592a
A
895 }
896
897 if (out != NULL) free(out);
898 if (fwd != NULL) free(fwd);
899
900 return 0;
901}
902
5dd30d76 903void
57b0aad2 904bsd_flush_duplicates(time_t now)
5dd30d76
A
905{
906 struct config_rule *r;
5dd30d76
A
907 uint64_t delta;
908
57b0aad2 909 global.bsd_flush_time = 0;
5dd30d76
A
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 {
57b0aad2
A
917 delta = now - r->last_time;
918 if (delta < global.bsd_max_dup_time)
5dd30d76 919 {
57b0aad2
A
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;
5dd30d76
A
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
b16a592a
A
936static 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
956int
957bsd_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
971int
972bsd_out_reset(void)
973{
974 reset = 1;
975 return 0;
976}
977
978int
979bsd_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;
5dd30d76 988
b16a592a
A
989 if (r->dst != NULL) free(r->dst);
990 if (r->fd > 0) close(r->fd);
991 if (r->addr != NULL) free(r->addr);
5dd30d76 992 if (r->last_msg != NULL) free(r->last_msg);
b16a592a
A
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}