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