]> git.saurik.com Git - apple/syslog.git/blobdiff - util.tproj/syslog.c
syslog-100.2.tar.gz
[apple/syslog.git] / util.tproj / syslog.c
index 74e69e78d54091db34a647c01c25759474b15358..383be598162d9fdc8c6f165be1faeb9776d7790d 100644 (file)
@@ -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@
  */
 #include <time.h>
 #include <string.h>
 #include <errno.h>
+#include <dirent.h>
+#include <fcntl.h>
 #include <sys/socket.h>
 #include <sys/sysctl.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
+#include <mach/mach.h>
+#include <servers/bootstrap.h>
 #include <netdb.h>
 #include <notify.h>
 #include <asl.h>
 #include <asl_private.h>
+#include <asl_ipc.h>
+#include <asl_core.h>
+#include <asl_store.h>
+#include <asl_file.h>
 
 #define MOD_CASE_FOLD 'C'
 #define MOD_REGEX     'R'
 
 #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
 #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
 #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, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
+       fprintf(f, "<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n");
+       fprintf(f, "<plist version=\"1.0\">\n");
+       fprintf(f, "<array>\n");
+}
+
+static void
+print_xml_trailer(FILE *f)
+{
+       if (f == NULL) return;
+
+       fprintf(f, "</array>\n");
+       fprintf(f, "</plist>\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] <Level>: 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", &notify_file, 0, &notify_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);
 }