]> git.saurik.com Git - apple/syslog.git/blobdiff - syslogd.tproj/asl_action.c
syslog-267.tar.gz
[apple/syslog.git] / syslogd.tproj / asl_action.c
index fd4d38108adff2f45560c3e1c9a2c74d29a142d5..b33d58275d28cf79af32ff42f41293cbc0532a5d 100644 (file)
@@ -22,7 +22,6 @@
  */
 
 #include <TargetConditionals.h>
-
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <sys/socket.h>
 #include <xpc/private.h>
 
 #define _PATH_WALL "/usr/bin/wall"
-#define NOTIFY_PATH_SERVICE "com.apple.system.notify.service.path:0x87:"
 
 #define MY_ID "asl_action"
 
-/* XXX add to asl.h */
-#define ASL_KEY_MODULE "ASLModule"
-
 #define MAX_FAILURES 5
 
 #define ACTION_STATUS_ERROR  -1
 
 #define IDLE_CLOSE 300
 
+#define PRIVATE_FLAG_NO_CLOSE    0x00000001 /* File or Store is closed by a cancellation handler */
+
+#define DST_CLOSE_CHECKPOINT 0
+#define DST_CLOSE_DELETED 1
+#define DST_CLOSE_ERROR 2
+#define DST_CLOSE_IDLE 3
+#define DST_CLOSE_SHUTDOWN 4
+static const char *why_str[] =
+{
+       "checkpoint",
+       "deleted",
+       "error",
+       "idle",
+       "shutdown"
+};
+
 #define forever for(;;)
 
 static dispatch_queue_t asl_action_queue;
@@ -67,31 +78,47 @@ static dispatch_source_t checkpoint_timer;
 static time_t sweep_time = 0;
 
 #if TARGET_OS_EMBEDDED
+#ifndef CRASH_MOVER_SERVICE
+#define CRASH_MOVER_SERVICE "com.apple.crash_mover"
+#endif
 static dispatch_queue_t crashlog_queue;
-static dispatch_source_t crashlog_sentinel_src;
-static int crashlog_sentinel_fd = -1;
 static time_t crashmover_state = 0;
 static int crashmover_token = -1;
 #endif
 
-typedef struct store_data
+typedef struct asl_file_data
+{
+       uint64_t next_id;
+       asl_file_t *aslfile;
+       time_t last_time;
+       uint32_t flags;
+       uint32_t pending;
+       dispatch_source_t monitor;
+} asl_action_asl_file_data_t;
+
+typedef struct asl_store_data
 {
-       asl_file_t *store;
        FILE *storedata;
+       asl_file_t *aslfile;
        uint64_t next_id;
        time_t last_time;
+       uint32_t flags;
+       uint32_t pending;
        uint32_t p_year;
        uint32_t p_month;
        uint32_t p_day;
-       dispatch_source_t monitor;
-} asl_action_store_data_t;
+       dispatch_source_t storedata_monitor;
+       dispatch_source_t aslfile_monitor;
+} asl_action_asl_store_data_t;
 
 typedef struct file_data
 {
        int fd;
-       uint32_t last_hash;
-       uint32_t last_count;
+       uint32_t flags;
        time_t last_time;
+       uint32_t last_count;
+       uint32_t last_hash;
+       uint32_t pending;
        char *last_msg;
        dispatch_source_t dup_timer;
        dispatch_source_t monitor;
@@ -105,12 +132,13 @@ typedef struct set_param_data
 static int action_asl_store_count;
 static bool store_has_logged;
 
-extern void db_save_message(aslmsg m);
+extern void db_save_message(asl_msg_t *m);
 
 /* forward */
 static int _act_file_checkpoint_all(uint32_t force);
 static void _asl_action_post_process_rule(asl_out_module_t *m, asl_out_rule_t *r);
 static void _asl_action_close_idle_files(time_t idle_time);
+static void _act_dst_close(asl_out_rule_t *r, int why);
 
 static void
 _act_out_set_param(asl_out_module_t *m, char *x, bool eval)
@@ -139,6 +167,7 @@ _act_out_set_param(asl_out_module_t *m, char *x, bool eval)
 
                if (intval == 0) m->flags &= ~MODULE_FLAG_ENABLED;
                else m->flags|= MODULE_FLAG_ENABLED;
+               free_string_list(l);
                return;
        }
        else if (!strcasecmp(l[0], "disable"))
@@ -151,6 +180,7 @@ _act_out_set_param(asl_out_module_t *m, char *x, bool eval)
 
                if (intval != 0) m->flags &= ~MODULE_FLAG_ENABLED;
                else m->flags|= MODULE_FLAG_ENABLED;
+               free_string_list(l);
                return;
        }
 
@@ -176,7 +206,7 @@ _act_notify(asl_out_module_t *m, asl_out_rule_t *r)
 }
 
 static void
-_act_broadcast(asl_out_module_t *m, asl_out_rule_t *r, aslmsg msg)
+_act_broadcast(asl_out_module_t *m, asl_out_rule_t *r, asl_msg_t *msg)
 {
 #if !TARGET_OS_EMBEDDED
        FILE *pw;
@@ -193,7 +223,7 @@ _act_broadcast(asl_out_module_t *m, asl_out_rule_t *r, aslmsg msg)
        if (strcmp(m->name, ASL_MODULE_NAME)) return;
 
        val = r->options;
-       if (val == NULL) val = asl_get(msg, ASL_KEY_MSG);
+       if (val == NULL) val = asl_msg_get_val_for_key(msg, ASL_KEY_MSG);
        if (val == NULL) return;
 
        pw = popen(_PATH_WALL, "w");
@@ -209,7 +239,19 @@ _act_broadcast(asl_out_module_t *m, asl_out_rule_t *r, aslmsg msg)
 }
 
 static void
-_act_access_control(asl_out_module_t *m, asl_out_rule_t *r, aslmsg msg)
+_act_set_key(asl_out_module_t *m, asl_out_rule_t *r, asl_msg_t *msg)
+{
+       /* Placeholder */
+}
+
+static void
+_act_unset_key(asl_out_module_t *m, asl_out_rule_t *r, asl_msg_t *msg)
+{
+       /* Placeholder */
+}
+
+static void
+_act_access_control(asl_out_module_t *m, asl_out_rule_t *r, asl_msg_t *msg)
 {
        int32_t ruid, rgid;
        char *p;
@@ -233,10 +275,10 @@ _act_access_control(asl_out_module_t *m, asl_out_rule_t *r, aslmsg msg)
                rgid = atoi(p);
        }
 
-       if (ruid != -1) asl_set(msg, ASL_KEY_READ_UID, r->options);
+       if (ruid != -1) asl_msg_set_key_val(msg, ASL_KEY_READ_UID, r->options);
        if (p != NULL)
        {
-               if (rgid != -1) asl_set(msg, ASL_KEY_READ_GID, p);
+               if (rgid != -1) asl_msg_set_key_val(msg, ASL_KEY_READ_GID, p);
                p--;
                *p = ' ';
        }
@@ -244,144 +286,696 @@ _act_access_control(asl_out_module_t *m, asl_out_rule_t *r, aslmsg msg)
 
 #if TARGET_OS_EMBEDDED
 static void
-_crashlog_sentinel_init(void)
+_crashlog_queue_check(void)
+{
+       /*
+        * Check whether the crashlog queue has been suspended for too long.
+        * We allow the crashlog quque to be suspended for 60 seconds.
+        * After that, we start logging again.  This prevents syslogd from
+        * filling memory due to a suspended queue.  CrashMover really shoud
+        * take no more than a second or two to finish.
+        */
+       if (crashmover_state == 0) return;
+       if ((time(NULL) - crashmover_state) <= 60) return;
+
+       asldebug("CrashMover timeout: resuming crashlog queue\n");
+       dispatch_resume(crashlog_queue);
+       crashmover_state = 0;
+}
+#endif
+
+/*
+ * cover routine for asl_out_dst_file_create_open()
+ */
+static int
+_act_file_create_open(asl_out_dst_data_t *dst)
+{
+       return asl_out_dst_file_create_open(dst, NULL);
+}
+
+/*
+ * Checks and creates (if required) a directory for an ASL data store.
+ */
+static int
+_asl_dir_create(asl_out_rule_t *r)
 {
-       char path[MAXPATHLEN];
+       struct stat sb;
+       int status;
+
+       memset(&sb, 0, sizeof(struct stat));
+       status = stat(r->dst->path, &sb);
+       if (status == 0)
+       {
+               /* Store path must be a directory */
+               if (!S_ISDIR(sb.st_mode))
+               {
+                       asldebug("_asl_dir_create: expected a directory at path %s\n", r->dst->path);
+                       return -1;
+               }
+       }
+       else if (errno == ENOENT)
+       {
+               /* Directory doesn't exists - try to create it */
+               status = asl_out_mkpath(global.asl_out_module, r);
+               if (status != 0)
+               {
+                       asldebug("_asl_dir_create: asl_out_mkpath failed: %s\n", r->dst->path);
+                       return -1;
+               }
+       }
+       else
+       {
+               /* Unexpected stat error */
+               asldebug("_asl_dir_create: stat error %s\n", strerror(errno));
+               return -1;
+       }
 
-       if (crashlog_sentinel_src != NULL) return;
+       return 0;
+}
 
