1/*
2 * Copyright (c) 2007-2011 Apple Inc.  All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@ */
22
23#include <asl_core.h>
24#include <asl_legacy1.h>
25#include <asl_private.h>
26#include <stdlib.h>
27#include <sys/file.h>
28#include <sys/stat.h>
29#include <sys/errno.h>
30#include <string.h>
31#include <membership.h>
32#include <mach/mach.h>
33#include <sys/syslimits.h>
34#include <sys/types.h>
35#include <time.h>
36#include <sys/mman.h>
37
38#define forever for(;;)
39
40#define FILE_MODE 0600
41
42#define DB_RECORD_LEN 80
43
44#define DB_HEADER_COOKIE_OFFSET 0
45#define DB_HEADER_VERS_OFFSET 12
46
47#define DB_TYPE_EMPTY   0
48#define DB_TYPE_HEADER  1
49#define DB_TYPE_MESSAGE 2
50#define DB_TYPE_KVLIST  3
51#define DB_TYPE_STRING  4
52#define DB_TYPE_STRCONT 5
53
54/*
55 * Magic Cookie for database files.
56 * MAXIMUM 12 CHARS! (DB_HEADER_VERS_OFFSET)
57 */
58#define ASL_DB_COOKIE "ASL DB"
59#define ASL_DB_COOKIE_LEN 6
60
61#define ASL_INDEX_NULL 0xffffffff
62
63#define DB_HLEN_EMPTY    0
64#define DB_HLEN_HEADER  13
65#define DB_HLEN_MESSAGE 13
66#define DB_HLEN_KVLIST   9
67#define DB_HLEN_STRING  25
68#define DB_HLEN_STRCONT  5
69
70#define MSG_OFF_KEY_TYPE 0
71#define MSG_OFF_KEY_NEXT 1
72#define MSG_OFF_KEY_ID 5
73#define MSG_OFF_KEY_RUID 13
74#define MSG_OFF_KEY_RGID 17
75#define MSG_OFF_KEY_TIME 21
76#define MSG_OFF_KEY_HOST 29
77#define MSG_OFF_KEY_SENDER 37
78#define MSG_OFF_KEY_FACILITY 45
79#define MSG_OFF_KEY_LEVEL 53
80#define MSG_OFF_KEY_PID 57
81#define MSG_OFF_KEY_UID 61
82#define MSG_OFF_KEY_GID 65
83#define MSG_OFF_KEY_MSG 69
84#define MSG_OFF_KEY_FLAGS 77
85
86extern time_t asl_parse_time(const char *str);
87extern int asl_msg_cmp(aslmsg a, aslmsg b);
88
89#define asl_msg_list_t asl_search_result_t
90
91#define Q_NULL 100001
92#define Q_FAST 100002
93#define Q_SLOW 100003
94#define Q_FAIL 100004
95
96static uint64_t
97_asl_htonq(uint64_t n)
98{
99#ifdef __BIG_ENDIAN__
100	return n;
101#else
102	u_int32_t t;
103	union
104	{
105		u_int64_t q;
106		u_int32_t l[2];
107	} x;
108
109	x.q = n;
110	t = x.l[0];
111	x.l[0] = htonl(x.l[1]);
112	x.l[1] = htonl(t);
113
114	return x.q;
115#endif
116}
117
118static uint64_t
119_asl_ntohq(uint64_t n)
120{
121#ifdef __BIG_ENDIAN__
122	return n;
123#else
124	u_int32_t t;
125	union
126	{
127		u_int64_t q;
128		u_int32_t l[2];
129	} x;
130
131	x.q = n;
132	t = x.l[0];
133	x.l[0] = ntohl(x.l[1]);
134	x.l[1] = ntohl(t);
135
136	return x.q;
137#endif
138}
139
140static uint16_t
141_asl_get_16(char *h)
142{
143	uint16_t x;
144
145	memcpy(&x, h, 2);
146	return ntohs(x);
147}
148
149static uint32_t
150_asl_get_32(char *h)
151{
152	uint32_t x;
153
154	memcpy(&x, h, 4);
155	return ntohl(x);
156}
157
158static uint64_t
159_asl_get_64(char *h)
160{
161	uint64_t x;
162
163	memcpy(&x, h, 8);
164	return _asl_ntohq(x);
165}
166
167#define header_get_next(h)		_asl_get_32(h +  1)
168#define header_get_id(h)		_asl_get_64(h +  5)
169#define header_get_hash(h)		_asl_get_32(h + 17)
170
171/*
172 * callback for sorting slotlist
173 * primary sort is by xid
174 * secondary sort is by slot, which happens when xid is 0
175 * this allows us to quickly find xids (using binary search on the xid key)
176 * it's also used to find slots quickly from record_chain_free()
177 */
178static int
179slot_comp(const void *a, const void *b)
180{
181	asl_legacy1_slot_info_t *ai, *bi;
182
183	if (a == NULL)
184	{
185		if (b == NULL) return 0;
186		return -1;
187	}
188
189	if (b == NULL) return 1;
190
191	ai = (asl_legacy1_slot_info_t *)a;
192	bi = (asl_legacy1_slot_info_t *)b;
193
194	if (ai->xid < bi->xid) return -1;
195
196	if (ai->xid == bi->xid)
197	{
198		if (ai->slot < bi->slot) return -1;
199		if (ai->slot == bi->slot) return 0;
200		return 1;
201	}
202
203	return 1;
204}
205
206/* find an xid in the slot list */
207static uint32_t
208slotlist_find(asl_legacy1_t *s, uint64_t xid, int32_t direction)
209{
210	uint32_t top, bot, mid, range;
211
212	if (s == NULL) return ASL_INDEX_NULL;
213	if (s->slotlist_count == 0) return ASL_INDEX_NULL;
214	if (xid == 0) return ASL_INDEX_NULL;
215
216	top = s->slotlist_count - 1;
217	bot = 0;
218	mid = top / 2;
219
220	range = top - bot;
221	while (range > 1)
222	{
223		if (xid == s->slotlist[mid].xid) return mid;
224		else if (xid < s->slotlist[mid].xid) top = mid;
225		else bot = mid;
226
227		range = top - bot;
228		mid = bot + (range / 2);
229	}
230
231	if (xid == s->slotlist[top].xid) return top;
232	if (xid == s->slotlist[bot].xid) return bot;
233
234	if (direction == 0) return ASL_INDEX_NULL;
235	if (direction < 0) return bot;
236	return top;
237}
238
239static uint32_t
240slotlist_init(asl_legacy1_t *s, uint32_t count)
241{
242	uint32_t i, si, status, hash, addslot;
243	uint64_t xid;
244	uint8_t t;
245	size_t rcount;
246	char tmp[DB_RECORD_LEN];
247
248	/* Start at first slot after the header */
249	status = fseek(s->db, DB_RECORD_LEN, SEEK_SET);
250	if (status != 0) return ASL_STATUS_READ_FAILED;
251
252	s->slotlist = (asl_legacy1_slot_info_t *)calloc(count, sizeof(asl_legacy1_slot_info_t));
253	if (s->slotlist == NULL) return ASL_STATUS_NO_MEMORY;
254
255	si = 0;
256
257	for (i = 1; i < count; i++)
258	{
259		rcount = fread(tmp, DB_RECORD_LEN, 1, s->db);
260		if (rcount != 1) return ASL_STATUS_READ_FAILED;
261
262		t = tmp[0];
263		addslot = 0;
264		xid = 0;
265		hash = 0;
266
267		if (t == DB_TYPE_EMPTY) addslot = 1;
268
269		if (t == DB_TYPE_STRING)
270		{
271			addslot = 1;
272			xid = header_get_id(tmp);
273			hash = header_get_hash(tmp);
274		}
275
276		if (t == DB_TYPE_MESSAGE)
277		{
278			addslot = 1;
279			xid = header_get_id(tmp);
280		}
281
282		if (addslot == 1)
283		{
284			s->slotlist[si].type = t;
285			s->slotlist[si].slot = i;
286			s->slotlist[si].xid = xid;
287			s->slotlist[si].hash = hash;
288			si++;
289		}
290	}
291
292	s->slotlist = (asl_legacy1_slot_info_t *)reallocf(s->slotlist, si * sizeof(asl_legacy1_slot_info_t));
293	if (s->slotlist == NULL) return ASL_STATUS_NO_MEMORY;
294	s->slotlist_count = si;
295
296	/* slotlist is sorted by xid */
297	qsort((void *)s->slotlist, s->slotlist_count, sizeof(asl_legacy1_slot_info_t), slot_comp);
298
299	return ASL_STATUS_OK;
300}
301
302uint32_t
303asl_legacy1_open(const char *path, asl_legacy1_t **out)
304{
305	asl_legacy1_t *s;
306	struct stat sb;
307	int status;
308	size_t rcount;
309	char cbuf[DB_RECORD_LEN];
310	off_t fsize;
311	uint32_t count;
312
313	memset(&sb, 0, sizeof(struct stat));
314	status = stat(path, &sb);
315	if (status < 0) return ASL_STATUS_FAILED;
316
317	fsize = sb.st_size;
318
319	s = (asl_legacy1_t *)calloc(1, sizeof(asl_legacy1_t));
320	if (s == NULL) return ASL_STATUS_NO_MEMORY;
321
322	s->db = fopen(path, "r");
323	if (s->db == NULL)
324	{
325		free(s);
326		return ASL_STATUS_INVALID_STORE;
327	}
328
329	memset(cbuf, 0, DB_RECORD_LEN);
330	rcount = fread(cbuf, DB_RECORD_LEN, 1, s->db);
331	if (rcount != 1)
332	{
333		fclose(s->db);
334		free(s);
335		return ASL_STATUS_READ_FAILED;
336	}
337
338	/* Check the database Magic Cookie */
339	if (strncmp(cbuf, ASL_DB_COOKIE, ASL_DB_COOKIE_LEN))
340	{
341		fclose(s->db);
342		free(s);
343		return ASL_STATUS_INVALID_STORE;
344	}
345
346	count = fsize / DB_RECORD_LEN;
347
348	status = slotlist_init(s, count);
349
350	*out = s;
351	return ASL_STATUS_OK;
352}
353
354uint32_t
355asl_legacy1_close(asl_legacy1_t *s)
356{
357	if (s == NULL) return ASL_STATUS_INVALID_STORE;
358
359	if (s->slotlist != NULL) free(s->slotlist);
360	if (s->db != NULL) fclose(s->db);
361	free(s);
362
363	return ASL_STATUS_OK;
364}
365
366static uint32_t
367string_fetch_slot(asl_legacy1_t *s, uint32_t slot, char **out)
368{
369	off_t offset;
370	uint8_t type;
371	uint32_t next, x, remaining;
372	size_t rcount, len;
373	int status;
374	char *outstr, *p, tmp[DB_RECORD_LEN];
375
376	if (s == NULL) return ASL_STATUS_INVALID_STORE;
377	if (out == NULL) return ASL_STATUS_INVALID_ARG;
378
379	*out = NULL;
380	offset = slot * DB_RECORD_LEN;
381	status = fseek(s->db, offset, SEEK_SET);
382
383	if (status < 0) return ASL_STATUS_READ_FAILED;
384
385	rcount = fread(tmp, DB_RECORD_LEN, 1, s->db);
386	if (rcount != 1) return ASL_STATUS_READ_FAILED;
387
388	type = tmp[0];
389	if (type != DB_TYPE_STRING) return ASL_STATUS_INVALID_STRING;
390
391	len = _asl_get_32(tmp + 21);
392	if (len == 0) return ASL_STATUS_OK;
393
394	next = header_get_next(tmp);
395
396	outstr = calloc(1, len);
397	if (outstr == NULL) return ASL_STATUS_NO_MEMORY;
398
399	p = outstr;
400	remaining = len;
401
402	x = DB_RECORD_LEN - DB_HLEN_STRING;
403	if (x > remaining) x = remaining;
404
405	memcpy(p, tmp + DB_HLEN_STRING, x);
406	p += x;
407	remaining -= x;
408
409	while ((next != 0) && (remaining > 0))
410	{
411		offset = next * DB_RECORD_LEN;
412		status = fseek(s->db, offset, SEEK_SET);
413
414		if (status < 0)
415		{
416			free(outstr);
417			return ASL_STATUS_READ_FAILED;
418		}
419
420		rcount = fread(tmp, DB_RECORD_LEN, 1, s->db);
421		if (rcount != 1)
422		{
423			free(outstr);
424			return ASL_STATUS_READ_FAILED;
425		}
426
427		next = header_get_next(tmp);
428
429		x = DB_RECORD_LEN - DB_HLEN_STRCONT;
430		if (x > remaining) x = remaining;
431
432		memcpy(p, tmp + DB_HLEN_STRCONT, x);
433		p += x;
434		remaining -= x;
435	}
436
437	if ((next != 0) || (remaining != 0))
438	{
439		free(outstr);
440		return ASL_STATUS_READ_FAILED;
441	}
442
443	*out = outstr;
444	return ASL_STATUS_OK;
445}
446
447static uint32_t
448string_fetch_sid(asl_legacy1_t *s, uint64_t sid, char **out)
449{
450	uint32_t i, len, ref;
451	uint64_t nsid;
452	uint8_t inls;
453	char *p;
454
455	if (s == NULL) return ASL_STATUS_INVALID_STORE;
456	if (out == NULL) return ASL_STATUS_INVALID_ARG;
457
458	*out = NULL;
459	if (sid == ASL_REF_NULL) return ASL_STATUS_OK;
460
461	ref = 0;
462
463	inls = 0;
464	nsid = _asl_htonq(sid);
465	memcpy(&inls, &nsid, 1);
466	if (inls & 0x80)
467	{
468		/* inline string */
469		inls &= 0x0f;
470		len = inls;
471		*out = calloc(1, len);
472		if (*out == NULL) return ASL_STATUS_NO_MEMORY;
473		p = 1 + (char *)&nsid;
474		memcpy(*out, p, len);
475		return ASL_STATUS_OK;
476	}
477
478	/* Find the string in the database */
479	i = slotlist_find(s, sid, 0);
480	if (i == ASL_INDEX_NULL) return ASL_STATUS_NOT_FOUND;
481
482	return string_fetch_slot(s, s->slotlist[i].slot, out);
483}
484
485static uint32_t
486asl_legacy1_fetch_helper_32(asl_legacy1_t *s, char **p, aslmsg m, const char *key, int ignore, uint32_t ignoreval)
487{
488	uint32_t out, doit;
489	char str[256];
490
491	out = _asl_get_32(*p);
492	*p += sizeof(uint32_t);
493
494	if ((m == NULL) || (key == NULL)) return out;
495
496	doit = 1;
497	if ((ignore != 0) && (out == ignoreval)) doit = 0;
498	if (doit != 0)
499	{
500		snprintf(str, sizeof(str), "%u", out);
501		asl_set(m, key, str);
502	}
503
504	return out;
505}
506
507static uint64_t
508asl_legacy1_fetch_helper_64(asl_legacy1_t *s, char **p, aslmsg m, const char *key)
509{
510	uint64_t out;
511	char str[256];
512
513	out = _asl_get_64(*p);
514	*p += sizeof(uint64_t);
515
516	if ((m == NULL) || (key == NULL)) return out;
517
518	snprintf(str, sizeof(str), "%llu", out);
519	asl_set(m, key, str);
520
521	return out;
522}
523
524static uint64_t
525asl_legacy1_fetch_helper_str(asl_legacy1_t *s, char **p, aslmsg m, const char *key, uint32_t *err)
526{
527	uint64_t out;
528	char *val;
529	uint32_t status;
530
531	out = _asl_get_64(*p);
532	*p += sizeof(uint64_t);
533
534	val = NULL;
535	status = ASL_STATUS_OK;
536	if (out != 0) status = string_fetch_sid(s, out, &val);
537
538	if (err != NULL) *err = status;
539	if ((status == ASL_STATUS_OK) && (val != NULL))
540	{
541		asl_set(m, key, val);
542		free(val);
543	}
544
545	return out;
546}
547
548static uint32_t
549msg_fetch(asl_legacy1_t *s, uint32_t slot, aslmsg *out)
550{
551	off_t offset;
552	uint32_t status, i, n, kvcount, next;
553	uint16_t flags;
554	uint64_t sid;
555	size_t rcount;
556	aslmsg msg;
557	int fstatus;
558	char *p, tmp[DB_RECORD_LEN], *key, *val;
559
560	if (s == NULL) return ASL_STATUS_INVALID_STORE;
561	if (out == NULL) return ASL_STATUS_INVALID_ARG;
562
563	*out = NULL;
564
565	offset = slot * DB_RECORD_LEN;
566	fstatus = fseek(s->db, offset, SEEK_SET);
567
568	if (fstatus < 0) return ASL_STATUS_READ_FAILED;
569
570	rcount = fread(tmp, DB_RECORD_LEN, 1, s->db);
571	if (rcount != 1) return ASL_STATUS_READ_FAILED;
572
573	flags = _asl_get_16(tmp + MSG_OFF_KEY_FLAGS);
574
575	msg = asl_new(ASL_TYPE_MSG);
576	if (msg == NULL) return ASL_STATUS_NO_MEMORY;
577
578	p = tmp + 5;
579
580	asl_legacy1_fetch_helper_64(s, &p, msg, ASL_KEY_MSG_ID);
581	asl_legacy1_fetch_helper_32(s, &p, msg, ASL_KEY_READ_UID, 1, (uint32_t)-1);
582	asl_legacy1_fetch_helper_32(s, &p, msg, ASL_KEY_READ_GID, 1, (uint32_t)-1);
583	asl_legacy1_fetch_helper_64(s, &p, msg, ASL_KEY_TIME);
584	asl_legacy1_fetch_helper_str(s, &p, msg, ASL_KEY_HOST, &status);
585	asl_legacy1_fetch_helper_str(s, &p, msg, ASL_KEY_SENDER, &status);
586	asl_legacy1_fetch_helper_str(s, &p, msg, ASL_KEY_FACILITY, &status);
587	asl_legacy1_fetch_helper_32(s, &p, msg, ASL_KEY_LEVEL, 0, 0);
588	asl_legacy1_fetch_helper_32(s, &p, msg, ASL_KEY_PID, 0, 0);
589	asl_legacy1_fetch_helper_32(s, &p, msg, ASL_KEY_UID, 0, 0);
590	asl_legacy1_fetch_helper_32(s, &p, msg, ASL_KEY_GID, 0, 0);
591	asl_legacy1_fetch_helper_str(s, &p, msg, ASL_KEY_MSG, &status);
592
593	next = header_get_next(tmp);
594
595	kvcount = 0;
596	n = 0;
597
598	while (next != 0)
599	{
600		offset = next * DB_RECORD_LEN;
601		fstatus = fseek(s->db, offset, SEEK_SET);
602		if (fstatus < 0)
603		{
604			free(out);
605			return ASL_STATUS_READ_FAILED;
606		}
607
608		rcount = fread(tmp, DB_RECORD_LEN, 1, s->db);
609		if (rcount != 1)
610		{
611			free(out);
612			return ASL_STATUS_READ_FAILED;
613		}
614
615		if (kvcount == 0) kvcount = _asl_get_32(tmp + 5);
616
617		p = tmp + 9;
618
619		for (i = 0; (i < 4) && (n < kvcount); i++)
620		{
621			key = NULL;
622			sid = _asl_get_64(p);
623			p += 8;
624			status = string_fetch_sid(s, sid, &key);
625
626			val = NULL;
627			sid = _asl_get_64(p);
628			p += 8;
629			if (status == ASL_STATUS_OK) status = string_fetch_sid(s, sid, &val);
630
631			if ((status == ASL_STATUS_OK) && (key != NULL)) asl_set(msg, key, val);
632			if (key != NULL) free(key);
633			if (val != NULL) free(val);
634
635			n++;
636		}
637
638		next = header_get_next(tmp);
639	}
640
641	*out = msg;
642	return ASL_STATUS_OK;
643}
644
645uint32_t
646asl_legacy1_fetch(asl_legacy1_t *s, uint64_t msgid, aslmsg *out)
647{
648	uint32_t i, status;
649
650	if (s == NULL) return ASL_STATUS_INVALID_STORE;
651	if (msgid == ASL_REF_NULL) return ASL_STATUS_INVALID_ARG;
652	if (out == NULL) return ASL_STATUS_INVALID_ARG;
653
654	i = slotlist_find(s, msgid, 0);
655	if (i == ASL_INDEX_NULL) return ASL_STATUS_INVALID_ID;
656
657	/* read the message */
658	status = msg_fetch(s, s->slotlist[i].slot, out);
659	if (status != ASL_STATUS_OK) return status;
660	if (*out == NULL) return ASL_STATUS_FAILED;
661
662	return status;
663}
664
665static uint32_t
666next_search_slot(asl_legacy1_t *s, uint32_t last_si, int32_t direction)
667{
668	uint32_t i;
669
670	if (direction >= 0)
671	{
672		for (i = last_si + 1; i < s->slotlist_count; i++)
673		{
674			if (s->slotlist[i].type == DB_TYPE_MESSAGE) return i;
675		}
676
677		return ASL_INDEX_NULL;
678	}
679
680	if (last_si == 0) return ASL_INDEX_NULL;
681	if (last_si > s->slotlist_count) return ASL_INDEX_NULL;
682
683	for (i = last_si - 1; i > 0; i--)
684	{
685		if (s->slotlist[i].type == DB_TYPE_MESSAGE) return i;
686	}
687
688	if (s->slotlist[0].type == DB_TYPE_MESSAGE) return 0;
689
690	return ASL_INDEX_NULL;
691}
692
693static void
694match_worker_cleanup(asl_msg_list_t **res)
695{
696	uint32_t i;
697
698	if (res != NULL)
699	{
700		for (i = 0; i < (*res)->count; i++) asl_free((aslmsg)(*res)->msg[i]);
701		free(*res);
702	}
703}
704
705/*
706 * Input to asl_legacy1_match is a list of queries.
707 * A record in the store matches if it matches any query (i.e. query list is "OR"ed)
708 *
709 * If counting up (direction is positive) find first record with ID > start_id.
710 * Else if counting down (direction is negative) find first record with ID < start_id.
711 *
712 * Set match flag on.
713 * If any query is NULL, set match flog off (skips matching below).
714 * Else if all queries only check "standard" keys, set std flag to on.
715 *
716 * If all queries are marked as "never matches", return NULL.
717 *
718 * match loop:
719 *  fetch record (with std flag)
720 *  if match flag is off, decode record and add it to result.
721 *  else for each query:
722 *    if query is NULL (shouldn't happen) decode record and add it to result.  Return to match loop.
723 *    else if query never matches, ignore it.
724 *    else decode record and use asl_cmp.  If it succeeds, add record to result.  Return to match loop.
725 *
726 * return results.
727 */
728static uint32_t
729match_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)
730{
731	uint32_t mx, si, slot, i, qcount, match, didmatch, status;
732	uint64_t xid;
733	aslmsg msg;
734
735	if (s == NULL) return ASL_STATUS_INVALID_STORE;
736	if ((res == NULL) && (idlist == NULL)) return ASL_STATUS_INVALID_ARG;
737	if (last_id == NULL) return ASL_STATUS_INVALID_ARG;
738	if (idcount == NULL) return ASL_STATUS_INVALID_ARG;
739
740	if (res != NULL) *res = NULL;
741	if (idlist != NULL) *idlist = NULL;
742
743	mx = 0;
744
745	if (direction < 0) direction = -1;
746	else direction = 1;
747
748	si = ASL_INDEX_NULL;
749	if ((direction == -1) && (start_id == ASL_REF_NULL)) si = s->slotlist_count;
750	else si = slotlist_find(s, start_id, direction);
751
752	si = next_search_slot(s, si, direction);
753	if (si == ASL_INDEX_NULL) return ASL_STATUS_OK;
754	if (si >= s->slotlist_count) return ASL_STATUS_FAILED;
755
756	slot = s->slotlist[si].slot;
757
758	match = 1;
759	qcount = 0;
760
761	if (query == NULL) match = 0;
762	else if (query->count == 0) match = 0;
763	else qcount = query->count;
764
765	/*
766	 * initialize result list if we've been asked to return messages
767	 */
768	if (res != NULL)
769	{
770		*res = (asl_msg_list_t *)calloc(1, sizeof(asl_msg_list_t));
771		if (*res == NULL) return ASL_STATUS_NO_MEMORY;
772	}
773
774	status = ASL_STATUS_OK;
775
776	/*
777	 * loop through records
778	 */
779	*idcount = 0;
780	while ((count == 0) || (*idcount < count))
781	{
782		if (si == ASL_INDEX_NULL) break;
783		if (si >= s->slotlist_count) break;
784
785		slot = s->slotlist[si].slot;
786		xid = s->slotlist[si].xid;
787
788		*last_id = xid;
789
790		status = msg_fetch(s, slot, &msg);
791
792		didmatch = 0;
793		if (match == 0)
794		{
795			didmatch = 1;
796		}
797		else
798		{
799			for (i = 0; i < qcount; i++)
800			{
801				didmatch = asl_msg_cmp((aslmsg)(query->msg[i]), msg);
802				if (didmatch == 1) break;
803			}
804		}
805
806		if (didmatch == 1)
807		{
808			if ((*res)->count == 0) (*res)->msg = (asl_msg_t **)calloc(1, sizeof(asl_msg_t *));
809			else (*res)->msg = (asl_msg_t **)reallocf((*res)->msg, (1 + (*res)->count) * sizeof(asl_msg_t *));
810			if ((*res)->msg == NULL)
811			{
812				match_worker_cleanup(res);
813				return ASL_STATUS_NO_MEMORY;
814			}
815
816			(*res)->msg[(*res)->count++] = (asl_msg_t *)msg;
817		}
818		else
819		{
820			asl_free(msg);
821		}
822
823		si = next_search_slot(s, si, direction);
824	}
825
826	return status;
827}
828
829uint32_t
830asl_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)
831{
832	uint32_t idcount;
833
834	idcount = 0;
835	return match_worker(s, query, res, last_id, NULL, &idcount, start_id, count, direction);
836}
837