[launchd-changes] [23780] trunk/launchd/src

source_changes at macosforge.org source_changes at macosforge.org
Tue Jan 13 14:18:50 PST 2009


Revision: 23780
          http://trac.macosforge.org/projects/launchd/changeset/23780
Author:   dsorresso at apple.com
Date:     2009-01-13 14:18:50 -0800 (Tue, 13 Jan 2009)
Log Message:
-----------
<rdar://problem/4344661> DOC: Launchd documentation needs updating
<rdar://problem/5665637> launchd needs to read: ~/.MacOSX/environment.plist
<rdar://problem/6252612> DOC: SystemStarter: man page doesn't reflect its deprecation
<rdar://problem/6311035> Investigate flattening the per-PID Mach namespace to let FileSyncAgent work in a flat Mach namespace world
<rdar://problem/6317143> Tweak HopefullyExitsLast behavior to SIGTERM and not wait for jobs to die
<rdar://problem/6444742> _vproc_transaction_count_for_pid() has a stack-butchering type mismatch.
<rdar://problem/6452938> Reconsider sync() and unmounts in launchd's shutdown path
<rdar://problem/6454800> launchd's logging about what looks like a bug in it.
<rdar://problem/6485062> 10A245: launchd logging errors about Bug: launchd_core_logic.c:2902
<rdar://problem/6485067> 10A245: launchd logging errors about Bug: launchd_core_logic.c:3051
<rdar://problem/6486016> 10A245: launchd logging many exited/killed message to system.log
<rdar://problem/6487997> vproc functions log too much.

Modified Paths:
--------------
    trunk/launchd/src/launchctl.c
    trunk/launchd/src/launchd.c
    trunk/launchd/src/launchd_core_logic.c
    trunk/launchd/src/libvproc.c

Modified: trunk/launchd/src/launchctl.c
===================================================================
--- trunk/launchd/src/launchctl.c	2009-01-13 19:16:03 UTC (rev 23779)
+++ trunk/launchd/src/launchctl.c	2009-01-13 22:18:50 UTC (rev 23780)
@@ -116,6 +116,7 @@
 static launch_data_t CF2launch_data(CFTypeRef);
 static launch_data_t read_plist_file(const char *file, bool editondisk, bool load);
 static CFPropertyListRef CreateMyPropertyListFromFile(const char *);
+static CFPropertyListRef CFPropertyListCreateFromFile(CFURLRef plistURL);
 static void WriteMyPropertyListToFile(CFPropertyListRef, const char *);
 static bool path_goodness_check(const char *path, bool forceload);
 static void readpath(const char *, struct load_unload_state *);
@@ -158,6 +159,7 @@
 static void do_single_user_mode(bool);
 static bool do_single_user_mode2(void);
 static void read_launchd_conf(void);
+static void read_environment_dot_plist(void);
 static bool job_disabled_logic(launch_data_t obj);
 static void fix_bogus_file_metadata(void);
 static void do_file_init(void) __attribute__((constructor));
@@ -415,6 +417,96 @@
 	fclose(f);
 }
 