-       snprintf(path, sizeof(path), "%s/com.apple.asl.%ld", _PATH_CRASHREPORTER, time(NULL));
+/*
+ * Close an ASL Directory StoreData file
+ * - cancel the dispatch source for the file
+ */
+static void
+_asl_dir_storedata_close(asl_out_rule_t *r)
+{
+       asl_action_asl_store_data_t *as_data = (asl_action_asl_store_data_t *)r->dst->private;
+       if (as_data->storedata == NULL) return;
 
-       crashlog_sentinel_fd = open(path, O_WRONLY | O_CREAT);
-       if (crashlog_sentinel_fd < 0)
+       if (as_data->storedata_monitor == NULL)
+       {
+               /*
+                * This should never happen, but _asl_dir_storedata_open allows
+                * dispatch_source_create to fail silently.  If there is no dispatch
+                * source, we just close the file.
+                */
+               asldebug("close ASL storedata fd %d\n", fileno(as_data->storedata));
+               fclose(as_data->storedata);
+       }
+       else
+       {
+               /*
+                * The storedata_monitor cancel handler will close the file.
+                */
+               dispatch_source_cancel(as_data->storedata_monitor);
+               dispatch_release(as_data->storedata_monitor);
+
+       }
+
+       asldebug("_asl_dir_storedata_close %p\n", as_data->storedata);
+       as_data->storedata = NULL;
+}
+
+/*
+ * Open an ASL Directory StoreData file
+ * - check directory existance and create it if necessary
+ * - check for StoreData file and create it (with given xid) if necessary.
+ * - create a dispatch source to watch for StoreData file deletion
+ */
+static int
+_asl_dir_storedata_open(asl_out_rule_t *r, uint64_t xid)
+{
+       struct stat sb;
+       int status;
+       char dstpath[MAXPATHLEN];
+
+       asl_action_asl_store_data_t *as_data = (asl_action_asl_store_data_t *)r->dst->private;
+       if (as_data->storedata != NULL) return 0;
+
+       status = _asl_dir_create(r);
+       if (status != 0)
+       {
+               asldebug("_asl_dir_storedata_open: No directory at path %s\n", r->dst->path);
+               return -1;
+       }
+
+       /* StoreData file is not open */
+       snprintf(dstpath, sizeof(dstpath), "%s/%s", r->dst->path, FILE_ASL_STORE_DATA);
+
+       memset(&sb, 0, sizeof(struct stat));
+       status = stat(dstpath, &sb);
+       if (status == 0)
+       {
+               /* StoreData file exists */
+               as_data->storedata = fopen(dstpath, "r+");
+               if (as_data->storedata == NULL)
+               {
+                       asldebug("_asl_dir_storedata_open: fopen existing %s: %s\n", dstpath, strerror(errno));
+                       return -1;
+               }
+       }
+       else if (errno == ENOENT)
+       {
+               /*
+                * StoreData file does not exist.
+                * Create a new StoreData with a given xid.
+                */
+               //TODO: This should return a failure if there are any ASL files.
+               /* that would require reading the directory, thus a performance hit */
+               as_data->storedata = fopen(dstpath, "w+");
+               if (as_data->storedata == NULL)
+               {
+                       asldebug("_asl_dir_storedata_open: fopen new %s: %s\n", dstpath, strerror(errno));
+                       return -1;
+               }
+
+               uint64_t xout = asl_core_htonq(xid);
+               status = fwrite(&xout, sizeof(uint64_t), 1, as_data->storedata);
+               if (status != 1)
+               {
+                       asldebug("_asl_dir_storedata_open: storedata write failed %d %s\n", errno, strerror(errno));
+                       return -1;
+               }
+
+#if !TARGET_IPHONE_SIMULATOR
+               if (chown(dstpath, r->dst->uid[0], r->dst->gid[0]) != 0)
+               {
+                       asldebug("_asl_dir_storedata_open: chown %d %d new %s: %s\n", dstpath, r->dst->uid[0], r->dst->gid[0], strerror(errno));
+                       return -1;
+               }
+#endif
+       }
+       else
+       {
+               /* Unexpected stat error */
+               asldebug("_asl_dir_storedata_open: stat error %s\n", strerror(errno));
+               return -1;
+       }
+
+       /* create storedata_monitor */
+       int fd = fileno(as_data->storedata);
+       FILE *sdfp = as_data->storedata;
+
+       as_data->storedata_monitor = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE, fd, DISPATCH_VNODE_DELETE, asl_action_queue);
+       if (as_data->storedata_monitor != NULL)
+       {
+               dispatch_source_set_event_handler(as_data->storedata_monitor, ^{
+                       _act_dst_close(r, DST_CLOSE_DELETED);
+               });
+
+               dispatch_source_set_cancel_handler(as_data->storedata_monitor, ^{
+                       asldebug("cancel/close ASL storedata fd %d\n", fd);
+                       fclose(sdfp);
+               });
+
+               dispatch_resume(as_data->storedata_monitor);
+       }
+
+       asldebug("_asl_dir_storedata_open ASL storedata %s fd %d\n", dstpath, fd);
+       return 0;
+}
+
+/*
+ * Close an ASL Directory ASL file
+ * - cancel the dispatch source for the file
+ * - clears file name and timestamp in asl_action_asl_store_data_t structue
+ */
+static void
+_asl_dir_today_close(asl_out_rule_t *r)
+{
+       asl_action_asl_store_data_t *as_data = (asl_action_asl_store_data_t *)r->dst->private;
+       if (as_data->aslfile == NULL) return;
+
+       if (as_data->pending != 0)
        {
                char *str = NULL;
-               asprintf(&str, "[Sender syslogd] [Level 3] [PID %u] [Facility syslog] [Message Sentinel %s create/open failed (%s)]", global.pid, path, strerror(errno));
+               asprintf(&str, "[Sender syslogd] [Level 4] [PID %u] [Facility syslog] [Message ASL Store %s was closed with %d pending messages]", global.pid, r->dst->fname, as_data->pending);
                internal_log_message(str);
                free(str);
-               return;
        }
 
-       close(crashlog_sentinel_fd);
+       if (as_data->aslfile_monitor == NULL)
+       {
+               /*
+                * This should never happen, but _asl_dir_today_open allows
+                * dispatch_source_create to fail silently.  If there is no dispatch
+                * source, we just close the file.
+                */
+               asldebug("close ASL fd %d\n", fileno(as_data->aslfile->store));
+               asl_file_close(as_data->aslfile);
+       }
+       else
+       {
+               /*
+                * The aslfile_monitor cancel handler will close the file.
+                */
+               dispatch_source_cancel(as_data->aslfile_monitor);
+               dispatch_release(as_data->aslfile_monitor);
+               as_data->aslfile_monitor = NULL;
+       }
+
+       as_data->p_year = 0;
+       as_data->p_month = 0;
+       as_data->p_day = 0;
+
+       free(r->dst->fname);
+       r->dst->fname = NULL;
+
+       as_data->aslfile = NULL;
+}
+
+/*
+ * Check file size.
+ */
+static int
+_act_checkpoint(asl_out_rule_t *r, uint32_t force)
+{
+       char tmpfname[MAXPATHLEN], *fn;
+
+       if (r == NULL) return 0;
+       if (r->dst == NULL) return 0;
+
+       fn = r->dst->fname;
+       if (fn == NULL)
+       {
+               if (r->dst->path == NULL) return 0;
+               asl_make_dst_filename(r->dst, tmpfname, sizeof(tmpfname));
+               fn = tmpfname;
+       }
+
+       if ((force == CHECKPOINT_TEST) && (r->dst->file_max == 0)) return 0;
+
+       if ((r->dst->size == 0) || (r->dst->stamp == 0))
+       {
+               struct stat sb;
+
+               memset(&sb, 0, sizeof(struct stat));
+
+               if (stat(fn, &sb) < 0)
+               {
+                       if (errno == ENOENT) return 0;
+                       return -1;
+               }
+
+               if (r->dst->stamp == 0) r->dst->stamp = sb.st_birthtimespec.tv_sec;
+               if (r->dst->stamp == 0) r->dst->stamp = sb.st_mtimespec.tv_sec;
+               r->dst->size = sb.st_size;
+       }
+       
+       if ((force == CHECKPOINT_TEST) && (r->dst->size < r->dst->file_max)) return 0;
+
+       if (r->dst->flags & MODULE_FLAG_BASESTAMP)
+       {
+               _act_dst_close(r, DST_CLOSE_CHECKPOINT);
+       }
+       else
+       {
+               char srcpath[MAXPATHLEN];
+               char dstpath[MAXPATHLEN];
+               char tstamp[32];
+
+               if (r->dst->stamp == 0) r->dst->stamp = time(NULL);
+               asl_make_timestamp(r->dst->stamp, r->dst->flags, tstamp, sizeof(tstamp));
+
+               snprintf(srcpath, sizeof(srcpath), "%s", fn);
+               snprintf(dstpath, sizeof(dstpath), "%s.%s", fn, tstamp);
+
+               _act_dst_close(r, DST_CLOSE_CHECKPOINT);
+               rename(srcpath, dstpath);
+       }
+
+       r->dst->stamp = 0;
+       r->dst->size = 0;
+       return 1;
+}
+
+/*
+ * Open today's ASL file in an ASL directory
+ * - Checks date and closes a currently open file if it has the wrong date
+ * - Opens today's file
+ */
+static int
+_asl_dir_today_open(asl_out_rule_t *r, const time_t *tick)
+{
+       int status;
+       mode_t mask;
+       struct tm ctm;
+       time_t now;
+
+       if (r == NULL) return -1;
+       if (r->dst == NULL) return -1;
+
+       status = _asl_dir_create(r);
+       if (status != 0)
+       {
+               asldebug("_asl_dir_today_open: No directory at path %s\n", r->dst->path);
+               return -1;
+       }
+
+       asl_action_asl_store_data_t *as_data = (asl_action_asl_store_data_t *)r->dst->private;
+
+       memset(&ctm, 0, sizeof(struct tm));
+       if (tick == NULL)
+       {
+               now = time(NULL);
+               tick = (const time_t *)&now;
+       }
+
+       if (localtime_r(tick, &ctm) == NULL)
+       {
+               asldebug("_asl_dir_today_open: localtime_r error %s\n", strerror(errno));
+               return -1;
+       }
+
+       /* checks file_max and closes if required */
+       status = _act_checkpoint(r, CHECKPOINT_TEST);
+       if (status == 1) trigger_aslmanager();
+
+       if (as_data->aslfile != NULL)
+       {
+               /* Check the date */
+               if ((as_data->p_year == ctm.tm_year) && (as_data->p_month == ctm.tm_mon) && (as_data->p_day == ctm.tm_mday)) return 0;
+
+               /* Wrong date, close the current file */
+               _asl_dir_today_close(r);
+       }
+
+       /* Open data file */
+
+       if (r->dst->flags & MODULE_FLAG_BASESTAMP)
+       {
+               char tstamp[32];
+
+               if (tick == NULL)
+               {
+                       now = time(NULL);
+                       tick = (const time_t *)&now;
+               }
+
+               asl_make_timestamp(now, r->dst->flags, tstamp, sizeof(tstamp));
+               asprintf(&(r->dst->fname), "%s/%s.asl", r->dst->path, tstamp);
+       }
+       else
+       {
+               asprintf(&(r->dst->fname), "%s/%d.%02d.%02d.asl", r->dst->path, ctm.tm_year + 1900, ctm.tm_mon + 1, ctm.tm_mday);
+       }
+
+
+       if (r->dst->fname == NULL)
+       {
+               asldebug("_asl_dir_today_open: asprintf error %s\n", strerror(errno));
+               return -1;
+       }
+
+#if TARGET_IPHONE_SIMULATOR
+       uid_t uid = -1;
+       gid_t gid = -1;
+#else
+       uid_t uid = r->dst->uid[0];
+       gid_t gid = r->dst->gid[0];
+#endif
+
+       mask = umask(0);
+       status = asl_file_open_write(r->dst->fname, (r->dst->mode & 00666), uid, gid, &(as_data->aslfile));
+       umask(mask);
+
+       if (status != ASL_STATUS_OK)
+       {
+               asldebug("_asl_dir_today_open: asl_file_open_write %s error %s\n", r->dst->fname, asl_core_error(status));
+               free(r->dst->fname);
+               r->dst->fname = NULL;
+               return -1;
+       }
+
+       if (fseek(as_data->aslfile->store, 0, SEEK_END) != 0)
+       {
+               asldebug("_asl_dir_today_open: fseek %s error %s\n", r->dst->fname, strerror(errno));
+               free(r->dst->fname);
+               r->dst->fname = NULL;
+               return -1;
+       }
+
+       as_data->p_year = ctm.tm_year;
+       as_data->p_month = ctm.tm_mon;
+       as_data->p_day = ctm.tm_mday;
 
-       crashlog_sentinel_fd = open(path, O_EVTONLY, 0);
-       if (crashlog_sentinel_fd < 0)
+       /* create aslfile_monitor */
+       int fd = fileno(as_data->aslfile->store);
+       asl_file_t *aslf = as_data->aslfile;
+
+       as_data->aslfile_monitor = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE, fd, DISPATCH_VNODE_DELETE, asl_action_queue);
+       if (as_data->aslfile_monitor != NULL)
+       {
+               dispatch_source_set_event_handler(as_data->aslfile_monitor, ^{
+                       _act_dst_close(r, DST_CLOSE_DELETED);
+               });
+
+               dispatch_source_set_cancel_handler(as_data->aslfile_monitor, ^{
+                       asldebug("cancel/close ASL file fd %d\n", fd);
+                       asl_file_close(aslf);
+               });
+
+               dispatch_resume(as_data->aslfile_monitor);
+       }
+
+       asldebug("_asl_dir_today_open ASL file %s fd %d\n", r->dst->fname, fd);
+
+       return 0;
+}
+
+static void
+_asl_file_close(asl_out_rule_t *r)
+{
+       if (r == NULL) return;
+       if (r->dst == NULL) return;
+
+       asl_action_asl_file_data_t *af_data = (asl_action_asl_file_data_t *)r->dst->private;
+       if (af_data->aslfile == NULL) return;
+
+       if (af_data->pending != 0)
        {
                char *str = NULL;
-               asprintf(&str, "[Sender syslogd] [Level 3] [PID %u] [Facility syslog] [Message Sentinel %s event/open failed (%s)]", global.pid, path, strerror(errno));
+               asprintf(&str, "[Sender syslogd] [Level 4] [PID %u] [Facility syslog] [Message ASL File %s was closed with %d pending messages]", global.pid, r->dst->fname, af_data->pending);
                internal_log_message(str);
                free(str);
-               return;
        }
 
-       crashlog_sentinel_src = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE, (uintptr_t)crashlog_sentinel_fd, DISPATCH_VNODE_DELETE, asl_action_queue);
-       if (crashlog_sentinel_src == NULL)
+       if (af_data->monitor == NULL)
+       {
+               /*
+                * This should never happen, but _asl_file_open allows
+                * dispatch_source_create to fail silently.  If there is no dispatch
+                * source, we just close the file.
+                */
+               asldebug("close ASL fd %d\n", fileno(af_data->aslfile->store));
+               asl_file_close(af_data->aslfile);
+       }
+       else
+       {
+               /*
+                * The monitor cancel handler will close the file.
+                */
+               dispatch_source_cancel(af_data->monitor);
+               dispatch_release(af_data->monitor);
+               af_data->monitor = NULL;
+       }
+
+       af_data->aslfile = NULL;
+}
+
+static int
+_asl_file_open(asl_out_rule_t *r)
+{
+       int fd, status;
+
+       if (r == NULL) return -1;
+       if (r->dst == NULL) return -1;
+
+       asl_action_asl_file_data_t *af_data = (asl_action_asl_file_data_t *)r->dst->private;
+       if (af_data->aslfile != NULL) return 0;
+
+       /* create path if necessary */
+       status = asl_out_mkpath(global.asl_out_module, r);
+       if (status != 0)
+       {
+               asldebug("_asl_file_open: asl_out_mkpath %s failed\n", r->dst->path);
+               return -1;
+       }
+
+       fd = _act_file_create_open(r->dst);
+       if (fd < 0)
+       {
+               asldebug("_asl_file_open: _act_file_create_open %s failed %d %s\n", r->dst->fname, errno, strerror(errno));
+               return -1;
+       }
+
+       close(fd);
+
+       if (r->dst->fname == NULL) return -1;
+
+       status = asl_file_open_write(r->dst->fname, 0, -1, -1, &(af_data->aslfile));
+       if (status != ASL_STATUS_OK)
+       {
+               asldebug("_asl_file_open: asl_file_open_write %s failed %d %s\n", r->dst->fname, errno, strerror(errno));
+               return -1;
+       }
+
+       /* create monitor */
+       fd = fileno(af_data->aslfile->store);
+       asl_file_t *aslf = af_data->aslfile;
+
+       af_data->monitor = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE, fd, DISPATCH_VNODE_DELETE, asl_action_queue);
+       if (af_data->monitor != NULL)
+       {
+               dispatch_source_set_event_handler(af_data->monitor, ^{
+                       _act_dst_close(r, DST_CLOSE_DELETED);
+               });
+
+               dispatch_source_set_cancel_handler(af_data->monitor, ^{
+                       asldebug("cancel/close ASL file fd %d\n", fd);
+                       asl_file_close(aslf);
+               });
+
+               dispatch_resume(af_data->monitor);
+       }
+
+       asldebug("_asl_file_open ASL file %s fd %d\n", r->dst->fname, fd);
+       return 0;
+}
+
+static void
+_text_file_close(asl_out_rule_t *r)
+{
+       asl_action_file_data_t *f_data = (asl_action_file_data_t *)r->dst->private;
+       if (f_data->fd < 0) return;
+
+       if (f_data->pending != 0)
        {
                char *str = NULL;
-               asprintf(&str, "[Sender syslogd] [Level 3] [PID %u] [Facility syslog] [Message Sentinel %s dispatch_source_create failed]", global.pid, path);
+               asprintf(&str, "[Sender syslogd] [Level 4] [PID %u] [Facility syslog] [Message File %s was closed with %d pending messages]", global.pid, r->dst->fname, f_data->pending);
                internal_log_message(str);
                free(str);
-               close(crashlog_sentinel_fd);
-               crashlog_sentinel_fd = -1;
-               return;
        }
 
-       dispatch_source_set_event_handler(crashlog_sentinel_src, ^{
-               if (crashmover_state != 0)
+       if (f_data->monitor == NULL)
+       {
+               /*
+                * This should never happen, but _text_file_open allows
+                * dispatch_source_create to fail silently.  If there is no dispatch
+                * source, we just close the file.
+                */
+               asldebug("close fd %d\n", f_data->fd);
+               close(f_data->fd);
+       }
+       else
+       {
+               /*
+                * The monitor cancel handler will close the file.
+                */
+               dispatch_source_cancel(f_data->monitor);
+               dispatch_release(f_data->monitor);
+               f_data->monitor = NULL;
+       }
+
+       f_data->fd = -1;
+}
+
+static int
+_text_file_open(asl_out_rule_t *r)
+{
+       asl_action_file_data_t *f_data = (asl_action_file_data_t *)r->dst->private;
+
+       if (f_data->fd < 0)
+       {
+               f_data->fd = _act_file_create_open(r->dst);
+               if (f_data->fd < 0)
+               {
+                       /*
+                        * lazy path creation: create path and retry
+                        * _act_file_create_open does not create the path
+                        * so we do it here.
+                        */
+                       int status = asl_out_mkpath(global.asl_out_module, r);
+                       if (status != 0) return -1;
+
+                       f_data->fd = _act_file_create_open(r->dst);
+               }
+
+               if (f_data->fd < 0) return -1;
+
+               f_data->monitor = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE, f_data->fd, DISPATCH_VNODE_DELETE, asl_action_queue);
+               if (f_data->monitor != NULL)
+               {
+                       int ffd = f_data->fd;
+
+                       dispatch_source_set_event_handler(f_data->monitor, ^{
+                               asldebug("dispatch_source DISPATCH_VNODE_DELETE fd %d\n", ffd);
+                               _act_dst_close(r, DST_CLOSE_DELETED);
+                       });
+
+                       dispatch_source_set_cancel_handler(f_data->monitor, ^{
+                               asldebug("cancel/close file fd %d\n", ffd);
+                               close(ffd);
+                       });
+
+                       dispatch_resume(f_data->monitor);
+               }
+       }
+
+       return 0;
+}
+
+static int
+_act_dst_open(asl_out_rule_t *r, const time_t *tick, uint64_t xid)
+{
+       if (r == NULL) return -1;
+       if (r->dst == NULL) return -1;
+       if (r->dst->private == NULL) return -1;
+
+       if (r->action == ACTION_ASL_DIR)
+       {
+               if (_asl_dir_today_open(r, tick) != 0)
                {
-                       asldebug("CrashMover inactive / sentinel deleted: resuming crashlog queue\n");
-                       dispatch_resume(crashlog_queue);
-                       crashmover_state = 0;
+                       asldebug("_act_dst_open:_asl_dir_today_open %s FAILED!\n", r->dst->path);
+                       return -1;
                }
 
-               if (crashlog_sentinel_src != NULL)
+               if (_asl_dir_storedata_open(r, xid) != 0)
                {
-                       dispatch_source_cancel(crashlog_sentinel_src);
-                       dispatch_release(crashlog_sentinel_src);
+                       asldebug("_act_dst_open:_asl_dir_storedata_open %s FAILED!  Closing today file\n", r->dst->path);
+                       _asl_dir_today_close(r);
+                       return -1;
                }
 
-               crashlog_sentinel_src = NULL;
-
-               close(crashlog_sentinel_fd);
-               crashlog_sentinel_fd = -1;
-               _crashlog_sentinel_init();
-       });
-
-       dispatch_resume(crashlog_sentinel_src);
-       asldebug("Created CrashLog Sentinel: %s\n", path);
-}
-
-static void
-_crashlog_queue_check(void)
-{
-       /*
-        * Check whether the crashlog queue has been suspended for too long.
-        * We allow the crashlog quque to be suspended for 60 seconds.
-        * After that, we start logging again.  This prevents syslogd from
-        * filling memory due to a suspended queue.  CrashMover really shoud
-        * take no more than a second or two to finish.
-        */
-       if (crashmover_state == 0) return;
-       if ((time(NULL) - crashmover_state) <= 60) return;
-
-       asldebug("CrashMover timeout: resuming crashlog queue\n");
-       dispatch_resume(crashlog_queue);
-       crashmover_state = 0;
+               return 0;
+       }
 
