/*
- * 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@
*/
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
+#include <ctype.h>
#include <fcntl.h>
#include <errno.h>
#include <netdb.h>
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 *);
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 **)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;
}
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);
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));
if (r->dst[0] == '!')
{
r->type = DST_TYPE_NOTE;
+ r->fd = 0;
return 0;
}
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;
if (strcmp(r->dst, "*") == 0)
{
r->type = DST_TYPE_WALL;
+ r->fd = 0;
return 0;
}
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)
{
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;
}
out->dst = strdup(semi[lasts]);
- _syslog_dst_open(out);
+ if (out->dst == NULL) return -1;
for (i = 0; i < lasts; i++)
{
}
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++;
}
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)
{
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)
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';
}
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);
}
}
+ 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;
sf = NULL;
asprintf(&sf, "<%d>%s", pf, *out);
if (sf == NULL) return -1;
+
*fwd = sf;
}
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.
* 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));
}
}
}
- 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));
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;
}
{
struct config_rule *r;
char *out, *fwd;
+ time_t tick;
+ uint64_t delta;
if (reset != 0)
{
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);
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)
{
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++)