1/* 2 * Copyright (c) 2004-2012 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 <netinet/in.h> 34#include <netinet/tcp.h> 35#include <arpa/inet.h> 36#include <sys/un.h> 37#include <sys/uio.h> 38#include <stdio.h> 39#include <stdlib.h> 40#include <string.h> 41#include <fcntl.h> 42#include <errno.h> 43#include <unistd.h> 44#include <netdb.h> 45#include <pthread.h> 46#include <notify.h> 47#include "daemon.h" 48 49#define forever for(;;) 50 51#define MY_ID "remote" 52#define MAXLINE 4096 53#define LOCKDOWN_PATH "/var/run/lockdown" 54#define SYSLOG_SOCK_PATH "/var/run/lockdown/syslog.sock" 55#define ASL_REMOTE_PORT 203 56 57#define PRINT_STD 0 58#define PRINT_RAW 1 59 60#define WATCH_OFF 0 61#define WATCH_LOCKDOWN_START 1 62#define WATCH_RUN 2 63 64#define SESSION_FLAGS_LOCKDOWN 0x00000001 65 66#define MAXSOCK 1 67 68static int rfd4 = -1; 69static int rfd6 = -1; 70static int rfdl = -1; 71 72static dispatch_source_t in_src_local; 73static dispatch_source_t in_src_tcp; 74static dispatch_queue_t in_queue; 75 76#ifdef NSS32 77typedef uint32_t notify_state_t; 78extern int notify_set_state(int, notify_state_t); 79#else 80typedef uint64_t notify_state_t; 81#endif 82 83extern char *asl_list_to_string(asl_search_result_t *list, uint32_t *outlen); 84extern size_t asl_memory_size(asl_memory_t *s); 85extern uint32_t db_query(aslresponse query, aslresponse *res, uint64_t startid, int count, int flags, uint64_t *lastid, int32_t ruid, int32_t rgid, int raccess); 86 87extern void add_lockdown_session(int fd); 88extern void remove_lockdown_session(int fd); 89 90#define SESSION_WRITE(f,x) if (write(f, x, strlen(x)) < 0) goto exit_session 91 92typedef struct 93{ 94 int sock; 95 uint32_t flags; 96} session_args_t; 97 98uint32_t 99remote_db_size(uint32_t sel) 100{ 101 if (sel == DB_TYPE_FILE) return global.db_file_max; 102 if (sel == DB_TYPE_MEMORY) return global.db_memory_max; 103 if (sel == DB_TYPE_MINI) return global.db_mini_max; 104 return 0; 105} 106 107uint32_t 108remote_db_set_size(uint32_t sel, uint32_t size) 109{ 110 if (sel == DB_TYPE_FILE) global.db_file_max = size; 111 if (sel == DB_TYPE_MEMORY) global.db_memory_max = size; 112 if (sel == DB_TYPE_MINI) global.db_mini_max = size; 113 return 0; 114} 115 116aslmsg 117remote_db_stats(uint32_t sel) 118{ 119 aslmsg m; 120 m = NULL; 121 122 if (sel == DB_TYPE_FILE) asl_store_statistics(global.file_db, &m); 123 if (sel == DB_TYPE_MEMORY) asl_memory_statistics(global.memory_db, &m); 124 if (sel == DB_TYPE_MINI) asl_mini_memory_statistics(global.mini_db, &m); 125 return m; 126} 127 128void 129session(void *x) 130{ 131 int i, s, wfd, status, pfmt, watch, wtoken, nfd, do_prompt; 132 aslresponse res; 133 asl_search_result_t ql; 134 uint32_t outlen; 135 aslmsg stats; 136 asl_msg_t *query; 137 asl_msg_t *qlq[1]; 138 char str[1024], *p, *qs, *out; 139 ssize_t len; 140 fd_set readfds, errfds; 141 uint64_t low_id, high_id; 142 uint32_t dbselect, flags; 143 session_args_t *sp; 144 145 if (x == NULL) pthread_exit(NULL); 146 147 sp = (session_args_t *)x; 148 s = sp->sock; 149 flags = sp->flags; 150 free(x); 151 152 asldebug("%s %d: starting interactive session for %ssocket %d\n", MY_ID, s, (flags & SESSION_FLAGS_LOCKDOWN) ? "lockdown " : "", s); 153 154 do_prompt = 1; 155 watch = WATCH_OFF; 156 wfd = -1; 157 wtoken = -1; 158 159 dbselect = 0; 160 if (global.dbtype & DB_TYPE_MEMORY) dbselect = DB_TYPE_MEMORY; 161 else if (global.dbtype & DB_TYPE_MINI) dbselect = DB_TYPE_MINI; 162 else if (global.dbtype & DB_TYPE_FILE) dbselect = DB_TYPE_FILE; 163 164 low_id = 0; 165 high_id = 0; 166 167 pfmt = PRINT_STD; 168 query = NULL; 169 memset(&ql, 0, sizeof(asl_search_result_t)); 170 171 if (flags & SESSION_FLAGS_LOCKDOWN) sleep(1); 172 173 snprintf(str, sizeof(str), "\n========================\nASL is here to serve you\n"); 174 if (write(s, str, strlen(str)) < 0) 175 { 176 close(s); 177 pthread_exit(NULL); 178 return; 179 } 180 181 if (flags & SESSION_FLAGS_LOCKDOWN) 182 { 183 snprintf(str, sizeof(str), "> "); 184 SESSION_WRITE(s, str); 185 } 186 187 forever 188 { 189 if (((flags & SESSION_FLAGS_LOCKDOWN) == 0) && (do_prompt > 0)) 190 { 191 snprintf(str, sizeof(str), "> "); 192 SESSION_WRITE(s, str); 193 } 194 195 do_prompt = 1; 196 197 memset(str, 0, sizeof(str)); 198 199 FD_ZERO(&readfds); 200 FD_SET(s, &readfds); 201 FD_ZERO(&errfds); 202 FD_SET(s, &errfds); 203 nfd = s; 204 205 if (wfd != -1) 206 { 207 FD_SET(wfd, &readfds); 208 if (wfd > nfd) nfd = wfd; 209 } 210 211 status = select(nfd + 1, &readfds, NULL, &errfds, NULL); 212 if (status == 0) continue; 213 if (status < 0) 214 { 215 asldebug("%s %d: select %d %s\n", MY_ID, s, errno, strerror(errno)); 216 goto exit_session; 217 } 218 219 if (FD_ISSET(s, &errfds)) 220 { 221 asldebug("%s %d: error on socket %d\n", MY_ID, s, s); 222 goto exit_session; 223 } 224 225 if ((wfd != -1) && (FD_ISSET(wfd, &readfds))) 226 { 227 (void)read(wfd, &i, sizeof(int)); 228 } 229 230 if (FD_ISSET(s, &errfds)) 231 { 232 asldebug("%s %d: socket %d reported error\n", MY_ID, s, s); 233 goto exit_session; 234 } 235 236 if (FD_ISSET(s, &readfds)) 237 { 238 len = read(s, str, sizeof(str) - 1); 239 if (len <= 0) 240 { 241 asldebug("%s %d: read error on socket %d: %d %s\n", MY_ID, s, s, errno, strerror(errno)); 242 goto exit_session; 243 } 244 245 while ((len > 1) && ((str[len - 1] == '\n') || (str[len - 1] == '\r'))) 246 { 247 str[len - 1] = '\0'; 248 len--; 249 } 250 251 if ((!strcmp(str, "q")) || (!strcmp(str, "quit")) || (!strcmp(str, "exit"))) 252 { 253 snprintf(str, sizeof(str), "Goodbye\n"); 254 write(s, str, strlen(str)); 255 close(s); 256 s = -1; 257 break; 258 } 259 260 if ((!strcmp(str, "?")) || (!strcmp(str, "help"))) 261 { 262 snprintf(str, sizeof(str), "Commands\n"); 263 SESSION_WRITE(s, str); 264 snprintf(str, sizeof(str), " quit exit session\n"); 265 SESSION_WRITE(s, str); 266 snprintf(str, sizeof(str), " select [val] get [set] current database\n"); 267 SESSION_WRITE(s, str); 268 snprintf(str, sizeof(str), " val must be \"file\", \"mem\", or \"mini\"\n"); 269 SESSION_WRITE(s, str); 270 snprintf(str, sizeof(str), " file [on/off] enable / disable file store\n"); 271 SESSION_WRITE(s, str); 272 snprintf(str, sizeof(str), " memory [on/off] enable / disable memory store\n"); 273 SESSION_WRITE(s, str); 274 snprintf(str, sizeof(str), " mini [on/off] enable / disable mini memory store\n"); 275 SESSION_WRITE(s, str); 276 snprintf(str, sizeof(str), " stats database statistics\n"); 277 SESSION_WRITE(s, str); 278 snprintf(str, sizeof(str), " flush flush database\n"); 279 SESSION_WRITE(s, str); 280 snprintf(str, sizeof(str), " dbsize [val] get [set] database size (# of records)\n"); 281 SESSION_WRITE(s, str); 282 snprintf(str, sizeof(str), " watch print new messages as they arrive\n"); 283 SESSION_WRITE(s, str); 284 snprintf(str, sizeof(str), " stop stop watching for new messages\n"); 285 SESSION_WRITE(s, str); 286 snprintf(str, sizeof(str), " raw use raw format for printing messages\n"); 287 SESSION_WRITE(s, str); 288 snprintf(str, sizeof(str), " std use standard format for printing messages\n"); 289 SESSION_WRITE(s, str); 290 snprintf(str, sizeof(str), " * show all log messages\n"); 291 SESSION_WRITE(s, str); 292 snprintf(str, sizeof(str), " * key val equality search for messages (single key/value pair)\n"); 293 SESSION_WRITE(s, str); 294 snprintf(str, sizeof(str), " * op key val search for matching messages (single key/value pair)\n"); 295 SESSION_WRITE(s, str); 296 snprintf(str, sizeof(str), " * [op key val] ... search for matching messages (multiple key/value pairs)\n"); 297 SESSION_WRITE(s, str); 298 snprintf(str, sizeof(str), " operators: = < > ! (not equal) T (key exists) R (regex)\n"); 299 SESSION_WRITE(s, str); 300 snprintf(str, sizeof(str), " modifiers (must follow operator):\n"); 301 SESSION_WRITE(s, str); 302 snprintf(str, sizeof(str), " C=casefold N=numeric S=substring A=prefix Z=suffix\n"); 303 SESSION_WRITE(s, str); 304 snprintf(str, sizeof(str), "\n"); 305 SESSION_WRITE(s, str); 306 continue; 307 } 308 else if (!strcmp(str, "stats")) 309 { 310 stats = remote_db_stats(dbselect); 311 out = asl_format_message((asl_msg_t *)stats, ASL_MSG_FMT_RAW, ASL_TIME_FMT_SEC, ASL_ENCODE_NONE, &outlen); 312 write(s, out, outlen); 313 free(out); 314 asl_free(stats); 315 continue; 316 } 317 else if (!strcmp(str, "flush")) 318 {} 319 else if (!strncmp(str, "select", 6)) 320 { 321 p = str + 6; 322 while ((*p == ' ') || (*p == '\t')) p++; 323 if (*p == '\0') 324 { 325 if (dbselect == 0) snprintf(str, sizeof(str), "no store\n"); 326 else if (dbselect == DB_TYPE_FILE) snprintf(str, sizeof(str), "file store\n"); 327 else if (dbselect == DB_TYPE_MEMORY) snprintf(str, sizeof(str), "memory store\n"); 328 else if (dbselect == DB_TYPE_MINI) snprintf(str, sizeof(str), "mini memory store\n"); 329 SESSION_WRITE(s, str); 330 continue; 331 } 332 333 if (!strncmp(p, "file", 4)) 334 { 335 if ((global.dbtype & DB_TYPE_FILE) == 0) 336 { 337 snprintf(str, sizeof(str), "file database is not enabled\n"); 338 SESSION_WRITE(s, str); 339 continue; 340 } 341 342 dbselect = DB_TYPE_FILE; 343 } 344 else if (!strncmp(p, "mem", 3)) 345 { 346 if ((global.dbtype & DB_TYPE_MEMORY) == 0) 347 { 348 snprintf(str, sizeof(str), "memory database is not enabled\n"); 349 SESSION_WRITE(s, str); 350 continue; 351 } 352 353 dbselect = DB_TYPE_MEMORY; 354 } 355 else if (!strncmp(p, "mini", 4)) 356 { 357 if ((global.dbtype & DB_TYPE_MINI) == 0) 358 { 359 if (global.mini_db != NULL) 360 { 361 snprintf(str, sizeof(str), "mini memory database is enabled for disaster messages\n"); 362 SESSION_WRITE(s, str); 363 } 364 else 365 { 366 snprintf(str, sizeof(str), "mini memory database is not enabled\n"); 367 SESSION_WRITE(s, str); 368 continue; 369 } 370 } 371 372 dbselect = DB_TYPE_MINI; 373 } 374 else 375 { 376 snprintf(str, sizeof(str), "unknown database type\n"); 377 SESSION_WRITE(s, str); 378 continue; 379 } 380 381 snprintf(str, sizeof(str), "OK\n"); 382 SESSION_WRITE(s, str); 383 continue; 384 } 385 else if (!strncmp(str, "file", 4)) 386 { 387 p = str + 4; 388 while ((*p == ' ') || (*p == '\t')) p++; 389 if (*p == '\0') 390 { 391 snprintf(str, sizeof(str), "file database is %senabled\n", (global.dbtype & DB_TYPE_FILE) ? "" : "not "); 392 SESSION_WRITE(s, str); 393 if ((global.dbtype & DB_TYPE_FILE) != 0) dbselect = DB_TYPE_FILE; 394 continue; 395 } 396 397 if (!strcmp(p, "on")) global.dbtype |= DB_TYPE_FILE; 398 else if (!strcmp(p, "off")) global.dbtype &= ~ DB_TYPE_FILE; 399 400 snprintf(str, sizeof(str), "OK\n"); 401 SESSION_WRITE(s, str); 402 continue; 403 } 404 else if (!strncmp(str, "memory", 6)) 405 { 406 p = str + 6; 407 while ((*p == ' ') || (*p == '\t')) p++; 408 if (*p == '\0') 409 { 410 snprintf(str, sizeof(str), "memory database is %senabled\n", (global.dbtype & DB_TYPE_MEMORY) ? "" : "not "); 411 SESSION_WRITE(s, str); 412 if ((global.dbtype & DB_TYPE_MEMORY) != 0) dbselect = DB_TYPE_MEMORY; 413 continue; 414 } 415 416 if (!strcmp(p, "on")) global.dbtype |= DB_TYPE_MEMORY; 417 else if (!strcmp(p, "off")) global.dbtype &= ~ DB_TYPE_MEMORY; 418 419 snprintf(str, sizeof(str), "OK\n"); 420 SESSION_WRITE(s, str); 421 continue; 422 } 423 else if (!strncmp(str, "mini", 4)) 424 { 425 p = str + 4; 426 while ((*p == ' ') || (*p == '\t')) p++; 427 if (*p == '\0') 428 { 429 snprintf(str, sizeof(str), "mini database is %senabled\n", (global.dbtype & DB_TYPE_MINI) ? "" : "not "); 430 SESSION_WRITE(s, str); 431 if ((global.dbtype & DB_TYPE_MINI) != 0) dbselect = DB_TYPE_MINI; 432 continue; 433 } 434 435 if (!strcmp(p, "on")) global.dbtype |= DB_TYPE_MINI; 436 else if (!strcmp(p, "off")) global.dbtype &= ~ DB_TYPE_MINI; 437 438 snprintf(str, sizeof(str), "OK\n"); 439 SESSION_WRITE(s, str); 440 continue; 441 } 442 else if (!strncmp(str, "dbsize", 6)) 443 { 444 if (dbselect == 0) 445 { 446 snprintf(str, sizeof(str), "no store\n"); 447 SESSION_WRITE(s, str); 448 continue; 449 } 450 451 p = str + 6; 452 while ((*p == ' ') || (*p == '\t')) p++; 453 if (*p == '\0') 454 { 455 snprintf(str, sizeof(str), "DB size %u\n", remote_db_size(dbselect)); 456 SESSION_WRITE(s, str); 457 continue; 458 } 459 460 i = atoi(p); 461 remote_db_set_size(dbselect, i); 462 463 snprintf(str, sizeof(str), "OK\n"); 464 SESSION_WRITE(s, str); 465 continue; 466 } 467 else if (!strcmp(str, "stop")) 468 { 469 if (watch != WATCH_OFF) 470 { 471 watch = WATCH_OFF; 472 notify_cancel(wtoken); 473 wfd = -1; 474 wtoken = -1; 475 476 low_id = 0; 477 high_id = 0; 478 479 if (query != NULL) free(query); 480 query = NULL; 481 482 snprintf(str, sizeof(str), "OK\n"); 483 SESSION_WRITE(s, str); 484 continue; 485 } 486 487 snprintf(str, sizeof(str), "not watching!\n"); 488 SESSION_WRITE(s, str); 489 continue; 490 } 491 else if (!strcmp(str, "raw")) 492 { 493 pfmt = PRINT_RAW; 494 continue; 495 } 496 else if (!strcmp(str, "std")) 497 { 498 pfmt = PRINT_STD; 499 continue; 500 } 501 else if (!strcmp(str, "watch")) 502 { 503 if (((flags & SESSION_FLAGS_LOCKDOWN) == 0) && (watch != WATCH_OFF)) 504 { 505 snprintf(str, sizeof(str), "already watching!\n"); 506 SESSION_WRITE(s, str); 507 continue; 508 } 509 510 if (flags & SESSION_FLAGS_LOCKDOWN) 511 { 512 /* 513 * If this session is PurpleConsole or Xcode watching for log messages, 514 * we pass through the bottom of the loop (below) once to pick up 515 * existing messages already in memory. After that, dbserver will 516 * send new messages in send_to_direct_watchers(). We wait until 517 * the initial messages are sent before adding the connection to 518 * global.lockdown_session_fds to allow this query to complete before 519 * dbserver starts sending. To prevent a race between this query and 520 * when messages are sent by send_to_direct_watchers, we suspend the 521 * work queue and resume it when lockdown_session_fds has been updated. 522 */ 523 watch = WATCH_LOCKDOWN_START; 524 dispatch_suspend(global.work_queue); 525 } 526 else 527 { 528 status = notify_register_file_descriptor(kNotifyASLDBUpdate, &wfd, 0, &wtoken); 529 if (status != 0) 530 { 531 snprintf(str, sizeof(str), "notify_register_file_descriptor failed: %d\n", status); 532 SESSION_WRITE(s, str); 533 continue; 534 } 535 536 watch = WATCH_RUN; 537 } 538 539 snprintf(str, sizeof(str), "OK\n"); 540 SESSION_WRITE(s, str); 541 do_prompt = 2; 542 } 543 else if ((str[0] == '*') || (str[0] == 'T') || (str[0] == '=') || (str[0] == '!') || (str[0] == '<') || (str[0] == '>')) 544 { 545 memset(&ql, 0, sizeof(asl_search_result_t)); 546 if (query != NULL) free(query); 547 query = NULL; 548 549 p = str; 550 if (*p == '*') p++; 551 while ((*p == ' ') || (*p == '\t')) p++; 552 553 if (*p == '\0') 554 { 555 /* NULL query */ 556 } 557 else if (*p == '[') 558 { 559 qs = NULL; 560 asprintf(&qs, "Q %s", p); 561 query = asl_msg_from_string(qs); 562 free(qs); 563 } 564 else if ((*p == 'T') || (*p == '=') || (*p == '!') || (*p == '<') || (*p == '>') || (*p == 'R')) 565 { 566 qs = NULL; 567 asprintf(&qs, "Q [%s]", p); 568 query = asl_msg_from_string(qs); 569 free(qs); 570 } 571 else 572 { 573 qs = NULL; 574 asprintf(&qs, "Q [= %s]", p); 575 query = asl_msg_from_string(qs); 576 free(qs); 577 } 578 } 579 else 580 { 581 snprintf(str, sizeof(str), "unrecognized command\n"); 582 SESSION_WRITE(s, str); 583 snprintf(str, sizeof(str), "enter \"help\" for help\n"); 584 SESSION_WRITE(s, str); 585 continue; 586 } 587 } 588 589 if ((flags & SESSION_FLAGS_LOCKDOWN) && (watch == WATCH_RUN)) continue; 590 591 /* Bottom of the loop: do a database query and print the results */ 592 593 if (query != NULL) 594 { 595 ql.count = 1; 596 qlq[0] = query; 597 ql.msg = qlq; 598 } 599 600 if (watch == WATCH_OFF) low_id = 0; 601 602 memset(&res, 0, sizeof(aslresponse)); 603 high_id = 0; 604 (void)db_query(&ql, (aslresponse *)&res, low_id, 0, 0, &high_id, 0, 0, 0); 605 606 if ((watch == WATCH_RUN) && (high_id >= low_id)) low_id = high_id + 1; 607 608 if (res == NULL) 609 { 610 if (watch == WATCH_OFF) 611 { 612 snprintf(str, sizeof(str), "-nil-\n"); 613 SESSION_WRITE(s, str); 614 } 615 else 616 { 617 if (do_prompt != 2) do_prompt = 0; 618 } 619 } 620 else if (pfmt == PRINT_RAW) 621 { 622 if (watch == WATCH_RUN) 623 { 624 snprintf(str, sizeof(str), "\n"); 625 SESSION_WRITE(s, str); 626 } 627 628 outlen = 0; 629 out = asl_list_to_string((asl_search_result_t *)res, &outlen); 630 write(s, out, outlen); 631 free(out); 632 633 snprintf(str, sizeof(str), "\n"); 634 SESSION_WRITE(s, str); 635 } 636 else 637 { 638 if ((watch == WATCH_RUN) || (watch == WATCH_LOCKDOWN_START)) 639 { 640 snprintf(str, sizeof(str), "\n"); 641 SESSION_WRITE(s, str); 642 } 643 644 snprintf(str, sizeof(str), "\n"); 645 for (i = 0; i < res->count; i++) 646 { 647 int wstatus; 648 649 out = asl_format_message(res->msg[i], ASL_MSG_FMT_STD, ASL_TIME_FMT_LCL, ASL_ENCODE_SAFE, &outlen); 650 651 do 652 { 653 int n = 0; 654 655 errno = 0; 656 wstatus = write(s, out, outlen); 657 if (wstatus < 0) 658 { 659 asldebug("%s %d: %d/%d write data failed: %d %s\n", MY_ID, s, i, res->count, errno, strerror(errno)); 660 if (errno == EAGAIN) 661 { 662 n++; 663 if (n > 10) break; 664 usleep(10000); 665 } 666 else 667 { 668 goto exit_session; 669 } 670 } 671 } while (errno == EAGAIN); 672 673 free(out); 674 if (global.remote_delay_time > 0) usleep(global.remote_delay_time); 675 } 676 } 677 678 aslresponse_free(res); 679 680 if (watch == WATCH_LOCKDOWN_START) 681 { 682 add_lockdown_session(s); 683 watch = WATCH_RUN; 684 dispatch_resume(global.work_queue); 685 } 686 } 687 688exit_session: 689 690 asldebug("%s %d: terminating session for %ssocket %d\n", MY_ID, s, (flags & SESSION_FLAGS_LOCKDOWN) ? "lockdown " : "", s); 691 692 if (s >= 0) 693 { 694 if (flags & SESSION_FLAGS_LOCKDOWN) remove_lockdown_session(s); 695 close(s); 696 } 697 698 if (watch == WATCH_LOCKDOWN_START) dispatch_resume(global.work_queue); 699 if (wtoken >= 0) notify_cancel(wtoken); 700 if (query != NULL) asl_msg_release(query); 701 pthread_exit(NULL); 702} 703 704aslmsg 705remote_acceptmsg(int fd, int tcp) 706{ 707 socklen_t fromlen; 708 int s, flags, status, v; 709 pthread_attr_t attr; 710 pthread_t t; 711 struct sockaddr_storage from; 712 session_args_t *sp; 713 714 fromlen = sizeof(struct sockaddr_un); 715 if (tcp == 1) fromlen = sizeof(struct sockaddr_storage); 716 717 memset(&from, 0, sizeof(from)); 718 719 s = accept(fd, (struct sockaddr *)&from, &fromlen); 720 if (s == -1) 721 { 722 asldebug("%s: accept: %s\n", MY_ID, strerror(errno)); 723 return NULL; 724 } 725 726 flags = fcntl(s, F_GETFL, 0); 727 flags &= ~ O_NONBLOCK; 728 status = fcntl(s, F_SETFL, flags); 729 if (status < 0) 730 { 731 asldebug("%s: fcntl: %s\n", MY_ID, strerror(errno)); 732 close(s); 733 return NULL; 734 } 735 736 v = 1; 737 setsockopt(s, SOL_SOCKET, SO_NOSIGPIPE, &v, sizeof(v)); 738 739 if (tcp == 1) 740 { 741 flags = 1; 742 setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &flags, sizeof(int)); 743 } 744 745 sp = (session_args_t *)calloc(1, sizeof(session_args_t)); 746 if (sp == NULL) 747 { 748 asldebug("%s: malloc: %s\n", MY_ID, strerror(errno)); 749 close(s); 750 return NULL; 751 } 752 753 sp->sock = s; 754 if (tcp == 0) sp->flags |= SESSION_FLAGS_LOCKDOWN; 755 756 pthread_attr_init(&attr); 757 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); 758 pthread_create(&t, &attr, (void *(*)(void *))session, (void *)sp); 759 pthread_attr_destroy(&attr); 760 761 return NULL; 762} 763 764aslmsg 765remote_acceptmsg_local(int fd) 766{ 767 return remote_acceptmsg(fd, 0); 768} 769 770aslmsg 771remote_acceptmsg_tcp(int fd) 772{ 773 return remote_acceptmsg(fd, 1); 774} 775 776int 777remote_init_lockdown(void) 778{ 779 int status, reuse, fd; 780 struct sockaddr_un local; 781 782 fd = socket(AF_UNIX, SOCK_STREAM, 0); 783 if (fd < 0) 784 { 785 asldebug("%s: socket: %s\n", MY_ID, strerror(errno)); 786 return -1; 787 } 788 789 reuse = 1; 790 status = setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &reuse, sizeof(int)); 791 if (status < 0) 792 { 793 asldebug("%s: setsockopt: %s\n", MY_ID, strerror(errno)); 794 close(fd); 795 return -1; 796 } 797 798 /* make sure the lockdown directory exists */ 799 mkdir(LOCKDOWN_PATH, 0777); 800 801 memset(&local, 0, sizeof(local)); 802 local.sun_family = AF_UNIX; 803 strlcpy(local.sun_path, SYSLOG_SOCK_PATH, sizeof(local.sun_path)); 804 unlink(local.sun_path); 805 806 status = bind(fd, (struct sockaddr *)&local, sizeof(local.sun_family) + sizeof(local.sun_path)); 807 808 if (status < 0) 809 { 810 asldebug("%s: bind: %s\n", MY_ID, strerror(errno)); 811 close(fd); 812 return -1; 813 } 814 815 status = fcntl(fd, F_SETFL, O_NONBLOCK); 816 if (status < 0) 817 { 818 asldebug("%s: fcntl: %s\n", MY_ID, strerror(errno)); 819 close(fd); 820 return -1; 821 } 822 823 status = listen(fd, 5); 824 if (status < 0) 825 { 826 asldebug("%s: listen: %s\n", MY_ID, strerror(errno)); 827 close(fd); 828 return -1; 829 } 830 831 chmod(SYSLOG_SOCK_PATH, 0666); 832 833 in_src_local = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, (uintptr_t)fd, 0, in_queue); 834 dispatch_source_set_event_handler(in_src_local, ^{ remote_acceptmsg_local(fd); }); 835 dispatch_resume(in_src_local); 836 837 return fd; 838} 839 840int 841remote_init_tcp(int family) 842{ 843 int status, reuse, fd; 844 struct sockaddr_in a4; 845 struct sockaddr_in6 a6; 846 struct sockaddr *s; 847 socklen_t len; 848 849 fd = socket(family, SOCK_STREAM, 0); 850 if (fd < 0) 851 { 852 asldebug("%s: socket: %s\n", MY_ID, strerror(errno)); 853 return -1; 854 } 855 856 reuse = 1; 857 status = setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &reuse, sizeof(int)); 858 if (status < 0) 859 { 860 asldebug("%s: setsockopt: %s\n", MY_ID, strerror(errno)); 861 close(fd); 862 return -1; 863 } 864 865 memset(&(a4.sin_addr), 0, sizeof(struct in_addr)); 866 a4.sin_family = AF_INET; 867 a4.sin_port = htons(ASL_REMOTE_PORT); 868 869 memset(&(a6.sin6_addr), 0, sizeof(struct in6_addr)); 870 a6.sin6_family = AF_INET6; 871 a6.sin6_port = htons(ASL_REMOTE_PORT); 872 873 s = (struct sockaddr *)&a4; 874 len = sizeof(struct sockaddr_in); 875 876 if (family == AF_INET6) 877 { 878 s = (struct sockaddr *)&a6; 879 len = sizeof(struct sockaddr_in6); 880 } 881 882 status = bind(fd, s, len); 883 if (status < 0) 884 { 885 asldebug("%s: bind: %s\n", MY_ID, strerror(errno)); 886 close(fd); 887 return -1; 888 } 889 890 status = fcntl(fd, F_SETFL, O_NONBLOCK); 891 if (status < 0) 892 { 893 asldebug("%s: fcntl: %s\n", MY_ID, strerror(errno)); 894 close(fd); 895 return -1; 896 } 897 898 status = listen(fd, 5); 899 if (status < 0) 900 { 901 asldebug("%s: listen: %s\n", MY_ID, strerror(errno)); 902 close(fd); 903 return -1; 904 } 905 906 in_src_tcp = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, (uintptr_t)fd, 0, in_queue); 907 dispatch_source_set_event_handler(in_src_tcp, ^{ remote_acceptmsg_tcp(fd); }); 908 dispatch_resume(in_src_tcp); 909 910 return fd; 911} 912 913int 914remote_init(void) 915{ 916 static dispatch_once_t once; 917 918 dispatch_once(&once, ^{ 919 in_queue = dispatch_queue_create(MY_ID, NULL); 920 }); 921 922 asldebug("%s: init\n", MY_ID); 923 924#ifdef LOCKDOWN 925 rfdl = remote_init_lockdown(); 926#endif 927 928#ifdef REMOTE_IPV4 929 rfd4 = remote_init_tcp(AF_INET); 930#endif 931 932#ifdef REMOTE_IPV6 933 rfd6 = remote_init_tcp(AF_INET6); 934#endif 935 936 return 0; 937} 938 939int 940remote_close(void) 941{ 942 if (rfdl >= 0) 943 { 944 close(rfdl); 945 } 946 947 rfdl = -1; 948 949 if (rfd4 >= 0) 950 { 951 close(rfd4); 952 } 953 954 rfd4 = -1; 955 956 if (rfd6 >= 0) 957 { 958 close(rfd6); 959 } 960 961 rfd6 = -1; 962 963 return 0; 964} 965 966int 967remote_reset(void) 968{ 969 return 0; 970 971 remote_close(); 972 return remote_init(); 973} 974 975#endif /* !TARGET_IPHONE_SIMULATOR */ 976