+/*
+ * Copyright (c) 2015 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include <asl.h>
+#include <asl_private.h>
+#include <asl_core.h>
+#include <asl_file.h>
+#include <asl_store.h>
+#include <copyfile.h>
+#include <time.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <zlib.h>
+#include <dirent.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <servers/bootstrap.h>
+#include <bootstrap_priv.h>
+#include <mach/mach.h>
+#include <fcntl.h>
+#include <sys/attr.h>
+#include <dispatch/dispatch.h>
+#include <xpc/xpc.h>
+#include <xpc/private.h>
+#include <os/assumes.h>
+#include <vproc_priv.h>
+#include <libkern/OSAtomic.h>
+#include "daemon.h"
+
+/* global */
+extern bool dryrun;
+extern uint32_t debug;
+extern FILE *debugfp;
+extern dispatch_queue_t work_queue;
+
+static mach_port_t asl_server_port;
+static aslclient aslc;
+static int asl_aux_fd = -1;
+
+extern kern_return_t _asl_server_query
+(
+ mach_port_t server,
+ caddr_t request,
+ mach_msg_type_number_t requestCnt,
+ uint64_t startid,
+ int count,
+ int flags,
+ caddr_t *reply,
+ mach_msg_type_number_t *replyCnt,
+ uint64_t *lastid,
+ int *status,
+ security_token_t *token
+ );
+
+const char *
+keep_str(uint8_t mask)
+{
+ static char str[9];
+ uint32_t x = 0;
+
+ memset(str, 0, sizeof(str));
+ if (mask & 0x01) str[x++] = '0';
+ if (mask & 0x02) str[x++] = '1';
+ if (mask & 0x04) str[x++] = '2';
+ if (mask & 0x08) str[x++] = '3';
+ if (mask & 0x10) str[x++] = '4';
+ if (mask & 0x20) str[x++] = '5';
+ if (mask & 0x40) str[x++] = '6';
+ if (mask & 0x80) str[x++] = '7';
+ if (x == 0) str[x++] = '-';
+ return str;
+}
+
+void
+set_debug(int flag, const char *str)
+{
+ int level, x;
+
+ if (str == NULL) x = ASL_LEVEL_ERR;
+ else if (((str[0] == 'L') || (str[0] == 'l')) && ((str[1] >= '0') && (str[1] <= '7')) && (str[2] == '\0')) x = atoi(str+1);
+ else if ((str[0] >= '0') && (str[0] <= '7') && (str[1] == '\0')) x = ASL_LEVEL_CRIT + atoi(str);
+ else x = ASL_LEVEL_ERR;
+
+ if (x <= 0) x = 0;
+ else if (x > 7) x = 7;
+
+ level = debug & DEBUG_LEVEL_MASK;
+ if (x > level) level = x;
+
+ debug = debug & DEBUG_FLAG_MASK;
+ debug |= flag;
+ debug |= level;
+}
+
+void
+debug_log(int level, char *str, ...)
+{
+ va_list v;
+ char ts[32];
+
+ time_t now = time(NULL);
+ memset(ts, 0, sizeof(ts));
+ strftime(ts, sizeof(ts), "%b %e %T", localtime(&now));
+
+ if ((debug & DEBUG_STDERR) && (level <= (debug & DEBUG_LEVEL_MASK)))
+ {
+ fprintf(stderr, "%s: ", ts);
+ va_start(v, str);
+ vfprintf(stderr, str, v);
+ va_end(v);
+ }
+
+ if ((debug & DEBUG_FILE) && (debugfp != NULL))
+ {
+ fprintf(debugfp, "%s: ", ts);
+ va_start(v, str);
+ vfprintf(debugfp, str, v);
+ va_end(v);
+ }
+
+ if (debug & DEBUG_ASL)
+ {
+ char *line = NULL;
+
+ if (aslc == NULL)
+ {
+ aslc = asl_open("aslmanager", "syslog", 0);
+ asl_msg_t *msg = asl_msg_new(ASL_TYPE_MSG);
+
+ asl_msg_set_key_val(msg, ASL_KEY_MSG, "Status Report");
+ asl_msg_set_key_val(msg, ASL_KEY_LEVEL, ASL_STRING_NOTICE);
+ asl_create_auxiliary_file((asl_object_t)msg, "Status Report", "public.text", &asl_aux_fd);
+ asl_msg_release(msg);
+ }
+
+ va_start(v, str);
+ vasprintf(&line, str, v);
+ va_end(v);
+
+ if (line != NULL)
+ {
+ write(asl_aux_fd, ts, strlen(ts));
+ write(asl_aux_fd, line, strlen(line));
+ }
+
+ free(line);
+ }
+}
+
+void
+debug_close()
+{
+ if (asl_aux_fd >= 0) asl_close_auxiliary_file(asl_aux_fd);
+ if (debugfp != NULL) fclose(debugfp);
+}
+
+name_list_t *
+add_to_name_list(name_list_t *l, const char *name, size_t size, uint32_t flags)
+{
+ name_list_t *e, *x;
+
+ if (name == NULL) return l;
+
+ e = (name_list_t *)calloc(1, sizeof(name_list_t));
+ if (e == NULL) return NULL;
+
+ e->name = strdup(name);
+ if (e->name == NULL)
+ {
+ free(e);
+ return NULL;
+ }
+
+ e->size = size;
+ e->flags = flags;
+
+ /* list is sorted by name (i.e. primarily by timestamp) */
+ if (l == NULL) return e;
+
+ if (strcmp(e->name, l->name) <= 0)
+ {
+ e->next = l;
+ return e;
+ }
+
+ for (x = l; (x->next != NULL) && (strcmp(e->name, x->next->name) > 0) ; x = x->next);
+
+ e->next = x->next;
+ x->next = e;
+ return l;
+}
+
+void
+free_name_list(name_list_t *l)
+{
+ name_list_t *e;
+
+ while (l != NULL)
+ {
+ e = l;
+ l = l->next;
+ free(e->name);
+ free(e);
+ }
+
+ free(l);
+}
+
+int
+copy_compress_file(asl_out_dst_data_t *asldst, const char *src, const char *dst)
+{
+ int in, out;
+ size_t n;
+ gzFile gz;
+ char buf[IOBUFSIZE];
+
+ in = open(src, O_RDONLY, 0);
+ if (in < 0) return -1;
+
+ out = open(dst, O_WRONLY | O_CREAT, asldst->mode & 0666);
+ if (out >= 0) out = asl_out_dst_set_access(out, asldst);
+ if (out < 0)
+ {
+ close(in);
+ return -1;
+ }
+
+ gz = gzdopen(out, "w");
+ if (gz == NULL)
+ {
+ close(in);
+ close(out);
+ return -1;
+ }
+
+ do {
+ n = read(in, buf, sizeof(buf));
+ if (n > 0) gzwrite(gz, buf, n);
+ } while (n == IOBUFSIZE);
+
+ gzclose(gz);
+ close(in);
+ close(out);
+
+ return 0;
+}
+
+void
+filesystem_rename(const char *src, const char *dst)
+{
+ int status = 0;
+
+ debug_log(ASL_LEVEL_NOTICE, " rename %s ---> %s\n", src, dst);
+ if (dryrun) return;
+
+ status = rename(src, dst);
+ if (status != 0) debug_log(ASL_LEVEL_ERR, " FAILED status %d errno %d [%s] rename %s ---> %s\n", status, errno, strerror(errno), src, dst);
+}
+
+void
+filesystem_unlink(const char *path)
+{
+ int status = 0;
+
+ debug_log(ASL_LEVEL_NOTICE, " remove %s\n", path);
+ if (dryrun) return;
+
+ status = unlink(path);
+ if (status != 0) debug_log(ASL_LEVEL_ERR, " FAILED status %d errno %d [%s] unlink %s\n", status, errno, strerror(errno), path);
+}
+
+void
+filesystem_truncate(const char *path)
+{
+ int status = 0;
+
+ debug_log(ASL_LEVEL_NOTICE, " truncate %s\n", path);
+ if (dryrun) return;
+
+ status = truncate(path, 0);
+ if (status != 0) debug_log(ASL_LEVEL_ERR, " FAILED status %d errno %d [%s] unlink %s\n", status, errno, strerror(errno), path);
+}
+
+void
+filesystem_rmdir(const char *path)
+{
+ int status = 0;
+
+ debug_log(ASL_LEVEL_NOTICE, " remove directory %s\n", path);
+ if (dryrun) return;
+
+ status = rmdir(path);
+ if (status != 0) debug_log(ASL_LEVEL_ERR, " FAILED status %d errno %d [%s] rmdir %s\n", status, errno, strerror(errno), path);
+}
+
+/*
+ * Copy ASL files by reading and writing each record.
+ */
+static uint32_t
+copy_asl_file(const char *src, const char *dst, mode_t mode)
+{
+ asl_file_t *fin, *fout;
+ uint32_t status;
+
+ if (src == NULL) return ASL_STATUS_INVALID_ARG;
+ if (dst == NULL) return ASL_STATUS_INVALID_ARG;
+
+ fin = NULL;
+ status = asl_file_open_read(src, &fin);
+ if (status != ASL_STATUS_OK) return status;
+
+ fout = NULL;
+ status = asl_file_open_write(dst, mode, -1, -1, &fout);
+ if (status != ASL_STATUS_OK)
+ {
+ asl_file_close(fin);
+ return status;
+ }
+
+ if (fout == NULL)
+ {
+ asl_file_close(fin);
+ return ASL_STATUS_FAILED;
+ }
+
+ fout->flags = ASL_FILE_FLAG_PRESERVE_MSG_ID;
+
+ status = asl_file_read_set_position(fin, ASL_FILE_POSITION_FIRST);
+ if (status != ASL_STATUS_OK)
+ {
+ asl_file_close(fin);
+ asl_file_close(fout);
+ return ASL_STATUS_READ_FAILED;
+ }
+
+ while (status == ASL_STATUS_OK)
+ {
+ uint64_t mid = 0;
+ asl_msg_t *msg = NULL;
+
+ status = asl_file_fetch_next(fin, &msg);
+ if (msg == NULL)
+ {
+ status = ASL_STATUS_OK;
+ break;
+ }
+
+ if (status != ASL_STATUS_OK) break;
+
+ status = asl_file_save(fout, msg, &mid);
+ asl_msg_release(msg);
+ }
+
+ asl_file_close(fin);
+ asl_file_close(fout);
+
+ return status;
+}
+
+int32_t
+filesystem_copy(asl_out_dst_data_t *asldst, const char *src, const char *dst, uint32_t flags)
+{
+ char *dot;
+
+ if ((src == NULL) || (dst == NULL)) return 0;
+
+ dot = strrchr(src, '.');
+ if ((dot != NULL) && (!strcmp(dot, ".gz"))) flags &= ~MODULE_FLAG_COMPRESS;
+
+ if (((flags & MODULE_FLAG_COMPRESS) == 0) && (!strcmp(src, dst))) return 0;
+
+ if (flags & MODULE_FLAG_TYPE_ASL) debug_log(ASL_LEVEL_NOTICE, " copy asl %s ---> %s\n", src, dst);
+ else if (flags & MODULE_FLAG_COMPRESS) debug_log(ASL_LEVEL_NOTICE, " copy compress %s ---> %s.gz\n", src, dst);
+ else debug_log(ASL_LEVEL_NOTICE, " copy %s ---> %s\n", src, dst);
+
+ if (dryrun) return 0;
+
+ if (flags & MODULE_FLAG_TYPE_ASL)
+ {
+ uint32_t status = copy_asl_file(src, dst, asldst->mode);
+ if (status != 0)
+ {
+ debug_log(ASL_LEVEL_ERR, " FAILED status %u [%s] asl copy %s ---> %s\n", status, asl_core_error(status), src, dst);
+ return 0;
+ }
+ }
+ else if (flags & MODULE_FLAG_COMPRESS)
+ {
+ char gzdst[MAXPATHLEN];
+
+ snprintf(gzdst, sizeof(gzdst), "%s.gz", dst);
+
+ int status = copy_compress_file(asldst, src, gzdst);
+ if (status != 0)
+ {
+ debug_log(ASL_LEVEL_ERR, " FAILED status %d errno %d [%s] copy & compress %s ---> %s\n", status, errno, strerror(errno), src, dst);
+ return 0;
+ }
+ }
+ else
+ {
+ int status = copyfile(src, dst, NULL, COPYFILE_ALL | COPYFILE_RECURSIVE);
+ if (status != 0)
+ {
+ debug_log(ASL_LEVEL_ERR, " FAILED status %d errno %d [%s] copy %s ---> %s\n", status, errno, strerror(errno), src, dst);
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+int32_t
+filesystem_reset_ctime(const char *path)
+{
+ struct attrlist attr_list;
+ struct timespec now;
+
+ debug_log(ASL_LEVEL_NOTICE, " reset ctime %s\n", path);
+
+ memset(&attr_list, 0, sizeof(attr_list));
+ attr_list.bitmapcount = ATTR_BIT_MAP_COUNT;
+ attr_list.commonattr = ATTR_CMN_CRTIME;
+
+ memset(&now, 0, sizeof(now));
+ now.tv_sec = time(NULL);
+
+ return setattrlist(path, &attr_list, &now, sizeof(now), 0);
+}
+
+int
+remove_directory(const char *path)
+{
+ DIR *dp;
+ struct dirent *dent;
+ char *str;
+
+ dp = opendir(path);
+ if (dp == NULL) return 0;
+
+ while ((dent = readdir(dp)) != NULL)
+ {
+ if ((!strcmp(dent->d_name, ".")) || (!strcmp(dent->d_name, ".."))) continue;
+ asprintf(&str, "%s/%s", path, dent->d_name);
+ if (str != NULL)
+ {
+ filesystem_unlink(str);
+ free(str);
+ str = NULL;
+ }
+ }
+
+ closedir(dp);
+ filesystem_rmdir(path);
+
+ return 0;
+}
+
+size_t
+directory_size(const char *path)
+{
+ DIR *dp;
+ struct dirent *dent;
+ struct stat sb;
+ size_t size;
+ char *str;
+
+ dp = opendir(path);
+ if (dp == NULL) return 0;
+
+ size = 0;
+ while ((dent = readdir(dp)) != NULL)
+ {
+ if ((!strcmp(dent->d_name, ".")) || (!strcmp(dent->d_name, ".."))) continue;
+
+ memset(&sb, 0, sizeof(struct stat));
+ str = NULL;
+ asprintf(&str, "%s/%s", path, dent->d_name);
+
+ if ((str != NULL) && (stat(str, &sb) == 0) && S_ISREG(sb.st_mode))
+ {
+ size += sb.st_size;
+ free(str);
+ }
+ }
+
+ closedir(dp);
+ return size;
+}
+
+time_t
+parse_ymd_name(const char *name)
+{
+ struct tm ftime;
+ time_t created;
+ int32_t tzh, tzm, sign = -1;
+ const char *x;
+ bool legacy = false;
+
+ if (name == NULL) return -1;
+
+ x = name;
+
+ if ((*x == 'T') || (*x == 't'))
+ {
+ x++;
+ created = atol(x);
+ if ((created == 0) && (*x != '0')) return -1;
+
+ x = strchr(x, '.');
+ if (x == NULL) return -1;
+
+ return created;
+ }
+
+ memset(&ftime, 0, sizeof(ftime));
+
+ if ((*x < '0') || (*x > '9')) return 1;
+ ftime.tm_year = 1000 * (*x++ - '0');
+
+ if ((*x < '0') || (*x > '9')) return 1;
+ ftime.tm_year += 100 * (*x++ - '0');
+
+ if ((*x < '0') || (*x > '9')) return 1;
+ ftime.tm_year += 10 * (*x++ - '0');
+
+ if ((*x < '0') || (*x > '9')) return 1;
+ ftime.tm_year += *x++ - '0';
+ ftime.tm_year -= 1900;
+
+ if (*x == '-') x++;
+ else if (*x == '.')
+ {
+ x++;
+ legacy = true;
+ }
+
+ if ((*x < '0') || (*x > '9')) return 1;
+ ftime.tm_mon = 10 * (*x++ - '0');
+
+ if ((*x < '0') || (*x > '9')) return 1;
+ ftime.tm_mon += *x++ - '0';
+ ftime.tm_mon -= 1;
+
+ if ((*x == '-') || (*x == '.')) x++;
+
+ if ((*x < '0') || (*x > '9')) return 1;
+ ftime.tm_mday = 10 * (*x++ - '0');
+
+ if ((*x < '0') || (*x > '9')) return 1;
+ ftime.tm_mday += *x++ - '0';
+
+ if (legacy)
+ {
+ if ((*x != '.') && (*x != '\0')) return -1;
+
+ /* assume the file was created at midnight */
+ ftime.tm_hour = 24;
+ ftime.tm_min = 0;
+ ftime.tm_sec = 0;
+ ftime.tm_isdst = -1;
+
+ created = mktime(&ftime);
+ return created;
+ }
+
+ if ((*x != 'T') && (*x != 't')) return 1;
+ x++;
+
+ if ((*x < '0') || (*x > '9')) return 1;
+ ftime.tm_hour = 10 * (*x++ - '0');
+
+ if ((*x < '0') || (*x > '9')) return 1;
+ ftime.tm_hour += *x++ - '0';
+
+ if (*x == ':') x++;
+
+ if ((*x < '0') || (*x > '9')) return 1;
+ ftime.tm_min = 10 * (*x++ - '0');
+
+ if ((*x < '0') || (*x > '9')) return 1;
+ ftime.tm_min += *x++ - '0';
+
+ if (*x == ':') x++;
+
+ if ((*x < '0') || (*x > '9')) return 1;
+ ftime.tm_sec = 10 * (*x++ - '0');
+
+ if ((*x < '0') || (*x > '9')) return 1;
+ ftime.tm_sec += *x++ - '0';
+
+ if ((*x == 'Z') || (*x == 'z'))
+ {
+ created = timegm(&ftime);
+ return created;
+ }
+
+ if ((*x != '+') && (*x != '-')) return 1;
+
+ if (*x == '-') sign = 1;
+ x++;
+
+ if ((*x < '0') || (*x > '9')) return 1;
+ tzh = 10 * (*x++ - '0');
+
+ if ((*x < '0') || (*x > '9')) tzh /= 10;
+ else tzh += *x++ - '0';
+
+ if (tzh > 23) return 1;
+
+ tzm = 0;
+ if ((*x == ':') || ((*x >= '0') && (*x <= '9')))
+ {
+ if (*x != ':') tzm = 10 * (*x - '0');
+ x++;
+
+ if ((*x < '0') || (*x > '9'))return -1;
+ tzm += *x++ - '0';
+
+ if (tzm > 59) return -1;
+ }
+
+ ftime.tm_sec += (sign * (tzh * SECONDS_PER_HOUR) + (tzm * SECONDS_PER_MINUTE));
+
+ if ((*x != '.') && (*x != '\0')) return -1;
+
+ created = timegm(&ftime);
+ return created;
+}
+
+
+/*
+ * Determine the age (in seconds) of a YMD file from its name.
+ * Also determines UID and GID from ".Unnn.Gnnn" part of file name.
+ */
+uint32_t
+ymd_file_age(const char *name, time_t now, uid_t *u, gid_t *g)
+{
+ struct tm ftime;
+ time_t created;
+ uint32_t seconds;
+ const char *p;
+
+ if (name == NULL) return 0;
+
+ if (now == 0) now = time(NULL);
+
+ memset(&ftime, 0, sizeof(struct tm));
+
+ created = parse_ymd_name(name);
+ if (created < 0) return 0;
+ if (created > now) return 0;
+ seconds = now - created;
+
+ if (u != NULL)
+ {
+ *u = -1;
+ p = strchr(name, 'U');
+ if (p != NULL) *u = atoi(p+1);
+ }
+
+ if (g != NULL)
+ {
+ *g = -1;
+ p = strchr(name, 'G');
+ if (p != NULL) *g = atoi(p+1);
+ }
+
+ return seconds;
+}
+
+
+static void
+aux_url_callback(const char *url)
+{
+ if (url == NULL) return;
+ if (!strncmp(url, AUX_URL_MINE, AUX_URL_MINE_LEN)) filesystem_unlink(url + AUX_URL_PATH_OFFSET);
+}
+
+uint32_t
+ymd_file_filter(const char *name, const char *path, uint32_t keep_mask, mode_t ymd_mode, uid_t ymd_uid, gid_t ymd_gid)
+{
+ asl_file_t *f = NULL;
+ uint8_t km = keep_mask;
+ uint32_t status, len, dstcount = 0;
+ char src[MAXPATHLEN];
+ char dst[MAXPATHLEN];
+
+ if (snprintf(src, MAXPATHLEN, "%s/%s", path, name) >= MAXPATHLEN) return ASL_STATUS_FAILED;
+ if (snprintf(dst, MAXPATHLEN, "%s/%s", path, name) >= MAXPATHLEN) return ASL_STATUS_FAILED;
+ len = strlen(src) - 3;
+ snprintf(dst + len, 4, "tmp");
+
+ //TODO: check if src file is already filtered
+ debug_log(ASL_LEVEL_NOTICE, " filter %s %s ---> %s\n", src, keep_str(km), dst);
+
+ status = ASL_STATUS_OK;
+
+ if (!dryrun)
+ {
+ status = asl_file_open_read(name, &f);
+ if (status != ASL_STATUS_OK) return status;
+
+ status = asl_file_filter_level(f, dst, keep_mask, ymd_mode, ymd_uid, ymd_gid, &dstcount, aux_url_callback);
+ asl_file_close(f);
+ }
+
+ filesystem_unlink(src);
+ if ((status != ASL_STATUS_OK) || (dstcount == 0)) filesystem_unlink(dst);
+ else filesystem_rename(dst, src);
+
+ return status;
+}
+
+int
+process_asl_data_store(asl_out_dst_data_t *dst, asl_out_dst_data_t *opts)
+{
+ time_t now, midnight, since_midnight;
+ char *str;
+ DIR *dp;
+ struct dirent *dent;
+ name_list_t *ymd_list, *bb_list, *aux_list, *bb_aux_list, *e;
+ size_t file_size, store_size;
+ struct stat sb;
+ char tstr[128];
+ struct tm t_tmp;
+ uint32_t ttl = 0;
+
+ ymd_list = NULL;
+ bb_list = NULL;
+ aux_list = NULL;
+ bb_aux_list = NULL;
+ store_size = 0;
+
+ if (dst == NULL) return 0;
+ if (dst->path == NULL) return 0;
+
+ ttl = dst->ttl[LEVEL_ALL];
+ if ((opts != NULL) && (opts->ttl[LEVEL_ALL] > 0)) ttl = opts->ttl[LEVEL_ALL];
+
+ size_t all_max = dst->all_max;
+ if ((opts != NULL) && (opts->all_max > 0)) all_max = opts->all_max;
+
+ debug_log(ASL_LEVEL_NOTICE, "----------------------------------------\n");
+ debug_log(ASL_LEVEL_NOTICE, "Processing data store %s\n", dst->path);
+
+ if (dst->rotate_dir != NULL)
+ {
+ /* check archive */
+ memset(&sb, 0, sizeof(struct stat));
+ if (stat(dst->rotate_dir, &sb) == 0)
+ {
+ /* must be a directory */
+ if (!S_ISDIR(sb.st_mode))
+ {
+ debug_log(ASL_LEVEL_ERR, "aslmanager error: archive %s is not a directory", dst->rotate_dir);
+ return -1;
+ }
+ }
+ else
+ {
+ if (errno == ENOENT)
+ {
+ /* archive doesn't exist - create it */
+ if (mkdir(dst->rotate_dir, 0755) != 0)
+ {
+ debug_log(ASL_LEVEL_ERR, "aslmanager error: can't create archive %s: %s\n", dst->rotate_dir, strerror(errno));
+ return -1;
+ }
+ }
+ else
+ {
+ /* stat failed for some other reason */
+ debug_log(ASL_LEVEL_ERR, "aslmanager error: can't stat archive %s: %s\n", dst->rotate_dir, strerror(errno));
+ return -1;
+ }
+ }
+ }
+
+ chdir(dst->path);
+
+ /* determine current time */
+ now = time(NULL);
+
+ localtime_r(&now, &t_tmp);
+
+ t_tmp.tm_sec = 0;
+ t_tmp.tm_min = 0;
+ t_tmp.tm_hour = 0;
+
+ midnight = mktime(&t_tmp);
+ since_midnight = now - midnight;
+
+ dp = opendir(dst->path);
+ if (dp == NULL) return -1;
+
+ /* gather a list of YMD files, AUX dirs, BB.AUX dirs, and BB files */
+ while ((dent = readdir(dp)) != NULL)
+ {
+ uint32_t file_flags = 0;
+ char *dot = NULL;
+
+ memset(&sb, 0, sizeof(struct stat));
+ file_size = 0;
+ if (stat(dent->d_name, &sb) == 0) file_size = sb.st_size;
+
+ dot = strrchr(dent->d_name, '.');
+ if ((dot != NULL) && !strcmp(dot, ".gz")) file_flags |= NAME_LIST_FLAG_COMPRESSED;
+
+ if ((dent->d_name[0] >= '0') && (dent->d_name[0] <= '9'))
+ {
+ ymd_list = add_to_name_list(ymd_list, dent->d_name, file_size, file_flags);
+ store_size += file_size;
+ }
+ else if (((dent->d_name[0] == 'T') || (dent->d_name[0] == 't')) && (dent->d_name[1] >= '0') && (dent->d_name[1] <= '9'))
+ {
+ ymd_list = add_to_name_list(ymd_list, dent->d_name, file_size, file_flags);
+ store_size += file_size;
+ }
+ else if (!strncmp(dent->d_name, "AUX.", 4) && (dent->d_name[4] >= '0') && (dent->d_name[4] <= '9') && S_ISDIR(sb.st_mode))
+ {
+ file_size = directory_size(dent->d_name);
+ aux_list = add_to_name_list(aux_list, dent->d_name, file_size, file_flags);
+ store_size += file_size;
+ }
+ else if (!strncmp(dent->d_name, "BB.AUX.", 7) && (dent->d_name[7] >= '0') && (dent->d_name[7] <= '9') && S_ISDIR(sb.st_mode))
+ {
+ file_size = directory_size(dent->d_name);
+ bb_aux_list = add_to_name_list(bb_aux_list, dent->d_name, file_size, file_flags);
+ store_size += file_size;
+ }
+ else if (!strncmp(dent->d_name, "BB.", 3) && (dent->d_name[3] >= '0') && (dent->d_name[3] <= '9'))
+ {
+ bb_list = add_to_name_list(bb_list, dent->d_name, file_size, file_flags);
+ store_size += file_size;
+ }
+ else if ((!strcmp(dent->d_name, ".")) || (!strcmp(dent->d_name, "..")))
+ {}
+ else if ((!strcmp(dent->d_name, "StoreData")) || (!strcmp(dent->d_name, "SweepStore")))
+ {}
+ else if (!strcmp(dent->d_name, ASL_INTERNAL_LOGS_DIR))
+ {}
+ else
+ {
+ debug_log(ASL_LEVEL_ERR, "aslmanager: unexpected file %s in ASL data store\n", dent->d_name);
+ }
+ }
+
+ closedir(dp);
+
+ debug_log(ASL_LEVEL_NOTICE, "Data Store Size = %lu\n", store_size);
+ asl_core_time_to_str(ttl, tstr, sizeof(tstr));
+ debug_log(ASL_LEVEL_NOTICE, "Data Store YMD Files (TTL = %s)\n", tstr);
+ for (e = ymd_list; e != NULL; e = e->next)
+ {
+ uint32_t age = ymd_file_age(e->name, now, NULL, NULL);
+ asl_core_time_to_str(age, tstr, sizeof(tstr));
+ debug_log(ASL_LEVEL_NOTICE, " %s %lu (age %s%s)\n", e->name, e->size, tstr, (age > ttl) ? " - expired" : "");
+ }
+
+ debug_log(ASL_LEVEL_NOTICE, "Data Store AUX Directories\n");
+ for (e = aux_list; e != NULL; e = e->next)
+ {
+ uint32_t age = ymd_file_age(e->name + 4, now, NULL, NULL) / SECONDS_PER_DAY;
+ asl_core_time_to_str(age, tstr, sizeof(tstr));
+ debug_log(ASL_LEVEL_NOTICE, " %s %lu (age %s)\n", e->name, e->size, tstr, (age > ttl) ? " - expired" : "");
+ }
+
+ debug_log(ASL_LEVEL_NOTICE, "Data Store BB.AUX Directories\n");
+ for (e = bb_aux_list; e != NULL; e = e->next)
+ {
+ uint32_t age = ymd_file_age(e->name + 7, now, NULL, NULL);
+ asl_core_time_to_str(age, tstr, sizeof(tstr));
+ debug_log(ASL_LEVEL_NOTICE, " %s %lu (age %s)\n", e->name, e->size, tstr, ((age / SECONDS_PER_DAY) > 0) ? " - expired" : "");
+ }
+
+ debug_log(ASL_LEVEL_NOTICE, "Data Store BB Files\n");
+ for (e = bb_list; e != NULL; e = e->next)
+ {
+ uint32_t age = ymd_file_age(e->name + 3, now, NULL, NULL) / SECONDS_PER_DAY;
+ asl_core_time_to_str(age, tstr, sizeof(tstr));
+ debug_log(ASL_LEVEL_NOTICE, " %s %lu (age %s)\n", e->name, e->size, tstr, ((age / SECONDS_PER_DAY) > 0) ? " - expired" : "");
+ }
+
+ /* Delete/achive expired YMD files */
+ debug_log(ASL_LEVEL_NOTICE, "Start YMD File Scan\n");
+
+ e = ymd_list;
+ while (e != NULL)
+ {
+ uid_t ymd_uid = -1;
+ gid_t ymd_gid = -1;
+ uint32_t age = ymd_file_age(e->name, now, &ymd_uid, &ymd_gid);
+
+ if (age > ttl)
+ {
+ /* file has expired, archive it if required, then unlink it */
+ if (dst->rotate_dir != NULL)
+ {
+ str = NULL;
+ asprintf(&str, "%s/%s", dst->rotate_dir, e->name);
+ if (str == NULL) return -1;
+
+ filesystem_copy(dst, e->name, str, 0);
+ free(str);
+ }
+
+ filesystem_unlink(e->name);
+ store_size -= e->size;
+ e->size = 0;
+ }
+ else if ((e->flags & NAME_LIST_FLAG_COMPRESSED) == 0)
+ {
+ uint32_t i, bit, keep_mask;
+ mode_t ymd_mode = 0600;
+
+ /* check if there are any per-level TTLs and filter the file if required */
+ if (age > 0)
+ {
+ keep_mask = 0x000000ff;
+ bit = 1;
+ for (i = 0; i <= 7; i++)
+ {
+ if ((dst->ttl[i] > 0) && (age >= dst->ttl[i])) keep_mask &= ~bit;
+ bit *= 2;
+ }
+
+ memset(&sb, 0, sizeof(struct stat));
+ if (stat(e->name, &sb) == 0) ymd_mode = sb.st_mode & 0777;
+
+ if (keep_mask != 0x000000ff) ymd_file_filter(e->name, dst->path, keep_mask, ymd_mode, ymd_uid, ymd_gid);
+ }
+
+ if ((age > since_midnight) && (dst->flags & MODULE_FLAG_COMPRESS))
+ {
+ char gzdst[MAXPATHLEN];
+
+ snprintf(gzdst, sizeof(gzdst), "%s.gz", e->name);
+ debug_log(ASL_LEVEL_NOTICE, " compress %s ---> %s\n", e->name, gzdst);
+
+ if (!dryrun)
+ {
+ int status = copy_compress_file(dst, e->name, gzdst);
+ if (status == 0)
+ {
+ filesystem_unlink(e->name);
+ }
+ else
+ {
+ debug_log(ASL_LEVEL_ERR, " FAILED status %d errno %d [%s] compress %s ---> %s\n", status, errno, strerror(errno), e->name, gzdst);
+ return 0;
+ }
+ }
+ }
+ }
+
+ e = e->next;
+ }
+
+ debug_log(ASL_LEVEL_NOTICE, "Finished YMD File Scan\n");
+
+ /* Delete/achive expired YMD AUX directories */
+ debug_log(ASL_LEVEL_NOTICE, "Start AUX Directory Scan\n");
+
+ e = aux_list;
+ while (e != NULL)
+ {
+ uint32_t age = ymd_file_age(e->name + 4, now, NULL, NULL);
+
+ if (age > ttl)
+ {
+ if (dst->rotate_dir != NULL)
+ {
+ str = NULL;
+ asprintf(&str, "%s/%s", dst->rotate_dir, e->name);
+ if (str == NULL) return -1;
+
+ filesystem_copy(dst, e->name, str, 0);
+ free(str);
+ }
+
+ remove_directory(e->name);
+ store_size -= e->size;
+ e->size = 0;
+ }
+
+ e = e->next;
+ }
+
+ debug_log(ASL_LEVEL_NOTICE, "Finished AUX Directory Scan\n");
+
+ /* Delete/achive expired BB.AUX directories */
+ debug_log(ASL_LEVEL_NOTICE, "Start BB.AUX Directory Scan\n");
+
+ e = bb_aux_list;
+ while (e != NULL)
+ {
+ uint32_t age = ymd_file_age(e->name + 7, now, NULL, NULL);
+
+ if (age > 0)
+ {
+ if (dst->rotate_dir != NULL)
+ {
+ str = NULL;
+ asprintf(&str, "%s/%s", dst->rotate_dir, e->name);
+ if (str == NULL) return -1;
+
+ filesystem_copy(dst, e->name, str, 0);
+ free(str);
+ }
+
+ remove_directory(e->name);
+ store_size -= e->size;
+ e->size = 0;
+ }
+
+ e = e->next;
+ }
+
+ debug_log(ASL_LEVEL_NOTICE, "Finished BB.AUX Directory Scan\n");
+
+ /* Delete/achive expired BB files */
+ debug_log(ASL_LEVEL_NOTICE, "Start BB Scan\n");
+
+ e = bb_list;
+ while (e != NULL)
+ {
+ uint32_t age = ymd_file_age(e->name + 3, now, NULL, NULL);
+
+ if (age > 0)
+ {
+ if (dst->rotate_dir != NULL)
+ {
+ str = NULL;
+ asprintf(&str, "%s/%s", dst->rotate_dir, e->name);
+ if (str == NULL) return -1;
+
+ /* syslog -x [str] -f [e->name] */
+ filesystem_copy(dst, e->name, str, 0);
+ free(str);
+ }
+
+ filesystem_unlink(e->name);
+ store_size -= e->size;
+ e->size = 0;
+ }
+
+ e = e->next;
+ }
+
+ debug_log(ASL_LEVEL_NOTICE, "Finished BB Scan\n");
+
+ if (all_max > 0)
+ {
+ /* if data store is over max_size, delete/archive more YMD files */
+ if (store_size > all_max) debug_log(ASL_LEVEL_NOTICE, "Additional YMD Scan\n");
+
+ e = ymd_list;
+ while ((e != NULL) && (store_size > all_max))
+ {
+ if (e->size != 0)
+ {
+ uint32_t age = ymd_file_age(e->name, now, NULL, NULL);
+ if (age == 0)
+ {
+ /* do not touch active file YYYY.MM.DD.asl */
+ e = e->next;
+ continue;
+ }
+
+ if (dst->rotate_dir != NULL)
+ {
+ str = NULL;
+ asprintf(&str, "%s/%s", dst->rotate_dir, e->name);
+ if (str == NULL) return -1;
+
+ /* syslog -x [str] -f [e->name] */
+ filesystem_copy(dst, e->name, str, 0);
+ free(str);
+ }
+
+ filesystem_unlink(e->name);
+ store_size -= e->size;
+ e->size = 0;
+ }
+
+ e = e->next;
+ }
+
+ /* if data store is over all_max, delete/archive more BB files */
+ if (store_size > all_max) debug_log(ASL_LEVEL_NOTICE, "Additional BB Scan\n");
+
+ e = bb_list;
+ while ((e != NULL) && (store_size > all_max))
+ {
+ if (e->size != 0)
+ {
+ if (dst->rotate_dir != NULL)
+ {
+ str = NULL;
+ asprintf(&str, "%s/%s", dst->rotate_dir, e->name);
+ if (str == NULL) return -1;
+
+ /* syslog -x [str] -f [e->name] */
+ filesystem_copy(dst, e->name, str, 0);
+ free(str);
+ }
+
+ filesystem_unlink(e->name);
+ store_size -= e->size;
+ e->size = 0;
+ }
+
+ e = e->next;
+ }
+ }
+
+ free_name_list(ymd_list);
+ free_name_list(bb_list);
+ free_name_list(aux_list);
+ free_name_list(bb_aux_list);
+
+ debug_log(ASL_LEVEL_NOTICE, "Data Store Size = %lu\n", store_size);
+
+ return 0;
+}
+
+static asl_out_file_list_t *
+_remove_youngest_activity_tracing_file(asl_out_file_list_t *l)
+{
+ asl_out_file_list_t *f;
+
+ /* ignore youngest activity tracing file - it is the active file */
+ if (l->next == NULL)
+ {
+ debug_log(ASL_LEVEL_INFO, " ignore youngest (only) activity tracing file %s\n", l->name);
+ asl_out_file_list_free(l);
+ return NULL;
+ }
+
+ for (f = l; f->next->next != NULL; f = f->next);
+ debug_log(ASL_LEVEL_INFO, " ignore youngest activity tracing file %s\n", f->next->name);
+ asl_out_file_list_free(f->next);
+ f->next = NULL;
+ return l;
+}
+
+/* move sequenced source files to dst dir, renaming as we go */
+int
+module_copy_rename(asl_out_dst_data_t *dst)
+{
+ asl_out_file_list_t *src_list, *dst_list, *f;
+ char *dst_dir;
+ char fpathsrc[MAXPATHLEN], fpathdst[MAXPATHLEN];
+ uint32_t src_count, dst_count;
+ int32_t x, moved;
+
+ if (dst == NULL) return -1;
+ if (dst->path == NULL) return -1;
+
+ src_list = asl_list_src_files(dst);
+
+ /*
+ * Note: the unmarked file (e.g. system.log) is included in src_list.
+ * If it is from a MODULE_FLAG_EXTERNAL dst and it is less than 24 hours old,
+ * we ignore it. If it is not external, we also ignore it since syslogd will
+ * checkpoint it to create system.log.Tnnnnnnnnnn.
+ */
+ if ((src_list != NULL) && (src_list->stamp == STAMP_STYLE_NULL))
+ {
+ bool ignore_it = false;
+
+ if (dst->flags & MODULE_FLAG_EXTERNAL)
+ {
+ if ((time(NULL) - src_list->ftime) < SECONDS_PER_DAY)
+ {
+ debug_log(ASL_LEVEL_INFO, " ignore src file %s since it is external and less than a day old\n", src_list->name);
+ ignore_it = true;
+ }
+ }
+ else
+ {
+ debug_log(ASL_LEVEL_INFO, " ignore src file %s since it is internal and syslogd will checkpoint it when it needs to be renamed\n", src_list->name);
+ ignore_it = true;
+ }
+
+ if (ignore_it)
+ {
+ asl_out_file_list_t *first = src_list;
+ src_list = src_list->next;
+ first->next = NULL;
+ asl_out_file_list_free(first);
+ }
+ }
+
+ if (src_list == NULL)
+ {
+ debug_log(ASL_LEVEL_INFO, " no src files\n");
+ return 0;
+ }
+
+ debug_log(ASL_LEVEL_INFO, " src files\n");
+
+ src_count = 0;
+ for (f = src_list; f != NULL; f = f->next)
+ {
+ debug_log(ASL_LEVEL_INFO, " %s\n", f->name);
+ src_count++;
+ }
+
+ dst_list = asl_list_dst_files(dst);
+
+ if ((dst_list != NULL) && (dst->flags & MODULE_FLAG_ACTIVITY))
+ {
+ dst_list = _remove_youngest_activity_tracing_file(dst_list);
+ }
+
+ dst_dir = dst->rotate_dir;
+ if (dst_dir == NULL) dst_dir = dst->dir;
+
+ dst_count = 0;
+
+ if (dst_list == NULL) debug_log(ASL_LEVEL_INFO, " no dst files\n");
+ else debug_log(ASL_LEVEL_INFO, " dst files\n");
+
+ for (f = dst_list; f != NULL; f = f->next)
+ {
+ debug_log(ASL_LEVEL_INFO, " %s\n", f->name);
+ dst_count++;
+ }
+
+ if (dst->style_flags & MODULE_NAME_STYLE_STAMP_SEQ)
+ {
+ for (f = dst_list; f != NULL; f = f->next)
+ {
+ int is_gz = 0;
+ char *dot = strrchr(f->name, '.');
+ if ((dot != NULL) && (!strcmp(dot, ".gz"))) is_gz = 1;
+
+ snprintf(fpathsrc, sizeof(fpathsrc), "%s/%s", dst_dir, f->name);
+
+ if (dst->style_flags & MODULE_NAME_STYLE_FORMAT_BS)
+ {
+ snprintf(fpathdst, sizeof(fpathdst), "%s/%s.%d%s", dst_dir, dst->base, f->seq+src_count, (is_gz == 1) ? ".gz" : "");
+ }
+ else if (dst->style_flags & MODULE_NAME_STYLE_FORMAT_BES)
+ {
+ snprintf(fpathdst, sizeof(fpathdst), "%s/%s.%s.%d%s", dst_dir, dst->base, dst->ext, f->seq+src_count, (is_gz == 1) ? ".gz" : "");
+ }
+ else if (dst->style_flags & MODULE_NAME_STYLE_FORMAT_BSE)
+ {
+ snprintf(fpathdst, sizeof(fpathdst), "%s/%s.%d.%s%s", dst_dir, dst->base, f->seq+src_count, dst->ext, (is_gz == 1) ? ".gz" : "");
+ }
+
+ filesystem_rename(fpathsrc, fpathdst);
+ }
+
+ for (f = src_list, x = 0; f != NULL; f = f->next, x++)
+ {
+ snprintf(fpathsrc, sizeof(fpathsrc), "%s/%s", dst->dir, f->name);
+
+ if (dst->style_flags & MODULE_NAME_STYLE_FORMAT_BS)
+ {
+ snprintf(fpathdst, sizeof(fpathdst), "%s/%s.%d", dst_dir, dst->base, x);
+ }
+ else if (dst->style_flags & MODULE_NAME_STYLE_FORMAT_BES)
+ {
+ snprintf(fpathdst, sizeof(fpathdst), "%s/%s.%s.%d", dst_dir, dst->base, dst->ext, x);
+ }
+ else if (dst->style_flags & MODULE_NAME_STYLE_FORMAT_BSE)
+ {
+ snprintf(fpathdst, sizeof(fpathdst), "%s/%s.%d.%s", dst_dir, dst->base, x, dst->ext);
+ }
+
+ moved = filesystem_copy(dst, fpathsrc, fpathdst, dst->flags);
+ if (moved != 0)
+ {
+ if (dst->flags & MODULE_FLAG_TRUNCATE)
+ {
+ filesystem_truncate(fpathsrc);
+ filesystem_reset_ctime(fpathsrc);
+ }
+ else
+ {
+ filesystem_unlink(fpathsrc);
+ }
+ }
+ }
+ }
+ else
+ {
+ for (f = src_list; f != NULL; f = f->next)
+ {
+ /* final / active base stamped file looks like a checkpointed file - ignore it */
+ if ((dst->flags & MODULE_FLAG_BASESTAMP) && (f->next == NULL)) break;
+
+ snprintf(fpathsrc, sizeof(fpathsrc), "%s/%s", dst->dir, f->name);
+
+ /* MODULE_FLAG_EXTERNAL files are not decorated with a timestamp */
+ if (dst->flags & MODULE_FLAG_EXTERNAL)
+ {
+ char tstamp[32];
+
+ asl_make_timestamp(f->ftime, dst->style_flags, tstamp, sizeof(tstamp));
+
+ if (dst->style_flags & MODULE_NAME_STYLE_FORMAT_BS)
+ {
+ snprintf(fpathdst, sizeof(fpathdst), "%s/%s.%s", dst_dir, dst->base, tstamp);
+ }
+ else if (dst->style_flags & MODULE_NAME_STYLE_FORMAT_BES)
+ {
+ snprintf(fpathdst, sizeof(fpathdst), "%s/%s.%s.%s", dst_dir, dst->base, dst->ext, tstamp);
+ }
+ else if (dst->style_flags & MODULE_NAME_STYLE_FORMAT_BSE)
+ {
+ snprintf(fpathdst, sizeof(fpathdst), "%s/%s.%s.%s", dst_dir, dst->base, tstamp, dst->ext);
+ }
+
+ }
+ else
+ {
+ snprintf(fpathdst, sizeof(fpathdst), "%s/%s", dst_dir, f->name);
+ }
+
+ moved = filesystem_copy(dst, fpathsrc, fpathdst, dst->flags);
+ if (moved != 0)
+ {
+ if (dst->flags & MODULE_FLAG_TRUNCATE) filesystem_truncate(fpathsrc);
+ else filesystem_unlink(fpathsrc);
+ }
+ }
+ }
+
+ asl_out_file_list_free(src_list);
+ asl_out_file_list_free(dst_list);
+
+ return 0;
+}
+
+/* delete expired files */
+int
+module_expire(asl_out_dst_data_t *dst, asl_out_dst_data_t *opts)
+{
+ asl_out_file_list_t *dst_list, *f;
+ char *base, *dst_dir, fpath[MAXPATHLEN];
+ time_t now, ttl, age;
+
+ if (dst == NULL) return -1;
+ if (dst->path == NULL) return -1;
+ if (dst->ttl[LEVEL_ALL] == 0) return 0;
+
+ ttl = dst->ttl[LEVEL_ALL];
+ if ((opts != NULL) && (opts->ttl[LEVEL_ALL] > 0)) ttl = opts->ttl[LEVEL_ALL];
+
+ now = time(NULL);
+ if (ttl > now) return 0;
+
+ base = strrchr(dst->path, '/');
+ if (base == NULL) return -1;
+
+ dst_list = asl_list_dst_files(dst);
+
+ if ((dst_list != NULL) && (dst->flags & MODULE_FLAG_ACTIVITY))
+ {
+ dst_list = _remove_youngest_activity_tracing_file(dst_list);
+ }
+
+ *base = '\0';
+
+ dst_dir = dst->rotate_dir;
+ if (dst_dir == NULL) dst_dir = dst->dir;
+
+ if (dst_list == NULL)
+ {
+ debug_log(ASL_LEVEL_INFO, " no dst files\n");
+ }
+ else
+ {
+ debug_log(ASL_LEVEL_INFO, " dst files\n");
+ for (f = dst_list; f != NULL; f = f->next)
+ {
+ char tstr[150];
+ age = now - f->ftime;
+
+ asl_core_time_to_str(age, tstr, sizeof(tstr));
+ debug_log(ASL_LEVEL_INFO, " %s (age %s%s)\n", f->name, tstr, (age > ttl) ? " - expired" : "");
+ }
+ }
+
+ for (f = dst_list; f != NULL; f = f->next)
+ {
+ age = now - f->ftime;
+ if (age > ttl)
+ {
+ snprintf(fpath, sizeof(fpath), "%s/%s", dst_dir, f->name);
+ filesystem_unlink(fpath);
+ }
+ }
+
+ asl_out_file_list_free(dst_list);
+
+ if (base != NULL) *base = '/';
+
+ return 0;
+}
+
+/*
+ * Check all_max size and delete files (oldest first) to stay within size limit.
+ * If query is true, then just report total size.
+ */
+int
+module_check_size(asl_out_dst_data_t *dst, asl_out_dst_data_t *opts, bool query, size_t *msize)
+{
+ asl_out_file_list_t *dst_list, *f, *dst_end;
+ char *dst_dir, fpath[MAXPATHLEN];
+ size_t total;
+
+ size_t all_max = dst->all_max;
+ if ((opts != NULL) && (opts->all_max > 0)) all_max = opts->all_max;
+
+ if (dst == NULL) return -1;
+ if (dst->path == NULL) return -1;
+
+ if (all_max == 0) return 0;
+
+ dst_list = asl_list_dst_files(dst);
+
+ if ((dst_list != NULL) && (dst->flags & MODULE_FLAG_ACTIVITY))
+ {
+ dst_list = _remove_youngest_activity_tracing_file(dst_list);
+ }
+
+ if (dst_list == NULL)
+ {
+ debug_log(ASL_LEVEL_INFO, " no dst files\n");
+ return 0;
+ }
+
+ dst_dir = dst->rotate_dir;
+ if (dst_dir == NULL) dst_dir = dst->dir;
+
+ debug_log(ASL_LEVEL_INFO, " dst files\n");
+ dst_end = dst_list;
+ for (f = dst_list; f != NULL; f = f->next)
+ {
+ dst_end = f;
+ debug_log(ASL_LEVEL_INFO, " %s size %lu\n", f->name, f->size);
+ }
+
+ total = 0;
+ for (f = dst_list; f != NULL; f = f->next) total += f->size;
+
+ if (!query)
+ {
+ for (f = dst_list; (total > all_max) && (f != NULL); f = f->next)
+ {
+ snprintf(fpath, sizeof(fpath), "%s/%s", dst_dir, f->name);
+ filesystem_unlink(fpath);
+ total -= f->size;
+ }
+ }
+
+ if (msize != NULL) *msize = total;
+
+ asl_out_file_list_free(dst_list);
+
+ return 0;
+}
+
+int
+process_dst(asl_out_dst_data_t *dst, asl_out_dst_data_t *opts)
+{
+ uint32_t ttl = dst->ttl[LEVEL_ALL];
+ if ((opts != NULL) && (opts->ttl[LEVEL_ALL] > 0)) ttl = opts->ttl[LEVEL_ALL];
+
+ size_t all_max = dst->all_max;
+ if ((opts != NULL) && (opts->all_max > 0)) all_max = opts->all_max;
+
+ if (dst == NULL)
+ {
+ debug_log(ASL_LEVEL_NOTICE, "NULL dst data for output rule - skipped\n");
+ }
+ else if (dst->flags & MODULE_FLAG_ROTATE)
+ {
+ debug_log(ASL_LEVEL_NOTICE, "Checking file %s\n", dst->path);
+ debug_log(ASL_LEVEL_NOTICE, "- Rename, move to destination directory, and compress as required\n");
+
+ module_copy_rename(dst);
+
+ if (ttl > 0)
+ {
+ char tstr[150];
+
+ asl_core_time_to_str(ttl, tstr, sizeof(tstr));
+ debug_log(ASL_LEVEL_NOTICE, "- Check for expired files - TTL = %s\n", tstr);
+ module_expire(dst, opts);
+ }
+
+ if (all_max > 0)
+ {
+ debug_log(ASL_LEVEL_NOTICE, "- Check total storage used - MAX = %lu\n", all_max);
+ module_check_size(dst, opts, false, NULL);
+ }
+ }
+ else if ((dst->flags & MODULE_FLAG_TYPE_ASL_DIR) && (ttl > 0))
+ {
+ process_asl_data_store(dst, opts);
+ }
+
+ return 0;
+}
+
+int
+process_module(asl_out_module_t *mod, asl_out_dst_data_t *opts)
+{
+ asl_out_rule_t *r;
+ uint32_t flags = 0;
+
+ if (mod == NULL) return -1;
+
+ if (opts != NULL) flags = opts->flags;
+
+ debug_log(ASL_LEVEL_NOTICE, "----------------------------------------\n");
+ debug_log(ASL_LEVEL_NOTICE, "Processing module %s\n", (mod->name == NULL) ? "asl.conf" : mod->name);
+
+ for (r = mod->ruleset; r != NULL; r = r->next)
+ {
+ if (r->action == ACTION_OUT_DEST)
+ {
+ if ((flags == 0) || ((flags & r->dst->flags) != 0)) process_dst(r->dst, opts);
+ }
+ }
+
+ debug_log(ASL_LEVEL_NOTICE, "Finished processing module %s\n", (mod->name == NULL) ? "asl.conf" : mod->name);
+ return 0;
+}
+
+int
+cache_delete_task(bool query, size_t *size)
+{
+ dispatch_sync(work_queue, ^{
+ asl_out_module_t *mod, *m;
+ asl_out_dst_data_t opts;
+ size_t total_size = 0;
+
+ memset(&opts, 0, sizeof(opts));
+ if ((!query) && (size != NULL)) opts.all_max = *size;
+
+ debug_log(ASL_LEVEL_NOTICE, "cache_delete_process%s size %lu\n", query ? " query" : "", opts.all_max);
+
+ mod = asl_out_module_init();
+
+ for (m = mod; m != NULL; m = m->next)
+ {
+ bool logged = false;
+ asl_out_rule_t *r;
+
+ for (r = m->ruleset; r != NULL; r = r->next)
+ {
+ if (r->action == ACTION_OUT_DEST)
+ {
+ if (r->dst->flags & MODULE_FLAG_ACTIVITY)
+ {
+ if (!logged)
+ {
+ debug_log(ASL_LEVEL_NOTICE, "----------------------------------------\n");
+ debug_log(ASL_LEVEL_NOTICE, "Processing activity module %s\n", (m->name == NULL) ? "asl.conf" : m->name);
+ logged = true;
+ }
+
+ size_t dsize = 0;
+ module_check_size(r->dst, &opts, false, &dsize);
+ total_size += dsize;
+ }
+ }
+ }
+
+ if (logged) debug_log(ASL_LEVEL_NOTICE, "Finished processing activity module %s\n", (m->name == NULL) ? "asl.conf" : m->name);
+ }
+
+ asl_out_module_free(mod);
+
+ if (size != NULL) *size = total_size;
+ });
+
+ return 0;
+}
+
+asl_msg_list_t *
+control_query(asl_msg_t *a)
+{
+ asl_msg_list_t *out;
+ char *qstr, *str, *res;
+ uint32_t len, reslen, status;
+ uint64_t cmax, qmin;
+ kern_return_t kstatus;
+ caddr_t vmstr;
+ security_token_t sec;
+
+ if (asl_server_port == MACH_PORT_NULL)
+ {
+ kstatus = bootstrap_look_up2(bootstrap_port, ASL_SERVICE_NAME, &asl_server_port, 0, BOOTSTRAP_PRIVILEGED_SERVER);
+ if (asl_server_port == MACH_PORT_NULL) return NULL;
+ }
+
+ qstr = asl_msg_to_string((asl_msg_t *)a, &len);
+
+ str = NULL;
+ if (qstr == NULL)
+ {
+ asprintf(&str, "1\nQ [= ASLOption control]\n");
+ }
+ else
+ {
+ asprintf(&str, "1\n%s [= ASLOption control]\n", qstr);
+ free(qstr);
+ }
+
+ if (str == NULL) return NULL;
+
+ /* length includes trailing nul */
+ len = strlen(str) + 1;
+ out = NULL;
+ qmin = 0;
+ cmax = 0;
+ sec.val[0] = -1;
+ sec.val[1] = -1;
+
+ res = NULL;
+ reslen = 0;
+ status = ASL_STATUS_OK;
+
+ kstatus = vm_allocate(mach_task_self(), (vm_address_t *)&vmstr, len, VM_FLAGS_ANYWHERE | VM_MAKE_TAG(VM_MEMORY_ASL));
+ if (kstatus != KERN_SUCCESS) return NULL;
+
+ memmove(vmstr, str, len);
+ free(str);
+
+ status = 0;
+ kstatus = _asl_server_query(asl_server_port, vmstr, len, qmin, 1, 0, (caddr_t *)&res, &reslen, &cmax, (int *)&status, &sec);
+ if (kstatus != KERN_SUCCESS) return NULL;
+
+ if (res == NULL) return NULL;
+
+ out = asl_msg_list_from_string(res);
+ vm_deallocate(mach_task_self(), (vm_address_t)res, reslen);
+
+ return out;
+}
+
+int
+checkpoint(const char *name)
+{
+ /* send checkpoint message to syslogd */
+ debug_log(ASL_LEVEL_NOTICE, "Checkpoint module %s\n", (name == NULL) ? "*" : name);
+ if (dryrun) return 0;
+
+ asl_msg_t *qmsg = asl_msg_new(ASL_TYPE_QUERY);
+ char *tmp = NULL;
+ asl_msg_list_t *res;
+
+ asprintf(&tmp, "%s checkpoint", (name == NULL) ? "*" : name);
+ asl_msg_set_key_val_op(qmsg, "action", tmp, ASL_QUERY_OP_EQUAL);
+ free(tmp);
+
+ res = control_query(qmsg);
+
+ asl_msg_list_release(res);
+ return 0;
+}