]> git.saurik.com Git - apple/syslog.git/blobdiff - syslogd.tproj/bsd_out.c
syslog-69.0.3.tar.gz
[apple/syslog.git] / syslogd.tproj / bsd_out.c
index a24fdaf9578de8ec75df5b16e65b10a753bf4d68..6e7bd2affaffea576724b399120f5d2056a1e395 100644 (file)
@@ -1,23 +1,22 @@
 /*
- * Copyright (c) 2004 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2004-2008 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@
  */
@@ -31,6 +30,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
+#include <ctype.h>
 #include <fcntl.h>
 #include <errno.h>
 #include <netdb.h>
@@ -64,11 +64,17 @@ struct config_rule
        struct sockaddr *addr;
        char **facility;
        int *pri;
+       uint32_t last_hash;
+       uint32_t last_count;
+       time_t last_time;
+       char *last_msg;
        TAILQ_ENTRY(config_rule) entries;
 };
 
 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 *);
 
@@ -88,7 +94,15 @@ _insertString(char *s, char **l, uint32_t x)
        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;
        }
@@ -96,17 +110,26 @@ _insertString(char *s, char **l, uint32_t x)
        for (i = 0; l[i] != NULL; i++);
        len = i + 1; /* count the NULL on the end of the list too! */
 
-       l = (char **)realloc(l, (len + 1) * sizeof(char *));
+       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;
 }
 
@@ -125,6 +148,8 @@ _explode(char *s, char *delim)
                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);
@@ -180,12 +205,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));
@@ -201,6 +225,7 @@ _syslog_dst_open(struct config_rule *r)
        if (r->dst[0] == '!')
        {
                r->type = DST_TYPE_NOTE;
+               r->fd = 0;
                return 0;
        }
 
@@ -231,6 +256,8 @@ _syslog_dst_open(struct config_rule *r)
                        if (r->fd < 0) continue;
 
                        r->addr = (struct sockaddr *)calloc(1, ai->ai_addrlen);
+                       if (r->addr == NULL) return -1;
+
                        memcpy(r->addr, ai->ai_addr, ai->ai_addrlen);
 
                        break;
@@ -260,6 +287,7 @@ _syslog_dst_open(struct config_rule *r)
        if (strcmp(r->dst, "*") == 0)
        {
                r->type = DST_TYPE_WALL;
+               r->fd = 0;
                return 0;
        }
 
@@ -268,6 +296,41 @@ _syslog_dst_open(struct config_rule *r)
        return -1;
 }
 
+static void
+_syslog_dst_close(struct config_rule *r)
+{
+       if (r == NULL) return;
+
+       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;
+                       if (r->addr != NULL) free(r->addr);
+                       r->addr = NULL;
+                       break;
+               }
+
+               case DST_TYPE_NONE:
+               case DST_TYPE_WALL:
+               case DST_TYPE_NOTE:
+               default:
+               {
+                       /* do nothing */
+                       return;
+               }
+       }
+}
+
 static int
 _parse_line(char *s)
 {
@@ -284,6 +347,7 @@ _parse_line(char *s)
        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;
@@ -295,7 +359,7 @@ _parse_line(char *s)
        }
 
        out->dst = strdup(semi[lasts]);
-       _syslog_dst_open(out);
+       if (out->dst == NULL) return -1;
 
        for (i = 0; i < lasts; i++)
        {
@@ -321,10 +385,16 @@ _parse_line(char *s)
                        }
                        else
                        {
-                               out->facility = (char **)realloc(out->facility, (out->count + 1) * sizeof(char *));
-                               out->pri = (int *)realloc(out->pri, (out->count + 1) * sizeof(int));
+                               out->facility = (char **)reallocf(out->facility, (out->count + 1) * sizeof(char *));
+                               out->pri = (int *)reallocf(out->pri, (out->count + 1) * sizeof(int));
                        }
+
+                       if (out->facility == NULL) return -1;
+                       if (out->pri == NULL) return -1;
+
                        out->facility[out->count] = strdup(comma[j]);
+                       if (out->facility[out->count] == NULL) return -1;
+
                        out->pri[out->count] = pri;
                        out->count++;
                }
@@ -339,18 +409,129 @@ _parse_line(char *s)
        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)