-       /*
-        * crashlog_sentinel_src should never be NULL, but if
-        * _crashlog_sentinel_init failed for some strange reason,
-        * it will be NULL here.
-        */
-       if (crashlog_sentinel_src != NULL)
+       if (r->action == ACTION_ASL_FILE)
        {
-               dispatch_source_cancel(crashlog_sentinel_src);
-               dispatch_release(crashlog_sentinel_src);
+               return _asl_file_open(r);
        }
 
-       crashlog_sentinel_src = NULL;
+       if (r->action == ACTION_FILE)
+       {
+               return _text_file_open(r);
+       }
 
-       close(crashlog_sentinel_fd);
-       crashlog_sentinel_fd = -1;
-       _crashlog_sentinel_init();
+       return -1;
 }
-#endif
 
 static void
-_act_dst_close(asl_out_rule_t *r)
+_act_dst_close(asl_out_rule_t *r, int why)
 {
        if (r == NULL) return;
        if (r->dst == NULL) return;
        if (r->dst->private == NULL) return;
 
-       if ((r->action == ACTION_ASL_DIR) || (r->action == ACTION_ASL_FILE))
+       if (r->action == ACTION_ASL_DIR)
        {
-               asl_action_store_data_t *sdata = (asl_action_store_data_t *)r->dst->private;
-               if (sdata->store != NULL) asl_file_close(sdata->store);
-               sdata->store = NULL;
-
-               if (sdata->storedata != NULL) fclose(sdata->storedata);
-               sdata->storedata = NULL;
-
-               if (sdata->monitor != NULL)
-               {
-                       dispatch_source_cancel(sdata->monitor);
-                       dispatch_release(sdata->monitor);
-                       sdata->monitor = NULL;
-               }
+               asldebug("_act_dst_close: %s ASL DIR %s\n", why_str[why], r->dst->path);
+               if (why != DST_CLOSE_CHECKPOINT) _asl_dir_storedata_close(r);
+               _asl_dir_today_close(r);
+       }
+       else if (r->action == ACTION_ASL_FILE)
+       {
+               asldebug("_act_dst_close: %s ASL FILE %s\n", why_str[why], (r->dst->fname == NULL) ? r->dst->path : r->dst->fname);
+               _asl_file_close(r);
        }
        else if (r->action == ACTION_FILE)
        {
-               asl_action_file_data_t *fdata = (asl_action_file_data_t *)r->dst->private;
-               if (fdata->fd >= 0) close(fdata->fd);
-               fdata->fd = -1;
-
-               if (fdata->monitor != NULL)
-               {
-                       dispatch_source_cancel(fdata->monitor);
-                       dispatch_release(fdata->monitor);
-                       fdata->monitor = NULL;
-               }
+               asldebug("_act_dst_close: %s FILE %s\n", why_str[why], (r->dst->fname == NULL) ? r->dst->path : r->dst->fname);
+               _text_file_close(r);
        }
 }
 
