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