]> git.saurik.com Git - apple/syslog.git/blobdiff - util.tproj/syslog.c
syslog-377.60.2.tar.gz
[apple/syslog.git] / util.tproj / syslog.c
index 3cc73bb7b8602cd50da85673a98daa91d89ff159..baa90f4e3e5dc5083c1f757d77a2b5feb682aaf5 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 Apple Inc. All rights reserved.
+ * Copyright (c) 2007-2015 Apple Inc. All rights reserved.
  *
  * @APPLE_LICENSE_HEADER_START@
  * 
@@ -21,6 +21,7 @@
  * @APPLE_LICENSE_HEADER_END@
  */
 
+#include <TargetConditionals.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
 #include <arpa/inet.h>
 #include <mach/mach.h>
 #include <servers/bootstrap.h>
+#include <bootstrap_priv.h>
 #include <netdb.h>
 #include <notify.h>
 #include <asl.h>
+#include <asl_msg.h>
+#include <asl_msg_list.h>
 #include <asl_private.h>
-#include <asl_ipc.h>
+#include "asl_ipc.h"
 #include <asl_core.h>
 #include <asl_store.h>
 #include <asl_file.h>
+#include <asl_client.h>
+#include "asl_common.h"
 
 #define MOD_CASE_FOLD 'C'
 #define MOD_REGEX     'R'
@@ -65,9 +71,6 @@
 
 #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
@@ -76,7 +79,6 @@
 #define PROC_NOT_UNIQUE -2
 
 #define RC_MASTER -1
-#define RC_SYSLOGD -2
 
 #define CHUNK 64
 #define forever for(;;)
 #define SEND_FORMAT_LEGACY 0
 #define SEND_FORMAT_ASL 1
 
-#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 COMPRESS_DUPS   0x00010000
 
 #define EXPORT                 0x00000100
 
 #define ASL_FILTER_MASK_PAC      0x07
 
 #define FETCH_BATCH    1024
+#define MAX_RANDOM 8192
+
+#define DB_SELECT_ASL     0
+#define DB_SELECT_STORE   1
+#define DB_SELECT_FILES   2
+#define DB_SELECT_SYSLOGD 3
+#define DB_SELECT_LEGACY  4
 
-#define DB_SELECT_STORE   0
-#define DB_SELECT_FILES   1
-#define DB_SELECT_SYSLOGD 2
-#define DB_SELECT_LEGACY  3
+/* STD and BSD format messages start with 'DAY MMM DD HH:MM:SS ' timestamp */
+#define STD_BSD_DATE_LEN 20
+
+/* Max message size for direct watch */
+#define MAX_DIRECT_SIZE 16384
+
+/* Buffer for direct watch data */
+#define DIRECT_BUF_SIZE 1024
 
 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;
-
-#ifdef CONFIG_IPHONE
+static const char *sort_key = NULL;
+static const char *sort_key_2 = NULL;
+static int sort_numeric = 0;
+static char *last_printmsg_str = NULL;
+static int last_printmsg_count = 0;
+static const char *tfmt = NULL;
+
+#if TARGET_OS_EMBEDDED
 static uint32_t dbselect = DB_SELECT_SYSLOGD;
 #else
-static uint32_t dbselect = DB_SELECT_STORE;
+static uint32_t dbselect = DB_SELECT_ASL;
 #endif
 
+typedef struct
+{
+       char *name;
+       uint32_t count;
+       uint32_t total_messages;
+       size_t total_size;
+       uint32_t *messages;
+       size_t *size;
+} sender_stat_t;
+
+#define ASL_IOS_STATS_DIR "/var/log/asl/Logs/ASLStatistics"
+static uint32_t stats_sender_count;
+static sender_stat_t **stats_sender;
+static uint32_t stats_total_all_messages;
+
 /* notify SPI */
 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);
+asl_msg_t *_asl_server_control_query(void);
 extern time_t asl_parse_time(const char *in);
+asl_msg_t * asl_base_msg(asl_client_t *asl, uint32_t level, const struct timeval *tv, const char *sstr, const char *fstr, const char *mstr);
 /* 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";
 
+/* forward */
+asl_msg_list_t *syslogd_query(asl_msg_list_t *q, uint64_t start, int count, int dir, uint64_t *last);
+static void printmsg(FILE *f, asl_msg_t *msg, char *fmt, int pflags);
+
+#define HELP_UNAVAILABLE -1
+#define HELP_CONTROL HELP_UNAVAILABLE /* undocumented */
+#define HELP_ALL 0
+#define HELP_SEND 1
+#define HELP_REMOTE_CONTROL 2
+#define HELP_CONFIG 3
+#define HELP_MODULE 4
+#define HELP_SEARCH 5
+#define HELP_STATS 6
+
 void
-usage()
+usage(uint32_t section)
 {
+       if (section == HELP_UNAVAILABLE)
+       {
+               fprintf(stderr, "help is not available for this command\n");
+               return;
+       }
+
        fprintf(stderr, "usage:\n");
-       fprintf(stderr, "%s -s [-r host] [-l level] message...\n", myname);
-       fprintf(stderr, "   send a message\n");
-       fprintf(stderr, "\n");
-       fprintf(stderr, "%s -s [-r host] -k key val [key val]...\n", myname);
-       fprintf(stderr, "   send a message with the given keys and values\n");
-       fprintf(stderr, "\n");
-       fprintf(stderr, "%s -c process [filter]\n", myname);
-       fprintf(stderr, "   get (set if filter is specified) syslog filter for process (pid or name)\n");
-       fprintf(stderr, "   level may be any combination of the characters \"p a c e w n i d\"\n");
-       fprintf(stderr, "   p = Emergency (\"Panic\")\n");
-       fprintf(stderr, "   a = Alert\n");
-       fprintf(stderr, "   c = Critical\n");
-       fprintf(stderr, "   e = Error\n");
-       fprintf(stderr, "   w = Warning\n");
-       fprintf(stderr, "   n = Notice\n");
-       fprintf(stderr, "   i = Info\n");
-       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 [-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, "   -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");
-       fprintf(stderr, "   %s   equal\n", OP_EQ);
-       fprintf(stderr, "   %s   not equal\n", OP_NE);
-       fprintf(stderr, "   %s   greater than\n", OP_GT);
-       fprintf(stderr, "   %s   greater or equal\n", OP_GE);
-       fprintf(stderr, "   %s   less than\n", OP_LT);
-       fprintf(stderr, "   %s   less or equal\n", OP_LE);
-       fprintf(stderr, "optional modifiers for operators\n");
-       fprintf(stderr, "   %c    case-fold\n", MOD_CASE_FOLD);
-       fprintf(stderr, "   %c    regular expression\n", MOD_REGEX);
-       fprintf(stderr, "   %c    substring\n", MOD_SUBSTRING);
-       fprintf(stderr, "   %c    prefix\n", MOD_PREFIX);
-       fprintf(stderr, "   %c    suffix\n", MOD_SUFFIX);
-       fprintf(stderr, "   %c    numeric comparison\n", MOD_NUMERIC);
+
+       if ((section == HELP_ALL) || (section == HELP_SEND))
+       {
+               fprintf(stderr, "%s -s [-r host] [-l level] message...\n", myname);
+               fprintf(stderr, "   send a message\n");
+               fprintf(stderr, "\n");
+
+               fprintf(stderr, "%s -s [-r host] -k key val [key val]...\n", myname);
+               fprintf(stderr, "   send a message with the given keys and values\n");
+               fprintf(stderr, "\n");
+       }
+
+       if ((section == HELP_ALL) || (section == HELP_REMOTE_CONTROL))
+       {
+               fprintf(stderr, "%s -c process [mask] [-s [on|off]] [-t [on|off]]\n", myname);
+               fprintf(stderr, "   get (set if mask or actions are specified) syslog filter mask and actions for process (pid or name)\n");
+               fprintf(stderr, "   mask may be any combination of the characters \"p a c e w n i d\"\n");
+               fprintf(stderr, "   p = Emergency (\"Panic\")\n");
+               fprintf(stderr, "   a = Alert\n");
+               fprintf(stderr, "   c = Critical\n");
+               fprintf(stderr, "   e = Error\n");
+               fprintf(stderr, "   w = Warning\n");
+               fprintf(stderr, "   n = Notice\n");
+               fprintf(stderr, "   i = Info\n");
+               fprintf(stderr, "   d = Debug\n");
+               fprintf(stderr, "   a minus sign preceding a single letter means \"up to\" that level\n");
+               fprintf(stderr, "   use \"0\" for process to get or set master syslog flags\n");
+               fprintf(stderr, "   use \"-c process off\" to deactivate current settings\n");
+               fprintf(stderr, "   -s controls sending ASL mesages (to syslogd)\n");
+               fprintf(stderr, "   -t controls sending Activity Tracing mesages\n");
+               fprintf(stderr, "\n");
+       }
+
+       if ((section == HELP_ALL) || (section == HELP_CONFIG))
+       {
+               fprintf(stderr, "%s -config [params...]\n", myname);
+               fprintf(stderr, "   without params, fetch and print syslogd parameters and statistics\n");
+               fprintf(stderr, "   otherwise, set or reset syslogd configuration parameters\n");
+               fprintf(stderr, "\n");
+       }
+
+       if ((section == HELP_ALL) || (section == HELP_MODULE))
+       {
+               fprintf(stderr, "%s -module [name [action]]\n", myname);
+               fprintf(stderr, "   with no name, prints configuration for all ASL output modules\n");
+               fprintf(stderr, "   with name and no action, prints configuration for named ASL output module\n");
+               fprintf(stderr, "   supported actions - module name required, use '*' (with single quotes) for all modules:\n");
+               fprintf(stderr, "       enable [01]          enables (or disables with 0) named module\n");
+               fprintf(stderr, "                            does not apply to com.apple.asl when '*' is used\n");
+               fprintf(stderr, "       checkpoint [file]    checkpoints all files or specified file for named module\n");
+               fprintf(stderr, "\n");
+       }
+
+       if ((section == HELP_ALL) || (section == HELP_SEARCH))
+       {
+               fprintf(stderr, "%s [-f file...] [-d path...] [-x file] [-w [N]] [-F format] [-nocompress] [-u] [-sort key1 [key2]] [-nsort key1 [key2]] [-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 all\" prints all matching lines before waiting\n");
+               fprintf(stderr, "          \"-w boot\" prints all matching lines since last system boot 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, "   -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, "   -nodc  no duplicate message compression\n");
+               fprintf(stderr, "   -u     print timestamps using UTC (equivalent to \"-T utc\")\n");
+               fprintf(stderr, "   -sort  sort messages using value for specified key1 (secondary sort by key2 if provided)\n");
+               fprintf(stderr, "   -nsort numeric sort messages using value for specified key1 (secondary sort by key2 if provided)\n");
+               fprintf(stderr, "   -k     key/value match\n");
+               fprintf(stderr, "          if no operator or value is given, checks for the existence of the key\n");
+               fprintf(stderr, "          if no operator is given, default is \"%s\"\n", OP_EQ);
+               fprintf(stderr, "   -B     only process log messages since last system boot\n");
+               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");
+               fprintf(stderr, "   %s   equal\n", OP_EQ);
+               fprintf(stderr, "   %s   not equal\n", OP_NE);
+               fprintf(stderr, "   %s   greater than\n", OP_GT);
+               fprintf(stderr, "   %s   greater or equal\n", OP_GE);
+               fprintf(stderr, "   %s   less than\n", OP_LT);
+               fprintf(stderr, "   %s   less or equal\n", OP_LE);
+               fprintf(stderr, "optional modifiers for operators\n");
+               fprintf(stderr, "   %c    case-fold\n", MOD_CASE_FOLD);
+               fprintf(stderr, "   %c    regular expression\n", MOD_REGEX);
+               fprintf(stderr, "   %c    substring\n", MOD_SUBSTRING);
+               fprintf(stderr, "   %c    prefix\n", MOD_PREFIX);
+               fprintf(stderr, "   %c    suffix\n", MOD_SUFFIX);
+               fprintf(stderr, "   %c    numeric comparison\n", MOD_NUMERIC);
+               fprintf(stderr, "\n");
+       }
+
+       if ((section == HELP_ALL) || (section == HELP_STATS))
+       {
+               fprintf(stderr, "%s -stats [-n n] [-d path] [-v]\n", myname);
+               fprintf(stderr, "   compiles and prints syslogd usage statistics\n");
+               fprintf(stderr, "   -n n     prints stats for just the top n (e.g. top 10) senders\n");
+               fprintf(stderr, "   -d path  reads the ASL database at the given path for statistics\n");
+               fprintf(stderr, "   -v       verbose ([message_count total_data data_average] for 10 minute intervals)\n");
+               fprintf(stderr, "\n");
+       }
+
+       fprintf(stderr, "NOTE:  Most system logs have moved to a new logging system.  See log(1) for more information.\n");
 }
 
 const char *
