]> git.saurik.com Git - apple/syslog.git/blame - libsystem_asl.tproj/src/asl_file.c
syslog-385.tar.gz
[apple/syslog.git] / libsystem_asl.tproj / src / asl_file.c
CommitLineData
81582353 1/*
f3df4c03 2 * Copyright (c) 2007-2013 Apple Inc. All rights reserved.
81582353
A
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
24#include <asl_core.h>
f3df4c03 25#include <asl_msg.h>
81582353 26#include <asl_file.h>
f3df4c03 27#include <asl.h>
81582353
A
28#include <fcntl.h>
29#include <stdlib.h>
30#include <stddef.h>
31#include <stdio.h>
32#include <unistd.h>
33#include <sys/errno.h>
34#include <string.h>
35#include <sys/stat.h>
36#include <sys/types.h>
37#include <sys/acl.h>
38#include <membership.h>
39#include <time.h>
40#include <sys/time.h>
41#include <asl_private.h>
42#include <asl_legacy1.h>
43#include <TargetConditionals.h>
51023e60 44#include <os/variant_private.h>
81582353 45
81582353 46#define forever for(;;)
81582353
A
47
48/*
49 * MSG and STR records have (at least) a type (uint16_t) and a length (uint32_t)
50 * type and level are both 16 bit fields so that alignment isn't a pain.
51 */
52#define RECORD_COMMON_LEN 6
53#define RECORD_TYPE_LEN 2
54#define BUFFER_OFFSET_KVCOUNT 56
55
56#define SCRATCH_BUFFER_SIZE (MSG_RECORD_FIXED_LENGTH + (20 * sizeof(uint64_t)))
57
58typedef struct
59{
60 uint64_t next;
61 uint64_t mid;
62 uint64_t time;
63 uint32_t nano;
64 uint16_t level;
65 uint16_t flags;
66 uint32_t pid;
67 uint32_t uid;
68 uint32_t gid;
69 uint32_t ruid;
70 uint32_t rgid;
71 uint32_t refpid;
72 uint32_t kvcount;
73 uint64_t host;
74 uint64_t sender;
75 uint64_t facility;
76 uint64_t message;
77 uint64_t refproc;
78 uint64_t session;
79 uint64_t prev;
80} file_record_t;
81
82typedef struct
83{
84 asl_file_list_t *list;
85 int dir;
86} asl_file_match_token_t;
87
88static uint16_t
89_asl_get_16(char *h)
90{
91 uint16_t x;
92
93 memcpy(&x, h, 2);
94 return ntohs(x);
95}
96
97static void
98_asl_put_16(uint16_t i, char *h)
99{
100 uint16_t x;
101
102 x = htons(i);
103 memcpy(h, &x, 2);
104}
105
106static uint32_t
107_asl_get_32(char *h)
108{
109 uint32_t x;
110
111 memcpy(&x, h, 4);
112 return ntohl(x);
113}
114
115static void
116_asl_put_32(uint32_t i, char *h)
117{
118 uint32_t x;
119
120 x = htonl(i);
121 memcpy(h, &x, 4);
122}
123
124static uint64_t
125_asl_get_64(char *h)
126{
127 uint64_t x;
128
129 memcpy(&x, h, 8);
130 return asl_core_ntohq(x);
131}
132
133static void
134_asl_put_64(uint64_t i, char *h)
135{
136 uint64_t x;
137
138 x = asl_core_htonq(i);
139 memcpy(h, &x, 8);
140}
141
f3df4c03 142static ASL_STATUS
81582353
A
143asl_file_read_uint32(asl_file_t *s, off_t off, uint32_t *out)
144{
145 uint32_t status, val;
146
147 if (s == NULL) return ASL_STATUS_INVALID_STORE;
148 if (s->store == NULL) return ASL_STATUS_INVALID_STORE;
149 if ((off + sizeof(uint32_t)) > s->file_size) return ASL_STATUS_READ_FAILED;
150
151 status = fseeko(s->store, off, SEEK_SET);
152 if (status != 0) return ASL_STATUS_READ_FAILED;
153
154 val = 0;
155
156 status = fread(&val, sizeof(uint32_t), 1, s->store);
157 if (status != 1) return ASL_STATUS_READ_FAILED;
158
159 if (out != NULL) *out = ntohl(val);
160 return ASL_STATUS_OK;
161}
162
f3df4c03 163static ASL_STATUS
81582353
A
164asl_file_read_uint64(asl_file_t *s, off_t off, uint64_t *out)
165{
166 uint32_t status;
167 uint64_t val;
168
169 if (s == NULL) return ASL_STATUS_INVALID_STORE;
170 if (s->store == NULL) return ASL_STATUS_INVALID_STORE;
171 if ((off + sizeof(uint64_t)) > s->file_size) return ASL_STATUS_READ_FAILED;
172
173 status = fseeko(s->store, off, SEEK_SET);
174 if (status != 0) return ASL_STATUS_READ_FAILED;
175
176 val = 0;
177
178 status = fread(&val, sizeof(uint64_t), 1, s->store);
179 if (status != 1) return ASL_STATUS_READ_FAILED;
180
181 if (out != NULL) *out = asl_core_ntohq(val);
182 return ASL_STATUS_OK;
183}
184
5222c21d
A
185static file_string_t *
186file_string_create(asl_file_t *s)
187{
188 if ((s != NULL) && (s->string_spare != NULL))
189 {
190 file_string_t *out = s->string_spare;
191 s->string_spare = NULL;
192 return out;
193 }
194
195 return (file_string_t *)calloc(1, sizeof(file_string_t));
196}
197
198static void
199file_string_dispose(asl_file_t *s, file_string_t *x)
200{
201 if ((s != NULL) && (s->string_spare == NULL))
202 {
203 s->string_spare = x;
204 memset(s->string_spare, 0, sizeof(file_string_t));
205 }
206 else
207 {
208 free(x);
209 }
210}
211
212
f3df4c03
A
213asl_file_t *
214asl_file_retain(asl_file_t *s)
215{
216 if (s == NULL) return NULL;
217 asl_retain((asl_object_t)s);
218 return s;
219}
220
221void
222asl_file_release(asl_file_t *s)
223{
224 if (s == NULL) return;
225 asl_release((asl_object_t)s);
226}
227
228ASL_STATUS
81582353 229asl_file_close(asl_file_t *s)
f3df4c03
A
230{
231 if (s == NULL) return ASL_STATUS_OK;
232 asl_release((asl_object_t)s);
233 return ASL_STATUS_OK;
234}
235
236static void
237_asl_file_free_internal(asl_file_t *s)
81582353
A
238{
239 file_string_t *x;
240
f3df4c03 241 if (s == NULL) return;
81582353
A
242
243 if (s->version == 1)
244 {
f3df4c03
A
245 asl_legacy1_close((asl_legacy1_t *)s->legacy);
246 return;
81582353
A
247 }
248
249 while (s->string_list != NULL)
250 {
251 x = s->string_list->next;
252 free(s->string_list);
253 s->string_list = x;
254 }
255
5222c21d
A
256 free(s->string_spare);
257
81582353
A
258 if (s->store != NULL) fclose(s->store);
259 if (s->scratch != NULL) free(s->scratch);
260
261 memset(s, 0, sizeof(asl_file_t));
262 free(s);
81582353
A
263}
264
f3df4c03 265__private_extern__ ASL_STATUS
81582353
A
266asl_file_open_write_fd(int fd, asl_file_t **s)
267{
268 time_t now;
269 int status;
270 char buf[DB_HEADER_LEN];
271 asl_file_t *out;
272
273 if (fd < 0) return ASL_STATUS_FAILED;
274 if (s == NULL) return ASL_STATUS_FAILED;
275
276 out = (asl_file_t *)calloc(1, sizeof(asl_file_t));
277 if (out == NULL) return ASL_STATUS_NO_MEMORY;
278
f3df4c03
A
279 out->asl_type = ASL_TYPE_FILE;
280 out->refcount = 1;
281
81582353
A
282 out->store = fdopen(fd, "w+");
283 if (out->store == NULL)
284 {
285 free(out);
286 return ASL_STATUS_FAILED;
287 }
288
289 memset(buf, 0, sizeof(buf));
290 memcpy(buf, ASL_DB_COOKIE, ASL_DB_COOKIE_LEN);
291
292 _asl_put_32(DB_VERSION, buf + DB_HEADER_VERS_OFFSET);
293
294 now = time(NULL);
295 out->dob = now;
296 _asl_put_64(out->dob, buf + DB_HEADER_TIME_OFFSET);
297
298 _asl_put_32(CACHE_SIZE, buf + DB_HEADER_CSIZE_OFFSET);
299
300 status = fwrite(buf, sizeof(buf), 1, out->store);
301 if (status != 1)
302 {
303 fclose(out->store);
304 free(out);
305 return ASL_STATUS_FAILED;
306 }
307
308 /* flush data */
309 fflush(out->store);
310
311 out->file_size = sizeof(buf);
312
313 /* scratch buffer for file writes (we test for NULL before using it) */
314 out->scratch = malloc(SCRATCH_BUFFER_SIZE);
315
316 *s = out;
317
318 return ASL_STATUS_OK;
319}
320
321__private_extern__ int
322asl_file_create(const char *path, uid_t uid, gid_t gid, mode_t mode)
323{
324#if TARGET_OS_IPHONE
325 return open(path, O_RDWR | O_CREAT | O_EXCL, mode);
326#else
51023e60
A
327 if (os_variant_is_basesystem("com.apple.syslog")) {
328 return open(path, O_RDWR | O_CREAT | O_EXCL, mode);
329 }
330
81582353
A
331 acl_t acl;
332 uuid_t uuid;
333 acl_entry_t entry;
334 acl_permset_t perms;
335 int status;
336 int fd = -1;
337
338 /* -1 means don't set ACL for uid or gid */
339 if ((uid == -1) && (gid == -1))
340 {
341 return open(path, O_RDWR | O_CREAT | O_EXCL, mode);
342 }
343
344 acl = acl_init(1);
345
346 if ((gid != 0) && (gid != -1))
347 {
348 status = mbr_gid_to_uuid(gid, uuid);
349 if (status != 0) goto asl_file_create_return;
350
351 status = acl_create_entry_np(&acl, &entry, ACL_FIRST_ENTRY);
352 if (status != 0) goto asl_file_create_return;
353
354 status = acl_set_tag_type(entry, ACL_EXTENDED_ALLOW);
355 if (status != 0) goto asl_file_create_return;
356
357 status = acl_set_qualifier(entry, &uuid);
358 if (status != 0) goto asl_file_create_return;
359
360 status = acl_get_permset(entry, &perms);
361 if (status != 0) goto asl_file_create_return;
362
363 status = acl_add_perm(perms, ACL_READ_DATA);
364 if (status != 0) goto asl_file_create_return;
365 }
366
367 if ((uid != 0) && (uid != -1))
368 {
369 status = mbr_uid_to_uuid(uid, uuid);
370 if (status != 0) goto asl_file_create_return;
371
372 status = acl_create_entry_np(&acl, &entry, ACL_FIRST_ENTRY);
373 if (status != 0) goto asl_file_create_return;
374
375 status = acl_set_tag_type(entry, ACL_EXTENDED_ALLOW);
376 if (status != 0) goto asl_file_create_return;
377
378 status = acl_set_qualifier(entry, &uuid);
379 if (status != 0) goto asl_file_create_return;
380
381 status = acl_get_permset(entry, &perms);
382 if (status != 0) goto asl_file_create_return;
383
384 status = acl_add_perm(perms, ACL_READ_DATA);
385 if (status != 0) goto asl_file_create_return;
386 }
387
388 fd = open(path, O_RDWR | O_CREAT | O_EXCL, mode);
389 if (fd < 0) goto asl_file_create_return;
390
391 status = acl_set_fd(fd, acl);
392 if (status != 0)
393 {
394 close(fd);
395 fd = -1;
396 unlink(path);
397 }
398
399asl_file_create_return:
400
401 acl_free(acl);
402 return fd;
403#endif
404}
405
f3df4c03 406ASL_STATUS
81582353
A
407asl_file_open_write(const char *path, mode_t mode, uid_t uid, gid_t gid, asl_file_t **s)
408{
409 int i, status, fd;
410 struct stat sb;
411 char buf[DB_HEADER_LEN];
412 asl_file_t *out;
413 uint32_t aslstatus, vers, last_len;
414 off_t off;
415
416 memset(&sb, 0, sizeof(struct stat));
417
418 status = stat(path, &sb);
419 if (status == 0)
420 {
421 /* must be a plain file */
422 if (!S_ISREG(sb.st_mode)) return ASL_STATUS_INVALID_STORE;
423
f3df4c03
A
424 /*
425 * If the file exists, we go with the existing mode, uid, and gid.
426 */
427
81582353
A
428 if (sb.st_size == 0)
429 {
430 fd = open(path, O_RDWR | O_EXCL, mode);
431 if (fd < 0) return ASL_STATUS_FAILED;
432
433 return asl_file_open_write_fd(fd, s);
434 }
435 else
436 {
81582353
A
437 out = (asl_file_t *)calloc(1, sizeof(asl_file_t));
438 if (out == NULL) return ASL_STATUS_NO_MEMORY;
439
f3df4c03
A
440 out->asl_type = ASL_TYPE_FILE;
441 out->refcount = 1;
442
81582353
A
443 out->store = fopen(path, "r+");
444 if (out->store == NULL)
445 {
446 free(out);
447 return ASL_STATUS_FAILED;
448 }
449
450 i = fread(buf, DB_HEADER_LEN, 1, out->store);
451 if (i < 1)
452 {
453 asl_file_close(out);
454 return ASL_STATUS_READ_FAILED;
455 }
456
457 /* check cookie */
458 if (strncmp(buf, ASL_DB_COOKIE, ASL_DB_COOKIE_LEN))
459 {
460 asl_file_close(out);
461 return ASL_STATUS_INVALID_STORE;
462 }
463
464 /* check version */
465 vers = _asl_get_32(buf + DB_HEADER_VERS_OFFSET);
466 if (vers != DB_VERSION)
467 {
468 asl_file_close(out);
469 return ASL_STATUS_INVALID_STORE;
470 }
471
472 out->dob = _asl_get_64(buf + DB_HEADER_TIME_OFFSET);
473 out->first = _asl_get_64(buf + DB_HEADER_FIRST_OFFSET);
474 out->last = _asl_get_64(buf + DB_HEADER_LAST_OFFSET);
475 out->file_size = (size_t)sb.st_size;
476
477 /*
478 * Detect bogus last pointer and check for odd-sized files.
479 * Setting out->last to zero forces asl_file_read_set_position to
480 * follow the linked list of records in the file to the last record.
481 * It's slower, but it's better at preventing crashes in corrupt files.
482 */
483
484 /* records are at least MSG_RECORD_FIXED_LENGTH bytes */
485 if ((out->last + MSG_RECORD_FIXED_LENGTH) > out->file_size)
486 {
487 out->last = 0;
488 }
489 else
490 {
491 /* read last record length and make sure the file is at least that large */
492 off = out->last + RECORD_TYPE_LEN;
493 status = asl_file_read_uint32(out, off, &last_len);
494 if (status != ASL_STATUS_OK)
495 {
496 asl_file_close(out);
497 return status;
498 }
499
500 if ((out->last + last_len) > out->file_size) out->last = 0;
501 }
502
5222c21d
A
503 if (out->last != 0)
504 {
505 /* skip type (uint16_t), len (uint32_t), and next (uint64_t) */
506 off = out->last + sizeof(uint16_t) + sizeof (uint32_t) + sizeof(uint64_t);
507 status = asl_file_read_uint64(out, off, &(out->last_mid));
508 if (status != ASL_STATUS_OK)
509 {
510 asl_file_close(out);
511 return status;
512 }
513 }
514
81582353
A
515 aslstatus = asl_file_read_set_position(out, ASL_FILE_POSITION_LAST);
516 if (aslstatus != ASL_STATUS_OK)
517 {
518 asl_file_close(out);
519 return aslstatus;
520 }
521
522 out->prev = out->cursor;
523 status = fseeko(out->store, 0, SEEK_END);
524 if (status != 0)
525 {
526 asl_file_close(out);
527 return ASL_STATUS_READ_FAILED;
528 }
529
530 out->file_size = (size_t)ftello(out->store);
531
532 /* scratch buffer for file writes (we test for NULL before using it) */
533 out->scratch = malloc(SCRATCH_BUFFER_SIZE);
534
535 *s = out;
536
537 return ASL_STATUS_OK;
538 }
539 }
540 else if (errno != ENOENT)
541 {
542 /* unexpected status */
543 return ASL_STATUS_FAILED;
544 }
545
f3df4c03
A
546 /*
547 * If the file does not exist, we set the mode, uid, and gid.
548 */
549
81582353
A
550 fd = asl_file_create(path, uid, gid, mode);
551 if (fd < 0) return ASL_STATUS_FAILED;
552
553 aslstatus = asl_file_open_write_fd(fd, s);
554 if (aslstatus != ASL_STATUS_OK) unlink(path);
555
556 return aslstatus;
557}
558
f3df4c03 559ASL_STATUS
81582353
A
560asl_file_compact(asl_file_t *s, const char *path, mode_t mode, uid_t uid, gid_t gid)
561{
562 asl_file_t *new;
563 struct stat sb;
f3df4c03 564 asl_msg_t *m;
81582353
A
565 uint64_t xid;
566 uint32_t status;
567
568 if (s == NULL) return ASL_STATUS_INVALID_STORE;
569 if (path == NULL) return ASL_STATUS_INVALID_ARG;
570
571 if (s->version == 1) return ASL_STATUS_FAILED;
572
573 memset(&sb, 0, sizeof(struct stat));
574
575 if (stat(path, &sb) == 0) return ASL_STATUS_FAILED;
576 if (errno != ENOENT) return ASL_STATUS_FAILED;
577
578 status = asl_file_read_set_position(s, ASL_FILE_POSITION_FIRST);
579 if (status != ASL_STATUS_OK) return status;
580
581 new = NULL;
582 status = asl_file_open_write(path, mode, uid, gid, &new);
583 if (status != ASL_STATUS_OK) return status;
584 new->flags = ASL_FILE_FLAG_UNLIMITED_CACHE | ASL_FILE_FLAG_PRESERVE_MSG_ID;
585
586 while ((status == ASL_STATUS_OK) && (s->cursor != 0))
587 {
588 m = NULL;
589 status = asl_file_fetch_next(s, &m);
590 if (status != ASL_STATUS_OK) break;
591
592 xid = 0;
593 status = asl_file_save(new, m, &xid);
f3df4c03 594 asl_msg_release(m);
81582353
A
595 }
596
597 asl_file_close(new);
598 return status;
599}
600
f3df4c03
A
601ASL_STATUS
602asl_file_filter(asl_file_t *s, const char *path, asl_msg_list_t *filter, uint32_t flags, mode_t mode, uid_t uid, gid_t gid, uint32_t *dstcount, void (*aux_callback)(const char *auxfile))
603{
604 asl_file_t *new;
605 struct stat sb;
606 asl_msg_t *m;
607 uint64_t xid = 0;
608 uint32_t status, n = 0;
609 uint32_t matchflag = flags & ASL_FILE_FILTER_FLAG_KEEP_MATCHES;
610
611 if (dstcount != NULL) *dstcount = n;
612
613 if (s == NULL) return ASL_STATUS_INVALID_STORE;
614 if (path == NULL) return ASL_STATUS_INVALID_ARG;
615
616 if (s->version == 1) return ASL_STATUS_FAILED;
617
618 memset(&sb, 0, sizeof(struct stat));
619
620 if (stat(path, &sb) == 0) return ASL_STATUS_FAILED;
621 if (errno != ENOENT) return ASL_STATUS_FAILED;
622
623 status = asl_file_read_set_position(s, ASL_FILE_POSITION_FIRST);
624 if (status != ASL_STATUS_OK) return status;
625
626 new = NULL;
627 status = asl_file_open_write(path, mode, uid, gid, &new);
628 if (status != ASL_STATUS_OK) return status;
629 new->flags = ASL_FILE_FLAG_UNLIMITED_CACHE | ASL_FILE_FLAG_PRESERVE_MSG_ID;
630
631 while ((status == ASL_STATUS_OK) && (s->cursor != 0))
632 {
633 m = NULL;
634 status = asl_file_fetch_next(s, &m);
635 if (status != ASL_STATUS_OK) break;
636
637 /*
638 * asl_msg_cmp_list is supposed to return 1 for a match, 0 otherwise,
639 * but just to be sure we only get a 1 or zero, we do an extra test.
640 */
641 uint32_t msgmatch = (asl_msg_cmp_list(m, filter) == 0) ? 0 : 1;
642 if (msgmatch == matchflag)
643 {
644 status = asl_file_save(new, m, &xid);
645 if (status == ASL_STATUS_OK) n++;
646 }
647 else if (aux_callback != NULL)
648 {
649 /* check for ASL_KEY_AUX_URL and pass it to callback */
650 const char *auxval = asl_msg_get_val_for_key(m, ASL_KEY_AUX_URL);
651 if (auxval != NULL) aux_callback(auxval);
652 }
653
654 asl_msg_release(m);
655 }
656
657 asl_file_close(new);
658 if (dstcount != NULL) *dstcount = n;
659 return status;
660}
661
662ASL_STATUS
663asl_file_filter_level(asl_file_t *s, const char *path, uint32_t keep_mask, mode_t mode, uid_t uid, gid_t gid, uint32_t *dstcount, void (*aux_callback)(const char *auxfile))
664{
665 asl_file_t *new;
666 struct stat sb;
667 asl_msg_t *m;
668 uint64_t xid = 0;
669 uint32_t status, n = 0;
670 uint8_t kmcur, kmnew;
671
672 if (dstcount != NULL) *dstcount = n;
673
674 if (s == NULL) return ASL_STATUS_INVALID_STORE;
675 if (path == NULL) return ASL_STATUS_INVALID_ARG;
676
677 if (s->version == 1) return ASL_STATUS_FAILED;
678
679 memset(&sb, 0, sizeof(struct stat));
680
681 if (stat(path, &sb) == 0) return ASL_STATUS_FAILED;
682 if (errno != ENOENT) return ASL_STATUS_FAILED;
683
684 kmnew = keep_mask;
685
686 /* get current filter mask in file's header */
687 status = fseeko(s->store, DB_HEADER_FILTER_MASK_OFFSET, SEEK_SET);
688 if (status != 0) return ASL_STATUS_READ_FAILED;
689 fread(&kmcur, 1, 1, s->store);
690
691 status = asl_file_read_set_position(s, ASL_FILE_POSITION_FIRST);
692 if (status != ASL_STATUS_OK) return status;
693
694 new = NULL;
695 status = asl_file_open_write(path, mode, uid, gid, &new);
696 if (status != ASL_STATUS_OK) return status;
697
698 new->flags = ASL_FILE_FLAG_UNLIMITED_CACHE | ASL_FILE_FLAG_PRESERVE_MSG_ID;
699
700 while ((status == ASL_STATUS_OK) && (s->cursor != 0))
701 {
702 m = NULL;
703 status = asl_file_fetch_next(s, &m);
704 if (status != ASL_STATUS_OK) break;
705 if (m == NULL) continue;
706 const char *lval = asl_msg_get_val_for_key(m, ASL_KEY_LEVEL);
707 if (lval == NULL) continue;
708 uint32_t level_bit = 0x01 << atoi(lval);
709
710 if (level_bit & keep_mask)
711 {
712 status = asl_file_save(new, m, &xid);
713 if (status == ASL_STATUS_OK) n++;
714 }
715 else if (aux_callback != NULL)
716 {
717 /* check for ASL_KEY_AUX_URL and pass it to callback */
718 const char *auxval = asl_msg_get_val_for_key(m, ASL_KEY_AUX_URL);
719 if (auxval != NULL) aux_callback(auxval);
720 }
721
722 asl_msg_release(m);
723 }
724
725 kmnew = kmcur & kmnew;
726 status = fseeko(new->store, DB_HEADER_FILTER_MASK_OFFSET, SEEK_SET);
727 if (status != 0) return ASL_STATUS_READ_FAILED;
728 fwrite(&kmnew, 1, 1, new->store);
729
730 asl_file_close(new);
731 if (dstcount != NULL) *dstcount = n;
732 return status;
733}
734
735static ASL_STATUS
81582353
A
736asl_file_string_encode(asl_file_t *s, const char *str, uint64_t *out)
737{
738 uint32_t i, hash, len, x32;
739 file_string_t *sp, *sx, *sl;
740 uint64_t x64;
741 uint8_t inls;
742 uint16_t type;
743 off_t off;
744 char *p;
745
746 if (s == NULL) return ASL_STATUS_INVALID_STORE;
f3df4c03 747 if (s->store == NULL) return ASL_STATUS_INVALID_STORE;
81582353
A
748 if (str == NULL) return ASL_STATUS_INVALID_ARG;
749
750 len = strlen(str);
751
752 /* inline strings */
753 if (len < 8)
754 {
755 /* inline string */
756 inls = len;
757 inls |= 0x80;
758
759 x64 = 0;
760 p = (char *)&x64;
761 memcpy(p, &inls, 1);
762 memcpy(p + 1, str, len);
763 *out = asl_core_ntohq(x64);
764 return ASL_STATUS_OK;
765 }
766
5222c21d
A
767 /* cached strings include trailing nul */
768 len++;
81582353 769
5222c21d 770 if (len <= CACHE_MAX_STRING_LEN)
81582353 771 {
5222c21d
A
772 /* check the cache */
773 hash = asl_core_string_hash(str, len);
774
775 sp = NULL;
776 for (sx = s->string_list; sx != NULL; sx = sx->next)
81582353 777 {
5222c21d 778 if ((hash == sx->hash) && (!strcmp(str, sx->str)))
81582353 779 {
5222c21d
A
780 /* Move this string to the head of the list */
781 if (sp != NULL)
782 {
783 sl = s->string_list;
784 sp->next = sx->next;
785 sx->next = sl;
786 s->string_list = sx;
787 }
788
789 *out = sx->where;
790 return ASL_STATUS_OK;
81582353
A
791 }
792
5222c21d 793 sp = sx;
81582353 794 }
81582353
A
795 }
796
797 off = ftello(s->store);
798
799 /* Type */
800 type = htons(ASL_FILE_TYPE_STR);
801 i = fwrite(&type, sizeof(uint16_t), 1, s->store);
802 if (i != 1) return ASL_STATUS_WRITE_FAILED;
803
804 /* Length (includes trailing nul) */
5222c21d 805 x32 = htonl(len);
81582353
A
806 i = fwrite(&x32, sizeof(uint32_t), 1, s->store);
807 if (i != 1) return ASL_STATUS_WRITE_FAILED;
808
809 /* String data (nul terminated) */
5222c21d 810 i = fwrite(str, len, 1, s->store);
81582353
A
811 if (i != 1) return ASL_STATUS_WRITE_FAILED;
812
813 /* flush data */
814 fflush(s->store);
815
5222c21d
A
816 /*
817 * Create file_string_t and insert into the cache, but only if the
818 * string is small. This prevents a huge string from eating memory.
819 * It's unlikely that large strings will be very re-usable.
820 */
821 if (len <= CACHE_MAX_STRING_LEN)
822 {
823 sx = file_string_create(s);
824 if (sx == NULL) return ASL_STATUS_NO_MEMORY;
81582353 825
5222c21d
A
826 sx->where = off;
827 sx->hash = hash;
828 sx->next = s->string_list;
81582353 829
5222c21d
A
830 /* includes trailing nul */
831 memcpy(sx->str, str, len);
81582353 832
5222c21d 833 s->string_list = sx;
81582353 834
5222c21d 835 if (((s->flags & ASL_FILE_FLAG_UNLIMITED_CACHE) == 0) && (s->string_cache_count == CACHE_SIZE))
81582353 836 {
5222c21d
A
837 /* drop last (lru) string from cache */
838 sp = s->string_list;
839 sx = sp->next;
81582353 840
5222c21d
A
841 /* NB CACHE_SIZE must be > 1 */
842 while (sx->next != NULL)
843 {
844 sp = sx;
845 sx = sx->next;
846 }
847
848 sp->next = NULL;
849 file_string_dispose(s, sx);
850 }
851 else
852 {
853 s->string_cache_count++;
854 }
81582353
A
855 }
856
857 *out = off;
858 return ASL_STATUS_OK;
859}
860
861/*
f3df4c03 862 * Encode an asl_msg_t *as a record structure.
81582353
A
863 * Creates and caches strings.
864 */
5222c21d
A
865#define KVSTACK_SIZE 128
866
f3df4c03
A
867ASL_STATUS
868asl_file_save(asl_file_t *s, asl_msg_t *in, uint64_t *mid)
81582353
A
869{
870 char *buf, *p;
871 uint32_t i, len, x, status;
872 file_record_t r;
873 uint64_t k, v;
5222c21d
A
874 uint64_t kvstack[KVSTACK_SIZE];
875 uint64_t *kvmalloc = NULL;
81582353
A
876 uint64_t *kvlist;
877 off_t off;
878 asl_msg_t *msg;
879 const char *key, *val;
880
881 if (s == NULL) return ASL_STATUS_INVALID_STORE;
f3df4c03 882 if (s->store == NULL) return ASL_STATUS_INVALID_STORE;
81582353
A
883 if (in == NULL) return ASL_STATUS_INVALID_MESSAGE;
884
f3df4c03 885 if (s->flags & ASL_FILE_FLAG_READ) return ASL_STATUS_READ_ONLY;
81582353
A
886
887 msg = (asl_msg_t *)in;
888
889 memset(&r, 0, sizeof(file_record_t));
890
5222c21d
A
891 r.mid = UINT64_MAX;
892 if ((mid != NULL ) && (*mid != 0)) r.mid = *mid;
893
81582353
A
894 r.flags = 0;
895 r.level = ASL_LEVEL_DEBUG;
896 r.pid = -1;
897 r.uid = -2;
898 r.gid = -2;
899 r.ruid = -1;
900 r.rgid = -1;
901 r.time = 0;
902 r.nano = 0;
903 r.prev = s->prev;
5222c21d 904 kvlist = kvstack;
81582353
A
905
906 key = NULL;
907 val = NULL;
908
909 for (x = asl_msg_fetch(msg, 0, &key, &val, NULL); x != IndexNull; x = asl_msg_fetch(msg, x, &key, &val, NULL))
910 {
911 if (key == NULL)
912 {
913 continue;
914 }
915 else if (!strcmp(key, ASL_KEY_TIME))
916 {
f3df4c03 917 if (val != NULL) r.time = asl_core_parse_time(val, NULL);
81582353
A
918 }
919 else if (!strcmp(key, ASL_KEY_TIME_NSEC))
920 {
921 if (val != NULL) r.nano = atoi(val);
922 }
923 else if (!strcmp(key, ASL_KEY_HOST))
924 {
925 if (val != NULL)
926 {
927 status = asl_file_string_encode(s, val, &(r.host));
928 if (status != ASL_STATUS_OK)
929 {
5222c21d 930 free(kvmalloc);
81582353
A
931 return status;
932 }
933 }
934 }
935 else if (!strcmp(key, ASL_KEY_SENDER))
936 {
937 if (val != NULL)
938 {
939 status = asl_file_string_encode(s, val, &(r.sender));
940 if (status != ASL_STATUS_OK)
941 {
5222c21d 942 free(kvmalloc);
81582353
A
943 return status;
944 }
945 }
946 }
947 else if (!strcmp(key, ASL_KEY_PID))
948 {
949 if (val != NULL) r.pid = atoi(val);
950 }
951 else if (!strcmp(key, ASL_KEY_REF_PID))
952 {
953 if (val != NULL) r.refpid = atoi(val);
954 }
955 else if (!strcmp(key, ASL_KEY_UID))
956 {
957 if (val != NULL) r.uid = atoi(val);
958 }
959 else if (!strcmp(key, ASL_KEY_GID))
960 {
961 if (val != NULL) r.gid = atoi(val);
962 }
963 else if (!strcmp(key, ASL_KEY_LEVEL))
964 {
965 if (val != NULL) r.level = atoi(val);
966 }
967 else if (!strcmp(key, ASL_KEY_MSG))
968 {
969 if (val != NULL)
970 {
971 status = asl_file_string_encode(s, val, &(r.message));
972 if (status != ASL_STATUS_OK)
973 {
5222c21d 974 free(kvmalloc);
81582353
A
975 return status;
976 }
977 }
978 }
979 else if (!strcmp(key, ASL_KEY_FACILITY))
980 {
981 if (val != NULL)
982 {
983 status = asl_file_string_encode(s, val, &(r.facility));
984 if (status != ASL_STATUS_OK)
985 {
5222c21d 986 free(kvmalloc);
81582353
A
987 return status;
988 }
989 }
990 }
991 else if (!strcmp(key, ASL_KEY_REF_PROC))
992 {
993 if (val != NULL)
994 {
995 status = asl_file_string_encode(s, val, &(r.refproc));
996 if (status != ASL_STATUS_OK)
997 {
5222c21d 998 free(kvmalloc);
81582353
A
999 return status;
1000 }
1001 }
1002 }
1003 else if (!strcmp(key, ASL_KEY_SESSION))
1004 {
1005 if (val != NULL)
1006 {
1007 status = asl_file_string_encode(s, val, &(r.session));
1008 if (status != ASL_STATUS_OK)
1009 {
5222c21d 1010 free(kvmalloc);
81582353
A
1011 return status;
1012 }
1013 }
1014 }
1015 else if (!strcmp(key, ASL_KEY_READ_UID))
1016 {
1017 if (((r.flags & ASL_MSG_FLAG_READ_UID_SET) == 0) && (val != NULL))
1018 {
1019 r.ruid = atoi(val);
1020 r.flags |= ASL_MSG_FLAG_READ_UID_SET;
1021 }
1022 }
1023 else if (!strcmp(key, ASL_KEY_READ_GID))
1024 {
1025 if (((r.flags & ASL_MSG_FLAG_READ_GID_SET) == 0) && (val != NULL))
1026 {
1027 r.rgid = atoi(val);
1028 r.flags |= ASL_MSG_FLAG_READ_GID_SET;
1029 }
1030 }
1031 else if (!strcmp(key, ASL_KEY_MSG_ID))
1032 {
5222c21d
A
1033 if (s->flags & ASL_FILE_FLAG_PRESERVE_MSG_ID)
1034 {
1035 r.mid = atoll(val);
1036 if (mid != NULL) *mid = r.mid;
1037 }
81582353
A
1038 }
1039 else if (!strcmp(key, ASL_KEY_OPTION))
1040 {
1041 /* ignore - we don't save ASLOption */
1042 }
1043 else
1044 {
1045 status = asl_file_string_encode(s, key, &k);
1046 if (status != ASL_STATUS_OK)
1047 {
5222c21d 1048 free(kvmalloc);
81582353
A
1049 return status;
1050 }
1051
1052 v = 0;
1053 if (val != NULL)
1054 {
1055 status = asl_file_string_encode(s, val, &v);
1056 if (status != ASL_STATUS_OK)
1057 {
5222c21d 1058 free(kvmalloc);
81582353
A
1059 return status;
1060 }
1061 }
1062
5222c21d 1063 if (r.kvcount >= KVSTACK_SIZE)
81582353 1064 {
5222c21d
A
1065 /* out of space for the kvlist on the stack - fall back to malloc */
1066 kvmalloc = reallocf(kvmalloc, (r.kvcount + 2) * sizeof(uint64_t));
1067 if (kvmalloc == NULL) return ASL_STATUS_NO_MEMORY;
81582353 1068
5222c21d
A
1069 kvlist = kvmalloc;
1070
1071 if (r.kvcount == KVSTACK_SIZE)
1072 {
1073 /* copy kvstack to kvmalloc */
1074 for (i = 0; i < KVSTACK_SIZE; i++) kvmalloc[i] = kvstack[i];
1075 }
81582353
A
1076 }
1077
1078 kvlist[r.kvcount++] = k;
1079 kvlist[r.kvcount++] = v;
1080 }
1081 }
1082
1083 len = MSG_RECORD_FIXED_LENGTH + (r.kvcount * sizeof(uint64_t));
1084 buf = NULL;
1085
1086 /* use the scratch buffer if it exists and is large enough */
1087 if ((s->scratch != NULL) && (len <= SCRATCH_BUFFER_SIZE))
1088 {
1089 memset(s->scratch, 0, SCRATCH_BUFFER_SIZE);
1090 buf = s->scratch;
1091 }
1092 else
1093 {
1094 buf = calloc(1, len);
1095 }
1096
1097 if (buf == NULL) return ASL_STATUS_NO_MEMORY;
1098
5222c21d 1099 if (r.mid == UINT64_MAX)
81582353 1100 {
5222c21d
A
1101 s->last_mid = s->last_mid + 1;
1102 r.mid = s->last_mid;
81582353
A
1103 }
1104
1105 p = buf;
1106
1107 /* Type */
1108 _asl_put_16(ASL_FILE_TYPE_MSG, p);
1109 p += sizeof(uint16_t);
1110
1111 /* Length of message (excludes type and length fields) */
1112 _asl_put_32(len - RECORD_COMMON_LEN, p);
1113 p += sizeof(uint32_t);
1114
1115 /* Message data... */
1116
1117 _asl_put_64(r.next, p);
1118 p += sizeof(uint64_t);
1119
1120 _asl_put_64(r.mid, p);
1121 p += sizeof(uint64_t);
1122
1123 _asl_put_64(r.time, p);
1124 p += sizeof(uint64_t);
1125
1126 _asl_put_32(r.nano, p);
1127 p += sizeof(uint32_t);
1128
1129 _asl_put_16(r.level, p);
1130 p += sizeof(uint16_t);
1131
1132 _asl_put_16(r.flags, p);
1133 p += sizeof(uint16_t);
1134
1135 _asl_put_32(r.pid, p);
1136 p += sizeof(uint32_t);
1137
1138 _asl_put_32(r.uid, p);
1139 p += sizeof(uint32_t);
1140
1141 _asl_put_32(r.gid, p);
1142 p += sizeof(uint32_t);
1143
1144 _asl_put_32(r.ruid, p);
1145 p += sizeof(uint32_t);
1146
1147 _asl_put_32(r.rgid, p);
1148 p += sizeof(uint32_t);
1149
1150 _asl_put_32(r.refpid, p);
1151 p += sizeof(uint32_t);
1152
1153 _asl_put_32(r.kvcount, p);
1154 p += sizeof(uint32_t);
1155
1156 _asl_put_64(r.host, p);
1157 p += sizeof(uint64_t);
1158
1159 _asl_put_64(r.sender, p);
1160 p += sizeof(uint64_t);
1161
1162 _asl_put_64(r.facility, p);
1163 p += sizeof(uint64_t);
1164
1165 _asl_put_64(r.message, p);
1166 p += sizeof(uint64_t);
1167
1168 _asl_put_64(r.refproc, p);
1169 p += sizeof(uint64_t);
1170
1171 _asl_put_64(r.session, p);
1172 p += sizeof(uint64_t);
1173
1174 for (i = 0; i < r.kvcount; i++)
1175 {
1176 _asl_put_64(kvlist[i], p);
1177 p += sizeof(uint64_t);
1178 }
1179
1180 _asl_put_64(r.prev, p);
1181 p += sizeof(uint64_t);
1182
5222c21d 1183 free(kvmalloc);
81582353
A
1184
1185 /* write record at end of file */
1186 status = fseeko(s->store, 0, SEEK_END);
1187 if (status != 0) return ASL_STATUS_WRITE_FAILED;
1188
1189 s->last = (uint64_t)ftello(s->store);
1190
1191 v = asl_core_htonq(s->last);
1192
1193 status = fwrite(buf, len, 1, s->store);
1194 fflush(s->store);
1195
1196 /* free the buffer if it was allocated here */
1197 if (buf != s->scratch) free(buf);
1198
1199 /* seek to "next" field of previous record, write last offset */
1200 off = s->prev + RECORD_COMMON_LEN;
1201 if (s->prev == 0) off = DB_HEADER_FIRST_OFFSET;
1202
1203 status = fseeko(s->store, off, SEEK_SET);
1204 if (status != 0) return ASL_STATUS_WRITE_FAILED;
1205
1206 status = fwrite(&v, sizeof(uint64_t), 1, s->store);
1207 if (status != 1) return ASL_STATUS_WRITE_FAILED;
1208
1209 /* seek to DB_HEADER_LAST_OFFSET, write last record offset */
1210 off = DB_HEADER_LAST_OFFSET;
1211
1212 status = fseeko(s->store, off, SEEK_SET);
1213 if (status != 0) return ASL_STATUS_WRITE_FAILED;
1214
1215 status = fwrite(&v, sizeof(uint64_t), 1, s->store);
1216 if (status != 1) return ASL_STATUS_WRITE_FAILED;
1217
1218 /* return to the end of the store (this is expected by other routines) */
1219 status = fseeko(s->store, 0, SEEK_END);
1220 if (status != 0) return ASL_STATUS_WRITE_FAILED;
1221
1222 /* flush data */
1223 fflush(s->store);
1224
1225 s->file_size = (size_t)ftello(s->store);
1226
1227 s->prev = s->last;
1228
1229 return ASL_STATUS_OK;
1230}
1231
f3df4c03 1232static ASL_STATUS
5222c21d 1233asl_file_fetch_object(asl_file_t *s, uint16_t fetch_type, uint64_t where, char **out, uint32_t *outlen)
81582353
A
1234{
1235 char ils[9];
1236 char *p;
1237 uint32_t len;
1238 int status;
1239 uint64_t x64;
1240 uint8_t inls;
1241 uint16_t type;
1242 off_t off;
1243
81582353 1244 if (s == NULL) return ASL_STATUS_INVALID_STORE;
f3df4c03 1245 if (s->store == NULL) return ASL_STATUS_INVALID_STORE;
81582353 1246 if (out == NULL) return ASL_STATUS_INVALID_ARG;
f3df4c03 1247 if (outlen == NULL) return ASL_STATUS_INVALID_ARG;
81582353
A
1248 if (where == 0) return ASL_STATUS_INVALID_ARG;
1249
f3df4c03
A
1250 *out = NULL;
1251 *outlen = 0;
5222c21d 1252
81582353
A
1253 inls = 0;
1254 x64 = asl_core_htonq(where);
1255 memcpy(&inls, &x64, 1);
1256 if (inls & 0x80)
1257 {
5222c21d
A
1258 if (fetch_type != ASL_FILE_TYPE_STR) return ASL_STATUS_INVALID_STORE;
1259
81582353
A
1260 /* inline string */
1261 inls &= 0x0f;
1262 if (inls > 7) return ASL_STATUS_INVALID_STORE;
1263
1264 p = 1 + (char *)&x64;
1265 memset(ils, 0, sizeof(ils));
1266 memcpy(ils, p, inls);
1267 *out = strdup(ils);
1268 if (*out == NULL) return ASL_STATUS_NO_MEMORY;
1269
1270 *outlen = inls;
1271 return ASL_STATUS_OK;
1272 }
1273
5222c21d
A
1274 if (fetch_type == ASL_FILE_TYPE_STR)
1275 {
1276 /* check the string cache */
1277 file_string_t *sx, *sp;
1278
1279 sp = NULL;
1280 for (sx = s->string_list; sx != NULL; sx = sx->next)
1281 {
1282 if (sx->where == where)
1283 {
1284 *out = strdup(sx->str);
1285 if (*out == NULL) return ASL_STATUS_NO_MEMORY;
1286
1287 /* N.B. hash field is used to hold length when reading */
1288 *outlen = sx->hash;
1289
1290 /* Move this string to the head of the list */
1291 if (sp != NULL)
1292 {
1293 file_string_t *sl = s->string_list;
1294 sp->next = sx->next;
1295 sx->next = sl;
1296 s->string_list = sx;
1297 }
1298
1299 return ASL_STATUS_OK;
1300 }
1301
1302 sp = sx;
1303 }
1304 }
1305
81582353
A
1306 off = where;
1307 if ((off + sizeof(uint16_t) + sizeof(uint32_t)) > s->file_size) return ASL_STATUS_READ_FAILED;
1308
1309 status = fseeko(s->store, off, SEEK_SET);
1310 if (status != 0) return ASL_STATUS_READ_FAILED;
1311
1312 /* Type */
1313 status = fread(&type, sizeof(uint16_t), 1, s->store);
1314 if (status != 1) return ASL_STATUS_READ_FAILED;
5222c21d 1315 type = ntohs(type);
81582353
A
1316 off += sizeof(uint16_t);
1317
5222c21d
A
1318 if (type != fetch_type) return ASL_STATUS_INVALID_STORE;
1319
81582353
A
1320 /* Length */
1321 len = 0;
1322 status = fread(&len, sizeof(uint32_t), 1, s->store);
1323 if (status != 1) return ASL_STATUS_READ_FAILED;
1324 off += sizeof(uint32_t);
1325
1326 len = ntohl(len);
1327 if ((off + len) > s->file_size) return ASL_STATUS_READ_FAILED;
1328
1329 *out = calloc(1, len);
1330 if (*out == NULL) return ASL_STATUS_NO_MEMORY;
1331
1332 status = fread(*out, len, 1, s->store);
1333 if (status != 1)
1334 {
1335 free(*out);
f3df4c03 1336 *out = NULL;
81582353
A
1337 return ASL_STATUS_READ_FAILED;
1338 }
1339
1340 *outlen = len;
5222c21d
A
1341
1342 if ((fetch_type == ASL_FILE_TYPE_STR) && (len <= CACHE_MAX_STRING_LEN))
1343 {
1344 file_string_t *sx = file_string_create(s);
1345 if (sx != NULL)
1346 {
1347 sx->where = where;
1348
1349 /* N.B. hash field is used to hold length when reading */
1350 sx->hash = len;
1351 sx->next = s->string_list;
1352 memcpy(sx->str, *out, len);
1353
1354 s->string_list = sx;
1355
1356 if (((s->flags & ASL_FILE_FLAG_UNLIMITED_CACHE) == 0) && (s->string_cache_count == CACHE_SIZE))
1357 {
1358 /* drop last (lru) string from cache */
1359 file_string_t *sp = s->string_list;
1360 sx = sp->next;
1361
1362 /* NB CACHE_SIZE must be > 1 */
1363 while (sx->next != NULL)
1364 {
1365 sp = sx;
1366 sx = sx->next;
1367 }
1368
1369 sp->next = NULL;
1370 file_string_dispose(s, sx);
1371 }
1372 else
1373 {
1374 s->string_cache_count++;
1375 }
1376 }
1377 }
1378
81582353
A
1379 return ASL_STATUS_OK;
1380}
1381
1382static uint16_t
f3df4c03 1383asl_file_fetch_helper_16(asl_file_t *s, char **p, asl_msg_t *m, const char *key)
81582353
A
1384{
1385 uint16_t out;
1386 char str[256];
1387
1388 out = _asl_get_16(*p);
1389 *p += sizeof(uint16_t);
1390
1391 if ((m == NULL) || (key == NULL)) return out;
1392
1393 snprintf(str, sizeof(str), "%hu", out);
f3df4c03 1394 asl_msg_set_key_val(m, key, str);
81582353
A
1395
1396 return out;
1397}
1398
1399static uint32_t
f3df4c03 1400asl_file_fetch_helper_32(asl_file_t *s, char **p, asl_msg_t *m, const char *key, int ignore, uint32_t ignoreval)
81582353
A
1401{
1402 uint32_t out, doit;
1403 char str[256];
1404
1405 out = _asl_get_32(*p);
1406 *p += sizeof(uint32_t);
1407
1408 if ((m == NULL) || (key == NULL)) return out;
1409
1410 doit = 1;
1411 if ((ignore != 0) && (out == ignoreval)) doit = 0;
1412 if (doit != 0)
1413 {
1414 snprintf(str, sizeof(str), "%u", out);
f3df4c03 1415 asl_msg_set_key_val(m, key, str);
81582353
A
1416 }
1417
1418 return out;
1419}
1420
1421static uint64_t
f3df4c03 1422asl_file_fetch_helper_64(asl_file_t *s, char **p, asl_msg_t *m, const char *key)
81582353
A
1423{
1424 uint64_t out;
1425 char str[256];
1426
1427 out = _asl_get_64(*p);
1428 *p += sizeof(uint64_t);
1429
1430 if ((m == NULL) || (key == NULL)) return out;
1431
1432 snprintf(str, sizeof(str), "%llu", out);
f3df4c03 1433 asl_msg_set_key_val(m, key, str);
81582353
A
1434
1435 return out;
1436}
1437
1438static uint64_t
f3df4c03 1439asl_file_fetch_helper_str(asl_file_t *s, char **p, asl_msg_t *m, const char *key, uint32_t *err)
81582353
A
1440{
1441 uint64_t out;
1442 char *val;
1443 uint32_t status, len;
1444
1445 out = _asl_get_64(*p);
1446 *p += sizeof(uint64_t);
1447
1448 val = NULL;
1449 len = 0;
1450 status = ASL_STATUS_OK;
5222c21d 1451 if (out != 0) status = asl_file_fetch_object(s, ASL_FILE_TYPE_STR, out, &val, &len);
81582353
A
1452
1453 if (err != NULL) *err = status;
1454 if ((status == ASL_STATUS_OK) && (val != NULL))
1455 {
f3df4c03 1456 asl_msg_set_key_val(m, key, val);
81582353
A
1457 free(val);
1458 }
1459
1460 return out;
1461}
1462
f3df4c03
A
1463static ASL_STATUS
1464asl_file_fetch_pos(asl_file_t *s, uint64_t where, int dir, asl_msg_t **msg)
81582353
A
1465{
1466 char *buf, *p, *k, *v;
1467 file_record_t r;
1468 uint32_t i, status, len, buflen, kvn;
1469 uint64_t x64, kv;
f3df4c03 1470 asl_msg_t *out;
81582353
A
1471 off_t off;
1472
1473 if (s == NULL) return ASL_STATUS_INVALID_STORE;
f3df4c03 1474 if (s->store == NULL) return ASL_STATUS_INVALID_STORE;
81582353 1475 if (msg == NULL) return ASL_STATUS_INVALID_ARG;
f3df4c03 1476 if ((s->flags & ASL_FILE_FLAG_READ) == 0) return ASL_STATUS_WRITE_ONLY;
81582353
A
1477
1478 buf = NULL;
1479 buflen = 0;
5222c21d 1480 status = asl_file_fetch_object(s, ASL_FILE_TYPE_MSG, where, &buf, &buflen);
81582353
A
1481 if ((status != ASL_STATUS_OK) || (buf == NULL))
1482 {
1483 s->cursor = 0;
1484 s->cursor_xid = 0;
1485 return status;
1486 }
1487
1488 /* check buffer size */
1489 kvn = _asl_get_32(buf + BUFFER_OFFSET_KVCOUNT);
1490 if (buflen < (MSG_RECORD_FIXED_LENGTH - RECORD_COMMON_LEN + (kvn * sizeof(uint64_t))))
1491 {
1492 free(buf);
1493 s->cursor = 0;
1494 s->cursor_xid = 0;
1495 return ASL_STATUS_READ_FAILED;
1496 }
1497
f3df4c03
A
1498 out = asl_msg_new(ASL_TYPE_MSG);
1499 if (out == NULL)
1500 {
1501 free(buf);
1502 return ASL_STATUS_NO_MEMORY;
1503 }
81582353
A
1504
1505 memset(&r, 0, sizeof(file_record_t));
1506 p = buf;
1507
1508 r.next = asl_file_fetch_helper_64(s, &p, NULL, NULL);
1509 r.mid = asl_file_fetch_helper_64(s, &p, out, ASL_KEY_MSG_ID);
1510 r.time = asl_file_fetch_helper_64(s, &p, out, ASL_KEY_TIME);
1511 r.nano = asl_file_fetch_helper_32(s, &p, out, ASL_KEY_TIME_NSEC, 0, 0);
1512 r.level = asl_file_fetch_helper_16(s, &p, out, ASL_KEY_LEVEL);
1513 r.flags = asl_file_fetch_helper_16(s, &p, NULL, NULL);
1514 r.pid = asl_file_fetch_helper_32(s, &p, out, ASL_KEY_PID, 0, 0);
1515 r.uid = asl_file_fetch_helper_32(s, &p, out, ASL_KEY_UID, 1, (uint32_t)-1);
1516 r.gid = asl_file_fetch_helper_32(s, &p, out, ASL_KEY_GID, 1, (uint32_t)-1);
1517 r.ruid = asl_file_fetch_helper_32(s, &p, out, ASL_KEY_READ_UID, 1, (uint32_t)-1);
1518 r.rgid = asl_file_fetch_helper_32(s, &p, out, ASL_KEY_READ_GID, 1, (uint32_t)-1);
1519 r.refpid = asl_file_fetch_helper_32(s, &p, out, ASL_KEY_REF_PID, 1, 0);
1520 r.kvcount = asl_file_fetch_helper_32(s, &p, NULL, NULL, 0, 0);
1521
1522 status = ASL_STATUS_OK;
1523 r.host = asl_file_fetch_helper_str(s, &p, out, ASL_KEY_HOST, &status); /* 68 */
1524 if (status == ASL_STATUS_OK) r.sender = asl_file_fetch_helper_str(s, &p, out, ASL_KEY_SENDER, &status); /* 76 */
1525 if (status == ASL_STATUS_OK) r.facility = asl_file_fetch_helper_str(s, &p, out, ASL_KEY_FACILITY, &status); /* 84 */
1526 if (status == ASL_STATUS_OK) r.message = asl_file_fetch_helper_str(s, &p, out, ASL_KEY_MSG, &status); /* 92 */
1527 if (status == ASL_STATUS_OK) r.refproc = asl_file_fetch_helper_str(s, &p, out, ASL_KEY_REF_PROC, &status); /* 100 */
1528 if (status == ASL_STATUS_OK) r.session = asl_file_fetch_helper_str(s, &p, out, ASL_KEY_SESSION, &status); /* 108 */
1529
1530 if (status != ASL_STATUS_OK)
1531 {
f3df4c03 1532 asl_msg_release(out);
81582353
A
1533 free(buf);
1534 s->cursor = 0;
1535 s->cursor_xid = 0;
1536 return status;
1537 }
1538
1539 kvn = r.kvcount / 2;
1540
1541 for (i = 0; i < kvn; i++)
1542 {
81582353 1543 k = NULL;
f3df4c03 1544 v = NULL;
81582353 1545 len = 0;
f3df4c03
A
1546
1547 kv = _asl_get_64(p);
1548 p += sizeof(uint64_t);
1549
5222c21d 1550 status = asl_file_fetch_object(s, ASL_FILE_TYPE_STR, kv, &k, &len);
81582353
A
1551 if (status != ASL_STATUS_OK)
1552 {
f3df4c03 1553 asl_msg_release(out);
81582353
A
1554 free(buf);
1555 s->cursor = 0;
1556 s->cursor_xid = 0;
1557 return status;
1558 }
1559
1560 kv = _asl_get_64(p);
1561 p += sizeof(uint64_t);
81582353
A
1562 len = 0;
1563
1564 if (kv != 0)
1565 {
5222c21d 1566 status = asl_file_fetch_object(s, ASL_FILE_TYPE_STR, kv, &v, &len);
81582353
A
1567 if (status != ASL_STATUS_OK)
1568 {
f3df4c03
A
1569 free(k);
1570 asl_msg_release(out);
81582353
A
1571 free(buf);
1572 s->cursor = 0;
1573 s->cursor_xid = 0;
1574 return status;
1575 }
1576 }
1577
1578 if ((status == ASL_STATUS_OK) && (k != NULL))
1579 {
f3df4c03 1580 asl_msg_set_key_val(out, k, v);
81582353 1581 }
f3df4c03
A
1582
1583 free(k);
1584 free(v);
81582353
A
1585 }
1586
1587 r.prev = asl_file_fetch_helper_64(s, &p, NULL, NULL); /* 116 */
1588
1589 free(buf);
1590
1591 if (dir >= 0)
1592 {
1593 if ((r.next != 0) && (r.next <= s->cursor))
1594 {
1595 /*
1596 * Next offset goes backwards or loops.
1597 * The database is corrupt, but we allow this call to fail
1598 * quietly so that the current record fetch succeeds.
1599 */
1600 s->cursor = 0;
1601 s->cursor_xid = 0;
1602 return ASL_STATUS_OK;
1603 }
1604
1605 s->cursor = r.next;
1606 }
1607 else
1608 {
1609 if ((r.prev != 0) && (r.prev >= s->cursor))
1610 {
1611 /*
1612 * Prev offset goes forward or loops.
1613 * The database is corrupt, but we allow this call to fail
1614 * quietly so that the current record fetch succeeds.
1615 */
1616 s->cursor = 0;
1617 s->cursor_xid = 0;
1618 return ASL_STATUS_OK;
1619 }
1620
1621 s->cursor = r.prev;
1622 }
1623
1624 s->cursor_xid = 0;
1625
1626 if (s->cursor != 0)
1627 {
1628 off = s->cursor + RECORD_COMMON_LEN + sizeof(uint64_t);
1629 if (off > s->file_size)
1630 {
1631 s->cursor = 0;
1632 s->cursor_xid = 0;
1633 /*
1634 * Next record offset is past the end of the file.
1635 * This is an error, but we allow it to fail quietly
1636 * so that the current record fetch succeeds.
1637 */
1638 *msg = out;
1639 return ASL_STATUS_OK;
1640 }
1641
1642 status = fseeko(s->store, off, SEEK_SET);
1643 if (status != 0)
1644 {
f3df4c03 1645 asl_msg_release(out);
81582353
A
1646 s->cursor = 0;
1647 s->cursor_xid = 0;
1648 return ASL_STATUS_READ_FAILED;
1649 }
1650
1651 status = fread(&x64, sizeof(uint64_t), 1, s->store);
1652 if (status != 1)
1653 {
f3df4c03 1654 asl_msg_release(out);
81582353
A
1655 s->cursor = 0;
1656 s->cursor_xid = 0;
1657 return ASL_STATUS_READ_FAILED;
1658 }
1659
1660 s->cursor_xid = asl_core_ntohq(x64);
1661 }
1662
1663 *msg = out;
1664 return ASL_STATUS_OK;
1665}
1666
f3df4c03 1667ASL_STATUS
81582353
A
1668asl_file_open_read(const char *path, asl_file_t **s)
1669{
1670 asl_file_t *out;
1671 FILE *f;
1672 int i;
1673 uint32_t status, vers, last_len;
1674 char buf[DB_HEADER_LEN];
1675 off_t off;
1676 asl_legacy1_t *legacy;
1677 struct stat sb;
1678
1679 memset(&sb, 0, sizeof(struct stat));
1680 if (stat(path, &sb) != 0) return ASL_STATUS_FAILED;
1681
1682 f = fopen(path, "r");
1683 if (f == NULL)
1684 {
1685 if (errno == EACCES) return ASL_STATUS_ACCESS_DENIED;
1686 return ASL_STATUS_FAILED;
1687 }
1688
1689 i = fread(buf, DB_HEADER_LEN, 1, f);
1690 if (i < 1)
1691 {
1692 fclose(f);
1693 return ASL_STATUS_INVALID_STORE;
1694 }
1695
1696 /* validate header */
1697 if (strncmp(buf, ASL_DB_COOKIE, ASL_DB_COOKIE_LEN))
1698 {
1699 fclose(f);
1700 return ASL_STATUS_INVALID_STORE;
1701 }
1702
1703 legacy = NULL;
1704
1705 vers = _asl_get_32(buf + DB_HEADER_VERS_OFFSET);
1706 if (vers == DB_VERSION_LEGACY_1)
1707 {
1708 fclose(f);
1709 status = asl_legacy1_open(path, &legacy);
1710 if (status != ASL_STATUS_OK) return status;
1711 }
1712
1713 out = (asl_file_t *)calloc(1, sizeof(asl_file_t));
1714 if (out == NULL)
1715 {
1716 fclose(f);
1717 return ASL_STATUS_NO_MEMORY;
1718 }
1719
f3df4c03
A
1720 out->asl_type = ASL_TYPE_FILE;
1721 out->refcount = 1;
1722
81582353 1723 out->store = f;
f3df4c03 1724 out->flags = ASL_FILE_FLAG_READ;
81582353
A
1725 out->version = vers;
1726
1727 if (legacy != NULL)
1728 {
1729 out->flags |= ASL_FILE_FLAG_LEGACY_STORE;
1730 out->legacy = (void *)legacy;
1731
1732 *s = out;
1733 return ASL_STATUS_OK;
1734 }
1735
1736 out->first = _asl_get_64(buf + DB_HEADER_FIRST_OFFSET);
1737 out->last = _asl_get_64(buf + DB_HEADER_LAST_OFFSET);
1738 out->file_size = (size_t)sb.st_size;
1739
1740 /*
1741 * Detect bogus last pointer and check for odd-sized files.
1742 * Setting out->last to zero forces us to follow the linked
1743 * list of records in the file to the last record. That's
1744 * done in the set_position code. It's a bit slower, but it's
1745 * better at preventing crashes in corrupt files.
1746 */
1747
1748 /* records are at least MSG_RECORD_FIXED_LENGTH bytes */
1749 if ((out->last + MSG_RECORD_FIXED_LENGTH) > out->file_size)
1750 {
1751 out->last = 0;
1752 }
1753 else
1754 {
1755 /* read last record length and make sure the file is at least that large */
1756 off = out->last + RECORD_TYPE_LEN;
1757 status = asl_file_read_uint32(out, off, &last_len);
1758 if (status != ASL_STATUS_OK)
1759 {
1760 fclose(out->store);
1761 free(out);
1762 return status;
1763 }
1764
1765 if ((out->last + last_len) > out->file_size) out->last = 0;
1766 }
1767
1768 out->cursor = out->first;
1769 if (out->cursor != 0)
1770 {
1771 off = out->cursor + RECORD_COMMON_LEN + sizeof(uint64_t);
1772 status = asl_file_read_uint64(out, off, &(out->cursor_xid));
1773 if (status != ASL_STATUS_OK)
1774 {
1775 fclose(out->store);
1776 free(out);
1777 return status;
1778 }
1779 }
1780
1781 *s = out;
1782 return ASL_STATUS_OK;
1783}
1784
f3df4c03 1785static ASL_STATUS
81582353
A
1786asl_file_read_set_position_first(asl_file_t *s)
1787{
1788 uint32_t status;
1789 off_t off;
1790
1791 s->cursor = s->first;
1792 s->cursor_xid = 0;
1793
1794 if (s->cursor == 0) return ASL_STATUS_OK;
1795
1796 /* read ID of the first record */
1797 off = s->cursor + RECORD_COMMON_LEN + sizeof(uint64_t);
1798 status = asl_file_read_uint64(s, off, &(s->cursor_xid));
1799 return status;
1800}
1801
f3df4c03
A
1802static ASL_STATUS
1803asl_file_read_set_position_last(asl_file_t *s, int do_count)
81582353
A
1804{
1805 uint64_t next;
1806 uint32_t status;
1807 off_t off;
1808
1809 /*
1810 * If the file has the offset of the last record, we just go there.
1811 * The last record offset was added to improve performance, so it may
1812 * or may not be there. If we don't have the last record offset, we
1813 * just iterate down the record links to find the last one.
1814 *
1815 * Note that s->last may be zero if the file is empty.
1816 */
1817
f3df4c03 1818 if ((s->last != 0) && (do_count == 0))
81582353
A
1819 {
1820 s->cursor = s->last;
1821 off = s->last + RECORD_COMMON_LEN + sizeof(uint64_t);
1822
1823 /* read ID of the last record */
1824 status = asl_file_read_uint64(s, off, &(s->cursor_xid));
1825 return status;
1826 }
1827
1828 /* start at the first record and iterate */
1829 s->cursor = s->first;
1830 s->cursor_xid = 0;
f3df4c03 1831 s->msg_count = 0;
81582353
A
1832
1833 forever
1834 {
1835 off = s->cursor + RECORD_COMMON_LEN;
1836 next = 0;
1837
1838 /* read next offset */
1839 status = asl_file_read_uint64(s, off, &next);
1840 if (status != ASL_STATUS_OK) return status;
1841
1842 /* detect bogus next pointer */
1843 if (((next + MSG_RECORD_FIXED_LENGTH) > s->file_size) || (next <= s->cursor)) next = 0;
1844
f3df4c03
A
1845 s->msg_count++;
1846
81582353
A
1847 if (next == 0)
1848 {
1849 if (s->cursor == 0) return ASL_STATUS_OK;
1850
1851 off = s->cursor + RECORD_COMMON_LEN + sizeof(uint64_t);
1852 status = asl_file_read_uint64(s, off, &(s->cursor_xid));
1853 return ASL_STATUS_OK;
1854 }
1855
1856 s->cursor = next;
1857 }
1858}
1859
f3df4c03 1860ASL_STATUS
81582353
A
1861asl_file_read_set_position(asl_file_t *s, uint32_t pos)
1862{
1863 uint64_t next;
1864 uint32_t len, status;
1865 off_t off;
1866
1867 if (s == NULL) return ASL_STATUS_INVALID_STORE;
1868 if (s->version == 1) return ASL_STATUS_FAILED;
1869
1870 if (pos == ASL_FILE_POSITION_FIRST) return asl_file_read_set_position_first(s);
f3df4c03 1871 if (pos == ASL_FILE_POSITION_LAST) return asl_file_read_set_position_last(s, 0);
81582353
A
1872
1873 off = 0;
1874
1875 if (pos == ASL_FILE_POSITION_PREVIOUS)
1876 {
1877 if (s->cursor == s->first) return ASL_STATUS_NO_RECORDS;
1878 if (s->cursor == 0) return ASL_STATUS_NO_RECORDS;
1879
1880 off = s->cursor + RECORD_TYPE_LEN;
1881 status = asl_file_read_uint32(s, off, &len);
1882 if (status != ASL_STATUS_OK) return status;
1883
1884 /* set offset to read the "previous" field at the end of the record */
1885 off = s->cursor + RECORD_COMMON_LEN + len - sizeof(uint64_t);
1886 }
1887 else if (pos == ASL_FILE_POSITION_NEXT)
1888 {
1889 if (s->cursor == s->last) return ASL_STATUS_NO_RECORDS;
1890 if (s->cursor == 0) return ASL_STATUS_NO_RECORDS;
1891
1892 /* set offset to read the "next" field in the current record */
1893 off = s->cursor + RECORD_COMMON_LEN;
1894 }
1895 else return ASL_STATUS_INVALID_ARG;
1896
1897 s->cursor_xid = 0;
1898
1899 /*
1900 * read offset of next / previous
1901 */
1902 next = 0;
1903 status = asl_file_read_uint64(s, off, &next);
1904 if (status != ASL_STATUS_OK) return ASL_STATUS_READ_FAILED;
1905
1906 /* detect bogus next pointer */
1907 if ((next + MSG_RECORD_FIXED_LENGTH) > s->file_size) next = 0;
1908 else if ((pos == ASL_FILE_POSITION_PREVIOUS) && (next >= s->cursor)) next = 0;
1909 else if ((pos == ASL_FILE_POSITION_NEXT) && (next <= s->cursor)) next = 0;
1910
1911 s->cursor = next;
1912 if (s->cursor == 0) return ASL_STATUS_NO_RECORDS;
1913
1914 /* read ID of the record */
1915 off = s->cursor + RECORD_COMMON_LEN + sizeof(uint64_t);
1916 status = asl_file_read_uint64(s, off, &(s->cursor_xid));
1917 return status;
1918}
1919
f3df4c03
A
1920ASL_STATUS
1921asl_file_fetch_next(asl_file_t *s, asl_msg_t **msg)
81582353
A
1922{
1923 if (s == NULL) return ASL_STATUS_INVALID_STORE;
1924 if (s->version == 1) return ASL_STATUS_FAILED;
1925
1926 return asl_file_fetch_pos(s, s->cursor, 1, msg);
1927}
1928
f3df4c03
A
1929ASL_STATUS
1930asl_file_fetch_previous(asl_file_t *s, asl_msg_t **msg)
81582353
A
1931{
1932 if (s == NULL) return ASL_STATUS_INVALID_STORE;
1933 if (s->version == 1) return ASL_STATUS_FAILED;
1934
1935 return asl_file_fetch_pos(s, s->cursor, -1, msg);
1936}
1937
f3df4c03
A
1938ASL_STATUS
1939asl_file_fetch(asl_file_t *s, uint64_t mid, asl_msg_t **msg)
81582353
A
1940{
1941 uint32_t status;
1942
1943 if (s == NULL) return ASL_STATUS_INVALID_STORE;
f3df4c03 1944 if ((s->flags & ASL_FILE_FLAG_READ) == 0) return ASL_STATUS_WRITE_ONLY;
81582353
A
1945
1946 if (s->version == 1)
1947 {
f3df4c03 1948 if (msg == NULL) return ASL_STATUS_OK;
81582353
A
1949 return asl_legacy1_fetch((asl_legacy1_t *)s->legacy, mid, msg);
1950 }
1951
1952 if (s->cursor_xid == 0)
1953 {
1954 status = asl_file_read_set_position(s, ASL_FILE_POSITION_FIRST);
1955 if (status != ASL_STATUS_OK) return status;
1956 if (s->cursor_xid == 0) return ASL_STATUS_INVALID_ID;
1957 }
1958
1959 while (s->cursor_xid < mid)
1960 {
1961 status = asl_file_read_set_position(s, ASL_FILE_POSITION_NEXT);
1962 if (status != ASL_STATUS_OK) return status;
1963 if (s->cursor_xid > mid) return ASL_STATUS_INVALID_ID;
1964 if (s->cursor_xid == 0) return ASL_STATUS_INVALID_ID;
1965 }
1966
1967 while (s->cursor_xid > mid)
1968 {
1969 status = asl_file_read_set_position(s, ASL_FILE_POSITION_PREVIOUS);
1970 if (status != ASL_STATUS_OK) return status;
1971 if (s->cursor_xid < mid) return ASL_STATUS_INVALID_ID;
1972 if (s->cursor_xid == 0) return ASL_STATUS_INVALID_ID;
1973 }
1974
1975 if (s->cursor_xid != mid) return ASL_STATUS_INVALID_ID;
1976
f3df4c03 1977 if (msg == NULL) return ASL_STATUS_OK;
81582353
A
1978 return asl_file_fetch_pos(s, s->cursor, 1, msg);
1979}
1980
1981__private_extern__ uint64_t
1982asl_file_cursor(asl_file_t *s)
1983{
1984 if (s == NULL) return 0;
f3df4c03 1985 if ((s->flags & ASL_FILE_FLAG_READ) == 0) return 0;
81582353
A
1986 if (s->version == 1) return 0;
1987
1988 return s->cursor_xid;
1989}
1990
f3df4c03
A
1991__private_extern__ ASL_STATUS
1992asl_file_match_start(asl_file_t *s, uint64_t start, int32_t direction)
81582353
A
1993{
1994 uint32_t status, d;
1995
1996 if (s == NULL) return ASL_STATUS_INVALID_STORE;
1997 if (s->version == 1) return ASL_STATUS_INVALID_STORE;
f3df4c03 1998 if ((s->flags & ASL_FILE_FLAG_READ) == 0) return ASL_STATUS_WRITE_ONLY;
81582353
A
1999
2000 d = ASL_FILE_POSITION_NEXT;
2001 if (direction < 0) d = ASL_FILE_POSITION_PREVIOUS;
2002
2003 /*
2004 * find starting point
2005 */
2006 status = ASL_STATUS_OK;
2007 if (direction >= 0) status = asl_file_read_set_position(s, ASL_FILE_POSITION_FIRST);
2008 else status = asl_file_read_set_position(s, ASL_FILE_POSITION_LAST);
2009 if (status != ASL_STATUS_OK) return status;
2010
f3df4c03 2011 while ((status == ASL_STATUS_OK) && (((direction >= 0) && (s->cursor_xid < start)) || ((direction < 0) && (s->cursor_xid > start))))
81582353
A
2012 {
2013 status = asl_file_read_set_position(s, d);
2014 }
2015
2016 return status;
2017}
2018
f3df4c03
A
2019__private_extern__ ASL_STATUS
2020asl_file_match_next(asl_file_t *s, asl_msg_list_t *query, asl_msg_t **msg, uint64_t *last, int32_t direction)
81582353
A
2021{
2022 uint32_t status, d, i, do_match, did_match;
f3df4c03 2023 asl_msg_t *m;
81582353
A
2024
2025 if (s == NULL) return ASL_STATUS_INVALID_STORE;
2026 if (msg == NULL) return ASL_STATUS_INVALID_ARG;
2027 if (s->version == 1) return ASL_STATUS_INVALID_STORE;
f3df4c03 2028 if ((s->flags & ASL_FILE_FLAG_READ) == 0) return ASL_STATUS_WRITE_ONLY;
81582353
A
2029 if (s->cursor == 0) return ASL_STATUS_NO_RECORDS;
2030
2031 *msg = NULL;
f3df4c03 2032
81582353 2033 do_match = 1;
f3df4c03
A
2034 if (query == NULL) do_match = 0;
2035 else if (query->count == 0) do_match = 0;
81582353
A
2036
2037 d = ASL_FILE_POSITION_NEXT;
2038 if (direction < 0) d = ASL_FILE_POSITION_PREVIOUS;
2039
81582353
A
2040 m = NULL;
2041
f3df4c03 2042 *last = s->cursor_xid;
81582353
A
2043
2044 status = asl_file_fetch_pos(s, s->cursor, direction, &m);
2045 if (status == ASL_STATUS_ACCESS_DENIED) return ASL_STATUS_MATCH_FAILED;
2046 if ((status == ASL_STATUS_INVALID_ARG) && (s->cursor == 0)) return ASL_STATUS_NO_RECORDS;
2047 if (status != ASL_STATUS_OK) return status;
2048
2049 did_match = 1;
2050
2051 if (do_match != 0)
2052 {
2053 did_match = 0;
2054
2055 for (i = 0; (i < query->count) && (did_match == 0); i++)
2056 {
f3df4c03 2057 did_match = asl_msg_cmp(query->msg[i], m);
81582353
A
2058 }
2059 }
2060
2061 if (did_match != 0)
2062 {
2063 *msg = m;
2064 return ASL_STATUS_OK;
2065 }
2066
2067 *msg = NULL;
f3df4c03 2068 asl_msg_release(m);
81582353
A
2069 return ASL_STATUS_MATCH_FAILED;
2070}
2071
f3df4c03
A
2072asl_msg_list_t *
2073asl_file_match(asl_file_t *s, asl_msg_list_t *qlist, uint64_t *last, uint64_t start, uint32_t count, uint32_t duration, int32_t direction)
81582353
A
2074{
2075 uint32_t status, d, i, do_match, did_match, rescount;
f3df4c03
A
2076 asl_msg_t *m;
2077 struct timeval now, finish;
2078 asl_msg_list_t *out = NULL;
81582353 2079
f3df4c03
A
2080 if (s == NULL) return NULL;
2081 if ((s->flags & ASL_FILE_FLAG_READ) == 0) return NULL;
81582353
A
2082
2083 if (s->version == 1)
2084 {
f3df4c03
A
2085 asl_legacy1_match((asl_legacy1_t *)s->legacy, qlist, &out, last, start, count, direction);
2086 return out;
81582353
A
2087 }
2088
2089 do_match = 1;
f3df4c03
A
2090 if (qlist == NULL) do_match = 0;
2091 else if (qlist->count == 0) do_match = 0;
2092
81582353
A
2093 rescount = 0;
2094
2095 d = ASL_FILE_POSITION_NEXT;
2096 if (direction < 0) d = ASL_FILE_POSITION_PREVIOUS;
2097
81582353
A
2098 /*
2099 * find starting point
2100 */
2101 status = ASL_STATUS_OK;
2102 if (direction >= 0) status = asl_file_read_set_position(s, ASL_FILE_POSITION_FIRST);
2103 else status = asl_file_read_set_position(s, ASL_FILE_POSITION_LAST);
f3df4c03 2104 if (status != ASL_STATUS_OK) return NULL;
81582353 2105
f3df4c03 2106 while ((status == ASL_STATUS_OK) && (((direction >= 0) && (s->cursor_xid < start)) || ((direction < 0) && (s->cursor_xid > start))))
81582353
A
2107 {
2108 status = asl_file_read_set_position(s, d);
2109 }
2110
f3df4c03
A
2111 /* start the timer if a duration was specified */
2112 memset(&finish, 0, sizeof(struct timeval));
2113 if (duration != 0)
2114 {
2115 if (gettimeofday(&finish, NULL) == 0)
2116 {
2117 finish.tv_sec += (duration / USEC_PER_SEC);
2118 finish.tv_usec += (duration % USEC_PER_SEC);
2119 if (finish.tv_usec > USEC_PER_SEC)
2120 {
2121 finish.tv_usec -= USEC_PER_SEC;
2122 finish.tv_sec += 1;
2123 }
2124 }
2125 else
2126 {
2127 /* shouldn't happen, but if gettimeofday failed we just run without a timeout */
2128 memset(&finish, 0, sizeof(struct timeval));
2129 }
2130 }
2131
2132 /*
81582353
A
2133 * loop through records
2134 */
2135 forever
2136 {
2137 m = NULL;
2138 status = asl_file_fetch_pos(s, s->cursor, direction, &m);
2139 if (status == ASL_STATUS_ACCESS_DENIED) continue;
2140 if (status != ASL_STATUS_OK) break;
2141
f3df4c03 2142 *last = s->cursor_xid;
81582353
A
2143
2144 did_match = 1;
2145
2146 if (do_match != 0)
2147 {
2148 did_match = 0;
2149
f3df4c03 2150 for (i = 0; (i < qlist->count) && (did_match == 0); i++)
81582353 2151 {
f3df4c03 2152 did_match = asl_msg_cmp(qlist->msg[i], m);
81582353
A
2153 }
2154 }
2155
2156 if (did_match == 1)
2157 {
2158 /* append m to res */
f3df4c03 2159 if (out == NULL)
81582353 2160 {
f3df4c03
A
2161 out = asl_msg_list_new();
2162 if (out == NULL) return NULL;
81582353 2163 }
81582353 2164
f3df4c03 2165 asl_msg_list_append(out, m);
81582353
A
2166 rescount++;
2167 if ((count != 0) && (rescount >= count)) break;
f3df4c03
A
2168
2169 /* check the timer */
2170 if ((finish.tv_sec != 0) && (gettimeofday(&now, NULL) == 0))
2171 {
2172 if ((now.tv_sec > finish.tv_sec) || ((now.tv_sec == finish.tv_sec) && (now.tv_usec > finish.tv_usec))) break;
2173 }
81582353 2174 }
f3df4c03
A
2175
2176 asl_msg_release(m);
81582353
A
2177 }
2178
f3df4c03 2179 return out;
81582353
A
2180}
2181
2182size_t
2183asl_file_size(asl_file_t *s)
2184{
2185 if (s == NULL) return 0;
2186 return s->file_size;
2187}
2188
2189uint64_t
2190asl_file_ctime(asl_file_t *s)
2191{
2192 if (s == NULL) return 0;
2193 return s->dob;
2194}
2195
2196void
2197asl_file_list_close(asl_file_list_t *head)
2198{
2199 asl_file_list_t *next;
2200
2201 while (head != NULL)
2202 {
2203 next = head->next;
2204 asl_file_close(head->file);
2205 free(head);
2206 head = next;
2207 }
2208}
2209
2210static void
2211asl_file_list_free(asl_file_list_t *head)
2212{
2213 asl_file_list_t *next;
2214
2215 while (head != NULL)
2216 {
2217 next = head->next;
2218 free(head);
2219 head = next;
2220 }
2221}
2222
2223static asl_file_list_t *
2224asl_file_list_insert(asl_file_list_t *list, asl_file_t *f, int32_t dir)
2225{
2226 asl_file_list_t *a, *b, *tmp;
2227
2228 if (f == NULL) return list;
2229
2230 tmp = (asl_file_list_t *)calloc(1, sizeof(asl_file_list_t));
2231 if (tmp == NULL) return NULL;
2232 tmp->file = f;
2233
2234 if (list == NULL) return tmp;
2235
2236 a = list;
2237 if (((dir < 0) && (f->cursor_xid > a->file->cursor_xid)) || ((dir >= 0) && (f->cursor_xid < a->file->cursor_xid)))
2238 {
2239 tmp->next = list;
2240 return tmp;
2241 }
2242
2243 b = a->next;
2244 while (b != NULL)
2245 {
2246 if (((dir < 0) && (f->cursor_xid > b->file->cursor_xid)) || ((dir >= 0) && (f->cursor_xid < b->file->cursor_xid)))
2247 {
2248 tmp->next = b;
2249 a->next = tmp;
2250 return list;
2251 }
2252
2253 a = b;
2254 b = a->next;
2255 }
2256
2257 a->next = tmp;
2258 return list;
2259}
2260
2261asl_file_list_t *
2262asl_file_list_add(asl_file_list_t *list, asl_file_t *f)
2263{
2264 asl_file_list_t *tmp;
2265
2266 if (f == NULL) return list;
2267 if (f->version == 1) return list;
2268
2269 tmp = (asl_file_list_t *)calloc(1, sizeof(asl_file_list_t));
2270 if (tmp == NULL) return NULL;
2271 tmp->file = f;
2272
2273 tmp->next = list;
2274 return tmp;
2275}
2276
2277void *
f3df4c03 2278asl_file_list_match_start(asl_file_list_t *list, uint64_t start, int32_t direction)
81582353
A
2279{
2280 uint32_t status;
2281 asl_file_list_t *n;
2282 asl_file_match_token_t *out;
2283
2284 if (list == NULL) return NULL;
2285
2286 out = (asl_file_match_token_t *)calloc(1, sizeof(asl_file_match_token_t));
2287 if (out == NULL) return NULL;
2288
2289 for (n = list; n != NULL; n = n->next)
2290 {
2291 /* init file for the search */
f3df4c03 2292 status = asl_file_match_start(n->file, start, direction);
81582353
A
2293 if (status != ASL_STATUS_OK) continue;
2294 if (n->file->cursor_xid == 0) continue;
2295
2296 out->list = asl_file_list_insert(out->list, n->file, direction);
2297 }
2298
2299 out->dir = direction;
2300 return out;
2301}
2302
f3df4c03
A
2303ASL_STATUS
2304asl_file_list_match_next(void *token, asl_msg_list_t *qlist, asl_msg_list_t **res, uint32_t count)
81582353
A
2305{
2306 uint32_t status, rescount;
2307 asl_file_list_t *n;
f3df4c03 2308 asl_msg_t *m;
81582353 2309 asl_file_match_token_t *work;
f3df4c03 2310 uint64_t last;
81582353
A
2311
2312 if (token == NULL) return ASL_STATUS_OK;
2313 if (res == NULL) return ASL_STATUS_INVALID_ARG;
2314
2315 work = (asl_file_match_token_t *)token;
2316
2317 rescount = 0;
f3df4c03 2318 last = 0;
81582353
A
2319
2320 while ((work->list != NULL) && ((rescount < count) || (count == 0)))
2321 {
2322 m = NULL;
f3df4c03 2323 status = asl_file_match_next(work->list->file, qlist, &m, &last, work->dir);
81582353
A
2324 if (m != NULL)
2325 {
f3df4c03 2326 if (*res == NULL) *res = asl_msg_list_new();
81582353
A
2327 if (*res == NULL)
2328 {
2329 asl_file_list_free(work->list);
2330 work->list = NULL;
2331 return ASL_STATUS_NO_MEMORY;
2332 }
2333
f3df4c03
A
2334 asl_msg_list_append(*res, m);
2335 asl_msg_release(m);
81582353
A
2336 rescount++;
2337 }
2338
2339 if ((status != ASL_STATUS_OK) || (work->list->file->cursor_xid == 0))
2340 {
2341 n = work->list->next;
2342 free(work->list);
2343 work->list = n;
2344 }
2345
2346 if (work->list != NULL)
2347 {
2348 n = work->list->next;
2349 if (n != NULL)
2350 {
2351 if (((work->dir < 0) && (work->list->file->cursor_xid <= n->file->cursor_xid)) || ((work->dir >= 0) && (work->list->file->cursor_xid > n->file->cursor_xid)))
2352 {
2353 n = work->list;
2354 work->list = work->list->next;
2355 n->next = NULL;
2356 work->list = asl_file_list_insert(work->list, n->file, work->dir);
2357 free(n);
2358 }
2359 }
2360 }
2361 }
2362
2363 return ASL_STATUS_OK;
2364}
2365
2366void
2367asl_file_list_match_end(void *token)
2368{
2369 asl_file_match_token_t *work;
2370
2371 if (token == NULL) return;
2372
2373 work = (asl_file_match_token_t *)token;
2374 asl_file_list_free(work->list);
2375 work->list = NULL;
2376
2377 free(token);
2378}
2379
f3df4c03
A
2380asl_msg_list_t *
2381asl_file_list_match(asl_file_list_t *list, asl_msg_list_t *qlist, uint64_t *last, uint64_t start, uint32_t count, uint32_t duration, int32_t direction)
81582353
A
2382{
2383 uint32_t status, rescount;
2384 asl_file_list_t *files, *n;
f3df4c03 2385 asl_msg_t *m;
81582353 2386 struct timeval now, finish;
f3df4c03 2387 asl_msg_list_t *out = NULL;
81582353 2388
f3df4c03
A
2389 if (list == NULL) return NULL;
2390 if (last == NULL) return NULL;
81582353
A
2391
2392 files = NULL;
2393
2394 for (n = list; n != NULL; n = n->next)
2395 {
2396 /* init file for the search */
f3df4c03 2397 status = asl_file_match_start(n->file, start, direction);
81582353
A
2398 if (status != ASL_STATUS_OK) continue;
2399 if (n->file->cursor_xid == 0) continue;
2400
2401 files = asl_file_list_insert(files, n->file, direction);
2402 }
2403
2404 if (files == NULL)
2405 {
2406 asl_file_list_free(files);
f3df4c03 2407 return NULL;
81582353
A
2408 }
2409
2410 /* start the timer if a timeout was specified */
2411 memset(&finish, 0, sizeof(struct timeval));
f3df4c03 2412 if (duration != 0)
81582353
A
2413 {
2414 if (gettimeofday(&finish, NULL) == 0)
2415 {
f3df4c03
A
2416 finish.tv_sec += (duration / USEC_PER_SEC);
2417 finish.tv_usec += (duration % USEC_PER_SEC);
2418 if (finish.tv_usec > USEC_PER_SEC)
81582353 2419 {
f3df4c03 2420 finish.tv_usec -= USEC_PER_SEC;
81582353
A
2421 finish.tv_sec += 1;
2422 }
2423 }
2424 else
2425 {
2426 /* shouldn't happen, but if gettimeofday failed we just run without a timeout */
2427 memset(&finish, 0, sizeof(struct timeval));
2428 }
2429 }
2430
2431 rescount = 0;
2432 while ((files != NULL) && ((rescount < count) || (count == 0)))
2433 {
2434 m = NULL;
f3df4c03 2435 status = asl_file_match_next(files->file, qlist, &m, last, direction);
81582353
A
2436 if (m != NULL)
2437 {
f3df4c03
A
2438 if (out == NULL) out = asl_msg_list_new();
2439 if (out == NULL)
81582353 2440 {
81582353 2441 asl_file_list_free(files);
f3df4c03 2442 return NULL;
81582353
A
2443 }
2444
f3df4c03
A
2445 asl_msg_list_append(out, m);
2446 asl_msg_release(m);
81582353
A
2447 rescount++;
2448 }
2449
2450 if (files->file->cursor_xid == 0)
2451 {
2452 n = files->next;
2453 free(files);
2454 files = n;
2455 }
2456
2457 if (files != NULL)
2458 {
2459 n = files->next;
2460 if (n != NULL)
2461 {
2462 if (((direction < 0) && (files->file->cursor_xid <= n->file->cursor_xid)) || ((direction >= 0) && (files->file->cursor_xid > n->file->cursor_xid)))
2463 {
2464 n = files;
2465 files = files->next;
2466 n->next = NULL;
2467 files = asl_file_list_insert(files, n->file, direction);
2468 free(n);
2469 }
2470 }
2471 }
2472
2473 /* check the timer */
2474 if ((finish.tv_sec != 0) && (gettimeofday(&now, NULL) == 0))
2475 {
2476 if ((now.tv_sec > finish.tv_sec) || ((now.tv_sec == finish.tv_sec) && (now.tv_usec > finish.tv_usec))) break;
2477 }
2478 }
2479
2480 asl_file_list_free(files);
f3df4c03
A
2481 return out;
2482}
2483
2484#pragma mark -
2485#pragma mark asl_object support
2486
2487static void
2488_jump_dealloc(asl_object_private_t *obj)
2489{
2490 _asl_file_free_internal((asl_file_t *)obj);
2491}
2492
2493static size_t
2494_jump_count(asl_object_private_t *obj)
2495{
2496 asl_file_t *s = (asl_file_t *)obj;
2497 if (s == NULL) return 0;
2498 if ((s->flags & ASL_FILE_FLAG_READ) == 0) return 0;
2499
2500 uint64_t cursor = s->cursor;
2501 uint64_t cursor_xid = s->cursor_xid;
2502
2503 if (asl_file_read_set_position_last((asl_file_t *)obj, 1) != ASL_STATUS_OK) return 0;
2504
2505 s->cursor = cursor;
2506 s->cursor_xid = cursor_xid;
2507 return s->msg_count;
2508}
2509
2510static asl_object_private_t *
2511_jump_next(asl_object_private_t *obj)
2512{
2513 asl_msg_t *msg = NULL;
2514 if (asl_file_fetch_next((asl_file_t *)obj, &msg) != ASL_STATUS_OK) return NULL;
2515 return (asl_object_private_t *)msg;
2516}
2517
2518static asl_object_private_t *
2519_jump_prev(asl_object_private_t *obj)
2520{
2521 asl_msg_t *msg = NULL;
2522 if (asl_file_fetch_previous((asl_file_t *)obj, &msg) != ASL_STATUS_OK) return NULL;
2523 return (asl_object_private_t *)msg;
2524}
2525
2526/* we don't really need to support this, but we are generous */
2527static asl_object_private_t *
2528_jump_get_object_at_index(asl_object_private_t *obj, size_t n)
2529{
2530 asl_msg_t *msg = NULL;
2531 uint64_t mid = n;
2532 if (asl_file_fetch((asl_file_t *)obj, mid, &msg) != ASL_STATUS_OK) return NULL;
2533 return (asl_object_private_t *)msg;
2534}
2535
2536static void
2537_jump_set_iteration_index(asl_object_private_t *obj, size_t n)
2538{
2539 asl_file_t *s = (asl_file_t *)obj;
2540 if (s == NULL) return;
2541 if ((s->flags & ASL_FILE_FLAG_READ) == 0) return;
2542
2543 if (n == 0)
2544 {
2545 asl_file_read_set_position_first(s);
2546 }
2547 else if (n == SIZE_MAX)
2548 {
2549 asl_file_read_set_position_last(s, 0);
2550 }
2551 else
2552 {
2553 /* we don't really need to support this, but we are generous */
2554 asl_file_fetch(s, n, NULL);
2555 }
2556}
2557
2558static void
af7d442c 2559_jump_append(asl_object_private_t *obj, asl_object_private_t *newobj, void *addr)
f3df4c03
A
2560{
2561 uint64_t xid;
2562 asl_file_t *s = (asl_file_t *)obj;
2563 int type = asl_get_type((asl_object_t)newobj);
2564 if (s == NULL) return;
2565 if (s->flags & ASL_FILE_FLAG_READ) return;
2566
2567 if (type == ASL_TYPE_LIST)
2568 {
2569 asl_msg_t *msg;
2570 asl_msg_list_reset_iteration((asl_msg_list_t *)newobj, 0);
2571 while (NULL != (msg = asl_msg_list_next((asl_msg_list_t *)newobj)))
2572 {
2573 if (asl_file_save(s, msg, &xid) != ASL_STATUS_OK) return;
2574 }
2575 }
2576 else if ((type == ASL_TYPE_MSG) || (type == ASL_TYPE_QUERY))
2577 {
2578 asl_file_save(s, (asl_msg_t *)newobj, &xid);
2579 }
2580}
2581
2582static asl_object_private_t *
2583_jump_search(asl_object_private_t *obj, asl_object_private_t *query)
2584{
2585 asl_file_t *s = (asl_file_t *)obj;
2586 int type = asl_get_type((asl_object_t)query);
2587 asl_msg_list_t *out = NULL;
2588 asl_msg_list_t *ql = NULL;
2589 uint64_t last;
2590
2591 if (query == NULL)
2592 {
2593 return (asl_object_private_t *)asl_file_match(s, NULL, &last, 0, 0, 0, 1);
2594 }
2595 else if (type == ASL_TYPE_LIST)
2596 {
2597 return (asl_object_private_t *)asl_file_match(s, (asl_msg_list_t *)query, &last, 0, 0, 0, 1);
2598 }
2599 else if ((type == ASL_TYPE_MSG) || (type == ASL_TYPE_QUERY))
2600 {
2601 ql = asl_msg_list_new();
2602 asl_msg_list_append(ql, query);
2603
2604 out = asl_file_match(s, ql, &last, 0, 0, 0, 1);
2605 asl_msg_list_release(ql);
2606 return (asl_object_private_t *)out;
2607 }
2608
2609 return NULL;
2610}
2611
2612static asl_object_private_t *
2613_jump_match(asl_object_private_t *obj, asl_object_private_t *qlist, size_t *last, size_t start, size_t count, uint32_t duration, int32_t dir)
2614{
2615 uint64_t x;
2616 asl_msg_list_t *out = asl_file_match((asl_file_t *)obj, (asl_msg_list_t *)qlist, &x, start, count, duration, dir);
2617 *last = x;
2618 return (asl_object_private_t *)out;
81582353
A
2619}
2620
f3df4c03
A
2621__private_extern__ const asl_jump_table_t *
2622asl_file_jump_table()
81582353 2623{
f3df4c03
A
2624 static const asl_jump_table_t jump =
2625 {
2626 .alloc = NULL,
2627 .dealloc = &_jump_dealloc,
2628 .set_key_val_op = NULL,
2629 .unset_key = NULL,
2630 .get_val_op_for_key = NULL,
2631 .get_key_val_op_at_index = NULL,
2632 .count = &_jump_count,
2633 .next = &_jump_next,
2634 .prev = &_jump_prev,
2635 .get_object_at_index = &_jump_get_object_at_index,
2636 .set_iteration_index = &_jump_set_iteration_index,
2637 .remove_object_at_index = NULL,
2638 .append = &_jump_append,
2639 .prepend = NULL,
2640 .search = &_jump_search,
2641 .match = &_jump_match
2642 };
2643
2644 return &jump;
81582353 2645}