X-Git-Url: https://git.saurik.com/apple/launchd.git/blobdiff_plain/f36da7255a20425f2d20e60b928ac991b21835df..dcace88fe6cde929a524daa9e1f1495bf1e24cfe:/launchd/src/launchd.c diff --git a/launchd/src/launchd.c b/launchd/src/launchd.c index 37bca50..78e1738 100644 --- a/launchd/src/launchd.c +++ b/launchd/src/launchd.c @@ -18,16 +18,11 @@ * @APPLE_APACHE_LICENSE_HEADER_END@ */ -static const char *const __rcs_file_version__ = "$Revision: 23506 $"; +static const char *const __rcs_file_version__ = "$Revision: 24863 $"; #include "config.h" #include "launchd.h" -#if HAVE_SECURITY -#include -#include -#include -#endif #include #include #include @@ -44,6 +39,8 @@ static const char *const __rcs_file_version__ = "$Revision: 23506 $"; #include #include #include +#include +#include #include #include #include @@ -67,20 +64,26 @@ static const char *const __rcs_file_version__ = "$Revision: 23506 $"; #include #include #include +#include +#include + +#if HAVE_LIBAUDITD +#include +#include +#endif -#include "libbootstrap_public.h" -#include "libvproc_public.h" -#include "libvproc_internal.h" -#include "liblaunch_public.h" +#include "bootstrap.h" +#include "vproc.h" +#include "vproc_priv.h" +#include "vproc_internal.h" +#include "launch.h" +#include "launch_internal.h" #include "launchd_runtime.h" #include "launchd_core_logic.h" #include "launchd_unix_ipc.h" #define LAUNCHD_CONF ".launchd.conf" -#define SECURITY_LIB "/System/Library/Frameworks/Security.framework/Versions/A/Security" -#define SHUTDOWN_LOG_DIR "/var/log/shutdown" - extern char **environ; @@ -95,15 +98,24 @@ static bool get_network_state(void); static void monitor_networking_state(void); static void fatal_signal_handler(int sig, siginfo_t *si, void *uap); static void handle_pid1_crashes_separately(void); -static void prep_shutdown_log_dir(void); +static void do_pid1_crash_diagnosis_mode(const char *msg); +static int basic_fork(void); +static bool do_pid1_crash_diagnosis_mode2(const char *msg); + +static void *update_thread(void *nothing); -static bool re_exec_in_single_user_mode = false; +static bool re_exec_in_single_user_mode; static void *crash_addr; static pid_t crash_pid; -static bool shutdown_in_progress = false; -bool debug_shutdown_hangs = false; -bool network_up = false; +bool shutdown_in_progress; +bool fake_shutdown_in_progress; +bool network_up; +char g_username[128] = "__Uninitialized__"; +char g_my_label[128] = "__Uninitialized__"; +char g_launchd_database_dir[PATH_MAX]; +FILE *g_console = NULL; +int32_t g_sync_frequency = 30; int main(int argc, char *const *argv) @@ -115,6 +127,24 @@ main(int argc, char *const *argv) testfd_or_openfd(STDOUT_FILENO, _PATH_DEVNULL, O_WRONLY); testfd_or_openfd(STDERR_FILENO, _PATH_DEVNULL, O_WRONLY); + if (g_use_gmalloc) { + if (!getenv("DYLD_INSERT_LIBRARIES")) { + setenv("DYLD_INSERT_LIBRARIES", "/usr/lib/libgmalloc.dylib", 1); + setenv("MALLOC_STRICT_SIZE", "1", 1); + execv(argv[0], argv); + } else { + unsetenv("DYLD_INSERT_LIBRARIES"); + unsetenv("MALLOC_STRICT_SIZE"); + } + } else if (g_malloc_log_stacks) { + if (!getenv("MallocStackLogging")) { + setenv("MallocStackLogging", "1", 1); + execv(argv[0], argv); + } else { + unsetenv("MallocStackLogging"); + } + } + while ((ch = getopt(argc, argv, "s")) != -1) { switch (ch) { case 's': sflag = true; break; /* single user */ @@ -132,24 +162,92 @@ main(int argc, char *const *argv) launchd_runtime_init(); + if (pid1_magic) { + int cfd = -1; + if (launchd_assumes((cfd = open(_PATH_CONSOLE, O_WRONLY | O_NOCTTY)) != -1)) { + _fd(cfd); + if (!launchd_assumes((g_console = fdopen(cfd, "w")) != NULL)) { + close(cfd); + } + } + } + if (NULL == getenv("PATH")) { setenv("PATH", _PATH_STDPATH, 1); } - if (getpid() == 1) { + if (pid1_magic) { pid1_magic_init(); } else { ipc_server_init(); + + runtime_log_push(); + + struct passwd *pwent = getpwuid(getuid()); + if (pwent) { + strlcpy(g_username, pwent->pw_name, sizeof(g_username) - 1); + } + + snprintf(g_my_label, sizeof(g_my_label), "com.apple.launchd.peruser.%u", getuid()); + + auditinfo_addr_t auinfo; + if (launchd_assumes(getaudit_addr(&auinfo, sizeof(auinfo)) != -1)) { + g_audit_session = auinfo.ai_asid; + runtime_syslog(LOG_DEBUG, "Our audit session ID is %i", g_audit_session); + } + + g_audit_session_port = _audit_session_self(); + snprintf(g_launchd_database_dir, sizeof(g_launchd_database_dir), LAUNCHD_DB_PREFIX "/com.apple.launchd.peruser.%u", getuid()); + runtime_syslog(LOG_DEBUG, "Per-user launchd for UID %u (%s) has begun.", getuid(), g_username); + } + + if (pid1_magic) { + runtime_syslog(LOG_NOTICE | LOG_CONSOLE, "*** launchd[1] has started up. ***"); + if (g_use_gmalloc) { + runtime_syslog(LOG_NOTICE | LOG_CONSOLE, "*** Using libgmalloc. ***"); + } + if (g_malloc_log_stacks) { + runtime_syslog(LOG_NOTICE | LOG_CONSOLE, "*** Logging stacks of malloc(3) allocations. ***"); + } + + if (g_verbose_boot) { + runtime_syslog(LOG_NOTICE | LOG_CONSOLE, "*** Verbose boot, will log to /dev/console. ***"); + } + + if (g_shutdown_debugging) { + runtime_syslog(LOG_NOTICE | LOG_CONSOLE, "*** Shutdown debugging is enabled. ***"); + } + + /* PID 1 doesn't have a flat namespace. */ + g_flat_mach_namespace = false; + } else { + if (g_use_gmalloc) { + runtime_syslog(LOG_NOTICE, "*** Per-user launchd using libgmalloc. ***"); + } } monitor_networking_state(); - if (getpid() == 1) { + if (pid1_magic) { handle_pid1_crashes_separately(); + } else { + #if !TARGET_OS_EMBEDDED + /* prime shared memory before the 'bootstrap_port' global is set to zero */ + _vproc_transaction_begin(); + _vproc_transaction_end(); + #endif } - jobmgr_init(sflag); + if (pid1_magic) { + /* Start the update thread -- rdar://problem/5039559&6153301 */ + pthread_t t = NULL; + int err = pthread_create(&t, NULL, update_thread, NULL); + (void)launchd_assumes(err == 0); + (void)launchd_assumes(pthread_detach(t) == 0); + } + jobmgr_init(sflag); + launchd_runtime_init2(); launchd_runtime(); @@ -164,10 +262,26 @@ handle_pid1_crashes_separately(void) fsa.sa_flags = SA_SIGINFO; sigemptyset(&fsa.sa_mask); - launchd_assumes(sigaction(SIGILL, &fsa, NULL) != -1); - launchd_assumes(sigaction(SIGFPE, &fsa, NULL) != -1); - launchd_assumes(sigaction(SIGBUS, &fsa, NULL) != -1); - launchd_assumes(sigaction(SIGSEGV, &fsa, NULL) != -1); + (void)launchd_assumes(sigaction(SIGILL, &fsa, NULL) != -1); + (void)launchd_assumes(sigaction(SIGFPE, &fsa, NULL) != -1); + (void)launchd_assumes(sigaction(SIGBUS, &fsa, NULL) != -1); + (void)launchd_assumes(sigaction(SIGSEGV, &fsa, NULL) != -1); + (void)launchd_assumes(sigaction(SIGABRT, &fsa, NULL) != -1); + (void)launchd_assumes(sigaction(SIGTRAP, &fsa, NULL) != -1); +} + +void *update_thread(void *nothing __attribute__((unused))) +{ + /* use IOPOL_PASSIVE for sync thread */ + (void)launchd_assumes(setiopolicy_np(IOPOL_TYPE_DISK, IOPOL_SCOPE_THREAD, IOPOL_PASSIVE) != -1); + + while( g_sync_frequency ) { + sync(); + sleep(g_sync_frequency); + } + + runtime_syslog(LOG_DEBUG, "Update thread exiting."); + return NULL; } #define PID1_CRASH_LOGFILE "/var/log/launchd-pid1.crash" @@ -178,16 +292,103 @@ static __attribute__((unused)) typeof(sleep) *__junk_dyld_trick2 = sleep; static __attribute__((unused)) typeof(reboot) *__junk_dyld_trick3 = reboot; void -fatal_signal_handler(int sig, siginfo_t *si, void *uap) +do_pid1_crash_diagnosis_mode(const char *msg) +{ + if (g_wsp) { + kill(g_wsp, SIGKILL); + sleep(3); + g_wsp = 0; + } + + while (g_shutdown_debugging && !do_pid1_crash_diagnosis_mode2(msg)) { + sleep(1); + } +} + +int +basic_fork(void) +{ + int wstatus = 0; + pid_t p; + + switch ((p = fork())) { + case -1: + runtime_syslog(LOG_ERR | LOG_CONSOLE, "Can't fork PID 1 copy for crash debugging: %m"); + return p; + case 0: + return p; + default: + do { + (void)waitpid(p, &wstatus, 0); + } while(!WIFEXITED(wstatus)); + + fprintf(stdout, "PID 1 copy: exit status: %d\n", WEXITSTATUS(wstatus)); + + return 1; + } + + return -1; +} + +bool +do_pid1_crash_diagnosis_mode2(const char *msg) +{ + if (basic_fork() == 0) { + /* Neuter our bootstrap port so that the shell doesn't try talking to us while + * we're blocked waiting on it. + */ + if (g_console) { + fflush(g_console); + } + task_set_bootstrap_port(mach_task_self(), MACH_PORT_NULL); + if (basic_fork() != 0) { + if (g_console) { + fflush(g_console); + } + return true; + } + } else { + return true; + } + + int fd; + revoke(_PATH_CONSOLE); + if ((fd = open(_PATH_CONSOLE, O_RDWR)) == -1) { + _exit(2); + } + if (login_tty(fd) == -1) { + _exit(3); + } + setenv("TERM", "vt100", 1); + fprintf(stdout, "\n"); + fprintf(stdout, "Entering launchd PID 1 debugging mode...\n"); + fprintf(stdout, "The PID 1 launchd has crashed %s.\n", msg); + fprintf(stdout, "It has fork(2)ed itself for debugging.\n"); + fprintf(stdout, "To debug the crashing thread of PID 1:\n"); + fprintf(stdout, " gdb attach %d\n", getppid()); + fprintf(stdout, "To exit this shell and shut down:\n"); + fprintf(stdout, " kill -9 1\n"); + fprintf(stdout, "A sample of PID 1 has been written to %s\n", PID1_CRASH_LOGFILE); + fprintf(stdout, "\n"); + fflush(stdout); + + execl(_PATH_BSHELL, "-sh", NULL); + syslog(LOG_ERR, "can't exec %s for PID 1 crash debugging: %m", _PATH_BSHELL); + _exit(EXIT_FAILURE); +} + +void +fatal_signal_handler(int sig, siginfo_t *si, void *uap __attribute__((unused))) { const char *doom_why = "at instruction"; + char msg[128]; char *sample_args[] = { "/usr/bin/sample", "1", "1", "-file", PID1_CRASH_LOGFILE, NULL }; pid_t sample_p; int wstatus; crash_addr = si->si_addr; crash_pid = si->si_pid; - + unlink(PID1_CRASH_LOGFILE); switch ((sample_p = vfork())) { @@ -211,8 +412,9 @@ fatal_signal_handler(int sig, siginfo_t *si, void *uap) doom_why = "trying to read/write"; case SIGILL: case SIGFPE: - runtime_syslog(LOG_EMERG, "We crashed %s: %p (sent by PID %u)", doom_why, crash_addr, crash_pid); + snprintf(msg, sizeof(msg), "%s: %p (%s sent by PID %u)", doom_why, crash_addr, strsignal(sig), crash_pid); sync(); + do_pid1_crash_diagnosis_mode(msg); sleep(3); reboot(0); break; @@ -222,50 +424,104 @@ fatal_signal_handler(int sig, siginfo_t *si, void *uap) void pid1_magic_init(void) { - launchd_assumes(setsid() != -1); - launchd_assumes(chdir("/") != -1); - launchd_assumes(setlogin("root") != -1); - launchd_assumes(mount("fdesc", "/dev", MNT_UNION, NULL) != -1); + (void)launchd_assumes(setsid() != -1); + (void)launchd_assumes(chdir("/") != -1); + (void)launchd_assumes(setlogin("root") != -1); + + strcpy(g_my_label, "com.apple.launchd"); + +#if !TARGET_OS_EMBEDDED + auditinfo_addr_t auinfo = { + .ai_termid = { .at_type = AU_IPv4 }, + .ai_asid = AU_ASSIGN_ASID, + .ai_auid = AU_DEFAUDITID, + .ai_flags = AU_SESSION_FLAG_IS_INITIAL, + }; + + if (!launchd_assumes(setaudit_addr(&auinfo, sizeof(auinfo)) != -1)) { + runtime_syslog(LOG_WARNING | LOG_CONSOLE, "Could not set audit session: %s.", strerror(errno)); + _exit(EXIT_FAILURE); + } + + g_audit_session = auinfo.ai_asid; + runtime_syslog(LOG_DEBUG, "Audit Session ID: %i", g_audit_session); + + g_audit_session_port = _audit_session_self(); +#endif + + strcpy(g_launchd_database_dir, LAUNCHD_DB_PREFIX "/com.apple.launchd"); } +char * +launchd_data_base_path(int db_type) +{ + static char result[PATH_MAX]; + static int last_db_type = -1; + + if (db_type == last_db_type) { + return result; + } + + switch (db_type) { + case LAUNCHD_DB_TYPE_OVERRIDES : + snprintf(result, sizeof(result), "%s/%s", g_launchd_database_dir, "overrides.plist"); + last_db_type = db_type; + break; + case LAUNCHD_DB_TYPE_JOBCACHE : + snprintf(result, sizeof(result), "%s/%s", g_launchd_database_dir, "jobcache.launchdata"); + last_db_type = db_type; + break; + default : + break; + } + + return result; +} int _fd(int fd) { if (fd >= 0) { - launchd_assumes(fcntl(fd, F_SETFD, 1) != -1); + (void)launchd_assumes(fcntl(fd, F_SETFD, 1) != -1); } return fd; } -void -prep_shutdown_log_dir(void) -{ - launchd_assumes(mkdir(SHUTDOWN_LOG_DIR, S_IRWXU) != -1 || errno == EEXIST); -} - void launchd_shutdown(void) { - struct stat sb; + int64_t now; if (shutdown_in_progress) { return; } + runtime_ktrace0(RTKT_LAUNCHD_EXITING); + shutdown_in_progress = true; - if (getpid() == 1 && stat("/var/db/debugShutdownHangs", &sb) != -1) { + if (pid1_magic || g_log_per_user_shutdown) { /* * When this changes to a more sustainable API, update this: * http://howto.apple.com/db.cgi?Debugging_Apps_Non-Responsive_At_Shutdown */ runtime_setlogmask(LOG_UPTO(LOG_DEBUG)); - prep_shutdown_log_dir(); - debug_shutdown_hangs = true; } + runtime_log_push(); + + now = runtime_get_wall_time(); + + char *term_who = pid1_magic ? "System shutdown" : "Per-user launchd termination for "; + runtime_syslog(LOG_INFO, "%s%s began", term_who, pid1_magic ? "" : g_username); + launchd_assert(jobmgr_shutdown(root_jobmgr) != NULL); + +#if HAVE_LIBAUDITD + if (pid1_magic) { + (void)launchd_assumes(audit_quick_stop() == 0); + } +#endif } void @@ -285,15 +541,19 @@ launchd_single_user(void) void launchd_SessionCreate(void) { -#if HAVE_SECURITY - OSStatus (*sescr)(SessionCreationFlags flags, SessionAttributeBits attributes); - void *seclib; - - if (launchd_assumes((seclib = dlopen(SECURITY_LIB, RTLD_LAZY)) != NULL)) { - if (launchd_assumes((sescr = dlsym(seclib, "SessionCreate")) != NULL)) { - launchd_assumes(sescr(0, 0) == noErr); - } - launchd_assumes(dlclose(seclib) != -1); +#if !TARGET_OS_EMBEDDED + auditinfo_addr_t auinfo = { + .ai_termid = { .at_type = AU_IPv4 }, + .ai_asid = AU_ASSIGN_ASID, + .ai_auid = getuid(), + .ai_flags = 0, + }; + if (launchd_assumes(setaudit_addr(&auinfo, sizeof(auinfo)) == 0)) { + char session[16]; + snprintf(session, sizeof(session), "%x", auinfo.ai_asid); + setenv("SECURITYSESSIONID", session, 1); + } else { + runtime_syslog(LOG_WARNING, "Could not set audit session: %s.", strerror(errno)); } #endif } @@ -304,13 +564,13 @@ testfd_or_openfd(int fd, const char *path, int flags) int tmpfd; if (-1 != (tmpfd = dup(fd))) { - launchd_assumes(runtime_close(tmpfd) == 0); + (void)launchd_assumes(runtime_close(tmpfd) == 0); } else { if (-1 == (tmpfd = open(path, flags | O_NOCTTY, DEFFILEMODE))) { runtime_syslog(LOG_ERR, "open(\"%s\", ...): %m", path); } else if (tmpfd != fd) { - launchd_assumes(dup2(tmpfd, fd) != -1); - launchd_assumes(runtime_close(tmpfd) == 0); + (void)launchd_assumes(dup2(tmpfd, fd) != -1); + (void)launchd_assumes(runtime_close(tmpfd) == 0); } } } @@ -325,7 +585,7 @@ get_network_state(void) /* Workaround 4978696: getifaddrs() reports false ENOMEM */ while ((r = getifaddrs(&ifa)) == -1 && errno == ENOMEM) { runtime_syslog(LOG_DEBUG, "Worked around bug: 4978696"); - launchd_assumes(sched_yield() != -1); + (void)launchd_assumes(sched_yield() != -1); } if (!launchd_assumes(r != -1)) { @@ -372,16 +632,16 @@ monitor_networking_state(void) return; } - launchd_assumes(kevent_mod(pfs, EVFILT_READ, EV_ADD, 0, 0, &kqpfsystem_callback) != -1); + (void)launchd_assumes(kevent_mod(pfs, EVFILT_READ, EV_ADD, 0, 0, &kqpfsystem_callback) != -1); } void -pfsystem_callback(void *obj, struct kevent *kev) +pfsystem_callback(void *obj __attribute__((unused)), struct kevent *kev) { bool new_networking_state; char buf[1024]; - launchd_assumes(read(kev->ident, &buf, sizeof(buf)) != -1); + (void)launchd_assumes(read((int)kev->ident, &buf, sizeof(buf)) != -1); new_networking_state = get_network_state(); @@ -399,6 +659,8 @@ _log_launchd_bug(const char *rcs_rev, const char *path, unsigned int line, const const char *file = strrchr(path, '/'); char *rcs_rev_tmp = strchr(rcs_rev, ' '); + runtime_ktrace1(RTKT_LAUNCHD_BUG); + if (!file) { file = path; } else {