]> git.saurik.com Git - apple/syslog.git/blobdiff - aslcommon/asl_common.c
syslog-267.tar.gz
[apple/syslog.git] / aslcommon / asl_common.c
index a4c21d36bd8e5e8a64634f2d6ccfa8003d582896..fea5e91b6c63892c9855a93cb54738bba06c5d8a 100644 (file)
@@ -38,6 +38,7 @@
 #include <xpc/xpc.h>
 #include <TargetConditionals.h>
 #include <configuration_profile.h>
+#include <asl.h>
 #include <asl_core.h>
 #include <asl_msg.h>
 #include "asl_common.h"
 #define _PATH_ASL_CONF "/etc/asl.conf"
 #define _PATH_ASL_CONF_DIR "/etc/asl"
 
+#define PATH_VAR_LOG "/var/log/"
+#define PATH_VAR_LOG_LEN 9
+
+#define PATH_LIBRARY_LOGS "/Library/Logs/"
+#define PATH_LIBRARY_LOGS_LEN 14
+
 #if !TARGET_IPHONE_SIMULATOR
 #define _PATH_ASL_CONF_LOCAL_DIR "/usr/local/etc/asl"
 #endif
@@ -60,6 +67,8 @@ static const char *asl_out_action_name[] =
        "notify       ",
        "broadcast    ",
        "access       ",
+       "set          ",
+       "unset        ",
        "store        ",
        "asl_file     ",
        "asl_dir      ",
@@ -71,13 +80,15 @@ static const char *asl_out_action_name[] =
        "set (profile)"
 };
 
-static time_t start_today;
-
-extern asl_msg_t *asl_msg_from_string(const char *buf);
-
 #define forever for(;;)
 #define KEYMATCH(S,K) ((strncasecmp(S, K, strlen(K)) == 0))
 
+#define STAMP_STYLE_INVALID -1
+#define STAMP_STYLE_NULL 0
+#define STAMP_STYLE_SEC 1
+#define STAMP_STYLE_SEQ 2
+#define STAMP_STYLE_UTC_OR_LCL 3
+
 asl_msg_t *
 xpc_object_to_asl_msg(xpc_object_t xobj)
 {
@@ -432,8 +443,143 @@ next_word_from_string(char **s)
        return out;
 }
 
+static asl_out_dst_data_t *
+_asl_out_dest_for_path(asl_out_module_t *m, const char *path)
+{
+       if (m == NULL) return NULL;
+       if (path == NULL) return NULL;
+
+       while (m != NULL)
+       {
+               asl_out_rule_t *r = m->ruleset;
+               while (r != NULL)
+               {
+                       if ((r->action == ACTION_OUT_DEST) && (r->dst != NULL) && (r->dst->path != NULL) && (!strcmp(r->dst->path, path))) return r->dst;
+                       r = r->next;
+               }
+
+               m = m->next;
+       }
+
+       return NULL;
+}
+
+/*
+ * Create a directory path.
+ *
+ * mlist provides owner, group, and access mode, which are required for "non-standard"
+ * directories.  Directories for standard paths (/var/log or /Library/Logs) default
+ * to root/admin/0755 if there is no mlist rule for them.
+ */
+static int
+_asl_common_make_dir_path(asl_out_module_t *mlist, uint32_t flags, const char *path)
+{
+       int i;
+       char **path_parts;
+       asl_string_t *processed_path;
+       mode_t mode;
+
+       if (path == NULL) return 0;
+
+       path_parts = explode(path, "/");
+       if (path_parts == NULL) return 0;
+
+       processed_path = asl_string_new(ASL_ENCODE_NONE);
+
+       i = 0;
+       if (path[0] == '/') i = 1;
+
+       for (; path_parts[i] != NULL; i++)
+       {
+               struct stat sb;
+               int status;
+               mode_t mask;
+               asl_out_dst_data_t *dst;
+               char *tmp;
+
+               asl_string_append_char_no_encoding(processed_path, '/');
+               asl_string_append_no_encoding(processed_path, path_parts[i]);
+               tmp = asl_string_bytes(processed_path);
+
+               memset(&sb, 0, sizeof(struct stat));
+               status = lstat(tmp, &sb);
+               if ((status == 0) && S_ISLNK(sb.st_mode))
+               {
+                       char real[MAXPATHLEN];
+                       if (realpath(tmp, real) == NULL)
+                       {
+                               asl_string_release(processed_path);
+                               free_string_list(path_parts);
+                               return -1;
+                       }
+
+                       memset(&sb, 0, sizeof(struct stat));
+                       status = stat(real, &sb);
+               }
+
+               if (status == 0)
+               {
+                       if (!S_ISDIR(sb.st_mode))
+                       {
+                               /* path component is not a directory! */
+                               asl_string_release(processed_path);
+                               free_string_list(path_parts);
+                               return -1;
+                       }
+
+                       /* exists and is a directory or a link to a directory */
+                       continue;
+               }
+               else if (errno != ENOENT)
+               {
+                       /* unexpected status from stat() */
+                       asl_string_release(processed_path);
+                       free_string_list(path_parts);
+                       return -1;
+               }
+
+               dst = _asl_out_dest_for_path(mlist, tmp);
+               if ((dst == NULL) && (flags & MODULE_FLAG_NONSTD_DIR))
+               {
+                       /* no rule to create a non-standard path component! */
+                       asl_string_release(processed_path);
+                       free_string_list(path_parts);
+                       return -1;
+               }
+
+               mode = 0755;
+               if (dst != NULL)
+               {
+                       mode = dst->mode;
+                       if (mode == 010000) mode = 0755;
+               }
+
+               mask = umask(0);
+               status = mkdir(tmp, mode);
+               umask(mask);
+
+#if !TARGET_IPHONE_SIMULATOR
+               uid_t u = 0;
+               gid_t g = 80;
+
+               if (dst != NULL)
+               {
+                       if (dst->nuid > 0) u = dst->uid[0];
+                       if (dst->ngid > 0) g = dst->gid[0];
+               }
+
+               chown(tmp, u, g);
+#endif
+       }
+
+       asl_string_release(processed_path);
+       free_string_list(path_parts);
+
+       return 0;
+}
+
 int
