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 38#include <TargetConditionals.h> 39 40#if TARGET_IPHONE_SIMULATOR 41#include <dispatch/dispatch.h> 42#include <assert.h> 43#endif 44 45extern time_t asl_parse_time(const char *str); 46extern uint64_t asl_file_cursor(asl_file_t *s); 47extern uint32_t asl_file_match_start(asl_file_t *s, uint64_t start_id, int32_t direction); 48extern uint32_t asl_file_match_next(asl_file_t *s, aslresponse query, asl_msg_t **msg, uint64_t *last_id, int32_t direction, int32_t ruid, int32_t rgid); 49extern int asl_file_create(const char *path, uid_t uid, gid_t gid, mode_t mode); 50 51#define SECONDS_PER_DAY 86400 52 53/* 54 * The ASL Store is organized as a set of files in a common directory. 55 * Files are prefixed by the date (YYYY.MM.DD) of their contents. 56 * 57 * Messages with no access controls are saved in YYYY.MM.DD.asl 58 * Messages with access limited to UID uuu are saved in YYYY.MM.DD.Uuuu.asl 59 * Messages with access limited to GID ggg are saved in YYYY.MM.DD.Gggg.asl 60 * Messages with access limited to UID uuu and GID ggg are saved in YYYY.MM.DD.Uuuu.Gggg.asl 61 * 62 * Messages that have a value for ASLExpireTime are saved in BB.YYYY.MM.DD.asl 63 * where the timestamp is the "Best Before" date of the file. Access controls 64 * are implemented as above with Uuuu and Gggg in the file name. Note that the 65 * Best Before files are for the last day of the month, so a single file contains 66 * messages that expire in that month. 67 * 68 * An external tool runs daily and deletes "old" files. 69 */ 70 71static time_t 72_asl_start_today() 73{ 74 time_t now; 75 struct tm ctm; 76 77 memset(&ctm, 0, sizeof(struct tm)); 78 now = time(NULL); 79 80 if (localtime_r((const time_t *)&now, &ctm) == NULL) return 0; 81 82 ctm.tm_sec = 0; 83 ctm.tm_min = 0; 84 ctm.tm_hour = 0; 85 86 return mktime(&ctm); 87} 88 89/* 90 * The base directory contains a data file which stores 91 * the last record ID. 92 * 93 * | MAX_ID (uint64_t) | 94 * 95 */ 96uint32_t 97asl_store_open_write(const char *basedir, asl_store_t **s) 98{ 99 asl_store_t *out; 100 struct stat sb; 101 uint32_t i, flags; 102 char *path; 103 FILE *sd; 104 uint64_t last_id; 105 time_t start; 106 107 if (s == NULL) return ASL_STATUS_INVALID_ARG; 108 109 start = _asl_start_today(); 110 if (start == 0) return ASL_STATUS_FAILED; 111 112 if (basedir == NULL) basedir = PATH_ASL_STORE; 113 114 memset(&sb, 0, sizeof(struct stat)); 115 if (stat(basedir, &sb) != 0) return ASL_STATUS_INVALID_STORE; 116 if (!S_ISDIR(sb.st_mode)) return ASL_STATUS_INVALID_STORE; 117 118 path = NULL; 119 asprintf(&path, "%s/%s", basedir, FILE_ASL_STORE_DATA); 120 if (path == NULL) return ASL_STATUS_NO_MEMORY; 121 122 sd = NULL; 123 124 memset(&sb, 0, sizeof(struct stat)); 125 if (stat(path, &sb) != 0) 126 { 127 if (errno != ENOENT) 128 { 129 free(path); 130 return ASL_STATUS_FAILED; 131 } 132 133 sd = fopen(path, "w+"); 134 free(path); 135 136 if (sd == NULL) return ASL_STATUS_FAILED; 137 138 last_id = 0; 139 140 /* Create new StoreData file (8 bytes ID + 4 bytes flags) */ 141 142 if (fwrite(&last_id, sizeof(uint64_t), 1, sd) != 1) 143 { 144 fclose(sd); 145 return ASL_STATUS_WRITE_FAILED; 146 } 147 148 flags = 0; 149 if (fwrite(&flags, sizeof(uint32_t), 1, sd) != 1) 150 { 151 fclose(sd); 152 return ASL_STATUS_WRITE_FAILED; 153 } 154 155 /* flush data */ 156 fflush(sd); 157 } 158 else 159 { 160 sd = fopen(path, "r+"); 161 free(path); 162 163 if (sd == NULL) return ASL_STATUS_FAILED; 164 if (fread(&last_id, sizeof(uint64_t), 1, sd) != 1) 165 { 166 fclose(sd); 167 return ASL_STATUS_READ_FAILED; 168 } 169 170 last_id = asl_core_ntohq(last_id); 171 } 172 173 out = (asl_store_t *)calloc(1, sizeof(asl_store_t)); 174 if (out == NULL) 175 { 176 fclose(sd); 177 return ASL_STATUS_NO_MEMORY; 178 } 179 180 if (basedir == NULL) out->base_dir = strdup(PATH_ASL_STORE); 181 else out->base_dir = strdup(basedir); 182 183 if (out->base_dir == NULL) 184 { 185 fclose(sd); 186 free(out); 187 return ASL_STATUS_NO_MEMORY; 188 } 189 190 out->start_today = start; 191 out->start_tomorrow = out->start_today + SECONDS_PER_DAY; 192 out->storedata = sd; 193 out->next_id = last_id + 1; 194 195 for (i = 0; i < FILE_CACHE_SIZE; i++) 196 { 197 memset(&out->file_cache[i], 0, sizeof(asl_cached_file_t)); 198 out->file_cache[i].u = -1; 199 out->file_cache[i].g = -1; 200 } 201 202 *s = out; 203 return ASL_STATUS_OK; 204} 205 206uint32_t 207asl_store_statistics(asl_store_t *s, aslmsg *msg) 208{ 209 aslmsg out; 210 211 if (s == NULL) return ASL_STATUS_INVALID_STORE; 212 if (msg == NULL) return ASL_STATUS_INVALID_ARG; 213 214 out = asl_new(ASL_TYPE_MSG); 215 if (out == NULL) return ASL_STATUS_NO_MEMORY; 216 217 /* does nothing for now */ 218 219 *msg = out; 220 return ASL_STATUS_OK; 221} 222 223uint32_t 224asl_store_open_read(const char *basedir, asl_store_t **s) 225{ 226 asl_store_t *out; 227 struct stat sb; 228 229 if (s == NULL) return ASL_STATUS_INVALID_ARG; 230 231 if (basedir == NULL) basedir = PATH_ASL_STORE; 232 233 memset(&sb, 0, sizeof(struct stat)); 234 if (stat(basedir, &sb) != 0) return ASL_STATUS_INVALID_STORE; 235 if (!S_ISDIR(sb.st_mode)) return ASL_STATUS_INVALID_STORE; 236 237 out = (asl_store_t *)calloc(1, sizeof(asl_store_t)); 238 if (out == NULL) return ASL_STATUS_NO_MEMORY; 239 240 if (basedir == NULL) out->base_dir = strdup(PATH_ASL_STORE); 241 else out->base_dir = strdup(basedir); 242 243 if (out->base_dir == NULL) 244 { 245 free(out); 246 return ASL_STATUS_NO_MEMORY; 247 } 248 249 *s = out; 250 return ASL_STATUS_OK; 251} 252 253uint32_t 254asl_store_max_file_size(asl_store_t *s, size_t max) 255{ 256 if (s == NULL) return ASL_STATUS_INVALID_STORE; 257 258 s->max_file_size = max; 259 return ASL_STATUS_OK; 260} 261 262__private_extern__ void 263asl_store_file_closeall(asl_store_t *s) 264{ 265 uint32_t i; 266 267 if (s == NULL) return; 268 269 for (i = 0; i < FILE_CACHE_SIZE; i++) 270 { 271 if (s->file_cache[i].f != NULL) asl_file_close(s->file_cache[i].f); 272 s->file_cache[i].f = NULL; 273 if (s->file_cache[i].path != NULL) free(s->file_cache[i].path); 274 s->file_cache[i].path = NULL; 275 s->file_cache[i].u = -1; 276 s->file_cache[i].g = -1; 277 s->file_cache[i].bb = 0; 278 s->file_cache[i].ts = 0; 279 } 280} 281 282uint32_t 283asl_store_close(asl_store_t *s) 284{ 285 if (s == NULL) return ASL_STATUS_OK; 286 287 if (s->base_dir != NULL) free(s->base_dir); 288 s->base_dir = NULL; 289 asl_store_file_closeall(s); 290 if (s->storedata != NULL) fclose(s->storedata); 291 292 free(s); 293 294 return ASL_STATUS_OK; 295} 296 297/* 298 * Sweep the file cache. 299 * Close any files that have not been used in the last FILE_CACHE_TTL seconds. 300 * Returns least recently used or unused cache slot. 301 */ 302static uint32_t 303asl_store_file_cache_lru(asl_store_t *s, time_t now, uint32_t ignorex) 304{ 305 time_t min; 306 uint32_t i, x; 307 308 if (s == NULL) return 0; 309 310 x = 0; 311 min = now - FILE_CACHE_TTL; 312 313 for (i = 0; i < FILE_CACHE_SIZE; i++) 314 { 315 if ((i != ignorex) && (s->file_cache[i].ts < min)) 316 { 317 asl_file_close(s->file_cache[i].f); 318 s->file_cache[i].f = NULL; 319 if (s->file_cache[i].path != NULL) free(s->file_cache[i].path); 320 s->file_cache[i].path = NULL; 321 s->file_cache[i].u = -1; 322 s->file_cache[i].g = -1; 323 s->file_cache[i].bb = 0; 324 s->file_cache[i].ts = 0; 325 } 326 327 if (s->file_cache[i].ts < s->file_cache[x].ts) x = i; 328 } 329 330 return x; 331} 332 333uint32_t 334asl_store_sweep_file_cache(asl_store_t *s) 335{ 336 if (s == NULL) return ASL_STATUS_INVALID_STORE; 337 338 asl_store_file_cache_lru(s, time(NULL), FILE_CACHE_SIZE); 339 return ASL_STATUS_OK; 340} 341 342static char * 343asl_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) 344{ 345 char *path = NULL; 346 347 *u = 0; 348 *g = 0; 349 *m = 0644; 350 351 if (ruid == -1) 352 { 353 if (rgid == -1) 354 { 355 if (ext == NULL) asprintf(&path, "%s/%s", dir, base); 356 else asprintf(&path, "%s/%s.%s", dir, base, ext); 357 } 358 else 359 { 360 *g = rgid; 361 *m = 0600; 362 if (ext == NULL) asprintf(&path, "%s/%s.G%d", dir, base, *g); 363 else asprintf(&path, "%s/%s.G%d.%s", dir, base, *g, ext); 364 } 365 } 366 else 367 { 368 *u = ruid; 369 if (rgid == -1) 370 { 371 *m = 0600; 372 if (ext == NULL) asprintf(&path, "%s/%s.U%d", dir, base, *u); 373 else asprintf(&path, "%s/%s.U%d.%s", dir, base, *u, ext); 374 } 375 else 376 { 377 *g = rgid; 378 *m = 0600; 379 if (ext == NULL) asprintf(&path, "%s/%s.U%d.G%d", dir, base, *u, *g); 380 else asprintf(&path, "%s/%s.U%d.G%u.%s", dir, base, *u, *g, ext); 381 } 382 } 383 384 return path; 385} 386 387static uint32_t 388asl_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) 389{ 390 char *path; 391 mode_t m; 392 int32_t i, x; 393 uid_t u; 394 gid_t g; 395 uint32_t status; 396 asl_file_t *out; 397 398 if (s == NULL) return ASL_STATUS_INVALID_STORE; 399 400 /* see if the file is already open and in the cache */ 401 for (i = 0; i < FILE_CACHE_SIZE; i++) 402 { 403 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)) 404 { 405 s->file_cache[i].ts = now; 406 *f = s->file_cache[i].f; 407 if (check_cache == 1) asl_store_file_cache_lru(s, now, i); 408 return ASL_STATUS_OK; 409 } 410 } 411 412 u = 0; 413 g = 0; 414 m = 0644; 415 path = asl_store_make_ug_path(s->base_dir, tstring, "asl", (uid_t)ruid, (gid_t)rgid, &u, &g, &m); 416 if (path == NULL) return ASL_STATUS_NO_MEMORY; 417 418 out = NULL; 419 status = asl_file_open_write(path, m, u, g, &out); 420 if (status != ASL_STATUS_OK) 421 { 422 free(path); 423 return status; 424 } 425 426 x = asl_store_file_cache_lru(s, now, FILE_CACHE_SIZE); 427 if (s->file_cache[x].f != NULL) asl_file_close(s->file_cache[x].f); 428 if (s->file_cache[x].path != NULL) free(s->file_cache[x].path); 429 430 s->file_cache[x].f = out; 431 s->file_cache[x].path = path; 432 s->file_cache[x].u = ruid; 433 s->file_cache[x].g = rgid; 434 s->file_cache[x].bb = bb; 435 s->file_cache[x].ts = time(NULL); 436 437 *f = out; 438 439 return ASL_STATUS_OK; 440} 441 442__private_extern__ char * 443asl_store_file_path(asl_store_t *s, asl_file_t *f) 444{ 445 uint32_t i; 446 447 if (s == NULL) return NULL; 448 449 for (i = 0; i < FILE_CACHE_SIZE; i++) 450 { 451 if (s->file_cache[i].f == f) 452 { 453 if (s->file_cache[i].path == NULL) return NULL; 454 return strdup(s->file_cache[i].path); 455 } 456 } 457 458 return NULL; 459} 460 461__private_extern__ void 462asl_store_file_close(asl_store_t *s, asl_file_t *f) 463{ 464 uint32_t i; 465 466 if (s == NULL) return; 467 if (f == NULL) return; 468 469 for (i = 0; i < FILE_CACHE_SIZE; i++) 470 { 471 if (s->file_cache[i].f == f) 472 { 473 asl_file_close(s->file_cache[i].f); 474 s->file_cache[i].f = NULL; 475 if (s->file_cache[i].path != NULL) free(s->file_cache[i].path); 476 s->file_cache[i].path = NULL; 477 s->file_cache[i].u = -1; 478 s->file_cache[i].g = -1; 479 s->file_cache[i].bb = 0; 480 s->file_cache[i].ts = 0; 481 return; 482 } 483 } 484} 485 486uint32_t 487asl_store_save(asl_store_t *s, aslmsg msg) 488{ 489 struct tm ctm; 490 time_t msg_time, now, bb; 491 char *path, *tmp_path, *tstring, *scratch; 492 const char *val; 493 uid_t ruid; 494 gid_t rgid; 495 asl_file_t *f; 496 uint32_t status, check_cache, trigger_aslmanager, len; 497 uint64_t xid, ftime; 498 size_t fsize; 499 500 if (s == NULL) return ASL_STATUS_INVALID_STORE; 501 if (msg == NULL) return ASL_STATUS_INVALID_ARG; 502 503 now = time(NULL); 504 505 check_cache = 0; 506 if ((s->last_write + FILE_CACHE_TTL) <= now) check_cache = 1; 507 508 trigger_aslmanager = 0; 509 510 msg_time = 0; 511 val = asl_get(msg, ASL_KEY_TIME); 512 if (val == NULL) msg_time = now; 513 else msg_time = asl_parse_time(val); 514 515 if (msg_time >= s->start_tomorrow) 516 { 517 if (now >= s->start_tomorrow) 518 { 519 /* new day begins */ 520 check_cache = 0; 521 asl_store_file_closeall(s); 522 523 /* 524 * _asl_start_today should never fail, but if it does, 525 * just push forward one day. That will probably be correct, and if 526 * it isn't, the next message that gets saved will push it ahead again 527 * until we get to the right date. 528 */ 529 s->start_today = _asl_start_today(); 530 if (s->start_today == 0) s->start_today = s->start_tomorrow; 531 532 s->start_tomorrow = s->start_today + SECONDS_PER_DAY; 533 } 534 } 535 536 val = asl_get(msg, ASL_KEY_READ_UID); 537 ruid = -1; 538 if (val != NULL) ruid = atoi(val); 539 540 val = asl_get(msg, ASL_KEY_READ_GID); 541 rgid = -1; 542 if (val != NULL) rgid = atoi(val); 543 544 bb = 0; 545 val = asl_get(msg, ASL_KEY_EXPIRE_TIME); 546 if (val != NULL) 547 { 548 bb = 1; 549 msg_time = asl_parse_time(val); 550 } 551 552 if (fseeko(s->storedata, 0, SEEK_SET) != 0) return ASL_STATUS_WRITE_FAILED; 553 554 xid = asl_core_htonq(s->next_id); 555 if (fwrite(&xid, sizeof(uint64_t), 1, s->storedata) != 1) return ASL_STATUS_WRITE_FAILED; 556 557 /* flush data */ 558 fflush(s->storedata); 559 560 xid = s->next_id; 561 s->next_id++; 562 563 s->last_write = now; 564 565 if (localtime_r((const time_t *)&msg_time, &ctm) == NULL) return ASL_STATUS_FAILED; 566 567 tstring = NULL; 568 if (bb == 1) 569 { 570 /* 571 * This supports 12 monthly "Best Before" buckets. 572 * We advance the actual expiry time to day zero of the following month. 573 * mktime() is clever enough to know that you actually mean the last day 574 * of the previous month. What we get back from localtime is the last 575 * day of the month in which the message expires, which we use in the name. 576 */ 577 ctm.tm_sec = 0; 578 ctm.tm_min = 0; 579 ctm.tm_hour = 0; 580 ctm.tm_mday = 0; 581 ctm.tm_mon += 1; 582 583 bb = mktime(&ctm); 584 585 if (localtime_r((const time_t *)&bb, &ctm) == NULL) return ASL_STATUS_FAILED; 586 asprintf(&tstring, "BB.%d.%02d.%02d", ctm.tm_year + 1900, ctm.tm_mon + 1, ctm.tm_mday); 587 } 588 else 589 { 590 asprintf(&tstring, "%d.%02d.%02d", ctm.tm_year + 1900, ctm.tm_mon + 1, ctm.tm_mday); 591 } 592 593 if (tstring == NULL) return ASL_STATUS_NO_MEMORY; 594 595 status = asl_store_file_open_write(s, tstring, ruid, rgid, bb, &f, now, check_cache); 596 free(tstring); 597 tstring = NULL; 598 599 if (status != ASL_STATUS_OK) return status; 600 601 status = asl_file_save(f, msg, &xid); 602 if (status != ASL_STATUS_OK) return status; 603 604 fsize = asl_file_size(f); 605 ftime = asl_file_ctime(f); 606 607 /* if file is larger than max_file_size, rename it and trigger aslmanager */ 608 if ((s->max_file_size != 0) && (fsize > s->max_file_size)) 609 { 610 trigger_aslmanager = 1; 611 status = ASL_STATUS_OK; 612 613 path = asl_store_file_path(s, f); 614 615 asl_store_file_close(s, f); 616 617 if (path != NULL) 618 { 619 tmp_path = NULL; 620 621 len = strlen(path); 622 if ((len >= 4) && (!strcmp(path + len - 4, ".asl"))) 623 { 624 /* rename xxxxxxx.asl to xxxxxxx.timestamp.asl */ 625 scratch = strdup(path); 626 if (scratch != NULL) 627 { 628 scratch[len - 4] = '\0'; 629 asprintf(&tmp_path, "%s.%llu.asl", scratch, ftime); 630 free(scratch); 631 632 } 633 } 634 else 635 { 636 /* append timestamp */ 637 asprintf(&tmp_path, "%s.%llu", path, ftime); 638 } 639 640 if (tmp_path == NULL) 641 { 642 status = ASL_STATUS_NO_MEMORY; 643 } 644 else 645 { 646 if (rename(path, tmp_path) != 0) status = ASL_STATUS_FAILED; 647 free(tmp_path); 648 } 649 650 free(path); 651 } 652 } 653 654 if (trigger_aslmanager != 0) asl_trigger_aslmanager(); 655 656 return status; 657} 658 659static uint32_t 660asl_store_mkdir(asl_store_t *s, const char *dir, mode_t m) 661{ 662 char *tstring = NULL; 663 int status; 664 struct stat sb; 665 666 asprintf(&tstring, "%s/%s", s->base_dir, dir); 667 if (tstring == NULL) return ASL_STATUS_NO_MEMORY; 668 669 memset(&sb, 0, sizeof(struct stat)); 670 status = stat(tstring, &sb); 671 672 if (status == 0) 673 { 674 /* must be a directory */ 675 if (!S_ISDIR(sb.st_mode)) 676 { 677 free(tstring); 678 return ASL_STATUS_INVALID_STORE; 679 } 680 } 681 else 682 { 683 if (errno == ENOENT) 684 { 685 /* doesn't exist - create it */ 686 if (mkdir(tstring, m) != 0) 687 { 688 free(tstring); 689 return ASL_STATUS_WRITE_FAILED; 690 } 691 } 692 else 693 { 694 /* stat failed for some other reason */ 695 free(tstring); 696 return ASL_STATUS_FAILED; 697 } 698 } 699 700 free(tstring); 701 return ASL_STATUS_OK; 702} 703 704uint32_t 705asl_store_open_aux(asl_store_t *s, aslmsg msg, int *out_fd, char **url) 706{ 707 struct tm ctm; 708 time_t msg_time, bb; 709 char *path, *dir, *tstring; 710 const char *val; 711 uid_t ruid, u; 712 gid_t rgid, g; 713 mode_t m; 714 uint32_t status; 715 uint64_t fid; 716 int fd; 717 718 if (s == NULL) return ASL_STATUS_INVALID_STORE; 719 if (msg == NULL) return ASL_STATUS_INVALID_ARG; 720 if (out_fd == NULL) return ASL_STATUS_INVALID_ARG; 721 if (url == NULL) return ASL_STATUS_INVALID_ARG; 722 723 msg_time = time(NULL); 724 725 val = asl_get(msg, ASL_KEY_READ_UID); 726 ruid = -1; 727 if (val != NULL) ruid = atoi(val); 728 729 val = asl_get(msg, ASL_KEY_READ_GID); 730 rgid = -1; 731 if (val != NULL) rgid = atoi(val); 732 733 bb = 0; 734 val = asl_get(msg, ASL_KEY_EXPIRE_TIME); 735 if (val != NULL) 736 { 737 bb = 1; 738 msg_time = asl_parse_time(val); 739 } 740 741 if (localtime_r((const time_t *)&msg_time, &ctm) == NULL) return ASL_STATUS_FAILED; 742 743 dir = NULL; 744 if (bb == 1) 745 { 746 /* 747 * This supports 12 monthly "Best Before" buckets. 748 * We advance the actual expiry time to day zero of the following month. 749 * mktime() is clever enough to know that you actually mean the last day 750 * of the previous month. What we get back from localtime is the last 751 * day of the month in which the message expires, which we use in the name. 752 */ 753 ctm.tm_sec = 0; 754 ctm.tm_min = 0; 755 ctm.tm_hour = 0; 756 ctm.tm_mday = 0; 757 ctm.tm_mon += 1; 758 759 bb = mktime(&ctm); 760 761 if (localtime_r((const time_t *)&bb, &ctm) == NULL) return ASL_STATUS_FAILED; 762 asprintf(&dir, "BB.AUX.%d.%02d.%02d", ctm.tm_year + 1900, ctm.tm_mon + 1, ctm.tm_mday); 763 } 764 else 765 { 766 asprintf(&dir, "AUX.%d.%02d.%02d", ctm.tm_year + 1900, ctm.tm_mon + 1, ctm.tm_mday); 767 } 768 769 if (dir == NULL) return ASL_STATUS_NO_MEMORY; 770 771 status = asl_store_mkdir(s, dir, 0755); 772 if (status != ASL_STATUS_OK) 773 { 774 free(dir); 775 return status; 776 } 777 778 fid = s->next_id; 779 s->next_id++; 780 tstring = NULL; 781 782 asprintf(&tstring, "%s/%llu", dir, fid); 783 free(dir); 784 if (tstring == NULL) return ASL_STATUS_NO_MEMORY; 785 786 u = 0; 787 g = 0; 788 m = 0644; 789 path = asl_store_make_ug_path(s->base_dir, tstring, NULL, ruid, rgid, &u, &g, &m); 790 free(tstring); 791 if (path == NULL) return ASL_STATUS_NO_MEMORY; 792 793 fd = asl_file_create(path, u, g, m); 794 if (fd < 0) 795 { 796 free(path); 797 *out_fd = -1; 798 return ASL_STATUS_WRITE_FAILED; 799 } 800 801 /* URL is file://<path> */ 802 *url = NULL; 803 asprintf(url, "file://%s", path); 804 free(path); 805 806 *out_fd = fd; 807 808 return status; 809} 810 811uint32_t 812asl_store_match_timeout(asl_store_t *s, aslresponse query, aslresponse *res, uint64_t *last_id, uint64_t start_id, uint32_t count, int32_t direction, uint32_t usec) 813{ 814 DIR *dp; 815 struct dirent *dent; 816 uint32_t status; 817 asl_file_t *f; 818 char *path; 819 asl_file_list_t *files; 820 821 if (s == NULL) return ASL_STATUS_INVALID_STORE; 822 if (res == NULL) return ASL_STATUS_INVALID_ARG; 823 824 files = NULL; 825 826 /* 827 * Open all readable files 828 */ 829 dp = opendir(s->base_dir); 830 if (dp == NULL) return ASL_STATUS_READ_FAILED; 831 832 while ((dent = readdir(dp)) != NULL) 833 { 834 if (dent->d_name[0] == '.') continue; 835 836 path = NULL; 837 asprintf(&path, "%s/%s", s->base_dir, dent->d_name); 838 839 /* 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 */ 840 status = asl_file_open_read(path, &f); 841 if (path != NULL) free(path); 842 if ((status != ASL_STATUS_OK) || (f == NULL)) continue; 843 844 files = asl_file_list_add(files, f); 845 } 846 847 closedir(dp); 848 849 status = asl_file_list_match_timeout(files, query, res, last_id, start_id, count, direction, usec); 850 asl_file_list_close(files); 851 return status; 852} 853 854uint32_t 855asl_store_match(asl_store_t *s, aslresponse query, aslresponse *res, uint64_t *last_id, uint64_t start_id, uint32_t count, int32_t direction) 856{ 857 return asl_store_match_timeout(s, query, res, last_id, start_id, count, direction, 0); 858} 859 860uint32_t 861asl_store_match_start(asl_store_t *s, uint64_t start_id, int32_t direction) 862{ 863 DIR *dp; 864 struct dirent *dent; 865 uint32_t status; 866 asl_file_t *f; 867 char *path; 868 asl_file_list_t *files; 869 870 if (s == NULL) return ASL_STATUS_INVALID_STORE; 871 872 if (s->work != NULL) asl_file_list_match_end(s->work); 873 s->work = NULL; 874 875 files = NULL; 876 877 /* 878 * Open all readable files 879 */ 880 dp = opendir(s->base_dir); 881 if (dp == NULL) return ASL_STATUS_READ_FAILED; 882 883 while ((dent = readdir(dp)) != NULL) 884 { 885 if (dent->d_name[0] == '.') continue; 886 887 path = NULL; 888 asprintf(&path, "%s/%s", s->base_dir, dent->d_name); 889 890 /* 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 */ 891 status = asl_file_open_read(path, &f); 892 if (path != NULL) free(path); 893 if ((status != ASL_STATUS_OK) || (f == NULL)) continue; 894 895 files = asl_file_list_add(files, f); 896 } 897 898 closedir(dp); 899 900 s->work = asl_file_list_match_start(files, start_id, direction); 901 if (s->work == NULL) return ASL_STATUS_FAILED; 902 903 return ASL_STATUS_OK; 904} 905 906uint32_t 907asl_store_match_next(asl_store_t *s, aslresponse query, aslresponse *res, uint32_t count) 908{ 909 if (s == NULL) return ASL_STATUS_INVALID_STORE; 910 if (s->work == NULL) return ASL_STATUS_OK; 911 912 return asl_file_list_match_next(s->work, query, res, count); 913} 914 915#if TARGET_IPHONE_SIMULATOR 916const char *_path_asl_store(void) { 917 static char * path; 918 static dispatch_once_t once; 919 920 dispatch_once(&once, ^{ 921 char *sim_log_dir = getenv("IPHONE_SIMULATOR_LOG_ROOT"); 922 assert(sim_log_dir); 923 924 asprintf(&path, "%s/asl", sim_log_dir); 925 assert(path); 926 }); 927 928 return path; 929} 930 931const char *_path_asl_archive(void) { 932 static char * path; 933 static dispatch_once_t once; 934 935 dispatch_once(&once, ^{ 936 char *sim_log_dir = getenv("IPHONE_SIMULATOR_LOG_ROOT"); 937 assert(sim_log_dir); 938 939 asprintf(&path, "%s/asl.archive", sim_log_dir); 940 assert(path); 941 }); 942 943 return path; 944} 945#endif 946