]> git.saurik.com Git - apple/syslog.git/blobdiff - syslogd.tproj/bsd_out.c
syslog-356.200.4.tar.gz
[apple/syslog.git] / syslogd.tproj / bsd_out.c
index 6e7bd2affaffea576724b399120f5d2056a1e395..9cd9d430c8d4fbd9994e7553afaa2325107e5344 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2004-2008 Apple Inc. All rights reserved.
+ * Copyright (c) 2004-2011 Apple Inc. All rights reserved.
  *
  * @APPLE_LICENSE_HEADER_START@
  * 
  * @APPLE_LICENSE_HEADER_END@
  */
 
+#include <TargetConditionals.h>
+
+#if TARGET_IPHONE_SIMULATOR
+struct _not_empty;
+#else
+
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <sys/socket.h>
@@ -34,6 +40,7 @@
 #include <fcntl.h>
 #include <errno.h>
 #include <netdb.h>
+#include <pthread.h>
 #include <notify.h>
 #include "daemon.h"
 
@@ -43,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
 #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
 {
@@ -63,10 +71,12 @@ 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;
 };
@@ -75,104 +85,6 @@ static TAILQ_HEAD(cr, config_rule) bsd_out_rule;
 
 extern uint32_t asl_core_string_hash(const char *s, uint32_t inlen);
 
-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);
-}
-
 static int
 _level_for_name(const char *name)
 {
@@ -225,7 +137,7 @@ _syslog_dst_open(struct config_rule *r)
        if (r->dst[0] == '!')
        {
                r->type = DST_TYPE_NOTE;
-               r->fd = 0;
+               r->fd = -1;
                return 0;
        }
 
@@ -255,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);
@@ -268,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;
                }
 
@@ -276,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;
                }
 
@@ -287,7 +203,7 @@ _syslog_dst_open(struct config_rule *r)
        if (strcmp(r->dst, "*") == 0)
        {
                r->type = DST_TYPE_WALL;
-               r->fd = 0;
+               r->fd = -1;
                return 0;
        }
 
@@ -301,6 +217,12 @@ _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:
@@ -315,8 +237,6 @@ _syslog_dst_close(struct config_rule *r)
                {
                        if (r->fd >= 0) close(r->fd);
                        r->fd = -1;
-                       if (r->addr != NULL) free(r->addr);
-                       r->addr = NULL;
                        break;
                }
 
@@ -331,10 +251,36 @@ _syslog_dst_close(struct config_rule *r)
        }
 }
 
+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;
 
@@ -342,7 +288,7 @@ _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));
@@ -364,7 +310,7 @@ _parse_line(char *s)
        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++)
                {
@@ -381,96 +327,45 @@ _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)
-{
-       uint32_t i, len, outlen;
-       char *out, *q;
-       uint8_t c;
-
-       if (msg == NULL) return NULL;
-
-       len = strlen(msg);
-       while ((len > 0) && (msg[len - 1] == '\n')) len--;
-
-       if (len == 0) return NULL;
-
-       outlen = len + 1;
-       for (i = 0; i < len; i++)
-       {
-               c = msg[i];
-               if (isascii(c) && iscntrl(c) && (c != '\t')) outlen++;
-       }
-
-       out = malloc(outlen);
-       if (out == NULL) return NULL;
-
-       q = out;
-
-       for (i = 0; i < len; i++)
-       {
-               c = msg[i];
-
-               if (isascii(c) && iscntrl(c))
-               {
-                       if (c == '\n')
-                       {
-                               *q++ = '\\';
-                               *q++ = 'n';
-                       }
-                       else if (c == '\t')
-                       {
-                               *q++ = c;
-                       }
-                       else
-                       {
-                               *q++ = '^';
-                               *q++ = c ^ 0100;
-                       }
-               }
-               else
-               {
-                       *q++ = c;
-               }
-       }
-
-       *q = '\0';
-
-       return out;
-}
-
 static int
