X-Git-Url: https://git.saurik.com/apple/syslog.git/blobdiff_plain/d7fa066074f8fd77fb237ffd8c1947e49f1e8837..e125da38929cd700d1ef4f2d364c6fbcbb2247cc:/syslogd.tproj/daemon.c?ds=sidebyside diff --git a/syslogd.tproj/daemon.c b/syslogd.tproj/daemon.c index 775a0d7..ca428fc 100644 --- a/syslogd.tproj/daemon.c +++ b/syslogd.tproj/daemon.c @@ -1,23 +1,22 @@ /* - * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2004-2009 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@ */ @@ -30,31 +29,68 @@ #include #include #include -#include #include #include #define SYSLOG_NAMES #include +#include +#include +#include +#include +#include +#include +#include #include "daemon.h" +#define LIST_SIZE_DELTA 256 + #define streq(A,B) (strcmp(A,B)==0) +#define forever for(;;) +#define IndexNull ((uint32_t)-1) + +#define ASL_MSG_TYPE_MASK 0x0000000f +#define ASL_TYPE_ERROR 2 + +#define ASL_KEY_FACILITY "Facility" + +#define FACILITY_USER "user" +#define FACILITY_CONSOLE "com.apple.console" +#define SYSTEM_RESERVED "com.apple.system" +#define SYSTEM_RESERVED_LEN 16 +#define VERIFY_STATUS_OK 0 +#define VERIFY_STATUS_INVALID_MESSAGE 1 +#define VERIFY_STATUS_EXCEEDED_QUOTA 2 + +extern void disaster_message(asl_msg_t *m); static char myname[MAXHOSTNAMELEN + 1] = {0}; -static int gotname = 0; - -struct aslevent -{ - int fd; - unsigned char read:1; - unsigned char write:1; - unsigned char except:1; - aslreadfn readfn; - aslwritefn writefn; - aslexceptfn exceptfn; - char *sender; - uid_t uid; - gid_t gid; - TAILQ_ENTRY(aslevent) entries; + +static OSSpinLock count_lock = 0; + +#ifndef CONFIG_IPHONE +static vproc_transaction_t vproc_trans = {0}; +#endif + +#define QUOTA_TABLE_SIZE 8192 +#define QUOTA_TABLE_SLOTS 8 + +#define QUOTA_EXCEEDED_MESSAGE "*** process %d exceeded %d log message per second limit - remaining messages this second discarded ***" +#define QUOTA_EXCEEDED_LEVEL "3" + +static time_t quota_table_time = 0; +static pid_t quota_table_pid[QUOTA_TABLE_SIZE]; +static int32_t quota_table_quota[QUOTA_TABLE_SIZE]; + +static const char *kern_notify_key[] = +{ + "com.apple.system.log.kernel.emergency", + "com.apple.system.log.kernel.alert", + "com.apple.system.log.kernel.critical", + "com.apple.system.log.kernel.error", + "com.apple.system.log.kernel.warning", + "com.apple.system.log.kernel.notice", + "com.apple.system.log.kernel.info", + "com.apple.system.log.kernel.debug" }; struct asloutput @@ -75,6 +111,239 @@ TAILQ_HEAD(ae, aslevent) Eventq; TAILQ_HEAD(ao, asloutput) Outq; TAILQ_HEAD(am, aslmatch) Matchq; +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; +} + +char ** +explode(const char *s, const char *delim) +{ + char **l = NULL; + const char *p; + char *t, quote; + int i, n; + + if (s == NULL) return NULL; + + quote = '\0'; + + p = s; + while (p[0] != '\0') + { + /* scan forward */ + for (i = 0; p[i] != '\0'; i++) + { + if (quote == '\0') + { + /* not inside a quoted string: check for delimiters and quotes */ + if (strchr(delim, p[i]) != NULL) break; + else if (p[i] == '\'') quote = p[i]; + else if (p[i] == '"') quote = p[i]; + } + else + { + /* inside a quoted string - look for matching quote */ + if (p[i] == quote) quote = '\0'; + } + } + + 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; +} + +void +freeList(char **l) +{ + int i; + + if (l == NULL) return; + for (i = 0; l[i] != NULL; i++) free(l[i]); + free(l); +} + +/* + * Quotas are maintained using a very fast fixed-size table. + * We hash into the pid table (quota_table_pid) using the last 10 + * bits of the pid, so the table has 1024 "buckets". The table is + * actually just an array with 8 entry slots (for collisions) per bucket. + * If there are more than 8 pids that hash to the same bucket, we + * re-use the one with the lowest message usage (highest remaining + * quota). This can lead to "generosity: if there are nine or more + * pids with the same last 10 bits all logging like crazy, we may + * end up allowing some of them to log more than their quota. + * That would be a remarkably rare occurrence. + */ + +static uint32_t +quota_check(pid_t pid, time_t now, asl_msg_t *msg) +{ + int i, x, maxx, max; + char *str; + + if (msg == NULL) return VERIFY_STATUS_INVALID_MESSAGE; + if (global.mps_limit == 0) return VERIFY_STATUS_OK; + + OSSpinLockLock(&global.lock); + + if (quota_table_time != now) + { + memset(quota_table_pid, 0, sizeof(quota_table_pid)); + quota_table_time = now; + } + + /* hash is last 10 bits of the pid, shifted up 3 bits to allow 8 slots per bucket */ + x = (pid & 0x000003ff) << 3; + maxx = x; + max = quota_table_quota[x]; + + for (i = 0; i < QUOTA_TABLE_SLOTS; i++) + { + if (quota_table_pid[x] == 0) + { + quota_table_pid[x] = pid; + quota_table_quota[x] = global.mps_limit; + + OSSpinLockUnlock(&global.lock); + return VERIFY_STATUS_OK; + } + + if (quota_table_pid[x] == pid) + { + quota_table_quota[x] = quota_table_quota[x] - 1; + + if (quota_table_quota[x] == 0) + { + quota_table_quota[x] = -1; + + str = NULL; + asprintf(&str, QUOTA_EXCEEDED_MESSAGE, (int)pid, global.mps_limit); + if (str != NULL) + { + asl_set(msg, ASL_KEY_MSG, str); + free(str); + asl_set(msg, ASL_KEY_LEVEL, QUOTA_EXCEEDED_LEVEL); + } + + OSSpinLockUnlock(&global.lock); + return VERIFY_STATUS_OK; + } + + if (quota_table_quota[x] < 0) + { + OSSpinLockUnlock(&global.lock); + return VERIFY_STATUS_EXCEEDED_QUOTA; + } + + OSSpinLockUnlock(&global.lock); + return VERIFY_STATUS_OK; + } + + if (quota_table_quota[x] > max) + { + maxx = x; + max = quota_table_quota[x]; + } + + x += 1; + } + + /* can't find the pid and no slots were available - reuse slot with highest remaining quota */ + quota_table_pid[maxx] = pid; + quota_table_quota[maxx] = global.mps_limit; + + OSSpinLockUnlock(&global.lock); + return VERIFY_STATUS_OK; +} + +int +asl_check_option(asl_msg_t *msg, const char *opt) +{ + const char *p; + uint32_t len; + + if (msg == NULL) return 0; + if (opt == NULL) return 0; + + len = strlen(opt); + if (len == 0) return 0; + + p = asl_get(msg, ASL_KEY_OPTION); + if (p == NULL) return 0; + + while (*p != '\0') + { + while ((*p == ' ') || (*p == '\t') || (*p == ',')) p++; + if (*p == '\0') return 0; + + if (strncasecmp(p, opt, len) == 0) + { + p += len; + if ((*p == ' ') || (*p == '\t') || (*p == ',') || (*p == '\0')) return 1; + } + + while ((*p != ' ') && (*p != '\t') && (*p != ',') && (*p != '\0')) p++; + } + + return 0; +} + int aslevent_init(void) { @@ -112,6 +381,7 @@ aslevent_addmatch(asl_msg_t *query, char *outid) tmp = calloc(1, sizeof(struct aslmatch)); if (tmp == NULL) return -1; + tmp->query = query; tmp->outid = outid; TAILQ_INSERT_TAIL(&Matchq, tmp, entries); @@ -120,7 +390,7 @@ aslevent_addmatch(asl_msg_t *query, char *outid) } void -aslevent_match(asl_msg_t *msg) +asl_message_match_and_log(asl_msg_t *msg) { struct aslmatch *i; @@ -138,21 +408,20 @@ aslevent_match(asl_msg_t *msg) int aslevent_removefd(int fd) { - struct aslevent *i; - int found = -1; + struct aslevent *e, *next; - for (i = Eventq.tqh_first; i != NULL; i = i->entries.tqe_next) + e = Eventq.tqh_first; + + while (e != NULL) { - if (fd == i->fd) + next = e->entries.tqe_next; + if (fd == e->fd) { - asldebug("removing %d\n", i->fd); - TAILQ_REMOVE(&Eventq, i, entries); - found = 0; - if (i->sender != NULL) free(i->sender); - free(i); - i = NULL; + e->fd = -1; return 0; } + + e = next; } return -1; @@ -163,31 +432,59 @@ whatsmyhostname() { char *dot; - if (gotname != 0) return (const char *)myname; - if (gethostname(myname, MAXHOSTNAMELEN) < 0) { memset(myname, 0, sizeof(myname)); return "localhost"; } - if (strcmp(myname, "localhost")) gotname = 1; - dot = strchr(myname, '.'); if (dot != NULL) *dot = '\0'; return (const char *)myname; } +void +asl_client_count_increment() +{ + OSSpinLockLock(&count_lock); + +#ifndef CONFIG_IPHONE + if (global.client_count == 0) vproc_trans = vproc_transaction_begin(NULL); +#endif + global.client_count++; +#ifdef DEBUG + asldebug("global.client_count++ (%d)\n", global.client_count); +#endif + + OSSpinLockUnlock(&count_lock); +} + +void +asl_client_count_decrement() +{ + OSSpinLockLock(&count_lock); + + if (global.client_count > 0) global.client_count--; +#ifndef CONFIG_IPHONE + if (global.client_count == 0) vproc_transaction_end(NULL, vproc_trans); +#endif +#ifdef DEBUG + asldebug("global.client_count-- (%d)\n", global.client_count); +#endif + + OSSpinLockUnlock(&count_lock); +} + int -aslevent_addfd(int fd, aslreadfn readfn, aslwritefn writefn, aslexceptfn exceptfn) +aslevent_addfd(int source, int fd, uint32_t flags, aslreadfn readfn, aslwritefn writefn, aslexceptfn exceptfn) { struct aslevent *e; - int found = 0; + int found = 0, status; #ifdef LOCAL_PEERCRED struct xucred cr; #endif - int len; + socklen_t len; uid_t u; gid_t g; struct sockaddr_storage ss; @@ -198,34 +495,45 @@ aslevent_addfd(int fd, aslreadfn readfn, aslwritefn writefn, aslexceptfn exceptf sender = NULL; memset(&ss, 0, sizeof(struct sockaddr_storage)); + memset(str, 0, sizeof(str)); len = sizeof(struct sockaddr_storage); - if (getpeername(fd, (struct sockaddr *)&ss, &len) == 0) + if (flags & ADDFD_FLAGS_LOCAL) { - if (len == 0) - { - /* UNIX Domain socket */ - snprintf(str, sizeof(str), whatsmyhostname()); - sender = str; - } - else - { - if (inet_ntop(ss.ss_family, (struct sockaddr *)&ss, str, 256) == 0) sender = str; - } - } + snprintf(str, sizeof(str), "localhost"); + sender = str; #ifdef LOCAL_PEERCRED - len = sizeof(cr); + len = sizeof(cr); - if (getsockopt(fd, LOCAL_PEERCRED, 1, &cr, &len) == 0) + status = getsockopt(fd, LOCAL_PEERCRED, 1, &cr, &len); + if (status == 0) + { + u = cr.cr_uid; + g = cr.cr_gid; + } +#endif + } + else { - u = cr.cr_uid; - g = cr.cr_gid; + status = getpeername(fd, (struct sockaddr *)&ss, &len); + if (status == 0) + { + if (len == 0) + { + /* UNIX Domain socket */ + snprintf(str, sizeof(str), "localhost"); + sender = str; + } + else + { + if (inet_ntop(ss.ss_family, (struct sockaddr *)&ss, str, 256) == NULL) sender = str; + } + } } -#endif - asldebug("fd %d UID %d GID %d Sender %s\n", fd, u, g, (sender == NULL) ? "NULL" : sender ); + asldebug("source %d fd %d flags 0x%08x UID %d GID %d Sender %s\n", source, fd, flags, u, g, (sender == NULL) ? "NULL" : sender ); for (e = Eventq.tqh_first; e != NULL; e = e->entries.tqe_next) { @@ -236,7 +544,12 @@ aslevent_addfd(int fd, aslreadfn readfn, aslwritefn writefn, aslexceptfn exceptf e->exceptfn = exceptfn; if (e->sender != NULL) free(e->sender); e->sender = NULL; - if (sender != NULL) e->sender = strdup(sender); + if (sender != NULL) + { + e->sender = strdup(sender); + if (e->sender == NULL) return -1; + } + e->uid = u; e->gid = g; found = 1; @@ -248,12 +561,18 @@ aslevent_addfd(int fd, aslreadfn readfn, aslwritefn writefn, aslexceptfn exceptf e = calloc(1, sizeof(struct aslevent)); if (e == NULL) return -1; + e->source = source; e->fd = fd; e->readfn = readfn; e->writefn = writefn; e->exceptfn = exceptfn; e->sender = NULL; - if (sender != NULL) e->sender = strdup(sender); + if (sender != NULL) + { + e->sender = strdup(sender); + if (e->sender == NULL) return -1; + } + e->uid = u; e->gid = g; @@ -262,86 +581,267 @@ aslevent_addfd(int fd, aslreadfn readfn, aslwritefn writefn, aslexceptfn exceptf return 0; } -int -aslmsg_verify(struct aslevent *e, asl_msg_t *msg) +/* + * Checks message content and sets attributes as required + * + * SOURCE_INTERNAL log messages sent by syslogd itself + * SOURCE_ASL_SOCKET legacy asl(3) TCP socket + * SOURCE_BSD_SOCKET legacy syslog(3) UDP socket + * SOURCE_UDP_SOCKET from the network + * SOURCE_KERN from the kernel + * SOURCE_ASL_MESSAGE mach messages sent from Libc by asl(3) and syslog(3) + * SOURCE_LAUNCHD forwarded from launchd + */ + +static uint32_t +aslmsg_verify(uint32_t source, struct aslevent *e, asl_msg_t *msg, int32_t *kern_post_level) { - const char *val; - char buf[32], *timestr; - time_t tick; - struct tm gtime; + const char *val, *fac; + char buf[64]; + time_t tick, now; + uid_t uid; + uint32_t status, level, fnum; + pid_t pid; + + if (msg == NULL) return VERIFY_STATUS_INVALID_MESSAGE; - if (msg == NULL) return -1; + if (kern_post_level != NULL) *kern_post_level = -1; /* Time */ + now = time(NULL); + tick = 0; val = asl_get(msg, ASL_KEY_TIME); if (val != NULL) tick = asl_parse_time(val); - if (tick == 0) tick = time(NULL); - memset(>ime, 0, sizeof(struct tm)); - gmtime_r(&tick, >ime); + /* Set time to now if it is unset or from the future (not allowed!) */ + if ((tick == 0) || (tick > now)) tick = now; - /* Canonical form: YYYY.MM.DD hh:mm:ss UTC */ - asprintf(×tr, "%d.%02d.%02d %02d:%02d:%02d UTC", gtime.tm_year + 1900, gtime.tm_mon + 1, gtime.tm_mday, gtime.tm_hour, gtime.tm_min, gtime.tm_sec); - if (timestr != NULL) - { - asl_set(msg, ASL_KEY_TIME, timestr); - free(timestr); - } + /* Canonical form: seconds since the epoch */ + snprintf(buf, sizeof(buf) - 1, "%lu", tick); + asl_set(msg, ASL_KEY_TIME, buf); /* Host */ if (e == NULL) asl_set(msg, ASL_KEY_HOST, whatsmyhostname()); - else if (e->sender != NULL) asl_set(msg, ASL_KEY_HOST, e->sender); - - /* Sender */ - val = asl_get(msg, ASL_KEY_SENDER); - if (val == NULL) asl_set(msg, ASL_KEY_SENDER, "Unknown"); + else if (e->sender != NULL) + { + if (!strcmp(e->sender, "localhost")) asl_set(msg, ASL_KEY_HOST, whatsmyhostname()); + else asl_set(msg, ASL_KEY_HOST, e->sender); + } /* PID */ + pid = 0; + val = asl_get(msg, ASL_KEY_PID); if (val == NULL) asl_set(msg, ASL_KEY_PID, "0"); + else pid = (pid_t)atoi(val); + + /* if PID is 1 (launchd), use the refpid if there is one */ + if (pid == 1) + { + val = asl_get(msg, ASL_KEY_REF_PID); + if (val != NULL) pid = (pid_t)atoi(val); + } + + /* if quotas are enabled and pid > 1 (not kernel or launchd) check quota */ + if ((global.mps_limit > 0) && (pid > 1)) + { + status = quota_check(pid, now, msg); + if (status != VERIFY_STATUS_OK) return status; + } /* UID */ + uid = -2; val = asl_get(msg, ASL_KEY_UID); - if (val == NULL) + + switch (source) { - if (e == NULL) asl_set(msg, ASL_KEY_UID, "-2"); - else if (e->uid == 99) asl_set(msg, ASL_KEY_UID, "-2"); - else + case SOURCE_KERN: + case SOURCE_INTERNAL: { - snprintf(buf, sizeof(buf), "%d", e->uid); - asl_set(msg, ASL_KEY_UID, buf); + /* we know the UID is 0 */ + uid = 0; + asl_set(msg, ASL_KEY_UID, "0"); + break; + } + case SOURCE_ASL_SOCKET: + case SOURCE_ASL_MESSAGE: + case SOURCE_LAUNCHD: + { + /* we trust the UID in the message */ + if (val != NULL) uid = atoi(val); + break; + } + case SOURCE_BSD_SOCKET: + case SOURCE_UDP_SOCKET: + { + if (val == NULL) + { + if (e == NULL) asl_set(msg, ASL_KEY_UID, "-2"); + else if (e->uid == 99) asl_set(msg, ASL_KEY_UID, "-2"); + else + { + uid = e->uid; + snprintf(buf, sizeof(buf), "%d", e->uid); + asl_set(msg, ASL_KEY_UID, buf); + } + } + else if ((e != NULL) && (e->uid != 99)) + { + uid = e->uid; + snprintf(buf, sizeof(buf), "%d", e->uid); + asl_set(msg, ASL_KEY_UID, buf); + } + } + default: + { + asl_set(msg, ASL_KEY_UID, "-2"); } - } - else if ((e != NULL) && (e->uid != 99)) - { - snprintf(buf, sizeof(buf), "%d", e->uid); - asl_set(msg, ASL_KEY_UID, buf); } /* GID */ val = asl_get(msg, ASL_KEY_GID); + + switch (source) + { + case SOURCE_KERN: + case SOURCE_INTERNAL: + { + /* we know the GID is 0 */ + asl_set(msg, ASL_KEY_GID, "0"); + break; + } + case SOURCE_ASL_SOCKET: + case SOURCE_ASL_MESSAGE: + case SOURCE_LAUNCHD: + { + /* we trust the GID in the message */ + break; + } + case SOURCE_BSD_SOCKET: + case SOURCE_UDP_SOCKET: + { + if (val == NULL) + { + if (e == NULL) asl_set(msg, ASL_KEY_GID, "-2"); + else if (e->gid == 99) asl_set(msg, ASL_KEY_GID, "-2"); + else + { + snprintf(buf, sizeof(buf), "%d", e->gid); + asl_set(msg, ASL_KEY_GID, buf); + } + } + else if ((e != NULL) && (e->gid != 99)) + { + snprintf(buf, sizeof(buf), "%d", e->gid); + asl_set(msg, ASL_KEY_GID, buf); + } + } + default: + { + asl_set(msg, ASL_KEY_GID, "-2"); + } + } + + /* Sender */ + val = asl_get(msg, ASL_KEY_SENDER); if (val == NULL) { - if (e == NULL) asl_set(msg, ASL_KEY_GID, "-2"); - else if (e->gid == 99) asl_set(msg, ASL_KEY_GID, "-2"); - else + switch (source) { - snprintf(buf, sizeof(buf), "%d", e->gid); - asl_set(msg, ASL_KEY_GID, buf); + case SOURCE_KERN: + { + asl_set(msg, ASL_KEY_SENDER, "kernel"); + break; + } + case SOURCE_INTERNAL: + { + asl_set(msg, ASL_KEY_SENDER, "syslogd"); + break; + } + default: + { + asl_set(msg, ASL_KEY_SENDER, "Unknown"); + } } } - else if ((e != NULL) && (e->gid != 99)) + else if ((source != SOURCE_KERN) && (uid != 0) && (!strcmp(val, "kernel"))) { - snprintf(buf, sizeof(buf), "%d", e->gid); - asl_set(msg, ASL_KEY_GID, buf); + /* allow UID 0 to send messages with "Sender kernel", but nobody else */ + asl_set(msg, ASL_KEY_SENDER, "Unknown"); } /* Level */ val = asl_get(msg, ASL_KEY_LEVEL); - if (val == NULL) asl_set(msg, ASL_KEY_LEVEL, "7"); + level = ASL_LEVEL_DEBUG; + if ((val != NULL) && (val[1] == '\0') && (val[0] >= '0') && (val[0] <= '7')) level = val[0] - '0'; + snprintf(buf, sizeof(buf), "%d", level); + asl_set(msg, ASL_KEY_LEVEL, buf); + + /* Facility */ + fac = asl_get(msg, ASL_KEY_FACILITY); + if (fac == NULL) + { + if (source == SOURCE_KERN) fac = "kern"; + else fac = "user"; + asl_set(msg, ASL_KEY_FACILITY, fac); + } + else if (fac[0] == '#') + { + fnum = LOG_USER; + if ((fac[1] >= '0') && (fac[1] <= '9')) + { + fnum = atoi(fac + 1) << 3; + if ((fnum == 0) && (strcmp(fac + 1, "0"))) fnum = LOG_USER; + } - return 0; + fac = asl_syslog_faciliy_num_to_name(fnum); + asl_set(msg, ASL_KEY_FACILITY, fac); + } + else if (!strncmp(fac, SYSTEM_RESERVED, SYSTEM_RESERVED_LEN)) + { + /* only UID 0 may use "com.apple.system" */ + if (uid != 0) asl_set(msg, ASL_KEY_FACILITY, FACILITY_USER); + } + + /* + * kernel messages are only readable by root and admin group. + */ + if (source == SOURCE_KERN) + { + asl_set(msg, ASL_KEY_READ_UID, "0"); + asl_set(msg, ASL_KEY_READ_GID, "80"); + } + + /* + * Access Control: only UID 0 may use facility com.apple.system (or anything with that prefix). + * N.B. kernel can use any facility name. + */ + + /* Set DB Expire Time for com.apple.system.utmpx and lastlog */ + if ((!strcmp(fac, "com.apple.system.utmpx")) || (!strcmp(fac, "com.apple.system.lastlog"))) + { + snprintf(buf, sizeof(buf), "%lu", tick + global.utmp_ttl); + asl_set(msg, ASL_KEY_EXPIRE_TIME, buf); + } + + /* Set DB Expire Time for Filestsrem errors */ + if (!strcmp(fac, FSLOG_VAL_FACILITY)) + { + snprintf(buf, sizeof(buf), "%lu", tick + global.fs_ttl); + asl_set(msg, ASL_KEY_EXPIRE_TIME, buf); + } + + /* + * special case handling of kernel disaster messages + */ + if ((source == SOURCE_KERN) && (level <= KERN_DISASTER_LEVEL)) + { + if (kern_post_level != NULL) *kern_post_level = level; + disaster_message(msg); + } + + return VERIFY_STATUS_OK; } int @@ -366,14 +866,16 @@ aslevent_fdsets(fd_set *rd, fd_set *wr, fd_set *ex) struct aslevent *e; int status = 0; - asldebug("--> aslevent_fdsets\n"); +// asldebug("--> aslevent_fdsets\n"); FD_ZERO(rd); FD_ZERO(wr); FD_ZERO(ex); for (e = Eventq.tqh_first; e != NULL; e = e->entries.tqe_next) { - asldebug("adding fd %d\n", e->fd); + if (e->fd < 0) continue; + +// asldebug("adding fd %d\n", e->fd); if (e->readfn) { FD_SET(e->fd, rd); @@ -393,53 +895,167 @@ aslevent_fdsets(fd_set *rd, fd_set *wr, fd_set *ex) } } - asldebug("<--aslevent_fdsets\n"); +// asldebug("<--aslevent_fdsets\n"); return status; } void -aslevent_handleevent(fd_set rd, fd_set wr, fd_set ex, char *errstr) +aslevent_cleanup() +{ + struct aslevent *e, *next; + + e = Eventq.tqh_first; + + while (e != NULL) + { + next = e->entries.tqe_next; + if (e->fd < 0) + { + TAILQ_REMOVE(&Eventq, e, entries); + if (e->sender != NULL) free(e->sender); + free(e); + } + + e = next; + } +} + +void +list_append_msg(asl_search_result_t *list, asl_msg_t *msg) +{ + if (list == NULL) return; + if (msg == NULL) return; + + /* + * NB: curr is the list size + * grow list if necessary + */ + if (list->count == list->curr) + { + if (list->curr == 0) + { + list->msg = (asl_msg_t **)calloc(LIST_SIZE_DELTA, sizeof(asl_msg_t *)); + } + else + { + list->msg = (asl_msg_t **)reallocf(list->msg, (list->curr + LIST_SIZE_DELTA) * sizeof(asl_msg_t *)); + } + + if (list->msg == NULL) + { + list->curr = 0; + list->count = 0; + return; + } + + list->curr += LIST_SIZE_DELTA; + } + + list->msg[list->count] = msg; + list->count++; +} + +void +work_enqueue(asl_msg_t *m) +{ + pthread_mutex_lock(global.work_queue_lock); + list_append_msg(global.work_queue, m); + pthread_mutex_unlock(global.work_queue_lock); + pthread_cond_signal(&global.work_queue_cond); +} + +void +asl_enqueue_message(uint32_t source, struct aslevent *e, asl_msg_t *msg) +{ + int32_t kplevel; + uint32_t status; + + if (msg == NULL) return; + + /* set retain count to 1 */ + msg->type |= 0x10; + + kplevel = -1; + status = aslmsg_verify(source, e, msg, &kplevel); + if (status == VERIFY_STATUS_OK) + { + if ((source == SOURCE_KERN) && (kplevel >= 0)) notify_post(kern_notify_key[kplevel]); + work_enqueue(msg); + } + else + { + asl_msg_release(msg); + } +} + +asl_msg_t ** +asl_work_dequeue(uint32_t *count) +{ + asl_msg_t **work; + + pthread_mutex_lock(global.work_queue_lock); + pthread_cond_wait(&global.work_queue_cond, global.work_queue_lock); + + work = NULL; + *count = 0; + + if (global.work_queue->count == 0) + { + pthread_mutex_unlock(global.work_queue_lock); + return NULL; + } + + work = global.work_queue->msg; + *count = global.work_queue->count; + + global.work_queue->count = 0; + global.work_queue->curr = 0; + global.work_queue->msg = NULL; + + pthread_mutex_unlock(global.work_queue_lock); + return work; +} + +void +aslevent_handleevent(fd_set *rd, fd_set *wr, fd_set *ex) { struct aslevent *e; char *out = NULL; asl_msg_t *msg; + int32_t cleanup; + +// asldebug("--> aslevent_handleevent\n"); - asldebug("--> aslevent_handleevent\n"); - if (errstr) errstr = NULL; + cleanup = 0; for (e = Eventq.tqh_first; e != NULL; e = e->entries.tqe_next) { - if (FD_ISSET(e->fd, &rd) && (e->readfn != NULL)) + if (e->fd < 0) + { + cleanup = 1; + continue; + } + + if (FD_ISSET(e->fd, rd) && (e->readfn != NULL)) { - asldebug("handling read event on %d\n", e->fd); +// asldebug("handling read event on %d\n", e->fd); msg = e->readfn(e->fd); - if (msg == NULL) - { - asldebug("error reading message\n"); - continue; - } + if (msg == NULL) continue; - if (aslmsg_verify(e, msg) < 0) - { - asl_free(msg); - asldebug("recieved invalid message\n"); - } - else - { - aslevent_match(msg); - asl_free(msg); - } + asl_enqueue_message(e->source, e, msg); } - if (FD_ISSET(e->fd, &ex) && e->exceptfn) + if (FD_ISSET(e->fd, ex) && e->exceptfn) { asldebug("handling except event on %d\n", e->fd); out = e->exceptfn(e->fd); - if (out == NULL) asldebug("error writing message\n"); + if (out == NULL) asldebug("error writing message\n\n"); } } - asldebug("<-- aslevent_handleevent\n"); + if (cleanup != 0) aslevent_cleanup(); + +// asldebug("<-- aslevent_handleevent\n"); } int @@ -450,19 +1066,47 @@ asl_log_string(const char *str) if (str == NULL) return 1; msg = asl_msg_from_string(str); - if (aslmsg_verify(NULL, msg) < 0) + if (msg == NULL) return 1; + + asl_enqueue_message(SOURCE_INTERNAL, NULL, msg); + + return 0; +} + +int +asldebug(const char *str, ...) +{ + va_list v; + int status; + FILE *dfp; + + OSSpinLockLock(&global.lock); + if (global.debug == 0) { - asl_free(msg); - return -1; + OSSpinLockUnlock(&global.lock); + return 0; } - aslevent_match(msg); - asl_free(msg); - return 0; + dfp = stderr; + if (global.debug_file != NULL) dfp = fopen(global.debug_file, "a"); + if (dfp == NULL) + { + OSSpinLockUnlock(&global.lock); + return 0; + } + + va_start(v, str); + status = vfprintf(dfp, str, v); + va_end(v); + + if (global.debug_file != NULL) fclose(dfp); + OSSpinLockUnlock(&global.lock); + + return status; } void -aslmark(void) +asl_mark(void) { char *str; @@ -491,8 +1135,6 @@ asl_syslog_input_convert(const char *in, int len, char *rhost, int kern) if (in == NULL) return NULL; if (len <= 0) return NULL; - asldebug("asl_syslog_input_convert: %s\n", in); - pri = LOG_DEBUG; tval = NULL; sval = NULL; @@ -541,6 +1183,8 @@ asl_syslog_input_convert(const char *in, int len, char *rhost, int kern) if (((len - index) > 15) && (p[9] == ':') && (p[12] == ':') && (p[15] == ' ')) { tmp = malloc(16); + if (tmp == NULL) return NULL; + memcpy(tmp, p, 15); tmp[15] = '\0'; @@ -563,16 +1207,8 @@ asl_syslog_input_convert(const char *in, int len, char *rhost, int kern) if (kern != 0) { msg = (asl_msg_t *)calloc(1, sizeof(asl_msg_t)); - msg->type = ASL_TYPE_MSG; - - asl_set(msg, ASL_KEY_SENDER, "kernel"); + if (msg == NULL) return NULL; - asl_set(msg, "Facility", "kern"); - if (tval != NULL) - { - asl_set(msg, ASL_KEY_TIME, tval); - free(tval); - } asl_set(msg, ASL_KEY_MSG, p); @@ -580,10 +1216,6 @@ asl_syslog_input_convert(const char *in, int len, char *rhost, int kern) asl_set(msg, ASL_KEY_PID, "0"); - asl_set(msg, ASL_KEY_UID, "0"); - - asl_set(msg, ASL_KEY_GID, "0"); - asl_set(msg, ASL_KEY_HOST, whatsmyhostname()); return msg; @@ -598,11 +1230,15 @@ asl_syslog_input_convert(const char *in, int len, char *rhost, int kern) { n = brace - p; sval = malloc(n + 1); + if (sval == NULL) return NULL; + memcpy(sval, p, n); sval[n] = '\0'; n = colon - (brace + 1) - 1; pval = malloc(n + 1); + if (pval == NULL) return NULL; + memcpy(pval, (brace + 1), n); pval[n] = '\0'; } @@ -610,6 +1246,8 @@ asl_syslog_input_convert(const char *in, int len, char *rhost, int kern) { n = colon - p; sval = malloc(n + 1); + if (sval == NULL) return NULL; + memcpy(sval, p, n); sval[n] = '\0'; } @@ -629,6 +1267,8 @@ asl_syslog_input_convert(const char *in, int len, char *rhost, int kern) if (n > 0) { mval = malloc(n + 1); + if (mval == NULL) return NULL; + memcpy(mval, p, n); mval[n] = '\0'; } @@ -636,7 +1276,7 @@ asl_syslog_input_convert(const char *in, int len, char *rhost, int kern) if (fval == NULL) fval = asl_syslog_faciliy_num_to_name(LOG_USER); msg = (asl_msg_t *)calloc(1, sizeof(asl_msg_t)); - msg->type = ASL_TYPE_MSG; + if (msg == NULL) return NULL; if (tval != NULL) { @@ -675,13 +1315,50 @@ asl_syslog_input_convert(const char *in, int len, char *rhost, int kern) if (msg->count == 0) { - asl_free(msg); + asl_msg_release(msg); return NULL; } return msg; } +asl_msg_t * +asl_input_parse(const char *in, int len, char *rhost, int kern) +{ + asl_msg_t *m; + int status, x, legacy; + + asldebug("asl_input_parse: %s\n", (in == NULL) ? "NULL" : in); + + if (in == NULL) return NULL; + + legacy = 1; + m = NULL; + + /* calculate length if not provided */ + if (len == 0) len = strlen(in); + + /* + * Determine if the input is "old" syslog format or new ASL format. + * Old format lines should start with "<", but they can just be straight text. + * ASL input starts with a length (10 bytes) followed by a space and a '['. + */ + if ((in[0] != '<') && (len > 11)) + { + status = sscanf(in, "%d ", &x); + if ((status == 1) && (in[10] == ' ') && (in[11] == '[')) legacy = 0; + } + + if (legacy == 1) return asl_syslog_input_convert(in, len, rhost, kern); + + m = asl_msg_from_string(in + 11); + if (m == NULL) return NULL; + + if (rhost != NULL) asl_set(m, ASL_KEY_HOST, rhost); + + return m; +} + char * get_line_from_file(FILE *f) { @@ -693,8 +1370,158 @@ get_line_from_file(FILE *f) if (len == 0) return NULL; s = malloc(len + 1); + if (s == NULL) return NULL; + memcpy(s, out, len); s[len - 1] = '\0'; return s; } + +uint32_t +asl_msg_type(asl_msg_t *m) +{ + if (m == NULL) return ASL_TYPE_ERROR; + return (m->type & ASL_MSG_TYPE_MASK); +} + +void +asl_msg_release(asl_msg_t *m) +{ + int32_t newval; + + if (m == NULL) return; + + newval = OSAtomicAdd32(-0x10, (int32_t*)&m->type) >> 4; + assert(newval >= 0); + + if (newval > 0) return; + + asl_free(m); +} + +asl_msg_t * +asl_msg_retain(asl_msg_t *m) +{ + int32_t newval; + + if (m == NULL) return NULL; + + newval = OSAtomicAdd32(0x10, (int32_t*)&m->type) >> 4; + assert(newval > 0); + + return m; +} + +void +launchd_callback(struct timeval *when, pid_t from_pid, pid_t about_pid, uid_t sender_uid, gid_t sender_gid, int priority, const char *from_name, const char *about_name, const char *session_name, const char *msg) +{ + asl_msg_t *m; + char str[256]; + time_t now; + +/* + asldebug("launchd_callback Time %lu %lu PID %u RefPID %u UID %d GID %d PRI %d Sender %s Ref %s Session %s Message %s\n", + when->tv_sec, when->tv_usec, from_pid, about_pid, sender_uid, sender_gid, priority, from_name, about_name, session_name, msg); +*/ + + m = asl_new(ASL_TYPE_MSG); + if (m == NULL) return; + + /* Level */ + if (priority < ASL_LEVEL_EMERG) priority = ASL_LEVEL_EMERG; + if (priority > ASL_LEVEL_DEBUG) priority = ASL_LEVEL_DEBUG; + snprintf(str, sizeof(str), "%d", priority); + + asl_set(m, ASL_KEY_LEVEL, str); + + /* Time */ + if (when != NULL) + { + snprintf(str, sizeof(str), "%lu", when->tv_sec); + asl_set(m, ASL_KEY_TIME, str); + + snprintf(str, sizeof(str), "%lu", 1000 * (unsigned long int)when->tv_usec); + asl_set(m, ASL_KEY_TIME_NSEC, str); + } + else + { + now = time(NULL); + snprintf(str, sizeof(str), "%lu", now); + asl_set(m, ASL_KEY_TIME, str); + } + + /* Host */ + asl_set(m, ASL_KEY_HOST, whatsmyhostname()); + + /* Facility */ + asl_set(m, ASL_KEY_FACILITY, FACILITY_CONSOLE); + + /* UID */ + snprintf(str, sizeof(str), "%u", (unsigned int)sender_uid); + asl_set(m, ASL_KEY_UID, str); + + /* GID */ + snprintf(str, sizeof(str), "%u", (unsigned int)sender_gid); + asl_set(m, ASL_KEY_GID, str); + + /* PID */ + if (from_pid != 0) + { + snprintf(str, sizeof(str), "%u", (unsigned int)from_pid); + asl_set(m, ASL_KEY_PID, str); + } + + /* Reference PID */ + if ((about_pid > 0) && (about_pid != from_pid)) + { + snprintf(str, sizeof(str), "%u", (unsigned int)about_pid); + asl_set(m, ASL_KEY_REF_PID, str); + } + + /* Sender */ + if (from_name != NULL) + { + asl_set(m, ASL_KEY_SENDER, from_name); + } + + /* ReadUID */ + if (sender_uid != 0) + { + snprintf(str, sizeof(str), "%d", (int)sender_uid); + asl_set(m, ASL_KEY_READ_UID, str); + } + + /* Reference Process */ + if (about_name != NULL) + { + if ((from_name != NULL) && (strcmp(from_name, about_name) != 0)) + { + asl_set(m, ASL_KEY_REF_PROC, about_name); + } + } + + /* Session */ + if (session_name != NULL) + { + asl_set(m, ASL_KEY_SESSION, session_name); + } + + /* Message */ + if (msg != NULL) + { + asl_set(m, ASL_KEY_MSG, msg); + } + + /* verify and push to receivers */ + asl_enqueue_message(SOURCE_LAUNCHD, NULL, m); +} + +void +launchd_drain() +{ + forever + { + _vprocmgr_log_drain(NULL, NULL, launchd_callback); + } +}