/*
- * Copyright (c) 2004-2011 Apple Inc. All rights reserved.
+ * Copyright (c) 2004-2013 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,
* 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 <TargetConditionals.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <netdb.h>
#include <notify.h>
#include <pthread.h>
-#include <asl_core.h>
+#include <sys/acl.h>
+#include <dirent.h>
+#include <time.h>
+#include <membership.h>
+#include <configuration_profile.h>
#include "daemon.h"
+#include <xpc/private.h>
#define _PATH_WALL "/usr/bin/wall"
-#define _PATH_ASL_CONF "/etc/asl.conf"
+
#define MY_ID "asl_action"
-#define ACTION_NONE 0
-#define ACTION_IGNORE 1
-#define ACTION_NOTIFY 2
-#define ACTION_BROADCAST 3
-#define ACTION_ACCESS 4
-#define ACTION_STORE 5
-#define ACTION_STORE_DIR 6
-#define ACTION_FORWARD 7
+#define MAX_FAILURES 5
+
+#define ACTION_STATUS_ERROR -1
+#define ACTION_STATUS_OK 0
+
+#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(;;)
-#define ACT_STORE_FLAG_STAY_OPEN 0x00000001
-#define ACT_STORE_FLAG_CONTINUE 0x00000002
+static dispatch_queue_t asl_action_queue;
+static dispatch_source_t checkpoint_timer;
+static time_t sweep_time = 0;
-static asl_msg_t *query = NULL;
-static int reset = RESET_NONE;
-static pthread_mutex_t reset_lock = PTHREAD_MUTEX_INITIALIZER;
+#if TARGET_OS_EMBEDDED
+#ifndef CRASH_MOVER_SERVICE
+#define CRASH_MOVER_SERVICE "com.apple.crash_mover"
+#endif
+static dispatch_queue_t crashlog_queue;
+static time_t crashmover_state = 0;
+static int crashmover_token = -1;
+#endif
-typedef struct action_rule_s
+typedef struct asl_file_data
{
- asl_msg_t *query;
- int action;
- char *options;
- void *data;
- struct action_rule_s *next;
-} action_rule_t;
-
-struct store_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;
- char *dir;
- char *path;
- mode_t mode;
- uid_t uid;
- gid_t gid;
+ 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 storedata_monitor;
+ dispatch_source_t aslfile_monitor;
+} asl_action_asl_store_data_t;
-static action_rule_t *asl_action_rule = NULL;
-static action_rule_t *asl_datastore_rule = NULL;
-static int filter_token = -1;
+typedef struct file_data
+{
+ int fd;
+ 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;
+} asl_action_file_data_t;
+
+typedef struct set_param_data
+{
+ int token;
+} asl_action_set_param_data_t;
-int asl_action_close();
-static int _parse_config_file(const char *);
-extern void db_save_message(aslmsg m);
+static int action_asl_store_count;
+static bool store_has_logged;
-static char *
-_next_word(char **s)
-{
- char *a, *p, *e, *out;
- int quote, len;
+extern void db_save_message(asl_msg_t *m);
- if (s == NULL) return NULL;
- if (*s == NULL) return NULL;
+/* 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);
- quote = 0;
+static void
+_act_out_set_param(asl_out_module_t *m, char *x, bool eval)
+{
+ char *s = x;
+ char **l;
+ uint32_t count, intval;
- p = *s;
- a = p;
- e = p;
+ l = explode(s, " \t");
+ if (l == NULL) return;
- while (*p != '\0')
+ for (count = 0; l[count] != NULL; count++);
+ if (count == 0)
{
- if (*p == '\\')
- {
- p++;
- e = p;
+ free_string_list(l);
+ return;
+ }
- if (*p == '\0')
- {
- p--;
- break;
- }
+ if (!strcasecmp(l[0], "enable"))
+ {
+ /* = enable [1|0] */
+ if (count < 2) intval = 1;
+ else intval = atoi(l[1]);
- p++;
- e = p;
- continue;
- }
+ if (!eval) intval = (intval == 0) ? 1 : 0;
- if (*p == '"')
- {
- if (quote == 0) quote = 1;
- else quote = 0;
- }
+ 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"))
+ {
+ /* = disable [1|0] */
+ if (count < 2) intval = 1;
+ else intval = atoi(l[1]);
- if (((*p == ' ') || (*p == '\t')) && (quote == 0))
- {
- e = p + 1;
- break;
- }
+ if (!eval) intval = (intval == 0) ? 1 : 0;
- p++;
- e = p;
+ if (intval != 0) m->flags &= ~MODULE_FLAG_ENABLED;
+ else m->flags|= MODULE_FLAG_ENABLED;
+ free_string_list(l);
+ return;
}
- *s = e;
+ free_string_list(l);
+
+ if (!strcmp(m->name, ASL_MODULE_NAME))
+ {
+ /* Other parameters may be set by com.apple.asl module */
+ control_set_param(x, eval);
+ }
+}
- len = p - a;
- if (len == 0) return NULL;
+static void
+_act_notify(asl_out_module_t *m, asl_out_rule_t *r)
+{
+ if (m == NULL) return;
+ if ((m->flags & MODULE_FLAG_ENABLED) == 0) return;
- out = malloc(len + 1);
- if (out == NULL) return NULL;
+ if (r == NULL) return;
+ if (r->options == NULL) return;
- memcpy(out, a, len);
- out[len] = '\0';
- return out;
+ notify_post(r->options);
}
static void
-_do_reset(void)
+_act_broadcast(asl_out_module_t *m, asl_out_rule_t *r, asl_msg_t *msg)
{
- pthread_mutex_lock(&reset_lock);
+#if !TARGET_OS_EMBEDDED
+ FILE *pw;
+ const char *val;
+
+ if (m == NULL) return;
+ if ((m->flags & MODULE_FLAG_ENABLED) == 0) return;
+
+ if (m->name == NULL) return;
+ if (r == NULL) return;
+ if (msg == NULL) return;
+
+ /* only base module (asl.conf) may broadcast */
+ if (strcmp(m->name, ASL_MODULE_NAME)) return;
+
+ val = r->options;
+ if (val == NULL) val = asl_msg_get_val_for_key(msg, ASL_KEY_MSG);
+ if (val == NULL) return;
- if (reset == RESET_CONFIG)
+ pw = popen(_PATH_WALL, "w");
+ if (pw == NULL)
{
- asl_action_close();
- _parse_config_file(_PATH_ASL_CONF);
+ asldebug("%s: error sending wall message: %s\n", MY_ID, strerror(errno));
+ return;
}
- reset = RESET_NONE;
+ fprintf(pw, "%s", val);
+ pclose(pw);
+#endif
+}
- pthread_mutex_unlock(&reset_lock);
+static void
+_act_set_key(asl_out_module_t *m, asl_out_rule_t *r, asl_msg_t *msg)
+{
+ /* Placeholder */
}
-/*
- * Config File format:
- * Set parameter rule - initializes a parameter.
- * = param args...
- * Query rule - if a message matches the query, then the action is invoked.
- * The rule may be identified by either "?" or "Q".
- * ? [k v] [k v] ... action args...
- * Q [k v] [k v] ... action args...
- * Universal match rule - the action is invoked for all messages
- * * action args...
- */
+static void
+_act_unset_key(asl_out_module_t *m, asl_out_rule_t *r, asl_msg_t *msg)
+{
+ /* Placeholder */
+}
-/* Skip over query */
-static char *
-_find_action(char *s)
+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;
- p = s;
- if (p == NULL) return NULL;
- if ((*p != 'Q') && (*p != '?') && (*p != '*')) return NULL;
+ if (m == NULL) return;
+ if (m->name == NULL) return;
+ if (r == NULL) return;
+ if (msg == NULL) return;
- p++;
+ /* only base module (asl.conf) may set access controls */
+ if (strcmp(m->name, ASL_MODULE_NAME)) return;
- forever
+ ruid = atoi(r->options);
+ rgid = -1;
+ p = strchr(r->options, ' ');
+ if (p == NULL) p = strchr(r->options, '\t');
+ if (p != NULL)
{
- /* Find next [ */
- while ((*p == ' ') || (*p == '\t')) p++;
-
- if (*p == '\0') return NULL;
- if (*p != '[') return p;
-
- /* skip to closing ] */
- while (*p != ']')
- {
- p++;
- if (*p == '\\')
- {
- p++;
- if (*p == ']') p++;
- }
- }
+ *p = '\0';
+ p++;
+ rgid = atoi(p);
+ }
- if (*p == ']') p++;
+ if (ruid != -1) asl_msg_set_key_val(msg, ASL_KEY_READ_UID, r->options);
+ if (p != NULL)
+ {
+ if (rgid != -1) asl_msg_set_key_val(msg, ASL_KEY_READ_GID, p);
+ p--;
+ *p = ' ';
}
+}
- return NULL;
+#if TARGET_OS_EMBEDDED
+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;
}
+#endif
+/*
+ * cover routine for asl_out_dst_file_create_open()
+ */
static int
-_parse_query_action(char *s)
+_act_file_create_open(asl_out_dst_data_t *dst)
{
- char *act, *p;
- action_rule_t *out, *rule;
-
- act = _find_action(s);
- if (act == NULL) return -1;
-
- out = (action_rule_t *)calloc(1, sizeof(action_rule_t));
- if (out == NULL) return -1;
-
- p = strchr(act, ' ');
- if (p != NULL) *p = '\0';
+ return asl_out_dst_file_create_open(dst, NULL);
+}
- if (!strcasecmp(act, "ignore")) out->action = ACTION_IGNORE;
- else if (!strcasecmp(act, "notify")) out->action = ACTION_NOTIFY;
- else if (!strcasecmp(act, "broadcast")) out->action = ACTION_BROADCAST;
- else if (!strcasecmp(act, "access")) out->action = ACTION_ACCESS;
- else if (!strcasecmp(act, "store")) out->action = ACTION_STORE;
- else if (!strcasecmp(act, "save")) out->action = ACTION_STORE;
- else if (!strcasecmp(act, "store_directory")) out->action = ACTION_STORE_DIR;
- else if (!strcasecmp(act, "store_dir")) out->action = ACTION_STORE_DIR;
- else if (!strcasecmp(act, "forward")) out->action = ACTION_FORWARD;
+/*
+ * Checks and creates (if required) a directory for an ASL data store.
+ */
+static int
+_asl_dir_create(asl_out_rule_t *r)
+{
+ struct stat sb;
+ int status;
- if (p != NULL)
+ memset(&sb, 0, sizeof(struct stat));
+ status = stat(r->dst->path, &sb);
+ if (status == 0)
{
- out->options = strdup(p+1);
-
- if (out->options == NULL)
+ /* Store path must be a directory */
+ if (!S_ISDIR(sb.st_mode))
{
- free(out);
+ asldebug("_asl_dir_create: expected a directory at path %s\n", r->dst->path);
return -1;
}
}
-
- p = act - 1;
-
- *p = '\0';
-
- if (s[0] == '*') out->query = asl_msg_new(ASL_TYPE_QUERY);
- else
+ else if (errno == ENOENT)
{
- s[0] = 'Q';
- out->query = asl_msg_from_string(s);
+ /* 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;
+ }
}
-
- if (out->query == NULL)
+ else
{
- asldebug("out->query is NULL (ERROR)\n");
- free(out->options);
- free(out);
+ /* Unexpected stat error */
+ asldebug("_asl_dir_create: stat error %s\n", strerror(errno));
return -1;
}
- if ((out->action == ACTION_STORE) && (out->options == NULL))
+ return 0;
+}
+
+/*
+ * 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;
+
+ if (as_data->storedata_monitor == NULL)
{
- asldebug("action = ACTION_STORE options = NULL\n");
- if (asl_datastore_rule == NULL) asl_datastore_rule = out;
- else
- {
- for (rule = asl_datastore_rule; rule->next != NULL; rule = rule->next);
- rule->next = out;
- }
+ /*
+ * 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
{
- asldebug("action = %d options = %s\n", out->action, out->options);
- if (asl_action_rule == NULL) asl_action_rule = out;
- else
- {
- for (rule = asl_action_rule; rule->next != NULL; rule = rule->next);
- rule->next = out;
- }
+ /*
+ * The storedata_monitor cancel handler will close the file.
+ */
+ dispatch_source_cancel(as_data->storedata_monitor);
+ dispatch_release(as_data->storedata_monitor);
+
}
- return 0;
+ asldebug("_asl_dir_storedata_close %p\n", as_data->storedata);
+ as_data->storedata = NULL;
}
/*
- * Used to sed config parameters.
- * Line format "= name value"
+ * 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
-_parse_set_param(char *s)
+_asl_dir_storedata_open(asl_out_rule_t *r, uint64_t xid)
{
- char **l;
- uint32_t intval, count, v32a, v32b, v32c;
-
- if (s == NULL) return -1;
- if (s[0] == '\0') return 0;
-
- /* skip '=' and whitespace */
- s++;
- while ((*s == ' ') || (*s == '\t')) s++;
-
- l = explode(s, " \t");
- if (l == NULL) return -1;
+ struct stat sb;
+ int status;
+ char dstpath[MAXPATHLEN];
- for (count = 0; l[count] != NULL; count++);
+ asl_action_asl_store_data_t *as_data = (asl_action_asl_store_data_t *)r->dst->private;
+ if (as_data->storedata != NULL) return 0;
- /* name is required */
- if (count == 0)
+ status = _asl_dir_create(r);
+ if (status != 0)
{
- freeList(l);
+ asldebug("_asl_dir_storedata_open: No directory at path %s\n", r->dst->path);
return -1;
}
- /* value is required */
- if (count == 1)
- {
- freeList(l);
- return -1;
- }
+ /* StoreData file is not open */
+ snprintf(dstpath, sizeof(dstpath), "%s/%s", r->dst->path, FILE_ASL_STORE_DATA);
- if (!strcasecmp(l[0], "debug"))
+ memset(&sb, 0, sizeof(struct stat));
+ status = stat(dstpath, &sb);
+ if (status == 0)
{
- /* = debug {0|1} [file] */
- intval = atoi(l[1]);
- config_debug(intval, l[2]);
+ /* 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 (!strcasecmp(l[0], "cutoff"))
+ else if (errno == ENOENT)
{
- /* = cutoff level */
- intval = atoi(l[1]);
- if (intval > ASL_LEVEL_DEBUG) intval = ASL_LEVEL_DEBUG;
- global.asl_log_filter = ASL_FILTER_MASK_UPTO(intval);
+ /*
+ * 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 if (!strcasecmp(l[0], "mark_time"))
+ else
{
- /* = mark_time seconds */
- OSSpinLockLock(&global.lock);
- global.mark_time = atoll(l[1]);
- OSSpinLockUnlock(&global.lock);
+ /* Unexpected stat error */
+ asldebug("_asl_dir_storedata_open: stat error %s\n", strerror(errno));
+ return -1;
}
- else if (!strcasecmp(l[0], "dup_delay"))
+
+ /* 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)
{
- /* = bsd_max_dup_time seconds */
- OSSpinLockLock(&global.lock);
- global.bsd_max_dup_time = atoll(l[1]);
- OSSpinLockUnlock(&global.lock);
+ 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);
}
- else if (!strcasecmp(l[0], "asl_store_ping_time"))
+
+ 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)
{
- /* NB this is private / unpublished */
- /* = asl_store_ping_time seconds */
- OSSpinLockLock(&global.lock);
- global.asl_store_ping_time = atoll(l[1]);
- OSSpinLockUnlock(&global.lock);
+ char *str = NULL;
+ 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);
}
- else if (!strcasecmp(l[0], "utmp_ttl"))
+
+ if (as_data->aslfile_monitor == NULL)
{
- /* = utmp_ttl seconds */
- OSSpinLockLock(&global.lock);
- global.utmp_ttl = (time_t)atoll(l[1]);
- OSSpinLockUnlock(&global.lock);
+ /*
+ * 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 if (!strcasecmp(l[0], "fs_ttl"))
+ else
{
- /* = fs_ttl seconds */
- OSSpinLockLock(&global.lock);
- global.fs_ttl = (time_t)atoll(l[1]);
- OSSpinLockUnlock(&global.lock);
+ /*
+ * 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;
}
- else if (!strcasecmp(l[0], "mps_limit"))
+
+ 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)
{
- /* = mps_limit number */
- OSSpinLockLock(&global.lock);
- global.mps_limit = (uint32_t)atol(l[1]);
- OSSpinLockUnlock(&global.lock);
+ if (r->dst->path == NULL) return 0;
+ asl_make_dst_filename(r->dst, tmpfname, sizeof(tmpfname));
+ fn = tmpfname;
}
- else if (!strcasecmp(l[0], "max_file_size"))
- {
- /* = max_file_size bytes */
- pthread_mutex_lock(global.db_lock);
- if (global.dbtype & DB_TYPE_FILE)
- {
- asl_store_close(global.file_db);
- global.file_db = NULL;
- global.db_file_max = atoi(l[1]);
- }
+ if ((force == CHECKPOINT_TEST) && (r->dst->file_max == 0)) return 0;
- pthread_mutex_unlock(global.db_lock);
- }
- else if ((!strcasecmp(l[0], "db")) || (!strcasecmp(l[0], "database")) || (!strcasecmp(l[0], "datastore")))
+ if ((r->dst->size == 0) || (r->dst->stamp == 0))
{
- /* NB this is private / unpublished */
- /* = db type [max]... */
+ struct stat sb;
- v32a = 0;
- v32b = 0;
- v32c = 0;
+ memset(&sb, 0, sizeof(struct stat));
- if ((l[1][0] >= '0') && (l[1][0] <= '9'))
- {
- intval = atoi(l[1]);
- if ((count >= 3) && (strcmp(l[2], "-"))) v32a = atoi(l[2]);
- if ((count >= 4) && (strcmp(l[3], "-"))) v32b = atoi(l[3]);
- if ((count >= 5) && (strcmp(l[4], "-"))) v32c = atoi(l[4]);
- }
- else if (!strcasecmp(l[1], "file"))
- {
- intval = DB_TYPE_FILE;
- if ((count >= 3) && (strcmp(l[2], "-"))) v32a = atoi(l[2]);
- }
- else if (!strncasecmp(l[1], "mem", 3))
- {
- intval = DB_TYPE_MEMORY;
- if ((count >= 3) && (strcmp(l[2], "-"))) v32b = atoi(l[2]);
- }
- else if (!strncasecmp(l[1], "min", 3))
- {
- intval = DB_TYPE_MINI;
- if ((count >= 3) && (strcmp(l[2], "-"))) v32c = atoi(l[2]);
- }
- else
+ if (stat(fn, &sb) < 0)
{
- freeList(l);
+ if (errno == ENOENT) return 0;
return -1;
}
- if (v32a == 0) v32a = global.db_file_max;
- if (v32b == 0) v32b = global.db_memory_max;
- if (v32c == 0) v32c = global.db_mini_max;
+ 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;
- config_data_store(intval, v32a, v32b, v32c);
+ if (r->dst->flags & MODULE_FLAG_BASESTAMP)
+ {
+ _act_dst_close(r, DST_CLOSE_CHECKPOINT);
}
+ else
+ {
+ char srcpath[MAXPATHLEN];
+ char dstpath[MAXPATHLEN];
+ char tstamp[32];
- freeList(l);
- return 0;
+ 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
-_parse_line(char *s)
+_asl_dir_today_open(asl_out_rule_t *r, const time_t *tick)
{
- char *str;
int status;
+ mode_t mask;
+ struct tm ctm;
+ time_t now;
- if (s == NULL) return -1;
- while ((*s == ' ') || (*s == '\t')) s++;
+ if (r == NULL) return -1;
+ if (r->dst == NULL) return -1;
- /* First non-whitespace char is the rule type */
- switch (*s)
+ status = _asl_dir_create(r);
+ if (status != 0)
{
- case '\0':
- case '#':
- {
- /* Blank Line or Comment */
- return 0;
- }
- case 'Q':
- case '?':
- case '*':
- {
- /* Query-match action */
- status = _parse_query_action(s);
- break;
- }
- case '=':
- {
- /* Set parameter */
- status = _parse_set_param(s);
- break;
- }
- default:
- {
- status = -1;
- break;
- }
+ asldebug("_asl_dir_today_open: No directory at path %s\n", r->dst->path);
+ return -1;
}
- if (status != 0)
+ 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)
{
- str = NULL;
- asprintf(&str, "[%s syslogd] [%s %u] [%s %u] [%s Ignoring unrecognized entry in %s: %s] [%s 0] [%s 0] [Facility syslog]",
- ASL_KEY_SENDER,
- ASL_KEY_LEVEL, ASL_LEVEL_ERR,
- ASL_KEY_PID, getpid(),
- ASL_KEY_MSG, _PATH_ASL_CONF, s,
- ASL_KEY_UID, ASL_KEY_GID);
+ now = time(NULL);
+ tick = (const time_t *)&now;
+ }
- asl_log_string(str);
- free(str);
+ if (localtime_r(tick, &ctm) == NULL)
+ {
+ asldebug("_asl_dir_today_open: localtime_r error %s\n", strerror(errno));
+ return -1;
}
- return status;
-}
+ /* checks file_max and closes if required */
+ status = _act_checkpoint(r, CHECKPOINT_TEST);
+ if (status == 1) trigger_aslmanager();
-static void
-_act_notify(action_rule_t *r)
-{
- if (r == NULL) return;
- if (r->options == NULL) return;
+ 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;
- notify_post(r->options);
+ /* 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;
+
+ /* 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
-_act_broadcast(action_rule_t *r, aslmsg msg)
+_asl_file_close(asl_out_rule_t *r)
{
-#ifndef CONFIG_IPHONE
- FILE *pw;
- const char *val;
-
if (r == NULL) return;
- if (msg == NULL) return;
+ if (r->dst == NULL) return;
- val = r->options;
- if (val == NULL) val = asl_get(msg, ASL_KEY_MSG);
- if (val == 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;
- pw = popen(_PATH_WALL, "w");
- if (pw < 0)
+ if (af_data->pending != 0)
{
- asldebug("%s: error sending wall message: %s\n", MY_ID, strerror(errno));
- return;
+ char *str = NULL;
+ 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);
}
- fprintf(pw, "%s", val);
- pclose(pw);
-#endif
+ 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
-_act_access_control(action_rule_t *r, aslmsg msg)
+_text_file_close(asl_out_rule_t *r)
{
- int32_t ruid, rgid;
- char *p;
+ asl_action_file_data_t *f_data = (asl_action_file_data_t *)r->dst->private;
+ if (f_data->fd < 0) return;
- ruid = atoi(r->options);
- rgid = -1;
- p = strchr(r->options, ' ');
- if (p == NULL) p = strchr(r->options, '\t');
- if (p != NULL)
+ if (f_data->pending != 0)
{
- *p = '\0';
- p++;
- rgid = atoi(p);
+ char *str = NULL;
+ 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);
}
- if (ruid != -1) asl_set(msg, ASL_KEY_READ_UID, r->options);
- if (p != NULL)
+ if (f_data->monitor == NULL)
{
- if (rgid != -1) asl_set(msg, ASL_KEY_READ_GID, p);
- p--;
- *p = ' ';
+ /*
+ * 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("_act_dst_open:_asl_dir_today_open %s FAILED!\n", r->dst->path);
+ return -1;
+ }
+
+ if (_asl_dir_storedata_open(r, xid) != 0)
+ {
+ asldebug("_act_dst_open:_asl_dir_storedata_open %s FAILED! Closing today file\n", r->dst->path);
+ _asl_dir_today_close(r);
+ return -1;
+ }
+
+ return 0;
+ }
+
+ if (r->action == ACTION_ASL_FILE)
+ {
+ return _asl_file_open(r);
+ }
+
+ if (r->action == ACTION_FILE)
+ {
+ return _text_file_open(r);
+ }
+
+ return -1;
+}
+
+static void
+_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)
+ {
+ 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)
+ {
+ 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);
}
}
static uint32_t
-_act_store_file_setup(struct store_data *sd)
+_act_store_file_setup(asl_out_module_t *m, asl_out_rule_t *r)
{
uint32_t status;
+ 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;
+
+ af_data = (asl_action_asl_file_data_t *)r->dst->private;
+ if (af_data->aslfile != NULL)
+ {
+ af_data->next_id++;
+ return ASL_STATUS_OK;
+ }
- if (sd == NULL) return ASL_STATUS_INVALID_STORE;
- if (sd->store == NULL) return ASL_STATUS_INVALID_STORE;
- if (sd->store->store == NULL) return ASL_STATUS_INVALID_STORE;
+ if (_act_dst_open(r, NULL, 0) != 0) return ASL_STATUS_WRITE_FAILED;
- status = asl_file_read_set_position(sd->store, ASL_FILE_POSITION_LAST);
- if (status != ASL_STATUS_OK) return status;
+ 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;
+ }
- sd->next_id = sd->store->cursor_xid + 1;
- if (fseek(sd->store->store, 0, SEEK_END) != 0) return ASL_STATUS_ACCESS_DENIED;
+ af_data->next_id = af_data->aslfile->cursor_xid + 1;
+ if (fseek(af_data->aslfile->store, 0, SEEK_END) != 0)
+ {
+ 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;
}
+/*
+ * _act_store_dir_setup
+ *
+ * Opens StoreData file and today's file
+ * Reads ASL Message ID from StoreData file
+ * Writes ASL Message ID + 1 to StoreData file
+ */
static uint32_t
-_act_store_dir_setup(struct store_data *sd, time_t tick)
+_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_asl_store_data_t *as_data;
- if (sd == NULL) return ASL_STATUS_INVALID_STORE;
- if (sd->dir == NULL) return ASL_STATUS_INVALID_STORE;
+ 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;
+
+ as_data = (asl_action_asl_store_data_t *)r->dst->private;
+
+ if (_act_dst_open(r, NULL, as_data->next_id) != 0)
+ {
+ asldebug("_act_store_dir_setup: _act_dst_open %s failed\n", r->dst->path);
+ return ASL_STATUS_WRITE_FAILED;
+ }
/* get / set message id from StoreData file */
xid = 0;
+ rewind(as_data->storedata);
+ if (fread(&xid, sizeof(uint64_t), 1, as_data->storedata) != 1)
+ {
+ 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++;
+ as_data->next_id = xid;
+
+ xid = asl_core_htonq(xid);
+ rewind(as_data->storedata);
+ status = fwrite(&xid, sizeof(uint64_t), 1, as_data->storedata);
+ if (status != 1)
+ {
+ 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;
+ }
+
+ fflush(as_data->storedata);
- if (sd->storedata == NULL)
+ if (fseek(as_data->aslfile->store, 0, SEEK_END) != 0)
{
- memset(&sb, 0, sizeof(struct stat));
- status = stat(sd->dir, &sb);
- if (status == 0)
+ 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;
+ }
+
+ return ASL_STATUS_OK;
+}
+
+static void
+_asl_action_asl_store_data_free(asl_action_asl_store_data_t *as_data)
+{
+ if (as_data == NULL) return;
+ free(as_data);
+}
+
+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 *f_data)
+{
+ if (f_data == NULL) return;
+
+ if (f_data->dup_timer != NULL)
+ {
+ 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(f_data->dup_timer);
+ }
+
+ dispatch_release(f_data->dup_timer);
+ }
+
+ free(f_data->last_msg);
+ free(f_data);
+}
+
+static void
+_asl_action_set_param_data_free(asl_action_set_param_data_t *spdata)
+{
+ if (spdata != NULL) notify_cancel(spdata->token);
+ free(spdata);
+}
+
+static void
+_asl_action_save_failed(const char *where, asl_out_module_t *m, asl_out_rule_t *r, uint32_t status)
+{
+ if (r->dst->flags & MODULE_FLAG_SOFT_WRITE) return;
+
+ r->dst->fails++;
+ asldebug("%s: %s save to %s failed: %s\n", where, m->name, r->dst->path, asl_core_error(status));
+
+ /* disable further activity after multiple failures */
+ if (r->dst->fails > MAX_FAILURES)
+ {
+ char *str = NULL;
+ asprintf(&str, "[Sender syslogd] [Level 3] [PID %u] [Facility syslog] [Message Disabling module %s writes to %s following %u failures (%s)]", global.pid, m->name, r->dst->path, r->dst->fails, asl_core_error(status));
+ internal_log_message(str);
+ free(str);
+
+ 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, asl_msg_t *msg)
+{
+ asl_action_asl_file_data_t *af_data;
+ uint32_t status;
+ uint64_t mid;
+
+ if (r == NULL) return ACTION_STATUS_ERROR;
+ if (r->dst == NULL) return ACTION_STATUS_ERROR;
+ if (r->dst->private == NULL) return ACTION_STATUS_ERROR;
+
+ 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)
+ {
+ af_data->last_time = time(NULL);
+
+ r->dst->fails = 0;
+ mid = af_data->next_id;
+
+ /* save message to file and update dst size */
+ status = asl_file_save(af_data->aslfile, msg, &mid);
+ if (status == ASL_STATUS_OK)
{
- /* must be a directory */
- if (!S_ISDIR(sb.st_mode)) return ASL_STATUS_INVALID_STORE;
+ r->dst->size = af_data->aslfile->file_size;
+
+ if (_act_checkpoint(r, CHECKPOINT_TEST) == 1) trigger_aslmanager();
}
- else if (errno == ENOENT)
+ }
+
+ if (status != ASL_STATUS_OK)
+ {
+ _asl_action_save_failed("_act_store_file", m, r, status);
+ return ACTION_STATUS_ERROR;
+ }
+
+ 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, asl_msg_t *msg)
+{
+ asl_action_asl_store_data_t *as_data;
+ uint32_t status;
+ uint64_t mid;
+ const char *val;
+ time_t tick;
+
+ if (r == NULL) return ACTION_STATUS_ERROR;
+ if (r->dst == NULL) return ACTION_STATUS_ERROR;
+ if (r->dst->private == NULL) return ACTION_STATUS_ERROR;
+
+ as_data = (asl_action_asl_store_data_t *)r->dst->private;
+ if (as_data->pending > 0) as_data->pending--;
+
+ val = asl_msg_get_val_for_key(msg, ASL_KEY_TIME);
+ if (val == NULL) return ACTION_STATUS_ERROR;
+
+ tick = atol(val);
+
+ status = _act_store_dir_setup(m, r, tick);
+ if (status == ASL_STATUS_OK)
+ {
+ as_data->last_time = time(NULL);
+
+ r->dst->fails = 0;
+ 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)
+ {
+ _asl_action_save_failed("_act_store_dir", m, r, status);
+ return ACTION_STATUS_ERROR;
+ }
+
+ return ACTION_STATUS_OK;
+}
+
+static void
+_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);
+}
+
+/*
+ * Save a message to an ASL format file (ACTION_ASL_FILE)
+ * or to an ASL directory (ACTION_ASL_DIR).
+ */
+static void
+_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->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(msg);
+ dispatch_async(crashlog_queue, ^{
+ dispatch_async(asl_action_queue, ^{
+ _act_store_final(m, r, msg);
+ asl_msg_release(msg);
+ });
+ });
+ return;
+ }
+#endif
+
+ _act_store_final(m, r, msg);
+}
+
+static int
+_send_repeat_msg(asl_out_rule_t *r)
+{
+ asl_action_file_data_t *f_data;
+ char vt[32], *msg;
+ int len, status;
+ time_t now = time(NULL);
+
+ if (r == NULL) return -1;
+ if (r->dst == NULL) return -1;
+ if (r->dst->private == NULL) return -1;
+
+ f_data = (asl_action_file_data_t *)r->dst->private;
+
+ free(f_data->last_msg);
+ f_data->last_msg = NULL;
+
+ if (f_data->last_count == 0) return 0;
+
+ /* stop the 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, 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 (f_data->fd < 0) f_data->fd = _act_file_create_open(r->dst);
+
+ len = strlen(msg);
+ 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->fname, strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+static void
+_start_cycling()
+{
+ struct timespec midnight;
+ struct tm t;
+ time_t x;
+
+ x = time(NULL);
+
+ if (checkpoint_timer != NULL) return;
+
+ localtime_r(&x, &t);
+
+ t.tm_sec = 0;
+ t.tm_min = 0;
+ t.tm_hour = 0;
+ t.tm_mday++;
+
+ x = mktime(&t);
+ midnight.tv_sec = x;
+ midnight.tv_nsec = 0;
+
+ checkpoint_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, asl_action_queue);
+ dispatch_source_set_timer(checkpoint_timer, dispatch_walltime(&midnight, 0), NSEC_PER_SEC * SEC_PER_DAY, 0);
+ dispatch_source_set_event_handler(checkpoint_timer, ^{ _act_file_checkpoint_all(CHECKPOINT_FORCE); });
+ dispatch_resume(checkpoint_timer);
+}
+
+/* check if a module path (mpath) matches a user path (upath) */
+static bool
+_act_file_equal(const char *mpath, const char *upath)
+{
+ const char *slash;
+
+ /* NULL upath means user wants to match all files */
+ if (upath == NULL) return true;
+
+ if (mpath == NULL) return false;
+
+ /* check for exact match */
+ if (!strcmp(mpath, upath)) return true;
+
+ /* upath may be the last component of mpath */
+ slash = strrchr(mpath, '/');
+ if (slash == NULL) return false;
+
+ if (!strcmp(slash + 1, upath)) return true;
+ return false;
+}
+
+static int
+_act_file_checkpoint(asl_out_module_t *m, const char *path, uint32_t force)
+{
+ asl_out_rule_t *r;
+ int did_checkpoint = 0;
+
+ if (m == NULL) return 0;
+
+
+ for (r = m->ruleset; r != NULL; r = r->next)
+ {
+ if ((r->action == ACTION_FILE) || (r->action == ACTION_ASL_FILE))
{
- /* doesn't exist - create it */
- mask = umask(0);
- status = mkdir(sd->dir, sd->mode);
- umask(mask);
-
- if (status != 0) return ASL_STATUS_WRITE_FAILED;
-
- if ((sd->uid != 0) || (sd->gid != 0))
+ if (r->dst->flags & MODULE_FLAG_ROTATE)
{
- if (chown(sd->dir, sd->uid, sd->gid) != 0) return ASL_STATUS_WRITE_FAILED;
+ if (_act_file_equal(r->dst->path, path))
+ {
+ if (force & CHECKPOINT_CRASH)
+ {
+ if (r->dst->flags & MODULE_FLAG_CRASHLOG)
+ {
+ if (_act_checkpoint(r, CHECKPOINT_FORCE) > 0)
+ {
+ did_checkpoint = 1;
+ _act_dst_close(r, DST_CLOSE_CHECKPOINT);
+ }
+ }
+ }
+ else
+ {
+ if (_act_checkpoint(r, force) > 0)
+ {
+ did_checkpoint = 1;
+ _act_dst_close(r, DST_CLOSE_CHECKPOINT);
+ }
+ }
+ }
}
}
- else
+ }
+
+ return did_checkpoint;
+}
+
+static int
+_act_file_checkpoint_all(uint32_t force)
+{
+ asl_out_module_t *m;
+ int did_checkpoint = 0;
+
+ for (m = global.asl_out_module; m != NULL; m = m->next)
+ {
+ if (_act_file_checkpoint(m, NULL, force) > 0) did_checkpoint = 1;
+ }
+
+ 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, asl_msg_t *msg)
+{
+ 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.
+ */
+ if (r->dst->flags & MODULE_FLAG_STD_BSD_MSG)
+ {
+ const char *msgval = NULL;
+ if (asl_msg_lookup(msg, ASL_KEY_MSG, &msgval, NULL) != 0) return;
+ if (msgval == NULL) return;
+ }
+
+ now = time(NULL);
+
+ is_dup = 0;
+
+ str = asl_format_message(msg, r->dst->fmt, r->dst->tfmt, ASL_ENCODE_SAFE, &len);
+
+ if (r->dst->flags & MODULE_FLAG_COALESCE)
+ {
+ if (f_data->dup_timer == NULL)
{
- /* Unexpected stat error */
- return ASL_STATUS_FAILED;
+ /* create a timer to flush dups on this file */
+ 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); });
}
-
- path = NULL;
- asprintf(&path, "%s/%s", sd->dir, 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)
+ if ((global.bsd_max_dup_time > 0) && (str != NULL) && (f_data->last_msg != NULL))
{
- /* StoreData exists: open and read last xid */
- sd->storedata = fopen(path, "r+");
- if (sd->storedata == NULL)
+ msg_hash = asl_core_string_hash(str + 16, len - 16);
+ if ((f_data->last_hash == msg_hash) && (!strcmp(f_data->last_msg, str + 16)))
{
- free(path);
- return ASL_STATUS_FAILED;
+ if ((now - f_data->last_time) < global.bsd_max_dup_time) is_dup = 1;
}
+ }
+ }
- if (fread(&xid, sizeof(uint64_t), 1, sd->storedata) != 1)
- {
- free(path);
- fclose(sd->storedata);
- sd->storedata = NULL;
- return ASL_STATUS_READ_FAILED;
- }
+ if (is_dup == 1)
+ {
+ if (f_data->last_count == 0)
+ {
+ /* start the 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);
}
- else if (errno == ENOENT)
+
+ f_data->last_count++;
+ }
+ else
+ {
+ if (_act_dst_open(r, NULL, 0) != 0)
{
- /* StoreData does not exist: create it */
- sd->storedata = fopen(path, "w");
- if (sd->storedata == NULL)
- {
- free(path);
- return ASL_STATUS_FAILED;
- }
+ _asl_action_save_failed("_act_file", m, r, ASL_STATUS_FAILED);
+ free(str);
+ return;
+ }
+ else
+ {
+ r->dst->fails = 0;
+ }
- if ((sd->uid != 0) || (sd->gid != 0))
- {
- if (chown(path, sd->uid, sd->gid) != 0)
- {
- free(path);
- return ASL_STATUS_WRITE_FAILED;
- }
- }
+ /*
+ * 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 (f_data->last_count > 0)
+ {
+ _send_repeat_msg(r);
}
else
{
- /* Unexpected stat error */
- free(path);
- return ASL_STATUS_FAILED;
+ free(f_data->last_msg);
+ f_data->last_msg = NULL;
}
- free(path);
- }
- else
- {
- rewind(sd->storedata);
- if (fread(&xid, sizeof(uint64_t), 1, sd->storedata) != 1)
+ if (str != NULL) f_data->last_msg = strdup(str + 16);
+
+ f_data->last_hash = msg_hash;
+ f_data->last_count = 0;
+ f_data->last_time = now;
+
+ if ((str != NULL) && (len > 1))
{
- fclose(sd->storedata);
- sd->storedata = NULL;
- return ASL_STATUS_READ_FAILED;
+ /* write line to file and update dst size */
+ 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();
}
}
- xid = asl_core_ntohq(xid);
- xid++;
- sd->next_id = xid;
+ free(str);
+}
- xid = asl_core_htonq(xid);
- rewind(sd->storedata);
- status = fwrite(&xid, sizeof(uint64_t), 1, sd->storedata);
- if (status != 1)
+static void
+_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;
+ if ((m->flags & MODULE_FLAG_ENABLED) == 0) return;
+ if (r->dst == NULL) return;
+ if (r->dst->private == NULL) return;
+
+ 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)
{
- fclose(sd->storedata);
- sd->storedata = NULL;
- return ASL_STATUS_WRITE_FAILED;
+ _crashlog_queue_check();
+ asl_msg_retain(msg);
+ dispatch_async(crashlog_queue, ^{
+ dispatch_async(asl_action_queue, ^{
+ _act_file_final(m, r, msg);
+ asl_msg_release(msg);
+ });
+ });
+ return;
+ }
+#endif
+
+ _act_file_final(m, r, msg);
+}
+
+static void
+_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, asl_msg_t *msg)
+{
+ const char *p;
+
+ if (m == NULL) return;
+ if (r == NULL) return;
+
+ p = asl_msg_get_val_for_key(msg, ASL_KEY_MODULE);
+
+ if (r->options == NULL) return;
+
+ if (!strcmp(r->options, "enable"))
+ {
+ m->flags |= MODULE_FLAG_ENABLED;
+ }
+ else if (!strcmp(r->options, "disable"))
+ {
+ m->flags &= ~MODULE_FLAG_ENABLED;
+ }
+ else if ((!strcmp(r->options, "checkpoint")) || (!strcmp(r->options, "rotate")))
+ {
+ _act_file_checkpoint(m, NULL, CHECKPOINT_FORCE);
}
+}
+
+static void
+_send_to_asl_store(asl_msg_t *msg)
+{
+ if ((global.asl_out_module != NULL) && ((global.asl_out_module->flags & MODULE_FLAG_ENABLED) == 0)) return;
- if ((sd->flags & ACT_STORE_FLAG_STAY_OPEN) == 0)
+ if (store_has_logged) return;
+ store_has_logged = true;
+
+ db_save_message(msg);
+}
+
+static int
+_asl_out_process_message(asl_out_module_t *m, asl_msg_t *msg)
+{
+ asl_out_rule_t *r;
+
+ if (m == NULL) return 1;
+ if (msg == NULL) return 1;
+
+ /* reset flag bit used for duplicate avoidance */
+ for (r = m->ruleset; r != NULL; r = r->next)
{
- fclose(sd->storedata);
- sd->storedata = NULL;
+ if ((r->action == ACTION_FILE) || (r->action == ACTION_ASL_DIR) || (r->action == ACTION_ASL_FILE))
+ {
+ if (r->dst != NULL) r->dst->flags &= MODULE_FLAG_CLEAR_LOGGED;
+ }
}
- memset(&ctm, 0, sizeof(struct tm));
+ for (r = m->ruleset; r != NULL; r = r->next)
+ {
+ if (r->query == NULL) continue;
+
+ /* ACTION_SET_FILE, ACTION_SET_PLIST, and ACTION_SET_PROF are handled independently */
+ if ((r->action == ACTION_SET_FILE) || (r->action == ACTION_SET_PLIST) || (r->action == ACTION_SET_PROF)) continue;
+
+ /*
+ * ACTION_CLAIM during processing is a filter. It will only be here if the option "only"
+ * was supplied. In this case we test the message against the query. If it does not
+ * match, we skip the message.
+ */
+ if (r->action == ACTION_CLAIM)
+ {
+ if ((asl_msg_cmp(r->query, msg) != 1)) return 0;
+ }
+
+ 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);
+ else if (r->action == ACTION_CONTROL) _act_control(m, r, msg);
+ else if (r->action == ACTION_SET_PARAM) _act_out_set_param(m, r->options, true);
+ else if ((r->action == ACTION_ASL_FILE) || (r->action == ACTION_ASL_DIR)) _act_store(m, r, msg);
+ else if (r->action == ACTION_FILE) _act_file(m, r, msg);
+ }
+ }
+
+ return 0;
+}
- if (localtime_r((const time_t *)&tick, &ctm) == NULL) return ASL_STATUS_FAILED;
- if ((sd->p_year == ctm.tm_year) && (sd->p_month == ctm.tm_mon) && (sd->p_day == ctm.tm_mday) && (sd->path != NULL)) return ASL_STATUS_OK;
+void
+asl_out_message(asl_msg_t *msg)
+{
+ OSAtomicIncrement32(&global.asl_queue_count);
+ asl_msg_retain(msg);
- if (sd->store != NULL) asl_file_close(sd->store);
+ dispatch_async(asl_action_queue, ^{
+ int ignore = 0;
+ const char *p;
+ time_t now = time(NULL);
+ asl_out_module_t *m = global.asl_out_module;
- sd->p_year = 0;
- sd->p_month = 0;
- sd->p_day = 0;
+ store_has_logged = false;
- free(sd->path);
- sd->path = NULL;
+ 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);
- asprintf(&(sd->path), "%s/%d.%02d.%02d.asl", sd->dir, ctm.tm_year + 1900, ctm.tm_mon + 1, ctm.tm_mday);
- if (sd->path == NULL) return ASL_STATUS_NO_MEMORY;
+ ignore = _asl_out_process_message(m, msg);
+ if (ignore == 0)
+ {
+ if (m != NULL) m = m->next;
+ while (m != NULL)
+ {
+ _asl_out_process_message(m, msg);
+ m = m->next;
+ }
+ }
+ }
+ else
+ {
+ if (m != NULL) m = m->next;
+ while (m != NULL)
+ {
+ if (!strcmp(p, m->name)) _asl_out_process_message(m, msg);
+ m = m->next;
+ }
+ }
- sd->p_year = ctm.tm_year;
- sd->p_month = ctm.tm_mon;
- sd->p_day = ctm.tm_mday;
+ 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);
- return ASL_STATUS_OK;
+ asl_msg_release(msg);
+ OSAtomicDecrement32(&global.asl_queue_count);
+
+ if ((now - sweep_time) >= IDLE_CLOSE)
+ {
+ _asl_action_close_idle_files(IDLE_CLOSE);
+ sweep_time = now;
+ }
+ });
}
-static void
-_act_store(action_rule_t *r, aslmsg msg)
+static char *
+_asl_action_profile_test(asl_out_module_t *m, asl_out_rule_t *r)
{
- struct store_data *sd;
- asl_file_t *s;
- uint8_t x;
- uint32_t status;
- uint64_t mid;
- mode_t tmp_mode, mask;
- char *str, *opts, *p;
- const char *val;
- time_t tick;
+ const char *ident;
+ asl_msg_t *profile;
+ bool eval;
- s = NULL;
+ /* ident is first message key */
+ asl_msg_fetch(r->query, 0, &ident, NULL, NULL);
+ if (ident == NULL)
+ {
+ r->action = ACTION_NONE;
+ return NULL;
+ }
+
+ profile = configuration_profile_to_asl_msg(ident);
+ eval = (asl_msg_cmp(r->query, profile) == 1);
+ _act_out_set_param(m, r->options, eval);
+ asl_msg_release(profile);
+
+ return strdup(ident);
+}
- /* _act_store is not used for the main ASL data store */
- if (r->options == NULL) return;
+static const char *
+_asl_action_file_test(asl_out_module_t *m, asl_out_rule_t *r)
+{
+ const char *path;
+ struct stat sb;
+ int status;
+ bool eval;
- if (r->data == NULL)
+ /* path is first message key */
+ asl_msg_fetch(r->query, 0, &path, NULL, NULL);
+ if (path == NULL)
{
- /* set up store data */
- sd = (struct store_data *)calloc(1, sizeof(struct store_data));
- if (sd == NULL) return;
+ r->action = ACTION_NONE;
+ return NULL;
+ }
- opts = r->options;
- sd->store = NULL;
+ memset(&sb, 0, sizeof(struct stat));
+ status = stat(path, &sb);
+ eval = (status == 0);
+ _act_out_set_param(m, r->options, eval);
- if (r->action == ACTION_STORE)
- {
- sd->path = _next_word(&opts);
- if ((sd->path == NULL) || (sd->path[0] != '/'))
- {
- str = NULL;
- asprintf(&str, "[%s syslogd] [%s %u] [%s %u] [Facility syslog] [%s Invalid path for \"store\" action: %s]",
- ASL_KEY_SENDER,
- ASL_KEY_LEVEL, ASL_LEVEL_ERR,
- ASL_KEY_PID, getpid(),
- ASL_KEY_MSG, (sd->path == NULL) ? "no path specified" : sd->path);
-
- asl_log_string(str);
- free(str);
- free(sd);
- r->action = ACTION_NONE;
- return;
- }
- }
- else if (r->action == ACTION_STORE_DIR)
- {
- sd->dir = _next_word(&opts);
- if ((sd->dir == NULL) || (sd->dir[0] != '/'))
- {
- str = NULL;
- asprintf(&str, "[%s syslogd] [%s %u] [%s %u] [Facility syslog] [%s Invalid path for \"store_directory\" action: %s]",
- ASL_KEY_SENDER,
- ASL_KEY_LEVEL, ASL_LEVEL_ERR,
- ASL_KEY_PID, getpid(),
- ASL_KEY_MSG, (sd->dir == NULL) ? "no path specified" : sd->dir);
-
- asl_log_string(str);
- free(str);
- free(sd);
- r->action = ACTION_NONE;
- return;
- }
- }
+ return path;
+}
- sd->mode = 0755;
- sd->next_id = 0;
- sd->uid = 0;
- sd->gid = 0;
- sd->flags = 0;
+static void
+_asl_action_handle_file_change_notification(int t)
+{
+ asl_out_module_t *m;
+ asl_out_rule_t *r;
- while (NULL != (p = _next_word(&opts)))
+ for (m = global.asl_out_module; m != NULL; m = m->next)
+ {
+ for (r = m->ruleset; r != NULL; r = r->next)
{
- if (!strcmp(p, "stayopen"))
+ if (r->action == ACTION_SET_FILE)
{
- sd->flags |= ACT_STORE_FLAG_STAY_OPEN;
+ asl_action_set_param_data_t *spdata = (asl_action_set_param_data_t *)r->private;
+ if ((spdata != NULL) && (spdata->token == t))
+ {
+ _asl_action_file_test(m, r);
+ return;
+ }
}
- else if (!strcmp(p, "continue"))
+ else if (r->action == ACTION_SET_PLIST)
{
- sd->flags |= ACT_STORE_FLAG_CONTINUE;
+ asl_action_set_param_data_t *spdata = (asl_action_set_param_data_t *)r->private;
+ if ((spdata != NULL) && (spdata->token == t))
+ {
+ char *str = _asl_action_profile_test(m, r);
+ free(str);
+ return;
+ }
}
- else if (!strncmp(p, "mode=0", 6))
+ else if (r->action == ACTION_SET_PROF)
{
- sd->mode = 0;
- x = *(p + 6);
- if ((x < '0') || (x > '7'))
+ asl_action_set_param_data_t *spdata = (asl_action_set_param_data_t *)r->private;
+ if ((spdata != NULL) && (spdata->token == t))
{
- free(p);
- free(sd->path);
- free(sd->dir);
- free(sd);
- r->action = ACTION_NONE;
+ char *str = _asl_action_profile_test(m, r);
+ free(str);
return;
}
+ }
+ }
+ }
- tmp_mode = x - '0';
- sd->mode += tmp_mode << 6;
+ asl_out_module_free(m);
+}
- x = *(p + 7);
- if ((x < '0') || (x > '7'))
- {
- free(p);
- free(sd->path);
- free(sd->dir);
- free(sd);
- r->action = ACTION_NONE;
- return;
- }
+static void
+_asl_action_post_process_rule(asl_out_module_t *m, asl_out_rule_t *r)
+{
+ if ((m == NULL) || (r == NULL)) return;
- tmp_mode = x - '0';
- sd->mode += tmp_mode << 3;
+ if (m != global.asl_out_module)
+ {
+ /* check if any previous module has used this destination */
+ asl_out_module_t *n;
+ bool search = true;
- x = *(p + 8);
- if ((x < '0') || (x > '7'))
+ if ((r->dst != NULL) && (r->dst->path != NULL))
+ {
+ for (n = global.asl_out_module; search && (n != NULL) && (n != m); n = n->next)
+ {
+ asl_out_rule_t *s;
+ for (s = n->ruleset; search && (s != NULL); s = s->next)
{
- free(p);
- free(sd->path);
- free(sd->dir);
- free(sd);
- r->action = ACTION_NONE;
- return;
+ if (s->action == ACTION_OUT_DEST)
+ {
+ if ((s->dst != NULL) && (s->dst->path != NULL) && (!strcmp(r->dst->path, s->dst->path)))
+ {
+ /* rule r of module m is using previously used dst of rule s of module n */
+ asl_out_dst_data_release(r->dst);
+ r->dst = NULL;
+
+ if (r->action == ACTION_OUT_DEST)
+ {
+ char *str = NULL;
+ asprintf(&str, "[Sender syslogd] [Level 5] [PID %u] [Message Configuration Notice:\nASL Module \"%s\" sharing output destination \"%s\" with ASL Module \"%s\".\nOutput parameters from ASL Module \"%s\" override any specified in ASL Module \"%s\".] [UID 0] [GID 0] [Facility syslog]", global.pid, m->name, s->dst->path, n->name, n->name, m->name);
+ internal_log_message(str);
+ free(str);
+ }
+ else
+ {
+ r->dst = asl_out_dst_data_retain(s->dst);
+ }
+
+ search = false;
+ }
+ }
}
+ }
+ }
+ }
+
+ if (r->action == ACTION_SET_PARAM)
+ {
+ if (r->query == NULL) _act_out_set_param(m, r->options, true);
+ }
+ else if (r->action == ACTION_CLAIM)
+ {
+ /* becomes ACTION_SKIP in com.apple.asl config */
+ if (m != global.asl_out_module)
+ {
+ asl_out_rule_t *rule = (asl_out_rule_t *)calloc(1, sizeof(asl_out_rule_t));
+ if (rule != NULL)
+ {
+ char *str = NULL;
+ asprintf(&str, "[Sender syslogd] [Level 5] [PID %u] [Message Configuration Notice:\nASL Module \"%s\" claims selected messages.\nThose messages may not appear in standard system log files or in the ASL database.] [UID 0] [GID 0] [Facility syslog]", global.pid, m->name);
+ internal_log_message(str);
+ free(str);
- tmp_mode = x - '0';
- sd->mode += tmp_mode;
+ rule->query = asl_msg_copy(r->query);
+ rule->action = ACTION_SKIP;
+ rule->next = global.asl_out_module->ruleset;
+ global.asl_out_module->ruleset = rule;
}
- else if (!strncmp(p, "mode=", 5)) sd->mode = atoi(p+4);
- else if (!strncmp(p, "uid=", 4)) sd->uid = atoi(p+4);
- else if (!strncmp(p, "gid=", 4)) sd->gid = atoi(p+4);
- free(p);
- p = NULL;
+ /*
+ * After adding ACTION_SKIP to com.apple.asl module, the claim becomes a no-op in this module
+ * UNLESS the claim includes the option "only". In that case, the claim becomes a filter:
+ * any messages that DO NOT match the claim are skipped by this module.
+ */
+ if (r->options == NULL) r->action = ACTION_NONE;
+ else if (strcmp(r->options, "only") != 0) r->action = ACTION_NONE;
}
-
- r->data = sd;
}
- else
+ else if (r->action == ACTION_ASL_STORE)
{
- sd = (struct store_data *)r->data;
+ action_asl_store_count++;
}
-
- if (r->action == ACTION_STORE_DIR)
+ else if (r->action == ACTION_ASL_DIR)
+ {
+ 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_asl_file_data_t *)calloc(1, sizeof(asl_action_asl_file_data_t));
+ }
+ else if (r->action == ACTION_FILE)
{
- val = asl_get(msg, ASL_KEY_TIME);
- if (val == NULL) return;
+ if (r->dst->private == NULL) r->dst->private = (asl_action_file_data_t *)calloc(1, sizeof(asl_action_file_data_t));
+ if (r->dst->private != NULL) ((asl_action_file_data_t *)(r->dst->private))->fd = -1;
+ }
+ else if (r->action == ACTION_SET_PLIST)
+ {
+ char *ident =_asl_action_profile_test(m, r);
+ char *notify_key = configuration_profile_create_notification_key(ident);
+ free(ident);
- tick = atol(val);
- status = _act_store_dir_setup(sd, tick);
- if (status != ASL_STATUS_OK)
+ if (notify_key != NULL)
{
- asldebug("_act_store_dir_setup %s failed: %s\n", sd->path, asl_core_error(status));
+ int status, token;
+ asl_action_set_param_data_t *spdata;
- /* disable further activity */
- asl_file_close(sd->store);
- sd->store = NULL;
- r->action = ACTION_NONE;
- return;
+ status = notify_register_dispatch(notify_key, &token, asl_action_queue, ^(int t){
+ _asl_action_handle_file_change_notification(t);
+ });
+
+ free(notify_key);
+
+ spdata = (asl_action_set_param_data_t *)calloc(1, sizeof(asl_action_set_param_data_t));
+ if (spdata == NULL)
+ {
+ notify_cancel(token);
+ }
+ else
+ {
+ spdata->token = token;
+ r->private = spdata;
+ }
}
}
-
- if (sd->store == NULL)
+ else if (r->action == ACTION_SET_PROF)
{
- s = NULL;
+ char *ident =_asl_action_profile_test(m, r);
+ char *notify_key = configuration_profile_create_notification_key(ident);
+ free(ident);
- mask = umask(0);
- status = asl_file_open_write(sd->path, (sd->mode & 0666), sd->uid, sd->gid, &s);
- umask(mask);
-
- if ((status != ASL_STATUS_OK) || (s == NULL))
+ if (notify_key != NULL)
{
- asldebug("asl_file_open_write %s failed: %s\n", sd->path, asl_core_error(status));
+ int status, token;
+ asl_action_set_param_data_t *spdata;
- /* disable further activity */
- asl_file_close(sd->store);
- sd->store = NULL;
- r->action = ACTION_NONE;
- return;
- }
+ status = notify_register_dispatch(notify_key, &token, asl_action_queue, ^(int t){
+ _asl_action_handle_file_change_notification(t);
+ });
- sd->store = s;
- }
+ free(notify_key);
- if (r->action != ACTION_STORE_DIR)
+ spdata = (asl_action_set_param_data_t *)calloc(1, sizeof(asl_action_set_param_data_t));
+ if (spdata == NULL)
+ {
+ notify_cancel(token);
+ }
+ else
+ {
+ spdata->token = token;
+ r->private = spdata;
+ }
+ }
+ }
+ else if (r->action == ACTION_SET_FILE)
{
- status = _act_store_file_setup(sd);
- if (status != ASL_STATUS_OK)
+ char *notify_key;
+ const char *path =_asl_action_file_test(m, r);
+
+ if (path != NULL)
{
- asldebug("_act_store_file_setup %s failed: %s\n", sd->path, asl_core_error(status));
+ asprintf(¬ify_key, "%s%s", NOTIFY_PATH_SERVICE, path);
+ if (notify_key != NULL)
+ {
+ int status, token;
+ asl_action_set_param_data_t *spdata;
- /* disable further activity */
- asl_file_close(sd->store);
- sd->store = NULL;
- r->action = ACTION_NONE;
- return;
+ status = notify_register_dispatch(notify_key, &token, asl_action_queue, ^(int t){
+ _asl_action_handle_file_change_notification(t);
+ });
+
+ free(notify_key);
+
+ spdata = (asl_action_set_param_data_t *)calloc(1, sizeof(asl_action_set_param_data_t));
+ if (spdata == NULL)
+ {
+ notify_cancel(token);
+ }
+ else
+ {
+ spdata->token = token;
+ r->private = spdata;
+ }
+ }
}
}
+}
- mid = sd->next_id;
+static void
+_asl_action_configure()
+{
+ asl_out_rule_t *r;
+ asl_out_module_t *m;
+ uint32_t flags = 0;
- status = asl_file_save(sd->store, msg, &mid);
- if (status != ASL_STATUS_OK)
- {
- asldebug("asl_file_save %s failed: %s\n", sd->path, asl_core_error(status));
+ if (global.asl_out_module == NULL) global.asl_out_module = asl_out_module_init();
+ if (global.asl_out_module == NULL) return;
- /* disable further activity on this file */
- asl_file_close(sd->store);
- sd->store = NULL;
- r->action = ACTION_NONE;
- return;
- }
+ asldebug("%s: init\n", MY_ID);
- if ((sd->flags & ACT_STORE_FLAG_STAY_OPEN) == 0)
- {
- asl_file_close(sd->store);
- sd->store = NULL;
- }
+ action_asl_store_count = 0;
- if ((sd->flags & ACT_STORE_FLAG_CONTINUE) == 0)
+ for (m = global.asl_out_module; m != NULL; m = m->next)
{
- opts = (char *)asl_get(msg, ASL_KEY_OPTION);
- if (opts == NULL)
+ for (r = m->ruleset; r != NULL; r = r->next)
{
- asl_set(msg, ASL_KEY_OPTION, ASL_OPT_IGNORE);
+ _asl_action_post_process_rule(m, r);
+ if (r->dst != NULL) flags |= (r->dst->flags & (MODULE_FLAG_ROTATE | MODULE_FLAG_CRASHLOG));
}
- else
+ }
+
+ 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)
{
- str = NULL;
- asprintf(&str, "%s %s", ASL_OPT_IGNORE, opts);
- if (str != NULL)
+ for (m = global.asl_out_module; m != NULL; m = m->next)
{
- asl_set(msg, ASL_KEY_OPTION, str);
- free(str);
+ 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);
}
}
+
+ sweep_time = time(NULL);
+
+ if (flags & MODULE_FLAG_ROTATE)
+ {
+ _act_file_checkpoint_all(CHECKPOINT_TEST);
+ if (checkpoint_timer == NULL) _start_cycling();
+ }
}
-static void
-_act_forward(action_rule_t *r, aslmsg msg)
+int
+asl_action_init(void)
{
- /* To do: <rdar://problem/6130747> Add a "forward" action to asl.conf */
+ static dispatch_once_t once;
+
+ dispatch_once(&once, ^{
+ 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_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)
+ {
+ 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
+ });
+
+ _asl_action_configure();
+
+ return 0;
}
+/*
+ * Close outputs and free modules.
+ */
static void
-send_to_asl_store(aslmsg msg)
+_asl_action_free_modules(asl_out_module_t *m)
{
- const char *vlevel, *val;
- uint64_t v64;
- uint32_t status, level, lmask;
- int x, log_me;
- action_rule_t *r;
-
- if (filter_token == -1)
+ asl_out_rule_t *r;
+ asl_out_module_t *x;
+
+ /*
+ * asl_common frees a list of modules with asl_out_module_free.
+ * This loop frees the private data attached some modules.
+ */
+ for (x = m; x != NULL; x = x->next)
{
- /* set up com.apple.syslog.asl_filter */
- status = notify_register_check(NOTIFY_SYSTEM_ASL_FILTER, &filter_token);
- if (status != NOTIFY_STATUS_OK)
- {
- filter_token = -1;
- }
- else
+ for (r = x->ruleset; r != NULL; r = r->next)
{
- status = notify_check(filter_token, &x);
- if (status == NOTIFY_STATUS_OK)
+ 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)
{
- v64 = global.asl_log_filter;
- status = notify_set_state(filter_token, v64);
+ _act_dst_close(r, DST_CLOSE_SHUTDOWN);
+ if (r->dst != NULL)
+ {
+ _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 *f_data = (asl_action_file_data_t *)r->dst->private;
+ if (f_data != NULL)
+ {
+ /* flush repeat message if necessary */
+ if (f_data->last_count > 0) _send_repeat_msg(r);
+ _asl_action_file_data_free(f_data);
+ r->dst->private = NULL;
+ }
+ }
+ }
+ else if (r->action == ACTION_SET_PLIST)
+ {
+ _asl_action_set_param_data_free((asl_action_set_param_data_t *)r->private);
}
- if (status != NOTIFY_STATUS_OK)
+ else if (r->action == ACTION_SET_PROF)
{
- notify_cancel(filter_token);
- filter_token = -1;
+ _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);
}
}
}
- /* ASLOption "store" forces a message to be saved */
- log_me = asl_check_option(msg, ASL_OPT_STORE);
- if (log_me == 1)
- {
- db_save_message(msg);
- return;
- }
-
- log_me = 0;
- if (filter_token >= 0)
- {
- x = 0;
- status = notify_check(filter_token, &x);
- if ((status == NOTIFY_STATUS_OK) && (x == 1))
- {
- v64 = 0;
- status = notify_get_state(filter_token, &v64);
- if ((status == NOTIFY_STATUS_OK) && (v64 != 0)) global.asl_log_filter = v64;
- }
- }
+ asl_out_module_free(m);
+}
- /* PID 0 (kernel) or PID 1 (launchd) messages are saved */
- val = asl_get(msg, ASL_KEY_PID);
- if ((val != NULL) && (atoi(val) <= 1)) log_me = 1;
- else
+static int
+_asl_action_close_internal(void)
+{
+#if TARGET_OS_EMBEDDED
+ if (crashmover_state != 0)
{
- vlevel = asl_get(msg, ASL_KEY_LEVEL);
- level = 7;
- if (vlevel != NULL) level = atoi(vlevel);
- lmask = ASL_FILTER_MASK(level);
- if ((lmask & global.asl_log_filter) != 0) log_me = 1;
+ dispatch_resume(crashlog_queue);
+ crashmover_state = 0;
}
- if (log_me == 0) return;
+ /* wait for the crashlog_queue to flush before _asl_action_free_modules() */
+ dispatch_sync(crashlog_queue, ^{ int x = 0; if (x == 1) x = 2; });
+#endif
- /* if there are no rules, save the message */
- if (asl_datastore_rule == NULL)
- {
- db_save_message(msg);
- return;
- }
+ _asl_action_free_modules(global.asl_out_module);
+ global.asl_out_module = NULL;
+ sweep_time = time(NULL);
- for (r = asl_datastore_rule; r != NULL; r = r->next)
- {
- if (asl_msg_cmp(r->query, (asl_msg_t *)msg) == 1)
- {
- /* if any rule matches, save the message (once!) */
- db_save_message(msg);
- return;
- }
- }
+ return 0;
}
-int
-asl_action_sendmsg(aslmsg msg, const char *outid)
+static void
+_asl_action_close_idle_files(time_t idle_time)
{
- action_rule_t *r;
-
- if (reset != RESET_NONE) _do_reset();
-
- if (msg == NULL) return -1;
+ asl_out_module_t *m;
+ time_t now = time(NULL);
- for (r = asl_action_rule; r != NULL; r = r->next)
+ for (m = global.asl_out_module; m != NULL; m = m->next)
{
- if (asl_msg_cmp(r->query, (asl_msg_t *)msg) == 1)
+ asl_out_rule_t *r;
+
+ for (r = m->ruleset; r != NULL; r = r->next)
{
- if ((r->action == ACTION_STORE) || (r->action == ACTION_STORE_DIR))
+ if (idle_time == 0)
+ {
+ if ((r->dst != NULL) && (r->dst->flags & MODULE_FLAG_CRASHLOG))
+ {
+ _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)
{
- _act_store(r, msg);
- if (asl_check_option(msg, ASL_OPT_IGNORE) != 0) return -1;
+ 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_FILE)
+ {
+ if (r->dst != NULL)
+ {
+ 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 *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);
+ }
}
-
- if (r->action == ACTION_NONE) continue;
- else if (r->action == ACTION_IGNORE) return -1;
- else if (r->action == ACTION_ACCESS) _act_access_control(r, msg);
- else if (r->action == ACTION_NOTIFY) _act_notify(r);
- else if (r->action == ACTION_BROADCAST) _act_broadcast(r, msg);
- else if (r->action == ACTION_FORWARD) _act_forward(r, msg);
}
}
+}
+
+int
+asl_action_close(void)
+{
+ dispatch_async(asl_action_queue, ^{
+ _asl_action_close_internal();
+ });
- if (asl_check_option(msg, ASL_OPT_IGNORE) != 0) return -1;
+ return 0;
+}
- send_to_asl_store(msg);
+int
+asl_action_reset(void)
+{
+ dispatch_async(asl_action_queue, ^{
+ _asl_action_close_internal();
+ asl_action_init();
+ });
return 0;
}
-static int
-_parse_config_file(const char *name)
+asl_out_module_t *
+_asl_action_module_with_name(const char *name)
{
- FILE *cf;
- char *line;
+ asl_out_module_t *m;
- cf = fopen(name, "r");
- if (cf == NULL) return 1;
+ if (global.asl_out_module == NULL) return NULL;
+ if (name == NULL) return global.asl_out_module;
- while (NULL != (line = get_line_from_file(cf)))
+ for (m = global.asl_out_module; m != NULL; m = m->next)
{
- _parse_line(line);
- free(line);
+ if ((m->name != NULL) && (!strcmp(m->name, name))) return m;
}
- fclose(cf);
-
- return 0;
+ return NULL;
}
+/*
+ * called from control_message
+ * Used to control modules dynamically.
+ * Line format "@ module param [value ...]"
+ *
+ * Note this is synchronous on asl_action queue.
+ */
int
-asl_action_init(void)
+asl_action_control_set_param(const char *s)
{
- asldebug("%s: init\n", MY_ID);
+ __block char **l;
+ __block char *p;
+ uint32_t count = 0;
- query = asl_msg_new(ASL_TYPE_QUERY);
- aslevent_addmatch(query, MY_ID);
- aslevent_addoutput(asl_action_sendmsg, MY_ID);
+ if (s == NULL) return -1;
+ if (s[0] == '\0') return 0;
- _parse_config_file(_PATH_ASL_CONF);
- return 0;
-}
+ /* skip '@' and whitespace */
+ if (*s == '@') s++;
+ while ((*s == ' ') || (*s == '\t')) s++;
-int
-asl_action_reset(void)
-{
- reset = global.reset;
- return 0;
-}
+ l = explode(s, " \t");
+ if (l != NULL) for (count = 0; l[count] != NULL; count++);
-int
-asl_action_close(void)
-{
- action_rule_t *r, *n;
- struct store_data *sd;
- n = NULL;
- for (r = asl_action_rule; r != NULL; r = n)
+ /* at least 2 parameters (l[0] = module, l[1] = param) required */
+ if (count < 2)
+ {
+ free_string_list(l);
+ return -1;
+ }
+
+ if (global.asl_out_module == NULL)
{
- n = r->next;
+ asldebug("asl_action_control_set_param: no modules loaded\n");
+ free_string_list(l);
+ return -1;
+ }
- if (((r->action == ACTION_STORE) || (r->action == ACTION_STORE_DIR) || (r->action == ACTION_NONE)) && (r->data != NULL))
+ /* create / modify a module */
+ if ((!strcasecmp(l[1], "define")) && (strcmp(l[0], "*")))
+ {
+ p = strdup(s);
+ if (p == NULL)
{
- sd = (struct store_data *)r->data;
- if (sd->store != NULL) asl_file_close(sd->store);
- if (sd->storedata != NULL) fclose(sd->storedata);
- free(sd->path);
- free(sd->dir);
- sd->store = NULL;
- free(sd);
+ asldebug("asl_action_control_set_param: memory allocation failed\n");
+ free_string_list(l);
+ return -1;
}
- if (r->query != NULL) asl_msg_release(r->query);
- free(r->options);
+ dispatch_sync(asl_action_queue, ^{
+ asl_out_module_t *m;
+ asl_out_rule_t *r;
- free(r);
- }
+ /* skip name, whitespace, "define" */
+ while ((*p != ' ') && (*p != '\t')) p++;
+ while ((*p == ' ') || (*p == '\t')) p++;
+ while ((*p != ' ') && (*p != '\t')) p++;
- asl_action_rule = NULL;
+ m = _asl_action_module_with_name(l[0]);
+ if (m == NULL)
+ {
+ asl_out_module_t *x;
- n = NULL;
- for (r = asl_datastore_rule; r != NULL; r = n)
- {
- n = r->next;
+ m = asl_out_module_new(l[0]);
+ for (x = global.asl_out_module; x->next != NULL; x = x->next);
+ x->next = m;
+ }
- if (r->query != NULL) asl_msg_release(r->query);
- free(r->options);
+ r = asl_out_module_parse_line(m, p);
+ if (r != NULL)
+ {
+ _asl_action_post_process_rule(m, r);
+ if ((r->dst != NULL) && (r->dst->flags & MODULE_FLAG_ROTATE))
+ {
+ _act_file_checkpoint_all(CHECKPOINT_TEST);
+ if (checkpoint_timer == NULL) _start_cycling();
+ }
+ }
+ });
- free(r);
+ free(p);
+ free_string_list(l);
+ return 0;
}
- asl_datastore_rule = NULL;
+ dispatch_sync(asl_action_queue, ^{
+ uint32_t intval;
+ int do_all = 0;
+ asl_out_module_t *m;
+
+ if (!strcmp(l[0], "*"))
+ {
+ do_all = 1;
+ m = _asl_action_module_with_name(NULL);
+ }
+ else
+ {
+ m = _asl_action_module_with_name(l[0]);
+ }
+
+ while (m != NULL)
+ {
+ if (!strcasecmp(l[1], "enable"))
+ {
+ intval = 1;
+
+ /* don't do enable for ASL_MODULE_NAME if input name is "*" */
+ if ((do_all == 0) || (strcmp(m->name, ASL_MODULE_NAME)))
+ {
+ /* @ module enable {0|1} */
+ if (count > 2) intval = atoi(l[2]);
+
+ if (intval == 0) m->flags &= ~MODULE_FLAG_ENABLED;
+ else m->flags |= MODULE_FLAG_ENABLED;
+ }
+ }
+ else if (!strcasecmp(l[1], "checkpoint"))
+ {
+ /* @ module checkpoint [file] */
+ if (count > 2) _act_file_checkpoint(m, l[2], CHECKPOINT_FORCE);
+ else _act_file_checkpoint(m, NULL, CHECKPOINT_FORCE);
+ }
+
+ if (do_all == 1) m = m->next;
+ else m = NULL;
+ }
+
+ });
+
+ free_string_list(l);
+ return 0;
+}
+
+int
+asl_action_file_checkpoint(const char *module, const char *path)
+{
+ /* Note this is synchronous on asl_action queue */
+ dispatch_sync(asl_action_queue, ^{
+ asl_out_module_t *m = _asl_action_module_with_name(module);
+ _act_file_checkpoint(m, path, CHECKPOINT_FORCE);
+ });
return 0;
}