1/* 2 * Copyright (c) 2007-2009 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 <stdio.h> 25#include <dirent.h> 26#include <string.h> 27#include <stdlib.h> 28#include <unistd.h> 29#include <stdint.h> 30#include <errno.h> 31#include <time.h> 32#include <sys/time.h> 33#include <sys/stat.h> 34#include <sys/param.h> 35#include <servers/bootstrap.h> 36#include <bootstrap_priv.h> 37#include <mach/mach.h> 38#include <copyfile.h> 39#include <fcntl.h> 40#include <zlib.h> 41#include <xpc/xpc.h> 42#include <xpc/private.h> 43#include <os/assumes.h> 44#include <vproc_priv.h> 45#include <asl.h> 46#include <asl_private.h> 47#include <asl_core.h> 48#include <asl_file.h> 49#include <asl_store.h> 50#include "asl_common.h" 51 52#define DEFAULT_MAX_SIZE 150000000 53#define IOBUFSIZE 4096 54 55#define DO_ASLDB 0x00000001 56#define DO_MODULE 0x00000002 57#define DO_CHECKPT 0x00000004 58 59#define DEBUG_FLAG_MASK 0xfffffff0 60#define DEBUG_LEVEL_MASK 0x0000000f 61#define DEBUG_STDERR 0x00000010 62#define DEBUG_ASL 0x00000020 63 64extern kern_return_t _asl_server_query 65( 66 mach_port_t server, 67 caddr_t request, 68 mach_msg_type_number_t requestCnt, 69 uint64_t startid, 70 int count, 71 int flags, 72 caddr_t *reply, 73 mach_msg_type_number_t *replyCnt, 74 uint64_t *lastid, 75 int *status, 76 security_token_t *token 77); 78 79/* global */ 80static time_t module_ttl; 81static uint32_t debug; 82static int dryrun; 83static int asl_aux_fd = -1; 84static aslclient aslc; 85static mach_port_t asl_server_port; 86static xpc_connection_t listener; 87static dispatch_queue_t serverq; 88 89typedef struct name_list_s 90{ 91 char *name; 92 size_t size; 93 struct name_list_s *next; 94} name_list_t; 95 96void 97set_debug(int flag, const char *str) 98{ 99 int level, x; 100 101 if (str == NULL) x = ASL_LEVEL_ERR; 102 else if (((str[0] == 'L') || (str[0] == 'l')) && ((str[1] >= '0') && (str[1] <= '7')) && (str[2] == '\0')) x = atoi(str+1); 103 else if ((str[0] >= '0') && (str[0] <= '7') && (str[1] == '\0')) x = ASL_LEVEL_CRIT + atoi(str); 104 else x = ASL_LEVEL_ERR; 105 106 if (x <= 0) x = 0; 107 else if (x > 7) x = 7; 108 109 level = debug & DEBUG_LEVEL_MASK; 110 if (x > level) level = x; 111 112 debug = debug & DEBUG_FLAG_MASK; 113 debug |= flag; 114 debug |= level; 115} 116 117void 118debug_log(int level, const char *str, ...) 119{ 120 va_list v; 121 122 if ((debug & DEBUG_STDERR) && (level <= (debug & DEBUG_LEVEL_MASK))) 123 { 124 va_start(v, str); 125 vfprintf(stderr, str, v); 126 va_end(v); 127 } 128 129 if (debug & DEBUG_ASL) 130 { 131 char *line = NULL; 132 133 if (aslc == NULL) 134 { 135 aslc = asl_open("aslmanager", "syslog", 0); 136 aslmsg msg = asl_new(ASL_TYPE_MSG); 137 138 asl_set(msg, ASL_KEY_MSG, "Status Report"); 139 asl_set(msg, ASL_KEY_LEVEL, ASL_STRING_NOTICE); 140 asl_create_auxiliary_file(msg, "Status Report", "public.text", &asl_aux_fd); 141 asl_free(msg); 142 } 143 144 va_start(v, str); 145 vasprintf(&line, str, v); 146 va_end(v); 147 148 if (line != NULL) write(asl_aux_fd, line, strlen(line)); 149 free(line); 150 } 151} 152 153__attribute__((noreturn)) static void 154xpc_server_exit(int status) 155{ 156 xpc_connection_cancel(listener); 157 xpc_release(listener); 158 dispatch_release(serverq); 159 exit(status); 160} 161 162name_list_t * 163add_to_name_list(name_list_t *l, const char *name, size_t size) 164{ 165 name_list_t *e, *x; 166 167 if (name == NULL) return l; 168 169 e = (name_list_t *)calloc(1, sizeof(name_list_t)); 170 if (e == NULL) return NULL; 171 172 e->name = strdup(name); 173 if (e->name == NULL) 174 { 175 free(e); 176 return NULL; 177 } 178 179 e->size = size; 180 181 /* list is sorted by name (i.e. primarily by timestamp) */ 182 if (l == NULL) return e; 183 184 if (strcmp(e->name, l->name) <= 0) 185 { 186 e->next = l; 187 return e; 188 } 189 190 for (x = l; (x->next != NULL) && (strcmp(e->name, x->next->name) > 0) ; x = x->next); 191 192 e->next = x->next; 193 x->next = e; 194 return l; 195} 196 197void 198free_name_list(name_list_t *l) 199{ 200 name_list_t *e; 201 202 while (l != NULL) 203 { 204 e = l; 205 l = l->next; 206 free(e->name); 207 free(e); 208 } 209 210 free(l); 211} 212 213/* 214 * Copy ASL files by reading and writing each record. 215 * Setting ASL_FILE_FLAG_UNLIMITED_CACHE when copying optimizes tring uniquing. 216 */ 217uint32_t 218copy_asl_file(const char *src, const char *dst, mode_t mode) 219{ 220 asl_search_result_t *res; 221 asl_file_t *f; 222 uint32_t status, i; 223 uint64_t mid; 224 225 if (src == NULL) return ASL_STATUS_INVALID_ARG; 226 if (dst == NULL) return ASL_STATUS_INVALID_ARG; 227 228 f = NULL; 229 status = asl_file_open_read(src, &f); 230 if (status != ASL_STATUS_OK) return status; 231 232 res = NULL; 233 mid = 0; 234 235 status = asl_file_match(f, NULL, &res, &mid, 0, 0, 1); 236 asl_file_close(f); 237 238 if (status != ASL_STATUS_OK) return status; 239 if (res == NULL) return ASL_STATUS_OK; 240 if (res->count == 0) 241 { 242 aslresponse_free(res); 243 return ASL_STATUS_OK; 244 } 245 246 f = NULL; 247 status = asl_file_open_write(dst, mode, -1, -1, &f); 248 if (status != ASL_STATUS_OK) return status; 249 if (f == ASL_STATUS_OK) return ASL_STATUS_FAILED; 250 251 f->flags = ASL_FILE_FLAG_UNLIMITED_CACHE | ASL_FILE_FLAG_PRESERVE_MSG_ID; 252 253 for (i = 0; i < res->count; i++) 254 { 255 mid = 0; 256 status = asl_file_save(f, (aslmsg)(res->msg[i]), &mid); 257 if (status != ASL_STATUS_OK) break; 258 } 259 260 asl_file_close(f); 261 return status; 262} 263 264int 265copy_compress_file(asl_out_dst_data_t *asldst, const char *src, const char *dst) 266{ 267 int in, out; 268 size_t n; 269 gzFile gz; 270 char buf[IOBUFSIZE]; 271 272 in = open(src, O_RDONLY, 0); 273 if (in < 0) return -1; 274 275 out = open(dst, O_WRONLY | O_CREAT, asldst->mode); 276 if (out >= 0) out = asl_out_dst_set_access(out, asldst); 277 if (out < 0) 278 { 279 close(in); 280 return -1; 281 } 282 283 gz = gzdopen(out, "w"); 284 if (gz == NULL) 285 { 286 close(in); 287 close(out); 288 return -1; 289 } 290 291 do { 292 n = read(in, buf, sizeof(buf)); 293 if (n > 0) gzwrite(gz, buf, n); 294 } while (n == IOBUFSIZE); 295 296 gzclose(gz); 297 close(in); 298 close(out); 299 300 return 0; 301} 302 303int32_t 304filesystem_copy(asl_out_dst_data_t *asldst, const char *src, const char *dst, uint32_t flags) 305{ 306 char *dot; 307 308 if ((src == NULL) || (dst == NULL)) return 0; 309 310 dot = strrchr(src, '.'); 311 if ((dot != NULL) && (!strcmp(dot, ".gz"))) flags &= ~MODULE_FLAG_COMPRESS; 312 313 if (((flags & MODULE_FLAG_COMPRESS) == 0) && (!strcmp(src, dst))) return 0; 314 315 if (flags & MODULE_FLAG_TYPE_ASL) debug_log(ASL_LEVEL_NOTICE, " copy asl %s ---> %s\n", src, dst); 316 else if (flags & MODULE_FLAG_COMPRESS) debug_log(ASL_LEVEL_NOTICE, " copy compress %s ---> %s.gz\n", src, dst); 317 else debug_log(ASL_LEVEL_NOTICE, " copy %s ---> %s\n", src, dst); 318 319 if (dryrun == 1) return 0; 320 321 if (flags & MODULE_FLAG_TYPE_ASL) 322 { 323 uint32_t status = copy_asl_file(src, dst, asldst->mode); 324 if (status != 0) 325 { 326 debug_log(ASL_LEVEL_ERR, " FAILED status %u [%s] asl copy %s ---> %s\n", status, asl_core_error(status), src, dst); 327 return 0; 328 } 329 } 330 else if (flags & MODULE_FLAG_COMPRESS) 331 { 332 char gzdst[MAXPATHLEN]; 333 334 snprintf(gzdst, sizeof(gzdst), "%s.gz", dst); 335 336 int status = copy_compress_file(asldst, src, gzdst); 337 if (status != 0) 338 { 339 debug_log(ASL_LEVEL_ERR, " FAILED status %d errno %d [%s] copy & compress %s ---> %s\n", status, errno, strerror(errno), src, dst); 340 return 0; 341 } 342 } 343 else 344 { 345 int status = copyfile(src, dst, NULL, COPYFILE_ALL | COPYFILE_RECURSIVE); 346 if (status != 0) 347 { 348 debug_log(ASL_LEVEL_ERR, " FAILED status %d errno %d [%s] copy %s ---> %s\n", status, errno, strerror(errno), src, dst); 349 return 0; 350 } 351 } 352 353 return 1; 354} 355 356void 357filesystem_rename(const char *src, const char *dst) 358{ 359 int status = 0; 360 361 debug_log(ASL_LEVEL_NOTICE, " rename %s ---> %s\n", src, dst); 362 if (dryrun == 1) return; 363 364 status = rename(src, dst); 365 if (status != 0) debug_log(ASL_LEVEL_ERR, " FAILED status %d errno %d [%s] rename %s ---> %s\n", status, errno, strerror(errno), src, dst); 366} 367 368void 369filesystem_unlink(const char *path) 370{ 371 int status = 0; 372 373 debug_log(ASL_LEVEL_NOTICE, " remove %s\n", path); 374 if (dryrun == 1) return; 375 376 status = unlink(path); 377 if (status != 0) debug_log(ASL_LEVEL_ERR, " FAILED status %d errno %d [%s] unlink %s\n", status, errno, strerror(errno), path); 378} 379 380void 381filesystem_rmdir(const char *path) 382{ 383 int status = 0; 384 385 debug_log(ASL_LEVEL_NOTICE, " remove directory %s\n", path); 386 if (dryrun == 1) return; 387 388 status = rmdir(path); 389 if (status != 0) debug_log(ASL_LEVEL_ERR, " FAILED status %d errno %d [%s] rmdir %s\n", status, errno, strerror(errno), path); 390} 391 392int 393remove_directory(const char *path) 394{ 395 DIR *dp; 396 struct dirent *dent; 397 char *str; 398 399 dp = opendir(path); 400 if (dp == NULL) return 0; 401 402 while ((dent = readdir(dp)) != NULL) 403 { 404 if ((!strcmp(dent->d_name, ".")) || (!strcmp(dent->d_name, ".."))) continue; 405 asprintf(&str, "%s/%s", path, dent->d_name); 406 if (str != NULL) 407 { 408 filesystem_unlink(str); 409 free(str); 410 str = NULL; 411 } 412 } 413 414 closedir(dp); 415 filesystem_rmdir(path); 416 417 return 0; 418} 419 420/* 421 * Used to set config parameters. 422 * Line format "= name value" 423 */ 424static void 425_aslmanager_set_param(asl_out_dst_data_t *dst, char *s) 426{ 427 char **l; 428 uint32_t count; 429 430 if (s == NULL) return; 431 if (s[0] == '\0') return; 432 433 /* skip '=' and whitespace */ 434 if (*s == '=') s++; 435 while ((*s == ' ') || (*s == '\t')) s++; 436 437 l = explode(s, " \t"); 438 if (l == NULL) return; 439 440 for (count = 0; l[count] != NULL; count++); 441 442 /* name is required */ 443 if (count == 0) 444 { 445 free_string_list(l); 446 return; 447 } 448 449 /* value is required */ 450 if (count == 1) 451 { 452 free_string_list(l); 453 return; 454 } 455 456 if (!strcasecmp(l[0], "aslmanager_debug")) 457 { 458 /* = debug level */ 459 set_debug(DEBUG_ASL, l[1]); 460 } 461 else if (!strcasecmp(l[0], "store_ttl")) 462 { 463 /* = store_ttl days */ 464 dst->ttl = (time_t)atoll(l[1]); 465 } 466 else if (!strcasecmp(l[0], "module_ttl")) 467 { 468 /* = module_ttl days */ 469 module_ttl = (time_t)atoll(l[1]); 470 } 471 else if (!strcasecmp(l[0], "max_store_size")) 472 { 473 /* = max_file_size bytes */ 474 dst->all_max = atoi(l[1]); 475 } 476 else if (!strcasecmp(l[0], "archive")) 477 { 478 free(dst->rotate_dir); 479 dst->rotate_dir = NULL; 480 481 /* = archive {0|1} path */ 482 if (!strcmp(l[1], "1")) 483 { 484 if (l[2] == NULL) dst->rotate_dir = strdup(PATH_ASL_ARCHIVE); 485 else dst->rotate_dir = strdup(l[2]); 486 } 487 } 488 else if (!strcasecmp(l[0], "store_path")) 489 { 490 /* = archive path */ 491 free(dst->path); 492 dst->path = strdup(l[1]); 493 } 494 else if (!strcasecmp(l[0], "archive_mode")) 495 { 496 dst->mode = strtol(l[1], NULL, 0); 497 if ((dst->mode == 0) && (errno == EINVAL)) dst->mode = 0400; 498 } 499 500 free_string_list(l); 501} 502 503size_t 504directory_size(const char *path) 505{ 506 DIR *dp; 507 struct dirent *dent; 508 struct stat sb; 509 size_t size; 510 char *str; 511 512 dp = opendir(path); 513 if (dp == NULL) return 0; 514 515 size = 0; 516 while ((dent = readdir(dp)) != NULL) 517 { 518 if ((!strcmp(dent->d_name, ".")) || (!strcmp(dent->d_name, ".."))) continue; 519 520 memset(&sb, 0, sizeof(struct stat)); 521 str = NULL; 522 asprintf(&str, "%s/%s", path, dent->d_name); 523 524 if ((str != NULL) && (stat(str, &sb) == 0) && S_ISREG(sb.st_mode)) 525 { 526 size += sb.st_size; 527 free(str); 528 } 529 } 530 531 closedir(dp); 532 return size; 533} 534 535static int 536process_asl_data_store(asl_out_dst_data_t *dst) 537{ 538 int32_t today_ymd_stringlen, expire_ymd_stringlen; 539 time_t now, ttl, ymd_expire; 540 struct tm ctm; 541 char today_ymd_string[32], expire_ymd_string[32], *str; 542 DIR *dp; 543 struct dirent *dent; 544 name_list_t *ymd_list, *bb_list, *aux_list, *bb_aux_list, *e; 545 size_t file_size, store_size; 546 struct stat sb; 547 548 ymd_list = NULL; 549 bb_list = NULL; 550 aux_list = NULL; 551 bb_aux_list = NULL; 552 store_size = 0; 553 554 if (dst == NULL) return 0; 555 if (dst->path == NULL) return 0; 556 557 debug_log(ASL_LEVEL_NOTICE, "----------------------------------------\n"); 558 debug_log(ASL_LEVEL_NOTICE, "Processing data store %s\n", dst->path); 559 560 if (dst->rotate_dir != NULL) 561 { 562 /* check archive */ 563 memset(&sb, 0, sizeof(struct stat)); 564 if (stat(dst->rotate_dir, &sb) == 0) 565 { 566 /* must be a directory */ 567 if (!S_ISDIR(sb.st_mode)) 568 { 569 debug_log(ASL_LEVEL_ERR, "aslmanager error: archive %s is not a directory", dst->rotate_dir); 570 return -1; 571 } 572 } 573 else 574 { 575 if (errno == ENOENT) 576 { 577 /* archive doesn't exist - create it */ 578 if (mkdir(dst->rotate_dir, 0755) != 0) 579 { 580 debug_log(ASL_LEVEL_ERR, "aslmanager error: can't create archive %s: %s\n", dst->rotate_dir, strerror(errno)); 581 return -1; 582 } 583 } 584 else 585 { 586 /* stat failed for some other reason */ 587 debug_log(ASL_LEVEL_ERR, "aslmanager error: can't stat archive %s: %s\n", dst->rotate_dir, strerror(errno)); 588 return -1; 589 } 590 } 591 } 592 593 chdir(dst->path); 594 595 /* determine current time */ 596 now = time(NULL); 597 598 /* ttl 0 means files never expire */ 599 ymd_expire = 0; 600 ttl = dst->ttl * SECONDS_PER_DAY; 601 602 if ((ttl > 0) && (ttl <= now)) ymd_expire = now - ttl; 603 604 /* construct today's date as YYYY.MM.DD */ 605 memset(&ctm, 0, sizeof(struct tm)); 606 if (localtime_r((const time_t *)&now, &ctm) == NULL) return -1; 607 608 snprintf(today_ymd_string, sizeof(today_ymd_string), "%d.%02d.%02d.", ctm.tm_year + 1900, ctm.tm_mon + 1, ctm.tm_mday); 609 today_ymd_stringlen = strlen(today_ymd_string); 610 611 /* construct regular file expiry date as YYYY.MM.DD */ 612 memset(&ctm, 0, sizeof(struct tm)); 613 if (localtime_r((const time_t *)&ymd_expire, &ctm) == NULL) return -1; 614 615 snprintf(expire_ymd_string, sizeof(expire_ymd_string), "%d.%02d.%02d.", ctm.tm_year + 1900, ctm.tm_mon + 1, ctm.tm_mday); 616 expire_ymd_stringlen = strlen(expire_ymd_string); 617 618 debug_log(ASL_LEVEL_NOTICE, "Expiry Date %s\n", expire_ymd_string); 619 620 dp = opendir(dst->path); 621 if (dp == NULL) return -1; 622 623 /* gather a list of YMD files, AUX dirs, BB.AUX dirs, and BB files */ 624 while ((dent = readdir(dp)) != NULL) 625 { 626 memset(&sb, 0, sizeof(struct stat)); 627 file_size = 0; 628 if (stat(dent->d_name, &sb) == 0) file_size = sb.st_size; 629 630 if ((dent->d_name[0] >= '0') && (dent->d_name[0] <= '9')) 631 { 632 ymd_list = add_to_name_list(ymd_list, dent->d_name, file_size); 633 store_size += file_size; 634 } 635 else if (!strncmp(dent->d_name, "AUX.", 4) && (dent->d_name[4] >= '0') && (dent->d_name[4] <= '9') && S_ISDIR(sb.st_mode)) 636 { 637 file_size = directory_size(dent->d_name); 638 aux_list = add_to_name_list(aux_list, dent->d_name, file_size); 639 store_size += file_size; 640 } 641 else if (!strncmp(dent->d_name, "BB.AUX.", 7) && (dent->d_name[7] >= '0') && (dent->d_name[7] <= '9') && S_ISDIR(sb.st_mode)) 642 { 643 file_size = directory_size(dent->d_name); 644 bb_aux_list = add_to_name_list(bb_aux_list, dent->d_name, file_size); 645 store_size += file_size; 646 } 647 else if (!strncmp(dent->d_name, "BB.", 3) && (dent->d_name[3] >= '0') && (dent->d_name[3] <= '9')) 648 { 649 bb_list = add_to_name_list(bb_list, dent->d_name, file_size); 650 store_size += file_size; 651 } 652 else if ((!strcmp(dent->d_name, ".")) || (!strcmp(dent->d_name, ".."))) 653 {} 654 else if ((!strcmp(dent->d_name, "StoreData")) || (!strcmp(dent->d_name, "SweepStore"))) 655 {} 656 else 657 { 658 debug_log(ASL_LEVEL_ERR, "aslmanager: unexpected file %s in ASL data store\n", dent->d_name); 659 } 660 } 661 662 closedir(dp); 663 664 debug_log(ASL_LEVEL_NOTICE, "Data Store Size = %lu\n", store_size); 665 debug_log(ASL_LEVEL_NOTICE, "Data Store YMD Files\n"); 666 for (e = ymd_list; e != NULL; e = e->next) debug_log(ASL_LEVEL_NOTICE, " %s %lu\n", e->name, e->size); 667 debug_log(ASL_LEVEL_NOTICE, "Data Store AUX Directories\n"); 668 for (e = aux_list; e != NULL; e = e->next) debug_log(ASL_LEVEL_NOTICE, " %s %lu\n", e->name, e->size); 669 debug_log(ASL_LEVEL_NOTICE, "Data Store BB.AUX Directories\n"); 670 for (e = bb_aux_list; e != NULL; e = e->next) debug_log(ASL_LEVEL_NOTICE, " %s %lu\n", e->name, e->size); 671 debug_log(ASL_LEVEL_NOTICE, "Data Store BB Files\n"); 672 for (e = bb_list; e != NULL; e = e->next) debug_log(ASL_LEVEL_NOTICE, " %s %lu\n", e->name, e->size); 673 674 /* Delete/achive expired YMD files */ 675 debug_log(ASL_LEVEL_NOTICE, "Start YMD File Scan\n"); 676 677 e = ymd_list; 678 while (e != NULL) 679 { 680 /* stop when a file name/date is after the expire date */ 681 if (strncmp(e->name, expire_ymd_string, expire_ymd_stringlen) > 0) break; 682 683 if (dst->rotate_dir != NULL) 684 { 685 str = NULL; 686 asprintf(&str, "%s/%s", dst->rotate_dir, e->name); 687 if (str == NULL) return -1; 688 689 filesystem_copy(dst, e->name, str, 0); 690 free(str); 691 } 692 693 filesystem_unlink(e->name); 694 store_size -= e->size; 695 e->size = 0; 696 697 e = e->next; 698 } 699 700 debug_log(ASL_LEVEL_NOTICE, "Finished YMD File Scan\n"); 701 702 /* Delete/achive expired YMD AUX directories */ 703 debug_log(ASL_LEVEL_NOTICE, "Start AUX Directory Scan\n"); 704 705 e = aux_list; 706 while (e != NULL) 707 { 708 /* stop when a file name/date is after the expire date */ 709 if (strncmp(e->name + 4, expire_ymd_string, expire_ymd_stringlen) > 0) break; 710 711 if (dst->rotate_dir != NULL) 712 { 713 str = NULL; 714 asprintf(&str, "%s/%s", dst->rotate_dir, e->name); 715 if (str == NULL) return -1; 716 717 filesystem_copy(dst, e->name, str, 0); 718 free(str); 719 } 720 721 remove_directory(e->name); 722 store_size -= e->size; 723 e->size = 0; 724 725 e = e->next; 726 } 727 728 debug_log(ASL_LEVEL_NOTICE, "Finished AUX Directory Scan\n"); 729 730 /* Delete/achive expired BB.AUX directories */ 731 debug_log(ASL_LEVEL_NOTICE, "Start BB.AUX Directory Scan\n"); 732 733 e = bb_aux_list; 734 while (e != NULL) 735 { 736 /* stop when a file name/date is after the expire date */ 737 if (strncmp(e->name + 7, today_ymd_string, today_ymd_stringlen) > 0) break; 738 739 if (dst->rotate_dir != NULL) 740 { 741 str = NULL; 742 asprintf(&str, "%s/%s", dst->rotate_dir, e->name); 743 if (str == NULL) return -1; 744 745 filesystem_copy(dst, e->name, str, 0); 746 free(str); 747 } 748 749 remove_directory(e->name); 750 store_size -= e->size; 751 e->size = 0; 752 753 e = e->next; 754 } 755 756 debug_log(ASL_LEVEL_NOTICE, "Finished BB.AUX Directory Scan\n"); 757 758 /* Delete/achive expired BB files */ 759 debug_log(ASL_LEVEL_NOTICE, "Start BB Scan\n"); 760 761 e = bb_list; 762 while (e != NULL) 763 { 764 /* stop when a file name/date is after the expire date */ 765 if (strncmp(e->name + 3, today_ymd_string, today_ymd_stringlen) > 0) break; 766 767 if (dst->rotate_dir != NULL) 768 { 769 str = NULL; 770 asprintf(&str, "%s/%s", dst->rotate_dir, e->name); 771 if (str == NULL) return -1; 772 773 /* syslog -x [str] -f [e->name] */ 774 filesystem_copy(dst, e->name, str, 0); 775 free(str); 776 } 777 778 filesystem_unlink(e->name); 779 store_size -= e->size; 780 e->size = 0; 781 782 e = e->next; 783 } 784 785 debug_log(ASL_LEVEL_NOTICE, "Finished BB Scan\n"); 786 787 if (dst->all_max > 0) 788 { 789 /* if data store is over max_size, delete/archive more YMD files */ 790 if (store_size > dst->all_max) debug_log(ASL_LEVEL_NOTICE, "Additional YMD Scan\n"); 791 792 e = ymd_list; 793 while ((e != NULL) && (store_size > dst->all_max)) 794 { 795 if (e->size != 0) 796 { 797 /* stop when we get to today's files */ 798 if (strncmp(e->name, today_ymd_string, today_ymd_stringlen) == 0) break; 799 800 if (dst->rotate_dir != NULL) 801 { 802 str = NULL; 803 asprintf(&str, "%s/%s", dst->rotate_dir, e->name); 804 if (str == NULL) return -1; 805 806 /* syslog -x [str] -f [e->name] */ 807 filesystem_copy(dst, e->name, str, 0); 808 free(str); 809 } 810 811 filesystem_unlink(e->name); 812 store_size -= e->size; 813 e->size = 0; 814 } 815 816 e = e->next; 817 } 818 819 /* if data store is over dst->all_max, delete/archive more BB files */ 820 if (store_size > dst->all_max) debug_log(ASL_LEVEL_NOTICE, "Additional BB Scan\n"); 821 822 e = bb_list; 823 while ((e != NULL) && (store_size > dst->all_max)) 824 { 825 if (e->size != 0) 826 { 827 if (dst->rotate_dir != NULL) 828 { 829 str = NULL; 830 asprintf(&str, "%s/%s", dst->rotate_dir, e->name); 831 if (str == NULL) return -1; 832 833 /* syslog -x [str] -f [e->name] */ 834 filesystem_copy(dst, e->name, str, 0); 835 free(str); 836 } 837 838 filesystem_unlink(e->name); 839 store_size -= e->size; 840 e->size = 0; 841 } 842 843 e = e->next; 844 } 845 } 846 847 free_name_list(ymd_list); 848 free_name_list(bb_list); 849 free_name_list(aux_list); 850 free_name_list(bb_aux_list); 851 852 debug_log(ASL_LEVEL_NOTICE, "Data Store Size = %lu\n", store_size); 853 854 return 0; 855} 856 857/* move sequenced source files to dst dir, renaming as we go */ 858static int 859module_copy_rename(asl_out_dst_data_t *dst) 860{ 861 asl_out_file_list_t *src_list, *dst_list, *f, *dst_last; 862 char *base, *dst_dir; 863 char fpathsrc[MAXPATHLEN], fpathdst[MAXPATHLEN]; 864 uint32_t src_count, dst_count; 865 int32_t x, moved; 866 867 if (dst == NULL) return -1; 868 if (dst->path == NULL) return -1; 869 870 base = strrchr(dst->path, '/'); 871 if (base == NULL) return -1; 872 873 src_list = asl_list_src_files(dst); 874 if (src_list == 0) 875 { 876 debug_log(ASL_LEVEL_INFO, " no src files\n"); 877 return 0; 878 } 879 880 debug_log(ASL_LEVEL_INFO, " src files\n"); 881 882 src_count = 0; 883 for (f = src_list; f != NULL; f = f->next) 884 { 885 debug_log(ASL_LEVEL_INFO, " %s\n", f->name); 886 src_count++; 887 } 888 889 dst_list = asl_list_dst_files(dst); 890 891 *base = '\0'; 892 base++; 893 894 dst_dir = dst->rotate_dir; 895 if (dst_dir == NULL) dst_dir = dst->path; 896 897 dst_count = 0; 898 dst_last = dst_list; 899 900 if (dst_list == NULL) debug_log(ASL_LEVEL_INFO, " no dst files\n"); 901 else debug_log(ASL_LEVEL_INFO, " dst files\n"); 902 903 for (f = dst_list; f != NULL; f = f->next) 904 { 905 debug_log(ASL_LEVEL_INFO, " %s\n", f->name); 906 dst_last = f; 907 dst_count++; 908 } 909 910 if (dst->flags & MODULE_FLAG_STYLE_SEQ) 911 { 912 for (f = dst_last; f != NULL; f = f->prev) 913 { 914 int is_gz = 0; 915 char *dot = strrchr(f->name, '.'); 916 if ((dot != NULL) && (!strcmp(dot, ".gz"))) is_gz = 1; 917 918 snprintf(fpathsrc, sizeof(fpathsrc), "%s/%s", dst_dir, f->name); 919 snprintf(fpathdst, sizeof(fpathdst), "%s/%s.%d%s", dst_dir, base, f->seq+src_count, (is_gz == 1) ? ".gz" : ""); 920 filesystem_rename(fpathsrc, fpathdst); 921 } 922 923 for (f = src_list, x = 0; f != NULL; f = f->next, x++) 924 { 925 snprintf(fpathsrc, sizeof(fpathsrc), "%s/%s", dst->path, f->name); 926 snprintf(fpathdst, sizeof(fpathdst), "%s/%s.%d", dst_dir, base, x); 927 moved = filesystem_copy(dst, fpathsrc, fpathdst, dst->flags); 928 if (moved != 0) filesystem_unlink(fpathsrc); 929 } 930 } 931 else 932 { 933 for (f = src_list; f != NULL; f = f->next) 934 { 935 /* final / active base stamped file looks like a checkpointed file - ignore it */ 936 if ((dst->flags & MODULE_FLAG_BASESTAMP) && (f->next == NULL)) break; 937 938 snprintf(fpathsrc, sizeof(fpathsrc), "%s/%s", dst->path, f->name); 939 940 /* MODULE_FLAG_EXTERNAL files are not decorated with a timestamp */ 941 if (dst->flags & MODULE_FLAG_EXTERNAL) 942 { 943 char tstamp[32]; 944 945 asl_make_timestamp(f->ftime, dst->flags, tstamp, sizeof(tstamp)); 946 snprintf(fpathdst, sizeof(fpathdst), "%s/%s.%s", dst_dir, base, tstamp); 947 } 948 else 949 { 950 snprintf(fpathdst, sizeof(fpathdst), "%s/%s", dst_dir, f->name); 951 } 952 953 moved = filesystem_copy(dst, fpathsrc, fpathdst, dst->flags); 954 if (moved != 0) filesystem_unlink(fpathsrc); 955 } 956 } 957 958 asl_out_file_list_free(src_list); 959 asl_out_file_list_free(dst_list); 960 961 if (base != NULL) *--base = '/'; 962 963 return 0; 964} 965 966/* delete expired files */ 967static int 968module_expire(asl_out_dst_data_t *dst) 969{ 970 asl_out_file_list_t *dst_list, *f; 971 char *base, *dst_dir, fpath[MAXPATHLEN]; 972 time_t now, ttl, cutoff; 973 974 if (dst == NULL) return -1; 975 if (dst->path == NULL) return -1; 976 if (dst->ttl == 0) return 0; 977 978 ttl = 0; 979 if (module_ttl > 0) ttl = module_ttl; 980 else ttl = dst->ttl; 981 982 ttl *= SECONDS_PER_DAY; 983 984 now = time(NULL); 985 if (ttl > now) return 0; 986 987 cutoff = now - ttl; 988 989 base = strrchr(dst->path, '/'); 990 if (base == NULL) return -1; 991 992 dst_list = asl_list_dst_files(dst); 993 994 *base = '\0'; 995 996 dst_dir = dst->rotate_dir; 997 if (dst_dir == NULL) dst_dir = dst->path; 998 999 if (dst_list == NULL) 1000 { 1001 debug_log(ASL_LEVEL_INFO, " no dst files\n"); 1002 } 1003 else 1004 { 1005 debug_log(ASL_LEVEL_INFO, " dst files\n"); 1006 for (f = dst_list; f != NULL; f = f->next) debug_log(ASL_LEVEL_INFO, " %s\n", f->name); 1007 } 1008 1009 for (f = dst_list; f != NULL; f = f->next) 1010 { 1011 if (f->ftime <= cutoff) 1012 { 1013 snprintf(fpath, sizeof(fpath), "%s/%s", dst_dir, f->name); 1014 filesystem_unlink(fpath); 1015 } 1016 } 1017 1018 asl_out_file_list_free(dst_list); 1019 1020 if (base != NULL) *base = '/'; 1021 1022 return 0; 1023} 1024 1025/* check all_max size and delete files (oldest first) to stay within size limit */ 1026static int 1027module_check_size(asl_out_dst_data_t *dst) 1028{ 1029 asl_out_file_list_t *dst_list, *f; 1030 char *base, *dst_dir, fpath[MAXPATHLEN]; 1031 size_t total; 1032 1033 if (dst == NULL) return -1; 1034 if (dst->path == NULL) return -1; 1035 1036 if (dst->all_max == 0) return 0; 1037 1038 base = NULL; 1039 dst_dir = dst->rotate_dir; 1040 if (dst_dir == NULL) 1041 { 1042 dst_dir = dst->path; 1043 base = strrchr(dst->path, '/'); 1044 if (base == NULL) return -1; 1045 *base = '\0'; 1046 } 1047 1048 dst_list = asl_list_dst_files(dst); 1049 1050 if (dst_list == NULL) 1051 { 1052 debug_log(ASL_LEVEL_INFO, " no dst files\n"); 1053 } 1054 else 1055 { 1056 debug_log(ASL_LEVEL_INFO, " dst files\n"); 1057 for (f = dst_list; f != NULL; f = f->next) debug_log(ASL_LEVEL_INFO, " %s size %lu\n", f->name, f->size); 1058 } 1059 1060 total = 0; 1061 for (f = dst_list; f != NULL; f = f->next) total += f->size; 1062 1063 for (f = dst_list; (total > dst->all_max) && (f != NULL); f = f->next) 1064 { 1065 snprintf(fpath, sizeof(fpath), "%s/%s", dst_dir, f->name); 1066 filesystem_unlink(fpath); 1067 total -= f->size; 1068 } 1069 1070 asl_out_file_list_free(dst_list); 1071 1072 if (base != NULL) *base = '/'; 1073 1074 return 0; 1075} 1076 1077 1078static int 1079process_module(asl_out_module_t *mod) 1080{ 1081 asl_out_rule_t *r; 1082 1083 if (mod == NULL) return -1; 1084 1085 debug_log(ASL_LEVEL_NOTICE, "----------------------------------------\n"); 1086 debug_log(ASL_LEVEL_NOTICE, "Processing module %s\n", (mod->name == NULL) ? "asl.conf" : mod->name); 1087 1088 for (r = mod->ruleset; r != NULL; r = r->next) 1089 { 1090 if (r->action == ACTION_OUT_DEST) 1091 { 1092 if (r->dst == NULL) 1093 { 1094 debug_log(ASL_LEVEL_NOTICE, "NULL dst data for output rule - skipped\n"); 1095 } 1096 else if (r->dst->flags & MODULE_FLAG_ROTATE) 1097 { 1098 debug_log(ASL_LEVEL_NOTICE, "Checking file %s\n", r->dst->path); 1099 debug_log(ASL_LEVEL_NOTICE, "- Rename, move to destination directory, and compress as required\n"); 1100 1101 module_copy_rename(r->dst); 1102 1103 if (r->dst->ttl > 0) 1104 { 1105 debug_log(ASL_LEVEL_NOTICE, "- Check for expired files - TTL = %d days\n", r->dst->ttl); 1106 module_expire(r->dst); 1107 } 1108 1109 if (r->dst->all_max > 0) 1110 { 1111 debug_log(ASL_LEVEL_NOTICE, "- Check total storage used - MAX = %lu\n", r->dst->all_max); 1112 module_check_size(r->dst); 1113 } 1114 } 1115 else if ((r->dst->flags & MODULE_FLAG_TYPE_ASL_DIR) && (r->dst->ttl > 0)) 1116 { 1117 process_asl_data_store(r->dst); 1118 } 1119 } 1120 } 1121 1122 debug_log(ASL_LEVEL_NOTICE, "Finished processing module %s\n", (mod->name == NULL) ? "asl.conf" : mod->name); 1123 return 0; 1124} 1125 1126aslresponse 1127control_query(aslmsg a) 1128{ 1129 asl_search_result_t *out; 1130 char *qstr, *str, *res; 1131 uint32_t len, reslen, status; 1132 uint64_t cmax, qmin; 1133 kern_return_t kstatus; 1134 caddr_t vmstr; 1135 security_token_t sec; 1136 1137 if (asl_server_port == MACH_PORT_NULL) 1138 { 1139 bootstrap_look_up2(bootstrap_port, ASL_SERVICE_NAME, &asl_server_port, 0, BOOTSTRAP_PRIVILEGED_SERVER); 1140 if (asl_server_port == MACH_PORT_NULL) return NULL; 1141 } 1142 1143 qstr = asl_msg_to_string((asl_msg_t *)a, &len); 1144 1145 str = NULL; 1146 if (qstr == NULL) 1147 { 1148 asprintf(&str, "1\nQ [= ASLOption control]\n"); 1149 } 1150 else 1151 { 1152 asprintf(&str, "1\n%s [= ASLOption control]\n", qstr); 1153 free(qstr); 1154 } 1155 1156 if (str == NULL) return NULL; 1157 1158 /* length includes trailing nul */ 1159 len = strlen(str) + 1; 1160 out = NULL; 1161 qmin = 0; 1162 cmax = 0; 1163 sec.val[0] = -1; 1164 sec.val[1] = -1; 1165 1166 res = NULL; 1167 reslen = 0; 1168 status = ASL_STATUS_OK; 1169 1170 kstatus = vm_allocate(mach_task_self(), (vm_address_t *)&vmstr, len, TRUE); 1171 if (kstatus != KERN_SUCCESS) return NULL; 1172 1173 memmove(vmstr, str, len); 1174 free(str); 1175 1176 status = 0; 1177 kstatus = _asl_server_query(asl_server_port, vmstr, len, qmin, 1, 0, (caddr_t *)&res, &reslen, &cmax, (int *)&status, &sec); 1178 if (kstatus != KERN_SUCCESS) return NULL; 1179 1180 if (res == NULL) return NULL; 1181 1182 out = asl_list_from_string(res); 1183 vm_deallocate(mach_task_self(), (vm_address_t)res, reslen); 1184 1185 return out; 1186} 1187 1188int 1189checkpoint(const char *name) 1190{ 1191 /* send checkpoint message to syslogd */ 1192 debug_log(ASL_LEVEL_NOTICE, "Checkpoint module %s\n", (name == NULL) ? "*" : name); 1193 if (dryrun != 0) return 0; 1194 1195 aslmsg qmsg = asl_new(ASL_TYPE_QUERY); 1196 char *tmp = NULL; 1197 aslresponse res; 1198 1199 asprintf(&tmp, "%s checkpoint", (name == NULL) ? "*" : name); 1200 asl_set_query(qmsg, "action", tmp, ASL_QUERY_OP_EQUAL); 1201 free(tmp); 1202 1203 res = control_query(qmsg); 1204 1205 aslresponse_free(res); 1206 return 0; 1207} 1208 1209int 1210cli_main(int argc, char *argv[]) 1211{ 1212 int i, work; 1213 asl_out_module_t *mod, *m; 1214 asl_out_rule_t *r; 1215 asl_out_dst_data_t store; 1216 const char *mname = NULL; 1217 1218 if (geteuid() != 0) 1219 { 1220 if (argc == 0) debug = DEBUG_ASL; 1221 else debug = DEBUG_STDERR; 1222 1223 debug_log(ASL_LEVEL_ERR, "aslmanager must be run by root\n"); 1224 exit(1); 1225 } 1226 1227 module_ttl = DEFAULT_TTL; 1228 1229 /* cobble up a dst_data for the main asl_store */ 1230 memset(&store, 0, sizeof(store)); 1231 store.ttl = DEFAULT_TTL; 1232 store.all_max = DEFAULT_MAX_SIZE; 1233 1234 /* get parameters from asl.conf */ 1235 mod = asl_out_module_init(); 1236 1237 if (mod != NULL) 1238 { 1239 for (r = mod->ruleset; r != NULL; r = r->next) 1240 { 1241 if (r->action == ACTION_SET_PARAM) 1242 { 1243 if (r->query == NULL) _aslmanager_set_param(&store, r->options); 1244 } 1245 } 1246 } 1247 1248 work = DO_ASLDB | DO_MODULE; 1249 1250 for (i = 1; i < argc; i++) 1251 { 1252 if (!strcmp(argv[i], "-a")) 1253 { 1254 if (((i + 1) < argc) && (argv[i + 1][0] != '-')) store.rotate_dir = strdup(argv[++i]); 1255 else store.rotate_dir = strdup(PATH_ASL_ARCHIVE); 1256 store.mode = 0400; 1257 } 1258 else if (!strcmp(argv[i], "-s")) 1259 { 1260 if (((i + 1) < argc) && (argv[i + 1][0] != '-')) store.path = strdup(argv[++i]); 1261 } 1262 else if (!strcmp(argv[i], "-store_ttl")) 1263 { 1264 if (((i + 1) < argc) && (argv[i + 1][0] != '-')) store.ttl = atoi(argv[++i]); 1265 } 1266 else if (!strcmp(argv[i], "-module_ttl")) 1267 { 1268 if (((i + 1) < argc) && (argv[i + 1][0] != '-')) module_ttl = atoi(argv[++i]); 1269 } 1270 else if (!strcmp(argv[i], "-ttl")) 1271 { 1272 if (((i + 1) < argc) && (argv[i + 1][0] != '-')) module_ttl = store.ttl = atoi(argv[++i]); 1273 } 1274 else if (!strcmp(argv[i], "-size")) 1275 { 1276 if (((i + 1) < argc) && (argv[i + 1][0] != '-')) store.all_max = asl_str_to_size(argv[++i]); 1277 } 1278 else if (!strcmp(argv[i], "-checkpoint")) 1279 { 1280 work |= DO_CHECKPT; 1281 } 1282 else if (!strcmp(argv[i], "-module")) 1283 { 1284 work &= ~DO_ASLDB; 1285 1286 /* optional name follows -module */ 1287 if ((i +1) < argc) 1288 { 1289 if (argv[i + 1][0] != '-') mname = argv[++i]; 1290 } 1291 } 1292 else if (!strcmp(argv[i], "-asldb")) 1293 { 1294 work = DO_ASLDB; 1295 } 1296 else if (!strcmp(argv[i], "-d")) 1297 { 1298 if (((i + i) < argc) && (argv[i+1][0] != '-')) set_debug(DEBUG_STDERR, argv[++i]); 1299 else set_debug(DEBUG_STDERR, NULL); 1300 } 1301 else if (!strcmp(argv[i], "-dd")) 1302 { 1303 dryrun = 1; 1304 1305 if (((i + i) < argc) && (argv[i+1][0] != '-')) set_debug(DEBUG_STDERR, argv[++i]); 1306 else set_debug(DEBUG_STDERR, NULL); 1307 } 1308 } 1309 1310 if (store.path == NULL) store.path = strdup(PATH_ASL_STORE); 1311 1312 debug_log(ASL_LEVEL_ERR, "aslmanager starting%s\n", (dryrun == 1) ? " dryrun" : ""); 1313 1314 if (work & DO_ASLDB) process_asl_data_store(&store); 1315 1316 free(store.path); 1317 free(store.rotate_dir); 1318 1319 if (work & DO_MODULE) 1320 { 1321 if (work & DO_CHECKPT) checkpoint(mname); 1322 1323 if (mod != NULL) 1324 { 1325 for (m = mod; m != NULL; m = m->next) 1326 { 1327 if ((mname == NULL) || ((m->name != NULL) && (!strcmp(m->name, mname)))) 1328 { 1329 process_module(m); 1330 } 1331 } 1332 } 1333 } 1334 1335 asl_out_module_free(mod); 1336 1337 debug_log(ASL_LEVEL_NOTICE, "----------------------------------------\n"); 1338 debug_log(ASL_LEVEL_ERR, "aslmanager finished%s\n", (dryrun == 1) ? " dryrun" : ""); 1339 if (asl_aux_fd >= 0) asl_close_auxiliary_file(asl_aux_fd); 1340 1341 return 0; 1342} 1343 1344static void 1345accept_connection(xpc_connection_t peer) 1346{ 1347 xpc_connection_set_event_handler(peer, ^(xpc_object_t request) { 1348 if (xpc_get_type(request) == XPC_TYPE_DICTIONARY) 1349 { 1350 uid_t uid = xpc_connection_get_euid(peer); 1351 1352 /* send a reply immediately */ 1353 xpc_object_t reply = xpc_dictionary_create_reply(request); 1354 xpc_connection_send_message(peer, reply); 1355 xpc_release(reply); 1356 1357 /* 1358 * Some day, we may use the dictionary to pass parameters 1359 * to aslmanager, but for now, we ignore the input. 1360 */ 1361 if (uid == 0) cli_main(0, NULL); 1362 } 1363 else if (xpc_get_type(request) == XPC_TYPE_ERROR) 1364 { 1365 /* disconnect */ 1366 } 1367 1368 dispatch_async(serverq, ^__attribute__((noreturn)) { xpc_server_exit(0); }); 1369 }); 1370 1371 xpc_connection_resume(peer); 1372} 1373 1374int 1375main(int argc, char *argv[]) 1376{ 1377 int64_t is_managed = 0; 1378 1379 vproc_swap_integer(NULL, VPROC_GSK_IS_MANAGED, NULL, &is_managed); 1380 1381 if (is_managed == 0) return cli_main(argc, argv); 1382 1383 /* XPC server */ 1384 serverq = dispatch_queue_create("aslmanager", NULL); 1385 xpc_track_activity(); 1386 1387 /* Handle incoming messages. */ 1388 listener = xpc_connection_create_mach_service("com.apple.aslmanager", serverq, XPC_CONNECTION_MACH_SERVICE_LISTENER); 1389 xpc_connection_set_event_handler(listener, ^(xpc_object_t peer) { 1390 if (xpc_get_type(peer) == XPC_TYPE_CONNECTION) accept_connection(peer); 1391 }); 1392 xpc_connection_resume(listener); 1393 1394 dispatch_main(); 1395} 1396