From 8158235332f5a3f4f20cebe26bf739b481ca2df5 Mon Sep 17 00:00:00 2001 From: Apple Date: Fri, 19 Jul 2013 21:58:49 +0000 Subject: [PATCH] syslog-217.1.4.tar.gz --- aslcommon/asl_common.c | 2352 ++++++++++++++++ aslcommon/asl_common.h | 178 ++ aslcommon/asl_ipc.defs | 112 + aslcommon/asl_memory.c | 57 +- aslcommon/asl_memory.h | 2 + aslcommon/asl_mini_memory.c | 133 +- aslcommon/asl_mini_memory.h | 10 +- aslmanager.tproj/aslmanager.8 | 164 +- aslmanager.tproj/aslmanager.c | 1232 ++++++--- aslmanager.tproj/com.apple.aslmanager.plist | 27 +- libsystem_asl.tproj/include/asl.h | 651 +++++ libsystem_asl.tproj/include/asl_core.h | 114 + libsystem_asl.tproj/include/asl_file.h | 150 + libsystem_asl.tproj/include/asl_legacy1.h | 85 + libsystem_asl.tproj/include/asl_msg.h | 130 + libsystem_asl.tproj/include/asl_private.h | 101 + libsystem_asl.tproj/include/asl_store.h | 92 + libsystem_asl.tproj/man/asl.3 | 1419 ++++++++++ libsystem_asl.tproj/man/syslog.3 | 323 +++ libsystem_asl.tproj/src/asl.c | 2288 +++++++++++++++ libsystem_asl.tproj/src/asl_core.c | 900 ++++++ libsystem_asl.tproj/src/asl_fd.c | 297 ++ libsystem_asl.tproj/src/asl_file.c | 2167 +++++++++++++++ libsystem_asl.tproj/src/asl_legacy1.c | 836 ++++++ libsystem_asl.tproj/src/asl_msg.c | 2752 +++++++++++++++++++ libsystem_asl.tproj/src/asl_store.c | 945 +++++++ libsystem_asl.tproj/src/asl_util.c | 272 ++ libsystem_asl.tproj/src/syslog.c | 223 ++ syslog.xcodeproj/project.pbxproj | 433 ++- syslogd.tproj/after_install.sh | 29 +- syslogd.tproj/asl.conf.5 | 849 ++++-- syslogd.tproj/asl_action.c | 2548 +++++++++-------- syslogd.tproj/asl_sim.conf | 34 + syslogd.tproj/bb_convert.c | 4 + syslogd.tproj/bsd_in.c | 10 +- syslogd.tproj/bsd_out.c | 16 +- syslogd.tproj/com.apple.syslogd.plist | 9 +- syslogd.tproj/com.apple.syslogd_sim.plist | 38 + syslogd.tproj/daemon.c | 571 ++-- syslogd.tproj/daemon.h | 42 +- syslogd.tproj/dbserver.c | 577 +++- syslogd.tproj/klog_in.c | 10 +- syslogd.tproj/remote.c | 70 +- syslogd.tproj/syslogd.8 | 12 +- syslogd.tproj/syslogd.c | 167 +- syslogd.tproj/udp_in.c | 10 +- util.tproj/entitlements.plist | 8 + util.tproj/syslog.1 | 131 +- util.tproj/syslog.c | 280 +- xcodeconfig/libasl.xcconfig | 52 + 50 files changed, 21642 insertions(+), 2270 deletions(-) create mode 100644 aslcommon/asl_common.c create mode 100644 aslcommon/asl_common.h create mode 100644 aslcommon/asl_ipc.defs create mode 100644 libsystem_asl.tproj/include/asl.h create mode 100644 libsystem_asl.tproj/include/asl_core.h create mode 100644 libsystem_asl.tproj/include/asl_file.h create mode 100644 libsystem_asl.tproj/include/asl_legacy1.h create mode 100644 libsystem_asl.tproj/include/asl_msg.h create mode 100644 libsystem_asl.tproj/include/asl_private.h create mode 100644 libsystem_asl.tproj/include/asl_store.h create mode 100644 libsystem_asl.tproj/man/asl.3 create mode 100644 libsystem_asl.tproj/man/syslog.3 create mode 100644 libsystem_asl.tproj/src/asl.c create mode 100644 libsystem_asl.tproj/src/asl_core.c create mode 100644 libsystem_asl.tproj/src/asl_fd.c create mode 100644 libsystem_asl.tproj/src/asl_file.c create mode 100644 libsystem_asl.tproj/src/asl_legacy1.c create mode 100644 libsystem_asl.tproj/src/asl_msg.c create mode 100644 libsystem_asl.tproj/src/asl_store.c create mode 100644 libsystem_asl.tproj/src/asl_util.c create mode 100644 libsystem_asl.tproj/src/syslog.c create mode 100644 syslogd.tproj/asl_sim.conf create mode 100644 syslogd.tproj/com.apple.syslogd_sim.plist create mode 100644 util.tproj/entitlements.plist create mode 100644 xcodeconfig/libasl.xcconfig diff --git a/aslcommon/asl_common.c b/aslcommon/asl_common.c new file mode 100644 index 0000000..a4c21d3 --- /dev/null +++ b/aslcommon/asl_common.c @@ -0,0 +1,2352 @@ +/* + * Copyright (c) 2012 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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, 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "asl_common.h" + +#define _PATH_ASL_CONF "/etc/asl.conf" +#define _PATH_ASL_CONF_DIR "/etc/asl" + +#if !TARGET_IPHONE_SIMULATOR +#define _PATH_ASL_CONF_LOCAL_DIR "/usr/local/etc/asl" +#endif + +static const char *asl_out_action_name[] = +{ + "none ", + "set ", + "output ", + "ignore ", + "skip ", + "claim ", + "notify ", + "broadcast ", + "access ", + "store ", + "asl_file ", + "asl_dir ", + "file ", + "forward ", + "control ", + "set (file) ", + "set (plist) ", + "set (profile)" +}; + +static time_t start_today; + +extern asl_msg_t *asl_msg_from_string(const char *buf); + +#define forever for(;;) +#define KEYMATCH(S,K) ((strncasecmp(S, K, strlen(K)) == 0)) + +asl_msg_t * +xpc_object_to_asl_msg(xpc_object_t xobj) +{ + __block asl_msg_t *out; + + if (xobj == NULL) return NULL; + if (xpc_get_type(xobj) != XPC_TYPE_DICTIONARY) return NULL; + + out = asl_msg_new(ASL_TYPE_MSG); + xpc_dictionary_apply(xobj, ^bool(const char *key, xpc_object_t xval) { + char tmp[64]; + + if (xpc_get_type(xval) == XPC_TYPE_NULL) + { + asl_msg_set_key_val_op(out, key, NULL, 0); + } + else if (xpc_get_type(xval) == XPC_TYPE_BOOL) + { + if (xpc_bool_get_value(xval)) asl_msg_set_key_val_op(out, key, "1", 0); + else asl_msg_set_key_val_op(out, key, "0", 0); + } + else if (xpc_get_type(xval) == XPC_TYPE_INT64) + { + snprintf(tmp, sizeof(tmp), "%lld", xpc_int64_get_value(xval)); + asl_msg_set_key_val_op(out, key, tmp, 0); + } + else if (xpc_get_type(xval) == XPC_TYPE_UINT64) + { + snprintf(tmp, sizeof(tmp), "%llu", xpc_uint64_get_value(xval)); + asl_msg_set_key_val_op(out, key, tmp, 0); + } + else if (xpc_get_type(xval) == XPC_TYPE_DOUBLE) + { + snprintf(tmp, sizeof(tmp), "%f", xpc_double_get_value(xval)); + asl_msg_set_key_val_op(out, key, tmp, 0); + } + else if (xpc_get_type(xval) == XPC_TYPE_DATE) + { + snprintf(tmp, sizeof(tmp), "%lld", xpc_date_get_value(xval)); + asl_msg_set_key_val_op(out, key, tmp, 0); + } + else if (xpc_get_type(xval) == XPC_TYPE_DATA) + { + size_t len = xpc_data_get_length(xval); + char *encoded = asl_core_encode_buffer(xpc_data_get_bytes_ptr(xval), len); + asl_msg_set_key_val_op(out, key, encoded, 0); + free(encoded); + } + else if (xpc_get_type(xval) == XPC_TYPE_STRING) + { + asl_msg_set_key_val_op(out, key, xpc_string_get_string_ptr(xval), 0); + } + else if (xpc_get_type(xval) == XPC_TYPE_UUID) + { + uuid_string_t us; + uuid_unparse(xpc_uuid_get_bytes(xval), us); + asl_msg_set_key_val_op(out, key, us, 0); + } + else if (xpc_get_type(xval) == XPC_TYPE_FD) + { + /* XPC_TYPE_FD is not supported */ + asl_msg_set_key_val_op(out, key, "{XPC_TYPE_FD}", 0); + } + else if (xpc_get_type(xval) == XPC_TYPE_SHMEM) + { + /* XPC_TYPE_SHMEM is not supported */ + asl_msg_set_key_val_op(out, key, "{XPC_TYPE_SHMEM}", 0); + } + else if (xpc_get_type(xval) == XPC_TYPE_ARRAY) + { + /* XPC_TYPE_ARRAY is not supported */ + asl_msg_set_key_val_op(out, key, "{XPC_TYPE_ARRAY}", 0); + } + else if (xpc_get_type(xval) == XPC_TYPE_DICTIONARY) + { + /* XPC_TYPE_DICTIONARY is not supported */ + asl_msg_set_key_val_op(out, key, "{XPC_TYPE_DICTIONARY}", 0); + } + else if (xpc_get_type(xval) == XPC_TYPE_ERROR) + { + /* XPC_TYPE_ERROR is not supported */ + asl_msg_set_key_val_op(out, key, "{XPC_TYPE_ERROR}", 0); + } + else + { + /* UNKNOWN TYPE */ + asl_msg_set_key_val_op(out, key, "{XPC_TYPE_???}", 0); + } + + return true; + }); + + return out; +} + +asl_msg_t * +configuration_profile_to_asl_msg(const char *ident) +{ + xpc_object_t xobj = configuration_profile_copy_property_list(ident); + asl_msg_t *out = xpc_object_to_asl_msg(xobj); + if (xobj != NULL) xpc_release(xobj); + return out; +} + +/* strdup + skip leading and trailing whitespace */ +static char * +_strdup_clean(const char *s) +{ + char *out; + const char *first, *last; + size_t len; + + if (s == NULL) return NULL; + + first = s; + while ((*first == ' ') || (*first == '\t')) first++; + len = strlen(first); + if (len == 0) return NULL; + + last = first + len - 1; + while ((len > 0) && ((*last == ' ') || (*last == '\t'))) + { + last--; + len--; + } + + if (len == 0) return NULL; + + out = malloc(len + 1); + if (out == NULL) return NULL; + + memcpy(out, first, len); + out[len] = '\0'; + return out; +} + +static char ** +_insert_string(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 includes the NULL at the end of the list */ + len = i + 1; + + 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 = _insert_string(t, l, IndexNull); + free(t); + t = NULL; + if (p[i] == '\0') return l; + if (p[i + 1] == '\0') l = _insert_string("", l, IndexNull); + p = p + i + 1; + } + + return l; +} + +void +free_string_list(char **l) +{ + int i; + + if (l == NULL) return; + for (i = 0; l[i] != NULL; i++) free(l[i]); + free(l); +} + +char * +get_line_from_file(FILE *f) +{ + char *s, *out; + size_t len; + + out = fgetln(f, &len); + if (out == NULL) return NULL; + if (len == 0) return NULL; + + s = malloc(len + 1); + if (s == NULL) return NULL; + + memcpy(s, out, len); + + if (s[len - 1] != '\n') len++; + s[len - 1] = '\0'; + return s; +} + +char * +next_word_from_string(char **s) +{ + char *a, *p, *e, *out, s0; + int quote1, quote2, len; + + if (s == NULL) return NULL; + if (*s == NULL) return NULL; + + s0 = **s; + + quote1 = 0; + quote2 = 0; + + p = *s; + + /* allow whole word to be contained in quotes */ + if (*p == '\'') + { + quote1 = 1; + p++; + } + + if (*p == '"') + { + quote2 = 1; + p++; + } + + a = p; + e = p; + + while (*p != '\0') + { + if (*p == '\\') + { + p++; + e = p; + + if (*p == '\0') + { + p--; + break; + } + + p++; + e = p; + continue; + } + + if (*p == '\'') + { + if (quote1 == 0) quote1 = 1; + else quote1 = 0; + } + + if (*p == '"') + { + if (quote2 == 0) quote2 = 1; + else quote2 = 0; + } + + if (((*p == ' ') || (*p == '\t')) && (quote1 == 0) && (quote2 == 0)) + { + e = p + 1; + break; + } + + p++; + e = p; + } + + *s = e; + + len = p - a; + + /* check for quoted string */ + if (((s0 == '\'') || (s0 == '"')) && (s0 == a[len-1])) len--; + + if (len == 0) return NULL; + + out = malloc(len + 1); + if (out == NULL) return NULL; + + memcpy(out, a, len); + out[len] = '\0'; + return out; +} + +int +asl_out_mkpath(asl_out_rule_t *r) +{ + char tmp[MAXPATHLEN], *p; + struct stat sb; + int status; + + if (r == NULL) return -1; + if (r->dst == NULL) return -1; + if (r->dst->path == NULL) return -1; + + snprintf(tmp, sizeof(tmp), "%s", r->dst->path); + + if (r->action != ACTION_ASL_DIR) + { + p = strrchr(tmp, '/'); + if (p == NULL) return -1; + *p = '\0'; + } + + memset(&sb, 0, sizeof(struct stat)); + status = stat(tmp, &sb); + if (status == 0) + { + if (!S_ISDIR(sb.st_mode)) return -1; + } + else if (errno == ENOENT) + { + status = mkpath_np(tmp, 0755); + } + + return status; +} + +void +asl_make_timestamp(time_t stamp, uint32_t flags, char *buf, size_t len) +{ + struct tm t; + uint32_t h, m, s; + + if (buf == NULL) return; + + if (flags & MODULE_FLAG_STYLE_UTC) + { + memset(&t, 0, sizeof(t)); + gmtime_r(&stamp, &t); + snprintf(buf, len, "%d-%02d-%02dT%02d:%02d:%02dZ", t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec); + } + else if (flags & MODULE_FLAG_STYLE_UTC_B) + { + memset(&t, 0, sizeof(t)); + gmtime_r(&stamp, &t); + snprintf(buf, len, "%d%02d%02dT%02d%02d%02dZ", t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec); + } + else if (flags & MODULE_FLAG_STYLE_LCL) + { + bool neg = false; + memset(&t, 0, sizeof(t)); + localtime_r(&stamp, &t); + + if ((neg = (t.tm_gmtoff < 0))) t.tm_gmtoff *= -1; + + s = t.tm_gmtoff; + h = s / 3600; + s %= 3600; + m = s / 60; + s %= 60; + + if (s > 0) snprintf(buf, len, "%d-%02d-%02dT%02d:%02d:%02d%c%u:%02u:%02u", t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec, neg ? '-' : '+', h, m, s); + else if (m > 0) snprintf(buf, len, "%d-%02d-%02dT%02d:%02d:%02d%c%u:%02u", t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec, neg ? '-' : '+', h, m); + else snprintf(buf, len, "%d-%02d-%02dT%02d:%02d:%02d%c%u", t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec, neg ? '-' : '+', h); + } + else if (flags & MODULE_FLAG_STYLE_LCL_B) + { + bool neg = false; + memset(&t, 0, sizeof(t)); + localtime_r(&stamp, &t); + + if ((neg = (t.tm_gmtoff < 0))) t.tm_gmtoff *= -1; + + s = t.tm_gmtoff; + h = s / 3600; + s %= 3600; + m = s / 60; + s %= 60; + + if (s > 0) snprintf(buf, len, "%d%02d%02dT%02d%02d%02d%c%02u%02u%02u", t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec, neg ? '-' : '+', h, m, s); + else if (m > 0) snprintf(buf, len, "%d%02d%02dT%02d%02d%02d%c%02u%02u", t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec, neg ? '-' : '+', h, m); + else snprintf(buf, len, "%d%02d%02dT%02d%02d%02d%c%02u", t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec, neg ? '-' : '+', h); + } + else + { + snprintf(buf, len, "%c%lu", STYLE_SEC_PREFIX_CHAR, stamp); + } +} + +void +asl_make_dst_filename(asl_out_dst_data_t *dst, char *buf, size_t len) +{ + if (dst == NULL) return; + if (buf == NULL) return; + + if (dst->flags & MODULE_FLAG_BASESTAMP) + { + char tstamp[32]; + + if (dst->stamp == 0) dst->stamp = time(NULL); + asl_make_timestamp(dst->stamp, dst->flags, tstamp, sizeof(tstamp)); + snprintf(buf, len, "%s.%s", dst->path, tstamp); + } + else + { + snprintf(buf, len, "%s", dst->path); + } +} + +int +asl_out_dst_checkpoint(asl_out_dst_data_t *dst, uint32_t force) +{ + char newpath[MAXPATHLEN]; + time_t now; + + now = time(NULL); + + /* clock went backwards - force a reset */ + if (now < start_today) start_today = 0; + + /* check start_today and reset if required */ + if (now >= (start_today + SECONDS_PER_DAY)) + { + /* use localtime / mktime since start_today might be zero */ + struct tm t; + + start_today = now; + + localtime_r(&start_today, &t); + + t.tm_sec = 0; + t.tm_min = 0; + t.tm_hour = 0; + + start_today = mktime(&t); + } + + /* sleep to prevent a sub-second rotation */ + while (now == dst->stamp) + { + sleep(1); + now = time(NULL); + } + + if ((dst->stamp == 0) || (dst->size == 0)) + { + struct stat sb; + + memset(&sb, 0, sizeof(struct stat)); + + if (stat(dst->path, &sb) < 0) + { + if (errno == ENOENT) return 0; + return -1; + } + + if (dst->stamp == 0) dst->stamp = sb.st_birthtimespec.tv_sec; + if (dst->stamp == 0) dst->stamp = sb.st_mtimespec.tv_sec; + dst->size = sb.st_size; + } + + if (force == CHECKPOINT_TEST) + { + if ((dst->file_max > 0) && (dst->size >= dst->file_max)) force |= CHECKPOINT_SIZE; + if (dst->stamp < start_today) force |= CHECKPOINT_TIME; + + if (force == CHECKPOINT_TEST) return 0; + } + + if (dst->flags & MODULE_FLAG_TYPE_ASL_DIR) + { + if (force & CHECKPOINT_SIZE) + { + snprintf(newpath, sizeof(newpath), "%s.%c%lu", dst->fname, STYLE_SEC_PREFIX_CHAR, dst->stamp); + rename(dst->fname, newpath); + } + else + { + return 0; + } + } + + if ((dst->flags & MODULE_FLAG_BASESTAMP) == 0) + { + char tstamp[32]; + + asl_make_timestamp(dst->stamp, dst->flags, tstamp, sizeof(tstamp)); + snprintf(newpath, sizeof(newpath), "%s.%s", dst->path, tstamp); + rename(dst->path, newpath); + } + + dst->stamp = 0; + dst->size = 0; + + return 1; +} + +int +asl_check_option(aslmsg 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; +} + +void +asl_out_dst_data_release(asl_out_dst_data_t *dst) +{ + if (dst == NULL) return; + + if (dst->refcount > 0) dst->refcount--; + if (dst->refcount > 0) return; + + free(dst->path); + free(dst->fname); + free(dst->rotate_dir); + free(dst->fmt); +#if !TARGET_IPHONE_SIMULATOR + free(dst->uid); + free(dst->gid); +#endif + free(dst); +} + +asl_out_dst_data_t * +asl_out_dst_data_retain(asl_out_dst_data_t *dst) +{ + if (dst == NULL) return NULL; + dst->refcount++; + return dst; +} + +/* set owner, group, mode, and acls for a file */ +int +asl_out_dst_set_access(int fd, asl_out_dst_data_t *dst) +{ +#if !TARGET_IPHONE_SIMULATOR + uid_t fuid = 0; + gid_t fgid = 80; +#if !TARGET_OS_EMBEDDED + int status; + acl_t acl; + uuid_t uuid; + acl_entry_t entry; + acl_permset_t perms; + uint32_t i; +#endif +#endif + + if (dst == NULL) return -1; + if (fd < 0) return -1; + +#if TARGET_IPHONE_SIMULATOR + return fd; +#else + + if (dst->nuid > 0) fuid = dst->uid[0]; + if (dst->ngid > 0) fgid = dst->gid[0]; + + fchown(fd, fuid, fgid); + +#if TARGET_OS_EMBEDDED + return fd; +#else + acl = acl_init(1); + + for (i = 0; i < dst->ngid; i++) + { + if (dst->gid[i] == -2) continue; + + /* + * Don't bother setting group access if this is + * file's group and the file is group-readable. + */ + if ((dst->gid[i] == fgid) && (dst->mode & 0040)) continue; + + status = mbr_gid_to_uuid(dst->gid[i], uuid); + if (status != 0) + { + dst->gid[i] = -2; + continue; + } + + status = acl_create_entry_np(&acl, &entry, ACL_FIRST_ENTRY); + if (status != 0) goto asl_file_create_return; + + status = acl_set_tag_type(entry, ACL_EXTENDED_ALLOW); + if (status != 0) goto asl_file_create_return; + + status = acl_set_qualifier(entry, &uuid); + if (status != 0) goto asl_file_create_return; + + status = acl_get_permset(entry, &perms); + if (status != 0) goto asl_file_create_return; + + status = acl_add_perm(perms, ACL_READ_DATA); + if (status != 0) goto asl_file_create_return; + } + + for (i = 0; i < dst->nuid; i++) + { + if (dst->uid[i] == -2) continue; + + /* + * Don't bother setting user access if this is + * file's owner and the file is owner-readable. + */ + if ((dst->uid[i] == fuid) && (dst->mode & 0400)) continue; + + status = mbr_uid_to_uuid(dst->uid[i], uuid); + if (status != 0) + { + dst->uid[i] = -2; + continue; + } + + status = acl_create_entry_np(&acl, &entry, ACL_FIRST_ENTRY); + if (status != 0) goto asl_file_create_return; + + status = acl_set_tag_type(entry, ACL_EXTENDED_ALLOW); + if (status != 0) goto asl_file_create_return; + + status = acl_set_qualifier(entry, &uuid); + if (status != 0) goto asl_file_create_return; + + status = acl_get_permset(entry, &perms); + if (status != 0) goto asl_file_create_return; + + status = acl_add_perm(perms, ACL_READ_DATA); + if (status != 0) goto asl_file_create_return; + } + + status = acl_set_fd(fd, acl); + if (status != 0) + { + close(fd); + fd = -1; + } + +asl_file_create_return: + + acl_free(acl); + return fd; +#endif /* !TARGET_OS_EMBEDDED */ +#endif /* !TARGET_IPHONE_SIMULATOR */ +} + +/* create a file with acls */ +int +asl_out_dst_file_create_open(asl_out_dst_data_t *dst) +{ + int fd, status; + struct stat sb; + char outpath[MAXPATHLEN]; + + if (dst == NULL) return -1; + if (dst->path == NULL) return -1; + + asl_make_dst_filename(dst, outpath, sizeof(outpath)); + + memset(&sb, 0, sizeof(struct stat)); + status = stat(outpath, &sb); + if (status == 0) + { + /* must be a regular file */ + if (!S_ISREG(sb.st_mode)) return -1; + + /* file exists */ + fd = open(outpath, O_RDWR | O_APPEND | O_EXCL, 0); + + if (dst->stamp == 0) dst->stamp = sb.st_birthtimespec.tv_sec; + if (dst->stamp == 0) dst->stamp = sb.st_mtimespec.tv_sec; + dst->size = sb.st_size; + + return fd; + } + else if (errno != ENOENT) + { + /* stat error other than non-existant file */ + return -1; + } + + fd = open(outpath, O_RDWR | O_CREAT | O_EXCL, (dst->mode & 0666)); + if (fd < 0) return -1; + + dst->stamp = time(NULL); + + fd = asl_out_dst_set_access(fd, dst); + if (fd < 0) unlink(outpath); + + return fd; +} + +void +asl_out_module_free(asl_out_module_t *m) +{ + asl_out_rule_t *r, *n; + asl_out_module_t *x; + + while (m != NULL) + { + x = m->next; + + /* free name */ + free(m->name); + + /* free ruleset */ + r = m->ruleset; + while (r != NULL) + { + n = r->next; + if (r->dst != NULL) asl_out_dst_data_release(r->dst); + + if (r->query != NULL) asl_msg_release(r->query); + free(r->options); + free(r); + r = n; + } + + free(m); + m = x; + } +} + +asl_out_module_t * +asl_out_module_new(const char *name) +{ + asl_out_module_t *out = (asl_out_module_t *)calloc(1, sizeof(asl_out_module_t)); + + if (out == NULL) return NULL; + if (name == NULL) return NULL; + + out->name = strdup(name); + if (out->name == NULL) + { + free(out); + return NULL; + } + + out->flags = MODULE_FLAG_ENABLED; + + return out; +} + +/* Skip over query */ +static char * +_asl_out_module_find_action(char *s) +{ + char *p; + + p = s; + if (p == NULL) return NULL; + + /* Skip command character (?, Q, *, or =) */ + p++; + + forever + { + /* Find next [ */ + while ((*p == ' ') || (*p == '\t')) p++; + + if (*p == '\0') return NULL; + if (*p != '[') return p; + + /* skip to closing ] */ + while (*p != ']') + { + p++; + if (*p == '\\') + { + p++; + if (*p == ']') p++; + } + } + + if (*p == ']') p++; + } + + /* skip whitespace */ + while ((*p == ' ') || (*p == '\t')) p++; + + return NULL; +} + +/* + * Parse parameter setting line + * + * = param options + * evaluated once when module is initialized + * + * = [query] param options + * evaluated for each message, param set if message matches query + * + * = param [File path] + * evaluated once when module is initialized + * evaluated when change notification received for path + * + * = param [Plist path] ... + * evaluated once when module is initialized + * evaluated when change notification received for path + * + * = param [Profile name] ... + * evaluated once when module is initialized + * evaluated when change notification received for profile + */ +static asl_out_rule_t * +_asl_out_module_parse_set_param(asl_out_module_t *m, char *s) +{ + char *act, *p, *q; + asl_out_rule_t *out, *rule; + + if (m == NULL) return NULL; + + out = (asl_out_rule_t *)calloc(1, sizeof(asl_out_rule_t)); + if (out == NULL) return NULL; + + q = s + 1; + while ((*q == ' ') || (*q == '\'')) q++; + out->action = ACTION_SET_PARAM; + + if (*q == '[') + { + /* = [query] param options */ + act = _asl_out_module_find_action(s); + if (act == NULL) + { + free(out); + return NULL; + } + + out->options = _strdup_clean(act); + + p = act - 1; + if (*p == ']') p = act; + *p = '\0'; + + *s = 'Q'; + out->query = asl_msg_from_string(s); + if (out->query == NULL) + { + free(out->options); + free(out); + return NULL; + } + } + else + { + /* = param ... */ + p = strchr(s, '['); + if (p == NULL) + { + /* = param options */ + out->options = _strdup_clean(q); + } + else + { + /* = param [query] */ + if ((!strncmp(p, "[File ", 6)) || (!strncmp(p, "[File\t", 6))) out->action = ACTION_SET_FILE; + else if ((!strncmp(p, "[Plist ", 7)) || (!strncmp(p, "[Plist\t", 7))) out->action = ACTION_SET_PLIST; + else if ((!strncmp(p, "[Profile ", 9)) || (!strncmp(p, "[Profile\t", 9))) out->action = ACTION_SET_PROF; + + p--; + *p = '\0'; + out->options = _strdup_clean(q); + + *p = ' '; + p--; + *p = 'Q'; + out->query = asl_msg_from_string(p); + if (out->query == NULL) + { + free(out->options); + free(out); + return NULL; + } + } + } + + if (m->ruleset == NULL) m->ruleset = out; + else + { + for (rule = m->ruleset; rule->next != NULL; rule = rule->next); + rule->next = out; + } + + return out; +} + +#if !TARGET_IPHONE_SIMULATOR +static void +_dst_add_uid(asl_out_dst_data_t *dst, char *s) +{ + int i; + uid_t uid; + + if (dst == NULL) return; + if (s == NULL) return; + + uid = atoi(s); + + for (i = 0 ; i < dst->nuid; i++) + { + if (dst->uid[i] == uid) return; + } + + dst->uid = reallocf(dst->uid, (dst->nuid + 1) * sizeof(uid_t)); + if (dst->uid == NULL) + { + dst->nuid = 0; + return; + } + + dst->uid[dst->nuid++] = uid; +} + +static void +_dst_add_gid(asl_out_dst_data_t *dst, char *s) +{ + int i; + gid_t gid; + + if (dst == NULL) return; + if (s == NULL) return; + + gid = atoi(s); + + for (i = 0 ; i < dst->ngid; i++) + { + if (dst->gid[i] == gid) return; + } + + dst->gid = reallocf(dst->gid, (dst->ngid + 1) * sizeof(gid_t)); + if (dst->gid == NULL) + { + dst->ngid = 0; + return; + } + + dst->gid[dst->ngid++] = gid; +} +#endif /* !TARGET_IPHONE_SIMULATOR */ + +static char * +_dst_format_string(char *s) +{ + char *fmt; + size_t i, len, n; + + if (s == NULL) return NULL; + + len = strlen(s); + + /* format string can be enclosed by quotes */ + if ((len >= 2) && ((s[0] == '\'') || (s[0] == '"')) && (s[len-1] == s[0])) + { + s++; + len -= 2; + } + + n = 0; + for (i = 0; i < len; i++) if (s[i] == '\\') n++; + + fmt = malloc(1 + len - n); + if (fmt == NULL) return NULL; + + for (i = 0, n = 0; i < len; i++) if (s[i] != '\\') fmt[n++] = s[i]; + fmt[n] = '\0'; + return fmt; +} + +size_t +asl_str_to_size(char *s) +{ + size_t len, n, max; + char x; + + if (s == NULL) return 0; + + len = strlen(s); + if (len == 0) return 0; + + n = 1; + x = s[len - 1]; + if (x > 90) x -= 32; + if (x == 'K') n = 1ll << 10; + else if (x == 'M') n = 1ll << 20; + else if (x == 'G') n = 1ll << 30; + + max = atoll(s) * n; + return max; +} + +static bool +_dst_path_match(const char *newpath, const char *existingpath) +{ + if (newpath == NULL) return (existingpath == NULL); + if (existingpath == NULL) return false; + if (newpath[0] == '/') return (strcmp(newpath, existingpath) == 0); + + const char *trailing = strrchr(existingpath, '/'); + if (trailing == NULL) return (strcmp(newpath, existingpath) == 0); + trailing++; + return (strcmp(newpath, trailing) == 0); +} + +static asl_out_dst_data_t * +_asl_out_module_parse_dst(asl_out_module_t *m, char *s, mode_t def_mode) +{ + asl_out_rule_t *out, *rule; + asl_out_dst_data_t *dst; + char *p, *opts, *path; + char **path_parts; + int has_dotdot, recursion_limit; + + if (m == NULL) return NULL; + if (s == NULL) return NULL; + + /* skip whitespace */ + while ((*s == ' ') || (*s == '\t')) s++; + + opts = s; + path = next_word_from_string(&opts); + if (path == NULL) return NULL; + + /* + * Check path for ".." component (not permitted). + * Also substitute environment variables. + */ + has_dotdot = 0; + path_parts = explode(path, "/"); + asl_string_t *processed_path = asl_string_new(ASL_ENCODE_NONE); + recursion_limit = 5; + + while ((recursion_limit > 0) && (path_parts != NULL) && (processed_path != NULL)) + { + uint32_t i; + int did_sub = 0; + + for (i = 0; path_parts[i] != NULL; i++) + { + if (!strncmp(path_parts[i], "$ENV(", 5)) + { + char *p = strchr(path_parts[i], ')'); + if (p != NULL) *p = '\0'; + char *env_val = getenv(path_parts[i] + 5); + if (env_val != NULL) + { + did_sub = 1; + + if (env_val[0] != '/') asl_string_append_char_no_encoding(processed_path, '/'); + asl_string_append_no_encoding(processed_path, env_val); + } + } + else + { + if (i == 0) + { + if (path_parts[0][0] != '\0') asl_string_append_no_encoding(processed_path, path_parts[i]); + } + else + { + asl_string_append_char_no_encoding(processed_path, '/'); + asl_string_append_no_encoding(processed_path, path_parts[i]); + } + } + + if ((has_dotdot == 0) && (!strcmp(path_parts[i], ".."))) has_dotdot = 1; + } + + free_string_list(path_parts); + + if ((did_sub == 0) || (has_dotdot == 1)) + { + path_parts = NULL; + } + else + { + /* substitution might have added a ".." so check the new path */ + free(path); + path = asl_string_free_return_bytes(processed_path); + processed_path = asl_string_new(ASL_ENCODE_NONE); + path_parts = explode(path, "/"); + recursion_limit--; + } + } + + free(path); + + if ((has_dotdot != 0) || (recursion_limit == 0)) + { + asl_string_free(processed_path); + return NULL; + } + + path = asl_string_free_return_bytes(processed_path); + + /* check if there's already a dst for this path */ + for (rule = m->ruleset; rule != NULL; rule = rule->next) + { + if (rule->action != ACTION_OUT_DEST) continue; + + dst = rule->dst; + if (dst == NULL) continue; + + if (_dst_path_match(path, dst->path)) + { + free(path); + return dst; + } + } + + if (path[0] != '/') + { + char *t = path; + const char *log_root = "/var/log"; + +#if TARGET_IPHONE_SIMULATOR + log_root = getenv("IPHONE_SIMULATOR_LOG_ROOT"); + assert(log_root); +#endif + + if (!strcmp(m->name, ASL_MODULE_NAME)) asprintf(&path, "%s/%s", log_root, t); + else asprintf(&path, "%s/module/%s/%s", log_root, m->name, t); + + free(t); + } + + out = (asl_out_rule_t *)calloc(1, sizeof(asl_out_rule_t)); + dst = (asl_out_dst_data_t *)calloc(1, sizeof(asl_out_dst_data_t)); + if ((out == NULL) || (dst == NULL)) + { + free(path); + free(out); + free(dst); + return NULL; + } + + dst->refcount = 1; + dst->path = path; + dst->mode = def_mode; + dst->ttl = DEFAULT_TTL; + dst->flags = MODULE_FLAG_COALESCE; + + while (NULL != (p = next_word_from_string(&opts))) + { + if (KEYMATCH(p, "mode=")) dst->mode = strtol(p+5, NULL, 0); + else if (KEYMATCH(p, "ttl=")) dst->ttl = strtol(p+4, NULL, 0); +#if !TARGET_IPHONE_SIMULATOR + else if (KEYMATCH(p, "uid=")) _dst_add_uid(dst, p+4); + else if (KEYMATCH(p, "gid=")) _dst_add_gid(dst, p+4); +#endif + else if (KEYMATCH(p, "fmt=")) dst->fmt = _dst_format_string(p+4); + else if (KEYMATCH(p, "format=")) dst->fmt = _dst_format_string(p+7); + else if (KEYMATCH(p, "dest=")) dst->rotate_dir = _strdup_clean(p+5); + else if (KEYMATCH(p, "dst=")) dst->rotate_dir = _strdup_clean(p+4); + else if (KEYMATCH(p, "coalesce=")) + { + if (KEYMATCH(p+9, "0")) dst->flags &= ~MODULE_FLAG_COALESCE; + else if (KEYMATCH(p+9, "off")) dst->flags &= ~MODULE_FLAG_COALESCE; + else if (KEYMATCH(p+9, "false")) dst->flags &= ~MODULE_FLAG_COALESCE; + } + else if (KEYMATCH(p, "compress")) dst->flags |= MODULE_FLAG_COMPRESS; + else if (KEYMATCH(p, "extern")) dst->flags |= MODULE_FLAG_EXTERNAL; + else if (KEYMATCH(p, "soft")) dst->flags |= MODULE_FLAG_SOFT_WRITE; + else if (KEYMATCH(p, "file_max=")) dst->file_max = asl_str_to_size(p+9); + else if (KEYMATCH(p, "all_max=")) dst->all_max = asl_str_to_size(p+8); + else if (KEYMATCH(p, "style=") || KEYMATCH(p, "rotate=")) + { + const char *x = p + 6; + + if (KEYMATCH(p, "rotate=")) x++; + + dst->flags |= MODULE_FLAG_ROTATE; + + if (KEYMATCH(x, "sec") || KEYMATCH(x, "seconds")) + { + dst->flags |= MODULE_FLAG_STYLE_SEC; + } + else if (KEYMATCH(x, "utc") || KEYMATCH(x, "date") || KEYMATCH(x, "zulu")) + { + const char *dash = strchr(x, '-'); + if ((dash != NULL) && (*(dash + 1) == 'b')) dst->flags |= MODULE_FLAG_STYLE_UTC_B; + else dst->flags |= MODULE_FLAG_STYLE_UTC; + } + else if (KEYMATCH(x, "local") || KEYMATCH(x, "lcl")) + { + const char *dash = strchr(x, '-'); + if ((dash != NULL) && (*(dash + 1) == 'b')) dst->flags |= MODULE_FLAG_STYLE_LCL_B; + else dst->flags |= MODULE_FLAG_STYLE_LCL; + } + else if (KEYMATCH(x, "#") || KEYMATCH(x, "seq") || KEYMATCH(x, "sequence")) + { + dst->flags |= MODULE_FLAG_STYLE_SEQ; + } + else + { + dst->flags |= MODULE_FLAG_STYLE_SEC; + } + } + else if (KEYMATCH(p, "rotate")) dst->flags |= MODULE_FLAG_ROTATE; + else if (KEYMATCH(p, "crashlog")) + { + /* crashlog implies rotation */ + dst->flags |= MODULE_FLAG_ROTATE; + dst->flags |= MODULE_FLAG_CRASHLOG; + dst->flags |= MODULE_FLAG_BASESTAMP; + dst->flags &= ~MODULE_FLAG_COALESCE; + } + else if (KEYMATCH(p, "basestamp")) + { + dst->flags |= MODULE_FLAG_BASESTAMP; + } + + free(p); + p = NULL; + } + +#if TARGET_OS_EMBEDDED + /* check for crashreporter files */ + if (KEYMATCH(dst->path, _PATH_CRASHREPORTER)) + { + dst->flags |= MODULE_FLAG_ROTATE; + dst->flags |= MODULE_FLAG_CRASHLOG; + dst->flags |= MODULE_FLAG_BASESTAMP; + dst->flags &= ~MODULE_FLAG_COALESCE; + } +#endif + + /* default text file format is "std" */ + if (dst->fmt == NULL) dst->fmt = strdup("std"); + + /* duplicate compression is only possible for std and bsd formats */ + if (strcmp(dst->fmt, "std") && strcmp(dst->fmt, "bsd")) dst->flags &= ~MODULE_FLAG_COALESCE; + + /* note if format is one of std, bsd, or msg */ + if ((!strcmp(dst->fmt, "std")) || (!strcmp(dst->fmt, "bsd")) || (!strcmp(dst->fmt, "msg"))) dst->flags |= MODULE_FLAG_STD_BSD_MSG; + + /* MODULE_FLAG_STYLE_SEQ can not be used with MODULE_FLAG_BASESTAMP */ + if ((dst->flags & MODULE_FLAG_BASESTAMP) && (dst->flags & MODULE_FLAG_STYLE_SEQ)) + { + dst->flags &= ~MODULE_FLAG_STYLE_SEQ; + dst->flags |= MODULE_FLAG_STYLE_SEC; + } + + /* set time format for raw output */ + if (!strcmp(dst->fmt, "raw")) dst->tfmt = "sec"; + + out->action = ACTION_OUT_DEST; + out->dst = dst; + + /* dst rules go first */ + out->next = m->ruleset; + m->ruleset = out; + + return dst; +} + +static asl_out_rule_t * +_asl_out_module_parse_query_action(asl_out_module_t *m, char *s) +{ + char *act, *p; + asl_out_rule_t *out, *rule; + + if (m == NULL) return NULL; + + out = (asl_out_rule_t *)calloc(1, sizeof(asl_out_rule_t)); + if (out == NULL) return NULL; + + act = _asl_out_module_find_action(s); + if (act == NULL) return NULL; + + /* find whitespace delimiter */ + p = strchr(act, ' '); + if (p == NULL) p = strchr(act, '\t'); + if (p != NULL) *p = '\0'; + + if (!strcasecmp(act, "ignore")) out->action = ACTION_IGNORE; + else if (!strcasecmp(act, "skip")) out->action = ACTION_SKIP; + else if (!strcasecmp(act, "claim")) out->action = ACTION_CLAIM; + else if (!strcasecmp(act, "notify")) out->action = ACTION_NOTIFY; + else if (!strcasecmp(act, "file")) out->action = ACTION_FILE; + else if (!strcasecmp(act, "asl_file")) out->action = ACTION_ASL_FILE; + else if (!strcasecmp(act, "directory")) out->action = ACTION_ASL_DIR; + else if (!strcasecmp(act, "dir")) out->action = ACTION_ASL_DIR; + else if (!strcasecmp(act, "asl_directory")) out->action = ACTION_ASL_DIR; + else if (!strcasecmp(act, "asl_dir")) out->action = ACTION_ASL_DIR; + else if (!strcasecmp(act, "store_dir")) out->action = ACTION_ASL_DIR; + else if (!strcasecmp(act, "store_directory")) out->action = ACTION_ASL_DIR; + else if (!strcasecmp(act, "control")) out->action = ACTION_CONTROL; + else if (!strcasecmp(act, "save")) out->action = ACTION_ASL_STORE; + else if (!strcasecmp(act, "store")) out->action = ACTION_ASL_STORE; + else if (!strcasecmp(act, "access")) out->action = ACTION_ACCESS; + else if (!strcmp(m->name, ASL_MODULE_NAME)) + { + /* actions only allowed in com.apple.asl */ + if (!strcasecmp(act, "broadcast")) out->action = ACTION_BROADCAST; + else if (!strcasecmp(act, "forward")) out->action = ACTION_FORWARD; + } + + if (out->action == ACTION_NONE) + { + free(out); + return NULL; + } + + /* options follow delimited (now zero) */ + if (p != NULL) + { + /* skip whitespace */ + while ((*p == ' ') || (*p == '\t')) p++; + + out->options = _strdup_clean(p+1); + + if (out->options == NULL) + { + free(out); + return NULL; + } + } + + p = act - 1; + + *p = '\0'; + + if (*s== '*') + { + out->query = asl_msg_new(ASL_TYPE_QUERY); + } + else + { + *s = 'Q'; + out->query = asl_msg_from_string(s); + } + + if (out->query == NULL) + { + free(out->options); + free(out); + return NULL; + } + + /* store /some/path means save to an asl file */ + if ((out->action == ACTION_ASL_STORE) && (out->options != NULL)) out->action = ACTION_ASL_FILE; + + if ((out->action == ACTION_FILE) || (out->action == ACTION_ASL_FILE) || (out->action == ACTION_ASL_DIR)) + { + mode_t def_mode = 0644; + if (out->action == ACTION_ASL_DIR) def_mode = 0755; + + out->dst = asl_out_dst_data_retain(_asl_out_module_parse_dst(m, out->options, def_mode)); + if (out->dst == NULL) + { + out->action = ACTION_NONE; + return out; + } + + if ((out->action == ACTION_FILE) && (out->dst != NULL) && (out->dst->fmt != NULL) && (!strcasecmp(out->dst->fmt, "asl"))) + { + out->action = ACTION_ASL_FILE; + } + + if ((out->action == ACTION_ASL_FILE) && (out->dst != NULL)) + { + /* remove meaningless flags */ + out->dst->flags &= ~MODULE_FLAG_COALESCE; + out->dst->flags &= ~MODULE_FLAG_STD_BSD_MSG; + out->dst->flags |= MODULE_FLAG_TYPE_ASL; + } + + if (out->action == ACTION_ASL_DIR) + { + /* remove meaningless flags */ + out->dst->flags &= ~MODULE_FLAG_ROTATE; + out->dst->flags &= ~MODULE_FLAG_COALESCE; + out->dst->flags &= ~MODULE_FLAG_STD_BSD_MSG; + out->dst->flags |= MODULE_FLAG_TYPE_ASL_DIR; + } + + /* only ACTION_FILE and ACTION_ASL_FILE may rotate */ + if ((out->action != ACTION_FILE) && (out->action != ACTION_ASL_FILE)) + { + out->dst->flags &= ~MODULE_FLAG_ROTATE; + } + +#if !TARGET_IPHONE_SIMULATOR + if (out->dst->nuid == 0) _dst_add_uid(out->dst, "0"); + if (out->dst->ngid == 0) _dst_add_gid(out->dst, "80"); +#endif + } + + if (m->ruleset == NULL) m->ruleset = out; + else + { + for (rule = m->ruleset; rule->next != NULL; rule = rule->next); + rule->next = out; + } + + return out; +} + +asl_out_rule_t * +asl_out_module_parse_line(asl_out_module_t *m, char *s) +{ + while ((*s == ' ') || (*s == '\t')) s++; + + if ((*s == 'Q') || (*s == '?') || (*s == '*')) + { + return _asl_out_module_parse_query_action(m, s); + } + else if (*s == '=') + { + return _asl_out_module_parse_set_param(m, s); + } + else if (*s == '>') + { + _asl_out_module_parse_dst(m, s + 1, 0644); + } + + return NULL; +} + +asl_out_module_t * +asl_out_module_init_from_file(const char *name, FILE *f) +{ + asl_out_module_t *out; + char *line; + + if (f == NULL) return NULL; + + out = asl_out_module_new(name); + if (out == NULL) return NULL; + + /* read and parse config file */ + while (NULL != (line = get_line_from_file(f))) + { + asl_out_module_parse_line(out, line); + free(line); + } + + return out; +} + +static asl_out_module_t * +_asl_out_module_find(asl_out_module_t *list, const char *name) +{ + asl_out_module_t *x; + + if (list == NULL) return NULL; + if (name == NULL) return NULL; + + for (x = list; x != NULL; x = x->next) + { + if ((x->name != NULL) && (!strcmp(x->name, name))) return x; + } + + return NULL; +} + +static void +_asl_out_module_read_and_merge_dir(asl_out_module_t **list, const char *path, uint32_t flags) +{ + DIR *d; + struct dirent *ent; + FILE *f; + asl_out_module_t *last, *x; + + if (list == NULL) return; + if (path == NULL) return; + + last = *list; + if (last != NULL) + { + while (last->next != NULL) last = last->next; + } + + d = opendir(path); + if (d != NULL) + { + while (NULL != (ent = readdir(d))) + { + if ((ent->d_name != NULL) && (ent->d_name[0] != '.')) + { + /* merge: skip this file if we already have a module with this name */ + if (_asl_out_module_find(*list, ent->d_name) != NULL) continue; + + char tmp[MAXPATHLEN]; + snprintf(tmp, sizeof(tmp), "%s/%s", path, ent->d_name); + f = fopen(tmp, "r"); + if (f != NULL) + { + x = asl_out_module_init_from_file(ent->d_name, f); + fclose(f); + + if (x != NULL) + { + x->flags |= flags; + + if (!strcmp(ent->d_name, ASL_MODULE_NAME)) + { + /* com.apple.asl goes at the head of the list */ + x->next = *list; + *list = x; + if (last == NULL) last = *list; + } + else if (*list == NULL) + { + *list = x; + last = *list; + } + else + { + last->next = x; + last = x; + } + } + } + } + } + + closedir(d); + } +} + +asl_out_module_t * +asl_out_module_init(void) +{ + asl_out_module_t *out = NULL; + +#if TARGET_IPHONE_SIMULATOR + char *sim_root_path, *sim_resources_path; + char *asl_conf, *asl_conf_dir, *asl_conf_local_dir; + + sim_root_path = getenv("IPHONE_SIMULATOR_ROOT"); + assert(sim_root_path); + + sim_resources_path = getenv("IPHONE_SHARED_RESOURCES_DIRECTORY"); + assert(sim_resources_path); + + asprintf(&asl_conf, "%s%s", sim_root_path, _PATH_ASL_CONF); + asprintf(&asl_conf_dir, "%s%s", sim_root_path, _PATH_ASL_CONF_DIR); + asprintf(&asl_conf_local_dir, "%s%s", sim_resources_path, _PATH_ASL_CONF_DIR); + + _asl_out_module_read_and_merge_dir(&out, asl_conf_local_dir, MODULE_FLAG_LOCAL); + free(asl_conf_local_dir); + + _asl_out_module_read_and_merge_dir(&out, asl_conf_dir, 0); + free(asl_conf_dir); +#else + _asl_out_module_read_and_merge_dir(&out, _PATH_ASL_CONF_LOCAL_DIR, MODULE_FLAG_LOCAL); + _asl_out_module_read_and_merge_dir(&out, _PATH_ASL_CONF_DIR, 0); +#endif + + if (_asl_out_module_find(out, ASL_MODULE_NAME) == NULL) + { + /* system just has old-style /etc/asl.conf */ +#if TARGET_IPHONE_SIMULATOR + FILE *f = fopen(asl_conf, "r"); + free(asl_conf); +#else + FILE *f = fopen(_PATH_ASL_CONF, "r"); +#endif + if (f != NULL) + { + asl_out_module_t *x = asl_out_module_init_from_file(ASL_MODULE_NAME, f); + fclose(f); + if (x != NULL) + { + x->next = out; + out = x; + } + } + } + + return out; +} + +/* + * Print rule + */ +char * +asl_out_module_rule_to_string(asl_out_rule_t *r) +{ + uint32_t len; + char *str, *out; + + if (r == NULL) + { + asprintf(&out, "NULL rule"); + return out; + } + + str = asl_msg_to_string(r->query, &len); + + asprintf(&out, " %s%s%s%s%s", + asl_out_action_name[r->action], + (r->query == NULL) ? "" : " ", + (r->query == NULL) ? "" : str, + (r->options == NULL) ? "" : " ", + (r->options == NULL) ? "" : r->options); + + free(str); + return out; +} + +/* + * Print module + */ +void +asl_out_module_print(FILE *f, asl_out_module_t *m) +{ + asl_out_rule_t *r, *n; + asl_out_dst_data_t *o; + uint32_t i; + + n = NULL; + for (r = m->ruleset; r != NULL; r = n) + { + uint32_t len; + char *str = asl_msg_to_string(r->query, &len); + + fprintf(f, " %s", asl_out_action_name[r->action]); + if (r->query != NULL) fprintf(f, " %s", str); + if (r->options != NULL) fprintf(f, " %s", r->options); + if (r->action == ACTION_OUT_DEST) + { + o = r->dst; + if (o == NULL) + { + fprintf(f, " data: NULL"); + } + else + { + fprintf(f, "%s\n", o->path); + fprintf(f, " rules: %u\n", o->refcount - 1); + fprintf(f, " dest: %s\n", (o->rotate_dir == NULL) ? "(none)" : o->rotate_dir); + fprintf(f, " format: %s\n", (o->fmt == NULL) ? "std" : o->fmt); + fprintf(f, " time_format: %s\n", (o->tfmt == NULL) ? "lcl" : o->tfmt); + fprintf(f, " flags: 0x%08x", o->flags); + if (o->flags != 0) + { + char c = '('; + fprintf(f, " "); + if (o->flags & MODULE_FLAG_ENABLED) + { + fprintf(f, "%cenabled", c); + c = ' '; + } + if (o->flags & MODULE_FLAG_LOCAL) + { + fprintf(f, "%clocal", c); + c = ' '; + } + if (o->flags & MODULE_FLAG_ROTATE) + { + fprintf(f, "%crotate", c); + c = ' '; + } + if (o->flags & MODULE_FLAG_COALESCE) + { + fprintf(f, "%ccoalesce", c); + c = ' '; + } + if (o->flags & MODULE_FLAG_COMPRESS) + { + fprintf(f, "%ccompress", c); + c = ' '; + } + if (o->flags & MODULE_FLAG_EXTERNAL) + { + fprintf(f, "%cexternal", c); + c = ' '; + } + if (o->flags & MODULE_FLAG_STYLE_SEC) + { + fprintf(f, "%cseconds", c); + c = ' '; + } + if (o->flags & MODULE_FLAG_STYLE_SEQ) + { + fprintf(f, "%csequence", c); + c = ' '; + } + if (o->flags & MODULE_FLAG_STYLE_UTC) + { + fprintf(f, "%cutc", c); + c = ' '; + } + if (o->flags & MODULE_FLAG_STYLE_UTC_B) + { + fprintf(f, "%cutc-basic", c); + c = ' '; + } + if (o->flags & MODULE_FLAG_STYLE_LCL) + { + fprintf(f, "%clocal", c); + c = ' '; + } + if (o->flags & MODULE_FLAG_STYLE_LCL_B) + { + fprintf(f, "%clocal-basic", c); + c = ' '; + } + if (o->flags & MODULE_FLAG_BASESTAMP) + { + fprintf(f, "%cbasestamp", c); + c = ' '; + } + if (o->flags & MODULE_FLAG_CRASHLOG) + { + fprintf(f, "%ccrashlog", c); + c = ' '; + } + if (o->flags & MODULE_FLAG_SOFT_WRITE) + { + fprintf(f, "%csoft", c); + c = ' '; + } + if (o->flags & MODULE_FLAG_TYPE_ASL) + { + fprintf(f, "%casl_file", c); + c = ' '; + } + if (o->flags & MODULE_FLAG_TYPE_ASL_DIR) + { + fprintf(f, "%casl_directory", c); + c = ' '; + } + if (o->flags & MODULE_FLAG_STD_BSD_MSG) + { + fprintf(f, "%cstd/bsd/msg", c); + c = ' '; + } + fprintf(f, ")"); + } + fprintf(f, "\n"); + + fprintf(f, " ttl: %u\n", o->ttl); + fprintf(f, " mode: 0%o\n", o->mode); + fprintf(f, " file_max: %lu\n", o->file_max); + fprintf(f, " all_max: %lu\n", o->all_max); +#if !TARGET_IPHONE_SIMULATOR + fprintf(f, " uid:"); + for (i = 0; i < o->nuid; i++) fprintf(f, " %d", o->uid[i]); + fprintf(f, "\n"); + fprintf(f, " gid:"); + for (i = 0; i < o->ngid; i++) fprintf(f, " %d", o->gid[i]); +#endif + } + } + + fprintf(f, "\n"); + n = r->next; + + free(str); + } +} + +void +asl_out_file_list_free(asl_out_file_list_t *l) +{ + asl_out_file_list_t *n; + + if (l == NULL) return; + + while (l != NULL) + { + free(l->name); + n = l->next; + free(l); + l = n; + } +} + +/* + * Checks input name for the form base[.stamp][.gz] + * name == base is allowed if src is true. + * base.gz is not allowed. + * Output parameter stamp must be freed by caller. + */ +bool +_check_file_name(const char *name, const char *base, bool src, char **stamp) +{ + size_t baselen, nparts; + const char *p, *q, *part[2]; + bool isgz = false; + + if (name == NULL) return false; + if (base == NULL) return false; + + baselen = strlen(base); + if (baselen == 0) return false; + + if (stamp != NULL) *stamp = NULL; + + if (strncmp(name, base, baselen)) return false; + + p = name + baselen; + + /* name == base not allowed (it's the "active" file) */ + if (*p == '\0') return false; + + /* name must be base.something */ + if (*p != '.') return false; + + /* maximum of 2 parts (stamp and gz) */ + nparts = 0; + for (q = p; *q != '\0'; q++) + { + if (*q == '.') + { + if (nparts == 2) return false; + part[nparts++] = q + 1; + } + } + + if (nparts == 0) return false; + + isgz = strcmp(part[nparts - 1], "gz") == 0; + + /* no compressed files in src */ + if (src && isgz) return false; + + /* expecting base.stamp or base.stamp.gz */ + + if (nparts == 1) + { + /* compressed files must have a stamp (base.gz is not allowed) */ + if (isgz) return false; + + /* got base.stamp */ + if (stamp != NULL) *stamp = strdup(part[0]); + return true; + } + + /* expecting base.stamp.gz */ + if (!isgz) return false; + + /* got base.stamp.gz */ + if (stamp != NULL) + { + *stamp = strdup(part[0]); + char *x = strchr(*stamp, '.'); + if (x != NULL) *x = '\0'; + } + + return true; +} + +/* + * Find files in a directory (dir) that all have a common prefix (base). + * Bits in flags further control the search. + * + * MODULE_FLAG_STYLE_SEQ means a numeric sequence number is expected, although not required. + * E.g. foo.log foo.log.0 + * + * MODULE_FLAG_STYLE_SEC also means a numeric sequence number is required following an 'T' character. + * The numeric value is the file's timestamp in seconds. E.g foo.log.T1335200452 + * + * MODULE_FLAG_STYLE_UTC requires a date/time component as the file's timestamp. + * E.g. foo.2012-04-06T15:30:00Z + * + * MODULE_FLAG_STYLE_UTC_B requires a date/time component as the file's timestamp. + * E.g. foo.20120406T153000Z + * + * MODULE_FLAG_STYLE_LCL requires a date/time component as the file's timestamp. + * E.g. foo.2012-04-06T15:30:00-7 + * + * MODULE_FLAG_STYLE_LCL_B requires a date/time component as the file's timestamp. + * E.g. foo.20120406T153000-07 + */ +asl_out_file_list_t * +asl_list_log_files(const char *dir, const char *base, bool src, uint32_t flags) +{ + DIR *d; + struct dirent *ent; + char path[MAXPATHLEN]; + uint32_t seq; + time_t ftime; + struct stat sb; + int n; + asl_out_file_list_t *out, *x, *y; + + if (dir == NULL) return NULL; + if (base == NULL) return NULL; + + out = NULL; + + d = opendir(dir); + if (d == NULL) return NULL; + + while (NULL != (ent = readdir(d))) + { + char *stamp = NULL; + bool check; + + if (ent->d_name == NULL) continue; + + check = _check_file_name(ent->d_name, base, src, &stamp); + if (!check) continue; + + /* exclude base from dst list */ + + seq = IndexNull; + ftime = 0; + + if (stamp == NULL) + { + } + else if (flags & MODULE_FLAG_STYLE_SEQ) + { + seq = atoi(stamp); + if ((seq == 0) && strcmp(stamp, "0")) + { + free(stamp); + continue; + } + } + else if (flags & MODULE_FLAG_STYLE_SEC) + { + ftime = atoi(stamp + 1); + } + else if ((flags & MODULE_FLAG_STYLE_UTC) || (flags & MODULE_FLAG_STYLE_UTC_B) || (flags & MODULE_FLAG_STYLE_LCL) || (flags & MODULE_FLAG_STYLE_LCL_B)) + { + struct tm t; + char zone; + uint32_t h, m, s; + long utc_offset = 0; + + memset(&t, 0, sizeof(t)); + h = m = s = 0; + + n = 0; + if ((flags & MODULE_FLAG_STYLE_UTC) || (flags & MODULE_FLAG_STYLE_LCL)) + { + n = sscanf(stamp, "%d-%d-%dT%d:%d:%d%c%u:%u:%u", &t.tm_year, &t.tm_mon, &t.tm_mday, &t.tm_hour, &t.tm_min, &t.tm_sec, &zone, &h, &m, &s); + } + else + { + n = sscanf(stamp, "%4d%2d%2dT%2d%2d%2d%c%2u%2u%2u", &t.tm_year, &t.tm_mon, &t.tm_mday, &t.tm_hour, &t.tm_min, &t.tm_sec, &zone, &h, &m, &s); + } + + if (n < 6) + { + continue; + } + else if (n == 6) + { + zone = 'J'; + } + else if ((zone == '-') || (zone == '+')) + { + if (n >= 8) utc_offset += (3600 * h); + if (n >= 9) utc_offset += (60 * m); + if (n == 10) utc_offset += s; + if (zone == '-') utc_offset *= -1; + } + else if ((zone >= 'A') && (zone <= 'Z')) + { + if (zone < 'J') utc_offset = 3600 * ((zone - 'A') + 1); + else if ((zone >= 'K') && (zone <= 'M')) utc_offset = 3600 * (zone - 'A'); + else if (zone <= 'Y') utc_offset = -3600 * ((zone - 'N') + 1); + } + else if ((zone >= 'a') && (zone <= 'z')) + { + if (zone < 'j') utc_offset = 3600 * ((zone - 'a') + 1); + else if ((zone >= 'k') && (zone <= 'm')) utc_offset = 3600 * (zone - 'a'); + else if (zone <= 'y') utc_offset = -3600 * ((zone - 'n') + 1); + } + else + { + free(stamp); + continue; + } + + t.tm_year -= 1900; + t.tm_mon -= 1; + t.tm_sec += utc_offset; + t.tm_isdst = -1; + + if ((zone == 'J') || (zone == 'j')) ftime = mktime(&t); + else ftime = timegm(&t); + } + + free(stamp); + + x = (asl_out_file_list_t *)calloc(1, sizeof(asl_out_file_list_t)); + if (x == NULL) + { + asl_out_file_list_free(out); + return NULL; + } + + x->name = strdup(ent->d_name); + x->ftime = ftime; + x->seq = seq; + + memset(&sb, 0, sizeof(sb)); + snprintf(path, sizeof(path), "%s/%s", dir, ent->d_name); + if (stat(path, &sb) == 0) + { + x->size = sb.st_size; + if (flags & MODULE_FLAG_STYLE_SEQ) + { + x->ftime = sb.st_birthtimespec.tv_sec; + if (x->ftime == 0) x->ftime = sb.st_mtimespec.tv_sec; + } + } + + if (flags & MODULE_FLAG_STYLE_SEQ) + { + if (out == NULL) + { + out = x; + } + else if ((x->seq == IndexNull) || ((x->seq < out->seq) && (out->seq != IndexNull))) + { + x->next = out; + out->prev = x; + out = x; + } + else + { + for (y = out; y != NULL; y = y->next) + { + if (y->next == NULL) + { + y->next = x; + x->prev = y; + break; + } + else if ((x->seq < y->next->seq) && (y->next->seq != IndexNull)) + { + x->next = y->next; + y->next = x; + x->prev = y; + x->next->prev = x; + break; + } + } + } + } + else + { + if (out == NULL) + { + out = x; + } + else if (x->ftime < out->ftime) + { + x->next = out; + out->prev = x; + out = x; + } + else + { + for (y = out; y != NULL; y = y->next) + { + if (y->next == NULL) + { + y->next = x; + x->prev = y; + break; + } + else if (x->ftime < y->next->ftime) + { + x->next = y->next; + y->next = x; + x->prev = y; + x->next->prev = x; + break; + } + } + } + } + } + + closedir(d); + return out; +} + +/* + * List the source files for an output asl_out_dst_data_t + */ +asl_out_file_list_t * +asl_list_src_files(asl_out_dst_data_t *dst) +{ + char *base; + uint32_t flags = MODULE_FLAG_STYLE_SEC; + asl_out_file_list_t *out; + + if (dst == NULL) return NULL; + if (dst->path == NULL) return NULL; + + /* + * MODULE_FLAG_EXTERNAL means some process other than syslogd writes the file. + * We simply check for its existence. + */ + if (dst->flags & MODULE_FLAG_EXTERNAL) + { + struct stat sb; + + memset(&sb, 0, sizeof(struct stat)); + + if (stat(dst->path, &sb) == 0) + { + if (S_ISREG(sb.st_mode)) + { + out = (asl_out_file_list_t *)calloc(1, sizeof(asl_out_file_list_t)); + if (out != NULL) + { + char *p = strrchr(dst->path, '/'); + if (p == NULL) p = dst->path; + else p++; + out->name = strdup(p); + out->ftime = sb.st_birthtimespec.tv_sec; + if (out->ftime == 0) out->ftime = sb.st_mtimespec.tv_sec; + return out; + } + } + } + + return NULL; + } + + /* + * Checkpoint / source format may be one of: + * MODULE_FLAG_STYLE_SEC (foo.T12345678.log), + * MODULE_FLAG_STYLE_UTC (foo.20120-06-24T12:34:56Z.log) + * MODULE_FLAG_STYLE_UTC_B (foo.201200624T123456Z.log) + * MODULE_FLAG_STYLE_LCL (foo.20120-06-24T12:34:56-7.log) + * MODULE_FLAG_STYLE_LCL_B (foo.201200624T123456-07.log) + * + * MODULE_FLAG_STYLE_SEC format is used for sequenced (MODULE_FLAG_STYLE_SEQ) files. + * aslmanager converts the file names. + */ + + if (dst->flags & MODULE_FLAG_STYLE_UTC) flags = MODULE_FLAG_STYLE_UTC; + else if (dst->flags & MODULE_FLAG_STYLE_UTC_B) flags = MODULE_FLAG_STYLE_UTC_B; + else if (dst->flags & MODULE_FLAG_STYLE_LCL) flags = MODULE_FLAG_STYLE_LCL; + else if (dst->flags & MODULE_FLAG_STYLE_LCL_B) flags = MODULE_FLAG_STYLE_LCL_B; + + if ((dst->rotate_dir == NULL) && ((dst->flags & MODULE_FLAG_STYLE_SEQ) == 0) && ((dst->flags & MODULE_FLAG_COMPRESS) == 0)) + { + /* files do not move to a dest dir, get renamed, or get compressed - nothing to do */ + return NULL; + } + + base = strrchr(dst->path, '/'); + if (base == NULL) return NULL; + + *base = '\0'; + base++; + + out = asl_list_log_files(dst->path, base, true, flags); + + if (base != NULL) *--base = '/'; + + return out; +} + +/* + * List the destination files for an output asl_out_dst_data_t + */ +asl_out_file_list_t * +asl_list_dst_files(asl_out_dst_data_t *dst) +{ + char *base, *dst_dir; + asl_out_file_list_t *out; + + if (dst == NULL) return NULL; + if (dst->path == NULL) return NULL; + + base = strrchr(dst->path, '/'); + if (base == NULL) return NULL; + + *base = '\0'; + base++; + + dst_dir = dst->rotate_dir; + if (dst_dir == NULL) dst_dir = dst->path; + + out = asl_list_log_files(dst_dir, base, false, dst->flags); + + if (base != NULL) *--base = '/'; + + return out; +} diff --git a/aslcommon/asl_common.h b/aslcommon/asl_common.h new file mode 100644 index 0000000..816b591 --- /dev/null +++ b/aslcommon/asl_common.h @@ -0,0 +1,178 @@ +/* + * Copyright (c) 2012 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#ifndef __ASL_COMMON_H__ +#define __ASL_COMMON_H__ + +#include +#include + +#define ASL_MODULE_NAME "com.apple.asl" +#define _PATH_CRASHREPORTER "/Library/Logs/CrashReporter" + +#define ASL_SERVICE_NAME "com.apple.system.logger" + +#define CRASH_MOVER_WILL_START_NOTIFICATION "CrashMoverWillStart" + +#define DEFAULT_TTL 7 /* days */ +#define SECONDS_PER_DAY 86400 + +#define ACTION_NONE 0 +#define ACTION_SET_PARAM 1 +#define ACTION_OUT_DEST 2 +#define ACTION_IGNORE 3 +#define ACTION_SKIP 4 +#define ACTION_CLAIM 5 +#define ACTION_NOTIFY 6 +#define ACTION_BROADCAST 7 +#define ACTION_ACCESS 8 +#define ACTION_ASL_STORE 9 /* Save in main ASL Database */ +#define ACTION_ASL_FILE 10 /* Save in an ASL format data file */ +#define ACTION_ASL_DIR 11 /* Save in an ASL directory */ +#define ACTION_FILE 12 /* Save in a text file */ +#define ACTION_FORWARD 13 +#define ACTION_CONTROL 14 +#define ACTION_SET_FILE 15 /* = foo [File /a/b/c] */ +#define ACTION_SET_PLIST 16 /* = foo [Plist /a/b/c] ... */ +#define ACTION_SET_PROF 17 /* = foo [Profile abc] ... */ + +#define STYLE_SEC_PREFIX_CHAR 'T' + +#define MODULE_FLAG_HAS_LOGGED 0x80000000 +#define MODULE_FLAG_CLEAR_LOGGED 0x7fffffff +#define MODULE_FLAG_ENABLED 0x00000001 +#define MODULE_FLAG_LOCAL 0x00000002 +#define MODULE_FLAG_ROTATE 0x00000004 +#define MODULE_FLAG_COALESCE 0x00000008 +#define MODULE_FLAG_COMPRESS 0x00000010 +#define MODULE_FLAG_EXTERNAL 0x00000020 +#define MODULE_FLAG_STYLE_SEC 0x00000100 /* foo.T1332799722 (note STYLE_SEC_PREFIX_CHAR) */ +#define MODULE_FLAG_STYLE_SEQ 0x00000200 /* foo.0 */ +#define MODULE_FLAG_STYLE_UTC 0x00000400 /* foo.2012-04-06T13:45:00Z */ +#define MODULE_FLAG_STYLE_UTC_B 0x00000800 /* ("basic utc") foo.20120406T134500Z */ +#define MODULE_FLAG_STYLE_LCL 0x00001000 /* foo.2012-04-06T13:45:00-7 */ +#define MODULE_FLAG_STYLE_LCL_B 0x00002000 /* ("basic local") foo.20120406T134500-7 */ +#define MODULE_FLAG_BASESTAMP 0x00004000 /* base file has timestamp */ +#define MODULE_FLAG_CRASHLOG 0x00008000 /* checkpoint on CrashMoverWillStart notification */ +#define MODULE_FLAG_SOFT_WRITE 0x00010000 /* ignore write failures */ +#define MODULE_FLAG_TYPE_ASL 0x00020000 /* asl format file */ +#define MODULE_FLAG_TYPE_ASL_DIR 0x00040000 /* asl format directory */ +#define MODULE_FLAG_STD_BSD_MSG 0x00080000 /* print format is std, bsd, or msg */ + +#define CHECKPOINT_TEST 0x00000000 +#define CHECKPOINT_FORCE 0xffffffff +#define CHECKPOINT_SIZE 0x00000001 +#define CHECKPOINT_TIME 0x00000002 +#define CHECKPOINT_CRASH 0x00000004 + +typedef struct +{ + char *path; + char *fname; + char *fmt; + const char *tfmt; + char *rotate_dir; + uint32_t flags; + uint32_t fails; + uint32_t ttl; + mode_t mode; +#if !TARGET_IPHONE_SIMULATOR + uid_t *uid; + uint32_t nuid; + gid_t *gid; + uint32_t ngid; +#endif + size_t file_max; + size_t all_max; + uint32_t refcount; + time_t stamp; + size_t size; + void *private; +} asl_out_dst_data_t; + +typedef struct asl_out_rule_s +{ + asl_msg_t *query; + uint32_t action; + char *options; + asl_out_dst_data_t *dst; + void *private; + struct asl_out_rule_s *next; +} asl_out_rule_t; + +typedef struct asl_out_module_s +{ + char *name; + uint32_t flags; + asl_out_rule_t *ruleset; + struct asl_out_module_s *next; +} asl_out_module_t; + +typedef struct asl_out_file_list_s +{ + char *name; + time_t ftime; + uint32_t seq; + size_t size; + struct asl_out_file_list_s *next; + struct asl_out_file_list_s *prev; +} asl_out_file_list_t; + +char **explode(const char *s, const char *delim); +void free_string_list(char **l); +char *get_line_from_file(FILE *f); +char *next_word_from_string(char **s); +size_t asl_str_to_size(char *s); +asl_msg_t *xpc_object_to_asl_msg(xpc_object_t xobj); + +int asl_check_option(aslmsg msg, const char *opt); + +/* ASL OUT MODULES */ +asl_out_module_t *asl_out_module_new(const char *name); +void asl_out_module_free(asl_out_module_t *m); +asl_out_module_t *asl_out_module_init(void); +asl_out_module_t *asl_out_module_init_from_file(const char *name, FILE *f); +asl_out_rule_t *asl_out_module_parse_line(asl_out_module_t *m, char *s); + +void asl_out_module_print(FILE *f, asl_out_module_t *m); +char *asl_out_module_rule_to_string(asl_out_rule_t *r); + +int asl_out_mkpath(asl_out_rule_t *r); +int asl_out_dst_checkpoint(asl_out_dst_data_t *dst, uint32_t force); +int asl_out_dst_file_create_open(asl_out_dst_data_t *dst); +int asl_out_dst_set_access(int fd, asl_out_dst_data_t *dst); +void asl_make_timestamp(time_t stamp, uint32_t flags, char *buf, size_t len); +void asl_make_dst_filename(asl_out_dst_data_t *dst, char *buf, size_t len); + +asl_out_dst_data_t *asl_out_dst_data_retain(asl_out_dst_data_t *dst); +void asl_out_dst_data_release(asl_out_dst_data_t *dst); + +/* rotated log files */ +asl_out_file_list_t *asl_list_log_files(const char *dir, const char *base, bool src, uint32_t flags); +asl_out_file_list_t * asl_list_src_files(asl_out_dst_data_t *dst); +asl_out_file_list_t * asl_list_dst_files(asl_out_dst_data_t *dst); +void asl_out_file_list_free(asl_out_file_list_t *l); + +asl_msg_t *configuration_profile_to_asl_msg(const char *ident); + +#endif /* __ASL_COMMON_H__ */ diff --git a/aslcommon/asl_ipc.defs b/aslcommon/asl_ipc.defs new file mode 100644 index 0000000..360e464 --- /dev/null +++ b/aslcommon/asl_ipc.defs @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2007-2011 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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, 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 +#include + +subsystem asl_ipc 114; +serverprefix _; + +import ; + +type ooline_data = ^ array [] of MACH_MSG_TYPE_BYTE + ctype : caddr_t; + +routine _asl_server_query +( + server : mach_port_t; + request : ooline_data, dealloc; + startid : uint64_t; + count : int; + flags : int; + out reply : ooline_data, dealloc; + out lastid : uint64_t; + out status : int; + SecToken token : security_token_t +); + +routine _asl_server_query_timeout +( + server : mach_port_t; + request : ooline_data, dealloc; + startid : uint64_t; + count : int; + flags : int; + WaitTime timeout: natural_t; + out reply : ooline_data, dealloc; + out lastid : uint64_t; + out status : int; + SecToken token : security_token_t +); + +routine _asl_server_prune +( + server : mach_port_t; + request : ooline_data, dealloc; + out status : int; + SecToken token : security_token_t +); + +routine _asl_server_create_aux_link +( + server : mach_port_t; + message : ooline_data, dealloc; + out fileport : mach_port_move_send_t; + out url : ooline_data, dealloc; + out status : int; + ServerAuditToken token : audit_token_t +); + +simpleroutine _asl_server_message +( + server : mach_port_t; + message : ooline_data, dealloc; + ServerAuditToken token : audit_token_t +); + +simpleroutine _asl_server_register_direct_watch +( + server : mach_port_t; + port : int; + ServerAuditToken token : audit_token_t +); + +simpleroutine _asl_server_cancel_direct_watch +( + server : mach_port_t; + port : int; + ServerAuditToken token : audit_token_t +); + +routine _asl_server_query_2 +( + server : mach_port_t; + request : ooline_data, dealloc; + startid : uint64_t; + count : int; + flags : int; + out reply : ooline_data, dealloc; + out lastid : uint64_t; + out status : int; + ServerAuditToken token : audit_token_t +); diff --git a/aslcommon/asl_memory.c b/aslcommon/asl_memory.c index f0f9478..1bf140f 100644 --- a/aslcommon/asl_memory.c +++ b/aslcommon/asl_memory.c @@ -248,6 +248,7 @@ static mem_string_t * asl_memory_string_retain(asl_memory_t *s, const char *str, int create) { uint32_t i, where, hash, len; + mem_string_t *new; if (s == NULL) return NULL; if (str == NULL) return NULL; @@ -292,8 +293,11 @@ asl_memory_string_retain(asl_memory_t *s, const char *str, int create) return NULL; } + new = mem_string_new(str, len, hash); + if (new == NULL) return NULL; + + s->string_cache[where] = new; s->string_count++; - s->string_cache[where] = mem_string_new(str, len, hash); return s->string_cache[where]; } @@ -355,6 +359,7 @@ asl_memory_record_clear(asl_memory_t *s, mem_record_t *r) asl_memory_string_release(s, r->host); asl_memory_string_release(s, r->sender); + asl_memory_string_release(s, r->sender_mach_uuid); asl_memory_string_release(s, r->facility); asl_memory_string_release(s, r->message); asl_memory_string_release(s, r->refproc); @@ -450,6 +455,10 @@ asl_memory_message_encode(asl_memory_t *s, aslmsg msg) { if (val != NULL) r->message = asl_memory_string_retain(s, val, 1); } + else if (!strcmp(key, ASL_KEY_SENDER_MACH_UUID)) + { + if (val != NULL) r->sender_mach_uuid = asl_memory_string_retain(s, val, 1); + } else if (!strcmp(key, ASL_KEY_FACILITY)) { if (val != NULL) r->facility = asl_memory_string_retain(s, val, 1); @@ -497,7 +506,7 @@ asl_memory_message_encode(asl_memory_t *s, aslmsg msg) } else { - r->kvlist = (mem_string_t **)realloc(r->kvlist, (r->kvcount + 2) * sizeof(mem_string_t *)); + r->kvlist = (mem_string_t **)reallocf(r->kvlist, (r->kvcount + 2) * sizeof(mem_string_t *)); } if (r->kvlist == NULL) @@ -606,6 +615,12 @@ asl_memory_message_decode(asl_memory_t *s, mem_record_t *r, aslmsg *out) asl_set(msg, ASL_KEY_SENDER, r->sender->str); } + /* Sender mach UUID */ + if (r->sender_mach_uuid != NULL) + { + asl_set(msg, ASL_KEY_SENDER_MACH_UUID, r->sender_mach_uuid->str); + } + /* Facility */ if (r->facility != NULL) { @@ -933,6 +948,26 @@ asl_memory_query_to_record(asl_memory_t *s, aslmsg q, uint32_t *type) return NULL; } } + else if (!strcmp(key, ASL_KEY_SENDER_MACH_UUID)) + { + if (val == NULL) continue; + + if (*type & ASL_QUERY_MATCH_SMUUID) + { + asl_memory_record_free(s, out); + *type = ASL_QUERY_MATCH_SLOW; + return NULL; + } + + *type |= ASL_QUERY_MATCH_SMUUID; + out->sender = asl_memory_string_retain(s, val, 0); + if (out->sender_mach_uuid == NULL) + { + asl_memory_record_free(s, out); + *type = ASL_QUERY_MATCH_FALSE; + return NULL; + } + } else if (!strcmp(key, ASL_KEY_FACILITY)) { if (val == NULL) continue; @@ -1041,7 +1076,7 @@ asl_memory_query_to_record(asl_memory_t *s, aslmsg q, uint32_t *type) } else { - out->kvlist = (mem_string_t **)realloc(out->kvlist, (out->kvcount + 2) * sizeof(mem_string_t *)); + out->kvlist = (mem_string_t **)reallocf(out->kvlist, (out->kvcount + 2) * sizeof(mem_string_t *)); } if (out->kvlist == NULL) @@ -1080,6 +1115,7 @@ asl_memory_fast_match(asl_memory_t *s, mem_record_t *r, uint32_t qtype, mem_reco if ((qtype & ASL_QUERY_MATCH_REF_PID) && (q->refpid != r->refpid)) return 0; if ((qtype & ASL_QUERY_MATCH_HOST) && (q->host != r->host)) return 0; if ((qtype & ASL_QUERY_MATCH_SENDER) && (q->sender != r->sender)) return 0; + if ((qtype & ASL_QUERY_MATCH_SMUUID) && (q->sender_mach_uuid != r->sender_mach_uuid)) return 0; if ((qtype & ASL_QUERY_MATCH_FACILITY) && (q->facility != r->facility)) return 0; if ((qtype & ASL_QUERY_MATCH_MESSAGE) && (q->message != r->message)) return 0; if ((qtype & ASL_QUERY_MATCH_REF_PROC) && (q->refproc != r->refproc)) return 0; @@ -1119,7 +1155,7 @@ asl_memory_slow_match(asl_memory_t *s, mem_record_t *r, aslmsg rawq) } uint32_t -asl_memory_match(asl_memory_t *s, aslresponse query, aslresponse *res, uint64_t *last_id, uint64_t start_id, uint32_t count, int32_t direction, int32_t ruid, int32_t rgid) +asl_memory_match_restricted_uuid(asl_memory_t *s, aslresponse query, aslresponse *res, uint64_t *last_id, uint64_t start_id, uint32_t count, int32_t direction, int32_t ruid, int32_t rgid, const char *uuid_str) { uint32_t status, i, where, start, j, do_match, did_match, rescount, *qtype; mem_record_t **qp; @@ -1201,6 +1237,13 @@ asl_memory_match(asl_memory_t *s, aslresponse query, aslresponse *res, uint64_t { status = ASL_STATUS_INVALID_ID; if (s->record[where]->mid != 0) status = asl_core_check_access(s->record[where]->ruid, s->record[where]->rgid, ruid, rgid, s->record[where]->flags); + + if ((status == ASL_STATUS_OK) && (uuid_str != NULL)) + { + if (s->record[where]->sender_mach_uuid == NULL) status = ASL_STATUS_INVALID_ID; + else if (strcmp(s->record[where]->sender_mach_uuid->str, uuid_str) != 0) status = ASL_STATUS_INVALID_ID; + } + if (status != ASL_STATUS_OK) { if (direction >= 0) @@ -1327,3 +1370,9 @@ asl_memory_match(asl_memory_t *s, aslresponse query, aslresponse *res, uint64_t (*res)->curr = 0; return ASL_STATUS_OK; } + +uint32_t +asl_memory_match(asl_memory_t *s, aslresponse query, aslresponse *res, uint64_t *last_id, uint64_t start_id, uint32_t count, int32_t direction, int32_t ruid, int32_t rgid) +{ + return asl_memory_match_restricted_uuid(s, query, res, last_id, start_id, count, direction, ruid, rgid, NULL); +} diff --git a/aslcommon/asl_memory.h b/aslcommon/asl_memory.h index a39821d..e838ccd 100644 --- a/aslcommon/asl_memory.h +++ b/aslcommon/asl_memory.h @@ -50,6 +50,7 @@ typedef struct uint32_t kvcount; mem_string_t *host; mem_string_t *sender; + mem_string_t *sender_mach_uuid; mem_string_t *facility; mem_string_t *message; mem_string_t *refproc; @@ -75,5 +76,6 @@ uint32_t asl_memory_save(asl_memory_t *s, aslmsg msg, uint64_t *mid); uint32_t asl_memory_fetch(asl_memory_t *s, uint64_t mid, aslmsg *msg, int32_t ruid, int32_t rgid); uint32_t asl_memory_match(asl_memory_t *s, aslresponse query, aslresponse *res, uint64_t *last_id, uint64_t start_id, uint32_t count, int32_t direction, int32_t ruid, int32_t rgid); +uint32_t asl_memory_match_restricted_uuid(asl_memory_t *s, aslresponse query, aslresponse *res, uint64_t *last_id, uint64_t start_id, uint32_t count, int32_t direction, int32_t ruid, int32_t rgid, const char *uuid_str); #endif /* __ASL_MEMORY_H__ */ diff --git a/aslcommon/asl_mini_memory.c b/aslcommon/asl_mini_memory.c index 5d9d79a..0651cb8 100644 --- a/aslcommon/asl_mini_memory.c +++ b/aslcommon/asl_mini_memory.c @@ -253,6 +253,7 @@ static mini_mem_string_t * asl_mini_memory_string_retain(asl_mini_memory_t *s, const char *str, int create) { uint32_t i, where, hash, len; + mini_mem_string_t *new; if (s == NULL) return NULL; if (str == NULL) return NULL; @@ -297,8 +298,11 @@ asl_mini_memory_string_retain(asl_mini_memory_t *s, const char *str, int create) return NULL; } + new = mem_string_new(str, len, hash); + if (new == NULL) return NULL; + + s->string_cache[where] = new; s->string_count++; - s->string_cache[where] = mem_string_new(str, len, hash); return s->string_cache[where]; } @@ -359,6 +363,7 @@ asl_mini_memory_record_clear(asl_mini_memory_t *s, mini_mem_record_t *r) if (r == NULL) return; asl_mini_memory_string_release(s, r->sender); + asl_mini_memory_string_release(s, r->sender_mach_uuid); asl_mini_memory_string_release(s, r->facility); asl_mini_memory_string_release(s, r->message); @@ -398,6 +403,8 @@ asl_mini_memory_message_encode(asl_mini_memory_t *s, aslmsg msg) r->flags = 0; r->level = ASL_LEVEL_DEBUG; r->pid = -1; + r->ruid = -1; + r->rgid = -1; r->time = (uint64_t)-1; r->nano = (uint32_t)-1; @@ -420,6 +427,10 @@ asl_mini_memory_message_encode(asl_mini_memory_t *s, aslmsg msg) { if (val != NULL) r->sender = asl_mini_memory_string_retain(s, val, 1); } + else if (!strcmp(key, ASL_KEY_SENDER_MACH_UUID)) + { + if (val != NULL) r->sender_mach_uuid = asl_mini_memory_string_retain(s, val, 1); + } else if (!strcmp(key, ASL_KEY_PID)) { if (val != NULL) r->pid = atoi(val); @@ -478,13 +489,19 @@ asl_mini_memory_message_encode(asl_mini_memory_t *s, aslmsg msg) } else if (!strcmp(key, ASL_KEY_READ_UID)) { - /* Ignore */ - continue; + if (((r->flags & ASL_MSG_FLAG_READ_UID_SET) == 0) && (val != NULL)) + { + r->ruid = atoi(val); + r->flags |= ASL_MSG_FLAG_READ_UID_SET; + } } else if (!strcmp(key, ASL_KEY_READ_GID)) { - /* Ignore */ - continue; + if (((r->flags & ASL_MSG_FLAG_READ_GID_SET) == 0) && (val != NULL)) + { + r->rgid = atoi(val); + r->flags |= ASL_MSG_FLAG_READ_GID_SET; + } } else if (!strcmp(key, CFLOG_LOCAL_TIME_KEY)) { @@ -510,7 +527,7 @@ asl_mini_memory_message_encode(asl_mini_memory_t *s, aslmsg msg) } else { - r->kvlist = (mini_mem_string_t **)realloc(r->kvlist, (r->kvcount + 2) * sizeof(mini_mem_string_t *)); + r->kvlist = (mini_mem_string_t **)reallocf(r->kvlist, (r->kvcount + 2) * sizeof(mini_mem_string_t *)); } if (r->kvlist == NULL) @@ -608,6 +625,12 @@ asl_mini_memory_message_decode(asl_mini_memory_t *s, mini_mem_record_t *r, aslms asl_set(msg, ASL_KEY_SENDER, r->sender->str); } + /* Sender mach UUID */ + if (r->sender_mach_uuid != NULL) + { + asl_set(msg, ASL_KEY_SENDER_MACH_UUID, r->sender_mach_uuid->str); + } + /* Facility */ if (r->facility != NULL) { @@ -627,6 +650,20 @@ asl_mini_memory_message_decode(asl_mini_memory_t *s, mini_mem_record_t *r, aslms asl_set(msg, ASL_KEY_MSG, r->message->str); } + /* ReadUID */ + if (r->flags & ASL_MSG_FLAG_READ_UID_SET) + { + snprintf(tmp, sizeof(tmp), "%d", r->ruid); + asl_set(msg, ASL_KEY_READ_UID, tmp); + } + + /* ReadGID */ + if (r->flags & ASL_MSG_FLAG_READ_GID_SET) + { + snprintf(tmp, sizeof(tmp), "%d", r->rgid); + asl_set(msg, ASL_KEY_READ_GID, tmp); + } + /* Key - Value List */ for (i = 0; i < r->kvcount; i++) { @@ -645,9 +682,9 @@ asl_mini_memory_message_decode(asl_mini_memory_t *s, mini_mem_record_t *r, aslms } uint32_t -asl_mini_memory_fetch(asl_mini_memory_t *s, uint64_t mid, aslmsg *msg) +asl_mini_memory_fetch(asl_mini_memory_t *s, uint64_t mid, aslmsg *msg, int32_t ruid, int32_t rgid) { - uint32_t i; + uint32_t i, status; if (s == NULL) return ASL_STATUS_INVALID_STORE; if (msg == NULL) return ASL_STATUS_INVALID_ARG; @@ -655,7 +692,13 @@ asl_mini_memory_fetch(asl_mini_memory_t *s, uint64_t mid, aslmsg *msg) for (i = 0; i < s->record_count; i++) { if (s->record[i]->mid == 0) break; - if (s->record[i]->mid == mid) return asl_mini_memory_message_decode(s, s->record[i], msg); + + if (s->record[i]->mid == mid) + { + status = asl_core_check_access(s->record[i]->ruid, s->record[i]->rgid, ruid, rgid, s->record[i]->flags); + if (status != ASL_STATUS_OK) return status; + return asl_mini_memory_message_decode(s, s->record[i], msg); + } } return ASL_STATUS_INVALID_ID; @@ -772,6 +815,34 @@ asl_mini_memory_query_to_record(asl_mini_memory_t *s, asl_msg_t *q, uint32_t *ty *type |= ASL_QUERY_MATCH_PID; out->pid = atoi(val); } + else if (!strcmp(key, ASL_KEY_READ_UID)) + { + if (val == NULL) continue; + + if (*type & ASL_QUERY_MATCH_RUID) + { + asl_mini_memory_record_free(s, out); + *type = ASL_QUERY_MATCH_SLOW; + return NULL; + } + + *type |= ASL_QUERY_MATCH_RUID; + out->ruid = atoi(val); + } + else if (!strcmp(key, ASL_KEY_READ_GID)) + { + if (val == NULL) continue; + + if (*type & ASL_QUERY_MATCH_RGID) + { + asl_mini_memory_record_free(s, out); + *type = ASL_QUERY_MATCH_SLOW; + return NULL; + } + + *type |= ASL_QUERY_MATCH_RGID; + out->rgid = atoi(val); + } else if (!strcmp(key, ASL_KEY_SENDER)) { if (val == NULL) continue; @@ -792,6 +863,26 @@ asl_mini_memory_query_to_record(asl_mini_memory_t *s, asl_msg_t *q, uint32_t *ty return NULL; } } + else if (!strcmp(key, ASL_KEY_SENDER_MACH_UUID)) + { + if (val == NULL) continue; + + if (*type & ASL_QUERY_MATCH_SMUUID) + { + asl_mini_memory_record_free(s, out); + *type = ASL_QUERY_MATCH_SLOW; + return NULL; + } + + *type |= ASL_QUERY_MATCH_SMUUID; + out->sender_mach_uuid = asl_mini_memory_string_retain(s, val, 0); + if (out->sender_mach_uuid == NULL) + { + asl_mini_memory_record_free(s, out); + *type = ASL_QUERY_MATCH_FALSE; + return NULL; + } + } else if (!strcmp(key, ASL_KEY_FACILITY)) { if (val == NULL) continue; @@ -860,7 +951,7 @@ asl_mini_memory_query_to_record(asl_mini_memory_t *s, asl_msg_t *q, uint32_t *ty } else { - out->kvlist = (mini_mem_string_t **)realloc(out->kvlist, (out->kvcount + 2) * sizeof(mini_mem_string_t *)); + out->kvlist = (mini_mem_string_t **)reallocf(out->kvlist, (out->kvcount + 2) * sizeof(mini_mem_string_t *)); } if (out->kvlist == NULL) @@ -892,7 +983,10 @@ asl_mini_memory_fast_match(asl_mini_memory_t *s, mini_mem_record_t *r, uint32_t if ((qtype & ASL_QUERY_MATCH_NANO) && (q->nano != r->nano)) return 0; if ((qtype & ASL_QUERY_MATCH_LEVEL) && (q->level != r->level)) return 0; if ((qtype & ASL_QUERY_MATCH_PID) && (q->pid != r->pid)) return 0; + if ((qtype & ASL_QUERY_MATCH_RUID) && (q->ruid != r->ruid)) return 0; + if ((qtype & ASL_QUERY_MATCH_RGID) && (q->rgid != r->rgid)) return 0; if ((qtype & ASL_QUERY_MATCH_SENDER) && (q->sender != r->sender)) return 0; + if ((qtype & ASL_QUERY_MATCH_SMUUID) && (q->sender_mach_uuid != r->sender_mach_uuid)) return 0; if ((qtype & ASL_QUERY_MATCH_FACILITY) && (q->facility != r->facility)) return 0; if ((qtype & ASL_QUERY_MATCH_MESSAGE) && (q->message != r->message)) return 0; @@ -930,7 +1024,7 @@ asl_mini_memory_slow_match(asl_mini_memory_t *s, mini_mem_record_t *r, mini_mem_ } uint32_t -asl_mini_memory_match(asl_mini_memory_t *s, aslresponse query, aslresponse *res, uint64_t *last_id, uint64_t start_id, uint32_t count, int32_t direction) +asl_mini_memory_match_restricted_uuid(asl_mini_memory_t *s, aslresponse query, aslresponse *res, uint64_t *last_id, uint64_t start_id, uint32_t count, int32_t direction, int32_t ruid, int32_t rgid, const char *uuid_str) { uint32_t status, i, where, start, j, do_match, did_match, rescount, *qtype; mini_mem_record_t **qp; @@ -1010,7 +1104,16 @@ asl_mini_memory_match(asl_mini_memory_t *s, aslresponse query, aslresponse *res, */ for (i = 0; i < s->record_count; i++) { - if (s->record[where]->mid == 0) + status = ASL_STATUS_INVALID_ID; + if (s->record[where]->mid != 0) status = asl_core_check_access(s->record[where]->ruid, s->record[where]->rgid, ruid, rgid, s->record[where]->flags); + + if ((status == ASL_STATUS_OK) && (uuid_str != NULL)) + { + if (s->record[where]->sender_mach_uuid == NULL) status = ASL_STATUS_INVALID_ID; + else if (strcmp(s->record[where]->sender_mach_uuid->str, uuid_str) != 0) status = ASL_STATUS_INVALID_ID; + } + + if (status != ASL_STATUS_OK) { if (direction >= 0) { @@ -1136,3 +1239,9 @@ asl_mini_memory_match(asl_mini_memory_t *s, aslresponse query, aslresponse *res, (*res)->curr = 0; return ASL_STATUS_OK; } + +uint32_t +asl_mini_memory_match(asl_mini_memory_t *s, aslresponse query, aslresponse *res, uint64_t *last_id, uint64_t start_id, uint32_t count, int32_t direction, int32_t ruid, int32_t rgid) +{ + return asl_mini_memory_match_restricted_uuid(s, query, res, last_id, start_id, count, direction, ruid, rgid, NULL); +} diff --git a/aslcommon/asl_mini_memory.h b/aslcommon/asl_mini_memory.h index e727223..7e81608 100644 --- a/aslcommon/asl_mini_memory.h +++ b/aslcommon/asl_mini_memory.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007-2011 Apple Inc. All rights reserved. + * Copyright (c) 2007-2012 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -46,8 +46,11 @@ typedef struct uint32_t pid; uint32_t uid; uint32_t gid; + uint32_t ruid; + uint32_t rgid; uint32_t kvcount; mini_mem_string_t *sender; + mini_mem_string_t *sender_mach_uuid; mini_mem_string_t *facility; mini_mem_string_t *message; mini_mem_string_t **kvlist; @@ -69,8 +72,9 @@ uint32_t asl_mini_memory_close(asl_mini_memory_t *s); uint32_t asl_mini_memory_statistics(asl_mini_memory_t *s, aslmsg *msg); uint32_t asl_mini_memory_save(asl_mini_memory_t *s, aslmsg msg, uint64_t *mid); -uint32_t asl_mini_memory_fetch(asl_mini_memory_t *s, uint64_t mid, aslmsg *msg); +uint32_t asl_mini_memory_fetch(asl_mini_memory_t *s, uint64_t mid, aslmsg *msg, int32_t ruid, int32_t rgid); -uint32_t asl_mini_memory_match(asl_mini_memory_t *s, aslresponse query, aslresponse *res, uint64_t *last_id, uint64_t start_id, uint32_t count, int32_t direction); +uint32_t asl_mini_memory_match(asl_mini_memory_t *s, aslresponse query, aslresponse *res, uint64_t *last_id, uint64_t start_id, uint32_t count, int32_t direction, int32_t ruid, int32_t rgid); +uint32_t asl_mini_memory_match_restricted_uuid(asl_mini_memory_t *s, aslresponse query, aslresponse *res, uint64_t *last_id, uint64_t start_id, uint32_t count, int32_t direction, int32_t ruid, int32_t rgid, const char *uuid_str); #endif /* __ASL_MINI_MEMORY_H__ */ diff --git a/aslmanager.tproj/aslmanager.8 b/aslmanager.tproj/aslmanager.8 index 4bb4ad3..925dfbf 100644 --- a/aslmanager.tproj/aslmanager.8 +++ b/aslmanager.tproj/aslmanager.8 @@ -24,59 +24,112 @@ .Os "Mac OS X" .Sh NAME .Nm aslmanager -.Nd Apple System Log data store file manager +.Nd Apple System Log data life-cycle manager .Sh SYNOPSIS .Nm .Op Fl s Ar store_dir .Op Fl a Op Ar archive_dir .Op Fl ttl Ar days +.Op Fl store_ttl Ar days +.Op Fl module_ttl Ar days .Op Fl size Ar max_size +.Op Fl asldb +.Op Fl module Op Ar name +.Op Fl checkpoint +.Op Fl d Op level +.Op Fl dd Op level .Sh DESCRIPTION -.Nm -manages files in the ASL data store written by the +.Nm aslmanager +manages rotated files and ASL data written by the .Nm syslogd server. +It is started automatically at various times by +.Nm syslogd . +It may also be invoked from the command line by the superuser (root). +It manages the life-cycle of data in the ASL database, rotated log files, +and ASL directory data stores. +Configuration information for +.Nm aslmanager +comes from the /etc/asl.conf configuration file, +any ASL output module configuration files in the /etc/asl directory, +and from command-line options that may be specified to override some settings +found in the configuration files. +In normal operation, it first checks the ASL database in /var/log/asl, +then it checks the files and directories specified by /etc/asl.conf and each ASL output module. .Pp -.Nm -is started automatically at various times. -It runs shortly after the -.Nm syslogd -server starts, upon receipt of the first message after midnight (local time), -and any time a file in the ASL data store directory (/var/log/asl) reaches a maximum size limit as specified to the -.Nm syslogd -server. +If the +.Fl asldb +flag is specified, then only the ASL database will be processed. .Pp -Command-line options may be specified in the aslmanager launch plist file. -However, -.Nm -reads the /etc/asl.conf file which may also contain parameter settings. -Settings in the asl.conf file will override those given on the command line. +If the +.Fl module +flag is specified, then the ASL database will not be processed. +All ASL output modules, or a single module if +.Ar name +is specified, will be processed. +.Pp +If +.Fl checkpoint +is specified, then +.Nm aslmanager +will signal +.Nm syslogd +to checkpoint files for all modules, or for a single named module, before processing. .Pp +The +.Fl d +flag causes .Nm -scans through the files in the ASL data store directory /var/log/asl, or some other directory specified following the +to print debug messages tracing it's actions as they are performed. +An integer value (1, 2, or 3) may follow the +.Fl d +flag. +Higher values cause more fine-grained messages to be printed. +The default value is 1. +The +.Fl dd +flag directs +.Nm +to do a +.Dq dry run . +Debug messages are printed as with +.Fl d , +but no actions are actually performed. +An optional debug level may follow the +.Fl dd +flag. +.Ss ASL DATABASE MANAGEMENT +.Nm aslmanager +scans the ASL database in /var/log/asl, or some other path specified by the setting of the +.Dq store_path +parameter in asl.conf, or by the path supplied following the .Fl s -flag, or by the setting of the store_path parameter in asl.conf. -Data files that are older than the default 7 day time-to-live are either archived or removed. -Files that contain messages with explicit expire times are removed or archived monthly after all thier contents expire. -The default 7 day time-to-live value may be overridden by supplying a value following the -.Fl ttl -flag, or by the setting of the store_ttl parameter in asl.conf. -A value of zero allows files to remain in the store with no time limit. +flag. +Data files that are older than the time-to-live for the database are either archived or removed. +Files that contain messages with explicit expire times are removed or archived monthly after all their contents expire. +The default 7 day time-to-live value may be overridden by the setting of the +.Dq store_ttl +parameter in asl.conf or by supplying a value following the +.Fl store_ttl +flag or the +.Fl ttl +flag (which overrides the time-to-live for both the ASL database and for all ASL modules). +A time-to-live value of zero allows files to remain in the store with no time limit. .Pp -A maximum size for the entire data store may be provided as a value following the +A maximum size for the entire database is provided by the setting of the +.Dq max_store_size +parameter in asl.conf, or as a value following the .Fl size -flag, or by the setting of the max_store_size parameter in asl.conf. +flag. This will cause .Nm -to archive (if enabled) and remove files until the total size of the data store is -.Ar max_size -bytes or less. +to archive (if enabled) and remove files to keep the database size below the specified limit. The default value is 150000000 bytes. A value of zero means the size is unlimited. An unlimited size specification should be used with great caution, since a runaway process could quickly fill all available disk space. Files are removed in order starting from oldest to newest. -Files with the same date are removed in standard lexigraphic sort order by file name. +Files with the same date are removed in standard lexicographic sort order by file name. .Pp Files are either removed entirely or copied to an archive directory. If the @@ -88,6 +141,56 @@ flag. The archive parameter setting in asl.conf enables or disables archiving. The archive parameter requires a value of "1" to enable archiving, or a value of "0" to disable it. An option archive directory path may follow the "0" or "1". +.Pp +.Ss ASL OUTPUT MODULE MANAGEMENT +For each ASL output module, or a single module specified as an argument following +.Fl module , +.Nm aslmanager +first locates all checkpoint files produced by +.Nm syslogd +for that module. +.Nm aslmanager +checks all ASL directory data stores and all rotated log files - those with a +.Dq rotate +option in the module's configuration rules. +Checkpoint files are renamed if necessary to conform to the naming style specified for the file by a +.Dq style +option in the module's configuration file. +.Nm aslmanager +will compress the file if directed by a +.Dq compress +option, and it will move the file to a destination directory if a +.Dq dest +option is specified for the file. +Following this, +.Nm +will delete expired files. +The time-to-live for files is 7 days by default, but may be specified using the +.Dq ttl +option for the file in the module's configuration rules. +If +.Fl module_ttl +or +.Fl ttl +are specified command line, then value specified as an argument is used as a time-to-live instead. +.Fl module_ttl +specifies time-to-live for module processing. +.Fl ttl +specifies time-to-live for both the ASL database and for modules. +.Pp +Finally, if the +.Dq all_max +option is specified for the output file, +.Nm +checks the total size of all the rotated versions, +and will delete them, (oldest first) to limit the total size as specified by +.Dq all_max . +When processing ASL directory data stores, +.Nm +will similarly delete data files after the expiry of their time-to-live, +and will delete data files (oldest first) to limit the total size as specified by +.Dq all_max . +.Pp .Sh SEE ALSO .Xr syslogd 8 , .Xr syslog 1 , @@ -98,3 +201,4 @@ An option archive directory path may follow the "0" or "1". The .Nm utility appeared in Mac OS X 10.6. +Support for log file and ASL directory life-cycle management was added in OS X 10.9. diff --git a/aslmanager.tproj/aslmanager.c b/aslmanager.tproj/aslmanager.c index b85fc93..a2c4f76 100644 --- a/aslmanager.tproj/aslmanager.c +++ b/aslmanager.tproj/aslmanager.c @@ -31,26 +31,60 @@ #include #include #include +#include +#include +#include +#include #include +#include +#include +#include +#include +#include +#include #include #include #include #include #include +#include "asl_common.h" -#define SECONDS_PER_DAY 86400 #define DEFAULT_MAX_SIZE 150000000 -#define DEFAULT_TTL 7 - -#define _PATH_ASL_CONF "/etc/asl.conf" +#define IOBUFSIZE 4096 + +#define DO_ASLDB 0x00000001 +#define DO_MODULE 0x00000002 +#define DO_CHECKPT 0x00000004 + +#define DEBUG_FLAG_MASK 0xfffffff0 +#define DEBUG_LEVEL_MASK 0x0000000f +#define DEBUG_STDERR 0x00000010 +#define DEBUG_ASL 0x00000020 + +extern kern_return_t _asl_server_query +( + mach_port_t server, + caddr_t request, + mach_msg_type_number_t requestCnt, + uint64_t startid, + int count, + int flags, + caddr_t *reply, + mach_msg_type_number_t *replyCnt, + uint64_t *lastid, + int *status, + security_token_t *token +); /* global */ -static char *archive = NULL; -static char *store_dir = PATH_ASL_STORE; -static time_t ttl; -static size_t max_size; -static mode_t archive_mode = 0400; -static int debug; +static time_t module_ttl; +static uint32_t debug; +static int dryrun; +static int asl_aux_fd = -1; +static aslclient aslc; +static mach_port_t asl_server_port; +static xpc_connection_t listener; +static dispatch_queue_t serverq; typedef struct name_list_s { @@ -59,8 +93,74 @@ typedef struct name_list_s struct name_list_s *next; } name_list_t; +void +set_debug(int flag, const char *str) +{ + int level, x; + + if (str == NULL) x = ASL_LEVEL_ERR; + else if (((str[0] == 'L') || (str[0] == 'l')) && ((str[1] >= '0') && (str[1] <= '7')) && (str[2] == '\0')) x = atoi(str+1); + else if ((str[0] >= '0') && (str[0] <= '7') && (str[1] == '\0')) x = ASL_LEVEL_CRIT + atoi(str); + else x = ASL_LEVEL_ERR; + + if (x <= 0) x = 0; + else if (x > 7) x = 7; + + level = debug & DEBUG_LEVEL_MASK; + if (x > level) level = x; + + debug = debug & DEBUG_FLAG_MASK; + debug |= flag; + debug |= level; +} + +void +debug_log(int level, const char *str, ...) +{ + va_list v; + + if ((debug & DEBUG_STDERR) && (level <= (debug & DEBUG_LEVEL_MASK))) + { + va_start(v, str); + vfprintf(stderr, str, v); + va_end(v); + } + + if (debug & DEBUG_ASL) + { + char *line = NULL; + + if (aslc == NULL) + { + aslc = asl_open("aslmanager", "syslog", 0); + aslmsg msg = asl_new(ASL_TYPE_MSG); + + asl_set(msg, ASL_KEY_MSG, "Status Report"); + asl_set(msg, ASL_KEY_LEVEL, ASL_STRING_NOTICE); + asl_create_auxiliary_file(msg, "Status Report", "public.text", &asl_aux_fd); + asl_free(msg); + } + + va_start(v, str); + vasprintf(&line, str, v); + va_end(v); + + if (line != NULL) write(asl_aux_fd, line, strlen(line)); + free(line); + } +} + +__attribute__((noreturn)) static void +xpc_server_exit(int status) +{ + xpc_connection_cancel(listener); + xpc_release(listener); + dispatch_release(serverq); + exit(status); +} + name_list_t * -add_to_list(name_list_t *l, const char *name, size_t size) +add_to_name_list(name_list_t *l, const char *name, size_t size) { name_list_t *e, *x; @@ -95,7 +195,7 @@ add_to_list(name_list_t *l, const char *name, size_t size) } void -free_list(name_list_t *l) +free_name_list(name_list_t *l) { name_list_t *e; @@ -110,19 +210,23 @@ free_list(name_list_t *l) free(l); } +/* + * Copy ASL files by reading and writing each record. + * Setting ASL_FILE_FLAG_UNLIMITED_CACHE when copying optimizes tring uniquing. + */ uint32_t -do_copy(const char *infile, const char *outfile, mode_t mode) +copy_asl_file(const char *src, const char *dst, mode_t mode) { asl_search_result_t *res; asl_file_t *f; uint32_t status, i; uint64_t mid; - if (infile == NULL) return ASL_STATUS_INVALID_ARG; - if (outfile == NULL) return ASL_STATUS_INVALID_ARG; + if (src == NULL) return ASL_STATUS_INVALID_ARG; + if (dst == NULL) return ASL_STATUS_INVALID_ARG; f = NULL; - status = asl_file_open_read(infile, &f); + status = asl_file_open_read(src, &f); if (status != ASL_STATUS_OK) return status; res = NULL; @@ -132,6 +236,7 @@ do_copy(const char *infile, const char *outfile, mode_t mode) asl_file_close(f); if (status != ASL_STATUS_OK) return status; + if (res == NULL) return ASL_STATUS_OK; if (res->count == 0) { aslresponse_free(res); @@ -139,7 +244,7 @@ do_copy(const char *infile, const char *outfile, mode_t mode) } f = NULL; - status = asl_file_open_write(outfile, mode, -1, -1, &f); + status = asl_file_open_write(dst, mode, -1, -1, &f); if (status != ASL_STATUS_OK) return status; if (f == ASL_STATUS_OK) return ASL_STATUS_FAILED; @@ -157,154 +262,167 @@ do_copy(const char *infile, const char *outfile, mode_t mode) } int -do_dir_archive(const char *indir, const char *outdir) +copy_compress_file(asl_out_dst_data_t *asldst, const char *src, const char *dst) { - return copyfile(indir, outdir, NULL, COPYFILE_ALL | COPYFILE_RECURSIVE); -} + int in, out; + size_t n; + gzFile gz; + char buf[IOBUFSIZE]; -int -remove_directory(const char *path) -{ - DIR *dp; - struct dirent *dent; - char *str; - int status; + in = open(src, O_RDONLY, 0); + if (in < 0) return -1; - dp = opendir(path); - if (dp == NULL) return 0; + out = open(dst, O_WRONLY | O_CREAT, asldst->mode); + if (out >= 0) out = asl_out_dst_set_access(out, asldst); + if (out < 0) + { + close(in); + return -1; + } - while ((dent = readdir(dp)) != NULL) + gz = gzdopen(out, "w"); + if (gz == NULL) { - if ((!strcmp(dent->d_name, ".")) || (!strcmp(dent->d_name, ".."))) continue; - asprintf(&str, "%s/%s", path, dent->d_name); - if (str != NULL) - { - status = unlink(str); - free(str); - str = NULL; - } + close(in); + close(out); + return -1; } - closedir(dp); - status = rmdir(path); + do { + n = read(in, buf, sizeof(buf)); + if (n > 0) gzwrite(gz, buf, n); + } while (n == IOBUFSIZE); - return status; -} + gzclose(gz); + close(in); + close(out); + return 0; +} -static char ** -_insertString(char *s, char **l, uint32_t x) +int32_t +filesystem_copy(asl_out_dst_data_t *asldst, const char *src, const char *dst, uint32_t flags) { - int i, len; + char *dot; - if (s == NULL) return l; - if (l == NULL) - { - l = (char **)malloc(2 * sizeof(char *)); - if (l == NULL) return NULL; + if ((src == NULL) || (dst == NULL)) return 0; - l[0] = strdup(s); - if (l[0] == NULL) - { - free(l); - return NULL; - } + dot = strrchr(src, '.'); + if ((dot != NULL) && (!strcmp(dot, ".gz"))) flags &= ~MODULE_FLAG_COMPRESS; - l[1] = NULL; - return l; - } + if (((flags & MODULE_FLAG_COMPRESS) == 0) && (!strcmp(src, dst))) return 0; - for (i = 0; l[i] != NULL; i++); - len = i + 1; /* count the NULL on the end of the list too! */ + if (flags & MODULE_FLAG_TYPE_ASL) debug_log(ASL_LEVEL_NOTICE, " copy asl %s ---> %s\n", src, dst); + else if (flags & MODULE_FLAG_COMPRESS) debug_log(ASL_LEVEL_NOTICE, " copy compress %s ---> %s.gz\n", src, dst); + else debug_log(ASL_LEVEL_NOTICE, " copy %s ---> %s\n", src, dst); - l = (char **)reallocf(l, (len + 1) * sizeof(char *)); - if (l == NULL) return NULL; + if (dryrun == 1) return 0; - if ((x >= (len - 1)) || (x == IndexNull)) + if (flags & MODULE_FLAG_TYPE_ASL) { - l[len - 1] = strdup(s); - if (l[len - 1] == NULL) + uint32_t status = copy_asl_file(src, dst, asldst->mode); + if (status != 0) { - free(l); - return NULL; + debug_log(ASL_LEVEL_ERR, " FAILED status %u [%s] asl copy %s ---> %s\n", status, asl_core_error(status), src, dst); + return 0; } - - l[len] = NULL; - return l; } + else if (flags & MODULE_FLAG_COMPRESS) + { + char gzdst[MAXPATHLEN]; - for (i = len; i > x; i--) l[i] = l[i - 1]; - l[x] = strdup(s); - if (l[x] == NULL) return NULL; + snprintf(gzdst, sizeof(gzdst), "%s.gz", dst); - return l; + int status = copy_compress_file(asldst, src, gzdst); + if (status != 0) + { + debug_log(ASL_LEVEL_ERR, " FAILED status %d errno %d [%s] copy & compress %s ---> %s\n", status, errno, strerror(errno), src, dst); + return 0; + } + } + else + { + int status = copyfile(src, dst, NULL, COPYFILE_ALL | COPYFILE_RECURSIVE); + if (status != 0) + { + debug_log(ASL_LEVEL_ERR, " FAILED status %d errno %d [%s] copy %s ---> %s\n", status, errno, strerror(errno), src, dst); + return 0; + } + } + + return 1; } -char ** -explode(const char *s, const char *delim) +void +filesystem_rename(const char *src, const char *dst) { - char **l = NULL; - const char *p; - char *t, quote; - int i, n; - - if (s == NULL) return NULL; + int status = 0; - quote = '\0'; + debug_log(ASL_LEVEL_NOTICE, " rename %s ---> %s\n", src, dst); + if (dryrun == 1) return; - 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'; - } - } + status = rename(src, dst); + if (status != 0) debug_log(ASL_LEVEL_ERR, " FAILED status %d errno %d [%s] rename %s ---> %s\n", status, errno, strerror(errno), src, dst); +} - n = i; - t = malloc(n + 1); - if (t == NULL) return NULL; +void +filesystem_unlink(const char *path) +{ + int status = 0; - 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; - } + debug_log(ASL_LEVEL_NOTICE, " remove %s\n", path); + if (dryrun == 1) return; - return l; + status = unlink(path); + if (status != 0) debug_log(ASL_LEVEL_ERR, " FAILED status %d errno %d [%s] unlink %s\n", status, errno, strerror(errno), path); } void -freeList(char **l) +filesystem_rmdir(const char *path) { - int i; + int status = 0; - if (l == NULL) return; - for (i = 0; l[i] != NULL; i++) free(l[i]); - free(l); + debug_log(ASL_LEVEL_NOTICE, " remove directory %s\n", path); + if (dryrun == 1) return; + + status = rmdir(path); + if (status != 0) debug_log(ASL_LEVEL_ERR, " FAILED status %d errno %d [%s] rmdir %s\n", status, errno, strerror(errno), path); +} + +int +remove_directory(const char *path) +{ + DIR *dp; + struct dirent *dent; + char *str; + + dp = opendir(path); + if (dp == NULL) return 0; + + while ((dent = readdir(dp)) != NULL) + { + if ((!strcmp(dent->d_name, ".")) || (!strcmp(dent->d_name, ".."))) continue; + asprintf(&str, "%s/%s", path, dent->d_name); + if (str != NULL) + { + filesystem_unlink(str); + free(str); + str = NULL; + } + } + + closedir(dp); + filesystem_rmdir(path); + + return 0; } /* - * Used to sed config parameters. + * Used to set config parameters. * Line format "= name value" */ static void -_parse_set_param(char *s) +_aslmanager_set_param(asl_out_dst_data_t *dst, char *s) { char **l; uint32_t count; @@ -313,7 +431,7 @@ _parse_set_param(char *s) if (s[0] == '\0') return; /* skip '=' and whitespace */ - s++; + if (*s == '=') s++; while ((*s == ' ') || (*s == '\t')) s++; l = explode(s, " \t"); @@ -324,107 +442,62 @@ _parse_set_param(char *s) /* name is required */ if (count == 0) { - freeList(l); + free_string_list(l); return; } /* value is required */ if (count == 1) { - freeList(l); + free_string_list(l); return; } if (!strcasecmp(l[0], "aslmanager_debug")) { - /* = debug {0|1} */ - debug = atoi(l[1]); + /* = debug level */ + set_debug(DEBUG_ASL, l[1]); } else if (!strcasecmp(l[0], "store_ttl")) { /* = store_ttl days */ - ttl = SECONDS_PER_DAY * (time_t)atoll(l[1]); + dst->ttl = (time_t)atoll(l[1]); + } + else if (!strcasecmp(l[0], "module_ttl")) + { + /* = module_ttl days */ + module_ttl = (time_t)atoll(l[1]); } else if (!strcasecmp(l[0], "max_store_size")) { /* = max_file_size bytes */ - max_size = atoi(l[1]); + dst->all_max = atoi(l[1]); } else if (!strcasecmp(l[0], "archive")) { + free(dst->rotate_dir); + dst->rotate_dir = NULL; + /* = archive {0|1} path */ if (!strcmp(l[1], "1")) { - if (l[2] == NULL) archive = PATH_ASL_ARCHIVE; - else archive = strdup(l[2]); /* never freed */ + if (l[2] == NULL) dst->rotate_dir = strdup(PATH_ASL_ARCHIVE); + else dst->rotate_dir = strdup(l[2]); } - else archive = NULL; } else if (!strcasecmp(l[0], "store_path")) { /* = archive path */ - store_dir = strdup(l[1]); /* never freed */ + free(dst->path); + dst->path = strdup(l[1]); } else if (!strcasecmp(l[0], "archive_mode")) { - archive_mode = strtol(l[1], NULL, 0); - if ((archive_mode == 0) && (errno == EINVAL)) archive_mode = 0400; + dst->mode = strtol(l[1], NULL, 0); + if ((dst->mode == 0) && (errno == EINVAL)) dst->mode = 0400; } - freeList(l); -} - -static void -_parse_line(char *s) -{ - if (s == NULL) return; - while ((*s == ' ') || (*s == '\t')) s++; - - /* - * First non-whitespace char is the rule type. - * aslmanager only checks "=" (set parameter) rules. - */ - if (*s == '=') _parse_set_param(s); -} - -char * -get_line_from_file(FILE *f) -{ - char *s, *out; - size_t len; - - out = fgetln(f, &len); - if (out == NULL) return NULL; - if (len == 0) return NULL; - - s = malloc(len + 1); - if (s == NULL) return NULL; - - memcpy(s, out, len); - - if (s[len - 1] != '\n') len++; - s[len - 1] = '\0'; - return s; -} - -static int -_parse_config_file(const char *name) -{ - FILE *cf; - char *line; - - cf = fopen(name, "r"); - if (cf == NULL) return 1; - - while (NULL != (line = get_line_from_file(cf))) - { - _parse_line(line); - free(line); - } - - fclose(cf); - - return 0; + free_string_list(l); } size_t @@ -459,17 +532,16 @@ directory_size(const char *path) return size; } -int -main(int argc, const char *argv[]) +static int +process_asl_data_store(asl_out_dst_data_t *dst) { - int i, today_ymd_stringlen, expire_ymd_stringlen; - time_t now, ymd_expire; + int32_t today_ymd_stringlen, expire_ymd_stringlen; + time_t now, ttl, ymd_expire; struct tm ctm; char today_ymd_string[32], expire_ymd_string[32], *str; DIR *dp; struct dirent *dent; name_list_t *ymd_list, *bb_list, *aux_list, *bb_aux_list, *e; - uint32_t status; size_t file_size, store_size; struct stat sb; @@ -477,51 +549,24 @@ main(int argc, const char *argv[]) bb_list = NULL; aux_list = NULL; bb_aux_list = NULL; - - ttl = DEFAULT_TTL * SECONDS_PER_DAY; - max_size = DEFAULT_MAX_SIZE; store_size = 0; - debug = 0; - - for (i = 1; i < argc; i++) - { - if (!strcmp(argv[i], "-a")) - { - if (((i + 1) < argc) && (argv[i + 1][0] != '-')) archive = (char *)argv[++i]; - else archive = PATH_ASL_ARCHIVE; - } - else if (!strcmp(argv[i], "-s")) - { - if (((i + 1) < argc) && (argv[i + 1][0] != '-')) store_dir = (char *)argv[++i]; - } - else if (!strcmp(argv[i], "-ttl")) - { - if (((i + 1) < argc) && (argv[i + 1][0] != '-')) ttl = atoi(argv[++i]) * SECONDS_PER_DAY; - } - else if (!strcmp(argv[i], "-size")) - { - if (((i + 1) < argc) && (argv[i + 1][0] != '-')) max_size = atoi(argv[++i]); - } - else if (!strcmp(argv[i], "-d")) - { - debug = 1; - } - } - _parse_config_file(_PATH_ASL_CONF); + if (dst == NULL) return 0; + if (dst->path == NULL) return 0; - if (debug == 1) printf("aslmanager starting\n"); + debug_log(ASL_LEVEL_NOTICE, "----------------------------------------\n"); + debug_log(ASL_LEVEL_NOTICE, "Processing data store %s\n", dst->path); - /* check archive */ - if (archive != NULL) + if (dst->rotate_dir != NULL) { + /* check archive */ memset(&sb, 0, sizeof(struct stat)); - if (stat(archive, &sb) == 0) + if (stat(dst->rotate_dir, &sb) == 0) { /* must be a directory */ if (!S_ISDIR(sb.st_mode)) { - fprintf(stderr, "aslmanager error: archive %s is not a directory", archive); + debug_log(ASL_LEVEL_ERR, "aslmanager error: archive %s is not a directory", dst->rotate_dir); return -1; } } @@ -530,29 +575,31 @@ main(int argc, const char *argv[]) if (errno == ENOENT) { /* archive doesn't exist - create it */ - if (mkdir(archive, 0755) != 0) + if (mkdir(dst->rotate_dir, 0755) != 0) { - fprintf(stderr, "aslmanager error: can't create archive %s: %s\n", archive, strerror(errno)); + debug_log(ASL_LEVEL_ERR, "aslmanager error: can't create archive %s: %s\n", dst->rotate_dir, strerror(errno)); return -1; } } else { /* stat failed for some other reason */ - fprintf(stderr, "aslmanager error: can't stat archive %s: %s\n", archive, strerror(errno)); + debug_log(ASL_LEVEL_ERR, "aslmanager error: can't stat archive %s: %s\n", dst->rotate_dir, strerror(errno)); return -1; } } } - chdir(store_dir); + chdir(dst->path); /* determine current time */ now = time(NULL); /* ttl 0 means files never expire */ ymd_expire = 0; - if (ttl > 0) ymd_expire = now - ttl; + ttl = dst->ttl * SECONDS_PER_DAY; + + if ((ttl > 0) && (ttl <= now)) ymd_expire = now - ttl; /* construct today's date as YYYY.MM.DD */ memset(&ctm, 0, sizeof(struct tm)); @@ -568,9 +615,9 @@ main(int argc, const char *argv[]) snprintf(expire_ymd_string, sizeof(expire_ymd_string), "%d.%02d.%02d.", ctm.tm_year + 1900, ctm.tm_mon + 1, ctm.tm_mday); expire_ymd_stringlen = strlen(expire_ymd_string); - if (debug == 1) printf("Expiry Date %s\n", expire_ymd_string); + debug_log(ASL_LEVEL_NOTICE, "Expiry Date %s\n", expire_ymd_string); - dp = opendir(store_dir); + dp = opendir(dst->path); if (dp == NULL) return -1; /* gather a list of YMD files, AUX dirs, BB.AUX dirs, and BB files */ @@ -582,24 +629,24 @@ main(int argc, const char *argv[]) if ((dent->d_name[0] >= '0') && (dent->d_name[0] <= '9')) { - ymd_list = add_to_list(ymd_list, dent->d_name, file_size); + ymd_list = add_to_name_list(ymd_list, dent->d_name, file_size); store_size += file_size; } else if (!strncmp(dent->d_name, "AUX.", 4) && (dent->d_name[4] >= '0') && (dent->d_name[4] <= '9') && S_ISDIR(sb.st_mode)) { file_size = directory_size(dent->d_name); - aux_list = add_to_list(aux_list, dent->d_name, file_size); + aux_list = add_to_name_list(aux_list, dent->d_name, file_size); store_size += file_size; } else if (!strncmp(dent->d_name, "BB.AUX.", 7) && (dent->d_name[7] >= '0') && (dent->d_name[7] <= '9') && S_ISDIR(sb.st_mode)) { file_size = directory_size(dent->d_name); - bb_aux_list = add_to_list(bb_aux_list, dent->d_name, file_size); + bb_aux_list = add_to_name_list(bb_aux_list, dent->d_name, file_size); store_size += file_size; } else if (!strncmp(dent->d_name, "BB.", 3) && (dent->d_name[3] >= '0') && (dent->d_name[3] <= '9')) { - bb_list = add_to_list(bb_list, dent->d_name, file_size); + bb_list = add_to_name_list(bb_list, dent->d_name, file_size); store_size += file_size; } else if ((!strcmp(dent->d_name, ".")) || (!strcmp(dent->d_name, ".."))) @@ -608,27 +655,24 @@ main(int argc, const char *argv[]) {} else { - fprintf(stderr, "aslmanager: unexpected file %s in ASL data store\n", dent->d_name); + debug_log(ASL_LEVEL_ERR, "aslmanager: unexpected file %s in ASL data store\n", dent->d_name); } } closedir(dp); - if (debug == 1) - { - printf("Data Store Size = %lu\n", store_size); - printf("Data Store YMD Files\n"); - for (e = ymd_list; e != NULL; e = e->next) printf(" %s %lu\n", e->name, e->size); - printf("Data Store AUX Directories\n"); - for (e = aux_list; e != NULL; e = e->next) printf(" %s %lu\n", e->name, e->size); - printf("Data Store BB.AUX Directories\n"); - for (e = bb_aux_list; e != NULL; e = e->next) printf(" %s %lu\n", e->name, e->size); - printf("Data Store BB Files\n"); - for (e = bb_list; e != NULL; e = e->next) printf(" %s %lu\n", e->name, e->size); - } + debug_log(ASL_LEVEL_NOTICE, "Data Store Size = %lu\n", store_size); + debug_log(ASL_LEVEL_NOTICE, "Data Store YMD Files\n"); + for (e = ymd_list; e != NULL; e = e->next) debug_log(ASL_LEVEL_NOTICE, " %s %lu\n", e->name, e->size); + debug_log(ASL_LEVEL_NOTICE, "Data Store AUX Directories\n"); + for (e = aux_list; e != NULL; e = e->next) debug_log(ASL_LEVEL_NOTICE, " %s %lu\n", e->name, e->size); + debug_log(ASL_LEVEL_NOTICE, "Data Store BB.AUX Directories\n"); + for (e = bb_aux_list; e != NULL; e = e->next) debug_log(ASL_LEVEL_NOTICE, " %s %lu\n", e->name, e->size); + debug_log(ASL_LEVEL_NOTICE, "Data Store BB Files\n"); + for (e = bb_list; e != NULL; e = e->next) debug_log(ASL_LEVEL_NOTICE, " %s %lu\n", e->name, e->size); /* Delete/achive expired YMD files */ - if (debug == 1) printf("Start YMD File Scan\n"); + debug_log(ASL_LEVEL_NOTICE, "Start YMD File Scan\n"); e = ymd_list; while (e != NULL) @@ -636,30 +680,27 @@ main(int argc, const char *argv[]) /* stop when a file name/date is after the expire date */ if (strncmp(e->name, expire_ymd_string, expire_ymd_stringlen) > 0) break; - if (archive != NULL) + if (dst->rotate_dir != NULL) { str = NULL; - asprintf(&str, "%s/%s", archive, e->name); + asprintf(&str, "%s/%s", dst->rotate_dir, e->name); if (str == NULL) return -1; - if (debug == 1) printf(" copy %s ---> %s\n", e->name, str); - status = do_copy(e->name, str, archive_mode); + filesystem_copy(dst, e->name, str, 0); free(str); } - if (debug == 1) printf(" unlink %s\n", e->name); - unlink(e->name); - + filesystem_unlink(e->name); store_size -= e->size; e->size = 0; e = e->next; } - if (debug == 1) printf("Finished YMD FILE Scan\n"); + debug_log(ASL_LEVEL_NOTICE, "Finished YMD File Scan\n"); /* Delete/achive expired YMD AUX directories */ - if (debug == 1) printf("Start AUX Directory Scan\n"); + debug_log(ASL_LEVEL_NOTICE, "Start AUX Directory Scan\n"); e = aux_list; while (e != NULL) @@ -667,30 +708,27 @@ main(int argc, const char *argv[]) /* stop when a file name/date is after the expire date */ if (strncmp(e->name + 4, expire_ymd_string, expire_ymd_stringlen) > 0) break; - if (archive != NULL) + if (dst->rotate_dir != NULL) { str = NULL; - asprintf(&str, "%s/%s", archive, e->name); + asprintf(&str, "%s/%s", dst->rotate_dir, e->name); if (str == NULL) return -1; - if (debug == 1) printf(" copy %s ---> %s\n", e->name, str); - do_dir_archive(e->name, str); + filesystem_copy(dst, e->name, str, 0); free(str); } - if (debug == 1) printf(" Remove %s\n", e->name); remove_directory(e->name); - store_size -= e->size; e->size = 0; e = e->next; } - if (debug == 1) printf("Finished AUX Directory Scan\n"); + debug_log(ASL_LEVEL_NOTICE, "Finished AUX Directory Scan\n"); /* Delete/achive expired BB.AUX directories */ - if (debug == 1) printf("Start BB.AUX Directory Scan\n"); + debug_log(ASL_LEVEL_NOTICE, "Start BB.AUX Directory Scan\n"); e = bb_aux_list; while (e != NULL) @@ -698,30 +736,27 @@ main(int argc, const char *argv[]) /* stop when a file name/date is after the expire date */ if (strncmp(e->name + 7, today_ymd_string, today_ymd_stringlen) > 0) break; - if (archive != NULL) + if (dst->rotate_dir != NULL) { str = NULL; - asprintf(&str, "%s/%s", archive, e->name); + asprintf(&str, "%s/%s", dst->rotate_dir, e->name); if (str == NULL) return -1; - if (debug == 1) printf(" copy %s ---> %s\n", e->name, str); - do_dir_archive(e->name, str); + filesystem_copy(dst, e->name, str, 0); free(str); } - if (debug == 1) printf(" remove %s\n", e->name); remove_directory(e->name); - store_size -= e->size; e->size = 0; e = e->next; } - if (debug == 1) printf("Finished BB.AUX Directory Scan\n"); + debug_log(ASL_LEVEL_NOTICE, "Finished BB.AUX Directory Scan\n"); /* Delete/achive expired BB files */ - if (debug == 1) printf("Start BB Scan\n"); + debug_log(ASL_LEVEL_NOTICE, "Start BB Scan\n"); e = bb_list; while (e != NULL) @@ -729,101 +764,632 @@ main(int argc, const char *argv[]) /* stop when a file name/date is after the expire date */ if (strncmp(e->name + 3, today_ymd_string, today_ymd_stringlen) > 0) break; - if (archive != NULL) + if (dst->rotate_dir != NULL) { str = NULL; - asprintf(&str, "%s/%s", archive, e->name); + asprintf(&str, "%s/%s", dst->rotate_dir, e->name); if (str == NULL) return -1; /* syslog -x [str] -f [e->name] */ - if (debug == 1) printf(" copy %s ---> %s\n", e->name, str); - status = do_copy(e->name, str, archive_mode); + filesystem_copy(dst, e->name, str, 0); free(str); } - if (debug == 1) printf(" unlink %s\n", e->name); - unlink(e->name); - + filesystem_unlink(e->name); store_size -= e->size; e->size = 0; e = e->next; } - if (debug == 1) printf("Finished BB Scan\n"); + debug_log(ASL_LEVEL_NOTICE, "Finished BB Scan\n"); - /* if data store is over max_size, delete/archive more YMD files */ - if ((debug == 1) && (store_size > max_size)) printf("Additional YMD Scan\n"); + if (dst->all_max > 0) + { + /* if data store is over max_size, delete/archive more YMD files */ + if (store_size > dst->all_max) debug_log(ASL_LEVEL_NOTICE, "Additional YMD Scan\n"); - e = ymd_list; - while ((e != NULL) && (store_size > max_size)) + e = ymd_list; + while ((e != NULL) && (store_size > dst->all_max)) + { + if (e->size != 0) + { + /* stop when we get to today's files */ + if (strncmp(e->name, today_ymd_string, today_ymd_stringlen) == 0) break; + + if (dst->rotate_dir != NULL) + { + str = NULL; + asprintf(&str, "%s/%s", dst->rotate_dir, e->name); + if (str == NULL) return -1; + + /* syslog -x [str] -f [e->name] */ + filesystem_copy(dst, e->name, str, 0); + free(str); + } + + filesystem_unlink(e->name); + store_size -= e->size; + e->size = 0; + } + + e = e->next; + } + + /* if data store is over dst->all_max, delete/archive more BB files */ + if (store_size > dst->all_max) debug_log(ASL_LEVEL_NOTICE, "Additional BB Scan\n"); + + e = bb_list; + while ((e != NULL) && (store_size > dst->all_max)) + { + if (e->size != 0) + { + if (dst->rotate_dir != NULL) + { + str = NULL; + asprintf(&str, "%s/%s", dst->rotate_dir, e->name); + if (str == NULL) return -1; + + /* syslog -x [str] -f [e->name] */ + filesystem_copy(dst, e->name, str, 0); + free(str); + } + + filesystem_unlink(e->name); + store_size -= e->size; + e->size = 0; + } + + e = e->next; + } + } + + free_name_list(ymd_list); + free_name_list(bb_list); + free_name_list(aux_list); + free_name_list(bb_aux_list); + + debug_log(ASL_LEVEL_NOTICE, "Data Store Size = %lu\n", store_size); + + return 0; +} + +/* move sequenced source files to dst dir, renaming as we go */ +static int +module_copy_rename(asl_out_dst_data_t *dst) +{ + asl_out_file_list_t *src_list, *dst_list, *f, *dst_last; + char *base, *dst_dir; + char fpathsrc[MAXPATHLEN], fpathdst[MAXPATHLEN]; + uint32_t src_count, dst_count; + int32_t x, moved; + + if (dst == NULL) return -1; + if (dst->path == NULL) return -1; + + base = strrchr(dst->path, '/'); + if (base == NULL) return -1; + + src_list = asl_list_src_files(dst); + if (src_list == 0) { - if (e->size != 0) + debug_log(ASL_LEVEL_INFO, " no src files\n"); + return 0; + } + + debug_log(ASL_LEVEL_INFO, " src files\n"); + + src_count = 0; + for (f = src_list; f != NULL; f = f->next) + { + debug_log(ASL_LEVEL_INFO, " %s\n", f->name); + src_count++; + } + + dst_list = asl_list_dst_files(dst); + + *base = '\0'; + base++; + + dst_dir = dst->rotate_dir; + if (dst_dir == NULL) dst_dir = dst->path; + + dst_count = 0; + dst_last = dst_list; + + if (dst_list == NULL) debug_log(ASL_LEVEL_INFO, " no dst files\n"); + else debug_log(ASL_LEVEL_INFO, " dst files\n"); + + for (f = dst_list; f != NULL; f = f->next) + { + debug_log(ASL_LEVEL_INFO, " %s\n", f->name); + dst_last = f; + dst_count++; + } + + if (dst->flags & MODULE_FLAG_STYLE_SEQ) + { + for (f = dst_last; f != NULL; f = f->prev) { - /* stop when we get to today's files */ - if (strncmp(e->name, today_ymd_string, today_ymd_stringlen) == 0) break; + int is_gz = 0; + char *dot = strrchr(f->name, '.'); + if ((dot != NULL) && (!strcmp(dot, ".gz"))) is_gz = 1; + + snprintf(fpathsrc, sizeof(fpathsrc), "%s/%s", dst_dir, f->name); + snprintf(fpathdst, sizeof(fpathdst), "%s/%s.%d%s", dst_dir, base, f->seq+src_count, (is_gz == 1) ? ".gz" : ""); + filesystem_rename(fpathsrc, fpathdst); + } - if (archive != NULL) + for (f = src_list, x = 0; f != NULL; f = f->next, x++) + { + snprintf(fpathsrc, sizeof(fpathsrc), "%s/%s", dst->path, f->name); + snprintf(fpathdst, sizeof(fpathdst), "%s/%s.%d", dst_dir, base, x); + moved = filesystem_copy(dst, fpathsrc, fpathdst, dst->flags); + if (moved != 0) filesystem_unlink(fpathsrc); + } + } + else + { + for (f = src_list; f != NULL; f = f->next) + { + /* final / active base stamped file looks like a checkpointed file - ignore it */ + if ((dst->flags & MODULE_FLAG_BASESTAMP) && (f->next == NULL)) break; + + snprintf(fpathsrc, sizeof(fpathsrc), "%s/%s", dst->path, f->name); + + /* MODULE_FLAG_EXTERNAL files are not decorated with a timestamp */ + if (dst->flags & MODULE_FLAG_EXTERNAL) { - str = NULL; - asprintf(&str, "%s/%s", archive, e->name); - if (str == NULL) return -1; - - /* syslog -x [str] -f [e->name] */ - if (debug == 1) printf(" copy %s ---> %s\n", e->name, str); - status = do_copy(e->name, str, archive_mode); - free(str); + char tstamp[32]; + + asl_make_timestamp(f->ftime, dst->flags, tstamp, sizeof(tstamp)); + snprintf(fpathdst, sizeof(fpathdst), "%s/%s.%s", dst_dir, base, tstamp); + } + else + { + snprintf(fpathdst, sizeof(fpathdst), "%s/%s", dst_dir, f->name); } - if (debug == 1) printf(" unlink %s\n", e->name); - unlink(e->name); + moved = filesystem_copy(dst, fpathsrc, fpathdst, dst->flags); + if (moved != 0) filesystem_unlink(fpathsrc); + } + } - store_size -= e->size; - e->size = 0; + asl_out_file_list_free(src_list); + asl_out_file_list_free(dst_list); + + if (base != NULL) *--base = '/'; + + return 0; +} + +/* delete expired files */ +static int +module_expire(asl_out_dst_data_t *dst) +{ + asl_out_file_list_t *dst_list, *f; + char *base, *dst_dir, fpath[MAXPATHLEN]; + time_t now, ttl, cutoff; + + if (dst == NULL) return -1; + if (dst->path == NULL) return -1; + if (dst->ttl == 0) return 0; + + ttl = 0; + if (module_ttl > 0) ttl = module_ttl; + else ttl = dst->ttl; + + ttl *= SECONDS_PER_DAY; + + now = time(NULL); + if (ttl > now) return 0; + + cutoff = now - ttl; + + base = strrchr(dst->path, '/'); + if (base == NULL) return -1; + + dst_list = asl_list_dst_files(dst); + + *base = '\0'; + + dst_dir = dst->rotate_dir; + if (dst_dir == NULL) dst_dir = dst->path; + + if (dst_list == NULL) + { + debug_log(ASL_LEVEL_INFO, " no dst files\n"); + } + else + { + debug_log(ASL_LEVEL_INFO, " dst files\n"); + for (f = dst_list; f != NULL; f = f->next) debug_log(ASL_LEVEL_INFO, " %s\n", f->name); + } + + for (f = dst_list; f != NULL; f = f->next) + { + if (f->ftime <= cutoff) + { + snprintf(fpath, sizeof(fpath), "%s/%s", dst_dir, f->name); + filesystem_unlink(fpath); } + } - e = e->next; + asl_out_file_list_free(dst_list); + + if (base != NULL) *base = '/'; + + return 0; +} + +/* check all_max size and delete files (oldest first) to stay within size limit */ +static int +module_check_size(asl_out_dst_data_t *dst) +{ + asl_out_file_list_t *dst_list, *f; + char *base, *dst_dir, fpath[MAXPATHLEN]; + size_t total; + + if (dst == NULL) return -1; + if (dst->path == NULL) return -1; + + if (dst->all_max == 0) return 0; + + base = NULL; + dst_dir = dst->rotate_dir; + if (dst_dir == NULL) + { + dst_dir = dst->path; + base = strrchr(dst->path, '/'); + if (base == NULL) return -1; + *base = '\0'; } - /* if data store is over max_size, delete/archive more BB files */ - if ((debug == 1) && (store_size > max_size)) printf("Additional BB Scan\n"); + dst_list = asl_list_dst_files(dst); - e = bb_list; - while ((e != NULL) && (store_size > max_size)) + if (dst_list == NULL) { - if (e->size != 0) + debug_log(ASL_LEVEL_INFO, " no dst files\n"); + } + else + { + debug_log(ASL_LEVEL_INFO, " dst files\n"); + for (f = dst_list; f != NULL; f = f->next) debug_log(ASL_LEVEL_INFO, " %s size %lu\n", f->name, f->size); + } + + total = 0; + for (f = dst_list; f != NULL; f = f->next) total += f->size; + + for (f = dst_list; (total > dst->all_max) && (f != NULL); f = f->next) + { + snprintf(fpath, sizeof(fpath), "%s/%s", dst_dir, f->name); + filesystem_unlink(fpath); + total -= f->size; + } + + asl_out_file_list_free(dst_list); + + if (base != NULL) *base = '/'; + + return 0; +} + + +static int +process_module(asl_out_module_t *mod) +{ + asl_out_rule_t *r; + + if (mod == NULL) return -1; + + debug_log(ASL_LEVEL_NOTICE, "----------------------------------------\n"); + debug_log(ASL_LEVEL_NOTICE, "Processing module %s\n", (mod->name == NULL) ? "asl.conf" : mod->name); + + for (r = mod->ruleset; r != NULL; r = r->next) + { + if (r->action == ACTION_OUT_DEST) { - if (archive != NULL) + if (r->dst == NULL) { - str = NULL; - asprintf(&str, "%s/%s", archive, e->name); - if (str == NULL) return -1; - - /* syslog -x [str] -f [e->name] */ - if (debug == 1) printf(" copy %s ---> %s\n", e->name, str); - status = do_copy(e->name, str, archive_mode); - free(str); + debug_log(ASL_LEVEL_NOTICE, "NULL dst data for output rule - skipped\n"); } + else if (r->dst->flags & MODULE_FLAG_ROTATE) + { + debug_log(ASL_LEVEL_NOTICE, "Checking file %s\n", r->dst->path); + debug_log(ASL_LEVEL_NOTICE, "- Rename, move to destination directory, and compress as required\n"); - if (debug == 1) printf(" unlink %s\n", e->name); - unlink(e->name); + module_copy_rename(r->dst); - store_size -= e->size; - e->size = 0; + if (r->dst->ttl > 0) + { + debug_log(ASL_LEVEL_NOTICE, "- Check for expired files - TTL = %d days\n", r->dst->ttl); + module_expire(r->dst); + } + + if (r->dst->all_max > 0) + { + debug_log(ASL_LEVEL_NOTICE, "- Check total storage used - MAX = %lu\n", r->dst->all_max); + module_check_size(r->dst); + } + } + else if ((r->dst->flags & MODULE_FLAG_TYPE_ASL_DIR) && (r->dst->ttl > 0)) + { + process_asl_data_store(r->dst); + } } + } - e = e->next; + debug_log(ASL_LEVEL_NOTICE, "Finished processing module %s\n", (mod->name == NULL) ? "asl.conf" : mod->name); + return 0; +} + +aslresponse +control_query(aslmsg a) +{ + asl_search_result_t *out; + char *qstr, *str, *res; + uint32_t len, reslen, status; + uint64_t cmax, qmin; + kern_return_t kstatus; + caddr_t vmstr; + security_token_t sec; + + if (asl_server_port == MACH_PORT_NULL) + { + bootstrap_look_up2(bootstrap_port, ASL_SERVICE_NAME, &asl_server_port, 0, BOOTSTRAP_PRIVILEGED_SERVER); + if (asl_server_port == MACH_PORT_NULL) return NULL; } - free_list(ymd_list); - free_list(bb_list); + qstr = asl_msg_to_string((asl_msg_t *)a, &len); - if (debug == 1) + str = NULL; + if (qstr == NULL) + { + asprintf(&str, "1\nQ [= ASLOption control]\n"); + } + else { - printf("Data Store Size = %lu\n", store_size); - printf("aslmanager finished\n"); + asprintf(&str, "1\n%s [= ASLOption control]\n", qstr); + free(qstr); } + if (str == NULL) return NULL; + + /* length includes trailing nul */ + len = strlen(str) + 1; + out = NULL; + qmin = 0; + cmax = 0; + sec.val[0] = -1; + sec.val[1] = -1; + + res = NULL; + reslen = 0; + status = ASL_STATUS_OK; + + kstatus = vm_allocate(mach_task_self(), (vm_address_t *)&vmstr, len, TRUE); + if (kstatus != KERN_SUCCESS) return NULL; + + memmove(vmstr, str, len); + free(str); + + status = 0; + kstatus = _asl_server_query(asl_server_port, vmstr, len, qmin, 1, 0, (caddr_t *)&res, &reslen, &cmax, (int *)&status, &sec); + if (kstatus != KERN_SUCCESS) return NULL; + + if (res == NULL) return NULL; + + out = asl_list_from_string(res); + vm_deallocate(mach_task_self(), (vm_address_t)res, reslen); + + return out; +} + +int +checkpoint(const char *name) +{ + /* send checkpoint message to syslogd */ + debug_log(ASL_LEVEL_NOTICE, "Checkpoint module %s\n", (name == NULL) ? "*" : name); + if (dryrun != 0) return 0; + + aslmsg qmsg = asl_new(ASL_TYPE_QUERY); + char *tmp = NULL; + aslresponse res; + + asprintf(&tmp, "%s checkpoint", (name == NULL) ? "*" : name); + asl_set_query(qmsg, "action", tmp, ASL_QUERY_OP_EQUAL); + free(tmp); + + res = control_query(qmsg); + + aslresponse_free(res); return 0; } +int +cli_main(int argc, char *argv[]) +{ + int i, work; + asl_out_module_t *mod, *m; + asl_out_rule_t *r; + asl_out_dst_data_t store; + const char *mname = NULL; + + if (geteuid() != 0) + { + if (argc == 0) debug = DEBUG_ASL; + else debug = DEBUG_STDERR; + + debug_log(ASL_LEVEL_ERR, "aslmanager must be run by root\n"); + exit(1); + } + + module_ttl = DEFAULT_TTL; + + /* cobble up a dst_data for the main asl_store */ + memset(&store, 0, sizeof(store)); + store.ttl = DEFAULT_TTL; + store.all_max = DEFAULT_MAX_SIZE; + + /* get parameters from asl.conf */ + mod = asl_out_module_init(); + + if (mod != NULL) + { + for (r = mod->ruleset; r != NULL; r = r->next) + { + if (r->action == ACTION_SET_PARAM) + { + if (r->query == NULL) _aslmanager_set_param(&store, r->options); + } + } + } + + work = DO_ASLDB | DO_MODULE; + + for (i = 1; i < argc; i++) + { + if (!strcmp(argv[i], "-a")) + { + if (((i + 1) < argc) && (argv[i + 1][0] != '-')) store.rotate_dir = strdup(argv[++i]); + else store.rotate_dir = strdup(PATH_ASL_ARCHIVE); + store.mode = 0400; + } + else if (!strcmp(argv[i], "-s")) + { + if (((i + 1) < argc) && (argv[i + 1][0] != '-')) store.path = strdup(argv[++i]); + } + else if (!strcmp(argv[i], "-store_ttl")) + { + if (((i + 1) < argc) && (argv[i + 1][0] != '-')) store.ttl = atoi(argv[++i]); + } + else if (!strcmp(argv[i], "-module_ttl")) + { + if (((i + 1) < argc) && (argv[i + 1][0] != '-')) module_ttl = atoi(argv[++i]); + } + else if (!strcmp(argv[i], "-ttl")) + { + if (((i + 1) < argc) && (argv[i + 1][0] != '-')) module_ttl = store.ttl = atoi(argv[++i]); + } + else if (!strcmp(argv[i], "-size")) + { + if (((i + 1) < argc) && (argv[i + 1][0] != '-')) store.all_max = asl_str_to_size(argv[++i]); + } + else if (!strcmp(argv[i], "-checkpoint")) + { + work |= DO_CHECKPT; + } + else if (!strcmp(argv[i], "-module")) + { + work &= ~DO_ASLDB; + + /* optional name follows -module */ + if ((i +1) < argc) + { + if (argv[i + 1][0] != '-') mname = argv[++i]; + } + } + else if (!strcmp(argv[i], "-asldb")) + { + work = DO_ASLDB; + } + else if (!strcmp(argv[i], "-d")) + { + if (((i + i) < argc) && (argv[i+1][0] != '-')) set_debug(DEBUG_STDERR, argv[++i]); + else set_debug(DEBUG_STDERR, NULL); + } + else if (!strcmp(argv[i], "-dd")) + { + dryrun = 1; + + if (((i + i) < argc) && (argv[i+1][0] != '-')) set_debug(DEBUG_STDERR, argv[++i]); + else set_debug(DEBUG_STDERR, NULL); + } + } + + if (store.path == NULL) store.path = strdup(PATH_ASL_STORE); + + debug_log(ASL_LEVEL_ERR, "aslmanager starting%s\n", (dryrun == 1) ? " dryrun" : ""); + + if (work & DO_ASLDB) process_asl_data_store(&store); + + free(store.path); + free(store.rotate_dir); + + if (work & DO_MODULE) + { + if (work & DO_CHECKPT) checkpoint(mname); + + if (mod != NULL) + { + for (m = mod; m != NULL; m = m->next) + { + if ((mname == NULL) || ((m->name != NULL) && (!strcmp(m->name, mname)))) + { + process_module(m); + } + } + } + } + + asl_out_module_free(mod); + + debug_log(ASL_LEVEL_NOTICE, "----------------------------------------\n"); + debug_log(ASL_LEVEL_ERR, "aslmanager finished%s\n", (dryrun == 1) ? " dryrun" : ""); + if (asl_aux_fd >= 0) asl_close_auxiliary_file(asl_aux_fd); + + return 0; +} + +static void +accept_connection(xpc_connection_t peer) +{ + xpc_connection_set_event_handler(peer, ^(xpc_object_t request) { + if (xpc_get_type(request) == XPC_TYPE_DICTIONARY) + { + uid_t uid = xpc_connection_get_euid(peer); + + /* send a reply immediately */ + xpc_object_t reply = xpc_dictionary_create_reply(request); + xpc_connection_send_message(peer, reply); + xpc_release(reply); + + /* + * Some day, we may use the dictionary to pass parameters + * to aslmanager, but for now, we ignore the input. + */ + if (uid == 0) cli_main(0, NULL); + } + else if (xpc_get_type(request) == XPC_TYPE_ERROR) + { + /* disconnect */ + } + + dispatch_async(serverq, ^__attribute__((noreturn)) { xpc_server_exit(0); }); + }); + + xpc_connection_resume(peer); +} + +int +main(int argc, char *argv[]) +{ + int64_t is_managed = 0; + + vproc_swap_integer(NULL, VPROC_GSK_IS_MANAGED, NULL, &is_managed); + + if (is_managed == 0) return cli_main(argc, argv); + + /* XPC server */ + serverq = dispatch_queue_create("aslmanager", NULL); + xpc_track_activity(); + + /* Handle incoming messages. */ + listener = xpc_connection_create_mach_service("com.apple.aslmanager", serverq, XPC_CONNECTION_MACH_SERVICE_LISTENER); + xpc_connection_set_event_handler(listener, ^(xpc_object_t peer) { + if (xpc_get_type(peer) == XPC_TYPE_CONNECTION) accept_connection(peer); + }); + xpc_connection_resume(listener); + + dispatch_main(); +} diff --git a/aslmanager.tproj/com.apple.aslmanager.plist b/aslmanager.tproj/com.apple.aslmanager.plist index a44d2dc..05586a1 100644 --- a/aslmanager.tproj/com.apple.aslmanager.plist +++ b/aslmanager.tproj/com.apple.aslmanager.plist @@ -1,16 +1,23 @@ - + - Label - com.apple.aslmanager - ProgramArguments - + Label + com.apple.aslmanager + Program /usr/sbin/aslmanager - - WatchPaths - - /var/log/asl/SweepStore - + ProgramArguments + + aslmanager + + MachServices + + com.apple.aslmanager + + + EnableTransactions + + POSIXSpawnType + Interactive diff --git a/libsystem_asl.tproj/include/asl.h b/libsystem_asl.tproj/include/asl.h new file mode 100644 index 0000000..1ae909c --- /dev/null +++ b/libsystem_asl.tproj/include/asl.h @@ -0,0 +1,651 @@ +/* + * Copyright (c) 2004-2012 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#ifndef __ASL_H__ +#define __ASL_H__ + +#include +#include +#include +#include + +typedef struct __aslclient *aslclient; +typedef struct __aslmsg *aslmsg; +typedef struct __aslresponse *aslresponse; + +/*! @header + * These routines provide an interface to the Apple System Log facility. + * The API allows client applications to create flexible, structured messages + * and send them to the syslogd server. Messages received by the server are + * saved in a data store, subject to input filtering constraints. + * This API also permits clients to create queries and search the message + * data store for matching messages. + */ + +/* + * NOTE FOR HeaderDoc + * + * These are added to allow headerdoc2html to process + * the prototypes of asl_log and asl_vlog correctly. + * The "-p" option to headerdoc2html is required. + */ +#ifndef __printflike +/*! @parseOnly */ +#define __printflike(a,b) +#endif + +/*! @defineblock Log Message Priority Levels + * Log levels of the message. + */ +#define ASL_LEVEL_EMERG 0 +#define ASL_LEVEL_ALERT 1 +#define ASL_LEVEL_CRIT 2 +#define ASL_LEVEL_ERR 3 +#define ASL_LEVEL_WARNING 4 +#define ASL_LEVEL_NOTICE 5 +#define ASL_LEVEL_INFO 6 +#define ASL_LEVEL_DEBUG 7 +/*! @/defineblock */ + +/*! @defineblock Log Message Priority Level Strings + * Strings corresponding to log levels. + */ +#define ASL_STRING_EMERG "Emergency" +#define ASL_STRING_ALERT "Alert" +#define ASL_STRING_CRIT "Critical" +#define ASL_STRING_ERR "Error" +#define ASL_STRING_WARNING "Warning" +#define ASL_STRING_NOTICE "Notice" +#define ASL_STRING_INFO "Info" +#define ASL_STRING_DEBUG "Debug" +/*! @/defineblock */ + +/*! @defineblock Attribute Matching + * Attribute value comparison operations. + */ +#define ASL_QUERY_OP_CASEFOLD 0x0010 +#define ASL_QUERY_OP_PREFIX 0x0020 +#define ASL_QUERY_OP_SUFFIX 0x0040 +#define ASL_QUERY_OP_SUBSTRING 0x0060 +#define ASL_QUERY_OP_NUMERIC 0x0080 +#define ASL_QUERY_OP_REGEX 0x0100 + +#define ASL_QUERY_OP_EQUAL 0x0001 +#define ASL_QUERY_OP_GREATER 0x0002 +#define ASL_QUERY_OP_GREATER_EQUAL 0x0003 +#define ASL_QUERY_OP_LESS 0x0004 +#define ASL_QUERY_OP_LESS_EQUAL 0x0005 +#define ASL_QUERY_OP_NOT_EQUAL 0x0006 +#define ASL_QUERY_OP_TRUE 0x0007 +/*! @/defineblock */ + +/*! @defineblock Message Attributes + * + * These attributes are known by ASL, and are generally + * associated with all log messages. + * Additional attributes may be added as desired. + */ +#define ASL_KEY_TIME "Time" /* Timestamp. Set automatically */ +#define ASL_KEY_TIME_NSEC "TimeNanoSec" /* Nanosecond time. */ +#define ASL_KEY_HOST "Host" /* Sender's address (set by the server). */ +#define ASL_KEY_SENDER "Sender" /* Sender's identification string. Default is process name. */ +#define ASL_KEY_FACILITY "Facility" /* Sender's facility. Default is "user". */ +#define ASL_KEY_PID "PID" /* Sending process ID encoded as a string. Set automatically. */ +#define ASL_KEY_UID "UID" /* UID that sent the log message (set by the server). */ +#define ASL_KEY_GID "GID" /* GID that sent the log message (set by the server). */ +#define ASL_KEY_LEVEL "Level" /* Log level number encoded as a string. See levels above. */ +#define ASL_KEY_MSG "Message" /* Message text. */ +#define ASL_KEY_READ_UID "ReadUID" /* User read access (-1 is any user). */ +#define ASL_KEY_READ_GID "ReadGID" /* Group read access (-1 is any group). */ +#define ASL_KEY_EXPIRE_TIME "ASLExpireTime" /* Expiration time for messages with long TTL. */ +#define ASL_KEY_MSG_ID "ASLMessageID" /* 64-bit message ID number (set by the server). */ +#define ASL_KEY_SESSION "Session" /* Session (set by the launchd). */ +#define ASL_KEY_REF_PID "RefPID" /* Reference PID for messages proxied by launchd */ +#define ASL_KEY_REF_PROC "RefProc" /* Reference process for messages proxied by launchd */ +#define ASL_KEY_AUX_TITLE "ASLAuxTitle" /* Auxiliary title string */ +#define ASL_KEY_AUX_UTI "ASLAuxUTI" /* Auxiliary Uniform Type ID */ +#define ASL_KEY_AUX_URL "ASLAuxURL" /* Auxiliary Uniform Resource Locator */ +#define ASL_KEY_AUX_DATA "ASLAuxData" /* Auxiliary in-line data */ +#define ASL_KEY_OPTION "ASLOption" /* Internal */ +#define ASL_KEY_SENDER_INSTANCE "SenderInstance" /* Sender instance UUID. */ +/*! @/defineblock */ + +/*! @defineblock aslmsg Types + * Message type argument passed to asl_new(). + */ +#define ASL_TYPE_MSG 0 +#define ASL_TYPE_QUERY 1 +/*! @/defineblock */ + +/*! @defineblock Filter Masks + * Used in client-side filtering, which determines which + * messages are sent by the client to the syslogd server. + */ +#define ASL_FILTER_MASK_EMERG 0x01 +#define ASL_FILTER_MASK_ALERT 0x02 +#define ASL_FILTER_MASK_CRIT 0x04 +#define ASL_FILTER_MASK_ERR 0x08 +#define ASL_FILTER_MASK_WARNING 0x10 +#define ASL_FILTER_MASK_NOTICE 0x20 +#define ASL_FILTER_MASK_INFO 0x40 +#define ASL_FILTER_MASK_DEBUG 0x80 +/*! @/defineblock */ + +/*! @defineblock Filter Mask Macros + * Macros to create bitmasks for filter settings - see asl_set_filter(). + */ +#define ASL_FILTER_MASK(level) (1 << (level)) +#define ASL_FILTER_MASK_UPTO(level) ((1 << ((level) + 1)) - 1) +/*! @/defineblock */ + +/*! @defineblock Client Creation Options + * Options for asl_open(). + */ +#define ASL_OPT_STDERR 0x00000001 +#define ASL_OPT_NO_DELAY 0x00000002 +#define ASL_OPT_NO_REMOTE 0x00000004 +/*! @/defineblock */ + +/*! @defineblock File Descriptor Types + * Instructions on how to treat the file descriptor in asl_log_descriptor(). + */ +#define ASL_LOG_DESCRIPTOR_READ 1 +#define ASL_LOG_DESCRIPTOR_WRITE 2 + +/*! @defineblock Output file message and time formats. + * These select internally defined formats for printed log messages for + * asl_add_output_file(). Custom message and time formats may also be + * used. These pre-defined formats and custom formats are described in detail + * in the syslog(1) manual page. + */ +#define ASL_MSG_FMT_RAW "raw" +#define ASL_MSG_FMT_STD "std" +#define ASL_MSG_FMT_BSD "bsd" +#define ASL_MSG_FMT_XML "xml" +#define ASL_MSG_FMT_MSG "msg" + +#define ASL_TIME_FMT_SEC "sec" +#define ASL_TIME_FMT_UTC "utc" +#define ASL_TIME_FMT_LCL "lcl" + +/*! @defineblock Text Encoding Types + * These are used by the library when formatting messages to be written + * to file descriptors associated with an ASL client handle with + * asl_add_output_file(). The syslog(1) manual page describes text encoding + * in detail. ASL_ENCODE_ASL corresponds to the "vis" encoding option + * described in the syslog(1) manual. ASL_ENCODE_XML should be used in + * combination with ASL_MSG_FMT_XML to ensure that special XML characters + * are correctly encoded. + */ +#define ASL_ENCODE_NONE 0 +#define ASL_ENCODE_SAFE 1 +#define ASL_ENCODE_ASL 2 +#define ASL_ENCODE_XML 3 + +/*! + * ASL_PREFILTER_LOG is a macro similar to asl_log(), but it first checks + * if the message will simply be ignored due to local filter settings. + * This prevents the variable argument list from being evaluated. + * Note that the message may still be processed if it will be written + * to a file or stderr. + * + * @param asl + * (input) An ASL client handle + * @param msg + * (input) An aslmsg (default attributes will be supplied if msg is NULL) + * @param level + * (input) Log level (ASL_LEVEL_DEBUG to ASL_LEVEL_EMERG) + * @param format + * (input) A printf() - style format string followed by a list of arguments + */ +#define ASL_PREFILTER_LOG(asl, msg, level, format, ...) \ + do { \ + aslclient _asl = (asl); \ + aslmsg _msg = (msg); \ + uint32_t _asl_eval = _asl_evaluate_send(_asl, _msg, (level)); \ + if (_asl_eval != 0) _asl_lib_log(_asl, _asl_eval, _msg, (format), ## __VA_ARGS__); \ + } while (0) + +__BEGIN_DECLS + +/* ASL Library SPI - do not call directly */ +int _asl_lib_log(aslclient asl, uint32_t eval, aslmsg msg, const char *format, ...) __printflike(4, 5); + +uint32_t _asl_evaluate_send(aslclient asl, aslmsg msg, int level); + +/*! + * Initialize a connection to the ASL server. + * + * This call is optional in most cases. The library will perform any + * necessary initializations on the fly. A call to asl_open() is required + * if optional settings must be made before messages are sent to the server. + * These include setting the client filter and managing additional output + * file descriptors. Note that the default setting of the client filter is + * ASL_FILTER_MASK_UPTO(ASL_LEVEL_NOTICE), so ASL_LEVEL_DEBUG and ASL_LEVEL_INFO + * messages are not sent to the server by default. + * + * Options (defined above) may be set using the opts parameter. They are: + * + * ASL_OPT_STDERR - adds stderr as an output file descriptor + * + * ASL_OPT_NO_DELAY - connects to the server immediately + * + * ASL_OPT_NO_REMOTE - disables the remote-control mechanism for adjusting + * filter levers for processes using e.g. syslog -c ... + * + * @param ident + * (input) Sender name + * @param facility + * (input) Facility name + * @param opts + * (input) Options (see asl_open Options) + * @result Returns an ASL client handle + */ +aslclient asl_open(const char *ident, const char *facility, uint32_t opts); + +/*! + * Shuts down a connection to the server. + * + * @param asl + * (input) An ASL client handle + */ +void asl_close(aslclient asl); + +/*! + * Write log messages to the given file descriptor. + * + * Log messages will be written to this file as well as to the server. + * This is equivalent to calling: + * asl_add_output_file(asl, descriptor, ASL_MSG_FMT_STD, ASL_TIME_FMT_LCL, ASL_FILTER_MASK_UPTO(ASL_LEVEL_DEBUG), ASL_ENCODE_SAFE) + * + * @param asl + * (input) An ASL client handle + * @param descriptor + * (input) A file descriptor + * @result Returns 0 on success, non-zero on failure +*/ +int asl_add_log_file(aslclient asl, int descriptor); + +/*! + * Write log messages to the given file descriptor. + * + * Log messages will be written to this file as well as to the server. + * This routine extends the basic interface offered by asl_add_log_file(), + * allowing control of the format used to write log message written to the file. + * control of the time zone used when printing time values, and allowing + * individual filtering control for each log file. + * + * @param asl + * (input) An ASL client handle + * @param descriptor + * (input) A file descriptor + * @param mfmt + * (input) A character string specifying the message format + * @param tfmt + * (input) A character string specifying the time format + * @param filter + * (input) A filter value + * @param text_encoding + * (input) A text encoding type + * @result Returns 0 on success, non-zero on failure + */ +int asl_add_output_file(aslclient asl, int descriptor, const char *mfmt, const char *tfmt, int filter, int text_encoding) __OSX_AVAILABLE_STARTING(__MAC_10_9, __IPHONE_7_0); + +/*! + * Write log messages to the given file descriptor. + * + * Sets or changes a filter value for filtering messages written to a file associated + * with an ASL client handle using asl_add_output_file() or asl_add_log_file(). + * + * @param asl + * (input) An ASL client handle + * @param descriptor + * (input) A file descriptor + * @param filter + * (input) A filter value + * @result Returns the previous filter value + */ +int asl_set_output_file_filter(aslclient ac, int fd, int filter) __OSX_AVAILABLE_STARTING(__MAC_10_9, __IPHONE_7_0); + +/*! + * Stop writing log messages to the given file descriptor. + * The file descripter is not closed by this routine. + * + * @param asl + * (input) An ASL client handle + * @param descriptor + * (input) A file descriptor + * @result Returns 0 on success, non-zero on failure + */ +int asl_remove_log_file(aslclient asl, int descriptor); + +/*! + * Set a filter for messages being sent to the server. + * The filter is a bitmask representing priorities. The ASL_FILTER_MASK + * macro may be used to convert a priority level into a bitmask for that + * level. The ASL_FILTER_MASK_UPTO macro creates a bitmask for all + * priorities up to and including a given priority. + * Messages with priority levels that do not have a corresponding bit + * set in the filter are not sent to the server, although they will be + * sent to any file descripters added with asl_add_log_file(). + * The default setting is ASL_FILTER_MASK_UPTO(ASL_LEVEL_NOTICE). + * Returns the previous filter value. + * + * @param asl + * (input) An ASL client handle + * @param f + * (input) A filter value + * @result Returns the previous filter value + */ +int asl_set_filter(aslclient asl, int f); + +/* + * Examine attribute keys. + * + * @param msg + * (input) An ASL message + * @param n + * (input) An index value + * @result Returns the key of the nth attribute in a message (beginning at zero), + * or NULL if n is greater than the largest message index. + */ +const char *asl_key(aslmsg msg, uint32_t n); + +/*! + * Create a new log message or query message. + * + * @param type + * (input) Message type (see aslmsg Types) + * @result Returns a newly allocated asmsg of the specified type + */ +aslmsg asl_new(uint32_t type); + +/*! + * Set or re-set a message attribute. + * + * @param msg + * (input) An aslmsg + * @param key + * (input) Attribute key + * @param value + * (input) Attribute value + * @result returns 0 for success, non-zero for failure + */ +int asl_set(aslmsg msg, const char *key, const char *value); + +/*! + * Remove a message attribute. + * + * @param msg + * (input) An aslmsg + * @param key + * (input) Attribute key + * returns 0 for success, non-zero for failure + */ +int asl_unset(aslmsg msg, const char *key); + +/*! + * Get the value of a message attribute. + * + * @param msg + * (input) An aslmsg + * @param key + * (input) Attribute key + * @result Returns the attribute value, or NULL if the message does not contain the key + */ +const char *asl_get(aslmsg msg, const char *key); + +/*! + * Log a message with a particular log level. + * + * @param asl + * (input) An ASL client handle + * @param msg + * (input) An aslmsg (default attributes will be supplied if msg is NULL) + * @param level + * (input) Log level (ASL_LEVEL_DEBUG to ASL_LEVEL_EMERG) + * @param format + * (input) A printf() - style format string followed by a list of arguments + * @result Returns 0 for success, non-zero for failure + */ +int asl_log(aslclient asl, aslmsg msg, int level, const char *format, ...) __printflike(4, 5); + +/*! + * Log a message with a particular log level. + * Similar to asl_log, but takes a va_list argument. + * + * @param asl + * (input) An ASL client handle + * @param msg + * (input) An aslmsg (default attributes will be supplied if msg is NULL) + * @param level + * (input) Log level (ASL_LEVEL_DEBUG to ASL_LEVEL_EMERG) + * @param format + * (input) A printf() - style format string followed by a list of arguments + * @param ap + * (input) A va_list containing the values for the format string + * @result Returns 0 for success, non-zero for failure + */ +int asl_vlog(aslclient asl, aslmsg msg, int level, const char *format, va_list ap) __printflike(4, 0); + +/*! + * Log a message. + * + * This routine may be used instead of asl_log() or asl_vlog() if asl_set() + * has been used to set all of a message's attributes. + * + * @param asl + * (input) An ASL client handle + * @param msg + * (input) An aslmsg + * @result Returns 0 for success, non-zero for failure + */ +int asl_send(aslclient asl, aslmsg msg); + +/*! + * Free a message. Frees all the attribute keys and values. + * + * @param msg + * (input) An aslmsg to free + */ +void asl_free(aslmsg msg); + +/*! + * Set arbitrary parameters of a query. + * This is similar to asl_set, but allows richer query operations. + * See ASL_QUERY_OP_* above. + * + * @param msg + * (input) An aslmsg + * @param key + * (input) Attribute key + * @param value + * (input) Attribute value + * @param op + * (input) An operation (ASL_QUERY_OP_*) + * @result Returns 0 for success, non-zero for failure + */ +int asl_set_query(aslmsg msg, const char *key, const char *value, uint32_t op); + +/*! + * Search for messages matching the criteria described by the aslmsg. + * The caller should set the attributes to match using asl_set_query() or asl_set(). + * The operatoin ASL_QUERY_OP_EQUAL is used for attributes set with asl_set(). + * + * @param msg + * (input) An aslmsg to match + * @result Returns a set of messages accessable using aslresponse_next(), + */ +aslresponse asl_search(aslclient asl, aslmsg msg); + +/*! + * Iterate over responses returned from asl_search(). + * + * @param r + * (input) An aslresponse returned by asl_search() + * @result Returns the next message (an aslmsg) in the response, or NULL when there are no more messages + */ +aslmsg aslresponse_next(aslresponse r); + +/*! + * Free a response returned from asl_search(). + * @param r + * (input) An aslresponse returned by asl_search() + */ +void aslresponse_free(aslresponse r); + +/*! + * Creates an auxiliary file that may be used to save arbitrary data. The ASL message msg + * will be saved at the time that the auxiliary file is closed with asl_close_auxiliary_file(). + * The log entry will include any keys and values found in msg, and it will include the title + * and Uniform Type Identifier specified. If NULL is supplied as a value for the uti parameter, + * the type "public.data" is used. Console.app will display a hyperlink to the file. + * Output parameter out_descriptor will contain a readable and writable file descriptor for the new + * auxiliary file. + * + * By default, the file will be world-readable. If the message contains a ReadUID and/or a + * ReadGID key, then the values for those keys will determine read access to the file. + * + * The file will be deleted at the same time that the message expires from the ASL data store. + * The aslmanager utility manages message expiry. If msg contains a value for ASLExpireTime, + * then the message and the file will not be deleted before that time. The value may be in + * seconds after the Epoch, or it may be ctime() format, e.g "Thu Jun 24 18:22:48 2010". + * + * @param msg + * (input) An aslmsg + * @param tite + * (input) A title string for the file + * @param uti + * (input) Uniform Type Identifier for the file + * @param out_descriptor + * (output) A writable file descriptor + * @result Returns 0 for success, non-zero for failure + */ +int asl_create_auxiliary_file(aslmsg msg, const char *title, const char *uti, int *out_descriptor) +__OSX_AVAILABLE_STARTING(__MAC_10_7,__IPHONE_5_0); + +/*! + * Close an auxiliary file opened by asl_create_auxiliary_file() when writing is complete. + * syslogd will log the message provided to asl_create_auxiliary_file() when this routine + * is called. + * + * @param descriptor + * (input) The file descriptor + * @result Returns 0 for success, non-zero for failure + */ +int asl_close_auxiliary_file(int descriptor) +__OSX_AVAILABLE_STARTING(__MAC_10_7,__IPHONE_5_0); + +/*! + * Sends an ASL message to syslogd along with a title string, Uniform Resource Locator, + * and Uniform Type Identifier specified. Console.app will hyperlink the title string to + * the specified URL. If NULL is supplied as a value for the uti parameter, the default + * type "public.data" is used. + * + * @param msg + * (input) An aslmsg + * @param title + * (input) A title string for the file + * @param uti + * (input) Uniform Type Identifier for the file + * @param url + * (input) Uniform Type Locator + * @result Returns 0 for success, non-zero for failure + */ +int asl_log_auxiliary_location(aslmsg msg, const char *title, const char *uti, const char *url) +__OSX_AVAILABLE_STARTING(__MAC_10_7,__IPHONE_5_0); + +/*! + * Creates an aslclient for logging to a file descriptor. The file must be opened for read and + * write access. This routine may be used in conjunction with asl_create_auxiliary_file() to + * save ASL format log messages to an auxiliary file. + * + * The file will be truncated if it is not empty. When logging to the auxiliary file is complete, + * aslclient should be closed using asl_close(). The file should be closed using + * asl_close_auxiliary_file() if it was returned by asl_create_auxiliary_file(), or close() + * otherwise. + * + * The returned aslclient is thread-safe. + * + * Note that per-message read access controls (ReadUID and ReadGID) and message expire + * times (ASLExpireTime) keys have no effect for messages written to this file. + * + * @param descriptor + * (input) A file descriptor + * @param ident + * (input) Sender name + * @param facility + * (input) Facility name + * @result An aslclient + */ +aslclient asl_open_from_file(int descriptor, const char *ident, const char *facility) +__OSX_AVAILABLE_STARTING(__MAC_10_7,__IPHONE_5_0); + +/*! + * This API provides functionality to use file descriptors to send logging + * data to ASL. + * + * asl is retained by ASL and must still be closed by the caller by calling + * asl_close() if the caller loses reference to it. msg is copied by ASL and + * similarly must still be freed by the caller by calling asl_free() if the + * caller loses reference to it. Any changes made to it after calling + * asl_log_descriptor() are not applicable to the message used. descriptor + * is treated differentlty based on the value of fd_type. + * + * If fd_type is ASL_LOG_DESCRIPTOR_READ, the descriptor must be open for read + * access. ASL uses GCD to read from the descriptor as data becomes available. + * These data are line buffered and passed to asl_log. When EOF is read, the + * descriptor is closed. + * + * Example: + * asl_log_descriptor(c, m, ASL_LEVEL_NOTICE, STDIN_FILENO, ASL_LOG_DESCRIPTOR_READ); + * + * If fd_type is ASL_LOG_DESCRIPTOR_WRITE, the descriptor is closed and a new + * writable descriptor is created with the same fileno. Any data written to + * this new descriptor are line buffered and passed to asl_log. When EOF is + * sent, no further data are read. The caller is responsible for closing the + * new descriptor. One common use for this API is to redirect writes to stdout + * or stderr to ASL by passing STDOUT_FILENO or STDERR_FILENO as descriptor. + * + * Example: + * asl_log_descriptor(c, m, ASL_LEVEL_NOTICE, STDOUT_FILENO, ASL_LOG_DESCRIPTOR_WRITE); + * asl_log_descriptor(c, m, ASL_LEVEL_ERR, STDERR_FILENO, ASL_LOG_DESCRIPTOR_WRITE); + * + * @param asl + * (input) An ASL client handle + * @param msg + * (input) An aslmsg (default attributes will be supplied if msg is NULL) + * @param level + * (input) Log level (ASL_LEVEL_DEBUG to ASL_LEVEL_EMERG) + * @param descriptor + * (input) An open file descriptor to read from + * @param fd_type + * (input) Either ASL_LOG_DESCRIPTOR_READ or ASL_LOG_DESCRIPTOR_WRITE + * @result Returns 0 for success, non-zero for failure + */ +int asl_log_descriptor(aslclient asl, aslmsg msg, int level, int descriptor, uint32_t fd_type) +__OSX_AVAILABLE_STARTING(__MAC_10_8,__IPHONE_5_1); + +__END_DECLS + +#endif /* __ASL_H__ */ diff --git a/libsystem_asl.tproj/include/asl_core.h b/libsystem_asl.tproj/include/asl_core.h new file mode 100644 index 0000000..722e860 --- /dev/null +++ b/libsystem_asl.tproj/include/asl_core.h @@ -0,0 +1,114 @@ +#ifndef __ASL_CORE_H__ +#define __ASL_CORE_H__ + +/* + * Copyright (c) 2007-2011 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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, 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 +#include +#include + +typedef struct +{ + uint32_t encoding; + size_t delta; + size_t bufsize; + size_t cursor; + char *buf; +} asl_string_t; + +#define ASL_STATUS_OK 0 +#define ASL_STATUS_INVALID_ARG 1 +#define ASL_STATUS_INVALID_STORE 2 +#define ASL_STATUS_INVALID_STRING 3 +#define ASL_STATUS_INVALID_ID 4 +#define ASL_STATUS_INVALID_MESSAGE 5 +#define ASL_STATUS_NOT_FOUND 6 +#define ASL_STATUS_READ_FAILED 7 +#define ASL_STATUS_WRITE_FAILED 8 +#define ASL_STATUS_NO_MEMORY 9 +#define ASL_STATUS_ACCESS_DENIED 10 +#define ASL_STATUS_READ_ONLY 11 +#define ASL_STATUS_WRITE_ONLY 12 +#define ASL_STATUS_MATCH_FAILED 13 +#define ASL_STATUS_NO_RECORDS 14 +#define ASL_STATUS_FAILED 9999 + +#define ASL_REF_NULL 0xffffffffffffffffLL + +#define ASL_MSG_FLAG_READ_UID_SET 0x0001 +#define ASL_MSG_FLAG_READ_GID_SET 0x0002 +#define ASL_MSG_FLAG_SEARCH_MATCH 0x8000 +#define ASL_MSG_FLAG_SEARCH_CLEAR 0x7fff + +#define ASL_QUERY_MATCH_SLOW 0x00000000 +#define ASL_QUERY_MATCH_MSG_ID 0x00000001 +#define ASL_QUERY_MATCH_TIME 0x00000002 +#define ASL_QUERY_MATCH_NANO 0x00000004 +#define ASL_QUERY_MATCH_LEVEL 0x00000008 +#define ASL_QUERY_MATCH_PID 0x00000010 +#define ASL_QUERY_MATCH_UID 0x00000020 +#define ASL_QUERY_MATCH_GID 0x00000040 +#define ASL_QUERY_MATCH_RUID 0x00000080 +#define ASL_QUERY_MATCH_RGID 0x00000100 +#define ASL_QUERY_MATCH_REF_PID 0x00000200 +#define ASL_QUERY_MATCH_HOST 0x00000400 +#define ASL_QUERY_MATCH_SENDER 0x00000800 +#define ASL_QUERY_MATCH_SMUUID 0x00001000 +#define ASL_QUERY_MATCH_FACILITY 0x00002000 +#define ASL_QUERY_MATCH_MESSAGE 0x00004000 +#define ASL_QUERY_MATCH_REF_PROC 0x00008000 +#define ASL_QUERY_MATCH_SESSION 0x00010000 +#define ASL_QUERY_MATCH_TRUE 0x80000000 +#define ASL_QUERY_MATCH_FALSE 0x40000000 +#define ASL_QUERY_MATCH_ERROR 0x20000000 + +#define ASL_ENCODE_MASK 0x0000000f +#define ASL_STRING_VM 0x80000000 +#define ASL_STRING_LEN 0x40000000 + +#define ASL_STRING_MIG 0xc0000002 + +uint32_t asl_core_string_hash(const char *str, uint32_t len) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); +const char *asl_core_error(uint32_t code) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); +uint32_t asl_core_check_access(int32_t msgu, int32_t msgg, int32_t readu, int32_t readg, uint16_t flags) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); +uint64_t asl_core_htonq(uint64_t n) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); +uint64_t asl_core_ntohq(uint64_t n)__OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); +uint64_t asl_core_new_msg_id(uint64_t start) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); +char *asl_core_encode_buffer(const char *in, uint32_t len) __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0); +int32_t asl_core_decode_buffer(const char *in, char **buf, uint32_t *len) __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0); + +asl_string_t *asl_string_new(uint32_t encoding) __OSX_AVAILABLE_STARTING(__MAC_10_8, __IPHONE_5_1); +char *asl_string_free_return_bytes(asl_string_t *str) __OSX_AVAILABLE_STARTING(__MAC_10_8, __IPHONE_5_1); +void asl_string_free(asl_string_t *str) __OSX_AVAILABLE_STARTING(__MAC_10_8, __IPHONE_5_1); +char *asl_string_bytes(asl_string_t *str) __OSX_AVAILABLE_STARTING(__MAC_10_8, __IPHONE_5_1); +size_t asl_string_length(asl_string_t *str) __OSX_AVAILABLE_STARTING(__MAC_10_8, __IPHONE_5_1); +size_t asl_string_allocated_size(asl_string_t *str) __OSX_AVAILABLE_STARTING(__MAC_10_8, __IPHONE_5_1); +asl_string_t *asl_string_append(asl_string_t *str, const char *app) __OSX_AVAILABLE_STARTING(__MAC_10_8, __IPHONE_5_1); +asl_string_t *asl_string_append_asl_key(asl_string_t *str, const char *app) __OSX_AVAILABLE_STARTING(__MAC_10_8, __IPHONE_5_1); +asl_string_t *asl_string_append_op(asl_string_t *str, uint32_t op) __OSX_AVAILABLE_STARTING(__MAC_10_8, __IPHONE_5_1); +asl_string_t *asl_string_append_no_encoding(asl_string_t *str, const char *app) __OSX_AVAILABLE_STARTING(__MAC_10_8, __IPHONE_5_1); +asl_string_t *asl_string_append_char_no_encoding(asl_string_t *str, const char c) __OSX_AVAILABLE_STARTING(__MAC_10_8, __IPHONE_5_1); +asl_string_t *asl_string_append_xml_tag(asl_string_t *str, const char *tag, const char *s) __OSX_AVAILABLE_STARTING(__MAC_10_8, __IPHONE_5_1); + +#endif /* __ASL_CORE_H__ */ diff --git a/libsystem_asl.tproj/include/asl_file.h b/libsystem_asl.tproj/include/asl_file.h new file mode 100644 index 0000000..d550d8d --- /dev/null +++ b/libsystem_asl.tproj/include/asl_file.h @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2007-2011 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#ifndef __ASL_FILE_H__ +#define __ASL_FILE_H__ + +#include +#include +#include +#include +#include + +#define DB_HEADER_LEN 80 +#define DB_HEADER_COOKIE_OFFSET 0 +#define DB_HEADER_VERS_OFFSET 12 +#define DB_HEADER_FIRST_OFFSET 16 +#define DB_HEADER_TIME_OFFSET 24 +#define DB_HEADER_CSIZE_OFFSET 32 +#define DB_HEADER_LAST_OFFSET 36 + +/* + * Magic Cookie for database files. + * MAXIMUM 12 CHARS! (DB_HEADER_VERS_OFFSET) + */ +#define ASL_DB_COOKIE "ASL DB" +#define ASL_DB_COOKIE_LEN 6 +#define DB_VERSION 2 +#define DB_VERSION_LEGACY_1 1 + +#define ASL_FILE_FLAG_READ_ONLY 0x00000001 +#define ASL_FILE_FLAG_UNLIMITED_CACHE 0x00000002 +#define ASL_FILE_FLAG_PRESERVE_MSG_ID 0x00000004 +#define ASL_FILE_FLAG_LEGACY_STORE 0x00000008 + +#define ASL_FILE_TYPE_MSG 0 +#define ASL_FILE_TYPE_STR 1 + +#define ASL_FILE_POSITION_FIRST 0 +#define ASL_FILE_POSITION_PREVIOUS 1 +#define ASL_FILE_POSITION_NEXT 2 +#define ASL_FILE_POSITION_LAST 3 + +/* NB CACHE_SIZE must be > 1 */ +#define CACHE_SIZE 256 + +/* Size of the fixed-length part of a MSG record */ +#define MSG_RECORD_FIXED_LENGTH 122 + +/* + * The first record (header) in the database has the format: + * + * | 12 | 4 | 8 | 8 | 4 | 8 | 36 | (80 bytes) + * | Cookie | Vers | First | Time | String cache size | Last | Zero | + * + * MSG records have the format: + * + * | 2 | 4 | 8 | 8 | 8 | 4 | 2 | 2 | 4 | 4 | 4 | 4 | 4 | 4 | 4 + * | 00 | Len | Next | ID | Time | Nano | Level | Flags | PID | UID | GID | RUID | RGID | RefPID | KV count ... + * + * | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | | 8 + * | Host | Sender | Facility | Message | RefProc | Session | Key0 | Val0 | ... | Previous | + * + * STR records have the format: + * + * | 2 | 4 | Len | (Len + 6 bytes) + * | 01 | Len | Data+NUL | + * + */ + +typedef struct file_string_s +{ + uint64_t where; + uint32_t hash; + struct file_string_s *next; + char str[]; +} file_string_t; + +typedef struct +{ + uint32_t flags; + uint32_t version; + uint32_t string_count; + file_string_t *string_list; + uint64_t first; + uint64_t last; + uint64_t prev; + uint64_t cursor; + uint64_t cursor_xid; + uint64_t dob; + size_t file_size; + FILE *store; + void *legacy; + char *scratch; +} asl_file_t; + +typedef struct asl_file_list_s +{ + asl_file_t *file; + struct asl_file_list_s *next; +} asl_file_list_t; + +asl_file_list_t *asl_file_list_add(asl_file_list_t *list, asl_file_t *f) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); +void asl_file_list_close(asl_file_list_t *list) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); + +uint32_t asl_file_open_write(const char *path, mode_t mode, uid_t uid, gid_t gid, asl_file_t **s) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); +uint32_t asl_file_close(asl_file_t *s) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); + +uint32_t asl_file_save(asl_file_t *s, aslmsg msg, uint64_t *mid) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); + +uint32_t asl_file_open_read(const char *path, asl_file_t **s) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); +uint32_t asl_file_fetch(asl_file_t *s, uint64_t mid, aslmsg *msg) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); + +uint32_t asl_file_read_set_position(asl_file_t *s, uint32_t pos) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); +uint32_t asl_file_fetch_next(asl_file_t *s, aslmsg *msg) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); +uint32_t asl_file_fetch_previous(asl_file_t *s, aslmsg *msg) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); + +uint32_t asl_file_match(asl_file_t *s, aslresponse query, aslresponse *res, uint64_t *last_id, uint64_t start_id, uint32_t count, int32_t direction) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); +uint32_t asl_file_list_match_timeout(asl_file_list_t *list, aslresponse query, aslresponse *res, uint64_t *last_id, uint64_t start_id, uint32_t count, int32_t direction, uint32_t usec) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_3_2); +uint32_t asl_file_list_match(asl_file_list_t *list, aslresponse query, aslresponse *res, uint64_t *last_id, uint64_t start_id, uint32_t count, int32_t direction) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); + +void *asl_file_list_match_start(asl_file_list_t *list, uint64_t start_id, int32_t direction) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); +uint32_t asl_file_list_match_next(void *token, aslresponse query, aslresponse *res, uint32_t count) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); +void asl_file_list_match_end(void *token) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); + +size_t asl_file_size(asl_file_t *s) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); +uint64_t asl_file_ctime(asl_file_t *s) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); + +uint32_t asl_file_compact(asl_file_t *s, const char *path, mode_t mode, uid_t uid, gid_t gid) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); + +#endif /* __ASL_FILE_H__ */ diff --git a/libsystem_asl.tproj/include/asl_legacy1.h b/libsystem_asl.tproj/include/asl_legacy1.h new file mode 100644 index 0000000..a2efcde --- /dev/null +++ b/libsystem_asl.tproj/include/asl_legacy1.h @@ -0,0 +1,85 @@ +#ifndef __ASL_LEGACY1_H__ +#define __ASL_LEGACY1_H__ + +/* + * Copyright (c) 2007-2011 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ */ + +/* + * ASL Database VERSION 1 (LEGACY) + * + * Log messages are stored in 80 byte records of the form: + * + * | 1 | 4 | 8 | 4 | 4 | 8 | 8 | 8 | 8 | 4 | 4 | 4 | 4 | 8 | 2 | 1 | (80 bytes) + * | Type | Next | ID | RUID | RGID | Time | Host | Sender | Facility | LEVEL | PID | UID | GID | Message | Flags | Zero | + * + * If there are no additional key/value pairs in the message, Next will be zero. If there are additional + * key/value pairs in the database, Next is a record number for a record with the format: + * + * | 1 | 4 | 4 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 7 | (80 bytes) + * | Type | Next | Count | Key1 | Val1 | Key2 | Val2 | Key3 | Val3 | Key4 | Val4 | Zero | + * + * Additional records will be chained using the Next field, with the count field left zero. + * + * Strings stored in records of the form: + * + * | 1 | 4 | 8 | 4 | 4 | 4 | 55 | (80 bytes) + * | Type | Next | ID | Refcount | Hash | Length | String | + * + * If the string is longer than 55 bytes, Next is a record number for a record with the format: + * + * | 1 | 4 | 75 | (80 bytes) + * | Type | Next | String | + * + * The first record (header) in the database has the format: + * + * | 12 | 4 | 8 | 56 | (80 bytes) + * | Cookie | Vers | Max ID | Zero | + * + */ + +#include +#include +#include +#include +#include + +typedef struct +{ + uint8_t type; + uint32_t slot; + uint64_t xid; + uint32_t hash; +} asl_legacy1_slot_info_t; + +typedef struct +{ + asl_legacy1_slot_info_t *slotlist; + uint32_t slotlist_count; + FILE *db; +} asl_legacy1_t; + +uint32_t asl_legacy1_open(const char *path, asl_legacy1_t **s) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); +uint32_t asl_legacy1_close(asl_legacy1_t *s) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); +uint32_t asl_legacy1_fetch(asl_legacy1_t *s, uint64_t msgid, aslmsg *msg) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); +uint32_t asl_legacy1_match(asl_legacy1_t *s, aslresponse query, aslresponse *res, uint64_t *last_id, uint64_t start_id, uint32_t count, int32_t direction) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); + +#endif /*__ASL_LEGACY1_H__*/ diff --git a/libsystem_asl.tproj/include/asl_msg.h b/libsystem_asl.tproj/include/asl_msg.h new file mode 100644 index 0000000..9540471 --- /dev/null +++ b/libsystem_asl.tproj/include/asl_msg.h @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2009-2012 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#ifndef __ASL_MSG_H__ +#define __ASL_MSG_H__ + +#include +#include +#include + +#define IndexNull ((uint32_t)-1) + +#define ASL_MSG_PAGE_DATA_SIZE 800 +#define ASL_MSG_PAGE_SLOTS 24 + +#define ASL_MSG_OFFSET_MASK 0x3fff +#define ASL_MSG_KV_MASK 0xc000 +#define ASL_MSG_KV_INLINE 0x0000 +#define ASL_MSG_KV_DICT 0x8000 +#define ASL_MSG_KV_EXTERN 0x4000 + +#define ASL_MSG_SLOT_FREE 0xffff + +#define ASL_STD_KEY_BASE 0x8000 +#define ASL_STD_KEY_TIME 0x8001 +#define ASL_STD_KEY_NANO 0x8002 +#define ASL_STD_KEY_HOST 0x8003 +#define ASL_STD_KEY_SENDER 0x8004 +#define ASL_STD_KEY_FACILITY 0x8005 +#define ASL_STD_KEY_PID 0x8006 +#define ASL_STD_KEY_UID 0x8007 +#define ASL_STD_KEY_GID 0x8008 +#define ASL_STD_KEY_LEVEL 0x8009 +#define ASL_STD_KEY_MESSAGE 0x800a +#define ASL_STD_KEY_READ_UID 0x800b +#define ASL_STD_KEY_READ_GID 0x800c +#define ASL_STD_KEY_SESSION 0x800d +#define ASL_STD_KEY_REF_PID 0x800e +#define ASL_STD_KEY_REF_PROC 0x800f +#define ASL_STD_KEY_MSG_ID 0x8010 +#define ASL_STD_KEY_EXPIRE 0x8011 +#define ASL_STD_KEY_OPTION 0x8012 +#define ASL_STD_KEY_LAST ASL_STD_KEY_OPTION + +#define ASL_MT_KEY_BASE 0x8100 +#define ASL_MT_KEY_DOMAIN 0x8101 +#define ASL_MT_KEY_SCOPE 0x8102 +#define ASL_MT_KEY_RESULT 0x8103 +#define ASL_MT_KEY_SIG 0x8104 +#define ASL_MT_KEY_SIG2 0x8105 +#define ASL_MT_KEY_SIG3 0x8106 +#define ASL_MT_KEY_SUCCESS 0x8107 +#define ASL_MT_KEY_UUID 0x8108 +#define ASL_MT_KEY_VAL 0x8109 +#define ASL_MT_KEY_VAL2 0x810a +#define ASL_MT_KEY_VAL3 0x810b +#define ASL_MT_KEY_VAL4 0x810c +#define ASL_MT_KEY_VAL5 0x810d +#define ASL_MT_KEY_LAST ASL_MT_KEY_VAL5 + +#define ASL_PRIVATE_KEY_BASE 0x8200 + +typedef struct asl_msg_s +{ + uint32_t type; + int32_t refcount; + uint32_t count; + uint32_t data_size; + struct asl_msg_s *next; + uint16_t key[ASL_MSG_PAGE_SLOTS]; + uint16_t val[ASL_MSG_PAGE_SLOTS]; + uint32_t op[ASL_MSG_PAGE_SLOTS]; + char data[ASL_MSG_PAGE_DATA_SIZE]; +} asl_msg_t; + +typedef struct __aslresponse +{ + uint32_t count; + uint32_t curr; + asl_msg_t **msg; +} asl_msg_list_t; + +#define asl_search_result_t asl_msg_list_t + +asl_msg_t *asl_msg_new(uint32_t type) __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_3); +asl_msg_t *asl_msg_retain(asl_msg_t *msg) __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_3); +void asl_msg_release(asl_msg_t *msg) __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_3); + +int asl_msg_set_key_val(asl_msg_t *msg, const char *key, const char *val) __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_3); +int asl_msg_set_key_val_op(asl_msg_t *msg, const char *key, const char *val, uint32_t op) __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_3); +void asl_msg_unset(asl_msg_t *msg, const char *key) __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_3); + +asl_msg_t *asl_msg_copy(asl_msg_t *msg) __OSX_AVAILABLE_STARTING(__MAC_10_8, __IPHONE_5_1); +asl_msg_t *asl_msg_merge(asl_msg_t *target, asl_msg_t *msg) __OSX_AVAILABLE_STARTING(__MAC_10_8, __IPHONE_5_1); + +int asl_msg_lookup(asl_msg_t *msg, const char *key, const char **valout, uint32_t *opout) __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_3); +uint32_t asl_msg_fetch(asl_msg_t *msg, uint32_t n, const char **keyout, const char **valout, uint32_t *opout) __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_3); + +uint32_t asl_msg_type(asl_msg_t *msg) __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_3); +uint32_t asl_msg_count(asl_msg_t *msg) __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_3); + +char *asl_msg_to_string(asl_msg_t *in, uint32_t *len) __OSX_AVAILABLE_STARTING(__MAC_10_4, __IPHONE_2_0); +char *asl_list_to_string(asl_search_result_t *, uint32_t *) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); +asl_search_result_t *asl_list_from_string(const char *buf) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); + +char *asl_format_message(asl_msg_t *msg, const char *msg_fmt, const char *time_fmt, uint32_t text_encoding, uint32_t *outlen) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); + +asl_string_t *asl_msg_to_string_raw(uint32_t encoding, asl_msg_t *msg, const char *tfmt) __OSX_AVAILABLE_STARTING(__MAC_10_8, __IPHONE_5_1); + +#endif /* __ASL_MSG_H__ */ diff --git a/libsystem_asl.tproj/include/asl_private.h b/libsystem_asl.tproj/include/asl_private.h new file mode 100644 index 0000000..6007ab0 --- /dev/null +++ b/libsystem_asl.tproj/include/asl_private.h @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2007-2011 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#ifndef __ASL_PRIVATE_H__ +#define __ASL_PRIVATE_H__ + +#include +#include +#include +#include +#include +#include "asl_file.h" +#include "asl_msg.h" +#include + +#define ASL_QUERY_OP_NULL 0x00000 + +#define ASLMANAGER_SERVICE_NAME "com.apple.aslmanager" +#define NOTIFY_SYSTEM_MASTER "com.apple.system.syslog.master" +#define NOTIFY_SYSTEM_ASL_FILTER "com.apple.system.syslog.asl_filter" +#define NOTIFY_PREFIX_SYSTEM "com.apple.system.syslog" +#define NOTIFY_PREFIX_USER "user.syslog" +#define NOTIFY_RC "com.apple.asl.remote" + +#define ASL_OPT_IGNORE "ignore" +#define ASL_OPT_STORE "store" + +#define ASL_STORE_LOCATION_FILE 0 +#define ASL_STORE_LOCATION_MEMORY 1 + +#define ASL_OPT_SYSLOG_LEGACY 0x00010000 + +#define ASL_KEY_SENDER_MACH_UUID "Sender_Mach_UUID" + +/* SPI to enable ASL filter tunneling using asl_set_filter() */ +#define ASL_FILTER_MASK_TUNNEL 0x100 + +typedef struct +{ + int fd; + uint32_t encoding; + uint32_t filter; + char *mfmt; + char *tfmt; +} asl_out_file_t; + +typedef struct __aslclient +{ + uint32_t options; + struct sockaddr_un server; + int sock; + pid_t pid; + uid_t uid; + gid_t gid; + char *name; + char *facility; + uint32_t filter; + int notify_token; + int notify_master_token; + uint32_t out_count; + asl_out_file_t *out_list; + asl_file_t *aslfile; + uint64_t aslfileid; + uint32_t reserved1; + void *reserved2; + int32_t refcount; +} asl_client_t; + +__BEGIN_DECLS + +int asl_add_output(aslclient asl, int fd, const char *msg_fmt, const char *time_fmt, uint32_t text_encoding) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); +int asl_remove_output(aslclient asl, int fd) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); +int asl_store_location() __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_3); +int asl_get_filter(aslclient asl, int *local, int *master, int *remote, int *active) __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_3); +char *asl_remote_notify_name() __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_3); +int asl_trigger_aslmanager(void) __OSX_AVAILABLE_STARTING(__MAC_10_9, __IPHONE_7_0); +aslmsg _asl_server_control_query(void) __OSX_AVAILABLE_STARTING(__MAC_10_9, __IPHONE_7_0); + +__END_DECLS + +#endif /* __ASL_PRIVATE_H__ */ diff --git a/libsystem_asl.tproj/include/asl_store.h b/libsystem_asl.tproj/include/asl_store.h new file mode 100644 index 0000000..5905877 --- /dev/null +++ b/libsystem_asl.tproj/include/asl_store.h @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2007-2011 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#ifndef __ASL_STORE_H__ +#define __ASL_STORE_H__ + +#include +#include +#include +#include +#include "asl_file.h" +#include +#include + +#if TARGET_IPHONE_SIMULATOR +extern const char *_path_asl_store(void); +extern const char *_path_asl_archive(void); + +#define PATH_ASL_STORE _path_asl_store() +#define PATH_ASL_ARCHIVE _path_asl_archive() +#else +#define PATH_ASL_STORE "/var/log/asl" +#define PATH_ASL_ARCHIVE "/var/log/asl.archive" +#endif + +#define FILE_ASL_STORE_DATA "StoreData" + +#define FILE_CACHE_SIZE 64 +#define FILE_CACHE_TTL 300 + +typedef struct +{ + time_t ts; + uid_t u; + gid_t g; + time_t bb; + char *path; + asl_file_t *f; +} asl_cached_file_t; + +typedef struct +{ + char *base_dir; + FILE *storedata; + uint64_t next_id; + asl_cached_file_t file_cache[FILE_CACHE_SIZE]; + void *work; + time_t start_today; + time_t start_tomorrow; + time_t last_write; + size_t max_file_size; +} asl_store_t; + +uint32_t asl_store_open_write(const char *basedir, asl_store_t **s) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); +uint32_t asl_store_open_read(const char *basedir, asl_store_t **s) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); +uint32_t asl_store_close(asl_store_t *s) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); +uint32_t asl_store_statistics(asl_store_t *s, aslmsg *msg) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); + +uint32_t asl_store_save(asl_store_t *s, aslmsg msg) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); + +uint32_t asl_store_match(asl_store_t *s, aslresponse query, aslresponse *res, uint64_t *last_id, uint64_t start_id, uint32_t count, int32_t direction) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); +uint32_t asl_store_match_timeout(asl_store_t *s, aslresponse query, aslresponse *res, uint64_t *last_id, uint64_t start_id, uint32_t count, int32_t direction, uint32_t usec) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_3_2); + +uint32_t asl_store_match_start(asl_store_t *s, uint64_t start_id, int32_t direction) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); +uint32_t asl_store_match_next(asl_store_t *s, aslresponse query, aslresponse *res, uint32_t count) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); + +uint32_t asl_store_max_file_size(asl_store_t *s, size_t max) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); +uint32_t asl_store_sweep_file_cache(asl_store_t *s) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_3_2); + +uint32_t asl_store_open_aux(asl_store_t *s, aslmsg msg, int *fd, char **url) __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_3); + +#endif /* __ASL_STORE_H__ */ diff --git a/libsystem_asl.tproj/man/asl.3 b/libsystem_asl.tproj/man/asl.3 new file mode 100644 index 0000000..ef93ed9 --- /dev/null +++ b/libsystem_asl.tproj/man/asl.3 @@ -0,0 +1,1419 @@ +.\" Copyright (c) 2005-2012 Apple Inc. +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 4. Neither the name of Apple Computer nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" +.Dd October 1, 2011 +.Dt asl 3 +.Os "Mac OS X" +.Sh NAME +.Nm asl_add_log_file , +.Nm asl_add_outout_file , +.Nm asl_close , +.Nm asl_close_auxiliary_file , +.Nm asl_create_auxiliary_file , +.Nm asl_free , +.Nm asl_get , +.Nm asl_key , +.Nm asl_log , +.Nm asl_log_auxiliary_location , +.Nm asl_log_descriptor , +.Nm asl_new , +.Nm asl_open , +.Nm asl_open_from_file , +.Nm asl_remove_log_file , +.Nm asl_search , +.Nm asl_send , +.Nm asl_set , +.Nm asl_set_filter , +.Nm asl_set_output_file_filter , +.Nm asl_set_query , +.Nm asl_unset , +.Nm asl_vlog , +.Nm aslresponse_free , +.Nm aslresponse_next +.Nd system log message sending and searching functions +.Sh SYNOPSIS +.Fd #include +.\" +.Ft int +.Fo asl_add_log_file +.Fa "aslclient asl" +.Fa "int descriptor" +.Fc +.Ft int +.Fo asl_add_output_file +.Fa "aslclient asl" +.Fa "int descriptor" +.Fa "const char *msg_fmt" +.Fa "const char *time_fmt" +.Fa "int filter" +.Fa "int text_encoding" +.Fc +.Ft void +.Fo asl_close +.Fa "aslclient asl" +.Fc +.Ft void +.Fo asl_close_auxiliary_file +.Fa "int descriptor" +.Fc +.Ft void +.Fo asl_create_auxiliary_file +.Fa "aslmsg msg" +.Fa "const char *title" +.Fa "const char *uti" +.Fa "int *out_descriptor" +.Fc +.Ft void +.Fo asl_free +.Fa "aslmsg msg" +.Fc +.Ft const char * +.Fo asl_get +.Fa "aslmsg msg" +.Fa "const char *key" +.Fc +.Ft const char * +.Fo asl_key +.Fa "aslmsg msg" +.Fa "uint32_t n" +.Fc +.Ft int +.Fo asl_log +.Fa "aslclient asl" +.Fa "aslmsg msg" +.Fa "int level" +.Fa "const char *format" +.Fa "..." +.Fc +.Ft void +.Fo asl_log_auxiliary_location +.Fa "aslmsg msg" +.Fa "const char *title" +.Fa "const char *uti" +.Fa "const char *url" +.Fc +.Ft int +.Fo asl_log_descriptor +.Fa "aslclient asl" +.Fa "aslmsg msg" +.Fa "int level" +.Fa "int descriptor" +.Fa "uint32_t fd_type" +.Fc +.Ft aslmsg +.Fo asl_new +.Fa "uint32_t type" +.Fc +.Ft aslclient +.Fo asl_open +.Fa "const char *ident" +.Fa "const char *facility" +.Fa "uint32_t opts" +.Fc +.Ft aslclient +.Fo asl_open_from_file +.Fa "int descriptor" +.Fa "const char *ident" +.Fa "const char *facility" +.Fc +.Ft int +.Fo asl_remove_log_file +.Fa "aslclient asl" +.Fa "int descriptor" +.Fc +.Ft aslresponse +.Fo asl_search +.Fa "aslclient asl" +.Fa "aslmsg msg" +.Fc +.Ft int +.Fo asl_send +.Fa "aslclient asl" +.Fa "aslmsg msg" +.Fc +.Ft int +.Fo asl_set +.Fa "aslmsg msg" +.Fa "const char *key" +.Fa "const char *value" +.Fc +.Ft int +.Fo asl_set_filter +.Fa "aslclient asl" +.Fa "int filter" +.Fc +.Ft int +.Fo asl_set_output_file_filter +.Fa "aslclient asl" +.Fa "int descriptor" +.Fa "int filter" +.Fc +.Ft int +.Fo asl_set_query +.Fa "aslmsg msg" +.Fa "const char *key" +.Fa "const char *value" +.Fa "uint32_t op" +.Fc +.Ft int +.Fo asl_unset +.Fa "aslmsg msg" +.Fa "const char *key" +.Fc +.Ft int +.Fo asl_vlog +.Fa "aslclient asl" +.Fa "aslmsg msg" +.Fa "int level" +.Fa "const char *format" +.Fa "va_list ap" +.Fc +.Ft void +.Fo aslresponse_free +.Fa "aslresponse r" +.Fc +.Ft aslmsg +.Fo aslresponse_next +.Fa "aslresponse r" +.Fc +.Sh DESCRIPTION +These routines provide an interface to the Apple System Log facility. +They are intended to be a replacement for the +.Xr syslog 3 +API, which will continue to be supported for backwards compatibility. +The new API allows client applications +to create flexible, structured messages and send them to the +.Nm syslogd +server, where they may undergo additional processing. +Messages received by the server are saved in a data store +(subject to input filtering constraints). +This API permits clients to create queries +and search the message data store for matching messages. +.Pp +An introduction to the concepts underlying this interface follows the interface summary below. +.Ss INTERFACE SUMMARY +.Fo asl_open +.Fa ident +.Fa facility +.Fa opts +.Fc +creates and returns a client handle, or NULL if an error occurs in the library. +Messages sent using this handle will default to having the string +.Ar ident +as the value associated with the ASL_KEY_SENDER key, and the value +.Ar facility +associated with the ASL_KEY_FACILITY key. +Several options are available, as described in the +.Sx CLIENT HANDLES +section. +.Pp +Each client handle holds state information that is used when a message is logged using that handle. +This information includes the +.Ar ident +and +.Ar facility +strings and the options from the +.Ar opts +parameter. +Client handles also contain various filter, file descriptor, and control data. +.Pp +The state information in a client handle is not protected by any locking or thread synchronization mechanism. +It is not safe for two or more threads to use a single client handle simultaneously. +Multi-threaded applications should generally create one client handle for each thread that logs messages. +A client handle may only be safely shared amongst multiple threads if the application uses locks or some +synchronization strategy to ensure single-threaded access. +.Pp +As a special case, the ASL library allows the use of NULL in place of a client handle. +In this case, the library uses an internal structure which contains its own lock. +Multiple threads may safely use NULL, although there may be contention for the lock. +.Pp +Applications that use libdispatch may use NULL in place of a client handle, +although this may cause undesirable synchronization behavior and degraded performance because of lock contention. +A better design is often to use one or more serial dispatch queues specifically for logging. +Each such serial queue should use a separate client handle. +.Pp +.Fo asl_close +.Fa asl +.Fc +closes the client handle +.Ar asl +and releases its associated resources. +.Pp +.Fo asl_add_output_file +.Fa asl +.Fa descriptor +.Fa msg_fmt +.Fa time_fmt +.Fa filter +.Fa text_encoding +.Fc +adds the file descriptor +.Ar descriptor +to the a set of file descriptors associated with the client handle +.Ar asl . +Each log message sent by that client handle is also written to these file descriptors +(depending on the setting of the +.Ar filter +argument). +The message format is specified by the +.Ar msg_fmt +argument. +The format for timestamps is specified by the +.Ar time_fmt +argument, although custom format strings may specify more advanced formats for timestamps. +Details on custom format strings are below. +.Pp +Each output file has an associated +.Ar filter +value. +The filter determines which messages are formatted and written to the file based on the message priority level. +.Pp +Special handling for certain characters is specified by the +.Ar text_encoding +argument. +The supported values and their effect are described below. +.Pp +The +.Ar msg_format +argument is a character string that tells the library how to format each message written to the output file. +There are several pre-defined message formats, described below. +Custom formats are also supported, +giving complete control over which ASL message keys should be written +and the overall format of each output line. +The pre-defined formats are identified by constants in the asl.h header file. +.Pp +.Bl -tag -width "ASL_MSG_FMT_RAW" -compact +.It ASL_MSG_FMT_RAW +The contents of the ASL message dictionaries are formatted as a list, +with each key-value pair formatted as +.Dq [Key Value] . +.Pp +.It ASL_MSG_FMT_STD +Messages are formatted using the standard ASL message format of the form +.Pp +.Dl Time Host Sender[PID] : Message +.Pp +Time formats are described below. +.Pp +.It ASL_MSG_FMT_BSD +The legacy format used for plain-text log files. +Similar to the ASL_MSG_FMT_STD format, but the message priority level is excluded. +.Pp +.It ASL_MSG_FMT_MSG +The output line contains only the value of the Message key in each ASL message dictionary. +.Pp +.It ASL_MSG_FMT_XML +Produces multiple lines of output for each ASL message. +The message is formatted as an XML dictionary: +.Pp +.Dl +.Dl \tASLMessageKey1 +.Dl \tKey 1 Value +.Dl \tASLMessageKey2 +.Dl \tKey 2 Value +.Dl \t\t... +.Dl +.Pp +.El +.Pp +A NULL value for +.Ar msg_fmt +causes the library to use the +.Dq std +format. +.Pp +Custom format strings may contain a mix of characters that are directly copied to the output line +and variables, which are a dollar sign +.Sq $ +followed by specific ASL message dictionary keys, whose values will be interpolated into the output. +For example, the format string: +.Pp +.Dl This message from $Sender PID=$PID at $Time *** $Message +.Pp +would result in lines in the output file like, e.g.: +.Pp +.Dl This message from login PID=982 at Jul 27 08:41:27 *** USER_PROCESS: 330 ttys000 +.Dl This message from Mail PID=987 at Jul 27 08:42:16 *** Using V2 Layout +.Pp +Normally, a space character terminates a variable name. +However, the name may be wrapped in parentheses if a space character is not desired in the output. +For example: +.Pp +.Dl $(Sender)[$(PID)]: $Message +.Pp +A third form for specifying variables may be used for the ASL +.Dq Level +and +.Dq Time +message keys. +Note that a +.Dq Time +specification using one of the forms below will override the +.Ar time_fmt +argument to the function. +.Pp +The following forms are recognized: +.Pp +.Bl -tag -width "$((Time)([+|-]HH[:MM]))" +.It $((Level)(str)) +Formats a Level value as a string, for example +.Dq Error , +.Dq Alert , +.Dq Warning , +and so on. +Note that $(Level) or $Level formats the value as an integer 0 through 7. +.It $((Time)(sec)) +Formats a Time value as the number of seconds since the Epoch. +.It $((Time)(raw)) +Alias for $((Time)(sec)). +.It $((Time)(local)) +Formats a Time value as a string of the form +.Dq "Mmm dd hh:mm:ss" , +where Mmm is the abbreviation for the month, dd is the date (1 - 31) and hh:mm:ss is the time. +The local timezone is used. +.It $((Time)(lcl)) +Alias for $((Time)(local)). +.It $((Time)(utc)) +Formats a Time value as a string of the form +.Dq "yyyy-mm-dd hh:mm:ssZ" , +using Coordinated Universal Time, or the +.Dq Zulu +time zone. +.It $((Time)(zulu)) +Alias for $((Time)(utc)). +.It $((Time)(X)) +Where X may be any letter in the range A - Z or a - z. +Formats the Time using the format +.Dq "yyyy-mm-dd hh:mm:ssX" , +using the specified nautical timezone. +Z is the same as UTC/Zulu time. Timezones A - M (except J) decrease by one hour to the east of the +Zulu time zone. +Timezones N - Y increase by one hour to the west of Z. +M and Y have the same clock time, but differ by one day. +J is used to indicate the local timezone. +When printing using $((Time)(J)), the output format is +.Dq "yyyy-mm-dd hh:mm:ss" , +without a trailing timezone letter. +.It $((Time)(JZ)) +Specifies the local timezone. +The timezone offset from UTC follows the date and time. +The time is formatted as +.Dq "yyyy-mm-dd hh:mm:ss[+|-]HH[:MM]" . +Minutes in the timezone offset are only printed if they are non-zero. +.It $((Time)(ISO8601)) +Specifies the local timezone, formatted as specified by ISO 8601. +The timezone offset from UTC follows the date and time. +The time is formatted as +.Dq "yyyy-mm-ddThh:mm:ss[+|-]HH[:MM]" . +Minutes in the timezone offset are only printed if they are non-zero. +Note that this differs from +.Dq JZ +format only in that a +.Dq T +character separates the date and time. +.It $((Time)([+|-]HH[:MM])) +Specifies an offset (+ or -) of the indicated number of hours (HH) and optionally minutes (MM) to UTC. +The value is formatted as a string of the form +.Dq "yyyy-mm-dd hh:mm:ss[+|-]HH[:MM]" . +Minutes in the timezone offset are only printed if they are non-zero. +.El +.Pp +Unless a custom message format uses one of the specialized forms for +.Dq Time +described above, then any timestamps in an output message will be formatted according the the +.Ar time_fmt +argument. +The known formats are identified by constants in the asl.h header file. +.Pp +.Bl -tag -width "ASL_TIME_FMT_SEC" +.It ASL_TIME_FMT_SEC +Formats timestamps as the number of seconds since the Epoch. +.Pp +.It ASL_TIME_FMT_UTC +Formats a Time value as a string of the form +.Dq "yyyy-mm-dd hh:mm:ssZ" , +using Coordinated Universal Time, or the +.Dq Zulu +time zone. +.It ASL_TIME_FMT_LCL +Formats a Time value as a string of the form +.Dq "Mmm dd hh:mm:ss" , +where Mmm is the abbreviation for the month, dd is the date (1 - 31) and hh:mm:ss is the time. +The local timezone is used. +.El +.Pp +A value of NULL for the +.Ar time_fmt +argument will cause the default format ASL_TIME_FMT_LCL to be used. +.Pp +The +.Ar encoding +parameter specifies how certain characters are to be treated when preparing a message for output. +The known encodings are: +.Bl -tag -width "ASL_ENCODE_NONE" +.It ASL_ENCODE_NONE +No special character encode is done. +.Pp +.It ASL_ENCODE_ASL +Newlines and tabs are also encoded as "\\n" and "\\t" respectively. +In +.Dq ASL_MSG_FMT_RAW +format, space characters embedded in log message keys are encoded as "\\s" +and embedded brackets are escaped to print as "\\[" and "\\]". +.Pp +.It ASL_ENCODE_SAFE +Encodes backspace characters as ^H. +Carriage returns are mapped to newlines. +A tab character is appended after newlines so that message text is indented. +.Pp +.It ASL_ENCODE_XML +This encoding should be used when formatting messages using ASL_MSG_FMT_XML. +XML format output requires that keys are valid UTF8 strings. +Keys which are not valid UTF8 are ignored, and the associated value is not printed. +.Pp +Values that contain legal UTF8 are printed as strings. +Ampersand, less than, greater than, quotation mark, and apostrophe characters are encoded according to XML conventions. +Embedded control characters are encoded as +.Dq &#xNN; +where NN is the character's hexadecimal value. +.Pp +Values that do not contain legal UTF8 are encoded in base-64 and printed as data objects. +.El +.Pp +.Nm asl_add_output_file +Returns 0 on success, non-zero on failure. +.Pp +.Pp +.Fo asl_add_log_file +.Fa asl +.Fa descriptor +.Fc +Is equivalent to +.Pp +.Dl asl_add_output_file(asl, descriptor, ASL_MSG_FMT_STD, ASL_TIME_FMT_LCL, ASL_FILTER_MASK_UPTO(ASL_LEVEL_DEBUG), ASL_ENCODE_SAFE); +.Pp +Returns 0 on success, non-zero on failure. +.Pp +.Fo asl_set_output_file_filter +.Fa asl +.Fa descriptor +.Fa filter +.Fc +replaces the current filter value associated with a file descriptor that has been added to a client handle. +Returns the previous filter value. +.Pp +.Fo asl_remove_log_file +.Fa asl +.Fa descriptor +.Fc +removes a file descriptor from the set of file descriptors associated with a client handle. +Returns 0 on success, non-zero on failure. +.Pp +.Fo asl_new +.Fa type +.Fc +allocates and returns an aslmsg structure, or NULL in the case of a failure in the library. +The +.Ar type +argument must be ASL_TYPE_MSG or ASL_TYPE_QUERY. +.Pp +.Fo asl_free +.Fa msg +.Fc +frees an aslmsg and releases resources associated with the structure. +.Pp +.Fo asl_set +.Fa msg +.Fa key +.Fa value +.Fc +creates a new key and value in an aslmsg structure, or replaces the value of an existing key. +Returns 0 on success, non-zero on failure. +.Pp +.Fo asl_set_query +.Fa msg +.Fa key +.Fa op +.Fa value +.Fc +is used to construct searches. +It is similar to +.Fn asl_set , +except that it takes an additional +.Ar op +(operation) argument. +Creates a new (key, op, value) triple in an aslmsg structure, +or replaces the value and operation for an existing key. +See the +.Sx SEARCHING +section for more information. +Returns 0 on success, non-zero on failure. +.Pp +.Fo asl_unset +.Fa msg +.Fa key +.Fc +removes a key and its associated value from an aslmsg structure. +Returns 0 on success, non-zero on failure. +.Pp +.Fo asl_key +.Fa msg +.Fa n +.Fc +returns the nth key in an aslmsg (beginning at zero), +allowing an application to iterate through the keys. +Returns NULL if +.Ar n +indexes beyond the number of keys in +.Ar msg . +.Pp +.Fo asl_get +.Fa msg +.Fa key +.Fc +returns the value associated with +.Ar key +in the aslmsg +.Ar msg . +Returns NULL if +.Ar msg +does not contain +. Ar key . +.Pp +.Fo asl_set_filter +.Fa asl +.Fa f +.Fc +sets a filter for messages being sent to the server. +The filter is a bitmask representing priority levels. +Only messages having a priority level with a corresponding bit set in the filter mask are sent to the +.Nm syslogd +server. +The filter does not control writes to additional files associated with the client handle using +.Fn asl_add_log_file . +Returns the previous filter value. +.Pp +.Fo asl_log +.Fa asl +.Fa msg +.Fa level +.Fa format +.Fa args... +.Fc +sends a log to the server (subject to filtering, see +.Fn asl_set_filter +above) and to any file descriptors associated with the client handle +.Ar asl . +The +.Ar msg +argument may contain any keys and values, which will be formatted as part of the log message. +The value for ASL_KEY_LEVEL is supplied by the +.Ar level +argument. +The value for ASL_KEY_MESSAGE is computed from +.Ar format +and the associated arguments +.Ar args... . +Normal +.Fn printf +style argument processing is applied to the format and the arguments. +The format may also contain +.Dq %m +which will be substituted with the string value corresponding to the current +.Em errno . +.Pp +The ASL_PREFILTER_LOG(asl, msg, level, format, ...) macro may be used in +place of +.Fn asl_log . +The macro avoids processing the variable argument list in those cases where +the message would be filtered out due to filter settings, would not be +written to a log file associated with the aslclient, or would not be +written to stderr. +The macro may provide a performance benefit for some applications. +Details on filter setting, additional log files, and aslclient options +are described below in this manual. +.Pp +.Fo asl_vlog +.Fa asl +.Fa msg +.Fa level +.Fa format +.Fa ap +.Fc +is similar to +.Fn asl_log +except that it takes a va_list argument. +.Pp +.Fo asl_send +.Fa asl +.Fa msg +.Fc +is similar to +.Fn asl_log , +except the value for ASL_KEY_MESSAGE is taken from +.Ar msg +rather than being constructed using a +.Fn printf +style syntax. +.Pp +.Fo asl_log_descriptor +.Fa asl +.Fa msg +.Fa level +.Fa descriptor +.Fa fd_type +.Fc +provides functionality to use file descriptors to send logging data to ASL. +.Ar asl +is retained by ASL and must still be closed by the caller by calling +.Fn asl_close +if the caller loses reference to it. +.Ar msg +is copied by ASL and similarly must still be freed by the caller by calling +.Fn asl_free +if the caller loses reference to it. Any changes made to it after calling +.Fn asl_log_descriptor() +are not applicable to the message used. +.Ar descriptor is treated differently based on the value of +.Ar fd_type . +.Pp +If +.Ar fd_type +is ASL_LOG_DESCRIPTOR_READ, the descriptor must be open for read access. ASL +uses +.Xr dispatch 2 +to read from the descriptor as data becomes available. These data are line +buffered and passed to +.Fn asl_log . +When EOF is read, ASL will +.Xr close 2 +.Ar descriptor .. +.Pp +If +.Ar fd_type +is ASL_LOG_DESCRIPTOR_WRITE, the descriptor is closed and a new writable +descriptor is created with the same fileno. Any data written to this new +descriptor are line buffered and passed to +.Fn asl_log . +When EOF is sent, no further data are read. The caller is responsible for +closing the new descriptor. One common use for this API is to redirect writes +to stdout or stderr to ASL by passing STDOUT_FILENO or STDERR_FILENO as +.Ar descriptor . +.Pp +.Fo asl_search +.Fa asl +.Fa msg +.Fc +searches for messages that match the keys and values in +.Ar msg , +subject to matching operations associated with those keys and values. +The +.Ar msg +argument should be constructed using +.Fn asl_set_query . +See the +.Sx SEARCHING +section for details on constructing queries. +Returns an aslresponse structure that contains matching log messages. +NULL is returned in case of error or if there are no matching messages in the ASL database. +.Pp +.Fo aslresponse_next +.Fa r +.Fc +iterates over an aslresponse structure returned by +.Fn asl_search . +Each call returns the next aslmsg in the response. +Returns NULL when there are no further messages. +.Pp +.Fo aslresponse_free +.Fa r +.Fc +frees the aslresponse structure +.Ar r +and all of its associated resources. +.Pp +.Fo asl_create_auxiliary_file +.Fa msg +.Fa title +.Fa uti +.Fa out_descriptor +.Fc +Creates an auxiliary file that may be used by the client to save arbitrary data. +When the file is closed using +.Fo asl_close_auxiliary_file +.Fc , +.Nm syslogd +will log the specified +.Fa msg +along with the +.Fa title +and the Uniform Type Identifier provided by +.Fa uti . +If a NULL value is supplied for +.Fa uti +the type +.Dq public.data +will be used. +The +.Nm Console +application will display the message with a link to the file. +.Pp +Auxiliary files are saved in the ASL data store. +They are automatically deleted at the same time that the log message expires. +Messages expire in 7 days by default. +A value set for the ASLExpireTime key will override the default. +Read access for the auxiliary file will be the same as read access for +.Fa msg . +By default, messages (and auxiliary files) are world-readable. +Access may be limited by setting values for the ReadUID and ReadGID keys. +.Pp +.Fo asl_close_auxiliary_file +.Fa descriptor +.Fc +closes the file descriptor +.Ar descriptor +previously returned by a call to +.Fn asl_create_auxiliary_file . +.Pp +.Fo asl_log_auxiliary_location +.Fa msg +.Fa title +.Fa uti +.Fa url +.Fc +will log the specified +.Fa msg +along with the +.Fa title , +the Uniform Type Identifier provided by +.Fa uti , +and the Uniform Resource Locator provided by +.Fa url . +The +.Nm Console +application will display the message with a link to the file. +This allows a client to save data in an auxiliary file, but unlike +.Fo asl_create_auxiliary_file +.Fc , +the life-cycle of this file must be managed by some external system. +The file will not be removed when the corresponding log message expired from the ASL data store. +.Pp +.Fo asl_open_from_file +.Fa descriptor +.Fa facility +.Fa opts +.Fc +creates a client handle for an open file descriptor +.Fa descriptor . +This routine may be used in conjunction with +.Fo asl_create_auxiliary_file +.Fc +or +.Fo asl_log_auxiliary_location +.Fc +to save ASL format log messages in an auxiliary file. +The UTI type +.Dq com.apple.asl-file +should be used for ASL format auxiliary files. +.Pp +Files with this format may be read from the command line using +.Nm syslog Fl f Ar file , +or from the +.Nm Console +utility. +.Pp +The file must be open for read and write access. +The file will be truncated and its existing contents will be lost. +.Fo asl_close +.Fc +must be called to close the client handle when logging to this file is complete. +The file should be closed using +.Fo asl_close_auxiliary_file +.Fc +if it was returned by +.Fo asl_create_auxiliary_file +.Fc , +or +.Fo close +.Fc +otherwise. +.Pp +The client handle returned by +.Fn asl_open_from_file +contains an internal lock, and may be used safely by multiple threads or from independent dispatch queues. +Note that callers will contend for the internal lock when saving log messages to a file. +.Pp +Note that messages with ReadUID or ReadGID values will simply be saved to the file, +and will not effect read access to either the message or the file itself. +Similarly, messages with ASLExpireTime values will be saved, but will not effect the +life-cycle of either the individual messages or the file. +.Ss MESSAGES +At the core of this API is the aslmsg structure. +Although the structure is opaque and may not be directly manipulated, +it contains a list of key/value pairs. +All keys and values are NUL-character terminated C language strings. +UTF-8 encoding may be used for non-ASCII characters. +.Pp +Message structures are generally used to send log messages, +and are created thusly: +.Pp + aslmsg m = asl_new(ASL_TYPE_MSG); +.Pp +Another message type, ASL_TYPE_QUERY, +is used to create queries when searching the data store. +Query type messages and searching are described in detail in the +.Sx SEARCHING +section. +For the remainder of this section, +the messages described will be of the ASL_TYPE_MSG variety. +.Pp +Each aslmsg contains a default set of keys +and values that are associated with them. +These keys are listed in the asl.h header file. +They are: +.Pp + #define ASL_KEY_TIME "Time" + #define ASL_KEY_HOST "Host" + #define ASL_KEY_SENDER "Sender" + #define ASL_KEY_FACILITY "Facility" + #define ASL_KEY_PID "PID" + #define ASL_KEY_UID "UID" + #define ASL_KEY_GID "GID" + #define ASL_KEY_LEVEL "Level" + #define ASL_KEY_MSG "Message" +.Pp +Many of these correspond to equivalent parts of messages described in the +.Xr syslog 3 +API. +Values associated with these message keys are assigned appropriate defaults. +The value for ASL_KEY_HOST is the local host name, +the value associated with ASL_KEY_SENDER is the process name, +the ASL_KEY_PID is the client's process ID number, and so on. +.Pp +Note the addition of the UID and GID keys. +The values for UID and GID are set in library code by the message sender. +The server will attempt to confirm the values, +but no claim is made that these values cannot be maliciously overridden +in an attempt to deceive a log message reader +as to the identity of the sender of a message. +The contents of log messages must be regarded as insecure. +.Pp +The +.Xr asl 3 +API does not require a process to choose a facility name. +The +.Nm syslogd +server will use a default value of +.Dq user +if a facility is not set. +However, a client may set a facility name as an argument in the +.Nm asl_open +call, or by setting a specific value for the ASL_KEY_FACILITY in a message: +.Pp + asl_set(m, ASL_KEY_FACILITY, "com.somename.greatservice"); +.Pp +An application may choose any facility name at will. +Different facility names may be attached to different messages, perhaps to distinguish different subsystems in log messages. +Developers are encouraged to adopt a +.Dq Reverse ICANN +naming convention to avoid conflicting facility names. +.Pp +Default values are set in the message for each of the keys listed above, +except for ASL_KEY_MSG, +which may be explicitly set at any time using the +.Nm asl_set +routine, or implicitly set at the time the message is sent using the +.Nm asl_log +or +.Nm asl_vlog +routines. +These two routines also have an integer-level parameter +for specifying the log priority. +The ASL_KEY_LEVEL value is set accordingly. +Finally, the value associated with ASL_KEY_TIME +is set in the sending routine. +.Pp +Although it may appear that there is significant overhead required +to send a log message using this API, +the opposite is actually true. +A simple +.Dq Hello World +program requires only: +.Pp + #include + ... + asl_log(NULL, NULL, ASL_LEVEL_INFO, "Hello World!"); +.Pp +Both +.Nm asl_log +and +.Nm asl_vlog +will provide the appropriate default values +when passed a NULL aslmsg argument. +.Pp +.Pp +In this example, the aslclient argument is NULL. +This is sufficient for a single-threaded application, +or for an application which only sends log messages from a single thread. +When logging from multiple threads, +each thread +.Em should +open a separate client handle using +.Nm asl_open . +The client handle may then be closed when it is no longer required using +.Nm asl_close . +Multiple threads may log messages safely using a NULL aslclient argument, +but the library will use an internal lock, so that in fact only one thread +will log at a time. +.Pp +When an application requires additional keys and values +to be associated with each log message, +a single message structure may be allocated and set up as +.Dq template +message of sorts: +.Pp + aslmsg m = asl_new(ASL_TYPE_MSG); + asl_set(m, ASL_KEY_FACILITY, "com.secrets.r.us"); + asl_set(m, "Clearance", "Top Secret"); + ... + asl_log(NULL, m, ASL_LEVEL_NOTICE, "Message One"); + ... + asl_log(NULL, m, ASL_LEVEL_ERR, "Message Two"); +.Pp +The message structure will carry the values set for the +.Dq Facility +and +.Dq Clearance +keys so that they are used in each call to +.Nm asl_log , +while the log level and the message text +are taken from the calling parameters. +.Pp +The +.Ar format +argument to +.Nm asl_log +and +.Nm asl_vlog +is identical to +.Xr printf 3 , +and may include +.Ql %m , +which is replaced by the current error message +(as denoted by the global variable +.Va errno ; +see +.Xr strerror 3 . ) +.Pp +Key/value pairs may be removed from a message structure with +.Nm asl_unset . +A message may be freed using +.Nm asl_free . +.Pp +The +.Nm asl_send +routine is used by +.Nm asl_log +and +.Nm asl_vlog +to transmit a message to the server. +This routine sets the value associated with ASL_KEY_TIME +and sends the message. +It may be called directly if all of a message's key/value pairs +have been created using +.Nm asl_set . +.Ss SECURITY +Messages that are sent to the +.Nm syslogd +server may be saved in a message store. +The store may be searched using +.Nm asl_search , +as described below. +By default, all messages are readable by any user. +However, some applications may wish to restrict read access +for some messages. +To accommodate this, +a client may set a value for the "ReadUID" and "ReadGID" keys. +These keys may be associated with a value +containing an ASCII representation of a numeric UID or GID. +Only the root user (UID 0), +the user with the given UID, +or a member of the group with the given GID +may fetch access-controlled messages from the database. +.Pp +Although the ASL system does not require a "Facility" key in a message, +many processes specify a "Facility" value similar +to the common usage of the BSD +.Nm syslog +API, although developers are encouraged to adopt facility names that make sense for their application. +A +.Dq Reverse ICANN +naming convention (e.g. "com.apple.system.syslog") should be adopted to avoid conflicting names. +The ASL system generally allows any string to be used as a facility value, +with one exception. +The value "com.apple.system", +or any string that has "com.apple.system" as a prefix, +may only be used by processes running with the UID 0. +This allows system processes to log messages that can not be "spoofed" by user processes. +Non-UID 0 client processes that specify "com.apple.system" as a facility, will be assigned the value "user" +by the +.Nm syslogd +server. +.Ss CLIENT HANDLES +A client handle contains various parameters and control settings that are used when a message is logged. +This includes an identification string, a facility name, filtering controls, additional file descriptors, and other data. +Client handles are not thread-safe. +Applications that log from multiple threads should create a client handle for each thread. +.Pp +Applications that use libdispatch must also avoid using a single client handle from multiple dispatch queues if those queues may run concurrently. +A good approach is to create one or more serial dispatch queues specifically for logging. +Each such queue should use its own ASL client handle. +.Pp +If a single handle must be accessed by multiple dispatch queues, +then the application must use locks, semaphores, or some other mechanism to prevent concurrent access to a client handle. +.Pp +A NULL value may be used in any of the routines +that require an aslclient argument. +In this case, the library will use an internal client handle. +This internal handle contains its own lock, allowing multiple threads to safely use the NULL client handle. +Note, however, that contention for the lock may cause undesirable synchronization behavior or reduced performance. +.Pp +The +.Nm asl_open +routine may be given an ident argument, +which becomes the default value for the ASL_KEY_SENDER key, +and a facility argument, +which becomes the value associated with the ASL_KEY_FACILITY key. +If NULL is passed as the value for +.Ar ident , +the name of the currently running program will be used. +If NULL is passed as the value for +.Ar facility , +the value +.Dq user +will be used for non UID 0 processes, and +.Dq +daemon +will be used for UID 0 processes. +.Pp +Several options are available when creating a client handle. +They are: +.Pp +.Bl -tag -width "ASL_OPT_NO_REMOTE" -compact +.It ASL_OPT_STDERR +adds stderr as an output file descriptor +.It ASL_OPT_NO_DELAY +connects to the server immediately +.It ASL_OPT_NO_REMOTE +disables remote-control filter adjustment +.El +.Pp +ASL_OPT_NO_DELAY makes the client library connect to the +.Nm syslogd +server at the time that +.Nm asl_open +is called, rather than waiting for the first message to be sent. +Opening the connection is quite fast, but some applications may want to avoid any unnecessary delays when calling +.Nm asl_log , +.Nm asl_vlog , +or +.Nm asl_send . +.Pp +See the FILTERING section below, and the +.Xr syslog 1 +for additional details on filter controls. +.Pp +A client handle is closed and its resources released using +.Nm asl_close . +Note that if additional file descriptors were added to the handle, +either using the ASL_OPT_STDERR option +or afterwards with the +.Nm asl_add_log_file +routine, those file descriptors are not closed by +.Nm asl_close . +.Ss LOGGING TO ADDITIONAL FILES +If a client handle is opened with the ASL_OPT_STDERR option to +.Nm asl_open , +a copy of each log message will be sent to stderr. +Additional output streams may be include using +.Nm asl_add_log_file . +.Pp +Messages sent to stderr or other files are printed in the "standard" message format +also used as a default format by the +.Xr syslog 1 +command line utility. +Non-ASCII characters in a message are encoded using the +.Dq safe +encoding style used by +.Xr syslog 1 +with the +.Fl E Ar safe +option. +Backspace characters are printed as ^H. +Carriage returns are mapped to newlines. +A tab character is appended after newlines so that message text is indented. +.Pp +File descriptors may be removed from the list of outputs associated +with a client handle with +.Nm asl_remove_log_file . +This routine simply removes the file descriptor from the output list. +The file is not closed as a result. +.Pp +The ASL_OPT_STDERR option may not be unset +after a client handle has been opened. +.Ss SEARCHING +The +.Nm syslogd +server archives received messages in a data store +that may be searched using the +.Nm asl_search , +.Nm aslresponse_next , +and +.Nm aslresponse_free +routines. +A query message is created using: +.Pp + aslmsg q = asl_new(ASL_TYPE_QUERY); +.Pp +Search settings are made in the query using +.Nm asl_set_query . +A search is performed on the data store with +.Nm asl_search . +It returns an +.Ft aslresponse +structure. +The caller may then call +.Nm aslresponse_next +to iterate through matching messages. +The +.Ft aslresponse +structure may be freed with +.Nm aslresponse_free . +.Pp +Like other messages, ASL_TYPE_QUERY messages contain keys and values. +They also associate an operation with each key and value. +The operation is used to decide if a message matches the query. +The simplest operation is ASL_QUERY_OP_EQUAL, which tests for equality. +For example, the following code snippet searches for messages +with a Sender value equal to +.Dq MyApp . +.Pp + aslmsg m; + aslresponse r; + q = asl_new(ASL_TYPE_QUERY); + asl_set_query(q, ASL_KEY_SENDER, "MyApp", ASL_QUERY_OP_EQUAL); + r = asl_search(NULL, q); +.Pp +More complex searches may be performed using other query operations. +.Pp +.Bl -tag -width "ASL_QUERY_OP_GREATER_EQUAL" -compact +.It ASL_QUERY_OP_EQUAL +value equality +.It ASL_QUERY_OP_GREATER +value greater than +.It ASL_QUERY_OP_GREATER_EQUAL +value greater than or equal to +.It ASL_QUERY_OP_LESS +value less than +.It ASL_QUERY_OP_LESS_EQUAL +value less than or equal to +.It ASL_QUERY_OP_NOT_EQUAL +value not equal +.It ASL_QUERY_OP_REGEX +regular expression search +.It ASL_QUERY_OP_TRUE +always true - use to test for the existence of a key +.El +.Pp +Regular expression search uses +.Xr regex 3 +library. +Patterns are compiled using the REG_EXTENDED and REG_NOSUB options. +.Pp +Modifiers that change the behavior of these operations +may also be specified by ORing the modifier value with the operation. +The modifiers are: +.Pp +.Bl -tag -width "ASL_QUERY_OP_SUBSTRING" -compact +.It ASL_QUERY_OP_CASEFOLD +string comparisons are case-folded +.It ASL_QUERY_OP_PREFIX +match a leading substring +.It ASL_QUERY_OP_SUFFIX +match a trailing substring +.It ASL_QUERY_OP_SUBSTRING +match any substring +.It ASL_QUERY_OP_NUMERIC +values are converted to integer using +.Nm atoi +.El +.Pp +The only modifier that is checked +for ASL_QUERY_OP_REGEX search is ASL_QUERY_OP_CASEFOLD. +This causes the regular expression to be compiled +with the REG_ICASE option. +.Pp +If a query message contains more than one set of key/value/operation triples, +the result will be a logical AND. For example, to find messages from +.Dq MyApp +with a priority level less than or equal to +.Dq 3 : +.Pp + aslmsg q; + aslresponse r; + q = asl_new(ASL_TYPE_QUERY); + asl_set_query(q, ASL_KEY_SENDER, "MyApp", ASL_QUERY_OP_EQUAL); + asl_set_query(q, ASL_KEY_LEVEL, "3", + ASL_QUERY_OP_LESS_EQUAL | ASL_QUERY_OP_NUMERIC); + r = asl_search(NULL, q); +.Pp +After calling +.Nm asl_search +to get an +.Ft aslresponse +structure, use +.Nm aslresponse_next +to iterate through all matching messages. +To iterate through the keys and values in a message, use +.Nm asl_key +to iterate through the keys, then call +.Nm asl_get +to get the value associated with each key. +.Pp + aslmsg q, m; + int i; + const char *key, *val; +.Pp + ... + r = asl_search(NULL, q); + while (NULL != (m = aslresponse_next(r))) + { + for (i = 0; (NULL != (key = asl_key(m, i))); i++) + { + val = asl_get(m, key); + ... + } + } + aslresponse_free(r); +.Pp +.Ss FILTERING AND REMOTE CONTROL +Clients may set a filter mask value with +.Nm asl_set_filter . +The mask specifies which messages should be sent to the +.Nm syslogd +daemon by specifying a yes/no setting for each priority level. +Clients typically set a filter mask +to avoid sending relatively unimportant messages. +For example, Debug or Info priority level messages +are generally only useful for debugging operations. +By setting a filter mask, a process can improve performance +by avoiding sending messages that are in most cases unnecessary. +.Pp +.Nm asl_set_filter returns the previous value of the filter, i.e. the value of the filter before the routine was called. +.Pp +As a convenience, the macros ASL_FILTER_MASK(level) and ASL_FILTER_MASK_UPTO(level) +may be used to construct a bit mask corresponding to a given priority level, +or corresponding to a bit mask for all priority levels +from ASL_LEVEL_EMERG to a given input level. +.Pp +The default filter mask is ASL_FILTER_MASK_UPTO(ASL_LEVEL_NOTICE). +This means that by default, +and in the absence of remote-control changes (described below), +ASL_LEVEL_DEBUG and ASL_LEVEL_INFO priority level messages +are not sent to the +.Mn syslogd +server. +.Pp +Three different filters exist for each application. +The first is the filter mask set using +.Nm asl_set_filter +as described above. +The Apple System Log facility also manages a +.Dq master +filter mask. +The master filter mask usually has a value +that indicates to the library that it is +.Dq off , +and thus it has no effect. +However, the mask filter mask may be enabled +by giving it a value using the +.Nm syslog +command, using the +.Fl c +0 option. +When the master filter mask has been set, +it takes precedence over the client's filter mask. +The client's mask is unmodified, +and will become active again if remote-control filtering is disabled. +.Pp +In addition to the master filter mask, +The Apple System Log facility +also manages a per-client remote-control filter mask. +Like the master filter mask, the per-client mask is usually +.Dq off , +having no effect on a client. +If a per-client filter mask is set using the +.Nm syslog +command, using the +.Fl c Ar process +option, then it takes precedence +over both the client's filter mask and the master filter mask. +As is the case with the master filter mask, +a per-client mask ceases having any effect when if is disabled. +.Pp +The ASL_OPT_NO_REMOTE option to +.Nm asl_open +causes both the master and per-client remote-control masks +to be ignored in the library. +In that case, only the client's own filter mask +is used to determine which messages are sent to the server. +This may be useful for Applications that produce log messages +that should never be filtered, due to security considerations. +Note that root (administrator) access is required +to set or change the master filter mask, +and that only root may change a per-client remote-control filter mask +for a root (UID 0) process. +.Pp +The per-process remote control filter value is kept as a state value +associated with a key managed by +.Nm notifyd . +The key is protected by an access control mechanism that only permits the +filter value to be accessed and modified by the same effective UID as the +ASL client at the time that the first ASL connection was created. +Remote filter control using +.Nm syslog Fl c +will fail for processes that change effective UID after starting an ASL connection. +Those processes should close all ASL client handles and then re-open ASL connections +if remote filter control support is desired. +.Sh HISTORY +These functions first appeared in +Mac OS X 10.4. +.Sh SEE ALSO +.Xr syslog 1 , +.Xr strvis 3 , +.Xr syslogd 8 diff --git a/libsystem_asl.tproj/man/syslog.3 b/libsystem_asl.tproj/man/syslog.3 new file mode 100644 index 0000000..bdce947 --- /dev/null +++ b/libsystem_asl.tproj/man/syslog.3 @@ -0,0 +1,323 @@ +.\" Copyright (c) 1985, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)syslog.3 8.1 (Berkeley) 6/4/93 +.\" $FreeBSD: src/lib/libc/gen/syslog.3,v 1.22 2001/10/01 16:08:51 ru Exp $ +.\" +.Dd June 4, 1993 +.Dt SYSLOG 3 +.Os +.Sh NAME +.Nm closelog , +.Nm openlog , +.Nm setlogmask , +.Nm syslog , +.Nm vsyslog +.Nd control system log +.Sh LIBRARY +.Lb libc +.Sh SYNOPSIS +.In syslog.h +.Ft void +.Fo closelog +.Fa void +.Fc +.Ft void +.Fo openlog +.Fa "const char *ident" +.Fa "int logopt" +.Fa "int facility" +.Fc +.Ft int +.Fo setlogmask +.Fa "int maskpri" +.Fc +.Ft void +.Fo syslog +.Fa "int priority" +.Fa "const char *message" +.Fa "..." +.Fc +.In syslog.h +.In stdarg.h +.Ft void +.Fo vsyslog +.Fa "int priority" +.Fa "const char *message" +.Fa "va_list args" +.Fc +.Sh DESCRIPTION +The +.Fn syslog +function +writes +.Fa message +to the system message logger. +The message is then written to the system console, log files, +logged-in users, or forwarded to other machines as appropriate. +(See +.Xr syslogd 8 . ) +.Pp +The message is identical to a +.Xr printf 3 +format string, except that +.Ql %m +is replaced by the current error +message. +(As denoted by the global variable +.Va errno ; +see +.Xr strerror 3 . ) +A trailing newline is added if none is present. +.Pp +Newlines and other non-printable characters embedded in the message string are printed in an alternate format. +This prevents someone from using non-printable characters to construct misleading log messages in an output file. +Newlines are printed as "\\n", +tabs are printed as "\\t". +Other control characters are printed using a caret ("^") representation, for example "^M" for carriage return. +.Pp +The +.Fn vsyslog +function +is an alternate form in which the arguments have already been captured +using the variable-length argument facilities of +.Xr stdarg 3 . +.Pp +The message is tagged with +.Fa priority . +Priorities are encoded as a +.Fa facility +and a +.Em level . +The facility describes the part of the system +generating the message. +The level is selected from the following +.Em ordered +(high to low) list: +.Bl -tag -width LOG_AUTHPRIV +.It Dv LOG_EMERG +A panic condition. +This is normally broadcast to all users. +.It Dv LOG_ALERT +A condition that should be corrected immediately, such as a corrupted +system database. +.It Dv LOG_CRIT +Critical conditions, e.g., hard device errors. +.It Dv LOG_ERR +Errors. +.It Dv LOG_WARNING +Warning messages. +.It Dv LOG_NOTICE +Conditions that are not error conditions, +but should possibly be handled specially. +.It Dv LOG_INFO +Informational messages. +.It Dv LOG_DEBUG +Messages that contain information +normally of use only when debugging a program. +.El +.Pp +The +.Fn openlog +function +provides for more specialized processing of the messages sent +by +.Fn syslog +and +.Fn vsyslog . +The parameter +.Fa ident +is a string that will be prepended to every message. +The +.Fa logopt +argument +is a bit field specifying logging options, which is formed by +.Tn OR Ns 'ing +one or more of the following values: +.Bl -tag -width LOG_AUTHPRIV +.It Dv LOG_CONS +If +.Fn syslog +cannot pass the message to +.Xr syslogd 8 +it will attempt to write the message to the console +.Pq Dq Pa /dev/console . +.It Dv LOG_NDELAY +Open the connection to +.Xr syslogd 8 +immediately. +Normally the open is delayed until the first message is logged. +Useful for programs that need to manage the order in which file +descriptors are allocated. +.It Dv LOG_PERROR +Write the message to standard error output as well to the system log. +.It Dv LOG_PID +Log the process id with each message: useful for identifying +instantiations of daemons. +.El +.Pp +The +.Fa facility +parameter encodes a default facility to be assigned to all messages +that do not have an explicit facility encoded: +.Bl -tag -width LOG_AUTHPRIV +.It Dv LOG_AUTH +The authorization system: +.Xr login 1 , +.Xr su 1 , +.Xr getty 8 , +etc. +.It Dv LOG_AUTHPRIV +The same as +.Dv LOG_AUTH , +but logged to a file readable only by +selected individuals. +.It Dv LOG_CRON +The cron daemon: +.Xr cron 8 . +.It Dv LOG_DAEMON +System daemons, such as +.Xr routed 8 , +that are not provided for explicitly by other facilities. +.It Dv LOG_FTP +The file transfer protocol daemons: +.Xr ftpd 8 , +.Xr tftpd 8 . +.It Dv LOG_KERN +Messages generated by the kernel. +These cannot be generated by any user processes. +.It Dv LOG_LPR +The line printer spooling system: +.Xr cups-lpd 8 , +.Xr cupsd 8 , +etc. +.It Dv LOG_MAIL +The mail system. +.It Dv LOG_NEWS +The network news system. +.It Dv LOG_SECURITY +Security subsystems, such as +.Xr ipfw 4 . +.It Dv LOG_SYSLOG +Messages generated internally by +.Xr syslogd 8 . +.It Dv LOG_USER +Messages generated by random user processes. +This is the default facility identifier if none is specified. +.It Dv LOG_UUCP +The uucp system. +.It Dv LOG_LOCAL0 +Reserved for local use. +Similarly for +.Dv LOG_LOCAL1 +through +.Dv LOG_LOCAL7 . +.El +.Pp +The +.Fn closelog +function +can be used to close the log file. +.Pp +The +.Fn setlogmask +function +sets the log priority mask to +.Fa maskpri +and returns the previous mask. +Calls to +.Fn syslog +with a priority not set in +.Fa maskpri +are rejected. +The mask for an individual priority +.Fa pri +is calculated by the macro +.Fn LOG_MASK pri ; +the mask for all priorities up to and including +.Fa toppri +is given by the macro +.Fn LOG_UPTO toppri ; . +The default allows all priorities to be logged. +.Sh RETURN VALUES +The routines +.Fn closelog , +.Fn openlog , +.Fn syslog , +and +.Fn vsyslog +return no value. +.Pp +The routine +.Fn setlogmask +always returns the previous log mask level. +.Sh EXAMPLES +.Bd -literal -offset indent -compact +syslog(LOG_ALERT, "who: internal error 23"); + +openlog("ftpd", LOG_PID | LOG_NDELAY, LOG_FTP); + +setlogmask(LOG_UPTO(LOG_ERR)); + +syslog(LOG_INFO, "Connection from host %d", CallingHost); + +syslog(LOG_INFO|LOG_LOCAL2, "foobar error: %m"); +.Ed +.Sh LEGACY SYNOPSIS +.Fd #include +.Fd #include +.Pp +These include files are necessary for all functions. +.Sh SEE ALSO +.Xr asl 3 , +.Xr logger 1 , +.Xr compat 5 , +.Xr syslogd 8 +.Sh HISTORY +These +functions appeared in +.Bx 4.2 . +.Sh BUGS +Never pass a string with user-supplied data as a format without using +.Ql %s . +An attacker can put format specifiers in the string to mangle your stack, +leading to a possible security hole. +This holds true even if the string was built using a function like +.Fn snprintf , +as the resulting string may still contain user-supplied conversion specifiers +for later interpolation by +.Fn syslog . +.Pp +Always use the proper secure idiom: +.Pp +.Bd -literal -offset indent -compact +syslog(LOG_ERR, "%s", string); +.Ed diff --git a/libsystem_asl.tproj/src/asl.c b/libsystem_asl.tproj/src/asl.c new file mode 100644 index 0000000..29640c1 --- /dev/null +++ b/libsystem_asl.tproj/src/asl.c @@ -0,0 +1,2288 @@ +/* + * Copyright (c) 2004-2012 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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, 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "asl_core.h" +#include "asl_msg.h" +#include "asl_store.h" +#include "asl_private.h" + +#define streq(A, B) (strcmp(A, B) == 0) +#define strcaseeq(A, B) (strcasecmp(A, B) == 0) + +#define forever for(;;) + +#define FETCH_BATCH 256 + +#define LEVEL_MASK 0x0000000f +#define EVAL_MASK 0x000000f0 +#define EVAL_IGNORE 0x00000000 +#define EVAL_ASLFILE 0x00000010 +#define EVAL_SEND 0x00000020 +#define EVAL_TUNNEL 0x00000040 +#define EVAL_FILE 0x00000080 +#define EVAL_QUOTA 0x00000100 + +/* + * Clients get a max of 36000 messages per hour. + * Their quota gets refilled at a rate of 10 messages per second. + */ +#define QUOTA_MPH 36000 +#define QUOTA_MPS 10 +#define QUOTA_MSG_INTERVAL 60 + +#define QUOTA_MSG "*** LOG MESSAGE QUOTA EXCEEDED - SOME MESSAGES FROM THIS PROCESS HAVE BEEN DISCARDED ***" +#define QUOTA_LEVEL "2" + +/* forward */ +time_t asl_parse_time(const char *); +const char *asl_syslog_faciliy_num_to_name(int n); +static int _asl_send_message(aslclient ac, uint32_t eval, asl_msg_t *msg, const char *mstring); +__private_extern__ asl_client_t *_asl_open_default(); + +/* private asl_file SPI */ +__private_extern__ uint32_t asl_file_open_write_fd(int fd, asl_file_t **s); + +/* notify SPI */ +uint32_t notify_register_plain(const char *name, int *out_token); + +/* fork handling in syslog.c */ +extern void _syslog_fork_child(); + +typedef struct +{ + int fd; + asl_msg_t *msg; + dispatch_semaphore_t sem; +} asl_aux_context_t; + +typedef struct +{ + int notify_count; + int rc_change_token; + int notify_token; + int master_token; + uint64_t proc_filter; + uint64_t master_filter; + time_t last_send; + time_t last_oq_msg; + uint32_t quota; + dispatch_once_t port_lookup_once; + mach_port_t server_port; + char *sender; + pthread_mutex_t lock; + int aux_count; + asl_aux_context_t **aux_ctx; + asl_client_t *asl; +} _asl_global_t; + + +#ifndef BUILDING_VARIANT +__private_extern__ _asl_global_t _asl_global = {0, -1, -1, -1, 0LL, 0LL, 0LL, 0LL, 0, 0, MACH_PORT_NULL, NULL, PTHREAD_MUTEX_INITIALIZER, 0, NULL, NULL}; + +#define ASL_SERVICE_NAME "com.apple.system.logger" + +/* + * Called from the child process inside fork() to clean up + * inherited state from the parent process. + * + * NB. A lock isn't required, since we're single threaded in this call. + */ +void +_asl_fork_child() +{ + _asl_global.notify_count = 0; + _asl_global.rc_change_token = -1; + _asl_global.master_token = -1; + _asl_global.notify_token = -1; + _asl_global.quota = 0; + _asl_global.last_send = 0; + _asl_global.last_oq_msg = 0; + + _asl_global.port_lookup_once = 0; + _asl_global.server_port = MACH_PORT_NULL; + + pthread_mutex_init(&(_asl_global.lock), NULL); +} + +/* + * asl_remote_notify_name: returns the notification key for remote-control filter + * changes for this process. + */ +char * +asl_remote_notify_name() +{ + pid_t pid = getpid(); + uid_t euid = geteuid(); + char *str = NULL; + + if (euid == 0) asprintf(&str, "%s.%d", NOTIFY_PREFIX_SYSTEM, pid); + else asprintf(&str, "user.uid.%d.syslog.%d", euid, pid); + + return str; +} + +static int +_asl_notify_open(int do_lock) +{ + char *notify_name; + uint32_t status; + + if (do_lock != 0) pthread_mutex_lock(&_asl_global.lock); + + _asl_global.notify_count++; + + if (_asl_global.notify_token != -1) + { + if (do_lock != 0) pthread_mutex_unlock(&_asl_global.lock); + return 0; + } + + if (_asl_global.rc_change_token == -1) + { + status = notify_register_check(NOTIFY_RC, &_asl_global.rc_change_token); + if (status != NOTIFY_STATUS_OK) _asl_global.rc_change_token = -1; + } + + if (_asl_global.master_token == -1) + { + status = notify_register_plain(NOTIFY_SYSTEM_MASTER, &_asl_global.master_token); + if (status != NOTIFY_STATUS_OK) _asl_global.master_token = -1; + } + + notify_name = asl_remote_notify_name(); + if (notify_name != NULL) + { + status = notify_register_plain(notify_name, &_asl_global.notify_token); + free(notify_name); + if (status != NOTIFY_STATUS_OK) _asl_global.notify_token = -1; + } + + if (do_lock != 0) pthread_mutex_unlock(&_asl_global.lock); + + if (_asl_global.notify_token == -1) return -1; + return 0; +} + +static void +_asl_notify_close() +{ + pthread_mutex_lock(&_asl_global.lock); + + if (_asl_global.notify_count > 0) _asl_global.notify_count--; + + if (_asl_global.notify_count > 0) + { + pthread_mutex_unlock(&_asl_global.lock); + return; + } + + if (_asl_global.rc_change_token >= 0) notify_cancel(_asl_global.rc_change_token); + _asl_global.rc_change_token = -1; + + if (_asl_global.master_token >= 0) notify_cancel(_asl_global.master_token); + _asl_global.master_token = -1; + + if (_asl_global.notify_token >= 0) notify_cancel(_asl_global.notify_token); + _asl_global.notify_token = -1; + + pthread_mutex_unlock(&_asl_global.lock); +} + +static void +_asl_global_init() +{ + if (_asl_global.server_port == MACH_PORT_NULL) + { + mach_port_t newport = MACH_PORT_NULL; + char *str = getenv("ASL_DISABLE"); + if ((str == NULL) || strcmp(str, "1")) + { + bootstrap_look_up2(bootstrap_port, ASL_SERVICE_NAME, &newport, 0, BOOTSTRAP_PRIVILEGED_SERVER); + if (newport != MACH_PORT_NULL) + { + if (!OSAtomicCompareAndSwap32Barrier(MACH_PORT_NULL, newport, (int32_t *)&_asl_global.server_port)) + { + mach_port_deallocate(mach_task_self(), newport); + } + } + } + } +} + +static void +_asl_global_reset() +{ + mach_port_t tmp = _asl_global.server_port; + _asl_global.server_port = MACH_PORT_NULL; + mach_port_deallocate(mach_task_self(), tmp); +} + +aslclient +asl_open(const char *ident, const char *facility, uint32_t opts) +{ + char *name, *x; + asl_client_t *asl; + + asl = (asl_client_t *)calloc(1, sizeof(asl_client_t)); + if (asl == NULL) + { + errno = ENOMEM; + return NULL; + } + + asl->options = opts; + + asl->sock = -1; + + _asl_global_init(); + + asl->pid = getpid(); + asl->uid = getuid(); + asl->gid = getgid(); + + asl->filter = ASL_FILTER_MASK_UPTO(ASL_LEVEL_NOTICE); + + if (ident != NULL) + { + asl->name = strdup(ident); + if (asl->name == NULL) + { + if (asl->sock >= 0) close(asl->sock); + free(asl); + return NULL; + } + } + else + { + name = *(*_NSGetArgv()); + if (name != NULL) + { + x = strrchr(name, '/'); + if (x != NULL) x++; + else x = name; + asl->name = strdup(x); + if (asl->name == NULL) + { + if (asl->sock >= 0) close(asl->sock); + free(asl); + return NULL; + } + } + } + + asl->facility = NULL; + if (facility != NULL) asl->facility = strdup(facility); + else asl->facility = strdup(asl_syslog_faciliy_num_to_name(LOG_USER)); + if (asl->facility == NULL) + { + if (asl->sock >= 0) close(asl->sock); + if (asl->name != NULL) free(asl->name); + free(asl); + return NULL; + } + + if (!(asl->options & ASL_OPT_NO_REMOTE)) _asl_notify_open(1); + + if (asl->options & ASL_OPT_STDERR) + { + /* only add stderr if it is valid */ + if (fcntl(STDERR_FILENO, F_GETFD) >= 0) + { + asl_add_output_file((aslclient)asl, STDERR_FILENO, ASL_MSG_FMT_STD, ASL_TIME_FMT_LCL, ASL_FILTER_MASK_UPTO(ASL_LEVEL_DEBUG), ASL_ENCODE_SAFE); + } + else + { + /* stderr has been closed, ignore ASL_OPT_STDERR flag */ + asl->options &= ~ASL_OPT_STDERR; + } + } + + asl->refcount = 1; + + return (aslclient)asl; +} + +aslclient +asl_open_from_file(int fd, const char *ident, const char *facility) +{ + char *name, *x; + asl_client_t *asl; + uint32_t status; + + asl = (asl_client_t *)calloc(1, sizeof(asl_client_t)); + if (asl == NULL) + { + errno = ENOMEM; + return NULL; + } + + asl->options = ASL_OPT_NO_REMOTE; + asl->sock = -1; + + asl->pid = getpid(); + asl->uid = getuid(); + asl->gid = getgid(); + + asl->filter = ASL_FILTER_MASK_UPTO(ASL_LEVEL_DEBUG); + + if (ident != NULL) + { + asl->name = strdup(ident); + if (asl->name == NULL) + { + free(asl); + return NULL; + } + } + else + { + name = *(*_NSGetArgv()); + if (name != NULL) + { + x = strrchr(name, '/'); + if (x != NULL) x++; + else x = name; + asl->name = strdup(x); + if (asl->name == NULL) + { + free(asl); + return NULL; + } + } + } + + asl->facility = NULL; + if (facility != NULL) asl->facility = strdup(facility); + else asl->facility = strdup(asl_syslog_faciliy_num_to_name(LOG_USER)); + if (asl->facility == NULL) + { + if (asl->name != NULL) free(asl->name); + free(asl); + return NULL; + } + + status = asl_file_open_write_fd(fd, &(asl->aslfile)); + if (status != ASL_STATUS_OK) + { + if (asl->name != NULL) free(asl->name); + if (asl->facility != NULL) free(asl->facility); + free(asl); + return NULL; + } + + asl->aslfileid = 1; + asl->refcount = 1; + + return (aslclient)asl; +} + +__private_extern__ void +asl_client_release(asl_client_t *asl) +{ + uint32_t i; + + if (asl == NULL) return; + + if (OSAtomicDecrement32(&asl->refcount) > 0) return; + + free(asl->name); + free(asl->facility); + + if (asl->sock >= 0) close(asl->sock); + if (!(asl->options & ASL_OPT_NO_REMOTE)) _asl_notify_close(); + + for (i = 0; i < asl->out_count; i++) + { + free(asl->out_list[i].mfmt); + free(asl->out_list[i].tfmt); + } + + free(asl->out_list); + + memset(asl, 0, sizeof(asl_client_t)); + free(asl); +} + +void +asl_close(aslclient ac) +{ + asl_client_release((asl_client_t *)ac); +} + +__private_extern__ asl_client_t * +asl_client_retain(asl_client_t *asl) +{ + int32_t new; + + if (asl == NULL) return NULL; + + new = OSAtomicIncrement32(&asl->refcount); + assert(new >= 1); + + return asl; +} + +__private_extern__ asl_client_t * +_asl_open_default() +{ + static dispatch_once_t once; + + dispatch_once(&once, ^{ + /* + * Do a sleight-of-hand with ASL_OPT_NO_REMOTE to avoid a deadlock + * since asl_open(xxx, yyy, 0) calls _asl_notify_open(1) + * which locks _asl_global.lock. + */ + _asl_global.asl = asl_open(NULL, NULL, ASL_OPT_NO_REMOTE); + + /* Reset options to clear ASL_OPT_NO_REMOTE bit */ + if (_asl_global.asl != NULL) _asl_global.asl->options = 0; + + /* Now call _asl_notify_open(0) to finish the work */ + _asl_notify_open(0); + }); + + return _asl_global.asl; +} + +/* + * asl_add_file: write log messages to the given file descriptor + * Log messages will be written to this file as well as to the server. + */ +int +asl_add_output_file(aslclient ac, int fd, const char *mfmt, const char *tfmt, int filter, int text_encoding) +{ + uint32_t i; + int use_global_lock; + asl_client_t *asl; + + use_global_lock = 0; + asl = (asl_client_t *)ac; + if (asl == NULL) + { + asl = _asl_open_default(); + if (asl == NULL) return -1; + pthread_mutex_lock(&_asl_global.lock); + use_global_lock = 1; + } + + for (i = 0; i < asl->out_count; i++) + { + if (asl->out_list[i].fd == fd) + { + /* update message format, time format, filter, and text encoding */ + free(asl->out_list[i].mfmt); + asl->out_list[i].mfmt = NULL; + if (mfmt != NULL) asl->out_list[i].mfmt = strdup(mfmt); + + free(asl->out_list[i].tfmt); + asl->out_list[i].tfmt = NULL; + if (tfmt != NULL) asl->out_list[i].tfmt = strdup(tfmt); + + asl->out_list[i].encoding = text_encoding; + asl->out_list[i].filter = filter; + + if (use_global_lock != 0) pthread_mutex_unlock(&_asl_global.lock); + return 0; + } + } + + if (asl->out_count == 0) + { + asl->out_list = (asl_out_file_t *)calloc(1, sizeof(asl_out_file_t)); + } + else + { + asl->out_list = (asl_out_file_t *)reallocf(asl->out_list, (1 + asl->out_count) * sizeof(asl_out_file_t)); + } + + if (asl->out_list == NULL) + { + if (use_global_lock != 0) pthread_mutex_unlock(&_asl_global.lock); + return -1; + } + + asl->out_list[asl->out_count].fd = fd; + asl->out_list[asl->out_count].encoding = text_encoding; + asl->out_list[asl->out_count].filter = filter; + if (mfmt != NULL) asl->out_list[asl->out_count].mfmt = strdup(mfmt); + if (tfmt != NULL) asl->out_list[asl->out_count].tfmt = strdup(tfmt); + + asl->out_count++; + + if (use_global_lock != 0) pthread_mutex_unlock(&_asl_global.lock); + return 0; +} + +int +asl_set_output_file_filter(aslclient ac, int fd, int filter) +{ + uint32_t i, last; + int use_global_lock; + asl_client_t *asl; + + use_global_lock = 0; + asl = (asl_client_t *)ac; + if (asl == NULL) + { + asl = _asl_open_default(); + if (asl == NULL) return -1; + pthread_mutex_lock(&_asl_global.lock); + use_global_lock = 1; + } + + last = 0; + + for (i = 0; i < asl->out_count; i++) + { + if (asl->out_list[i].fd == fd) + { + /* update filter */ + last = asl->out_list[i].filter; + asl->out_list[i].filter = filter; + break; + } + } + + if (use_global_lock != 0) pthread_mutex_unlock(&_asl_global.lock); + return last; +} + +/* SPI - Deprecated */ +int +asl_add_output(aslclient ac, int fd, const char *mfmt, const char *tfmt, uint32_t text_encoding) +{ + return asl_add_output_file(ac, fd, mfmt, tfmt, ASL_FILTER_MASK_UPTO(ASL_LEVEL_DEBUG), text_encoding); +} + +int +asl_add_log_file(aslclient ac, int fd) +{ + return asl_add_output_file(ac, fd, ASL_MSG_FMT_STD, ASL_TIME_FMT_LCL, ASL_FILTER_MASK_UPTO(ASL_LEVEL_DEBUG), ASL_ENCODE_SAFE); +} + +/* + * asl_remove_output: stop writing log messages to the given file descriptor + */ +int +asl_remove_output_file(aslclient ac, int fd) +{ + uint32_t i; + int x, use_global_lock; + asl_client_t *asl; + + use_global_lock = 0; + asl = (asl_client_t *)ac; + if (asl == NULL) + { + asl = _asl_open_default(); + if (asl == NULL) return -1; + pthread_mutex_lock(&_asl_global.lock); + use_global_lock = 1; + } + + if (asl->out_count == 0) + { + if (use_global_lock != 0) pthread_mutex_unlock(&_asl_global.lock); + return 0; + } + + x = -1; + for (i = 0; i < asl->out_count; i++) + { + if (asl->out_list[i].fd == fd) + { + x = i; + break; + } + } + + if (x == -1) + { + if (use_global_lock != 0) pthread_mutex_unlock(&_asl_global.lock); + return 0; + } + + free(asl->out_list[x].mfmt); + free(asl->out_list[x].tfmt); + + for (i = x + 1; i < asl->out_count; i++, x++) + { + asl->out_list[x] = asl->out_list[i]; + } + + asl->out_count--; + + if (asl->out_count == 0) + { + free(asl->out_list); + asl->out_list = NULL; + } + else + { + asl->out_list = (asl_out_file_t *)reallocf(asl->out_list, asl->out_count * sizeof(asl_out_file_t)); + + if (asl->out_list == NULL) + { + asl->out_count = 0; + if (use_global_lock != 0) pthread_mutex_unlock(&_asl_global.lock); + return -1; + } + } + + if (use_global_lock != 0) pthread_mutex_unlock(&_asl_global.lock); + return 0; +} + +int +asl_remove_output(aslclient ac, int fd) +{ + return asl_remove_output_file(ac, fd); +} + +int +asl_remove_log_file(aslclient ac, int fd) +{ + return asl_remove_output_file(ac, fd); +} + +int +asl_set_filter(aslclient ac, int f) +{ + int last, use_global_lock; + asl_client_t *asl; + + use_global_lock = 0; + asl = (asl_client_t *)ac; + if (asl == NULL) + { + asl = _asl_open_default(); + if (asl == NULL) return -1; + pthread_mutex_lock(&_asl_global.lock); + use_global_lock = 1; + } + + last = asl->filter; + asl->filter = f; + + if (use_global_lock != 0) pthread_mutex_unlock(&_asl_global.lock); + return last; +} + +/* + * Evaluate client / message / level to determine what to do with a message. + * Checks filters, tunneling, and log files. Returns EVAL_IGNORE if the message + * can be ignored. Otherwise it returns the bits below, ORed with the level. + * + * EVAL_ASLFILE - will write to an asl file (see asl_open_from_file) + * EVAL_SEND - will send to syslogd + * EVAL_TUNNEL - will send to syslogd with tunneling enabled + * EVAL_FILE - will write to file + */ +uint32_t +_asl_evaluate_send(aslclient ac, aslmsg msg, int slevel) +{ + asl_client_t *asl = (asl_client_t *)ac; + uint32_t level, lmask, filter, status, tunnel; + int check, out; + uint64_t v64; + const char *val; + + if (asl == NULL) + { + asl = _asl_open_default(); + if (asl == NULL) return EVAL_IGNORE; + } + + check = ASL_LEVEL_DEBUG; + if (slevel >= 0) check = slevel; + + val = asl_get((aslmsg)msg, ASL_KEY_LEVEL); + if (val != NULL) check = atoi(val); + + if (check < ASL_LEVEL_EMERG) check = ASL_LEVEL_EMERG; + else if (check > ASL_LEVEL_DEBUG) check = ASL_LEVEL_DEBUG; + level = check; + + out = check; + + if (asl->aslfile != NULL) return (out | EVAL_ASLFILE); + + lmask = ASL_FILTER_MASK(level); + + filter = asl->filter & 0xff; + tunnel = (asl->filter & ASL_FILTER_MASK_TUNNEL) >> 8; + + if (!(asl->options & ASL_OPT_NO_REMOTE)) + { + pthread_mutex_lock(&_asl_global.lock); + + if (_asl_global.rc_change_token >= 0) + { + /* initialize or re-check process-specific and master filters */ + check = 0; + status = notify_check(_asl_global.rc_change_token, &check); + if ((status == NOTIFY_STATUS_OK) && (check != 0)) + { + if (_asl_global.master_token >= 0) + { + v64 = 0; + status = notify_get_state(_asl_global.master_token, &v64); + if (status == NOTIFY_STATUS_OK) _asl_global.master_filter = v64; + } + + if (_asl_global.notify_token >= 0) + { + v64 = 0; + status = notify_get_state(_asl_global.notify_token, &v64); + if (status == NOTIFY_STATUS_OK) _asl_global.proc_filter = v64; + } + } + } + + pthread_mutex_unlock(&_asl_global.lock); + + /* master filter overrides local filter */ + if (_asl_global.master_filter != 0) + { + filter = _asl_global.master_filter; + tunnel = 1; + } + + /* process-specific filter overrides local and master */ + if (_asl_global.proc_filter != 0) + { + filter = _asl_global.proc_filter; + tunnel = 1; + } + } + + if ((filter != 0) && ((filter & lmask) != 0)) + { + out |= EVAL_SEND; + if (tunnel != 0) out |= EVAL_TUNNEL; + if (asl->out_count > 0) out |= EVAL_FILE; + + return out; + } + + if ((asl->options & ASL_OPT_SYSLOG_LEGACY) && (filter != 0) && ((filter & lmask) == 0)) + { + return EVAL_IGNORE; + } + + if (asl->out_count > 0) return (out | EVAL_FILE); + + return EVAL_IGNORE; +} + +#endif /* BUILDING_VARIANT */ + +/* + * _asl_lib_vlog + * Internal routine used by asl_vlog. + * msg: an aslmsg + * eval: log level and send flags for the message + * format: A formating string + * ap: va_list for the format + * returns 0 for success, non-zero for failure + */ +static int +_asl_lib_vlog(aslclient ac, uint32_t eval, aslmsg msg, const char *format, va_list ap) +{ + int saved_errno = errno; + int status; + char *str, *fmt, estr[NL_TEXTMAX]; + uint32_t i, len, elen, expand; + asl_client_t *asl; + + asl = (asl_client_t *)ac; + if (asl == NULL) + { + /* + * Initialize _asl_global so that asl_new will have global data. + * Not strictly necessary, but helps performance. + */ + asl = _asl_open_default(); + if (asl == NULL) return -1; + } + + if (format == NULL) return -1; + + /* insert strerror for %m */ + len = 0; + elen = 0; + + expand = 0; + for (i = 0; format[i] != '\0'; i++) + { + if (format[i] == '%') + { + if (format[i+1] == '\0') len++; + else if (format[i+1] == 'm') + { + expand = 1; + strerror_r(saved_errno, estr, sizeof(estr)); + elen = strlen(estr); + len += elen; + i++; + } + else + { + len += 2; + i++; + } + } + else len++; + } + + fmt = (char *)format; + + if (expand != 0) + { + fmt = malloc(len + 1); + if (fmt == NULL) + { + if (estr != NULL) free(estr); + return -1; + } + + len = 0; + + for (i = 0; format[i] != '\0'; i++) + { + if (format[i] == '%') + { + if (format[i+1] == '\0') + { + } + else if ((format[i+1] == 'm') && (elen != 0)) + { + memcpy(fmt+len, estr, elen); + len += elen; + i++; + } + else + { + fmt[len++] = format[i++]; + fmt[len++] = format[i]; + } + } + else fmt[len++] = format[i]; + } + + fmt[len] = '\0'; + } + + str = NULL; + vasprintf(&str, fmt, ap); + if (expand != 0) free(fmt); + + if (str == NULL) return -1; + + status = _asl_send_message(ac, eval, (asl_msg_t *)msg, str); + free(str); + + return status; +} + +/* + * asl_vlog + * Similar to asl_log, but take a va_list instead of a list of arguments. + * msg: an aslmsg + * level: the log level of the associated message + * format: A formating string + * ap: va_list for the format + * returns 0 for success, non-zero for failure + */ +int +asl_vlog(aslclient ac, aslmsg msg, int level, const char *format, va_list ap) +{ + uint32_t eval = _asl_evaluate_send(ac, msg, level); + if (eval == EVAL_IGNORE) return 0; + + return _asl_lib_vlog(ac, eval, msg, format, ap); +} + +/* + * _asl_lib_log + * SPI used by ASL_PREFILTER_LOG. Converts format arguments to a va_list and + * forwards the call to _asl_lib_vlog. + * msg: an aslmsg + * eval: log level and send flags for the message + * format: A formating string + * ... args for format + * returns 0 for success, non-zero for failure + */ +int +_asl_lib_log(aslclient ac, uint32_t eval, aslmsg msg, const char *format, ...) +{ + int status; + if (eval == EVAL_IGNORE) return 0; + + va_list ap; + va_start(ap, format); + status = _asl_lib_vlog(ac, eval, msg, format, ap); + va_end(ap); + + return status; +} + +/* + * asl_log + * Processes an ASL log message. + * msg: an aslmsg + * level: the log level of the associated message + * format: A formating string + * ... args for format + * returns 0 for success, non-zero for failure + */ +int +asl_log(aslclient ac, aslmsg msg, int level, const char *format, ...) +{ + int status; + uint32_t eval = _asl_evaluate_send(ac, msg, level); + if (eval == EVAL_IGNORE) return 0; + + va_list ap; + va_start(ap, format); + status = _asl_lib_vlog(ac, eval, msg, format, ap); + va_end(ap); + + return status; +} + +#ifndef BUILDING_VARIANT + +/* + * asl_get_filter: gets the values for the local, master, and remote filters, + * and indicates which one is active. + */ +int +asl_get_filter(aslclient ac, int *local, int *master, int *remote, int *active) +{ + asl_client_t *asl, *asl_default; + int l, m, r, x; + int status, check; + uint64_t v64; + + l = 0; + m = 0; + r = 0; + x = 0; + + asl_default = _asl_open_default(); + + asl = (asl_client_t *)ac; + if (asl == NULL) asl = asl_default; + if (asl != NULL) l = asl->filter & 0xff; + + if ((asl_default != NULL) && (!(asl_default->options & ASL_OPT_NO_REMOTE))) + { + pthread_mutex_lock(&_asl_global.lock); + + if (_asl_global.rc_change_token >= 0) + { + /* initialize or re-check process-specific and master filters */ + check = 0; + status = notify_check(_asl_global.rc_change_token, &check); + if ((status == NOTIFY_STATUS_OK) && (check != 0)) + { + if (_asl_global.master_token >= 0) + { + v64 = 0; + status = notify_get_state(_asl_global.master_token, &v64); + if (status == NOTIFY_STATUS_OK) _asl_global.master_filter = v64; + } + + if (_asl_global.notify_token >= 0) + { + v64 = 0; + status = notify_get_state(_asl_global.notify_token, &v64); + if (status == NOTIFY_STATUS_OK) _asl_global.proc_filter = v64; + } + } + } + + m = _asl_global.master_filter; + if (m != 0) x = 1; + + r = _asl_global.proc_filter; + if (r != 0) x = 2; + + pthread_mutex_unlock(&_asl_global.lock); + } + + if (local != NULL) *local = l; + if (master != NULL) *master = m; + if (remote != NULL) *remote = r; + if (active != NULL) *active = x; + + return 0; +} + +static int +_asl_send_message(aslclient ac, uint32_t eval, asl_msg_t *msg, const char *mstring) +{ + uint32_t i, len, level, lmask, outstatus; + const char *val; + char *name, *x; + time_t tick; + struct timeval tval; + int status; + asl_client_t *asl; + int use_global_lock; + kern_return_t kstatus; + char aux_val[64]; + char aux_host[_POSIX_HOST_NAME_MAX]; + asl_msg_t *aux; + + if (eval == EVAL_IGNORE) return 0; + + level = eval & LEVEL_MASK; + eval &= EVAL_MASK; + lmask = ASL_FILTER_MASK(level); + + use_global_lock = 0; + asl = (asl_client_t *)ac; + if (asl == NULL) + { + asl = _asl_open_default(); + if (asl == NULL) return -1; + use_global_lock = 1; + } + + if (asl->aslfile != NULL) use_global_lock = 1; + + /* + * Time, TimeNanoSec, Host, PID, UID, and GID values get set here. + * Also sets Sender & Facility (if unset) and "ASLOption store" if remote control is active. + */ + aux = asl_msg_new(ASL_TYPE_MSG); + + if (mstring != NULL) asl_msg_set_key_val(aux, ASL_KEY_MSG, mstring); + + snprintf(aux_val, sizeof(aux_val), "%u", level); + asl_msg_set_key_val(aux, ASL_KEY_LEVEL, aux_val); + + memset(&tval, 0, sizeof(struct timeval)); + + status = gettimeofday(&tval, NULL); + if (status == 0) + { + snprintf(aux_val, sizeof(aux_val), "%lu", tval.tv_sec); + asl_msg_set_key_val(aux, ASL_KEY_TIME, aux_val); + snprintf(aux_val, sizeof(aux_val), "%d", tval.tv_usec * 1000); + asl_msg_set_key_val(aux, ASL_KEY_TIME_NSEC, aux_val); + } + else + { + tick = time(NULL); + snprintf(aux_val, sizeof(aux_val), "%lu", tick); + asl_msg_set_key_val(aux, ASL_KEY_TIME, aux_val); + } + + memset(&aux_host, 0, _POSIX_HOST_NAME_MAX); + if (gethostname(aux_host, _POSIX_HOST_NAME_MAX) == 0) + { + asl_msg_set_key_val(aux, ASL_KEY_HOST, aux_host); + } + + snprintf(aux_val, sizeof(aux_val), "%u", getpid()); + asl_msg_set_key_val(aux, ASL_KEY_PID, aux_val); + + snprintf(aux_val, sizeof(aux_val), "%d", getuid()); + asl_msg_set_key_val(aux, ASL_KEY_UID, aux_val); + + snprintf(aux_val, sizeof(aux_val), "%d", getgid()); + asl_msg_set_key_val(aux, ASL_KEY_GID, aux_val); + + /* + * Set Sender if needed + */ + status = asl_msg_lookup((asl_msg_t *)msg, ASL_KEY_SENDER, &val, NULL); + if ((status != 0) || (val == NULL)) + { + if ((ac != NULL) && (ac->name != NULL)) + { + /* Use the Sender name from the client handle */ + asl_msg_set_key_val(aux, ASL_KEY_SENDER, ac->name); + } + else + { + /* Get the value for ASL_KEY_SENDER from cache */ + if (_asl_global.sender == NULL) + { + name = *(*_NSGetArgv()); + if (name != NULL) + { + x = strrchr(name, '/'); + if (x != NULL) x++; + else x = name; + + pthread_mutex_lock(&_asl_global.lock); + if (_asl_global.sender == NULL) _asl_global.sender = strdup(x); + pthread_mutex_unlock(&_asl_global.lock); + } + } + + if (_asl_global.sender != NULL) asl_msg_set_key_val(aux, ASL_KEY_SENDER, _asl_global.sender); + else asl_msg_set_key_val(aux, ASL_KEY_SENDER, "Unknown"); + } + } + + /* + * Set Facility + */ + status = asl_msg_lookup((asl_msg_t *)msg, ASL_KEY_FACILITY, &val, NULL); + if ((status != 0) || (val == NULL)) + { + if ((ac != NULL) && (ac->facility != NULL)) + { + /* Use the Facility name from the client handle */ + asl_msg_set_key_val(aux, ASL_KEY_FACILITY, ac->facility); + } + } + + /* Set "ASLOption store" if tunneling */ + + if (eval & EVAL_TUNNEL) + { + val = asl_get((aslmsg)msg, ASL_KEY_OPTION); + if (val == NULL) + { + asl_msg_set_key_val(aux, ASL_KEY_OPTION, ASL_OPT_STORE); + } + else + { + char *aux_option = NULL; + asprintf(&aux_option, "%s %s", ASL_OPT_STORE, val); + asl_msg_set_key_val(aux, ASL_KEY_OPTION, aux_option); + free(aux_option); + } + } + + outstatus = -1; + + if (use_global_lock != 0) pthread_mutex_lock(&_asl_global.lock); + + aux = asl_msg_merge(aux, msg); + + /* + * If there is an aslfile this is a stand-alone file client. + * Just save to the file. + */ + if (asl->aslfile != NULL) + { + outstatus = ASL_STATUS_FAILED; + + if (aux != NULL) + { + outstatus = asl_file_save(asl->aslfile, (aslmsg)aux, &(asl->aslfileid)); + asl->aslfileid++; + } + + asl_msg_release(aux); + + if (use_global_lock != 0) pthread_mutex_unlock(&_asl_global.lock); + return outstatus; + } + + _asl_global_init(); + outstatus = 0; + + /* manage quota */ + if ((eval & EVAL_TUNNEL) == 0) + { + time_t last_send = _asl_global.last_send; + time_t last_oq = _asl_global.last_oq_msg; + uint32_t qcurr = _asl_global.quota; + time_t delta; + uint32_t qinc, qnew; + + qnew = qcurr; + + /* add QUOTA_MPS to quota for each second we've been idle */ + if (tval.tv_sec > last_send) + { + delta = tval.tv_sec - last_send; + + qinc = QUOTA_MPH; + if (delta < (QUOTA_MPH / QUOTA_MPS)) qinc = delta * QUOTA_MPS; + + qnew = MIN(QUOTA_MPH, qcurr + qinc); + OSAtomicCompareAndSwapLongBarrier(last_send, tval.tv_sec, (long *)&_asl_global.last_send); + } + + if (qnew == 0) + { + if ((tval.tv_sec - last_oq) > QUOTA_MSG_INTERVAL) + { + eval |= EVAL_QUOTA; + OSAtomicCompareAndSwapLongBarrier(last_oq, tval.tv_sec, (long *)&_asl_global.last_oq_msg); + } + else + { + eval &= ~EVAL_SEND; + } + } + else + { + OSAtomicCompareAndSwap32Barrier(qcurr, qnew - 1, (int32_t *)&_asl_global.quota); + } + } + + if ((_asl_global.server_port != MACH_PORT_NULL) && (eval & EVAL_SEND)) + { + asl_string_t *send_str; + const char *str; + size_t vmsize; + + if (eval & EVAL_QUOTA) + { + asl_msg_set_key_val(aux, ASL_KEY_LEVEL, QUOTA_LEVEL); + asl_msg_set_key_val(aux, ASL_KEY_MSG, QUOTA_MSG); + } + + send_str = asl_msg_to_string_raw(ASL_STRING_MIG, aux, "raw"); + len = asl_string_length(send_str); + vmsize = asl_string_allocated_size(send_str); + str = asl_string_free_return_bytes(send_str); + + if (len != 0) + { + /* send a mach message to syslogd */ + kstatus = _asl_server_message(_asl_global.server_port, (caddr_t)str, len); + if (kstatus != KERN_SUCCESS) + { + /* retry once if the call failed */ + _asl_global_reset(); + _asl_global_init(); + kstatus = _asl_server_message(_asl_global.server_port, (caddr_t)str, len); + if (kstatus != KERN_SUCCESS) + { + _asl_global_reset(); + vm_deallocate(mach_task_self(), (vm_address_t)str, vmsize); + outstatus = -1; + } + } + } + else if (vmsize >0) vm_deallocate(mach_task_self(), (vm_address_t)str, vmsize); + } + + if ((aux != NULL) && (asl->out_count > 0)) + { + /* write to file descriptors */ + for (i = 0; i < asl->out_count; i++) + { + if ((asl->out_list[i].fd >= 0) && (asl->out_list[i].filter != 0) && ((asl->out_list[i].filter & lmask) != 0)) + { + char *str; + + len = 0; + str = asl_format_message(aux, asl->out_list[i].mfmt, asl->out_list[i].tfmt, asl->out_list[i].encoding, &len); + if (str == NULL) continue; + + status = write(asl->out_list[i].fd, str, len - 1); + if (status < 0) + { + /* soft error for fd 2 (stderr) */ + if (asl->out_list[i].fd != 2) outstatus = -1; + asl->out_list[i].fd = -1; + } + + free(str); + } + } + } + + asl_msg_release(aux); + + if (use_global_lock != 0) pthread_mutex_unlock(&_asl_global.lock); + + return outstatus; +} + +/* + * asl_send: send a message + * This routine may be used instead of asl_log() or asl_vlog() if asl_set() + * has been used to set all of a message's attributes. + * eval: hints about what to do with the message + * msg: an aslmsg + * returns 0 for success, non-zero for failure + */ +int +asl_send(aslclient ac, aslmsg msg) +{ + int status = 0; + uint32_t eval = _asl_evaluate_send(ac, msg, -1); + if (eval != 0) status = _asl_send_message(ac, eval, (asl_msg_t *)msg, NULL); + + return status; +} + +static int +_asl_aux_save_context(asl_aux_context_t *ctx) +{ + if (ctx == NULL) return -1; + + pthread_mutex_lock(&_asl_global.lock); + + _asl_global.aux_ctx = (asl_aux_context_t **)reallocf(_asl_global.aux_ctx, (_asl_global.aux_count + 1) * sizeof(asl_aux_context_t *)); + if (_asl_global.aux_ctx == NULL) + { + _asl_global.aux_count = 0; + return -1; + } + + _asl_global.aux_ctx[_asl_global.aux_count++] = ctx; + + pthread_mutex_unlock(&_asl_global.lock); + + return 0; +} + +/* + * Creates an auxiliary file that may be used to save arbitrary data. The ASL message msg + * will be saved at the time that the auxiliary file is created. The message will include + * any keys and values found in msg, and it will include the title and Uniform Type + * Identifier specified. Output parameter out_fd will contain the file descriptor of the + * new auxiliary file. + */ +static int +_asl_auxiliary(asl_msg_t *msg, const char *title, const char *uti, const char *url, int *out_fd) +{ + asl_msg_t *aux; + asl_string_t *send_str; + const char *str; + fileport_t fileport; + kern_return_t kstatus; + size_t len, vmsize; + uint32_t newurllen, where; + int status, fd, fdpair[2]; + caddr_t newurl; + dispatch_queue_t pipe_q; + dispatch_io_t pipe_channel; + dispatch_semaphore_t sem; + + aux = asl_msg_new(ASL_TYPE_MSG); + + if (title != NULL) + { + asl_msg_set_key_val(aux, ASL_KEY_AUX_TITLE, title); + } + + if (uti == NULL) + { + asl_msg_set_key_val(aux, ASL_KEY_AUX_UTI, "public.data"); + } + else + { + asl_msg_set_key_val(aux, ASL_KEY_AUX_UTI, uti); + } + + if (url != NULL) + { + asl_msg_set_key_val(aux, ASL_KEY_AUX_URL, url); + } + + aux = asl_msg_merge(aux, msg); + + /* if (out_fd == NULL), this is from asl_log_auxiliary_location */ + if (out_fd == NULL) + { + uint32_t eval = _asl_evaluate_send(NULL, (aslmsg)aux, -1); + status = _asl_send_message(NULL, eval, aux, NULL); + asl_msg_release(aux); + return status; + } + + where = asl_store_location(); + + if (where == ASL_STORE_LOCATION_MEMORY) + { + /* create a pipe */ + + asl_aux_context_t *ctx = (asl_aux_context_t *)calloc(1, sizeof(asl_aux_context_t)); + if (ctx == NULL) return -1; + + status = pipe(fdpair); + if (status < 0) + { + free(ctx); + return -1; + } + + /* give read end to dispatch_io_read */ + fd = fdpair[0]; + sem = dispatch_semaphore_create(0); + ctx->sem = sem; + ctx->fd = fdpair[1]; + + status = _asl_aux_save_context(ctx); + if (status != 0) + { + close(fdpair[0]); + close(fdpair[1]); + dispatch_release(sem); + free(ctx); + return -1; + } + + pipe_q = dispatch_queue_create("PipeQ", NULL); + pipe_channel = dispatch_io_create(DISPATCH_IO_STREAM, fd, pipe_q, ^(int err){ + close(fd); + }); + + *out_fd = fdpair[1]; + + dispatch_io_set_low_water(pipe_channel, SIZE_MAX); + + dispatch_io_read(pipe_channel, 0, SIZE_MAX, pipe_q, ^(bool done, dispatch_data_t pipedata, int err){ + if (err == 0) + { + size_t len = dispatch_data_get_size(pipedata); + if (len > 0) + { + const char *bytes = NULL; + char *encoded; + uint32_t eval; + + dispatch_data_t md = dispatch_data_create_map(pipedata, (const void **)&bytes, &len); + encoded = asl_core_encode_buffer(bytes, len); + asl_msg_set_key_val(aux, ASL_KEY_AUX_DATA, encoded); + free(encoded); + eval = _asl_evaluate_send(NULL, (aslmsg)aux, -1); + _asl_send_message(NULL, eval, aux, NULL); + asl_msg_release(aux); + dispatch_release(md); + } + } + + if (done) + { + dispatch_semaphore_signal(sem); + dispatch_release(pipe_channel); + dispatch_release(pipe_q); + } + }); + + return 0; + } + + _asl_global_init(); + if (_asl_global.server_port == MACH_PORT_NULL) return -1; + + send_str = asl_msg_to_string_raw(ASL_STRING_MIG, aux, "raw"); + len = asl_string_length(send_str); + vmsize = asl_string_allocated_size(send_str); + str = asl_string_free_return_bytes(send_str); + + if (len == 0) + { + asl_msg_release(aux); + vm_deallocate(mach_task_self(), (vm_address_t)str, vmsize); + return -1; + } + + status = 0; + fileport = MACH_PORT_NULL; + status = KERN_SUCCESS; + + kstatus = _asl_server_create_aux_link(_asl_global.server_port, (caddr_t)str, len, &fileport, &newurl, &newurllen, &status); + if (kstatus != KERN_SUCCESS) + { + /* retry once if the call failed */ + _asl_global_reset(); + _asl_global_init(); + kstatus = _asl_server_create_aux_link(_asl_global.server_port, (caddr_t)str, len, &fileport, &newurl, &newurllen, &status); + if (kstatus != KERN_SUCCESS) + { + _asl_global_reset(); + vm_deallocate(mach_task_self(), (vm_address_t)str, vmsize); + asl_msg_release(aux); + return -1; + } + } + + if (status != 0) + { + asl_msg_release(aux); + return status; + } + + if (newurl != NULL) + { + asl_msg_set_key_val(aux, ASL_KEY_AUX_URL, newurl); + vm_deallocate(mach_task_self(), (vm_address_t)newurl, newurllen); + } + + if (fileport == MACH_PORT_NULL) + { + asl_msg_release(aux); + return -1; + } + + fd = fileport_makefd(fileport); + mach_port_deallocate(mach_task_self(), fileport); + if (fd < 0) + { + asl_msg_release(aux); + status = -1; + } + else + { + asl_aux_context_t *ctx = (asl_aux_context_t *)calloc(1, sizeof(asl_aux_context_t)); + if (ctx == NULL) + { + status = -1; + } + else + { + *out_fd = fd; + + ctx->fd = fd; + ctx->msg = aux; + + status = _asl_aux_save_context(ctx); + } + } + + return status; +} + +int +asl_create_auxiliary_file(aslmsg msg, const char *title, const char *uti, int *out_fd) +{ + if (out_fd == NULL) return -1; + + return _asl_auxiliary((asl_msg_t *)msg, title, uti, NULL, out_fd); +} + +int +asl_log_auxiliary_location(aslmsg msg, const char *title, const char *uti, const char *url) +{ + return _asl_auxiliary((asl_msg_t *)msg, title, uti, url, NULL); +} + +/* + * Close an auxiliary file. + * Sends the cached auxiliary message to syslogd. + */ +int +asl_close_auxiliary_file(int fd) +{ + int i, j, status; + asl_msg_t *aux_msg; + dispatch_semaphore_t aux_sem = NULL; + + pthread_mutex_lock(&(_asl_global.lock)); + + aux_msg = NULL; + status = -1; + + for (i = 0; i < _asl_global.aux_count; i++) + { + if (_asl_global.aux_ctx[i]->fd == fd) + { + status = 0; + + aux_msg = _asl_global.aux_ctx[i]->msg; + aux_sem = _asl_global.aux_ctx[i]->sem; + + free(_asl_global.aux_ctx[i]); + + for (j = i + 1; j < _asl_global.aux_count; i++, j++) + { + _asl_global.aux_ctx[i] = _asl_global.aux_ctx[j]; + } + + _asl_global.aux_count--; + + if (_asl_global.aux_count == 0) + { + free(_asl_global.aux_ctx); + _asl_global.aux_ctx = NULL; + } + else + { + _asl_global.aux_ctx = (asl_aux_context_t **)reallocf(_asl_global.aux_ctx, _asl_global.aux_count * sizeof(asl_aux_context_t *)); + if (_asl_global.aux_ctx == NULL) + { + _asl_global.aux_count = 0; + status = -1; + } + } + + break; + } + } + + pthread_mutex_unlock(&(_asl_global.lock)); + + close(fd); + + if (aux_msg != NULL) + { + uint32_t eval = _asl_evaluate_send(NULL, (aslmsg)aux_msg, -1); + if (_asl_send_message(NULL, eval, aux_msg, NULL) != ASL_STATUS_OK) status = -1; + asl_msg_release(aux_msg); + } + + if (aux_sem != NULL) + { + dispatch_semaphore_wait(aux_sem, DISPATCH_TIME_FOREVER); + dispatch_release(aux_sem); + } + + return status; +} + +/* + * asl_search: Search for messages matching the criteria described + * by the aslmsg. The caller should set the attributes to match using + * asl_set_query() or asl_set(). The operatoin ASL_QUERY_OP_EQUAL is + * used for attributes set with asl_set(). + * a: an aslmsg + * returns: a set of messages that can be iterated over using aslresp_next(), + * and the values can be retrieved using aslresp_get. + */ + +/* + * This routine searches the ASL datastore on disk (/var/log/asl). + * It is called my asl_search if syslogd is not running or if syslogd + * indicates that an in-memory store is not being used. + */ +static aslresponse +_asl_search_store(aslclient ac, aslmsg a) +{ + asl_search_result_t query, *out; + asl_msg_t *q, *qlist[1]; + uint32_t status, op; + uint64_t last_id, start_id; + asl_store_t *store; + const char *val; + + if (a == NULL) return NULL; + + q = (asl_msg_t *)a; + + /* check for "ASLMessageId >[=] n" and set start_id */ + start_id = 0; + val = NULL; + + status = asl_msg_lookup(q, ASL_KEY_MSG_ID, &val, &op); + if ((status == 0) && (val != NULL) && (op & ASL_QUERY_OP_GREATER)) + { + if (op & ASL_QUERY_OP_EQUAL) start_id = atoll(val); + else start_id = atoll(val) + 1; + } + + store = NULL; + status = asl_store_open_read(NULL, &store); + if (status != 0) return NULL; + if (store == NULL) return NULL; + + out = NULL; + last_id = 0; + + qlist[0] = (asl_msg_t *)a; + memset(&query, 0, sizeof(asl_search_result_t)); + query.count = 1; + query.msg = qlist; + + status = asl_store_match(store, &query, &out, &last_id, start_id, 0, 1); + asl_store_close(store); + + return out; +} + +static uint32_t +_asl_search_concat_results(asl_search_result_t *batch, asl_search_result_t **out) +{ + uint32_t i, j; + + if (out == NULL) return ASL_STATUS_FAILED; + + /* nothing to do if batch is NULL or contains no messages */ + if (batch == NULL) return 0; + if (batch->count == 0) + { + aslresponse_free(batch); + return 0; + } + + if (*out == NULL) *out = (asl_search_result_t *)calloc(1, sizeof(asl_search_result_t)); + if (*out == NULL) + { + aslresponse_free(batch); + return ASL_STATUS_FAILED; + } + + if ((*out)->count == 0) + { + (*out)->msg = (asl_msg_t **)calloc(batch->count, sizeof(asl_msg_t *)); + } + else + { + (*out)->msg = (asl_msg_t **)reallocf((*out)->msg, ((*out)->count + batch->count) * sizeof(asl_msg_t *)); + } + + if ((*out)->msg == NULL) + { + aslresponse_free(batch); + free(*out); + *out = NULL; + return ASL_STATUS_FAILED; + } + + for (i = 0, j = (*out)->count; i < batch->count; i++, j++) (*out)->msg[j] = batch->msg[i]; + + (*out)->count += batch->count; + free(batch->msg); + free(batch); + return ASL_STATUS_OK; +} + +static aslresponse +_asl_search_memory(aslclient ac, aslmsg a) +{ + asl_search_result_t *batch, *out; + char *qstr, *str, *res; + uint32_t len, reslen, status; + uint64_t cmax, qmin; + kern_return_t kstatus; + caddr_t vmstr; + + if (a == NULL) return 0; + + _asl_global_init(); + if (_asl_global.server_port == MACH_PORT_NULL) return NULL; + + len = 0; + qstr = asl_msg_to_string((asl_msg_t *)a, &len); + + str = NULL; + if (qstr == NULL) + { + asprintf(&str, "0\n"); + len = 3; + } + else + { + asprintf(&str, "1\n%s\n", qstr); + len += 3; + free(qstr); + } + + if (str == NULL) return NULL; + + /* + * Fetch a batch of results each time through the loop. + * Fetching small batches rebuces the load on syslogd. + */ + out = NULL; + qmin = 0; + cmax = 0; + + forever + { + res = NULL; + reslen = 0; + status = ASL_STATUS_OK; + + kstatus = vm_allocate(mach_task_self(), (vm_address_t *)&vmstr, len, TRUE); + if (kstatus != KERN_SUCCESS) return NULL; + + memmove(vmstr, str, len); + + status = 0; + kstatus = _asl_server_query_2(_asl_global.server_port, vmstr, len, qmin, FETCH_BATCH, 0, (caddr_t *)&res, &reslen, &cmax, (int *)&status); + if (kstatus != KERN_SUCCESS) + { + /* retry once if the call failed */ + _asl_global_reset(); + _asl_global_init(); + kstatus = _asl_server_query_2(_asl_global.server_port, vmstr, len, qmin, FETCH_BATCH, 0, (caddr_t *)&res, &reslen, &cmax, (int *)&status); + if (kstatus != KERN_SUCCESS) + { + _asl_global_reset(); + break; + } + } + + if (res == NULL) break; + + batch = asl_list_from_string(res); + vm_deallocate(mach_task_self(), (vm_address_t)res, reslen); + + status = _asl_search_concat_results(batch, &out); + if (status != ASL_STATUS_OK) break; + if ((out == NULL) || (out->count < FETCH_BATCH)) break; + + if (cmax >= qmin) qmin = cmax + 1; + } + + free(str); + + return out; +} + +aslmsg +_asl_server_control_query(void) +{ + asl_search_result_t *list = NULL; + char *qstr, *res; + uint32_t len, reslen, status; + uint64_t cmax, qmin; + kern_return_t kstatus; + caddr_t vmstr; + asl_msg_t *m = NULL; + static const char ctlstr[] = "1\nQ [= ASLOption control]\n"; + + _asl_global_init(); + if (_asl_global.server_port == MACH_PORT_NULL) return NULL; + + len = strlen(ctlstr) + 1; + + qmin = 0; + cmax = 0; + res = NULL; + reslen = 0; + + kstatus = vm_allocate(mach_task_self(), (vm_address_t *)&vmstr, len, TRUE); + if (kstatus != KERN_SUCCESS) return NULL; + + memmove(vmstr, ctlstr, len); + + status = 0; + kstatus = _asl_server_query_2(_asl_global.server_port, vmstr, len, qmin, FETCH_BATCH, 0, (caddr_t *)&res, &reslen, &cmax, (int *)&status); + if (kstatus != KERN_SUCCESS) + { + /* retry once if the call failed */ + _asl_global_reset(); + _asl_global_init(); + kstatus = _asl_server_query_2(_asl_global.server_port, vmstr, len, qmin, FETCH_BATCH, 0, (caddr_t *)&res, &reslen, &cmax, (int *)&status); + if (kstatus != KERN_SUCCESS) _asl_global_reset(); + } + + list = asl_list_from_string(res); + vm_deallocate(mach_task_self(), (vm_address_t)res, reslen); + + if (list == NULL) return NULL; + if (list->count > 0) m = asl_msg_retain(list->msg[0]); + aslresponse_free((aslresponse)list); + return (aslmsg)m; +} + +int +asl_store_location() +{ + kern_return_t kstatus; + char *res; + uint32_t reslen, status; + uint64_t cmax; + + _asl_global_init(); + if (_asl_global.server_port == MACH_PORT_NULL) return ASL_STORE_LOCATION_FILE; + + res = NULL; + reslen = 0; + cmax = 0; + status = ASL_STATUS_OK; + + kstatus = _asl_server_query_2(_asl_global.server_port, NULL, 0, 0, -1, 0, (caddr_t *)&res, &reslen, &cmax, (int *)&status); + if (kstatus != KERN_SUCCESS) + { + /* retry once if the call failed */ + _asl_global_reset(); + _asl_global_init(); + kstatus = _asl_server_query_2(_asl_global.server_port, NULL, 0, 0, -1, 0, (caddr_t *)&res, &reslen, &cmax, (int *)&status); + } + + /* res should never be returned, but just to be certain we don't leak VM ... */ + if (res != NULL) vm_deallocate(mach_task_self(), (vm_address_t)res, reslen); + + if (kstatus != KERN_SUCCESS) + { + _asl_global_reset(); + return ASL_STORE_LOCATION_FILE; + } + + if (status == ASL_STATUS_OK) return ASL_STORE_LOCATION_MEMORY; + return ASL_STORE_LOCATION_FILE; +} + +aslresponse +asl_search(aslclient ac, aslmsg a) +{ + int where; + asl_search_result_t *out; + + _asl_global_init(); + + where = asl_store_location(); + if (where == ASL_STORE_LOCATION_FILE) out = _asl_search_store(ac, a); + else out = _asl_search_memory(ac, a); + + return out; +} + +int +asl_syslog_faciliy_name_to_num(const char *name) +{ + if (name == NULL) return -1; + + if (strcaseeq(name, "auth")) return LOG_AUTH; + if (strcaseeq(name, "authpriv")) return LOG_AUTHPRIV; + if (strcaseeq(name, "cron")) return LOG_CRON; + if (strcaseeq(name, "daemon")) return LOG_DAEMON; + if (strcaseeq(name, "ftp")) return LOG_FTP; + if (strcaseeq(name, "install")) return LOG_INSTALL; + if (strcaseeq(name, "kern")) return LOG_KERN; + if (strcaseeq(name, "lpr")) return LOG_LPR; + if (strcaseeq(name, "mail")) return LOG_MAIL; + if (strcaseeq(name, "netinfo")) return LOG_NETINFO; + if (strcaseeq(name, "remoteauth")) return LOG_REMOTEAUTH; + if (strcaseeq(name, "news")) return LOG_NEWS; + if (strcaseeq(name, "security")) return LOG_AUTH; + if (strcaseeq(name, "syslog")) return LOG_SYSLOG; + if (strcaseeq(name, "user")) return LOG_USER; + if (strcaseeq(name, "uucp")) return LOG_UUCP; + if (strcaseeq(name, "local0")) return LOG_LOCAL0; + if (strcaseeq(name, "local1")) return LOG_LOCAL1; + if (strcaseeq(name, "local2")) return LOG_LOCAL2; + if (strcaseeq(name, "local3")) return LOG_LOCAL3; + if (strcaseeq(name, "local4")) return LOG_LOCAL4; + if (strcaseeq(name, "local5")) return LOG_LOCAL5; + if (strcaseeq(name, "local6")) return LOG_LOCAL6; + if (strcaseeq(name, "local7")) return LOG_LOCAL7; + if (strcaseeq(name, "launchd")) return LOG_LAUNCHD; + + return -1; +} + +const char * +asl_syslog_faciliy_num_to_name(int n) +{ + if (n < 0) return NULL; + + if (n == LOG_AUTH) return "auth"; + if (n == LOG_AUTHPRIV) return "authpriv"; + if (n == LOG_CRON) return "cron"; + if (n == LOG_DAEMON) return "daemon"; + if (n == LOG_FTP) return "ftp"; + if (n == LOG_INSTALL) return "install"; + if (n == LOG_KERN) return "kern"; + if (n == LOG_LPR) return "lpr"; + if (n == LOG_MAIL) return "mail"; + if (n == LOG_NETINFO) return "netinfo"; + if (n == LOG_REMOTEAUTH) return "remoteauth"; + if (n == LOG_NEWS) return "news"; + if (n == LOG_AUTH) return "security"; + if (n == LOG_SYSLOG) return "syslog"; + if (n == LOG_USER) return "user"; + if (n == LOG_UUCP) return "uucp"; + if (n == LOG_LOCAL0) return "local0"; + if (n == LOG_LOCAL1) return "local1"; + if (n == LOG_LOCAL2) return "local2"; + if (n == LOG_LOCAL3) return "local3"; + if (n == LOG_LOCAL4) return "local4"; + if (n == LOG_LOCAL5) return "local5"; + if (n == LOG_LOCAL6) return "local6"; + if (n == LOG_LOCAL7) return "local7"; + if (n == LOG_LAUNCHD) return "launchd"; + + return NULL; +} + +/* + * utility for converting a time string into a time_t + * we only deal with the following formats: + * Canonical form YYYY.MM.DD hh:mm:ss UTC + * ctime() form Mth dd hh:mm:ss (e.g. Aug 25 09:54:37) + * absolute form - # seconds since the epoch (e.g. 1095789191) + * relative time - seconds before or after now (e.g. -300, +43200) + * relative time - days/hours/minutes/seconds before or after now (e.g. -1d, +6h, +30m, -10s) + */ + +#define CANONICAL_TIME_REX "^[0-9][0-9][0-9][0-9].[01]?[0-9].[0-3]?[0-9][ ]+[0-2]?[0-9]:[0-5][0-9]:[0-5][0-9][ ]+UTC$" +#define CTIME_REX "^[adfjmnos][aceopu][bcglnprtvy][ ]+[0-3]?[0-9][ ]+[0-2]?[0-9]:[0-5][0-9]:[0-5][0-9]$" +#define ABSOLUTE_TIME_REX "^[0-9]+[s]?$" +#define RELATIVE_TIME_REX "^[\\+-\\][0-9]+[smhdw]?$" + +#define SECONDS_PER_MINUTE 60 +#define SECONDS_PER_HOUR 3600 +#define SECONDS_PER_DAY 86400 +#define SECONDS_PER_WEEK 604800 + +static regex_t rex_canon, rex_ctime, rex_abs, rex_rel; +static int reg_status = 0; + +/* + * We use the last letter in the month name to determine + * the month number (0-11). There are two collisions: + * Jan and Jun both end in n + * Mar and Apr both end in r + * In these cases we check the second letter. + * + * The MTH_LAST array maps the last letter to a number. + */ +static const int8_t MTH_LAST[] = {-1, 1, 11, -1, -1, -1, 7, -1, -1, -1, -1, 6, -1, 5, -1, 8, -1, 3, -1, 9, -1, 10, -1, -1, 4, -1}; + +static int +_month_num(char *s) +{ + int i; + int8_t v8; + + v8 = -1; + if (s[2] > 90) v8 = s[2] - 'a'; + else v8 = s[2] - 'A'; + + if ((v8 < 0) || (v8 > 25)) return -1; + + v8 = MTH_LAST[v8]; + if (v8 < 0) return -1; + + i = v8; + if ((i == 5) && ((s[1] == 'a') || (s[1] == 'A'))) return 0; + if ((i == 3) && ((s[1] == 'a') || (s[1] == 'A'))) return 2; + return i; +} + +time_t +asl_parse_time(const char *in) +{ + int len, y; + struct tm t; + time_t tick, delta, factor; + char *str, *p, *x; + static dispatch_once_t once; + + if (in == NULL) return -1; + + dispatch_once(&once, ^{ + int status; + int rflags = REG_EXTENDED | REG_NOSUB | REG_ICASE; + + memset(&rex_canon, 0, sizeof(regex_t)); + status = regcomp(&rex_canon, CANONICAL_TIME_REX, rflags); + if (status != 0) reg_status = -1; + + memset(&rex_ctime, 0, sizeof(regex_t)); + status = regcomp(&rex_ctime, CTIME_REX, rflags); + if (status != 0) reg_status = -1; + + memset(&rex_abs, 0, sizeof(regex_t)); + status = regcomp(&rex_abs, ABSOLUTE_TIME_REX, rflags); + if (status != 0) reg_status = -1; + + memset(&rex_rel, 0, sizeof(regex_t)); + status = regcomp(&rex_rel, RELATIVE_TIME_REX, rflags); + if (status != 0) reg_status = -1; + }); + + if (reg_status < 0) return -1; + + len = strlen(in) + 1; + + if (regexec(&rex_abs, in, 0, NULL, 0) == 0) + { + /* + * Absolute time (number of seconds since the epoch) + */ + str = strdup(in); + if (str == NULL) return -1; + + if ((str[len-2] == 's') || (str[len-2] == 'S')) str[len-2] = '\0'; + + tick = atol(str); + free(str); + + return tick; + } + else if (regexec(&rex_rel, in, 0, NULL, 0) == 0) + { + /* + * Reletive time (number of seconds before or after right now) + */ + str = strdup(in); + if (str == NULL) return -1; + + factor = 1; + + if ((str[len-2] == 's') || (str[len-2] == 'S')) + { + str[len-2] = '\0'; + } + else if ((str[len-2] == 'm') || (str[len-2] == 'M')) + { + str[len-2] = '\0'; + factor = SECONDS_PER_MINUTE; + } + else if ((str[len-2] == 'h') || (str[len-2] == 'H')) + { + str[len-2] = '\0'; + factor = SECONDS_PER_HOUR; + } + else if ((str[len-2] == 'd') || (str[len-2] == 'D')) + { + str[len-2] = '\0'; + factor = SECONDS_PER_DAY; + } + else if ((str[len-2] == 'w') || (str[len-2] == 'W')) + { + str[len-2] = '\0'; + factor = SECONDS_PER_WEEK; + } + + tick = time(NULL); + delta = factor * atol(str); + tick += delta; + + free(str); + + return tick; + } + else if (regexec(&rex_canon, in, 0, NULL, 0) == 0) + { + memset(&t, 0, sizeof(struct tm)); + str = strdup(in); + if (str == NULL) return -1; + + /* Get year */ + x = str; + p = strchr(x, '.'); + *p = '\0'; + t.tm_year = atoi(x) - 1900; + + /* Get month */ + x = p + 1; + p = strchr(x, '.'); + *p = '\0'; + t.tm_mon = atoi(x) - 1; + + /* Get day */ + x = p + 1; + p = strchr(x, ' '); + *p = '\0'; + t.tm_mday = atoi(x); + + /* Get hour */ + for (x = p + 1; *x == ' '; x++); + p = strchr(x, ':'); + *p = '\0'; + t.tm_hour = atoi(x); + + /* Get minutes */ + x = p + 1; + p = strchr(x, ':'); + *p = '\0'; + t.tm_min = atoi(x); + + /* Get seconds */ + x = p + 1; + p = strchr(x, ' '); + *p = '\0'; + t.tm_sec = atoi(x); + + free(str); + return timegm(&t); + } + else if (regexec(&rex_ctime, in, 0, NULL, 0) == 0) + { + /* We assume it's in the current year */ + memset(&t, 0, sizeof(struct tm)); + tick = time(NULL); + gmtime_r(&tick, &t); + y = t.tm_year; + + memset(&t, 0, sizeof(struct tm)); + str = strdup(in); + if (str == NULL) return -1; + + t.tm_year = y; + t.tm_mon = _month_num(str); + if (t.tm_mon < 0) return -1; + + for (x = strchr(str, ' '); *x == ' '; x++); + p = strchr(x, ' '); + *p = '\0'; + t.tm_mday = atoi(x); + + /* Get hour */ + for (x = p + 1; *x == ' '; x++); + p = strchr(x, ':'); + *p = '\0'; + t.tm_hour = atoi(x); + + /* Get minutes */ + x = p + 1; + p = strchr(x, ':'); + *p = '\0'; + t.tm_min = atoi(x); + + /* Get seconds */ + x = p + 1; + t.tm_sec = atoi(x); + + t.tm_isdst = -1; + + free(str); + return mktime(&t); + } + + return -1; +} + +#endif /* BUILDING_VARIANT */ diff --git a/libsystem_asl.tproj/src/asl_core.c b/libsystem_asl.tproj/src/asl_core.c new file mode 100644 index 0000000..b363d2d --- /dev/null +++ b/libsystem_asl.tproj/src/asl_core.c @@ -0,0 +1,900 @@ +/* + * Copyright (c) 2007-2011 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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, 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 +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define ASL_STRING_QUANTUM 256 +static const char *cvis_7_13 = "abtnvfr"; + +/* + * Message ID generation + */ +static uint64_t _asl_core_msg_next_id = 1; +static pthread_mutex_t core_lock = PTHREAD_MUTEX_INITIALIZER; + +#define mix(a, b, c) \ +{ \ + a -= b; a -= c; a ^= (c>>13); \ + b -= c; b -= a; b ^= (a<< 8); \ + c -= a; c -= b; c ^= (b>>13); \ + a -= b; a -= c; a ^= (c>>12); \ + b -= c; b -= a; b ^= (a<<16); \ + c -= a; c -= b; c ^= (b>> 5); \ + a -= b; a -= c; a ^= (c>> 3); \ + b -= c; b -= a; b ^= (a<<10); \ + c -= a; c -= b; c ^= (b>>15); \ +} + +/* + * Hash is used to improve string search. + */ +uint32_t +asl_core_string_hash(const char *s, uint32_t inlen) +{ + uint32_t a, b, c, l, len; + + if (s == NULL) return 0; + + l = inlen; + if (l == 0) + { + if (s[0] == '\0') return 0; + l = strlen(s); + } + + len = l; + a = b = 0x9e3779b9; + c = 0; + + while (len >= 12) + { + a += (s[0] + ((uint32_t)s[1]<<8) + ((uint32_t)s[ 2]<<16) + ((uint32_t)s[ 3]<<24)); + b += (s[4] + ((uint32_t)s[5]<<8) + ((uint32_t)s[ 6]<<16) + ((uint32_t)s[ 7]<<24)); + c += (s[8] + ((uint32_t)s[9]<<8) + ((uint32_t)s[10]<<16) + ((uint32_t)s[11]<<24)); + + mix(a, b, c); + + s += 12; + len -= 12; + } + + c += l; + switch(len) + { + case 11: c += ((uint32_t)s[10]<<24); + case 10: c += ((uint32_t)s[9]<<16); + case 9 : c += ((uint32_t)s[8]<<8); + + case 8 : b += ((uint32_t)s[7]<<24); + case 7 : b += ((uint32_t)s[6]<<16); + case 6 : b += ((uint32_t)s[5]<<8); + case 5 : b += s[4]; + + case 4 : a += ((uint32_t)s[3]<<24); + case 3 : a += ((uint32_t)s[2]<<16); + case 2 : a += ((uint32_t)s[1]<<8); + case 1 : a += s[0]; + } + + mix(a, b, c); + + if (c == 0) c = 1; + return c; +} + +const char * +asl_core_error(uint32_t code) +{ + switch (code) + { + case ASL_STATUS_OK: return "Operation Succeeded"; + case ASL_STATUS_INVALID_ARG: return "Invalid Argument"; + case ASL_STATUS_INVALID_STORE: return "Invalid Data Store"; + case ASL_STATUS_INVALID_STRING: return "Invalid String"; + case ASL_STATUS_INVALID_ID: return "Invalid ID Number"; + case ASL_STATUS_INVALID_MESSAGE: return "Invalid Message"; + case ASL_STATUS_NOT_FOUND: return "Not Found"; + case ASL_STATUS_READ_FAILED: return "Read Operation Failed"; + case ASL_STATUS_WRITE_FAILED: return "Write Operation Failed"; + case ASL_STATUS_NO_MEMORY: return "System Memory Allocation Failed"; + case ASL_STATUS_ACCESS_DENIED: return "Access Denied"; + case ASL_STATUS_READ_ONLY: return "Read Only Access"; + case ASL_STATUS_WRITE_ONLY: return "Write Only Access"; + case ASL_STATUS_MATCH_FAILED: return "Match Failed"; + case ASL_STATUS_NO_RECORDS: return "No More Records"; + } + + return "Operation Failed"; +} + +static uint32_t +asl_core_check_user_access(int32_t msgu, int32_t readu) +{ + /* -1 means anyone may read */ + if (msgu == -1) return ASL_STATUS_OK; + + /* Check for exact match */ + if (msgu == readu) return ASL_STATUS_OK; + + return ASL_STATUS_ACCESS_DENIED; +} + +static uint32_t +asl_core_check_group_access(int32_t msgg, int32_t readu, int32_t readg) +{ + int check; + uuid_t uu, gu; + + /* -1 means anyone may read */ + if (msgg == -1) return ASL_STATUS_OK; + + /* Check for exact match */ + if (msgg == readg) return ASL_STATUS_OK; + + /* Check if user (u) is in read group (msgg) */ + mbr_uid_to_uuid(readu, uu); + mbr_gid_to_uuid(msgg, gu); + + check = 0; + mbr_check_membership(uu, gu, &check); + if (check != 0) return ASL_STATUS_OK; + + return ASL_STATUS_ACCESS_DENIED; +} + +uint32_t +asl_core_check_access(int32_t msgu, int32_t msgg, int32_t readu, int32_t readg, uint16_t flags) +{ + uint16_t uset, gset; + + /* root (uid 0) may always read */ + if (readu == 0) return ASL_STATUS_OK; + + uset = flags & ASL_MSG_FLAG_READ_UID_SET; + gset = flags & ASL_MSG_FLAG_READ_GID_SET; + + /* if no access controls are set, anyone may read */ + if ((uset | gset) == 0) return ASL_STATUS_OK; + + /* if only uid is set, then access is only by uid match */ + if ((uset != 0) && (gset == 0)) return asl_core_check_user_access(msgu, readu); + + /* if only gid is set, then access is only by gid match */ + if ((uset == 0) && (gset != 0)) return asl_core_check_group_access(msgg, readu, readg); + + /* both uid and gid are set - check user, then group */ + if ((asl_core_check_user_access(msgu, readu)) == ASL_STATUS_OK) return ASL_STATUS_OK; + return asl_core_check_group_access(msgg, readu, readg); +} + +uint64_t +asl_core_htonq(uint64_t n) +{ +#ifdef __BIG_ENDIAN__ + return n; +#else + u_int32_t t; + union + { + u_int64_t q; + u_int32_t l[2]; + } x; + + x.q = n; + t = x.l[0]; + x.l[0] = htonl(x.l[1]); + x.l[1] = htonl(t); + + return x.q; +#endif +} + +uint64_t +asl_core_ntohq(uint64_t n) +{ +#ifdef __BIG_ENDIAN__ + return n; +#else + u_int32_t t; + union + { + u_int64_t q; + u_int32_t l[2]; + } x; + + x.q = n; + t = x.l[0]; + x.l[0] = ntohl(x.l[1]); + x.l[1] = ntohl(t); + + return x.q; +#endif +} + +uint64_t +asl_core_new_msg_id(uint64_t start) +{ + uint64_t out; + + pthread_mutex_lock(&core_lock); + + if (start != 0) _asl_core_msg_next_id = start; + + out = _asl_core_msg_next_id; + _asl_core_msg_next_id++; + + pthread_mutex_unlock(&core_lock); + + return out; +} + +/* + * asl_core_encode_buffer + * encode arbitrary data as a C string without embedded zero (nul) characters + * + * The routine computes a histogram of the input buffer and finds + * the two least frequently used non-nul chars (L[0] and L[1]). + * + * L[0] is used to stand in for nul. + * L[1] is used as the escape character. + * Occurrences of nul in the data are encoded as L[0] + * Occurrences of L[0] in the data are encoded as the sequence L[1] 1. + * Occurrences of L[1] in the data are encoded as the sequence L[1] 2. + * + * The output string is preceded by L[0] L[1], and is nul terminated. + * The output length is 2 + n + N(L[0]) + N(L[1]) + 1 + * where N(x) is the number of occurrences of x in the input string. + * The worst case occurs when all characters are equally frequent, + * In that case the output size will less that 1% larger than the input. + */ +char * +asl_core_encode_buffer(const char *in, uint32_t len) +{ + char *str; + uint32_t i, j, k, outlen, breakit, min, hist[256]; + uint32_t lfu[2], save[2]; + uint8_t v; + + if (in == NULL) return NULL; + if (len == 0) return NULL; + + memset(hist, 0, sizeof(hist)); + save[0] = 0; + save[1] = 0; + + for (i = 0; i < len; i++) + { + v = in[i]; + hist[v]++; + } + + for (j = 0; j < 2; j++) + { + lfu[j] = 1; + min = hist[1]; + + for (i = 2; i < 256; i++) + { + if (hist[i] < min) + { + lfu[j] = i; + min = hist[i]; + + /* + * Stop if there are no occurances or character i in the input. + * The minimum will never be less than zero. + */ + if (min == 0) break; + + /* + * When looking for the second least frequently used character, + * stop scanning if we hit the same minimum as we saw in the first + * pass. There will be no smaller values. + */ + if ((j == 1) && (min == save[0])) break; + } + } + + save[j] = hist[lfu[j]]; + hist[lfu[j]] = (uint32_t)-1; + } + + outlen = 2 + len + save[0] + save[1] + 1; + + str = malloc(outlen); + if (str == NULL) return NULL; + + str[outlen - 1] = '\0'; + + str[0] = lfu[0]; + str[1] = lfu[1]; + + j = 2; + + for (i = 0; i < len; i++) + { + v = in[i]; + if (v == 0) + { + str[j++] = lfu[0]; + continue; + } + + breakit = 0; + for (k = 0; (k < 2) && (breakit == 0); k++) + { + if (v == lfu[k]) + { + str[j++] = lfu[1]; + str[j++] = k + 1; + breakit = 1; + } + } + + if (breakit == 1) continue; + + str[j++] = v; + } + + return str; +} + +/* + * asl_core_decode_buffer + * decode a string produced by asl_encode_buffer to recreate the original data + */ +int32_t +asl_core_decode_buffer(const char *in, char **buf, uint32_t *len) +{ + uint8_t v; + uint32_t i, j, n, outlen; + uint8_t lfu[2]; + char *out; + + if (buf == NULL) return -1; + if (len == NULL) return -1; + + lfu[0] = in[0]; + lfu[1] = in[1]; + + outlen = 0; + + /* strip trailing nul */ + n = strlen(in); + + /* determine output length and check for invalid input */ + for (i = 2; i < n; i++) + { + v = in[i]; + if (v == lfu[1]) + { + i++; + if (i == n) return -1; + + v = in[i]; + if ((v < 1) || (v > 2)) return -1; + + outlen++; + } + else outlen++; + } + + if (outlen == 0) return -1; + + out = malloc(outlen); + if (out == NULL) return -1; + + j = 0; + for (i = 2; i < n; i++) + { + v = in[i]; + if (v == lfu[0]) + { + out[j++] = 0; + } + else if (v == lfu[1]) + { + i++; + v = in[i]; + out[j++] = lfu[v - 1]; + } + else out[j++] = v; + } + + *len = outlen; + *buf = out; + return 0; +} + +/* asl_string_t support */ + +asl_string_t * +asl_string_new(uint32_t encoding) +{ + asl_string_t *str = (asl_string_t *)calloc(1, sizeof(asl_string_t)); + if (str == NULL) return NULL; + + str->encoding = encoding; + str->delta = ASL_STRING_QUANTUM; + if (encoding & ASL_STRING_VM) str->delta = PAGE_SIZE; + str->bufsize = 0; + str->cursor = 0; + + if (encoding & ASL_STRING_LEN) asl_string_append_no_encoding(str, " 0 "); + return str; +} + +void +asl_string_free(asl_string_t *str) +{ + if (str == NULL) return; + + if (str->encoding & ASL_STRING_VM) + { + vm_deallocate(mach_task_self(), (vm_address_t)str->buf, str->bufsize); + } + else + { + free(str->buf); + } + + free(str); +} + +char * +asl_string_free_return_bytes(asl_string_t *str) +{ + char *out; + if (str == NULL) return NULL; + + out = str->buf; + free(str); + return out; +} + +char * +asl_string_bytes(asl_string_t *str) +{ + if (str == NULL) return NULL; + return str->buf; +} + +/* length includes trailing nul */ +size_t +asl_string_length(asl_string_t *str) +{ + if (str == NULL) return 0; + if (str->cursor == 0) return 0; + + return str->cursor + 1; +} + +size_t +asl_string_allocated_size(asl_string_t *str) +{ + if (str == NULL) return 0; + return str->bufsize; +} + +static int +_asl_string_grow(asl_string_t *str, size_t len) +{ + size_t newlen = 0; + + if (str == NULL) return -1; + if (len == 0) return 0; + + if (str->bufsize == 0) + { + newlen = ((len + str->delta - 1) / str->delta) * str->delta; + } + else + { + /* used size is (str->cursor + 1) including tailiing nul */ + if (len <= (str->bufsize - (str->cursor + 1))) return 0; + + /* really this is ((str->cursor + 1) + len + (str->delta - 1)) */ + newlen = ((str->cursor + len + str->delta) / str->delta) * str->delta; + } + + if (str->encoding & ASL_STRING_VM) + { + kern_return_t kstatus; + vm_address_t new = 0; + + kstatus = vm_allocate(mach_task_self(), &new, newlen, TRUE); + if (kstatus != KERN_SUCCESS) + { + new = 0; + newlen = 0; + return -1; + } + + if (str->buf != NULL) + { + memcpy((void *)new, str->buf, str->bufsize); + vm_deallocate(mach_task_self(), (vm_address_t)str->buf, str->bufsize); + } + + str->buf = (char *)new; + str->bufsize = newlen; + } + else + { + str->buf = reallocf(str->buf, newlen); + if (str->buf == NULL) + { + str->cursor = 0; + str->bufsize = 0; + return -1; + } + + str->bufsize = newlen; + } + + return 0; +} + +asl_string_t * +asl_string_append_char_no_encoding(asl_string_t *str, const char c) +{ + size_t len; + + if (str == NULL) return NULL; + + len = 1; + if (str->bufsize == 0) len++; + + if (_asl_string_grow(str, len) < 0) return str; + + str->buf[str->cursor] = c; + str->cursor++; + str->buf[str->cursor] = '\0'; + + if (str->encoding & ASL_STRING_LEN) + { + char tmp[11]; + snprintf(tmp, sizeof(tmp), "%10lu", str->cursor - 10); + memcpy(str->buf, tmp, 10); + } + + return str; +} + +asl_string_t * +asl_string_append_no_encoding(asl_string_t *str, const char *app) +{ + size_t len, applen; + + if (str == NULL) return NULL; + if (app == NULL) return str; + + applen = strlen(app); + len = applen; + if (str->bufsize == 0) len++; + + if (_asl_string_grow(str, len) < 0) return str; + + memcpy(str->buf + str->cursor, app, applen); + + str->cursor += applen; + str->buf[str->cursor] = '\0'; + + if (str->encoding & ASL_STRING_LEN) + { + char tmp[11]; + snprintf(tmp, sizeof(tmp), "%10lu", str->cursor - 10); + memcpy(str->buf, tmp, 10); + } + + return str; +} + +static asl_string_t * +asl_string_append_internal(asl_string_t *str, const char *app, int encode_space) +{ + uint8_t x; + const char *p; + + if (str == NULL) return NULL; + if (app == NULL) return str; + + switch (str->encoding & ASL_ENCODE_MASK) + { + case ASL_ENCODE_NONE: + { + return asl_string_append_no_encoding(str, app); + } + case ASL_ENCODE_SAFE: + { + /* minor encoding to reduce the likelyhood of spoof attacks */ + const char *p; + + for (p = app; *p != '\0'; p++) + { + if ((*p == 10) || (*p == 13)) + { + asl_string_append_no_encoding(str, "\n\t"); + } + else if (*p == 8) + { + asl_string_append_no_encoding(str, "^H"); + } + else + { + asl_string_append_char_no_encoding(str, *p); + } + } + + return str; + } + case ASL_ENCODE_ASL: + { + for (p = app; *p != '\0'; p++) + { + int meta = 0; + + x = *p; + + /* Meta chars get \M prefix */ + if (x >= 128) + { + /* except meta-space, which is \240 */ + if (x == 160) + { + asl_string_append_no_encoding(str, "\\240"); + continue; + } + + asl_string_append_no_encoding(str, "\\M"); + x &= 0x7f; + meta = 1; + } + + /* space is either ' ' or \s */ + if (x == 32) + { + if (encode_space == 0) + { + asl_string_append_char_no_encoding(str, ' '); + continue; + } + + asl_string_append_no_encoding(str, "\\s"); + continue; + } + + /* \ is escaped */ + if ((meta == 0) && (x == 92)) + { + asl_string_append_no_encoding(str, "\\\\"); + continue; + } + + /* [ and ] are escaped in ASL encoding */ + if ((str->encoding & ASL_ENCODE_ASL) && (meta == 0) && ((*p == 91) || (*p == 93))) + { + if (*p == '[') asl_string_append_no_encoding(str, "\\["); + else asl_string_append_no_encoding(str, "\\]"); + continue; + } + + /* DEL is \^? */ + if (x == 127) + { + if (meta == 0) + { + asl_string_append_char_no_encoding(str, '\\'); + } + + asl_string_append_no_encoding(str, "^?"); + continue; + } + + /* 33-126 are printable (add a '-' prefix for meta) */ + if ((x >= 33) && (x <= 126)) + { + if (meta == 1) + { + asl_string_append_char_no_encoding(str, '-'); + } + + asl_string_append_char_no_encoding(str, x); + continue; + } + + /* non-meta BEL, BS, HT, NL, VT, NP, CR (7-13) are \a, \b, \t, \n, \v, \f, and \r */ + if ((meta == 0) && (x >= 7) && (x <= 13)) + { + asl_string_append_char_no_encoding(str, '\\'); + asl_string_append_char_no_encoding(str, cvis_7_13[x - 7]); + continue; + } + + /* 0 - 31 are ^@ - ^_ (non-meta get a leading \) */ + if (x <= 31) + { + if (meta == 0) + { + asl_string_append_char_no_encoding(str, '\\'); + } + + asl_string_append_char_no_encoding(str, '^'); + asl_string_append_char_no_encoding(str, 64 + x); + continue; + } + + asl_string_append_char_no_encoding(str, x); + } + + return str; + } + case ASL_ENCODE_XML: + { + for (p = app; *p != '\0'; p++) + { + x = *p; + + if (x == '&') + { + asl_string_append_no_encoding(str, "&"); + } + else if (x == '<') + { + asl_string_append_no_encoding(str, "<"); + } + else if (x == '>') + { + asl_string_append_no_encoding(str, ">"); + } + else if (x == '"') + { + asl_string_append_no_encoding(str, """); + } + else if (x == '\'') + { + asl_string_append_no_encoding(str, "'"); + } + else if (iscntrl(x)) + { + char tmp[8]; + snprintf(tmp, sizeof(tmp), "&#x%02hhx;", x); + asl_string_append_no_encoding(str, tmp); + } + else + { + asl_string_append_char_no_encoding(str, x); + } + } + } + default: + { + return str; + } + } + + return str; +} + +asl_string_t * +asl_string_append(asl_string_t *str, const char *app) +{ + return asl_string_append_internal(str, app, 0); +} + +asl_string_t * +asl_string_append_asl_key(asl_string_t *str, const char *app) +{ + return asl_string_append_internal(str, app, 1); +} + +asl_string_t * +asl_string_append_op(asl_string_t *str, uint32_t op) +{ + char opstr[8]; + uint32_t i; + + if (str == NULL) return NULL; + + if (op == ASL_QUERY_OP_NULL) + { + return asl_string_append_char_no_encoding(str, '.'); + } + + i = 0; + if (op & ASL_QUERY_OP_CASEFOLD) opstr[i++] = 'C'; + + if (op & ASL_QUERY_OP_REGEX) opstr[i++] = 'R'; + + if (op & ASL_QUERY_OP_NUMERIC) opstr[i++] = 'N'; + + if (op & ASL_QUERY_OP_PREFIX) + { + if (op & ASL_QUERY_OP_SUFFIX) opstr[i++] = 'S'; + else opstr[i++] = 'A'; + } + if (op & ASL_QUERY_OP_SUFFIX) opstr[i++] = 'Z'; + + switch (op & ASL_QUERY_OP_TRUE) + { + case ASL_QUERY_OP_EQUAL: + opstr[i++] = '='; + break; + case ASL_QUERY_OP_GREATER: + opstr[i++] = '>'; + break; + case ASL_QUERY_OP_GREATER_EQUAL: + opstr[i++] = '>'; + opstr[i++] = '='; + break; + case ASL_QUERY_OP_LESS: + opstr[i++] = '<'; + break; + case ASL_QUERY_OP_LESS_EQUAL: + opstr[i++] = '<'; + opstr[i++] = '='; + break; + case ASL_QUERY_OP_NOT_EQUAL: + opstr[i++] = '!'; + break; + case ASL_QUERY_OP_TRUE: + opstr[i++] = 'T'; + break; + default: + break; + } + + if (i == 0) + { + return asl_string_append_char_no_encoding(str, '.'); + } + + opstr[i] = '\0'; + return asl_string_append_no_encoding(str, opstr); +} + +asl_string_t * +asl_string_append_xml_tag(asl_string_t *str, const char *tag, const char *s) +{ + asl_string_append_no_encoding(str, "\t\t<"); + asl_string_append_no_encoding(str, tag); + asl_string_append_no_encoding(str, ">"); + asl_string_append_internal(str, s, 0); + asl_string_append_no_encoding(str, "\n"); + return str; +} diff --git a/libsystem_asl.tproj/src/asl_fd.c b/libsystem_asl.tproj/src/asl_fd.c new file mode 100644 index 0000000..af60ccf --- /dev/null +++ b/libsystem_asl.tproj/src/asl_fd.c @@ -0,0 +1,297 @@ +/* + * Copyright (c) 2011 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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, 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* asl.c */ +__private_extern__ void asl_client_release(asl_client_t *asl); +__private_extern__ asl_client_t *asl_client_retain(asl_client_t *asl); + +#define BUF_SIZE 512 + +static dispatch_queue_t redirect_serial_q; +static dispatch_group_t read_source_group; + +typedef struct { + int level; + asl_client_t *asl; + asl_msg_t *msg; + + /* Buffered reading */ + char *buf; + char *w; + + dispatch_source_t read_source; +} asl_redirect_t; + +static asl_redirect_t *redirect_descriptors = NULL; +static int n_redirect_descriptors = 0; + +/* Read from the FD until there is no more to read and redirect to ASL. + * Preconditions: + * 1: called from the appropriate serial queue for operating on + * redirect_descriptors + * 2: descriptor corresponds to a valid entry in redirect_descriptors + * + * Return values: + * If the pipe is closed, EOF is returned regardless of how many bytes + * were processed. If the pipe is still open, the number of read bytes + * is returned. + */ +static inline int _read_redirect(int descriptor, int flush) { + int total_read = 0; + int nbytes; + asl_redirect_t *aslr = &redirect_descriptors[descriptor]; + + while ((nbytes = read(descriptor, aslr->w, BUF_SIZE - (aslr->w - aslr->buf) - 1)) > 0) { + char *s, *p; + + /* Increment our returned number read */ + total_read += nbytes; + + /* Increment our write location */ + aslr->w += nbytes; + aslr->w[0] = '\0'; + + /* One line at a time */ + for (p = aslr->buf; p < aslr->w; p = s + 1) { + /* Find null or \n */ + for (s=p; *s && *s != '\n'; s++); + + if (*s == '\n') { + *s='\0'; + } + + if (s < aslr->w || aslr->buf == p) { + /* Either the first of multiple messages or one message which is larger than our buffer */ + asl_log((aslclient)aslr->asl, (aslmsg)aslr->msg, aslr->level, "%s", p); + } else { + /* We reached the end of the buffer, move this chunk to the start. */ + memmove(aslr->buf, p, BUF_SIZE - (p - aslr->buf)); + aslr->w = aslr->buf + (s - p); + break; + } + } + + if (p == aslr->w) { + /* Start writing at the beginning in the case where we cleared the buffer */ + aslr->w = aslr->buf; + } + } + + /* Flush if requested or we're at EOF */ + if (flush || nbytes == 0) { + if (aslr->w > aslr->buf) { + *aslr->w = '\0'; + asl_log((aslclient)aslr->asl, (aslmsg)aslr->msg, aslr->level, "%s", aslr->buf); + } + } + + if (nbytes == 0) + return EOF; + return total_read; +} + +static void read_from_source(void *_source) { + dispatch_source_t source = (dispatch_source_t)_source; + int descriptor = dispatch_source_get_handle(source); + if (_read_redirect(descriptor, 0) == EOF) { + dispatch_source_cancel(source); + } +} + +static void cancel_source(void *_source) { + dispatch_source_t source = (dispatch_source_t)_source; + int descriptor = dispatch_source_get_handle(source); + asl_redirect_t *aslr = &redirect_descriptors[descriptor]; + + /* Flush the buffer */ + _read_redirect(descriptor, 1); + + close(descriptor); + + asl_client_release(aslr->asl); + asl_msg_release(aslr->msg); + free(aslr->buf); + + memset(aslr, 0, sizeof(*aslr)); + dispatch_release(source); + dispatch_group_leave(read_source_group); +} + + +static void redirect_atexit(void) { + int i; + + /* stdout is linebuffered, so flush the buffer */ + if (redirect_descriptors[STDOUT_FILENO].buf) + fflush(stdout); + + /* Cancel all of our dispatch sources, so they flush to ASL */ + for (i=0; i < n_redirect_descriptors; i++) + if (redirect_descriptors[i].read_source) + dispatch_source_cancel(redirect_descriptors[i].read_source); + + /* Wait at least three seconds for our sources to flush to ASL */ + dispatch_group_wait(read_source_group, dispatch_time(DISPATCH_TIME_NOW, 3LL * NSEC_PER_SEC)); +} + +static void asl_descriptor_init(void *ctx __unused) +{ + assert((redirect_descriptors = calloc(16, sizeof(*redirect_descriptors))) != NULL); + n_redirect_descriptors = 16; + + redirect_serial_q = dispatch_queue_create("com.apple.asl-redirect", NULL); + assert(redirect_serial_q != NULL); + + read_source_group = dispatch_group_create(); + assert(read_source_group != NULL); + + atexit(redirect_atexit); +} + +static int asl_log_from_descriptor(aslclient ac, aslmsg am, int level, int descriptor) { + int err __block = 0; + static dispatch_once_t once_control; + dispatch_once_f(&once_control, NULL, asl_descriptor_init); + asl_client_t *asl = (asl_client_t *)ac; + asl_msg_t *msg = (asl_msg_t *)am; + + if (descriptor < 0) + return EBADF; + + if (msg != NULL) { + msg = asl_msg_copy(msg); + if (msg == NULL) + return ENOMEM; + } + + dispatch_sync(redirect_serial_q, ^{ + dispatch_source_t read_source; + + /* Reallocate if we need more space */ + if (descriptor >= n_redirect_descriptors) { + size_t new_n = 1 << (fls(descriptor) + 1); + asl_redirect_t *new_array = realloc(redirect_descriptors, new_n * sizeof(*redirect_descriptors)); + if (!new_array) { + err = errno; + return; + } + redirect_descriptors = new_array; + memset(redirect_descriptors + n_redirect_descriptors, 0, (new_n - n_redirect_descriptors) * sizeof(*redirect_descriptors)); + n_redirect_descriptors = new_n; + } + + /* If we're already listening on it, return error. */ + if (redirect_descriptors[descriptor].buf != NULL) { + err = EBADF; + return; + } + + /* Initialize our buffer */ + redirect_descriptors[descriptor].buf = (char *)malloc(BUF_SIZE); + if (redirect_descriptors[descriptor].buf == NULL) { + err = errno; + return; + } + redirect_descriptors[descriptor].w = redirect_descriptors[descriptor].buf; + + /* Store our ASL settings */ + redirect_descriptors[descriptor].level = level; + redirect_descriptors[descriptor].asl = asl_client_retain(asl); + redirect_descriptors[descriptor].msg = msg; + + /* Don't block on reads from this descriptor */ + (void)fcntl(descriptor, F_SETFL, O_NONBLOCK); + + /* Start listening */ + read_source = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, descriptor, 0, redirect_serial_q); + redirect_descriptors[descriptor].read_source = read_source; + dispatch_set_context(read_source, read_source); + dispatch_source_set_event_handler_f(read_source, read_from_source); + dispatch_source_set_cancel_handler_f(read_source, cancel_source); + dispatch_group_enter(read_source_group); + dispatch_resume(read_source); + }); + + if (err) { + asl_msg_release(msg); + } + + return err; +} + +int asl_log_descriptor(aslclient ac, aslmsg am, int level, int descriptor, uint32_t fd_type) { + int pipepair[2]; + int retval; + int oerrno = errno; + + if (fd_type == ASL_LOG_DESCRIPTOR_READ) + return asl_log_from_descriptor(ac, am, level, descriptor); + + assert(fd_type == ASL_LOG_DESCRIPTOR_WRITE); + + /* Create pipe */ + if (pipe(pipepair) == -1) { + retval = errno; + errno = oerrno; + return retval; + } + + /* Close the read descriptor but not the write descriptor on exec */ + if (fcntl(pipepair[0], F_SETFD, FD_CLOEXEC) == -1) { + retval = errno; + errno = oerrno; + return retval; + } + + /* Replace the existing descriptor */ + if (dup2(pipepair[1], descriptor) == -1) { + close(pipepair[0]); + close(pipepair[1]); + retval = errno; + errno = oerrno; + return retval; + } + + /* If we capture STDOUT_FILENO, make sure we linebuffer stdout */ + if (descriptor == STDOUT_FILENO) + setlinebuf(stdout); + + /* Close the duplicate descriptors since they've been reassigned */ + close(pipepair[1]); + + /* Hand off the read end of our pipe to asl_log_descriptor */ + return asl_log_from_descriptor(ac, am, level, pipepair[0]); +} diff --git a/libsystem_asl.tproj/src/asl_file.c b/libsystem_asl.tproj/src/asl_file.c new file mode 100644 index 0000000..bf43c7b --- /dev/null +++ b/libsystem_asl.tproj/src/asl_file.c @@ -0,0 +1,2167 @@ +/* + * Copyright (c) 2007-2011 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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, 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern time_t asl_parse_time(const char *str); +extern int asl_msg_cmp(aslmsg a, aslmsg b); + +#define forever for(;;) +#define MILLION 1000000 + +/* + * MSG and STR records have (at least) a type (uint16_t) and a length (uint32_t) + * type and level are both 16 bit fields so that alignment isn't a pain. + */ +#define RECORD_COMMON_LEN 6 +#define RECORD_TYPE_LEN 2 +#define BUFFER_OFFSET_KVCOUNT 56 + +#define SCRATCH_BUFFER_SIZE (MSG_RECORD_FIXED_LENGTH + (20 * sizeof(uint64_t))) + +typedef struct +{ + uint64_t next; + uint64_t mid; + uint64_t time; + uint32_t nano; + uint16_t level; + uint16_t flags; + uint32_t pid; + uint32_t uid; + uint32_t gid; + uint32_t ruid; + uint32_t rgid; + uint32_t refpid; + uint32_t kvcount; + uint64_t host; + uint64_t sender; + uint64_t facility; + uint64_t message; + uint64_t refproc; + uint64_t session; + uint64_t prev; +} file_record_t; + +typedef struct +{ + asl_file_list_t *list; + int dir; +} asl_file_match_token_t; + +static uint16_t +_asl_get_16(char *h) +{ + uint16_t x; + + memcpy(&x, h, 2); + return ntohs(x); +} + +static void +_asl_put_16(uint16_t i, char *h) +{ + uint16_t x; + + x = htons(i); + memcpy(h, &x, 2); +} + +static uint32_t +_asl_get_32(char *h) +{ + uint32_t x; + + memcpy(&x, h, 4); + return ntohl(x); +} + +static void +_asl_put_32(uint32_t i, char *h) +{ + uint32_t x; + + x = htonl(i); + memcpy(h, &x, 4); +} + +static uint64_t +_asl_get_64(char *h) +{ + uint64_t x; + + memcpy(&x, h, 8); + return asl_core_ntohq(x); +} + +static void +_asl_put_64(uint64_t i, char *h) +{ + uint64_t x; + + x = asl_core_htonq(i); + memcpy(h, &x, 8); +} + +static uint32_t +asl_file_read_uint32(asl_file_t *s, off_t off, uint32_t *out) +{ + uint32_t status, val; + + if (s == NULL) return ASL_STATUS_INVALID_STORE; + if (s->store == NULL) return ASL_STATUS_INVALID_STORE; + if ((off + sizeof(uint32_t)) > s->file_size) return ASL_STATUS_READ_FAILED; + + status = fseeko(s->store, off, SEEK_SET); + if (status != 0) return ASL_STATUS_READ_FAILED; + + val = 0; + + status = fread(&val, sizeof(uint32_t), 1, s->store); + if (status != 1) return ASL_STATUS_READ_FAILED; + + if (out != NULL) *out = ntohl(val); + return ASL_STATUS_OK; +} + +static uint32_t +asl_file_read_uint64(asl_file_t *s, off_t off, uint64_t *out) +{ + uint32_t status; + uint64_t val; + + if (s == NULL) return ASL_STATUS_INVALID_STORE; + if (s->store == NULL) return ASL_STATUS_INVALID_STORE; + if ((off + sizeof(uint64_t)) > s->file_size) return ASL_STATUS_READ_FAILED; + + status = fseeko(s->store, off, SEEK_SET); + if (status != 0) return ASL_STATUS_READ_FAILED; + + val = 0; + + status = fread(&val, sizeof(uint64_t), 1, s->store); + if (status != 1) return ASL_STATUS_READ_FAILED; + + if (out != NULL) *out = asl_core_ntohq(val); + return ASL_STATUS_OK; +} + +uint32_t +asl_file_close(asl_file_t *s) +{ + file_string_t *x; + + if (s == NULL) return ASL_STATUS_OK; + + if (s->version == 1) + { + return asl_legacy1_close((asl_legacy1_t *)s->legacy); + } + + while (s->string_list != NULL) + { + x = s->string_list->next; + free(s->string_list); + s->string_list = x; + } + + if (s->store != NULL) fclose(s->store); + if (s->scratch != NULL) free(s->scratch); + + memset(s, 0, sizeof(asl_file_t)); + free(s); + + return ASL_STATUS_OK; +} + +__private_extern__ uint32_t +asl_file_open_write_fd(int fd, asl_file_t **s) +{ + time_t now; + int status; + char buf[DB_HEADER_LEN]; + asl_file_t *out; + + if (fd < 0) return ASL_STATUS_FAILED; + if (s == NULL) return ASL_STATUS_FAILED; + + out = (asl_file_t *)calloc(1, sizeof(asl_file_t)); + if (out == NULL) return ASL_STATUS_NO_MEMORY; + + out->store = fdopen(fd, "w+"); + if (out->store == NULL) + { + free(out); + return ASL_STATUS_FAILED; + } + + memset(buf, 0, sizeof(buf)); + memcpy(buf, ASL_DB_COOKIE, ASL_DB_COOKIE_LEN); + + _asl_put_32(DB_VERSION, buf + DB_HEADER_VERS_OFFSET); + + now = time(NULL); + out->dob = now; + _asl_put_64(out->dob, buf + DB_HEADER_TIME_OFFSET); + + _asl_put_32(CACHE_SIZE, buf + DB_HEADER_CSIZE_OFFSET); + + status = fwrite(buf, sizeof(buf), 1, out->store); + if (status != 1) + { + fclose(out->store); + free(out); + return ASL_STATUS_FAILED; + } + + /* flush data */ + fflush(out->store); + + out->file_size = sizeof(buf); + + /* scratch buffer for file writes (we test for NULL before using it) */ + out->scratch = malloc(SCRATCH_BUFFER_SIZE); + + *s = out; + + return ASL_STATUS_OK; +} + +__private_extern__ int +asl_file_create(const char *path, uid_t uid, gid_t gid, mode_t mode) +{ +#if TARGET_OS_IPHONE + return open(path, O_RDWR | O_CREAT | O_EXCL, mode); +#else + acl_t acl; + uuid_t uuid; + acl_entry_t entry; + acl_permset_t perms; + int status; + int fd = -1; + + /* -1 means don't set ACL for uid or gid */ + if ((uid == -1) && (gid == -1)) + { + return open(path, O_RDWR | O_CREAT | O_EXCL, mode); + } + + acl = acl_init(1); + + if ((gid != 0) && (gid != -1)) + { + status = mbr_gid_to_uuid(gid, uuid); + if (status != 0) goto asl_file_create_return; + + status = acl_create_entry_np(&acl, &entry, ACL_FIRST_ENTRY); + if (status != 0) goto asl_file_create_return; + + status = acl_set_tag_type(entry, ACL_EXTENDED_ALLOW); + if (status != 0) goto asl_file_create_return; + + status = acl_set_qualifier(entry, &uuid); + if (status != 0) goto asl_file_create_return; + + status = acl_get_permset(entry, &perms); + if (status != 0) goto asl_file_create_return; + + status = acl_add_perm(perms, ACL_READ_DATA); + if (status != 0) goto asl_file_create_return; + } + + if ((uid != 0) && (uid != -1)) + { + status = mbr_uid_to_uuid(uid, uuid); + if (status != 0) goto asl_file_create_return; + + status = acl_create_entry_np(&acl, &entry, ACL_FIRST_ENTRY); + if (status != 0) goto asl_file_create_return; + + status = acl_set_tag_type(entry, ACL_EXTENDED_ALLOW); + if (status != 0) goto asl_file_create_return; + + status = acl_set_qualifier(entry, &uuid); + if (status != 0) goto asl_file_create_return; + + status = acl_get_permset(entry, &perms); + if (status != 0) goto asl_file_create_return; + + status = acl_add_perm(perms, ACL_READ_DATA); + if (status != 0) goto asl_file_create_return; + } + + fd = open(path, O_RDWR | O_CREAT | O_EXCL, mode); + if (fd < 0) goto asl_file_create_return; + + status = acl_set_fd(fd, acl); + if (status != 0) + { + close(fd); + fd = -1; + unlink(path); + } + +asl_file_create_return: + + acl_free(acl); + return fd; +#endif +} + +uint32_t +asl_file_open_write(const char *path, mode_t mode, uid_t uid, gid_t gid, asl_file_t **s) +{ + int i, status, fd; + struct stat sb; + char buf[DB_HEADER_LEN]; + asl_file_t *out; + uint32_t aslstatus, vers, last_len; + off_t off; + + memset(&sb, 0, sizeof(struct stat)); + + status = stat(path, &sb); + if (status == 0) + { + /* must be a plain file */ + if (!S_ISREG(sb.st_mode)) return ASL_STATUS_INVALID_STORE; + + if (sb.st_size == 0) + { + fd = open(path, O_RDWR | O_EXCL, mode); + if (fd < 0) return ASL_STATUS_FAILED; + + return asl_file_open_write_fd(fd, s); + } + else + { + /* XXX Check that mode, uid, and gid are correct */ + out = (asl_file_t *)calloc(1, sizeof(asl_file_t)); + if (out == NULL) return ASL_STATUS_NO_MEMORY; + + out->store = fopen(path, "r+"); + if (out->store == NULL) + { + free(out); + return ASL_STATUS_FAILED; + } + + i = fread(buf, DB_HEADER_LEN, 1, out->store); + if (i < 1) + { + asl_file_close(out); + return ASL_STATUS_READ_FAILED; + } + + /* check cookie */ + if (strncmp(buf, ASL_DB_COOKIE, ASL_DB_COOKIE_LEN)) + { + asl_file_close(out); + return ASL_STATUS_INVALID_STORE; + } + + /* check version */ + vers = _asl_get_32(buf + DB_HEADER_VERS_OFFSET); + if (vers != DB_VERSION) + { + asl_file_close(out); + return ASL_STATUS_INVALID_STORE; + } + + out->dob = _asl_get_64(buf + DB_HEADER_TIME_OFFSET); + out->first = _asl_get_64(buf + DB_HEADER_FIRST_OFFSET); + out->last = _asl_get_64(buf + DB_HEADER_LAST_OFFSET); + out->file_size = (size_t)sb.st_size; + + /* + * Detect bogus last pointer and check for odd-sized files. + * Setting out->last to zero forces asl_file_read_set_position to + * follow the linked list of records in the file to the last record. + * It's slower, but it's better at preventing crashes in corrupt files. + */ + + /* records are at least MSG_RECORD_FIXED_LENGTH bytes */ + if ((out->last + MSG_RECORD_FIXED_LENGTH) > out->file_size) + { + out->last = 0; + } + else + { + /* read last record length and make sure the file is at least that large */ + off = out->last + RECORD_TYPE_LEN; + status = asl_file_read_uint32(out, off, &last_len); + if (status != ASL_STATUS_OK) + { + asl_file_close(out); + return status; + } + + if ((out->last + last_len) > out->file_size) out->last = 0; + } + + aslstatus = asl_file_read_set_position(out, ASL_FILE_POSITION_LAST); + if (aslstatus != ASL_STATUS_OK) + { + asl_file_close(out); + return aslstatus; + } + + out->prev = out->cursor; + status = fseeko(out->store, 0, SEEK_END); + if (status != 0) + { + asl_file_close(out); + return ASL_STATUS_READ_FAILED; + } + + out->file_size = (size_t)ftello(out->store); + + /* scratch buffer for file writes (we test for NULL before using it) */ + out->scratch = malloc(SCRATCH_BUFFER_SIZE); + + *s = out; + + return ASL_STATUS_OK; + } + } + else if (errno != ENOENT) + { + /* unexpected status */ + return ASL_STATUS_FAILED; + } + + /* the file does not exist */ + fd = asl_file_create(path, uid, gid, mode); + if (fd < 0) return ASL_STATUS_FAILED; + + aslstatus = asl_file_open_write_fd(fd, s); + if (aslstatus != ASL_STATUS_OK) unlink(path); + + return aslstatus; +} + +uint32_t +asl_file_compact(asl_file_t *s, const char *path, mode_t mode, uid_t uid, gid_t gid) +{ + asl_file_t *new; + struct stat sb; + aslmsg m; + uint64_t xid; + uint32_t status; + + if (s == NULL) return ASL_STATUS_INVALID_STORE; + if (path == NULL) return ASL_STATUS_INVALID_ARG; + + if (s->version == 1) return ASL_STATUS_FAILED; + + memset(&sb, 0, sizeof(struct stat)); + + if (stat(path, &sb) == 0) return ASL_STATUS_FAILED; + if (errno != ENOENT) return ASL_STATUS_FAILED; + + status = asl_file_read_set_position(s, ASL_FILE_POSITION_FIRST); + if (status != ASL_STATUS_OK) return status; + + new = NULL; + status = asl_file_open_write(path, mode, uid, gid, &new); + if (status != ASL_STATUS_OK) return status; + new->flags = ASL_FILE_FLAG_UNLIMITED_CACHE | ASL_FILE_FLAG_PRESERVE_MSG_ID; + + while ((status == ASL_STATUS_OK) && (s->cursor != 0)) + { + m = NULL; + status = asl_file_fetch_next(s, &m); + if (status != ASL_STATUS_OK) break; + + xid = 0; + status = asl_file_save(new, m, &xid); + asl_free(m); + } + + asl_file_close(new); + return status; +} + +static uint32_t +asl_file_string_encode(asl_file_t *s, const char *str, uint64_t *out) +{ + uint32_t i, hash, len, x32; + file_string_t *sp, *sx, *sl; + uint64_t x64; + uint8_t inls; + uint16_t type; + off_t off; + char *p; + + if (s == NULL) return ASL_STATUS_INVALID_STORE; + if (str == NULL) return ASL_STATUS_INVALID_ARG; + + len = strlen(str); + + /* inline strings */ + if (len < 8) + { + /* inline string */ + inls = len; + inls |= 0x80; + + x64 = 0; + p = (char *)&x64; + memcpy(p, &inls, 1); + memcpy(p + 1, str, len); + *out = asl_core_ntohq(x64); + return ASL_STATUS_OK; + } + + /* check the cache */ + hash = asl_core_string_hash(str, len); + + sp = NULL; + for (sx = s->string_list; sx != NULL; sx = sx->next) + { + if ((hash == sx->hash) && (!strcmp(str, sx->str))) + { + /* Move this string to the head of the list */ + if (sp != NULL) + { + sl = s->string_list; + sp->next = sx->next; + sx->next = sl; + s->string_list = sx; + } + + *out = sx->where; + return ASL_STATUS_OK; + } + + sp = sx; + } + + off = ftello(s->store); + + /* Type */ + type = htons(ASL_FILE_TYPE_STR); + i = fwrite(&type, sizeof(uint16_t), 1, s->store); + if (i != 1) return ASL_STATUS_WRITE_FAILED; + + /* Length (includes trailing nul) */ + x32 = htonl(len + 1); + i = fwrite(&x32, sizeof(uint32_t), 1, s->store); + if (i != 1) return ASL_STATUS_WRITE_FAILED; + + /* String data (nul terminated) */ + i = fwrite(str, len + 1, 1, s->store); + if (i != 1) return ASL_STATUS_WRITE_FAILED; + + /* flush data */ + fflush(s->store); + + /* create file_string_t and insert into the cache */ + sx = (file_string_t *)calloc(1, offsetof(file_string_t, str) + len + 1); + if (sx == NULL) return ASL_STATUS_NO_MEMORY; + + sx->where = off; + sx->hash = hash; + sx->next = s->string_list; + memcpy(sx->str, str, len); + + s->string_list = sx; + + if (((s->flags & ASL_FILE_FLAG_UNLIMITED_CACHE) == 0) && (s->string_count == CACHE_SIZE)) + { + /* drop last (lru) string from cache */ + sp = s->string_list; + sx = sp->next; + + /* NB CACHE_SIZE must be > 1 */ + while (sx->next != NULL) + { + sp = sx; + sx = sx->next; + } + + sp->next = NULL; + free(sx); + } + else + { + s->string_count++; + } + + *out = off; + return ASL_STATUS_OK; +} + +/* + * Encode an aslmsg as a record structure. + * Creates and caches strings. + */ +uint32_t +asl_file_save(asl_file_t *s, aslmsg in, uint64_t *mid) +{ + char *buf, *p; + uint32_t i, len, x, status; + file_record_t r; + uint64_t k, v; + uint64_t *kvlist; + off_t off; + asl_msg_t *msg; + const char *key, *val; + + if (s == NULL) return ASL_STATUS_INVALID_STORE; + if (in == NULL) return ASL_STATUS_INVALID_MESSAGE; + + if (s->flags & ASL_FILE_FLAG_READ_ONLY) return ASL_STATUS_READ_ONLY; + + msg = (asl_msg_t *)in; + + memset(&r, 0, sizeof(file_record_t)); + + r.flags = 0; + r.level = ASL_LEVEL_DEBUG; + r.pid = -1; + r.uid = -2; + r.gid = -2; + r.ruid = -1; + r.rgid = -1; + r.time = 0; + r.nano = 0; + r.prev = s->prev; + kvlist = NULL; + + key = NULL; + val = NULL; + + for (x = asl_msg_fetch(msg, 0, &key, &val, NULL); x != IndexNull; x = asl_msg_fetch(msg, x, &key, &val, NULL)) + { + if (key == NULL) + { + continue; + } + else if (!strcmp(key, ASL_KEY_TIME)) + { + if (val != NULL) r.time = asl_parse_time(val); + } + else if (!strcmp(key, ASL_KEY_TIME_NSEC)) + { + if (val != NULL) r.nano = atoi(val); + } + else if (!strcmp(key, ASL_KEY_HOST)) + { + if (val != NULL) + { + status = asl_file_string_encode(s, val, &(r.host)); + if (status != ASL_STATUS_OK) + { + if (kvlist != NULL) free(kvlist); + return status; + } + } + } + else if (!strcmp(key, ASL_KEY_SENDER)) + { + if (val != NULL) + { + status = asl_file_string_encode(s, val, &(r.sender)); + if (status != ASL_STATUS_OK) + { + if (kvlist != NULL) free(kvlist); + return status; + } + } + } + else if (!strcmp(key, ASL_KEY_PID)) + { + if (val != NULL) r.pid = atoi(val); + } + else if (!strcmp(key, ASL_KEY_REF_PID)) + { + if (val != NULL) r.refpid = atoi(val); + } + else if (!strcmp(key, ASL_KEY_UID)) + { + if (val != NULL) r.uid = atoi(val); + } + else if (!strcmp(key, ASL_KEY_GID)) + { + if (val != NULL) r.gid = atoi(val); + } + else if (!strcmp(key, ASL_KEY_LEVEL)) + { + if (val != NULL) r.level = atoi(val); + } + else if (!strcmp(key, ASL_KEY_MSG)) + { + if (val != NULL) + { + status = asl_file_string_encode(s, val, &(r.message)); + if (status != ASL_STATUS_OK) + { + if (kvlist != NULL) free(kvlist); + return status; + } + } + } + else if (!strcmp(key, ASL_KEY_FACILITY)) + { + if (val != NULL) + { + status = asl_file_string_encode(s, val, &(r.facility)); + if (status != ASL_STATUS_OK) + { + if (kvlist != NULL) free(kvlist); + return status; + } + } + } + else if (!strcmp(key, ASL_KEY_REF_PROC)) + { + if (val != NULL) + { + status = asl_file_string_encode(s, val, &(r.refproc)); + if (status != ASL_STATUS_OK) + { + if (kvlist != NULL) free(kvlist); + return status; + } + } + } + else if (!strcmp(key, ASL_KEY_SESSION)) + { + if (val != NULL) + { + status = asl_file_string_encode(s, val, &(r.session)); + if (status != ASL_STATUS_OK) + { + if (kvlist != NULL) free(kvlist); + return status; + } + } + } + else if (!strcmp(key, ASL_KEY_READ_UID)) + { + if (((r.flags & ASL_MSG_FLAG_READ_UID_SET) == 0) && (val != NULL)) + { + r.ruid = atoi(val); + r.flags |= ASL_MSG_FLAG_READ_UID_SET; + } + } + else if (!strcmp(key, ASL_KEY_READ_GID)) + { + if (((r.flags & ASL_MSG_FLAG_READ_GID_SET) == 0) && (val != NULL)) + { + r.rgid = atoi(val); + r.flags |= ASL_MSG_FLAG_READ_GID_SET; + } + } + else if (!strcmp(key, ASL_KEY_MSG_ID)) + { + if (s->flags & ASL_FILE_FLAG_PRESERVE_MSG_ID) *mid = atoll(val); + } + else if (!strcmp(key, ASL_KEY_OPTION)) + { + /* ignore - we don't save ASLOption */ + } + else + { + status = asl_file_string_encode(s, key, &k); + if (status != ASL_STATUS_OK) + { + if (kvlist != NULL) free(kvlist); + return status; + } + + v = 0; + if (val != NULL) + { + status = asl_file_string_encode(s, val, &v); + if (status != ASL_STATUS_OK) + { + if (kvlist != NULL) free(kvlist); + return status; + } + } + + if (r.kvcount == 0) + { + kvlist = (uint64_t *)calloc(2, sizeof(uint64_t)); + } + else + { + kvlist = (uint64_t *)reallocf(kvlist, (r.kvcount + 2) * sizeof(uint64_t)); + } + + if (kvlist == NULL) + { + return ASL_STATUS_NO_MEMORY; + } + + kvlist[r.kvcount++] = k; + kvlist[r.kvcount++] = v; + } + } + + len = MSG_RECORD_FIXED_LENGTH + (r.kvcount * sizeof(uint64_t)); + buf = NULL; + + /* use the scratch buffer if it exists and is large enough */ + if ((s->scratch != NULL) && (len <= SCRATCH_BUFFER_SIZE)) + { + memset(s->scratch, 0, SCRATCH_BUFFER_SIZE); + buf = s->scratch; + } + else + { + buf = calloc(1, len); + } + + if (buf == NULL) return ASL_STATUS_NO_MEMORY; + + if (*mid != 0) + { + r.mid = *mid; + } + else + { + r.mid = asl_core_new_msg_id(0); + *mid = r.mid; + } + + p = buf; + + /* Type */ + _asl_put_16(ASL_FILE_TYPE_MSG, p); + p += sizeof(uint16_t); + + /* Length of message (excludes type and length fields) */ + _asl_put_32(len - RECORD_COMMON_LEN, p); + p += sizeof(uint32_t); + + /* Message data... */ + + _asl_put_64(r.next, p); + p += sizeof(uint64_t); + + _asl_put_64(r.mid, p); + p += sizeof(uint64_t); + + _asl_put_64(r.time, p); + p += sizeof(uint64_t); + + _asl_put_32(r.nano, p); + p += sizeof(uint32_t); + + _asl_put_16(r.level, p); + p += sizeof(uint16_t); + + _asl_put_16(r.flags, p); + p += sizeof(uint16_t); + + _asl_put_32(r.pid, p); + p += sizeof(uint32_t); + + _asl_put_32(r.uid, p); + p += sizeof(uint32_t); + + _asl_put_32(r.gid, p); + p += sizeof(uint32_t); + + _asl_put_32(r.ruid, p); + p += sizeof(uint32_t); + + _asl_put_32(r.rgid, p); + p += sizeof(uint32_t); + + _asl_put_32(r.refpid, p); + p += sizeof(uint32_t); + + _asl_put_32(r.kvcount, p); + p += sizeof(uint32_t); + + _asl_put_64(r.host, p); + p += sizeof(uint64_t); + + _asl_put_64(r.sender, p); + p += sizeof(uint64_t); + + _asl_put_64(r.facility, p); + p += sizeof(uint64_t); + + _asl_put_64(r.message, p); + p += sizeof(uint64_t); + + _asl_put_64(r.refproc, p); + p += sizeof(uint64_t); + + _asl_put_64(r.session, p); + p += sizeof(uint64_t); + + for (i = 0; i < r.kvcount; i++) + { + _asl_put_64(kvlist[i], p); + p += sizeof(uint64_t); + } + + _asl_put_64(r.prev, p); + p += sizeof(uint64_t); + + free(kvlist); + kvlist = NULL; + + /* write record at end of file */ + status = fseeko(s->store, 0, SEEK_END); + if (status != 0) return ASL_STATUS_WRITE_FAILED; + + s->last = (uint64_t)ftello(s->store); + + v = asl_core_htonq(s->last); + + status = fwrite(buf, len, 1, s->store); + fflush(s->store); + + /* free the buffer if it was allocated here */ + if (buf != s->scratch) free(buf); + + /* seek to "next" field of previous record, write last offset */ + off = s->prev + RECORD_COMMON_LEN; + if (s->prev == 0) off = DB_HEADER_FIRST_OFFSET; + + status = fseeko(s->store, off, SEEK_SET); + if (status != 0) return ASL_STATUS_WRITE_FAILED; + + status = fwrite(&v, sizeof(uint64_t), 1, s->store); + if (status != 1) return ASL_STATUS_WRITE_FAILED; + + /* seek to DB_HEADER_LAST_OFFSET, write last record offset */ + off = DB_HEADER_LAST_OFFSET; + + status = fseeko(s->store, off, SEEK_SET); + if (status != 0) return ASL_STATUS_WRITE_FAILED; + + status = fwrite(&v, sizeof(uint64_t), 1, s->store); + if (status != 1) return ASL_STATUS_WRITE_FAILED; + + /* return to the end of the store (this is expected by other routines) */ + status = fseeko(s->store, 0, SEEK_END); + if (status != 0) return ASL_STATUS_WRITE_FAILED; + + /* flush data */ + fflush(s->store); + + s->file_size = (size_t)ftello(s->store); + + s->prev = s->last; + + return ASL_STATUS_OK; +} + +static uint32_t +asl_file_fetch_object(asl_file_t *s, uint64_t where, char **out, uint32_t *outlen) +{ + char ils[9]; + char *p; + uint32_t len; + int status; + uint64_t x64; + uint8_t inls; + uint16_t type; + off_t off; + + *out = NULL; + *outlen = 0; + + if (s == NULL) return ASL_STATUS_INVALID_STORE; + if (out == NULL) return ASL_STATUS_INVALID_ARG; + if (where == 0) return ASL_STATUS_INVALID_ARG; + + inls = 0; + x64 = asl_core_htonq(where); + memcpy(&inls, &x64, 1); + if (inls & 0x80) + { + /* inline string */ + inls &= 0x0f; + if (inls > 7) return ASL_STATUS_INVALID_STORE; + + p = 1 + (char *)&x64; + memset(ils, 0, sizeof(ils)); + memcpy(ils, p, inls); + *out = strdup(ils); + if (*out == NULL) return ASL_STATUS_NO_MEMORY; + + *outlen = inls; + return ASL_STATUS_OK; + } + + off = where; + if ((off + sizeof(uint16_t) + sizeof(uint32_t)) > s->file_size) return ASL_STATUS_READ_FAILED; + + status = fseeko(s->store, off, SEEK_SET); + if (status != 0) return ASL_STATUS_READ_FAILED; + + /* Type */ + status = fread(&type, sizeof(uint16_t), 1, s->store); + if (status != 1) return ASL_STATUS_READ_FAILED; + off += sizeof(uint16_t); + + /* Length */ + len = 0; + status = fread(&len, sizeof(uint32_t), 1, s->store); + if (status != 1) return ASL_STATUS_READ_FAILED; + off += sizeof(uint32_t); + + len = ntohl(len); + if ((off + len) > s->file_size) return ASL_STATUS_READ_FAILED; + + *out = calloc(1, len); + if (*out == NULL) return ASL_STATUS_NO_MEMORY; + + status = fread(*out, len, 1, s->store); + if (status != 1) + { + free(*out); + return ASL_STATUS_READ_FAILED; + } + + *outlen = len; + return ASL_STATUS_OK; +} + +static uint16_t +asl_file_fetch_helper_16(asl_file_t *s, char **p, aslmsg m, const char *key) +{ + uint16_t out; + char str[256]; + + out = _asl_get_16(*p); + *p += sizeof(uint16_t); + + if ((m == NULL) || (key == NULL)) return out; + + snprintf(str, sizeof(str), "%hu", out); + asl_set(m, key, str); + + return out; +} + +static uint32_t +asl_file_fetch_helper_32(asl_file_t *s, char **p, aslmsg m, const char *key, int ignore, uint32_t ignoreval) +{ + uint32_t out, doit; + char str[256]; + + out = _asl_get_32(*p); + *p += sizeof(uint32_t); + + if ((m == NULL) || (key == NULL)) return out; + + doit = 1; + if ((ignore != 0) && (out == ignoreval)) doit = 0; + if (doit != 0) + { + snprintf(str, sizeof(str), "%u", out); + asl_set(m, key, str); + } + + return out; +} + +static uint64_t +asl_file_fetch_helper_64(asl_file_t *s, char **p, aslmsg m, const char *key) +{ + uint64_t out; + char str[256]; + + out = _asl_get_64(*p); + *p += sizeof(uint64_t); + + if ((m == NULL) || (key == NULL)) return out; + + snprintf(str, sizeof(str), "%llu", out); + asl_set(m, key, str); + + return out; +} + +static uint64_t +asl_file_fetch_helper_str(asl_file_t *s, char **p, aslmsg m, const char *key, uint32_t *err) +{ + uint64_t out; + char *val; + uint32_t status, len; + + out = _asl_get_64(*p); + *p += sizeof(uint64_t); + + val = NULL; + len = 0; + status = ASL_STATUS_OK; + if (out != 0) status = asl_file_fetch_object(s, out, &val, &len); + + if (err != NULL) *err = status; + if ((status == ASL_STATUS_OK) && (val != NULL)) + { + asl_set(m, key, val); + free(val); + } + + return out; +} + +static uint32_t +asl_file_fetch_pos(asl_file_t *s, uint64_t where, int dir, aslmsg *msg) +{ + char *buf, *p, *k, *v; + file_record_t r; + uint32_t i, status, len, buflen, kvn; + uint64_t x64, kv; + aslmsg out; + off_t off; + + if (s == NULL) return ASL_STATUS_INVALID_STORE; + if (msg == NULL) return ASL_STATUS_INVALID_ARG; + if ((s->flags & ASL_FILE_FLAG_READ_ONLY) == 0) return ASL_STATUS_WRITE_ONLY; + + buf = NULL; + buflen = 0; + status = asl_file_fetch_object(s, where, &buf, &buflen); + if ((status != ASL_STATUS_OK) || (buf == NULL)) + { + s->cursor = 0; + s->cursor_xid = 0; + return status; + } + + /* check buffer size */ + kvn = _asl_get_32(buf + BUFFER_OFFSET_KVCOUNT); + if (buflen < (MSG_RECORD_FIXED_LENGTH - RECORD_COMMON_LEN + (kvn * sizeof(uint64_t)))) + { + free(buf); + s->cursor = 0; + s->cursor_xid = 0; + return ASL_STATUS_READ_FAILED; + } + + out = asl_new(ASL_TYPE_MSG); + if (out == NULL) return ASL_STATUS_NO_MEMORY; + + memset(&r, 0, sizeof(file_record_t)); + p = buf; + + r.next = asl_file_fetch_helper_64(s, &p, NULL, NULL); + r.mid = asl_file_fetch_helper_64(s, &p, out, ASL_KEY_MSG_ID); + r.time = asl_file_fetch_helper_64(s, &p, out, ASL_KEY_TIME); + r.nano = asl_file_fetch_helper_32(s, &p, out, ASL_KEY_TIME_NSEC, 0, 0); + r.level = asl_file_fetch_helper_16(s, &p, out, ASL_KEY_LEVEL); + r.flags = asl_file_fetch_helper_16(s, &p, NULL, NULL); + r.pid = asl_file_fetch_helper_32(s, &p, out, ASL_KEY_PID, 0, 0); + r.uid = asl_file_fetch_helper_32(s, &p, out, ASL_KEY_UID, 1, (uint32_t)-1); + r.gid = asl_file_fetch_helper_32(s, &p, out, ASL_KEY_GID, 1, (uint32_t)-1); + r.ruid = asl_file_fetch_helper_32(s, &p, out, ASL_KEY_READ_UID, 1, (uint32_t)-1); + r.rgid = asl_file_fetch_helper_32(s, &p, out, ASL_KEY_READ_GID, 1, (uint32_t)-1); + r.refpid = asl_file_fetch_helper_32(s, &p, out, ASL_KEY_REF_PID, 1, 0); + r.kvcount = asl_file_fetch_helper_32(s, &p, NULL, NULL, 0, 0); + + status = ASL_STATUS_OK; + r.host = asl_file_fetch_helper_str(s, &p, out, ASL_KEY_HOST, &status); /* 68 */ + if (status == ASL_STATUS_OK) r.sender = asl_file_fetch_helper_str(s, &p, out, ASL_KEY_SENDER, &status); /* 76 */ + if (status == ASL_STATUS_OK) r.facility = asl_file_fetch_helper_str(s, &p, out, ASL_KEY_FACILITY, &status); /* 84 */ + if (status == ASL_STATUS_OK) r.message = asl_file_fetch_helper_str(s, &p, out, ASL_KEY_MSG, &status); /* 92 */ + if (status == ASL_STATUS_OK) r.refproc = asl_file_fetch_helper_str(s, &p, out, ASL_KEY_REF_PROC, &status); /* 100 */ + if (status == ASL_STATUS_OK) r.session = asl_file_fetch_helper_str(s, &p, out, ASL_KEY_SESSION, &status); /* 108 */ + + if (status != ASL_STATUS_OK) + { + asl_free(out); + free(buf); + s->cursor = 0; + s->cursor_xid = 0; + return status; + } + + kvn = r.kvcount / 2; + + for (i = 0; i < kvn; i++) + { + kv = _asl_get_64(p); + p += sizeof(uint64_t); + k = NULL; + len = 0; + status = asl_file_fetch_object(s, kv, &k, &len); + if (status != ASL_STATUS_OK) + { + asl_free(out); + free(buf); + s->cursor = 0; + s->cursor_xid = 0; + return status; + } + + kv = _asl_get_64(p); + p += sizeof(uint64_t); + v = NULL; + len = 0; + + if (kv != 0) + { + status = asl_file_fetch_object(s, kv, &v, &len); + if (status != ASL_STATUS_OK) + { + asl_free(out); + free(buf); + s->cursor = 0; + s->cursor_xid = 0; + return status; + } + } + + if ((status == ASL_STATUS_OK) && (k != NULL)) + { + asl_set(out, k, v); + if (v != NULL) free(v); + free(k); + } + } + + r.prev = asl_file_fetch_helper_64(s, &p, NULL, NULL); /* 116 */ + + free(buf); + + if (dir >= 0) + { + if ((r.next != 0) && (r.next <= s->cursor)) + { + /* + * Next offset goes backwards or loops. + * The database is corrupt, but we allow this call to fail + * quietly so that the current record fetch succeeds. + */ + s->cursor = 0; + s->cursor_xid = 0; + return ASL_STATUS_OK; + } + + s->cursor = r.next; + } + else + { + if ((r.prev != 0) && (r.prev >= s->cursor)) + { + /* + * Prev offset goes forward or loops. + * The database is corrupt, but we allow this call to fail + * quietly so that the current record fetch succeeds. + */ + s->cursor = 0; + s->cursor_xid = 0; + return ASL_STATUS_OK; + } + + s->cursor = r.prev; + } + + s->cursor_xid = 0; + + if (s->cursor != 0) + { + off = s->cursor + RECORD_COMMON_LEN + sizeof(uint64_t); + if (off > s->file_size) + { + s->cursor = 0; + s->cursor_xid = 0; + /* + * Next record offset is past the end of the file. + * This is an error, but we allow it to fail quietly + * so that the current record fetch succeeds. + */ + *msg = out; + return ASL_STATUS_OK; + } + + status = fseeko(s->store, off, SEEK_SET); + if (status != 0) + { + asl_free(out); + s->cursor = 0; + s->cursor_xid = 0; + return ASL_STATUS_READ_FAILED; + } + + status = fread(&x64, sizeof(uint64_t), 1, s->store); + if (status != 1) + { + asl_free(out); + s->cursor = 0; + s->cursor_xid = 0; + return ASL_STATUS_READ_FAILED; + } + + s->cursor_xid = asl_core_ntohq(x64); + } + + *msg = out; + return ASL_STATUS_OK; +} + +uint32_t +asl_file_open_read(const char *path, asl_file_t **s) +{ + asl_file_t *out; + FILE *f; + int i; + uint32_t status, vers, last_len; + char buf[DB_HEADER_LEN]; + off_t off; + asl_legacy1_t *legacy; + struct stat sb; + + memset(&sb, 0, sizeof(struct stat)); + if (stat(path, &sb) != 0) return ASL_STATUS_FAILED; + + f = fopen(path, "r"); + if (f == NULL) + { + if (errno == EACCES) return ASL_STATUS_ACCESS_DENIED; + return ASL_STATUS_FAILED; + } + + i = fread(buf, DB_HEADER_LEN, 1, f); + if (i < 1) + { + fclose(f); + return ASL_STATUS_INVALID_STORE; + } + + /* validate header */ + if (strncmp(buf, ASL_DB_COOKIE, ASL_DB_COOKIE_LEN)) + { + fclose(f); + return ASL_STATUS_INVALID_STORE; + } + + legacy = NULL; + + vers = _asl_get_32(buf + DB_HEADER_VERS_OFFSET); + if (vers == DB_VERSION_LEGACY_1) + { + fclose(f); + status = asl_legacy1_open(path, &legacy); + if (status != ASL_STATUS_OK) return status; + } + + out = (asl_file_t *)calloc(1, sizeof(asl_file_t)); + if (out == NULL) + { + fclose(f); + return ASL_STATUS_NO_MEMORY; + } + + out->store = f; + out->flags = ASL_FILE_FLAG_READ_ONLY; + out->version = vers; + + if (legacy != NULL) + { + out->flags |= ASL_FILE_FLAG_LEGACY_STORE; + out->legacy = (void *)legacy; + + *s = out; + return ASL_STATUS_OK; + } + + out->first = _asl_get_64(buf + DB_HEADER_FIRST_OFFSET); + out->last = _asl_get_64(buf + DB_HEADER_LAST_OFFSET); + out->file_size = (size_t)sb.st_size; + + /* + * Detect bogus last pointer and check for odd-sized files. + * Setting out->last to zero forces us to follow the linked + * list of records in the file to the last record. That's + * done in the set_position code. It's a bit slower, but it's + * better at preventing crashes in corrupt files. + */ + + /* records are at least MSG_RECORD_FIXED_LENGTH bytes */ + if ((out->last + MSG_RECORD_FIXED_LENGTH) > out->file_size) + { + out->last = 0; + } + else + { + /* read last record length and make sure the file is at least that large */ + off = out->last + RECORD_TYPE_LEN; + status = asl_file_read_uint32(out, off, &last_len); + if (status != ASL_STATUS_OK) + { + fclose(out->store); + free(out); + return status; + } + + if ((out->last + last_len) > out->file_size) out->last = 0; + } + + out->cursor = out->first; + if (out->cursor != 0) + { + off = out->cursor + RECORD_COMMON_LEN + sizeof(uint64_t); + status = asl_file_read_uint64(out, off, &(out->cursor_xid)); + if (status != ASL_STATUS_OK) + { + fclose(out->store); + free(out); + return status; + } + } + + *s = out; + return ASL_STATUS_OK; +} + +static uint32_t +asl_file_read_set_position_first(asl_file_t *s) +{ + uint32_t status; + off_t off; + + s->cursor = s->first; + s->cursor_xid = 0; + + if (s->cursor == 0) return ASL_STATUS_OK; + + /* read ID of the first record */ + off = s->cursor + RECORD_COMMON_LEN + sizeof(uint64_t); + status = asl_file_read_uint64(s, off, &(s->cursor_xid)); + return status; +} + +static uint32_t +asl_file_read_set_position_last(asl_file_t *s) +{ + uint64_t next; + uint32_t status; + off_t off; + + /* + * If the file has the offset of the last record, we just go there. + * The last record offset was added to improve performance, so it may + * or may not be there. If we don't have the last record offset, we + * just iterate down the record links to find the last one. + * + * Note that s->last may be zero if the file is empty. + */ + + if (s->last != 0) + { + s->cursor = s->last; + off = s->last + RECORD_COMMON_LEN + sizeof(uint64_t); + + /* read ID of the last record */ + status = asl_file_read_uint64(s, off, &(s->cursor_xid)); + return status; + } + + /* start at the first record and iterate */ + s->cursor = s->first; + s->cursor_xid = 0; + + forever + { + off = s->cursor + RECORD_COMMON_LEN; + next = 0; + + /* read next offset */ + status = asl_file_read_uint64(s, off, &next); + if (status != ASL_STATUS_OK) return status; + + /* detect bogus next pointer */ + if (((next + MSG_RECORD_FIXED_LENGTH) > s->file_size) || (next <= s->cursor)) next = 0; + + if (next == 0) + { + if (s->cursor == 0) return ASL_STATUS_OK; + + off = s->cursor + RECORD_COMMON_LEN + sizeof(uint64_t); + status = asl_file_read_uint64(s, off, &(s->cursor_xid)); + return ASL_STATUS_OK; + } + + s->cursor = next; + } +} + +uint32_t +asl_file_read_set_position(asl_file_t *s, uint32_t pos) +{ + uint64_t next; + uint32_t len, status; + off_t off; + + if (s == NULL) return ASL_STATUS_INVALID_STORE; + if (s->version == 1) return ASL_STATUS_FAILED; + + if (pos == ASL_FILE_POSITION_FIRST) return asl_file_read_set_position_first(s); + if (pos == ASL_FILE_POSITION_LAST) return asl_file_read_set_position_last(s); + + off = 0; + + if (pos == ASL_FILE_POSITION_PREVIOUS) + { + if (s->cursor == s->first) return ASL_STATUS_NO_RECORDS; + if (s->cursor == 0) return ASL_STATUS_NO_RECORDS; + + off = s->cursor + RECORD_TYPE_LEN; + status = asl_file_read_uint32(s, off, &len); + if (status != ASL_STATUS_OK) return status; + + /* set offset to read the "previous" field at the end of the record */ + off = s->cursor + RECORD_COMMON_LEN + len - sizeof(uint64_t); + } + else if (pos == ASL_FILE_POSITION_NEXT) + { + if (s->cursor == s->last) return ASL_STATUS_NO_RECORDS; + if (s->cursor == 0) return ASL_STATUS_NO_RECORDS; + + /* set offset to read the "next" field in the current record */ + off = s->cursor + RECORD_COMMON_LEN; + } + else return ASL_STATUS_INVALID_ARG; + + s->cursor_xid = 0; + + /* + * read offset of next / previous + */ + next = 0; + status = asl_file_read_uint64(s, off, &next); + if (status != ASL_STATUS_OK) return ASL_STATUS_READ_FAILED; + + /* detect bogus next pointer */ + if ((next + MSG_RECORD_FIXED_LENGTH) > s->file_size) next = 0; + else if ((pos == ASL_FILE_POSITION_PREVIOUS) && (next >= s->cursor)) next = 0; + else if ((pos == ASL_FILE_POSITION_NEXT) && (next <= s->cursor)) next = 0; + + s->cursor = next; + if (s->cursor == 0) return ASL_STATUS_NO_RECORDS; + + /* read ID of the record */ + off = s->cursor + RECORD_COMMON_LEN + sizeof(uint64_t); + status = asl_file_read_uint64(s, off, &(s->cursor_xid)); + return status; +} + +uint32_t +asl_file_fetch_next(asl_file_t *s, aslmsg *msg) +{ + if (s == NULL) return ASL_STATUS_INVALID_STORE; + if (s->version == 1) return ASL_STATUS_FAILED; + + return asl_file_fetch_pos(s, s->cursor, 1, msg); +} + +uint32_t +asl_file_fetch_previous(asl_file_t *s, aslmsg *msg) +{ + if (s == NULL) return ASL_STATUS_INVALID_STORE; + if (s->version == 1) return ASL_STATUS_FAILED; + + return asl_file_fetch_pos(s, s->cursor, -1, msg); +} + +uint32_t +asl_file_fetch(asl_file_t *s, uint64_t mid, aslmsg *msg) +{ + uint32_t status; + + if (s == NULL) return ASL_STATUS_INVALID_STORE; + if (msg == NULL) return ASL_STATUS_INVALID_ARG; + if ((s->flags & ASL_FILE_FLAG_READ_ONLY) == 0) return ASL_STATUS_WRITE_ONLY; + + if (s->version == 1) + { + return asl_legacy1_fetch((asl_legacy1_t *)s->legacy, mid, msg); + } + + if (s->cursor_xid == 0) + { + status = asl_file_read_set_position(s, ASL_FILE_POSITION_FIRST); + if (status != ASL_STATUS_OK) return status; + if (s->cursor_xid == 0) return ASL_STATUS_INVALID_ID; + } + + while (s->cursor_xid < mid) + { + status = asl_file_read_set_position(s, ASL_FILE_POSITION_NEXT); + if (status != ASL_STATUS_OK) return status; + if (s->cursor_xid > mid) return ASL_STATUS_INVALID_ID; + if (s->cursor_xid == 0) return ASL_STATUS_INVALID_ID; + } + + while (s->cursor_xid > mid) + { + status = asl_file_read_set_position(s, ASL_FILE_POSITION_PREVIOUS); + if (status != ASL_STATUS_OK) return status; + if (s->cursor_xid < mid) return ASL_STATUS_INVALID_ID; + if (s->cursor_xid == 0) return ASL_STATUS_INVALID_ID; + } + + if (s->cursor_xid != mid) return ASL_STATUS_INVALID_ID; + + return asl_file_fetch_pos(s, s->cursor, 1, msg); +} + +__private_extern__ uint64_t +asl_file_cursor(asl_file_t *s) +{ + if (s == NULL) return 0; + if ((s->flags & ASL_FILE_FLAG_READ_ONLY) == 0) return 0; + if (s->version == 1) return 0; + + return s->cursor_xid; +} + +__private_extern__ uint32_t +asl_file_match_start(asl_file_t *s, uint64_t start_id, int32_t direction) +{ + uint32_t status, d; + + if (s == NULL) return ASL_STATUS_INVALID_STORE; + if (s->version == 1) return ASL_STATUS_INVALID_STORE; + if ((s->flags & ASL_FILE_FLAG_READ_ONLY) == 0) return ASL_STATUS_WRITE_ONLY; + + d = ASL_FILE_POSITION_NEXT; + if (direction < 0) d = ASL_FILE_POSITION_PREVIOUS; + + /* + * find starting point + */ + status = ASL_STATUS_OK; + if (direction >= 0) status = asl_file_read_set_position(s, ASL_FILE_POSITION_FIRST); + else status = asl_file_read_set_position(s, ASL_FILE_POSITION_LAST); + if (status != ASL_STATUS_OK) return status; + + while ((status == ASL_STATUS_OK) && (((direction >= 0) && (s->cursor_xid < start_id)) || ((direction < 0) && (s->cursor_xid > start_id)))) + { + status = asl_file_read_set_position(s, d); + } + + return status; +} + +__private_extern__ uint32_t +asl_file_match_next(asl_file_t *s, aslresponse query, aslmsg *msg, uint64_t *last_id, int32_t direction) +{ + uint32_t status, d, i, do_match, did_match; + aslmsg m; + + if (s == NULL) return ASL_STATUS_INVALID_STORE; + if (msg == NULL) return ASL_STATUS_INVALID_ARG; + if (s->version == 1) return ASL_STATUS_INVALID_STORE; + if ((s->flags & ASL_FILE_FLAG_READ_ONLY) == 0) return ASL_STATUS_WRITE_ONLY; + if (s->cursor == 0) return ASL_STATUS_NO_RECORDS; + + *msg = NULL; + do_match = 1; + + d = ASL_FILE_POSITION_NEXT; + if (direction < 0) d = ASL_FILE_POSITION_PREVIOUS; + + if ((query == NULL) || ((query != NULL) && (query->count == 0))) do_match = 0; + + m = NULL; + + *last_id = s->cursor_xid; + + status = asl_file_fetch_pos(s, s->cursor, direction, &m); + if (status == ASL_STATUS_ACCESS_DENIED) return ASL_STATUS_MATCH_FAILED; + if ((status == ASL_STATUS_INVALID_ARG) && (s->cursor == 0)) return ASL_STATUS_NO_RECORDS; + if (status != ASL_STATUS_OK) return status; + + did_match = 1; + + if (do_match != 0) + { + did_match = 0; + + for (i = 0; (i < query->count) && (did_match == 0); i++) + { + did_match = asl_msg_cmp((aslmsg)(query->msg[i]), m); + } + } + + if (did_match != 0) + { + *msg = m; + return ASL_STATUS_OK; + } + + *msg = NULL; + asl_free(m); + return ASL_STATUS_MATCH_FAILED; +} + +uint32_t +asl_file_match(asl_file_t *s, aslresponse query, aslresponse *res, uint64_t *last_id, uint64_t start_id, uint32_t count, int32_t direction) +{ + uint32_t status, d, i, do_match, did_match, rescount; + aslmsg m; + + if (s == NULL) return ASL_STATUS_INVALID_STORE; + if (res == NULL) return ASL_STATUS_INVALID_ARG; + if ((s->flags & ASL_FILE_FLAG_READ_ONLY) == 0) return ASL_STATUS_WRITE_ONLY; + + if (s->version == 1) + { + return asl_legacy1_match((asl_legacy1_t *)s->legacy, query, res, last_id, start_id, count, direction); + } + + do_match = 1; + rescount = 0; + + d = ASL_FILE_POSITION_NEXT; + if (direction < 0) d = ASL_FILE_POSITION_PREVIOUS; + + if ((query == NULL) || ((query != NULL) && (query->count == 0))) do_match = 0; + + /* + * find starting point + */ + status = ASL_STATUS_OK; + if (direction >= 0) status = asl_file_read_set_position(s, ASL_FILE_POSITION_FIRST); + else status = asl_file_read_set_position(s, ASL_FILE_POSITION_LAST); + if (status != ASL_STATUS_OK) return status; + + while ((status == ASL_STATUS_OK) && (((direction >= 0) && (s->cursor_xid < start_id)) || ((direction < 0) && (s->cursor_xid > start_id)))) + { + status = asl_file_read_set_position(s, d); + } + + /* + * loop through records + */ + forever + { + m = NULL; + status = asl_file_fetch_pos(s, s->cursor, direction, &m); + if (status == ASL_STATUS_ACCESS_DENIED) continue; + if (status != ASL_STATUS_OK) break; + + *last_id = s->cursor_xid; + + did_match = 1; + + if (do_match != 0) + { + did_match = 0; + + for (i = 0; (i < query->count) && (did_match == 0); i++) + { + did_match = asl_msg_cmp((aslmsg)query->msg[i], m); + } + } + + if (did_match == 1) + { + /* append m to res */ + if (*res == NULL) + { + *res = (aslresponse)calloc(1, sizeof(aslresponse)); + if (*res == NULL) return ASL_STATUS_NO_MEMORY; + (*res)->msg = (asl_msg_t **)calloc(1, sizeof(aslmsg)); + if ((*res)->msg == NULL) + { + free(*res); + return ASL_STATUS_NO_MEMORY; + } + } + else + { + (*res)->msg = (asl_msg_t **)reallocf((*res)->msg, ((*res)->count + 1) * sizeof(aslmsg)); + if ((*res)->msg == NULL) + { + free(*res); + return ASL_STATUS_NO_MEMORY; + } + } + + (*res)->msg[(*res)->count] = (asl_msg_t *)m; + (*res)->count++; + + rescount++; + if ((count != 0) && (rescount >= count)) break; + } + else + { + asl_free(m); + } + } + + /* NOT REACHED */ + return ASL_STATUS_OK; +} + +size_t +asl_file_size(asl_file_t *s) +{ + if (s == NULL) return 0; + return s->file_size; +} + +uint64_t +asl_file_ctime(asl_file_t *s) +{ + if (s == NULL) return 0; + return s->dob; +} + +void +asl_file_list_close(asl_file_list_t *head) +{ + asl_file_list_t *next; + + while (head != NULL) + { + next = head->next; + asl_file_close(head->file); + free(head); + head = next; + } +} + +static void +asl_file_list_free(asl_file_list_t *head) +{ + asl_file_list_t *next; + + while (head != NULL) + { + next = head->next; + free(head); + head = next; + } +} + +static asl_file_list_t * +asl_file_list_insert(asl_file_list_t *list, asl_file_t *f, int32_t dir) +{ + asl_file_list_t *a, *b, *tmp; + + if (f == NULL) return list; + + tmp = (asl_file_list_t *)calloc(1, sizeof(asl_file_list_t)); + if (tmp == NULL) return NULL; + tmp->file = f; + + if (list == NULL) return tmp; + + a = list; + if (((dir < 0) && (f->cursor_xid > a->file->cursor_xid)) || ((dir >= 0) && (f->cursor_xid < a->file->cursor_xid))) + { + tmp->next = list; + return tmp; + } + + b = a->next; + while (b != NULL) + { + if (((dir < 0) && (f->cursor_xid > b->file->cursor_xid)) || ((dir >= 0) && (f->cursor_xid < b->file->cursor_xid))) + { + tmp->next = b; + a->next = tmp; + return list; + } + + a = b; + b = a->next; + } + + a->next = tmp; + return list; +} + +asl_file_list_t * +asl_file_list_add(asl_file_list_t *list, asl_file_t *f) +{ + asl_file_list_t *tmp; + + if (f == NULL) return list; + if (f->version == 1) return list; + + tmp = (asl_file_list_t *)calloc(1, sizeof(asl_file_list_t)); + if (tmp == NULL) return NULL; + tmp->file = f; + + tmp->next = list; + return tmp; +} + +void * +asl_file_list_match_start(asl_file_list_t *list, uint64_t start_id, int32_t direction) +{ + uint32_t status; + asl_file_list_t *n; + asl_file_match_token_t *out; + + if (list == NULL) return NULL; + + out = (asl_file_match_token_t *)calloc(1, sizeof(asl_file_match_token_t)); + if (out == NULL) return NULL; + + for (n = list; n != NULL; n = n->next) + { + /* init file for the search */ + status = asl_file_match_start(n->file, start_id, direction); + if (status != ASL_STATUS_OK) continue; + if (n->file->cursor_xid == 0) continue; + + out->list = asl_file_list_insert(out->list, n->file, direction); + } + + out->dir = direction; + return out; +} + +uint32_t +asl_file_list_match_next(void *token, aslresponse query, aslresponse *res, uint32_t count) +{ + uint32_t status, rescount; + asl_file_list_t *n; + aslmsg m; + asl_file_match_token_t *work; + uint64_t last_id; + + if (token == NULL) return ASL_STATUS_OK; + if (res == NULL) return ASL_STATUS_INVALID_ARG; + + work = (asl_file_match_token_t *)token; + + rescount = 0; + last_id = 0; + + while ((work->list != NULL) && ((rescount < count) || (count == 0))) + { + m = NULL; + status = asl_file_match_next(work->list->file, query, &m, &last_id, work->dir); + if (m != NULL) + { + if (*res == NULL) *res = (aslresponse)calloc(1, sizeof(asl_search_result_t)); + if (*res == NULL) + { + asl_file_list_free(work->list); + work->list = NULL; + return ASL_STATUS_NO_MEMORY; + } + + if ((*res)->msg == NULL) (*res)->msg = (asl_msg_t **)calloc(1, sizeof(aslmsg)); + else (*res)->msg = (asl_msg_t **)reallocf((*res)->msg, ((*res)->count + 1) * sizeof(aslmsg)); + if ((*res)->msg == NULL) + { + free(*res); + *res = NULL; + asl_file_list_free(work->list); + work->list = NULL; + return ASL_STATUS_NO_MEMORY; + } + + (*res)->msg[(*res)->count] = (asl_msg_t *)m; + (*res)->count++; + rescount++; + } + + if ((status != ASL_STATUS_OK) || (work->list->file->cursor_xid == 0)) + { + n = work->list->next; + free(work->list); + work->list = n; + } + + if (work->list != NULL) + { + n = work->list->next; + if (n != NULL) + { + if (((work->dir < 0) && (work->list->file->cursor_xid <= n->file->cursor_xid)) || ((work->dir >= 0) && (work->list->file->cursor_xid > n->file->cursor_xid))) + { + n = work->list; + work->list = work->list->next; + n->next = NULL; + work->list = asl_file_list_insert(work->list, n->file, work->dir); + free(n); + } + } + } + } + + return ASL_STATUS_OK; +} + +void +asl_file_list_match_end(void *token) +{ + asl_file_match_token_t *work; + + if (token == NULL) return; + + work = (asl_file_match_token_t *)token; + asl_file_list_free(work->list); + work->list = NULL; + + free(token); +} + +uint32_t +asl_file_list_match_timeout(asl_file_list_t *list, aslresponse query, aslresponse *res, uint64_t *last_id, uint64_t start_id, uint32_t count, int32_t direction, uint32_t usec) +{ + uint32_t status, rescount; + asl_file_list_t *files, *n; + aslmsg m; + struct timeval now, finish; + + if (list == NULL) return ASL_STATUS_INVALID_ARG; + if (res == NULL) return ASL_STATUS_INVALID_ARG; + if (last_id == NULL) return ASL_STATUS_INVALID_ARG; + + files = NULL; + + for (n = list; n != NULL; n = n->next) + { + /* init file for the search */ + status = asl_file_match_start(n->file, start_id, direction); + if (status != ASL_STATUS_OK) continue; + if (n->file->cursor_xid == 0) continue; + + files = asl_file_list_insert(files, n->file, direction); + } + + if (files == NULL) + { + asl_file_list_free(files); + return ASL_STATUS_OK; + } + + /* start the timer if a timeout was specified */ + memset(&finish, 0, sizeof(struct timeval)); + if (usec != 0) + { + if (gettimeofday(&finish, NULL) == 0) + { + finish.tv_sec += (usec / MILLION); + finish.tv_usec += (usec % MILLION); + if (finish.tv_usec > MILLION) + { + finish.tv_usec -= MILLION; + finish.tv_sec += 1; + } + } + else + { + /* shouldn't happen, but if gettimeofday failed we just run without a timeout */ + memset(&finish, 0, sizeof(struct timeval)); + } + } + + rescount = 0; + while ((files != NULL) && ((rescount < count) || (count == 0))) + { + m = NULL; + status = asl_file_match_next(files->file, query, &m, last_id, direction); + if (m != NULL) + { + if (*res == NULL) *res = (aslresponse)calloc(1, sizeof(asl_search_result_t)); + if (*res == NULL) + { + asl_file_list_free(files); + return ASL_STATUS_NO_MEMORY; + } + + if ((*res)->msg == NULL) (*res)->msg = (asl_msg_t **)calloc(1, sizeof(aslmsg)); + else (*res)->msg = (asl_msg_t **)reallocf((*res)->msg, ((*res)->count + 1) * sizeof(aslmsg)); + if ((*res)->msg == NULL) + { + free(*res); + *res = NULL; + asl_file_list_free(files); + return ASL_STATUS_NO_MEMORY; + } + + (*res)->msg[(*res)->count] = (asl_msg_t *)m; + (*res)->count++; + rescount++; + } + + if (files->file->cursor_xid == 0) + { + n = files->next; + free(files); + files = n; + } + + if (files != NULL) + { + n = files->next; + if (n != NULL) + { + if (((direction < 0) && (files->file->cursor_xid <= n->file->cursor_xid)) || ((direction >= 0) && (files->file->cursor_xid > n->file->cursor_xid))) + { + n = files; + files = files->next; + n->next = NULL; + files = asl_file_list_insert(files, n->file, direction); + free(n); + } + } + } + + /* check the timer */ + if ((finish.tv_sec != 0) && (gettimeofday(&now, NULL) == 0)) + { + if ((now.tv_sec > finish.tv_sec) || ((now.tv_sec == finish.tv_sec) && (now.tv_usec > finish.tv_usec))) break; + } + } + + asl_file_list_free(files); + return ASL_STATUS_OK; +} + +uint32_t +asl_file_list_match(asl_file_list_t *list, aslresponse query, aslresponse *res, uint64_t *last_id, uint64_t start_id, uint32_t count, int32_t direction) +{ + return asl_file_list_match_timeout(list, query, res, last_id, start_id, count, direction, 0); +} diff --git a/libsystem_asl.tproj/src/asl_legacy1.c b/libsystem_asl.tproj/src/asl_legacy1.c new file mode 100644 index 0000000..8bd9ba1 --- /dev/null +++ b/libsystem_asl.tproj/src/asl_legacy1.c @@ -0,0 +1,836 @@ +/* + * Copyright (c) 2007-2011 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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, 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define forever for(;;) + +#define FILE_MODE 0600 + +#define DB_RECORD_LEN 80 + +#define DB_HEADER_COOKIE_OFFSET 0 +#define DB_HEADER_VERS_OFFSET 12 + +#define DB_TYPE_EMPTY 0 +#define DB_TYPE_HEADER 1 +#define DB_TYPE_MESSAGE 2 +#define DB_TYPE_KVLIST 3 +#define DB_TYPE_STRING 4 +#define DB_TYPE_STRCONT 5 + +/* + * Magic Cookie for database files. + * MAXIMUM 12 CHARS! (DB_HEADER_VERS_OFFSET) + */ +#define ASL_DB_COOKIE "ASL DB" +#define ASL_DB_COOKIE_LEN 6 + +#define ASL_INDEX_NULL 0xffffffff + +#define DB_HLEN_EMPTY 0 +#define DB_HLEN_HEADER 13 +#define DB_HLEN_MESSAGE 13 +#define DB_HLEN_KVLIST 9 +#define DB_HLEN_STRING 25 +#define DB_HLEN_STRCONT 5 + +#define MSG_OFF_KEY_TYPE 0 +#define MSG_OFF_KEY_NEXT 1 +#define MSG_OFF_KEY_ID 5 +#define MSG_OFF_KEY_RUID 13 +#define MSG_OFF_KEY_RGID 17 +#define MSG_OFF_KEY_TIME 21 +#define MSG_OFF_KEY_HOST 29 +#define MSG_OFF_KEY_SENDER 37 +#define MSG_OFF_KEY_FACILITY 45 +#define MSG_OFF_KEY_LEVEL 53 +#define MSG_OFF_KEY_PID 57 +#define MSG_OFF_KEY_UID 61 +#define MSG_OFF_KEY_GID 65 +#define MSG_OFF_KEY_MSG 69 +#define MSG_OFF_KEY_FLAGS 77 + +extern time_t asl_parse_time(const char *str); +extern int asl_msg_cmp(aslmsg a, aslmsg b); + +#define asl_msg_list_t asl_search_result_t + +#define Q_NULL 100001 +#define Q_FAST 100002 +#define Q_SLOW 100003 +#define Q_FAIL 100004 + +static uint64_t +_asl_htonq(uint64_t n) +{ +#ifdef __BIG_ENDIAN__ + return n; +#else + u_int32_t t; + union + { + u_int64_t q; + u_int32_t l[2]; + } x; + + x.q = n; + t = x.l[0]; + x.l[0] = htonl(x.l[1]); + x.l[1] = htonl(t); + + return x.q; +#endif +} + +static uint64_t +_asl_ntohq(uint64_t n) +{ +#ifdef __BIG_ENDIAN__ + return n; +#else + u_int32_t t; + union + { + u_int64_t q; + u_int32_t l[2]; + } x; + + x.q = n; + t = x.l[0]; + x.l[0] = ntohl(x.l[1]); + x.l[1] = ntohl(t); + + return x.q; +#endif +} + +static uint16_t +_asl_get_16(char *h) +{ + uint16_t x; + + memcpy(&x, h, 2); + return ntohs(x); +} + +static uint32_t +_asl_get_32(char *h) +{ + uint32_t x; + + memcpy(&x, h, 4); + return ntohl(x); +} + +static uint64_t +_asl_get_64(char *h) +{ + uint64_t x; + + memcpy(&x, h, 8); + return _asl_ntohq(x); +} + +#define header_get_next(h) _asl_get_32(h + 1) +#define header_get_id(h) _asl_get_64(h + 5) +#define header_get_hash(h) _asl_get_32(h + 17) + +/* + * callback for sorting slotlist + * primary sort is by xid + * secondary sort is by slot, which happens when xid is 0 + * this allows us to quickly find xids (using binary search on the xid key) + * it's also used to find slots quickly from record_chain_free() + */ +static int +slot_comp(const void *a, const void *b) +{ + asl_legacy1_slot_info_t *ai, *bi; + + if (a == NULL) + { + if (b == NULL) return 0; + return -1; + } + + if (b == NULL) return 1; + + ai = (asl_legacy1_slot_info_t *)a; + bi = (asl_legacy1_slot_info_t *)b; + + if (ai->xid < bi->xid) return -1; + + if (ai->xid == bi->xid) + { + if (ai->slot < bi->slot) return -1; + if (ai->slot == bi->slot) return 0; + return 1; + } + + return 1; +} + +/* find an xid in the slot list */ +static uint32_t +slotlist_find(asl_legacy1_t *s, uint64_t xid, int32_t direction) +{ + uint32_t top, bot, mid, range; + + if (s == NULL) return ASL_INDEX_NULL; + if (s->slotlist_count == 0) return ASL_INDEX_NULL; + if (xid == 0) return ASL_INDEX_NULL; + + top = s->slotlist_count - 1; + bot = 0; + mid = top / 2; + + range = top - bot; + while (range > 1) + { + if (xid == s->slotlist[mid].xid) return mid; + else if (xid < s->slotlist[mid].xid) top = mid; + else bot = mid; + + range = top - bot; + mid = bot + (range / 2); + } + + if (xid == s->slotlist[top].xid) return top; + if (xid == s->slotlist[bot].xid) return bot; + + if (direction == 0) return ASL_INDEX_NULL; + if (direction < 0) return bot; + return top; +} + +static uint32_t +slotlist_init(asl_legacy1_t *s, uint32_t count) +{ + uint32_t i, si, status, hash, addslot; + uint64_t xid; + uint8_t t; + size_t rcount; + char tmp[DB_RECORD_LEN]; + + /* Start at first slot after the header */ + status = fseek(s->db, DB_RECORD_LEN, SEEK_SET); + if (status != 0) return ASL_STATUS_READ_FAILED; + + s->slotlist = (asl_legacy1_slot_info_t *)calloc(count, sizeof(asl_legacy1_slot_info_t)); + if (s->slotlist == NULL) return ASL_STATUS_NO_MEMORY; + + si = 0; + + for (i = 1; i < count; i++) + { + rcount = fread(tmp, DB_RECORD_LEN, 1, s->db); + if (rcount != 1) return ASL_STATUS_READ_FAILED; + + t = tmp[0]; + addslot = 0; + xid = 0; + hash = 0; + + if (t == DB_TYPE_EMPTY) addslot = 1; + + if (t == DB_TYPE_STRING) + { + addslot = 1; + xid = header_get_id(tmp); + hash = header_get_hash(tmp); + } + + if (t == DB_TYPE_MESSAGE) + { + addslot = 1; + xid = header_get_id(tmp); + } + + if (addslot == 1) + { + s->slotlist[si].type = t; + s->slotlist[si].slot = i; + s->slotlist[si].xid = xid; + s->slotlist[si].hash = hash; + si++; + } + } + + s->slotlist = (asl_legacy1_slot_info_t *)reallocf(s->slotlist, si * sizeof(asl_legacy1_slot_info_t)); + if (s->slotlist == NULL) return ASL_STATUS_NO_MEMORY; + s->slotlist_count = si; + + /* slotlist is sorted by xid */ + qsort((void *)s->slotlist, s->slotlist_count, sizeof(asl_legacy1_slot_info_t), slot_comp); + + return ASL_STATUS_OK; +} + +uint32_t +asl_legacy1_open(const char *path, asl_legacy1_t **out) +{ + asl_legacy1_t *s; + struct stat sb; + int status; + size_t rcount; + char cbuf[DB_RECORD_LEN]; + off_t fsize; + uint32_t count; + + memset(&sb, 0, sizeof(struct stat)); + status = stat(path, &sb); + if (status < 0) return ASL_STATUS_FAILED; + + fsize = sb.st_size; + + s = (asl_legacy1_t *)calloc(1, sizeof(asl_legacy1_t)); + if (s == NULL) return ASL_STATUS_NO_MEMORY; + + s->db = fopen(path, "r"); + if (s->db == NULL) + { + free(s); + return ASL_STATUS_INVALID_STORE; + } + + memset(cbuf, 0, DB_RECORD_LEN); + rcount = fread(cbuf, DB_RECORD_LEN, 1, s->db); + if (rcount != 1) + { + fclose(s->db); + free(s); + return ASL_STATUS_READ_FAILED; + } + + /* Check the database Magic Cookie */ + if (strncmp(cbuf, ASL_DB_COOKIE, ASL_DB_COOKIE_LEN)) + { + fclose(s->db); + free(s); + return ASL_STATUS_INVALID_STORE; + } + + count = fsize / DB_RECORD_LEN; + + status = slotlist_init(s, count); + + *out = s; + return ASL_STATUS_OK; +} + +uint32_t +asl_legacy1_close(asl_legacy1_t *s) +{ + if (s == NULL) return ASL_STATUS_INVALID_STORE; + + if (s->slotlist != NULL) free(s->slotlist); + if (s->db != NULL) fclose(s->db); + free(s); + + return ASL_STATUS_OK; +} + +static uint32_t +string_fetch_slot(asl_legacy1_t *s, uint32_t slot, char **out) +{ + off_t offset; + uint8_t type; + uint32_t next, x, remaining; + size_t rcount, len; + int status; + char *outstr, *p, tmp[DB_RECORD_LEN]; + + if (s == NULL) return ASL_STATUS_INVALID_STORE; + if (out == NULL) return ASL_STATUS_INVALID_ARG; + + *out = NULL; + offset = slot * DB_RECORD_LEN; + status = fseek(s->db, offset, SEEK_SET); + + if (status < 0) return ASL_STATUS_READ_FAILED; + + rcount = fread(tmp, DB_RECORD_LEN, 1, s->db); + if (rcount != 1) return ASL_STATUS_READ_FAILED; + + type = tmp[0]; + if (type != DB_TYPE_STRING) return ASL_STATUS_INVALID_STRING; + + len = _asl_get_32(tmp + 21); + if (len == 0) return ASL_STATUS_OK; + + next = header_get_next(tmp); + + outstr = calloc(1, len); + if (outstr == NULL) return ASL_STATUS_NO_MEMORY; + + p = outstr; + remaining = len; + + x = DB_RECORD_LEN - DB_HLEN_STRING; + if (x > remaining) x = remaining; + + memcpy(p, tmp + DB_HLEN_STRING, x); + p += x; + remaining -= x; + + while ((next != 0) && (remaining > 0)) + { + offset = next * DB_RECORD_LEN; + status = fseek(s->db, offset, SEEK_SET); + + if (status < 0) + { + free(outstr); + return ASL_STATUS_READ_FAILED; + } + + rcount = fread(tmp, DB_RECORD_LEN, 1, s->db); + if (rcount != 1) + { + free(outstr); + return ASL_STATUS_READ_FAILED; + } + + next = header_get_next(tmp); + + x = DB_RECORD_LEN - DB_HLEN_STRCONT; + if (x > remaining) x = remaining; + + memcpy(p, tmp + DB_HLEN_STRCONT, x); + p += x; + remaining -= x; + } + + if ((next != 0) || (remaining != 0)) + { + free(outstr); + return ASL_STATUS_READ_FAILED; + } + + *out = outstr; + return ASL_STATUS_OK; +} + +static uint32_t +string_fetch_sid(asl_legacy1_t *s, uint64_t sid, char **out) +{ + uint32_t i, len, ref; + uint64_t nsid; + uint8_t inls; + char *p; + + if (s == NULL) return ASL_STATUS_INVALID_STORE; + if (out == NULL) return ASL_STATUS_INVALID_ARG; + + *out = NULL; + if (sid == ASL_REF_NULL) return ASL_STATUS_OK; + + ref = 0; + + inls = 0; + nsid = _asl_htonq(sid); + memcpy(&inls, &nsid, 1); + if (inls & 0x80) + { + /* inline string */ + inls &= 0x0f; + len = inls; + *out = calloc(1, len); + if (*out == NULL) return ASL_STATUS_NO_MEMORY; + p = 1 + (char *)&nsid; + memcpy(*out, p, len); + return ASL_STATUS_OK; + } + + /* Find the string in the database */ + i = slotlist_find(s, sid, 0); + if (i == ASL_INDEX_NULL) return ASL_STATUS_NOT_FOUND; + + return string_fetch_slot(s, s->slotlist[i].slot, out); +} + +static uint32_t +asl_legacy1_fetch_helper_32(asl_legacy1_t *s, char **p, aslmsg m, const char *key, int ignore, uint32_t ignoreval) +{ + uint32_t out, doit; + char str[256]; + + out = _asl_get_32(*p); + *p += sizeof(uint32_t); + + if ((m == NULL) || (key == NULL)) return out; + + doit = 1; + if ((ignore != 0) && (out == ignoreval)) doit = 0; + if (doit != 0) + { + snprintf(str, sizeof(str), "%u", out); + asl_set(m, key, str); + } + + return out; +} + +static uint64_t +asl_legacy1_fetch_helper_64(asl_legacy1_t *s, char **p, aslmsg m, const char *key) +{ + uint64_t out; + char str[256]; + + out = _asl_get_64(*p); + *p += sizeof(uint64_t); + + if ((m == NULL) || (key == NULL)) return out; + + snprintf(str, sizeof(str), "%llu", out); + asl_set(m, key, str); + + return out; +} + +static uint64_t +asl_legacy1_fetch_helper_str(asl_legacy1_t *s, char **p, aslmsg m, const char *key, uint32_t *err) +{ + uint64_t out; + char *val; + uint32_t status; + + out = _asl_get_64(*p); + *p += sizeof(uint64_t); + + val = NULL; + status = ASL_STATUS_OK; + if (out != 0) status = string_fetch_sid(s, out, &val); + + if (err != NULL) *err = status; + if ((status == ASL_STATUS_OK) && (val != NULL)) + { + asl_set(m, key, val); + free(val); + } + + return out; +} + +static uint32_t +msg_fetch(asl_legacy1_t *s, uint32_t slot, aslmsg *out) +{ + off_t offset; + uint32_t status, i, n, kvcount, next; + uint16_t flags; + uint64_t sid; + size_t rcount; + aslmsg msg; + int fstatus; + char *p, tmp[DB_RECORD_LEN], *key, *val; + + if (s == NULL) return ASL_STATUS_INVALID_STORE; + if (out == NULL) return ASL_STATUS_INVALID_ARG; + + *out = NULL; + + offset = slot * DB_RECORD_LEN; + fstatus = fseek(s->db, offset, SEEK_SET); + + if (fstatus < 0) return ASL_STATUS_READ_FAILED; + + rcount = fread(tmp, DB_RECORD_LEN, 1, s->db); + if (rcount != 1) return ASL_STATUS_READ_FAILED; + + flags = _asl_get_16(tmp + MSG_OFF_KEY_FLAGS); + + msg = asl_new(ASL_TYPE_MSG); + if (msg == NULL) return ASL_STATUS_NO_MEMORY; + + p = tmp + 5; + + asl_legacy1_fetch_helper_64(s, &p, msg, ASL_KEY_MSG_ID); + asl_legacy1_fetch_helper_32(s, &p, msg, ASL_KEY_READ_UID, 1, (uint32_t)-1); + asl_legacy1_fetch_helper_32(s, &p, msg, ASL_KEY_READ_GID, 1, (uint32_t)-1); + asl_legacy1_fetch_helper_64(s, &p, msg, ASL_KEY_TIME); + asl_legacy1_fetch_helper_str(s, &p, msg, ASL_KEY_HOST, &status); + asl_legacy1_fetch_helper_str(s, &p, msg, ASL_KEY_SENDER, &status); + asl_legacy1_fetch_helper_str(s, &p, msg, ASL_KEY_FACILITY, &status); + asl_legacy1_fetch_helper_32(s, &p, msg, ASL_KEY_LEVEL, 0, 0); + asl_legacy1_fetch_helper_32(s, &p, msg, ASL_KEY_PID, 0, 0); + asl_legacy1_fetch_helper_32(s, &p, msg, ASL_KEY_UID, 0, 0); + asl_legacy1_fetch_helper_32(s, &p, msg, ASL_KEY_GID, 0, 0); + asl_legacy1_fetch_helper_str(s, &p, msg, ASL_KEY_MSG, &status); + + next = header_get_next(tmp); + + kvcount = 0; + n = 0; + + while (next != 0) + { + offset = next * DB_RECORD_LEN; + fstatus = fseek(s->db, offset, SEEK_SET); + if (fstatus < 0) + { + free(out); + return ASL_STATUS_READ_FAILED; + } + + rcount = fread(tmp, DB_RECORD_LEN, 1, s->db); + if (rcount != 1) + { + free(out); + return ASL_STATUS_READ_FAILED; + } + + if (kvcount == 0) kvcount = _asl_get_32(tmp + 5); + + p = tmp + 9; + + for (i = 0; (i < 4) && (n < kvcount); i++) + { + key = NULL; + sid = _asl_get_64(p); + p += 8; + status = string_fetch_sid(s, sid, &key); + + val = NULL; + sid = _asl_get_64(p); + p += 8; + if (status == ASL_STATUS_OK) status = string_fetch_sid(s, sid, &val); + + if ((status == ASL_STATUS_OK) && (key != NULL)) asl_set(msg, key, val); + if (key != NULL) free(key); + if (val != NULL) free(val); + + n++; + } + + next = header_get_next(tmp); + } + + *out = msg; + return ASL_STATUS_OK; +} + +uint32_t +asl_legacy1_fetch(asl_legacy1_t *s, uint64_t msgid, aslmsg *out) +{ + uint32_t i, status; + + if (s == NULL) return ASL_STATUS_INVALID_STORE; + if (msgid == ASL_REF_NULL) return ASL_STATUS_INVALID_ARG; + if (out == NULL) return ASL_STATUS_INVALID_ARG; + + i = slotlist_find(s, msgid, 0); + if (i == ASL_INDEX_NULL) return ASL_STATUS_INVALID_ID; + + /* read the message */ + status = msg_fetch(s, s->slotlist[i].slot, out); + if (status != ASL_STATUS_OK) return status; + if (*out == NULL) return ASL_STATUS_FAILED; + + return status; +} + +static uint32_t +next_search_slot(asl_legacy1_t *s, uint32_t last_si, int32_t direction) +{ + uint32_t i; + + if (direction >= 0) + { + for (i = last_si + 1; i < s->slotlist_count; i++) + { + if (s->slotlist[i].type == DB_TYPE_MESSAGE) return i; + } + + return ASL_INDEX_NULL; + } + + if (last_si == 0) return ASL_INDEX_NULL; + if (last_si > s->slotlist_count) return ASL_INDEX_NULL; + + for (i = last_si - 1; i > 0; i--) + { + if (s->slotlist[i].type == DB_TYPE_MESSAGE) return i; + } + + if (s->slotlist[0].type == DB_TYPE_MESSAGE) return 0; + + return ASL_INDEX_NULL; +} + +static void +match_worker_cleanup(asl_msg_list_t **res) +{ + uint32_t i; + + if (res != NULL) + { + for (i = 0; i < (*res)->count; i++) asl_free((aslmsg)(*res)->msg[i]); + free(*res); + } +} + +/* + * Input to asl_legacy1_match is a list of queries. + * A record in the store matches if it matches any query (i.e. query list is "OR"ed) + * + * If counting up (direction is positive) find first record with ID > start_id. + * Else if counting down (direction is negative) find first record with ID < start_id. + * + * Set match flag on. + * If any query is NULL, set match flog off (skips matching below). + * Else if all queries only check "standard" keys, set std flag to on. + * + * If all queries are marked as "never matches", return NULL. + * + * match loop: + * fetch record (with std flag) + * if match flag is off, decode record and add it to result. + * else for each query: + * if query is NULL (shouldn't happen) decode record and add it to result. Return to match loop. + * else if query never matches, ignore it. + * else decode record and use asl_cmp. If it succeeds, add record to result. Return to match loop. + * + * return results. + */ +static uint32_t +match_worker(asl_legacy1_t *s, asl_msg_list_t *query, asl_msg_list_t **res, uint64_t *last_id, uint64_t **idlist, uint32_t *idcount, uint64_t start_id, int32_t count, int32_t direction) +{ + uint32_t mx, si, slot, i, qcount, match, didmatch, status; + uint64_t xid; + aslmsg msg; + + if (s == NULL) return ASL_STATUS_INVALID_STORE; + if ((res == NULL) && (idlist == NULL)) return ASL_STATUS_INVALID_ARG; + if (last_id == NULL) return ASL_STATUS_INVALID_ARG; + if (idcount == NULL) return ASL_STATUS_INVALID_ARG; + + if (res != NULL) *res = NULL; + if (idlist != NULL) *idlist = NULL; + + mx = 0; + + if (direction < 0) direction = -1; + else direction = 1; + + si = ASL_INDEX_NULL; + if ((direction == -1) && (start_id == ASL_REF_NULL)) si = s->slotlist_count; + else si = slotlist_find(s, start_id, direction); + + si = next_search_slot(s, si, direction); + if (si == ASL_INDEX_NULL) return ASL_STATUS_OK; + if (si >= s->slotlist_count) return ASL_STATUS_FAILED; + + slot = s->slotlist[si].slot; + + match = 1; + qcount = 0; + + if (query == NULL) match = 0; + else if (query->count == 0) match = 0; + else qcount = query->count; + + /* + * initialize result list if we've been asked to return messages + */ + if (res != NULL) + { + *res = (asl_msg_list_t *)calloc(1, sizeof(asl_msg_list_t)); + if (*res == NULL) return ASL_STATUS_NO_MEMORY; + } + + status = ASL_STATUS_OK; + + /* + * loop through records + */ + *idcount = 0; + while ((count == 0) || (*idcount < count)) + { + if (si == ASL_INDEX_NULL) break; + if (si >= s->slotlist_count) break; + + slot = s->slotlist[si].slot; + xid = s->slotlist[si].xid; + + *last_id = xid; + + status = msg_fetch(s, slot, &msg); + + didmatch = 0; + if (match == 0) + { + didmatch = 1; + } + else + { + for (i = 0; i < qcount; i++) + { + didmatch = asl_msg_cmp((aslmsg)(query->msg[i]), msg); + if (didmatch == 1) break; + } + } + + if (didmatch == 1) + { + if ((*res)->count == 0) (*res)->msg = (asl_msg_t **)calloc(1, sizeof(asl_msg_t *)); + else (*res)->msg = (asl_msg_t **)reallocf((*res)->msg, (1 + (*res)->count) * sizeof(asl_msg_t *)); + if ((*res)->msg == NULL) + { + match_worker_cleanup(res); + return ASL_STATUS_NO_MEMORY; + } + + (*res)->msg[(*res)->count++] = (asl_msg_t *)msg; + } + else + { + asl_free(msg); + } + + si = next_search_slot(s, si, direction); + } + + return status; +} + +uint32_t +asl_legacy1_match(asl_legacy1_t *s, asl_msg_list_t *query, asl_msg_list_t **res, uint64_t *last_id, uint64_t start_id, uint32_t count, int32_t direction) +{ + uint32_t idcount; + + idcount = 0; + return match_worker(s, query, res, last_id, NULL, &idcount, start_id, count, direction); +} diff --git a/libsystem_asl.tproj/src/asl_msg.c b/libsystem_asl.tproj/src/asl_msg.c new file mode 100644 index 0000000..f0b0c0d --- /dev/null +++ b/libsystem_asl.tproj/src/asl_msg.c @@ -0,0 +1,2752 @@ +/* + * Copyright (c) 2009-2012 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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, 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "asl_msg.h" + +#define TOKEN_NULL 0 +#define TOKEN_OPEN 1 +#define TOKEN_CLOSE 2 +#define TOKEN_WORD 3 +#define TOKEN_INT 4 + +#define MFMT_RAW 0 +#define MFMT_STD 1 +#define MFMT_BSD 2 +#define MFMT_XML 3 +#define MFMT_STR 4 +#define MFMT_MSG 5 + +#define SEC_PER_HOUR 3600 + +#define forever for(;;) + +#define streq(A, B) (strcmp(A, B) == 0) +#define streq_len(A, B, C) (strncmp(A, B, C) == 0) +#define strneq(A, B) (strcmp(A, B) != 0) +#define strcaseeq(A, B) (strcasecmp(A, B) == 0) +#define strcaseneq(A, B) (strcasecmp(A, B) != 0) + +#ifndef ASL_KEY_OPTION +#define ASL_KEY_OPTION "ASLOption" +#endif + +#ifndef ASL_QUERY_OP_FALSE +#define ASL_QUERY_OP_FALSE 0 +#endif + +#define AUX_0_TIME 0x00000001 +#define AUX_0_TIME_NSEC 0x00000002 +#define AUX_0_HOST 0x00000004 +#define AUX_0_SENDER 0x00000008 +#define AUX_0_FACILITY 0x00000010 +#define AUX_0_PID 0x00000020 +#define AUX_0_UID 0x00000040 +#define AUX_0_GID 0x00000080 +#define AUX_0_MSG 0x00000100 +#define AUX_0_OPTION 0x00000200 +#define AUX_0_LEVEL 0x00000400 + +extern time_t asl_parse_time(const char *in); + +/* from asl_util.c */ +int asl_is_utf8(const char *str); +uint8_t *asl_b64_encode(const uint8_t *buf, size_t len); + +static const char *ASLStandardKey[] = +{ + ASL_KEY_TIME, + ASL_KEY_TIME_NSEC, + ASL_KEY_HOST, + ASL_KEY_SENDER, + ASL_KEY_FACILITY, + ASL_KEY_PID, + ASL_KEY_UID, + ASL_KEY_GID, + ASL_KEY_LEVEL, + ASL_KEY_MSG, + ASL_KEY_READ_UID, + ASL_KEY_READ_GID, + ASL_KEY_SESSION, + ASL_KEY_REF_PID, + ASL_KEY_REF_PROC, + ASL_KEY_MSG_ID, + ASL_KEY_EXPIRE_TIME, + ASL_KEY_OPTION +}; + +static const char *MTStandardKey[] = +{ + "com.apple.message.domain", + "com.apple.message.domain_scope", + "com.apple.message.result", + "com.apple.message.signature", + "com.apple.message.signature2", + "com.apple.message.signature3", + "com.apple.message.success", + "com.apple.message.uuid", + "com.apple.message.value", + "com.apple.message.value2", + "com.apple.message.value3", + "com.apple.message.value4", + "com.apple.message.value5" +}; + +static uint16_t +_asl_msg_std_key(const char *s, uint32_t len) +{ + if ((len > 18) && (streq_len(s, "com.apple.message.", 18))) + { + if (streq(s + 18, "domain")) return ASL_MT_KEY_DOMAIN; + else if (streq(s + 18, "domain_scope")) return ASL_MT_KEY_SCOPE; + else if (streq(s + 18, "result")) return ASL_MT_KEY_RESULT; + else if (streq(s + 18, "signature")) return ASL_MT_KEY_SIG; + else if (streq(s + 18, "signature2")) return ASL_MT_KEY_SIG2; + else if (streq(s + 18, "signature3")) return ASL_MT_KEY_SIG3; + else if (streq(s + 18, "success")) return ASL_MT_KEY_SUCCESS; + else if (streq(s + 18, "uuid")) return ASL_MT_KEY_UUID; + else if (streq(s + 18, "value")) return ASL_MT_KEY_VAL; + else if (streq(s + 18, "value2")) return ASL_MT_KEY_VAL2; + else if (streq(s + 18, "value3")) return ASL_MT_KEY_VAL3; + else if (streq(s + 18, "value4")) return ASL_MT_KEY_VAL4; + else if (streq(s + 18, "value5")) return ASL_MT_KEY_VAL5; + + return 0; + } + + switch (len) + { + case 3: + { + if streq(s, ASL_KEY_PID) return ASL_STD_KEY_PID; + else if streq(s, ASL_KEY_UID) return ASL_STD_KEY_UID; + else if streq(s, ASL_KEY_GID) return ASL_STD_KEY_GID; + } + case 4: + { + if streq(s, ASL_KEY_TIME) return ASL_STD_KEY_TIME; + else if streq(s, ASL_KEY_HOST) return ASL_STD_KEY_HOST; + } + case 5: + { + if streq(s, ASL_KEY_LEVEL) return ASL_STD_KEY_LEVEL; + } + case 6: + { + if streq(s, ASL_KEY_SENDER) return ASL_STD_KEY_SENDER; + else if streq(s, ASL_KEY_REF_PID) return ASL_STD_KEY_REF_PID; + } + case 7: + { + if streq(s, ASL_KEY_MSG) return ASL_STD_KEY_MESSAGE; + else if streq(s, ASL_KEY_SESSION) return ASL_STD_KEY_SESSION; + else if streq(s, ASL_KEY_READ_UID) return ASL_STD_KEY_READ_UID; + else if streq(s, ASL_KEY_READ_GID) return ASL_STD_KEY_READ_GID; + else if streq(s, ASL_KEY_REF_PROC) return ASL_STD_KEY_REF_PROC; + } + case 8: + { + if streq(s, ASL_KEY_FACILITY) return ASL_STD_KEY_FACILITY; + } + case 9: + { + if streq(s, ASL_KEY_OPTION) return ASL_STD_KEY_OPTION; + } + case 11: + { + if streq(s, ASL_KEY_TIME_NSEC) return ASL_STD_KEY_NANO; + } + case 12: + { + if streq(s, ASL_KEY_MSG_ID) return ASL_STD_KEY_MSG_ID; + } + case 13: + { + if streq(s, ASL_KEY_EXPIRE_TIME) return ASL_STD_KEY_EXPIRE; + } + default: + { + return 0; + } + } + + return 0; +} + +static asl_msg_t * +_asl_msg_make_page() +{ + asl_msg_t *out; + int i; + + out = calloc(1, sizeof(asl_msg_t)); + if (out == NULL) return NULL; + + for (i = 0; i < ASL_MSG_PAGE_SLOTS; i++) + { + out->key[i] = ASL_MSG_SLOT_FREE; + out->val[i] = ASL_MSG_SLOT_FREE; + } + + return out; +} + +static const char * +_asl_msg_slot_key(asl_msg_t *page, uint32_t slot) +{ + const char *out; + uint16_t x; + + if (page == NULL) return NULL; + if (slot >= ASL_MSG_PAGE_SLOTS) return NULL; + + if (page->key[slot] == ASL_MSG_SLOT_FREE) return NULL; + + switch (page->key[slot] & ASL_MSG_KV_MASK) + { + case ASL_MSG_KV_INLINE: + { + return page->data + page->key[slot]; + } + case ASL_MSG_KV_DICT: + { + if ((page->key[slot] > ASL_STD_KEY_BASE) && (page->key[slot] <= ASL_STD_KEY_LAST)) + { + x = page->key[slot] - ASL_STD_KEY_BASE - 1; + return ASLStandardKey[x]; + } + else if ((page->key[slot] > ASL_MT_KEY_BASE) && (page->key[slot] <= ASL_MT_KEY_LAST)) + { + x = page->key[slot] - ASL_MT_KEY_BASE - 1; + return MTStandardKey[x]; + } + + return NULL; + } + case ASL_MSG_KV_EXTERN: + { + x = page->key[slot] & ASL_MSG_OFFSET_MASK; + memcpy(&out, page->data + x, sizeof(char *)); + return out; + } + } + + return NULL; +} + +static const char * +_asl_msg_slot_val(asl_msg_t *page, uint32_t slot) +{ + const char *out; + uint16_t x, type; + + if (page == NULL) return NULL; + if (slot >= ASL_MSG_PAGE_SLOTS) return NULL; + + if (page->val[slot] == ASL_MSG_SLOT_FREE) return NULL; + + type = page->val[slot] & ASL_MSG_KV_MASK; + + if (type == ASL_MSG_KV_INLINE) + { + return page->data + page->val[slot]; + } + else if (type == ASL_MSG_KV_EXTERN) + { + x = page->val[slot] & ASL_MSG_OFFSET_MASK; + memcpy(&out, page->data + x, sizeof(char *)); + return out; + } + + return NULL; +} + +/* + * asl_new: create a new log message. + */ +asl_msg_t * +asl_msg_new(uint32_t type) +{ + asl_msg_t *out; + + out = _asl_msg_make_page(); + if (out == NULL) return NULL; + + out->type = type; + out->refcount = 1; + + return out; +} + +asl_msg_t * +asl_msg_retain(asl_msg_t *msg) +{ + int32_t new; + + if (msg == NULL) return NULL; + + new = OSAtomicIncrement32Barrier(&msg->refcount); + assert(new >= 1); + + return msg; +} + +static void +_asl_msg_free(asl_msg_t *page) +{ + uint32_t i; + char *p; + + if (page == NULL) return; + + for (i = 0; i < ASL_MSG_PAGE_SLOTS; i++) + { + if ((page->key[i] & ASL_MSG_KV_MASK) == ASL_MSG_KV_EXTERN) + { + memcpy(&p, page->data + (page->key[i] & ASL_MSG_OFFSET_MASK), sizeof(char *)); + free(p); + } + + if ((page->val[i] & ASL_MSG_KV_MASK) == ASL_MSG_KV_EXTERN) + { + memcpy(&p, page->data + (page->val[i] & ASL_MSG_OFFSET_MASK), sizeof(char *)); + free(p); + } + } + + free(page); +} + +void +asl_msg_release(asl_msg_t *msg) +{ + int32_t new; + asl_msg_t *next; + + if (msg == NULL) return; + + new = OSAtomicDecrement32Barrier(&msg->refcount); + assert(new >= 0); + + if (new > 0) return; + + while (msg != NULL) + { + next = msg->next; + _asl_msg_free(msg); + msg = next; + } +} + +static uint32_t +_asl_msg_index(asl_msg_t *msg, const char *key, uint32_t *oslot, asl_msg_t **opage) +{ + uint32_t i, len, slot; + uint16_t kx; + asl_msg_t *page; + const char *kp; + + if (msg == NULL) return IndexNull; + if (key == NULL) return IndexNull; + + i = 0; + slot = 0; + if (oslot != NULL) *oslot = slot; + + page = msg; + if (opage != NULL) *opage = page; + + len = strlen(key); + kx = _asl_msg_std_key(key, len); + + forever + { + if (page->key[slot] != ASL_MSG_SLOT_FREE) + { + if (kx != 0) + { + if (page->key[slot] == kx) return i; + } + else if ((page->key[slot] & ASL_MSG_KV_MASK) == ASL_MSG_KV_DICT) + { + /* page->key[slot] is a dictionary key, but key is not (kx == 0) so skip this slot */ + } + else if ((page->key[slot] & ASL_MSG_KV_MASK) == ASL_MSG_KV_EXTERN) + { + memcpy(&kp, page->data + (page->key[slot] & ASL_MSG_OFFSET_MASK), sizeof(char *)); + if (streq(key, kp)) return i; + } + else + { + kp = page->data + page->key[slot]; + if (streq(key, kp)) return i; + } + } + + i++; + slot++; + if (oslot != NULL) *oslot = slot; + + if (slot >= ASL_MSG_PAGE_SLOTS) + { + if (page->next == NULL) return IndexNull; + + slot = 0; + if (oslot != NULL) *oslot = slot; + + page = page->next; + if (opage != NULL) *opage = page; + } + } + + return IndexNull; +} + +/* + * asl_msg_key: iterate over entries + * initial value of n should be 0 + * after that, the value of n should be previously returned value + * sets the pointers for the next key, value, and op in the msgionary + * returns IndexNull when there are no more entries + */ +static uint32_t +_asl_msg_fetch_internal(asl_msg_t *msg, uint32_t n, const char **keyout, const char **valout, uint32_t *opout, asl_msg_t **outpage, uint32_t *outslot) +{ + uint32_t slot; + asl_msg_t *page; + + if (msg == NULL) return IndexNull; + if (outpage != NULL) *outpage = NULL; + if (outslot != NULL) *outslot = IndexNull; + + slot = n; + page = msg; + + while (slot >= ASL_MSG_PAGE_SLOTS) + { + if (page->next == NULL) return IndexNull; + page = page->next; + slot -= ASL_MSG_PAGE_SLOTS; + } + + while (page->key[slot] == ASL_MSG_SLOT_FREE) + { + slot++; + n++; + + if (slot >= ASL_MSG_PAGE_SLOTS) + { + if (page->next == NULL) return IndexNull; + page = page->next; + slot = 0; + } + } + + n++; + + if (keyout != NULL) *keyout = _asl_msg_slot_key(page, slot); + if (valout != NULL) *valout = _asl_msg_slot_val(page, slot); + if (opout != NULL) *opout = page->op[slot]; + + if (outpage != NULL) *outpage = page; + if (outslot != NULL) *outslot = slot; + + return n; +} + +uint32_t +asl_msg_fetch(asl_msg_t *msg, uint32_t n, const char **keyout, const char **valout, uint32_t *opout) +{ + return _asl_msg_fetch_internal(msg, n, keyout, valout, opout, NULL, NULL); +} + +static int +_asl_msg_new_key_val_op(asl_msg_t *msg, const char *key, const char *val, uint32_t op) +{ + uint32_t slot, keylen, vallen, total; + uint16_t kx; + asl_msg_t *page, *last; + char *extkey, *extval; + + if (msg == NULL) return -1; + if (key == NULL) return -1; + + extkey = NULL; + extval = NULL; + + keylen = strlen(key); + kx = _asl_msg_std_key(key, keylen); + + if (kx == 0) keylen++; + else keylen = 0; + + total = keylen; + + vallen = 0; + if (val != NULL) + { + vallen = strlen(val) + 1; + total += vallen; + } + + /* check if one or both of key and value must be "external" (in its own malloced space) */ + if (keylen > ASL_MSG_PAGE_DATA_SIZE) + { + extkey = strdup(key); + keylen = sizeof(char *); + } + + if (vallen > ASL_MSG_PAGE_DATA_SIZE) + { + extval = strdup(val); + vallen = sizeof(char *); + } + + total = keylen + vallen; + if ((total > ASL_MSG_PAGE_DATA_SIZE) && (extval == NULL) && (keylen > 0)) + { + extval = strdup(val); + vallen = sizeof(char *); + total = keylen + vallen; + } + + if ((total > ASL_MSG_PAGE_DATA_SIZE) && (extkey == NULL)) + { + extkey = strdup(key); + keylen = sizeof(char *); + total = keylen + vallen; + } + + if (total > ASL_MSG_PAGE_DATA_SIZE) + { + /* can't happen, but... */ + if (extkey != NULL) free(extkey); + if (extval != NULL) free(extval); + return -1; + } + + /* find a page with space for the key and value and a free slot*/ + slot = 0; + last = msg; + + for (page = msg; page != NULL; page = page->next) + { + last = page; + + if (total <= (ASL_MSG_PAGE_DATA_SIZE - page->data_size)) + { + /* check for a free slot */ + for (slot = 0; (slot < ASL_MSG_PAGE_SLOTS) && (page->key[slot] != ASL_MSG_SLOT_FREE); slot++); + if (slot < ASL_MSG_PAGE_SLOTS) break; + } + } + + if (page == NULL) + { + /* allocate a new page and attach it */ + page = _asl_msg_make_page(); + if (page == NULL) + { + if (extkey != NULL) free(extkey); + if (extval != NULL) free(extval); + return -1; + } + + last->next = page; + slot = 0; + } + + /* copy key or external key pointer into page data */ + if (kx != 0) + { + page->key[slot] = kx; + } + else if (extkey == NULL) + { + page->key[slot] = page->data_size; + memcpy(page->data + page->data_size, key, keylen); + } + else + { + page->key[slot] = page->data_size | ASL_MSG_KV_EXTERN; + memcpy(page->data + page->data_size, &extkey, keylen); + } + + page->data_size += keylen; + + /* copy val or external val pointer into page data */ + page->val[slot] = ASL_MSG_SLOT_FREE; + + if (val != NULL) + { + if (extval == NULL) + { + page->val[slot] = page->data_size; + memcpy(page->data + page->data_size, val, vallen); + } + else + { + page->val[slot] = page->data_size | ASL_MSG_KV_EXTERN; + memcpy(page->data + page->data_size, &extval, vallen); + } + + page->data_size += vallen; + } + + /* set op */ + page->op[slot] = op; + + /* update page count */ + page->count++; + + return 0; +} + +/* + * Most of the code in asl_msg_set_key_val_op is concerned with trying to re-use + * space in an asl_msg_t page when given a new value for an existing key. + * If the key is new, we just call _asl_msg_new_key_val_op. + * + * Note that queries can have duplicate keys, so for ASL_TYPE_QUERY we just + * call through to _asl_msg_new_key_val_op. + */ +static int +_asl_msg_set_kvo(asl_msg_t *msg, const char *key, const char *val, uint32_t op) +{ + uint32_t i, slot, newexternal; + asl_msg_t *page; + uint32_t intvallen, extvallen, newvallen; + char *intval, *extval, *newval; + + if (msg == NULL) return -1; + if (key == NULL) return -1; + + slot = IndexNull; + page = NULL; + + if ((msg->type == ASL_TYPE_QUERY) || (IndexNull == _asl_msg_index(msg, key, &slot, &page))) + { + /* add key */ + return _asl_msg_new_key_val_op(msg, key, val, op); + } + + intval = NULL; + intvallen = 0; + + extval = NULL; + extvallen = 0; + + if (page->val[slot] != ASL_MSG_SLOT_FREE) + { + if ((page->val[slot] & ASL_MSG_KV_MASK) == ASL_MSG_KV_EXTERN) + { + i = page->val[slot] & ASL_MSG_OFFSET_MASK; + memcpy(&extval, page->data + i, sizeof(char *)); + extvallen = sizeof(char *); + } + else + { + intval = page->data + page->val[slot]; + intvallen = strlen(intval) + 1; + } + } + + /* replace val and op for existing entry */ + + /* easy case - remove val */ + if (val == NULL) + { + if (extval != NULL) free(extval); + page->val[slot] = ASL_MSG_SLOT_FREE; + if (op != IndexNull) page->op[slot] = op; + return 0; + } + + /* trivial case - internal val doesn't change */ + if ((intval != NULL) && (streq(val, intval))) + { + if (op != IndexNull) page->op[slot] = op; + return 0; + } + + /* trivial case - external val doesn't change */ + if ((extval != NULL) && (streq(val, extval))) + { + if (op != IndexNull) page->op[slot] = op; + return 0; + } + + /* + * special case: we generally don't compress out holes in the data + * space, but if this is the last string in the currently used data space + * we can just back up the data_size and reset page->val[slot] + */ + i = page->val[slot] & ASL_MSG_OFFSET_MASK; + if ((intval != NULL) && ((i + intvallen) == page->data_size)) + { + page->val[slot] = ASL_MSG_SLOT_FREE; + page->data_size -= intvallen; + intval = NULL; + intvallen = 0; + } + else if ((extval != NULL) && ((i + extvallen) == page->data_size)) + { + page->val[slot] = ASL_MSG_SLOT_FREE; + page->data_size -= extvallen; + free(extval); + extval = NULL; + extvallen = 0; + } + + /* val changes - see if it needs to be external */ + newvallen = strlen(val) + 1; + newexternal = 0; + + if (newvallen > ASL_MSG_PAGE_DATA_SIZE) + { + newexternal = 1; + newvallen = sizeof(char *); + } + + /* check if there is room to change val in place */ + if (((extval != NULL) && (newvallen <= extvallen)) || ((extval == NULL) && (newvallen <= intvallen))) + { + if (extval != NULL) free(extval); + extval = NULL; + + /* we can re-use the space of the old value */ + i = page->val[slot] & ASL_MSG_OFFSET_MASK; + + if (newexternal == 1) + { + /* create an external val and copy in the new pointer */ + newval = strdup(val); + if (newval == NULL) return -1; + + page->val[slot] = i | ASL_MSG_KV_EXTERN; + memcpy(page->data + i, &newval, sizeof(char *)); + } + else + { + /* new internal value */ + page->val[slot] = i; + memcpy(page->data + i, val, newvallen); + } + + if (op != IndexNull) page->op[slot] = op; + return 0; + } + + /* we're done with the old value if it is external - free it now */ + if (extval != NULL) free(extval); + extval = NULL; + + if (newvallen <= (ASL_MSG_PAGE_DATA_SIZE - page->data_size)) + { + /* can't re-use the old space, but there's room on the page */ + i = page->data_size; + page->data_size += newvallen; + + if (newexternal == 1) + { + /* create an external val and copy in the new pointer */ + newval = strdup(val); + if (newval == NULL) return -1; + + page->val[slot] = i | ASL_MSG_KV_EXTERN; + memcpy(page->data + i, &newval, sizeof(char *)); + } + else + { + /* new internal value */ + page->val[slot] = i; + memcpy(page->data + i, val, newvallen); + } + + if (op != IndexNull) page->op[slot] = op; + return 0; + + } + + /* no room on this page - free up existing entry and treat this as a new entry */ + if ((page->key[slot] & ASL_MSG_KV_MASK) == ASL_MSG_KV_EXTERN) + { + memcpy(&extval, page->data + (page->key[slot] & ASL_MSG_OFFSET_MASK), sizeof(char *)); + free(extval); + } + + page->key[slot] = ASL_MSG_SLOT_FREE; + page->val[slot] = ASL_MSG_SLOT_FREE; + page->op[slot] = 0; + + return _asl_msg_new_key_val_op(msg, key, val, op); +} + +int +asl_msg_set_key_val_op(asl_msg_t *msg, const char *key, const char *val, uint32_t op) +{ + char *special, buf[512]; + uint32_t i, len; + int status; + + /* Special case handling */ + special = NULL; + + /* if query modifier is set but op is zero, default to equality test */ + if ((op != 0) && ((op & ASL_QUERY_OP_TRUE) == 0)) op |= ASL_QUERY_OP_EQUAL; + + /* convert "Level" values to "0" through "7" */ + if (streq(key, ASL_KEY_LEVEL)) + { + if (val == NULL) val = "7"; + else if ((val[0] >= '0') && (val[0] <= '7') && (val[1] == '\0')) /* do nothing */; + else if (strcaseeq(val, ASL_STRING_EMERG)) val = "0"; + else if (strcaseeq(val, ASL_STRING_ALERT)) val = "1"; + else if (strcaseeq(val, ASL_STRING_CRIT)) val = "2"; + else if (strcaseeq(val, ASL_STRING_ERR)) val = "3"; + else if (strcaseeq(val, ASL_STRING_WARNING)) val = "4"; + else if (strcaseeq(val, ASL_STRING_NOTICE)) val = "5"; + else if (strcaseeq(val, ASL_STRING_INFO)) val = "6"; + else if (strcaseeq(val, ASL_STRING_DEBUG)) val = "7"; + else val = "7"; + } + + /* strip trailing newlines from "Message" values */ + if ((streq(key, ASL_KEY_MSG)) && (val != NULL)) + { + len = strlen(val); + i = len; + while ((i > 0) && (val[i - 1] == '\n')) i--; + if (i == 0) val = NULL; + else if (i < len) + { + /* use buf if it is big enough, else malloc a temporary buffer */ + if (i < sizeof(buf)) + { + memcpy(buf, val, i); + buf[i] = 0; + val = (const char *)buf; + } + else + { + special = malloc(i + 1); + if (special == NULL) return -1; + memcpy(special, val, i); + special[i] = 0; + val = (const char *)special; + } + } + } + + status = _asl_msg_set_kvo(msg, key, val, op); + + if (special != NULL) free(special); + return status; +} + +int +asl_msg_set_key_val(asl_msg_t *msg, const char *key, const char *val) +{ + return asl_msg_set_key_val_op(msg, key, val, 0); +} + +/* + * Merge a key / val into a message (only ASL_TYPE_MSG). + * Adds the key / val if the key is not found. + * Does not replace the value if the key is found. + */ +static void +_asl_msg_merge_key_val_op(asl_msg_t *msg, const char *key, const char *val, uint32_t op) +{ + uint32_t i, slot; + asl_msg_t *page; + + if (msg == NULL) return; + if (key == NULL) return; + + slot = IndexNull; + page = NULL; + + i = _asl_msg_index(msg, key, &slot, &page); + if (i != IndexNull) return; + + asl_msg_set_key_val_op(msg, key, val, op); +} + +/* + * Merge msg into target (does not replace existing keys). + * Creates a new asl_msg_t if target is NULL. + * Returns target. + */ +asl_msg_t * +asl_msg_merge(asl_msg_t *target, asl_msg_t *msg) +{ + uint32_t x, slot, op, isnew = 0; + const char *key, *val; + asl_msg_t *page; + + if (msg == NULL) return target; + + if (target == NULL) + { + isnew = 1; + target = asl_msg_new(msg->type); + } + + for (x = _asl_msg_fetch_internal(msg, 0, &key, &val, &op, &page, &slot); x != IndexNull; x = _asl_msg_fetch_internal(msg, x, &key, &val, &op, &page, &slot)) + { + if (msg->type == ASL_TYPE_MSG) op = 0; + if (isnew == 1) asl_msg_set_key_val_op(target, key, val, op); + else _asl_msg_merge_key_val_op(target, key, val, op); + } + + return target; +} + +/* + * Copy msg. + */ +asl_msg_t * +asl_msg_copy(asl_msg_t *msg) +{ + return asl_msg_merge(NULL, msg); +} + +/* + * asl_msg_unset + * Frees external key and val strings, but does not try to reclaim data space. + */ +void +asl_msg_unset(asl_msg_t *msg, const char *key) +{ + uint32_t i, slot; + asl_msg_t *page; + char *ext; + + if (msg == NULL) return; + if (key == NULL) return; + + slot = IndexNull; + page = NULL; + + i = _asl_msg_index(msg, key, &slot, &page); + if (i == IndexNull) return; + + if ((page->key[slot] & ASL_MSG_KV_MASK) == ASL_MSG_KV_EXTERN) + { + memcpy(&ext, page->data + (page->key[slot] & ASL_MSG_OFFSET_MASK), sizeof(char *)); + free(ext); + } + + if ((page->val[slot] & ASL_MSG_KV_MASK) == ASL_MSG_KV_EXTERN) + { + memcpy(&ext, page->data + (page->val[slot] & ASL_MSG_OFFSET_MASK), sizeof(char *)); + free(ext); + } + + page->key[slot] = ASL_MSG_SLOT_FREE; + page->val[slot] = ASL_MSG_SLOT_FREE; + page->op[slot] = 0; + + page->count--; +} + +int +asl_msg_lookup(asl_msg_t *msg, const char *key, const char **valout, uint32_t *opout) +{ + uint32_t i, slot; + asl_msg_t *page; + + if (valout != NULL) *valout = NULL; + if (opout != NULL) *opout = 0; + + slot = IndexNull; + page = NULL; + + i = _asl_msg_index(msg, key, &slot, &page); + if (i == IndexNull) return -1; + + if (valout != NULL) *valout = _asl_msg_slot_val(page, slot); + if (opout != NULL) *opout = page->op[slot]; + + return 0; +} + +uint32_t +asl_msg_type(asl_msg_t *msg) +{ + if (msg == NULL) return 0; + return msg->type; +} + +uint32_t +asl_msg_count(asl_msg_t *msg) +{ + uint32_t total; + + total = 0; + + for (; msg != NULL; msg = msg->next) total += msg->count; + return total; +} + +/* + * Compare messages + */ +static int +_asl_msg_equal(asl_msg_t *a, asl_msg_t *b) +{ + uint32_t x, oa, ob; + const char *key, *va, *vb; + + if (asl_msg_count(a) != asl_msg_count(b)) return 0; + + key = NULL; + va = NULL; + oa = 0; + + for (x = asl_msg_fetch(a, 0, &key, &va, &oa); x != IndexNull; x = asl_msg_fetch(a, x, &key, &va, &oa)) + { + if (asl_msg_lookup(b, key, &vb, &ob) != 0) return 0; + if (strcmp(va, vb)) return 0; + if ((a->type == ASL_TYPE_QUERY) && (oa != ob)) return 0; + } + + return 1; +} + +static int +_asl_isanumber(const char *s) +{ + int i; + + if (s == NULL) return 0; + + i = 0; + if ((s[0] == '-') || (s[0] == '+')) i = 1; + + if (s[i] == '\0') return 0; + + for (; s[i] != '\0'; i++) + { + if (!isdigit(s[i])) return 0; + } + + return 1; +} + +static int +_asl_msg_basic_test(uint32_t op, const char *q, const char *m, uint32_t n) +{ + int cmp; + uint32_t t; + int64_t nq, nm; + int rflags; + regex_t rex; + + t = op & ASL_QUERY_OP_TRUE; + + /* NULL value from query or message string fails */ + if ((q == NULL) || (m == NULL)) return (t & ASL_QUERY_OP_NOT_EQUAL); + + if (op & ASL_QUERY_OP_REGEX) + { + /* greater than or less than make no sense in substring search */ + if ((t == ASL_QUERY_OP_GREATER) || (t == ASL_QUERY_OP_LESS)) return 0; + + memset(&rex, 0, sizeof(regex_t)); + + rflags = REG_EXTENDED | REG_NOSUB; + if (op & ASL_QUERY_OP_CASEFOLD) rflags |= REG_ICASE; + + /* A bad reqular expression matches nothing */ + if (regcomp(&rex, q, rflags) != 0) return (t & ASL_QUERY_OP_NOT_EQUAL); + + cmp = regexec(&rex, m, 0, NULL, 0); + regfree(&rex); + + if (t == ASL_QUERY_OP_NOT_EQUAL) return (cmp != 0); + return (cmp == 0); + } + + if (op & ASL_QUERY_OP_NUMERIC) + { + if (_asl_isanumber(q) == 0) return (t == ASL_QUERY_OP_NOT_EQUAL); + if (_asl_isanumber(m) == 0) return (t == ASL_QUERY_OP_NOT_EQUAL); + + nq = atoll(q); + nm = atoll(m); + + switch (t) + { + case ASL_QUERY_OP_EQUAL: return (nm == nq); + case ASL_QUERY_OP_GREATER: return (nm > nq); + case ASL_QUERY_OP_GREATER_EQUAL: return (nm >= nq); + case ASL_QUERY_OP_LESS: return (nm < nq); + case ASL_QUERY_OP_LESS_EQUAL: return (nm <= nq); + case ASL_QUERY_OP_NOT_EQUAL: return (nm != nq); + default: return (t == ASL_QUERY_OP_NOT_EQUAL); + } + } + + cmp = 0; + if (op & ASL_QUERY_OP_CASEFOLD) + { + if (n == 0) cmp = strcasecmp(m, q); + else cmp = strncasecmp(m, q, n); + } + else + { + if (n == 0) cmp = strcmp(m, q); + else cmp = strncmp(m, q, n); + } + + switch (t) + { + case ASL_QUERY_OP_EQUAL: return (cmp == 0); + case ASL_QUERY_OP_GREATER: return (cmp > 0); + case ASL_QUERY_OP_GREATER_EQUAL: return (cmp >= 0); + case ASL_QUERY_OP_LESS: return (cmp < 0); + case ASL_QUERY_OP_LESS_EQUAL: return (cmp <= 0); + case ASL_QUERY_OP_NOT_EQUAL: return (cmp != 0); + } + + return (t == ASL_QUERY_OP_NOT_EQUAL); +} + +static int +_asl_msg_test_substring(uint32_t op, const char *q, const char *m) +{ + uint32_t t, i, d, lm, lq, match, newop; + + t = op & ASL_QUERY_OP_TRUE; + + lm = 0; + if (m != NULL) lm = strlen(m); + + lq = 0; + if (q != NULL) lq = strlen(q); + + /* NULL is a substring of any string */ + if (lq == 0) return (t & ASL_QUERY_OP_EQUAL); + + /* A long string is defined to be not equal to a short string */ + if (lq > lm) return (t == ASL_QUERY_OP_NOT_EQUAL); + + /* greater than or less than make no sense in substring search */ + if ((t == ASL_QUERY_OP_GREATER) || (t == ASL_QUERY_OP_LESS)) return 0; + + /* + * We scan the string doing an equality test. + * If the input test is equality, we stop as soon as we hit a match. + * Otherwise we keep scanning the whole message string. + */ + newop = op & 0xff0; + newop |= ASL_QUERY_OP_EQUAL; + + match = 0; + d = lm - lq; + for (i = 0; i <= d; i++) + { + if (_asl_msg_basic_test(newop, q, m + i, lq) != 0) + { + if (t & ASL_QUERY_OP_EQUAL) return 1; + match++; + } + } + + /* If the input test was for equality, no matches were found */ + if (t & ASL_QUERY_OP_EQUAL) return 0; + + /* The input test was for not equal. Return true if no matches were found */ + return (match == 0); +} + +static int +_asl_msg_test_prefix(uint32_t op, const char *q, const char *m) +{ + uint32_t lm, lq, t; + + t = op & ASL_QUERY_OP_TRUE; + + lm = 0; + if (m != NULL) lm = strlen(m); + + lq = 0; + if (q != NULL) lq = strlen(q); + + /* NULL is a prefix of any string */ + if (lq == 0) return (t & ASL_QUERY_OP_EQUAL); + + /* A long string is defined to be not equal to a short string */ + if (lq > lm) return (t == ASL_QUERY_OP_NOT_EQUAL); + + /* Compare two equal-length strings */ + return _asl_msg_basic_test(op, q, m, lq); +} + +static int +_asl_msg_test_suffix(uint32_t op, const char *q, const char *m) +{ + uint32_t lm, lq, d, t; + + t = op & ASL_QUERY_OP_TRUE; + + lm = 0; + if (m != NULL) lm = strlen(m); + + lq = 0; + if (q != NULL) lq = strlen(q); + + /* NULL is a suffix of any string */ + if (lq == 0) return (t & ASL_QUERY_OP_EQUAL); + + /* A long string is defined to be not equal to a short string */ + if (lq > lm) return (t == ASL_QUERY_OP_NOT_EQUAL); + + /* Compare two equal-length strings */ + d = lm - lq; + return _asl_msg_basic_test(op, q, m + d, lq); +} + +/* + * Splits out prefix, suffix, and substring tests. + * Sends the rest to _asl_msg_basic_test(). + */ +static int +_asl_msg_test_expression(uint32_t op, const char *q, const char *m) +{ + uint32_t t; + + t = op & ASL_QUERY_OP_TRUE; + if (t == ASL_QUERY_OP_TRUE) return 1; + + if (op & ASL_QUERY_OP_PREFIX) + { + if (op & ASL_QUERY_OP_SUFFIX) return _asl_msg_test_substring(op, q, m); + return _asl_msg_test_prefix(op, q, m); + } + if (op & ASL_QUERY_OP_SUFFIX) return _asl_msg_test_suffix(op, q, m); + + return _asl_msg_basic_test(op, q, m, 0); +} + +/* + * Special case for comparing time values. + * If both inputs are time strings, this compares the time + * value in seconds. Otherwise it just does normal matching. + */ +static int +_asl_msg_test_time_expression(uint32_t op, const char *q, const char *m) +{ + time_t tq, tm; + uint32_t t; + + if ((op & ASL_QUERY_OP_PREFIX) || (op & ASL_QUERY_OP_SUFFIX) || (op & ASL_QUERY_OP_REGEX)) return _asl_msg_test_expression(op, q, m); + if ((q == NULL) || (m == NULL)) return _asl_msg_test_expression(op, q, m); + + tq = asl_parse_time(q); + if (tq < 0) return _asl_msg_test_expression(op, q, m); + + tm = asl_parse_time(m); + if (tm < 0) return _asl_msg_test_expression(op, q, m); + + t = op & ASL_QUERY_OP_TRUE; + + switch (t) + { + case ASL_QUERY_OP_FALSE: + { + return 0; + } + case ASL_QUERY_OP_EQUAL: + { + if (tm == tq) return 1; + return 0; + } + case ASL_QUERY_OP_GREATER: + { + if (tm > tq) return 1; + return 0; + } + case ASL_QUERY_OP_GREATER_EQUAL: + { + if (tm >= tq) return 1; + return 0; + } + case ASL_QUERY_OP_LESS: + { + if (tm < tq) return 1; + return 0; + } + case ASL_QUERY_OP_LESS_EQUAL: + { + if (tm <= tq) return 1; + return 0; + } + case ASL_QUERY_OP_NOT_EQUAL: + { + if (tm != tq) return 1; + return 0; + } + case ASL_QUERY_OP_TRUE: + { + return 1; + } + } + + /* NOTREACHED */ + return 0; +} + +/* test a query against a message */ +static int +_asl_msg_test(asl_msg_t *q, asl_msg_t *m) +{ + uint32_t i, t, x, op; + int cmp; + const char *kq, *vq, *vm; + + /* + * Check each simple expression (key op val) separately. + * The query suceeds (returns 1) if all simple expressions + * succeed (i.e. AND the simple expressions). + */ + + kq = NULL; + vq = NULL; + op = 0; + + for (x = asl_msg_fetch(q, 0, &kq, &vq, &op); x != IndexNull; x = asl_msg_fetch(q, x, &kq, &vq, &op)) + { + /* Find query key in the message */ + vm = NULL; + i = asl_msg_lookup(m, kq, &vm, NULL); + + /* ASL_QUERY_OP_TRUE tests if key is present in the message */ + t = op & ASL_QUERY_OP_TRUE; + if (t == ASL_QUERY_OP_TRUE) + { + if (i != 0) return 0; + continue; + } + + /* ASL_QUERY_OP_FALSE tests if the key is NOT present in the message */ + if (t == ASL_QUERY_OP_FALSE) + { + if (i == 0) return 0; + continue; + } + + if (i != 0) + { + /* the message does NOT have query key - fail unless we are testing not equal */ + if (t == ASL_QUERY_OP_NOT_EQUAL) continue; + return 0; + } + + cmp = 1; + if (streq(kq, ASL_KEY_TIME)) + { + cmp = _asl_msg_test_time_expression(op, vq, vm); + } + else + { + cmp = _asl_msg_test_expression(op, vq, vm); + } + + if (cmp == 0) return 0; + } + + return 1; +} + +int +asl_msg_cmp(asl_msg_t *a, asl_msg_t *b) +{ + + if (a == NULL) return 0; + if (b == NULL) return 0; + + if (a->type == b->type) return _asl_msg_equal(a, b); + if (a->type == ASL_TYPE_QUERY) return _asl_msg_test(a, b); + return _asl_msg_test(b, a); +} + + +static char * +_asl_time_string(const char *infmt, const char *str, const char *nano) +{ + time_t tick, off; + struct tm stm; + char *ltime, *out, *p, *q; + char ltbuf[32], nanobuf[16], fmt[32], zstr[8]; + time_t zh, zm; + uint32_t subsec = 0; + bool neg; + + out = NULL; + memset(zstr, 0, sizeof(zstr)); + zh = 0; + zm = 0; + off = 0; + neg = false; + + /* + * The longest infmt string we understand is "-hh:mm.N" (8 chars), so + * it is safe to ignore the input if it doesn't fit in the temp buffer. + * The default is local time zone format. + */ + if (infmt == NULL) + { + snprintf(fmt, sizeof(fmt), "local"); + } + else if (strlen(infmt) >= sizeof (fmt)) + { + snprintf(fmt, sizeof(fmt), "local"); + } + else + { + snprintf(fmt, sizeof(fmt), "%s", infmt); + + p = fmt; + q = strchr(fmt, '.'); + if (q != NULL) + { + *q = '\0'; + q++; + if (q != '\0') subsec = atoi(q); + } + } + + nanobuf[0] = '\0'; + + if (subsec > 0) + { + uint32_t nsec = 0; + if (nano != NULL) nsec = atoi(nano); + snprintf(nanobuf, sizeof(nanobuf), ".%09u", nsec); + if (subsec > 9) subsec = 9; + nanobuf[subsec + 1] = '\0'; + } + + tick = 0; + if (str != NULL) tick = asl_parse_time(str); + + if ((!strcasecmp(fmt, "lcl")) || (!strcasecmp(fmt, "local"))) + { + ltime = ctime_r(&tick, ltbuf); + if (ltime == NULL) return NULL; + ltime[19] = '\0'; + asprintf(&out, "%s%s", ltime + 4, nanobuf); + return out; + } + + if ((!strcasecmp(fmt, "jz")) || (!strcasecmp(fmt, "iso8601")) || (!strcasecmp(fmt, "iso8601e"))) + { + char sep = ' '; + if (!strncasecmp(fmt, "iso8601", 7)) sep = 'T'; + + if (NULL == localtime_r(&tick, &stm)) return NULL; + + off = stm.tm_gmtoff; + if ((neg = (off < 0))) off *= -1; + zh = off / 3600; + off %= 3600; + zm = off / 60; + + if (zm == 0) snprintf(zstr, sizeof(zstr), "%c%02ld", neg ? '-' : '+', zh); + else snprintf(zstr, sizeof(zstr), "%c%02ld:%02ld", neg ? '-' : '+', zh, zm); + + asprintf(&out, "%d-%02d-%02d%c%02d:%02d:%02d%s%s", stm.tm_year + 1900, stm.tm_mon + 1, stm.tm_mday, sep, stm.tm_hour, stm.tm_min, stm.tm_sec, nanobuf, zstr); + return out; + } + + if (!strcasecmp(fmt, "iso8601b")) + { + if (NULL == localtime_r(&tick, &stm)) return NULL; + + off = stm.tm_gmtoff; + if ((neg = (off < 0))) off *= -1; + zh = off / 3600; + off %= 3600; + zm = off / 60; + + if (zm == 0) snprintf(zstr, sizeof(zstr), "%c%02ld", neg ? '-' : '+', zh); + else snprintf(zstr, sizeof(zstr), "%c%02ld:%02ld", neg ? '-' : '+', zh, zm); + + asprintf(&out, "%d%02d%02dT%02d%02d%02d%s%s", stm.tm_year + 1900, stm.tm_mon + 1, stm.tm_mday, stm.tm_hour, stm.tm_min, stm.tm_sec, nanobuf, zstr); + return out; + } + + if ((!strcasecmp(fmt, "sec")) || (!strcasecmp(fmt, "raw"))) + { + asprintf(&out, "%lu%s", tick, nanobuf); + return out; + } + + if (!strcasecmp(fmt, "j")) + { + if (NULL == localtime_r(&tick, &stm)) return NULL; + asprintf(&out, "%d-%02d-%02d %02d:%02d:%02d%s", stm.tm_year + 1900, stm.tm_mon + 1, stm.tm_mday, stm.tm_hour, stm.tm_min, stm.tm_sec, nanobuf); + return out; + } + + if ((!strcasecmp(fmt, "utc")) || (!strcasecmp(fmt, "zulu")) || (!strcasecmp(fmt, "iso8601z")) || (!strcasecmp(fmt, "iso8601ez"))) + { + char sep = ' '; + if (!strncasecmp(fmt, "iso8601", 7)) sep = 'T'; + + if (NULL == gmtime_r(&tick, &stm)) return NULL; + asprintf(&out, "%d-%02d-%02d%c%02d:%02d:%02d%sZ", stm.tm_year + 1900, stm.tm_mon + 1, stm.tm_mday, sep, stm.tm_hour, stm.tm_min, stm.tm_sec, nanobuf); + return out; + } + + if (!strcasecmp(fmt, "iso8601bz")) + { + if (NULL == gmtime_r(&tick, &stm)) return NULL; + asprintf(&out, "%d%02d%02dT%02d%02d%02d%sZ", stm.tm_year + 1900, stm.tm_mon + 1, stm.tm_mday, stm.tm_hour, stm.tm_min, stm.tm_sec, nanobuf); + return out; + } + + if ((fmt[1] == '\0') && (((fmt[0] >= 'a') && (fmt[0] <= 'z')) || ((fmt[0] >= 'A') && (fmt[0] <= 'Z')))) + { + char z = fmt[0]; + if (z >= 'a') z -= 32; + + if (z == 'Z') off = 0; + else if ((z >= 'A') && (z <= 'I')) off = ((z - 'A') + 1) * SEC_PER_HOUR; + else if ((z >= 'K') && (z <= 'M')) off = (z - 'A') * SEC_PER_HOUR; + else if ((z >= 'N') && (z <= 'Y')) off = ('M' - z) * SEC_PER_HOUR; + else return NULL; + } + else + { + if (fmt[0] == '-') neg = true; + if ((*p == '-') || (*p == '+')) p++; + if ((*p) >= '0' && (*p <= '9')) + { + zh = atoi(p); + + p = strchr(p, ':'); + if (p != NULL) zm = atoi(p + 1); + } + else + { + return NULL; + } + + off = (zh * 3600) + (zm * 60); + if (neg) off *= -1; + + if (zm == 0) snprintf(zstr, sizeof(zstr), "%c%02ld", neg ? '-' : '+', zh); + else snprintf(zstr, sizeof(zstr), "%c%02ld:%02ld", neg ? '-' : '+', zh, zm); + } + + + tick += off; + + memset(&stm, 0, sizeof (struct tm)); + if (NULL == gmtime_r(&tick, &stm)) return NULL; + + if ((fmt[0] >= 'A') && (fmt[0] <= 'Z')) + { + asprintf(&out, "%d-%02d-%02d %02d:%02d:%02d%s%c", stm.tm_year + 1900, stm.tm_mon + 1, stm.tm_mday, stm.tm_hour, stm.tm_min, stm.tm_sec, nanobuf, fmt[0]); + } + else if ((fmt[0] >= 'a') && (fmt[0] <= 'z')) + { + asprintf(&out, "%d-%02d-%02d %02d:%02d:%02d%s%c", stm.tm_year + 1900, stm.tm_mon + 1, stm.tm_mday, stm.tm_hour, stm.tm_min, stm.tm_sec, nanobuf, fmt[0] - 32); + } + else + { + asprintf(&out, "%d-%02d-%02d %02d:%02d:%02d%s%s", stm.tm_year + 1900, stm.tm_mon + 1, stm.tm_mday, stm.tm_hour, stm.tm_min, stm.tm_sec, nanobuf, zstr); + } + + return out; +} + +/* called from asl_format_message and _asl_send_message */ +__private_extern__ asl_string_t * +asl_msg_to_string_raw(uint32_t encoding, asl_msg_t *msg, const char *tfmt) +{ + uint32_t i, x, count; + const char *key, *val, *nano; + asl_string_t *str; + + if (msg == NULL) return NULL; + + count = asl_msg_count(msg); + if (count == 0) return NULL; + + str = asl_string_new(encoding); + if (str == NULL) return NULL; + + key = NULL; + val = NULL; + i = 0; + + nano = NULL; + asl_msg_lookup(msg, ASL_KEY_TIME_NSEC, &nano, NULL); + + for (x = asl_msg_fetch(msg, 0, &key, &val, NULL); x != IndexNull; x = asl_msg_fetch(msg, x, &key, &val, NULL)) + { + if (key == NULL) continue; + + if (i > 0) asl_string_append_char_no_encoding(str, ' '); + + asl_string_append_char_no_encoding(str, '['); + asl_string_append_asl_key(str, key); + + if (!strcmp(key, ASL_KEY_TIME)) + { + char *vtime = NULL; + asl_string_append_char_no_encoding(str, ' '); + + if (val != NULL) vtime = _asl_time_string(tfmt, val, nano); + + if (vtime != NULL) + { + asl_string_append_no_encoding(str, vtime); + free(vtime); + } + else + { + asl_string_append_char_no_encoding(str, '0'); + } + } + else if (val != NULL) + { + asl_string_append_char_no_encoding(str, ' '); + asl_string_append(str, val); + } + + asl_string_append_char_no_encoding(str, ']'); + + i++; + } + + return str; +} + +static asl_string_t * +_asl_string_append_asl_msg(asl_string_t *str, asl_msg_t *msg) +{ + const char *key, *val; + uint32_t i, op, n; + + if (msg == NULL) return str; + + if (msg->type == ASL_TYPE_QUERY) asl_string_append(str, "Q "); + + i = 0; + n = 0; + + forever + { + key = NULL; + val = NULL; + + i = asl_msg_fetch(msg, i, &key, &val, &op); + if (key != NULL) + { + if (n != 0) asl_string_append_char_no_encoding(str, ' '); + n++; + + asl_string_append_char_no_encoding(str, '['); + + if (msg->type == ASL_TYPE_QUERY) + { + asl_string_append_op(str, op); + asl_string_append_char_no_encoding(str, ' '); + } + + asl_string_append_asl_key(str, key); + + if (val != NULL) + { + asl_string_append_char_no_encoding(str, ' '); + asl_string_append(str, val); + } + + asl_string_append_char_no_encoding(str, ']'); + } + + if (i == IndexNull) break; + } + + return str; +} + +char * +asl_msg_to_string(asl_msg_t *msg, uint32_t *len) +{ + char *out; + asl_string_t *str = asl_string_new(ASL_ENCODE_ASL); + if (str == NULL) return NULL; + + str = _asl_string_append_asl_msg(str, msg); + *len = asl_string_length(str); + out = asl_string_free_return_bytes(str); + return out; +} + +static uint32_t +_asl_msg_op_from_string(char *o) +{ + uint32_t op, i; + + op = ASL_QUERY_OP_NULL; + + if (o == NULL) return op; + + for (i = 0; o[i] != '\0'; i++) + { + if (o[i] == '.') return ASL_QUERY_OP_NULL; + if (o[i] == 'C') op |= ASL_QUERY_OP_CASEFOLD; + if (o[i] == 'R') op |= ASL_QUERY_OP_REGEX; + if (o[i] == 'N') op |= ASL_QUERY_OP_NUMERIC; + if (o[i] == 'S') op |= ASL_QUERY_OP_SUBSTRING; + if (o[i] == 'A') op |= ASL_QUERY_OP_PREFIX; + if (o[i] == 'Z') op |= ASL_QUERY_OP_SUFFIX; + if (o[i] == '<') op |= ASL_QUERY_OP_LESS; + if (o[i] == '>') op |= ASL_QUERY_OP_GREATER; + if (o[i] == '=') op |= ASL_QUERY_OP_EQUAL; + if (o[i] == '!') op |= ASL_QUERY_OP_NOT_EQUAL; + if (o[i] == 'T') op |= ASL_QUERY_OP_TRUE; + } + + return op; +} + +static char * +_asl_msg_get_next_word(char **p, uint32_t *tt, uint32_t spacedel) +{ + char *str, *out, c, oval; + uint32_t i, len, n, outlen; + + *tt = TOKEN_NULL; + + if (p == NULL) return NULL; + if (*p == NULL) return NULL; + if (**p == '\0') return NULL; + + /* skip one space if it's there (word separator) */ + if (**p == ' ') (*p)++; + + /* skip leading white space */ + if (spacedel != 0) + { + while ((**p == ' ') || (**p == '\t')) (*p)++; + } + + if (**p == '\0') return NULL; + if (**p == '\n') return NULL; + + str = *p; + + /* opening [ */ + if (**p == '[') + { + *tt = TOKEN_OPEN; + + (*p)++; + out = malloc(2); + if (out == NULL) return NULL; + + out[0] = '['; + out[1] = '\0'; + return out; + } + + /* scan for token and calulate it's length (input and decoded output len) */ + len = 0; + outlen = 0; + + forever + { + c = str[len]; + + /* stop scanning when we hit a delimiter */ + if (((spacedel != 0) && (c == ' ')) || (c == ']') || (c == '\0')) break; + + if (c == '\\') + { + len++; + c = str[len]; + if ((c == 'a') || (c == 'b') || (c == 't') || (c == 'n') || (c == 'v') || (c == 'f') || (c == 'r') || (c == 's') || (c == '[') || (c == '\\') || (c == ']')) + { + } + else if (c == '^') + { + if (str[++len] == '\0') return NULL; + } + else if (c == 'M') + { + if (str[++len] == '\0') return NULL; + if (str[++len] == '\0') return NULL; + } + else if ((c >= '0') && (c <= '3')) + { + if (str[++len] == '\0') return NULL; + if (str[++len] == '\0') return NULL; + } + else + { + return NULL; + } + } + + len++; + outlen++; + } + + (*p) += len; + + if ((len == 0) && (**p == ']')) + { + *tt = TOKEN_CLOSE; + (*p)++; + out = malloc(2); + if (out == NULL) return NULL; + + out[0] = ']'; + out[1] = '\0'; + return out; + } + + *tt = TOKEN_INT; + + out = malloc(outlen + 1); + if (out == NULL) return NULL; + + n = 0; + for (i = 0; i < len; i++) + { + c = str[i]; + + if (c == '\\') + { + *tt = TOKEN_WORD; + + i++; + c = str[i]; + if (c == 'a') + { + out[n++] = '\a'; + } + else if (c == 'b') + { + out[n++] = '\b'; + } + else if (c == 't') + { + out[n++] = '\t'; + } + else if (c == 'n') + { + out[n++] = '\n'; + } + else if (c == 'v') + { + out[n++] = '\v'; + } + else if (c == 'f') + { + out[n++] = '\f'; + } + else if (c == 'r') + { + out[n++] = '\r'; + } + else if (c == 's') + { + out[n++] = ' '; + } + else if (c == '[') + { + out[n++] = '['; + } + else if (c == '\\') + { + out[n++] = '\\'; + } + else if (c == ']') + { + out[n++] = ']'; + } + else if (c == '^') + { + i++; + if (str[i] == '?') out[n++] = 127; + else out[n++] = str[i] - 64; + } + else if (c == 'M') + { + i++; + c = str[i]; + if (c == '^') + { + i++; + if (str[i] == '?') out[n++] = 255; + else out[n++] = str[i] + 64; + } + else if (c == '-') + { + i++; + out[n++] = str[i] + 128; + } + else + { + *tt = TOKEN_NULL; + free(out); + return NULL; + } + + } + else if ((c >= '0') && (c <= '3')) + { + oval = (c - '0') * 64; + + i++; + c = str[i]; + if ((c < '0') || (c > '7')) + { + *tt = TOKEN_NULL; + free(out); + return NULL; + } + + oval += ((c - '0') * 8); + + i++; + c = str[i]; + if ((c < '0') || (c > '7')) + { + *tt = TOKEN_NULL; + free(out); + return NULL; + } + + oval += (c - '0'); + + out[n++] = oval; + } + else + { + *tt = TOKEN_NULL; + free(out); + return NULL; + } + } + else + { + + if ((c < '0') || (c > '9')) *tt = TOKEN_WORD; + out[n++] = c; + } + } + + out[n] = '\0'; + + return out; +} + +asl_msg_t * +asl_msg_from_string(const char *buf) +{ + uint32_t tt, type, op; + char *k, *v, *o, *p; + asl_msg_t *out; + + if (buf == NULL) return NULL; + + type = ASL_TYPE_MSG; + p = (char *)buf; + + k = _asl_msg_get_next_word(&p, &tt, 1); + if (k == NULL) return NULL; + + if (streq(k, "Q")) + { + type = ASL_TYPE_QUERY; + free(k); + + k = _asl_msg_get_next_word(&p, &tt, 1); + } + else if (tt == TOKEN_INT) + { + /* Leading integer is a string length - skip it */ + free(k); + k = _asl_msg_get_next_word(&p, &tt, 1); + if (k == NULL) return NULL; + } + + out = asl_msg_new(ASL_TYPE_MSG); + if (out == NULL) return NULL; + + out->type = type; + + /* OPEN WORD [WORD [WORD]] CLOSE */ + while (k != NULL) + { + op = ASL_QUERY_OP_NULL; + + if (tt != TOKEN_OPEN) + { + asl_msg_release(out); + return NULL; + } + + free(k); + + /* get op for query type */ + if (type == ASL_TYPE_QUERY) + { + o = _asl_msg_get_next_word(&p, &tt, 1); + if ((o == NULL) || (tt != TOKEN_WORD)) + { + if (o != NULL) free(o); + asl_msg_release(out); + return NULL; + } + + op = _asl_msg_op_from_string(o); + free(o); + } + + k = _asl_msg_get_next_word(&p, &tt, 1); + if (tt == TOKEN_INT) tt = TOKEN_WORD; + if ((k == NULL) || (tt != TOKEN_WORD)) + { + if (k != NULL) free(k); + asl_msg_release(out); + return NULL; + } + + v = _asl_msg_get_next_word(&p, &tt, 0); + if (tt == TOKEN_INT) tt = TOKEN_WORD; + if (v == NULL) + { + asl_msg_set_key_val_op(out, k, NULL, op); + free(k); + break; + } + + if (tt == TOKEN_CLOSE) + { + asl_msg_set_key_val_op(out, k, NULL, op); + } + else if (tt == TOKEN_WORD) + { + asl_msg_set_key_val_op(out, k, v, op); + } + else + { + if (k != NULL) free(k); + if (v != NULL) free(v); + asl_msg_release(out); + return NULL; + } + + if (k != NULL) free(k); + if (v != NULL) free(v); + + if (tt != TOKEN_CLOSE) + { + k = _asl_msg_get_next_word(&p, &tt, 1); + if (k == NULL) break; + + if (tt != TOKEN_CLOSE) + { + asl_msg_release(out); + return NULL; + } + + free(k); + } + + k = _asl_msg_get_next_word(&p, &tt, 1); + if (k == NULL) break; + } + + return out; +} + +char * +asl_list_to_string(asl_search_result_t *list, uint32_t *len) +{ + uint32_t i; + char tmp[16]; + char *out; + asl_string_t *str; + + if (list == NULL) return NULL; + if (list->count == 0) return NULL; + if (list->msg == NULL) return NULL; + + str = asl_string_new(ASL_ENCODE_ASL); + if (str == NULL) return NULL; + + snprintf(tmp, sizeof(tmp), "%u", list->count); + asl_string_append(str, tmp); + asl_string_append_char_no_encoding(str, '\n'); + + for (i = 0; i < list->count; i++) + { + _asl_string_append_asl_msg(str, list->msg[i]); + asl_string_append_char_no_encoding(str, '\n'); + } + + *len = asl_string_length(str); + out = asl_string_free_return_bytes(str); + return out; +} + +asl_search_result_t * +asl_list_from_string(const char *buf) +{ + uint32_t i, n; + const char *p; + asl_search_result_t *out; + asl_msg_t *m; + + if (buf == NULL) return NULL; + p = buf; + + n = atoi(buf); + if (n == 0) return NULL; + + out = (asl_search_result_t *)calloc(1, sizeof(asl_search_result_t)); + if (out == NULL) return NULL; + + out->msg = (asl_msg_t **)calloc(n, sizeof(asl_msg_t *)); + if (out->msg == NULL) + { + free(out); + return NULL; + } + + for (i = 0; i < n; i++) + { + p = strchr(p, '\n'); + if (p == NULL) + { + aslresponse_free((aslresponse)out); + return NULL; + } + + p++; + + m = asl_msg_from_string(p); + if (m == NULL) + { + aslresponse_free((aslresponse)out); + return NULL; + } + + out->msg[i] = (asl_msg_t *)m; + out->count += 1; + } + + return out; +} + +static const char * +_asl_level_string(int level) +{ + if (level == ASL_LEVEL_EMERG) return ASL_STRING_EMERG; + if (level == ASL_LEVEL_ALERT) return ASL_STRING_ALERT; + if (level == ASL_LEVEL_CRIT) return ASL_STRING_CRIT; + if (level == ASL_LEVEL_ERR) return ASL_STRING_ERR; + if (level == ASL_LEVEL_WARNING) return ASL_STRING_WARNING; + if (level == ASL_LEVEL_NOTICE) return ASL_STRING_NOTICE; + if (level == ASL_LEVEL_INFO) return ASL_STRING_INFO; + if (level == ASL_LEVEL_DEBUG) return ASL_STRING_DEBUG; + return "unknown"; +} + +/* + * Find the value for a key in a message and append a formatted value to str. + * kf may be a simple (no embedded white space) key, or one of (key) or ((key)(format)). + * WARNING: modifies kf! + */ +static asl_string_t * +_asl_string_append_value_for_key_format(asl_string_t *str, asl_msg_t *msg, char *kf, const char *tfmt) +{ + uint32_t i, get_fmt; + int status; + char *key, *fmt; + const char *mval, *nano; + + if (str == NULL) return NULL; + if (msg == NULL) return str; + if (kf == NULL) return str; + + key = NULL; + fmt = NULL; + get_fmt = 0; + + for (i = 0; kf[i] != '\0'; i++) + { + if (kf[i] == ')') + { + kf[i] = '\0'; + get_fmt = 1; + } + else if (kf[i] != '(') + { + if (key == NULL) key = kf + i; + else if ((get_fmt == 1) && (fmt == NULL)) fmt = kf + i; + } + } + + if (key == NULL) return str; + + nano = NULL; + asl_msg_lookup(msg, ASL_KEY_TIME_NSEC, &nano, NULL); + + status = asl_msg_lookup(msg, key, &mval, NULL); + if ((status != 0) || (mval == NULL)) return str; + + if (!strcmp(key, ASL_KEY_TIME)) + { + char *fval = NULL; + + /* format in $((Time)(fmt)) overrides tfmt */ + if (fmt == NULL) + { + fval = _asl_time_string(tfmt, mval, nano); + } + else + { + fval = _asl_time_string(fmt, mval, nano); + } + + if (fval != NULL) + { + asl_string_append_no_encoding(str, fval); + free(fval); + } + else + { + asl_string_append_char_no_encoding(str, '0'); + } + + return str; + } + + /* Level: num str */ + if (!strcmp(key, ASL_KEY_LEVEL)) + { + if (fmt == NULL) + { + asl_string_append_no_encoding(str, mval); + } + else if (!strcmp(fmt, "str")) + { + mval = _asl_level_string(atoi(mval)); + asl_string_append_no_encoding(str, mval); + } + else + { + asl_string_append_no_encoding(str, mval); + } + + return str; + } + + return asl_string_append(str, mval); +} + +/* + * format a message for printing + * out parameter len returns string length including trailing NUL + */ +char * +asl_format_message(asl_msg_t *msg, const char *mfmt, const char *tfmt, uint32_t text_encoding, uint32_t *len) +{ + char *out, *vtime, *k, c, skey[512], tfmt_ext[16]; + const char *vhost, *vpid, *vsender, *vmessage, *vlevel, *vrefproc, *vrefpid, *v, *key, *val, *nano; + int i, j, l, mf, paren, oval, level; + uint32_t x, cursor; + asl_string_t *str; + uint8_t *b64; + + out = NULL; + *len = 0; + + if (msg == NULL) return NULL; + + mf = MFMT_RAW; + + if (mfmt == NULL) mf = MFMT_RAW; + else if (!strcmp(mfmt, ASL_MSG_FMT_RAW)) mf = MFMT_RAW; + else if (!strcmp(mfmt, ASL_MSG_FMT_STD)) mf = MFMT_STD; + else if (!strcmp(mfmt, ASL_MSG_FMT_BSD)) mf = MFMT_BSD; + else if (!strcmp(mfmt, ASL_MSG_FMT_XML)) mf = MFMT_XML; + else if (!strcmp(mfmt, ASL_MSG_FMT_MSG)) mf = MFMT_MSG; + else if ((!strncmp(mfmt, ASL_MSG_FMT_RAW, 3)) && (mfmt[3] == '.')) + { + mf = MFMT_RAW; + if ((tfmt == NULL) && (mfmt[4] != '\0')) + { + snprintf(tfmt_ext, sizeof(tfmt_ext), "sec.%s", mfmt + 4); + tfmt = (const char *)tfmt_ext; + } + } + else if ((!strncmp(mfmt, ASL_MSG_FMT_STD, 3)) && (mfmt[3] == '.')) + { + mf = MFMT_STD; + if ((tfmt == NULL) && (mfmt[4] != '\0')) + { + snprintf(tfmt_ext, sizeof(tfmt_ext), "lcl.%s", mfmt + 4); + tfmt = (const char *)tfmt_ext; + } + } + else if ((!strncmp(mfmt, ASL_MSG_FMT_BSD, 3)) && (mfmt[3] == '.')) + { + mf = MFMT_BSD; + if ((tfmt == NULL) && (mfmt[4] != '\0')) + { + snprintf(tfmt_ext, sizeof(tfmt_ext), "lcl.%s", mfmt + 4); + tfmt = (const char *)tfmt_ext; + } + } + else mf = MFMT_STR; + + nano = NULL; + asl_msg_lookup(msg, ASL_KEY_TIME_NSEC, &nano, NULL); + + if (mf == MFMT_RAW) + { + str = asl_msg_to_string_raw(text_encoding, msg, tfmt); + asl_string_append_char_no_encoding(str, '\n'); + + *len = asl_string_length(str); + out = asl_string_free_return_bytes(str); + return out; + } + + if (mf == MFMT_MSG) + { + vmessage = NULL; + + if (asl_msg_lookup(msg, ASL_KEY_MSG, &vmessage, NULL) != 0) return NULL; + + str = asl_string_new(text_encoding); + if (str == NULL) return NULL; + + asl_string_append(str, vmessage); + asl_string_append_char_no_encoding(str, '\n'); + + *len = asl_string_length(str); + out = asl_string_free_return_bytes(str); + return out; + } + + if ((mf == MFMT_STD) || (mf == MFMT_BSD)) + { + /* COMMON: Mth dd hh:mm:ss host sender[pid] (refproc[refpid])*/ + /* BSD: : message */ + /* STD: : message */ + + v = NULL; + vtime = NULL; + vhost = NULL; + vsender = NULL; + vpid = NULL; + vmessage = NULL; + vlevel = NULL; + vrefproc = NULL; + vrefpid = NULL; + + if (asl_msg_lookup(msg, ASL_KEY_TIME, &v, NULL) == 0) + { + vtime = _asl_time_string(tfmt, v, nano); + } + + level = 7; + if (asl_msg_lookup(msg, ASL_KEY_LEVEL, &vlevel, NULL) == 0) + { + if (vlevel != NULL) level = atoi(vlevel); + } + + if (asl_msg_lookup(msg, ASL_KEY_HOST, &vhost, NULL) == 0) + { + if (vhost == NULL) vhost = "unknown"; + } + + if (asl_msg_lookup(msg, ASL_KEY_SENDER, &vsender, NULL) == 0) + { + if (vsender == NULL) vsender = "unknown"; + } + + asl_msg_lookup(msg, ASL_KEY_PID, &vpid, NULL); + asl_msg_lookup(msg, ASL_KEY_MSG, &vmessage, NULL); + asl_msg_lookup(msg, ASL_KEY_REF_PROC, &vrefproc, NULL); + asl_msg_lookup(msg, ASL_KEY_REF_PID, &vrefpid, NULL); + + /* COMMON */ + str = asl_string_new(text_encoding); + if (str == NULL) return NULL; + + if (vtime != NULL) + { + asl_string_append(str, vtime); + free(vtime); + } + else + { + asl_string_append_char_no_encoding(str, '0'); + } + + asl_string_append_char_no_encoding(str, ' '); + asl_string_append(str, vhost); + asl_string_append_char_no_encoding(str, ' '); + asl_string_append(str, vsender); + + if ((vpid != NULL) && (strcmp(vpid, "-1"))) + { + asl_string_append_char_no_encoding(str, '['); + asl_string_append(str, vpid); + asl_string_append_char_no_encoding(str, ']'); + } + + if ((vrefproc != NULL) || (vrefpid != NULL)) asl_string_append_no_encoding(str, " ("); + + if (vrefproc != NULL) asl_string_append(str, vrefproc); + if (vrefpid != NULL) + { + asl_string_append_char_no_encoding(str, '['); + asl_string_append(str, vrefpid); + asl_string_append_char_no_encoding(str, ']'); + } + + if ((vrefproc != NULL) || (vrefpid != NULL)) asl_string_append_char_no_encoding(str, ')'); + + if (mf == MFMT_STD) + { + asl_string_append_no_encoding(str, " <"); + asl_string_append(str, _asl_level_string(level)); + asl_string_append_char_no_encoding(str, '>'); + } + + asl_string_append_no_encoding(str, ": "); + if (vmessage != NULL) asl_string_append(str, vmessage); + asl_string_append_char_no_encoding(str, '\n'); + + *len = asl_string_length(str); + out = asl_string_free_return_bytes(str); + return out; + } + + if (mf == MFMT_XML) + { + str = asl_string_new(text_encoding); + if (str == NULL) return NULL; + + asl_string_append_char_no_encoding(str, '\t'); + asl_string_append_no_encoding(str, ""); + asl_string_append_char_no_encoding(str, '\n'); + + for (x = asl_msg_fetch(msg, 0, &key, &val, NULL); x != IndexNull; x = asl_msg_fetch(msg, x, &key, &val, NULL)) + { + if (asl_is_utf8(key) == 1) + { + asl_string_append_xml_tag(str, "key", key); + if (!strcmp(key, ASL_KEY_TIME)) + { + vtime = _asl_time_string(tfmt, val, nano); + if (vtime != NULL) + { + asl_string_append_xml_tag(str, "string", vtime); + free(vtime); + } + else + { + asl_string_append_xml_tag(str, "string", "0"); + } + } + else + { + if (asl_is_utf8(val) == 1) asl_string_append_xml_tag(str, "string", val); + else + { + b64 = asl_b64_encode((uint8_t *)val, strlen(val)); + asl_string_append_xml_tag(str, "data", (char *)b64); + free(b64); + } + } + } + } + + asl_string_append_char_no_encoding(str, '\t'); + asl_string_append_no_encoding(str, ""); + asl_string_append_char_no_encoding(str, '\n'); + + *len = asl_string_length(str); + out = asl_string_free_return_bytes(str); + return out; + } + + /* + * Custom format + * The format string may contain arbitrary characters. + * Keys are identified by $Key or $(Key). The value for + * that key is substituted. If there are alterate formats + * for the value (for example a time may be formatted as + * raw seconds, in UTC, or a local timezone), then the + * key may be $((Key)(Format)). "\$" prints a plain "$". + */ + + str = asl_string_new(text_encoding); + if (str == NULL) return NULL; + + /* + * We need enough space to copy any keys found in mfmt. + * The key obviously can't be longer than strlen(mfmt), + * in fact, keys must be shorter, since there's at least a '$' + * in front of the key, so we allocate a buffer with strlen(mfmt). + * If strlen(mfmt) <= sizeof(skey), we use skey to avoid a malloc. + */ + + x = strlen(mfmt); + if (x <= sizeof(skey)) + { + k = skey; + } + else + { + k = malloc(x); + if (k == NULL) return NULL; + } + + cursor = 0; + + for (i = 0; mfmt[i] != '\0'; i++) + { + if (mfmt[i] == '$') + { + paren = 0; + + /* scan key, (key) or ((key)(format)) */ + for (j = i + 1; mfmt[j] != 0; j++) + { + if (mfmt[j] == '(') + { + paren++; + } + else if (mfmt[j] == ')') + { + if (paren > 0) paren--; + if (paren == 0) + { + j++; + break; + } + } + else if (((mfmt[j] == ' ') || (mfmt[j] == '\t')) && (paren == 0)) break; + } + + /* mfmt[i + 1] is the first char of the key or a '('. mfmt[j] is one char past the end. */ + l = j - (i + 1); + memcpy(k, mfmt+i+1, l); + k[l] = '\0'; + _asl_string_append_value_for_key_format(str, msg, k, tfmt); + + i = j - 1; + continue; + } + + if (mfmt[i] == '\\') + { + i++; + if (mfmt[i] == '$') asl_string_append_char_no_encoding(str, '$'); + else if (mfmt[i] == 'e') asl_string_append_char_no_encoding(str, '\e'); + else if (mfmt[i] == 's') asl_string_append_char_no_encoding(str, ' '); + else if (mfmt[i] == 'a') asl_string_append_char_no_encoding(str, '\a'); + else if (mfmt[i] == 'b') asl_string_append_char_no_encoding(str, '\b'); + else if (mfmt[i] == 'f') asl_string_append_char_no_encoding(str, '\f'); + else if (mfmt[i] == 'n') asl_string_append_char_no_encoding(str, '\n'); + else if (mfmt[i] == 'r') asl_string_append_char_no_encoding(str, '\r'); + else if (mfmt[i] == 't') asl_string_append_char_no_encoding(str, '\t'); + else if (mfmt[i] == 'v') asl_string_append_char_no_encoding(str, '\v'); + else if (mfmt[i] == '\'') asl_string_append_char_no_encoding(str, '\''); + else if (mfmt[i] == '\\') asl_string_append_char_no_encoding(str, '\\'); + else if (isdigit(mfmt[i])) + { + oval = mfmt[i] - '0'; + if (isdigit(mfmt[i+1])) + { + i++; + oval = (oval * 8) + (mfmt[i] - '0'); + if (isdigit(mfmt[i+1])) + { + i++; + oval = (oval * 8) + (mfmt[i] - '0'); + } + } + c = oval; + asl_string_append_char_no_encoding(str, c); + } + continue; + } + + if (mfmt[i] == '\0') break; + asl_string_append_char_no_encoding(str, mfmt[i]); + } + + if (k != skey) free(k); + + asl_string_append_char_no_encoding(str, '\n'); + + *len = asl_string_length(str); + out = asl_string_free_return_bytes(str); + return out; +} + +/* + * OLD ASLMSG COMPATIBILITY + */ +const char * +asl_key(aslmsg msg, uint32_t n) +{ + uint32_t slot, i; + asl_msg_t *page; + + i = 0; + for (page = (asl_msg_t *)msg; page != NULL; page = page->next) + { + for (slot = 0; slot < ASL_MSG_PAGE_SLOTS; slot++) + { + if (page->key[slot] != ASL_MSG_SLOT_FREE) + { + if (i == n) return _asl_msg_slot_key(page, slot); + i++; + } + } + } + + return NULL; +} + +aslmsg +asl_new(uint32_t type) +{ + return (aslmsg)asl_msg_new(type); +} + +int +asl_set(aslmsg msg, const char *key, const char *value) +{ + return asl_msg_set_key_val_op((asl_msg_t *)msg, key, value, IndexNull); +} + +int +asl_set_query(aslmsg msg, const char *key, const char *value, uint32_t op) +{ + return asl_msg_set_key_val_op((asl_msg_t *)msg, key, value, op); +} + +int +asl_unset(aslmsg msg, const char *key) +{ + asl_msg_unset((asl_msg_t *)msg, key); + return 0; +} + +const char * +asl_get(aslmsg msg, const char *key) +{ + const char *val; + int status; + + val = NULL; + status = asl_msg_lookup((asl_msg_t *)msg, key, &val, NULL); + if (status != 0) return NULL; + return val; +} + +void +asl_free(aslmsg msg) +{ + asl_msg_release((asl_msg_t *)msg); +} + +/* aslresponse */ + +/* + * aslresponse_next: Iterate over responses returned from asl_search() + * a: a response returned from asl_search(); + * returns: The next log message (an aslmsg) or NULL on failure + */ +aslmsg +aslresponse_next(aslresponse r) +{ + asl_search_result_t *res; + asl_msg_t *m; + + res = (asl_search_result_t *)r; + if (res == NULL) return NULL; + + if (res->curr >= res->count) return NULL; + m = res->msg[res->curr]; + res->curr++; + + return (aslmsg)m; +} + +/* + * aslresponse_free: Free a response returned from asl_search() + * a: a response returned from asl_search() + */ +void +aslresponse_free(aslresponse r) +{ + asl_search_result_t *res; + uint32_t i; + + res = (asl_search_result_t *)r; + if (res == NULL) return; + + for (i = 0; i < res->count; i++) asl_msg_release(res->msg[i]); + free(res->msg); + free(res); +} diff --git a/libsystem_asl.tproj/src/asl_store.c b/libsystem_asl.tproj/src/asl_store.c new file mode 100644 index 0000000..0640d8f --- /dev/null +++ b/libsystem_asl.tproj/src/asl_store.c @@ -0,0 +1,945 @@ +/* + * Copyright (c) 2007-2011 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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, 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#if TARGET_IPHONE_SIMULATOR +#include +#include +#endif + +extern time_t asl_parse_time(const char *str); +extern uint64_t asl_file_cursor(asl_file_t *s); +extern uint32_t asl_file_match_start(asl_file_t *s, uint64_t start_id, int32_t direction); +extern uint32_t asl_file_match_next(asl_file_t *s, aslresponse query, asl_msg_t **msg, uint64_t *last_id, int32_t direction, int32_t ruid, int32_t rgid); +extern int asl_file_create(const char *path, uid_t uid, gid_t gid, mode_t mode); + +#define SECONDS_PER_DAY 86400 + +/* + * The ASL Store is organized as a set of files in a common directory. + * Files are prefixed by the date (YYYY.MM.DD) of their contents. + * + * Messages with no access controls are saved in YYYY.MM.DD.asl + * Messages with access limited to UID uuu are saved in YYYY.MM.DD.Uuuu.asl + * Messages with access limited to GID ggg are saved in YYYY.MM.DD.Gggg.asl + * Messages with access limited to UID uuu and GID ggg are saved in YYYY.MM.DD.Uuuu.Gggg.asl + * + * Messages that have a value for ASLExpireTime are saved in BB.YYYY.MM.DD.asl + * where the timestamp is the "Best Before" date of the file. Access controls + * are implemented as above with Uuuu and Gggg in the file name. Note that the + * Best Before files are for the last day of the month, so a single file contains + * messages that expire in that month. + * + * An external tool runs daily and deletes "old" files. + */ + +static time_t +_asl_start_today() +{ + time_t now; + struct tm ctm; + + memset(&ctm, 0, sizeof(struct tm)); + now = time(NULL); + + if (localtime_r((const time_t *)&now, &ctm) == NULL) return 0; + + ctm.tm_sec = 0; + ctm.tm_min = 0; + ctm.tm_hour = 0; + + return mktime(&ctm); +} + +/* + * The base directory contains a data file which stores + * the last record ID. + * + * | MAX_ID (uint64_t) | + * + */ +uint32_t +asl_store_open_write(const char *basedir, asl_store_t **s) +{ + asl_store_t *out; + struct stat sb; + uint32_t i, flags; + char *path; + FILE *sd; + uint64_t last_id; + time_t start; + + if (s == NULL) return ASL_STATUS_INVALID_ARG; + + start = _asl_start_today(); + if (start == 0) return ASL_STATUS_FAILED; + + if (basedir == NULL) basedir = PATH_ASL_STORE; + + memset(&sb, 0, sizeof(struct stat)); + if (stat(basedir, &sb) != 0) return ASL_STATUS_INVALID_STORE; + if (!S_ISDIR(sb.st_mode)) return ASL_STATUS_INVALID_STORE; + + path = NULL; + asprintf(&path, "%s/%s", basedir, FILE_ASL_STORE_DATA); + if (path == NULL) return ASL_STATUS_NO_MEMORY; + + sd = NULL; + + memset(&sb, 0, sizeof(struct stat)); + if (stat(path, &sb) != 0) + { + if (errno != ENOENT) + { + free(path); + return ASL_STATUS_FAILED; + } + + sd = fopen(path, "w+"); + free(path); + + if (sd == NULL) return ASL_STATUS_FAILED; + + last_id = 0; + + /* Create new StoreData file (8 bytes ID + 4 bytes flags) */ + + if (fwrite(&last_id, sizeof(uint64_t), 1, sd) != 1) + { + fclose(sd); + return ASL_STATUS_WRITE_FAILED; + } + + flags = 0; + if (fwrite(&flags, sizeof(uint32_t), 1, sd) != 1) + { + fclose(sd); + return ASL_STATUS_WRITE_FAILED; + } + + /* flush data */ + fflush(sd); + } + else + { + sd = fopen(path, "r+"); + free(path); + + if (sd == NULL) return ASL_STATUS_FAILED; + if (fread(&last_id, sizeof(uint64_t), 1, sd) != 1) + { + fclose(sd); + return ASL_STATUS_READ_FAILED; + } + + last_id = asl_core_ntohq(last_id); + } + + out = (asl_store_t *)calloc(1, sizeof(asl_store_t)); + if (out == NULL) + { + fclose(sd); + return ASL_STATUS_NO_MEMORY; + } + + if (basedir == NULL) out->base_dir = strdup(PATH_ASL_STORE); + else out->base_dir = strdup(basedir); + + if (out->base_dir == NULL) + { + fclose(sd); + free(out); + return ASL_STATUS_NO_MEMORY; + } + + out->start_today = start; + out->start_tomorrow = out->start_today + SECONDS_PER_DAY; + out->storedata = sd; + out->next_id = last_id + 1; + + for (i = 0; i < FILE_CACHE_SIZE; i++) + { + memset(&out->file_cache[i], 0, sizeof(asl_cached_file_t)); + out->file_cache[i].u = -1; + out->file_cache[i].g = -1; + } + + *s = out; + return ASL_STATUS_OK; +} + +uint32_t +asl_store_statistics(asl_store_t *s, aslmsg *msg) +{ + aslmsg out; + + if (s == NULL) return ASL_STATUS_INVALID_STORE; + if (msg == NULL) return ASL_STATUS_INVALID_ARG; + + out = asl_new(ASL_TYPE_MSG); + if (out == NULL) return ASL_STATUS_NO_MEMORY; + + /* does nothing for now */ + + *msg = out; + return ASL_STATUS_OK; +} + +uint32_t +asl_store_open_read(const char *basedir, asl_store_t **s) +{ + asl_store_t *out; + struct stat sb; + + if (s == NULL) return ASL_STATUS_INVALID_ARG; + + if (basedir == NULL) basedir = PATH_ASL_STORE; + + memset(&sb, 0, sizeof(struct stat)); + if (stat(basedir, &sb) != 0) return ASL_STATUS_INVALID_STORE; + if (!S_ISDIR(sb.st_mode)) return ASL_STATUS_INVALID_STORE; + + out = (asl_store_t *)calloc(1, sizeof(asl_store_t)); + if (out == NULL) return ASL_STATUS_NO_MEMORY; + + if (basedir == NULL) out->base_dir = strdup(PATH_ASL_STORE); + else out->base_dir = strdup(basedir); + + if (out->base_dir == NULL) + { + free(out); + return ASL_STATUS_NO_MEMORY; + } + + *s = out; + return ASL_STATUS_OK; +} + +uint32_t +asl_store_max_file_size(asl_store_t *s, size_t max) +{ + if (s == NULL) return ASL_STATUS_INVALID_STORE; + + s->max_file_size = max; + return ASL_STATUS_OK; +} + +__private_extern__ void +asl_store_file_closeall(asl_store_t *s) +{ + uint32_t i; + + if (s == NULL) return; + + for (i = 0; i < FILE_CACHE_SIZE; i++) + { + if (s->file_cache[i].f != NULL) asl_file_close(s->file_cache[i].f); + s->file_cache[i].f = NULL; + if (s->file_cache[i].path != NULL) free(s->file_cache[i].path); + s->file_cache[i].path = NULL; + s->file_cache[i].u = -1; + s->file_cache[i].g = -1; + s->file_cache[i].bb = 0; + s->file_cache[i].ts = 0; + } +} + +uint32_t +asl_store_close(asl_store_t *s) +{ + if (s == NULL) return ASL_STATUS_OK; + + if (s->base_dir != NULL) free(s->base_dir); + s->base_dir = NULL; + asl_store_file_closeall(s); + if (s->storedata != NULL) fclose(s->storedata); + + free(s); + + return ASL_STATUS_OK; +} + +/* + * Sweep the file cache. + * Close any files that have not been used in the last FILE_CACHE_TTL seconds. + * Returns least recently used or unused cache slot. + */ +static uint32_t +asl_store_file_cache_lru(asl_store_t *s, time_t now, uint32_t ignorex) +{ + time_t min; + uint32_t i, x; + + if (s == NULL) return 0; + + x = 0; + min = now - FILE_CACHE_TTL; + + for (i = 0; i < FILE_CACHE_SIZE; i++) + { + if ((i != ignorex) && (s->file_cache[i].ts < min)) + { + asl_file_close(s->file_cache[i].f); + s->file_cache[i].f = NULL; + if (s->file_cache[i].path != NULL) free(s->file_cache[i].path); + s->file_cache[i].path = NULL; + s->file_cache[i].u = -1; + s->file_cache[i].g = -1; + s->file_cache[i].bb = 0; + s->file_cache[i].ts = 0; + } + + if (s->file_cache[i].ts < s->file_cache[x].ts) x = i; + } + + return x; +} + +uint32_t +asl_store_sweep_file_cache(asl_store_t *s) +{ + if (s == NULL) return ASL_STATUS_INVALID_STORE; + + asl_store_file_cache_lru(s, time(NULL), FILE_CACHE_SIZE); + return ASL_STATUS_OK; +} + +static char * +asl_store_make_ug_path(const char *dir, const char *base, const char *ext, uid_t ruid, gid_t rgid, uid_t *u, gid_t *g, mode_t *m) +{ + char *path = NULL; + + *u = 0; + *g = 0; + *m = 0644; + + if (ruid == -1) + { + if (rgid == -1) + { + if (ext == NULL) asprintf(&path, "%s/%s", dir, base); + else asprintf(&path, "%s/%s.%s", dir, base, ext); + } + else + { + *g = rgid; + *m = 0600; + if (ext == NULL) asprintf(&path, "%s/%s.G%d", dir, base, *g); + else asprintf(&path, "%s/%s.G%d.%s", dir, base, *g, ext); + } + } + else + { + *u = ruid; + if (rgid == -1) + { + *m = 0600; + if (ext == NULL) asprintf(&path, "%s/%s.U%d", dir, base, *u); + else asprintf(&path, "%s/%s.U%d.%s", dir, base, *u, ext); + } + else + { + *g = rgid; + *m = 0600; + if (ext == NULL) asprintf(&path, "%s/%s.U%d.G%d", dir, base, *u, *g); + else asprintf(&path, "%s/%s.U%d.G%u.%s", dir, base, *u, *g, ext); + } + } + + return path; +} + +static uint32_t +asl_store_file_open_write(asl_store_t *s, char *tstring, int32_t ruid, int32_t rgid, time_t bb, asl_file_t **f, time_t now, uint32_t check_cache) +{ + char *path; + mode_t m; + int32_t i, x; + uid_t u; + gid_t g; + uint32_t status; + asl_file_t *out; + + if (s == NULL) return ASL_STATUS_INVALID_STORE; + + /* see if the file is already open and in the cache */ + for (i = 0; i < FILE_CACHE_SIZE; i++) + { + if ((s->file_cache[i].u == ruid) && (s->file_cache[i].g == rgid) && (s->file_cache[i].bb == bb) && (s->file_cache[i].f != NULL)) + { + s->file_cache[i].ts = now; + *f = s->file_cache[i].f; + if (check_cache == 1) asl_store_file_cache_lru(s, now, i); + return ASL_STATUS_OK; + } + } + + u = 0; + g = 0; + m = 0644; + path = asl_store_make_ug_path(s->base_dir, tstring, "asl", (uid_t)ruid, (gid_t)rgid, &u, &g, &m); + if (path == NULL) return ASL_STATUS_NO_MEMORY; + + out = NULL; + status = asl_file_open_write(path, m, u, g, &out); + if (status != ASL_STATUS_OK) + { + free(path); + return status; + } + + x = asl_store_file_cache_lru(s, now, FILE_CACHE_SIZE); + if (s->file_cache[x].f != NULL) asl_file_close(s->file_cache[x].f); + if (s->file_cache[x].path != NULL) free(s->file_cache[x].path); + + s->file_cache[x].f = out; + s->file_cache[x].path = path; + s->file_cache[x].u = ruid; + s->file_cache[x].g = rgid; + s->file_cache[x].bb = bb; + s->file_cache[x].ts = time(NULL); + + *f = out; + + return ASL_STATUS_OK; +} + +__private_extern__ char * +asl_store_file_path(asl_store_t *s, asl_file_t *f) +{ + uint32_t i; + + if (s == NULL) return NULL; + + for (i = 0; i < FILE_CACHE_SIZE; i++) + { + if (s->file_cache[i].f == f) + { + if (s->file_cache[i].path == NULL) return NULL; + return strdup(s->file_cache[i].path); + } + } + + return NULL; +} + +__private_extern__ void +asl_store_file_close(asl_store_t *s, asl_file_t *f) +{ + uint32_t i; + + if (s == NULL) return; + if (f == NULL) return; + + for (i = 0; i < FILE_CACHE_SIZE; i++) + { + if (s->file_cache[i].f == f) + { + asl_file_close(s->file_cache[i].f); + s->file_cache[i].f = NULL; + if (s->file_cache[i].path != NULL) free(s->file_cache[i].path); + s->file_cache[i].path = NULL; + s->file_cache[i].u = -1; + s->file_cache[i].g = -1; + s->file_cache[i].bb = 0; + s->file_cache[i].ts = 0; + return; + } + } +} + +uint32_t +asl_store_save(asl_store_t *s, aslmsg msg) +{ + struct tm ctm; + time_t msg_time, now, bb; + char *path, *tmp_path, *tstring, *scratch; + const char *val; + uid_t ruid; + gid_t rgid; + asl_file_t *f; + uint32_t status, check_cache, trigger_aslmanager, len; + uint64_t xid, ftime; + size_t fsize; + + if (s == NULL) return ASL_STATUS_INVALID_STORE; + if (msg == NULL) return ASL_STATUS_INVALID_ARG; + + now = time(NULL); + + check_cache = 0; + if ((s->last_write + FILE_CACHE_TTL) <= now) check_cache = 1; + + trigger_aslmanager = 0; + + msg_time = 0; + val = asl_get(msg, ASL_KEY_TIME); + if (val == NULL) msg_time = now; + else msg_time = asl_parse_time(val); + + if (msg_time >= s->start_tomorrow) + { + if (now >= s->start_tomorrow) + { + /* new day begins */ + check_cache = 0; + asl_store_file_closeall(s); + + /* + * _asl_start_today should never fail, but if it does, + * just push forward one day. That will probably be correct, and if + * it isn't, the next message that gets saved will push it ahead again + * until we get to the right date. + */ + s->start_today = _asl_start_today(); + if (s->start_today == 0) s->start_today = s->start_tomorrow; + + s->start_tomorrow = s->start_today + SECONDS_PER_DAY; + } + } + + val = asl_get(msg, ASL_KEY_READ_UID); + ruid = -1; + if (val != NULL) ruid = atoi(val); + + val = asl_get(msg, ASL_KEY_READ_GID); + rgid = -1; + if (val != NULL) rgid = atoi(val); + + bb = 0; + val = asl_get(msg, ASL_KEY_EXPIRE_TIME); + if (val != NULL) + { + bb = 1; + msg_time = asl_parse_time(val); + } + + if (fseeko(s->storedata, 0, SEEK_SET) != 0) return ASL_STATUS_WRITE_FAILED; + + xid = asl_core_htonq(s->next_id); + if (fwrite(&xid, sizeof(uint64_t), 1, s->storedata) != 1) return ASL_STATUS_WRITE_FAILED; + + /* flush data */ + fflush(s->storedata); + + xid = s->next_id; + s->next_id++; + + s->last_write = now; + + if (localtime_r((const time_t *)&msg_time, &ctm) == NULL) return ASL_STATUS_FAILED; + + tstring = NULL; + if (bb == 1) + { + /* + * This supports 12 monthly "Best Before" buckets. + * We advance the actual expiry time to day zero of the following month. + * mktime() is clever enough to know that you actually mean the last day + * of the previous month. What we get back from localtime is the last + * day of the month in which the message expires, which we use in the name. + */ + ctm.tm_sec = 0; + ctm.tm_min = 0; + ctm.tm_hour = 0; + ctm.tm_mday = 0; + ctm.tm_mon += 1; + + bb = mktime(&ctm); + + if (localtime_r((const time_t *)&bb, &ctm) == NULL) return ASL_STATUS_FAILED; + asprintf(&tstring, "BB.%d.%02d.%02d", ctm.tm_year + 1900, ctm.tm_mon + 1, ctm.tm_mday); + } + else + { + asprintf(&tstring, "%d.%02d.%02d", ctm.tm_year + 1900, ctm.tm_mon + 1, ctm.tm_mday); + } + + if (tstring == NULL) return ASL_STATUS_NO_MEMORY; + + status = asl_store_file_open_write(s, tstring, ruid, rgid, bb, &f, now, check_cache); + free(tstring); + tstring = NULL; + + if (status != ASL_STATUS_OK) return status; + + status = asl_file_save(f, msg, &xid); + if (status != ASL_STATUS_OK) return status; + + fsize = asl_file_size(f); + ftime = asl_file_ctime(f); + + /* if file is larger than max_file_size, rename it and trigger aslmanager */ + if ((s->max_file_size != 0) && (fsize > s->max_file_size)) + { + trigger_aslmanager = 1; + status = ASL_STATUS_OK; + + path = asl_store_file_path(s, f); + + asl_store_file_close(s, f); + + if (path != NULL) + { + tmp_path = NULL; + + len = strlen(path); + if ((len >= 4) && (!strcmp(path + len - 4, ".asl"))) + { + /* rename xxxxxxx.asl to xxxxxxx.timestamp.asl */ + scratch = strdup(path); + if (scratch != NULL) + { + scratch[len - 4] = '\0'; + asprintf(&tmp_path, "%s.%llu.asl", scratch, ftime); + free(scratch); + + } + } + else + { + /* append timestamp */ + asprintf(&tmp_path, "%s.%llu", path, ftime); + } + + if (tmp_path == NULL) + { + status = ASL_STATUS_NO_MEMORY; + } + else + { + if (rename(path, tmp_path) != 0) status = ASL_STATUS_FAILED; + free(tmp_path); + } + + free(path); + } + } + + if (trigger_aslmanager != 0) asl_trigger_aslmanager(); + + return status; +} + +static uint32_t +asl_store_mkdir(asl_store_t *s, const char *dir, mode_t m) +{ + char *tstring = NULL; + int status; + struct stat sb; + + asprintf(&tstring, "%s/%s", s->base_dir, dir); + if (tstring == NULL) return ASL_STATUS_NO_MEMORY; + + memset(&sb, 0, sizeof(struct stat)); + status = stat(tstring, &sb); + + if (status == 0) + { + /* must be a directory */ + if (!S_ISDIR(sb.st_mode)) + { + free(tstring); + return ASL_STATUS_INVALID_STORE; + } + } + else + { + if (errno == ENOENT) + { + /* doesn't exist - create it */ + if (mkdir(tstring, m) != 0) + { + free(tstring); + return ASL_STATUS_WRITE_FAILED; + } + } + else + { + /* stat failed for some other reason */ + free(tstring); + return ASL_STATUS_FAILED; + } + } + + free(tstring); + return ASL_STATUS_OK; +} + +uint32_t +asl_store_open_aux(asl_store_t *s, aslmsg msg, int *out_fd, char **url) +{ + struct tm ctm; + time_t msg_time, bb; + char *path, *dir, *tstring; + const char *val; + uid_t ruid, u; + gid_t rgid, g; + mode_t m; + uint32_t status; + uint64_t fid; + int fd; + + if (s == NULL) return ASL_STATUS_INVALID_STORE; + if (msg == NULL) return ASL_STATUS_INVALID_ARG; + if (out_fd == NULL) return ASL_STATUS_INVALID_ARG; + if (url == NULL) return ASL_STATUS_INVALID_ARG; + + msg_time = time(NULL); + + val = asl_get(msg, ASL_KEY_READ_UID); + ruid = -1; + if (val != NULL) ruid = atoi(val); + + val = asl_get(msg, ASL_KEY_READ_GID); + rgid = -1; + if (val != NULL) rgid = atoi(val); + + bb = 0; + val = asl_get(msg, ASL_KEY_EXPIRE_TIME); + if (val != NULL) + { + bb = 1; + msg_time = asl_parse_time(val); + } + + if (localtime_r((const time_t *)&msg_time, &ctm) == NULL) return ASL_STATUS_FAILED; + + dir = NULL; + if (bb == 1) + { + /* + * This supports 12 monthly "Best Before" buckets. + * We advance the actual expiry time to day zero of the following month. + * mktime() is clever enough to know that you actually mean the last day + * of the previous month. What we get back from localtime is the last + * day of the month in which the message expires, which we use in the name. + */ + ctm.tm_sec = 0; + ctm.tm_min = 0; + ctm.tm_hour = 0; + ctm.tm_mday = 0; + ctm.tm_mon += 1; + + bb = mktime(&ctm); + + if (localtime_r((const time_t *)&bb, &ctm) == NULL) return ASL_STATUS_FAILED; + asprintf(&dir, "BB.AUX.%d.%02d.%02d", ctm.tm_year + 1900, ctm.tm_mon + 1, ctm.tm_mday); + } + else + { + asprintf(&dir, "AUX.%d.%02d.%02d", ctm.tm_year + 1900, ctm.tm_mon + 1, ctm.tm_mday); + } + + if (dir == NULL) return ASL_STATUS_NO_MEMORY; + + status = asl_store_mkdir(s, dir, 0755); + if (status != ASL_STATUS_OK) + { + free(dir); + return status; + } + + fid = s->next_id; + s->next_id++; + tstring = NULL; + + asprintf(&tstring, "%s/%llu", dir, fid); + free(dir); + if (tstring == NULL) return ASL_STATUS_NO_MEMORY; + + u = 0; + g = 0; + m = 0644; + path = asl_store_make_ug_path(s->base_dir, tstring, NULL, ruid, rgid, &u, &g, &m); + free(tstring); + if (path == NULL) return ASL_STATUS_NO_MEMORY; + + fd = asl_file_create(path, u, g, m); + if (fd < 0) + { + free(path); + *out_fd = -1; + return ASL_STATUS_WRITE_FAILED; + } + + /* URL is file:// */ + *url = NULL; + asprintf(url, "file://%s", path); + free(path); + + *out_fd = fd; + + return status; +} + +uint32_t +asl_store_match_timeout(asl_store_t *s, aslresponse query, aslresponse *res, uint64_t *last_id, uint64_t start_id, uint32_t count, int32_t direction, uint32_t usec) +{ + DIR *dp; + struct dirent *dent; + uint32_t status; + asl_file_t *f; + char *path; + asl_file_list_t *files; + + if (s == NULL) return ASL_STATUS_INVALID_STORE; + if (res == NULL) return ASL_STATUS_INVALID_ARG; + + files = NULL; + + /* + * Open all readable files + */ + dp = opendir(s->base_dir); + if (dp == NULL) return ASL_STATUS_READ_FAILED; + + while ((dent = readdir(dp)) != NULL) + { + if (dent->d_name[0] == '.') continue; + + path = NULL; + asprintf(&path, "%s/%s", s->base_dir, dent->d_name); + + /* NB asl_file_open_read will fail if path is NULL, if the file is not an ASL store file, or if it isn't readable */ + status = asl_file_open_read(path, &f); + if (path != NULL) free(path); + if ((status != ASL_STATUS_OK) || (f == NULL)) continue; + + files = asl_file_list_add(files, f); + } + + closedir(dp); + + status = asl_file_list_match_timeout(files, query, res, last_id, start_id, count, direction, usec); + asl_file_list_close(files); + return status; +} + +uint32_t +asl_store_match(asl_store_t *s, aslresponse query, aslresponse *res, uint64_t *last_id, uint64_t start_id, uint32_t count, int32_t direction) +{ + return asl_store_match_timeout(s, query, res, last_id, start_id, count, direction, 0); +} + +uint32_t +asl_store_match_start(asl_store_t *s, uint64_t start_id, int32_t direction) +{ + DIR *dp; + struct dirent *dent; + uint32_t status; + asl_file_t *f; + char *path; + asl_file_list_t *files; + + if (s == NULL) return ASL_STATUS_INVALID_STORE; + + if (s->work != NULL) asl_file_list_match_end(s->work); + s->work = NULL; + + files = NULL; + + /* + * Open all readable files + */ + dp = opendir(s->base_dir); + if (dp == NULL) return ASL_STATUS_READ_FAILED; + + while ((dent = readdir(dp)) != NULL) + { + if (dent->d_name[0] == '.') continue; + + path = NULL; + asprintf(&path, "%s/%s", s->base_dir, dent->d_name); + + /* NB asl_file_open_read will fail if path is NULL, if the file is not an ASL store file, or if it isn't readable */ + status = asl_file_open_read(path, &f); + if (path != NULL) free(path); + if ((status != ASL_STATUS_OK) || (f == NULL)) continue; + + files = asl_file_list_add(files, f); + } + + closedir(dp); + + s->work = asl_file_list_match_start(files, start_id, direction); + if (s->work == NULL) return ASL_STATUS_FAILED; + + return ASL_STATUS_OK; +} + +uint32_t +asl_store_match_next(asl_store_t *s, aslresponse query, aslresponse *res, uint32_t count) +{ + if (s == NULL) return ASL_STATUS_INVALID_STORE; + if (s->work == NULL) return ASL_STATUS_OK; + + return asl_file_list_match_next(s->work, query, res, count); +} + +#if TARGET_IPHONE_SIMULATOR +const char *_path_asl_store(void) { + static char * path; + static dispatch_once_t once; + + dispatch_once(&once, ^{ + char *sim_log_dir = getenv("IPHONE_SIMULATOR_LOG_ROOT"); + assert(sim_log_dir); + + asprintf(&path, "%s/asl", sim_log_dir); + assert(path); + }); + + return path; +} + +const char *_path_asl_archive(void) { + static char * path; + static dispatch_once_t once; + + dispatch_once(&once, ^{ + char *sim_log_dir = getenv("IPHONE_SIMULATOR_LOG_ROOT"); + assert(sim_log_dir); + + asprintf(&path, "%s/asl.archive", sim_log_dir); + assert(path); + }); + + return path; +} +#endif diff --git a/libsystem_asl.tproj/src/asl_util.c b/libsystem_asl.tproj/src/asl_util.c new file mode 100644 index 0000000..4127a80 --- /dev/null +++ b/libsystem_asl.tproj/src/asl_util.c @@ -0,0 +1,272 @@ +/* + * Copyright (c) 2006-2011 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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." + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static uint8_t *b64charset = (uint8_t *)"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +static int +asl_is_utf8_char(const unsigned char *p, int *state, int *ctype) +{ + switch (*state) + { + case 0: + { + *ctype = 0; + + if (*p >= 0x80) + { + *state = 1; + if ((*p >= 0xc2) && (*p <= 0xdf)) *ctype = 1; + else if (*p == 0xe0) *ctype = 2; + else if ((*p >= 0xe1) && (*p <= 0xef)) *ctype = 3; + else if (*p == 0xf0) *ctype = 4; + else if ((*p >= 0xf1) && (*p <= 0xf3)) *ctype = 5; + else if (*p == 0xf4) *ctype = 6; + else return 0; + } + + break; + } + + case 1: + { + switch (*ctype) + { + case 1: + { + if ((*p >= 0x80) && (*p <= 0xbf)) *state = 0; + else return 0; + break; + } + + case 2: + { + if ((*p >= 0xa0) && (*p <= 0xbf)) *state = 2; + else return 0; + break; + } + + case 3: + { + if ((*p >= 0x80) && (*p <= 0xbf)) *state = 2; + else return 0; + break; + } + + case 4: + { + if ((*p >= 0x90) && (*p <= 0xbf)) *state = 2; + else return 0; + break; + } + + case 5: + { + if ((*p >= 0x80) && (*p <= 0xbf)) *state = 2; + else return 0; + break; + } + + case 6: + { + if ((*p >= 0x80) && (*p <= 0x8f)) *state = 2; + else return 0; + break; + } + + default: return 0; + } + + break; + } + + case 2: + { + if ((*ctype >= 2) && (*ctype <= 3)) + { + if ((*p >= 0x80) && (*p <= 0xbf)) *state = 0; + else return 0; + } + else if ((*ctype >= 4) && (*ctype <= 6)) + { + if ((*p >= 0x80) && (*p <= 0xbf)) *state = 3; + else return 0; + } + else + { + return 0; + } + + break; + } + + case 3: + { + if ((*ctype >= 4) && (*ctype <= 6)) + { + if ((*p >= 0x80) && (*p <= 0xbf)) *state = 0; + else return 0; + } + else + { + return 0; + } + + break; + } + + default: return 0; + } + + return 1; +} + +__private_extern__ int +asl_is_utf8(const char *str) +{ + const unsigned char *p; + int flag = 1; + int state = 0; + int ctype = 0; + + if (str == NULL) return flag; + + for (p = (const unsigned char *)str; (*p != '\0') && (flag == 1); p++) + { + flag = asl_is_utf8_char(p, &state, &ctype); + } + + return flag; +} + +__private_extern__ uint8_t * +asl_b64_encode(const uint8_t *buf, size_t len) +{ + uint8_t *out; + uint8_t b; + size_t i0, i1, i2, j, outlen; + + if (buf == NULL) return NULL; + if (len == 0) return NULL; + + outlen = ((len + 2) / 3) * 4; + out = (uint8_t *)malloc(outlen + 1); + if (out == NULL) + { + errno = ENOMEM; + return NULL; + } + + out[outlen] = 0; + + i0 = 0; + i1 = 1; + i2 = 2; + j = 0; + + while (i2 < len) + { + b = buf[i0] >> 2; + out[j++] = b64charset[b]; + + b = ((buf[i0] & 0x03) << 4) | (buf[i1] >> 4); + out[j++] = b64charset[b]; + + b = ((buf[i1] & 0x0f) << 2) | ((buf[i2] & 0xc0) >> 6); + out[j++] = b64charset[b]; + + b = buf[i2] & 0x3f; + out[j++] = b64charset[b]; + + i0 += 3; + i1 = i0 + 1; + i2 = i1 + 1; + } + + if (i0 < len) + { + b = buf[i0] >> 2; + out[j++] = b64charset[b]; + + b = (buf[i0] & 0x03) << 4; + + if (i1 < len) b |= (buf[i1] >> 4); + out[j++] = b64charset[b]; + + if (i1 >= len) + { + out[j++] = '='; + out[j++] = '='; + return out; + } + + b = (buf[i1] & 0x0f) << 2; + out[j++] = b64charset[b]; + out[j++] = '='; + } + + return out; +} + +static xpc_connection_t +_create_aslmanager_connection(void) +{ + xpc_connection_t connection; + + connection = xpc_connection_create_mach_service(ASLMANAGER_SERVICE_NAME, NULL, XPC_CONNECTION_MACH_SERVICE_PRIVILEGED); + xpc_connection_set_event_handler(connection, ^(xpc_object_t xobj) { if (xobj != NULL) {}; }); + xpc_connection_resume(connection); + + return connection; +} + +int +asl_trigger_aslmanager(void) +{ + xpc_connection_t connection = _create_aslmanager_connection(); + if (connection == NULL) return -1; + + xpc_object_t request = xpc_dictionary_create(NULL, NULL, 0); + if (request == NULL) return -1; + + xpc_object_t reply = xpc_connection_send_message_with_reply_sync(connection, request); + + if (reply != NULL) xpc_release(reply); + xpc_release(request); + xpc_release(connection); + return 0; +} diff --git a/libsystem_asl.tproj/src/syslog.c b/libsystem_asl.tproj/src/syslog.c new file mode 100644 index 0000000..cc4110f --- /dev/null +++ b/libsystem_asl.tproj/src/syslog.c @@ -0,0 +1,223 @@ +/* + * Copyright (c) 1999-2010 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +/* + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "asl_private.h" + +#ifdef __STDC__ +#include +#else +#include +#endif + +#define LOG_NO_NOTIFY 0x1000 +extern const char *asl_syslog_faciliy_num_to_name(int n); + +#ifdef BUILDING_VARIANT +__private_extern__ pthread_mutex_t _sl_lock; +__private_extern__ aslclient _sl_asl; +__private_extern__ char *_sl_ident; +__private_extern__ int _sl_fac; +__private_extern__ int _sl_opts; +__private_extern__ int _sl_mask; +#else /* !BUILDING_VARIANT */ +__private_extern__ pthread_mutex_t _sl_lock = PTHREAD_MUTEX_INITIALIZER; +__private_extern__ aslclient _sl_asl = NULL; +__private_extern__ char *_sl_ident = NULL; +__private_extern__ int _sl_fac = 0; +__private_extern__ int _sl_opts = 0; +__private_extern__ int _sl_mask = 0; +#endif /* BUILDING_VARIANT */ + +/* + * syslog, vsyslog -- + * print message on log file; output is intended for syslogd(8). + */ +void +#ifdef __STDC__ +syslog(int pri, const char *fmt, ...) +#else +syslog(pri, fmt, va_alist) + int pri; + char *fmt; + va_dcl +#endif +{ + va_list ap; + +#ifdef __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + vsyslog(pri, fmt, ap); + va_end(ap); +} + +void +vsyslog(int pri, const char *fmt, va_list ap) +{ + int fac; + aslmsg facmsg; + const char *facility; + + facmsg = NULL; + fac = pri & LOG_FACMASK; + if (fac != 0) + { + facility = asl_syslog_faciliy_num_to_name(fac); + if (facility != NULL) + { + facmsg = asl_new(ASL_TYPE_MSG); + asl_set(facmsg, ASL_KEY_FACILITY, facility); + } + } + + pthread_mutex_lock(&_sl_lock); + + /* open syslog ASL client if required */ + if (_sl_asl == NULL) + { + _sl_asl = asl_open(NULL, NULL, ASL_OPT_SYSLOG_LEGACY); + _sl_mask = ASL_FILTER_MASK_UPTO(ASL_LEVEL_DEBUG); + asl_set_filter(_sl_asl, _sl_mask); + } + + asl_vlog(_sl_asl, facmsg, LOG_PRI(pri), fmt, ap); + + pthread_mutex_unlock(&_sl_lock); + + if (facmsg != NULL) asl_free(facmsg); +} + +#ifndef BUILDING_VARIANT + +void +openlog(const char *ident, int opts, int logfac) +{ + const char *facility; + uint32_t asl_opts; + + pthread_mutex_lock(&_sl_lock); + + /* close existing aslclient */ + asl_close(_sl_asl); + _sl_asl = NULL; + + free(_sl_ident); + _sl_ident = NULL; + + /* open with specified parameters */ + + if (ident != NULL) _sl_ident = strdup(ident); + /* NB we allow the strdup to fail silently */ + + _sl_fac = logfac; + facility = asl_syslog_faciliy_num_to_name(_sl_fac); + + _sl_opts = opts; + asl_opts = ASL_OPT_SYSLOG_LEGACY; + + if (_sl_opts & LOG_NO_NOTIFY) asl_opts |= ASL_OPT_NO_REMOTE; + if (_sl_opts & LOG_PERROR) asl_opts |= ASL_OPT_STDERR; + + _sl_mask = ASL_FILTER_MASK_UPTO(ASL_LEVEL_DEBUG); + + _sl_asl = asl_open(_sl_ident, facility, asl_opts); + asl_set_filter(_sl_asl, _sl_mask); + + pthread_mutex_unlock(&_sl_lock); +} + +void +closelog() +{ + pthread_mutex_lock(&_sl_lock); + + if (_sl_asl != NULL) asl_close(_sl_asl); + _sl_asl = NULL; + + if (_sl_ident != NULL) free(_sl_ident); + _sl_ident = NULL; + + pthread_mutex_unlock(&_sl_lock); +} + +/* setlogmask -- set the log mask level */ +int +setlogmask(int mask) +{ + int oldmask; + + if (mask == 0) return _sl_mask; + + pthread_mutex_lock(&_sl_lock); + + _sl_mask = mask; + oldmask = asl_set_filter(_sl_asl, mask); + + pthread_mutex_unlock(&_sl_lock); + + return oldmask; +} + +#endif /* !BUILDING_VARIANT */ diff --git a/syslog.xcodeproj/project.pbxproj b/syslog.xcodeproj/project.pbxproj index a8a52d1..34418a3 100644 --- a/syslog.xcodeproj/project.pbxproj +++ b/syslog.xcodeproj/project.pbxproj @@ -7,9 +7,45 @@ objects = { /* Begin PBXAggregateTarget section */ - 5020A3211098EBDE00982ED6 /* All */ = { + 3F0552BB1614E58B00F729CC /* libasl_Sim */ = { isa = PBXAggregateTarget; - buildConfigurationList = 5020A3311098EC3000982ED6 /* Build configuration list for PBXAggregateTarget "All" */; + buildConfigurationList = 3F0552BC1614E58B00F729CC /* Build configuration list for PBXAggregateTarget "libasl_Sim" */; + buildPhases = ( + 3FFD4404174862F2007DAC1B /* Delete man pages */, + ); + dependencies = ( + 3FFD440917486329007DAC1B /* PBXTargetDependency */, + ); + name = libasl_Sim; + productName = libasl_Sim; + }; + 3F6F440B1613AA3600CA9ADB /* libasl */ = { + isa = PBXAggregateTarget; + buildConfigurationList = 3F6F440C1613AA3700CA9ADB /* Build configuration list for PBXAggregateTarget "libasl" */; + buildPhases = ( + ); + dependencies = ( + 3F6F440F1613AA3B00CA9ADB /* PBXTargetDependency */, + ); + name = libasl; + productName = libasl; + }; + 3FFD43F9174862D0007DAC1B /* executables_Sim */ = { + isa = PBXAggregateTarget; + buildConfigurationList = 3FFD4402174862D0007DAC1B /* Build configuration list for PBXAggregateTarget "executables_Sim" */; + buildPhases = ( + 3FFD440517486314007DAC1B /* Delete man pages */, + 3F5F5B9C17487ADB00C12281 /* Configuration */, + ); + dependencies = ( + 3FFD440717486325007DAC1B /* PBXTargetDependency */, + ); + name = executables_Sim; + productName = All; + }; + 5020A3211098EBDE00982ED6 /* executables */ = { + isa = PBXAggregateTarget; + buildConfigurationList = 5020A3311098EC3000982ED6 /* Build configuration list for PBXAggregateTarget "executables" */; buildPhases = ( ); dependencies = ( @@ -18,15 +54,37 @@ 5020A3281098EBFC00982ED6 /* PBXTargetDependency */, 503A82141098FB9300B0D08A /* PBXTargetDependency */, ); - name = All; + name = executables; productName = All; }; /* End PBXAggregateTarget section */ /* Begin PBXBuildFile section */ + 2D30656E150E6EFF00F31A54 /* asl_common.c in Sources */ = {isa = PBXBuildFile; fileRef = 2D30656C150E6EFF00F31A54 /* asl_common.c */; }; + 2D60F61115657D0F00F2E3F9 /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 2D60F61015657D0F00F2E3F9 /* libz.dylib */; }; + 2D9DEB64150E6FE80059BA61 /* asl_common.h in Headers */ = {isa = PBXBuildFile; fileRef = 2D30656D150E6EFF00F31A54 /* asl_common.h */; }; + 2DCF701A150E97C0002D5E8F /* libaslcommon.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 505ACB9D108FD16400197086 /* libaslcommon.a */; }; + 3F6F43F21613A8E300CA9ADB /* asl.h in Headers */ = {isa = PBXBuildFile; fileRef = 3F6F43CF1613A8E300CA9ADB /* asl.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 3F6F43F31613A8E300CA9ADB /* asl_core.h in Headers */ = {isa = PBXBuildFile; fileRef = 3F6F43D01613A8E300CA9ADB /* asl_core.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 3F6F43F41613A8E300CA9ADB /* asl_file.h in Headers */ = {isa = PBXBuildFile; fileRef = 3F6F43D11613A8E300CA9ADB /* asl_file.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 3F6F43F51613A8E300CA9ADB /* asl_legacy1.h in Headers */ = {isa = PBXBuildFile; fileRef = 3F6F43D21613A8E300CA9ADB /* asl_legacy1.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 3F6F43F61613A8E300CA9ADB /* asl_msg.h in Headers */ = {isa = PBXBuildFile; fileRef = 3F6F43D31613A8E300CA9ADB /* asl_msg.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 3F6F43F71613A8E300CA9ADB /* asl_private.h in Headers */ = {isa = PBXBuildFile; fileRef = 3F6F43D41613A8E300CA9ADB /* asl_private.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 3F6F43F81613A8E300CA9ADB /* asl_store.h in Headers */ = {isa = PBXBuildFile; fileRef = 3F6F43D51613A8E300CA9ADB /* asl_store.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 3F6F43FB1613A8E300CA9ADB /* asl.c in Sources */ = {isa = PBXBuildFile; fileRef = 3F6F43E21613A8E300CA9ADB /* asl.c */; }; + 3F6F43FC1613A8E300CA9ADB /* asl_core.c in Sources */ = {isa = PBXBuildFile; fileRef = 3F6F43E31613A8E300CA9ADB /* asl_core.c */; }; + 3F6F43FD1613A8E300CA9ADB /* asl_fd.c in Sources */ = {isa = PBXBuildFile; fileRef = 3F6F43E41613A8E300CA9ADB /* asl_fd.c */; }; + 3F6F43FE1613A8E300CA9ADB /* asl_file.c in Sources */ = {isa = PBXBuildFile; fileRef = 3F6F43E51613A8E300CA9ADB /* asl_file.c */; }; + 3F6F43FF1613A8E300CA9ADB /* asl_legacy1.c in Sources */ = {isa = PBXBuildFile; fileRef = 3F6F43E61613A8E300CA9ADB /* asl_legacy1.c */; }; + 3F6F44001613A8E300CA9ADB /* asl_msg.c in Sources */ = {isa = PBXBuildFile; fileRef = 3F6F43E71613A8E300CA9ADB /* asl_msg.c */; }; + 3F6F44011613A8E300CA9ADB /* asl_store.c in Sources */ = {isa = PBXBuildFile; fileRef = 3F6F43E81613A8E300CA9ADB /* asl_store.c */; }; + 3F6F44021613A8E300CA9ADB /* asl_util.c in Sources */ = {isa = PBXBuildFile; fileRef = 3F6F43E91613A8E300CA9ADB /* asl_util.c */; }; + 3F6F440A1613A91E00CA9ADB /* asl_ipc.defs in Sources */ = {isa = PBXBuildFile; fileRef = 505ACBA2108FD18400197086 /* asl_ipc.defs */; }; + 3F6F44141613AAA600CA9ADB /* asl.3 in Install man3 */ = {isa = PBXBuildFile; fileRef = 3F6F43D91613A8E300CA9ADB /* asl.3 */; }; + 3FE798E416161F2A00D547B0 /* syslog.c in Sources */ = {isa = PBXBuildFile; fileRef = 3FE798E316161F2A00D547B0 /* syslog.c */; }; + 3FE798E616161F5D00D547B0 /* syslog.3 in Install man3 */ = {isa = PBXBuildFile; fileRef = 3FE798E516161F3A00D547B0 /* syslog.3 */; }; 5020A36B1098EE1D00982ED6 /* libaslcommon.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 505ACB9D108FD16400197086 /* libaslcommon.a */; }; 5020A37F1098EEC400982ED6 /* libaslcommon.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 505ACB9D108FD16400197086 /* libaslcommon.a */; }; - 5020A3821098EEFD00982ED6 /* System.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5020A3811098EEFD00982ED6 /* System.framework */; }; 5039176F1091408B0001165E /* aslmanager.c in Sources */ = {isa = PBXBuildFile; fileRef = 5039176C1091408B0001165E /* aslmanager.c */; }; 503917B81091410E0001165E /* asl_action.c in Sources */ = {isa = PBXBuildFile; fileRef = 503917A61091410E0001165E /* asl_action.c */; }; 503917BA1091410E0001165E /* bb_convert.c in Sources */ = {isa = PBXBuildFile; fileRef = 503917A91091410E0001165E /* bb_convert.c */; }; @@ -54,6 +112,27 @@ /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ + 3F6F440E1613AA3B00CA9ADB /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 3F6F43C91613922800CA9ADB; + remoteInfo = libsystem_asl; + }; + 3FFD440617486325007DAC1B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 5020A3211098EBDE00982ED6; + remoteInfo = executables; + }; + 3FFD440817486329007DAC1B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 3F6F440B1613AA3600CA9ADB; + remoteInfo = libasl; + }; 5020A3231098EBF400982ED6 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; @@ -99,6 +178,18 @@ /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ + 3F6F44131613AA9300CA9ADB /* Install man3 */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 8; + dstPath = /usr/share/man/man3; + dstSubfolderSpec = 0; + files = ( + 3F6F44141613AAA600CA9ADB /* asl.3 in Install man3 */, + 3FE798E616161F5D00D547B0 /* syslog.3 in Install man3 */, + ); + name = "Install man3"; + runOnlyForDeploymentPostprocessing = 1; + }; 503A82631099037D00B0D08A /* Copy Manpage.8 */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 8; @@ -147,8 +238,33 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 2D30656C150E6EFF00F31A54 /* asl_common.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = asl_common.c; sourceTree = ""; }; + 2D30656D150E6EFF00F31A54 /* asl_common.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = asl_common.h; sourceTree = ""; }; + 2D60F61015657D0F00F2E3F9 /* libz.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.dylib; path = usr/lib/libz.dylib; sourceTree = SDKROOT; }; 2DB4DA0A125FC69A001CDC45 /* after_install.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; name = after_install.sh; path = syslogd.tproj/after_install.sh; sourceTree = ""; }; - 5020A3811098EEFD00982ED6 /* System.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = System.framework; path = /System/Library/Frameworks/System.framework; sourceTree = ""; }; + 2DB8178915589D0C004D0BDE /* entitlements.plist */ = {isa = PBXFileReference; lastKnownFileType = file.bplist; name = entitlements.plist; path = util.tproj/entitlements.plist; sourceTree = ""; }; + 3F6F43CA1613922800CA9ADB /* libsystem_asl.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libsystem_asl.dylib; sourceTree = BUILT_PRODUCTS_DIR; }; + 3F6F43CF1613A8E300CA9ADB /* asl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = asl.h; sourceTree = ""; }; + 3F6F43D01613A8E300CA9ADB /* asl_core.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = asl_core.h; sourceTree = ""; }; + 3F6F43D11613A8E300CA9ADB /* asl_file.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = asl_file.h; sourceTree = ""; }; + 3F6F43D21613A8E300CA9ADB /* asl_legacy1.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = asl_legacy1.h; sourceTree = ""; }; + 3F6F43D31613A8E300CA9ADB /* asl_msg.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = asl_msg.h; sourceTree = ""; }; + 3F6F43D41613A8E300CA9ADB /* asl_private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = asl_private.h; sourceTree = ""; }; + 3F6F43D51613A8E300CA9ADB /* asl_store.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = asl_store.h; sourceTree = ""; }; + 3F6F43D91613A8E300CA9ADB /* asl.3 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = asl.3; sourceTree = ""; }; + 3F6F43DF1613A8E300CA9ADB /* README */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = README; sourceTree = ""; }; + 3F6F43E21613A8E300CA9ADB /* asl.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = asl.c; sourceTree = ""; }; + 3F6F43E31613A8E300CA9ADB /* asl_core.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = asl_core.c; sourceTree = ""; }; + 3F6F43E41613A8E300CA9ADB /* asl_fd.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = asl_fd.c; sourceTree = ""; }; + 3F6F43E51613A8E300CA9ADB /* asl_file.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = asl_file.c; sourceTree = ""; }; + 3F6F43E61613A8E300CA9ADB /* asl_legacy1.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = asl_legacy1.c; sourceTree = ""; }; + 3F6F43E71613A8E300CA9ADB /* asl_msg.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = asl_msg.c; sourceTree = ""; }; + 3F6F43E81613A8E300CA9ADB /* asl_store.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = asl_store.c; sourceTree = ""; }; + 3F6F43E91613A8E300CA9ADB /* asl_util.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = asl_util.c; sourceTree = ""; }; + 3FCCB5361749B53D00F8FEBC /* asl_sim.conf */ = {isa = PBXFileReference; lastKnownFileType = text; name = asl_sim.conf; path = syslogd.tproj/asl_sim.conf; sourceTree = ""; }; + 3FE798E316161F2A00D547B0 /* syslog.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = syslog.c; sourceTree = ""; }; + 3FE798E516161F3A00D547B0 /* syslog.3 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = syslog.3; sourceTree = ""; }; + 3FFD43F817485C5B007DAC1B /* libasl.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = libasl.xcconfig; sourceTree = ""; }; 50391766109140450001165E /* aslmanager */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = aslmanager; sourceTree = BUILT_PRODUCTS_DIR; }; 5039176B1091408B0001165E /* aslmanager.8 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = aslmanager.8; path = aslmanager.tproj/aslmanager.8; sourceTree = ""; }; 5039176C1091408B0001165E /* aslmanager.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = aslmanager.c; path = aslmanager.tproj/aslmanager.c; sourceTree = ""; }; @@ -173,9 +289,9 @@ 503917C81091413E0001165E /* syslog */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = syslog; sourceTree = BUILT_PRODUCTS_DIR; }; 503917D0109141530001165E /* syslog.1 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.man; name = syslog.1; path = util.tproj/syslog.1; sourceTree = ""; }; 503917D1109141530001165E /* syslog.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = syslog.c; path = util.tproj/syslog.c; sourceTree = ""; }; - 503A82461099029200B0D08A /* libbsm.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libbsm.dylib; path = /usr/lib/libbsm.dylib; sourceTree = ""; }; + 503A82461099029200B0D08A /* libbsm.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libbsm.dylib; path = usr/lib/libbsm.dylib; sourceTree = SDKROOT; }; 505ACB9D108FD16400197086 /* libaslcommon.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libaslcommon.a; sourceTree = BUILT_PRODUCTS_DIR; }; - 505ACBA2108FD18400197086 /* asl_ipc.defs */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.mig; name = asl_ipc.defs; path = /usr/local/include/asl_ipc.defs; sourceTree = SDKROOT; }; + 505ACBA2108FD18400197086 /* asl_ipc.defs */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.mig; path = asl_ipc.defs; sourceTree = ""; }; 505ACBA3108FD18400197086 /* asl_memory.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = asl_memory.c; sourceTree = ""; }; 505ACBA4108FD18400197086 /* asl_memory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = asl_memory.h; sourceTree = ""; }; 505ACBA5108FD18400197086 /* asl_mini_memory.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = asl_mini_memory.c; sourceTree = ""; }; @@ -183,10 +299,19 @@ /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ + 3F6F43C71613922800CA9ADB /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 50391764109140450001165E /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 2D60F61115657D0F00F2E3F9 /* libz.dylib in Frameworks */, + 2DCF701A150E97C0002D5E8F /* libaslcommon.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -195,7 +320,6 @@ buildActionMask = 2147483647; files = ( 5020A37F1098EEC400982ED6 /* libaslcommon.a in Frameworks */, - 5020A3821098EEFD00982ED6 /* System.framework in Frameworks */, 503A82471099029200B0D08A /* libbsm.dylib in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -225,13 +349,74 @@ 503917691091404D0001165E /* aslmanager */, 503917C41091412B0001165E /* util */, 503917711091409F0001165E /* syslogd */, + 3F6F43CD1613A8E300CA9ADB /* libsystem_asl */, 505ACB9E108FD16400197086 /* Products */, - 5020A3811098EEFD00982ED6 /* System.framework */, + 3FFD43F717485C5B007DAC1B /* xcodeconfig */, 503A82461099029200B0D08A /* libbsm.dylib */, + 2D60F61015657D0F00F2E3F9 /* libz.dylib */, ); name = syslog; sourceTree = ""; }; + 3F6F43CD1613A8E300CA9ADB /* libsystem_asl */ = { + isa = PBXGroup; + children = ( + 3F6F43CE1613A8E300CA9ADB /* include */, + 3F6F43D81613A8E300CA9ADB /* man */, + 3F6F43DF1613A8E300CA9ADB /* README */, + 3F6F43E01613A8E300CA9ADB /* src */, + ); + name = libsystem_asl; + path = libsystem_asl.tproj; + sourceTree = ""; + }; + 3F6F43CE1613A8E300CA9ADB /* include */ = { + isa = PBXGroup; + children = ( + 3F6F43CF1613A8E300CA9ADB /* asl.h */, + 3F6F43D01613A8E300CA9ADB /* asl_core.h */, + 3F6F43D11613A8E300CA9ADB /* asl_file.h */, + 3F6F43D21613A8E300CA9ADB /* asl_legacy1.h */, + 3F6F43D31613A8E300CA9ADB /* asl_msg.h */, + 3F6F43D41613A8E300CA9ADB /* asl_private.h */, + 3F6F43D51613A8E300CA9ADB /* asl_store.h */, + ); + path = include; + sourceTree = ""; + }; + 3F6F43D81613A8E300CA9ADB /* man */ = { + isa = PBXGroup; + children = ( + 3F6F43D91613A8E300CA9ADB /* asl.3 */, + 3FE798E516161F3A00D547B0 /* syslog.3 */, + ); + path = man; + sourceTree = ""; + }; + 3F6F43E01613A8E300CA9ADB /* src */ = { + isa = PBXGroup; + children = ( + 3FE798E316161F2A00D547B0 /* syslog.c */, + 3F6F43E21613A8E300CA9ADB /* asl.c */, + 3F6F43E31613A8E300CA9ADB /* asl_core.c */, + 3F6F43E41613A8E300CA9ADB /* asl_fd.c */, + 3F6F43E51613A8E300CA9ADB /* asl_file.c */, + 3F6F43E61613A8E300CA9ADB /* asl_legacy1.c */, + 3F6F43E71613A8E300CA9ADB /* asl_msg.c */, + 3F6F43E81613A8E300CA9ADB /* asl_store.c */, + 3F6F43E91613A8E300CA9ADB /* asl_util.c */, + ); + path = src; + sourceTree = ""; + }; + 3FFD43F717485C5B007DAC1B /* xcodeconfig */ = { + isa = PBXGroup; + children = ( + 3FFD43F817485C5B007DAC1B /* libasl.xcconfig */, + ); + path = xcodeconfig; + sourceTree = ""; + }; 503917691091404D0001165E /* aslmanager */ = { isa = PBXGroup; children = ( @@ -245,6 +430,7 @@ 503917711091409F0001165E /* syslogd */ = { isa = PBXGroup; children = ( + 3FCCB5361749B53D00F8FEBC /* asl_sim.conf */, 503917A61091410E0001165E /* asl_action.c */, 2DB4DA0A125FC69A001CDC45 /* after_install.sh */, 503917A81091410E0001165E /* asl.conf.5 */, @@ -271,6 +457,7 @@ children = ( 503917D0109141530001165E /* syslog.1 */, 503917D1109141530001165E /* syslog.c */, + 2DB8178915589D0C004D0BDE /* entitlements.plist */, ); name = util; sourceTree = ""; @@ -282,6 +469,7 @@ 50391766109140450001165E /* aslmanager */, 5039177C109140C30001165E /* syslogd */, 503917C81091413E0001165E /* syslog */, + 3F6F43CA1613922800CA9ADB /* libsystem_asl.dylib */, ); name = Products; sourceTree = ""; @@ -289,6 +477,8 @@ 505ACBA1108FD18400197086 /* aslcommon */ = { isa = PBXGroup; children = ( + 2D30656C150E6EFF00F31A54 /* asl_common.c */, + 2D30656D150E6EFF00F31A54 /* asl_common.h */, 505ACBA2108FD18400197086 /* asl_ipc.defs */, 505ACBA3108FD18400197086 /* asl_memory.c */, 505ACBA4108FD18400197086 /* asl_memory.h */, @@ -301,6 +491,20 @@ /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ + 3F6F43C81613922800CA9ADB /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 3F6F43F21613A8E300CA9ADB /* asl.h in Headers */, + 3F6F43F31613A8E300CA9ADB /* asl_core.h in Headers */, + 3F6F43F41613A8E300CA9ADB /* asl_file.h in Headers */, + 3F6F43F51613A8E300CA9ADB /* asl_legacy1.h in Headers */, + 3F6F43F61613A8E300CA9ADB /* asl_msg.h in Headers */, + 3F6F43F71613A8E300CA9ADB /* asl_private.h in Headers */, + 3F6F43F81613A8E300CA9ADB /* asl_store.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 505ACB99108FD16400197086 /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; @@ -308,12 +512,31 @@ 505ACBA8108FD18400197086 /* asl_ipc.defs in Headers */, 505ACBAA108FD18400197086 /* asl_memory.h in Headers */, 505ACBAC108FD18400197086 /* asl_mini_memory.h in Headers */, + 2D9DEB64150E6FE80059BA61 /* asl_common.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXHeadersBuildPhase section */ /* Begin PBXNativeTarget section */ + 3F6F43C91613922800CA9ADB /* libsystem_asl */ = { + isa = PBXNativeTarget; + buildConfigurationList = 3F6F43CB1613922800CA9ADB /* Build configuration list for PBXNativeTarget "libsystem_asl" */; + buildPhases = ( + 3F6F43C61613922800CA9ADB /* Sources */, + 3F6F43C71613922800CA9ADB /* Frameworks */, + 3F6F43C81613922800CA9ADB /* Headers */, + 3F6F44131613AA9300CA9ADB /* Install man3 */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = libsystem_asl; + productName = libsystem_asl.dylib; + productReference = 3F6F43CA1613922800CA9ADB /* libsystem_asl.dylib */; + productType = "com.apple.product-type.library.dynamic"; + }; 50391765109140450001165E /* aslmanager */ = { isa = PBXNativeTarget; buildConfigurationList = 5039176A1091404D0001165E /* Build configuration list for PBXNativeTarget "aslmanager" */; @@ -394,7 +617,7 @@ 08FB7793FE84155DC02AAC07 /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 0440; + LastUpgradeCheck = 0460; }; buildConfigurationList = 1DEB928908733DD80010E9CD /* Build configuration list for PBXProject "syslog" */; compatibilityVersion = "Xcode 3.2"; @@ -411,16 +634,62 @@ projectDirPath = ""; projectRoot = ""; targets = ( - 5020A3211098EBDE00982ED6 /* All */, + 5020A3211098EBDE00982ED6 /* executables */, + 3FFD43F9174862D0007DAC1B /* executables_Sim */, + 3F6F440B1613AA3600CA9ADB /* libasl */, + 3F0552BB1614E58B00F729CC /* libasl_Sim */, 505ACB9C108FD16400197086 /* aslcommon */, 50391765109140450001165E /* aslmanager */, 503917C71091413E0001165E /* util */, 5039177B109140C30001165E /* syslogd */, + 3F6F43C91613922800CA9ADB /* libsystem_asl */, ); }; /* End PBXProject section */ /* Begin PBXShellScriptBuildPhase section */ + 3F5F5B9C17487ADB00C12281 /* Configuration */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 8; + files = ( + ); + inputPaths = ( + ); + name = Configuration; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 1; + shellPath = /bin/sh; + shellScript = "mkdir -p ${DSTROOT}${SDKROOT}/System/Library/LaunchDaemons\nmv ${DSTROOT}/System/Library/LaunchDaemons/* ${DSTROOT}${SDKROOT}/System/Library/LaunchDaemons/\n\nmkdir -p ${DSTROOT}${SDKROOT}/etc\ncp ${SRCROOT}/syslogd.tproj/asl_sim.conf ${DSTROOT}${SDKROOT}/etc/asl.conf\n"; + }; + 3FFD4404174862F2007DAC1B /* Delete man pages */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 8; + files = ( + ); + inputPaths = ( + ); + name = "Delete man pages"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 1; + shellPath = /bin/sh; + shellScript = "rm -rf ${DSTROOT}/usr/share/man\n"; + }; + 3FFD440517486314007DAC1B /* Delete man pages */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 8; + files = ( + ); + inputPaths = ( + ); + name = "Delete man pages"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 1; + shellPath = /bin/sh; + shellScript = "rm -rf ${DSTROOT}/usr/share/man"; + }; 50A9CB7A10A8D16300AA715E /* after install */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 8; @@ -465,6 +734,23 @@ /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ + 3F6F43C61613922800CA9ADB /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 3F6F43FB1613A8E300CA9ADB /* asl.c in Sources */, + 3F6F43FC1613A8E300CA9ADB /* asl_core.c in Sources */, + 3F6F43FD1613A8E300CA9ADB /* asl_fd.c in Sources */, + 3F6F43FE1613A8E300CA9ADB /* asl_file.c in Sources */, + 3F6F43FF1613A8E300CA9ADB /* asl_legacy1.c in Sources */, + 3F6F44001613A8E300CA9ADB /* asl_msg.c in Sources */, + 3F6F44011613A8E300CA9ADB /* asl_store.c in Sources */, + 3F6F44021613A8E300CA9ADB /* asl_util.c in Sources */, + 3F6F440A1613A91E00CA9ADB /* asl_ipc.defs in Sources */, + 3FE798E416161F2A00D547B0 /* syslog.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 50391763109140450001165E /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -505,12 +791,28 @@ 503A81D81098FA3900B0D08A /* asl_ipc.defs in Sources */, 505ACBA9108FD18400197086 /* asl_memory.c in Sources */, 505ACBAB108FD18400197086 /* asl_mini_memory.c in Sources */, + 2D30656E150E6EFF00F31A54 /* asl_common.c in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ + 3F6F440F1613AA3B00CA9ADB /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 3F6F43C91613922800CA9ADB /* libsystem_asl */; + targetProxy = 3F6F440E1613AA3B00CA9ADB /* PBXContainerItemProxy */; + }; + 3FFD440717486325007DAC1B /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 5020A3211098EBDE00982ED6 /* executables */; + targetProxy = 3FFD440617486325007DAC1B /* PBXContainerItemProxy */; + }; + 3FFD440917486329007DAC1B /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 3F6F440B1613AA3600CA9ADB /* libasl */; + targetProxy = 3FFD440817486329007DAC1B /* PBXContainerItemProxy */; + }; 5020A3241098EBF400982ED6 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 505ACB9C108FD16400197086 /* aslcommon */; @@ -554,8 +856,50 @@ GCC_PREFIX_HEADER = ""; GCC_WARN_ABOUT_RETURN_TYPE = YES; GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = ( + "$(BUILT_PRODUCTS_DIR)/$(CURRENT_ARCH)", + "$(PROJECT_DIR)/libsystem_asl.tproj/include", + "$(PROJECT_DIR)/aslcommon", + ); USE_HEADERMAP = NO; VERSIONING_SYSTEM = "apple-generic"; + WARNING_CFLAGS = "-Werror=format"; + ZERO_LINK = NO; + }; + name = Release; + }; + 3F0552BD1614E58B00F729CC /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + 3F6F43CC1613922800CA9ADB /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 3FFD43F817485C5B007DAC1B /* libasl.xcconfig */; + buildSettings = { + HEADER_SEARCH_PATHS = ( + "$(inherited)", + "$(SDKROOT)/System/Library/Frameworks/System.framework/PrivateHeaders", + ); + }; + name = Release; + }; + 3F6F440D1613AA3700CA9ADB /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + 3FFD4403174862D0007DAC1B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + COMBINE_HIDPI_IMAGES = YES; + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + PRODUCT_NAME = "executables copy"; ZERO_LINK = NO; }; name = Release; @@ -563,9 +907,10 @@ 5020A3221098EBDF00982ED6 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + COMBINE_HIDPI_IMAGES = YES; COPY_PHASE_STRIP = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - PRODUCT_NAME = All; + PRODUCT_NAME = "$(TARGET_NAME)"; ZERO_LINK = NO; }; name = Release; @@ -580,6 +925,7 @@ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; GCC_DYNAMIC_NO_PIC = NO; INSTALL_PATH = /usr/sbin; + "INSTALL_PATH[sdk=iphonesimulator*]" = "$(SDKROOT)/usr/sbin"; OTHER_CFLAGS = ( "-Wall", "-DINET6", @@ -599,23 +945,21 @@ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; GCC_DYNAMIC_NO_PIC = NO; HEADER_SEARCH_PATHS = ( - "$(BUILT_PRODUCTS_DIR)/$(CURRENT_ARCH)", - "$(PROJECT_DIR)/aslcommon", + "$(inherited)", "$(SDKROOT)/System/Library/Frameworks/System.framework/PrivateHeaders", ); INSTALL_PATH = /usr/sbin; + "INSTALL_PATH[sdk=iphonesimulator*]" = "$(SDKROOT)/usr/sbin"; OTHER_CFLAGS = ( "-Wall", "-DINET6", ); "OTHER_CFLAGS[sdk=iphoneos*][arch=*]" = ( "$(OTHER_CFLAGS)", - "-DCONFIG_IPHONE", "-DLOCKDOWN", ); "OTHER_CFLAGS[sdk=macosx*][arch=*]" = ( "$(OTHER_CFLAGS)", - "-DCONFIG_MAC", "-DREMOTE_IPV4", ); PRODUCT_NAME = syslogd; @@ -627,24 +971,18 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + "CODE_SIGN_ENTITLEMENTS[sdk=iphoneos*]" = util.tproj/entitlements.plist; CODE_SIGN_IDENTITY = "-"; COPY_PHASE_STRIP = YES; DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; GCC_DYNAMIC_NO_PIC = NO; - HEADER_SEARCH_PATHS = ( - "$(BUILT_PRODUCTS_DIR)/$(CURRENT_ARCH)", - "$(PROJECT_DIR)/aslcommon", - ); INSTALL_PATH = /usr/bin; + "INSTALL_PATH[sdk=iphonesimulator*]" = "$(SDKROOT)/usr/bin"; OTHER_CFLAGS = ( "-Wall", "-DINET6", ); - "OTHER_CFLAGS[sdk=iphoneos*][arch=*]" = ( - "$(OTHER_CFLAGS)", - "-DCONFIG_IPHONE", - ); PRODUCT_NAME = syslog; ZERO_LINK = NO; }; @@ -655,15 +993,22 @@ buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CODE_SIGN_IDENTITY = "-"; - COPY_PHASE_STRIP = YES; + COMBINE_HIDPI_IMAGES = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/aslcommon"; - INSTALL_PATH = /usr/local/lib; OTHER_CFLAGS = ( "-Wall", "-D__MigTypeCheck=1", ); + "OTHER_CFLAGS[sdk=iphoneos*][arch=*]" = ( + "$(OTHER_CFLAGS)", + "-DLOCKDOWN", + ); + "OTHER_CFLAGS[sdk=macosx*][arch=*]" = ( + "$(OTHER_CFLAGS)", + "-DREMOTE_IPV4", + ); PRODUCT_NAME = aslcommon; + SKIP_INSTALL = YES; ZERO_LINK = NO; }; name = Release; @@ -679,7 +1024,39 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - 5020A3311098EC3000982ED6 /* Build configuration list for PBXAggregateTarget "All" */ = { + 3F0552BC1614E58B00F729CC /* Build configuration list for PBXAggregateTarget "libasl_Sim" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 3F0552BD1614E58B00F729CC /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 3F6F43CB1613922800CA9ADB /* Build configuration list for PBXNativeTarget "libsystem_asl" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 3F6F43CC1613922800CA9ADB /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 3F6F440C1613AA3700CA9ADB /* Build configuration list for PBXAggregateTarget "libasl" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 3F6F440D1613AA3700CA9ADB /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 3FFD4402174862D0007DAC1B /* Build configuration list for PBXAggregateTarget "executables_Sim" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 3FFD4403174862D0007DAC1B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 5020A3311098EC3000982ED6 /* Build configuration list for PBXAggregateTarget "executables" */ = { isa = XCConfigurationList; buildConfigurations = ( 5020A3221098EBDF00982ED6 /* Release */, diff --git a/syslogd.tproj/after_install.sh b/syslogd.tproj/after_install.sh index 190f6eb..c2406d9 100755 --- a/syslogd.tproj/after_install.sh +++ b/syslogd.tproj/after_install.sh @@ -1,6 +1,22 @@ #! /bin/bash set -e +if [ "${RC_ProjectName%_Sim}" != "${RC_ProjectName}" ] ; then + DESTDIR="${DSTROOT}${SDKROOT}"/System/Library/LaunchDaemons + PLIST="${SRCROOT}"/syslogd.tproj/com.apple.syslogd_sim.plist +else + DESTDIR="${DSTROOT}"/System/Library/LaunchDaemons + PLIST="${SRCROOT}"/syslogd.tproj/com.apple.syslogd.plist +fi + +install -d -m 0755 -o root -g wheel "${DESTDIR}" +install -m 0644 -o root -g wheel "${PLIST}" "${DESTDIR}"/com.apple.syslogd.plist +plutil -convert binary1 "${DESTDIR}"/com.apple.syslogd.plist + +if [ "${RC_ProjectName%_Sim}" != "${RC_ProjectName}" ] ; then + exit 0 +fi + install -d -m 0755 -o root -g wheel "$DSTROOT"/private/var/log/asl PRODUCT=$(xcodebuild -sdk "${SDKROOT}" -version PlatformPath | head -1 | sed 's,^.*/\([^/]*\)\.platform$,\1,') @@ -17,16 +33,3 @@ if [ ${PRODUCT} = iPhone ]; then install -d -m 0755 -o root -g wheel "$DSTROOT"/usr/share/sandbox install -m 0644 -o root -g wheel "$SRCROOT"/syslogd.tproj/syslogd.sb "$DSTROOT"/usr/share/sandbox fi - -DESTDIR="$DSTROOT"/System/Library/LaunchDaemons -install -d -m 0755 -o root -g wheel "$DESTDIR" -install -m 0644 -o root -g wheel "$SRCROOT"/syslogd.tproj/com.apple.syslogd.plist "$DESTDIR" -if [ ${PRODUCT} = iPhoneOS ]; then - /usr/libexec/PlistBuddy \ - -c "Add :POSIXSpawnType string Interactive" \ - "$DESTDIR"/com.apple.syslogd.plist -fi -plutil -convert binary1 "$DESTDIR"/com.apple.syslogd.plist - -mkfile 8 "$DSTROOT"/private/var/log/asl/SweepStore -chmod 0644 "$DSTROOT"/private/var/log/asl/SweepStore diff --git a/syslogd.tproj/asl.conf.5 b/syslogd.tproj/asl.conf.5 index 8d305ac..85617b9 100644 --- a/syslogd.tproj/asl.conf.5 +++ b/syslogd.tproj/asl.conf.5 @@ -31,37 +31,69 @@ and .Sh DESCRIPTION The .Xr syslogd 8 -server reads the -.Nm -file at startup, and re-reads the file whenever it received a HUP signal. +server reads the /etc/asl.conf file at startup, and re-reads the file when it receives a HUP signal. The .Xr aslmanager 8 daemon reads the file when it starts. -See the ASLMANAGER PARAMETER SETTINGS section below for details on those parameter settings. +See the ASLMANAGER PARAMETER SETTINGS section for details on aslmanager-specific parameters. .Pp -The file may contain parameter settings, used in place of (and which will override) command-line options, -and may contain query-action rules that trigger specific actions when +If the /etc/asl directory exists, then .Nm syslogd -receives messages that match the query pattern. +and +.Nm aslmanager +will read each file it contains. +These files must have the same format as +.Nm asl.conf . +Each file configures an independent module, identified by the file name. +Modules may be enabled or disabled independently. +Each module may specify its own set of rules for acting on received messages. +See the ASL MODULES section for details. +.Pp +The files contains four types of lines. +Each type is identified by the first non-whitespace character. +.Pp +.Bl -tag -width "=" -compact +.It = +Parameter settings +.It ? +Query-action rules +.It > +Output file or directory configuration options +.It # +Comments +.El .Pp -Parameter setting lines in the configuration file begin with an equal sign -.Dq = , -and are generally of the form: +Parameter setting lines in the configuration file are generally of the form: .Pp .Dl = parameter_name value ... .Pp Most parameter settings require a single value, although some may take several values. -See the PARAMETER SETTINGS section below for details. +See the PARAMETER SETTINGS section for details. .Pp -Query-action rules in the file begin with a question-mark ``?'' or a -.Dq Q , -and generally have the form: +Query-action rules in the file generally have the form: .Pp .Dl ? query action ... .Pp -Specific actions may be followed by optional arguments. -See the QUERY-ACTION RULES section below for details. -.Sh PARAMETER SETTINGS +This directs +.Nm syslogd +to perform the specified action when a received message matches the given query. +Actions may be followed by optional arguments. +See the QUERY-ACTION RULES section for details. +.Pp +Most query-action rules specify output files or ASL-format data stores where matching messages should be saved. +The optional parameters for those rules can specify a number of options for these outputs. +As a convenience, these configuration options may be specified on a separate line. +Output configuration settings in the file begin with a greater-than sign +.Dq > +followed by a file or ASL directory name and the configuration options for that file or directory. +These lines generally have the form: +.Pp +.Dl > filename option ... +.Pp +See the OUTPUT CONFIGURATION SETTINGS section for details. +.Pp +Comments lines are ignored. +.Ss PARAMETER SETTINGS The following parameter-settings are recognized by .Nm syslogd . .Pp @@ -74,12 +106,7 @@ The debug parameter requires a value of to enable debug output, or a value of .Dq 0 to disable it. -An option file name may follow the -.Dq 0 -or -.Dq 1 . -If a file name is provided, debug messages are written to that file. -Otherwise, debug writes are treated as log messages. +Debugging messages are written to /var/log/syslogd.log. .Pp .It mark_time Sets the time interval for the mark facility. @@ -96,21 +123,49 @@ Sets the time-to-live for messages used by the utmp, wtmp, and lastlog subsystem The default is 31622400 seconds (approximately 1 year). .Pp .It mps_limit -Sets the per-process message per second quota. +Sets the kernel message per second quota. The default is value is 500. A value of 0 disables the quota mechanism. +Note that this setting only limits the number of kernel messages that will be saved by +.Nm syslogd . +User processes are limited to 36000 messages per hour. +The limit for a user process is not enforced if a remote-control ASL filter is in +place for the process. +See the +.Xr syslog 1 +manual for enabling a remote-control filter using the +.Fl c +option with the +.Nm syslog +command. .Pp .It max_file_size -Sets the maximum file size for individual files in the ASL data store. +Sets the maximum file size for individual files in the ASL database. The default is 25600000 bytes. .El +.Ss QUERY-ACTION RULES +Query-action rules are used to cause +.Nm syslogd +to perform specific actions when received messages match a specified query pattern. +For example, to save certain messages in a file. +The rules are processed in the order in which they appear in the file. +This matters because some actions can affect further processing. +For example, an +.Dq ignore +action causes +.Nm syslogd +to stop processing the rules in a file for messages that match a given query pattern. .Pp -.Sh QUERY-ACTION RULES -Rules contain three components: a query; an action; and optionally, parameters specific to that action. -For example: -.Pp -.Dl ? [= Sender foobar] [<= Level error] notify com.apple.foobar +Query-action rules contain three components: a query, an action, and optional parameters specific to that action. +For example, the following rule matches log messages sent by the +.Dq example +process which have log priority levels in the range emergency to error. +If a received message matches, +.Nm syslogd +posts a BSD notification for the key +.Dq com.example.log_message . .Pp +.Dl ? [= Sender example] [<= Level error] notify com.example.log_message .Ss Query Format Queries comprise one or more message matching components, each of which has the form: .Pp @@ -154,10 +209,10 @@ suffix KEY and VAL are message keys and values. For example .Pp -.Dl [= Sender foobar] +.Dl [= Sender example] .Pp matches any message with value -.Dq foobar +.Dq example for the .Dq Sender key. @@ -166,21 +221,25 @@ The query .Dl [CA= Color gr] .Pp matches any message with a value beginning with the letters GR, Gr, gr, or gR -(C meaning casefold, A meaning prefix) for the +( +.Dq C +meaning casefold, +.Dq A +meaning prefix) for the .Dq Color key. The example query above, .Pp -.Dl [= Sender foobar] [N< Level 3] +.Dl [= Sender example] [N< Level 3] .Pp matches any message from -.Dq foobar +.Dq example with a level numerically less than 3 (string values are converted to integers, and the comparison is done on the integer values). Note that the string values may be used equivalently for the Level key, so the example above may also be written as: .Pp -.Dl [= Sender foobar] [< Level Error] +.Dl [= Sender example] [< Level Error] .Pp String values for levels may be any of the set .Dq emergency , @@ -189,7 +248,8 @@ String values for levels may be any of the set .Dq error , .Dq warning , .Dq notice , -.Dq info , or +.Dq info , +or .Dq debug . These strings may be upper, lower, or mixed case. .Pp @@ -203,10 +263,62 @@ Will match any message that has a .Dq Flavor key, regardless of its value. .Pp +As a special case, the query +.Pp +.Dl * +.Pp +matches all messages. .Ss Actions The following actions are available. .Pp -.Bl -tag -width "store_directory" -compact -offset indent +.Bl -tag -width "directory" -compact -offset indent +.It store +Causes +.Nm syslogd +to save matching messages in the ASL database. +Note that if /etc/asl.conf contains no +.Dq store +action rules, then +.Nm syslogd +will save all messages it receives in the ASL database. +.Pp +.It file +Causes matching messages to be stored in a log file. +The file's path name must follow as the first parameter. +If the path already exists, it must be a plain file. +If the file does not exist, it will be created when the first message is written. +If the pathname specified is not an absolute path, +.Nm syslogd +will treat the given path as relative to /var/log (for /etc/asl.conf), +or for other output modules relative to /var/log/module/NAME where NAME is the module name. +.Pp +By default, the file's owner will be root, and the file will be readable by the admin group. +Various options may follow the file name to specify ownership and access controls, +printed log message format, and controls for file rotation, compression, time-to-live, +and other aspects of output file life-cycle management. +See the OUTPUT CONFIGURATION SETTINGS section for more details. +.Pp +.It directory +Causes matching messages to be stored in an ASL-format log message data store. +A directory path name must follow as the first parameter. +If the path exists, it must be a directory. +.Pp +Messages saved to an ASL directory are saved in files that are named +.Dq yyyy.mm.dd.asl , +where +.Dq yyyy , +.Dq mm , +and +.Dq dd +are the year, month (01 to 12) and day of the month (01 to 31) associated with +matching messages. +This has the effect of saving messages in a separate file for each day. +.Pp +By default, files in the directory will be owned by root, and readable by the admin group. +Various options may follow the directory name to control ownership, access controls, +and the management of the store and its contents. +See the OUTPUT CONFIGURATION SETTINGS section for a list of options that may be set for store directories. +.Pp .It notify Causes .Nm syslogd @@ -216,173 +328,549 @@ The notification key must appear as a single parameter following the .Dq notify action. .Pp +.It skip +Causes a matching message to be ignored in all subsequent matching rules in the file. +Its scope is local to a single module configuration file. +.Pp +.It claim +Messages that match the query associated with a +.Dq claim +action are not processed by the main ASL configuration file /etc/asl.conf. +While claimed messages are not processed by /etc/asl.conf, +they are not completely private. +Other modules may also claim messages, +and in some cases two or more modules may have claim actions +that match the same messages. +This action only blocks processing by /etc/asl.conf. +.Pp +The +.Dq claim +action may be followed by the keyword +.Dq only . +In this case, only those messages that match the +.Dq claim only +query will be processed by subsequent rules in the module. +.Pp .It access -Sets read access controls for messages that match the associated query pattern. +Sets read access controls for messages that match the associated query pattern. .Nm syslogd will restrict read access to matching messages to a specific user and group. The user ID number and group ID number must follow the .Dq access keyword as parameters. .Pp -.It store -Causes -.Nm syslogd -to save matching messages, either in the main ASL data store, -or in a separate log message data store file is a file name is given as a parameter. -A separate data store file may be accessed using the -.Nm syslog -command line utility. -A new file will be created if one does not exist. -If a new file is being created, the UID, GID, and mode of the file may be specified using the options -.Dq uid=UUU , -.Dq gid=GGG , -and -.Dq mode=MMMM , -where UUU and GGG are a user ID and group ID, and MMMM is a -mode specification of the form -.Dq 0644 -with a leading zero for an octal number or DDD for a decimal number. +.It broadcast +Causes syslogd to write the text of matching messages to all terminal windows. +If optional text follows the +.Dq broadcast +keyword, then that text is written rather that the matching message text. +Note that this action is restricted to the main ASL configuration file /etc/asl.conf. .Pp -Two other optional parameters may also follow the pathname. +.It ignore +Causes a matching message to be ignored in all subsequent matching rules in the file. +This action is equivalent to the +.Dq skip +action in all module configuration files except the main ASL configuration file /etc/asl.conf. +When used in the main configuration file, the scope of the action is global, +and matching messages will be ignored by all ASL modules. +.El +.Ss OUTPUT CONFIGURATION SETTINGS +Various options may follow the path name in a +.Dq file +or +.Dq directory +query-action rule. +For example, the following rule specifies that all messages from the +.Dq example +facility will be saved in the file +.Dq example.log , +and that messages are printed in a +.Dq raw +format that shows all the keys and values in the message: .Pp -If a separate log message data store file is specified as a parameter, then -.Nm syslogd -will open the database, save a matching message, and then close the database. -If a high volume of messages is expected, specifying the -.Dq stayopen -option will improve performance. -.Pp -Also, if a separate log message data store file is specified as a parameter, -matching messages will be excluded from all further processing. -Adding the -.Dq continue -option will cause syslogd to save matching messages in the specified store file -and then continue processing matching messages in accordance with the actions -specified in /etc/asl.conf and /etc/syslog.conf. -.Pp -Note that if the -.Nm asl.conf -configuration file contains no matching rules for the main ASL data store, then -.Nm syslogd -will save all messages. +.Dl ? [= Facility example] file example.log format=raw .Pp -.It store_directory -Causes matching messages to be stored in a log message data store file in a separate directory. -The directory path name must follow as the first parameter. -The named directory must exist. -.Nm syslogd -will not create the directory path. +Multiple options may be specified separated by whitespace characters. +For example: .Pp -Messages saved to a store directory are saved in files that are named -.Dq yyyy.mm.dd.asl , -where -.Dq yyyy , -.Dq mm , -and -.Dq dd -are the year, month (01 to 12) and day of the month (01 to 31) associated with -matching messages. -This has the effect of saving messages in a separate file for each day. +.Dl ? [= Facility example] file example.log format=raw rotate=local compress ttl=3 mode=0640 uid=0 gid=5 gid=20 .Pp -The -.Dq uid=UUU , -.Dq gid=GGG , -.Dq mode=MMMM , -and -.Dq continue -options available for the -.Dq store -action -may also be specified for a store directory. -The uid, gid, and mode specification will be used when the individual daily store files are created. +As a convenience, a file or directory name and any associated options can be specified on a separate +output configuration line following a +.Dq > +character: .Pp -.It file -Causes matching messages to be stored in a log file. -The file's path name must follow as the first parameter. -The file's directory must exist. -If the path already exists, it must be a plain file. -Otherwise -.Nm syslogd -will create the file. -The file's owner will be root, and the file's group will be admin. -A file mode may be specified as an option of the form -.Dq mode=MMMM -as described above. -One or more UIDs may be given as the values of options of the form -.Dq uid=UUU . -One or more GIDs may be given as the values of options of the form -.Dq gid=GGG . -If any UIDs or GIDs are provided, the specified users and groups will be given read access to the file. -Note that UIDs and GIDs should be defined in the local Open Directory database, since -.Nm syslogd -starts and may create the log file before network directory services are available. -Unknown UIDs and GIDs will be ignored when setting access controls. +.Dl > example.log format=raw rotate=local compress ttl=3 mode=0640 uid=0 gid=5 gid=20 +.Pp +Options for a file or directory are taken from the first query-action rule +or output configuration line for the given path. +A good usage pattern for multiple rules that specify the same output file or directory is: +.Pp +.Dl > example.log options ... +.Dl ? query1 file example.log +.Dl ? query2 file example.log +.Dl ? query3 file example.log +.Pp +Most of the options listed below may be used with either file or directory outputs. +Exceptions are noted. +.Pp +.Bl -tag -width "coalesce=VAL" -compact -offset indent +.It format=FMT +Controls the format of log messages saved in a file. +Note that this option is specific to file outputs. +It is ignored for ASL directories. .Pp -By default, log files will be written using the same format used for printing by +The format is specified by the value given for FMT. +Several pre-defined formats are available: +.Bl -tag -width "abcd" +.It bsd +Format used by the +.Nm syslogd +daemon for system log files, e.g. /var/log/system.log. +.It std +Standard (default) format. +Similar to +.Dq bsd , +but includes the message priority level. +.It raw +Prints the complete message structure. +Each key/value pair is enclosed in square brackets. +Embedded closing brackets and white space are escaped. +Time stamps are printed as seconds since the epoch. +.It xml +The list of messages is printed as an XML property list. +Each message is represented as a dictionary in a array. +Dictionary keys represent message keys. +Dictionary values are strings. +.It asl +The output file is written as an ASL-format data store file. +Files in this format may be read and searched using the .Nm syslog -when the -.Fl F Ar std -flag is supplied. -A print format may be specified as the value of the -.Dq format=FMT +command line utility with the use of the +.Fl f Ar path option. -The default is -.Dq format=std . -Alternate file formats, including -.Dq bsd -and -.Dq raw -are supported. -Custom formats may be specified as well, using the syntax supported by -.Nm syslog Fl F . -Space and tab character in a custom format string must be escaped with a leading backslash character. -Custom format strings may include variables of the form -.Dq $Name -.Dq $(Name) -or -.Dq $((Name)(fmt)) . -which will be expanded to the associated with the named key. -The first form may be used in most cases. -The second form may be used if the variable is not delimited by whitespace. -The third form permits the selection of alternate output formats for certain keys, -such as Time and Level. -See -.Xr syslog 1 -for details. -.Pp -For example, the option: +.El .Pp -.Dl format=$((Time)(Z))\ $Host\ $(Sender)[$(PID)]\ <$((Level)(str))>:\ $Message +Custom format strings may also be specified. +Since custom formats often contain white-space characters, +the entire string may be enclosed in single or double quote characters, +or each white-space character may be preceded by a backslash escape character. +Escaped characters are not interpreted. +Custom format strings are described in detail in the READING MESSAGES section of the +.Xr syslog 1 +manual. +.Pp +.It mode=MMM +Sets the mode of the file or files within an ASL directory. +The value MMM may be specified as a decimal value, +a hexadecimal value (if preceded by ``0x''), +or octal value (if preceded by ``0''). +.Pp +.It uid=UUU +Specifies the file's owner. +If more than one +.Dq uid=UUU +option is given, the first will be used to set ownership, +and subsequent user IDs will be given read access to in the files POSIX.1e ACLs. +Note that UIDs should be defined in the local Open Directory database, since +.Nm syslogd +starts and may create the log file before network directory services are available. +Unknown UIDs and GIDs will be ignored when setting access controls. .Pp -produces output similar to the -.Dq std -format, but using the UTC (Zulu) timezone. +.It gid=GGG +Specifies the file's group. +If more than one +.Dq gid=GGG +option is given, the first will be used to set the file's group, +and subsequent group IDs will be given read access to in the files POSIX.1e ACLs. +As with UID=UUU options, groups should be defined in the local Open Directory database. .Pp +.It coalesce=VAL By default, files printed using the .Dq bsd and .Dq std -formats will suppress printing duplicates. +formats will coalesce duplicates. If two or more messages are logged within 30 seconds, and which differ only in time, then the second and subsequent messages will not be printed. When a different message is logged, or 30 seconds have elapsed since the initial message was logged, a line with the text .Dl --- last message repeated N times --- will be added to the file. -The default may be disabled using the -.Dq no_dup_supress +The default is +.Dq coalesce=1 . +The default may be overridden by specifying +.Dq coalesce=0 . +The values +.Dq off +and +.Dq false +may be used in place of +.Dq 0 . +.El +.Pp +The following options all deal with file rotation and life-cycle management. +The FILE ROTATION section describes this in detail. +.Pp +.Bl -tag -width "rotate=NAME_STYLE" -compact -offset indent +.Pp +.It rotate=NAME_STYLE +Enables log file rotation and specifies the file naming scheme for rotated files. +This option does not apply to ASL directories. +Four styles are supported: +.Pp +.Bl -tag -width "local-basic" -compact -indent +.It sec +Rotated file names are of the form +.Dq example.log.T1340607600 . +The file names include the creation time of the file in seconds since the epoch. +.Pp +.It utc +Rotated file names are in ISO 8601 extended format, for example +.Dq example.log.2012-06-24T07:00:00Z . +The file names includes its creation time as a UTC date and time. +.Pp +.It utc-basic +Rotated file names are in ISO 8601 basic format, for example +.Dq example.log.20120624T070000Z . +The file names includes its creation time as a UTC date and time. +.Pp +.It local +Rotated file names are in ISO 8601 extended format, for example +.Dq example.log.2012-06-24T07:00:00-7 . +The file names includes its creation time as date and time in the local time zone. +The local timezone offset is included as a trailing part of the name. +.Pp +.It local-basic +Rotated file names are in ISO 8601 basic format, for example +.Dq example.log.20120624T070000-07 . +The file names includes its creation time as date and time in the local time zone. +The local timezone offset is included as a trailing part of the name. +.Pp +.It seq +Rotated file names are of the form +.Dq example.log.N +where N is an integer sequence number. +Files are re-numbered on each rotation so that the +.Dq 0 +file is the most recent. +.El +.Pp +If the option +.Dq rotate +appears without a value, the naming style defaults to +.Dq "sec" . +.Pp +Note that using the local timezone for timestamped files may cause odd behavior on highly-mobile systems. +.Nm aslmanager +will delete files after a specified time-to-live (see below). +The age of the file is determined by the file name. +If files are created in different timezones but saved with a non-absolute timestamp, +the age calculation may result in some files being considered older or newer than they are in reality. +.Pp +Also note that sequenced files (using the +.Dq sec +style) will initially be checkpointed using a file name containing a timestamp in seconds. +.Nm aslmanager +will re-sequence the files when it scans for checkpoint files. +.Pp +.It ttl=DAYS +Specifies the number of days that older versions of rotated files should be allowed to remain in the filesystem. +Rotated files older than this limit are deleted. +.Pp +.It dest=PATH +By default, rotated files are left in the same directory as the original file. +However, in some cases it may be useful to move the rotated versions to a different +directory for archival or other reasons. +If this option is specified, +.Nm aslmanager +will move files to the directory given by PATH. +.Pp +.It soft +Makes +.Nm syslogd +ignore write errors when saving messages. +Normally, +.Nm syslogd +will stop saving to a file or ASL directory after 5 consecutive write errors. +.Pp +.It compress +Enables gzip file compression for rotated log files. +When compressed, the extension +.Dq .gz +is appended to the file name. +.Pp +.It file_max=SIZE +Limits the size of an active log file. +SIZE may be an integer number of bytes, or the value may be followed by a single character +.Dq k , +.Dq m , +or +.Dq g +(upper or lower case), +to indicate a size limit in multiples of 1024 (kibibyte), 1048576 (mebibyte), or 1073741824 (gibibyte). +If a file exceeds this limit, it is immediately checkpointed by +.Nm syslogd +and a new file is opened. +Note that +.Dq file_max +specifies a size limit before file compression is performed if the +.Dq compress +option is also present. +.Pp +.It all_max=SIZE +Specifies a size limit for the total of all rotated versions of a file. +.Nm aslmanager +will delete rotated files, oldest first, to reduce the total below the limit. +SIZE may be specified in the same format as the file_max option. +.El +.Ss FILE ROTATION +.Nm syslogd +and +.Nm aslmanager +work together to automatically provide all the features of file rotation. +However, it is useful to understand how the process works. +This section describes the file rotation options that may be used in /etc/asl.conf +or an ASL Output Module configuration file, +together with a description of how the system works to support those features. +.Pp +If a file is marked for rotation, +.Nm syslogd +will close the file at the start of a new day or when the file exceeds its +.Dq file_max +size limit. +At that point, +.Nm syslogd +renames the file and starts a new file to continue logging. +The old file is renamed with the file's creation time included in its name. +This operation is called checkpointing the file. +.Pp +For example, +.Nm syslogd +might close +.Dq example.log +and rename it +.Dq example.log.T1340521200 , +1340521200 being the time that the file was created. +It would then start a new +.Dq example.log +file and use it until midnight, when the cycle would be repeated. +.Pp +Files are normally checkpointed at midnight. +If the system is sleeping or powered off, then files are checkpointed when +the the first message of a new day (local time) is received. +Files are also checkpointed if they exceed a size limit specified by a file_max option, +and they may be checkpointed manually through options provided by the +.Xr syslog 1 +and +.Xr aslmanager 8 +utilities. +The checkpointed file name always contains the file's creation time. +If the options for the file include +.Dq rotate=utc +then the timestamp will be a UTC date and time string. +.Dq rotate=local +causes the timestamp to be the date and time in the current local timezone. +Otherwise, the timestamp will be in seconds since the epoch. +.Pp +.Nm syslogd +only performs the checkpointing operation. +It closes old files, moves them out of the way, and starts writing new files. +Most of the work of file rotation is done by the +.Xr aslmanager 8 +utility. +That includes moving files to a destination directory, compressing files, +re-naming files according to one of the naming style options, +deleting old files after they exceed their time-to-live, +and checking file space usage. +.Pp +.Nm aslmanager +normally runs once during system start-up, and once a day just after midnight. +It may also be triggered occasionally by +.Nm syslogd , +and it may be run manually. +.Pp +.Nm aslmanager +scans for any checkpointed files created by +.Nm syslogd +and will rename the files (if required) to match the naming style specified by the +.Dq rotate=NAME_STYLE option. +If +.Dq rotate=seq +is specified for a file, checkpointed files created by +.Nm syslogd +contain a timestamp in seconds. +These files are renamed so that the file names +contain a sequence number. +The most recent version has the number +.Dq 0 , +and older versions have higher numbers. +For example: .Pp -.It broadcast -Causes syslogd to write the text of matching messages to all terminal windows. -If optional text follows the +.Dl example.log.0 +.Dl example.log.1 +.Dl example.log.2 +.Dl ... +.Pp +As well as renaming files, +.Nm aslmanager +may perform other actions. +If the file has been given a +.Dq dest=PATH +option, +the rotated versions of the file will be moved to the specified directory. +Files will be gzip compressed using the +.Xr zlib 3 +library if the +.Dq compress +option has been given. +If the total size of all the rotated versions of the file exceeds a value given in an +.Dq all_max +option, older version of the rotated file will be deleted to keep the total below the specified limit. +.Pp +Although checkpoint and file rotation operations are normally done automatically, +.Nm aslmanager +supports an option that will trigger +.Nm syslogd +to checkpoint files before +.Nm aslmanager +starts its scan. +.Nm syslog +also supports an option to force files to be checkpointed without running +.Nm aslmanager . +See the +.Xr aslmanager 8 +and +.Xr syslog 1 +manuals for details. +.Ss ASL OUTPUT MODULES +.Pp +An ASL output module is created by a configuration file in the directory /etc/asl. +The file name is used as the module's name. +The format of the file is generally the same as +.Nm asl.conf +with a few exceptions. +Mdules may not have parameter setting lines for the system parameters listed in the +PARAMETER SETTINGS or ASLMANAGER PARAMETER SETTINGS sections, +nor may they include .Dq broadcast -keyword, then that text is written rather that the matching message text. +query-action rules. .Pp -.It ignore -Causes a matching message to be ignored in all subsequent matching rules. -.El -.Sh ASLMANAGER PARAMETER SETTINGS +Module configuration files are read by +.Nm syslogd +when it starts, and whenever it gets a HUP signal. +Messages received by +.Nm syslogd +are first processed according the the rules found in /etc/asl.conf +(also known as the +.Dq com.apple.asl +module), then the message is processed by the +rules from each module found in /etc/asl. +.Pp +An exception to this is that messages that match the query in a +.Dq claim +action rule in any module are not processed by the rules in /etc/asl.conf. +.Pp +ASL output modules are enabled by default, but a module may include a parameter setting: +.Pp +.Dl = enable 0 +.Pp +The module is still loaded by +.Nm syslogd , +but the module will not save messages to files or directories, and will not post BSD notifications. +.Pp +Several mechanisms allow modules to be enabled or disabled dynamically. +One mechanism allows the setting of the +.Dq enable +parameter to be based on the existence of a path in the filesystem, +or on the value associated with a dictionary key in a property list file. +On iOS only, the value of a key in an installed configuration profile may be tested. +.Pp +To enable a module based on the existence of a file, the module may use: +.Pp +.Dl = enable [File /a/b/c] +.Pp +where +.Dq /a/b/c +may be any filesystem path. +.Pp +To enable a module based on the value of a dictionary key in a property list file, +.Pp +.Dl = enable [Plist /path/config.plist] [= SomeKey SomeValue] +.Pp +Any of the test operations described above in the QUERY-ACTION RULES section +may also be used in testing key / value pairs. +Multiple operations are also allowed, for example: +.Pp +.Dl = enable [Plist /path/config.plist] [N>= DebugLevel 7] [S= Othervalue xyz] +.Pp +If the property list file does not exist, the test will evaluate to zero. +The file may be in binary or xml format. +It may only contain a single dictionary object at its top level. +Only keys and values at the top level of the dictionary may be tested. +Values must be strings, integer values, doubles, UUIDs, dates, or booleans. +Boolean and values are converted to 1 and 0 respectively. +Values are converted into strings, +and string comparisons are used unless unless an +.Dq N +modifier is specified with the test operator. +.Pp +On iOS, a module may test key / value pairs in a configuration profile using the +same key / value tests that may be used for property list files. +.Pp +.Dl = enable [Profile name] [= Verbose 1] +.Pp +The profile name is the value of its DefaultsDomainName key. +The test will evaluate to zero if the profile is not installed. +.Pp +A module may be also enabled or disabled using +.Nm syslog +or by sending +.Nm syslogd +a special +.Xr asl 3 +control message. +Only the user +.Dq root +may enable or disable modules. +.Pp +A module may be enabled or disabled by sending an +.Xr asl 3 +message as shown in this example, which enables a module named +.Dq com.apple.example : +.Pp + #include + aslmsg ctl = asl_new(ASL_TYPE_MSG); + asl_set(ctl, ASL_KEY_OPTION, "control"); + asl_set(ctl, ASL_KEY_MSG, "@ com.apple.example enable 1"); + asl_send(NULL, ctl); + asl_free(ctl); +.Pp +A control message may also be sent using +.Nm syslog +as the following example shows to disable a module named +.Dq com.apple.example : +.Pp +.Dl sudo syslog -module com.apple.example enable 0 +.Pp +A module may also enable or disable itself. +Although a module that is not enabled will not write or post notifications, +it still will scan messages. +The module may contain conditional parameter-setting rules like: +.Pp +.Dl = [= Color Green] enable 1 +.Dl = [= Color Red] enable 0 +.Pp +This is similar to a query-action rule. +If a message received by syslogd matches the specified query, +in this case having a Color key with the value Green or Red, +then the enable parameter is set as specified. +So in this example, the module would be enabled and disabled +whenever syslogd received a message containing the appropriate value for the +.Dq Color +key. +.Ss ASLMANAGER PARAMETER SETTINGS The following parameter-settings are recognized by .Nm aslmanager . .Pp @@ -395,19 +883,19 @@ The debug parameter requires a value of to enable debug output, or a value of .Dq 0 to disable it. -Debug messages are sent to -.Nm syslogd . +Debug messages saved in an auxiliary file attached to an ASL log message. +The file may be inspected by opening the file attachement from the Console utility. .Pp .It store_ttl -Sets the time-to-live in days for messages in the syslog data store. +Sets the time-to-live in days for messages in the ASL database. The default is 7 days. .Pp .It max_store_size -Sets the maximum size for for the ASL data store. +Sets the maximum size for for the ASL database. The default is 150000000 bytes. .Pp .It archive -Enables or disables archiving. +Enables or disables archiving of the ASL database. The archive parameter requires a value of .Dq 1 to enable archiving, or a value of @@ -417,17 +905,18 @@ An option archive directory path may follow the .Dq 0 or .Dq 1 . -If enabled, files removed from the ASL data store are moved to the archive directory. +If enabled, files removed from the ASL database are moved to the archive directory. The default archive directory path is /var/log/asl.archive. .Pp .It store_path -The data store path used by +The ASL database path used by .Nm aslmanager . The default is /var/log/asl. Note that this parameter is ignored by .Nm syslogd . +.Pp .It archive_mode -Files copied to the archive will be given the specified access mode. +Files copied to the ASL database archive will be given the specified access mode. The default is 0400, so archive files will only be readable by root. .El .Pp diff --git a/syslogd.tproj/asl_action.c b/syslogd.tproj/asl_action.c index 64715c8..fd4d381 100644 --- a/syslogd.tproj/asl_action.c +++ b/syslogd.tproj/asl_action.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004-2011 Apple Inc. All rights reserved. + * Copyright (c) 2004-2013 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -21,6 +21,8 @@ * @APPLE_LICENSE_HEADER_END@ */ +#include + #include #include #include @@ -36,359 +38,137 @@ #include #include #include +#include +#include #include +#include #include "daemon.h" -#include +#include #define _PATH_WALL "/usr/bin/wall" -#define _PATH_ASL_CONF "/etc/asl.conf" -#define MY_ID "asl_action" +#define NOTIFY_PATH_SERVICE "com.apple.system.notify.service.path:0x87:" -#define MAX_FAILURES 5 +#define MY_ID "asl_action" -#define ACTION_NONE 0 -#define ACTION_IGNORE 1 -#define ACTION_NOTIFY 2 -#define ACTION_BROADCAST 3 -#define ACTION_ACCESS 4 -#define ACTION_ASL_STORE 5 /* Save in main ASL Database */ -#define ACTION_ASL_FILE 6 /* Save in an ASL format data file */ -#define ACTION_ASL_DIR 7 /* Save in an ASL directory */ -#define ACTION_FILE 8 -#define ACTION_FORWARD 9 +/* XXX add to asl.h */ +#define ASL_KEY_MODULE "ASLModule" -#define forever for(;;) +#define MAX_FAILURES 5 -#define ACT_FLAG_HAS_LOGGED 0x80000000 -#define ACT_FLAG_CLEAR_LOGGED 0x7fffffff +#define ACTION_STATUS_ERROR -1 +#define ACTION_STATUS_OK 0 -#define ACT_STORE_FLAG_STAY_OPEN 0x00000001 -#define ACT_STORE_FLAG_CONTINUE 0x00000002 +#define IDLE_CLOSE 300 -#define ACT_FILE_FLAG_DUP_SUPRESS 0x00000001 -#define ACT_FILE_FLAG_ROTATE 0x00000002 +#define forever for(;;) static dispatch_queue_t asl_action_queue; -static time_t last_file_day; +static dispatch_source_t checkpoint_timer; +static time_t sweep_time = 0; -typedef struct action_rule_s -{ - asl_msg_t *query; - int action; - char *options; - void *data; - struct action_rule_s *next; -} action_rule_t; - -struct store_data +#if TARGET_OS_EMBEDDED +static dispatch_queue_t crashlog_queue; +static dispatch_source_t crashlog_sentinel_src; +static int crashlog_sentinel_fd = -1; +static time_t crashmover_state = 0; +static int crashmover_token = -1; +#endif + +typedef struct store_data { asl_file_t *store; FILE *storedata; - char *dir; - char *path; - mode_t mode; - uid_t uid; - gid_t gid; uint64_t next_id; - uint32_t fails; - uint32_t flags; - uint32_t refcount; + time_t last_time; uint32_t p_year; uint32_t p_month; uint32_t p_day; -}; + dispatch_source_t monitor; +} asl_action_store_data_t; -struct file_data +typedef struct file_data { int fd; - char *path; - char *fmt; - const char *tfmt; - mode_t mode; - uid_t *uid; - uint32_t nuid; - gid_t *gid; - uint32_t ngid; - size_t max_size; - uint32_t fails; - uint32_t flags; - uint32_t refcount; - time_t stamp; uint32_t last_hash; uint32_t last_count; time_t last_time; - dispatch_source_t dup_timer; char *last_msg; -}; - -static action_rule_t *asl_action_rule = NULL; -static action_rule_t *asl_datastore_rule = NULL; - -static int _parse_config_file(const char *); -extern void db_save_message(aslmsg m); - -/* forward */ -int _act_file_open(struct file_data *fdata); -static void _act_file_init(action_rule_t *r); -static void _act_store_init(action_rule_t *r); + dispatch_source_t dup_timer; + dispatch_source_t monitor; +} asl_action_file_data_t; -static char * -_next_word(char **s) +typedef struct set_param_data { - char *a, *p, *e, *out; - int quote, len; - - if (s == NULL) return NULL; - if (*s == NULL) return NULL; - - quote = 0; - - p = *s; - a = p; - e = p; - - while (*p != '\0') - { - if (*p == '\\') - { - p++; - e = p; - - if (*p == '\0') - { - p--; - break; - } - - p++; - e = p; - continue; - } - - if (*p == '"') - { - if (quote == 0) quote = 1; - else quote = 0; - } - - if (((*p == ' ') || (*p == '\t')) && (quote == 0)) - { - e = p + 1; - break; - } - - p++; - e = p; - } - - *s = e; - - len = p - a; - if (len == 0) return NULL; + int token; +} asl_action_set_param_data_t; - out = malloc(len + 1); - if (out == NULL) return NULL; +static int action_asl_store_count; +static bool store_has_logged; - memcpy(out, a, len); - out[len] = '\0'; - return out; -} +extern void db_save_message(aslmsg m); -/* - * Config File format: - * Set parameter rule - initializes a parameter. - * = param args... - * Query rule - if a message matches the query, then the action is invoked. - * The rule may be identified by either "?" or "Q". - * ? [k v] [k v] ... action args... - * Q [k v] [k v] ... action args... - * Universal match rule - the action is invoked for all messages - * * action args... - */ +/* forward */ +static int _act_file_checkpoint_all(uint32_t force); +static void _asl_action_post_process_rule(asl_out_module_t *m, asl_out_rule_t *r); +static void _asl_action_close_idle_files(time_t idle_time); -/* Skip over query */ -static char * -_find_action(char *s) +static void +_act_out_set_param(asl_out_module_t *m, char *x, bool eval) { - char *p; + char *s = x; + char **l; + uint32_t count, intval; - p = s; - if (p == NULL) return NULL; - if ((*p != 'Q') && (*p != '?') && (*p != '*')) return NULL; + l = explode(s, " \t"); + if (l == NULL) return; - p++; - - forever + for (count = 0; l[count] != NULL; count++); + if (count == 0) { - /* Find next [ */ - while ((*p == ' ') || (*p == '\t')) p++; - - if (*p == '\0') return NULL; - if (*p != '[') return p; - - /* skip to closing ] */ - while (*p != ']') - { - p++; - if (*p == '\\') - { - p++; - if (*p == ']') p++; - } - } - - if (*p == ']') p++; + free_string_list(l); + return; } - return NULL; -} - -static int -_parse_query_action(char *s) -{ - char *act, *p; - action_rule_t *out, *rule; - - act = _find_action(s); - if (act == NULL) return -1; - - out = (action_rule_t *)calloc(1, sizeof(action_rule_t)); - if (out == NULL) return -1; - - p = strchr(act, ' '); - if (p != NULL) *p = '\0'; - - if (!strcasecmp(act, "ignore")) out->action = ACTION_IGNORE; - else if (!strcasecmp(act, "notify")) out->action = ACTION_NOTIFY; - else if (!strcasecmp(act, "broadcast")) out->action = ACTION_BROADCAST; - else if (!strcasecmp(act, "access")) out->action = ACTION_ACCESS; - else if (!strcasecmp(act, "store")) out->action = ACTION_ASL_STORE; - else if (!strcasecmp(act, "save")) out->action = ACTION_ASL_STORE; - else if (!strcasecmp(act, "store_file")) out->action = ACTION_ASL_FILE; - else if (!strcasecmp(act, "store_directory")) out->action = ACTION_ASL_DIR; - else if (!strcasecmp(act, "store_dir")) out->action = ACTION_ASL_DIR; - else if (!strcasecmp(act, "file")) out->action = ACTION_FILE; - else if (!strcasecmp(act, "forward")) out->action = ACTION_FORWARD; - - if (p != NULL) + if (!strcasecmp(l[0], "enable")) { - out->options = strdup(p+1); - - if (out->options == NULL) - { - free(out); - return -1; - } - } - - p = act - 1; + /* = enable [1|0] */ + if (count < 2) intval = 1; + else intval = atoi(l[1]); - *p = '\0'; + if (!eval) intval = (intval == 0) ? 1 : 0; - if (s[0] == '*') out->query = asl_msg_new(ASL_TYPE_QUERY); - else - { - s[0] = 'Q'; - out->query = asl_msg_from_string(s); + if (intval == 0) m->flags &= ~MODULE_FLAG_ENABLED; + else m->flags|= MODULE_FLAG_ENABLED; + return; } - - if (out->query == NULL) + else if (!strcasecmp(l[0], "disable")) { - asldebug("out->query is NULL (ERROR)\n"); - free(out->options); - free(out); - return -1; - } + /* = disable [1|0] */ + if (count < 2) intval = 1; + else intval = atoi(l[1]); - /* store /some/path means save to a file */ - if ((out->action == ACTION_ASL_STORE) && (out->options != NULL)) out->action = ACTION_ASL_FILE; + if (!eval) intval = (intval == 0) ? 1 : 0; - if (out->action == ACTION_FILE) _act_file_init(out); - else if ((out->action == ACTION_ASL_FILE) || (out->action == ACTION_ASL_DIR)) _act_store_init(out); - - if (out->action == ACTION_ASL_STORE) - { - asldebug("action = ACTION_ASL_STORE\n"); - if (asl_datastore_rule == NULL) asl_datastore_rule = out; - else - { - for (rule = asl_datastore_rule; rule->next != NULL; rule = rule->next); - rule->next = out; - } - } - else - { - asldebug("action = %d options = %s\n", out->action, out->options); - if (asl_action_rule == NULL) asl_action_rule = out; - else - { - for (rule = asl_action_rule; rule->next != NULL; rule = rule->next); - rule->next = out; - } + if (intval != 0) m->flags &= ~MODULE_FLAG_ENABLED; + else m->flags|= MODULE_FLAG_ENABLED; + return; } - return 0; -} - -static int -_parse_line(char *s) -{ - char *str; - int status; - - if (s == NULL) return -1; - while ((*s == ' ') || (*s == '\t')) s++; - - /* First non-whitespace char is the rule type */ - switch (*s) - { - case '\0': - case '#': - { - /* Blank Line or Comment */ - return 0; - } - case 'Q': - case '?': - case '*': - { - /* Query-match action */ - status = _parse_query_action(s); - break; - } - case '=': - { - /* Set parameter */ - status = control_set_param(s); - break; - } - default: - { - status = -1; - break; - } - } + free_string_list(l); - if (status != 0) + if (!strcmp(m->name, ASL_MODULE_NAME)) { - str = NULL; - asprintf(&str, "[%s syslogd] [%s %u] [%s %u] [%s Ignoring unrecognized entry in %s: %s] [%s 0] [%s 0] [Facility syslog]", - ASL_KEY_SENDER, - ASL_KEY_LEVEL, ASL_LEVEL_ERR, - ASL_KEY_PID, getpid(), - ASL_KEY_MSG, _PATH_ASL_CONF, s, - ASL_KEY_UID, ASL_KEY_GID); - - internal_log_message(str); - free(str); + /* Other parameters may be set by com.apple.asl module */ + control_set_param(x, eval); } - - return status; } static void -_act_notify(action_rule_t *r) +_act_notify(asl_out_module_t *m, asl_out_rule_t *r) { + if (m == NULL) return; + if ((m->flags & MODULE_FLAG_ENABLED) == 0) return; + if (r == NULL) return; if (r->options == NULL) return; @@ -396,21 +176,28 @@ _act_notify(action_rule_t *r) } static void -_act_broadcast(action_rule_t *r, aslmsg msg) +_act_broadcast(asl_out_module_t *m, asl_out_rule_t *r, aslmsg msg) { -#ifndef CONFIG_IPHONE +#if !TARGET_OS_EMBEDDED FILE *pw; const char *val; + if (m == NULL) return; + if ((m->flags & MODULE_FLAG_ENABLED) == 0) return; + + if (m->name == NULL) return; if (r == NULL) return; if (msg == NULL) return; + /* only base module (asl.conf) may broadcast */ + if (strcmp(m->name, ASL_MODULE_NAME)) return; + val = r->options; if (val == NULL) val = asl_get(msg, ASL_KEY_MSG); if (val == NULL) return; pw = popen(_PATH_WALL, "w"); - if (pw < 0) + if (pw == NULL) { asldebug("%s: error sending wall message: %s\n", MY_ID, strerror(errno)); return; @@ -422,11 +209,19 @@ _act_broadcast(action_rule_t *r, aslmsg msg) } static void -_act_access_control(action_rule_t *r, aslmsg msg) +_act_access_control(asl_out_module_t *m, asl_out_rule_t *r, aslmsg msg) { int32_t ruid, rgid; char *p; + if (m == NULL) return; + if (m->name == NULL) return; + if (r == NULL) return; + if (msg == NULL) return; + + /* only base module (asl.conf) may set access controls */ + if (strcmp(m->name, ASL_MODULE_NAME)) return; + ruid = atoi(r->options); rgid = -1; p = strchr(r->options, ' '); @@ -447,26 +242,218 @@ _act_access_control(action_rule_t *r, aslmsg msg) } } +#if TARGET_OS_EMBEDDED +static void +_crashlog_sentinel_init(void) +{ + char path[MAXPATHLEN]; + + if (crashlog_sentinel_src != NULL) return; + + snprintf(path, sizeof(path), "%s/com.apple.asl.%ld", _PATH_CRASHREPORTER, time(NULL)); + + crashlog_sentinel_fd = open(path, O_WRONLY | O_CREAT); + if (crashlog_sentinel_fd < 0) + { + char *str = NULL; + asprintf(&str, "[Sender syslogd] [Level 3] [PID %u] [Facility syslog] [Message Sentinel %s create/open failed (%s)]", global.pid, path, strerror(errno)); + internal_log_message(str); + free(str); + return; + } + + close(crashlog_sentinel_fd); + + crashlog_sentinel_fd = open(path, O_EVTONLY, 0); + if (crashlog_sentinel_fd < 0) + { + char *str = NULL; + asprintf(&str, "[Sender syslogd] [Level 3] [PID %u] [Facility syslog] [Message Sentinel %s event/open failed (%s)]", global.pid, path, strerror(errno)); + internal_log_message(str); + free(str); + return; + } + + crashlog_sentinel_src = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE, (uintptr_t)crashlog_sentinel_fd, DISPATCH_VNODE_DELETE, asl_action_queue); + if (crashlog_sentinel_src == NULL) + { + char *str = NULL; + asprintf(&str, "[Sender syslogd] [Level 3] [PID %u] [Facility syslog] [Message Sentinel %s dispatch_source_create failed]", global.pid, path); + internal_log_message(str); + free(str); + close(crashlog_sentinel_fd); + crashlog_sentinel_fd = -1; + return; + } + + dispatch_source_set_event_handler(crashlog_sentinel_src, ^{ + if (crashmover_state != 0) + { + asldebug("CrashMover inactive / sentinel deleted: resuming crashlog queue\n"); + dispatch_resume(crashlog_queue); + crashmover_state = 0; + } + + if (crashlog_sentinel_src != NULL) + { + dispatch_source_cancel(crashlog_sentinel_src); + dispatch_release(crashlog_sentinel_src); + } + + crashlog_sentinel_src = NULL; + + close(crashlog_sentinel_fd); + crashlog_sentinel_fd = -1; + _crashlog_sentinel_init(); + }); + + dispatch_resume(crashlog_sentinel_src); + asldebug("Created CrashLog Sentinel: %s\n", path); +} + +static void +_crashlog_queue_check(void) +{ + /* + * Check whether the crashlog queue has been suspended for too long. + * We allow the crashlog quque to be suspended for 60 seconds. + * After that, we start logging again. This prevents syslogd from + * filling memory due to a suspended queue. CrashMover really shoud + * take no more than a second or two to finish. + */ + if (crashmover_state == 0) return; + if ((time(NULL) - crashmover_state) <= 60) return; + + asldebug("CrashMover timeout: resuming crashlog queue\n"); + dispatch_resume(crashlog_queue); + crashmover_state = 0; + + /* + * crashlog_sentinel_src should never be NULL, but if + * _crashlog_sentinel_init failed for some strange reason, + * it will be NULL here. + */ + if (crashlog_sentinel_src != NULL) + { + dispatch_source_cancel(crashlog_sentinel_src); + dispatch_release(crashlog_sentinel_src); + } + + crashlog_sentinel_src = NULL; + + close(crashlog_sentinel_fd); + crashlog_sentinel_fd = -1; + _crashlog_sentinel_init(); +} +#endif + +static void +_act_dst_close(asl_out_rule_t *r) +{ + if (r == NULL) return; + if (r->dst == NULL) return; + if (r->dst->private == NULL) return; + + if ((r->action == ACTION_ASL_DIR) || (r->action == ACTION_ASL_FILE)) + { + asl_action_store_data_t *sdata = (asl_action_store_data_t *)r->dst->private; + if (sdata->store != NULL) asl_file_close(sdata->store); + sdata->store = NULL; + + if (sdata->storedata != NULL) fclose(sdata->storedata); + sdata->storedata = NULL; + + if (sdata->monitor != NULL) + { + dispatch_source_cancel(sdata->monitor); + dispatch_release(sdata->monitor); + sdata->monitor = NULL; + } + } + else if (r->action == ACTION_FILE) + { + asl_action_file_data_t *fdata = (asl_action_file_data_t *)r->dst->private; + if (fdata->fd >= 0) close(fdata->fd); + fdata->fd = -1; + + if (fdata->monitor != NULL) + { + dispatch_source_cancel(fdata->monitor); + dispatch_release(fdata->monitor); + fdata->monitor = NULL; + } + } +} + static uint32_t -_act_store_file_setup(struct store_data *sd) +_act_store_file_setup(asl_out_module_t *m, asl_out_rule_t *r) { uint32_t status; + asl_action_store_data_t *sdata; + char dstpath[MAXPATHLEN]; - if (sd == NULL) return ASL_STATUS_INVALID_STORE; - if (sd->store == NULL) return ASL_STATUS_INVALID_STORE; - if (sd->store->store == NULL) return ASL_STATUS_INVALID_STORE; + if (r == NULL) return ASL_STATUS_INVALID_STORE; + if (r->dst == NULL) return ASL_STATUS_INVALID_STORE; + if (r->dst->private == NULL) return ASL_STATUS_INVALID_STORE; - status = asl_file_read_set_position(sd->store, ASL_FILE_POSITION_LAST); - if (status != ASL_STATUS_OK) return status; + sdata = (asl_action_store_data_t *)r->dst->private; + if (sdata->store == NULL) + { + /* create path if necessary */ + asl_out_mkpath(r); + + int fd = asl_out_dst_file_create_open(r->dst); + if (fd < 0) + { + asldebug("_act_store_file_setup: asl_out_dst_file_create_open failed %d %s\n", errno, strerror(errno)); + return ASL_STATUS_WRITE_FAILED; + } + close(fd); + + asl_make_dst_filename(r->dst, dstpath, sizeof(dstpath)); + status = asl_file_open_write(dstpath, 0, -1, -1, &(sdata->store)); + if (status != ASL_STATUS_OK) return status; - sd->next_id = sd->store->cursor_xid + 1; - if (fseek(sd->store->store, 0, SEEK_END) != 0) return ASL_STATUS_ACCESS_DENIED; + sdata->monitor = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE, fileno(sdata->store->store), DISPATCH_VNODE_DELETE, asl_action_queue); + if (sdata->monitor != NULL) + { + dispatch_source_set_event_handler(sdata->monitor, ^{ _act_dst_close(r); }); + dispatch_resume(sdata->monitor); + } + + status = asl_file_read_set_position(sdata->store, ASL_FILE_POSITION_LAST); + if (status != ASL_STATUS_OK) + { + asldebug("_act_store_file_setup: asl_file_read_set_position failed %d %s\n", status, asl_core_error(status)); + return status; + } + + sdata->next_id = sdata->store->cursor_xid + 1; + if (fseek(sdata->store->store, 0, SEEK_END) != 0) + { + asldebug("_act_store_file_setup: fseek failed %d %s\n", errno, strerror(errno)); + return ASL_STATUS_WRITE_FAILED; + } + } + else + { + sdata->next_id++; + } return ASL_STATUS_OK; } +/* + * _act_store_dir_setup + * + * Creates store directory if it does not exist + * Creates StoreData file if it does not exist + * Reads ASL Message ID from StoreData file + * Writes ASL Message ID + 1 to StoreData file + * Opens current day file (e.g. "/foo/bar/2012.04.06.asl") + */ static uint32_t -_act_store_dir_setup(struct store_data *sd, time_t tick) +_act_store_dir_setup(asl_out_module_t *m, asl_out_rule_t *r, time_t tick) { struct tm ctm; char *path; @@ -474,17 +461,22 @@ _act_store_dir_setup(struct store_data *sd, time_t tick) uint64_t xid; int status; mode_t mask; + asl_action_store_data_t *sdata; - if (sd == NULL) return ASL_STATUS_INVALID_STORE; - if (sd->dir == NULL) return ASL_STATUS_INVALID_STORE; + if (r == NULL) return ASL_STATUS_INVALID_STORE; + if (r->dst == NULL) return ASL_STATUS_INVALID_STORE; + if (r->dst->private == NULL) return ASL_STATUS_INVALID_STORE; + if (r->dst->path == NULL) return ASL_STATUS_INVALID_STORE; + + sdata = (asl_action_store_data_t *)r->dst->private; /* get / set message id from StoreData file */ xid = 0; - if (sd->storedata == NULL) + if (sdata->storedata == NULL) { memset(&sb, 0, sizeof(struct stat)); - status = stat(sd->dir, &sb); + status = stat(r->dst->path, &sb); if (status == 0) { /* must be a directory */ @@ -493,12 +485,15 @@ _act_store_dir_setup(struct store_data *sd, time_t tick) else if (errno == ENOENT) { /* doesn't exist - create it */ - mask = umask(0); - status = mkdir(sd->dir, sd->mode); + mask = umask(S_IWGRP | S_IWOTH); + status = mkpath_np(r->dst->path, 0755); + if (status == 0) status = chmod(r->dst->path, r->dst->mode); umask(mask); if (status != 0) return ASL_STATUS_WRITE_FAILED; - if (chown(sd->dir, sd->uid, sd->gid) != 0) return ASL_STATUS_WRITE_FAILED; +#if !TARGET_IPHONE_SIMULATOR + if (chown(r->dst->path, r->dst->uid[0], r->dst->gid[0]) != 0) return ASL_STATUS_WRITE_FAILED; +#endif } else { @@ -507,7 +502,7 @@ _act_store_dir_setup(struct store_data *sd, time_t tick) } path = NULL; - asprintf(&path, "%s/%s", sd->dir, FILE_ASL_STORE_DATA); + asprintf(&path, "%s/%s", r->dst->path, FILE_ASL_STORE_DATA); if (path == NULL) return ASL_STATUS_NO_MEMORY; memset(&sb, 0, sizeof(struct stat)); @@ -515,36 +510,38 @@ _act_store_dir_setup(struct store_data *sd, time_t tick) if (status == 0) { /* StoreData exists: open and read last xid */ - sd->storedata = fopen(path, "r+"); - if (sd->storedata == NULL) + sdata->storedata = fopen(path, "r+"); + if (sdata->storedata == NULL) { free(path); return ASL_STATUS_FAILED; } - if (fread(&xid, sizeof(uint64_t), 1, sd->storedata) != 1) + if (fread(&xid, sizeof(uint64_t), 1, sdata->storedata) != 1) { free(path); - fclose(sd->storedata); - sd->storedata = NULL; + fclose(sdata->storedata); + sdata->storedata = NULL; return ASL_STATUS_READ_FAILED; } } else if (errno == ENOENT) { /* StoreData does not exist: create it */ - sd->storedata = fopen(path, "w"); - if (sd->storedata == NULL) + sdata->storedata = fopen(path, "w"); + if (sdata->storedata == NULL) { free(path); return ASL_STATUS_FAILED; } - if (chown(path, sd->uid, sd->gid) != 0) +#if !TARGET_IPHONE_SIMULATOR + if (chown(path, r->dst->uid[0], r->dst->gid[0]) != 0) { free(path); return ASL_STATUS_WRITE_FAILED; } +#endif } else { @@ -553,354 +550,297 @@ _act_store_dir_setup(struct store_data *sd, time_t tick) return ASL_STATUS_FAILED; } + sdata->monitor = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE, fileno(sdata->storedata), DISPATCH_VNODE_DELETE, asl_action_queue); + if (sdata->monitor != NULL) + { + dispatch_source_set_event_handler(sdata->monitor, ^{ _act_dst_close(r); }); + dispatch_resume(sdata->monitor); + } + free(path); } else { - rewind(sd->storedata); - if (fread(&xid, sizeof(uint64_t), 1, sd->storedata) != 1) + rewind(sdata->storedata); + if (fread(&xid, sizeof(uint64_t), 1, sdata->storedata) != 1) { - fclose(sd->storedata); - sd->storedata = NULL; + fclose(sdata->storedata); + sdata->storedata = NULL; return ASL_STATUS_READ_FAILED; } } xid = asl_core_ntohq(xid); xid++; - sd->next_id = xid; + sdata->next_id = xid; xid = asl_core_htonq(xid); - rewind(sd->storedata); - status = fwrite(&xid, sizeof(uint64_t), 1, sd->storedata); + rewind(sdata->storedata); + status = fwrite(&xid, sizeof(uint64_t), 1, sdata->storedata); if (status != 1) { - fclose(sd->storedata); - sd->storedata = NULL; + fclose(sdata->storedata); + sdata->storedata = NULL; return ASL_STATUS_WRITE_FAILED; } - if ((sd->flags & ACT_STORE_FLAG_STAY_OPEN) == 0) + memset(&ctm, 0, sizeof(struct tm)); + + if (localtime_r((const time_t *)&tick, &ctm) == NULL) return ASL_STATUS_FAILED; + if ((sdata->store != NULL) && (sdata->p_year == ctm.tm_year) && (sdata->p_month == ctm.tm_mon) && (sdata->p_day == ctm.tm_mday)) { - fclose(sd->storedata); - sd->storedata = NULL; + return ASL_STATUS_OK; } - memset(&ctm, 0, sizeof(struct tm)); + if (sdata->store != NULL) asl_file_close(sdata->store); + sdata->store = NULL; + free(r->dst->fname); + r->dst->fname = NULL; - if (localtime_r((const time_t *)&tick, &ctm) == NULL) return ASL_STATUS_FAILED; - if ((sd->p_year == ctm.tm_year) && (sd->p_month == ctm.tm_mon) && (sd->p_day == ctm.tm_mday) && (sd->path != NULL)) return ASL_STATUS_OK; + r->dst->stamp = tick; - if (sd->store != NULL) asl_file_close(sd->store); - sd->store = NULL; + sdata->p_year = ctm.tm_year; + sdata->p_month = ctm.tm_mon; + sdata->p_day = ctm.tm_mday; - sd->p_year = 0; - sd->p_month = 0; - sd->p_day = 0; + asprintf(&(r->dst->fname), "%s/%d.%02d.%02d.asl", r->dst->path, ctm.tm_year + 1900, ctm.tm_mon + 1, ctm.tm_mday); + if (r->dst->fname == NULL) return ASL_STATUS_NO_MEMORY; + mask = umask(0); - free(sd->path); - sd->path = NULL; + status = ASL_STATUS_OK; + if (sdata->store == NULL) { +#if TARGET_IPHONE_SIMULATOR + uid_t uid = -1; + gid_t gid = -1; +#else + uid_t uid = r->dst->uid[0]; + gid_t gid = r->dst->gid[0]; +#endif + status = asl_file_open_write(r->dst->fname, (r->dst->mode & 0666), uid, gid, &(sdata->store)); + } + umask(mask); - asprintf(&(sd->path), "%s/%d.%02d.%02d.asl", sd->dir, ctm.tm_year + 1900, ctm.tm_mon + 1, ctm.tm_mday); - if (sd->path == NULL) return ASL_STATUS_NO_MEMORY; + if (status != ASL_STATUS_OK) return status; - sd->p_year = ctm.tm_year; - sd->p_month = ctm.tm_mon; - sd->p_day = ctm.tm_mday; + if (fseek(sdata->store->store, 0, SEEK_END) != 0) return ASL_STATUS_FAILED; return ASL_STATUS_OK; } static void -_act_store_init(action_rule_t *r) +_asl_action_store_data_free(asl_action_store_data_t *sdata) { - struct store_data *sd, *xd; - char *str, *opts, *p, *path; - action_rule_t *x; + if (sdata == NULL) return; - /* check if the store data is already set up */ - if (r->data != NULL) return; + if (sdata->store != NULL) asl_file_close(sdata->store); + sdata->store = NULL; - opts = r->options; - path = _next_word(&opts); + if (sdata->storedata != NULL) fclose(sdata->storedata); + sdata->storedata = NULL; - if ((path == NULL) || (path[0] != '/')) - { - str = NULL; - asprintf(&str, "[%s syslogd] [%s %u] [%s %u] [Facility syslog] [%s Invalid path for \"%s\" action: %s]", - ASL_KEY_SENDER, - ASL_KEY_LEVEL, ASL_LEVEL_ERR, - ASL_KEY_PID, getpid(), - ASL_KEY_MSG, - (r->action == ACTION_ASL_FILE) ? "store" : "store_directory", - (path == NULL) ? "no path specified" : path); + free(sdata); +} - internal_log_message(str); - free(str); - r->action = ACTION_NONE; - free(path); - return; - } +static void +_asl_action_file_data_free(asl_action_file_data_t *fdata) +{ + if (fdata == NULL) return; - /* check if a previous rule has set up this path (ACTION_ASL_FILE) or dir (ACTION_ASL_DIR) */ - for (x = asl_action_rule; x != NULL; x = x->next) + if (fdata->dup_timer != NULL) { - if ((x->action == r->action) && (x->data != NULL)) + if (fdata->last_count == 0) { - xd = (struct store_data *)x->data; - p = xd->path; - if (r->action == ACTION_ASL_DIR) p = xd->dir; - - if ((p != NULL) && (!strcmp(path, p))) - { - free(path); - xd->refcount++; - r->data = x->data; - return; - } + /* + * The timer exists, but last_count is zero, so the timer is suspended. + * Sources must not be released in when suspended. + * So we resume it so that we can release it. + */ + dispatch_resume(fdata->dup_timer); } + + dispatch_release(fdata->dup_timer); } - /* set up store data */ - sd = (struct store_data *)calloc(1, sizeof(struct store_data)); - if (sd == NULL) return; + free(fdata->last_msg); + if (fdata->fd >= 0) close(fdata->fd); + free(fdata); +} - sd->refcount = 1; - sd->mode = 0755; - sd->next_id = 0; - sd->uid = 0; - sd->gid = 0; - sd->flags = 0; +static void +_asl_action_set_param_data_free(asl_action_set_param_data_t *spdata) +{ + if (spdata != NULL) notify_cancel(spdata->token); + free(spdata); +} - if (r->action == ACTION_ASL_DIR) sd->dir = path; - else sd->path = path; +static void +_asl_action_save_failed(const char *where, asl_out_module_t *m, asl_out_rule_t *r, uint32_t status) +{ + if (r->dst->flags & MODULE_FLAG_SOFT_WRITE) return; - while (NULL != (p = _next_word(&opts))) + r->dst->fails++; + asldebug("%s: %s save to %s failed: %s\n", where, m->name, r->dst->path, asl_core_error(status)); + + /* disable further activity after multiple failures */ + if (r->dst->fails > MAX_FAILURES) { - if (!strcmp(p, "stayopen")) - { - sd->flags |= ACT_STORE_FLAG_STAY_OPEN; - } - else if (!strcmp(p, "continue")) - { - sd->flags |= ACT_STORE_FLAG_CONTINUE; - } - else if (!strncmp(p, "mode=", 5)) sd->mode = strtol(p+5, NULL, 0); - else if (!strncmp(p, "uid=", 4)) sd->uid = atoi(p+4); - else if (!strncmp(p, "gid=", 4)) sd->gid = atoi(p+4); + char *str = NULL; + asprintf(&str, "[Sender syslogd] [Level 3] [PID %u] [Facility syslog] [Message Disabling module %s writes to %s following %u failures (%s)]", global.pid, m->name, r->dst->path, r->dst->fails, asl_core_error(status)); + internal_log_message(str); + free(str); - free(p); - p = NULL; - } + if (r->action == ACTION_FILE) _asl_action_file_data_free((asl_action_file_data_t *)r->dst->private); + else _asl_action_store_data_free((asl_action_store_data_t *)r->dst->private); - r->data = sd; + r->dst->private = NULL; + r->action = ACTION_NONE; + } } -/* - * Save a message to an ASL format file (ACTION_ASL_FILE) - * or to an ASL directory (ACTION_ASL_DIR). - */ -static void -_act_store(action_rule_t *r, aslmsg msg) +static int +_act_store_file(asl_out_module_t *m, asl_out_rule_t *r, aslmsg msg) { - struct store_data *sd; - asl_file_t *s; + asl_action_store_data_t *sdata; uint32_t status; uint64_t mid; - mode_t mask; - char *str, *opts; - const char *val; - time_t tick; - - s = NULL; - - if (r->data == NULL) return; - sd = (struct store_data *)r->data; + if (r == NULL) return ACTION_STATUS_ERROR; + if (r->dst == NULL) return ACTION_STATUS_ERROR; + if (r->dst->private == NULL) return ACTION_STATUS_ERROR; - if (sd->flags & ACT_FLAG_HAS_LOGGED) return; - sd->flags |= ACT_FLAG_HAS_LOGGED; + sdata = (asl_action_store_data_t *)r->dst->private; - if (r->action == ACTION_ASL_DIR) + /* check dst for file_max & etc */ + if (r->dst->flags & MODULE_FLAG_ROTATE) { - val = asl_get(msg, ASL_KEY_TIME); - if (val == NULL) return; - - tick = atol(val); - status = _act_store_dir_setup(sd, tick); - if (status != ASL_STATUS_OK) - { - asldebug("_act_store_dir_setup %s failed: %s\n", sd->path, asl_core_error(status)); - - sd->fails++; - - /* disable further activity after multiple failures */ - if (sd->fails > MAX_FAILURES) - { - char *str = NULL; - asprintf(&str, "[%s syslogd] [%s %u] [%s %u] [Facility syslog] [%s Disabling writes to path %s following %u failures (%s)]", - ASL_KEY_SENDER, - ASL_KEY_LEVEL, ASL_LEVEL_ERR, - ASL_KEY_PID, getpid(), - ASL_KEY_MSG, sd->path, sd->fails, asl_core_error(status)); - - internal_log_message(str); - free(str); - - asl_file_close(sd->store); - sd->store = NULL; - r->action = ACTION_NONE; - return; - } - } - else + if (asl_out_dst_checkpoint(r->dst, CHECKPOINT_TEST) != 0) { - sd->fails = 0; + _act_dst_close(r); + asl_trigger_aslmanager(); } } - if (sd->store == NULL) + status = _act_store_file_setup(m, r); + if (status == ASL_STATUS_OK) { - s = NULL; + sdata->last_time = time(NULL); - mask = umask(0); - status = asl_file_open_write(sd->path, (sd->mode & 0666), sd->uid, sd->gid, &s); - umask(mask); + r->dst->fails = 0; + mid = sdata->next_id; - if ((status != ASL_STATUS_OK) || (s == NULL)) - { - asldebug("asl_file_open_write %s failed: %s\n", sd->path, asl_core_error(status)); + /* save message to file and update dst size */ + status = asl_file_save(sdata->store, msg, &mid); + if (status == ASL_STATUS_OK) r->dst->size = sdata->store->file_size; + } - sd->fails++; + if (status != ASL_STATUS_OK) + { + _asl_action_save_failed("_act_store_file", m, r, status); + return ACTION_STATUS_ERROR; + } - /* disable further activity after multiple failures */ - if (sd->fails > MAX_FAILURES) - { - char *str = NULL; - asprintf(&str, "[%s syslogd] [%s %u] [%s %u] [Facility syslog] [%s Disabling writes to path %s following %u failures (%s)]", - ASL_KEY_SENDER, - ASL_KEY_LEVEL, ASL_LEVEL_ERR, - ASL_KEY_PID, getpid(), - ASL_KEY_MSG, sd->path, sd->fails, asl_core_error(status)); + return ACTION_STATUS_OK; +} - internal_log_message(str); - free(str); +static int +_act_store_dir(asl_out_module_t *m, asl_out_rule_t *r, aslmsg msg) +{ + asl_action_store_data_t *sdata; + uint32_t status; + uint64_t mid; + const char *val; + time_t tick; - asl_file_close(sd->store); - sd->store = NULL; - r->action = ACTION_NONE; - return; - } - } - else if (status == ASL_STATUS_OK) - { - sd->fails = 0; - } + if (r == NULL) return ACTION_STATUS_ERROR; + if (r->dst == NULL) return ACTION_STATUS_ERROR; + if (r->dst->private == NULL) return ACTION_STATUS_ERROR; - sd->store = s; - } + sdata = (asl_action_store_data_t *)r->dst->private; - if (r->action != ACTION_ASL_DIR) - { - status = _act_store_file_setup(sd); - if (status != ASL_STATUS_OK) - { - asldebug("_act_store_file_setup %s failed: %s\n", sd->path, asl_core_error(status)); + val = asl_get(msg, ASL_KEY_TIME); + if (val == NULL) return ACTION_STATUS_ERROR; - sd->fails++; + /* check dst for file_max & etc */ + if (asl_out_dst_checkpoint(r->dst, CHECKPOINT_TEST) != 0) + { + _act_dst_close(r); + asl_trigger_aslmanager(); + } - /* disable further activity after multiple failures */ - if (sd->fails > MAX_FAILURES) - { - char *str = NULL; - asprintf(&str, "[%s syslogd] [%s %u] [%s %u] [Facility syslog] [%s Disabling writes to path %s following %u failures (%s)]", - ASL_KEY_SENDER, - ASL_KEY_LEVEL, ASL_LEVEL_ERR, - ASL_KEY_PID, getpid(), - ASL_KEY_MSG, sd->path, sd->fails, asl_core_error(status)); + tick = atol(val); - internal_log_message(str); - free(str); + status = _act_store_dir_setup(m, r, tick); + if (status == ASL_STATUS_OK) + { + sdata->last_time = time(NULL); - asl_file_close(sd->store); - sd->store = NULL; - r->action = ACTION_NONE; - return; - } - } - else - { - sd->fails = 0; - } + r->dst->fails = 0; + mid = sdata->next_id; + status = asl_file_save(sdata->store, msg, &mid); + if (status == ASL_STATUS_OK) r->dst->size = sdata->store->file_size; } - mid = sd->next_id; - - status = asl_file_save(sd->store, msg, &mid); if (status != ASL_STATUS_OK) { - asldebug("asl_file_save %s failed: %s\n", sd->path, asl_core_error(status)); + _asl_action_save_failed("_act_store_dir", m, r, status); + return ACTION_STATUS_ERROR; + } - sd->fails++; + return ACTION_STATUS_OK; +} - /* disable further activity after multiple failures */ - if (sd->fails > MAX_FAILURES) - { - char *str = NULL; - asprintf(&str, "[%s syslogd] [%s %u] [%s %u] [Facility syslog] [%s Disabling writes to path %s following %u failures (%s)]", - ASL_KEY_SENDER, - ASL_KEY_LEVEL, ASL_LEVEL_ERR, - ASL_KEY_PID, getpid(), - ASL_KEY_MSG, sd->path, sd->fails, asl_core_error(status)); - - internal_log_message(str); - free(str); +static void +_act_store_final(asl_out_module_t *m, asl_out_rule_t *r, aslmsg msg) +{ + if (r->action == ACTION_ASL_DIR) _act_store_dir(m, r, msg); + else _act_store_file(m, r, msg); +} - asl_file_close(sd->store); - sd->store = NULL; - r->action = ACTION_NONE; - return; - } - } - else - { - sd->fails = 0; - } +/* + * Save a message to an ASL format file (ACTION_ASL_FILE) + * or to an ASL directory (ACTION_ASL_DIR). + */ +static void +_act_store(asl_out_module_t *m, asl_out_rule_t *r, aslmsg msg) +{ + if (r == NULL) return; + if (msg == NULL) return; + if (m == NULL) return; + if ((m->flags & MODULE_FLAG_ENABLED) == 0) return; + if (r->dst == NULL) return; - if ((sd->flags & ACT_STORE_FLAG_STAY_OPEN) == 0) - { - asl_file_close(sd->store); - sd->store = NULL; - } + if (r->dst->flags & MODULE_FLAG_HAS_LOGGED) return; + r->dst->flags |= MODULE_FLAG_HAS_LOGGED; - if ((sd->flags & ACT_STORE_FLAG_CONTINUE) == 0) +#if TARGET_OS_EMBEDDED + if (r->dst->flags & MODULE_FLAG_CRASHLOG) { - opts = (char *)asl_get(msg, ASL_KEY_OPTION); - if (opts == NULL) - { - asl_set(msg, ASL_KEY_OPTION, ASL_OPT_IGNORE); - } - else - { - str = NULL; - asprintf(&str, "%s %s", ASL_OPT_IGNORE, opts); - if (str != NULL) - { - asl_set(msg, ASL_KEY_OPTION, str); - free(str); - } - } + _crashlog_queue_check(); + asl_msg_retain((asl_msg_t *)msg); + dispatch_async(crashlog_queue, ^{ + _act_store_final(m, r, msg); + asl_msg_release((asl_msg_t *)msg); + }); + return; } +#endif + + _act_store_final(m, r, msg); } static int -_act_file_send_repeat_msg(struct file_data *fdata) +_send_repeat_msg(asl_out_rule_t *r) { + asl_action_file_data_t *fdata; char vt[32], *msg; - int len, status, closeit; + int len, status; time_t now = time(NULL); - if (fdata == NULL) return -1; + if (r == NULL) return -1; + if (r->dst == NULL) return -1; + if (r->dst->private == NULL) return -1; + + fdata = (asl_action_file_data_t *)r->dst->private; free(fdata->last_msg); fdata->last_msg = NULL; @@ -917,663 +857,822 @@ _act_file_send_repeat_msg(struct file_data *fdata) msg = NULL; asprintf(&msg, "%s --- last message repeated %u time%s ---\n", vt + 4, fdata->last_count, (fdata->last_count == 1) ? "" : "s"); fdata->last_count = 0; + fdata->last_time = now; if (msg == NULL) return -1; - closeit = 0; - if (fdata->fd < 0) - { - closeit = 1; - fdata->fd = _act_file_open(fdata); - if (fdata->fd < 0) - { - asldebug("%s: error opening for repeat message (%s): %s\n", MY_ID, fdata->path, strerror(errno)); - return -1; - } - } + if (fdata->fd < 0) fdata->fd = asl_out_dst_file_create_open(r->dst); len = strlen(msg); status = write(fdata->fd, msg, len); free(msg); - if (closeit != 0) - { - close(fdata->fd); - fdata->fd = -1; - } if ((status < 0) || (status < len)) { - asldebug("%s: error writing repeat message (%s): %s\n", MY_ID, fdata->path, strerror(errno)); + asldebug("%s: error writing repeat message (%s): %s\n", MY_ID, r->dst->path, strerror(errno)); return -1; } return 0; } -/* - * N.B. This is basic file rotation support. - * More rotation options will be added in the future, along - * with support in aslmanager for compression and deletion. - */ -static void -_act_file_rotate_file_data(struct file_data *fdata, time_t now) +static int +_act_file_open(asl_out_module_t *m, asl_out_rule_t *r) { - char str[MAXPATHLEN]; - size_t len; - int width; - - if (now == 0) now = time(NULL); - - /* flush duplicates if pending */ - _act_file_send_repeat_msg(fdata); - - /* sleep to prevent a sub-second rotation */ - while (now == fdata->stamp) - { - sleep(1); - now = time(NULL); - } - - len = strlen(fdata->path); - width = len - 4; - if ((len > 4) && (!strcasecmp(fdata->path + width, ".log"))) - { - /* ".log" rename: abc.log -> abc.timestamp.log */ - snprintf(str, sizeof(str), "%.*s.%lu.log", width, fdata->path, fdata->stamp); - } - else - { - snprintf(str, sizeof(str), "%s.%lu", fdata->path, fdata->stamp); - } + asl_action_file_data_t *fdata; - rename(fdata->path, str); - - fdata->stamp = now; -} - -int -_act_file_open(struct file_data *fdata) -{ - acl_t acl; - uuid_t uuid; - acl_entry_t entry; - acl_permset_t perms; - int status; - int fd = -1; - mode_t mask; - struct stat sb; - uint32_t i; + if (r == NULL) return -1; + if (r->dst == NULL) return -1; + if (r->dst->private == NULL) return -1; - memset(&sb, 0, sizeof(struct stat)); - status = stat(fdata->path, &sb); - if (status == 0) + fdata = (asl_action_file_data_t *)r->dst->private; + if (fdata->fd < 0) { - /* must be a regular file */ - if (!S_ISREG(sb.st_mode)) return -1; - - /* use st_birthtimespec if stamp is zero */ - if (fdata->stamp == 0) fdata->stamp = sb.st_birthtimespec.tv_sec; - - /* rotate if over size limit */ - if ((fdata->max_size > 0) && (sb.st_size > fdata->max_size)) - { - _act_file_rotate_file_data(fdata, 0); - } - else + fdata->fd = asl_out_dst_file_create_open(r->dst); + if (fdata->fd < 0) { - /* open existing file */ - fd = open(fdata->path, O_RDWR | O_APPEND | O_EXCL, 0); - return fd; + /* + * lazy path creation: create path and retry + * asl_out_dst_file_create_open doesn not create the path + * so we do it here. + */ + asl_out_mkpath(r); + fdata->fd = asl_out_dst_file_create_open(r->dst); } - } - else if (errno != ENOENT) - { - return -1; - } - -#if TARGET_OS_EMBEDDED - return open(fdata->path, O_RDWR | O_CREAT | O_EXCL, (fdata->mode & 0666)); -#else - - acl = acl_init(1); - for (i = 0; i < fdata->ngid; i++) - { - status = mbr_gid_to_uuid(fdata->gid[i], uuid); - if (status != 0) + if (fdata->fd >= 0) { - char *str = NULL; - asprintf(&str, "[%s syslogd] [%s %u] [%s %u] [Facility syslog] [%s Unknown GID %d for \"file\" action: %s]", - ASL_KEY_SENDER, - ASL_KEY_LEVEL, ASL_LEVEL_ERR, - ASL_KEY_PID, getpid(), - ASL_KEY_MSG, fdata->gid[i], fdata->path); - - internal_log_message(str); - free(str); - continue; + fdata->monitor = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE, fdata->fd, DISPATCH_VNODE_DELETE, asl_action_queue); + if (fdata->monitor != NULL) + { + dispatch_source_set_event_handler(fdata->monitor, ^{ _act_dst_close(r); }); + dispatch_resume(fdata->monitor); + } } + } - status = acl_create_entry_np(&acl, &entry, ACL_FIRST_ENTRY); - if (status != 0) goto asl_file_create_return; - - status = acl_set_tag_type(entry, ACL_EXTENDED_ALLOW); - if (status != 0) goto asl_file_create_return; - - status = acl_set_qualifier(entry, &uuid); - if (status != 0) goto asl_file_create_return; + return fdata->fd; +} - status = acl_get_permset(entry, &perms); - if (status != 0) goto asl_file_create_return; +static void +_start_cycling() +{ + struct timespec midnight; + struct tm t; + time_t x; - status = acl_add_perm(perms, ACL_READ_DATA); - if (status != 0) goto asl_file_create_return; - } + x = time(NULL); - for (i = 0; i < fdata->nuid; i++) - { - status = mbr_uid_to_uuid(fdata->uid[i], uuid); - if (status != 0) - { - char *str = NULL; - asprintf(&str, "[%s syslogd] [%s %u] [%s %u] [Facility syslog] [%s Unknown UID %d for \"file\" action: %s]", - ASL_KEY_SENDER, - ASL_KEY_LEVEL, ASL_LEVEL_ERR, - ASL_KEY_PID, getpid(), - ASL_KEY_MSG, fdata->uid[i], fdata->path); - - internal_log_message(str); - free(str); - continue; - } + if (checkpoint_timer != NULL) return; - status = acl_create_entry_np(&acl, &entry, ACL_FIRST_ENTRY); - if (status != 0) goto asl_file_create_return; + localtime_r(&x, &t); - status = acl_set_tag_type(entry, ACL_EXTENDED_ALLOW); - if (status != 0) goto asl_file_create_return; + t.tm_sec = 0; + t.tm_min = 0; + t.tm_hour = 0; + t.tm_mday++; - status = acl_set_qualifier(entry, &uuid); - if (status != 0) goto asl_file_create_return; + x = mktime(&t); + midnight.tv_sec = x; + midnight.tv_nsec = 0; - status = acl_get_permset(entry, &perms); - if (status != 0) goto asl_file_create_return; + checkpoint_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, asl_action_queue); + dispatch_source_set_timer(checkpoint_timer, dispatch_walltime(&midnight, 0), NSEC_PER_SEC * SEC_PER_DAY, 0); + dispatch_source_set_event_handler(checkpoint_timer, ^{ _act_file_checkpoint_all(CHECKPOINT_FORCE); }); + dispatch_resume(checkpoint_timer); +} - status = acl_add_perm(perms, ACL_READ_DATA); - if (status != 0) goto asl_file_create_return; - } +/* check if a module path (mpath) matches a user path (upath) */ +static bool +_act_file_equal(const char *mpath, const char *upath) +{ + const char *slash; - mask = umask(0); - fd = open(fdata->path, O_RDWR | O_CREAT | O_EXCL, (fdata->mode & 0666)); - umask(mask); - if (fd < 0) goto asl_file_create_return; + /* NULL upath means user wants to match all files */ + if (upath == NULL) return true; - errno = 0; - status = acl_set_fd(fd, acl); + if (mpath == NULL) return false; - if (status != 0) - { - close(fd); - fd = -1; - unlink(fdata->path); - } + /* check for exact match */ + if (!strcmp(mpath, upath)) return true; -asl_file_create_return: + /* upath may be the last component of mpath */ + slash = strrchr(mpath, '/'); + if (slash == NULL) return false; - acl_free(acl); - return fd; -#endif + if (!strcmp(slash + 1, upath)) return true; + return false; } -static void -_act_file_rotate(const char *path) +static int +_act_file_checkpoint(asl_out_module_t *m, const char *path, uint32_t force) { - action_rule_t *r; - struct file_data *fdata; - time_t now = time(NULL); + asl_out_rule_t *r; + int did_checkpoint = 0; + + if (m == NULL) return 0; - for (r = asl_action_rule; r != NULL; r = r->next) + + for (r = m->ruleset; r != NULL; r = r->next) { - if (r->action == ACTION_FILE) + if ((r->action == ACTION_FILE) || (r->action == ACTION_ASL_FILE)) { - fdata = (struct file_data *)r->data; - if (fdata->flags & ACT_FILE_FLAG_ROTATE) + if (r->dst->flags & MODULE_FLAG_ROTATE) { - if ((path == NULL) || ((fdata->path != NULL) && !strcmp(fdata->path, path))) + if (_act_file_equal(r->dst->path, path)) { - _act_file_rotate_file_data(fdata, now); + if (force & CHECKPOINT_CRASH) + { + if (r->dst->flags & MODULE_FLAG_CRASHLOG) + { + if (asl_out_dst_checkpoint(r->dst, CHECKPOINT_FORCE) > 0) + { + did_checkpoint = 1; + _act_dst_close(r); + } + } + } + else + { + if (asl_out_dst_checkpoint(r->dst, force) > 0) + { + did_checkpoint = 1; + _act_dst_close(r); + } + } } } } } -} - -static char * -_act_file_format_string(char *s) -{ - char *fmt; - size_t i, len, n; - - if (s == NULL) return NULL; - - len = strlen(s); - n = 0; - for (i = 0; i < len; i++) if (s[i] == '\\') n++; - - fmt = malloc(1 + len - n); - if (fmt == NULL) return NULL; - for (i = 0, n = 0; i < len; i++) if (s[i] != '\\') fmt[n++] = s[i]; - fmt[n] = '\0'; - return fmt; + return did_checkpoint; } -static size_t -_act_file_max_size(char *s) -{ - size_t len, n, max; - char x; - - if (s == NULL) return 0; - - len = strlen(s); - if (len == 0) return 0; - - n = 1; - x = s[len - 1]; - if (x > 90) x -= 32; - if (x == 'K') n = 1ll << 10; - else if (x == 'M') n = 1ll << 20; - else if (x == 'G') n = 1ll << 30; - else if (x == 'T') n = 1ll << 40; - - max = atoll(s) * n; - return max; -} - -static void -_act_file_add_uid(struct file_data *fdata, char *s) +static int +_act_file_checkpoint_all(uint32_t force) { - if (fdata == NULL) return; - if (s == NULL) return; + asl_out_module_t *m; + int did_checkpoint = 0; - fdata->uid = reallocf(fdata->uid, (fdata->nuid + 1) * sizeof(uid_t)); - if (fdata->uid == NULL) + for (m = global.asl_out_module; m != NULL; m = m->next) { - fdata->nuid = 0; - return; + if (_act_file_checkpoint(m, NULL, force) > 0) did_checkpoint = 1; } - fdata->uid[fdata->nuid++] = atoi(s); + asl_trigger_aslmanager(); + + return did_checkpoint; } static void -_act_file_add_gid(struct file_data *fdata, char *s) +_act_file_final(asl_out_module_t *m, asl_out_rule_t *r, aslmsg msg) { - if (fdata == NULL) return; - if (s == NULL) return; + asl_action_file_data_t *fdata; + int is_dup; + uint32_t len, msg_hash = 0; + char *str; + time_t now; - fdata->gid = reallocf(fdata->gid, (fdata->ngid + 1) * sizeof(gid_t)); - if (fdata->gid == NULL) + /* + * If print format is std, bsd, or msg, then skip messages with + * no ASL_KEY_MSG, or without a value for it. + */ + if (r->dst->flags & MODULE_FLAG_STD_BSD_MSG) { - fdata->ngid = 0; - return; + const char *msgval = NULL; + if (asl_msg_lookup((asl_msg_t *)msg, ASL_KEY_MSG, &msgval, NULL) != 0) return; + if (msgval == NULL) return; } - fdata->gid[fdata->ngid++] = atoi(s); -} + fdata = (asl_action_file_data_t *)r->dst->private; -static void -_act_file_init(action_rule_t *r) -{ - struct file_data *fdata, *xdata; - char *str, *opts, *p, *path; - action_rule_t *x; + now = time(NULL); - /* check if the file data is already set up */ - if (r->data != NULL) return; + is_dup = 0; - /* requires at least a path */ - if (r->options == NULL) return; - opts = r->options; - path = _next_word(&opts); + str = asl_format_message((asl_msg_t *)msg, r->dst->fmt, r->dst->tfmt, ASL_ENCODE_SAFE, &len); - if ((path == NULL) || (path[0] != '/')) + if (r->dst->flags & MODULE_FLAG_COALESCE) { - str = NULL; - asprintf(&str, "[%s syslogd] [%s %u] [%s %u] [Facility syslog] [%s Invalid path for \"file\" action: %s]", - ASL_KEY_SENDER, - ASL_KEY_LEVEL, ASL_LEVEL_ERR, - ASL_KEY_PID, getpid(), - ASL_KEY_MSG, (path == NULL) ? "no path specified" : path); - - internal_log_message(str); - free(str); - free(path); - r->action = ACTION_NONE; - return; - } + if (fdata->dup_timer == NULL) + { + /* create a timer to flush dups on this file */ + fdata->dup_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, asl_action_queue); + dispatch_source_set_event_handler(fdata->dup_timer, ^{ _send_repeat_msg(r); }); + } - /* check if a previous rule has set up this path */ - for (x = asl_action_rule; x != NULL; x = x->next) - { - if ((x->action == ACTION_FILE) && (x->data != NULL)) + if ((global.bsd_max_dup_time > 0) && (str != NULL) && (fdata->last_msg != NULL)) { - xdata = (struct file_data *)x->data; - if ((xdata->path != NULL) && (!strcmp(path, xdata->path))) + msg_hash = asl_core_string_hash(str + 16, len - 16); + if ((fdata->last_hash == msg_hash) && (!strcmp(fdata->last_msg, str + 16))) { - free(path); - xdata->refcount++; - r->data = x->data; - return; + if ((now - fdata->last_time) < global.bsd_max_dup_time) is_dup = 1; } } } - /* set up file data */ - fdata = (struct file_data *)calloc(1, sizeof(struct file_data)); - if (fdata == NULL) return; - - fdata->refcount = 1; - fdata->path = path; - - /* - * options: - * mode= set file creation mode - * uid= user added to read ACL - * gid= group added to read ACL - * format= format string (also fmt=) - * no_dup_supress no duplicate supression - * - * rotate automatic daily rotation - * this is basic rotation - more support is TBD - */ - fdata->mode = 0644; - fdata->flags = ACT_FILE_FLAG_DUP_SUPRESS; - - while (NULL != (p = _next_word(&opts))) + if (is_dup == 1) { - if (!strncmp(p, "mode=", 5)) fdata->mode = strtol(p+5, NULL, 0); - else if (!strncmp(p, "uid=", 4)) _act_file_add_uid(fdata, p+4); - else if (!strncmp(p, "gid=", 4)) _act_file_add_gid(fdata, p+4); - else if (!strncmp(p, "fmt=", 4)) fdata->fmt = _act_file_format_string(p+4); - else if (!strncmp(p, "format=", 7)) fdata->fmt = _act_file_format_string(p+7); - else if (!strncmp(p, "no_dup_supress", 14)) fdata->flags &= ~ACT_FILE_FLAG_DUP_SUPRESS; - else if (!strncmp(p, "rotate", 6)) fdata->flags |= ACT_FILE_FLAG_ROTATE; - else if (!strncmp(p, "max_size=", 9)) fdata->max_size = _act_file_max_size(p+9); + if (fdata->last_count == 0) + { + /* start the timer */ + dispatch_source_set_timer(fdata->dup_timer, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * global.bsd_max_dup_time), DISPATCH_TIME_FOREVER, 0); + dispatch_resume(fdata->dup_timer); + } - free(p); - p = NULL; + fdata->last_count++; } + else + { + if (_act_file_open(m, r) < 0) + { + _asl_action_save_failed("_act_file", m, r, ASL_STATUS_FAILED); + free(str); + return; + } + else + { + r->dst->fails = 0; + } + + /* + * The current message is not a duplicate. If fdata->last_count > 0 + * we need to write a "last message repeated N times" log entry. + * _send_repeat_msg will free last_msg and do nothing if + * last_count == 0, but we test and free here to avoid a function call. + */ + if (fdata->last_count > 0) + { + _send_repeat_msg(r); + } + else + { + free(fdata->last_msg); + fdata->last_msg = NULL; + } + + /* check dst for file_max & etc */ + if (r->dst->flags & MODULE_FLAG_ROTATE) + { + int ckpt = asl_out_dst_checkpoint(r->dst, CHECKPOINT_TEST); + if (ckpt != 0) + { + _act_dst_close(r); + asl_trigger_aslmanager(); - if (fdata->fmt == NULL) fdata->fmt = strdup("std"); + if (_act_file_open(m, r) < 0) + { + _asl_action_save_failed("_act_file", m, r, ASL_STATUS_FAILED); + free(str); + return; + } + else + { + r->dst->fails = 0; + } + } + } + + if (str != NULL) fdata->last_msg = strdup(str + 16); - /* duplicate compression is only possible for std and bsd formats */ - if (strcmp(fdata->fmt, "std") && strcmp(fdata->fmt, "bsd")) fdata->flags &= ~ACT_FILE_FLAG_DUP_SUPRESS; + fdata->last_hash = msg_hash; + fdata->last_count = 0; + fdata->last_time = now; - /* set time format for raw output */ - if (!strcmp(fdata->fmt, "raw")) fdata->tfmt = "sec"; + if ((str != NULL) && (len > 1)) + { + /* write line to file and update dst size */ + size_t bytes = write(fdata->fd, str, len - 1); + if (bytes > 0) r->dst->size += bytes; + } + } - r->data = fdata; + free(str); } static void -_act_file(action_rule_t *r, aslmsg msg) +_act_file(asl_out_module_t *m, asl_out_rule_t *r, aslmsg msg) { - struct file_data *fdata; - int is_dup; - uint32_t len, msg_hash = 0; - char *str; - time_t now, today; - struct tm ctm; - - if (r->data == NULL) return; + if (r == NULL) return; + if (msg == NULL) return; + if (m == NULL) return; + if ((m->flags & MODULE_FLAG_ENABLED) == 0) return; + if (r->dst == NULL) return; + if (r->dst->private == NULL) return; - fdata = (struct file_data *)r->data; + if (r->dst->flags & MODULE_FLAG_HAS_LOGGED) return; - now = time(NULL); - today = now; + r->dst->flags |= MODULE_FLAG_HAS_LOGGED; - memset(&ctm, 0, sizeof(struct tm)); - if (localtime_r((const time_t *)&now, &ctm) != NULL) +#if TARGET_OS_EMBEDDED + if (r->dst->flags & MODULE_FLAG_CRASHLOG) { - ctm.tm_sec = 0; - ctm.tm_min = 0; - ctm.tm_hour = 0; - today = mktime(&ctm); + _crashlog_queue_check(); + asl_msg_retain((asl_msg_t *)msg); + dispatch_async(crashlog_queue, ^{ + _act_file_final(m, r, msg); + asl_msg_release((asl_msg_t *)msg); + }); + return; } +#endif + + _act_file_final(m, r, msg); +} + +static void +_act_forward(asl_out_module_t *m, asl_out_rule_t *r, aslmsg msg) +{ + /* To do: Add a "forward" action to asl.conf */ +} + +static void +_act_control(asl_out_module_t *m, asl_out_rule_t *r, aslmsg msg) +{ + const char *p; - /* check for rotation */ - if ((last_file_day != 0) && (last_file_day != today)) + if (m == NULL) return; + if (r == NULL) return; + p = asl_get(msg, ASL_KEY_MODULE); + + if (r->options == NULL) return; + + if (!strcmp(r->options, "enable")) { - _act_file_rotate(NULL); + m->flags |= MODULE_FLAG_ENABLED; } + else if (!strcmp(r->options, "disable")) + { + m->flags &= ~MODULE_FLAG_ENABLED; + } + else if ((!strcmp(r->options, "checkpoint")) || (!strcmp(r->options, "rotate"))) + { + _act_file_checkpoint(m, NULL, CHECKPOINT_FORCE); + } +} - last_file_day = today; +static void +_send_to_asl_store(aslmsg msg) +{ + if ((global.asl_out_module != NULL) && ((global.asl_out_module->flags & MODULE_FLAG_ENABLED) == 0)) return; + if (store_has_logged) return; + store_has_logged = true; - /* - * asl.conf may contain multuple rules for the same file, eg: - * ? [= Facility zippy] /var/log/abc.log - * ? [= Color purple] /var/log/abc.log - * - * To prevent duplicates we set a flag bit when a message is logged - * to this file, and bail out if it has already been logged. - * Note that asl_out_message clears the flag bit in all file_data - * structures before processing each message. - */ - if (fdata->flags & ACT_FLAG_HAS_LOGGED) return; - fdata->flags |= ACT_FLAG_HAS_LOGGED; + db_save_message(msg); +} - is_dup = 0; +static int +_asl_out_process_message(asl_out_module_t *m, aslmsg msg) +{ + asl_out_rule_t *r; - str = asl_format_message((asl_msg_t *)msg, fdata->fmt, fdata->tfmt, ASL_ENCODE_SAFE, &len); + if (m == NULL) return 1; + if (msg == NULL) return 1; - if (fdata->flags & ACT_FILE_FLAG_DUP_SUPRESS) + /* reset flag bit used for duplicate avoidance */ + for (r = m->ruleset; r != NULL; r = r->next) { - if (fdata->dup_timer == NULL) + if ((r->action == ACTION_FILE) || (r->action == ACTION_ASL_DIR) || (r->action == ACTION_ASL_FILE)) { - /* create a timer to flush dups on this file */ - fdata->dup_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, asl_action_queue); - dispatch_source_set_event_handler(fdata->dup_timer, ^{ _act_file_send_repeat_msg((struct file_data *)r->data); }); - } - - if ((global.bsd_max_dup_time > 0) && (str != NULL) && (fdata->last_msg != NULL)) - { - msg_hash = asl_core_string_hash(str + 16, len - 16); - if ((fdata->last_hash == msg_hash) && (!strcmp(fdata->last_msg, str + 16))) - { - if ((now - fdata->last_time) < global.bsd_max_dup_time) is_dup = 1; - } + if (r->dst != NULL) r->dst->flags &= MODULE_FLAG_CLEAR_LOGGED; } } - if (is_dup == 1) + for (r = m->ruleset; r != NULL; r = r->next) { - if (fdata->last_count == 0) + if (r->query == NULL) continue; + + /* ACTION_SET_FILE, ACTION_SET_PLIST, and ACTION_SET_PROF are handled independently */ + if ((r->action == ACTION_SET_FILE) || (r->action == ACTION_SET_PLIST) || (r->action == ACTION_SET_PROF)) continue; + + /* + * ACTION_CLAIM during processing is a filter. It will only be here if the option "only" + * was supplied. In this case we test the message against the query. If it does not + * match, we skip the message. + */ + if (r->action == ACTION_CLAIM) { - /* start the timer */ - dispatch_source_set_timer(fdata->dup_timer, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * global.bsd_max_dup_time), DISPATCH_TIME_FOREVER, 0); - dispatch_resume(fdata->dup_timer); + if ((asl_msg_cmp(r->query, (asl_msg_t *)msg) != 1)) return 0; } - fdata->last_count++; - } - else - { - fdata->fd = _act_file_open(fdata); - if (fdata->fd < 0) + if ((asl_msg_cmp(r->query, (asl_msg_t *)msg) == 1)) { - asldebug("_act_file_open %s failed: %s\n", fdata->path, strerror(errno)); + if (r->action == ACTION_NONE) continue; + else if (r->action == ACTION_IGNORE) return 1; + else if (r->action == ACTION_SKIP) return 0; + else if (r->action == ACTION_ASL_STORE) _send_to_asl_store(msg); + else if (r->action == ACTION_ACCESS) _act_access_control(m, r, msg); + else if (r->action == ACTION_NOTIFY) _act_notify(m, r); + else if (r->action == ACTION_BROADCAST) _act_broadcast(m, r, msg); + else if (r->action == ACTION_FORWARD) _act_forward(m, r, msg); + else if (r->action == ACTION_CONTROL) _act_control(m, r, msg); + else if (r->action == ACTION_SET_PARAM) _act_out_set_param(m, r->options, true); + else if ((r->action == ACTION_ASL_FILE) || (r->action == ACTION_ASL_DIR)) _act_store(m, r, msg); + else if (r->action == ACTION_FILE) _act_file(m, r, msg); + } + } - fdata->fails++; + return 0; +} - /* disable further activity after multiple failures */ - if (fdata->fails > MAX_FAILURES) - { - char *tmp = NULL; - asprintf(&tmp, "[%s syslogd] [%s %u] [%s %u] [Facility syslog] [%s Disabling writes to path %s following %u failures (%s)]", - ASL_KEY_SENDER, - ASL_KEY_LEVEL, ASL_LEVEL_ERR, - ASL_KEY_PID, getpid(), - ASL_KEY_MSG, fdata->path, fdata->fails, strerror(errno)); +void +asl_out_message(aslmsg msg) +{ + OSAtomicIncrement32(&global.asl_queue_count); + asl_msg_retain((asl_msg_t *)msg); - internal_log_message(tmp); - free(tmp); + dispatch_async(asl_action_queue, ^{ + int ignore = 0; + const char *p; + time_t now = time(NULL); + asl_out_module_t *m = global.asl_out_module; - r->action = ACTION_NONE; - free(str); - return; + store_has_logged = false; + + p = asl_get(msg, ASL_KEY_MODULE); + if (p == NULL) + { + if ((action_asl_store_count == 0) || (asl_check_option(msg, ASL_OPT_STORE) == 1)) _send_to_asl_store(msg); + + ignore = _asl_out_process_message(m, msg); + if (ignore == 0) + { + if (m != NULL) m = m->next; + while (m != NULL) + { + _asl_out_process_message(m, msg); + m = m->next; + } } } - else + else { - fdata->fails = 0; + if (m != NULL) m = m->next; + while (m != NULL) + { + if (!strcmp(p, m->name)) _asl_out_process_message(m, msg); + m = m->next; + } } - /* - * The current message is not a duplicate. If fdata->last_count > 0 - * we need to write a "last message repeated N times" log entry. - * _act_file_send_repeat_msg will free last_msg and do nothing if - * last_count == 0, but we test and free here to avoid a function call. - */ - if (fdata->last_count > 0) + asl_msg_release((asl_msg_t *)msg); + OSAtomicDecrement32(&global.asl_queue_count); + + if ((now - sweep_time) >= IDLE_CLOSE) { - _act_file_send_repeat_msg(fdata); + _asl_action_close_idle_files(IDLE_CLOSE); + sweep_time = now; } - else - { - free(fdata->last_msg); - fdata->last_msg = NULL; - } - - if (str != NULL) fdata->last_msg = strdup(str + 16); + }); +} - fdata->last_hash = msg_hash; - fdata->last_count = 0; - fdata->last_time = now; +static char * +_asl_action_profile_test(asl_out_module_t *m, asl_out_rule_t *r) +{ + const char *ident; + asl_msg_t *profile; + bool eval; - if ((str != NULL) && (len > 1)) write(fdata->fd, str, len - 1); - close(fdata->fd); - fdata->fd = -1; + /* ident is first message key */ + asl_msg_fetch((asl_msg_t *)r->query, 0, &ident, NULL, NULL); + if (ident == NULL) + { + r->action = ACTION_NONE; + return NULL; } - free(str); -} + profile = configuration_profile_to_asl_msg(ident); + eval = (asl_msg_cmp(r->query, profile) == 1); + _act_out_set_param(m, r->options, eval); + asl_msg_release(profile); -static void -_act_forward(action_rule_t *r, aslmsg msg) -{ - /* To do: Add a "forward" action to asl.conf */ + return strdup(ident); } -static void -_send_to_asl_store(aslmsg msg) +static const char * +_asl_action_file_test(asl_out_module_t *m, asl_out_rule_t *r) { - int log_me; - action_rule_t *r; + const char *path; + struct stat sb; + int status; + bool eval; - /* ASLOption "store" forces a message to be saved */ - log_me = asl_check_option(msg, ASL_OPT_STORE); - if (log_me == 1) + /* path is first message key */ + asl_msg_fetch((asl_msg_t *)r->query, 0, &path, NULL, NULL); + if (path == NULL) { - db_save_message(msg); - return; + r->action = ACTION_NONE; + return NULL; } - /* if there are no rules, save the message */ - if (asl_datastore_rule == NULL) - { - db_save_message(msg); - return; - } + memset(&sb, 0, sizeof(struct stat)); + status = stat(path, &sb); + eval = (status == 0); + _act_out_set_param(m, r->options, eval); - for (r = asl_datastore_rule; r != NULL; r = r->next) + return path; +} + +static void +_asl_action_handle_file_change_notification(int t) +{ + asl_out_module_t *m; + asl_out_rule_t *r; + + for (m = global.asl_out_module; m != NULL; m = m->next) { - if (asl_msg_cmp(r->query, (asl_msg_t *)msg) == 1) + for (r = m->ruleset; r != NULL; r = r->next) { - /* if any rule matches, save the message (once!) */ - db_save_message(msg); - return; + if (r->action == ACTION_SET_FILE) + { + asl_action_set_param_data_t *spdata = (asl_action_set_param_data_t *)r->private; + if ((spdata != NULL) && (spdata->token == t)) + { + _asl_action_file_test(m, r); + return; + } + } + else if (r->action == ACTION_SET_PLIST) + { + asl_action_set_param_data_t *spdata = (asl_action_set_param_data_t *)r->private; + if ((spdata != NULL) && (spdata->token == t)) + { + char *str = _asl_action_profile_test(m, r); + free(str); + return; + } + } + else if (r->action == ACTION_SET_PROF) + { + asl_action_set_param_data_t *spdata = (asl_action_set_param_data_t *)r->private; + if ((spdata != NULL) && (spdata->token == t)) + { + char *str = _asl_action_profile_test(m, r); + free(str); + return; + } + } } } + + asl_out_module_free(m); } static void -_asl_action_message(aslmsg msg) +_asl_action_post_process_rule(asl_out_module_t *m, asl_out_rule_t *r) { - action_rule_t *r; - - if (msg == NULL) return; + if ((m == NULL) || (r == NULL)) return; - /* reset flag bit used for file duplicate avoidance */ - for (r = asl_action_rule; r != NULL; r = r->next) + if (m != global.asl_out_module) { - if ((r->action == ACTION_FILE) && (r->data != NULL)) + /* check if any previous module has used this destination */ + asl_out_module_t *n; + bool search = true; + + if ((r->dst != NULL) && (r->dst->path != NULL)) { - ((struct file_data *)(r->data))->flags &= ACT_FLAG_CLEAR_LOGGED; + for (n = global.asl_out_module; search && (n != NULL) && (n != m); n = n->next) + { + asl_out_rule_t *s; + for (s = n->ruleset; search && (s != NULL); s = s->next) + { + if (s->action == ACTION_OUT_DEST) + { + if ((s->dst != NULL) && (s->dst->path != NULL) && (!strcmp(r->dst->path, s->dst->path))) + { + /* rule r of module m is using previously used dst of rule s of module n */ + asl_out_dst_data_release(r->dst); + r->dst = NULL; + + if (r->action == ACTION_OUT_DEST) + { + char *str = NULL; + asprintf(&str, "[Sender syslogd] [Level 5] [PID %u] [Message Configuration Notice:\nASL Module \"%s\" sharing output destination \"%s\" with ASL Module \"%s\".\nOutput parameters from ASL Module \"%s\" override any specified in ASL Module \"%s\".] [UID 0] [GID 0] [Facility syslog]", global.pid, m->name, s->dst->path, n->name, n->name, m->name); + internal_log_message(str); + free(str); + } + else + { + r->dst = asl_out_dst_data_retain(s->dst); + } + + search = false; + } + } + } + } } - else if (((r->action == ACTION_ASL_DIR) || (r->action == ACTION_ASL_FILE)) && (r->data != NULL)) + } + + if (r->action == ACTION_SET_PARAM) + { + if (r->query == NULL) _act_out_set_param(m, r->options, true); + } + else if (r->action == ACTION_CLAIM) + { + /* becomes ACTION_SKIP in com.apple.asl config */ + if (m != global.asl_out_module) { - ((struct store_data *)(r->data))->flags &= ACT_FLAG_CLEAR_LOGGED; + asl_out_rule_t *rule = (asl_out_rule_t *)calloc(1, sizeof(asl_out_rule_t)); + if (rule != NULL) + { + char *str = NULL; + asprintf(&str, "[Sender syslogd] [Level 5] [PID %u] [Message Configuration Notice:\nASL Module \"%s\" claims selected messages.\nThose messages may not appear in standard system log files or in the ASL database.] [UID 0] [GID 0] [Facility syslog]", global.pid, m->name); + internal_log_message(str); + free(str); + + rule->query = asl_msg_copy(r->query); + rule->action = ACTION_SKIP; + rule->next = global.asl_out_module->ruleset; + global.asl_out_module->ruleset = rule; + } + + /* + * After adding ACTION_SKIP to com.apple.asl module, the claim becomes a no-op in this module + * UNLESS the claim includes the option "only". In that case, the claim becomes a filter: + * any messages that DO NOT match the claim are skipped by this module. + */ + if (r->options == NULL) r->action = ACTION_NONE; + else if (strcmp(r->options, "only") != 0) r->action = ACTION_NONE; } } - - for (r = asl_action_rule; r != NULL; r = r->next) + else if (r->action == ACTION_ASL_STORE) + { + action_asl_store_count++; + } + else if (r->action == ACTION_ASL_DIR) + { + if (r->dst->private == NULL) r->dst->private = (asl_action_store_data_t *)calloc(1, sizeof(asl_action_store_data_t)); + } + else if (r->action == ACTION_ASL_FILE) + { + if (r->dst->private == NULL)r->dst->private = (asl_action_store_data_t *)calloc(1, sizeof(asl_action_store_data_t)); + } + else if (r->action == ACTION_FILE) + { + if (r->dst->private == NULL) r->dst->private = (asl_action_file_data_t *)calloc(1, sizeof(asl_action_file_data_t)); + if (r->dst->private != NULL) ((asl_action_file_data_t *)(r->dst->private))->fd = -1; + } + else if (r->action == ACTION_SET_PLIST) { - if (asl_msg_cmp(r->query, (asl_msg_t *)msg) == 1) + char *ident =_asl_action_profile_test(m, r); + char *notify_key = configuration_profile_create_notification_key(ident); + free(ident); + + if (notify_key != NULL) { - if ((r->action == ACTION_ASL_FILE) || (r->action == ACTION_ASL_DIR)) + int status, token; + asl_action_set_param_data_t *spdata; + + status = notify_register_dispatch(notify_key, &token, asl_action_queue, ^(int t){ + _asl_action_handle_file_change_notification(t); + }); + + free(notify_key); + + spdata = (asl_action_set_param_data_t *)calloc(1, sizeof(asl_action_set_param_data_t)); + if (spdata == NULL) { - _act_store(r, msg); - if (asl_check_option(msg, ASL_OPT_IGNORE) != 0) return; + notify_cancel(token); } + else + { + spdata->token = token; + r->private = spdata; + } + } + } + else if (r->action == ACTION_SET_PROF) + { + char *ident =_asl_action_profile_test(m, r); + char *notify_key = configuration_profile_create_notification_key(ident); + free(ident); - if (r->action == ACTION_NONE) continue; - else if (r->action == ACTION_IGNORE) return; - else if (r->action == ACTION_ACCESS) _act_access_control(r, msg); - else if (r->action == ACTION_NOTIFY) _act_notify(r); - else if (r->action == ACTION_BROADCAST) _act_broadcast(r, msg); - else if (r->action == ACTION_FILE) _act_file(r, msg); - else if (r->action == ACTION_FORWARD) _act_forward(r, msg); + if (notify_key != NULL) + { + int status, token; + asl_action_set_param_data_t *spdata; + + status = notify_register_dispatch(notify_key, &token, asl_action_queue, ^(int t){ + _asl_action_handle_file_change_notification(t); + }); + + free(notify_key); + + spdata = (asl_action_set_param_data_t *)calloc(1, sizeof(asl_action_set_param_data_t)); + if (spdata == NULL) + { + notify_cancel(token); + } + else + { + spdata->token = token; + r->private = spdata; + } } } + else if (r->action == ACTION_SET_FILE) + { + char *notify_key; + const char *path =_asl_action_file_test(m, r); + + if (path != NULL) + { + asprintf(¬ify_key, "%s%s", NOTIFY_PATH_SERVICE, path); + if (notify_key != NULL) + { + int status, token; + asl_action_set_param_data_t *spdata; + + status = notify_register_dispatch(notify_key, &token, asl_action_queue, ^(int t){ + _asl_action_handle_file_change_notification(t); + }); - if (asl_check_option(msg, ASL_OPT_IGNORE) != 0) return; + free(notify_key); - _send_to_asl_store(msg); + spdata = (asl_action_set_param_data_t *)calloc(1, sizeof(asl_action_set_param_data_t)); + if (spdata == NULL) + { + notify_cancel(token); + } + else + { + spdata->token = token; + r->private = spdata; + } + } + } + } } -void -asl_out_message(aslmsg msg) +static void +_asl_action_configure() { - dispatch_flush_continuation_cache(); + asl_out_rule_t *r; + asl_out_module_t *m; + uint32_t flags = 0; - asl_msg_retain((asl_msg_t *)msg); + if (global.asl_out_module == NULL) global.asl_out_module = asl_out_module_init(); + if (global.asl_out_module == NULL) return; - dispatch_async(asl_action_queue, ^{ - _asl_action_message(msg); - asl_msg_release((asl_msg_t *)msg); - }); -} + if (global.debug != 0) + { + FILE *dfp; + if (global.debug_file == NULL) dfp = fopen(_PATH_SYSLOGD_LOG, "a"); + else dfp = fopen(global.debug_file, "a"); + if (dfp != NULL) + { + for (m = global.asl_out_module; m != NULL; m = m->next) + { + fprintf(dfp, "module: %s%s\n", (m->name == NULL) ? "" : m->name, (m->flags & MODULE_FLAG_LOCAL) ? " (local)" : ""); + asl_out_module_print(dfp, m); + fprintf(dfp, "\n"); + } + fclose(dfp); + } + } -static int -_parse_config_file(const char *name) -{ - FILE *cf; - char *line; + asldebug("%s: init\n", MY_ID); + + action_asl_store_count = 0; - cf = fopen(name, "r"); - if (cf == NULL) return 1; + for (m = global.asl_out_module; m != NULL; m = m->next) + { + for (r = m->ruleset; r != NULL; r = r->next) + { + _asl_action_post_process_rule(m, r); + if (r->dst != NULL) flags |= (r->dst->flags & (MODULE_FLAG_ROTATE | MODULE_FLAG_CRASHLOG)); + } + } - while (NULL != (line = get_line_from_file(cf))) + if (global.debug != 0) { - _parse_line(line); - free(line); + FILE *dfp; + if (global.debug_file == NULL) dfp = fopen(_PATH_SYSLOGD_LOG, "a"); + else dfp = fopen(global.debug_file, "a"); + if (dfp != NULL) + { + for (m = global.asl_out_module; m != NULL; m = m->next) + { + fprintf(dfp, "module: %s%s\n", (m->name == NULL) ? "" : m->name, (m->flags & MODULE_FLAG_LOCAL) ? " (local)" : ""); + asl_out_module_print(dfp, m); + fprintf(dfp, "\n"); + } + fclose(dfp); + } } - fclose(cf); + sweep_time = time(NULL); - return 0; + if (flags & MODULE_FLAG_ROTATE) + { + _act_file_checkpoint_all(CHECKPOINT_TEST); + if (checkpoint_timer == NULL) _start_cycling(); + } + +#if TARGET_OS_EMBEDDED + if (flags & MODULE_FLAG_CRASHLOG) _crashlog_sentinel_init(); +#endif } int @@ -1581,90 +1680,143 @@ asl_action_init(void) { static dispatch_once_t once; - asldebug("%s: init\n", MY_ID); - _parse_config_file(_PATH_ASL_CONF); - dispatch_once(&once, ^{ asl_action_queue = dispatch_queue_create("ASL Action Queue", NULL); +#if TARGET_OS_EMBEDDED + crashlog_queue = dispatch_queue_create("iOS CrashLog Queue", NULL); + notify_register_dispatch(CRASH_MOVER_WILL_START_NOTIFICATION, &crashmover_token, asl_action_queue, ^(int unused){ + if (crashmover_state == 0) + { + asldebug("CrashMover active: suspending crashlog queue and closing files\n"); + crashmover_state = time(NULL); + dispatch_suspend(crashlog_queue); + _asl_action_close_idle_files(0); + } + }); +#endif }); + _asl_action_configure(); + return 0; } -int -_asl_action_close_internal(void) +/* + * Free a module. + */ +static void +_asl_action_free_modules(asl_out_module_t *m) { - action_rule_t *r, *n; - struct store_data *sd; - struct file_data *fdata; - n = NULL; - for (r = asl_action_rule; r != NULL; r = n) - { - n = r->next; - if (r->data != NULL) + asl_out_rule_t *r; + asl_out_module_t *x; + + /* + * asl_common frees a list of modules with asl_out_module_free. + * This loop frees the private data attached some modules. + */ + for (x = m; x != NULL; x = x->next) + { + for (r = x->ruleset; r != NULL; r = r->next) { - if (((r->action == ACTION_ASL_FILE) || (r->action == ACTION_ASL_DIR) || (r->action == ACTION_NONE))) + if ((r->action == ACTION_ASL_FILE) || (r->action == ACTION_ASL_DIR)) { - sd = (struct store_data *)r->data; - if (sd->refcount > 0) sd->refcount--; - if (sd->refcount == 0) + if (r->dst != NULL) { - if (sd->store != NULL) asl_file_close(sd->store); - if (sd->storedata != NULL) fclose(sd->storedata); - - free(sd->dir); - free(sd->path); - free(sd); + _asl_action_store_data_free((asl_action_store_data_t *)r->dst->private); + r->dst->private = NULL; } } - - if (r->action == ACTION_FILE) + else if (r->action == ACTION_FILE) { - fdata = (struct file_data *)r->data; - if (fdata->refcount > 0) fdata->refcount--; - if (fdata->refcount == 0) + if (r->dst != NULL) { - _act_file_send_repeat_msg(fdata); - - if (fdata->dup_timer != NULL) + asl_action_file_data_t *fdata = (asl_action_file_data_t *)r->dst->private; + if (fdata != NULL) { - dispatch_source_cancel(fdata->dup_timer); - dispatch_resume(fdata->dup_timer); - dispatch_release(fdata->dup_timer); + /* flush repeat message if necessary */ + if (fdata->last_count > 0) _send_repeat_msg(r); + _asl_action_file_data_free(fdata); + r->dst->private = NULL; } - - free(fdata->path); - free(fdata->fmt); - free(fdata->uid); - free(fdata->gid); - free(fdata->last_msg); - free(fdata); } } + else if (r->action == ACTION_SET_PLIST) + { + _asl_action_set_param_data_free((asl_action_set_param_data_t *)r->private); + } + else if (r->action == ACTION_SET_PROF) + { + _asl_action_set_param_data_free((asl_action_set_param_data_t *)r->private); + } } + } - if (r->query != NULL) asl_msg_release(r->query); - free(r->options); + asl_out_module_free(m); +} - free(r); +static int +_asl_action_close_internal(void) +{ +#if TARGET_OS_EMBEDDED + dispatch_source_cancel(crashlog_sentinel_src); + dispatch_release(crashlog_sentinel_src); + crashlog_sentinel_src = NULL; + close(crashlog_sentinel_fd); + if (crashmover_state != 0) + { + dispatch_resume(crashlog_queue); + crashmover_state = 0; } - asl_action_rule = NULL; + /* wait for the crashlog_queue to flush before _asl_action_free_modules() */ + dispatch_sync(crashlog_queue, ^{ crashlog_sentinel_fd = -1; }); +#endif - n = NULL; - for (r = asl_datastore_rule; r != NULL; r = n) - { - n = r->next; + _asl_action_free_modules(global.asl_out_module); + global.asl_out_module = NULL; + sweep_time = time(NULL); - if (r->query != NULL) asl_msg_release(r->query); - free(r->options); + return 0; +} - free(r); - } +static void +_asl_action_close_idle_files(time_t idle_time) +{ + asl_out_module_t *m; + time_t now = time(NULL); - asl_datastore_rule = NULL; + for (m = global.asl_out_module; m != NULL; m = m->next) + { + asl_out_rule_t *r; - return 0; + for (r = m->ruleset; r != NULL; r = r->next) + { + if (idle_time == 0) + { + if ((r->dst != NULL) && (r->dst->flags & MODULE_FLAG_CRASHLOG)) + { + _act_dst_close(r); + if (r->action != ACTION_ASL_DIR) asl_out_dst_checkpoint(r->dst, CHECKPOINT_FORCE); + } + } + else if ((r->action == ACTION_ASL_DIR) || (r->action == ACTION_ASL_FILE)) + { + if (r->dst != NULL) + { + asl_action_store_data_t *sdata = (asl_action_store_data_t *)r->dst->private; + if ((sdata != NULL) && (sdata->store != NULL) && ((now - sdata->last_time) >= idle_time)) _act_dst_close(r); + } + } + else if (r->action == ACTION_FILE) + { + if (r->dst != NULL) + { + asl_action_file_data_t *fdata = (asl_action_file_data_t *)r->dst->private; + if ((fdata != NULL) && (fdata->fd >= 0) && ((now - fdata->last_time) >= idle_time)) _act_dst_close(r); + } + } + } + } } int @@ -1688,17 +1840,157 @@ asl_action_reset(void) return 0; } +asl_out_module_t * +_asl_action_module_with_name(const char *name) +{ + asl_out_module_t *m; + + if (global.asl_out_module == NULL) return NULL; + if (name == NULL) return global.asl_out_module; + + for (m = global.asl_out_module; m != NULL; m = m->next) + { + if ((m->name != NULL) && (!strcmp(m->name, name))) return m; + } + + return NULL; +} + +/* + * called from control_message + * Used to control modules dynamically. + * Line format "@ module param [value ...]" + * + * Note this is synchronous on asl_action queue. + */ int -asl_action_file_rotate(const char *path) +asl_action_control_set_param(const char *s) { - /* - * The caller may want to know when the rotation has been completed, - * so this is synchronous. Also ensures the string stays intact while we work. - */ + __block char **l; + __block char *p; + uint32_t count = 0; + + if (s == NULL) return -1; + if (s[0] == '\0') return 0; + + /* skip '@' and whitespace */ + if (*s == '@') s++; + while ((*s == ' ') || (*s == '\t')) s++; + + l = explode(s, " \t"); + if (l != NULL) for (count = 0; l[count] != NULL; count++); + + /* at least 2 parameters (l[0] = module, l[1] = param) required */ + if (count < 2) return -1; + + if (global.asl_out_module == NULL) + { + asldebug("asl_action_control_set_param: no modules loaded\n"); + return -1; + } + + /* create / modify a module */ + if ((!strcasecmp(l[1], "define")) && (strcmp(l[0], "*"))) + { + p = strdup(s); + if (p == NULL) + { + asldebug("asl_action_control_set_param: memory allocation failed\n"); + return -1; + } + + dispatch_sync(asl_action_queue, ^{ + asl_out_module_t *m; + asl_out_rule_t *r; + + /* skip name, whitespace, "define" */ + while ((*p != ' ') && (*p != '\t')) p++; + while ((*p == ' ') || (*p == '\t')) p++; + while ((*p != ' ') && (*p != '\t')) p++; + + m = _asl_action_module_with_name(l[0]); + if (m == NULL) + { + asl_out_module_t *x; + + m = asl_out_module_new(l[0]); + for (x = global.asl_out_module; x->next != NULL; x = x->next); + x->next = m; + } + + r = asl_out_module_parse_line(m, p); + if (r != NULL) + { + _asl_action_post_process_rule(m, r); + if ((r->dst != NULL) && (r->dst->flags & MODULE_FLAG_ROTATE)) + { + _act_file_checkpoint_all(CHECKPOINT_TEST); + if (checkpoint_timer == NULL) _start_cycling(); + } + } + }); + + free(p); + free_string_list(l); + return 0; + } + dispatch_sync(asl_action_queue, ^{ - _act_file_rotate(path); + uint32_t intval; + int do_all = 0; + asl_out_module_t *m; + + if (!strcmp(l[0], "*")) + { + do_all = 1; + m = _asl_action_module_with_name(NULL); + } + else + { + m = _asl_action_module_with_name(l[0]); + } + + while (m != NULL) + { + if (!strcasecmp(l[1], "enable")) + { + intval = 1; + + /* don't do enable for ASL_MODULE_NAME if input name is "*" */ + if ((do_all == 0) || (strcmp(m->name, ASL_MODULE_NAME))) + { + /* @ module enable {0|1} */ + if (count > 2) intval = atoi(l[2]); + + if (intval == 0) m->flags &= ~MODULE_FLAG_ENABLED; + else m->flags |= MODULE_FLAG_ENABLED; + } + } + else if (!strcasecmp(l[1], "checkpoint")) + { + /* @ module checkpoint [file] */ + if (count > 2) _act_file_checkpoint(m, l[2], CHECKPOINT_FORCE); + else _act_file_checkpoint(m, NULL, CHECKPOINT_FORCE); + } + + if (do_all == 1) m = m->next; + else m = NULL; + } + }); + free_string_list(l); return 0; } +int +asl_action_file_checkpoint(const char *module, const char *path) +{ + /* Note this is synchronous on asl_action queue */ + dispatch_sync(asl_action_queue, ^{ + asl_out_module_t *m = _asl_action_module_with_name(module); + _act_file_checkpoint(m, path, CHECKPOINT_FORCE); + }); + + return 0; +} diff --git a/syslogd.tproj/asl_sim.conf b/syslogd.tproj/asl_sim.conf new file mode 100644 index 0000000..5af3ff6 --- /dev/null +++ b/syslogd.tproj/asl_sim.conf @@ -0,0 +1,34 @@ +## +# configuration file for syslogd and aslmanager in the iOS Simulator +## + +# redirect com.apple.message.domain to $ENV(IPHONE_SIMULATOR_LOG_ROOT)/DiagnosticMessages +? [T com.apple.message.domain] store_dir $ENV(IPHONE_SIMULATOR_LOG_ROOT)/DiagnosticMessages + +# redirect com.apple.performance* messages to $ENV(IPHONE_SIMULATOR_LOG_ROOT)/performance +? [A= Facility com.apple.performance] store_dir $ENV(IPHONE_SIMULATOR_LOG_ROOT)/performance + +# redirect com.apple.eventmonitor* messages to $ENV(IPHONE_SIMULATOR_LOG_ROOT)/eventmonitor +? [A= Facility com.apple.eventmonitor] store_dir $ENV(IPHONE_SIMULATOR_LOG_ROOT)/eventmonitor + +# broadcast emergency messages +? [= Level emergency] broadcast + +# ignore "internal" facility +? [= Facility internal] ignore + +# save everything from emergency to notice +? [<= Level notice] store + +# install messages get saved only in install.log +? [= Facility install] file $ENV(IPHONE_SIMULATOR_LOG_ROOT)/install.log format=bsd +? [= Facility install] ignore + +# emergency - notice get saved in system.log +? [<= Level notice] file $ENV(IPHONE_SIMULATOR_LOG_ROOT)/system.log + +# Facility auth to level info gets saved in system.log +? [= Facility auth] [<= Level info] file $ENV(IPHONE_SIMULATOR_LOG_ROOT)/system.log + +# Facility authpriv gets saved in system.log +? [= Facility authpriv] file $ENV(IPHONE_SIMULATOR_LOG_ROOT)/system.log diff --git a/syslogd.tproj/bb_convert.c b/syslogd.tproj/bb_convert.c index afc83d4..18ee310 100644 --- a/syslogd.tproj/bb_convert.c +++ b/syslogd.tproj/bb_convert.c @@ -42,7 +42,11 @@ extern time_t asl_parse_time(const char *); #define TEMP_NAME "_TMP_.asl" #define STORE_DATA_FLAGS 0x00000000 +#if TARGET_IPHONE_SIMULATOR +const char *store_path; +#else static const char *store_path = PATH_ASL_STORE; +#endif /* * Cache the output file for BB writes. diff --git a/syslogd.tproj/bsd_in.c b/syslogd.tproj/bsd_in.c index 7595f9c..c27ac8b 100644 --- a/syslogd.tproj/bsd_in.c +++ b/syslogd.tproj/bsd_in.c @@ -21,6 +21,8 @@ * @APPLE_LICENSE_HEADER_END@ */ +#include + #include #include #include @@ -59,7 +61,7 @@ bsd_in_acceptmsg(int fd) line[n] = '\0'; m = asl_input_parse(line, n, NULL, SOURCE_BSD_SOCKET); - dispatch_async(global.work_queue, ^{ process_message(m, SOURCE_BSD_SOCKET); }); + process_message(m, SOURCE_BSD_SOCKET); } int @@ -69,7 +71,11 @@ bsd_in_init() int len; launch_data_t sockets_dict, fd_array, fd_dict; static dispatch_once_t once; - + +#if TARGET_IPHONE_SIMULATOR + const char *_PATH_SYSLOG_IN = getenv("IOS_SIMULATOR_SYSLOG_SOCKET"); +#endif + dispatch_once(&once, ^{ in_queue = dispatch_queue_create(MY_ID, NULL); }); diff --git a/syslogd.tproj/bsd_out.c b/syslogd.tproj/bsd_out.c index 48ab169..c7c0639 100644 --- a/syslogd.tproj/bsd_out.c +++ b/syslogd.tproj/bsd_out.c @@ -21,6 +21,12 @@ * @APPLE_LICENSE_HEADER_END@ */ +#include + +#if TARGET_IPHONE_SIMULATOR +struct _not_empty; +#else + #include #include #include @@ -346,10 +352,10 @@ _parse_line(char *s) out->count++; } - freeList(comma); + free_string_list(comma); } - freeList(semi); + free_string_list(semi); TAILQ_INSERT_TAIL(&bsd_out_rule, out, entries); @@ -537,7 +543,7 @@ _bsd_send(aslmsg msg, struct config_rule *r, char **out, char **fwd, time_t now) } else if (r->type == DST_TYPE_WALL) { -#ifndef CONFIG_IPHONE +#if !TARGET_OS_EMBEDDED FILE *pw = popen(_PATH_WALL, "w"); if (pw < 0) { @@ -656,11 +662,13 @@ bsd_out_message(aslmsg msg) { if (msg == NULL) return; + OSAtomicIncrement32(&global.bsd_queue_count); asl_msg_retain((asl_msg_t *)msg); dispatch_async(bsd_out_queue, ^{ _bsd_match_and_send(msg); asl_msg_release((asl_msg_t *)msg); + OSAtomicDecrement32(&global.bsd_queue_count); }); } @@ -795,3 +803,5 @@ bsd_out_reset(void) return 0; } + +#endif /* !TARGET_IPHONE_SIMULATOR */ diff --git a/syslogd.tproj/com.apple.syslogd.plist b/syslogd.tproj/com.apple.syslogd.plist index 5841c5e..df73a1d 100644 --- a/syslogd.tproj/com.apple.syslogd.plist +++ b/syslogd.tproj/com.apple.syslogd.plist @@ -17,6 +17,8 @@ EnableTransactions + POSIXSpawnType + Interactive ProgramArguments