]> git.saurik.com Git - apple/libc.git/blob - gen/asl_file.c
159d628606f6841d67091b704e284ec44b279a28
[apple/libc.git] / gen / asl_file.c
1 /*
2 * Copyright (c) 2007-2008 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * "Portions Copyright (c) 2007 Apple Inc. All Rights
7 * Reserved. This file contains Original Code and/or Modifications of
8 * Original Code as defined in and that are subject to the Apple Public
9 * Source License Version 1.0 (the 'License'). You may not use this file
10 * except in compliance with the License. Please obtain a copy of the
11 * License at http://www.apple.com/publicsource and read it before using
12 * this file.
13 *
14 * The Original Code and all software distributed under the License are
15 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
16 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
17 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
19 * License for the specific language governing rights and limitations
20 * under the License."
21 *
22 * @APPLE_LICENSE_HEADER_END@
23 */
24
25 #include <asl_core.h>
26 #include <asl_file.h>
27 #include <fcntl.h>
28 #include <stdlib.h>
29 #include <stddef.h>
30 #include <stdio.h>
31 #include <unistd.h>
32 #include <sys/errno.h>
33 #include <string.h>
34 #include <sys/stat.h>
35 #include <time.h>
36 #include <sys/time.h>
37 #include <asl_private.h>
38 #include <asl_legacy1.h>
39
40 extern time_t asl_parse_time(const char *str);
41 extern int asl_msg_cmp(asl_msg_t *a, asl_msg_t *b);
42
43 #define forever for(;;)
44 #define MILLION 1000000
45
46 /*
47 * MSG and STR records have (at least) a type (uint16_t) and a length (uint32_t)
48 * type and level are both 16 bit fields so that alignment isn't a pain.
49 */
50 #define RECORD_COMMON_LEN 6
51 #define RECORD_TYPE_LEN 2
52 #define RECORD_OFFSET_FLAGS 30
53 #define RECORD_OFFSET_RUID 44
54 #define RECORD_OFFSET_RGID 48
55
56 #define SCRATCH_BUFFER_SIZE (MSG_RECORD_FIXED_LENGTH + (20 * sizeof(uint64_t)))
57
58 typedef 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
82 typedef struct
83 {
84 asl_file_list_t *list;
85 int dir;
86 } asl_file_match_token_t;
87
88 static 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
97 static 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
106 static 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
115 static 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
124 static 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
133 static 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
142 static uint32_t
143 asl_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
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 uint32_t
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
171 status = fseeko(s->store, off, SEEK_SET);
172 if (status != 0) return ASL_STATUS_READ_FAILED;
173
174 val = 0;
175
176 status = fread(&val, sizeof(uint64_t), 1, s->store);
177 if (status != 1) return ASL_STATUS_READ_FAILED;
178
179 if (out != NULL) *out = asl_core_ntohq(val);
180 return ASL_STATUS_OK;
181 }
182
183 uint32_t
184 asl_file_close(asl_file_t *s)
185 {
186 file_string_t *x;
187
188 if (s == NULL) return ASL_STATUS_OK;
189
190 if (s->version == 1)
191 {
192 return asl_legacy1_close((asl_legacy1_t *)s->legacy);
193 }
194
195 while (s->string_list != NULL)
196 {
197 x = s->string_list->next;
198 free(s->string_list);
199 s->string_list = x;
200 }
201
202 if (s->store != NULL) fclose(s->store);
203
204 memset(s, 0, sizeof(asl_file_t));
205 free(s);
206
207 return ASL_STATUS_OK;
208 }
209
210 uint32_t
211 asl_file_open_write(const char *path, mode_t mode, uid_t uid, gid_t gid, asl_file_t **s)
212 {
213 time_t now;
214 int i, status, fd;
215 struct stat sb;
216 char buf[DB_HEADER_LEN];
217 asl_file_t *out;
218 uint32_t aslstatus, vers;
219
220 memset(&sb, 0, sizeof(struct stat));
221
222 status = stat(path, &sb);
223 if (status == 0)
224 {
225 /* XXX Check that mode, uid, and gid are correct */
226 out = (asl_file_t *)calloc(1, sizeof(asl_file_t));
227 if (out == NULL) return ASL_STATUS_NO_MEMORY;
228
229 out->store = fopen(path, "r+");
230 if (out->store == NULL)
231 {
232 free(out);
233 return ASL_STATUS_FAILED;
234 }
235
236 out->file_size = sb.st_size;
237
238 i = fread(buf, DB_HEADER_LEN, 1, out->store);
239 if (i < 1)
240 {
241 asl_file_close(out);
242 return ASL_STATUS_READ_FAILED;
243 }
244
245 /* check version */
246 vers = _asl_get_32(buf + DB_HEADER_VERS_OFFSET);
247 if (vers != DB_VERSION)
248 {
249 asl_file_close(out);
250 return ASL_STATUS_INVALID_STORE;
251 }
252
253 out->dob = _asl_get_64(buf + DB_HEADER_TIME_OFFSET);
254 out->first = _asl_get_64(buf + DB_HEADER_FIRST_OFFSET);
255 out->last = _asl_get_64(buf + DB_HEADER_LAST_OFFSET);
256
257 aslstatus = asl_file_read_set_position(out, ASL_FILE_POSITION_LAST);
258 if (aslstatus != ASL_STATUS_OK)
259 {
260 asl_file_close(out);
261 return aslstatus;
262 }
263
264 out->prev = out->cursor;
265 status = fseeko(out->store, 0, SEEK_END);
266 if (status != 0)
267 {
268 asl_file_close(out);
269 return ASL_STATUS_READ_FAILED;
270 }
271
272 out->file_size = ftello(out->store);
273
274 /* scratch buffer for file writes (we test for NULL before using it) */
275 out->scratch = malloc(SCRATCH_BUFFER_SIZE);
276
277 *s = out;
278
279 return ASL_STATUS_OK;
280 }
281
282 if (errno != ENOENT) return ASL_STATUS_FAILED;
283
284 fd = open(path, O_RDWR | O_CREAT | O_EXCL, mode);
285 if (fd < 0) return ASL_STATUS_FAILED;
286
287 status = fchown(fd, uid, gid);
288 if (status != 0)
289 {
290 close(fd);
291 unlink(path);
292 return ASL_STATUS_FAILED;
293 }
294
295 out = (asl_file_t *)calloc(1, sizeof(asl_file_t));
296 if (out == NULL) return ASL_STATUS_NO_MEMORY;
297
298 out->store = fdopen(fd, "w+");
299 if (out->store == NULL)
300 {
301 free(out);
302 return ASL_STATUS_FAILED;
303 }
304
305 memset(buf, 0, sizeof(buf));
306 memcpy(buf, ASL_DB_COOKIE, ASL_DB_COOKIE_LEN);
307
308 _asl_put_32(DB_VERSION, buf + DB_HEADER_VERS_OFFSET);
309
310 now = time(NULL);
311 out->dob = now;
312 _asl_put_64(out->dob, buf + DB_HEADER_TIME_OFFSET);
313
314 _asl_put_32(CACHE_SIZE, buf + DB_HEADER_CSIZE_OFFSET);
315
316 status = fwrite(buf, sizeof(buf), 1, out->store);
317 if (status != 1)
318 {
319 fclose(out->store);
320 free(out);
321 unlink(path);
322 return ASL_STATUS_FAILED;
323 }
324
325 out->file_size = sizeof(buf);
326
327 *s = out;
328
329 return ASL_STATUS_OK;
330 }
331
332 uint32_t
333 asl_file_compact(asl_file_t *s, const char *path, mode_t mode, uid_t uid, gid_t gid)
334 {
335 asl_file_t *new;
336 struct stat sb;
337 aslmsg m;
338 uint64_t xid;
339 uint32_t status;
340
341 if (s == NULL) return ASL_STATUS_INVALID_STORE;
342 if (path == NULL) return ASL_STATUS_INVALID_ARG;
343
344 if (s->version == 1) return ASL_STATUS_FAILED;
345
346 memset(&sb, 0, sizeof(struct stat));
347
348 if (stat(path, &sb) == 0) return ASL_STATUS_FAILED;
349 if (errno != ENOENT) return ASL_STATUS_FAILED;
350
351 status = asl_file_read_set_position(s, ASL_FILE_POSITION_FIRST);
352 if (status != ASL_STATUS_OK) return status;
353
354 new = NULL;
355 status = asl_file_open_write(path, mode, uid, gid, &new);
356 if (status != ASL_STATUS_OK) return status;
357 new->flags = ASL_FILE_FLAG_UNLIMITED_CACHE | ASL_FILE_FLAG_PRESERVE_MSG_ID;
358
359 while ((status == ASL_STATUS_OK) && (s->cursor != 0))
360 {
361 m = NULL;
362 status = asl_file_fetch_next(s, &m);
363 if (status != ASL_STATUS_OK) break;
364
365 xid = 0;
366 status = asl_file_save(new, m, &xid);
367 asl_free(m);
368 }
369
370 asl_file_close(new);
371 return status;
372 }
373
374 static uint32_t
375 asl_file_string_encode(asl_file_t *s, const char *str, uint64_t *out)
376 {
377 uint32_t i, hash, len, x32;
378 file_string_t *sp, *sx, *sl;
379 uint64_t x64;
380 uint8_t inls;
381 uint16_t type;
382 off_t off;
383 char *p;
384
385 if (s == NULL) return ASL_STATUS_INVALID_STORE;
386 if (str == NULL) return ASL_STATUS_INVALID_ARG;
387
388 len = strlen(str);
389
390 /* inline strings */
391 if (len < 8)
392 {
393 /* inline string */
394 inls = len;
395 inls |= 0x80;
396
397 x64 = 0;
398 p = (char *)&x64;
399 memcpy(p, &inls, 1);
400 memcpy(p + 1, str, len);
401 *out = asl_core_ntohq(x64);
402 return ASL_STATUS_OK;
403 }
404
405 /* check the cache */
406 hash = asl_core_string_hash(str, len);
407
408 sp = NULL;
409 for (sx = s->string_list; sx != NULL; sx = sx->next)
410 {
411 if ((hash == sx->hash) && (!strcmp(str, sx->str)))
412 {
413 /* Move this string to the head of the list */
414 if (sp != NULL)
415 {
416 sl = s->string_list;
417 sp->next = sx->next;
418 sx->next = sl;
419 s->string_list = sx;
420 }
421
422 *out = sx->where;
423 return ASL_STATUS_OK;
424 }
425
426 sp = sx;
427 }
428
429 off = ftello(s->store);
430
431 /* Type */
432 type = htons(ASL_FILE_TYPE_STR);
433 i = fwrite(&type, sizeof(uint16_t), 1, s->store);
434 if (i != 1) return ASL_STATUS_WRITE_FAILED;
435 s->file_size += sizeof(uint16_t);
436
437 /* Length (includes trailing nul) */
438 x32 = htonl(len + 1);
439 i = fwrite(&x32, sizeof(uint32_t), 1, s->store);
440 if (i != 1) return ASL_STATUS_WRITE_FAILED;
441 s->file_size += sizeof(uint32_t);
442
443 /* String data (nul terminated) */
444 i = fwrite(str, len + 1, 1, s->store);
445 if (i != 1) return ASL_STATUS_WRITE_FAILED;
446 s->file_size += len;
447
448 /* create file_string_t and insert into the cache */
449 sx = (file_string_t *)calloc(1, offsetof(file_string_t, str) + len + 1);
450 if (sx == NULL) return ASL_STATUS_NO_MEMORY;
451
452 sx->where = off;
453 sx->hash = hash;
454 sx->next = s->string_list;
455 memcpy(sx->str, str, len);
456
457 s->string_list = sx;
458
459 if (((s->flags & ASL_FILE_FLAG_UNLIMITED_CACHE) == 0) && (s->string_count == CACHE_SIZE))
460 {
461 /* drop last (lru) string from cache */
462 sp = s->string_list;
463 sx = sp->next;
464
465 /* NB CACHE_SIZE must be > 1 */
466 while (sx->next != NULL)
467 {
468 sp = sx;
469 sx = sx->next;
470 }
471
472 sp->next = NULL;
473 free(sx);
474 }
475 else
476 {
477 s->string_count++;
478 }
479
480 *out = off;
481 return ASL_STATUS_OK;
482 }
483
484 /*
485 * Encode an aslmsg as a record structure.
486 * Creates and caches strings.
487 */
488 uint32_t
489 asl_file_save(asl_file_t *s, aslmsg msg, uint64_t *mid)
490 {
491 char *buf, *p;
492 uint32_t len, i, status;
493 file_record_t r;
494 uint64_t k, v;
495 uint64_t *kvlist;
496 off_t off;
497
498 if (s == NULL) return ASL_STATUS_INVALID_STORE;
499 if (msg == NULL) return ASL_STATUS_INVALID_MESSAGE;
500
501 if (s->flags & ASL_FILE_FLAG_READ_ONLY) return ASL_STATUS_READ_ONLY;
502
503 memset(&r, 0, sizeof(file_record_t));
504
505 r.flags = 0;
506 r.level = ASL_LEVEL_DEBUG;
507 r.pid = -1;
508 r.uid = -2;
509 r.gid = -2;
510 r.ruid = -1;
511 r.rgid = -1;
512 r.time = 0;
513 r.nano = 0;
514 r.prev = s->prev;
515 kvlist = NULL;
516
517 for (i = 0; i < msg->count; i++)
518 {
519 if (msg->key[i] == NULL)
520 {
521 continue;
522 }
523 else if (!strcmp(msg->key[i], ASL_KEY_TIME))
524 {
525 if (msg->val[i] != NULL) r.time = asl_parse_time(msg->val[i]);
526 }
527 else if (!strcmp(msg->key[i], ASL_KEY_TIME_NSEC))
528 {
529 if (msg->val[i] != NULL) r.nano = atoi(msg->val[i]);
530 }
531 else if (!strcmp(msg->key[i], ASL_KEY_HOST))
532 {
533 if (msg->val[i] != NULL)
534 {
535 status = asl_file_string_encode(s, msg->val[i], &(r.host));
536 if (status != ASL_STATUS_OK)
537 {
538 if (kvlist != NULL) free(kvlist);
539 return status;
540 }
541 }
542 }
543 else if (!strcmp(msg->key[i], ASL_KEY_SENDER))
544 {
545 if (msg->val[i] != NULL)
546 {
547 status = asl_file_string_encode(s, msg->val[i], &(r.sender));
548 if (status != ASL_STATUS_OK)
549 {
550 if (kvlist != NULL) free(kvlist);
551 return status;
552 }
553 }
554 }
555 else if (!strcmp(msg->key[i], ASL_KEY_PID))
556 {
557 if (msg->val[i] != NULL) r.pid = atoi(msg->val[i]);
558 }
559 else if (!strcmp(msg->key[i], ASL_KEY_REF_PID))
560 {
561 if (msg->val[i] != NULL) r.refpid = atoi(msg->val[i]);
562 }
563 else if (!strcmp(msg->key[i], ASL_KEY_UID))
564 {
565 if (msg->val[i] != NULL) r.uid = atoi(msg->val[i]);
566 }
567 else if (!strcmp(msg->key[i], ASL_KEY_GID))
568 {
569 if (msg->val[i] != NULL) r.gid = atoi(msg->val[i]);
570 }
571 else if (!strcmp(msg->key[i], ASL_KEY_LEVEL))
572 {
573 if (msg->val[i] != NULL) r.level = atoi(msg->val[i]);
574 }
575 else if (!strcmp(msg->key[i], ASL_KEY_MSG))
576 {
577 if (msg->val[i] != NULL)
578 {
579 status = asl_file_string_encode(s, msg->val[i], &(r.message));
580 if (status != ASL_STATUS_OK)
581 {
582 if (kvlist != NULL) free(kvlist);
583 return status;
584 }
585 }
586 }
587 else if (!strcmp(msg->key[i], ASL_KEY_FACILITY))
588 {
589 if (msg->val[i] != NULL)
590 {
591 status = asl_file_string_encode(s, msg->val[i], &(r.facility));
592 if (status != ASL_STATUS_OK)
593 {
594 if (kvlist != NULL) free(kvlist);
595 return status;
596 }
597 }
598 }
599 else if (!strcmp(msg->key[i], ASL_KEY_REF_PROC))
600 {
601 if (msg->val[i] != NULL)
602 {
603 status = asl_file_string_encode(s, msg->val[i], &(r.refproc));
604 if (status != ASL_STATUS_OK)
605 {
606 if (kvlist != NULL) free(kvlist);
607 return status;
608 }
609 }
610 }
611 else if (!strcmp(msg->key[i], ASL_KEY_SESSION))
612 {
613 if (msg->val[i] != NULL)
614 {
615 status = asl_file_string_encode(s, msg->val[i], &(r.session));
616 if (status != ASL_STATUS_OK)
617 {
618 if (kvlist != NULL) free(kvlist);
619 return status;
620 }
621 }
622 }
623 else if (!strcmp(msg->key[i], ASL_KEY_READ_UID))
624 {
625 if (((r.flags & ASL_MSG_FLAG_READ_UID_SET) == 0) && (msg->val[i] != NULL))
626 {
627 r.ruid = atoi(msg->val[i]);
628 r.flags |= ASL_MSG_FLAG_READ_UID_SET;
629 }
630 }
631 else if (!strcmp(msg->key[i], ASL_KEY_READ_GID))
632 {
633 if (((r.flags & ASL_MSG_FLAG_READ_GID_SET) == 0) && (msg->val[i] != NULL))
634 {
635 r.rgid = atoi(msg->val[i]);
636 r.flags |= ASL_MSG_FLAG_READ_GID_SET;
637 }
638 }
639 else if (!strcmp(msg->key[i], ASL_KEY_MSG_ID))
640 {
641 if (s->flags & ASL_FILE_FLAG_PRESERVE_MSG_ID) *mid = atoll(msg->val[i]);
642 }
643 else
644 {
645 status = asl_file_string_encode(s, msg->key[i], &k);
646 if (status != ASL_STATUS_OK)
647 {
648 if (kvlist != NULL) free(kvlist);
649 return status;
650 }
651
652 v = 0;
653 if (msg->val[i] != NULL)
654 {
655 status = asl_file_string_encode(s, msg->val[i], &v);
656 if (status != ASL_STATUS_OK)
657 {
658 if (kvlist != NULL) free(kvlist);
659 return status;
660 }
661 }
662
663 if (r.kvcount == 0)
664 {
665 kvlist = (uint64_t *)calloc(2, sizeof(uint64_t));
666 }
667 else
668 {
669 kvlist = (uint64_t *)reallocf(kvlist, (r.kvcount + 2) * sizeof(uint64_t));
670 }
671
672 if (kvlist == NULL)
673 {
674 return ASL_STATUS_NO_MEMORY;
675 }
676
677 kvlist[r.kvcount++] = k;
678 kvlist[r.kvcount++] = v;
679 }
680 }
681
682 len = MSG_RECORD_FIXED_LENGTH + (r.kvcount * sizeof(uint64_t));
683 buf = NULL;
684
685 /* use the scratch buffer if it exists and is large enough */
686 if ((s->scratch != NULL) && (len <= SCRATCH_BUFFER_SIZE))
687 {
688 memset(s->scratch, 0, SCRATCH_BUFFER_SIZE);
689 buf = s->scratch;
690 }
691 else
692 {
693 buf = calloc(1, len);
694 }
695
696 if (buf == NULL) return ASL_STATUS_NO_MEMORY;
697
698 if (*mid != 0)
699 {
700 r.mid = *mid;
701 }
702 else
703 {
704 r.mid = asl_core_new_msg_id(0);
705 *mid = r.mid;
706 }
707
708 p = buf;
709
710 /* Type */
711 _asl_put_16(ASL_FILE_TYPE_MSG, p);
712 p += sizeof(uint16_t);
713
714 /* Length of message (excludes type and length fields) */
715 _asl_put_32(len - RECORD_COMMON_LEN, p);
716 p += sizeof(uint32_t);
717
718 /* Message data... */
719
720 _asl_put_64(r.next, p);
721 p += sizeof(uint64_t);
722
723 _asl_put_64(r.mid, p);
724 p += sizeof(uint64_t);
725
726 _asl_put_64(r.time, p);
727 p += sizeof(uint64_t);
728
729 _asl_put_32(r.nano, p);
730 p += sizeof(uint32_t);
731
732 _asl_put_16(r.level, p);
733 p += sizeof(uint16_t);
734
735 _asl_put_16(r.flags, p);
736 p += sizeof(uint16_t);
737
738 _asl_put_32(r.pid, p);
739 p += sizeof(uint32_t);
740
741 _asl_put_32(r.uid, p);
742 p += sizeof(uint32_t);
743
744 _asl_put_32(r.gid, p);
745 p += sizeof(uint32_t);
746
747 _asl_put_32(r.ruid, p);
748 p += sizeof(uint32_t);
749
750 _asl_put_32(r.rgid, p);
751 p += sizeof(uint32_t);
752
753 _asl_put_32(r.refpid, p);
754 p += sizeof(uint32_t);
755
756 _asl_put_32(r.kvcount, p);
757 p += sizeof(uint32_t);
758
759 _asl_put_64(r.host, p);
760 p += sizeof(uint64_t);
761
762 _asl_put_64(r.sender, p);
763 p += sizeof(uint64_t);
764
765 _asl_put_64(r.facility, p);
766 p += sizeof(uint64_t);
767
768 _asl_put_64(r.message, p);
769 p += sizeof(uint64_t);
770
771 _asl_put_64(r.refproc, p);
772 p += sizeof(uint64_t);
773
774 _asl_put_64(r.session, p);
775 p += sizeof(uint64_t);
776
777 for (i = 0; i < r.kvcount; i++)
778 {
779 _asl_put_64(kvlist[i], p);
780 p += sizeof(uint64_t);
781 }
782
783 _asl_put_64(r.prev, p);
784 p += sizeof(uint64_t);
785
786 free(kvlist);
787 kvlist = NULL;
788
789 if (status != 0) return ASL_STATUS_WRITE_FAILED;
790
791 /* write record at end of file */
792 status = fseeko(s->store, 0, SEEK_END);
793 if (status != 0) return ASL_STATUS_WRITE_FAILED;
794
795 s->last = (uint64_t)ftello(s->store);
796 v = asl_core_htonq(s->last);
797
798 status = fwrite(buf, len, 1, s->store);
799 fflush(s->store);
800
801 /* free the buffer if it was allocated here */
802 if (buf != s->scratch) free(buf);
803
804 /* seek to "next" field of previous record, write last offset */
805 off = s->prev + RECORD_COMMON_LEN;
806 if (s->prev == 0) off = DB_HEADER_FIRST_OFFSET;
807
808 status = fseeko(s->store, off, SEEK_SET);
809 if (status != 0) return ASL_STATUS_WRITE_FAILED;
810
811 status = fwrite(&v, sizeof(uint64_t), 1, s->store);
812 if (status != 1) return ASL_STATUS_WRITE_FAILED;
813
814 /* seek to DB_HEADER_LAST_OFFSET, write last record offset */
815 off = DB_HEADER_LAST_OFFSET;
816
817 status = fseeko(s->store, off, SEEK_SET);
818 if (status != 0) return ASL_STATUS_WRITE_FAILED;
819
820 status = fwrite(&v, sizeof(uint64_t), 1, s->store);
821 if (status != 1) return ASL_STATUS_WRITE_FAILED;
822
823 /* return to the end of the store (this is expected by other routines) */
824 status = fseeko(s->store, 0, SEEK_END);
825 if (status != 0) return ASL_STATUS_WRITE_FAILED;
826
827 s->prev = s->last;
828
829 return ASL_STATUS_OK;
830 }
831
832 static uint32_t
833 asl_file_fetch_object(asl_file_t *s, uint64_t where, char **out)
834 {
835 static char ils[9];
836 char *p;
837 uint32_t len;
838 int status;
839 uint64_t x64;
840 uint8_t inls;
841 uint16_t type;
842 off_t off;
843
844 if (s == NULL) return ASL_STATUS_INVALID_STORE;
845 if (out == NULL) return ASL_STATUS_INVALID_ARG;
846 if (where == 0) return ASL_STATUS_INVALID_ARG;
847
848 *out = NULL;
849
850 inls = 0;
851 x64 = asl_core_htonq(where);
852 memcpy(&inls, &x64, 1);
853 if (inls & 0x80)
854 {
855 /* inline string */
856 memset(ils, 0, sizeof(ils));
857 inls &= 0x0f;
858 p = 1 + (char *)&x64;
859 memcpy(ils, p, inls);
860 *out = strdup(ils);
861
862 if (*out == NULL) return ASL_STATUS_NO_MEMORY;
863 return ASL_STATUS_OK;
864 }
865
866 off = where;
867 status = fseeko(s->store, off, SEEK_SET);
868 if (status != 0) return ASL_STATUS_READ_FAILED;
869
870 /* Type */
871 status = fread(&type, sizeof(uint16_t), 1, s->store);
872 if (status != 1) return ASL_STATUS_READ_FAILED;
873
874 /* Length */
875 len = 0;
876 status = fread(&len, sizeof(uint32_t), 1, s->store);
877 if (status != 1) return ASL_STATUS_READ_FAILED;
878 len = ntohl(len);
879
880 *out = calloc(1, len);
881 if (*out == NULL) return ASL_STATUS_NO_MEMORY;
882
883 status = fread(*out, len, 1, s->store);
884 if (status != 1)
885 {
886 free(*out);
887 return ASL_STATUS_READ_FAILED;
888 }
889
890 return ASL_STATUS_OK;
891 }
892
893 static uint16_t
894 asl_file_fetch_helper_16(asl_file_t *s, char **p, aslmsg m, const char *key)
895 {
896 uint16_t out;
897 char str[256];
898
899 out = _asl_get_16(*p);
900 *p += sizeof(uint16_t);
901
902 if ((m == NULL) || (key == NULL)) return out;
903
904 snprintf(str, sizeof(str), "%hu", out);
905 asl_set(m, key, str);
906
907 return out;
908 }
909
910 static uint32_t
911 asl_file_fetch_helper_32(asl_file_t *s, char **p, aslmsg m, const char *key, int ignore, uint32_t ignoreval)
912 {
913 uint32_t out, doit;
914 char str[256];
915
916 out = _asl_get_32(*p);
917 *p += sizeof(uint32_t);
918
919 if ((m == NULL) || (key == NULL)) return out;
920
921 doit = 1;
922 if ((ignore != 0) && (out == ignoreval)) doit = 0;
923 if (doit != 0)
924 {
925 snprintf(str, sizeof(str), "%u", out);
926 asl_set(m, key, str);
927 }
928
929 return out;
930 }
931
932 static uint64_t
933 asl_file_fetch_helper_64(asl_file_t *s, char **p, aslmsg m, const char *key)
934 {
935 uint64_t out;
936 char str[256];
937
938 out = _asl_get_64(*p);
939 *p += sizeof(uint64_t);
940
941 if ((m == NULL) || (key == NULL)) return out;
942
943 snprintf(str, sizeof(str), "%llu", out);
944 asl_set(m, key, str);
945
946 return out;
947 }
948
949 static uint64_t
950 asl_file_fetch_helper_str(asl_file_t *s, char **p, aslmsg m, const char *key)
951 {
952 uint64_t out;
953 char *val;
954 uint32_t status;
955
956 out = _asl_get_64(*p);
957 *p += sizeof(uint64_t);
958
959 val = NULL;
960 status = asl_file_fetch_object(s, out, &val);
961 if ((status == ASL_STATUS_OK) && (val != NULL))
962 {
963 asl_set(m, key, val);
964 free(val);
965 }
966
967 return out;
968 }
969
970 static uint32_t
971 asl_file_fetch_pos(asl_file_t *s, uint64_t where, int dir, aslmsg *msg)
972 {
973 char *buf, *p, *k, *v;
974 file_record_t r;
975 uint32_t i, status;
976 uint64_t x64, kv;
977 aslmsg out;
978 off_t off;
979
980 if (s == NULL) return ASL_STATUS_INVALID_STORE;
981 if (msg == NULL) return ASL_STATUS_INVALID_ARG;
982 if ((s->flags & ASL_FILE_FLAG_READ_ONLY) == 0) return ASL_STATUS_WRITE_ONLY;
983
984 buf = NULL;
985 status = asl_file_fetch_object(s, where, &buf);
986 if (buf == NULL) return status;
987
988 out = asl_new(ASL_TYPE_MSG);
989 if (out == NULL) return ASL_STATUS_NO_MEMORY;
990
991 memset(&r, 0, sizeof(file_record_t));
992 p = buf;
993
994 r.next = asl_file_fetch_helper_64(s, &p, NULL, NULL);
995 r.mid = asl_file_fetch_helper_64(s, &p, out, ASL_KEY_MSG_ID);
996 r.time = asl_file_fetch_helper_64(s, &p, out, ASL_KEY_TIME);
997 r.nano = asl_file_fetch_helper_32(s, &p, out, ASL_KEY_TIME_NSEC, 0, 0);
998 r.level = asl_file_fetch_helper_16(s, &p, out, ASL_KEY_LEVEL);
999 r.flags = asl_file_fetch_helper_16(s, &p, NULL, NULL);
1000 r.pid = asl_file_fetch_helper_32(s, &p, out, ASL_KEY_PID, 0, 0);
1001 r.uid = asl_file_fetch_helper_32(s, &p, out, ASL_KEY_UID, 1, (uint32_t)-1);
1002 r.gid = asl_file_fetch_helper_32(s, &p, out, ASL_KEY_GID, 1, (uint32_t)-1);
1003 r.ruid = asl_file_fetch_helper_32(s, &p, out, ASL_KEY_READ_UID, 1, (uint32_t)-1);
1004 r.rgid = asl_file_fetch_helper_32(s, &p, out, ASL_KEY_READ_GID, 1, (uint32_t)-1);
1005 r.refpid = asl_file_fetch_helper_32(s, &p, out, ASL_KEY_REF_PID, 1, 0);
1006 r.kvcount = asl_file_fetch_helper_32(s, &p, NULL, NULL, 0, 0);
1007 r.host = asl_file_fetch_helper_str(s, &p, out, ASL_KEY_HOST);
1008 r.sender = asl_file_fetch_helper_str(s, &p, out, ASL_KEY_SENDER);
1009 r.facility = asl_file_fetch_helper_str(s, &p, out, ASL_KEY_FACILITY);
1010 r.message = asl_file_fetch_helper_str(s, &p, out, ASL_KEY_MSG);
1011 r.refproc = asl_file_fetch_helper_str(s, &p, out, ASL_KEY_REF_PROC);
1012 r.session = asl_file_fetch_helper_str(s, &p, out, ASL_KEY_SESSION);
1013
1014 for (i = 0; i < r.kvcount / 2; i++)
1015 {
1016 kv = _asl_get_64(p);
1017 p += sizeof(uint64_t);
1018 k = NULL;
1019 status = asl_file_fetch_object(s, kv, &k);
1020
1021 kv = _asl_get_64(p);
1022 p += sizeof(uint64_t);
1023 v = NULL;
1024 status = asl_file_fetch_object(s, kv, &v);
1025
1026 if ((status == ASL_STATUS_OK) && (k != NULL))
1027 {
1028 asl_set(out, k, v);
1029 if (v != NULL) free(v);
1030 free(k);
1031 }
1032 }
1033
1034 r.prev = asl_file_fetch_helper_64(s, &p, NULL, NULL);
1035
1036 free(buf);
1037
1038 if (dir >= 0) s->cursor = r.next;
1039 else s->cursor = r.prev;
1040
1041 s->cursor_xid = 0;
1042
1043 if (s->cursor != 0)
1044 {
1045 off = s->cursor + RECORD_COMMON_LEN + sizeof(uint64_t);
1046 status = fseeko(s->store, off, SEEK_SET);
1047 if (status != 0) return ASL_STATUS_READ_FAILED;
1048
1049 status = fread(&x64, sizeof(uint64_t), 1, s->store);
1050 if (status != 1) return ASL_STATUS_READ_FAILED;
1051
1052 s->cursor_xid = asl_core_ntohq(x64);
1053 }
1054
1055 *msg = out;
1056 return ASL_STATUS_OK;
1057 }
1058
1059 uint32_t
1060 asl_file_open_read(const char *path, asl_file_t **s)
1061 {
1062 asl_file_t *out;
1063 FILE *f;
1064 int i;
1065 uint32_t status, vers;
1066 char buf[DB_HEADER_LEN];
1067 off_t off;
1068 asl_legacy1_t *legacy;
1069
1070 f = fopen(path, "r");
1071 if (f == NULL)
1072 {
1073 if (errno == EACCES) return ASL_STATUS_ACCESS_DENIED;
1074 return ASL_STATUS_FAILED;
1075 }
1076
1077 i = fread(buf, DB_HEADER_LEN, 1, f);
1078 if (i < 1)
1079 {
1080 fclose(f);
1081 return ASL_STATUS_INVALID_STORE;
1082 }
1083
1084 /* validate header */
1085 if (strncmp(buf, ASL_DB_COOKIE, ASL_DB_COOKIE_LEN))
1086 {
1087 fclose(f);
1088 return ASL_STATUS_INVALID_STORE;
1089 }
1090
1091 legacy = NULL;
1092
1093 vers = _asl_get_32(buf + DB_HEADER_VERS_OFFSET);
1094 if (vers == DB_VERSION_LEGACY_1)
1095 {
1096 fclose(f);
1097 status = asl_legacy1_open(path, &legacy);
1098 if (status != ASL_STATUS_OK) return status;
1099 }
1100
1101 out = (asl_file_t *)calloc(1, sizeof(asl_file_t));
1102 if (out == NULL)
1103 {
1104 fclose(f);
1105 return ASL_STATUS_NO_MEMORY;
1106 }
1107
1108 out->flags = ASL_FILE_FLAG_READ_ONLY;
1109 out->version = vers;
1110
1111 if (legacy != NULL)
1112 {
1113 out->flags |= ASL_FILE_FLAG_LEGACY_STORE;
1114 out->legacy = (void *)legacy;
1115
1116 *s = out;
1117 return ASL_STATUS_OK;
1118 }
1119
1120 out->first = _asl_get_64(buf + DB_HEADER_FIRST_OFFSET);
1121 out->last = _asl_get_64(buf + DB_HEADER_LAST_OFFSET);
1122
1123 out->store = f;
1124
1125 out->cursor = out->first;
1126 if (out->cursor != 0)
1127 {
1128 off = out->cursor + RECORD_COMMON_LEN + sizeof(uint64_t);
1129 status = asl_file_read_uint64(out, off, &(out->cursor_xid));
1130 if (status != ASL_STATUS_OK)
1131 {
1132 fclose(f);
1133 return status;
1134 }
1135 }
1136
1137 *s = out;
1138 return ASL_STATUS_OK;
1139 }
1140
1141 static uint32_t
1142 asl_file_read_set_position_first(asl_file_t *s)
1143 {
1144 uint32_t status;
1145 off_t off;
1146
1147 s->cursor = s->first;
1148 s->cursor_xid = 0;
1149
1150 if (s->cursor == 0) return ASL_STATUS_OK;
1151
1152 /* read ID of the first record */
1153 off = s->cursor + RECORD_COMMON_LEN + sizeof(uint64_t);
1154 status = asl_file_read_uint64(s, off, &(s->cursor_xid));
1155 return status;
1156 }
1157
1158 static uint32_t
1159 asl_file_read_set_position_last(asl_file_t *s)
1160 {
1161 uint64_t next;
1162 uint32_t status;
1163 off_t off;
1164
1165 /*
1166 * If the file has the offset of the last record, we just go there.
1167 * The last record offset was added to improve performance, so it may
1168 * or may not be there. If we don't have the last record offset, we
1169 * just iterate down the record links to find the last one.
1170 *
1171 * Note that s->last may be zero if the file is empty.
1172 */
1173
1174 if (s->last != 0)
1175 {
1176 s->cursor = s->last;
1177 off = s->last + RECORD_COMMON_LEN + sizeof(uint64_t);
1178
1179 /* read ID of the last record */
1180 status = asl_file_read_uint64(s, off, &(s->cursor_xid));
1181 return status;
1182 }
1183
1184 /* start at the first record and iterate */
1185 s->cursor = s->first;
1186 s->cursor_xid = 0;
1187
1188 forever
1189 {
1190 off = s->cursor + RECORD_COMMON_LEN;
1191 next = 0;
1192
1193 /* read next offset */
1194 status = asl_file_read_uint64(s, off, &next);
1195 if (status != ASL_STATUS_OK) return status;
1196
1197 if (next == 0)
1198 {
1199 if (s->cursor == 0) return ASL_STATUS_OK;
1200
1201 off = s->cursor + RECORD_COMMON_LEN + sizeof(uint64_t);
1202 status = asl_file_read_uint64(s, off, &(s->cursor_xid));
1203 return ASL_STATUS_OK;
1204 }
1205
1206 s->cursor = next;
1207 }
1208 }
1209
1210 uint32_t
1211 asl_file_read_set_position(asl_file_t *s, uint32_t pos)
1212 {
1213 uint32_t len, status;
1214 off_t off;
1215
1216 if (s == NULL) return ASL_STATUS_INVALID_STORE;
1217 if (s->version == 1) return ASL_STATUS_FAILED;
1218
1219 if (pos == ASL_FILE_POSITION_FIRST) return asl_file_read_set_position_first(s);
1220 if (pos == ASL_FILE_POSITION_LAST) return asl_file_read_set_position_last(s);
1221
1222 off = 0;
1223
1224 if (pos == ASL_FILE_POSITION_PREVIOUS)
1225 {
1226 if (s->cursor == s->first) return ASL_STATUS_NO_RECORDS;
1227 if (s->cursor == 0) return ASL_STATUS_NO_RECORDS;
1228
1229 off = s->cursor + RECORD_TYPE_LEN;
1230 status = asl_file_read_uint32(s, off, &len);
1231 if (status != ASL_STATUS_OK) return status;
1232
1233 /* set offset to read the "previous" field at the end of the record */
1234 off = s->cursor + RECORD_COMMON_LEN + len - sizeof(uint64_t);
1235 }
1236 else if (pos == ASL_FILE_POSITION_NEXT)
1237 {
1238 if (s->cursor == s->last) return ASL_STATUS_NO_RECORDS;
1239 if (s->cursor == 0) return ASL_STATUS_NO_RECORDS;
1240
1241 off = s->cursor + RECORD_COMMON_LEN;
1242 }
1243 else return ASL_STATUS_INVALID_ARG;
1244
1245 s->cursor_xid = 0;
1246
1247 /*
1248 * read offset of next / previous
1249 */
1250 status = asl_file_read_uint64(s, off, &(s->cursor));
1251 if (status != ASL_STATUS_OK) return ASL_STATUS_READ_FAILED;
1252
1253 if (s->cursor == 0) return ASL_STATUS_NO_RECORDS;
1254
1255 /* read ID of the record */
1256 off = s->cursor + RECORD_COMMON_LEN + sizeof(uint64_t);
1257 status = asl_file_read_uint64(s, off, &(s->cursor_xid));
1258 return status;
1259 }
1260
1261 uint32_t
1262 asl_file_fetch_next(asl_file_t *s, aslmsg *msg)
1263 {
1264 if (s == NULL) return ASL_STATUS_INVALID_STORE;
1265 if (s->version == 1) return ASL_STATUS_FAILED;
1266
1267 return asl_file_fetch_pos(s, s->cursor, 1, msg);
1268 }
1269
1270 uint32_t
1271 asl_file_fetch_previous(asl_file_t *s, aslmsg *msg)
1272 {
1273 if (s == NULL) return ASL_STATUS_INVALID_STORE;
1274 if (s->version == 1) return ASL_STATUS_FAILED;
1275
1276 return asl_file_fetch_pos(s, s->cursor, -1, msg);
1277 }
1278
1279 uint32_t
1280 asl_file_fetch(asl_file_t *s, uint64_t mid, aslmsg *msg)
1281 {
1282 uint32_t status;
1283
1284 if (s == NULL) return ASL_STATUS_INVALID_STORE;
1285 if (msg == NULL) return ASL_STATUS_INVALID_ARG;
1286 if ((s->flags & ASL_FILE_FLAG_READ_ONLY) == 0) return ASL_STATUS_WRITE_ONLY;
1287
1288 if (s->version == 1)
1289 {
1290 return asl_legacy1_fetch((asl_legacy1_t *)s->legacy, mid, msg);
1291 }
1292
1293 if (s->cursor_xid == 0)
1294 {
1295 status = asl_file_read_set_position(s, ASL_FILE_POSITION_FIRST);
1296 if (status != ASL_STATUS_OK) return status;
1297 if (s->cursor_xid == 0) return ASL_STATUS_INVALID_ID;
1298 }
1299
1300 while (s->cursor_xid < mid)
1301 {
1302 status = asl_file_read_set_position(s, ASL_FILE_POSITION_NEXT);
1303 if (status != ASL_STATUS_OK) return status;
1304 if (s->cursor_xid > mid) return ASL_STATUS_INVALID_ID;
1305 if (s->cursor_xid == 0) return ASL_STATUS_INVALID_ID;
1306 }
1307
1308 while (s->cursor_xid > mid)
1309 {
1310 status = asl_file_read_set_position(s, ASL_FILE_POSITION_PREVIOUS);
1311 if (status != ASL_STATUS_OK) return status;
1312 if (s->cursor_xid < mid) return ASL_STATUS_INVALID_ID;
1313 if (s->cursor_xid == 0) return ASL_STATUS_INVALID_ID;
1314 }
1315
1316 if (s->cursor_xid != mid) return ASL_STATUS_INVALID_ID;
1317
1318 return asl_file_fetch_pos(s, s->cursor, 1, msg);
1319 }
1320
1321 uint64_t
1322 asl_file_cursor(asl_file_t *s)
1323 {
1324 if (s == NULL) return 0;
1325 if ((s->flags & ASL_FILE_FLAG_READ_ONLY) == 0) return 0;
1326 if (s->version == 1) return 0;
1327
1328 return s->cursor_xid;
1329 }
1330
1331 uint32_t
1332 asl_file_match_start(asl_file_t *s, uint64_t start_id, int32_t direction)
1333 {
1334 uint32_t status, d;
1335
1336 if (s == NULL) return ASL_STATUS_INVALID_STORE;
1337 if (s->version == 1) return ASL_STATUS_INVALID_STORE;
1338 if ((s->flags & ASL_FILE_FLAG_READ_ONLY) == 0) return ASL_STATUS_WRITE_ONLY;
1339
1340 d = ASL_FILE_POSITION_NEXT;
1341 if (direction < 0) d = ASL_FILE_POSITION_PREVIOUS;
1342
1343 /*
1344 * find starting point
1345 */
1346 status = ASL_STATUS_OK;
1347 if (direction >= 0) status = asl_file_read_set_position(s, ASL_FILE_POSITION_FIRST);
1348 else status = asl_file_read_set_position(s, ASL_FILE_POSITION_LAST);
1349 if (status != ASL_STATUS_OK) return status;
1350
1351 while ((status == ASL_STATUS_OK) && (((direction >= 0) && (s->cursor_xid < start_id)) || ((direction < 0) && (s->cursor_xid > start_id))))
1352 {
1353 status = asl_file_read_set_position(s, d);
1354 }
1355
1356 return status;
1357 }
1358
1359 uint32_t
1360 asl_file_match_next(asl_file_t *s, aslresponse query, asl_msg_t **msg, uint64_t *last_id, int32_t direction)
1361 {
1362 uint32_t status, d, i, do_match, did_match;
1363 aslmsg m;
1364
1365 if (s == NULL) return ASL_STATUS_INVALID_STORE;
1366 if (msg == NULL) return ASL_STATUS_INVALID_ARG;
1367 if (s->version == 1) return ASL_STATUS_INVALID_STORE;
1368 if ((s->flags & ASL_FILE_FLAG_READ_ONLY) == 0) return ASL_STATUS_WRITE_ONLY;
1369 if (s->cursor == 0) return ASL_STATUS_NO_RECORDS;
1370
1371 *msg = NULL;
1372 do_match = 1;
1373
1374 d = ASL_FILE_POSITION_NEXT;
1375 if (direction < 0) d = ASL_FILE_POSITION_PREVIOUS;
1376
1377 if ((query == NULL) || ((query != NULL) && (query->count == 0))) do_match = 0;
1378
1379 m = NULL;
1380
1381 *last_id = s->cursor_xid;
1382
1383 status = asl_file_fetch_pos(s, s->cursor, direction, &m);
1384 if (status == ASL_STATUS_ACCESS_DENIED) return ASL_STATUS_MATCH_FAILED;
1385 if ((status == ASL_STATUS_INVALID_ARG) && (s->cursor == 0)) return ASL_STATUS_NO_RECORDS;
1386 if (status != ASL_STATUS_OK) return status;
1387
1388 did_match = 1;
1389
1390 if (do_match != 0)
1391 {
1392 did_match = 0;
1393
1394 for (i = 0; (i < query->count) && (did_match == 0); i++)
1395 {
1396 did_match = asl_msg_cmp(query->msg[i], m);
1397 }
1398 }
1399
1400 if (did_match != 0)
1401 {
1402 *msg = m;
1403 return ASL_STATUS_OK;
1404 }
1405
1406 asl_free(m);
1407 return ASL_STATUS_MATCH_FAILED;
1408 }
1409
1410 uint32_t
1411 asl_file_match(asl_file_t *s, aslresponse query, aslresponse *res, uint64_t *last_id, uint64_t start_id, uint32_t count, int32_t direction)
1412 {
1413 uint32_t status, d, i, do_match, did_match, rescount;
1414 asl_msg_t *m;
1415
1416 if (s == NULL) return ASL_STATUS_INVALID_STORE;
1417 if (res == NULL) return ASL_STATUS_INVALID_ARG;
1418 if ((s->flags & ASL_FILE_FLAG_READ_ONLY) == 0) return ASL_STATUS_WRITE_ONLY;
1419
1420 if (s->version == 1)
1421 {
1422 return asl_legacy1_match((asl_legacy1_t *)s->legacy, query, res, last_id, start_id, count, direction);
1423 }
1424
1425 do_match = 1;
1426 rescount = 0;
1427
1428 d = ASL_FILE_POSITION_NEXT;
1429 if (direction < 0) d = ASL_FILE_POSITION_PREVIOUS;
1430
1431 if ((query == NULL) || ((query != NULL) && (query->count == 0))) do_match = 0;
1432
1433 /*
1434 * find starting point
1435 */
1436 status = ASL_STATUS_OK;
1437 if (direction >= 0) status = asl_file_read_set_position(s, ASL_FILE_POSITION_FIRST);
1438 else status = asl_file_read_set_position(s, ASL_FILE_POSITION_LAST);
1439 if (status != ASL_STATUS_OK) return status;
1440
1441 while ((status == ASL_STATUS_OK) && (((direction >= 0) && (s->cursor_xid < start_id)) || ((direction < 0) && (s->cursor_xid > start_id))))
1442 {
1443 status = asl_file_read_set_position(s, d);
1444 }
1445
1446 /*
1447 * loop through records
1448 */
1449 forever
1450 {
1451 m = NULL;
1452 status = asl_file_fetch_pos(s, s->cursor, direction, &m);
1453 if (status == ASL_STATUS_ACCESS_DENIED) continue;
1454 if (status != ASL_STATUS_OK) break;
1455
1456 *last_id = s->cursor_xid;
1457
1458 did_match = 1;
1459
1460 if (do_match != 0)
1461 {
1462 did_match = 0;
1463
1464 for (i = 0; (i < query->count) && (did_match == 0); i++)
1465 {
1466 did_match = asl_msg_cmp(query->msg[i], m);
1467 }
1468 }
1469
1470 if (did_match == 1)
1471 {
1472 /* append m to res */
1473 if (*res == NULL)
1474 {
1475 *res = (aslresponse)calloc(1, sizeof(aslresponse));
1476 if (*res == NULL) return ASL_STATUS_NO_MEMORY;
1477 (*res)->msg = (asl_msg_t **)calloc(1, sizeof(asl_msg_t *));
1478 if ((*res)->msg == NULL)
1479 {
1480 free(*res);
1481 return ASL_STATUS_NO_MEMORY;
1482 }
1483 }
1484 else
1485 {
1486 (*res)->msg = (asl_msg_t **)realloc((*res)->msg, ((*res)->count + 1) * sizeof(asl_msg_t *));
1487 if ((*res)->msg == NULL)
1488 {
1489 free(*res);
1490 return ASL_STATUS_NO_MEMORY;
1491 }
1492 }
1493
1494 (*res)->msg[(*res)->count] = m;
1495 (*res)->count++;
1496
1497 rescount++;
1498 if ((count != 0) && (rescount >= count)) break;
1499 }
1500 else
1501 {
1502 asl_free(m);
1503 }
1504 }
1505
1506 /* NOT REACHED */
1507 return ASL_STATUS_OK;
1508 }
1509
1510 size_t
1511 asl_file_size(asl_file_t *s)
1512 {
1513 if (s == NULL) return 0;
1514 return s->file_size;
1515 }
1516
1517 uint64_t
1518 asl_file_ctime(asl_file_t *s)
1519 {
1520 if (s == NULL) return 0;
1521 return s->dob;
1522 }
1523
1524 void
1525 asl_file_list_close(asl_file_list_t *head)
1526 {
1527 asl_file_list_t *next;
1528
1529 while (head != NULL)
1530 {
1531 next = head->next;
1532 asl_file_close(head->file);
1533 free(head);
1534 head = next;
1535 }
1536 }
1537
1538 static void
1539 asl_file_list_free(asl_file_list_t *head)
1540 {
1541 asl_file_list_t *next;
1542
1543 while (head != NULL)
1544 {
1545 next = head->next;
1546 free(head);
1547 head = next;
1548 }
1549 }
1550
1551 asl_file_list_t *
1552 asl_file_list_insert(asl_file_list_t *list, asl_file_t *f, int32_t dir)
1553 {
1554 asl_file_list_t *a, *b, *tmp;
1555
1556 if (f == NULL) return list;
1557
1558 tmp = (asl_file_list_t *)calloc(1, sizeof(asl_file_list_t));
1559 if (tmp == NULL) return NULL;
1560 tmp->file = f;
1561
1562 if (list == NULL) return tmp;
1563
1564 a = list;
1565 if (((dir < 0) && (f->cursor_xid > a->file->cursor_xid)) || ((dir >= 0) && (f->cursor_xid < a->file->cursor_xid)))
1566 {
1567 tmp->next = list;
1568 return tmp;
1569 }
1570
1571 b = a->next;
1572 while (b != NULL)
1573 {
1574 if (((dir < 0) && (f->cursor_xid > b->file->cursor_xid)) || ((dir >= 0) && (f->cursor_xid < b->file->cursor_xid)))
1575 {
1576 tmp->next = b;
1577 a->next = tmp;
1578 return list;
1579 }
1580
1581 a = b;
1582 b = a->next;
1583 }
1584
1585 a->next = tmp;
1586 return list;
1587 }
1588
1589 asl_file_list_t *
1590 asl_file_list_add(asl_file_list_t *list, asl_file_t *f)
1591 {
1592 asl_file_list_t *tmp;
1593
1594 if (f == NULL) return list;
1595 if (f->version == 1) return list;
1596
1597 tmp = (asl_file_list_t *)calloc(1, sizeof(asl_file_list_t));
1598 if (tmp == NULL) return NULL;
1599 tmp->file = f;
1600
1601 tmp->next = list;
1602 return tmp;
1603 }
1604
1605 void *
1606 asl_file_list_match_start(asl_file_list_t *list, uint64_t start_id, int32_t direction)
1607 {
1608 uint32_t status;
1609 asl_file_list_t *n;
1610 asl_file_match_token_t *out;
1611
1612 if (list == NULL) return NULL;
1613
1614 out = (asl_file_match_token_t *)calloc(1, sizeof(asl_file_match_token_t));
1615 if (out == NULL) return NULL;
1616
1617 for (n = list; n != NULL; n = n->next)
1618 {
1619 /* init file for the search */
1620 status = asl_file_match_start(n->file, start_id, direction);
1621 if (status != ASL_STATUS_OK) continue;
1622 if (n->file->cursor_xid == 0) continue;
1623
1624 out->list = asl_file_list_insert(out->list, n->file, direction);
1625 }
1626
1627 out->dir = direction;
1628 return out;
1629 }
1630
1631 uint32_t
1632 asl_file_list_match_next(void *token, aslresponse query, aslresponse *res, uint32_t count)
1633 {
1634 uint32_t status, rescount;
1635 asl_file_list_t *n;
1636 asl_msg_t *m;
1637 asl_file_match_token_t *work;
1638 uint64_t last_id;
1639
1640 if (token == NULL) return ASL_STATUS_OK;
1641 if (res == NULL) return ASL_STATUS_INVALID_ARG;
1642
1643 work = (asl_file_match_token_t *)token;
1644
1645 rescount = 0;
1646 last_id = 0;
1647
1648 while ((work->list != NULL) && ((rescount < count) || (count == 0)))
1649 {
1650 m = NULL;
1651 status = asl_file_match_next(work->list->file, query, &m, &last_id, work->dir);
1652 if (m != NULL)
1653 {
1654 if (*res == NULL) *res = (aslresponse)calloc(1, sizeof(asl_search_result_t));
1655 if (*res == NULL)
1656 {
1657 asl_file_list_free(work->list);
1658 work->list = NULL;
1659 return ASL_STATUS_NO_MEMORY;
1660 }
1661
1662 if ((*res)->msg == NULL) (*res)->msg = (asl_msg_t **)calloc(1, sizeof(asl_msg_t *));
1663 else (*res)->msg = (asl_msg_t **)reallocf((*res)->msg, ((*res)->count + 1) * sizeof(asl_msg_t *));
1664 if ((*res)->msg == NULL)
1665 {
1666 free(*res);
1667 *res = NULL;
1668 asl_file_list_free(work->list);
1669 work->list = NULL;
1670 return ASL_STATUS_NO_MEMORY;
1671 }
1672
1673 (*res)->msg[(*res)->count] = m;
1674 (*res)->count++;
1675 rescount++;
1676 }
1677
1678 if (work->list->file->cursor_xid == 0)
1679 {
1680 n = work->list->next;
1681 free(work->list);
1682 work->list = n;
1683 }
1684
1685 if (work->list != NULL)
1686 {
1687 n = work->list->next;
1688 if (n != NULL)
1689 {
1690 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)))
1691 {
1692 n = work->list;
1693 work->list = work->list->next;
1694 n->next = NULL;
1695 work->list = asl_file_list_insert(work->list, n->file, work->dir);
1696 free(n);
1697 }
1698 }
1699 }
1700 }
1701
1702 return ASL_STATUS_OK;
1703 }
1704
1705 void
1706 asl_file_list_match_end(void *token)
1707 {
1708 asl_file_match_token_t *work;
1709
1710 if (token == NULL) return;
1711
1712 work = (asl_file_match_token_t *)token;
1713 asl_file_list_free(work->list);
1714 work->list = NULL;
1715
1716 free(token);
1717 }
1718
1719 uint32_t
1720 asl_file_list_match_timeout(asl_file_list_t *list, aslresponse query, aslresponse *res, uint64_t *last_id, uint64_t start_id, uint32_t count, int32_t direction, uint32_t usec)
1721 {
1722 uint32_t status, rescount;
1723 asl_file_list_t *files, *n;
1724 asl_msg_t *m;
1725 struct timeval now, finish;
1726
1727 if (list == NULL) return ASL_STATUS_INVALID_ARG;
1728 if (res == NULL) return ASL_STATUS_INVALID_ARG;
1729 if (last_id == NULL) return ASL_STATUS_INVALID_ARG;
1730
1731 files = NULL;
1732
1733 for (n = list; n != NULL; n = n->next)
1734 {
1735 /* init file for the search */
1736 status = asl_file_match_start(n->file, start_id, direction);
1737 if (status != ASL_STATUS_OK) continue;
1738 if (n->file->cursor_xid == 0) continue;
1739
1740 files = asl_file_list_insert(files, n->file, direction);
1741 }
1742
1743 if (files == NULL)
1744 {
1745 asl_file_list_free(files);
1746 return ASL_STATUS_OK;
1747 }
1748
1749 /* start the timer if a timeout was specified */
1750 memset(&finish, 0, sizeof(struct timeval));
1751 if (usec != 0)
1752 {
1753 if (gettimeofday(&finish, NULL) == 0)
1754 {
1755 finish.tv_sec += (usec / MILLION);
1756 finish.tv_usec += (usec % MILLION);
1757 if (finish.tv_usec > MILLION)
1758 {
1759 finish.tv_usec -= MILLION;
1760 finish.tv_sec += 1;
1761 }
1762 }
1763 else
1764 {
1765 /* shouldn't happen, but if gettimeofday failed we just run without a timeout */
1766 memset(&finish, 0, sizeof(struct timeval));
1767 }
1768 }
1769
1770 rescount = 0;
1771 while ((files != NULL) && ((rescount < count) || (count == 0)))
1772 {
1773 m = NULL;
1774 status = asl_file_match_next(files->file, query, &m, last_id, direction);
1775 if (m != NULL)
1776 {
1777 if (*res == NULL) *res = (aslresponse)calloc(1, sizeof(asl_search_result_t));
1778 if (*res == NULL)
1779 {
1780 asl_file_list_free(files);
1781 return ASL_STATUS_NO_MEMORY;
1782 }
1783
1784 if ((*res)->msg == NULL) (*res)->msg = (asl_msg_t **)calloc(1, sizeof(asl_msg_t *));
1785 else (*res)->msg = (asl_msg_t **)reallocf((*res)->msg, ((*res)->count + 1) * sizeof(asl_msg_t *));
1786 if ((*res)->msg == NULL)
1787 {
1788 free(*res);
1789 *res = NULL;
1790 asl_file_list_free(files);
1791 return ASL_STATUS_NO_MEMORY;
1792 }
1793
1794 (*res)->msg[(*res)->count] = m;
1795 (*res)->count++;
1796 rescount++;
1797 }
1798
1799 if (files->file->cursor_xid == 0)
1800 {
1801 n = files->next;
1802 free(files);
1803 files = n;
1804 }
1805
1806 if (files != NULL)
1807 {
1808 n = files->next;
1809 if (n != NULL)
1810 {
1811 if (((direction < 0) && (files->file->cursor_xid <= n->file->cursor_xid)) || ((direction >= 0) && (files->file->cursor_xid > n->file->cursor_xid)))
1812 {
1813 n = files;
1814 files = files->next;
1815 n->next = NULL;
1816 files = asl_file_list_insert(files, n->file, direction);
1817 free(n);
1818 }
1819 }
1820 }
1821
1822 /* check the timer */
1823 if ((finish.tv_sec != 0) && (gettimeofday(&now, NULL) == 0))
1824 {
1825 if ((now.tv_sec > finish.tv_sec) || ((now.tv_sec == finish.tv_sec) && (now.tv_usec > finish.tv_usec))) break;
1826 }
1827 }
1828
1829 asl_file_list_free(files);
1830 return ASL_STATUS_OK;
1831 }
1832
1833 uint32_t
1834 asl_file_list_match(asl_file_list_t *list, aslresponse query, aslresponse *res, uint64_t *last_id, uint64_t start_id, uint32_t count, int32_t direction)
1835 {
1836 return asl_file_list_match_timeout(list, query, res, last_id, start_id, count, direction, 0);
1837 }