-asl_out_mkpath(asl_out_rule_t *r)
+asl_out_mkpath(asl_out_module_t *mlist, asl_out_rule_t *r)
 {
        char tmp[MAXPATHLEN], *p;
        struct stat sb;
@@ -456,14 +602,18 @@ asl_out_mkpath(asl_out_rule_t *r)
        status = stat(tmp, &sb);
        if (status == 0)
        {
-               if (!S_ISDIR(sb.st_mode)) return -1;
+               if (S_ISDIR(sb.st_mode)) return 0;
+               return -1;
        }
-       else if (errno == ENOENT)
+
+       if (errno == ENOENT)
        {
-               status = mkpath_np(tmp, 0755);
+               uint32_t dirflag = r->dst->flags & MODULE_FLAG_NONSTD_DIR;
+               status = _asl_common_make_dir_path(mlist, dirflag, tmp);
+               return status;
        }
 
-       return status;
+       return -1;
 }
 
 void
@@ -534,13 +684,16 @@ asl_make_dst_filename(asl_out_dst_data_t *dst, char *buf, size_t len)
        if (dst == NULL) return;
        if (buf == NULL) return;
 
-       if (dst->flags & MODULE_FLAG_BASESTAMP)
+       if (dst->flags & (MODULE_FLAG_BASESTAMP | MODULE_FLAG_TYPE_ASL_DIR))
        {
                char tstamp[32];
+               const char *name = dst->path;
+
+               if (dst->flags & MODULE_FLAG_TYPE_ASL_DIR) name = dst->fname;
 
                if (dst->stamp == 0) dst->stamp = time(NULL);
                asl_make_timestamp(dst->stamp, dst->flags, tstamp, sizeof(tstamp));
-               snprintf(buf, len, "%s.%s", dst->path, tstamp);
+               snprintf(buf, len, "%s.%s", name, tstamp);
        }
        else
        {
@@ -549,95 +702,7 @@ asl_make_dst_filename(asl_out_dst_data_t *dst, char *buf, size_t len)
 }
 
 int