@@ -389,55 +983,35 @@ static uint32_t
 _act_store_file_setup(asl_out_module_t *m, asl_out_rule_t *r)
 {
        uint32_t status;
-       asl_action_store_data_t *sdata;
-       char dstpath[MAXPATHLEN];
+       asl_action_asl_file_data_t *af_data;
 
        if (r == NULL) return ASL_STATUS_INVALID_STORE;
        if (r->dst == NULL) return ASL_STATUS_INVALID_STORE;
        if (r->dst->private == NULL) return ASL_STATUS_INVALID_STORE;
 
-       sdata = (asl_action_store_data_t *)r->dst->private;
-       if (sdata->store == NULL)
+       af_data = (asl_action_asl_file_data_t *)r->dst->private;
+       if (af_data->aslfile != NULL)
        {
-               /* create path if necessary */
-               asl_out_mkpath(r);
-
-               int fd = asl_out_dst_file_create_open(r->dst);
-               if (fd < 0)
-               {
-                       asldebug("_act_store_file_setup: asl_out_dst_file_create_open failed %d %s\n", errno, strerror(errno));
-                       return ASL_STATUS_WRITE_FAILED;
-               }
-               close(fd);
-
-               asl_make_dst_filename(r->dst, dstpath, sizeof(dstpath));
-               status = asl_file_open_write(dstpath, 0, -1, -1, &(sdata->store));
-               if (status != ASL_STATUS_OK) return status;
-
-               sdata->monitor = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE, fileno(sdata->store->store), DISPATCH_VNODE_DELETE, asl_action_queue);
-               if (sdata->monitor != NULL)
-               {
-                       dispatch_source_set_event_handler(sdata->monitor, ^{ _act_dst_close(r); });
-                       dispatch_resume(sdata->monitor);
-               }
+               af_data->next_id++;
+               return ASL_STATUS_OK;
+       }
 
-               status = asl_file_read_set_position(sdata->store, ASL_FILE_POSITION_LAST);
-               if (status != ASL_STATUS_OK)
-               {
-                       asldebug("_act_store_file_setup: asl_file_read_set_position failed %d %s\n", status, asl_core_error(status));
-                       return status;
-               }
+       if (_act_dst_open(r, NULL, 0) != 0) return ASL_STATUS_WRITE_FAILED;
 
-               sdata->next_id = sdata->store->cursor_xid + 1;
-               if (fseek(sdata->store->store, 0, SEEK_END) != 0)
-               {
-                       asldebug("_act_store_file_setup: fseek failed %d %s\n", errno, strerror(errno));
-                       return ASL_STATUS_WRITE_FAILED;
-               }
+       status = asl_file_read_set_position(af_data->aslfile, ASL_FILE_POSITION_LAST);
+       if (status != ASL_STATUS_OK)
+       {
+               asldebug("_act_store_file_setup: asl_file_read_set_position failed %d %s\n", status, asl_core_error(status));
+               _act_dst_close(r, DST_CLOSE_ERROR);
+               return status;
        }
-       else
+
+       af_data->next_id = af_data->aslfile->cursor_xid + 1;
+       if (fseek(af_data->aslfile->store, 0, SEEK_END) != 0)
        {
-               sdata->next_id++;
+               asldebug("_act_store_file_setup: fseek failed %d %s\n", errno, strerror(errno));
+               _act_dst_close(r, DST_CLOSE_ERROR);
+               return ASL_STATUS_WRITE_FAILED;
        }
 
        return ASL_STATUS_OK;
@@ -446,224 +1020,102 @@ _act_store_file_setup(asl_out_module_t *m, asl_out_rule_t *r)
 /*
  * _act_store_dir_setup
  *
- * Creates store directory if it does not exist
- * Creates StoreData file if it does not exist
+ * Opens StoreData file and today's file
  * Reads ASL Message ID from StoreData file
  * Writes ASL Message ID + 1 to StoreData file
- * Opens current day file (e.g. "/foo/bar/2012.04.06.asl")
  */
 static uint32_t
 _act_store_dir_setup(asl_out_module_t *m, asl_out_rule_t *r, time_t tick)
 {
-       struct tm ctm;
-       char *path;
-       struct stat sb;
        uint64_t xid;
        int status;
-       mode_t mask;
-       asl_action_store_data_t *sdata;
+       asl_action_asl_store_data_t *as_data;
 
        if (r == NULL) return ASL_STATUS_INVALID_STORE;
        if (r->dst == NULL) return ASL_STATUS_INVALID_STORE;
        if (r->dst->private == NULL) return ASL_STATUS_INVALID_STORE;
        if (r->dst->path == NULL) return ASL_STATUS_INVALID_STORE;
 
-       sdata = (asl_action_store_data_t *)r->dst->private;
-
-       /* get / set message id from StoreData file */
-       xid = 0;
+       as_data = (asl_action_asl_store_data_t *)r->dst->private;
 
-       if (sdata->storedata == NULL)
+       if (_act_dst_open(r, NULL, as_data->next_id) != 0)
        {
-               memset(&sb, 0, sizeof(struct stat));
-               status = stat(r->dst->path, &sb);
-               if (status == 0)
-               {
-                       /* must be a directory */
-                       if (!S_ISDIR(sb.st_mode)) return ASL_STATUS_INVALID_STORE;
-               }
-               else if (errno == ENOENT)
-               {
-                       /* doesn't exist - create it */
-                       mask = umask(S_IWGRP | S_IWOTH);
-                       status = mkpath_np(r->dst->path, 0755);
-                       if (status == 0) status = chmod(r->dst->path, r->dst->mode);
-                       umask(mask);
-
-                       if (status != 0) return ASL_STATUS_WRITE_FAILED;
-#if !TARGET_IPHONE_SIMULATOR
-                       if (chown(r->dst->path, r->dst->uid[0], r->dst->gid[0]) != 0) return ASL_STATUS_WRITE_FAILED;
-#endif
-               }
-               else
-               {
-                       /* Unexpected stat error */
-                       return ASL_STATUS_FAILED;
-               }
-
-               path = NULL;
-               asprintf(&path, "%s/%s", r->dst->path, FILE_ASL_STORE_DATA);
-               if (path == NULL) return ASL_STATUS_NO_MEMORY;
-
-               memset(&sb, 0, sizeof(struct stat));
-               status = stat(path, &sb);
-               if (status == 0)
-               {
-                       /* StoreData exists: open and read last xid */
-                       sdata->storedata = fopen(path, "r+");
-                       if (sdata->storedata == NULL)
-                       {
-                               free(path);
-                               return ASL_STATUS_FAILED;
-                       }
-
-                       if (fread(&xid, sizeof(uint64_t), 1, sdata->storedata) != 1)
-                       {
-                               free(path);
-                               fclose(sdata->storedata);
-                               sdata->storedata = NULL;
-                               return ASL_STATUS_READ_FAILED;
-                       }
-               }
-               else if (errno == ENOENT)
-               {
-                       /* StoreData does not exist: create it */
-                       sdata->storedata = fopen(path, "w");
-                       if (sdata->storedata == NULL)
-                       {
-                               free(path);
-                               return ASL_STATUS_FAILED;
-                       }
-
-#if !TARGET_IPHONE_SIMULATOR
-                       if (chown(path, r->dst->uid[0], r->dst->gid[0]) != 0)
-                       {
-                               free(path);
-                               return ASL_STATUS_WRITE_FAILED;
-                       }
-#endif
-               }
-               else
-               {
-                       /* Unexpected stat error */
-                       free(path);
-                       return ASL_STATUS_FAILED;
-               }
-
-               sdata->monitor = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE, fileno(sdata->storedata), DISPATCH_VNODE_DELETE, asl_action_queue);
-               if (sdata->monitor != NULL)
-               {
-                       dispatch_source_set_event_handler(sdata->monitor, ^{ _act_dst_close(r); });
-                       dispatch_resume(sdata->monitor);
-               }
-
-               free(path);
+               asldebug("_act_store_dir_setup: _act_dst_open %s failed\n", r->dst->path);
+               return ASL_STATUS_WRITE_FAILED;
        }
