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