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