-       else
+
+       /* get / set message id from StoreData file */
+       xid = 0;
+       rewind(as_data->storedata);
+       if (fread(&xid, sizeof(uint64_t), 1, as_data->storedata) != 1)
        {
-               rewind(sdata->storedata);
-               if (fread(&xid, sizeof(uint64_t), 1, sdata->storedata) != 1)
-               {
-                       fclose(sdata->storedata);
-                       sdata->storedata = NULL;
-                       return ASL_STATUS_READ_FAILED;
-               }
+               asldebug("_act_store_dir_setup: storedata read failed %d %s\n", errno, strerror(errno));
+               _act_dst_close(r, DST_CLOSE_ERROR);
+               return ASL_STATUS_READ_FAILED;
        }
 
        xid = asl_core_ntohq(xid);
        xid++;
-       sdata->next_id = xid;
+       as_data->next_id = xid;
 
        xid = asl_core_htonq(xid);
-       rewind(sdata->storedata);
-       status = fwrite(&xid, sizeof(uint64_t), 1, sdata->storedata);
+       rewind(as_data->storedata);
+       status = fwrite(&xid, sizeof(uint64_t), 1, as_data->storedata);
        if (status != 1)
        {
-               fclose(sdata->storedata);
-               sdata->storedata = NULL;
+               asldebug("_act_store_dir_setup: storedata write failed %d %s\n", errno, strerror(errno));
+               _act_dst_close(r, DST_CLOSE_ERROR);
                return ASL_STATUS_WRITE_FAILED;
        }
 
-       memset(&ctm, 0, sizeof(struct tm));
+       fflush(as_data->storedata);
 
-       if (localtime_r((const time_t *)&tick, &ctm) == NULL) return ASL_STATUS_FAILED;
-       if ((sdata->store != NULL) && (sdata->p_year == ctm.tm_year) && (sdata->p_month == ctm.tm_mon) && (sdata->p_day == ctm.tm_mday))
+       if (fseek(as_data->aslfile->store, 0, SEEK_END) != 0)
        {
-               return ASL_STATUS_OK;
-       }
-
-       if (sdata->store != NULL) asl_file_close(sdata->store);
-       sdata->store = NULL;
-       free(r->dst->fname);
-       r->dst->fname = NULL;
-
-    r->dst->stamp = tick;
-
-       sdata->p_year = ctm.tm_year;
-       sdata->p_month = ctm.tm_mon;
-       sdata->p_day = ctm.tm_mday;
-
-       asprintf(&(r->dst->fname), "%s/%d.%02d.%02d.asl", r->dst->path, ctm.tm_year + 1900, ctm.tm_mon + 1, ctm.tm_mday);
-       if (r->dst->fname == NULL) return ASL_STATUS_NO_MEMORY;
-       mask = umask(0);
-
-       status = ASL_STATUS_OK;
-       if (sdata->store == NULL) {
-#if TARGET_IPHONE_SIMULATOR
-               uid_t uid = -1;
-               gid_t gid = -1;
-#else
-               uid_t uid = r->dst->uid[0];
-               gid_t gid = r->dst->gid[0];
-#endif
-               status = asl_file_open_write(r->dst->fname, (r->dst->mode & 0666), uid, gid, &(sdata->store));
+               asldebug("_act_store_dir_setup: aslfile fseek failed %d %s\n", errno, strerror(errno));
+               _act_dst_close(r, DST_CLOSE_ERROR);
+               return ASL_STATUS_FAILED;
        }
-       umask(mask);
-
-       if (status != ASL_STATUS_OK) return status;
-
-       if (fseek(sdata->store->store, 0, SEEK_END) != 0) return ASL_STATUS_FAILED;
 
        return ASL_STATUS_OK;
 }
 
 static void
-_asl_action_store_data_free(asl_action_store_data_t *sdata)
+_asl_action_asl_store_data_free(asl_action_asl_store_data_t *as_data)
 {
-       if (sdata == NULL) return;
-
-       if (sdata->store != NULL) asl_file_close(sdata->store);
-       sdata->store = NULL;
-
-       if (sdata->storedata != NULL) fclose(sdata->storedata);
-       sdata->storedata = NULL;
+       if (as_data == NULL) return;
+       free(as_data);
+}
 
-       free(sdata);
+static void
+_asl_action_asl_file_data_free(asl_action_asl_file_data_t *af_data)
+{
+       if (af_data == NULL) return;
+       free(af_data);
 }
 
 static void
-_asl_action_file_data_free(asl_action_file_data_t *fdata)
+_asl_action_file_data_free(asl_action_file_data_t *f_data)
 {
-       if (fdata == NULL) return;
+       if (f_data == NULL) return;
 
-       if (fdata->dup_timer != NULL)
+       if (f_data->dup_timer != NULL)
        {
-               if (fdata->last_count == 0)
+               if (f_data->last_count == 0)
                {
                        /*
                         * The timer exists, but last_count is zero, so the timer is suspended.
                         * Sources must not be released in when suspended.
                         * So we resume it so that we can release it.
                         */
-                       dispatch_resume(fdata->dup_timer);
+                       dispatch_resume(f_data->dup_timer);
                }
 
-               dispatch_release(fdata->dup_timer);
+               dispatch_release(f_data->dup_timer);
        }
 
-       free(fdata->last_msg);
-       if (fdata->fd >= 0) close(fdata->fd);
-       free(fdata);
+       free(f_data->last_msg);
+       free(f_data);
 }
 
 static void
@@ -689,18 +1141,22 @@ _asl_action_save_failed(const char *where, asl_out_module_t *m, asl_out_rule_t *
                internal_log_message(str);
                free(str);
 
-               if (r->action == ACTION_FILE) _asl_action_file_data_free((asl_action_file_data_t *)r->dst->private);
-               else _asl_action_store_data_free((asl_action_store_data_t *)r->dst->private);
+               if (r->action == ACTION_ASL_DIR) _asl_action_asl_store_data_free((asl_action_asl_store_data_t *)r->dst->private);
+               else if (r->action == ACTION_ASL_FILE) _asl_action_asl_file_data_free((asl_action_asl_file_data_t *)r->dst->private);
+               else if (r->action == ACTION_FILE) _asl_action_file_data_free((asl_action_file_data_t *)r->dst->private);
 
                r->dst->private = NULL;
                r->action = ACTION_NONE;
        }
 }
 
+/*
+ * Save a message in an ASL file.
+ */
 static int
-_act_store_file(asl_out_module_t *m, asl_out_rule_t *r, aslmsg msg)
+_act_store_file(asl_out_module_t *m, asl_out_rule_t *r, asl_msg_t *msg)
 {
-       asl_action_store_data_t *sdata;
+       asl_action_asl_file_data_t *af_data;
        uint32_t status;
        uint64_t mid;
 
@@ -708,29 +1164,25 @@ _act_store_file(asl_out_module_t *m, asl_out_rule_t *r, aslmsg msg)
        if (r->dst == NULL) return ACTION_STATUS_ERROR;
        if (r->dst->private == NULL) return ACTION_STATUS_ERROR;
 
-       sdata = (asl_action_store_data_t *)r->dst->private;
-
-       /* check dst for file_max & etc */
-       if (r->dst->flags & MODULE_FLAG_ROTATE)
-       {
-               if (asl_out_dst_checkpoint(r->dst, CHECKPOINT_TEST) != 0)
-               {
-                       _act_dst_close(r);
-                       asl_trigger_aslmanager();
-               }
-       }
+       af_data = (asl_action_asl_file_data_t *)r->dst->private;
+       if (af_data->pending > 0) af_data->pending--;
 
        status = _act_store_file_setup(m, r);
        if (status == ASL_STATUS_OK)
        {
-               sdata->last_time = time(NULL);
+               af_data->last_time = time(NULL);
 
                r->dst->fails = 0;
-               mid = sdata->next_id;
+               mid = af_data->next_id;
 
                /* save message to file and update dst size */
-               status = asl_file_save(sdata->store, msg, &mid);
-               if (status == ASL_STATUS_OK) r->dst->size = sdata->store->file_size;
+               status = asl_file_save(af_data->aslfile, msg, &mid);
+               if (status == ASL_STATUS_OK)
+               {
+                       r->dst->size = af_data->aslfile->file_size;
+
+                       if (_act_checkpoint(r, CHECKPOINT_TEST) == 1) trigger_aslmanager();
+               }
        }
 
        if (status != ASL_STATUS_OK)
@@ -742,10 +1194,13 @@ _act_store_file(asl_out_module_t *m, asl_out_rule_t *r, aslmsg msg)
        return ACTION_STATUS_OK;
 }
 
+/*
+ * Save a message in an ASL data store.
+ */
 static int
-_act_store_dir(asl_out_module_t *m, asl_out_rule_t *r, aslmsg msg)
+_act_store_dir(asl_out_module_t *m, asl_out_rule_t *r, asl_msg_t *msg)
 {
-       asl_action_store_data_t *sdata;
+       asl_action_asl_store_data_t *as_data;
        uint32_t status;
        uint64_t mid;
        const char *val;
@@ -755,29 +1210,30 @@ _act_store_dir(asl_out_module_t *m, asl_out_rule_t *r, aslmsg msg)
        if (r->dst == NULL) return ACTION_STATUS_ERROR;
        if (r->dst->private == NULL) return ACTION_STATUS_ERROR;
 
-       sdata = (asl_action_store_data_t *)r->dst->private;
+       as_data = (asl_action_asl_store_data_t *)r->dst->private;
+       if (as_data->pending > 0) as_data->pending--;
 
-       val = asl_get(msg, ASL_KEY_TIME);
+       val = asl_msg_get_val_for_key(msg, ASL_KEY_TIME);
        if (val == NULL) return ACTION_STATUS_ERROR;
 
-       /* check dst for file_max & etc */
-       if (asl_out_dst_checkpoint(r->dst, CHECKPOINT_TEST) != 0)
-       {
-               _act_dst_close(r);
-               asl_trigger_aslmanager();
-       }
-
        tick = atol(val);
 
        status = _act_store_dir_setup(m, r, tick);
        if (status == ASL_STATUS_OK)
        {
-               sdata->last_time = time(NULL);
+               as_data->last_time = time(NULL);
 
                r->dst->fails = 0;
-               mid = sdata->next_id;
-               status = asl_file_save(sdata->store, msg, &mid);
-               if (status == ASL_STATUS_OK) r->dst->size = sdata->store->file_size;
+               mid = as_data->next_id;
+               status = asl_file_save(as_data->aslfile, msg, &mid);
+               if (status == ASL_STATUS_OK) r->dst->size = as_data->aslfile->file_size;
+               else asldebug("_act_store_dir asl_file_save FAILED %s\n", asl_core_error(status));
+               //TODO: checkpoint here?
+               /*
+                * Currently, the checkpoint is in _asl_dir_today_open().
+                * Moving it here would be in keeping with the way that
+                * _act_store_file() and _act_file_final() do checkpoints.
+                */
        }
 
        if (status != ASL_STATUS_OK)
@@ -790,7 +1246,7 @@ _act_store_dir(asl_out_module_t *m, asl_out_rule_t *r, aslmsg msg)
 }
 
 static void
