1/* 2 * Copyright (c) 2004-2011 Apple Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24#include <TargetConditionals.h> 25 26#if TARGET_IPHONE_SIMULATOR 27struct _not_empty; 28#else 29 30#include <sys/types.h> 31#include <sys/stat.h> 32#include <sys/socket.h> 33#include <sys/un.h> 34#include <sys/uio.h> 35#include <stdio.h> 36#include <stdlib.h> 37#include <string.h> 38#include <unistd.h> 39#include <ctype.h> 40#include <fcntl.h> 41#include <errno.h> 42#include <netdb.h> 43#include <pthread.h> 44#include <notify.h> 45#include "daemon.h" 46 47#define MY_ID "bsd_out" 48 49#define _PATH_WALL "/usr/bin/wall" 50#define ASL_KEY_FACILITY "Facility" 51#define FACILITY_KERNEL "kern" 52#define _PATH_CONSOLE "/dev/console" 53 54#define DST_TYPE_NONE 0 55#define DST_TYPE_FILE 1 56#define DST_TYPE_CONS 2 57#define DST_TYPE_SOCK 3 58#define DST_TYPE_WALL 4 59#define DST_TYPE_NOTE 5 60 61#define CLOSE_ON_IDLE_SEC 300 62 63static dispatch_queue_t bsd_out_queue; 64static dispatch_source_t bsd_idle_timer; 65 66struct config_rule 67{ 68 uint32_t count; 69 char *dst; 70 int fd; 71 int type; 72 struct sockaddr *addr; 73 char **facility; 74 uint32_t *fac_prefix_len; 75 int *pri; 76 uint32_t last_hash; 77 uint32_t last_count; 78 time_t last_time; 79 dispatch_source_t dup_timer; 80 char *last_msg; 81 TAILQ_ENTRY(config_rule) entries; 82}; 83 84static TAILQ_HEAD(cr, config_rule) bsd_out_rule; 85 86extern uint32_t asl_core_string_hash(const char *s, uint32_t inlen); 87 88static int 89_level_for_name(const char *name) 90{ 91 if (name == NULL) return -1; 92 93 if (!strcasecmp(name, "emerg")) return ASL_LEVEL_EMERG; 94 if (!strcasecmp(name, "panic")) return ASL_LEVEL_EMERG; 95 if (!strcasecmp(name, "alert")) return ASL_LEVEL_ALERT; 96 if (!strcasecmp(name, "crit")) return ASL_LEVEL_CRIT; 97 if (!strcasecmp(name, "err")) return ASL_LEVEL_ERR; 98 if (!strcasecmp(name, "error")) return ASL_LEVEL_ERR; 99 if (!strcasecmp(name, "warn")) return ASL_LEVEL_WARNING; 100 if (!strcasecmp(name, "warning")) return ASL_LEVEL_WARNING; 101 if (!strcasecmp(name, "notice")) return ASL_LEVEL_NOTICE; 102 if (!strcasecmp(name, "info")) return ASL_LEVEL_INFO; 103 if (!strcasecmp(name, "debug")) return ASL_LEVEL_DEBUG; 104 if (!strcmp(name, "*")) return ASL_LEVEL_DEBUG; 105 106 /* special case */ 107 if (!strcasecmp(name, "none")) return -2; 108 109 return -1; 110} 111 112static int 113_syslog_dst_open(struct config_rule *r) 114{ 115 int i; 116 char *node, *serv; 117 struct addrinfo hints, *gai, *ai; 118 119 if (r == NULL) return -1; 120 if (r->fd != -1) return 0; 121 122 if (r->dst[0] == '/') 123 { 124 r->fd = open(r->dst, O_WRONLY | O_APPEND | O_CREAT | O_NOCTTY, 0644); 125 if (r->fd < 0) 126 { 127 asldebug("%s: open failed for file: %s (%s)\n", MY_ID, r->dst, strerror(errno)); 128 return -1; 129 } 130 131 r->type = DST_TYPE_FILE; 132 if (!strcmp(r->dst, _PATH_CONSOLE)) r->type = DST_TYPE_CONS; 133 134 return 0; 135 } 136 137 if (r->dst[0] == '!') 138 { 139 r->type = DST_TYPE_NOTE; 140 r->fd = -1; 141 return 0; 142 } 143 144 if (r->dst[0] == '@') 145 { 146 node = strdup(r->dst + 1); 147 if (node == NULL) return -1; 148 149 serv = NULL; 150 serv = strrchr(node, ':'); 151 if (serv != NULL) *serv++ = '\0'; 152 else serv = "syslog"; 153 154 memset(&hints, 0, sizeof(hints)); 155 hints.ai_family = PF_UNSPEC; 156 hints.ai_socktype = SOCK_DGRAM; 157 i = getaddrinfo(node, serv, &hints, &gai); 158 free(node); 159 if (i != 0) 160 { 161 asldebug("%s: getaddrinfo failed for node %s service %s: (%s)\n", MY_ID, node, serv, gai_strerror(i)); 162 return -1; 163 } 164 165 for (ai = gai; ai != NULL; ai = ai->ai_next) 166 { 167 r->fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); 168 if (r->fd < 0) continue; 169 170 r->addr = (struct sockaddr *)malloc(ai->ai_addrlen); 171 if (r->addr == NULL) return -1; 172 173 memcpy(r->addr, ai->ai_addr, ai->ai_addrlen); 174 175 break; 176 } 177 178 freeaddrinfo(gai); 179 180 if (r->fd < 0) 181 { 182 asldebug("%s: connection failed for %s\n", MY_ID, (r->dst) + 1); 183 free(r->addr); 184 r->addr = NULL; 185 return -1; 186 } 187 188 if (fcntl(r->fd, F_SETFL, O_NONBLOCK) < 0) 189 { 190 close(r->fd); 191 r->fd = -1; 192 asldebug("%s: couldn't set O_NONBLOCK for fd %d: %s\n", MY_ID, r->fd, strerror(errno)); 193 free(r->addr); 194 r->addr = NULL; 195 return -1; 196 } 197 198 r->type = DST_TYPE_SOCK; 199 return 0; 200 201 } 202 203 if (strcmp(r->dst, "*") == 0) 204 { 205 r->type = DST_TYPE_WALL; 206 r->fd = -1; 207 return 0; 208 } 209 210 /* Can't deal with dst! */ 211 asldebug("%s: unsupported / unknown output name: %s\n", MY_ID, r->dst); 212 return -1; 213} 214 215static void 216_syslog_dst_close(struct config_rule *r) 217{ 218 if (r == NULL) return; 219 220 if (r->addr != NULL) 221 { 222 free(r->addr); 223 r->addr = NULL; 224 } 225 226 switch (r->type) 227 { 228 case DST_TYPE_FILE: 229 case DST_TYPE_CONS: 230 { 231 if (r->fd >= 0) close(r->fd); 232 r->fd = -1; 233 break; 234 } 235 236 case DST_TYPE_SOCK: 237 { 238 if (r->fd >= 0) close(r->fd); 239 r->fd = -1; 240 break; 241 } 242 243 case DST_TYPE_NONE: 244 case DST_TYPE_WALL: 245 case DST_TYPE_NOTE: 246 default: 247 { 248 /* do nothing */ 249 return; 250 } 251 } 252} 253 254static char * 255_clean_facility_name(char *s) 256{ 257 uint32_t len; 258 char *p, *out; 259 260 if (s == NULL) return NULL; 261 len = strlen(s); 262 if (len == 0) return NULL; 263 264 p = s; 265 266 if ((*s == '\'') || (*s == '"')) 267 { 268 len--; 269 p++; 270 if (p[len - 1] == *s) len --; 271 } 272 273 out = calloc(1, len + 1); 274 if (out == NULL) return NULL; 275 276 memcpy(out, p, len); 277 return out; 278} 279 280static int 281_parse_line(char *s) 282{ 283 char **semi, **comma, *star; 284 int i, j, n, lasts, lastc, pri; 285 struct config_rule *out; 286 287 if (s == NULL) return -1; 288 while ((*s == ' ') || (*s == '\t')) s++; 289 if (*s == '#') return -1; 290 291 semi = explode(s, "; \t"); 292 293 if (semi == NULL) return -1; 294 out = (struct config_rule *)calloc(1, sizeof(struct config_rule)); 295 if (out == NULL) return -1; 296 out->fd = -1; 297 298 n = 0; 299 lasts = -1; 300 for (i = 0; semi[i] != NULL; i++) 301 { 302 if (semi[i][0] == '\0') continue; 303 n++; 304 lasts = i; 305 } 306 307 out->dst = strdup(semi[lasts]); 308 if (out->dst == NULL) return -1; 309 310 for (i = 0; i < lasts; i++) 311 { 312 if (semi[i][0] == '\0') continue; 313 comma = explode(semi[i], ",."); 314 lastc = -1; 315 for (j = 0; comma[j] != NULL; j++) 316 { 317 if (comma[j][0] == '\0') continue; 318 lastc = j; 319 } 320 321 for (j = 0; j < lastc; j++) 322 { 323 if (comma[j][0] == '\0') continue; 324 pri = _level_for_name(comma[lastc]); 325 if (pri == -1) continue; 326 327 if (out->count == 0) 328 { 329 out->facility = (char **)calloc(1, sizeof(char *)); 330 out->fac_prefix_len = (uint32_t *)calloc(1, sizeof(uint32_t)); 331 out->pri = (int *)calloc(1, sizeof(int)); 332 } 333 else 334 { 335 out->facility = (char **)reallocf(out->facility, (out->count + 1) * sizeof(char *)); 336 out->fac_prefix_len = (uint32_t *)reallocf(out->fac_prefix_len, (out->count + 1) * sizeof(uint32_t)); 337 out->pri = (int *)reallocf(out->pri, (out->count + 1) * sizeof(int)); 338 } 339 340 if (out->facility == NULL) return -1; 341 if (out->fac_prefix_len == NULL) return -1; 342 if (out->pri == NULL) return -1; 343 344 out->facility[out->count] = _clean_facility_name(comma[j]); 345 if (out->facility[out->count] == NULL) return -1; 346 347 out->fac_prefix_len[out->count] = 0; 348 star = strchr(out->facility[out->count], '*'); 349 if (star != NULL) out->fac_prefix_len[out->count] = (uint32_t)(star - out->facility[out->count]); 350 351 out->pri[out->count] = pri; 352 out->count++; 353 } 354 355 free_string_list(comma); 356 } 357 358 free_string_list(semi); 359 360 TAILQ_INSERT_TAIL(&bsd_out_rule, out, entries); 361 362 return 0; 363} 364 365static int 366_bsd_send_repeat_msg(struct config_rule *r) 367{ 368 char vt[32], *msg; 369 time_t tick; 370 int len, status; 371 372 if (r == NULL) return -1; 373 if (r->type != DST_TYPE_FILE) return 0; 374 if (r->last_count == 0) return 0; 375 376 /* stop the timer */ 377 dispatch_suspend(r->dup_timer); 378 379 tick = time(NULL); 380 memset(vt, 0, sizeof(vt)); 381 ctime_r(&tick, vt); 382 vt[19] = '\0'; 383 384 msg = NULL; 385 asprintf(&msg, "%s: --- last message repeated %u time%s ---\n", vt + 4, r->last_count, (r->last_count == 1) ? "" : "s"); 386 r->last_count = 0; 387 if (msg == NULL) return -1; 388 389 len = strlen(msg); 390 status = write(r->fd, msg, len); 391 if ((status < 0) || (status < len)) 392 { 393 asldebug("%s: error writing repeat message (%s): %s\n", MY_ID, r->dst, strerror(errno)); 394 395 /* Try re-opening the file (once) and write again */ 396 close(r->fd); 397 r->fd = open(r->dst, O_WRONLY | O_APPEND | O_CREAT | O_NOCTTY, 0644); 398 if (r->fd < 0) 399 { 400 asldebug("%s: re-open failed for file: %s (%s)\n", MY_ID, r->dst, strerror(errno)); 401 free(msg); 402 return -1; 403 } 404 405 status = write(r->fd, msg, len); 406 if ((status < 0) || (status < len)) 407 { 408 asldebug("%s: error re-writing message (%s): %s\n", MY_ID, r->dst, strerror(errno)); 409 free(msg); 410 return -1; 411 } 412 } 413 414 free(msg); 415 return 0; 416} 417 418static int 419_bsd_send(asl_msg_t *msg, struct config_rule *r, char **out, char **fwd, time_t now) 420{ 421 char *sf, *outmsg; 422 const char *vlevel, *vfacility; 423 size_t outlen; 424 int pf, fc, status, is_dup, do_write; 425 uint32_t msg_hash, n; 426 427 if (out == NULL) return -1; 428 if (fwd == NULL) return -1; 429 if (r == NULL) return -1; 430 431 _syslog_dst_open(r); 432 433 if (r->type == DST_TYPE_NOTE) 434 { 435 notify_post(r->dst+1); 436 return 0; 437 } 438 439 msg_hash = 0; 440 outmsg = NULL; 441 442 /* Build output string if it hasn't been built by a previous rule-match */ 443 if (*out == NULL) 444 { 445 *out = asl_format_message((asl_msg_t *)msg, ASL_MSG_FMT_BSD, ASL_TIME_FMT_LCL, ASL_ENCODE_SAFE, &n); 446 if (*out == NULL) return -1; 447 } 448 449 /* check if message is a duplicate of the last message, and inside the dup time window */ 450 is_dup = 0; 451 if ((global.bsd_max_dup_time > 0) && (*out != NULL) && (r->last_msg != NULL)) 452 { 453 msg_hash = asl_core_string_hash(*out + 16, strlen(*out + 16)); 454 if ((r->last_hash == msg_hash) && (!strcmp(r->last_msg, *out + 16))) 455 { 456 if ((now - r->last_time) < global.bsd_max_dup_time) is_dup = 1; 457 } 458 } 459 460 if ((*fwd == NULL) && (r->type == DST_TYPE_SOCK)) 461 { 462 pf = 7; 463 vlevel = asl_msg_get_val_for_key(msg, ASL_KEY_LEVEL); 464 if (vlevel != NULL) pf = atoi(vlevel); 465 466 fc = asl_syslog_faciliy_name_to_num(asl_msg_get_val_for_key(msg, ASL_KEY_FACILITY)); 467 if (fc > 0) pf |= fc; 468 469 sf = NULL; 470 asprintf(&sf, "<%d>%s", pf, *out); 471 if (sf == NULL) return -1; 472 473 *fwd = sf; 474 } 475 476 if (r->type == DST_TYPE_SOCK) outlen = strlen(*fwd); 477 else outlen = strlen(*out); 478 479 if ((r->type == DST_TYPE_FILE) || (r->type == DST_TYPE_CONS)) 480 { 481 /* 482 * If current message is NOT a duplicate and r->last_count > 0 483 * we need to write a "last message was repeated N times" log entry 484 */ 485 if ((r->type == DST_TYPE_FILE) && (is_dup == 0) && (r->last_count > 0)) _bsd_send_repeat_msg(r); 486 487 do_write = 1; 488 489 /* 490 * Special case for kernel messages. 491 * Don't write kernel messages to /dev/console. 492 * The kernel printf routine already sends them to /dev/console 493 * so writing them here would cause duplicates. 494 */ 495 vfacility = asl_msg_get_val_for_key(msg, ASL_KEY_FACILITY); 496 if ((vfacility != NULL) && (!strcmp(vfacility, FACILITY_KERNEL)) && (r->type == DST_TYPE_CONS)) do_write = 0; 497 if ((do_write == 1) && (r->type == DST_TYPE_FILE) && (is_dup == 1)) 498 { 499 do_write = 0; 500 501 if (r->dup_timer == NULL) 502 { 503 /* create a timer to flush dups on this file */ 504 r->dup_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, bsd_out_queue); 505 dispatch_source_set_event_handler(r->dup_timer, ^{ _bsd_send_repeat_msg(r); }); 506 } 507 508 if (r->last_count == 0) 509 { 510 /* start the timer */ 511 dispatch_source_set_timer(r->dup_timer, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * global.bsd_max_dup_time), DISPATCH_TIME_FOREVER, 0); 512 dispatch_resume(r->dup_timer); 513 } 514 } 515 516 if (do_write == 0) status = outlen; 517 else status = write(r->fd, *out, outlen); 518 519 if ((status < 0) || (status < outlen)) 520 { 521 asldebug("%s: error writing message (%s): %s\n", MY_ID, r->dst, strerror(errno)); 522 523 /* Try re-opening the file (once) and write again */ 524 close(r->fd); 525 r->fd = open(r->dst, O_WRONLY | O_APPEND | O_CREAT | O_NOCTTY, 0644); 526 if (r->fd < 0) 527 { 528 asldebug("%s: re-open failed for file: %s (%s)\n", MY_ID, r->dst, strerror(errno)); 529 return -1; 530 } 531 532 status = write(r->fd, *out, outlen); 533 if ((status < 0) || (status < outlen)) 534 { 535 asldebug("%s: error re-writing message (%s): %s\n", MY_ID, r->dst, strerror(errno)); 536 } 537 } 538 } 539 else if ((r->type == DST_TYPE_SOCK) && (r->addr != NULL)) 540 { 541 status = sendto(r->fd, *fwd, outlen, 0, r->addr, r->addr->sa_len); 542 if (status < 0) asldebug("%s: error sending message (%s): %s\n", MY_ID, r->dst, strerror(errno)); 543 } 544 else if (r->type == DST_TYPE_WALL) 545 { 546#if !TARGET_OS_EMBEDDED 547 FILE *pw = popen(_PATH_WALL, "w"); 548 if (pw < 0) 549 { 550 asldebug("%s: error sending wall message: %s\n", MY_ID, strerror(errno)); 551 return -1; 552 } 553 554 fprintf(pw, "%s", *out); 555 pclose(pw); 556#endif 557 } 558 559 if (is_dup == 1) 560 { 561 r->last_count++; 562 } 563 else 564 { 565 free(r->last_msg); 566 r->last_msg = NULL; 567 568 if (*out != NULL) r->last_msg = strdup(*out + 16); 569 570 r->last_hash = msg_hash; 571 r->last_count = 0; 572 r->last_time = now; 573 } 574 575 return 0; 576} 577 578static int 579_bsd_rule_match(asl_msg_t *msg, struct config_rule *r) 580{ 581 uint32_t i, test, f; 582 int32_t pri; 583 const char *val; 584 585 if (msg == NULL) return 0; 586 if (r == NULL) return 0; 587 if (r->count == 0) return 0; 588 589 test = 0; 590 591 for (i = 0; i < r->count; i++) 592 { 593 if (r->pri[i] == -1) continue; 594 595 if ((test == 1) && (r->pri[i] >= 0)) continue; 596 if ((test == 0) && (r->pri[i] == -2)) continue; 597 598 f = 0; 599 val = asl_msg_get_val_for_key(msg, ASL_KEY_FACILITY); 600 601 if (strcmp(r->facility[i], "*") == 0) 602 { 603 f = 1; 604 } 605 else if ((r->fac_prefix_len[i] > 0) && (strncasecmp(r->facility[i], val, r->fac_prefix_len[i]) == 0)) 606 { 607 f = 1; 608 } 609 else if ((val != NULL) && (strcasecmp(r->facility[i], val) == 0)) 610 { 611 f = 1; 612 } 613 614 if (f == 0) continue; 615 616 /* Turn off matching facility with priority "none" */ 617 if (r->pri[i] == -2) 618 { 619 test = 0; 620 continue; 621 } 622 623 val = asl_msg_get_val_for_key(msg, ASL_KEY_LEVEL); 624 if (val == NULL) continue; 625 626 pri = atoi(val); 627 if (pri < 0) continue; 628 629 if (pri <= r->pri[i]) test = 1; 630 } 631 632 return test; 633} 634 635static int 636_bsd_match_and_send(asl_msg_t *msg) 637{ 638 struct config_rule *r; 639 char *out, *fwd; 640 time_t now; 641 642 if (msg == NULL) return -1; 643 644 out = NULL; 645 fwd = NULL; 646 647 now = time(NULL); 648 649 for (r = bsd_out_rule.tqh_first; r != NULL; r = r->entries.tqe_next) 650 { 651 if (_bsd_rule_match(msg, r) == 1) _bsd_send(msg, r, &out, &fwd, now); 652 } 653 654 free(out); 655 free(fwd); 656 657 return 0; 658} 659 660void 661bsd_out_message(asl_msg_t *msg) 662{ 663 if (msg == NULL) return; 664 665 OSAtomicIncrement32(&global.bsd_queue_count); 666 asl_msg_retain((asl_msg_t *)msg); 667 668 dispatch_async(bsd_out_queue, ^{ 669 _bsd_match_and_send(msg); 670 asl_msg_release((asl_msg_t *)msg); 671 OSAtomicDecrement32(&global.bsd_queue_count); 672 }); 673} 674 675static void 676_bsd_close_idle_files() 677{ 678 time_t now; 679 struct config_rule *r; 680 uint64_t delta; 681 682 now = time(NULL); 683 684 for (r = bsd_out_rule.tqh_first; r != NULL; r = r->entries.tqe_next) 685 { 686 /* only applies to files */ 687 if (r->type != DST_TYPE_FILE) continue; 688 689 /* 690 * If the last message repeat count is non-zero, a _bsd_flush_duplicates() 691 * call will occur within 30 seconds. Don't bother closing the file. 692 */ 693 if (r->last_count > 0) continue; 694 695 delta = now - r->last_time; 696 if (delta > CLOSE_ON_IDLE_SEC) _syslog_dst_close(r); 697 } 698} 699 700static int 701_parse_config_file(const char *confname) 702{ 703 FILE *cf; 704 char *line; 705 706 cf = fopen(confname, "r"); 707 if (cf == NULL) return 1; 708 709 while (NULL != (line = get_line_from_file(cf))) 710 { 711 _parse_line(line); 712 free(line); 713 } 714 715 fclose(cf); 716 717 return 0; 718} 719 720int 721bsd_out_init(void) 722{ 723 static dispatch_once_t once; 724 725 asldebug("%s: init\n", MY_ID); 726 727 TAILQ_INIT(&bsd_out_rule); 728 _parse_config_file(_PATH_SYSLOG_CONF); 729 730 dispatch_once(&once, ^{ 731 bsd_out_queue = dispatch_queue_create("BSD Out Queue", NULL); 732 733 /* start a timer to close idle files */ 734 bsd_idle_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, bsd_out_queue); 735 dispatch_source_set_event_handler(bsd_idle_timer, ^{ _bsd_close_idle_files(); }); 736 dispatch_source_set_timer(bsd_idle_timer, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * CLOSE_ON_IDLE_SEC), NSEC_PER_SEC * CLOSE_ON_IDLE_SEC, 0); 737 dispatch_resume(bsd_idle_timer); 738 }); 739 740 return 0; 741} 742 743static int 744_bsd_out_close_internal(void) 745{ 746 struct config_rule *r, *n; 747 int i; 748 749 n = NULL; 750 for (r = bsd_out_rule.tqh_first; r != NULL; r = n) 751 { 752 n = r->entries.tqe_next; 753 754 if (r->dup_timer != NULL) 755 { 756 if (r->last_count > 0) _bsd_send_repeat_msg(r); 757 dispatch_source_cancel(r->dup_timer); 758 dispatch_resume(r->dup_timer); 759 dispatch_release(r->dup_timer); 760 } 761 762 free(r->dst); 763 free(r->addr); 764 free(r->last_msg); 765 free(r->fac_prefix_len); 766 free(r->pri); 767 768 if (r->fd >= 0) close(r->fd); 769 770 if (r->facility != NULL) 771 { 772 for (i = 0; i < r->count; i++) 773 free(r->facility[i]); 774 775 free(r->facility); 776 } 777 778 779 TAILQ_REMOVE(&bsd_out_rule, r, entries); 780 free(r); 781 } 782 783 return 0; 784} 785 786int 787bsd_out_close(void) 788{ 789 dispatch_async(bsd_out_queue, ^{ 790 _bsd_out_close_internal(); 791 }); 792 793 return 0; 794} 795 796int 797bsd_out_reset(void) 798{ 799 dispatch_async(bsd_out_queue, ^{ 800 _bsd_out_close_internal(); 801 bsd_out_init(); 802 }); 803 804 return 0; 805} 806 807#endif /* !TARGET_IPHONE_SIMULATOR */ 808