]> git.saurik.com Git - apple/syslog.git/blobdiff - aslmanager.tproj/aslmanager.c
syslog-322.tar.gz
[apple/syslog.git] / aslmanager.tproj / aslmanager.c
index 02eb7e0d439edea0f1b7cf627f417ffb6870c089..ace82324328c03e027e6fee76c743d1e1886b44f 100644 (file)
 /*
- * Copyright (c) 2007-2009 Apple Inc. All rights reserved.
+ * Copyright (c) 2007-2015 Apple Inc. All rights reserved.
  *
  * @APPLE_LICENSE_HEADER_START@
- * 
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- * 
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- * 
- * @APPLE_LICENSE_HEADER_END@
- */
-
-#include <stdio.h>
-#include <dirent.h>
-#include <string.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <stdint.h>
-#include <errno.h>
-#include <time.h>
-#include <sys/time.h>
-#include <sys/stat.h>
-#include <sys/param.h>
-#include <servers/bootstrap.h>
-#include <bootstrap_priv.h>
-#include <mach/mach.h>
-#include <copyfile.h>
-#include <fcntl.h>
-#include <zlib.h>
-#include <xpc/xpc.h>
-#include <xpc/private.h>
-#include <os/assumes.h>
-#include <vproc_priv.h>
-#include <asl.h>
-#include <asl_private.h>
-#include <asl_core.h>
-#include <asl_file.h>
-#include <asl_store.h>
-#include "asl_common.h"
-
-#define DEFAULT_MAX_SIZE 150000000
-#define IOBUFSIZE 4096
-
-#define DO_ASLDB       0x00000001
-#define DO_MODULE      0x00000002
-#define DO_CHECKPT     0x00000004
-
-#define DEBUG_FLAG_MASK  0xfffffff0
-#define DEBUG_LEVEL_MASK 0x0000000f
-#define DEBUG_STDERR     0x00000010
-#define DEBUG_ASL        0x00000020
-
-#define AUX_URL_MINE "file:///var/log/asl/"
-#define AUX_URL_MINE_LEN 20
-
-/* length of "file://" */
-#define AUX_URL_PATH_OFFSET 7
-
-extern kern_return_t _asl_server_query
-(
- mach_port_t server,
- caddr_t request,
- mach_msg_type_number_t requestCnt,
- uint64_t startid,
- int count,
- int flags,
- caddr_t *reply,
- mach_msg_type_number_t *replyCnt,
- uint64_t *lastid,
- int *status,
- security_token_t *token
-);
-
-/* global */
-static time_t module_ttl;
-static uint32_t debug;
-static int dryrun;
-static int asl_aux_fd = -1;
-static aslclient aslc;
-static mach_port_t asl_server_port;
-static xpc_connection_t listener;
-static dispatch_queue_t serverq;
-
-typedef struct name_list_s
-{
-       char *name;
-       size_t size;
-       struct name_list_s *next;
-} name_list_t;
-
-static const char *
-keep_str(uint8_t mask)
-{
-       static char str[9];
-       uint32_t x = 0;
-
-       memset(str, 0, sizeof(str));
-       if (mask & 0x01) str[x++] = '0';
-       if (mask & 0x02) str[x++] = '1';
-       if (mask & 0x04) str[x++] = '2';
-       if (mask & 0x08) str[x++] = '3';
-       if (mask & 0x10) str[x++] = '4';
-       if (mask & 0x20) str[x++] = '5';
-       if (mask & 0x40) str[x++] = '6';
-       if (mask & 0x80) str[x++] = '7';
-       if (x == 0) str[x++] = '-';
-       return str;
-}
-
-void
-set_debug(int flag, const char *str)
-{
-       int level, x;
-
-       if (str == NULL) x = ASL_LEVEL_ERR;
-       else if (((str[0] == 'L') || (str[0] == 'l')) && ((str[1] >= '0') && (str[1] <= '7')) && (str[2] == '\0')) x = atoi(str+1);
-       else if ((str[0] >= '0') && (str[0] <= '7') && (str[1] == '\0')) x = ASL_LEVEL_CRIT + atoi(str);
-       else x = ASL_LEVEL_ERR;
-
-       if (x <= 0) x = 0;
-       else if (x > 7) x = 7;
-
-       level = debug & DEBUG_LEVEL_MASK;
-       if (x > level) level = x;
-
-       debug = debug & DEBUG_FLAG_MASK;
-       debug |= flag;
-       debug |= level;
-}
-
-void
-debug_log(int level, const char *str, ...)
-{
-       va_list v;
-
-       if ((debug & DEBUG_STDERR) && (level <= (debug & DEBUG_LEVEL_MASK)))
-       {
-               va_start(v, str);
-               vfprintf(stderr, str, v);
-               va_end(v);
-       }
-
-       if (debug & DEBUG_ASL)
-       {
-               char *line = NULL;
-
-               if (aslc == NULL)
-               {
-                       aslc = asl_open("aslmanager", "syslog", 0);
-                       asl_msg_t *msg = asl_msg_new(ASL_TYPE_MSG);
-
-                       asl_msg_set_key_val(msg, ASL_KEY_MSG, "Status Report");
-                       asl_msg_set_key_val(msg, ASL_KEY_LEVEL, ASL_STRING_NOTICE);
-                       asl_create_auxiliary_file((asl_object_t)msg, "Status Report", "public.text", &asl_aux_fd);
-                       asl_msg_release(msg);
-               }
-
-               va_start(v, str);
-               vasprintf(&line, str, v);
-               va_end(v);
-
-               if (line != NULL) write(asl_aux_fd, line, strlen(line));
-               free(line);
-       }
-}
-
-__attribute__((noreturn)) static void
-xpc_server_exit(int status)
-{
-       xpc_connection_cancel(listener);
-       xpc_release(listener);
-       dispatch_release(serverq);
-       exit(status);
-}
-
-name_list_t *
-add_to_name_list(name_list_t *l, const char *name, size_t size)
-{
-       name_list_t *e, *x;
-
-       if (name == NULL) return l;
-
-       e = (name_list_t *)calloc(1, sizeof(name_list_t));
-       if (e == NULL) return NULL;
-
-       e->name = strdup(name);
-       if (e->name == NULL)
-       {
-               free(e);
-               return NULL;
-       }
-
-       e->size = size;
-
-       /* list is sorted by name (i.e. primarily by timestamp) */
-       if (l == NULL) return e;
-
-       if (strcmp(e->name, l->name) <= 0)
-       {
-               e->next = l;
-               return e;
-       }
-
-       for (x = l; (x->next != NULL) && (strcmp(e->name, x->next->name) > 0) ; x = x->next);
-
-       e->next = x->next;
-       x->next = e;
-       return l;
-}
-
-void
-free_name_list(name_list_t *l)
-{
-       name_list_t *e;
-
-       while (l != NULL)
-       {
-               e = l;
-               l = l->next;
-               free(e->name);
-               free(e);
-       }
-
-       free(l);
-}
-/*
- * Copy ASL files by reading and writing each record.
- */
-uint32_t
-copy_asl_file(const char *src, const char *dst, mode_t mode)
-{
-       asl_msg_list_t *res;
-       asl_file_t *f;
-       uint32_t status, i;
-       uint64_t mid;
-       size_t rcount;
-
-       if (src == NULL) return ASL_STATUS_INVALID_ARG;
-       if (dst == NULL) return ASL_STATUS_INVALID_ARG;
-
-       f = NULL;
-       status = asl_file_open_read(src, &f);
-       if (status != ASL_STATUS_OK) return status;
-
-       res = NULL;
-       mid = 0;
-
-       res = asl_file_match(f, NULL, &mid, 0, 0, 0, 1);
-       asl_file_close(f);
-
-       if (res == NULL) return ASL_STATUS_OK;
-       rcount = asl_msg_list_count(res);
-       if (rcount == 0)
-       {
-               asl_msg_list_release(res);
-               return ASL_STATUS_OK;
-       }
-
-       f = NULL;
-       status = asl_file_open_write(dst, mode, -1, -1, &f);
-       if (status != ASL_STATUS_OK) return status;
-       if (f == ASL_STATUS_OK) return ASL_STATUS_FAILED;
-
-       f->flags = ASL_FILE_FLAG_PRESERVE_MSG_ID;
-
-       for (i = 0; i < rcount; i++)
-       {
-               mid = 0;
-               status = asl_file_save(f, asl_msg_list_get_index(res, i), &mid);
-               if (status != ASL_STATUS_OK) break;
-       }
-
-       asl_file_close(f);
-       return status;
-}
-
-int
-copy_compress_file(asl_out_dst_data_t *asldst, const char *src, const char *dst)
-{
-       int in, out;
-       size_t n;
-       gzFile gz;
-       char buf[IOBUFSIZE];
-
-       in = open(src, O_RDONLY, 0);
-       if (in < 0) return -1;
-
-       out = open(dst, O_WRONLY | O_CREAT, asldst->mode);
-       if (out >= 0) out = asl_out_dst_set_access(out, asldst);
-       if (out < 0)
-       {
-               close(in);
-               return -1;
-       }
-
-       gz = gzdopen(out, "w");
-       if (gz == NULL)
-       {
-               close(in);
-               close(out);
-               return -1;
-       }
-
-       do {
-               n = read(in, buf, sizeof(buf));
-               if (n > 0) gzwrite(gz, buf, n);
-       } while (n == IOBUFSIZE);
-
-       gzclose(gz);
-       close(in);
-       close(out);
-
-       return 0;
-}
-
-void
-filesystem_rename(const char *src, const char *dst)
-{
-       int status = 0;
-
-       debug_log(ASL_LEVEL_NOTICE, "  rename %s ---> %s\n", src, dst);
-       if (dryrun == 1) return;
-
-       status = rename(src, dst);
-       if (status != 0) debug_log(ASL_LEVEL_ERR, "  FAILED status %d errno %d [%s] rename %s ---> %s\n", status, errno, strerror(errno), src, dst);
-}
-
-void
-filesystem_unlink(const char *path)
-{
-       int status = 0;
-       
-       debug_log(ASL_LEVEL_NOTICE, "  remove %s\n", path);
-       if (dryrun == 1) return;
-       
-       status = unlink(path);
-       if (status != 0) debug_log(ASL_LEVEL_ERR, "  FAILED status %d errno %d [%s] unlink %s\n", status, errno, strerror(errno), path);
-}
-
-void
-filesystem_truncate(const char *path)
-{
-       int status = 0;
-       
-       debug_log(ASL_LEVEL_NOTICE, "  truncate %s\n", path);
-       if (dryrun == 1) return;
-       
-       status = truncate(path, 0);
-       if (status != 0) debug_log(ASL_LEVEL_ERR, "  FAILED status %d errno %d [%s] unlink %s\n", status, errno, strerror(errno), path);
-}
-
-void
-filesystem_rmdir(const char *path)
-{
-       int status = 0;
-
-       debug_log(ASL_LEVEL_NOTICE, "  remove directory %s\n", path);
-       if (dryrun == 1) return;
-
-       status = rmdir(path);
-       if (status != 0) debug_log(ASL_LEVEL_ERR, "  FAILED status %d errno %d [%s] rmdir %s\n", status, errno, strerror(errno), path);
-}
-
-int32_t
-filesystem_copy(asl_out_dst_data_t *asldst, const char *src, const char *dst, uint32_t flags)
-{
-       char *dot;
-
-       if ((src == NULL) || (dst == NULL)) return 0;
-
-       dot = strrchr(src, '.');
-       if ((dot != NULL) && (!strcmp(dot, ".gz"))) flags &= ~MODULE_FLAG_COMPRESS;
-
-       if (((flags & MODULE_FLAG_COMPRESS) == 0) && (!strcmp(src, dst))) return 0;
-
-       if (flags & MODULE_FLAG_TYPE_ASL) debug_log(ASL_LEVEL_NOTICE, "  copy asl %s ---> %s\n", src, dst);
-       else if (flags & MODULE_FLAG_COMPRESS) debug_log(ASL_LEVEL_NOTICE, "  copy compress %s ---> %s.gz\n", src, dst);
-       else debug_log(ASL_LEVEL_NOTICE, "  copy %s ---> %s\n", src, dst);
-
-       if (dryrun == 1) return 0;
-
-       if (flags & MODULE_FLAG_TYPE_ASL)
-       {
-               uint32_t status = copy_asl_file(src, dst, asldst->mode);
-               if (status != 0)
-               {
-                       debug_log(ASL_LEVEL_ERR, "  FAILED status %u [%s] asl copy %s ---> %s\n", status, asl_core_error(status), src, dst);
-                       return 0;
-               }
-       }
-       else if (flags & MODULE_FLAG_COMPRESS)
-       {
-               char gzdst[MAXPATHLEN];
-
-               snprintf(gzdst, sizeof(gzdst), "%s.gz", dst);
-
-               int status = copy_compress_file(asldst, src, gzdst);
-               if (status != 0)
-               {
-                       debug_log(ASL_LEVEL_ERR, "  FAILED status %d errno %d [%s] copy & compress %s ---> %s\n", status, errno, strerror(errno), src, dst);
-                       return 0;
-               }
-       }
-       else
-       {
-               int status = copyfile(src, dst, NULL, COPYFILE_ALL | COPYFILE_RECURSIVE);
-               if (status != 0)
-               {
-                       debug_log(ASL_LEVEL_ERR, "  FAILED status %d errno %d [%s] copy %s ---> %s\n", status, errno, strerror(errno), src, dst);
-                       return 0;
-               }
-       }
-
-       return 1;
-}
-
-int
-remove_directory(const char *path)
-{
-       DIR *dp;
-       struct dirent *dent;
-       char *str;
-
-       dp = opendir(path);
-       if (dp == NULL) return 0;
-
-       while ((dent = readdir(dp)) != NULL)
-       {
-               if ((!strcmp(dent->d_name, ".")) || (!strcmp(dent->d_name, ".."))) continue;
-               asprintf(&str, "%s/%s", path, dent->d_name);
-               if (str != NULL)
-               {
-                       filesystem_unlink(str);
-                       free(str);
-                       str = NULL;
-               }
-       }
-
-       closedir(dp);
-       filesystem_rmdir(path);
-
-       return 0;
-}
-
-/*
- * Determine the age (in whole days) of a YMD file from its name.
- * Also determines UID and GID from ".Unnn.Gnnn" part of file name.
- */
-uint32_t
-ymd_file_age(const char *name, time_t now, uid_t *u, gid_t *g)
-{
-       struct tm ftime;
-       time_t created;
-       uint32_t days;
-       const char *p;
-
-       if (name == NULL) return 0;
-
-       if (now == 0) now = time(NULL);
-
-       memset(&ftime, 0, sizeof(struct tm));
-       ftime.tm_hour = 24;
-
-       /* name is YYYY.MM.DD.<...> */
-
-       if ((name[0] < '0') || (name[0] > '9')) return 0;
-       ftime.tm_year = 1000 * (name[0] - '0');
-
-       if ((name[1] < '0') || (name[1] > '9')) return 0;
-       ftime.tm_year += 100 * (name[1] - '0');
-
-       if ((name[2] < '0') || (name[2] > '9')) return 0;
-       ftime.tm_year += 10 * (name[2] - '0');
-
-       if ((name[3] < '0') || (name[3] > '9')) return 0;
-       ftime.tm_year += name[3] - '0';
-       ftime.tm_year -= 1900;
-
-       if (name[4] != '.') return 0;
-
-       if ((name[5] < '0') || (name[5] > '9')) return 0;
-       ftime.tm_mon = 10 * (name[5] - '0');
-
-       if ((name[6] < '0') || (name[6] > '9')) return 0;
-       ftime.tm_mon += name[6] - '0';
-       ftime.tm_mon -= 1;
-
-       if (name[7] != '.') return 0;
-
-       if ((name[8] < '0') || (name[8] > '9')) return 0;
-       ftime.tm_mday = 10 * (name[8] - '0');
-
-       if ((name[9] < '0') || (name[9] > '9')) return 0;
-       ftime.tm_mday += name[9] - '0';
-
-       if (name[10] != '.') return 0;
-
-       created = mktime(&ftime);
-       if (created > now) return 0;
-
-       days = (now - created) / 86400;
-
-       if (u != NULL)
-       {
-               *u = -1;
-               p = strchr(name+10, 'U');
-               if (p != NULL) *u = atoi(p+1);
-       }
-
-       if (g != NULL)
-       {
-               *g = -1;
-               p = strchr(name+10, 'G');
-               if (p != NULL) *g = atoi(p+1);
-       }
-
-       return days;
-}
-
-void
-aux_url_callback(const char *url)
-{
-       if (url == NULL) return;
-       if (!strncmp(url, AUX_URL_MINE, AUX_URL_MINE_LEN)) filesystem_unlink(url + AUX_URL_PATH_OFFSET);
-}
-
-uint32_t
-ymd_file_filter(const char *name, const char *path, uint32_t keep_mask, mode_t ymd_mode, uid_t ymd_uid, gid_t ymd_gid)
-{
-       asl_file_t *f = NULL;
-       uint8_t km = keep_mask;
-       uint32_t status, len, dstcount = 0;
-       char src[MAXPATHLEN];
-       char dst[MAXPATHLEN];
-
-       if (snprintf(src, MAXPATHLEN, "%s/%s", path, name) >= MAXPATHLEN) return ASL_STATUS_FAILED;
-       if (snprintf(dst, MAXPATHLEN, "%s/%s", path, name) >= MAXPATHLEN) return ASL_STATUS_FAILED;
-       len = strlen(src) - 3;
-       snprintf(dst + len, 4, "tmp");
-
-       //TODO: check if src file is already filtered
-       debug_log(ASL_LEVEL_NOTICE, "  filter %s %s ---> %s\n", src, keep_str(km), dst);
-
-       status = ASL_STATUS_OK;
-
-       if (dryrun == 0)
-       {
-               status = asl_file_open_read(name, &f);
-               if (status != ASL_STATUS_OK) return status;
-
-               status = asl_file_filter_level(f, dst, keep_mask, ymd_mode, ymd_uid, ymd_gid, &dstcount, aux_url_callback);
-               asl_file_close(f);
-       }
-
-       filesystem_unlink(src);
-       if ((status != ASL_STATUS_OK) || (dstcount == 0)) filesystem_unlink(dst);
-       else filesystem_rename(dst, src);
-
-       return status;
-}
-
-/*
- * Used to set config parameters.
- * Line format "= name value"
- */
-static void
-_aslmanager_set_param(asl_out_dst_data_t *dst, char *s)
-{
-       char **l;
-       uint32_t count;
-
-       if (s == NULL) return;
-       if (s[0] == '\0') return;
-
-       /* skip '=' and whitespace */
-       if (*s == '=') s++;
-       while ((*s == ' ') || (*s == '\t')) s++;
-
-       l = explode(s, " \t");
-       if (l == NULL) return;
-
-       for (count = 0; l[count] != NULL; count++);
-
-       /* name is required */
-       if (count == 0)
-       {
-               free_string_list(l);
-               return;
-       }
-
-       /* value is required */
-       if (count == 1)
-       {
-               free_string_list(l);
-               return;
-       }
-
-       if (!strcasecmp(l[0], "aslmanager_debug"))
-       {
-               /* = debug level */
-               set_debug(DEBUG_ASL, l[1]);
-       }
-       else if (!strcasecmp(l[0], "store_ttl"))
-       {
-               /* = store_ttl days */
-               dst->ttl[LEVEL_ALL] = (time_t)atoll(l[1]);
-       }
-       else if (!strcasecmp(l[0], "module_ttl"))
-       {
-               /* = module_ttl days */
-               module_ttl = (time_t)atoll(l[1]);
-       }
-       else if (!strcasecmp(l[0], "max_store_size"))
-       {
-               /* = max_file_size bytes */
-               dst->all_max = atoi(l[1]);
-       }
-       else if (!strcasecmp(l[0], "archive"))
-       {
-               free(dst->rotate_dir);
-               dst->rotate_dir = NULL;
-
-               /* = archive {0|1} path */
-               if (!strcmp(l[1], "1"))
-               {
-                       if (l[2] == NULL) dst->rotate_dir = strdup(PATH_ASL_ARCHIVE);
-                       else dst->rotate_dir = strdup(l[2]);
-               }
-       }
-       else if (!strcasecmp(l[0], "store_path"))
-       {
-               /* = archive path */
-               free(dst->path);
-               dst->path = strdup(l[1]);
-       }
-       else if (!strcasecmp(l[0], "archive_mode"))
-       {
-               dst->mode = strtol(l[1], NULL, 0);
-               if ((dst->mode == 0) && (errno == EINVAL)) dst->mode = 0400;
-       }
-
-       free_string_list(l);
-}
-
-size_t
-directory_size(const char *path)
-{
-       DIR *dp;
-       struct dirent *dent;
-       struct stat sb;
-       size_t size;
-       char *str;
-
-       dp = opendir(path);
-       if (dp == NULL) return 0;
-
-       size = 0;
-       while ((dent = readdir(dp)) != NULL)
-       {
-               if ((!strcmp(dent->d_name, ".")) || (!strcmp(dent->d_name, ".."))) continue;
-
-               memset(&sb, 0, sizeof(struct stat));
-               str = NULL;
-               asprintf(&str, "%s/%s", path, dent->d_name);
-
-               if ((str != NULL) && (stat(str, &sb) == 0) && S_ISREG(sb.st_mode))
-               {
-                       size += sb.st_size;
-                       free(str);
-               }
-       }
-
-       closedir(dp);
-       return size;
-}
-
-static int
-process_asl_data_store(asl_out_dst_data_t *dst)
-{
-       int32_t today_ymd_stringlen, expire_ymd_stringlen;
-       time_t now, ttl, ymd_expire;
-       struct tm ctm;
-       char today_ymd_string[32], expire_ymd_string[32], *str;
-       DIR *dp;
-       struct dirent *dent;
-       name_list_t *ymd_list, *bb_list, *aux_list, *bb_aux_list, *e;
-       size_t file_size, store_size;
-       struct stat sb;
-
-       ymd_list = NULL;
-       bb_list = NULL;
-       aux_list = NULL;
-       bb_aux_list = NULL;
-       store_size = 0;
-
-       if (dst == NULL) return 0;
-       if (dst->path == NULL) return 0;
-
-       debug_log(ASL_LEVEL_NOTICE, "----------------------------------------\n");
-       debug_log(ASL_LEVEL_NOTICE, "Processing data store %s\n", dst->path);
-
-       if (dst->rotate_dir != NULL)
-       {
-               /* check archive */
-               memset(&sb, 0, sizeof(struct stat));
-               if (stat(dst->rotate_dir, &sb) == 0)
-               {
-                       /* must be a directory */
-                       if (!S_ISDIR(sb.st_mode))
-                       {
-                               debug_log(ASL_LEVEL_ERR, "aslmanager error: archive %s is not a directory", dst->rotate_dir);
-                               return -1;
-                       }
-               }
-               else
-               {
-                       if (errno == ENOENT)
-                       {
-                               /* archive doesn't exist - create it */
-                               if (mkdir(dst->rotate_dir, 0755) != 0)
-                               {
-                                       debug_log(ASL_LEVEL_ERR, "aslmanager error: can't create archive %s: %s\n", dst->rotate_dir, strerror(errno));
-                                       return -1;
-                               }
-                       }
-                       else
-                       {
-                               /* stat failed for some other reason */
-                               debug_log(ASL_LEVEL_ERR, "aslmanager error: can't stat archive %s: %s\n", dst->rotate_dir, strerror(errno));
-                               return -1;
-                       }
-               }
-       }
-
-       chdir(dst->path);
-
-       /* determine current time */
-       now = time(NULL);
-
-       /* ttl 0 means files never expire */
-       ymd_expire = 0;
-       ttl = dst->ttl[LEVEL_ALL] * SECONDS_PER_DAY;
-
-       if ((ttl > 0) && (ttl <= now)) ymd_expire = now - ttl;
-
-       /* construct today's date as YYYY.MM.DD */
-       memset(&ctm, 0, sizeof(struct tm));
-       if (localtime_r((const time_t *)&now, &ctm) == NULL) return -1;
-
-       snprintf(today_ymd_string, sizeof(today_ymd_string), "%d.%02d.%02d.", ctm.tm_year + 1900, ctm.tm_mon + 1, ctm.tm_mday);
-       today_ymd_stringlen = strlen(today_ymd_string);
-
-       /* construct regular file expiry date as YYYY.MM.DD */
-       memset(&ctm, 0, sizeof(struct tm));
-       if (localtime_r((const time_t *)&ymd_expire, &ctm) == NULL) return -1;
-
-       snprintf(expire_ymd_string, sizeof(expire_ymd_string), "%d.%02d.%02d.", ctm.tm_year + 1900, ctm.tm_mon + 1, ctm.tm_mday);
-       expire_ymd_stringlen = strlen(expire_ymd_string);
-
-       debug_log(ASL_LEVEL_NOTICE, "Expiry Date %s\n", expire_ymd_string);
-
-       dp = opendir(dst->path);
-       if (dp == NULL) return -1;
-
-       /* gather a list of YMD files, AUX dirs, BB.AUX dirs, and BB files */
-       while ((dent = readdir(dp)) != NULL)
-       {
-               memset(&sb, 0, sizeof(struct stat));
-               file_size = 0;
-               if (stat(dent->d_name, &sb) == 0) file_size = sb.st_size;
-
-               if ((dent->d_name[0] >= '0') && (dent->d_name[0] <= '9'))
-               {
-                       ymd_list = add_to_name_list(ymd_list, dent->d_name, file_size);
-                       store_size += file_size;
-               }
-               else if (!strncmp(dent->d_name, "AUX.", 4) && (dent->d_name[4] >= '0') && (dent->d_name[4] <= '9') && S_ISDIR(sb.st_mode))
-               {
-                       file_size = directory_size(dent->d_name);
-                       aux_list = add_to_name_list(aux_list, dent->d_name, file_size);
-                       store_size += file_size;
-               }
-               else if (!strncmp(dent->d_name, "BB.AUX.", 7) && (dent->d_name[7] >= '0') && (dent->d_name[7] <= '9') && S_ISDIR(sb.st_mode))
-               {
-                       file_size = directory_size(dent->d_name);
-                       bb_aux_list = add_to_name_list(bb_aux_list, dent->d_name, file_size);
-                       store_size += file_size;
-               }
-               else if (!strncmp(dent->d_name, "BB.", 3) && (dent->d_name[3] >= '0') && (dent->d_name[3] <= '9'))
-               {
-                       bb_list = add_to_name_list(bb_list, dent->d_name, file_size);
-                       store_size += file_size;
-               }
-               else if ((!strcmp(dent->d_name, ".")) || (!strcmp(dent->d_name, "..")))
-               {}
-               else if ((!strcmp(dent->d_name, "StoreData")) || (!strcmp(dent->d_name, "SweepStore")))
-               {}
-               else
-               {
-                       debug_log(ASL_LEVEL_ERR, "aslmanager: unexpected file %s in ASL data store\n", dent->d_name);
-               }
-       }
-
-       closedir(dp);
-
-       debug_log(ASL_LEVEL_NOTICE, "Data Store Size = %lu\n", store_size);
-       debug_log(ASL_LEVEL_NOTICE, "Data Store YMD Files\n");
-       for (e = ymd_list; e != NULL; e = e->next) debug_log(ASL_LEVEL_NOTICE, "        %s   %lu\n", e->name, e->size);
-       debug_log(ASL_LEVEL_NOTICE, "Data Store AUX Directories\n");
-       for (e = aux_list; e != NULL; e = e->next) debug_log(ASL_LEVEL_NOTICE, "        %s   %lu\n", e->name, e->size);
-       debug_log(ASL_LEVEL_NOTICE, "Data Store BB.AUX Directories\n");
-       for (e = bb_aux_list; e != NULL; e = e->next) debug_log(ASL_LEVEL_NOTICE, "     %s   %lu\n", e->name, e->size);
-       debug_log(ASL_LEVEL_NOTICE, "Data Store BB Files\n");
-       for (e = bb_list; e != NULL; e = e->next) debug_log(ASL_LEVEL_NOTICE, " %s   %lu\n", e->name, e->size);
-
-       /* Delete/achive expired YMD files */
-       debug_log(ASL_LEVEL_NOTICE, "Start YMD File Scan\n");
-
-       e = ymd_list;
-       while (e != NULL)
-       {
-               if (strncmp(e->name, expire_ymd_string, expire_ymd_stringlen) <= 0)
-               {
-                       /* file has expired, archive it if required, then unlink it */
-                       if (dst->rotate_dir != NULL)
-                       {
-                               str = NULL;
-                               asprintf(&str, "%s/%s", dst->rotate_dir, e->name);
-                               if (str == NULL) return -1;
-
-                               filesystem_copy(dst, e->name, str, 0);
-                               free(str);
-                       }
-
-                       filesystem_unlink(e->name);
-                       store_size -= e->size;
-                       e->size = 0;
-               }
-               else
-               {
-                       /* check if there are any per-level TTLs and filter the file if required */
-                       uint32_t i, bit, keep_mask;
-                       uid_t ymd_uid = -1;
-                       gid_t ymd_gid = -1;
-                       mode_t ymd_mode = 0600;
-                       uint32_t age = ymd_file_age(e->name, now, &ymd_uid, &ymd_gid);
-
-                       if (age > 0)
-                       {
-                               keep_mask = 0x000000ff;
-                               bit = 1;
-                               for (i = 0; i <= 7; i++)
-                               {
-                                       if ((dst->ttl[i] > 0) && (age >= dst->ttl[i])) keep_mask &= ~bit;
-                                       bit *= 2;
-                               }
-
-                               memset(&sb, 0, sizeof(struct stat));
-                               if (stat(e->name, &sb) == 0) ymd_mode = sb.st_mode & 0777;
-
-                               if (keep_mask != 0x000000ff) ymd_file_filter(e->name, dst->path, keep_mask, ymd_mode, ymd_uid, ymd_gid);
-                       }
-               }
-
-               e = e->next;
-       }
-
-       debug_log(ASL_LEVEL_NOTICE, "Finished YMD File Scan\n");
-
-       /* Delete/achive expired YMD AUX directories */
-       debug_log(ASL_LEVEL_NOTICE, "Start AUX Directory Scan\n");
-
-       e = aux_list;
-       while (e != NULL)
-       {
-               /* stop when a file name/date is after the expire date */
-               if (strncmp(e->name + 4, expire_ymd_string, expire_ymd_stringlen) > 0) break;
-
-               if (dst->rotate_dir != NULL)
-               {
-                       str = NULL;
-                       asprintf(&str, "%s/%s", dst->rotate_dir, e->name);
-                       if (str == NULL) return -1;
-
-                       filesystem_copy(dst, e->name, str, 0);
-                       free(str);
-               }
-
-               remove_directory(e->name);
-               store_size -= e->size;
-               e->size = 0;
-
-               e = e->next;
-       }
-
-       debug_log(ASL_LEVEL_NOTICE, "Finished AUX Directory Scan\n");
-
-       /* Delete/achive expired BB.AUX directories */
-       debug_log(ASL_LEVEL_NOTICE, "Start BB.AUX Directory Scan\n");
-
-       e = bb_aux_list;
-       while (e != NULL)
-       {
-               /* stop when a file name/date is after the expire date */
-               if (strncmp(e->name + 7, today_ymd_string, today_ymd_stringlen) > 0) break;
-
-               if (dst->rotate_dir != NULL)
-               {
-                       str = NULL;
-                       asprintf(&str, "%s/%s", dst->rotate_dir, e->name);
-                       if (str == NULL) return -1;
-
-                       filesystem_copy(dst, e->name, str, 0);
-                       free(str);
-               }
-
-               remove_directory(e->name);
-               store_size -= e->size;
-               e->size = 0;
-
-               e = e->next;
-       }
-
-       debug_log(ASL_LEVEL_NOTICE, "Finished BB.AUX Directory Scan\n");
-
-       /* Delete/achive expired BB files */
-       debug_log(ASL_LEVEL_NOTICE, "Start BB Scan\n");
-
-       e = bb_list;
-       while (e != NULL)
-       {
-               /* stop when a file name/date is after the expire date */
-               if (strncmp(e->name + 3, today_ymd_string, today_ymd_stringlen) > 0) break;
-
-               if (dst->rotate_dir != NULL)
-               {
-                       str = NULL;
-                       asprintf(&str, "%s/%s", dst->rotate_dir, e->name);
-                       if (str == NULL) return -1;
-
-                       /* syslog -x [str] -f [e->name] */
-                       filesystem_copy(dst, e->name, str, 0);
-                       free(str);
-               }
-
-               filesystem_unlink(e->name);
-               store_size -= e->size;
-               e->size = 0;
-
-               e = e->next;
-       }
-
-       debug_log(ASL_LEVEL_NOTICE, "Finished BB Scan\n");
-
-       if (dst->all_max > 0)
-       {
-               /* if data store is over max_size, delete/archive more YMD files */
-               if (store_size > dst->all_max) debug_log(ASL_LEVEL_NOTICE, "Additional YMD Scan\n");
-
-               e = ymd_list;
-               while ((e != NULL) && (store_size > dst->all_max))
-               {
-                       if (e->size != 0)
-                       {
-                               if (strncmp(e->name, today_ymd_string, today_ymd_stringlen) == 0)
-                               {
-                                       /* do not touch active file YYYY.MM.DD.asl */
-                                       if (strcmp(e->name + today_ymd_stringlen, "asl") == 0)
-                                       {
-                                               e = e->next;
-                                               continue;
-                                       }
-                               }
-
-                               if (dst->rotate_dir != NULL)
-                               {
-                                       str = NULL;
-                                       asprintf(&str, "%s/%s", dst->rotate_dir, e->name);
-                                       if (str == NULL) return -1;
-
-                                       /* syslog -x [str] -f [e->name] */
-                                       filesystem_copy(dst, e->name, str, 0);
-                                       free(str);
-                               }
-
-                               filesystem_unlink(e->name);
-                               store_size -= e->size;
-                               e->size = 0;
-                       }
-
-                       e = e->next;
-               }
-
-               /* if data store is over dst->all_max, delete/archive more BB files */
-               if (store_size > dst->all_max) debug_log(ASL_LEVEL_NOTICE, "Additional BB Scan\n");
-
-               e = bb_list;
-               while ((e != NULL) && (store_size > dst->all_max))
-               {
-                       if (e->size != 0)
-                       {
-                               if (dst->rotate_dir != NULL)
-                               {
-                                       str = NULL;
-                                       asprintf(&str, "%s/%s", dst->rotate_dir, e->name);
-                                       if (str == NULL) return -1;
-
-                                       /* syslog -x [str] -f [e->name] */
-                                       filesystem_copy(dst, e->name, str, 0);
-                                       free(str);
-                               }
-
-                               filesystem_unlink(e->name);
-                               store_size -= e->size;
-                               e->size = 0;
-                       }
-
-                       e = e->next;
-               }
-       }
-
-       free_name_list(ymd_list);        
-       free_name_list(bb_list);
-       free_name_list(aux_list);
-       free_name_list(bb_aux_list);
-
-       debug_log(ASL_LEVEL_NOTICE, "Data Store Size = %lu\n", store_size);
-
-       return 0;
-}
-
-/* move sequenced source files to dst dir, renaming as we go */
-static int
-module_copy_rename(asl_out_dst_data_t *dst)
-{
-       asl_out_file_list_t *src_list, *dst_list, *f, *dst_last;
-       char *base, *dst_dir;
-       char fpathsrc[MAXPATHLEN], fpathdst[MAXPATHLEN];
-       uint32_t src_count, dst_count;
-       int32_t x, moved;
-
-       if (dst == NULL) return -1;
-       if (dst->path == NULL) return -1;
-
-       base = strrchr(dst->path, '/');
-       if (base == NULL) return -1;
-
-       src_list = asl_list_src_files(dst);
-       if (src_list == 0)
-       {
-               debug_log(ASL_LEVEL_INFO, "    no src files\n");
-               return 0;
-       }
-
-       debug_log(ASL_LEVEL_INFO, "    src files\n");
-
-       src_count = 0;
-       for (f = src_list; f != NULL; f = f->next)
-       {
-               debug_log(ASL_LEVEL_INFO, "      %s\n", f->name);
-               src_count++;
-       }
-
-       dst_list = asl_list_dst_files(dst);
-
-       *base = '\0';
-       base++;
-
-       dst_dir = dst->rotate_dir;
-       if (dst_dir == NULL) dst_dir = dst->path;
-
-       dst_count = 0;
-       dst_last = dst_list;
-
-       if (dst_list == NULL) debug_log(ASL_LEVEL_INFO, "    no dst files\n");
-       else debug_log(ASL_LEVEL_INFO, "    dst files\n");
-
-       for (f = dst_list; f != NULL; f = f->next)
-       {
-               debug_log(ASL_LEVEL_INFO, "      %s\n", f->name);
-               dst_last = f;
-               dst_count++;
-       }
-
-       if (dst->flags & MODULE_FLAG_STYLE_SEQ)
-       {
-               for (f = dst_last; f != NULL; f = f->prev)
-               {
-                       int is_gz = 0;
-                       char *dot = strrchr(f->name, '.');
-                       if ((dot != NULL) && (!strcmp(dot, ".gz"))) is_gz = 1;
-
-                       snprintf(fpathsrc, sizeof(fpathsrc), "%s/%s", dst_dir, f->name);
-                       snprintf(fpathdst, sizeof(fpathdst), "%s/%s.%d%s", dst_dir, base, f->seq+src_count, (is_gz == 1) ? ".gz" : "");
-                       filesystem_rename(fpathsrc, fpathdst);
-               }
-
-               for (f = src_list, x = 0; f != NULL; f = f->next, x++)
-               {
-                       snprintf(fpathsrc, sizeof(fpathsrc), "%s/%s", dst->path, f->name);
-                       snprintf(fpathdst, sizeof(fpathdst), "%s/%s.%d", dst_dir, base, x);
-                       moved = filesystem_copy(dst, fpathsrc, fpathdst, dst->flags);
-                       if (moved != 0)
-                       {
-                               if (dst->flags & MODULE_FLAG_TRUNCATE) filesystem_truncate(fpathsrc);
-                               else filesystem_unlink(fpathsrc);
-                       }
-               }
-       }
-       else
-       {
-               for (f = src_list; f != NULL; f = f->next)
-               {
-                       /* final / active base stamped file looks like a checkpointed file - ignore it */
-                       if ((dst->flags & MODULE_FLAG_BASESTAMP) && (f->next == NULL)) break;
-
-                       snprintf(fpathsrc, sizeof(fpathsrc), "%s/%s", dst->path, f->name);
-
-                       /* MODULE_FLAG_EXTERNAL files are not decorated with a timestamp */
-                       if (dst->flags & MODULE_FLAG_EXTERNAL)
-                       {
-                               char tstamp[32];
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
 
-                               asl_make_timestamp(f->ftime, dst->flags, tstamp, sizeof(tstamp));
-                               snprintf(fpathdst, sizeof(fpathdst), "%s/%s.%s", dst_dir, base, tstamp);
-                       }
-                       else
-                       {
-                               snprintf(fpathdst, sizeof(fpathdst), "%s/%s", dst_dir, f->name);
-                       }
+#include <asl.h>
+#include <asl_msg.h>
+#include <asl_msg_list.h>
+#include <asl_store.h>
+#include <errno.h>
+#include <vproc_priv.h>
 
-                       moved = filesystem_copy(dst, fpathsrc, fpathdst, dst->flags);
-                       if (moved != 0)
-                       {
-                               if (dst->flags & MODULE_FLAG_TRUNCATE) filesystem_truncate(fpathsrc);
-                               else filesystem_unlink(fpathsrc);
-                       }
-               }
-       }
+#include "asl_common.h"
+#include "daemon.h"
+#include "cache_delete.h"
 
-       asl_out_file_list_free(src_list);
-       asl_out_file_list_free(dst_list);
+/* global */
+bool dryrun;
+uint32_t debug;
+FILE *debugfp;
+dispatch_queue_t work_queue;
 
-       if (base != NULL) *--base = '/';
+static dispatch_queue_t server_queue;
+static time_t module_ttl;
+static xpc_connection_t listener;
+static bool main_task_enqueued;
+static bool initial_main_task = true;
+static dispatch_source_t sig_term_src;
 
-       return 0;
-}
+/* wait 5 minutes to run main task after being invoked by XPC */
+#define MAIN_TASK_INITIAL_DELAY 300
 