-_act_store_final(asl_out_module_t *m, asl_out_rule_t *r, aslmsg msg)
+_act_store_final(asl_out_module_t *m, asl_out_rule_t *r, asl_msg_t *msg)
 {
        if (r->action == ACTION_ASL_DIR) _act_store_dir(m, r, msg);
        else _act_store_file(m, r, msg);
@@ -801,25 +1257,38 @@ _act_store_final(asl_out_module_t *m, asl_out_rule_t *r, aslmsg msg)
  * or to an ASL directory (ACTION_ASL_DIR).
  */
 static void
-_act_store(asl_out_module_t *m, asl_out_rule_t *r, aslmsg msg)
+_act_store(asl_out_module_t *m, asl_out_rule_t *r, asl_msg_t *msg)
 {
        if (r == NULL) return;
+       if (r->dst == NULL) return;
        if (msg == NULL) return;
        if (m == NULL) return;
        if ((m->flags & MODULE_FLAG_ENABLED) == 0) return;
-       if (r->dst == NULL) return;
 
        if (r->dst->flags & MODULE_FLAG_HAS_LOGGED) return;
+
        r->dst->flags |= MODULE_FLAG_HAS_LOGGED;
+       if (r->action == ACTION_ASL_DIR)
+       {
+               asl_action_asl_store_data_t *as_data = (asl_action_asl_store_data_t *)r->dst->private;
+               if (as_data != NULL) as_data->pending++;
+       }
+       else if (r->action == ACTION_ASL_FILE)
+       {
+               asl_action_asl_file_data_t *af_data = (asl_action_asl_file_data_t *)r->dst->private;
+               if (af_data != NULL) af_data->pending++;
+       }
 
 #if TARGET_OS_EMBEDDED
        if (r->dst->flags & MODULE_FLAG_CRASHLOG)
        {
                _crashlog_queue_check();
-               asl_msg_retain((asl_msg_t *)msg);
+               asl_msg_retain(msg);
                dispatch_async(crashlog_queue, ^{
-                       _act_store_final(m, r, msg);
-                       asl_msg_release((asl_msg_t *)msg);
+                       dispatch_async(asl_action_queue, ^{
+                               _act_store_final(m, r, msg);
+                               asl_msg_release(msg);
+                       });
                });
                return;
        }
@@ -831,7 +1300,7 @@ _act_store(asl_out_module_t *m, asl_out_rule_t *r, aslmsg msg)
 static int
 _send_repeat_msg(asl_out_rule_t *r)
 {
-       asl_action_file_data_t *fdata;
+       asl_action_file_data_t *f_data;
        char vt[32], *msg;
        int len, status;
        time_t now = time(NULL);
@@ -840,79 +1309,41 @@ _send_repeat_msg(asl_out_rule_t *r)
        if (r->dst == NULL) return -1;
        if (r->dst->private == NULL) return -1;
 
-       fdata = (asl_action_file_data_t *)r->dst->private;
+       f_data = (asl_action_file_data_t *)r->dst->private;
 
-       free(fdata->last_msg);
-       fdata->last_msg = NULL;
+       free(f_data->last_msg);
+       f_data->last_msg = NULL;
 
-       if (fdata->last_count == 0) return 0;
+       if (f_data->last_count == 0) return 0;
 
        /* stop the timer */
-       dispatch_suspend(fdata->dup_timer);
+       dispatch_suspend(f_data->dup_timer);
 
        memset(vt, 0, sizeof(vt));
        ctime_r(&now, vt);
        vt[19] = '\0';
 
        msg = NULL;
-       asprintf(&msg, "%s --- last message repeated %u time%s ---\n", vt + 4, fdata->last_count, (fdata->last_count == 1) ? "" : "s");
-       fdata->last_count = 0;
-       fdata->last_time = now;
+       asprintf(&msg, "%s --- last message repeated %u time%s ---\n", vt + 4, f_data->last_count, (f_data->last_count == 1) ? "" : "s");
+       f_data->last_count = 0;
+       f_data->last_time = now;
        if (msg == NULL) return -1;
 
-       if (fdata->fd < 0) fdata->fd = asl_out_dst_file_create_open(r->dst);
+       if (f_data->fd < 0) f_data->fd = _act_file_create_open(r->dst);
 
        len = strlen(msg);
-       status = write(fdata->fd, msg, len);
+       status = write(f_data->fd, msg, len);
        free(msg);
 
        if ((status < 0) || (status < len))
        {
-               asldebug("%s: error writing repeat message (%s): %s\n", MY_ID, r->dst->path, strerror(errno));
+               asldebug("%s: error writing repeat message (%s): %s\n", MY_ID, r->dst->fname, strerror(errno));
                return -1;
        }
 
        return 0;
 }
 
-static int
-_act_file_open(asl_out_module_t *m, asl_out_rule_t *r)
-{
-       asl_action_file_data_t *fdata;
-
-       if (r == NULL) return -1;
-       if (r->dst == NULL) return -1;
-       if (r->dst->private == NULL) return -1;
-
-       fdata = (asl_action_file_data_t *)r->dst->private;
-       if (fdata->fd < 0)
-       {
-               fdata->fd = asl_out_dst_file_create_open(r->dst);
-               if (fdata->fd < 0)
-               {
-                       /*
-                        * lazy path creation: create path and retry
-                        * asl_out_dst_file_create_open doesn not create the path
-                        * so we do it here.
-                        */
-                       asl_out_mkpath(r);
-                       fdata->fd = asl_out_dst_file_create_open(r->dst);
-               }
-
-               if (fdata->fd >= 0)
-               {
-                       fdata->monitor = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE, fdata->fd, DISPATCH_VNODE_DELETE, asl_action_queue);
-                       if (fdata->monitor != NULL)
-                       {
-                               dispatch_source_set_event_handler(fdata->monitor, ^{ _act_dst_close(r); });
-                               dispatch_resume(fdata->monitor);
-                       }
-               }
-       }
-
-       return fdata->fd;
-}
-
 static void
 _start_cycling()
 {
@@ -984,19 +1415,19 @@ _act_file_checkpoint(asl_out_module_t *m, const char *path, uint32_t force)
                                        {
                                                if (r->dst->flags & MODULE_FLAG_CRASHLOG)
                                                {
-                                                       if (asl_out_dst_checkpoint(r->dst, CHECKPOINT_FORCE) > 0)
+                                                       if (_act_checkpoint(r, CHECKPOINT_FORCE) > 0)
                                                        {
                                                                did_checkpoint = 1;
-                                                               _act_dst_close(r);
+                                                               _act_dst_close(r, DST_CLOSE_CHECKPOINT);
                                                        }
                                                }
                                        }
                                        else
                                        {
-                                               if (asl_out_dst_checkpoint(r->dst, force) > 0)
+                                               if (_act_checkpoint(r, force) > 0)
                                                {
                                                        did_checkpoint = 1;
-                                                       _act_dst_close(r);
+                                                       _act_dst_close(r, DST_CLOSE_CHECKPOINT);
                                                }
                                        }
                                }
@@ -1018,20 +1449,28 @@ _act_file_checkpoint_all(uint32_t force)
                if (_act_file_checkpoint(m, NULL, force) > 0) did_checkpoint = 1;
        }
 
-       asl_trigger_aslmanager();
+       trigger_aslmanager();
 
        return did_checkpoint;
 }
 
+/*
+ * Save a message in a plain text file.
+ */
 static void
-_act_file_final(asl_out_module_t *m, asl_out_rule_t *r, aslmsg msg)
+_act_file_final(asl_out_module_t *m, asl_out_rule_t *r, asl_msg_t *msg)
 {
-       asl_action_file_data_t *fdata;
+       asl_action_file_data_t *f_data;
        int is_dup;
        uint32_t len, msg_hash = 0;
        char *str;
        time_t now;
 
+       if (r->dst->private == NULL) return;
+
+       f_data = (asl_action_file_data_t *)r->dst->private;
+       if (f_data->pending > 0) f_data->pending--;
+
        /*
         * If print format is std, bsd, or msg, then skip messages with
         * no ASL_KEY_MSG, or without a value for it.
@@ -1039,51 +1478,49 @@ _act_file_final(asl_out_module_t *m, asl_out_rule_t *r, aslmsg msg)
        if (r->dst->flags & MODULE_FLAG_STD_BSD_MSG)
        {
                const char *msgval = NULL;
-               if (asl_msg_lookup((asl_msg_t *)msg, ASL_KEY_MSG, &msgval, NULL) != 0) return;
+               if (asl_msg_lookup(msg, ASL_KEY_MSG, &msgval, NULL) != 0) return;
                if (msgval == NULL) return;
        }
 
-       fdata = (asl_action_file_data_t *)r->dst->private;
-
        now = time(NULL);
 
        is_dup = 0;
 
-       str = asl_format_message((asl_msg_t *)msg, r->dst->fmt, r->dst->tfmt, ASL_ENCODE_SAFE, &len);
+       str = asl_format_message(msg, r->dst->fmt, r->dst->tfmt, ASL_ENCODE_SAFE, &len);
 
        if (r->dst->flags & MODULE_FLAG_COALESCE)
        {
-               if (fdata->dup_timer == NULL)
+               if (f_data->dup_timer == NULL)
                {
                        /* create a timer to flush dups on this file */
-                       fdata->dup_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, asl_action_queue);
-                       dispatch_source_set_event_handler(fdata->dup_timer, ^{ _send_repeat_msg(r); });
+                       f_data->dup_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, asl_action_queue);
+                       dispatch_source_set_event_handler(f_data->dup_timer, ^{ _send_repeat_msg(r); });
                }
 