+CFPropertyListRef CFPropertyListCreateFromFile(CFURLRef plistURL)
+{	
+	CFReadStreamRef plistReadStream = CFReadStreamCreateWithFile(NULL, plistURL);
+	
+	CFErrorRef streamErr = NULL;
+	if( !CFReadStreamOpen(plistReadStream) ) {
+		streamErr = CFReadStreamCopyError(plistReadStream);
+		CFStringRef errString = CFErrorCopyDescription(streamErr);
+		
+		CFShow(errString);
+		
+		CFRelease(errString);
+		CFRelease(streamErr);
+	}
+	
+	CFPropertyListRef plist = NULL;
+	if( plistReadStream ) {
+		CFStringRef errString = NULL;
+		CFPropertyListFormat plistFormat = 0;
+		plist = CFPropertyListCreateFromStream(NULL, plistReadStream, 0, kCFPropertyListImmutable, &plistFormat, &errString);
+		if( !plist ) {
+			CFShow(errString);
+			CFRelease(errString);
+		}
+	}
+	
+	CFReadStreamClose(plistReadStream);
+	CFRelease(plistReadStream);
+	
+	return plist;
+}
+
+#define CFReleaseIfNotNULL(cf) if( cf ) CFRelease(cf);
+void
+read_environment_dot_plist(void)
+{
+	CFStringRef plistPath = NULL;
+	CFURLRef plistURL = NULL;
+	CFDictionaryRef envPlist = NULL;
+	launch_data_t req = NULL, launch_env_dict = NULL, resp = NULL;
+	
+	plistPath = CFStringCreateWithFormat(NULL, NULL, CFSTR("%s/.MacOSX/environment.plist"), getenv("HOME"));
+	if( !assumes(plistPath != NULL) ) {
+		goto out;
+	}
+	
+	plistURL = CFURLCreateWithFileSystemPath(NULL, plistPath, kCFURLPOSIXPathStyle, false);
+	if( !assumes(plistURL != NULL) ) {
+		goto out;
+	}
+	
+	envPlist = (CFDictionaryRef)CFPropertyListCreateFromFile(plistURL);
+	if( !assumes(envPlist != NULL) ) {
+		goto out;
+	}
+	
+	launch_env_dict = CF2launch_data(envPlist);
+	if( !assumes(launch_env_dict != NULL) ) {
+		goto out;
+	}
+	
+	req = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
+	if( !assumes(req != NULL) ) {
+		goto out;
+	}
+	
+	launch_data_dict_insert(req, launch_env_dict, LAUNCH_KEY_SETUSERENVIRONMENT);
+	resp = launch_msg(req);
+	if( !assumes(resp != NULL) ) {
+		goto out;
+	}
+	
+	if( !assumes(launch_data_get_type(resp) == LAUNCH_DATA_ERRNO) ) {
+		goto out;
+	}
+
+	assumes(launch_data_get_errno(resp) == 0);
+out:
+	CFReleaseIfNotNULL(plistPath);
+	CFReleaseIfNotNULL(plistURL);
+	CFReleaseIfNotNULL(envPlist);	
+	if( req ) {
+		launch_data_free(req);
+	}
+	
+	if( resp ) {
+		launch_data_free(resp);
+	}
+}
+
 int
 unsetenv_cmd(int argc, char *const argv[])
 {
@@ -1897,7 +1989,7 @@
 		if (is_safeboot()) {
 			load_launchd_items[4] = "system";
 		}
-
+		
 		if (strcasecmp(session_type, VPROCMGR_SESSION_BACKGROUND) == 0 || strcasecmp(session_type, VPROCMGR_SESSION_LOGINWINDOW) == 0) {
 			load_launchd_items[4] = "system";
 			if (!is_safeboot()) {
@@ -1937,6 +2029,18 @@
 			 * agents.
 			 */
 			the_argc_user = 5;
+			
+			/* We want to read environment.plist, which is in the user's home directory.
+			 * Since the dance to mount a network home directory is fairly complex, all we
+			 * can do is try and read environment.plist when bootstrapping the Aqua session,
+			 * which is when we assume the home directory is present.
+			 *
+			 * The drawback here is that jobs bootstrapped in the Background session won't
+			 * get the new environment until they quit and relaunch. But then again, they 
+			 * won't get the updated HOME directory or anything either. This is just a messy
+			 * problem.
+			 */
+			read_environment_dot_plist();
 		} else if (strcasecmp(session_type, VPROCMGR_SESSION_AQUA) == 0) {
 			/* If we're bootstrapping the StandardIO session, bootstrap the user's Background
 			 * agents.

Modified: trunk/launchd/src/launchd.c
===================================================================
--- trunk/launchd/src/launchd.c	2009-01-13 19:16:03 UTC (rev 23779)
+++ trunk/launchd/src/launchd.c	2009-01-13 22:18:50 UTC (rev 23780)
@@ -175,15 +175,13 @@
 		ipc_server_init();
 		
 		runtime_log_push();
-
-		int64_t now = runtime_get_wall_time();
 		
 		struct passwd *pwent = getpwuid(getuid());
 		if( pwent ) {
 			strlcpy(g_username, pwent->pw_name, sizeof(g_username) - 1);
 		}
 		
-		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);
+		runtime_syslog(LOG_DEBUG, "Per-user launchd for UID %u (%s) has begun.", getuid(), g_username);
 	}
 
 	if( pid1_magic ) {

Modified: trunk/launchd/src/launchd_core_logic.c
===================================================================
--- trunk/launchd/src/launchd_core_logic.c	2009-01-13 19:16:03 UTC (rev 23779)
+++ trunk/launchd/src/launchd_core_logic.c	2009-01-13 22:18:50 UTC (rev 23780)
@@ -336,9 +336,7 @@
 	unsigned int hopefully_first_cnt;
 	unsigned int normal_active_cnt;
 	unsigned int jetsam_jobs_cnt;
-	unsigned int 	sent_stop_to_normal_jobs		:1, 
-					sent_stop_to_hopefully_last_jobs:1, 
-					shutting_down					:1, 
+	unsigned int 	shutting_down					:1,
 					session_initialized				:1, 
 					killed_hopefully_first_jobs		:1,
 					killed_normal_jobs				:1,
@@ -357,16 +355,19 @@
 static jobmgr_t jobmgr_new(jobmgr_t jm, mach_port_t requestorport, mach_port_t transfer_port, bool sflag, const char *name);
 static job_t jobmgr_import2(jobmgr_t jm, launch_data_t pload);
 static jobmgr_t jobmgr_parent(jobmgr_t jm);
+static jobmgr_t jobmgr_do_hopefully_first_shutdown_phase(jobmgr_t jm);
+static jobmgr_t jobmgr_do_normal_shutdown_phase(jobmgr_t jm);
+static jobmgr_t jobmgr_do_hopefully_last_shutdown_phase(jobmgr_t jm);
 static jobmgr_t jobmgr_do_garbage_collection(jobmgr_t jm);
 static bool jobmgr_label_test(jobmgr_t jm, const char *str);
 static void jobmgr_reap_bulk(jobmgr_t jm, struct kevent *kev);
-static void jobmgr_log_stray_children(jobmgr_t jm);
+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_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_deep(jobmgr_t jm, pid_t p);
+static job_t jobmgr_find_by_pid_deep(jobmgr_t jm, pid_t p, bool anon_okay);
 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);
@@ -499,6 +500,7 @@
 			nosy						:1, /* The job has an OtherJobEnabled KeepAlive criterion. */
 			crashed						:1, /* The job is the default Mach exception handler, and it crashed. */
 			reaped						:1, /* We've received NOTE_EXIT for the job. */
+			stopped						:1, /* job_stop() was called. */
 			jetsam_frontmost			:1; /* The job is considered "frontmost" by Jetsam. */
 	mode_t mask;
 	pid_t sample_pid;
@@ -591,7 +593,6 @@
 static size_t our_strhash(const char *s) __attribute__((pure));
 static void extract_rcsid_substr(const char *i, char *o, size_t osz);
 static void do_first_per_user_launchd_hack(void);
-static void do_unmounts(void);
 void eliminate_double_reboot(void);
 
 /* For Jetsam. */
@@ -737,6 +738,8 @@
 
 		job_log(j, LOG_DEBUG, "Sent SIGTERM signal%s", extralog);
 	}
+	
+	j->stopped = true;
 }
 
 launch_data_t
@@ -894,88 +897,12 @@
 {
 	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, false);
-		}
-		
-		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++;
-				}
-			}
-
-			type = "hopefully first";
-			if( unkilled_jobs == 0 ) {
-				jm->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 ) {
-				jm->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");
-		} else {
-			jobmgr_do_garbage_collection(jm);
-		}
-	}
-
-	runtime_closelog(); /* hack to flush logs */
 }
 
 jobmgr_t
 jobmgr_shutdown(jobmgr_t jm)
 {
 	jobmgr_t jmi, jmn;
-	job_t ji;
-
 	jobmgr_log(jm, LOG_DEBUG, "Beginning job manager shutdown with flags: %s", reboot_flags_to_C_names(jm->reboot_flags));
 
 	jm->shutting_down = true;
@@ -983,15 +910,7 @@
 	SLIST_FOREACH_SAFE(jmi, &jm->submgrs, sle, jmn) {
 		jobmgr_shutdown(jmi);
 	}
-
-	if (jm->hopefully_first_cnt) {
-		LIST_FOREACH(ji, &jm->jobs, sle) {
-			if (ji->p && ji->hopefully_exits_first) {
-				job_stop(ji);
-			}
-		}
-	}
-
+	
 	if (jm->parentmgr == NULL && pid1_magic) {
 		jobmgr_assumes(jm, kevent_mod((uintptr_t)jm, EVFILT_TIMER, EV_ADD, NOTE_SECONDS, 5, jm));
 	}
@@ -1013,11 +932,11 @@
 	}
 
 	while( (ji = LIST_FIRST(&jm->jobs)) ) {
-		if( !expect_real_jobs && !job_assumes(ji, ji->anonymous) ) {
+		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 ) {
+			if( !ji->anonymous && ji->p ) {
 				job_log(ji, LOG_WARNING | LOG_CONSOLE, "Job has overstayed its welcome. Forcing removal.");
 				job_remove(ji, true);
 			} else {
@@ -1039,18 +958,13 @@
 		SLIST_REMOVE(&jm->parentmgr->submgrs, jm, jobmgr_s, sle);
 	} else if (pid1_magic) {
 		eliminate_double_reboot();
-		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 | LOG_CONSOLE, "Unmounting all filesystems except / and /dev");
-		do_unmounts();
 		launchd_log_vm_stats();
 		jobmgr_log(root_jobmgr, LOG_NOTICE | LOG_CONSOLE, "About to call: reboot(%s).", reboot_flags_to_C_names(jm->reboot_flags));
 		runtime_closelog();
 		jobmgr_assumes(jm, reboot(jm->reboot_flags) != -1);
-		runtime_closelog();
 	} else {
-		runtime_closelog();
 		jobmgr_log(jm, LOG_DEBUG, "About to exit");
+		runtime_closelog();
 		exit(EXIT_SUCCESS);
 	}
 	
@@ -1198,12 +1112,10 @@
 	if (j->poll_for_vfs_changes) {
 		job_assumes(j, kevent_mod((uintptr_t)&j->semaphores, EVFILT_TIMER, EV_DELETE, 0, 0, j) != -1);
 	}
-
 	if( j->exit_timeout ) {
 		/* Not a big deal if this fails. It means that the timer's already been freed. */
 		kevent_mod((uintptr_t)&j->exit_timeout, EVFILT_TIMER, EV_DELETE, 0, 0, NULL);
 	}
-	
 	if( j->jetsam_priority != LAUNCHD_JETSAM_PRIORITY_UNSET ) {
 		LIST_REMOVE(j, jetsam_sle);
 		j->mgr->jetsam_jobs_cnt--;
@@ -2323,18 +2235,18 @@
 
 /* Should try and consolidate with job_mig_intran2() and jobmgr_find_by_pid(). */
 job_t
-jobmgr_find_by_pid_deep(jobmgr_t jm, pid_t p)
+jobmgr_find_by_pid_deep(jobmgr_t jm, pid_t p, bool anon_okay)
 {
 	job_t ji = NULL;
 	LIST_FOREACH( ji, &jm->active_jobs[ACTIVE_JOB_HASH(p)], pid_hash_sle ) {
-		if (ji->p == p && !ji->anonymous) {
+		if (ji->p == p && (!ji->anonymous || (ji->anonymous && anon_okay)) ) {
 			return ji;
 		}
 	}
 
 	jobmgr_t jmi = NULL;
 	SLIST_FOREACH( jmi, &jm->submgrs, sle ) {
-		if( (ji = jobmgr_find_by_pid_deep(jmi, p)) ) {
+		if( (ji = jobmgr_find_by_pid_deep(jmi, p, anon_okay)) ) {
 			break;
 		}
 	}
@@ -2642,7 +2554,7 @@
 
 	if (WIFSIGNALED(status)) {
 		int s = WTERMSIG(status);
-		if (SIGKILL == s || SIGTERM == s) {
+		if ((SIGKILL == s || SIGTERM == s) && !j->stopped) {
 			job_log(j, LOG_NOTICE, "Exited: %s", strsignal(s));
 		} else {
 			switch( s ) {
@@ -2699,6 +2611,10 @@
 	j->lastlookup_gennum = 0;
 	j->p = 0;
 
+	if( !j->anonymous ) {
+		jobmgr_do_garbage_collection(j->mgr);
+	}
+
 	/*
 	 * We need to someday evaluate other jobs and find those who wish to track the
 	 * active/inactive state of this job. The current job_dispatch() logic makes
@@ -2951,7 +2867,7 @@
 	if (unlikely(rsz == 0)) {
 		job_log(j, LOG_DEBUG, "Standard out/error pipe closed");
 		close_log_redir = true;
-	} else if (!job_assumes(j, rsz != -1)) {
+	} else if (!job_assumes(j, rsz != -1 && errno != EAGAIN)) {
 		close_log_redir = true;
 	} else {
 		buf[rsz] = '\0';
@@ -3099,8 +3015,8 @@
 			struct kinfo_proc kp;
 			size_t len = sizeof(kp);
 
-			if (job_assumes(j, sysctl(mib, 4, &kp, &len, NULL, 0) != -1)
-					&& job_assumes(j, len == sizeof(kp))) {
+			/* Sometimes, the kernel says it succeeded but really didn't. */
+			if (job_assumes(j, sysctl(mib, 4, &kp, &len, NULL, 0) != -1) && len == sizeof(kp)) {
 				char newlabel[1000];
 
 				snprintf(newlabel, sizeof(newlabel), "%p.%s", j, kp.kp_proc.p_comm);
@@ -3212,6 +3128,7 @@
 				}
 				job_log(j, LOG_WARNING | LOG_CONSOLE, "Exit timeout elapsed (%u seconds). Killing", j->exit_timeout);
 				job_kill(j);
+				jobmgr_do_garbage_collection(j->mgr);
 			}
 		}
 	} else {
@@ -3433,7 +3350,8 @@
 		j->start_pending = false;
 		j->reaped = false;
 		j->crashed = false;
-
+		j->stopped = false;
+			
 		runtime_add_ref();
 		total_children++;
 		LIST_INSERT_HEAD(&j->mgr->active_jobs[ACTIVE_JOB_HASH(c)], j, pid_hash_sle);
@@ -4171,9 +4089,17 @@
 	newmsg = alloca(newmsgsz);
 
 	if (err) {
+	#if !TARGET_OS_EMBEDDED
 		snprintf(newmsg, newmsgsz, "%s: %s", msg, strerror(err));
+	#else
+		snprintf(newmsg, newmsgsz, "(%s) %s: %s", j->label, msg, strerror(err));
+	#endif
 	} else {
+	#if !TARGET_OS_EMBEDDED
 		snprintf(newmsg, newmsgsz, "%s", msg);
+	#else
+		snprintf(newmsg, newmsgsz, "(%s) %s", j->label, msg);
+	#endif
 	}
 
 	if (unlikely(j->debug)) {
@@ -5001,7 +4927,6 @@
 job_active(job_t j)
 {
 	struct machservice *ms;
-
 	if (j->p) {
 		return "PID is still valid";
 	}
@@ -5267,90 +5192,182 @@
 }
 
 jobmgr_t
-jobmgr_do_garbage_collection(jobmgr_t jm)
+jobmgr_do_hopefully_first_shutdown_phase(jobmgr_t jm)
 {
-	jobmgr_t jmi, jmn;
-	job_t ji, jn;
-
-	SLIST_FOREACH_SAFE(jmi, &jm->submgrs, sle, jmn) {
-		jobmgr_do_garbage_collection(jmi);
+	jobmgr_t _jm = jm;
+	if( !jm->shutting_down ) {
+		return _jm;
 	}
-
-	if (likely(!jm->shutting_down)) {
-		return jm;
+	
+	bool should_proceed = !jm->killed_hopefully_first_jobs;
+	
+	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);
+				}
+			}
+		}
+		
+		/* If we've killed everyone, move on. */
+		if( unkilled_cnt == 0 ) {
+			jm->killed_hopefully_first_jobs = true;
+			_jm = NULL;
+		}
+	} else {
+		jm->killed_hopefully_first_jobs = true;
+		_jm = NULL;
 	}
+	
+	return _jm;
+}
 
-	jobmgr_log(jm, LOG_DEBUG, "Garbage collecting.");
-
-	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;
+jobmgr_t
+jobmgr_do_normal_shutdown_phase(jobmgr_t jm)
+{
+	jobmgr_t _jm = jm;
+	if( !jm->shutting_down ) {
+		return _jm;
 	}
-
-	if (jm->parentmgr && jm->parentmgr->shutting_down && jm->parentmgr->hopefully_first_cnt) {
-		return jm;
-	}
-
-	if (!jm->sent_stop_to_normal_jobs) {
-		jobmgr_log(jm, LOG_DEBUG, "Asking \"normal\" jobs to exit.");
-
-		LIST_FOREACH_SAFE(ji, &jm->jobs, sle, jn) {
-			if (!job_active(ji)) {
-				job_remove(ji, false);
-			} else if (!ji->hopefully_exits_last) {
-				job_stop(ji);
+	
+	bool should_proceed = !jm->killed_normal_jobs;
+	
+	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) && !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);
+				}
+			}
 		}
-
-		jm->sent_stop_to_normal_jobs = true;
+		
+		/* If we've killed everyone, move on. */
+		if( unkilled_cnt == 0 ) {
+			jm->killed_normal_jobs = true;
+			_jm = NULL;
+		}
+	} else {
+		jm->killed_normal_jobs = true;
+		_jm = NULL;
 	}
+	
+	return _jm;
+}
 
-	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;
+jobmgr_t
+jobmgr_do_hopefully_last_shutdown_phase(jobmgr_t jm)
+{
+	jobmgr_t _jm = jm;
+	if( !jm->shutting_down ) {
+		return _jm;
 	}
-
-	if (!jm->sent_stop_to_hopefully_last_jobs) {
-		jobmgr_log(jm, LOG_DEBUG, "Asking \"hopefully last\" jobs to exit.");
-
-		LIST_FOREACH(ji, &jm->jobs, sle) {
-			if (ji->p && ji->anonymous) {
-				continue;
-			} else if (ji->p && job_assumes(ji, ji->hopefully_exits_last)) {
-				job_stop(ji);
+	
+	static bool killed_stray_jobs = false;
+	if( !killed_stray_jobs ) {
+		jobmgr_log_stray_children(jm, true);
+		killed_stray_jobs = true;
+	}
+	
+	bool should_proceed = !jm->killed_hopefully_last_jobs && total_children != 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);
+				}
+			}
 		}
-
-		jm->sent_stop_to_hopefully_last_jobs = true;
+		
+		/* If we've killed everyone, move on. */
+		if( unkilled_cnt == 0 ) {
+			jm->killed_hopefully_last_jobs = true;
+			_jm = NULL;
+		}
+	} else {
+		jm->killed_hopefully_last_jobs = true;
+		_jm = NULL;
 	}
+	
+	return _jm;
+}
 