@@ -217,6 +319,170 @@ asl_level_string(int level)
        return "Unknown";
 }
 
+int
+module_control(int argc, char *argv[])
+{
+       const char *val = NULL;
+       uint64_t last;
+       char *str;
+       int i;
+
+       for (i = 2; i < argc; i++)
+       {
+               if ((!strcmp(argv[i], "-help")) || (!strcmp(argv[i], "--help")))
+               {
+                       usage(HELP_MODULE);
+                       return 0;
+               }
+       }
+
+       asl_msg_t *ctl = _asl_server_control_query();
+       if (ctl == NULL)
+       {
+               fprintf(stderr, "can't get status information from syslogd\n");
+               return -1;
+       }
+
+       argc -= 2;
+       argv += 2;
+
+       if (argc < 2)
+       {
+               int first = 1;
+
+               /* print config */
+               asl_out_module_t *m = asl_out_module_init();
+               asl_out_module_t *x = m;
+
+               while (x != NULL)
+               {
+                       if ((argc == 0) || (!strcmp(argv[0], x->name)))
+                       {
+                               asl_msg_lookup(ctl, x->name, &val, NULL);
+
+                               if (first == 0) printf("\n");
+                               first = 0;
+
+                               if (x->name == NULL) printf("ASL out module has no name\n");
+                               else printf("ASL out module: %s %s[current status: %s]\n", x->name, (x->flags & MODULE_FLAG_LOCAL) ? "local " : "",  (val == NULL) ? "unknown" : val );
+
+                               asl_out_module_print(stdout, x);
+                       }
+
+                       x = x->next;
+               }
+
+               asl_msg_release(ctl);
+               asl_out_module_free(m);
+               return 0;
+       }
+
+       /* name enable [val] */
+       /* name disable [val] */
+       if ((!strcmp(argv[1], "enable")) || (!strcmp(argv[1], "disable")))
+       {
+               int want = -1;
+               int status = -1;
+               asl_msg_t *cm;
+               asl_client_t *ac;
+
+               if (!strcmp(argv[1], "enable"))
+               {
+                       if (argc < 3) want = 1;
+                       else if (!strcmp(argv[2], "1")) want = 1;
+                       else if (!strcmp(argv[2], "0")) want = 0;
+                       else
+                       {
+                               printf("invalid value %s for %s %s - expecting 0 or 1\n", argv[2], argv[0], argv[1]);
+                               exit(-1);
+                       }
+               }
+               else
+               {
+                       if (argc < 3) want = 0;
+                       else if (!strcmp(argv[2], "1")) want = 0;
+                       else if (!strcmp(argv[2], "0")) want = 1;
+                       else
+                       {
+                               printf("invalid value %s for %s %s - expecting 0 or 1\n", argv[2], argv[0], argv[1]);
+                               exit(-1);
+                       }
+               }
+
+               asl_msg_lookup(ctl, argv[0], &val, NULL);
+               if (val != NULL)
+               {
+                       if (!strcmp(val, "enabled")) status = 1;
+                       else status = 0;
+               }
+
+               asl_msg_release(ctl);
+
+               if (want < 0)
+               {
+                       printf("internal error: want = -1\n");
+                       exit(-1);
+               }
+
+               if (want == status)
+               {
+                       printf("module %s is already %s\n", argv[0], val);
+                       return 0;
+               }
+
+               cm = asl_msg_new(ASL_TYPE_MSG);
+               asprintf(&str, "@ %s enable %d", argv[0], want);
+
+               if ((cm == NULL) || (str == NULL))
+               {
+                       fprintf(stderr, "can't allocate memory - exiting\n");
+                       exit(-1);
+               }
+
+               ac = asl_client_open(NULL, NULL, 0);
+               asl_client_set_filter(ac, ASL_FILTER_MASK_UPTO(ASL_LEVEL_DEBUG));
+               asl_msg_set_key_val(cm, ASL_KEY_LEVEL, "7");
+               asl_msg_set_key_val(cm, ASL_KEY_OPTION, "control");
+               asl_msg_set_key_val(cm, ASL_KEY_MSG, str);
+               asl_client_send(ac, cm);
+
+               asl_client_release(ac);
+               asl_msg_release(cm);
+               free(str);
+               return 0;
+       }
+
+       asl_msg_release(ctl);
+
+       /* name checkpoint [file] */
+       if (!strcmp(argv[1], "checkpoint"))
+       {
+               asl_msg_list_t *q = asl_msg_list_new();
+               asl_msg_t *qm = asl_msg_new(ASL_TYPE_QUERY);
+
+               if ((q == NULL) || (qm == NULL))
+               {
+                       fprintf(stderr, "can't allocate memory - exiting\n");
+                       exit(-1);
+               }
+
+               asl_msg_list_append(q, qm);
+               asl_msg_release(qm);
+
+               asl_msg_set_key_val_op(qm, ASL_KEY_OPTION, "control", ASL_QUERY_OP_EQUAL);
+               asprintf(&str, "%s checkpoint%s%s", argv[0], (argc > 2) ? " " : "", (argc > 2) ? argv[2] : "");
+               asl_msg_set_key_val_op(qm, "action", str, ASL_QUERY_OP_EQUAL);
+
+               asl_msg_list_t *res = syslogd_query((asl_msg_list_t *)q, 0, 0, 1, &last);
+               free(q);
+               asl_msg_list_release(res);
+               return 0;
+       }
+
+       printf("unknown module control: %s\n", argv[1]);
+       exit(-1);
+}
+
 int
 procinfo(char *pname, int *pid, int *uid)
 {
@@ -322,7 +588,7 @@ rcontrol_get_string(const char *name, int *val)
 }
 
 int
-rcontrol_set_string(const char *name, int filter)
+rcontrol_set_string(const char *name, uint32_t bits)
 {
        int t, status;
        uint64_t x;
@@ -330,7 +596,7 @@ rcontrol_set_string(const char *name, int filter)
        status = notify_register_plain(name, &t);
        if (status != NOTIFY_STATUS_OK) return status;
 
-       x = filter;
+       x = bits;
        status = notify_set_state(t, x);
        notify_post(NOTIFY_RC);
        notify_cancel(t);
@@ -490,7 +756,6 @@ 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));
@@ -499,68 +764,77 @@ rcontrol_name(pid_t pid, uid_t uid)
        return str;
 }
 