+{
+       char vt[16], *p, *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;
+
+       tick = time(NULL);
+       p = ctime(&tick);
+       if (p == NULL) return -1;
+
+       memcpy(vt, p+4, 15);
+       vt[15] = '\0';
+
+       msg = NULL;
+       asprintf(&msg, "%s: --- last message repeated %u time%s ---\n", vt, r->last_count, (r->last_count == 1) ? "" : "s");
+       if (msg == NULL) return -1;
+
+       len = strlen(msg);
+       status = write(r->fd, msg, len);
+       if ((status < 0) || (status < len))
+       {
+               asldebug("%s: error writing repeat 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 | O_NOCTTY, 0644);
+               if (r->fd < 0)
+               {
+                       asldebug("%s: re-open failed for file: %s (%s)\n", MY_ID, r->dst, strerror(errno));
+                       free(msg);
+                       return -1;
+               }
+
+               status = write(r->fd, msg, len);
+               if ((status < 0) || (status < len))
+               {
+                       asldebug("%s: error re-writing message (%s): %s\n", MY_ID, r->dst, strerror(errno));
+                       free(msg);
+                       return -1;
+               }
+       }
+
+       free(msg);
+       return 0;
+}
+
 static int
-_syslog_send(asl_msg_t *msg, struct config_rule *r, char **out, char **fwd)
+_syslog_send(asl_msg_t *msg, struct config_rule *r, char **out, char **fwd, time_t now)
 {
-       char *so, *sf, *vt, *p;
-       const char *vtime, *vhost, *vident, *vpid, *vmsg, *vlevel, *vfacility;
+       char *so, *sf, *vt, *p, *outmsg;
+       const char *vtime, *vhost, *vident, *vpid, *vmsg, *vlevel, *vfacility, *vrefproc, *vrefpid;
        size_t outlen, n;
-       int pf, fc, status;
-       FILE *pw;
        time_t tick;
+       int pf, fc, status, is_dup, do_write;
+       FILE *pw;
+       uint32_t msg_hash;
 
        if (out == NULL) return -1;
        if (fwd == NULL) return -1;
+       if (r == NULL) return -1;
 
        if (r->type == DST_TYPE_NOTE)
        {
@@ -358,7 +539,9 @@ _syslog_send(asl_msg_t *msg, struct config_rule *r, char **out, char **fwd)
                return 0;
        }
 
+       msg_hash = 0;
        vt = NULL;
+       outmsg = NULL;
 
        /* Build output string if it hasn't been built by a previous rule-match */
        if (*out == NULL)
@@ -372,25 +555,32 @@ _syslog_send(asl_msg_t *msg, struct config_rule *r, char **out, char **fwd)
                                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);
-               else
-               {
-                       vt = malloc(16);
-                       if (vt == NULL) return -1;
-                       memcpy(vt, vtime+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);
+                       tick = now;
                        p = ctime(&tick);
                        vt = malloc(16);
                        if (vt == NULL) return -1;
+
                        memcpy(vt, p+4, 15);
                        vt[15] = '\0';
                }
@@ -406,17 +596,46 @@ _syslog_send(asl_msg_t *msg, struct config_rule *r, char **out, char **fwd)
 
                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);
-               n += 2;
+
+               /* "[" PID "]" */
                if (vpid != NULL) n += (strlen(vpid) + 2);
-               if (vmsg != NULL) n += strlen(vmsg);
+
+               /* " (" */
+               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);
@@ -445,18 +664,47 @@ _syslog_send(asl_msg_t *msg, struct config_rule *r, char **out, char **fwd)
                        }
                }
 
+               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 (vmsg != NULL)
+               if (outmsg != NULL)
                {
-                       strcat(so, vmsg);
-                       strcat(so, "\n");
+                       strcat(so, outmsg);
+                       free(outmsg);
                }
 
+               strcat(so, "\n");
+
                free(vt);
                *out = so;
        }
 
+       /* 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)))
+               {
+                       if ((now - r->last_time) < global.bsd_max_dup_time) is_dup = 1;
+               }
+       }
+
        if ((*fwd == NULL) && (r->type == DST_TYPE_SOCK))
        {
                pf = 7;
@@ -469,6 +717,7 @@ _syslog_send(asl_msg_t *msg, struct config_rule *r, char **out, char **fwd)
                sf = NULL;
                asprintf(&sf, "<%d>%s", pf, *out);
                if (sf == NULL) return -1;
+
                *fwd = sf;
        }
 
@@ -476,8 +725,18 @@ _syslog_send(asl_msg_t *msg, struct config_rule *r, char **out, char **fwd)
        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);
+
+               do_write = 1;
+
                /*
                 * Special case for kernel messages.
                 * Don't write kernel messages to /dev/console.
@@ -485,16 +744,19 @@ _syslog_send(asl_msg_t *msg, struct config_rule *r, char **out, char **fwd)
                 * 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;
+               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 == 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));
@@ -508,7 +770,7 @@ _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));
@@ -526,6 +788,24 @@ _syslog_send(asl_msg_t *msg, struct config_rule *r, char **out, char **fwd)
                pclose(pw);
        }
 
+       _syslog_dst_close(r);
+
+       if (is_dup == 1)
+       {
+               r->last_count++;
+       }
+       else
+       {
+               if (r->last_msg != NULL) 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;
 }
 
@@ -582,6 +862,8 @@ bsd_out_sendmsg(asl_msg_t *msg, const char *outid)
 {
        struct config_rule *r;
        char *out, *fwd;
+       time_t tick;
+       uint64_t delta;
 
        if (reset != 0)
        {
@@ -594,9 +876,22 @@ bsd_out_sendmsg(asl_msg_t *msg, const char *outid)
        out = NULL;
        fwd = NULL;
 
+       tick = time(NULL);
+       global.bsd_flush_time = 0;
+
        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 (_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 (out != NULL) free(out);
@@ -605,6 +900,39 @@ bsd_out_sendmsg(asl_msg_t *msg, const char *outid)
        return 0;
 }
 
+void
+bsd_flush_duplicates(time_t now)
+{
+       struct config_rule *r;
+       uint64_t delta;
+
+       global.bsd_flush_time = 0;
+
+       for (r = bsd_out_rule.tqh_first; r != NULL; r = r->entries.tqe_next)
+       {
+               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);
+
+                               r->last_count = 0;
+                       }
+               }
+       }
+}
+
 static int
 _parse_config_file(const char *confname)
 {
@@ -657,10 +985,11 @@ 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->last_msg != NULL) free(r->last_msg);
                if (r->facility != NULL)
                {
                        for (i = 0; i < r->count; i++)