-	if (!SLIST_EMPTY(&jm->submgrs)) {
-		jobmgr_log(jm, LOG_DEBUG, "jm->submgrs not empty!");
+jobmgr_t
+jobmgr_do_garbage_collection(jobmgr_t jm)
+{
+	if( !jm->shutting_down ) {
 		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, total_children == 0 ? false : true);
-		}
-	} else {
-		LIST_FOREACH(ji, &jm->jobs, sle) {
-			if (!ji->anonymous) {
-				retval = jm;
+	
+	jobmgr_t jmi = NULL, jmn = NULL;
+	SLIST_FOREACH_SAFE(jmi, &jm->submgrs, sle, jmn) {
+		jobmgr_do_garbage_collection(jmi);
+	}
+	
+	jobmgr_t _jm = jobmgr_do_hopefully_first_shutdown_phase(jm);
+	if( !_jm ) {
+		_jm = jobmgr_do_normal_shutdown_phase(jm) ? : jobmgr_do_hopefully_last_shutdown_phase(jm);
+	}
+	
+	if( !_jm ) {
+		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( !job_assumes(ji, (ji->anonymous || ji->sent_sigkill) && ji->p) ) {
+				job_log(ji, LOG_NOTICE | LOG_CONSOLE, "Job should be gone but is not.");
 			}
 		}
 		
-		if( !retval ) {
-			jobmgr_log_stray_children(jm);
-			jobmgr_remove(jm, false);
-		}
+		jobmgr_remove(jm, total_children != 0);
 	}
 	
-	return retval;
+	return _jm;
 }
 
 void
@@ -5402,16 +5419,12 @@
 }
 
 void
