/* * Copyright (c) 2004-2011 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include "asl_core.h" #include "asl_msg.h" #include "asl_store.h" #include "asl_private.h" #define streq(A, B) (strcmp(A, B) == 0) #define strcaseeq(A, B) (strcasecmp(A, B) == 0) #define forever for(;;) #define FETCH_BATCH 256 #define LEVEL_MASK 0x0000000f #define EVAL_MASK 0x000000f0 #define EVAL_IGNORE 0x00000000 #define EVAL_ASLFILE 0x00000010 #define EVAL_SEND 0x00000020 #define EVAL_TUNNEL 0x00000040 #define EVAL_FILE 0x00000080 /* forward */ time_t asl_parse_time(const char *); const char *asl_syslog_faciliy_num_to_name(int n); static int _asl_send_message(aslclient ac, uint32_t eval, asl_msg_t *msg, const char *mstring); __private_extern__ asl_client_t *_asl_open_default(); /* private asl_file SPI */ __private_extern__ uint32_t asl_file_open_write_fd(int fd, asl_file_t **s); /* private asl_msg SPI */ __private_extern__ asl_string_t *asl_msg_to_string_raw(uint32_t encoding, asl_msg_t *msg, int tf); /* notify SPI */ uint32_t notify_register_plain(const char *name, int *out_token); /* fork handling in syslog.c */ extern void _syslog_fork_child(); typedef struct { int fd; asl_msg_t *msg; dispatch_semaphore_t sem; } asl_aux_context_t; typedef struct { int notify_count; int rc_change_token; int notify_token; int master_token; uint64_t proc_filter; uint64_t master_filter; dispatch_once_t port_lookup_once; mach_port_t server_port; char *sender; pthread_mutex_t lock; int aux_count; asl_aux_context_t **aux_ctx; asl_client_t *asl; } _asl_global_t; #ifndef BUILDING_VARIANT __private_extern__ _asl_global_t _asl_global = {0, -1, -1, -1, 0LL, 0LL, 0, MACH_PORT_NULL, NULL, PTHREAD_MUTEX_INITIALIZER, 0, NULL, NULL}; #define ASL_SERVICE_NAME "com.apple.system.logger" /* * Called from the child process inside fork() to clean up * inherited state from the parent process. * * NB. A lock isn't required, since we're single threaded in this call. */ __private_extern__ void _asl_fork_child() { _asl_global.notify_count = 0; _asl_global.rc_change_token = -1; _asl_global.master_token = -1; _asl_global.notify_token = -1; _asl_global.port_lookup_once = 0; _asl_global.server_port = MACH_PORT_NULL; pthread_mutex_init(&(_asl_global.lock), NULL); } /* * asl_remote_notify_name: returns the notification key for remote-control filter * changes for this process. */ char * asl_remote_notify_name() { pid_t pid = getpid(); uid_t euid = geteuid(); char *str = NULL; if (euid == 0) asprintf(&str, "%s.%d", NOTIFY_PREFIX_SYSTEM, pid); else asprintf(&str, "user.uid.%d.syslog.%d", euid, pid); return str; } static int _asl_notify_open(int do_lock) { char *notify_name; uint32_t status; if (do_lock != 0) pthread_mutex_lock(&_asl_global.lock); _asl_global.notify_count++; if (_asl_global.notify_token != -1) { if (do_lock != 0) pthread_mutex_unlock(&_asl_global.lock); return 0; } if (_asl_global.rc_change_token == -1) { status = notify_register_check(NOTIFY_RC, &_asl_global.rc_change_token); if (status != NOTIFY_STATUS_OK) _asl_global.rc_change_token = -1; } if (_asl_global.master_token == -1) { status = notify_register_plain(NOTIFY_SYSTEM_MASTER, &_asl_global.master_token); if (status != NOTIFY_STATUS_OK) _asl_global.master_token = -1; } notify_name = asl_remote_notify_name(); if (notify_name != NULL) { status = notify_register_plain(notify_name, &_asl_global.notify_token); free(notify_name); if (status != NOTIFY_STATUS_OK) _asl_global.notify_token = -1; } if (do_lock != 0) pthread_mutex_unlock(&_asl_global.lock); if (_asl_global.notify_token == -1) return -1; return 0; } static void _asl_notify_close() { pthread_mutex_lock(&_asl_global.lock); if (_asl_global.notify_count > 0) _asl_global.notify_count--; if (_asl_global.notify_count > 0) { pthread_mutex_unlock(&_asl_global.lock); return; } if (_asl_global.rc_change_token >= 0) notify_cancel(_asl_global.rc_change_token); _asl_global.rc_change_token = -1; if (_asl_global.master_token >= 0) notify_cancel(_asl_global.master_token); _asl_global.master_token = -1; if (_asl_global.notify_token >= 0) notify_cancel(_asl_global.notify_token); _asl_global.notify_token = -1; pthread_mutex_unlock(&_asl_global.lock); } static void _asl_global_init() { if (_asl_global.server_port == MACH_PORT_NULL) { mach_port_t newport = MACH_PORT_NULL; char *str = getenv("ASL_DISABLE"); if ((str == NULL) || strcmp(str, "1")) { bootstrap_look_up(bootstrap_port, ASL_SERVICE_NAME, &newport); if (newport != MACH_PORT_NULL) { if (!OSAtomicCompareAndSwap32Barrier(MACH_PORT_NULL, newport, (int32_t *)&_asl_global.server_port)) { mach_port_deallocate(mach_task_self(), newport); } } } } } static void _asl_global_reset() { mach_port_t tmp = _asl_global.server_port; _asl_global.server_port = MACH_PORT_NULL; mach_port_deallocate(mach_task_self(), tmp); } aslclient asl_open(const char *ident, const char *facility, uint32_t opts) { char *name, *x; asl_client_t *asl; asl = (asl_client_t *)calloc(1, sizeof(asl_client_t)); if (asl == NULL) { errno = ENOMEM; return NULL; } asl->options = opts; asl->sock = -1; _asl_global_init(); asl->pid = getpid(); asl->uid = getuid(); asl->gid = getgid(); asl->filter = ASL_FILTER_MASK_UPTO(ASL_LEVEL_NOTICE); if (ident != NULL) { asl->name = strdup(ident); if (asl->name == NULL) { if (asl->sock >= 0) close(asl->sock); free(asl); return NULL; } } else { name = *(*_NSGetArgv()); if (name != NULL) { x = strrchr(name, '/'); if (x != NULL) x++; else x = name; asl->name = strdup(x); if (asl->name == NULL) { if (asl->sock >= 0) close(asl->sock); free(asl); return NULL; } } } asl->facility = NULL; if (facility != NULL) asl->facility = strdup(facility); else asl->facility = strdup(asl_syslog_faciliy_num_to_name(LOG_USER)); if (asl->facility == NULL) { if (asl->sock >= 0) close(asl->sock); if (asl->name != NULL) free(asl->name); free(asl); return NULL; } if (!(asl->options & ASL_OPT_NO_REMOTE)) _asl_notify_open(1); if (asl->options & ASL_OPT_STDERR) asl_add_output((aslclient)asl, fileno(stderr), ASL_MSG_FMT_STD, ASL_TIME_FMT_LCL, ASL_ENCODE_SAFE); asl->refcount = 1; return (aslclient)asl; } aslclient asl_open_from_file(int fd, const char *ident, const char *facility) { char *name, *x; asl_client_t *asl; uint32_t status; asl = (asl_client_t *)calloc(1, sizeof(asl_client_t)); if (asl == NULL) { errno = ENOMEM; return NULL; } asl->options = ASL_OPT_NO_REMOTE; asl->sock = -1; asl->pid = getpid(); asl->uid = getuid(); asl->gid = getgid(); asl->filter = ASL_FILTER_MASK_UPTO(ASL_LEVEL_DEBUG); if (ident != NULL) { asl->name = strdup(ident); if (asl->name == NULL) { free(asl); return NULL; } } else { name = *(*_NSGetArgv()); if (name != NULL) { x = strrchr(name, '/'); if (x != NULL) x++; else x = name; asl->name = strdup(x); if (asl->name == NULL) { free(asl); return NULL; } } } asl->facility = NULL; if (facility != NULL) asl->facility = strdup(facility); else asl->facility = strdup(asl_syslog_faciliy_num_to_name(LOG_USER)); if (asl->facility == NULL) { if (asl->name != NULL) free(asl->name); free(asl); return NULL; } status = asl_file_open_write_fd(fd, &(asl->aslfile)); if (status != ASL_STATUS_OK) { if (asl->name != NULL) free(asl->name); if (asl->facility != NULL) free(asl->facility); free(asl); return NULL; } asl->aslfileid = 1; asl->refcount = 1; return (aslclient)asl; } __private_extern__ void asl_client_release(asl_client_t *asl) { uint32_t i; if (asl == NULL) return; if(OSAtomicDecrement32(&asl->refcount) > 0) return; free(asl->name); free(asl->facility); if (asl->sock >= 0) close(asl->sock); if (!(asl->options & ASL_OPT_NO_REMOTE)) _asl_notify_close(); if (asl->fd_list != NULL) free(asl->fd_list); if (asl->fd_mfmt != NULL) { for (i = 0; i < asl->fd_count; i++) if (asl->fd_mfmt[i] != NULL) free(asl->fd_mfmt[i]); free(asl->fd_mfmt); } if (asl->fd_tfmt != NULL) { for (i = 0; i < asl->fd_count; i++) if (asl->fd_tfmt[i] != NULL) free(asl->fd_tfmt[i]); free(asl->fd_tfmt); } if (asl->fd_encoding != NULL) free(asl->fd_encoding); memset(asl, 0, sizeof(asl_client_t)); free(asl); } void asl_close(aslclient ac) { asl_client_release((asl_client_t *)ac); } __private_extern__ asl_client_t * asl_client_retain(asl_client_t *asl) { int32_t new; if (asl == NULL) return NULL; new = OSAtomicIncrement32(&asl->refcount); assert(new >= 1); return asl; } __private_extern__ asl_client_t * _asl_open_default() { static dispatch_once_t once; dispatch_once(&once, ^{ /* * Do a sleight-of-hand with ASL_OPT_NO_REMOTE to avoid a deadlock * since asl_open(xxx, yyy, 0) calls _asl_notify_open(1) * which locks _asl_global.lock. */ _asl_global.asl = asl_open(NULL, NULL, ASL_OPT_NO_REMOTE); /* Reset options to clear ASL_OPT_NO_REMOTE bit */ if (_asl_global.asl != NULL) _asl_global.asl->options = 0; /* Now call _asl_notify_open(0) to finish the work */ _asl_notify_open(0); }); return _asl_global.asl; } /* * asl_add_file: write log messages to the given file descriptor * Log messages will be written to this file as well as to the server. */ int asl_add_output(aslclient ac, int fd, const char *mfmt, const char *tfmt, uint32_t text_encoding) { uint32_t i; int use_global_lock; asl_client_t *asl; use_global_lock = 0; asl = (asl_client_t *)ac; if (asl == NULL) { asl = _asl_open_default(); if (asl == NULL) return -1; pthread_mutex_lock(&_asl_global.lock); use_global_lock = 1; } for (i = 0; i < asl->fd_count; i++) { if (asl->fd_list[i] == fd) { /* update message format, time format, and text encoding */ if (asl->fd_mfmt[i] != NULL) free(asl->fd_mfmt[i]); asl->fd_mfmt[i] = NULL; if (mfmt != NULL) asl->fd_mfmt[i] = strdup(mfmt); if (asl->fd_tfmt[i] != NULL) free(asl->fd_tfmt[i]); asl->fd_tfmt[i] = NULL; if (tfmt != NULL) asl->fd_tfmt[i] = strdup(tfmt); asl->fd_encoding[i] = text_encoding; if (use_global_lock != 0) pthread_mutex_unlock(&_asl_global.lock); return 0; } } if (asl->fd_count == 0) { asl->fd_list = (int *)calloc(1, sizeof(int)); asl->fd_mfmt = (char **)calloc(1, sizeof(char *)); asl->fd_tfmt = (char **)calloc(1, sizeof(char *)); asl->fd_encoding = (uint32_t *)calloc(1, sizeof(int)); } else { asl->fd_list = (int *)reallocf(asl->fd_list, (1 + asl->fd_count) * sizeof(int)); asl->fd_mfmt = (char **)reallocf(asl->fd_mfmt, (1 + asl->fd_count) * sizeof(char *)); asl->fd_tfmt = (char **)reallocf(asl->fd_tfmt, (1 + asl->fd_count) * sizeof(char *)); asl->fd_encoding = (uint32_t *)reallocf(asl->fd_encoding, (1 + asl->fd_count) * sizeof(uint32_t)); } if ((asl->fd_list == NULL) || (asl->fd_mfmt == NULL) || (asl->fd_tfmt == NULL) || (asl->fd_encoding == NULL)) { if (asl->fd_list != NULL) free(asl->fd_list); if (asl->fd_mfmt != NULL) free(asl->fd_mfmt); if (asl->fd_tfmt != NULL) free(asl->fd_tfmt); if (asl->fd_encoding != NULL) free(asl->fd_encoding); if (use_global_lock != 0) pthread_mutex_unlock(&_asl_global.lock); return -1; } asl->fd_list[asl->fd_count] = fd; if (mfmt != NULL) asl->fd_mfmt[asl->fd_count] = strdup(mfmt); if (tfmt != NULL) asl->fd_tfmt[asl->fd_count] = strdup(tfmt); asl->fd_encoding[asl->fd_count] = text_encoding; asl->fd_count++; if (use_global_lock != 0) pthread_mutex_unlock(&_asl_global.lock); return 0; } int asl_add_log_file(aslclient ac, int fd) { return asl_add_output(ac, fd, ASL_MSG_FMT_STD, ASL_TIME_FMT_LCL, ASL_ENCODE_SAFE); } /* * asl_remove_output: stop writing log messages to the given file descriptor */ int asl_remove_output(aslclient ac, int fd) { uint32_t i; int x, use_global_lock; asl_client_t *asl; use_global_lock = 0; asl = (asl_client_t *)ac; if (asl == NULL) { asl = _asl_open_default(); if (asl == NULL) return -1; pthread_mutex_lock(&_asl_global.lock); use_global_lock = 1; } if (asl->fd_count == 0) { if (use_global_lock != 0) pthread_mutex_unlock(&_asl_global.lock); return 0; } x = -1; for (i = 0; i < asl->fd_count; i++) { if (asl->fd_list[i] == fd) { x = i; break; } } if (x == -1) { if (use_global_lock != 0) pthread_mutex_unlock(&_asl_global.lock); return 0; } if (asl->fd_mfmt[x] != NULL) free(asl->fd_mfmt[x]); if (asl->fd_tfmt[x] != NULL) free(asl->fd_tfmt[x]); for (i = x + 1; i < asl->fd_count; i++, x++) { asl->fd_list[x] = asl->fd_list[i]; asl->fd_mfmt[x] = asl->fd_mfmt[i]; asl->fd_tfmt[x] = asl->fd_tfmt[i]; asl->fd_encoding[x] = asl->fd_encoding[i]; } asl->fd_count--; if (asl->fd_count == 0) { free(asl->fd_list); asl->fd_list = NULL; free(asl->fd_mfmt); asl->fd_mfmt = NULL; free(asl->fd_tfmt); asl->fd_tfmt = NULL; free(asl->fd_encoding); asl->fd_encoding = NULL; } else { asl->fd_list = (int *)reallocf(asl->fd_list, asl->fd_count * sizeof(int)); asl->fd_mfmt = (char **)reallocf(asl->fd_mfmt, asl->fd_count * sizeof(char *)); asl->fd_tfmt = (char **)reallocf(asl->fd_tfmt, asl->fd_count * sizeof(char *)); asl->fd_encoding = (uint32_t *)reallocf(asl->fd_encoding, asl->fd_count * sizeof(uint32_t)); if ((asl->fd_list == NULL) || (asl->fd_mfmt == NULL) || (asl->fd_tfmt == NULL) || (asl->fd_encoding == NULL)) { if (asl->fd_list != NULL) { free(asl->fd_list); asl->fd_list = NULL; } if (asl->fd_mfmt != NULL) { for (i = 0; i < asl->fd_count; i++) if (asl->fd_mfmt[i] != NULL) free(asl->fd_mfmt[i]); free(asl->fd_mfmt); asl->fd_mfmt = NULL; } if (asl->fd_tfmt != NULL) { for (i = 0; i < asl->fd_count; i++) if (asl->fd_tfmt[i] != NULL) free(asl->fd_tfmt[i]); free(asl->fd_tfmt); asl->fd_tfmt = NULL; } if (asl->fd_encoding != NULL) { free(asl->fd_encoding); asl->fd_encoding = NULL; } asl->fd_count = 0; if (use_global_lock != 0) pthread_mutex_unlock(&_asl_global.lock); return -1; } } if (use_global_lock != 0) pthread_mutex_unlock(&_asl_global.lock); return 0; } int asl_remove_log_file(aslclient ac, int fd) { return asl_remove_output(ac, fd); } int asl_set_filter(aslclient ac, int f) { int last, use_global_lock; asl_client_t *asl; use_global_lock = 0; asl = (asl_client_t *)ac; if (asl == NULL) { asl = _asl_open_default(); if (asl == NULL) return -1; pthread_mutex_lock(&_asl_global.lock); use_global_lock = 1; } last = asl->filter; asl->filter = f; if (use_global_lock != 0) pthread_mutex_unlock(&_asl_global.lock); return last; } /* * Evaluate client / message / level to determine what to do with a message. * Checks filters, tunneling, and log files. Returns EVAL_IGNORE if the message * can be ignored. Otherwise it returns the bits below, ORed with the level. * * EVAL_ASLFILE - will write to an asl file (see asl_open_from_file) * EVAL_SEND - will send to syslogd * EVAL_TUNNEL - will send to syslogd with tunneling enabled * EVAL_FILE - will write to file */ uint32_t _asl_evaluate_send(aslclient ac, aslmsg msg, int slevel) { asl_client_t *asl = (asl_client_t *)ac; uint32_t level, lmask, filter, status, tunnel; int check, out; uint64_t v64; const char *val; if (asl == NULL) { asl = _asl_open_default(); if (asl == NULL) return EVAL_IGNORE; } check = ASL_LEVEL_DEBUG; if (slevel >= 0) check = slevel; val = asl_get((aslmsg)msg, ASL_KEY_LEVEL); if (val != NULL) check = atoi(val); if (check < ASL_LEVEL_EMERG) check = ASL_LEVEL_EMERG; else if (check > ASL_LEVEL_DEBUG) check = ASL_LEVEL_DEBUG; level = check; out = check; if (asl->aslfile != NULL) return (out | EVAL_ASLFILE); lmask = ASL_FILTER_MASK(level); if (!(asl->options & ASL_OPT_NO_REMOTE)) { pthread_mutex_lock(&_asl_global.lock); if (_asl_global.rc_change_token >= 0) { /* initialize or re-check process-specific and master filters */ check = 0; status = notify_check(_asl_global.rc_change_token, &check); if ((status == NOTIFY_STATUS_OK) && (check != 0)) { if (_asl_global.master_token >= 0) { v64 = 0; status = notify_get_state(_asl_global.master_token, &v64); if (status == NOTIFY_STATUS_OK) _asl_global.master_filter = v64; } if (_asl_global.notify_token >= 0) { v64 = 0; status = notify_get_state(_asl_global.notify_token, &v64); if (status == NOTIFY_STATUS_OK) _asl_global.proc_filter = v64; } } } pthread_mutex_unlock(&_asl_global.lock); } filter = asl->filter & 0xff; tunnel = (asl->filter & ASL_FILTER_MASK_TUNNEL) >> 8; /* master filter overrides local filter */ if (_asl_global.master_filter != 0) { filter = _asl_global.master_filter; tunnel = 1; } /* process-specific filter overrides local and master */ if (_asl_global.proc_filter != 0) { filter = _asl_global.proc_filter; tunnel = 1; } if ((filter != 0) && ((filter & lmask) != 0)) { out |= EVAL_SEND; if (tunnel != 0) out |= EVAL_TUNNEL; if (asl->fd_count > 0) out |= EVAL_FILE; return out; } if ((asl->options & ASL_OPT_SYSLOG_LEGACY) && (filter != 0) && ((filter & lmask) == 0)) { return EVAL_IGNORE; } if (asl->fd_count > 0) return (out | EVAL_FILE); return EVAL_IGNORE; } #endif /* BUILDING_VARIANT */ /* * _asl_lib_vlog * Internal routine used by asl_vlog. * msg: an aslmsg * eval: log level and send flags for the message * format: A formating string * ap: va_list for the format * returns 0 for success, non-zero for failure */ static int _asl_lib_vlog(aslclient ac, uint32_t eval, aslmsg msg, const char *format, va_list ap) { int saved_errno = errno; int status; char *str, *fmt, estr[NL_TEXTMAX]; uint32_t i, len, elen, expand; asl_client_t *asl; asl = (asl_client_t *)ac; if (asl == NULL) { /* * Initialize _asl_global so that asl_new will have global data. * Not strictly necessary, but helps performance. */ asl = _asl_open_default(); if (asl == NULL) return -1; } if (format == NULL) return -1; /* insert strerror for %m */ len = 0; elen = 0; expand = 0; for (i = 0; format[i] != '\0'; i++) { if (format[i] == '%') { if (format[i+1] == '\0') len++; else if (format[i+1] == 'm') { expand = 1; strerror_r(saved_errno, estr, sizeof(estr)); elen = strlen(estr); len += elen; i++; } else { len += 2; i++; } } else len++; } fmt = (char *)format; if (expand != 0) { fmt = malloc(len + 1); if (fmt == NULL) { if (estr != NULL) free(estr); return -1; } len = 0; for (i = 0; format[i] != '\0'; i++) { if (format[i] == '%') { if (format[i+1] == '\0') { } else if ((format[i+1] == 'm') && (elen != 0)) { memcpy(fmt+len, estr, elen); len += elen; i++; } else { fmt[len++] = format[i++]; fmt[len++] = format[i]; } } else fmt[len++] = format[i]; } fmt[len] = '\0'; } vasprintf(&str, fmt, ap); if (expand != 0) free(fmt); if (str == NULL) return -1; status = _asl_send_message(ac, eval, (asl_msg_t *)msg, str); free(str); return status; } /* * asl_vlog * Similar to asl_log, but take a va_list instead of a list of arguments. * msg: an aslmsg * level: the log level of the associated message * format: A formating string * ap: va_list for the format * returns 0 for success, non-zero for failure */ int asl_vlog(aslclient ac, aslmsg msg, int level, const char *format, va_list ap) { uint32_t eval = _asl_evaluate_send(ac, msg, level); if (eval == EVAL_IGNORE) return 0; return _asl_lib_vlog(ac, eval, msg, format, ap); } /* * _asl_lib_log * SPI used by ASL_PREFILTER_LOG. Converts format arguments to a va_list and * forwards the call to _asl_lib_vlog. * msg: an aslmsg * eval: log level and send flags for the message * format: A formating string * ... args for format * returns 0 for success, non-zero for failure */ int _asl_lib_log(aslclient ac, uint32_t eval, aslmsg msg, const char *format, ...) { int status; if (eval == EVAL_IGNORE) return 0; va_list ap; va_start(ap, format); status = _asl_lib_vlog(ac, eval, msg, format, ap); va_end(ap); return status; } /* * asl_log * Processes an ASL log message. * msg: an aslmsg * level: the log level of the associated message * format: A formating string * ... args for format * returns 0 for success, non-zero for failure */ int asl_log(aslclient ac, aslmsg msg, int level, const char *format, ...) { int status; uint32_t eval = _asl_evaluate_send(ac, msg, level); if (eval == EVAL_IGNORE) return 0; va_list ap; va_start(ap, format); status = _asl_lib_vlog(ac, eval, msg, format, ap); va_end(ap); return status; } #ifndef BUILDING_VARIANT /* * asl_get_filter: gets the values for the local, master, and remote filters, * and indicates which one is active. */ int asl_get_filter(aslclient ac, int *local, int *master, int *remote, int *active) { asl_client_t *asl, *asl_default; int l, m, r, x; int status, check; uint64_t v64; l = 0; m = 0; r = 0; x = 0; asl_default = _asl_open_default(); asl = (asl_client_t *)ac; if (asl == NULL) asl = asl_default; if (asl != NULL) l = asl->filter & 0xff; if ((asl_default != NULL) && (!(asl_default->options & ASL_OPT_NO_REMOTE))) { pthread_mutex_lock(&_asl_global.lock); if (_asl_global.rc_change_token >= 0) { /* initialize or re-check process-specific and master filters */ check = 0; status = notify_check(_asl_global.rc_change_token, &check); if ((status == NOTIFY_STATUS_OK) && (check != 0)) { if (_asl_global.master_token >= 0) { v64 = 0; status = notify_get_state(_asl_global.master_token, &v64); if (status == NOTIFY_STATUS_OK) _asl_global.master_filter = v64; } if (_asl_global.notify_token >= 0) { v64 = 0; status = notify_get_state(_asl_global.notify_token, &v64); if (status == NOTIFY_STATUS_OK) _asl_global.proc_filter = v64; } } } m = _asl_global.master_filter; if (m != 0) x = 1; r = _asl_global.proc_filter; if (r != 0) x = 2; pthread_mutex_unlock(&_asl_global.lock); } if (local != NULL) *local = l; if (master != NULL) *master = m; if (remote != NULL) *remote = r; if (active != NULL) *active = x; return 0; } static int _asl_send_message(aslclient ac, uint32_t eval, asl_msg_t *msg, const char *mstring) { uint32_t i, len, level, lmask, outstatus, filter, fd_write; uint64_t v64; const char *val; char *name, *x; time_t tick; struct timeval tval; int status, check; asl_client_t *asl; int use_global_lock; kern_return_t kstatus; char aux_val[64]; char aux_host[_POSIX_HOST_NAME_MAX]; asl_msg_t *aux; if (eval == EVAL_IGNORE) return 0; level = eval & LEVEL_MASK; eval &= EVAL_MASK; use_global_lock = 0; asl = (asl_client_t *)ac; if (asl == NULL) { asl = _asl_open_default(); if (asl == NULL) return -1; use_global_lock = 1; } if (asl->aslfile != NULL) use_global_lock = 1; /* * Time, TimeNanoSec, Host, PID, UID, and GID values get set here. * Also sets Sender & Facility (if unset) and "ASLOption store" if remote control is active. */ aux = asl_msg_new(ASL_TYPE_MSG); if (mstring != NULL) asl_msg_set_key_val(aux, ASL_KEY_MSG, mstring); snprintf(aux_val, sizeof(aux_val), "%u", level); asl_msg_set_key_val(aux, ASL_KEY_LEVEL, aux_val); memset(&tval, 0, sizeof(struct timeval)); status = gettimeofday(&tval, NULL); if (status == 0) { snprintf(aux_val, sizeof(aux_val), "%lu", tval.tv_sec); asl_msg_set_key_val(aux, ASL_KEY_TIME, aux_val); snprintf(aux_val, sizeof(aux_val), "%d", tval.tv_usec * 1000); asl_msg_set_key_val(aux, ASL_KEY_TIME_NSEC, aux_val); } else { tick = time(NULL); snprintf(aux_val, sizeof(aux_val), "%lu", tick); asl_msg_set_key_val(aux, ASL_KEY_TIME, aux_val); } memset(&aux_host, 0, _POSIX_HOST_NAME_MAX); if (gethostname(aux_host, _POSIX_HOST_NAME_MAX) == 0) { asl_msg_set_key_val(aux, ASL_KEY_HOST, aux_host); } snprintf(aux_val, sizeof(aux_val), "%u", getpid()); asl_msg_set_key_val(aux, ASL_KEY_PID, aux_val); snprintf(aux_val, sizeof(aux_val), "%d", getuid()); asl_msg_set_key_val(aux, ASL_KEY_UID, aux_val); snprintf(aux_val, sizeof(aux_val), "%d", getgid()); asl_msg_set_key_val(aux, ASL_KEY_GID, aux_val); /* * Set Sender if needed */ status = asl_msg_lookup((asl_msg_t *)msg, ASL_KEY_SENDER, &val, NULL); if ((status != 0) || (val == NULL)) { if ((ac != NULL) && (ac->name != NULL)) { /* Use the Sender name from the client handle */ asl_msg_set_key_val(aux, ASL_KEY_SENDER, ac->name); } else { /* Get the value for ASL_KEY_SENDER from cache */ if (_asl_global.sender == NULL) { name = *(*_NSGetArgv()); if (name != NULL) { x = strrchr(name, '/'); if (x != NULL) x++; else x = name; pthread_mutex_lock(&_asl_global.lock); if (_asl_global.sender == NULL) _asl_global.sender = strdup(x); pthread_mutex_unlock(&_asl_global.lock); } } if (_asl_global.sender != NULL) asl_msg_set_key_val(aux, ASL_KEY_SENDER, _asl_global.sender); else asl_msg_set_key_val(aux, ASL_KEY_SENDER, "Unknown"); } } /* * Set Facility */ status = asl_msg_lookup((asl_msg_t *)msg, ASL_KEY_FACILITY, &val, NULL); if ((status != 0) || (val == NULL)) { if ((ac != NULL) && (ac->facility != NULL)) { /* Use the Facility name from the client handle */ asl_msg_set_key_val(aux, ASL_KEY_FACILITY, ac->facility); } } /* Set "ASLOption store" if tunneling */ if (eval & EVAL_TUNNEL) { val = asl_get((aslmsg)msg, ASL_KEY_OPTION); if (val == NULL) { asl_msg_set_key_val(aux, ASL_KEY_OPTION, ASL_OPT_STORE); } else { char *aux_option = NULL; asprintf(&aux_option, "%s %s", ASL_OPT_STORE, val); asl_msg_set_key_val(aux, ASL_KEY_OPTION, aux_option); free(aux_option); } } outstatus = -1; if (use_global_lock != 0) pthread_mutex_lock(&_asl_global.lock); aux = asl_msg_merge(aux, msg); /* * If there is an aslfile this is a stand-alone file client. * Just save to the file. */ if (asl->aslfile != NULL) { outstatus = ASL_STATUS_FAILED; if (aux != NULL) { outstatus = asl_file_save(asl->aslfile, (aslmsg)aux, &(asl->aslfileid)); asl->aslfileid++; } asl_msg_release(aux); if (use_global_lock != 0) pthread_mutex_unlock(&_asl_global.lock); return outstatus; } _asl_global_init(); outstatus = 0; if ((_asl_global.server_port != MACH_PORT_NULL) && (eval & EVAL_SEND)) { asl_string_t *send_str; const char *str; size_t vmsize; send_str = asl_msg_to_string_raw(ASL_STRING_MIG, aux, 0); len = asl_string_length(send_str); vmsize = asl_string_allocated_size(send_str); str = asl_string_free_return_bytes(send_str); if (len != 0) { /* send a mach message to syslogd */ kstatus = _asl_server_message(_asl_global.server_port, (caddr_t)str, len); if (kstatus != KERN_SUCCESS) { /* retry once if the call failed */ _asl_global_reset(); _asl_global_init(); kstatus = _asl_server_message(_asl_global.server_port, (caddr_t)str, len); if (kstatus != KERN_SUCCESS) { _asl_global_reset(); vm_deallocate(mach_task_self(), (vm_address_t)str, vmsize); outstatus = -1; } } } } /* messages from syslog() get filtered on the way out to stderr */ fd_write = 1; if ((asl->options & ASL_OPT_SYSLOG_LEGACY) && (filter != 0) && ((filter & lmask) == 0)) fd_write = 0; if ((fd_write != 0) && (asl->fd_count > 0)) { if (aux != NULL) { /* write to file descriptors */ for (i = 0; i < asl->fd_count; i++) { char *str; if (asl->fd_list[i] < 0) continue; len = 0; str = asl_format_message(aux, asl->fd_mfmt[i], asl->fd_tfmt[i], asl->fd_encoding[i], &len); if (str == NULL) continue; status = write(asl->fd_list[i], str, len - 1); if (status < 0) { asl->fd_list[i] = -1; outstatus = -1; } free(str); } } } asl_msg_release(aux); if (use_global_lock != 0) pthread_mutex_unlock(&_asl_global.lock); return outstatus; } /* * asl_send: send a message * This routine may be used instead of asl_log() or asl_vlog() if asl_set() * has been used to set all of a message's attributes. * eval: hints about what to do with the message * msg: an aslmsg * returns 0 for success, non-zero for failure */ int asl_send(aslclient ac, aslmsg msg) { int status = 0; uint32_t eval = _asl_evaluate_send(ac, msg, -1); if (eval != 0) status = _asl_send_message(ac, eval, (asl_msg_t *)msg, NULL); return status; } static int _asl_aux_save_context(asl_aux_context_t *ctx) { if (ctx == NULL) return -1; pthread_mutex_lock(&_asl_global.lock); _asl_global.aux_ctx = (asl_aux_context_t **)reallocf(_asl_global.aux_ctx, (_asl_global.aux_count + 1) * sizeof(asl_aux_context_t *)); if (_asl_global.aux_ctx == NULL) { _asl_global.aux_count = 0; return -1; } _asl_global.aux_ctx[_asl_global.aux_count++] = ctx; pthread_mutex_unlock(&_asl_global.lock); return 0; } /* * Creates an auxiliary file that may be used to save arbitrary data. The ASL message msg * will be saved at the time that the auxiliary file is created. The message will include * any keys and values found in msg, and it will include the title and Uniform Type * Identifier specified. Output parameter out_fd will contain the file descriptor of the * new auxiliary file. */ static int _asl_auxiliary(asl_msg_t *msg, const char *title, const char *uti, const char *url, int *out_fd) { asl_msg_t *aux; asl_string_t *send_str; const char *str; fileport_t fileport; kern_return_t kstatus; size_t len, vmsize; uint32_t newurllen, where; int status, fd, fdpair[2]; caddr_t newurl; dispatch_queue_t pipe_q; dispatch_io_t pipe_channel; dispatch_semaphore_t sem; aux = asl_msg_new(ASL_TYPE_MSG); if (title != NULL) { asl_msg_set_key_val(aux, ASL_KEY_AUX_TITLE, title); } if (uti == NULL) { asl_msg_set_key_val(aux, ASL_KEY_AUX_UTI, "public.data"); } else { asl_msg_set_key_val(aux, ASL_KEY_AUX_UTI, uti); } if (url != NULL) { asl_msg_set_key_val(aux, ASL_KEY_AUX_URL, url); } aux = asl_msg_merge(aux, msg); /* if (out_fd == NULL), this is from asl_log_auxiliary_location */ if (out_fd == NULL) { uint32_t eval = _asl_evaluate_send(NULL, (aslmsg)aux, -1); status = _asl_send_message(NULL, eval, aux, NULL); asl_msg_release(aux); return status; } where = asl_store_location(); if (where == ASL_STORE_LOCATION_MEMORY) { /* create a pipe */ asl_aux_context_t *ctx = (asl_aux_context_t *)calloc(1, sizeof(asl_aux_context_t)); if (ctx == NULL) return -1; status = pipe(fdpair); if (status < 0) { free(ctx); return -1; } /* give read end to dispatch_io_read */ fd = fdpair[0]; sem = dispatch_semaphore_create(0); ctx->sem = sem; ctx->fd = fdpair[1]; status = _asl_aux_save_context(ctx); if (status != 0) { close(fdpair[0]); close(fdpair[1]); dispatch_release(sem); free(ctx); return -1; } pipe_q = dispatch_queue_create("PipeQ", NULL); pipe_channel = dispatch_io_create(DISPATCH_IO_STREAM, fd, pipe_q, ^(int err){ close(fd); }); *out_fd = fdpair[1]; dispatch_io_set_low_water(pipe_channel, SIZE_MAX); dispatch_io_read(pipe_channel, 0, SIZE_MAX, pipe_q, ^(bool done, dispatch_data_t pipedata, int err){ if (err == 0) { size_t len = dispatch_data_get_size(pipedata); if (len > 0) { const char *bytes = NULL; char *encoded; uint32_t eval; dispatch_data_t md = dispatch_data_create_map(pipedata, (const void **)&bytes, &len); encoded = asl_core_encode_buffer(bytes, len); asl_msg_set_key_val(aux, ASL_KEY_AUX_DATA, encoded); free(encoded); eval = _asl_evaluate_send(NULL, (aslmsg)aux, -1); _asl_send_message(NULL, eval, aux, NULL); asl_msg_release(aux); dispatch_release(md); } } if (done) { dispatch_semaphore_signal(sem); dispatch_release(pipe_channel); dispatch_release(pipe_q); } }); return 0; } _asl_global_init(); if (_asl_global.server_port == MACH_PORT_NULL) return -1; send_str = asl_msg_to_string_raw(ASL_STRING_MIG, aux, 0); len = asl_string_length(send_str); vmsize = asl_string_allocated_size(send_str); str = asl_string_free_return_bytes(send_str); if (len == 0) { asl_msg_release(aux); vm_deallocate(mach_task_self(), (vm_address_t)str, vmsize); return -1; } status = 0; fileport = MACH_PORT_NULL; status = KERN_SUCCESS; kstatus = _asl_server_create_aux_link(_asl_global.server_port, (caddr_t)str, len, &fileport, &newurl, &newurllen, &status); if (kstatus != KERN_SUCCESS) { /* retry once if the call failed */ _asl_global_reset(); _asl_global_init(); kstatus = _asl_server_create_aux_link(_asl_global.server_port, (caddr_t)str, len, &fileport, &newurl, &newurllen, &status); if (kstatus != KERN_SUCCESS) { _asl_global_reset(); vm_deallocate(mach_task_self(), (vm_address_t)str, vmsize); asl_msg_release(aux); return -1; } } if (status != 0) { asl_msg_release(aux); return status; } if (newurl != NULL) { asl_msg_set_key_val(aux, ASL_KEY_AUX_URL, newurl); vm_deallocate(mach_task_self(), (vm_address_t)newurl, newurllen); } if (fileport == MACH_PORT_NULL) { asl_msg_release(aux); return -1; } fd = fileport_makefd(fileport); mach_port_deallocate(mach_task_self(), fileport); if (fd < 0) { asl_msg_release(aux); status = -1; } else { asl_aux_context_t *ctx = (asl_aux_context_t *)calloc(1, sizeof(asl_aux_context_t)); if (ctx == NULL) { status = -1; } else { *out_fd = fd; ctx->fd = fd; ctx->msg = aux; status = _asl_aux_save_context(ctx); } } return status; } int asl_create_auxiliary_file(aslmsg msg, const char *title, const char *uti, int *out_fd) { if (out_fd == NULL) return -1; return _asl_auxiliary((asl_msg_t *)msg, title, uti, NULL, out_fd); } int asl_log_auxiliary_location(aslmsg msg, const char *title, const char *uti, const char *url) { return _asl_auxiliary((asl_msg_t *)msg, title, uti, url, NULL); } /* * Close an auxiliary file. * Sends the cached auxiliary message to syslogd. */ int asl_close_auxiliary_file(int fd) { int i, j, status; asl_msg_t *aux_msg; dispatch_semaphore_t aux_sem; pthread_mutex_lock(&(_asl_global.lock)); aux_msg = NULL; status = -1; for (i = 0; i < _asl_global.aux_count; i++) { if (_asl_global.aux_ctx[i]->fd == fd) { status = 0; aux_msg = _asl_global.aux_ctx[i]->msg; aux_sem = _asl_global.aux_ctx[i]->sem; free(_asl_global.aux_ctx[i]); for (j = i + 1; j < _asl_global.aux_count; i++, j++) { _asl_global.aux_ctx[i] = _asl_global.aux_ctx[j]; } _asl_global.aux_count--; if (_asl_global.aux_count == 0) { free(_asl_global.aux_ctx); _asl_global.aux_ctx = NULL; } else { _asl_global.aux_ctx = (asl_aux_context_t **)reallocf(_asl_global.aux_ctx, _asl_global.aux_count * sizeof(asl_aux_context_t *)); if (_asl_global.aux_ctx == NULL) { _asl_global.aux_count = 0; status = -1; } } break; } } pthread_mutex_unlock(&(_asl_global.lock)); close(fd); if (aux_msg != NULL) { uint32_t eval = _asl_evaluate_send(NULL, (aslmsg)aux_msg, -1); if (_asl_send_message(NULL, eval, aux_msg, NULL) != ASL_STATUS_OK) status = -1; asl_msg_release(aux_msg); } if (aux_sem != NULL) { dispatch_semaphore_wait(aux_sem, DISPATCH_TIME_FOREVER); dispatch_release(aux_sem); } return status; } /* * asl_search: Search for messages matching the criteria described * by the aslmsg. The caller should set the attributes to match using * asl_set_query() or asl_set(). The operatoin ASL_QUERY_OP_EQUAL is * used for attributes set with asl_set(). * a: an aslmsg * returns: a set of messages that can be iterated over using aslresp_next(), * and the values can be retrieved using aslresp_get. */ /* * This routine searches the ASL datastore on disk (/var/log/asl). * It is called my asl_search if syslogd is not running or if syslogd * indicates that an in-memory store is not being used. */ static aslresponse _asl_search_store(aslclient ac, aslmsg a) { asl_search_result_t query, *out; asl_msg_t *q, *qlist[1]; uint32_t status, op; uint64_t last_id, start_id; asl_store_t *store; const char *val; if (a == NULL) return NULL; q = (asl_msg_t *)a; /* check for "ASLMessageId >[=] n" and set start_id */ start_id = 0; val = NULL; status = asl_msg_lookup(q, ASL_KEY_MSG_ID, &val, &op); if ((status == 0) && (val != NULL) && (op & ASL_QUERY_OP_GREATER)) { if (op & ASL_QUERY_OP_EQUAL) start_id = atoll(val); else start_id = atoll(val) + 1; } store = NULL; status = asl_store_open_read(NULL, &store); if (status != 0) return NULL; if (store == NULL) return NULL; out = NULL; last_id = 0; qlist[0] = (asl_msg_t *)a; memset(&query, 0, sizeof(asl_search_result_t)); query.count = 1; query.msg = qlist; status = asl_store_match(store, &query, &out, &last_id, start_id, 0, 1); asl_store_close(store); return out; } static uint32_t _asl_search_concat_results(asl_search_result_t *batch, asl_search_result_t **out) { uint32_t i, j; if (out == NULL) return ASL_STATUS_FAILED; /* nothing to do if batch is NULL or contains no messages */ if (batch == NULL) return 0; if (batch->count == 0) { aslresponse_free(batch); return 0; } if (*out == NULL) *out = (asl_search_result_t *)calloc(1, sizeof(asl_search_result_t)); if (*out == NULL) { aslresponse_free(batch); return ASL_STATUS_FAILED; } if ((*out)->count == 0) { (*out)->msg = (asl_msg_t **)calloc(batch->count, sizeof(asl_msg_t *)); } else { (*out)->msg = (asl_msg_t **)reallocf((*out)->msg, ((*out)->count + batch->count) * sizeof(asl_msg_t *)); } if ((*out)->msg == NULL) { aslresponse_free(batch); free(*out); *out = NULL; return ASL_STATUS_FAILED; } for (i = 0, j = (*out)->count; i < batch->count; i++, j++) (*out)->msg[j] = batch->msg[i]; (*out)->count += batch->count; free(batch->msg); free(batch); return ASL_STATUS_OK; } static aslresponse _asl_search_memory(aslclient ac, aslmsg a) { asl_search_result_t *batch, *out; char *qstr, *str, *res; uint32_t len, reslen, status; uint64_t cmax, qmin; kern_return_t kstatus; security_token_t sec; caddr_t vmstr; if (a == NULL) return 0; _asl_global_init(); if (_asl_global.server_port == MACH_PORT_NULL) return NULL; len = 0; qstr = asl_msg_to_string((asl_msg_t *)a, &len); str = NULL; if (qstr == NULL) { asprintf(&str, "0\n"); len = 3; } else { asprintf(&str, "1\n%s\n", qstr); len += 3; free(qstr); } if (str == NULL) return NULL; /* * Fetch a batch of results each time through the loop. * Fetching small batches rebuces the load on syslogd. */ out = NULL; qmin = 0; cmax = 0; forever { res = NULL; reslen = 0; sec.val[0] = -1; sec.val[1] = -1; status = ASL_STATUS_OK; kstatus = vm_allocate(mach_task_self(), (vm_address_t *)&vmstr, len, TRUE); if (kstatus != KERN_SUCCESS) return NULL; memmove(vmstr, str, len); status = 0; kstatus = _asl_server_query(_asl_global.server_port, vmstr, len, qmin, FETCH_BATCH, 0, (caddr_t *)&res, &reslen, &cmax, (int *)&status, &sec); if (kstatus != KERN_SUCCESS) { /* retry once if the call failed */ _asl_global_reset(); _asl_global_init(); kstatus = _asl_server_query(_asl_global.server_port, vmstr, len, qmin, FETCH_BATCH, 0, (caddr_t *)&res, &reslen, &cmax, (int *)&status, &sec); if (kstatus != KERN_SUCCESS) { _asl_global_reset(); break; } } if (res == NULL) break; batch = asl_list_from_string(res); vm_deallocate(mach_task_self(), (vm_address_t)res, reslen); status = _asl_search_concat_results(batch, &out); if (status != ASL_STATUS_OK) break; if ((out == NULL) || (out->count < FETCH_BATCH)) break; if (cmax >= qmin) qmin = cmax + 1; } free(str); return out; } int asl_store_location() { kern_return_t kstatus; char *res; uint32_t reslen, status; uint64_t cmax; security_token_t sec; _asl_global_init(); if (_asl_global.server_port == MACH_PORT_NULL) return ASL_STORE_LOCATION_FILE; res = NULL; reslen = 0; cmax = 0; sec.val[0] = -1; sec.val[1] = -1; status = ASL_STATUS_OK; kstatus = _asl_server_query(_asl_global.server_port, NULL, 0, 0, -1, 0, (caddr_t *)&res, &reslen, &cmax, (int *)&status, &sec); if (kstatus != KERN_SUCCESS) { /* retry once if the call failed */ _asl_global_reset(); _asl_global_init(); kstatus = _asl_server_query(_asl_global.server_port, NULL, 0, 0, -1, 0, (caddr_t *)&res, &reslen, &cmax, (int *)&status, &sec); } /* res should never be returned, but just to be certain we don't leak VM ... */ if (res != NULL) vm_deallocate(mach_task_self(), (vm_address_t)res, reslen); if (kstatus != KERN_SUCCESS) { _asl_global_reset(); return ASL_STORE_LOCATION_FILE; } if (status == ASL_STATUS_OK) return ASL_STORE_LOCATION_MEMORY; return ASL_STORE_LOCATION_FILE; } aslresponse asl_search(aslclient ac, aslmsg a) { int where; asl_search_result_t *out; _asl_global_init(); where = asl_store_location(); if (where == ASL_STORE_LOCATION_FILE) out = _asl_search_store(ac, a); else out = _asl_search_memory(ac, a); return out; } int asl_syslog_faciliy_name_to_num(const char *name) { if (name == NULL) return -1; if (strcaseeq(name, "auth")) return LOG_AUTH; if (strcaseeq(name, "authpriv")) return LOG_AUTHPRIV; if (strcaseeq(name, "cron")) return LOG_CRON; if (strcaseeq(name, "daemon")) return LOG_DAEMON; if (strcaseeq(name, "ftp")) return LOG_FTP; if (strcaseeq(name, "install")) return LOG_INSTALL; if (strcaseeq(name, "kern")) return LOG_KERN; if (strcaseeq(name, "lpr")) return LOG_LPR; if (strcaseeq(name, "mail")) return LOG_MAIL; if (strcaseeq(name, "netinfo")) return LOG_NETINFO; if (strcaseeq(name, "remoteauth")) return LOG_REMOTEAUTH; if (strcaseeq(name, "news")) return LOG_NEWS; if (strcaseeq(name, "security")) return LOG_AUTH; if (strcaseeq(name, "syslog")) return LOG_SYSLOG; if (strcaseeq(name, "user")) return LOG_USER; if (strcaseeq(name, "uucp")) return LOG_UUCP; if (strcaseeq(name, "local0")) return LOG_LOCAL0; if (strcaseeq(name, "local1")) return LOG_LOCAL1; if (strcaseeq(name, "local2")) return LOG_LOCAL2; if (strcaseeq(name, "local3")) return LOG_LOCAL3; if (strcaseeq(name, "local4")) return LOG_LOCAL4; if (strcaseeq(name, "local5")) return LOG_LOCAL5; if (strcaseeq(name, "local6")) return LOG_LOCAL6; if (strcaseeq(name, "local7")) return LOG_LOCAL7; if (strcaseeq(name, "launchd")) return LOG_LAUNCHD; return -1; } const char * asl_syslog_faciliy_num_to_name(int n) { if (n < 0) return NULL; if (n == LOG_AUTH) return "auth"; if (n == LOG_AUTHPRIV) return "authpriv"; if (n == LOG_CRON) return "cron"; if (n == LOG_DAEMON) return "daemon"; if (n == LOG_FTP) return "ftp"; if (n == LOG_INSTALL) return "install"; if (n == LOG_KERN) return "kern"; if (n == LOG_LPR) return "lpr"; if (n == LOG_MAIL) return "mail"; if (n == LOG_NETINFO) return "netinfo"; if (n == LOG_REMOTEAUTH) return "remoteauth"; if (n == LOG_NEWS) return "news"; if (n == LOG_AUTH) return "security"; if (n == LOG_SYSLOG) return "syslog"; if (n == LOG_USER) return "user"; if (n == LOG_UUCP) return "uucp"; if (n == LOG_LOCAL0) return "local0"; if (n == LOG_LOCAL1) return "local1"; if (n == LOG_LOCAL2) return "local2"; if (n == LOG_LOCAL3) return "local3"; if (n == LOG_LOCAL4) return "local4"; if (n == LOG_LOCAL5) return "local5"; if (n == LOG_LOCAL6) return "local6"; if (n == LOG_LOCAL7) return "local7"; if (n == LOG_LAUNCHD) return "launchd"; return NULL; } /* * utility for converting a time string into a time_t * we only deal with the following formats: * Canonical form YYYY.MM.DD hh:mm:ss UTC * ctime() form Mth dd hh:mm:ss (e.g. Aug 25 09:54:37) * absolute form - # seconds since the epoch (e.g. 1095789191) * relative time - seconds before or after now (e.g. -300, +43200) * relative time - days/hours/minutes/seconds before or after now (e.g. -1d, +6h, +30m, -10s) */ #define CANONICAL_TIME_REX "^[0-9][0-9][0-9][0-9].[01]?[0-9].[0-3]?[0-9][ ]+[0-2]?[0-9]:[0-5][0-9]:[0-5][0-9][ ]+UTC$" #define CTIME_REX "^[adfjmnos][aceopu][bcglnprtvy][ ]+[0-3]?[0-9][ ]+[0-2]?[0-9]:[0-5][0-9]:[0-5][0-9]$" #define ABSOLUTE_TIME_REX "^[0-9]+[s]?$" #define RELATIVE_TIME_REX "^[\\+-\\][0-9]+[smhdw]?$" #define SECONDS_PER_MINUTE 60 #define SECONDS_PER_HOUR 3600 #define SECONDS_PER_DAY 86400 #define SECONDS_PER_WEEK 604800 static regex_t rex_canon, rex_ctime, rex_abs, rex_rel; static int reg_status = 0; /* * We use the last letter in the month name to determine * the month number (0-11). There are two collisions: * Jan and Jun both end in n * Mar and Apr both end in r * In these cases we check the second letter. * * The MTH_LAST array maps the last letter to a number. */ static const int8_t MTH_LAST[] = {-1, 1, 11, -1, -1, -1, 7, -1, -1, -1, -1, 6, -1, 5, -1, 8, -1, 3, -1, 9, -1, 10, -1, -1, 4, -1}; static int _month_num(char *s) { int i; int8_t v8; v8 = -1; if (s[2] > 90) v8 = s[2] - 'a'; else v8 = s[2] - 'A'; if ((v8 < 0) || (v8 > 25)) return -1; v8 = MTH_LAST[v8]; if (v8 < 0) return -1; i = v8; if ((i == 5) && ((s[1] == 'a') || (s[1] == 'A'))) return 0; if ((i == 3) && ((s[1] == 'a') || (s[1] == 'A'))) return 2; return i; } time_t asl_parse_time(const char *in) { int len, y; struct tm t; time_t tick, delta, factor; char *str, *p, *x; static dispatch_once_t once; if (in == NULL) return -1; dispatch_once(&once, ^{ int status; int rflags = REG_EXTENDED | REG_NOSUB | REG_ICASE; memset(&rex_canon, 0, sizeof(regex_t)); status = regcomp(&rex_canon, CANONICAL_TIME_REX, rflags); if (status != 0) reg_status = -1; memset(&rex_ctime, 0, sizeof(regex_t)); status = regcomp(&rex_ctime, CTIME_REX, rflags); if (status != 0) reg_status = -1; memset(&rex_abs, 0, sizeof(regex_t)); status = regcomp(&rex_abs, ABSOLUTE_TIME_REX, rflags); if (status != 0) reg_status = -1; memset(&rex_rel, 0, sizeof(regex_t)); status = regcomp(&rex_rel, RELATIVE_TIME_REX, rflags); if (status != 0) reg_status = -1; }); if (reg_status < 0) return -1; len = strlen(in) + 1; if (regexec(&rex_abs, in, 0, NULL, 0) == 0) { /* * Absolute time (number of seconds since the epoch) */ str = strdup(in); if (str == NULL) return -1; if ((str[len-2] == 's') || (str[len-2] == 'S')) str[len-2] = '\0'; tick = atol(str); free(str); return tick; } else if (regexec(&rex_rel, in, 0, NULL, 0) == 0) { /* * Reletive time (number of seconds before or after right now) */ str = strdup(in); if (str == NULL) return -1; factor = 1; if ((str[len-2] == 's') || (str[len-2] == 'S')) { str[len-2] = '\0'; } else if ((str[len-2] == 'm') || (str[len-2] == 'M')) { str[len-2] = '\0'; factor = SECONDS_PER_MINUTE; } else if ((str[len-2] == 'h') || (str[len-2] == 'H')) { str[len-2] = '\0'; factor = SECONDS_PER_HOUR; } else if ((str[len-2] == 'd') || (str[len-2] == 'D')) { str[len-2] = '\0'; factor = SECONDS_PER_DAY; } else if ((str[len-2] == 'w') || (str[len-2] == 'W')) { str[len-2] = '\0'; factor = SECONDS_PER_WEEK; } tick = time(NULL); delta = factor * atol(str); tick += delta; free(str); return tick; } else if (regexec(&rex_canon, in, 0, NULL, 0) == 0) { memset(&t, 0, sizeof(struct tm)); str = strdup(in); if (str == NULL) return -1; /* Get year */ x = str; p = strchr(x, '.'); *p = '\0'; t.tm_year = atoi(x) - 1900; /* Get month */ x = p + 1; p = strchr(x, '.'); *p = '\0'; t.tm_mon = atoi(x) - 1; /* Get day */ x = p + 1; p = strchr(x, ' '); *p = '\0'; t.tm_mday = atoi(x); /* Get hour */ for (x = p + 1; *x == ' '; x++); p = strchr(x, ':'); *p = '\0'; t.tm_hour = atoi(x); /* Get minutes */ x = p + 1; p = strchr(x, ':'); *p = '\0'; t.tm_min = atoi(x); /* Get seconds */ x = p + 1; p = strchr(x, ' '); *p = '\0'; t.tm_sec = atoi(x); free(str); return timegm(&t); } else if (regexec(&rex_ctime, in, 0, NULL, 0) == 0) { /* We assume it's in the current year */ memset(&t, 0, sizeof(struct tm)); tick = time(NULL); gmtime_r(&tick, &t); y = t.tm_year; memset(&t, 0, sizeof(struct tm)); str = strdup(in); if (str == NULL) return -1; t.tm_year = y; t.tm_mon = _month_num(str); if (t.tm_mon < 0) return -1; for (x = strchr(str, ' '); *x == ' '; x++); p = strchr(x, ' '); *p = '\0'; t.tm_mday = atoi(x); /* Get hour */ for (x = p + 1; *x == ' '; x++); p = strchr(x, ':'); *p = '\0'; t.tm_hour = atoi(x); /* Get minutes */ x = p + 1; p = strchr(x, ':'); *p = '\0'; t.tm_min = atoi(x); /* Get seconds */ x = p + 1; t.tm_sec = atoi(x); t.tm_isdst = -1; free(str); return mktime(&t); } return -1; } #endif /* BUILDING_VARIANT */