1/* 2 * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. 3 * 4 * @APPLE_APACHE_LICENSE_HEADER_START@ 5 * 6 * Licensed under the Apache License, Version 2.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * See the License for the specific language governing permissions and 16 * limitations under the License. 17 * 18 * @APPLE_APACHE_LICENSE_HEADER_END@ 19 */ 20 21#include "config.h" 22#include "launchd.h" 23 24#include <sys/types.h> 25#include <sys/queue.h> 26#include <sys/event.h> 27#include <sys/stat.h> 28#include <sys/ucred.h> 29#include <sys/fcntl.h> 30#include <sys/un.h> 31#include <sys/wait.h> 32#include <sys/sysctl.h> 33#include <sys/sockio.h> 34#include <sys/time.h> 35#include <sys/resource.h> 36#include <sys/ioctl.h> 37#include <sys/mount.h> 38#include <sys/kern_event.h> 39#include <sys/reboot.h> 40#include <sys/socket.h> 41#include <sys/syscall.h> 42#include <net/if.h> 43#include <netinet/in.h> 44#include <netinet/in_var.h> 45#include <netinet6/nd6.h> 46#include <ifaddrs.h> 47#include <unistd.h> 48#include <signal.h> 49#include <errno.h> 50#include <libgen.h> 51#include <stdio.h> 52#include <stdlib.h> 53#include <stdarg.h> 54#include <stdbool.h> 55#include <paths.h> 56#include <pwd.h> 57#include <grp.h> 58#include <ttyent.h> 59#include <dlfcn.h> 60#include <dirent.h> 61#include <string.h> 62#include <setjmp.h> 63#include <spawn.h> 64#include <sched.h> 65#include <pthread.h> 66#include <util.h> 67#include <os/assumes.h> 68 69#if HAVE_LIBAUDITD 70#include <bsm/auditd_lib.h> 71#include <bsm/audit_session.h> 72#endif 73 74#include "bootstrap.h" 75#include "vproc.h" 76#include "vproc_priv.h" 77#include "vproc_internal.h" 78#include "launch.h" 79#include "launch_internal.h" 80 81#include "runtime.h" 82#include "core.h" 83#include "ipc.h" 84 85#define LAUNCHD_CONF ".launchd.conf" 86 87extern char **environ; 88 89static void pfsystem_callback(void *, struct kevent *); 90 91static kq_callback kqpfsystem_callback = pfsystem_callback; 92 93static void pid1_magic_init(void); 94 95static void testfd_or_openfd(int fd, const char *path, int flags); 96static bool get_network_state(void); 97static void monitor_networking_state(void); 98static void fatal_signal_handler(int sig, siginfo_t *si, void *uap); 99static void handle_pid1_crashes_separately(void); 100static void do_pid1_crash_diagnosis_mode(const char *msg); 101static int basic_fork(void); 102static bool do_pid1_crash_diagnosis_mode2(const char *msg); 103 104static void *update_thread(void *nothing); 105 106static void *crash_addr; 107static pid_t crash_pid; 108 109char *_launchd_database_dir; 110char *_launchd_log_dir; 111 112bool launchd_shutting_down; 113bool network_up; 114uid_t launchd_uid; 115FILE *launchd_console = NULL; 116int32_t launchd_sync_frequency = 30; 117 118int 119main(int argc, char *const *argv) 120{ 121 bool sflag = false; 122 int ch; 123 124 /* This needs to be cleaned up. Currently, we risk tripping assumes() macros 125 * before we've properly set things like launchd's log database paths, the 126 * global launchd label for syslog messages and the like. Luckily, these are 127 * operations that will probably never fail, like test_of_openfd(), the 128 * stuff in launchd_runtime_init() and the stuff in 129 * handle_pid1_crashes_separately(). 130 */ 131 testfd_or_openfd(STDIN_FILENO, _PATH_DEVNULL, O_RDONLY); 132 testfd_or_openfd(STDOUT_FILENO, _PATH_DEVNULL, O_WRONLY); 133 testfd_or_openfd(STDERR_FILENO, _PATH_DEVNULL, O_WRONLY); 134 135 if (launchd_use_gmalloc) { 136 if (!getenv("DYLD_INSERT_LIBRARIES")) { 137 setenv("DYLD_INSERT_LIBRARIES", "/usr/lib/libgmalloc.dylib", 1); 138 setenv("MALLOC_STRICT_SIZE", "1", 1); 139 execv(argv[0], argv); 140 } else { 141 unsetenv("DYLD_INSERT_LIBRARIES"); 142 unsetenv("MALLOC_STRICT_SIZE"); 143 } 144 } else if (launchd_malloc_log_stacks) { 145 if (!getenv("MallocStackLogging")) { 146 setenv("MallocStackLogging", "1", 1); 147 execv(argv[0], argv); 148 } else { 149 unsetenv("MallocStackLogging"); 150 } 151 } 152 153 while ((ch = getopt(argc, argv, "s")) != -1) { 154 switch (ch) { 155 case 's': sflag = true; break; /* single user */ 156 case '?': /* we should do something with the global optopt variable here */ 157 default: 158 fprintf(stderr, "%s: ignoring unknown arguments\n", getprogname()); 159 break; 160 } 161 } 162 163 if (getpid() != 1 && getppid() != 1) { 164 fprintf(stderr, "%s: This program is not meant to be run directly.\n", getprogname()); 165 exit(EXIT_FAILURE); 166 } 167 168 launchd_runtime_init(); 169 170 if (NULL == getenv("PATH")) { 171 setenv("PATH", _PATH_STDPATH, 1); 172 } 173 174 if (pid1_magic) { 175 pid1_magic_init(); 176 177 int cfd = -1; 178 if ((cfd = open(_PATH_CONSOLE, O_WRONLY | O_NOCTTY)) != -1) { 179 _fd(cfd); 180 if (!(launchd_console = fdopen(cfd, "w"))) { 181 (void)close(cfd); 182 } 183 } 184 185 char *extra = ""; 186 if (launchd_osinstaller) { 187 extra = " in the OS Installer"; 188 } else if (sflag) { 189 extra = " in single-user mode"; 190 } 191 192 launchd_syslog(LOG_NOTICE | LOG_CONSOLE, "*** launchd[1] has started up%s. ***", extra); 193 if (launchd_use_gmalloc) { 194 launchd_syslog(LOG_NOTICE | LOG_CONSOLE, "*** Using libgmalloc. ***"); 195 } 196 197 if (launchd_verbose_boot) { 198 launchd_syslog(LOG_NOTICE | LOG_CONSOLE, "*** Verbose boot, will log to /dev/console. ***"); 199 } 200 201 if (launchd_shutdown_debugging) { 202 launchd_syslog(LOG_NOTICE | LOG_CONSOLE, "*** Shutdown debugging is enabled. ***"); 203 } 204 205 if (launchd_log_shutdown) { 206 launchd_syslog(LOG_NOTICE | LOG_CONSOLE, "*** Shutdown logging is enabled. ***"); 207 } 208 209 if (launchd_log_perf) { 210 launchd_syslog(LOG_NOTICE | LOG_CONSOLE, "*** Performance logging is enabled. ***"); 211 } 212 213 if (launchd_log_debug) { 214 launchd_syslog(LOG_NOTICE | LOG_CONSOLE, "*** Debug logging is enabled. ***"); 215 } 216 217 handle_pid1_crashes_separately(); 218 219 /* Start the update thread. 220 * 221 * <rdar://problem/5039559&6153301> 222 */ 223 pthread_t t = NULL; 224 (void)os_assumes_zero(pthread_create(&t, NULL, update_thread, NULL)); 225 (void)os_assumes_zero(pthread_detach(t)); 226 227 /* PID 1 doesn't have a flat namespace. */ 228 launchd_flat_mach_namespace = false; 229 fflush(launchd_console); 230 } else { 231 launchd_uid = getuid(); 232 launchd_var_available = true; 233 if (asprintf(&launchd_label, "com.apple.launchd.peruser.%u", launchd_uid) == 0) { 234 launchd_label = "com.apple.launchd.peruser.unknown"; 235 } 236 237 struct passwd *pwent = getpwuid(launchd_uid); 238 if (pwent) { 239 launchd_username = strdup(pwent->pw_name); 240 } else { 241 launchd_username = "(unknown)"; 242 } 243 244 if (asprintf(&_launchd_database_dir, LAUNCHD_DB_PREFIX "/com.apple.launchd.peruser.%u", launchd_uid) == 0) { 245 _launchd_database_dir = ""; 246 } 247 248 if (asprintf(&_launchd_log_dir, LAUNCHD_LOG_PREFIX "/com.apple.launchd.peruser.%u", launchd_uid) == 0) { 249 _launchd_log_dir = ""; 250 } 251 252 if (launchd_allow_global_dyld_envvars) { 253 launchd_syslog(LOG_WARNING, "Per-user launchd will allow DYLD_* environment variables in the global environment."); 254 } 255 256 ipc_server_init(); 257 launchd_log_push(); 258 259 auditinfo_addr_t auinfo; 260 if (posix_assumes_zero(getaudit_addr(&auinfo, sizeof(auinfo))) != -1) { 261 launchd_audit_session = auinfo.ai_asid; 262 launchd_syslog(LOG_DEBUG, "Our audit session ID is %i", launchd_audit_session); 263 } 264 265 launchd_audit_port = _audit_session_self(); 266 267 vproc_transaction_begin(NULL); 268 vproc_transaction_end(NULL, NULL); 269 270 launchd_syslog(LOG_DEBUG, "Per-user launchd started (UID/username): %u/%s.", launchd_uid, launchd_username); 271 } 272 273 monitor_networking_state(); 274 jobmgr_init(sflag); 275 276 launchd_runtime_init2(); 277 launchd_runtime(); 278} 279 280void 281handle_pid1_crashes_separately(void) 282{ 283 struct sigaction fsa; 284 285 fsa.sa_sigaction = fatal_signal_handler; 286 fsa.sa_flags = SA_SIGINFO; 287 sigemptyset(&fsa.sa_mask); 288 289 (void)posix_assumes_zero(sigaction(SIGILL, &fsa, NULL)); 290 (void)posix_assumes_zero(sigaction(SIGFPE, &fsa, NULL)); 291 (void)posix_assumes_zero(sigaction(SIGBUS, &fsa, NULL)); 292 (void)posix_assumes_zero(sigaction(SIGTRAP, &fsa, NULL)); 293 (void)posix_assumes_zero(sigaction(SIGABRT, &fsa, NULL)); 294 (void)posix_assumes_zero(sigaction(SIGSEGV, &fsa, NULL)); 295} 296 297void * 298update_thread(void *nothing __attribute__((unused))) 299{ 300 (void)posix_assumes_zero(setiopolicy_np(IOPOL_TYPE_DISK, IOPOL_SCOPE_THREAD, IOPOL_THROTTLE)); 301 302 while (launchd_sync_frequency) { 303 sync(); 304 sleep(launchd_sync_frequency); 305 } 306 307 launchd_syslog(LOG_DEBUG, "Update thread exiting."); 308 return NULL; 309} 310 311#define PID1_CRASH_LOGFILE "/var/log/launchd-pid1.crash" 312 313/* This hack forces the dynamic linker to resolve these symbols ASAP */ 314static __attribute__((unused)) typeof(sync) *__junk_dyld_trick1 = sync; 315static __attribute__((unused)) typeof(sleep) *__junk_dyld_trick2 = sleep; 316static __attribute__((unused)) typeof(reboot) *__junk_dyld_trick3 = reboot; 317 318void 319do_pid1_crash_diagnosis_mode(const char *msg) 320{ 321 if (launchd_wsp) { 322 kill(launchd_wsp, SIGKILL); 323 sleep(3); 324 launchd_wsp = 0; 325 } 326 327 while (launchd_shutdown_debugging && !do_pid1_crash_diagnosis_mode2(msg)) { 328 sleep(1); 329 } 330} 331 332int 333basic_fork(void) 334{ 335 int wstatus = 0; 336 pid_t p; 337 338 switch ((p = fork())) { 339 case -1: 340 launchd_syslog(LOG_ERR | LOG_CONSOLE, "Can't fork PID 1 copy for crash debugging: %m"); 341 return p; 342 case 0: 343 return p; 344 default: 345 do { 346 (void)waitpid(p, &wstatus, 0); 347 } while(!WIFEXITED(wstatus)); 348 349 fprintf(stdout, "PID 1 copy: exit status: %d\n", WEXITSTATUS(wstatus)); 350 351 return 1; 352 } 353 354 return -1; 355} 356 357bool 358do_pid1_crash_diagnosis_mode2(const char *msg) 359{ 360 if (basic_fork() == 0) { 361 /* Neuter our bootstrap port so that the shell doesn't try talking to us 362 * while we're blocked waiting on it. 363 */ 364 if (launchd_console) { 365 fflush(launchd_console); 366 } 367 368 task_set_bootstrap_port(mach_task_self(), MACH_PORT_NULL); 369 if (basic_fork() != 0) { 370 if (launchd_console) { 371 fflush(launchd_console); 372 } 373 374 return true; 375 } 376 } else { 377 return true; 378 } 379 380 int fd; 381 revoke(_PATH_CONSOLE); 382 if ((fd = open(_PATH_CONSOLE, O_RDWR)) == -1) { 383 _exit(2); 384 } 385 if (login_tty(fd) == -1) { 386 _exit(3); 387 } 388 389 setenv("TERM", "vt100", 1); 390 fprintf(stdout, "\n"); 391 fprintf(stdout, "Entering launchd PID 1 debugging mode...\n"); 392 fprintf(stdout, "The PID 1 launchd has crashed %s.\n", msg); 393 fprintf(stdout, "It has fork(2)ed itself for debugging.\n"); 394 fprintf(stdout, "To debug the crashing thread of PID 1:\n"); 395 fprintf(stdout, " gdb attach %d\n", getppid()); 396 fprintf(stdout, "To exit this shell and shut down:\n"); 397 fprintf(stdout, " kill -9 1\n"); 398 fprintf(stdout, "A sample of PID 1 has been written to %s\n", PID1_CRASH_LOGFILE); 399 fprintf(stdout, "\n"); 400 fflush(stdout); 401 402 execl(_PATH_BSHELL, "-sh", NULL); 403 syslog(LOG_ERR, "can't exec %s for PID 1 crash debugging: %m", _PATH_BSHELL); 404 _exit(EXIT_FAILURE); 405} 406 407void 408fatal_signal_handler(int sig, siginfo_t *si, void *uap __attribute__((unused))) 409{ 410 const char *doom_why = "at instruction"; 411 char msg[128]; 412#if 0 413 char *sample_args[] = { "/usr/bin/sample", "1", "1", "-file", PID1_CRASH_LOGFILE, NULL }; 414 pid_t sample_p; 415 int wstatus; 416#endif 417 418 crash_addr = si->si_addr; 419 crash_pid = si->si_pid; 420#if 0 421 setenv("XPC_SERVICES_UNAVAILABLE", "1", 0); 422 unlink(PID1_CRASH_LOGFILE); 423 424 switch ((sample_p = vfork())) { 425 case 0: 426 execve(sample_args[0], sample_args, environ); 427 _exit(EXIT_FAILURE); 428 break; 429 default: 430 waitpid(sample_p, &wstatus, 0); 431 break; 432 case -1: 433 break; 434 } 435#endif 436 switch (sig) { 437 default: 438 case 0: 439 break; 440 case SIGBUS: 441 case SIGSEGV: 442 doom_why = "trying to read/write"; 443 case SIGILL: 444 case SIGFPE: 445 case SIGTRAP: 446 snprintf(msg, sizeof(msg), "%s: %p (%s sent by PID %u)", doom_why, crash_addr, strsignal(sig), crash_pid); 447 sync(); 448 do_pid1_crash_diagnosis_mode(msg); 449 sleep(3); 450 reboot(0); 451 break; 452 } 453} 454 455void 456pid1_magic_init(void) 457{ 458 launchd_label = "com.apple.launchd"; 459 launchd_username = "system"; 460 461 _launchd_database_dir = LAUNCHD_DB_PREFIX "/com.apple.launchd"; 462 _launchd_log_dir = LAUNCHD_LOG_PREFIX "/com.apple.launchd"; 463 464 (void)posix_assumes_zero(setsid()); 465 (void)posix_assumes_zero(chdir("/")); 466 (void)posix_assumes_zero(setlogin("root")); 467 468#if !TARGET_OS_EMBEDDED 469 auditinfo_addr_t auinfo = { 470 .ai_termid = { 471 .at_type = AU_IPv4 472 }, 473 .ai_asid = AU_ASSIGN_ASID, 474 .ai_auid = AU_DEFAUDITID, 475 .ai_flags = AU_SESSION_FLAG_IS_INITIAL, 476 }; 477 478 if (setaudit_addr(&auinfo, sizeof(auinfo)) == -1) { 479 launchd_syslog(LOG_WARNING | LOG_CONSOLE, "Could not set audit session: %d: %s.", errno, strerror(errno)); 480 _exit(EXIT_FAILURE); 481 } 482 483 launchd_audit_session = auinfo.ai_asid; 484 launchd_syslog(LOG_DEBUG, "Audit Session ID: %i", launchd_audit_session); 485 486 launchd_audit_port = _audit_session_self(); 487#endif // !TARGET_OS_EMBEDDED 488} 489 490char * 491launchd_copy_persistent_store(int type, const char *file) 492{ 493 char *result = NULL; 494 if (!file) { 495 file = ""; 496 } 497 498 switch (type) { 499 case LAUNCHD_PERSISTENT_STORE_DB: 500 (void)asprintf(&result, "%s/%s", _launchd_database_dir, file); 501 break; 502 case LAUNCHD_PERSISTENT_STORE_LOGS: 503 (void)asprintf(&result, "%s/%s", _launchd_log_dir, file); 504 break; 505 default: 506 break; 507 } 508 509 return result; 510} 511 512int 513_fd(int fd) 514{ 515 if (fd >= 0) { 516 (void)posix_assumes_zero(fcntl(fd, F_SETFD, 1)); 517 } 518 return fd; 519} 520 521void 522launchd_shutdown(void) 523{ 524 int64_t now; 525 526 if (launchd_shutting_down) { 527 return; 528 } 529 530 runtime_ktrace0(RTKT_LAUNCHD_EXITING); 531 532 launchd_shutting_down = true; 533 launchd_log_push(); 534 535 now = runtime_get_wall_time(); 536 537 char *term_who = pid1_magic ? "System shutdown" : "Per-user launchd termination for "; 538 launchd_syslog(LOG_INFO, "%s%s began", term_who, pid1_magic ? "" : launchd_username); 539 540 os_assert(jobmgr_shutdown(root_jobmgr) != NULL); 541 542#if HAVE_LIBAUDITD 543 if (pid1_magic) { 544 (void)os_assumes_zero(audit_quick_stop()); 545 } 546#endif 547} 548 549void 550launchd_SessionCreate(void) 551{ 552#if !TARGET_OS_EMBEDDED 553 auditinfo_addr_t auinfo = { 554 .ai_termid = { .at_type = AU_IPv4 }, 555 .ai_asid = AU_ASSIGN_ASID, 556 .ai_auid = getuid(), 557 .ai_flags = 0, 558 }; 559 if (setaudit_addr(&auinfo, sizeof(auinfo)) == 0) { 560 char session[16]; 561 snprintf(session, sizeof(session), "%x", auinfo.ai_asid); 562 setenv("SECURITYSESSIONID", session, 1); 563 } else { 564 launchd_syslog(LOG_WARNING, "Could not set audit session: %d: %s.", errno, strerror(errno)); 565 } 566#endif // !TARGET_OS_EMBEDDED 567} 568 569void 570testfd_or_openfd(int fd, const char *path, int flags) 571{ 572 int tmpfd; 573 574 if (-1 != (tmpfd = dup(fd))) { 575 (void)posix_assumes_zero(runtime_close(tmpfd)); 576 } else { 577 if (-1 == (tmpfd = open(path, flags | O_NOCTTY, DEFFILEMODE))) { 578 launchd_syslog(LOG_ERR, "open(\"%s\", ...): %m", path); 579 } else if (tmpfd != fd) { 580 (void)posix_assumes_zero(dup2(tmpfd, fd)); 581 (void)posix_assumes_zero(runtime_close(tmpfd)); 582 } 583 } 584} 585 586bool 587get_network_state(void) 588{ 589 struct ifaddrs *ifa, *ifai; 590 bool up = false; 591 int r; 592 593 /* Workaround 4978696: getifaddrs() reports false ENOMEM */ 594 while ((r = getifaddrs(&ifa)) == -1 && errno == ENOMEM) { 595 launchd_syslog(LOG_DEBUG, "Worked around bug: 4978696"); 596 (void)posix_assumes_zero(sched_yield()); 597 } 598 599 if (posix_assumes_zero(r) == -1) { 600 return network_up; 601 } 602 603 for (ifai = ifa; ifai; ifai = ifai->ifa_next) { 604 if (!(ifai->ifa_flags & IFF_UP)) { 605 continue; 606 } 607 if (ifai->ifa_flags & IFF_LOOPBACK) { 608 continue; 609 } 610 if (ifai->ifa_addr->sa_family != AF_INET && ifai->ifa_addr->sa_family != AF_INET6) { 611 continue; 612 } 613 up = true; 614 break; 615 } 616 617 freeifaddrs(ifa); 618 619 return up; 620} 621 622void 623monitor_networking_state(void) 624{ 625 int pfs = _fd(socket(PF_SYSTEM, SOCK_RAW, SYSPROTO_EVENT)); 626 struct kev_request kev_req; 627 628 network_up = get_network_state(); 629 630 if (pfs == -1) { 631 (void)os_assumes_zero(errno); 632 return; 633 } 634 635 memset(&kev_req, 0, sizeof(kev_req)); 636 kev_req.vendor_code = KEV_VENDOR_APPLE; 637 kev_req.kev_class = KEV_NETWORK_CLASS; 638 639 if (posix_assumes_zero(ioctl(pfs, SIOCSKEVFILT, &kev_req)) == -1) { 640 runtime_close(pfs); 641 return; 642 } 643 644 (void)posix_assumes_zero(kevent_mod(pfs, EVFILT_READ, EV_ADD, 0, 0, &kqpfsystem_callback)); 645} 646 647void 648pfsystem_callback(void *obj __attribute__((unused)), struct kevent *kev) 649{ 650 bool new_networking_state; 651 char buf[1024]; 652 653 (void)posix_assumes_zero(read((int)kev->ident, &buf, sizeof(buf))); 654 655 new_networking_state = get_network_state(); 656 657 if (new_networking_state != network_up) { 658 network_up = new_networking_state; 659 jobmgr_dispatch_all_semaphores(root_jobmgr); 660 } 661} 662