X-Git-Url: https://git.saurik.com/apple/syslog.git/blobdiff_plain/b16a592aee171655b85f8e45c0604c38494ab8ea..5dd30d768c7cd795c2a3b974a6a1809dd8a3becf:/syslogd.tproj/bsd_out.c diff --git a/syslogd.tproj/bsd_out.c b/syslogd.tproj/bsd_out.c index a24fdaf..7da14d8 100644 --- a/syslogd.tproj/bsd_out.c +++ b/syslogd.tproj/bsd_out.c @@ -3,21 +3,20 @@ * * @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 #include #include +#include #include #include #include @@ -55,6 +55,9 @@ static asl_msg_t *query = NULL; static int reset = 0; +extern uint64_t bsd_flush_time; +extern uint64_t bsd_max_dup_time; + struct config_rule { uint32_t count; @@ -64,11 +67,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 string_hash(const char *s, uint32_t inlen); + int bsd_out_close(); static int _parse_config_file(const char *); @@ -88,7 +97,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 +113,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 +151,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 +208,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 +228,7 @@ _syslog_dst_open(struct config_rule *r) if (r->dst[0] == '!') { r->type = DST_TYPE_NOTE; + r->fd = 0; return 0; } @@ -231,6 +259,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 +290,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 +299,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 +350,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 +362,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 +388,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 +412,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 +542,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 +558,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 +599,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 +667,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 ((bsd_max_dup_time > 0) && (*out != NULL) && (r->last_msg != NULL)) + { + msg_hash = string_hash(*out + 16, strlen(*out + 16)); + if ((r->last_hash == msg_hash) && (!strcmp(r->last_msg, *out + 16))) + { + if ((now - r->last_time) < bsd_max_dup_time) is_dup = 1; + } + } + if ((*fwd == NULL) && (r->type == DST_TYPE_SOCK)) { pf = 7; @@ -469,6 +720,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 +728,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 +747,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 +773,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 +791,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 +865,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 +879,22 @@ bsd_out_sendmsg(asl_msg_t *msg, const char *outid) out = NULL; fwd = NULL; + tick = time(NULL); + 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 < bsd_max_dup_time) + { + delta = bsd_max_dup_time - delta; + if (bsd_flush_time == 0) bsd_flush_time = delta; + else if (delta < bsd_flush_time) bsd_flush_time = delta; + } + } } if (out != NULL) free(out); @@ -605,6 +903,41 @@ bsd_out_sendmsg(asl_msg_t *msg, const char *outid) return 0; } +void +bsd_flush_duplicates() +{ + struct config_rule *r; + time_t tick; + uint64_t delta; + + tick = time(NULL); + 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 = tick - r->last_time; + if (delta < bsd_max_dup_time) + { + delta = bsd_max_dup_time - delta; + if (bsd_flush_time == 0) bsd_flush_time = delta; + else if (delta < bsd_flush_time) 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 +990,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++)