+void
+print_eval_bits(uint32_t eval)
+{
+       printf("0x%08x O%s ", eval, (eval & EVAL_ACTIVE) ? "N " : "FF");
+       if (eval & EVAL_SEND_ASL) printf("ASL ");
+       if (eval & EVAL_SEND_TRACE) printf("TRACE ");
+       if (eval & EVAL_TEXT_FILE) printf("TEXT ");
+       if (eval & EVAL_ASL_FILE) printf("FILE ");
+       if (eval & EVAL_TUNNEL) printf("TUNNEL ");
+       printf("/ 0x%02x %s\n", eval & EVAL_LEVEL_MASK, asl_filter_string(eval & EVAL_LEVEL_MASK));
+}
+
 int
 rcontrol_get(pid_t pid, uid_t uid)
 {
        int filter, status;
-       const char *name;
 
        filter = 0;
 
        if (pid < 0)
        {
-               name = "Master";
-               if (pid == RC_SYSLOGD) name = "ASL Data Store";
-
                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));
+                       printf("Master settings: ");
+                       print_eval_bits(filter);
                        return 0;
                }
 
-               printf("Unable to determine %s filter mask\n", name);
+               printf("Unable to determine master settings\n");
                return -1;
        }
 
        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));
+               printf("Process %d syslog settings: ", pid);
+               print_eval_bits(filter);
                return 0;
        }
 
-       printf("Unable to determine syslog filter mask for pid %d\n", pid);
+       printf("Unable to determine syslog settings for pid %d\n", pid);
        return -1;
 }
 
 int
-rcontrol_set(pid_t pid, uid_t uid, int filter)
+rcontrol_set(pid_t pid, uid_t uid, uint32_t bits)
 {
        int status;
-       const char *name;
+       const char *rcname;
+
+       rcname = rcontrol_name(pid, uid);
 
        if (pid < 0)
        {
-               name = "Master";
-               if (pid == RC_SYSLOGD) name = "ASL Data Store";
-               status = rcontrol_set_string(rcontrol_name(pid, uid), filter);
+               status = rcontrol_set_string(rcname, bits);
 
                if (status == NOTIFY_STATUS_OK)
                {
-                       printf("Set %s syslog filter mask: %s\n", name, asl_filter_string(filter));
+                       if (pid == RC_MASTER) status = notify_post(NOTIFY_SYSTEM_MASTER);
                        return 0;
                }
 
-               printf("Unable to set %s syslog filter mask: %s\n", name, notify_status_string(status));
+               printf("Unable to set master syslog filter mask: %s\n", notify_status_string(status));
                return -1;
        }
 
-       status = rcontrol_set_string(rcontrol_name(pid, uid), filter);
+       status = rcontrol_set_string(rcname, bits);
        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));
+               status = notify_post(rcname);
                return 0;
        }
 
@@ -569,18 +843,17 @@ rcontrol_set(pid_t pid, uid_t uid, int filter)
 }
 
 int
-rsend(aslmsg msg, char *rhost)
+rsend(asl_msg_t *msg, char *rhost)
 {
        char *str, *out;
        uint32_t len, level;
        char *timestr;
        const char *val;
        time_t tick;
-       struct tm gtime;
        int s;
        struct sockaddr_in dst;
        struct hostent *h;
-       char myname[MAXHOSTNAMELEN + 1];
+       char host_name[MAXHOSTNAMELEN + 1];
 
        if (msg == NULL) return 0;
 
@@ -598,28 +871,23 @@ rsend(aslmsg msg, char *rhost)
 
        level = ASL_LEVEL_DEBUG;
 
-       val = asl_get(msg, ASL_KEY_LEVEL);
+       val = asl_msg_get_val_for_key(msg, ASL_KEY_LEVEL);
        if (val != NULL) level = atoi(val);
 
-       memset(&gtime, 0, sizeof(struct tm));
-       timestr = NULL;
 
        tick = time(NULL);
-       gmtime_r(&tick, &gtime);
-
-       /* Canonical form: YYYY.MM.DD hh:mm:ss UTC */
-       asprintf(&timestr, "%d.%02d.%02d %02d:%02d:%02d UTC", gtime.tm_year + 1900, gtime.tm_mon + 1, gtime.tm_mday, gtime.tm_hour, gtime.tm_min, gtime.tm_sec);
-
+       timestr = NULL;
+       asprintf(&timestr, "%llu", (unsigned long long)tick);
        if (timestr != NULL)
        {
-               asl_set(msg, ASL_KEY_TIME, timestr);
+               asl_msg_set_key_val(msg, ASL_KEY_TIME, timestr);
                free(timestr);
        }
 
-       if (gethostname(myname, MAXHOSTNAMELEN) == 0) asl_set(msg, ASL_KEY_HOST, myname);
+       if (gethostname(host_name, MAXHOSTNAMELEN) == 0) asl_msg_set_key_val(msg, ASL_KEY_HOST, host_name);
 
        len = 0;
-       str = asl_msg_to_string(msg, &len);
+       str = asl_msg_to_string((asl_msg_t *)msg, &len);
        if (str == NULL) return -1;
 
        asprintf(&out, "%10u %s\n", len+1, str);
@@ -643,7 +911,7 @@ rlegacy(char *msg, int level, char *rhost)
        int s;
        struct sockaddr_in dst;
        struct hostent *h;
-       char myname[MAXHOSTNAMELEN + 1];
+       char host_name[MAXHOSTNAMELEN + 1];
 
        if (msg == NULL) return 0;
 
@@ -663,9 +931,9 @@ rlegacy(char *msg, int level, char *rhost)
        ltime = ctime(&tick);
        ltime[19] = '\0';
 
-       gethostname(myname, MAXHOSTNAMELEN);
+       gethostname(host_name, MAXHOSTNAMELEN);
 
-       asprintf(&out, "<%d>%s %s syslog[%d]: %s", level, ltime+4, myname, getpid(), msg);
+       asprintf(&out, "<%d>%s %s syslog[%d]: %s", level, ltime+4, host_name, getpid(), msg);
        len = strlen(out);
        sendto(s, out, len, 0, (const struct sockaddr *)&dst, sizeof(struct sockaddr_in));
 
@@ -674,6 +942,230 @@ rlegacy(char *msg, int level, char *rhost)
        return 0;
 }
 