-jobmgr_log_stray_children(jobmgr_t jm)
+jobmgr_log_stray_children(jobmgr_t jm, bool kill_strays)
 {
 	int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL };
 	size_t i, kp_cnt = 0, kp_skipped = 0, len = sizeof(struct kinfo_proc) * get_kern_max_proc();
 	struct kinfo_proc *kp;
 
-	if (!do_apple_internal_logging) {
-		return;
-	}
-
 	if (likely(jm->parentmgr || !pid1_magic)) {
 		return;
 	}
@@ -5443,10 +5456,9 @@
 		/* We might have some jobs hanging around that we've decided to shut down in spite of. */
 		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_log(jm, LOG_WARNING | LOG_CONSOLE, "Stray %s %s at shutdown: PID %u PPID %u PGID %u %s", z, j ? "anonymous job" : "process", p_i, pp_i, pg_i, n);
 			
-			/* <rdar://problem/6399100> Let the kernel clean up the stragglers. */
-			if( 0 ) {
+			if( kill_strays ) {
 				jobmgr_kill_stray_child(jm, p_i);
 			}
 		}
@@ -5680,18 +5692,29 @@
 	struct machservice *ms;
 	job_t target_j;
 
+	jobmgr_log(jm, LOG_DEBUG, "Looking up %sservice %s", target_pid ? "per-PID " : "", name);
+
 	if (target_pid) {
-		//jobmgr_assumes(jm, !check_parent);
-		if (unlikely((target_j = jobmgr_find_by_pid(jm, target_pid, false)) == NULL)) {
-			return NULL;
+		/* This is a hack to let FileSyncAgent look up per-PID Mach services from the Background
+		 * bootstrap in other bootstraps.
+		 */
+		
+		/* Start in the given bootstrap. */
+		if( unlikely((target_j = jobmgr_find_by_pid(jm, target_pid, false)) == NULL) ) {
+			/* If we fail, do a deep traversal. */
+			if (unlikely((target_j = jobmgr_find_by_pid_deep(root_jobmgr, target_pid, true)) == NULL)) {
+				jobmgr_log(jm, LOG_DEBUG, "Didn't find PID %i", target_pid);
+				return NULL;
+			}
 		}
-
+		
 		SLIST_FOREACH(ms, &target_j->machservices, sle) {
 			if (ms->per_pid && strcmp(name, ms->name) == 0) {
 				return ms;
 			}
 		}
 
+		job_log(target_j, LOG_DEBUG, "Didn't find per-PID Mach service: %s", name);
 		return NULL;
 	}
 	
@@ -6309,7 +6332,7 @@
 	}
 
 	if (unlikely(j->anonymous)) {
-		job_log(j, LOG_ERR, "Anonymous job tried to setup shared memory");
+		job_log(j, LOG_DEBUG, "Anonymous job tried to setup shared memory");
 		return BOOTSTRAP_NOT_PRIVILEGED;
 	}
 
