/*
- * Copyright (c) 2004-2008 Apple Inc. All rights reserved.
+ * Copyright (c) 2004-2011 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
* @APPLE_LICENSE_HEADER_END@
*/
+#include <TargetConditionals.h>
+
+#if TARGET_IPHONE_SIMULATOR
+struct _not_empty;
+#else
+
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#define DST_TYPE_WALL 4
#define DST_TYPE_NOTE 5
-#define CLOSE_ON_IDLE_SEC 60
+#define CLOSE_ON_IDLE_SEC 300
-static asl_msg_t *query = NULL;
-static int reset = RESET_NONE;
-static pthread_mutex_t reset_lock = PTHREAD_MUTEX_INITIALIZER;
+static dispatch_queue_t bsd_out_queue;
+static dispatch_source_t bsd_idle_timer;
struct config_rule
{
int type;
struct sockaddr *addr;
char **facility;
+ uint32_t *fac_prefix_len;
int *pri;
uint32_t last_hash;
uint32_t last_count;
time_t last_time;
+ dispatch_source_t dup_timer;
char *last_msg;
TAILQ_ENTRY(config_rule) entries;
};
extern uint32_t asl_core_string_hash(const char *s, uint32_t inlen);
-int bsd_out_close();
-int bsd_out_network_reset(void);
-static int _parse_config_file(const char *);
-
-static void
-_do_reset(void)
-{
- pthread_mutex_lock(&reset_lock);
- if (reset == RESET_NONE)
- {
- pthread_mutex_unlock(&reset_lock);
- return;
- }
-
- if (reset == RESET_CONFIG)
- {
- bsd_out_close();
- _parse_config_file(_PATH_SYSLOG_CONF);
- }
- else if (reset == RESET_NETWORK)
- {
- bsd_out_network_reset();
- }
-
- reset = RESET_NONE;
-
- pthread_mutex_unlock(&reset_lock);
-}
-
static int
_level_for_name(const char *name)
{
static int
_parse_line(char *s)
{
- char **semi, **comma;
+ char **semi, **comma, *star;
int i, j, n, lasts, lastc, pri;
struct config_rule *out;
if (out->count == 0)
{
out->facility = (char **)calloc(1, sizeof(char *));
+ out->fac_prefix_len = (uint32_t *)calloc(1, sizeof(uint32_t));
out->pri = (int *)calloc(1, sizeof(int));
}
else
{
out->facility = (char **)reallocf(out->facility, (out->count + 1) * sizeof(char *));
+ out->fac_prefix_len = (uint32_t *)reallocf(out->fac_prefix_len, (out->count + 1) * sizeof(uint32_t));
out->pri = (int *)reallocf(out->pri, (out->count + 1) * sizeof(int));
}
if (out->facility == NULL) return -1;
+ if (out->fac_prefix_len == NULL) return -1;
if (out->pri == NULL) return -1;
out->facility[out->count] = _clean_facility_name(comma[j]);
if (out->facility[out->count] == NULL) return -1;
+ out->fac_prefix_len[out->count] = 0;
+ star = strchr(out->facility[out->count], '*');
+ if (star != NULL) out->fac_prefix_len[out->count] = (uint32_t)(star - out->facility[out->count]);
+
out->pri[out->count] = pri;
out->count++;
}
- freeList(comma);
+ free_string_list(comma);
}
- freeList(semi);
+ free_string_list(semi);
TAILQ_INSERT_TAIL(&bsd_out_rule, out, entries);
return 0;
}
-static char *
-bsd_log_string(const char *msg)
-{
- uint32_t i, len, outlen;
- char *out, *q;
- uint8_t c;
-
- if (msg == NULL) return NULL;
-
- len = strlen(msg);
- while ((len > 0) && (msg[len - 1] == '\n')) len--;
-
- if (len == 0) return NULL;
-
- outlen = len + 1;
- for (i = 0; i < len; i++)
- {
- c = msg[i];
- if (isascii(c) && iscntrl(c) && (c != '\t')) outlen++;
- }
-
- out = malloc(outlen);
- if (out == NULL) return NULL;
-
- q = out;
-
- for (i = 0; i < len; i++)
- {
- c = msg[i];
-
- if (isascii(c) && iscntrl(c))
- {
- if (c == '\n')
- {
- *q++ = '\\';
- *q++ = 'n';
- }
- else if (c == '\t')
- {
- *q++ = c;
- }
- else
- {
- *q++ = '^';
- *q++ = c ^ 0100;
- }
- }
- else
- {
- *q++ = c;
- }
- }
-
- *q = '\0';
-
- return out;
-}
-
static int
-_syslog_send_repeat_msg(struct config_rule *r)
+_bsd_send_repeat_msg(struct config_rule *r)
{
char vt[32], *msg;
time_t tick;
if (r->type != DST_TYPE_FILE) return 0;
if (r->last_count == 0) return 0;
- tick = time(NULL);
+ /* stop the timer */
+ dispatch_suspend(r->dup_timer);
+ tick = time(NULL);
memset(vt, 0, sizeof(vt));
ctime_r(&tick, vt);
vt[19] = '\0';
msg = NULL;
asprintf(&msg, "%s: --- last message repeated %u time%s ---\n", vt + 4, r->last_count, (r->last_count == 1) ? "" : "s");
+ r->last_count = 0;
if (msg == NULL) return -1;
len = strlen(msg);
}
static int
-_syslog_send(asl_msg_t *msg, struct config_rule *r, char **out, char **fwd, time_t now)
+_bsd_send(asl_msg_t *msg, struct config_rule *r, char **out, char **fwd, time_t now)
{
- char vt[16], tstr[32], *so, *sf, *outmsg;
- const char *vtime, *vhost, *vident, *vpid, *vmsg, *vlevel, *vfacility, *vrefproc, *vrefpid;
- size_t outlen, n;
- time_t tick;
+ char *sf, *outmsg;
+ const char *vlevel, *vfacility;
+ size_t outlen;
int pf, fc, status, is_dup, do_write;
- FILE *pw;
- uint32_t msg_hash;
+ uint32_t msg_hash, n;
if (out == NULL) return -1;
if (fwd == NULL) return -1;
if (r == NULL) return -1;
+ _syslog_dst_open(r);
+
if (r->type == DST_TYPE_NOTE)
{
notify_post(r->dst+1);
/* Build output string if it hasn't been built by a previous rule-match */
if (*out == NULL)
{
- tick = now;
- vtime = asl_get(msg, ASL_KEY_TIME);
- if (vtime != NULL)
- {
- /* aslmsg_verify converts time to seconds, but use current time if something went sour */
- tick = atol(vtime);
- if (tick == 0) tick = now;
- }
-
- memset(tstr, 0, sizeof(tstr));
- ctime_r(&tick, tstr);
- memcpy(vt, tstr+4, 15);
- vt[15] = '\0';
-
- vhost = asl_get(msg, ASL_KEY_HOST);
- if (vhost == NULL) vhost = "localhost";
-
- vident = asl_get(msg, ASL_KEY_SENDER);
- if ((vident != NULL) && (!strcmp(vident, "Unknown"))) vident = NULL;
-
- vpid = asl_get(msg, ASL_KEY_PID);
- if ((vpid != NULL) && (!strcmp(vpid, "-1"))) vpid = NULL;
-
- if ((vpid != NULL) && (vident == NULL)) vident = "Unknown";
-
- vrefproc = asl_get(msg, ASL_KEY_REF_PROC);
- vrefpid = asl_get(msg, ASL_KEY_REF_PID);
-
- vmsg = asl_get(msg, ASL_KEY_MSG);
- if (vmsg != NULL) outmsg = bsd_log_string(vmsg);
-
- n = 0;
- /* Time + " " */
- n += (strlen(vt) + 1);
-
- /* Host + " " */
- if (vhost != NULL) n += (strlen(vhost) + 1);
-
- /* Sender */
- if (vident != NULL) n += strlen(vident);
-
- /* "[" PID "]" */
- if (vpid != NULL) n += (strlen(vpid) + 2);
-
- /* " (" */
- if ((vrefproc != NULL) || (vrefpid != NULL)) n += 2;
-
- /* RefProc */
- if (vrefproc != NULL) n += strlen(vrefproc);
-
- /* "[" RefPID "]" */
- if (vrefpid != NULL) n += (strlen(vrefpid) + 2);
-
- /* ")" */
- if ((vrefproc != NULL) || (vrefpid != NULL)) n += 1;
-
- /* ": " */
- n += 2;
-
- /* Message */
- if (outmsg != NULL) n += strlen(outmsg);
-
- if (n == 0) return -1;
-
- /* "\n" + nul */
- n += 2;
-
- so = calloc(1, n);
- if (so == NULL) return -1;
-
- strcat(so, vt);
- strcat(so, " ");
-
- if (vhost != NULL)
- {
- strcat(so, vhost);
- strcat(so, " ");
- }
-
- if (vident != NULL)
- {
- strcat(so, vident);
- if (vpid != NULL)
- {
- strcat(so, "[");
- strcat(so, vpid);
- strcat(so, "]");
- }
- }
-
- if ((vrefproc != NULL) || (vrefpid != NULL))
- {
- strcat(so, " (");
-
- if (vrefproc != NULL) strcat(so, vrefproc);
-
- if (vrefpid != NULL)
- {
- strcat(so, "[");
- strcat(so, vrefpid);
- strcat(so, "]");
- }
-
- strcat(so, ")");
- }
-
- strcat(so, ": ");
-
- if (outmsg != NULL)
- {
- strcat(so, outmsg);
- free(outmsg);
- }
-
- strcat(so, "\n");
-
- *out = so;
+ *out = asl_format_message((asl_msg_t *)msg, ASL_MSG_FMT_BSD, ASL_TIME_FMT_LCL, ASL_ENCODE_SAFE, &n);
+ if (*out == NULL) return -1;
}
/* check if message is a duplicate of the last message, and inside the dup time window */
if ((*fwd == NULL) && (r->type == DST_TYPE_SOCK))
{
pf = 7;
- vlevel = asl_get(msg, ASL_KEY_LEVEL);
+ vlevel = asl_msg_get_val_for_key(msg, ASL_KEY_LEVEL);
if (vlevel != NULL) pf = atoi(vlevel);
- fc = asl_syslog_faciliy_name_to_num(asl_get(msg, ASL_KEY_FACILITY));
+ fc = asl_syslog_faciliy_name_to_num(asl_msg_get_val_for_key(msg, ASL_KEY_FACILITY));
if (fc > 0) pf |= fc;
sf = NULL;
*fwd = sf;
}
- outlen = 0;
if (r->type == DST_TYPE_SOCK) outlen = strlen(*fwd);
else outlen = strlen(*out);
- _syslog_dst_open(r);
-
if ((r->type == DST_TYPE_FILE) || (r->type == DST_TYPE_CONS))
{
/*
* If current message is NOT a duplicate and r->last_count > 0
* we need to write a "last message was repeated N times" log entry
*/
- if ((r->type == DST_TYPE_FILE) && (is_dup == 0) && (r->last_count > 0)) _syslog_send_repeat_msg(r);
+ if ((r->type == DST_TYPE_FILE) && (is_dup == 0) && (r->last_count > 0)) _bsd_send_repeat_msg(r);
do_write = 1;
* The kernel printf routine already sends them to /dev/console
* so writing them here would cause duplicates.
*/
- vfacility = asl_get(msg, ASL_KEY_FACILITY);
+ vfacility = asl_msg_get_val_for_key(msg, ASL_KEY_FACILITY);
if ((vfacility != NULL) && (!strcmp(vfacility, FACILITY_KERNEL)) && (r->type == DST_TYPE_CONS)) do_write = 0;
- if ((do_write == 1) && (r->type == DST_TYPE_FILE) && (is_dup == 1)) do_write = 0;
+ if ((do_write == 1) && (r->type == DST_TYPE_FILE) && (is_dup == 1))
+ {
+ do_write = 0;
+
+ if (r->dup_timer == NULL)
+ {
+ /* create a timer to flush dups on this file */
+ r->dup_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, bsd_out_queue);
+ dispatch_source_set_event_handler(r->dup_timer, ^{ _bsd_send_repeat_msg(r); });
+ }
+
+ if (r->last_count == 0)
+ {
+ /* start the timer */
+ dispatch_source_set_timer(r->dup_timer, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * global.bsd_max_dup_time), DISPATCH_TIME_FOREVER, 0);
+ dispatch_resume(r->dup_timer);
+ }
+ }
if (do_write == 0) status = outlen;
else status = write(r->fd, *out, outlen);
}
else if (r->type == DST_TYPE_WALL)
{
- pw = popen(_PATH_WALL, "w");
+#if !TARGET_OS_EMBEDDED
+ FILE *pw = popen(_PATH_WALL, "w");
if (pw < 0)
{
asldebug("%s: error sending wall message: %s\n", MY_ID, strerror(errno));
fprintf(pw, "%s", *out);
pclose(pw);
+#endif
}
if (is_dup == 1)
}
else
{
- if (r->last_msg != NULL) free(r->last_msg);
+ free(r->last_msg);
r->last_msg = NULL;
if (*out != NULL) r->last_msg = strdup(*out + 16);
}
static int
-_syslog_rule_match(asl_msg_t *msg, struct config_rule *r)
+_bsd_rule_match(asl_msg_t *msg, struct config_rule *r)
{
- uint32_t i, test, f, pri;
+ uint32_t i, test, f;
+ int32_t pri;
const char *val;
if (msg == NULL) return 0;
if ((test == 0) && (r->pri[i] == -2)) continue;
f = 0;
- if (strcmp(r->facility[i], "*") == 0) f = 1;
- else
+ val = asl_msg_get_val_for_key(msg, ASL_KEY_FACILITY);
+
+ if (strcmp(r->facility[i], "*") == 0)
+ {
+ f = 1;
+ }
+ else if ((r->fac_prefix_len[i] > 0) && (strncasecmp(r->facility[i], val, r->fac_prefix_len[i]) == 0))
+ {
+ f = 1;
+ }
+ else if ((val != NULL) && (strcasecmp(r->facility[i], val) == 0))
{
- val = asl_get(msg, ASL_KEY_FACILITY);
- if ((val != NULL) && (strcasecmp(r->facility[i], val) == 0)) f = 1;
+ f = 1;
}
if (f == 0) continue;
continue;
}
- val = asl_get(msg, ASL_KEY_LEVEL);
+ val = asl_msg_get_val_for_key(msg, ASL_KEY_LEVEL);
if (val == NULL) continue;
pri = atoi(val);
return test;
}
-int
-bsd_out_sendmsg(asl_msg_t *msg, const char *outid)
+static int
+_bsd_match_and_send(asl_msg_t *msg)
{
struct config_rule *r;
char *out, *fwd;
- time_t tick;
- uint64_t delta;
-
- if (reset != RESET_NONE) _do_reset();
+ time_t now;
if (msg == NULL) return -1;
out = NULL;
fwd = NULL;
- tick = time(NULL);
- global.bsd_flush_time = 0;
+ now = time(NULL);
for (r = bsd_out_rule.tqh_first; r != NULL; r = r->entries.tqe_next)
{
- if (_syslog_rule_match(msg, r) == 1) _syslog_send(msg, r, &out, &fwd, tick);
- if ((r->type == DST_TYPE_FILE) && (r->last_count > 0))
- {
- delta = tick - r->last_time;
- if (delta < global.bsd_max_dup_time)
- {
- delta = global.bsd_max_dup_time - delta;
- if (global.bsd_flush_time == 0) global.bsd_flush_time = delta;
- else if (delta < global.bsd_flush_time) global.bsd_flush_time = delta;
- }
- }
+ if (_bsd_rule_match(msg, r) == 1) _bsd_send(msg, r, &out, &fwd, now);
}
- if (out != NULL) free(out);
- if (fwd != NULL) free(fwd);
+ free(out);
+ free(fwd);
return 0;
}
void
-bsd_close_idle_files(time_t now)
+bsd_out_message(asl_msg_t *msg, int64_t msize)
{
- struct config_rule *r;
- uint64_t delta;
+ if (msg == NULL) return;
- for (r = bsd_out_rule.tqh_first; r != NULL; r = r->entries.tqe_next)
- {
- /* only applies to files */
- if (r->type != DST_TYPE_FILE) continue;
+ OSAtomicIncrement32(&global.bsd_queue_count);
+ asl_msg_retain((asl_msg_t *)msg);
- /*
- * If the last message repeat count is non-zero, a bsd_flush_duplicates()
- * call will occur within 30 seconds. Don't bother closing the file.
- */
- if (r->last_count > 0) continue;
+ dispatch_async(bsd_out_queue, ^{
+ _bsd_match_and_send(msg);
+ asl_msg_release((asl_msg_t *)msg);
- delta = now - r->last_time;
- if (delta > CLOSE_ON_IDLE_SEC) _syslog_dst_close(r);
- }
+ /* end of the output module chain (after asl) - decrement global memory stats */
+ OSAtomicAdd64(-1ll * msize, &global.memory_size);
+
+ OSAtomicDecrement32(&global.bsd_queue_count);
+ });
}
-void
-bsd_flush_duplicates(time_t now)
+static void
+_bsd_close_idle_files()
{
+ time_t now;
struct config_rule *r;
uint64_t delta;
- global.bsd_flush_time = 0;
+ now = time(NULL);
for (r = bsd_out_rule.tqh_first; r != NULL; r = r->entries.tqe_next)
{
+ /* only applies to files */
if (r->type != DST_TYPE_FILE) continue;
- if (r->last_count > 0)
- {
- delta = now - r->last_time;
- if (delta < global.bsd_max_dup_time)
- {
- delta = global.bsd_max_dup_time - delta;
- if (global.bsd_flush_time == 0) global.bsd_flush_time = delta;
- else if (delta < global.bsd_flush_time) global.bsd_flush_time = delta;
- }
- else
- {
- _syslog_dst_open(r);
- _syslog_send_repeat_msg(r);
+ /*
+ * If the last message repeat count is non-zero, a _bsd_flush_duplicates()
+ * call will occur within 30 seconds. Don't bother closing the file.
+ */
+ if (r->last_count > 0) continue;
- r->last_count = 0;
- }
- }
+ delta = now - r->last_time;
+ if (delta > CLOSE_ON_IDLE_SEC) _syslog_dst_close(r);
}
}
int
bsd_out_init(void)
{
+ static dispatch_once_t once;
+
asldebug("%s: init\n", MY_ID);
TAILQ_INIT(&bsd_out_rule);
+ _parse_config_file(_PATH_SYSLOG_CONF);
- query = asl_new(ASL_TYPE_QUERY);
- aslevent_addmatch(query, MY_ID);
- aslevent_addoutput(bsd_out_sendmsg, MY_ID);
+ dispatch_once(&once, ^{
+ bsd_out_queue = dispatch_queue_create("BSD Out Queue", NULL);
- _parse_config_file(_PATH_SYSLOG_CONF);
- return 0;
-}
+ /* start a timer to close idle files */
+ bsd_idle_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, bsd_out_queue);
+ dispatch_source_set_event_handler(bsd_idle_timer, ^{ _bsd_close_idle_files(); });
+ dispatch_source_set_timer(bsd_idle_timer, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * CLOSE_ON_IDLE_SEC), NSEC_PER_SEC * CLOSE_ON_IDLE_SEC, 0);
+ dispatch_resume(bsd_idle_timer);
+ });
-int
-bsd_out_reset(void)
-{
- reset = global.reset;
return 0;
}
-int
-bsd_out_close(void)
+static int
+_bsd_out_close_internal(void)
{
struct config_rule *r, *n;
int i;
{
n = r->entries.tqe_next;
- if (r->dst != NULL) free(r->dst);
- if (r->fd > 0) close(r->fd);
- if (r->addr != NULL) free(r->addr);
- if (r->last_msg != NULL) free(r->last_msg);
+ if (r->dup_timer != NULL)
+ {
+ if (r->last_count > 0) _bsd_send_repeat_msg(r);
+ dispatch_source_cancel(r->dup_timer);
+ dispatch_resume(r->dup_timer);
+ dispatch_release(r->dup_timer);
+ }
+
+ free(r->dst);
+ free(r->addr);
+ free(r->last_msg);
+ free(r->fac_prefix_len);
+ free(r->pri);
+
+ if (r->fd >= 0) close(r->fd);
+
if (r->facility != NULL)
{
for (i = 0; i < r->count; i++)
- {
- if (r->facility[i] != NULL) free(r->facility[i]);
- }
+ free(r->facility[i]);
+
free(r->facility);
}
- if (r->pri != NULL) free(r->pri);
+
TAILQ_REMOVE(&bsd_out_rule, r, entries);
free(r);
}
int
-bsd_out_network_reset(void)
+bsd_out_close(void)
{
- struct config_rule *r;
+ dispatch_async(bsd_out_queue, ^{
+ _bsd_out_close_internal();
+ });
- for (r = bsd_out_rule.tqh_first; r != NULL; r = r->entries.tqe_next)
- {
- if (r->type == DST_TYPE_SOCK)
- {
- close(r->fd);
- r->fd = -1;
- }
- }
+ return 0;
+}
+
+int
+bsd_out_reset(void)
+{
+ dispatch_async(bsd_out_queue, ^{
+ _bsd_out_close_internal();
+ bsd_out_init();
+ });
return 0;
}
+
+#endif /* !TARGET_IPHONE_SIMULATOR */