+void
+stats_print_sender_stats(sender_stat_t *s, uint32_t interval, bool print_samples)
+{
+       uint32_t i;
+       uint32_t p10000 = (s->total_messages * 10000) / stats_total_all_messages;
+
+       if (strcmp(s->name, "*")) printf("%s: %u (%u.%02u%%) %lu\n", s->name, s->total_messages, p10000 / 100, p10000 % 100, s->total_size);
+       else printf("TOTAL: %u (100.00%%) %lu\n", s->total_messages, s->total_size);
+
+       if (print_samples)
+       {
+               int k = 0;
+               printf("[message_count data_size data_average]\n");
+
+               for (i = 0, k = 0; i < s->count; i++)
+               {
+                       size_t avg = 0;
+
+                       if (s->messages[i] > 0) avg = s->size[i] / s->messages[i];
+                       printf("[%u %lu %lu]", s->messages[i], s->size[i], avg);
+                       if (++k == 6)
+                       {
+                               printf("\n");
+                               k = 0;
+                       }
+                       else
+                       {
+                               printf(" ");
+                       }
+               }
+
+               for (; i < interval; i++)
+               {
+                       printf("[0 0 0]");
+                       if ((++k == 6) || ((i + 1) == interval))
+                       {
+                               printf("\n");
+                               k = 0;
+                       }
+                       else
+                       {
+                               printf(" ");
+                       }
+               }
+
+               printf("\n");
+       }
+}
+
+int
+stats_n_comp(const void *x, const void *y)
+{
+       int pn, qn;
+       sender_stat_t **p = (sender_stat_t **)x;
+       sender_stat_t **q = (sender_stat_t **)y;
+       pn = (*p)->total_messages;
+       qn = (*q)->total_messages;
+       return qn - pn;
+}
+
+void
+stats_sender_set_stat_numbers(const char *name, sender_stat_t *s, uint32_t interval, uint32_t nmsgs, size_t msize)
+{
+       s->messages = (uint32_t *)reallocf(s->messages, interval * sizeof(uint32_t));
+       s->size = (size_t *)reallocf(s->size, interval * sizeof(size_t));
+
+       for (; s->count < interval; s->count++)
+       {
+               s->messages[s->count] = 0;
+               s->size[s->count] = 0;
+       }
+
+       s->messages[interval - 1] = nmsgs;
+       s->size[interval - 1] = msize;
+
+       s->total_messages += nmsgs;
+       s->total_size += msize;
+
+       if (strcmp(name, "*")) stats_total_all_messages += nmsgs;
+}
+
+void
+stats_sender_set_stats(uint32_t interval, const char *name, uint32_t nmsgs, size_t msize)
+{
+       uint32_t i;
+       for (i = 0; i < stats_sender_count; i++)
+       {
+               if (strcmp(stats_sender[i]->name, name) == 0)
+               {
+                       stats_sender_set_stat_numbers(name, stats_sender[i], interval, nmsgs, msize);
+                       return;
+               }
+       }
+
+       stats_sender = (sender_stat_t **)realloc(stats_sender, (stats_sender_count + 1) * sizeof(sender_stat_t *));
+       stats_sender[stats_sender_count] = (sender_stat_t *)calloc(1, sizeof(sender_stat_t));
+       stats_sender[stats_sender_count]->name = strdup(name);
+
+       stats_sender_set_stat_numbers(name, stats_sender[stats_sender_count], interval, nmsgs, msize);
+       stats_sender_count++;
+}
+
+void
+stats_process_stat_msg(uint32_t interval, asl_object_t msg)
+{
+       uint32_t i, n;
+
+       n = asl_count(msg);
+
+       for (i = 0; i < n; i++)
+       {
+               const char *key = NULL;
+               const char *val = NULL;
+               uint32_t s_n = 0;
+               size_t s_size = 0;
+               if (asl_fetch_key_val_op(msg, i, &key, &val, NULL) != 0) break;
+               if (key[0] == '*')
+               {
+                       if (2 == sscanf(val, "%u %lu", &s_n, &s_size))
+                       {
+                               stats_sender_set_stats(interval, key + 1, s_n, s_size);
+                       }
+               }
+       }
+}
+
+
+int
+asl_stats(int argc, char *argv[])
+{
+       asl_object_t msg;
+       uint32_t i, interval, top = UINT32_MAX;
+       bool print_samples = false;
+       asl_object_t store = NULL;
+
+       for (i = 2; i < argc; i++)
+       {
+               if ((!strcmp(argv[i], "-help")) || (!strcmp(argv[i], "--help")))
+               {
+                       usage(HELP_STATS);
+                       return 0;
+               }
+               else if (!strcmp(argv[i], "-n"))
+               {
+                       i++;
+                       if (i >= argc)
+                       {
+                               fprintf(stderr, "error: expected an integer value following \"-n\"\n");
+                               usage(HELP_STATS);
+                               return -1;
+                       }
+
+                       top = 1 + atoi(argv[i]);
+               }
+               else if (!strcmp(argv[i], "-v"))
+               {
+                       print_samples = true;
+               }
+               else if (!strcmp(argv[i], "-d"))
+               {
+                       i++;
+                       if (i >= argc)
+                       {
+                               fprintf(stderr, "error: expected a directory path following \"-d\"\n");
+                               usage(HELP_STATS);
+                               return -1;
+                       }
+                       store = asl_open_path(argv[i], 0);
+                       if (store == NULL)
+                       {
+                               fprintf(stderr, "error: failed to open ASL directory %s\n", argv[i]);
+                               return -1;
+                       }
+               }
+       }
+
+#if TARGET_OS_EMBEDDED
+       if (store == NULL) store = asl_open_path(ASL_IOS_STATS_DIR, 0);
+       if (store == NULL)
+       {
+               fprintf(stderr, "error: failed to open ASL directory %s\n", ASL_IOS_STATS_DIR);
+               return -1;
+       }
+#endif
+
+       asl_object_t stats_query = asl_new(ASL_TYPE_QUERY);
+       if (stats_query == NULL)
+       {
+               fprintf(stderr, "error: failed to create stats_query\n");
+               return -1;
+       }
+
+       asl_set_query(stats_query, ASL_KEY_FACILITY, "com.apple.asl.statistics", ASL_QUERY_OP_EQUAL);
+
+       asl_object_t stats = asl_search(store, stats_query);
+       asl_release(stats_query);
+       asl_release(store);
+
+       if (stats == NULL)
+       {
+               printf("no statistics records in the ASL database\n");
+               return 0;
+       }
+
+       for (interval = 1, msg = asl_next(stats); msg != NULL; interval++, msg = asl_next(stats))
+       {
+               stats_process_stat_msg(interval, msg);
+       }
+
+       asl_release(stats);
+
+       qsort(stats_sender, stats_sender_count, sizeof(sender_stat_t *), stats_n_comp);
+
+       printf("sender: message_count (%% of total) data_size\n");
+
+       for (i = 0; (i < stats_sender_count) && (i < top); i++)
+       {
+               stats_print_sender_stats(stats_sender[i], interval - 1, print_samples);
+       }
+
+       /* NB sender stats are not freed since we exit after this */
+       return 0;
+}
+
 static int
 _isanumber(char *s)
 {
@@ -739,20 +1231,24 @@ asl_string_to_char_level(const char *s)
 int
 syslog_remote_control(int argc, char *argv[])
 {
-       int pid, uid, status, mask;
+       int i, pid, uid, status, mask;
+       uint32_t bits;
 
-       if ((argc < 3) || (argc > 4))
+       if (argc < 3)
        {
-               fprintf(stderr, "usage:\n");
-               fprintf(stderr, "%s -c process [mask]\n", myname);
-               fprintf(stderr, "   get (set if mask is specified) syslog filter mask for process (pid or name)\n");
-               fprintf(stderr, "   process may be pid or process name\n");
-               fprintf(stderr, "   use \"-c 0\" to get master syslog filter mask\n");
-               fprintf(stderr, "   use \"-c 0 off\" to disable master syslog filter mask\n");
-               fprintf(stderr, "\n");
+               usage(HELP_REMOTE_CONTROL);
                return -1;
        }
 
+       for (i = 2; i < argc; i++)
+       {
+               if ((!strcmp(argv[i], "-help")) || (!strcmp(argv[i], "--help")))
+               {
+                       usage(HELP_REMOTE_CONTROL);
+                       return 0;
+               }
+       }
+
        pid = RC_MASTER;
        uid = -2;
 
@@ -760,9 +1256,8 @@ syslog_remote_control(int argc, char *argv[])
 
        if ((!strcmp(argv[2], "syslogd")) || (!strcmp(argv[2], "syslog")))
        {
-               pid = RC_SYSLOGD;
-               uid = 0;
-               status = 0;
+               fprintf(stderr, "%s: does not have a filter mask\n", argv[2]);
+               return -1;
        }
        else if (_isanumber(argv[2]) != 0)
        {
@@ -789,37 +1284,78 @@ syslog_remote_control(int argc, char *argv[])
 
        if (pid == 0) pid = RC_MASTER;
 
-       if (argc == 4)
+       if (argc == 3)
+       {
+               rcontrol_get(pid, uid);
+               return 0;
+       }
+
+       bits = EVAL_ACTIVE | EVAL_SEND_ASL | EVAL_SEND_TRACE;
+
+       for (i = 3; i < argc; i++)
        {
-               if ((pid == RC_MASTER) && (!strcasecmp(argv[3], "off"))) mask = 0;
-               else if ((pid == RC_SYSLOGD) && (!strcasecmp(argv[3], "off"))) mask = 0;
+               if ((!strcasecmp(argv[i], "off")) || (!strcmp(argv[i], "0")))
+               {
+                       bits = 0;
+               }
+               else if (!strcmp(argv[i], "-s"))
+               {
+                       if (((i + 1) < argc) && (argv[i + 1][0] != '-'))
+                       {
+                               i++;
+                               if (!strcasecmp(argv[i], "off") || !strcmp(argv[i], "0")) bits &= ~EVAL_SEND_ASL;
+                       }
+               }
+               else if (!strcmp(argv[i], "-t"))
+               {
+                       if (((i + 1) < argc) && (argv[i + 1][0] != '-'))
+                       {
+                               i++;
+                               if (!strcasecmp(argv[i], "off") || !strcmp(argv[i], "0")) bits &= ~EVAL_SEND_TRACE;
+                       }
+               }
                else
                {
-                       mask = asl_string_to_filter(argv[3]);
+                       mask = asl_string_to_filter(argv[i]);
                        if (mask < 0)
                        {
-                               printf("unknown syslog mask: %s\n", argv[3]);
+                               printf("can't understand mask: %s\n", argv[i]);
                                return -1;
                        }
+                       bits = (bits & EVAL_ACTION_MASK) | mask;
                }
-
-               rcontrol_set(pid, uid, mask);
-       }
-       else
-       {
-               rcontrol_get(pid, uid);
        }
 
+       rcontrol_set(pid, uid, bits);
        return 0;
 }
 
 int
 syslog_send(int argc, char *argv[])
 {
-       int i, start, kv, len, rfmt, rlevel, filter, status;
-       aslclient asl;
-       aslmsg m;
+       int status, i, start, kv, len, rfmt, rlevel;
+       asl_object_t asl = NULL;
+       asl_msg_t *m;
        char tmp[64], *str, *rhost;
+       struct timeval tval = {0, 0};
+       char host_name[MAXHOSTNAMELEN + 1];
+
+       for (i = 2; i < argc; i++)
+       {
+               if ((!strcmp(argv[i], "-help")) || (!strcmp(argv[i], "--help")))
+               {
+                       usage(HELP_SEND);
+                       return 0;
+               }
+       }
+
+       status = gettimeofday(&tval, NULL);
+       if (status != 0)
+       {
+               time_t tick = time(NULL);
+               tval.tv_sec = tick;
+               tval.tv_usec = 0;
+       }
 
        kv = 0;
        rhost = NULL;
@@ -850,16 +1386,42 @@ syslog_send(int argc, char *argv[])
                        }
                        start = i+1;
                }
+               else if (!strcmp(argv[i], "-x"))
+               {
+                       i++;
+                       if (i >= argc)
+                       {
+                               fprintf(stderr, "expected a path following -x\n");
+                               return -1;
+                       }
+
+                       asl = asl_open_path(argv[i], ASL_OPT_OPEN_WRITE);
+                       if (asl == NULL)
+                       {
+                               fprintf(stderr, "Could not open %s for write\n", argv[i]);
+                               return -1;
+                       }
+               }
        }
 
-       asl = asl_open(myname, "syslog", 0);
+
+       if (asl == NULL) asl = asl_open(myname, "syslog", 0);
        asl_set_filter(asl, ASL_FILTER_MASK_UPTO(ASL_LEVEL_DEBUG));
 
-       m = asl_new(ASL_TYPE_MSG);
-       asl_set(m, ASL_KEY_SENDER, myname);
+       m = asl_base_msg(NULL, rlevel, &tval, myname, "syslog", NULL);
+       if (m == NULL)
+       {
+               fprintf(stderr, "Could not create message\n");
+               return -1;
+       }
 
