From c4fdb7d114b21b1b97b3777dfb8a8053693e7a91 Mon Sep 17 00:00:00 2001 From: Apple Date: Fri, 10 Jul 2009 01:28:27 +0000 Subject: [PATCH] syslog-97.1.tar.gz --- aslcommon/asl_ipc.defs | 7 + aslcommon/asl_memory.c | 12 +- aslcommon/asl_mini_memory.c | 12 +- aslmanager.tproj/Makefile | 3 + aslmanager.tproj/aslmanager.8 | 39 +- aslmanager.tproj/aslmanager.c | 581 ++++++++---- aslmanager.tproj/com.apple.aslmanager.plist | 2 - syslogd.tproj/Makefile | 7 +- syslogd.tproj/asl.conf.5 | 207 ++++- syslogd.tproj/asl_action.c | 950 ++++++++++++++++++-- syslogd.tproj/asl_in.c | 121 +-- syslogd.tproj/bb_convert.c | 531 +++++++++++ syslogd.tproj/bsd_in.c | 14 +- syslogd.tproj/bsd_out.c | 263 +++--- syslogd.tproj/com.apple.syslogd.plist | 6 +- syslogd.tproj/daemon.c | 726 +++++++++++---- syslogd.tproj/daemon.h | 110 ++- syslogd.tproj/dbserver.c | 475 +++++----- syslogd.tproj/klog_in.c | 2 +- syslogd.tproj/remote.c | 4 +- syslogd.tproj/syslog.conf.5 | 15 +- syslogd.tproj/syslogd.8 | 24 +- syslogd.tproj/syslogd.c | 200 ++++- syslogd.tproj/udp_in.c | 13 +- util.tproj/Makefile | 3 + util.tproj/syslog.1 | 34 +- util.tproj/syslog.c | 140 +-- 27 files changed, 3417 insertions(+), 1084 deletions(-) create mode 100644 syslogd.tproj/bb_convert.c diff --git a/aslcommon/asl_ipc.defs b/aslcommon/asl_ipc.defs index 2d1d246..1fe5bce 100644 --- a/aslcommon/asl_ipc.defs +++ b/aslcommon/asl_ipc.defs @@ -66,3 +66,10 @@ routine _asl_server_prune 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 +); diff --git a/aslcommon/asl_memory.c b/aslcommon/asl_memory.c index 5964cf0..66d8703 100644 --- a/aslcommon/asl_memory.c +++ b/aslcommon/asl_memory.c @@ -1372,7 +1372,17 @@ asl_memory_match(asl_memory_t *s, aslresponse query, aslresponse *res, uint64_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; diff --git a/aslcommon/asl_mini_memory.c b/aslcommon/asl_mini_memory.c index 1f45aec..08ba36e 100644 --- a/aslcommon/asl_mini_memory.c +++ b/aslcommon/asl_mini_memory.c @@ -1051,7 +1051,17 @@ asl_mini_memory_match(asl_mini_memory_t *s, aslresponse query, aslresponse *res, } } - 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; diff --git a/aslmanager.tproj/Makefile b/aslmanager.tproj/Makefile index 50d5e41..197b0f7 100644 --- a/aslmanager.tproj/Makefile +++ b/aslmanager.tproj/Makefile @@ -10,3 +10,6 @@ Extra_CC_Flags = -Wall -mdynamic-no-pic -DINET6 Extra_LD_Flags = -dead_strip include $(MAKEFILEPATH)/CoreOS/ReleaseControl/BSDCommon.make + +after_install: + codesign -s- $(DSTROOT)/usr/sbin/aslmanager diff --git a/aslmanager.tproj/aslmanager.8 b/aslmanager.tproj/aslmanager.8 index 0fb9cfb..4bb4ad3 100644 --- a/aslmanager.tproj/aslmanager.8 +++ b/aslmanager.tproj/aslmanager.8 @@ -1,4 +1,4 @@ -.\"Copyright (c) 2004-2008 Apple Inc. All rights reserved. +.\"Copyright (c) 2004-2009 Apple Inc. All rights reserved. .\" .\"@APPLE_LICENSE_HEADER_START@ .\" @@ -41,31 +41,40 @@ server. 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 @@ -76,12 +85,16 @@ flag is specified with no argument, files are copied to the /var/log/asl.archive 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. diff --git a/aslmanager.tproj/aslmanager.c b/aslmanager.tproj/aslmanager.c index f5b8a6f..747a1b9 100644 --- a/aslmanager.tproj/aslmanager.c +++ b/aslmanager.tproj/aslmanager.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007-2008 Apple Inc. All rights reserved. + * Copyright (c) 2007-2009 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -38,10 +38,19 @@ #include #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 { @@ -50,25 +59,6 @@ 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) { @@ -121,117 +111,302 @@ free_list(name_list_t *l) } 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; @@ -241,12 +416,12 @@ main(int argc, const char *argv[]) { 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")) { @@ -262,6 +437,10 @@ main(int argc, const char *argv[]) } } + _parse_config_file(_PATH_ASL_CONF); + + if (debug == 1) printf("aslmanager starting\n"); + /* check archive */ if (archive != NULL) { @@ -284,7 +463,7 @@ main(int argc, const char *argv[]) { fprintf(stderr, "aslmanager error: can't create archive %s: %s\n", archive, strerror(errno)); return -1; - } + } } else { @@ -297,93 +476,124 @@ main(int argc, const char *argv[]) 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; @@ -391,61 +601,80 @@ main(int argc, const char *argv[]) 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; } diff --git a/aslmanager.tproj/com.apple.aslmanager.plist b/aslmanager.tproj/com.apple.aslmanager.plist index 88571e6..a44d2dc 100644 --- a/aslmanager.tproj/com.apple.aslmanager.plist +++ b/aslmanager.tproj/com.apple.aslmanager.plist @@ -7,8 +7,6 @@ ProgramArguments /usr/sbin/aslmanager - -size - 65536000 WatchPaths diff --git a/syslogd.tproj/Makefile b/syslogd.tproj/Makefile index bfb2043..2aa8a32 100644 --- a/syslogd.tproj/Makefile +++ b/syslogd.tproj/Makefile @@ -2,7 +2,7 @@ Project = syslogd 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 @@ -26,7 +26,7 @@ ifeq ($(PRODUCT),iPhone) 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 @@ -34,3 +34,6 @@ after_install: $(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 diff --git a/syslogd.tproj/asl.conf.5 b/syslogd.tproj/asl.conf.5 index 8ac5e56..e9ac022 100644 --- a/syslogd.tproj/asl.conf.5 +++ b/syslogd.tproj/asl.conf.5 @@ -1,4 +1,4 @@ -.\"Copyright (c) 2004-2008 Apple Inc. All rights reserved. +.\"Copyright (c) 2004-2009 Apple Inc. All rights reserved. .\" .\"@APPLE_LICENSE_HEADER_START@ .\" @@ -19,29 +19,100 @@ .\" .\"@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 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 @@ -83,59 +154,143 @@ suffix 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 . diff --git a/syslogd.tproj/asl_action.c b/syslogd.tproj/asl_action.c index 0462c16..e800e65 100644 --- a/syslogd.tproj/asl_action.c +++ b/syslogd.tproj/asl_action.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004-2008 Apple Inc. All rights reserved. + * Copyright (c) 2004-2009 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -34,42 +34,150 @@ #include #include #include +#include +#include #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 */ @@ -80,7 +188,7 @@ _find_action(char *s) p = s; if (p == NULL) return NULL; - if (*p != 'Q') return NULL; + if ((*p != 'Q') && (*p != '?') && (*p != '*')) return NULL; p++; @@ -110,30 +218,29 @@ _find_action(char *s) } 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) { @@ -141,7 +248,6 @@ _parse_line(char *s) if (out->options == NULL) { - free(out->action); free(out); return -1; } @@ -150,95 +256,291 @@ _parse_line(char *s) 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; @@ -263,35 +565,475 @@ _act_access_control(struct action_rule *r, asl_msg_t *msg) } } +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: 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; @@ -315,40 +1057,62 @@ asl_action_init(void) { 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; } diff --git a/syslogd.tproj/asl_in.c b/syslogd.tproj/asl_in.c index d62be08..1bd0883 100644 --- a/syslogd.tproj/asl_in.c +++ b/syslogd.tproj/asl_in.c @@ -37,19 +37,9 @@ #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) @@ -67,6 +57,8 @@ asl_in_getmsg(int fd) { if (n == 0) { + asl_client_count_decrement(); + close(fd); aslevent_removefd(fd); return NULL; @@ -77,6 +69,8 @@ asl_in_getmsg(int fd) 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; @@ -100,6 +94,8 @@ asl_in_getmsg(int fd) 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); @@ -132,15 +128,15 @@ asl_in_getmsg(int fd) } 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; } @@ -152,78 +148,10 @@ asl_in_acceptmsg(int fd) 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 @@ -237,40 +165,40 @@ asl_in_init(void) 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; } @@ -295,11 +223,7 @@ asl_in_init(void) 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 @@ -313,11 +237,6 @@ asl_in_close(void) { 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); diff --git a/syslogd.tproj/bb_convert.c b/syslogd.tproj/bb_convert.c new file mode 100644 index 0000000..f247dc9 --- /dev/null +++ b/syslogd.tproj/bb_convert.c @@ -0,0 +1,531 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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(); +} diff --git a/syslogd.tproj/bsd_in.c b/syslogd.tproj/bsd_in.c index 9b8a0a0..eafca64 100644 --- a/syslogd.tproj/bsd_in.c +++ b/syslogd.tproj/bsd_in.c @@ -70,40 +70,40 @@ bsd_in_init(void) 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; } @@ -128,7 +128,7 @@ bsd_in_init(void) 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 diff --git a/syslogd.tproj/bsd_out.c b/syslogd.tproj/bsd_out.c index 6e7bd2a..f4f2a55 100644 --- a/syslogd.tproj/bsd_out.c +++ b/syslogd.tproj/bsd_out.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include "daemon.h" @@ -43,7 +44,6 @@ #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 @@ -52,8 +52,11 @@ #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 { @@ -76,101 +79,32 @@ static TAILQ_HEAD(cr, config_rule) bsd_out_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 @@ -225,7 +159,7 @@ _syslog_dst_open(struct config_rule *r) if (r->dst[0] == '!') { r->type = DST_TYPE_NOTE; - r->fd = 0; + r->fd = -1; return 0; } @@ -255,7 +189,7 @@ _syslog_dst_open(struct config_rule *r) 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); @@ -268,6 +202,8 @@ _syslog_dst_open(struct config_rule *r) if (r->fd < 0) { asldebug("%s: connection failed for %s\n", MY_ID, (r->dst) + 1); + free(r->addr); + r->addr = NULL; return -1; } @@ -276,6 +212,8 @@ _syslog_dst_open(struct config_rule *r) 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; } @@ -287,7 +225,7 @@ _syslog_dst_open(struct config_rule *r) if (strcmp(r->dst, "*") == 0) { r->type = DST_TYPE_WALL; - r->fd = 0; + r->fd = -1; return 0; } @@ -301,6 +239,12 @@ _syslog_dst_close(struct config_rule *r) { if (r == NULL) return; + if (r->addr != NULL) + { + free(r->addr); + r->addr = NULL; + } + switch (r->type) { case DST_TYPE_FILE: @@ -315,8 +259,6 @@ _syslog_dst_close(struct config_rule *r) { if (r->fd >= 0) close(r->fd); r->fd = -1; - if (r->addr != NULL) free(r->addr); - r->addr = NULL; break; } @@ -331,6 +273,32 @@ _syslog_dst_close(struct config_rule *r) } } +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) { @@ -342,7 +310,7 @@ _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)); @@ -364,7 +332,7 @@ _parse_line(char *s) 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++) { @@ -392,17 +360,17 @@ _parse_line(char *s) 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); @@ -470,7 +438,7 @@ bsd_log_string(const char *msg) 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; @@ -521,7 +489,7 @@ _syslog_send_repeat_msg(struct config_rule *r) 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; @@ -540,50 +508,23 @@ _syslog_send(asl_msg_t *msg, struct config_rule *r, char **out, char **fwd, time } 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"; @@ -604,7 +545,7 @@ _syslog_send(asl_msg_t *msg, struct config_rule *r, char **out, char **fwd, time n = 0; /* Time + " " */ - if (vt != NULL) n += (strlen(vt) + 1); + n += (strlen(vt) + 1); /* Host + " " */ if (vhost != NULL) n += (strlen(vhost) + 1); @@ -641,11 +582,8 @@ _syslog_send(asl_msg_t *msg, struct config_rule *r, char **out, char **fwd, time 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) { @@ -690,7 +628,6 @@ _syslog_send(asl_msg_t *msg, struct config_rule *r, char **out, char **fwd, time strcat(so, "\n"); - free(vt); *out = so; } @@ -784,12 +721,10 @@ _syslog_send(asl_msg_t *msg, struct config_rule *r, char **out, char **fwd, time return -1; } - fprintf(pw, *out); + fprintf(pw, "%s", *out); pclose(pw); } - _syslog_dst_close(r); - if (is_dup == 1) { r->last_count++; @@ -865,11 +800,7 @@ bsd_out_sendmsg(asl_msg_t *msg, const char *outid) time_t tick; uint64_t delta; - if (reset != 0) - { - _do_reset(); - reset = 0; - } + if (reset != RESET_NONE) _do_reset(); if (msg == NULL) return -1; @@ -900,6 +831,28 @@ bsd_out_sendmsg(asl_msg_t *msg, const char *outid) 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) { @@ -925,7 +878,6 @@ bsd_flush_duplicates(time_t now) { _syslog_dst_open(r); _syslog_send_repeat_msg(r); - _syslog_dst_close(r); r->last_count = 0; } @@ -971,7 +923,7 @@ bsd_out_init(void) int bsd_out_reset(void) { - reset = 1; + reset = global.reset; return 0; } @@ -1006,3 +958,20 @@ bsd_out_close(void) 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; +} diff --git a/syslogd.tproj/com.apple.syslogd.plist b/syslogd.tproj/com.apple.syslogd.plist index 4b1854d..7ddd7f6 100644 --- a/syslogd.tproj/com.apple.syslogd.plist +++ b/syslogd.tproj/com.apple.syslogd.plist @@ -6,7 +6,11 @@ com.apple.syslogd OnDemand - ProgramArguments + HopefullyExitsLast + + EnableTransactions + + ProgramArguments aslevent_handleevent\n"); @@ -593,25 +1027,7 @@ aslevent_handleevent(fd_set *rd, fd_set *wr, fd_set *ex) 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) @@ -631,29 +1047,13 @@ int 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; } @@ -665,17 +1065,28 @@ asldebug(const char *str, ...) 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; } @@ -962,27 +1373,28 @@ asl_msg_type(asl_msg_t *m) 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; } @@ -991,8 +1403,6 @@ launchd_callback(struct timeval *when, pid_t from_pid, pid_t about_pid, uid_t se { asl_msg_t *m; char str[256]; - int32_t unused; - int status; time_t now; /* @@ -1000,12 +1410,6 @@ launchd_callback(struct timeval *when, pid_t from_pid, pid_t about_pid, uid_t se 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; @@ -1038,6 +1442,14 @@ launchd_callback(struct timeval *when, pid_t from_pid, pid_t about_pid, uid_t se /* 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) { @@ -1086,23 +1498,15 @@ launchd_callback(struct timeval *when, pid_t from_pid, pid_t about_pid, uid_t se 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); } } diff --git a/syslogd.tproj/daemon.h b/syslogd.tproj/daemon.h index 73a5732..f4fce9e 100644 --- a/syslogd.tproj/daemon.h +++ b/syslogd.tproj/daemon.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004-2008 Apple Inc. All rights reserved. + * Copyright (c) 2004-2009 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -35,6 +35,7 @@ #include #include #include +#include #define ADDFD_FLAGS_LOCAL 0x00000001 @@ -44,11 +45,14 @@ #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" @@ -63,35 +67,86 @@ #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; @@ -102,12 +157,22 @@ struct module_list 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 *, ...); @@ -118,22 +183,23 @@ asl_msg_t *asl_msg_from_string(const char *buf); 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); diff --git a/syslogd.tproj/dbserver.c b/syslogd.tproj/dbserver.c index e1329b9..5bce571 100644 --- a/syslogd.tproj/dbserver.c +++ b/syslogd.tproj/dbserver.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007-2008 Apple Inc. All rights reserved. + * Copyright (c) 2007-2009 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include @@ -44,6 +45,7 @@ #include #include #include +#include #include #include "daemon.h" @@ -58,16 +60,14 @@ #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 { @@ -82,94 +82,15 @@ 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 @@ -199,7 +120,7 @@ db_asl_open() { asldebug("error: can't create data store %s: %s\n", PATH_ASL_STORE, strerror(errno)); return; - } + } } else { @@ -209,6 +130,16 @@ db_asl_open() } } + /* + * 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) { @@ -217,12 +148,7 @@ db_asl_open() 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); } } @@ -246,129 +172,153 @@ db_asl_open() } /* - * 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 @@ -415,6 +365,60 @@ db_query(aslresponse query, aslresponse *res, uint64_t startid, int count, int f 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. @@ -430,6 +434,7 @@ database_server() 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; @@ -439,7 +444,7 @@ database_server() 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 @@ -478,7 +483,7 @@ database_server() 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) @@ -491,6 +496,14 @@ database_server() 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) @@ -505,17 +518,17 @@ database_server() 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; @@ -565,17 +578,17 @@ __asl_server_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); @@ -593,3 +606,51 @@ __asl_server_prune { 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; +} diff --git a/syslogd.tproj/klog_in.c b/syslogd.tproj/klog_in.c index 92addd5..cb734f6 100644 --- a/syslogd.tproj/klog_in.c +++ b/syslogd.tproj/klog_in.c @@ -86,7 +86,7 @@ klog_in_init(void) 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 diff --git a/syslogd.tproj/remote.c b/syslogd.tproj/remote.c index 520c6f6..7a97f55 100644 --- a/syslogd.tproj/remote.c +++ b/syslogd.tproj/remote.c @@ -783,7 +783,7 @@ remote_init_lockdown(void) 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; } @@ -853,7 +853,7 @@ remote_init_tcp(int family) return -1; } - aslevent_addfd(fd, 0, remote_acceptmsg_tcp, NULL, NULL); + aslevent_addfd(SOURCE_SESSION, fd, 0, remote_acceptmsg_tcp, NULL, NULL); return fd; } diff --git a/syslogd.tproj/syslog.conf.5 b/syslogd.tproj/syslog.conf.5 index e3b88b9..f11d3cd 100644 --- a/syslogd.tproj/syslog.conf.5 +++ b/syslogd.tproj/syslog.conf.5 @@ -76,8 +76,8 @@ are case insensitive. .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 @@ -88,6 +88,16 @@ and .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 @@ -221,6 +231,7 @@ The effects of multiple selectors are sometimes not intuitive. 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 diff --git a/syslogd.tproj/syslogd.8 b/syslogd.tproj/syslogd.8 index b428167..c2df7b8 100644 --- a/syslogd.tproj/syslogd.8 +++ b/syslogd.tproj/syslogd.8 @@ -35,6 +35,7 @@ .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 @@ -68,7 +69,7 @@ found in various locations on the system. 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 @@ -106,7 +107,10 @@ and .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 @@ -118,18 +122,14 @@ See the 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. @@ -153,6 +153,14 @@ As in the case of .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, diff --git a/syslogd.tproj/syslogd.c b/syslogd.tproj/syslogd.c index 230655a..6d1baf8 100644 --- a/syslogd.tproj/syslogd.c +++ b/syslogd.tproj/syslogd.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004-2008 Apple Inc. All rights reserved. + * Copyright (c) 2004-2009 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -52,6 +52,7 @@ #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 @@ -61,10 +62,9 @@ #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__; @@ -112,9 +112,10 @@ int udp_in_close(); 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 @@ -372,7 +373,13 @@ detach(void) static void catch_sighup(int x) { - reset = 1; + global.reset = RESET_CONFIG; +} + +static void +catch_siginfo(int x) +{ + global.reset = RESET_NETWORK; } static void @@ -392,7 +399,7 @@ send_reset(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); @@ -407,6 +414,7 @@ timed_events(struct timeval **run) time_start = now; time_last = now; mark_last = now; + ping_last = now; } /* @@ -429,6 +437,7 @@ timed_events(struct timeval **run) */ time_last = now; mark_last = now; + ping_last = now; } /* @@ -437,6 +446,7 @@ timed_events(struct timeval **run) 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; @@ -445,33 +455,42 @@ timed_events(struct timeval **run) } /* - * 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; @@ -518,8 +537,99 @@ init_config() 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 @@ -532,19 +642,30 @@ main(int argc, const char *argv[]) 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 @@ -552,10 +673,11 @@ main(int argc, const char *argv[]) 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 @@ -563,13 +685,12 @@ main(int argc, const char *argv[]) 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; @@ -606,7 +727,7 @@ main(int argc, const char *argv[]) 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); @@ -641,7 +762,7 @@ main(int argc, const char *argv[]) } 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")) { @@ -651,6 +772,10 @@ main(int argc, const char *argv[]) { 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]; @@ -697,7 +822,12 @@ main(int argc, const char *argv[]) } } - 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(); @@ -720,9 +850,11 @@ main(int argc, const char *argv[]) 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) { @@ -739,11 +871,11 @@ main(int argc, const char *argv[]) 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); @@ -755,6 +887,8 @@ main(int argc, const char *argv[]) */ if (global.kfd >= 0) { + FD_ZERO(&kern); + FD_SET(global.kfd, &kern); max = global.kfd + 1; while (select(max, &kern, NULL, NULL, &zto) > 0) { @@ -781,6 +915,8 @@ main(int argc, const char *argv[]) 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) @@ -789,10 +925,10 @@ main(int argc, const char *argv[]) } } - if (reset != 0) + if (global.reset != RESET_NONE) { send_reset(); - reset = 0; + global.reset = RESET_NONE; } if (status != 0) aslevent_handleevent(&rd, &wr, &ex); diff --git a/syslogd.tproj/udp_in.c b/syslogd.tproj/udp_in.c index bc8b52e..85b865e 100644 --- a/syslogd.tproj/udp_in.c +++ b/syslogd.tproj/udp_in.c @@ -104,28 +104,28 @@ udp_in_init(void) 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; } @@ -134,7 +134,7 @@ udp_in_init(void) 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; } @@ -160,7 +160,8 @@ udp_in_init(void) } } - 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; } diff --git a/util.tproj/Makefile b/util.tproj/Makefile index b477cf0..895a116 100644 --- a/util.tproj/Makefile +++ b/util.tproj/Makefile @@ -17,3 +17,6 @@ Extra_CC_Flags += -DCONFIG_IPHONE endif include $(MAKEFILEPATH)/CoreOS/ReleaseControl/BSDCommon.make + +after_install: + codesign -s- $(DSTROOT)/usr/bin/syslog diff --git a/util.tproj/syslog.1 b/util.tproj/syslog.1 index e562162..4fca72a 100644 --- a/util.tproj/syslog.1 +++ b/util.tproj/syslog.1 @@ -135,10 +135,6 @@ is an alias for 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. @@ -508,13 +504,21 @@ or that have a numeric priority level of 4 or less: .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 , @@ -599,12 +603,12 @@ Root access is required to set the per-process filter mask for system (UID 0) pr 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 @@ -615,6 +619,16 @@ as described above. 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 , diff --git a/util.tproj/syslog.c b/util.tproj/syslog.c index ab2f865..656c6cb 100644 --- a/util.tproj/syslog.c +++ b/util.tproj/syslog.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007-2008 Apple Inc. All rights reserved. + * Copyright (c) 2007-2009 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -65,6 +65,9 @@ #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 @@ -296,32 +299,12 @@ procinfo(char *pname, int *pid, int *uid) } 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; @@ -334,37 +317,17 @@ rcontrol_get_string(const char *prefix, int pid, int *val) } 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; } @@ -517,8 +480,22 @@ asl_filter_string(int f) 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; @@ -530,7 +507,7 @@ rcontrol_get(const char *prefix, int pid) 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)); @@ -541,7 +518,7 @@ rcontrol_get(const char *prefix, int pid) 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)); @@ -553,7 +530,7 @@ rcontrol_get(const char *prefix, int pid) } 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; @@ -562,7 +539,7 @@ rcontrol_set(const char *prefix, int pid, int filter) { 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) { @@ -574,9 +551,10 @@ rcontrol_set(const char *prefix, int pid, int filter) 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; } @@ -732,11 +710,31 @@ asl_string_to_level(const char *s) 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)) { @@ -786,9 +784,6 @@ syslog_remote_control(int argc, char *argv[]) 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; @@ -803,11 +798,11 @@ syslog_remote_control(int argc, char *argv[]) } } - rcontrol_set(prefix, pid, mask); + rcontrol_set(pid, uid, mask); } else { - rcontrol_get(prefix, pid); + rcontrol_get(pid, uid); } return 0; @@ -890,7 +885,7 @@ syslog_send(int argc, char *argv[]) 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"); @@ -1251,11 +1246,30 @@ int 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); @@ -1266,16 +1280,16 @@ add_op(asl_msg_t *q, char *key, char *op, char *val, uint32_t flags) 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; } @@ -1720,7 +1734,7 @@ main(int argc, char *argv[]) } /* 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); -- 2.45.2