X-Git-Url: https://git.saurik.com/apple/syslog.git/blobdiff_plain/57b0aad2c678a02fcc3e8b5756d4a13d9061babf..5222c21d179b0b291b379bab771199072df3ea35:/aslmanager.tproj/aslmanager.c diff --git a/aslmanager.tproj/aslmanager.c b/aslmanager.tproj/aslmanager.c index 38420b5..ace8232 100644 --- a/aslmanager.tproj/aslmanager.c +++ b/aslmanager.tproj/aslmanager.c @@ -1,15 +1,15 @@ /* - * Copyright (c) 2007-2008 Apple Inc. All rights reserved. + * Copyright (c) 2007-2015 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ - * + * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. - * + * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, @@ -17,432 +17,467 @@ * 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 +#include #include +#include +#include -#define SECONDS_PER_DAY 86400 -#define DEFAULT_MAX_SIZE 51200000 -#define DEFAULT_TTL 2 +#include "asl_common.h" +#include "daemon.h" +#include "cache_delete.h" -typedef struct name_list_s -{ - char *name; - size_t size; - struct name_list_s *next; -} name_list_t; +/* global */ +bool dryrun; +uint32_t debug; +FILE *debugfp; +dispatch_queue_t work_queue; -void -mgr_exit(const char *store, int status) -{ - char *s; +static dispatch_queue_t server_queue; +static time_t module_ttl; +static xpc_connection_t listener; +static bool main_task_enqueued; +static bool initial_main_task = true; +static dispatch_source_t sig_term_src; - if (store == NULL) exit(status); +/* wait 5 minutes to run main task after being invoked by XPC */ +#define MAIN_TASK_INITIAL_DELAY 300 - s = NULL; - asprintf(&s, "%s/%s", store, FILE_ASL_STORE_SWEEP_SEMAPHORE); - if (s != NULL) - { - unlink(s); - free(s); - } - else exit(1); +/* + * Used to set config parameters. + * Line format "= name value" + */ +static void +_aslmanager_set_param(asl_out_dst_data_t *dst, char *s) +{ + char **l; + uint32_t count; - exit(status); -} + if (s == NULL) return; + if (s[0] == '\0') return; -name_list_t * -add_to_list(name_list_t *l, const char *name, size_t size) -{ - name_list_t *e, *x; + /* skip '=' and whitespace */ + if (*s == '=') s++; + while ((*s == ' ') || (*s == '\t')) s++; - if (name == NULL) return l; + l = explode(s, " \t"); + if (l == NULL) return; - e = (name_list_t *)calloc(1, sizeof(name_list_t)); - if (e == NULL) return NULL; + for (count = 0; l[count] != NULL; count++); - e->name = strdup(name); - if (e->name == NULL) + /* name is required */ + if (count == 0) { - free(e); - return NULL; + free_string_list(l); + return; } - e->size = size; - - /* list is sorted by name (i.e. primarily by timestamp) */ - if (l == NULL) return e; + /* value is required */ + if (count == 1) + { + free_string_list(l); + return; + } - if (strcmp(e->name, l->name) <= 0) + if (!strcasecmp(l[0], "aslmanager_debug")) + { + /* = debug level */ + set_debug(DEBUG_ASL, l[1]); + } + else if (!strcasecmp(l[0], "store_ttl")) { - e->next = l; - return e; + /* = store_ttl days */ + dst->ttl[LEVEL_ALL] = asl_core_str_to_time(l[1], SECONDS_PER_DAY); } + else if (!strcasecmp(l[0], "module_ttl")) + { + /* = module_ttl days */ + module_ttl = asl_core_str_to_time(l[1], SECONDS_PER_DAY); + } + else if (!strcasecmp(l[0], "max_store_size")) + { + /* = max_file_size bytes */ + dst->all_max = asl_core_str_to_size(l[1]); + } + else if (!strcasecmp(l[0], "archive")) + { + free(dst->rotate_dir); + dst->rotate_dir = NULL; - for (x = l; (x->next != NULL) && (strcmp(e->name, x->next->name) > 0) ; x = x->next); + /* = archive {0|1} path */ + if (!strcmp(l[1], "1")) + { + if (l[2] == NULL) dst->rotate_dir = strdup(PATH_ASL_ARCHIVE); + else dst->rotate_dir = strdup(l[2]); + } + } + else if (!strcasecmp(l[0], "store_path")) + { + /* = archive path */ + free(dst->path); + dst->path = strdup(l[1]); + } + else if (!strcasecmp(l[0], "archive_mode")) + { + dst->mode = strtol(l[1], NULL, 0); + if ((dst->mode == 0) && (errno == EINVAL)) dst->mode = 0400; + } - e->next = x->next; - x->next = e; - return l; + free_string_list(l); } -void -free_list(name_list_t *l) +int +cli_main(int argc, char *argv[]) { - name_list_t *e; - - while (l != NULL) + int i, work; + asl_out_module_t *mod, *m; + asl_out_rule_t *r; + asl_out_dst_data_t store, opts, *asl_store_dst = NULL; + const char *mname = NULL; + char *path = NULL; + bool quiet = false; + bool cache_delete = false; + bool cache_delete_query = false; + +#if !TARGET_OS_SIMULATOR + if (geteuid() != 0) { - e = l; - l = l->next; - free(e->name); - free(e); + if (argc == 0) debug = DEBUG_ASL; + else debug = DEBUG_STDERR; + + debug_log(ASL_LEVEL_ERR, "aslmanager must be run by root\n"); + exit(1); } +#endif - free(l); -} + module_ttl = DEFAULT_TTL; -uint32_t -do_match(const char *infile, const char *outfile, int do_ttl, time_t expire_time) -{ - asl_search_result_t q, *query, *res; - asl_msg_t *m, *qm[1]; - asl_file_t *in, *out; - 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); - if (status != ASL_STATUS_OK) return status; - - query = NULL; - q.count = 1; - q.curr = 0; - q.msg = qm; - qm[0] = NULL; - m = NULL; - - if (do_ttl == 1) + /* cobble up a dst_data with defaults and parameter settings */ + memset(&store, 0, sizeof(store)); + store.ttl[LEVEL_ALL] = DEFAULT_TTL; + store.all_max = DEFAULT_MAX_SIZE; + + memset(&opts, 0, sizeof(opts)); + opts.ttl[LEVEL_ALL] = DEFAULT_TTL; + opts.all_max = DEFAULT_MAX_SIZE; + + for (i = 1; i < argc; i++) { - query = &q; - m = asl_new(ASL_TYPE_QUERY); - if (m == NULL) + if (!strcmp(argv[i], "-q")) { - asl_file_close(in); - return ASL_STATUS_NO_MEMORY; + quiet = true; } - - qm[0] = m; - - if (expire_time != 0) + else if (!strcmp(argv[i], "-dd")) { - 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; - } + quiet = true; } - else + else if (!strcmp(argv[i], "-s")) { - if (asl_set_query(m, ASL_KEY_EXPIRE_TIME, NULL, ASL_QUERY_OP_TRUE) != 0) + if (((i + 1) < argc) && (argv[i + 1][0] != '-')) { - asl_file_close(in); - asl_free(m); - return ASL_STATUS_NO_MEMORY; + store.path = strdup(argv[++i]); + asl_store_dst = &store; } } } - 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); + if (!quiet) + { + int status = asl_make_database_dir(NULL, NULL); + if (status == 0) status = asl_make_database_dir(ASL_INTERNAL_LOGS_DIR, &path); + if (status == 0) + { + char tstamp[32], *str = NULL; - if (status != ASL_STATUS_OK) return status; + asl_make_timestamp(time(NULL), MODULE_NAME_STYLE_STAMP_LCL_B, tstamp, sizeof(tstamp)); + asprintf(&str, "%s/aslmanager.%s", path, tstamp); - /* - * 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) - { - aslresponse_free(res); - return ASL_STATUS_NOT_FOUND; + if (str != NULL) + { + if (status == 0) debugfp = fopen(str, "w"); + if (debugfp != NULL) debug |= DEBUG_FILE; + free(str); + } + } } - out = NULL; - status = asl_file_open_write(outfile, 0644, -1, -1, &out); - if (status != ASL_STATUS_OK) return status; - - out->flags = ASL_FILE_FLAG_UNLIMITED_CACHE | ASL_FILE_FLAG_PRESERVE_MSG_ID; + /* get parameters from asl.conf */ + mod = asl_out_module_init(); - for (i = 0; i < res->count; i++) + if (mod != NULL) { - mid = 0; - status = asl_file_save(out, res->msg[i], &mid); - if (status != ASL_STATUS_OK) break; - } + for (r = mod->ruleset; (r != NULL) && (asl_store_dst == NULL); r = r->next) + { + if ((r->dst != NULL) && (r->action == ACTION_OUT_DEST) && (!strcmp(r->dst->path, PATH_ASL_STORE))) + asl_store_dst = r->dst; + } - asl_file_close(out); - return status; -} + for (r = mod->ruleset; r != NULL; r = r->next) + { + if (r->action == ACTION_SET_PARAM) + { + if (r->query == NULL) _aslmanager_set_param(asl_store_dst, r->options); + } + } + } -int -main(int argc, const char *argv[]) -{ - int i, bbstrlen, debug; - const char *archive, *store_dir; - time_t now, best_before, ttl; - struct tm ctm; - char bbstr[32], *str, *p; - DIR *dp; - struct dirent *dent; - name_list_t *list, *e; - uint32_t status; - size_t file_size, store_size, max_size; - struct stat sb; - - list = NULL; - - archive = NULL; - store_dir = PATH_ASL_STORE; - ttl = DEFAULT_TTL * SECONDS_PER_DAY; - max_size = DEFAULT_MAX_SIZE; - store_size = 0; - debug = 0; + work = DO_ASLDB | DO_MODULE; for (i = 1; i < argc; i++) { if (!strcmp(argv[i], "-a")) { - if (((i + 1) < argc) && (argv[i + 1][0] != '-')) archive = argv[++i]; - else archive = PATH_ASL_ARCHIVE; + if (asl_store_dst == NULL) asl_store_dst = &store; + + if (((i + 1) < argc) && (argv[i + 1][0] != '-')) asl_store_dst->rotate_dir = strdup(argv[++i]); + else asl_store_dst->rotate_dir = strdup(PATH_ASL_ARCHIVE); + asl_store_dst->mode = 0400; } - else if (!strcmp(argv[i], "-s")) + else if (!strcmp(argv[i], "-store_ttl")) { - if (((i + 1) < argc) && (argv[i + 1][0] != '-')) store_dir = argv[++i]; + if (((i + 1) < argc) && (argv[i + 1][0] != '-')) + { + if (asl_store_dst == NULL) asl_store_dst = &store; + asl_store_dst->ttl[LEVEL_ALL] = asl_core_str_to_time(argv[++i], SECONDS_PER_DAY); + } + } + else if (!strcmp(argv[i], "-module_ttl")) + { + if (((i + 1) < argc) && (argv[i + 1][0] != '-')) module_ttl = asl_core_str_to_time(argv[++i], SECONDS_PER_DAY); } else if (!strcmp(argv[i], "-ttl")) { - if (((i + 1) < argc) && (argv[i + 1][0] != '-')) ttl = atoi(argv[++i]) * SECONDS_PER_DAY; + if (((i + 1) < argc) && (argv[i + 1][0] != '-')) + { + opts.ttl[LEVEL_ALL] = asl_core_str_to_time(argv[++i], SECONDS_PER_DAY); + + if (asl_store_dst == NULL) asl_store_dst = &store; + asl_store_dst->ttl[LEVEL_ALL] = opts.ttl[LEVEL_ALL]; + + module_ttl = opts.ttl[LEVEL_ALL]; + } } else if (!strcmp(argv[i], "-size")) { - if (((i + 1) < argc) && (argv[i + 1][0] != '-')) max_size = atoi(argv[++i]); + if (((i + 1) < argc) && (argv[i + 1][0] != '-')) + { + opts.all_max = asl_core_str_to_size(argv[++i]); + + if (asl_store_dst == NULL) asl_store_dst = &store; + asl_store_dst->all_max = opts.all_max; + } + } + else if (!strcmp(argv[i], "-checkpoint")) + { + work |= DO_CHECKPT; + } + else if (!strcmp(argv[i], "-cache_delete")) + { + cache_delete = true; + if (((i + 1) < argc) && (argv[i + 1][0] == 'q')) cache_delete_query = true; + } + else if (!strcmp(argv[i], "-module")) + { + work &= ~DO_ASLDB; + + /* optional name follows -module */ + if ((i +1) < argc) + { + if (argv[i + 1][0] != '-') mname = argv[++i]; + } + } + else if (!strcmp(argv[i], "-asldb")) + { + work = DO_ASLDB; } else if (!strcmp(argv[i], "-d")) { - debug = 1; + if (((i + i) < argc) && (argv[i+1][0] != '-')) set_debug(DEBUG_STDERR, argv[++i]); + else set_debug(DEBUG_STDERR, NULL); + } + else if (!strcmp(argv[i], "-dd")) + { + dryrun = true; + + if (((i + i) < argc) && (argv[i+1][0] != '-')) set_debug(DEBUG_STDERR, argv[++i]); + else set_debug(DEBUG_STDERR, "l7"); } } - /* check archive */ - if (archive != NULL) + if (asl_store_dst->path == NULL) asl_store_dst->path = strdup(PATH_ASL_STORE); + + debug_log(ASL_LEVEL_ERR, "aslmanager starting%s\n", dryrun ? " dryrun" : ""); + + if (cache_delete) { - memset(&sb, 0, sizeof(struct stat)); - if (stat(archive, &sb) == 0) + size_t curr_size = 0; + + if (cache_delete_task(true, &curr_size) != 0) { - /* must be a directory */ - if ((sb.st_mode & S_IFDIR) == 0) - { - fprintf(stderr, "aslmanager error: archive %s is not a directory", archive); - return -1; - } + debug_log(ASL_LEVEL_NOTICE, "cache_delete_process failed - can't determine current size\n"); } else { - if (errno == ENOENT) + debug_log(ASL_LEVEL_NOTICE, "cache delete current size = %lu\n", curr_size); + + if (!cache_delete_query) { - /* archive doesn't exist - create it */ - if (mkdir(archive, 0755) != 0) + size_t new_size = curr_size - opts.all_max; + + if (cache_delete_task(false, &new_size) != 0) { - fprintf(stderr, "aslmanager error: can't create archive %s: %s\n", archive, strerror(errno)); - return -1; - } - } - else - { - /* stat failed for some other reason */ - fprintf(stderr, "aslmanager error: can't stat archive %s: %s\n", archive, strerror(errno)); - return -1; + debug_log(ASL_LEVEL_NOTICE, "cache_delete_process failed - delete failed\n"); + } + else + { + debug_log(ASL_LEVEL_NOTICE, "cache delete new size = %lu\n", new_size); + } } } - } - chdir(store_dir); + asl_out_module_free(mod); - /* determine current time and time TTL ago */ - now = time(NULL); - best_before = 0; - if (ttl > 0) best_before = now - ttl; + debug_log(ASL_LEVEL_NOTICE, "----------------------------------------\n"); + debug_log(ASL_LEVEL_ERR, "aslmanager finished%s\n", dryrun ? " dryrun" : ""); + debug_close(); - /* construct best before 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); + return 0; + } - snprintf(bbstr, sizeof(bbstr), "%d.%02d.%02d.", ctm.tm_year + 1900, ctm.tm_mon + 1, ctm.tm_mday); - bbstrlen = strlen(bbstr); + if (work & DO_ASLDB) process_asl_data_store(asl_store_dst, &opts); - if (debug == 1) printf("Best Before Date %s\n", bbstr); + if (work & DO_MODULE) + { + if (work & DO_CHECKPT) checkpoint(mname); - dp = opendir(store_dir); - if (dp == NULL) mgr_exit(store_dir, 1); + if (mod != NULL) + { + for (m = mod; m != NULL; m = m->next) + { + if (mname == NULL) + { + process_module(m, NULL); + } + else if ((m->name != NULL) && (!strcmp(m->name, mname))) + { + process_module(m, &opts); + } + } + } + } - /* gather a list of files for dates before the best before date */ + asl_out_module_free(mod); - while ((dent = readdir(dp)) != NULL) - { - if ((dent->d_name[0] < '0') || (dent->d_name[0] > '9')) continue; + debug_log(ASL_LEVEL_NOTICE, "----------------------------------------\n"); + debug_log(ASL_LEVEL_ERR, "aslmanager finished%s\n", dryrun ? " dryrun" : ""); + debug_close(); - 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; + return 0; +} - list = add_to_list(list, dent->d_name, file_size); - } +/* dispatched on server_queue, dispatches to work_queue */ +void +main_task(void) +{ + /* if main task is already running or queued, do nothing */ + if (main_task_enqueued) return; - closedir(dp); + main_task_enqueued = true; + xpc_transaction_begin(); - if (debug == 1) + if (initial_main_task) { - 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); + initial_main_task = false; + dispatch_time_t delay = dispatch_walltime(NULL, MAIN_TASK_INITIAL_DELAY * NSEC_PER_SEC); + + dispatch_after(delay, work_queue, ^{ + cli_main(0, NULL); + main_task_enqueued = false; + xpc_transaction_end(); + }); } - - /* copy messages in each expired file with ASLExpireTime values to LongTTL files */ - if (debug == 1) printf("\nStart Scan\n"); - - e = list; - while (e != NULL) + else { - if ((store_size <= max_size) && (strncmp(e->name, bbstr, bbstrlen) >= 0)) break; - - /* find '.' after year */ - p = strchr(e->name, '.'); - if (p == NULL) continue; - - /* find '.' after month */ - p++; - p = strchr(p, '.'); - if (p == NULL) continue; - - /* find '.' after day */ - p++; - p = strchr(p, '.'); - if (p == NULL) continue; + dispatch_async(work_queue, ^{ + cli_main(0, NULL); + main_task_enqueued = false; + xpc_transaction_end(); + }); + } +} - str = NULL; - asprintf(&str, "LongTTL%s", p); - if (str == NULL) mgr_exit(store_dir, 1); +static void +accept_connection(xpc_connection_t peer) +{ + xpc_connection_set_event_handler(peer, ^(xpc_object_t request) { + if (xpc_get_type(request) == XPC_TYPE_DICTIONARY) + { + uid_t uid = xpc_connection_get_euid(peer); - /* 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); + /* send a reply immediately */ + xpc_object_t reply = xpc_dictionary_create_reply(request); + xpc_connection_send_message(peer, reply); + xpc_release(reply); - free(str); - str = NULL; + /* + * Some day, we may use the dictionary to pass parameters + * to aslmanager, but for now, we ignore the input. + */ - if (archive != NULL) + if (uid == geteuid()) + { + main_task(); + } + } + else if (xpc_get_type(request) == XPC_TYPE_ERROR) { - str = NULL; - asprintf(&str, "%s/%s", archive, e->name); - if (str == NULL) mgr_exit(store_dir, 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); - free(str); + /* disconnect */ } + }); - if (debug == 1) printf(" unlink %s\n", e->name); - else unlink(e->name); - - store_size -= e->size; - e->size = 0; - - e = e->next; - } - - if (debug == 1) - { - printf("Finished Scan\n"); - printf("\nData Store Size = %lu\n", store_size); - } + xpc_connection_resume(peer); +} - free_list(list); - list = NULL; +int +main(int argc, char *argv[]) +{ + int64_t is_managed = 0; - dp = opendir(PATH_ASL_STORE); - if (dp == NULL) mgr_exit(store_dir, 1); + vproc_swap_integer(NULL, VPROC_GSK_IS_MANAGED, NULL, &is_managed); - /* gather a list of LongTTL files */ + if (is_managed == 0) return cli_main(argc, argv); - while ((dent = readdir(dp)) != NULL) - { - if (!strncmp(dent->d_name, "LongTTL.", 8)) list = add_to_list(list, dent->d_name, 0); - } + /* Set I/O policy */ + setiopolicy_np(IOPOL_TYPE_DISK, IOPOL_SCOPE_PROCESS, IOPOL_THROTTLE); - closedir(dp); + /* XPC server */ + server_queue = dispatch_queue_create("aslmanager", NULL); - if (debug == 1) - { - printf("\nData Store LongTTL Files\n"); - for (e = list; e != NULL; e = e->next) printf(" %s\n", e->name); - } + work_queue = dispatch_queue_create("work queue", NULL); - if (debug == 1) printf("\nScan for expired messages\n"); + /* Exit on SIGTERM */ + signal(SIGTERM, SIG_IGN); + sig_term_src = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, (uintptr_t)SIGTERM, 0, dispatch_get_main_queue()); + dispatch_source_set_event_handler(sig_term_src, ^{ + debug_log(ASL_LEVEL_NOTICE, "SIGTERM exit\n"); + exit(0); + }); - e = list; - while (e != NULL) - { - /* syslog -x LongTTL.new -db [e->name] -k ASLExpireTime Nge [now] */ - if (debug == 1) - { - printf(" %s\n", e->name); - } - else - { - status = do_match(e->name, "LongTTL.new", 1, now); - unlink(e->name); - if (status == ASL_STATUS_OK) rename("LongTTL.new", e->name); - } + dispatch_resume(sig_term_src); - e = e->next; - } - - if (debug == 1) printf("Finished scan for expired messages\n"); + /* Handle incoming messages. */ + listener = xpc_connection_create_mach_service("com.apple.aslmanager", server_queue, XPC_CONNECTION_MACH_SERVICE_LISTENER); + xpc_connection_set_event_handler(listener, ^(xpc_object_t peer) { + if (xpc_get_type(peer) == XPC_TYPE_CONNECTION) accept_connection(peer); + }); + xpc_connection_resume(listener); - free_list(list); - list = NULL; + cache_delete_register(); - mgr_exit(store_dir, 0); - /* UNREACHED */ - return 0; + dispatch_main(); } -