#include <asl_core.h>
#include <asl_legacy1.h>
#include <asl_private.h>
#include <stdlib.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <sys/errno.h>
#include <string.h>
#include <membership.h>
#include <mach/mach.h>
#include <sys/syslimits.h>
#include <sys/types.h>
#include <time.h>
#include <sys/mman.h>
#define forever for(;;)
#define FILE_MODE 0600
#define DB_RECORD_LEN 80
#define DB_HEADER_COOKIE_OFFSET 0
#define DB_HEADER_VERS_OFFSET 12
#define DB_TYPE_EMPTY 0
#define DB_TYPE_HEADER 1
#define DB_TYPE_MESSAGE 2
#define DB_TYPE_KVLIST 3
#define DB_TYPE_STRING 4
#define DB_TYPE_STRCONT 5
#define ASL_DB_COOKIE "ASL DB"
#define ASL_DB_COOKIE_LEN 6
#define ASL_INDEX_NULL 0xffffffff
#define DB_HLEN_EMPTY 0
#define DB_HLEN_HEADER 13
#define DB_HLEN_MESSAGE 13
#define DB_HLEN_KVLIST 9
#define DB_HLEN_STRING 25
#define DB_HLEN_STRCONT 5
#define MSG_OFF_KEY_TYPE 0
#define MSG_OFF_KEY_NEXT 1
#define MSG_OFF_KEY_ID 5
#define MSG_OFF_KEY_RUID 13
#define MSG_OFF_KEY_RGID 17
#define MSG_OFF_KEY_TIME 21
#define MSG_OFF_KEY_HOST 29
#define MSG_OFF_KEY_SENDER 37
#define MSG_OFF_KEY_FACILITY 45
#define MSG_OFF_KEY_LEVEL 53
#define MSG_OFF_KEY_PID 57
#define MSG_OFF_KEY_UID 61
#define MSG_OFF_KEY_GID 65
#define MSG_OFF_KEY_MSG 69
#define MSG_OFF_KEY_FLAGS 77
extern time_t asl_parse_time(const char *str);
extern int asl_msg_cmp(aslmsg a, aslmsg b);
#define asl_msg_list_t asl_search_result_t
#define Q_NULL 100001
#define Q_FAST 100002
#define Q_SLOW 100003
#define Q_FAIL 100004
static uint64_t
_asl_htonq(uint64_t n)
{
#ifdef __BIG_ENDIAN__
return n;
#else
u_int32_t t;
union
{
u_int64_t q;
u_int32_t l[2];
} x;
x.q = n;
t = x.l[0];
x.l[0] = htonl(x.l[1]);
x.l[1] = htonl(t);
return x.q;
#endif
}
static uint64_t
_asl_ntohq(uint64_t n)
{
#ifdef __BIG_ENDIAN__
return n;
#else
u_int32_t t;
union
{
u_int64_t q;
u_int32_t l[2];
} x;
x.q = n;
t = x.l[0];
x.l[0] = ntohl(x.l[1]);
x.l[1] = ntohl(t);
return x.q;
#endif
}
static uint16_t
_asl_get_16(char *h)
{
uint16_t x;
memcpy(&x, h, 2);
return ntohs(x);
}
static uint32_t
_asl_get_32(char *h)
{
uint32_t x;
memcpy(&x, h, 4);
return ntohl(x);
}
static uint64_t
_asl_get_64(char *h)
{
uint64_t x;
memcpy(&x, h, 8);
return _asl_ntohq(x);
}
#define header_get_next(h) _asl_get_32(h + 1)
#define header_get_id(h) _asl_get_64(h + 5)
#define header_get_hash(h) _asl_get_32(h + 17)
static int
slot_comp(const void *a, const void *b)
{
asl_legacy1_slot_info_t *ai, *bi;
if (a == NULL)
{
if (b == NULL) return 0;
return -1;
}
if (b == NULL) return 1;
ai = (asl_legacy1_slot_info_t *)a;
bi = (asl_legacy1_slot_info_t *)b;
if (ai->xid < bi->xid) return -1;
if (ai->xid == bi->xid)
{
if (ai->slot < bi->slot) return -1;
if (ai->slot == bi->slot) return 0;
return 1;
}
return 1;
}
static uint32_t
slotlist_find(asl_legacy1_t *s, uint64_t xid, int32_t direction)
{
uint32_t top, bot, mid, range;
if (s == NULL) return ASL_INDEX_NULL;
if (s->slotlist_count == 0) return ASL_INDEX_NULL;
if (xid == 0) return ASL_INDEX_NULL;
top = s->slotlist_count - 1;
bot = 0;
mid = top / 2;
range = top - bot;
while (range > 1)
{
if (xid == s->slotlist[mid].xid) return mid;
else if (xid < s->slotlist[mid].xid) top = mid;
else bot = mid;
range = top - bot;
mid = bot + (range / 2);
}
if (xid == s->slotlist[top].xid) return top;
if (xid == s->slotlist[bot].xid) return bot;
if (direction == 0) return ASL_INDEX_NULL;
if (direction < 0) return bot;
return top;
}
static uint32_t
slotlist_init(asl_legacy1_t *s, uint32_t count)
{
uint32_t i, si, status, hash, addslot;
uint64_t xid;
uint8_t t;
size_t rcount;
char tmp[DB_RECORD_LEN];
status = fseek(s->db, DB_RECORD_LEN, SEEK_SET);
if (status != 0) return ASL_STATUS_READ_FAILED;
s->slotlist = (asl_legacy1_slot_info_t *)calloc(count, sizeof(asl_legacy1_slot_info_t));
if (s->slotlist == NULL) return ASL_STATUS_NO_MEMORY;
si = 0;
for (i = 1; i < count; i++)
{
rcount = fread(tmp, DB_RECORD_LEN, 1, s->db);
if (rcount != 1) return ASL_STATUS_READ_FAILED;
t = tmp[0];
addslot = 0;
xid = 0;
hash = 0;
if (t == DB_TYPE_EMPTY) addslot = 1;
if (t == DB_TYPE_STRING)
{
addslot = 1;
xid = header_get_id(tmp);
hash = header_get_hash(tmp);
}
if (t == DB_TYPE_MESSAGE)
{
addslot = 1;
xid = header_get_id(tmp);
}
if (addslot == 1)
{
s->slotlist[si].type = t;
s->slotlist[si].slot = i;
s->slotlist[si].xid = xid;
s->slotlist[si].hash = hash;
si++;
}
}
s->slotlist = (asl_legacy1_slot_info_t *)reallocf(s->slotlist, si * sizeof(asl_legacy1_slot_info_t));
if (s->slotlist == NULL) return ASL_STATUS_NO_MEMORY;
s->slotlist_count = si;
qsort((void *)s->slotlist, s->slotlist_count, sizeof(asl_legacy1_slot_info_t), slot_comp);
return ASL_STATUS_OK;
}
uint32_t
asl_legacy1_open(const char *path, asl_legacy1_t **out)
{
asl_legacy1_t *s;
struct stat sb;
int status;
size_t rcount;
char cbuf[DB_RECORD_LEN];
off_t fsize;
uint32_t count;
memset(&sb, 0, sizeof(struct stat));
status = stat(path, &sb);
if (status < 0) return ASL_STATUS_FAILED;
fsize = sb.st_size;
s = (asl_legacy1_t *)calloc(1, sizeof(asl_legacy1_t));
if (s == NULL) return ASL_STATUS_NO_MEMORY;
s->db = fopen(path, "r");
if (s->db == NULL)
{
free(s);
return ASL_STATUS_INVALID_STORE;
}
memset(cbuf, 0, DB_RECORD_LEN);
rcount = fread(cbuf, DB_RECORD_LEN, 1, s->db);
if (rcount != 1)
{
fclose(s->db);
free(s);
return ASL_STATUS_READ_FAILED;
}
if (strncmp(cbuf, ASL_DB_COOKIE, ASL_DB_COOKIE_LEN))
{
fclose(s->db);
free(s);
return ASL_STATUS_INVALID_STORE;
}
count = fsize / DB_RECORD_LEN;
status = slotlist_init(s, count);
*out = s;
return ASL_STATUS_OK;
}
uint32_t
asl_legacy1_close(asl_legacy1_t *s)
{
if (s == NULL) return ASL_STATUS_INVALID_STORE;
if (s->slotlist != NULL) free(s->slotlist);
if (s->db != NULL) fclose(s->db);
free(s);
return ASL_STATUS_OK;
}
static uint32_t
string_fetch_slot(asl_legacy1_t *s, uint32_t slot, char **out)
{
off_t offset;
uint8_t type;
uint32_t next, x, remaining;
size_t rcount, len;
int status;
char *outstr, *p, tmp[DB_RECORD_LEN];
if (s == NULL) return ASL_STATUS_INVALID_STORE;
if (out == NULL) return ASL_STATUS_INVALID_ARG;
*out = NULL;
offset = slot * DB_RECORD_LEN;
status = fseek(s->db, offset, SEEK_SET);
if (status < 0) return ASL_STATUS_READ_FAILED;
rcount = fread(tmp, DB_RECORD_LEN, 1, s->db);
if (rcount != 1) return ASL_STATUS_READ_FAILED;
type = tmp[0];
if (type != DB_TYPE_STRING) return ASL_STATUS_INVALID_STRING;
len = _asl_get_32(tmp + 21);
if (len == 0) return ASL_STATUS_OK;
next = header_get_next(tmp);
outstr = calloc(1, len);
if (outstr == NULL) return ASL_STATUS_NO_MEMORY;
p = outstr;
remaining = len;
x = DB_RECORD_LEN - DB_HLEN_STRING;
if (x > remaining) x = remaining;
memcpy(p, tmp + DB_HLEN_STRING, x);
p += x;
remaining -= x;
while ((next != 0) && (remaining > 0))
{
offset = next * DB_RECORD_LEN;
status = fseek(s->db, offset, SEEK_SET);
if (status < 0)
{
free(outstr);
return ASL_STATUS_READ_FAILED;
}
rcount = fread(tmp, DB_RECORD_LEN, 1, s->db);
if (rcount != 1)
{
free(outstr);
return ASL_STATUS_READ_FAILED;
}
next = header_get_next(tmp);
x = DB_RECORD_LEN - DB_HLEN_STRCONT;
if (x > remaining) x = remaining;
memcpy(p, tmp + DB_HLEN_STRCONT, x);
p += x;
remaining -= x;
}
if ((next != 0) || (remaining != 0))
{
free(outstr);
return ASL_STATUS_READ_FAILED;
}
*out = outstr;
return ASL_STATUS_OK;
}
static uint32_t
string_fetch_sid(asl_legacy1_t *s, uint64_t sid, char **out)
{
uint32_t i, len, ref;
uint64_t nsid;
uint8_t inls;
char *p;
if (s == NULL) return ASL_STATUS_INVALID_STORE;
if (out == NULL) return ASL_STATUS_INVALID_ARG;
*out = NULL;
if (sid == ASL_REF_NULL) return ASL_STATUS_OK;
ref = 0;
inls = 0;
nsid = _asl_htonq(sid);
memcpy(&inls, &nsid, 1);
if (inls & 0x80)
{
inls &= 0x0f;
len = inls;
*out = calloc(1, len);
if (*out == NULL) return ASL_STATUS_NO_MEMORY;
p = 1 + (char *)&nsid;
memcpy(*out, p, len);
return ASL_STATUS_OK;
}
i = slotlist_find(s, sid, 0);
if (i == ASL_INDEX_NULL) return ASL_STATUS_NOT_FOUND;
return string_fetch_slot(s, s->slotlist[i].slot, out);
}
static uint32_t
asl_legacy1_fetch_helper_32(asl_legacy1_t *s, char **p, aslmsg m, const char *key, int ignore, uint32_t ignoreval)
{
uint32_t out, doit;
char str[256];
out = _asl_get_32(*p);
*p += sizeof(uint32_t);
if ((m == NULL) || (key == NULL)) return out;
doit = 1;
if ((ignore != 0) && (out == ignoreval)) doit = 0;
if (doit != 0)
{
snprintf(str, sizeof(str), "%u", out);
asl_set(m, key, str);
}
return out;
}
static uint64_t
asl_legacy1_fetch_helper_64(asl_legacy1_t *s, char **p, aslmsg m, const char *key)
{
uint64_t out;
char str[256];
out = _asl_get_64(*p);
*p += sizeof(uint64_t);
if ((m == NULL) || (key == NULL)) return out;
snprintf(str, sizeof(str), "%llu", out);
asl_set(m, key, str);
return out;
}
static uint64_t
asl_legacy1_fetch_helper_str(asl_legacy1_t *s, char **p, aslmsg m, const char *key, uint32_t *err)
{
uint64_t out;
char *val;
uint32_t status;
out = _asl_get_64(*p);
*p += sizeof(uint64_t);
val = NULL;
status = ASL_STATUS_OK;
if (out != 0) status = string_fetch_sid(s, out, &val);
if (err != NULL) *err = status;
if ((status == ASL_STATUS_OK) && (val != NULL))
{
asl_set(m, key, val);
free(val);
}
return out;
}
static uint32_t
msg_fetch(asl_legacy1_t *s, uint32_t slot, aslmsg *out)
{
off_t offset;
uint32_t status, i, n, kvcount, next;
uint16_t flags;
uint64_t sid;
size_t rcount;
aslmsg msg;
int fstatus;
char *p, tmp[DB_RECORD_LEN], *key, *val;
if (s == NULL) return ASL_STATUS_INVALID_STORE;
if (out == NULL) return ASL_STATUS_INVALID_ARG;
*out = NULL;
offset = slot * DB_RECORD_LEN;
fstatus = fseek(s->db, offset, SEEK_SET);
if (fstatus < 0) return ASL_STATUS_READ_FAILED;
rcount = fread(tmp, DB_RECORD_LEN, 1, s->db);
if (rcount != 1) return ASL_STATUS_READ_FAILED;
flags = _asl_get_16(tmp + MSG_OFF_KEY_FLAGS);
msg = asl_new(ASL_TYPE_MSG);
if (msg == NULL) return ASL_STATUS_NO_MEMORY;
p = tmp + 5;
asl_legacy1_fetch_helper_64(s, &p, msg, ASL_KEY_MSG_ID);
asl_legacy1_fetch_helper_32(s, &p, msg, ASL_KEY_READ_UID, 1, (uint32_t)-1);
asl_legacy1_fetch_helper_32(s, &p, msg, ASL_KEY_READ_GID, 1, (uint32_t)-1);
asl_legacy1_fetch_helper_64(s, &p, msg, ASL_KEY_TIME);
asl_legacy1_fetch_helper_str(s, &p, msg, ASL_KEY_HOST, &status);
asl_legacy1_fetch_helper_str(s, &p, msg, ASL_KEY_SENDER, &status);
asl_legacy1_fetch_helper_str(s, &p, msg, ASL_KEY_FACILITY, &status);
asl_legacy1_fetch_helper_32(s, &p, msg, ASL_KEY_LEVEL, 0, 0);
asl_legacy1_fetch_helper_32(s, &p, msg, ASL_KEY_PID, 0, 0);
asl_legacy1_fetch_helper_32(s, &p, msg, ASL_KEY_UID, 0, 0);
asl_legacy1_fetch_helper_32(s, &p, msg, ASL_KEY_GID, 0, 0);
asl_legacy1_fetch_helper_str(s, &p, msg, ASL_KEY_MSG, &status);
next = header_get_next(tmp);
kvcount = 0;
n = 0;
while (next != 0)
{
offset = next * DB_RECORD_LEN;
fstatus = fseek(s->db, offset, SEEK_SET);
if (fstatus < 0)
{
free(out);
return ASL_STATUS_READ_FAILED;
}
rcount = fread(tmp, DB_RECORD_LEN, 1, s->db);
if (rcount != 1)
{
free(out);
return ASL_STATUS_READ_FAILED;
}
if (kvcount == 0) kvcount = _asl_get_32(tmp + 5);
p = tmp + 9;
for (i = 0; (i < 4) && (n < kvcount); i++)
{
key = NULL;
sid = _asl_get_64(p);
p += 8;
status = string_fetch_sid(s, sid, &key);
val = NULL;
sid = _asl_get_64(p);
p += 8;
if (status == ASL_STATUS_OK) status = string_fetch_sid(s, sid, &val);
if ((status == ASL_STATUS_OK) && (key != NULL)) asl_set(msg, key, val);
if (key != NULL) free(key);
if (val != NULL) free(val);
n++;
}
next = header_get_next(tmp);
}
*out = msg;
return ASL_STATUS_OK;
}
uint32_t
asl_legacy1_fetch(asl_legacy1_t *s, uint64_t msgid, aslmsg *out)
{
uint32_t i, status;
if (s == NULL) return ASL_STATUS_INVALID_STORE;
if (msgid == ASL_REF_NULL) return ASL_STATUS_INVALID_ARG;
if (out == NULL) return ASL_STATUS_INVALID_ARG;
i = slotlist_find(s, msgid, 0);
if (i == ASL_INDEX_NULL) return ASL_STATUS_INVALID_ID;
status = msg_fetch(s, s->slotlist[i].slot, out);
if (status != ASL_STATUS_OK) return status;
if (*out == NULL) return ASL_STATUS_FAILED;
return status;
}
static uint32_t
next_search_slot(asl_legacy1_t *s, uint32_t last_si, int32_t direction)
{
uint32_t i;
if (direction >= 0)
{
for (i = last_si + 1; i < s->slotlist_count; i++)
{
if (s->slotlist[i].type == DB_TYPE_MESSAGE) return i;
}
return ASL_INDEX_NULL;
}
if (last_si == 0) return ASL_INDEX_NULL;
if (last_si > s->slotlist_count) return ASL_INDEX_NULL;
for (i = last_si - 1; i > 0; i--)
{
if (s->slotlist[i].type == DB_TYPE_MESSAGE) return i;
}
if (s->slotlist[0].type == DB_TYPE_MESSAGE) return 0;
return ASL_INDEX_NULL;
}
static void
match_worker_cleanup(asl_msg_list_t **res)
{
uint32_t i;
if (res != NULL)
{
for (i = 0; i < (*res)->count; i++) asl_free((aslmsg)(*res)->msg[i]);
free(*res);
}
}
static uint32_t
match_worker(asl_legacy1_t *s, asl_msg_list_t *query, asl_msg_list_t **res, uint64_t *last_id, uint64_t **idlist, uint32_t *idcount, uint64_t start_id, int32_t count, int32_t direction)
{
uint32_t mx, si, slot, i, qcount, match, didmatch, status;
uint64_t xid;
aslmsg msg;
if (s == NULL) return ASL_STATUS_INVALID_STORE;
if ((res == NULL) && (idlist == NULL)) return ASL_STATUS_INVALID_ARG;
if (last_id == NULL) return ASL_STATUS_INVALID_ARG;
if (idcount == NULL) return ASL_STATUS_INVALID_ARG;
if (res != NULL) *res = NULL;
if (idlist != NULL) *idlist = NULL;
mx = 0;
if (direction < 0) direction = -1;
else direction = 1;
si = ASL_INDEX_NULL;
if ((direction == -1) && (start_id == ASL_REF_NULL)) si = s->slotlist_count;
else si = slotlist_find(s, start_id, direction);
si = next_search_slot(s, si, direction);
if (si == ASL_INDEX_NULL) return ASL_STATUS_OK;
if (si >= s->slotlist_count) return ASL_STATUS_FAILED;
slot = s->slotlist[si].slot;
match = 1;
qcount = 0;
if (query == NULL) match = 0;
else if (query->count == 0) match = 0;
else qcount = query->count;
if (res != NULL)
{
*res = (asl_msg_list_t *)calloc(1, sizeof(asl_msg_list_t));
if (*res == NULL) return ASL_STATUS_NO_MEMORY;
}
status = ASL_STATUS_OK;
*idcount = 0;
while ((count == 0) || (*idcount < count))
{
if (si == ASL_INDEX_NULL) break;
if (si >= s->slotlist_count) break;
slot = s->slotlist[si].slot;
xid = s->slotlist[si].xid;
*last_id = xid;
status = msg_fetch(s, slot, &msg);
didmatch = 0;
if (match == 0)
{
didmatch = 1;
}
else
{
for (i = 0; i < qcount; i++)
{
didmatch = asl_msg_cmp((aslmsg)(query->msg[i]), msg);
if (didmatch == 1) break;
}
}
if (didmatch == 1)
{
if ((*res)->count == 0) (*res)->msg = (asl_msg_t **)calloc(1, sizeof(asl_msg_t *));
else (*res)->msg = (asl_msg_t **)reallocf((*res)->msg, (1 + (*res)->count) * sizeof(asl_msg_t *));
if ((*res)->msg == NULL)
{
match_worker_cleanup(res);
return ASL_STATUS_NO_MEMORY;
}
(*res)->msg[(*res)->count++] = (asl_msg_t *)msg;
}
else
{
asl_free(msg);
}
si = next_search_slot(s, si, direction);
}
return status;
}
uint32_t
asl_legacy1_match(asl_legacy1_t *s, asl_msg_list_t *query, asl_msg_list_t **res, uint64_t *last_id, uint64_t start_id, uint32_t count, int32_t direction)
{
uint32_t idcount;
idcount = 0;
return match_worker(s, query, res, last_id, NULL, &idcount, start_id, count, direction);
}