-_syslog_send_repeat_msg(struct config_rule *r)
+_bsd_send_repeat_msg(struct config_rule *r)
 {
-       char vt[16], *p, *msg;
+       char vt[32], *msg;
        time_t tick;
        int len, status;
 
@@ -478,15 +373,17 @@ _syslog_send_repeat_msg(struct config_rule *r)
        if (r->type != DST_TYPE_FILE) return 0;
        if (r->last_count == 0) return 0;
 
-       tick = time(NULL);
-       p = ctime(&tick);
-       if (p == NULL) return -1;
+       /* stop the timer */
+       dispatch_suspend(r->dup_timer);
 
-       memcpy(vt, p+4, 15);
-       vt[15] = '\0';
+       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, r->last_count, (r->last_count == 1) ? "" : "s");
+       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);
@@ -519,20 +416,20 @@ _syslog_send_repeat_msg(struct config_rule *r)
 }
 
 static int
-_syslog_send(asl_msg_t *msg, struct config_rule *r, char **out, char **fwd, time_t now)
+_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, *vrefproc, *vrefpid;
-       size_t outlen, n;
-       time_t tick;
+       char *sf, *outmsg;
+       const char *vlevel, *vfacility;
+       size_t outlen;
        int pf, fc, status, is_dup, do_write;
-       FILE *pw;
-       uint32_t msg_hash;
+       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)
        {
                notify_post(r->dst+1);
@@ -540,158 +437,13 @@ _syslog_send(asl_msg_t *msg, struct config_rule *r, char **out, char **fwd, time
        }
 
        msg_hash = 0;
-       vt = NULL;
        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 = now;
-                       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";
-
-               vrefproc = asl_get(msg, ASL_KEY_REF_PROC);
-               vrefpid = asl_get(msg, ASL_KEY_REF_PID);
-
-               vmsg = asl_get(msg, ASL_KEY_MSG);
-               if (vmsg != NULL) outmsg = bsd_log_string(vmsg);
-
-               n = 0;
-               /* Time + " " */
-               if (vt != NULL) n += (strlen(vt) + 1);
-
-               /* Host + " " */
-               if (vhost != NULL) n += (strlen(vhost) + 1);
-
-               /* Sender */
-               if (vident != NULL) n += strlen(vident);
-
-               /* "[" PID "]" */
-               if (vpid != NULL) n += (strlen(vpid) + 2);
-
-               /* " (" */
-               if ((vrefproc != NULL) || (vrefpid != NULL)) n += 2;
-
-               /* RefProc */
-               if (vrefproc != NULL) n += strlen(vrefproc);
-
-               /* "[" RefPID "]" */
-               if (vrefpid != NULL) n += (strlen(vrefpid) + 2);
-
-               /* ")" */
-               if ((vrefproc != NULL) || (vrefpid != NULL)) n += 1;
-
-               /* ": " */
-               n += 2;
-
-               /* Message */
-               if (outmsg != NULL) n += strlen(outmsg);
-
-               if (n == 0) return -1;
-
-               /* "\n" + nul */
-               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, "]");
-                       }
-               }
-
-               if ((vrefproc != NULL) || (vrefpid != NULL))
-               {
-                       strcat(so, " (");
-
-                       if (vrefproc != NULL) strcat(so, vrefproc);
-
-                       if (vrefpid != NULL)
-                       {
-                               strcat(so, "[");
-                               strcat(so, vrefpid);
-                               strcat(so, "]");
-                       }
-
-                       strcat(so, ")");
-               }
-
-               strcat(so, ": ");
-
-               if (outmsg != NULL)
-               {
-                       strcat(so, outmsg);
-                       free(outmsg);
-               }
-
-               strcat(so, "\n");
-
-               free(vt);
-               *out = 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;
        }
 
        /* check if message is a duplicate of the last message, and inside the dup time window */
