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