[launchd-changes] [23738] trunk/launchd/src
source_changes at macosforge.org
source_changes at macosforge.org
Thu Oct 16 17:18:38 PDT 2008
Revision: 23738
http://trac.macosforge.org/projects/launchd/changeset/23738
Author: dsorresso at apple.com
Date: 2008-10-16 17:18:37 -0700 (Thu, 16 Oct 2008)
Log Message:
-----------
<rdar://problem/5054857> launchd has too long a time out when shutting down the system
<rdar://problem/5293374> launchctl limit maxfiles unlimited unlimited does not work properly
<rdar://problem/5496612> unsupported keys in launchd items
<rdar://problem/5811660> launchd fork/posix_spawn log message is unreasonable
<rdar://problem/5947376> 10A79: launchd is preventing me from capturing a crash report for loginwindow
<rdar://problem/5952607> launchd is deaf to MIG requests while running 'sample'
<rdar://problem/6112276> launchd should stop trying to mount fdesc
<rdar://problem/6153922> Support allowing Launchd to launch by label
<rdar://problem/6156146> vproc_transaction_{begin,end} should be declared AVAILABLE_MAC_OS_X_VERSION_10_6_AND_LATER
<rdar://problem/6159782> Thousands of messages about "Background: Aqua: Inconsistency: ..."
<rdar://problem/6242414> launchd: "System: Bug: launchd_core_logic.c:5263" on 10A174
<rdar://problem/6243586> Shutdown Tracer timeout threshold should be changed to 1s temporarily
<rdar://problem/6252640> Launchd being noisy about seatbelt Mach port caching
<rdar://problem/6259490> bootstrap_look_up2(..., BOOTSTRAP_PRIVILEGED_SERVER) fails the second time around
<rdar://problem/6263268> launchd WatchPaths option starts process when watched directory isn't there
<rdar://problem/6268517> Remove/deprecate vproc_standby_* APIs in libvproc
<rdar://problem/6280155> SnowLeopard: support rc.boot executable in embedded
Modified Paths:
--------------
trunk/launchd/src/launchctl.c
trunk/launchd/src/launchd.c
trunk/launchd/src/launchd.h
trunk/launchd/src/launchd_core_logic.c
trunk/launchd/src/launchd_runtime.c
trunk/launchd/src/launchd_runtime.h
trunk/launchd/src/libbootstrap.c
trunk/launchd/src/liblaunch.c
trunk/launchd/src/libvproc.c
trunk/launchd/src/libvproc_private.h
trunk/launchd/src/libvproc_public.h
trunk/launchd/src/protocol_job.defs
Modified: trunk/launchd/src/launchctl.c
===================================================================
--- trunk/launchd/src/launchctl.c 2008-10-17 00:16:24 UTC (rev 23737)
+++ trunk/launchd/src/launchctl.c 2008-10-17 00:18:37 UTC (rev 23738)
@@ -43,6 +43,10 @@
#include <sys/sysctl.h>
#include <sys/stat.h>
#include <sys/socket.h>
+#ifndef SO_EXECPATH
+/* This is just so it's easy for me to compile launchctl without buildit. */
+ #define SO_EXECPATH 0x1085
+#endif
#include <sys/un.h>
#include <sys/fcntl.h>
#include <sys/event.h>
@@ -74,6 +78,7 @@
#include <sysexits.h>
#include <util.h>
#include <spawn.h>
+#include <sys/syslimits.h>
extern char **environ;
@@ -1449,6 +1454,13 @@
assumes(fwexec(rcserver_tool, NULL) != -1);
}
+#if TARGET_OS_EMBEDDED
+ if (path_check("/etc/rc.boot")) {
+ const char *rcboot_tool[] = { "/etc/rc.boot", NULL };
+ assumes(fwexec(rcboot_tool, true) != -1);
+ }
+#endif
+
read_launchd_conf();
if (path_check("/var/account/acct")) {
@@ -2269,7 +2281,7 @@
}
int
-limit_cmd(int argc __attribute__((unused)), char *const argv[])
+limit_cmd(int argc, char *const argv[])
{
char slimstr[100];
char hlimstr[100];
@@ -2340,6 +2352,20 @@
lmts[which].rlim_cur = slim;
lmts[which].rlim_max = hlim;
+ bool maxfiles_exceeded = false;
+ if( argc > 2 ) {
+ maxfiles_exceeded = ( strncmp(argv[2], "unlimited", sizeof("unlimited")) == 0 );
+ }
+
+ if( argc > 3 ) {
+ maxfiles_exceeded = ( maxfiles_exceeded || strncmp(argv[3], "unlimited", sizeof("unlimited")) == 0 );
+ }
+
+ if( maxfiles_exceeded ) {
+ fprintf(stderr, "Neither the hard nor soft limit for \"maxfiles\" can be unlimited. Please use a numeric parameter for both.\n");
+ return 1;
+ }
+
msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
tmp = launch_data_new_opaque(lmts, lsz);
launch_data_dict_insert(msg, tmp, LAUNCH_KEY_SETRESOURCELIMITS);
@@ -2784,7 +2810,7 @@
goto out;
}
#endif
-
+ fprintf(stderr, "Running fsck on the boot volume...\n");
if (fwexec(fsck_tool, NULL) != -1) {
goto out;
}
Modified: trunk/launchd/src/launchd.c
===================================================================
--- trunk/launchd/src/launchd.c 2008-10-17 00:16:24 UTC (rev 23737)
+++ trunk/launchd/src/launchd.c 2008-10-17 00:18:37 UTC (rev 23738)
@@ -107,6 +107,7 @@
bool fake_shutdown_in_progress;
bool network_up;
char g_username[128] = "__UnknownUserToLaunchd_DontPanic_NotImportant__";
+FILE *g_console = NULL;
int
main(int argc, char *const *argv)
@@ -149,6 +150,20 @@
launchd_runtime_init();
+ if( pid1_magic ) {
+ if( low_level_debug ) {
+ g_console = stdout;
+ } else {
+ if( !launchd_assumes((g_console = fopen(_PATH_CONSOLE, "w")) != NULL) ) {
+ g_console = stdout;
+ } else {
+ _fd(fileno(g_console));
+ }
+
+ _fd(fileno(g_console));
+ }
+ }
+
if (NULL == getenv("PATH")) {
setenv("PATH", _PATH_STDPATH, 1);
}
@@ -170,6 +185,10 @@
runtime_syslog(LOG_NOTICE, "Per-user launchd for UID %u (%s) began at: %lld.%06llu", getuid(), g_username, now / USEC_PER_SEC, now % USEC_PER_SEC);
}
+ if( pid1_magic ) {
+ runtime_syslog(LOG_NOTICE | LOG_CONSOLE, "*** launchd[1] has started up. ***");
+ }
+
monitor_networking_state();
if (pid1_magic) {
@@ -274,7 +293,6 @@
launchd_assumes(setsid() != -1);
launchd_assumes(chdir("/") != -1);
launchd_assumes(setlogin("root") != -1);
- launchd_assumes(mount("fdesc", "/dev", MNT_UNION, NULL) != -1);
}
Modified: trunk/launchd/src/launchd.h
===================================================================
--- trunk/launchd/src/launchd.h 2008-10-17 00:16:24 UTC (rev 23737)
+++ trunk/launchd/src/launchd.h 2008-10-17 00:18:37 UTC (rev 23738)
@@ -33,6 +33,7 @@
extern bool fake_shutdown_in_progress;
extern bool network_up;
extern bool g_force_old_kill_path;
+extern FILE *g_console;
INTERNAL_ABI bool init_check_pid(pid_t);
Modified: trunk/launchd/src/launchd_core_logic.c
===================================================================
--- trunk/launchd/src/launchd_core_logic.c 2008-10-17 00:16:24 UTC (rev 23737)
+++ trunk/launchd/src/launchd_core_logic.c 2008-10-17 00:18:37 UTC (rev 23738)
@@ -108,7 +108,7 @@
* it a SIGTERM, SIGKILL it. Can be overriden in the job plist.
*/
#define LAUNCHD_MIN_JOB_RUN_TIME 10
-#define LAUNCHD_SAMPLE_TIMEOUT 2
+#define LAUNCHD_SAMPLE_TIMEOUT 1
#define LAUNCHD_DEFAULT_EXIT_TIMEOUT 20
#define LAUNCHD_SIGKILL_TIMER 5
@@ -259,6 +259,7 @@
struct semaphoreitem {
SLIST_ENTRY(semaphoreitem) sle;
semaphore_reason_t why;
+ bool watching_parent;
int fd;
union {
const char what[0];
@@ -293,6 +294,7 @@
LIST_HEAD(, job_s) active_jobs[ACTIVE_JOB_HASH_SIZE];
LIST_HEAD(, machservice) ms_hash[MACHSERVICE_HASH_SIZE];
LIST_HEAD(, job_s) global_env_jobs;
+ LIST_HEAD(, job_s) pending_samples;
mach_port_t jm_port;
mach_port_t req_port;
jobmgr_t parentmgr;
@@ -300,7 +302,15 @@
unsigned int global_on_demand_cnt;
unsigned int hopefully_first_cnt;
unsigned int normal_active_cnt;
- unsigned int sent_stop_to_normal_jobs:1, sent_stop_to_hopefully_last_jobs:1, shutting_down:1, session_initialized:1, __junk:28;
+ unsigned int sent_stop_to_normal_jobs :1,
+ sent_stop_to_hopefully_last_jobs:1,
+ shutting_down :1,
+ session_initialized :1,
+ killed_hopefully_first_jobs :1,
+ killed_normal_jobs :1,
+ killed_hopefully_last_jobs :1,
+ __junk :25;
+ char sample_log_file[PATH_MAX];
union {
const char name[0];
char name_init[0];
@@ -318,8 +328,9 @@
static void jobmgr_reap_bulk(jobmgr_t jm, struct kevent *kev);
static void jobmgr_log_stray_children(jobmgr_t jm);
static void jobmgr_kill_stray_child(jobmgr_t jm, pid_t p);
-static void jobmgr_remove(jobmgr_t jm);
+static void jobmgr_remove(jobmgr_t jm, bool skip_formalities);
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);
static job_t jobmgr_find_by_pid(jobmgr_t jm, pid_t p, bool create_anon);
static job_t job_mig_intran2(jobmgr_t jm, mach_port_t mport, pid_t upid);
@@ -341,6 +352,7 @@
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) pending_samples_sle;
SLIST_HEAD(, socketgroup) sockets;
SLIST_HEAD(, calendarinterval) cal_intervals;
SLIST_HEAD(, envitem) global_env;
@@ -401,47 +413,50 @@
J_TYPE_INETD,
} j_type;
#endif
- bool debug:1, /* man launchd.plist --> Debug */
- ondemand:1, /* man launchd.plist --> KeepAlive == false */
- session_create:1, /* man launchd.plist --> SessionCreate */
- low_pri_io:1, /* man launchd.plist --> LowPriorityIO */
- no_init_groups:1, /* man launchd.plist --> InitGroups */
- priv_port_has_senders:1, /* a legacy mach_init concept to make bootstrap_create_server/service() work */
- importing_global_env:1, /* a hack during job importing */
- importing_hard_limits:1, /* a hack during job importing */
- setmask:1, /* man launchd.plist --> Umask */
- anonymous:1, /* a process that launchd knows about, but isn't managed by launchd */
- checkedin:1, /* a legacy mach_init concept to detect sick jobs */
- legacy_mach_job:1, /* a job created via bootstrap_create_server() */
- legacy_LS_job:1, /* a job created via spawn_via_launchd() */
- inetcompat:1, /* a legacy job that wants inetd compatible semantics */
- inetcompat_wait:1, /* a twist on inetd compatibility */
- start_pending:1, /* an event fired and the job should start, but not necessarily right away */
- globargv:1, /* man launchd.plist --> EnableGlobbing */
- wait4debugger:1, /* man launchd.plist --> WaitForDebugger */
- internal_exc_handler:1, /* MachExceptionHandler == true */
- stall_before_exec:1, /* a hack to support an option of spawn_via_launchd() */
- only_once:1, /* man launchd.plist --> LaunchOnlyOnce. Note: 5465184 Rename this to "HopefullyNeverExits" */
- currently_ignored:1, /* Make job_ignore() / job_watch() work. If these calls were balanced, then this wouldn't be necessarily. */
- forced_peers_to_demand_mode:1, /* A job that forced all other jobs to be temporarily launch-on-demand */
- setnice:1, /* man launchd.plist --> Nice */
- hopefully_exits_last:1, /* man launchd.plist --> HopefullyExitsLast */
- removal_pending:1, /* a job was asked to be unloaded/removed while running, we'll remove it after it exits */
- sent_sigkill:1, /* job_kill() was called */
- sampled:1, /* job_force_sampletool() was called (or is disabled) */
- debug_before_kill:1, /* enter the kernel debugger before killing a job */
- weird_bootstrap:1, /* a hack that launchd+launchctl use during jobmgr_t creation */
- start_on_mount:1, /* man launchd.plist --> StartOnMount */
- per_user:1, /* This job is a per-user launchd managed by the PID 1 launchd */
- hopefully_exits_first:1, /* man launchd.plist --> HopefullyExitsFirst */
- deny_unknown_mslookups:1, /* A flag for changing the behavior of bootstrap_look_up() */
- unload_at_mig_return:1, /* A job thoroughly confused launchd. We need to unload it ASAP */
- abandon_pg:1, /* man launchd.plist --> AbandonProcessGroup */
- poll_for_vfs_changes:1, /* a hack to work around the fact that kqueues don't work on all filesystems */
- 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 */
+ bool debug :1, /* man launchd.plist --> Debug */
+ ondemand :1, /* man launchd.plist --> KeepAlive == false */
+ session_create :1, /* man launchd.plist --> SessionCreate */
+ low_pri_io :1, /* man launchd.plist --> LowPriorityIO */
+ no_init_groups :1, /* man launchd.plist --> InitGroups */
+ priv_port_has_senders :1, /* a legacy mach_init concept to make bootstrap_create_server/service() work */
+ importing_global_env :1, /* a hack during job importing */
+ importing_hard_limits :1, /* a hack during job importing */
+ setmask :1, /* man launchd.plist --> Umask */
+ anonymous :1, /* a process that launchd knows about, but isn't managed by launchd */
+ checkedin :1, /* a legacy mach_init concept to detect sick jobs */
+ legacy_mach_job :1, /* a job created via bootstrap_create_server() */
+ legacy_LS_job :1, /* a job created via spawn_via_launchd() */
+ inetcompat :1, /* a legacy job that wants inetd compatible semantics */
+ inetcompat_wait :1, /* a twist on inetd compatibility */
+ start_pending :1, /* an event fired and the job should start, but not necessarily right away */
+ globargv :1, /* man launchd.plist --> EnableGlobbing */
+ wait4debugger :1, /* man launchd.plist --> WaitForDebugger */
+ internal_exc_handler :1, /* MachExceptionHandler == true */
+ stall_before_exec :1, /* a hack to support an option of spawn_via_launchd() */
+ only_once :1, /* man launchd.plist --> LaunchOnlyOnce. Note: 5465184 Rename this to "HopefullyNeverExits" */
+ currently_ignored :1, /* Make job_ignore() / job_watch() work. If these calls were balanced, then this wouldn't be necessarily. */
+ forced_peers_to_demand_mode :1, /* A job that forced all other jobs to be temporarily launch-on-demand */
+ setnice :1, /* man launchd.plist --> Nice */
+ hopefully_exits_last :1, /* man launchd.plist --> HopefullyExitsLast */
+ removal_pending :1, /* a job was asked to be unloaded/removed while running, we'll remove it after it exits */
+ sent_sigkill :1, /* job_kill() was called */
+ sampled :1, /* job_force_sampletool() was called (or is disabled) */
+ debug_before_kill :1, /* enter the kernel debugger before killing a job */
+ weird_bootstrap :1, /* a hack that launchd+launchctl use during jobmgr_t creation */
+ start_on_mount :1, /* man launchd.plist --> StartOnMount */
+ per_user :1, /* This job is a per-user launchd managed by the PID 1 launchd */
+ hopefully_exits_first :1, /* man launchd.plist --> HopefullyExitsFirst */
+ deny_unknown_mslookups :1, /* A flag for changing the behavior of bootstrap_look_up() */
+ unload_at_mig_return :1, /* A job thoroughly confused launchd. We need to unload it ASAP */
+ abandon_pg :1, /* man launchd.plist --> AbandonProcessGroup */
+ poll_for_vfs_changes :1, /* a hack to work around the fact that kqueues don't work on all filesystems */
+ 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 */
+ pending_sample :1, /* This job needs to be sampled for some reason. */
+ kill_after_sample :1; /* The job is to be killed after sampling. */
mode_t mask;
+ pid_t sample_pid;
const char label[0];
};
@@ -475,10 +490,14 @@
static bool job_setup_machport(job_t j);
static void job_setup_fd(job_t j, int target_fd, const char *path, int flags);
static void job_postfork_become_user(job_t j);
+#if !TARGET_OS_EMBEDDED
static void job_enable_audit_for_user(job_t j, uid_t u, char *name);
+#endif
static void job_postfork_test_user(job_t j);
static void job_log_pids_with_weird_uids(job_t j);
+#if 0
static void job_force_sampletool(job_t j);
+#endif
static void job_setup_exception_port(job_t j, task_t target_task);
static void job_reparent_hack(job_t j, const char *where);
INTERNAL_ABI static void job_callback(void *obj, struct kevent *kev);
@@ -497,11 +516,9 @@
static void job_log_error(job_t j, int pri, const char *msg, ...) __attribute__((format(printf, 3, 4)));
static void job_log_bug(job_t j, unsigned int line);
static void job_log_stdouterr2(job_t j, const char *msg, ...);
-static void job_set_exeception_port(job_t j, mach_port_t port);
+static void job_set_exception_port(job_t j, mach_port_t port);
static kern_return_t job_handle_mpm_wait(job_t j, mach_port_t srp, int *waitstatus);
-
-
static const struct {
const char *key;
int val;
@@ -819,18 +836,87 @@
LIST_FOREACH(ji, &jm->jobs, sle) {
why_active = job_active(ji);
- job_log(ji, LOG_DEBUG, "%s", why_active ? why_active : "Inactive");
+ job_log(ji, LOG_DEBUG | LOG_CONSOLE, "%s", why_active ? why_active : "Inactive");
}
}
INTERNAL_ABI static void
-still_alive_with_check(void)
+jobmgr_still_alive_with_check(jobmgr_t jm)
{
- jobmgr_log(root_jobmgr, LOG_NOTICE, "Still alive with %lu/%lu children", total_children, total_anon_children);
+ jobmgr_log(jm, LOG_NOTICE | LOG_CONSOLE, "Still alive with %lu/%lu (normal/anonymous) children", total_children, total_anon_children);
+ jobmgr_log_active_jobs(jm);
+
+ if( pid1_magic && jm == root_jobmgr ) {
+ /* Take the opportunity to shut the system down if it presents itself. */
+ if( total_children == 0 ) {
+ jobmgr_log(jm, LOG_DEBUG | LOG_CONSOLE, "No more non-anonymous children left. Shutting down the system.");
+ jobmgr_log_stray_children(jm);
+ jobmgr_remove(jm, true);
+ }
+
+ unsigned int unkilled_jobs = 0;
+ job_t ji = NULL;
+ char *type = NULL;
+ if( !jm->killed_hopefully_first_jobs ) {
+ LIST_FOREACH( ji, &jm->jobs, sle ) {
+ if( ji->hopefully_exits_first && !ji->sent_sigkill && !ji->anonymous ) {
+ job_log(ji, LOG_DEBUG | LOG_CONSOLE, "Hopefully First job has not yet been sent SIGKILL.");
+ unkilled_jobs++;
+ }
+ }
- jobmgr_log_active_jobs(root_jobmgr);
+ type = "hopefully first";
+ if( unkilled_jobs == 0 ) {
+ root_jobmgr->killed_hopefully_first_jobs = true;
+ }
+ } else if( !jm->killed_normal_jobs ) {
+ LIST_FOREACH( ji, &jm->jobs, sle ) {
+ if( !ji->hopefully_exits_first && !ji->hopefully_exits_last && !ji->sent_sigkill && !ji->anonymous ) {
+ job_log(ji, LOG_DEBUG | LOG_CONSOLE, "Normal job has not yet been sent SIGKILL.");
+ unkilled_jobs++;
+ }
+ }
+ type = "normal";
+ if( unkilled_jobs == 0 ) {
+ root_jobmgr->killed_normal_jobs = true;
+ }
+ } else if( !jm->killed_hopefully_last_jobs ) {
+ LIST_FOREACH( ji, &jm->jobs, sle ) {
+ if( !ji->hopefully_exits_last && !ji->sent_sigkill && !ji->anonymous ) {
+ job_log(ji, LOG_DEBUG | LOG_CONSOLE, "Hopefully Last job has not yet been sent SIGKILL.");
+ unkilled_jobs++;
+ }
+ }
+
+ type = "hopefully last";
+ if( unkilled_jobs == 0 ) {
+ jm->killed_hopefully_last_jobs = true;
+ }
+ }
+
+ /* Our club-over-the-head solution to rdar://problem/5054857.
+ * If all remaining jobs in the PID 1 launchd have been sent SIGKILL, wash our hands of them. This way,
+ * unkillable processes don't hold up shutdown for customers. Disable this behavior for AppleInternal,
+ * since we still want to catch kernel bugs and processes that don't properly terminate when receiving
+ * SIGKILL. See jobmgr_do_garbage_collection() for the rest.
+ */
+ if( unkilled_jobs == 0 && jm->killed_hopefully_last_jobs ) {
+ if( !do_apple_internal_logging ) {
+ jobmgr_assumes(jm, kevent_mod((uintptr_t)jm, EVFILT_TIMER, EV_DELETE, 0, 0, NULL) != -1);
+ jobmgr_log(root_jobmgr, LOG_NOTICE | LOG_CONSOLE, "All remaining non-anonymous jobs have died or been sent SIGKILL. Shutting down the system.");
+ jobmgr_log_stray_children(jm);
+ runtime_closelog(); /* hack to flush logs */
+ jobmgr_remove(jm, true);
+ } else {
+ jobmgr_log(root_jobmgr, LOG_NOTICE | LOG_CONSOLE, "All remaining non-anonymous jobs have died or been sent SIGKILL. On a customer-facing system, we would call reboot() at this point.");
+ }
+ } else if( unkilled_jobs != 0 ) {
+ jobmgr_log(jm, LOG_NOTICE | LOG_CONSOLE, "Still have %u %s non-anonymous job%s that %s not been sent SIGKILL.", unkilled_jobs, type, unkilled_jobs > 1 ? "s" : "", unkilled_jobs > 1 ? "have" : "has");
+ }
+ }
+
runtime_closelog(); /* hack to flush logs */
}
@@ -857,32 +943,32 @@
}
if (do_apple_internal_logging && jm->parentmgr == NULL && pid1_magic) {
- if( pid1_magic ) {
- jobmgr_assumes(jm, kevent_mod((uintptr_t)jm, EVFILT_TIMER, EV_ADD, NOTE_SECONDS, 5, jm));
- }
+ jobmgr_assumes(jm, kevent_mod((uintptr_t)jm, EVFILT_TIMER, EV_ADD, NOTE_SECONDS, 5, jm));
}
return jobmgr_do_garbage_collection(jm);
}
void
-jobmgr_remove(jobmgr_t jm)
+jobmgr_remove(jobmgr_t jm, bool skip_formalities)
{
jobmgr_t jmi;
job_t ji;
jobmgr_log(jm, LOG_DEBUG, "Removed job manager");
- if (!jobmgr_assumes(jm, SLIST_EMPTY(&jm->submgrs))) {
- while ((jmi = SLIST_FIRST(&jm->submgrs))) {
- jobmgr_remove(jmi);
+ if( !skip_formalities ) {
+ if (!jobmgr_assumes(jm, SLIST_EMPTY(&jm->submgrs))) {
+ while ((jmi = SLIST_FIRST(&jm->submgrs))) {
+ jobmgr_remove(jmi, false);
+ }
}
- }
- while ((ji = LIST_FIRST(&jm->jobs))) {
- /* We should only have anonymous jobs left */
- job_assumes(ji, ji->anonymous);
- job_remove(ji);
+ while ((ji = LIST_FIRST(&jm->jobs))) {
+ /* We should only have anonymous jobs left */
+ job_assumes(ji, ji->anonymous);
+ job_remove(ji);
+ }
}
if (jm->req_port) {
@@ -901,13 +987,13 @@
runtime_del_weak_ref();
SLIST_REMOVE(&jm->parentmgr->submgrs, jm, jobmgr_s, sle);
} else if (pid1_magic) {
- jobmgr_log(jm, LOG_DEBUG, "About to call: sync()");
+ jobmgr_log(jm, LOG_DEBUG | LOG_CONSOLE, "About to call: sync()");
sync(); /* We're are going to rely on log timestamps to benchmark this call */
- jobmgr_log(jm, LOG_DEBUG, "Unmounting all filesystems except / and /dev");
+ jobmgr_log(jm, LOG_DEBUG | LOG_CONSOLE, "Unmounting all filesystems except / and /dev");
do_unmounts();
launchd_log_vm_stats();
- jobmgr_log(jm, LOG_DEBUG, "About to call: reboot(%s)", reboot_flags_to_C_names(jm->reboot_flags));
runtime_closelog();
+ jobmgr_log(root_jobmgr, LOG_NOTICE | LOG_CONSOLE, "About to call: reboot(%s).", reboot_flags_to_C_names(jm->reboot_flags));
jobmgr_assumes(jm, reboot(jm->reboot_flags) != -1);
runtime_closelog();
} else {
@@ -1251,7 +1337,7 @@
}
if (unlikely(kp.kp_proc.p_flag & P_SUGID)) {
- jobmgr_log(jm, LOG_APPLEONLY, "Inconsistency: P_SUGID is set on PID %u: %s", anonpid, kp.kp_proc.p_comm);
+ jobmgr_log(jm, LOG_DEBUG, "Inconsistency: P_SUGID is set on PID %u: %s", anonpid, kp.kp_proc.p_comm);
}
kp_euid = kp.kp_eproc.e_ucred.cr_uid;
@@ -1262,7 +1348,7 @@
kp_svgid = kp.kp_eproc.e_pcred.p_svgid;
if (unlikely(kp_euid != kp_uid || kp_euid != kp_svuid || kp_uid != kp_svuid || kp_egid != kp_gid || kp_egid != kp_svgid || kp_gid != kp_svgid)) {
- jobmgr_log(jm, LOG_APPLEONLY, "Inconsistency: Mixed credentials (e/r/s UID %u/%u/%u GID %u/%u/%u) detected on PID %u: %s",
+ jobmgr_log(jm, LOG_DEBUG, "Inconsistency: Mixed credentials (e/r/s UID %u/%u/%u GID %u/%u/%u) detected on PID %u: %s",
kp_euid, kp_uid, kp_svuid, kp_egid, kp_gid, kp_svgid, anonpid, kp.kp_proc.p_comm);
}
@@ -1283,7 +1369,7 @@
}
if (jp && !jp->anonymous && unlikely(!(kp.kp_proc.p_flag & P_EXEC))) {
- job_log(jp, LOG_APPLEONLY, "Called *fork(). Please switch to posix_spawn*(), pthreads or launchd. Child PID %u",
+ job_log(jp, LOG_DEBUG, "Called *fork(). Please switch to posix_spawn*(), pthreads or launchd. Child PID %u",
kp.kp_proc.p_pid);
}
@@ -1700,7 +1786,10 @@
if (likely(where2put)) {
job_assumes(j, (*where2put = strdup(value)) != NULL);
} else {
- job_log(j, LOG_WARNING, "Unknown key: %s", key);
+ /* See rdar://problem/5496612. These two are okay. */
+ if( strncmp(key, "SHAuthorizationRight", sizeof("SHAuthorizationRight")) != 0 && strncmp(key, "ServiceDescription", sizeof("ServiceDescription")) != 0 ) {
+ job_log(j, LOG_WARNING, "Unknown key: %s", key);
+ }
}
}
@@ -2502,6 +2591,57 @@
}
}
+void
+jobmgr_dequeue_next_sample(jobmgr_t jm)
+{
+ if( LIST_EMPTY(&jm->pending_samples) ) {
+ jobmgr_log(jm, LOG_DEBUG | LOG_CONSOLE, "Sample queue is empty.");
+ return;
+ }
+
+ /* Dequeue the next in line. */
+ job_t j = LIST_FIRST(&jm->pending_samples);
+ if( j->sample_pid ) {
+ job_log(j, LOG_DEBUG | LOG_CONSOLE, "Sampling is in progress. Not dequeuing next job.");
+ return;
+ }
+
+ if (j->sampled || j->per_user) {
+ return;
+ }
+
+ if (!job_assumes(j, do_apple_internal_logging)) {
+ return;
+ }
+
+ if (!job_assumes(j, mkdir(SHUTDOWN_LOG_DIR, S_IRWXU) != -1 || errno == EEXIST)) {
+ return;
+ }
+
+ job_log(j, LOG_DEBUG | LOG_CONSOLE, "Going to sample job.");
+
+ char pidstr[32];
+ snprintf(pidstr, sizeof(pidstr), "%u", j->p);
+ snprintf(j->mgr->sample_log_file, sizeof(j->mgr->sample_log_file), SHUTDOWN_LOG_DIR "/%s-%u.sample.txt", j->label, j->p);
+ job_log(j, LOG_DEBUG | LOG_CONSOLE, "Going to write sample to %s.", j->mgr->sample_log_file);
+
+ 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 };
+ if (!job_assumes(j, (errno = posix_spawnp(&sp, sample_args[0], NULL, NULL, sample_args, environ)) == 0)) {
+ job_log(j, LOG_ERR | LOG_CONSOLE, "Sampling failed for job! Kill it! Kill it with fire!");
+ job_kill(j);
+ jobmgr_dequeue_next_sample(jm);
+ } else {
+ j->sample_pid = sp;
+ j->pending_sample = false;
+
+ /* 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);
+ }
+ }
+}
+
INTERNAL_ABI job_t
job_dispatch(job_t j, bool kickstart)
{
@@ -2518,8 +2658,10 @@
job_remove(j);
return NULL;
} else if (kickstart || job_keepalive(j)) {
+ job_log(j, LOG_DEBUG, "Starting job (kickstart = %s)", kickstart ? "true" : "false");
job_start(j);
} else {
+ job_log(j, LOG_DEBUG, "Watching job (kickstart = %s)", kickstart ? "true" : "false");
job_watch(j);
/*
@@ -2533,7 +2675,7 @@
}
}
} else {
- job_log(j, LOG_DEBUG, "Tried to dispatch an already active job (%s), kickstart = %s.", job_active(j), kickstart ? "YES" : "NO");
+ job_log(j, LOG_DEBUG, "Tried to dispatch an already active job (%s), kickstart = %s.", job_active(j), kickstart ? "true" : "false");
}
return j;
@@ -2546,7 +2688,7 @@
va_list ap;
va_start(ap, msg);
- runtime_vsyslog(&attr, msg, ap);
+ runtime_vsyslog(&attr, false, msg, ap);
va_end(ap);
}
@@ -2642,7 +2784,7 @@
continue;
}
- job_log(j, LOG_APPLEONLY, "Called *fork(). Please switch to posix_spawn*(), pthreads or launchd. Child PID %u",
+ job_log(j, LOG_DEBUG, "Called *fork(). Please switch to posix_spawn*(), pthreads or launchd. Child PID %u",
kp[i].kp_proc.p_pid);
}
@@ -2650,11 +2792,99 @@
free(kp);
}
+static void
+job_reap_sample(job_t j)
+{
+ char *contents = NULL;
+ int wstatus = 0;
+ int logfile_fd = -1;
+
+ LIST_REMOVE(j, pending_samples_sle);
+
+ if (!job_assumes(j, waitpid(j->sample_pid, &wstatus, 0) != -1)) {
+ goto out;
+ }
+
+ j->sampled = true;
+
+ /*
+ * This won't work if the VFS or filesystems are sick:
+ * sync();
+ */
+
+ if (!job_assumes(j, WIFEXITED(wstatus) && WEXITSTATUS(wstatus) == 0)) {
+ goto out;
+ }
+
+ /* Echo our sample file contents to the console. */
+ if (!job_assumes(j, (logfile_fd = open(root_jobmgr->sample_log_file, O_RDONLY|O_NOCTTY)) != -1)) {
+ job_log(j, LOG_NOTICE | LOG_CONSOLE, "No sample present at %s.", j->mgr->sample_log_file);
+ goto out;
+ }
+
+ struct stat sb;
+ if (!job_assumes(j, fstat(logfile_fd, &sb) != -1)) {
+ goto out;
+ }
+
+ if (sizeof(size_t) == 4 && !job_assumes(j, !(sb.st_size & 0xffffffff00000000llu))) {
+ goto out;
+ }
+
+ size_t contents_sz = (size_t)sb.st_size;
+ contents = (char *)malloc(contents_sz);
+
+ if (!job_assumes(j, contents != NULL)) {
+ goto out;
+ }
+
+ if (!job_assumes(j, read(logfile_fd, contents, contents_sz) == (ssize_t)contents_sz)) {
+ goto out;
+ }
+
+ /* Special case here. We don't want any formatting, just the sample contents. */
+ job_assumes(j, write(fileno(g_console), contents, contents_sz) == (ssize_t)contents_sz);
+
+out:
+ if (contents) {
+ free(contents);
+ }
+
+ if (logfile_fd != -1) {
+ job_assumes(j, runtime_fsync(logfile_fd) != -1);
+ job_assumes(j, runtime_close(logfile_fd) != -1);
+ }
+
+ if( j->kill_after_sample ) {
+ if (unlikely(j->debug_before_kill)) {
+ job_log(j, LOG_NOTICE, "Exit timeout elapsed. Entering the kernel debugger");
+ job_assumes(j, host_reboot(mach_host_self(), HOST_REBOOT_DEBUGGER) == KERN_SUCCESS);
+ }
+
+ job_log(j, LOG_NOTICE, "Killing...");
+ job_kill(j);
+ }
+
+ j->sample_pid = 0;
+ job_log(j, LOG_DEBUG | LOG_CONSOLE, "Finished sampling.");
+
+ jobmgr_dequeue_next_sample(j->mgr);
+}
+
void
job_callback_proc(job_t j, int fflags)
{
bool program_changed = false;
-
+
+ if( j->sample_pid ) {
+ job_assumes(j, (fflags & NOTE_EXIT) != 0);
+ job_log(j, LOG_NOTICE | LOG_CONSOLE, "Sampling for job done. Reaping sample...");
+
+ job_reap_sample(j);
+
+ return;
+ }
+
if (fflags & NOTE_EXEC) {
program_changed = true;
@@ -2732,25 +2962,33 @@
td /= NSEC_PER_SEC;
td -= j->exit_timeout;
- job_log(j, LOG_WARNING, "Did not die after sending SIGKILL %llu seconds ago...", td);
+ job_log(j, LOG_WARNING | LOG_CONSOLE, "Did not die after sending SIGKILL %llu seconds ago...", td);
} else if (!j->sampled && (!j->exit_timeout || (LAUNCHD_SAMPLE_TIMEOUT < j->exit_timeout))) {
/* This should work even if the job changes its exit_timeout midstream */
- job_log(j, LOG_NOTICE, "Sampling timeout elapsed (%u seconds). Sampling...", LAUNCHD_SAMPLE_TIMEOUT);
+ job_log(j, LOG_NOTICE | LOG_CONSOLE, "Sampling timeout elapsed (%u seconds). Scheduling a sample...", LAUNCHD_SAMPLE_TIMEOUT);
if (j->exit_timeout) {
unsigned int ttk = (j->exit_timeout - LAUNCHD_SAMPLE_TIMEOUT);
job_assumes(j, kevent_mod((uintptr_t)&j->exit_timeout, EVFILT_TIMER,
EV_ADD|EV_ONESHOT, NOTE_SECONDS, ttk, j) != -1);
- job_log(j, LOG_NOTICE, "Scheduled new exit timeout for %u seconds later", ttk);
+ job_log(j, LOG_NOTICE | LOG_CONSOLE, "Scheduled new exit timeout for %u seconds later", ttk);
}
- job_force_sampletool(j);
+
+ LIST_INSERT_HEAD(&j->mgr->pending_samples, j, pending_samples_sle);
+ jobmgr_dequeue_next_sample(j->mgr);
} else {
- job_force_sampletool(j); /* no-op if already done in previous pass */
- if (unlikely(j->debug_before_kill)) {
- job_log(j, LOG_NOTICE, "Exit timeout elapsed. Entering the kernel debugger");
- job_assumes(j, host_reboot(mach_host_self(), HOST_REBOOT_DEBUGGER) == KERN_SUCCESS);
+ if( !j->sampled ) {
+ job_log(j, LOG_WARNING | LOG_CONSOLE, "Exit timeout elapsed (%u seconds). Will kill after sampling.", j->exit_timeout);
+ LIST_INSERT_HEAD(&j->mgr->pending_samples, j, pending_samples_sle);
+ jobmgr_dequeue_next_sample(j->mgr);
+ j->kill_after_sample = true;
+ } else {
+ if (unlikely(j->debug_before_kill)) {
+ job_log(j, LOG_NOTICE, "Exit timeout elapsed. Entering the kernel debugger");
+ job_assumes(j, host_reboot(mach_host_self(), HOST_REBOOT_DEBUGGER) == KERN_SUCCESS);
+ }
+ job_log(j, LOG_WARNING | LOG_CONSOLE, "Exit timeout elapsed (%u seconds). Killing", j->exit_timeout);
+ job_kill(j);
}
- job_log(j, LOG_WARNING, "Exit timeout elapsed (%u seconds). Killing", j->exit_timeout);
- job_kill(j);
}
} else {
job_assumes(j, false);
@@ -2834,11 +3072,11 @@
jobmgr_dispatch_all_semaphores(jm);
break;
case EVFILT_TIMER:
- if (jobmgr_assumes(jm, kev->ident == (uintptr_t)&sorted_calendar_events)) {
+ if( kev->ident == (uintptr_t)&sorted_calendar_events ) {
calendarinterval_callback();
} else if( kev->ident == (uintptr_t)jm ) {
jobmgr_log(jm, LOG_DEBUG, "Shutdown timer firing.");
- still_alive_with_check();
+ jobmgr_still_alive_with_check(jm);
}
break;
default:
@@ -3371,7 +3609,9 @@
desired_gid = gre->gr_gid;
}
+#if !TARGET_OS_EMBEDDED
job_enable_audit_for_user(j, desired_uid, loginname);
+#endif
if (!job_assumes(j, setlogin(loginname) != -1)) {
_exit(EXIT_FAILURE);
@@ -3408,6 +3648,7 @@
setenv("LOGNAME", loginname, 0);
}
+#if !TARGET_OS_EMBEDDED
void
job_enable_audit_for_user(job_t j, uid_t u, char *name)
{
@@ -3429,6 +3670,7 @@
}
}
}
+#endif
void
job_setup_attributes(job_t j)
@@ -3682,7 +3924,10 @@
void
job_logv(job_t j, int pri, int err, const char *msg, va_list ap)
{
- struct runtime_syslog_attr attr = { "com.apple.launchd", j->label, j->mgr->name, pri, getuid(), getpid(), j->p };
+ bool log_to_console = pri & LOG_CONSOLE;
+ int _pri = pri & ~LOG_CONSOLE;
+
+ struct runtime_syslog_attr attr = { "com.apple.launchd", j->label, j->mgr->name, _pri, getuid(), getpid(), j->p };
char *newmsg;
int oldmask = 0;
size_t newmsgsz;
@@ -3709,7 +3954,7 @@
oldmask = setlogmask(LOG_UPTO(LOG_DEBUG));
}
- runtime_vsyslog(&attr, newmsg, ap);
+ runtime_vsyslog(&attr, log_to_console, newmsg, ap);
if (unlikely(j->debug)) {
setlogmask(oldmask);
@@ -3787,9 +4032,12 @@
if (jm->parentmgr) {
jobmgr_logv(jm->parentmgr, pri, 0, newmsg, ap);
} else {
- struct runtime_syslog_attr attr = { "com.apple.launchd", "com.apple.launchd", jm->name, pri, getuid(), getpid(), getpid() };
+ bool log_to_console = pri & LOG_CONSOLE;
+ int _pri = pri & ~LOG_CONSOLE;
+
+ struct runtime_syslog_attr attr = { "com.apple.launchd", "com.apple.launchd", jm->name, _pri, getuid(), getpid(), getpid() };
- runtime_vsyslog(&attr, newmsg, ap);
+ runtime_vsyslog(&attr, log_to_console, newmsg, ap);
}
}
@@ -3836,15 +4084,18 @@
if (si->fd == -1) {
if ((si->fd = _fd(open(which_path, O_EVTONLY|O_NOCTTY))) == -1) {
which_path = parentdir;
+ si->watching_parent = true;
si->fd = _fd(open(which_path, O_EVTONLY|O_NOCTTY));
+ } else {
+ si->watching_parent = false;
}
}
if (si->fd == -1) {
- return job_log_error(j, LOG_ERR, "Path monitoring failed on \"%s\"", which_path);
+ return job_log_error(j, LOG_ERR, "Path monitoring failed on \"%s\"%s", si->what, si->watching_parent ? " (Could not monitor parent directory)" : "");
}
- job_log(j, LOG_DEBUG, "Watching Vnode: %d", si->fd);
+ job_log(j, LOG_DEBUG, "Watching %svnode: %d", si->watching_parent ? "parent ": "", si->fd);
if (kevent_mod(si->fd, EVFILT_VNODE, EV_ADD, fflags, 0, j) == -1) {
saved_errno = errno;
@@ -3926,12 +4177,24 @@
si->fd = -1; /* this will get fixed in semaphoreitem_watch() */
}
- job_log(j, LOG_DEBUG, "Watch path modified: %s", si->what);
+ if( !si->watching_parent ) {
+ job_log(j, LOG_DEBUG, "Watch path modified: %s", si->what);
- if (si->why == PATH_CHANGES) {
- j->start_pending = true;
+ if (si->why == PATH_CHANGES) {
+ j->start_pending = true;
+ }
+ } else { /* Something happened to the parent directory. See if our target file appeared. */
+ if( !invalidation_reason[0] ){
+ job_assumes(j, runtime_close(si->fd) == 0);
+ si->fd = -1; /* this will get fixed in semaphoreitem_watch() */
+ semaphoreitem_watch(j, si);
+ }
+
+ if( !si->watching_parent ) {
+ j->start_pending = true;
+ }
}
-
+
job_dispatch(j, false);
}
@@ -4330,6 +4593,11 @@
return true;
} else if (j->mgr->shutting_down && (j->hopefully_exits_first || j->mgr->hopefully_first_cnt == 0)) {
job_log(j, LOG_DEBUG, "Exited while shutdown in progress. Processes remaining: %lu/%lu", total_children, total_anon_children);
+ if( total_children == 0 && !j->anonymous ) {
+ job_log(j, LOG_DEBUG | LOG_CONSOLE, "Job was last (non-anonymous) to exit during %s shutdown.", (pid1_magic && j->mgr == root_jobmgr) ? "system" : "job manager");
+ } else if( total_anon_children == 0 && j->anonymous ) {
+ job_log(j, LOG_DEBUG | LOG_CONSOLE, "Job was last (anonymous) to exit during %s shutdown.", (pid1_magic && j->mgr == root_jobmgr) ? "system" : "job manager");
+ }
return true;
} else if (j->legacy_mach_job) {
if (SLIST_EMPTY(&j->machservices)) {
@@ -4617,11 +4885,10 @@
EXCEPTION_STATE_IDENTITY | MACH_EXCEPTION_CODES, f) == KERN_SUCCESS);
job_assumes(j, launchd_mport_deallocate(mhp) == KERN_SUCCESS);
}
-
}
void
-job_set_exeception_port(job_t j, mach_port_t port)
+job_set_exception_port(job_t j, mach_port_t port)
{
if (unlikely(!the_exception_server)) {
the_exception_server = port;
@@ -4678,14 +4945,14 @@
} else if (strcasecmp(key, LAUNCH_JOBKEY_MACH_HIDEUNTILCHECKIN) == 0) {
ms->hide = b;
} else if (strcasecmp(key, LAUNCH_JOBKEY_MACH_EXCEPTIONSERVER) == 0) {
- job_set_exeception_port(ms->job, ms->port);
+ job_set_exception_port(ms->job, ms->port);
} else if (strcasecmp(key, LAUNCH_JOBKEY_MACH_KUNCSERVER) == 0) {
ms->kUNCServer = b;
job_assumes(ms->job, host_set_UNDServer(mhp, ms->port) == KERN_SUCCESS);
}
break;
case LAUNCH_DATA_DICTIONARY:
- job_set_exeception_port(ms->job, ms->port);
+ job_set_exception_port(ms->job, ms->port);
break;
default:
break;
@@ -4733,7 +5000,8 @@
jobmgr_log(jm, LOG_DEBUG, "Garbage collecting.");
- if (jm->hopefully_first_cnt) {
+ if (jm->hopefully_first_cnt && !jm->killed_hopefully_first_jobs) {
+ jobmgr_log(jm, LOG_DEBUG, "jm->hopefully_first_cnt = %u, jm->killed_hopefully_first_jobs = %s", jm->hopefully_first_cnt, jm->killed_hopefully_first_jobs ? "true" : "false");
return jm;
}
@@ -4755,7 +5023,8 @@
jm->sent_stop_to_normal_jobs = true;
}
- if (jm->normal_active_cnt) {
+ if (jm->normal_active_cnt && !jm->killed_normal_jobs) {
+ jobmgr_log(jm, LOG_DEBUG, "jm->normal_active_cnt = %u, jm->killed_normal_jobs = %s", jm->normal_active_cnt, jm->killed_normal_jobs ? "true" : "false");
return jm;
}
@@ -4774,24 +5043,39 @@
}
if (!SLIST_EMPTY(&jm->submgrs)) {
+ jobmgr_log(jm, LOG_DEBUG, "jm->submgrs not empty!");
return jm;
}
- LIST_FOREACH(ji, &jm->jobs, sle) {
- if (!ji->anonymous) {
- return jm;
+ jobmgr_t retval = NULL;
+ if( pid1_magic && jm == root_jobmgr ) {
+ if( total_children > 0 && !jm->killed_hopefully_last_jobs ) {
+ retval = jm;
+ } else {
+ jobmgr_log_stray_children(jm);
+ jobmgr_remove(jm, false);
}
+ } else {
+ LIST_FOREACH(ji, &jm->jobs, sle) {
+ if (!ji->anonymous) {
+ retval = jm;
+ }
+ }
+
+ if( !retval ) {
+ jobmgr_log_stray_children(jm);
+ jobmgr_remove(jm, false);
+ }
}
-
- jobmgr_log_stray_children(jm);
- jobmgr_remove(jm);
- return NULL;
+
+ return retval;
}
void
jobmgr_kill_stray_child(jobmgr_t jm, pid_t p)
{
- struct timespec ts = { 2, 0 };
+ struct timespec tts = { 2, 0 }; /* Wait 2 seconds for stray children to die after being SIGTERM'ed. */
+ struct timespec kts = { 1, 0 }; /* Wait 1 second for stray children to die after being SIGKILL'ed. */
uint64_t start, end, nanosec;
struct kevent kev;
int r, kq = kqueue();
@@ -4812,22 +5096,25 @@
goto out;
}
- r = kevent(kq, NULL, 0, &kev, 1, &ts);
+ r = kevent(kq, NULL, 0, &kev, 1, &tts);
if (!jobmgr_assumes(jm, r == 0 || r == 1)) {
goto out;
}
if (r == 0 && jobmgr_assumes(jm, runtime_kill(p, SIGKILL) != -1)) {
- jobmgr_assumes(jm, kevent(kq, NULL, 0, &kev, 1, NULL) == 1);
+ jobmgr_assumes(jm, (r = kevent(kq, NULL, 0, &kev, 1, &kts)) == 1);
}
end = runtime_get_opaque_time();
nanosec = runtime_opaque_time_to_nano(end - start);
- jobmgr_log(jm, LOG_DEBUG, "PID %u died after %llu nanoseconds", p, nanosec);
-
+ jobmgr_log(jm, LOG_DEBUG | LOG_CONSOLE, "PID %u %s after %llu nanoseconds", p, r == 0 ? "did not die" : "died", nanosec);
+ if( r == 0 ) {
+ jobmgr_log(jm, LOG_DEBUG | LOG_CONSOLE, "Disregarding PID %u and continuing.", p);
+ }
+
out:
jobmgr_assumes(jm, runtime_close(kq) != -1);
}
@@ -4870,10 +5157,13 @@
kp_skipped++;
continue;
}
-
- jobmgr_log(jm, LOG_WARNING, "Stray %sprocess at shutdown: PID %u PPID %u PGID %u %s", z, p_i, pp_i, pg_i, n);
-
- jobmgr_kill_stray_child(jm, p_i);
+
+ /* We might have some jobs hanging around that we've decided to shut down in spite of. If so, don't bother trying to kill them again. */
+ job_t j = jobmgr_find_by_pid(jm, p_i, false);
+ if( !j || (j && j->anonymous) ) {
+ jobmgr_log(jm, LOG_WARNING, "Stray %s %s at shutdown: PID %u PPID %u PGID %u %s", z, j ? "anonymous job" : "process", p_i, pp_i, pg_i, n);
+ jobmgr_kill_stray_child(jm, p_i);
+ }
}
out:
@@ -4998,6 +5288,10 @@
}
}
+ jmr->killed_hopefully_first_jobs = false;
+ jmr->killed_normal_jobs = false;
+ jmr->killed_hopefully_last_jobs = false;
+
jobmgr_log(jmr, LOG_DEBUG, "Created job manager%s%s", jm ? " with parent: " : ".", jm ? jm->name : "");
if (bootstrapper) {
@@ -5012,7 +5306,7 @@
out_bad:
if (jmr) {
- jobmgr_remove(jmr);
+ jobmgr_remove(jmr, false);
}
return NULL;
}
@@ -5303,6 +5597,7 @@
job_dispatch(j, false);
}
+#if 0
void
job_force_sampletool(job_t j)
{
@@ -5409,6 +5704,7 @@
job_log(j, LOG_DEBUG, "Finished sampling.");
}
+#endif
bool
semaphoreitem_new(job_t j, semaphore_reason_t why, const char *what)
@@ -6393,28 +6689,32 @@
return BOOTSTRAP_NO_MEMORY;
}
- ms->delete_on_destruction = !j->legacy_mach_job;
+ /* Treat this like a legacy job. */
+ if( !j->legacy_mach_job ) {
+ ms->isActive = true;
+ ms->recv = false;
+ }
job_checkin(j);
if (!(j->anonymous || j->legacy_LS_job || j->legacy_mach_job)) {
job_log(j, LOG_NOTICE, "Please add the following service to the configuration file for this job: %s", servicename);
}
- }
+ } else {
+ if (unlikely((jo = machservice_job(ms)) != j)) {
+ static pid_t last_warned_pid;
- if (unlikely((jo = machservice_job(ms)) != j)) {
- static pid_t last_warned_pid;
+ if (last_warned_pid != ldc->pid) {
+ job_log(j, LOG_NOTICE, "Check-in of Mach service failed. The service \"%s\" is owned by: %s", servicename, jo->label);
+ last_warned_pid = ldc->pid;
+ }
- if (last_warned_pid != ldc->pid) {
- job_log(j, LOG_NOTICE, "Check-in of Mach service failed. The service \"%s\" is owned by: %s", servicename, jo->label);
- last_warned_pid = ldc->pid;
+ return BOOTSTRAP_NOT_PRIVILEGED;
}
-
- return BOOTSTRAP_NOT_PRIVILEGED;
+ if (unlikely(machservice_active(ms))) {
+ job_log(j, LOG_WARNING, "Check-in of Mach service failed. Already active: %s", servicename);
+ return BOOTSTRAP_SERVICE_ACTIVE;
+ }
}
- if (unlikely(machservice_active(ms))) {
- job_log(j, LOG_WARNING, "Check-in of Mach service failed. Already active: %s", servicename);
- return BOOTSTRAP_SERVICE_ACTIVE;
- }
machservice_request_notifications(ms);
@@ -6467,7 +6767,7 @@
return BOOTSTRAP_SERVICE_ACTIVE;
}
if (ms->recv && (serviceport != MACH_PORT_NULL)) {
- job_log(j, LOG_ERR, "bootstrap_register() erroneous called instead of bootstrap_check_in(). Mach service: %s", servicename);
+ job_log(j, LOG_ERR, "bootstrap_register() erroneously called instead of bootstrap_check_in(). Mach service: %s", servicename);
return BOOTSTRAP_NOT_PRIVILEGED;
}
job_checkin(j);
@@ -6529,7 +6829,7 @@
if (unlikely(!per_pid_lookup && j->lastlookup == ms && j->lastlookup_gennum == ms->gen_num && !j->per_user)) {
/* we need to think more about the per_pid_lookup logic before logging about repeated lookups */
- job_log(j, LOG_APPLEONLY, "Performance: Please fix the framework that talks to \"%s\" to cache the Mach port for service: %s", ms->job->label, servicename);
+ job_log(j, LOG_DEBUG, "Performance: Please fix the framework that talks to \"%s\" to cache the Mach port for service: %s", ms->job->label, servicename);
}
j->lastlookup = ms;
@@ -7068,10 +7368,9 @@
}
kern_return_t
-job_mig_embedded_kickstart(job_t j, name_t targetlabel, pid_t *out_pid, mach_port_t *out_name_port)
+job_mig_kickstart(job_t j, name_t targetlabel, pid_t *out_pid, mach_port_t *out_name_port, mach_port_t *obsrvr_port)
{
struct ldcred *ldc = runtime_get_caller_creds();
- kern_return_t kr;
job_t otherj;
if (!launchd_assumes(j != NULL)) {
@@ -7082,12 +7381,13 @@
return BOOTSTRAP_UNKNOWN_SERVICE;
}
- if (ldc->euid != 0 && ldc->euid != geteuid()
#if TARGET_OS_EMBEDDED
- && j->username && otherj->username
- && strcmp(j->username, otherj->username) != 0
+ bool embedded_check = j->username && otherj->username && ( strcmp(j->username, otherj->username) != 0 );
+#else
+ bool embedded_check = true;
#endif
- ) {
+
+ if( ldc->euid != 0 && ldc->euid != geteuid() && embedded_check ) {
return BOOTSTRAP_NOT_PRIVILEGED;
}
@@ -7097,11 +7397,16 @@
return BOOTSTRAP_NO_MEMORY;
}
- kr = task_name_for_pid(mach_task_self(), otherj->p, out_name_port);
+ kern_return_t kr = task_name_for_pid(mach_task_self(), otherj->p, out_name_port);
if (!job_assumes(j, kr == 0)) {
return kr;
}
+ if (!job_setup_machport(otherj)) {
+ return BOOTSTRAP_NO_MEMORY;
+ }
+
+ *obsrvr_port = otherj->j_port;
*out_pid = otherj->p;
return 0;
Modified: trunk/launchd/src/launchd_runtime.c
===================================================================
--- trunk/launchd/src/launchd_runtime.c 2008-10-17 00:16:24 UTC (rev 23737)
+++ trunk/launchd/src/launchd_runtime.c 2008-10-17 00:18:37 UTC (rev 23738)
@@ -1201,22 +1201,24 @@
INTERNAL_ABI void
runtime_syslog(int pri, const char *message, ...)
{
+ bool log_to_console = pri & LOG_CONSOLE;
+ int _pri = pri & ~LOG_CONSOLE;
+
struct runtime_syslog_attr attr = {
"com.apple.launchd", "com.apple.launchd",
pid1_magic ? "System" : "Background",
- pri, getuid(), getpid(), getpid()
+ _pri, getuid(), getpid(), getpid()
};
va_list ap;
va_start(ap, message);
+ runtime_vsyslog(&attr, log_to_console, message, ap);
- runtime_vsyslog(&attr, message, ap);
-
va_end(ap);
}
INTERNAL_ABI void
-runtime_vsyslog(struct runtime_syslog_attr *attr, const char *message, va_list args)
+runtime_vsyslog(struct runtime_syslog_attr *attr, bool echo_to_console, const char *message, va_list args)
{
int saved_errno = errno;
char newmsg[10000];
@@ -1235,8 +1237,8 @@
vsnprintf(newmsg, sizeof(newmsg), message, args);
- if (unlikely(low_level_debug)) {
- fprintf(stderr, "%s %u\t%s %u\t%s\n", attr->from_name, attr->from_pid,
+ if (unlikely(low_level_debug) || echo_to_console) {
+ fprintf(g_console, "%s %u\t%s %u\t%s\n", attr->from_name, attr->from_pid,
attr->about_name, attr->about_pid, newmsg);
}
Modified: trunk/launchd/src/launchd_runtime.h
===================================================================
--- trunk/launchd/src/launchd_runtime.h 2008-10-17 00:16:24 UTC (rev 23737)
+++ trunk/launchd/src/launchd_runtime.h 2008-10-17 00:18:37 UTC (rev 23738)
@@ -144,6 +144,7 @@
INTERNAL_ABI kern_return_t runtime_log_drain(mach_port_t srp, vm_offset_t *outval, mach_msg_type_number_t *outvalCnt);
#define LOG_APPLEONLY 0x4141504c /* AAPL in hex */
+#define LOG_CONSOLE (1 << 31)
struct runtime_syslog_attr {
const char *from_name;
@@ -158,7 +159,7 @@
INTERNAL_ABI int runtime_setlogmask(int maskpri);
INTERNAL_ABI void runtime_closelog(void);
INTERNAL_ABI void runtime_syslog(int pri, const char *message, ...) __attribute__((format(printf, 2, 3)));
-INTERNAL_ABI void runtime_vsyslog(struct runtime_syslog_attr *attr, const char *message, va_list args) __attribute__((format(printf, 2, 0)));
+INTERNAL_ABI void runtime_vsyslog(struct runtime_syslog_attr *attr, bool log_to_console, const char *message, va_list args) __attribute__((format(printf, 3, 0)));
INTERNAL_ABI void runtime_log_push(void);
INTERNAL_ABI int64_t runtime_get_wall_time(void) __attribute__((warn_unused_result));
Modified: trunk/launchd/src/libbootstrap.c
===================================================================
--- trunk/launchd/src/libbootstrap.c 2008-10-17 00:16:24 UTC (rev 23737)
+++ trunk/launchd/src/libbootstrap.c 2008-10-17 00:18:37 UTC (rev 23738)
@@ -174,6 +174,7 @@
static mach_port_t prev_bp;
static mach_port_t prev_sp;
static name_t prev_name;
+ static bool privileged_server_okay;
audit_token_t au_tok;
bool per_pid_lookup = flags & BOOTSTRAP_PER_PID_SERVICE;
kern_return_t kr = 0;
@@ -186,7 +187,7 @@
}
if (prev_sp) {
- if ((bp == prev_bp) && (strncmp(prev_name, service_name, sizeof(name_t)) == 0)
+ if ((bp == prev_bp) && (strncmp(prev_name, service_name, sizeof(name_t)) == 0)
&& (mach_port_mod_refs(mach_task_self(), prev_sp, MACH_PORT_RIGHT_SEND, 1) == 0)) {
*sp = prev_sp;
goto out;
@@ -197,6 +198,7 @@
}
skip_cache:
+ privileged_server_okay = false;
if ((kr = vproc_mig_look_up2(bp, (char *)service_name, sp, &au_tok, target_pid, flags)) != VPROC_ERR_TRY_PER_USER) {
goto out;
}
@@ -219,7 +221,7 @@
pthread_mutex_unlock(&bslu2_lock);
- if ((kr == 0) && (flags & BOOTSTRAP_PRIVILEGED_SERVER)) {
+ if ((kr == 0) && (flags & BOOTSTRAP_PRIVILEGED_SERVER) && !privileged_server_okay) {
uid_t server_euid;
/*
@@ -236,6 +238,8 @@
if (server_euid) {
mach_port_deallocate(mach_task_self(), *sp);
kr = BOOTSTRAP_NOT_PRIVILEGED;
+ } else {
+ privileged_server_okay = true;
}
}
Modified: trunk/launchd/src/liblaunch.c
===================================================================
--- trunk/launchd/src/liblaunch.c 2008-10-17 00:16:24 UTC (rev 23737)
+++ trunk/launchd/src/liblaunch.c 2008-10-17 00:18:37 UTC (rev 23738)
@@ -210,20 +210,23 @@
if (where && where[0] != '\0') {
strncpy(sun.sun_path, where, sizeof(sun.sun_path));
- } else if (!getenv("SUDO_COMMAND") && _vprocmgr_getsocket(spath) == 0) {
+ } else if ((!getenv("SUDO_COMMAND") || geteuid() != 0) && _vprocmgr_getsocket(spath) == 0) {
size_t min_len;
min_len = sizeof(sun.sun_path) < sizeof(spath) ? sizeof(sun.sun_path) : sizeof(spath);
- strncpy(sun.sun_path, spath, min_len);
+ strncpy(sun.sun_path, spath, min_len);
} else {
strncpy(sun.sun_path, LAUNCHD_SOCK_PREFIX "/sock", sizeof(sun.sun_path));
}
- if ((lfd = _fd(socket(AF_UNIX, SOCK_STREAM, 0))) == -1)
+ if ((lfd = _fd(socket(AF_UNIX, SOCK_STREAM, 0))) == -1) {
goto out_bad;
- if (-1 == connect(lfd, (struct sockaddr *)&sun, sizeof(sun)))
+ }
+
+ if (-1 == connect(lfd, (struct sockaddr *)&sun, sizeof(sun))) {
goto out_bad;
+ }
}
if (!(_lc->l = launchd_fdopen(lfd)))
goto out_bad;
@@ -1256,18 +1259,20 @@
pid_t
create_and_switch_to_per_session_launchd(const char *login __attribute__((unused)), int flags __attribute__((unused)), ...)
{
- mach_port_t bezel_ui_server;
- struct stat sb;
uid_t target_user = geteuid() ? geteuid() : getuid();
if (_vprocmgr_move_subset_to_user(target_user, "Aqua")) {
return -1;
}
-#define BEZEL_UI_PATH "/System/Library/LoginPlugins/BezelServices.loginPlugin/Contents/Resources/BezelUI/BezelUIServer"
-#define BEZEL_UI_PLIST "/System/Library/LaunchAgents/com.apple.BezelUIServer.plist"
-#define BEZEL_UI_SERVICE "BezelUI"
+#define BEZEL_UI_HACK
+#ifdef BEZEL_UI_HACK
+ #define BEZEL_UI_PATH "/System/Library/LoginPlugins/BezelServices.loginPlugin/Contents/Resources/BezelUI/BezelUIServer"
+ #define BEZEL_UI_PLIST "/System/Library/LaunchAgents/com.apple.BezelUIServer.plist"
+ #define BEZEL_UI_SERVICE "BezelUI"
+ mach_port_t bezel_ui_server;
+ struct stat sb;
if (!(stat(BEZEL_UI_PLIST, &sb) == 0 && S_ISREG(sb.st_mode))) {
if (bootstrap_create_server(bootstrap_port, BEZEL_UI_PATH, target_user, true, &bezel_ui_server) == BOOTSTRAP_SUCCESS) {
mach_port_t srv;
@@ -1279,6 +1284,7 @@
mach_port_deallocate(mach_task_self(), bezel_ui_server);
}
}
+#endif
return 1;
}
Modified: trunk/launchd/src/libvproc.c
===================================================================
--- trunk/launchd/src/libvproc.c 2008-10-17 00:16:24 UTC (rev 23737)
+++ trunk/launchd/src/libvproc.c 2008-10-17 00:18:37 UTC (rev 23738)
@@ -144,7 +144,11 @@
size_t
_vproc_standby_count(void)
{
+#ifdef VPROC_STANDBY_IMPLEMENTED
return likely(vproc_shmem) ? vproc_shmem->vp_shmem_standby_cnt : INT32_MAX;
+#else
+ return 0;
+#endif
}
size_t
@@ -203,16 +207,21 @@
vproc_standby_t
vproc_standby_begin(vproc_t vp __attribute__((unused)))
{
+#ifdef VPROC_STANDBY_IMPLEMENTED
vproc_standby_t vpsb = (vproc_standby_t)vproc_shmem_init; /* we need a "random" variable that is testable */
_vproc_standby_begin();
return vpsb;
+#else
+ return NULL;
+#endif
}
void
_vproc_standby_begin(void)
{
+#ifdef VPROC_STANDBY_IMPLEMENTED
typeof(vproc_shmem->vp_shmem_standby_cnt) newval;
if (unlikely(vproc_shmem == NULL)) {
@@ -228,22 +237,30 @@
__crashreporter_info__ = "Unbalanced: vproc_standby_begin()";
abort();
}
+#else
+ return;
+#endif
}
void
-vproc_standby_end(vproc_t vp __attribute__((unused)), vproc_standby_t vpt)
+vproc_standby_end(vproc_t vp __attribute__((unused)), vproc_standby_t vpt __attribute__((unused)))
{
+#ifdef VPROC_STANDBY_IMPLEMENTED
if (unlikely(vpt != (vproc_standby_t)vproc_shmem_init)) {
__crashreporter_info__ = "Bogus standby handle passed to vproc_standby_end() ";
abort();
}
_vproc_standby_end();
+#else
+ return;
+#endif
}
void
_vproc_standby_end(void)
{
+#ifdef VPROC_STANDBY_IMPLEMENTED
typeof(vproc_shmem->vp_shmem_standby_cnt) newval;
if( unlikely(vproc_shmem == NULL) ) {
@@ -257,6 +274,9 @@
__crashreporter_info__ = "Unbalanced: vproc_standby_end()";
abort();
}
+#else
+ return;
+#endif
}
kern_return_t
@@ -371,7 +391,15 @@
}
}
- return _vproc_post_fork_ping();
+ /* 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;
}
@@ -794,9 +822,20 @@
}
vproc_err_t
-_vproc_kickstart_by_label(const char *label, pid_t *out_pid, mach_port_t *out_port_name)
+_vproc_kickstart_by_label(const char *label, pid_t *out_pid, mach_port_t *out_port_name, mach_port_t *out_obsrvr_port)
{
- if (vproc_mig_embedded_kickstart(bootstrap_port, (char *)label, out_pid, out_port_name) == 0) {
+ mach_port_t junk = MACH_PORT_NULL;
+ mach_port_t junk2 = MACH_PORT_NULL;
+
+ if( vproc_mig_kickstart(bootstrap_port, (char *)label, out_pid, out_port_name ?: &junk, out_obsrvr_port ?: &junk2) == 0 ) {
+ if( !out_port_name ) {
+ mach_port_mod_refs(mach_task_self(), junk, MACH_PORT_RIGHT_SEND, -1);
+ }
+
+ if( !out_obsrvr_port ) {
+ mach_port_mod_refs(mach_task_self(), junk2, MACH_PORT_RIGHT_SEND, -1);
+ }
+
return NULL;
}
Modified: trunk/launchd/src/libvproc_private.h
===================================================================
--- trunk/launchd/src/libvproc_private.h 2008-10-17 00:16:24 UTC (rev 23737)
+++ trunk/launchd/src/libvproc_private.h 2008-10-17 00:18:37 UTC (rev 23738)
@@ -28,7 +28,9 @@
#include <stdbool.h>
#include <launch.h>
-#define VPROC_HAS_TRANSACTIONS 1
+#ifndef VPROC_HAS_TRANSACTIONS
+ #define VPROC_HAS_TRANSACTIONS
+#endif
__BEGIN_DECLS
@@ -68,7 +70,7 @@
vproc_err_t _vprocmgr_log_drain(vproc_t vp, pthread_mutex_t *optional_mutex_around_callback, _vprocmgr_log_drain_callback_t func);
vproc_err_t _vproc_send_signal_by_label(const char *label, int sig);
-vproc_err_t _vproc_kickstart_by_label(const char *label, pid_t *out_pid, mach_port_t *out_port_name);
+vproc_err_t _vproc_kickstart_by_label(const char *label, pid_t *out_pid, mach_port_t *out_port_name, mach_port_t *out_obsrvr_port);
vproc_err_t _vproc_wait_by_label(const char *label, int *out_wstatus);
void _vproc_log(int pri, const char *msg, ...) __attribute__((format(printf, 2, 3)));
Modified: trunk/launchd/src/libvproc_public.h
===================================================================
--- trunk/launchd/src/libvproc_public.h 2008-10-17 00:16:24 UTC (rev 23737)
+++ trunk/launchd/src/libvproc_public.h 2008-10-17 00:18:37 UTC (rev 23738)
@@ -22,7 +22,12 @@
#include <sys/cdefs.h>
#include <sys/types.h>
+#include <Availability.h>
+#ifndef VPROC_HAS_TRANSACTIONS
+ #define VPROC_HAS_TRANSACTIONS
+#endif
+
__BEGIN_DECLS
#pragma GCC visibility push(default)
@@ -92,7 +97,7 @@
* Call this API before creating data that needs to be saved via I/O later.
*/
vproc_transaction_t
-vproc_transaction_begin(vproc_t virtual_proc);
+vproc_transaction_begin(vproc_t virtual_proc) __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_NA);
/*!
* @function vproc_transaction_end
@@ -110,7 +115,7 @@
* Calling this API with the same handle more than once is undefined.
*/
void
-vproc_transaction_end(vproc_t virtual_proc, vproc_transaction_t handle);
+vproc_transaction_end(vproc_t virtual_proc, vproc_transaction_t handle) __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_NA);
/*!
* @typedef vproc_standby_t
@@ -132,9 +137,12 @@
* @abstract
* Call this API before registering notifications. For example: timers network
* state change, or when monitoring keyboard/mouse events.
+ *
+ * @discussion
+ * This API is undefined and is currently a no-op.
*/
vproc_standby_t
-vproc_standby_begin(vproc_t virtual_proc);
+vproc_standby_begin(vproc_t virtual_proc) __OSX_AVAILABLE_STARTING(__MAC_NA, __IPHONE_NA);
/*!
* @function vproc_standby_end
@@ -150,9 +158,10 @@
*
* @discussion
* Calling this API with the same handle more than once is undefined.
+ * This API is undefined and is currently a no-op.
*/
void
-vproc_standby_end(vproc_t virtual_proc, vproc_standby_t handle);
+vproc_standby_end(vproc_t virtual_proc, vproc_standby_t handle) __OSX_AVAILABLE_STARTING(__MAC_NA, __IPHONE_NA);
#pragma GCC visibility pop
Modified: trunk/launchd/src/protocol_job.defs
===================================================================
--- trunk/launchd/src/protocol_job.defs 2008-10-17 00:16:24 UTC (rev 23737)
+++ trunk/launchd/src/protocol_job.defs 2008-10-17 00:18:37 UTC (rev 23738)
@@ -170,11 +170,12 @@
__bs_port : job_t;
__inval : pointer_t);
-routine embedded_kickstart(
- __bs_port : job_t;
- __label : name_t;
- out __pid : pid_t;
- out __name_port : mach_port_t);
+routine kickstart(
+ __bs_port : job_t;
+ __label : name_t;
+ out __pid : pid_t;
+ out __name_port : mach_port_t;
+ out __obsrvr_port : mach_port_make_send_t);
routine embedded_wait(
__bs_port : job_t;
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.macosforge.org/pipermail/launchd-changes/attachments/20081016/7a256152/attachment-0001.html
More information about the launchd-changes
mailing list