/*
- * Copyright (c) 2004 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2004-2012 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 <TargetConditionals.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
-#include <sys/queue.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define SYSLOG_NAMES
#include <syslog.h>
+#include <sys/fslog.h>
+#include <vproc.h>
+#include <pthread.h>
+#include <vproc_priv.h>
+#include <mach/mach.h>
+#include <libkern/OSAtomic.h>
+#include <libproc.h>
+#include <uuid/uuid.h>
+#include <asl_private.h>
#include "daemon.h"
-#define streq(A,B) (strcmp(A,B)==0)
+#define LIST_SIZE_DELTA 256
+#define STATS_TABLE_SIZE 256
+
+#define forever for(;;)
+
+#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);
+extern int asl_action_reset(void);
+extern int asl_action_control_set_param(const char *s);
static char myname[MAXHOSTNAMELEN + 1] = {0};
-static int gotname = 0;
+static int name_change_token = -1;
-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;
-struct asloutput
-{
- aslsendmsgfn sendmsg;
- const char *outid;
- TAILQ_ENTRY(asloutput) entries;
-};
+#if !TARGET_OS_EMBEDDED
+static vproc_transaction_t vproc_trans = {0};
+#endif
-struct aslmatch
+#define DEFAULT_MEMORY_MAX SYSLOGD_WORK_QUEUE_MEMORY
+
+#define QUOTA_KERN_EXCEEDED_MESSAGE "*** kernel exceeded %d log message per second limit - remaining messages this second discarded ***"
+
+#define DEFAULT_DB_FILE_MAX 25600000
+#define DEFAULT_DB_MEMORY_MAX 256
+#define DEFAULT_DB_MEMORY_STR_MAX 1024000
+#define DEFAULT_MPS_LIMIT 500
+#define DEFAULT_REMOTE_DELAY 5000
+#define DEFAULT_BSD_MAX_DUP_SEC 30
+#define DEFAULT_MARK_SEC 0
+#define DEFAULT_UTMP_TTL_SEC 31622400
+#define DEFAULT_STATS_INTERVAL 600
+
+#define ASL_STATS_LEVEL 5
+
+static time_t quota_time = 0;
+static int32_t kern_quota;
+static int32_t kern_level;
+
+static const char *kern_notify_key[] =
{
- char *outid;
- asl_msg_t *query;
- TAILQ_ENTRY(aslmatch) entries;
+ "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"
};
-TAILQ_HEAD(ae, aslevent) Eventq;
-TAILQ_HEAD(ao, asloutput) Outq;
-TAILQ_HEAD(am, aslmatch) Matchq;
+static int kern_notify_token[8] = {-1, -1, -1, -1, -1, -1, -1, -1};
-int
-aslevent_init(void)
+static stats_table_t *
+stats_table_new()
{
- TAILQ_INIT(&Eventq);
- TAILQ_INIT(&Outq);
- TAILQ_INIT(&Matchq);
+ stats_table_t *t = (stats_table_t *)malloc(sizeof(stats_table_t));
+ if (t == NULL) return NULL;
- return 0;
+ t->bucket_count = STATS_TABLE_SIZE;
+ t->bucket = (sender_stats_t **)calloc(t->bucket_count, sizeof(sender_stats_t *));
+ if (t->bucket == NULL)
+ {
+ free(t);
+ return NULL;
+ }
+
+ t->mcount = 0;
+ t->shim_count = 0;
+
+ return t;
}
-int
-aslevent_log(asl_msg_t *msg, char *outid)
+static asl_msg_t *
+stats_table_final(stats_table_t *t)
{
- struct asloutput *i;
- int status = -1;
-
- for (i = Outq.tqh_first; i != NULL; i = i->entries.tqe_next)
+ uint32_t i;
+ asl_msg_t *msg;
+ char val[64];
+
+ if (t == NULL) return NULL;
+
+ msg = asl_msg_new(ASL_TYPE_MSG);
+ if (msg == NULL) return NULL;
+
+ asl_msg_set_key_val(msg, ASL_KEY_MSG, "ASL Sender Statistics");
+ asl_msg_set_key_val(msg, ASL_KEY_SENDER, "syslogd");
+ asl_msg_set_key_val(msg, ASL_KEY_FACILITY, "com.apple.asl.statistics");
+ snprintf(val, sizeof(val), "%d", global.pid);
+ asl_msg_set_key_val(msg, ASL_KEY_PID, val);
+ snprintf(val, sizeof(val), "%d", ASL_STATS_LEVEL);
+ asl_msg_set_key_val(msg, ASL_KEY_LEVEL, val);
+ snprintf(val, sizeof(val), "%u", t->mcount);
+ asl_msg_set_key_val(msg, "MsgCount", val);
+ snprintf(val, sizeof(val), "%u", t->shim_count);
+ asl_msg_set_key_val(msg, "ShimCount", val);
+
+ for (i = 0; i < t->bucket_count; i++)
{
- if ((outid != NULL) && (strcmp(i->outid, outid) == 0))
+ sender_stats_t *s;
+ s = t->bucket[i];
+ while (s != NULL)
{
- status = i->sendmsg(msg, outid);
+ char val[64], *key = NULL;
+ sender_stats_t *n = s->next;
+
+ snprintf(val, sizeof(val), "%llu %llu", s->count, s->size);
+ asprintf(&key, "*%s", s->sender);
+ if (key != NULL) asl_msg_set_key_val(msg, key, val);
+ free(key);
+ free(s->sender);
+ free(s);
+ s = n;
}
}
- return status;
+ free(t->bucket);
+ free(t);
+
+ return msg;
}
-int
-aslevent_addmatch(asl_msg_t *query, char *outid)
+static void
+stats_table_update(stats_table_t *t, const char *sender, uint64_t msg_size)
{
- struct aslmatch *tmp;
+ uint32_t i;
+ sender_stats_t *s;
+ uint8_t *p;
- if (query == NULL) return -1;
- if (outid == NULL) return -1;
+ if (t == NULL) return;
+ if (sender == NULL) return;
- tmp = calloc(1, sizeof(struct aslmatch));
- if (tmp == NULL) return -1;
- tmp->query = query;
- tmp->outid = outid;
- TAILQ_INSERT_TAIL(&Matchq, tmp, entries);
+ /* hash */
+ i = 0;
+ for (p = (uint8_t *)sender; *p != '\0'; p++) i = (i << 1) ^ (i ^ *p);
+ i %= STATS_TABLE_SIZE;
- return 0;
+ for (s = t->bucket[i]; s != NULL; s = s->next)
+ {
+ if ((s->sender != NULL) && (strcmp(sender, s->sender) == 0))
+ {
+ s->count++;
+ s->size += msg_size;
+ return;
+ }
+ }
+
+ s = (sender_stats_t *)malloc(sizeof(sender_stats_t));
+ s->sender = strdup(sender);
+ if (s->sender == NULL)
+ {
+ free(s);
+ return;
+ }
+
+ s->count = 1;
+ s->size = msg_size;
+ s->next = t->bucket[i];
+ t->bucket[i] = s;
}
-void
-aslevent_match(asl_msg_t *msg)
+static uint32_t
+kern_quota_check(time_t now, asl_msg_t *msg, uint32_t level)
{
- struct aslmatch *i;
+ char *str, lstr[2];
- if (msg == NULL) return;
+ if (msg == NULL) return VERIFY_STATUS_INVALID_MESSAGE;
+ if (global.mps_limit == 0) return VERIFY_STATUS_OK;
- for (i = Matchq.tqh_first; i != NULL; i = i->entries.tqe_next)
+ if (quota_time != now)
{
- if (asl_msg_cmp(i->query, msg) != 0)
- {
- aslevent_log(msg, i->outid);
- }
+ kern_quota = global.mps_limit;
+ kern_level = 7;
+ quota_time = now;
+ }
+
+ if (level < kern_level) kern_level = level;
+ if (kern_quota > 0) kern_quota--;
+
+ if (kern_quota > 0) return VERIFY_STATUS_OK;
+ if (kern_quota < 0) return VERIFY_STATUS_EXCEEDED_QUOTA;
+
+ kern_quota = -1;
+
+ str = NULL;
+ asprintf(&str, QUOTA_KERN_EXCEEDED_MESSAGE, global.mps_limit);
+ if (str != NULL)
+ {
+ asl_msg_set_key_val(msg, ASL_KEY_MSG, str);
+ free(str);
+ lstr[0] = kern_level + '0';
+ lstr[1] = 0;
+ asl_msg_set_key_val(msg, ASL_KEY_LEVEL, lstr);
}
+
+ return VERIFY_STATUS_OK;
}
-int
-aslevent_removefd(int fd)
+static void
+stats_msg(const char *sender, time_t now, asl_msg_t *msg)
{
- struct aslevent *i;
- int found = -1;
+ asl_msg_t *x;
+ uint64_t msize = 0;
- for (i = Eventq.tqh_first; i != NULL; i = i->entries.tqe_next)
+ /* flush stats after N seconds */
+ if ((global.stats_interval != 0) && ((now - global.stats_last) >= global.stats_interval) && (global.stats != NULL))
{
- if (fd == i->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;
- return 0;
- }
+ asl_msg_t *msg = stats_table_final(global.stats);
+ process_message(msg, SOURCE_INTERNAL);
+ global.stats = NULL;
+ global.stats_last = now;
}
- return -1;
+ if (global.stats == NULL) global.stats = stats_table_new();
+
+ for (x = msg; x != NULL; x = x->next) msize += x->mem_size;
+
+ const char *shim_vers = asl_msg_get_val_for_key(msg, "ASLSHIM");
+ global.stats->mcount++;
+ if (shim_vers != NULL) global.stats->shim_count++;
+
+ /* update count and total size - total and for this sender */
+ stats_table_update(global.stats, "*", msize);
+ stats_table_update(global.stats, sender, msize);
}
-const char *
+static const char *
whatsmyhostname()
{
- char *dot;
+ static dispatch_once_t once;
+ int check, status;
+
+ if (global.hostname != NULL) return global.hostname;
+
+ dispatch_once(&once, ^{
+ snprintf(myname, sizeof(myname), "%s", "localhost");
+ notify_register_check(kNotifySCHostNameChange, &name_change_token);
+ });
- if (gotname != 0) return (const char *)myname;
+ check = 1;
+ status = 0;
+
+ if (name_change_token >= 0) status = notify_check(name_change_token, &check);
+
+ if ((status == 0) && (check == 0)) return (const char *)myname;
if (gethostname(myname, MAXHOSTNAMELEN) < 0)
{
- memset(myname, 0, sizeof(myname));
- return "localhost";
+ snprintf(myname, sizeof(myname), "%s", "localhost");
+ }
+ else
+ {
+ char *dot;
+ dot = strchr(myname, '.');
+ if (dot != NULL) *dot = '\0';
}
- if (strcmp(myname, "localhost")) gotname = 1;
+ return (const char *)myname;
+}
- dot = strchr(myname, '.');
- if (dot != NULL) *dot = '\0';
+void
+asl_client_count_increment()
+{
+ OSSpinLockLock(&count_lock);
- return (const char *)myname;
+#if !TARGET_OS_EMBEDDED
+ if (global.client_count == 0) vproc_trans = vproc_transaction_begin(NULL);
+#endif
+ global.client_count++;
+
+ OSSpinLockUnlock(&count_lock);
}
-int
-aslevent_addfd(int fd, aslreadfn readfn, aslwritefn writefn, aslexceptfn exceptfn)
+void
+asl_client_count_decrement()
{
- struct aslevent *e;
- int found = 0;
-#ifdef LOCAL_PEERCRED
- struct xucred cr;
+ OSSpinLockLock(&count_lock);
+
+ if (global.client_count > 0) global.client_count--;
+#if !TARGET_OS_EMBEDDED
+ if (global.client_count == 0) vproc_transaction_end(NULL, vproc_trans);
#endif
- int len;
- uid_t u;
- gid_t g;
- struct sockaddr_storage ss;
- char *sender, str[256];
- u = 99;
- g = 99;
- sender = NULL;
+ OSSpinLockUnlock(&count_lock);
+}
+
+/*
+ * 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
+ */
- memset(&ss, 0, sizeof(struct sockaddr_storage));
+static uint32_t
+aslmsg_verify(asl_msg_t *msg, uint32_t source, int32_t *kern_post_level, uid_t *uid_out)
+{
+ const char *val, *fac, *ruval, *rgval, *sval = NULL;
+ char buf[64];
+ time_t tick, now;
+ uid_t uid;
+ gid_t gid;
+ uint32_t status, level, fnum;
+ pid_t pid;
+ uuid_string_t ustr;
+ struct proc_uniqidentifierinfo pinfo;
+
+ if (msg == NULL) return VERIFY_STATUS_INVALID_MESSAGE;
+
+ /* Time */
+ now = time(NULL);
+
+ if (kern_post_level != NULL) *kern_post_level = -1;
+ if (uid_out != NULL) *uid_out = -2;
+
+ /* PID */
+ pid = 0;
- len = sizeof(struct sockaddr_storage);
+ val = asl_msg_get_val_for_key(msg, ASL_KEY_PID);
+ if (val == NULL) asl_msg_set_key_val(msg, ASL_KEY_PID, "0");
+ else pid = (pid_t)atoi(val);
- if (getpeername(fd, (struct sockaddr *)&ss, &len) == 0)
+ /* if PID is 1 (launchd), use the RefPID and RefProc provided */
+ if (pid == 1)
{
- if (len == 0)
+ val = asl_msg_get_val_for_key(msg, ASL_KEY_REF_PID);
+ if (val != NULL) pid = (pid_t)atoi(val);
+
+ sval = asl_msg_get_val_for_key(msg, ASL_KEY_REF_PROC);
+ }
+
+ /* Level */
+ val = asl_msg_get_val_for_key(msg, ASL_KEY_LEVEL);
+ level = ASL_LEVEL_DEBUG;
+ if (source == SOURCE_KERN) level = ASL_LEVEL_NOTICE;
+
+ if ((val != NULL) && (val[1] == '\0') && (val[0] >= '0') && (val[0] <= '7')) level = val[0] - '0';
+ snprintf(buf, sizeof(buf), "%d", level);
+ asl_msg_set_key_val(msg, ASL_KEY_LEVEL, buf);
+
+
+ /* check kernel quota if enabled and no processes are watching */
+ if ((pid == 0) && (global.mps_limit > 0) && (global.watchers_active == 0))
+ {
+ status = kern_quota_check(now, msg, level);
+ if (status != VERIFY_STATUS_OK) return status;
+ }
+
+ if (pid != 0)
+ {
+ /* set Sender_Mach_UUID */
+ uuid_clear(pinfo.p_uuid);
+ if (proc_pidinfo(pid, PROC_PIDUNIQIDENTIFIERINFO, 1, &pinfo, sizeof(pinfo)) == sizeof(pinfo))
{
- /* UNIX Domain socket */
- snprintf(str, sizeof(str), whatsmyhostname());
- sender = str;
+ uuid_unparse(pinfo.p_uuid, ustr);
+ asl_msg_set_key_val(msg, ASL_KEY_SENDER_MACH_UUID, ustr);
}
- else
+ }
+
+ tick = 0;
+ val = asl_msg_get_val_for_key(msg, ASL_KEY_TIME);
+ if (val != NULL) tick = asl_core_parse_time(val, NULL);
+
+ /* Set time to now if it is unset or from the future (not allowed!) */
+ if ((tick == 0) || (tick > now)) tick = now;
+
+ /* Canonical form: seconds since the epoch */
+ snprintf(buf, sizeof(buf) - 1, "%llu", (unsigned long long) tick);
+ asl_msg_set_key_val(msg, ASL_KEY_TIME, buf);
+
+ /* Host */
+ val = asl_msg_get_val_for_key(msg, ASL_KEY_HOST);
+ if (val == NULL) asl_msg_set_key_val(msg, ASL_KEY_HOST, whatsmyhostname());
+
+ /* UID & GID */
+ uid = -2;
+ val = asl_msg_get_val_for_key(msg, ASL_KEY_UID);
+ if (val == NULL)
+ {
+ asl_msg_set_key_val(msg, ASL_KEY_UID, "-2");
+ }
+ else
+ {
+ uid = atoi(val);
+ if ((uid == 0) && strcmp(val, "0"))
{
- if (inet_ntop(ss.ss_family, (struct sockaddr *)&ss, str, 256) == 0) sender = str;
+ uid = -2;
+ asl_msg_set_key_val(msg, ASL_KEY_UID, "-2");
}
}
-#ifdef LOCAL_PEERCRED
- len = sizeof(cr);
+ gid = -2;
+ val = asl_msg_get_val_for_key(msg, ASL_KEY_GID);
+ if (val == NULL)
+ {
+ asl_msg_set_key_val(msg, ASL_KEY_GID, "-2");
+ }
+ else
+ {
+ gid = atoi(val);
+ if ((gid == 0) && strcmp(val, "0"))
+ {
+ gid = -2;
+ asl_msg_set_key_val(msg, ASL_KEY_GID, "-2");
+ }
+ }
- if (getsockopt(fd, LOCAL_PEERCRED, 1, &cr, &len) == 0)
+ switch (source)
{
- u = cr.cr_uid;
- g = cr.cr_gid;
+ case SOURCE_KERN:
+ case SOURCE_INTERNAL:
+ {
+ uid = 0;
+ asl_msg_set_key_val(msg, ASL_KEY_UID, "0");
+
+ gid = 0;
+ asl_msg_set_key_val(msg, ASL_KEY_GID, "0");
+
+ break;
+ }
+ case SOURCE_ASL_MESSAGE:
+ case SOURCE_LAUNCHD:
+ {
+ break;
+ }
+ default:
+ {
+ /* do not trust the UID 0 or GID 0 or 80 in SOURCE_BSD_SOCKET or SOURCE_UNKNOWN */
+ if (uid == 0)
+ {
+ uid = -2;
+ asl_msg_set_key_val(msg, ASL_KEY_UID, "-2");
+ }
+
+ if ((gid == 0) || (gid == 80))
+ {
+ gid = -2;
+ asl_msg_set_key_val(msg, ASL_KEY_GID, "-2");
+ }
+ }
}
-#endif
- asldebug("fd %d UID %d GID %d Sender %s\n", fd, u, g, (sender == NULL) ? "NULL" : sender );
+ if (uid_out != NULL) *uid_out = uid;
- for (e = Eventq.tqh_first; e != NULL; e = e->entries.tqe_next)
+ /* Sender */
+ if (sval == NULL) sval = asl_msg_get_val_for_key(msg, ASL_KEY_SENDER);
+ if (sval == NULL)
{
- if (fd == e->fd)
+ switch (source)
{
- e->readfn = readfn;
- e->writefn = writefn;
- e->exceptfn = exceptfn;
- if (e->sender != NULL) free(e->sender);
- e->sender = NULL;
- if (sender != NULL) e->sender = strdup(sender);
- e->uid = u;
- e->gid = g;
- found = 1;
+ case SOURCE_KERN:
+ {
+ sval = "kernel";
+ asl_msg_set_key_val(msg, ASL_KEY_SENDER, sval);
+ break;
+ }
+ case SOURCE_INTERNAL:
+ {
+ sval = "syslogd";
+ asl_msg_set_key_val(msg, ASL_KEY_SENDER, sval);
+ break;
+ }
+ default:
+ {
+ asl_msg_set_key_val(msg, ASL_KEY_SENDER, "Unknown");
+ }
}
}
+ else if ((source != SOURCE_KERN) && (uid != 0) && (!strcmp(sval, "kernel")))
+ {
+ /* allow UID 0 to send messages with "Sender kernel", but nobody else */
+ asl_msg_set_key_val(msg, ASL_KEY_SENDER, "Unknown");
+ sval = NULL;
+ }
- if (found) return 0;
+ /* Facility */
+ fac = asl_msg_get_val_for_key(msg, ASL_KEY_FACILITY);
+ if (fac == NULL)
+ {
+ if (source == SOURCE_KERN) fac = "kern";
+ else fac = "user";
+ asl_msg_set_key_val(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;
+ }
- e = calloc(1, sizeof(struct aslevent));
- if (e == NULL) return -1;
+ fac = asl_syslog_faciliy_num_to_name(fnum);
+ asl_msg_set_key_val(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_msg_set_key_val(msg, ASL_KEY_FACILITY, FACILITY_USER);
+ }
- e->fd = fd;
- e->readfn = readfn;
- e->writefn = writefn;
- e->exceptfn = exceptfn;
- e->sender = NULL;
- if (sender != NULL) e->sender = strdup(sender);
- e->uid = u;
- e->gid = g;
+ /*
+ * kernel messages are only readable by root and admin group.
+ * all other messages are admin-only readable unless they already
+ * have specific read access controls set.
+ */
+ if (source == SOURCE_KERN)
+ {
+ asl_msg_set_key_val(msg, ASL_KEY_READ_UID, "0");
+ asl_msg_set_key_val(msg, ASL_KEY_READ_GID, "80");
+ }
+ else
+ {
+ ruval = asl_msg_get_val_for_key(msg, ASL_KEY_READ_UID);
+ rgval = asl_msg_get_val_for_key(msg, ASL_KEY_READ_GID);
- TAILQ_INSERT_TAIL(&Eventq, e, entries);
+ if ((ruval == NULL) && (rgval == NULL))
+ {
+ asl_msg_set_key_val(msg, ASL_KEY_READ_GID, "80");
+ }
+ }
- return 0;
+ /* 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), "%llu", (unsigned long long) tick + global.utmp_ttl);
+ asl_msg_set_key_val(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);
+ }
+
+ /*
+ * gather sender stats
+ */
+ if (source != SOURCE_INTERNAL) stats_msg(sval, now, msg);
+
+ return VERIFY_STATUS_OK;
}
-int
-aslmsg_verify(struct aslevent *e, asl_msg_t *msg)
+void
+list_append_msg(asl_msg_list_t *list, asl_msg_t *msg)
{
- const char *val;
- char buf[32], *timestr;
- time_t tick;
- struct tm gtime;
+ if (list == NULL) return;
+ if (msg == NULL) return;
- if (msg == NULL) return -1;
+ /*
+ * 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 *));
+ }
- /* Time */
- tick = 0;
- val = asl_get(msg, ASL_KEY_TIME);
- if (val != NULL) tick = asl_parse_time(val);
+ if (list->msg == NULL)
+ {
+ list->curr = 0;
+ list->count = 0;
+ return;
+ }
+
+ list->curr += LIST_SIZE_DELTA;
+ }
+
+ list->msg[list->count] = (asl_msg_t *)msg;
+ list->count++;
+}
+
+void
+init_globals(void)
+{
+ asl_out_rule_t *r;
+
+ OSSpinLockLock(&global.lock);
- if (tick == 0) tick = time(NULL);
- memset(>ime, 0, sizeof(struct tm));
- gmtime_r(&tick, >ime);
+ global.pid = getpid();
+ global.debug = 0;
+ free(global.debug_file);
+ global.debug_file = NULL;
+ global.launchd_enabled = 1;
- /* 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)
+#if TARGET_OS_EMBEDDED
+ global.dbtype = DB_TYPE_MEMORY;
+#else
+ global.dbtype = DB_TYPE_FILE;
+#endif
+ global.db_file_max = DEFAULT_DB_FILE_MAX;
+ global.db_memory_max = DEFAULT_DB_MEMORY_MAX;
+ global.db_memory_str_max = DEFAULT_DB_MEMORY_STR_MAX;
+ global.mps_limit = DEFAULT_MPS_LIMIT;
+ global.remote_delay_time = DEFAULT_REMOTE_DELAY;
+ global.bsd_max_dup_time = DEFAULT_BSD_MAX_DUP_SEC;
+ global.mark_time = DEFAULT_MARK_SEC;
+ global.utmp_ttl = DEFAULT_UTMP_TTL_SEC;
+ global.memory_max = DEFAULT_MEMORY_MAX;
+ global.stats_interval = DEFAULT_STATS_INTERVAL;
+
+ global.asl_out_module = asl_out_module_init();
+ OSSpinLockUnlock(&global.lock);
+
+ if (global.asl_out_module != NULL)
{
- asl_set(msg, ASL_KEY_TIME, timestr);
- free(timestr);
+ for (r = global.asl_out_module->ruleset; r != NULL; r = r->next)
+ {
+ if ((r->action == ACTION_SET_PARAM) && (r->query == NULL) && (!strncmp(r->options, "debug", 5))) control_set_param(r->options, true);
+ }
}
+}
- /* Host */
- if (e == NULL) asl_set(msg, ASL_KEY_HOST, whatsmyhostname());
- else if (e->sender != NULL) asl_set(msg, ASL_KEY_HOST, e->sender);
+/*
+ * Used to set config parameters.
+ * Line format "= name value"
+ */
+int
+control_set_param(const char *s, bool eval)
+{
+ char **l;
+ uint32_t intval, count, v32a, v32b, v32c;
- /* Sender */
- val = asl_get(msg, ASL_KEY_SENDER);
- if (val == NULL) asl_set(msg, ASL_KEY_SENDER, "Unknown");
+ if (s == NULL) return -1;
+ if (s[0] == '\0') return 0;
- /* PID */
- val = asl_get(msg, ASL_KEY_PID);
- if (val == NULL) asl_set(msg, ASL_KEY_PID, "0");
+ /* skip '=' and whitespace */
+ if (*s == '=') s++;
+ while ((*s == ' ') || (*s == '\t')) s++;
- /* UID */
- val = asl_get(msg, ASL_KEY_UID);
- if (val == NULL)
+ l = explode(s, " \t");
+ if (l == NULL) return -1;
+
+ for (count = 0; l[count] != NULL; count++);
+
+ /* name is required */
+ if (count == 0)
{
- if (e == NULL) asl_set(msg, ASL_KEY_UID, "-2");
- else if (e->uid == 99) asl_set(msg, ASL_KEY_UID, "-2");
+ free_string_list(l);
+ return -1;
+ }
+
+ /* Check variables that allow 0 or 1 / boolean */
+ if (!strcasecmp(l[0], "debug"))
+ {
+ /* = debug [0|1] [file] */
+ if (count == 1)
+ {
+ intval = (eval) ? 1 : 0;
+ config_debug(intval, NULL);
+ }
+ else if (!strcmp(l[1], "0"))
+ {
+ config_debug(0, l[2]);
+ }
+ else if (!strcmp(l[1], "1"))
+ {
+ config_debug(1, l[2]);
+ }
else
{
- snprintf(buf, sizeof(buf), "%d", e->uid);
- asl_set(msg, ASL_KEY_UID, buf);
+ intval = (eval) ? 1 : 0;
+ config_debug(intval, l[1]);
}
+
+ free_string_list(l);
+ return 0;
}
- else if ((e != NULL) && (e->uid != 99))
+
+ /* value is required */
+ if (count == 1)
{
- snprintf(buf, sizeof(buf), "%d", e->uid);
- asl_set(msg, ASL_KEY_UID, buf);
+ free_string_list(l);
+ return -1;
}
- /* GID */
- val = asl_get(msg, ASL_KEY_GID);
- if (val == NULL)
+ if (!strcasecmp(l[0], "hostname"))
{
- if (e == NULL) asl_set(msg, ASL_KEY_GID, "-2");
- else if (e->gid == 99) asl_set(msg, ASL_KEY_GID, "-2");
+ /* = hostname name */
+ OSSpinLockLock(&global.lock);
+ if (eval)
+ {
+ global.hostname = strdup(l[1]);
+ }
else
{
- snprintf(buf, sizeof(buf), "%d", e->gid);
- asl_set(msg, ASL_KEY_GID, buf);
+ free(global.hostname);
+ global.hostname = NULL;
}
+ OSSpinLockUnlock(&global.lock);
+ }
+ else if (!strcasecmp(l[0], "mark_time"))
+ {
+ /* = mark_time seconds */
+ OSSpinLockLock(&global.lock);
+ if (eval) global.mark_time = atoll(l[1]);
+ else global.mark_time = 0;
+ OSSpinLockUnlock(&global.lock);
+ }
+ else if (!strcasecmp(l[0], "dup_delay"))
+ {
+ /* = bsd_max_dup_time seconds */
+ OSSpinLockLock(&global.lock);
+ if (eval) global.bsd_max_dup_time = atoll(l[1]);
+ else global.bsd_max_dup_time = DEFAULT_BSD_MAX_DUP_SEC;
+ OSSpinLockUnlock(&global.lock);
+ }
+ else if (!strcasecmp(l[0], "remote_delay"))
+ {
+ /* = remote_delay microseconds */
+ OSSpinLockLock(&global.lock);
+ if (eval) global.remote_delay_time = atol(l[1]);
+ else global.remote_delay_time = DEFAULT_REMOTE_DELAY;
+ OSSpinLockUnlock(&global.lock);
}
- else if ((e != NULL) && (e->gid != 99))
+ else if (!strcasecmp(l[0], "utmp_ttl"))
{
- snprintf(buf, sizeof(buf), "%d", e->gid);
- asl_set(msg, ASL_KEY_GID, buf);
+ /* = utmp_ttl seconds */
+ OSSpinLockLock(&global.lock);
+ if (eval) global.utmp_ttl = (time_t)atoll(l[1]);
+ else global.utmp_ttl = DEFAULT_UTMP_TTL_SEC;
+ OSSpinLockUnlock(&global.lock);
}
+ else if (!strcasecmp(l[0], "mps_limit"))
+ {
+ /* = mps_limit number */
+ OSSpinLockLock(&global.lock);
+ if (eval) global.mps_limit = (uint32_t)atol(l[1]);
+ else global.mps_limit = DEFAULT_MPS_LIMIT;
+ OSSpinLockUnlock(&global.lock);
+ }
+ else if (!strcasecmp(l[0], "memory_max"))
+ {
+ /* = memory_max number */
+ OSSpinLockLock(&global.lock);
+ if (eval) global.memory_max = (int64_t)atoll(l[1]);
+ else global.memory_max = DEFAULT_MEMORY_MAX;
+ OSSpinLockUnlock(&global.lock);
+ }
+ else if (!strcasecmp(l[0], "stats_interval"))
+ {
+ /* = stats_interval number */
+ OSSpinLockLock(&global.lock);
+ if (eval) global.stats_interval = (time_t)atoll(l[1]);
+ else global.stats_interval = DEFAULT_STATS_INTERVAL;
+ OSSpinLockUnlock(&global.lock);
+ }
+ else if (!strcasecmp(l[0], "max_file_size"))
+ {
+ /* = max_file_size bytes */
+ pthread_mutex_lock(global.db_lock);
- /* Level */
- val = asl_get(msg, ASL_KEY_LEVEL);
- if (val == NULL) asl_set(msg, ASL_KEY_LEVEL, "7");
+ if (global.dbtype & DB_TYPE_FILE)
+ {
+ asl_store_close(global.file_db);
+ global.file_db = NULL;
+ if (eval) global.db_file_max = atoi(l[1]);
+ else global.db_file_max = DEFAULT_DB_FILE_MAX;
+ }
+
+ pthread_mutex_unlock(global.db_lock);
+ }
+ else if ((!strcasecmp(l[0], "db")) || (!strcasecmp(l[0], "database")) || (!strcasecmp(l[0], "datastore")))
+ {
+ /* NB this is private / unpublished */
+ /* = db type [max]... */
+ if (eval)
+ {
+ v32a = 0;
+ v32b = 0;
+ v32c = 0;
+
+ if ((l[1][0] >= '0') && (l[1][0] <= '9'))
+ {
+ intval = atoi(l[1]);
+ if ((count >= 3) && (strcmp(l[2], "-"))) v32a = atoi(l[2]);
+ if ((count >= 4) && (strcmp(l[3], "-"))) v32b = atoi(l[3]);
+ if ((count >= 5) && (strcmp(l[4], "-"))) v32c = atoi(l[4]);
+ }
+ else if (!strcasecmp(l[1], "file"))
+ {
+ intval = DB_TYPE_FILE;
+ if ((count >= 3) && (strcmp(l[2], "-"))) v32a = atoi(l[2]);
+ }
+ else if (!strncasecmp(l[1], "mem", 3))
+ {
+ intval = DB_TYPE_MEMORY;
+ if ((count >= 3) && (strcmp(l[2], "-"))) v32b = atoi(l[2]);
+ }
+ else
+ {
+ free_string_list(l);
+ return -1;
+ }
+
+ if (v32a == 0) v32a = global.db_file_max;
+ if (v32b == 0) v32b = global.db_memory_max;
+ if (v32c == 0) v32c = global.db_memory_str_max;
+ config_data_store(intval, v32a, v32b, v32c);
+ }
+ else
+ {
+#if TARGET_OS_EMBEDDED
+ intval = DB_TYPE_MEMORY;
+#else
+ intval = DB_TYPE_FILE;
+#endif
+ config_data_store(intval, DEFAULT_DB_FILE_MAX, DEFAULT_DB_MEMORY_MAX, DEFAULT_DB_MEMORY_STR_MAX);
+ }
+ }
+
+ free_string_list(l);
return 0;
}
-int
-aslevent_addoutput(aslsendmsgfn fn, const char *outid)
+static int
+control_message(asl_msg_t *msg)
{
- struct asloutput *tmp;
-
- tmp = calloc(1, sizeof(struct asloutput));
- if (tmp == NULL) return -1;
+ const char *str = asl_msg_get_val_for_key(msg, ASL_KEY_MSG);
- tmp->sendmsg = fn;
- tmp->outid = outid;
+ if (str == NULL) return 0;
- TAILQ_INSERT_TAIL(&Outq, tmp, entries);
+ if (!strncmp(str, "= reset", 7))
+ {
+ init_globals();
+ return asl_action_reset();
+ }
+ else if (!strncmp(str, "= crash", 7))
+ {
+ abort();
+ }
+ else if (!strncmp(str, "@ ", 2))
+ {
+ return asl_action_control_set_param(str);
+ }
+ else if (!strncmp(str, "= ", 2))
+ {
+ return control_set_param(str, true);
+ }
return 0;
}
-int
-aslevent_fdsets(fd_set *rd, fd_set *wr, fd_set *ex)
+void
+process_message(asl_msg_t *msg, uint32_t source)
{
- struct aslevent *e;
- int status = 0;
+ int64_t msize = 0;
+ static bool wq_draining = false;
+ bool is_control = false;
+ asl_msg_t *x;
- asldebug("--> aslevent_fdsets\n");
- FD_ZERO(rd);
- FD_ZERO(wr);
- FD_ZERO(ex);
+ if (msg == NULL) return;
+
+ is_control = asl_check_option(msg, ASL_OPT_CONTROL) != 0;
- for (e = Eventq.tqh_first; e != NULL; e = e->entries.tqe_next)
+ if ((!is_control) && wq_draining)
{
- asldebug("adding fd %d\n", e->fd);
- if (e->readfn)
+ if (global.memory_size >= (global.memory_max / 2))
{
- FD_SET(e->fd, rd);
- status = MAX(e->fd, status);
+ asldebug("Work queue draining: dropped message.\n");
+ asl_msg_release(msg);
+ return;
}
-
- if (e->writefn)
+ else
{
- FD_SET(e->fd, wr);
- status = MAX(e->fd, status);
+ asldebug("Work queue re-enabled at 1/2 max. size %lld max %lld\n", global.memory_size, global.memory_max);
+ wq_draining = false;
}
+ }
- if (e->exceptfn)
- {
- FD_SET(e->fd, ex);
- status = MAX(e->fd, status);
- }
+ __block vproc_transaction_t vt = vproc_transaction_begin(NULL);
+
+ for (x = msg; x != NULL; x = x->next) msize += x->mem_size;
+
+ if ((global.memory_size + msize) >= global.memory_max)
+ {
+ char str[256];
+
+ wq_draining = true;
+ asl_msg_release(msg);
+
+ asldebug("Work queue disabled. msize %lld size %lld max %lld\n", msize, global.memory_size + msize, global.memory_max);
+ snprintf(str, sizeof(str), "[Sender syslogd] [Level 2] [PID %u] [Message Internal memory size limit %lld exceeded - dropping messages] [UID 0] [UID 0] [Facility syslog]", global.pid, global.memory_max);
+ msg = asl_msg_from_string(str);
}
- asldebug("<--aslevent_fdsets\n");
- return status;
-}
+ OSAtomicAdd64(msize, &global.memory_size);
+ OSAtomicIncrement32(&global.work_queue_count);
-void
-aslevent_handleevent(fd_set rd, fd_set wr, fd_set ex, char *errstr)
-{
- struct aslevent *e;
- char *out = NULL;
- asl_msg_t *msg;
+ dispatch_async(global.work_queue, ^{
+ int32_t kplevel;
+ uint32_t status;
+ uid_t uid;
- asldebug("--> aslevent_handleevent\n");
- if (errstr) errstr = NULL;
+ kplevel = -1;
+ uid = -2;
- for (e = Eventq.tqh_first; e != NULL; e = e->entries.tqe_next)
- {
- if (FD_ISSET(e->fd, &rd) && (e->readfn != NULL))
+ status = aslmsg_verify(msg, source, &kplevel, &uid);
+ if (status == VERIFY_STATUS_OK)
{
- asldebug("handling read event on %d\n", e->fd);
- msg = e->readfn(e->fd);
- if (msg == NULL)
+ if ((source == SOURCE_KERN) && (kplevel >= 0))
{
- asldebug("error reading message\n");
- continue;
+ if (kplevel > 7) kplevel = 7;
+ if (kern_notify_token[kplevel] < 0)
+ {
+ status = notify_register_plain(kern_notify_key[kplevel], &(kern_notify_token[kplevel]));
+ if (status != 0) asldebug("notify_register_plain(%s) failed status %u\n", status);
+ }
+
+ notify_post(kern_notify_key[kplevel]);
}
- if (aslmsg_verify(e, msg) < 0)
- {
- asl_free(msg);
- asldebug("recieved invalid message\n");
- }
- else
- {
- aslevent_match(msg);
- asl_free(msg);
- }
- }
+ if ((uid == 0) && is_control) control_message(msg);
- if (FD_ISSET(e->fd, &ex) && e->exceptfn)
+ /*
+ * Send message to output module chain (asl is first).
+ * The last module in the chain will decrement global.memory_size.
+ */
+ asl_out_message(msg, msize);
+ }
+ else
{
- asldebug("handling except event on %d\n", e->fd);
- out = e->exceptfn(e->fd);
- if (out == NULL) asldebug("error writing message\n");
+ OSAtomicAdd64(-1ll * msize, &global.memory_size);
}
- }
- asldebug("<-- aslevent_handleevent\n");
+ asl_msg_release(msg);
+
+ OSAtomicDecrement32(&global.work_queue_count);
+ vproc_transaction_end(NULL, vt);
+ });
}
int
-asl_log_string(const char *str)
+internal_log_message(const char *str)
{
asl_msg_t *msg;
if (str == NULL) return 1;
msg = asl_msg_from_string(str);
- if (aslmsg_verify(NULL, msg) < 0)
- {
- asl_free(msg);
- return -1;
- }
+ if (msg == NULL) return 1;
+
+ process_message(msg, SOURCE_INTERNAL);
+
+ return 0;
+}
+
+int
+asldebug(const char *str, ...)
+{
+ va_list v;
+ FILE *dfp = NULL;
+
+ if (global.debug == 0) return 0;
+
+ if (global.debug_file == NULL) dfp = fopen(_PATH_SYSLOGD_LOG, "a");
+ else dfp = fopen(global.debug_file, "a");
+ if (dfp == NULL) return 0;
+
+ va_start(v, str);
+ fprintf(dfp, "W %d %llu", global.work_queue_count, global.memory_size);
+ if (global.memory_db != NULL) fprintf(dfp, " M %u %u %lu", global.memory_db->record_count, global.memory_db->string_count, global.memory_db->curr_string_mem);
+ fprintf(dfp, " ; ");
+ vfprintf(dfp, str, v);
+ va_end(v);
+
+ fclose(dfp);
- aslevent_match(msg);
- asl_free(msg);
return 0;
}
void
-aslmark(void)
+asl_mark(void)
{
- char *str;
+ char *str = NULL;
- str = NULL;
- asprintf(&str, "[%s syslogd] [%s %u] [%s %u] [%s -- MARK --] [%s 0] [%s 0] [Facility syslog]",
- ASL_KEY_SENDER,
- ASL_KEY_LEVEL, ASL_LEVEL_INFO,
- ASL_KEY_PID, getpid(),
- ASL_KEY_MSG, ASL_KEY_UID, ASL_KEY_GID);
-
- asl_log_string(str);
- if (str != NULL) free(str);
+ asprintf(&str, "[Sender syslogd] [Level 6] [PID %u] [Message -- MARK --] [UID 0] [UID 0] [Facility syslog]", global.pid);
+ internal_log_message(str);
+ free(str);
}
asl_msg_t *
-asl_syslog_input_convert(const char *in, int len, char *rhost, int kern)
+asl_syslog_input_convert(const char *in, int len, char *rhost, uint32_t source)
{
int pf, pri, index, n;
- char *p, *colon, *brace, *tmp, *tval, *sval, *pval, *mval;
+ char *p, *colon, *brace, *space, *tmp, *tval, *hval, *sval, *pval, *mval;
char prival[8];
const char *fval;
asl_msg_t *msg;
if (in == NULL) return NULL;
if (len <= 0) return NULL;
- asldebug("asl_syslog_input_convert: %s\n", in);
-
pri = LOG_DEBUG;
+ if (source == SOURCE_KERN) pri = LOG_NOTICE;
+
tval = NULL;
+ hval = NULL;
sval = NULL;
pval = NULL;
mval = NULL;
index = 0;
p = (char *)in;
+ /* skip leading whitespace */
while ((index < len) && ((*p == ' ') || (*p == '\t')))
{
p++;
if (index >= len) return NULL;
+ /* parse "<NN>" priority (level and facility) */
if (*p == '<')
{
p++;
snprintf(prival, sizeof(prival), "%d", pri);
+ /* check if a timestamp is included */
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';
- tick = asl_parse_time(tmp);
+ tick = asl_core_parse_time(tmp, NULL);
if (tick == (time_t)-1)
{
tval = tmp;
index += 16;
}
- if (kern != 0)
+ /* stop here for kernel messages */
+ if (source == SOURCE_KERN)
{
- msg = (asl_msg_t *)calloc(1, sizeof(asl_msg_t));
- msg->type = ASL_TYPE_MSG;
-
- asl_set(msg, ASL_KEY_SENDER, "kernel");
-
- asl_set(msg, "Facility", "kern");
- if (tval != NULL)
- {
- asl_set(msg, ASL_KEY_TIME, tval);
- free(tval);
- }
+ msg = asl_msg_new(ASL_TYPE_MSG);
+ if (msg == NULL) return NULL;
- asl_set(msg, ASL_KEY_MSG, p);
+ asl_msg_set_key_val(msg, ASL_KEY_MSG, p);
+ asl_msg_set_key_val(msg, ASL_KEY_LEVEL, prival);
+ asl_msg_set_key_val(msg, ASL_KEY_PID, "0");
- asl_set(msg, ASL_KEY_LEVEL, prival);
-
- asl_set(msg, ASL_KEY_PID, "0");
-
- asl_set(msg, ASL_KEY_UID, "0");
+ return msg;
+ }
- asl_set(msg, ASL_KEY_GID, "0");
+ /* if message is from a network socket, hostname follows */
+ if (source == SOURCE_UDP_SOCKET)
+ {
+ space = strchr(p, ' ');
+ if (space != NULL)
+ {
+ n = space - p;
+ hval = malloc(n + 1);
+ if (hval == NULL) return NULL;
- asl_set(msg, ASL_KEY_HOST, whatsmyhostname());
+ memcpy(hval, p, n);
+ hval[n] = '\0';
- return msg;
+ p = space + 1;
+ index += (n + 1);
+ }
}
colon = strchr(p, ':');
brace = strchr(p, '[');
+ /* check for "sender:" or sender[pid]:" */
if (colon != NULL)
{
if ((brace != NULL) && (brace < colon))
{
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';
}
{
n = colon - p;
sval = malloc(n + 1);
+ if (sval == NULL) return NULL;
+
memcpy(sval, p, n);
sval[n] = '\0';
}
if (n > 0)
{
mval = malloc(n + 1);
+ if (mval == NULL) return NULL;
+
memcpy(mval, p, n);
mval[n] = '\0';
}
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;
+ msg = asl_msg_new(ASL_TYPE_MSG);
+ if (msg == NULL) return NULL;
if (tval != NULL)
{
- asl_set(msg, ASL_KEY_TIME, tval);
+ asl_msg_set_key_val(msg, ASL_KEY_TIME, tval);
free(tval);
}
- if (fval != NULL) asl_set(msg, "Facility", fval);
- else asl_set(msg, "Facility", "user");
+ if (fval != NULL) asl_msg_set_key_val(msg, "Facility", fval);
+ else asl_msg_set_key_val(msg, "Facility", "user");
if (sval != NULL)
{
- asl_set(msg, ASL_KEY_SENDER, sval);
+ asl_msg_set_key_val(msg, ASL_KEY_SENDER, sval);
free(sval);
}
if (pval != NULL)
{
- asl_set(msg, ASL_KEY_PID, pval);
+ asl_msg_set_key_val(msg, ASL_KEY_PID, pval);
free(pval);
}
- else asl_set(msg, ASL_KEY_PID, "-1");
+ else
+ {
+ asl_msg_set_key_val(msg, ASL_KEY_PID, "-1");
+ }
if (mval != NULL)
{
- asl_set(msg, ASL_KEY_MSG, mval);
+ asl_msg_set_key_val(msg, ASL_KEY_MSG, mval);
free(mval);
}
- asl_set(msg, ASL_KEY_LEVEL, prival);
- asl_set(msg, ASL_KEY_UID, "-2");
- asl_set(msg, ASL_KEY_GID, "-2");
+ asl_msg_set_key_val(msg, ASL_KEY_LEVEL, prival);
+ asl_msg_set_key_val(msg, ASL_KEY_UID, "-2");
+ asl_msg_set_key_val(msg, ASL_KEY_GID, "-2");
- if (rhost == NULL) asl_set(msg, ASL_KEY_HOST, whatsmyhostname());
- else asl_set(msg, ASL_KEY_HOST, rhost);
+ if (hval != NULL)
+ {
+ asl_msg_set_key_val(msg, ASL_KEY_HOST, hval);
+ free(hval);
+ }
+ else if (rhost != NULL)
+ {
+ asl_msg_set_key_val(msg, ASL_KEY_HOST, rhost);
+ }
- if (msg->count == 0)
+ return msg;
+}
+
+asl_msg_t *
+asl_input_parse(const char *in, int len, char *rhost, uint32_t source)
+{
+ asl_msg_t *msg;
+ int status, x, legacy, off;
+
+ asldebug("asl_input_parse: %s\n", (in == NULL) ? "NULL" : in);
+
+ if (in == NULL) return NULL;
+
+ legacy = 1;
+ msg = 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 may start with a length (10 bytes) followed by a space and a '['.
+ * The length is optional, so ASL messages may also just start with '['.
+ */
+ if ((in[0] != '<') && (len > 11))
{
- asl_free(msg);
- return NULL;
+ 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, source);
+
+ off = 11;
+ if (in[0] == '[') off = 0;
+
+ msg = asl_msg_from_string(in + off);
+ if (msg == NULL) return NULL;
+
+ if (rhost != NULL) asl_msg_set_key_val(msg, ASL_KEY_HOST, rhost);
+
return msg;
}
-char *
-get_line_from_file(FILE *f)
+#if !TARGET_IPHONE_SIMULATOR
+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)
{
- char *s, *out;
- size_t len;
+ asl_msg_t *m;
+ char str[256];
+ time_t now;
+
+ if (global.launchd_enabled == 0) return;
- out = fgetln(f, &len);
- if (out == NULL) return NULL;
- if (len == 0) return NULL;
+/*
+ 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);
+*/
- s = malloc(len + 1);
- memcpy(s, out, len);
+ m = asl_msg_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_msg_set_key_val(m, ASL_KEY_LEVEL, str);
+
+ /* Time */
+ if (when != NULL)
+ {
+ snprintf(str, sizeof(str), "%llu", (unsigned long long) when->tv_sec);
+ asl_msg_set_key_val(m, ASL_KEY_TIME, str);
- s[len - 1] = '\0';
- return s;
+ snprintf(str, sizeof(str), "%lu", 1000 * (unsigned long int)when->tv_usec);
+ asl_msg_set_key_val(m, ASL_KEY_TIME_NSEC, str);
+ }
+ else
+ {
+ now = time(NULL);
+ snprintf(str, sizeof(str), "%llu", (unsigned long long) now);
+ asl_msg_set_key_val(m, ASL_KEY_TIME, str);
+ }
+
+ /* Facility */
+ asl_msg_set_key_val(m, ASL_KEY_FACILITY, FACILITY_CONSOLE);
+
+ /* UID */
+ snprintf(str, sizeof(str), "%u", (unsigned int)sender_uid);
+ asl_msg_set_key_val(m, ASL_KEY_UID, str);
+
+ /* GID */
+ snprintf(str, sizeof(str), "%u", (unsigned int)sender_gid);
+ asl_msg_set_key_val(m, ASL_KEY_GID, str);
+
+ /* PID */
+ if (from_pid != 0)
+ {
+ snprintf(str, sizeof(str), "%u", (unsigned int)from_pid);
+ asl_msg_set_key_val(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_msg_set_key_val(m, ASL_KEY_REF_PID, str);
+ }
+
+ /* Sender */
+ if (from_name != NULL)
+ {
+ asl_msg_set_key_val(m, ASL_KEY_SENDER, from_name);
+ }
+
+ /* ReadUID */
+ if (sender_uid != 0)
+ {
+ snprintf(str, sizeof(str), "%d", (int)sender_uid);
+ asl_msg_set_key_val(m, ASL_KEY_READ_UID, str);
+ }
+
+ /* Reference Process */
+ if (about_name != NULL)
+ {
+ if ((from_name != NULL) && (strcmp(from_name, about_name) != 0))
+ {
+ asl_msg_set_key_val(m, ASL_KEY_REF_PROC, about_name);
+ }
+ }
+
+ /* Session */
+ if (session_name != NULL)
+ {
+ asl_msg_set_key_val(m, ASL_KEY_SESSION, session_name);
+ }
+
+ /* Message */
+ if (msg != NULL)
+ {
+ asl_msg_set_key_val(m, ASL_KEY_MSG, msg);
+ }
+
+ process_message(m, SOURCE_LAUNCHD);
}
+
+#endif