@@ -7261,7 +7284,7 @@
 	
 	struct ldcred *ldc = runtime_get_caller_creds();
 	
-	/* Only allow root processes to look up children, even if we're in the per-user laucnhd.
+	/* Only allow root processes to look up children, even if we're in the per-user launchd.
 	 * Otherwise, this could be used to cross sessions, which counts as a security vulnerability
 	 * in a non-flat namespace.
 	 */
@@ -7389,7 +7412,7 @@
 		return BOOTSTRAP_NOT_PRIVILEGED;
 	}
 	
-	job_t j_for_pid = jobmgr_find_by_pid_deep(j->mgr, p);
+	job_t j_for_pid = jobmgr_find_by_pid_deep(j->mgr, p, false);
 	if( j_for_pid ) {
 		if( j_for_pid->kill_via_shmem ) {
 			if( j_for_pid->shmem ) {
@@ -7423,7 +7446,7 @@
 	/* This is so loginwindow doesn't try to quit GUI apps that have been launched
 	 * directly by launchd as agents.
 	 */
-	job_t j_for_pid = jobmgr_find_by_pid_deep(root_jobmgr, p);
+	job_t j_for_pid = jobmgr_find_by_pid_deep(root_jobmgr, p, false);
 	if( j_for_pid && !j_for_pid->anonymous && !j_for_pid->legacy_LS_job ) {
 		*managed = true;
 	}
@@ -7493,8 +7516,6 @@
 			}
 		}
 	}
