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