1/* 2 * Copyright (c) 2007-2011 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 <stdlib.h> 25#include <unistd.h> 26#include <string.h> 27#include <errno.h> 28#include <dirent.h> 29#include <sys/types.h> 30#include <sys/stat.h> 31#include <fcntl.h> 32#include <asl.h> 33#include <asl_private.h> 34#include <asl_core.h> 35#include <asl_store.h> 36#include <notify.h> 37 38extern uint64_t asl_file_cursor(asl_file_t *s); 39extern uint32_t asl_file_match_start(asl_file_t *s, uint64_t start_id, int32_t direction); 40extern uint32_t asl_file_match_next(asl_file_t *s, asl_msg_list_t *qlist, asl_msg_t **msg, uint64_t *last_id, int32_t direction, int32_t ruid, int32_t rgid); 41extern int asl_file_create(const char *path, uid_t uid, gid_t gid, mode_t mode); 42 43#define SECONDS_PER_DAY 86400 44 45/* 46 * The ASL Store is organized as a set of files in a common directory. 47 * Files are prefixed by the date (YYYY.MM.DD) of their contents. 48 * 49 * Messages with no access controls are saved in YYYY.MM.DD.asl 50 * Messages with access limited to UID uuu are saved in YYYY.MM.DD.Uuuu.asl 51 * Messages with access limited to GID ggg are saved in YYYY.MM.DD.Gggg.asl 52 * Messages with access limited to UID uuu and GID ggg are saved in YYYY.MM.DD.Uuuu.Gggg.asl 53 * 54 * Messages that have a value for ASLExpireTime are saved in BB.YYYY.MM.DD.asl 55 * where the timestamp is the "Best Before" date of the file. Access controls 56 * are implemented as above with Uuuu and Gggg in the file name. Note that the 57 * Best Before files are for the last day of the month, so a single file contains 58 * messages that expire in that month. 59 * 60 * An external tool runs daily and deletes "old" files. 61 */ 62 63static time_t 64_asl_start_today() 65{ 66 time_t now; 67 struct tm ctm; 68 69 memset(&ctm, 0, sizeof(struct tm)); 70 now = time(NULL); 71 72 if (localtime_r((const time_t *)&now, &ctm) == NULL) return 0; 73 74 ctm.tm_sec = 0; 75 ctm.tm_min = 0; 76 ctm.tm_hour = 0; 77 78 return mktime(&ctm); 79} 80 81/* 82 * The base directory contains a data file which stores 83 * the last record ID. 84 * 85 * | MAX_ID (uint64_t) | 86 * 87 */ 88ASL_STATUS 89asl_store_open_write(const char *basedir, asl_store_t **s) 90{ 91 asl_store_t *out; 92 struct stat sb; 93 uint32_t i, flags; 94 char *path; 95 FILE *sd; 96 uint64_t last_id; 97 time_t start; 98 99 if (s == NULL) return ASL_STATUS_INVALID_ARG; 100 101 start = _asl_start_today(); 102 if (start == 0) return ASL_STATUS_FAILED; 103 104 if (basedir == NULL) basedir = PATH_ASL_STORE; 105 106 memset(&sb, 0, sizeof(struct stat)); 107 if (stat(basedir, &sb) != 0) return ASL_STATUS_INVALID_STORE; 108 if (!S_ISDIR(sb.st_mode)) return ASL_STATUS_INVALID_STORE; 109 110 path = NULL; 111 asprintf(&path, "%s/%s", basedir, FILE_ASL_STORE_DATA); 112 if (path == NULL) return ASL_STATUS_NO_MEMORY; 113 114 sd = NULL; 115 116 memset(&sb, 0, sizeof(struct stat)); 117 if (stat(path, &sb) != 0) 118 { 119 if (errno != ENOENT) 120 { 121 free(path); 122 return ASL_STATUS_FAILED; 123 } 124 125 sd = fopen(path, "w+"); 126 free(path); 127 128 if (sd == NULL) return ASL_STATUS_FAILED; 129 130 last_id = 0; 131 132 /* Create new StoreData file (8 bytes ID + 4 bytes flags) */ 133 134 if (fwrite(&last_id, sizeof(uint64_t), 1, sd) != 1) 135 { 136 fclose(sd); 137 return ASL_STATUS_WRITE_FAILED; 138 } 139 140 flags = 0; 141 if (fwrite(&flags, sizeof(uint32_t), 1, sd) != 1) 142 { 143 fclose(sd); 144 return ASL_STATUS_WRITE_FAILED; 145 } 146 147 /* flush data */ 148 fflush(sd); 149 } 150 else 151 { 152 sd = fopen(path, "r+"); 153 free(path); 154 155 if (sd == NULL) return ASL_STATUS_FAILED; 156 if (fread(&last_id, sizeof(uint64_t), 1, sd) != 1) 157 { 158 fclose(sd); 159 return ASL_STATUS_READ_FAILED; 160 } 161 162 last_id = asl_core_ntohq(last_id); 163 } 164 165 out = (asl_store_t *)calloc(1, sizeof(asl_store_t)); 166 if (out == NULL) 167 { 168 fclose(sd); 169 return ASL_STATUS_NO_MEMORY; 170 } 171 172 out->asl_type = ASL_TYPE_STORE; 173 out->refcount = 1; 174 175 if (basedir == NULL) out->base_dir = strdup(PATH_ASL_STORE); 176 else out->base_dir = strdup(basedir); 177 178 if (out->base_dir == NULL) 179 { 180 fclose(sd); 181 free(out); 182 return ASL_STATUS_NO_MEMORY; 183 } 184 185 out->start_today = start; 186 out->start_tomorrow = out->start_today + SECONDS_PER_DAY; 187 out->storedata = sd; 188 out->next_id = last_id + 1; 189 190 for (i = 0; i < FILE_CACHE_SIZE; i++) 191 { 192 memset(&out->file_cache[i], 0, sizeof(asl_cached_file_t)); 193 out->file_cache[i].u = -1; 194 out->file_cache[i].g = -1; 195 } 196 197 *s = out; 198 return ASL_STATUS_OK; 199} 200 201uint32_t 202asl_store_set_flags(asl_store_t *s, uint32_t flags) 203{ 204 if (s == NULL) return 0; 205 uint32_t oldflags = s->flags; 206 s->flags = flags; 207 return oldflags; 208} 209 210ASL_STATUS 211asl_store_statistics(asl_store_t *s, asl_msg_t **msg) 212{ 213 asl_msg_t *out; 214 215 if (s == NULL) return ASL_STATUS_INVALID_STORE; 216 if (msg == NULL) return ASL_STATUS_INVALID_ARG; 217 218 out = asl_msg_new(ASL_TYPE_MSG); 219 if (out == NULL) return ASL_STATUS_NO_MEMORY; 220 221 /* does nothing for now */ 222 223 *msg = out; 224 return ASL_STATUS_OK; 225} 226 227uint32_t 228asl_store_open_read(const char *basedir, asl_store_t **s) 229{ 230 asl_store_t *out; 231 struct stat sb; 232 233 if (s == NULL) return ASL_STATUS_INVALID_ARG; 234 235 if (basedir == NULL) basedir = PATH_ASL_STORE; 236 237 memset(&sb, 0, sizeof(struct stat)); 238 if (stat(basedir, &sb) != 0) return ASL_STATUS_INVALID_STORE; 239 if (!S_ISDIR(sb.st_mode)) return ASL_STATUS_INVALID_STORE; 240 241 out = (asl_store_t *)calloc(1, sizeof(asl_store_t)); 242 if (out == NULL) return ASL_STATUS_NO_MEMORY; 243 244 out->asl_type = ASL_TYPE_STORE; 245 out->refcount = 1; 246 247 if (basedir == NULL) out->base_dir = strdup(PATH_ASL_STORE); 248 else out->base_dir = strdup(basedir); 249 250 if (out->base_dir == NULL) 251 { 252 free(out); 253 return ASL_STATUS_NO_MEMORY; 254 } 255 256 *s = out; 257 return ASL_STATUS_OK; 258} 259 260uint32_t 261asl_store_max_file_size(asl_store_t *s, size_t max) 262{ 263 if (s == NULL) return ASL_STATUS_INVALID_STORE; 264 265 s->max_file_size = max; 266 return ASL_STATUS_OK; 267} 268 269__private_extern__ void 270asl_store_file_closeall(asl_store_t *s) 271{ 272 uint32_t i; 273 274 if (s == NULL) return; 275 276 for (i = 0; i < FILE_CACHE_SIZE; i++) 277 { 278 if (s->file_cache[i].f != NULL) asl_file_close(s->file_cache[i].f); 279 s->file_cache[i].f = NULL; 280 if (s->file_cache[i].path != NULL) free(s->file_cache[i].path); 281 s->file_cache[i].path = NULL; 282 s->file_cache[i].u = -1; 283 s->file_cache[i].g = -1; 284 s->file_cache[i].bb = 0; 285 s->file_cache[i].ts = 0; 286 } 287} 288 289asl_store_t * 290asl_store_retain(asl_store_t *s) 291{ 292 if (s == NULL) return NULL; 293 asl_retain((asl_object_t)s); 294 return s; 295} 296 297void 298asl_store_release(asl_store_t *s) 299{ 300 if (s == NULL) return; 301 asl_release((asl_object_t)s); 302} 303 304ASL_STATUS 305asl_store_close(asl_store_t *s) 306{ 307 if (s == NULL) return ASL_STATUS_OK; 308 asl_release((asl_object_t)s); 309 return ASL_STATUS_OK; 310} 311 312static void 313_asl_store_free_internal(asl_store_t *s) 314{ 315 if (s == NULL) return; 316 317 if (s->base_dir != NULL) free(s->base_dir); 318 s->base_dir = NULL; 319 asl_store_file_closeall(s); 320 if (s->storedata != NULL) fclose(s->storedata); 321 322 free(s); 323} 324 325/* 326 * Sweep the file cache. 327 * Close any files that have not been used in the last FILE_CACHE_TTL seconds. 328 * Returns least recently used or unused cache slot. 329 */ 330static uint32_t 331asl_store_file_cache_lru(asl_store_t *s, time_t now, uint32_t ignorex) 332{ 333 time_t min; 334 uint32_t i, x; 335 336 if (s == NULL) return 0; 337 338 x = 0; 339 min = now - FILE_CACHE_TTL; 340 341 for (i = 0; i < FILE_CACHE_SIZE; i++) 342 { 343 if ((i != ignorex) && (s->file_cache[i].ts < min)) 344 { 345 asl_file_close(s->file_cache[i].f); 346 s->file_cache[i].f = NULL; 347 if (s->file_cache[i].path != NULL) free(s->file_cache[i].path); 348 s->file_cache[i].path = NULL; 349 s->file_cache[i].u = -1; 350 s->file_cache[i].g = -1; 351 s->file_cache[i].bb = 0; 352 s->file_cache[i].ts = 0; 353 } 354 355 if (s->file_cache[i].ts < s->file_cache[x].ts) x = i; 356 } 357 358 return x; 359} 360 361ASL_STATUS 362asl_store_sweep_file_cache(asl_store_t *s) 363{ 364 if (s == NULL) return ASL_STATUS_INVALID_STORE; 365 366 asl_store_file_cache_lru(s, time(NULL), FILE_CACHE_SIZE); 367 return ASL_STATUS_OK; 368} 369 370static char * 371asl_store_make_ug_path(const char *dir, const char *base, const char *ext, uid_t ruid, gid_t rgid, uid_t *u, gid_t *g, mode_t *m) 372{ 373 char *path = NULL; 374 375 *u = 0; 376 *g = 0; 377 *m = 0644; 378 379 if (ruid == -1) 380 { 381 if (rgid == -1) 382 { 383 if (ext == NULL) asprintf(&path, "%s/%s", dir, base); 384 else asprintf(&path, "%s/%s.%s", dir, base, ext); 385 } 386 else 387 { 388 *g = rgid; 389 *m = 0600; 390 if (ext == NULL) asprintf(&path, "%s/%s.G%d", dir, base, *g); 391 else asprintf(&path, "%s/%s.G%d.%s", dir, base, *g, ext); 392 } 393 } 394 else 395 { 396 *u = ruid; 397 if (rgid == -1) 398 { 399 *m = 0600; 400 if (ext == NULL) asprintf(&path, "%s/%s.U%d", dir, base, *u); 401 else asprintf(&path, "%s/%s.U%d.%s", dir, base, *u, ext); 402 } 403 else 404 { 405 *g = rgid; 406 *m = 0600; 407 if (ext == NULL) asprintf(&path, "%s/%s.U%d.G%d", dir, base, *u, *g); 408 else asprintf(&path, "%s/%s.U%d.G%u.%s", dir, base, *u, *g, ext); 409 } 410 } 411 412 return path; 413} 414 415static ASL_STATUS 416asl_store_file_open_write(asl_store_t *s, char *tstring, int32_t ruid, int32_t rgid, time_t bb, asl_file_t **f, time_t now, uint32_t check_cache) 417{ 418 char *path; 419 mode_t m; 420 int32_t i, x; 421 uid_t u; 422 gid_t g; 423 uint32_t status; 424 asl_file_t *out; 425 426 if (s == NULL) return ASL_STATUS_INVALID_STORE; 427 428 /* see if the file is already open and in the cache */ 429 for (i = 0; i < FILE_CACHE_SIZE; i++) 430 { 431 if ((s->file_cache[i].u == ruid) && (s->file_cache[i].g == rgid) && (s->file_cache[i].bb == bb) && (s->file_cache[i].f != NULL)) 432 { 433 s->file_cache[i].ts = now; 434 *f = s->file_cache[i].f; 435 if (check_cache == 1) asl_store_file_cache_lru(s, now, i); 436 return ASL_STATUS_OK; 437 } 438 } 439 440 u = 0; 441 g = 0; 442 m = 0644; 443 path = asl_store_make_ug_path(s->base_dir, tstring, "asl", (uid_t)ruid, (gid_t)rgid, &u, &g, &m); 444 if (path == NULL) return ASL_STATUS_NO_MEMORY; 445 446 out = NULL; 447 status = asl_file_open_write(path, m, u, g, &out); 448 if (status != ASL_STATUS_OK) 449 { 450 free(path); 451 return status; 452 } 453 454 x = asl_store_file_cache_lru(s, now, FILE_CACHE_SIZE); 455 if (s->file_cache[x].f != NULL) asl_file_close(s->file_cache[x].f); 456 if (s->file_cache[x].path != NULL) free(s->file_cache[x].path); 457 458 s->file_cache[x].f = out; 459 s->file_cache[x].path = path; 460 s->file_cache[x].u = ruid; 461 s->file_cache[x].g = rgid; 462 s->file_cache[x].bb = bb; 463 s->file_cache[x].ts = time(NULL); 464 465 *f = out; 466 467 return ASL_STATUS_OK; 468} 469 470__private_extern__ char * 471asl_store_file_path(asl_store_t *s, asl_file_t *f) 472{ 473 uint32_t i; 474 475 if (s == NULL) return NULL; 476 477 for (i = 0; i < FILE_CACHE_SIZE; i++) 478 { 479 if (s->file_cache[i].f == f) 480 { 481 if (s->file_cache[i].path == NULL) return NULL; 482 return strdup(s->file_cache[i].path); 483 } 484 } 485 486 return NULL; 487} 488 489__private_extern__ void 490asl_store_file_close(asl_store_t *s, asl_file_t *f) 491{ 492 uint32_t i; 493 494 if (s == NULL) return; 495 if (f == NULL) return; 496 497 for (i = 0; i < FILE_CACHE_SIZE; i++) 498 { 499 if (s->file_cache[i].f == f) 500 { 501 asl_file_close(s->file_cache[i].f); 502 s->file_cache[i].f = NULL; 503 if (s->file_cache[i].path != NULL) free(s->file_cache[i].path); 504 s->file_cache[i].path = NULL; 505 s->file_cache[i].u = -1; 506 s->file_cache[i].g = -1; 507 s->file_cache[i].bb = 0; 508 s->file_cache[i].ts = 0; 509 return; 510 } 511 } 512} 513 514ASL_STATUS 515asl_store_save(asl_store_t *s, asl_msg_t *msg) 516{ 517 struct tm ctm; 518 time_t msg_time, now, bb; 519 char *path, *tmp_path, *tstring, *scratch; 520 const char *val; 521 uid_t ruid; 522 gid_t rgid; 523 asl_file_t *f; 524 uint32_t status, check_cache, trigger_aslmanager, len; 525 uint64_t xid, ftime; 526 size_t fsize; 527 528 if (s == NULL) return ASL_STATUS_INVALID_STORE; 529 if (msg == NULL) return ASL_STATUS_INVALID_ARG; 530 531 now = time(NULL); 532 533 check_cache = 0; 534 if ((s->last_write + FILE_CACHE_TTL) <= now) check_cache = 1; 535 536 trigger_aslmanager = 0; 537 538 msg_time = 0; 539 val = NULL; 540 541 if (asl_msg_lookup(msg, ASL_KEY_TIME, &val, NULL) != 0) msg_time = now; 542 else if (val == NULL) msg_time = now; 543 else msg_time = asl_core_parse_time(val, NULL); 544 545 if (msg_time >= s->start_tomorrow) 546 { 547 if (now >= s->start_tomorrow) 548 { 549 /* new day begins */ 550 check_cache = 0; 551 asl_store_file_closeall(s); 552 553 /* 554 * _asl_start_today should never fail, but if it does, 555 * just push forward one day. That will probably be correct, and if 556 * it isn't, the next message that gets saved will push it ahead again 557 * until we get to the right date. 558 */ 559 s->start_today = _asl_start_today(); 560 if (s->start_today == 0) s->start_today = s->start_tomorrow; 561 562 s->start_tomorrow = s->start_today + SECONDS_PER_DAY; 563 } 564 } 565 566 ruid = -1; 567 rgid = -1; 568 if ((s->flags & ASL_STORE_FLAG_NO_ACLS) == 0) 569 { 570 val = NULL; 571 if ((asl_msg_lookup(msg, ASL_KEY_READ_UID, &val, NULL) == 0) && (val != NULL)) ruid = atoi(val); 572 573 val = NULL; 574 if ((asl_msg_lookup(msg, ASL_KEY_READ_GID, &val, NULL) == 0) && (val != NULL)) rgid = atoi(val); 575 } 576 577 bb = 0; 578 if ((s->flags & ASL_STORE_FLAG_NO_TTL) == 0) 579 { 580 val = NULL; 581 if ((asl_msg_lookup(msg, ASL_KEY_EXPIRE_TIME, &val, NULL) == 0) && (val != NULL)) 582 { 583 bb = 1; 584 msg_time = asl_core_parse_time(val, NULL); 585 } 586 } 587 588 if (fseeko(s->storedata, 0, SEEK_SET) != 0) return ASL_STATUS_WRITE_FAILED; 589 590 xid = asl_core_htonq(s->next_id); 591 if (fwrite(&xid, sizeof(uint64_t), 1, s->storedata) != 1) return ASL_STATUS_WRITE_FAILED; 592 593 /* flush data */ 594 fflush(s->storedata); 595 596 xid = s->next_id; 597 s->next_id++; 598 599 s->last_write = now; 600 601 if (localtime_r((const time_t *)&msg_time, &ctm) == NULL) return ASL_STATUS_FAILED; 602 603 tstring = NULL; 604 if (bb == 1) 605 { 606 /* 607 * This supports 12 monthly "Best Before" buckets. 608 * We advance the actual expiry time to day zero of the following month. 609 * mktime() is clever enough to know that you actually mean the last day 610 * of the previous month. What we get back from localtime is the last 611 * day of the month in which the message expires, which we use in the name. 612 */ 613 ctm.tm_sec = 0; 614 ctm.tm_min = 0; 615 ctm.tm_hour = 0; 616 ctm.tm_mday = 0; 617 ctm.tm_mon += 1; 618 619 bb = mktime(&ctm); 620 621 if (localtime_r((const time_t *)&bb, &ctm) == NULL) return ASL_STATUS_FAILED; 622 asprintf(&tstring, "BB.%d.%02d.%02d", ctm.tm_year + 1900, ctm.tm_mon + 1, ctm.tm_mday); 623 } 624 else 625 { 626 asprintf(&tstring, "%d.%02d.%02d", ctm.tm_year + 1900, ctm.tm_mon + 1, ctm.tm_mday); 627 } 628 629 if (tstring == NULL) return ASL_STATUS_NO_MEMORY; 630 631 status = asl_store_file_open_write(s, tstring, ruid, rgid, bb, &f, now, check_cache); 632 free(tstring); 633 tstring = NULL; 634 635 if (status != ASL_STATUS_OK) return status; 636 637 status = asl_file_save(f, msg, &xid); 638 if (status != ASL_STATUS_OK) return status; 639 640 fsize = asl_file_size(f); 641 ftime = asl_file_ctime(f); 642 643 /* if file is larger than max_file_size, rename it and trigger aslmanager */ 644 if ((s->max_file_size != 0) && (fsize > s->max_file_size)) 645 { 646 trigger_aslmanager = 1; 647 status = ASL_STATUS_OK; 648 649 path = asl_store_file_path(s, f); 650 651 asl_store_file_close(s, f); 652 653 if (path != NULL) 654 { 655 tmp_path = NULL; 656 657 len = strlen(path); 658 if ((len >= 4) && (!strcmp(path + len - 4, ".asl"))) 659 { 660 /* rename xxxxxxx.asl to xxxxxxx.timestamp.asl */ 661 scratch = strdup(path); 662 if (scratch != NULL) 663 { 664 scratch[len - 4] = '\0'; 665 asprintf(&tmp_path, "%s.%llu.asl", scratch, ftime); 666 free(scratch); 667 668 } 669 } 670 else 671 { 672 /* append timestamp */ 673 asprintf(&tmp_path, "%s.%llu", path, ftime); 674 } 675 676 if (tmp_path == NULL) 677 { 678 status = ASL_STATUS_NO_MEMORY; 679 } 680 else 681 { 682 if (rename(path, tmp_path) != 0) status = ASL_STATUS_FAILED; 683 free(tmp_path); 684 } 685 686 free(path); 687 } 688 } 689 690 if (trigger_aslmanager != 0) asl_trigger_aslmanager(); 691 692 return status; 693} 694 695static ASL_STATUS 696asl_store_mkdir(asl_store_t *s, const char *dir, mode_t m) 697{ 698 char *tstring = NULL; 699 int status; 700 struct stat sb; 701 702 asprintf(&tstring, "%s/%s", s->base_dir, dir); 703 if (tstring == NULL) return ASL_STATUS_NO_MEMORY; 704 705 memset(&sb, 0, sizeof(struct stat)); 706 status = stat(tstring, &sb); 707 708 if (status == 0) 709 { 710 /* must be a directory */ 711 if (!S_ISDIR(sb.st_mode)) 712 { 713 free(tstring); 714 return ASL_STATUS_INVALID_STORE; 715 } 716 } 717 else 718 { 719 if (errno == ENOENT) 720 { 721 /* doesn't exist - create it */ 722 if (mkdir(tstring, m) != 0) 723 { 724 free(tstring); 725 return ASL_STATUS_WRITE_FAILED; 726 } 727 } 728 else 729 { 730 /* stat failed for some other reason */ 731 free(tstring); 732 return ASL_STATUS_FAILED; 733 } 734 } 735 736 free(tstring); 737 return ASL_STATUS_OK; 738} 739 740ASL_STATUS 741asl_store_open_aux(asl_store_t *s, asl_msg_t *msg, int *out_fd, char **url) 742{ 743 struct tm ctm; 744 time_t msg_time, bb; 745 char *path, *dir, *tstring; 746 const char *val; 747 uid_t ruid, u; 748 gid_t rgid, g; 749 mode_t m; 750 uint32_t status; 751 uint64_t fid; 752 int fd; 753 754 if (s == NULL) return ASL_STATUS_INVALID_STORE; 755 if (msg == NULL) return ASL_STATUS_INVALID_ARG; 756 if (out_fd == NULL) return ASL_STATUS_INVALID_ARG; 757 if (url == NULL) return ASL_STATUS_INVALID_ARG; 758 759 msg_time = time(NULL); 760 761 ruid = -1; 762 rgid = -1; 763 if ((s->flags & ASL_STORE_FLAG_NO_ACLS) == 0) 764 { 765 val = NULL; 766 if ((asl_msg_lookup(msg, ASL_KEY_READ_UID, &val, NULL) == 0) && (val != NULL)) ruid = atoi(val); 767 768 val = NULL; 769 if ((asl_msg_lookup(msg, ASL_KEY_READ_GID, &val, NULL) == 0) && (val != NULL)) rgid = atoi(val); 770 } 771 772 bb = 0; 773 if ((s->flags & ASL_STORE_FLAG_NO_TTL) == 0) 774 { 775 val = NULL; 776 if ((asl_msg_lookup(msg, ASL_KEY_EXPIRE_TIME, &val, NULL) == 0) && (val != NULL)) 777 { 778 bb = 1; 779 msg_time = asl_core_parse_time(val, NULL); 780 } 781 } 782 783 if (localtime_r((const time_t *)&msg_time, &ctm) == NULL) return ASL_STATUS_FAILED; 784 785 dir = NULL; 786 if (bb == 1) 787 { 788 /* 789 * This supports 12 monthly "Best Before" buckets. 790 * We advance the actual expiry time to day zero of the following month. 791 * mktime() is clever enough to know that you actually mean the last day 792 * of the previous month. What we get back from localtime is the last 793 * day of the month in which the message expires, which we use in the name. 794 */ 795 ctm.tm_sec = 0; 796 ctm.tm_min = 0; 797 ctm.tm_hour = 0; 798 ctm.tm_mday = 0; 799 ctm.tm_mon += 1; 800 801 bb = mktime(&ctm); 802 803 if (localtime_r((const time_t *)&bb, &ctm) == NULL) return ASL_STATUS_FAILED; 804 asprintf(&dir, "BB.AUX.%d.%02d.%02d", ctm.tm_year + 1900, ctm.tm_mon + 1, ctm.tm_mday); 805 } 806 else 807 { 808 asprintf(&dir, "AUX.%d.%02d.%02d", ctm.tm_year + 1900, ctm.tm_mon + 1, ctm.tm_mday); 809 } 810 811 if (dir == NULL) return ASL_STATUS_NO_MEMORY; 812 813 status = asl_store_mkdir(s, dir, 0755); 814 if (status != ASL_STATUS_OK) 815 { 816 free(dir); 817 return status; 818 } 819 820 fid = s->next_id; 821 s->next_id++; 822 tstring = NULL; 823 824 asprintf(&tstring, "%s/%llu", dir, fid); 825 free(dir); 826 if (tstring == NULL) return ASL_STATUS_NO_MEMORY; 827 828 u = 0; 829 g = 0; 830 m = 0644; 831 path = asl_store_make_ug_path(s->base_dir, tstring, NULL, ruid, rgid, &u, &g, &m); 832 free(tstring); 833 if (path == NULL) return ASL_STATUS_NO_MEMORY; 834 835 fd = asl_file_create(path, u, g, m); 836 if (fd < 0) 837 { 838 free(path); 839 *out_fd = -1; 840 return ASL_STATUS_WRITE_FAILED; 841 } 842 843 /* URL is file://<path> */ 844 *url = NULL; 845 asprintf(url, "file://%s", path); 846 free(path); 847 848 *out_fd = fd; 849 850 return status; 851} 852 853asl_msg_list_t * 854asl_store_match(asl_store_t *s, asl_msg_list_t *qlist, uint64_t *last_id, uint64_t start_id, uint32_t count, uint32_t duration, int32_t direction) 855{ 856 DIR *dp; 857 struct dirent *dent; 858 uint32_t status; 859 asl_file_t *f; 860 char *path; 861 asl_file_list_t *files; 862 asl_msg_list_t *res; 863 864 if (s == NULL) return NULL; 865 866 files = NULL; 867 868 /* 869 * Open all readable files 870 */ 871 dp = opendir(s->base_dir); 872 if (dp == NULL) return NULL; 873 874 while ((dent = readdir(dp)) != NULL) 875 { 876 if (dent->d_name[0] == '.') continue; 877 878 path = NULL; 879 asprintf(&path, "%s/%s", s->base_dir, dent->d_name); 880 881 /* NB asl_file_open_read will fail if path is NULL, if the file is not an ASL store file, or if it isn't readable */ 882 status = asl_file_open_read(path, &f); 883 if (path != NULL) free(path); 884 if ((status != ASL_STATUS_OK) || (f == NULL)) continue; 885 886 files = asl_file_list_add(files, f); 887 } 888 889 closedir(dp); 890 891 res = asl_file_list_match(files, qlist, last_id, start_id, count, duration, direction); 892 asl_file_list_close(files); 893 return res; 894} 895 896/* 897 * PRIVATE FOR DEV TOOLS SUPPORT 898 * DO NOT USE THIS INTERFACE OTHERWISE 899 * 900 * This is only called by a client that compiled with a 10.9 SDK, but is running 901 * with an new 10.10 libasl. 902 * 903 * Only searches the ASL database, so the store (first parameter) is ignored. 904 * 905 * The query and result are old-style message lists. 906 * 907 */ 908typedef struct 909{ 910 uint32_t count; 911 uint32_t curr; 912 void **msg; 913} asl_msg_list_v1_t; 914 915ASL_STATUS 916asl_store_match_timeout(void *ignored, void *query_v1, void **result_v1, uint64_t *last_id, uint64_t start_id, uint32_t count, int32_t direction, uint32_t usec) 917{ 918 asl_store_t *asldb = NULL; 919 asl_msg_list_v1_t *listv1; 920 asl_msg_list_t *qlist = NULL; 921 uint32_t status, n; 922 923 if (result_v1 == NULL) return ASL_STATUS_FAILED; 924 *result_v1 = NULL; 925 926 status = asl_store_open_read(NULL, &asldb); 927 if (status != ASL_STATUS_OK) return status; 928 929 /* convert query_v1 into an asl_msg_list_t */ 930 listv1 = (asl_msg_list_v1_t *)query_v1; 931 if (listv1 != NULL) 932 { 933 if (listv1->count > 0) qlist = (asl_msg_list_t *)asl_new(ASL_TYPE_LIST); 934 935 for (listv1->curr = 0; listv1->curr < listv1->count; listv1->curr++) 936 { 937 asl_append((asl_object_t)qlist, (asl_object_t)listv1->msg[listv1->curr]); 938 } 939 } 940 941 asl_msg_list_t *result = asl_store_match(asldb, qlist, last_id, start_id, count, usec, direction); 942 asl_release((asl_object_t)asldb); 943 asl_release((asl_object_t)qlist); 944 945 if (result == NULL) return ASL_STATUS_OK; 946 947 n = asl_count((asl_object_t)result); 948 if (n == 0) 949 { 950 asl_release((asl_object_t)result); 951 return ASL_STATUS_OK; 952 } 953 954 listv1 = (asl_msg_list_v1_t *)calloc(1, sizeof(asl_msg_list_v1_t)); 955 if (listv1 == NULL) 956 { 957 asl_release((asl_object_t)result); 958 return ASL_STATUS_NO_MEMORY; 959 } 960 961 listv1->count = n; 962 listv1->msg = (void **)calloc(listv1->count, sizeof(void *)); 963 if (listv1 == NULL) 964 { 965 free(listv1); 966 asl_release((asl_object_t)result); 967 return ASL_STATUS_NO_MEMORY; 968 } 969 970 for (listv1->curr = 0; listv1->curr < listv1->count; listv1->curr++) 971 { 972 listv1->msg[listv1->curr] = asl_retain(asl_next((asl_object_t)result)); 973 } 974 975 listv1->curr = 0; 976 *result_v1 = listv1; 977 978 asl_release((asl_object_t)result); 979 return ASL_STATUS_OK; 980} 981 982ASL_STATUS 983asl_store_match_start(asl_store_t *s, uint64_t start_id, int32_t direction) 984{ 985 DIR *dp; 986 struct dirent *dent; 987 uint32_t status; 988 asl_file_t *f; 989 char *path; 990 asl_file_list_t *files; 991 992 if (s == NULL) return ASL_STATUS_INVALID_STORE; 993 994 if (s->work != NULL) asl_file_list_match_end(s->work); 995 s->work = NULL; 996 997 files = NULL; 998 999 /* 1000 * Open all readable files 1001 */ 1002 dp = opendir(s->base_dir); 1003 if (dp == NULL) return ASL_STATUS_READ_FAILED; 1004 1005 while ((dent = readdir(dp)) != NULL) 1006 { 1007 if (dent->d_name[0] == '.') continue; 1008 1009 path = NULL; 1010 asprintf(&path, "%s/%s", s->base_dir, dent->d_name); 1011 1012 /* 1013 * NB asl_file_open_read will fail if path is NULL, 1014 * if it is not an ASL store file, or if it isn't readable. 1015 * We expect that. 1016 */ 1017 status = asl_file_open_read(path, &f); 1018 if (path != NULL) free(path); 1019 if ((status != ASL_STATUS_OK) || (f == NULL)) continue; 1020 1021 files = asl_file_list_add(files, f); 1022 } 1023 1024 closedir(dp); 1025 1026 s->work = asl_file_list_match_start(files, start_id, direction); 1027 if (s->work == NULL) return ASL_STATUS_FAILED; 1028 1029 return ASL_STATUS_OK; 1030} 1031 1032ASL_STATUS 1033asl_store_match_next(asl_store_t *s, asl_msg_list_t *qlist, asl_msg_list_t **res, uint32_t count) 1034{ 1035 if (s == NULL) return ASL_STATUS_INVALID_STORE; 1036 if (s->work == NULL) return ASL_STATUS_OK; 1037 1038 return asl_file_list_match_next(s->work, qlist, res, count); 1039} 1040 1041#pragma mark - 1042#pragma mark asl_object support 1043 1044static void 1045_jump_dealloc(asl_object_private_t *obj) 1046{ 1047 _asl_store_free_internal((asl_store_t *)obj); 1048} 1049 1050static asl_object_private_t * 1051_jump_next(asl_object_private_t *obj) 1052{ 1053 asl_store_t *s = (asl_store_t *)obj; 1054 asl_msg_list_t *list; 1055 asl_msg_t *out = NULL; 1056 uint64_t last = 0; 1057 1058 if (s == NULL) return NULL; 1059 if (s->curr == SIZE_MAX) return NULL; 1060 1061 s->curr++; 1062 list = asl_store_match(s, NULL, &last, s->curr, 1, 0, 1); 1063 if (list == NULL) 1064 { 1065 s->curr = SIZE_MAX; 1066 return NULL; 1067 } 1068 1069 s->curr = last; 1070 out = asl_msg_list_get_index(list, 0); 1071 asl_msg_list_release(list); 1072 1073 return (asl_object_private_t *)out; 1074} 1075 1076static asl_object_private_t * 1077_jump_prev(asl_object_private_t *obj) 1078{ 1079 asl_store_t *s = (asl_store_t *)obj; 1080 asl_msg_list_t *list; 1081 asl_msg_t *out = NULL; 1082 uint64_t last = 0; 1083 1084 if (s == NULL) return NULL; 1085 if (s->curr == 0) return NULL; 1086 1087 s->curr--; 1088 if (s->curr == 0) return NULL; 1089 1090 list = asl_store_match(s, NULL, &last, s->curr, 1, 0, -1); 1091 if (list == NULL) 1092 { 1093 s->curr = 0; 1094 return NULL; 1095 } 1096 1097 s->curr = last; 1098 out = asl_msg_list_get_index(list, 0); 1099 asl_msg_list_release(list); 1100 1101 return (asl_object_private_t *)out; 1102} 1103 1104static void 1105_jump_set_iteration_index(asl_object_private_t *obj, size_t n) 1106{ 1107 asl_store_t *s = (asl_store_t *)obj; 1108 if (s == NULL) return; 1109 1110 s->curr = n; 1111} 1112 1113static void 1114_jump_append(asl_object_private_t *obj, asl_object_private_t *newobj) 1115{ 1116 asl_store_t *s = (asl_store_t *)obj; 1117 int type = asl_get_type((asl_object_t)newobj); 1118 if (s == NULL) return; 1119 if (s->flags & ASL_FILE_FLAG_READ) return; 1120 1121 if (type == ASL_TYPE_LIST) 1122 { 1123 asl_msg_t *msg; 1124 asl_msg_list_reset_iteration((asl_msg_list_t *)newobj, 0); 1125 while (NULL != (msg = asl_msg_list_next((asl_msg_list_t *)newobj))) 1126 { 1127 if (asl_store_save(s, msg) != ASL_STATUS_OK) return; 1128 } 1129 } 1130 else if ((type == ASL_TYPE_MSG) || (type == ASL_TYPE_QUERY)) 1131 { 1132 asl_store_save(s, (asl_msg_t *)newobj); 1133 } 1134} 1135 1136static asl_object_private_t * 1137_jump_search(asl_object_private_t *obj, asl_object_private_t *query) 1138{ 1139 asl_store_t *s = (asl_store_t *)obj; 1140 int type = asl_get_type((asl_object_t)query); 1141 asl_msg_list_t *out = NULL; 1142 asl_msg_list_t *ql = NULL; 1143 uint64_t last; 1144 uint32_t status = ASL_STATUS_FAILED; 1145 1146 if (query == NULL) 1147 { 1148 out = asl_store_match(s, NULL, &last, 0, 0, 0, 1); 1149 } 1150 else if (type == ASL_TYPE_LIST) 1151 { 1152 out = asl_store_match(s, (asl_msg_list_t *)query, &last, 0, 0, 0, 1); 1153 } 1154 else if ((type == ASL_TYPE_MSG) || (type == ASL_TYPE_QUERY)) 1155 { 1156 ql = asl_msg_list_new(); 1157 asl_msg_list_append(ql, query); 1158 1159 out = asl_store_match(s, ql, &last, 0, 0, 0, 1); 1160 asl_msg_list_release(ql); 1161 } 1162 1163 if (status != ASL_STATUS_OK) return NULL; 1164 return (asl_object_private_t *)out; 1165} 1166 1167static asl_object_private_t * 1168_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) 1169{ 1170 uint64_t x; 1171 asl_msg_list_t *out; 1172 1173 out = asl_store_match((asl_store_t *)obj, (asl_msg_list_t *)qlist, &x, start, count, duration, dir); 1174 *last = x; 1175 return (asl_object_private_t *)out; 1176} 1177 1178__private_extern__ const asl_jump_table_t * 1179asl_store_jump_table() 1180{ 1181 static const asl_jump_table_t jump = 1182 { 1183 .alloc = NULL, 1184 .dealloc = &_jump_dealloc, 1185 .set_key_val_op = NULL, 1186 .unset_key = NULL, 1187 .get_val_op_for_key = NULL, 1188 .get_key_val_op_at_index = NULL, 1189 .count = NULL, 1190 .next = &_jump_next, 1191 .prev = &_jump_prev, 1192 .get_object_at_index = NULL, 1193 .set_iteration_index = &_jump_set_iteration_index, 1194 .remove_object_at_index = NULL, 1195 .append = &_jump_append, 1196 .prepend = NULL, 1197 .search = &_jump_search, 1198 .match = &_jump_match 1199 }; 1200 1201 return &jump; 1202} 1203