-
-	launchd_assumes(jmi != NULL);
 	
 jm_found:
 	return jmi;
@@ -8198,39 +8219,6 @@
 	free(w4r);
 }
 
-void
-do_unmounts(void)
-{
-	struct statfs buf[250];
-	int r, i, found, returned;
-
-	do {
-		returned = getfsstat(buf, (int) sizeof(buf), MNT_NOWAIT);
-		found = 0;
-
-		if (!launchd_assumes(returned != -1)) {
-			return;
-		}
-
-		/* Work backwards due to mounts on top of mounts */
-		for (i = returned - 1; i >= 0; i--) {
-			if (strcmp(buf[i].f_mntonname, "/") == 0) {
-				continue;
-			} else if (strncmp(buf[i].f_mntonname, "/dev", strlen("/dev")) == 0) {
-				continue;
-			}
-
-			r = unmount(buf[i].f_mntonname, 0);
-
-			runtime_syslog(LOG_DEBUG, "unmount(\"%s\", 0): %s", buf[i].f_mntonname, r == -1 ? strerror(errno) : "Success");
-
-			if (r != -1) {
-				found++;
-			}
-		}
-	} while ((returned == (sizeof(buf) / sizeof(buf[0]))) && (found > 0));
-}
-
 size_t
 get_kern_max_proc(void)
 {

Modified: trunk/launchd/src/libvproc.c
===================================================================
--- trunk/launchd/src/libvproc.c	2009-01-13 19:16:03 UTC (rev 23779)
+++ trunk/launchd/src/libvproc.c	2009-01-13 22:18:50 UTC (rev 23780)
@@ -131,15 +131,7 @@
 		if( !_vm_addr ) {
 			return;
 		}
-		
-		_vproc_log(LOG_WARNING,
-					"Using private memory for transactions. You are likely running under a launchd agent or daemon under a debugger.\n"
-					"Please keep the following considerations in mind.\n"
-					"0. This process is not actually participating in Instant Off. It will only be able to keep track of its transaction count.\n"
-					"1. This process will not die after cleaning up its last transaction after it has received SIGTERM.\n"
-					"2. You are debugging your program in an environment that is very different from the one it will run under.\n"
-					"3. You can use the WaitForDebugger key to stall execution of your daemon or agent so that you can attach to it. See launchd.plist(5). "
-					"This only applies if you are debugging a launchd daemon or agent. If you are debugging a GUI application under Xcode, this consideration does not apply.\n");
+
 		vm_addr = (vm_address_t)_vm_addr;
 	} else {
 		kr = vm_map(mach_task_self(), &vm_addr, getpagesize(), 0, true, shmem_port, 0, false,
@@ -245,7 +237,12 @@
 _vproc_transaction_count_for_pid(pid_t p, int32_t *count, bool *condemned)
 {
 	boolean_t _condemned = false;
-	return vproc_mig_transaction_count_for_pid(bootstrap_port, p, count, (boolean_t *)condemned ? : &_condemned);
+	kern_return_t kr = vproc_mig_transaction_count_for_pid(bootstrap_port, p, count, &_condemned);
+	if( kr == KERN_SUCCESS ) {
+		*condemned = _condemned ? true : false;
+	}
+	
+	return kr;
 }
 
 void
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/launchd-changes/attachments/20090113/c9707de9/attachment-0001.html>


More information about the launchd-changes mailing list