@@ -708,10 +460,10 @@ _syslog_send(asl_msg_t *msg, struct config_rule *r, char **out, char **fwd, time
        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;
@@ -721,19 +473,16 @@ _syslog_send(asl_msg_t *msg, struct config_rule *r, char **out, char **fwd, time
                *fwd = sf;
        }
 
-       outlen = 0;
        if (r->type == DST_TYPE_SOCK) outlen = strlen(*fwd);
        else outlen = strlen(*out);
 
-       _syslog_dst_open(r);
-
        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)) _syslog_send_repeat_msg(r);
+               if ((r->type == DST_TYPE_FILE) && (is_dup == 0) && (r->last_count > 0)) _bsd_send_repeat_msg(r);
 
                do_write = 1;
 
@@ -743,9 +492,26 @@ _syslog_send(asl_msg_t *msg, struct config_rule *r, char **out, char **fwd, time
                 * The kernel printf routine already sends them to /dev/console
                 * so writing them here would cause duplicates.
                 */
-               vfacility = asl_get(msg, ASL_KEY_FACILITY);
+               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 ((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);
@@ -777,26 +543,26 @@ _syslog_send(asl_msg_t *msg, struct config_rule *r, char **out, char **fwd, time
        }
        else if (r->type == DST_TYPE_WALL)
        {
-               pw = popen(_PATH_WALL, "w");
+#if !TARGET_OS_EMBEDDED
+               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
        }
 
-       _syslog_dst_close(r);
-
        if (is_dup == 1)
        {
                r->last_count++;
        }
        else
        {
-               if (r->last_msg != NULL) free(r->last_msg);
+               free(r->last_msg);
                r->last_msg = NULL;
 
                if (*out != NULL) r->last_msg = strdup(*out + 16);
@@ -810,9 +576,10 @@ _syslog_send(asl_msg_t *msg, struct config_rule *r, char **out, char **fwd, time
 }
 
 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;
@@ -829,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;
@@ -845,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);
@@ -857,79 +632,72 @@ _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;
-       time_t tick;
-       uint64_t delta;
-
-       if (reset != 0)
-       {
-               _do_reset();
-               reset = 0;
-       }
+       time_t now;
 
        if (msg == NULL) return -1;
 
        out = NULL;
        fwd = NULL;
 
-       tick = time(NULL);
-       global.bsd_flush_time = 0;
+       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, tick);
-               if ((r->type == DST_TYPE_FILE) && (r->last_count > 0))
-               {
-                       delta = tick - r->last_time;
-                       if (delta < global.bsd_max_dup_time)
-                       {
-                               delta = global.bsd_max_dup_time - delta;
-                               if (global.bsd_flush_time == 0) global.bsd_flush_time = delta;
-                               else if (delta < global.bsd_flush_time) global.bsd_flush_time = delta;
-                       }
-               }
+               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_flush_duplicates(time_t now)
+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;
 
-       global.bsd_flush_time = 0;
+       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 (r->last_count > 0)
-               {
-                       delta = now - r->last_time;
-                       if (delta < global.bsd_max_dup_time)
-                       {
-                               delta = global.bsd_max_dup_time - delta;
-                               if (global.bsd_flush_time == 0) global.bsd_flush_time = delta;
-                               else if (delta < global.bsd_flush_time) global.bsd_flush_time = delta;
-                       }
-                       else
-                       {
-                               _syslog_dst_open(r);
-                               _syslog_send_repeat_msg(r);
-                               _syslog_dst_close(r);
+               /*
+                * 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;
 
-                               r->last_count = 0;
-                       }
-               }
+               delta = now - r->last_time;
+               if (delta > CLOSE_ON_IDLE_SEC) _syslog_dst_close(r);
        }
 }
 
@@ -956,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;
@@ -986,19 +755,30 @@ bsd_out_close(void)
        {
                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->last_msg != NULL) free(r->last_msg);
+               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);
@@ -1006,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_IPHONE_SIMULATOR */