-asl_out_dst_checkpoint(asl_out_dst_data_t *dst, uint32_t force)
-{
-       char newpath[MAXPATHLEN];
-       time_t now;
-
-       now = time(NULL);
-
-       /* clock went backwards - force a reset */
-       if (now < start_today) start_today = 0;
-
-       /* check start_today and reset if required */
-       if (now >= (start_today + SECONDS_PER_DAY))
-       {
-               /* use localtime / mktime since start_today might be zero */
-               struct tm t;
-
-               start_today = now;
-
-               localtime_r(&start_today, &t);
-
-               t.tm_sec = 0;
-               t.tm_min = 0;
-               t.tm_hour = 0;
-
-               start_today = mktime(&t);
-       }
-
-       /* sleep to prevent a sub-second rotation */
-       while (now == dst->stamp)
-       {
-               sleep(1);
-               now = time(NULL);
-       }
-
-       if ((dst->stamp == 0) || (dst->size == 0))
-       {
-               struct stat sb;
-
-               memset(&sb, 0, sizeof(struct stat));
-
-               if (stat(dst->path, &sb) < 0)
-               {
-                       if (errno == ENOENT) return 0;
-                       return -1;
-               }
-
-               if (dst->stamp == 0) dst->stamp = sb.st_birthtimespec.tv_sec;
-               if (dst->stamp == 0) dst->stamp = sb.st_mtimespec.tv_sec;
-               dst->size = sb.st_size;
-       }
-
-       if (force == CHECKPOINT_TEST)
-       {
-               if ((dst->file_max > 0) && (dst->size >= dst->file_max)) force |= CHECKPOINT_SIZE;
-               if (dst->stamp < start_today) force |= CHECKPOINT_TIME;
-
-               if (force == CHECKPOINT_TEST) return 0;
-       }
-
-       if (dst->flags & MODULE_FLAG_TYPE_ASL_DIR)
-       {
-               if (force & CHECKPOINT_SIZE)
-               {
-                       snprintf(newpath, sizeof(newpath), "%s.%c%lu", dst->fname, STYLE_SEC_PREFIX_CHAR, dst->stamp);
-                       rename(dst->fname, newpath);
-               }
-               else
-               {
-                       return 0;
-               }
-       }
-
-       if ((dst->flags & MODULE_FLAG_BASESTAMP) == 0)
-       {
-               char tstamp[32];
-
-               asl_make_timestamp(dst->stamp, dst->flags, tstamp, sizeof(tstamp));
-               snprintf(newpath, sizeof(newpath), "%s.%s", dst->path, tstamp);
-               rename(dst->path, newpath);
-       }
-
-       dst->stamp = 0;
-       dst->size = 0;
-
-       return 1;
-}
-
-int
-asl_check_option(aslmsg msg, const char *opt)
+asl_check_option(asl_msg_t *msg, const char *opt)
 {
        const char *p;
        uint32_t len;
@@ -648,7 +713,7 @@ asl_check_option(aslmsg msg, const char *opt)
        len = strlen(opt);
        if (len == 0) return 0;
 
-       p = asl_get(msg, ASL_KEY_OPTION);
+       p = asl_msg_get_val_for_key(msg, ASL_KEY_OPTION);
        if (p == NULL) return 0;
 
        while (*p != '\0')
@@ -737,7 +802,7 @@ asl_out_dst_set_access(int fd, asl_out_dst_data_t *dst)
                 * Don't bother setting group access if this is
                 * file's group and the file is group-readable.
                 */
-               if ((dst->gid[i] == fgid) && (dst->mode & 0040)) continue;
+               if ((dst->gid[i] == fgid) && (dst->mode & 00040)) continue;
 
                status = mbr_gid_to_uuid(dst->gid[i], uuid);
                if (status != 0)
@@ -770,7 +835,7 @@ asl_out_dst_set_access(int fd, asl_out_dst_data_t *dst)
                 * Don't bother setting user access if this is
                 * file's owner and the file is owner-readable.
                 */
-               if ((dst->uid[i] == fuid) && (dst->mode & 0400)) continue;
+               if ((dst->uid[i] == fuid) && (dst->mode & 00400)) continue;
 
                status = mbr_uid_to_uuid(dst->uid[i], uuid);
                if (status != 0)
@@ -812,7 +877,7 @@ asl_file_create_return:
 
 /* create a file with acls */
 int
-asl_out_dst_file_create_open(asl_out_dst_data_t *dst)
+asl_out_dst_file_create_open(asl_out_dst_data_t *dst, char **pathp)
 {
        int fd, status;
        struct stat sb;
@@ -822,6 +887,12 @@ asl_out_dst_file_create_open(asl_out_dst_data_t *dst)
        if (dst->path == NULL) return -1;
 
        asl_make_dst_filename(dst, outpath, sizeof(outpath));
+       if (dst->fname != NULL) free(dst->fname);
+
+       dst->fname = strdup(outpath);
+       if (dst->fname == NULL) return -1;
+
+       if (pathp != NULL) *pathp = strdup(outpath);
 
        memset(&sb, 0, sizeof(struct stat));
        status = stat(outpath, &sb);
@@ -845,7 +916,7 @@ asl_out_dst_file_create_open(asl_out_dst_data_t *dst)
                return -1;
        }
 
-       fd = open(outpath, O_RDWR | O_CREAT | O_EXCL, (dst->mode & 0666));
+       fd = open(outpath, O_RDWR | O_CREAT | O_EXCL, (dst->mode & 00666));
        if (fd < 0) return -1;
 
        dst->stamp = time(NULL);
@@ -1176,6 +1247,7 @@ _asl_out_module_parse_dst(asl_out_module_t *m, char *s, mode_t def_mode)
        char *p, *opts, *path;
        char **path_parts;
        int has_dotdot, recursion_limit;
+       uint32_t i, flags = 0;
 
        if (m == NULL) return NULL;
        if (s == NULL) return NULL;
@@ -1233,16 +1305,13 @@ _asl_out_module_parse_dst(asl_out_module_t *m, char *s, mode_t def_mode)
                }
 
                free_string_list(path_parts);
+               path_parts = NULL;
 
-               if ((did_sub == 0) || (has_dotdot == 1))
-               {
-                       path_parts = NULL;
-               }
-               else
+               if ((did_sub == 1) && (has_dotdot == 0))
                {
                        /* substitution might have added a ".." so check the new path */
                        free(path);
-                       path = asl_string_free_return_bytes(processed_path);
+                       path = asl_string_release_return_bytes(processed_path);
                        processed_path = asl_string_new(ASL_ENCODE_NONE);
                        path_parts = explode(path, "/");
                        recursion_limit--;
@@ -1250,14 +1319,16 @@ _asl_out_module_parse_dst(asl_out_module_t *m, char *s, mode_t def_mode)
        }
 
        free(path);
+       free_string_list(path_parts);
+       path_parts = NULL;
 
        if ((has_dotdot != 0) || (recursion_limit == 0))
        {
-               asl_string_free(processed_path);
+               asl_string_release(processed_path);
                return NULL;
        }
 
-       path = asl_string_free_return_bytes(processed_path);
+       path = asl_string_release_return_bytes(processed_path);
 
        /* check if there's already a dst for this path */
        for (rule = m->ruleset; rule != NULL; rule = rule->next)
@@ -1274,20 +1345,38 @@ _asl_out_module_parse_dst(asl_out_module_t *m, char *s, mode_t def_mode)
                }
        }
 
+       flags |= MODULE_FLAG_NONSTD_DIR;
+
        if (path[0] != '/')
        {
                char *t = path;
                const char *log_root = "/var/log";
 
 #if TARGET_IPHONE_SIMULATOR
-               log_root = getenv("IPHONE_SIMULATOR_LOG_ROOT");
-               assert(log_root);
+               log_root = getenv("SIMULATOR_LOG_ROOT");
+               if (log_root == NULL) log_root = "/tmp/log";
 #endif
 
-               if (!strcmp(m->name, ASL_MODULE_NAME)) asprintf(&path, "%s/%s", log_root, t);
-               else asprintf(&path, "%s/module/%s/%s", log_root, m->name, t);
+               if (!strcmp(m->name, ASL_MODULE_NAME))
+               {
+                       asprintf(&path, "%s/%s", log_root, t);
+               }
+               else
+               {
+                       asprintf(&path, "%s/module/%s/%s", log_root, m->name, t);
+               }
 
                free(t);
+               flags &= ~MODULE_FLAG_NONSTD_DIR;
+       }
+       else
+       {
+               /*
+                * Standard log directories get marked so that syslogd
+                * will create them without explicit rules.
+                */
+               if (!strncmp(path, PATH_VAR_LOG, PATH_VAR_LOG_LEN)) flags &= ~MODULE_FLAG_NONSTD_DIR;
+               else if (!strncmp(path, PATH_LIBRARY_LOGS, PATH_LIBRARY_LOGS_LEN)) flags &= ~MODULE_FLAG_NONSTD_DIR;
        }
 
        out = (asl_out_rule_t *)calloc(1, sizeof(asl_out_rule_t));