-       sprintf(tmp, "%d", rlevel);
-       asl_set(m, ASL_KEY_LEVEL, tmp);
+       if (gethostname(host_name, MAXHOSTNAMELEN) == 0) asl_msg_set_key_val(m, ASL_KEY_HOST, host_name);
+
+       snprintf(tmp, sizeof(tmp), "%d", getuid());
+       asl_msg_set_key_val(m, ASL_KEY_UID, tmp);
+
+       snprintf(tmp, sizeof(tmp), "%d", getgid());
+       asl_msg_set_key_val(m, ASL_KEY_GID, tmp);
 
        str = NULL;
 
@@ -875,33 +1437,21 @@ syslog_send(int argc, char *argv[])
                        strcat(str, argv[i]);
                        if ((i+1) < argc) strcat(str, " ");
                }
-               asl_set(m, ASL_KEY_MSG, str);
+               asl_msg_set_key_val(m, ASL_KEY_MSG, str);
        }
        else
        {
                for (i = start + 1; i < argc; i += 2)
                {
                        if (!strcmp(argv[i], "-k")) i++;
-                       asl_set(m, argv[i], argv[i + 1]);
+                       asl_msg_set_key_val(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);
+               asl_send(asl, (asl_object_t)m);
        }
        else if (rfmt == SEND_FORMAT_ASL)
        {
@@ -912,11 +1462,133 @@ syslog_send(int argc, char *argv[])
                rlegacy(str, rlevel, rhost);
        }
 
-       asl_free(m);
+       asl_msg_release(m);
 
        if (str != NULL) free(str);
 
-       asl_close(asl);
+       asl_release(asl);
+
+       return 0;
+}
+
+int
+syslog_config(int argc, char *argv[])
+{
+       int i;
+       uint32_t x;
+       uid_t uid;
+       asl_client_t *asl;
+       asl_msg_t *m;
+       asl_string_t *str;
+       const char *key, *val;
+
+       for (i = 2; i < argc; i++)
+       {
+               if ((!strcmp(argv[i], "-help")) || (!strcmp(argv[i], "--help")))
+               {
+                       usage(HELP_CONFIG);
+                       return 0;
+               }
+       }
+
+       if (argc == 2)
+       {
+               asl_msg_t *ctl = _asl_server_control_query();
+               if (ctl == NULL)
+               {
+                       fprintf(stderr, "can't get status information from syslogd\n");
+                       return -1;
+               }
+
+               for (x = asl_msg_fetch(ctl, 0, &key, &val, NULL); x != IndexNull; x = asl_msg_fetch(ctl, x, &key, &val, NULL))
+               {
+                       printf("%s %s\n", key, val);
+               }
+
+               asl_msg_release(ctl);
+               return 0;
+       }
+
+       uid = geteuid();
+       if (uid != 0)
+       {
+               fprintf(stderr, "syslogd parameters may only be set by the superuser\n");
+               return -1;
+       }
+
+       str = asl_string_new(0);
+       asl_string_append(str, "= ");
+
+       for (i = 2; i < argc; i++)
+       {
+               asl_string_append(str, argv[i]);
+               if ((i + 1) < argc) asl_string_append(str, " ");
+       }
+
+       asl = asl_client_open(myname, "syslog", 0);
+
+       m = asl_msg_new(ASL_TYPE_MSG);
+       asl_msg_set_key_val(m, ASL_KEY_LEVEL, ASL_STRING_NOTICE);
+       asl_msg_set_key_val(m, ASL_KEY_OPTION, ASL_OPT_CONTROL);
+       asl_msg_set_key_val(m, ASL_KEY_SENDER, myname);
+       asl_msg_set_key_val(m, ASL_KEY_MSG, asl_string_bytes(str));
+
+       asl_client_send(asl, m);
+
+       asl_string_release(str);
+       asl_msg_release(m);
+       asl_client_release(asl);
+
+       return 0;
+}
+
+int
+syslog_control(int argc, char *argv[])
+{
+       int i;
+       uid_t uid;
+       asl_client_t *asl;
+       asl_msg_t *m;
+       asl_string_t *str;
+
+       for (i = 2; i < argc; i++)
+       {
+               if ((!strcmp(argv[i], "-help")) || (!strcmp(argv[i], "--help")))
+               {
+                       usage(HELP_CONTROL);
+                       return 0;
+               }
+       }
+
+       uid = geteuid();
+       if (uid != 0)
+       {
+               fprintf(stderr, "syslog control limited to use by superuser\n");
+               return -1;
+       }
+
+       str = asl_string_new(0);
+       asl_string_append(str, "@ ");
+
+       for (i = 2; i < argc; i++)
+       {
+               asl_string_append(str, argv[i]);
+               if ((i + 1) < argc) asl_string_append(str, " ");
+       }
+
+       asl = asl_client_open(myname, "syslog", 0);
+
+       m = asl_msg_new(ASL_TYPE_MSG);
+       asl_msg_set_key_val(m, ASL_KEY_LEVEL, ASL_STRING_NOTICE);
+       asl_msg_set_key_val(m, ASL_KEY_OPTION, ASL_OPT_CONTROL);
+       asl_msg_set_key_val(m, ASL_KEY_SENDER, myname);
+       asl_msg_set_key_val(m, ASL_KEY_MSG, asl_string_bytes(str));
+
+       asl_client_send(asl, m);
+
+       asl_string_release(str);
+       asl_msg_release(m);
+       asl_client_release(asl);
 
        return 0;
 }
@@ -945,7 +1617,7 @@ static void
 printmsg(FILE *f, asl_msg_t *msg, char *fmt, int pflags)
 {
        char *str;
-       const char *mf, *tf;
+       const char *mf;
        uint32_t encode, len, status;
        uint64_t xid;
 
@@ -974,78 +1646,85 @@ printmsg(FILE *f, asl_msg_t *msg, char *fmt, int pflags)
        else if (pflags & FORMAT_LEGACY) mf = ASL_MSG_FMT_BSD;
        else if (pflags & FORMAT_XML) mf = ASL_MSG_FMT_XML;
 
-       tf = ASL_TIME_FMT_SEC;
-       if (pflags & TIME_UTC) tf = ASL_TIME_FMT_UTC;
-       if (pflags & TIME_LCL) tf = ASL_TIME_FMT_LCL;
+       len = 0;
+       str = asl_format_message((asl_msg_t *)msg, mf, tfmt, encode, &len);
+       if (str == NULL) return;
+
+       if (pflags & COMPRESS_DUPS)
+       {
+               if (last_printmsg_str != NULL) 
+               {
+                       if (!strcmp(str + STD_BSD_DATE_LEN, last_printmsg_str + STD_BSD_DATE_LEN))
+                       {
+                               last_printmsg_count++;
+                               free(str);
+                       }
+                       else
+                       {
+                               if (last_printmsg_count > 0)
+                               {
+                                       fprintf(f, "--- last message repeated %d time%s ---\n", last_printmsg_count, (last_printmsg_count == 1) ? "" : "s");
+                               }
+
+                               free(last_printmsg_str);
+                               last_printmsg_str = str;
+                               last_printmsg_count = 0;
+
+                               fprintf(f, "%s", str);
+                       }
+               }
+               else
+               {
+                       last_printmsg_str = str;
+                       last_printmsg_count = 0;
 
-       len = 0;
-       str = asl_format_message(msg, mf, tf, encode, &len);
-       if (str != NULL)
+                       fprintf(f, "%s", str);
+               }
+       }
+       else
        {
                fprintf(f, "%s", str);
                free(str);
        }
 }
 
-asl_search_result_t *
-store_query(asl_search_result_t *q, uint64_t start, int count, int dir, uint64_t *last)
+asl_msg_list_t *
+store_query(asl_msg_list_t *q, uint64_t start, int count, int dir, uint64_t *last)
 {
-       uint32_t status;
-       asl_search_result_t *res;
-
        if (store == NULL)
        {
-               status = asl_store_open_read(NULL, &store);
+               uint32_t status = asl_store_open_read(NULL, &store);
                if (status != 0) return NULL;
        }
 
-       res = NULL;
-       status = asl_store_match(store, q, &res, last, start, count, dir);
-       if (status != 0) return NULL;
-
-       return res;
+       return asl_store_match(store, q, last, start, count, 0, dir);
 }
 
-asl_search_result_t *
-file_query(asl_search_result_t *q, uint64_t start, int count, int dir, uint64_t *last)
+asl_msg_list_t *
+file_query(asl_msg_list_t *q, uint64_t start, int count, int dir, uint64_t *last)
 {
-       uint32_t status;
-       asl_search_result_t *res;
-
-       res = NULL;
-       status = asl_file_list_match(db_files, q, &res, last, start, count, dir);
-       if (status != 0) return NULL;
-
-       return res;
+       return asl_file_list_match(db_files, q, last, start, count, 0, dir);;
 }
 
-asl_search_result_t *
-legacy_query(asl_search_result_t *q, uint64_t start, int count, int dir, uint64_t *last)
+asl_msg_list_t *
+legacy_query(asl_msg_list_t *q, uint64_t start, int count, int dir, uint64_t *last)
 {
-       uint32_t status;
-       asl_search_result_t *res;
-
-       res = NULL;
-       status = asl_file_match(legacy, q, &res, last, start, count, dir);
-       if (status != 0) return NULL;
-
-       return res;
+       return asl_file_match(legacy, q, last, start, count, 0, dir);
 }
 