-/* delete expired files */
-static int
-module_expire(asl_out_dst_data_t *dst)
+/*
+ * Used to set config parameters.
+ * Line format "= name value"
+ */
+static void
+_aslmanager_set_param(asl_out_dst_data_t *dst, char *s)
 {
-       asl_out_file_list_t *dst_list, *f;
-       char *base, *dst_dir, fpath[MAXPATHLEN];
-       time_t now, ttl, cutoff;
-
-       if (dst == NULL) return -1;
-       if (dst->path == NULL) return -1;
-       if (dst->ttl[LEVEL_ALL] == 0) return 0;
-
-       ttl = 0;
-       if (module_ttl > 0) ttl = module_ttl;
-       else ttl = dst->ttl[LEVEL_ALL];
-
-       ttl *= SECONDS_PER_DAY;
-
-       now = time(NULL);
-       if (ttl > now) return 0;
-
-       cutoff = now - ttl;
+       char **l;
+       uint32_t count;
 
-       base = strrchr(dst->path, '/');
-       if (base == NULL) return -1;
+       if (s == NULL) return;
+       if (s[0] == '\0') return;
 
-       dst_list = asl_list_dst_files(dst);
+       /* skip '=' and whitespace */
+       if (*s == '=') s++;
+       while ((*s == ' ') || (*s == '\t')) s++;
 
-       *base = '\0';
+       l = explode(s, " \t");
+       if (l == NULL) return;
 
-       dst_dir = dst->rotate_dir;
-       if (dst_dir == NULL) dst_dir = dst->path;
+       for (count = 0; l[count] != NULL; count++);
 
-       if (dst_list == NULL)
-       {
-               debug_log(ASL_LEVEL_INFO, "    no dst files\n");
-       }
-       else
+       /* name is required */
+       if (count == 0)
        {
-               debug_log(ASL_LEVEL_INFO, "    dst files\n");
-               for (f = dst_list; f != NULL; f = f->next) debug_log(ASL_LEVEL_INFO, "      %s\n", f->name);
+               free_string_list(l);
+               return;
        }
 
-       for (f = dst_list; f != NULL; f = f->next)
+       /* value is required */
+       if (count == 1)
        {
-               if (f->ftime <= cutoff)
-               {
-                       snprintf(fpath, sizeof(fpath), "%s/%s", dst_dir, f->name);
-                       filesystem_unlink(fpath);
-               }
+               free_string_list(l);
+               return;
        }
 
-       asl_out_file_list_free(dst_list);
-
-       if (base != NULL) *base = '/';
-
-       return 0;
-}
-
-/* check all_max size and delete files (oldest first) to stay within size limit */
-static int
-module_check_size(asl_out_dst_data_t *dst)
-{
-       asl_out_file_list_t *dst_list, *f, *dst_end;
-       char *base, *dst_dir, fpath[MAXPATHLEN];
-       size_t total;
-
-       if (dst == NULL) return -1;
-       if (dst->path == NULL) return -1;
-
-       if (dst->all_max == 0) return 0;
-
-       dst_list = asl_list_dst_files(dst);
-       if (dst_list == NULL)
+       if (!strcasecmp(l[0], "aslmanager_debug"))
        {
-               debug_log(ASL_LEVEL_INFO, "    no dst files\n");
-               return 0;
+               /* = debug level */
+               set_debug(DEBUG_ASL, l[1]);
        }
-
-       base = NULL;
-       dst_dir = dst->rotate_dir;
-       if (dst_dir == NULL)
+       else if (!strcasecmp(l[0], "store_ttl"))
        {
-               dst_dir = dst->path;
-               base = strrchr(dst->path, '/');
-               if (base == NULL)
-               {
-                       asl_out_file_list_free(dst_list);
-                       return -1;
-               }
-
-               *base = '\0';
+               /* = store_ttl days */
+               dst->ttl[LEVEL_ALL] = asl_core_str_to_time(l[1], SECONDS_PER_DAY);
        }
-
-       debug_log(ASL_LEVEL_INFO, "    dst files\n");
-       dst_end = dst_list;
-       for (f = dst_list; f != NULL; f = f->next)
+       else if (!strcasecmp(l[0], "module_ttl"))
        {
-               dst_end = f;
-               debug_log(ASL_LEVEL_INFO, "      %s size %lu\n", f->name, f->size);
+               /* = module_ttl days */
+               module_ttl = asl_core_str_to_time(l[1], SECONDS_PER_DAY);
        }
-
-       total = 0;
-       for (f = dst_list; f != NULL; f = f->next) total += f->size;
-       
-       for (f = dst_end; (total > dst->all_max) && (f != NULL); f = f->prev)
+       else if (!strcasecmp(l[0], "max_store_size"))
        {
-               snprintf(fpath, sizeof(fpath), "%s/%s", dst_dir, f->name);
-               filesystem_unlink(fpath);
-               total -= f->size;
+               /* = max_file_size bytes */
+               dst->all_max = asl_core_str_to_size(l[1]);
        }
-
-       asl_out_file_list_free(dst_list);
-
-       if (base != NULL) *base = '/';
-
-       return 0;
-}
-
-
-static int
-process_module(asl_out_module_t *mod)
-{
-       asl_out_rule_t *r;
-
-       if (mod == NULL) return -1;
-
-       debug_log(ASL_LEVEL_NOTICE, "----------------------------------------\n");
-       debug_log(ASL_LEVEL_NOTICE, "Processing module %s\n", (mod->name == NULL) ? "asl.conf" : mod->name);
-
-       for (r = mod->ruleset; r != NULL; r = r->next)
+       else if (!strcasecmp(l[0], "archive"))
        {
-               if (r->action == ACTION_OUT_DEST)
-               {
-                       if (r->dst == NULL)
-                       {
-                               debug_log(ASL_LEVEL_NOTICE, "NULL dst data for output rule - skipped\n");
-                       }
-                       else if (r->dst->flags & MODULE_FLAG_ROTATE)
-                       {
-                               debug_log(ASL_LEVEL_NOTICE, "Checking file %s\n", r->dst->path);
-                               debug_log(ASL_LEVEL_NOTICE, "- Rename, move to destination directory, and compress as required\n");
-
-                               module_copy_rename(r->dst);
-
-                               if (r->dst->ttl[LEVEL_ALL] > 0)
-                               {
-                                       debug_log(ASL_LEVEL_NOTICE, "- Check for expired files - TTL = %d days\n", r->dst->ttl[LEVEL_ALL]);
-                                       module_expire(r->dst);
-                               }
+               free(dst->rotate_dir);
+               dst->rotate_dir = NULL;
 
-                               if (r->dst->all_max > 0)
-                               {
-                                       debug_log(ASL_LEVEL_NOTICE, "- Check total storage used - MAX = %lu\n", r->dst->all_max);
-                                       module_check_size(r->dst);
-                               }
-                       }
-                       else if ((r->dst->flags & MODULE_FLAG_TYPE_ASL_DIR) && (r->dst->ttl[LEVEL_ALL] > 0))
-                       {
-                               process_asl_data_store(r->dst);
-                       }
+               /* = archive {0|1} path */
+               if (!strcmp(l[1], "1"))
+               {
+                       if (l[2] == NULL) dst->rotate_dir = strdup(PATH_ASL_ARCHIVE);
+                       else dst->rotate_dir = strdup(l[2]);
                }
        }
-
-       debug_log(ASL_LEVEL_NOTICE, "Finished processing module %s\n", (mod->name == NULL) ? "asl.conf" : mod->name);
-       return 0;
-}
-
-asl_msg_list_t *
-control_query(asl_msg_t *a)
-{
-       asl_msg_list_t *out;
-       char *qstr, *str, *res;
-       uint32_t len, reslen, status;
-       uint64_t cmax, qmin;
-       kern_return_t kstatus;
-       caddr_t vmstr;
-       security_token_t sec;
-
-       if (asl_server_port == MACH_PORT_NULL)
-       {
-               bootstrap_look_up2(bootstrap_port, ASL_SERVICE_NAME, &asl_server_port, 0, BOOTSTRAP_PRIVILEGED_SERVER);
-               if (asl_server_port == MACH_PORT_NULL) return NULL;
-       }
-
-       qstr = asl_msg_to_string((asl_msg_t *)a, &len);
-
-       str = NULL;
-       if (qstr == NULL)
+       else if (!strcasecmp(l[0], "store_path"))
        {
-               asprintf(&str, "1\nQ [= ASLOption control]\n");
+               /* = archive path */
+               free(dst->path);
+               dst->path = strdup(l[1]);
        }
-       else
+       else if (!strcasecmp(l[0], "archive_mode"))
        {
-               asprintf(&str, "1\n%s [= ASLOption control]\n", qstr);
-               free(qstr);
+               dst->mode = strtol(l[1], NULL, 0);
+               if ((dst->mode == 0) && (errno == EINVAL)) dst->mode = 0400;
        }
 
-       if (str == NULL) return NULL;
-
-       /* length includes trailing nul */
-       len = strlen(str) + 1;
-       out = NULL;
-       qmin = 0;
-       cmax = 0;
-       sec.val[0] = -1;
-       sec.val[1] = -1;
-
-       res = NULL;
-       reslen = 0;
-       status = ASL_STATUS_OK;
-
-       kstatus = vm_allocate(mach_task_self(), (vm_address_t *)&vmstr, len, TRUE);
-       if (kstatus != KERN_SUCCESS) return NULL;
-
-       memmove(vmstr, str, len);
-       free(str);
-
-       status = 0;
-       kstatus = _asl_server_query(asl_server_port, vmstr, len, qmin, 1, 0, (caddr_t *)&res, &reslen, &cmax, (int *)&status, &sec);
-       if (kstatus != KERN_SUCCESS) return NULL;
-
-       if (res == NULL) return NULL;
-
-       out = asl_msg_list_from_string(res);
-       vm_deallocate(mach_task_self(), (vm_address_t)res, reslen);
-
-       return out;
-}
-
-int
-checkpoint(const char *name)
-{
-       /* send checkpoint message to syslogd */
-       debug_log(ASL_LEVEL_NOTICE, "Checkpoint module %s\n", (name == NULL) ? "*" : name);
-       if (dryrun != 0) return 0;
-
-       asl_msg_t *qmsg = asl_msg_new(ASL_TYPE_QUERY);
-       char *tmp = NULL;
-       asl_msg_list_t *res;
-
-       asprintf(&tmp, "%s checkpoint", (name == NULL) ? "*" : name);
-       asl_msg_set_key_val_op(qmsg, "action", tmp, ASL_QUERY_OP_EQUAL);
-       free(tmp);
-
-       res = control_query(qmsg);
-
-       asl_msg_list_release(res);
-       return 0;
+       free_string_list(l);
 }
 
 int
@@ -1414,9 +137,14 @@ cli_main(int argc, char *argv[])
        int i, work;
        asl_out_module_t *mod, *m;
        asl_out_rule_t *r;
-       asl_out_dst_data_t store, *asl_store_dst = NULL;
+       asl_out_dst_data_t store, opts, *asl_store_dst = NULL;
        const char *mname = NULL;
+       char *path = NULL;
+       bool quiet = false;
+       bool cache_delete = false;
+       bool cache_delete_query = false;
 
+#if !TARGET_OS_SIMULATOR
        if (geteuid() != 0)
        {
                if (argc == 0) debug = DEBUG_ASL;
@@ -1425,6 +153,7 @@ cli_main(int argc, char *argv[])
                debug_log(ASL_LEVEL_ERR, "aslmanager must be run by root\n");
                exit(1);
        }
+#endif
 
        module_ttl = DEFAULT_TTL;
 
@@ -1433,9 +162,21 @@ cli_main(int argc, char *argv[])
        store.ttl[LEVEL_ALL] = DEFAULT_TTL;
        store.all_max = DEFAULT_MAX_SIZE;
 
+       memset(&opts, 0, sizeof(opts));
+       opts.ttl[LEVEL_ALL] = DEFAULT_TTL;
+       opts.all_max = DEFAULT_MAX_SIZE;
+
        for (i = 1; i < argc; i++)
        {
-               if (!strcmp(argv[i], "-s"))
+               if (!strcmp(argv[i], "-q"))
+               {
+                       quiet = true;
+               }
+               else if (!strcmp(argv[i], "-dd"))
+               {
+                       quiet = true;
+               }
+               else if (!strcmp(argv[i], "-s"))
                {
                        if (((i + 1) < argc) && (argv[i + 1][0] != '-'))
                        {
@@ -1445,14 +186,34 @@ cli_main(int argc, char *argv[])
                }
        }
 
+       if (!quiet)
+       {
+               int status = asl_make_database_dir(NULL, NULL);
+               if (status == 0) status = asl_make_database_dir(ASL_INTERNAL_LOGS_DIR, &path);
+               if (status == 0)
+               {
+                       char tstamp[32], *str = NULL;
+
+                       asl_make_timestamp(time(NULL), MODULE_NAME_STYLE_STAMP_LCL_B, tstamp, sizeof(tstamp));
+                       asprintf(&str, "%s/aslmanager.%s", path, tstamp);
+
+                       if (str != NULL)
+                       {
+                               if (status == 0) debugfp = fopen(str, "w");
+                               if (debugfp != NULL) debug |= DEBUG_FILE;
+                               free(str);
+                       }
+               }
+       }
+
        /* get parameters from asl.conf */
        mod = asl_out_module_init();
 
        if (mod != NULL)
        {
-               for (r = mod->ruleset; r != NULL; r = r->next)
+               for (r = mod->ruleset; (r != NULL) && (asl_store_dst == NULL); r = r->next)
                {
-                       if ((asl_store_dst == NULL) && (r->action == ACTION_OUT_DEST) && (!strcmp(r->dst->path, PATH_ASL_STORE)))
+                       if ((r->dst != NULL) && (r->action == ACTION_OUT_DEST) && (!strcmp(r->dst->path, PATH_ASL_STORE)))
                                asl_store_dst = r->dst;
                }
 
@@ -1471,30 +232,55 @@ cli_main(int argc, char *argv[])
        {
                if (!strcmp(argv[i], "-a"))
                {
+                       if (asl_store_dst == NULL) asl_store_dst = &store;
+
                        if (((i + 1) < argc) && (argv[i + 1][0] != '-')) asl_store_dst->rotate_dir = strdup(argv[++i]);
                        else asl_store_dst->rotate_dir = strdup(PATH_ASL_ARCHIVE);
                        asl_store_dst->mode = 0400;
                }
                else if (!strcmp(argv[i], "-store_ttl"))
                {
-                       if (((i + 1) < argc) && (argv[i + 1][0] != '-')) asl_store_dst->ttl[LEVEL_ALL] = atoi(argv[++i]);
+                       if (((i + 1) < argc) && (argv[i + 1][0] != '-'))
+                       {
+                               if (asl_store_dst == NULL) asl_store_dst = &store;
+                               asl_store_dst->ttl[LEVEL_ALL] = asl_core_str_to_time(argv[++i], SECONDS_PER_DAY);
+                       }
                }
                else if (!strcmp(argv[i], "-module_ttl"))
                {
-                       if (((i + 1) < argc) && (argv[i + 1][0] != '-')) module_ttl = atoi(argv[++i]);
+                       if (((i + 1) < argc) && (argv[i + 1][0] != '-')) module_ttl = asl_core_str_to_time(argv[++i], SECONDS_PER_DAY);
                }
                else if (!strcmp(argv[i], "-ttl"))
                {
-                       if (((i + 1) < argc) && (argv[i + 1][0] != '-')) module_ttl = asl_store_dst->ttl[LEVEL_ALL] = atoi(argv[++i]);
+                       if (((i + 1) < argc) && (argv[i + 1][0] != '-'))
+                       {
+                               opts.ttl[LEVEL_ALL] = asl_core_str_to_time(argv[++i], SECONDS_PER_DAY);
+
+                               if (asl_store_dst == NULL) asl_store_dst = &store;
+                               asl_store_dst->ttl[LEVEL_ALL] = opts.ttl[LEVEL_ALL];
+
+                               module_ttl = opts.ttl[LEVEL_ALL];
+                       }
                }
                else if (!strcmp(argv[i], "-size"))
                {
-                       if (((i + 1) < argc) && (argv[i + 1][0] != '-')) asl_store_dst->all_max = asl_str_to_size(argv[++i]);
+                       if (((i + 1) < argc) && (argv[i + 1][0] != '-'))
+                       {
+                               opts.all_max = asl_core_str_to_size(argv[++i]);
+
+                               if (asl_store_dst == NULL) asl_store_dst = &store;
+                               asl_store_dst->all_max = opts.all_max;
+                       }
                }
                else if (!strcmp(argv[i], "-checkpoint"))
                {
                        work |= DO_CHECKPT;
                }
+               else if (!strcmp(argv[i], "-cache_delete"))
+               {
+                       cache_delete = true;
+                       if (((i + 1) < argc) && (argv[i + 1][0] == 'q')) cache_delete_query = true;
+               }
                else if (!strcmp(argv[i], "-module"))
                {
                        work &= ~DO_ASLDB;
@@ -1516,18 +302,54 @@ cli_main(int argc, char *argv[])
                }
                else if (!strcmp(argv[i], "-dd"))
                {
-                       dryrun = 1;
+                       dryrun = true;
 
                        if (((i + i) < argc) && (argv[i+1][0] != '-')) set_debug(DEBUG_STDERR, argv[++i]);
-                       else set_debug(DEBUG_STDERR, NULL);
+                       else set_debug(DEBUG_STDERR, "l7");
                }
        }
 
        if (asl_store_dst->path == NULL) asl_store_dst->path = strdup(PATH_ASL_STORE);
 
-       debug_log(ASL_LEVEL_ERR, "aslmanager starting%s\n", (dryrun == 1) ? " dryrun" : "");
+       debug_log(ASL_LEVEL_ERR, "aslmanager starting%s\n", dryrun ? " dryrun" : "");
+
+       if (cache_delete)
+       {
+               size_t curr_size = 0;
+
+               if (cache_delete_task(true, &curr_size) != 0)
+               {
+                       debug_log(ASL_LEVEL_NOTICE, "cache_delete_process failed - can't determine current size\n");
+               }
+               else
+               {
+                       debug_log(ASL_LEVEL_NOTICE, "cache delete current size = %lu\n", curr_size);
+
+                       if (!cache_delete_query)
+                       {
+                               size_t new_size = curr_size - opts.all_max;
+
+                               if (cache_delete_task(false, &new_size) != 0)
+                               {
+                                       debug_log(ASL_LEVEL_NOTICE, "cache_delete_process failed - delete failed\n");
+                               }
+                               else
+                               {
+                                       debug_log(ASL_LEVEL_NOTICE, "cache delete new size = %lu\n", new_size);
+                               }
+                       }
+               }
+
+               asl_out_module_free(mod);
+
+               debug_log(ASL_LEVEL_NOTICE, "----------------------------------------\n");
+               debug_log(ASL_LEVEL_ERR, "aslmanager finished%s\n", dryrun ? " dryrun" : "");
+               debug_close();
+
+               return 0;
+       }
 
-       if (work & DO_ASLDB) process_asl_data_store(asl_store_dst);
+       if (work & DO_ASLDB) process_asl_data_store(asl_store_dst, &opts);
 
        if (work & DO_MODULE)
        {
@@ -1537,9 +359,13 @@ cli_main(int argc, char *argv[])
                {
                        for (m = mod; m != NULL; m = m->next)
                        {
-                               if ((mname == NULL) || ((m->name != NULL) && (!strcmp(m->name, mname))))
+                               if (mname == NULL)
                                {
-                                       process_module(m);
+                                       process_module(m, NULL);
+                               }
+                               else if ((m->name != NULL) && (!strcmp(m->name, mname)))
+                               {
+                                       process_module(m, &opts);
                                }
                        }
                }
@@ -1548,12 +374,43 @@ cli_main(int argc, char *argv[])
        asl_out_module_free(mod);
 
        debug_log(ASL_LEVEL_NOTICE, "----------------------------------------\n");
-       debug_log(ASL_LEVEL_ERR, "aslmanager finished%s\n", (dryrun == 1) ? " dryrun" : "");
-       if (asl_aux_fd >= 0) asl_close_auxiliary_file(asl_aux_fd);
+       debug_log(ASL_LEVEL_ERR, "aslmanager finished%s\n", dryrun ? " dryrun" : "");
+       debug_close();
 
        return 0;
 }
 
+/* dispatched on server_queue, dispatches to work_queue */
+void
+main_task(void)
+{
+       /* if main task is already running or queued, do nothing */
+       if (main_task_enqueued) return;
+
+       main_task_enqueued = true;
+       xpc_transaction_begin();
+
+       if (initial_main_task)
+       {
+               initial_main_task = false;
+               dispatch_time_t delay = dispatch_walltime(NULL, MAIN_TASK_INITIAL_DELAY * NSEC_PER_SEC);
+
+               dispatch_after(delay, work_queue, ^{
+                       cli_main(0, NULL);
+                       main_task_enqueued = false;
+                       xpc_transaction_end();
+               });
+       }
+       else
+       {
+               dispatch_async(work_queue, ^{
+                       cli_main(0, NULL);
+                       main_task_enqueued = false;
+                       xpc_transaction_end();
+               });
+       }
+}
+
 static void
 accept_connection(xpc_connection_t peer)
 {
@@ -1571,14 +428,16 @@ accept_connection(xpc_connection_t peer)
                         * Some day, we may use the dictionary to pass parameters
                         * to aslmanager, but for now, we ignore the input.
                         */
-                       if (uid == 0) cli_main(0, NULL);
+
+                       if (uid == geteuid())
+                       {
+                               main_task();
+                       }
                }
                else if (xpc_get_type(request) == XPC_TYPE_ERROR)
                {
                        /* disconnect */
                }
-
-               dispatch_async(serverq, ^__attribute__((noreturn)) { xpc_server_exit(0); });
        });
 
        xpc_connection_resume(peer);
@@ -1593,16 +452,32 @@ main(int argc, char *argv[])
 
        if (is_managed == 0) return cli_main(argc, argv);
 
+       /* Set I/O policy */
+       setiopolicy_np(IOPOL_TYPE_DISK, IOPOL_SCOPE_PROCESS, IOPOL_THROTTLE);
+
        /* XPC server */
-       serverq = dispatch_queue_create("aslmanager", NULL);
-       xpc_track_activity();
+       server_queue = dispatch_queue_create("aslmanager", NULL);
+
+       work_queue = dispatch_queue_create("work queue", NULL);
+
+       /* Exit on SIGTERM */
+       signal(SIGTERM, SIG_IGN);
+       sig_term_src = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, (uintptr_t)SIGTERM, 0, dispatch_get_main_queue());
+       dispatch_source_set_event_handler(sig_term_src, ^{
+               debug_log(ASL_LEVEL_NOTICE, "SIGTERM exit\n");
+               exit(0);
+       });
+
+       dispatch_resume(sig_term_src);
 
        /* Handle incoming messages. */
-       listener = xpc_connection_create_mach_service("com.apple.aslmanager", serverq, XPC_CONNECTION_MACH_SERVICE_LISTENER);
+       listener = xpc_connection_create_mach_service("com.apple.aslmanager", server_queue, XPC_CONNECTION_MACH_SERVICE_LISTENER);
        xpc_connection_set_event_handler(listener, ^(xpc_object_t peer) {
                if (xpc_get_type(peer) == XPC_TYPE_CONNECTION) accept_connection(peer);
        });
        xpc_connection_resume(listener);
 
+       cache_delete_register();
+
        dispatch_main();
 }