@@ -1303,13 +1392,12 @@ _asl_out_module_parse_dst(asl_out_module_t *m, char *s, mode_t def_mode)
        dst->refcount = 1;
        dst->path = path;
        dst->mode = def_mode;
-       dst->ttl = DEFAULT_TTL;
-       dst->flags = MODULE_FLAG_COALESCE;
+       dst->ttl[LEVEL_ALL] = DEFAULT_TTL;
+       dst->flags = flags | MODULE_FLAG_COALESCE;
 
        while (NULL != (p = next_word_from_string(&opts)))
        {
                if (KEYMATCH(p, "mode=")) dst->mode = strtol(p+5, NULL, 0);
-               else if (KEYMATCH(p, "ttl=")) dst->ttl = strtol(p+4, NULL, 0);
 #if !TARGET_IPHONE_SIMULATOR
                else if (KEYMATCH(p, "uid=")) _dst_add_uid(dst, p+4);
                else if (KEYMATCH(p, "gid=")) _dst_add_gid(dst, p+4);
@@ -1326,6 +1414,8 @@ _asl_out_module_parse_dst(asl_out_module_t *m, char *s, mode_t def_mode)
                }
                else if (KEYMATCH(p, "compress")) dst->flags |= MODULE_FLAG_COMPRESS;
                else if (KEYMATCH(p, "extern")) dst->flags |= MODULE_FLAG_EXTERNAL;
+               else if (KEYMATCH(p, "truncate")) dst->flags |= MODULE_FLAG_TRUNCATE;
+               else if (KEYMATCH(p, "dir")) dst->flags |= MODULE_FLAG_TYPE_ASL_DIR;
                else if (KEYMATCH(p, "soft")) dst->flags |= MODULE_FLAG_SOFT_WRITE;
                else if (KEYMATCH(p, "file_max=")) dst->file_max = asl_str_to_size(p+9);
                else if (KEYMATCH(p, "all_max=")) dst->all_max = asl_str_to_size(p+8);
@@ -1375,6 +1465,19 @@ _asl_out_module_parse_dst(asl_out_module_t *m, char *s, mode_t def_mode)
                {
                        dst->flags |= MODULE_FLAG_BASESTAMP;
                }
+               else if (KEYMATCH(p, "ttl"))
+               {
+                       char *q = p + 3;
+                       if (*q == '=')
+                       {
+                               dst->ttl[LEVEL_ALL] = strtol(p+4, NULL, 0);
+                       }
+                       else if ((*q >= '0') && (*q <= '7') && (*(q+1) == '='))
+                       {
+                               uint32_t x = *q - '0';
+                               dst->ttl[x] = strtol(p+5, NULL, 0);
+                       }
+               }
 
                free(p);
                p = NULL;
@@ -1382,7 +1485,7 @@ _asl_out_module_parse_dst(asl_out_module_t *m, char *s, mode_t def_mode)
 
 #if TARGET_OS_EMBEDDED
        /* check for crashreporter files */
-       if (KEYMATCH(dst->path, _PATH_CRASHREPORTER))
+       if ((KEYMATCH(dst->path, _PATH_CRASHREPORTER)) || (KEYMATCH(dst->path, _PATH_CRASHREPORTER_MOBILE)))
        {
                dst->flags |= MODULE_FLAG_ROTATE;
                dst->flags |= MODULE_FLAG_CRASHLOG;
@@ -1391,6 +1494,9 @@ _asl_out_module_parse_dst(asl_out_module_t *m, char *s, mode_t def_mode)
        }
 #endif
 
+       /* ttl[LEVEL_ALL] must be max of all level-specific ttls */
+       for (i = 0; i <= 7; i++) if (dst->ttl[i] > dst->ttl[LEVEL_ALL]) dst->ttl[LEVEL_ALL] = dst->ttl[i];
+
        /* default text file format is "std" */
        if (dst->fmt == NULL) dst->fmt = strdup("std");
 
@@ -1398,7 +1504,7 @@ _asl_out_module_parse_dst(asl_out_module_t *m, char *s, mode_t def_mode)
        if (strcmp(dst->fmt, "std") && strcmp(dst->fmt, "bsd")) dst->flags &= ~MODULE_FLAG_COALESCE;
 
        /* note if format is one of std, bsd, or msg */
-       if ((!strcmp(dst->fmt, "std")) || (!strcmp(dst->fmt, "bsd")) || (!strcmp(dst->fmt, "msg"))) dst->flags |= MODULE_FLAG_STD_BSD_MSG;
+       if (!strcmp(dst->fmt, "std") || !strcmp(dst->fmt, "bsd") || !strcmp(dst->fmt, "msg")) dst->flags |= MODULE_FLAG_STD_BSD_MSG;
 
        /* MODULE_FLAG_STYLE_SEQ can not be used with MODULE_FLAG_BASESTAMP */
        if ((dst->flags & MODULE_FLAG_BASESTAMP) && (dst->flags & MODULE_FLAG_STYLE_SEQ))