-asl_search_result_t *
-syslogd_query(asl_search_result_t *q, uint64_t start, int count, int dir, uint64_t *last)
+asl_msg_list_t *
+syslogd_query(asl_msg_list_t *q, uint64_t start, int count, int dir, uint64_t *last)
 {
        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;
+       asl_msg_list_t *l;
 
        if (asl_server_port == MACH_PORT_NULL)
        {
-               kstatus = bootstrap_look_up(bootstrap_port, ASL_SERVICE_NAME, &asl_server_port);
+               kstatus = bootstrap_look_up2(bootstrap_port, ASL_SERVICE_NAME, &asl_server_port, 0, BOOTSTRAP_PRIVILEGED_SERVER);
                if (kstatus != KERN_SUCCESS)
                {
                        fprintf(stderr, "query failed: can't contact syslogd\n");
@@ -1054,9 +1733,9 @@ syslogd_query(asl_search_result_t *q, uint64_t start, int count, int dir, uint64
        }
 
        len = 0;
-       str = asl_list_to_string(q, &len);
+       str = asl_msg_list_to_string(q, &len);
 
-       kstatus = vm_allocate(mach_task_self(), (vm_address_t *)&vmstr, len, TRUE);
+       kstatus = vm_allocate(mach_task_self(), (vm_address_t *)&vmstr, len, VM_FLAGS_ANYWHERE | VM_MAKE_TAG(VM_MEMORY_ASL));
        if (kstatus != KERN_SUCCESS)
        {
                free(str);
@@ -1068,24 +1747,221 @@ syslogd_query(asl_search_result_t *q, uint64_t start, int count, int dir, uint64
 
        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);
+       kstatus = _asl_server_query_2(asl_server_port, (caddr_t)vmstr, len, start, count, flags, (caddr_t *)&res, &reslen, last, (int *)&status);
 
        if (res == NULL) return NULL;
-       l = asl_list_from_string(res);
+       l = asl_msg_list_from_string(res);
        vm_deallocate(mach_task_self(), (vm_address_t)res, reslen);
        return l;
 }
 
 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)
+filter_and_print(asl_msg_t *msg, asl_msg_list_t *ql, FILE *f, char *pfmt, int pflags)
+{
+       int i, do_match, did_match;
+
+       if (msg == NULL) return;
+
+       do_match = 1;
+       if (ql == NULL) do_match = 0;
+       else if (ql->count == 0) do_match = 0;
+
+       did_match = 1;
+
+       if (do_match != 0)
+       {
+               did_match = 0;
+
+               for (i = 0; (i < ql->count) && (did_match == 0); i++)
+               {
+                       did_match = asl_msg_cmp(ql->msg[i], (asl_msg_t *)msg);
+               }
+       }
+
+       if (did_match != 0) printmsg(f, msg, pfmt, pflags);
+}
+
+#if TARGET_OS_EMBEDDED
+void
+syslogd_direct_watch(FILE *f, char *pfmt, int pflags, asl_msg_list_t *ql)
+{
+       struct sockaddr_in address;
+       int i, bytes, sock, stream, status;
+       uint32_t n, inlen;
+       uint16_t port;
+       socklen_t addresslength;
+       char *str, buf[DIRECT_BUF_SIZE];
+       asl_msg_t *msg;
+
+       if (asl_server_port == MACH_PORT_NULL)
+       {
+               status = bootstrap_look_up(bootstrap_port, ASL_SERVICE_NAME, &asl_server_port);
+               if (status != KERN_SUCCESS)
+               {
+                       fprintf(stderr, "query failed: can't contact syslogd\n");
+                       exit(1);
+               }
+       }
+
+       addresslength = sizeof(address);
+       sock = socket(AF_INET, SOCK_STREAM, 0);
+       port = (arc4random() % (IPPORT_HILASTAUTO - IPPORT_HIFIRSTAUTO)) + IPPORT_HIFIRSTAUTO;
+
+       memset(&address, 0, addresslength);
+       address.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+       address.sin_family = AF_INET;
+       address.sin_port = htons(port);
+
+       status = bind(sock, (struct sockaddr *)&address, sizeof(address));
+
+       for (i = 0; (i < MAX_RANDOM) && (status < 0); i++)
+       {
+               port = (arc4random() % (IPPORT_HILASTAUTO - IPPORT_HIFIRSTAUTO)) + IPPORT_HIFIRSTAUTO;
+               address.sin_port = htons(port);
+
+               status = bind(sock, (struct sockaddr *)&address, sizeof(address));
+       }
+
+       if (status < 0)
+       {
+               fprintf(stderr, "query failed: can't find a port to connect to syslogd\n");
+               exit(1);
+       }
+
+       bytes = 0;
+
+       if (listen(sock, 1) == -1)
+       {
+               perror("listen");
+               exit(1);
+       }
+
+       i = htons(port);
+       _asl_server_register_direct_watch(asl_server_port, i);
+
+       stream = accept(sock, (struct sockaddr*)&address, &addresslength);
+       if (stream == -1)
+       {
+               perror("accept");
+               exit(1);
+       }
+
+       forever
+       {
+               inlen = 0;
+               errno = 0;
+               bytes = recvfrom(stream, &n, sizeof(n), 0, NULL, NULL);
+               if (bytes <= 0)
+               {
+                       fprintf(stderr, "\nrecvfrom (message length) returned %d (errno %d) - exiting\n", bytes, errno);
+                       break;
+               }
+               else inlen = ntohl(n);
+
+               if (inlen == 0) continue;
+
+               str = NULL;
+               if (inlen <= DIRECT_BUF_SIZE)
+               {
+                       str = buf;
+               }
+               else
+               {
+                       str = calloc(1, inlen + 1);
+                       if (str == NULL)
+                       {
+                               fprintf(stderr, "\ncan't allocate memory - exiting\n");
+                               close(stream);
+                               close(sock);
+                               exit(1);
+                       }
+               }
+
+               n = 0;
+               while (n < inlen)
+               {
+                       errno = 0;
+                       bytes = recvfrom(stream, str + n, inlen - n, 0, NULL, NULL);
+                       if (bytes <= 0)
+                       {
+                               fprintf(stderr, "\nrecvfrom (message body) returned %d (errno %d) at length %d of %d - exiting\n", bytes, errno, n, inlen);
+                               break;
+                       }
+                       else n += bytes;
+               }
+
+               if (n < inlen)
+               {
+                       fprintf(stderr, "\ntruncated message: expected %d bytes received %d bytes\n", inlen, n);
+                       close(stream);
+                       close(sock);
+                       exit(1);
+               }
+
+               msg = asl_msg_from_string(str);
+               if (str != buf) free(str);
+               filter_and_print(msg, ql, f, pfmt, pflags);
+               asl_msg_release(msg);
+       }
+
+       close(stream);
+       close(sock);
+
+       address.sin_addr.s_addr = 0;
+}
+#endif
+
+int
+sort_compare_key(asl_msg_t *a, asl_msg_t *b, const char *key)
+{
+       const char *va, *vb;
+       uint64_t na, nb;
+
+       if (key == NULL) return 0;
+
+       va = asl_msg_get_val_for_key(a, key);
+       vb = asl_msg_get_val_for_key(b, key);
+
+       if (va == NULL) return -1;
+       if (vb == NULL) return 1;
+
+       if (sort_numeric == 1)
+       {
+               na = atoll(va);
+               nb = atoll(vb);
+               if (na < nb) return -1;
+               if (na > nb) return 1;
+               return 0;
+       }
+
+       return strcmp(va, vb);
+}
+
+int
+sort_compare(const void **ap, const void **bp)
+{
+       int cmp;
+       asl_msg_t *a, *b;
+
+       if (sort_key == NULL) return 0;
+
+       a = (asl_msg_t *)*ap;
+       b = (asl_msg_t *)*bp;
+
+       cmp = sort_compare_key(a, b, sort_key);
+       if ((cmp == 0) && (sort_key_2 != NULL)) cmp = sort_compare_key(a, b, sort_key_2);
+
+       return cmp;
+}
+
+void
+search_once(FILE *f, char *pfmt, int pflags, asl_msg_list_t *ql, uint64_t qmin, uint64_t *cmax, uint32_t count, uint32_t batch, int dir, uint32_t tail)
 {
-       asl_search_result_t *res;
+       asl_msg_list_t *res;
        int i, more, total;
 
        if (pflags & FORMAT_XML) print_xml_header(f);
@@ -1096,13 +1972,15 @@ search_once(FILE *f, char *pfmt, int pflags, asl_search_result_t *ql, uint64_t q
 
        while (more == 1)
        {
-               if (dbselect == DB_SELECT_STORE) res = store_query(ql, qmin, batch, dir, cmax);
+               if (batch == 0) more = 0;
+
+               if ((dbselect == DB_SELECT_ASL) || (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);
 
-               if ((dir >= 0) && (*cmax > qmin)) qmin = *cmax;
-               else if ((dir < 0) && (*cmax < qmin)) qmin = *cmax;
+               if ((dir >= 0) && (*cmax > qmin)) qmin = *cmax + 1;
+               else if ((dir < 0) && (*cmax < qmin)) qmin = *cmax - 1;
 
                if (res == NULL)
                {
@@ -1122,15 +2000,28 @@ search_once(FILE *f, char *pfmt, int pflags, asl_search_result_t *ql, uint64_t q
                                if (i < 0) i = 0;
                        }
 
+                       if (sort_key != NULL)
+                       {
+                               qsort(res->msg, res->count, sizeof(asl_msg_t *), sort_compare);
+                       }
+
                        if ((f != NULL) || (export != NULL))
                        {
                                for (; i < res->count; i++) printmsg(f, res->msg[i], pfmt, pflags);
                        }
 
-                       aslresponse_free((aslresponse)res);
+                       asl_msg_list_release(res);
                }
        }
 
+       if ((pflags & COMPRESS_DUPS) && (last_printmsg_count > 0))
+       {
+               fprintf(f, "--- last message repeated %d time%s ---\n", last_printmsg_count, (last_printmsg_count == 1) ? "" : "s");
+               free(last_printmsg_str);
+               last_printmsg_str = NULL;
+               last_printmsg_count = 0;
+       }
+
        if (pflags & FORMAT_XML) print_xml_trailer(f);
 }
 
@@ -1293,8 +2184,8 @@ add_op(asl_msg_t *q, char *key, char *op, char *val, uint32_t flags)
        }
 
        o |= flags;
-       if (qval != NULL) asl_set_query(q, key, qval, o);
-       else asl_set_query(q, key, val, o);
+       if (qval != NULL) asl_msg_set_key_val_op(q, key, qval, o);
+       else asl_msg_set_key_val_op(q, key, val, o);
 
        return 0;
 }
@@ -1313,7 +2204,7 @@ add_db_file(const char *name)
        }
 
        /* shouldn't happen */
-       if (name == NULL) return DB_SELECT_STORE;
+       if (name == NULL) return DB_SELECT_ASL;
 
        s = NULL;
        status = asl_file_open_read(name, &s);
@@ -1355,6 +2246,17 @@ add_db_dir(const char *name)
        asl_file_t *s;
        char *path;
 
+       /* 
+        * Try opening as a data store
+        */
+       status = asl_store_open_read(name, &store);
+       if (status == 0)
+       {
+               if (name == NULL) return DB_SELECT_ASL;
+               if (!strcmp(name, PATH_ASL_STORE)) return DB_SELECT_ASL;
+               return DB_SELECT_STORE;
+       }
+
        /*
         * Open all readable files
         */
@@ -1394,9 +2296,9 @@ int
 main(int argc, char *argv[])
 {
        FILE *outfile;
-       int i, j, n, watch, status, pflags, tflags, iamroot, user_tflag;
+       int i, j, n, watch, status, pflags, iamroot, user_tflag, export_preserve_id, saw_dash_d, since_boot;
        int notify_file, notify_token;
-       asl_search_result_t *qlist;
+       asl_msg_list_t *qlist;
        asl_msg_t *cq;
        char *pfmt;
        const char *exportname;
@@ -1410,26 +2312,56 @@ main(int argc, char *argv[])
        flags = 0;
        tail_count = 0;
        batch = FETCH_BATCH;
-       pflags = FORMAT_STD;
-       tflags = TIME_LCL;
-       encode = ASL_ENCODE_ASL;
+       pflags = FORMAT_STD | COMPRESS_DUPS;
+       encode = ASL_ENCODE_SAFE;
        cq = NULL;
        exportname = NULL;
+       export_preserve_id = 0;
+       saw_dash_d = 0;
+       since_boot = 0;
+
+       i = asl_store_location();
+       if (i == ASL_STORE_LOCATION_MEMORY) dbselect = DB_SELECT_SYSLOGD;
 
        if (getuid() == 0) iamroot = 1;
 
+       if ((argc > 1) && ((!strcmp(argv[1], "-help")) || (!strcmp(argv[1], "--help"))))
+       {
+               usage(HELP_ALL);
+               exit(0);
+       }
+
        for (i = 1; i < argc; i++)
        {
-               if ((!strcmp(argv[i], "-help")) || (!strcmp(argv[i], "--help")))
+
+               if ((!strcmp(argv[i], "-time")) || (!strcmp(argv[i], "--time")))
                {
-                       usage();
+                       qmin = time(NULL);
+                       printf("%llu\n", qmin);
                        exit(0);
                }
 
-               if (!strcmp(argv[i], "-time"))
+               if ((!strcmp(argv[i], "-stats")) || (!strcmp(argv[i], "--stats")))
                {
-                       qmin = time(NULL);
-                       printf("%llu\n", qmin);
+                       asl_stats(argc, argv);
+                       exit(0);
+               }
+
+               if ((!strcmp(argv[i], "-config")) || (!strcmp(argv[i], "--config")))
+               {
+                       syslog_config(argc, argv);
+                       exit(0);
+               }
+
+               if ((!strcmp(argv[i], "-control")) || (!strcmp(argv[i], "--control")))
+               {
+                       syslog_control(argc, argv);
+                       exit(0);
+               }
+
+               if ((!strcmp(argv[i], "-module")) || (!strcmp(argv[i], "--module")))
+               {
+                       module_control(argc, argv);
                        exit(0);
                }
 
@@ -1446,9 +2378,11 @@ main(int argc, char *argv[])
                }
        }
 
-       qlist = (asl_search_result_t *)calloc(1, sizeof(asl_search_result_t));
+       qlist = asl_msg_list_new();
        if (qlist == NULL) exit(1);
 
+       cq = NULL;
+
        for (i = 1; i < argc; i++)
        {
                if (!strcmp(argv[i], "-f"))
@@ -1460,6 +2394,7 @@ main(int argc, char *argv[])
                                        if (!strcmp(argv[j], "-"))
                                        {
                                                dbselect = DB_SELECT_SYSLOGD;
+                                               i++;
                                                break;
                                        }
                                        else if (argv[j][0] == '-')
@@ -1469,23 +2404,28 @@ main(int argc, char *argv[])
                                        else
                                        {
                                                dbselect = add_db_file(argv[j]);
+                                               i++;
                                        }
                                }
                        }
                }
