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