* @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 <Security/Authorization.h>
-#include <Security/AuthorizationTags.h>
-#include <Security/AuthSession.h>
-#endif
#include <sys/types.h>
#include <sys/queue.h>
#include <sys/event.h>
#include <sys/mount.h>
#include <sys/kern_event.h>
#include <sys/reboot.h>
+#include <sys/socket.h>
+#include <sys/syscall.h>
#include <net/if.h>
#include <netinet/in.h>
#include <netinet/in_var.h>
#include <setjmp.h>
#include <spawn.h>
#include <sched.h>
+#include <pthread.h>
+#include <util.h>
+
+#if HAVE_LIBAUDITD
+#include <bsm/auditd_lib.h>
+#include <bsm/audit_session.h>
+#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;
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)
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 */
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();
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)))
+{
+ /* <rdar://problem/7385963> 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"
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())) {
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;
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
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
}
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);
}
}
}
/* 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)) {
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();
const char *file = strrchr(path, '/');
char *rcs_rev_tmp = strchr(rcs_rev, ' ');
+ runtime_ktrace1(RTKT_LAUNCHD_BUG);
+
if (!file) {
file = path;
} else {