-               if ((global.bsd_max_dup_time > 0) && (str != NULL) && (fdata->last_msg != NULL))
+               if ((global.bsd_max_dup_time > 0) && (str != NULL) && (f_data->last_msg != NULL))
                {
                        msg_hash = asl_core_string_hash(str + 16, len - 16);
-                       if ((fdata->last_hash == msg_hash) && (!strcmp(fdata->last_msg, str + 16)))
+                       if ((f_data->last_hash == msg_hash) && (!strcmp(f_data->last_msg, str + 16)))
                        {
-                               if ((now - fdata->last_time) < global.bsd_max_dup_time) is_dup = 1;
+                               if ((now - f_data->last_time) < global.bsd_max_dup_time) is_dup = 1;
                        }
                }
        }
 
        if (is_dup == 1)
        {
-               if (fdata->last_count == 0)
+               if (f_data->last_count == 0)
                {
                        /* start the timer */
-                       dispatch_source_set_timer(fdata->dup_timer, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * global.bsd_max_dup_time), DISPATCH_TIME_FOREVER, 0);
-                       dispatch_resume(fdata->dup_timer);
+                       dispatch_source_set_timer(f_data->dup_timer, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * global.bsd_max_dup_time), DISPATCH_TIME_FOREVER, 0);
+                       dispatch_resume(f_data->dup_timer);
                }
 
-               fdata->last_count++;
+               f_data->last_count++;
        }
        else
        {
-               if (_act_file_open(m, r) < 0)
+               if (_act_dst_open(r, NULL, 0) != 0)
                {
                        _asl_action_save_failed("_act_file", m, r, ASL_STATUS_FAILED);
                        free(str);
@@ -1095,54 +1532,34 @@ _act_file_final(asl_out_module_t *m, asl_out_rule_t *r, aslmsg msg)
                }
 
                /*
-                * The current message is not a duplicate.  If fdata->last_count > 0
+                * The current message is not a duplicate.  If f_data->last_count > 0
                 * we need to write a "last message repeated N times" log entry.
                 * _send_repeat_msg will free last_msg and do nothing if
                 * last_count == 0, but we test and free here to avoid a function call.
                 */
-               if (fdata->last_count > 0)
+               if (f_data->last_count > 0)
                {
                        _send_repeat_msg(r);
                }
                else
                {
-                       free(fdata->last_msg);
-                       fdata->last_msg = NULL;
-               }
-
-               /* check dst for file_max & etc */
-               if (r->dst->flags & MODULE_FLAG_ROTATE)
-               {
-                       int ckpt = asl_out_dst_checkpoint(r->dst, CHECKPOINT_TEST);
-                       if (ckpt != 0)
-                       {
-                               _act_dst_close(r);
-                               asl_trigger_aslmanager();
-
-                               if (_act_file_open(m, r) < 0)
-                               {
-                                       _asl_action_save_failed("_act_file", m, r, ASL_STATUS_FAILED);
-                                       free(str);
-                                       return;
-                               }
-                               else
-                               {
-                                       r->dst->fails = 0;
-                               }
-                       }
+                       free(f_data->last_msg);
+                       f_data->last_msg = NULL;
                }
 
-               if (str != NULL) fdata->last_msg = strdup(str + 16);
+               if (str != NULL) f_data->last_msg = strdup(str + 16);
 
-               fdata->last_hash = msg_hash;
-               fdata->last_count = 0;
-               fdata->last_time = now;
+               f_data->last_hash = msg_hash;
+               f_data->last_count = 0;
+               f_data->last_time = now;
 
                if ((str != NULL) && (len > 1))
                {
                        /* write line to file and update dst size */
-                       size_t bytes = write(fdata->fd, str, len - 1);
+                       size_t bytes = write(f_data->fd, str, len - 1);
                        if (bytes > 0) r->dst->size += bytes;
+
+                       if (_act_checkpoint(r, CHECKPOINT_TEST) == 1) trigger_aslmanager();
                }
        }
 
@@ -1150,8 +1567,10 @@ _act_file_final(asl_out_module_t *m, asl_out_rule_t *r, aslmsg msg)
 }
 
 static void
-_act_file(asl_out_module_t *m, asl_out_rule_t *r, aslmsg msg)
+_act_file(asl_out_module_t *m, asl_out_rule_t *r, asl_msg_t *msg)
 {
+       asl_action_file_data_t *f_data;
+
        if (r == NULL) return;
        if (msg == NULL) return;
        if (m == NULL) return;
@@ -1162,15 +1581,19 @@ _act_file(asl_out_module_t *m, asl_out_rule_t *r, aslmsg msg)
        if (r->dst->flags & MODULE_FLAG_HAS_LOGGED) return;
 
        r->dst->flags |= MODULE_FLAG_HAS_LOGGED;
+       f_data = (asl_action_file_data_t *)r->dst->private;
+       if (f_data != NULL) f_data->pending++;
 
 #if TARGET_OS_EMBEDDED
        if (r->dst->flags & MODULE_FLAG_CRASHLOG)
        {
                _crashlog_queue_check();
-               asl_msg_retain((asl_msg_t *)msg);
+               asl_msg_retain(msg);
                dispatch_async(crashlog_queue, ^{
-                       _act_file_final(m, r, msg);
-                       asl_msg_release((asl_msg_t *)msg);
+                       dispatch_async(asl_action_queue, ^{
+                               _act_file_final(m, r, msg);
+                               asl_msg_release(msg);
+                       });
                });
                return;
        }
@@ -1180,19 +1603,20 @@ _act_file(asl_out_module_t *m, asl_out_rule_t *r, aslmsg msg)
 }
 
 static void
-_act_forward(asl_out_module_t *m, asl_out_rule_t *r, aslmsg msg)
+_act_forward(asl_out_module_t *m, asl_out_rule_t *r, asl_msg_t *msg)
 {
        /* To do: <rdar://problem/6130747> Add a "forward" action to asl.conf */
 }
 
 static void
-_act_control(asl_out_module_t *m, asl_out_rule_t *r, aslmsg msg)
+_act_control(asl_out_module_t *m, asl_out_rule_t *r, asl_msg_t *msg)
 {
        const char *p;
 
        if (m == NULL) return;
        if (r == NULL) return;
-       p = asl_get(msg, ASL_KEY_MODULE);
+
+       p = asl_msg_get_val_for_key(msg, ASL_KEY_MODULE);
 
        if (r->options == NULL) return;
 
@@ -1211,7 +1635,7 @@ _act_control(asl_out_module_t *m, asl_out_rule_t *r, aslmsg msg)
 }
 
 static void
-_send_to_asl_store(aslmsg msg)
+_send_to_asl_store(asl_msg_t *msg)
 {
        if ((global.asl_out_module != NULL) && ((global.asl_out_module->flags & MODULE_FLAG_ENABLED) == 0)) return;
 
@@ -1222,7 +1646,7 @@ _send_to_asl_store(aslmsg msg)
 }
 
 static int
