X-Git-Url: https://git.saurik.com/apple/syslog.git/blobdiff_plain/b16a592aee171655b85f8e45c0604c38494ab8ea..e125da38929cd700d1ef4f2d364c6fbcbb2247cc:/util.tproj/syslog.c diff --git a/util.tproj/syslog.c b/util.tproj/syslog.c index 74e69e7..383be59 100644 --- a/util.tproj/syslog.c +++ b/util.tproj/syslog.c @@ -1,23 +1,22 @@ /* - * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2007-2009 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * - * "Portions Copyright (c) 2004 Apple Computer, Inc. All Rights - * Reserved. This file contains Original Code and/or Modifications of - * Original Code as defined in and that are subject to the Apple Public - * Source License Version 1.0 (the 'License'). You may not use this file - * except in compliance with the License. Please obtain a copy of the - * License at http://www.apple.com/publicsource and read it before using - * this file. + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the - * License for the specific language governing rights and limitations - * under the License." + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. * * @APPLE_LICENSE_HEADER_END@ */ @@ -29,14 +28,22 @@ #include #include #include +#include +#include #include #include #include #include +#include +#include #include #include #include #include +#include +#include +#include +#include #define MOD_CASE_FOLD 'C' #define MOD_REGEX 'R' @@ -54,6 +61,13 @@ #define ASL_QUERY_OP_NOT 0x1000 +#define QUERY_FLAG_SEARCH_REVERSE 0x00000001 + +#define FACILITY_CONSOLE "com.apple.console" + +/* Shared with Libc */ +#define NOTIFY_RC "com.apple.asl.remote" + #define SEARCH_EOF -1 #define SEARCH_NULL 0 #define SEARCH_MATCH 1 @@ -70,9 +84,16 @@ #define SEND_FORMAT_LEGACY 0 #define SEND_FORMAT_ASL 1 -#define PRINT_LOCALTIME 0x00000001 -#define PRINT_LEGACY_FMT 0x00000002 -#define PRINT_STD_FMT 0x00000004 +#define TIME_SEC 0x00000010 +#define TIME_UTC 0x00000020 +#define TIME_LCL 0x00000040 + +#define FORMAT_RAW 0x00000100 +#define FORMAT_LEGACY 0x00000200 +#define FORMAT_STD 0x00000400 +#define FORMAT_XML 0x00000800 + +#define EXPORT 0x00000100 #define ASL_FILTER_MASK_PACEWNID 0xff #define ASL_FILTER_MASK_PACEWNI 0x7f @@ -81,22 +102,34 @@ #define ASL_FILTER_MASK_PACE 0x0f #define ASL_FILTER_MASK_PAC 0x07 +#define FETCH_BATCH 1024 + +#define DB_SELECT_STORE 0 +#define DB_SELECT_FILES 1 +#define DB_SELECT_SYSLOGD 2 +#define DB_SELECT_LEGACY 3 -/* BEGIN PRIVATE API */ -#define _PATH_ASL_PRUNE "/var/run/asl_prune" -#define _PATH_SYSLOGD_PID "/var/run/syslog.pid" +static asl_file_list_t *db_files = NULL; +static asl_store_t *store = NULL; +static asl_file_t *legacy = NULL; +static asl_file_t *export = NULL; + +static uint32_t dbselect = DB_SELECT_STORE; /* notify SPI */ -uint32_t notify_get_state(int token, int *state); -uint32_t notify_set_state(int token, int state); uint32_t notify_register_plain(const char *name, int *out_token); extern char *asl_msg_to_string(aslmsg msg, uint32_t *len); extern asl_msg_t *asl_msg_from_string(const char *buf); +extern char *asl_list_to_string(asl_search_result_t *list, uint32_t *outlen); +extern asl_search_result_t *asl_list_from_string(const char *buf); extern int asl_msg_cmp(asl_msg_t *a, asl_msg_t *b); extern time_t asl_parse_time(const char *in); /* END PRIVATE API */ +#define ASL_SERVICE_NAME "com.apple.system.logger" +static mach_port_t asl_server_port = MACH_PORT_NULL; + static const char *myname = "syslog"; void @@ -122,18 +155,23 @@ usage() fprintf(stderr, " d = Debug\n"); fprintf(stderr, " a minus sign preceeding a single letter means \"up to\" that level\n"); fprintf(stderr, "\n"); - fprintf(stderr, "%s -p [-k key [[op] val]]... [-o -k key [[op] val]] ...]...\n", myname); - fprintf(stderr, " -p prune datastore according to input expression (see below)\n"); - fprintf(stderr, "\n"); - fprintf(stderr, "%s [-w] [-F format] [-u] [-k key [[op] val]]... [-o -k key [[op] val]] ...]...\n", myname); - fprintf(stderr, " -w watch file (^C to quit)\n"); - fprintf(stderr, " -F output format may be \"std\", \"raw\", or \"bsd\"\n"); + fprintf(stderr, "%s [-f file...] [-d path...] [-x file] [-w [N]] [-F format] [-u] [-k key [[op] val]]... [-o -k key [[op] val]] ...]...\n", myname); + fprintf(stderr, " -f read named file[s], rather than standard log message store.\n"); + fprintf(stderr, " -d read all file in named directory path, rather than standard log message store.\n"); + fprintf(stderr, " -x export to named ASL format file, rather than printing\n"); + fprintf(stderr, " -w watch data store (^C to quit)\n"); + fprintf(stderr, " prints the last N matching lines (default 10) before waiting\n"); + fprintf(stderr, " \"-w 0\" prints all matching lines before waiting\n"); + fprintf(stderr, " -F output format may be \"std\", \"raw\", \"bsd\", or \"xml\"\n"); fprintf(stderr, " format may also be a string containing variables of the form\n"); fprintf(stderr, " $Key or $(Key) - use the latter for non-whitespace delimited variables\n"); - fprintf(stderr, " -u force printing of all timestamps using UTC\n"); + fprintf(stderr, " -T timestamp format may be \"sec\" (seconds), \"utc\" (UTC), or \"local\" (local timezone)\n"); + fprintf(stderr, " -E text encoding may be \"vis\", \"safe\", or \"none\"\n"); + fprintf(stderr, " -u print timestamps using UTC (equivalent to \"-T utc\")\n"); fprintf(stderr, " -k key/value match\n"); fprintf(stderr, " if no operator or value is given, checks for the existance of the key\n"); fprintf(stderr, " if no operator is given, default is \"%s\"\n", OP_EQ); + fprintf(stderr, " -C alias for \"-k Facility com.apple.console\"\n"); fprintf(stderr, " -o begins a new query\n"); fprintf(stderr, " queries are \'OR\'ed together\n"); fprintf(stderr, "operators are zero or more modifiers followed by a comparison\n"); @@ -196,8 +234,8 @@ procinfo(char *pname, int *pid, int *uid) do { size += size / 10; - newprocs = realloc(procs, size); - if (newprocs == 0) + newprocs = reallocf(procs, size); + if (newprocs == NULL) { if (procs != NULL) free(procs); return PROC_NOT_FOUND; @@ -262,31 +300,12 @@ procinfo(char *pname, int *pid, int *uid) } int -rcontrol_get_string(const char *prefix, int pid, int *val) +rcontrol_get_string(const char *name, int *val) { - int t, x, status; - char *name; - - status = NOTIFY_STATUS_OK; - - if (pid == RC_SYSLOGD) - { - status = notify_register_plain(NOTIFY_SYSTEM_ASL_FILTER, &t); - } - else if (pid == RC_MASTER) - { - status = notify_register_plain(NOTIFY_SYSTEM_MASTER, &t); - } - else - { - name = NULL; - asprintf(&name, "%s.%d", prefix, pid); - if (name == NULL) return NOTIFY_STATUS_FAILED; - - status = notify_register_plain(name, &t); - free(name); - } + int t, status; + uint64_t x; + status = notify_register_plain(name, &t); if (status != NOTIFY_STATUS_OK) return status; x = 0; @@ -299,34 +318,17 @@ rcontrol_get_string(const char *prefix, int pid, int *val) } int -rcontrol_set_string(const char *prefix, int pid, int filter) +rcontrol_set_string(const char *name, int filter) { int t, status; - char *name; - - status = NOTIFY_STATUS_OK; - - if (pid == RC_SYSLOGD) - { - status = notify_register_plain(NOTIFY_SYSTEM_ASL_FILTER, &t); - } - else if (pid == RC_MASTER) - { - status = notify_register_plain(NOTIFY_SYSTEM_MASTER, &t); - } - else - { - name = NULL; - asprintf(&name, "%s.%d", prefix, pid); - if (name == NULL) return NOTIFY_STATUS_FAILED; - - status = notify_register_plain(name, &t); - free(name); - } + uint64_t x; + status = notify_register_plain(name, &t); if (status != NOTIFY_STATUS_OK) return status; - status = notify_set_state(t, filter); - if ((pid == RC_SYSLOGD) && (status == NOTIFY_STATUS_OK)) status = notify_post(NOTIFY_SYSTEM_ASL_FILTER); + + x = filter; + status = notify_set_state(t, x); + notify_post(NOTIFY_RC); notify_cancel(t); return status; } @@ -388,37 +390,37 @@ asl_filter_string(int f) strcat(str, "Emergency - Debug"); return str; } - + if ((f == ASL_FILTER_MASK_PACEWNI) != 0) { strcat(str, "Emergency - Info"); return str; } - + if ((f == ASL_FILTER_MASK_PACEWN) != 0) { strcat(str, "Emergency - Notice"); return str; } - + if ((f == ASL_FILTER_MASK_PACEW) != 0) { strcat(str, "Emergency - Warning"); return str; } - + if ((f == ASL_FILTER_MASK_PACE) != 0) { strcat(str, "Emergency - Error"); return str; } - + if ((f == ASL_FILTER_MASK_PAC) != 0) { strcat(str, "Emergency - Critical"); return str; } - + if ((f & ASL_FILTER_MASK_EMERG) != 0) { strcat(str, "Emergency"); @@ -475,12 +477,26 @@ asl_filter_string(int f) } if (i == 0) sprintf(str, "Off"); - + + return str; +} + +const char * +rcontrol_name(pid_t pid, uid_t uid) +{ + static char str[1024]; + + if (pid == RC_SYSLOGD) return NOTIFY_SYSTEM_ASL_FILTER; + if (pid == RC_MASTER) return NOTIFY_SYSTEM_MASTER; + + memset(str, 0, sizeof(str)); + if (uid == 0) snprintf(str, sizeof(str) - 1, "%s.%d", NOTIFY_PREFIX_SYSTEM, pid); + else snprintf(str, sizeof(str) - 1, "user.uid.%d.syslog.%d", uid, pid); return str; } int -rcontrol_get(const char *prefix, int pid) +rcontrol_get(pid_t pid, uid_t uid) { int filter, status; const char *name; @@ -492,7 +508,7 @@ rcontrol_get(const char *prefix, int pid) name = "Master"; if (pid == RC_SYSLOGD) name = "ASL Data Store"; - status = rcontrol_get_string(NULL, pid, &filter); + status = rcontrol_get_string(rcontrol_name(pid, uid), &filter); if (status == NOTIFY_STATUS_OK) { printf("%s filter mask: %s\n", name, asl_filter_string(filter)); @@ -503,19 +519,19 @@ rcontrol_get(const char *prefix, int pid) return -1; } - status = rcontrol_get_string(prefix, pid, &filter); + status = rcontrol_get_string(rcontrol_name(pid, uid), &filter); if (status == NOTIFY_STATUS_OK) { printf("Process %d syslog filter mask: %s\n", pid, asl_filter_string(filter)); return 0; } - + printf("Unable to determine syslog filter mask for pid %d\n", pid); return -1; } int -rcontrol_set(const char *prefix, int pid, int filter) +rcontrol_set(pid_t pid, uid_t uid, int filter) { int status; const char *name; @@ -524,7 +540,7 @@ rcontrol_set(const char *prefix, int pid, int filter) { name = "Master"; if (pid == RC_SYSLOGD) name = "ASL Data Store"; - status = rcontrol_set_string(NULL, pid, filter); + status = rcontrol_set_string(rcontrol_name(pid, uid), filter); if (status == NOTIFY_STATUS_OK) { @@ -536,9 +552,10 @@ rcontrol_set(const char *prefix, int pid, int filter) return -1; } - status = rcontrol_set_string(prefix, pid, filter); + status = rcontrol_set_string(rcontrol_name(pid, uid), filter); if (status == NOTIFY_STATUS_OK) { + if (pid == RC_SYSLOGD) status = notify_post(NOTIFY_SYSTEM_ASL_FILTER); printf("Set process %d syslog filter mask set: %s\n", pid, asl_filter_string(filter)); return 0; } @@ -657,19 +674,19 @@ static int _isanumber(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; } @@ -679,7 +696,7 @@ asl_string_to_level(const char *s) if (s == NULL) return -1; if ((s[0] >= '0') && (s[0] <= '7') && (s[1] == '\0')) return atoi(s); - + if (!strncasecmp(s, "em", 2)) return ASL_LEVEL_EMERG; else if (!strncasecmp(s, "p", 1)) return ASL_LEVEL_EMERG; else if (!strncasecmp(s, "a", 1)) return ASL_LEVEL_ALERT; @@ -693,12 +710,32 @@ asl_string_to_level(const char *s) return -1; } - + +const char * +asl_string_to_char_level(const char *s) +{ + if (s == NULL) return NULL; + + if ((s[0] >= '0') && (s[0] <= '7') && (s[1] == '\0')) return s; + + if (!strncasecmp(s, "em", 2)) return "0"; + else if (!strncasecmp(s, "p", 1)) return "0"; + else if (!strncasecmp(s, "a", 1)) return "1"; + else if (!strncasecmp(s, "c", 1)) return "2"; + else if (!strncasecmp(s, "er", 2)) return "3"; + else if (!strncasecmp(s, "x", 1)) return "3"; + else if (!strncasecmp(s, "w", 1)) return "4"; + else if (!strncasecmp(s, "n", 1)) return "5"; + else if (!strncasecmp(s, "i", 1)) return "6"; + else if (!strncasecmp(s, "d", 1)) return "7"; + + return NULL; +} + int syslog_remote_control(int argc, char *argv[]) { int pid, uid, status, mask; - const char *prefix; if ((argc < 3) || (argc > 4)) { @@ -748,9 +785,6 @@ syslog_remote_control(int argc, char *argv[]) if (pid == 0) pid = RC_MASTER; - prefix = NOTIFY_PREFIX_USER; - if (uid == 0) prefix = NOTIFY_PREFIX_SYSTEM; - if (argc == 4) { if ((pid == RC_MASTER) && (!strcasecmp(argv[3], "off"))) mask = 0; @@ -765,41 +799,39 @@ syslog_remote_control(int argc, char *argv[]) } } - rcontrol_set(prefix, pid, mask); + rcontrol_set(pid, uid, mask); } else { - rcontrol_get(prefix, pid); + rcontrol_get(pid, uid); } return 0; } - + int syslog_send(int argc, char *argv[]) { - int i, start, kv, len, rfmt, rlevel; + int i, start, kv, len, rfmt, rlevel, filter, status; aslclient asl; aslmsg m; char tmp[64], *str, *rhost; kv = 0; rhost = NULL; - rfmt = SEND_FORMAT_ASL; + rfmt = SEND_FORMAT_LEGACY; start = 1; rlevel = 7; for (i = 1; i < argc; i++) { if (!strcmp(argv[i], "-s")) start = i+1; - else if (!strcmp(argv[i], "-k")) kv = 1; - else if (!strcmp(argv[i], "-r")) + else if (!strcmp(argv[i], "-k")) { - rhost = argv[++i]; - start = i+1; - rfmt = SEND_FORMAT_LEGACY; + kv = 1; + rfmt = SEND_FORMAT_ASL; } - else if (!strcmp(argv[i], "-R")) + else if (!strcmp(argv[i], "-r")) { rhost = argv[++i]; start = i+1; @@ -832,6 +864,8 @@ syslog_send(int argc, char *argv[]) len = 0; for (i = start; i < argc; i++) len += (strlen(argv[i]) + 1); str = calloc(len + 1, 1); + if (str == NULL) return -1; + for (i = start; i < argc; i++) { strcat(str, argv[i]); @@ -841,11 +875,28 @@ syslog_send(int argc, char *argv[]) } else { - for (i = start + 1; i < argc; i += 2) asl_set(m, argv[i], argv[i + 1]); + for (i = start + 1; i < argc; i += 2) + { + if (!strcmp(argv[i], "-k")) i++; + asl_set(m, argv[i], argv[i + 1]); + if (!strcmp(argv[i], ASL_KEY_LEVEL)) rlevel = atoi(argv[i + 1]); + } } if (rhost == NULL) { + filter = 0; + status = rcontrol_get_string(rcontrol_name(RC_SYSLOGD, 0), &filter); + if (status != 0) + { + fprintf(stderr, "Warning: Can't get current syslogd ASL filter value\n"); + } + else if ((ASL_FILTER_MASK(rlevel) & filter) == 0) + { + fprintf(stderr, "Warning: The current syslogd ASL filter value (%s)\n", asl_filter_string(filter)); + fprintf(stderr, " will exclude this message from the ASL database\n"); + } + asl_send(asl, m); } else if (rfmt == SEND_FORMAT_ASL) @@ -867,257 +918,216 @@ syslog_send(int argc, char *argv[]) } static void -printmsg(FILE *f, asl_msg_t *msg, char *mstr, char *fmt, int pflags) +print_xml_header(FILE *f) { - char *k, *t, c; - const char *v; - int i, j, l, paren, oval; - time_t tick; + if (f == NULL) return; + + fprintf(f, "\n"); + fprintf(f, "\n"); + fprintf(f, "\n"); + fprintf(f, "\n"); +} + +static void +print_xml_trailer(FILE *f) +{ + if (f == NULL) return; + + fprintf(f, "\n"); + fprintf(f, "\n"); +} - if ((pflags & PRINT_STD_FMT) || (pflags & PRINT_LEGACY_FMT)) +static void +printmsg(FILE *f, asl_msg_t *msg, char *fmt, int pflags) +{ + char *str; + const char *mf, *tf; + uint32_t encode, len, status; + uint64_t xid; + + if (f == NULL) { - /* LEGACY: Mth dd hh:mm:ss host sender[pid]: message */ - /* STD: Mth dd hh:mm:ss host sender[pid] : message */ - - /* Time */ - v = asl_get(msg, ASL_KEY_TIME); - tick = 0; - if (v == NULL) - { - fprintf(f, "***Time unknown "); - } - else + if (export != NULL) { - tick = asl_parse_time(v); - t = ctime(&tick); - if (t == NULL) fprintf(f, "***Time unknown "); - else + xid = 0; + status = asl_file_save(export, msg, &xid); + if (status != ASL_STATUS_OK) { - t[19] = '\0'; - fprintf(f, "%s ", t + 4); + fprintf(stderr, "export file write failed: %s\n", asl_core_error(status)); + asl_file_close(export); + export = NULL; } } - /* Host */ - v = asl_get(msg, ASL_KEY_HOST); - if (v != NULL) fprintf(f, "%s ", v); - - /* Sender */ - v = asl_get(msg, ASL_KEY_SENDER); - if (v != NULL) fprintf(f, "%s", v); - - /* PID */ - v = asl_get(msg, ASL_KEY_PID); - if ((v != NULL) && (v[0] != '-')) fprintf(f, "[%s]", v); + return; + } - v = asl_get(msg, ASL_KEY_LEVEL); - i = -1; - if (_isanumber((char *)v)) i = atoi(v); - if (pflags & PRINT_STD_FMT) fprintf(f, " <%s>", asl_level_string(i)); + encode = pflags & 0x0000000f; - fprintf(f, ": "); - - /* Message */ - v = asl_get(msg, ASL_KEY_MSG); - if (v != NULL) fprintf(f, "%s", v); + mf = ASL_MSG_FMT_RAW; + if (fmt != NULL) mf = (const char *)fmt; + else if (pflags & FORMAT_STD) mf = ASL_MSG_FMT_STD; + else if (pflags & FORMAT_LEGACY) mf = ASL_MSG_FMT_BSD; + else if (pflags & FORMAT_XML) mf = ASL_MSG_FMT_XML; - fprintf(f, "\n"); - return; - } + tf = ASL_TIME_FMT_SEC; + if (pflags & TIME_UTC) tf = ASL_TIME_FMT_UTC; + if (pflags & TIME_LCL) tf = ASL_TIME_FMT_LCL; - if (fmt == NULL) + len = 0; + str = asl_format_message(msg, mf, tf, encode, &len); + if (str != NULL) { - fprintf(f, "%s\n", mstr); - return; + fprintf(f, "%s", str); + free(str); } +} - for (i = 0; fmt[i] != '\0'; i++) - { - if (fmt[i] == '$') - { - i++; - paren = 0; +asl_search_result_t * +store_query(asl_search_result_t *q, uint64_t start, int count, int dir, uint64_t *last) +{ + uint32_t status; + asl_search_result_t *res; - if (fmt[i] == '(') - { - paren = 1; - i++; - } + if (store == NULL) + { + status = asl_store_open_read(NULL, &store); + if (status != 0) return NULL; + } - k = calloc(1, 1); - l = 0; + res = NULL; + status = asl_store_match(store, q, &res, last, start, count, dir); + if (status != 0) return NULL; - for (j = i; fmt[j] != '\0'; j++) - { - c = '\0'; - if (fmt[j] == '\\') c = fmt[++j]; - else if ((paren == 1) && (fmt[j] ==')')) break; - else if (fmt[j] != ' ') c = fmt[j]; + return res; +} - if (c == '\0') break; +asl_search_result_t * +file_query(asl_search_result_t *q, uint64_t start, int count, int dir, uint64_t *last) +{ + uint32_t status; + asl_search_result_t *res; - k = realloc(k, l + 1); - k[l] = c; - k[l + 1] = '\0'; - l++; - } + res = NULL; + status = asl_file_list_match(db_files, q, &res, last, start, count, dir); + if (status != 0) return NULL; - if (paren == 1) j++; - i = j; - if (l > 0) - { - v = asl_get(msg, k); - if (v != NULL) - { - if ((pflags & PRINT_LOCALTIME) && (!strcmp(k, ASL_KEY_TIME))) - { - /* convert UTC time to localtime */ - tick = asl_parse_time(v); - t = ctime(&tick); - if (t == NULL) fprintf(f, "%s", v); - else - { - t[19] = '\0'; - fprintf(f, "%s", t + 4); - } - } - else - { - fprintf(f, "%s", v); - } - } - } - free(k); - } + return res; +} - if (fmt[i] == '\\') - { - i++; - if (fmt[i] == '$') fprintf(f, "$"); - else if (fmt[i] == 'e') fprintf(f, "\e"); - else if (fmt[i] == 'a') fprintf(f, "\a"); - else if (fmt[i] == 'b') fprintf(f, "\b"); - else if (fmt[i] == 'f') fprintf(f, "\f"); - else if (fmt[i] == 'n') fprintf(f, "\n"); - else if (fmt[i] == 'r') fprintf(f, "\r"); - else if (fmt[i] == 't') fprintf(f, "\t"); - else if (fmt[i] == 'v') fprintf(f, "\v"); - else if (fmt[i] == '\'') fprintf(f, "\'"); - else if (fmt[i] == '\\') fprintf(f, "\\"); - else if (isdigit(fmt[i])) - { - oval = fmt[i] - '0'; - if (isdigit(fmt[i+1])) - { - i++; - oval = (oval * 8) + (fmt[i] - '0'); - if (isdigit(fmt[i+1])) - { - i++; - oval = (oval * 8) + (fmt[i] - '0'); - } - } - c = oval; - fputc(c, stdout); - } - continue; - } +asl_search_result_t * +legacy_query(asl_search_result_t *q, uint64_t start, int count, int dir, uint64_t *last) +{ + uint32_t status; + asl_search_result_t *res; - if (fmt[i] == '\0') break; - fputc(fmt[i], stdout); - } + res = NULL; + status = asl_file_match(legacy, q, &res, last, start, count, dir); + if (status != 0) return NULL; - fprintf(f, "\n"); + return res; } -static char * -getnextline(FILE *fp, int watch) +asl_search_result_t * +syslogd_query(asl_search_result_t *q, uint64_t start, int count, int dir, uint64_t *last) { - char *out, c; - int len, count; - - len = CHUNK; - count = 0; - out = calloc(len + 1, 1); - - forever + char *str, *res; + caddr_t vmstr; + uint32_t len, reslen, status; + int flags; + kern_return_t kstatus; + security_token_t sec; + asl_search_result_t *l; + + if (asl_server_port == MACH_PORT_NULL) { - c = getc(fp); - if (c == EOF) + kstatus = bootstrap_look_up(bootstrap_port, ASL_SERVICE_NAME, &asl_server_port); + if (kstatus != KERN_SUCCESS) { - if (watch == 0) - { - if (count == 0) - { - free(out); - return NULL; - } - return out; - } - clearerr(fp); - usleep(250000); - continue; + fprintf(stderr, "query failed: can't contact syslogd\n"); + return NULL; } + } - if (c == '\n') return out; - if (c == '\0') return out; - - if (count == len) - { - len += CHUNK; - out = realloc(out, len + 1); - } + len = 0; + str = asl_list_to_string(q, &len); - out[count++] = c; - out[count] = '\0'; + kstatus = vm_allocate(mach_task_self(), (vm_address_t *)&vmstr, len, TRUE); + if (kstatus != KERN_SUCCESS) + { + free(str); + return NULL; } - return NULL; + memmove(vmstr, str, len); + free(str); + + res = NULL; + reslen = 0; + sec.val[0] = -1; + sec.val[1] = -1; + status = 0; + flags = 0; + if (dir < 0) flags = QUERY_FLAG_SEARCH_REVERSE; + + kstatus = _asl_server_query(asl_server_port, (caddr_t)vmstr, len, start, count, flags, (caddr_t *)&res, &reslen, last, (int *)&status, &sec); + + if (res == NULL) return NULL; + l = asl_list_from_string(res); + vm_deallocate(mach_task_self(), (vm_address_t)res, reslen); + return l; } -int -search_next(asl_msg_t **q, int nq, FILE *log, int watch, aslmsg *outmsg, char **outstr) +void +search_once(FILE *f, char *pfmt, int pflags, asl_search_result_t *ql, uint64_t qmin, uint64_t *cmax, uint32_t count, uint32_t batch, int dir, uint32_t tail) { - char *str; - aslmsg m; - int i, match; + asl_search_result_t *res; + int i, more, total; - *outmsg = NULL; - *outstr = NULL; + if (pflags & FORMAT_XML) print_xml_header(f); - if (log == NULL) return SEARCH_EOF; + res = NULL; + more = 1; + total = 0; - str = getnextline(log, watch); - if (str == NULL) return SEARCH_EOF; - - m = asl_msg_from_string(str); - if (m == NULL) + while (more == 1) { - free(str); - return SEARCH_NULL; - } + if (dbselect == DB_SELECT_STORE) res = store_query(ql, qmin, batch, dir, cmax); + else if (dbselect == DB_SELECT_FILES) res = file_query(ql, qmin, batch, dir, cmax); + else if (dbselect == DB_SELECT_SYSLOGD) res = syslogd_query(ql, qmin, batch, dir, cmax); + else if (dbselect == DB_SELECT_LEGACY) res = legacy_query(ql, qmin, batch, dir, cmax); - match = 0; - if (q == NULL) match = 1; - for (i = 0; (i < nq) && (match == 0); i++) - { - match = asl_msg_cmp(q[i], m); - if ((q[i]->count > 0) && (q[i]->op[0] & ASL_QUERY_OP_NOT)) + if ((dir >= 0) && (*cmax > qmin)) qmin = *cmax; + else if ((dir < 0) && (*cmax < qmin)) qmin = *cmax; + + if (res == NULL) { - match = !match; + more = 0; } - } + else + { + if ((batch > 0) && (res->count < batch)) more = 0; + total += res->count; + if ((count > 0) && (total >= count)) more = 0; - if (match == 0) - { - free(str); - asl_free(m); - return SEARCH_NULL; + i = 0; + if (tail != 0) + { + i = res->count - tail; + tail = 0; + if (i < 0) i = 0; + } + + if ((f != NULL) || (export != NULL)) + { + for (; i < res->count; i++) printmsg(f, res->msg[i], pfmt, pflags); + } + + aslresponse_free((aslresponse)res); + } } - *outmsg = m; - *outstr = str; - return SEARCH_MATCH; + if (pflags & FORMAT_XML) print_xml_trailer(f); } uint32_t @@ -1237,11 +1247,30 @@ int add_op(asl_msg_t *q, char *key, char *op, char *val, uint32_t flags) { uint32_t o; + const char *qval; if (key == NULL) return -1; if (q == NULL) return -1; + qval = NULL; + if (strcmp(key, ASL_KEY_TIME) == 0) + { + qval = (const char *)val; + } + else if ((strcmp(key, ASL_KEY_LEVEL) == 0) && (_isanumber(val) == 0)) + { + /* Convert level strings to numeric values */ + qval = asl_string_to_char_level(val); + if (qval == NULL) + { + fprintf(stderr, "invalid value for \"Level\"key: %s\n", val); + return -1; + } + } + o = ASL_QUERY_OP_NULL; + if (val == NULL) o = ASL_QUERY_OP_TRUE; + if (op != NULL) { o = optype(op); @@ -1252,43 +1281,157 @@ add_op(asl_msg_t *q, char *key, char *op, char *val, uint32_t flags) return -1; } - if ((o & ASL_QUERY_OP_NUMERIC) && (_isanumber(val) == 0)) + if ((qval == NULL) && (o & ASL_QUERY_OP_NUMERIC) && (_isanumber(val) == 0)) { fprintf(stderr, "non-numeric value supplied for numeric operator %s %s %s\n", key, op, val); return -1; } - } o |= flags; - asl_set_query(q, key, val, o); + if (qval != NULL) asl_set_query(q, key, qval, o); + else asl_set_query(q, key, val, o); return 0; } +static uint32_t +add_db_file(const char *name) +{ + asl_file_t *s; + uint32_t status; + + if (dbselect == DB_SELECT_LEGACY) + { + fprintf(stderr, "syslog can only read one legacy format database\n"); + fprintf(stderr, "can't combine legacy and non-legacy databases in a single search\n"); + exit(1); + } + + /* shouldn't happen */ + if (name == NULL) return DB_SELECT_STORE; + + s = NULL; + status = asl_file_open_read(name, &s); + if (status != ASL_STATUS_OK) + { + fprintf(stderr, "data store file %s open failed: %s \n", name, asl_core_error(status)); + exit(1); + } + + if (s == NULL) + { + fprintf(stderr, "data store file %s open failed\n", name); + exit(1); + } + + if (s->flags & ASL_FILE_FLAG_LEGACY_STORE) + { + if (db_files != NULL) + { + fprintf(stderr, "syslog can only read a single legacy format database\n"); + fprintf(stderr, "can't combine legacy and non-legacy databases in a single search\n"); + exit(1); + } + + legacy = s; + return DB_SELECT_LEGACY; + } + + db_files = asl_file_list_add(db_files, s); + return DB_SELECT_FILES; +} + +static uint32_t +add_db_dir(const char *name) +{ + DIR *dp; + struct dirent *dent; + uint32_t status; + asl_file_t *s; + char *path; + + /* + * Open all readable files + */ + dp = opendir(name); + if (dp == NULL) + { + fprintf(stderr, "%s: %s\n", name, strerror(errno)); + exit(1); + } + + while ((dent = readdir(dp)) != NULL) + { + if (dent->d_name[0] == '.') continue; + + path = NULL; + asprintf(&path, "%s/%s", name, dent->d_name); + + /* + * 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. + */ + s = NULL; + status = asl_file_open_read(path, &s); + if (path != NULL) free(path); + if ((status != ASL_STATUS_OK) || (s == NULL)) continue; + + db_files = asl_file_list_add(db_files, s); + } + + closedir(dp); + + return DB_SELECT_FILES; +} + int main(int argc, char *argv[]) { - FILE *log, *pf, *outfile; - int i, j, n, qcount, qn, watch, prune, status, pflags, tflag; - asl_msg_t **qlist, *outmsg; - char *logname, *outname, *pfmt, *outstr; - pid_t syslogd_pid; - uint32_t flags; - - qn = 0; + FILE *outfile; + int i, j, n, watch, status, pflags, tflags, iamroot, user_tflag; + int notify_file, notify_token; + asl_search_result_t *qlist; + asl_msg_t *cq; + char *pfmt; + const char *exportname; + uint32_t flags, tail_count, batch, encode; + uint64_t qmin, cmax; + watch = 0; - prune = 0; - logname = _PATH_ASL_OUT; - qlist = NULL; - qcount = 0; + iamroot = 0; + user_tflag = 0; pfmt = NULL; flags = 0; - pflags = PRINT_STD_FMT; - tflag = PRINT_LOCALTIME; + tail_count = 0; + batch = FETCH_BATCH; + pflags = FORMAT_STD; + tflags = TIME_LCL; + encode = ASL_ENCODE_ASL; + cq = NULL; + exportname = NULL; + + i = asl_store_location(); + if (i == ASL_STORE_LOCATION_MEMORY) dbselect = DB_SELECT_SYSLOGD; + + if (getuid() == 0) iamroot = 1; for (i = 1; i < argc; i++) { + if ((!strcmp(argv[i], "-help")) || (!strcmp(argv[i], "--help"))) + { + usage(); + exit(0); + } + + if (!strcmp(argv[i], "-time")) + { + qmin = time(NULL); + printf("%llu\n", qmin); + exit(0); + } + if (!strcmp(argv[i], "-s")) { syslog_send(argc, argv); @@ -1302,56 +1445,127 @@ main(int argc, char *argv[]) } } + qlist = (asl_search_result_t *)calloc(1, sizeof(asl_search_result_t)); + if (qlist == NULL) exit(1); + for (i = 1; i < argc; i++) { - if ((!strcmp(argv[i], "-help")) || (!strcmp(argv[i], "--help"))) + if (!strcmp(argv[i], "-f")) { - usage(); - exit(0); + if ((i + 1) < argc) + { + for (j = i + 1; j < argc; j++) + { + if (!strcmp(argv[j], "-")) + { + dbselect = DB_SELECT_SYSLOGD; + break; + } + else if (argv[j][0] == '-') + { + break; + } + else + { + dbselect = add_db_file(argv[j]); + } + } + } + } + if (!strcmp(argv[i], "-d")) + { + if ((i + 1) < argc) + { + for (j = i + 1; j < argc; j++) + { + if (!strcmp(argv[j], "store")) + { + dbselect = add_db_dir(PATH_ASL_STORE); + } + else if (!strcmp(argv[j], "archive")) + { + dbselect = add_db_dir(PATH_ASL_ARCHIVE); + } + else if (argv[j][0] == '-') + { + break; + } + else + { + dbselect = add_db_dir(argv[j]); + } + } + } } else if (!strcmp(argv[i], "-w")) { watch = 1; + tail_count = 10; + if (((i + 1) < argc) && (argv[i + 1][0] != '-')) + { + i++; + tail_count = atoi(argv[i]); + } } else if (!strcmp(argv[i], "-u")) { - tflag = 0; + tflags = TIME_UTC; + user_tflag = 1; } - else if (!strcmp(argv[i], "-p")) + else if (!strcmp(argv[i], "-x")) { - prune = 1; + if ((i + 1) >= argc) + { + aslresponse_free(qlist); + usage(); + exit(1); + } + + exportname = argv[++i]; } - else if (!strcmp(argv[i], "-f")) + else if (!strcmp(argv[i], "-E")) { if ((i + 1) >= argc) { + aslresponse_free(qlist); usage(); exit(1); } - logname = argv[++i]; + i++; + + if (!strcmp(argv[i], "vis")) encode = ASL_ENCODE_ASL; + else if (!strcmp(argv[i], "safe")) encode = ASL_ENCODE_SAFE; + else if (!strcmp(argv[i], "none")) encode = ASL_ENCODE_NONE; + else if ((argv[i][0] >= '0') && (argv[i][0] <= '9') && (argv[i][1] == '\0')) encode = atoi(argv[i]); } else if (!strcmp(argv[i], "-F")) { if ((i + 1) >= argc) { + aslresponse_free(qlist); usage(); exit(1); } i++; + if (!strcmp(argv[i], "raw")) { - pflags = 0; - tflag = 0; + pflags = FORMAT_RAW; + if (user_tflag == 0) tflags = TIME_SEC; } else if (!strcmp(argv[i], "std")) { - pflags = PRINT_STD_FMT; + pflags = FORMAT_STD; } else if (!strcmp(argv[i], "bsd")) { - pflags = PRINT_LEGACY_FMT; + pflags = FORMAT_LEGACY; + } + else if (!strcmp(argv[i], "xml")) + { + pflags = FORMAT_XML; } else { @@ -1359,27 +1573,68 @@ main(int argc, char *argv[]) pfmt = argv[i]; } } + else if (!strcmp(argv[i], "-T")) + { + if ((i + 1) >= argc) + { + aslresponse_free(qlist); + usage(); + exit(1); + } + + i++; + user_tflag = 1; + + if (!strcmp(argv[i], "sec")) tflags = TIME_SEC; + else if (!strcmp(argv[i], "utc")) tflags = TIME_UTC; + else if (!strcmp(argv[i], "local")) tflags = TIME_LCL; + else if (!strcmp(argv[i], "lcl")) tflags = TIME_LCL; + else tflags = TIME_LCL; + } else if (!strcmp(argv[i], "-o")) { flags = 0; - if (qlist == NULL) + if (qlist->count == 0) { - qlist = (asl_msg_t **)calloc(1, sizeof(asl_msg_t *)); + qlist->msg = (asl_msg_t **)calloc(1, sizeof(asl_msg_t *)); } else { - qlist = (asl_msg_t **)realloc(qlist, (qcount + 1) * sizeof(asl_msg_t *)); + qlist->msg = (asl_msg_t **)reallocf(qlist->msg, (qlist->count + 1) * sizeof(asl_msg_t *)); } - - qcount++; - qn = qcount - 1; - qlist[qn] = asl_new(ASL_TYPE_QUERY); + + if (qlist->msg == NULL) exit(1); + + cq = asl_new(ASL_TYPE_QUERY); + qlist->msg[qlist->count] = cq; + qlist->count++; } else if (!strcmp(argv[i], "-n")) { flags = ASL_QUERY_OP_NOT; } + else if (!strcmp(argv[i], "-C")) + { + if (qlist->count == 0) + { + qlist->msg = (asl_msg_t **)calloc(1, sizeof(asl_msg_t *)); + if (qlist->msg == NULL) exit(1); + + cq = asl_new(ASL_TYPE_QUERY); + qlist->msg[qlist->count] = cq; + qlist->count++; + } + + status = add_op(cq, ASL_KEY_FACILITY, OP_EQ, FACILITY_CONSOLE, flags); + + flags = 0; + if (status != 0) + { + aslresponse_free(qlist); + exit(1); + } + } else if (!strcmp(argv[i], "-k")) { i++; @@ -1405,117 +1660,114 @@ main(int argc, char *argv[]) continue; } - if (qlist == NULL) + if (qlist->count == 0) { - qlist = (asl_msg_t **)calloc(1, sizeof(asl_msg_t *)); - qcount = 1; - qn = 0; - qlist[qn] = asl_new(ASL_TYPE_QUERY); + qlist->msg = (asl_msg_t **)calloc(1, sizeof(asl_msg_t *)); + if (qlist->msg == NULL) exit(1); + + cq = asl_new(ASL_TYPE_QUERY); + qlist->msg[qlist->count] = cq; + qlist->count++; } status = 0; - if (n == 1) status = add_op(qlist[qn], argv[i], NULL, NULL, flags); - else if (n == 2) status = add_op(qlist[qn], argv[i], OP_EQ, argv[i+1], flags); - else status = add_op(qlist[qn], argv[i], argv[i+1], argv[i+2], flags); + if (n == 1) status = add_op(cq, argv[i], NULL, NULL, flags); + else if (n == 2) status = add_op(cq, argv[i], OP_EQ, argv[i+1], flags); + else status = add_op(cq, argv[i], argv[i+1], argv[i+2], flags); flags = 0; - if (status != 0) exit(1); + if (status != 0) + { + aslresponse_free(qlist); + exit(1); + } } } - pflags |= tflag; + pflags |= tflags; + pflags |= encode; + + outfile = stdout; - if (prune == 1) + if (exportname != NULL) { if (watch == 1) { - fprintf(stderr, "-w flag has no effect when pruning log file\n"); + fprintf(stderr, "Warning: -w flag has no effect with -x export flag\n"); + watch = 0; } - if (getuid() != 0) + status = asl_file_open_write(exportname, 0644, -1, -1, &export); + if (status != ASL_STATUS_OK) { - fprintf(stderr, "you must be root to prune the log file\n"); + aslresponse_free(qlist); + fprintf(stderr, "export file open failed: %s\n", asl_core_error(status)); exit(1); } - if (qlist == NULL) - { - fprintf(stderr, "no queries for pruning\n"); - exit(0); - } - - pf = fopen(_PATH_SYSLOGD_PID, "r"); - if (pf == NULL) - { - perror(_PATH_SYSLOGD_PID); - exit(1); - } + /* + * allow the string cache to be unlimited to maximize string dup compression + * preserve message IDs + */ + export->flags = ASL_FILE_FLAG_UNLIMITED_CACHE | ASL_FILE_FLAG_PRESERVE_MSG_ID; - status = fscanf(pf, "%d", &syslogd_pid); - fclose(pf); - if (status != 1) - { - fprintf(stderr, "can't read syslogd pid from %s\n", _PATH_SYSLOGD_PID); - exit(1); - } + outfile = NULL; + pflags = EXPORT; + } - unlink(_PATH_ASL_PRUNE); - pf = fopen(_PATH_ASL_PRUNE, "w"); - if (pf == NULL) - { - perror(_PATH_ASL_PRUNE); - exit(1); - } + qmin = 0; + cmax = 0; + notify_file = -1; + notify_token = -1; - for (i = 0; i < qcount; i++) + if (watch == 1) + { + if ((dbselect == DB_SELECT_STORE) || (dbselect == DB_SELECT_SYSLOGD)) { - outstr = asl_msg_to_string(qlist[i], &j); - fprintf(pf, "%s\n", outstr); - free(outstr); + status = notify_register_file_descriptor("com.apple.system.logger.message", ¬ify_file, 0, ¬ify_token); + if (status != NOTIFY_STATUS_OK) notify_token = -1; } - - fclose(pf); - - kill(syslogd_pid, SIGWINCH); - - exit(0); } - log = NULL; - - if (!strcmp(logname, "-")) log = stdin; - else log = fopen(logname, "r"); - - if (log == NULL) + if ((qlist->count == 0) && (watch == 1)) { - perror(logname); - exit(1); + qmin = -1; + search_once(NULL, NULL, 0, NULL, qmin, &cmax, 1, 1, -1, 0); + qmin = (cmax + 1) - tail_count; + tail_count = 0; } - if (watch == 1) fseek(log, 0, SEEK_END); - outname = NULL; - outfile = stdout; + /* output should be line buffered */ + if (outfile != NULL) setlinebuf(outfile); - do - { - outmsg = NULL; - outstr = NULL; - status = search_next(qlist, qcount, log, watch, &outmsg, &outstr); + search_once(outfile, pfmt, pflags, qlist, qmin, &cmax, 0, batch, 1, tail_count); - if (status == SEARCH_MATCH) + if (watch == 1) + { + if (notify_token == -1) { - printmsg(outfile, outmsg, outstr, pfmt, pflags); + forever + { + usleep(500000); + if (cmax > qmin) qmin = cmax; + search_once(outfile, pfmt, pflags, qlist, qmin + 1, &cmax, 0, batch, 1, 0); + } + } + else + { + while (read(notify_file, &i, 4) == 4) + { + if (cmax > qmin) qmin = cmax; + search_once(outfile, pfmt, pflags, qlist, qmin + 1, &cmax, 0, batch, 1, 0); + } } - - if (outstr != NULL) free(outstr); - if (outmsg != NULL) asl_free(outmsg); } - while (status != SEARCH_EOF); - fclose(log); + if (db_files != NULL) asl_file_list_close(db_files); + if (store != NULL) asl_store_close(store); + if (export != NULL) asl_file_close(export); - for (i = 0; i < qcount; i++) asl_free(qlist[i]); - if (qlist != NULL) free(qlist); + aslresponse_free(qlist); exit(0); }