@@ -1410,6 +1516,12 @@ _asl_out_module_parse_dst(asl_out_module_t *m, char *s, mode_t def_mode)
        /* set time format for raw output */
        if (!strcmp(dst->fmt, "raw")) dst->tfmt = "sec";
 
+       /* check for ASL_PLACE_DATABASE_DEFAULT */
+       if (!strcmp(dst->path, ASL_PLACE_DATABASE_DEFAULT))
+       {
+               dst->flags = MODULE_FLAG_TYPE_ASL_DIR;
+       }
+
        out->action = ACTION_OUT_DEST;
        out->dst = dst;
 
@@ -1455,6 +1567,8 @@ _asl_out_module_parse_query_action(asl_out_module_t *m, char *s)
        else if (!strcasecmp(act, "save"))            out->action = ACTION_ASL_STORE;
        else if (!strcasecmp(act, "store"))           out->action = ACTION_ASL_STORE;
        else if (!strcasecmp(act, "access"))          out->action = ACTION_ACCESS;
+       else if (!strcasecmp(act, "set"))             out->action = ACTION_SET_KEY;
+       else if (!strcasecmp(act, "unset"))           out->action = ACTION_UNSET_KEY;
        else if (!strcmp(m->name, ASL_MODULE_NAME))
        {
                /* actions only allowed in com.apple.asl */
@@ -1505,7 +1619,12 @@ _asl_out_module_parse_query_action(asl_out_module_t *m, char *s)
        }
 
        /* store /some/path means save to an asl file */
-       if ((out->action == ACTION_ASL_STORE) && (out->options != NULL)) out->action = ACTION_ASL_FILE;
+       if (out->action == ACTION_ASL_STORE)
+       {
+               if (out->options == NULL) out->dst = asl_out_dst_data_retain(_asl_out_module_parse_dst(m, ASL_PLACE_DATABASE_DEFAULT, 0755));
+               else if (!strncmp(out->options, ASL_PLACE_DATABASE_DEFAULT, strlen(ASL_PLACE_DATABASE_DEFAULT))) out->dst = asl_out_dst_data_retain(_asl_out_module_parse_dst(m, out->options, 0755));
+               else if (out->options != NULL) out->action = ACTION_ASL_FILE;
+       }
 
        if ((out->action == ACTION_FILE) || (out->action == ACTION_ASL_FILE) || (out->action == ACTION_ASL_DIR))
        {
@@ -1519,6 +1638,12 @@ _asl_out_module_parse_query_action(asl_out_module_t *m, char *s)
                        return out;
                }
 
+               /*
+                * dst might have been set up by a previous ACTION_OUT_DEST ('>') rule with no mode.
+                * If so, mode would be 010000.  Set it now, since we know whether it is a file or dir.
+                */
+               if (out->dst->mode == 010000) out->dst->mode = def_mode;
+
                if ((out->action == ACTION_FILE) && (out->dst != NULL) && (out->dst->fmt != NULL) && (!strcasecmp(out->dst->fmt, "asl")))
                {
                        out->action = ACTION_ASL_FILE;
@@ -1526,19 +1651,21 @@ _asl_out_module_parse_query_action(asl_out_module_t *m, char *s)
 
                if ((out->action == ACTION_ASL_FILE) && (out->dst != NULL))
                {
-                       /* remove meaningless flags */
-                       out->dst->flags &= ~MODULE_FLAG_COALESCE;
-                       out->dst->flags &= ~MODULE_FLAG_STD_BSD_MSG;
                        out->dst->flags |= MODULE_FLAG_TYPE_ASL;
                }
 
                if (out->action == ACTION_ASL_DIR)
                {
-                       /* remove meaningless flags */
-                       out->dst->flags &= ~MODULE_FLAG_ROTATE;
+                       /* coalesce is meaningless for ASL directories */
                        out->dst->flags &= ~MODULE_FLAG_COALESCE;
-                       out->dst->flags &= ~MODULE_FLAG_STD_BSD_MSG;
+
+                       /* no compression at this point */
+                       out->dst->flags &= ~MODULE_FLAG_COMPRESS;
+
                        out->dst->flags |= MODULE_FLAG_TYPE_ASL_DIR;
+
+                       /* set style bits for basestamp asl_dirs */
+                       if (((out->dst->flags & MODULE_FLAG_STYLE_BITS) == 0) && (out->dst->flags & MODULE_FLAG_BASESTAMP)) out->dst->flags |= MODULE_FLAG_STYLE_LCL_B;
                }
 
                /* only ACTION_FILE and ACTION_ASL_FILE may rotate */
@@ -1578,7 +1705,7 @@ asl_out_module_parse_line(asl_out_module_t *m, char *s)
        }
        else if (*s == '>') 
        {
-               _asl_out_module_parse_dst(m, s + 1, 0644);
+               _asl_out_module_parse_dst(m, s + 1, 010000);
        }
 
        return NULL;
@@ -1775,7 +1902,7 @@ asl_out_module_print(FILE *f, asl_out_module_t *m)
 {
        asl_out_rule_t *r, *n;
        asl_out_dst_data_t *o;
-       uint32_t i;
+       uint32_t i, ttlnset;
 
        n = NULL;
        for (r = m->ruleset; r != NULL; r = n)
@@ -1810,9 +1937,9 @@ asl_out_module_print(FILE *f, asl_out_module_t *m)
                                                fprintf(f, "%cenabled", c);
                                                c = ' ';
                                        }
