[launchd-changes] [23794] trunk
source_changes at macosforge.org
source_changes at macosforge.org
Tue Feb 10 17:24:50 PST 2009
Revision: 23794
http://trac.macosforge.org/projects/launchd/changeset/23794
Author: dsorresso at apple.com
Date: 2009-02-10 17:24:50 -0800 (Tue, 10 Feb 2009)
Log Message:
-----------
<rdar://problem/6120453> Add SPI to disable/enable per-user launchd's
<rdar://problem/6424345> audit start up and shut down integration in launchd
<rdar://problem/6460582> Allow a session to be bootstrapped in on-demand mode
<rdar://problem/6549046> Roll back fix for <rdar://problem/5947376>
<rdar://problem/6549469> SnowLeopard: launchd console: gssd-agent errors
<rdar://problem/6551269> K3: 10a261: Black screen hang during restart test.
Modified Paths:
--------------
trunk/launchd/src/config.h
trunk/launchd/src/launchctl.c
trunk/launchd/src/launchd.c
trunk/launchd/src/launchd_core_logic.c
trunk/launchd/src/launchd_core_logic.h
trunk/launchd/src/launchd_runtime.c
trunk/launchd/src/launchd_runtime.h
trunk/launchd/src/launchd_unix_ipc.c
trunk/launchd/src/launchproxy.c
trunk/launchd/src/libvproc.c
trunk/launchd/src/protocol_job_reply.defs
trunk/launchd/src/protocol_vproc.defs
trunk/launchd/src/vproc_priv.h
trunk/launchd.xcodeproj/project.pbxproj
Modified: trunk/launchd/src/config.h
===================================================================
--- trunk/launchd/src/config.h 2009-02-02 21:35:16 UTC (rev 23793)
+++ trunk/launchd/src/config.h 2009-02-11 01:24:50 UTC (rev 23794)
@@ -4,4 +4,5 @@
#define HAVE_QUARANTINE TARGET_HAVE_QUARANTINE
#define HAVE_SANDBOX TARGET_HAVE_SANDBOX
#define HAVE_SECURITY !TARGET_HAVE_EMBEDDED_SECURITY
+#define HAVE_LIBAUDITD !TARGET_OS_EMBEDDED
#endif /* __CONFIG_H__ */
Modified: trunk/launchd/src/launchctl.c
===================================================================
--- trunk/launchd/src/launchctl.c 2009-02-02 21:35:16 UTC (rev 23793)
+++ trunk/launchd/src/launchctl.c 2009-02-11 01:24:50 UTC (rev 23794)
@@ -20,6 +20,7 @@
static const char *const __rcs_file_version__ = "$Revision$";
+#include "config.h"
#include "launch.h"
#include "launch_priv.h"
#include "bootstrap.h"
@@ -81,6 +82,13 @@
#include <spawn.h>
#include <sys/syslimits.h>
+#if HAVE_LIBAUDITD
+#include <bsm/auditd_lib.h>
+#ifndef AUDITD_PLIST_FILE
+#define AUDITD_PLIST_FILE "/System/Library/LaunchDaemons/com.apple.auditd.plist"
+#endif
+#endif
+
extern char **environ;
@@ -1756,6 +1764,9 @@
int hnmib[] = { CTL_KERN, KERN_HOSTNAME };
struct kevent kev;
int kq;
+#if HAVE_LIBAUDITD
+ launch_data_t lda, ldb;
+#endif
do_sysversion_sysctl();
@@ -1862,10 +1873,23 @@
assumes(touch_file(_PATH_VARRUN "/.systemStarterRunning", DEFFILEMODE) != -1);
#endif
+#if HAVE_LIBAUDITD
+ /*
+ * Only start auditing if not "Disabled" in auditd plist.
+ */
+ if ((lda = read_plist_file(AUDITD_PLIST_FILE, false, false)) != NULL &&
+ ((ldb = launch_data_dict_lookup(lda, LAUNCH_JOBKEY_DISABLED)) == NULL ||
+ job_disabled_logic(ldb) == false))
+ {
+ assumes(audit_quick_start() == 0);
+ launch_data_free(lda);
+ }
+#else
if (path_check("/etc/security/rc.audit")) {
const char *audit_tool[] = { _PATH_BSHELL, "/etc/security/rc.audit", NULL };
assumes(fwexec(audit_tool, NULL) != -1);
}
+#endif
do_BootCache_magic(BOOTCACHE_START);
@@ -2079,7 +2103,7 @@
if (strcasecmp(session_type, VPROCMGR_SESSION_BACKGROUND) == 0) {
read_launchd_conf();
-#if HAVE_SECURITY
+#if 0 /* XXX PR-6456403 */
assumes(SessionCreate(sessionKeepCurrentBootstrap, 0) == 0);
#endif
}
Modified: trunk/launchd/src/launchd.c
===================================================================
--- trunk/launchd/src/launchd.c 2009-02-02 21:35:16 UTC (rev 23793)
+++ trunk/launchd/src/launchd.c 2009-02-11 01:24:50 UTC (rev 23794)
@@ -71,6 +71,10 @@
#include <pthread.h>
#include <util.h>
+#if HAVE_LIBAUDITD
+#include <bsm/auditd_lib.h>
+#endif
+
#include "bootstrap.h"
#include "vproc.h"
#include "vproc_priv.h"
@@ -261,6 +265,12 @@
void
do_pid1_crash_diagnosis_mode(void)
{
+ if( g_wsp ) {
+ kill(g_wsp, SIGKILL);
+ sleep(3);
+ g_wsp = 0;
+ }
+
while( g_shutdown_debugging && !do_pid1_crash_diagnosis_mode2() ) {
sleep(1);
}
@@ -269,7 +279,7 @@
int
basic_fork(void)
{
- int wstatus;
+ int wstatus = 0;
pid_t p;
switch ((p = fork())) {
@@ -279,7 +289,14 @@
case 0:
return p;
default:
+ #if 0
+ /* If we attach with the debugger, the kernel reparenting could
+ * cause this to return prematurely.
+ */
waitpid(p, &wstatus, 0);
+ #else
+ sleep(UINT_MAX);
+ #endif
if (WIFEXITED(wstatus)) {
if (WEXITSTATUS(wstatus) == EXIT_SUCCESS) {
return 1;
@@ -326,9 +343,9 @@
fprintf(stdout, "The PID 1 launchd has crashed. It has fork(2)ed itself for debugging.\n");
fprintf(stdout, "To debug the main thread of PID 1:\n");
fprintf(stdout, " gdb attach %d\n", getppid());
- fprintf(stdout, "To exit this shell, capture a sample of launchd and shut down:\n");
- fprintf(stdout, " exit\n");
- fprintf(stdout, "A sample of PID 1 will be written to %s\n", PID1_CRASH_LOGFILE);
+ 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);
@@ -347,8 +364,6 @@
crash_addr = si->si_addr;
crash_pid = si->si_pid;
-
- do_pid1_crash_diagnosis_mode();
unlink(PID1_CRASH_LOGFILE);
@@ -364,6 +379,8 @@
break;
}
+ do_pid1_crash_diagnosis_mode();
+
switch (sig) {
default:
case 0:
@@ -428,6 +445,12 @@
runtime_syslog(LOG_NOTICE, "%s%s began", term_who, pid1_magic ? "" : g_username);
launchd_assert(jobmgr_shutdown(root_jobmgr) != NULL);
+
+#if HAVE_LIBAUDITD
+ if( pid1_magic ) {
+ launchd_assumes(audit_quick_stop() == 0);
+ }
+#endif
}
void
Modified: trunk/launchd/src/launchd_core_logic.c
===================================================================
--- trunk/launchd/src/launchd_core_logic.c 2009-02-02 21:35:16 UTC (rev 23793)
+++ trunk/launchd/src/launchd_core_logic.c 2009-02-11 01:24:50 UTC (rev 23794)
@@ -148,6 +148,14 @@
static bool waiting4removal_new(job_t j, mach_port_t rp);
static void waiting4removal_delete(job_t j, struct waiting_for_removal *w4r);
+struct waiting_for_exit {
+ LIST_ENTRY(waiting_for_exit) sle;
+ mach_port_t rp;
+};
+
+static bool waiting4exit_new(job_t j, mach_port_t rp);
+static void waiting4exit_delete(job_t j, struct waiting_for_exit *w4e);
+
struct mspolicy {
SLIST_ENTRY(mspolicy) sle;
unsigned int allow:1, per_pid:1, __junk:30;
@@ -363,7 +371,7 @@
static void jobmgr_reap_bulk(jobmgr_t jm, struct kevent *kev);
static void jobmgr_log_stray_children(jobmgr_t jm, bool kill_strays);
static void jobmgr_kill_stray_child(jobmgr_t jm, pid_t p);
-static void jobmgr_remove(jobmgr_t jm, bool expect_real_jobs);
+static void jobmgr_remove(jobmgr_t jm);
static void jobmgr_dispatch_all(jobmgr_t jm, bool newmounthack);
static void jobmgr_dequeue_next_sample(jobmgr_t jm);
static job_t jobmgr_init_session(jobmgr_t jm, const char *session_type, bool sflag);
@@ -371,6 +379,7 @@
static job_t jobmgr_find_by_pid(jobmgr_t jm, pid_t p, bool create_anon);
static jobmgr_t jobmgr_find_by_name(jobmgr_t jm, const char *where);
static job_t job_mig_intran2(jobmgr_t jm, mach_port_t mport, pid_t upid);
+static job_t jobmgr_lookup_per_user_context_internal(job_t j, uid_t which_user, bool dispatch, mach_port_t *mp);
static void job_export_all2(jobmgr_t jm, launch_data_t where);
static void jobmgr_callback(void *obj, struct kevent *kev);
static void jobmgr_setup_env_from_other_jobs(jobmgr_t jm);
@@ -390,8 +399,11 @@
LIST_ENTRY(job_s) pid_hash_sle;
LIST_ENTRY(job_s) label_hash_sle;
LIST_ENTRY(job_s) global_env_sle;
+ LIST_ENTRY(job_s) suspended_peruser_sle;
STAILQ_ENTRY(job_s) pending_samples_sle;
SLIST_ENTRY(job_s) curious_jobs_sle;
+ LIST_HEAD(, job_s) suspended_perusers;
+ LIST_HEAD(, waiting_for_exit) exit_watchers;
SLIST_HEAD(, socketgroup) sockets;
SLIST_HEAD(, calendarinterval) cal_intervals;
SLIST_HEAD(, envitem) global_env;
@@ -444,6 +456,7 @@
uint64_t start_time;
uint32_t min_run_time;
uint32_t start_interval;
+ uint32_t peruser_suspend_count; /* The number of jobs that have disabled this per-user launchd. */
#if 0
/* someday ... */
enum {
@@ -494,6 +507,7 @@
deny_job_creation :1, /* Don't let this job create new 'job_t' objects in launchd */
kill_via_shmem :1, /* man launchd.plist --> EnableTransactions */
sent_kill_via_shmem :1, /* We need to 'kill_via_shmem' once-and-only-once */
+ clean_kill :1, /* The job was sent SIGKILL because it was clean. */
pending_sample :1, /* This job needs to be sampled for some reason. */
kill_after_sample :1, /* The job is to be killed after sampling. */
is_being_sampled :1, /* We've spawned a sample tool to sample the job. */
@@ -505,6 +519,7 @@
jetsam_frontmost :1, /* The job is considered "frontmost" by Jetsam. */
needs_kickoff :1, /* The job is to be kept alive continuously, but it must be initially kicked off. */
is_bootstrapper :1, /* The job is a bootstrapper. */
+ has_console :1, /* The job owns the console. */
migratory :1; /* The (anonymous) job called vprocmgr_switch_to_session(). */
mode_t mask;
pid_t tracing_pid;
@@ -598,6 +613,10 @@
static void extract_rcsid_substr(const char *i, char *o, size_t osz);
static void do_first_per_user_launchd_hack(void);
static void simulate_pid1_crash(void);
+static pid_t spawn_sync(job_t j);
+static pid_t basic_spawn(job_t j, void (*what_to_do)(job_t));
+static void take_sample(job_t j);
+static void do_sync(job_t j);
void eliminate_double_reboot(void);
@@ -613,6 +632,7 @@
static bool did_first_per_user_launchd_BootCache_hack;
#define JOB_BOOTCACHE_HACK_CHECK(j) (unlikely(j->per_user && !did_first_per_user_launchd_BootCache_hack && (j->mach_uid >= 500) && (j->mach_uid != (uid_t)-2)))
static job_t workaround_5477111;
+static pid_t s_update_pid = 0;
/* process wide globals */
mach_port_t inherited_bootstrap_port;
@@ -711,6 +731,7 @@
j->sent_signal_time = runtime_get_opaque_time();
if (newval < 0) {
+ j->clean_kill = true;
job_kill(j);
} else {
/*
@@ -892,11 +913,10 @@
}
LIST_FOREACH(ji, &jm->jobs, sle) {
- why_active = job_active(ji);
-
- job_log(ji, LOG_DEBUG | LOG_CONSOLE, "%s", why_active ? why_active : "Inactive");
+ if( (why_active = job_active(ji)) ) {
+ job_log(ji, LOG_DEBUG | LOG_CONSOLE, "%s", why_active);
+ }
}
-
}
static void
@@ -926,7 +946,7 @@
}
void
-jobmgr_remove(jobmgr_t jm, bool expect_real_jobs)
+jobmgr_remove(jobmgr_t jm)
{
jobmgr_t jmi;
job_t ji;
@@ -934,22 +954,16 @@
jobmgr_log(jm, LOG_DEBUG, "Removing job manager.");
if (!jobmgr_assumes(jm, SLIST_EMPTY(&jm->submgrs))) {
while ((jmi = SLIST_FIRST(&jm->submgrs))) {
- jobmgr_remove(jmi, false);
+ jobmgr_remove(jmi);
}
}
while( (ji = LIST_FIRST(&jm->jobs)) ) {
- if( !expect_real_jobs && ji->p && !job_assumes(ji, ji->anonymous) ) {
- job_log(ji, LOG_WARNING | LOG_CONSOLE, "%s() called incorrectly with non-anonymous job still active. Forcing removal.", __func__);
- job_remove(ji, true);
- } else {
- if( !ji->anonymous && ji->p ) {
- job_log(ji, LOG_WARNING | LOG_CONSOLE, "Job has overstayed its welcome. Forcing removal.");
- job_remove(ji, true);
- } else {
- job_remove(ji, false);
- }
+ if( !ji->anonymous && ji->p ) {
+ job_log(ji, LOG_WARNING | LOG_CONSOLE, "Job has overstayed its welcome. Forcing removal.");
+ ji->p = 0;
}
+ job_remove(ji);
}
if (jm->req_port) {
@@ -979,7 +993,7 @@
}
void
-job_remove(job_t j, bool force)
+job_remove(job_t j)
{
struct waiting_for_removal *w4r;
struct calendarinterval *ci;
@@ -993,7 +1007,7 @@
if (unlikely(j->p)) {
if (j->anonymous) {
job_reap(j);
- } else if( !force ) {
+ } else {
job_log(j, LOG_DEBUG, "Removal pended until the job exits");
if (!j->removal_pending) {
@@ -1256,7 +1270,7 @@
out_bad:
if (jr) {
- job_remove(jr, false);
+ job_remove(jr);
}
return NULL;
}
@@ -1484,6 +1498,10 @@
j->argv[i] = NULL;
}
+ if( strcmp(j->label, "com.apple.WindowServer") == 0 ) {
+ j->has_console = true;
+ }
+
LIST_INSERT_HEAD(&jm->jobs, j, sle);
LIST_INSERT_HEAD(&label_hash[hash_label(j->label)], j, label_hash_sle);
@@ -2351,7 +2369,7 @@
if (unlikely(j && (j != workaround_5477111) && j->unload_at_mig_return)) {
job_log(j, LOG_NOTICE, "Unloading PID %u at MIG return.", j->p);
- job_remove(j, false);
+ job_remove(j);
}
workaround_5477111 = NULL;
@@ -2509,15 +2527,6 @@
kevent_mod((uintptr_t)&j->exit_timeout, EVFILT_TIMER, EV_DELETE, 0, 0, NULL);
}
- if (j->anonymous) {
- total_anon_children--;
- if( j->migratory ) {
- runtime_del_ref();
- }
- } else {
- runtime_del_ref();
- total_children--;
- }
LIST_REMOVE(j, pid_hash_sle);
if (j->wait_reply_port) {
@@ -2566,7 +2575,7 @@
int s = WTERMSIG(status);
if ((SIGKILL == s || SIGTERM == s) && !j->stopped) {
job_log(j, LOG_NOTICE, "Exited: %s", strsignal(s));
- } else {
+ } else if( !j->stopped && !j->clean_kill ) {
switch( s ) {
/* Signals which indicate a crash. */
case SIGILL :
@@ -2607,6 +2616,33 @@
}
}
+ job_t ji = NULL;
+ while( (ji = LIST_FIRST(&j->suspended_perusers)) ) {
+ job_log(j, LOG_ERR, "Job exited before resuming per-user launchd for UID %u. Will forcibly resume.", ji->mach_uid);
+ ji->peruser_suspend_count--;
+ job_dispatch(ji, false);
+ LIST_REMOVE(ji, suspended_peruser_sle);
+ }
+
+ struct waiting_for_exit *w4e = NULL;
+ while( (w4e = LIST_FIRST(&j->exit_watchers)) ) {
+ waiting4exit_delete(j, w4e);
+ }
+
+ if (j->anonymous) {
+ total_anon_children--;
+ if( j->migratory ) {
+ runtime_del_ref();
+ }
+ } else {
+ runtime_del_ref();
+ total_children--;
+ }
+
+ if( j->has_console ) {
+ g_wsp = 0;
+ }
+
if (j->hopefully_exits_first) {
j->mgr->hopefully_first_cnt--;
} else if (!j->anonymous && !j->hopefully_exits_last) {
@@ -2615,6 +2651,7 @@
j->last_exit_status = status;
j->sent_signal_time = 0;
j->sent_sigkill = false;
+ j->clean_kill = false;
j->sampling_complete = false;
j->sent_kill_via_shmem = false;
j->lastlookup = NULL;
@@ -2655,7 +2692,133 @@
}
}
+/* Maybe someday... */
+pid_t
+spawn_sync(job_t j)
+{
+ pid_t p = 0;
+ char *sync_args[] = { "/bin/sync", NULL };
+ switch( (p = vfork()) ) {
+ case 0 :
+ execve("/bin/sync", sync_args, environ);
+ _exit(EXIT_FAILURE);
+ case -1 :
+ job_log(j, LOG_NOTICE, "vfork(2) failed: %d", errno);
+ break;
+ default :
+ break;
+ }
+
+ int r = -1;
+ if( p != -1 ) {
+ /* Let us know when the process is done. ONESHOT is implicit if we're just interested in NOTE_EXIT. */
+ if( !job_assumes(j, (r = kevent_mod(p, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, j)) != -1) ) {
+ if( errno != ESRCH ) {
+ job_assumes(j, runtime_kill(p, SIGKILL) != -1);
+ }
+ }
+
+ int status = 0;
+ if( r == -1 ) {
+ job_assumes(j, waitpid(p, &status, WNOHANG) != -1);
+ }
+ }
+
+ return p;
+}
+
+pid_t
+basic_spawn(job_t j, void (*what_to_do)(job_t))
+{
+ pid_t p = 0;
+ thread_state_flavor_t f = 0;
+#if defined (__ppc__) || defined(__ppc64__)
+ f = PPC_THREAD_STATE64;
+#elif defined(__i386__) || defined(__x86_64__)
+ f = x86_THREAD_STATE;
+#elif defined(__arm__)
+ f = ARM_THREAD_STATE;
+#else
+ #error "unknown architecture"
+#endif
+
+ int execpair[2] = { 0, 0 };
+ job_assumes(j, socketpair(AF_UNIX, SOCK_STREAM, 0, execpair) != -1);
+
+ switch( (p = fork()) ) {
+ case 0 :
+ job_assumes(j, runtime_close(execpair[0]) != -1);
+ /* Handle exceptions directly. */
+ task_set_exception_ports(mach_task_self(), EXC_MASK_CRASH, runtime_get_kernel_port(), EXCEPTION_STATE_IDENTITY | MACH_EXCEPTION_CODES, f);
+
+ /* Wait for the parent to attach a kevent. */
+ read(_fd(execpair[1]), &p, sizeof(p));
+ what_to_do(j);
+ _exit(EXIT_FAILURE);
+ case -1 :
+ job_assumes(j, runtime_close(execpair[0]) != -1);
+ job_assumes(j, runtime_close(execpair[1]) != -1);
+ execpair[0] = -1;
+ execpair[1] = -1;
+ job_log(j, LOG_NOTICE | LOG_CONSOLE, "fork(2) failed: %d", errno);
+ break;
+ default :
+ job_assumes(j, runtime_close(execpair[1]) != -1);
+ execpair[1] = -1;
+ break;
+ }
+
+ int r = -1;
+ if( p != -1 ) {
+ /* Let us know when sample is done. ONESHOT is implicit if we're just interested in NOTE_EXIT. */
+ if( job_assumes(j, (r = kevent_mod(p, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, j)) != -1) ) {
+ if( !job_assumes(j, write(execpair[0], &p, sizeof(p)) == sizeof(p)) ) {
+ job_assumes(j, kevent_mod(p, EVFILT_PROC, EV_DELETE, 0, 0, NULL) != -1);
+ job_assumes(j, runtime_kill(p, SIGKILL) != -1);
+ r = -1;
+ p = -1;
+ }
+ } else {
+ job_assumes(j, runtime_kill(p, SIGKILL) != -1);
+ }
+
+ int status = 0;
+ if( r == -1 ) {
+ job_assumes(j, waitpid(p, &status, WNOHANG) != -1);
+ }
+ }
+
+ if( execpair[0] != -1 ) {
+ job_assumes(j, runtime_close(execpair[0]) != -1);
+ }
+
+ if( execpair[1] != -1 ) {
+ job_assumes(j, runtime_close(execpair[0]) != -1);
+ }
+
+ return p;
+}
+
void
+take_sample(job_t j)
+{
+ char pidstr[32];
+ snprintf(pidstr, sizeof(pidstr), "%u", j->p);
+ char *sample_args[] = { "/usr/bin/sample", pidstr, "1", "-unsupportedShowArch", "-mayDie", "-file", j->mgr->sample_log_file, NULL };
+
+ execve(sample_args[0], sample_args, environ);
+ _exit(EXIT_FAILURE);
+}
+
+void
+do_sync(job_t j __attribute__((unused)))
+{
+ char *sync_args[] = { "/bin/sync", NULL };
+ execve(sync_args[0], sync_args, environ);
+ _exit(EXIT_FAILURE);
+}
+
+void
jobmgr_dequeue_next_sample(jobmgr_t jm)
{
if( STAILQ_EMPTY(&jm->pending_samples) ) {
@@ -2687,103 +2850,18 @@
snprintf(j->mgr->sample_log_file, sizeof(j->mgr->sample_log_file), SHUTDOWN_LOG_DIR "/%s-%u.sample.txt", j->label, j->p);
if (job_assumes(j, unlink(jm->sample_log_file) != -1 || errno == ENOENT)) {
- pid_t sp = 0;
- char *sample_args[] = { "/usr/bin/sample", pidstr, "1", "-unsupportedShowArch", "-mayDie", "-file", j->mgr->sample_log_file, NULL };
- thread_state_flavor_t f = 0;
- #if defined (__ppc__) || defined(__ppc64__)
- f = PPC_THREAD_STATE64;
- #elif defined(__i386__) || defined(__x86_64__)
- f = x86_THREAD_STATE;
- #elif defined(__arm__)
- f = ARM_THREAD_STATE;
- #else
- #error "unknown architecture"
- #endif
+ pid_t sp = basic_spawn(j, take_sample);
- #if NEEDS_MULTI_THREADED_EXEC
- posix_spawnattr_t psattr;
- posix_spawnattr_init(psattr);
- posix_spawnattr_setexceptionports_np(&psattr, EXC_MASK_CRASH, runtime_get_kernel_port(), EXCEPTION_STATE_IDENTITY | MACH_EXCEPTION_CODES , f);
-
- if (!job_assumes(j, (errno = posix_spawnp(&sp, sample_args[0], NULL, &psattr, sample_args, environ)) == 0)) {
+ if( sp == -1 ) {
job_log(j, LOG_ERR | LOG_CONSOLE, "Sampling for job failed!");
STAILQ_REMOVE(&jm->pending_samples, j, job_s, pending_samples_sle);
+ j->sampling_complete = true;
jobmgr_dequeue_next_sample(jm);
} else {
j->tracing_pid = sp;
-
- /* Let us know when sample is done. ONESHOT is implicit if we're just interested in NOTE_EXIT. */
- job_assumes(j, kevent_mod(sp, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, j) != -1);
- }
-
- posix_spawnattr_destroy(&psattr);
- #else
- int execpair[2] = { 0, 0 };
- job_assumes(j, socketpair(AF_UNIX, SOCK_STREAM, 0, execpair) != -1);
-
- switch( (sp = fork()) ) {
- case 0 :
- job_assumes(j, runtime_close(execpair[0]) != -1);
- /* Handle sample's exceptions directly, since ReportCrash will not be able to. */
- task_set_exception_ports(mach_task_self(), EXC_MASK_CRASH, runtime_get_kernel_port(), EXCEPTION_STATE_IDENTITY | MACH_EXCEPTION_CODES, f);
-
- /* Wait for the parent to attach a kevent. */
- read(_fd(execpair[1]), &sp, sizeof(sp));
- execve(sample_args[0], sample_args, environ);
- job_log(j, LOG_NOTICE | LOG_CONSOLE, "Could not exec(2): %d", errno);
- _exit(EXIT_FAILURE);
- case -1 :
- job_assumes(j, runtime_close(execpair[0]) != -1);
- job_assumes(j, runtime_close(execpair[1]) != -1);
- execpair[0] = -1;
- execpair[1] = -1;
- job_log(j, LOG_NOTICE | LOG_CONSOLE, "fork(2) failed: %d", errno);
- break;
- default :
- job_assumes(j, runtime_close(execpair[1]) != -1);
- execpair[1] = -1;
- break;
- }
-
- int r = -1;
- if( sp != -1 ) {
- /* Let us know when sample is done. ONESHOT is implicit if we're just interested in NOTE_EXIT. */
- if( job_assumes(j, (r = kevent_mod(sp, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, j)) != -1) ) {
- if( job_assumes(j, write(execpair[0], &sp, sizeof(sp)) == sizeof(sp)) ) {
- j->tracing_pid = sp;
- j->is_being_sampled = true;
- } else {
- job_assumes(j, kevent_mod(sp, EVFILT_PROC, EV_DELETE, 0, 0, NULL) != -1);
- job_assumes(j, runtime_kill(sp, SIGKILL) != -1);
- r = -1;
- }
- } else {
- job_assumes(j, runtime_kill(sp, SIGKILL) != -1);
- }
-
- int status = 0;
- if( r == -1 ) {
- job_assumes(j, waitpid(sp, &status, WNOHANG) != -1);
- }
- }
-
- if( execpair[0] != -1 ) {
- job_assumes(j, runtime_close(execpair[0]) != -1);
- }
-
- if( execpair[1] != -1 ) {
- job_assumes(j, runtime_close(execpair[0]) != -1);
- }
-
- if( r == -1 ) {
- job_log(j, LOG_ERR | LOG_CONSOLE, "Sampling for job failed!");
- STAILQ_REMOVE(&jm->pending_samples, j, job_s, pending_samples_sle);
- j->sampling_complete = true;
- jobmgr_dequeue_next_sample(jm);
- } else {
+ j->is_being_sampled = true;
job_log(j, LOG_DEBUG | LOG_CONSOLE, "Sampling job (sample PID: %i, file: %s).", sp, j->mgr->sample_log_file);
}
- #endif
} else {
STAILQ_REMOVE(&jm->pending_samples, j, job_s, pending_samples_sle);
j->sampling_complete = true;
@@ -2823,9 +2901,13 @@
*/
if (!job_active(j)) {
if (job_useless(j)) {
- job_remove(j, false);
+ job_remove(j);
return NULL;
- }
+ }
+ if( unlikely(j->per_user && j->peruser_suspend_count > 0) ) {
+ return NULL;
+ }
+
if (kickstart || job_keepalive(j)) {
job_log(j, LOG_DEBUG, "Starting job (kickstart = %s)", kickstart ? "true" : "false");
job_start(j);
@@ -3011,6 +3093,14 @@
int fflags = kev->fflags;
if( fflags & NOTE_EXIT ) {
+ if( s_update_pid == (pid_t)kev->ident ) {
+ int status = 0;
+ job_assumes(j, waitpid(s_update_pid, &status, 0) == 0);
+ job_log(j, LOG_NOTICE, "Reaping update job (PID %i, exit status %i)", s_update_pid, WEXITSTATUS(status));
+
+ s_update_pid = 0;
+ }
+
if( j->p == (pid_t)kev->ident && !j->anonymous && !j->is_being_sampled ) {
int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, j->p };
struct kinfo_proc kp;
@@ -3086,7 +3176,7 @@
job_reap(j);
if (j->anonymous) {
- job_remove(j, false);
+ job_remove(j);
j = NULL;
} else {
j = job_dispatch(j, false);
@@ -3111,6 +3201,12 @@
job_log(j, LOG_DEBUG, "&j->start_interval == ident (%p)", ident);
j->start_pending = true;
job_dispatch(j, false);
+ } else if( do_sync == ident ) {
+ pid_t p = spawn_sync(j);
+ if( job_assumes(j, p != -1) ) {
+ job_log(j, LOG_NOTICE, "Starting update job (PID %i)", p);
+ s_update_pid = p;
+ }
} else if (&j->exit_timeout == ident) {
if( !job_assumes(j, j->p != 0) ) {
return;
@@ -3403,6 +3499,10 @@
}
}
+ if( j->has_console ) {
+ g_wsp = c;
+ }
+
runtime_add_ref();
total_children++;
LIST_INSERT_HEAD(&j->mgr->active_jobs[ACTIVE_JOB_HASH(c)], j, pid_hash_sle);
@@ -3822,9 +3922,24 @@
*/
if (likely(!j->no_init_groups)) {
+ #if 1
if (!job_assumes(j, initgroups(loginname, desired_gid) != -1)) {
_exit(EXIT_FAILURE);
}
+ #else
+ /* Do our own little initgroups(). We do this to guarantee that we're
+ * always opted into dynamic group resolution in the kernel. initgroups(3)
+ * does not make this guarantee.
+ */
+ int groups[NGROUPS], ngroups;
+
+ /* A failure here isn't fatal, and we'll still get data we can use. */
+ job_assumes(j, getgrouplist(j->username, desired_gid, groups, &ngroups) != -1);
+
+ if( !job_assumes(j, syscall(SYS_initgroups, ngroups, groups, desired_uid) != -1) ) {
+ _exit(EXIT_FAILURE);
+ }
+ #endif
}
if (!job_assumes(j, setuid(desired_uid) != -1)) {
@@ -5269,109 +5384,115 @@
jobmgr_t
jobmgr_do_hopefully_first_shutdown_phase(jobmgr_t jm)
{
- jobmgr_t _jm = jm;
if( !jm->shutting_down ) {
- return _jm;
+ return jm;
}
+
+ if( jm->killed_hopefully_first_jobs ) {
+ return NULL;
+ }
+
+ jobmgr_log(jm, LOG_DEBUG, "Doing first phase of garbage collection.");
- bool should_proceed = !jm->killed_hopefully_first_jobs;
-
+ uint32_t unkilled_cnt = 0;
job_t ji = NULL, jn = NULL;
- if( should_proceed ) {
- jobmgr_log(jm, LOG_DEBUG, "Doing first phase of garbage collection.");
- uint32_t unkilled_cnt = 0;
- LIST_FOREACH_SAFE( ji, &jm->jobs, sle, jn ) {
- if( jm->parentmgr ) {
- job_log(ji, LOG_DEBUG, "Examining...");
- }
- if( ji->hopefully_exits_first ) {
- bool active = job_active(ji);
- if( active && !ji->stopped ) {
- job_stop(ji);
-
- /* We may have sent SIGKILL to the job in job_stop(). */
- unkilled_cnt += !ji->sent_sigkill ? 1 : 0;
- } else if( ji->stopped ) {
- unkilled_cnt += !ji->sent_sigkill ? 1 : 0;
- } else if( !active ) {
- job_remove(ji, false);
- }
- }
+ LIST_FOREACH_SAFE( ji, &jm->jobs, sle, jn ) {
+ if( !ji->hopefully_exits_first ) {
+ continue;
}
- /* If we've killed everyone, move on. */
- if( unkilled_cnt == 0 ) {
- jm->killed_hopefully_first_jobs = true;
- _jm = NULL;
+ bool active = job_active(ji);
+ if( active && !ji->stopped ) {
+ /* If the job is active and we haven't told it to stop yet, stop it. */
+ job_stop(ji);
+
+ /* We may have sent SIGKILL to the job in job_stop(). In this case,
+ * "clean" jobs should exit immediately, so we shouldn't have to wait
+ * for them.
+ */
+ unkilled_cnt += !ji->sent_sigkill ? 1 : 0;
+ } else if( ji->stopped ) {
+ /* If the job is active and has been told to stop, disregard it
+ * after we've sent SIGKILL.
+ */
+ unkilled_cnt += !ji->sent_sigkill ? 1 : 0;
+ } else if( !active ) {
+ /* If the job was not active when shutdown began, remove it. */
+ job_remove(ji);
}
- } else {
+ }
+
+ /* If we've killed everyone, move on. */
+ if( unkilled_cnt == 0 ) {
jm->killed_hopefully_first_jobs = true;
- _jm = NULL;
+ jm = NULL;
}
- return _jm;
+ return jm;
}
jobmgr_t
jobmgr_do_normal_shutdown_phase(jobmgr_t jm)
{
- jobmgr_t _jm = jm;
if( !jm->shutting_down ) {
- return _jm;
+ return jm;
}
- bool should_proceed = !jm->killed_normal_jobs;
+ if( jm->killed_normal_jobs ) {
+ return NULL;
+ }
+ jobmgr_log(jm, LOG_DEBUG, "Doing second phase of garbage collection.");
+
+ uint32_t unkilled_cnt = 0;
job_t ji = NULL, jn = NULL;
- if( should_proceed ) {
- jobmgr_log(jm, LOG_DEBUG, "Doing second phase of garbage collection.");
- uint32_t unkilled_cnt = 0;
- LIST_FOREACH_SAFE( ji, &jm->jobs, sle, jn ) {
- if( jm->parentmgr ) {
- job_log(ji, LOG_DEBUG, "Examining...");
- }
-
- if( !(ji->hopefully_exits_first || ji->hopefully_exits_last) ) {
- if( !ji->anonymous ) {
- bool active = job_active(ji);
- if( active && !ji->stopped ) {
- job_stop(ji);
-
- /* We may have sent SIGKILL to the job in job_stop(). */
- unkilled_cnt += !ji->sent_sigkill ? 1 : 0;
- } else if( ji->stopped ) {
- unkilled_cnt += !ji->sent_sigkill ? 1 : 0;
- } else if( !active ) {
- job_remove(ji, false);
- }
- } else if( ji->migratory && jm->parentmgr ) {
- /* If a job has migrated into a sub-bootstrap, we want to
- * keep the job manager around as long as the job is there.
- */
- unkilled_cnt++;
- }
- }
+ LIST_FOREACH_SAFE( ji, &jm->jobs, sle, jn ) {
+ if( ji->migratory ) {
+ /* If we're shutting down, release the hold migratory jobs
+ * have on us.
+ */
+ job_remove(ji);
}
- /* If we've killed everyone, move on. */
- if( unkilled_cnt == 0 ) {
- jm->killed_normal_jobs = true;
- _jm = NULL;
+ if( ji->anonymous || ji->hopefully_exits_first || ji->hopefully_exits_last ) {
+ continue;
}
- } else {
- jm->killed_normal_jobs = true;
- _jm = NULL;
+
+ bool active = job_active(ji);
+ if( active && !ji->stopped ) {
+ /* If the job is active and we haven't told it to stop yet, stop it. */
+ job_stop(ji);
+
+ /* We may have sent SIGKILL to the job in job_stop(). In this case,
+ * "clean" jobs should exit immediately, so we shouldn't have to wait
+ * for them.
+ */
+ unkilled_cnt += !ji->sent_sigkill ? 1 : 0;
+ } else if( ji->stopped ) {
+ /* If the job is active and has been told to stop, disregard it
+ * after we've sent SIGKILL.
+ */
+ unkilled_cnt += !ji->sent_sigkill ? 1 : 0;
+ } else if( !active ) {
+ /* If the job was not active when shutdown began, remove it. */
+ job_remove(ji);
+ }
}
- return _jm;
+ /* If we've killed everyone, move on. */
+ if( unkilled_cnt == 0 ) {
+ jm->killed_hopefully_first_jobs = true;
+ jm = NULL;
+ }
+
+ return jm;
}
jobmgr_t
jobmgr_do_hopefully_last_shutdown_phase(jobmgr_t jm)
{
- jobmgr_t _jm = jm;
if( !jm->shutting_down ) {
- return _jm;
+ return jm;
}
if( jm == root_jobmgr ) {
@@ -5384,42 +5505,46 @@
killed_stray_jobs = true;
}
- bool should_proceed = !jm->killed_hopefully_last_jobs && total_children != 0;
+ if( jm->killed_hopefully_last_jobs || total_children == 0 ) {
+ return NULL;
+ }
+ uint32_t unkilled_cnt = 0;
job_t ji = NULL, jn = NULL;
- if( should_proceed ) {
- jobmgr_log(jm, LOG_DEBUG, "Doing third phase of garbage collection.");
- uint32_t unkilled_cnt = 0;
- LIST_FOREACH_SAFE( ji, &jm->jobs, sle, jn ) {
- if( jm->parentmgr ) {
- job_log(ji, LOG_DEBUG, "Examining...");
- }
- if( ji->hopefully_exits_last ) {
- bool active = job_active(ji);
- if( active && !ji->stopped ) {
- job_stop(ji);
-
- /* We may have sent SIGKILL to the job in job_stop(). */
- unkilled_cnt += !ji->sent_sigkill ? 1 : 0;
- } else if( ji->stopped ) {
- unkilled_cnt += !ji->sent_sigkill ? 1 : 0;
- } else if( !active ) {
- job_remove(ji, false);
- }
- }
+ jobmgr_log(jm, LOG_DEBUG, "Doing third phase of garbage collection.");
+ LIST_FOREACH_SAFE( ji, &jm->jobs, sle, jn ) {
+ if( !ji->hopefully_exits_last ) {
+ continue;
}
- /* If we've killed everyone, move on. */
- if( unkilled_cnt == 0 ) {
- jm->killed_hopefully_last_jobs = true;
- _jm = NULL;
+ bool active = job_active(ji);
+ if( active && !ji->stopped ) {
+ /* If the job is active and we haven't told it to stop yet, stop it. */
+ job_stop(ji);
+
+ /* We may have sent SIGKILL to the job in job_stop(). In this case,
+ * "clean" jobs should exit immediately, so we shouldn't have to wait
+ * for them.
+ */
+ unkilled_cnt += !ji->sent_sigkill ? 1 : 0;
+ } else if( ji->stopped ) {
+ /* If the job is active and has been told to stop, disregard it
+ * after we've sent SIGKILL.
+ */
+ unkilled_cnt += !ji->sent_sigkill ? 1 : 0;
+ } else if( !active ) {
+ /* If the job was not active when shutdown began, remove it. */
+ job_remove(ji);
}
- } else {
- jm->killed_hopefully_last_jobs = true;
- _jm = NULL;
}
- return _jm;
+ /* If we've killed everyone, move on. */
+ if( unkilled_cnt == 0 ) {
+ jm->killed_hopefully_first_jobs = true;
+ jm = NULL;
+ }
+
+ return jm;
}
jobmgr_t
@@ -5439,22 +5564,12 @@
_jm = jobmgr_do_normal_shutdown_phase(jm) ? : jobmgr_do_hopefully_last_shutdown_phase(jm);
}
- if( !_jm ) {
+ if( !_jm && SLIST_EMPTY(&jm->submgrs) ) {
jobmgr_log(jm, LOG_NOTICE | LOG_CONSOLE, "Removing.");
jobmgr_log_stray_children(jm, false);
-
- job_t ji = NULL;
- LIST_FOREACH( ji, &jm->jobs, sle ) {
- if( pid1_magic && !jm->parentmgr ) {
- if( (ji->anonymous || ji->sent_sigkill) && ji->p ) {
- job_log(ji, LOG_NOTICE | LOG_CONSOLE, "Job should be gone but is not.");
- }
- } else if( !pid1_magic && !ji->anonymous ) {
- job_log(ji, LOG_NOTICE | LOG_CONSOLE, "Job should be gone but is not.");
- }
- }
-
- jobmgr_remove(jm, total_children != 0);
+ jobmgr_remove(jm);
+ } else {
+ _jm = jm;
}
return _jm;
@@ -5692,7 +5807,7 @@
out_bad:
if (jmr) {
- jobmgr_remove(jmr, false);
+ jobmgr_remove(jmr);
}
return NULL;
}
@@ -5724,6 +5839,11 @@
#if TARGET_OS_EMBEDDED
bootstrapper->stderrpath = strdup(_PATH_CONSOLE);
#endif
+
+ #if 0
+ /* Start the update job. */
+ jobmgr_assumes(jm, kevent_mod((uintptr_t)do_sync, EVFILT_TIMER, EV_ADD, NOTE_SECONDS, 30, bootstrapper) != -1);
+ #endif
}
}
@@ -6530,7 +6650,7 @@
return BOOTSTRAP_NOT_PRIVILEGED;
}
- job_remove(otherj, false);
+ job_remove(otherj);
if (do_block) {
job_log(j, LOG_DEBUG, "Blocking MIG return of job_remove(): %s", otherj->label);
@@ -6885,6 +7005,45 @@
g_shutdown_debugging = true;
}
break;
+ case VPROC_GSK_PERUSER_SUSPEND:
+ if( pid1_magic && ldc->euid == 0 ) {
+ mach_port_t junk = MACH_PORT_NULL;
+ job_t jpu = jobmgr_lookup_per_user_context_internal(j, (uid_t)inval, false, &junk);
+ if( jpu ) {
+ job_t ji = NULL;
+ LIST_FOREACH( ji, &j->suspended_perusers, suspended_peruser_sle ) {
+ if( (int64_t)(ji->mach_uid) == inval ) {
+ job_log(j, LOG_WARNING, "Job tried to suspend per-user launchd for UID %u twice.", ji->mach_uid);
+ break;
+ }
+ }
+
+ if( ji == NULL ) {
+ jpu->peruser_suspend_count++;
+ LIST_INSERT_HEAD(&j->suspended_perusers, jpu, suspended_peruser_sle);
+ job_stop(jpu);
+ }
+ }
+ }
+ break;
+ case VPROC_GSK_PERUSER_RESUME:
+ if( pid1_magic && ldc->euid == 0 ) {
+ job_t ji = NULL, jt = NULL;
+ LIST_FOREACH_SAFE( ji, &j->suspended_perusers, suspended_peruser_sle, jt ) {
+ if( (int64_t)(ji->mach_uid) == inval ) {
+ ji->peruser_suspend_count--;
+ LIST_REMOVE(ji, suspended_peruser_sle);
+ break;
+ }
+ }
+
+ if( ji == NULL ) {
+ job_log(j, LOG_WARNING, "Job tried to resume per-user launchd for UID %llu that it did not suspend.", inval);
+ } else if( ji->peruser_suspend_count == 0 ) {
+ job_dispatch(ji, false);
+ }
+ }
+ break;
case 0:
break;
default:
@@ -7014,29 +7173,10 @@
return 0;
}
-kern_return_t
-job_mig_lookup_per_user_context(job_t j, uid_t which_user, mach_port_t *up_cont)
+job_t
+jobmgr_lookup_per_user_context_internal(job_t j, uid_t which_user, bool dispatch, mach_port_t *mp)
{
- struct ldcred *ldc = runtime_get_caller_creds();
- job_t ji;
-
- if (!launchd_assumes(j != NULL)) {
- return BOOTSTRAP_NO_MEMORY;
- }
-
- job_log(j, LOG_DEBUG, "Looking up per user launchd for UID: %u", which_user);
-
- if (unlikely(!pid1_magic)) {
- job_log(j, LOG_ERR, "Only PID 1 supports per user launchd lookups.");
- return BOOTSTRAP_NOT_PRIVILEGED;
- }
-
- if (ldc->euid || ldc->uid) {
- which_user = ldc->euid ?: ldc->uid;
- }
-
- *up_cont = MACH_PORT_NULL;
-
+ job_t ji = NULL;
LIST_FOREACH(ji, &root_jobmgr->jobs, sle) {
if (!ji->per_user) {
continue;
@@ -7052,42 +7192,65 @@
}
break;
}
-
- if (unlikely(ji == NULL)) {
+
+ if( unlikely(ji == NULL) ) {
struct machservice *ms;
char lbuf[1024];
-
+
job_log(j, LOG_DEBUG, "Creating per user launchd job for UID: %u", which_user);
-
+
sprintf(lbuf, "com.apple.launchd.peruser.%u", which_user);
-
+
ji = job_new(root_jobmgr, lbuf, "/sbin/launchd", NULL);
-
- if (ji == NULL) {
- return BOOTSTRAP_NO_MEMORY;
+
+ if( ji != NULL ) {
+ ji->mach_uid = which_user;
+ ji->per_user = true;
+ ji->kill_via_shmem = true;
+
+ if ((ms = machservice_new(ji, lbuf, mp, false)) == NULL) {
+ job_remove(ji);
+ ji = NULL;
+ } else {
+ ms->per_user_hack = true;
+ ms->hide = true;
+
+ ji = dispatch ? job_dispatch(ji, false) : ji;
+ }
}
-
- ji->mach_uid = which_user;
- ji->per_user = true;
- ji->kill_via_shmem = true;
-
- if ((ms = machservice_new(ji, lbuf, up_cont, false)) == NULL) {
- job_remove(ji, false);
- return BOOTSTRAP_NO_MEMORY;
- }
-
- ms->per_user_hack = true;
- ms->hide = true;
-
- ji = job_dispatch(ji, false);
} else {
+ *mp = machservice_port(SLIST_FIRST(&ji->machservices));
job_log(j, LOG_DEBUG, "Per user launchd job found for UID: %u", which_user);
}
+
+ return ji;
+}
- if (job_assumes(j, ji != NULL)) {
- *up_cont = machservice_port(SLIST_FIRST(&ji->machservices));
+kern_return_t
+job_mig_lookup_per_user_context(job_t j, uid_t which_user, mach_port_t *up_cont)
+{
+ struct ldcred *ldc = runtime_get_caller_creds();
+ job_t jpu;
+
+ if (!launchd_assumes(j != NULL)) {
+ return BOOTSTRAP_NO_MEMORY;
}
-
+
+ job_log(j, LOG_DEBUG, "Looking up per user launchd for UID: %u", which_user);
+
+ if (unlikely(!pid1_magic)) {
+ job_log(j, LOG_ERR, "Only PID 1 supports per user launchd lookups.");
+ return BOOTSTRAP_NOT_PRIVILEGED;
+ }
+
+ if (ldc->euid || ldc->uid) {
+ which_user = ldc->euid ?: ldc->uid;
+ }
+
+ *up_cont = MACH_PORT_NULL;
+
+ jpu = jobmgr_lookup_per_user_context_internal(j, which_user, true, up_cont);
+
return 0;
}
@@ -7136,7 +7299,6 @@
job_log(j, LOG_WARNING, "Check-in of Mach service failed. Already active: %s", servicename);
return BOOTSTRAP_SERVICE_ACTIVE;
}
-
}
job_checkin(j);
@@ -7726,11 +7888,14 @@
*/
if( flags & LAUNCH_GLOBAL_ON_DEMAND ) {
/* This is so awful. */
- mach_port_t mp = MACH_PORT_NULL;
- name_t target_name;
- strlcpy(target_name, jmr->name, sizeof(target_name));
+ /* Remove the job from its current job manager. */
+ LIST_REMOVE(j, sle);
+ LIST_REMOVE(j, pid_hash_sle);
+
+ /* Put the job into the target job manager. */
+ LIST_INSERT_HEAD(&jmr->jobs, j, sle);
+ LIST_INSERT_HEAD(&jmr->active_jobs[ACTIVE_JOB_HASH(j->p)], j, pid_hash_sle);
- kr = job_mig_switch_to_session(j, MACH_PORT_NULL, target_name, &mp);
if( job_assumes(j, kr == KERN_SUCCESS) ) {
job_set_global_on_demand(j, true);
} else {
@@ -8164,6 +8329,27 @@
}
kern_return_t
+job_mig_wait2(job_t j, job_t target_j, mach_port_t srp, integer_t *status __attribute__((unused)))
+{
+ if( !launchd_assumes(j != NULL) ) {
+ return BOOTSTRAP_NO_MEMORY;
+ }
+ if( !launchd_assumes(target_j != NULL) ) {
+ return BOOTSTRAP_NO_MEMORY;
+ }
+
+ if( target_j->p == 0 ) {
+ return BOOTSTRAP_SUCCESS;
+ }
+
+ if( !job_assumes(j, waiting4exit_new(target_j, srp) == true) ) {
+ return BOOTSTRAP_NO_MEMORY;
+ }
+
+ return MIG_NO_REPLY;
+}
+
+kern_return_t
job_mig_uncork_fork(job_t j)
{
if (!launchd_assumes(j != NULL)) {
@@ -8249,12 +8435,12 @@
}
if (!job_assumes(jr, jr->p)) {
- job_remove(jr, false);
+ job_remove(jr);
return BOOTSTRAP_NO_MEMORY;
}
if (!job_setup_machport(jr)) {
- job_remove(jr, false);
+ job_remove(jr);
return BOOTSTRAP_NO_MEMORY;
}
@@ -8412,6 +8598,29 @@
free(w4r);
}
+bool
+waiting4exit_new(job_t j, mach_port_t rp)
+{
+ struct waiting_for_exit *w4e = NULL;
+ if( !job_assumes(j, (w4e = malloc(sizeof(struct waiting_for_exit))) != NULL) ) {
+ return false;
+ }
+
+ w4e->rp = rp;
+ LIST_INSERT_HEAD(&j->exit_watchers, w4e, sle);
+
+ return true;
+}
+
+void
+waiting4exit_delete(job_t j, struct waiting_for_exit *w4e)
+{
+ job_assumes(j, job_mig_wait2_reply(w4e->rp, KERN_SUCCESS, j->last_exit_status) == KERN_SUCCESS);
+ LIST_REMOVE(w4e, sle);
+
+ free(w4e);
+}
+
size_t
get_kern_max_proc(void)
{
Modified: trunk/launchd/src/launchd_core_logic.h
===================================================================
--- trunk/launchd/src/launchd_core_logic.h 2009-02-02 21:35:16 UTC (rev 23793)
+++ trunk/launchd/src/launchd_core_logic.h 2009-02-11 01:24:50 UTC (rev 23794)
@@ -47,7 +47,7 @@
launch_data_t job_export(job_t j);
void job_stop(job_t j);
void job_checkin(job_t j);
-void job_remove(job_t j, bool force);
+void job_remove(job_t j);
job_t job_import(launch_data_t pload);
launch_data_t job_import_bulk(launch_data_t pload);
job_t job_mig_intran(mach_port_t mp);
Modified: trunk/launchd/src/launchd_runtime.c
===================================================================
--- trunk/launchd/src/launchd_runtime.c 2009-02-02 21:35:16 UTC (rev 23793)
+++ trunk/launchd/src/launchd_runtime.c 2009-02-11 01:24:50 UTC (rev 23794)
@@ -90,7 +90,6 @@
static void mportset_callback(void);
static kq_callback kqmportset_callback = (kq_callback)mportset_callback;
static void *kqueue_demand_loop(void *arg);
-static void log_kevent_struct(int level, struct kevent *kev_base, int indx);
boolean_t launchd_internal_demux(mach_msg_header_t *Request, mach_msg_header_t *Reply);
static void record_caller_creds(mach_msg_header_t *mh);
@@ -134,6 +133,7 @@
bool g_flat_mach_namespace = true;
bool g_simulate_pid1_crash = false;
bool g_use_gmalloc = false;
+pid_t g_wsp = 0;
mach_port_t
runtime_get_kernel_port(void)
@@ -536,11 +536,11 @@
#if 0
if (launchd_assumes(kev.udata != NULL)) {
#endif
- log_kevent_struct(LOG_DEBUG, &kev, 0);
+ log_kevent_struct(LOG_DEBUG, &kev, i);
(*((kq_callback *)kev.udata))(kev.udata, &kev);
#if 0
} else {
- log_kevent_struct(LOG_ERR, &kev, 0);
+ log_kevent_struct(LOG_ERR, &kev, i);
}
#endif
/* the callback may have tainted our ability to continue this for loop */
@@ -586,30 +586,29 @@
bulk_kev = kev;
if (launchd_assumes((bulk_kev_cnt = kevent(fd, NULL, 0, kev, BULK_KEV_MAX, &ts)) != -1)) {
+ #if 0
for (i = 0; i < bulk_kev_cnt; i++) {
log_kevent_struct(LOG_DEBUG, kev, i);
}
+ #endif
for (i = 0; i < bulk_kev_cnt; i++) {
bulk_kev_i = i;
kevi = &kev[i];
if (kevi->filter) {
-#if 0
Dl_info dli;
/* Check if kevi->udata was either malloc(3)ed or is a valid function pointer.
* If neither, it's probably an invalid pointer and we should log it.
*/
if (launchd_assumes(malloc_size(kevi->udata) || dladdr(kevi->udata, &dli))) {
-#endif
- runtime_ktrace(RTKT_LAUNCHD_BSD_KEVENT|DBG_FUNC_START, kevi->ident, kevi->filter, kevi->fflags);
- (*((kq_callback *)kevi->udata))(kevi->udata, kevi);
- runtime_ktrace0(RTKT_LAUNCHD_BSD_KEVENT|DBG_FUNC_END);
-#if 0
+ runtime_ktrace(RTKT_LAUNCHD_BSD_KEVENT|DBG_FUNC_START, kevi->ident, kevi->filter, kevi->fflags);
+ (*((kq_callback *)kevi->udata))(kevi->udata, kevi);
+ runtime_ktrace0(RTKT_LAUNCHD_BSD_KEVENT|DBG_FUNC_END);
} else {
+ runtime_syslog(LOG_ERR, "The following kevent had invalid context data.");
log_kevent_struct(LOG_EMERG, kevi, i);
}
-#endif
}
}
}
@@ -619,8 +618,6 @@
return 0;
}
-
-
void
launchd_runtime(void)
{
@@ -852,6 +849,14 @@
if (flags & EV_ADD && !launchd_assumes(udata != NULL)) {
errno = EINVAL;
return -1;
+ } else if( (flags & EV_DELETE) && bulk_kev ) {
+ int i = 0;
+ for( i = bulk_kev_i + 1; i < bulk_kev_cnt; i++ ) {
+ if( bulk_kev[i].filter == filter && bulk_kev[i].ident == ident ) {
+ runtime_syslog(LOG_DEBUG, "Skipping PROC event for PID %lu", ident);
+ bulk_kev[i].filter = 0;
+ }
+ }
}
EV_SET(&kev, ident, filter, flags, fflags, data, udata);
@@ -1349,7 +1354,31 @@
offset = (void *)*outval;
+#if 0
+ if( !ourlogfile && !pid1_magic && shutdown_in_progress ) {
+ char logfile[NAME_MAX];
+ snprintf(logfile, sizeof(logfile), "/var/tmp/launchd-%s.shutdown.log", g_username);
+
+ char logfile1[NAME_MAX];
+ snprintf(logfile1, sizeof(logfile1), "/var/tmp/launchd-%s.shutdown.log.1", g_username);
+
+ rename(logfile, logfile1);
+ ourlogfile = fopen(logfile, "a");
+ }
+#endif
+
+ static int64_t shutdown_start = 0;
+ if( shutdown_start == 0 ) {
+ shutdown_start = runtime_get_wall_time();
+ }
+
while ((lm = STAILQ_FIRST(&logmsg_queue))) {
+ int64_t log_delta = lm->when - shutdown_start;
+ if( !pid1_magic && ourlogfile ) {
+ fprintf(ourlogfile, "%8lld%6u %-40s%6u %-40s %s\n", log_delta,
+ lm->from_pid, lm->from_name, lm->about_pid, lm->about_name, lm->msg);
+ }
+
lm->from_name_offset = lm->from_name - (char *)lm;
lm->about_name_offset = lm->about_name - (char *)lm;
lm->msg_offset = lm->msg - (char *)lm;
@@ -1361,6 +1390,10 @@
logmsg_remove(lm);
}
+
+ if( ourlogfile ) {
+ fflush(ourlogfile);
+ }
return 0;
}
@@ -1447,6 +1480,8 @@
logmsg_remove(lm);
}
+
+ fflush(ourlogfile);
}
kern_return_t
@@ -1533,6 +1568,11 @@
{
if (!pid1_magic) {
#if !TARGET_OS_EMBEDDED
+ if( _vproc_transaction_count() == 0 ) {
+ runtime_syslog(LOG_NOTICE, "Exiting cleanly.");
+ }
+
+ runtime_closelog();
_vproc_transaction_end();
#endif
}
Modified: trunk/launchd/src/launchd_runtime.h
===================================================================
--- trunk/launchd/src/launchd_runtime.h 2009-02-02 21:35:16 UTC (rev 23793)
+++ trunk/launchd/src/launchd_runtime.h 2009-02-11 01:24:50 UTC (rev 23794)
@@ -103,6 +103,7 @@
extern char g_username[128];
extern bool g_shutdown_debugging;
extern bool g_use_gmalloc;
+extern pid_t g_wsp;
mach_port_t runtime_get_kernel_port(void);
extern boolean_t launchd_internal_demux(mach_msg_header_t *Request, mach_msg_header_t *Reply);
@@ -134,6 +135,7 @@
int kevent_bulk_mod(struct kevent *kev, size_t kev_cnt);
int kevent_mod(uintptr_t ident, short filter, u_short flags, u_int fflags, intptr_t data, void *udata);
+void log_kevent_struct(int level, struct kevent *kev_base, int indx);
pid_t runtime_fork(mach_port_t bsport);
Modified: trunk/launchd/src/launchd_unix_ipc.c
===================================================================
--- trunk/launchd/src/launchd_unix_ipc.c 2009-02-02 21:35:16 UTC (rev 23793)
+++ trunk/launchd/src/launchd_unix_ipc.c 2009-02-11 01:24:50 UTC (rev 23794)
@@ -393,7 +393,7 @@
resp = launch_data_new_errno(errno);
} else if (!strcmp(cmd, LAUNCH_KEY_REMOVEJOB)) {
if ((j = job_find(launch_data_get_string(data))) != NULL) {
- job_remove(j, false);
+ job_remove(j);
errno = 0;
}
resp = launch_data_new_errno(errno);
Modified: trunk/launchd/src/launchproxy.c
===================================================================
--- trunk/launchd/src/launchproxy.c 2009-02-02 21:35:16 UTC (rev 23793)
+++ trunk/launchd/src/launchproxy.c 2009-02-11 01:24:50 UTC (rev 23794)
@@ -156,7 +156,7 @@
if ((r = accept((int)kev.ident, (struct sockaddr *)&ss, &slen)) == -1) {
if (errno == EWOULDBLOCK)
continue;
- syslog(LOG_DEBUG, "accept(): %m");
+ syslog(LOG_WARNING, "accept(): %m");
goto out;
} else {
if (ss.ss_family == AF_INET || ss.ss_family == AF_INET6) {
@@ -181,6 +181,9 @@
switch (fork()) {
case -1:
syslog(LOG_WARNING, "fork(): %m");
+ if( errno != ENOMEM ) {
+ continue;
+ }
goto out;
case 0:
break;
Modified: trunk/launchd/src/libvproc.c
===================================================================
--- trunk/launchd/src/libvproc.c 2009-02-02 21:35:16 UTC (rev 23793)
+++ trunk/launchd/src/libvproc.c 2009-02-11 01:24:50 UTC (rev 23794)
@@ -467,15 +467,7 @@
return (vproc_err_t)_vprocmgr_move_subset_to_user;
}
- /* Don't do things like setting up the exception port if we're tainted. This is primarily for
- * loginwindow, which setuid(2)s to the logged-in user but still runs under the system Mach
- * bootstrap. So we want the system launchd to spawn a crash reporter to report loginwindow
- * crashes, not the per-user launchd, since that session will be wiped out of there is a loginwindow
- * crash.
- *
- * See rdar://problem/5947376 for more details.
- */
- return !issetugid() ? _vproc_post_fork_ping() : NULL;
+ return _vproc_post_fork_ping();
}
vproc_err_t
@@ -831,6 +823,21 @@
/* Once you're in the transaction model, you're in for good. Like the Mafia. */
s_cached_transactions_enabled = 1;
break;
+ case VPROC_GSK_PERUSER_SUSPEND:
+ {
+ char peruser_label[NAME_MAX];
+ snprintf(peruser_label, NAME_MAX - 1, "com.apple.launchd.peruser.%u", (uid_t)*inval);
+
+ vproc_t pu_vp = vprocmgr_lookup_vproc(peruser_label);
+ if( pu_vp ) {
+ int status = 0;
+ kern_return_t kr = vproc_mig_wait2(bootstrap_port, pu_vp->j_port, &status);
+ vproc_release(pu_vp);
+
+ syslog(LOG_DEBUG, "%u's suspended launchd exited with status %i (kr = 0x%x).", (uid_t)*inval, status, kr);
+ }
+ }
+ break;
default:
break;
}
Modified: trunk/launchd/src/protocol_job_reply.defs
===================================================================
--- trunk/launchd/src/protocol_job_reply.defs 2009-02-02 21:35:16 UTC (rev 23793)
+++ trunk/launchd/src/protocol_job_reply.defs 2009-02-11 01:24:50 UTC (rev 23794)
@@ -78,3 +78,26 @@
__r_port : mach_port_move_send_once_t;
__result : kern_return_t, RetCode;
__outval : pointer_t);
+
+skip; /* log_forward */
+
+skip; /* kickstart */
+
+skip; /* embedded_wait */
+
+skip; /* lookup_children */
+
+skip; /* switch_to_session */
+
+skip; /* transaction_count_for_pid */
+
+skip; /* pid_is_managed */
+
+skip; /* port_for_label */
+
+skip; /* init_session */
+
+simpleroutine job_mig_wait2_reply(
+ __r_port : mach_port_move_send_once_t;
+ __result : kern_return_t, RetCode;
+ __waitval : integer_t);
Modified: trunk/launchd/src/protocol_vproc.defs
===================================================================
--- trunk/launchd/src/protocol_vproc.defs 2009-02-02 21:35:16 UTC (rev 23793)
+++ trunk/launchd/src/protocol_vproc.defs 2009-02-11 01:24:50 UTC (rev 23794)
@@ -209,3 +209,9 @@
routine init_session(
__bs_port : job_t;
__session_name : name_t);
+
+routine wait2(
+ __bs_port : job_t;
+ __target_port : job_t;
+sreplyport __rport : mach_port_make_send_once_t;
+out __waitval : integer_t);
Modified: trunk/launchd/src/vproc_priv.h
===================================================================
--- trunk/launchd/src/vproc_priv.h 2009-02-02 21:35:16 UTC (rev 23793)
+++ trunk/launchd/src/vproc_priv.h 2009-02-11 01:24:50 UTC (rev 23794)
@@ -62,6 +62,8 @@
VPROC_GSK_WEIRD_BOOTSTRAP,
VPROC_GSK_WAITFORDEBUGGER,
VPROC_GSK_SHUTDOWN_DEBUGGING,
+ VPROC_GSK_PERUSER_SUSPEND,
+ VPROC_GSK_PERUSER_RESUME,
} vproc_gsk_t;
typedef unsigned int vproc_flags_t;
Modified: trunk/launchd.xcodeproj/project.pbxproj
===================================================================
--- trunk/launchd.xcodeproj/project.pbxproj 2009-02-02 21:35:16 UTC (rev 23793)
+++ trunk/launchd.xcodeproj/project.pbxproj 2009-02-11 01:24:50 UTC (rev 23794)
@@ -51,9 +51,11 @@
/* Begin PBXBuildFile section */
4B9EDCA20EAFC77E00A78496 /* DiskArbitration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4B9EDCA10EAFC77E00A78496 /* DiskArbitration.framework */; };
+ 7215DE4C0EFAF2EC00ABD81E /* libauditd.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 7215DE4B0EFAF2EC00ABD81E /* libauditd.dylib */; };
721FBEBC0EA7AE2F0057462B /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 721FBEBB0EA7AE2F0057462B /* Security.framework */; };
726055EC0EA7EC2400D65FE7 /* mach_exc.defs in Sources */ = {isa = PBXBuildFile; fileRef = FC36291F0E9349410054F1A3 /* mach_exc.defs */; settings = {ATTRIBUTES = (Server, ); }; };
726056090EA7FCF200D65FE7 /* launchd_ktrace.c in Sources */ = {isa = PBXBuildFile; fileRef = 72FDB15D0EA7D7B200B2AC84 /* launchd_ktrace.c */; };
+ 72AFE8090EFAF3D9004BDA46 /* libauditd.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 7215DE4B0EFAF2EC00ABD81E /* libauditd.dylib */; };
72FDB15F0EA7D7B200B2AC84 /* launchd_ktrace.c in Sources */ = {isa = PBXBuildFile; fileRef = 72FDB15D0EA7D7B200B2AC84 /* launchd_ktrace.c */; };
72FDB1C00EA7E21C00B2AC84 /* protocol_job_forward.defs in Sources */ = {isa = PBXBuildFile; fileRef = 72FDB1BF0EA7E21C00B2AC84 /* protocol_job_forward.defs */; };
FC3627BA0E9343220054F1A3 /* StartupItems.c in Sources */ = {isa = PBXBuildFile; fileRef = FC59A0FD0E8C8ADF00D41150 /* StartupItems.c */; };
@@ -288,6 +290,7 @@
/* Begin PBXFileReference section */
4B9EDCA10EAFC77E00A78496 /* DiskArbitration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = DiskArbitration.framework; path = /System/Library/Frameworks/DiskArbitration.framework; sourceTree = "<absolute>"; };
+ 7215DE4B0EFAF2EC00ABD81E /* libauditd.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libauditd.dylib; path = /usr/lib/libauditd.dylib; sourceTree = "<absolute>"; };
721FBEA50EA7ABC40057462B /* config.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = config.h; path = launchd/src/config.h; sourceTree = "<group>"; };
721FBEBB0EA7AE2F0057462B /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = /System/Library/Frameworks/Security.framework; sourceTree = "<absolute>"; };
72FDB15D0EA7D7B200B2AC84 /* launchd_ktrace.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = launchd_ktrace.c; path = launchd/src/launchd_ktrace.c; sourceTree = "<group>"; };
@@ -363,6 +366,7 @@
buildActionMask = 2147483647;
files = (
FC36292D0E934AA40054F1A3 /* libbsm.dylib in Frameworks */,
+ 7215DE4C0EFAF2EC00ABD81E /* libauditd.dylib in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -380,6 +384,7 @@
FCC841CC0EA7138700C01666 /* IOKit.framework in Frameworks */,
FC3628080E9345E10054F1A3 /* CoreFoundation.framework in Frameworks */,
FCD713740E95DE49001B0111 /* libedit.dylib in Frameworks */,
+ 72AFE8090EFAF3D9004BDA46 /* libauditd.dylib in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -471,6 +476,7 @@
4B9EDCA10EAFC77E00A78496 /* DiskArbitration.framework */,
721FBEBB0EA7AE2F0057462B /* Security.framework */,
FC36292C0E934AA40054F1A3 /* libbsm.dylib */,
+ 7215DE4B0EFAF2EC00ABD81E /* libauditd.dylib */,
FCD713730E95DE49001B0111 /* libedit.dylib */,
FC3628070E9345E10054F1A3 /* CoreFoundation.framework */,
FC36283E0E93463C0054F1A3 /* IOKit.framework */,
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/launchd-changes/attachments/20090210/482ca41b/attachment-0001.html>
More information about the launchd-changes
mailing list