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