-                                       if (o->flags & MODULE_FLAG_LOCAL)
+                                       if (o->flags & MODULE_FLAG_SOFT_WRITE)
                                        {
-                                               fprintf(f, "%clocal", c);
+                                               fprintf(f, "%csoft", c);
                                                c = ' ';
                                        }
                                        if (o->flags & MODULE_FLAG_ROTATE)
@@ -1830,11 +1957,6 @@ asl_out_module_print(FILE *f, asl_out_module_t *m)
                                                fprintf(f, "%ccompress", c);
                                                c = ' ';
                                        }
-                                       if (o->flags & MODULE_FLAG_EXTERNAL)
-                                       {
-                                               fprintf(f, "%cexternal", c);
-                                               c = ' ';
-                                       }
                                        if (o->flags & MODULE_FLAG_STYLE_SEC)
                                        {
                                                fprintf(f, "%cseconds", c);
@@ -1870,14 +1992,19 @@ asl_out_module_print(FILE *f, asl_out_module_t *m)
                                                fprintf(f, "%cbasestamp", c);
                                                c = ' ';
                                        }
-                                       if (o->flags & MODULE_FLAG_CRASHLOG)
+                                       if (o->flags & MODULE_FLAG_NONSTD_DIR)
                                        {
-                                               fprintf(f, "%ccrashlog", c);
+                                               fprintf(f, "%cnon-std_dir", c);
                                                c = ' ';
                                        }
-                                       if (o->flags & MODULE_FLAG_SOFT_WRITE)
+                                       if (o->flags & MODULE_FLAG_EXTERNAL)
                                        {
-                                               fprintf(f, "%csoft", c);
+                                               fprintf(f, "%cexternal", c);
+                                               c = ' ';
+                                       }
+                                       if (o->flags & MODULE_FLAG_CRASHLOG)
+                                       {
+                                               fprintf(f, "%ccrashlog", c);
                                                c = ' ';
                                        }
                                        if (o->flags & MODULE_FLAG_TYPE_ASL)
@@ -1890,16 +2017,16 @@ asl_out_module_print(FILE *f, asl_out_module_t *m)
                                                fprintf(f, "%casl_directory", c);
                                                c = ' ';
                                        }
-                                       if (o->flags & MODULE_FLAG_STD_BSD_MSG)
-                                       {
-                                               fprintf(f, "%cstd/bsd/msg", c);
-                                               c = ' ';
-                                       }
                                        fprintf(f, ")");
                                }
                                fprintf(f, "\n");
 
-                               fprintf(f, "    ttl: %u\n", o->ttl);
+                               fprintf(f, "    ttl: %u", o->ttl[LEVEL_ALL]);
+                               ttlnset = 0;
+                               for (i = 0; (i <= 7) & (ttlnset == 0); i++) if (o->ttl[i] != 0) ttlnset = 1;
+                               if (ttlnset != 0) for (i = 0; i <= 7; i++) printf(" [%d %d]", i, (o->ttl[i] == 0) ? o->ttl[LEVEL_ALL] : o->ttl[i]);
+                               fprintf(f, "\n");
+
                                fprintf(f, "    mode: 0%o\n", o->mode);
                                fprintf(f, "    file_max: %lu\n", o->file_max);
                                fprintf(f, "    all_max: %lu\n", o->all_max);
@@ -2033,6 +2160,104 @@ _check_file_name(const char *name, const char *base, bool src, char **stamp)
  * MODULE_FLAG_STYLE_LCL_B requires a date/time component as the file's timestamp.
  * E.g. foo.20120406T153000-07
  */
