1/* 2 * Copyright (c) 2004-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 <TargetConditionals.h> 25 26#include <sys/types.h> 27#include <sys/stat.h> 28#include <sys/socket.h> 29#include <sys/un.h> 30#include <sys/uio.h> 31#include <stdio.h> 32#include <stdlib.h> 33#include <string.h> 34#include <unistd.h> 35#include <fcntl.h> 36#include <errno.h> 37#include <netdb.h> 38#include <notify.h> 39#include <pthread.h> 40#include <sys/acl.h> 41#include <dirent.h> 42#include <time.h> 43#include <membership.h> 44#include <configuration_profile.h> 45#include "daemon.h" 46#include <xpc/private.h> 47 48#define _PATH_WALL "/usr/bin/wall" 49#define NOTIFY_PATH_SERVICE "com.apple.system.notify.service.path:0x87:" 50 51#define MY_ID "asl_action" 52 53/* XXX add to asl.h */ 54#define ASL_KEY_MODULE "ASLModule" 55 56#define MAX_FAILURES 5 57 58#define ACTION_STATUS_ERROR -1 59#define ACTION_STATUS_OK 0 60 61#define IDLE_CLOSE 300 62 63#define forever for(;;) 64 65static dispatch_queue_t asl_action_queue; 66static dispatch_source_t checkpoint_timer; 67static time_t sweep_time = 0; 68 69#if TARGET_OS_EMBEDDED 70static dispatch_queue_t crashlog_queue; 71static dispatch_source_t crashlog_sentinel_src; 72static int crashlog_sentinel_fd = -1; 73static time_t crashmover_state = 0; 74static int crashmover_token = -1; 75#endif 76 77typedef struct store_data 78{ 79 asl_file_t *store; 80 FILE *storedata; 81 uint64_t next_id; 82 time_t last_time; 83 uint32_t p_year; 84 uint32_t p_month; 85 uint32_t p_day; 86 dispatch_source_t monitor; 87} asl_action_store_data_t; 88 89typedef struct file_data 90{ 91 int fd; 92 uint32_t last_hash; 93 uint32_t last_count; 94 time_t last_time; 95 char *last_msg; 96 dispatch_source_t dup_timer; 97 dispatch_source_t monitor; 98} asl_action_file_data_t; 99 100typedef struct set_param_data 101{ 102 int token; 103} asl_action_set_param_data_t; 104 105static int action_asl_store_count; 106static bool store_has_logged; 107 108extern void db_save_message(aslmsg m); 109 110/* forward */ 111static int _act_file_checkpoint_all(uint32_t force); 112static void _asl_action_post_process_rule(asl_out_module_t *m, asl_out_rule_t *r); 113static void _asl_action_close_idle_files(time_t idle_time); 114 115static void 116_act_out_set_param(asl_out_module_t *m, char *x, bool eval) 117{ 118 char *s = x; 119 char **l; 120 uint32_t count, intval; 121 122 l = explode(s, " \t"); 123 if (l == NULL) return; 124 125 for (count = 0; l[count] != NULL; count++); 126 if (count == 0) 127 { 128 free_string_list(l); 129 return; 130 } 131 132 if (!strcasecmp(l[0], "enable")) 133 { 134 /* = enable [1|0] */ 135 if (count < 2) intval = 1; 136 else intval = atoi(l[1]); 137 138 if (!eval) intval = (intval == 0) ? 1 : 0; 139 140 if (intval == 0) m->flags &= ~MODULE_FLAG_ENABLED; 141 else m->flags|= MODULE_FLAG_ENABLED; 142 return; 143 } 144 else if (!strcasecmp(l[0], "disable")) 145 { 146 /* = disable [1|0] */ 147 if (count < 2) intval = 1; 148 else intval = atoi(l[1]); 149 150 if (!eval) intval = (intval == 0) ? 1 : 0; 151 152 if (intval != 0) m->flags &= ~MODULE_FLAG_ENABLED; 153 else m->flags|= MODULE_FLAG_ENABLED; 154 return; 155 } 156 157 free_string_list(l); 158 159 if (!strcmp(m->name, ASL_MODULE_NAME)) 160 { 161 /* Other parameters may be set by com.apple.asl module */ 162 control_set_param(x, eval); 163 } 164} 165 166static void 167_act_notify(asl_out_module_t *m, asl_out_rule_t *r) 168{ 169 if (m == NULL) return; 170 if ((m->flags & MODULE_FLAG_ENABLED) == 0) return; 171 172 if (r == NULL) return; 173 if (r->options == NULL) return; 174 175 notify_post(r->options); 176} 177 178static void 179_act_broadcast(asl_out_module_t *m, asl_out_rule_t *r, aslmsg msg) 180{ 181#if !TARGET_OS_EMBEDDED 182 FILE *pw; 183 const char *val; 184 185 if (m == NULL) return; 186 if ((m->flags & MODULE_FLAG_ENABLED) == 0) return; 187 188 if (m->name == NULL) return; 189 if (r == NULL) return; 190 if (msg == NULL) return; 191 192 /* only base module (asl.conf) may broadcast */ 193 if (strcmp(m->name, ASL_MODULE_NAME)) return; 194 195 val = r->options; 196 if (val == NULL) val = asl_get(msg, ASL_KEY_MSG); 197 if (val == NULL) return; 198 199 pw = popen(_PATH_WALL, "w"); 200 if (pw == NULL) 201 { 202 asldebug("%s: error sending wall message: %s\n", MY_ID, strerror(errno)); 203 return; 204 } 205 206 fprintf(pw, "%s", val); 207 pclose(pw); 208#endif 209} 210 211static void 212_act_access_control(asl_out_module_t *m, asl_out_rule_t *r, aslmsg msg) 213{ 214 int32_t ruid, rgid; 215 char *p; 216 217 if (m == NULL) return; 218 if (m->name == NULL) return; 219 if (r == NULL) return; 220 if (msg == NULL) return; 221 222 /* only base module (asl.conf) may set access controls */ 223 if (strcmp(m->name, ASL_MODULE_NAME)) return; 224 225 ruid = atoi(r->options); 226 rgid = -1; 227 p = strchr(r->options, ' '); 228 if (p == NULL) p = strchr(r->options, '\t'); 229 if (p != NULL) 230 { 231 *p = '\0'; 232 p++; 233 rgid = atoi(p); 234 } 235 236 if (ruid != -1) asl_set(msg, ASL_KEY_READ_UID, r->options); 237 if (p != NULL) 238 { 239 if (rgid != -1) asl_set(msg, ASL_KEY_READ_GID, p); 240 p--; 241 *p = ' '; 242 } 243} 244 245#if TARGET_OS_EMBEDDED 246static void 247_crashlog_sentinel_init(void) 248{ 249 char path[MAXPATHLEN]; 250 251 if (crashlog_sentinel_src != NULL) return; 252 253 snprintf(path, sizeof(path), "%s/com.apple.asl.%ld", _PATH_CRASHREPORTER, time(NULL)); 254 255 crashlog_sentinel_fd = open(path, O_WRONLY | O_CREAT); 256 if (crashlog_sentinel_fd < 0) 257 { 258 char *str = NULL; 259 asprintf(&str, "[Sender syslogd] [Level 3] [PID %u] [Facility syslog] [Message Sentinel %s create/open failed (%s)]", global.pid, path, strerror(errno)); 260 internal_log_message(str); 261 free(str); 262 return; 263 } 264 265 close(crashlog_sentinel_fd); 266 267 crashlog_sentinel_fd = open(path, O_EVTONLY, 0); 268 if (crashlog_sentinel_fd < 0) 269 { 270 char *str = NULL; 271 asprintf(&str, "[Sender syslogd] [Level 3] [PID %u] [Facility syslog] [Message Sentinel %s event/open failed (%s)]", global.pid, path, strerror(errno)); 272 internal_log_message(str); 273 free(str); 274 return; 275 } 276 277 crashlog_sentinel_src = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE, (uintptr_t)crashlog_sentinel_fd, DISPATCH_VNODE_DELETE, asl_action_queue); 278 if (crashlog_sentinel_src == NULL) 279 { 280 char *str = NULL; 281 asprintf(&str, "[Sender syslogd] [Level 3] [PID %u] [Facility syslog] [Message Sentinel %s dispatch_source_create failed]", global.pid, path); 282 internal_log_message(str); 283 free(str); 284 close(crashlog_sentinel_fd); 285 crashlog_sentinel_fd = -1; 286 return; 287 } 288 289 dispatch_source_set_event_handler(crashlog_sentinel_src, ^{ 290 if (crashmover_state != 0) 291 { 292 asldebug("CrashMover inactive / sentinel deleted: resuming crashlog queue\n"); 293 dispatch_resume(crashlog_queue); 294 crashmover_state = 0; 295 } 296 297 if (crashlog_sentinel_src != NULL) 298 { 299 dispatch_source_cancel(crashlog_sentinel_src); 300 dispatch_release(crashlog_sentinel_src); 301 } 302 303 crashlog_sentinel_src = NULL; 304 305 close(crashlog_sentinel_fd); 306 crashlog_sentinel_fd = -1; 307 _crashlog_sentinel_init(); 308 }); 309 310 dispatch_resume(crashlog_sentinel_src); 311 asldebug("Created CrashLog Sentinel: %s\n", path); 312} 313 314static void 315_crashlog_queue_check(void) 316{ 317 /* 318 * Check whether the crashlog queue has been suspended for too long. 319 * We allow the crashlog quque to be suspended for 60 seconds. 320 * After that, we start logging again. This prevents syslogd from 321 * filling memory due to a suspended queue. CrashMover really shoud 322 * take no more than a second or two to finish. 323 */ 324 if (crashmover_state == 0) return; 325 if ((time(NULL) - crashmover_state) <= 60) return; 326 327 asldebug("CrashMover timeout: resuming crashlog queue\n"); 328 dispatch_resume(crashlog_queue); 329 crashmover_state = 0; 330 331 /* 332 * crashlog_sentinel_src should never be NULL, but if 333 * _crashlog_sentinel_init failed for some strange reason, 334 * it will be NULL here. 335 */ 336 if (crashlog_sentinel_src != NULL) 337 { 338 dispatch_source_cancel(crashlog_sentinel_src); 339 dispatch_release(crashlog_sentinel_src); 340 } 341 342 crashlog_sentinel_src = NULL; 343 344 close(crashlog_sentinel_fd); 345 crashlog_sentinel_fd = -1; 346 _crashlog_sentinel_init(); 347} 348#endif 349 350static void 351_act_dst_close(asl_out_rule_t *r) 352{ 353 if (r == NULL) return; 354 if (r->dst == NULL) return; 355 if (r->dst->private == NULL) return; 356 357 if ((r->action == ACTION_ASL_DIR) || (r->action == ACTION_ASL_FILE)) 358 { 359 asl_action_store_data_t *sdata = (asl_action_store_data_t *)r->dst->private; 360 if (sdata->store != NULL) asl_file_close(sdata->store); 361 sdata->store = NULL; 362 363 if (sdata->storedata != NULL) fclose(sdata->storedata); 364 sdata->storedata = NULL; 365 366 if (sdata->monitor != NULL) 367 { 368 dispatch_source_cancel(sdata->monitor); 369 dispatch_release(sdata->monitor); 370 sdata->monitor = NULL; 371 } 372 } 373 else if (r->action == ACTION_FILE) 374 { 375 asl_action_file_data_t *fdata = (asl_action_file_data_t *)r->dst->private; 376 if (fdata->fd >= 0) close(fdata->fd); 377 fdata->fd = -1; 378 379 if (fdata->monitor != NULL) 380 { 381 dispatch_source_cancel(fdata->monitor); 382 dispatch_release(fdata->monitor); 383 fdata->monitor = NULL; 384 } 385 } 386} 387 388static uint32_t 389_act_store_file_setup(asl_out_module_t *m, asl_out_rule_t *r) 390{ 391 uint32_t status; 392 asl_action_store_data_t *sdata; 393 char dstpath[MAXPATHLEN]; 394 395 if (r == NULL) return ASL_STATUS_INVALID_STORE; 396 if (r->dst == NULL) return ASL_STATUS_INVALID_STORE; 397 if (r->dst->private == NULL) return ASL_STATUS_INVALID_STORE; 398 399 sdata = (asl_action_store_data_t *)r->dst->private; 400 if (sdata->store == NULL) 401 { 402 /* create path if necessary */ 403 asl_out_mkpath(r); 404 405 int fd = asl_out_dst_file_create_open(r->dst); 406 if (fd < 0) 407 { 408 asldebug("_act_store_file_setup: asl_out_dst_file_create_open failed %d %s\n", errno, strerror(errno)); 409 return ASL_STATUS_WRITE_FAILED; 410 } 411 close(fd); 412 413 asl_make_dst_filename(r->dst, dstpath, sizeof(dstpath)); 414 status = asl_file_open_write(dstpath, 0, -1, -1, &(sdata->store)); 415 if (status != ASL_STATUS_OK) return status; 416 417 sdata->monitor = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE, fileno(sdata->store->store), DISPATCH_VNODE_DELETE, asl_action_queue); 418 if (sdata->monitor != NULL) 419 { 420 dispatch_source_set_event_handler(sdata->monitor, ^{ _act_dst_close(r); }); 421 dispatch_resume(sdata->monitor); 422 } 423 424 status = asl_file_read_set_position(sdata->store, ASL_FILE_POSITION_LAST); 425 if (status != ASL_STATUS_OK) 426 { 427 asldebug("_act_store_file_setup: asl_file_read_set_position failed %d %s\n", status, asl_core_error(status)); 428 return status; 429 } 430 431 sdata->next_id = sdata->store->cursor_xid + 1; 432 if (fseek(sdata->store->store, 0, SEEK_END) != 0) 433 { 434 asldebug("_act_store_file_setup: fseek failed %d %s\n", errno, strerror(errno)); 435 return ASL_STATUS_WRITE_FAILED; 436 } 437 } 438 else 439 { 440 sdata->next_id++; 441 } 442 443 return ASL_STATUS_OK; 444} 445 446/* 447 * _act_store_dir_setup 448 * 449 * Creates store directory if it does not exist 450 * Creates StoreData file if it does not exist 451 * Reads ASL Message ID from StoreData file 452 * Writes ASL Message ID + 1 to StoreData file 453 * Opens current day file (e.g. "/foo/bar/2012.04.06.asl") 454 */ 455static uint32_t 456_act_store_dir_setup(asl_out_module_t *m, asl_out_rule_t *r, time_t tick) 457{ 458 struct tm ctm; 459 char *path; 460 struct stat sb; 461 uint64_t xid; 462 int status; 463 mode_t mask; 464 asl_action_store_data_t *sdata; 465 466 if (r == NULL) return ASL_STATUS_INVALID_STORE; 467 if (r->dst == NULL) return ASL_STATUS_INVALID_STORE; 468 if (r->dst->private == NULL) return ASL_STATUS_INVALID_STORE; 469 if (r->dst->path == NULL) return ASL_STATUS_INVALID_STORE; 470 471 sdata = (asl_action_store_data_t *)r->dst->private; 472 473 /* get / set message id from StoreData file */ 474 xid = 0; 475 476 if (sdata->storedata == NULL) 477 { 478 memset(&sb, 0, sizeof(struct stat)); 479 status = stat(r->dst->path, &sb); 480 if (status == 0) 481 { 482 /* must be a directory */ 483 if (!S_ISDIR(sb.st_mode)) return ASL_STATUS_INVALID_STORE; 484 } 485 else if (errno == ENOENT) 486 { 487 /* doesn't exist - create it */ 488 mask = umask(S_IWGRP | S_IWOTH); 489 status = mkpath_np(r->dst->path, 0755); 490 if (status == 0) status = chmod(r->dst->path, r->dst->mode); 491 umask(mask); 492 493 if (status != 0) return ASL_STATUS_WRITE_FAILED; 494#if !TARGET_IPHONE_SIMULATOR 495 if (chown(r->dst->path, r->dst->uid[0], r->dst->gid[0]) != 0) return ASL_STATUS_WRITE_FAILED; 496#endif 497 } 498 else 499 { 500 /* Unexpected stat error */ 501 return ASL_STATUS_FAILED; 502 } 503 504 path = NULL; 505 asprintf(&path, "%s/%s", r->dst->path, FILE_ASL_STORE_DATA); 506 if (path == NULL) return ASL_STATUS_NO_MEMORY; 507 508 memset(&sb, 0, sizeof(struct stat)); 509 status = stat(path, &sb); 510 if (status == 0) 511 { 512 /* StoreData exists: open and read last xid */ 513 sdata->storedata = fopen(path, "r+"); 514 if (sdata->storedata == NULL) 515 { 516 free(path); 517 return ASL_STATUS_FAILED; 518 } 519 520 if (fread(&xid, sizeof(uint64_t), 1, sdata->storedata) != 1) 521 { 522 free(path); 523 fclose(sdata->storedata); 524 sdata->storedata = NULL; 525 return ASL_STATUS_READ_FAILED; 526 } 527 } 528 else if (errno == ENOENT) 529 { 530 /* StoreData does not exist: create it */ 531 sdata->storedata = fopen(path, "w"); 532 if (sdata->storedata == NULL) 533 { 534 free(path); 535 return ASL_STATUS_FAILED; 536 } 537 538#if !TARGET_IPHONE_SIMULATOR 539 if (chown(path, r->dst->uid[0], r->dst->gid[0]) != 0) 540 { 541 free(path); 542 return ASL_STATUS_WRITE_FAILED; 543 } 544#endif 545 } 546 else 547 { 548 /* Unexpected stat error */ 549 free(path); 550 return ASL_STATUS_FAILED; 551 } 552 553 sdata->monitor = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE, fileno(sdata->storedata), DISPATCH_VNODE_DELETE, asl_action_queue); 554 if (sdata->monitor != NULL) 555 { 556 dispatch_source_set_event_handler(sdata->monitor, ^{ _act_dst_close(r); }); 557 dispatch_resume(sdata->monitor); 558 } 559 560 free(path); 561 } 562 else 563 { 564 rewind(sdata->storedata); 565 if (fread(&xid, sizeof(uint64_t), 1, sdata->storedata) != 1) 566 { 567 fclose(sdata->storedata); 568 sdata->storedata = NULL; 569 return ASL_STATUS_READ_FAILED; 570 } 571 } 572 573 xid = asl_core_ntohq(xid); 574 xid++; 575 sdata->next_id = xid; 576 577 xid = asl_core_htonq(xid); 578 rewind(sdata->storedata); 579 status = fwrite(&xid, sizeof(uint64_t), 1, sdata->storedata); 580 if (status != 1) 581 { 582 fclose(sdata->storedata); 583 sdata->storedata = NULL; 584 return ASL_STATUS_WRITE_FAILED; 585 } 586 587 memset(&ctm, 0, sizeof(struct tm)); 588 589 if (localtime_r((const time_t *)&tick, &ctm) == NULL) return ASL_STATUS_FAILED; 590 if ((sdata->store != NULL) && (sdata->p_year == ctm.tm_year) && (sdata->p_month == ctm.tm_mon) && (sdata->p_day == ctm.tm_mday)) 591 { 592 return ASL_STATUS_OK; 593 } 594 595 if (sdata->store != NULL) asl_file_close(sdata->store); 596 sdata->store = NULL; 597 free(r->dst->fname); 598 r->dst->fname = NULL; 599 600 r->dst->stamp = tick; 601 602 sdata->p_year = ctm.tm_year; 603 sdata->p_month = ctm.tm_mon; 604 sdata->p_day = ctm.tm_mday; 605 606 asprintf(&(r->dst->fname), "%s/%d.%02d.%02d.asl", r->dst->path, ctm.tm_year + 1900, ctm.tm_mon + 1, ctm.tm_mday); 607 if (r->dst->fname == NULL) return ASL_STATUS_NO_MEMORY; 608 mask = umask(0); 609 610 status = ASL_STATUS_OK; 611 if (sdata->store == NULL) { 612#if TARGET_IPHONE_SIMULATOR 613 uid_t uid = -1; 614 gid_t gid = -1; 615#else 616 uid_t uid = r->dst->uid[0]; 617 gid_t gid = r->dst->gid[0]; 618#endif 619 status = asl_file_open_write(r->dst->fname, (r->dst->mode & 0666), uid, gid, &(sdata->store)); 620 } 621 umask(mask); 622 623 if (status != ASL_STATUS_OK) return status; 624 625 if (fseek(sdata->store->store, 0, SEEK_END) != 0) return ASL_STATUS_FAILED; 626 627 return ASL_STATUS_OK; 628} 629 630static void 631_asl_action_store_data_free(asl_action_store_data_t *sdata) 632{ 633 if (sdata == NULL) return; 634 635 if (sdata->store != NULL) asl_file_close(sdata->store); 636 sdata->store = NULL; 637 638 if (sdata->storedata != NULL) fclose(sdata->storedata); 639 sdata->storedata = NULL; 640 641 free(sdata); 642} 643 644static void 645_asl_action_file_data_free(asl_action_file_data_t *fdata) 646{ 647 if (fdata == NULL) return; 648 649 if (fdata->dup_timer != NULL) 650 { 651 if (fdata->last_count == 0) 652 { 653 /* 654 * The timer exists, but last_count is zero, so the timer is suspended. 655 * Sources must not be released in when suspended. 656 * So we resume it so that we can release it. 657 */ 658 dispatch_resume(fdata->dup_timer); 659 } 660 661 dispatch_release(fdata->dup_timer); 662 } 663 664 free(fdata->last_msg); 665 if (fdata->fd >= 0) close(fdata->fd); 666 free(fdata); 667} 668 669static void 670_asl_action_set_param_data_free(asl_action_set_param_data_t *spdata) 671{ 672 if (spdata != NULL) notify_cancel(spdata->token); 673 free(spdata); 674} 675 676static void 677_asl_action_save_failed(const char *where, asl_out_module_t *m, asl_out_rule_t *r, uint32_t status) 678{ 679 if (r->dst->flags & MODULE_FLAG_SOFT_WRITE) return; 680 681 r->dst->fails++; 682 asldebug("%s: %s save to %s failed: %s\n", where, m->name, r->dst->path, asl_core_error(status)); 683 684 /* disable further activity after multiple failures */ 685 if (r->dst->fails > MAX_FAILURES) 686 { 687 char *str = NULL; 688 asprintf(&str, "[Sender syslogd] [Level 3] [PID %u] [Facility syslog] [Message Disabling module %s writes to %s following %u failures (%s)]", global.pid, m->name, r->dst->path, r->dst->fails, asl_core_error(status)); 689 internal_log_message(str); 690 free(str); 691 692 if (r->action == ACTION_FILE) _asl_action_file_data_free((asl_action_file_data_t *)r->dst->private); 693 else _asl_action_store_data_free((asl_action_store_data_t *)r->dst->private); 694 695 r->dst->private = NULL; 696 r->action = ACTION_NONE; 697 } 698} 699 700static int 701_act_store_file(asl_out_module_t *m, asl_out_rule_t *r, aslmsg msg) 702{ 703 asl_action_store_data_t *sdata; 704 uint32_t status; 705 uint64_t mid; 706 707 if (r == NULL) return ACTION_STATUS_ERROR; 708 if (r->dst == NULL) return ACTION_STATUS_ERROR; 709 if (r->dst->private == NULL) return ACTION_STATUS_ERROR; 710 711 sdata = (asl_action_store_data_t *)r->dst->private; 712 713 /* check dst for file_max & etc */ 714 if (r->dst->flags & MODULE_FLAG_ROTATE) 715 { 716 if (asl_out_dst_checkpoint(r->dst, CHECKPOINT_TEST) != 0) 717 { 718 _act_dst_close(r); 719 asl_trigger_aslmanager(); 720 } 721 } 722 723 status = _act_store_file_setup(m, r); 724 if (status == ASL_STATUS_OK) 725 { 726 sdata->last_time = time(NULL); 727 728 r->dst->fails = 0; 729 mid = sdata->next_id; 730 731 /* save message to file and update dst size */ 732 status = asl_file_save(sdata->store, msg, &mid); 733 if (status == ASL_STATUS_OK) r->dst->size = sdata->store->file_size; 734 } 735 736 if (status != ASL_STATUS_OK) 737 { 738 _asl_action_save_failed("_act_store_file", m, r, status); 739 return ACTION_STATUS_ERROR; 740 } 741 742 return ACTION_STATUS_OK; 743} 744 745static int 746_act_store_dir(asl_out_module_t *m, asl_out_rule_t *r, aslmsg msg) 747{ 748 asl_action_store_data_t *sdata; 749 uint32_t status; 750 uint64_t mid; 751 const char *val; 752 time_t tick; 753 754 if (r == NULL) return ACTION_STATUS_ERROR; 755 if (r->dst == NULL) return ACTION_STATUS_ERROR; 756 if (r->dst->private == NULL) return ACTION_STATUS_ERROR; 757 758 sdata = (asl_action_store_data_t *)r->dst->private; 759 760 val = asl_get(msg, ASL_KEY_TIME); 761 if (val == NULL) return ACTION_STATUS_ERROR; 762 763 /* check dst for file_max & etc */ 764 if (asl_out_dst_checkpoint(r->dst, CHECKPOINT_TEST) != 0) 765 { 766 _act_dst_close(r); 767 asl_trigger_aslmanager(); 768 } 769 770 tick = atol(val); 771 772 status = _act_store_dir_setup(m, r, tick); 773 if (status == ASL_STATUS_OK) 774 { 775 sdata->last_time = time(NULL); 776 777 r->dst->fails = 0; 778 mid = sdata->next_id; 779 status = asl_file_save(sdata->store, msg, &mid); 780 if (status == ASL_STATUS_OK) r->dst->size = sdata->store->file_size; 781 } 782 783 if (status != ASL_STATUS_OK) 784 { 785 _asl_action_save_failed("_act_store_dir", m, r, status); 786 return ACTION_STATUS_ERROR; 787 } 788 789 return ACTION_STATUS_OK; 790} 791 792static void 793_act_store_final(asl_out_module_t *m, asl_out_rule_t *r, aslmsg msg) 794{ 795 if (r->action == ACTION_ASL_DIR) _act_store_dir(m, r, msg); 796 else _act_store_file(m, r, msg); 797} 798 799/* 800 * Save a message to an ASL format file (ACTION_ASL_FILE) 801 * or to an ASL directory (ACTION_ASL_DIR). 802 */ 803static void 804_act_store(asl_out_module_t *m, asl_out_rule_t *r, aslmsg msg) 805{ 806 if (r == NULL) return; 807 if (msg == NULL) return; 808 if (m == NULL) return; 809 if ((m->flags & MODULE_FLAG_ENABLED) == 0) return; 810 if (r->dst == NULL) return; 811 812 if (r->dst->flags & MODULE_FLAG_HAS_LOGGED) return; 813 r->dst->flags |= MODULE_FLAG_HAS_LOGGED; 814 815#if TARGET_OS_EMBEDDED 816 if (r->dst->flags & MODULE_FLAG_CRASHLOG) 817 { 818 _crashlog_queue_check(); 819 asl_msg_retain((asl_msg_t *)msg); 820 dispatch_async(crashlog_queue, ^{ 821 _act_store_final(m, r, msg); 822 asl_msg_release((asl_msg_t *)msg); 823 }); 824 return; 825 } 826#endif 827 828 _act_store_final(m, r, msg); 829} 830 831static int 832_send_repeat_msg(asl_out_rule_t *r) 833{ 834 asl_action_file_data_t *fdata; 835 char vt[32], *msg; 836 int len, status; 837 time_t now = time(NULL); 838 839 if (r == NULL) return -1; 840 if (r->dst == NULL) return -1; 841 if (r->dst->private == NULL) return -1; 842 843 fdata = (asl_action_file_data_t *)r->dst->private; 844 845 free(fdata->last_msg); 846 fdata->last_msg = NULL; 847 848 if (fdata->last_count == 0) return 0; 849 850 /* stop the timer */ 851 dispatch_suspend(fdata->dup_timer); 852 853 memset(vt, 0, sizeof(vt)); 854 ctime_r(&now, vt); 855 vt[19] = '\0'; 856 857 msg = NULL; 858 asprintf(&msg, "%s --- last message repeated %u time%s ---\n", vt + 4, fdata->last_count, (fdata->last_count == 1) ? "" : "s"); 859 fdata->last_count = 0; 860 fdata->last_time = now; 861 if (msg == NULL) return -1; 862 863 if (fdata->fd < 0) fdata->fd = asl_out_dst_file_create_open(r->dst); 864 865 len = strlen(msg); 866 status = write(fdata->fd, msg, len); 867 free(msg); 868 869 if ((status < 0) || (status < len)) 870 { 871 asldebug("%s: error writing repeat message (%s): %s\n", MY_ID, r->dst->path, strerror(errno)); 872 return -1; 873 } 874 875 return 0; 876} 877 878static int 879_act_file_open(asl_out_module_t *m, asl_out_rule_t *r) 880{ 881 asl_action_file_data_t *fdata; 882 883 if (r == NULL) return -1; 884 if (r->dst == NULL) return -1; 885 if (r->dst->private == NULL) return -1; 886 887 fdata = (asl_action_file_data_t *)r->dst->private; 888 if (fdata->fd < 0) 889 { 890 fdata->fd = asl_out_dst_file_create_open(r->dst); 891 if (fdata->fd < 0) 892 { 893 /* 894 * lazy path creation: create path and retry 895 * asl_out_dst_file_create_open doesn not create the path 896 * so we do it here. 897 */ 898 asl_out_mkpath(r); 899 fdata->fd = asl_out_dst_file_create_open(r->dst); 900 } 901 902 if (fdata->fd >= 0) 903 { 904 fdata->monitor = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE, fdata->fd, DISPATCH_VNODE_DELETE, asl_action_queue); 905 if (fdata->monitor != NULL) 906 { 907 dispatch_source_set_event_handler(fdata->monitor, ^{ _act_dst_close(r); }); 908 dispatch_resume(fdata->monitor); 909 } 910 } 911 } 912 913 return fdata->fd; 914} 915 916static void 917_start_cycling() 918{ 919 struct timespec midnight; 920 struct tm t; 921 time_t x; 922 923 x = time(NULL); 924 925 if (checkpoint_timer != NULL) return; 926 927 localtime_r(&x, &t); 928 929 t.tm_sec = 0; 930 t.tm_min = 0; 931 t.tm_hour = 0; 932 t.tm_mday++; 933 934 x = mktime(&t); 935 midnight.tv_sec = x; 936 midnight.tv_nsec = 0; 937 938 checkpoint_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, asl_action_queue); 939 dispatch_source_set_timer(checkpoint_timer, dispatch_walltime(&midnight, 0), NSEC_PER_SEC * SEC_PER_DAY, 0); 940 dispatch_source_set_event_handler(checkpoint_timer, ^{ _act_file_checkpoint_all(CHECKPOINT_FORCE); }); 941 dispatch_resume(checkpoint_timer); 942} 943 944/* check if a module path (mpath) matches a user path (upath) */ 945static bool 946_act_file_equal(const char *mpath, const char *upath) 947{ 948 const char *slash; 949 950 /* NULL upath means user wants to match all files */ 951 if (upath == NULL) return true; 952 953 if (mpath == NULL) return false; 954 955 /* check for exact match */ 956 if (!strcmp(mpath, upath)) return true; 957 958 /* upath may be the last component of mpath */ 959 slash = strrchr(mpath, '/'); 960 if (slash == NULL) return false; 961 962 if (!strcmp(slash + 1, upath)) return true; 963 return false; 964} 965 966static int 967_act_file_checkpoint(asl_out_module_t *m, const char *path, uint32_t force) 968{ 969 asl_out_rule_t *r; 970 int did_checkpoint = 0; 971 972 if (m == NULL) return 0; 973 974 975 for (r = m->ruleset; r != NULL; r = r->next) 976 { 977 if ((r->action == ACTION_FILE) || (r->action == ACTION_ASL_FILE)) 978 { 979 if (r->dst->flags & MODULE_FLAG_ROTATE) 980 { 981 if (_act_file_equal(r->dst->path, path)) 982 { 983 if (force & CHECKPOINT_CRASH) 984 { 985 if (r->dst->flags & MODULE_FLAG_CRASHLOG) 986 { 987 if (asl_out_dst_checkpoint(r->dst, CHECKPOINT_FORCE) > 0) 988 { 989 did_checkpoint = 1; 990 _act_dst_close(r); 991 } 992 } 993 } 994 else 995 { 996 if (asl_out_dst_checkpoint(r->dst, force) > 0) 997 { 998 did_checkpoint = 1; 999 _act_dst_close(r); 1000 } 1001 } 1002 } 1003 } 1004 } 1005 } 1006 1007 return did_checkpoint; 1008} 1009 1010static int 1011_act_file_checkpoint_all(uint32_t force) 1012{ 1013 asl_out_module_t *m; 1014 int did_checkpoint = 0; 1015 1016 for (m = global.asl_out_module; m != NULL; m = m->next) 1017 { 1018 if (_act_file_checkpoint(m, NULL, force) > 0) did_checkpoint = 1; 1019 } 1020 1021 asl_trigger_aslmanager(); 1022 1023 return did_checkpoint; 1024} 1025 1026static void 1027_act_file_final(asl_out_module_t *m, asl_out_rule_t *r, aslmsg msg) 1028{ 1029 asl_action_file_data_t *fdata; 1030 int is_dup; 1031 uint32_t len, msg_hash = 0; 1032 char *str; 1033 time_t now; 1034 1035 /* 1036 * If print format is std, bsd, or msg, then skip messages with 1037 * no ASL_KEY_MSG, or without a value for it. 1038 */ 1039 if (r->dst->flags & MODULE_FLAG_STD_BSD_MSG) 1040 { 1041 const char *msgval = NULL; 1042 if (asl_msg_lookup((asl_msg_t *)msg, ASL_KEY_MSG, &msgval, NULL) != 0) return; 1043 if (msgval == NULL) return; 1044 } 1045 1046 fdata = (asl_action_file_data_t *)r->dst->private; 1047 1048 now = time(NULL); 1049 1050 is_dup = 0; 1051 1052 str = asl_format_message((asl_msg_t *)msg, r->dst->fmt, r->dst->tfmt, ASL_ENCODE_SAFE, &len); 1053 1054 if (r->dst->flags & MODULE_FLAG_COALESCE) 1055 { 1056 if (fdata->dup_timer == NULL) 1057 { 1058 /* create a timer to flush dups on this file */ 1059 fdata->dup_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, asl_action_queue); 1060 dispatch_source_set_event_handler(fdata->dup_timer, ^{ _send_repeat_msg(r); }); 1061 } 1062 1063 if ((global.bsd_max_dup_time > 0) && (str != NULL) && (fdata->last_msg != NULL)) 1064 { 1065 msg_hash = asl_core_string_hash(str + 16, len - 16); 1066 if ((fdata->last_hash == msg_hash) && (!strcmp(fdata->last_msg, str + 16))) 1067 { 1068 if ((now - fdata->last_time) < global.bsd_max_dup_time) is_dup = 1; 1069 } 1070 } 1071 } 1072 1073 if (is_dup == 1) 1074 { 1075 if (fdata->last_count == 0) 1076 { 1077 /* start the timer */ 1078 dispatch_source_set_timer(fdata->dup_timer, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * global.bsd_max_dup_time), DISPATCH_TIME_FOREVER, 0); 1079 dispatch_resume(fdata->dup_timer); 1080 } 1081 1082 fdata->last_count++; 1083 } 1084 else 1085 { 1086 if (_act_file_open(m, r) < 0) 1087 { 1088 _asl_action_save_failed("_act_file", m, r, ASL_STATUS_FAILED); 1089 free(str); 1090 return; 1091 } 1092 else 1093 { 1094 r->dst->fails = 0; 1095 } 1096 1097 /* 1098 * The current message is not a duplicate. If fdata->last_count > 0 1099 * we need to write a "last message repeated N times" log entry. 1100 * _send_repeat_msg will free last_msg and do nothing if 1101 * last_count == 0, but we test and free here to avoid a function call. 1102 */ 1103 if (fdata->last_count > 0) 1104 { 1105 _send_repeat_msg(r); 1106 } 1107 else 1108 { 1109 free(fdata->last_msg); 1110 fdata->last_msg = NULL; 1111 } 1112 1113 /* check dst for file_max & etc */ 1114 if (r->dst->flags & MODULE_FLAG_ROTATE) 1115 { 1116 int ckpt = asl_out_dst_checkpoint(r->dst, CHECKPOINT_TEST); 1117 if (ckpt != 0) 1118 { 1119 _act_dst_close(r); 1120 asl_trigger_aslmanager(); 1121 1122 if (_act_file_open(m, r) < 0) 1123 { 1124 _asl_action_save_failed("_act_file", m, r, ASL_STATUS_FAILED); 1125 free(str); 1126 return; 1127 } 1128 else 1129 { 1130 r->dst->fails = 0; 1131 } 1132 } 1133 } 1134 1135 if (str != NULL) fdata->last_msg = strdup(str + 16); 1136 1137 fdata->last_hash = msg_hash; 1138 fdata->last_count = 0; 1139 fdata->last_time = now; 1140 1141 if ((str != NULL) && (len > 1)) 1142 { 1143 /* write line to file and update dst size */ 1144 size_t bytes = write(fdata->fd, str, len - 1); 1145 if (bytes > 0) r->dst->size += bytes; 1146 } 1147 } 1148 1149 free(str); 1150} 1151 1152static void 1153_act_file(asl_out_module_t *m, asl_out_rule_t *r, aslmsg msg) 1154{ 1155 if (r == NULL) return; 1156 if (msg == NULL) return; 1157 if (m == NULL) return; 1158 if ((m->flags & MODULE_FLAG_ENABLED) == 0) return; 1159 if (r->dst == NULL) return; 1160 if (r->dst->private == NULL) return; 1161 1162 if (r->dst->flags & MODULE_FLAG_HAS_LOGGED) return; 1163 1164 r->dst->flags |= MODULE_FLAG_HAS_LOGGED; 1165 1166#if TARGET_OS_EMBEDDED 1167 if (r->dst->flags & MODULE_FLAG_CRASHLOG) 1168 { 1169 _crashlog_queue_check(); 1170 asl_msg_retain((asl_msg_t *)msg); 1171 dispatch_async(crashlog_queue, ^{ 1172 _act_file_final(m, r, msg); 1173 asl_msg_release((asl_msg_t *)msg); 1174 }); 1175 return; 1176 } 1177#endif 1178 1179 _act_file_final(m, r, msg); 1180} 1181 1182static void 1183_act_forward(asl_out_module_t *m, asl_out_rule_t *r, aslmsg msg) 1184{ 1185 /* To do: <rdar://problem/6130747> Add a "forward" action to asl.conf */ 1186} 1187 1188static void 1189_act_control(asl_out_module_t *m, asl_out_rule_t *r, aslmsg msg) 1190{ 1191 const char *p; 1192 1193 if (m == NULL) return; 1194 if (r == NULL) return; 1195 p = asl_get(msg, ASL_KEY_MODULE); 1196 1197 if (r->options == NULL) return; 1198 1199 if (!strcmp(r->options, "enable")) 1200 { 1201 m->flags |= MODULE_FLAG_ENABLED; 1202 } 1203 else if (!strcmp(r->options, "disable")) 1204 { 1205 m->flags &= ~MODULE_FLAG_ENABLED; 1206 } 1207 else if ((!strcmp(r->options, "checkpoint")) || (!strcmp(r->options, "rotate"))) 1208 { 1209 _act_file_checkpoint(m, NULL, CHECKPOINT_FORCE); 1210 } 1211} 1212 1213static void 1214_send_to_asl_store(aslmsg msg) 1215{ 1216 if ((global.asl_out_module != NULL) && ((global.asl_out_module->flags & MODULE_FLAG_ENABLED) == 0)) return; 1217 1218 if (store_has_logged) return; 1219 store_has_logged = true; 1220 1221 db_save_message(msg); 1222} 1223 1224static int 1225_asl_out_process_message(asl_out_module_t *m, aslmsg msg) 1226{ 1227 asl_out_rule_t *r; 1228 1229 if (m == NULL) return 1; 1230 if (msg == NULL) return 1; 1231 1232 /* reset flag bit used for duplicate avoidance */ 1233 for (r = m->ruleset; r != NULL; r = r->next) 1234 { 1235 if ((r->action == ACTION_FILE) || (r->action == ACTION_ASL_DIR) || (r->action == ACTION_ASL_FILE)) 1236 { 1237 if (r->dst != NULL) r->dst->flags &= MODULE_FLAG_CLEAR_LOGGED; 1238 } 1239 } 1240 1241 for (r = m->ruleset; r != NULL; r = r->next) 1242 { 1243 if (r->query == NULL) continue; 1244 1245 /* ACTION_SET_FILE, ACTION_SET_PLIST, and ACTION_SET_PROF are handled independently */ 1246 if ((r->action == ACTION_SET_FILE) || (r->action == ACTION_SET_PLIST) || (r->action == ACTION_SET_PROF)) continue; 1247 1248 /* 1249 * ACTION_CLAIM during processing is a filter. It will only be here if the option "only" 1250 * was supplied. In this case we test the message against the query. If it does not 1251 * match, we skip the message. 1252 */ 1253 if (r->action == ACTION_CLAIM) 1254 { 1255 if ((asl_msg_cmp(r->query, (asl_msg_t *)msg) != 1)) return 0; 1256 } 1257 1258 if ((asl_msg_cmp(r->query, (asl_msg_t *)msg) == 1)) 1259 { 1260 if (r->action == ACTION_NONE) continue; 1261 else if (r->action == ACTION_IGNORE) return 1; 1262 else if (r->action == ACTION_SKIP) return 0; 1263 else if (r->action == ACTION_ASL_STORE) _send_to_asl_store(msg); 1264 else if (r->action == ACTION_ACCESS) _act_access_control(m, r, msg); 1265 else if (r->action == ACTION_NOTIFY) _act_notify(m, r); 1266 else if (r->action == ACTION_BROADCAST) _act_broadcast(m, r, msg); 1267 else if (r->action == ACTION_FORWARD) _act_forward(m, r, msg); 1268 else if (r->action == ACTION_CONTROL) _act_control(m, r, msg); 1269 else if (r->action == ACTION_SET_PARAM) _act_out_set_param(m, r->options, true); 1270 else if ((r->action == ACTION_ASL_FILE) || (r->action == ACTION_ASL_DIR)) _act_store(m, r, msg); 1271 else if (r->action == ACTION_FILE) _act_file(m, r, msg); 1272 } 1273 } 1274 1275 return 0; 1276} 1277 1278void 1279asl_out_message(aslmsg msg) 1280{ 1281 OSAtomicIncrement32(&global.asl_queue_count); 1282 asl_msg_retain((asl_msg_t *)msg); 1283 1284 dispatch_async(asl_action_queue, ^{ 1285 int ignore = 0; 1286 const char *p; 1287 time_t now = time(NULL); 1288 asl_out_module_t *m = global.asl_out_module; 1289 1290 store_has_logged = false; 1291 1292 p = asl_get(msg, ASL_KEY_MODULE); 1293 if (p == NULL) 1294 { 1295 if ((action_asl_store_count == 0) || (asl_check_option(msg, ASL_OPT_STORE) == 1)) _send_to_asl_store(msg); 1296 1297 ignore = _asl_out_process_message(m, msg); 1298 if (ignore == 0) 1299 { 1300 if (m != NULL) m = m->next; 1301 while (m != NULL) 1302 { 1303 _asl_out_process_message(m, msg); 1304 m = m->next; 1305 } 1306 } 1307 } 1308 else 1309 { 1310 if (m != NULL) m = m->next; 1311 while (m != NULL) 1312 { 1313 if (!strcmp(p, m->name)) _asl_out_process_message(m, msg); 1314 m = m->next; 1315 } 1316 } 1317 1318 asl_msg_release((asl_msg_t *)msg); 1319 OSAtomicDecrement32(&global.asl_queue_count); 1320 1321 if ((now - sweep_time) >= IDLE_CLOSE) 1322 { 1323 _asl_action_close_idle_files(IDLE_CLOSE); 1324 sweep_time = now; 1325 } 1326 }); 1327} 1328 1329static char * 1330_asl_action_profile_test(asl_out_module_t *m, asl_out_rule_t *r) 1331{ 1332 const char *ident; 1333 asl_msg_t *profile; 1334 bool eval; 1335 1336 /* ident is first message key */ 1337 asl_msg_fetch((asl_msg_t *)r->query, 0, &ident, NULL, NULL); 1338 if (ident == NULL) 1339 { 1340 r->action = ACTION_NONE; 1341 return NULL; 1342 } 1343 1344 profile = configuration_profile_to_asl_msg(ident); 1345 eval = (asl_msg_cmp(r->query, profile) == 1); 1346 _act_out_set_param(m, r->options, eval); 1347 asl_msg_release(profile); 1348 1349 return strdup(ident); 1350} 1351 1352static const char * 1353_asl_action_file_test(asl_out_module_t *m, asl_out_rule_t *r) 1354{ 1355 const char *path; 1356 struct stat sb; 1357 int status; 1358 bool eval; 1359 1360 /* path is first message key */ 1361 asl_msg_fetch((asl_msg_t *)r->query, 0, &path, NULL, NULL); 1362 if (path == NULL) 1363 { 1364 r->action = ACTION_NONE; 1365 return NULL; 1366 } 1367 1368 memset(&sb, 0, sizeof(struct stat)); 1369 status = stat(path, &sb); 1370 eval = (status == 0); 1371 _act_out_set_param(m, r->options, eval); 1372 1373 return path; 1374} 1375 1376static void 1377_asl_action_handle_file_change_notification(int t) 1378{ 1379 asl_out_module_t *m; 1380 asl_out_rule_t *r; 1381 1382 for (m = global.asl_out_module; m != NULL; m = m->next) 1383 { 1384 for (r = m->ruleset; r != NULL; r = r->next) 1385 { 1386 if (r->action == ACTION_SET_FILE) 1387 { 1388 asl_action_set_param_data_t *spdata = (asl_action_set_param_data_t *)r->private; 1389 if ((spdata != NULL) && (spdata->token == t)) 1390 { 1391 _asl_action_file_test(m, r); 1392 return; 1393 } 1394 } 1395 else if (r->action == ACTION_SET_PLIST) 1396 { 1397 asl_action_set_param_data_t *spdata = (asl_action_set_param_data_t *)r->private; 1398 if ((spdata != NULL) && (spdata->token == t)) 1399 { 1400 char *str = _asl_action_profile_test(m, r); 1401 free(str); 1402 return; 1403 } 1404 } 1405 else if (r->action == ACTION_SET_PROF) 1406 { 1407 asl_action_set_param_data_t *spdata = (asl_action_set_param_data_t *)r->private; 1408 if ((spdata != NULL) && (spdata->token == t)) 1409 { 1410 char *str = _asl_action_profile_test(m, r); 1411 free(str); 1412 return; 1413 } 1414 } 1415 } 1416 } 1417 1418 asl_out_module_free(m); 1419} 1420 1421static void 1422_asl_action_post_process_rule(asl_out_module_t *m, asl_out_rule_t *r) 1423{ 1424 if ((m == NULL) || (r == NULL)) return; 1425 1426 if (m != global.asl_out_module) 1427 { 1428 /* check if any previous module has used this destination */ 1429 asl_out_module_t *n; 1430 bool search = true; 1431 1432 if ((r->dst != NULL) && (r->dst->path != NULL)) 1433 { 1434 for (n = global.asl_out_module; search && (n != NULL) && (n != m); n = n->next) 1435 { 1436 asl_out_rule_t *s; 1437 for (s = n->ruleset; search && (s != NULL); s = s->next) 1438 { 1439 if (s->action == ACTION_OUT_DEST) 1440 { 1441 if ((s->dst != NULL) && (s->dst->path != NULL) && (!strcmp(r->dst->path, s->dst->path))) 1442 { 1443 /* rule r of module m is using previously used dst of rule s of module n */ 1444 asl_out_dst_data_release(r->dst); 1445 r->dst = NULL; 1446 1447 if (r->action == ACTION_OUT_DEST) 1448 { 1449 char *str = NULL; 1450 asprintf(&str, "[Sender syslogd] [Level 5] [PID %u] [Message Configuration Notice:\nASL Module \"%s\" sharing output destination \"%s\" with ASL Module \"%s\".\nOutput parameters from ASL Module \"%s\" override any specified in ASL Module \"%s\".] [UID 0] [GID 0] [Facility syslog]", global.pid, m->name, s->dst->path, n->name, n->name, m->name); 1451 internal_log_message(str); 1452 free(str); 1453 } 1454 else 1455 { 1456 r->dst = asl_out_dst_data_retain(s->dst); 1457 } 1458 1459 search = false; 1460 } 1461 } 1462 } 1463 } 1464 } 1465 } 1466 1467 if (r->action == ACTION_SET_PARAM) 1468 { 1469 if (r->query == NULL) _act_out_set_param(m, r->options, true); 1470 } 1471 else if (r->action == ACTION_CLAIM) 1472 { 1473 /* becomes ACTION_SKIP in com.apple.asl config */ 1474 if (m != global.asl_out_module) 1475 { 1476 asl_out_rule_t *rule = (asl_out_rule_t *)calloc(1, sizeof(asl_out_rule_t)); 1477 if (rule != NULL) 1478 { 1479 char *str = NULL; 1480 asprintf(&str, "[Sender syslogd] [Level 5] [PID %u] [Message Configuration Notice:\nASL Module \"%s\" claims selected messages.\nThose messages may not appear in standard system log files or in the ASL database.] [UID 0] [GID 0] [Facility syslog]", global.pid, m->name); 1481 internal_log_message(str); 1482 free(str); 1483 1484 rule->query = asl_msg_copy(r->query); 1485 rule->action = ACTION_SKIP; 1486 rule->next = global.asl_out_module->ruleset; 1487 global.asl_out_module->ruleset = rule; 1488 } 1489 1490 /* 1491 * After adding ACTION_SKIP to com.apple.asl module, the claim becomes a no-op in this module 1492 * UNLESS the claim includes the option "only". In that case, the claim becomes a filter: 1493 * any messages that DO NOT match the claim are skipped by this module. 1494 */ 1495 if (r->options == NULL) r->action = ACTION_NONE; 1496 else if (strcmp(r->options, "only") != 0) r->action = ACTION_NONE; 1497 } 1498 } 1499 else if (r->action == ACTION_ASL_STORE) 1500 { 1501 action_asl_store_count++; 1502 } 1503 else if (r->action == ACTION_ASL_DIR) 1504 { 1505 if (r->dst->private == NULL) r->dst->private = (asl_action_store_data_t *)calloc(1, sizeof(asl_action_store_data_t)); 1506 } 1507 else if (r->action == ACTION_ASL_FILE) 1508 { 1509 if (r->dst->private == NULL)r->dst->private = (asl_action_store_data_t *)calloc(1, sizeof(asl_action_store_data_t)); 1510 } 1511 else if (r->action == ACTION_FILE) 1512 { 1513 if (r->dst->private == NULL) r->dst->private = (asl_action_file_data_t *)calloc(1, sizeof(asl_action_file_data_t)); 1514 if (r->dst->private != NULL) ((asl_action_file_data_t *)(r->dst->private))->fd = -1; 1515 } 1516 else if (r->action == ACTION_SET_PLIST) 1517 { 1518 char *ident =_asl_action_profile_test(m, r); 1519 char *notify_key = configuration_profile_create_notification_key(ident); 1520 free(ident); 1521 1522 if (notify_key != NULL) 1523 { 1524 int status, token; 1525 asl_action_set_param_data_t *spdata; 1526 1527 status = notify_register_dispatch(notify_key, &token, asl_action_queue, ^(int t){ 1528 _asl_action_handle_file_change_notification(t); 1529 }); 1530 1531 free(notify_key); 1532 1533 spdata = (asl_action_set_param_data_t *)calloc(1, sizeof(asl_action_set_param_data_t)); 1534 if (spdata == NULL) 1535 { 1536 notify_cancel(token); 1537 } 1538 else 1539 { 1540 spdata->token = token; 1541 r->private = spdata; 1542 } 1543 } 1544 } 1545 else if (r->action == ACTION_SET_PROF) 1546 { 1547 char *ident =_asl_action_profile_test(m, r); 1548 char *notify_key = configuration_profile_create_notification_key(ident); 1549 free(ident); 1550 1551 if (notify_key != NULL) 1552 { 1553 int status, token; 1554 asl_action_set_param_data_t *spdata; 1555 1556 status = notify_register_dispatch(notify_key, &token, asl_action_queue, ^(int t){ 1557 _asl_action_handle_file_change_notification(t); 1558 }); 1559 1560 free(notify_key); 1561 1562 spdata = (asl_action_set_param_data_t *)calloc(1, sizeof(asl_action_set_param_data_t)); 1563 if (spdata == NULL) 1564 { 1565 notify_cancel(token); 1566 } 1567 else 1568 { 1569 spdata->token = token; 1570 r->private = spdata; 1571 } 1572 } 1573 } 1574 else if (r->action == ACTION_SET_FILE) 1575 { 1576 char *notify_key; 1577 const char *path =_asl_action_file_test(m, r); 1578 1579 if (path != NULL) 1580 { 1581 asprintf(¬ify_key, "%s%s", NOTIFY_PATH_SERVICE, path); 1582 if (notify_key != NULL) 1583 { 1584 int status, token; 1585 asl_action_set_param_data_t *spdata; 1586 1587 status = notify_register_dispatch(notify_key, &token, asl_action_queue, ^(int t){ 1588 _asl_action_handle_file_change_notification(t); 1589 }); 1590 1591 free(notify_key); 1592 1593 spdata = (asl_action_set_param_data_t *)calloc(1, sizeof(asl_action_set_param_data_t)); 1594 if (spdata == NULL) 1595 { 1596 notify_cancel(token); 1597 } 1598 else 1599 { 1600 spdata->token = token; 1601 r->private = spdata; 1602 } 1603 } 1604 } 1605 } 1606} 1607 1608static void 1609_asl_action_configure() 1610{ 1611 asl_out_rule_t *r; 1612 asl_out_module_t *m; 1613 uint32_t flags = 0; 1614 1615 if (global.asl_out_module == NULL) global.asl_out_module = asl_out_module_init(); 1616 if (global.asl_out_module == NULL) return; 1617 1618 if (global.debug != 0) 1619 { 1620 FILE *dfp; 1621 if (global.debug_file == NULL) dfp = fopen(_PATH_SYSLOGD_LOG, "a"); 1622 else dfp = fopen(global.debug_file, "a"); 1623 if (dfp != NULL) 1624 { 1625 for (m = global.asl_out_module; m != NULL; m = m->next) 1626 { 1627 fprintf(dfp, "module: %s%s\n", (m->name == NULL) ? "<unknown>" : m->name, (m->flags & MODULE_FLAG_LOCAL) ? " (local)" : ""); 1628 asl_out_module_print(dfp, m); 1629 fprintf(dfp, "\n"); 1630 } 1631 fclose(dfp); 1632 } 1633 } 1634 1635 asldebug("%s: init\n", MY_ID); 1636 1637 action_asl_store_count = 0; 1638 1639 for (m = global.asl_out_module; m != NULL; m = m->next) 1640 { 1641 for (r = m->ruleset; r != NULL; r = r->next) 1642 { 1643 _asl_action_post_process_rule(m, r); 1644 if (r->dst != NULL) flags |= (r->dst->flags & (MODULE_FLAG_ROTATE | MODULE_FLAG_CRASHLOG)); 1645 } 1646 } 1647 1648 if (global.debug != 0) 1649 { 1650 FILE *dfp; 1651 if (global.debug_file == NULL) dfp = fopen(_PATH_SYSLOGD_LOG, "a"); 1652 else dfp = fopen(global.debug_file, "a"); 1653 if (dfp != NULL) 1654 { 1655 for (m = global.asl_out_module; m != NULL; m = m->next) 1656 { 1657 fprintf(dfp, "module: %s%s\n", (m->name == NULL) ? "<unknown>" : m->name, (m->flags & MODULE_FLAG_LOCAL) ? " (local)" : ""); 1658 asl_out_module_print(dfp, m); 1659 fprintf(dfp, "\n"); 1660 } 1661 fclose(dfp); 1662 } 1663 } 1664 1665 sweep_time = time(NULL); 1666 1667 if (flags & MODULE_FLAG_ROTATE) 1668 { 1669 _act_file_checkpoint_all(CHECKPOINT_TEST); 1670 if (checkpoint_timer == NULL) _start_cycling(); 1671 } 1672 1673#if TARGET_OS_EMBEDDED 1674 if (flags & MODULE_FLAG_CRASHLOG) _crashlog_sentinel_init(); 1675#endif 1676} 1677 1678int 1679asl_action_init(void) 1680{ 1681 static dispatch_once_t once; 1682 1683 dispatch_once(&once, ^{ 1684 asl_action_queue = dispatch_queue_create("ASL Action Queue", NULL); 1685#if TARGET_OS_EMBEDDED 1686 crashlog_queue = dispatch_queue_create("iOS CrashLog Queue", NULL); 1687 notify_register_dispatch(CRASH_MOVER_WILL_START_NOTIFICATION, &crashmover_token, asl_action_queue, ^(int unused){ 1688 if (crashmover_state == 0) 1689 { 1690 asldebug("CrashMover active: suspending crashlog queue and closing files\n"); 1691 crashmover_state = time(NULL); 1692 dispatch_suspend(crashlog_queue); 1693 _asl_action_close_idle_files(0); 1694 } 1695 }); 1696#endif 1697 }); 1698 1699 _asl_action_configure(); 1700 1701 return 0; 1702} 1703 1704/* 1705 * Free a module. 1706 */ 1707static void 1708_asl_action_free_modules(asl_out_module_t *m) 1709{ 1710 asl_out_rule_t *r; 1711 asl_out_module_t *x; 1712 1713 /* 1714 * asl_common frees a list of modules with asl_out_module_free. 1715 * This loop frees the private data attached some modules. 1716 */ 1717 for (x = m; x != NULL; x = x->next) 1718 { 1719 for (r = x->ruleset; r != NULL; r = r->next) 1720 { 1721 if ((r->action == ACTION_ASL_FILE) || (r->action == ACTION_ASL_DIR)) 1722 { 1723 if (r->dst != NULL) 1724 { 1725 _asl_action_store_data_free((asl_action_store_data_t *)r->dst->private); 1726 r->dst->private = NULL; 1727 } 1728 } 1729 else if (r->action == ACTION_FILE) 1730 { 1731 if (r->dst != NULL) 1732 { 1733 asl_action_file_data_t *fdata = (asl_action_file_data_t *)r->dst->private; 1734 if (fdata != NULL) 1735 { 1736 /* flush repeat message if necessary */ 1737 if (fdata->last_count > 0) _send_repeat_msg(r); 1738 _asl_action_file_data_free(fdata); 1739 r->dst->private = NULL; 1740 } 1741 } 1742 } 1743 else if (r->action == ACTION_SET_PLIST) 1744 { 1745 _asl_action_set_param_data_free((asl_action_set_param_data_t *)r->private); 1746 } 1747 else if (r->action == ACTION_SET_PROF) 1748 { 1749 _asl_action_set_param_data_free((asl_action_set_param_data_t *)r->private); 1750 } 1751 } 1752 } 1753 1754 asl_out_module_free(m); 1755} 1756 1757static int 1758_asl_action_close_internal(void) 1759{ 1760#if TARGET_OS_EMBEDDED 1761 dispatch_source_cancel(crashlog_sentinel_src); 1762 dispatch_release(crashlog_sentinel_src); 1763 crashlog_sentinel_src = NULL; 1764 close(crashlog_sentinel_fd); 1765 if (crashmover_state != 0) 1766 { 1767 dispatch_resume(crashlog_queue); 1768 crashmover_state = 0; 1769 } 1770 1771 /* wait for the crashlog_queue to flush before _asl_action_free_modules() */ 1772 dispatch_sync(crashlog_queue, ^{ crashlog_sentinel_fd = -1; }); 1773#endif 1774 1775 _asl_action_free_modules(global.asl_out_module); 1776 global.asl_out_module = NULL; 1777 sweep_time = time(NULL); 1778 1779 return 0; 1780} 1781 1782static void 1783_asl_action_close_idle_files(time_t idle_time) 1784{ 1785 asl_out_module_t *m; 1786 time_t now = time(NULL); 1787 1788 for (m = global.asl_out_module; m != NULL; m = m->next) 1789 { 1790 asl_out_rule_t *r; 1791 1792 for (r = m->ruleset; r != NULL; r = r->next) 1793 { 1794 if (idle_time == 0) 1795 { 1796 if ((r->dst != NULL) && (r->dst->flags & MODULE_FLAG_CRASHLOG)) 1797 { 1798 _act_dst_close(r); 1799 if (r->action != ACTION_ASL_DIR) asl_out_dst_checkpoint(r->dst, CHECKPOINT_FORCE); 1800 } 1801 } 1802 else if ((r->action == ACTION_ASL_DIR) || (r->action == ACTION_ASL_FILE)) 1803 { 1804 if (r->dst != NULL) 1805 { 1806 asl_action_store_data_t *sdata = (asl_action_store_data_t *)r->dst->private; 1807 if ((sdata != NULL) && (sdata->store != NULL) && ((now - sdata->last_time) >= idle_time)) _act_dst_close(r); 1808 } 1809 } 1810 else if (r->action == ACTION_FILE) 1811 { 1812 if (r->dst != NULL) 1813 { 1814 asl_action_file_data_t *fdata = (asl_action_file_data_t *)r->dst->private; 1815 if ((fdata != NULL) && (fdata->fd >= 0) && ((now - fdata->last_time) >= idle_time)) _act_dst_close(r); 1816 } 1817 } 1818 } 1819 } 1820} 1821 1822int 1823asl_action_close(void) 1824{ 1825 dispatch_async(asl_action_queue, ^{ 1826 _asl_action_close_internal(); 1827 }); 1828 1829 return 0; 1830} 1831 1832int 1833asl_action_reset(void) 1834{ 1835 dispatch_async(asl_action_queue, ^{ 1836 _asl_action_close_internal(); 1837 asl_action_init(); 1838 }); 1839 1840 return 0; 1841} 1842 1843asl_out_module_t * 1844_asl_action_module_with_name(const char *name) 1845{ 1846 asl_out_module_t *m; 1847 1848 if (global.asl_out_module == NULL) return NULL; 1849 if (name == NULL) return global.asl_out_module; 1850 1851 for (m = global.asl_out_module; m != NULL; m = m->next) 1852 { 1853 if ((m->name != NULL) && (!strcmp(m->name, name))) return m; 1854 } 1855 1856 return NULL; 1857} 1858 1859/* 1860 * called from control_message 1861 * Used to control modules dynamically. 1862 * Line format "@ module param [value ...]" 1863 * 1864 * Note this is synchronous on asl_action queue. 1865 */ 1866int 1867asl_action_control_set_param(const char *s) 1868{ 1869 __block char **l; 1870 __block char *p; 1871 uint32_t count = 0; 1872 1873 if (s == NULL) return -1; 1874 if (s[0] == '\0') return 0; 1875 1876 /* skip '@' and whitespace */ 1877 if (*s == '@') s++; 1878 while ((*s == ' ') || (*s == '\t')) s++; 1879 1880 l = explode(s, " \t"); 1881 if (l != NULL) for (count = 0; l[count] != NULL; count++); 1882 1883 /* at least 2 parameters (l[0] = module, l[1] = param) required */ 1884 if (count < 2) return -1; 1885 1886 if (global.asl_out_module == NULL) 1887 { 1888 asldebug("asl_action_control_set_param: no modules loaded\n"); 1889 return -1; 1890 } 1891 1892 /* create / modify a module */ 1893 if ((!strcasecmp(l[1], "define")) && (strcmp(l[0], "*"))) 1894 { 1895 p = strdup(s); 1896 if (p == NULL) 1897 { 1898 asldebug("asl_action_control_set_param: memory allocation failed\n"); 1899 return -1; 1900 } 1901 1902 dispatch_sync(asl_action_queue, ^{ 1903 asl_out_module_t *m; 1904 asl_out_rule_t *r; 1905 1906 /* skip name, whitespace, "define" */ 1907 while ((*p != ' ') && (*p != '\t')) p++; 1908 while ((*p == ' ') || (*p == '\t')) p++; 1909 while ((*p != ' ') && (*p != '\t')) p++; 1910 1911 m = _asl_action_module_with_name(l[0]); 1912 if (m == NULL) 1913 { 1914 asl_out_module_t *x; 1915 1916 m = asl_out_module_new(l[0]); 1917 for (x = global.asl_out_module; x->next != NULL; x = x->next); 1918 x->next = m; 1919 } 1920 1921 r = asl_out_module_parse_line(m, p); 1922 if (r != NULL) 1923 { 1924 _asl_action_post_process_rule(m, r); 1925 if ((r->dst != NULL) && (r->dst->flags & MODULE_FLAG_ROTATE)) 1926 { 1927 _act_file_checkpoint_all(CHECKPOINT_TEST); 1928 if (checkpoint_timer == NULL) _start_cycling(); 1929 } 1930 } 1931 }); 1932 1933 free(p); 1934 free_string_list(l); 1935 return 0; 1936 } 1937 1938 dispatch_sync(asl_action_queue, ^{ 1939 uint32_t intval; 1940 int do_all = 0; 1941 asl_out_module_t *m; 1942 1943 if (!strcmp(l[0], "*")) 1944 { 1945 do_all = 1; 1946 m = _asl_action_module_with_name(NULL); 1947 } 1948 else 1949 { 1950 m = _asl_action_module_with_name(l[0]); 1951 } 1952 1953 while (m != NULL) 1954 { 1955 if (!strcasecmp(l[1], "enable")) 1956 { 1957 intval = 1; 1958 1959 /* don't do enable for ASL_MODULE_NAME if input name is "*" */ 1960 if ((do_all == 0) || (strcmp(m->name, ASL_MODULE_NAME))) 1961 { 1962 /* @ module enable {0|1} */ 1963 if (count > 2) intval = atoi(l[2]); 1964 1965 if (intval == 0) m->flags &= ~MODULE_FLAG_ENABLED; 1966 else m->flags |= MODULE_FLAG_ENABLED; 1967 } 1968 } 1969 else if (!strcasecmp(l[1], "checkpoint")) 1970 { 1971 /* @ module checkpoint [file] */ 1972 if (count > 2) _act_file_checkpoint(m, l[2], CHECKPOINT_FORCE); 1973 else _act_file_checkpoint(m, NULL, CHECKPOINT_FORCE); 1974 } 1975 1976 if (do_all == 1) m = m->next; 1977 else m = NULL; 1978 } 1979 1980 }); 1981 1982 free_string_list(l); 1983 return 0; 1984} 1985 1986int 1987asl_action_file_checkpoint(const char *module, const char *path) 1988{ 1989 /* Note this is synchronous on asl_action queue */ 1990 dispatch_sync(asl_action_queue, ^{ 1991 asl_out_module_t *m = _asl_action_module_with_name(module); 1992 _act_file_checkpoint(m, path, CHECKPOINT_FORCE); 1993 }); 1994 1995 return 0; 1996} 1997