X-Git-Url: https://git.saurik.com/apple/syslog.git/blobdiff_plain/d7fa066074f8fd77fb237ffd8c1947e49f1e8837..51023e6095f291a0cf1026b76579218476e239c4:/syslogd.tproj/bsd_out.c diff --git a/syslogd.tproj/bsd_out.c b/syslogd.tproj/bsd_out.c index 4b16bc9..571b998 100644 --- a/syslogd.tproj/bsd_out.c +++ b/syslogd.tproj/bsd_out.c @@ -1,27 +1,32 @@ /* - * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2004-2011 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * - * "Portions Copyright (c) 2004 Apple Computer, Inc. All Rights - * Reserved. This file contains Original Code and/or Modifications of - * Original Code as defined in and that are subject to the Apple Public - * Source License Version 1.0 (the 'License'). You may not use this file - * except in compliance with the License. Please obtain a copy of the - * License at http://www.apple.com/publicsource and read it before using - * this file. + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the - * License for the specific language governing rights and limitations - * under the License." + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. * * @APPLE_LICENSE_HEADER_END@ */ +#include + +#if TARGET_OS_SIMULATOR +struct _not_empty; +#else + #include #include #include @@ -35,6 +40,7 @@ #include #include #include +#include #include #include "daemon.h" @@ -44,7 +50,6 @@ #define ASL_KEY_FACILITY "Facility" #define FACILITY_KERNEL "kern" #define _PATH_CONSOLE "/dev/console" -#define IndexNull ((uint32_t)-1) #define DST_TYPE_NONE 0 #define DST_TYPE_FILE 1 @@ -53,8 +58,10 @@ #define DST_TYPE_WALL 4 #define DST_TYPE_NOTE 5 -static asl_msg_t *query = NULL; -static int reset = 0; +#define CLOSE_ON_IDLE_SEC 300 + +static dispatch_queue_t bsd_out_queue; +static dispatch_source_t bsd_idle_timer; struct config_rule { @@ -64,109 +71,19 @@ struct config_rule int type; struct sockaddr *addr; char **facility; + uint32_t *fac_prefix_len; int *pri; + uint32_t last_hash; + uint32_t last_count; + time_t last_time; + dispatch_source_t dup_timer; + char *last_msg; TAILQ_ENTRY(config_rule) entries; }; static TAILQ_HEAD(cr, config_rule) bsd_out_rule; -int bsd_out_close(); -static int _parse_config_file(const char *); - -static void -_do_reset(void) -{ - bsd_out_close(); - _parse_config_file(_PATH_SYSLOG_CONF); -} - -static char ** -_insertString(char *s, char **l, uint32_t x) -{ - int i, len; - - if (s == NULL) return l; - if (l == NULL) - { - l = (char **)malloc(2 * sizeof(char *)); - if (l == NULL) return NULL; - - l[0] = strdup(s); - if (l[0] == NULL) - { - free(l); - return NULL; - } - - l[1] = NULL; - return l; - } - - for (i = 0; l[i] != NULL; i++); - len = i + 1; /* count the NULL on the end of the list too! */ - - l = (char **)reallocf(l, (len + 1) * sizeof(char *)); - if (l == NULL) return NULL; - - if ((x >= (len - 1)) || (x == IndexNull)) - { - l[len - 1] = strdup(s); - if (l[len - 1] == NULL) - { - free(l); - return NULL; - } - - l[len] = NULL; - return l; - } - - for (i = len; i > x; i--) l[i] = l[i - 1]; - l[x] = strdup(s); - if (l[x] == NULL) return NULL; - - return l; -} - -static char ** -_explode(char *s, char *delim) -{ - char **l = NULL; - char *p, *t; - int i, n; - - if (s == NULL) return NULL; - - p = s; - while (p[0] != '\0') - { - for (i = 0; ((p[i] != '\0') && (strchr(delim, p[i]) == NULL)); i++); - n = i; - t = malloc(n + 1); - if (t == NULL) return NULL; - - for (i = 0; i < n; i++) t[i] = p[i]; - t[n] = '\0'; - l = _insertString(t, l, IndexNull); - free(t); - t = NULL; - if (p[i] == '\0') return l; - if (p[i + 1] == '\0') l = _insertString("", l, IndexNull); - p = p + i + 1; - } - - return l; -} - -static void -_freeList(char **l) -{ - int i; - - if (l == NULL) return; - for (i = 0; l[i] != NULL; i++) free(l[i]); - free(l); -} +extern uint32_t asl_core_string_hash(const char *s, uint32_t inlen); static int _level_for_name(const char *name) @@ -200,12 +117,11 @@ _syslog_dst_open(struct config_rule *r) struct addrinfo hints, *gai, *ai; if (r == NULL) return -1; - - r->fd = -1; + if (r->fd != -1) return 0; if (r->dst[0] == '/') { - r->fd = open(r->dst, O_WRONLY | O_APPEND | O_CREAT, 0644); + r->fd = open(r->dst, O_WRONLY | O_APPEND | O_CREAT | O_NOCTTY, 0644); if (r->fd < 0) { asldebug("%s: open failed for file: %s (%s)\n", MY_ID, r->dst, strerror(errno)); @@ -221,6 +137,7 @@ _syslog_dst_open(struct config_rule *r) if (r->dst[0] == '!') { r->type = DST_TYPE_NOTE; + r->fd = -1; return 0; } @@ -250,7 +167,7 @@ _syslog_dst_open(struct config_rule *r) r->fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); if (r->fd < 0) continue; - r->addr = (struct sockaddr *)calloc(1, ai->ai_addrlen); + r->addr = (struct sockaddr *)malloc(ai->ai_addrlen); if (r->addr == NULL) return -1; memcpy(r->addr, ai->ai_addr, ai->ai_addrlen); @@ -263,6 +180,8 @@ _syslog_dst_open(struct config_rule *r) if (r->fd < 0) { asldebug("%s: connection failed for %s\n", MY_ID, (r->dst) + 1); + free(r->addr); + r->addr = NULL; return -1; } @@ -271,6 +190,8 @@ _syslog_dst_open(struct config_rule *r) close(r->fd); r->fd = -1; asldebug("%s: couldn't set O_NONBLOCK for fd %d: %s\n", MY_ID, r->fd, strerror(errno)); + free(r->addr); + r->addr = NULL; return -1; } @@ -282,6 +203,7 @@ _syslog_dst_open(struct config_rule *r) if (strcmp(r->dst, "*") == 0) { r->type = DST_TYPE_WALL; + r->fd = -1; return 0; } @@ -290,10 +212,75 @@ _syslog_dst_open(struct config_rule *r) return -1; } +static void +_syslog_dst_close(struct config_rule *r) +{ + if (r == NULL) return; + + if (r->addr != NULL) + { + free(r->addr); + r->addr = NULL; + } + + switch (r->type) + { + case DST_TYPE_FILE: + case DST_TYPE_CONS: + { + if (r->fd >= 0) close(r->fd); + r->fd = -1; + break; + } + + case DST_TYPE_SOCK: + { + if (r->fd >= 0) close(r->fd); + r->fd = -1; + break; + } + + case DST_TYPE_NONE: + case DST_TYPE_WALL: + case DST_TYPE_NOTE: + default: + { + /* do nothing */ + return; + } + } +} + +static char * +_clean_facility_name(char *s) +{ + uint32_t len; + char *p, *out; + + if (s == NULL) return NULL; + len = strlen(s); + if (len == 0) return NULL; + + p = s; + + if ((*s == '\'') || (*s == '"')) + { + len--; + p++; + if (p[len - 1] == *s) len --; + } + + out = calloc(1, len + 1); + if (out == NULL) return NULL; + + memcpy(out, p, len); + return out; +} + static int _parse_line(char *s) { - char **semi, **comma; + char **semi, **comma, *star; int i, j, n, lasts, lastc, pri; struct config_rule *out; @@ -301,11 +288,12 @@ _parse_line(char *s) while ((*s == ' ') || (*s == '\t')) s++; if (*s == '#') return -1; - semi = _explode(s, "; \t"); + semi = explode(s, "; \t"); if (semi == NULL) return -1; out = (struct config_rule *)calloc(1, sizeof(struct config_rule)); if (out == NULL) return -1; + out->fd = -1; n = 0; lasts = -1; @@ -319,12 +307,10 @@ _parse_line(char *s) out->dst = strdup(semi[lasts]); if (out->dst == NULL) return -1; - _syslog_dst_open(out); - for (i = 0; i < lasts; i++) { if (semi[i][0] == '\0') continue; - comma = _explode(semi[i], ",."); + comma = explode(semi[i], ",."); lastc = -1; for (j = 0; comma[j] != NULL; j++) { @@ -341,101 +327,108 @@ _parse_line(char *s) if (out->count == 0) { out->facility = (char **)calloc(1, sizeof(char *)); + out->fac_prefix_len = (uint32_t *)calloc(1, sizeof(uint32_t)); out->pri = (int *)calloc(1, sizeof(int)); } else { out->facility = (char **)reallocf(out->facility, (out->count + 1) * sizeof(char *)); + out->fac_prefix_len = (uint32_t *)reallocf(out->fac_prefix_len, (out->count + 1) * sizeof(uint32_t)); out->pri = (int *)reallocf(out->pri, (out->count + 1) * sizeof(int)); } if (out->facility == NULL) return -1; + if (out->fac_prefix_len == NULL) return -1; if (out->pri == NULL) return -1; - - out->facility[out->count] = strdup(comma[j]); + + out->facility[out->count] = _clean_facility_name(comma[j]); if (out->facility[out->count] == NULL) return -1; + out->fac_prefix_len[out->count] = 0; + star = strchr(out->facility[out->count], '*'); + if (star != NULL) out->fac_prefix_len[out->count] = (uint32_t)(star - out->facility[out->count]); + out->pri[out->count] = pri; out->count++; } - _freeList(comma); + free_string_list(comma); } - _freeList(semi); + free_string_list(semi); TAILQ_INSERT_TAIL(&bsd_out_rule, out, entries); return 0; } -static char * -bsd_log_string(const char *msg) +static int +_bsd_send_repeat_msg(struct config_rule *r) { - uint32_t i, len, outlen; - char *out, *q; - uint8_t c; - - if (msg == NULL) return NULL; - + char vt[32], *msg; + time_t tick; + int len, status; + + if (r == NULL) return -1; + if (r->type != DST_TYPE_FILE) return 0; + if (r->last_count == 0) return 0; + + /* stop the timer */ + dispatch_suspend(r->dup_timer); + + tick = time(NULL); + memset(vt, 0, sizeof(vt)); + ctime_r(&tick, vt); + vt[19] = '\0'; + + msg = NULL; + asprintf(&msg, "%s: --- last message repeated %u time%s ---\n", vt + 4, r->last_count, (r->last_count == 1) ? "" : "s"); + r->last_count = 0; + if (msg == NULL) return -1; + len = strlen(msg); - - outlen = len + 1; - for (i = 0; i < len; i++) + status = write(r->fd, msg, len); + if ((status < 0) || (status < len)) { - c = msg[i]; - if (isascii(c) && iscntrl(c) && (c != '\t')) outlen++; - } - - out = malloc(outlen); - if (out == NULL) return NULL; + asldebug("%s: error writing repeat message (%s): %s\n", MY_ID, r->dst, strerror(errno)); - q = out; - - for (i = 0; i < len; i++) - { - c = msg[i]; - - if (isascii(c) && iscntrl(c)) + /* Try re-opening the file (once) and write again */ + close(r->fd); + r->fd = open(r->dst, O_WRONLY | O_APPEND | O_CREAT | O_NOCTTY, 0644); + if (r->fd < 0) { - if (c == '\n') - { - *q++ = '\\'; - *q++ = 'n'; - } - else if (c == '\t') - { - *q++ = c; - } - else - { - *q++ = '^'; - *q++ = c ^ 0100; - } + asldebug("%s: re-open failed for file: %s (%s)\n", MY_ID, r->dst, strerror(errno)); + free(msg); + return -1; } - else + + status = write(r->fd, msg, len); + if ((status < 0) || (status < len)) { - *q++ = c; + asldebug("%s: error re-writing message (%s): %s\n", MY_ID, r->dst, strerror(errno)); + free(msg); + return -1; } } - - *q = '\0'; - - return out; + + free(msg); + return 0; } static int -_syslog_send(asl_msg_t *msg, struct config_rule *r, char **out, char **fwd) +_bsd_send(asl_msg_t *msg, struct config_rule *r, char **out, char **fwd, time_t now) { - char *so, *sf, *vt, *p, *outmsg; - const char *vtime, *vhost, *vident, *vpid, *vmsg, *vlevel, *vfacility; - size_t outlen, n; - int pf, fc, status; - FILE *pw; - time_t tick; + char *sf, *outmsg; + const char *vlevel, *vfacility; + size_t outlen; + int pf, fc, status, is_dup, do_write; + uint32_t msg_hash, n; if (out == NULL) return -1; if (fwd == NULL) return -1; + if (r == NULL) return -1; + + _syslog_dst_open(r); if (r->type == DST_TYPE_NOTE) { @@ -443,154 +436,93 @@ _syslog_send(asl_msg_t *msg, struct config_rule *r, char **out, char **fwd) return 0; } - vt = NULL; + msg_hash = 0; outmsg = NULL; /* Build output string if it hasn't been built by a previous rule-match */ if (*out == NULL) { - vtime = asl_get(msg, ASL_KEY_TIME); - if (vtime != NULL) - { - tick = asl_parse_time(vtime); - if (tick != (time_t)-1) - { - p = ctime(&tick); - vt = malloc(16); - if (vt == NULL) return -1; - - memcpy(vt, p+4, 15); - vt[15] = '\0'; - } - } - else if (strlen(vtime) < 24) - { - vt = strdup(vtime); - if (vt == NULL) return -1; - } - else - { - vt = malloc(16); - if (vt == NULL) return -1; - - memcpy(vt, vtime+4, 15); - vt[15] = '\0'; - } - - if (vt == NULL) - { - tick = time(NULL); - p = ctime(&tick); - vt = malloc(16); - if (vt == NULL) return -1; - - memcpy(vt, p+4, 15); - vt[15] = '\0'; - } - - vhost = asl_get(msg, ASL_KEY_HOST); - if (vhost == NULL) vhost = "localhost"; - - vident = asl_get(msg, ASL_KEY_SENDER); - if ((vident != NULL) && (!strcmp(vident, "Unknown"))) vident = NULL; - - vpid = asl_get(msg, ASL_KEY_PID); - if ((vpid != NULL) && (!strcmp(vpid, "-1"))) vpid = NULL; - - if ((vpid != NULL) && (vident == NULL)) vident = "Unknown"; - - vmsg = asl_get(msg, ASL_KEY_MSG); - if (vmsg != NULL) outmsg = bsd_log_string(vmsg); - - n = 0; - if (vt != NULL) n += (strlen(vt) + 1); - if (vhost != NULL) n += (strlen(vhost) + 1); - if (vident != NULL) n += strlen(vident); - n += 2; - if (vpid != NULL) n += (strlen(vpid) + 2); - - if (outmsg != NULL) n += strlen(outmsg); - - if (n == 0) return -1; - n += 2; - - so = calloc(1, n); - if (so == NULL) return -1; - - if (vt != NULL) - { - strcat(so, vt); - strcat(so, " "); - } - - if (vhost != NULL) - { - strcat(so, vhost); - strcat(so, " "); - } - - if (vident != NULL) - { - strcat(so, vident); - if (vpid != NULL) - { - strcat(so, "["); - strcat(so, vpid); - strcat(so, "]"); - } - } - - strcat(so, ": "); + *out = asl_format_message((asl_msg_t *)msg, ASL_MSG_FMT_BSD, ASL_TIME_FMT_LCL, ASL_ENCODE_SAFE, &n); + if (*out == NULL) return -1; + } - if (outmsg != NULL) + /* check if message is a duplicate of the last message, and inside the dup time window */ + is_dup = 0; + if ((global.bsd_max_dup_time > 0) && (*out != NULL) && (r->last_msg != NULL)) + { + msg_hash = asl_core_string_hash(*out + 16, strlen(*out + 16)); + if ((r->last_hash == msg_hash) && (!strcmp(r->last_msg, *out + 16))) { - strcat(so, outmsg); - free(outmsg); - strcat(so, "\n"); + if ((now - r->last_time) < global.bsd_max_dup_time) is_dup = 1; } - - free(vt); - *out = so; } if ((*fwd == NULL) && (r->type == DST_TYPE_SOCK)) { pf = 7; - vlevel = asl_get(msg, ASL_KEY_LEVEL); + vlevel = asl_msg_get_val_for_key(msg, ASL_KEY_LEVEL); if (vlevel != NULL) pf = atoi(vlevel); - fc = asl_syslog_faciliy_name_to_num(asl_get(msg, ASL_KEY_FACILITY)); + fc = asl_syslog_faciliy_name_to_num(asl_msg_get_val_for_key(msg, ASL_KEY_FACILITY)); if (fc > 0) pf |= fc; sf = NULL; asprintf(&sf, "<%d>%s", pf, *out); if (sf == NULL) return -1; + *fwd = sf; } - outlen = 0; if (r->type == DST_TYPE_SOCK) outlen = strlen(*fwd); else outlen = strlen(*out); if ((r->type == DST_TYPE_FILE) || (r->type == DST_TYPE_CONS)) { + /* + * If current message is NOT a duplicate and r->last_count > 0 + * we need to write a "last message was repeated N times" log entry + */ + if ((r->type == DST_TYPE_FILE) && (is_dup == 0) && (r->last_count > 0)) _bsd_send_repeat_msg(r); + + do_write = 1; + /* * Special case for kernel messages. * Don't write kernel messages to /dev/console. * The kernel printf routine already sends them to /dev/console * so writing them here would cause duplicates. */ - vfacility = asl_get(msg, ASL_KEY_FACILITY); - if ((vfacility != NULL) && (!strcmp(vfacility, FACILITY_KERNEL)) && (r->type == DST_TYPE_CONS)) return 0; + vfacility = asl_msg_get_val_for_key(msg, ASL_KEY_FACILITY); + if ((vfacility != NULL) && (!strcmp(vfacility, FACILITY_KERNEL)) && (r->type == DST_TYPE_CONS)) do_write = 0; + if ((do_write == 1) && (r->type == DST_TYPE_FILE) && (is_dup == 1)) + { + do_write = 0; + + if (r->dup_timer == NULL) + { + /* create a timer to flush dups on this file */ + r->dup_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, bsd_out_queue); + dispatch_source_set_event_handler(r->dup_timer, ^{ _bsd_send_repeat_msg(r); }); + } + + if (r->last_count == 0) + { + /* start the timer */ + dispatch_source_set_timer(r->dup_timer, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * global.bsd_max_dup_time), DISPATCH_TIME_FOREVER, 0); + dispatch_resume(r->dup_timer); + } + } + + if (do_write == 0) status = outlen; + else status = write(r->fd, *out, outlen); - status = write(r->fd, *out, outlen); if ((status < 0) || (status < outlen)) { asldebug("%s: error writing message (%s): %s\n", MY_ID, r->dst, strerror(errno)); /* Try re-opening the file (once) and write again */ close(r->fd); - r->fd = open(r->dst, O_WRONLY | O_APPEND | O_CREAT, 0644); + r->fd = open(r->dst, O_WRONLY | O_APPEND | O_CREAT | O_NOCTTY, 0644); if (r->fd < 0) { asldebug("%s: re-open failed for file: %s (%s)\n", MY_ID, r->dst, strerror(errno)); @@ -604,31 +536,50 @@ _syslog_send(asl_msg_t *msg, struct config_rule *r, char **out, char **fwd) } } } - else if (r->type == DST_TYPE_SOCK) + else if ((r->type == DST_TYPE_SOCK) && (r->addr != NULL)) { status = sendto(r->fd, *fwd, outlen, 0, r->addr, r->addr->sa_len); if (status < 0) asldebug("%s: error sending message (%s): %s\n", MY_ID, r->dst, strerror(errno)); } else if (r->type == DST_TYPE_WALL) { - pw = popen(_PATH_WALL, "w"); +#if !(TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR) + FILE *pw = popen(_PATH_WALL, "w"); if (pw < 0) { asldebug("%s: error sending wall message: %s\n", MY_ID, strerror(errno)); return -1; } - fprintf(pw, *out); + fprintf(pw, "%s", *out); pclose(pw); +#endif + } + + if (is_dup == 1) + { + r->last_count++; + } + else + { + free(r->last_msg); + r->last_msg = NULL; + + if (*out != NULL) r->last_msg = strdup(*out + 16); + + r->last_hash = msg_hash; + r->last_count = 0; + r->last_time = now; } return 0; } static int -_syslog_rule_match(asl_msg_t *msg, struct config_rule *r) +_bsd_rule_match(asl_msg_t *msg, struct config_rule *r) { - uint32_t i, test, f, pri; + uint32_t i, test, f; + int32_t pri; const char *val; if (msg == NULL) return 0; @@ -645,11 +596,19 @@ _syslog_rule_match(asl_msg_t *msg, struct config_rule *r) if ((test == 0) && (r->pri[i] == -2)) continue; f = 0; - if (strcmp(r->facility[i], "*") == 0) f = 1; - else + val = asl_msg_get_val_for_key(msg, ASL_KEY_FACILITY); + + if (strcmp(r->facility[i], "*") == 0) { - val = asl_get(msg, ASL_KEY_FACILITY); - if ((val != NULL) && (strcasecmp(r->facility[i], val) == 0)) f = 1; + f = 1; + } + else if ((r->fac_prefix_len[i] > 0) && (strncasecmp(r->facility[i], val, r->fac_prefix_len[i]) == 0)) + { + f = 1; + } + else if ((val != NULL) && (strcasecmp(r->facility[i], val) == 0)) + { + f = 1; } if (f == 0) continue; @@ -661,7 +620,7 @@ _syslog_rule_match(asl_msg_t *msg, struct config_rule *r) continue; } - val = asl_get(msg, ASL_KEY_LEVEL); + val = asl_msg_get_val_for_key(msg, ASL_KEY_LEVEL); if (val == NULL) continue; pri = atoi(val); @@ -673,34 +632,75 @@ _syslog_rule_match(asl_msg_t *msg, struct config_rule *r) return test; } -int -bsd_out_sendmsg(asl_msg_t *msg, const char *outid) +static int +_bsd_match_and_send(asl_msg_t *msg) { struct config_rule *r; char *out, *fwd; - - if (reset != 0) - { - _do_reset(); - reset = 0; - } + time_t now; if (msg == NULL) return -1; out = NULL; fwd = NULL; + now = time(NULL); + for (r = bsd_out_rule.tqh_first; r != NULL; r = r->entries.tqe_next) { - if (_syslog_rule_match(msg, r) == 1) _syslog_send(msg, r, &out, &fwd); + if (_bsd_rule_match(msg, r) == 1) _bsd_send(msg, r, &out, &fwd, now); } - if (out != NULL) free(out); - if (fwd != NULL) free(fwd); + free(out); + free(fwd); return 0; } +void +bsd_out_message(asl_msg_t *msg, int64_t msize) +{ + if (msg == NULL) return; + + OSAtomicIncrement32(&global.bsd_queue_count); + asl_msg_retain((asl_msg_t *)msg); + + dispatch_async(bsd_out_queue, ^{ + _bsd_match_and_send(msg); + asl_msg_release((asl_msg_t *)msg); + + /* end of the output module chain (after asl) - decrement global memory stats */ + OSAtomicAdd64(-1ll * msize, &global.memory_size); + + OSAtomicDecrement32(&global.bsd_queue_count); + }); +} + +static void +_bsd_close_idle_files() +{ + time_t now; + struct config_rule *r; + uint64_t delta; + + now = time(NULL); + + for (r = bsd_out_rule.tqh_first; r != NULL; r = r->entries.tqe_next) + { + /* only applies to files */ + if (r->type != DST_TYPE_FILE) continue; + + /* + * If the last message repeat count is non-zero, a _bsd_flush_duplicates() + * call will occur within 30 seconds. Don't bother closing the file. + */ + if (r->last_count > 0) continue; + + delta = now - r->last_time; + if (delta > CLOSE_ON_IDLE_SEC) _syslog_dst_close(r); + } +} + static int _parse_config_file(const char *confname) { @@ -724,27 +724,28 @@ _parse_config_file(const char *confname) int bsd_out_init(void) { + static dispatch_once_t once; + asldebug("%s: init\n", MY_ID); TAILQ_INIT(&bsd_out_rule); + _parse_config_file(_PATH_SYSLOG_CONF); - query = asl_new(ASL_TYPE_QUERY); - aslevent_addmatch(query, MY_ID); - aslevent_addoutput(bsd_out_sendmsg, MY_ID); + dispatch_once(&once, ^{ + bsd_out_queue = dispatch_queue_create("BSD Out Queue", NULL); - _parse_config_file(_PATH_SYSLOG_CONF); - return 0; -} + /* start a timer to close idle files */ + bsd_idle_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, bsd_out_queue); + dispatch_source_set_event_handler(bsd_idle_timer, ^{ _bsd_close_idle_files(); }); + 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); + dispatch_resume(bsd_idle_timer); + }); -int -bsd_out_reset(void) -{ - reset = 1; return 0; } -int -bsd_out_close(void) +static int +_bsd_out_close_internal(void) { struct config_rule *r, *n; int i; @@ -753,19 +754,31 @@ bsd_out_close(void) for (r = bsd_out_rule.tqh_first; r != NULL; r = n) { n = r->entries.tqe_next; - - if (r->dst != NULL) free(r->dst); - if (r->fd > 0) close(r->fd); - if (r->addr != NULL) free(r->addr); + + if (r->dup_timer != NULL) + { + if (r->last_count > 0) _bsd_send_repeat_msg(r); + dispatch_source_cancel(r->dup_timer); + dispatch_resume(r->dup_timer); + dispatch_release(r->dup_timer); + } + + free(r->dst); + free(r->addr); + free(r->last_msg); + free(r->fac_prefix_len); + free(r->pri); + + if (r->fd >= 0) close(r->fd); + if (r->facility != NULL) { for (i = 0; i < r->count; i++) - { - if (r->facility[i] != NULL) free(r->facility[i]); - } + free(r->facility[i]); + free(r->facility); } - if (r->pri != NULL) free(r->pri); + TAILQ_REMOVE(&bsd_out_rule, r, entries); free(r); @@ -773,3 +786,26 @@ bsd_out_close(void) return 0; } + +int +bsd_out_close(void) +{ + dispatch_async(bsd_out_queue, ^{ + _bsd_out_close_internal(); + }); + + return 0; +} + +int +bsd_out_reset(void) +{ + dispatch_async(bsd_out_queue, ^{ + _bsd_out_close_internal(); + bsd_out_init(); + }); + + return 0; +} + +#endif /* !TARGET_OS_SIMULATOR */