-               if (!strcmp(argv[i], "-d"))
+               else if (!strcmp(argv[i], "-d"))
                {
-                       if ((i + 1) < argc)
+                       saw_dash_d = i + 1;
+
+                       if (saw_dash_d < argc)
                        {
-                               for (j = i + 1; j < argc; j++)
+                               for (j = saw_dash_d; j < argc; j++)
                                {
                                        if (!strcmp(argv[j], "store"))
                                        {
                                                dbselect = add_db_dir(PATH_ASL_STORE);
+                                               i++;
                                        }
                                        else if (!strcmp(argv[j], "archive"))
                                        {
                                                dbselect = add_db_dir(PATH_ASL_ARCHIVE);
+                                               i++;
                                        }
                                        else if (argv[j][0] == '-')
                                        {
@@ -1494,9 +2434,23 @@ main(int argc, char *argv[])
                                        else
                                        {
                                                dbselect = add_db_dir(argv[j]);
+                                               i++;
                                        }
                                }
                        }
+                       else
+                       {
+                               fprintf(stderr, "missing directory name following -d flag\n");
+                               return -1;
+                       }
+               }
+               else if (!strcmp(argv[i], "-b"))
+               {
+                       batch = atoi(argv[++i]);
+               }
+               else if (!strcmp(argv[i], "-B"))
+               {
+                       since_boot = 1;
                }
                else if (!strcmp(argv[i], "-w"))
                {
@@ -1505,31 +2459,85 @@ main(int argc, char *argv[])
                        if (((i + 1) < argc) && (argv[i + 1][0] != '-'))
                        {
                                i++;
-                               tail_count = atoi(argv[i]);
+                               if (!strcmp(argv[i], "all"))
+                               {
+                                       tail_count = (uint32_t)-1;
+                               }
+                               else if (!strcmp(argv[i], "boot"))
+                               {
+                                       since_boot = 1;
+                               }
+                               else
+                               {
+                                       tail_count = atoi(argv[i]);
+                               }
+                       }
+               }
+               else if (!strcmp(argv[i], "-sort"))
+               {
+                       if (((i + 1) < argc) && (argv[i + 1][0] != '-'))
+                       {
+                               i++;
+                               sort_key = argv[i];
+
+                               if (((i + 1) < argc) && (argv[i + 1][0] != '-'))
+                               {
+                                       i++;
+                                       sort_key_2 = argv[i];
+                               }
+                       }
+                       else
+                       {
+                               sort_key = ASL_KEY_MSG_ID;
+                       }
+
+                       batch = 0;
+               }
+               else if (!strcmp(argv[i], "-nsort"))
+               {
+                       if (((i + 1) < argc) && (argv[i + 1][0] != '-'))
+                       {
+                               i++;
+                               sort_key = argv[i];
+
+                               if (((i + 1) < argc) && (argv[i + 1][0] != '-'))
+                               {
+                                       i++;
+                                       sort_key_2 = argv[i];
+                               }
+                       }
+                       else
+                       {
+                               sort_key = ASL_KEY_MSG_ID;
                        }
+
+                       sort_numeric = 1;
+                       batch = 0;
                }
                else if (!strcmp(argv[i], "-u"))
                {
-                       tflags = TIME_UTC;
+                       tfmt = "Z";
                        user_tflag = 1;
                }
-               else if (!strcmp(argv[i], "-x"))
+               else if ((!strcmp(argv[i], "-x")) || (!strcmp(argv[i], "-X")))
                {
                        if ((i + 1) >= argc)
                        {
-                               aslresponse_free(qlist);
-                               usage();
+                               asl_msg_list_release(qlist);
+                               usage(HELP_SEARCH);
                                exit(1);
                        }
 
+                       if (!strcmp(argv[i], "-x")) export_preserve_id = 1;
+
                        exportname = argv[++i];
                }
                else if (!strcmp(argv[i], "-E"))
                {
                        if ((i + 1) >= argc)
                        {
-                               aslresponse_free(qlist);
-                               usage();
+                               asl_msg_list_release(qlist);
+                               usage(HELP_SEARCH);
                                exit(1);
                        }
 
@@ -1537,6 +2545,7 @@ main(int argc, char *argv[])
 
                        if (!strcmp(argv[i], "vis")) encode = ASL_ENCODE_ASL;
                        else if (!strcmp(argv[i], "safe")) encode = ASL_ENCODE_SAFE;
+                       else if (!strcmp(argv[i], "xml")) encode = ASL_ENCODE_XML;
                        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]);
                }