+int
+_parse_stamp_style(char *stamp, uint32_t flags, uint32_t *sp, time_t *tp)
+{
+       int i, n;
+       bool digits;
+       struct tm t;
+       char zone;
+       uint32_t h, m, s;
+       long utc_offset = 0;
+       time_t ftime = 0;
+
+       /* check for NULL (no stamp) */
+       if (stamp == NULL) return STAMP_STYLE_NULL;
+
+       /* check for MODULE_FLAG_STYLE_SEC (foo.T12345678) */
+       if (stamp[0] == 'T')
+       {
+               n = atoi(stamp + 1);
+               if ((n == 0) && strcmp(stamp + 1, "0")) return STAMP_STYLE_INVALID;
+               if (tp != NULL) *tp = (time_t)n;
+
+               return STAMP_STYLE_SEC;
+       }
+
+       /* check for MODULE_FLAG_STYLE_SEQ (foo.0 or foo.2.gz) */
+       digits = true;
+       for (i = 0; digits && (stamp[i] != '\0'); i++) digits = (stamp[i] >= '0') && (stamp[i] <= '9');
+
+       if (!digits && (!strcmp(stamp + i, ".gz"))) digits = true;
+
+       if (digits)
+       {
+               n = atoi(stamp);
+               if (sp != NULL) *sp = (uint32_t)n;
+               return STAMP_STYLE_SEQ;
+       }
+
+       /* check for MODULE_FLAG_STYLE_UTC, UTC_B, LCL, or LCL_B */
+       memset(&t, 0, sizeof(t));
+       h = m = s = 0;
+
+       n = 0;
+       if ((flags & MODULE_FLAG_STYLE_UTC) || (flags & MODULE_FLAG_STYLE_LCL))
+       {
+               n = sscanf(stamp, "%d-%d-%dT%d:%d:%d%c%u:%u:%u", &t.tm_year, &t.tm_mon, &t.tm_mday, &t.tm_hour, &t.tm_min, &t.tm_sec, &zone, &h, &m, &s);
+       }
+       else if ((flags & MODULE_FLAG_STYLE_UTC_B) || (flags & MODULE_FLAG_STYLE_LCL_B))
+       {
+               n = sscanf(stamp, "%4d%2d%2dT%2d%2d%2d%c%2u%2u%2u", &t.tm_year, &t.tm_mon, &t.tm_mday, &t.tm_hour, &t.tm_min, &t.tm_sec, &zone, &h, &m, &s);
+       }
+       else
+       {
+               return STAMP_STYLE_INVALID;
+       }
+
+       if (n < 6) return STAMP_STYLE_INVALID;
+
+       if (n == 6)
+       {
+               zone = 'J';
+       }
+       else if ((zone == '-') || (zone == '+'))
+       {
+               if (n >= 8) utc_offset += (3600 * h);
+               if (n >= 9) utc_offset += (60 * m);
+               if (n == 10) utc_offset += s;
+               if (zone == '-') utc_offset *= -1;
+       }
+       else if ((zone >= 'A') && (zone <= 'Z'))
+       {
+               if (zone < 'J') utc_offset = 3600 * ((zone - 'A') + 1);
+               else if ((zone >= 'K') && (zone <= 'M')) utc_offset = 3600 * (zone - 'A');
+               else if (zone <= 'Y') utc_offset = -3600 * ((zone - 'N') + 1);
+       }
+       else if ((zone >= 'a') && (zone <= 'z'))
+       {
+               if (zone < 'j') utc_offset = 3600 * ((zone - 'a') + 1);
+               else if ((zone >= 'k') && (zone <= 'm')) utc_offset = 3600 * (zone - 'a');
+               else if (zone <= 'y') utc_offset = -3600 * ((zone - 'n') + 1);
+       }
+       else
+       {
+               return STAMP_STYLE_INVALID;
+       }
+
+       t.tm_year -= 1900;
+       t.tm_mon -= 1;
+       t.tm_sec += utc_offset;
+       t.tm_isdst = -1;
+
+       if ((zone == 'J') || (zone == 'j')) ftime = mktime(&t);
+       else ftime = timegm(&t);
+
+       if (tp != NULL) *tp = ftime;
+
+       return STAMP_STYLE_UTC_OR_LCL;
+}
+
 asl_out_file_list_t *
 asl_list_log_files(const char *dir, const char *base, bool src, uint32_t flags)
 {
@@ -2042,7 +2267,7 @@ asl_list_log_files(const char *dir, const char *base, bool src, uint32_t flags)
        uint32_t seq;
        time_t ftime;
        struct stat sb;
-       int n;
+       int pstyle, fstyle;
        asl_out_file_list_t *out, *x, *y;
 
        if (dir == NULL) return NULL;
@@ -2063,90 +2288,33 @@ asl_list_log_files(const char *dir, const char *base, bool src, uint32_t flags)
                check = _check_file_name(ent->d_name, base, src, &stamp);
                if (!check) continue;
 
-               /* exclude base from dst list */
-
                seq = IndexNull;
                ftime = 0;
 
-               if (stamp == NULL)
-               {
-               }
-               else if (flags & MODULE_FLAG_STYLE_SEQ)
-               {
-                       seq = atoi(stamp);
-                       if ((seq == 0) && strcmp(stamp, "0"))
-                       {
-                               free(stamp);
-                               continue;
-                       }
-               }
-               else if (flags & MODULE_FLAG_STYLE_SEC)
-               {
-                       ftime = atoi(stamp + 1);
-               }
-               else if ((flags & MODULE_FLAG_STYLE_UTC) || (flags & MODULE_FLAG_STYLE_UTC_B) || (flags & MODULE_FLAG_STYLE_LCL) || (flags & MODULE_FLAG_STYLE_LCL_B))
-               {
-                       struct tm t;
-                       char zone;
-                       uint32_t h, m, s;
-                       long utc_offset = 0;
-
-                       memset(&t, 0, sizeof(t));
-                       h = m = s = 0;
+               pstyle = _parse_stamp_style(stamp, flags, &seq, &ftime);
+               free(stamp);
 
-                       n = 0;
-                       if ((flags & MODULE_FLAG_STYLE_UTC) || (flags & MODULE_FLAG_STYLE_LCL))
-                       {
-                               n = sscanf(stamp, "%d-%d-%dT%d:%d:%d%c%u:%u:%u", &t.tm_year, &t.tm_mon, &t.tm_mday, &t.tm_hour, &t.tm_min, &t.tm_sec, &zone, &h, &m, &s);
-                       }
-                       else
-                       {
-                               n = sscanf(stamp, "%4d%2d%2dT%2d%2d%2d%c%2u%2u%2u", &t.tm_year, &t.tm_mon, &t.tm_mday, &t.tm_hour, &t.tm_min, &t.tm_sec, &zone, &h, &m, &s);
-                       }
+               if (pstyle == STAMP_STYLE_INVALID) continue;
 
-                       if (n < 6)
-                       {
-                               continue;
-                       }
-                       else if (n == 6)
-                       {
-                               zone = 'J';
-                       }
-                       else if ((zone == '-') || (zone == '+'))
-                       {
-                               if (n >= 8) utc_offset += (3600 * h);
-                               if (n >= 9) utc_offset += (60 * m);
-                               if (n == 10) utc_offset += s;
-                               if (zone == '-') utc_offset *= -1;
-                       }
-                       else if ((zone >= 'A') && (zone <= 'Z'))
-                       {
-                               if (zone < 'J') utc_offset = 3600 * ((zone - 'A') + 1);
-                               else if ((zone >= 'K') && (zone <= 'M')) utc_offset = 3600 * (zone - 'A');
-                               else if (zone <= 'Y') utc_offset = -3600 * ((zone - 'N') + 1);
-                       }
-                       else if ((zone >= 'a') && (zone <= 'z'))
-                       {
-                               if (zone < 'j') utc_offset = 3600 * ((zone - 'a') + 1);
-                               else if ((zone >= 'k') && (zone <= 'm')) utc_offset = 3600 * (zone - 'a');
-                               else if (zone <= 'y') utc_offset = -3600 * ((zone - 'n') + 1);
-                       }
-                       else
-                       {
-                               free(stamp);
-                               continue;
-                       }
+               fstyle = STAMP_STYLE_NULL;
+               if (flags & MODULE_FLAG_STYLE_SEC) fstyle = STAMP_STYLE_SEC;
+               else if (flags & MODULE_FLAG_STYLE_SEQ) fstyle = STAMP_STYLE_SEQ;
+               else if ((flags & MODULE_FLAG_STYLE_UTC) || (flags & MODULE_FLAG_STYLE_LCL)) fstyle = STAMP_STYLE_UTC_OR_LCL;
+               else if ((flags & MODULE_FLAG_STYLE_UTC_B) || (flags & MODULE_FLAG_STYLE_LCL_B)) fstyle = STAMP_STYLE_UTC_OR_LCL;
 
-                       t.tm_year -= 1900;
-                       t.tm_mon -= 1;
-                       t.tm_sec += utc_offset;
-                       t.tm_isdst = -1;
+               /*
+                * accept the file if:
+                * style is STAMP_STYLE_NULL (no timestamp)
+                * src is true and style is STAMP_STYLE_SEC
+                * actual style matches the style implied by the input flags
+                */
 
-                       if ((zone == 'J') || (zone == 'j')) ftime = mktime(&t);
-                       else ftime = timegm(&t);
-               }
+               check = false;
+               if (pstyle == STAMP_STYLE_NULL) check = true;
+               if ((pstyle == STAMP_STYLE_SEC) && src) check = true;
+               if (pstyle == fstyle) check = true;
 
-               free(stamp);
+               if (!check) continue;
 
                x = (asl_out_file_list_t *)calloc(1, sizeof(asl_out_file_list_t));
                if (x == NULL)
@@ -2164,14 +2332,14 @@ asl_list_log_files(const char *dir, const char *base, bool src, uint32_t flags)
                if (stat(path, &sb) == 0)
                {
                        x->size = sb.st_size;
-                       if (flags & MODULE_FLAG_STYLE_SEQ)
+                       if (pstyle == STAMP_STYLE_SEQ)
                        {
                                x->ftime = sb.st_birthtimespec.tv_sec;
                                if (x->ftime == 0) x->ftime = sb.st_mtimespec.tv_sec;
                        }
                }
 
-               if (flags & MODULE_FLAG_STYLE_SEQ)
+               if (pstyle == STAMP_STYLE_SEQ)
                {
                        if (out == NULL)
                        {
@@ -2258,7 +2426,7 @@ asl_list_src_files(asl_out_dst_data_t *dst)
 
        /*
         * MODULE_FLAG_EXTERNAL means some process other than syslogd writes the file.
-        * We simply check for its existence.
+        * We check for its existence, and that it is non-zero in size.
         */
        if (dst->flags & MODULE_FLAG_EXTERNAL)
        {
@@ -2268,7 +2436,7 @@ asl_list_src_files(asl_out_dst_data_t *dst)
 
                if (stat(dst->path, &sb) == 0)
                {
-                       if (S_ISREG(sb.st_mode))
+                       if (S_ISREG(sb.st_mode) && (sb.st_size != 0))
                        {
                                out = (asl_out_file_list_t *)calloc(1, sizeof(asl_out_file_list_t));
                                if (out != NULL)
@@ -2350,3 +2518,78 @@ asl_list_dst_files(asl_out_dst_data_t *dst)
 
        return out;
 }
+
+static int
+asl_secure_open_dir(const char *path)
+{
+       int fd, i;
+       char **path_parts;
+
+       if (path == NULL) return -1;
+       if (path[0] != '/') return -1;
+
+       path_parts = explode(path + 1, "/");
+       if (path_parts == NULL) return 0;
+
+       fd = open("/", O_RDONLY | O_NOFOLLOW, 0);
+       if (fd < 0)
+       {
+               free_string_list(path_parts);
+               return -1;
+       }
+
+       for (i = 0; path_parts[i] != NULL; i++)
+       {
+               int fd_next, status;
+               struct stat sb;
+
+               fd_next = openat(fd, path_parts[i], O_RDONLY | O_NOFOLLOW, 0);
+               close(fd);
+               fd = fd_next;
+               if (fd < 0)
+               {
+                       free_string_list(path_parts);
+                       return -1;
+               }
+
+               memset(&sb, 0, sizeof(sb));
+
+               status = fstat(fd, &sb);
+               if (status < 0)
+               {
+                       free_string_list(path_parts);
+                       return -1;
+               }
+
+               if (!S_ISDIR(sb.st_mode))
+               {
+                       free_string_list(path_parts);
+                       return -1;
+               }
+       }
+
+       free_string_list(path_parts);
+       return fd;
+}
+
+int
+asl_secure_chown_chmod_dir(const char *path, uid_t uid, gid_t gid, mode_t mode)
+{
+       int fd, status;
+       fd = asl_secure_open_dir(path);
+       if (fd < 0) return fd;
+
+       status = fchown(fd, uid, gid);
+       if (status < 0)
+       {
+               close(fd);
+               return -1;
+       }
+
+       if (mode >= 0) status = fchmod(fd, mode);
+       close(fd);
+
+       if (status < 0) return -1;
+       return 0;
+}