out status : int;
SecToken token : security_token_t
);
+
+simpleroutine _asl_server_message
+(
+ server : mach_port_t;
+ message : ooline_data, dealloc;
+ ServerAuditToken token : audit_token_t
+);
}
}
- if (i >= s->record_count) return ASL_STATUS_OK;
+ if (i >= s->record_count)
+ {
+ if (qp != NULL)
+ {
+ for (i = 0; i < query->count; i++) asl_memory_record_free(s, qp[i]);
+ free(qp);
+ free(qtype);
+ }
+
+ return ASL_STATUS_OK;
+ }
start = where;
}
}
- if (i >= s->record_count) return ASL_STATUS_OK;
+ if (i >= s->record_count)
+ {
+ if (qp != NULL)
+ {
+ for (i = 0; i < query->count; i++) asl_mini_memory_record_free(s, qp[i]);
+ free(qp);
+ free(qtype);
+ }
+
+ return ASL_STATUS_OK;
+ }
start = where;
Extra_LD_Flags = -dead_strip
include $(MAKEFILEPATH)/CoreOS/ReleaseControl/BSDCommon.make
+
+after_install:
+ codesign -s- $(DSTROOT)/usr/sbin/aslmanager
-.\"Copyright (c) 2004-2008 Apple Inc. All rights reserved.
+.\"Copyright (c) 2004-2009 Apple Inc. All rights reserved.
.\"
.\"@APPLE_LICENSE_HEADER_START@
.\"
is started automatically at various times.
It runs shortly after the
.Nm syslogd
-server starts, at midnight (local time) if the system is running,
+server starts, upon receipt of the first message after midnight (local time),
and any time a file in the ASL data store directory (/var/log/asl) reaches a maximum size limit as specified to the
.Nm syslogd
server.
.Pp
+Command-line options may be specified in the aslmanager launch plist file.
+However,
+.Nm
+reads the /etc/asl.conf file which may also contain parameter settings.
+Settings in the asl.conf file will override those given on the command line.
+.Pp
.Nm
scans through the files in the ASL data store directory /var/log/asl, or some other directory specified following the
.Fl s
-flag.
-Files that contain only messages that are older than the default 2 day time-to-live are either archived or removed.
-The default 2 day time-to-live value may be overridden by supplying a value following the
+flag, or by the setting of the store_path parameter in asl.conf.
+Data files that are older than the default 7 day time-to-live are either archived or removed.
+Files that contain messages with explicit expire times are removed or archived monthly after all thier contents expire.
+The default 7 day time-to-live value may be overridden by supplying a value following the
.Fl ttl
-flag.
+flag, or by the setting of the store_ttl parameter in asl.conf.
A value of zero allows files to remain in the store with no time limit.
.Pp
-A maximum size may be provided as a value following the
+A maximum size for the entire data store may be provided as a value following the
.Fl size
-flag.
+flag, or by the setting of the max_store_size parameter in asl.conf.
This will cause
.Nm
-to archive (if
-.Fl a
-is specified) and remove files until the total size of the data store is
+to archive (if enabled) and remove files until the total size of the data store is
.Ar max_size
bytes or less.
+The default value is 150000000 bytes.
+A value of zero means the size is unlimited.
+An unlimited size specification should be used with great caution,
+since a runaway process could quickly fill all available disk space.
Files are removed in order starting from oldest to newest.
Files with the same date are removed in standard lexigraphic sort order by file name.
.Pp
An alternate directory path may be specified following the
.Fl a
flag.
+The archive parameter setting in asl.conf enables or disables archiving.
+The archive parameter requires a value of "1" to enable archiving, or a value of "0" to disable it.
+An option archive directory path may follow the "0" or "1".
.Sh SEE ALSO
.Xr syslogd 8 ,
.Xr syslog 1 ,
.Xr asl 3 ,
-.Xr syslog 3 ,
+.Xr asl.conf 5 ,
+.Xr syslog 3 .
.Sh HISTORY
The
.Nm
-utility appeared in Mac OS X 10.5.6.
+utility appeared in Mac OS X 10.6.
/*
- * Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+ * Copyright (c) 2007-2009 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
#include <asl_store.h>
#define SECONDS_PER_DAY 86400
-#define DEFAULT_MAX_SIZE 51200000
-#define DEFAULT_TTL 2
+#define DEFAULT_MAX_SIZE 150000000
+#define DEFAULT_TTL 7
-#define LONGTTL_TMP_FILE "LongTTL.new"
+#define IndexNull (uint32_t)-1
+#define _PATH_ASL_CONF "/etc/asl.conf"
+
+/* global */
+static char *archive = NULL;
+static char *store_dir = PATH_ASL_STORE;
+static time_t ttl;
+static size_t max_size;
+static mode_t archive_mode = 0400;
+static int debug;
typedef struct name_list_s
{
struct name_list_s *next;
} name_list_t;
-void
-mgr_exit(const char *store, int status)
-{
- char *s;
-
- if (store == NULL) exit(status);
-
- s = NULL;
- asprintf(&s, "%s/%s", store, FILE_ASL_STORE_SWEEP_SEMAPHORE);
- if (s != NULL)
- {
- unlink(s);
- free(s);
- }
- else exit(1);
-
- exit(status);
-}
-
name_list_t *
add_to_list(name_list_t *l, const char *name, size_t size)
{
}
uint32_t
-do_match(const char *infile, const char *outfile, int do_ttl, time_t expire_time)
+do_copy(const char *infile, const char *outfile, mode_t mode)
{
- asl_search_result_t q, *query, *res;
- asl_msg_t *m, *qm[1];
- asl_file_t *in, *out;
+ asl_search_result_t *res;
+ asl_file_t *f;
uint32_t status, i;
- char str[64];
uint64_t mid;
if (infile == NULL) return ASL_STATUS_INVALID_ARG;
if (outfile == NULL) return ASL_STATUS_INVALID_ARG;
- in = NULL;
- status = asl_file_open_read(infile, &in);
+ f = NULL;
+ status = asl_file_open_read(infile, &f);
if (status != ASL_STATUS_OK) return status;
- query = NULL;
- q.count = 1;
- q.curr = 0;
- q.msg = qm;
- qm[0] = NULL;
- m = NULL;
+ res = NULL;
+ mid = 0;
- if (do_ttl == 1)
+ status = asl_file_match(f, NULL, &res, &mid, 0, 0, 1);
+ asl_file_close(f);
+
+ if (status != ASL_STATUS_OK) return status;
+ if (res->count == 0)
+ {
+ aslresponse_free(res);
+ return ASL_STATUS_OK;
+ }
+
+ f = NULL;
+ status = asl_file_open_write(outfile, mode, -1, -1, &f);
+ if (status != ASL_STATUS_OK) return status;
+ if (f == ASL_STATUS_OK) return ASL_STATUS_FAILED;
+
+ f->flags = ASL_FILE_FLAG_UNLIMITED_CACHE | ASL_FILE_FLAG_PRESERVE_MSG_ID;
+
+ for (i = 0; i < res->count; i++)
{
- query = &q;
- m = asl_new(ASL_TYPE_QUERY);
- if (m == NULL)
+ mid = 0;
+ status = asl_file_save(f, res->msg[i], &mid);
+ if (status != ASL_STATUS_OK) break;
+ }
+
+ asl_file_close(f);
+ return status;
+}
+
+static char **
+_insertString(char *s, char **l, uint32_t x)
+{
+ int i, len;
+
+ if (s == NULL) return l;
+ if (l == NULL)
+ {
+ l = (char **)malloc(2 * sizeof(char *));
+ if (l == NULL) return NULL;
+
+ l[0] = strdup(s);
+ if (l[0] == NULL)
{
- asl_file_close(in);
- return ASL_STATUS_NO_MEMORY;
+ free(l);
+ return NULL;
}
- qm[0] = m;
+ l[1] = NULL;
+ return l;
+ }
+
+ for (i = 0; l[i] != NULL; i++);
+ len = i + 1; /* count the NULL on the end of the list too! */
+
+ l = (char **)reallocf(l, (len + 1) * sizeof(char *));
+ if (l == NULL) return NULL;
- if (expire_time != 0)
+ if ((x >= (len - 1)) || (x == IndexNull))
+ {
+ l[len - 1] = strdup(s);
+ if (l[len - 1] == NULL)
{
- snprintf(str, sizeof(str), "%llu", (long long unsigned int)expire_time);
- if (asl_set_query(m, ASL_KEY_EXPIRE_TIME, str, ASL_QUERY_OP_NUMERIC | ASL_QUERY_OP_GREATER_EQUAL) != 0)
- {
- asl_file_close(in);
- asl_free(m);
- return ASL_STATUS_NO_MEMORY;
- }
+ free(l);
+ return NULL;
}
- else
+
+ l[len] = NULL;
+ return l;
+ }
+
+ for (i = len; i > x; i--) l[i] = l[i - 1];
+ l[x] = strdup(s);
+ if (l[x] == NULL) return NULL;
+
+ return l;
+}
+
+char **
+explode(const char *s, const char *delim)
+{
+ char **l = NULL;
+ const char *p;
+ char *t, quote;
+ int i, n;
+
+ if (s == NULL) return NULL;
+
+ quote = '\0';
+
+ p = s;
+ while (p[0] != '\0')
+ {
+ /* scan forward */
+ for (i = 0; p[i] != '\0'; i++)
{
- if (asl_set_query(m, ASL_KEY_EXPIRE_TIME, NULL, ASL_QUERY_OP_TRUE) != 0)
+ if (quote == '\0')
+ {
+ /* not inside a quoted string: check for delimiters and quotes */
+ if (strchr(delim, p[i]) != NULL) break;
+ else if (p[i] == '\'') quote = p[i];
+ else if (p[i] == '"') quote = p[i];
+ }
+ else
{
- asl_file_close(in);
- asl_free(m);
- return ASL_STATUS_NO_MEMORY;
+ /* inside a quoted string - look for matching quote */
+ if (p[i] == quote) quote = '\0';
}
}
+
+ n = i;
+ t = malloc(n + 1);
+ if (t == NULL) return NULL;
+
+ for (i = 0; i < n; i++) t[i] = p[i];
+ t[n] = '\0';
+ l = _insertString(t, l, IndexNull);
+ free(t);
+ t = NULL;
+ if (p[i] == '\0') return l;
+ if (p[i + 1] == '\0') l = _insertString("", l, IndexNull);
+ p = p + i + 1;
}
- res = NULL;
- mid = 0;
- status = asl_file_match(in, query, &res, &mid, 0, 0, 1);
- if (m != NULL) asl_free(m);
- asl_file_close(in);
+ return l;
+}
- if (status != ASL_STATUS_OK) return status;
+void
+freeList(char **l)
+{
+ int i;
- /*
- * N.B. "ASL_STATUS_NOT_FOUND" is never returned by asl_file_match.
- * We use it here to signal the caller that no records were found by the match.
- */
- if (res == NULL) return ASL_STATUS_NOT_FOUND;
- if (res->count == 0)
+ if (l == NULL) return;
+ for (i = 0; l[i] != NULL; i++) free(l[i]);
+ free(l);
+}
+
+/*
+ * Used to sed config parameters.
+ * Line format "= name value"
+ */
+static void
+_parse_set_param(char *s)
+{
+ char **l;
+ uint32_t count;
+
+ if (s == NULL) return;
+ if (s[0] == '\0') return;
+
+ /* skip '=' and whitespace */
+ s++;
+ while ((*s == ' ') || (*s == '\t')) s++;
+
+ l = explode(s, " \t");
+ if (l == NULL) return;
+
+ for (count = 0; l[count] != NULL; count++);
+
+ /* name is required */
+ if (count == 0)
{
- aslresponse_free(res);
- return ASL_STATUS_NOT_FOUND;
+ freeList(l);
+ return;
}
- out = NULL;
- status = asl_file_open_write(outfile, 0644, -1, -1, &out);
- if (status != ASL_STATUS_OK) return status;
+ /* value is required */
+ if (count == 1)
+ {
+ freeList(l);
+ return;
+ }
- out->flags = ASL_FILE_FLAG_UNLIMITED_CACHE | ASL_FILE_FLAG_PRESERVE_MSG_ID;
+ if (!strcasecmp(l[0], "aslmanager_debug"))
+ {
+ /* = debug {0|1} */
+ debug = atoi(l[1]);
+ }
+ else if (!strcasecmp(l[0], "store_ttl"))
+ {
+ /* = store_ttl days */
+ ttl = SECONDS_PER_DAY * (time_t)atoll(l[1]);
+ }
+ else if (!strcasecmp(l[0], "max_store_size"))
+ {
+ /* = max_file_size bytes */
+ max_size = atoi(l[1]);
+ }
+ else if (!strcasecmp(l[0], "archive"))
+ {
+ /* = archive {0|1} path */
+ if (!strcmp(l[1], "1"))
+ {
+ if (l[2] == NULL) archive = PATH_ASL_ARCHIVE;
+ else archive = strdup(l[2]); /* never freed */
+ }
+ else archive = NULL;
+ }
+ else if (!strcasecmp(l[0], "store_path"))
+ {
+ /* = archive path */
+ store_dir = strdup(l[1]); /* never freed */
+ }
+ else if (!strcasecmp(l[0], "archive_mode"))
+ {
+ archive_mode = strtol(l[1], NULL, 0);
+ if ((archive_mode == 0) && (errno == EINVAL)) archive_mode = 0400;
+ }
- for (i = 0; i < res->count; i++)
+ freeList(l);
+}
+
+static void
+_parse_line(char *s)
+{
+ if (s == NULL) return;
+ while ((*s == ' ') || (*s == '\t')) s++;
+
+ /*
+ * First non-whitespace char is the rule type.
+ * aslmanager only checks "=" (set parameter) rules.
+ */
+ if (*s == '=') _parse_set_param(s);
+}
+
+char *
+get_line_from_file(FILE *f)
+{
+ char *s, *out;
+ size_t len;
+
+ out = fgetln(f, &len);
+ if (out == NULL) return NULL;
+ if (len == 0) return NULL;
+
+ s = malloc(len + 1);
+ if (s == NULL) return NULL;
+
+ memcpy(s, out, len);
+
+ s[len - 1] = '\0';
+ return s;
+}
+
+static int
+_parse_config_file(const char *name)
+{
+ FILE *cf;
+ char *line;
+
+ cf = fopen(name, "r");
+ if (cf == NULL) return 1;
+
+ while (NULL != (line = get_line_from_file(cf)))
{
- mid = 0;
- status = asl_file_save(out, res->msg[i], &mid);
- if (status != ASL_STATUS_OK) break;
+ _parse_line(line);
+ free(line);
}
- asl_file_close(out);
- return status;
+ fclose(cf);
+
+ return 0;
}
int
main(int argc, const char *argv[])
{
- int i, bbstrlen, debug;
- const char *archive, *store_dir;
- time_t now, best_before, ttl;
+ int i, today_ymd_stringlen, expire_ymd_stringlen;
+ time_t now, ymd_expire;
struct tm ctm;
- char bbstr[32], *str, *p;
+ char today_ymd_string[32], expire_ymd_string[32], *str;
DIR *dp;
struct dirent *dent;
- name_list_t *list, *e;
+ name_list_t *ymd_list, *bb_list, *e;
uint32_t status;
- size_t file_size, store_size, max_size;
+ size_t file_size, store_size;
struct stat sb;
- list = NULL;
+ ymd_list = NULL;
+ bb_list = NULL;
- archive = NULL;
- store_dir = PATH_ASL_STORE;
ttl = DEFAULT_TTL * SECONDS_PER_DAY;
max_size = DEFAULT_MAX_SIZE;
store_size = 0;
{
if (!strcmp(argv[i], "-a"))
{
- if (((i + 1) < argc) && (argv[i + 1][0] != '-')) archive = argv[++i];
+ if (((i + 1) < argc) && (argv[i + 1][0] != '-')) archive = (char *)argv[++i];
else archive = PATH_ASL_ARCHIVE;
}
else if (!strcmp(argv[i], "-s"))
{
- if (((i + 1) < argc) && (argv[i + 1][0] != '-')) store_dir = argv[++i];
+ if (((i + 1) < argc) && (argv[i + 1][0] != '-')) store_dir = (char *)argv[++i];
}
else if (!strcmp(argv[i], "-ttl"))
{
}
}
+ _parse_config_file(_PATH_ASL_CONF);
+
+ if (debug == 1) printf("aslmanager starting\n");
+
/* check archive */
if (archive != NULL)
{
{
fprintf(stderr, "aslmanager error: can't create archive %s: %s\n", archive, strerror(errno));
return -1;
- }
+ }
}
else
{
chdir(store_dir);
- /* determine current time and time TTL ago */
+ /* determine current time */
now = time(NULL);
- best_before = 0;
- if (ttl > 0) best_before = now - ttl;
- /* construct best before date as YYYY.MM.DD */
+ /* ttl 0 means files never expire */
+ ymd_expire = 0;
+ if (ttl > 0) ymd_expire = now - ttl;
+
+ /* construct today's date as YYYY.MM.DD */
memset(&ctm, 0, sizeof(struct tm));
- if (localtime_r((const time_t *)&best_before, &ctm) == NULL) mgr_exit(store_dir, 1);
+ if (localtime_r((const time_t *)&now, &ctm) == NULL) return -1;
- snprintf(bbstr, sizeof(bbstr), "%d.%02d.%02d.", ctm.tm_year + 1900, ctm.tm_mon + 1, ctm.tm_mday);
- bbstrlen = strlen(bbstr);
+ snprintf(today_ymd_string, sizeof(today_ymd_string), "%d.%02d.%02d.", ctm.tm_year + 1900, ctm.tm_mon + 1, ctm.tm_mday);
+ today_ymd_stringlen = strlen(today_ymd_string);
- if (debug == 1) printf("Best Before Date %s\n", bbstr);
+ /* construct regular file expiry date as YYYY.MM.DD */
+ memset(&ctm, 0, sizeof(struct tm));
+ if (localtime_r((const time_t *)&ymd_expire, &ctm) == NULL) return -1;
- dp = opendir(store_dir);
- if (dp == NULL) mgr_exit(store_dir, 1);
+ snprintf(expire_ymd_string, sizeof(expire_ymd_string), "%d.%02d.%02d.", ctm.tm_year + 1900, ctm.tm_mon + 1, ctm.tm_mday);
+ expire_ymd_stringlen = strlen(expire_ymd_string);
- /* gather a list of files for dates before the best before date */
+ if (debug == 1) printf("Expiry Date %s\n", expire_ymd_string);
+ dp = opendir(store_dir);
+ if (dp == NULL) return -1;
+
+ /* gather a list of YMD files and BB files */
while ((dent = readdir(dp)) != NULL)
{
- if ((dent->d_name[0] < '0') || (dent->d_name[0] > '9')) continue;
-
memset(&sb, 0, sizeof(struct stat));
file_size = 0;
if (stat(dent->d_name, &sb) == 0) file_size = sb.st_size;
- store_size += file_size;
- list = add_to_list(list, dent->d_name, file_size);
+ if ((dent->d_name[0] >= '0') && (dent->d_name[0] <= '9'))
+ {
+ ymd_list = add_to_list(ymd_list, dent->d_name, file_size);
+ store_size += file_size;
+ }
+ else if (!strncmp(dent->d_name, "BB.", 3) && (dent->d_name[3] >= '0') && (dent->d_name[3] <= '9'))
+ {
+ bb_list = add_to_list(bb_list, dent->d_name, file_size);
+ store_size += file_size;
+ }
+ else if ((!strcmp(dent->d_name, ".")) || (!strcmp(dent->d_name, "..")))
+ {}
+ else if ((!strcmp(dent->d_name, "StoreData")) || (!strcmp(dent->d_name, "SweepStore")))
+ {}
+ else
+ {
+ fprintf(stderr, "aslmanager: unexpected file %s in ASL data store\n", dent->d_name);
+ }
}
closedir(dp);
if (debug == 1)
{
- printf("\nData Store Size = %lu\n", store_size);
- printf("\nData Store Files\n");
- for (e = list; e != NULL; e = e->next) printf(" %s %lu\n", e->name, e->size);
+ printf("Data Store Size = %lu\n", store_size);
+ printf("Data Store YMD Files\n");
+ for (e = ymd_list; e != NULL; e = e->next) printf(" %s %lu\n", e->name, e->size);
+ printf("Data Store BB Files\n");
+ for (e = bb_list; e != NULL; e = e->next) printf(" %s %lu\n", e->name, e->size);
}
- /* copy messages in each expired file with ASLExpireTime values to LongTTL files */
- if (debug == 1) printf("\nStart Scan\n");
+ /* Delete/achive expired YMD files */
+ if (debug == 1) printf("Start YMD Scan\n");
- e = list;
+ e = ymd_list;
while (e != NULL)
{
- if ((store_size <= max_size) && (strncmp(e->name, bbstr, bbstrlen) >= 0)) break;
+ /* stop when a file name/date is after the expire date */
+ if (strncmp(e->name, expire_ymd_string, expire_ymd_stringlen) > 0) break;
- /* find '.' after year */
- p = strchr(e->name, '.');
- if (p == NULL) continue;
+ if (archive != NULL)
+ {
+ str = NULL;
+ asprintf(&str, "%s/%s", archive, e->name);
+ if (str == NULL) return -1;
- /* find '.' after month */
- p++;
- p = strchr(p, '.');
- if (p == NULL) continue;
+ if (debug == 1) printf(" copy %s ---> %s\n", e->name, str);
+ status = do_copy(e->name, str, archive_mode);
+ free(str);
+ }
+
+ if (debug == 1) printf(" unlink %s\n", e->name);
+ unlink(e->name);
+
+ store_size -= e->size;
+ e->size = 0;
- /* find '.' after day */
- p++;
- p = strchr(p, '.');
- if (p == NULL) continue;
+ e = e->next;
+ }
- str = NULL;
- asprintf(&str, "LongTTL%s", p);
- if (str == NULL) mgr_exit(store_dir, 1);
+ if (debug == 1) printf("Finished YMD Scan\n");
- /* syslog -x [str] -db [e->name] -k ASLExpireTime */
- if (debug == 1) printf(" scan %s ---> %s\n", e->name, str);
- else status = do_match(e->name, str, 1, 0);
+ /* Delete/achive expired BB files */
+ if (debug == 1) printf("Start BB Scan\n");
- free(str);
- str = NULL;
+ e = bb_list;
+ while (e != NULL)
+ {
+ /* stop when a file name/date is after the expire date */
+ if (strncmp(e->name + 3, today_ymd_string, today_ymd_stringlen) > 0) break;
if (archive != NULL)
{
str = NULL;
asprintf(&str, "%s/%s", archive, e->name);
- if (str == NULL) mgr_exit(store_dir, 1);
+ if (str == NULL) return -1;
- /* syslog -x [str] -db [e->name] */
- if (debug == 1) printf(" copy %s ---> %s\n", e->name, str);
- else status = do_match(e->name, str, 0, 0);
+ /* syslog -x [str] -f [e->name] */
+ if (debug == 1) printf(" copy %s ---> %s\n", e->name, str);
+ status = do_copy(e->name, str, archive_mode);
free(str);
}
- if (debug == 1) printf(" unlink %s\n", e->name);
- else unlink(e->name);
+ if (debug == 1) printf(" unlink %s\n", e->name);
+ unlink(e->name);
store_size -= e->size;
e->size = 0;
e = e->next;
}
- if (debug == 1)
- {
- printf("Finished Scan\n");
- printf("\nData Store Size = %lu\n", store_size);
- }
-
- free_list(list);
- list = NULL;
+ if (debug == 1) printf("Finished BB Scan\n");
- dp = opendir(PATH_ASL_STORE);
- if (dp == NULL) mgr_exit(store_dir, 1);
+ /* if data store is over max_size, delete/archive more YMD files */
+ if ((debug == 1) && (store_size > max_size)) printf("Additional YMD Scan\n");
+
+ e = ymd_list;
+ while ((e != NULL) && (store_size > max_size))
+ {
+ if (e->size != 0)
+ {
+ /* stop when we get to today's files */
+ if (strncmp(e->name, today_ymd_string, today_ymd_stringlen) == 0) break;
- /* gather a list of LongTTL files */
+ if (archive != NULL)
+ {
+ str = NULL;
+ asprintf(&str, "%s/%s", archive, e->name);
+ if (str == NULL) return -1;
+
+ /* syslog -x [str] -f [e->name] */
+ if (debug == 1) printf(" copy %s ---> %s\n", e->name, str);
+ status = do_copy(e->name, str, archive_mode);
+ free(str);
+ }
- while ((dent = readdir(dp)) != NULL)
- {
- if (!strcmp(dent->d_name, LONGTTL_TMP_FILE)) unlink(LONGTTL_TMP_FILE);
- else if (!strncmp(dent->d_name, "LongTTL.", 8)) list = add_to_list(list, dent->d_name, 0);
- }
+ if (debug == 1) printf(" unlink %s\n", e->name);
+ unlink(e->name);
- closedir(dp);
+ store_size -= e->size;
+ e->size = 0;
+ }
- if (debug == 1)
- {
- printf("\nData Store LongTTL Files\n");
- for (e = list; e != NULL; e = e->next) printf(" %s\n", e->name);
+ e = e->next;
}
- if (debug == 1) printf("\nScan for expired messages\n");
+ /* if data store is over max_size, delete/archive more BB files */
+ if ((debug == 1) && (store_size > max_size)) printf("Additional BB Scan\n");
- e = list;
- while (e != NULL)
+ e = bb_list;
+ while ((e != NULL) && (store_size > max_size))
{
- /* syslog -x LongTTL.new -db [e->name] -k ASLExpireTime Nge [now] */
- if (debug == 1)
- {
- printf(" %s\n", e->name);
- }
- else
+ if (e->size != 0)
{
- status = do_match(e->name, LONGTTL_TMP_FILE, 1, now);
+ if (archive != NULL)
+ {
+ str = NULL;
+ asprintf(&str, "%s/%s", archive, e->name);
+ if (str == NULL) return -1;
+
+ /* syslog -x [str] -f [e->name] */
+ if (debug == 1) printf(" copy %s ---> %s\n", e->name, str);
+ status = do_copy(e->name, str, archive_mode);
+ free(str);
+ }
+
+ if (debug == 1) printf(" unlink %s\n", e->name);
unlink(e->name);
- if (status == ASL_STATUS_OK) rename(LONGTTL_TMP_FILE, e->name);
+
+ store_size -= e->size;
+ e->size = 0;
}
e = e->next;
}
- if (debug == 1) printf("Finished scan for expired messages\n");
+ free_list(ymd_list);
+ free_list(bb_list);
- free_list(list);
- list = NULL;
+ if (debug == 1)
+ {
+ printf("Data Store Size = %lu\n", store_size);
+ printf("aslmanager finished\n");
+ }
- mgr_exit(store_dir, 0);
- /* UNREACHED */
return 0;
}
<key>ProgramArguments</key>
<array>
<string>/usr/sbin/aslmanager</string>
- <string>-size</string>
- <string>65536000</string>
</array>
<key>WatchPaths</key>
<array>
ProductType = tool
Install_Dir = /usr/sbin
-CFILES = asl_action.c asl_in.c bsd_in.c bsd_out.c daemon.c dbserver.c klog_in.c remote.c syslogd.c udp_in.c
+CFILES = asl_action.c asl_in.c bb_convert.c bsd_in.c bsd_out.c daemon.c dbserver.c klog_in.c remote.c syslogd.c udp_in.c
MANPAGES = asl.conf.5 syslogd.8 syslog.conf.5
#syslogd.sb
Extra_CC_Flags += -DCONFIG_IPHONE -DLOCKDOWN
endif
-Extra_LD_Flags = -dead_strip -L"$(SYMROOT)" -laslcommon
+Extra_LD_Flags = -dead_strip -L"$(SYMROOT)" -laslcommon -lbsm
include $(MAKEFILEPATH)/CoreOS/ReleaseControl/BSDCommon.make
$(INSTALL_DIRECTORY) "$(DSTROOT)"/private/var/log/asl
$(INSTALL_DIRECTORY) "$(DSTROOT)"/usr/share/sandbox
$(INSTALL_FILE) syslogd.sb "$(DSTROOT)"/usr/share/sandbox
+ codesign -s- $(DSTROOT)/usr/sbin/syslogd
+ mkfile 8 "$(DSTROOT)"/private/var/log/asl/SweepStore
+ chmod 644 "$(DSTROOT)"/private/var/log/asl/SweepStore
-.\"Copyright (c) 2004-2008 Apple Inc. All rights reserved.
+.\"Copyright (c) 2004-2009 Apple Inc. All rights reserved.
.\"
.\"@APPLE_LICENSE_HEADER_START@
.\"
.\"
.\"@APPLE_LICENSE_HEADER_END@
.\"
-.Dd December 22, 2005
+.Dd Sept 19, 2008
.Dt asl.conf 5
.Os "Mac OS X"
.Sh NAME
.Nm asl.conf
.Nd configuration file for
.Xr syslogd 8
-asl_action module.
+and
+.Xr aslmanager 8
.Sh DESCRIPTION
The
.Xr syslogd 8
-server in Mac OS X includes a module that compares messages with a set of query patterns,
-and which performs various actions when messages match the query patterns.
+server reads the
+.Nm
+file at startup, and re-reads the file whenever it received a HUP signal.
+The
+.Xr aslmanager 8
+daemon reads the file when it starts.
+See the ASLMANAGER PARAMETER SETTINGS section below for details on those parameter settings.
+.Pp
+The file may contain parameter settings, used in place of (and which will override) command-line options,
+and may contain query-action rules that trigger specific actions when
+.Nm syslogd
+recieves messages that match the query pattern.
+.Pp
+Parameter setting lines in the configuration file begin with an equal sign ("="),
+and are generally of the form:
+.Pp
+.Dl = parameter_name value ...
+.Pp
+Most parameter settings require a single value, although some may take several values.
+See the PARAMETER SETTINGS section below for details.
+.Pp
+Query-action rules in the file begin with a question-mark ("?") or a "Q", and generally have the form:
+.Pp
+.Dl ? query action ...
+.Pp
+Specific actions may be followed by optional arguments.
+See the QUERY-ACTION RULES section below for details.
+.Sh PARAMETER SETTINGS
+The following parameter-settings are recognized by
+.Nm syslogd .
+.Pp
+.Bl -tag -width "bsd_max_dup_time" -compact -offset indent
+.It debug
+Enables or disables internal debugging output.
+This is probably of little interest to most users.
+The debug parameter requires a value of "1" to enable debug output, or a value of "0" to disable it.
+An option file name may follow the "0" or "1".
+If a file name is provided, debug messages are written to that file.
+Otherwise, debug writes are treated as log messages.
.Pp
-Each line in the file contains three components.
-The first is a query, the second is an action, and the third contains parameters specific to that action.
+.It cutoff
+Sets the ASL data store cutoff level, given as an integer in the range 0 to 7 as an argument.
+The cutoff level is 7 by default, allowing any message that matches a "store" action
+(see QUERY-ACTION RULES below) to be saved.
+Setting the cutoff to a lower value will prevent messages with log priority levels numerically
+greater that the specified cutoff from being saved in the ASL data store.
+.Pp
+.It mark_time
+Sets the time interval for the mark facility.
+The default is 0 seconds, which indicates that mark messages are not generated.
+.Pp
+.It dup_delay
+Sets the maximum time that the bsd_out module will allow before writing a "last message repeated <N> times"
+message in a log file specified in /etc/syslog.conf.
+The default is 30 seconds.
+.Pp
+.It utmp_ttl
+Sets the time-to-live for messages used by the utmp, wtmp, and lastlog subsystems.
+The default is 31622400 seconds (approximately 1 year).
+.Pp
+.It fs_ttl
+Sets the time-to-live for filesystem error messages generated by the kernel.
+The default is 31622400 seconds (approximately 1 year).
+.Pp
+.It mps_limit
+Sets the per-process message per second quota.
+The default is value is 500.
+A value of 0 disables the quota mechanism.
+.Pp
+.It max_file_size
+Sets the maximum file size for individual files in the ASL data store.
+The default is 25600000 bytes.
+.El
+.Pp
+.Sh QUERY-ACTION RULES
+Rules contain three components: a query; an action; and optionally, parameters specific to that action.
For example:
.Pp
-.Dl Q [= Sender foobar] [N< Level 3] notify com.apple.foobar
+.Dl ? [= Sender foobar] [<= Level error] notify com.apple.foobar
.Pp
-.Ss Queries
-Queries start with the letter "Q" followed by whitespace.
-Following that are any number of message matching components, each of which has the form:
+.Ss Query Format
+Queries comprise one or more message matching components, each of which has the form:
.Pp
.Dl [OP KEY VAL]
.Pp
KEY and VAL are message keys and values.
For example
.Pp
-.Dl Q [= Sender foobar]
+.Dl [= Sender foobar]
.Pp
matches any message with key="Sender" and val="foobar".
The query
.Pp
-.Dl Q [CA= Color gr]
+.Dl [CA= Color gr]
.Pp
matches any message with key=Color and val beginning with the letters GR, Gr, gr, or gR
(C meaning casefold, A meaning prefix).
The example query above,
.Pp
-.Dl Q [= Sender foobar] [N< Level 3]
+.Dl [= Sender foobar] [N< Level 3]
.Pp
matches any message from "foobar" with a level numerically less than 3
(string values are converted to integers, and the comparison is done on the integer values).
+Note that the string values may be used equivalently for the Level key,
+so the example above may also be written as:
+.Pp
+.Dl [= Sender foobar] [< Level Error]
+.Pp
+String values for levels may be any of the set "emergency", "alert", "critical", "error",
+"warning", "notice", "info", or "debug". These strings may be upper, lower, or mixed case.
.Pp
The "T" operator is useful to test for the presence of a particular key.
.Pp
-.Dl Q [T Flavor whatever]
+.Dl [T Flavor]
.Pp
Will match any message that has a "Flavor" key, regardless of its value.
.Pp
.Ss Actions
-The "notify" action causes
+The following actions are available.
+.Pp
+.Bl -tag -width "store_directory" -compact -offset indent
+.It notify
+Causes
.Nm syslogd
to post a notification with
.Fn notify_post .
The notification key must appear as a single parameter following the "notify" action.
.Pp
-The "access" action sets read access controls for messages that match the associated query pattern.
+.It access
+Sets read access controls for messages that match the associated query pattern.
.Nm syslogd
will restrict read access to matching messages to a specific user and group.
The user ID number and group ID number must follow the "access" keyword as parameters.
.Pp
-The "store" action saves matching messages in a separate log message database.
-The database may be accessed using the
+.It store
+Causes
+.Nm syslogd
+to save matching messages, either in the main ASL data store,
+or in a separate log message data store file is a file name is given as a parameter.
+A separate data store file may be accessed using the
.Nm syslog
command line utility.
-A database pathname must follow the "store" keyword.
-A new database will be created if one does not exist.
-Two optional parameters, "stayopen" and "exclude_asldb" may follow the database pathname.
+A new file will be created if one does not exist.
+If a new file is being created, the UID, GID, and mode of the file may be specified using the options
+"uid=UUU", "gid=GGG", and "mode=MMMM", where UUU and GGG are a user ID and group ID, and MMMM is a
+mode specification of the form "0644" (for an octal number) or DDD for a decimal number.
+.Pp
+Two other optional parameters may also follow the pathname.
.Pp
By default,
.Nm syslogd
will open the database, save a matching message, and then close the database.
-If a high volume of messages is expected, specifying "stayopen" will improve performance.
+If a high volume of messages is expected, specifying the "stayopen" option will improve performance.
+.Pp
+Specifying "exclude_asldb" will cause syslogd to save matching messages in the specificed file,
+but exclude them from the main ASL data store.
.Pp
-Specifying "exclude_asldb" will cause syslogd to save matching messages in the database,
-but exclude them from the main
+Note that if the configuration file contains no matching rules for the ASL data store, then
.Nm syslogd
-database (/var/log/asl.db).
+will save all messages, subject to filtering in accordance with the log cutoff level.
+.Pp
+.It store_directory
+Causes matching messages to be stored in a log message data store file in a separate directory.
+The directory path name must follow as the first parameter.
+The named directory must exist.
+.Nm syslogd
+will not ceate the directory path.
+.Pp
+Messages saved to a store directory are saved in files that are named "yyyy.mm.dd.asl",
+where "yyyy", "mm", and "dd" are the year, month (01 to 12) and day of the month (01 to 31) associated with
+matching messages.
+This has the effect of saving messages in a separate file for each day.
+.Pp
+The "exclude_asldb", "uid=UUU", "gid=GGG", and "mode=MMMM" options available for the "store" action
+may also be specified for a store directory.
+The uid, gid, and mode specification will be used when the individual daily store files are created.
+.Pp
+.It broadcast
+Causes syslogd to write the text of matching messages to all terminal windows.
+If optional text follows the "broadcast" keyword, then that text is written rather that the matching message text.
+.Pp
+.It ignore
+Causes a matching message to be ignored in all subsequent matching rules.
+.El
+.Sh ASLMANAGER PARAMETER SETTINGS
+The following parameter-settings are recognized by
+.Nm aslmanager .
+.Pp
+.Bl -tag -width "aslmanager_debug" -compact -offset indent
+.It aslmanager_debug
+Enables or disables internal debugging output.
+This is probably of little interest to most users.
+The debug parameter requires a value of "1" to enable debug output, or a value of "0" to disable it.
+Debug messages are sent to
+.Nm syslogd .
+.Pp
+.It store_ttl
+Sets the time-to-live in days for messages in the syslog data store.
+The default is 7 days.
+.Pp
+.It max_store_size
+Sets the maximum size for for the ASL data store.
+The default is 150000000 bytes.
+.Pp
+.It archive
+Enables or disables archiving.
+The archive parameter requires a value of "1" to enable archiving, or a value of "0" to disable it.
+An option archive directory path may follow the "0" or "1".
+If enabled, files removed from the ASL data store are moved to the archive directory.
+The default archive directory path is /var/log/asl.archive.
+.Pp
+.It store_path
+The data store path used by
+.Nm aslmanager .
+The default is /var/log/asl.
+Note that this parameter is ignored by
+.Nm syslogd .
+.It archive_mode
+Files copied to the archive will be given the specified access mode.
+The default is 0400, so archive files will only be readable by root.
+.El
+.Pp
.Sh SEE ALSO
.Xr asl 3 ,
.Xr notify 3 ,
.Xr syslog 1 ,
+.Xr aslmanager 8 ,
.Xr syslogd 8 .
/*
- * Copyright (c) 2004-2008 Apple Inc. All rights reserved.
+ * Copyright (c) 2004-2009 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
#include <errno.h>
#include <netdb.h>
#include <notify.h>
+#include <pthread.h>
+#include <asl_core.h>
#include "daemon.h"
+#define _PATH_WALL "/usr/bin/wall"
#define _PATH_ASL_CONF "/etc/asl.conf"
#define MY_ID "asl_action"
-#define ASL_KEY_FACILITY "Facility"
+#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 IndexNull ((uint32_t)-1)
#define forever for(;;)
+#define ACT_STORE_FLAG_STAY_OPEN 0x00000001
+#define ACT_STORE_FLAG_EXCLUDE_ASLDB 0x00000002
+
static asl_msg_t *query = NULL;
-static int reset = 0;
+static int reset = RESET_NONE;
+static pthread_mutex_t reset_lock = PTHREAD_MUTEX_INITIALIZER;
-struct action_rule
+typedef struct action_rule_s
{
asl_msg_t *query;
- char *action;
+ int action;
char *options;
void *data;
- TAILQ_ENTRY(action_rule) entries;
+ struct action_rule_s *next;
+} action_rule_t;
+
+struct store_data
+{
+ asl_file_t *store;
+ FILE *storedata;
+ char *dir;
+ char *path;
+ mode_t mode;
+ uid_t uid;
+ gid_t gid;
+ uint64_t next_id;
+ uint32_t flags;
+ uint32_t p_year;
+ uint32_t p_month;
+ uint32_t p_day;
};
-static TAILQ_HEAD(cr, action_rule) asl_action_rule;
+static action_rule_t *asl_action_rule = NULL;
+static action_rule_t *asl_datastore_rule = NULL;
+static int filter_token = -1;
int asl_action_close();
-static int _parse_notify_file(const char *);
+static int _parse_config_file(const char *);
+extern void db_save_message(asl_msg_t *m);
+
+static char *
+_next_word(char **s)
+{
+ char *a, *p, *e, *out;
+ int quote, len;
+
+ if (s == NULL) return NULL;
+ if (*s == NULL) return NULL;
+
+ quote = 0;
+
+ p = *s;
+ a = p;
+ e = p;
+
+ while (*p != '\0')
+ {
+ if (*p == '\\')
+ {
+ p++;
+ e = p;
+
+ if (*p == '\0')
+ {
+ p--;
+ break;
+ }
+
+ p++;
+ e = p;
+ continue;
+ }
+
+ if (*p == '"')
+ {
+ if (quote == 0) quote = 1;
+ else quote = 0;
+ }
+
+ if (((*p == ' ') || (*p == '\t')) && (quote == 0))
+ {
+ e = p + 1;
+ break;
+ }
+
+ p++;
+ e = p;
+ }
+
+ *s = e;
+
+ len = p - a;
+ if (len == 0) return NULL;
+
+ out = malloc(len + 1);
+ if (out == NULL) return NULL;
+
+ memcpy(out, a, len);
+ out[len] = '\0';
+ return out;
+}
static void
_do_reset(void)
{
+ pthread_mutex_lock(&reset_lock);
+
asl_action_close();
- _parse_notify_file(_PATH_ASL_CONF);
+ _parse_config_file(_PATH_ASL_CONF);
+ reset = RESET_NONE;
+
+ pthread_mutex_unlock(&reset_lock);
}
/*
* Config File format:
- * Q [k v] [k v] ... action args...
+ * 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...
*/
/* Skip over query */
p = s;
if (p == NULL) return NULL;
- if (*p != 'Q') return NULL;
+ if ((*p != 'Q') && (*p != '?') && (*p != '*')) return NULL;
p++;
}
static int
-_parse_line(char *s)
+_parse_query_action(char *s)
{
char *act, *p;
- struct action_rule *out;
-
- if (s == NULL) return -1;
- while ((*s == ' ') || (*s == '\t')) s++;
- if (*s == '#') return -1;
+ action_rule_t *out, *rule;
act = _find_action(s);
-
if (act == NULL) return -1;
- out = (struct action_rule *)calloc(1, sizeof(struct action_rule));
+
+ out = (action_rule_t *)calloc(1, sizeof(action_rule_t));
if (out == NULL) return -1;
p = strchr(act, ' ');
if (p != NULL) *p = '\0';
- out->action = strdup(act);
- if (out->action == NULL)
- {
- free(out);
- return -1;
- }
+ 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;
if (p != NULL)
{
if (out->options == NULL)
{
- free(out->action);
free(out);
return -1;
}
p = act - 1;
*p = '\0';
- out->query = asl_msg_from_string(s);
+
+ if (s[0] == '*') out->query = asl_new(ASL_TYPE_QUERY);
+ else
+ {
+ s[0] = 'Q';
+ out->query = asl_msg_from_string(s);
+ }
if (out->query == NULL)
{
- free(out->action);
+ asldebug("out->query is NULL (ERROR)\n");
if (out->options != NULL) free(out->options);
free(out);
return -1;
}
- TAILQ_INSERT_TAIL(&asl_action_rule, out, entries);
+ if ((out->action == ACTION_STORE) && (out->options == 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;
+ }
+ }
+ 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;
+ }
+ }
return 0;
}
-#ifdef NOTDEF
-static char *
-_next_word(char **s)
+/*
+ * Used to sed config parameters.
+ * Line format "= name value"
+ */
+static int
+_parse_set_param(char *s)
{
- char *a, *p, *e, *out;
- int quote, len;
+ char **l;
+ uint32_t intval, count, v32a, v32b, v32c;
- if (s == NULL) return NULL;
- if (*s == NULL) return NULL;
+ if (s == NULL) return -1;
+ if (s[0] == '\0') return 0;
- quote = 0;
+ /* skip '=' and whitespace */
+ s++;
+ while ((*s == ' ') || (*s == '\t')) s++;
- p = *s;
- a = p;
- e = p;
+ l = explode(s, " \t");
+ if (l == NULL) return -1;
- while (*p != '\0')
+ for (count = 0; l[count] != NULL; count++);
+
+ /* name is required */
+ if (count == 0)
{
- if (*p == '\\')
- {
- p++;
- e = p;
+ freeList(l);
+ return -1;
+ }
- if (*p == '\0')
- {
- p--;
- break;
- }
+ /* value is required */
+ if (count == 1)
+ {
+ freeList(l);
+ return -1;
+ }
- p++;
- e = p;
- continue;
- }
+ if (!strcasecmp(l[0], "debug"))
+ {
+ /* = debug {0|1} [file] */
+ intval = atoi(l[1]);
+ config_debug(intval, l[2]);
+ }
+ else if (!strcasecmp(l[0], "cutoff"))
+ {
+ /* = cutoff level */
+ intval = atoi(l[1]);
+ if (intval > ASL_LEVEL_DEBUG) intval = ASL_LEVEL_DEBUG;
+ global.asl_log_filter = ASL_FILTER_MASK_UPTO(intval);
+ }
+ else if (!strcasecmp(l[0], "mark_time"))
+ {
+ /* = mark_time seconds */
+ OSSpinLockLock(&global.lock);
+ global.mark_time = atoll(l[1]);
+ OSSpinLockUnlock(&global.lock);
+ }
+ else if (!strcasecmp(l[0], "dup_delay"))
+ {
+ /* = bsd_max_dup_time seconds */
+ OSSpinLockLock(&global.lock);
+ global.bsd_max_dup_time = atoll(l[1]);
+ OSSpinLockUnlock(&global.lock);
+ }
+ else if (!strcasecmp(l[0], "asl_store_ping_time"))
+ {
+ /* NB this is private / unpublished */
+ /* = asl_store_ping_time seconds */
+ OSSpinLockLock(&global.lock);
+ global.asl_store_ping_time = atoll(l[1]);
+ OSSpinLockUnlock(&global.lock);
+ }
+ else if (!strcasecmp(l[0], "utmp_ttl"))
+ {
+ /* = utmp_ttl seconds */
+ OSSpinLockLock(&global.lock);
+ global.utmp_ttl = (time_t)atoll(l[1]);
+ OSSpinLockUnlock(&global.lock);
+ }
+ else if (!strcasecmp(l[0], "fs_ttl"))
+ {
+ /* = fs_ttl seconds */
+ OSSpinLockLock(&global.lock);
+ global.fs_ttl = (time_t)atoll(l[1]);
+ OSSpinLockUnlock(&global.lock);
+ }
+ else if (!strcasecmp(l[0], "mps_limit"))
+ {
+ /* = mps_limit number */
+ OSSpinLockLock(&global.lock);
+ global.mps_limit = (uint32_t)atol(l[1]);
+ OSSpinLockUnlock(&global.lock);
+ }
+ else if (!strcasecmp(l[0], "max_file_size"))
+ {
+ /* = max_file_size bytes */
+ pthread_mutex_lock(global.db_lock);
- if (*p == '"')
+ if (global.dbtype & DB_TYPE_FILE)
{
- if (quote == 0) quote = 1;
- else quote = 0;
+ asl_store_close(global.file_db);
+ global.file_db = NULL;
+ global.db_file_max = atoi(l[1]);
}
- if (((*p == ' ') || (*p == '\t')) && (quote == 0))
+ pthread_mutex_unlock(global.db_lock);
+ }
+ else if ((!strcasecmp(l[0], "db")) || (!strcasecmp(l[0], "database")) || (!strcasecmp(l[0], "datastore")))
+ {
+ /* NB this is private / unpublished */
+ /* = db type [max]... */
+
+ v32a = 0;
+ v32b = 0;
+ v32c = 0;
+
+ if ((l[1][0] >= '0') && (l[1][0] <= '9'))
{
- e = p + 1;
- break;
+ 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
+ {
+ freeList(l);
+ return -1;
}
- p++;
- e = p;
+ if (v32a == 0) v32a = global.db_file_max;
+ if (v32b == 0) v32b = global.db_memory_max;
+ if (v32c == 0) v32c = global.db_mini_max;
+
+ config_data_store(intval, v32a, v32b, v32c);
}
- *s = e;
+ freeList(l);
+ return 0;
+}
- len = p - a;
- if (len == 0) return NULL;
+static int
+_parse_line(char *s)
+{
+ char *str;
+ int status;
- out = malloc(len + 1);
- if (out == NULL) return NULL;
+ if (s == NULL) return -1;
+ while ((*s == ' ') || (*s == '\t')) s++;
- memcpy(out, a, len);
- out[len] = '\0';
- return out;
+ /* First non-whitespace char is the rule type */
+ switch (*s)
+ {
+ 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;
+ }
+ }
+
+ if (status != 0)
+ {
+ 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);
+
+ asl_log_string(str);
+ if (str != NULL) free(str);
+ }
+
+ return status;
}
-#endif
static void
-_act_notify(struct action_rule *r)
+_act_notify(action_rule_t *r)
{
if (r == NULL) return;
if (r->options == NULL) return;
+
notify_post(r->options);
}
static void
-_act_access_control(struct action_rule *r, asl_msg_t *msg)
+_act_broadcast(action_rule_t *r, asl_msg_t *msg)
+{
+ FILE *pw;
+ const char *val;
+
+ if (r == NULL) return;
+ if (msg == NULL) return;
+
+ val = r->options;
+ if (val == NULL) val = asl_get(msg, ASL_KEY_MSG);
+ if (val == NULL) return;
+
+ pw = popen(_PATH_WALL, "w");
+ if (pw < 0)
+ {
+ asldebug("%s: error sending wall message: %s\n", MY_ID, strerror(errno));
+ return;
+ }
+
+ fprintf(pw, "%s", val);
+ pclose(pw);
+}
+
+static void
+_act_access_control(action_rule_t *r, asl_msg_t *msg)
{
int32_t ruid, rgid;
char *p;
}
}
+static uint32_t
+_act_store_file_setup(struct store_data *sd)
+{
+ uint32_t status;
-int
-asl_action_sendmsg(asl_msg_t *msg, const char *outid)
+ 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;
+
+ status = asl_file_read_set_position(sd->store, ASL_FILE_POSITION_LAST);
+ if (status != ASL_STATUS_OK) return status;
+
+ sd->next_id = sd->store->cursor_xid + 1;
+ if (fseek(sd->store->store, 0, SEEK_END) != 0) return ASL_STATUS_ACCESS_DENIED;
+
+ return ASL_STATUS_OK;
+}
+
+static uint32_t
+_act_store_dir_setup(struct store_data *sd, time_t tick)
{
- struct action_rule *r;
+ struct tm ctm;
+ char *path;
+ struct stat sb;
+ uint64_t xid;
+ int status;
+
+ if (sd == NULL) return ASL_STATUS_INVALID_STORE;
+ if (sd->dir == NULL) return ASL_STATUS_INVALID_STORE;
+
+ /* get / set message id from StoreData file */
+ xid = 0;
+
+ if (sd->storedata == NULL)
+ {
+ 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)
+ {
+ /* StoreData exists: open and read last xid */
+ sd->storedata = fopen(path, "r+");
+ if (sd->storedata == NULL)
+ {
+ free(path);
+ return ASL_STATUS_FAILED;
+ }
+
+ if (fread(&xid, sizeof(uint64_t), 1, sd->storedata) != 1)
+ {
+ free(path);
+ fclose(sd->storedata);
+ sd->storedata = NULL;
+ return ASL_STATUS_READ_FAILED;
+ }
+ }
+ else if (errno != ENOENT)
+ {
+ /* Unexpected stat error */
+ free(path);
+ return ASL_STATUS_FAILED;
+ }
+ else
+ {
+ /* StoreData does not exist: create it */
+ sd->storedata = fopen(path, "w");
+ if (sd->storedata == NULL)
+ {
+ free(path);
+ return ASL_STATUS_FAILED;
+ }
+ }
- if (reset != 0)
+ free(path);
+ }
+ else
{
- _do_reset();
- reset = 0;
+ rewind(sd->storedata);
+ if (fread(&xid, sizeof(uint64_t), 1, sd->storedata) != 1)
+ {
+ fclose(sd->storedata);
+ sd->storedata = NULL;
+ return ASL_STATUS_READ_FAILED;
+ }
}
+ xid = asl_core_ntohq(xid);
+ xid++;
+ sd->next_id = xid;
+
+ xid = asl_core_htonq(xid);
+ rewind(sd->storedata);
+ status = fwrite(&xid, sizeof(uint64_t), 1, sd->storedata);
+ if (status != 1)
+ {
+ fclose(sd->storedata);
+ sd->storedata = NULL;
+ return ASL_STATUS_WRITE_FAILED;
+ }
+
+ if ((sd->flags & ACT_STORE_FLAG_STAY_OPEN) == 0)
+ {
+ fclose(sd->storedata);
+ sd->storedata = NULL;
+ }
+
+ memset(&ctm, 0, sizeof(struct tm));
+
+ 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;
+
+ if (sd->store != NULL) asl_file_close(sd->store);
+
+ sd->p_year = 0;
+ sd->p_month = 0;
+ sd->p_day = 0;
+
+ if (sd->path != NULL) free(sd->path);
+ sd->path = NULL;
+
+ 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;
+
+ sd->p_year = ctm.tm_year;
+ sd->p_month = ctm.tm_mon;
+ sd->p_day = ctm.tm_mday;
+
+ return ASL_STATUS_OK;
+}
+
+static void
+_act_store(action_rule_t *r, asl_msg_t *msg)
+{
+ struct store_data *sd;
+ asl_file_t *s;
+ uint8_t x;
+ uint32_t status;
+ uint64_t mid;
+ mode_t tmp_mode;
+ char *str, *opts, *p;
+ const char *val;
+ time_t tick;
+
+ s = NULL;
+
+ /* _act_store is not used for the main ASL data store */
+ if (r->options == NULL) return;
+
+ if (r->data == NULL)
+ {
+ /* set up store data */
+ sd = (struct store_data *)calloc(1, sizeof(struct store_data));
+ if (sd == NULL) return;
+
+ opts = r->options;
+ sd->store = NULL;
+
+ if (r->action == ACTION_STORE)
+ {
+ sd->path = _next_word(&opts);
+ if (sd->path == NULL)
+ {
+ free(sd);
+ r->action = ACTION_NONE;
+ return;
+ }
+ }
+ else if (r->action == ACTION_STORE_DIR)
+ {
+ sd->dir = _next_word(&opts);
+ if (sd->dir == NULL)
+ {
+ free(sd);
+ r->action = ACTION_NONE;
+ return;
+ }
+ }
+
+ sd->mode = 0644;
+ sd->next_id = 0;
+ sd->uid = 0;
+ sd->gid = 0;
+ sd->flags = 0;
+
+ while (NULL != (p = _next_word(&opts)))
+ {
+ if (!strcmp(p, "stayopen")) sd->flags |= ACT_STORE_FLAG_STAY_OPEN;
+ else if (!strcmp(p, "exclude_asldb")) sd->flags |= ACT_STORE_FLAG_EXCLUDE_ASLDB;
+ else if (!strncmp(p, "mode=0", 6))
+ {
+ sd->mode = 0;
+ x = *(p + 6);
+ if ((x < '0') || (x > '7'))
+ {
+ free(p);
+ if (sd->path != NULL) free(sd->path);
+ if (sd->dir != NULL) free(sd->dir);
+ free(sd);
+ r->action = ACTION_NONE;
+ return;
+ }
+
+ tmp_mode = x - '0';
+ sd->mode += tmp_mode << 6;
+
+ x = *(p + 7);
+ if ((x < '0') || (x > '7'))
+ {
+ free(p);
+ if (sd->path != NULL) free(sd->path);
+ if (sd->dir != NULL) free(sd->dir);
+ free(sd);
+ r->action = ACTION_NONE;
+ return;
+ }
+
+ tmp_mode = x - '0';
+ sd->mode += tmp_mode << 3;
+
+ x = *(p + 8);
+ if ((x < '0') || (x > '7'))
+ {
+ free(p);
+ if (sd->path != NULL) free(sd->path);
+ if (sd->dir != NULL) free(sd->dir);
+ free(sd);
+ r->action = ACTION_NONE;
+ return;
+ }
+
+ tmp_mode = x - '0';
+ sd->mode += tmp_mode;
+ }
+ 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;
+ }
+
+ r->data = sd;
+ }
+ else
+ {
+ sd = (struct store_data *)r->data;
+ }
+
+ if (r->action == ACTION_STORE_DIR)
+ {
+ val = asl_get(msg, ASL_KEY_TIME);
+ if (val == NULL) return;
+
+ tick = atol(val);
+ status = _act_store_dir_setup(sd, tick);
+ if (status != ASL_STATUS_OK)
+ {
+ asldebug("_act_store_dir_setup %s failed: %s\n", sd->path, asl_core_error(status));
+
+ /* disable further activity */
+ asl_file_close(sd->store);
+ sd->store = NULL;
+ r->action = ACTION_NONE;
+ return;
+ }
+ }
+
+ if (sd->store == NULL)
+ {
+ s = NULL;
+ status = asl_file_open_write(sd->path, sd->mode, sd->uid, sd->gid, &s);
+ if ((status != ASL_STATUS_OK) || (s == NULL))
+ {
+ asldebug("asl_file_open_write %s failed: %s\n", sd->path, asl_core_error(status));
+
+ /* disable further activity */
+ asl_file_close(sd->store);
+ sd->store = NULL;
+ r->action = ACTION_NONE;
+ return;
+ }
+
+ sd->store = s;
+ }
+
+ if (r->action != ACTION_STORE_DIR)
+ {
+ status = _act_store_file_setup(sd);
+ if (status != ASL_STATUS_OK)
+ {
+ asldebug("_act_store_file_setup %s failed: %s\n", sd->path, asl_core_error(status));
+
+ /* disable further activity */
+ asl_file_close(sd->store);
+ sd->store = NULL;
+ r->action = ACTION_NONE;
+ return;
+ }
+ }
+
+ mid = sd->next_id;
+
+ 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));
+
+ /* disable further activity on this file */
+ asl_file_close(sd->store);
+ sd->store = NULL;
+ r->action = ACTION_NONE;
+ return;
+ }
+
+ if ((sd->flags & ACT_STORE_FLAG_STAY_OPEN) == 0)
+ {
+ asl_file_close(sd->store);
+ sd->store = NULL;
+ }
+
+ if (sd->flags & ACT_STORE_FLAG_EXCLUDE_ASLDB)
+ {
+ opts = (char *)asl_get(msg, ASL_KEY_OPTION);
+ if (opts == NULL)
+ {
+ asl_set(msg, ASL_KEY_OPTION, ASL_OPT_IGNORE);
+ }
+ else
+ {
+ str = NULL;
+ asprintf(&str, "%s %s", ASL_OPT_IGNORE, opts);
+ if (str != NULL)
+ {
+ asl_set(msg, ASL_KEY_OPTION, str);
+ free(str);
+ }
+ }
+ }
+}
+
+static void
+_act_forward(action_rule_t *r, asl_msg_t *msg)
+{
+ /* To do: <rdar://problem/6130747> Add a "forward" action to asl.conf */
+}
+
+static void
+send_to_asl_store(asl_msg_t *msg)
+{
+ const char *vlevel, *val;
+ uint64_t v64;
+ uint32_t status, level, lmask;
+ int x, log_me;
+ action_rule_t *r;
+
+ /* ASLOption "ignore" keeps a message out of the ASL datastore */
+ if (asl_check_option(msg, ASL_OPT_IGNORE) != 0) return;
+
+ if (filter_token == -1)
+ {
+ /* 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
+ {
+ status = notify_check(filter_token, &x);
+ if (status == NOTIFY_STATUS_OK)
+ {
+ v64 = global.asl_log_filter;
+ status = notify_set_state(filter_token, v64);
+ }
+ if (status != NOTIFY_STATUS_OK)
+ {
+ notify_cancel(filter_token);
+ filter_token = -1;
+ }
+ }
+ }
+
+ /* 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;
+ }
+ }
+
+ /* 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
+ {
+ 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;
+ }
+
+ if (log_me == 0) return;
+
+ /* if there are no rules, save the message */
+ if (asl_datastore_rule == NULL)
+ {
+ db_save_message(msg);
+ return;
+ }
+
+ for (r = asl_datastore_rule; r != NULL; r = r->next)
+ {
+ if (asl_msg_cmp(r->query, msg) == 1)
+ {
+ /* if any rule matches, save the message (once!) */
+ db_save_message(msg);
+ return;
+ }
+ }
+}
+
+int
+asl_action_sendmsg(asl_msg_t *msg, const char *outid)
+{
+ action_rule_t *r;
+
+ if (reset == RESET_CONFIG) _do_reset();
+
if (msg == NULL) return -1;
- for (r = asl_action_rule.tqh_first; r != NULL; r = r->entries.tqe_next)
+ for (r = asl_action_rule; r != NULL; r = r->next)
{
if (asl_msg_cmp(r->query, msg) == 1)
{
- if (r->action == NULL) continue;
- else if (!strcmp(r->action, "access")) _act_access_control(r, msg);
- else if (!strcmp(r->action, "notify")) _act_notify(r);
+ if (r->action == ACTION_NONE) continue;
+ else if (r->action == ACTION_IGNORE) return 0;
+ 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_STORE) _act_store(r, msg);
+ else if (r->action == ACTION_STORE_DIR) _act_store(r, msg);
+ else if (r->action == ACTION_BROADCAST) _act_broadcast(r, msg);
+ else if (r->action == ACTION_FORWARD) _act_forward(r, msg);
}
}
+ send_to_asl_store(msg);
+
return 0;
}
static int
-_parse_notify_file(const char *name)
+_parse_config_file(const char *name)
{
FILE *cf;
char *line;
{
asldebug("%s: init\n", MY_ID);
- TAILQ_INIT(&asl_action_rule);
-
query = asl_new(ASL_TYPE_QUERY);
aslevent_addmatch(query, MY_ID);
aslevent_addoutput(asl_action_sendmsg, MY_ID);
- _parse_notify_file(_PATH_ASL_CONF);
+ _parse_config_file(_PATH_ASL_CONF);
return 0;
}
int
asl_action_reset(void)
{
- reset = 1;
+ reset = global.reset;
return 0;
}
int
asl_action_close(void)
{
- struct action_rule *r, *n;
+ action_rule_t *r, *n;
+ struct store_data *sd;
+ n = NULL;
+ for (r = asl_action_rule; r != NULL; r = n)
+ {
+ n = r->next;
+
+ if (((r->action == ACTION_STORE) || (r->action == ACTION_STORE_DIR) || (r->action == ACTION_NONE)) && (r->data != NULL))
+ {
+ sd = (struct store_data *)r->data;
+ if (sd->store != NULL) asl_file_close(sd->store);
+ if (sd->storedata != NULL) fclose(sd->storedata);
+ if (sd->path != NULL) free(sd->path);
+ if (sd->dir != NULL) free(sd->dir);
+ sd->store = NULL;
+ free(sd);
+ }
+
+ if (r->query != NULL) asl_free(r->query);
+ if (r->options != NULL) free(r->options);
+
+ free(r);
+ }
+
+ asl_action_rule = NULL;
n = NULL;
- for (r = asl_action_rule.tqh_first; r != NULL; r = n)
+ for (r = asl_datastore_rule; r != NULL; r = n)
{
- n = r->entries.tqe_next;
+ n = r->next;
if (r->query != NULL) asl_free(r->query);
- if (r->action != NULL) free(r->action);
if (r->options != NULL) free(r->options);
- TAILQ_REMOVE(&asl_action_rule, r, entries);
free(r);
}
+ asl_datastore_rule = NULL;
+
return 0;
}
#define forever for(;;)
#define ASL_SOCKET_NAME "AppleSystemLogger"
-#define MY_ID "asl"
+#define MY_ID "asl_in"
static int sock = -1;
-static asl_msg_t *query = NULL;
-
-#define MATCH_EOF -1
-#define MATCH_NULL 0
-#define MATCH_TRUE 1
-#define MATCH_FALSE 2
-
-extern void db_enqueue(asl_msg_t *m);
-
-static int filter_token = -1;
asl_msg_t *
asl_in_getmsg(int fd)
{
if (n == 0)
{
+ asl_client_count_decrement();
+
close(fd);
aslevent_removefd(fd);
return NULL;
asldebug("%s: read error (len=%d): %s\n", MY_ID, n, strerror(errno));
if (errno != EINTR)
{
+ asl_client_count_decrement();
+
close(fd);
aslevent_removefd(fd);
return NULL;
asldebug("%s: read error (body): %s\n", MY_ID, strerror(errno));
if (errno != EINTR)
{
+ asl_client_count_decrement();
+
close(fd);
aslevent_removefd(fd);
free(out);
}
asl_msg_t *
-asl_in_acceptmsg(int fd)
+asl_in_new_connection(int fd)
{
int clientfd;
- asldebug("%s: accepting message\n", MY_ID);
+ asldebug("%s: accepting connection\n", MY_ID);
clientfd = accept(fd, NULL, 0);
if (clientfd < 0)
{
- asldebug("%s: error accepting socket fd %d: %s\n", MY_ID, fd, strerror(errno));
+ asldebug("%s: error connecting socket fd %d: %s\n", MY_ID, fd, strerror(errno));
return NULL;
}
return NULL;
}
- aslevent_addfd(clientfd, ADDFD_FLAGS_LOCAL, asl_in_getmsg, NULL, NULL);
- return NULL;
-}
-
-int
-aslmod_sendmsg(asl_msg_t *msg, const char *outid)
-{
- const char *vlevel, *facility, *sender, *ignore;
- uint32_t lmask;
- uint64_t v64;
- int status, x, level, log_me;
+ asl_client_count_increment();
- /* set up com.apple.syslog.asl_filter */
- if (filter_token == -1)
- {
- status = notify_register_check(NOTIFY_SYSTEM_ASL_FILTER, &filter_token);
- if (status != NOTIFY_STATUS_OK)
- {
- filter_token = -1;
- }
- else
- {
- status = notify_check(filter_token, &x);
- if (status == NOTIFY_STATUS_OK)
- {
- v64 = global.asl_log_filter;
- status = notify_set_state(filter_token, v64);
- }
- if (status != NOTIFY_STATUS_OK)
- {
- notify_cancel(filter_token);
- filter_token = -1;
- }
- }
- }
-
- 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;
- }
- }
-
- facility = asl_get(msg, ASL_KEY_FACILITY);
- sender = asl_get(msg, ASL_KEY_SENDER);
-
- log_me = 0;
- if ((facility != NULL) && (!strcmp(facility, "kern"))) log_me = 1;
- else if ((sender != NULL) && (!strcmp(sender, "launchd"))) log_me = 1;
- else
- {
- 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;
- }
-
- if (log_me == 1)
- {
- ignore = asl_get(msg, ASL_KEY_IGNORE);
- if ((ignore != NULL) && (!strcasecmp(ignore, "yes"))) log_me = 0;
- }
-
- if (log_me == 1) db_enqueue(msg);
-
- return 0;
+ aslevent_addfd(SOURCE_ASL_SOCKET, clientfd, ADDFD_FLAGS_LOCAL, asl_in_getmsg, NULL, NULL);
+ return NULL;
}
int
if (sock >= 0) return sock;
if (global.launch_dict == NULL)
{
- asldebug("%s: laucnchd dict is NULL\n", MY_ID);
+ asldebug("%s: launchd dict is NULL\n", MY_ID);
return -1;
}
sockets_dict = launch_data_dict_lookup(global.launch_dict, LAUNCH_JOBKEY_SOCKETS);
if (sockets_dict == NULL)
{
- asldebug("%s: laucnchd lookup of LAUNCH_JOBKEY_SOCKETS failed\n", MY_ID);
+ asldebug("%s: launchd lookup of LAUNCH_JOBKEY_SOCKETS failed\n", MY_ID);
return -1;
}
fd_array = launch_data_dict_lookup(sockets_dict, ASL_SOCKET_NAME);
if (fd_array == NULL)
{
- asldebug("%s: laucnchd lookup of ASL_SOCKET_NAME failed\n", MY_ID);
+ asldebug("%s: launchd lookup of ASL_SOCKET_NAME failed\n", MY_ID);
return -1;
}
len = launch_data_array_get_count(fd_array);
if (len <= 0)
{
- asldebug("%s: laucnchd fd array is empty\n", MY_ID);
+ asldebug("%s: launchd fd array is empty\n", MY_ID);
return -1;
}
if (len > 1)
{
- asldebug("%s: warning! laucnchd fd array has %d sockets\n", MY_ID, len);
+ asldebug("%s: warning! launchd fd array has %d sockets\n", MY_ID, len);
}
fd_dict = launch_data_array_get_index(fd_array, 0);
if (fd_dict == NULL)
{
- asldebug("%s: laucnchd file discriptor array element 0 is NULL\n", MY_ID);
+ asldebug("%s: launchd file discriptor array element 0 is NULL\n", MY_ID);
return -1;
}
return -1;
}
- query = asl_new(ASL_TYPE_QUERY);
- aslevent_addmatch(query, MY_ID);
- aslevent_addoutput(aslmod_sendmsg, MY_ID);
-
- return aslevent_addfd(sock, ADDFD_FLAGS_LOCAL, asl_in_acceptmsg, NULL, NULL);
+ return aslevent_addfd(SOURCE_ASL_SOCKET, sock, ADDFD_FLAGS_LOCAL, asl_in_new_connection, NULL, NULL);
}
int
{
if (sock < 0) return 1;
- if (filter_token >= 0) notify_cancel(filter_token);
- filter_token = -1;
- global.asl_log_filter = 0;
-
- asl_free(query);
close(sock);
unlink(_PATH_ASL_IN);
--- /dev/null
+/*
+ * Copyright (c) 2009 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#include <stdio.h>
+#include <dirent.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <errno.h>
+#include <time.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <asl.h>
+#include <asl_private.h>
+#include <asl_core.h>
+#include <asl_file.h>
+#include <asl_store.h>
+
+extern time_t asl_parse_time(const char *);
+
+#define TEMP_NAME "_TMP_.asl"
+#define STORE_DATA_FLAGS 0x00000000
+
+static const char *store_path = PATH_ASL_STORE;
+
+/*
+ * Cache the output file for BB writes.
+ * we write messages in the order in which they were generated,
+ * so we are almost guaranteed to use the cache in most cases.
+ */
+static asl_file_t *cache_file = NULL;
+static uid_t cache_uid = -1;
+static uid_t cache_gid = -1;
+static time_t cache_bb = 0;
+
+typedef struct name_list_s
+{
+ char *name;
+ struct name_list_s *next;
+} name_list_t;
+
+static name_list_t *
+add_to_list(name_list_t *l, const char *name)
+{
+ name_list_t *e, *x;
+
+ if (name == NULL) return l;
+
+ e = (name_list_t *)calloc(1, sizeof(name_list_t));
+ if (e == NULL) return NULL;
+
+ e->name = strdup(name);
+ if (e->name == NULL)
+ {
+ free(e);
+ return NULL;
+ }
+
+ /* list is sorted by name (i.e. primarily by timestamp) */
+ if (l == NULL) return e;
+
+ if (strcmp(e->name, l->name) <= 0)
+ {
+ e->next = l;
+ return e;
+ }
+
+ for (x = l; (x->next != NULL) && (strcmp(e->name, x->next->name) > 0) ; x = x->next);
+
+ e->next = x->next;
+ x->next = e;
+ return l;
+}
+
+static void
+free_list(name_list_t *l)
+{
+ name_list_t *e;
+
+ while (l != NULL)
+ {
+ e = l;
+ l = l->next;
+ free(e->name);
+ free(e);
+ }
+
+ free(l);
+}
+
+/* find all messages that have an ASLExpireTime key */
+static uint32_t
+do_ASLExpireTime_search(asl_store_t *s, asl_search_result_t **out)
+{
+ asl_search_result_t q, *query, *res;
+ asl_msg_t *qm[1];
+ uint32_t status;
+ uint64_t mid;
+
+ qm[0] = asl_new(ASL_TYPE_QUERY);
+ if (qm[0] == NULL) return ASL_STATUS_NO_MEMORY;
+
+ q.count = 1;
+ q.curr = 0;
+ q.msg = qm;
+ query = &q;
+
+ if (asl_set_query(qm[0], ASL_KEY_EXPIRE_TIME, NULL, ASL_QUERY_OP_TRUE) != 0)
+ {
+ asl_free(qm[0]);
+ return ASL_STATUS_NO_MEMORY;
+ }
+
+ res = NULL;
+ mid = 0;
+ status = asl_store_match(s, query, out, &mid, 0, 0, 1);
+
+ asl_free(qm[0]);
+ return status;
+}
+
+/* remove all messages that have an ASLExpireTime key */
+static uint32_t
+do_ASLExpireTime_filter(const char *name)
+{
+ asl_msg_t *msg;
+ asl_file_t *in, *out;
+ uint32_t status;
+ uint64_t mid;
+ char *inpath, *outpath;
+ struct stat sb;
+
+ if (name == NULL) return ASL_STATUS_INVALID_ARG;
+
+ in = NULL;
+ inpath = NULL;
+ asprintf(&inpath, "%s/%s", store_path, name);
+ if (inpath == NULL) return ASL_STATUS_NO_MEMORY;
+
+ memset(&sb, 0, sizeof(struct stat));
+ if (stat(inpath, &sb) < 0)
+ {
+ free(inpath);
+ return ASL_STATUS_INVALID_STORE;
+ }
+
+ status = asl_file_open_read(inpath, &in);
+ if (status != ASL_STATUS_OK)
+ {
+ free(inpath);
+ return ASL_STATUS_OK;
+ }
+
+ out = NULL;
+ outpath = NULL;
+ asprintf(&outpath, "%s/%s", store_path, TEMP_NAME);
+ if (outpath == NULL)
+ {
+ asl_file_close(in);
+ free(inpath);
+ return ASL_STATUS_NO_MEMORY;
+ }
+
+ status = asl_file_open_write(outpath, sb.st_mode, sb.st_uid, sb.st_gid, &out);
+ if (status != ASL_STATUS_OK)
+ {
+ asl_file_close(in);
+ free(inpath);
+ free(outpath);
+ return status;
+ }
+
+ out->flags = ASL_FILE_FLAG_PRESERVE_MSG_ID;
+
+ msg = NULL;
+ while (asl_file_fetch_next(in, &msg) == ASL_STATUS_OK)
+ {
+ if (msg == NULL) break;
+
+ mid = 0;
+
+ if (asl_get(msg, ASL_KEY_EXPIRE_TIME) == NULL) status = asl_file_save(out, msg, &mid);
+
+ asl_free(msg);
+ msg = NULL;
+
+ if (status != ASL_STATUS_OK) break;
+ }
+
+ asl_file_close(in);
+ asl_file_close(out);
+
+ unlink(inpath);
+ rename(outpath, inpath);
+
+ free(inpath);
+ free(outpath);
+
+ return status;
+}
+
+/* qsort compare function for sorting by message ID */
+static int
+sort_compare(const void *a, const void *b)
+{
+ const char *va, *vb;
+ uint64_t na, nb;
+
+ va = asl_get(*(asl_msg_t **)a, ASL_KEY_MSG_ID);
+ vb = asl_get(*(asl_msg_t **)b, ASL_KEY_MSG_ID);
+
+ if (va == NULL) return -1;
+ if (vb == NULL) return 1;
+
+ na = atoll(va);
+ nb = atoll(vb);
+
+ if (na < nb) return -1;
+ if (na > nb) return 1;
+ return 0;
+}
+
+/* save a message to an appropriately named BB file */
+static uint32_t
+save_bb_msg(asl_msg_t *msg)
+{
+ const char *val;
+ uid_t u, ruid;
+ gid_t g, rgid;
+ struct tm ctm;
+ time_t msg_time, bb;
+ char *path, *tstring;
+ asl_file_t *out;
+ uint64_t mid;
+ mode_t m;
+ uint32_t status;
+
+ if (msg == NULL) return ASL_STATUS_OK;
+
+ val = asl_get(msg, ASL_KEY_EXPIRE_TIME);
+ if (val == NULL) return ASL_STATUS_INVALID_ARG;
+ msg_time = asl_parse_time(val);
+
+ val = asl_get(msg, ASL_KEY_READ_UID);
+ ruid = -1;
+ if (val != NULL) ruid = atoi(val);
+
+ val = asl_get(msg, ASL_KEY_READ_GID);
+ rgid = -1;
+ if (val != NULL) rgid = atoi(val);
+
+ if (localtime_r((const time_t *)&msg_time, &ctm) == NULL) return ASL_STATUS_FAILED;
+
+ /*
+ * This supports 12 monthy "Best Before" buckets.
+ * We advance the actual expiry time to day zero of the following month.
+ * mktime() is clever enough to know that you actually mean the last day
+ * of the previous month. What we get back from localtime is the last
+ * day of the month in which the message expires, which we use in the name.
+ */
+ ctm.tm_sec = 0;
+ ctm.tm_min = 0;
+ ctm.tm_hour = 0;
+ ctm.tm_mday = 0;
+ ctm.tm_mon += 1;
+
+ bb = mktime(&ctm);
+
+ u = 0;
+ g = 0;
+ if (ruid != -1) u = ruid;
+ if (rgid != -1) g = rgid;
+
+ out = NULL;
+
+ if (cache_file != NULL)
+ {
+ if ((cache_uid == u) && (cache_gid == g) && (cache_bb == bb))
+ {
+ out = cache_file;
+ }
+ else
+ {
+ asl_file_close(cache_file);
+ cache_file = NULL;
+ cache_uid = -1;
+ cache_gid = -1;
+ cache_bb = 0;
+ }
+ }
+
+ if (out == NULL)
+ {
+ if (localtime_r((const time_t *)&bb, &ctm) == NULL) return ASL_STATUS_FAILED;
+
+ tstring = NULL;
+ asprintf(&tstring, "%s/BB.%d.%02d.%02d", store_path, ctm.tm_year + 1900, ctm.tm_mon + 1, ctm.tm_mday);
+ if (tstring == NULL) return ASL_STATUS_NO_MEMORY;
+
+ path = NULL;
+ m = 0644;
+
+ if (ruid == -1)
+ {
+ if (rgid == -1)
+ {
+ asprintf(&path, "%s.asl", tstring);
+ }
+ else
+ {
+ m = 0640;
+ asprintf(&path, "%s.G%d.asl", tstring, g);
+ }
+ }
+ else
+ {
+ if (rgid == -1)
+ {
+ m = 0600;
+ asprintf(&path, "%s.U%d.asl", tstring, u);
+ }
+ else
+ {
+ m = 0640;
+ asprintf(&path, "%s.U%d.G%u.asl", tstring, u, g);
+ }
+ }
+
+ if (path == NULL) return ASL_STATUS_NO_MEMORY;
+
+ status = asl_file_open_write(path, m, u, g, &out);
+ free(path);
+ if (status != ASL_STATUS_OK) return status;
+ if (out == NULL) return ASL_STATUS_FAILED;
+
+ out->flags = ASL_FILE_FLAG_PRESERVE_MSG_ID;
+
+ cache_file = out;
+ cache_uid = u;
+ cache_gid = g;
+ cache_bb = bb;
+ }
+
+ status = asl_file_save(out, msg, &mid);
+
+ return status;
+}
+
+static uint32_t
+finish_conversion()
+{
+ FILE *sd;
+ uint32_t store_flags;
+ int status;
+ char *path;
+
+ path = NULL;
+ asprintf(&path, "%s/%s", store_path, FILE_ASL_STORE_DATA);
+
+ sd = fopen(path, "a");
+ free(path);
+ if (sd == NULL) return ASL_STATUS_WRITE_FAILED;
+
+ store_flags = STORE_DATA_FLAGS;
+ status = fwrite(&store_flags, sizeof(uint32_t), 1, sd);
+ fclose(sd);
+
+ if (status != 1) return ASL_STATUS_WRITE_FAILED;
+
+ return ASL_STATUS_OK;
+}
+
+/*
+ * Utility to convert a data store with LongTTL files into
+ * a store with Best Before files.
+ *
+ * Returns quickly if the data store has already been converted.
+ *
+ * Older versions of the data store included messages with non-standard time-to-live
+ * records in the daily data files (yyyy.mm.dd.asl). When the files expired, aslmanager
+ * first copied messages with ASLExpireTime keys to a LongTTL file, then deleted the
+ * original data file.
+ *
+ * We now write ASLExpireTime messages to a Best Before file (BB.yyyy.mm.dd.asl)
+ * and aslmanager just deletes these files after the Best Before date has passed.
+ *
+ * If StoreData is bigger than 8 bytes, the store has been converted. Do nothing.
+ *
+ * Convert the store:
+ * Search the store for messages that have an ASLExpireTime.
+ * Sort by ASLMessageID
+ * Remove all BB.* files and all LongTTL.* files
+ * Write the ASLExpireTime messages into a new set of BB files
+ * Re-write each YMD file without messages that have an ASLExpireTime
+ * Add a new 4-byte flags field to StoreData
+ */
+
+uint32_t
+bb_convert(const char *name)
+{
+ struct stat sb;
+ asl_store_t *store;
+ uint32_t status;
+ asl_search_result_t *expire_time_records;
+ DIR *dp;
+ struct dirent *dent;
+ int i;
+ name_list_t *list, *e;
+ char *path;
+
+ if (name != NULL) store_path = name;
+
+ /* StoreData must exist */
+ path = NULL;
+ asprintf(&path, "%s/%s", store_path, FILE_ASL_STORE_DATA);
+ if (path == NULL) return ASL_STATUS_NO_MEMORY;
+
+ memset(&sb, 0, sizeof(struct stat));
+ i = stat(path, &sb);
+ free(path);
+ if (i != 0) return ASL_STATUS_INVALID_STORE;
+
+ /* must be a regular file */
+ if ((sb.st_mode & S_IFREG) == 0) return ASL_STATUS_INVALID_STORE;
+
+ /* check is the store has already been converted */
+ if (sb.st_size > sizeof(uint64_t)) return ASL_STATUS_OK;
+
+ /* find ASLExpireTime messages */
+ status = asl_store_open_read(store_path, &store);
+ if (status != ASL_STATUS_OK) return status;
+
+ expire_time_records = NULL;
+ status = do_ASLExpireTime_search(store, &expire_time_records);
+
+ asl_store_close(store);
+ if (status != ASL_STATUS_OK) return status;
+
+ /* unlink BB.* and LongTTL.* */
+ dp = opendir(store_path);
+ if (dp == NULL) return ASL_STATUS_READ_FAILED;
+
+ while ((dent = readdir(dp)) != NULL)
+ {
+ if ((!strncmp(dent->d_name, "BB.", 3)) || (!strncmp(dent->d_name, "LongTTL.", 8)))
+ {
+ path = NULL;
+ asprintf(&path, "%s/%s", store_path, dent->d_name);
+ if (path == NULL)
+ {
+ closedir(dp);
+ return ASL_STATUS_NO_MEMORY;
+ }
+
+ unlink(path);
+ free(path);
+ }
+ }
+
+ closedir(dp);
+
+ if ((expire_time_records == NULL) || (expire_time_records->count == 0)) return finish_conversion();
+
+ /* sort by ASLMessageID */
+ qsort(expire_time_records->msg, expire_time_records->count, sizeof(asl_msg_t *), sort_compare);
+
+ /* save the ASLExpireTime messages into a new set of BB files */
+ for (i = 0; i < expire_time_records->count; i++)
+ {
+ status = save_bb_msg(expire_time_records->msg[i]);
+ if (status != ASL_STATUS_OK)
+ {
+ if (cache_file != NULL) asl_file_close(cache_file);
+ return status;
+ }
+ }
+
+ if (cache_file != NULL) asl_file_close(cache_file);
+
+ aslresponse_free(expire_time_records);
+
+ /* Re-write each YMD file without messages that have an ASLExpireTime */
+ dp = opendir(store_path);
+ if (dp == NULL) return ASL_STATUS_READ_FAILED;
+
+ list = NULL;
+
+ while ((dent = readdir(dp)) != NULL)
+ {
+ if ((dent->d_name[0] < '0') || (dent->d_name[0] > '9')) continue;
+ list = add_to_list(list, dent->d_name);
+ }
+
+ closedir(dp);
+
+ e = list;
+ for (e = list; e != NULL; e = e->next)
+ {
+ status = do_ASLExpireTime_filter(e->name);
+ if (status != ASL_STATUS_OK)
+ {
+ free_list(list);
+ return status;
+ }
+ }
+
+ free_list(list);
+
+ return finish_conversion();
+}
if (global.launch_dict == NULL)
{
- asldebug("%s: laucnchd dict is NULL\n", MY_ID);
+ asldebug("%s: launchd dict is NULL\n", MY_ID);
return -1;
}
sockets_dict = launch_data_dict_lookup(global.launch_dict, LAUNCH_JOBKEY_SOCKETS);
if (sockets_dict == NULL)
{
- asldebug("%s: laucnchd lookup of LAUNCH_JOBKEY_SOCKETS failed\n", MY_ID);
+ asldebug("%s: launchd lookup of LAUNCH_JOBKEY_SOCKETS failed\n", MY_ID);
return -1;
}
fd_array = launch_data_dict_lookup(sockets_dict, BSD_SOCKET_NAME);
if (fd_array == NULL)
{
- asldebug("%s: laucnchd lookup of BSD_SOCKET_NAME failed\n", MY_ID);
+ asldebug("%s: launchd lookup of BSD_SOCKET_NAME failed\n", MY_ID);
return -1;
}
len = launch_data_array_get_count(fd_array);
if (len <= 0)
{
- asldebug("%s: laucnchd fd array is empty\n", MY_ID);
+ asldebug("%s: launchd fd array is empty\n", MY_ID);
return -1;
}
if (len > 1)
{
- asldebug("%s: warning! laucnchd fd array has %d sockets\n", MY_ID, len);
+ asldebug("%s: warning! launchd fd array has %d sockets\n", MY_ID, len);
}
fd_dict = launch_data_array_get_index(fd_array, 0);
if (fd_dict == NULL)
{
- asldebug("%s: laucnchd file discriptor array element 0 is NULL\n", MY_ID);
+ asldebug("%s: launchd file discriptor array element 0 is NULL\n", MY_ID);
return -1;
}
return -1;
}
- return aslevent_addfd(sock, ADDFD_FLAGS_LOCAL, bsd_in_acceptmsg, NULL, NULL);
+ return aslevent_addfd(SOURCE_BSD_SOCKET, sock, ADDFD_FLAGS_LOCAL, bsd_in_acceptmsg, NULL, NULL);
}
int
#include <fcntl.h>
#include <errno.h>
#include <netdb.h>
+#include <pthread.h>
#include <notify.h>
#include "daemon.h"
#define ASL_KEY_FACILITY "Facility"
#define FACILITY_KERNEL "kern"
#define _PATH_CONSOLE "/dev/console"
-#define IndexNull ((uint32_t)-1)
#define DST_TYPE_NONE 0
#define DST_TYPE_FILE 1
#define DST_TYPE_WALL 4
#define DST_TYPE_NOTE 5
+#define CLOSE_ON_IDLE_SEC 60
+
static asl_msg_t *query = NULL;
-static int reset = 0;
+static int reset = RESET_NONE;
+static pthread_mutex_t reset_lock = PTHREAD_MUTEX_INITIALIZER;
struct config_rule
{
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)
{
- bsd_out_close();
- _parse_config_file(_PATH_SYSLOG_CONF);
-}
-
-static char **
-_insertString(char *s, char **l, uint32_t x)
-{
- int i, len;
-
- if (s == NULL) return l;
- if (l == NULL)
+ pthread_mutex_lock(&reset_lock);
+ if (reset == RESET_NONE)
{
- l = (char **)malloc(2 * sizeof(char *));
- if (l == NULL) return NULL;
-
- l[0] = strdup(s);
- if (l[0] == NULL)
- {
- free(l);
- return NULL;
- }
-
- l[1] = NULL;
- return l;
+ pthread_mutex_unlock(&reset_lock);
+ return;
}
- for (i = 0; l[i] != NULL; i++);
- len = i + 1; /* count the NULL on the end of the list too! */
-
- l = (char **)reallocf(l, (len + 1) * sizeof(char *));
- if (l == NULL) return NULL;
-
- if ((x >= (len - 1)) || (x == IndexNull))
+ if (reset == RESET_CONFIG)
{
- l[len - 1] = strdup(s);
- if (l[len - 1] == NULL)
- {
- free(l);
- return NULL;
- }
-
- l[len] = NULL;
- return l;
+ bsd_out_close();
+ _parse_config_file(_PATH_SYSLOG_CONF);
}
-
- for (i = len; i > x; i--) l[i] = l[i - 1];
- l[x] = strdup(s);
- if (l[x] == NULL) return NULL;
-
- return l;
-}
-
-static char **
-_explode(char *s, char *delim)
-{
- char **l = NULL;
- char *p, *t;
- int i, n;
-
- if (s == NULL) return NULL;
-
- p = s;
- while (p[0] != '\0')
+ else if (reset == RESET_NETWORK)
{
- for (i = 0; ((p[i] != '\0') && (strchr(delim, p[i]) == NULL)); i++);
- n = i;
- t = malloc(n + 1);
- if (t == NULL) return NULL;
-
- for (i = 0; i < n; i++) t[i] = p[i];
- t[n] = '\0';
- l = _insertString(t, l, IndexNull);
- free(t);
- t = NULL;
- if (p[i] == '\0') return l;
- if (p[i + 1] == '\0') l = _insertString("", l, IndexNull);
- p = p + i + 1;
+ bsd_out_network_reset();
}
+
+ reset = RESET_NONE;
- return l;
-}
-
-static void
-_freeList(char **l)
-{
- int i;
-
- if (l == NULL) return;
- for (i = 0; l[i] != NULL; i++) free(l[i]);
- free(l);
+ pthread_mutex_unlock(&reset_lock);
}
static int
if (r->dst[0] == '!')
{
r->type = DST_TYPE_NOTE;
- r->fd = 0;
+ r->fd = -1;
return 0;
}
r->fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
if (r->fd < 0) continue;
- r->addr = (struct sockaddr *)calloc(1, ai->ai_addrlen);
+ r->addr = (struct sockaddr *)malloc(ai->ai_addrlen);
if (r->addr == NULL) return -1;
memcpy(r->addr, ai->ai_addr, ai->ai_addrlen);
if (r->fd < 0)
{
asldebug("%s: connection failed for %s\n", MY_ID, (r->dst) + 1);
+ free(r->addr);
+ r->addr = NULL;
return -1;
}
close(r->fd);
r->fd = -1;
asldebug("%s: couldn't set O_NONBLOCK for fd %d: %s\n", MY_ID, r->fd, strerror(errno));
+ free(r->addr);
+ r->addr = NULL;
return -1;
}
if (strcmp(r->dst, "*") == 0)
{
r->type = DST_TYPE_WALL;
- r->fd = 0;
+ r->fd = -1;
return 0;
}
{
if (r == NULL) return;
+ if (r->addr != NULL)
+ {
+ free(r->addr);
+ r->addr = NULL;
+ }
+
switch (r->type)
{
case DST_TYPE_FILE:
{
if (r->fd >= 0) close(r->fd);
r->fd = -1;
- if (r->addr != NULL) free(r->addr);
- r->addr = NULL;
break;
}
}
}
+static char *
+_clean_facility_name(char *s)
+{
+ uint32_t len;
+ char *p, *out;
+
+ if (s == NULL) return NULL;
+ len = strlen(s);
+ if (len == 0) return NULL;
+
+ p = s;
+
+ if ((*s == '\'') || (*s == '"'))
+ {
+ len--;
+ p++;
+ if (p[len - 1] == *s) len --;
+ }
+
+ out = calloc(1, len + 1);
+ if (out == NULL) return NULL;
+
+ memcpy(out, p, len);
+ return out;
+}
+
static int
_parse_line(char *s)
{
while ((*s == ' ') || (*s == '\t')) s++;
if (*s == '#') return -1;
- semi = _explode(s, "; \t");
+ semi = explode(s, "; \t");
if (semi == NULL) return -1;
out = (struct config_rule *)calloc(1, sizeof(struct config_rule));
for (i = 0; i < lasts; i++)
{
if (semi[i][0] == '\0') continue;
- comma = _explode(semi[i], ",.");
+ comma = explode(semi[i], ",.");
lastc = -1;
for (j = 0; comma[j] != NULL; j++)
{
if (out->facility == NULL) return -1;
if (out->pri == NULL) return -1;
- out->facility[out->count] = strdup(comma[j]);
+ out->facility[out->count] = _clean_facility_name(comma[j]);
if (out->facility[out->count] == NULL) return -1;
out->pri[out->count] = pri;
out->count++;
}
- _freeList(comma);
+ freeList(comma);
}
- _freeList(semi);
+ freeList(semi);
TAILQ_INSERT_TAIL(&bsd_out_rule, out, entries);
static int
_syslog_send_repeat_msg(struct config_rule *r)
{
- char vt[16], *p, *msg;
+ char vt[32], *p, *msg;
time_t tick;
int len, status;
static int
_syslog_send(asl_msg_t *msg, struct config_rule *r, char **out, char **fwd, time_t now)
{
- char *so, *sf, *vt, *p, *outmsg;
+ char vt[16], *so, *sf, *p, *outmsg;
const char *vtime, *vhost, *vident, *vpid, *vmsg, *vlevel, *vfacility, *vrefproc, *vrefpid;
size_t outlen, n;
time_t tick;
}
msg_hash = 0;
- vt = NULL;
outmsg = NULL;
/* 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)
{
- tick = asl_parse_time(vtime);
- if (tick != (time_t)-1)
- {
- p = ctime(&tick);
- vt = malloc(16);
- if (vt == NULL) return -1;
-
- memcpy(vt, p+4, 15);
- vt[15] = '\0';
- }
- else if (strlen(vtime) < 24)
- {
- vt = strdup(vtime);
- if (vt == NULL) return -1;
- }
- else
- {
- vt = malloc(16);
- if (vt == NULL) return -1;
-
- memcpy(vt, vtime+4, 15);
- vt[15] = '\0';
- }
+ /* aslmsg_verify converts time to seconds, but use current time if something went sour */
+ tick = atol(vtime);
+ if (tick == 0) tick = now;
}
- if (vt == NULL)
- {
- tick = now;
- p = ctime(&tick);
- vt = malloc(16);
- if (vt == NULL) return -1;
-
- memcpy(vt, p+4, 15);
- vt[15] = '\0';
- }
+ p = ctime(&tick);
+ memcpy(vt, p+4, 15);
+ vt[15] = '\0';
vhost = asl_get(msg, ASL_KEY_HOST);
if (vhost == NULL) vhost = "localhost";
n = 0;
/* Time + " " */
- if (vt != NULL) n += (strlen(vt) + 1);
+ n += (strlen(vt) + 1);
/* Host + " " */
if (vhost != NULL) n += (strlen(vhost) + 1);
so = calloc(1, n);
if (so == NULL) return -1;
- if (vt != NULL)
- {
- strcat(so, vt);
- strcat(so, " ");
- }
+ strcat(so, vt);
+ strcat(so, " ");
if (vhost != NULL)
{
strcat(so, "\n");
- free(vt);
*out = so;
}
return -1;
}
- fprintf(pw, *out);
+ fprintf(pw, "%s", *out);
pclose(pw);
}
- _syslog_dst_close(r);
-
if (is_dup == 1)
{
r->last_count++;
time_t tick;
uint64_t delta;
- if (reset != 0)
- {
- _do_reset();
- reset = 0;
- }
+ if (reset != RESET_NONE) _do_reset();
if (msg == NULL) return -1;
return 0;
}
+void
+bsd_close_idle_files(time_t now)
+{
+ struct config_rule *r;
+ uint64_t delta;
+
+ 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 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;
+
+ delta = now - r->last_time;
+ if (delta > CLOSE_ON_IDLE_SEC) _syslog_dst_close(r);
+ }
+}
+
void
bsd_flush_duplicates(time_t now)
{
{
_syslog_dst_open(r);
_syslog_send_repeat_msg(r);
- _syslog_dst_close(r);
r->last_count = 0;
}
int
bsd_out_reset(void)
{
- reset = 1;
+ reset = global.reset;
return 0;
}
return 0;
}
+
+int
+bsd_out_network_reset(void)
+{
+ struct config_rule *r;
+
+ 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;
+}
<string>com.apple.syslogd</string>
<key>OnDemand</key>
<false/>
- <key>ProgramArguments</key>
+ <key>HopefullyExitsLast</key>
+ <true/>
+ <key>EnableTransactions</key>
+ <true/>
+ <key>ProgramArguments</key>
<array>
<!--
Un-comment the following lines to run syslogd with a sandbox profile.
/*
- * Copyright (c) 2004-2008 Apple Inc. All rights reserved.
+ * Copyright (c) 2004-2009 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
-#include <sys/queue.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define SYSLOG_NAMES
#include <pthread.h>
#include <vproc_priv.h>
#include <mach/mach.h>
+#include <vproc.h>
+#include <assert.h>
+#include <libkern/OSAtomic.h>
#include "daemon.h"
+#define LIST_SIZE_DELTA 256
+
#define streq(A,B) (strcmp(A,B)==0)
#define forever for(;;)
+#define IndexNull ((uint32_t)-1)
#define ASL_MSG_TYPE_MASK 0x0000000f
#define ASL_TYPE_ERROR 2
#define SYSTEM_RESERVED "com.apple.system"
#define SYSTEM_RESERVED_LEN 16
-extern void disaster_message(asl_msg_t *m);
+#define VERIFY_STATUS_OK 0
+#define VERIFY_STATUS_INVALID_MESSAGE 1
+#define VERIFY_STATUS_EXCEEDED_QUOTA 2
+extern void disaster_message(asl_msg_t *m);
static char myname[MAXHOSTNAMELEN + 1] = {0};
-static pthread_mutex_t event_lock = PTHREAD_MUTEX_INITIALIZER;
+static OSSpinLock count_lock = 0;
+
+static vproc_transaction_t vproc_trans = {0};
+
+#define QUOTA_TABLE_SIZE 8192
+#define QUOTA_TABLE_SLOTS 8
+
+#define QUOTA_EXCEEDED_MESSAGE "*** process %d exceeded %d log message per second limit - remaining messages this second discarded ***"
+#define QUOTA_EXCEEDED_LEVEL "3"
+
+static time_t quota_table_time = 0;
+static pid_t quota_table_pid[QUOTA_TABLE_SIZE];
+static int32_t quota_table_quota[QUOTA_TABLE_SIZE];
static const char *kern_notify_key[] =
{
"com.apple.system.log.kernel.debug"
};
-struct aslevent
-{
- int fd;
- unsigned char read:1;
- unsigned char write:1;
- unsigned char except:1;
- aslreadfn readfn;
- aslwritefn writefn;
- aslexceptfn exceptfn;
- char *sender;
- uid_t uid;
- gid_t gid;
- TAILQ_ENTRY(aslevent) entries;
-};
-
struct asloutput
{
aslsendmsgfn sendmsg;
TAILQ_HEAD(ao, asloutput) Outq;
TAILQ_HEAD(am, aslmatch) Matchq;
-static struct aslevent *launchd_event;
+static char **
+_insertString(char *s, char **l, uint32_t x)
+{
+ int i, len;
+
+ if (s == NULL) return l;
+ if (l == NULL)
+ {
+ l = (char **)malloc(2 * sizeof(char *));
+ if (l == NULL) return NULL;
+
+ l[0] = strdup(s);
+ if (l[0] == NULL)
+ {
+ free(l);
+ return NULL;
+ }
+
+ l[1] = NULL;
+ return l;
+ }
+
+ for (i = 0; l[i] != NULL; i++);
+ len = i + 1; /* count the NULL on the end of the list too! */
+
+ l = (char **)reallocf(l, (len + 1) * sizeof(char *));
+ if (l == NULL) return NULL;
+
+ if ((x >= (len - 1)) || (x == IndexNull))
+ {
+ l[len - 1] = strdup(s);
+ if (l[len - 1] == NULL)
+ {
+ free(l);
+ return NULL;
+ }
+
+ l[len] = NULL;
+ return l;
+ }
+
+ for (i = len; i > x; i--) l[i] = l[i - 1];
+ l[x] = strdup(s);
+ if (l[x] == NULL) return NULL;
+
+ return l;
+}
+
+char **
+explode(const char *s, const char *delim)
+{
+ char **l = NULL;
+ const char *p;
+ char *t, quote;
+ int i, n;
+
+ if (s == NULL) return NULL;
+
+ quote = '\0';
+
+ p = s;
+ while (p[0] != '\0')
+ {
+ /* scan forward */
+ for (i = 0; p[i] != '\0'; i++)
+ {
+ if (quote == '\0')
+ {
+ /* not inside a quoted string: check for delimiters and quotes */
+ if (strchr(delim, p[i]) != NULL) break;
+ else if (p[i] == '\'') quote = p[i];
+ else if (p[i] == '"') quote = p[i];
+ }
+ else
+ {
+ /* inside a quoted string - look for matching quote */
+ if (p[i] == quote) quote = '\0';
+ }
+ }
+
+ n = i;
+ t = malloc(n + 1);
+ if (t == NULL) return NULL;
+
+ for (i = 0; i < n; i++) t[i] = p[i];
+ t[n] = '\0';
+ l = _insertString(t, l, IndexNull);
+ free(t);
+ t = NULL;
+ if (p[i] == '\0') return l;
+ if (p[i + 1] == '\0') l = _insertString("", l, IndexNull);
+ p = p + i + 1;
+ }
+
+ return l;
+}
+
+void
+freeList(char **l)
+{
+ int i;
+
+ if (l == NULL) return;
+ for (i = 0; l[i] != NULL; i++) free(l[i]);
+ free(l);
+}
+
+/*
+ * Quotas are maintained using a very fast fixed-size table.
+ * We hash into the pid table (quota_table_pid) using the last 10
+ * bits of the pid, so the table has 1024 "buckets". The table is
+ * actually just an array with 8 entry slots (for collisions) per bucket.
+ * If there are more than 8 pids that hash to the same bucket, we
+ * re-use the one with the lowest message usage (highest remaining
+ * quota). This can lead to "generosity: if there are nine of more
+ * pids with the same last 10 bits all logging like crazy, we may
+ * end up allowing some of them to log more than their quota.
+ * That would be a remarkably rare occurrence.
+ */
+
+static uint32_t
+quota_check(pid_t pid, time_t now, asl_msg_t *msg)
+{
+ int i, x, maxx, max;
+ char *str;
+
+ if (msg == NULL) return VERIFY_STATUS_INVALID_MESSAGE;
+ if (global.mps_limit == 0) return VERIFY_STATUS_OK;
+
+ if (quota_table_time != now)
+ {
+ memset(quota_table_pid, 0, sizeof(quota_table_pid));
+ quota_table_time = now;
+ }
+
+ /* hash is last 10 bits of the pid, shifted up 3 bits to allow 8 slots per bucket */
+ x = (pid & 0x000003ff) << 3;
+ maxx = x;
+ max = quota_table_quota[x];
+
+ for (i = 0; i < QUOTA_TABLE_SLOTS; i++)
+ {
+ if (quota_table_pid[x] == 0)
+ {
+ quota_table_pid[x] = pid;
+ quota_table_quota[x] = global.mps_limit;
+ return VERIFY_STATUS_OK;
+ }
+
+ if (quota_table_pid[x] == pid)
+ {
+ quota_table_quota[x] = quota_table_quota[x] - 1;
+
+ if (quota_table_quota[x] == 0)
+ {
+ quota_table_quota[x] = -1;
+
+ str = NULL;
+ asprintf(&str, QUOTA_EXCEEDED_MESSAGE, (int)pid, global.mps_limit);
+ if (str != NULL)
+ {
+ asl_set(msg, ASL_KEY_MSG, str);
+ free(str);
+ asl_set(msg, ASL_KEY_LEVEL, QUOTA_EXCEEDED_LEVEL);
+ }
+
+ return VERIFY_STATUS_OK;
+ }
+
+ if (quota_table_quota[x] < 0) return VERIFY_STATUS_EXCEEDED_QUOTA;
+
+ return VERIFY_STATUS_OK;
+ }
+
+ if (quota_table_quota[x] > max)
+ {
+ maxx = x;
+ max = quota_table_quota[x];
+ }
+
+ x += 1;
+ }
+
+ /* can't find the pid and no slots were available - reuse slot with highest remaining quota */
+ asldebug("Quotas: reused slot %d pid %d quota %d for new pid %d\n", maxx, (int)quota_table_pid[maxx], quota_table_quota[maxx], (int)pid);
+ quota_table_pid[maxx] = pid;
+ quota_table_quota[maxx] = global.mps_limit;
+
+ return VERIFY_STATUS_OK;
+}
+
+int
+asl_check_option(asl_msg_t *msg, const char *opt)
+{
+ const char *p;
+ uint32_t len;
+
+ if (msg == NULL) return 0;
+ if (opt == NULL) return 0;
+
+ len = strlen(opt);
+ if (len == 0) return 0;
+
+ p = asl_get(msg, ASL_KEY_OPTION);
+ if (p == NULL) return 0;
+
+ while (*p != '\0')
+ {
+ while ((*p == ' ') || (*p == '\t') || (*p == ',')) p++;
+ if (*p == '\0') return 0;
+
+ if (strncasecmp(p, opt, len) == 0)
+ {
+ p += len;
+ if ((*p == ' ') || (*p == '\t') || (*p == ',') || (*p == '\0')) return 1;
+ }
+
+ while ((*p != ' ') && (*p != '\t') && (*p != ',') && (*p != '\0')) p++;
+ }
+
+ return 0;
+}
int
aslevent_init(void)
}
void
-aslevent_match(asl_msg_t *msg)
+asl_message_match_and_log(asl_msg_t *msg)
{
struct aslmatch *i;
return (const char *)myname;
}
+void
+asl_client_count_increment()
+{
+ OSSpinLockLock(&count_lock);
+
+ if (global.client_count == 0) vproc_trans = vproc_transaction_begin(NULL);
+ global.client_count++;
+#ifdef DEBUG
+ asldebug("global.client_count++ (%d)\n", global.client_count);
+#endif
+
+ OSSpinLockUnlock(&count_lock);
+}
+
+void
+asl_client_count_decrement()
+{
+ OSSpinLockLock(&count_lock);
+
+ if (global.client_count > 0) global.client_count--;
+ if (global.client_count == 0) vproc_transaction_end(NULL, vproc_trans);
+#ifdef DEBUG
+ asldebug("global.client_count-- (%d)\n", global.client_count);
+#endif
+
+ OSSpinLockUnlock(&count_lock);
+}
+
int
-aslevent_addfd(int fd, uint32_t flags, aslreadfn readfn, aslwritefn writefn, aslexceptfn exceptfn)
+aslevent_addfd(int source, int fd, uint32_t flags, aslreadfn readfn, aslwritefn writefn, aslexceptfn exceptfn)
{
struct aslevent *e;
int found = 0, status;
}
}
- asldebug("fd %d flags 0x%08x UID %d GID %d Sender %s\n", fd, flags, u, g, (sender == NULL) ? "NULL" : sender );
+ asldebug("source %d fd %d flags 0x%08x UID %d GID %d Sender %s\n", source, fd, flags, u, g, (sender == NULL) ? "NULL" : sender );
for (e = Eventq.tqh_first; e != NULL; e = e->entries.tqe_next)
{
e = calloc(1, sizeof(struct aslevent));
if (e == NULL) return -1;
+ e->source = source;
e->fd = fd;
e->readfn = readfn;
e->writefn = writefn;
return 0;
}
-static int
-aslmsg_verify(struct aslevent *e, asl_msg_t *msg, int32_t *kern_post_level)
+/*
+ * Checks message content and sets attributes as required
+ *
+ * SOURCE_INTERNAL log messages sent by syslogd itself
+ * SOURCE_ASL_SOCKET legacy asl(3) TCP socket
+ * SOURCE_BSD_SOCKET legacy syslog(3) UDP socket
+ * SOURCE_UDP_SOCKET from the network
+ * SOURCE_KERN from the kernel
+ * SOURCE_ASL_MESSAGE mach messages sent from Libc by asl(3) and syslog(3)
+ * SOURCE_LAUNCHD forwarded from launchd
+ */
+
+static uint32_t
+aslmsg_verify(uint32_t source, struct aslevent *e, asl_msg_t *msg, int32_t *kern_post_level)
{
const char *val, *fac;
- char buf[32], *timestr;
+ char buf[64];
time_t tick, now;
- struct tm gtime;
uid_t uid;
- uint32_t level, fnum, kern;
- int isreserved;
-
- if (msg == NULL) return -1;
+ uint32_t status, level, fnum;
+ pid_t pid;
- *kern_post_level = -1;
+ if (msg == NULL) return VERIFY_STATUS_INVALID_MESSAGE;
- kern = 0;
- if ((e != NULL) && (e->fd == global.kfd)) kern = 1;
+ if (kern_post_level != NULL) *kern_post_level = -1;
/* Time */
now = time(NULL);
/* Set time to now if it is unset or from the future (not allowed!) */
if ((tick == 0) || (tick > now)) tick = now;
- memset(>ime, 0, sizeof(struct tm));
- gmtime_r(&tick, >ime);
-
- /* Canonical form: YYYY.MM.DD hh:mm:ss UTC */
- asprintf(×tr, "%d.%02d.%02d %02d:%02d:%02d UTC", gtime.tm_year + 1900, gtime.tm_mon + 1, gtime.tm_mday, gtime.tm_hour, gtime.tm_min, gtime.tm_sec);
- if (timestr != NULL)
- {
- asl_set(msg, ASL_KEY_TIME, timestr);
- free(timestr);
- }
+ /* Canonical form: seconds since the epoch */
+ snprintf(buf, sizeof(buf) - 1, "%lu", tick);
+ asl_set(msg, ASL_KEY_TIME, buf);
/* Host */
if (e == NULL) asl_set(msg, ASL_KEY_HOST, whatsmyhostname());
}
/* PID */
+ pid = 0;
+
val = asl_get(msg, ASL_KEY_PID);
if (val == NULL) asl_set(msg, ASL_KEY_PID, "0");
+ else pid = (pid_t)atoi(val);
+
+ /* if PID is 1 (launchd), use the refpid if there is one */
+ if (pid == 1)
+ {
+ val = asl_get(msg, ASL_KEY_REF_PID);
+ if (val != NULL) pid = (pid_t)atoi(val);
+ }
+
+ /* if quotas are enabled and pid > 1 (not kernel or launchd) check quota */
+ if ((global.mps_limit > 0) && (pid > 1))
+ {
+ status = quota_check(pid, now, msg);
+ if (status != VERIFY_STATUS_OK) return status;
+ }
/* UID */
uid = -2;
val = asl_get(msg, ASL_KEY_UID);
- if (kern == 1)
- {
- uid = 0;
- asl_set(msg, ASL_KEY_UID, "0");
- }
- else if (val == NULL)
+
+ switch (source)
{
- if (e == NULL) asl_set(msg, ASL_KEY_UID, "-2");
- else if (e->uid == 99) asl_set(msg, ASL_KEY_UID, "-2");
- else
+ case SOURCE_KERN:
+ case SOURCE_INTERNAL:
{
- uid = e->uid;
- snprintf(buf, sizeof(buf), "%d", e->uid);
- asl_set(msg, ASL_KEY_UID, buf);
+ /* we know the UID is 0 */
+ uid = 0;
+ asl_set(msg, ASL_KEY_UID, "0");
+ break;
+ }
+ case SOURCE_ASL_SOCKET:
+ case SOURCE_ASL_MESSAGE:
+ case SOURCE_LAUNCHD:
+ {
+ /* we trust the UID in the message */
+ if (val != NULL) uid = atoi(val);
+ break;
+ }
+ case SOURCE_BSD_SOCKET:
+ case SOURCE_UDP_SOCKET:
+ {
+ if (val == NULL)
+ {
+ if (e == NULL) asl_set(msg, ASL_KEY_UID, "-2");
+ else if (e->uid == 99) asl_set(msg, ASL_KEY_UID, "-2");
+ else
+ {
+ uid = e->uid;
+ snprintf(buf, sizeof(buf), "%d", e->uid);
+ asl_set(msg, ASL_KEY_UID, buf);
+ }
+ }
+ else if ((e != NULL) && (e->uid != 99))
+ {
+ uid = e->uid;
+ snprintf(buf, sizeof(buf), "%d", e->uid);
+ asl_set(msg, ASL_KEY_UID, buf);
+ }
+ }
+ default:
+ {
+ asl_set(msg, ASL_KEY_UID, "-2");
}
- }
- else if ((e != NULL) && (e->uid != 99))
- {
- uid = e->uid;
- snprintf(buf, sizeof(buf), "%d", e->uid);
- asl_set(msg, ASL_KEY_UID, buf);
}
/* GID */
val = asl_get(msg, ASL_KEY_GID);
- if (kern == 1)
- {
- asl_set(msg, ASL_KEY_GID, "0");
- }
- else if (val == NULL)
+
+ switch (source)
{
- if (e == NULL) asl_set(msg, ASL_KEY_GID, "-2");
- else if (e->gid == 99) asl_set(msg, ASL_KEY_GID, "-2");
- else
+ case SOURCE_KERN:
+ case SOURCE_INTERNAL:
{
- snprintf(buf, sizeof(buf), "%d", e->gid);
- asl_set(msg, ASL_KEY_GID, buf);
+ /* we know the GID is 0 */
+ asl_set(msg, ASL_KEY_GID, "0");
+ break;
+ }
+ case SOURCE_ASL_SOCKET:
+ case SOURCE_ASL_MESSAGE:
+ case SOURCE_LAUNCHD:
+ {
+ /* we trust the GID in the message */
+ break;
+ }
+ case SOURCE_BSD_SOCKET:
+ case SOURCE_UDP_SOCKET:
+ {
+ if (val == NULL)
+ {
+ if (e == NULL) asl_set(msg, ASL_KEY_GID, "-2");
+ else if (e->gid == 99) asl_set(msg, ASL_KEY_GID, "-2");
+ else
+ {
+ snprintf(buf, sizeof(buf), "%d", e->gid);
+ asl_set(msg, ASL_KEY_GID, buf);
+ }
+ }
+ else if ((e != NULL) && (e->gid != 99))
+ {
+ snprintf(buf, sizeof(buf), "%d", e->gid);
+ asl_set(msg, ASL_KEY_GID, buf);
+ }
+ }
+ default:
+ {
+ asl_set(msg, ASL_KEY_GID, "-2");
}
- }
- else if ((e != NULL) && (e->gid != 99))
- {
- snprintf(buf, sizeof(buf), "%d", e->gid);
- asl_set(msg, ASL_KEY_GID, buf);
}
/* Sender */
val = asl_get(msg, ASL_KEY_SENDER);
if (val == NULL)
{
- if (kern == 0) asl_set(msg, ASL_KEY_SENDER, "Unknown");
- else asl_set(msg, ASL_KEY_SENDER, "kernel");
+ switch (source)
+ {
+ case SOURCE_KERN:
+ {
+ asl_set(msg, ASL_KEY_SENDER, "kernel");
+ break;
+ }
+ case SOURCE_INTERNAL:
+ {
+ asl_set(msg, ASL_KEY_SENDER, "syslogd");
+ break;
+ }
+ default:
+ {
+ asl_set(msg, ASL_KEY_SENDER, "Unknown");
+ }
+ }
}
- else if ((kern == 0) && (uid != 0) && (!strcmp(val, "kernel")))
+ else if ((source != SOURCE_KERN) && (uid != 0) && (!strcmp(val, "kernel")))
{
+ /* allow UID 0 to send messages with "Sender kernel", but nobody else */
asl_set(msg, ASL_KEY_SENDER, "Unknown");
}
fac = asl_get(msg, ASL_KEY_FACILITY);
if (fac == NULL)
{
- if (kern == 1) fac = "kern";
+ if (source == SOURCE_KERN) fac = "kern";
else fac = "user";
asl_set(msg, ASL_KEY_FACILITY, fac);
}
fac = asl_syslog_faciliy_num_to_name(fnum);
asl_set(msg, ASL_KEY_FACILITY, fac);
}
+ else if (!strncmp(fac, SYSTEM_RESERVED, SYSTEM_RESERVED_LEN))
+ {
+ /* only UID 0 may use "com.apple.system" */
+ if (uid != 0) asl_set(msg, ASL_KEY_FACILITY, FACILITY_USER);
+ }
+
+ /*
+ * kernel messages are only readable by root and admin group.
+ */
+ if (source == SOURCE_KERN)
+ {
+ asl_set(msg, ASL_KEY_READ_UID, "0");
+ asl_set(msg, ASL_KEY_READ_GID, "80");
+ }
/*
* Access Control: only UID 0 may use facility com.apple.system (or anything with that prefix).
asl_set(msg, ASL_KEY_EXPIRE_TIME, buf);
}
- if (e != NULL)
- {
- isreserved = 0;
- if (!strncmp(fac, SYSTEM_RESERVED, SYSTEM_RESERVED_LEN))
- {
- if (uid != 0) asl_set(msg, ASL_KEY_FACILITY, FACILITY_USER);
- else isreserved = 1;
- }
- }
-
/*
* special case handling of kernel disaster messages
*/
- if ((kern == 1) && (level <= KERN_DISASTER_LEVEL))
+ if ((source == SOURCE_KERN) && (level <= KERN_DISASTER_LEVEL))
{
- *kern_post_level = level;
+ if (kern_post_level != NULL) *kern_post_level = level;
disaster_message(msg);
}
- return 0;
+ return VERIFY_STATUS_OK;
}
int
}
}
+void
+list_append_msg(asl_search_result_t *list, asl_msg_t *msg)
+{
+ if (list == NULL) return;
+ if (msg == NULL) return;
+
+ /*
+ * NB: curr is the list size
+ * grow list if necessary
+ */
+ if (list->count == list->curr)
+ {
+ if (list->curr == 0)
+ {
+ list->msg = (asl_msg_t **)calloc(LIST_SIZE_DELTA, sizeof(asl_msg_t *));
+ }
+ else
+ {
+ list->msg = (asl_msg_t **)reallocf(list->msg, (list->curr + LIST_SIZE_DELTA) * sizeof(asl_msg_t *));
+ }
+
+ if (list->msg == NULL)
+ {
+ list->curr = 0;
+ list->count = 0;
+ return;
+ }
+
+ list->curr += LIST_SIZE_DELTA;
+ }
+
+ list->msg[list->count] = msg;
+ list->count++;
+}
+
+void
+work_enqueue(asl_msg_t *m)
+{
+ pthread_mutex_lock(global.work_queue_lock);
+ list_append_msg(global.work_queue, m);
+ pthread_mutex_unlock(global.work_queue_lock);
+ pthread_cond_signal(&global.work_queue_cond);
+}
+
+void
+asl_enqueue_message(uint32_t source, struct aslevent *e, asl_msg_t *msg)
+{
+ int32_t kplevel;
+ uint32_t status;
+
+ if (msg == NULL) return;
+
+ /* set retain count to 1 */
+ msg->type |= 0x10;
+
+ kplevel = -1;
+ status = aslmsg_verify(source, e, msg, &kplevel);
+ if (status == VERIFY_STATUS_OK)
+ {
+ if ((source == SOURCE_KERN) && (kplevel >= 0)) notify_post(kern_notify_key[kplevel]);
+ work_enqueue(msg);
+ }
+ else
+ {
+ asl_msg_release(msg);
+ }
+}
+
+asl_msg_t **
+asl_work_dequeue(uint32_t *count)
+{
+ asl_msg_t **work;
+
+ pthread_mutex_lock(global.work_queue_lock);
+ pthread_cond_wait(&global.work_queue_cond, global.work_queue_lock);
+
+ work = NULL;
+ *count = 0;
+
+ if (global.work_queue->count == 0)
+ {
+ pthread_mutex_unlock(global.work_queue_lock);
+ return NULL;
+ }
+
+ work = global.work_queue->msg;
+ *count = global.work_queue->count;
+
+ global.work_queue->count = 0;
+ global.work_queue->curr = 0;
+ global.work_queue->msg = NULL;
+
+ pthread_mutex_unlock(global.work_queue_lock);
+ return work;
+}
+
void
aslevent_handleevent(fd_set *rd, fd_set *wr, fd_set *ex)
{
struct aslevent *e;
char *out = NULL;
asl_msg_t *msg;
- int32_t kplevel, cleanup;
+ int32_t cleanup;
// asldebug("--> aslevent_handleevent\n");
msg = e->readfn(e->fd);
if (msg == NULL) continue;
- /* type field is overloaded to provide retain/release inside syslogd */
- msg->type |= 0x10;
-
- pthread_mutex_lock(&event_lock);
-
- kplevel = -1;
- if (aslmsg_verify(e, msg, &kplevel) < 0)
- {
- asl_msg_release(msg);
- asldebug("recieved invalid message\n\n");
- }
- else
- {
- aslevent_match(msg);
- asl_msg_release(msg);
- if (kplevel >= 0) notify_post(kern_notify_key[kplevel]);
- }
-
- pthread_mutex_unlock(&event_lock);
+ asl_enqueue_message(e->source, e, msg);
}
if (FD_ISSET(e->fd, ex) && e->exceptfn)
asl_log_string(const char *str)
{
asl_msg_t *msg;
- int32_t unused;
if (str == NULL) return 1;
msg = asl_msg_from_string(str);
+ if (msg == NULL) return 1;
- /* set retain count */
- msg->type |= 0x10;
-
- pthread_mutex_lock(&event_lock);
-
- unused = -1;
- if (aslmsg_verify(NULL, msg, &unused) < 0)
- {
- pthread_mutex_unlock(&event_lock);
- asl_msg_release(msg);
- return -1;
- }
-
- aslevent_match(msg);
- asl_msg_release(msg);
-
- pthread_mutex_unlock(&event_lock);
+ asl_enqueue_message(SOURCE_INTERNAL, NULL, msg);
return 0;
}
int status;
FILE *dfp;
- if (global.debug == 0) return 0;
+ OSSpinLockLock(&global.lock);
+ if (global.debug == 0)
+ {
+ OSSpinLockUnlock(&global.lock);
+ return 0;
+ }
dfp = stderr;
if (global.debug_file != NULL) dfp = fopen(global.debug_file, "a");
- if (dfp == NULL) return 0;
+ if (dfp == NULL)
+ {
+ OSSpinLockUnlock(&global.lock);
+ return 0;
+ }
va_start(v, str);
status = vfprintf(dfp, str, v);
va_end(v);
if (global.debug_file != NULL) fclose(dfp);
+ OSSpinLockUnlock(&global.lock);
+
return status;
}
void
asl_msg_release(asl_msg_t *m)
{
- uint32_t refcount;
+ int32_t newval;
if (m == NULL) return;
- refcount = m->type >> 4;
- if (refcount > 0)
- {
- refcount--;
- m->type -= 0x10;
- }
+ newval = OSAtomicAdd32(-0x10, (int32_t*)&m->type) >> 4;
+ assert(newval >= 0);
+
+ if (newval > 0) return;
- if (refcount > 0) return;
asl_free(m);
}
asl_msg_t *
asl_msg_retain(asl_msg_t *m)
{
+ int32_t newval;
+
if (m == NULL) return NULL;
- m->type += 0x10;
+ newval = OSAtomicAdd32(0x10, (int32_t*)&m->type) >> 4;
+ assert(newval > 0);
+
return m;
}
{
asl_msg_t *m;
char str[256];
- int32_t unused;
- int status;
time_t now;
/*
when->tv_sec, when->tv_usec, from_pid, about_pid, sender_uid, sender_gid, priority, from_name, about_name, session_name, msg);
*/
- if (launchd_event != NULL)
- {
- launchd_event->uid = sender_uid;
- launchd_event->gid = sender_gid;
- }
-
m = asl_new(ASL_TYPE_MSG);
if (m == NULL) return;
/* Facility */
asl_set(m, ASL_KEY_FACILITY, FACILITY_CONSOLE);
+ /* UID */
+ snprintf(str, sizeof(str), "%u", (unsigned int)sender_uid);
+ asl_set(m, ASL_KEY_UID, str);
+
+ /* GID */
+ snprintf(str, sizeof(str), "%u", (unsigned int)sender_gid);
+ asl_set(m, ASL_KEY_GID, str);
+
/* PID */
if (from_pid != 0)
{
asl_set(m, ASL_KEY_MSG, msg);
}
- /* set retain count */
- m->type |= 0x10;
-
/* verify and push to receivers */
- status = aslmsg_verify(launchd_event, m, &unused);
- if (status >= 0) aslevent_match(m);
-
- asl_msg_release(m);
+ asl_enqueue_message(SOURCE_LAUNCHD, NULL, m);
}
void
launchd_drain()
{
- launchd_event = (struct aslevent *)calloc(1, sizeof(struct aslevent));
-
forever
{
- _vprocmgr_log_drain(NULL, &event_lock, launchd_callback);
+ _vprocmgr_log_drain(NULL, NULL, launchd_callback);
}
}
/*
- * Copyright (c) 2004-2008 Apple Inc. All rights reserved.
+ * Copyright (c) 2004-2009 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
#include <asl_mini_memory.h>
#include <notify.h>
#include <launch.h>
+#include <libkern/OSAtomic.h>
#define ADDFD_FLAGS_LOCAL 0x00000001
#define ASL_KEY_READ_UID "ReadUID"
#define ASL_KEY_READ_GID "ReadGID"
#define ASL_KEY_EXPIRE_TIME "ASLExpireTime"
-#define ASL_KEY_IGNORE "ASLIgnore"
#define ASL_KEY_TIME_NSEC "TimeNanoSec"
#define ASL_KEY_REF_PID "RefPID"
#define ASL_KEY_REF_PROC "RefProc"
#define ASL_KEY_SESSION "Session"
+#define ASL_KEY_OPTION "ASLOption"
+
+#define ASL_OPT_IGNORE "ignore"
+#define ASL_OPT_STORE "store"
#define _PATH_PIDFILE "/var/run/syslog.pid"
#define _PATH_ASL_IN "/var/run/asl_input"
#define KERN_DISASTER_LEVEL 3
+#define SOURCE_UNKNOWN 0
+#define SOURCE_INTERNAL 1
+#define SOURCE_ASL_SOCKET 2
+#define SOURCE_BSD_SOCKET 3
+#define SOURCE_UDP_SOCKET 4
+#define SOURCE_KERN 5
+#define SOURCE_ASL_MESSAGE 6
+#define SOURCE_LAUNCHD 7
+
+#define SOURCE_SESSION 100 /* does not generate messages */
+
+#define STORE_FLAGS_FILE_CACHE_SWEEP_REQUESTED 0x00000001
+
+#define RESET_NONE 0
+#define RESET_CONFIG 1
+#define RESET_NETWORK 2
+
struct global_s
{
- int asl_log_filter;
- int restart;
- int debug;
+ OSSpinLock lock;
+ int client_count;
int disaster_occurred;
+ mach_port_t listen_set;
mach_port_t server_port;
+ mach_port_t self_port;
+ mach_port_t dead_session_port;
launch_data_t launch_dict;
- const char *debug_file;
- int dbtype;
- int did_store_sweep;
+ uint32_t store_flags;
time_t start_time;
- uint32_t db_file_max;
- uint32_t db_memory_max;
- uint32_t db_mini_max;
int kfd;
+ int reset;
uint64_t bsd_flush_time;
- uint64_t asl_store_ping_time;
- uint64_t bsd_max_dup_time;
- time_t utmp_ttl;
- time_t fs_ttl;
+ pthread_mutex_t *db_lock;
+ pthread_mutex_t *work_queue_lock;
+ pthread_cond_t work_queue_cond;
+ asl_search_result_t *work_queue;
asl_store_t *file_db;
asl_memory_t *memory_db;
asl_mini_memory_t *mini_db;
asl_mini_memory_t *disaster_db;
+
+ /* parameters below are configurable as command-line args or in /etc/asl.conf */
+ int asl_log_filter;
+ int debug;
+ char *debug_file;
+ int dbtype;
+ uint32_t db_file_max;
+ uint32_t db_memory_max;
+ uint32_t db_mini_max;
+ uint32_t mps_limit;
+ uint64_t bsd_max_dup_time;
+ uint64_t asl_store_ping_time;
+ uint64_t mark_time;
+ time_t utmp_ttl;
+ time_t fs_ttl;
};
extern struct global_s global;
+typedef asl_msg_t *(*aslreadfn)(int);
+typedef char *(*aslwritefn)(const char *, int);
+typedef char *(*aslexceptfn)(int);
+typedef int (*aslsendmsgfn)(asl_msg_t *msg, const char *outid);
+
+struct aslevent
+{
+ int source;
+ int fd;
+ unsigned char read:1;
+ unsigned char write:1;
+ unsigned char except:1;
+ aslreadfn readfn;
+ aslwritefn writefn;
+ aslexceptfn exceptfn;
+ char *sender;
+ uid_t uid;
+ gid_t gid;
+ TAILQ_ENTRY(aslevent) entries;
+};
+
struct module_list
{
char *name;
TAILQ_ENTRY(module_list) entries;
};
+void config_debug(int enable, const char *path);
+void config_data_store(int type, uint32_t file_max, uint32_t memory_max, uint32_t mini_max);
+void config_timers(uint64_t bsd_max_dup, uint64_t asl_store_ping, uint64_t utmp, uint64_t fs);
+
+char **explode(const char *s, const char *delim);
+void freeList(char **l);
+
int aslevent_init(void);
int aslevent_fdsets(fd_set *, fd_set *, fd_set *);
void aslevent_handleevent(fd_set *, fd_set *, fd_set *);
void asl_mark(void);
void asl_archive(void);
+void asl_client_count_increment();
+void asl_client_count_decrement();
+
char *get_line_from_file(FILE *f);
int asldebug(const char *, ...);
int asl_msg_cmp(asl_msg_t *a, asl_msg_t *b);
time_t asl_parse_time(const char *str);
-typedef asl_msg_t *(*aslreadfn)(int);
-typedef char *(*aslwritefn)(const char *, int);
-typedef char *(*aslexceptfn)(int);
-typedef int (*aslsendmsgfn)(asl_msg_t *msg, const char *outid);
-
-int aslevent_addfd(int fd, uint32_t flags, aslreadfn, aslwritefn, aslexceptfn);
+int aslevent_addfd(int source, int fd, uint32_t flags, aslreadfn readfn, aslwritefn writefn, aslexceptfn exceptfn);
int aslevent_removefd(int fd);
int aslevent_addmatch(asl_msg_t *query, char *outid);
+int asl_check_option(asl_msg_t *msg, const char *opt);
+
int aslevent_addoutput(aslsendmsgfn, const char *outid);
+void asl_enqueue_message(uint32_t source, struct aslevent *e, asl_msg_t *msg);
+asl_msg_t **asl_work_dequeue(uint32_t *count);
+void asl_message_match_and_log(asl_msg_t *msg);
+
int asl_syslog_faciliy_name_to_num(const char *fac);
const char *asl_syslog_faciliy_num_to_name(int num);
asl_msg_t *asl_input_parse(const char *in, int len, char *rhost, int flag);
-void db_ping_store(time_t now);
+void db_ping_store(void);
/* message refcount utilities */
uint32_t asl_msg_type(asl_msg_t *m);
/*
- * Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+ * Copyright (c) 2007-2009 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
#include <sys/errno.h>
#include <mach/mach.h>
#include <mach/mach_error.h>
+#include <bsm/libbsm.h>
#include <errno.h>
#include <netinet/in.h>
#include <sys/event.h>
#include <sys/time.h>
#include <asl.h>
#include <asl_ipc.h>
+#include <asl_ipc_server.h>
#include <asl_core.h>
#include "daemon.h"
#define SEARCH_BACKWARD -1
static pthread_mutex_t db_lock = PTHREAD_MUTEX_INITIALIZER;
-static pthread_mutex_t queue_lock = PTHREAD_MUTEX_INITIALIZER;
-static pthread_cond_t queue_cond = PTHREAD_COND_INITIALIZER;
extern char *asl_list_to_string(asl_search_result_t *list, uint32_t *outlen);
extern asl_search_result_t *asl_list_from_string(const char *buf);
extern boolean_t asl_ipc_server(mach_msg_header_t *InHeadP, mach_msg_header_t *OutHeadP);
+extern uint32_t bb_convert(const char *name);
-static asl_search_result_t work_queue = {0, 0, NULL};
-
-static time_t file_sweep_last = 0;
+static task_name_t *client_tasks = NULL;
+static uint32_t client_tasks_count = 0;
typedef union
{
} asl_reply_msg;
void
-list_append_msg(asl_search_result_t *list, asl_msg_t *msg, uint32_t retain)
-{
- if (list == NULL) return;
- if (msg == NULL) return;
-
- /*
- * NB: curr is the list size
- * grow list if necessary
- */
- if (list->count == list->curr)
- {
- if (list->curr == 0)
- {
- list->msg = (asl_msg_t **)calloc(LIST_SIZE_DELTA, sizeof(asl_msg_t *));
- }
- else
- {
- list->msg = (asl_msg_t **)reallocf(list->msg, (list->curr + LIST_SIZE_DELTA) * sizeof(asl_msg_t *));
- }
-
- if (list->msg == NULL)
- {
- list->curr = 0;
- list->count = 0;
- return;
- }
-
- list->curr += LIST_SIZE_DELTA;
- }
-
- if (retain != 0) asl_msg_retain(msg);
- list->msg[list->count] = msg;
- list->count++;
-}
-
-void
-db_ping_store(time_t now)
+db_ping_store(void)
{
- time_t delta;
-
if ((global.dbtype & DB_TYPE_FILE) && (global.file_db != NULL))
{
- delta = now - file_sweep_last;
- if (delta >= global.asl_store_ping_time)
- {
- asl_store_sweep_file_cache(global.file_db);
- file_sweep_last = now;
- }
- }
-}
-
-void
-db_enqueue(asl_msg_t *m)
-{
- if (m == NULL) return;
-
- pthread_mutex_lock(&queue_lock);
- list_append_msg(&work_queue, m, 1);
- pthread_mutex_unlock(&queue_lock);
- pthread_cond_signal(&queue_cond);
-}
-
-asl_msg_t **
-db_dequeue(uint32_t *count)
-{
- asl_msg_t **work;
-
- pthread_mutex_lock(&queue_lock);
- pthread_cond_wait(&queue_cond, &queue_lock);
-
- work = NULL;
- *count = 0;
+ global.store_flags |= STORE_FLAGS_FILE_CACHE_SWEEP_REQUESTED;
- if (work_queue.count == 0)
- {
- pthread_mutex_unlock(&queue_lock);
- return NULL;
+ /* wake up the output worker thread */
+ pthread_cond_signal(&global.work_queue_cond);
}
-
- work = work_queue.msg;
- *count = work_queue.count;
-
- work_queue.count = 0;
- work_queue.curr = 0;
- work_queue.msg = NULL;
-
- pthread_mutex_unlock(&queue_lock);
- return work;
}
void
{
asldebug("error: can't create data store %s: %s\n", PATH_ASL_STORE, strerror(errno));
return;
- }
+ }
}
else
{
}
}
+ /*
+ * One-time store conversion from the old "LongTTL" style to the new "Best Before" style.
+ * bb_convert returns quickly if the store has already been converted.
+ */
+ status = bb_convert(PATH_ASL_STORE);
+ if (status != ASL_STATUS_OK)
+ {
+ asldebug("ASL data store conversion failed!: %s\n", asl_core_error(status));
+ }
+
status = asl_store_open_write(NULL, &(global.file_db));
if (status != ASL_STATUS_OK)
{
else
{
if (global.db_file_max != 0) asl_store_max_file_size(global.file_db, global.db_file_max);
- }
-
- if (global.did_store_sweep == 0)
- {
- status = asl_store_signal_sweep(global.file_db);
- if (status == ASL_STATUS_OK) global.did_store_sweep = 1;
+ asl_store_signal_sweep(global.file_db);
}
}
}
/*
- * Takes messages off the work queue and saves them in the database.
+ * Takes messages off the work queue and saves them.
* Runs in it's own thread.
*/
void
-db_worker()
+output_worker()
{
asl_msg_t **work;
- uint64_t msgid;
- uint32_t i, count, status;
- mach_msg_empty_send_t *msg;
+ uint32_t i, count;
+ mach_msg_empty_send_t *empty;
kern_return_t kstatus;
- msg = (mach_msg_empty_send_t *)calloc(1, sizeof(mach_msg_empty_send_t));
- if (msg == NULL) return;
-
- msg->header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSGH_BITS_ZERO);
- msg->header.msgh_remote_port = global.server_port;
- msg->header.msgh_local_port = MACH_PORT_NULL;
- msg->header.msgh_size = sizeof(mach_msg_empty_send_t);
- msg->header.msgh_id = SEND_NOTIFICATION;
+ empty = (mach_msg_empty_send_t *)calloc(1, sizeof(mach_msg_empty_send_t));
+ if (empty == NULL) return;
forever
{
count = 0;
- work = db_dequeue(&count);
- if (work == NULL) continue;
+ /* blocks until work is available */
+ work = asl_work_dequeue(&count);
- pthread_mutex_lock(&db_lock);
+ if (work == NULL)
+ {
+ if ((global.dbtype & DB_TYPE_FILE) && (global.store_flags & STORE_FLAGS_FILE_CACHE_SWEEP_REQUESTED))
+ {
+ asl_store_sweep_file_cache(global.file_db);
+ global.store_flags &= ~STORE_FLAGS_FILE_CACHE_SWEEP_REQUESTED;
+ }
- db_asl_open();
+ continue;
+ }
for (i = 0; i < count; i++)
{
- if (global.dbtype & DB_TYPE_FILE)
- {
- status = asl_store_save(global.file_db, work[i]);
- if (status != ASL_STATUS_OK)
- {
- /* write failed - reopen & retry */
- asldebug("asl_store_save: %s\n", asl_core_error(status));
- asl_store_close(global.file_db);
- global.file_db = NULL;
+ asl_message_match_and_log(work[i]);
+ asl_msg_release(work[i]);
+ }
- db_asl_open();
- status = asl_store_save(global.file_db, work[i]);
- if (status != ASL_STATUS_OK)
- {
- asldebug("(retry) asl_store_save: %s\n", asl_core_error(status));
- asl_store_close(global.file_db);
- global.file_db = NULL;
-
- global.dbtype |= DB_TYPE_MEMORY;
- if (global.memory_db == NULL)
- {
- status = asl_memory_open(global.db_memory_max, &(global.memory_db));
- if (status != ASL_STATUS_OK)
- {
- asldebug("asl_memory_open: %s\n", asl_core_error(status));
- }
- }
- }
- }
- }
+ free(work);
+
+ if (global.store_flags & STORE_FLAGS_FILE_CACHE_SWEEP_REQUESTED)
+ {
+ asl_store_sweep_file_cache(global.file_db);
+ global.store_flags &= ~STORE_FLAGS_FILE_CACHE_SWEEP_REQUESTED;
+ }
+
+ empty->header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSGH_BITS_ZERO);
+ empty->header.msgh_remote_port = global.self_port;
+ empty->header.msgh_local_port = MACH_PORT_NULL;
+ empty->header.msgh_size = sizeof(mach_msg_empty_send_t);
+ empty->header.msgh_id = SEND_NOTIFICATION;
+
+ kstatus = mach_msg(&(empty->header), MACH_SEND_MSG | MACH_SEND_TIMEOUT, empty->header.msgh_size, 0, MACH_PORT_NULL, 2, MACH_PORT_NULL);
+ }
+}
+
+/*
+ * Called from asl_action.c to save messgaes to the ASL data store
+ */
+void
+db_save_message(asl_msg_t *msg)
+{
+ uint64_t msgid;
+ uint32_t status;
+
+ pthread_mutex_lock(&db_lock);
+
+ db_asl_open();
- if (global.dbtype & DB_TYPE_MEMORY)
+ if (global.dbtype & DB_TYPE_FILE)
+ {
+ status = asl_store_save(global.file_db, msg);
+ if (status != ASL_STATUS_OK)
+ {
+ /* write failed - reopen & retry */
+ asldebug("asl_store_save: %s\n", asl_core_error(status));
+ asl_store_close(global.file_db);
+ global.file_db = NULL;
+
+ db_asl_open();
+ status = asl_store_save(global.file_db, msg);
+ if (status != ASL_STATUS_OK)
{
- msgid = 0;
- status = asl_memory_save(global.memory_db, work[i], &msgid);
- if (status != ASL_STATUS_OK)
+ asldebug("(retry) asl_store_save: %s\n", asl_core_error(status));
+ asl_store_close(global.file_db);
+ global.file_db = NULL;
+
+ global.dbtype |= DB_TYPE_MEMORY;
+ if (global.memory_db == NULL)
{
- /* save failed - reopen & retry*/
- asldebug("asl_memory_save: %s\n", asl_core_error(status));
- asl_memory_close(global.memory_db);
- global.memory_db = NULL;
-
- db_asl_open();
- msgid = 0;
- status = asl_memory_save(global.memory_db, work[i], &msgid);
+ status = asl_memory_open(global.db_memory_max, &(global.memory_db));
if (status != ASL_STATUS_OK)
{
- asldebug("(retry) asl_memory_save: %s\n", asl_core_error(status));
- asl_memory_close(global.memory_db);
- global.memory_db = NULL;
+ asldebug("asl_memory_open: %s\n", asl_core_error(status));
}
}
}
+ }
+ }
- if (global.dbtype & DB_TYPE_MINI)
+ if (global.dbtype & DB_TYPE_MEMORY)
+ {
+ msgid = 0;
+ status = asl_memory_save(global.memory_db, msg, &msgid);
+ if (status != ASL_STATUS_OK)
+ {
+ /* save failed - reopen & retry*/
+ asldebug("asl_memory_save: %s\n", asl_core_error(status));
+ asl_memory_close(global.memory_db);
+ global.memory_db = NULL;
+
+ db_asl_open();
+ msgid = 0;
+ status = asl_memory_save(global.memory_db, msg, &msgid);
+ if (status != ASL_STATUS_OK)
{
- status = asl_mini_memory_save(global.mini_db, work[i], &msgid);
- if (status != ASL_STATUS_OK)
- {
- /* save failed - reopen & retry*/
- asldebug("asl_mini_memory_save: %s\n", asl_core_error(status));
- asl_mini_memory_close(global.mini_db);
- global.mini_db = NULL;
-
- db_asl_open();
- status = asl_mini_memory_save(global.mini_db, work[i], &msgid);
- if (status != ASL_STATUS_OK)
- {
- asldebug("(retry) asl_memory_save: %s\n", asl_core_error(status));
- asl_mini_memory_close(global.mini_db);
- global.mini_db = NULL;
- }
- }
+ asldebug("(retry) asl_memory_save: %s\n", asl_core_error(status));
+ asl_memory_close(global.memory_db);
+ global.memory_db = NULL;
}
+ }
+ }
- if ((i % 500) == 499)
+ if (global.dbtype & DB_TYPE_MINI)
+ {
+ status = asl_mini_memory_save(global.mini_db, msg, &msgid);
+ if (status != ASL_STATUS_OK)
+ {
+ /* save failed - reopen & retry*/
+ asldebug("asl_mini_memory_save: %s\n", asl_core_error(status));
+ asl_mini_memory_close(global.mini_db);
+ global.mini_db = NULL;
+
+ db_asl_open();
+ status = asl_mini_memory_save(global.mini_db, msg, &msgid);
+ if (status != ASL_STATUS_OK)
{
- pthread_mutex_unlock(&db_lock);
- pthread_mutex_lock(&db_lock);
+ asldebug("(retry) asl_memory_save: %s\n", asl_core_error(status));
+ asl_mini_memory_close(global.mini_db);
+ global.mini_db = NULL;
}
}
+ }
- pthread_mutex_unlock(&db_lock);
- for (i = 0; i < count; i++) asl_msg_release(work[i]);
- free(work);
+ pthread_mutex_unlock(&db_lock);
- kstatus = mach_msg(&(msg->header), MACH_SEND_MSG, msg->header.msgh_size, 0, MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
- }
}
void
return status;
}
+static void
+register_session(task_name_t task_name, pid_t pid)
+{
+ mach_port_t previous;
+ uint32_t i;
+
+ if (task_name == MACH_PORT_NULL) return;
+ if (global.dead_session_port == MACH_PORT_NULL) return;
+
+ for (i = 0; i < client_tasks_count; i++) if (task_name == client_tasks[i]) return;
+
+ if (client_tasks_count == 0) client_tasks = (task_name_t *)calloc(1, sizeof(task_name_t));
+ else client_tasks = (task_name_t *)reallocf(client_tasks, (client_tasks_count + 1) * sizeof(task_name_t));
+
+ if (client_tasks == NULL) return;
+ client_tasks[client_tasks_count] = task_name;
+ client_tasks_count++;
+
+ asldebug("register_session: %u PID %d\n", (unsigned int)task_name, (int)pid);
+
+ /* register for port death notification */
+ mach_port_request_notification(mach_task_self(), task_name, MACH_NOTIFY_DEAD_NAME, 0, global.dead_session_port, MACH_MSG_TYPE_MAKE_SEND_ONCE, &previous);
+ mach_port_deallocate(mach_task_self(), previous);
+
+ asl_client_count_increment();
+}
+
+static void
+cancel_session(task_name_t task_name)
+{
+ uint32_t i;
+
+ for (i = 0; (i < client_tasks_count) && (task_name != client_tasks[i]); i++);
+
+ if (i >= client_tasks_count) return;
+
+ if (client_tasks_count == 1)
+ {
+ free(client_tasks);
+ client_tasks = NULL;
+ client_tasks_count = 0;
+ }
+ else
+ {
+ for (i++; i < client_tasks_count; i++) client_tasks[i-1] = client_tasks[i];
+ client_tasks_count--;
+ client_tasks = (task_name_t *)reallocf(client_tasks, client_tasks_count * sizeof(task_name_t));
+ }
+
+ asldebug("cancel_session: %u\n", (unsigned int)task_name);
+ mach_port_destroy(mach_task_self(), task_name);
+ asl_client_count_decrement();
+}
+
/*
* Receives messages on the "com.apple.system.logger" mach port.
* Services database search requests.
uint32_t rbits, sbits;
uint32_t flags, snooze;
struct timeval now, send_time;
+ mach_dead_name_notification_t *deadname;
send_time.tv_sec = 0;
send_time.tv_usec = 0;
reply = (asl_reply_msg *)calloc(1, rps);
if (reply == NULL) return;
- rbits = MACH_RCV_MSG | MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_SENDER) | MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0);
+ rbits = MACH_RCV_MSG | MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AUDIT) | MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0);
sbits = MACH_SEND_MSG | MACH_SEND_TIMEOUT;
forever
flags = rbits;
if (snooze != 0) flags |= MACH_RCV_TIMEOUT;
- kstatus = mach_msg(&(request->head), flags, 0, rqs, global.server_port, snooze, MACH_PORT_NULL);
+ kstatus = mach_msg(&(request->head), flags, 0, rqs, global.listen_set, snooze, MACH_PORT_NULL);
if (request->head.msgh_id == SEND_NOTIFICATION)
{
if (send_time.tv_sec == 0)
continue;
}
+ if (request->head.msgh_id == MACH_NOTIFY_DEAD_NAME)
+ {
+ deadname = (mach_dead_name_notification_t *)request;
+ cancel_session(deadname->not_port);
+ free(request);
+ continue;
+ }
+
kstatus = asl_ipc_server(&(request->head), &(reply->head));
kstatus = mach_msg(&(reply->head), sbits, reply->head.msgh_size, 0, MACH_PORT_NULL, 10, MACH_PORT_NULL);
if (kstatus == MACH_SEND_INVALID_DEST)
kern_return_t
__asl_server_query
(
- mach_port_t server,
- caddr_t request,
- mach_msg_type_number_t requestCnt,
- uint64_t startid,
- int count,
- int flags,
- caddr_t *reply,
- mach_msg_type_number_t *replyCnt,
- uint64_t *lastid,
- int *status,
- security_token_t *token
+ mach_port_t server,
+ caddr_t request,
+ mach_msg_type_number_t requestCnt,
+ uint64_t startid,
+ int count,
+ int flags,
+ caddr_t *reply,
+ mach_msg_type_number_t *replyCnt,
+ uint64_t *lastid,
+ int *status,
+ security_token_t *token
)
{
aslresponse query;
kern_return_t
__asl_server_query_timeout
(
- mach_port_t server,
- caddr_t request,
- mach_msg_type_number_t requestCnt,
- uint64_t startid,
- int count,
- int flags,
- caddr_t *reply,
- mach_msg_type_number_t *replyCnt,
- uint64_t *lastid,
- int *status,
- security_token_t *token
+ mach_port_t server,
+ caddr_t request,
+ mach_msg_type_number_t requestCnt,
+ uint64_t startid,
+ int count,
+ int flags,
+ caddr_t *reply,
+ mach_msg_type_number_t *replyCnt,
+ uint64_t *lastid,
+ int *status,
+ security_token_t *token
)
{
return __asl_server_query(server, request, requestCnt, startid, count, flags, reply, replyCnt, lastid, status, token);
{
return KERN_SUCCESS;
}
+
+kern_return_t
+__asl_server_message
+(
+ mach_port_t server,
+ caddr_t message,
+ mach_msg_type_number_t messageCnt,
+ audit_token_t token
+)
+{
+ asl_msg_t *m;
+ char tmp[64];
+ uid_t uid;
+ gid_t gid;
+ pid_t pid;
+ kern_return_t kstatus;
+ mach_port_name_t client;
+
+ asldebug("__asl_server_message: %s\n", (message == NULL) ? "NULL" : message);
+
+ m = asl_msg_from_string(message);
+ vm_deallocate(mach_task_self(), (vm_address_t)message, messageCnt);
+
+ uid = (uid_t)-1;
+ gid = (gid_t)-1;
+ pid = (gid_t)-1;
+ audit_token_to_au32(token, NULL, &uid, &gid, NULL, NULL, &pid, NULL, NULL);
+
+ client = MACH_PORT_NULL;
+ kstatus = task_name_for_pid(mach_task_self(), pid, &client);
+ if (kstatus == KERN_SUCCESS) register_session(client, pid);
+
+ if (m == NULL) return KERN_SUCCESS;
+
+ snprintf(tmp, sizeof(tmp), "%d", uid);
+ asl_set(m, ASL_KEY_UID, tmp);
+
+ snprintf(tmp, sizeof(tmp), "%d", gid);
+ asl_set(m, ASL_KEY_GID, tmp);
+
+ snprintf(tmp, sizeof(tmp), "%d", pid);
+ asl_set(m, ASL_KEY_PID, tmp);
+
+ /* verify and enqueue for processing */
+ asl_enqueue_message(SOURCE_ASL_MESSAGE, NULL, m);
+
+ return KERN_SUCCESS;
+}
return -1;
}
- return aslevent_addfd(global.kfd, ADDFD_FLAGS_LOCAL, klog_in_acceptmsg, NULL, NULL);
+ return aslevent_addfd(SOURCE_KERN, global.kfd, ADDFD_FLAGS_LOCAL, klog_in_acceptmsg, NULL, NULL);
}
int
chmod(SYSLOG_SOCK_PATH, 0666);
- aslevent_addfd(fd, 0, remote_acceptmsg_local, NULL, NULL);
+ aslevent_addfd(SOURCE_SESSION, fd, 0, remote_acceptmsg_local, NULL, NULL);
return fd;
}
return -1;
}
- aslevent_addfd(fd, 0, remote_acceptmsg_tcp, NULL, NULL);
+ aslevent_addfd(SOURCE_SESSION, fd, 0, remote_acceptmsg_tcp, NULL, NULL);
return fd;
}
.Pp
The
.Em facility
-describes the part of the system generating the message, and is one of
-the following keywords: auth, authpriv, cron, daemon, kern, lpr, mail,
+describes the part of the system generating the message.
+Legacy facility names are: auth, authpriv, cron, daemon, kern, lpr, mail,
mark, news, syslog, user, uucp and local0 through local7.
These keywords (with the exception of mark) correspond to the
similar
.Xr syslog 3
library routines.
.Pp
+Apple System Log messages, sent using the
+.Xr asl 3
+library routines permit the facility name to be an arbitrary string,
+although users of the system are encouraged to use a
+.Dq reverse ICANN
+naming convention, for example
+.Dq com.apple.system.syslog .
+Since these facility names may contain dot characters, the names may be enclosed in
+either singe quote or double quote characters.
+.Pp
The
.Em level
describes the severity of the message, and is a keyword from the
For example ``mail.crit,*.err'' will select ``mail'' facility messages at
the level of ``err'' or higher, not at the level of ``crit'' or higher.
.Sh SEE ALSO
+.Xr asl 3 ,
.Xr syslog 3 ,
.Xr syslogd 8
.Sh HISTORY
.Op Fl db_max Ar size
.Op Fl utmp_ttl Ar time
.Op Fl fs_ttl Ar time
+.Op Fl mps_limit Ar quota
.Op Fl dup_delay Ar time
.Op Fl module_name Li {0|1}
.Sh DESCRIPTION
Those files will be phased out in future versions of Mac OS.
.Pp
The following options are recognized:
-.Bl -tag -width "-utmp_ttl"
+.Bl -tag -width "-dup_delay"
.It Fl d
Run
.Nm
.Xr asl 3
header files.
Received messages with a priority or level value greater than the cutoff will not be saved in the data store.
-The default filter will retain messages in the range 0 (Emergency) to 5 (Notice) inclusive.
+The default filter value is set to allow all message priorities.
+Message filtering is primarily specified by the rules in the /etc/asl.conf file.
+However, if there are no matching rules for the ASL data store in the asl.conf file,
+then all messages that are allowed by the cutoff filter are saved.
.Pp
Note that a this filter value may be adjusted while
.Nm
manual.
The filter may be adjusted using the
.Dq -c
-option, e.g.
-.Pp
-.Li sudo syslog -c syslogd -d
-.Pp
-will set the filter to retain messages in the range 0 (Emergency) to 7 (Debug).
+option.
.It Fl l
Specifies an alternate path for loading plug-in modules.
By default,
.Nm
checks for plug-in modules in the directory /usr/lib/asl.
.It Fl db_max
-Sets the size limit in bytes for files in the data store.
+Sets the size limit in bytes for individual files in the data store.
The default value for
.Fl db_max
is 25600000 bytes.
.Fl utmp_ttl ,
if archival is enabled, these messages will be copied to an archive after the regular time-to-live
interval but will persist in the data store until their own expiry time.
+.It Fl mps_limit
+Sets the per-process quota for messages per second allowed by
+.Nm .
+Any messages in excess of the quota limit from any process are ignored.
+An error message is logged on behalf of the limited process, stating that its message quota has
+been exceeded, and that remaining messages for the current second will be discarded.
+The default limit is 500 messages per second per process.
+A value of 0 turns off the quota mechanism.
.It Fl dup_delay
Sets the time to delay for coalescing duplicate message in log files.
If a process logs multiple messages with the same text,
/*
- * Copyright (c) 2004-2008 Apple Inc. All rights reserved.
+ * Copyright (c) 2004-2009 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
#define DEFAULT_UTMP_TTL_SEC 31622400
#define DEFAULT_FS_TTL_SEC 31622400
#define DEFAULT_BSD_MAX_DUP_SEC 30
+#define DEFAULT_MPS_LIMIT 500
#define BILLION 1000000000
#define NOTIFY_DELAY 1
#define streq(A,B) (strcmp(A,B)==0)
#define forever for(;;)
-static int reset = 0;
static uint64_t time_start = 0;
static uint64_t mark_last = 0;
-static uint64_t mark_time = 0;
+static uint64_t ping_last = 0;
static uint64_t time_last = 0;
extern int __notify_78945668_info__;
static int activate_udp_in = 1;
extern void database_server();
-extern void db_worker();
+extern void output_worker();
extern void launchd_drain();
extern void bsd_flush_duplicates(time_t now);
+extern void bsd_close_idle_files(time_t now);
/*
* Module approach: only one type of module. This module may implement
static void
catch_sighup(int x)
{
- reset = 1;
+ global.reset = RESET_CONFIG;
+}
+
+static void
+catch_siginfo(int x)
+{
+ global.reset = RESET_NETWORK;
}
static void
static void
timed_events(struct timeval **run)
{
- uint64_t now, delta, t;
+ time_t now, delta, t;
static struct timeval next;
now = time(NULL);
time_start = now;
time_last = now;
mark_last = now;
+ ping_last = now;
}
/*
*/
time_last = now;
mark_last = now;
+ ping_last = now;
}
/*
if (global.bsd_flush_time > 0)
{
bsd_flush_duplicates(now);
+ bsd_close_idle_files(now);
if (global.bsd_flush_time > 0)
{
if (next.tv_sec == 0) next.tv_sec = global.bsd_flush_time;
}
/*
- * Tickle asl_store
+ * Tickle asl_store to sweep file cache
*/
if (global.asl_store_ping_time > 0)
{
- db_ping_store(now);
- if (global.asl_store_ping_time > 0)
+ delta = now - ping_last;
+ if (delta >= global.asl_store_ping_time)
{
- if (next.tv_sec == 0) next.tv_sec = global.asl_store_ping_time;
- else if (global.asl_store_ping_time < next.tv_sec) next.tv_sec = global.asl_store_ping_time;
+ db_ping_store();
+ bsd_close_idle_files(now);
+ ping_last = now;
+ t = global.asl_store_ping_time;
}
+ else
+ {
+ t = global.asl_store_ping_time - delta;
+ }
+
+ if (next.tv_sec == 0) next.tv_sec = t;
+ else if (t < next.tv_sec) next.tv_sec = t;
}
/*
* Send MARK
*/
- if (mark_time > 0)
+ if (global.mark_time > 0)
{
delta = now - mark_last;
- if (delta >= mark_time)
+ if (delta >= global.mark_time)
{
asl_mark();
mark_last = now;
- t = mark_time;
+ t = global.mark_time;
}
else
{
- t = mark_time - delta;
+ t = global.mark_time - delta;
}
if (next.tv_sec == 0) next.tv_sec = t;
global.server_port = launch_data_get_machport(pdict);
- status = mach_port_insert_right(mach_task_self(), global.server_port, global.server_port, MACH_MSG_TYPE_MAKE_SEND);
- if (status != KERN_SUCCESS) fprintf(stderr, "Warning! Can't make send right for server_port: %x\n", status);
+ /* port for receiving internal messages */
+ status = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &(global.self_port));
+ if (status != KERN_SUCCESS)
+ {
+ fprintf(stderr, "mach_port_allocate self_port failed: %d", status);
+ exit(1);
+ }
+
+ status = mach_port_insert_right(mach_task_self(), global.self_port, global.self_port, MACH_MSG_TYPE_MAKE_SEND);
+ if (status != KERN_SUCCESS)
+ {
+ fprintf(stderr, "Can't make send right for self_port: %d\n", status);
+ exit(1);
+ }
+
+ /* port for receiving MACH_NOTIFY_DEAD_NAME notifications */
+ status = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &(global.dead_session_port));
+ if (status != KERN_SUCCESS)
+ {
+ fprintf(stderr, "mach_port_allocate dead_session_port failed: %d", status);
+ exit(1);
+ }
+
+ status = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_PORT_SET, &(global.listen_set));
+ if (status != KERN_SUCCESS)
+ {
+ fprintf(stderr, "mach_port_allocate listen_set failed: %d", status);
+ exit(1);
+ }
+
+ status = mach_port_move_member(mach_task_self(), global.server_port, global.listen_set);
+ if (status != KERN_SUCCESS)
+ {
+ fprintf(stderr, "mach_port_move_member server_port failed: %d", status);
+ exit(1);
+ }
+
+ status = mach_port_move_member(mach_task_self(), global.self_port, global.listen_set);
+ if (status != KERN_SUCCESS)
+ {
+ fprintf(stderr, "mach_port_move_member self_port failed: %d", status);
+ exit(1);
+ }
+
+ status = mach_port_move_member(mach_task_self(), global.dead_session_port, global.listen_set);
+ if (status != KERN_SUCCESS)
+ {
+ fprintf(stderr, "mach_port_move_member dead_session_port failed (%u)", status);
+ exit(1);
+ }
+}
+
+void
+config_debug(int enable, const char *path)
+{
+ OSSpinLockLock(&global.lock);
+
+ global.debug = enable;
+ if (global.debug_file != NULL) free(global.debug_file);
+ global.debug_file = strdup(path);
+
+ OSSpinLockUnlock(&global.lock);
+}
+
+void
+config_data_store(int type, uint32_t file_max, uint32_t memory_max, uint32_t mini_max)
+{
+ pthread_mutex_lock(global.db_lock);
+
+ if (global.dbtype & DB_TYPE_FILE)
+ {
+ asl_store_close(global.file_db);
+ global.file_db = NULL;
+ }
+
+ if (global.dbtype & DB_TYPE_MEMORY)
+ {
+ asl_memory_close(global.memory_db);
+ global.memory_db = NULL;
+ }
+
+ if (global.dbtype & DB_TYPE_MINI)
+ {
+ asl_mini_memory_close(global.mini_db);
+ global.mini_db = NULL;
+ }
+
+ global.dbtype = type;
+ global.db_file_max = file_max;
+ global.db_memory_max = memory_max;
+ global.db_mini_max = mini_max;
+
+ pthread_mutex_unlock(global.db_lock);
}
int
struct timeval *runloop_timer, zto;
pthread_attr_t attr;
pthread_t t;
- int nctoken;
+ int network_change_token;
char tstr[32];
time_t now;
memset(&global, 0, sizeof(struct global_s));
- global.asl_log_filter = ASL_FILTER_MASK_UPTO(ASL_LEVEL_NOTICE);
+ global.db_lock = (pthread_mutex_t *)calloc(1, sizeof(pthread_mutex_t));
+ pthread_mutex_init(global.db_lock, NULL);
+
+ global.work_queue_lock = (pthread_mutex_t *)calloc(1, sizeof(pthread_mutex_t));
+ pthread_mutex_init(global.work_queue_lock, NULL);
+
+ pthread_cond_init(&global.work_queue_cond, NULL);
+
+ global.work_queue = (asl_search_result_t *)calloc(1, sizeof(asl_search_result_t));
+
+ global.asl_log_filter = ASL_FILTER_MASK_UPTO(ASL_LEVEL_DEBUG);
global.db_file_max = 16384000;
global.db_memory_max = 8192;
global.db_mini_max = 256;
global.bsd_max_dup_time = DEFAULT_BSD_MAX_DUP_SEC;
global.utmp_ttl = DEFAULT_UTMP_TTL_SEC;
global.fs_ttl = DEFAULT_FS_TTL_SEC;
+ global.mps_limit = DEFAULT_MPS_LIMIT;
global.kfd = -1;
#ifdef CONFIG_MAC
global.db_file_max = 25600000;
global.asl_store_ping_time = 150;
#endif
-
+
#ifdef CONFIG_APPLETV
global.dbtype = DB_TYPE_FILE;
global.db_file_max = 10240000;
+ global.asl_store_ping_time = 150;
#endif
#ifdef CONFIG_IPHONE
activate_remote = 1;
activate_bsd_out = 0;
#endif
-
+
mp = _PATH_MODULE_LIB;
daemonize = 0;
- __notify_78945668_info__ = -1;
+ __notify_78945668_info__ = 0xf0000000;
zto.tv_sec = 0;
zto.tv_usec = 0;
- FD_ZERO(&kern);
/* prevent malloc from calling ASL on error */
_malloc_no_asl_log = 1;
if (streq(argv[i], "-d"))
{
global.debug = 1;
- if (((i+1) < argc) && (argv[i+1][0] != '-')) global.debug_file = argv[++i];
+ if (((i+1) < argc) && (argv[i+1][0] != '-')) global.debug_file = strdup(argv[++i]);
memset(tstr, 0, sizeof(tstr));
now = time(NULL);
ctime_r(&now, tstr);
}
else if (streq(argv[i], "-m"))
{
- if ((i + 1) < argc) mark_time = 60 * atoll(argv[++i]);
+ if ((i + 1) < argc) global.mark_time = 60 * atoll(argv[++i]);
}
else if (streq(argv[i], "-utmp_ttl"))
{
{
if ((i + 1) < argc) global.fs_ttl = atol(argv[++i]);
}
+ else if (streq(argv[i], "-mps_limit"))
+ {
+ if ((i + 1) < argc) global.mps_limit = atol(argv[++i]);
+ }
else if (streq(argv[i], "-l"))
{
if ((i + 1) < argc) mp = argv[++i];
}
}
- if (global.dbtype == 0) global.dbtype = DB_TYPE_FILE;
+ if (global.dbtype == 0)
+ {
+ global.dbtype = DB_TYPE_FILE;
+ global.db_file_max = 25600000;
+ global.asl_store_ping_time = 150;
+ }
TAILQ_INIT(&Moduleq);
static_modules();
init_config();
signal(SIGHUP, catch_sighup);
+ signal(SIGINFO, catch_siginfo);
- nctoken = -1;
- notify_register_signal(NETWORK_CHANGE_NOTIFICATION, SIGHUP, &nctoken);
+ /* register for network change notifications if the udp_in module is active */
+ network_change_token = -1;
+ if (activate_udp_in != 0) notify_register_signal(NETWORK_CHANGE_NOTIFICATION, SIGINFO, &network_change_token);
for (mod = Moduleq.tqh_first; mod != NULL; mod = mod->entries.tqe_next)
{
pthread_attr_destroy(&attr);
/*
- * Start database worker thread
+ * Start output worker thread
*/
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
- pthread_create(&t, &attr, (void *(*)(void *))db_worker, NULL);
+ pthread_create(&t, &attr, (void *(*)(void *))output_worker, NULL);
pthread_attr_destroy(&attr);
FD_ZERO(&rd);
*/
if (global.kfd >= 0)
{
+ FD_ZERO(&kern);
+ FD_SET(global.kfd, &kern);
max = global.kfd + 1;
while (select(max, &kern, NULL, NULL, &zto) > 0)
{
if ((global.kfd >= 0) && FD_ISSET(global.kfd, &rd))
{
/* drain /dev/klog */
+ FD_ZERO(&kern);
+ FD_SET(global.kfd, &kern);
max = global.kfd + 1;
while (select(max, &kern, NULL, NULL, &zto) > 0)
}
}
- if (reset != 0)
+ if (global.reset != RESET_NONE)
{
send_reset();
- reset = 0;
+ global.reset = RESET_NONE;
}
if (status != 0) aslevent_handleevent(&rd, &wr, &ex);
if (nsock > 0) return 0;
if (global.launch_dict == NULL)
{
- asldebug("%s: laucnchd dict is NULL\n", MY_ID);
+ asldebug("%s: launchd dict is NULL\n", MY_ID);
return -1;
}
sockets_dict = launch_data_dict_lookup(global.launch_dict, LAUNCH_JOBKEY_SOCKETS);
if (sockets_dict == NULL)
{
- asldebug("%s: laucnchd lookup of LAUNCH_JOBKEY_SOCKETS failed\n", MY_ID);
+ asldebug("%s: launchd lookup of LAUNCH_JOBKEY_SOCKETS failed\n", MY_ID);
return -1;
}
fd_array = launch_data_dict_lookup(sockets_dict, UDP_SOCKET_NAME);
if (fd_array == NULL)
{
- asldebug("%s: laucnchd lookup of UDP_SOCKET_NAME failed\n", MY_ID);
+ asldebug("%s: launchd lookup of UDP_SOCKET_NAME failed\n", MY_ID);
return -1;
}
nsock = launch_data_array_get_count(fd_array);
if (nsock <= 0)
{
- asldebug("%s: laucnchd fd array is empty\n", MY_ID);
+ asldebug("%s: launchd fd array is empty\n", MY_ID);
return -1;
}
fd_dict = launch_data_array_get_index(fd_array, i);
if (fd_dict == NULL)
{
- asldebug("%s: laucnchd file discriptor array element 0 is NULL\n", MY_ID);
+ asldebug("%s: launchd file discriptor array element 0 is NULL\n", MY_ID);
return -1;
}
}
}
- for (i = 0; i < nsock; i++) if (ufd[i] != -1) aslevent_addfd(ufd[i], 0, udp_in_acceptmsg, NULL, NULL);
+ for (i = 0; i < nsock; i++) if (ufd[i] != -1) aslevent_addfd(SOURCE_UDP_SOCKET, ufd[i], 0, udp_in_acceptmsg, NULL, NULL);
+
return 0;
}
endif
include $(MAKEFILEPATH)/CoreOS/ReleaseControl/BSDCommon.make
+
+after_install:
+ codesign -s- $(DSTROOT)/usr/bin/syslog
If the
.Fl l
option is omitted, the log level defaults to 7 (Debug).
-Note that the log message server
-.Nm syslogd
-filters messages based on log level.
-The default filter for the ASL database excludes Debug and Info level messages.
.Pp
.Nm
only requires one or two leading characters for a level specification.
.Pp
.Dl syslog -k Sender portmap -o -k Level Nle 4
.Pp
+Log priority levels are internally handled as an integer value between 0 and 7.
+Level values in expressions may either be given as integers, or as string equivalents.
+See the table string values in the SENDING MESSAGES section for details.
+The example query above could also be specified with the command:
+.Pp
+.Dl syslog -k Sender portmap -o -k Level Nle warning
+.Pp
+.Pp
A special convention exists for matching time stamps.
An unsigned integer value is regarded as the given number of seconds since
0 hours, 0 minutes, 0 seconds, January 1, 1970, Coordinated Universal Time.
An negative integer value is regarded as the given number of seconds before the current time.
-For example, to find all messages of priority level 3 (error) or less which were logged in the last 30 seconds:
+For example, to find all messages of Error priority level (3) or less which were logged in the last 30 seconds:
.Pp
-.Dl syslog -k Level Nle 3 -k Time ge -30
+.Dl syslog -k Level Nle error -k Time ge -30
.Pp
a relative time value may be optionally followed by one of the characters
.Dq s ,
The filtering described above takes place in the client library to determine which messages are sent to the
.Nm syslogd
daemon.
-The daemon also contains a filter which determines which messages are saved in the data store.
-Note that this additionally determines which messages are seen when reading messages using the
+The daemon also contains filters which determines which messages are saved in the data store.
+This determines which messages are seen when reading messages using the
.Nm
-utility.
+utility, or when viewing data store messages in the Console utility application.
.Pp
-The default data store filter mask saves messages with priority levels from Emergency to Notice (level 0 to 5).
+The default data store filter mask permits all messages with priority levels from Emergency to Debug (level 0 to 7).
The level may be inspected using:
.Pp
.Dl syslog -c syslogd
For example, to save messages with priority level Error or less in the data store:
.Pp
.Dl syslog -c syslog -e
+.Pp
+The
+.Nm syslogd
+server also follows filtering rules specified in the /etc/asl.conf file.
+When the remote-control mechanism is used to change the filter of a process,
+.Nm syslogd
+will save any messages received from that process until the remote-control filter is turned off.
+It is no longer necessary to adjust the filtering for both a process and for
+.Nm syslogd
+to have messages saved in the ASL data store.
.Sh SEE ALSO
.Xr syslogd 8 ,
.Xr logger 1 ,
/*
- * Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+ * Copyright (c) 2007-2009 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
#define FACILITY_CONSOLE "com.apple.console"
+/* Shared with Libc */
+#define NOTIFY_RC "com.apple.asl.remote"
+
#define SEARCH_EOF -1
#define SEARCH_NULL 0
#define SEARCH_MATCH 1
}
int
-rcontrol_get_string(const char *prefix, int pid, int *val)
+rcontrol_get_string(const char *name, int *val)
{
int t, status;
- char *name;
uint64_t x;
- status = NOTIFY_STATUS_OK;
-
- if (pid == RC_SYSLOGD)
- {
- status = notify_register_plain(NOTIFY_SYSTEM_ASL_FILTER, &t);
- }
- else if (pid == RC_MASTER)
- {
- status = notify_register_plain(NOTIFY_SYSTEM_MASTER, &t);
- }
- else
- {
- name = NULL;
- asprintf(&name, "%s.%d", prefix, pid);
- if (name == NULL) return NOTIFY_STATUS_FAILED;
-
- status = notify_register_plain(name, &t);
- free(name);
- }
-
+ status = notify_register_plain(name, &t);
if (status != NOTIFY_STATUS_OK) return status;
x = 0;
}
int
-rcontrol_set_string(const char *prefix, int pid, int filter)
+rcontrol_set_string(const char *name, int filter)
{
int t, status;
- char *name;
uint64_t x;
- status = NOTIFY_STATUS_OK;
-
- if (pid == RC_SYSLOGD)
- {
- status = notify_register_plain(NOTIFY_SYSTEM_ASL_FILTER, &t);
- }
- else if (pid == RC_MASTER)
- {
- status = notify_register_plain(NOTIFY_SYSTEM_MASTER, &t);
- }
- else
- {
- name = NULL;
- asprintf(&name, "%s.%d", prefix, pid);
- if (name == NULL) return NOTIFY_STATUS_FAILED;
-
- status = notify_register_plain(name, &t);
- free(name);
- }
-
+ status = notify_register_plain(name, &t);
if (status != NOTIFY_STATUS_OK) return status;
x = filter;
status = notify_set_state(t, x);
- if ((pid == RC_SYSLOGD) && (status == NOTIFY_STATUS_OK)) status = notify_post(NOTIFY_SYSTEM_ASL_FILTER);
+ notify_post(NOTIFY_RC);
notify_cancel(t);
return status;
}
return str;
}
+const char *
+rcontrol_name(pid_t pid, uid_t uid)
+{
+ static char str[1024];
+
+ if (pid == RC_SYSLOGD) return NOTIFY_SYSTEM_ASL_FILTER;
+ if (pid == RC_MASTER) return NOTIFY_SYSTEM_MASTER;
+
+ memset(str, 0, sizeof(str));
+ if (uid == 0) snprintf(str, sizeof(str) - 1, "%s.%d", NOTIFY_PREFIX_SYSTEM, pid);
+ else snprintf(str, sizeof(str) - 1, "user.uid.%d.syslog.%d", uid, pid);
+ return str;
+}
+
int
-rcontrol_get(const char *prefix, int pid)
+rcontrol_get(pid_t pid, uid_t uid)
{
int filter, status;
const char *name;
name = "Master";
if (pid == RC_SYSLOGD) name = "ASL Data Store";
- status = rcontrol_get_string(NULL, pid, &filter);
+ status = rcontrol_get_string(rcontrol_name(pid, uid), &filter);
if (status == NOTIFY_STATUS_OK)
{
printf("%s filter mask: %s\n", name, asl_filter_string(filter));
return -1;
}
- status = rcontrol_get_string(prefix, pid, &filter);
+ status = rcontrol_get_string(rcontrol_name(pid, uid), &filter);
if (status == NOTIFY_STATUS_OK)
{
printf("Process %d syslog filter mask: %s\n", pid, asl_filter_string(filter));
}
int
-rcontrol_set(const char *prefix, int pid, int filter)
+rcontrol_set(pid_t pid, uid_t uid, int filter)
{
int status;
const char *name;
{
name = "Master";
if (pid == RC_SYSLOGD) name = "ASL Data Store";
- status = rcontrol_set_string(NULL, pid, filter);
+ status = rcontrol_set_string(rcontrol_name(pid, uid), filter);
if (status == NOTIFY_STATUS_OK)
{
return -1;
}
- status = rcontrol_set_string(prefix, pid, filter);
+ status = rcontrol_set_string(rcontrol_name(pid, uid), filter);
if (status == NOTIFY_STATUS_OK)
{
+ if (pid == RC_SYSLOGD) status = notify_post(NOTIFY_SYSTEM_ASL_FILTER);
printf("Set process %d syslog filter mask set: %s\n", pid, asl_filter_string(filter));
return 0;
}
return -1;
}
+const char *
+asl_string_to_char_level(const char *s)
+{
+ if (s == NULL) return NULL;
+
+ if ((s[0] >= '0') && (s[0] <= '7') && (s[1] == '\0')) return s;
+
+ if (!strncasecmp(s, "em", 2)) return "0";
+ else if (!strncasecmp(s, "p", 1)) return "0";
+ else if (!strncasecmp(s, "a", 1)) return "1";
+ else if (!strncasecmp(s, "c", 1)) return "2";
+ else if (!strncasecmp(s, "er", 2)) return "3";
+ else if (!strncasecmp(s, "x", 1)) return "3";
+ else if (!strncasecmp(s, "w", 1)) return "4";
+ else if (!strncasecmp(s, "n", 1)) return "5";
+ else if (!strncasecmp(s, "i", 1)) return "6";
+ else if (!strncasecmp(s, "d", 1)) return "7";
+
+ return NULL;
+}
+
int
syslog_remote_control(int argc, char *argv[])
{
int pid, uid, status, mask;
- const char *prefix;
if ((argc < 3) || (argc > 4))
{
if (pid == 0) pid = RC_MASTER;
- prefix = NOTIFY_PREFIX_USER;
- if (uid == 0) prefix = NOTIFY_PREFIX_SYSTEM;
-
if (argc == 4)
{
if ((pid == RC_MASTER) && (!strcasecmp(argv[3], "off"))) mask = 0;
}
}
- rcontrol_set(prefix, pid, mask);
+ rcontrol_set(pid, uid, mask);
}
else
{
- rcontrol_get(prefix, pid);
+ rcontrol_get(pid, uid);
}
return 0;
if (rhost == NULL)
{
filter = 0;
- status = rcontrol_get_string(NULL, RC_SYSLOGD, &filter);
+ status = rcontrol_get_string(rcontrol_name(RC_SYSLOGD, 0), &filter);
if (status != 0)
{
fprintf(stderr, "Warning: Can't get current syslogd ASL filter value\n");
add_op(asl_msg_t *q, char *key, char *op, char *val, uint32_t flags)
{
uint32_t o;
+ const char *qval;
if (key == NULL) return -1;
if (q == NULL) return -1;
+ qval = NULL;
+ if (strcmp(key, ASL_KEY_TIME) == 0)
+ {
+ qval = (const char *)val;
+ }
+ else if ((strcmp(key, ASL_KEY_LEVEL) == 0) && (_isanumber(val) == 0))
+ {
+ /* Convert level strings to numeric values */
+ qval = asl_string_to_char_level(val);
+ if (qval == NULL)
+ {
+ fprintf(stderr, "invalid value for \"Level\"key: %s\n", val);
+ return -1;
+ }
+ }
+
o = ASL_QUERY_OP_NULL;
+ if (val == NULL) o = ASL_QUERY_OP_TRUE;
+
if (op != NULL)
{
o = optype(op);
return -1;
}
- if ((o & ASL_QUERY_OP_NUMERIC) && (strcmp(key, ASL_KEY_TIME) != 0) && (_isanumber(val) == 0))
+ if ((qval == NULL) && (o & ASL_QUERY_OP_NUMERIC) && (_isanumber(val) == 0))
{
fprintf(stderr, "non-numeric value supplied for numeric operator %s %s %s\n", key, op, val);
return -1;
}
-
}
o |= flags;
- asl_set_query(q, key, val, o);
+ if (qval != NULL) asl_set_query(q, key, qval, o);
+ else asl_set_query(q, key, val, o);
return 0;
}
}
/* output should be line buffered */
- setlinebuf(outfile);
+ if (outfile != NULL) setlinebuf(outfile);
search_once(outfile, pfmt, pflags, qlist, qmin, &cmax, 0, batch, 1, tail_count);