@@ -1544,8 +2553,8 @@ main(int argc, char *argv[])
                {
                        if ((i + 1) >= argc)
                        {
-                               aslresponse_free(qlist);
-                               usage();
+                               asl_msg_list_release(qlist);
+                               usage(HELP_SEARCH);
                                exit(1);
                        }
 
@@ -1554,19 +2563,20 @@ main(int argc, char *argv[])
                        if (!strcmp(argv[i], "raw"))
                        {
                                pflags = FORMAT_RAW;
-                               if (user_tflag == 0) tflags = TIME_SEC;
+                               if (user_tflag == 0) tfmt = "sec";
                        }
                        else if (!strcmp(argv[i], "std"))
                        {
-                               pflags = FORMAT_STD;
+                               pflags = FORMAT_STD | COMPRESS_DUPS;
                        }
                        else if (!strcmp(argv[i], "bsd"))
                        {
-                               pflags = FORMAT_LEGACY;
+                               pflags = FORMAT_LEGACY | COMPRESS_DUPS;
                        }
                        else if (!strcmp(argv[i], "xml"))
                        {
                                pflags = FORMAT_XML;
+                               encode = ASL_ENCODE_XML;
                        }
                        else 
                        {
@@ -1578,38 +2588,29 @@ main(int argc, char *argv[])
                {
                        if ((i + 1) >= argc)
                        {
-                               aslresponse_free(qlist);
-                               usage();
+                               asl_msg_list_release(qlist);
+                               usage(HELP_SEARCH);
                                exit(1);
                        }
 
                        i++;
+                       tfmt = argv[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], "-nodc"))
+               {
+                       pflags = pflags & ~COMPRESS_DUPS;
                }
                else if (!strcmp(argv[i], "-o"))
                {
                        flags = 0;
 
-                       if (qlist->count == 0)
-                       {
-                               qlist->msg = (asl_msg_t **)calloc(1, sizeof(asl_msg_t *));
-                       }
-                       else 
+                       if (cq != NULL)
                        {
-                               qlist->msg = (asl_msg_t **)reallocf(qlist->msg, (qlist->count + 1) * sizeof(asl_msg_t *));
+                               asl_msg_list_append(qlist, cq);
+                               asl_msg_release(cq);
+                               cq = NULL;
                        }
-
-                       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"))
                {
@@ -1617,22 +2618,23 @@ main(int argc, char *argv[])
                }
                else if (!strcmp(argv[i], "-C"))
                {
-                       if (qlist->count == 0)
+                       if (cq != NULL)
                        {
-                               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++;
+                               asl_msg_list_append(qlist, cq);
+                               asl_msg_release(cq);
+                               cq = NULL;
                        }
 
+                       flags = 0;
+                       cq = asl_msg_new(ASL_TYPE_QUERY);
                        status = add_op(cq, ASL_KEY_FACILITY, OP_EQ, FACILITY_CONSOLE, flags);
+                       asl_msg_list_append(qlist, cq);
+                       asl_msg_release(cq);
+                       cq = NULL;
 
-                       flags = 0;
                        if (status != 0)
                        {
-                               aslresponse_free(qlist);
+                               asl_msg_list_release(qlist);
                                exit(1);
                        }
                }
@@ -1649,7 +2651,7 @@ main(int argc, char *argv[])
                                        fprintf(stderr, "invalid sequence: -k");
                                        for (j = i; j <= n; j++) fprintf(stderr, " %s", argv[j]);
                                        fprintf(stderr, "\n");
-                                       usage();
+                                       usage(HELP_SEARCH);
                                        exit(1);
                                }
                        }
@@ -1661,15 +2663,7 @@ main(int argc, char *argv[])
                                continue;
                        }
 
-                       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++;
-                       }
+                       if (cq == NULL) cq = asl_msg_new(ASL_TYPE_QUERY);
 
                        status = 0;
                        if (n == 1) status = add_op(cq, argv[i], NULL, NULL, flags);
@@ -1679,17 +2673,58 @@ main(int argc, char *argv[])
                        flags = 0;
                        if (status != 0)
                        {
-                               aslresponse_free(qlist);
+                               asl_msg_list_release(qlist);
                                exit(1);
                        }
+
+                       i += (n - 1);
+               }
+               else
+               {
+                       fprintf(stderr, "syslog: unknown option \"%s\"\n", argv[i]);
+                       fprintf(stderr, "run \"syslog -help\" for usage\n");
+                       exit(1);
                }
        }
 
-       pflags |= tflags;
+       if (cq != NULL)
+       {
+               asl_msg_list_append(qlist, cq);
+               asl_msg_release(cq);
+               cq = NULL;
+       }
+
        pflags |= encode;
 
        outfile = stdout;
 
+       /*
+        * Catch and report some cases where watch (-w) doesn't work
+        */
+       if (watch == 1)
+       {
+               if (sort_key != NULL)
+               {
+                       fprintf(stderr, "Warning: -w flag has no effect with -sort flag\n");
+                       watch = 0;
+               }
+
+               if (dbselect == DB_SELECT_FILES)
+               {
+                       if (saw_dash_d == 0)
+                       {
+                               fprintf(stderr, "Warning: -w flag not supported for a set of one or more files\n");
+                       }
+                       else
+                       {
+                               fprintf(stderr, "Warning: directory \"%s\" is not an ASL data store\n", argv[saw_dash_d]);
+                               fprintf(stderr, "         -w flag not supported for a set of one or more files\n");
+                       }
+
+                       watch = 0;
+               }
+       }
+
        if (exportname != NULL)
        {
                if (watch == 1)
@@ -1701,7 +2736,7 @@ main(int argc, char *argv[])
                status = asl_file_open_write(exportname, 0644, -1, -1, &export);
                if (status != ASL_STATUS_OK) 
                {
-                       aslresponse_free(qlist);
+                       asl_msg_list_release(qlist);
                        fprintf(stderr, "export file open failed: %s\n", asl_core_error(status));
                        exit(1);
                }
@@ -1710,7 +2745,8 @@ main(int argc, char *argv[])
                 * 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;
+               export->flags = ASL_FILE_FLAG_UNLIMITED_CACHE;
+               if (export_preserve_id != 0) export->flags |= ASL_FILE_FLAG_PRESERVE_MSG_ID;
 
                outfile = NULL;
                pflags = EXPORT;
@@ -1721,31 +2757,75 @@ main(int argc, char *argv[])
        notify_file = -1;
        notify_token = -1;
 
-       if (watch == 1)
+       fprintf(stderr, "NOTE:  Most system logs have moved to a new logging system.  See log(1) for more information.\n");
+
+       /* set starting point */
+       if (since_boot == 1)
        {
-               if ((dbselect == DB_SELECT_STORE) || (dbselect == DB_SELECT_SYSLOGD))
+               /* search back for last "BOOT_TIME (ut_type == 2) record */
+               asl_msg_list_t *bt;
+               asl_msg_t *bq;
+
+               bt = asl_msg_list_new();
+               if (bt == NULL)
                {
-                       status = notify_register_file_descriptor("com.apple.system.logger.message", &notify_file, 0, &notify_token);
-                       if (status != NOTIFY_STATUS_OK) notify_token = -1;
+                       fprintf(stderr, "\ncan't allocate memory - exiting\n");
+                       exit(1);
                }
-       }
 
-       if ((qlist->count == 0) && (watch == 1))
+               bq = asl_msg_new(ASL_TYPE_QUERY);
+               if (bq == NULL)
+               {
+                       fprintf(stderr, "\ncan't allocate memory - exiting\n");
+                       exit(1);
+               }
+
+               asl_msg_list_append(bt, bq);
+               asl_msg_release(bq);
+
+               asl_msg_set_key_val_op(bq, "ut_type", "2", ASL_QUERY_OP_EQUAL);
+
+               search_once(NULL, NULL, 0, (asl_msg_list_t *)bt, -1, &qmin, 1, 1, -1, 0);
+               asl_msg_list_release(bt);
+
+               if (qmin > 0) qmin--;
+               tail_count = 0;
+       }
+       else if (watch == 1)
        {
+               /* go back tail_count records from last record */
                qmin = -1;
-               search_once(NULL, NULL, 0, NULL, qmin, &cmax, 1, 1, -1, 0);
-               qmin = (cmax + 1) - tail_count;
+               search_once(NULL, NULL, 0, qlist, qmin, &cmax, 1, 1, -1, 0);
+
+               if (cmax >= tail_count) qmin = cmax - tail_count;
+               else qmin = 0;
+
                tail_count = 0;
        }
 
+       if ((watch == 1) && (dbselect == DB_SELECT_ASL))
+       {
+               status = notify_register_file_descriptor("com.apple.system.logger.message", &notify_file, 0, &notify_token);
+               if (status != NOTIFY_STATUS_OK) notify_token = -1;
+       }
+
        /* output should be line buffered */
        if (outfile != NULL) setlinebuf(outfile);
 
-       search_once(outfile, pfmt, pflags, qlist, qmin, &cmax, 0, batch, 1, tail_count);
+       search_once(outfile, pfmt, pflags, qlist, qmin + 1, &cmax, 0, 0, 1, tail_count);
 
        if (watch == 1)
        {
-               if (notify_token == -1)
+               if (dbselect == DB_SELECT_SYSLOGD)
+               {
+#if TARGET_OS_EMBEDDED
+                       syslogd_direct_watch(outfile, pfmt, pflags, qlist);
+#else
+                       fprintf(stderr, "Warning: -w flag cannot be used when querying syslogd directly\n");
+                       exit(1);
+#endif
+               }
+               else if (notify_token == -1)
                {
                        forever
                        {
@@ -1765,10 +2845,10 @@ main(int argc, char *argv[])
        }
 
        if (db_files != NULL) asl_file_list_close(db_files);
-       if (store != NULL) asl_store_close(store);
-       if (export != NULL) asl_file_close(export);
+       if (store != NULL) asl_store_release(store);
+       if (export != NULL) asl_file_release(export);
 
-       aslresponse_free(qlist);
+       asl_msg_list_release(qlist);
 
        exit(0);
 }