#include <assert.h>
#include <string.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <unistd.h>
#include <stdarg.h>
#include <syslog.h>
#include <errno.h>
#include <limits.h>
#include <time.h>
#include <sys/time.h>
#include <sys/fcntl.h>
#include <crt_externs.h>
#include <asl.h>
#include <regex.h>
#include <notify.h>
#include <mach/mach.h>
#include <mach/std_types.h>
#include <mach/mig.h>
#include <mach/mach_types.h>
#include <sys/types.h>
#include <servers/bootstrap.h>
#include <pthread.h>
#include <dispatch/dispatch.h>
#include <libkern/OSAtomic.h>
#include <asl_ipc.h>
#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
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_extern__ uint32_t asl_file_open_write_fd(int fd, asl_file_t **s);
__private_extern__ asl_string_t *asl_msg_to_string_raw(uint32_t encoding, asl_msg_t *msg, int tf);
uint32_t notify_register_plain(const char *name, int *out_token);
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"
__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);
}
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, ^{
_asl_global.asl = asl_open(NULL, NULL, ASL_OPT_NO_REMOTE);
if (_asl_global.asl != NULL) _asl_global.asl->options = 0;
_asl_notify_open(0);
});
return _asl_global.asl;
}
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)
{
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);
}
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;
}
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)
{
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;
if (_asl_global.master_filter != 0)
{
filter = _asl_global.master_filter;
tunnel = 1;
}
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
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)
{
asl = _asl_open_default();
if (asl == NULL) return -1;
}
if (format == NULL) return -1;
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;
}
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);
}
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;
}
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
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)
{
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;
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);
status = asl_msg_lookup((asl_msg_t *)msg, ASL_KEY_SENDER, &val, NULL);
if ((status != 0) || (val == NULL))
{
if ((ac != NULL) && (ac->name != NULL))
{
asl_msg_set_key_val(aux, ASL_KEY_SENDER, ac->name);
}
else
{
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");
}
}
status = asl_msg_lookup((asl_msg_t *)msg, ASL_KEY_FACILITY, &val, NULL);
if ((status != 0) || (val == NULL))
{
if ((ac != NULL) && (ac->facility != NULL))
{
asl_msg_set_key_val(aux, ASL_KEY_FACILITY, ac->facility);
}
}
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 (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)
{
kstatus = _asl_server_message(_asl_global.server_port, (caddr_t)str, len);
if (kstatus != KERN_SUCCESS)
{
_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;
}
}
}
}
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)
{
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;
}
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;
}
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)
{
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)
{
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;
}
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)
{
_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);
}
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;
}
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;
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;
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;
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)
{
_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)
{
_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);
}
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;
}
#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;
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)
{
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)
{
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;
x = str;
p = strchr(x, '.');
*p = '\0';
t.tm_year = atoi(x) - 1900;
x = p + 1;
p = strchr(x, '.');
*p = '\0';
t.tm_mon = atoi(x) - 1;
x = p + 1;
p = strchr(x, ' ');
*p = '\0';
t.tm_mday = atoi(x);
for (x = p + 1; *x == ' '; x++);
p = strchr(x, ':');
*p = '\0';
t.tm_hour = atoi(x);
x = p + 1;
p = strchr(x, ':');
*p = '\0';
t.tm_min = atoi(x);
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)
{
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);
for (x = p + 1; *x == ' '; x++);
p = strchr(x, ':');
*p = '\0';
t.tm_hour = atoi(x);
x = p + 1;
p = strchr(x, ':');
*p = '\0';
t.tm_min = atoi(x);
x = p + 1;
t.tm_sec = atoi(x);
t.tm_isdst = -1;
free(str);
return mktime(&t);
}
return -1;
}
#endif