-_asl_out_process_message(asl_out_module_t *m, aslmsg msg)
+_asl_out_process_message(asl_out_module_t *m, asl_msg_t *msg)
 {
        asl_out_rule_t *r;
 
@@ -1252,16 +1676,18 @@ _asl_out_process_message(asl_out_module_t *m, aslmsg msg)
                 */
                if (r->action == ACTION_CLAIM)
                {
-                       if ((asl_msg_cmp(r->query, (asl_msg_t *)msg) != 1)) return 0;
+                       if ((asl_msg_cmp(r->query, msg) != 1)) return 0;
                }
 
-               if ((asl_msg_cmp(r->query, (asl_msg_t *)msg) == 1))
+               if ((asl_msg_cmp(r->query, msg) == 1))
                {
                        if (r->action == ACTION_NONE) continue;
                        else if (r->action == ACTION_IGNORE) return 1;
                        else if (r->action == ACTION_SKIP) return 0;
                        else if (r->action == ACTION_ASL_STORE) _send_to_asl_store(msg);
                        else if (r->action == ACTION_ACCESS) _act_access_control(m, r, msg);
+                       else if (r->action == ACTION_SET_KEY) _act_set_key(m, r, msg);
+                       else if (r->action == ACTION_UNSET_KEY) _act_unset_key(m, r, msg);
                        else if (r->action == ACTION_NOTIFY) _act_notify(m, r);
                        else if (r->action == ACTION_BROADCAST) _act_broadcast(m, r, msg);
                        else if (r->action == ACTION_FORWARD) _act_forward(m, r, msg);
@@ -1276,10 +1702,10 @@ _asl_out_process_message(asl_out_module_t *m, aslmsg msg)
 }
 
 void
-asl_out_message(aslmsg msg)
+asl_out_message(asl_msg_t *msg)
 {
        OSAtomicIncrement32(&global.asl_queue_count);
-       asl_msg_retain((asl_msg_t *)msg);
+       asl_msg_retain(msg);
 
        dispatch_async(asl_action_queue, ^{
                int ignore = 0;
@@ -1289,7 +1715,7 @@ asl_out_message(aslmsg msg)
 
                store_has_logged = false;
 
-               p = asl_get(msg, ASL_KEY_MODULE);
+               p = asl_msg_get_val_for_key(msg, ASL_KEY_MODULE);
                if (p == NULL)
                {
                        if ((action_asl_store_count == 0) || (asl_check_option(msg, ASL_OPT_STORE) == 1)) _send_to_asl_store(msg);
@@ -1315,7 +1741,10 @@ asl_out_message(aslmsg msg)
                        }
                }
 
-               asl_msg_release((asl_msg_t *)msg);
+               p = asl_msg_get_val_for_key(msg, ASL_KEY_FINAL_NOTIFICATION);
+               if (p != NULL) asl_msg_set_key_val(msg, ASL_KEY_FREE_NOTE, p);
+
+               asl_msg_release(msg);
                OSAtomicDecrement32(&global.asl_queue_count);
 
                if ((now - sweep_time) >= IDLE_CLOSE)
@@ -1334,7 +1763,7 @@ _asl_action_profile_test(asl_out_module_t *m, asl_out_rule_t *r)
        bool eval;
 
        /* ident is first message key */
-       asl_msg_fetch((asl_msg_t *)r->query, 0, &ident, NULL, NULL);
+       asl_msg_fetch(r->query, 0, &ident, NULL, NULL);
        if (ident == NULL)
        {
                r->action = ACTION_NONE;
@@ -1358,7 +1787,7 @@ _asl_action_file_test(asl_out_module_t *m, asl_out_rule_t *r)
        bool eval;
 
        /* path is first message key */
-       asl_msg_fetch((asl_msg_t *)r->query, 0, &path, NULL, NULL);
+       asl_msg_fetch(r->query, 0, &path, NULL, NULL);
        if (path == NULL)
        {
                r->action = ACTION_NONE;
@@ -1502,11 +1931,11 @@ _asl_action_post_process_rule(asl_out_module_t *m, asl_out_rule_t *r)
        }
        else if (r->action == ACTION_ASL_DIR)
        {
-               if (r->dst->private == NULL) r->dst->private = (asl_action_store_data_t *)calloc(1, sizeof(asl_action_store_data_t));
+               if (r->dst->private == NULL) r->dst->private = (asl_action_asl_store_data_t *)calloc(1, sizeof(asl_action_asl_store_data_t));
        }
        else if (r->action == ACTION_ASL_FILE)
        {
-               if (r->dst->private == NULL)r->dst->private = (asl_action_store_data_t *)calloc(1, sizeof(asl_action_store_data_t));
+               if (r->dst->private == NULL)r->dst->private = (asl_action_asl_file_data_t *)calloc(1, sizeof(asl_action_asl_file_data_t));
        }
        else if (r->action == ACTION_FILE)
        {
@@ -1615,23 +2044,6 @@ _asl_action_configure()
        if (global.asl_out_module == NULL) global.asl_out_module = asl_out_module_init();
        if (global.asl_out_module == NULL) return;
 
-       if (global.debug != 0)
-       {
-               FILE *dfp;
-               if (global.debug_file == NULL) dfp = fopen(_PATH_SYSLOGD_LOG, "a");
-               else dfp = fopen(global.debug_file, "a");
-               if (dfp != NULL)
-               {
-                       for (m = global.asl_out_module; m != NULL; m = m->next)
-                       {
-                               fprintf(dfp, "module: %s%s\n", (m->name == NULL) ? "<unknown>" : m->name, (m->flags & MODULE_FLAG_LOCAL) ? " (local)" : "");
-                               asl_out_module_print(dfp, m);
-                               fprintf(dfp, "\n");
-                       }
-                       fclose(dfp);
-               }
-       }
-
        asldebug("%s: init\n", MY_ID);
 
        action_asl_store_count = 0;
@@ -1669,10 +2081,6 @@ _asl_action_configure()
                _act_file_checkpoint_all(CHECKPOINT_TEST);
                if (checkpoint_timer == NULL) _start_cycling();
        }
-
-#if TARGET_OS_EMBEDDED
-       if (flags & MODULE_FLAG_CRASHLOG) _crashlog_sentinel_init();
-#endif
 }
 
 int
@@ -1684,13 +2092,30 @@ asl_action_init(void)
                asl_action_queue = dispatch_queue_create("ASL Action Queue", NULL);
 #if TARGET_OS_EMBEDDED
                crashlog_queue = dispatch_queue_create("iOS CrashLog Queue", NULL);
-               notify_register_dispatch(CRASH_MOVER_WILL_START_NOTIFICATION, &crashmover_token, asl_action_queue, ^(int unused){
-                       if (crashmover_state == 0)
+               notify_register_dispatch(CRASH_MOVER_SERVICE, &crashmover_token, asl_action_queue, ^(int unused) {
+                       uint64_t cmstate = 0;
+                       uint64_t oldstate = (crashmover_state == 0) ? 0llu : 1llu;
+
+                       uint32_t status = notify_get_state(crashmover_token, &cmstate);
+                       if (status == 0)
                        {
-                               asldebug("CrashMover active: suspending crashlog queue and closing files\n");
-                               crashmover_state = time(NULL);
-                               dispatch_suspend(crashlog_queue);
-                               _asl_action_close_idle_files(0);
+                               if (cmstate != oldstate)
+                               {
+                                       crashmover_state = 0;
+                                       if (cmstate == 1) crashmover_state = time(NULL);
+
+                                       if (crashmover_state == 0)
+                                       {
+                                               asldebug("CrashMover finished\n");
+                                               dispatch_resume(crashlog_queue);
+                                       }
+                                       else
+                                       {
+                                               asldebug("CrashMover active: suspending crashlog queue and closing files\n");
+                                               dispatch_suspend(crashlog_queue);
+                                               _asl_action_close_idle_files(0);
+                                       }
+                               }
                        }
                });
 #endif
@@ -1702,7 +2127,7 @@ asl_action_init(void)
 }
 
 /*
- * Free a module.
+ * Close outputs and free modules.
  */
 static void
 _asl_action_free_modules(asl_out_module_t *m)
@@ -1718,24 +2143,35 @@ _asl_action_free_modules(asl_out_module_t *m)
        {
                for (r = x->ruleset; r != NULL; r = r->next)
                {
-                       if ((r->action == ACTION_ASL_FILE) || (r->action == ACTION_ASL_DIR))
+                       if (r->action == ACTION_ASL_DIR)
+                       {
+                               _act_dst_close(r, DST_CLOSE_SHUTDOWN);
+                               if (r->dst != NULL)
+                               {
+                                       _asl_action_asl_store_data_free((asl_action_asl_store_data_t *)r->dst->private);
+                                       r->dst->private = NULL;
+                               }
+                       }
+                       else if (r->action == ACTION_ASL_FILE)
                        {
+                               _act_dst_close(r, DST_CLOSE_SHUTDOWN);
                                if (r->dst != NULL)
                                {
-                                       _asl_action_store_data_free((asl_action_store_data_t *)r->dst->private);
+                                       _asl_action_asl_file_data_free((asl_action_asl_file_data_t *)r->dst->private);
                                        r->dst->private = NULL;
                                }
                        }
                        else if (r->action == ACTION_FILE)
                        {
+                               _act_dst_close(r, DST_CLOSE_SHUTDOWN);
                                if (r->dst != NULL)
                                {
-                                       asl_action_file_data_t *fdata = (asl_action_file_data_t *)r->dst->private;
-                                       if (fdata != NULL)
+                                       asl_action_file_data_t *f_data = (asl_action_file_data_t *)r->dst->private;
+                                       if (f_data != NULL)
                                        {
                                                /* flush repeat message if necessary */
-                                               if (fdata->last_count > 0) _send_repeat_msg(r);
-                                               _asl_action_file_data_free(fdata);
+                                               if (f_data->last_count > 0) _send_repeat_msg(r);
+                                               _asl_action_file_data_free(f_data);
                                                r->dst->private = NULL;
                                        }
                                }
@@ -1748,6 +2184,10 @@ _asl_action_free_modules(asl_out_module_t *m)
                        {
                                _asl_action_set_param_data_free((asl_action_set_param_data_t *)r->private);
                        }
+                       else if (r->action == ACTION_SET_FILE)
+                       {
+                               _asl_action_set_param_data_free((asl_action_set_param_data_t *)r->private);
+                       }
                }
        }
 
@@ -1758,10 +2198,6 @@ static int
 _asl_action_close_internal(void)
 {
 #if TARGET_OS_EMBEDDED
-       dispatch_source_cancel(crashlog_sentinel_src);
-       dispatch_release(crashlog_sentinel_src);
-       crashlog_sentinel_src = NULL;
-       close(crashlog_sentinel_fd);
        if (crashmover_state != 0)
        {
                dispatch_resume(crashlog_queue);
@@ -1769,7 +2205,7 @@ _asl_action_close_internal(void)
        }
 
        /* wait for the crashlog_queue to flush before _asl_action_free_modules() */
-       dispatch_sync(crashlog_queue, ^{ crashlog_sentinel_fd = -1; });
+       dispatch_sync(crashlog_queue, ^{ int x = 0; if (x == 1) x = 2; });
 #endif
 
        _asl_action_free_modules(global.asl_out_module);
@@ -1795,24 +2231,34 @@ _asl_action_close_idle_files(time_t idle_time)
                        {
                                if ((r->dst != NULL) && (r->dst->flags & MODULE_FLAG_CRASHLOG))
                                {
-                                       _act_dst_close(r);
-                                       if (r->action != ACTION_ASL_DIR) asl_out_dst_checkpoint(r->dst, CHECKPOINT_FORCE);
+                                       _act_dst_close(r, DST_CLOSE_IDLE);
+                                       //TODO: can r->action even be ACTION_ASL_DIR?
+                                       /* if not, we can avoid the extra check here */
+                                       if (r->action != ACTION_ASL_DIR) _act_checkpoint(r, CHECKPOINT_FORCE);
+                               }
+                       }
+                       else if (r->action == ACTION_ASL_DIR)
+                       {
+                               if (r->dst != NULL)
+                               {
+                                       asl_action_asl_store_data_t *as_data = (asl_action_asl_store_data_t *)r->dst->private;
+                                       if ((as_data != NULL) && (as_data->aslfile != NULL) && (as_data->pending == 0) && ((now - as_data->last_time) >= idle_time)) _act_dst_close(r, DST_CLOSE_IDLE);
                                }
                        }
-                       else if ((r->action == ACTION_ASL_DIR) || (r->action == ACTION_ASL_FILE))
+                       else if (r->action == ACTION_ASL_FILE)
                        {
                                if (r->dst != NULL)
                                {
-                                       asl_action_store_data_t *sdata = (asl_action_store_data_t *)r->dst->private;
-                                       if ((sdata != NULL) && (sdata->store != NULL) && ((now - sdata->last_time) >= idle_time)) _act_dst_close(r);
+                                       asl_action_asl_file_data_t *af_data = (asl_action_asl_file_data_t *)r->dst->private;
+                                       if ((af_data != NULL) && (af_data->aslfile != NULL) && (af_data->pending == 0) && ((now - af_data->last_time) >= idle_time)) _act_dst_close(r, DST_CLOSE_IDLE);
                                }
                        }
                        else if (r->action == ACTION_FILE)
                        {
                                if (r->dst != NULL)
                                {
-                                       asl_action_file_data_t *fdata = (asl_action_file_data_t *)r->dst->private;
-                                       if ((fdata != NULL) && (fdata->fd >= 0) && ((now - fdata->last_time) >= idle_time)) _act_dst_close(r);
+                                       asl_action_file_data_t *f_data = (asl_action_file_data_t *)r->dst->private;
+                                       if ((f_data != NULL) && (f_data->fd >= 0) && (f_data->pending == 0) && ((now - f_data->last_time) >= idle_time)) _act_dst_close(r, DST_CLOSE_IDLE);
                                }
                        }
                }
@@ -1881,11 +2327,16 @@ asl_action_control_set_param(const char *s)
        if (l != NULL) for (count = 0; l[count] != NULL; count++);
 
        /* at least 2 parameters (l[0] = module, l[1] = param) required */
-       if (count < 2) return -1;
+       if (count < 2)
+       {
+               free_string_list(l);
+               return -1;
+       }
 
        if (global.asl_out_module == NULL)
        {
                asldebug("asl_action_control_set_param: no modules loaded\n");
+               free_string_list(l);
                return -1;
        }
 
@@ -1896,6 +2347,7 @@ asl_action_control_set_param(const char *s)
                if (p == NULL)
                {
                        asldebug("asl_action_control_set_param: memory allocation failed\n");
+                       free_string_list(l);
                        return -1;
                }